Browse Source

fix: expose the built-in electron module via the ESM loader (#35957)

Co-authored-by: Samuel Attard <[email protected]>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Samuel Attard <[email protected]>
trop[bot] 2 years ago
parent
commit
c87b479a05

+ 1 - 0
patches/node/.patches

@@ -51,3 +51,4 @@ fixup_for_error_declaration_shadows_a_local_variable.patch
 fixup_for_wc_98-compat-extra-semi.patch
 drop_deserializerequest_move_constructor_for_c_20_compat.patch
 fix_parallel_test-v8-stats.patch
+fix_expose_the_built-in_electron_module_via_the_esm_loader.patch

+ 87 - 0
patches/node/fix_expose_the_built-in_electron_module_via_the_esm_loader.patch

@@ -0,0 +1,87 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Samuel Attard <[email protected]>
+Date: Thu, 6 Oct 2022 04:09:16 -0700
+Subject: fix: expose the built-in electron module via the ESM loader
+
+This allows usage of `import { app } from 'electron'` and `import('electron')` natively in the browser + non-sandboxed renderer
+
+diff --git a/lib/internal/modules/esm/get_format.js b/lib/internal/modules/esm/get_format.js
+index 5ae0e17dcfb5e24a1a117c33c4d42891686e693f..619fe6cef3b02eb575410225f41d3e7d51f37b93 100644
+--- a/lib/internal/modules/esm/get_format.js
++++ b/lib/internal/modules/esm/get_format.js
+@@ -31,6 +31,7 @@ const protocolHandlers = ObjectAssign(ObjectCreate(null), {
+   'http:': getHttpProtocolModuleFormat,
+   'https:': getHttpProtocolModuleFormat,
+   'node:'() { return 'builtin'; },
++  'electron:'() { return 'commonjs'; },
+ });
+ 
+ function getDataProtocolModuleFormat(parsed) {
+diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js
+index fc5dcd6863dc358102a74bd2cc723d54436fae64..97eea17d815967671a2a0fc2a9c95a9bb85947ac 100644
+--- a/lib/internal/modules/esm/resolve.js
++++ b/lib/internal/modules/esm/resolve.js
+@@ -883,6 +883,8 @@ function parsePackageName(specifier, base) {
+   return { packageName, packageSubpath, isScoped };
+ }
+ 
++const electronSpecifiers = new SafeSet(['electron', 'electron/main', 'electron/common', 'electron/renderer']);
++
+ /**
+  * @param {string} specifier
+  * @param {string | URL | undefined} base
+@@ -895,6 +897,10 @@ function packageResolve(specifier, base, conditions) {
+     return new URL('node:' + specifier);
+   }
+ 
++  if (electronSpecifiers.has(specifier)) {
++    return new URL('electron:electron');
++  }
++
+   const { packageName, packageSubpath, isScoped } =
+     parsePackageName(specifier, base);
+ 
+@@ -1095,7 +1101,7 @@ function checkIfDisallowedImport(specifier, parsed, parsedParentURL) {
+ 
+ function throwIfUnsupportedURLProtocol(url) {
+   if (url.protocol !== 'file:' && url.protocol !== 'data:' &&
+-      url.protocol !== 'node:') {
++      url.protocol !== 'node:' && url.protocol !== 'electron:') {
+     throw new ERR_UNSUPPORTED_ESM_URL_SCHEME(url);
+   }
+ }
+diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js
+index 8fb3c96f8dc4c535c3898755f7846ae24d9867c4..bda3ca0797e8f1b1d69295c2276c0f841cae99f2 100644
+--- a/lib/internal/modules/esm/translators.js
++++ b/lib/internal/modules/esm/translators.js
+@@ -155,7 +155,7 @@ translators.set('commonjs', async function commonjsStrategy(url, source,
+ 
+   if (!cjsParse) await initCJSParse();
+   const { module, exportNames } = cjsPreparseModuleExports(filename);
+-  const namesWithDefault = exportNames.has('default') ?
++  const namesWithDefault = filename === 'electron' ? ['default', ...Object.keys(module.exports)] : exportNames.has('default') ?
+     [...exportNames] : ['default', ...exportNames];
+ 
+   return new ModuleWrap(url, undefined, namesWithDefault, function() {
+@@ -174,7 +174,7 @@ translators.set('commonjs', async function commonjsStrategy(url, source,
+       }
+     }
+ 
+-    for (const exportName of exportNames) {
++    for (const exportName of namesWithDefault) {
+       if (!ObjectPrototypeHasOwnProperty(exports, exportName) ||
+           exportName === 'default')
+         continue;
+diff --git a/lib/internal/url.js b/lib/internal/url.js
+index 22bff28595f6f5b109ae47c79aa1f5ac463ec6c3..d4eb2e044cc1152f48c92d0503f9216e3aad5f4b 100644
+--- a/lib/internal/url.js
++++ b/lib/internal/url.js
+@@ -1432,6 +1432,8 @@ function fileURLToPath(path) {
+     path = new URL(path);
+   else if (!isURLInstance(path))
+     throw new ERR_INVALID_ARG_TYPE('path', ['string', 'URL'], path);
++  if (path.protocol === 'electron:')
++    return 'electron';
+   if (path.protocol !== 'file:')
+     throw new ERR_INVALID_URL_SCHEME('file');
+   return isWindows ? getPathFromURLWin32(path) : getPathFromURLPosix(path);

+ 12 - 0
spec/modules-spec.ts

@@ -174,4 +174,16 @@ describe('modules support', () => {
       });
     });
   });
+
+  describe('esm', () => {
+    it('can load the built-in "electron" module via ESM import', async () => {
+      await expect(import('electron')).to.eventually.be.ok();
+    });
+
+    it('the built-in "electron" module loaded via ESM import has the same exports as the CJS module', async () => {
+      const esmElectron = await import('electron');
+      const cjsElectron = require('electron');
+      expect(Object.keys(esmElectron)).to.deep.equal(Object.keys(cjsElectron));
+    });
+  });
 });