@serwist/build 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -0
  3. package/dist/get-manifest.d.ts +19 -0
  4. package/dist/get-manifest.d.ts.map +1 -0
  5. package/dist/index.d.ts +10 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +1295 -0
  8. package/dist/index.old.cjs +1303 -0
  9. package/dist/inject-manifest.d.ts +29 -0
  10. package/dist/inject-manifest.d.ts.map +1 -0
  11. package/dist/lib/.swcrc.d.ts +7 -0
  12. package/dist/lib/.swcrc.d.ts.map +1 -0
  13. package/dist/lib/additional-precache-entries-transform.d.ts +14 -0
  14. package/dist/lib/additional-precache-entries-transform.d.ts.map +1 -0
  15. package/dist/lib/bundle.d.ts +12 -0
  16. package/dist/lib/bundle.d.ts.map +1 -0
  17. package/dist/lib/cdn-utils.d.ts +2 -0
  18. package/dist/lib/cdn-utils.d.ts.map +1 -0
  19. package/dist/lib/copy-serwist-libraries.d.ts +17 -0
  20. package/dist/lib/copy-serwist-libraries.d.ts.map +1 -0
  21. package/dist/lib/errors.d.ts +52 -0
  22. package/dist/lib/errors.d.ts.map +1 -0
  23. package/dist/lib/escape-regexp.d.ts +2 -0
  24. package/dist/lib/escape-regexp.d.ts.map +1 -0
  25. package/dist/lib/get-composite-details.d.ts +3 -0
  26. package/dist/lib/get-composite-details.d.ts.map +1 -0
  27. package/dist/lib/get-file-details.d.ts +15 -0
  28. package/dist/lib/get-file-details.d.ts.map +1 -0
  29. package/dist/lib/get-file-hash.d.ts +2 -0
  30. package/dist/lib/get-file-hash.d.ts.map +1 -0
  31. package/dist/lib/get-file-manifest-entries.d.ts +3 -0
  32. package/dist/lib/get-file-manifest-entries.d.ts.map +1 -0
  33. package/dist/lib/get-file-size.d.ts +2 -0
  34. package/dist/lib/get-file-size.d.ts.map +1 -0
  35. package/dist/lib/get-source-map-url.d.ts +2 -0
  36. package/dist/lib/get-source-map-url.d.ts.map +1 -0
  37. package/dist/lib/get-string-details.d.ts +3 -0
  38. package/dist/lib/get-string-details.d.ts.map +1 -0
  39. package/dist/lib/get-string-hash.d.ts +4 -0
  40. package/dist/lib/get-string-hash.d.ts.map +1 -0
  41. package/dist/lib/maximum-size-transform.d.ts +3 -0
  42. package/dist/lib/maximum-size-transform.d.ts.map +1 -0
  43. package/dist/lib/modify-url-prefix-transform.d.ts +5 -0
  44. package/dist/lib/modify-url-prefix-transform.d.ts.map +1 -0
  45. package/dist/lib/no-revision-for-urls-matching-transform.d.ts +3 -0
  46. package/dist/lib/no-revision-for-urls-matching-transform.d.ts.map +1 -0
  47. package/dist/lib/rebase-path.d.ts +5 -0
  48. package/dist/lib/rebase-path.d.ts.map +1 -0
  49. package/dist/lib/replace-and-update-source-map.d.ts +42 -0
  50. package/dist/lib/replace-and-update-source-map.d.ts.map +1 -0
  51. package/dist/lib/stringify-without-comments.d.ts +4 -0
  52. package/dist/lib/stringify-without-comments.d.ts.map +1 -0
  53. package/dist/lib/transform-manifest.d.ts +61 -0
  54. package/dist/lib/transform-manifest.d.ts.map +1 -0
  55. package/dist/lib/translate-url-to-sourcemap-paths.d.ts +6 -0
  56. package/dist/lib/translate-url-to-sourcemap-paths.d.ts.map +1 -0
  57. package/dist/lib/validate-options.d.ts +8 -0
  58. package/dist/lib/validate-options.d.ts.map +1 -0
  59. package/dist/schema/index.d.ts +444 -0
  60. package/dist/schema/index.d.ts.map +1 -0
  61. package/dist/types.d.ts +530 -0
  62. package/dist/types.d.ts.map +1 -0
  63. package/package.json +89 -0
