html-webpack-plugin 3.1.0 → 4.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,99 +1,12 @@
1
+ // @ts-check
2
+ /** @typedef {import("webpack/lib/Compilation.js")} WebpackCompilation */
1
3
  'use strict';
2
4
 
3
- const toposort = require('toposort');
4
- const _ = require('lodash');
5
-
6
- /**
7
- Sorts dependencies between chunks by their "parents" attribute.
8
-
9
- This function sorts chunks based on their dependencies with each other.
10
- The parent relation between chunks as generated by Webpack for each chunk
11
- is used to define a directed (and hopefully acyclic) graph, which is then
12
- topologically sorted in order to retrieve the correct order in which
13
- chunks need to be embedded into HTML. A directed edge in this graph is
14
- describing a "is parent of" relationship from a chunk to another (distinct)
15
- chunk. Thus topological sorting orders chunks from bottom-layer chunks to
16
- highest level chunks that use the lower-level chunks.
17
-
18
- @param {Array} chunks an array of chunks as generated by the html-webpack-plugin.
19
- - For webpack < 4, It is assumed that each entry contains at least the properties
20
- "id" (containing the chunk id) and "parents" (array containing the ids of the
21
- parent chunks).
22
- - For webpack 4+ the see the chunkGroups param for parent-child relationships
23
-
24
- @param {Array} chunks an array of ChunkGroups that has a getParents method.
25
- Each ChunkGroup contains a list of chunks in order.
26
-
27
- @return {Array} A topologically sorted version of the input chunks
28
- */
29
- module.exports.dependency = (chunks, chunkGroups) => {
30
- if (!chunks) {
31
- return chunks;
32
- }
33
-
34
- // We build a map (chunk-id -> chunk) for faster access during graph building.
35
- const nodeMap = {};
36
-
37
- chunks.forEach(chunk => {
38
- nodeMap[chunk.id] = chunk;
39
- });
40
-
41
- // Next, we add an edge for each parent relationship into the graph
42
- let edges = [];
43
-
44
- if (chunkGroups) {
45
- // Add an edge for each parent (parent -> child)
46
- edges = chunkGroups.reduce((result, chunkGroup) => result.concat(
47
- Array.from(chunkGroup.parentsIterable, parentGroup => [parentGroup, chunkGroup])
48
- ), []);
49
- const sortedGroups = toposort.array(chunkGroups, edges);
50
- // flatten chunkGroup into chunks
51
- const sortedChunks = sortedGroups
52
- .reduce((result, chunkGroup) => result.concat(chunkGroup.chunks), [])
53
- .map(chunk => // use the chunk from the list passed in, since it may be a filtered list
54
- nodeMap[chunk.id])
55
- .filter((chunk, index, self) => {
56
- // make sure exists (ie excluded chunks not in nodeMap)
57
- const exists = !!chunk;
58
- // make sure we have a unique list
59
- const unique = self.indexOf(chunk) === index;
60
- return exists && unique;
61
- });
62
- return sortedChunks;
63
- } else {
64
- // before webpack 4 there was no chunkGroups
65
- chunks.forEach(chunk => {
66
- if (chunk.parents) {
67
- // Add an edge for each parent (parent -> child)
68
- chunk.parents.forEach(parentId => {
69
- // webpack2 chunk.parents are chunks instead of string id(s)
70
- const parentChunk = _.isObject(parentId) ? parentId : nodeMap[parentId];
71
- // If the parent chunk does not exist (e.g. because of an excluded chunk)
72
- // we ignore that parent
73
- if (parentChunk) {
74
- edges.push([parentChunk, chunk]);
75
- }
76
- });
77
- }
78
- });
79
- // We now perform a topological sorting on the input chunks and built edges
80
- return toposort.array(chunks, edges);
81
- }
82
- };
83
-
84
5
  /**
85
- * Sorts the chunks based on the chunk id.
86
- *
87
- * @param {Array} chunks the list of chunks to sort
88
- * @return {Array} The sorted list of chunks
6
+ * @type {{[sortmode: string] : (entryPointNames: Array<string>, compilation, htmlWebpackPluginOptions) => Array<string> }}
7
+ * This file contains different sort methods for the entry chunks names
89
8
  */
