Override more function validation (#2911)

* Override more fn validation

* Opt-out of linting
This commit is contained in:
Jeffrey Posnick
2021-08-11 12:44:13 -04:00
committed by GitHub
parent 2d008261e1
commit 175671eb52
9 changed files with 89 additions and 57 deletions
+9
View File
@@ -71,10 +71,19 @@ async function generateWorkboxBuildJSONSchema(packagePath) {
schema.properties.include.items = {};
}
// See https://github.com/GoogleChrome/workbox/issues/2910
if (schema.definitions.OnSyncCallback) {
schema.definitions.OnSyncCallback = {};
}
if (schema.definitions.RouteMatchCallback) {
schema.definitions.RouteMatchCallback = {};
}
if (schema.definitions.RouteHandlerCallback) {
schema.definitions.RouteHandlerCallback = {};
}
// See https://github.com/GoogleChrome/workbox/issues/2901
if (schema.definitions.WorkboxPlugin) {
for (const plugin of Object.keys(
+1
View File
@@ -120,4 +120,5 @@ export const errors = {
configure a custom cacheName.`,
'manifest-transforms': ol`When using manifestTransforms, you must provide
an array of functions.`,
'invalid-handler-string': ol`The handler name provided is not valid: `,
};
@@ -40,7 +40,12 @@ export class WorkboxConfigError extends Error {
}
}
function validate<T>(input: unknown, methodName: MethodNames): T {
// Some methods need to do follow-up validation using the JSON schema,
// so return both the validated options and then schema.
function validate<T>(
input: unknown,
methodName: MethodNames,
): [T, JSONSchemaType<T>] {
// Don't mutate input: https://github.com/GoogleChrome/workbox/issues/2158
const inputCopy = Object.assign({}, input);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
@@ -49,7 +54,7 @@ function validate<T>(input: unknown, methodName: MethodNames): T {
if (validate(inputCopy)) {
// All methods support manifestTransforms, so validate it here.
ensureValidManifestTransforms(inputCopy);
return inputCopy;
return [inputCopy, jsonSchema];
}
const betterErrors = betterAjvErrors({
@@ -126,23 +131,65 @@ function ensureValidRuntimeCachingOrGlobDirectory(
}
}
// This is... messy, because we can't rely on the built-in ajv validation for
// runtimeCaching.handler, as it needs to accept {} (i.e. any) due to
// https://github.com/GoogleChrome/workbox/pull/2899
// So we need to perform validation when a string (not a function) is used.
function ensureValidStringHandler(
options: GenerateSWOptions | WebpackGenerateSWOptions,
jsonSchema: JSONSchemaType<GenerateSWOptions | WebpackGenerateSWOptions>,
): void {
let validHandlers: Array<string> = [];
/* eslint-disable */
for (const handler of jsonSchema.definitions?.RuntimeCaching?.properties
?.handler?.anyOf || []) {
if ('enum' in handler) {
validHandlers = handler.enum;
break;
}
}
/* eslint-enable */
for (const runtimeCaching of options.runtimeCaching || []) {
if (
typeof runtimeCaching.handler === 'string' &&
!validHandlers.includes(runtimeCaching.handler)
) {
throw new WorkboxConfigError(
errors['invalid-handler-string'] + runtimeCaching.handler,
);
}
}
}
export function validateGenerateSWOptions(input: unknown): GenerateSWOptions {
const validatedOptions = validate<GenerateSWOptions>(input, 'GenerateSW');
const [validatedOptions, jsonSchema] = validate<GenerateSWOptions>(
input,
'GenerateSW',
);
ensureValidNavigationPreloadConfig(validatedOptions);
ensureValidCacheExpiration(validatedOptions);
ensureValidRuntimeCachingOrGlobDirectory(validatedOptions);
ensureValidStringHandler(validatedOptions, jsonSchema);
return validatedOptions;
}
export function validateGetManifestOptions(input: unknown): GetManifestOptions {
return validate<GetManifestOptions>(input, 'GetManifest');
const [validatedOptions] = validate<GetManifestOptions>(input, 'GetManifest');
return validatedOptions;
}
export function validateInjectManifestOptions(
input: unknown,
): InjectManifestOptions {
return validate<InjectManifestOptions>(input, 'InjectManifest');
const [validatedOptions] = validate<InjectManifestOptions>(
input,
'InjectManifest',
);
return validatedOptions;
}
// The default `exclude: [/\.map$/, /^manifest.*\.js$/]` value can't be
@@ -157,13 +204,14 @@ export function validateWebpackGenerateSWOptions(
},
input,
);
const validatedOptions = validate<WebpackGenerateSWOptions>(
const [validatedOptions, jsonSchema] = validate<WebpackGenerateSWOptions>(
inputWithExcludeDefault,
'WebpackGenerateSW',
);
ensureValidNavigationPreloadConfig(validatedOptions);
ensureValidCacheExpiration(validatedOptions);
ensureValidStringHandler(validatedOptions, jsonSchema);
return validatedOptions;
}
@@ -178,8 +226,10 @@ export function validateWebpackInjectManifestOptions(
},
input,
);
return validate<WebpackInjectManifestOptions>(
const [validatedOptions] = validate<WebpackInjectManifestOptions>(
inputWithExcludeDefault,
'WebpackInjectManifest',
);
return validatedOptions;
}
@@ -407,11 +407,7 @@
"urlPattern"
]
},
"RouteHandlerCallback": {
"description": "The \"handler\" callback is invoked whenever a `Router` matches a URL/Request\nto a `Route` via its `RouteMatchCallback`. This handler callback should\nreturn a `Promise` that resolves with a `Response`.\n\nIf a non-empty array or object is returned by the `RouteMatchCallback` it\nwill be passed in as this handler's `options.params` argument.",
"type": "object",
"additionalProperties": false
},
"RouteHandlerCallback": {},
"RouteHandlerObject": {
"description": "An object with a `handle` method of type `RouteHandlerCallback`.\n\nA `Route` object can be created with either an `RouteHandlerCallback`\nfunction or this `RouteHandler` object. The benefit of the `RouteHandler`\nis it can be extended (as is done by the `workbox-strategies` package).",
"type": "object",
@@ -437,10 +433,7 @@
},
"additionalProperties": false
},
"OnSyncCallback": {
"type": "object",
"additionalProperties": false
},
"OnSyncCallback": {},
"BroadcastCacheUpdateOptions": {
"type": "object",
"properties": {
@@ -299,11 +299,7 @@
"urlPattern"
]
},
"RouteHandlerCallback": {
"description": "The \"handler\" callback is invoked whenever a `Router` matches a URL/Request\nto a `Route` via its `RouteMatchCallback`. This handler callback should\nreturn a `Promise` that resolves with a `Response`.\n\nIf a non-empty array or object is returned by the `RouteMatchCallback` it\nwill be passed in as this handler's `options.params` argument.",
"type": "object",
"additionalProperties": false
},
"RouteHandlerCallback": {},
"RouteHandlerObject": {
"description": "An object with a `handle` method of type `RouteHandlerCallback`.\n\nA `Route` object can be created with either an `RouteHandlerCallback`\nfunction or this `RouteHandler` object. The benefit of the `RouteHandler`\nis it can be extended (as is done by the `workbox-strategies` package).",
"type": "object",
@@ -329,10 +325,7 @@
},
"additionalProperties": false
},
"OnSyncCallback": {
"type": "object",
"additionalProperties": false
},
"OnSyncCallback": {},
"BroadcastCacheUpdateOptions": {
"type": "object",
"properties": {
@@ -311,11 +311,7 @@
"urlPattern"
]
},
"RouteHandlerCallback": {
"description": "The \"handler\" callback is invoked whenever a `Router` matches a URL/Request\nto a `Route` via its `RouteMatchCallback`. This handler callback should\nreturn a `Promise` that resolves with a `Response`.\n\nIf a non-empty array or object is returned by the `RouteMatchCallback` it\nwill be passed in as this handler's `options.params` argument.",
"type": "object",
"additionalProperties": false
},
"RouteHandlerCallback": {},
"RouteHandlerObject": {
"description": "An object with a `handle` method of type `RouteHandlerCallback`.\n\nA `Route` object can be created with either an `RouteHandlerCallback`\nfunction or this `RouteHandler` object. The benefit of the `RouteHandler`\nis it can be extended (as is done by the `workbox-strategies` package).",
"type": "object",
@@ -341,10 +337,7 @@
},
"additionalProperties": false
},
"OnSyncCallback": {
"type": "object",
"additionalProperties": false
},
"OnSyncCallback": {},
"BroadcastCacheUpdateOptions": {
"type": "object",
"properties": {
@@ -386,11 +386,7 @@
"urlPattern"
]
},
"RouteHandlerCallback": {
"description": "The \"handler\" callback is invoked whenever a `Router` matches a URL/Request\nto a `Route` via its `RouteMatchCallback`. This handler callback should\nreturn a `Promise` that resolves with a `Response`.\n\nIf a non-empty array or object is returned by the `RouteMatchCallback` it\nwill be passed in as this handler's `options.params` argument.",
"type": "object",
"additionalProperties": false
},
"RouteHandlerCallback": {},
"RouteHandlerObject": {
"description": "An object with a `handle` method of type `RouteHandlerCallback`.\n\nA `Route` object can be created with either an `RouteHandlerCallback`\nfunction or this `RouteHandler` object. The benefit of the `RouteHandler`\nis it can be extended (as is done by the `workbox-strategies` package).",
"type": "object",
@@ -416,10 +412,7 @@
},
"additionalProperties": false
},
"OnSyncCallback": {
"type": "object",
"additionalProperties": false
},
"OnSyncCallback": {},
"BroadcastCacheUpdateOptions": {
"type": "object",
"properties": {
@@ -298,11 +298,7 @@
"urlPattern"
]
},
"RouteHandlerCallback": {
"description": "The \"handler\" callback is invoked whenever a `Router` matches a URL/Request\nto a `Route` via its `RouteMatchCallback`. This handler callback should\nreturn a `Promise` that resolves with a `Response`.\n\nIf a non-empty array or object is returned by the `RouteMatchCallback` it\nwill be passed in as this handler's `options.params` argument.",
"type": "object",
"additionalProperties": false
},
"RouteHandlerCallback": {},
"RouteHandlerObject": {
"description": "An object with a `handle` method of type `RouteHandlerCallback`.\n\nA `Route` object can be created with either an `RouteHandlerCallback`\nfunction or this `RouteHandler` object. The benefit of the `RouteHandler`\nis it can be extended (as is done by the `workbox-strategies` package).",
"type": "object",
@@ -328,10 +324,7 @@
},
"additionalProperties": false
},
"OnSyncCallback": {
"type": "object",
"additionalProperties": false
},
"OnSyncCallback": {},
"BroadcastCacheUpdateOptions": {
"type": "object",
"properties": {
+12 -5
View File
@@ -921,18 +921,25 @@ describe(`[workbox-build] generate-sw.js (End to End)`, function() {
errors['invalid-network-timeout-seconds']);
});
it(`should support passing in a function for cachedResponseWillBeUsed`, async function() {
it(`should support passing in a function when allowed`, async function() {
const swDest = tempy.file({extension: 'js'});
const handler = 'CacheFirst';
const handler = () => {};
const urlPattern = () => {};
const runtimeCachingOptions = {
backgroundSync: {
name: 'test',
options: {
onSync: () => {},
},
},
plugins: [{
cachedResponseWillBeUsed: async () => {},
cachedResponseWillBeUsed: () => {},
}],
};
const runtimeCaching = [{
urlPattern: REGEXP_URL_PATTERN,
handler,
urlPattern,
options: runtimeCachingOptions,
}];
const options = Object.assign({}, BASE_OPTIONS, {
@@ -968,7 +975,7 @@ describe(`[workbox-build] generate-sw.js (End to End)`, function() {
revision: /^[0-9a-f]{32}$/,
}], {}]],
registerRoute: [
[REGEXP_URL_PATTERN, {name: handler}, DEFAULT_METHOD],
[urlPattern.toString(), handler.toString(), DEFAULT_METHOD],
],
}});
});