- Complete React app with 7 developer tools - JSON Tool with visual structured editor - Serialize Tool with visual structured editor - URL, Base64, CSV/JSON, Beautifier, Diff tools - Responsive navigation with dropdown menu - Dark/light mode toggle - Mobile-responsive design with sticky header - All tools working with copy/paste functionality
253 lines
7.4 KiB
TypeScript
253 lines
7.4 KiB
TypeScript
/*
|
|
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.
|
|
*/
|
|
|
|
import {
|
|
Asset,
|
|
Chunk,
|
|
Compilation,
|
|
ModuleFilenameHelpers,
|
|
WebpackError,
|
|
} from 'webpack';
|
|
import {transformManifest} from 'workbox-build/build/lib/transform-manifest';
|
|
|
|
import {
|
|
WebpackGenerateSWOptions,
|
|
WebpackInjectManifestOptions,
|
|
ManifestEntry,
|
|
FileDetails,
|
|
} from 'workbox-build';
|
|
import {getAssetHash} from './get-asset-hash';
|
|
import {resolveWebpackURL} from './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: Asset,
|
|
compilation: Compilation,
|
|
|
|
conditions: Array<
|
|
//eslint-disable-next-line @typescript-eslint/ban-types
|
|
string | RegExp | ((arg0: any) => boolean)
|
|
> = [],
|
|
): boolean {
|
|
for (const condition of conditions) {
|
|
if (typeof condition === 'function') {
|
|
return condition({asset, compilation});
|
|
//return compilation !== null;
|
|
} else {
|
|
if (ModuleFilenameHelpers.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 all the chunks in a chunk group,
|
|
* if provided a chunk group name.
|
|
* Otherwise, if provided a chunk name, return all the assets in that chunk.
|
|
* Otherwise, if there isn't a chunk group or chunk with that name, return null.
|
|
*
|
|
* @param {Compilation} compilation
|
|
* @param {string} chunkOrGroup
|
|
* @return {Array<string>|null}
|
|
* @private
|
|
*/
|
|
function getNamesOfAssetsInChunkOrGroup(
|
|
compilation: Compilation,
|
|
chunkOrGroup: string,
|
|
): Array<string> | null {
|
|
const chunkGroup =
|
|
compilation.namedChunkGroups &&
|
|
compilation.namedChunkGroups.get(chunkOrGroup);
|
|
if (chunkGroup) {
|
|
const assetNames = [];
|
|
for (const chunk of chunkGroup.chunks) {
|
|
assetNames.push(...getNamesOfAssetsInChunk(chunk));
|
|
}
|
|
return assetNames;
|
|
} else {
|
|
const chunk =
|
|
compilation.namedChunks && compilation.namedChunks.get(chunkOrGroup);
|
|
if (chunk) {
|
|
return getNamesOfAssetsInChunk(chunk);
|
|
}
|
|
}
|
|
|
|
// If we get here, there's no chunkGroup or chunk with that name.
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Returns the names of all the assets in a chunk.
|
|
*
|
|
* @param {Chunk} chunk
|
|
* @return {Array<string>}
|
|
* @private
|
|
*/
|
|
function getNamesOfAssetsInChunk(chunk: Chunk): Array<string> {
|
|
const assetNames: Array<string> = [];
|
|
|
|
assetNames.push(...chunk.files);
|
|
|
|
// This only appears to be set in webpack v5.
|
|
if (chunk.auxiliaryFiles) {
|
|
assetNames.push(...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: Compilation,
|
|
config: WebpackInjectManifestOptions | WebpackGenerateSWOptions,
|
|
): Set<Asset> {
|
|
const filteredAssets = new Set<Asset>();
|
|
const assets = compilation.getAssets();
|
|
|
|
const allowedAssetNames = new Set<string>();
|
|
// See https://github.com/GoogleChrome/workbox/issues/1287
|
|
if (Array.isArray(config.chunks)) {
|
|
for (const name of config.chunks) {
|
|
// See https://github.com/GoogleChrome/workbox/issues/2717
|
|
const assetsInChunkOrGroup = getNamesOfAssetsInChunkOrGroup(
|
|
compilation,
|
|
name,
|
|
);
|
|
if (assetsInChunkOrGroup) {
|
|
for (const assetName of assetsInChunkOrGroup) {
|
|
allowedAssetNames.add(assetName);
|
|
}
|
|
} else {
|
|
compilation.warnings.push(
|
|
new Error(
|
|
`The chunk '${name}' was ` +
|
|
`provided in your Workbox chunks config, but was not found in the ` +
|
|
`compilation.`,
|
|
) as WebpackError,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
const deniedAssetNames = new Set();
|
|
if (Array.isArray(config.excludeChunks)) {
|
|
for (const name of config.excludeChunks) {
|
|
// See https://github.com/GoogleChrome/workbox/issues/2717
|
|
const assetsInChunkOrGroup = getNamesOfAssetsInChunkOrGroup(
|
|
compilation,
|
|
name,
|
|
);
|
|
if (assetsInChunkOrGroup) {
|
|
for (const assetName of assetsInChunkOrGroup) {
|
|
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;
|
|
}
|
|
|
|
export async function getManifestEntriesFromCompilation(
|
|
compilation: Compilation,
|
|
config: WebpackGenerateSWOptions | WebpackInjectManifestOptions,
|
|
): Promise<{size: number; sortedEntries: ManifestEntry[]}> {
|
|
const filteredAssets = filterAssets(compilation, config);
|
|
|
|
const {publicPath} = compilation.options.output;
|
|
|
|
const fileDetails = Array.from(filteredAssets).map((asset) => {
|
|
return {
|
|
file: resolveWebpackURL(publicPath as string, asset.name),
|
|
hash: getAssetHash(asset),
|
|
size: asset.source.size() || 0,
|
|
} as FileDetails;
|
|
});
|
|
|
|
const {manifestEntries, size, warnings} = await transformManifest({
|
|
fileDetails,
|
|
additionalManifestEntries: config.additionalManifestEntries,
|
|
dontCacheBustURLsMatching: config.dontCacheBustURLsMatching,
|
|
manifestTransforms: config.manifestTransforms,
|
|
maximumFileSizeToCacheInBytes: config.maximumFileSizeToCacheInBytes,
|
|
modifyURLPrefix: config.modifyURLPrefix,
|
|
transformParam: compilation,
|
|
});
|
|
|
|
// See https://github.com/GoogleChrome/workbox/issues/2790
|
|
for (const warning of warnings) {
|
|
compilation.warnings.push(new Error(warning) as WebpackError);
|
|
}
|
|
|
|
// 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};
|
|
}
|