Browse Source

refactor: bundle the browser and renderer process electron code (#18553)

* refactor: bundle the browser and renderer process electron code

* Bundles browser/init and renderer/init
  * Improves load performance of main process by ~40%
  * Improves load performance of renderer process by ~30%
* Prevents users from importing our "requiring" our internal logic such
as ipc-main-internal.  This makes those message buses safer as they are
less accessible, there is still some more work to be done though to lock
down those buses completely.
* The electron.asar file now only contains 2 files, as a future
improvement maybe we can use atom_natives to ship these two files
embedded in the binary
* This also removes our dependency on browserify which had some strange
edge cases that caused us to have to hack around require-order and
stopped us using certain ES6/7 features we should have been able to use
(async / await in some files in the sandboxed renderer init script)

TLDR: Things are faster and better :)

* fix: I really do not want to talk about it

* chore: add performance improvements from debugging

* fix: resolve the provided path so webpack thinks it is absolute

* chore: fixup per PR review

* fix: use webpacks ProvidePlugin to keep global, process and Buffer alive after deletion from global scope for use in internal code

* fix: bundle worker/init as well to make node-in-workers work

* chore: update wording as per feedback

* chore: make the timers hack work when yarn is not used
Samuel Attard 5 years ago
parent
commit
bc527f6b51
40 changed files with 1007 additions and 696 deletions
  1. 40 132
      BUILD.gn
  2. 0 19
      build/js_wrap.gni
  3. 0 19
      build/js_wrap.py
  4. 2 0
      build/webpack/get-outputs.js
  5. 22 0
      build/webpack/run-compiler.js
  6. 80 0
      build/webpack/webpack.config.base.js
  7. 4 0
      build/webpack/webpack.config.browser.js
  8. 4 0
      build/webpack/webpack.config.content_script.js
  9. 4 0
      build/webpack/webpack.config.isolated_renderer.js
  10. 5 0
      build/webpack/webpack.config.renderer.js
  11. 4 0
      build/webpack/webpack.config.sandboxed_renderer.js
  12. 6 0
      build/webpack/webpack.config.worker.js
  13. 35 0
      build/webpack/webpack.gni
  14. 1 2
      docs/api/sandbox-option.md
  15. 177 8
      filenames.auto.gni
  16. 0 94
      filenames.gni
  17. 1 3
      lib/browser/api/app.ts
  18. 1 6
      lib/browser/api/exports/electron.js
  19. 51 0
      lib/browser/api/module-keys.js
  20. 36 34
      lib/browser/api/module-list.js
  21. 3 2
      lib/browser/crash-reporter-init.js
  22. 2 7
      lib/browser/init.ts
  23. 7 6
      lib/common/api/exports/electron.js
  24. 5 5
      lib/common/api/module-list.js
  25. 16 12
      lib/common/reset-search-paths.ts
  26. 1 1
      lib/content_script/init.js
  27. 4 18
      lib/renderer/api/exports/electron.js
  28. 11 9
      lib/renderer/api/module-list.js
  29. 1 1
      lib/renderer/api/remote.js
  30. 11 12
      lib/renderer/init.ts
  31. 2 2
      lib/renderer/security-warnings.ts
  32. 18 0
      lib/renderer/webpack-provider.ts
  33. 4 16
      lib/sandboxed_renderer/init.js
  34. 4 7
      lib/worker/init.js
  35. 9 17
      package.json
  36. 28 33
      script/gen-filenames.js
  37. 1 1
      spec/asar-spec.js
  38. 1 1
      spec/internal-spec.js
  39. 0 19
      tools/list-browserify-deps.py
  40. 406 210
      yarn.lock

+ 40 - 132
BUILD.gn

@@ -10,9 +10,9 @@ import("//tools/grit/repack.gni")
 import("//tools/v8_context_snapshot/v8_context_snapshot.gni")
 import("//v8/gni/snapshot_toolchain.gni")
 import("build/asar.gni")
-import("build/js_wrap.gni")
 import("build/npm.gni")
 import("build/tsc.gni")
+import("build/webpack/webpack.gni")
 import("buildflags/buildflags.gni")
 import("electron_paks.gni")
 import("filenames.auto.gni")
@@ -71,135 +71,70 @@ npm_action("build_electron_definitions") {
   ]
 }
 
