html-webpack-plugin: TypeError: Cannot read property 'get' of undefined when used with webpack 5

Expected behaviour

TypeError should not be thrown

Current behaviour

TypeError is thrown

Environment

capaj@capaj-i7-4771:~/git_projects/looop/project-alpha$ node -e "var os=require('os');console.log('Node.js ' + process.version + '\n' + os.platform() + ' ' + os.release())"
Node.js v8.11.4
linux 4.18.0-12-generic
capaj@capaj-i7-4771:~/git_projects/looop/project-alpha$ npm --version
6.4.1

capaj@capaj-i7-4771:~/git_projects/looop/project-alpha$ npm ls html-webpack-plugin
looop@ /home/capaj/git_projects/looop/project-alpha
└─┬ @storybook/react@4.1.2
  └─┬ @storybook/core@4.1.2
    └── html-webpack-plugin@4.0.0-beta.5 

I have webpack@5.0.0-alpha.1

/home/capaj/git_projects/looop/project-alpha/node_modules/html-webpack-plugin/lib/compiler.js:341
    const timestamp = fileTimestamps.get(fileDependency);
                                     ^

TypeError: Cannot read property 'get' of undefined
    at childCompiler.fileDependencies.some (/home/capaj/git_projects/looop/project-alpha/node_modules/html-webpack-plugin/lib/compiler.js:341:38)
    at Array.some (<anonymous>)
    at isChildCompilerCacheOutdated (/home/capaj/git_projects/looop/project-alpha/node_modules/html-webpack-plugin/lib/compiler.js:340:59)
    at Object.hasOutDatedTemplateCache (/home/capaj/git_projects/looop/project-alpha/node_modules/html-webpack-plugin/lib/compiler.js:321:16)
    at compiler.hooks.thisCompilation.tap (/home/capaj/git_projects/looop/project-alpha/node_modules/html-webpack-plugin/index.js:132:25)
    at SyncHook.eval [as call] (eval at create (/home/capaj/git_projects/looop/project-alpha/node_modules/tapable/lib/HookCodeFactory.js:19:10), <anonymous>:7:1)
    at Compiler.newCompilation (/home/capaj/git_projects/looop/project-alpha/node_modules/webpack/lib/Compiler.js:526:30)
    at hooks.beforeCompile.callAsync.err (/home/capaj/git_projects/looop/project-alpha/node_modules/webpack/lib/Compiler.js:567:29)
    at AsyncSeriesHook.eval [as callAsync] (eval at create (/home/capaj/git_projects/looop/project-alpha/node_modules/tapable/lib/HookCodeFactory.js:32:10), <anonymous>:6:1)
    at Compiler.compile (/home/capaj/git_projects/looop/project-alpha/node_modules/webpack/lib/Compiler.js:562:28)
    at compiler.hooks.watchRun.callAsync.err (/home/capaj/git_projects/looop/project-alpha/node_modules/webpack/lib/Watching.js:107:19)
    at AsyncSeriesHook.eval [as callAsync] (eval at create (/home/capaj/git_projects/looop/project-alpha/node_modules/tapable/lib/HookCodeFactory.js:32:10), <anonymous>:15:1)
    at compiler.cache.endIdle.err (/home/capaj/git_projects/looop/project-alpha/node_modules/webpack/lib/Watching.js:70:33)
    at AsyncParallelHook.eval [as callAsync] (eval at create (/home/capaj/git_projects/looop/project-alpha/node_modules/tapable/lib/HookCodeFactory.js:32:10), <anonymous>:6:1)
    at Cache.endIdle (/home/capaj/git_projects/looop/project-alpha/node_modules/webpack/lib/Cache.js:39:22)
    at Watching._go (/home/capaj/git_projects/looop/project-alpha/node_modules/webpack/lib/Watching.js:68:23)

Sorry I don’t have a minimal webpack.config.js

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 15
  • Comments: 51 (27 by maintainers)

Commits related to this issue

Most upvoted comments

If anyone is looking for a quick solution, I can’t vouch for the correctness of this but it seems to work for me. Install the patch-package NPM module and set the postinstall script as instructed. Then, create a directory named “patches” and a file named something like “html-webpack-plugin+4.0.0-beta.8.patch” with the following contents:

diff --git a/node_modules/html-webpack-plugin/lib/compiler.js b/node_modules/html-webpack-plugin/lib/compiler.js
index 8960360..2f431d7 100644
--- a/node_modules/html-webpack-plugin/lib/compiler.js
+++ b/node_modules/html-webpack-plugin/lib/compiler.js
@@ -336,9 +336,9 @@ function isChildCompilerCacheOutdated (mainCompilation, childCompiler) {
     return false;
   }
   // Check if any dependent file was changed after the last compilation
