Skip to content

Commit f31f853

Browse files
clydinalan-agius4
authored andcommitted
refactor(@angular-devkit/build-angular): use Webpack ChunkGraph API in SuppressExtractedTextChunksWebpackPlugin
1 parent ed1b109 commit f31f853

File tree

1 file changed

+71
-47
lines changed

1 file changed

+71
-47
lines changed

packages/angular_devkit/build_angular/src/angular-cli-files/plugins/suppress-entry-chunks-webpack-plugin.ts

Lines changed: 71 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,58 +5,82 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
// tslint:disable
9-
// TODO: cleanup this file, it's copied as is from Angular CLI.
10-
11-
// Remove .js files from entry points consisting entirely of .css|scss|sass|less|styl.
12-
// To be used together with ExtractTextPlugin.
138

9+
/**
10+
* Remove .js files from entry points consisting entirely of stylesheets.
11+
* To be used together with mini-css-extract-plugin.
12+
*/
1413
export class SuppressExtractedTextChunksWebpackPlugin {
15-
constructor() { }
16-
17-
apply(compiler: any): void {
18-
compiler.hooks.compilation.tap('SuppressExtractedTextChunks', (compilation: any) => {
19-
// find which chunks have css only entry points
20-
const cssOnlyChunks: string[] = [];
21-
const entryPoints = compilation.options.entry;
22-
// determine which entry points are composed entirely of css files
23-
for (let entryPoint of Object.keys(entryPoints)) {
24-
let entryFiles: string[] | string = entryPoints[entryPoint];
25-
// when type of entryFiles is not array, make it as an array
26-
entryFiles = entryFiles instanceof Array ? entryFiles : [entryFiles];
27-
if (entryFiles.every((el: string) =>
28-
el.match(/\.(css|scss|sass|less|styl)$/) !== null)) {
29-
cssOnlyChunks.push(entryPoint);
14+
apply(compiler: import('webpack').Compiler): void {
15+
compiler.hooks.compilation.tap('SuppressExtractedTextChunks', (compilation) => {
16+
compilation.hooks.chunkAsset.tap('SuppressExtractedTextChunks', (chunk, filename) => {
17+
// Remove only JavaScript assets
18+
if (!filename.endsWith('.js')) {
19+
return;
20+
}
21+
22+
// Only chunks with a css asset should have JavaScript assets removed
23+
let hasCssFile = false;
24+
// chunk.files is an Array in Webpack 4 and a Set in Webpack 5
25+
for (const file of chunk.files) {
26+
if (file.endsWith('.css')) {
27+
hasCssFile = true;
28+
break;
29+
}
30+
}
31+
32+
if (!hasCssFile) {
33+
return;
3034
}
31-
}
32-
// Remove the js file for supressed chunks
33-
compilation.hooks.afterSeal.tap('SuppressExtractedTextChunks', () => {
34-
compilation.chunks
35-
.filter((chunk: any) => cssOnlyChunks.indexOf(chunk.name) !== -1)
36-
.forEach((chunk: any) => {
37-
let newFiles: string[] = [];
38-
chunk.files.forEach((file: string) => {
39-
if (file.match(/\.js(\.map)?$/)) {
40-
// remove js files
41-
delete compilation.assets[file];
42-
} else {
43-
newFiles.push(file);
44-
}
35+
36+
// Only chunks with all CSS entry dependencies should have JavaScript assets removed
37+
let cssOnly = false;
38+
// The any cast is used for default Webpack 4 type compatibility
39+
// tslint:disable-next-line: no-any
40+
const entryModules = (compilation as any).chunkGraph?.getChunkEntryModulesIterable(chunk);
41+
if (entryModules) {
42+
// Webpack 5
43+
for (const module of entryModules) {
44+
cssOnly = module.dependencies.every(
45+
(dependency: {}) => dependency.constructor.name === 'CssDependency',
46+
);
47+
48+
if (!cssOnly) {
49+
break;
50+
}
51+
}
52+
} else {
53+
// Webpack 4
54+
for (const module of chunk.modulesIterable as Iterable<{ dependencies: {}[] }>) {
55+
cssOnly = module.dependencies.every((dependency) => {
56+
const name = dependency.constructor.name;
57+
58+
return (
59+
name === 'CssDependency' ||
60+
name === 'SingleEntryDependency' ||
61+
name === 'MultiEntryDependency'
62+
);
4563
});
46-
chunk.files = newFiles;
47-
});
64+
65+
if (!cssOnly) {
66+
break;
67+
}
68+
}
69+
}
70+
71+
if (cssOnly) {
72+
if (Array.isArray(chunk.files)) {
73+
// Webpack 4
74+
(chunk.files as string[]) = chunk.files.filter((file) => file !== filename);
75+
delete compilation.assets[filename];
76+
} else {
77+
// Webpack 5
78+
// Casting is used for default Webpack 4 type compatibility
79+
((chunk.files as unknown) as Set<string>).delete(filename);
80+
((compilation as unknown) as { deleteAsset(file: string): void }).deleteAsset(filename);
81+
}
82+
}
4883
});
49-
// Remove scripts tags with a css file as source, because HtmlWebpackPlugin will use
50-
// a css file as a script for chunks without js files.
51-
// TODO: Enable this once HtmlWebpackPlugin supports Webpack 4
52-
// compilation.plugin('html-webpack-plugin-alter-asset-tags',
53-
// (htmlPluginData: any, callback: any) => {
54-
// const filterFn = (tag: any) =>
55-
// !(tag.tagName === 'script' && tag.attributes.src.match(/\.css$/));
56-
// htmlPluginData.head = htmlPluginData.head.filter(filterFn);
57-
// htmlPluginData.body = htmlPluginData.body.filter(filterFn);
58-
// callback(null, htmlPluginData);
59-
// });
6084
});
6185
}
6286
}

0 commit comments

Comments
 (0)