package/dist/index.js ADDED
@@ -0,0 +1,1295 @@
1
+ import assert from 'assert';
2
+ import { oneLine } from 'common-tags';
3
+ import crypto from 'crypto';
4
+ import { glob } from 'glob';
5
+ import upath from 'upath';
6
+ import fse from 'fs-extra';
7
+ import prettyBytes from 'pretty-bytes';
8
+ import { betterAjvErrors } from '@apideck/better-ajv-errors';
9
+ import Ajv from 'ajv';
10
+ import stringify from 'fast-json-stable-stringify';
11
+ import { SourceMapGenerator, SourceMapConsumer } from 'source-map';
12
+
13
+ const errors = {
14
+ "unable-to-get-rootdir": `Unable to get the root directory of your web app.`,
15
+ "no-extension": oneLine`Unable to detect a usable extension for a file in your web
16
+ app directory.`,
17
+ "invalid-file-manifest-name": oneLine`The File Manifest Name must have at least one
18
+ character.`,
19
+ "unable-to-get-file-manifest-name": "Unable to get a file manifest name.",
20
+ "invalid-sw-dest": `The 'swDest' value must be a valid path.`,
21
+ "unable-to-get-sw-name": "Unable to get a service worker file name.",
22
+ "unable-to-get-save-config": oneLine`An error occurred when asking to save details
23
+ in a config file.`,
24
+ "unable-to-get-file-hash": oneLine`An error occurred when attempting to create a
25
+ file hash.`,
26
+ "unable-to-get-file-size": oneLine`An error occurred when attempting to get a file
27
+ size.`,
28
+ "unable-to-glob-files": "An error occurred when globbing for files.",
29
+ "unable-to-make-manifest-directory": oneLine`Unable to make output directory for
30
+ file manifest.`,
31
+ "read-manifest-template-failure": "Unable to read template for file manifest",
32
+ "populating-manifest-tmpl-failed": oneLine`An error occurred when populating the
33
+ file manifest template.`,
34
+ "manifest-file-write-failure": "Unable to write the file manifest.",
35
+ "unable-to-make-sw-directory": oneLine`Unable to make the directories to output
36
+ the service worker path.`,
37
+ "sw-write-failure": "Unable to write the service worker file.",
38
+ "sw-write-failure-directory": oneLine`Unable to write the service worker file;
39
+ 'swDest' should be a full path to the file, not a path to a directory.`,
40
+ "unable-to-copy-serwist-libraries": oneLine`One or more of the Serwist libraries
41
+ could not be copied over to the destination directory: `,
42
+ "invalid-glob-directory": oneLine`The supplied globDirectory must be a path as a
43
+ string.`,
44
+ "invalid-dont-cache-bust": oneLine`The supplied 'dontCacheBustURLsMatching'
45
+ parameter must be a RegExp.`,
46
+ "invalid-exclude-files": "The excluded files should be an array of strings.",
47
+ "invalid-get-manifest-entries-input": oneLine`The input to
48
+ 'getFileManifestEntries()' must be an object.`,
49
+ "invalid-manifest-path": oneLine`The supplied manifest path is not a string with
50
+ at least one character.`,
51
+ "invalid-manifest-entries": oneLine`The manifest entries must be an array of
52
+ strings or JavaScript objects containing a url parameter.`,
53
+ "invalid-manifest-format": oneLine`The value of the 'format' option passed to
54
+ generateFileManifest() must be either 'iife' (the default) or 'es'.`,
55
+ "invalid-static-file-globs": oneLine`The 'globPatterns' value must be an array
56
+ of strings.`,
57
+ "invalid-templated-urls": oneLine`The 'templatedURLs' value should be an object
58
+ that maps URLs to either a string, or to an array of glob patterns.`,
59
+ "templated-url-matches-glob": oneLine`One of the 'templatedURLs' URLs is already
60
+ being tracked via 'globPatterns': `,
61
+ "invalid-glob-ignores": oneLine`The 'globIgnores' parameter must be an array of
62
+ glob pattern strings.`,
63
+ "manifest-entry-bad-url": oneLine`The generated manifest contains an entry without
64
+ a URL string. This is likely an error with @serwist/build.`,
65
+ "modify-url-prefix-bad-prefixes": oneLine`The 'modifyURLPrefix' parameter must be
66
+ an object with string key value pairs.`,
67
+ "invalid-inject-manifest-arg": oneLine`The input to 'injectManifest()' must be an
68
+ object.`,
69
+ "injection-point-not-found": oneLine`Unable to find a place to inject the manifest.
70
+ Please ensure that your service worker file contains the following: `,
71
+ "multiple-injection-points": oneLine`Please ensure that your 'swSrc' file contains
72
+ only one match for the following: `,
73
+ "useless-glob-pattern": oneLine`One of the glob patterns doesn't match any files.
74
+ Please remove or fix the following: `,
75
+ "bad-template-urls-asset": oneLine`There was an issue using one of the provided
76
+ 'templatedURLs'.`,
77
+ "invalid-runtime-caching": oneLine`The 'runtimeCaching' parameter must an an
78
+ array of objects with at least a 'urlPattern' and 'handler'.`,
79
+ "static-file-globs-deprecated": oneLine`'staticFileGlobs' is deprecated.
80
+ Please use 'globPatterns' instead.`,
81
+ "dynamic-url-deprecated": oneLine`'dynamicURLToDependencies' is deprecated.
82
+ Please use 'templatedURLs' instead.`,
83
+ "urlPattern-is-required": oneLine`The 'urlPattern' option is required when using
84
+ 'runtimeCaching'.`,
85
+ "handler-is-required": oneLine`The 'handler' option is required when using
86
+ runtimeCaching.`,
87
+ "invalid-generate-file-manifest-arg": oneLine`The input to generateFileManifest()
88
+ must be an Object.`,
89
+ "invalid-sw-src": `The 'swSrc' file can't be read.`,
90
+ "same-src-and-dest": oneLine`Unable to find a place to inject the manifest. This is
91
+ likely because swSrc and swDest are configured to the same file.
92
+ Please ensure that your swSrc file contains the following:`,
93
+ "no-module-name": oneLine`You must provide a moduleName parameter when calling
94
+ getModuleURL().`,
95
+ "bad-manifest-transforms-return-value": oneLine`The return value from a
96
+ manifestTransform should be an object with 'manifest' and optionally
97
+ 'warnings' properties.`,
98
+ "string-entry-warning": oneLine`Some items were passed to additionalPrecacheEntries
99
+ without revisioning info. This is generally NOT safe. Learn more at
100
+ https://bit.ly/wb-precache.`,
101
+ "cant-find-sourcemap": oneLine`The swSrc file refers to a sourcemap that can't be
102
+ opened:`,
103
+ "manifest-transforms": oneLine`When using manifestTransforms, you must provide
104
+ an array of functions.`
105
+ };
106
+
107
+ function getCompositeDetails(compositeURL, dependencyDetails) {
108
+ let totalSize = 0;
109
+ let compositeHash = "";
110
+ for (const fileDetails of dependencyDetails){
111
+ totalSize += fileDetails.size;
112
+ compositeHash += fileDetails.hash;
113
+ }
114
+ const md5 = crypto.createHash("md5");
115
+ md5.update(compositeHash);
116
+ const hashOfHashes = md5.digest("hex");
117
+ return {
118
+ file: compositeURL,
119
+ hash: hashOfHashes,
120
+ size: totalSize
121
+ };
122
+ }
123
+
124
+ function getStringHash(input) {
125
+ const md5 = crypto.createHash("md5");
126
+ md5.update(input);
127
+ return md5.digest("hex");
128
+ }
129
+
130
+ function getFileHash(file) {
131
+ try {
132
+ const buffer = fse.readFileSync(file);
133
+ return getStringHash(buffer);
134
+ } catch (err) {
135
+ throw new Error(errors["unable-to-get-file-hash"] + ` '${err instanceof Error && err.message ? err.message : ""}'`);
136
+ }
137
+ }
138
+
139
+ function getFileSize(file) {
140
+ try {
141
+ const stat = fse.statSync(file);
142
+ if (!stat.isFile()) {
143
+ return null;
144
+ }
145
+ return stat.size;
146
+ } catch (err) {
147
+ throw new Error(errors["unable-to-get-file-size"] + ` '${err instanceof Error && err.message ? err.message : ""}'`);
148
+ }
149
+ }
150
+
151
+ function getFileDetails({ globDirectory, globFollow, globIgnores, globPattern }) {
152
+ let globbedFiles;
153
+ let warning = "";
154
+ try {
155
+ globbedFiles = glob.sync(globPattern, {
156
+ cwd: globDirectory,
157
+ follow: globFollow,
158
+ ignore: globIgnores
159
+ });
160
+ } catch (err) {
161
+ throw new Error(errors["unable-to-glob-files"] + ` '${err instanceof Error && err.message ? err.message : ""}'`);
162
+ }
163
+ if (globbedFiles.length === 0) {
164
+ warning = errors["useless-glob-pattern"] + " " + JSON.stringify({
165
+ globDirectory,
166
+ globPattern,
167
+ globIgnores
168
+ }, null, 2);
169
+ }
170
+ const globbedFileDetails = [];
171
+ for (const file of globbedFiles){
172
+ const fullPath = upath.join(globDirectory, file);
173
+ const fileSize = getFileSize(fullPath);
174
+ if (fileSize !== null) {
175
+ const fileHash = getFileHash(fullPath);
176
+ globbedFileDetails.push({
177
+ file: `${upath.relative(globDirectory, fullPath)}`,
178
+ hash: fileHash,
179
+ size: fileSize
180
+ });
181
+ }
182
+ }
183
+ return {
184
+ globbedFileDetails,
185
+ warning
186
+ };
187
+ }
188
+
189
+ function getStringDetails(url, str) {
190
+ return {
191
+ file: url,
192
+ hash: getStringHash(str),
193
+ size: str.length
194
+ };
195
+ }
196
+
197
+ function additionalPrecacheEntriesTransform(additionalPrecacheEntries) {
198
+ return (manifest)=>{
199
+ const warnings = [];
200
+ const stringEntries = new Set();
201
+ for (const additionalEntry of additionalPrecacheEntries){
202
+ // Warn about either a string or an object that lacks a revision property.
203
+ // (An object with a revision property set to null is okay.)
204
+ if (typeof additionalEntry === "string") {
205
+ stringEntries.add(additionalEntry);
206
+ manifest.push({
207
+ revision: null,
208
+ size: 0,
209
+ url: additionalEntry
210
+ });
211
+ } else {
212
+ if (additionalEntry && additionalEntry.revision === undefined) {
213
+ stringEntries.add(additionalEntry.url);
214
+ }
215
+ manifest.push(Object.assign({
216
+ size: 0
217
+ }, additionalEntry));
218
+ }
219
+ }
220
+ if (stringEntries.size > 0) {
221
+ let urls = "\n";
222
+ for (const stringEntry of stringEntries){
223
+ urls += ` - ${stringEntry}\n`;
224
+ }
225
+ warnings.push(errors["string-entry-warning"] + urls);
226
+ }
227
+ return {
228
+ manifest,
229
+ warnings
230
+ };
231
+ };
232
+ }
233
+
234
+ function maximumSizeTransform(maximumFileSizeToCacheInBytes) {
235
+ return (originalManifest)=>{
236
+ const warnings = [];
237
+ const manifest = originalManifest.filter((entry)=>{
238
+ if (entry.size <= maximumFileSizeToCacheInBytes) {
239
+ return true;
240
+ }
241
+ warnings.push(`${entry.url} is ${prettyBytes(entry.size)}, and won't ` + `be precached. Configure maximumFileSizeToCacheInBytes to change ` + `this limit.`);
242
+ return false;
243
+ });
244
+ return {
245
+ manifest,
246
+ warnings
247
+ };
248
+ };
249
+ }
250
+
251
+ /*
252
+ Copyright 2019 Google LLC
253
+
254
+ Use of this source code is governed by an MIT-style
255
+ license that can be found in the LICENSE file or at
256
+ https://opensource.org/licenses/MIT.
257
+ */ // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
258
+ function escapeRegExp(str) {
259
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
260
+ }
261
+
262
+ function modifyURLPrefixTransform(modifyURLPrefix) {
263
+ if (!modifyURLPrefix || typeof modifyURLPrefix !== "object" || Array.isArray(modifyURLPrefix)) {
264
+ throw new Error(errors["modify-url-prefix-bad-prefixes"]);
265
+ }
266
+ // If there are no entries in modifyURLPrefix, just return an identity
267
+ // function as a shortcut.
268
+ if (Object.keys(modifyURLPrefix).length === 0) {
269
+ return (manifest)=>{
270
+ return {
271
+ manifest
272
+ };
273
+ };
274
+ }
275
+ for (const key of Object.keys(modifyURLPrefix)){
276
+ if (typeof modifyURLPrefix[key] !== "string") {
277
+ throw new Error(errors["modify-url-prefix-bad-prefixes"]);
278
+ }
279
+ }
280
+ // Escape the user input so it's safe to use in a regex.
281
+ const safeModifyURLPrefixes = Object.keys(modifyURLPrefix).map(escapeRegExp);
282
+ // Join all the `modifyURLPrefix` keys so a single regex can be used.
283
+ const prefixMatchesStrings = safeModifyURLPrefixes.join("|");
284
+ // Add `^` to the front the prefix matches so it only matches the start of
285
+ // a string.
286
+ const modifyRegex = new RegExp(`^(${prefixMatchesStrings})`);
287
+ return (originalManifest)=>{
288
+ const manifest = originalManifest.map((entry)=>{
289
+ if (typeof entry.url !== "string") {
290
+ throw new Error(errors["manifest-entry-bad-url"]);
291
+ }
292
+ entry.url = entry.url.replace(modifyRegex, (match)=>{
293
+ return modifyURLPrefix[match];
294
+ });
295
+ return entry;
296
+ });
297
+ return {
298
+ manifest
299
+ };
300
+ };
301
+ }
302
+
303
+ function noRevisionForURLsMatchingTransform(regexp) {
304
+ if (!(regexp instanceof RegExp)) {
305
+ throw new Error(errors["invalid-dont-cache-bust"]);
306
+ }
307
+ return (originalManifest)=>{
308
+ const manifest = originalManifest.map((entry)=>{
309
+ if (typeof entry.url !== "string") {
310
+ throw new Error(errors["manifest-entry-bad-url"]);
311
+ }
312
+ if (entry.url.match(regexp)) {
313
+ entry.revision = null;
314
+ }
315
+ return entry;
316
+ });
317
+ return {
318
+ manifest
319
+ };
320
+ };
321
+ }
322
+
323
+ async function transformManifest({ additionalPrecacheEntries, dontCacheBustURLsMatching, fileDetails, manifestTransforms, maximumFileSizeToCacheInBytes, modifyURLPrefix, transformParam }) {
324
+ const allWarnings = [];
325
+ // Take the array of fileDetail objects and convert it into an array of
326
+ // {url, revision, size} objects, with \ replaced with /.
327
+ const normalizedManifest = fileDetails.map((fileDetails)=>{
328
+ return {
329
+ url: fileDetails.file.replace(/\\/g, "/"),
330
+ revision: fileDetails.hash,
331
+ size: fileDetails.size
332
+ };
333
+ });
334
+ const transformsToApply = [];
335
+ if (maximumFileSizeToCacheInBytes) {
336
+ transformsToApply.push(maximumSizeTransform(maximumFileSizeToCacheInBytes));
337
+ }
338
+ if (modifyURLPrefix) {
339
+ transformsToApply.push(modifyURLPrefixTransform(modifyURLPrefix));
340
+ }
341
+ if (dontCacheBustURLsMatching) {
342
+ transformsToApply.push(noRevisionForURLsMatchingTransform(dontCacheBustURLsMatching));
343
+ }
344
+ // Run any manifestTransforms functions second-to-last.
345
+ if (manifestTransforms) {
346
+ transformsToApply.push(...manifestTransforms);
347
+ }
348
+ // Run additionalPrecacheEntriesTransform last.
349
+ if (additionalPrecacheEntries) {
350
+ transformsToApply.push(additionalPrecacheEntriesTransform(additionalPrecacheEntries));
351
+ }
352
+ let transformedManifest = normalizedManifest;
353
+ for (const transform of transformsToApply){
354
+ const result = await transform(transformedManifest, transformParam);
355
+ if (!("manifest" in result)) {
356
+ throw new Error(errors["bad-manifest-transforms-return-value"]);
357
+ }
358
+ transformedManifest = result.manifest;
359
+ allWarnings.push(...result.warnings || []);
360
+ }
361
+ // Generate some metadata about the manifest before we clear out the size
362
+ // properties from each entry.
363
+ const count = transformedManifest.length;
364
+ let size = 0;
365
+ for (const manifestEntry of transformedManifest){
366
+ size += manifestEntry.size || 0;
367
+ delete manifestEntry.size;
368
+ }
369
+ return {
370
+ count,
371
+ size,
372
+ manifestEntries: transformedManifest,
373
+ warnings: allWarnings
374
+ };
375
+ }
376
+
377
+ async function getFileManifestEntries({ additionalPrecacheEntries, dontCacheBustURLsMatching, globDirectory, globFollow, globIgnores, globPatterns = [], globStrict, manifestTransforms, maximumFileSizeToCacheInBytes, modifyURLPrefix, templatedURLs }) {
378
+ const warnings = [];
379
+ const allFileDetails = new Map();
380
+ try {
381
+ for (const globPattern of globPatterns){
382
+ const { globbedFileDetails, warning } = getFileDetails({
383
+ globDirectory,
384
+ globFollow,
385
+ globIgnores,
386
+ globPattern,
387
+ globStrict
388
+ });
389
+ if (warning) {
390
+ warnings.push(warning);
391
+ }
392
+ for (const details of globbedFileDetails){
393
+ if (details && !allFileDetails.has(details.file)) {
394
+ allFileDetails.set(details.file, details);
395
+ }
396
+ }
397
+ }
398
+ } catch (error) {
399
+ // If there's an exception thrown while globbing, then report
400
+ // it back as a warning, and don't consider it fatal.
401
+ if (error instanceof Error && error.message) {
402
+ warnings.push(error.message);
403
+ }
404
+ }
405
+ if (templatedURLs) {
406
+ for (const url of Object.keys(templatedURLs)){
407
+ assert(!allFileDetails.has(url), errors["templated-url-matches-glob"]);
408
+ const dependencies = templatedURLs[url];
409
+ if (Array.isArray(dependencies)) {
410
+ const details = dependencies.reduce((previous, globPattern)=>{
411
+ try {
412
+ const { globbedFileDetails, warning } = getFileDetails({
413
+ globDirectory,
414
+ globFollow,
415
+ globIgnores,
416
+ globPattern,
417
+ globStrict
418
+ });
419
+ if (warning) {
420
+ warnings.push(warning);
421
+ }
422
+ return previous.concat(globbedFileDetails);
423
+ } catch (error) {
424
+ const debugObj = {};
425
+ debugObj[url] = dependencies;
426
+ throw new Error(`${errors["bad-template-urls-asset"]} ` + `'${globPattern}' from '${JSON.stringify(debugObj)}':\n` + `${error instanceof Error ? error.toString() : ""}`);
427
+ }
428
+ }, []);
429
+ if (details.length === 0) {
430
+ throw new Error(`${errors["bad-template-urls-asset"]} The glob ` + `pattern '${dependencies.toString()}' did not match anything.`);
431
+ }
432
+ allFileDetails.set(url, getCompositeDetails(url, details));
433
+ } else if (typeof dependencies === "string") {
434
+ allFileDetails.set(url, getStringDetails(url, dependencies));
435
+ }
436
+ }
437
+ }
438
+ const transformedManifest = await transformManifest({
439
+ additionalPrecacheEntries,
440
+ dontCacheBustURLsMatching,
441
+ manifestTransforms,
442
+ maximumFileSizeToCacheInBytes,
443
+ modifyURLPrefix,
444
+ fileDetails: Array.from(allFileDetails.values())
445
+ });
446
+ transformedManifest.warnings.push(...warnings);
447
+ return transformedManifest;
448
+ }
449
+
450
+ var additionalProperties$2 = false;
451
+ var type$2 = "object";
452
+ var properties$2 = {
453
+ additionalPrecacheEntries: {
454
+ description: "A list of entries to be precached, in addition to any entries that are\ngenerated as part of the build configuration.",
455
+ type: "array",
456
+ items: {
457
+ anyOf: [
458
+ {
459
+ $ref: "#/definitions/ManifestEntry"
460
+ },
461
+ {
462
+ type: "string"
463
+ }
464
+ ]
465
+ }
466
+ },
467
+ dontCacheBustURLsMatching: {
468
+ description: "Assets that match this will be assumed to be uniquely versioned via their\nURL, and exempted from the normal HTTP cache-busting that's done when\npopulating the precache. While not required, it's recommended that if your\nexisting build process already inserts a `[hash]` value into each filename,\nyou provide a RegExp that will detect that, as it will reduce the bandwidth\nconsumed when precaching.",
469
+ $ref: "#/definitions/RegExp"
470
+ },
471
+ manifestTransforms: {
472
+ description: "One or more functions which will be applied sequentially against the\ngenerated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are\nalso specified, their corresponding transformations will be applied first.",
473
+ type: "array",
474
+ items: {}
475
+ },
476
+ maximumFileSizeToCacheInBytes: {
477
+ description: "This value can be used to determine the maximum size of files that will be\nprecached. This prevents you from inadvertently precaching very large files\nthat might have accidentally matched one of your patterns.",
478
+ "default": 2097152,
479
+ type: "number"
480
+ },
481
+ modifyURLPrefix: {
482
+ description: "An object mapping string prefixes to replacement string values. This can be\nused to, e.g., remove or add a path prefix from a manifest entry if your\nweb hosting setup doesn't match your local filesystem setup. As an\nalternative with more flexibility, you can use the `manifestTransforms`\noption and provide a function that modifies the entries in the manifest\nusing whatever logic you provide.\n\nExample usage:\n\n```\n// Replace a '/dist/' prefix with '/', and also prepend\n// '/static' to every URL.\nmodifyURLPrefix: {\n '/dist/': '/',\n '': '/static',\n}\n```",
483
+ type: "object",
484
+ additionalProperties: {
485
+ type: "string"
486
+ }
487
+ },
488
+ globFollow: {
489
+ description: "Determines whether or not symlinks are followed when generating the\nprecache manifest. For more information, see the definition of `follow` in\nthe `glob` [documentation](https://github.com/isaacs/node-glob#options).",
490
+ "default": true,
491
+ type: "boolean"
492
+ },
493
+ globIgnores: {
494
+ description: "A set of patterns matching files to always exclude when generating the\nprecache manifest. For more information, see the definition of `ignore` in\nthe `glob` [documentation](https://github.com/isaacs/node-glob#options).",
495
+ "default": [
496
+ "**/node_modules/**/*"
497
+ ],
498
+ type: "array",
499
+ items: {
500
+ type: "string"
501
+ }
502
+ },
503
+ globPatterns: {
504
+ description: "Files matching any of these patterns will be included in the precache\nmanifest. For more information, see the\n[`glob` primer](https://github.com/isaacs/node-glob#glob-primer).",
505
+ "default": [
506
+ "**/*.{js,css,html}"
507
+ ],
508
+ type: "array",
509
+ items: {
510
+ type: "string"
511
+ }
512
+ },
513
+ globStrict: {
514
+ description: "If true, an error reading a directory when generating a precache manifest\nwill cause the build to fail. If false, the problematic directory will be\nskipped. For more information, see the definition of `strict` in the `glob`\n[documentation](https://github.com/isaacs/node-glob#options).",
515
+ "default": true,
516
+ type: "boolean"
517
+ },
518
+ templatedURLs: {
519
+ description: "If a URL is rendered based on some server-side logic, its contents may\ndepend on multiple files or on some other unique string value. The keys in\nthis object are server-rendered URLs. If the values are an array of\nstrings, they will be interpreted as `glob` patterns, and the contents of\nany files matching the patterns will be used to uniquely version the URL.\nIf used with a single string, it will be interpreted as unique versioning\ninformation that you've generated for a given URL.",
520
+ type: "object",
521
+ additionalProperties: {
522
+ anyOf: [
523
+ {
524
+ type: "array",
525
+ items: {
526
+ type: "string"
527
+ }
528
+ },
529
+ {
530
+ type: "string"
531
+ }
532
+ ]
533
+ }
534
+ },
535
+ globDirectory: {
536
+ description: "The local directory you wish to match `globPatterns` against. The path is\nrelative to the current directory.",
537
+ type: "string"
538
+ }
539
+ };
540
+ var required$2 = [
541
+ "globDirectory"
542
+ ];
543
+ var definitions$2 = {
544
+ ManifestEntry: {
545
+ type: "object",
546
+ properties: {
547
+ integrity: {
548
+ type: "string"
549
+ },
550
+ revision: {
551
+ type: [
552
+ "null",
553
+ "string"
554
+ ]
555
+ },
556
+ url: {
557
+ type: "string"
558
+ }
559
+ },
560
+ additionalProperties: false,
561
+ required: [
562
+ "revision",
563
+ "url"
564
+ ]
565
+ },
566
+ "RegExp": {
567
+ type: "object",
568
+ properties: {
569
+ source: {
570
+ type: "string"
571
+ },
572
+ global: {
573
+ type: "boolean"
574
+ },
575
+ ignoreCase: {
576
+ type: "boolean"
577
+ },
578
+ multiline: {
579
+ type: "boolean"
580
+ },
581
+ lastIndex: {
582
+ type: "number"
583
+ },
584
+ flags: {
585
+ type: "string"
586
+ },
587
+ sticky: {
588
+ type: "boolean"
589
+ },
590
+ unicode: {
591
+ type: "boolean"
592
+ },
593
+ dotAll: {
594
+ type: "boolean"
595
+ }
596
+ },
597
+ additionalProperties: false,
598
+ required: [
599
+ "dotAll",
600
+ "flags",
601
+ "global",
602
+ "ignoreCase",
603
+ "lastIndex",
604
+ "multiline",
605
+ "source",
606
+ "sticky",
607
+ "unicode"
608
+ ]
609
+ }
610
+ };
611
+ var $schema$2 = "http://json-schema.org/draft-07/schema#";
612
+ var getManifestOptionsSchema = {
613
+ additionalProperties: additionalProperties$2,
614
+ type: type$2,
615
+ properties: properties$2,
616
+ required: required$2,
617
+ definitions: definitions$2,
618
+ $schema: $schema$2
619
+ };
620
+
621
+ var additionalProperties$1 = false;
622
+ var type$1 = "object";
623
+ var properties$1 = {
624
+ additionalPrecacheEntries: {
625
+ description: "A list of entries to be precached, in addition to any entries that are\ngenerated as part of the build configuration.",
626
+ type: "array",
627
+ items: {
628
+ anyOf: [
629
+ {
630
+ $ref: "#/definitions/ManifestEntry"
631
+ },
632
+ {
633
+ type: "string"
634
+ }
635
+ ]
636
+ }
637
+ },
638
+ dontCacheBustURLsMatching: {
639
+ description: "Assets that match this will be assumed to be uniquely versioned via their\nURL, and exempted from the normal HTTP cache-busting that's done when\npopulating the precache. While not required, it's recommended that if your\nexisting build process already inserts a `[hash]` value into each filename,\nyou provide a RegExp that will detect that, as it will reduce the bandwidth\nconsumed when precaching.",
640
+ $ref: "#/definitions/RegExp"
641
+ },
642
+ manifestTransforms: {
643
+ description: "One or more functions which will be applied sequentially against the\ngenerated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are\nalso specified, their corresponding transformations will be applied first.",
644
+ type: "array",
645
+ items: {}
646
+ },
647
+ maximumFileSizeToCacheInBytes: {
648
+ description: "This value can be used to determine the maximum size of files that will be\nprecached. This prevents you from inadvertently precaching very large files\nthat might have accidentally matched one of your patterns.",
649
+ "default": 2097152,
650
+ type: "number"
651
+ },
652
+ modifyURLPrefix: {
653
+ description: "An object mapping string prefixes to replacement string values. This can be\nused to, e.g., remove or add a path prefix from a manifest entry if your\nweb hosting setup doesn't match your local filesystem setup. As an\nalternative with more flexibility, you can use the `manifestTransforms`\noption and provide a function that modifies the entries in the manifest\nusing whatever logic you provide.\n\nExample usage:\n\n```\n// Replace a '/dist/' prefix with '/', and also prepend\n// '/static' to every URL.\nmodifyURLPrefix: {\n '/dist/': '/',\n '': '/static',\n}\n```",
654
+ type: "object",
655
+ additionalProperties: {
656
+ type: "string"
657
+ }
658
+ },
659
+ globFollow: {
660
+ description: "Determines whether or not symlinks are followed when generating the\nprecache manifest. For more information, see the definition of `follow` in\nthe `glob` [documentation](https://github.com/isaacs/node-glob#options).",
661
+ "default": true,
662
+ type: "boolean"
663
+ },
664
+ globIgnores: {
665
+ description: "A set of patterns matching files to always exclude when generating the\nprecache manifest. For more information, see the definition of `ignore` in\nthe `glob` [documentation](https://github.com/isaacs/node-glob#options).",
666
+ "default": [
667
+ "**/node_modules/**/*"
668
+ ],
669
+ type: "array",
670
+ items: {
671
+ type: "string"
672
+ }
673
+ },
674
+ globPatterns: {
675
+ description: "Files matching any of these patterns will be included in the precache\nmanifest. For more information, see the\n[`glob` primer](https://github.com/isaacs/node-glob#glob-primer).",
676
+ "default": [
677
+ "**/*.{js,css,html}"
678
+ ],
679
+ type: "array",
680
+ items: {
681
+ type: "string"
682
+ }
683
+ },
684
+ globStrict: {
685
+ description: "If true, an error reading a directory when generating a precache manifest\nwill cause the build to fail. If false, the problematic directory will be\nskipped. For more information, see the definition of `strict` in the `glob`\n[documentation](https://github.com/isaacs/node-glob#options).",
686
+ "default": true,
687
+ type: "boolean"
688
+ },
689
+ templatedURLs: {
690
+ description: "If a URL is rendered based on some server-side logic, its contents may\ndepend on multiple files or on some other unique string value. The keys in\nthis object are server-rendered URLs. If the values are an array of\nstrings, they will be interpreted as `glob` patterns, and the contents of\nany files matching the patterns will be used to uniquely version the URL.\nIf used with a single string, it will be interpreted as unique versioning\ninformation that you've generated for a given URL.",
691
+ type: "object",
692
+ additionalProperties: {
693
+ anyOf: [
694
+ {
695
+ type: "array",
696
+ items: {
697
+ type: "string"
698
+ }
699
+ },
700
+ {
701
+ type: "string"
702
+ }
703
+ ]
704
+ }
705
+ },
706
+ injectionPoint: {
707
+ description: "The string to find inside of the `swSrc` file. Once found, it will be\nreplaced by the generated precache manifest.",
708
+ "default": "self.__SW_MANIFEST",
709
+ type: "string"
710
+ },
711
+ swSrc: {
712
+ description: "The path and filename of the service worker file that will be read during\nthe build process, relative to the current working directory.",
713
+ type: "string"
714
+ },
715
+ swDest: {
716
+ description: "The path and filename of the service worker file that will be created by\nthe build process, relative to the current working directory. It must end\nin '.js'.",
717
+ type: "string"
718
+ },
719
+ globDirectory: {
720
+ description: "The local directory you wish to match `globPatterns` against. The path is\nrelative to the current directory.",
721
+ type: "string"
722
+ }
723
+ };
724
+ var required$1 = [
725
+ "globDirectory",
726
+ "swDest",
727
+ "swSrc"
728
+ ];
729
+ var definitions$1 = {
730
+ ManifestEntry: {
731
+ type: "object",
732
+ properties: {
733
+ integrity: {
734
+ type: "string"
735
+ },
736
+ revision: {
737
+ type: [
738
+ "null",
739
+ "string"
740
+ ]
741
+ },
742
+ url: {
743
+ type: "string"
744
+ }
745
+ },
746
+ additionalProperties: false,
747
+ required: [
748
+ "revision",
749
+ "url"
750
+ ]
751
+ },
752
+ "RegExp": {
753
+ type: "object",
754
+ properties: {
755
+ source: {
756
+ type: "string"
757
+ },
758
+ global: {
759
+ type: "boolean"
760
+ },
761
+ ignoreCase: {
762
+ type: "boolean"
763
+ },
764
+ multiline: {
765
+ type: "boolean"
766
+ },
767
+ lastIndex: {
768
+ type: "number"
769
+ },
770
+ flags: {
771
+ type: "string"
772
+ },
773
+ sticky: {
774
+ type: "boolean"
775
+ },
776
+ unicode: {
777
+ type: "boolean"
778
+ },
779
+ dotAll: {
780
+ type: "boolean"
781
+ }
782
+ },
783
+ additionalProperties: false,
784
+ required: [
785
+ "dotAll",
786
+ "flags",
787
+ "global",
788
+ "ignoreCase",
789
+ "lastIndex",
790
+ "multiline",
791
+ "source",
792
+ "sticky",
793
+ "unicode"
794
+ ]
795
+ }
796
+ };
797
+ var $schema$1 = "http://json-schema.org/draft-07/schema#";
798
+ var injectManifestOptionsSchema = {
799
+ additionalProperties: additionalProperties$1,
800
+ type: type$1,
801
+ properties: properties$1,
802
+ required: required$1,
803
+ definitions: definitions$1,
804
+ $schema: $schema$1
805
+ };
806
+
807
+ var additionalProperties = false;
808
+ var type = "object";
809
+ var properties = {
810
+ additionalPrecacheEntries: {
811
+ description: "A list of entries to be precached, in addition to any entries that are\ngenerated as part of the build configuration.",
812
+ type: "array",
813
+ items: {
814
+ anyOf: [
815
+ {
816
+ $ref: "#/definitions/ManifestEntry"
817
+ },
818
+ {
819
+ type: "string"
820
+ }
821
+ ]
822
+ }
823
+ },
824
+ dontCacheBustURLsMatching: {
825
+ description: "Assets that match this will be assumed to be uniquely versioned via their\nURL, and exempted from the normal HTTP cache-busting that's done when\npopulating the precache. While not required, it's recommended that if your\nexisting build process already inserts a `[hash]` value into each filename,\nyou provide a RegExp that will detect that, as it will reduce the bandwidth\nconsumed when precaching.",
826
+ $ref: "#/definitions/RegExp"
827
+ },
828
+ manifestTransforms: {
829
+ description: "One or more functions which will be applied sequentially against the\ngenerated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are\nalso specified, their corresponding transformations will be applied first.",
830
+ type: "array",
831
+ items: {}
832
+ },
833
+ maximumFileSizeToCacheInBytes: {
834
+ description: "This value can be used to determine the maximum size of files that will be\nprecached. This prevents you from inadvertently precaching very large files\nthat might have accidentally matched one of your patterns.",
835
+ "default": 2097152,
836
+ type: "number"
837
+ },
838
+ modifyURLPrefix: {
839
+ description: "An object mapping string prefixes to replacement string values. This can be\nused to, e.g., remove or add a path prefix from a manifest entry if your\nweb hosting setup doesn't match your local filesystem setup. As an\nalternative with more flexibility, you can use the `manifestTransforms`\noption and provide a function that modifies the entries in the manifest\nusing whatever logic you provide.\n\nExample usage:\n\n```\n// Replace a '/dist/' prefix with '/', and also prepend\n// '/static' to every URL.\nmodifyURLPrefix: {\n '/dist/': '/',\n '': '/static',\n}\n```",
840
+ type: "object",
841
+ additionalProperties: {
842
+ type: "string"
843
+ }
844
+ },
845
+ chunks: {
846
+ description: "One or more chunk names whose corresponding output files should be included\nin the precache manifest.",
847
+ type: "array",
848
+ items: {
849
+ type: "string"
850
+ }
851
+ },
852
+ exclude: {
853
+ description: "One or more specifiers used to exclude assets from the precache manifest.\nThis is interpreted following\n[the same rules](https://webpack.js.org/configuration/module/#condition)\nas `webpack`'s standard `exclude` option.\nIf not provided, the default value is `[/\\.map$/, /^manifest.*\\.js$]`.",
854
+ type: "array",
855
+ items: {}
856
+ },
857
+ excludeChunks: {
858
+ description: "One or more chunk names whose corresponding output files should be excluded\nfrom the precache manifest.",
859
+ type: "array",
860
+ items: {
861
+ type: "string"
862
+ }
863
+ },
864
+ include: {
865
+ description: "One or more specifiers used to include assets in the precache manifest.\nThis is interpreted following\n[the same rules](https://webpack.js.org/configuration/module/#condition)\nas `webpack`'s standard `include` option.",
866
+ type: "array",
867
+ items: {}
868
+ },
869
+ mode: {
870
+ description: "If set to 'production', then an optimized service worker bundle that\nexcludes debugging info will be produced. If not explicitly configured\nhere, the `mode` value configured in the current `webpack` compilation\nwill be used.",
871
+ type: [
872
+ "null",
873
+ "string"
874
+ ]
875
+ },
876
+ injectionPoint: {
877
+ description: "The string to find inside of the `swSrc` file. Once found, it will be\nreplaced by the generated precache manifest.",
878
+ "default": "self.__SW_MANIFEST",
879
+ type: "string"
880
+ },
881
+ swSrc: {
882
+ description: "The path and filename of the service worker file that will be read during\nthe build process, relative to the current working directory.",
883
+ type: "string"
884
+ },
885
+ compileSrc: {
886
+ description: "When `true` (the default), the `swSrc` file will be compiled by webpack.\nWhen `false`, compilation will not occur (and `webpackCompilationPlugins`\ncan't be used.) Set to `false` if you want to inject the manifest into,\ne.g., a JSON file.",
887
+ "default": true,
888
+ type: "boolean"
889
+ },
890
+ swDest: {
891
+ description: "The asset name of the service worker file that will be created by this\nplugin. If omitted, the name will be based on the `swSrc` name.",
892
+ type: "string"
893
+ },
894
+ webpackCompilationPlugins: {
895
+ description: "Optional `webpack` plugins that will be used when compiling the `swSrc`\ninput file. Only valid if `compileSrc` is `true`.",
896
+ type: "array",
897
+ items: {}
898
+ },
899
+ disablePrecacheManifest: {
900
+ description: "Whether the precache manifest should be set to `undefined`.",
901
+ "default": false,
902
+ type: "boolean"
903
+ }
904
+ };
905
+ var required = [
906
+ "swSrc"
907
+ ];
908
+ var definitions = {
909
+ ManifestEntry: {
910
+ type: "object",
911
+ properties: {
912
+ integrity: {
913
+ type: "string"
914
+ },
915
+ revision: {
916
+ type: [
917
+ "null",
918
+ "string"
919
+ ]
920
+ },
921
+ url: {
922
+ type: "string"
923
+ }
924
+ },
925
+ additionalProperties: false,
926
+ required: [
927
+ "revision",
928
+ "url"
929
+ ]
930
+ },
931
+ "RegExp": {
932
+ type: "object",
933
+ properties: {
934
+ source: {
935
+ type: "string"
936
+ },
937
+ global: {
938
+ type: "boolean"
939
+ },
940
+ ignoreCase: {
941
+ type: "boolean"
942
+ },
943
+ multiline: {
944
+ type: "boolean"
945
+ },
946
+ lastIndex: {
947
+ type: "number"
948
+ },
949
+ flags: {
950
+ type: "string"
951
+ },
952
+ sticky: {
953
+ type: "boolean"
954
+ },
955
+ unicode: {
956
+ type: "boolean"
957
+ },
958
+ dotAll: {
959
+ type: "boolean"
960
+ }
961
+ },
962
+ additionalProperties: false,
963
+ required: [
964
+ "dotAll",
965
+ "flags",
966
+ "global",
967
+ "ignoreCase",
968
+ "lastIndex",
969
+ "multiline",
970
+ "source",
971
+ "sticky",
972
+ "unicode"
973
+ ]
974
+ }
975
+ };
976
+ var $schema = "http://json-schema.org/draft-07/schema#";
977
+ var webpackInjectManifestOptionsSchema = {
978
+ additionalProperties: additionalProperties,
979
+ type: type,
980
+ properties: properties,
981
+ required: required,
982
+ definitions: definitions,
983
+ $schema: $schema
984
+ };
985
+
986
+ const optionsSchemas = {
987
+ GetManifest: getManifestOptionsSchema,
988
+ InjectManifest: injectManifestOptionsSchema,
989
+ WebpackInjectManifest: webpackInjectManifestOptionsSchema
990
+ };
991
+
992
+ const ajv = new Ajv({
993
+ useDefaults: true
994
+ });
995
+ const DEFAULT_EXCLUDE_VALUE = [
996
+ /\.map$/,
997
+ /^manifest.*\.js$/
998
+ ];
999
+ class SerwistConfigError extends Error {
1000
+ constructor(message){
1001
+ super(message);
1002
+ Object.setPrototypeOf(this, new.target.prototype);
1003
+ }
1004
+ }
1005
+ // Some methods need to do follow-up validation using the JSON schema,
1006
+ // so return both the validated options and then schema.
1007
+ function validate(input, methodName) {
1008
+ // Don't mutate input: https://github.com/GoogleChrome/workbox/issues/2158
1009
+ const inputCopy = Object.assign({}, input);
1010
+ const jsonSchema = optionsSchemas[methodName];
1011
+ const validate = ajv.compile(jsonSchema);
1012
+ if (validate(inputCopy)) {
1013
+ // All methods support manifestTransforms, so validate it here.
1014
+ ensureValidManifestTransforms(inputCopy);
1015
+ return [
1016
+ inputCopy,
1017
+ jsonSchema
1018
+ ];
1019
+ }
1020
+ const betterErrors = betterAjvErrors({
1021
+ basePath: methodName,
1022
+ data: input,
1023
+ errors: validate.errors,
1024
+ // This is needed as JSONSchema6 is expected, but JSONSchemaType works.
1025
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1026
+ schema: jsonSchema
1027
+ });
1028
+ const messages = betterErrors.map((err)=>oneLine`[${err.path}] ${err.message}.
1029
+ ${err.suggestion ? err.suggestion : ""}`);
1030
+ throw new SerwistConfigError(messages.join("\n\n"));
1031
+ }
1032
+ function ensureValidManifestTransforms(options) {
1033
+ if ("manifestTransforms" in options && !(Array.isArray(options.manifestTransforms) && options.manifestTransforms.every((item)=>typeof item === "function"))) {
1034
+ throw new SerwistConfigError(errors["manifest-transforms"]);
1035
+ }
1036
+ }
1037
+ function validateGetManifestOptions(input) {
1038
+ const [validatedOptions] = validate(input, "GetManifest");
1039
+ return validatedOptions;
1040
+ }
1041
+ function validateInjectManifestOptions(input) {
1042
+ const [validatedOptions] = validate(input, "InjectManifest");
1043
+ return validatedOptions;
1044
+ }
1045
+ function validateWebpackInjectManifestOptions(input) {
1046
+ const inputWithExcludeDefault = Object.assign({
1047
+ // Make a copy, as exclude can be mutated when used.
1048
+ exclude: Array.from(DEFAULT_EXCLUDE_VALUE)
1049
+ }, input);
1050
+ const [validatedOptions] = validate(inputWithExcludeDefault, "WebpackInjectManifest");
1051
+ return validatedOptions;
1052
+ }
1053
+
1054
+ /**
1055
+ * This method returns a list of URLs to precache, referred to as a "precache
1056
+ * manifest", along with details about the number of entries and their size,
1057
+ * based on the options you provide.
1058
+ *
1059
+ * ```
1060
+ * // The following lists some common options; see the rest of the documentation
1061
+ * // for the full set of options and defaults.
1062
+ * const {count, manifestEntries, size, warnings} = await getManifest({
1063
+ * dontCacheBustURLsMatching: [new RegExp('...')],
1064
+ * globDirectory: '...',
1065
+ * globPatterns: ['...', '...'],
1066
+ * maximumFileSizeToCacheInBytes: ...,
1067
+ * });
1068
+ * ```
1069
+ */ async function getManifest(config) {
1070
+ const options = validateGetManifestOptions(config);
1071
+ return await getFileManifestEntries(options);
1072
+ }
1073
+
1074
+ /*
1075
+ Copyright 2022 Google LLC
1076
+
1077
+ Use of this source code is governed by an MIT-style
1078
+ license that can be found in the LICENSE file or at
1079
+ https://opensource.org/licenses/MIT.
1080
+ */ // Adapted from https://github.com/lydell/source-map-url/blob/master/source-map-url.js
1081
+ // See https://github.com/GoogleChrome/workbox/issues/3019
1082
+ const innerRegex = /[#@] sourceMappingURL=([^\s'"]*)/;
1083
+ const regex = RegExp("(?:" + "/\\*" + "(?:\\s*\r?\n(?://)?)?" + "(?:" + innerRegex.source + ")" + "\\s*" + "\\*/" + "|" + "//(?:" + innerRegex.source + ")" + ")" + "\\s*");
1084
+ function getSourceMapURL(srcContents) {
1085
+ const match = srcContents.match(regex);
1086
+ return match ? match[1] || match[2] || "" : null;
1087
+ }
1088
+
1089
+ function rebasePath({ baseDirectory, file }) {
1090
+ // The initial path is relative to the current directory, so make it absolute.
1091
+ const absolutePath = upath.resolve(file);
1092
+ // Convert the absolute path so that it's relative to the baseDirectory.
1093
+ const relativePath = upath.relative(baseDirectory, absolutePath);
1094
+ // Remove any leading ./ as it won't work in a glob pattern.
1095
+ const normalizedPath = upath.normalize(relativePath);
1096
+ return normalizedPath;
1097
+ }
1098
+
1099
+ /**
1100
+ * Adapted from https://github.com/nsams/sourcemap-aware-replace, with modern
1101
+ * JavaScript updates, along with additional properties copied from originalMap.
1102
+ *
1103
+ * @param options
1104
+ * @returns An object containing both
1105
+ * originalSource with the replacement applied, and the modified originalMap.
1106
+ * @private
1107
+ */ async function replaceAndUpdateSourceMap({ jsFilename, originalMap, originalSource, replaceString, searchString }) {
1108
+ const generator = new SourceMapGenerator({
1109
+ file: jsFilename
1110
+ });
1111
+ const consumer = await new SourceMapConsumer(originalMap);
1112
+ let pos;
1113
+ let src = originalSource;
1114
+ const replacements = [];
1115
+ let lineNum = 0;
1116
+ let filePos = 0;
1117
+ const lines = src.split("\n");
1118
+ for (let line of lines){
1119
+ lineNum++;
1120
+ let searchPos = 0;
1121
+ while((pos = line.indexOf(searchString, searchPos)) !== -1){
1122
+ src = src.substring(0, filePos + pos) + replaceString + src.substring(filePos + pos + searchString.length);
1123
+ line = line.substring(0, pos) + replaceString + line.substring(pos + searchString.length);
1124
+ replacements.push({
1125
+ line: lineNum,
1126
+ column: pos
1127
+ });
1128
+ searchPos = pos + replaceString.length;
1129
+ }
1130
+ filePos += line.length + 1;
1131
+ }
1132
+ replacements.reverse();
1133
+ consumer.eachMapping((mapping)=>{
1134
+ for (const replacement of replacements){
1135
+ if (replacement.line === mapping.generatedLine && mapping.generatedColumn > replacement.column) {
1136
+ const offset = searchString.length - replaceString.length;
1137
+ mapping.generatedColumn -= offset;
1138
+ }
1139
+ }
1140
+ if (mapping.source) {
1141
+ const newMapping = {
1142
+ generated: {
1143
+ line: mapping.generatedLine,
1144
+ column: mapping.generatedColumn
1145
+ },
1146
+ original: {
1147
+ line: mapping.originalLine,
1148
+ column: mapping.originalColumn
1149
+ },
1150
+ source: mapping.source
1151
+ };
1152
+ return generator.addMapping(newMapping);
1153
+ }
1154
+ return mapping;
1155
+ });
1156
+ consumer.destroy();
1157
+ // JSON.parse returns any.
1158
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1159
+ const updatedSourceMap = Object.assign(JSON.parse(generator.toString()), {
1160
+ names: originalMap.names,
1161
+ sourceRoot: originalMap.sourceRoot,
1162
+ sources: originalMap.sources,
1163
+ sourcesContent: originalMap.sourcesContent
1164
+ });
1165
+ return {
1166
+ map: JSON.stringify(updatedSourceMap),
1167
+ source: src
1168
+ };
1169
+ }
1170
+
1171
+ function translateURLToSourcemapPaths(url, swSrc, swDest) {
1172
+ let destPath = undefined;
1173
+ let srcPath = undefined;
1174
+ let warning = undefined;
1175
+ if (url && !url.startsWith("data:")) {
1176
+ const possibleSrcPath = upath.resolve(upath.dirname(swSrc), url);
1177
+ if (fse.existsSync(possibleSrcPath)) {
1178
+ srcPath = possibleSrcPath;
1179
+ destPath = upath.resolve(upath.dirname(swDest), url);
1180
+ } else {
1181
+ warning = `${errors["cant-find-sourcemap"]} ${possibleSrcPath}`;
1182
+ }
1183
+ }
1184
+ return {
1185
+ destPath,
1186
+ srcPath,
1187
+ warning
1188
+ };
1189
+ }
1190
+
1191
+ /**
1192
+ * This method creates a list of URLs to precache, referred to as a "precache
1193
+ * manifest", based on the options you provide.
1194
+ *
1195
+ * The manifest is injected into the `swSrc` file, and the placeholder string
1196
+ * `injectionPoint` determines where in the file the manifest should go.
1197
+ *
1198
+ * The final service worker file, with the manifest injected, is written to
1199
+ * disk at `swDest`.
1200
+ *
1201
+ * This method will not compile or bundle your `swSrc` file; it just handles
1202
+ * injecting the manifest.
1203
+ *
1204
+ * ```
1205
+ * // The following lists some common options; see the rest of the documentation
1206
+ * // for the full set of options and defaults.
1207
+ * const {count, size, warnings} = await injectManifest({
1208
+ * dontCacheBustURLsMatching: [new RegExp('...')],
1209
+ * globDirectory: '...',
1210
+ * globPatterns: ['...', '...'],
1211
+ * maximumFileSizeToCacheInBytes: ...,
1212
+ * swDest: '...',
1213
+ * swSrc: '...',
1214
+ * });
1215
+ * ```
1216
+ */ async function injectManifest(config) {
1217
+ const options = validateInjectManifestOptions(config);
1218
+ // Make sure we leave swSrc and swDest out of the precache manifest.
1219
+ for (const file of [
1220
+ options.swSrc,
1221
+ options.swDest
1222
+ ]){
1223
+ options.globIgnores.push(rebasePath({
1224
+ file,
1225
+ baseDirectory: options.globDirectory
1226
+ }));
1227
+ }
1228
+ const globalRegexp = new RegExp(escapeRegExp(options.injectionPoint), "g");
1229
+ const { count, size, manifestEntries, warnings } = await getFileManifestEntries(options);
1230
+ let swFileContents;
1231
+ try {
1232
+ swFileContents = await fse.readFile(options.swSrc, "utf8");
1233
+ } catch (error) {
1234
+ throw new Error(`${errors["invalid-sw-src"]} ${error instanceof Error && error.message ? error.message : ""}`);
1235
+ }
1236
+ const injectionResults = swFileContents.match(globalRegexp);
1237
+ // See https://github.com/GoogleChrome/workbox/issues/2230
1238
+ const injectionPoint = options.injectionPoint ? options.injectionPoint : "";
1239
+ if (!injectionResults) {
1240
+ if (upath.resolve(options.swSrc) === upath.resolve(options.swDest)) {
1241
+ throw new Error(`${errors["same-src-and-dest"]} ${injectionPoint}`);
1242
+ }
1243
+ throw new Error(`${errors["injection-point-not-found"]} ${injectionPoint}`);
1244
+ }
1245
+ assert(injectionResults.length === 1, `${errors["multiple-injection-points"]} ${injectionPoint}`);
1246
+ const manifestString = stringify(manifestEntries);
1247
+ const filesToWrite = {};
1248
+ const url = getSourceMapURL(swFileContents);
1249
+ // See https://github.com/GoogleChrome/workbox/issues/2957
1250
+ const { destPath, srcPath, warning } = translateURLToSourcemapPaths(url, options.swSrc, options.swDest);
1251
+ if (warning) {
1252
+ warnings.push(warning);
1253
+ }
1254
+ // If our swSrc file contains a sourcemap, we would invalidate that
1255
+ // mapping if we just replaced injectionPoint with the stringified manifest.
1256
+ // Instead, we need to update the swDest contents as well as the sourcemap
1257
+ // (assuming it's a real file, not a data: URL) at the same time.
1258
+ // See https://github.com/GoogleChrome/workbox/issues/2235
1259
+ // and https://github.com/GoogleChrome/workbox/issues/2648
1260
+ if (srcPath && destPath) {
1261
+ const originalMap = await fse.readJSON(srcPath, {
1262
+ encoding: "utf8"
1263
+ });
1264
+ const { map, source } = await replaceAndUpdateSourceMap({
1265
+ originalMap,
1266
+ jsFilename: upath.basename(options.swDest),
1267
+ originalSource: swFileContents,
1268
+ replaceString: manifestString,
1269
+ searchString: options.injectionPoint
1270
+ });
1271
+ filesToWrite[options.swDest] = source;
1272
+ filesToWrite[destPath] = map;
1273
+ } else {
1274
+ // If there's no sourcemap associated with swSrc, a simple string
1275
+ // replacement will suffice.
1276
+ filesToWrite[options.swDest] = swFileContents.replace(globalRegexp, manifestString);
1277
+ }
1278
+ for (const [file, contents] of Object.entries(filesToWrite)){
1279
+ try {
1280
+ await fse.mkdirp(upath.dirname(file));
1281
+ } catch (error) {
1282
+ throw new Error(errors["unable-to-make-sw-directory"] + ` '${error instanceof Error && error.message ? error.message : ""}'`);
1283
+ }
1284
+ await fse.writeFile(file, contents);
1285
+ }
1286
+ return {
1287
+ count,
1288
+ size,
1289
+ warnings,
1290
+ // Use upath.resolve() to make all the paths absolute.
1291
+ filePaths: Object.keys(filesToWrite).map((f)=>upath.resolve(f))
1292
+ };
1293
+ }
1294
+
1295
+ export { escapeRegExp, getManifest, getSourceMapURL, injectManifest, replaceAndUpdateSourceMap, transformManifest, validateWebpackInjectManifestOptions };