-  const fileTimestamps = mainCompilation.fileTimestamps;
+  const fileTimestamps = mainCompilation.fileSystemInfo._fileTimestamps;
   const isCacheOutOfDate = childCompiler.fileDependencies.some((fileDependency) => {
-    const timestamp = fileTimestamps.get(fileDependency);
+    const timestamp = fileTimestamps.get(fileDependency).timestamp;
     // If the timestamp is not known the file is new
     // If the timestamp is larger then the file has changed
     // Otherwise the file is still the same

Run npx patch-package and it will at least stop the build from crashing and seems to allow proper hot reloading when I edit things so far.

I had patched my packages already, but it’s more quick-and-dirty: This change-comparison reflects the changes I’ve made locally, so everything is working fine with Webpack 5 again. I didn’t really understand the previous caching algorithm and adapted it in the easiest way for me, that’s certainly not right for all cases and needs to be looked at again. Also, it doesn’t fit perfectly yet, because the first time you render it, it also returns that the cache is invalid. Unfortunately, I don’t have time to understand or improve it any further. But if someone wants a (not perfect) solution with Webpack 5 for now, he can use my changes as a patch. [edit: added some code, that I forgot to commit]

This is the first time I see the FileSystemInfo.createSnapshot api and I must say it is impressive.
It abstracts the entire “do I have to recompile” problem in a very simple way! 👍

Will FileSystemInfo also be back ported to webpack 4 in order to help use so we can support webpack 4 and 5 with one code base?

Is a snapshot automatically created for every compilation or for every compiler?
If not is there an util to create a snapshot for all dependencies of a given compiler?

For anyone interested in an even quicker and dirtier foolproof patch, here’s what I’m doing now:

  1. npm i patch-package
  2. Open package.json and add this in the scripts section:
    "postinstall": "patch-package"
    
  3. Create a patches directory in your project root, add a file named html-webpack-plugin+4.0.0-beta.11.patch and paste the following in the file:
diff --git a/node_modules/html-webpack-plugin/lib/compiler.js b/node_modules/html-webpack-plugin/lib/compiler.js
index a13abf3..011294d 100644
--- a/node_modules/html-webpack-plugin/lib/compiler.js
+++ b/node_modules/html-webpack-plugin/lib/compiler.js
@@ -335,6 +335,7 @@ function isChildCompilerCacheOutdated (mainCompilation, childCompiler) {
   if (!childCompiler.compilationStartedTimestamp) {
     return false;
   }
+  return true;
   // Check if any dependent file was changed after the last compilation
   const fileTimestamps = mainCompilation.fileTimestamps;
   const isCacheOutOfDate = childCompiler.fileDependencies.some((fileDependency) => {
  1. If you don’t need to run an npm install and just need to apply the patch, run npx patch-package or npm run postinstall.

@elchicofrommo I added the step-by-step here for your benefit. Hope it helps!

@sokra I thought I’d see if I could implement a proper fix real quick but it doesn’t have any documentation and looking at the source for it just leaves me with too many questions. I can’t tell if there’s supposed to be a general cache managed by Webpack that is just accessed by plugins or if the individual plugins actually have to call createSnapshot. I also don’t know what each parameter for that call is supposed to be and the jsdoc comments are extremely unhelpful.

I’m not sure much can be done about a proper solution until there’s real documentation on Webpack 5 but the main thing needed until then is just for this plugin to not completely crash the build.

Accessing _fileTimestamps is not a perfect solution. It’s private and may not include all timestamps.

Instead use the snapshot api to create a snapshot and later check if the snapshot is still valid. Methods: createSnapshot and checkSnapshotValid in FileSystemInfo

There is a new API in FileSystemInfo.

FileSystemInfo.createSnapshot(startTime, files, directories, missing, options, callback) creates a snapshot of the filesystem to check for validity later.

FileSystemInfo.checkSnapshotValid(snapshot, callback) checks if a the filesystem changed (for the passed files and directories) compared to the snapshot.

@fivethreeo if we have a working solution I would love to release it as html-webpack-plugin 5 alpha however the current solution is using an unsupported private feature of webpack which might break once they publish the new major

I am happy to publish it as alpha if anyone has time to develop a solution based on the new FileSystemInfo.createSnapshot proposed by the webpack core team

+1 using @PutziSan patch for now…

@PutziSan wow cool thanks a lot I’ll take a look at it! 💯

Although It works on the first build, as you save a file and it is on watch mode, we get the same error, more details here: https://github.com/entria/entria-fullstack/pull/46#issuecomment-452123511 running 4.0.0-beta.5

4.0.0-beta.5 works on our config

I forked this repo and cherry picked PutziSan’s changes, working on fixing tests so they pass on webpack5. If I get it fixed I will release a scoped package until changes can be incorporated in master.

here are another error we are getting on webpack 5

 TypeError: Cannot add property htmlWebpackPluginAlterChunks, object is not extensible
@entria/web:     at compiler.hooks.compilation.tap.compilation (/Users/sibelius/Dev/entria/entria-fullstack/node_modules/html-webpack-plugin/index.js:59:56)

you can run it here https://github.com/entria/entria-fullstack/pull/46

it is using 3.2.0