123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- "use strict";
- /*
- 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;
- }
- /**
- * Creates a mapping of an asset name to an Set of zero or more chunk names
- * that the asset is associated with.
- *
- * Those chunk names come from a combination of the `chunkName` property on the
- * asset, as well as the `stats.namedChunkGroups` property. That is the only
- * way to find out if an asset has an implicit descendent relationship with a
- * chunk, if it was, e.g., created by `SplitChunksPlugin`.
- *
- * See https://github.com/GoogleChrome/workbox/issues/1859
- * See https://github.com/webpack/webpack/issues/7073
- *
- * @param {Object} stats The webpack compilation stats.
- * @return {object<string, Set<string>>}
- * @private
- */
- function assetToChunkNameMapping(stats) {
- const mapping = {};
- for (const asset of stats.assets) {
- mapping[asset.name] = new Set(asset.chunkNames);
- }
- for (const [chunkName, {
- assets
- }] of Object.entries(stats.namedChunkGroups)) {
- for (const assetName of assets) {
- // See https://github.com/GoogleChrome/workbox/issues/2194
- if (mapping[assetName]) {
- mapping[assetName].add(chunkName);
- }
- }
- }
- return mapping;
- }
- /**
- * 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(); // See https://webpack.js.org/configuration/stats/#stats
- // We only need assets and chunkGroups here.
- const stats = compilation.getStats().toJson({
- assets: true,
- chunkGroups: true
- });
- const assetNameToChunkNames = assetToChunkNameMapping(stats); // See https://github.com/GoogleChrome/workbox/issues/1287
- if (Array.isArray(config.chunks)) {
- for (const chunk of config.chunks) {
- if (!(chunk in stats.namedChunkGroups)) {
- compilation.warnings.push(`The chunk '${chunk}' was provided in ` + `your Workbox chunks config, but was not found in the compilation.`);
- }
- }
- } // See https://webpack.js.org/api/stats/#asset-objects
- for (const asset of stats.assets) {
- // chunkName based filtering is funky because:
- // - Each asset might belong to one or more chunkNames.
- // - 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.
- const isExcludedChunk = Array.isArray(config.excludeChunks) && config.excludeChunks.some(chunkName => {
- return assetNameToChunkNames[asset.name].has(chunkName);
- });
- if (isExcludedChunk) {
- continue;
- }
- const isIncludedChunk = !Array.isArray(config.chunks) || config.chunks.some(chunkName => {
- return assetNameToChunkNames[asset.name].has(chunkName);
- });
- if (!isIncludedChunk) {
- 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 (compilation, config) => {
- const filteredAssets = filterAssets(compilation, config);
- const {
- publicPath
- } = compilation.options.output;
- const fileDetails = [];
- for (const asset of filteredAssets) {
- // Not sure why this would be false, but checking just in case, since
- // our original list of assets comes from compilation.getStats().toJson(),
- // not from compilation.assets.
- if (asset.name in compilation.assets) {
- // This matches the format expected by transformManifest().
- fileDetails.push({
- file: resolveWebpackURL(publicPath, asset.name),
- hash: getAssetHash(compilation.assets[asset.name]),
- size: asset.size || 0
- });
- } else {
- compilation.warnings.push(`Could not precache ${asset.name}, as it's ` + `missing from compilation.assets. Please open a bug against Workbox ` + `with details about your webpack config.`);
- }
- } // We also get back `size` and `count`, and it would be nice to log that
- // somewhere, but... webpack doesn't offer info-level logs?
- // https://github.com/webpack/webpack/issues/3996
- const {
- manifestEntries,
- warnings
- } = await transformManifest({
- fileDetails,
- additionalManifestEntries: config.additionalManifestEntries,
- dontCacheBustURLsMatching: config.dontCacheBustURLsMatching,
- manifestTransforms: config.manifestTransforms,
- maximumFileSizeToCacheInBytes: config.maximumFileSizeToCacheInBytes,
- modifyURLPrefix: config.modifyURLPrefix,
- transformParam: compilation
- });
- compilation.warnings = compilation.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 sortedEntries;
- };
|