build: run the TypeScript scripts under script/ with plain node (#54168)

ts-node was used as a CLI for a handful of TypeScript scripts under
script/. Those now rely on the type stripping built into Node.js instead:

- script/gen-filenames, check-patch-diff, run-clang-tidy and the release
  helpers (find-github-release, upload-to-github, github-token, types)
  are renamed to .mts and invoked with `node`. They use
  import.meta.dirname and explicit .mts / .js specifiers, and
  tsconfig.script.json moves to nodenext with verbatimModuleSyntax so
  tsc checks them the way Node runs them.
- The documented minimum Node.js for building goes from 22.12 to 22.18,
  the first 22.x release with type stripping on by default.
- script/codesign/gen-trust.ts and its trust.xml template are deleted;
  nothing has invoked them since generate-identity.sh switched to a
  user-scoped keychain in #50058.
- oxfmt, oxlint, lint-staged and .gitattributes learn about .mts.

ts-node itself stays for now because the spec runner still loads specs
through its require hook.

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Samuel AttardandClaude authored and GitHub committed 2026-09-21 13:09:47 -07:00
1 parent e74f11ae98
commit 8d424eb34d
20 files changed
+68 -249

No files matched your search

+2
View File
@@ -23,8 +23,10 @@ patches/**/.patches merge=union
*.json text eol=lf
*.json5 text eol=lf
*.md text eol=lf
*.mjs text eol=lf
*.mm text eol=lf
*.mojom text eol=lf
*.mts text eol=lf
*.patches text eol=lf
*.proto text eol=lf
*.py text eol=lf
@@ -188,7 +188,7 @@ jobs:
node script/gen-libc++-filenames.js --check
node script/gen-siso-revision.js --check
node script/yarn.js ts-node script/gen-filenames.ts -- --check
node script/gen-filenames.mts --check
- name: Wait for active SSH sessions
if: always() && !cancelled()
shell: bash
+1 -1
View File
@@ -6,7 +6,7 @@
// --buildflags out/gen/electron/buildflags/buildflags.h --mode production
//
// --print-graph prints the bundle's source files as a JSON array instead of
// writing anything; script/gen-filenames.ts uses it to keep filenames.auto.gni
// writing anything; script/gen-filenames.mts uses it to keep filenames.auto.gni
// in sync.
//
// Type checking is not done here; see typecheck.mjs.
+1 -1
View File
@@ -11,7 +11,7 @@ Follow the guidelines below for building **Electron itself** on macOS, for the p
needed depends on what branch you are building, but the latest version of
Xcode is generally a good bet for building `main`.
* Python >= 3.9
* [Node.js](https://nodejs.org/download/) >= 22.12.0
* [Node.js](https://nodejs.org/download/) >= 22.18.0
### Arm64-specific prerequisites
@@ -14,7 +14,7 @@ Follow the guidelines below for building **Electron itself** on Windows, for the
set a few environment variables to point the toolchains to your installation path.
* `vs2022_install = DRIVE:\path\to\Microsoft Visual Studio\2022\Community`, replacing `2022` and `Community` with your installed versions and replacing `DRIVE:` with the drive that Visual Studio is on. Often, this will be `C:`.
* `WINDOWSSDKDIR = DRIVE:\path\to\Windows Kits\10`, replacing `DRIVE:` with the drive that Windows Kits is on. Often, this will be `C:`.
* [Node.js](https://nodejs.org/download/) >= 22.12.0
* [Node.js](https://nodejs.org/download/) >= 22.18.0
* [Git](https://git-scm.com)
* Debugging Tools for Windows of Windows SDK 10.0.15063.468 if you plan on
creating a full distribution since `symstore.exe` is used for creating a symbol
+9 -9
View File
@@ -42,10 +42,10 @@
"generate-version-json": "node script/generate-version-json.js",
"lint": "node ./script/lint.js && npm run lint:fmt && npm run lint:docs && npm run lint:chromium-roller",
"lint:js": "node ./script/lint.js --js",
"lint:fmt": "oxfmt --check \"{lib,spec,script,build,default_app,npm,typings}/**/*.{js,ts,mjs,cjs}\"",
"format": "oxfmt --write \"{lib,spec,script,build,default_app,npm,typings}/**/*.{js,ts,mjs,cjs}\"",
"lint:fmt": "oxfmt --check \"{lib,spec,script,build,default_app,npm,typings}/**/*.{js,ts,mjs,mts,cjs}\"",
"format": "oxfmt --write \"{lib,spec,script,build,default_app,npm,typings}/**/*.{js,ts,mjs,mts,cjs}\"",
"lint:clang-format": "python3 script/run-clang-format.py -r -c shell/ || (echo \"\\nCode not formatted correctly.\" && exit 1)",
"lint:clang-tidy": "ts-node ./script/run-clang-tidy.ts",
"lint:clang-tidy": "node ./script/run-clang-tidy.mts",
"lint:cpp": "node ./script/lint.js --cc",
"lint:objc": "node ./script/lint.js --objc",
"lint:py": "node ./script/lint.js --py",
@@ -85,15 +85,15 @@
"*": [
"npm run lint:chromium-roller"
],
"*.{js,ts,mjs,cjs}": [
"*.{js,ts,mjs,mts,cjs}": [
"oxfmt --write"
],
"*.{js,ts}": [
"*.{js,ts,mjs,mts}": [
"node script/lint.js --js --fix --only --",
"oxfmt --write"
],
"*.{js,mjs,ts,d.ts}": [
"ts-node script/gen-filenames.ts"
"*.{js,mjs,ts,mts,d.ts}": [
"node script/gen-filenames.mts"
],
"*.{cc,mm,c,h}": [
"python3 script/run-clang-format.py -r -c --fix"
@@ -109,12 +109,12 @@
"node script/lint.js --py --fix --only --"
],
"docs/api/**/*.md": [
"ts-node script/gen-filenames.ts",
"node script/gen-filenames.mts",
"markdownlint-cli2 --config .autofix.markdownlint-cli2.jsonc --fix"
],
"{*.patch,.patches}": [
"node script/lint.js --patches --only --",
"ts-node script/check-patch-diff.ts"
"node script/check-patch-diff.mts"
],
"DEPS": [
"node script/gen-hunspell-filenames.js",
@@ -1,9 +1,9 @@
import { spawnSync } from 'node:child_process';
import * as path from 'node:path';
const srcPath = path.resolve(__dirname, '..', '..', '..');
const patchExportFnPath = path.resolve(__dirname, 'export_all_patches.py');
const configPath = path.resolve(__dirname, '..', 'patches', 'config.json');
const srcPath = path.resolve(import.meta.dirname, '..', '..', '..');
const patchExportFnPath = path.resolve(import.meta.dirname, 'export_all_patches.py');
const configPath = path.resolve(import.meta.dirname, '..', 'patches', 'config.json');
// Re-export all the patches to check if there were changes.
const proc = spawnSync('python3', [patchExportFnPath, configPath, '--dry-run'], {
-43
View File
@@ -1,43 +0,0 @@
import * as cp from 'node:child_process';
import * as fs from 'node:fs';
import * as path from 'node:path';
const certificatePath = process.argv[2];
const outPath = process.argv[3];
const templatePath = path.resolve(__dirname, 'trust.xml');
const template = fs.readFileSync(templatePath, 'utf8');
const fingerprintResult = cp.spawnSync('openssl', ['x509', '-noout', '-fingerprint', '-sha1', '-in', certificatePath]);
if (fingerprintResult.status !== 0) {
console.error(fingerprintResult.stderr.toString());
process.exit(1);
}
const fingerprint = fingerprintResult.stdout
.toString()
.replace(/^SHA1 Fingerprint=/, '')
.replace(/:/g, '')
.trim();
const serialResult = cp.spawnSync('openssl', ['x509', '-serial', '-noout', '-in', certificatePath]);
if (serialResult.status !== 0) {
console.error(serialResult.stderr.toString());
process.exit(1);
}
let serialHex = serialResult.stdout
.toString()
.replace(/^serial=/, '')
.trim();
// Pad the serial number out to 18 hex chars
while (serialHex.length < 18) {
serialHex = `0${serialHex}`;
}
const serialB64 = Buffer.from(serialHex, 'hex').toString('base64');
const trust = template.replace(/{{FINGERPRINT}}/g, fingerprint).replace(/{{SERIAL_BASE64}}/g, serialB64);
fs.writeFileSync(outPath, trust);
console.log('Generated Trust Settings');
-138
View File
@@ -1,138 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>trustList</key>
<dict>
<key>{{FINGERPRINT}}</key>
<dict>
<key>issuerName</key>
<data>
MH8xCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJCQzESMBAGA1UEBwwJ
VmFuY291dmVyMRMwEQYDVQQKDApFbGVjdHJvbkpTMRgwFgYDVQQL
DA9CdWlsZEF1dG9tYXRpb24xIDAeBgNVBAMMF2NvZGVzaWduLmVs
ZWN0cm9uanMub3Jn
</data>
<key>modDate</key>
<date>2019-01-01T00:00:00Z</date>
<key>serialNumber</key>
<data>
{{SERIAL_BASE64}}
</data>
<key>trustSettings</key>
<array>
<dict>
<key>kSecTrustSettingsAllowedError</key>
<integer>-2147409654</integer>
<key>kSecTrustSettingsPolicy</key>
<data>
KoZIhvdjZAED
</data>
<key>kSecTrustSettingsPolicyName</key>
<string>sslServer</string>
<key>kSecTrustSettingsResult</key>
<integer>1</integer>
</dict>
<dict>
<key>kSecTrustSettingsAllowedError</key>
<integer>-2147408896</integer>
<key>kSecTrustSettingsPolicy</key>
<data>
KoZIhvdjZAED
</data>
<key>kSecTrustSettingsPolicyName</key>
<string>sslServer</string>
<key>kSecTrustSettingsResult</key>
<integer>1</integer>
</dict>
<dict>
<key>kSecTrustSettingsAllowedError</key>
<integer>-2147409654</integer>
<key>kSecTrustSettingsPolicy</key>
<data>
KoZIhvdjZAEI
</data>
<key>kSecTrustSettingsPolicyName</key>
<string>SMIME</string>
<key>kSecTrustSettingsResult</key>
<integer>1</integer>
</dict>
<dict>
<key>kSecTrustSettingsAllowedError</key>
<integer>-2147408872</integer>
<key>kSecTrustSettingsPolicy</key>
<data>
KoZIhvdjZAEI
</data>
<key>kSecTrustSettingsPolicyName</key>
<string>SMIME</string>
<key>kSecTrustSettingsResult</key>
<integer>1</integer>
</dict>
<dict>
<key>kSecTrustSettingsAllowedError</key>
<integer>-2147409654</integer>
<key>kSecTrustSettingsPolicy</key>
<data>
KoZIhvdjZAEJ
</data>
<key>kSecTrustSettingsPolicyName</key>
<string>eapServer</string>
<key>kSecTrustSettingsResult</key>
<integer>1</integer>
</dict>
<dict>
<key>kSecTrustSettingsAllowedError</key>
<integer>-2147409654</integer>
<key>kSecTrustSettingsPolicy</key>
<data>
KoZIhvdjZAEL
</data>
<key>kSecTrustSettingsPolicyName</key>
<string>ipsecServer</string>
<key>kSecTrustSettingsResult</key>
<integer>1</integer>
</dict>
<dict>
<key>kSecTrustSettingsAllowedError</key>
<integer>-2147409654</integer>
<key>kSecTrustSettingsPolicy</key>
<data>
KoZIhvdjZAEQ
</data>
<key>kSecTrustSettingsPolicyName</key>
<string>CodeSigning</string>
<key>kSecTrustSettingsResult</key>
<integer>1</integer>
</dict>
<dict>
<key>kSecTrustSettingsAllowedError</key>
<integer>-2147409654</integer>
<key>kSecTrustSettingsPolicy</key>
<data>
KoZIhvdjZAEU
</data>
<key>kSecTrustSettingsPolicyName</key>
<string>AppleTimeStamping</string>
<key>kSecTrustSettingsResult</key>
<integer>1</integer>
</dict>
<dict>
<key>kSecTrustSettingsAllowedError</key>
<integer>-2147409654</integer>
<key>kSecTrustSettingsPolicy</key>
<data>
KoZIhvdjZAEC
</data>
<key>kSecTrustSettingsPolicyName</key>
<string>basicX509</string>
<key>kSecTrustSettingsResult</key>
<integer>1</integer>
</dict>
</array>
</dict>
</dict>
<key>trustVersion</key>
<integer>1</integer>
</dict>
</plist>
+1 -1
View File
@@ -28,7 +28,7 @@ ELECTRON_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SRC_DIR = os.path.dirname(ELECTRON_DIR)
SHELL_DIR = os.path.join(ELECTRON_DIR, 'shell')
# Keep in sync with script/run-clang-tidy.ts.
# Keep in sync with script/run-clang-tidy.mts.
SKIPPED_SOURCES = {
# Host tool built in v8_snapshot_toolchain; its generated buildflag headers
# don't exist in the default toolchain's gen dir.
@@ -2,15 +2,15 @@ import * as cp from 'node:child_process';
import * as fs from 'node:fs';
import * as path from 'node:path';
const rootPath = path.resolve(__dirname, '..');
const gniPath = path.resolve(__dirname, '../filenames.auto.gni');
const rootPath = path.resolve(import.meta.dirname, '..');
const gniPath = path.resolve(rootPath, 'filenames.auto.gni');
const allDocs = fs
.readdirSync(path.resolve(__dirname, '../docs/api'))
.readdirSync(path.resolve(rootPath, 'docs/api'))
.map((doc) => `docs/api/${doc}`)
.concat(fs.readdirSync(path.resolve(__dirname, '../docs/api/structures')).map((doc) => `docs/api/structures/${doc}`));
.concat(fs.readdirSync(path.resolve(rootPath, 'docs/api/structures')).map((doc) => `docs/api/structures/${doc}`));
const typingFiles = fs.readdirSync(path.resolve(__dirname, '../typings')).map((child) => `typings/${child}`);
const typingFiles = fs.readdirSync(path.resolve(rootPath, 'typings')).map((child) => `typings/${child}`);
// Recursively collect files under `dir` matching any of the provided
// extensions. Paths are returned relative to `rootPath` using forward slashes
@@ -41,8 +41,8 @@ const collectSources = (dir: string, extensions: readonly string[]): string[] =>
const nodeHeaderSources = Array.from(
new Set([
'../third_party/electron_node/tools/install.py',
...collectSources(path.resolve(__dirname, '../../third_party/electron_node/src'), ['.h']),
...collectSources(path.resolve(__dirname, '../../v8/include'), ['.h', '.inc'])
...collectSources(path.resolve(rootPath, '../third_party/electron_node/src'), ['.h']),
...collectSources(path.resolve(rootPath, '../v8/include'), ['.h', '.inc'])
])
).sort();
@@ -131,7 +131,7 @@ ${target.dependencies.map((dep) => ` "${dep}",`).join('\n')}
const existing = fs.existsSync(gniPath) ? fs.readFileSync(gniPath, 'utf8') : '';
if (existing !== generated) {
console.error(
`${path.relative(rootPath, gniPath)} is out of date. Run 'node script/gen-filenames.ts' to regenerate.`
`${path.relative(rootPath, gniPath)} is out of date. Run 'node script/gen-filenames.mts' to regenerate.`
);
process.exit(1);
}
@@ -140,9 +140,7 @@ ${target.dependencies.map((dep) => ` "${dep}",`).join('\n')}
}
};
if (require.main === module) {
main().catch((err) => {
console.error(err);
process.exit(1);
});
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
-4
View File
@@ -16,12 +16,8 @@ from lib.config import verbose_mode_print
ELECTRON_DIR = os.path.abspath(
os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
)
TS_NODE = os.path.join(ELECTRON_DIR, 'node_modules', '.bin', 'ts-node')
SRC_DIR = os.path.abspath(os.path.join(__file__, '..', '..', '..', '..'))
if sys.platform in ['win32', 'cygwin']:
TS_NODE += '.cmd'
@contextlib.contextmanager
def scoped_cwd(path):
cwd = os.getcwd()
+2 -1
View File
@@ -156,7 +156,8 @@ const LINTERS = [
'spec/node_modules',
'spec/fixtures/native-addon'
],
test: (filename) => filename.endsWith('.js') || filename.endsWith('.ts') || filename.endsWith('.mjs'),
test: (filename) =>
filename.endsWith('.js') || filename.endsWith('.ts') || filename.endsWith('.mjs') || filename.endsWith('.mts'),
run: async (opts, filenames) => {
const clean = runOxlint(filenames, { fix: opts.fix });
if (!clean) {
@@ -1,7 +1,7 @@
import { Octokit } from '@octokit/rest';
import { createGitHubTokenStrategy } from './github-token';
import { ELECTRON_ORG, ELECTRON_REPO, ElectronReleaseRepo, NIGHTLY_REPO } from './types';
import { createGitHubTokenStrategy } from './github-token.mts';
import { ELECTRON_ORG, ELECTRON_REPO, type ElectronReleaseRepo, NIGHTLY_REPO } from './types.mts';
if (process.argv.length < 3) {
console.log('Usage: find-release version');
@@ -1,6 +1,6 @@
import { createTokenAuth } from '@octokit/auth-token';
import { ElectronReleaseRepo } from './types';
import type { ElectronReleaseRepo } from './types.mts';
const cachedTokens = Object.create(null);
File renamed without changes.
@@ -2,8 +2,8 @@ import { Octokit } from '@octokit/rest';
import * as fs from 'node:fs';
import { createGitHubTokenStrategy } from '../github-token';
import { ELECTRON_ORG, ELECTRON_REPO, ElectronReleaseRepo, NIGHTLY_REPO } from '../types';
import { createGitHubTokenStrategy } from '../github-token.mts';
import { ELECTRON_ORG, ELECTRON_REPO, type ElectronReleaseRepo, NIGHTLY_REPO } from '../types.mts';
if (process.argv.length < 6) {
console.log('Usage: upload-to-github filePath fileName releaseId');
+5 -5
View File
@@ -21,7 +21,7 @@ from lib.config import PLATFORM, get_target_arch, \
verbose_mode_print
from lib.util import get_electron_branding, execute, get_electron_version, \
store_artifact, get_electron_exec, get_out_dir, \
SRC_DIR, ELECTRON_DIR, TS_NODE
SRC_DIR, ELECTRON_DIR
ELECTRON_VERSION = 'v' + get_electron_version()
@@ -361,8 +361,8 @@ def upload_electron(release, file_path, args):
def upload_io_to_github(release, filename, filepath, version):
print(f'Uploading {filename} to GitHub')
script_path = os.path.join(
ELECTRON_DIR, 'script', 'release', 'uploaders', 'upload-to-github.ts')
with subprocess.Popen([TS_NODE, script_path, filepath,
ELECTRON_DIR, 'script', 'release', 'uploaders', 'upload-to-github.mts')
with subprocess.Popen(['node', script_path, filepath,
filename, str(release['id']), version],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT) as upload_process:
@@ -399,12 +399,12 @@ def upload_sha256_checksum(version, file_path, key_prefix=None):
def get_release(version):
script_path = os.path.join(
ELECTRON_DIR, 'script', 'release', 'find-github-release.ts')
ELECTRON_DIR, 'script', 'release', 'find-github-release.mts')
# Strip warnings from stdout to ensure the only output is the desired object
release_env = os.environ.copy()
release_env['NODE_NO_WARNINGS'] = '1'
release_info = execute([TS_NODE, script_path, version], release_env)
release_info = execute(['node', script_path, version], release_env)
verbose_mode_print(f'Release info for version: {version}:\n')
verbose_mode_print(release_info)
release = json.loads(release_info)
@@ -1,16 +1,16 @@
import * as minimist from 'minimist';
import * as streamChain from 'stream-chain';
import * as streamJson from 'stream-json';
import { ignore as streamJsonIgnore } from 'stream-json/filters/Ignore';
import { streamArray as streamJsonStreamArray } from 'stream-json/streamers/StreamArray';
import minimist from 'minimist';
import StreamChain from 'stream-chain';
import StreamJson from 'stream-json';
import StreamJsonIgnore from 'stream-json/filters/Ignore.js';
import StreamJsonStreamArray from 'stream-json/streamers/StreamArray.js';
import * as childProcess from 'node:child_process';
import * as fs from 'node:fs';
import * as path from 'node:path';
import { chunkFilenames, findMatchingFiles, getDepotToolsEnv } from './lib/utils';
import { chunkFilenames, findMatchingFiles, getDepotToolsEnv } from './lib/utils.js';
const SOURCE_ROOT = path.normalize(path.dirname(__dirname));
const SOURCE_ROOT = path.normalize(path.dirname(import.meta.dirname));
const LLVM_BIN = path.resolve(SOURCE_ROOT, '..', 'third_party', 'llvm-build', 'Release+Asserts', 'bin');
type SpawnAsyncResult = {
@@ -88,11 +88,11 @@ async function runClangTidy(
const compiledFilenames: string[] = [];
return new Promise((resolve) => {
const pipeline = streamChain.chain([
const pipeline = StreamChain.chain([
fs.createReadStream(path.resolve(outDir, 'compile_commands.json')),
streamJson.parser(),
streamJsonIgnore({ filter: /\bcommand\b/i }),
streamJsonStreamArray(),
StreamJson.parser(),
StreamJsonIgnore.ignore({ filter: /\bcommand\b/i }),
StreamJsonStreamArray.streamArray(),
({ value: { file, directory } }) => {
const filename = path.resolve(directory, file);
return filenames.includes(filename) ? filename : null;
@@ -163,7 +163,8 @@ function parseCommandLine() {
const showUsage = (arg?: string): boolean => {
if (!arg || arg.startsWith('-')) {
console.log(
'Usage: script/run-clang-tidy.ts [-h|--help] [--jobs|-j] ' + '[--fix] [--checks] --out-dir OUTDIR [file1 file2]'
'Usage: script/run-clang-tidy.mts [-h|--help] [--jobs|-j] ' +
'[--fix] [--checks] --out-dir OUTDIR [file1 file2]'
);
process.exit(0);
}
@@ -252,13 +253,11 @@ async function main(): Promise<boolean> {
return runClangTidy(outDir, filenames, opts.checks, opts.jobs, opts.fix);
}
if (require.main === module) {
main()
.then((success) => {
process.exit(success ? 0 : 1);
})
.catch((err: ErrorWithExitCode) => {
console.error(`ERROR: ${err.message}`);
process.exit(err.exitCode || 1);
});
}
main()
.then((success) => {
process.exit(success ? 0 : 1);
})
.catch((err: ErrorWithExitCode) => {
console.error(`ERROR: ${err.message}`);
process.exit(err.exitCode || 1);
});
+4
View File
@@ -2,6 +2,10 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"rootDir": ".",
"module": "nodenext",
"moduleResolution": "nodenext",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,
"skipLibCheck": true
},