From 56d1fafe66f538707c16122fb58e14fdf91cbfbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julie=20Koubov=C3=A1?= Date: Tue, 20 Oct 2020 21:10:15 +0200 Subject: [PATCH] build: Wrap bundles using webpack (#25557) --- build/webpack/get-outputs.js | 2 - build/webpack/run-compiler.js | 45 ----- build/webpack/webpack.config.base.js | 263 +++++++++++++++------------ build/webpack/webpack.gni | 8 +- package.json | 7 +- script/gen-filenames.ts | 9 +- shell/common/node_bindings.cc | 2 +- shell/common/node_util.cc | 2 +- yarn.lock | 9 +- 9 files changed, 169 insertions(+), 178 deletions(-) delete mode 100644 build/webpack/get-outputs.js delete mode 100644 build/webpack/run-compiler.js diff --git a/build/webpack/get-outputs.js b/build/webpack/get-outputs.js deleted file mode 100644 index 518a513b55c4..000000000000 --- a/build/webpack/get-outputs.js +++ /dev/null @@ -1,2 +0,0 @@ -process.env.PRINT_WEBPACK_GRAPH = true; -require('./run-compiler'); diff --git a/build/webpack/run-compiler.js b/build/webpack/run-compiler.js deleted file mode 100644 index eb924f4e5dd2..000000000000 --- a/build/webpack/run-compiler.js +++ /dev/null @@ -1,45 +0,0 @@ -const fs = require('fs'); -const path = require('path'); -const webpack = require('webpack'); - -const configPath = process.argv[2]; -const outPath = path.resolve(process.argv[3]); -const config = require(configPath); -config.output = { - path: path.dirname(outPath), - filename: path.basename(outPath) -}; - -const { wrapInitWithProfilingTimeout, wrapInitWithTryCatch, ...webpackConfig } = config; - -webpack(webpackConfig, (err, stats) => { - if (err) { - console.error(err); - process.exit(1); - } else if (stats.hasErrors()) { - console.error(stats.toString('normal')); - process.exit(1); - } else { - let contents = fs.readFileSync(outPath, 'utf8'); - if (wrapInitWithTryCatch) { - contents = `try { -${contents} -} catch (err) { - console.error('Electron ${webpackConfig.output.filename} script failed to run'); - console.error(err); -}`; - } - if (wrapInitWithProfilingTimeout) { - contents = `function ___electron_webpack_init__() { -${contents} -}; -if ((globalThis.process || binding.process).argv.includes("--profile-electron-init")) { - setTimeout(___electron_webpack_init__, 0); -} else { - ___electron_webpack_init__(); -}`; - } - fs.writeFileSync(outPath, contents); - process.exit(0); - } -}); diff --git a/build/webpack/webpack.config.base.js b/build/webpack/webpack.config.base.js index 1076df08be88..b23ffb95e774 100644 --- a/build/webpack/webpack.config.base.js +++ b/build/webpack/webpack.config.base.js @@ -2,16 +2,12 @@ const fs = require('fs'); const path = require('path'); const webpack = require('webpack'); const TerserPlugin = require('terser-webpack-plugin'); +const WrapperPlugin = require('wrapper-webpack-plugin'); const electronRoot = path.resolve(__dirname, '../..'); -const onlyPrintingGraph = !!process.env.PRINT_WEBPACK_GRAPH; - class AccessDependenciesPlugin { apply (compiler) { - // Only hook into webpack when we are printing the dependency graph - if (!onlyPrintingGraph) return; - 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)); @@ -21,49 +17,6 @@ class AccessDependenciesPlugin { } } -const defines = { - BUILDFLAG: onlyPrintingGraph ? '(a => a)' : '' -}; - -const buildFlagsPrefix = '--buildflags='; -const buildFlagArg = process.argv.find(arg => arg.startsWith(buildFlagsPrefix)); - -if (buildFlagArg) { - const buildFlagPath = buildFlagArg.substr(buildFlagsPrefix.length); - - const flagFile = fs.readFileSync(buildFlagPath, '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 = []; - -if (defines.ENABLE_DESKTOP_CAPTURER === 'false') { - ignoredModules.push( - '@electron/internal/browser/desktop-capturer', - '@electron/internal/browser/api/desktop-capturer', - '@electron/internal/renderer/api/desktop-capturer' - ); -} - -if (defines.ENABLE_REMOTE_MODULE === 'false') { - ignoredModules.push( - '@electron/internal/browser/remote/server', - '@electron/internal/renderer/api/remote' - ); -} - -if (defines.ENABLE_VIEWS_API === 'false') { - ignoredModules.push( - '@electron/internal/browser/api/views/image-view.js' - ); -} - module.exports = ({ alwaysHasNode, loadElectronFromAlternateTarget, @@ -79,76 +32,148 @@ module.exports = ({ const electronAPIFile = path.resolve(electronRoot, 'lib', loadElectronFromAlternateTarget || target, 'api', 'exports', 'electron.ts'); - return ({ - mode: 'development', - devtool: false, - entry, - target: alwaysHasNode ? 'node' : 'web', - output: { - filename: `${target}.bundle.js` - }, - wrapInitWithProfilingTimeout, - wrapInitWithTryCatch, - resolve: { - alias: { - '@electron/internal': path.resolve(electronRoot, 'lib'), - electron$: electronAPIFile, - 'electron/main$': electronAPIFile, - 'electron/renderer$': electronAPIFile, - 'electron/common$': electronAPIFile, - // Force timers to resolve to our dependency that doesn't use window.postMessage - timers: path.resolve(electronRoot, 'node_modules', 'timers-browserify', 'main.js') - }, - extensions: ['.ts', '.js'] - }, - 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 - ] + 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))); } - }] - }, - node: { - __dirname: false, - __filename: false, - // 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 - }, - optimization: { - minimize: true, - minimizer: [ - new TerserPlugin({ - terserOptions: { - keep_classnames: true, - keep_fnames: true - } - }) - ] - }, - plugins: [ - new AccessDependenciesPlugin(), - ...(targetDeletesNodeGlobals ? [ - new webpack.ProvidePlugin({ - process: ['@electron/internal/common/webpack-provider', 'process'], - global: ['@electron/internal/common/webpack-provider', '_global'], - Buffer: ['@electron/internal/common/webpack-provider', 'Buffer'] - }) - ] : []), - new webpack.ProvidePlugin({ - Promise: ['@electron/internal/common/webpack-globals-provider', 'Promise'] - }), - new webpack.DefinePlugin(defines) - ] - }); + } + } + + const ignoredModules = []; + + if (defines.ENABLE_DESKTOP_CAPTURER === 'false') { + ignoredModules.push( + '@electron/internal/browser/desktop-capturer', + '@electron/internal/browser/api/desktop-capturer', + '@electron/internal/renderer/api/desktop-capturer' + ); + } + + if (defines.ENABLE_REMOTE_MODULE === 'false') { + ignoredModules.push( + '@electron/internal/browser/remote/server', + '@electron/internal/renderer/api/remote' + ); + } + + if (defines.ENABLE_VIEWS_API === 'false') { + ignoredModules.push( + '@electron/internal/browser/api/views/image-view.js' + ); + } + + const plugins = []; + + if (onlyPrintingGraph) { + plugins.push(new AccessDependenciesPlugin()); + } + + if (targetDeletesNodeGlobals) { + plugins.push(new webpack.ProvidePlugin({ + process: ['@electron/internal/common/webpack-provider', 'process'], + global: ['@electron/internal/common/webpack-provider', '_global'], + Buffer: ['@electron/internal/common/webpack-provider', 'Buffer'] + })); + } + + 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, + // Force timers to resolve to our dependency that doesn't use window.postMessage + timers: path.resolve(electronRoot, 'node_modules', 'timers-browserify', 'main.js') + }, + extensions: ['.ts', '.js'] + }, + 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 + ] + } + }] + }, + node: { + __dirname: false, + __filename: false, + // 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 + }, + optimization: { + minimize: true, + minimizer: [ + new TerserPlugin({ + terserOptions: { + keep_classnames: true, + keep_fnames: true + } + }) + ] + }, + plugins + }; + }; }; diff --git a/build/webpack/webpack.gni b/build/webpack/webpack.gni index ade1fe78e07b..f2bddbd5ae2b 100644 --- a/build/webpack/webpack.gni +++ b/build/webpack/webpack.gni @@ -16,7 +16,6 @@ template("webpack_build") { inputs = [ invoker.config_file, "//electron/build/webpack/webpack.config.base.js", - "//electron/build/webpack/run-compiler.js", "//electron/tsconfig.json", "//electron/yarn.lock", "//electron/typings/internal-ambient.d.ts", @@ -24,9 +23,12 @@ template("webpack_build") { ] + invoker.inputs args = [ + "--config", rebase_path(invoker.config_file), - rebase_path(invoker.out_file), - "--buildflags=" + rebase_path("$target_gen_dir/buildflags/buildflags.h"), + "--output-filename=" + 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"), ] deps += [ "buildflags" ] diff --git a/package.json b/package.json index d1ed6842abc5..a761d35deafc 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,8 @@ "ts-node": "6.2.0", "typescript": "^4.0.2", "webpack": "^4.43.0", - "webpack-cli": "^3.3.12" + "webpack-cli": "^3.3.12", + "wrapper-webpack-plugin": "^2.1.0" }, "private": true, "scripts": { @@ -91,7 +92,7 @@ "start": "node ./script/start.js", "test": "node ./script/spec-runner.js", "tsc": "tsc", - "webpack": "node build/webpack/run-compiler" + "webpack": "webpack" }, "license": "MIT", "author": "Electron Community", @@ -141,4 +142,4 @@ "@types/temp": "^0.8.34", "aws-sdk": "^2.727.1" } -} \ No newline at end of file +} diff --git a/script/gen-filenames.ts b/script/gen-filenames.ts index 854c68d13ee6..b8d6c234ad9d 100644 --- a/script/gen-filenames.ts +++ b/script/gen-filenames.ts @@ -46,9 +46,12 @@ const main = async () => { const webpackTargetsWithDeps = await Promise.all(webpackTargets.map(async webpackTarget => { const tmpDir = await fs.mkdtemp(path.resolve(os.tmpdir(), 'electron-filenames-')); const child = cp.spawn('node', [ - 'build/webpack/get-outputs.js', - `./${webpackTarget.config}`, - path.resolve(tmpDir, `${webpackTarget.name}.measure.js`) + './node_modules/webpack-cli/bin/cli.js', + '--config', `./build/webpack/${webpackTarget.config}`, + '--display', 'errors-only', + `--output-path=${tmpDir}`, + `--output-filename=${webpackTarget.name}.measure.js`, + '--env.PRINT_WEBPACK_GRAPH' ], { cwd: path.resolve(__dirname, '..') }); diff --git a/shell/common/node_bindings.cc b/shell/common/node_bindings.cc index 74942280d5d3..a5fb596996ba 100644 --- a/shell/common/node_bindings.cc +++ b/shell/common/node_bindings.cc @@ -424,7 +424,7 @@ node::Environment* NodeBindings::CreateEnvironment( DCHECK(env); // This will only be caught when something has gone terrible wrong as all - // electron scripts are wrapped in a try {} catch {} in run-compiler.js + // electron scripts are wrapped in a try {} catch {} by webpack if (try_catch.HasCaught()) { LOG(ERROR) << "Failed to initialize node environment in process: " << process_type; diff --git a/shell/common/node_util.cc b/shell/common/node_util.cc index ab4207d5909d..6f69452cb3b5 100644 --- a/shell/common/node_util.cc +++ b/shell/common/node_util.cc @@ -29,7 +29,7 @@ v8::MaybeLocal CompileAndCall( v8::MaybeLocal ret = fn->Call( 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 {} in run-compiler.js + // electron scripts are wrapped in a try {} catch {} by webpack if (try_catch.HasCaught()) { LOG(ERROR) << "Failed to CompileAndCall electron script: " << id; } diff --git a/yarn.lock b/yarn.lock index 3474da45732b..b7cfe96c051a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8151,7 +8151,7 @@ webpack-cli@^3.3.12: v8-compile-cache "^2.1.1" yargs "^13.3.2" -webpack-sources@^1.4.0, webpack-sources@^1.4.1: +webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1: version "1.4.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== @@ -8257,6 +8257,13 @@ wrapped@^1.0.1: co "3.1.0" sliced "^1.0.1" +wrapper-webpack-plugin@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrapper-webpack-plugin/-/wrapper-webpack-plugin-2.1.0.tgz#2b5d80f46af84c9eeb707d08796a115e233adeac" + integrity sha512-e+2FhSYGCxhDq3PcUw5mRhH+8vcYa+9d9AuLChJUZ9ZbUPhQOHZ/O2dnN98iTqeUuvrzSSOv13+x/NhrAm5JEg== + dependencies: + webpack-sources "^1.1.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"