Add API to WebpackPlugin for compiling utilityProcess code
- 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
Currently, the `WebpackPlugin` only takes Webpack configs for the main and renderer process. If I want to create a `utilityProcess` in Electron, there's no way to compile a separate bundle for that code.
### Proposed solution
I propose adding an option to the `WebpackPlugin` constructor to pass a Webpack config for `utilityProcess` code, so that it is compiled alongside the main and renderer process code. Similar to the renderer process, the `utilityProcess` bundle would also have a global variable exposed in the main process indicating from where to load the compiled module.
### Alternatives considered
For now, I have written a custom Forge plugin to mimic this behavior, but I do not think this is a stable solution. To do this, I have created a plugin called `UtilityProcessPlugin`:
```typescript
import { webpack, Configuration as RawWebpackConfiguration } from "webpack";
import { PluginBase } from "@electron-forge/plugin-base";
import { ForgeHookMap } from "@electron-forge/shared-types";
export default class UtilityProcessPlugin extends PluginBase {
name = "utility-process";
getHooks(): ForgeHookMap {
return {
generateAssets: this.generateAssets.bind(this),
};
}
private async generateAssets(): Promise {
return new Promise((resolve, reject) => {
webpack(this.config, (err, stats) => {
if (err) {
reject(err);
return;
}
if (stats?.hasErrors()) {
const json = stats.toJson();
for (const error of json.errors ?? []) {
reject(new Error(`${error.message}\n${error.stack}`));
return;
}
}
resolve();
});
});
}
}
```
Then, I have created a separate config for the `utilityProcess`, like so:
```typescript
import { Configuration } from "webpack";
import path from "path";
import { rules } from "./webpack.rules";
const modulePath = path.resolve(__dirname, "dist_utility/utility_process");
export const utilityConfig: Configuration = {
entry: "./src/utility.ts", // Change to your own entry point
target: "node",
module: {
rules,
},
output: {
path: modulePath,
filename: "index.js",
},
resolve: {
extensions: [".js", ".ts", ".jsx", ".tsx", ".css", ".json"],
},
// TODO: find a way to infer this based on whether we run electron-forge start
// or package.
mode: "development",
};
```
Finally, to expose the compiled location, I use the `DefinePlugin` from Webpack to manually set the global variable in the main process config:
```typescript
new DefinePlugin({
UTILITY_PROCESS_MODULE_PATH: JSON.stringify(modulePath),
}),
```
As you can see in my TODO for the `utilityProcess` config, I believe it is currently not possible to tell whether we are running `electron-forge start` or `electron-forge make`, which makes it hard to pass the correct `mode` value to Webpack. This also feels like a less stable approach than having something natively supported by the `WebpackPlugin`.
### Additional information
The main use case for this feature is that you want to share code across your main process, renderer process, and utility process. If you are using Typescript for the common code, for example, then sharing without using a Webpack build step is impossible.
Contributor guide
Assessment
This issue has not been assessed yet.