mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
4 Commits
build-win-
...
replace-we
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
225fc5aa08 | ||
|
|
43d61d4cd0 | ||
|
|
646dfd24f7 | ||
|
|
f89c8efc9d |
41
BUILD.gn
41
BUILD.gn
@@ -18,12 +18,12 @@ import("//tools/v8_context_snapshot/v8_context_snapshot.gni")
|
||||
import("//v8/gni/snapshot_toolchain.gni")
|
||||
import("build/asar.gni")
|
||||
import("build/electron_paks.gni")
|
||||
import("build/esbuild/esbuild.gni")
|
||||
import("build/extract_symbols.gni")
|
||||
import("build/js2c_toolchain.gni")
|
||||
import("build/npm.gni")
|
||||
import("build/templated_file.gni")
|
||||
import("build/tsc.gni")
|
||||
import("build/webpack/webpack.gni")
|
||||
import("buildflags/buildflags.gni")
|
||||
import("filenames.auto.gni")
|
||||
import("filenames.gni")
|
||||
@@ -162,75 +162,81 @@ npm_action("build_electron_definitions") {
|
||||
outputs = [ "$target_gen_dir/tsc/typings/electron.d.ts" ]
|
||||
}
|
||||
|
||||
webpack_build("electron_browser_bundle") {
|
||||
typescript_check("electron_lib_typecheck") {
|
||||
deps = [ ":build_electron_definitions" ]
|
||||
tsconfig = "//electron/tsconfig.electron.json"
|
||||
sources = auto_filenames.typecheck_sources
|
||||
}
|
||||
|
||||
esbuild_build("electron_browser_bundle") {
|
||||
deps = [ ":build_electron_definitions" ]
|
||||
|
||||
inputs = auto_filenames.browser_bundle_deps
|
||||
|
||||
config_file = "//electron/build/webpack/webpack.config.browser.js"
|
||||
config_file = "//electron/build/esbuild/configs/browser.js"
|
||||
out_file = "$target_gen_dir/js2c/browser_init.js"
|
||||
}
|
||||
|
||||
webpack_build("electron_renderer_bundle") {
|
||||
esbuild_build("electron_renderer_bundle") {
|
||||
deps = [ ":build_electron_definitions" ]
|
||||
|
||||
inputs = auto_filenames.renderer_bundle_deps
|
||||
|
||||
config_file = "//electron/build/webpack/webpack.config.renderer.js"
|
||||
config_file = "//electron/build/esbuild/configs/renderer.js"
|
||||
out_file = "$target_gen_dir/js2c/renderer_init.js"
|
||||
}
|
||||
|
||||
webpack_build("electron_worker_bundle") {
|
||||
esbuild_build("electron_worker_bundle") {
|
||||
deps = [ ":build_electron_definitions" ]
|
||||
|
||||
inputs = auto_filenames.worker_bundle_deps
|
||||
|
||||
config_file = "//electron/build/webpack/webpack.config.worker.js"
|
||||
config_file = "//electron/build/esbuild/configs/worker.js"
|
||||
out_file = "$target_gen_dir/js2c/worker_init.js"
|
||||
}
|
||||
|
||||
webpack_build("electron_sandboxed_renderer_bundle") {
|
||||
esbuild_build("electron_sandboxed_renderer_bundle") {
|
||||
deps = [ ":build_electron_definitions" ]
|
||||
|
||||
inputs = auto_filenames.sandbox_bundle_deps
|
||||
|
||||
config_file = "//electron/build/webpack/webpack.config.sandboxed_renderer.js"
|
||||
config_file = "//electron/build/esbuild/configs/sandboxed_renderer.js"
|
||||
out_file = "$target_gen_dir/js2c/sandbox_bundle.js"
|
||||
}
|
||||
|
||||
webpack_build("electron_isolated_renderer_bundle") {
|
||||
esbuild_build("electron_isolated_renderer_bundle") {
|
||||
deps = [ ":build_electron_definitions" ]
|
||||
|
||||
inputs = auto_filenames.isolated_bundle_deps
|
||||
|
||||
config_file = "//electron/build/webpack/webpack.config.isolated_renderer.js"
|
||||
config_file = "//electron/build/esbuild/configs/isolated_renderer.js"
|
||||
out_file = "$target_gen_dir/js2c/isolated_bundle.js"
|
||||
}
|
||||
|
||||
webpack_build("electron_node_bundle") {
|
||||
esbuild_build("electron_node_bundle") {
|
||||
deps = [ ":build_electron_definitions" ]
|
||||
|
||||
inputs = auto_filenames.node_bundle_deps
|
||||
|
||||
config_file = "//electron/build/webpack/webpack.config.node.js"
|
||||
config_file = "//electron/build/esbuild/configs/node.js"
|
||||
out_file = "$target_gen_dir/js2c/node_init.js"
|
||||
}
|
||||
|
||||
webpack_build("electron_utility_bundle") {
|
||||
esbuild_build("electron_utility_bundle") {
|
||||
deps = [ ":build_electron_definitions" ]
|
||||
|
||||
inputs = auto_filenames.utility_bundle_deps
|
||||
|
||||
config_file = "//electron/build/webpack/webpack.config.utility.js"
|
||||
config_file = "//electron/build/esbuild/configs/utility.js"
|
||||
out_file = "$target_gen_dir/js2c/utility_init.js"
|
||||
}
|
||||
|
||||
webpack_build("electron_preload_realm_bundle") {
|
||||
esbuild_build("electron_preload_realm_bundle") {
|
||||
deps = [ ":build_electron_definitions" ]
|
||||
|
||||
inputs = auto_filenames.preload_realm_bundle_deps
|
||||
|
||||
config_file = "//electron/build/webpack/webpack.config.preload_realm.js"
|
||||
config_file = "//electron/build/esbuild/configs/preload_realm.js"
|
||||
out_file = "$target_gen_dir/js2c/preload_realm_bundle.js"
|
||||
}
|
||||
|
||||
@@ -238,6 +244,7 @@ action("electron_js2c") {
|
||||
deps = [
|
||||
":electron_browser_bundle",
|
||||
":electron_isolated_renderer_bundle",
|
||||
":electron_lib_typecheck",
|
||||
":electron_node_bundle",
|
||||
":electron_preload_realm_bundle",
|
||||
":electron_renderer_bundle",
|
||||
|
||||
326
build/esbuild/bundle.js
Normal file
326
build/esbuild/bundle.js
Normal file
@@ -0,0 +1,326 @@
|
||||
#!/usr/bin/env node
|
||||
// Driver script that replaces webpack for building Electron's internal
|
||||
// JS bundles. Each bundle is a single esbuild invocation parameterized by
|
||||
// the per-target configuration files under build/esbuild/configs.
|
||||
//
|
||||
// Invoked by the GN `esbuild_build` template via `npm run bundle -- …`.
|
||||
|
||||
'use strict';
|
||||
|
||||
const esbuild = require('esbuild');
|
||||
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const electronRoot = path.resolve(__dirname, '../..');
|
||||
|
||||
function parseArgs (argv) {
|
||||
const args = {};
|
||||
for (let i = 0; i < argv.length; i++) {
|
||||
const a = argv[i];
|
||||
if (a.startsWith('--')) {
|
||||
const key = a.slice(2);
|
||||
const next = argv[i + 1];
|
||||
if (next === undefined || next.startsWith('--')) {
|
||||
args[key] = true;
|
||||
} else {
|
||||
args[key] = next;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
// Parse $target_gen_dir/buildflags/buildflags.h (a C++ header containing
|
||||
// `#define BUILDFLAG_INTERNAL_NAME() (0|1)` lines) into a map of flag name
|
||||
// to boolean. Used to seed the `define` table so that `BUILDFLAG(NAME)` call
|
||||
// sites can be statically folded to `true`/`false` at build time.
|
||||
function parseBuildflags (buildflagsPath) {
|
||||
const flags = {};
|
||||
if (!buildflagsPath) return flags;
|
||||
const source = fs.readFileSync(buildflagsPath, 'utf8');
|
||||
const re = /#define BUILDFLAG_INTERNAL_(.+?)\(\) \(([01])\)/g;
|
||||
let match;
|
||||
while ((match = re.exec(source)) !== null) {
|
||||
const [, name, value] = match;
|
||||
flags[name] = value === '1';
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
// Return the list of esbuild `alias` entries used by every bundle. esbuild's
|
||||
// alias matches the full module specifier (no `$` suffix trickery like
|
||||
// webpack), so the bare `electron` alias also matches `electron/main`, etc.,
|
||||
// because esbuild matches the leftmost segment first.
|
||||
function buildAliases (electronAPIFile, { aliasTimers }) {
|
||||
const aliases = {
|
||||
electron: electronAPIFile,
|
||||
'electron/main': electronAPIFile,
|
||||
'electron/renderer': electronAPIFile,
|
||||
'electron/common': electronAPIFile,
|
||||
'electron/utility': electronAPIFile
|
||||
};
|
||||
// Only browser-platform bundles (sandboxed_renderer, isolated_renderer,
|
||||
// preload_realm) need the timers shim — Node's `timers` builtin is not
|
||||
// available there. For node-platform bundles (browser, renderer, worker,
|
||||
// utility, node) the alias MUST NOT apply: lib/common/init.ts wraps the
|
||||
// real Node timers and then assigns the wrappers onto globalThis. If
|
||||
// those bundles saw the shim, the wrappers would recursively call back
|
||||
// into globalThis.setTimeout and blow the stack.
|
||||
if (aliasTimers) {
|
||||
aliases.timers = path.resolve(electronRoot, 'lib', 'common', 'timers-shim.ts');
|
||||
}
|
||||
return aliases;
|
||||
}
|
||||
|
||||
// esbuild's `alias` does not support wildcard prefixes like `@electron/internal/*`.
|
||||
// We instead install a tiny resolve plugin that rewrites any import starting
|
||||
// with that prefix to an absolute path under `lib/`. The plugin must also
|
||||
// replicate esbuild's extension/index resolution because returning a path
|
||||
// from onResolve bypasses the default resolver.
|
||||
function internalAliasPlugin () {
|
||||
const candidates = (base) => [
|
||||
base,
|
||||
`${base}.ts`,
|
||||
`${base}.js`,
|
||||
path.join(base, 'index.ts'),
|
||||
path.join(base, 'index.js')
|
||||
];
|
||||
return {
|
||||
name: 'electron-internal-alias',
|
||||
setup (build) {
|
||||
build.onResolve({ filter: /^@electron\/internal(\/|$)/ }, (args) => {
|
||||
// Tolerate stray double slashes in import paths (webpack was lenient).
|
||||
const rel = args.path.replace(/^@electron\/internal\/?/, '').replace(/^\/+/, '');
|
||||
const base = path.resolve(electronRoot, 'lib', rel);
|
||||
for (const c of candidates(base)) {
|
||||
try {
|
||||
if (fs.statSync(c).isFile()) return { path: c };
|
||||
} catch { /* keep looking */ }
|
||||
}
|
||||
return { errors: [{ text: `Cannot resolve @electron/internal path: ${args.path}` }] };
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Rewrites `BUILDFLAG(NAME)` call-sites to `(true)` or `(false)` at load
|
||||
// time, equivalent to the combination of webpack's DefinePlugin substitution
|
||||
// (BUILDFLAG -> "" and NAME -> "true"/"false") that the old config used.
|
||||
// Doing it in a single regex pass keeps the semantics identical and avoids
|
||||
// fighting with esbuild's AST-level `define` quoting rules.
|
||||
function buildflagPlugin (buildflags, { allowUnknown = false } = {}) {
|
||||
return {
|
||||
name: 'electron-buildflag',
|
||||
setup (build) {
|
||||
build.onLoad({ filter: /\.(ts|js)$/ }, async (args) => {
|
||||
const source = await fs.promises.readFile(args.path, 'utf8');
|
||||
if (!source.includes('BUILDFLAG(')) {
|
||||
return { contents: source, loader: args.path.endsWith('.ts') ? 'ts' : 'js' };
|
||||
}
|
||||
const rewritten = source.replace(/BUILDFLAG\(([A-Z0-9_]+)\)/g, (_, name) => {
|
||||
if (!Object.prototype.hasOwnProperty.call(buildflags, name)) {
|
||||
if (allowUnknown) return '(false)';
|
||||
throw new Error(`Unknown BUILDFLAG: ${name} (in ${args.path})`);
|
||||
}
|
||||
return `(${buildflags[name]})`;
|
||||
});
|
||||
return { contents: rewritten, loader: args.path.endsWith('.ts') ? 'ts' : 'js' };
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// TODO(MarshallOfSound): drop this patch once evanw/esbuild#4441 lands and
|
||||
// we bump esbuild — that PR adds a `__toCommonJSCached` helper for the
|
||||
// inline-require path so identity is preserved upstream. Tracked at
|
||||
// https://github.com/evanw/esbuild/issues/4440.
|
||||
//
|
||||
// esbuild's runtime emits `__toCommonJS = (mod) => __copyProps(__defProp({},
|
||||
// "__esModule", { value: true }), mod)`, which allocates a fresh wrapper
|
||||
// object every time `require()` resolves to a bundled ESM module. That
|
||||
// breaks identity expectations our code relies on (e.g. sandboxed preloads
|
||||
// expecting `require('timers') === require('node:timers')`, and the
|
||||
// defineProperties getters in lib/common/define-properties.ts expecting
|
||||
// stable namespaces). A cached WeakMap-backed version of __toCommonJS
|
||||
// existed in older esbuild releases (see evanw/esbuild#2126) but was
|
||||
// removed in evanw/esbuild@f4ff26d3 (0.14.27). Substitute a memoized
|
||||
// variant in post-processing so every call site returns the same wrapper
|
||||
// for the same underlying namespace, matching webpack's
|
||||
// `__webpack_require__` cache semantics.
|
||||
const ESBUILD_TO_COMMONJS_PATTERN =
|
||||
/var __toCommonJS = \(mod\) => __copyProps\(__defProp\(\{\}, "__esModule", \{ value: true \}\), mod\);/;
|
||||
const ESBUILD_TO_COMMONJS_REPLACEMENT =
|
||||
'var __toCommonJS = /* @__PURE__ */ ((cache) => (mod) => {\n' +
|
||||
' var cached = cache.get(mod);\n' +
|
||||
' if (cached) return cached;\n' +
|
||||
' var result = __copyProps(__defProp({}, "__esModule", { value: true }), mod);\n' +
|
||||
' cache.set(mod, result);\n' +
|
||||
' return result;\n' +
|
||||
' })(new WeakMap());';
|
||||
|
||||
function patchToCommonJS (source) {
|
||||
// Once evanw/esbuild#4441 lands, esbuild will emit `__toCommonJSCached`
|
||||
// for inline require() — when we see that helper in the output, the
|
||||
// upstream fix is active and this whole patch is a no-op (and should be
|
||||
// deleted on the next esbuild bump).
|
||||
if (source.includes('__toCommonJSCached')) {
|
||||
return source;
|
||||
}
|
||||
if (!ESBUILD_TO_COMMONJS_PATTERN.test(source)) {
|
||||
// Some bundles may not contain any ESM-shaped modules, in which case
|
||||
// esbuild omits the helper entirely and there is nothing to patch.
|
||||
if (source.includes('__toCommonJS')) {
|
||||
throw new Error(
|
||||
'esbuild bundle contains __toCommonJS but did not match the ' +
|
||||
'expected pattern; the runtime helper has likely changed upstream. ' +
|
||||
'Update ESBUILD_TO_COMMONJS_PATTERN in build/esbuild/bundle.js, or ' +
|
||||
'delete patchToCommonJS entirely if evanw/esbuild#4441 has landed.'
|
||||
);
|
||||
}
|
||||
return source;
|
||||
}
|
||||
return source.replace(ESBUILD_TO_COMMONJS_PATTERN, ESBUILD_TO_COMMONJS_REPLACEMENT);
|
||||
}
|
||||
|
||||
// Wrap bundle source text in the same header/footer pairs webpack's
|
||||
// wrapper-webpack-plugin used. The try/catch wrapper is load-bearing:
|
||||
// shell/common/node_util.cc's CompileAndCall relies on it to prevent
|
||||
// exceptions from tearing down bootstrap.
|
||||
function applyWrappers (source, opts, outputFilename) {
|
||||
let wrapped = patchToCommonJS(source);
|
||||
if (opts.wrapInitWithProfilingTimeout) {
|
||||
const header = 'function ___electron_webpack_init__() {';
|
||||
const footer = '\n};\nif ((globalThis.process || binding.process).argv.includes("--profile-electron-init")) {\n setTimeout(___electron_webpack_init__, 0);\n} else {\n ___electron_webpack_init__();\n}';
|
||||
wrapped = header + wrapped + footer;
|
||||
}
|
||||
if (opts.wrapInitWithTryCatch) {
|
||||
const header = 'try {';
|
||||
const footer = `\n} catch (err) {\n console.error('Electron ${outputFilename} script failed to run');\n console.error(err);\n}`;
|
||||
wrapped = header + wrapped + footer;
|
||||
}
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
async function buildBundle (opts, cliArgs) {
|
||||
const {
|
||||
target,
|
||||
alwaysHasNode,
|
||||
loadElectronFromAlternateTarget,
|
||||
wrapInitWithProfilingTimeout,
|
||||
wrapInitWithTryCatch
|
||||
} = opts;
|
||||
|
||||
const outputFilename = cliArgs['output-filename'] || `${target}.bundle.js`;
|
||||
const outputPath = cliArgs['output-path'] || path.resolve(electronRoot, 'out');
|
||||
const mode = cliArgs.mode || 'development';
|
||||
const minify = mode === 'production';
|
||||
const printGraph = !!cliArgs['print-graph'];
|
||||
|
||||
let entry = path.resolve(electronRoot, 'lib', target, 'init.ts');
|
||||
if (!fs.existsSync(entry)) {
|
||||
entry = path.resolve(electronRoot, 'lib', target, 'init.js');
|
||||
}
|
||||
|
||||
const electronAPIFile = path.resolve(
|
||||
electronRoot,
|
||||
'lib',
|
||||
loadElectronFromAlternateTarget || target,
|
||||
'api',
|
||||
'exports',
|
||||
'electron.ts'
|
||||
);
|
||||
|
||||
const buildflags = parseBuildflags(cliArgs.buildflags);
|
||||
|
||||
// Shims that stand in for webpack ProvidePlugin. Each target gets the
|
||||
// minimum set of globals it needs; the capture files mirror the originals
|
||||
// under lib/common so the behavior (grab globals before user code can
|
||||
// delete them) is preserved exactly.
|
||||
const inject = [];
|
||||
if (opts.targetDeletesNodeGlobals) {
|
||||
inject.push(path.resolve(__dirname, 'shims', 'node-globals-shim.js'));
|
||||
}
|
||||
if (!alwaysHasNode) {
|
||||
inject.push(path.resolve(__dirname, 'shims', 'browser-globals-shim.js'));
|
||||
}
|
||||
inject.push(path.resolve(__dirname, 'shims', 'promise-shim.js'));
|
||||
|
||||
const result = await esbuild.build({
|
||||
entryPoints: [entry],
|
||||
bundle: true,
|
||||
format: 'iife',
|
||||
platform: alwaysHasNode ? 'node' : 'browser',
|
||||
target: 'es2022',
|
||||
minify,
|
||||
// Preserve class/function names in both development and production so
|
||||
// gin_helper-surfaced constructor names and stack traces stay readable.
|
||||
// (Under webpack this only mattered when terser ran in is_official_build;
|
||||
// esbuild applies the same rename pressure in dev too, so keep it on
|
||||
// unconditionally for consistency.)
|
||||
keepNames: true,
|
||||
sourcemap: false,
|
||||
logLevel: 'warning',
|
||||
metafile: true,
|
||||
write: false,
|
||||
resolveExtensions: ['.ts', '.js'],
|
||||
alias: buildAliases(electronAPIFile, { aliasTimers: !alwaysHasNode }),
|
||||
inject,
|
||||
define: {
|
||||
__non_webpack_require__: 'require'
|
||||
},
|
||||
// Node internal modules we pull through __non_webpack_require__ at runtime.
|
||||
// These must not be bundled — esbuild should leave the literal require()
|
||||
// call alone so the outer Node scope resolves them.
|
||||
external: [
|
||||
'internal/modules/helpers',
|
||||
'internal/modules/run_main',
|
||||
'internal/fs/utils',
|
||||
'internal/util',
|
||||
'internal/validators',
|
||||
'internal/url'
|
||||
],
|
||||
plugins: [
|
||||
internalAliasPlugin(),
|
||||
buildflagPlugin(buildflags, { allowUnknown: printGraph })
|
||||
]
|
||||
});
|
||||
|
||||
if (printGraph) {
|
||||
const inputs = Object.keys(result.metafile.inputs)
|
||||
.filter((p) => !p.includes('node_modules') && !p.startsWith('..'))
|
||||
.map((p) => path.relative(electronRoot, path.resolve(electronRoot, p)));
|
||||
process.stdout.write(JSON.stringify(inputs) + '\n');
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.outputFiles.length !== 1) {
|
||||
throw new Error(`Expected exactly one output file, got ${result.outputFiles.length}`);
|
||||
}
|
||||
|
||||
const wrapped = applyWrappers(
|
||||
result.outputFiles[0].text,
|
||||
{ wrapInitWithProfilingTimeout, wrapInitWithTryCatch },
|
||||
outputFilename
|
||||
);
|
||||
|
||||
await fs.promises.mkdir(outputPath, { recursive: true });
|
||||
await fs.promises.writeFile(path.join(outputPath, outputFilename), wrapped);
|
||||
}
|
||||
|
||||
async function main () {
|
||||
const cliArgs = parseArgs(process.argv.slice(2));
|
||||
if (!cliArgs.config) {
|
||||
console.error('Usage: bundle.js --config <path> [--output-filename X] [--output-path Y] [--mode development|production] [--buildflags path/to/buildflags.h] [--print-graph]');
|
||||
process.exit(1);
|
||||
}
|
||||
const configPath = path.resolve(cliArgs.config);
|
||||
const opts = require(configPath);
|
||||
await buildBundle(opts, cliArgs);
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
4
build/esbuild/configs/browser.js
Normal file
4
build/esbuild/configs/browser.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
target: 'browser',
|
||||
alwaysHasNode: true
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
module.exports = require('./webpack.config.base')({
|
||||
module.exports = {
|
||||
target: 'isolated_renderer',
|
||||
alwaysHasNode: false,
|
||||
wrapInitWithTryCatch: true
|
||||
});
|
||||
};
|
||||
4
build/esbuild/configs/node.js
Normal file
4
build/esbuild/configs/node.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
target: 'node',
|
||||
alwaysHasNode: true
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
module.exports = require('./webpack.config.base')({
|
||||
module.exports = {
|
||||
target: 'preload_realm',
|
||||
alwaysHasNode: false,
|
||||
wrapInitWithProfilingTimeout: true,
|
||||
wrapInitWithTryCatch: true
|
||||
});
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
module.exports = require('./webpack.config.base')({
|
||||
module.exports = {
|
||||
target: 'renderer',
|
||||
alwaysHasNode: true,
|
||||
targetDeletesNodeGlobals: true,
|
||||
wrapInitWithProfilingTimeout: true,
|
||||
wrapInitWithTryCatch: true
|
||||
});
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
module.exports = require('./webpack.config.base')({
|
||||
module.exports = {
|
||||
target: 'sandboxed_renderer',
|
||||
alwaysHasNode: false,
|
||||
wrapInitWithProfilingTimeout: true,
|
||||
wrapInitWithTryCatch: true
|
||||
});
|
||||
};
|
||||
4
build/esbuild/configs/utility.js
Normal file
4
build/esbuild/configs/utility.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
target: 'utility',
|
||||
alwaysHasNode: true
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
module.exports = require('./webpack.config.base')({
|
||||
module.exports = {
|
||||
target: 'worker',
|
||||
loadElectronFromAlternateTarget: 'renderer',
|
||||
alwaysHasNode: true,
|
||||
targetDeletesNodeGlobals: true,
|
||||
wrapInitWithTryCatch: true
|
||||
});
|
||||
};
|
||||
@@ -1,9 +1,9 @@
|
||||
import("../npm.gni")
|
||||
|
||||
template("webpack_build") {
|
||||
assert(defined(invoker.config_file), "Need webpack config file to run")
|
||||
template("esbuild_build") {
|
||||
assert(defined(invoker.config_file), "Need esbuild config file to run")
|
||||
assert(defined(invoker.out_file), "Need output file to run")
|
||||
assert(defined(invoker.inputs), "Need webpack inputs to run")
|
||||
assert(defined(invoker.inputs), "Need esbuild inputs to run")
|
||||
|
||||
npm_action(target_name) {
|
||||
forward_variables_from(invoker,
|
||||
@@ -11,11 +11,14 @@ template("webpack_build") {
|
||||
"deps",
|
||||
"public_deps",
|
||||
])
|
||||
script = "webpack"
|
||||
script = "bundle"
|
||||
|
||||
inputs = [
|
||||
invoker.config_file,
|
||||
"//electron/build/webpack/webpack.config.base.js",
|
||||
"//electron/build/esbuild/bundle.js",
|
||||
"//electron/build/esbuild/shims/node-globals-shim.js",
|
||||
"//electron/build/esbuild/shims/browser-globals-shim.js",
|
||||
"//electron/build/esbuild/shims/promise-shim.js",
|
||||
"//electron/tsconfig.json",
|
||||
"//electron/yarn.lock",
|
||||
"//electron/typings/internal-ambient.d.ts",
|
||||
@@ -34,10 +37,10 @@ template("webpack_build") {
|
||||
get_path_info(invoker.out_file, "file"),
|
||||
"--output-path",
|
||||
rebase_path(get_path_info(invoker.out_file, "dir")),
|
||||
"--env",
|
||||
"buildflags=" + rebase_path("$target_gen_dir/buildflags/buildflags.h"),
|
||||
"--env",
|
||||
"mode=" + mode,
|
||||
"--buildflags",
|
||||
rebase_path("$target_gen_dir/buildflags/buildflags.h"),
|
||||
"--mode",
|
||||
mode,
|
||||
]
|
||||
deps += [ "//electron/buildflags" ]
|
||||
|
||||
18
build/esbuild/shims/browser-globals-shim.js
Normal file
18
build/esbuild/shims/browser-globals-shim.js
Normal file
@@ -0,0 +1,18 @@
|
||||
// Injected into browser-platform bundles (sandboxed_renderer, isolated_renderer,
|
||||
// preload_realm) where Node globals are not implicitly available. Supplies
|
||||
// `Buffer`, `process`, and `global` — replacing webpack's ProvidePlugin
|
||||
// polyfill injection plus webpack 5's built-in `global -> globalThis` rewrite
|
||||
// that `target: 'web'` performed automatically.
|
||||
//
|
||||
// The `buffer` and `process/browser` imports below intentionally use the
|
||||
// npm polyfill packages, not Node's built-in `node:buffer` / `node:process`
|
||||
// modules, because these shims ship into browser-platform bundles that do
|
||||
// not have Node globals available at runtime.
|
||||
|
||||
/* eslint-disable import/order, import/enforce-node-protocol-usage */
|
||||
import { Buffer as _Buffer } from 'buffer';
|
||||
import _process from 'process/browser';
|
||||
|
||||
const _global = globalThis;
|
||||
|
||||
export { _Buffer as Buffer, _process as process, _global as global };
|
||||
14
build/esbuild/shims/node-globals-shim.js
Normal file
14
build/esbuild/shims/node-globals-shim.js
Normal file
@@ -0,0 +1,14 @@
|
||||
// Injected into renderer/worker bundles to replace webpack's ProvidePlugin
|
||||
// that captured `Buffer`, `global`, and `process` before user code could
|
||||
// delete them from the global scope. The Module.wrapper override in
|
||||
// lib/renderer/init.ts re-injects these into user preload scripts later.
|
||||
|
||||
// Rip globals off of globalThis/self/window so they are captured in this
|
||||
// module's closure and retained even if the caller later deletes them.
|
||||
const _global = typeof globalThis !== 'undefined'
|
||||
? globalThis.global
|
||||
: (self || window).global;
|
||||
const _process = _global.process;
|
||||
const _Buffer = _global.Buffer;
|
||||
|
||||
export { _global as global, _process as process, _Buffer as Buffer };
|
||||
7
build/esbuild/shims/promise-shim.js
Normal file
7
build/esbuild/shims/promise-shim.js
Normal file
@@ -0,0 +1,7 @@
|
||||
// Captures the original `Promise` constructor so that userland mutations of
|
||||
// `global.Promise.resolve` do not affect Electron's internal code. Mirrors
|
||||
// webpack's ProvidePlugin reference to lib/common/webpack-globals-provider.
|
||||
|
||||
const _Promise = globalThis.Promise;
|
||||
|
||||
export { _Promise as Promise };
|
||||
@@ -1,5 +1,42 @@
|
||||
import("npm.gni")
|
||||
|
||||
# Runs `tsgo --noEmit` over a tsconfig via the `tsc-check` npm script (which
|
||||
# wraps script/typecheck.js) and writes a stamp on success. Use this to gate
|
||||
# downstream targets on a successful typecheck without emitting JS.
|
||||
template("typescript_check") {
|
||||
assert(defined(invoker.tsconfig), "Need tsconfig name to run")
|
||||
assert(defined(invoker.sources), "Need tsc sources to run")
|
||||
|
||||
npm_action(target_name) {
|
||||
forward_variables_from(invoker,
|
||||
[
|
||||
"deps",
|
||||
"public_deps",
|
||||
])
|
||||
script = "tsc-check"
|
||||
|
||||
sources = invoker.sources
|
||||
inputs = [
|
||||
invoker.tsconfig,
|
||||
"//electron/tsconfig.json",
|
||||
"//electron/yarn.lock",
|
||||
"//electron/script/typecheck.js",
|
||||
"//electron/typings/internal-ambient.d.ts",
|
||||
"//electron/typings/internal-electron.d.ts",
|
||||
]
|
||||
|
||||
stamp_file = "$target_gen_dir/$target_name.stamp"
|
||||
outputs = [ stamp_file ]
|
||||
|
||||
args = [
|
||||
"--tsconfig",
|
||||
rebase_path(invoker.tsconfig),
|
||||
"--stamp",
|
||||
rebase_path(stamp_file),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
template("typescript_build") {
|
||||
assert(defined(invoker.tsconfig), "Need tsconfig name to run")
|
||||
assert(defined(invoker.sources), "Need tsc sources to run")
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
const webpack = require('webpack');
|
||||
const WrapperPlugin = require('wrapper-webpack-plugin');
|
||||
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const electronRoot = path.resolve(__dirname, '../..');
|
||||
|
||||
class AccessDependenciesPlugin {
|
||||
apply (compiler) {
|
||||
compiler.hooks.compilation.tap('AccessDependenciesPlugin', compilation => {
|
||||
compilation.hooks.finishModules.tap('AccessDependenciesPlugin', modules => {
|
||||
const filePaths = modules.map(m => m.resource).filter(p => p).map(p => path.relative(electronRoot, p));
|
||||
console.info(JSON.stringify(filePaths));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ({
|
||||
alwaysHasNode,
|
||||
loadElectronFromAlternateTarget,
|
||||
targetDeletesNodeGlobals,
|
||||
target,
|
||||
wrapInitWithProfilingTimeout,
|
||||
wrapInitWithTryCatch
|
||||
}) => {
|
||||
let entry = path.resolve(electronRoot, 'lib', target, 'init.ts');
|
||||
if (!fs.existsSync(entry)) {
|
||||
entry = path.resolve(electronRoot, 'lib', target, 'init.js');
|
||||
}
|
||||
|
||||
const electronAPIFile = path.resolve(electronRoot, 'lib', loadElectronFromAlternateTarget || target, 'api', 'exports', 'electron.ts');
|
||||
|
||||
return (env = {}, argv = {}) => {
|
||||
const onlyPrintingGraph = !!env.PRINT_WEBPACK_GRAPH;
|
||||
const outputFilename = argv['output-filename'] || `${target}.bundle.js`;
|
||||
|
||||
const defines = {
|
||||
BUILDFLAG: onlyPrintingGraph ? '(a => a)' : ''
|
||||
};
|
||||
|
||||
if (env.buildflags) {
|
||||
const flagFile = fs.readFileSync(env.buildflags, 'utf8');
|
||||
for (const line of flagFile.split(/(\r\n|\r|\n)/g)) {
|
||||
const flagMatch = line.match(/#define BUILDFLAG_INTERNAL_(.+?)\(\) \(([01])\)/);
|
||||
if (flagMatch) {
|
||||
const [, flagName, flagValue] = flagMatch;
|
||||
defines[flagName] = JSON.stringify(Boolean(parseInt(flagValue, 10)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ignoredModules = [];
|
||||
|
||||
const plugins = [];
|
||||
|
||||
if (onlyPrintingGraph) {
|
||||
plugins.push(new AccessDependenciesPlugin());
|
||||
}
|
||||
|
||||
if (targetDeletesNodeGlobals) {
|
||||
plugins.push(new webpack.ProvidePlugin({
|
||||
Buffer: ['@electron/internal/common/webpack-provider', 'Buffer'],
|
||||
global: ['@electron/internal/common/webpack-provider', '_global'],
|
||||
process: ['@electron/internal/common/webpack-provider', 'process']
|
||||
}));
|
||||
}
|
||||
|
||||
// Webpack 5 no longer polyfills process or Buffer.
|
||||
if (!alwaysHasNode) {
|
||||
plugins.push(new webpack.ProvidePlugin({
|
||||
Buffer: ['buffer', 'Buffer'],
|
||||
process: 'process/browser'
|
||||
}));
|
||||
}
|
||||
|
||||
plugins.push(new webpack.ProvidePlugin({
|
||||
Promise: ['@electron/internal/common/webpack-globals-provider', 'Promise']
|
||||
}));
|
||||
|
||||
plugins.push(new webpack.DefinePlugin(defines));
|
||||
|
||||
if (wrapInitWithProfilingTimeout) {
|
||||
plugins.push(new WrapperPlugin({
|
||||
header: 'function ___electron_webpack_init__() {',
|
||||
footer: `
|
||||
};
|
||||
if ((globalThis.process || binding.process).argv.includes("--profile-electron-init")) {
|
||||
setTimeout(___electron_webpack_init__, 0);
|
||||
} else {
|
||||
___electron_webpack_init__();
|
||||
}`
|
||||
}));
|
||||
}
|
||||
|
||||
if (wrapInitWithTryCatch) {
|
||||
plugins.push(new WrapperPlugin({
|
||||
header: 'try {',
|
||||
footer: `
|
||||
} catch (err) {
|
||||
console.error('Electron ${outputFilename} script failed to run');
|
||||
console.error(err);
|
||||
}`
|
||||
}));
|
||||
}
|
||||
|
||||
return {
|
||||
mode: 'development',
|
||||
devtool: false,
|
||||
entry,
|
||||
target: alwaysHasNode ? 'node' : 'web',
|
||||
output: {
|
||||
filename: outputFilename
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@electron/internal': path.resolve(electronRoot, 'lib'),
|
||||
electron$: electronAPIFile,
|
||||
'electron/main$': electronAPIFile,
|
||||
'electron/renderer$': electronAPIFile,
|
||||
'electron/common$': electronAPIFile,
|
||||
'electron/utility$': electronAPIFile,
|
||||
// Force timers to resolve to our own shim that doesn't use window.postMessage
|
||||
timers: path.resolve(electronRoot, 'lib', 'common', 'timers-shim.ts')
|
||||
},
|
||||
extensions: ['.ts', '.js'],
|
||||
fallback: {
|
||||
// We provide our own "timers" import above, any usage of setImmediate inside
|
||||
// one of our renderer bundles should import it from the 'timers' package
|
||||
setImmediate: false
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
test: (moduleName) => !onlyPrintingGraph && ignoredModules.includes(moduleName),
|
||||
loader: 'null-loader'
|
||||
}, {
|
||||
test: /\.ts$/,
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
configFile: path.resolve(electronRoot, 'tsconfig.electron.json'),
|
||||
transpileOnly: onlyPrintingGraph,
|
||||
ignoreDiagnostics: [
|
||||
// File '{0}' is not under 'rootDir' '{1}'.
|
||||
6059,
|
||||
// Private field '{0}' must be declared in an enclosing class.
|
||||
1111
|
||||
]
|
||||
}
|
||||
}]
|
||||
},
|
||||
node: {
|
||||
__dirname: false,
|
||||
__filename: false
|
||||
},
|
||||
optimization: {
|
||||
minimize: env.mode === 'production',
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
keep_classnames: true,
|
||||
keep_fnames: true
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
plugins
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -1,4 +0,0 @@
|
||||
module.exports = require('./webpack.config.base')({
|
||||
target: 'browser',
|
||||
alwaysHasNode: true
|
||||
});
|
||||
@@ -1,4 +0,0 @@
|
||||
module.exports = require('./webpack.config.base')({
|
||||
target: 'node',
|
||||
alwaysHasNode: true
|
||||
});
|
||||
@@ -1,4 +0,0 @@
|
||||
module.exports = require('./webpack.config.base')({
|
||||
target: 'utility',
|
||||
alwaysHasNode: true
|
||||
});
|
||||
@@ -174,62 +174,10 @@ auto_filenames = {
|
||||
"docs/api/structures/window-session-end-event.md",
|
||||
]
|
||||
|
||||
sandbox_bundle_deps = [
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/deprecate.ts",
|
||||
"lib/common/ipc-messages.ts",
|
||||
"lib/common/timers-shim.ts",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/shared-texture.ts",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/api/web-utils.ts",
|
||||
"lib/renderer/common-init.ts",
|
||||
"lib/renderer/inspector.ts",
|
||||
"lib/renderer/ipc-native-setup.ts",
|
||||
"lib/renderer/ipc-renderer-bindings.ts",
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
"lib/renderer/ipc-renderer-internal.ts",
|
||||
"lib/renderer/security-warnings.ts",
|
||||
"lib/renderer/web-frame-init.ts",
|
||||
"lib/renderer/web-view/guest-view-internal.ts",
|
||||
"lib/renderer/web-view/web-view-attributes.ts",
|
||||
"lib/renderer/web-view/web-view-constants.ts",
|
||||
"lib/renderer/web-view/web-view-element.ts",
|
||||
"lib/renderer/web-view/web-view-impl.ts",
|
||||
"lib/renderer/web-view/web-view-init.ts",
|
||||
"lib/renderer/window-setup.ts",
|
||||
"lib/sandboxed_renderer/api/exports/electron.ts",
|
||||
"lib/sandboxed_renderer/api/module-list.ts",
|
||||
"lib/sandboxed_renderer/init.ts",
|
||||
"lib/sandboxed_renderer/pre-init.ts",
|
||||
"lib/sandboxed_renderer/preload.ts",
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
"tsconfig.json",
|
||||
"typings/internal-ambient.d.ts",
|
||||
"typings/internal-electron.d.ts",
|
||||
]
|
||||
|
||||
isolated_bundle_deps = [
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/isolated_renderer/init.ts",
|
||||
"lib/renderer/web-view/web-view-attributes.ts",
|
||||
"lib/renderer/web-view/web-view-constants.ts",
|
||||
"lib/renderer/web-view/web-view-element.ts",
|
||||
"lib/renderer/web-view/web-view-impl.ts",
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
"tsconfig.json",
|
||||
"typings/internal-ambient.d.ts",
|
||||
"typings/internal-electron.d.ts",
|
||||
]
|
||||
|
||||
browser_bundle_deps = [
|
||||
typecheck_sources = [
|
||||
"build/esbuild/shims/browser-globals-shim.js",
|
||||
"build/esbuild/shims/node-globals-shim.js",
|
||||
"build/esbuild/shims/promise-shim.js",
|
||||
"lib/browser/api/app.ts",
|
||||
"lib/browser/api/auto-updater.ts",
|
||||
"lib/browser/api/auto-updater/auto-updater-msix.ts",
|
||||
@@ -300,8 +248,189 @@ auto_filenames = {
|
||||
"lib/common/deprecate.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/ipc-messages.ts",
|
||||
"lib/common/timers-shim.ts",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/isolated_renderer/init.ts",
|
||||
"lib/node/asar-fs-wrapper.ts",
|
||||
"lib/node/init.ts",
|
||||
"lib/preload_realm/api/exports/electron.ts",
|
||||
"lib/preload_realm/api/module-list.ts",
|
||||
"lib/preload_realm/init.ts",
|
||||
"lib/renderer/api/clipboard.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
"lib/renderer/api/exports/electron.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/module-list.ts",
|
||||
"lib/renderer/api/shared-texture.ts",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/api/web-utils.ts",
|
||||
"lib/renderer/common-init.ts",
|
||||
"lib/renderer/init.ts",
|
||||
"lib/renderer/inspector.ts",
|
||||
"lib/renderer/ipc-native-setup.ts",
|
||||
"lib/renderer/ipc-renderer-bindings.ts",
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
"lib/renderer/ipc-renderer-internal.ts",
|
||||
"lib/renderer/security-warnings.ts",
|
||||
"lib/renderer/web-frame-init.ts",
|
||||
"lib/renderer/web-view/guest-view-internal.ts",
|
||||
"lib/renderer/web-view/web-view-attributes.ts",
|
||||
"lib/renderer/web-view/web-view-constants.ts",
|
||||
"lib/renderer/web-view/web-view-element.ts",
|
||||
"lib/renderer/web-view/web-view-impl.ts",
|
||||
"lib/renderer/web-view/web-view-init.ts",
|
||||
"lib/renderer/window-setup.ts",
|
||||
"lib/sandboxed_renderer/api/exports/electron.ts",
|
||||
"lib/sandboxed_renderer/api/module-list.ts",
|
||||
"lib/sandboxed_renderer/init.ts",
|
||||
"lib/sandboxed_renderer/pre-init.ts",
|
||||
"lib/sandboxed_renderer/preload.ts",
|
||||
"lib/utility/api/exports/electron.ts",
|
||||
"lib/utility/api/module-list.ts",
|
||||
"lib/utility/api/net.ts",
|
||||
"lib/utility/init.ts",
|
||||
"lib/utility/parent-port.ts",
|
||||
"lib/worker/init.ts",
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
"tsconfig.json",
|
||||
"typings/internal-ambient.d.ts",
|
||||
"typings/internal-electron.d.ts",
|
||||
]
|
||||
|
||||
sandbox_bundle_deps = [
|
||||
"build/esbuild/shims/browser-globals-shim.js",
|
||||
"build/esbuild/shims/promise-shim.js",
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/deprecate.ts",
|
||||
"lib/common/ipc-messages.ts",
|
||||
"lib/common/timers-shim.ts",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/shared-texture.ts",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/api/web-utils.ts",
|
||||
"lib/renderer/common-init.ts",
|
||||
"lib/renderer/inspector.ts",
|
||||
"lib/renderer/ipc-native-setup.ts",
|
||||
"lib/renderer/ipc-renderer-bindings.ts",
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
"lib/renderer/ipc-renderer-internal.ts",
|
||||
"lib/renderer/security-warnings.ts",
|
||||
"lib/renderer/web-frame-init.ts",
|
||||
"lib/renderer/web-view/guest-view-internal.ts",
|
||||
"lib/renderer/web-view/web-view-attributes.ts",
|
||||
"lib/renderer/web-view/web-view-constants.ts",
|
||||
"lib/renderer/web-view/web-view-element.ts",
|
||||
"lib/renderer/web-view/web-view-impl.ts",
|
||||
"lib/renderer/web-view/web-view-init.ts",
|
||||
"lib/renderer/window-setup.ts",
|
||||
"lib/sandboxed_renderer/api/exports/electron.ts",
|
||||
"lib/sandboxed_renderer/api/module-list.ts",
|
||||
"lib/sandboxed_renderer/init.ts",
|
||||
"lib/sandboxed_renderer/pre-init.ts",
|
||||
"lib/sandboxed_renderer/preload.ts",
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
"tsconfig.json",
|
||||
"typings/internal-ambient.d.ts",
|
||||
"typings/internal-electron.d.ts",
|
||||
]
|
||||
|
||||
isolated_bundle_deps = [
|
||||
"build/esbuild/shims/browser-globals-shim.js",
|
||||
"build/esbuild/shims/promise-shim.js",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/isolated_renderer/init.ts",
|
||||
"lib/renderer/web-view/web-view-attributes.ts",
|
||||
"lib/renderer/web-view/web-view-constants.ts",
|
||||
"lib/renderer/web-view/web-view-element.ts",
|
||||
"lib/renderer/web-view/web-view-impl.ts",
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
"tsconfig.json",
|
||||
"typings/internal-ambient.d.ts",
|
||||
"typings/internal-electron.d.ts",
|
||||
]
|
||||
|
||||
browser_bundle_deps = [
|
||||
"build/esbuild/shims/promise-shim.js",
|
||||
"lib/browser/api/app.ts",
|
||||
"lib/browser/api/auto-updater.ts",
|
||||
"lib/browser/api/auto-updater/auto-updater-msix.ts",
|
||||
"lib/browser/api/auto-updater/auto-updater-native.ts",
|
||||
"lib/browser/api/auto-updater/auto-updater-win.ts",
|
||||
"lib/browser/api/auto-updater/msix-update-win.ts",
|
||||
"lib/browser/api/auto-updater/squirrel-update-win.ts",
|
||||
"lib/browser/api/base-window.ts",
|
||||
"lib/browser/api/browser-view.ts",
|
||||
"lib/browser/api/browser-window.ts",
|
||||
"lib/browser/api/clipboard.ts",
|
||||
"lib/browser/api/content-tracing.ts",
|
||||
"lib/browser/api/crash-reporter.ts",
|
||||
"lib/browser/api/desktop-capturer.ts",
|
||||
"lib/browser/api/dialog.ts",
|
||||
"lib/browser/api/exports/electron.ts",
|
||||
"lib/browser/api/global-shortcut.ts",
|
||||
"lib/browser/api/in-app-purchase.ts",
|
||||
"lib/browser/api/ipc-main.ts",
|
||||
"lib/browser/api/menu-item-roles.ts",
|
||||
"lib/browser/api/menu-item.ts",
|
||||
"lib/browser/api/menu-utils.ts",
|
||||
"lib/browser/api/menu.ts",
|
||||
"lib/browser/api/message-channel.ts",
|
||||
"lib/browser/api/module-list.ts",
|
||||
"lib/browser/api/native-theme.ts",
|
||||
"lib/browser/api/net-fetch.ts",
|
||||
"lib/browser/api/net-log.ts",
|
||||
"lib/browser/api/net.ts",
|
||||
"lib/browser/api/notification.ts",
|
||||
"lib/browser/api/power-monitor.ts",
|
||||
"lib/browser/api/power-save-blocker.ts",
|
||||
"lib/browser/api/protocol.ts",
|
||||
"lib/browser/api/push-notifications.ts",
|
||||
"lib/browser/api/safe-storage.ts",
|
||||
"lib/browser/api/screen.ts",
|
||||
"lib/browser/api/service-worker-main.ts",
|
||||
"lib/browser/api/session.ts",
|
||||
"lib/browser/api/share-menu.ts",
|
||||
"lib/browser/api/shared-texture.ts",
|
||||
"lib/browser/api/system-preferences.ts",
|
||||
"lib/browser/api/touch-bar.ts",
|
||||
"lib/browser/api/tray.ts",
|
||||
"lib/browser/api/utility-process.ts",
|
||||
"lib/browser/api/view.ts",
|
||||
"lib/browser/api/views/image-view.ts",
|
||||
"lib/browser/api/web-contents-view.ts",
|
||||
"lib/browser/api/web-contents.ts",
|
||||
"lib/browser/api/web-frame-main.ts",
|
||||
"lib/browser/default-menu.ts",
|
||||
"lib/browser/devtools.ts",
|
||||
"lib/browser/guest-view-manager.ts",
|
||||
"lib/browser/guest-window-manager.ts",
|
||||
"lib/browser/init.ts",
|
||||
"lib/browser/ipc-dispatch.ts",
|
||||
"lib/browser/ipc-main-impl.ts",
|
||||
"lib/browser/ipc-main-internal-utils.ts",
|
||||
"lib/browser/ipc-main-internal.ts",
|
||||
"lib/browser/message-port-main.ts",
|
||||
"lib/browser/parse-features-string.ts",
|
||||
"lib/browser/rpc-server.ts",
|
||||
"lib/browser/web-view-events.ts",
|
||||
"lib/common/api/module-list.ts",
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/api/net-client-request.ts",
|
||||
"lib/common/api/shell.ts",
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/deprecate.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/ipc-messages.ts",
|
||||
"lib/common/timers-shim.ts",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
"tsconfig.json",
|
||||
@@ -310,6 +439,8 @@ auto_filenames = {
|
||||
]
|
||||
|
||||
renderer_bundle_deps = [
|
||||
"build/esbuild/shims/node-globals-shim.js",
|
||||
"build/esbuild/shims/promise-shim.js",
|
||||
"lib/common/api/module-list.ts",
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/api/shell.ts",
|
||||
@@ -317,8 +448,8 @@ auto_filenames = {
|
||||
"lib/common/deprecate.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/ipc-messages.ts",
|
||||
"lib/common/timers-shim.ts",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/common/webpack-provider.ts",
|
||||
"lib/renderer/api/clipboard.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
@@ -352,6 +483,8 @@ auto_filenames = {
|
||||
]
|
||||
|
||||
worker_bundle_deps = [
|
||||
"build/esbuild/shims/node-globals-shim.js",
|
||||
"build/esbuild/shims/promise-shim.js",
|
||||
"lib/common/api/module-list.ts",
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/api/shell.ts",
|
||||
@@ -359,7 +492,7 @@ auto_filenames = {
|
||||
"lib/common/deprecate.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/ipc-messages.ts",
|
||||
"lib/common/webpack-provider.ts",
|
||||
"lib/common/timers-shim.ts",
|
||||
"lib/renderer/api/clipboard.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
@@ -381,6 +514,7 @@ auto_filenames = {
|
||||
]
|
||||
|
||||
node_bundle_deps = [
|
||||
"build/esbuild/shims/promise-shim.js",
|
||||
"lib/node/asar-fs-wrapper.ts",
|
||||
"lib/node/init.ts",
|
||||
"package.json",
|
||||
@@ -391,6 +525,7 @@ auto_filenames = {
|
||||
]
|
||||
|
||||
utility_bundle_deps = [
|
||||
"build/esbuild/shims/promise-shim.js",
|
||||
"lib/browser/api/net-fetch.ts",
|
||||
"lib/browser/api/system-preferences.ts",
|
||||
"lib/browser/message-port-main.ts",
|
||||
@@ -398,7 +533,7 @@ auto_filenames = {
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/deprecate.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
"lib/common/timers-shim.ts",
|
||||
"lib/utility/api/exports/electron.ts",
|
||||
"lib/utility/api/module-list.ts",
|
||||
"lib/utility/api/net.ts",
|
||||
@@ -412,10 +547,11 @@ auto_filenames = {
|
||||
]
|
||||
|
||||
preload_realm_bundle_deps = [
|
||||
"build/esbuild/shims/browser-globals-shim.js",
|
||||
"build/esbuild/shims/promise-shim.js",
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/ipc-messages.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
"lib/preload_realm/api/exports/electron.ts",
|
||||
"lib/preload_realm/api/module-list.ts",
|
||||
"lib/preload_realm/init.ts",
|
||||
|
||||
@@ -5,23 +5,27 @@ import type { ClientRequestConstructorOptions } from 'electron/main';
|
||||
|
||||
const { isOnline } = process._linkedBinding('electron_common_net');
|
||||
|
||||
export function request (options: ClientRequestConstructorOptions | string, callback?: (message: IncomingMessage) => void) {
|
||||
function request (options: ClientRequestConstructorOptions | string, callback?: (message: IncomingMessage) => void) {
|
||||
if (!app.isReady()) {
|
||||
throw new Error('net module can only be used after app is ready');
|
||||
}
|
||||
return new ClientRequest(options, callback);
|
||||
}
|
||||
|
||||
export function fetch (input: RequestInfo, init?: RequestInit): Promise<Response> {
|
||||
function fetch (input: RequestInfo, init?: RequestInit): Promise<Response> {
|
||||
return session.defaultSession.fetch(input, init);
|
||||
}
|
||||
|
||||
export function resolveHost (host: string, options?: Electron.ResolveHostOptions): Promise<Electron.ResolvedHost> {
|
||||
function resolveHost (host: string, options?: Electron.ResolveHostOptions): Promise<Electron.ResolvedHost> {
|
||||
return session.defaultSession.resolveHost(host, options);
|
||||
}
|
||||
|
||||
exports.isOnline = isOnline;
|
||||
|
||||
Object.defineProperty(exports, 'online', {
|
||||
get: () => isOnline()
|
||||
});
|
||||
module.exports = {
|
||||
request,
|
||||
fetch,
|
||||
resolveHost,
|
||||
isOnline,
|
||||
get online () {
|
||||
return isOnline();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -181,7 +181,7 @@ delete process.appCodeLoaded;
|
||||
if (packagePath) {
|
||||
// Finally load app's main.js and transfer control to C++.
|
||||
if ((packageJson.type === 'module' && !mainStartupScript.endsWith('.cjs')) || mainStartupScript.endsWith('.mjs')) {
|
||||
const { runEntryPointWithESMLoader } = __non_webpack_require__('internal/modules/run_main') as typeof import('@node/lib/internal/modules/run_main');
|
||||
const { runEntryPointWithESMLoader } = __non_webpack_require__('internal/modules/run_main');
|
||||
const main = (require('url') as typeof url).pathToFileURL(path.join(packagePath, mainStartupScript));
|
||||
runEntryPointWithESMLoader(async (cascadedLoader: any) => {
|
||||
try {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import timers = require('timers');
|
||||
import * as timers from 'timers';
|
||||
import * as util from 'util';
|
||||
|
||||
import type * as stream from 'stream';
|
||||
@@ -41,15 +41,15 @@ function wrap <T extends AnyFn> (func: T, wrapper: (fn: AnyFn) => T) {
|
||||
// initiatively activate the uv loop once process.nextTick and setImmediate is
|
||||
// called.
|
||||
process.nextTick = wrapWithActivateUvLoop(process.nextTick);
|
||||
global.setImmediate = timers.setImmediate = wrapWithActivateUvLoop(timers.setImmediate);
|
||||
global.setImmediate = wrapWithActivateUvLoop(timers.setImmediate);
|
||||
global.clearImmediate = timers.clearImmediate;
|
||||
|
||||
// setTimeout needs to update the polling timeout of the event loop, when
|
||||
// called under Chromium's event loop the node's event loop won't get a chance
|
||||
// to update the timeout, so we have to force the node's event loop to
|
||||
// recalculate the timeout in the process.
|
||||
timers.setTimeout = wrapWithActivateUvLoop(timers.setTimeout);
|
||||
timers.setInterval = wrapWithActivateUvLoop(timers.setInterval);
|
||||
const wrappedSetTimeout = wrapWithActivateUvLoop(timers.setTimeout);
|
||||
const wrappedSetInterval = wrapWithActivateUvLoop(timers.setInterval);
|
||||
|
||||
// Update the global version of the timer apis to use the above wrapper
|
||||
// only in the process that runs node event loop alongside chromium
|
||||
@@ -57,8 +57,8 @@ timers.setInterval = wrapWithActivateUvLoop(timers.setInterval);
|
||||
// are deleted in these processes, see renderer/init.js for reference.
|
||||
if (process.type === 'browser' ||
|
||||
process.type === 'utility') {
|
||||
global.setTimeout = timers.setTimeout;
|
||||
global.setInterval = timers.setInterval;
|
||||
global.setTimeout = wrappedSetTimeout;
|
||||
global.setInterval = wrappedSetInterval;
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Drop-in replacement for timers-browserify@1.4.2.
|
||||
// Provides the Node.js 'timers' API surface for renderer/web webpack bundles
|
||||
// Provides the Node.js 'timers' API surface for renderer/web bundles
|
||||
// without relying on window.postMessage (which the newer timers-browserify 2.x
|
||||
// polyfill uses and can interfere with Electron IPC).
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
// Captures original globals into a scope to ensure that userland modifications do
|
||||
// not impact Electron. Note that users doing:
|
||||
//
|
||||
// global.Promise.resolve = myFn
|
||||
//
|
||||
// Will mutate this captured one as well and that is OK.
|
||||
|
||||
export const Promise = global.Promise;
|
||||
@@ -1,18 +0,0 @@
|
||||
// This file provides the global, process and Buffer variables to internal
|
||||
// Electron code once they have been deleted from the global scope.
|
||||
//
|
||||
// It does this through the ProvidePlugin in the webpack.config.base.js file
|
||||
// Check out the Module.wrapper override in renderer/init.ts for more
|
||||
// information on how this works and why we need it
|
||||
|
||||
// Rip global off of window (which is also global) so that webpack doesn't
|
||||
// auto replace it with a looped reference to this file
|
||||
const _global = typeof globalThis !== 'undefined' ? globalThis.global : (self || window).global;
|
||||
const process = _global.process;
|
||||
const Buffer = _global.Buffer;
|
||||
|
||||
export {
|
||||
_global,
|
||||
process,
|
||||
Buffer
|
||||
};
|
||||
@@ -52,20 +52,20 @@ const {
|
||||
getValidatedPath,
|
||||
getOptions,
|
||||
getDirent
|
||||
} = __non_webpack_require__('internal/fs/utils') as typeof import('@node/lib/internal/fs/utils');
|
||||
} = __non_webpack_require__('internal/fs/utils');
|
||||
|
||||
const {
|
||||
assignFunctionName
|
||||
} = __non_webpack_require__('internal/util') as typeof import('@node/lib/internal/util');
|
||||
} = __non_webpack_require__('internal/util');
|
||||
|
||||
const {
|
||||
validateBoolean,
|
||||
validateFunction
|
||||
} = __non_webpack_require__('internal/validators') as typeof import('@node/lib/internal/validators');
|
||||
} = __non_webpack_require__('internal/validators');
|
||||
|
||||
// In the renderer node internals use the node global URL but we do not set that to be
|
||||
// the global URL instance. We need to do instanceof checks against the internal URL impl
|
||||
const { URL: NodeURL } = __non_webpack_require__('internal/url') as typeof import('@node/lib/internal/url');
|
||||
// the global URL instance. We need to do instanceof checks against the internal URL impl.
|
||||
const { URL: NodeURL } = __non_webpack_require__('internal/url');
|
||||
|
||||
// Separate asar package's path from full path.
|
||||
const splitPath = (archivePathOrBuffer: string | Buffer | URL) => {
|
||||
|
||||
@@ -29,8 +29,9 @@ Module._load = function (request: string) {
|
||||
// code with JavaScript.
|
||||
//
|
||||
// Note 3: We provide the equivalent extra variables internally through the
|
||||
// webpack ProvidePlugin in webpack.config.base.js. If you add any extra
|
||||
// variables to this wrapper please ensure to update that plugin as well.
|
||||
// esbuild inject shim in build/esbuild/shims/node-globals-shim.js. If you
|
||||
// add any extra variables to this wrapper please ensure to update that shim
|
||||
// as well.
|
||||
Module.wrapper = [
|
||||
'(function (exports, require, module, __filename, __dirname, process, global, Buffer) { ' +
|
||||
// By running the code in a new closure, it would be possible for the module
|
||||
@@ -65,9 +66,9 @@ require('@electron/internal/renderer/common-init');
|
||||
|
||||
if (nodeIntegration) {
|
||||
// Export node bindings to global.
|
||||
const { makeRequireFunction } = __non_webpack_require__('internal/modules/helpers') as typeof import('@node/lib/internal/modules/helpers');
|
||||
const { makeRequireFunction } = __non_webpack_require__('internal/modules/helpers');
|
||||
global.module = new Module('electron/js2c/renderer_init');
|
||||
global.require = makeRequireFunction(global.module) as NodeRequire;
|
||||
global.require = makeRequireFunction(global.module);
|
||||
|
||||
// Set the __filename to the path of html file if it is file: protocol.
|
||||
if (window.location.protocol === 'file:') {
|
||||
@@ -152,7 +153,7 @@ if (cjsPreloads.length) {
|
||||
}
|
||||
}
|
||||
if (esmPreloads.length) {
|
||||
const { runEntryPointWithESMLoader } = __non_webpack_require__('internal/modules/run_main') as typeof import('@node/lib/internal/modules/run_main');
|
||||
const { runEntryPointWithESMLoader } = __non_webpack_require__('internal/modules/run_main');
|
||||
|
||||
runEntryPointWithESMLoader(async (cascadedLoader: any) => {
|
||||
// Load the preload scripts.
|
||||
|
||||
@@ -5,18 +5,20 @@ import type { ClientRequestConstructorOptions, IncomingMessage } from 'electron/
|
||||
|
||||
const { isOnline, resolveHost } = process._linkedBinding('electron_common_net');
|
||||
|
||||
export function request (options: ClientRequestConstructorOptions | string, callback?: (message: IncomingMessage) => void) {
|
||||
function request (options: ClientRequestConstructorOptions | string, callback?: (message: IncomingMessage) => void) {
|
||||
return new ClientRequest(options, callback);
|
||||
}
|
||||
|
||||
export function fetch (input: RequestInfo, init?: RequestInit): Promise<Response> {
|
||||
function fetch (input: RequestInfo, init?: RequestInit): Promise<Response> {
|
||||
return fetchWithSession(input, init, undefined, request);
|
||||
}
|
||||
|
||||
exports.resolveHost = resolveHost;
|
||||
|
||||
exports.isOnline = isOnline;
|
||||
|
||||
Object.defineProperty(exports, 'online', {
|
||||
get: () => isOnline()
|
||||
});
|
||||
module.exports = {
|
||||
request,
|
||||
fetch,
|
||||
resolveHost,
|
||||
isOnline,
|
||||
get online () {
|
||||
return isOnline();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -36,7 +36,7 @@ parentPort.on('removeListener', (name: string) => {
|
||||
});
|
||||
|
||||
// Finally load entry script.
|
||||
const { runEntryPointWithESMLoader } = __non_webpack_require__('internal/modules/run_main') as typeof import('@node/lib/internal/modules/run_main');
|
||||
const { runEntryPointWithESMLoader } = __non_webpack_require__('internal/modules/run_main');
|
||||
const mainEntry = pathToFileURL(entryScript);
|
||||
|
||||
runEntryPointWithESMLoader(async (cascadedLoader: any) => {
|
||||
|
||||
@@ -13,9 +13,9 @@ require('@electron/internal/common/init');
|
||||
const { hasSwitch, getSwitchValue } = process._linkedBinding('electron_common_command_line');
|
||||
|
||||
// Export node bindings to global.
|
||||
const { makeRequireFunction } = __non_webpack_require__('internal/modules/helpers') as typeof import('@node/lib/internal/modules/helpers');
|
||||
const { makeRequireFunction } = __non_webpack_require__('internal/modules/helpers');
|
||||
global.module = new Module('electron/js2c/worker_init');
|
||||
global.require = makeRequireFunction(global.module) as NodeRequire;
|
||||
global.require = makeRequireFunction(global.module);
|
||||
|
||||
// See WebWorkerObserver::WorkerScriptReadyForEvaluation.
|
||||
if ((globalThis as any).blinkfetch) {
|
||||
|
||||
12
package.json
12
package.json
@@ -23,10 +23,12 @@
|
||||
"@types/temp": "^0.9.4",
|
||||
"@typescript-eslint/eslint-plugin": "^8.32.1",
|
||||
"@typescript-eslint/parser": "^8.7.0",
|
||||
"@typescript/native-preview": "^7.0.0-dev.20260324.1",
|
||||
"@xmldom/xmldom": "^0.8.11",
|
||||
"buffer": "^6.0.3",
|
||||
"chalk": "^4.1.0",
|
||||
"check-for-leaks": "^1.2.1",
|
||||
"esbuild": "^0.25.0",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
@@ -43,20 +45,15 @@
|
||||
"markdownlint-cli2": "^0.18.0",
|
||||
"minimist": "^1.2.8",
|
||||
"node-gyp": "^11.4.2",
|
||||
"null-loader": "^4.0.1",
|
||||
"pre-flight": "^2.0.0",
|
||||
"process": "^0.11.10",
|
||||
"semver": "^7.6.3",
|
||||
"stream-json": "^1.9.1",
|
||||
"tap-xunit": "^2.4.1",
|
||||
"temp": "^0.9.4",
|
||||
"ts-loader": "^8.0.2",
|
||||
"ts-node": "6.2.0",
|
||||
"typescript": "^5.8.3",
|
||||
"url": "^0.11.4",
|
||||
"webpack": "^5.104.1",
|
||||
"webpack-cli": "^6.0.1",
|
||||
"wrapper-webpack-plugin": "^2.2.0",
|
||||
"yaml": "^2.8.1"
|
||||
},
|
||||
"private": true,
|
||||
@@ -93,8 +90,9 @@
|
||||
"repl": "node ./script/start.js --interactive",
|
||||
"start": "node ./script/start.js",
|
||||
"test": "node ./script/spec-runner.js",
|
||||
"tsc": "tsc",
|
||||
"webpack": "webpack"
|
||||
"tsc": "tsgo",
|
||||
"tsc-check": "node script/typecheck.js",
|
||||
"bundle": "node build/esbuild/bundle.js"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "Electron Community",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as cp from 'node:child_process';
|
||||
import * as fs from 'node:fs';
|
||||
import * as os from 'node:os';
|
||||
import * as path from 'node:path';
|
||||
|
||||
const rootPath = path.resolve(__dirname, '..');
|
||||
@@ -16,52 +15,24 @@ const allDocs = fs.readdirSync(path.resolve(__dirname, '../docs/api'))
|
||||
const typingFiles = fs.readdirSync(path.resolve(__dirname, '../typings')).map(child => `typings/${child}`);
|
||||
|
||||
const main = async () => {
|
||||
const webpackTargets = [
|
||||
{
|
||||
name: 'sandbox_bundle_deps',
|
||||
config: 'webpack.config.sandboxed_renderer.js'
|
||||
},
|
||||
{
|
||||
name: 'isolated_bundle_deps',
|
||||
config: 'webpack.config.isolated_renderer.js'
|
||||
},
|
||||
{
|
||||
name: 'browser_bundle_deps',
|
||||
config: 'webpack.config.browser.js'
|
||||
},
|
||||
{
|
||||
name: 'renderer_bundle_deps',
|
||||
config: 'webpack.config.renderer.js'
|
||||
},
|
||||
{
|
||||
name: 'worker_bundle_deps',
|
||||
config: 'webpack.config.worker.js'
|
||||
},
|
||||
{
|
||||
name: 'node_bundle_deps',
|
||||
config: 'webpack.config.node.js'
|
||||
},
|
||||
{
|
||||
name: 'utility_bundle_deps',
|
||||
config: 'webpack.config.utility.js'
|
||||
},
|
||||
{
|
||||
name: 'preload_realm_bundle_deps',
|
||||
config: 'webpack.config.preload_realm.js'
|
||||
}
|
||||
const bundleTargets = [
|
||||
{ name: 'sandbox_bundle_deps', config: 'sandboxed_renderer.js' },
|
||||
{ name: 'isolated_bundle_deps', config: 'isolated_renderer.js' },
|
||||
{ name: 'browser_bundle_deps', config: 'browser.js' },
|
||||
{ name: 'renderer_bundle_deps', config: 'renderer.js' },
|
||||
{ name: 'worker_bundle_deps', config: 'worker.js' },
|
||||
{ name: 'node_bundle_deps', config: 'node.js' },
|
||||
{ name: 'utility_bundle_deps', config: 'utility.js' },
|
||||
{ name: 'preload_realm_bundle_deps', config: 'preload_realm.js' }
|
||||
];
|
||||
|
||||
const webpackTargetsWithDeps = await Promise.all(webpackTargets.map(async webpackTarget => {
|
||||
const tmpDir = await fs.promises.mkdtemp(path.resolve(os.tmpdir(), 'electron-filenames-'));
|
||||
const targetsWithDeps = await Promise.all(bundleTargets.map(async bundleTarget => {
|
||||
const child = cp.spawn('node', [
|
||||
'./node_modules/webpack-cli/bin/cli.js',
|
||||
'--config', `./build/webpack/${webpackTarget.config}`,
|
||||
'--stats', 'errors-only',
|
||||
'--output-path', tmpDir,
|
||||
'--output-filename', `${webpackTarget.name}.measure.js`,
|
||||
'--env', 'PRINT_WEBPACK_GRAPH'
|
||||
'./build/esbuild/bundle.js',
|
||||
'--config', `./build/esbuild/configs/${bundleTarget.config}`,
|
||||
'--print-graph'
|
||||
], {
|
||||
cwd: path.resolve(__dirname, '..')
|
||||
cwd: rootPath
|
||||
});
|
||||
let output = '';
|
||||
child.stdout.on('data', chunk => {
|
||||
@@ -71,32 +42,33 @@ const main = async () => {
|
||||
await new Promise<void>((resolve, reject) => child.on('exit', (code) => {
|
||||
if (code !== 0) {
|
||||
console.error(output);
|
||||
return reject(new Error(`Failed to list webpack dependencies for entry: ${webpackTarget.name}`));
|
||||
return reject(new Error(`Failed to list bundle dependencies for entry: ${bundleTarget.name}`));
|
||||
}
|
||||
|
||||
resolve();
|
||||
}));
|
||||
|
||||
const webpackTargetWithDeps = {
|
||||
...webpackTarget,
|
||||
return {
|
||||
...bundleTarget,
|
||||
dependencies: (JSON.parse(output) as string[])
|
||||
// Remove whitespace
|
||||
.map(line => line.trim())
|
||||
// Get the relative path
|
||||
.map(line => path.relative(rootPath, line).replace(/\\/g, '/'))
|
||||
// Only care about files in //electron
|
||||
.map(line => path.relative(rootPath, path.resolve(rootPath, line)).replace(/\\/g, '/'))
|
||||
.filter(line => !line.startsWith('..'))
|
||||
// Only care about our own files
|
||||
.filter(line => !line.startsWith('node_modules'))
|
||||
// All webpack builds depend on the tsconfig and package json files
|
||||
.concat(['tsconfig.json', 'tsconfig.electron.json', 'package.json', ...typingFiles])
|
||||
// Make the generated list easier to read
|
||||
.sort()
|
||||
};
|
||||
await fs.promises.rm(tmpDir, { force: true, recursive: true });
|
||||
return webpackTargetWithDeps;
|
||||
}));
|
||||
|
||||
// The typecheck step runs tsgo over tsconfig.electron.json which includes
|
||||
// the whole lib/ + typings/ trees. For GN dependency tracking, list the
|
||||
// union of every bundle's dependency set (lib files) plus typings, and
|
||||
// dedupe.
|
||||
const typecheckSources = Array.from(new Set([
|
||||
...targetsWithDeps.flatMap(t => t.dependencies),
|
||||
...typingFiles
|
||||
])).sort();
|
||||
|
||||
fs.writeFileSync(
|
||||
gniPath,
|
||||
`# THIS FILE IS AUTO-GENERATED, PLEASE DO NOT EDIT BY HAND
|
||||
@@ -105,7 +77,11 @@ auto_filenames = {
|
||||
${allDocs.map(doc => ` "${doc}",`).join('\n')}
|
||||
]
|
||||
|
||||
${webpackTargetsWithDeps.map(target => ` ${target.name} = [
|
||||
typecheck_sources = [
|
||||
${typecheckSources.map(src => ` "${src}",`).join('\n')}
|
||||
]
|
||||
|
||||
${targetsWithDeps.map(target => ` ${target.name} = [
|
||||
${target.dependencies.map(dep => ` "${dep}",`).join('\n')}
|
||||
]`).join('\n\n')}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Octokit } from '@octokit/rest';
|
||||
|
||||
import * as assert from 'node:assert';
|
||||
import { strict as assert } from 'node:assert';
|
||||
|
||||
import { createGitHubTokenStrategy } from './github-token';
|
||||
import { ELECTRON_ORG, ELECTRON_REPO } from './types';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as minimist from 'minimist';
|
||||
import * as streamChain from 'stream-chain';
|
||||
import * as streamJson from 'stream-json';
|
||||
import minimist = require('minimist');
|
||||
import streamChain = require('stream-chain');
|
||||
import streamJson = require('stream-json');
|
||||
import { ignore as streamJsonIgnore } from 'stream-json/filters/Ignore';
|
||||
import { streamArray as streamJsonStreamArray } from 'stream-json/streamers/StreamArray';
|
||||
|
||||
|
||||
58
script/typecheck.js
Normal file
58
script/typecheck.js
Normal file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env node
|
||||
// Runs `tsgo --noEmit -p <tsconfig>` and writes a stamp file on success,
|
||||
// so GN can track typecheck results as a build output.
|
||||
//
|
||||
// Usage: node script/typecheck.js --tsconfig <path> --stamp <path>
|
||||
|
||||
'use strict';
|
||||
|
||||
const cp = require('node:child_process');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
function parseArgs (argv) {
|
||||
const out = {};
|
||||
for (let i = 0; i < argv.length; i++) {
|
||||
const a = argv[i];
|
||||
if (a.startsWith('--')) {
|
||||
const next = argv[i + 1];
|
||||
if (next === undefined || next.startsWith('--')) {
|
||||
out[a.slice(2)] = true;
|
||||
} else {
|
||||
out[a.slice(2)] = next;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const args = parseArgs(process.argv.slice(2));
|
||||
if (!args.tsconfig || !args.stamp) {
|
||||
console.error('Usage: typecheck.js --tsconfig <path> --stamp <path>');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const electronRoot = path.resolve(__dirname, '..');
|
||||
// Resolve tsgo's bin entry directly from the package's `bin` map and run it
|
||||
// via the current Node executable. We can't `require.resolve` the bin path
|
||||
// (the package's `exports` field doesn't expose it) and we can't spawn
|
||||
// `node_modules/.bin/tsgo` directly on Windows (it's a `.cmd` shim).
|
||||
const tsgoPkgPath = require.resolve('@typescript/native-preview/package.json', {
|
||||
paths: [electronRoot]
|
||||
});
|
||||
const tsgoPkg = JSON.parse(fs.readFileSync(tsgoPkgPath, 'utf8'));
|
||||
const tsgoEntry = path.resolve(path.dirname(tsgoPkgPath), tsgoPkg.bin.tsgo);
|
||||
|
||||
const child = cp.spawnSync(
|
||||
process.execPath,
|
||||
[tsgoEntry, '--noEmit', '-p', path.resolve(args.tsconfig)],
|
||||
{ cwd: electronRoot, stdio: 'inherit' }
|
||||
);
|
||||
|
||||
if (child.status !== 0) {
|
||||
process.exit(child.status || 1);
|
||||
}
|
||||
|
||||
fs.mkdirSync(path.dirname(args.stamp), { recursive: true });
|
||||
fs.writeFileSync(args.stamp, '');
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "shell/common/gin_helper/wrappable.h"
|
||||
|
||||
#include "base/task/sequenced_task_runner.h"
|
||||
#include "gin/object_template_builder.h"
|
||||
#include "gin/public/isolate_holder.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
@@ -90,7 +91,22 @@ void WrappableBase::SecondWeakCallback(
|
||||
if (gin::IsolateHolder::DestroyedMicrotasksRunner()) {
|
||||
return;
|
||||
}
|
||||
delete static_cast<WrappableBase*>(data.GetInternalField(0));
|
||||
// Defer destruction to a posted task. V8's second-pass weak callbacks run
|
||||
// inside a DisallowJavascriptExecutionScope (they may touch the V8 API but
|
||||
// must not invoke JS). Several Electron Wrappables (e.g. WebContents) emit
|
||||
// JS events from their destructors, so deleting synchronously here can
|
||||
// crash with "Invoke in DisallowJavascriptExecutionScope" — see
|
||||
// https://github.com/electron/electron/issues/47420. Posting via the
|
||||
// current sequence's task runner ensures the destructor runs once V8 has
|
||||
// left the GC scope. If no task runner is available (e.g. early/late in
|
||||
// process lifetime), fall back to synchronous deletion.
|
||||
auto* wrappable = static_cast<WrappableBase*>(data.GetInternalField(0));
|
||||
if (base::SequencedTaskRunner::HasCurrentDefault()) {
|
||||
base::SequencedTaskRunner::GetCurrentDefault()->DeleteSoon(FROM_HERE,
|
||||
wrappable);
|
||||
} else {
|
||||
delete wrappable;
|
||||
}
|
||||
}
|
||||
|
||||
DeprecatedWrappableBase::DeprecatedWrappableBase() = default;
|
||||
@@ -126,9 +142,19 @@ void DeprecatedWrappableBase::SecondWeakCallback(
|
||||
const v8::WeakCallbackInfo<DeprecatedWrappableBase>& data) {
|
||||
if (gin::IsolateHolder::DestroyedMicrotasksRunner())
|
||||
return;
|
||||
// See WrappableBase::SecondWeakCallback for why deletion is posted: V8's
|
||||
// second-pass weak callbacks run inside a DisallowJavascriptExecutionScope,
|
||||
// and several Wrappables emit JS events from their destructors.
|
||||
// https://github.com/electron/electron/issues/47420
|
||||
DeprecatedWrappableBase* wrappable = data.GetParameter();
|
||||
if (wrappable)
|
||||
if (!wrappable)
|
||||
return;
|
||||
if (base::SequencedTaskRunner::HasCurrentDefault()) {
|
||||
base::SequencedTaskRunner::GetCurrentDefault()->DeleteSoon(FROM_HERE,
|
||||
wrappable);
|
||||
} else {
|
||||
delete wrappable;
|
||||
}
|
||||
}
|
||||
|
||||
v8::MaybeLocal<v8::Object> DeprecatedWrappableBase::GetWrapperImpl(
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#define ELECTRON_SHELL_COMMON_GIN_HELPER_WRAPPABLE_BASE_H_
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/task/sequenced_task_runner_helpers.h"
|
||||
#include "v8/include/v8-forward.h"
|
||||
|
||||
namespace gin {
|
||||
@@ -75,6 +76,11 @@ class DeprecatedWrappableBase {
|
||||
DeprecatedWrappableBase();
|
||||
virtual ~DeprecatedWrappableBase();
|
||||
|
||||
// SecondWeakCallback posts destruction via DeleteSoon so that destructors
|
||||
// (which may emit JS events) run outside V8's GC scope. DeleteSoon needs
|
||||
// access to the protected destructor.
|
||||
friend class base::DeleteHelper<DeprecatedWrappableBase>;
|
||||
|
||||
// Overrides of this method should be declared final and not overridden again.
|
||||
virtual gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
|
||||
v8::Isolate* isolate);
|
||||
|
||||
@@ -53,7 +53,8 @@ v8::MaybeLocal<v8::Value> CompileAndCall(
|
||||
context, v8::Null(isolate), arguments->size(), arguments->data());
|
||||
|
||||
// This will only be caught when something has gone terrible wrong as all
|
||||
// electron scripts are wrapped in a try {} catch {} by webpack
|
||||
// electron scripts are wrapped in a try {} catch {} by the esbuild bundler
|
||||
// (see build/esbuild/bundle.js applyWrappers).
|
||||
if (try_catch.HasCaught()) {
|
||||
std::string msg = "no error message";
|
||||
if (!try_catch.Message().IsEmpty()) {
|
||||
|
||||
29
spec/fixtures/crash-cases/wrappable-gc-weak-callback-emit/index.js
vendored
Normal file
29
spec/fixtures/crash-cases/wrappable-gc-weak-callback-emit/index.js
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
const { app, WebContentsView } = require('electron');
|
||||
|
||||
const v8 = require('node:v8');
|
||||
|
||||
// Force V8 to schedule incremental-marking finalization steps as foreground
|
||||
// tasks on every allocation. Those tasks run inside V8's
|
||||
// DisallowJavascriptExecutionScope. Prior to the fix in
|
||||
// shell/common/gin_helper/wrappable.cc, gin_helper::SecondWeakCallback would
|
||||
// `delete` the Wrappable synchronously inside that scope, and Wrappables
|
||||
// whose destructors emit JS events (WebContents emits 'will-destroy' /
|
||||
// 'destroyed') would crash with "Invoke in DisallowJavascriptExecutionScope".
|
||||
//
|
||||
// Regression test for https://github.com/electron/electron/issues/47420.
|
||||
v8.setFlagsFromString('--stress-incremental-marking');
|
||||
|
||||
app.whenReady().then(() => {
|
||||
// Leak several WebContentsView instances so at least one wrapper's
|
||||
// collection lands in a foreground GC task during app.quit()'s uv_run
|
||||
// drain. Three is enough for the crash to reproduce 10/10 on a Linux
|
||||
// testing build before the fix.
|
||||
// eslint-disable-next-line no-new
|
||||
new WebContentsView();
|
||||
// eslint-disable-next-line no-new
|
||||
new WebContentsView();
|
||||
// eslint-disable-next-line no-new
|
||||
new WebContentsView();
|
||||
|
||||
app.quit();
|
||||
});
|
||||
18
spec/fixtures/module/preload-require-identity.js
vendored
Normal file
18
spec/fixtures/module/preload-require-identity.js
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
/* eslint-disable no-self-compare, import/enforce-node-protocol-usage */
|
||||
// Regression test fixture for the esbuild __toCommonJS identity break.
|
||||
// Each pair must be the exact same object reference — see the
|
||||
// "module identity" describe block in spec/modules-spec.ts. The comparisons
|
||||
// are intentionally self-referential and intentionally use both the bare and
|
||||
// `node:`-prefixed module names, which is why no-self-compare and
|
||||
// import/enforce-node-protocol-usage are disabled for this file.
|
||||
const { ipcRenderer } = require('electron');
|
||||
|
||||
ipcRenderer.send('require-identity', {
|
||||
electron: require('electron') === require('electron'),
|
||||
electronCommon: require('electron/common') === require('electron'),
|
||||
events: require('events') === require('node:events'),
|
||||
timers: require('timers') === require('node:timers'),
|
||||
url: require('url') === require('node:url'),
|
||||
ipcRenderer: require('electron').ipcRenderer === require('electron').ipcRenderer,
|
||||
contextBridge: require('electron').contextBridge === require('electron').contextBridge
|
||||
});
|
||||
@@ -284,6 +284,41 @@ describe('modules support', () => {
|
||||
expect(result).to.equal('function');
|
||||
});
|
||||
});
|
||||
|
||||
// Regression test for the esbuild __toCommonJS identity break: esbuild's
|
||||
// runtime helper for `require()`-of-ESM allocates a fresh wrapper on
|
||||
// every call (see evanw/esbuild@f4ff26d3). The bundle driver patches it
|
||||
// with a WeakMap-memoized variant so module identity matches webpack's
|
||||
// __webpack_require__ cache semantics. Without the patch, every getter
|
||||
// on `require('electron')` that resolves to a non-default-exporting ESM
|
||||
// module would yield a fresh namespace on each access.
|
||||
describe('module identity', () => {
|
||||
it('returns the same object for repeated require("electron") accesses in the main process', () => {
|
||||
const electron = require('electron');
|
||||
for (const key of Object.keys(electron)) {
|
||||
// Touching `electron.net` before app ready throws — handled by the
|
||||
// sandboxed-preload identity test below; everything else must be
|
||||
// strictly equal across two reads.
|
||||
if (key === 'net') continue;
|
||||
expect((electron as any)[key]).to.equal((electron as any)[key],
|
||||
`require('electron').${key} must be identity-stable across reads`);
|
||||
}
|
||||
});
|
||||
|
||||
it('returns the same object for repeated require() in a sandboxed preload', async () => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: { sandbox: true, contextIsolation: false, preload: path.join(fixtures, 'module', 'preload-require-identity.js') }
|
||||
});
|
||||
const result = once(w.webContents.ipc, 'require-identity');
|
||||
await w.loadURL('about:blank');
|
||||
const [, identities] = await result;
|
||||
for (const [name, same] of Object.entries(identities)) {
|
||||
expect(same).to.equal(true, `${name} must be identity-stable in sandboxed preload`);
|
||||
}
|
||||
w.destroy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('esm', () => {
|
||||
|
||||
@@ -2,8 +2,8 @@ const childProcess = require('node:child_process');
|
||||
const path = require('node:path');
|
||||
|
||||
const typeCheck = () => {
|
||||
const tscExec = path.resolve(require.resolve('typescript'), '../../bin/tsc');
|
||||
const tscChild = childProcess.spawn(process.execPath, [tscExec, '--project', './ts-smoke/tsconfig.json'], {
|
||||
const tsgoExec = path.resolve(require.resolve('@typescript/native-preview/package.json'), '..', 'bin', 'tsgo.js');
|
||||
const tscChild = childProcess.spawn(process.execPath, [tsgoExec, '--project', './ts-smoke/tsconfig.json'], {
|
||||
cwd: path.resolve(__dirname, '../')
|
||||
});
|
||||
tscChild.stdout.on('data', d => console.log(d.toString()));
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"compilerOptions": {
|
||||
"rootDir": "default_app",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node"
|
||||
"moduleResolution": "bundler"
|
||||
},
|
||||
"include": [
|
||||
"default_app",
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "lib"
|
||||
// rootDir intentionally omitted: some lib/ files pull type declarations
|
||||
// out of ../third_party/electron_node via the `@node/*` path alias (see
|
||||
// lib/node/asar-fs-wrapper.ts) and a rootDir of 'lib' rejects those
|
||||
// as outside-rootDir imports.
|
||||
},
|
||||
"include": [
|
||||
"lib",
|
||||
|
||||
@@ -10,14 +10,12 @@
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"strict": true,
|
||||
"baseUrl": ".",
|
||||
"allowJs": true,
|
||||
"noUnusedLocals": true,
|
||||
"outDir": "ts-gen",
|
||||
"typeRoots" : ["./node_modules/@types", "./spec/node_modules/@types"],
|
||||
"paths": {
|
||||
"@electron/internal/*": ["lib/*"],
|
||||
"@node/*": ["../third_party/electron_node/*"]
|
||||
"@electron/internal/*": ["./lib/*"]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
|
||||
36
typings/internal-ambient.d.ts
vendored
36
typings/internal-ambient.d.ts
vendored
@@ -1,7 +1,37 @@
|
||||
/// <reference types="webpack/module" />
|
||||
|
||||
declare const BUILDFLAG: (flag: boolean) => boolean;
|
||||
|
||||
// esbuild build/esbuild/bundle.js rewrites calls to this identifier into
|
||||
// literal `require()` calls so that consumers can reach Node internal modules
|
||||
// (e.g. 'internal/modules/helpers') without the bundler trying to resolve
|
||||
// them. Overloads below pin the known internal-module IDs to narrow types;
|
||||
// the final catch-all signature keeps less-common paths usable at the cost
|
||||
// of no static type information.
|
||||
interface NodeInternalModules {
|
||||
'internal/modules/helpers': {
|
||||
makeRequireFunction: (mod: NodeModule) => NodeRequire;
|
||||
};
|
||||
'internal/modules/run_main': {
|
||||
runEntryPointWithESMLoader: (cb: (cascadedLoader: any) => any) => Promise<void>;
|
||||
};
|
||||
'internal/fs/utils': {
|
||||
getValidatedPath: (path: any, ...args: any[]) => string;
|
||||
getOptions: (options: any, defaultOptions?: any) => any;
|
||||
getDirent: (path: string, name: string | Buffer, type: number, callback?: Function) => any;
|
||||
};
|
||||
'internal/util': {
|
||||
assignFunctionName: (name: string | symbol, fn: Function) => Function;
|
||||
};
|
||||
'internal/validators': {
|
||||
validateBoolean: (value: unknown, name: string) => asserts value is boolean;
|
||||
validateFunction: (value: unknown, name: string) => void;
|
||||
};
|
||||
'internal/url': {
|
||||
URL: typeof URL;
|
||||
};
|
||||
}
|
||||
declare function __non_webpack_require__<K extends keyof NodeInternalModules>(id: K): NodeInternalModules[K];
|
||||
declare function __non_webpack_require__(id: string): unknown;
|
||||
|
||||
declare namespace NodeJS {
|
||||
interface ModuleInternal extends NodeJS.Module {
|
||||
new(id: string, parent?: NodeJS.Module | null): NodeJS.Module;
|
||||
@@ -282,7 +312,7 @@ declare namespace NodeJS {
|
||||
}
|
||||
}
|
||||
|
||||
declare module NodeJS {
|
||||
declare namespace NodeJS {
|
||||
interface Global {
|
||||
require: NodeRequire;
|
||||
module: NodeModule;
|
||||
|
||||
Reference in New Issue
Block a user