-
Notifications
You must be signed in to change notification settings - Fork 836
/
Copy pathget-manifest-entries-from-compilation.js
179 lines (156 loc) · 5.74 KB
/
get-manifest-entries-from-compilation.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const {matchPart} = require('webpack').ModuleFilenameHelpers;
const transformManifest = require('workbox-build/build/lib/transform-manifest');
const getAssetHash = require('./get-asset-hash');
const resolveWebpackURL = require('./resolve-webpack-url');
/**
* For a given asset, checks whether at least one of the conditions matches.
*
* @param {Asset} asset The webpack asset in question. This will be passed
* to any functions that are listed as conditions.
* @param {Compilation} compilation The webpack compilation. This will be passed
* to any functions that are listed as conditions.
* @param {Array<string|RegExp|Function>} conditions
* @return {boolean} Whether or not at least one condition matches.
* @private
*/
function checkConditions(asset, compilation, conditions = []) {
for (const condition of conditions) {
if (typeof condition === 'function') {
if (condition({asset, compilation})) {
return true;
}
} else {
if (matchPart(asset.name, condition)) {
return true;
}
}
}
// We'll only get here if none of the conditions applied.
return false;
}
/**
* Returns the names of all the assets in a chunk group.
*
* @param {ChunkGroup} chunkGroup
* @return {Array<Asset>}
* @private
*/
function getNamesOfAssetsInChunkGroup(chunkGroup) {
const assetNames = [];
for (const chunk of chunkGroup.chunks) {
assetNames.splice(0, 0, ...chunk.files);
// This only appears to be set in webpack v5.
if (chunk.auxiliaryFiles) {
assetNames.splice(0, 0, ...chunk.auxiliaryFiles);
}
}
return assetNames;
}
/**
* Filters the set of assets out, based on the configuration options provided:
* - chunks and excludeChunks, for chunkName-based criteria.
* - include and exclude, for more general criteria.
*
* @param {Compilation} compilation The webpack compilation.
* @param {Object} config The validated configuration, obtained from the plugin.
* @return {Set<Asset>} The assets that should be included in the manifest,
* based on the criteria provided.
* @private
*/
function filterAssets(compilation, config) {
const filteredAssets = new Set();
const assets = compilation.getAssets();
const allowedAssetNames = new Set();
// See https://github.com/GoogleChrome/workbox/issues/1287
if (Array.isArray(config.chunks)) {
for (const chunkName of config.chunks) {
const namedChunkGroup = compilation.namedChunkGroups.get(chunkName);
if (namedChunkGroup) {
for (const assetName of getNamesOfAssetsInChunkGroup(namedChunkGroup)) {
allowedAssetNames.add(assetName);
}
} else {
compilation.warnings.push(`The chunk '${chunkName}' was ` +
`provided in your Workbox chunks config, but was not found in the ` +
`compilation.`);
}
}
}
const deniedAssetNames = new Set();
if (Array.isArray(config.excludeChunks)) {
for (const chunkName of config.excludeChunks) {
const namedChunkGroup = compilation.namedChunkGroups.get(chunkName);
if (namedChunkGroup) {
for (const assetName of getNamesOfAssetsInChunkGroup(namedChunkGroup)) {
deniedAssetNames.add(assetName);
}
} // Don't warn if the chunk group isn't found.
}
}
for (const asset of assets) {
// chunk based filtering is funky because:
// - Each asset might belong to one or more chunks.
// - If *any* of those chunk names match our config.excludeChunks,
// then we skip that asset.
// - If the config.chunks is defined *and* there's no match
// between at least one of the chunkNames and one entry, then
// we skip that assets as well.
if (deniedAssetNames.has(asset.name)) {
continue;
}
if (Array.isArray(config.chunks) && !allowedAssetNames.has(asset.name)) {
continue;
}
// Next, check asset-level checks via includes/excludes:
const isExcluded = checkConditions(asset, compilation, config.exclude);
if (isExcluded) {
continue;
}
// Treat an empty config.includes as an implicit inclusion.
const isIncluded = !Array.isArray(config.include) ||
checkConditions(asset, compilation, config.include);
if (!isIncluded) {
continue;
}
// If we've gotten this far, then add the asset.
filteredAssets.add(asset);
}
return filteredAssets;
}
module.exports = async (mainCompilation, config) => {
const filteredAssets = new Set();
// children will be set for, e.g., html-webpack-plugin.
for (const compilation of [mainCompilation, ...mainCompilation.children]) {
for (const asset of filterAssets(compilation, config)) {
filteredAssets.add(asset);
}
}
const {publicPath} = mainCompilation.options.output;
const fileDetails = Array.from(filteredAssets).map((asset) => {
return {
file: resolveWebpackURL(publicPath, asset.name),
hash: getAssetHash(asset),
size: asset.source.size() || 0,
};
});
const {manifestEntries, size, warnings} = await transformManifest({
fileDetails,
additionalManifestEntries: config.additionalManifestEntries,
dontCacheBustURLsMatching: config.dontCacheBustURLsMatching,
manifestTransforms: config.manifestTransforms,
maximumFileSizeToCacheInBytes: config.maximumFileSizeToCacheInBytes,
modifyURLPrefix: config.modifyURLPrefix,
transformParam: mainCompilation,
});
mainCompilation.warnings = mainCompilation.warnings.concat(warnings || []);
// Ensure that the entries are properly sorted by URL.
const sortedEntries = manifestEntries.sort(
(a, b) => a.url === b.url ? 0 : (a.url > b.url ? 1 : -1));
return {size, sortedEntries};
};