UNPKG

10 kBJavaScriptView Raw
1/**
2 * @license Angular v14.1.2
3 * (c) 2010-2022 Google LLC. https://angular.io/
4 * License: MIT
5 */
6
7import { __awaiter } from 'tslib';
8
9/**
10 * @license
11 * Copyright Google LLC All Rights Reserved.
12 *
13 * Use of this source code is governed by an MIT-style license that can be
14 * found in the LICENSE file at https://angular.io/license
15 */
16const PARSE_TO_PAIRS = /([0-9]+[^0-9]+)/g;
17const PAIR_SPLIT = /^([0-9]+)([dhmsu]+)$/;
18function parseDurationToMs(duration) {
19 const matches = [];
20 let array;
21 while ((array = PARSE_TO_PAIRS.exec(duration)) !== null) {
22 matches.push(array[0]);
23 }
24 return matches
25 .map(match => {
26 const res = PAIR_SPLIT.exec(match);
27 if (res === null) {
28 throw new Error(`Not a valid duration: ${match}`);
29 }
30 let factor = 0;
31 switch (res[2]) {
32 case 'd':
33 factor = 86400000;
34 break;
35 case 'h':
36 factor = 3600000;
37 break;
38 case 'm':
39 factor = 60000;
40 break;
41 case 's':
42 factor = 1000;
43 break;
44 case 'u':
45 factor = 1;
46 break;
47 default:
48 throw new Error(`Not a valid duration unit: ${res[2]}`);
49 }
50 return parseInt(res[1]) * factor;
51 })
52 .reduce((total, value) => total + value, 0);
53}
54
55/**
56 * @license
57 * Copyright Google LLC All Rights Reserved.
58 *
59 * Use of this source code is governed by an MIT-style license that can be
60 * found in the LICENSE file at https://angular.io/license
61 */
62const QUESTION_MARK = '[^/]';
63const WILD_SINGLE = '[^/]*';
64const WILD_OPEN = '(?:.+\\/)?';
65const TO_ESCAPE_BASE = [
66 { replace: /\./g, with: '\\.' },
67 { replace: /\+/g, with: '\\+' },
68 { replace: /\*/g, with: WILD_SINGLE },
69];
70const TO_ESCAPE_WILDCARD_QM = [
71 ...TO_ESCAPE_BASE,
72 { replace: /\?/g, with: QUESTION_MARK },
73];
74const TO_ESCAPE_LITERAL_QM = [
75 ...TO_ESCAPE_BASE,
76 { replace: /\?/g, with: '\\?' },
77];
78function globToRegex(glob, literalQuestionMark = false) {
79 const toEscape = literalQuestionMark ? TO_ESCAPE_LITERAL_QM : TO_ESCAPE_WILDCARD_QM;
80 const segments = glob.split('/').reverse();
81 let regex = '';
82 while (segments.length > 0) {
83 const segment = segments.pop();
84 if (segment === '**') {
85 if (segments.length > 0) {
86 regex += WILD_OPEN;
87 }
88 else {
89 regex += '.*';
90 }
91 }
92 else {
93 const processed = toEscape.reduce((segment, escape) => segment.replace(escape.replace, escape.with), segment);
94 regex += processed;
95 if (segments.length > 0) {
96 regex += '\\/';
97 }
98 }
99 }
100 return regex;
101}
102
103const DEFAULT_NAVIGATION_URLS = [
104 '/**',
105 '!/**/*.*',
106 '!/**/*__*',
107 '!/**/*__*/**', // Exclude URLs containing `__` in any other segment.
108];
109/**
110 * Consumes service worker configuration files and processes them into control files.
111 *
112 * @publicApi
113 */
114class Generator {
115 constructor(fs, baseHref) {
116 this.fs = fs;
117 this.baseHref = baseHref;
118 }
119 process(config) {
120 var _a;
121 return __awaiter(this, void 0, void 0, function* () {
122 const unorderedHashTable = {};
123 const assetGroups = yield this.processAssetGroups(config, unorderedHashTable);
124 return {
125 configVersion: 1,
126 timestamp: Date.now(),
127 appData: config.appData,
128 index: joinUrls(this.baseHref, config.index),
129 assetGroups,
130 dataGroups: this.processDataGroups(config),
131 hashTable: withOrderedKeys(unorderedHashTable),
132 navigationUrls: processNavigationUrls(this.baseHref, config.navigationUrls),
133 navigationRequestStrategy: (_a = config.navigationRequestStrategy) !== null && _a !== void 0 ? _a : 'performance',
134 };
135 });
136 }
137 processAssetGroups(config, hashTable) {
138 return __awaiter(this, void 0, void 0, function* () {
139 // Retrieve all files of the build.
140 const allFiles = yield this.fs.list('/');
141 const seenMap = new Set();
142 const filesPerGroup = new Map();
143 // Computed which files belong to each asset-group.
144 for (const group of (config.assetGroups || [])) {
145 if (group.resources.versionedFiles) {
146 throw new Error(`Asset-group '${group.name}' in 'ngsw-config.json' uses the 'versionedFiles' option, ` +
147 'which is no longer supported. Use \'files\' instead.');
148 }
149 const fileMatcher = globListToMatcher(group.resources.files || []);
150 const matchedFiles = allFiles.filter(fileMatcher).filter(file => !seenMap.has(file)).sort();
151 matchedFiles.forEach(file => seenMap.add(file));
152 filesPerGroup.set(group, matchedFiles);
153 }
154 // Compute hashes for all matched files and add them to the hash-table.
155 const allMatchedFiles = [].concat(...Array.from(filesPerGroup.values())).sort();
156 const allMatchedHashes = yield processInBatches(allMatchedFiles, 500, file => this.fs.hash(file));
157 allMatchedFiles.forEach((file, idx) => {
158 hashTable[joinUrls(this.baseHref, file)] = allMatchedHashes[idx];
159 });
160 // Generate and return the processed asset-groups.
161 return Array.from(filesPerGroup.entries())
162 .map(([group, matchedFiles]) => ({
163 name: group.name,
164 installMode: group.installMode || 'prefetch',
165 updateMode: group.updateMode || group.installMode || 'prefetch',
166 cacheQueryOptions: buildCacheQueryOptions(group.cacheQueryOptions),
167 urls: matchedFiles.map(url => joinUrls(this.baseHref, url)),
168 patterns: (group.resources.urls || []).map(url => urlToRegex(url, this.baseHref, true)),
169 }));
170 });
171 }
172 processDataGroups(config) {
173 return (config.dataGroups || []).map(group => {
174 return {
175 name: group.name,
176 patterns: group.urls.map(url => urlToRegex(url, this.baseHref, true)),
177 strategy: group.cacheConfig.strategy || 'performance',
178 maxSize: group.cacheConfig.maxSize,
179 maxAge: parseDurationToMs(group.cacheConfig.maxAge),
180 timeoutMs: group.cacheConfig.timeout && parseDurationToMs(group.cacheConfig.timeout),
181 cacheOpaqueResponses: group.cacheConfig.cacheOpaqueResponses,
182 cacheQueryOptions: buildCacheQueryOptions(group.cacheQueryOptions),
183 version: group.version !== undefined ? group.version : 1,
184 };
185 });
186 }
187}
188function processNavigationUrls(baseHref, urls = DEFAULT_NAVIGATION_URLS) {
189 return urls.map(url => {
190 const positive = !url.startsWith('!');
191 url = positive ? url : url.slice(1);
192 return { positive, regex: `^${urlToRegex(url, baseHref)}$` };
193 });
194}
195function processInBatches(items, batchSize, processFn) {
196 return __awaiter(this, void 0, void 0, function* () {
197 const batches = [];
198 for (let i = 0; i < items.length; i += batchSize) {
199 batches.push(items.slice(i, i + batchSize));
200 }
201 return batches.reduce((prev, batch) => __awaiter(this, void 0, void 0, function* () { return (yield prev).concat(yield Promise.all(batch.map(item => processFn(item)))); }), Promise.resolve([]));
202 });
203}
204function globListToMatcher(globs) {
205 const patterns = globs.map(pattern => {
206 if (pattern.startsWith('!')) {
207 return {
208 positive: false,
209 regex: new RegExp('^' + globToRegex(pattern.slice(1)) + '$'),
210 };
211 }
212 else {
213 return {
214 positive: true,
215 regex: new RegExp('^' + globToRegex(pattern) + '$'),
216 };
217 }
218 });
219 return (file) => matches(file, patterns);
220}
221function matches(file, patterns) {
222 const res = patterns.reduce((isMatch, pattern) => {
223 if (pattern.positive) {
224 return isMatch || pattern.regex.test(file);
225 }
226 else {
227 return isMatch && !pattern.regex.test(file);
228 }
229 }, false);
230 return res;
231}
232function urlToRegex(url, baseHref, literalQuestionMark) {
233 if (!url.startsWith('/') && url.indexOf('://') === -1) {
234 // Prefix relative URLs with `baseHref`.
235 // Strip a leading `.` from a relative `baseHref` (e.g. `./foo/`), since it would result in an
236 // incorrect regex (matching a literal `.`).
237 url = joinUrls(baseHref.replace(/^\.(?=\/)/, ''), url);
238 }
239 return globToRegex(url, literalQuestionMark);
240}
241function joinUrls(a, b) {
242 if (a.endsWith('/') && b.startsWith('/')) {
243 return a + b.slice(1);
244 }
245 else if (!a.endsWith('/') && !b.startsWith('/')) {
246 return a + '/' + b;
247 }
248 return a + b;
249}
250function withOrderedKeys(unorderedObj) {
251 const orderedObj = {};
252 Object.keys(unorderedObj).sort().forEach(key => orderedObj[key] = unorderedObj[key]);
253 return orderedObj;
254}
255function buildCacheQueryOptions(inOptions) {
256 return Object.assign({ ignoreVary: true }, inOptions);
257}
258
259/**
260 * @license
261 * Copyright Google LLC All Rights Reserved.
262 *
263 * Use of this source code is governed by an MIT-style license that can be
264 * found in the LICENSE file at https://angular.io/license
265 */
266
267/**
268 * @license
269 * Copyright Google LLC All Rights Reserved.
270 *
271 * Use of this source code is governed by an MIT-style license that can be
272 * found in the LICENSE file at https://angular.io/license
273 */
274
275/**
276 * Generated bundle index. Do not edit.
277 */
278
279export { Generator };
280//# sourceMappingURL=config.mjs.map