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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't get assets by entry point chunk name in v4 #1369

Open
frosas opened this issue Mar 25, 2020 · 15 comments
Open

Can't get assets by entry point chunk name in v4 #1369

frosas opened this issue Mar 25, 2020 · 15 comments

Comments

@frosas
Copy link

frosas commented Mar 25, 2020

Congrats on the new major version guys.

Expected behaviour

In v3, it was possible to obtain chunk assets by accessing htmlWebpackPlugin.files.chunks[entryName].entry from the template.

Current behaviour

According to https://github.com/jantimon/html-webpack-plugin/blob/master/CHANGELOG.md#breaking-changes there's a breaking change affecting such feature:

The chunks property was removed and the js and css property was converted from a string into an object { entryName: string, path: string}

According to that, one would expect js and css properties in htmlWebpackPlugin.files to contain the mentioned object but I still get the same array of strings as in v3:

{
  ...,
  "files": {
    "publicPath": "",
    "js": [
      "scripts/main.js",
      "scripts/service-worker.js",
      "scripts/ping-worker.js"
    ],
    "css": [
      "styles/main.css"
    ]
  },
  ...
}

Environment

Tell us which operating system you are using, as well as which versions of Node.js, npm, webpack, and html-webpack-plugin. Run the following to get it quickly:

~/projects/lag (dependabot/npm_and_yarn/html-webpack-plugin-4.0.1, Node v12.11.1)
❯ node -e "var os=require('os');console.log('Node.js ' + process.version + '\n' + os.platform() + ' ' + os.release())"
npm --version
npm ls webpack
npm ls html-webpack-plugin
Node.js v12.11.1
darwin 19.3.0

~/projects/lag (dependabot/npm_and_yarn/html-webpack-plugin-4.0.1, Node v12.11.1)
❯ npm --version
npm ls webpack
npm ls html-webpack-plugin
6.13.7

~/projects/lag (dependabot/npm_and_yarn/html-webpack-plugin-4.0.1, Node v12.11.1)
❯ npm ls webpack
npm ls html-webpack-plugin
[email protected] /Users/frosas/projects/lag
└── [email protected]

~/projects/lag (dependabot/npm_and_yarn/html-webpack-plugin-4.0.1, Node v12.11.1)
❯ npm ls html-webpack-plugin
[email protected] /Users/frosas/projects/lag
└── [email protected]

Config

Copy the minimal webpack.config.js to produce this issue:

https://github.com/frosas/lag/blob/f82961175485b1540cefc7f48bc251dad83df10b/webpack.config.js#L55-L71

Copy your template file if it is part of this issue:

https://github.com/frosas/lag/blob/f82961175485b1540cefc7f48bc251dad83df10b/html/index.ejs

Additional context

I couldn't find any reference to entryName in the plugin code that suggested the new behavior was actually implemented.

Also, htmlWebpackPlugin.files section in https://github.com/jantimon/html-webpack-plugin/blob/master/README.md seems outdated now.

@jantimon
Copy link
Owner

jantimon commented Mar 26, 2020

unfortunately you are right this is the entire information available inside the template:

/**
* Generate all tags script for the given file paths
* @param {Array<string>} jsAssets
* @returns {Array<HtmlTagObject>}
*/
generatedScriptTags (jsAssets) {
return jsAssets.map(scriptAsset => ({
tagName: 'script',
voidTag: false,
attributes: {
defer: this.options.scriptLoading !== 'blocking',
src: scriptAsset
}
}));
}

what we could do is to extend the template variable generator:

html-webpack-plugin/index.js

Lines 1015 to 1045 in 2ab27f0

/**
* The default for options.templateParameter
* Generate the template parameters
*
* Generate the template parameters for the template function
* @param {WebpackCompilation} compilation
* @param {{
publicPath: string,
js: Array<string>,
css: Array<string>,
manifest?: string,
favicon?: string
}} assets
* @param {{
headTags: HtmlTagObject[],
bodyTags: HtmlTagObject[]
}} assetTags
* @param {ProcessedHtmlWebpackOptions} options
* @returns {TemplateParameter}
*/
function templateParametersGenerator (compilation, assets, assetTags, options) {
return {
compilation: compilation,
webpackConfig: compilation.options,
htmlWebpackPlugin: {
tags: assetTags,
files: assets,
options: options
}
};
}

To provide for example a key value map like

htmlWebpackPlugin.entryFileMapping = {
  'path/to/my/js/file.js': 'main'
}

Or we could iterate over those entries and attach it to the object directly here:

const preparedAssetTags = {
headTags: this.prepareAssetTagGroupForRendering(assetTags.headTags),
bodyTags: this.prepareAssetTagGroupForRendering(assetTags.bodyTags)
};

@frosas
Copy link
Author

frosas commented Mar 26, 2020

