Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Node's ES Modules #21457

Closed
3 tasks done
Tracked by #19 ...
Jamesernator opened this issue Dec 10, 2019 · 282 comments
Closed
3 tasks done
Tracked by #19 ...

Support Node's ES Modules #21457

Jamesernator opened this issue Dec 10, 2019 · 282 comments

Comments

@Jamesernator
Copy link

Jamesernator commented Dec 10, 2019

Important

Support for ES Modules in Electron will land in Electron 28 and will be available in the electron-nightly released on August 31st

馃コ

Original Issue Below

Preflight Checklist

  • I have read the Contributing Guidelines for this project.
  • I agree to follow the Code of Conduct that this project adheres to.
  • I have searched the issue tracker for a feature request that matches the one I want to file, without success.

Feature Request

As of Node 13.3 ES modules have been unflagged (albeit still experimental), it would be nice to be able to use ES modules as supported in electron.

Currently when trying to run electron with an ES entry point (either .mjs or .js+"type": "module") electron fails to run.

For example:

> electron-test@1.0.0 start /home/jamesernator/Projects/electron-test
> electron .

App threw an error during load
/home/jamesernator/Projects/electron-test/main.js:1
import electron from 'electron';
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at Module._compile (internal/modules/cjs/loader.js:815:22)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:892:10)
    at Module.load (internal/modules/cjs/loader.js:735:32)
    at Module._load (internal/modules/cjs/loader.js:648:12)
    at Module._load (electron/js2c/asar.js:717:26)
    at Function.Module._load (electron/js2c/asar.js:717:26)
    at loadApplicationPackage (/home/jamesernator/Projects/electron-test/node_modules/electron/dist/resources/default_app.asar/main.js:109:16)
    at Object.<anonymous> (/home/jamesernator/Projects/electron-test/node_modules/electron/dist/resources/default_app.asar/main.js:155:9)
    at Module._compile (internal/modules/cjs/loader.js:880:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:892:10)

Alternatives Considered

This is primarily a request for support, the alternative is not supporting it but that would be unfortunate given ES modules will be officially supported in both browsers and Node but not usable by electron without additional tooling.

Additional Information

ES Modules are still new (and experimental) so actual implementation might be delayed (or put behind flag) however starting to consider implementation considerations now would be helpful for supporting this more readily in future.

@nornagon
Copy link
Member

FWIW, Electron won't be supporting Node 13 as our policy is to only roll in LTS versions of Node. So, while we definitely want to support ESM, this isn't going to be happening until Node 14.

@pauliusuza
Copy link

Sure, following LTS makes sense for production releases, but there's no harm in creating a v10 branch and adding ESM support now with Node v13 which can be rolled over to v14 once it's out in April 2020.

@nornagon
Copy link
Member

@pauliusuza "no harm" indeed, except that we would then have to make sure we backport every change from master into the v10 branch, which is a significant burden on the maintainer team.

We'll do like we've done for all previous release branches, and branch v10 once v9 is stable. See https://electronjs.org/docs/tutorial/electron-timelines.

@trusktr
Copy link

trusktr commented Dec 19, 2019

As a workaround, you can use the esm package for the time being. Just install it, import it, then any file you import after that can be an ES Module.

Then, once Node 14 is out and Electron upgrades, all you need to do is modify the entry point not to import esm, and to import your ES Module directly. All the rest of the code base will remain untouched.

@DalderupMaurice
Copy link

That got the job done, Thanks! @trusktr
I have a TS monorepo project for desktop and web and every possible combination of tsconfig resulted in one of both being broken. Adding ESM fixed the issue 馃憤

@awkj
Copy link

awkj commented Feb 2, 2020

wish electron use deno replace node

@ghost
Copy link

ghost commented Mar 4, 2020

@nornagon ES modules unflagged is expected to land in April for Node 12. For more details, see nodejs/modules#450.

@ghost
Copy link

ghost commented Apr 11, 2020

As a workaround, you can use the esm package for the time being. Just install it, import it, then any file you import after that can be an ES Module.

Then, once Node 14 is out and Electron upgrades, all you need to do is modify the entry point not to import esm, and to import your ES Module directly. All the rest of the code base will remain untouched.

I don't quite undestand how you did this. Can you give an example of how this workaround is supposed to work?

