@serwist/build 8.4.3 → 9.0.0-preview.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.
- package/dist/chunks/getManifest.js +6 -0
- package/dist/chunks/glob.js +60 -0
- package/dist/chunks/injectManifest.js +23 -0
- package/dist/chunks/serwist-config-error.js +57 -0
- package/dist/chunks/vite.js +7 -0
- package/dist/chunks/webpack.js +34 -0
- package/dist/get-manifest.d.ts +2 -1
- package/dist/get-manifest.d.ts.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -813
- package/dist/index.next.d.ts +3 -0
- package/dist/index.next.d.ts.map +1 -0
- package/dist/index.next.js +35 -0
- package/dist/inject-manifest.d.ts +2 -1
- package/dist/inject-manifest.d.ts.map +1 -0
- package/dist/lib/additional-precache-entries-transform.d.ts +6 -5
- package/dist/lib/additional-precache-entries-transform.d.ts.map +1 -0
- package/dist/lib/errors.d.ts +1 -2
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/escape-regexp.d.ts +2 -1
- package/dist/lib/escape-regexp.d.ts.map +1 -0
- package/dist/lib/get-composite-details.d.ts +2 -1
- package/dist/lib/get-composite-details.d.ts.map +1 -0
- package/dist/lib/get-file-details.d.ts +2 -1
- package/dist/lib/get-file-details.d.ts.map +1 -0
- package/dist/lib/get-file-hash.d.ts +1 -0
- package/dist/lib/get-file-hash.d.ts.map +1 -0
- package/dist/lib/get-file-manifest-entries.d.ts +1 -0
- package/dist/lib/get-file-manifest-entries.d.ts.map +1 -0
- package/dist/lib/get-file-size.d.ts +1 -0
- package/dist/lib/get-file-size.d.ts.map +1 -0
- package/dist/lib/get-source-map-url.d.ts +1 -0
- package/dist/lib/get-source-map-url.d.ts.map +1 -0
- package/dist/lib/get-string-details.d.ts +1 -0
- package/dist/lib/get-string-details.d.ts.map +1 -0
- package/dist/lib/get-string-hash.d.ts +1 -0
- package/dist/lib/get-string-hash.d.ts.map +1 -0
- package/dist/lib/maximum-size-transform.d.ts +1 -0
- package/dist/lib/maximum-size-transform.d.ts.map +1 -0
- package/dist/lib/modify-url-prefix-transform.d.ts +1 -0
- package/dist/lib/modify-url-prefix-transform.d.ts.map +1 -0
- package/dist/lib/no-revision-for-urls-matching-transform.d.ts +1 -0
- package/dist/lib/no-revision-for-urls-matching-transform.d.ts.map +1 -0
- package/dist/lib/rebase-path.d.ts +1 -0
- package/dist/lib/rebase-path.d.ts.map +1 -0
- package/dist/lib/replace-and-update-source-map.d.ts +1 -0
- package/dist/lib/replace-and-update-source-map.d.ts.map +1 -0
- package/dist/lib/serwist-config-error.d.ts +7 -0
- package/dist/lib/serwist-config-error.d.ts.map +1 -0
- package/dist/lib/transform-manifest.d.ts +2 -1
- package/dist/lib/transform-manifest.d.ts.map +1 -0
- package/dist/lib/translate-url-to-sourcemap-paths.d.ts +1 -0
- package/dist/lib/translate-url-to-sourcemap-paths.d.ts.map +1 -0
- package/dist/lib/validate-next-options.d.ts +3 -0
- package/dist/lib/validate-next-options.d.ts.map +1 -0
- package/dist/lib/validate-options.d.ts +6 -8
- package/dist/lib/validate-options.d.ts.map +1 -0
- package/dist/schema/base.d.ts +169 -0
- package/dist/schema/base.d.ts.map +1 -0
- package/dist/schema/getManifest.d.ts +187 -0
- package/dist/schema/getManifest.d.ts.map +1 -0
- package/dist/schema/glob.d.ts +35 -0
- package/dist/schema/glob.d.ts.map +1 -0
- package/dist/schema/injectManifest.d.ts +206 -0
- package/dist/schema/injectManifest.d.ts.map +1 -0
- package/dist/schema/manifestEntry.d.ts +15 -0
- package/dist/schema/manifestEntry.d.ts.map +1 -0
- package/dist/schema/manifestTransform.d.ts +121 -0
- package/dist/schema/manifestTransform.d.ts.map +1 -0
- package/dist/schema/next.d.ts +243 -0
- package/dist/schema/next.d.ts.map +1 -0
- package/dist/schema/swDest.d.ts +16 -0
- package/dist/schema/swDest.d.ts.map +1 -0
- package/dist/schema/validationErrorMap.d.ts +3 -0
- package/dist/schema/validationErrorMap.d.ts.map +1 -0
- package/dist/schema/vite.d.ts +196 -0
- package/dist/schema/vite.d.ts.map +1 -0
- package/dist/schema/webpack.d.ts +231 -0
- package/dist/schema/webpack.d.ts.map +1 -0
- package/dist/types.d.ts +147 -175
- package/dist/types.d.ts.map +1 -0
- package/package.json +42 -28
- package/src/_types.js +112 -0
- package/src/get-manifest.ts +33 -0
- package/src/index.next.ts +3 -0
- package/{dist/index.d.cts → src/index.ts} +27 -2
- package/src/inject-manifest.ts +140 -0
- package/src/lib/additional-precache-entries-transform.ts +58 -0
- package/src/lib/errors.ts +99 -0
- package/src/lib/escape-regexp.ts +12 -0
- package/src/lib/get-composite-details.ts +31 -0
- package/src/lib/get-file-details.ts +68 -0
- package/src/lib/get-file-hash.ts +21 -0
- package/src/lib/get-file-manifest-entries.ts +126 -0
- package/src/lib/get-file-size.ts +23 -0
- package/src/lib/get-source-map-url.ts +17 -0
- package/src/lib/get-string-details.ts +18 -0
- package/src/lib/get-string-hash.ts +15 -0
- package/src/lib/maximum-size-transform.ts +29 -0
- package/src/lib/modify-url-prefix-transform.ts +55 -0
- package/src/lib/no-revision-for-urls-matching-transform.ts +32 -0
- package/src/lib/rebase-path.ts +22 -0
- package/src/lib/replace-and-update-source-map.ts +122 -0
- package/src/lib/serwist-config-error.ts +6 -0
- package/src/lib/transform-manifest.ts +158 -0
- package/src/lib/translate-url-to-sourcemap-paths.ts +38 -0
- package/src/lib/validate-next-options.ts +14 -0
- package/src/lib/validate-options.ts +47 -0
- package/src/schema/base.ts +16 -0
- package/src/schema/getManifest.ts +14 -0
- package/src/schema/glob.ts +41 -0
- package/src/schema/injectManifest.ts +23 -0
- package/src/schema/manifestEntry.ts +9 -0
- package/src/schema/manifestTransform.ts +15 -0
- package/src/schema/next.ts +35 -0
- package/src/schema/swDest.ts +14 -0
- package/src/schema/validationErrorMap.ts +36 -0
- package/src/schema/vite.ts +18 -0
- package/src/schema/webpack.ts +47 -0
- package/src/types.ts +407 -0
- package/dist/index.cjs +0 -1632
- package/dist/lib/cdn-utils.d.ts +0 -1
- package/dist/lib/copy-serwist-libraries.d.ts +0 -16
- package/dist/schema/index.d.ts +0 -605
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2021 Google LLC
|
|
3
|
+
|
|
4
|
+
Use of this source code is governed by an MIT-style
|
|
5
|
+
license that can be found in the LICENSE file or at
|
|
6
|
+
https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import assert from "assert";
|
|
10
|
+
|
|
11
|
+
import type { FileDetails, GetManifestOptions, GetManifestResult } from "../types.js";
|
|
12
|
+
import { errors } from "./errors.js";
|
|
13
|
+
import { getCompositeDetails } from "./get-composite-details.js";
|
|
14
|
+
import { getFileDetails } from "./get-file-details.js";
|
|
15
|
+
import { getStringDetails } from "./get-string-details.js";
|
|
16
|
+
import { transformManifest } from "./transform-manifest.js";
|
|
17
|
+
|
|
18
|
+
export async function getFileManifestEntries({
|
|
19
|
+
additionalPrecacheEntries,
|
|
20
|
+
dontCacheBustURLsMatching,
|
|
21
|
+
globDirectory,
|
|
22
|
+
globFollow,
|
|
23
|
+
globIgnores,
|
|
24
|
+
globPatterns = [],
|
|
25
|
+
globStrict,
|
|
26
|
+
manifestTransforms,
|
|
27
|
+
maximumFileSizeToCacheInBytes,
|
|
28
|
+
modifyURLPrefix,
|
|
29
|
+
templatedURLs,
|
|
30
|
+
disablePrecacheManifest,
|
|
31
|
+
}: GetManifestOptions): Promise<GetManifestResult> {
|
|
32
|
+
if (disablePrecacheManifest) {
|
|
33
|
+
return {
|
|
34
|
+
count: 0,
|
|
35
|
+
size: 0,
|
|
36
|
+
manifestEntries: undefined,
|
|
37
|
+
warnings: [],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const warnings: string[] = [];
|
|
42
|
+
const allFileDetails = new Map<string, FileDetails>();
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
for (const globPattern of globPatterns) {
|
|
46
|
+
const { globbedFileDetails, warning } = getFileDetails({
|
|
47
|
+
globDirectory,
|
|
48
|
+
globFollow,
|
|
49
|
+
globIgnores,
|
|
50
|
+
globPattern,
|
|
51
|
+
globStrict,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (warning) {
|
|
55
|
+
warnings.push(warning);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
for (const details of globbedFileDetails) {
|
|
59
|
+
if (details && !allFileDetails.has(details.file)) {
|
|
60
|
+
allFileDetails.set(details.file, details);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} catch (error) {
|
|
65
|
+
// If there's an exception thrown while globbing, then report
|
|
66
|
+
// it back as a warning, and don't consider it fatal.
|
|
67
|
+
if (error instanceof Error && error.message) {
|
|
68
|
+
warnings.push(error.message);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (templatedURLs) {
|
|
73
|
+
for (const url of Object.keys(templatedURLs)) {
|
|
74
|
+
assert(!allFileDetails.has(url), errors["templated-url-matches-glob"]);
|
|
75
|
+
|
|
76
|
+
const dependencies = templatedURLs[url];
|
|
77
|
+
if (Array.isArray(dependencies)) {
|
|
78
|
+
const details = dependencies.reduce<FileDetails[]>((previous, globPattern) => {
|
|
79
|
+
try {
|
|
80
|
+
const { globbedFileDetails, warning } = getFileDetails({
|
|
81
|
+
globDirectory,
|
|
82
|
+
globFollow,
|
|
83
|
+
globIgnores,
|
|
84
|
+
globPattern,
|
|
85
|
+
globStrict,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (warning) {
|
|
89
|
+
warnings.push(warning);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return previous.concat(globbedFileDetails);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
const debugObj: { [key: string]: string[] } = {};
|
|
95
|
+
debugObj[url] = dependencies;
|
|
96
|
+
throw new Error(
|
|
97
|
+
`${errors["bad-template-urls-asset"]} ` +
|
|
98
|
+
`'${globPattern}' from '${JSON.stringify(debugObj)}':\n` +
|
|
99
|
+
`${error instanceof Error ? error.toString() : ""}`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}, []);
|
|
103
|
+
if (details.length === 0) {
|
|
104
|
+
throw new Error(`${errors["bad-template-urls-asset"]} The glob ` + `pattern '${dependencies.toString()}' did not match anything.`);
|
|
105
|
+
}
|
|
106
|
+
allFileDetails.set(url, getCompositeDetails(url, details));
|
|
107
|
+
} else if (typeof dependencies === "string") {
|
|
108
|
+
allFileDetails.set(url, getStringDetails(url, dependencies));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const transformedManifest = await transformManifest({
|
|
114
|
+
additionalPrecacheEntries,
|
|
115
|
+
dontCacheBustURLsMatching,
|
|
116
|
+
manifestTransforms,
|
|
117
|
+
maximumFileSizeToCacheInBytes,
|
|
118
|
+
modifyURLPrefix,
|
|
119
|
+
fileDetails: Array.from(allFileDetails.values()),
|
|
120
|
+
disablePrecacheManifest,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
transformedManifest.warnings.push(...warnings);
|
|
124
|
+
|
|
125
|
+
return transformedManifest;
|
|
126
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2018 Google LLC
|
|
3
|
+
|
|
4
|
+
Use of this source code is governed by an MIT-style
|
|
5
|
+
license that can be found in the LICENSE file or at
|
|
6
|
+
https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fse from "fs-extra";
|
|
10
|
+
|
|
11
|
+
import { errors } from "./errors.js";
|
|
12
|
+
|
|
13
|
+
export function getFileSize(file: string): number | null {
|
|
14
|
+
try {
|
|
15
|
+
const stat = fse.statSync(file);
|
|
16
|
+
if (!stat.isFile()) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return stat.size;
|
|
20
|
+
} catch (err) {
|
|
21
|
+
throw new Error(`${errors["unable-to-get-file-size"]} '${err instanceof Error && err.message ? err.message : ""}'`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2022 Google LLC
|
|
3
|
+
|
|
4
|
+
Use of this source code is governed by an MIT-style
|
|
5
|
+
license that can be found in the LICENSE file or at
|
|
6
|
+
https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// Adapted from https://github.com/lydell/source-map-url/blob/master/source-map-url.js
|
|
10
|
+
// See https://github.com/GoogleChrome/workbox/issues/3019
|
|
11
|
+
const innerRegex = /[#@] sourceMappingURL=([^\s'"]*)/;
|
|
12
|
+
const regex = RegExp(`(?:/\\*(?:\\s*\r?\n(?://)?)?(?:${innerRegex.source})\\s*\\*/|//(?:${innerRegex.source}))\\s*`);
|
|
13
|
+
|
|
14
|
+
export function getSourceMapURL(srcContents: string): string | null {
|
|
15
|
+
const match = srcContents.match(regex);
|
|
16
|
+
return match ? match[1] || match[2] || "" : null;
|
|
17
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2018 Google LLC
|
|
3
|
+
|
|
4
|
+
Use of this source code is governed by an MIT-style
|
|
5
|
+
license that can be found in the LICENSE file or at
|
|
6
|
+
https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { FileDetails } from "../types.js";
|
|
10
|
+
import { getStringHash } from "./get-string-hash.js";
|
|
11
|
+
|
|
12
|
+
export function getStringDetails(url: string, str: string): FileDetails {
|
|
13
|
+
return {
|
|
14
|
+
file: url,
|
|
15
|
+
hash: getStringHash(str),
|
|
16
|
+
size: str.length,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2018 Google LLC
|
|
3
|
+
|
|
4
|
+
Use of this source code is governed by an MIT-style
|
|
5
|
+
license that can be found in the LICENSE file or at
|
|
6
|
+
https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import crypto from "crypto";
|
|
10
|
+
|
|
11
|
+
export function getStringHash(input: crypto.BinaryLike): string {
|
|
12
|
+
const md5 = crypto.createHash("md5");
|
|
13
|
+
md5.update(input);
|
|
14
|
+
return md5.digest("hex");
|
|
15
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2018 Google LLC
|
|
3
|
+
|
|
4
|
+
Use of this source code is governed by an MIT-style
|
|
5
|
+
license that can be found in the LICENSE file or at
|
|
6
|
+
https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import prettyBytes from "pretty-bytes";
|
|
10
|
+
|
|
11
|
+
import type { ManifestTransform } from "../types.js";
|
|
12
|
+
|
|
13
|
+
export function maximumSizeTransform(maximumFileSizeToCacheInBytes: number): ManifestTransform {
|
|
14
|
+
return (originalManifest) => {
|
|
15
|
+
const warnings: string[] = [];
|
|
16
|
+
const manifest = originalManifest.filter((entry) => {
|
|
17
|
+
if (entry.size <= maximumFileSizeToCacheInBytes) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
warnings.push(
|
|
22
|
+
`${entry.url} is ${prettyBytes(entry.size)}, and won't be precached. Configure maximumFileSizeToCacheInBytes to change this limit.`,
|
|
23
|
+
);
|
|
24
|
+
return false;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return { manifest, warnings };
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2018 Google LLC
|
|
3
|
+
|
|
4
|
+
Use of this source code is governed by an MIT-style
|
|
5
|
+
license that can be found in the LICENSE file or at
|
|
6
|
+
https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ManifestTransform } from "../types.js";
|
|
10
|
+
import { errors } from "./errors.js";
|
|
11
|
+
import { escapeRegExp } from "./escape-regexp.js";
|
|
12
|
+
|
|
13
|
+
export function modifyURLPrefixTransform(modifyURLPrefix: { [key: string]: string }): ManifestTransform {
|
|
14
|
+
if (!modifyURLPrefix || typeof modifyURLPrefix !== "object" || Array.isArray(modifyURLPrefix)) {
|
|
15
|
+
throw new Error(errors["modify-url-prefix-bad-prefixes"]);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// If there are no entries in modifyURLPrefix, just return an identity
|
|
19
|
+
// function as a shortcut.
|
|
20
|
+
if (Object.keys(modifyURLPrefix).length === 0) {
|
|
21
|
+
return (manifest) => {
|
|
22
|
+
return { manifest };
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
for (const key of Object.keys(modifyURLPrefix)) {
|
|
27
|
+
if (typeof modifyURLPrefix[key] !== "string") {
|
|
28
|
+
throw new Error(errors["modify-url-prefix-bad-prefixes"]);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Escape the user input so it's safe to use in a regex.
|
|
33
|
+
const safeModifyURLPrefixes = Object.keys(modifyURLPrefix).map(escapeRegExp);
|
|
34
|
+
// Join all the `modifyURLPrefix` keys so a single regex can be used.
|
|
35
|
+
const prefixMatchesStrings = safeModifyURLPrefixes.join("|");
|
|
36
|
+
// Add `^` to the front the prefix matches so it only matches the start of
|
|
37
|
+
// a string.
|
|
38
|
+
const modifyRegex = new RegExp(`^(${prefixMatchesStrings})`);
|
|
39
|
+
|
|
40
|
+
return (originalManifest) => {
|
|
41
|
+
const manifest = originalManifest.map((entry) => {
|
|
42
|
+
if (typeof entry.url !== "string") {
|
|
43
|
+
throw new Error(errors["manifest-entry-bad-url"]);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
entry.url = entry.url.replace(modifyRegex, (match) => {
|
|
47
|
+
return modifyURLPrefix[match];
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return entry;
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return { manifest };
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2018 Google LLC
|
|
3
|
+
|
|
4
|
+
Use of this source code is governed by an MIT-style
|
|
5
|
+
license that can be found in the LICENSE file or at
|
|
6
|
+
https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ManifestTransform } from "../types.js";
|
|
10
|
+
import { errors } from "./errors.js";
|
|
11
|
+
|
|
12
|
+
export function noRevisionForURLsMatchingTransform(regexp: RegExp): ManifestTransform {
|
|
13
|
+
if (!(regexp instanceof RegExp)) {
|
|
14
|
+
throw new Error(errors["invalid-dont-cache-bust"]);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return (originalManifest) => {
|
|
18
|
+
const manifest = originalManifest.map((entry) => {
|
|
19
|
+
if (typeof entry.url !== "string") {
|
|
20
|
+
throw new Error(errors["manifest-entry-bad-url"]);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (entry.url.match(regexp)) {
|
|
24
|
+
entry.revision = null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return entry;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return { manifest };
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2019 Google LLC
|
|
3
|
+
|
|
4
|
+
Use of this source code is governed by an MIT-style
|
|
5
|
+
license that can be found in the LICENSE file or at
|
|
6
|
+
https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import upath from "upath";
|
|
10
|
+
|
|
11
|
+
export function rebasePath({ baseDirectory, file }: { baseDirectory: string; file: string }): string {
|
|
12
|
+
// The initial path is relative to the current directory, so make it absolute.
|
|
13
|
+
const absolutePath = upath.resolve(file);
|
|
14
|
+
|
|
15
|
+
// Convert the absolute path so that it's relative to the baseDirectory.
|
|
16
|
+
const relativePath = upath.relative(baseDirectory, absolutePath);
|
|
17
|
+
|
|
18
|
+
// Remove any leading ./ as it won't work in a glob pattern.
|
|
19
|
+
const normalizedPath = upath.normalize(relativePath);
|
|
20
|
+
|
|
21
|
+
return normalizedPath;
|
|
22
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2019 Google LLC
|
|
3
|
+
|
|
4
|
+
Use of this source code is governed by an MIT-style
|
|
5
|
+
license that can be found in the LICENSE file or at
|
|
6
|
+
https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { RawSourceMap } from "source-map";
|
|
10
|
+
import { SourceMapConsumer, SourceMapGenerator } from "source-map";
|
|
11
|
+
|
|
12
|
+
interface ReplaceAndUpdateSourceMapOptions {
|
|
13
|
+
/**
|
|
14
|
+
* The name for the file whose contents
|
|
15
|
+
* correspond to originalSource.
|
|
16
|
+
*/
|
|
17
|
+
jsFilename: string;
|
|
18
|
+
/**
|
|
19
|
+
* The sourcemap for originalSource,
|
|
20
|
+
* prior to any replacements.
|
|
21
|
+
*/
|
|
22
|
+
originalMap: RawSourceMap;
|
|
23
|
+
/**
|
|
24
|
+
* The source code, prior to any
|
|
25
|
+
* replacements.
|
|
26
|
+
*/
|
|
27
|
+
originalSource: string;
|
|
28
|
+
/**
|
|
29
|
+
* A string to swap in for searchString.
|
|
30
|
+
*/
|
|
31
|
+
replaceString: string;
|
|
32
|
+
/**
|
|
33
|
+
* A string in originalSource to replace.
|
|
34
|
+
* Only the first occurrence will be replaced.
|
|
35
|
+
*/
|
|
36
|
+
searchString: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Adapted from https://github.com/nsams/sourcemap-aware-replace, with modern
|
|
41
|
+
* JavaScript updates, along with additional properties copied from originalMap.
|
|
42
|
+
*
|
|
43
|
+
* @param options
|
|
44
|
+
* @returns An object containing both
|
|
45
|
+
* originalSource with the replacement applied, and the modified originalMap.
|
|
46
|
+
* @private
|
|
47
|
+
*/
|
|
48
|
+
export async function replaceAndUpdateSourceMap({
|
|
49
|
+
jsFilename,
|
|
50
|
+
originalMap,
|
|
51
|
+
originalSource,
|
|
52
|
+
replaceString,
|
|
53
|
+
searchString,
|
|
54
|
+
}: ReplaceAndUpdateSourceMapOptions): Promise<{ map: string; source: string }> {
|
|
55
|
+
const generator = new SourceMapGenerator({
|
|
56
|
+
file: jsFilename,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const consumer = await new SourceMapConsumer(originalMap);
|
|
60
|
+
|
|
61
|
+
let pos: number;
|
|
62
|
+
let src = originalSource;
|
|
63
|
+
const replacements: { line: number; column: number }[] = [];
|
|
64
|
+
let lineNum = 0;
|
|
65
|
+
let filePos = 0;
|
|
66
|
+
|
|
67
|
+
const lines = src.split("\n");
|
|
68
|
+
for (let line of lines) {
|
|
69
|
+
lineNum++;
|
|
70
|
+
let searchPos = 0;
|
|
71
|
+
while ((pos = line.indexOf(searchString, searchPos)) !== -1) {
|
|
72
|
+
src = src.substring(0, filePos + pos) + replaceString + src.substring(filePos + pos + searchString.length);
|
|
73
|
+
line = line.substring(0, pos) + replaceString + line.substring(pos + searchString.length);
|
|
74
|
+
replacements.push({ line: lineNum, column: pos });
|
|
75
|
+
searchPos = pos + replaceString.length;
|
|
76
|
+
}
|
|
77
|
+
filePos += line.length + 1;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
replacements.reverse();
|
|
81
|
+
|
|
82
|
+
consumer.eachMapping((mapping) => {
|
|
83
|
+
for (const replacement of replacements) {
|
|
84
|
+
if (replacement.line === mapping.generatedLine && mapping.generatedColumn > replacement.column) {
|
|
85
|
+
const offset = searchString.length - replaceString.length;
|
|
86
|
+
mapping.generatedColumn -= offset;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (mapping.source) {
|
|
91
|
+
const newMapping = {
|
|
92
|
+
generated: {
|
|
93
|
+
line: mapping.generatedLine,
|
|
94
|
+
column: mapping.generatedColumn,
|
|
95
|
+
},
|
|
96
|
+
original: {
|
|
97
|
+
line: mapping.originalLine,
|
|
98
|
+
column: mapping.originalColumn,
|
|
99
|
+
},
|
|
100
|
+
source: mapping.source,
|
|
101
|
+
};
|
|
102
|
+
return generator.addMapping(newMapping);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return mapping;
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
consumer.destroy();
|
|
109
|
+
// JSON.parse returns any.
|
|
110
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
111
|
+
const updatedSourceMap: RawSourceMap = Object.assign(JSON.parse(generator.toString()), {
|
|
112
|
+
names: originalMap.names,
|
|
113
|
+
sourceRoot: originalMap.sourceRoot,
|
|
114
|
+
sources: originalMap.sources,
|
|
115
|
+
sourcesContent: originalMap.sourcesContent,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
map: JSON.stringify(updatedSourceMap),
|
|
120
|
+
source: src,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export class SerwistConfigError extends Error {
|
|
2
|
+
constructor({ moduleName, message }: { moduleName?: string; message?: string }) {
|
|
3
|
+
super(`Received an invalid ${moduleName ?? "Serwist"} configuration: ${message}`);
|
|
4
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
5
|
+
}
|
|
6
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2018 Google LLC
|
|
3
|
+
|
|
4
|
+
Use of this source code is governed by an MIT-style
|
|
5
|
+
license that can be found in the LICENSE file or at
|
|
6
|
+
https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { BasePartial, FileDetails, ManifestEntry, ManifestTransform } from "../types.js";
|
|
10
|
+
import { additionalPrecacheEntriesTransform } from "./additional-precache-entries-transform.js";
|
|
11
|
+
import { errors } from "./errors.js";
|
|
12
|
+
import { maximumSizeTransform } from "./maximum-size-transform.js";
|
|
13
|
+
import { modifyURLPrefixTransform } from "./modify-url-prefix-transform.js";
|
|
14
|
+
import { noRevisionForURLsMatchingTransform } from "./no-revision-for-urls-matching-transform.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A `ManifestTransform` function can be used to modify the modify the `url` or
|
|
18
|
+
* `revision` properties of some or all of the
|
|
19
|
+
* `@serwist/build.ManifestEntry` in the manifest.
|
|
20
|
+
*
|
|
21
|
+
* Deleting the `revision` property of an entry will cause
|
|
22
|
+
* the corresponding `url` to be precached without cache-busting parameters
|
|
23
|
+
* applied, which is to say, it implies that the URL itself contains
|
|
24
|
+
* proper versioning info. If the `revision` property is present, it must be
|
|
25
|
+
* set to a string.
|
|
26
|
+
*
|
|
27
|
+
* @example A transformation that prepended the origin of a CDN for any
|
|
28
|
+
* URL starting with '/assets/' could be implemented as:
|
|
29
|
+
*
|
|
30
|
+
* const cdnTransform = async (manifestEntries) => {
|
|
31
|
+
* const manifest = manifestEntries.map(entry => {
|
|
32
|
+
* const cdnOrigin = 'https://example.com';
|
|
33
|
+
* if (entry.url.startsWith('/assets/')) {
|
|
34
|
+
* entry.url = cdnOrigin + entry.url;
|
|
35
|
+
* }
|
|
36
|
+
* return entry;
|
|
37
|
+
* });
|
|
38
|
+
* return {manifest, warnings: []};
|
|
39
|
+
* };
|
|
40
|
+
*
|
|
41
|
+
* @example A transformation that nulls the revision field when the
|
|
42
|
+
* URL contains an 8-character hash surrounded by '.', indicating that it
|
|
43
|
+
* already contains revision information:
|
|
44
|
+
*
|
|
45
|
+
* const removeRevisionTransform = async (manifestEntries) => {
|
|
46
|
+
* const manifest = manifestEntries.map(entry => {
|
|
47
|
+
* const hashRegExp = /\.\w{8}\./;
|
|
48
|
+
* if (entry.url.match(hashRegExp)) {
|
|
49
|
+
* entry.revision = null;
|
|
50
|
+
* }
|
|
51
|
+
* return entry;
|
|
52
|
+
* });
|
|
53
|
+
* return {manifest, warnings: []};
|
|
54
|
+
* };
|
|
55
|
+
*
|
|
56
|
+
* @callback ManifestTransform
|
|
57
|
+
* @param manifestEntries The full
|
|
58
|
+
* array of entries, prior to the current transformation.
|
|
59
|
+
* @param compilation When used in the webpack plugins, this param
|
|
60
|
+
* will be set to the current `compilation`.
|
|
61
|
+
* @returns The array of entries with the transformation applied,
|
|
62
|
+
* and optionally, any warnings that should be reported back to the build tool.
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
interface ManifestTransformResultWithWarnings {
|
|
66
|
+
count: number;
|
|
67
|
+
size: number;
|
|
68
|
+
manifestEntries: ManifestEntry[] | undefined;
|
|
69
|
+
warnings: string[];
|
|
70
|
+
}
|
|
71
|
+
export async function transformManifest({
|
|
72
|
+
additionalPrecacheEntries,
|
|
73
|
+
dontCacheBustURLsMatching,
|
|
74
|
+
fileDetails,
|
|
75
|
+
manifestTransforms,
|
|
76
|
+
maximumFileSizeToCacheInBytes,
|
|
77
|
+
modifyURLPrefix,
|
|
78
|
+
transformParam,
|
|
79
|
+
disablePrecacheManifest,
|
|
80
|
+
}: BasePartial & {
|
|
81
|
+
fileDetails: FileDetails[];
|
|
82
|
+
// When this is called by the webpack plugin, transformParam will be the
|
|
83
|
+
// current webpack compilation.
|
|
84
|
+
transformParam?: unknown;
|
|
85
|
+
}): Promise<ManifestTransformResultWithWarnings> {
|
|
86
|
+
if (disablePrecacheManifest) {
|
|
87
|
+
return {
|
|
88
|
+
count: 0,
|
|
89
|
+
size: 0,
|
|
90
|
+
manifestEntries: undefined,
|
|
91
|
+
warnings: [],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const allWarnings: string[] = [];
|
|
96
|
+
|
|
97
|
+
// Take the array of fileDetail objects and convert it into an array of
|
|
98
|
+
// {url, revision, size} objects, with \ replaced with /.
|
|
99
|
+
const normalizedManifest = fileDetails.map((fileDetails) => {
|
|
100
|
+
return {
|
|
101
|
+
url: fileDetails.file.replace(/\\/g, "/"),
|
|
102
|
+
revision: fileDetails.hash,
|
|
103
|
+
size: fileDetails.size,
|
|
104
|
+
};
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const transformsToApply: ManifestTransform[] = [];
|
|
108
|
+
|
|
109
|
+
if (maximumFileSizeToCacheInBytes) {
|
|
110
|
+
transformsToApply.push(maximumSizeTransform(maximumFileSizeToCacheInBytes));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (modifyURLPrefix) {
|
|
114
|
+
transformsToApply.push(modifyURLPrefixTransform(modifyURLPrefix));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (dontCacheBustURLsMatching) {
|
|
118
|
+
transformsToApply.push(noRevisionForURLsMatchingTransform(dontCacheBustURLsMatching));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Run any manifestTransforms functions second-to-last.
|
|
122
|
+
if (manifestTransforms) {
|
|
123
|
+
transformsToApply.push(...manifestTransforms);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Run additionalPrecacheEntriesTransform last.
|
|
127
|
+
if (additionalPrecacheEntries) {
|
|
128
|
+
transformsToApply.push(additionalPrecacheEntriesTransform(additionalPrecacheEntries));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
let transformedManifest: (ManifestEntry & { size: number })[] = normalizedManifest;
|
|
132
|
+
for (const transform of transformsToApply) {
|
|
133
|
+
const result = await transform(transformedManifest, transformParam);
|
|
134
|
+
if (!("manifest" in result)) {
|
|
135
|
+
throw new Error(errors["bad-manifest-transforms-return-value"]);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
transformedManifest = result.manifest;
|
|
139
|
+
allWarnings.push(...(result.warnings || []));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Generate some metadata about the manifest before we clear out the size
|
|
143
|
+
// properties from each entry.
|
|
144
|
+
const count = transformedManifest.length;
|
|
145
|
+
let size = 0;
|
|
146
|
+
for (const manifestEntry of transformedManifest as (ManifestEntry & { size?: number })[]) {
|
|
147
|
+
size += manifestEntry.size || 0;
|
|
148
|
+
// biome-ignore lint/performance/noDelete: I don't understand this part yet.
|
|
149
|
+
delete manifestEntry.size;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
count,
|
|
154
|
+
size,
|
|
155
|
+
manifestEntries: transformedManifest as ManifestEntry[],
|
|
156
|
+
warnings: allWarnings,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2021 Google LLC
|
|
3
|
+
|
|
4
|
+
Use of this source code is governed by an MIT-style
|
|
5
|
+
license that can be found in the LICENSE file or at
|
|
6
|
+
https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fse from "fs-extra";
|
|
10
|
+
import upath from "upath";
|
|
11
|
+
|
|
12
|
+
import { errors } from "./errors.js";
|
|
13
|
+
|
|
14
|
+
export function translateURLToSourcemapPaths(
|
|
15
|
+
url: string | null,
|
|
16
|
+
swSrc: string,
|
|
17
|
+
swDest: string,
|
|
18
|
+
): {
|
|
19
|
+
destPath: string | undefined;
|
|
20
|
+
srcPath: string | undefined;
|
|
21
|
+
warning: string | undefined;
|
|
22
|
+
} {
|
|
23
|
+
let destPath: string | undefined = undefined;
|
|
24
|
+
let srcPath: string | undefined = undefined;
|
|
25
|
+
let warning: string | undefined = undefined;
|
|
26
|
+
|
|
27
|
+
if (url && !url.startsWith("data:")) {
|
|
28
|
+
const possibleSrcPath = upath.resolve(upath.dirname(swSrc), url);
|
|
29
|
+
if (fse.existsSync(possibleSrcPath)) {
|
|
30
|
+
srcPath = possibleSrcPath;
|
|
31
|
+
destPath = upath.resolve(upath.dirname(swDest), url);
|
|
32
|
+
} else {
|
|
33
|
+
warning = `${errors["cant-find-sourcemap"]} ${possibleSrcPath}`;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return { destPath, srcPath, warning };
|
|
38
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { nextInjectManifestOptions } from "../schema/next.js";
|
|
2
|
+
import { validationErrorMap } from "../schema/validationErrorMap.js";
|
|
3
|
+
import type { NextInjectManifestOptionsComplete } from "../types.js";
|
|
4
|
+
import { SerwistConfigError } from "./serwist-config-error.js";
|
|
5
|
+
|
|
6
|
+
export const validateNextInjectManifestOptions = (input: unknown): NextInjectManifestOptionsComplete => {
|
|
7
|
+
const result = nextInjectManifestOptions.safeParse(input, {
|
|
8
|
+
errorMap: validationErrorMap,
|
|
9
|
+
});
|
|
10
|
+
if (!result.success) {
|
|
11
|
+
throw new SerwistConfigError({ moduleName: "@serwist/next", message: JSON.stringify(result.error.format(), null, 2) });
|
|
12
|
+
}
|
|
13
|
+
return result.data;
|
|
14
|
+
};
|