From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samuel Attard <sattard@salesforce.com>
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 4ac9c011d153f4cb39cb7d4f46de0f8e65f70a56..f8462b69368aa7987e1e8ac8cd73e4ff2d921c51 100644
--- a/lib/internal/modules/esm/get_format.js
+++ b/lib/internal/modules/esm/get_format.js
@@ -27,6 +27,7 @@ const protocolHandlers = {
   'http:': getHttpProtocolModuleFormat,
   'https:': getHttpProtocolModuleFormat,
   'node:'() { return 'builtin'; },
+  'electron:'() { return 'electron'; },
 };
 
 /**
diff --git a/lib/internal/modules/esm/load.js b/lib/internal/modules/esm/load.js
index d064296d11c463616111d28a32b7ad3f6a72bebd..14d5236d0ead946acf9a2f63bf979e7193bfbf56 100644
--- a/lib/internal/modules/esm/load.js
+++ b/lib/internal/modules/esm/load.js
@@ -123,7 +123,8 @@ async function defaultLoad(url, context = kEmptyObject) {
 
   if (
     format === 'builtin' ||
-    format === 'commonjs'
+    format === 'commonjs' ||
+    format === 'electron'
   ) {
     source = null;
   } else if (source == null) {
@@ -201,6 +202,7 @@ function throwIfUnsupportedURLScheme(parsed, experimentalNetworkImports) {
     protocol !== 'file:' &&
     protocol !== 'data:' &&
     protocol !== 'node:' &&
+    protocol !== 'electron:' &&
     (
       !experimentalNetworkImports ||
       (
@@ -209,7 +211,7 @@ function throwIfUnsupportedURLScheme(parsed, experimentalNetworkImports) {
       )
     )
   ) {
-    const schemes = ['file', 'data', 'node'];
+    const schemes = ['file', 'data', 'node', 'electron'];
     if (experimentalNetworkImports) {
       ArrayPrototypePush(schemes, 'https', 'http');
     }
diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js
index acb5ddca8af3d2495ce27d6426b28e6ff73a8f33..acfcc1f1f92548117ded857fa8d4dd140e07ab2b 100644
--- a/lib/internal/modules/esm/resolve.js
+++ b/lib/internal/modules/esm/resolve.js
@@ -725,6 +725,8 @@ function parsePackageName(specifier, base) {
   return { packageName, packageSubpath, isScoped };
 }
 
+const electronTypes = ['electron', 'electron/main', 'electron/common', 'electron/renderer'];
+
 /**
  * @param {string} specifier
  * @param {string | URL | undefined} base
@@ -736,6 +738,11 @@ function packageResolve(specifier, base, conditions) {
     return new URL('node:' + specifier);
   }
 
+  const electronSpecifiers = new SafeSet(electronTypes);
+  if (electronSpecifiers.has(specifier)) {
+    return new URL('electron:electron');
+  }
+
   const { packageName, packageSubpath, isScoped } =
     parsePackageName(specifier, base);
 
diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js
index b143cd0ad34d0e039db3e7493be00d923cc68b2d..178f362b2869173d51b2e97a8d35a16ab4ed4962 100644
--- a/lib/internal/modules/esm/translators.js
+++ b/lib/internal/modules/esm/translators.js
@@ -220,7 +220,7 @@ function createCJSModuleWrap(url, source, isMain, loadCJS = loadCJSModule) {
 
   const { exportNames, module } = cjsPreparseModuleExports(filename, source);
   cjsCache.set(url, module);
-  const namesWithDefault = exportNames.has('default') ?
+  const namesWithDefault = filename === 'electron' ? ['default', ...Object.keys(module.exports)] : exportNames.has('default') ?
     [...exportNames] : ['default', ...exportNames];
 
   if (isMain) {
@@ -241,9 +241,9 @@ function createCJSModuleWrap(url, source, isMain, loadCJS = loadCJSModule) {
     } else {
       ({ exports } = module);
     }
-    for (const exportName of exportNames) {
-      if (!ObjectPrototypeHasOwnProperty(exports, exportName) ||
-          exportName === 'default')
+    for (const exportName of namesWithDefault) {
+      if (exportName === 'default' ||
+          !ObjectPrototypeHasOwnProperty(exports, exportName))
         continue;
       // We might trigger a getter -> dont fail.
       let value;
@@ -267,6 +267,10 @@ translators.set('require-commonjs', (url, source, isMain) => {
   return createCJSModuleWrap(url, source);
 });
 
+translators.set('electron', () => {
+  return createCJSModuleWrap('electron', '');
+});
+
 // Handle CommonJS modules referenced by `import` statements or expressions,
 // or as the initial entry point when the ESM loader handles a CommonJS entry.
 translators.set('commonjs', async function commonjsStrategy(url, source,
diff --git a/lib/internal/url.js b/lib/internal/url.js
index 8d5926e8fcb9df031e37698588075a34a80aea6c..6df0b68c8d35445d5f02475678484a6d5157762e 100644
--- a/lib/internal/url.js
+++ b/lib/internal/url.js
@@ -1389,6 +1389,8 @@ function fileURLToPath(path) {
     path = new URL(path);
   else if (!isURL(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);