90
- module.exports.id = chunks => chunks.sort(function orderEntryLast (a, b) {
91
- if (a.entry !== b.entry) {
92
- return b.entry ? 1 : -1;
93
- } else {
94
- return b.id - a.id;
95
- }
96
- });
9
+ module.exports = {};
97
10
 
98
11
  /**
99
12
  * Performs identity mapping (no-sort).
@@ -104,34 +17,24 @@ module.exports.none = chunks => chunks;
104
17
 
105
18
  /**
106
19
  * Sort manually by the chunks
107
- * @param {Array} chunks the chunks to sort
108
- * @return {Array} The sorted chunks
20
+ * @param {string[]} entryPointNames the chunks to sort
21
+ * @param {WebpackCompilation} compilation the webpack compilation
22
+ * @param htmlWebpackPluginOptions the plugin options
23
+ * @return {string[]} The sorted chunks
109
24
  */
110
- module.exports.manual = (chunks, specifyChunks) => {
111
- const chunksResult = [];
112
- let filterResult = [];
113
- if (Array.isArray(specifyChunks)) {
114
- for (var i = 0; i < specifyChunks.length; i++) {
115
- filterResult = chunks.filter(chunk => {
116
- if (chunk.names[0] && chunk.names[0] === specifyChunks[i]) {
117
- return true;
118
- }
119
- return false;
120
- });
121
- filterResult.length > 0 && chunksResult.push(filterResult[0]);
122
- }
25
+ module.exports.manual = (entryPointNames, compilation, htmlWebpackPluginOptions) => {
26
+ const chunks = htmlWebpackPluginOptions.chunks;
27
+ if (!Array.isArray(chunks)) {
28
+ return entryPointNames;
123
29
  }
124
- return chunksResult;
30
+ // Remove none existing entries from
31
+ // htmlWebpackPluginOptions.chunks
32
+ return chunks.filter((entryPointName) => {
33
+ return compilation.entrypoints.has(entryPointName);
34
+ });
125
35
  };
126
36
 
127
37
  /**
128
38
  * Defines the default sorter.
129
39
  */
130
- module.exports.auto = module.exports.id;
131
-
132
- // In webpack 2 the ids have been flipped.
133
- // Therefore the id sort doesn't work the same way as it did for webpack 1
134
- // Luckily the dependency sort is working as expected
135
- if (Number(require('webpack/package.json').version.split('.')[0]) > 1) {
136
- module.exports.auto = module.exports.dependency;
137
- }
40
+ module.exports.auto = module.exports.none;
package/lib/compiler.js CHANGED
@@ -1,11 +1,16 @@
1
- /*
1
+ // @ts-check
2
+ /** @typedef {import("webpack/lib/Compilation.js")} WebpackCompilation */
3
+ /** @typedef {import("webpack/lib/Compiler.js")} WebpackCompiler */
4
+ /** @typedef {import("webpack/lib/Chunk.js")} WebpackChunk */
5
+ 'use strict';
6
+ /**
7
+ * @file
2
8
  * This file uses webpack to compile a template with a child compiler.
3
9
  *
4
10
  * [TEMPLATE] -> [JAVASCRIPT]
5
11
  *
6
12
  */
7
13
  'use strict';
8
- const path = require('path');
9
14
  const NodeTemplatePlugin = require('webpack/lib/node/NodeTemplatePlugin');
10
15
  const NodeTargetPlugin = require('webpack/lib/node/NodeTargetPlugin');
11
16
  const LoaderTargetPlugin = require('webpack/lib/LoaderTargetPlugin');
@@ -13,113 +18,366 @@ const LibraryTemplatePlugin = require('webpack/lib/LibraryTemplatePlugin');
13
18
  const SingleEntryPlugin = require('webpack/lib/SingleEntryPlugin');
14
19
 
15
20
  /**
16
- * Compiles the template into a nodejs factory, adds its to the compilation.assets
17
- * and returns a promise of the result asset object.
18
- *
19
- * @param template relative path to the template file
20
- * @param context path context
21
- * @param outputFilename the file name
22
- * @param compilation The webpack compilation object
23
- *
24
- * Returns an object:
25
- * {
26
- * hash: {String} - Base64 hash of the file
27
- * content: {String} - Javascript executable code of the template
28
- * }
29
- *
21
+ * The HtmlWebpackChildCompiler is a helper to allow resusing one childCompiler
22
+ * for multile HtmlWebpackPlugin instances to improve the compilation performance.
30
23
  */
31
- module.exports.compileTemplate = function compileTemplate (template, context, outputFilename, compilation) {
32
- // The entry file is just an empty helper as the dynamic template
33
- // require is added in "loader.js"
34
- const outputOptions = {
35
- filename: outputFilename,
36
- publicPath: compilation.outputOptions.publicPath
37
- };
38
- // Store the result of the parent compilation before we start the child compilation
39
- const assetsBeforeCompilation = Object.assign({}, compilation.assets[outputOptions.filename]);
40
- // Create an additional child compiler which takes the template
41
- // and turns it into an Node.JS html factory.
42
- // This allows us to use loaders during the compilation
43
- const compilerName = getCompilerName(context, outputFilename);
44
- const childCompiler = compilation.createChildCompiler(compilerName, outputOptions);
45
- childCompiler.context = context;
46
- new NodeTemplatePlugin(outputOptions).apply(childCompiler);
47
- new NodeTargetPlugin().apply(childCompiler);
48
- new LibraryTemplatePlugin('HTML_WEBPACK_PLUGIN_RESULT', 'var').apply(childCompiler);
49
-
50
- // Using undefined as name for the SingleEntryPlugin causes a unexpected output as described in
51
- // https://github.com/jantimon/html-webpack-plugin/issues/895
52
- // Using a string as a name for the SingleEntryPlugin causes problems with HMR as described in
53
- // https://github.com/jantimon/html-webpack-plugin/issues/900
54
- // Until the HMR issue is fixed we keep the ugly output:
55
- new SingleEntryPlugin(this.context, template, undefined).apply(childCompiler);
56
-
57
- new LoaderTargetPlugin('node').apply(childCompiler);
58
-
59
- // Fix for "Uncaught TypeError: __webpack_require__(...) is not a function"
60
- // Hot module replacement requires that every child compiler has its own
61
- // cache. @see https://github.com/ampedandwired/html-webpack-plugin/pull/179
62
-
63
- // Backwards compatible version of: childCompiler.hooks.compilation
64
- (childCompiler.hooks ? childCompiler.hooks.compilation.tap.bind(childCompiler.hooks.compilation, 'HtmlWebpackPlugin') : childCompiler.plugin.bind(childCompiler, 'compilation'))(compilation => {
65
- if (compilation.cache) {
66
- if (!compilation.cache[compilerName]) {
67
- compilation.cache[compilerName] = {};
68
- }
69
- compilation.cache = compilation.cache[compilerName];
24
+ class HtmlWebpackChildCompiler {
25
+ constructor () {
26
+ /**
27
+ * @type {string[]} templateIds
28
+ * The template array will allow us to keep track which input generated which output
29
+ */
30
+ this.templates = [];
31
+ /**
32
+ * @type {Promise<{[templatePath: string]: { content: string, hash: string, entry: WebpackChunk }}>}
33
+ */
34
+ this.compilationPromise;
35
+ /**
36
+ * @type {number}
37
+ */
38
+ this.compilationStartedTimestamp;
39
+ /**
40
+ * @type {number}
41
+ */
42
+ this.compilationEndedTimestamp;
43
+ /**
44
+ * All file dependencies of the child compiler
45
+ * @type {string[]}
46
+ */
47
+ this.fileDependencies = [];
48
+ }
49
+
50
+ /**
51
+ * Add a templatePath to the child compiler
52
+ * The given template will be compiled by `compileTemplates`
53
+ * @param {string} template - The webpack path to the template e.g. `'!!html-loader!index.html'`
54
+ * @returns {boolean} true if the template is new
55
+ */
56
+ addTemplate (template) {
57
+ const templateId = this.templates.indexOf(template);
58
+ // Don't add the template to the compiler if a similar template was already added
59
+ if (templateId !== -1) {
60
+ return false;
70
61
  }
71
- });
62
+ // A child compiler can compile only once
63
+ // throw an error if a new template is added after the compilation started
64
+ if (this.isCompiling()) {
65
+ throw new Error('New templates can only be added before `compileTemplates` was called.');
66
+ }
67
+ // Add the template to the childCompiler
68
+ this.templates.push(template);
69
+ // Mark the cache invalid
70
+ return true;
71
+ }
72
72
 
73
- // Compile and return a promise
74
- return new Promise((resolve, reject) => {
75
- childCompiler.runAsChild((err, entries, childCompilation) => {
76
- // Resolve / reject the promise
77
- if (childCompilation && childCompilation.errors && childCompilation.errors.length) {
78
- const errorDetails = childCompilation.errors.map(error => error.message + (error.error ? ':\n' + error.error : '')).join('\n');
79
- reject(new Error('Child compilation failed:\n' + errorDetails));
80
- } else if (err) {
81
- reject(err);
82
- } else {
83
- // Replace [hash] placeholders in filename
84
- // In webpack 4 the plugin interface changed, so check for available fns
85
- const outputName = compilation.mainTemplate.getAssetPath
86
- ? compilation.mainTemplate.hooks.assetPath.call(outputOptions.filename, {
87
- hash: childCompilation.hash,
88
- chunk: entries[0]
89
- })
90
- : compilation.mainTemplate.applyPluginsWaterfall(
91
- 'asset-path',
92
- outputOptions.filename,
93
- {
94
- hash: childCompilation.hash,
95
- chunk: entries[0]
96
- });
97
-
98
- // Restore the parent compilation to the state like it
99
- // was before the child compilation
100
- compilation.assets[outputName] = assetsBeforeCompilation[outputName];
101
- if (assetsBeforeCompilation[outputName] === undefined) {
102
- // If it wasn't there - delete it
103
- delete compilation.assets[outputName];
73
+ /**
74
+ * Returns true if the childCompiler is currently compiling
75
+ * @retuns {boolean}
76
+ */
77
+ isCompiling () {
78
+ return !this.didCompile() && this.compilationStartedTimestamp !== undefined;
79
+ }
80
+
81
+ /**
82
+ * Returns true if the childCOmpiler is done compiling
83
+ */
84
+ didCompile () {
85
+ return this.compilationEndedTimestamp !== undefined;
86
+ }
87
+
88
+ /**
89
+ * This function will start the template compilation
90
+ * once it is started no more templates can be added
91
+ *
92
+ * @param {WebpackCompilation} mainCompilation
93
+ * @returns {Promise<{[templatePath: string]: { content: string, hash: string, entry: WebpackChunk }}>}
94
+ */
95
+ compileTemplates (mainCompilation) {
96
+ // To prevent multiple compilations for the same template
97
+ // the compilation is cached in a promise.
98
+ // If it already exists return
99
+ if (this.compilationPromise) {
100
+ return this.compilationPromise;
101
+ }
102
+
103
+ // The entry file is just an empty helper as the dynamic template
104
+ // require is added in "loader.js"
105
+ const outputOptions = {
106
+ filename: '__child-[name]',
107
+ publicPath: mainCompilation.outputOptions.publicPath
108
+ };
109
+ const compilerName = 'HtmlWebpackCompiler';
110
+ // Create an additional child compiler which takes the template
111
+ // and turns it into an Node.JS html factory.
112
+ // This allows us to use loaders during the compilation
113
+ const childCompiler = mainCompilation.createChildCompiler(compilerName, outputOptions);
114
+ // The file path context which webpack uses to resolve all relative files to
115
+ childCompiler.context = mainCompilation.compiler.context;
116
+ // Compile the template to nodejs javascript
117
+ new NodeTemplatePlugin(outputOptions).apply(childCompiler);
118
+ new NodeTargetPlugin().apply(childCompiler);
119
+ new LibraryTemplatePlugin('HTML_WEBPACK_PLUGIN_RESULT', 'var').apply(childCompiler);
120
+ new LoaderTargetPlugin('node').apply(childCompiler);
121
+
122
+ // Fix for "Uncaught TypeError: __webpack_require__(...) is not a function"
123
+ // Hot module replacement requires that every child compiler has its own
124
+ // cache. @see https://github.com/ampedandwired/html-webpack-plugin/pull/179
125
+ childCompiler.hooks.compilation.tap('HtmlWebpackPlugin', compilation => {
126
+ if (compilation.cache) {
127
+ if (!compilation.cache[compilerName]) {
128
+ compilation.cache[compilerName] = {};
104
129
  }
105
- resolve({
106
- // Hash of the template entry point
107
- hash: entries[0].hash,
108
- // Output name
109
- outputName: outputName,
110
- // Compiled code
111
- content: childCompilation.assets[outputName].source()
112
- });
130
+ compilation.cache = compilation.cache[compilerName];
113
131
  }
114
132
  });
133
+
134
+ // Add all templates
135
+ this.templates.forEach((template, index) => {
136
+ new SingleEntryPlugin(childCompiler.context, template, `HtmlWebpackPlugin_${index}`).apply(childCompiler);
137
+ });
138
+
139
+ this.compilationStartedTimestamp = new Date().getTime();
140
+ this.compilationPromise = new Promise((resolve, reject) => {
141
+ childCompiler.runAsChild((err, entries, childCompilation) => {
142
+ // Extract templates
143
+ const compiledTemplates = entries
144
+ ? extractHelperFilesFromCompilation(mainCompilation, childCompilation, outputOptions.filename, entries)
145
+ : [];
146
+ // Extract file dependencies
147
+ if (entries) {
148
+ this.fileDependencies = extractFileDependenciesFilesFromCompilation(entries);
149
+ }
150
+ // Reject the promise if the childCompilation contains error
151
+ if (childCompilation && childCompilation.errors && childCompilation.errors.length) {
152
+ const errorDetails = childCompilation.errors.map(error => error.message + (error.error ? ':\n' + error.error : '')).join('\n');
153
+ reject(new Error('Child compilation failed:\n' + errorDetails));
154
+ return;
155
+ }
156
+ // Reject if the error object contains errors
157
+ if (err) {
158
+ reject(err);
159
+ return;
160
+ }
161
+ /**
162
+ * @type {{[templatePath: string]: { content: string, hash: string, entry: WebpackChunk }}}
163
+ */
164
+ const result = {};
165
+ compiledTemplates.forEach((templateSource, entryIndex) => {
166
+ // The compiledTemplates are generated from the entries added in
167
+ // the addTemplate function.
168
+ // Therefore the array index of this.templates should be the as entryIndex.
169
+ result[this.templates[entryIndex]] = {
170
+ content: templateSource,
171
+ hash: childCompilation.hash,
172
+ entry: entries[entryIndex]
173
+ };
174
+ });
175
+ this.compilationEndedTimestamp = new Date().getTime();
176
+ resolve(result);
177
+ });
178
+ });
179
+
180
+ return this.compilationPromise;
181
+ }
182
+ }
183
+
184
+ /**
185
+ * The webpack child compilation will create files as a side effect.
186
+ * This function will extract them and clean them up so they won't be written to disk.
187
+ *
188
+ * Returns the source code of the compiled templates as string
189
+ *
190
+ * @returns Array<string>
191
+ */
192
+ function extractHelperFilesFromCompilation (mainCompilation, childCompilation, filename, childEntryChunks) {
193
+ const helperAssetNames = childEntryChunks.map((entryChunk, index) => {
194
+ return mainCompilation.mainTemplate.hooks.assetPath.call(filename, {
195
+ hash: childCompilation.hash,
196
+ chunk: entryChunk,
197
+ name: `HtmlWebpackPlugin_${index}`
198
+ });
115
199
  });
116
- };
200
+
201
+ helperAssetNames.forEach((helperFileName) => {
202
+ delete mainCompilation.assets[helperFileName];
203
+ });
204
+
205
+ const helperContents = helperAssetNames.map((helperFileName) => {
206
+ return childCompilation.assets[helperFileName].source();
207
+ });
208
+
209
+ return helperContents;
210
+ }
211
+
212
+ /**
213
+ * Return all file dependencies from the given set of entries.
214
+ * @param {WebpackChunk[]} entries
215
+ * @returns {string[]}
216
+ */
217
+ function extractFileDependenciesFilesFromCompilation (entries) {
218
+ const fileDependencies = new Map();
219
+ entries.forEach((entry) => {
220
+ entry.entryModule.buildInfo.fileDependencies.forEach((fileDependency) => {
221
+ fileDependencies.set(fileDependency, true);
222
+ });
223
+ });
224
+ return Array.from(fileDependencies.keys());
225
+ }
226
+
227
+ /**
228
+ * @type {WeakMap<WebpackCompiler, HtmlWebpackChildCompiler>}}
229
+ */
230
+ const childCompilerCache = new WeakMap();
231
+
232
+ /**
233
+ * Get child compiler from cache or a new child compiler for the given mainCompilation
234
+ *
235
+ * @param {WebpackCompiler} mainCompiler
236
+ */
237
+ function getChildCompiler (mainCompiler) {
238
+ const cachedChildCompiler = childCompilerCache.get(mainCompiler);
239
+ if (cachedChildCompiler) {
240
+ return cachedChildCompiler;
241
+ }
242
+ const newCompiler = new HtmlWebpackChildCompiler();
243
+ childCompilerCache.set(mainCompiler, newCompiler);
244
+ return newCompiler;
245
+ }
246
+
247
+ /**
248
+ * Remove the childCompiler from the cache
249
+ *
250
+ * @param {WebpackCompiler} mainCompiler
251
+ */
252
+ function clearCache (mainCompiler) {
253
+ const childCompiler = getChildCompiler(mainCompiler);
254
+ // If this childCompiler was already used
255
+ // remove the entire childCompiler from the cache
256
+ if (childCompiler.isCompiling() || childCompiler.didCompile()) {
257
+ childCompilerCache.delete(mainCompiler);
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Register a template for the current main compiler
263
+ * @param {WebpackCompiler} mainCompiler
264
+ * @param {string} templatePath
265
+ */
266
+ function addTemplateToCompiler (mainCompiler, templatePath) {
267
+ const childCompiler = getChildCompiler(mainCompiler);
268
+ const isNew = childCompiler.addTemplate(templatePath);
269
+ if (isNew) {
270
+ clearCache(mainCompiler);
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Starts the compilation for all templates.
276
+ * This has to be called once all templates where added.
277
+ *
278
+ * If this function is called multiple times it will use a cache inside
279
+ * the childCompiler
280
+ *
281
+ * @param {string} templatePath
282
+ * @param {string} outputFilename
283
+ * @param {WebpackCompilation} mainCompilation
284
+ */
285
+ function compileTemplate (templatePath, outputFilename, mainCompilation) {
286
+ const childCompiler = getChildCompiler(mainCompilation.compiler);
287
+ return childCompiler.compileTemplates(mainCompilation).then((compiledTemplates) => {
288
+ if (!compiledTemplates[templatePath]) console.log(Object.keys(compiledTemplates), templatePath);
289
+ const compiledTemplate = compiledTemplates[templatePath];
290
+ // Replace [hash] placeholders in filename
291
+ const outputName = mainCompilation.mainTemplate.hooks.assetPath.call(outputFilename, {
292
+ hash: compiledTemplate.hash,
293
+ chunk: compiledTemplate.entry
294
+ });
295
+ return {
296
+ // Hash of the template entry point
297
+ hash: compiledTemplate.hash,
298
+ // Output name
299
+ outputName: outputName,
300
+ // Compiled code
301
+ content: compiledTemplate.content
302
+ };
303
+ });
304
+ }
305
+
306
+ /**
307
+ * Return all file dependencies of the last child compilation
308
+ *
309
+ * @param {WebpackCompiler} compiler
310
+ * @returns {Array<string>}
311
+ */
312
+ function getFileDependencies (compiler) {
313
+ const childCompiler = getChildCompiler(compiler);
314
+ return childCompiler.fileDependencies;
315
+ }
316
+
317
+ /**
318
+ * @type {WeakMap<WebpackCompilation, WeakMap<HtmlWebpackChildCompiler, boolean>>}}
319
+ */
320
+ const hasOutdatedCompilationDependenciesMap = new WeakMap();
321
+ /**
322
+ * Returns `true` if the file dependencies of the current childCompiler
323
+ * for the given mainCompilation are outdated.
324
+ *
325
+ * Uses the `hasOutdatedCompilationDependenciesMap` cache if possible.
326
+ *
327
+ * @param {WebpackCompilation} mainCompilation
328
+ * @returns {boolean}
329
+ */
330
+ function hasOutDatedTemplateCache (mainCompilation) {
331
+ const childCompiler = getChildCompiler(mainCompilation.compiler);
332
+ /**
333
+ * @type {WeakMap<HtmlWebpackChildCompiler, boolean>|undefined}
334
+ */
335
+ let hasOutdatedChildCompilerDependenciesMap = hasOutdatedCompilationDependenciesMap.get(mainCompilation);
336
+ // Create map for childCompiler if none exist
337
+ if (!hasOutdatedChildCompilerDependenciesMap) {
338
+ hasOutdatedChildCompilerDependenciesMap = new WeakMap();
339
+ hasOutdatedCompilationDependenciesMap.set(mainCompilation, hasOutdatedChildCompilerDependenciesMap);
340
+ }
341
+ // Try to get the `checkChildCompilerCache` result from cache
342
+ let isOutdated = hasOutdatedChildCompilerDependenciesMap.get(childCompiler);
343
+ if (isOutdated !== undefined) {
344
+ return isOutdated;
345
+ }
346
+ // If `checkChildCompilerCache` has never been called for the given
347
+ // `mainCompilation` and `childCompiler` combination call it:
348
+ isOutdated = isChildCompilerCacheOutdated(mainCompilation, childCompiler);
349
+ hasOutdatedChildCompilerDependenciesMap.set(childCompiler, isOutdated);
350
+ return isOutdated;
351
+ }
117
352
 
118
353
  /**
119
- * Returns the child compiler name e.g. 'html-webpack-plugin for "index.html"'
354
+ * Returns `true` if the file dependencies of the given childCompiler are outdated.
355
+ *
356
+ * @param {WebpackCompilation} mainCompilation
357
+ * @param {HtmlWebpackChildCompiler} childCompiler
358
+ * @returns {boolean}
120
359
  */
121
- function getCompilerName (context, filename) {
122
- const absolutePath = path.resolve(context, filename);
123
- const relativePath = path.relative(context, absolutePath);
124
- return 'html-webpack-plugin for "' + (absolutePath.length < relativePath.length ? absolutePath : relativePath) + '"';
360
+ function isChildCompilerCacheOutdated (mainCompilation, childCompiler) {
361
+ // If the compilation was never run there is no invalid cache
362
+ if (!childCompiler.compilationStartedTimestamp) {
363
+ return false;
364
+ }
365
+ // Check if any dependent file was changed after the last compilation
366
+ const fileTimestamps = mainCompilation.fileTimestamps;
367
+ const isCacheOutOfDate = childCompiler.fileDependencies.some((fileDependency) => {
368
+ const timestamp = fileTimestamps.get(fileDependency);
369
+ // If the timestamp is not known the file is new
370
+ // If the timestamp is larger then the file has changed
371
+ // Otherwise the file is still the same
372
+ return !timestamp || timestamp > childCompiler.compilationStartedTimestamp;
373
+ });
374
+ return isCacheOutOfDate;
125
375
  }
376
+
377
+ module.exports = {
378
+ addTemplateToCompiler,
379
+ compileTemplate,
380
+ hasOutDatedTemplateCache,
381
+ clearCache,
382
+ getFileDependencies
383
+ };
package/lib/errors.js CHANGED
@@ -1,8 +1,9 @@
1
+ // @ts-nocheck
1
2
  'use strict';
2
3
  const PrettyError = require('pretty-error');
3
4
  const prettyError = new PrettyError();
4
5
  prettyError.withoutColors();
5
- prettyError.skipPackage(['html-plugin-evaluation']);
6
+ prettyError.skipPackage('html-plugin-evaluation');
6
7
  prettyError.skipNodeFiles();
7
8
  prettyError.skip(function (traceLine) {
8
9
  return traceLine.path === 'html-plugin-evaluation';