Skip to content
This repository was archived by the owner on Jan 21, 2021. It is now read-only.

Commit 4ecc631

Browse files
committed
Getting closer.
1 parent 1dd7eb9 commit 4ecc631

File tree

7 files changed

+131
-52
lines changed

7 files changed

+131
-52
lines changed

package-lock.json

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"eslint-config-google": "^0.9.1",
4343
"jasmine": "^3.1.0",
4444
"jsdom": "^11.6.2",
45+
"memory-fs": "^0.4.1",
4546
"rimraf": "^2.6.2"
4647
},
4748
"peerDependencies": {

src/index.js

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
* limitations under the License.
1616
*/
1717

18+
const assert = require('assert');
19+
1820
const createHTMLElementString = require('./lib/create-html-element-string');
1921
const defaultOptions = require('./lib/default-options');
2022
const determineAsValue = require('./lib/determine-as-value');
@@ -27,26 +29,44 @@ class PreloadPlugin {
2729
this.options = Object.assign({}, defaultOptions, options);
2830
}
2931

30-
addLinks(compilation, htmlPluginData) {
31-
const options = this.options;
32-
33-
const extractedChunks = extractChunks(compilation, options.include);
32+
addLinks(webpackVersion, compilation, htmlPluginData) {
33+
assert(webpackVersion in doesChunkBelongToHTML,
34+
`An invalid webpackVersion was supplied. Supported values: ${Object.keys(doesChunkBelongToHTML)}.`);
3435

35-
const publicPath = compilation.outputOptions.publicPath || '';
36+
const options = this.options;
3637

37-
// Only handle chunks imported by this HtmlWebpackPlugin.
38-
const htmlChunks = extractedChunks.filter(
39-
(chunk) => doesChunkBelongToHTML(chunk, Object.values(htmlPluginData.assets.chunks), {}));
38+
// Bail out early if we're configured to exclude this HTML file.
39+
if (options.excludeHtmlNames.includes(htmlPluginData.plugin.options.filename)) {
40+
return htmlPluginData;
41+
}
4042

43+
const extractedChunks = extractChunks({
44+
compilation,
45+
optionsInclude: options.include,
46+
});
47+
48+
const htmlChunks = options.include === 'allAssets' ?
49+
// Handle all chunks.
50+
extractedChunks :
51+
// Only handle chunks imported by this HtmlWebpackPlugin.
52+
extractedChunks.filter((chunk) => doesChunkBelongToHTML[webpackVersion]({
53+
chunk,
54+
compilation,
55+
htmlAssetsChunks: Object.values(htmlPluginData.assets.chunks),
56+
}));
57+
58+
// Flatten the list of files.
4159
const allFiles = htmlChunks.reduce((accumulated, chunk) => {
4260
return accumulated.concat(chunk.files);
4361
}, []);
4462
const uniqueFiles = new Set(allFiles);
4563
const filteredFiles = [...uniqueFiles].filter(
4664
(file) => this.options.fileBlacklist.every(regex => !regex.test(file)));
65+
// Sort to ensure the output is predictable.
4766
const sortedFilteredFiles = filteredFiles.sort();
4867

4968
const links = [];
69+
const publicPath = compilation.outputOptions.publicPath || '';
5070
for (const file of sortedFilteredFiles) {
5171
const href = `${publicPath}${file}`;
5272

@@ -86,7 +106,7 @@ class PreloadPlugin {
86106
this.constructor.name,
87107
(htmlPluginData, callback) => {
88108
try {
89-
callback(null, this.addLinks(compilation, htmlPluginData));
109+
callback(null, this.addLinks('v4', compilation, htmlPluginData));
90110
} catch (error) {
91111
callback(error);
92112
}
@@ -98,7 +118,7 @@ class PreloadPlugin {
98118
compiler.plugin('compilation', (compilation) => {
99119
compilation.plugin('html-webpack-plugin-before-html-processing', (htmlPluginData, callback) => {
100120
try {
101-
callback(null, this.addLinks(compilation, htmlPluginData));
121+
callback(null, this.addLinks('v3', compilation, htmlPluginData));
102122
} catch (error) {
103123
callback(error);
104124
}

src/lib/default-options.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
const defaultOptions = {
1919
rel: 'preload',
2020
include: 'asyncChunks',
21+
excludeHtmlNames: [],
2122
fileBlacklist: [/\.map/]
2223
};
2324

src/lib/does-chunk-belong-to-html.js

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,41 @@
1515
* limitations under the License.
1616
*/
1717

18-
function doesChunkBelongToHTML(chunk, roots, visitedChunks) {
18+
function v3({chunk, htmlAssetsChunks, visitedChunks = {}}) {
1919
// Prevent circular recursion.
2020
// See https://github.com/GoogleChromeLabs/preload-webpack-plugin/issues/49
2121
if (visitedChunks[chunk.renderedHash]) {
2222
return false;
2323
}
2424
visitedChunks[chunk.renderedHash] = true;
2525

26-
for (const root of roots) {
27-
if (root.hash === chunk.renderedHash) {
26+
for (const htmlAssetChunk of htmlAssetsChunks) {
27+
if (htmlAssetChunk.hash === chunk.renderedHash) {
2828
return true;
2929
}
3030
}
3131

32-
const parents = ('_groups' in chunk) ?
33-
chunk.getParents() :
34-
chunk.parents;
35-
36-
for (const parent of parents) {
37-
if (doesChunkBelongToHTML(parent, roots, visitedChunks)) {
32+
for (const parent of chunk.parents) {
33+
if (v3({chunk: parent, htmlAssetsChunks, visitedChunks})) {
3834
return true;
3935
}
4036
}
4137

4238
return false;
4339
}
4440

45-
module.exports = doesChunkBelongToHTML;
41+
function v4({chunk, htmlAssetsChunks, compilation}) {
42+
// Get all the hashes of the HTML assets.
43+
const rootHashes = Object.values(htmlAssetsChunks).map(({hash}) => hash);
44+
// Get a list of chunk groups that contain one of those hashes.
45+
const rootChunkGroups = compilation.chunkGroups.filter((chunkGroup) => {
46+
return chunkGroup.chunks.filter((chunk) => rootHashes.includes(chunk.renderedHash));
47+
});
48+
// Get an id for each of those chunk groups.
49+
const rootChunkGroupsIds = new Set(rootChunkGroups.map(({id}) => id));
50+
// Return true iff the chunk we're passed belongs to a group whose id is in
51+
// the list of root chunk groups.
52+
return Array.from(chunk.groupsIterable).some(({id}) => rootChunkGroupsIds.has(id));
53+
}
54+
55+
module.exports = {v3, v4};

src/lib/extract-chunks.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,30 @@
1515
* limitations under the License.
1616
*/
1717

18-
function extractChunks(compilation, optionsInclude) {
18+
function extractChunks({compilation, optionsInclude}) {
1919
try {
2020
// 'asyncChunks' are chunks intended for lazy/async loading usually generated as
2121
// part of code-splitting with import() or require.ensure(). By default, asyncChunks
2222
// get wired up using link rel=preload when using this plugin. This behaviour can be
2323
// configured to preload all types of chunks or just prefetch chunks as needed.
2424
if (optionsInclude === undefined || optionsInclude === 'asyncChunks') {
25-
return compilation.chunks.filter(chunk => !chunk.isInitial());
25+
return compilation.chunks.filter(chunk => {
26+
if ('canBeInitial' in chunk) {
27+
return !chunk.canBeInitial();
28+
} else {
29+
return !chunk.isInitial();
30+
}
31+
});
2632
}
2733

2834
if (optionsInclude === 'initial') {
29-
return compilation.chunks.filter(chunk => chunk.isInitial());
35+
return compilation.chunks.filter(chunk => {
36+
if ('canBeInitial' in chunk) {
37+
return chunk.canBeInitial();
38+
} else {
39+
return chunk.isInitial();
40+
}
41+
});
3042
}
3143

3244
if (optionsInclude === 'all') {
@@ -36,9 +48,7 @@ function extractChunks(compilation, optionsInclude) {
3648

3749
if (Array.isArray(optionsInclude)) {
3850
// Keep only user specified chunks.
39-
return compilation.chunks.filter((chunk) => {
40-
return chunk.name && optionsInclude.includes(chunk.name);
41-
});
51+
return compilation.chunks.filter((chunk) => chunk.name && optionsInclude.includes(chunk.name));
4252
}
4353
} catch (error) {
4454
return compilation.chunks;

0 commit comments

Comments
 (0)