Skip to content

fix: do not load source for electron module in ESM loader synchronous flow#46810

Merged
codebytere merged 1 commit intomainfrom
clavin/fix-packaged-esm-electron-import
Jun 3, 2025
Merged

fix: do not load source for electron module in ESM loader synchronous flow#46810
codebytere merged 1 commit intomainfrom
clavin/fix-packaged-esm-electron-import

Conversation

@clavin
Copy link
Member

@clavin clavin commented Apr 26, 2025

Description of Change

Mirrors a modification in the ESM patch that avoids loading source for the electron module to the same (synchronous) code used for CJS interoperability. This code path can only be triggered in packaged applications.


It was an interesting journey figuring out the root cause for this one.

There exists two flows in the ESM loader: asynchronous (ESM) and synchronous (CJS). The second flow enables CJS modules to load ESM modules and vice versa. When a CJS module loads an ESM module, its imports are loaded synchronously for compatibility.

We patch the ESM loader to avoid loading source code for the electron module (because there is none). The patch only applies to the asynchronous flow. The patch also needs to be applied to the synchronous flow, which only runs on the electron module in packaged applications.

In an unpackaged application, default_app is the entrypoint. At the beginning of default_app, the electron module is loaded via import as an ESM module. Loading the electron module this way uses the (patched) asynchronous flow. As a side effect, the module instance gets cached.

In a packaged application, the app's main script is the entrypoint. default_app is not present and does not run. As a consequence, the module cache is relatively fresh.

The domino effect starts when (1) a CJS module loads via require (2) an ESM module that loads via import (3) the electron module (4) for the first time.12 The ESM loader's synchronous flow does not take the usual cache hit branch and instead attempts to load the module. Since the synchronous flow is not patched to avoid loading the electron module's source code, an error ends up getting thrown.3 💥


I also took the opportunity to move some code between patches, but honestly I don't know what goes where, so lmk if there's a better home for these changes. As for testing, I couldn't find a simple way to get our test runner to skip the default_app entrypoint, so I'm pushing the fix up by itself. If you accept "it works on my machine" though, then rest assured I manually verified this fix.

Fixes #46614

Checklist

Release Notes

Notes: Fixed an error when importing electron for the first time from an ESM module loaded by a CJS module in a packaged app.

Footnotes

  1. (1) The CJS module is necessary to trigger the synchronous flow; (2) The ESM module is necessary to trigger the ESM loader; (3) The electron module is special; (4) It must be the first time or else the module will be cached.

  2. The electron/js2c/browser_init module is loaded at startup, but it is a special case that uses the built-in code cache, completely circumvents most of the module loading code, and is distinct from the electron module.

  3. Somewhat interestingly, the thrown error is not from the file system. Instead, it's from a validation check on the resolved module path: electron: is not a protocol it knows how to load source for.

@clavin clavin added semver/patch backwards-compatible bug fixes target/36-x-y PR should also be added to the "36-x-y" branch. labels Apr 26, 2025
@clavin clavin requested a review from a team as a code owner April 26, 2025 08:04
@electron-cation electron-cation bot added new-pr 🌱 PR opened recently and removed new-pr 🌱 PR opened recently labels Apr 26, 2025
@deepak1556
Copy link
Member

LGTM 👍

As for testing, I couldn't find a simple way to get our test runner to skip the default_app entrypoint, so I'm pushing the fix up by itself

Yeah first test of its kind I believe, but could we add a utility variant of startRemoteControlApp that would copy the Electron app from process.execPath and replace the resources/default_app.asar with resources/app where app folder would be a folder the spec points to under fixtures. We have one for macOS codesign fixtures today copyMacOSFixtureApp, maybe make it generic for all platforms

export async function startCustomApp (appPath: string, extraArgs: string[] = [], options?: childProcess.SpawnOptionsWithoutStdio)

@clavin clavin added the target/35-x-y PR should also be added to the "35-x-y" branch. label Apr 28, 2025
@github-actions github-actions bot added the target/37-x-y PR should also be added to the "37-x-y" branch. label Apr 29, 2025
@codebytere
Copy link
Member

@clavin are you planning to add a test to this?

@clavin
Copy link
Member Author

clavin commented May 29, 2025

Open to pushback here, but no I don't think the complexity of writing the test code is worth it to prevent this specific bug from regressing. If we really want to test this sort of behavior, we should do a more comprehensively ESM quality initiative. (If it does happen to regress, then definitely yeah add a test.)

@codebytere ready to merge if you agree :)

Copy link
Member

@deepak1556 deepak1556 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, my comment for test was more of a suggestion on how it could be achieved if we are to write one to exercise this code path, not a blocker for this PR.

@clavin can you create a tracking issue for follow-up, thanks!

@codebytere codebytere merged commit 508c601 into main Jun 3, 2025
66 checks passed
@codebytere codebytere deleted the clavin/fix-packaged-esm-electron-import branch June 3, 2025 10:50
@release-clerk
Copy link

release-clerk bot commented Jun 3, 2025

Release Notes Persisted

Fixed an error when importing electron for the first time from an ESM module loaded by a CJS module in a packaged app.

@trop
Copy link
Contributor

trop bot commented Jun 3, 2025

I have automatically backported this PR to "37-x-y", please check out #47342

@trop trop bot added in-flight/37-x-y and removed target/37-x-y PR should also be added to the "37-x-y" branch. labels Jun 3, 2025
@trop
Copy link
Contributor

trop bot commented Jun 3, 2025

I have automatically backported this PR to "36-x-y", please check out #47343

@trop
Copy link
Contributor

trop bot commented Jun 3, 2025

I have automatically backported this PR to "35-x-y", please check out #47344

@trop trop bot added in-flight/36-x-y in-flight/35-x-y and removed target/36-x-y PR should also be added to the "36-x-y" branch. target/35-x-y PR should also be added to the "35-x-y" branch. labels Jun 3, 2025
@jkleinsc jkleinsc mentioned this pull request Jun 3, 2025
3 tasks
@trop trop bot added merged/37-x-y PR was merged to the "37-x-y" branch. merged/35-x-y PR was merged to the "35-x-y" branch. merged/36-x-y PR was merged to the "36-x-y" branch. and removed in-flight/37-x-y in-flight/35-x-y in-flight/36-x-y labels Jun 5, 2025
kigh-ota pushed a commit to kigh-ota/electron that referenced this pull request Sep 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merged/35-x-y PR was merged to the "35-x-y" branch. merged/36-x-y PR was merged to the "36-x-y" branch. merged/37-x-y PR was merged to the "37-x-y" branch. semver/patch backwards-compatible bug fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ESM Modules loading in main crash with unsupported url scheme

3 participants