Browse Source

build: Wrap bundles using webpack (#25557)

Julie Koubová 4 years ago
parent
commit
56d1fafe66

+ 0 - 2
build/webpack/get-outputs.js

@@ -1,2 +0,0 @@
-process.env.PRINT_WEBPACK_GRAPH = true;
-require('./run-compiler');

+ 0 - 45
build/webpack/run-compiler.js

@@ -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);
-  }
-});

+ 143 - 118
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
+      }
+    }
+
+    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
+            ]
           }
-        })
-      ]
-    },
-    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)
-    ]
-  });
+        }]
+      },
+      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
+    };
+  };
 };

+ 5 - 3
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" ]
 

+ 4 - 3
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"
   }
-}
+}

+ 6 - 3
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, '..')
     });

+ 1 - 1
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;

+ 1 - 1
shell/common/node_util.cc

@@ -29,7 +29,7 @@ v8::MaybeLocal<v8::Value> CompileAndCall(
   v8::MaybeLocal<v8::Value> 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;
   }

+ 8 - 1
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"