validate-options.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. "use strict";
  2. /*
  3. Copyright 2021 Google LLC
  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. var __importDefault = (this && this.__importDefault) || function (mod) {
  9. return (mod && mod.__esModule) ? mod : { "default": mod };
  10. };
  11. Object.defineProperty(exports, "__esModule", { value: true });
  12. exports.validateWebpackInjectManifestOptions = exports.validateWebpackGenerateSWOptions = exports.validateInjectManifestOptions = exports.validateGetManifestOptions = exports.validateGenerateSWOptions = exports.WorkboxConfigError = void 0;
  13. const better_ajv_errors_1 = require("@apideck/better-ajv-errors");
  14. const common_tags_1 = require("common-tags");
  15. const ajv_1 = __importDefault(require("ajv"));
  16. const errors_1 = require("./errors");
  17. const ajv = new ajv_1.default({
  18. useDefaults: true,
  19. });
  20. const DEFAULT_EXCLUDE_VALUE = [/\.map$/, /^manifest.*\.js$/];
  21. class WorkboxConfigError extends Error {
  22. constructor(message) {
  23. super(message);
  24. Object.setPrototypeOf(this, new.target.prototype);
  25. }
  26. }
  27. exports.WorkboxConfigError = WorkboxConfigError;
  28. // Some methods need to do follow-up validation using the JSON schema,
  29. // so return both the validated options and then schema.
  30. function validate(input, methodName) {
  31. // Don't mutate input: https://github.com/GoogleChrome/workbox/issues/2158
  32. const inputCopy = Object.assign({}, input);
  33. // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
  34. const jsonSchema = require(`../schema/${methodName}Options.json`);
  35. const validate = ajv.compile(jsonSchema);
  36. if (validate(inputCopy)) {
  37. // All methods support manifestTransforms, so validate it here.
  38. ensureValidManifestTransforms(inputCopy);
  39. return [inputCopy, jsonSchema];
  40. }
  41. const betterErrors = (0, better_ajv_errors_1.betterAjvErrors)({
  42. basePath: methodName,
  43. data: input,
  44. errors: validate.errors,
  45. // This is needed as JSONSchema6 is expected, but JSONSchemaType works.
  46. // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
  47. schema: jsonSchema,
  48. });
  49. const messages = betterErrors.map((err) => (0, common_tags_1.oneLine) `[${err.path}] ${err.message}.
  50. ${err.suggestion ? err.suggestion : ''}`);
  51. throw new WorkboxConfigError(messages.join('\n\n'));
  52. }
  53. function ensureValidManifestTransforms(options) {
  54. if ('manifestTransforms' in options &&
  55. !(Array.isArray(options.manifestTransforms) &&
  56. options.manifestTransforms.every((item) => typeof item === 'function'))) {
  57. throw new WorkboxConfigError(errors_1.errors['manifest-transforms']);
  58. }
  59. }
  60. function ensureValidNavigationPreloadConfig(options) {
  61. if (options.navigationPreload &&
  62. (!Array.isArray(options.runtimeCaching) ||
  63. options.runtimeCaching.length === 0)) {
  64. throw new WorkboxConfigError(errors_1.errors['nav-preload-runtime-caching']);
  65. }
  66. }
  67. function ensureValidCacheExpiration(options) {
  68. var _a, _b;
  69. for (const runtimeCaching of options.runtimeCaching || []) {
  70. if (((_a = runtimeCaching.options) === null || _a === void 0 ? void 0 : _a.expiration) &&
  71. !((_b = runtimeCaching.options) === null || _b === void 0 ? void 0 : _b.cacheName)) {
  72. throw new WorkboxConfigError(errors_1.errors['cache-name-required']);
  73. }
  74. }
  75. }
  76. function ensureValidRuntimeCachingOrGlobDirectory(options) {
  77. if (!options.globDirectory &&
  78. (!Array.isArray(options.runtimeCaching) ||
  79. options.runtimeCaching.length === 0)) {
  80. throw new WorkboxConfigError(errors_1.errors['no-manifest-entries-or-runtime-caching']);
  81. }
  82. }
  83. // This is... messy, because we can't rely on the built-in ajv validation for
  84. // runtimeCaching.handler, as it needs to accept {} (i.e. any) due to
  85. // https://github.com/GoogleChrome/workbox/pull/2899
  86. // So we need to perform validation when a string (not a function) is used.
  87. function ensureValidStringHandler(options, jsonSchema) {
  88. var _a, _b, _c, _d;
  89. let validHandlers = [];
  90. /* eslint-disable */
  91. for (const handler of ((_d = (_c = (_b = (_a = jsonSchema.definitions) === null || _a === void 0 ? void 0 : _a.RuntimeCaching) === null || _b === void 0 ? void 0 : _b.properties) === null || _c === void 0 ? void 0 : _c.handler) === null || _d === void 0 ? void 0 : _d.anyOf) || []) {
  92. if ('enum' in handler) {
  93. validHandlers = handler.enum;
  94. break;
  95. }
  96. }
  97. /* eslint-enable */
  98. for (const runtimeCaching of options.runtimeCaching || []) {
  99. if (typeof runtimeCaching.handler === 'string' &&
  100. !validHandlers.includes(runtimeCaching.handler)) {
  101. throw new WorkboxConfigError(errors_1.errors['invalid-handler-string'] + runtimeCaching.handler);
  102. }
  103. }
  104. }
  105. function validateGenerateSWOptions(input) {
  106. const [validatedOptions, jsonSchema] = validate(input, 'GenerateSW');
  107. ensureValidNavigationPreloadConfig(validatedOptions);
  108. ensureValidCacheExpiration(validatedOptions);
  109. ensureValidRuntimeCachingOrGlobDirectory(validatedOptions);
  110. ensureValidStringHandler(validatedOptions, jsonSchema);
  111. return validatedOptions;
  112. }
  113. exports.validateGenerateSWOptions = validateGenerateSWOptions;
  114. function validateGetManifestOptions(input) {
  115. const [validatedOptions] = validate(input, 'GetManifest');
  116. return validatedOptions;
  117. }
  118. exports.validateGetManifestOptions = validateGetManifestOptions;
  119. function validateInjectManifestOptions(input) {
  120. const [validatedOptions] = validate(input, 'InjectManifest');
  121. return validatedOptions;
  122. }
  123. exports.validateInjectManifestOptions = validateInjectManifestOptions;
  124. // The default `exclude: [/\.map$/, /^manifest.*\.js$/]` value can't be
  125. // represented in the JSON schema, so manually set it for the webpack options.
  126. function validateWebpackGenerateSWOptions(input) {
  127. const inputWithExcludeDefault = Object.assign({
  128. // Make a copy, as exclude can be mutated when used.
  129. exclude: Array.from(DEFAULT_EXCLUDE_VALUE),
  130. }, input);
  131. const [validatedOptions, jsonSchema] = validate(inputWithExcludeDefault, 'WebpackGenerateSW');
  132. ensureValidNavigationPreloadConfig(validatedOptions);
  133. ensureValidCacheExpiration(validatedOptions);
  134. ensureValidStringHandler(validatedOptions, jsonSchema);
  135. return validatedOptions;
  136. }
  137. exports.validateWebpackGenerateSWOptions = validateWebpackGenerateSWOptions;
  138. function validateWebpackInjectManifestOptions(input) {
  139. const inputWithExcludeDefault = Object.assign({
  140. // Make a copy, as exclude can be mutated when used.
  141. exclude: Array.from(DEFAULT_EXCLUDE_VALUE),
  142. }, input);
  143. const [validatedOptions] = validate(inputWithExcludeDefault, 'WebpackInjectManifest');
  144. return validatedOptions;
  145. }
  146. exports.validateWebpackInjectManifestOptions = validateWebpackInjectManifestOptions;