* fix: allow ESM loads from within ASAR files * fix: ensure that ESM entry points finish loading before app ready * fix: allow loading ESM entrypoints via default_app * fix: allow ESM loading for renderer preloads * docs: document current known limitations of esm * chore: add patches to support blending esm handlers * refactor: use SetDefersLoading instead of JoinAppCode in renderers Blink has it's own event loop so pumping the uv loop in the renderer is not enough, luckily in blink we can suspend the loading of the frame while we do additional work. * chore: add patch to expose SetDefersLoading * fix: use fileURLToPath instead of pathname * chore: update per PR feedback * fix: fs.exists/existsSync should never throw * fix: convert path to file url before importing * fix: oops * fix: oops * Update docs/tutorial/esm-limitations.md Co-authored-by: Jeremy Rose <jeremya@chromium.org> * windows... * windows... * chore: update patches * spec: fix tests and document empty body edge case * Apply suggestions from code review Co-authored-by: Daniel Scalzi <d_scalzi@yahoo.com> Co-authored-by: Jeremy Rose <jeremya@chromium.org> * spec: add tests for esm * spec: windows * chore: update per PR feedback * chore: update patches * Update shell/common/node_bindings.h Co-authored-by: Jeremy Rose <jeremya@chromium.org> * chore: update patches * rebase * use cjs loader by default for preload scripts * chore: fix lint * chore: update patches * chore: update patches * chore: fix patches * build: debug depshash * ? * Revert "build: debug depshash" This reverts commit 0de82523fb93f475226356b37418ce4b69acdcdf. * chore: allow electron as builtin protocol in esm loader * Revert "Revert "build: debug depshash"" This reverts commit ff86b1243ca6d05c9b3b38e0a6d717fb380343a4. * chore: fix esm doc * chore: update node patches --------- Co-authored-by: Jeremy Rose <jeremya@chromium.org> Co-authored-by: electron-patch-conflict-fixer[bot] <83340002+electron-patch-conflict-fixer[bot]@users.noreply.github.com> Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com> Co-authored-by: Daniel Scalzi <d_scalzi@yahoo.com>
4.2 KiB
ESM Limitations
This document serves to outline the limitations / differences between ESM in Electron and ESM in Node.js and Chromium.
ESM Support Matrix
This table gives a general overview of where ESM is supported and most importantly which ESM loader is used.
Supported | Loader | Supported in Preload | Loader in Preload | Applicable Requirements | |
---|---|---|---|---|---|
Main Process | Yes | Node.js | N/A | N/A | |
Sandboxed Renderer | Yes | Chromium | No | ||
Node.js Renderer + Context Isolation | Yes | Chromium | Yes | Node.js | |
Node.js Renderer + No Context Isolation | Yes | Chromium | Yes | Node.js |
Requirements
You must use await
generously in the main process to avoid race conditions
Certain APIs in Electron (app.setPath
for instance) are documented as needing to be called before the app.on('ready')
event is emitted. When using ESM in the main process it is only guaranteed that the ready
event hasn't been emitted while executing the side-effects of the primary import. i.e. if index.mjs
calls import('./set-up-paths.mjs')
at the top level the app will likely already be "ready" by the time that dynamic import resolves. To avoid this you should await import('./set-up-paths.mjs')
at the top level of index.mjs
. It's not just import calls you should await, if you are reading files asynchronously or performing other asynchronous actions you must await those at the top-level as well to ensure the app does not resume initialization and become ready too early.
Sandboxed preload scripts can't use ESM imports
Sandboxed preload scripts are run as plain javascript without an ESM context. It is recommended that preload scripts are bundled via something like webpack
or vite
for performance reasons regardless, so your preload script should just be a single file that doesn't need to use ESM imports. Loading the electron
API is still done via require('electron')
.
Node.js ESM Preload Scripts will run after page load on pages with no content
If the response body for the page is completely empty, i.e. Content-Length: 0
, the preload script will not block the page load, which may result in race conditions. If this impacts you, change your response body to have something in it, for example an empty html
tag (<html></html>
) or swap back to using a CommonJS preload script (.js
or .cjs
) which will block the page load.
ESM Preload Scripts must have the .mjs
extension
In order to load an ESM preload script it must have a .mjs
file extension. Using type: module
in a nearby package.json is not sufficient. Please also note the limitation above around not blocking page load if the page is empty.
Non-context-isolated renderers can't use dynamic Node.js ESM imports
If your renderer process does not have contextIsolation
enabled you can not import()
ESM files via the Node.js module loader. This means that you can't import('fs')
or import('./foo')
. If you want to be able to do so you must enable context isolation. This is because in the renderer Chromium's import()
function takes precedence and without context isolation there is no way for Electron to know which loader to route the request to.
If you enable context isolation import()
from the isolated preload context will use the Node.js loader and import()
from the main context will continue using Chromium's loader.