The original feature seems to be quite popular (https://github.com/search?q=htmlWebpackPlugin.files.chunks&type=Code), would it be possible to maintain backwards compatibility to make the upgrade process as easy as possible to all? Happy to discuss alternatives if not :)

@jantimon
Copy link
Owner

jantimon commented Mar 26, 2020

The goal was to make it easier for plugin writers to modify which tags are injected into the html

The idea behind that is that there would be a single source of truth - headTags are injected into the head and bodyTags are injected in the body.

If the information is hold at two places the plugin writers will always need to keep it in sync.
So this api tried to prevent errors but it looks like it also prevents some features now 🤦‍♂

I would prefer to not add files.chunks again for that reason.
However we might bring it back as a plugin (which would be full backwards compatible) or provide a better api to allow the same features as before.

An api might look like for example like this (would output all <script> / tags for the common entry):

<%= htmlWebpackPlugin.utils.getChunkTags('common', htmlWebpackPlugin.headTags) %>

Also other utils might be possible e.g. (would inline js or css directly in the markup for all headTags):

<%= htmlWebpackPlugin.utils.inlineTags(htmlWebpackPlugin.headTags) %>

And they could be combined e.g.:

<%= htmlWebpackPlugin.utils.inlineTags(htmlWebpackPlugin.utils.getChunkTags('common', htmlWebpackPlugin.headTags)) %>

@mmaday
Copy link

mmaday commented Mar 30, 2020

The README references the custom html-webpack-template that no longer works with v4:

You can use the lodash syntax out of the box. If the inject feature doesn't fit your needs and you want full control over the asset placement use the default template of the html-webpack-template project as a starting point for writing your own.

That template reliant on the files object along with chunks.

@kroko
Copy link
Contributor

kroko commented Apr 1, 2020

Reposting in right ticket.

So...

Doing

  <!--
    // DEBUG
    <%= JSON.stringify(htmlWebpackPlugin.files, null, 1) %>
  -->

in template will reveal structure such as this in 3.2.0

<!--
    // DEBUG
    {
 "publicPath": "assets/",
 "chunks": {
  "runtime": {
   "size": 0,
   "entry": "assets/runtime.8379fd9de1396362f7d6.js",
   "hash": "2ca3c4b7e4ed2cdacddd",
   "css": []
  },
  "vendors": {
   "size": 1155526,
   "entry": "assets/vendors.eb616a137720b1a65dd4.js",
   "hash": "9ef5ffdd5a6b634a2d3d",
   "css": []
  },
  "corepoly": {
   "size": 88179,
   "entry": "assets/corepoly.0cf265cbf5506d68f293.js",
   "hash": "b2a7eccd9d2fa4b53fd6",
   "css": []
  },
  "index": {
   "size": 682872,
   "entry": "assets/index.a8a382748a1236847da2.js",
   "hash": "ff015be1159991382c21",
   "css": [
    "assets/index.5728905c20b76099151d.css"
   ]
  }
 },
 "js": [
  "assets/runtime.8379fd9de1396362f7d6.js",
  "assets/vendors.eb616a137720b1a65dd4.js",
  "assets/corepoly.0cf265cbf5506d68f293.js",
  "assets/index.a8a382748a1236847da2.js"
 ],
 "css": [
  "assets/index.5728905c20b76099151d.css"
 ]
}
  -->

however in 4.x it generates

<!--
    // DEBUG
    {
 "publicPath": "assets/",
 "js": [
  "assets/runtime.8379fd9de1396362f7d6.js",
  "assets/vendors.63498add9423ff0ff9a0.js",
  "assets/corepoly.b51addbe29703dc78ebb.js",
  "assets/index.9eca6c1781f27940f1de.js"
 ],
 "css": [
  "assets/index.5728905c20b76099151d.css"
 ]
}
  -->

thus in 4.0 it is not as shown in docs https://github.com/jantimon/html-webpack-plugin#writing-your-own-templates which shows

"htmlWebpackPlugin": {
  "files": {
    "css": [ "main.css" ],
    "js": [ "assets/head_bundle.js", "assets/main_bundle.js"],
    "chunks": {
      "head": {
        "entry": "assets/head_bundle.js",
        "css": [ "main.css" ]
      },
      "main": {
        "entry": "assets/main_bundle.js",
        "css": []
      },
    }
  }
}

Thus template that worked with 3.2.0

<% for (let key in htmlWebpackPlugin.files.chunks) { %>
   <script src="<%= htmlWebpackPlugin.files.chunks[key].entry %>"></script>
<% } %>

does not work any more with 4.0. Just as the OP states.

So what is the supposed way to get chunks in template in v4?

@jantimon
Copy link
Owner

jantimon commented Apr 1, 2020

@kroko I am very sorry but right now <%= htmlWebpackPlugin.chunks is no longer supported anymore as explained here: #1369 (comment)

I have some solutions in mind but it will take me a moment to implement it (as I am doing that in my spare time).

Thanks for bringing up the issue with the outdated documentation 👍

@kroko
Copy link
Contributor

kroko commented Apr 1, 2020

Sure, I know that the changes are for good, as you have explained it in the comment. Thanks for the great work! 🙇

Just to clarify - what does "not supported" mean given that htmlWebpackPlugin.files does return results (just not the chunks)?
Can htmlWebpackPlugin.files.js and htmlWebpackPlugin.files.css still be used if one does not plan on having some chunk logic (in other words, if there is a case where all compiled assets should be dumped in template without any filtering - as it is with SPA with one entry - then htmlWebpackPlugin.files still can be used)? Or "not supported" means that htmlWebpackPlugin.files key will be removed altogether at some point?

@jantimon
Copy link
Owner

jantimon commented Apr 1, 2020

Sorry for the confusion you are right only chunks was removed.

The new structure is:

/**
* The values which are available during template execution
*
* Please keep in mind that the `templateParameter` options allows to change them
*/
interface TemplateParameter {
compilation: any;
htmlWebpackPlugin: {
tags: {
headTags: HtmlTagObject[];
bodyTags: HtmlTagObject[];
};
files: {
publicPath: string;
js: Array<string>;
css: Array<string>;
manifest?: string;
favicon?: string;
};
options: Options;
};
webpackConfig: any;
}

The easiest way to output all head tags is:

<%= htmlWebpackPlugin.tags.headTags %>
<%= htmlWebpackPlugin.tags.bodyTags %>

@ghost
Copy link

ghost commented Apr 1, 2020

Is this going to change again?

@jantimon
Copy link
Owner

jantimon commented Apr 1, 2020

I will not remove or change the existing structure but probably extend it in some way to allow accessing the chunkname and source of the files to allow inlining again

bernd added a commit to Graylog2/graylog2-server that referenced this issue Apr 2, 2020
Version 4.x dropped support for accessing the "chunks" field in
templates. See the discussion in:
jantimon/html-webpack-plugin#1369 (comment)

This fixes loading web plugins in a Graylog production build.

Also sync "yarn.lock".
kyleknighted pushed a commit to Graylog2/graylog2-server that referenced this issue Apr 2, 2020
Version 4.x dropped support for accessing the "chunks" field in
templates. See the discussion in:
jantimon/html-webpack-plugin#1369 (comment)

This fixes loading web plugins in a Graylog production build.

Also sync "yarn.lock".
@hiradyazdan
Copy link

hiradyazdan commented May 5, 2020

I don't know if I am following these commits properly, but it looks like they're not going the way the discussion here was going, and more like the opposite! @jantimon the files.chunks object property was quite useful in earlier versions specially for SPAs. Is this still something you consider rethinking a solution for?

@melyourhero
Copy link

Any updates about this issue?

@ajkl2533
Copy link

@jantimon This seems to still be an issue, do you have any plan to provide some way how to get common chunk names?

@rastalamm
Copy link

Bumping for viz

Repository owner deleted a comment from stale bot Feb 2, 2022
@jantimon jantimon removed the wontfix label Feb 2, 2022
@jantimon jantimon reopened this Feb 2, 2022
@daniel-nagy
Copy link

I just upgraded from v3 to v5 and ran into this issue. This was part of a larger effort to upgrade to Webpack 5. The removal of the chunks property from the template context was the most difficult part of the upgrade.

I ended up writing a backwards compatibility plugin however this is far from complete.

class HtmlWebpackBackwardsCompatibilityPlugin {
  apply(compiler) {
    compiler
      .hooks
      .compilation
      .tap("HtmlWebpackBackwardsCompatibilityPlugin", compilation => {
        HtmlWebpackPlugin
          .getHooks(compilation)
          .beforeAssetTagGeneration
          .tapAsync("HtmlWebpackBackwardsCompatibilityPlugin", (data, callback) => {
            const { publicPath } = data.assets;
            data.assets.chunks = {};

            for (const entryPoint of compilation.entrypoints.values()) {
              for (const chunk of entryPoint.chunks) {
                data.assets.chunks[chunk.name] = {
                  entry: chunk.files
                    .map(file => publicPath + file)
                    .find(file => file.endsWith(".js"))
                };
              }
            }

            callback(null, data);
          }
        );
      });
  }
}

There is a lot of logic that would need to be copied from

function htmlWebpackPluginAssets (compilation, entryNames, publicPath) {
in order to make this complete, e.g. chunk sorting, proper chunk filtering, public path resolution, hot module replacement, etc.

In our index.ejs file we need to be able to look up, and filter, a chunk entry by name. Not all of our chunks are simply loaded as script tags.

Given the barrier to upgrade by either rearchitecting our chunk loading strategy or writing a custom Webpack plugin to duplicate the logic in the HTMLWebpack plugin I think it makes sense for this plugin to add back the chunks property in some form of deprecated manor or to provide an API that allows listing and indexing chunks in the template context.

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

No branches or pull requests

9 participants