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

Commit 81ebc2a

Browse files
committed
[update] add webpack-v3 logic back
1 parent 2cff0b4 commit 81ebc2a

File tree

4 files changed

+795
-102
lines changed

4 files changed

+795
-102
lines changed

index.js

Lines changed: 211 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -87,123 +87,233 @@ class PreloadPlugin {
8787
}
8888

8989
apply(compiler) {
90+
if (compiler.hooks) {
91+
compiler.hooks['compilation'].tap(PLUGIN_NAME, this.hooksHandler.bind(this));
92+
} else {
93+
compiler.plugin('compilation', this.pluginHandler.bind(this));
94+
}
95+
}
96+
97+
// handler use for webpack v4
98+
hooksHandler(compilation) {
9099
const options = this.options;
91-
compiler.hooks['compilation'].tap(PLUGIN_NAME, (compilation) => {
92-
if (!compilation.hooks.htmlWebpackPluginAfterHtmlProcessing) {
93-
const message = `compilation.hooks.htmlWebpackPluginAfterHtmlProcessing is lost. Please make sure you have installed html-webpack-plugin and put it before ${PLUGIN_NAME}`;
94-
log.error(message);
95-
throw new Error(message);
100+
if (!compilation.hooks.htmlWebpackPluginAfterHtmlProcessing) {
101+
const message = `compilation.hooks.htmlWebpackPluginAfterHtmlProcessing is lost. Please make sure you have installed html-webpack-plugin and put it before ${PLUGIN_NAME}`;
102+
log.error(message);
103+
throw new Error(message);
104+
}
105+
compilation.hooks.htmlWebpackPluginAfterHtmlProcessing.tapAsync(PLUGIN_NAME, (htmlPluginData, cb) => {
106+
if (this.options.excludeHtmlNames.indexOf(htmlPluginData.plugin.options.filename) > -1) {
107+
cb(null, htmlPluginData);
108+
return;
96109
}
97-
compilation.hooks.htmlWebpackPluginAfterHtmlProcessing.tapAsync(PLUGIN_NAME, (htmlPluginData, cb) => {
98-
if (this.options.excludeHtmlNames.indexOf(htmlPluginData.plugin.options.filename) > -1) {
99-
cb(null, htmlPluginData);
100-
return;
110+
let filesToInclude = '';
111+
let extractedChunks = [];
112+
const initialChunkGroups = compilation.chunkGroups.filter(chunkGroup => chunkGroup.isInitial());
113+
const initialChunks = initialChunkGroups.reduce((initialChunks, {chunks}) => {
114+
return initialChunks.concat(chunks);
115+
}, []);
116+
// 'asyncChunks' are chunks intended for lazy/async loading usually generated as
117+
// part of code-splitting with import() or require.ensure(). By default, asyncChunks
118+
// get wired up using link rel=preload when using this plugin. This behaviour can be
119+
// configured to preload all types of chunks or just prefetch chunks as needed.
120+
if (options.include === undefined || options.include === 'asyncChunks') {
121+
extractedChunks = compilation.chunks.filter(chunk => {
122+
return initialChunks.indexOf(chunk) < 0;
123+
});
124+
} else if (options.include === 'initial') {
125+
extractedChunks = compilation.chunks.filter(chunk => {
126+
return initialChunks.indexOf(chunk) > -1;
127+
});
128+
} else if (options.include === 'allChunks' || options.include === 'all') {
129+
if (options.include === 'all') {
130+
/* eslint-disable no-console */
131+
console.warn('[WARNING]: { include: "all" } is deprecated, please use "allChunks" instead.');
132+
/* eslint-enable no-console */
101133
}
102-
let filesToInclude = '';
103-
let extractedChunks = [];
104-
const initialChunkGroups = compilation.chunkGroups.filter(chunkGroup => chunkGroup.isInitial());
105-
const initialChunks = initialChunkGroups.reduce((initialChunks, {chunks}) => {
106-
return initialChunks.concat(chunks);
107-
}, []);
108-
// 'asyncChunks' are chunks intended for lazy/async loading usually generated as
109-
// part of code-splitting with import() or require.ensure(). By default, asyncChunks
110-
// get wired up using link rel=preload when using this plugin. This behaviour can be
111-
// configured to preload all types of chunks or just prefetch chunks as needed.
112-
if (options.include === undefined || options.include === 'asyncChunks') {
113-
extractedChunks = compilation.chunks.filter(chunk => {
114-
return initialChunks.indexOf(chunk) < 0;
115-
});
116-
} else if (options.include === 'initial') {
117-
extractedChunks = compilation.chunks.filter(chunk => {
118-
return initialChunks.indexOf(chunk) > -1;
134+
// Async chunks, vendor chunks, normal chunks.
135+
extractedChunks = compilation.chunks;
136+
} else if (options.include === 'allAssets') {
137+
extractedChunks = [{files: Object.keys(compilation.assets)}];
138+
} else if (Array.isArray(options.include)) {
139+
// Keep only user specified chunks
140+
extractedChunks = compilation.chunks
141+
.filter((chunk) => {
142+
const chunkName = chunk.name;
143+
// Works only for named chunks
144+
if (!chunkName) {
145+
return false;
146+
}
147+
return options.include.indexOf(chunkName) > -1;
119148
});
120-
} else if (options.include === 'allChunks' || options.include === 'all') {
121-
if (options.include === 'all') {
122-
/* eslint-disable no-console */
123-
console.warn('[WARNING]: { include: "all" } is deprecated, please use "allChunks" instead.');
124-
/* eslint-enable no-console */
125-
}
126-
// Async chunks, vendor chunks, normal chunks.
127-
extractedChunks = compilation.chunks;
128-
} else if (options.include === 'allAssets') {
129-
extractedChunks = [{files: Object.keys(compilation.assets)}];
130-
} else if (Array.isArray(options.include)) {
131-
// Keep only user specified chunks
132-
extractedChunks = compilation.chunks
133-
.filter((chunk) => {
134-
const chunkName = chunk.name;
135-
// Works only for named chunks
136-
if (!chunkName) {
137-
return false;
138-
}
139-
return options.include.indexOf(chunkName) > -1;
140-
});
141-
}
149+
}
142150

143-
const publicPath = compilation.outputOptions.publicPath || '';
144-
145-
// only handle the chunks associated to this htmlWebpackPlugin instance, in case of multiple html plugin outputs
146-
// allow `allAssets` mode to skip, as assets are just files to be filtered by black/whitelist, not real chunks
147-
if (options.include !== 'allAssets') {
148-
extractedChunks = extractedChunks.filter(chunk => {
149-
const rootChunksHashs = Object.values(htmlPluginData.assets.chunks).map(({hash}) => hash);
150-
const rootChunkGroups = compilation.chunkGroups.reduce((groups, chunkGroup) => {
151-
const isRootChunkGroup = chunkGroup.chunks.reduce((flag, chunk) => {
152-
return flag ||
153-
rootChunksHashs.indexOf(chunk.renderedHash) > -1;
154-
}, false);
155-
if (isRootChunkGroup) groups.push(chunkGroup);
156-
return groups;
157-
}, []);
158-
return Array.from(chunk.groupsIterable).reduce((flag, chunkGroup) => {
151+
const publicPath = compilation.outputOptions.publicPath || '';
152+
153+
// only handle the chunks associated to this htmlWebpackPlugin instance, in case of multiple html plugin outputs
154+
// allow `allAssets` mode to skip, as assets are just files to be filtered by black/whitelist, not real chunks
155+
if (options.include !== 'allAssets') {
156+
extractedChunks = extractedChunks.filter(chunk => {
157+
const rootChunksHashs = Object.values(htmlPluginData.assets.chunks).map(({hash}) => hash);
158+
const rootChunkGroups = compilation.chunkGroups.reduce((groups, chunkGroup) => {
159+
const isRootChunkGroup = chunkGroup.chunks.reduce((flag, chunk) => {
159160
return flag ||
160-
doesChunkGroupBelongToHTML(chunkGroup, rootChunkGroups, {});
161+
rootChunksHashs.indexOf(chunk.renderedHash) > -1;
161162
}, false);
162-
});
163+
if (isRootChunkGroup) groups.push(chunkGroup);
164+
return groups;
165+
}, []);
166+
return Array.from(chunk.groupsIterable).reduce((flag, chunkGroup) => {
167+
return flag ||
168+
doesChunkGroupBelongToHTML(chunkGroup, rootChunkGroups, {});
169+
}, false);
170+
});
171+
}
172+
173+
flatten(extractedChunks.map(chunk => chunk.files))
174+
.filter(entry => {
175+
return (
176+
!this.options.fileWhitelist ||
177+
this.options.fileWhitelist.some(regex => regex.test(entry) === true)
178+
);
179+
})
180+
.filter(entry => {
181+
return this.options.fileBlacklist.every(regex => regex.test(entry) === false);
182+
}).forEach(entry => {
183+
entry = `${publicPath}${entry}`;
184+
if (options.rel === 'preload') {
185+
// If `as` value is not provided in option, dynamically determine the correct
186+
// value depends on suffix of filename. Otherwise use the given `as` value.
187+
let asValue;
188+
if (!options.as) {
189+
if (entry.match(/\.css$/)) asValue = 'style';
190+
else if (entry.match(/\.woff2$/)) asValue = 'font';
191+
else asValue = 'script';
192+
} else if (typeof options.as === 'function') {
193+
asValue = options.as(entry);
194+
} else {
195+
asValue = options.as;
196+
}
197+
const crossOrigin = asValue === 'font' ? 'crossorigin="crossorigin" ' : '';
198+
filesToInclude+= `<link rel="${options.rel}" as="${asValue}" ${crossOrigin}href="${entry}">\n`;
199+
} else {
200+
// If preload isn't specified, the only other valid entry is prefetch here
201+
// You could specify preconnect but as we're dealing with direct paths to resources
202+
// instead of origins that would make less sense.
203+
filesToInclude+= `<link rel="${options.rel}" href="${entry}">\n`;
204+
}
205+
});
206+
if (htmlPluginData.html.indexOf('</head>') !== -1) {
207+
// If a valid closing </head> is found, update it to include preload/prefetch tags
208+
htmlPluginData.html = htmlPluginData.html.replace('</head>', filesToInclude + '</head>');
209+
} else {
210+
// Otherwise assume at least a <body> is present and update it to include a new <head>
211+
htmlPluginData.html = htmlPluginData.html.replace('<body>', '<head>' + filesToInclude + '</head><body>');
212+
}
213+
cb(null, htmlPluginData);
214+
});
215+
}
216+
217+
// handler use before webpack v4
218+
pluginHandler(compilation) {
219+
const options = this.options;
220+
compilation.plugin('html-webpack-plugin-before-html-processing', (htmlPluginData, cb) => {
221+
if (this.options.excludeHtmlNames.indexOf(htmlPluginData.plugin.options.filename) > -1) {
222+
cb(null, htmlPluginData);
223+
return;
224+
}
225+
let filesToInclude = '';
226+
let extractedChunks = [];
227+
// 'asyncChunks' are chunks intended for lazy/async loading usually generated as
228+
// part of code-splitting with import() or require.ensure(). By default, asyncChunks
229+
// get wired up using link rel=preload when using this plugin. This behaviour can be
230+
// configured to preload all types of chunks or just prefetch chunks as needed.
231+
if (options.include === undefined || options.include === 'asyncChunks') {
232+
try {
233+
extractedChunks = compilation.chunks.filter(chunk => !chunk.isInitial());
234+
} catch (e) {
235+
extractedChunks = compilation.chunks;
163236
}
237+
} else if (options.include === 'initial') {
238+
try {
239+
extractedChunks = compilation.chunks.filter(chunk => chunk.isInitial());
240+
} catch (e) {
241+
extractedChunks = compilation.chunks;
242+
}
243+
} else if (options.include === 'allChunks' || options.include === 'all') {
244+
if (options.include === 'all') {
245+
/* eslint-disable no-console */
246+
console.warn('[WARNING]: { include: "all" } is deprecated, please use "allChunks" instead.');
247+
/* eslint-enable no-console */
248+
}
249+
// Async chunks, vendor chunks, normal chunks.
250+
extractedChunks = compilation.chunks;
251+
} else if (options.include === 'allAssets') {
252+
extractedChunks = [{files: Object.keys(compilation.assets)}];
253+
} else if (Array.isArray(options.include)) {
254+
// Keep only user specified chunks
255+
extractedChunks = compilation
256+
.chunks
257+
.filter((chunk) => {
258+
const chunkName = chunk.name;
259+
// Works only for named chunks
260+
if (!chunkName) {
261+
return false;
262+
}
263+
return options.include.indexOf(chunkName) > -1;
264+
});
265+
}
164266

165-
flatten(extractedChunks.map(chunk => chunk.files))
166-
.filter(entry => {
167-
return (
168-
!this.options.fileWhitelist ||
267+
const publicPath = compilation.outputOptions.publicPath || '';
268+
269+
// only handle the chunks associated to this htmlWebpackPlugin instance, in case of multiple html plugin outputs
270+
// allow `allAssets` mode to skip, as assets are just files to be filtered by black/whitelist, not real chunks
271+
if (options.include !== 'allAssets') {
272+
extractedChunks = extractedChunks.filter(chunk => doesChunkBelongToHTML(
273+
chunk, Object.values(htmlPluginData.assets.chunks), {}));
274+
}
275+
276+
flatten(extractedChunks.map(chunk => chunk.files))
277+
.filter(entry => {
278+
return (
279+
!this.options.fileWhitelist ||
169280
this.options.fileWhitelist.some(regex => regex.test(entry) === true)
170-
);
171-
})
172-
.filter(entry => {
173-
return this.options.fileBlacklist.every(regex => regex.test(entry) === false);
174-
}).forEach(entry => {
175-
entry = `${publicPath}${entry}`;
176-
if (options.rel === 'preload') {
281+
);
282+
})
283+
.filter(entry => {
284+
return this.options.fileBlacklist.every(regex => regex.test(entry) === false);
285+
}).forEach(entry => {
286+
entry = `${publicPath}${entry}`;
287+
if (options.rel === 'preload') {
177288
// If `as` value is not provided in option, dynamically determine the correct
178289
// value depends on suffix of filename. Otherwise use the given `as` value.
179-
let asValue;
180-
if (!options.as) {
181-
if (entry.match(/\.css$/)) asValue = 'style';
182-
else if (entry.match(/\.woff2$/)) asValue = 'font';
183-
else asValue = 'script';
184-
} else if (typeof options.as === 'function') {
185-
asValue = options.as(entry);
186-
} else {
187-
asValue = options.as;
188-
}
189-
const crossOrigin = asValue === 'font' ? 'crossorigin="crossorigin" ' : '';
190-
filesToInclude+= `<link rel="${options.rel}" as="${asValue}" ${crossOrigin}href="${entry}">\n`;
290+
let asValue;
291+
if (!options.as) {
292+
if (entry.match(/\.css$/)) asValue = 'style';
293+
else if (entry.match(/\.woff2$/)) asValue = 'font';
294+
else asValue = 'script';
295+
} else if (typeof options.as === 'function') {
296+
asValue = options.as(entry);
191297
} else {
298+
asValue = options.as;
299+
}
300+
const crossOrigin = asValue === 'font' ? 'crossorigin="crossorigin" ' : '';
301+
filesToInclude+= `<link rel="${options.rel}" as="${asValue}" ${crossOrigin}href="${entry}">\n`;
302+
} else {
192303
// If preload isn't specified, the only other valid entry is prefetch here
193304
// You could specify preconnect but as we're dealing with direct paths to resources
194305
// instead of origins that would make less sense.
195-
filesToInclude+= `<link rel="${options.rel}" href="${entry}">\n`;
196-
}
197-
});
198-
if (htmlPluginData.html.indexOf('</head>') !== -1) {
199-
// If a valid closing </head> is found, update it to include preload/prefetch tags
200-
htmlPluginData.html = htmlPluginData.html.replace('</head>', filesToInclude + '</head>');
201-
} else {
202-
// Otherwise assume at least a <body> is present and update it to include a new <head>
203-
htmlPluginData.html = htmlPluginData.html.replace('<body>', '<head>' + filesToInclude + '</head><body>');
204-
}
205-
cb(null, htmlPluginData);
206-
});
306+
filesToInclude+= `<link rel="${options.rel}" href="${entry}">\n`;
307+
}
308+
});
309+
if (htmlPluginData.html.indexOf('</head>') !== -1) {
310+
// If a valid closing </head> is found, update it to include preload/prefetch tags
311+
htmlPluginData.html = htmlPluginData.html.replace('</head>', filesToInclude + '</head>');
312+
} else {
313+
// Otherwise assume at least a <body> is present and update it to include a new <head>
314+
htmlPluginData.html = htmlPluginData.html.replace('<body>', '<head>' + filesToInclude + '</head><body>');
315+
}
316+
cb(null, htmlPluginData);
207317
});
208318
}
209319
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"scripts": {
77
"lint": "eslint --quiet -f codeframe index.js test/spec.js",
88
"lint-fix": "eslint --fix --quiet -f codeframe index.js test/spec.js",
9+
"webpack-v3-t": "node_modules/jasmine/bin/jasmine.js test/webpack-v3.spec.js",
910
"test": "node_modules/jasmine/bin/jasmine.js test/spec.js"
1011
},
1112
"files": [
@@ -36,7 +37,7 @@
3637
"eslint-config-google": "^0.9.1",
3738
"extract-text-webpack-plugin": "^4.0.0-beta.0",
3839
"file-loader": "^1.1.11",
39-
"html-webpack-plugin": "^3.0.4",
40+
"html-webpack-plugin": "^3.0.6",
4041
"jasmine": "^3.1.0",
4142
"webpack": "^4.1.0"
4243
},

0 commit comments

Comments
 (0)