Browse Source

[RFC] perf: use an internal module resolver to improve require performance (#14633)

* perf: use an internal module resolver instead of relative requires

* perf: memoize the results of getting exported Electron properties

* perf: make internal module changes consistent across sandboxed / bundled files
Samuel Attard 6 years ago
parent
commit
54ef906832

+ 4 - 0
BUILD.gn

@@ -94,6 +94,8 @@ npm_action("atom_browserify_sandbox") {
     "./lib/sandboxed_renderer/api/exports/path.js:path",
     "-r",
     "./lib/sandboxed_renderer/api/exports/child_process.js:child_process",
+    "-t",
+    "aliasify"
   ]
 
   inputs = [
@@ -129,6 +131,8 @@ npm_action("atom_browserify_isolated") {
 
   script = "browserify"
   args = inputs + [
+           "-t",
+           "aliasify",
            "-o",
            rebase_path(outputs[0]),
          ]

+ 2 - 2
lib/browser/api/auto-updater.js

@@ -1,5 +1,5 @@
 if (process.platform === 'win32') {
-  module.exports = require('./auto-updater/auto-updater-win')
+  module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-win')
 } else {
-  module.exports = require('./auto-updater/auto-updater-native')
+  module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-native')
 }

+ 1 - 1
lib/browser/api/auto-updater/auto-updater-win.js

@@ -2,7 +2,7 @@
 
 const { app } = require('electron')
 const { EventEmitter } = require('events')
-const squirrelUpdate = require('./squirrel-update-win')
+const squirrelUpdate = require('@electron/internal/browser/api/auto-updater/squirrel-update-win')
 
 class AutoUpdater extends EventEmitter {
   quitAndInstall () {

+ 3 - 3
lib/browser/api/exports/electron.js

@@ -1,6 +1,6 @@
-const common = require('../../../common/api/exports/electron')
+const common = require('@electron/internal/common/api/exports/electron')
 // since browser module list is also used in renderer, keep it separate.
-const moduleList = require('../module-list')
+const moduleList = require('@electron/internal/browser/api/module-list')
 
 // Import common modules.
 common.defineProperties(exports)
@@ -8,6 +8,6 @@ common.defineProperties(exports)
 for (const module of moduleList) {
   Object.defineProperty(exports, module.name, {
     enumerable: !module.private,
-    get: () => require(`../${module.file}`)
+    get: common.memoizedGetter(() => require(`@electron/internal/browser/api/${module.file}.js`))
   })
 }

+ 1 - 1
lib/browser/api/menu-item.js

@@ -1,6 +1,6 @@
 'use strict'
 
-const roles = require('./menu-item-roles')
+const roles = require('@electron/internal/browser/api/menu-item-roles')
 
 let nextCommandId = 0
 

+ 1 - 1
lib/browser/api/menu.js

@@ -1,7 +1,7 @@
 'use strict'
 
 const { TopLevelWindow, MenuItem, webContents } = require('electron')
-const { sortMenuItems } = require('./menu-utils')
+const { sortMenuItems } = require('@electron/internal/browser/api/menu-utils')
 const EventEmitter = require('events').EventEmitter
 const v8Util = process.atomBinding('v8_util')
 const bindings = process.atomBinding('menu')

+ 1 - 1
lib/browser/api/web-contents.js

@@ -6,7 +6,7 @@ const path = require('path')
 const url = require('url')
 const { app, ipcMain, session, NavigationController, deprecate } = electron
 
-const errorUtils = require('../../common/error-utils')
+const errorUtils = require('@electron/internal/common/error-utils')
 
 // session is not used here, the purpose is to make sure session is initalized
 // before the webContents module.

+ 1 - 1
lib/browser/guest-view-manager.js

@@ -1,7 +1,7 @@
 'use strict'
 
 const { ipcMain, webContents } = require('electron')
-const parseFeaturesString = require('../common/parse-features-string')
+const parseFeaturesString = require('@electron/internal/common/parse-features-string')
 
 // Doesn't exist in early initialization.
 let webViewManager = null

+ 1 - 1
lib/browser/guest-window-manager.js

@@ -2,7 +2,7 @@
 
 const { BrowserWindow, ipcMain, webContents } = require('electron')
 const { isSameOrigin } = process.atomBinding('v8_util')
-const parseFeaturesString = require('../common/parse-features-string')
+const parseFeaturesString = require('@electron/internal/common/parse-features-string')
 
 const hasProp = {}.hasOwnProperty
 const frameToGuest = new Map()

+ 7 - 7
lib/browser/init.js

@@ -15,7 +15,7 @@ process.argv.splice(1, 1)
 require('../common/reset-search-paths')
 
 // Import common settings.
-require('../common/init')
+require('@electron/internal/common/init')
 
 var globalPaths = Module.globalPaths
 
@@ -94,11 +94,11 @@ if (process.platform === 'win32') {
 process.exit = app.exit
 
 // Load the RPC server.
-require('./rpc-server')
+require('@electron/internal/browser/rpc-server')
 
 // Load the guest view manager.
-require('./guest-view-manager')
-require('./guest-window-manager')
+require('@electron/internal/browser/guest-view-manager')
+require('@electron/internal/browser/guest-window-manager')
 
 // Now we try to load app's package.json.
 let packagePath = null
@@ -151,16 +151,16 @@ app.setPath('userCache', path.join(app.getPath('cache'), app.getName()))
 app.setAppPath(packagePath)
 
 // Load the chrome extension support.
-require('./chrome-extension')
+require('@electron/internal/browser/chrome-extension')
 
 const features = process.atomBinding('features')
 if (features.isDesktopCapturerEnabled()) {
   // Load internal desktop-capturer module.
-  require('./desktop-capturer')
+  require('@electron/internal/browser/desktop-capturer')
 }
 
 // Load protocol module to ensure it is populated on app ready
-require('./api/protocol')
+require('@electron/internal/browser/api/protocol')
 
 // Set main startup script of the app.
 const mainStartupScript = packageJson.main || 'index.js'

+ 4 - 4
lib/browser/rpc-server.js

@@ -7,8 +7,8 @@ const v8Util = process.atomBinding('v8_util')
 
 const { ipcMain, isPromise } = electron
 
-const objectsRegistry = require('./objects-registry')
-const bufferUtils = require('../common/buffer-utils')
+const objectsRegistry = require('@electron/internal/browser/objects-registry')
+const bufferUtils = require('@electron/internal/common/buffer-utils')
 
 const hasProp = {}.hasOwnProperty
 
@@ -367,13 +367,13 @@ handleRemoteCommand('ELECTRON_BROWSER_CONTEXT_RELEASE', (event, contextId) => {
 })
 
 handleRemoteCommand('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, contextId, guestInstanceId) {
-  let guestViewManager = require('./guest-view-manager')
+  let guestViewManager = require('@electron/internal/browser/guest-view-manager')
   return valueToMeta(event.sender, contextId, guestViewManager.getGuest(guestInstanceId))
 })
 
 ipcMain.on('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function (event, contextId, requestId, guestInstanceId, method, ...args) {
   try {
-    let guestViewManager = require('./guest-view-manager')
+    let guestViewManager = require('@electron/internal/browser/guest-view-manager')
     let guest = guestViewManager.getGuest(guestInstanceId)
     if (requestId) {
       const responseCallback = function (result) {

+ 21 - 5
lib/common/api/exports/electron.js

@@ -1,13 +1,29 @@
-const moduleList = require('../module-list')
+const moduleList = require('@electron/internal/common/api/module-list')
 
-// Attaches properties to |exports|.
-exports.defineProperties = function (exports) {
+exports.memoizedGetter = (getter) => {
+  /*
+   * It's ok to leak this value as it would be leaked by the global
+   * node module cache anyway at `Module._cache`.  This memoization
+   * is dramatically faster than relying on nodes module cache however
+   */
+  let memoizedValue = null
+
+  return () => {
+    if (memoizedValue === null) {
+      memoizedValue = getter()
+    }
+    return memoizedValue
+  }
+}
+
+// Attaches properties to |targetExports|.
+exports.defineProperties = function (targetExports) {
   const descriptors = {}
   for (const module of moduleList) {
     descriptors[module.name] = {
       enumerable: !module.private,
-      get: () => require(`../${module.file}`)
+      get: exports.memoizedGetter(() => require(`@electron/internal/common/api/${module.file}`))
     }
   }
-  return Object.defineProperties(exports, descriptors)
+  return Object.defineProperties(targetExports, descriptors)
 }

+ 1 - 1
lib/common/init.js

@@ -1,7 +1,7 @@
 const timers = require('timers')
 const util = require('util')
 
-process.atomBinding = require('./atom-binding-setup')(process.binding, process.type)
+process.atomBinding = require('@electron/internal/common/atom-binding-setup')(process.binding, process.type)
 
 // setImmediate and process.nextTick makes use of uv_check and uv_prepare to
 // run the callbacks, however since we only run uv loop on requests, the

+ 6 - 0
lib/common/reset-search-paths.js

@@ -24,6 +24,9 @@ Module._nodeModulePaths = function (from) {
   }
 }
 
+const BASE_INTERNAL_PATH = path.resolve(__dirname, '..')
+const INTERNAL_MODULE_PREFIX = '@electron/internal/'
+
 // 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')
@@ -31,6 +34,9 @@ const originalResolveFilename = Module._resolveFilename
 Module._resolveFilename = function (request, parent, isMain) {
   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'}`)
   } else {
     return originalResolveFilename(request, parent, isMain)
   }

+ 1 - 1
lib/isolated_renderer/init.js

@@ -22,4 +22,4 @@ let { guestInstanceId, hiddenPage, openerId, nativeWindowOpen } = binding
 if (guestInstanceId != null) guestInstanceId = parseInt(guestInstanceId)
 if (openerId != null) openerId = parseInt(openerId)
 
-require('../renderer/window-setup')(ipcRenderer, guestInstanceId, openerId, hiddenPage, nativeWindowOpen)
+require('@electron/internal/renderer/window-setup')(ipcRenderer, guestInstanceId, openerId, hiddenPage, nativeWindowOpen)

+ 3 - 3
lib/renderer/api/exports/electron.js

@@ -1,5 +1,5 @@
-const common = require('../../../common/api/exports/electron')
-const moduleList = require('../module-list')
+const common = require('@electron/internal/common/api/exports/electron')
+const moduleList = require('@electron/internal/renderer/api/module-list')
 
 // Import common modules.
 common.defineProperties(exports)
@@ -16,6 +16,6 @@ for (const {
 
   Object.defineProperty(exports, name, {
     enumerable: !isPrivate,
-    get: () => require(`../${file}`)
+    get: common.memoizedGetter(() => require(`@electron/internal/renderer/api/${file}`))
   })
 }

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

@@ -4,8 +4,8 @@ const v8Util = process.atomBinding('v8_util')
 const { ipcRenderer, isPromise } = require('electron')
 const resolvePromise = Promise.resolve.bind(Promise)
 
-const CallbacksRegistry = require('../callbacks-registry')
-const bufferUtils = require('../../common/buffer-utils')
+const CallbacksRegistry = require('@electron/internal/renderer/callbacks-registry')
+const bufferUtils = require('@electron/internal/common/buffer-utils')
 
 const callbacksRegistry = new CallbacksRegistry()
 const remoteObjectCache = v8Util.createIDWeakMap()
@@ -349,8 +349,8 @@ const addBuiltinProperty = (name) => {
 }
 
 const browserModules =
-  require('../../common/api/module-list').concat(
-    require('../../browser/api/module-list'))
+  require('@electron/internal/common/api/module-list').concat(
+    require('@electron/internal/browser/api/module-list'))
 
 // And add a helper receiver for each one.
 browserModules

+ 4 - 4
lib/renderer/chrome-api.js

@@ -1,5 +1,5 @@
 const { ipcRenderer } = require('electron')
-const Event = require('./extensions/event')
+const Event = require('@electron/internal/renderer/extensions/event')
 const url = require('url')
 
 let nextId = 0
@@ -175,7 +175,7 @@ exports.injectTo = function (extensionId, isBackgroundPage, context) {
     onMessage: chrome.runtime.onMessage
   }
 
-  chrome.storage = require('./extensions/storage').setup(extensionId)
+  chrome.storage = require('@electron/internal/renderer/extensions/storage').setup(extensionId)
 
   chrome.pageAction = {
     show () {},
@@ -187,6 +187,6 @@ exports.injectTo = function (extensionId, isBackgroundPage, context) {
     getPopup () {}
   }
 
-  chrome.i18n = require('./extensions/i18n').setup(extensionId)
-  chrome.webNavigation = require('./extensions/web-navigation').setup()
+  chrome.i18n = require('@electron/internal/renderer/extensions/i18n').setup(extensionId)
+  chrome.webNavigation = require('@electron/internal/renderer/extensions/web-navigation').setup()
 }

+ 1 - 1
lib/renderer/content-scripts-injector.js

@@ -13,7 +13,7 @@ const matchesPattern = function (pattern) {
 // Run the code with chrome API integrated.
 const runContentScript = function (extensionId, url, code) {
   const context = {}
-  require('./chrome-api').injectTo(extensionId, false, context)
+  require('@electron/internal/renderer/chrome-api').injectTo(extensionId, false, context)
   const wrapper = `((chrome) => {\n  ${code}\n  })`
   const compiledWrapper = runInThisContext(wrapper, {
     filename: url,

+ 1 - 1
lib/renderer/extensions/web-navigation.js

@@ -1,4 +1,4 @@
-const Event = require('./event')
+const Event = require('@electron/internal/renderer/extensions/event')
 const { ipcRenderer } = require('electron')
 
 class WebNavigation {

+ 9 - 9
lib/renderer/init.js

@@ -12,7 +12,7 @@ process.argv.splice(1, 1)
 require('../common/reset-search-paths')
 
 // Import common settings.
-require('../common/init')
+require('@electron/internal/common/init')
 
 var globalPaths = Module.globalPaths
 
@@ -37,9 +37,9 @@ const {
   warnAboutInsecureCSP,
   warnAboutAllowedPopups,
   shouldLogSecurityWarnings
-} = require('./security-warnings')
+} = require('@electron/internal/renderer/security-warnings')
 
-require('./web-frame-init')()
+require('@electron/internal/renderer/web-frame-init')()
 
 // Process command line arguments.
 let nodeIntegration = 'false'
@@ -77,26 +77,26 @@ if (preloadScript) {
 
 if (window.location.protocol === 'chrome-devtools:') {
   // Override some inspector APIs.
-  require('./inspector')
+  require('@electron/internal/renderer/inspector')
   nodeIntegration = 'false'
 } else if (window.location.protocol === 'chrome-extension:') {
   // Add implementations of chrome API.
-  require('./chrome-api').injectTo(window.location.hostname, isBackgroundPage, window)
+  require('@electron/internal/renderer/chrome-api').injectTo(window.location.hostname, isBackgroundPage, window)
   nodeIntegration = 'false'
 } else if (window.location.protocol === 'chrome:') {
   // Disable node integration for chrome UI scheme.
   nodeIntegration = 'false'
 } else {
   // Override default web functions.
-  require('./override')
+  require('@electron/internal/renderer/override')
 
   // Inject content scripts.
-  require('./content-scripts-injector')
+  require('@electron/internal/renderer/content-scripts-injector')
 
   // Load webview tag implementation.
   if (webviewTag === 'true' && process.guestInstanceId == null) {
-    require('./web-view/web-view')
-    require('./web-view/web-view-attributes')
+    require('@electron/internal/renderer/web-view/web-view')
+    require('@electron/internal/renderer/web-view/web-view-attributes')
   }
 }
 

+ 1 - 1
lib/renderer/override.js

@@ -6,4 +6,4 @@ const { guestInstanceId, openerId } = process
 const hiddenPage = process.argv.includes('--hidden-page')
 const usesNativeWindowOpen = process.argv.includes('--native-window-open')
 
-require('./window-setup')(ipcRenderer, guestInstanceId, openerId, hiddenPage, usesNativeWindowOpen)
+require('@electron/internal/renderer/window-setup')(ipcRenderer, guestInstanceId, openerId, hiddenPage, usesNativeWindowOpen)

+ 1 - 1
lib/renderer/web-frame-init.js

@@ -1,5 +1,5 @@
 const { ipcRenderer, webFrame } = require('electron')
-const errorUtils = require('../common/error-utils')
+const errorUtils = require('@electron/internal/common/error-utils')
 
 module.exports = () => {
   // Call webFrame method

+ 2 - 2
lib/renderer/web-view/web-view-attributes.js

@@ -1,7 +1,7 @@
 'use strict'
 
-const WebViewImpl = require('./web-view')
-const webViewConstants = require('./web-view-constants')
+const WebViewImpl = require('@electron/internal/renderer/web-view/web-view')
+const webViewConstants = require('@electron/internal/renderer/web-view/web-view-constants')
 const { remote } = require('electron')
 
 // Helper function to resolve url set in attribute.

+ 2 - 2
lib/renderer/web-view/web-view.js

@@ -3,8 +3,8 @@
 const { ipcRenderer, remote, webFrame } = require('electron')
 
 const v8Util = process.atomBinding('v8_util')
-const guestViewInternal = require('./guest-view-internal')
-const webViewConstants = require('./web-view-constants')
+const guestViewInternal = require('@electron/internal/renderer/web-view/guest-view-internal')
+const webViewConstants = require('@electron/internal/renderer/web-view/web-view-constants')
 
 // An unique ID that can represent current context.
 const contextId = v8Util.getHiddenValue(global, 'contextId')

+ 1 - 1
lib/sandboxed_renderer/api/exports/electron.js

@@ -1,4 +1,4 @@
-const moduleList = require('../module-list')
+const moduleList = require('@electron/internal/sandboxed_renderer/api/module-list')
 
 for (const {
   name,

+ 1 - 1
lib/sandboxed_renderer/api/ipc-renderer.js

@@ -1,4 +1,4 @@
-const ipcRenderer = require('../../renderer/api/ipc-renderer')
+const ipcRenderer = require('@electron/internal/renderer/api/ipc-renderer')
 
 const v8Util = process.atomBinding('v8_util')
 const ipcNative = process.atomBinding('ipc')

+ 7 - 7
lib/sandboxed_renderer/api/module-list.js

@@ -3,32 +3,32 @@ const features = process.atomBinding('features')
 module.exports = [
   {
     name: 'crashReporter',
-    load: () => require('../../common/api/crash-reporter')
+    load: () => require('@electron/internal/common/api/crash-reporter')
   },
   {
     name: 'desktopCapturer',
-    load: () => require('../../renderer/api/desktop-capturer'),
+    load: () => require('@electron/internal/renderer/api/desktop-capturer'),
     enabled: features.isDesktopCapturerEnabled()
   },
   {
     name: 'ipcRenderer',
-    load: () => require('./ipc-renderer')
+    load: () => require('@electron/internal/sandboxed_renderer/api/ipc-renderer')
   },
   {
     name: 'isPromise',
-    load: () => require('../../common/api/is-promise'),
+    load: () => require('@electron/internal/common/api/is-promise'),
     private: true
   },
   {
     name: 'nativeImage',
-    load: () => require('../../common/api/native-image')
+    load: () => require('@electron/internal/common/api/native-image')
   },
   {
     name: 'remote',
-    load: () => require('../../renderer/api/remote')
+    load: () => require('@electron/internal/renderer/api/remote')
   },
   {
     name: 'webFrame',
-    load: () => require('../../renderer/api/web-frame')
+    load: () => require('@electron/internal/renderer/api/web-frame')
   }
 ]

+ 5 - 5
lib/sandboxed_renderer/init.js

@@ -2,7 +2,7 @@
 /* global binding, Buffer */
 const events = require('events')
 
-process.atomBinding = require('../common/atom-binding-setup')(binding.get, 'renderer')
+process.atomBinding = require('@electron/internal/common/atom-binding-setup')(binding.get, 'renderer')
 
 // The electron module depends on process.atomBinding
 const electron = require('electron')
@@ -41,7 +41,7 @@ const {
   preloadSrc, preloadError, process: processProps
 } = electron.ipcRenderer.sendSync('ELECTRON_BROWSER_SANDBOX_LOAD')
 
-require('../renderer/web-frame-init')()
+require('@electron/internal/renderer/web-frame-init')()
 
 // Pass different process object to the preload script(which should not have
 // access to things like `process.atomBinding`).
@@ -76,7 +76,7 @@ function preloadRequire (module) {
 
 if (window.location.protocol === 'chrome-devtools:') {
   // Override some inspector APIs.
-  require('../renderer/inspector')
+  require('@electron/internal/renderer/inspector')
 }
 
 if (binding.guestInstanceId) {
@@ -85,8 +85,8 @@ if (binding.guestInstanceId) {
 
 if (!process.guestInstanceId && preloadProcess.argv.indexOf('--webview-tag=true') !== -1) {
   // don't allow recursive `<webview>`
-  require('../renderer/web-view/web-view')
-  require('../renderer/web-view/web-view-attributes')
+  require('@electron/internal/renderer/web-view/web-view')
+  require('@electron/internal/renderer/web-view/web-view-attributes')
 }
 
 // Wrap the script into a function executed in global scope. It won't have

+ 1 - 1
lib/worker/init.js

@@ -11,7 +11,7 @@ process.argv.splice(1, 1)
 require('../common/reset-search-paths')
 
 // Import common settings.
-require('../common/init')
+require('@electron/internal/common/init')
 
 // Expose public APIs.
 Module.globalPaths.push(path.join(__dirname, 'api', 'exports'))

+ 51 - 0
package-lock.json

@@ -230,6 +230,15 @@
       "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=",
       "dev": true
     },
+    "aliasify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/aliasify/-/aliasify-2.1.0.tgz",
+      "integrity": "sha1-fDCCW5RQueYYW6J1M+r24gZ9S0I=",
+      "dev": true,
+      "requires": {
+        "browserify-transform-tools": "~1.7.0"
+      }
+    },
     "align-text": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
@@ -1098,6 +1107,16 @@
         "parse-asn1": "^5.0.0"
       }
     },
+    "browserify-transform-tools": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/browserify-transform-tools/-/browserify-transform-tools-1.7.0.tgz",
+      "integrity": "sha1-g+J3Ih9jJZvtLn6yooOpcKUB9MQ=",
+      "dev": true,
+      "requires": {
+        "falafel": "^2.0.0",
+        "through": "^2.3.7"
+      }
+    },
     "browserify-zlib": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz",
@@ -3607,6 +3626,32 @@
       "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
       "dev": true
     },
+    "falafel": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/falafel/-/falafel-2.1.0.tgz",
+      "integrity": "sha1-lrsXdh2rqU9G0AFzizzt86Z/4Gw=",
+      "dev": true,
+      "requires": {
+        "acorn": "^5.0.0",
+        "foreach": "^2.0.5",
+        "isarray": "0.0.1",
+        "object-keys": "^1.0.6"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "5.7.3",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
+          "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==",
+          "dev": true
+        },
+        "object-keys": {
+          "version": "1.0.12",
+          "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
+          "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==",
+          "dev": true
+        }
+      }
+    },
     "fast-deep-equal": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
@@ -4115,6 +4160,12 @@
         "for-in": "^1.0.1"
       }
     },
+    "foreach": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
+      "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=",
+      "dev": true
+    },
     "forever-agent": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",

+ 7 - 1
package.json

@@ -4,6 +4,7 @@
   "repository": "https://github.com/electron/electron",
   "description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
   "devDependencies": {
+    "aliasify": "^2.1.0",
     "asar": "^0.11.0",
     "browserify": "^13.1.0",
     "check-for-leaks": "^1.0.2",
@@ -65,5 +66,10 @@
   "author": "Electron Community",
   "keywords": [
     "electron"
-  ]
+  ],
+  "aliasify": {
+    "replacements": {
+      "@electron/internal/(.+)": "./lib/$1"
+    }
+}
 }