electron / electron/forge

Relax the Webpack plugin's requirement that the `package.json`'s `main` be exactly `".webpack/main"`, to support packages with `"type": "module"`

Open
#3,230 0 comments 7 reactions 0 assignees View on GitHub
esm plugin/webpack
Dominant language
TypeScript
Stars
7.1k
Forks
641
Avg merge
3d 1h
Merged PRs (30d)
30

Description

### Pre-flight checklist

- [X] I have read the [contribution documentation](https://github.com/electron/forge/blob/main/CONTRIBUTING.md) for this project.
- [X] I agree to follow the [code of conduct](https://github.com/electron/electron/blob/main/CODE_OF_CONDUCT.md) that this project uses.
- [X] I have searched the issue tracker for a feature request that matches the one I want to file, without success.

### Problem description

The plugin will throw an error if the `package.json`'s `"main"` field is not **exactly** `".webpack/main"`. (Actually, it only checks if it [_ends_ with it](https://github.com/electron/forge/blob/455e476eef5c16fcdda983705900afcd60d1d4f6/packages/plugin/webpack/src/WebpackPlugin.ts#L225))

This is because the Webpack plugin outputs the main Electron bundle to `.webpack/main/index.js`. This causes some problems if you have a `package.json` of `"type": "module"`:

1. Electron is started with the package to run provided _by path_.
2. Electron imports the given package path using `require()`.
3. Because `require()` is given a path instead of a name, Node consults the `package.json`'s `"main"` property for a script to import.
- When `require()` is given a path, it will _only_ consult the `"main"` field even if `"exports"` is defined. See [the Node docs](https://nodejs.org/api/modules.html#folders-as-modules)
4. Node finds the `"main"` property is set to `".webpack/main"`. Because this is a directory, Node will load `.webpack/main/index.js`.
5. Because the `"type"` is `"module"`, all `.js` files are assumed to be ES modules, and Node throws an error about attempting to `require()` it (even though the plugin outputs a CJS bundle).

### Proposed solution

In a `"type": "module"` package, all `.js` files are treated as ES modules, and any CommonJS modules must have a `.cjs` extension. (The `"exports"` field lets you explicitly specify these, but this does not get used due to passing a path to `require()`).

The simplest solution is to ensure the output bundle has a `.cjs` extension by editing the Webpack config, and explicitly specify `"main": ".webpack/main/index.cjs"`. This fixes the problem, but the plugin throws an error because the entry point isn't the expected value. The plugin would need to be updated to accept `/\.webpack\/main(\/index\..js)?$/` as the entry point.

Perhaps a more proper solution would be to allow the user to configure the bundle output/entry point instead of requiring it to be `.webpack/main`. #2770 suggested this, but didn't have a specific use case.

### Alternatives considered

- It's possible to set up a hook to copy a different `package.json` or replace the `"main"` in the output files, but this doesn't seem like a great workaround.
- It's possible to split the package up into multiple packages, with one just for building the Electron package, but this adds development/configuration overhead, especially if you want to share a common Webpack config for multiple targets/environments.

### Additional information

I'm aware Electron doesn't support ES modules, but it doesn't have to. Webpack already turns everything into cjs, we just need to make sure Node knows that.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.