Edit: nevermind, I got it working. My code looks like this now (if anyone's wondering):

./main.js:

require = require("esm")(module)
module.exports = require("./js/electronMain.js")

with my Electron (main process) startup code in ./js/electronMain.js

@kskalski
Copy link

kskalski commented Jun 1, 2020

Node 12.17 removed necessity to enable -experimental-modules flag, so I wonder if after incorporating #23789 it will just start working out of the box. And which electron release will get it... :)

@dy
Copy link

dy commented Jun 19, 2020

It is node@14.4 already, with stabilized support for ESM. It is time for the ecosystem to gradually switch to common standard. Would be really nice if electron enabled modules. esm doesn't work with node@13+.

@nornagon
Copy link
Member

As mentioned earlier in the thread, we will support this when Electron includes a version of node which supports it, and not before.

Electron only includes LTS releases of Node, and does not upgrade Node versions in stable releases. Node 14 won't be LTS until October, so the earliest version of Electron that could include Node 14 LTS is Electron 12.

@ghost
Copy link

ghost commented Jun 22, 2020

@nornagon Node 12 LTS already supports it.

See the Node release notes:
https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V12.md#ecmascript-modules-----experimental-modules-flag-removal

@nornagon
Copy link
Member

Ah, in that case Electron 11 will likely include support for it.

@ghost
Copy link

ghost commented Jun 22, 2020

@nornagon I would expect some stuff to break without deeper testing of native ESM with Electron's API. Will the entire Electron API be supported or parts of it in ESM in Electron 11?

@MarshallOfSound
Copy link
Member

Full transparency we might turn off node's ESM loader in Electron 11 and it will almost certainly be disabled completely in renderer processes. Still reviewing exactly what the implications of this loader, it's implementation and it's interactions with Chromium exactly are and when we know more we'll share it but I want to set the expectations quite clearly here. This is not a simple flag or switch we can just turn on and move on with our lives, there are significant security, performance and compatibility concerns that we need to address here.

@ghost
Copy link

ghost commented Jun 22, 2020

@MarshallOfSound @nornagon While I understand the challenge of integrating Node and Chromium on a periodic basis, integrating ESM into Electron will be a challenge in itself.

I believe the spirit of this current issue is to sort out these security, performance and compatibility issues before ESM lands. Maybe it is impossible to do beforehand.

Any ideas on how to proceed? How should this be tackled?

@pauliusuza
Copy link

pauliusuza commented Jun 22, 2020

and it will almost certainly be disabled completely in renderer processes

Frankly, that's exactly the opposite of what I expected to hear.

@cata-code
Copy link

@pauliusuza Weback already converts all your imports into one file, so you can write things like import { ipcRenderer } from 'electron' in your client site code and it works.

@cata-code
Copy link

Latest version of Electron 9.0.5 uses Node.js 12.14.1, so according to the docs https://nodejs.org/dist/v12.14.1/docs/api/esm.html the only way to use ESM modules is to pass the --experimental-modules flag. But electron won't allow this flag, so we are stuck.

According to https://nodejs.org/en/about/releases/ Node v14 will be LTS at the end of October
Also, according to Electron's timeline https://www.electronjs.org/docs/tutorial/electron-timelines , releasing a version every 3 months, Node v14 will be part of Electron 11 released at the end of November.

If we get ESM module in Electron before Christmas it would be a great deal, but we shouldn't hold our breaths until then.

@ghost
Copy link

ghost commented Jun 25, 2020

@cata-code --experimental-modules has been removed in 12.17.0 (LTS). Even if this is true, Electron might disable this feature because of security, performance and compatibility issues.

If there is such a precedent, I'm not convinced native ESM would land with 14.0.0 in Electron. There is much work to verify everything beforehand and no plan that I'm aware of.

Please take note of the comment history in this issue.

@cata-code
Copy link

Ok, so instead of waiting for this feature, we can use imports in electron main files with the help of webpack.

This is how I set it up for my app, using webpack for both client side and electron main files

webpack.config.cjs

const path = require('path')
const CircularDependencyPlugin = require('circular-dependency-plugin')
const webpack = require('webpack')
const nodeExternals = require('webpack-node-externals')

module.exports = [
  {
    target: 'web',
    entry: './src/js/index.js',
    output: {
      filename: 'script.js',
      path: path.resolve(__dirname, 'app/js')
    },
    plugins: [
      new CircularDependencyPlugin(),
      new webpack.ExternalsPlugin('commonjs', [
        'electron'
      ])
    ]
  },
  {
    target: 'electron-main',
    entry: './src/electron/index.js',
    output: {
      filename: 'index.js',
      path: path.resolve(__dirname, 'app')
    },
    plugins: [
      new CircularDependencyPlugin()
    ],
    externals: [nodeExternals()]
  }
]

index.js

import { app, BrowserWindow, shell } from 'electron'
import contextMenu from 'electron-context-menu'

async function createWindow () {
 ...
}

Foo.js

import { ipcMain } from 'electron'

export default {
  foo () {
    ipcMain.handle('foo') ...
  }
}

@kskalski
Copy link

I was using solution mentioned by @leontepe for quite some time and it allows me to use esm modules and imports without problems by using "esm" node module and this little wrapper (main_esm.js) for entrypoint (main.js):

var _require = require("esm")(module);
_require('./main.js');

ironically at runtime Electron complains that using require in esm modules, which apparently it thinks this file is (sic!), isn't supported:

(node:19076) Warning: require() of ES modules is not supported.
require() of main_esm.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename main_esm.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from package.json.

Hope that whatever version of Electron that will be branched from current master (which already includes #23789 forcing support of esm in node AFAICT) will allow me to remove above little hack.

@cata-code
Copy link

cata-code commented Jun 29, 2020

@kskalski I had a look at the source code for the esm module and when I saw this https://github.com/standard-things/esm/tree/master/src I realized how heavy the hack is. Also this is run every time you start the app.

I rather use webpack and compile it once, and then the app is optimized every time you run it.

Besides you are most likely using webpack for the client side code. So when webpack is run, it builds both main and render/client files.

@frank-dspeed
Copy link

frank-dspeed commented Aug 13, 2020

@kskalski that is total wrong no one should need to use webpack so the solution is to use npm module ESM and wait till they fixed #24971

@kirkouimet
Copy link
Contributor

When Electron upgrades to using Node 14, would I be able to do this:

my-app/package.json
{ "type": "module" }

my-app/index.js
const path = await import('path');

And then run:
electron index.js?

Would I also be able to use Node's --experimental-loader option? Such as:
electron index.js --js-flags=--experimental-loader loader.js

Would love this. How can I help?

Offroaders123 added a commit to Offroaders123/Electron-Text-Editor that referenced this issue Sep 24, 2023
Wow, can't believe this day has finally come! You can essentially write most of your Electron code in pure ESM now! The only holdup is that preload scripts must be CommonJS still. But you can just add the `.cjs/.cts` extension for that one module, so that works out really well.

This was what made me come back to work on this project a little bit again. Rather than starting fresh with a new Electron project, I thought it would be a better idea to update this one to use my more recent tooling (TS, ESM in this case), and I want to try some Electron-specific things out too, like Accelerators, things like that. Ooh, I want to get Vite/live reload working with this too, I didn't end up getting that set up when I started this, Node projects were still above my head at that time.

electron/electron#21457
electron/electron#37535
https://github.com/electron/electron/blob/main/docs/tutorial/esm.md

This was a placeholder I set up for the reference to resolve the preload script, since `__dirname` isn't an available global from within ESM.
https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/

Sevendust Next is definitely one of my favorite albums of theirs for me, looking back on it more recently the last year or so.
LarrMarburger added a commit to LarrMarburger/privacy.sexy that referenced this issue Nov 16, 2023
- Switch from deprecated Vue CLI plugin to `electron-vite` (see
  nklayman/vue-cli-plugin-electron-builder#1982)
- Update main/preload scripts to use `index.cjs` filenames to support
  `"type": "module"`, resolving crash issue (#233). This crash was
  related to Electron not supporting ESM (see electron/asar#249,
  electron/electron#21457).
- This commit completes migration to Vite from Vue CLI (#230).

Structure changes:

- Introduce separate folders for Electron's main and preload processes.
- Move TypeHelpers to `src/` to mark tit as accessible by the rest of
  the code.

Config changes:

- Make `vite.config.ts` reusable by Electron configuration.
- On electron-builder, use `--publish` flag instead of `-p` for clarity.

Tests:

- Add log for preload script loading verification.
- Implement runtime environment sanity checks.
- Enhance logging in `check-desktop-runtime-errors`.
@slhck
Copy link

slhck commented Jan 19, 2024

There's a rather popular project that uses TypeScript and WebPack to build Electron apps with, which uses ts-node to load the TypeScript sources at runtime. It breaks with Electron v28 due to the way ESM modules are handled: electron-react-boilerplate/electron-react-boilerplate#3568

Would anyone be able to provide support or guide towards a fix? The specific issue seems to be only with Node v18.19.0 or above, and Electron v28. ts-node has some issues with ESM (see TypeStrong/ts-node#1997), but using --import tsx also does not seem to work out of the box.

@GabenGar
Copy link

Well it doesn't "load" typescript sources at runtime, it resolves them in typescript, stores them somewhere and then redirects require() calls to them, most likely achieved by monkey-patching require(). Which is of course not possible in ESM since import is a keyword and not a symbol.
The solution is to explicitly have a ts -> js transformation step in your build pipeline and drop ts-node from runtime dependencies.

undergroundwires added a commit to undergroundwires/privacy.sexy that referenced this issue Mar 18, 2024
This commit bumps Electron and related dependencies to their latest
versions to leverage native ESM support. It adjusts build configuration
to use native ESM support instead of relying on CommonJS bundling.

Key changes:

- Bump Electron to latest v29.
  Electron v28 ships with native ESM/ECMAScript modules support.
  Details on Electron ESM support:
    - electron/electron#21457
    - electron/electron#37535
- Bump `electron-builder` to latest v24.13.
  `electron-builder` is used to package and publish the application.
  It supports ESM since 24.10.
  Details on `electron-builder` ESM support:
    - electron-userland/electron-builder#7936
    - electron-userland/electron-builder#7935
- Bump `electron-log` to latest v5.1.
  `electron-log` supports ESM since version 5.0.4.
  Details on `electron-log` ESM support:
    - megahertz/electron-log#390.
- Change `electron-vite` configuration to bundle as ESM instead of
  CommonJS to leverage Electron's native ESM support.

Other supporting changes:

- Add type hint for electron-builder configuration file.
- Update import statements for `electron-updater` as it still is a
  CommonJS module and does not support ESM.
  Details:
    - electron-userland/electron-builder#7976
- Improve `electron-builder` configuration file to dynamically locate
  main entry files, supporting various JavaScript file extensions
  (`.js`, `.mjs` and `.cjs`) to facilitate easier future changes.
- Change comment about Electron process-specific module alias
  registration. This issue has been fixed in `electron-vite`, but
  subpath module imports for Electron still do not work when building
  tests (`npm run test:unit`).
  Details:
   - alex8088/electron-vite#372
- Add `electron-log` in bundling process instead of externalizing to
  workaround Electron ESM loader issues with subpath imports (inability
  to do `electron-log/main`).
  Details:
    - alex8088/electron-vite#401
    - electron/electron#41241
- Improve desktop runtime error checks' assertion message for better
  clarity.
@KaKi87
Copy link

KaKi87 commented May 31, 2024

So, I'm trying Electron 30 with the following :

import { app, BrowserWindow } from 'electron';

await app.whenReady();

const mainWindow = new BrowserWindow({
    width: 800,
    height: 600
});

await mainWindow.loadURL('https://example.com');

The commands runs indefinitely but nothing happens 馃

What am I missing ?

Thanks

@hyrious
Copy link
Contributor

hyrious commented May 31, 2024

@KaKi87 You can not await whenReady() from top level, see #40719. You must use whenReady().then(...). :/

@KaKi87
Copy link

KaKi87 commented May 31, 2024

Indeed, thanks !

It would be nice to update the docs, especially the quick start, to include ESM.

@KaKi87
Copy link

KaKi87 commented May 31, 2024

So, preload doesn't support ESM even with the mjs extension as long as sandboxing is enabled ?

@hyrious
Copy link
Contributor

hyrious commented May 31, 2024

@KaKi87 Yes, if you print a log from the sandboxed preload script, you will find that the script is wrapped in an IIFE like this:

(function(require, process, Buffer, global, setImmediate, clearImmediate, exports, module) {
  // preload.js contents
})

So it seems the preload script is always executed in IIFE format with sandboxing enabled.

@KaKi87
Copy link

KaKi87 commented May 31, 2024

Is static import support planned ?

Meanwhile, could the dynamic import function be added to these ?

@StreetStrider
Copy link

@KaKi87 from my experience, it is better to just bundle everything (with the exception of electron/ deps) into preload.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.