source: frontend/node_modules/workbox-build/src/lib/validate-options.ts

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 6.6 KB
Line 
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
9import {betterAjvErrors} from '@apideck/better-ajv-errors';
10import {oneLine as ol} from 'common-tags';
11import Ajv, {JSONSchemaType} from 'ajv';
12
13import {errors} from './errors';
14
15import {
16 GenerateSWOptions,
17 GetManifestOptions,
18 InjectManifestOptions,
19 WebpackGenerateSWOptions,
20 WebpackInjectManifestOptions,
21} from '../types';
22
23type MethodNames =
24 | 'GenerateSW'
25 | 'GetManifest'
26 | 'InjectManifest'
27 | 'WebpackGenerateSW'
28 | 'WebpackInjectManifest';
29
30const ajv = new Ajv({
31 useDefaults: true,
32});
33
34const DEFAULT_EXCLUDE_VALUE = [/\.map$/, /^manifest.*\.js$/];
35
36export class WorkboxConfigError extends Error {
37 constructor(message?: string) {
38 super(message);
39 Object.setPrototypeOf(this, new.target.prototype);
40 }
41}
42
43// Some methods need to do follow-up validation using the JSON schema,
44// so return both the validated options and then schema.
45function validate<T>(
46 input: unknown,
47 methodName: MethodNames,
48): [T, JSONSchemaType<T>] {
49 // Don't mutate input: https://github.com/GoogleChrome/workbox/issues/2158
50 const inputCopy = Object.assign({}, input);
51 // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
52 const jsonSchema: JSONSchemaType<T> = require(`../schema/${methodName}Options.json`);
53 const validate = ajv.compile(jsonSchema);
54 if (validate(inputCopy)) {
55 // All methods support manifestTransforms, so validate it here.
56 ensureValidManifestTransforms(inputCopy);
57 return [inputCopy, jsonSchema];
58 }
59
60 const betterErrors = betterAjvErrors({
61 basePath: methodName,
62 data: input,
63 errors: validate.errors,
64 // This is needed as JSONSchema6 is expected, but JSONSchemaType works.
65 // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
66 schema: jsonSchema as any,
67 });
68 const messages = betterErrors.map(
69 (err) => ol`[${err.path}] ${err.message}.
70 ${err.suggestion ? err.suggestion : ''}`,
71 );
72
73 throw new WorkboxConfigError(messages.join('\n\n'));
74}
75
76function ensureValidManifestTransforms(
77 options:
78 | GenerateSWOptions
79 | GetManifestOptions
80 | InjectManifestOptions
81 | WebpackGenerateSWOptions
82 | WebpackInjectManifestOptions,
83): void {
84 if (
85 'manifestTransforms' in options &&
86 !(
87 Array.isArray(options.manifestTransforms) &&
88 options.manifestTransforms.every((item) => typeof item === 'function')
89 )
90 ) {
91 throw new WorkboxConfigError(errors['manifest-transforms']);
92 }
93}
94
95function ensureValidNavigationPreloadConfig(
96 options: GenerateSWOptions | WebpackGenerateSWOptions,
97): void {
98 if (
99 options.navigationPreload &&
100 (!Array.isArray(options.runtimeCaching) ||
101 options.runtimeCaching.length === 0)
102 ) {
103 throw new WorkboxConfigError(errors['nav-preload-runtime-caching']);
104 }
105}
106
107function ensureValidCacheExpiration(
108 options: GenerateSWOptions | WebpackGenerateSWOptions,
109): void {
110 for (const runtimeCaching of options.runtimeCaching || []) {
111 if (
112 runtimeCaching.options?.expiration &&
113 !runtimeCaching.options?.cacheName
114 ) {
115 throw new WorkboxConfigError(errors['cache-name-required']);
116 }
117 }
118}
119
120function ensureValidRuntimeCachingOrGlobDirectory(
121 options: GenerateSWOptions,
122): void {
123 if (
124 !options.globDirectory &&
125 (!Array.isArray(options.runtimeCaching) ||
126 options.runtimeCaching.length === 0)
127 ) {
128 throw new WorkboxConfigError(
129 errors['no-manifest-entries-or-runtime-caching'],
130 );
131 }
132}
133
134// This is... messy, because we can't rely on the built-in ajv validation for
135// runtimeCaching.handler, as it needs to accept {} (i.e. any) due to
136// https://github.com/GoogleChrome/workbox/pull/2899
137// So we need to perform validation when a string (not a function) is used.
138function ensureValidStringHandler(
139 options: GenerateSWOptions | WebpackGenerateSWOptions,
140 jsonSchema: JSONSchemaType<GenerateSWOptions | WebpackGenerateSWOptions>,
141): void {
142 let validHandlers: Array<string> = [];
143 /* eslint-disable */
144 for (const handler of jsonSchema.definitions?.RuntimeCaching?.properties
145 ?.handler?.anyOf || []) {
146 if ('enum' in handler) {
147 validHandlers = handler.enum;
148 break;
149 }
150 }
151 /* eslint-enable */
152
153 for (const runtimeCaching of options.runtimeCaching || []) {
154 if (
155 typeof runtimeCaching.handler === 'string' &&
156 !validHandlers.includes(runtimeCaching.handler)
157 ) {
158 throw new WorkboxConfigError(
159 errors['invalid-handler-string'] + runtimeCaching.handler,
160 );
161 }
162 }
163}
164
165export function validateGenerateSWOptions(input: unknown): GenerateSWOptions {
166 const [validatedOptions, jsonSchema] = validate<GenerateSWOptions>(
167 input,
168 'GenerateSW',
169 );
170 ensureValidNavigationPreloadConfig(validatedOptions);
171 ensureValidCacheExpiration(validatedOptions);
172 ensureValidRuntimeCachingOrGlobDirectory(validatedOptions);
173 ensureValidStringHandler(validatedOptions, jsonSchema);
174
175 return validatedOptions;
176}
177
178export function validateGetManifestOptions(input: unknown): GetManifestOptions {
179 const [validatedOptions] = validate<GetManifestOptions>(input, 'GetManifest');
180
181 return validatedOptions;
182}
183
184export function validateInjectManifestOptions(
185 input: unknown,
186): InjectManifestOptions {
187 const [validatedOptions] = validate<InjectManifestOptions>(
188 input,
189 'InjectManifest',
190 );
191
192 return validatedOptions;
193}
194
195// The default `exclude: [/\.map$/, /^manifest.*\.js$/]` value can't be
196// represented in the JSON schema, so manually set it for the webpack options.
197export function validateWebpackGenerateSWOptions(
198 input: unknown,
199): WebpackGenerateSWOptions {
200 const inputWithExcludeDefault = Object.assign(
201 {
202 // Make a copy, as exclude can be mutated when used.
203 exclude: Array.from(DEFAULT_EXCLUDE_VALUE),
204 },
205 input,
206 );
207 const [validatedOptions, jsonSchema] = validate<WebpackGenerateSWOptions>(
208 inputWithExcludeDefault,
209 'WebpackGenerateSW',
210 );
211
212 ensureValidNavigationPreloadConfig(validatedOptions);
213 ensureValidCacheExpiration(validatedOptions);
214 ensureValidStringHandler(validatedOptions, jsonSchema);
215
216 return validatedOptions;
217}
218
219export function validateWebpackInjectManifestOptions(
220 input: unknown,
221): WebpackInjectManifestOptions {
222 const inputWithExcludeDefault = Object.assign(
223 {
224 // Make a copy, as exclude can be mutated when used.
225 exclude: Array.from(DEFAULT_EXCLUDE_VALUE),
226 },
227 input,
228 );
229 const [validatedOptions] = validate<WebpackInjectManifestOptions>(
230 inputWithExcludeDefault,
231 'WebpackInjectManifest',
232 );
233
234 return validatedOptions;
235}
Note: See TracBrowser for help on using the repository browser.