-npm_action("atom_browserify_sandbox_unwrapped") {
-  script = "browserify"
+webpack_build("electron_browser_bundle") {
   deps = [
     ":build_electron_definitions",
   ]
 
-  inputs = auto_filenames.sandbox_browserify_deps
+  inputs = auto_filenames.browser_bundle_deps
 
-  outputs = [
-    "$target_gen_dir/js2c/sandbox_bundle_unwrapped.js",
-  ]
-
-  args = [
-    "lib/sandboxed_renderer/init.js",
-    "-r",
-    "./lib/sandboxed_renderer/api/exports/electron.js:electron",
-    "-t",
-    "aliasify",
-    "-p",
-    "[",
-    "tsify",
-    "-p",
-    "tsconfig.electron.json",
-    "]",
-    "--standalone",
-    "sandboxed_preload",
-    "-o",
-    rebase_path(outputs[0]),
-  ]
+  config_file = "//electron/build/webpack/webpack.config.browser.js"
+  out_file = "$target_gen_dir/electron_asar/browser/init.js"
 }
 
-npm_action("atom_browserify_isolated_unwrapped") {
-  script = "browserify"
+webpack_build("electron_renderer_bundle") {
   deps = [
     ":build_electron_definitions",
   ]
 
-  inputs = auto_filenames.isolated_browserify_deps
+  inputs = auto_filenames.renderer_bundle_deps
 
-  outputs = [
-    "$target_gen_dir/js2c/isolated_bundle_unwrapped.js",
-  ]
-
-  args = [
-    "lib/isolated_renderer/init.js",
-    "-t",
-    "aliasify",
-    "-p",
-    "[",
-    "tsify",
-    "-p",
-    "tsconfig.electron.json",
-    "]",
-    "--standalone",
-    "isolated_preload",
-    "-o",
-    rebase_path(outputs[0]),
-  ]
+  config_file = "//electron/build/webpack/webpack.config.renderer.js"
+  out_file = "$target_gen_dir/electron_asar/renderer/init.js"
 }
 
-npm_action("atom_browserify_content_script_unwrapped") {
-  script = "browserify"
+webpack_build("electron_worker_bundle") {
   deps = [
     ":build_electron_definitions",
   ]
 
-  inputs = auto_filenames.context_script_browserify_deps
-
-  outputs = [
-    "$target_gen_dir/js2c/content_script_bundle_unwrapped.js",
-  ]
+  inputs = auto_filenames.worker_bundle_deps
 
-  args = [
-    "lib/content_script/init.js",
-    "-t",
-    "aliasify",
-    "-p",
-    "[",
-    "tsify",
-    "-p",
-    "tsconfig.electron.json",
-    "]",
-    "--standalone",
-    "content_script_preload",
-    "-o",
-    rebase_path(outputs[0]),
-  ]
+  config_file = "//electron/build/webpack/webpack.config.worker.js"
+  out_file = "$target_gen_dir/electron_asar/worker/init.js"
 }
 
-js_wrap("atom_browserify_content_script") {
+webpack_build("electron_sandboxed_renderer_bundle") {
   deps = [
-    ":atom_browserify_content_script_unwrapped",
+    ":build_electron_definitions",
   ]
 
-  inputs = [
-    "$target_gen_dir/js2c/content_script_bundle_unwrapped.js",
-  ]
+  inputs = auto_filenames.sandbox_bundle_deps
 
-  outputs = [
-    "$target_gen_dir/js2c/content_script_bundle.js",
-  ]
+  config_file = "//electron/build/webpack/webpack.config.sandboxed_renderer.js"
+  out_file = "$target_gen_dir/js2c/sandbox_bundle.js"
 }
 
-js_wrap("atom_browserify_isolated") {
+webpack_build("electron_isolated_renderer_bundle") {
   deps = [
-    ":atom_browserify_isolated_unwrapped",
+    ":build_electron_definitions",
   ]
 
-  inputs = [
-    "$target_gen_dir/js2c/isolated_bundle_unwrapped.js",
-  ]
+  inputs = auto_filenames.isolated_bundle_deps
 
-  outputs = [
-    "$target_gen_dir/js2c/isolated_bundle.js",
-  ]
+  config_file = "//electron/build/webpack/webpack.config.isolated_renderer.js"
+  out_file = "$target_gen_dir/js2c/isolated_bundle.js"
 }
 
-js_wrap("atom_browserify_sandbox") {
+webpack_build("electron_content_script_bundle") {
   deps = [
-    ":atom_browserify_sandbox_unwrapped",
+    ":build_electron_definitions",
   ]
 
-  inputs = [
-    "$target_gen_dir/js2c/sandbox_bundle_unwrapped.js",
-  ]
+  inputs = auto_filenames.content_script_bundle_deps
 
-  outputs = [
-    "$target_gen_dir/js2c/sandbox_bundle.js",
-  ]
+  config_file = "//electron/build/webpack/webpack.config.content_script.js"
+  out_file = "$target_gen_dir/js2c/content_script_bundle.js"
 }
 
 copy("atom_js2c_copy") {
@@ -214,19 +149,19 @@ copy("atom_js2c_copy") {
 
 action("atom_js2c") {
   deps = [
-    ":atom_browserify_content_script",
-    ":atom_browserify_isolated",
-    ":atom_browserify_sandbox",
     ":atom_js2c_copy",
+    ":electron_content_script_bundle",
+    ":electron_isolated_renderer_bundle",
+    ":electron_sandboxed_renderer_bundle",
   ]
 
-  browserify_sources = [
+  webpack_sources = [
     "$target_gen_dir/js2c/content_script_bundle.js",
     "$target_gen_dir/js2c/isolated_bundle.js",
     "$target_gen_dir/js2c/sandbox_bundle.js",
   ]
 
-  sources = browserify_sources + [
+  sources = webpack_sources + [
               "$target_gen_dir/js2c/asar.js",
               "$target_gen_dir/js2c/asar_init.js",
             ]
@@ -242,46 +177,19 @@ action("atom_js2c") {
          rebase_path(sources, root_build_dir)
 }
 
-target_gen_electron_js = "$target_gen_dir/js/electron"
 target_gen_default_app_js = "$target_gen_dir/js/default_app"
 
-typescript_build("lib_js") {
-  deps = [
-    ":build_electron_definitions",
-  ]
-  type_root = rebase_path("$target_gen_dir/tsc/electron/typings")
-
-  sources = filenames.js_sources
-  if (enable_desktop_capturer) {
-    sources += [
-      "lib/browser/desktop-capturer.js",
-      "lib/renderer/api/desktop-capturer.js",
-    ]
-  }
-  if (enable_view_api) {
-    sources += [
-      "lib/browser/api/views/box-layout.js",
-      "lib/browser/api/views/button.js",
-      "lib/browser/api/views/label-button.js",
-      "lib/browser/api/views/layout-manager.js",
-      "lib/browser/api/views/md-text-button.js",
-      "lib/browser/api/views/resize-area.js",
-      "lib/browser/api/views/text-field.js",
-    ]
-  }
-
-  output_gen_dir = target_gen_electron_js
-  output_dir_name = "lib"
-  tsconfig = "tsconfig.electron.json"
-}
-
 asar("electron_asar") {
   deps = [
-    ":lib_js",
+    ":electron_browser_bundle",
+    ":electron_renderer_bundle",
+    ":electron_worker_bundle",
   ]
 
-  root = "$target_gen_electron_js/electron/lib"
-  sources = get_target_outputs(":lib_js")
+  root = "$target_gen_dir/electron_asar"
+  sources = get_target_outputs(":electron_browser_bundle") +
+            get_target_outputs(":electron_renderer_bundle") +
+            get_target_outputs(":electron_worker_bundle")
   outputs = [
     "$root_out_dir/resources/electron.asar",
   ]

+ 0 - 19
build/js_wrap.gni

@@ -1,19 +0,0 @@
-template("js_wrap") {
-  assert(defined(invoker.inputs), "Need input JS script")
-  assert(defined(invoker.outputs), "Need output JS script")
-
-  action(target_name) {
-    forward_variables_from(invoker,
-                           [
-                             "deps",
-                             "public_deps",
-                             "sources",
-                             "inputs",
-                             "outputs",
-                           ])
-
-    script = "//electron/build/js_wrap.py"
-    args = [ "--in" ] + rebase_path(invoker.inputs) + [ "--out" ] +
-           rebase_path(invoker.outputs)
-  }
-}

+ 0 - 19
build/js_wrap.py

@@ -1,19 +0,0 @@
-import sys
-
-in_start = sys.argv.index("--in") + 1
-out_start = sys.argv.index("--out") + 1
-
-in_bundles = sys.argv[in_start:out_start - 1]
-out_bundles = sys.argv[out_start:]
-
-if len(in_bundles) != len(out_bundles):
-  print("--out and --in must provide the same number of arguments")
-  sys.exit(1)
-
-for i in range(len(in_bundles)):
-  in_bundle = in_bundles[i]
-  out_path = out_bundles[i]
-  with open(in_bundle, 'r') as f:
-    lines = ["(function(){var exports={},module={exports};"] + f.readlines() + ["})();"]
-    with open(out_path, 'w') as out_f:
-      out_f.writelines(lines)

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

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

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

@@ -0,0 +1,22 @@
+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)
+}
+
+webpack(config, (err, stats) => {
+  if (err) {
+    console.error(err)
+    process.exit(1)
+  } else if (stats.hasErrors()) {
+    console.error(stats.toString('normal'))
+    process.exit(1)
+  } else {
+    process.exit(0)
+  }
+})

+ 80 - 0
build/webpack/webpack.config.base.js

@@ -0,0 +1,80 @@
+const fs = require('fs')
+const path = require('path')
+const webpack = require('webpack')
+
+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))
+        console.info(JSON.stringify(filePaths))
+      })
+    })
+  }
+}
+
+module.exports = ({
+  alwaysHasNode,
+  loadElectronFromAlternateTarget,
+  targetDeletesNodeGlobals,
+  target
+}) => {
+  let entry = path.resolve(electronRoot, 'lib', target, 'init.ts')
+  if (!fs.existsSync(entry)) {
+    entry = path.resolve(electronRoot, 'lib', target, 'init.js')
+  }
+
+  return ({
+    mode: 'development',
+    devtool: 'inline-source-map',
+    entry,
+    target: alwaysHasNode ? 'node' : 'web',
+    output: {
+      filename: `${target}.bundle.js`
+    },
+    resolve: {
+      alias: {
+        '@electron/internal': path.resolve(electronRoot, 'lib'),
+        'electron': path.resolve(electronRoot, 'lib', loadElectronFromAlternateTarget || target, 'api', 'exports', 'electron.js'),
+        // Force timers to resolve to our dependency that doens't use window.postMessage
+        'timers': path.resolve(electronRoot, 'node_modules', 'timers-browserify', 'main.js')
+      },
+      extensions: ['.ts', '.js']
+    },
+    module: {
+      rules: [{
+        test: /\.ts$/,
+        loader: 'ts-loader',
+        options: {
+          configFile: path.resolve(electronRoot, 'tsconfig.electron.json'),
+          transpileOnly: onlyPrintingGraph,
+          ignoreDiagnostics: [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,
+    },
+    plugins: [
+      new AccessDependenciesPlugin(),
+      ...(targetDeletesNodeGlobals ? [
+        new webpack.ProvidePlugin({
+          process: ['@electron/internal/renderer/webpack-provider', 'process'],
+          global: ['@electron/internal/renderer/webpack-provider', '_global'],
+          Buffer: ['@electron/internal/renderer/webpack-provider', 'Buffer'],
+        })
+      ] : [])
+    ]
+  })
+}

+ 4 - 0
build/webpack/webpack.config.browser.js

@@ -0,0 +1,4 @@
+module.exports = require('./webpack.config.base')({
+  target: 'browser',
+  alwaysHasNode: true
+})

+ 4 - 0
build/webpack/webpack.config.content_script.js

@@ -0,0 +1,4 @@
+module.exports = require('./webpack.config.base')({
+  target: 'content_script',
+  alwaysHasNode: false
+})

+ 4 - 0
build/webpack/webpack.config.isolated_renderer.js

@@ -0,0 +1,4 @@
+module.exports = require('./webpack.config.base')({
+  target: 'isolated_renderer',
+  alwaysHasNode: false
+})

+ 5 - 0
build/webpack/webpack.config.renderer.js

@@ -0,0 +1,5 @@
+module.exports = require('./webpack.config.base')({
+  target: 'renderer',
+  alwaysHasNode: true,
+  targetDeletesNodeGlobals: true
+})

+ 4 - 0
build/webpack/webpack.config.sandboxed_renderer.js

@@ -0,0 +1,4 @@
+module.exports = require('./webpack.config.base')({
+  target: 'sandboxed_renderer',
+  alwaysHasNode: false
+})

+ 6 - 0
build/webpack/webpack.config.worker.js

@@ -0,0 +1,6 @@
+module.exports = require('./webpack.config.base')({
+  target: 'worker',
+  loadElectronFromAlternateTarget: 'renderer',
+  alwaysHasNode: true,
+  targetDeletesNodeGlobals: true
+})

+ 35 - 0
build/webpack/webpack.gni

@@ -0,0 +1,35 @@
+import("../npm.gni")
+
+template("webpack_build") {
+  assert(defined(invoker.config_file), "Need webpack config file to run")
+  assert(defined(invoker.out_file), "Need output file to run")
+  assert(defined(invoker.inputs), "Need webpack inputs to run")
+
+  npm_action(target_name) {
+    forward_variables_from(invoker,
+                           [
+                             "deps",
+                             "public_deps",
+                           ])
+    script = "webpack"
+
+    inputs = [
+               invoker.config_file,
+               "//electron/build/webpack/webpack.config.base.js",
+               "//electron/tsconfig.json",
+               "//electron/yarn.lock",
+               "//electron/typings/internal-ambient.d.ts",
+               "//electron/typings/internal-electron.d.ts",
+               "//electron/typings/internal-helpers.d.ts",
+             ] + invoker.inputs
+
+    args = [
+      rebase_path(invoker.config_file),
+      rebase_path(invoker.out_file),
+    ]
+
+    outputs = [
+      invoker.out_file,
+    ]
+  }
+}

+ 1 - 2
docs/api/sandbox-option.md

@@ -119,8 +119,7 @@ Important things to notice in the preload script:
   `remote` and `ipcRenderer` modules.
 - The preload script must be contained in a single script, but it is possible to have
   complex preload code composed with multiple modules by using a tool like
-  browserify, as explained below. In fact, browserify is already used by
-  Electron to provide a node-like environment to the preload script.
+  webpack or browserify. An example of using browserify is below.
 
 To create a browserify bundle and use it as a preload script, something like
 the following should be used:

+ 177 - 8
filenames.auto.gni

@@ -112,13 +112,16 @@ auto_filenames = {
     "docs/api/structures/web-source.md",
   ]
 
-  sandbox_browserify_deps = [
-    "lib/browser/api/module-list.js",
+  sandbox_bundle_deps = [
+    "lib/browser/api/module-keys.js",
+    "lib/common/api/clipboard.js",
     "lib/common/api/deprecate.ts",
     "lib/common/api/is-promise.js",
     "lib/common/api/module-list.js",
     "lib/common/api/native-image.js",
+    "lib/common/api/shell.js",
     "lib/common/buffer-utils.js",
+    "lib/common/clipboard-utils.js",
     "lib/common/crash-reporter.js",
     "lib/common/electron-binding-setup.ts",
     "lib/common/error-utils.js",
@@ -142,29 +145,31 @@ auto_filenames = {
     "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/sandboxed_renderer/api/exports/electron.js",
     "lib/sandboxed_renderer/api/module-list.js",
     "lib/sandboxed_renderer/init.js",
-    "tsconfig.json",
-    "tsconfig.electron.json",
     "package.json",
+    "tsconfig.electron.json",
+    "tsconfig.json",
   ]
 
-  isolated_browserify_deps = [
+  isolated_bundle_deps = [
     "lib/common/electron-binding-setup.ts",
     "lib/isolated_renderer/init.js",
     "lib/renderer/ipc-renderer-internal.ts",
+    "lib/renderer/web-view/web-view-constants.ts",
     "lib/renderer/web-view/web-view-element.ts",
     "lib/renderer/window-setup.ts",
-    "tsconfig.json",
-    "tsconfig.electron.json",
     "package.json",
+    "tsconfig.electron.json",
+    "tsconfig.json",
   ]
 
-  context_script_browserify_deps = [
+  content_script_bundle_deps = [
     "lib/common/electron-binding-setup.ts",
     "lib/common/error-utils.js",
     "lib/content_script/init.js",
@@ -176,8 +181,172 @@ auto_filenames = {
     "lib/renderer/ipc-renderer-internal-utils.ts",
     "lib/renderer/ipc-renderer-internal.ts",
     "lib/renderer/window-setup.ts",
+    "package.json",
+    "tsconfig.electron.json",
+    "tsconfig.json",
+  ]
+
+  browser_bundle_deps = [
+    "lib/browser/api/app.ts",
+    "lib/browser/api/auto-updater.js",
+    "lib/browser/api/auto-updater/auto-updater-native.js",
+    "lib/browser/api/auto-updater/auto-updater-win.js",
+    "lib/browser/api/auto-updater/squirrel-update-win.js",
+    "lib/browser/api/browser-view.js",
+    "lib/browser/api/browser-window.js",
+    "lib/browser/api/content-tracing.js",
+    "lib/browser/api/crash-reporter.js",
+    "lib/browser/api/dialog.js",
+    "lib/browser/api/exports/electron.js",
+    "lib/browser/api/global-shortcut.js",
+    "lib/browser/api/in-app-purchase.js",
+    "lib/browser/api/ipc-main.ts",
+    "lib/browser/api/menu-item-roles.js",
+    "lib/browser/api/menu-item.js",
+    "lib/browser/api/menu-utils.js",
+    "lib/browser/api/menu.js",
+    "lib/browser/api/module-list.js",
+    "lib/browser/api/net-log.js",
+    "lib/browser/api/net.js",
+    "lib/browser/api/notification.js",
+    "lib/browser/api/power-monitor.ts",
+    "lib/browser/api/power-save-blocker.js",
+    "lib/browser/api/protocol.ts",
+    "lib/browser/api/screen.ts",
+    "lib/browser/api/session.js",
+    "lib/browser/api/system-preferences.js",
+    "lib/browser/api/top-level-window.js",
+    "lib/browser/api/touch-bar.js",
+    "lib/browser/api/tray.js",
+    "lib/browser/api/view.js",
+    "lib/browser/api/views/box-layout.js",
+    "lib/browser/api/views/button.js",
+    "lib/browser/api/views/label-button.js",
+    "lib/browser/api/views/layout-manager.js",
+    "lib/browser/api/views/md-text-button.js",
+    "lib/browser/api/views/resize-area.js",
+    "lib/browser/api/views/text-field.js",
+    "lib/browser/api/web-contents-view.js",
+    "lib/browser/api/web-contents.js",
+    "lib/browser/chrome-extension.js",
+    "lib/browser/crash-reporter-init.js",
+    "lib/browser/default-menu.ts",
+    "lib/browser/desktop-capturer.js",
+    "lib/browser/devtools.js",
+    "lib/browser/guest-view-manager.js",
+    "lib/browser/guest-window-manager.js",
+    "lib/browser/init.ts",
+    "lib/browser/ipc-main-internal-utils.ts",
+    "lib/browser/ipc-main-internal.ts",
+    "lib/browser/navigation-controller.js",
+    "lib/browser/objects-registry.js",
+    "lib/browser/rpc-server.js",
+    "lib/browser/utils.ts",
+    "lib/common/api/clipboard.js",
+    "lib/common/api/deprecate.ts",
+    "lib/common/api/exports/electron.js",
+    "lib/common/api/is-promise.js",
+    "lib/common/api/module-list.js",
+    "lib/common/api/native-image.js",
+    "lib/common/api/shell.js",
+    "lib/common/buffer-utils.js",
+    "lib/common/clipboard-utils.js",
+    "lib/common/crash-reporter.js",
+    "lib/common/electron-binding-setup.ts",
+    "lib/common/error-utils.js",
+    "lib/common/init.ts",
+    "lib/common/parse-features-string.js",
+    "lib/common/path-utils.ts",
+    "lib/common/reset-search-paths.ts",
+    "lib/common/web-view-methods.js",
+    "lib/renderer/ipc-renderer-internal-utils.ts",
+    "lib/renderer/ipc-renderer-internal.ts",
+    "package.json",
+    "tsconfig.electron.json",
     "tsconfig.json",
+  ]
+
+  renderer_bundle_deps = [
+    "lib/browser/api/module-keys.js",
+    "lib/common/api/clipboard.js",
+    "lib/common/api/deprecate.ts",
+    "lib/common/api/exports/electron.js",
+    "lib/common/api/is-promise.js",
+    "lib/common/api/module-list.js",
+    "lib/common/api/native-image.js",
+    "lib/common/api/shell.js",
+    "lib/common/buffer-utils.js",
+    "lib/common/clipboard-utils.js",
+    "lib/common/crash-reporter.js",
+    "lib/common/electron-binding-setup.ts",
+    "lib/common/error-utils.js",
+    "lib/common/init.ts",
+    "lib/common/path-utils.ts",
+    "lib/common/reset-search-paths.ts",
+    "lib/common/web-view-methods.js",
+    "lib/renderer/api/crash-reporter.js",
+    "lib/renderer/api/desktop-capturer.js",
+    "lib/renderer/api/exports/electron.js",
+    "lib/renderer/api/ipc-renderer.js",
+    "lib/renderer/api/module-list.js",
+    "lib/renderer/api/remote.js",
+    "lib/renderer/api/web-frame.ts",
+    "lib/renderer/callbacks-registry.js",
+    "lib/renderer/chrome-api.ts",
+    "lib/renderer/content-scripts-injector.ts",
+    "lib/renderer/extensions/event.js",
+    "lib/renderer/extensions/i18n.js",
+    "lib/renderer/extensions/storage.js",
+    "lib/renderer/extensions/web-navigation.js",
+    "lib/renderer/init.ts",
+    "lib/renderer/inspector.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/webpack-provider.ts",
+    "lib/renderer/window-setup.ts",
+    "package.json",
     "tsconfig.electron.json",
+    "tsconfig.json",
+  ]
+
+  worker_bundle_deps = [
+    "lib/browser/api/module-keys.js",
+    "lib/common/api/clipboard.js",
+    "lib/common/api/deprecate.ts",
+    "lib/common/api/exports/electron.js",
+    "lib/common/api/is-promise.js",
+    "lib/common/api/module-list.js",
+    "lib/common/api/native-image.js",
+    "lib/common/api/shell.js",
+    "lib/common/buffer-utils.js",
+    "lib/common/clipboard-utils.js",
+    "lib/common/crash-reporter.js",
+    "lib/common/electron-binding-setup.ts",
+    "lib/common/error-utils.js",
+    "lib/common/init.ts",
+    "lib/common/reset-search-paths.ts",
+    "lib/renderer/api/crash-reporter.js",
+    "lib/renderer/api/desktop-capturer.js",
+    "lib/renderer/api/exports/electron.js",
+    "lib/renderer/api/ipc-renderer.js",
+    "lib/renderer/api/module-list.js",
+    "lib/renderer/api/remote.js",
+    "lib/renderer/api/web-frame.ts",
+    "lib/renderer/callbacks-registry.js",
+    "lib/renderer/ipc-renderer-internal-utils.ts",
+    "lib/renderer/ipc-renderer-internal.ts",
+    "lib/renderer/webpack-provider.ts",
+    "lib/worker/init.js",
     "package.json",
+    "tsconfig.electron.json",
+    "tsconfig.json",
   ]
 }

+ 0 - 94
filenames.gni

@@ -1,98 +1,4 @@
 filenames = {
-  js_sources = [
-    "lib/browser/api/app.ts",
-    "lib/browser/api/auto-updater.js",
-    "lib/browser/api/auto-updater/auto-updater-native.js",
-    "lib/browser/api/auto-updater/auto-updater-win.js",
-    "lib/browser/api/auto-updater/squirrel-update-win.js",
-    "lib/browser/api/browser-view.js",
-    "lib/browser/api/browser-window.js",
-    "lib/browser/api/content-tracing.js",
-    "lib/browser/api/crash-reporter.js",
-    "lib/browser/api/dialog.js",
-    "lib/browser/api/exports/electron.js",
-    "lib/browser/api/global-shortcut.js",
-    "lib/browser/api/ipc-main.ts",
-    "lib/browser/api/in-app-purchase.js",
-    "lib/browser/api/menu-item-roles.js",
-    "lib/browser/api/menu-item.js",
-    "lib/browser/api/menu-utils.js",
-    "lib/browser/api/menu.js",
-    "lib/browser/api/module-list.js",
-    "lib/browser/api/net.js",
-    "lib/browser/api/net-log.js",
-    "lib/browser/api/notification.js",
-    "lib/browser/api/power-monitor.ts",
-    "lib/browser/api/power-save-blocker.js",
-    "lib/browser/api/protocol.ts",
-    "lib/browser/api/screen.ts",
-    "lib/browser/api/session.js",
-    "lib/browser/api/system-preferences.js",
-    "lib/browser/api/top-level-window.js",
-    "lib/browser/api/touch-bar.js",
-    "lib/browser/api/tray.js",
-    "lib/browser/api/view.js",
-    "lib/browser/api/web-contents.js",
-    "lib/browser/api/web-contents-view.js",
-    "lib/browser/devtools.js",
-    "lib/browser/chrome-extension.js",
-    "lib/browser/crash-reporter-init.js",
-    "lib/browser/default-menu.ts",
-    "lib/browser/guest-view-manager.js",
-    "lib/browser/guest-window-manager.js",
-    "lib/browser/init.ts",
-    "lib/browser/ipc-main-internal-utils.ts",
-    "lib/browser/ipc-main-internal.ts",
-    "lib/browser/navigation-controller.js",
-    "lib/browser/objects-registry.js",
-    "lib/browser/rpc-server.js",
-    "lib/browser/utils.ts",
-    "lib/common/api/clipboard.js",
-    "lib/common/api/deprecate.ts",
-    "lib/common/api/is-promise.js",
-    "lib/common/api/exports/electron.js",
-    "lib/common/api/module-list.js",
-    "lib/common/api/native-image.js",
-    "lib/common/api/shell.js",
-    "lib/common/buffer-utils.js",
-    "lib/common/clipboard-utils.js",
-    "lib/common/crash-reporter.js",
-    "lib/common/electron-binding-setup.ts",
-    "lib/common/error-utils.js",
-    "lib/common/init.ts",
-    "lib/common/parse-features-string.js",
-    "lib/common/path-utils.ts",
-    "lib/common/reset-search-paths.ts",
-    "lib/common/web-view-methods.js",
-    "lib/renderer/callbacks-registry.js",
-    "lib/renderer/chrome-api.ts",
-    "lib/renderer/content-scripts-injector.ts",
-    "lib/renderer/init.ts",
-    "lib/renderer/inspector.ts",
-    "lib/renderer/ipc-renderer-internal-utils.ts",
-    "lib/renderer/ipc-renderer-internal.ts",
-    "lib/renderer/security-warnings.ts",
-    "lib/renderer/window-setup.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/api/exports/electron.js",
-    "lib/renderer/api/crash-reporter.js",
-    "lib/renderer/api/ipc-renderer.js",
-    "lib/renderer/api/module-list.js",
-    "lib/renderer/api/remote.js",
-    "lib/renderer/api/web-frame.ts",
-    "lib/renderer/extensions/event.js",
-    "lib/renderer/extensions/i18n.js",
-    "lib/renderer/extensions/storage.js",
-    "lib/renderer/extensions/web-navigation.js",
-    "lib/worker/init.js",
-  ]
-
   default_app_ts_sources = [
     "default_app/default_app.ts",
     "default_app/index.ts",

+ 1 - 3
lib/browser/api/app.ts

@@ -1,6 +1,6 @@
 import * as path from 'path'
 
-import * as electron from 'electron'
+import { deprecate, Menu } from 'electron'
 import { EventEmitter } from 'events'
 
 const bindings = process.electronBinding('app')
@@ -10,8 +10,6 @@ const { app, App } = bindings
 // Only one app object permitted.
 export default app
 
-const { deprecate, Menu } = electron
-
 let dockMenu: Electron.Menu | null = null
 
 // App is an EventEmitter.

+ 1 - 6
lib/browser/api/exports/electron.js

@@ -10,11 +10,6 @@ common.defineProperties(exports)
 for (const module of moduleList) {
   Object.defineProperty(exports, module.name, {
     enumerable: !module.private,
-    get: common.memoizedGetter(() => {
-      const value = require(`@electron/internal/browser/api/${module.file}.js`)
-      // Handle Typescript modules with an "export default X" statement
-      if (value.__esModule) return value.default
-      return value
-    })
+    get: common.handleESModule(module.loader)
   })
 }

+ 51 - 0
lib/browser/api/module-keys.js

@@ -0,0 +1,51 @@
+'use strict'
+
+// TODO: Figure out a way to not duplicate this information between here and module-list
+// It is currently duplicated as module-list "require"s all the browser API file and the
+// remote module in the renderer process depends on that file.  As a result webpack
+// includes all the browser API files in the renderer process as well and we want to avoid that
+
+const features = process.electronBinding('features')
+
+// Browser side modules, please sort alphabetically.
+module.exports = [
+  { name: 'app' },
+  { name: 'autoUpdater' },
+  { name: 'BrowserView' },
+  { name: 'BrowserWindow' },
+  { name: 'contentTracing' },
+  { name: 'crashReporter' },
+  { name: 'dialog' },
+  { name: 'globalShortcut' },
+  { name: 'ipcMain' },
+  { name: 'inAppPurchase' },
+  { name: 'Menu' },
+  { name: 'MenuItem' },
+  { name: 'net' },
+  { name: 'netLog' },
+  { name: 'Notification' },
+  { name: 'powerMonitor' },
+  { name: 'powerSaveBlocker' },
+  { name: 'protocol' },
+  { name: 'screen' },
+  { name: 'session' },
+  { name: 'systemPreferences' },
+  { name: 'TopLevelWindow' },
+  { name: 'TouchBar' },
+  { name: 'Tray' },
+  { name: 'View' },
+  { name: 'webContents' },
+  { name: 'WebContentsView' }
+]
+
+if (features.isViewApiEnabled()) {
+  module.exports.push(
+    { name: 'BoxLayout' },
+    { name: 'Button' },
+    { name: 'LabelButton' },
+    { name: 'LayoutManager' },
+    { name: 'MdTextButton' },
+    { name: 'ResizeArea' },
+    { name: 'TextField' }
+  )
+}

+ 36 - 34
lib/browser/api/module-list.js

@@ -1,46 +1,48 @@
 'use strict'
 
+// TODO: Updating this file also required updating the module-keys file
+
 const features = process.electronBinding('features')
 
 // Browser side modules, please sort alphabetically.
 module.exports = [
-  { name: 'app', file: 'app' },
-  { name: 'autoUpdater', file: 'auto-updater' },
-  { name: 'BrowserView', file: 'browser-view' },
-  { name: 'BrowserWindow', file: 'browser-window' },
-  { name: 'contentTracing', file: 'content-tracing' },
-  { name: 'crashReporter', file: 'crash-reporter' },
-  { name: 'dialog', file: 'dialog' },
-  { name: 'globalShortcut', file: 'global-shortcut' },
-  { name: 'ipcMain', file: 'ipc-main' },
-  { name: 'inAppPurchase', file: 'in-app-purchase' },
-  { name: 'Menu', file: 'menu' },
-  { name: 'MenuItem', file: 'menu-item' },
-  { name: 'net', file: 'net' },
-  { name: 'netLog', file: 'net-log' },
-  { name: 'Notification', file: 'notification' },
-  { name: 'powerMonitor', file: 'power-monitor' },
-  { name: 'powerSaveBlocker', file: 'power-save-blocker' },
-  { name: 'protocol', file: 'protocol' },
-  { name: 'screen', file: 'screen' },
-  { name: 'session', file: 'session' },
-  { name: 'systemPreferences', file: 'system-preferences' },
-  { name: 'TopLevelWindow', file: 'top-level-window' },
-  { name: 'TouchBar', file: 'touch-bar' },
-  { name: 'Tray', file: 'tray' },
-  { name: 'View', file: 'view' },
-  { name: 'webContents', file: 'web-contents' },
-  { name: 'WebContentsView', file: 'web-contents-view' }
+  { name: 'app', loader: () => require('./app') },
+  { name: 'autoUpdater', loader: () => require('./auto-updater') },
+  { name: 'BrowserView', loader: () => require('./browser-view') },
+  { name: 'BrowserWindow', loader: () => require('./browser-window') },
+  { name: 'contentTracing', loader: () => require('./content-tracing') },
+  { name: 'crashReporter', loader: () => require('./crash-reporter') },
+  { name: 'dialog', loader: () => require('./dialog') },
+  { name: 'globalShortcut', loader: () => require('./global-shortcut') },
+  { name: 'ipcMain', loader: () => require('./ipc-main') },
+  { name: 'inAppPurchase', loader: () => require('./in-app-purchase') },
+  { name: 'Menu', loader: () => require('./menu') },
+  { name: 'MenuItem', loader: () => require('./menu-item') },
+  { name: 'net', loader: () => require('./net') },
+  { name: 'netLog', loader: () => require('./net-log') },
+  { name: 'Notification', loader: () => require('./notification') },
+  { name: 'powerMonitor', loader: () => require('./power-monitor') },
+  { name: 'powerSaveBlocker', loader: () => require('./power-save-blocker') },
+  { name: 'protocol', loader: () => require('./protocol') },
+  { name: 'screen', loader: () => require('./screen') },
+  { name: 'session', loader: () => require('./session') },
+  { name: 'systemPreferences', loader: () => require('./system-preferences') },
+  { name: 'TopLevelWindow', loader: () => require('./top-level-window') },
+  { name: 'TouchBar', loader: () => require('./touch-bar') },
+  { name: 'Tray', loader: () => require('./tray') },
+  { name: 'View', loader: () => require('./view') },
+  { name: 'webContents', loader: () => require('./web-contents') },
+  { name: 'WebContentsView', loader: () => require('./web-contents-view') }
 ]
 
 if (features.isViewApiEnabled()) {
   module.exports.push(
-    { name: 'BoxLayout', file: 'views/box-layout' },
-    { name: 'Button', file: 'views/button' },
-    { name: 'LabelButton', file: 'views/label-button' },
-    { name: 'LayoutManager', file: 'views/layout-manager' },
-    { name: 'MdTextButton', file: 'views/md-text-button' },
-    { name: 'ResizeArea', file: 'views/resize-area' },
-    { name: 'TextField', file: 'views/text-field' }
+    { name: 'BoxLayout', loader: () => require('./views/box-layout') },
+    { name: 'Button', loader: () => require('./views/button') },
+    { name: 'LabelButton', loader: () => require('./views/label-button') },
+    { name: 'LayoutManager', loader: () => require('./views/layout-manager') },
+    { name: 'MdTextButton', loader: () => require('./views/md-text-button') },
+    { name: 'ResizeArea', loader: () => require('./views/resize-area') },
+    { name: 'TextField', loader: () => require('./views/text-field') }
   )
 }

+ 3 - 2
lib/browser/crash-reporter-init.js

@@ -2,14 +2,15 @@
 
 const { app } = require('electron')
 const cp = require('child_process')
-const os = require('os')
 const path = require('path')
 
 const getTempDirectory = function () {
   try {
     return app.getPath('temp')
   } catch {
-    return os.tmpdir()
+    // Delibrately laze-load the os module, this file is on the hot
+    // path when booting Electron and os takes between 5 - 8ms to load and we do not need it yet
+    return require('os').tmpdir()
   }
 }
 

+ 2 - 7
lib/browser/init.ts

@@ -16,11 +16,6 @@ require('../common/reset-search-paths')
 // Import common settings.
 require('@electron/internal/common/init')
 
-const globalPaths = Module.globalPaths
-
-// Expose public APIs.
-globalPaths.push(path.join(__dirname, 'api', 'exports'))
-
 if (process.platform === 'win32') {
   // Redirect node's console to use our own implementations, since node can not
   // handle console output when running as GUI program.
@@ -112,7 +107,7 @@ if (process.resourcesPath) {
   for (packagePath of searchPaths) {
     try {
       packagePath = path.join(process.resourcesPath, packagePath)
-      packageJson = require(path.join(packagePath, 'package.json'))
+      packageJson = __non_webpack_require__(path.join(packagePath, 'package.json')) // eslint-disable-line
       break
     } catch {
       continue
@@ -194,7 +189,7 @@ app.on('window-all-closed', () => {
 
 Promise.all([
   import('@electron/internal/browser/default-menu'),
-  app.whenReady
+  app.whenReady()
 ]).then(([{ setDefaultApplicationMenu }]) => {
   // Create default menu
   setDefaultApplicationMenu()

+ 7 - 6
lib/common/api/exports/electron.js

@@ -2,6 +2,12 @@
 
 const moduleList = require('@electron/internal/common/api/module-list')
 
+exports.handleESModule = (loader) => () => {
+  const value = loader()
+  if (value.__esModule) return value.default
+  return value
+}
+
 exports.memoizedGetter = (getter) => {
   /*
    * It's ok to leak this value as it would be leaked by the global
@@ -24,12 +30,7 @@ exports.defineProperties = function (targetExports) {
   for (const module of moduleList) {
     descriptors[module.name] = {
       enumerable: !module.private,
-      get: exports.memoizedGetter(() => {
-        const value = require(`@electron/internal/common/api/${module.file}.js`)
-        // Handle Typescript modules with an "export default X" statement
-        if (value.__esModule) return value.default
-        return value
-      })
+      get: exports.handleESModule(module.loader)
     }
   }
   return Object.defineProperties(targetExports, descriptors)

+ 5 - 5
lib/common/api/module-list.js

@@ -2,10 +2,10 @@
 
 // Common modules, please sort alphabetically
 module.exports = [
-  { name: 'clipboard', file: 'clipboard' },
-  { name: 'nativeImage', file: 'native-image' },
-  { name: 'shell', file: 'shell' },
+  { name: 'clipboard', loader: () => require('./clipboard') },
+  { name: 'nativeImage', loader: () => require('./native-image') },
+  { name: 'shell', loader: () => require('./shell') },
   // The internal modules, invisible unless you know their names.
-  { name: 'deprecate', file: 'deprecate', private: true },
-  { name: 'isPromise', file: 'is-promise', private: true }
+  { name: 'deprecate', loader: () => require('./deprecate'), private: true },
+  { name: 'isPromise', loader: () => require('./is-promise'), private: true }
 ]

+ 16 - 12
lib/common/reset-search-paths.ts

@@ -5,9 +5,11 @@ const Module = require('module')
 // Clear Node's global search paths.
 Module.globalPaths.length = 0
 
-// Clear current and parent(init.js)'s search paths.
-module.paths = []
-module.parent!.paths = []
+// Clear current bundles search paths.
+const currentNodeModule = Module._cache[__filename]
+if (currentNodeModule) {
+  currentNodeModule.paths = []
+}
 
 // Prevent Node from adding paths outside this app to search paths.
 const resourcesPathWithTrailingSlash = process.resourcesPath + path.sep
@@ -25,19 +27,21 @@ Module._nodeModulePaths = function (from: string) {
   }
 }
 
-const BASE_INTERNAL_PATH = path.resolve(__dirname, '..')
-const INTERNAL_MODULE_PREFIX = '@electron/internal/'
+// Make a fake Electron module that we will insert into the module cache
+const electronModule = new Module('electron', null)
+electronModule.id = 'electron'
+electronModule.loaded = true
+electronModule.filename = 'electron'
+Object.defineProperty(electronModule, 'exports', {
+  get: () => require('electron')
+})
+
+Module._cache['electron'] = electronModule
 
-// Patch Module._resolveFilename to always require the Electron API when
-// require('electron') is done.
-const electronPath = path.join(__dirname, '..', process.type!, 'api', 'exports', 'electron.js')
 const originalResolveFilename = Module._resolveFilename
 Module._resolveFilename = function (request: string, parent: NodeModule, isMain: boolean) {
   if (request === 'electron') {
-    return electronPath
-  } else if (request.startsWith(INTERNAL_MODULE_PREFIX) && request.length > INTERNAL_MODULE_PREFIX.length) {
-    const slicedRequest = request.slice(INTERNAL_MODULE_PREFIX.length)
-    return path.resolve(BASE_INTERNAL_PATH, `${slicedRequest}${slicedRequest.endsWith('.js') ? '' : '.js'}`)
+    return 'electron'
   } else {
     return originalResolveFilename(request, parent, isMain)
   }

+ 1 - 1
lib/content_script/init.js

@@ -12,7 +12,7 @@ const v8Util = process.electronBinding('v8_util')
 // "ipc-internal" hidden value
 v8Util.setHiddenValue(global, 'ipc-internal', v8Util.getHiddenValue(isolatedWorld, 'ipc-internal'))
 
-// The process object created by browserify is not an event emitter, fix it so
+// The process object created by webpack is not an event emitter, fix it so
 // the API is more compatible with non-sandboxed renderers.
 for (const prop of Object.keys(EventEmitter.prototype)) {
   if (process.hasOwnProperty(prop)) {

+ 4 - 18
lib/renderer/api/exports/electron.js

@@ -6,23 +6,9 @@ const moduleList = require('@electron/internal/renderer/api/module-list')
 // Import common modules.
 common.defineProperties(exports)
 
-for (const {
-  name,
-  file,
-  enabled = true,
-  private: isPrivate = false
-} of moduleList) {
-  if (!enabled) {
-    continue
-  }
-
-  Object.defineProperty(exports, name, {
-    enumerable: !isPrivate,
-    get: common.memoizedGetter(() => {
-      const value = require(`@electron/internal/renderer/api/${file}.js`)
-      // Handle Typescript modules with an "export default X" statement
-      if (value.__esModule) return value.default
-      return value
-    })
+for (const module of moduleList) {
+  Object.defineProperty(exports, module.name, {
+    enumerable: !module.private,
+    get: common.handleESModule(module.loader)
   })
 }

+ 11 - 9
lib/renderer/api/module-list.js

@@ -8,13 +8,15 @@ const enableRemoteModule = v8Util.getHiddenValue(global, 'enableRemoteModule')
 // Renderer side modules, please sort alphabetically.
 // A module is `enabled` if there is no explicit condition defined.
 module.exports = [
-  { name: 'crashReporter', file: 'crash-reporter', enabled: true },
-  {
-    name: 'desktopCapturer',
-    file: 'desktop-capturer',
-    enabled: features.isDesktopCapturerEnabled()
-  },
-  { name: 'ipcRenderer', file: 'ipc-renderer' },
-  { name: 'remote', file: 'remote', enabled: enableRemoteModule },
-  { name: 'webFrame', file: 'web-frame' }
+  { name: 'crashReporter', loader: () => require('./crash-reporter') },
+  { name: 'ipcRenderer', loader: () => require('./ipc-renderer') },
+  { name: 'webFrame', loader: () => require('./web-frame') }
 ]
+
+if (features.isDesktopCapturerEnabled()) {
+  module.exports.push({ name: 'desktopCapturer', loader: () => require('./desktop-capturer') })
+}
+
+if (enableRemoteModule) {
+  module.exports.push({ name: 'remote', loader: () => require('./remote') })
+}

+ 1 - 1
lib/renderer/api/remote.js

@@ -350,7 +350,7 @@ const addBuiltinProperty = (name) => {
 
 const browserModules =
   require('@electron/internal/common/api/module-list').concat(
-    require('@electron/internal/browser/api/module-list'))
+    require('@electron/internal/browser/api/module-keys'))
 
 // And add a helper receiver for each one.
 browserModules

+ 11 - 12
lib/renderer/init.ts

@@ -14,6 +14,10 @@ const Module = require('module')
 // code, which does not work with this hack. However by modifying the
 // "Module.wrapper" we can force Node to use the old code path to wrap module
 // code with JavaScript.
+//
+// Note 3: We provide the equivilant 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.
 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
@@ -33,11 +37,6 @@ require('../common/reset-search-paths')
 // Import common settings.
 require('@electron/internal/common/init')
 
-const globalPaths = Module.globalPaths
-
-// Expose public APIs.
-globalPaths.push(path.join(__dirname, 'api', 'exports'))
-
 // The global variable will be used by ipc for event dispatching
 const v8Util = process.electronBinding('v8_util')
 
@@ -129,8 +128,8 @@ if (contextIsolation) {
 
 if (nodeIntegration) {
   // Export node bindings to global.
-  global.require = require
-  global.module = module
+  global.require = __non_webpack_require__ // eslint-disable-line
+  global.module = Module._cache[__filename]
 
   // Set the __filename to the path of html file if it is file: protocol.
   if (window.location.protocol === 'file:') {
@@ -140,7 +139,7 @@ if (nodeIntegration) {
     if (process.platform === 'win32') {
       if (pathname[0] === '/') pathname = pathname.substr(1)
 
-      const isWindowsNetworkSharePath = location.hostname.length > 0 && globalPaths[0].startsWith('\\')
+      const isWindowsNetworkSharePath = location.hostname.length > 0 && __filename.startsWith('\\')
       if (isWindowsNetworkSharePath) {
         pathname = `//${location.host}/${pathname}`
       }
@@ -150,17 +149,17 @@ if (nodeIntegration) {
     global.__dirname = path.dirname(global.__filename)
 
     // Set module's filename so relative require can work as expected.
-    module.filename = global.__filename
+    global.module.filename = global.__filename
 
     // Also search for module under the html file.
-    module.paths = module.paths.concat(Module._nodeModulePaths(global.__dirname))
+    global.module.paths = global.module.paths.concat(Module._nodeModulePaths(global.__dirname))
   } else {
     global.__filename = __filename
     global.__dirname = __dirname
 
     if (appPath) {
       // Search for module under the app directory
-      module.paths = module.paths.concat(Module._nodeModulePaths(appPath))
+      global.module.paths = global.module.paths.concat(Module._nodeModulePaths(appPath))
     }
   }
 
@@ -204,7 +203,7 @@ for (const preloadScript of preloadScripts) {
     if (!isParentDir(getAppPath(), fs.realpathSync(preloadScript))) {
       throw new Error('Preload scripts outside of app path are not allowed')
     }
-    require(preloadScript)
+    __non_webpack_require__(preloadScript)  // eslint-disable-line
   } catch (error) {
     console.error(`Unable to load preload script: ${preloadScript}`)
     console.error(`${error}`)

+ 2 - 2
lib/renderer/security-warnings.ts

@@ -3,6 +3,8 @@ import { invokeSync } from '@electron/internal/renderer/ipc-renderer-internal-ut
 
 let shouldLog: boolean | null = null
 
+const { platform, execPath, env } = process
+
 /**
  * This method checks if a security message should be logged.
  * It does so by determining whether we're running as Electron,
@@ -16,8 +18,6 @@ const shouldLogSecurityWarnings = function (): boolean {
     return shouldLog
   }
 
-  const { platform, execPath, env } = process
-
   switch (platform) {
     case 'darwin':
       shouldLog = execPath.endsWith('MacOS/Electron') ||

+ 18 - 0
lib/renderer/webpack-provider.ts

@@ -0,0 +1,18 @@
+// 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 = (self as any || window as any).global as NodeJS.Global
+const process = _global.process
+const Buffer = _global.Buffer
+
+export {
+  _global,
+  process,
+  Buffer
+}

+ 4 - 16
lib/sandboxed_renderer/init.js

@@ -8,7 +8,7 @@ const { EventEmitter } = events
 process.electronBinding = require('@electron/internal/common/electron-binding-setup').electronBindingSetup(binding.get, 'renderer')
 
 const v8Util = process.electronBinding('v8_util')
-// Expose browserify Buffer as a hidden value. This is used by C++ code to
+// Expose Buffer shim as a hidden value. This is used by C++ code to
 // deserialize Buffer instances sent from browser process.
 v8Util.setHiddenValue(global, 'Buffer', Buffer)
 // The `lib/renderer/api/ipc-renderer.js` module looks for the ipc object in the
@@ -17,7 +17,7 @@ v8Util.setHiddenValue(global, 'ipc', new EventEmitter())
 // The `lib/renderer/ipc-renderer-internal.js` module looks for the ipc object in the
 // "ipc-internal" hidden value
 v8Util.setHiddenValue(global, 'ipc-internal', new EventEmitter())
-// The process object created by browserify is not an event emitter, fix it so
+// The process object created by webpack is not an event emitter, fix it so
 // the API is more compatible with non-sandboxed renderers.
 for (const prop of Object.keys(EventEmitter.prototype)) {
   if (process.hasOwnProperty(prop)) {
@@ -143,20 +143,8 @@ const errorUtils = require('@electron/internal/common/error-utils')
 //
 // - `require`: The `preloadRequire` function
 // - `process`: The `preloadProcess` object
-// - `Buffer`: Browserify `Buffer` implementation
-// - `global`: The window object, which is aliased to `global` by browserify.
-//
-// Browserify bundles can make use of an external require function as explained
-// in https://github.com/substack/node-browserify#multiple-bundles, so electron
-// apps can use multi-module preload scripts in sandboxed renderers.
-//
-// For example, the user can create a bundle with:
-//
-//     $ browserify -x electron preload.js > renderer.js
-//
-// and any `require('electron')` calls in `preload.js` will work as expected
-// since browserify won't try to include `electron` in the bundle, falling back
-// to the `preloadRequire` function above.
+// - `Buffer`: Shim of `Buffer` implementation
+// - `global`: The window object, which is aliased to `global` by webpack.
 function runPreloadScript (preloadSrc) {
   const preloadWrapperSrc = `(function(require, process, Buffer, global, setImmediate, clearImmediate, exports) {
   ${preloadSrc}

+ 4 - 7
lib/worker/init.js

@@ -13,12 +13,9 @@ require('../common/reset-search-paths')
 // Import common settings.
 require('@electron/internal/common/init')
 
-// Expose public APIs.
-Module.globalPaths.push(path.join(__dirname, 'api', 'exports'))
-
 // Export node bindings to global.
-global.require = require
-global.module = module
+global.require = __non_webpack_require__ // eslint-disable-line
+global.module = Module._cache[__filename]
 
 // Set the __filename to the path of html file if it is file: protocol.
 if (self.location.protocol === 'file:') {
@@ -27,10 +24,10 @@ if (self.location.protocol === 'file:') {
   global.__dirname = path.dirname(global.__filename)
 
   // Set module's filename so relative require can work as expected.
-  module.filename = global.__filename
+  global.module.filename = global.__filename
 
   // Also search for module under the html file.
-  module.paths = module.paths.concat(Module._nodeModulePaths(global.__dirname))
+  global.module.paths = global.module.paths.concat(Module._nodeModulePaths(global.__dirname))
 } else {
   global.__filename = __filename
   global.__dirname = __dirname

+ 9 - 17
package.json

@@ -14,11 +14,11 @@
     "@types/mocha": "^5.2.6",
     "@types/node": "^10.12.21",
     "@types/split": "^1.0.0",
+    "@types/webpack": "^4.4.32",
+    "@types/webpack-env": "^1.13.9",
     "@typescript-eslint/eslint-plugin": "^1.4.2",
     "@typescript-eslint/parser": "^1.4.2",
-    "aliasify": "^2.1.0",
     "asar": "^1.0.0",
-    "browserify": "^16.2.3",
     "check-for-leaks": "^1.0.2",
     "clang-format": "^1.2.3",
     "colors": "^1.1.2",
@@ -51,14 +51,16 @@
     "standard-markdown": "^5.0.0",
     "sumchecker": "^2.0.2",
     "temp": "^0.8.3",
+    "timers-browserify": "1.4.2",
+    "ts-loader": "^6.0.2",
     "ts-node": "^6.0.3",
-    "tsify": "^4.0.1",
-    "typescript": "~3.3.3333"
+    "typescript": "~3.3.3333",
+    "webpack": "^4.32.2",
+    "webpack-cli": "^3.3.2"
   },
   "private": true,
   "scripts": {
     "asar": "asar",
-    "browserify": "browserify",
     "bump-version": "./script/bump-version.js",
     "check-tls": "python ./script/tls.py",
     "clang-format": "find atom/ chromium_src/ -iname *.h -o -iname *.cc -o -iname *.mm | xargs clang-format -i",
@@ -82,24 +84,14 @@
     "repl": "node ./script/start.js --interactive",
     "start": "node ./script/start.js",
     "test": "node ./script/spec-runner.js",
-    "tsc": "tsc"
+    "tsc": "tsc",
+    "webpack": "node build/webpack/run-compiler"
   },
   "license": "MIT",
   "author": "Electron Community",
   "keywords": [
     "electron"
   ],
-  "aliasify": {
-    "replacements": {
-      "@electron/internal/(.+)": "./lib/$1"
-    },
-    "appliesTo": {
-      "includeExtensions": [
-        ".js",
-        ".ts"
-      ]
-    }
-  },
   "husky": {
     "hooks": {
       "pre-commit": "lint-staged",

+ 28 - 33
script/gen-filenames.js

@@ -14,41 +14,39 @@ const allDocs = fs.readdirSync(path.resolve(__dirname, '../docs/api'))
   )
 
 const main = async () => {
-  const browserifyTargets = [
+  const webpackTargets = [
     {
-      name: 'sandbox_browserify_deps',
-      entry: 'lib/sandboxed_renderer/init.js'
+      name: 'sandbox_bundle_deps',
+      config: 'webpack.config.sandboxed_renderer.js'
     },
     {
-      name: 'isolated_browserify_deps',
-      entry: 'lib/isolated_renderer/init.js'
+      name: 'isolated_bundle_deps',
+      config: 'webpack.config.isolated_renderer.js'
     },
     {
-      name: 'context_script_browserify_deps',
-      entry: 'lib/content_script/init.js'
+      name: 'content_script_bundle_deps',
+      config: 'webpack.config.content_script.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'
     }
   ]
 
-  await Promise.all(browserifyTargets.map(async browserifyTarget => {
+  await Promise.all(webpackTargets.map(async webpackTarget => {
     const tmpDir = await fs.mkdtemp(path.resolve(os.tmpdir(), 'electron-filenames-'))
     const child = cp.spawn('node', [
-      'node_modules/browserify/bin/cmd.js',
-      browserifyTarget.entry,
-      ...(browserifyTarget.name === 'sandbox_browserify_deps' ? [
-        '-r',
-        './lib/sandboxed_renderer/api/exports/electron.js:electron'
-      ] : []),
-      '-t',
-      'aliasify',
-      '-p',
-      '[',
-      'tsify',
-      '-p',
-      'tsconfig.electron.json',
-      ']',
-      '-o',
-      path.resolve(tmpDir, 'out.js'),
-      '--list'
+      'build/webpack/get-outputs.js',
+      `./${webpackTarget.config}`,
+      path.resolve(tmpDir, `${webpackTarget.name}.measure.js`)
     ], {
       cwd: path.resolve(__dirname, '..')
     })
@@ -60,28 +58,25 @@ const main = async () => {
     await new Promise((resolve, reject) => child.on('exit', (code) => {
       if (code !== 0) {
         console.error(output)
-        return reject(new Error(`Failed to list browserify dependencies for entry: ${browserifyTarget.name}`))
+        return reject(new Error(`Failed to list webpack dependencies for entry: ${webpackTarget.name}`))
       }
 
       resolve()
     }))
 
-    browserifyTarget.dependencies = output
-      .split('\n')
+    webpackTarget.dependencies = JSON.parse(output)
       // Remove whitespace
       .map(line => line.trim())
-      // Ignore empty lines
-      .filter(line => line)
       // Get the relative path
       .map(line => path.relative(rootPath, line))
       // Only care about files in //electron
       .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'])
       // Make the generated list easier to read
       .sort()
-      // All browserify commands depend on the tsconfig  and package json files
-      .concat(['tsconfig.json', 'tsconfig.electron.json', 'package.json'])
     await fs.remove(tmpDir)
   }))
 
@@ -93,7 +88,7 @@ auto_filenames = {
 ${allDocs.map(doc => `    "${doc}",`).join('\n')}
   ]
 
-${browserifyTargets.map(target => `  ${target.name} = [
+${webpackTargets.map(target => `  ${target.name} = [
 ${target.dependencies.map(dep => `    "${dep}",`).join('\n')}
   ]`).join('\n\n')}
 }

+ 1 - 1
spec/asar-spec.js

@@ -42,7 +42,7 @@ describe('asar package', function () {
       it('does not leak fd', function () {
         let readCalls = 1
         while (readCalls <= 10000) {
-          fs.readFileSync(path.join(process.resourcesPath, 'electron.asar', 'renderer', 'api', 'ipc-renderer.js'))
+          fs.readFileSync(path.join(process.resourcesPath, 'electron.asar', 'renderer', 'init.js'))
           readCalls++
         }
       })

+ 1 - 1
spec/internal-spec.js

@@ -3,7 +3,7 @@ const { expect } = chai
 
 describe('feature-string parsing', () => {
   it('is indifferent to whitespace around keys and values', () => {
-    const parseFeaturesString = require('@electron/internal/common/parse-features-string')
+    const parseFeaturesString = require('../lib/common/parse-features-string')
     const checkParse = (string, parsed) => {
       const features = {}
       parseFeaturesString(string, (k, v) => { features[k] = v })

+ 0 - 19
tools/list-browserify-deps.py

@@ -1,19 +0,0 @@
-#!/usr/bin/env python
-import os
-import subprocess
-import sys
-
-
-SOURCE_ROOT = os.path.dirname(os.path.dirname(__file__))
-BROWSERIFY = os.path.join(SOURCE_ROOT, 'node_modules', '.bin', 'browserify')
-if sys.platform == 'win32':
-  BROWSERIFY += '.cmd'
-
-deps = subprocess.check_output([BROWSERIFY, '--list'] + sys.argv[1:])
-for dep in deps.split('\n'):
-    if dep:
-        dep = os.path.relpath(dep, SOURCE_ROOT)
-        if sys.platform == 'win32':
-            print('/'.join(dep.split('\\')))
-        else:
-            print(dep)

File diff suppressed because it is too large
+ 406 - 210
yarn.lock


Some files were not shown because too many files changed in this diff