Skip to content

Customizable options for HtmlWebpackPlugin #2968

Open
@sgammon

Description

Pre-flight checklist

  • I have read the contribution documentation for this project.
  • I agree to follow the code of conduct that this project uses.
  • I have searched the issue tracker for a feature request that matches the one I want to file, without success.

Problem description

I want to adjust the output from HtmlWebpackPlugin, or use addons with it via the @electron-forge Webpack plugin. But, because @electron-forge/plugin-webpack uses the HtmlWebpackPlugin privately, and no such options are provided, the plugin invocation cannot be adjusted.

As a user, I still want to leverage @electron-forge's built-in configuration of HtmlWebpackPlugin, just with adjusted parameters and extra add-ons.

Additionally, some plugins need to be invoked only when the HTML plugin is invoked, which leaves no opportunity in the existing Forge configuration structure to safely declare the plugin.

Proposed solution

I would like to propose three new properties to be added to WebpackConfig.ts:

Config.ts:

export interface WebpackPluginEntryPoint {
  //...

  /**
   * Custom options to merge into the configuration passed to `HtmlWebpackPlugin`.
   */
  htmlOptions?: Partial<HtmlWebpackPlugin.Options>;
  /**
   * Plugins to include before `HtmlWebpackPlugin`; typically, HTML plugin add-ons will
   * need to be placed here.
   */
  htmlPlugins?: WebpackPlugin[];
  /**
   * Additional options to merge into the Webpack `output` configuration for this entry-
   * point.
   */
  output?: object;
}

These options would be passed to the HtmlWebpackPlugin invocation via Object.assign(...), like so:

Config.ts:

@@ -161,28 +161,29 @@ class WebpackConfigGenerator {
                 target: this.rendererTarget(entryPoint),
                 devtool: this.rendererSourceMapOption,
                 mode: this.mode,
-                output: {
+                output: Object.assign(entryPoint.output || {}, {
                     path: _path.default.resolve(this.webpackDir, 'renderer'),
                     filename: '[name]/index.js',
                     globalObject: 'self',
                     ...this.isProd ? {} : {
                         publicPath: '/'
                     }
-                },
+                }),
                 node: {
                     __dirname: false,
                     __filename: false
                 },
                 plugins: [
+                    ...(entryPoint.htmlPlugins || []),
                     ...entryPoint.html ? [
-                        new _htmlWebpackPlugin.default({
+                        new _htmlWebpackPlugin.default(Object.assign({}, {
                             title: entryPoint.name,
                             template: entryPoint.html,
                             filename: `${entryPoint.name}/index.html`,
                             chunks: [
                                 entryPoint.name
                             ].concat(entryPoint.additionalChunks || [])
-                        }), 
+                        }, entryPoint.htmlOptions || {}))
                     ] : [],
                     new _webpack.default.DefinePlugin(defines),
                     new _assetRelocatorPatch.default(this.isProd, !!this.pluginConfig.renderer.nodeIntegration), 

Then, in forge.config.js, the user can control these parameters:

forge.config.js:

  "plugins": [
    [
      "@electron-forge/plugin-webpack",
      {
        "mainConfig": "./webpack.main.config.js",
        "renderer": {
          "config": "./webpack.renderer.config.js",
          "entryPoints": [
            {
              "html": "./src/index.html",
              "js": "./src/renderer.tsx",
              "name": "main_window",
              "preload": {"js": "./src/preload.ts"},

              // 👇 control over output options needed for some plugins
              "output": {
                "crossOriginLoading": "anonymous",
              },

              // 👇 custom HTML plugin options
              "htmlOptions": {
                "minify": true
              },

              // 👇 add-ons for HtmlWebpackPlugin here
              "htmlPlugins": [
                new SubresourceIntegrityPlugin(),
              ]
            }
          ]
        }
      }
    ],

Alternatives considered

I have considered:

1) Suppressing HTML features in @electron-forge/plugin-webpack.
This is sub-optimal because the plugin still drives chunking, entrypoints, etc., and is very useful in this regard. I don't want to have to make a trade-off decision between these benefits and custom options.

2) Mounting as regular plugins.
When I try to use HTML plugin add-ons as regular plug-ins, they end up in the plugin list after HtmlWebpackPlugin, and so their output isn't picked up.

3) Patching.
We're doing this currently via Yarn and it's working great, but this limits the benefits only to us and forces maintenance of the patch. The plugin's approach is solid, it just needs extra options to allow users to get in there and customize the invocation.

Additional information

No response

Metadata

Assignees

No one assigned

    Labels

    enhancementplugin/webpackIssues or pull requests related to first-party webpack plugins/templates

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions