get-manifest-entries-from-compilation.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
  4. /*
  5. Copyright 2018 Google LLC
  6. Use of this source code is governed by an MIT-style
  7. license that can be found in the LICENSE file or at
  8. https://opensource.org/licenses/MIT.
  9. */
  10. const ModuleFilenameHelpers = require('webpack/lib/ModuleFilenameHelpers');
  11. const getAssetHash = require('./get-asset-hash');
  12. const resolveWebpackURL = require('./resolve-webpack-url');
  13. /**
  14. * A single manifest entry that Workbox can precache.
  15. * When possible, we leave out the revision information, which tells Workbox
  16. * that the URL contains enough info to uniquely version the asset.
  17. *
  18. * @param {Array<string>} knownHashes All of the hashes that are associated
  19. * with this webpack build.
  20. * @param {string} url webpack asset url path
  21. * @param {string} [revision] A revision hash for the entry
  22. * @return {module:workbox-build.ManifestEntry} A single manifest entry
  23. *
  24. * @private
  25. */
  26. function getEntry(knownHashes, url, revision) {
  27. // We're assuming that if the URL contains any of the known hashes
  28. // (either the short or full chunk hash or compilation hash) then it's
  29. // already revisioned, and we don't need additional out-of-band revisioning.
  30. if (!revision || knownHashes.some(hash => url.includes(hash))) {
  31. return {
  32. url
  33. };
  34. }
  35. return {
  36. revision,
  37. url
  38. };
  39. }
  40. /**
  41. * Filter to narrow down the asset list to chunks that:
  42. * - have a name.
  43. * - if there's a whitelist, the chunk's name is in the whitelist.
  44. * - if there's a blacklist, the chunk's name is not in the blacklist.
  45. *
  46. * TODO:
  47. * Filter files by size:
  48. * https://github.com/GoogleChrome/workbox/pull/808#discussion_r139606242
  49. * Filter files that match `staticFileGlobsIgnorePatterns` (or something)
  50. * but filter for [/\.map$/, /asset-manifest\.json$/] by default:
  51. * https://github.com/GoogleChrome/workbox/pull/808#discussion_r140565156
  52. *
  53. * @param {Object<string, Object>} assetMetadata Metadata about the assets.
  54. * @param {Array<string>} [whitelist] Chunk names to include.
  55. * @param {Array<string>} [blacklist] Chunk names to exclude.
  56. * @return {Object<string, Object>} Filtered asset metadata.
  57. *
  58. * @private
  59. */
  60. function filterAssets(assetMetadata, whitelist, blacklist) {
  61. const filteredMapping = {};
  62. var _arr = Object.entries(assetMetadata);
  63. for (var _i = 0; _i < _arr.length; _i++) {
  64. const _arr$_i = (0, _slicedToArray2.default)(_arr[_i], 2),
  65. file = _arr$_i[0],
  66. metadata = _arr$_i[1];
  67. const chunkName = metadata.chunkName; // This file is whitelisted if:
  68. // - Trivially, if there is no whitelist defined.
  69. // - There is a whitelist and our file is associated with a chunk whose name
  70. // is listed.
  71. const isWhitelisted = whitelist.length === 0 || whitelist.includes(chunkName); // This file is blacklisted if our file is associated with a chunk whose
  72. // name is listed.
  73. const isBlacklisted = blacklist.includes(chunkName); // Only include this entry in the filtered mapping if we're whitelisted and
  74. // not blacklisted.
  75. if (isWhitelisted && !isBlacklisted) {
  76. filteredMapping[file] = metadata;
  77. }
  78. }
  79. return filteredMapping;
  80. }
  81. /**
  82. * Takes in compilation.assets and compilation.chunks, and assigns metadata
  83. * to each file listed in assets:
  84. *
  85. * - If the asset was created by a chunk, it assigns the existing chunk name and
  86. * chunk hash.
  87. * - If the asset was created outside of a chunk, it assigns a chunk name of ''
  88. * and generates a hash of the asset.
  89. *
  90. * @param {Object} assets The compilation.assets
  91. * @param {Array<Object>} chunks The compilation.chunks
  92. * @return {Object<string, Object>} Mapping of asset paths to chunk name and
  93. * hash metadata.
  94. *
  95. * @private
  96. */
  97. function generateMetadataForAssets(assets, chunks) {
  98. const mapping = {}; // Start out by getting metadata for all the assets associated with a chunk.
  99. var _iteratorNormalCompletion = true;
  100. var _didIteratorError = false;
  101. var _iteratorError = undefined;
  102. try {
  103. for (var _iterator = chunks[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
  104. const chunk = _step.value;
  105. var _iteratorNormalCompletion2 = true;
  106. var _didIteratorError2 = false;
  107. var _iteratorError2 = undefined;
  108. try {
  109. for (var _iterator2 = chunk.files[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
  110. const file = _step2.value;
  111. mapping[file] = {
  112. chunkName: chunk.name,
  113. hash: chunk.renderedHash
  114. };
  115. }
  116. } catch (err) {
  117. _didIteratorError2 = true;
  118. _iteratorError2 = err;
  119. } finally {
  120. try {
  121. if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
  122. _iterator2.return();
  123. }
  124. } finally {
  125. if (_didIteratorError2) {
  126. throw _iteratorError2;
  127. }
  128. }
  129. }
  130. } // Next, loop through the total list of assets and find anything that isn't
  131. // associated with a chunk.
  132. } catch (err) {
  133. _didIteratorError = true;
  134. _iteratorError = err;
  135. } finally {
  136. try {
  137. if (!_iteratorNormalCompletion && _iterator.return != null) {
  138. _iterator.return();
  139. }
  140. } finally {
  141. if (_didIteratorError) {
  142. throw _iteratorError;
  143. }
  144. }
  145. }
  146. var _arr2 = Object.entries(assets);
  147. for (var _i2 = 0; _i2 < _arr2.length; _i2++) {
  148. const _arr2$_i = (0, _slicedToArray2.default)(_arr2[_i2], 2),
  149. file = _arr2$_i[0],
  150. asset = _arr2$_i[1];
  151. if (file in mapping) {
  152. continue;
  153. }
  154. mapping[file] = {
  155. // Just use an empty string to denote the lack of chunk association.
  156. chunkName: '',
  157. hash: getAssetHash(asset)
  158. };
  159. }
  160. return mapping;
  161. }
  162. /**
  163. * Given an assetMetadata mapping, returns a Set of all of the hashes that
  164. * are associated with at least one asset.
  165. *
  166. * @param {Object<string, Object>} assetMetadata Mapping of asset paths to chunk
  167. * name and hash metadata.
  168. * @return {Set} The known hashes associated with an asset.
  169. *
  170. * @private
  171. */
  172. function getKnownHashesFromAssets(assetMetadata) {
  173. const knownHashes = new Set();
  174. var _arr3 = Object.values(assetMetadata);
  175. for (var _i3 = 0; _i3 < _arr3.length; _i3++) {
  176. const metadata = _arr3[_i3];
  177. knownHashes.add(metadata.hash);
  178. }
  179. return knownHashes;
  180. }
  181. /**
  182. * Generate an array of manifest entries using webpack's compilation data.
  183. *
  184. * @param {Object} compilation webpack compilation
  185. * @param {Object} config
  186. * @return {Array<workbox.build.ManifestEntry>}
  187. *
  188. * @private
  189. */
  190. function getManifestEntriesFromCompilation(compilation, config) {
  191. const blacklistedChunkNames = config.excludeChunks;
  192. const whitelistedChunkNames = config.chunks;
  193. const assets = compilation.assets,
  194. chunks = compilation.chunks;
  195. const publicPath = compilation.options.output.publicPath;
  196. const assetMetadata = generateMetadataForAssets(assets, chunks);
  197. const filteredAssetMetadata = filterAssets(assetMetadata, whitelistedChunkNames, blacklistedChunkNames);
  198. const knownHashes = [compilation.hash, compilation.fullHash, ...getKnownHashesFromAssets(filteredAssetMetadata)].filter(hash => !!hash);
  199. const manifestEntries = [];
  200. var _arr4 = Object.entries(filteredAssetMetadata);
  201. for (var _i4 = 0; _i4 < _arr4.length; _i4++) {
  202. const _arr4$_i = (0, _slicedToArray2.default)(_arr4[_i4], 2),
  203. file = _arr4$_i[0],
  204. metadata = _arr4$_i[1];
  205. // Filter based on test/include/exclude options set in the config,
  206. // following webpack's conventions.
  207. // This matches the behavior of, e.g., UglifyJS's webpack plugin.
  208. if (!ModuleFilenameHelpers.matchObject(config, file)) {
  209. continue;
  210. }
  211. const publicURL = resolveWebpackURL(publicPath, file);
  212. const manifestEntry = getEntry(knownHashes, publicURL, metadata.hash);
  213. manifestEntries.push(manifestEntry);
  214. }
  215. return manifestEntries;
  216. }
  217. module.exports = getManifestEntriesFromCompilation;