diff --git a/.gitignore b/.gitignore index d7ae53213..91267075b 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,7 @@ firebase-debug.log # Generated TypeScript files and build data tsconfig.tsbuildinfo packages/workbox-*/**/*.d.ts -!packages/workbox-cli/src/index.d.ts +!packages/workbox-*/src/**/*.d.ts packages/workbox-*/**/*.js !packages/workbox-build/**/*.js !packages/workbox-webpack-plugin/**/*.js diff --git a/gulp-tasks/build-node-packages.js b/gulp-tasks/build-node-packages.js index c8ec54d4f..2a5a4bf09 100644 --- a/gulp-tasks/build-node-packages.js +++ b/gulp-tasks/build-node-packages.js @@ -8,6 +8,8 @@ const {parallel} = require('gulp'); const execa = require('execa'); +const fse = require('fs-extra'); +const TJS = require('typescript-json-schema'); const upath = require('upath'); const constants = require('./utils/constants'); @@ -28,7 +30,59 @@ async function buildNodePackage(packagePath) { ], {preferLocal: true}); } +async function generateWorkboxBuildJSONSchema(packagePath) { + const program = TJS.programFromConfig(upath.join(packagePath, + 'tsconfig.json')); + const generator = TJS.buildGenerator(program, { + noExtraProps: true, + required: true, + }); + const optionTypes = [ + 'GenerateSWOptions', + 'GetManifestOptions', + 'InjectManifestOptions', + 'WebpackGenerateSWOptions', + 'WebpackInjectManifestOptions', + ]; + for (const optionType of optionTypes) { + const schema = generator.getSchemaForSymbol(optionType); + // Ideally, we'd set typeOfKeyword so that functions could be represented. + // Instead, we need to hardcode a few overrides to deal with functions. + // See https://github.com/YousefED/typescript-json-schema/issues/424 + if (schema.properties.manifestTransforms) { + schema.properties.manifestTransforms.items = {typeof: 'function'}; + } + if (schema.properties.exclude) { + schema.properties.exclude.items.anyOf = [ + {'$ref': '#/definitions/RegExp'}, + {type: 'string'}, + {typeof: 'function'}, + ]; + } + if (schema.properties.include) { + schema.properties.include.items.anyOf = [ + {'$ref': '#/definitions/RegExp'}, + {type: 'string'}, + {typeof: 'function'}, + ]; + } + if (schema.definitions.RouteMatchCallback) { + delete schema.definitions.RouteMatchCallback.type; + delete schema.definitions.RouteMatchCallback.additionalProperties; + schema.definitions.RouteMatchCallback.typeof = 'function'; + } + await fse.writeJSON(upath.join(packagePath, 'src', 'schema', + `${optionType}.json`), schema); + } +} + async function buildNodeTSPackage(packagePath) { + // Hardcode special logic for workbox-build, as it's the only package + // that requires JSON schema generation. + if (packagePath.endsWith('workbox-build')) { + await generateWorkboxBuildJSONSchema(packagePath); + } + await execa('tsc', ['-b', packagePath], {preferLocal: true}); } diff --git a/gulp-tasks/build-packages.js b/gulp-tasks/build-packages.js index 8fe014ca8..f9e1c52c1 100644 --- a/gulp-tasks/build-packages.js +++ b/gulp-tasks/build-packages.js @@ -23,9 +23,11 @@ async function cleanPackage(packagePath) { if (await fse.pathExists(upath.join(packagePath, 'src', 'index.ts'))) { // Store the list of deleted files, so we can delete directories after. const deletedPaths = await del([ - upath.join(packagePath, '**/*.+(js|mjs|d.ts)'), + `${packagePath}/**/*.+(js|mjs|d.ts)`, // Don't delete files in node_modules. - '!**/node_modules', '!**/node_modules/**/*', + '!**/node_modules/**/*', + // Don't delete anything under src. + `!${packagePath}/src/**/*`, ]); // Any directories in `deletedPaths` that are top-level directories to the @@ -57,12 +59,12 @@ module.exports = { build_packages_clean: cleanSequence(), build_packages: series( cleanSequence(), + // This needs to be a series, not in parallel, so that there isn't a + // race condition with the terser nameCache. + series(build_sw_packages, build_window_packages), parallel( build_node_packages, build_node_ts_packages, - // This needs to be a series, not in parallel, so that there isn't a - // race condition with the terser nameCache. - series(build_sw_packages, build_window_packages), ), ), }; diff --git a/gulp-tasks/utils/versioned-cdn-url.js b/gulp-tasks/utils/versioned-cdn-url.js index 6acf7d18d..b1d04bb26 100644 --- a/gulp-tasks/utils/versioned-cdn-url.js +++ b/gulp-tasks/utils/versioned-cdn-url.js @@ -6,8 +6,8 @@ https://opensource.org/licenses/MIT. */ -const {getCDNOrigin} = require( - '../../packages/workbox-build/src/lib/cdn-utils'); +const cdn = require('../../packages/workbox-build/src/cdn-details.json'); const lernaPkg = require('../../lerna.json'); -module.exports = () => `${getCDNOrigin()}/${lernaPkg.version}`; +module.exports = () => `${cdn.origin}/${cdn.bucketName}/${cdn.releasesDir}` + + `/${lernaPkg.version}`; diff --git a/infra/testing/webpack-build-check.js b/infra/testing/webpack-build-check.js index 382d71312..e039407f2 100644 --- a/infra/testing/webpack-build-check.js +++ b/infra/testing/webpack-build-check.js @@ -7,7 +7,7 @@ */ function joinMessages(errorsOrWarnings) { - if ('message' in errorsOrWarnings[0]) { + if (errorsOrWarnings[0].message) { return errorsOrWarnings.map((item) => item.message).join('\n'); } else { return errorsOrWarnings.join('\n'); diff --git a/package-lock.json b/package-lock.json index 70bb94829..3088fafae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1130,6 +1130,14 @@ "dev": true, "requires": { "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } } }, "ignore": { @@ -4176,6 +4184,59 @@ "integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==", "dev": true }, + "@types/babel__core": { + "version": "7.1.14", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.14.tgz", + "integrity": "sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz", + "integrity": "sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__preset-env": { + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/@types/babel__preset-env/-/babel__preset-env-7.9.1.tgz", + "integrity": "sha512-kkCHo5fk24m3gcH7oVhRLdw6xGF2tog5rc8/CbXIyTSYSO0NhStjEYCaEmRjs5VB5Jy5Wbj+0QvuGdxuGdvAfQ==", + "dev": true + }, + "@types/babel__template": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.0.tgz", + "integrity": "sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.11.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.11.1.tgz", + "integrity": "sha512-Vs0hm0vPahPMYi9tDjtP66llufgO3ST16WXaSTtDGEl9cewAl3AibmxWw6TINOqHPT9z0uABKAYjT9jNSg4npw==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/common-tags": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@types/common-tags/-/common-tags-1.8.0.tgz", + "integrity": "sha512-htRqZr5qn8EzMelhX/Xmx142z218lLyGaeZ3YR8jlze4TATRU9huKKvuBmAJEW4LCC4pnY1N6JAm6p85fMHjhg==", + "dev": true + }, "@types/eslint": { "version": "7.2.7", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.7.tgz", @@ -4208,6 +4269,15 @@ "integrity": "sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g==", "dev": true }, + "@types/fs-extra": { + "version": "9.0.11", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.11.tgz", + "integrity": "sha512-mZsifGG4QeQ7hlkhO56u7zt/ycBgGxSVsFI/6lGTU34VtwkiqrrSDgw0+ygs8kFGWcXnFQWMrzF2h7TtDFNixA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", @@ -4230,6 +4300,12 @@ "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", "dev": true }, + "@types/lodash": { + "version": "4.14.168", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.168.tgz", + "integrity": "sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==", + "dev": true + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -4275,6 +4351,12 @@ "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", "dev": true }, + "@types/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@types/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-ryxTolaNg1l809rknW9q9T7wG8QHcjtZX6syJx7kpOLY2qev75VzC9HMVimUxlA1YzjpGsDI29yLjHBotqhUhA==", + "dev": true + }, "@types/tapable": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.7.tgz", @@ -4867,6 +4949,12 @@ "readable-stream": "^2.0.6" } }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -5798,9 +5886,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001148", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001148.tgz", - "integrity": "sha512-E66qcd0KMKZHNJQt9hiLZGE3J4zuTqE1OnU53miEVtylFbwOEmeA5OsRu90noZful+XGSQOni1aT2tiqu/9yYw==", + "version": "1.0.30001236", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001236.tgz", + "integrity": "sha512-o0PRQSrSCGJKCPZcgMzl5fUaj5xHe8qA2m4QRvnyY4e1lITqoNkr7q/Oh1NcpGSy0Th97UZ35yoKcINPoq7YOQ==", "dev": true }, "caseless": { @@ -5832,6 +5920,15 @@ "type-detect": "^4.0.5" } }, + "chai-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", + "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "dev": true, + "requires": { + "check-error": "^1.0.2" + } + }, "chai-match-pattern": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/chai-match-pattern/-/chai-match-pattern-1.2.0.tgz", @@ -7070,6 +7167,12 @@ "sha.js": "^2.4.8" } }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -8188,6 +8291,14 @@ "dev": true, "requires": { "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } } }, "ignore": { @@ -10554,6 +10665,14 @@ "requires": { "is-stream": "^2.0.0", "type-fest": "^0.8.0" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } } }, "he": { @@ -11949,6 +12068,15 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "~0.0.0" + } + }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -11980,6 +12108,12 @@ "universalify": "^1.0.0" } }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, "jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", @@ -12411,6 +12545,12 @@ } } }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "make-fetch-happen": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz", @@ -17414,6 +17554,20 @@ "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=", "dev": true }, + "ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + } + }, "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -17472,9 +17626,9 @@ "dev": true }, "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, "type-is": { @@ -17503,11 +17657,119 @@ } }, "typescript": { - "version": "3.9.7", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", - "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.5.tgz", + "integrity": "sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==", "dev": true }, + "typescript-json-schema": { + "version": "0.50.1", + "resolved": "https://registry.npmjs.org/typescript-json-schema/-/typescript-json-schema-0.50.1.tgz", + "integrity": "sha512-GCof/SDoiTDl0qzPonNEV4CHyCsZEIIf+mZtlrjoD8vURCcEzEfa2deRuxYid8Znp/e27eDR7Cjg8jgGrimBCA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.7", + "@types/node": "^14.14.33", + "glob": "^7.1.6", + "json-stable-stringify": "^1.0.1", + "ts-node": "^9.1.1", + "typescript": "~4.2.3", + "yargs": "^16.2.0" + }, + "dependencies": { + "@types/json-schema": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", + "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", + "dev": true + }, + "@types/node": { + "version": "14.17.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.3.tgz", + "integrity": "sha512-e6ZowgGJmTuXa3GyaPbTGxX17tnThl2aSSizrFthQ7m9uLGZBXiGhgE55cjRZTF5kjZvYn9EOPOMljdjwbflxw==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "typescript": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", + "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.7", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", + "dev": true + } + } + }, "uc.micro": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", @@ -19020,6 +19282,12 @@ "fd-slicer": "~1.1.0" } }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 73426e10f..59c59357e 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,13 @@ "@rollup/plugin-multi-entry": "^4.0.0", "@rollup/plugin-node-resolve": "^11.2.1", "@rollup/plugin-replace": "^2.4.1", + "@types/babel__core": "^7.1.12", + "@types/babel__preset-env": "^7.9.1", + "@types/common-tags": "^1.8.0", "@types/estree": "0.0.45", + "@types/fs-extra": "^9.0.5", + "@types/lodash": "^4.14.165", + "@types/stringify-object": "^3.3.0", "@typescript-eslint/eslint-plugin": "^2.30.0", "@typescript-eslint/parser": "^2.30.0", "acorn": "^8.0.1", @@ -63,6 +69,7 @@ "bytes": "^3.1.0", "camelcase": "^6.0.0", "chai": "^4.2.0", + "chai-as-promised": "^7.1.1", "chai-match-pattern": "^1.2.0", "chalk": "^4.1.0", "clear-module": "^4.1.1", @@ -107,7 +114,9 @@ "service-worker-mock": "^1.9.3", "sinon": "^9.0.3", "tempy": "^0.6.0", - "typescript": "^3.9.7", + "type-fest": "^0.20.2", + "typescript": "^4.1.5", + "typescript-json-schema": "^0.50.1", "upath": "^1.2.0", "webpack-v4": "npm:webpack@^4.44.2", "webpack-v5": "npm:webpack@^5.9.0", diff --git a/packages/workbox-build/package-lock.json b/packages/workbox-build/package-lock.json index cb3596e86..8f24f758c 100644 --- a/packages/workbox-build/package-lock.json +++ b/packages/workbox-build/package-lock.json @@ -4,6 +4,16 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@apideck/better-ajv-errors": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.2.4.tgz", + "integrity": "sha512-scZfMrs2MhqtaHgBhVif2YsrFmSUZ9BxryilZWLnafQYCLCx6l/lu+BIPlCJZ+s9JucUn7YdtnsVPb27oXKRjQ==", + "requires": { + "json-schema": "^0.3.0", + "jsonpointer": "^4.1.0", + "leven": "^3.1.0" + } + }, "@babel/code-frame": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", @@ -1055,6 +1065,25 @@ "@types/node": "*" } }, + "ajv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz", + "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.0.0.tgz", + "integrity": "sha512-ULd1QMjRoH6JDNUQIfDLrlE+OgZlFaxyYCjzt58uNuUQtKXt8/U+vK/8Ql0gyn/C5mqZzUWtKMqr/4YquvTrWA==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -1234,6 +1263,11 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1402,6 +1436,16 @@ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" }, + "json-schema": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.3.0.tgz", + "integrity": "sha512-TYfxx36xfl52Rf1LU9HyWSLGPdYLL+SQ8/E/0yVyKG8wCCDaSrhPap0vEdlsZWRaS6tnKKLPGiEJGiREVC8kxQ==" + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "json5": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", @@ -1419,6 +1463,11 @@ "universalify": "^1.0.0" } }, + "jsonpointer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.1.0.tgz", + "integrity": "sha512-CXcRvMyTlnR53xMcKnuMzfCA5i/nfblTnnr74CZb6C4vG39eu6w51t7nKmU5MfLfbTgGItliNyjO/ciNPDqClg==" + }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -1602,6 +1651,11 @@ } } }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, "resolve": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", @@ -1797,6 +1851,14 @@ "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, "webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", @@ -1812,6 +1874,131 @@ "webidl-conversions": "^4.0.2" } }, + "workbox-background-sync": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.1.5.tgz", + "integrity": "sha512-VbUmPLsdz+sLzuNxHvMylzyRTiM4q+q7rwLBk3p2mtRL5NZozI8j/KgoGbno96vs84jx4b9zCZMEOIKEUTPf6w==", + "requires": { + "workbox-core": "^6.1.5" + } + }, + "workbox-broadcast-update": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.1.5.tgz", + "integrity": "sha512-zGrTTs+n4wHpYtqYMqBg6kl/x5j1UrczGCQnODSHTxIDV8GXLb/GtA1BCZdysNxpMmdVSeLmTcgIYAAqWFamrA==", + "requires": { + "workbox-core": "^6.1.5" + } + }, + "workbox-cacheable-response": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.1.5.tgz", + "integrity": "sha512-x8DC71lO/JCgiaJ194l9le8wc8lFPLgUpDkLhp2si7mXV6S/wZO+8Osvw1LLgYa8YYTWGbhbFhFTXIkEMknIIA==", + "requires": { + "workbox-core": "^6.1.5" + } + }, + "workbox-core": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.1.5.tgz", + "integrity": "sha512-9SOEle7YcJzg3njC0xMSmrPIiFjfsFm9WjwGd5enXmI8Lwk8wLdy63B0nzu5LXoibEmS9k+aWF8EzaKtOWjNSA==" + }, + "workbox-expiration": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.1.5.tgz", + "integrity": "sha512-6cN+FVbh8fNq56LFKPMchGNKCJeyboHsDuGBqmhDUPvD4uDjsegQpDQzn52VaE0cpywbSIsDF/BSq9E9Yjh5oQ==", + "requires": { + "workbox-core": "^6.1.5" + } + }, + "workbox-google-analytics": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.1.5.tgz", + "integrity": "sha512-LYsJ/VxTkYVLxM1uJKXZLz4cJdemidY7kPyAYtKVZ6EiDG89noASqis75/5lhqM1m3HwQfp2DtoPrelKSpSDBA==", + "requires": { + "workbox-background-sync": "^6.1.5", + "workbox-core": "^6.1.5", + "workbox-routing": "^6.1.5", + "workbox-strategies": "^6.1.5" + } + }, + "workbox-navigation-preload": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.1.5.tgz", + "integrity": "sha512-hDbNcWlffv0uvS21jCAC/mYk7NzaGRSWOQXv1p7bj2aONAX5l699D2ZK4D27G8TO0BaLHUmW/1A5CZcsvweQdg==", + "requires": { + "workbox-core": "^6.1.5" + } + }, + "workbox-precaching": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.1.5.tgz", + "integrity": "sha512-yhm1kb6wgi141JeM5X7z42XJxCry53tbMLB3NgrxktrZbwbrJF8JILzYy+RFKC9tHC6u2bPmL789GPLT2NCDzw==", + "requires": { + "workbox-core": "^6.1.5", + "workbox-routing": "^6.1.5", + "workbox-strategies": "^6.1.5" + } + }, + "workbox-range-requests": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.1.5.tgz", + "integrity": "sha512-iACChSapzB0yuIum3ascP/+cfBNuZi5DRrE+u4u5mCHigPlwfSWtlaY+y8p+a8EwcDTVTZVtnrGrRnF31SiLqQ==", + "requires": { + "workbox-core": "^6.1.5" + } + }, + "workbox-recipes": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.1.5.tgz", + "integrity": "sha512-MD1yabHca6O/oj1hrRdfj9cRwhKA5zqIE53rWOAg/dKMMzWQsf9nyRbXRgzK3a13iQvYKuQzURU4Cx58tdnR+Q==", + "requires": { + "workbox-cacheable-response": "^6.1.5", + "workbox-core": "^6.1.5", + "workbox-expiration": "^6.1.5", + "workbox-precaching": "^6.1.5", + "workbox-routing": "^6.1.5", + "workbox-strategies": "^6.1.5" + } + }, + "workbox-routing": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.1.5.tgz", + "integrity": "sha512-uC/Ctz+4GXGL42h1WxUNKxqKRik/38uS0NZ6VY/EHqL2F1ObLFqMHUZ4ZYvyQsKdyI82cxusvhJZHOrY0a2fIQ==", + "requires": { + "workbox-core": "^6.1.5" + } + }, + "workbox-strategies": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.1.5.tgz", + "integrity": "sha512-QhiOn9KT9YGBdbfWOmJT6pXZOIAxaVrs6J6AMYzRpkUegBTEcv36+ZhE/cfHoT0u2fxVtthHnskOQ/snEzaXQw==", + "requires": { + "workbox-core": "^6.1.5" + } + }, + "workbox-streams": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.1.5.tgz", + "integrity": "sha512-OI1kLvRHGFXV+soDvs6aEwfBwdAkvPB0mRryqdh3/K17qUj/1gRXc8QtpgU+83xqx/I/ar2bTCIj0KPzI/ChCQ==", + "requires": { + "workbox-core": "^6.1.5", + "workbox-routing": "^6.1.5" + } + }, + "workbox-sw": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.1.5.tgz", + "integrity": "sha512-IMDiqxYbKzPorZLGMUMacLB6r76iVQbdTzYthIZoPfy+uFURJFUtqiWQJKg1L+RMyuYXwKXTahCIGkgFs4jBeg==" + }, + "workbox-window": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.1.5.tgz", + "integrity": "sha512-akL0X6mAegai2yypnq78RgfazeqvKbsllRtEI4dnbhPcRINEY1NmecFmsQk8SD+zWLK1gw5OdwAOX+zHSRVmeA==", + "requires": { + "workbox-core": "^6.1.5" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/packages/workbox-build/package.json b/packages/workbox-build/package.json index 170a418ea..a565bb3e0 100644 --- a/packages/workbox-build/package.json +++ b/packages/workbox-build/package.json @@ -20,14 +20,16 @@ "bugs": "https://github.com/GoogleChrome/workbox/issues", "homepage": "https://github.com/GoogleChrome/workbox", "dependencies": { + "@apideck/better-ajv-errors": "^0.2.4", "@babel/core": "^7.11.1", "@babel/preset-env": "^7.11.0", "@babel/runtime": "^7.11.2", - "@hapi/joi": "^16.1.8", "@rollup/plugin-babel": "^5.2.0", "@rollup/plugin-node-resolve": "^11.2.1", "@rollup/plugin-replace": "^2.4.1", "@surma/rollup-plugin-off-main-thread": "^1.4.1", + "ajv": "^8.6.0", + "ajv-keywords": "^5.0.0", "common-tags": "^1.8.0", "fast-json-stable-stringify": "^2.1.0", "fs-extra": "^9.0.1", @@ -60,6 +62,6 @@ }, "main": "build/index.js", "workbox": { - "packageType": "node" + "packageType": "node_ts" } } diff --git a/packages/workbox-build/src/_types.js b/packages/workbox-build/src/_types.js index addee892e..59a9dc8b1 100644 --- a/packages/workbox-build/src/_types.js +++ b/packages/workbox-build/src/_types.js @@ -89,13 +89,13 @@ import './_version.mjs'; * property to use when creating the * [`ExpirationPlugin`]{@link module:workbox-expiration.ExpirationPlugin}. * - * @property {number} [options.expiration.maxEntries] The `maxAgeSeconds` + * @property {number} [options.expiration.maxEntries] The `maxEntries` * property to use when creating the * [`ExpirationPlugin`]{@link module:workbox-expiration.ExpirationPlugin}. * * @property {Object} [options.precacheFallback] * - * @property {number} [options.precacheFallback.fallbackURL] The `fallbackURL` + * @property {string} [options.precacheFallback.fallbackURL] The `fallbackURL` * property to use when creating the * [`PrecacheFallbackPlugin`]{@link module:workbox-precaching.PrecacheFallbackPlugin}. * diff --git a/packages/workbox-build/src/generate-sw.js b/packages/workbox-build/src/generate-sw.ts similarity index 90% rename from packages/workbox-build/src/generate-sw.js rename to packages/workbox-build/src/generate-sw.ts index 1a60808f1..a1f4b2547 100644 --- a/packages/workbox-build/src/generate-sw.js +++ b/packages/workbox-build/src/generate-sw.ts @@ -6,14 +6,13 @@ https://opensource.org/licenses/MIT. */ -const upath = require('upath'); +import upath from 'upath'; -const generateSWSchema = require('./options/schema/generate-sw'); -const getFileManifestEntries = require('./lib/get-file-manifest-entries'); -const rebasePath = require('./lib/rebase-path'); -const validate = require('./lib/validate-options'); -const writeServiceWorkerUsingDefaultTemplate = - require('./lib/write-sw-using-default-template'); +import {BuildResult, GetManifestOptions} from './types'; +import {getFileManifestEntries} from './lib/get-file-manifest-entries'; +import {rebasePath} from './lib/rebase-path'; +import {validateGenerateSWOptions} from './lib/validate-options'; +import {writeSWUsingDefaultTemplate} from './lib/write-sw-using-default-template'; // eslint-disable-next-line jsdoc/newline-after-description /** @@ -194,12 +193,13 @@ const writeServiceWorkerUsingDefaultTemplate = * * @memberof module:workbox-build */ -async function generateSW(config) { - const options = validate(config, generateSWSchema); +export async function generateSW(config: unknown): Promise { + const options = validateGenerateSWOptions(config); + let entriesResult; if (options.globDirectory) { // Make sure we leave swDest out of the precache manifest. - options.globIgnores.push(rebasePath({ + options.globIgnores!.push(rebasePath({ baseDirectory: options.globDirectory, file: options.swDest, })); @@ -209,21 +209,32 @@ async function generateSW(config) { if (!options.inlineWorkboxRuntime) { const swDestDir = upath.dirname(options.swDest); const workboxRuntimeFile = upath.join(swDestDir, 'workbox-*.js'); - options.globIgnores.push(rebasePath({ + options.globIgnores!.push(rebasePath({ baseDirectory: options.globDirectory, file: workboxRuntimeFile, })); } - } - const {count, size, manifestEntries, warnings} = - await getFileManifestEntries(options); + // We've previously asserted that options.globDirectory is set, so this + // should be a safe cast. + entriesResult = await getFileManifestEntries(options as GetManifestOptions); + } else { + entriesResult = { + count: 0, + manifestEntries: [], + size: 0, + warnings: [], + }; + } - const filePaths = await writeServiceWorkerUsingDefaultTemplate(Object.assign({ - manifestEntries, + const filePaths = await writeSWUsingDefaultTemplate(Object.assign({ + manifestEntries: entriesResult.manifestEntries, }, options)); - return {count, filePaths, size, warnings}; + return { + filePaths, + count: entriesResult.count, + size: entriesResult.size, + warnings: entriesResult.warnings, + }; } - -module.exports = generateSW; diff --git a/packages/workbox-build/src/get-manifest.js b/packages/workbox-build/src/get-manifest.ts similarity index 91% rename from packages/workbox-build/src/get-manifest.js rename to packages/workbox-build/src/get-manifest.ts index 8d3abf590..447e46584 100644 --- a/packages/workbox-build/src/get-manifest.js +++ b/packages/workbox-build/src/get-manifest.ts @@ -6,9 +6,9 @@ https://opensource.org/licenses/MIT. */ -const getFileManifestEntries = require('./lib/get-file-manifest-entries'); -const getManifestSchema = require('./options/schema/get-manifest'); -const validate = require('./lib/validate-options'); +import {getFileManifestEntries} from './lib/get-file-manifest-entries'; +import {GetManifestResult} from './types'; +import {validateGetManifestOptions} from './lib/validate-options'; // eslint-disable-next-line jsdoc/newline-after-description /** @@ -88,13 +88,8 @@ const validate = require('./lib/validate-options'); * * @memberof module:workbox-build */ -async function getManifest(config) { - const options = validate(config, getManifestSchema); +export async function getManifest(config: unknown): Promise { + const options = validateGetManifestOptions(config); - const {manifestEntries, count, size, warnings} = - await getFileManifestEntries(options); - - return {manifestEntries, count, size, warnings}; + return await getFileManifestEntries(options); } - -module.exports = getManifest; diff --git a/packages/workbox-build/src/index.js b/packages/workbox-build/src/index.ts similarity index 51% rename from packages/workbox-build/src/index.js rename to packages/workbox-build/src/index.ts index 84317f1c9..7b79bf5c9 100644 --- a/packages/workbox-build/src/index.js +++ b/packages/workbox-build/src/index.ts @@ -6,19 +6,22 @@ https://opensource.org/licenses/MIT. */ -const {getModuleURL} = require('./lib/cdn-utils'); -const copyWorkboxLibraries = require('./lib/copy-workbox-libraries'); -const generateSW = require('./generate-sw'); -const getManifest = require('./get-manifest'); -const injectManifest = require('./inject-manifest'); +import {copyWorkboxLibraries} from './lib/copy-workbox-libraries'; +import {getModuleURL} from './lib/cdn-utils'; +import {generateSW} from './generate-sw'; +import {getManifest} from './get-manifest'; +import {injectManifest} from './inject-manifest'; + /** * @module workbox-build */ -module.exports = { +export default { copyWorkboxLibraries, generateSW, getManifest, getModuleURL, injectManifest, }; + +export * from './types'; diff --git a/packages/workbox-build/src/inject-manifest.js b/packages/workbox-build/src/inject-manifest.ts similarity index 86% rename from packages/workbox-build/src/inject-manifest.js rename to packages/workbox-build/src/inject-manifest.ts index bc922ce07..9d1e133b1 100644 --- a/packages/workbox-build/src/inject-manifest.js +++ b/packages/workbox-build/src/inject-manifest.ts @@ -6,20 +6,20 @@ https://opensource.org/licenses/MIT. */ -const assert = require('assert'); -const fse = require('fs-extra'); -const sourceMapURL = require('source-map-url'); -const stringify = require('fast-json-stable-stringify'); -const upath = require('upath'); - -const errors = require('./lib/errors'); -const escapeRegexp = require('./lib/escape-regexp'); -const getFileManifestEntries = require('./lib/get-file-manifest-entries'); -const injectManifestSchema = require('./options/schema/inject-manifest'); -const rebasePath = require('./lib/rebase-path'); -const replaceAndUpdateSourceMap = - require('./lib/replace-and-update-source-map'); -const validate = require('./lib/validate-options'); +import {RawSourceMap} from 'source-map'; +import assert from 'assert'; +import fse from 'fs-extra'; +import sourceMapURL from 'source-map-url'; +import stringify from 'fast-json-stable-stringify'; +import upath from 'upath'; + +import {BuildResult} from './types'; +import {errors} from './lib/errors'; +import {escapeRegExp} from './lib/escape-regexp'; +import {getFileManifestEntries} from './lib/get-file-manifest-entries'; +import {rebasePath} from './lib/rebase-path'; +import {replaceAndUpdateSourceMap} from './lib/replace-and-update-source-map'; +import {validateInjectManifestOptions} from './lib/validate-options'; // eslint-disable-next-line jsdoc/newline-after-description /** @@ -116,22 +116,22 @@ const validate = require('./lib/validate-options'); * * @memberof module:workbox-build */ -async function injectManifest(config) { - const options = validate(config, injectManifestSchema); +export async function injectManifest(config: unknown): Promise { + const options = validateInjectManifestOptions(config); // Make sure we leave swSrc and swDest out of the precache manifest. for (const file of [options.swSrc, options.swDest]) { - options.globIgnores.push(rebasePath({ + options.globIgnores!.push(rebasePath({ file, baseDirectory: options.globDirectory, })); } - const globalRegexp = new RegExp(escapeRegexp(options.injectionPoint), 'g'); + const globalRegexp = new RegExp(escapeRegExp(options.injectionPoint!), 'g'); const {count, size, manifestEntries, warnings} = await getFileManifestEntries(options); - let swFileContents; + let swFileContents: string; try { swFileContents = await fse.readFile(options.swSrc, 'utf8'); } catch (error) { @@ -153,9 +153,9 @@ async function injectManifest(config) { options.injectionPoint); const manifestString = stringify(manifestEntries); - const filesToWrite = {}; + const filesToWrite: {[key: string]: string} = {}; - const url = sourceMapURL.getFrom(swFileContents); + const url: string = sourceMapURL.getFrom(swFileContents); // If our swSrc file contains a sourcemap, we would invalidate that // mapping if we just replaced injectionPoint with the stringified manifest. // Instead, we need to update the swDest contents as well as the sourcemap @@ -166,9 +166,9 @@ async function injectManifest(config) { const sourcemapSrcPath = upath.resolve(upath.dirname(options.swSrc), url); const sourcemapDestPath = upath.resolve(upath.dirname(options.swDest), url); - let originalMap; + let originalMap: RawSourceMap; try { - originalMap = await fse.readJSON(sourcemapSrcPath, 'utf8'); + originalMap = await fse.readJSON(sourcemapSrcPath, {encoding: 'utf8'}); } catch (error) { throw new Error(`${errors['cant-find-sourcemap']} ${error.message}`); } @@ -178,7 +178,7 @@ async function injectManifest(config) { jsFilename: upath.basename(options.swDest), originalSource: swFileContents, replaceString: manifestString, - searchString: options.injectionPoint, + searchString: options.injectionPoint!, }); filesToWrite[options.swDest] = source; @@ -194,7 +194,7 @@ async function injectManifest(config) { try { await fse.mkdirp(upath.dirname(file)); } catch (error) { - throw new Error(errors['unable-to-make-injection-directory'] + + throw new Error(errors['unable-to-make-sw-directory'] + ` '${error.message}'`); } @@ -209,5 +209,3 @@ async function injectManifest(config) { filePaths: Object.keys(filesToWrite).map((f) => upath.resolve(f)), }; } - -module.exports = injectManifest; diff --git a/packages/workbox-build/src/lib/additional-manifest-entries-transform.js b/packages/workbox-build/src/lib/additional-manifest-entries-transform.js deleted file mode 100644 index 57d474761..000000000 --- a/packages/workbox-build/src/lib/additional-manifest-entries-transform.js +++ /dev/null @@ -1,42 +0,0 @@ -/* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/ - -const errors = require('./errors'); - -module.exports = (additionalManifestEntries) => { - return (manifest) => { - const warnings = []; - const stringEntries = new Set(); - - for (const additionalEntry of additionalManifestEntries) { - // Warn about either a string or an object that lacks a precache property. - // (An object with a revision property set to null is okay.) - if (typeof additionalEntry === 'string') { - stringEntries.add(additionalEntry); - } else if (additionalEntry && additionalEntry.revision === undefined) { - stringEntries.add(additionalEntry.url); - } - - manifest.push(additionalEntry); - } - - if (stringEntries.size > 0) { - let urls = '\n'; - for (const stringEntry of stringEntries) { - urls += ` - ${stringEntry}\n`; - } - - warnings.push(errors['string-entry-warning'] + urls); - } - - return { - manifest, - warnings, - }; - }; -}; diff --git a/packages/workbox-build/src/lib/additional-manifest-entries-transform.ts b/packages/workbox-build/src/lib/additional-manifest-entries-transform.ts new file mode 100644 index 000000000..6780a8bde --- /dev/null +++ b/packages/workbox-build/src/lib/additional-manifest-entries-transform.ts @@ -0,0 +1,49 @@ +/* + Copyright 2019 Google LLC + + Use of this source code is governed by an MIT-style + license that can be found in the LICENSE file or at + https://opensource.org/licenses/MIT. +*/ + +import {errors} from './errors'; +import {ManifestEntry} from '../types'; + +export function additionalManifestEntriesTransform(additionalManifestEntries: Array) { + return (manifest: Array) => { + const warnings: Array = []; + const stringEntries = new Set(); + + for (const additionalEntry of additionalManifestEntries) { + // Warn about either a string or an object that lacks a revision property. + // (An object with a revision property set to null is okay.) + if (typeof additionalEntry === 'string') { + stringEntries.add(additionalEntry); + manifest.push({ + revision: null, + size: 0, + url: additionalEntry, + }); + } else { + if (additionalEntry && additionalEntry.revision === undefined) { + stringEntries.add(additionalEntry.url); + } + manifest.push(Object.assign({size: 0}, additionalEntry)); + } + } + + if (stringEntries.size > 0) { + let urls = '\n'; + for (const stringEntry of stringEntries) { + urls += ` - ${stringEntry}\n`; + } + + warnings.push(errors['string-entry-warning'] + urls); + } + + return { + manifest, + warnings, + }; + }; +} diff --git a/packages/workbox-build/src/lib/bundle.js b/packages/workbox-build/src/lib/bundle.ts similarity index 72% rename from packages/workbox-build/src/lib/bundle.js rename to packages/workbox-build/src/lib/bundle.ts index 7f608e039..555acd2ae 100644 --- a/packages/workbox-build/src/lib/bundle.js +++ b/packages/workbox-build/src/lib/bundle.ts @@ -6,25 +6,32 @@ https://opensource.org/licenses/MIT. */ -const {babel} = require('@rollup/plugin-babel'); -const {nodeResolve} = require('@rollup/plugin-node-resolve'); -const {rollup} = require('rollup'); -const {terser} = require('rollup-plugin-terser'); -const {writeFile} = require('fs-extra'); -const omt = require('@surma/rollup-plugin-off-main-thread'); -const presetEnv = require('@babel/preset-env'); -const replace = require('@rollup/plugin-replace'); -const tempy = require('tempy'); -const upath = require('upath'); - -module.exports = async ({ +import {babel} from '@rollup/plugin-babel'; +import {nodeResolve} from '@rollup/plugin-node-resolve'; +import {rollup, Plugin} from 'rollup'; +import {terser} from 'rollup-plugin-terser'; +import {writeFile} from 'fs-extra'; +import omt from '@surma/rollup-plugin-off-main-thread'; +import presetEnv from '@babel/preset-env'; +import replace from '@rollup/plugin-replace'; +import tempy from 'tempy'; +import upath from 'upath'; + +import {GeneratePartial, RequiredSWDestPartial} from '../types'; + +interface NameAndContents { + contents: string | Uint8Array; + name: string; +} + +export async function bundle({ babelPresetEnvTargets, inlineWorkboxRuntime, mode, sourcemap, swDest, unbundledCode, -}) => { +}: Omit & RequiredSWDestPartial & {unbundledCode: string}): Promise> { // We need to write this to the "real" file system, as Rollup won't read from // a custom file system. const {dir, base} = upath.parse(swDest); @@ -65,7 +72,11 @@ module.exports = async ({ })); } - const rollupConfig = { + const rollupConfig: { + input: string; + manualChunks?: (id: string) => string | undefined; + plugins: Array; + } = { plugins, input: temporaryFile, }; @@ -87,9 +98,9 @@ module.exports = async ({ format: inlineWorkboxRuntime ? 'es' : 'amd', }); - const files = []; + const files: Array= []; for (const chunkOrAsset of output) { - if (chunkOrAsset.isAsset) { + if (chunkOrAsset.type === 'asset') { files.push({ name: chunkOrAsset.fileName, contents: chunkOrAsset.source, @@ -120,7 +131,10 @@ module.exports = async ({ file.name = upath.format({ dir, base: file.name, + ext: '', + name: '', + root: '', }); return file; }); -}; +} diff --git a/packages/workbox-build/src/lib/cdn-utils.js b/packages/workbox-build/src/lib/cdn-utils.js deleted file mode 100644 index 8365385b1..000000000 --- a/packages/workbox-build/src/lib/cdn-utils.js +++ /dev/null @@ -1,40 +0,0 @@ -/* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/ - -const assert = require('assert'); - -const cdn = require('../cdn-details.json'); -const errors = require('./errors'); - -const getCDNOrigin = () => { - return `${cdn.origin}/${cdn.bucketName}/${cdn.releasesDir}`; -}; - -const getVersionedCDNURL = () => { - return `${getCDNOrigin()}/${cdn.latestVersion}`; -}; - -const getModuleURL = (moduleName, buildType) => { - assert(moduleName, errors['no-module-name']); - - if (buildType) { - const pkgJson = require(`${moduleName}/package.json`); - if (buildType === 'dev' && pkgJson.workbox.prodOnly) { - // This is not due to a public-facing exception, so just throw an Error(), - // without creating an entry in errors.js. - throw Error(`The 'dev' build of ${moduleName} is not available.`); - } - return `${getVersionedCDNURL()}/${moduleName}.${buildType.slice(0, 4)}.js`; - } - return `${getVersionedCDNURL()}/${moduleName}.js`; -}; - -module.exports = { - getCDNOrigin, - getModuleURL, -}; diff --git a/packages/workbox-build/src/lib/cdn-utils.ts b/packages/workbox-build/src/lib/cdn-utils.ts new file mode 100644 index 000000000..33ab081ef --- /dev/null +++ b/packages/workbox-build/src/lib/cdn-utils.ts @@ -0,0 +1,36 @@ +/* + Copyright 2021 Google LLC + + Use of this source code is governed by an MIT-style + license that can be found in the LICENSE file or at + https://opensource.org/licenses/MIT. +*/ + +import {ok} from 'assert'; + +import {BuildType, WorkboxPackageJSON} from '../types'; +import {errors} from './errors'; +import * as cdn from '../cdn-details.json'; + +function getVersionedURL(): string { + return `${getCDNPrefix()}/${cdn.latestVersion}`; +} + +function getCDNPrefix() { + return `${cdn.origin}/${cdn.bucketName}/${cdn.releasesDir}`; +} + +export function getModuleURL(moduleName: string, buildType: BuildType) { + ok(moduleName, errors['no-module-name']); + + if (buildType) { + const pkgJson: WorkboxPackageJSON = require(`${moduleName}/package.json`); + if (buildType === 'dev' && pkgJson.workbox && pkgJson.workbox.prodOnly) { + // This is not due to a public-facing exception, so just throw an Error(), + // without creating an entry in errors.js. + throw Error(`The 'dev' build of ${moduleName} is not available.`); + } + return `${getVersionedURL()}/${moduleName}.${buildType.slice(0, 4)}.js`; + } + return `${getVersionedURL()}/${moduleName}.js`; +} diff --git a/packages/workbox-build/src/lib/copy-workbox-libraries.js b/packages/workbox-build/src/lib/copy-workbox-libraries.ts similarity index 86% rename from packages/workbox-build/src/lib/copy-workbox-libraries.js rename to packages/workbox-build/src/lib/copy-workbox-libraries.ts index ecca3c8da..3385aa980 100644 --- a/packages/workbox-build/src/lib/copy-workbox-libraries.js +++ b/packages/workbox-build/src/lib/copy-workbox-libraries.ts @@ -6,10 +6,11 @@ https://opensource.org/licenses/MIT. */ -const fse = require('fs-extra'); -const upath = require('upath'); -const errors = require('./errors'); +import fse from 'fs-extra'; +import upath from 'upath'; +import {WorkboxPackageJSON} from '../types'; +import {errors} from './errors'; // Used to filter the libraries to copy based on our package.json dependencies. const WORKBOX_PREFIX = 'workbox-'; @@ -36,8 +37,8 @@ const BUILD_DIR = 'build'; * * @alias module:workbox-build.copyWorkboxLibraries */ -module.exports = async (destDirectory) => { - const thisPkg = require('../../package.json'); +export async function copyWorkboxLibraries(destDirectory: string): Promise { + const thisPkg: WorkboxPackageJSON = require('../../package.json'); // Use the version string from workbox-build in the name of the parent // directory. This should be safe, because lerna will bump workbox-build's // pkg.version whenever one of the dependent libraries gets bumped, and we @@ -46,8 +47,8 @@ module.exports = async (destDirectory) => { const workboxDirectoryPath = upath.join(destDirectory, workboxDirectoryName); await fse.ensureDir(workboxDirectoryPath); - const copyPromises = []; - const librariesToCopy = Object.keys(thisPkg.dependencies).filter( + const copyPromises: Array> = []; + const librariesToCopy = Object.keys(thisPkg.dependencies || {}).filter( (dependency) => dependency.startsWith(WORKBOX_PREFIX)); for (const library of librariesToCopy) { @@ -69,4 +70,4 @@ module.exports = async (destDirectory) => { } catch (error) { throw Error(`${errors['unable-to-copy-workbox-libraries']} ${error}`); } -}; +} diff --git a/packages/workbox-build/src/lib/errors.js b/packages/workbox-build/src/lib/errors.ts similarity index 95% rename from packages/workbox-build/src/lib/errors.js rename to packages/workbox-build/src/lib/errors.ts index 52ec8a837..11eb82f4a 100644 --- a/packages/workbox-build/src/lib/errors.js +++ b/packages/workbox-build/src/lib/errors.ts @@ -6,9 +6,9 @@ https://opensource.org/licenses/MIT. */ -const ol = require('common-tags').oneLine; +import {oneLine as ol} from 'common-tags'; -module.exports = { +export const errors = { 'unable-to-get-rootdir': `Unable to get the root directory of your web app.`, 'no-extension': ol`Unable to detect a usable extension for a file in your web app directory.`, @@ -114,4 +114,8 @@ module.exports = { runtimeCaching option.`, 'cant-find-sourcemap': ol`The swSrc file refers to a sourcemap that can't be opened:`, + 'nav-preload-runtime-caching': ol`When using navigationPreload, you must also + configure a runtimeCaching route that will use the preloaded response.`, + 'cache-name-required': ol`When using cache expiration, you must also + configure a custom cacheName.`, }; diff --git a/packages/workbox-build/src/lib/escape-regexp.js b/packages/workbox-build/src/lib/escape-regexp.ts similarity index 73% rename from packages/workbox-build/src/lib/escape-regexp.js rename to packages/workbox-build/src/lib/escape-regexp.ts index 47b109e9a..1e4d9f4b9 100644 --- a/packages/workbox-build/src/lib/escape-regexp.js +++ b/packages/workbox-build/src/lib/escape-regexp.ts @@ -7,4 +7,6 @@ */ // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions -module.exports = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +export function escapeRegExp(str: string) { + return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} diff --git a/packages/workbox-build/src/lib/get-composite-details.js b/packages/workbox-build/src/lib/get-composite-details.ts similarity index 75% rename from packages/workbox-build/src/lib/get-composite-details.js rename to packages/workbox-build/src/lib/get-composite-details.ts index 36f3c24c3..3412bec74 100644 --- a/packages/workbox-build/src/lib/get-composite-details.js +++ b/packages/workbox-build/src/lib/get-composite-details.ts @@ -6,9 +6,11 @@ https://opensource.org/licenses/MIT. */ -const crypto = require('crypto'); +import crypto from 'crypto'; -module.exports = (compositeURL, dependencyDetails) => { +import {FileDetails} from '../types'; + +export function getCompositeDetails(compositeURL: string, dependencyDetails: Array): FileDetails { let totalSize = 0; let compositeHash = ''; @@ -26,4 +28,4 @@ module.exports = (compositeURL, dependencyDetails) => { hash: hashOfHashes, size: totalSize, }; -}; +} diff --git a/packages/workbox-build/src/lib/get-file-details.js b/packages/workbox-build/src/lib/get-file-details.js deleted file mode 100644 index a26519fbd..000000000 --- a/packages/workbox-build/src/lib/get-file-details.js +++ /dev/null @@ -1,61 +0,0 @@ -/* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/ - -const glob = require('glob'); -const upath = require('upath'); - -const errors = require('./errors'); -const getFileSize = require('./get-file-size'); -const getFileHash = require('./get-file-hash'); - -module.exports = ({ - globDirectory, - globFollow, - globIgnores, - globPattern, - globStrict, -}) => { - let globbedFiles; - let warning; - - try { - globbedFiles = glob.sync(globPattern, { - cwd: globDirectory, - follow: globFollow, - ignore: globIgnores, - strict: globStrict, - }); - } catch (err) { - throw new Error(errors['unable-to-glob-files'] + ` '${err.message}'`); - } - - if (globbedFiles.length === 0) { - warning = errors['useless-glob-pattern'] + ' ' + - JSON.stringify({globDirectory, globPattern, globIgnores}, null, 2); - } - - const fileDetails = globbedFiles.map((file) => { - const fullPath = upath.join(globDirectory, file); - const fileSize = getFileSize(fullPath); - if (fileSize === null) { - return null; - } - - const fileHash = getFileHash(fullPath); - return { - file: `${upath.relative(globDirectory, fullPath)}`, - hash: fileHash, - size: fileSize, - }; - }); - - // If !== null, means it's a valid file. - const globbedFileDetails = fileDetails.filter((details) => details !== null); - - return {globbedFileDetails, warning}; -}; diff --git a/packages/workbox-build/src/lib/get-file-details.ts b/packages/workbox-build/src/lib/get-file-details.ts new file mode 100644 index 000000000..40b7c0434 --- /dev/null +++ b/packages/workbox-build/src/lib/get-file-details.ts @@ -0,0 +1,72 @@ +/* + Copyright 2021 Google LLC + + Use of this source code is governed by an MIT-style + license that can be found in the LICENSE file or at + https://opensource.org/licenses/MIT. +*/ + +import glob from 'glob'; +import upath from 'upath'; + +import {errors} from './errors'; +import {getFileSize} from './get-file-size'; +import {getFileHash} from './get-file-hash'; + +import {GlobPartial} from '../types'; + +interface FileDetails { + file: string; + hash: string; + size: number; +} + +export function getFileDetails({ + globDirectory, + globFollow, + globIgnores, + globPattern, + globStrict, +}: Omit & { + // This will only be called when globDirectory is not undefined. + globDirectory: string; + globPattern: string; +}): { + globbedFileDetails: Array; + warning: string; +} { + let globbedFiles: Array; + let warning = ''; + + try { + globbedFiles = glob.sync(globPattern, { + cwd: globDirectory, + follow: globFollow, + ignore: globIgnores, + strict: globStrict, + }); + } catch (err) { + throw new Error(errors['unable-to-glob-files'] + ` '${err.message}'`); + } + + if (globbedFiles.length === 0) { + warning = errors['useless-glob-pattern'] + ' ' + + JSON.stringify({globDirectory, globPattern, globIgnores}, null, 2); + } + + const globbedFileDetails: Array = []; + for (const file of globbedFiles) { + const fullPath = upath.join(globDirectory, file); + const fileSize = getFileSize(fullPath); + if (fileSize !== null) { + const fileHash = getFileHash(fullPath); + globbedFileDetails.push({ + file: `${upath.relative(globDirectory, fullPath)}`, + hash: fileHash, + size: fileSize, + }); + } + } + + return {globbedFileDetails, warning}; +} diff --git a/packages/workbox-build/src/lib/get-file-hash.js b/packages/workbox-build/src/lib/get-file-hash.ts similarity index 61% rename from packages/workbox-build/src/lib/get-file-hash.js rename to packages/workbox-build/src/lib/get-file-hash.ts index 2c3c0cc29..550ae4435 100644 --- a/packages/workbox-build/src/lib/get-file-hash.js +++ b/packages/workbox-build/src/lib/get-file-hash.ts @@ -6,16 +6,16 @@ https://opensource.org/licenses/MIT. */ -const fs = require('fs'); +import fse from 'fs-extra'; -const getStringHash = require('./get-string-hash'); -const errors = require('./errors'); +import {getStringHash} from './get-string-hash'; +import {errors} from './errors'; -module.exports = (file) => { +export function getFileHash(file: string): string { try { - const buffer = fs.readFileSync(file); + const buffer = fse.readFileSync(file); return getStringHash(buffer); } catch (err) { throw new Error(errors['unable-to-get-file-hash'] + ` '${err.message}'`); } -}; +} diff --git a/packages/workbox-build/src/lib/get-file-manifest-entries.js b/packages/workbox-build/src/lib/get-file-manifest-entries.js deleted file mode 100644 index 681d7f01f..000000000 --- a/packages/workbox-build/src/lib/get-file-manifest-entries.js +++ /dev/null @@ -1,119 +0,0 @@ -/* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/ - -const assert = require('assert'); - -const errors = require('./errors'); -const transformManifest = require('./transform-manifest'); -const getCompositeDetails = require('./get-composite-details'); -const getFileDetails = require('./get-file-details'); -const getStringDetails = require('./get-string-details'); - -module.exports = async ({ - additionalManifestEntries, - dontCacheBustURLsMatching, - globDirectory, - globFollow, - globIgnores, - globPatterns, - globStrict, - manifestTransforms, - maximumFileSizeToCacheInBytes, - modifyURLPrefix, - swDest, - templatedURLs, -}) => { - const warnings = []; - // Initialize to an empty array so that we can still pass something to - // transformManifest() and get a normalized output. - let fileDetails = []; - const fileSet = new Set(); - - if (globDirectory) { - try { - fileDetails = globPatterns.reduce((accumulated, globPattern) => { - const {globbedFileDetails, warning} = getFileDetails({ - globDirectory, - globFollow, - globIgnores, - globPattern, - globStrict, - }); - - if (warning) { - warnings.push(warning); - } - - globbedFileDetails.forEach((fileDetails) => { - if (fileSet.has(fileDetails.file)) { - return; - } - - fileSet.add(fileDetails.file); - accumulated.push(fileDetails); - }); - return accumulated; - }, []); - } catch (error) { - // If there's an exception thrown while globbing, then report - // it back as a warning, and don't consider it fatal. - warnings.push(error.message); - } - } - - if (templatedURLs) { - for (const url of Object.keys(templatedURLs)) { - assert(!fileSet.has(url), errors['templated-url-matches-glob']); - - const dependencies = templatedURLs[url]; - if (Array.isArray(dependencies)) { - const details = dependencies.reduce((previous, globPattern) => { - try { - const {globbedFileDetails, warning} = getFileDetails({ - globDirectory, - globFollow, - globIgnores, - globPattern, - globStrict, - }); - - if (warning) { - warnings.push(warning); - } - - return previous.concat(globbedFileDetails); - } catch (error) { - const debugObj = {}; - debugObj[url] = dependencies; - throw new Error(`${errors['bad-template-urls-asset']} ` + - `'${globPattern}' from '${JSON.stringify(debugObj)}':\n` + - error); - } - }, []); - fileDetails.push(getCompositeDetails(url, details)); - } else if (typeof dependencies === 'string') { - fileDetails.push(getStringDetails(url, dependencies)); - } - } - } - - const transformedManifest = await transformManifest({ - additionalManifestEntries, - dontCacheBustURLsMatching, - fileDetails, - manifestTransforms, - maximumFileSizeToCacheInBytes, - modifyURLPrefix, - }); - - if (warnings.length > 0) { - transformedManifest.warnings.push(...warnings); - } - - return transformedManifest; -}; diff --git a/packages/workbox-build/src/lib/get-file-manifest-entries.ts b/packages/workbox-build/src/lib/get-file-manifest-entries.ts new file mode 100644 index 000000000..521345a66 --- /dev/null +++ b/packages/workbox-build/src/lib/get-file-manifest-entries.ts @@ -0,0 +1,112 @@ +/* + Copyright 2021 Google LLC + + Use of this source code is governed by an MIT-style + license that can be found in the LICENSE file or at + https://opensource.org/licenses/MIT. +*/ + +import assert from 'assert'; + +import {GetManifestResult, FileDetails, GetManifestOptions} from '../types'; +import {errors} from './errors'; +import {getCompositeDetails} from './get-composite-details'; +import {getFileDetails} from './get-file-details'; +import {getStringDetails} from './get-string-details'; +import {transformManifest} from './transform-manifest'; + +export async function getFileManifestEntries({ + additionalManifestEntries, + dontCacheBustURLsMatching, + globDirectory, + globFollow, + globIgnores, + globPatterns = [], + globStrict, + manifestTransforms, + maximumFileSizeToCacheInBytes, + modifyURLPrefix, + templatedURLs, +}: GetManifestOptions): Promise { + const warnings: Array = []; + const allFileDetails = new Map(); + + try { + for (const globPattern of globPatterns) { + const {globbedFileDetails, warning} = getFileDetails({ + globDirectory, + globFollow, + globIgnores, + globPattern, + globStrict, + }); + + if (warning) { + warnings.push(warning); + } + + for (const details of globbedFileDetails) { + if (details && !allFileDetails.has(details.file)) { + allFileDetails.set(details.file, details); + } + } + } + } catch (error) { + // If there's an exception thrown while globbing, then report + // it back as a warning, and don't consider it fatal. + warnings.push(error.message); + } + + if (templatedURLs) { + for (const url of Object.keys(templatedURLs)) { + assert(!allFileDetails.has(url), errors['templated-url-matches-glob']); + + const dependencies = templatedURLs[url]; + if (Array.isArray(dependencies)) { + const details = dependencies.reduce>((previous, globPattern) => { + try { + const {globbedFileDetails, warning} = getFileDetails({ + globDirectory, + globFollow, + globIgnores, + globPattern, + globStrict, + }); + + if (warning) { + warnings.push(warning); + } + + return previous.concat(globbedFileDetails); + } catch (error) { + const debugObj: {[key: string]: Array} = {}; + debugObj[url] = dependencies; + throw new Error(`${errors['bad-template-urls-asset']} ` + + `'${globPattern}' from '${JSON.stringify(debugObj)}':\n` + + error); + } + }, []); + if (details.length === 0) { + throw new Error(`${errors['bad-template-urls-asset']} The glob ` + + `pattern '${dependencies}' did not match anything.`); + } + allFileDetails.set(url, getCompositeDetails(url, details)); + } else if (typeof dependencies === 'string') { + allFileDetails.set(url, getStringDetails(url, dependencies)); + } + } + } + + const transformedManifest = await transformManifest({ + additionalManifestEntries, + dontCacheBustURLsMatching, + manifestTransforms, + maximumFileSizeToCacheInBytes, + modifyURLPrefix, + fileDetails: Array.from(allFileDetails.values()), + }); + + transformedManifest.warnings.push(...warnings); + + return transformedManifest; +} diff --git a/packages/workbox-build/src/lib/get-file-size.js b/packages/workbox-build/src/lib/get-file-size.ts similarity index 69% rename from packages/workbox-build/src/lib/get-file-size.js rename to packages/workbox-build/src/lib/get-file-size.ts index 5ae5f2f50..6379e2681 100644 --- a/packages/workbox-build/src/lib/get-file-size.js +++ b/packages/workbox-build/src/lib/get-file-size.ts @@ -6,13 +6,13 @@ https://opensource.org/licenses/MIT. */ -const fs = require('fs'); +import fse from 'fs-extra'; -const errors = require('./errors'); +import {errors} from './errors'; -module.exports = (file) => { +export function getFileSize(file: string): number | null { try { - const stat = fs.statSync(file); + const stat = fse.statSync(file); if (!stat.isFile()) { return null; } @@ -20,4 +20,4 @@ module.exports = (file) => { } catch (err) { throw new Error(errors['unable-to-get-file-size'] + ` '${err.message}'`); } -}; +} diff --git a/packages/workbox-build/src/lib/get-string-details.js b/packages/workbox-build/src/lib/get-string-details.ts similarity index 50% rename from packages/workbox-build/src/lib/get-string-details.js rename to packages/workbox-build/src/lib/get-string-details.ts index 20de53d17..141863e84 100644 --- a/packages/workbox-build/src/lib/get-string-details.js +++ b/packages/workbox-build/src/lib/get-string-details.ts @@ -6,12 +6,13 @@ https://opensource.org/licenses/MIT. */ -const getStringHash = require('./get-string-hash'); +import {FileDetails} from '../types'; +import {getStringHash} from './get-string-hash'; -module.exports = (url, string) => { +export function getStringDetails(url: string, str: string): FileDetails { return { file: url, - hash: getStringHash(string), - size: string.length, + hash: getStringHash(str), + size: str.length, }; -}; +} diff --git a/packages/workbox-build/src/lib/get-string-hash.js b/packages/workbox-build/src/lib/get-string-hash.ts similarity index 68% rename from packages/workbox-build/src/lib/get-string-hash.js rename to packages/workbox-build/src/lib/get-string-hash.ts index d606f78ee..5ce1a5c1c 100644 --- a/packages/workbox-build/src/lib/get-string-hash.js +++ b/packages/workbox-build/src/lib/get-string-hash.ts @@ -6,10 +6,10 @@ https://opensource.org/licenses/MIT. */ -const crypto = require('crypto'); +import crypto from 'crypto'; -module.exports = (string) => { +export function getStringHash(input: crypto.BinaryLike): string { const md5 = crypto.createHash('md5'); - md5.update(string); + md5.update(input); return md5.digest('hex'); -}; +} diff --git a/packages/workbox-build/src/lib/maximum-size-transform.js b/packages/workbox-build/src/lib/maximum-size-transform.ts similarity index 73% rename from packages/workbox-build/src/lib/maximum-size-transform.js rename to packages/workbox-build/src/lib/maximum-size-transform.ts index 2fc1a9a87..fb56d9047 100644 --- a/packages/workbox-build/src/lib/maximum-size-transform.js +++ b/packages/workbox-build/src/lib/maximum-size-transform.ts @@ -6,11 +6,13 @@ https://opensource.org/licenses/MIT. */ -const prettyBytes = require('pretty-bytes'); +import prettyBytes from 'pretty-bytes'; -module.exports = (maximumFileSizeToCacheInBytes) => { +import {ManifestTransform} from '../types'; + +export function maximumSizeTransform(maximumFileSizeToCacheInBytes: number): ManifestTransform { return (originalManifest) => { - const warnings = []; + const warnings: Array = []; const manifest = originalManifest.filter((entry) => { if (entry.size <= maximumFileSizeToCacheInBytes) { return true; @@ -24,4 +26,4 @@ module.exports = (maximumFileSizeToCacheInBytes) => { return {manifest, warnings}; }; -}; +} diff --git a/packages/workbox-build/src/lib/modify-url-prefix-transform.js b/packages/workbox-build/src/lib/modify-url-prefix-transform.ts similarity index 80% rename from packages/workbox-build/src/lib/modify-url-prefix-transform.js rename to packages/workbox-build/src/lib/modify-url-prefix-transform.ts index 08148d64b..ea82dbb67 100644 --- a/packages/workbox-build/src/lib/modify-url-prefix-transform.js +++ b/packages/workbox-build/src/lib/modify-url-prefix-transform.ts @@ -6,10 +6,11 @@ https://opensource.org/licenses/MIT. */ -const errors = require('./errors'); -const escapeRegExp = require('./escape-regexp'); +import {errors} from './errors'; +import {escapeRegExp} from './escape-regexp'; +import {ManifestTransform} from '../types'; -module.exports = (modifyURLPrefix) => { +export function modifyURLPrefixTransform(modifyURLPrefix: {[key: string]: string}): ManifestTransform { if (!modifyURLPrefix || typeof modifyURLPrefix !== 'object' || Array.isArray(modifyURLPrefix)) { @@ -19,14 +20,16 @@ module.exports = (modifyURLPrefix) => { // If there are no entries in modifyURLPrefix, just return an identity // function as a shortcut. if (Object.keys(modifyURLPrefix).length === 0) { - return (entry) => entry; + return (manifest) => { + return {manifest}; + }; } - Object.keys(modifyURLPrefix).forEach((key) => { + for (const key of Object.keys(modifyURLPrefix)) { if (typeof modifyURLPrefix[key] !== 'string') { throw new Error(errors['modify-url-prefix-bad-prefixes']); } - }); + } // Escape the user input so it's safe to use in a regex. const safeModifyURLPrefixes = Object.keys(modifyURLPrefix).map(escapeRegExp); @@ -51,4 +54,4 @@ module.exports = (modifyURLPrefix) => { return {manifest}; }; -}; +} diff --git a/packages/workbox-build/src/lib/module-registry.js b/packages/workbox-build/src/lib/module-registry.ts similarity index 76% rename from packages/workbox-build/src/lib/module-registry.js rename to packages/workbox-build/src/lib/module-registry.ts index f38ad169d..b3dbb1174 100644 --- a/packages/workbox-build/src/lib/module-registry.js +++ b/packages/workbox-build/src/lib/module-registry.ts @@ -6,8 +6,8 @@ https://opensource.org/licenses/MIT. */ -const ol = require('common-tags').oneLine; -const upath = require('upath'); +import {oneLine as ol} from 'common-tags'; +import upath from 'upath'; /** * Class for keeping track of which Workbox modules are used by the generated @@ -15,12 +15,13 @@ const upath = require('upath'); * * @private */ -class ModuleRegistry { +export class ModuleRegistry { + private readonly _modulesUsed: Map; /** * @private */ constructor() { - this.modulesUsed = new Map(); + this._modulesUsed = new Map(); } /** @@ -28,10 +29,10 @@ class ModuleRegistry { * needed for the modules being used. * @private */ - getImportStatements() { - const workboxModuleImports = []; + getImportStatements(): Array { + const workboxModuleImports: Array = []; - for (const [localName, {moduleName, pkg}] of this.modulesUsed) { + for (const [localName, {moduleName, pkg}] of this._modulesUsed) { // By default require.resolve returns the resolved path of the 'main' // field, which might be deeper than the package root. To work around // this, we can find the package's root by resolving its package.json and @@ -53,7 +54,7 @@ class ModuleRegistry { * @return {string} The local variable name that corresponds to that module. * @private */ - getLocalName(pkg, moduleName) { + getLocalName(pkg: string, moduleName: string): string { return `${pkg.replace(/-/g, '_')}_${moduleName}`; } @@ -63,12 +64,10 @@ class ModuleRegistry { * @return {string} The local variable name that corresponds to that module. * @private */ - use(pkg, moduleName) { + use(pkg: string, moduleName: string): string { const localName = this.getLocalName(pkg, moduleName); - this.modulesUsed.set(localName, {moduleName, pkg}); + this._modulesUsed.set(localName, {moduleName, pkg}); return localName; } } - -module.exports = ModuleRegistry; diff --git a/packages/workbox-build/src/lib/no-revision-for-urls-matching-transform.js b/packages/workbox-build/src/lib/no-revision-for-urls-matching-transform.ts similarity index 78% rename from packages/workbox-build/src/lib/no-revision-for-urls-matching-transform.js rename to packages/workbox-build/src/lib/no-revision-for-urls-matching-transform.ts index 0bc21b6d1..71b4a872d 100644 --- a/packages/workbox-build/src/lib/no-revision-for-urls-matching-transform.js +++ b/packages/workbox-build/src/lib/no-revision-for-urls-matching-transform.ts @@ -6,9 +6,10 @@ https://opensource.org/licenses/MIT. */ -const errors = require('./errors'); +import {errors} from './errors'; +import {ManifestTransform} from '../types'; -module.exports = (regexp) => { +export function noRevisionForURLsMatchingTransform(regexp: RegExp): ManifestTransform { if (!(regexp instanceof RegExp)) { throw new Error(errors['invalid-dont-cache-bust']); } @@ -28,4 +29,4 @@ module.exports = (regexp) => { return {manifest}; }; -}; +} diff --git a/packages/workbox-build/src/lib/populate-sw-template.js b/packages/workbox-build/src/lib/populate-sw-template.ts similarity index 83% rename from packages/workbox-build/src/lib/populate-sw-template.js rename to packages/workbox-build/src/lib/populate-sw-template.ts index 432834a8f..cb84f0d17 100644 --- a/packages/workbox-build/src/lib/populate-sw-template.js +++ b/packages/workbox-build/src/lib/populate-sw-template.ts @@ -6,15 +6,16 @@ https://opensource.org/licenses/MIT. */ -const template = require('lodash/template'); -const swTemplate = require('../templates/sw-template'); +import template from 'lodash/template'; -const errors = require('./errors'); -const ModuleRegistry = require('./module-registry'); -const runtimeCachingConverter = require('./runtime-caching-converter'); -const stringifyWithoutComments = require('./stringify-without-comments'); +import {errors} from './errors'; +import {GeneratePartial, ManifestEntry} from '../types'; +import {ModuleRegistry} from './module-registry'; +import {runtimeCachingConverter} from './runtime-caching-converter'; +import {stringifyWithoutComments} from './stringify-without-comments'; +import {swTemplate} from '../templates/sw-template'; -module.exports = ({ +export function populateSWTemplate({ cacheId, cleanupOutdatedCaches, clientsClaim, @@ -30,7 +31,7 @@ module.exports = ({ offlineGoogleAnalytics, runtimeCaching = [], skipWaiting, -}) => { +}: GeneratePartial & {manifestEntries: Array}): string { // There needs to be at least something to precache, or else runtime caching. if (!(manifestEntries.length > 0 || runtimeCaching.length > 0)) { throw new Error(errors['no-manifest-entries-or-runtime-caching']); @@ -42,7 +43,7 @@ module.exports = ({ // An array of RegExp objects can't be serialized by JSON.stringify()'s // default behavior, so if it's given, convert it manually. ignoreURLParametersMatching: ignoreURLParametersMatching ? - [] : + [] as Array : undefined, }; @@ -55,7 +56,7 @@ module.exports = ({ ); } - let offlineAnalyticsConfigString; + let offlineAnalyticsConfigString: string | undefined = undefined; if (offlineGoogleAnalytics) { // If offlineGoogleAnalytics is a truthy value, we need to convert it to the // format expected by the template. @@ -97,4 +98,4 @@ module.exports = ({ throw new Error( `${errors['populating-sw-tmpl-failed']} '${error.message}'`); } -}; +} diff --git a/packages/workbox-build/src/lib/rebase-path.js b/packages/workbox-build/src/lib/rebase-path.ts similarity index 81% rename from packages/workbox-build/src/lib/rebase-path.js rename to packages/workbox-build/src/lib/rebase-path.ts index 5c9215f2a..3755b3094 100644 --- a/packages/workbox-build/src/lib/rebase-path.js +++ b/packages/workbox-build/src/lib/rebase-path.ts @@ -6,9 +6,12 @@ https://opensource.org/licenses/MIT. */ -const upath = require('upath'); +import upath from 'upath'; -module.exports = ({baseDirectory, file}) => { +export function rebasePath({ + baseDirectory, + file +}: {baseDirectory: string; file: string}): string { // The initial path is relative to the current directory, so make it absolute. const absolutePath = upath.resolve(file); @@ -19,4 +22,4 @@ module.exports = ({baseDirectory, file}) => { const normalizedPath = upath.normalize(relativePath); return normalizedPath; -}; +} diff --git a/packages/workbox-build/src/lib/replace-and-update-source-map.js b/packages/workbox-build/src/lib/replace-and-update-source-map.ts similarity index 83% rename from packages/workbox-build/src/lib/replace-and-update-source-map.js rename to packages/workbox-build/src/lib/replace-and-update-source-map.ts index 4fd853b77..244721371 100644 --- a/packages/workbox-build/src/lib/replace-and-update-source-map.js +++ b/packages/workbox-build/src/lib/replace-and-update-source-map.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -const {SourceMapConsumer, SourceMapGenerator} = require('source-map'); +import {RawSourceMap, SourceMapConsumer, SourceMapGenerator} from 'source-map'; /** * Adapted from https://github.com/nsams/sourcemap-aware-replace, with modern @@ -27,22 +27,28 @@ const {SourceMapConsumer, SourceMapGenerator} = require('source-map'); * * @private */ -async function replaceAndUpdateSourceMap({ +export async function replaceAndUpdateSourceMap({ jsFilename, originalMap, originalSource, replaceString, searchString, -}) { +}: { + jsFilename: string; + originalMap: RawSourceMap; + originalSource: string; + replaceString: string; + searchString: string; +}): Promise<{map: string; source: string}> { const generator = new SourceMapGenerator({ file: jsFilename, }); const consumer = await new SourceMapConsumer(originalMap); - let pos; + let pos: number; let src = originalSource; - const replacements = []; + const replacements: Array<{line: number; column: number}> = []; let lineNum = 0; let filePos = 0; @@ -65,7 +71,7 @@ async function replaceAndUpdateSourceMap({ consumer.eachMapping((mapping) => { for (const replacement of replacements) { - if (replacement.line == mapping.generatedLine && + if (replacement.line === mapping.generatedLine && mapping.generatedColumn > replacement.column) { const offset = searchString.length - replaceString.length; mapping.generatedColumn -= offset; @@ -92,7 +98,7 @@ async function replaceAndUpdateSourceMap({ consumer.destroy(); - const updatedSourceMap = Object.assign(JSON.parse(generator.toString()), { + const updatedSourceMap: RawSourceMap = Object.assign(JSON.parse(generator.toString()), { names: originalMap.names, sourceRoot: originalMap.sourceRoot, sources: originalMap.sources, @@ -104,5 +110,3 @@ async function replaceAndUpdateSourceMap({ source: src, }; } - -module.exports = replaceAndUpdateSourceMap; diff --git a/packages/workbox-build/src/lib/runtime-caching-converter.js b/packages/workbox-build/src/lib/runtime-caching-converter.ts similarity index 56% rename from packages/workbox-build/src/lib/runtime-caching-converter.js rename to packages/workbox-build/src/lib/runtime-caching-converter.ts index 97b46c450..29b6fdcc1 100644 --- a/packages/workbox-build/src/lib/runtime-caching-converter.js +++ b/packages/workbox-build/src/lib/runtime-caching-converter.ts @@ -6,10 +6,12 @@ https://opensource.org/licenses/MIT. */ -const ol = require('common-tags').oneLine; +import {oneLine as ol} from 'common-tags'; -const errors = require('./errors'); -const stringifyWithoutComments = require('./stringify-without-comments'); +import {errors} from './errors'; +import {ModuleRegistry} from './module-registry'; +import {RuntimeCaching} from '../types'; +import {stringifyWithoutComments} from './stringify-without-comments'; /** * Given a set of options that configures runtime caching behavior, convert it @@ -22,61 +24,56 @@ const stringifyWithoutComments = require('./stringify-without-comments'); * * @private */ -function getOptionsString(moduleRegistry, options = {}) { - let plugins = []; - if (options.plugins) { - // Using libs because JSON.stringify won't handle functions. - plugins = options.plugins.map(stringifyWithoutComments); - delete options.plugins; - } +function getOptionsString(moduleRegistry: ModuleRegistry, options: RuntimeCaching['options'] = {}) { + const plugins: Array = []; + const handlerOptions: {[key in keyof typeof options]: any} = {}; - // Pull handler-specific config from the options object, since they are - // not directly used to construct a plugin instance. If set, need to be - // passed as options to the handler constructor instead. - const handlerOptionKeys = [ - 'cacheName', - 'networkTimeoutSeconds', - 'fetchOptions', - 'matchOptions', - ]; - const handlerOptions = {}; - for (const key of handlerOptionKeys) { - if (key in options) { - handlerOptions[key] = options[key]; - delete options[key]; - } - } - - for (const [pluginName, pluginConfig] of Object.entries(options)) { - // Ensure that we have some valid configuration to pass to the plugin. - if (Object.keys(pluginConfig).length === 0) { + for (const optionName of Object.keys(options) as Array) { + if (options[optionName] === undefined) { continue; } - let pluginCode; - switch (pluginName) { + switch (optionName) { + // Using a library here because JSON.stringify won't handle functions. + case 'plugins': { + plugins.push(...options.plugins!.map(stringifyWithoutComments)); + break; + } + + // These are the option properties that we want to pull out, so that + // they're passed to the handler constructor. + case 'cacheName': + case 'networkTimeoutSeconds': + case 'fetchOptions': + case 'matchOptions': { + handlerOptions[optionName] = options[optionName]; + break; + } + + // The following cases are all shorthands for creating a plugin with a + // given configuration. case 'backgroundSync': { - const name = pluginConfig.name; + const name = options.backgroundSync!.name; const plugin = moduleRegistry.use( 'workbox-background-sync', 'BackgroundSyncPlugin'); - pluginCode = `new ${plugin}(${JSON.stringify(name)}`; - if ('options' in pluginConfig) { - pluginCode += `, ${stringifyWithoutComments(pluginConfig.options)}`; + let pluginCode = `new ${plugin}(${JSON.stringify(name)}`; + if (options.backgroundSync!.options) { + pluginCode += `, ${stringifyWithoutComments(options.backgroundSync!.options)}`; } pluginCode += `)`; + plugins.push(pluginCode); break; } case 'broadcastUpdate': { - const channelName = pluginConfig.channelName; - const opts = Object.assign({channelName}, pluginConfig.options); + const channelName = options.broadcastUpdate!.channelName; + const opts = Object.assign({channelName}, options.broadcastUpdate!.options); const plugin = moduleRegistry.use( 'workbox-broadcast-update', 'BroadcastUpdatePlugin'); - pluginCode = `new ${plugin}(${stringifyWithoutComments(opts)})`; - + plugins.push(`new ${plugin}(${stringifyWithoutComments(opts)})`); break; } @@ -84,8 +81,7 @@ function getOptionsString(moduleRegistry, options = {}) { const plugin = moduleRegistry.use( 'workbox-cacheable-response', 'CacheableResponsePlugin'); - pluginCode = `new ${plugin}(${stringifyWithoutComments(pluginConfig)})`; - + plugins.push(`new ${plugin}(${stringifyWithoutComments(options.cacheableResponse!)})`); break; } @@ -93,8 +89,7 @@ function getOptionsString(moduleRegistry, options = {}) { const plugin = moduleRegistry.use( 'workbox-expiration', 'ExpirationPlugin'); - pluginCode = `new ${plugin}(${stringifyWithoutComments(pluginConfig)})`; - + plugins.push(`new ${plugin}(${stringifyWithoutComments(options.expiration!)})`); break; } @@ -102,17 +97,14 @@ function getOptionsString(moduleRegistry, options = {}) { const plugin = moduleRegistry.use( 'workbox-precaching', 'PrecacheFallbackPlugin'); - pluginCode = `new ${plugin}(${stringifyWithoutComments(pluginConfig)})`; - + plugins.push(`new ${plugin}(${stringifyWithoutComments(options.precacheFallback!)})`); break; } default: { - throw new Error(errors['bad-runtime-caching-config'] + pluginName); + throw new Error(errors['bad-runtime-caching-config'] + optionName); } } - - plugins.push(pluginCode); } if (Object.keys(handlerOptions).length > 0 || plugins.length > 0) { @@ -126,7 +118,7 @@ function getOptionsString(moduleRegistry, options = {}) { } } -module.exports = (moduleRegistry, runtimeCaching) => { +export function runtimeCachingConverter(moduleRegistry: ModuleRegistry, runtimeCaching: Array): Array { return runtimeCaching.map((entry) => { const method = entry.method || 'GET'; @@ -138,8 +130,6 @@ module.exports = (moduleRegistry, runtimeCaching) => { throw new Error(errors['handler-is-required']); } - // This validation logic is a bit too gnarly for joi, so it's manually - // implemented here. if (entry.options && entry.options.networkTimeoutSeconds && entry.handler !== 'NetworkFirst') { throw new Error(errors['invalid-network-timeout-seconds']); @@ -162,5 +152,8 @@ module.exports = (moduleRegistry, runtimeCaching) => { } else if (typeof entry.handler === 'function') { return `${registerRoute}(${matcher}, ${entry.handler}, '${method}');\n`; } - }).filter((entry) => Boolean(entry)); // Remove undefined map() return values. -}; + + // '' will be filtered out. + return ''; + }).filter((entry) => Boolean(entry)); +} diff --git a/packages/workbox-build/src/lib/stringify-without-comments.js b/packages/workbox-build/src/lib/stringify-without-comments.js deleted file mode 100644 index 05b11041b..000000000 --- a/packages/workbox-build/src/lib/stringify-without-comments.js +++ /dev/null @@ -1,17 +0,0 @@ -/* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/ - -const objectStringify = require('stringify-object'); -const stripComments = require('strip-comments'); - -module.exports = (obj) => { - return objectStringify(obj, { - transform: (_obj, _prop, str) => - typeof _obj[_prop] === 'function' ? stripComments(str) : str, - }); -}; diff --git a/packages/workbox-build/src/lib/stringify-without-comments.ts b/packages/workbox-build/src/lib/stringify-without-comments.ts new file mode 100644 index 000000000..b1b8d03ae --- /dev/null +++ b/packages/workbox-build/src/lib/stringify-without-comments.ts @@ -0,0 +1,22 @@ +/* + Copyright 2021 Google LLC + + Use of this source code is governed by an MIT-style + license that can be found in the LICENSE file or at + https://opensource.org/licenses/MIT. +*/ + +import objectStringify from 'stringify-object'; +import stripComments from 'strip-comments'; + +export function stringifyWithoutComments(obj: {[key: string]: any}): string { + return objectStringify(obj, { + // See https://github.com/yeoman/stringify-object#transformobject-property-originalresult + transform: (_obj: {[key: string]: any}, _prop, str) => { + if (typeof _prop !== 'symbol' && typeof _obj[_prop] === 'function') { + return stripComments(str); + } + return str; + }, + }); +} diff --git a/packages/workbox-build/src/lib/transform-manifest.js b/packages/workbox-build/src/lib/transform-manifest.ts similarity index 78% rename from packages/workbox-build/src/lib/transform-manifest.js rename to packages/workbox-build/src/lib/transform-manifest.ts index e8aade57d..5800717d2 100644 --- a/packages/workbox-build/src/lib/transform-manifest.js +++ b/packages/workbox-build/src/lib/transform-manifest.ts @@ -6,13 +6,12 @@ https://opensource.org/licenses/MIT. */ -const errors = require('./errors'); -const additionalManifestEntriesTransform = - require('./additional-manifest-entries-transform'); -const maximumSizeTransform = require('./maximum-size-transform'); -const modifyURLPrefixTransform = require('./modify-url-prefix-transform'); -const noRevisionForURLsMatchingTransform = - require('./no-revision-for-urls-matching-transform'); +import {BasePartial, FileDetails, ManifestEntry, ManifestTransform} from '../types'; +import {additionalManifestEntriesTransform} from './additional-manifest-entries-transform'; +import {errors} from './errors'; +import {maximumSizeTransform} from './maximum-size-transform'; +import {modifyURLPrefixTransform} from './modify-url-prefix-transform'; +import {noRevisionForURLsMatchingTransform} from './no-revision-for-urls-matching-transform'; /** * A `ManifestTransform` function can be used to modify the modify the `url` or @@ -66,7 +65,7 @@ const noRevisionForURLsMatchingTransform = * @memberof module:workbox-build */ -module.exports = async ({ +export async function transformManifest({ additionalManifestEntries, dontCacheBustURLsMatching, fileDetails, @@ -74,8 +73,13 @@ module.exports = async ({ maximumFileSizeToCacheInBytes, modifyURLPrefix, transformParam, -}) => { - let allWarnings = []; +}: BasePartial & { + fileDetails: Array; + // When this is called by the webpack plugin, transformParam will be the + // current webpack compilation. + transformParam?: unknown; +}) { + const allWarnings: Array = []; // Take the array of fileDetail objects and convert it into an array of // {url, revision, size} objects, with \ replaced with /. @@ -87,7 +91,7 @@ module.exports = async ({ }; }); - const transformsToApply = []; + const transformsToApply: Array = []; if (maximumFileSizeToCacheInBytes) { transformsToApply.push(maximumSizeTransform(maximumFileSizeToCacheInBytes)); @@ -113,7 +117,7 @@ module.exports = async ({ additionalManifestEntriesTransform(additionalManifestEntries)); } - let transformedManifest = normalizedManifest; + let transformedManifest: Array = normalizedManifest; for (const transform of transformsToApply) { const result = await transform(transformedManifest, transformParam); if (!('manifest' in result)) { @@ -121,14 +125,14 @@ module.exports = async ({ } transformedManifest = result.manifest; - allWarnings = allWarnings.concat(result.warnings || []); + allWarnings.push(...(result.warnings || [])); } // Generate some metadata about the manifest before we clear out the size // properties from each entry. const count = transformedManifest.length; let size = 0; - for (const manifestEntry of transformedManifest) { + for (const manifestEntry of transformedManifest as Array) { size += manifestEntry.size || 0; delete manifestEntry.size; } @@ -136,7 +140,7 @@ module.exports = async ({ return { count, size, - manifestEntries: transformedManifest, + manifestEntries: transformedManifest as Array, warnings: allWarnings, }; -}; +} diff --git a/packages/workbox-build/src/lib/validate-options.js b/packages/workbox-build/src/lib/validate-options.js deleted file mode 100644 index a0c388d13..000000000 --- a/packages/workbox-build/src/lib/validate-options.js +++ /dev/null @@ -1,23 +0,0 @@ -/* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/ - -module.exports = (options, schema) => { - const {value, error} = schema.validate(options, { - language: { - object: { - allowUnknown: 'is not a supported parameter.', - }, - }, - }); - - if (error) { - throw error; - } - - return value; -}; diff --git a/packages/workbox-build/src/lib/validate-options.ts b/packages/workbox-build/src/lib/validate-options.ts new file mode 100644 index 000000000..13407b1a8 --- /dev/null +++ b/packages/workbox-build/src/lib/validate-options.ts @@ -0,0 +1,128 @@ +/* + Copyright 2021 Google LLC + + Use of this source code is governed by an MIT-style + license that can be found in the LICENSE file or at + https://opensource.org/licenses/MIT. +*/ + +import {betterAjvErrors} from '@apideck/better-ajv-errors'; +import {oneLine as ol} from 'common-tags'; +import Ajv, {JSONSchemaType} from 'ajv'; +import ajvKeywords from 'ajv-keywords'; + +import {errors} from './errors'; + +import { + GenerateSWOptions, + GetManifestOptions, + InjectManifestOptions, + WebpackGenerateSWOptions, + WebpackInjectManifestOptions, +} from '../types'; + +type MethodNames = 'GenerateSW' | 'GetManifest' | 'InjectManifest' | + 'WebpackGenerateSW' | 'WebpackInjectManifest'; + +const ajv = new Ajv({ + useDefaults: true, +}); +ajvKeywords(ajv, ['typeof']); + +const DEFAULT_EXCLUDE_VALUE= [/\.map$/, /^manifest.*\.js$/]; + +export class WorkboxConfigError extends Error { + constructor(message?: string) { + super(message); + Object.setPrototypeOf(this, new.target.prototype); + } +} + +function validate(input: unknown, methodName: MethodNames): T { + // Don't mutate input: https://github.com/GoogleChrome/workbox/issues/2158 + const inputCopy = Object.assign({}, input); + const jsonSchema: JSONSchemaType = require(`../schema/${methodName}Options.json`); + const validate = ajv.compile(jsonSchema); + if (validate(inputCopy)) { + return inputCopy; + } + + const betterErrors = betterAjvErrors({ + basePath: methodName, + data: input, + errors: validate.errors, + // This is needed as JSONSchema6 is expected, but JSONSchemaType works. + schema: jsonSchema as any, + }); + const messages = betterErrors.map((err) => ol`[${err.path}] ${err.message}. + ${err.suggestion ? err.suggestion : ''}`); + + throw new WorkboxConfigError(messages.join('\n\n')); +} + +function ensureValidNavigationPreloadConfig( + options: GenerateSWOptions | WebpackGenerateSWOptions, +): void { + if (options.navigationPreload && + (!Array.isArray(options.runtimeCaching) || + options.runtimeCaching.length === 0)) { + throw new WorkboxConfigError(errors['nav-preload-runtime-caching']); + } +} + +function ensureValidCacheExpiration( + options: GenerateSWOptions | WebpackGenerateSWOptions, +): void { + for (const runtimeCaching of options.runtimeCaching || []) { + if (runtimeCaching.options?.expiration && !runtimeCaching.options?.cacheName) { + throw new WorkboxConfigError(errors['cache-name-required']); + } + } +} + +function ensureValidRuntimeCachingOrGlobDirectory( + options: GenerateSWOptions, +): void { + if (!options.globDirectory && + (!Array.isArray(options.runtimeCaching) || + options.runtimeCaching.length === 0)) { + throw new WorkboxConfigError(errors['no-manifest-entries-or-runtime-caching']); + } +} + +export function validateGenerateSWOptions(input: unknown): GenerateSWOptions { + const validatedOptions = validate(input, 'GenerateSW'); + ensureValidNavigationPreloadConfig(validatedOptions); + ensureValidCacheExpiration(validatedOptions); + ensureValidRuntimeCachingOrGlobDirectory(validatedOptions); + return validatedOptions; +} + +export function validateGetManifestOptions(input: unknown): GetManifestOptions { + return validate(input, 'GetManifest'); +} + +export function validateInjectManifestOptions(input: unknown): InjectManifestOptions { + return validate(input, 'InjectManifest'); +} + +// The default `exclude: [/\.map$/, /^manifest.*\.js$/]` value can't be +// represented in the JSON schema, so manually set it for the webpack options. +export function validateWebpackGenerateSWOptions(input: unknown): WebpackGenerateSWOptions { + const inputWithExcludeDefault = Object.assign({ + // Make a copy, as exclude can be mutated when used. + exclude: Array.from(DEFAULT_EXCLUDE_VALUE), + }, input); + const validatedOptions = validate(inputWithExcludeDefault, 'WebpackGenerateSW'); + ensureValidNavigationPreloadConfig(validatedOptions); + ensureValidCacheExpiration(validatedOptions); + return validatedOptions; +} + +export function validateWebpackInjectManifestOptions(input: unknown): WebpackInjectManifestOptions { + const inputWithExcludeDefault = Object.assign({ + // Make a copy, as exclude can be mutated when used. + exclude: Array.from(DEFAULT_EXCLUDE_VALUE), + }, input); + return validate(inputWithExcludeDefault, 'WebpackInjectManifest'); +} diff --git a/packages/workbox-build/src/lib/write-sw-using-default-template.js b/packages/workbox-build/src/lib/write-sw-using-default-template.ts similarity index 80% rename from packages/workbox-build/src/lib/write-sw-using-default-template.js rename to packages/workbox-build/src/lib/write-sw-using-default-template.ts index 31570b50f..ceed58436 100644 --- a/packages/workbox-build/src/lib/write-sw-using-default-template.js +++ b/packages/workbox-build/src/lib/write-sw-using-default-template.ts @@ -6,14 +6,15 @@ https://opensource.org/licenses/MIT. */ -const fse = require('fs-extra'); -const upath = require('upath'); +import fse from 'fs-extra'; +import upath from 'upath'; -const bundle = require('./bundle'); -const errors = require('./errors'); -const populateSWTemplate = require('./populate-sw-template'); +import {bundle} from './bundle'; +import {errors} from './errors'; +import {GenerateSWOptions, ManifestEntry} from '../types'; +import {populateSWTemplate} from './populate-sw-template'; -module.exports = async ({ +export async function writeSWUsingDefaultTemplate({ babelPresetEnvTargets, cacheId, cleanupOutdatedCaches, @@ -34,7 +35,7 @@ module.exports = async ({ skipWaiting, sourcemap, swDest, -}) => { +}: GenerateSWOptions & {manifestEntries: Array}): Promise> { const outputDir = upath.dirname(swDest); try { await fse.mkdirp(outputDir); @@ -71,7 +72,7 @@ module.exports = async ({ unbundledCode, }); - const filePaths = []; + const filePaths: Array = []; for (const file of files) { const filePath = upath.resolve(file.name); @@ -87,4 +88,4 @@ module.exports = async ({ } throw new Error(`${errors['sw-write-failure']} '${error.message}'`); } -}; +} diff --git a/packages/workbox-build/src/options/defaults.js b/packages/workbox-build/src/options/defaults.js deleted file mode 100644 index 4cdc4749d..000000000 --- a/packages/workbox-build/src/options/defaults.js +++ /dev/null @@ -1,34 +0,0 @@ -/* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/ - -module.exports = { - babelPresetEnvTargets: ['chrome >= 56'], - cleanupOutdatedCaches: false, - clientsClaim: false, - compileSrc: true, - disableDevLogs: false, - exclude: [ - /\.map$/, - /^manifest.*\.js$/, - ], - globFollow: true, - globIgnores: ['**/node_modules/**/*'], - globPatterns: ['**/*.{js,css,html}'], - globStrict: true, - injectionPoint: 'self.__WB_MANIFEST', - inlineWorkboxRuntime: false, - maximumFileSizeToCacheInBytes: 2 * 1024 * 1024, - mode: 'production', - navigateFallback: null, - navigationPreload: false, - offlineGoogleAnalytics: false, - purgeOnQuotaError: true, - skipWaiting: false, - sourcemap: true, - swDestFilename: 'service-worker.js', -}; diff --git a/packages/workbox-build/src/options/objects/manifest-entry.js b/packages/workbox-build/src/options/objects/manifest-entry.js deleted file mode 100644 index 1c1896331..000000000 --- a/packages/workbox-build/src/options/objects/manifest-entry.js +++ /dev/null @@ -1,15 +0,0 @@ -/* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/ - -const joi = require('@hapi/joi'); - -module.exports = joi.object().keys({ - integrity: joi.string(), - revision: joi.string().required().allow(null), - url: joi.string().required(), -}); diff --git a/packages/workbox-build/src/options/objects/reg-exp.js b/packages/workbox-build/src/options/objects/reg-exp.js deleted file mode 100644 index 643bfe0a1..000000000 --- a/packages/workbox-build/src/options/objects/reg-exp.js +++ /dev/null @@ -1,11 +0,0 @@ -/* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/ - -const joi = require('@hapi/joi'); - -module.exports = joi.object().instance(RegExp); diff --git a/packages/workbox-build/src/options/partials/base.js b/packages/workbox-build/src/options/partials/base.js deleted file mode 100644 index deb96377d..000000000 --- a/packages/workbox-build/src/options/partials/base.js +++ /dev/null @@ -1,23 +0,0 @@ -/* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/ - -const joi = require('@hapi/joi'); - -const defaults = require('../defaults'); -const manifestEntryObject = require('../objects/manifest-entry'); -const regExpObject = require('../objects/reg-exp'); - -module.exports = { - additionalManifestEntries: joi.array() - .items(joi.string(), manifestEntryObject), - dontCacheBustURLsMatching: regExpObject, - manifestTransforms: joi.array().items(joi.func().minArity(1).maxArity(2)), - maximumFileSizeToCacheInBytes: joi.number().min(1) - .default(defaults.maximumFileSizeToCacheInBytes), - modifyURLPrefix: joi.object(), -}; diff --git a/packages/workbox-build/src/options/partials/generate.js b/packages/workbox-build/src/options/partials/generate.js deleted file mode 100644 index fc2a18c9d..000000000 --- a/packages/workbox-build/src/options/partials/generate.js +++ /dev/null @@ -1,88 +0,0 @@ -/* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/ - -const joi = require('@hapi/joi'); - -const defaults = require('../defaults'); -const regExpObject = require('../objects/reg-exp'); - -module.exports = { - babelPresetEnvTargets: joi.array().items(joi.string()) - .default(defaults.babelPresetEnvTargets), - cacheId: joi.string(), - cleanupOutdatedCaches: joi.boolean().default(defaults.cleanupOutdatedCaches), - clientsClaim: joi.boolean().default(defaults.clientsClaim), - directoryIndex: joi.string(), - disableDevLogs: joi.boolean().default(defaults.disableDevLogs), - ignoreURLParametersMatching: joi.array().items(regExpObject), - importScripts: joi.array().items(joi.string()), - inlineWorkboxRuntime: joi.boolean().default(defaults.inlineWorkboxRuntime), - mode: joi.string().default(process.env.NODE_ENV || defaults.mode), - navigateFallback: joi.string().default(defaults.navigateFallback), - navigateFallbackAllowlist: joi.array().items(regExpObject), - navigateFallbackBlacklist: joi.forbidden().error(new Error( - 'navigateFallbackBlacklist has been renamed navigateFallbackDenylist.')), - navigateFallbackDenylist: joi.array().items(regExpObject), - navigateFallbackWhitelist: joi.forbidden().error(new Error( - 'navigateFallbackWhitelist has been renamed navigateFallbackAllowlist.')), - navigationPreload: joi.boolean().default(defaults.navigationPreload), - offlineGoogleAnalytics: joi.alternatives().try(joi.boolean(), joi.object()) - .default(defaults.offlineGoogleAnalytics), - runtimeCaching: joi.array().items(joi.object().keys({ - method: joi.string().valid( - 'DELETE', - 'GET', - 'HEAD', - 'PATCH', - 'POST', - 'PUT', - ), - urlPattern: [regExpObject, joi.string(), joi.func()], - handler: [ - joi.func(), - joi.string().valid( - 'CacheFirst', - 'CacheOnly', - 'NetworkFirst', - 'NetworkOnly', - 'StaleWhileRevalidate'), - ], - options: joi.object().keys({ - backgroundSync: joi.object().keys({ - name: joi.string().required(), - options: joi.object(), - }), - broadcastUpdate: joi.object().keys({ - channelName: joi.string().required(), - options: joi.object(), - }), - cacheableResponse: joi.object().keys({ - statuses: joi.array().items(joi.number().min(0).max(599)), - headers: joi.object(), - }).or('statuses', 'headers'), - cacheName: joi.string(), - expiration: joi.object().keys({ - maxEntries: joi.number().min(1), - maxAgeSeconds: joi.number().min(1), - purgeOnQuotaError: joi.boolean().default(defaults.purgeOnQuotaError), - }).or('maxEntries', 'maxAgeSeconds'), - networkTimeoutSeconds: joi.number().min(1), - plugins: joi.array().items(joi.object()), - precacheFallback: joi.object().keys({ - fallbackURL: joi.string().required(), - }), - fetchOptions: joi.object(), - matchOptions: joi.object(), - }).with('expiration', 'cacheName'), - })).when('navigationPreload', { - is: true, - then: joi.required('runtimeCaching'), - }), - skipWaiting: joi.boolean().default(defaults.skipWaiting), - sourcemap: joi.boolean().default(defaults.sourcemap), -}; diff --git a/packages/workbox-build/src/options/partials/glob.js b/packages/workbox-build/src/options/partials/glob.js deleted file mode 100644 index efe83850d..000000000 --- a/packages/workbox-build/src/options/partials/glob.js +++ /dev/null @@ -1,23 +0,0 @@ -/* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/ - -const joi = require('@hapi/joi'); - -const defaults = require('../defaults'); - -module.exports = { - globDirectory: joi.string(), - globFollow: joi.boolean().default(defaults.globFollow), - globIgnores: joi.array().items(joi.string()).default(defaults.globIgnores), - globPatterns: joi.array().items(joi.string()).default(defaults.globPatterns), - globStrict: joi.boolean().default(defaults.globStrict), - // templatedURLs is an object where any property name is valid, and the values - // can be either a string or an array of strings. - templatedURLs: joi.object() - .pattern(/./, [joi.string(), joi.array().items(joi.string())]), -}; diff --git a/packages/workbox-build/src/options/partials/inject.js b/packages/workbox-build/src/options/partials/inject.js deleted file mode 100644 index e91adf00e..000000000 --- a/packages/workbox-build/src/options/partials/inject.js +++ /dev/null @@ -1,16 +0,0 @@ -/* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/ - -const joi = require('@hapi/joi'); - -const defaults = require('../defaults'); - -module.exports = { - injectionPoint: joi.string().default(defaults.injectionPoint), - swSrc: joi.string().required(), -}; diff --git a/packages/workbox-build/src/options/partials/webpack.js b/packages/workbox-build/src/options/partials/webpack.js deleted file mode 100644 index e530addb9..000000000 --- a/packages/workbox-build/src/options/partials/webpack.js +++ /dev/null @@ -1,21 +0,0 @@ -/* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/ - -const joi = require('@hapi/joi'); - -const defaults = require('../defaults'); -const regExpObject = require('../objects/reg-exp'); - -module.exports = { - chunks: joi.array().items(joi.string()), - exclude: joi.array().items(joi.string(), regExpObject, joi.func().arity(1)) - .default(defaults.exclude), - excludeChunks: joi.array().items(joi.string()), - include: joi.array().items(joi.string(), regExpObject, joi.func().arity(1)), - mode: joi.string().default(process.env.NODE_ENV || defaults.mode), -}; diff --git a/packages/workbox-build/src/options/schema/generate-sw.js b/packages/workbox-build/src/options/schema/generate-sw.js deleted file mode 100644 index 5abbfe3a0..000000000 --- a/packages/workbox-build/src/options/schema/generate-sw.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/ - -const joi = require('@hapi/joi'); - -const basePartial = require('../partials/base'); -const generatePartial = require('../partials/generate'); -const globPartial = require('../partials/glob'); - -const supportedOptions = Object.assign({ - swDest: joi.string().required().regex(/\.js$/), -}, basePartial, generatePartial, globPartial); - -module.exports = joi.object().keys(supportedOptions); diff --git a/packages/workbox-build/src/options/schema/get-manifest.js b/packages/workbox-build/src/options/schema/get-manifest.js deleted file mode 100644 index c2b5368c1..000000000 --- a/packages/workbox-build/src/options/schema/get-manifest.js +++ /dev/null @@ -1,16 +0,0 @@ -/* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/ - -const joi = require('@hapi/joi'); - -const basePartial = require('../partials/base'); -const globPartial = require('../partials/glob'); - -const supportedOptions = Object.assign({}, basePartial, globPartial); - -module.exports = joi.object().keys(supportedOptions); diff --git a/packages/workbox-build/src/options/schema/inject-manifest.js b/packages/workbox-build/src/options/schema/inject-manifest.js deleted file mode 100644 index ce2708847..000000000 --- a/packages/workbox-build/src/options/schema/inject-manifest.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/ - -const joi = require('@hapi/joi'); - -const basePartial = require('../partials/base'); -const globPartial = require('../partials/glob'); -const injectPartial = require('../partials/inject'); - -const supportedOptions = Object.assign({ - swDest: joi.string().required().regex(/\.js$/), -}, basePartial, globPartial, injectPartial); - -module.exports = joi.object().keys(supportedOptions); diff --git a/packages/workbox-build/src/options/schema/webpack-generate-sw.js b/packages/workbox-build/src/options/schema/webpack-generate-sw.js deleted file mode 100644 index 21f0ab31a..000000000 --- a/packages/workbox-build/src/options/schema/webpack-generate-sw.js +++ /dev/null @@ -1,21 +0,0 @@ -/* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/ - -const joi = require('@hapi/joi'); - -const basePartial = require('../partials/base'); -const defaults = require('../defaults'); -const generatePartial = require('../partials/generate'); -const webpackPartial = require('../partials/webpack'); - -const supportedOptions = Object.assign({ - importScriptsViaChunks: joi.array().items(joi.string()), - swDest: joi.string().default(defaults.swDestFilename), -}, basePartial, generatePartial, webpackPartial); - -module.exports = joi.object().keys(supportedOptions); diff --git a/packages/workbox-build/src/options/schema/webpack-inject-manifest.js b/packages/workbox-build/src/options/schema/webpack-inject-manifest.js deleted file mode 100644 index 9f91171c4..000000000 --- a/packages/workbox-build/src/options/schema/webpack-inject-manifest.js +++ /dev/null @@ -1,34 +0,0 @@ -/* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/ - -const joi = require('@hapi/joi'); -const upath = require('upath'); - -const basePartial = require('../partials/base'); -const defaults = require('../defaults'); -const injectPartial = require('../partials/inject'); -const webpackPartial = require('../partials/webpack'); - -// See https://github.com/hapijs/joi/blob/v16.0.0-rc2/API.md#anydefaultvalue-description -const swSrcBasename = (context) => { - const {name} = upath.parse(context.swSrc); - // Always use the .js extension when generating a default filename. - return name + '.js'; -}; -swSrcBasename.description = 'derived from the swSrc file name'; - -const supportedOptions = Object.assign({ - compileSrc: joi.boolean().default(defaults.compileSrc), - webpackCompilationPlugins: joi.array().items(joi.object()).when( - 'compileSrc', {is: false, then: joi.forbidden()}), -}, basePartial, injectPartial, webpackPartial); - -module.exports = joi.object().keys(supportedOptions).keys({ - // List this separately, so that the swSrc validation happens first. - swDest: joi.string().default(swSrcBasename), -}); diff --git a/packages/workbox-build/src/rollup-plugin-off-main-thread.d.ts b/packages/workbox-build/src/rollup-plugin-off-main-thread.d.ts new file mode 100644 index 000000000..ced888a2c --- /dev/null +++ b/packages/workbox-build/src/rollup-plugin-off-main-thread.d.ts @@ -0,0 +1 @@ +declare module '@surma/rollup-plugin-off-main-thread'; diff --git a/packages/workbox-build/src/schema/GenerateSWOptions.json b/packages/workbox-build/src/schema/GenerateSWOptions.json new file mode 100644 index 000000000..d8378a4af --- /dev/null +++ b/packages/workbox-build/src/schema/GenerateSWOptions.json @@ -0,0 +1 @@ +{"additionalProperties":false,"type":"object","properties":{"additionalManifestEntries":{"type":"array","items":{"anyOf":[{"$ref":"#/definitions/ManifestEntry"},{"type":"string"}]}},"dontCacheBustURLsMatching":{"$ref":"#/definitions/RegExp"},"manifestTransforms":{"type":"array","items":{"typeof":"function"}},"maximumFileSizeToCacheInBytes":{"default":2097152,"type":"number"},"modifyURLPrefix":{"type":"object","additionalProperties":{"type":"string"}},"globFollow":{"default":true,"type":"boolean"},"globIgnores":{"default":["**/node_modules/**/*"],"type":"array","items":{"type":"string"}},"globPatterns":{"default":["**/*.{js,css,html}"],"type":"array","items":{"type":"string"}},"globStrict":{"default":true,"type":"boolean"},"templatedURLs":{"type":"object","additionalProperties":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"string"}]}},"babelPresetEnvTargets":{"default":["chrome >= 56"],"type":"array","items":{"type":"string"}},"cacheId":{"type":["null","string"]},"cleanupOutdatedCaches":{"default":false,"type":"boolean"},"clientsClaim":{"default":false,"type":"boolean"},"directoryIndex":{"type":["null","string"]},"disableDevLogs":{"default":false,"type":"boolean"},"ignoreURLParametersMatching":{"type":"array","items":{"$ref":"#/definitions/RegExp"}},"importScripts":{"type":"array","items":{"type":"string"}},"inlineWorkboxRuntime":{"default":false,"type":"boolean"},"mode":{"default":"production","type":["null","string"]},"navigateFallback":{"default":null,"type":["null","string"]},"navigateFallbackAllowlist":{"type":"array","items":{"$ref":"#/definitions/RegExp"}},"navigateFallbackDenylist":{"type":"array","items":{"$ref":"#/definitions/RegExp"}},"navigationPreload":{"description":"navigationPreload is only valid when runtimeCaching is configured. However,\nthis can't be expressed via TypeScript, so it's enforced via runtime logic.","default":false,"type":"boolean"},"offlineGoogleAnalytics":{"default":false,"anyOf":[{"$ref":"#/definitions/GoogleAnalyticsInitializeOptions"},{"type":"boolean"}]},"runtimeCaching":{"type":"array","items":{"$ref":"#/definitions/RuntimeCaching"}},"skipWaiting":{"default":false,"type":"boolean"},"sourcemap":{"default":true,"type":"boolean"},"swDest":{"type":"string"},"globDirectory":{"type":"string"}},"required":["swDest"],"definitions":{"ManifestEntry":{"type":"object","properties":{"integrity":{"type":"string"},"revision":{"type":["null","string"]},"url":{"type":"string"}},"additionalProperties":false,"required":["revision","url"]},"RegExp":{"type":"object","properties":{"source":{"type":"string"},"global":{"type":"boolean"},"ignoreCase":{"type":"boolean"},"multiline":{"type":"boolean"},"lastIndex":{"type":"number"},"flags":{"type":"string"},"sticky":{"type":"boolean"},"unicode":{"type":"boolean"},"dotAll":{"type":"boolean"}},"additionalProperties":false,"required":["dotAll","flags","global","ignoreCase","lastIndex","multiline","source","sticky","unicode"]},"GoogleAnalyticsInitializeOptions":{"type":"object","properties":{"cacheName":{"type":"string"},"parameterOverrides":{"type":"object","additionalProperties":{"type":"string"}},"hitFilter":{"type":"object","additionalProperties":false}},"additionalProperties":false},"RuntimeCaching":{"type":"object","properties":{"handler":{"anyOf":[{"$ref":"#/definitions/RouteHandlerCallback"},{"$ref":"#/definitions/RouteHandlerObject"},{"enum":["CacheFirst","CacheOnly","NetworkFirst","NetworkOnly","StaleWhileRevalidate"],"type":"string"}]},"method":{"enum":["DELETE","GET","HEAD","PATCH","POST","PUT"],"type":"string"},"options":{"type":"object","properties":{"backgroundSync":{"type":"object","properties":{"name":{"type":"string"},"options":{"$ref":"#/definitions/QueueOptions"}},"additionalProperties":false,"required":["name"]},"broadcastUpdate":{"type":"object","properties":{"channelName":{"type":"string"},"options":{"$ref":"#/definitions/BroadcastCacheUpdateOptions"}},"additionalProperties":false,"required":["options"]},"cacheableResponse":{"$ref":"#/definitions/CacheableResponseOptions"},"cacheName":{"type":["null","string"]},"expiration":{"$ref":"#/definitions/ExpirationPluginOptions"},"networkTimeoutSeconds":{"type":"number"},"plugins":{"type":"array","items":{"$ref":"#/definitions/WorkboxPlugin"}},"precacheFallback":{"type":"object","properties":{"fallbackURL":{"type":"string"}},"additionalProperties":false,"required":["fallbackURL"]},"fetchOptions":{"$ref":"#/definitions/RequestInit"},"matchOptions":{"$ref":"#/definitions/CacheQueryOptions"}},"additionalProperties":false},"urlPattern":{"anyOf":[{"$ref":"#/definitions/RegExp"},{"$ref":"#/definitions/RouteMatchCallback"},{"type":"string"}]}},"additionalProperties":false,"required":["handler","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},"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","properties":{"handle":{"$ref":"#/definitions/RouteHandlerCallback"}},"additionalProperties":false,"required":["handle"]},"QueueOptions":{"type":"object","properties":{"onSync":{"$ref":"#/definitions/OnSyncCallback"},"maxRetentionTime":{"type":"number"}},"additionalProperties":false},"OnSyncCallback":{"type":"object","additionalProperties":false},"BroadcastCacheUpdateOptions":{"type":"object","properties":{"headersToCheck":{"type":"array","items":{"type":"string"}},"generatePayload":{"type":"object","additionalProperties":false}},"additionalProperties":false},"CacheableResponseOptions":{"type":"object","properties":{"statuses":{"type":"array","items":{"type":"number"}},"headers":{"type":"object","additionalProperties":{"type":"string"}}},"additionalProperties":false},"ExpirationPluginOptions":{"type":"object","properties":{"maxEntries":{"type":"number"},"maxAgeSeconds":{"type":"number"},"matchOptions":{"$ref":"#/definitions/CacheQueryOptions"},"purgeOnQuotaError":{"type":"boolean"}},"additionalProperties":false},"CacheQueryOptions":{"type":"object","properties":{"ignoreMethod":{"type":"boolean"},"ignoreSearch":{"type":"boolean"},"ignoreVary":{"type":"boolean"}},"additionalProperties":false},"WorkboxPlugin":{"description":"An object with optional lifecycle callback properties for the fetch and\ncache operations.","type":"object","properties":{"cacheDidUpdate":{"$ref":"#/definitions/CacheDidUpdateCallback"},"cachedResponseWillBeUsed":{"$ref":"#/definitions/CachedResponseWillBeUsedCallback"},"cacheKeyWillBeUsed":{"$ref":"#/definitions/CacheKeyWillBeUsedCallback"},"cacheWillUpdate":{"$ref":"#/definitions/CacheWillUpdateCallback"},"fetchDidFail":{"$ref":"#/definitions/FetchDidFailCallback"},"fetchDidSucceed":{"$ref":"#/definitions/FetchDidSucceedCallback"},"handlerDidComplete":{"$ref":"#/definitions/HandlerDidCompleteCallback"},"handlerDidError":{"$ref":"#/definitions/HandlerDidErrorCallback"},"handlerDidRespond":{"$ref":"#/definitions/HandlerDidRespondCallback"},"handlerWillRespond":{"$ref":"#/definitions/HandlerWillRespondCallback"},"handlerWillStart":{"$ref":"#/definitions/HandlerWillStartCallback"},"requestWillFetch":{"$ref":"#/definitions/RequestWillFetchCallback"}},"additionalProperties":false},"CacheDidUpdateCallback":{"type":"object","additionalProperties":false},"CachedResponseWillBeUsedCallback":{"type":"object","additionalProperties":false},"CacheKeyWillBeUsedCallback":{"type":"object","additionalProperties":false},"CacheWillUpdateCallback":{"type":"object","additionalProperties":false},"FetchDidFailCallback":{"type":"object","additionalProperties":false},"FetchDidSucceedCallback":{"type":"object","additionalProperties":false},"HandlerDidCompleteCallback":{"type":"object","additionalProperties":false},"HandlerDidErrorCallback":{"type":"object","additionalProperties":false},"HandlerDidRespondCallback":{"type":"object","additionalProperties":false},"HandlerWillRespondCallback":{"type":"object","additionalProperties":false},"HandlerWillStartCallback":{"type":"object","additionalProperties":false},"RequestWillFetchCallback":{"type":"object","additionalProperties":false},"RequestInit":{"type":"object","properties":{"body":{"anyOf":[{"$ref":"#/definitions/ArrayBuffer"},{"$ref":"#/definitions/ArrayBufferView"},{"$ref":"#/definitions/Blob"},{"$ref":"#/definitions/FormData"},{"$ref":"#/definitions/URLSearchParams"},{"$ref":"#/definitions/ReadableStream"},{"type":["null","string"]}]},"cache":{"enum":["default","force-cache","no-cache","no-store","only-if-cached","reload"],"type":"string"},"credentials":{"enum":["include","omit","same-origin"],"type":"string"},"headers":{"anyOf":[{"$ref":"#/definitions/Headers"},{"type":"array","items":{"type":"array","items":{"type":"string"}}},{"$ref":"#/definitions/Record"}]},"integrity":{"type":"string"},"keepalive":{"type":"boolean"},"method":{"type":"string"},"mode":{"enum":["cors","navigate","no-cors","same-origin"],"type":"string"},"redirect":{"enum":["error","follow","manual"],"type":"string"},"referrer":{"type":"string"},"referrerPolicy":{"enum":["","no-referrer","no-referrer-when-downgrade","origin","origin-when-cross-origin","same-origin","strict-origin","strict-origin-when-cross-origin","unsafe-url"],"type":"string"},"signal":{"anyOf":[{"$ref":"#/definitions/AbortSignal"},{"type":"null"}]},"window":{}},"additionalProperties":false},"ArrayBuffer":{"type":"object","properties":{"byteLength":{"type":"number"},"__@toStringTag":{"type":"string"}},"additionalProperties":false,"required":["__@toStringTag","byteLength"]},"ArrayBufferView":{"type":"object","properties":{"buffer":{"anyOf":[{"$ref":"#/definitions/ArrayBuffer"},{"$ref":"#/definitions/SharedArrayBuffer"}]},"byteLength":{"type":"number"},"byteOffset":{"type":"number"}},"additionalProperties":false,"required":["buffer","byteLength","byteOffset"]},"SharedArrayBuffer":{"type":"object","properties":{"byteLength":{"type":"number"},"__@species":{"$ref":"#/definitions/SharedArrayBuffer"},"__@toStringTag":{"type":"string","enum":["SharedArrayBuffer"]}},"additionalProperties":false,"required":["__@species","__@toStringTag","byteLength"]},"Blob":{"type":"object","properties":{"size":{"type":"number"},"type":{"type":"string"}},"additionalProperties":false,"required":["size","type"]},"FormData":{"type":"object","additionalProperties":false},"URLSearchParams":{"type":"object","additionalProperties":false},"ReadableStream":{"type":"object","properties":{"locked":{"type":"boolean"}},"additionalProperties":false,"required":["locked"]},"Headers":{"type":"object","additionalProperties":false},"Record":{"type":"object","additionalProperties":false},"AbortSignal":{"type":"object","properties":{"aborted":{"type":"boolean"},"onabort":{"anyOf":[{"type":"object","additionalProperties":false},{"type":"null"}]}},"additionalProperties":false,"required":["aborted","onabort"]},"RouteMatchCallback":{"description":"The \"match\" callback is used to determine if a `Route` should apply for a\nparticular URL and request. When matching occurs in response to a fetch\nevent from the client, the `event` object is also supplied. However, since\nthe match callback can be invoked outside of a fetch event, matching logic\nshould not assume the `event` object will always be available.\nIf the match callback returns a truthy value, the matching route's\n`RouteHandlerCallback` will be invoked immediately. If the value returned\nis a non-empty array or object, that value will be set on the handler's\n`options.params` argument.","typeof":"function"}},"$schema":"http://json-schema.org/draft-07/schema#"} diff --git a/packages/workbox-build/src/schema/GetManifestOptions.json b/packages/workbox-build/src/schema/GetManifestOptions.json new file mode 100644 index 000000000..9eb30d29a --- /dev/null +++ b/packages/workbox-build/src/schema/GetManifestOptions.json @@ -0,0 +1 @@ +{"additionalProperties":false,"type":"object","properties":{"additionalManifestEntries":{"type":"array","items":{"anyOf":[{"$ref":"#/definitions/ManifestEntry"},{"type":"string"}]}},"dontCacheBustURLsMatching":{"$ref":"#/definitions/RegExp"},"manifestTransforms":{"type":"array","items":{"typeof":"function"}},"maximumFileSizeToCacheInBytes":{"default":2097152,"type":"number"},"modifyURLPrefix":{"type":"object","additionalProperties":{"type":"string"}},"globFollow":{"default":true,"type":"boolean"},"globIgnores":{"default":["**/node_modules/**/*"],"type":"array","items":{"type":"string"}},"globPatterns":{"default":["**/*.{js,css,html}"],"type":"array","items":{"type":"string"}},"globStrict":{"default":true,"type":"boolean"},"templatedURLs":{"type":"object","additionalProperties":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"string"}]}},"globDirectory":{"type":"string"}},"required":["globDirectory"],"definitions":{"ManifestEntry":{"type":"object","properties":{"integrity":{"type":"string"},"revision":{"type":["null","string"]},"url":{"type":"string"}},"additionalProperties":false,"required":["revision","url"]},"RegExp":{"type":"object","properties":{"source":{"type":"string"},"global":{"type":"boolean"},"ignoreCase":{"type":"boolean"},"multiline":{"type":"boolean"},"lastIndex":{"type":"number"},"flags":{"type":"string"},"sticky":{"type":"boolean"},"unicode":{"type":"boolean"},"dotAll":{"type":"boolean"}},"additionalProperties":false,"required":["dotAll","flags","global","ignoreCase","lastIndex","multiline","source","sticky","unicode"]},"GoogleAnalyticsInitializeOptions":{"type":"object","properties":{"cacheName":{"type":"string"},"parameterOverrides":{"type":"object","additionalProperties":{"type":"string"}},"hitFilter":{"type":"object","additionalProperties":false}},"additionalProperties":false},"RuntimeCaching":{"type":"object","properties":{"handler":{"anyOf":[{"$ref":"#/definitions/RouteHandlerCallback"},{"$ref":"#/definitions/RouteHandlerObject"},{"enum":["CacheFirst","CacheOnly","NetworkFirst","NetworkOnly","StaleWhileRevalidate"],"type":"string"}]},"method":{"enum":["DELETE","GET","HEAD","PATCH","POST","PUT"],"type":"string"},"options":{"type":"object","properties":{"backgroundSync":{"type":"object","properties":{"name":{"type":"string"},"options":{"$ref":"#/definitions/QueueOptions"}},"additionalProperties":false,"required":["name"]},"broadcastUpdate":{"type":"object","properties":{"channelName":{"type":"string"},"options":{"$ref":"#/definitions/BroadcastCacheUpdateOptions"}},"additionalProperties":false,"required":["options"]},"cacheableResponse":{"$ref":"#/definitions/CacheableResponseOptions"},"cacheName":{"type":["null","string"]},"expiration":{"$ref":"#/definitions/ExpirationPluginOptions"},"networkTimeoutSeconds":{"type":"number"},"plugins":{"type":"array","items":{"$ref":"#/definitions/WorkboxPlugin"}},"precacheFallback":{"type":"object","properties":{"fallbackURL":{"type":"string"}},"additionalProperties":false,"required":["fallbackURL"]},"fetchOptions":{"$ref":"#/definitions/RequestInit"},"matchOptions":{"$ref":"#/definitions/CacheQueryOptions"}},"additionalProperties":false},"urlPattern":{"anyOf":[{"$ref":"#/definitions/RegExp"},{"$ref":"#/definitions/RouteMatchCallback"},{"type":"string"}]}},"additionalProperties":false,"required":["handler","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},"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","properties":{"handle":{"$ref":"#/definitions/RouteHandlerCallback"}},"additionalProperties":false,"required":["handle"]},"QueueOptions":{"type":"object","properties":{"onSync":{"$ref":"#/definitions/OnSyncCallback"},"maxRetentionTime":{"type":"number"}},"additionalProperties":false},"OnSyncCallback":{"type":"object","additionalProperties":false},"BroadcastCacheUpdateOptions":{"type":"object","properties":{"headersToCheck":{"type":"array","items":{"type":"string"}},"generatePayload":{"type":"object","additionalProperties":false}},"additionalProperties":false},"CacheableResponseOptions":{"type":"object","properties":{"statuses":{"type":"array","items":{"type":"number"}},"headers":{"type":"object","additionalProperties":{"type":"string"}}},"additionalProperties":false},"ExpirationPluginOptions":{"type":"object","properties":{"maxEntries":{"type":"number"},"maxAgeSeconds":{"type":"number"},"matchOptions":{"$ref":"#/definitions/CacheQueryOptions"},"purgeOnQuotaError":{"type":"boolean"}},"additionalProperties":false},"CacheQueryOptions":{"type":"object","properties":{"ignoreMethod":{"type":"boolean"},"ignoreSearch":{"type":"boolean"},"ignoreVary":{"type":"boolean"}},"additionalProperties":false},"WorkboxPlugin":{"description":"An object with optional lifecycle callback properties for the fetch and\ncache operations.","type":"object","properties":{"cacheDidUpdate":{"$ref":"#/definitions/CacheDidUpdateCallback"},"cachedResponseWillBeUsed":{"$ref":"#/definitions/CachedResponseWillBeUsedCallback"},"cacheKeyWillBeUsed":{"$ref":"#/definitions/CacheKeyWillBeUsedCallback"},"cacheWillUpdate":{"$ref":"#/definitions/CacheWillUpdateCallback"},"fetchDidFail":{"$ref":"#/definitions/FetchDidFailCallback"},"fetchDidSucceed":{"$ref":"#/definitions/FetchDidSucceedCallback"},"handlerDidComplete":{"$ref":"#/definitions/HandlerDidCompleteCallback"},"handlerDidError":{"$ref":"#/definitions/HandlerDidErrorCallback"},"handlerDidRespond":{"$ref":"#/definitions/HandlerDidRespondCallback"},"handlerWillRespond":{"$ref":"#/definitions/HandlerWillRespondCallback"},"handlerWillStart":{"$ref":"#/definitions/HandlerWillStartCallback"},"requestWillFetch":{"$ref":"#/definitions/RequestWillFetchCallback"}},"additionalProperties":false},"CacheDidUpdateCallback":{"type":"object","additionalProperties":false},"CachedResponseWillBeUsedCallback":{"type":"object","additionalProperties":false},"CacheKeyWillBeUsedCallback":{"type":"object","additionalProperties":false},"CacheWillUpdateCallback":{"type":"object","additionalProperties":false},"FetchDidFailCallback":{"type":"object","additionalProperties":false},"FetchDidSucceedCallback":{"type":"object","additionalProperties":false},"HandlerDidCompleteCallback":{"type":"object","additionalProperties":false},"HandlerDidErrorCallback":{"type":"object","additionalProperties":false},"HandlerDidRespondCallback":{"type":"object","additionalProperties":false},"HandlerWillRespondCallback":{"type":"object","additionalProperties":false},"HandlerWillStartCallback":{"type":"object","additionalProperties":false},"RequestWillFetchCallback":{"type":"object","additionalProperties":false},"RequestInit":{"type":"object","properties":{"body":{"anyOf":[{"$ref":"#/definitions/ArrayBuffer"},{"$ref":"#/definitions/ArrayBufferView"},{"$ref":"#/definitions/Blob"},{"$ref":"#/definitions/FormData"},{"$ref":"#/definitions/URLSearchParams"},{"$ref":"#/definitions/ReadableStream"},{"type":["null","string"]}]},"cache":{"enum":["default","force-cache","no-cache","no-store","only-if-cached","reload"],"type":"string"},"credentials":{"enum":["include","omit","same-origin"],"type":"string"},"headers":{"anyOf":[{"$ref":"#/definitions/Headers"},{"type":"array","items":{"type":"array","items":{"type":"string"}}},{"$ref":"#/definitions/Record"}]},"integrity":{"type":"string"},"keepalive":{"type":"boolean"},"method":{"type":"string"},"mode":{"enum":["cors","navigate","no-cors","same-origin"],"type":"string"},"redirect":{"enum":["error","follow","manual"],"type":"string"},"referrer":{"type":"string"},"referrerPolicy":{"enum":["","no-referrer","no-referrer-when-downgrade","origin","origin-when-cross-origin","same-origin","strict-origin","strict-origin-when-cross-origin","unsafe-url"],"type":"string"},"signal":{"anyOf":[{"$ref":"#/definitions/AbortSignal"},{"type":"null"}]},"window":{}},"additionalProperties":false},"ArrayBuffer":{"type":"object","properties":{"byteLength":{"type":"number"},"__@toStringTag":{"type":"string"}},"additionalProperties":false,"required":["__@toStringTag","byteLength"]},"ArrayBufferView":{"type":"object","properties":{"buffer":{"anyOf":[{"$ref":"#/definitions/ArrayBuffer"},{"$ref":"#/definitions/SharedArrayBuffer"}]},"byteLength":{"type":"number"},"byteOffset":{"type":"number"}},"additionalProperties":false,"required":["buffer","byteLength","byteOffset"]},"SharedArrayBuffer":{"type":"object","properties":{"byteLength":{"type":"number"},"__@species":{"$ref":"#/definitions/SharedArrayBuffer"},"__@toStringTag":{"type":"string","enum":["SharedArrayBuffer"]}},"additionalProperties":false,"required":["__@species","__@toStringTag","byteLength"]},"Blob":{"type":"object","properties":{"size":{"type":"number"},"type":{"type":"string"}},"additionalProperties":false,"required":["size","type"]},"FormData":{"type":"object","additionalProperties":false},"URLSearchParams":{"type":"object","additionalProperties":false},"ReadableStream":{"type":"object","properties":{"locked":{"type":"boolean"}},"additionalProperties":false,"required":["locked"]},"Headers":{"type":"object","additionalProperties":false},"Record":{"type":"object","additionalProperties":false},"AbortSignal":{"type":"object","properties":{"aborted":{"type":"boolean"},"onabort":{"anyOf":[{"type":"object","additionalProperties":false},{"type":"null"}]}},"additionalProperties":false,"required":["aborted","onabort"]},"RouteMatchCallback":{"description":"The \"match\" callback is used to determine if a `Route` should apply for a\nparticular URL and request. When matching occurs in response to a fetch\nevent from the client, the `event` object is also supplied. However, since\nthe match callback can be invoked outside of a fetch event, matching logic\nshould not assume the `event` object will always be available.\nIf the match callback returns a truthy value, the matching route's\n`RouteHandlerCallback` will be invoked immediately. If the value returned\nis a non-empty array or object, that value will be set on the handler's\n`options.params` argument.","typeof":"function"}},"$schema":"http://json-schema.org/draft-07/schema#"} diff --git a/packages/workbox-build/src/schema/InjectManifestOptions.json b/packages/workbox-build/src/schema/InjectManifestOptions.json new file mode 100644 index 000000000..fc12f138e --- /dev/null +++ b/packages/workbox-build/src/schema/InjectManifestOptions.json @@ -0,0 +1 @@ +{"additionalProperties":false,"type":"object","properties":{"additionalManifestEntries":{"type":"array","items":{"anyOf":[{"$ref":"#/definitions/ManifestEntry"},{"type":"string"}]}},"dontCacheBustURLsMatching":{"$ref":"#/definitions/RegExp"},"manifestTransforms":{"type":"array","items":{"typeof":"function"}},"maximumFileSizeToCacheInBytes":{"default":2097152,"type":"number"},"modifyURLPrefix":{"type":"object","additionalProperties":{"type":"string"}},"globFollow":{"default":true,"type":"boolean"},"globIgnores":{"default":["**/node_modules/**/*"],"type":"array","items":{"type":"string"}},"globPatterns":{"default":["**/*.{js,css,html}"],"type":"array","items":{"type":"string"}},"globStrict":{"default":true,"type":"boolean"},"templatedURLs":{"type":"object","additionalProperties":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"string"}]}},"injectionPoint":{"default":"self.__WB_MANIFEST","type":"string"},"swSrc":{"type":"string"},"swDest":{"type":"string"},"globDirectory":{"type":"string"}},"required":["globDirectory","swDest","swSrc"],"definitions":{"ManifestEntry":{"type":"object","properties":{"integrity":{"type":"string"},"revision":{"type":["null","string"]},"url":{"type":"string"}},"additionalProperties":false,"required":["revision","url"]},"RegExp":{"type":"object","properties":{"source":{"type":"string"},"global":{"type":"boolean"},"ignoreCase":{"type":"boolean"},"multiline":{"type":"boolean"},"lastIndex":{"type":"number"},"flags":{"type":"string"},"sticky":{"type":"boolean"},"unicode":{"type":"boolean"},"dotAll":{"type":"boolean"}},"additionalProperties":false,"required":["dotAll","flags","global","ignoreCase","lastIndex","multiline","source","sticky","unicode"]},"GoogleAnalyticsInitializeOptions":{"type":"object","properties":{"cacheName":{"type":"string"},"parameterOverrides":{"type":"object","additionalProperties":{"type":"string"}},"hitFilter":{"type":"object","additionalProperties":false}},"additionalProperties":false},"RuntimeCaching":{"type":"object","properties":{"handler":{"anyOf":[{"$ref":"#/definitions/RouteHandlerCallback"},{"$ref":"#/definitions/RouteHandlerObject"},{"enum":["CacheFirst","CacheOnly","NetworkFirst","NetworkOnly","StaleWhileRevalidate"],"type":"string"}]},"method":{"enum":["DELETE","GET","HEAD","PATCH","POST","PUT"],"type":"string"},"options":{"type":"object","properties":{"backgroundSync":{"type":"object","properties":{"name":{"type":"string"},"options":{"$ref":"#/definitions/QueueOptions"}},"additionalProperties":false,"required":["name"]},"broadcastUpdate":{"type":"object","properties":{"channelName":{"type":"string"},"options":{"$ref":"#/definitions/BroadcastCacheUpdateOptions"}},"additionalProperties":false,"required":["options"]},"cacheableResponse":{"$ref":"#/definitions/CacheableResponseOptions"},"cacheName":{"type":["null","string"]},"expiration":{"$ref":"#/definitions/ExpirationPluginOptions"},"networkTimeoutSeconds":{"type":"number"},"plugins":{"type":"array","items":{"$ref":"#/definitions/WorkboxPlugin"}},"precacheFallback":{"type":"object","properties":{"fallbackURL":{"type":"string"}},"additionalProperties":false,"required":["fallbackURL"]},"fetchOptions":{"$ref":"#/definitions/RequestInit"},"matchOptions":{"$ref":"#/definitions/CacheQueryOptions"}},"additionalProperties":false},"urlPattern":{"anyOf":[{"$ref":"#/definitions/RegExp"},{"$ref":"#/definitions/RouteMatchCallback"},{"type":"string"}]}},"additionalProperties":false,"required":["handler","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},"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","properties":{"handle":{"$ref":"#/definitions/RouteHandlerCallback"}},"additionalProperties":false,"required":["handle"]},"QueueOptions":{"type":"object","properties":{"onSync":{"$ref":"#/definitions/OnSyncCallback"},"maxRetentionTime":{"type":"number"}},"additionalProperties":false},"OnSyncCallback":{"type":"object","additionalProperties":false},"BroadcastCacheUpdateOptions":{"type":"object","properties":{"headersToCheck":{"type":"array","items":{"type":"string"}},"generatePayload":{"type":"object","additionalProperties":false}},"additionalProperties":false},"CacheableResponseOptions":{"type":"object","properties":{"statuses":{"type":"array","items":{"type":"number"}},"headers":{"type":"object","additionalProperties":{"type":"string"}}},"additionalProperties":false},"ExpirationPluginOptions":{"type":"object","properties":{"maxEntries":{"type":"number"},"maxAgeSeconds":{"type":"number"},"matchOptions":{"$ref":"#/definitions/CacheQueryOptions"},"purgeOnQuotaError":{"type":"boolean"}},"additionalProperties":false},"CacheQueryOptions":{"type":"object","properties":{"ignoreMethod":{"type":"boolean"},"ignoreSearch":{"type":"boolean"},"ignoreVary":{"type":"boolean"}},"additionalProperties":false},"WorkboxPlugin":{"description":"An object with optional lifecycle callback properties for the fetch and\ncache operations.","type":"object","properties":{"cacheDidUpdate":{"$ref":"#/definitions/CacheDidUpdateCallback"},"cachedResponseWillBeUsed":{"$ref":"#/definitions/CachedResponseWillBeUsedCallback"},"cacheKeyWillBeUsed":{"$ref":"#/definitions/CacheKeyWillBeUsedCallback"},"cacheWillUpdate":{"$ref":"#/definitions/CacheWillUpdateCallback"},"fetchDidFail":{"$ref":"#/definitions/FetchDidFailCallback"},"fetchDidSucceed":{"$ref":"#/definitions/FetchDidSucceedCallback"},"handlerDidComplete":{"$ref":"#/definitions/HandlerDidCompleteCallback"},"handlerDidError":{"$ref":"#/definitions/HandlerDidErrorCallback"},"handlerDidRespond":{"$ref":"#/definitions/HandlerDidRespondCallback"},"handlerWillRespond":{"$ref":"#/definitions/HandlerWillRespondCallback"},"handlerWillStart":{"$ref":"#/definitions/HandlerWillStartCallback"},"requestWillFetch":{"$ref":"#/definitions/RequestWillFetchCallback"}},"additionalProperties":false},"CacheDidUpdateCallback":{"type":"object","additionalProperties":false},"CachedResponseWillBeUsedCallback":{"type":"object","additionalProperties":false},"CacheKeyWillBeUsedCallback":{"type":"object","additionalProperties":false},"CacheWillUpdateCallback":{"type":"object","additionalProperties":false},"FetchDidFailCallback":{"type":"object","additionalProperties":false},"FetchDidSucceedCallback":{"type":"object","additionalProperties":false},"HandlerDidCompleteCallback":{"type":"object","additionalProperties":false},"HandlerDidErrorCallback":{"type":"object","additionalProperties":false},"HandlerDidRespondCallback":{"type":"object","additionalProperties":false},"HandlerWillRespondCallback":{"type":"object","additionalProperties":false},"HandlerWillStartCallback":{"type":"object","additionalProperties":false},"RequestWillFetchCallback":{"type":"object","additionalProperties":false},"RequestInit":{"type":"object","properties":{"body":{"anyOf":[{"$ref":"#/definitions/ArrayBuffer"},{"$ref":"#/definitions/ArrayBufferView"},{"$ref":"#/definitions/Blob"},{"$ref":"#/definitions/FormData"},{"$ref":"#/definitions/URLSearchParams"},{"$ref":"#/definitions/ReadableStream"},{"type":["null","string"]}]},"cache":{"enum":["default","force-cache","no-cache","no-store","only-if-cached","reload"],"type":"string"},"credentials":{"enum":["include","omit","same-origin"],"type":"string"},"headers":{"anyOf":[{"$ref":"#/definitions/Headers"},{"type":"array","items":{"type":"array","items":{"type":"string"}}},{"$ref":"#/definitions/Record"}]},"integrity":{"type":"string"},"keepalive":{"type":"boolean"},"method":{"type":"string"},"mode":{"enum":["cors","navigate","no-cors","same-origin"],"type":"string"},"redirect":{"enum":["error","follow","manual"],"type":"string"},"referrer":{"type":"string"},"referrerPolicy":{"enum":["","no-referrer","no-referrer-when-downgrade","origin","origin-when-cross-origin","same-origin","strict-origin","strict-origin-when-cross-origin","unsafe-url"],"type":"string"},"signal":{"anyOf":[{"$ref":"#/definitions/AbortSignal"},{"type":"null"}]},"window":{}},"additionalProperties":false},"ArrayBuffer":{"type":"object","properties":{"byteLength":{"type":"number"},"__@toStringTag":{"type":"string"}},"additionalProperties":false,"required":["__@toStringTag","byteLength"]},"ArrayBufferView":{"type":"object","properties":{"buffer":{"anyOf":[{"$ref":"#/definitions/ArrayBuffer"},{"$ref":"#/definitions/SharedArrayBuffer"}]},"byteLength":{"type":"number"},"byteOffset":{"type":"number"}},"additionalProperties":false,"required":["buffer","byteLength","byteOffset"]},"SharedArrayBuffer":{"type":"object","properties":{"byteLength":{"type":"number"},"__@species":{"$ref":"#/definitions/SharedArrayBuffer"},"__@toStringTag":{"type":"string","enum":["SharedArrayBuffer"]}},"additionalProperties":false,"required":["__@species","__@toStringTag","byteLength"]},"Blob":{"type":"object","properties":{"size":{"type":"number"},"type":{"type":"string"}},"additionalProperties":false,"required":["size","type"]},"FormData":{"type":"object","additionalProperties":false},"URLSearchParams":{"type":"object","additionalProperties":false},"ReadableStream":{"type":"object","properties":{"locked":{"type":"boolean"}},"additionalProperties":false,"required":["locked"]},"Headers":{"type":"object","additionalProperties":false},"Record":{"type":"object","additionalProperties":false},"AbortSignal":{"type":"object","properties":{"aborted":{"type":"boolean"},"onabort":{"anyOf":[{"type":"object","additionalProperties":false},{"type":"null"}]}},"additionalProperties":false,"required":["aborted","onabort"]},"RouteMatchCallback":{"description":"The \"match\" callback is used to determine if a `Route` should apply for a\nparticular URL and request. When matching occurs in response to a fetch\nevent from the client, the `event` object is also supplied. However, since\nthe match callback can be invoked outside of a fetch event, matching logic\nshould not assume the `event` object will always be available.\nIf the match callback returns a truthy value, the matching route's\n`RouteHandlerCallback` will be invoked immediately. If the value returned\nis a non-empty array or object, that value will be set on the handler's\n`options.params` argument.","typeof":"function"}},"$schema":"http://json-schema.org/draft-07/schema#"} diff --git a/packages/workbox-build/src/schema/WebpackGenerateSWOptions.json b/packages/workbox-build/src/schema/WebpackGenerateSWOptions.json new file mode 100644 index 000000000..16a3f1c00 --- /dev/null +++ b/packages/workbox-build/src/schema/WebpackGenerateSWOptions.json @@ -0,0 +1 @@ +{"additionalProperties":false,"type":"object","properties":{"additionalManifestEntries":{"type":"array","items":{"anyOf":[{"$ref":"#/definitions/ManifestEntry"},{"type":"string"}]}},"dontCacheBustURLsMatching":{"$ref":"#/definitions/RegExp"},"manifestTransforms":{"type":"array","items":{"typeof":"function"}},"maximumFileSizeToCacheInBytes":{"default":2097152,"type":"number"},"modifyURLPrefix":{"type":"object","additionalProperties":{"type":"string"}},"chunks":{"type":"array","items":{"type":"string"}},"exclude":{"type":"array","items":{"anyOf":[{"$ref":"#/definitions/RegExp"},{"type":"string"},{"typeof":"function"}]}},"excludeChunks":{"type":"array","items":{"type":"string"}},"include":{"type":"array","items":{"anyOf":[{"$ref":"#/definitions/RegExp"},{"type":"string"},{"typeof":"function"}]}},"mode":{"default":"production","type":["null","string"]},"babelPresetEnvTargets":{"default":["chrome >= 56"],"type":"array","items":{"type":"string"}},"cacheId":{"type":["null","string"]},"cleanupOutdatedCaches":{"default":false,"type":"boolean"},"clientsClaim":{"default":false,"type":"boolean"},"directoryIndex":{"type":["null","string"]},"disableDevLogs":{"default":false,"type":"boolean"},"ignoreURLParametersMatching":{"type":"array","items":{"$ref":"#/definitions/RegExp"}},"importScripts":{"type":"array","items":{"type":"string"}},"inlineWorkboxRuntime":{"default":false,"type":"boolean"},"navigateFallback":{"default":null,"type":["null","string"]},"navigateFallbackAllowlist":{"type":"array","items":{"$ref":"#/definitions/RegExp"}},"navigateFallbackDenylist":{"type":"array","items":{"$ref":"#/definitions/RegExp"}},"navigationPreload":{"description":"navigationPreload is only valid when runtimeCaching is configured. However,\nthis can't be expressed via TypeScript, so it's enforced via runtime logic.","default":false,"type":"boolean"},"offlineGoogleAnalytics":{"default":false,"anyOf":[{"$ref":"#/definitions/GoogleAnalyticsInitializeOptions"},{"type":"boolean"}]},"runtimeCaching":{"type":"array","items":{"$ref":"#/definitions/RuntimeCaching"}},"skipWaiting":{"default":false,"type":"boolean"},"sourcemap":{"default":true,"type":"boolean"},"importScriptsViaChunks":{"type":"array","items":{"type":"string"}},"swDest":{"default":"service-worker.js","type":"string"}},"definitions":{"ManifestEntry":{"type":"object","properties":{"integrity":{"type":"string"},"revision":{"type":["null","string"]},"url":{"type":"string"}},"additionalProperties":false,"required":["revision","url"]},"RegExp":{"type":"object","properties":{"source":{"type":"string"},"global":{"type":"boolean"},"ignoreCase":{"type":"boolean"},"multiline":{"type":"boolean"},"lastIndex":{"type":"number"},"flags":{"type":"string"},"sticky":{"type":"boolean"},"unicode":{"type":"boolean"},"dotAll":{"type":"boolean"}},"additionalProperties":false,"required":["dotAll","flags","global","ignoreCase","lastIndex","multiline","source","sticky","unicode"]},"GoogleAnalyticsInitializeOptions":{"type":"object","properties":{"cacheName":{"type":"string"},"parameterOverrides":{"type":"object","additionalProperties":{"type":"string"}},"hitFilter":{"type":"object","additionalProperties":false}},"additionalProperties":false},"RuntimeCaching":{"type":"object","properties":{"handler":{"anyOf":[{"$ref":"#/definitions/RouteHandlerCallback"},{"$ref":"#/definitions/RouteHandlerObject"},{"enum":["CacheFirst","CacheOnly","NetworkFirst","NetworkOnly","StaleWhileRevalidate"],"type":"string"}]},"method":{"enum":["DELETE","GET","HEAD","PATCH","POST","PUT"],"type":"string"},"options":{"type":"object","properties":{"backgroundSync":{"type":"object","properties":{"name":{"type":"string"},"options":{"$ref":"#/definitions/QueueOptions"}},"additionalProperties":false,"required":["name"]},"broadcastUpdate":{"type":"object","properties":{"channelName":{"type":"string"},"options":{"$ref":"#/definitions/BroadcastCacheUpdateOptions"}},"additionalProperties":false,"required":["options"]},"cacheableResponse":{"$ref":"#/definitions/CacheableResponseOptions"},"cacheName":{"type":["null","string"]},"expiration":{"$ref":"#/definitions/ExpirationPluginOptions"},"networkTimeoutSeconds":{"type":"number"},"plugins":{"type":"array","items":{"$ref":"#/definitions/WorkboxPlugin"}},"precacheFallback":{"type":"object","properties":{"fallbackURL":{"type":"string"}},"additionalProperties":false,"required":["fallbackURL"]},"fetchOptions":{"$ref":"#/definitions/RequestInit"},"matchOptions":{"$ref":"#/definitions/CacheQueryOptions"}},"additionalProperties":false},"urlPattern":{"anyOf":[{"$ref":"#/definitions/RegExp"},{"$ref":"#/definitions/RouteMatchCallback"},{"type":"string"}]}},"additionalProperties":false,"required":["handler","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},"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","properties":{"handle":{"$ref":"#/definitions/RouteHandlerCallback"}},"additionalProperties":false,"required":["handle"]},"QueueOptions":{"type":"object","properties":{"onSync":{"$ref":"#/definitions/OnSyncCallback"},"maxRetentionTime":{"type":"number"}},"additionalProperties":false},"OnSyncCallback":{"type":"object","additionalProperties":false},"BroadcastCacheUpdateOptions":{"type":"object","properties":{"headersToCheck":{"type":"array","items":{"type":"string"}},"generatePayload":{"type":"object","additionalProperties":false}},"additionalProperties":false},"CacheableResponseOptions":{"type":"object","properties":{"statuses":{"type":"array","items":{"type":"number"}},"headers":{"type":"object","additionalProperties":{"type":"string"}}},"additionalProperties":false},"ExpirationPluginOptions":{"type":"object","properties":{"maxEntries":{"type":"number"},"maxAgeSeconds":{"type":"number"},"matchOptions":{"$ref":"#/definitions/CacheQueryOptions"},"purgeOnQuotaError":{"type":"boolean"}},"additionalProperties":false},"CacheQueryOptions":{"type":"object","properties":{"ignoreMethod":{"type":"boolean"},"ignoreSearch":{"type":"boolean"},"ignoreVary":{"type":"boolean"}},"additionalProperties":false},"WorkboxPlugin":{"description":"An object with optional lifecycle callback properties for the fetch and\ncache operations.","type":"object","properties":{"cacheDidUpdate":{"$ref":"#/definitions/CacheDidUpdateCallback"},"cachedResponseWillBeUsed":{"$ref":"#/definitions/CachedResponseWillBeUsedCallback"},"cacheKeyWillBeUsed":{"$ref":"#/definitions/CacheKeyWillBeUsedCallback"},"cacheWillUpdate":{"$ref":"#/definitions/CacheWillUpdateCallback"},"fetchDidFail":{"$ref":"#/definitions/FetchDidFailCallback"},"fetchDidSucceed":{"$ref":"#/definitions/FetchDidSucceedCallback"},"handlerDidComplete":{"$ref":"#/definitions/HandlerDidCompleteCallback"},"handlerDidError":{"$ref":"#/definitions/HandlerDidErrorCallback"},"handlerDidRespond":{"$ref":"#/definitions/HandlerDidRespondCallback"},"handlerWillRespond":{"$ref":"#/definitions/HandlerWillRespondCallback"},"handlerWillStart":{"$ref":"#/definitions/HandlerWillStartCallback"},"requestWillFetch":{"$ref":"#/definitions/RequestWillFetchCallback"}},"additionalProperties":false},"CacheDidUpdateCallback":{"type":"object","additionalProperties":false},"CachedResponseWillBeUsedCallback":{"type":"object","additionalProperties":false},"CacheKeyWillBeUsedCallback":{"type":"object","additionalProperties":false},"CacheWillUpdateCallback":{"type":"object","additionalProperties":false},"FetchDidFailCallback":{"type":"object","additionalProperties":false},"FetchDidSucceedCallback":{"type":"object","additionalProperties":false},"HandlerDidCompleteCallback":{"type":"object","additionalProperties":false},"HandlerDidErrorCallback":{"type":"object","additionalProperties":false},"HandlerDidRespondCallback":{"type":"object","additionalProperties":false},"HandlerWillRespondCallback":{"type":"object","additionalProperties":false},"HandlerWillStartCallback":{"type":"object","additionalProperties":false},"RequestWillFetchCallback":{"type":"object","additionalProperties":false},"RequestInit":{"type":"object","properties":{"body":{"anyOf":[{"$ref":"#/definitions/ArrayBuffer"},{"$ref":"#/definitions/ArrayBufferView"},{"$ref":"#/definitions/Blob"},{"$ref":"#/definitions/FormData"},{"$ref":"#/definitions/URLSearchParams"},{"$ref":"#/definitions/ReadableStream"},{"type":["null","string"]}]},"cache":{"enum":["default","force-cache","no-cache","no-store","only-if-cached","reload"],"type":"string"},"credentials":{"enum":["include","omit","same-origin"],"type":"string"},"headers":{"anyOf":[{"$ref":"#/definitions/Headers"},{"type":"array","items":{"type":"array","items":{"type":"string"}}},{"$ref":"#/definitions/Record"}]},"integrity":{"type":"string"},"keepalive":{"type":"boolean"},"method":{"type":"string"},"mode":{"enum":["cors","navigate","no-cors","same-origin"],"type":"string"},"redirect":{"enum":["error","follow","manual"],"type":"string"},"referrer":{"type":"string"},"referrerPolicy":{"enum":["","no-referrer","no-referrer-when-downgrade","origin","origin-when-cross-origin","same-origin","strict-origin","strict-origin-when-cross-origin","unsafe-url"],"type":"string"},"signal":{"anyOf":[{"$ref":"#/definitions/AbortSignal"},{"type":"null"}]},"window":{}},"additionalProperties":false},"ArrayBuffer":{"type":"object","properties":{"byteLength":{"type":"number"},"__@toStringTag":{"type":"string"}},"additionalProperties":false,"required":["__@toStringTag","byteLength"]},"ArrayBufferView":{"type":"object","properties":{"buffer":{"anyOf":[{"$ref":"#/definitions/ArrayBuffer"},{"$ref":"#/definitions/SharedArrayBuffer"}]},"byteLength":{"type":"number"},"byteOffset":{"type":"number"}},"additionalProperties":false,"required":["buffer","byteLength","byteOffset"]},"SharedArrayBuffer":{"type":"object","properties":{"byteLength":{"type":"number"},"__@species":{"$ref":"#/definitions/SharedArrayBuffer"},"__@toStringTag":{"type":"string","enum":["SharedArrayBuffer"]}},"additionalProperties":false,"required":["__@species","__@toStringTag","byteLength"]},"Blob":{"type":"object","properties":{"size":{"type":"number"},"type":{"type":"string"}},"additionalProperties":false,"required":["size","type"]},"FormData":{"type":"object","additionalProperties":false},"URLSearchParams":{"type":"object","additionalProperties":false},"ReadableStream":{"type":"object","properties":{"locked":{"type":"boolean"}},"additionalProperties":false,"required":["locked"]},"Headers":{"type":"object","additionalProperties":false},"Record":{"type":"object","additionalProperties":false},"AbortSignal":{"type":"object","properties":{"aborted":{"type":"boolean"},"onabort":{"anyOf":[{"type":"object","additionalProperties":false},{"type":"null"}]}},"additionalProperties":false,"required":["aborted","onabort"]},"RouteMatchCallback":{"description":"The \"match\" callback is used to determine if a `Route` should apply for a\nparticular URL and request. When matching occurs in response to a fetch\nevent from the client, the `event` object is also supplied. However, since\nthe match callback can be invoked outside of a fetch event, matching logic\nshould not assume the `event` object will always be available.\nIf the match callback returns a truthy value, the matching route's\n`RouteHandlerCallback` will be invoked immediately. If the value returned\nis a non-empty array or object, that value will be set on the handler's\n`options.params` argument.","typeof":"function"}},"$schema":"http://json-schema.org/draft-07/schema#"} diff --git a/packages/workbox-build/src/schema/WebpackInjectManifestOptions.json b/packages/workbox-build/src/schema/WebpackInjectManifestOptions.json new file mode 100644 index 000000000..fb1e922f3 --- /dev/null +++ b/packages/workbox-build/src/schema/WebpackInjectManifestOptions.json @@ -0,0 +1 @@ +{"additionalProperties":false,"type":"object","properties":{"additionalManifestEntries":{"type":"array","items":{"anyOf":[{"$ref":"#/definitions/ManifestEntry"},{"type":"string"}]}},"dontCacheBustURLsMatching":{"$ref":"#/definitions/RegExp"},"manifestTransforms":{"type":"array","items":{"typeof":"function"}},"maximumFileSizeToCacheInBytes":{"default":2097152,"type":"number"},"modifyURLPrefix":{"type":"object","additionalProperties":{"type":"string"}},"chunks":{"type":"array","items":{"type":"string"}},"exclude":{"type":"array","items":{"anyOf":[{"$ref":"#/definitions/RegExp"},{"type":"string"},{"typeof":"function"}]}},"excludeChunks":{"type":"array","items":{"type":"string"}},"include":{"type":"array","items":{"anyOf":[{"$ref":"#/definitions/RegExp"},{"type":"string"},{"typeof":"function"}]}},"mode":{"type":["null","string"]},"injectionPoint":{"default":"self.__WB_MANIFEST","type":"string"},"swSrc":{"type":"string"},"compileSrc":{"default":true,"type":"boolean"},"swDest":{"type":"string"},"webpackCompilationPlugins":{"type":"array","items":{}}},"required":["swSrc"],"definitions":{"ManifestEntry":{"type":"object","properties":{"integrity":{"type":"string"},"revision":{"type":["null","string"]},"url":{"type":"string"}},"additionalProperties":false,"required":["revision","url"]},"RegExp":{"type":"object","properties":{"source":{"type":"string"},"global":{"type":"boolean"},"ignoreCase":{"type":"boolean"},"multiline":{"type":"boolean"},"lastIndex":{"type":"number"},"flags":{"type":"string"},"sticky":{"type":"boolean"},"unicode":{"type":"boolean"},"dotAll":{"type":"boolean"}},"additionalProperties":false,"required":["dotAll","flags","global","ignoreCase","lastIndex","multiline","source","sticky","unicode"]},"GoogleAnalyticsInitializeOptions":{"type":"object","properties":{"cacheName":{"type":"string"},"parameterOverrides":{"type":"object","additionalProperties":{"type":"string"}},"hitFilter":{"type":"object","additionalProperties":false}},"additionalProperties":false},"RuntimeCaching":{"type":"object","properties":{"handler":{"anyOf":[{"$ref":"#/definitions/RouteHandlerCallback"},{"$ref":"#/definitions/RouteHandlerObject"},{"enum":["CacheFirst","CacheOnly","NetworkFirst","NetworkOnly","StaleWhileRevalidate"],"type":"string"}]},"method":{"enum":["DELETE","GET","HEAD","PATCH","POST","PUT"],"type":"string"},"options":{"type":"object","properties":{"backgroundSync":{"type":"object","properties":{"name":{"type":"string"},"options":{"$ref":"#/definitions/QueueOptions"}},"additionalProperties":false,"required":["name"]},"broadcastUpdate":{"type":"object","properties":{"channelName":{"type":"string"},"options":{"$ref":"#/definitions/BroadcastCacheUpdateOptions"}},"additionalProperties":false,"required":["options"]},"cacheableResponse":{"$ref":"#/definitions/CacheableResponseOptions"},"cacheName":{"type":["null","string"]},"expiration":{"$ref":"#/definitions/ExpirationPluginOptions"},"networkTimeoutSeconds":{"type":"number"},"plugins":{"type":"array","items":{"$ref":"#/definitions/WorkboxPlugin"}},"precacheFallback":{"type":"object","properties":{"fallbackURL":{"type":"string"}},"additionalProperties":false,"required":["fallbackURL"]},"fetchOptions":{"$ref":"#/definitions/RequestInit"},"matchOptions":{"$ref":"#/definitions/CacheQueryOptions"}},"additionalProperties":false},"urlPattern":{"anyOf":[{"$ref":"#/definitions/RegExp"},{"$ref":"#/definitions/RouteMatchCallback"},{"type":"string"}]}},"additionalProperties":false,"required":["handler","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},"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","properties":{"handle":{"$ref":"#/definitions/RouteHandlerCallback"}},"additionalProperties":false,"required":["handle"]},"QueueOptions":{"type":"object","properties":{"onSync":{"$ref":"#/definitions/OnSyncCallback"},"maxRetentionTime":{"type":"number"}},"additionalProperties":false},"OnSyncCallback":{"type":"object","additionalProperties":false},"BroadcastCacheUpdateOptions":{"type":"object","properties":{"headersToCheck":{"type":"array","items":{"type":"string"}},"generatePayload":{"type":"object","additionalProperties":false}},"additionalProperties":false},"CacheableResponseOptions":{"type":"object","properties":{"statuses":{"type":"array","items":{"type":"number"}},"headers":{"type":"object","additionalProperties":{"type":"string"}}},"additionalProperties":false},"ExpirationPluginOptions":{"type":"object","properties":{"maxEntries":{"type":"number"},"maxAgeSeconds":{"type":"number"},"matchOptions":{"$ref":"#/definitions/CacheQueryOptions"},"purgeOnQuotaError":{"type":"boolean"}},"additionalProperties":false},"CacheQueryOptions":{"type":"object","properties":{"ignoreMethod":{"type":"boolean"},"ignoreSearch":{"type":"boolean"},"ignoreVary":{"type":"boolean"}},"additionalProperties":false},"WorkboxPlugin":{"description":"An object with optional lifecycle callback properties for the fetch and\ncache operations.","type":"object","properties":{"cacheDidUpdate":{"$ref":"#/definitions/CacheDidUpdateCallback"},"cachedResponseWillBeUsed":{"$ref":"#/definitions/CachedResponseWillBeUsedCallback"},"cacheKeyWillBeUsed":{"$ref":"#/definitions/CacheKeyWillBeUsedCallback"},"cacheWillUpdate":{"$ref":"#/definitions/CacheWillUpdateCallback"},"fetchDidFail":{"$ref":"#/definitions/FetchDidFailCallback"},"fetchDidSucceed":{"$ref":"#/definitions/FetchDidSucceedCallback"},"handlerDidComplete":{"$ref":"#/definitions/HandlerDidCompleteCallback"},"handlerDidError":{"$ref":"#/definitions/HandlerDidErrorCallback"},"handlerDidRespond":{"$ref":"#/definitions/HandlerDidRespondCallback"},"handlerWillRespond":{"$ref":"#/definitions/HandlerWillRespondCallback"},"handlerWillStart":{"$ref":"#/definitions/HandlerWillStartCallback"},"requestWillFetch":{"$ref":"#/definitions/RequestWillFetchCallback"}},"additionalProperties":false},"CacheDidUpdateCallback":{"type":"object","additionalProperties":false},"CachedResponseWillBeUsedCallback":{"type":"object","additionalProperties":false},"CacheKeyWillBeUsedCallback":{"type":"object","additionalProperties":false},"CacheWillUpdateCallback":{"type":"object","additionalProperties":false},"FetchDidFailCallback":{"type":"object","additionalProperties":false},"FetchDidSucceedCallback":{"type":"object","additionalProperties":false},"HandlerDidCompleteCallback":{"type":"object","additionalProperties":false},"HandlerDidErrorCallback":{"type":"object","additionalProperties":false},"HandlerDidRespondCallback":{"type":"object","additionalProperties":false},"HandlerWillRespondCallback":{"type":"object","additionalProperties":false},"HandlerWillStartCallback":{"type":"object","additionalProperties":false},"RequestWillFetchCallback":{"type":"object","additionalProperties":false},"RequestInit":{"type":"object","properties":{"body":{"anyOf":[{"$ref":"#/definitions/ArrayBuffer"},{"$ref":"#/definitions/ArrayBufferView"},{"$ref":"#/definitions/Blob"},{"$ref":"#/definitions/FormData"},{"$ref":"#/definitions/URLSearchParams"},{"$ref":"#/definitions/ReadableStream"},{"type":["null","string"]}]},"cache":{"enum":["default","force-cache","no-cache","no-store","only-if-cached","reload"],"type":"string"},"credentials":{"enum":["include","omit","same-origin"],"type":"string"},"headers":{"anyOf":[{"$ref":"#/definitions/Headers"},{"type":"array","items":{"type":"array","items":{"type":"string"}}},{"$ref":"#/definitions/Record"}]},"integrity":{"type":"string"},"keepalive":{"type":"boolean"},"method":{"type":"string"},"mode":{"enum":["cors","navigate","no-cors","same-origin"],"type":"string"},"redirect":{"enum":["error","follow","manual"],"type":"string"},"referrer":{"type":"string"},"referrerPolicy":{"enum":["","no-referrer","no-referrer-when-downgrade","origin","origin-when-cross-origin","same-origin","strict-origin","strict-origin-when-cross-origin","unsafe-url"],"type":"string"},"signal":{"anyOf":[{"$ref":"#/definitions/AbortSignal"},{"type":"null"}]},"window":{}},"additionalProperties":false},"ArrayBuffer":{"type":"object","properties":{"byteLength":{"type":"number"},"__@toStringTag":{"type":"string"}},"additionalProperties":false,"required":["__@toStringTag","byteLength"]},"ArrayBufferView":{"type":"object","properties":{"buffer":{"anyOf":[{"$ref":"#/definitions/ArrayBuffer"},{"$ref":"#/definitions/SharedArrayBuffer"}]},"byteLength":{"type":"number"},"byteOffset":{"type":"number"}},"additionalProperties":false,"required":["buffer","byteLength","byteOffset"]},"SharedArrayBuffer":{"type":"object","properties":{"byteLength":{"type":"number"},"__@species":{"$ref":"#/definitions/SharedArrayBuffer"},"__@toStringTag":{"type":"string","enum":["SharedArrayBuffer"]}},"additionalProperties":false,"required":["__@species","__@toStringTag","byteLength"]},"Blob":{"type":"object","properties":{"size":{"type":"number"},"type":{"type":"string"}},"additionalProperties":false,"required":["size","type"]},"FormData":{"type":"object","additionalProperties":false},"URLSearchParams":{"type":"object","additionalProperties":false},"ReadableStream":{"type":"object","properties":{"locked":{"type":"boolean"}},"additionalProperties":false,"required":["locked"]},"Headers":{"type":"object","additionalProperties":false},"Record":{"type":"object","additionalProperties":false},"AbortSignal":{"type":"object","properties":{"aborted":{"type":"boolean"},"onabort":{"anyOf":[{"type":"object","additionalProperties":false},{"type":"null"}]}},"additionalProperties":false,"required":["aborted","onabort"]},"RouteMatchCallback":{"description":"The \"match\" callback is used to determine if a `Route` should apply for a\nparticular URL and request. When matching occurs in response to a fetch\nevent from the client, the `event` object is also supplied. However, since\nthe match callback can be invoked outside of a fetch event, matching logic\nshould not assume the `event` object will always be available.\nIf the match callback returns a truthy value, the matching route's\n`RouteHandlerCallback` will be invoked immediately. If the value returned\nis a non-empty array or object, that value will be set on the handler's\n`options.params` argument.","typeof":"function"}},"$schema":"http://json-schema.org/draft-07/schema#"} diff --git a/packages/workbox-build/src/source-map-url.d.ts b/packages/workbox-build/src/source-map-url.d.ts new file mode 100644 index 000000000..8958463c8 --- /dev/null +++ b/packages/workbox-build/src/source-map-url.d.ts @@ -0,0 +1 @@ +declare module 'source-map-url'; diff --git a/packages/workbox-build/src/strip-comments.d.ts b/packages/workbox-build/src/strip-comments.d.ts new file mode 100644 index 000000000..3d681f453 --- /dev/null +++ b/packages/workbox-build/src/strip-comments.d.ts @@ -0,0 +1 @@ +declare module 'strip-comments'; diff --git a/packages/workbox-build/src/templates/sw-template.js b/packages/workbox-build/src/templates/sw-template.ts similarity index 98% rename from packages/workbox-build/src/templates/sw-template.js rename to packages/workbox-build/src/templates/sw-template.ts index 0cc6ac47a..dacfaef89 100644 --- a/packages/workbox-build/src/templates/sw-template.js +++ b/packages/workbox-build/src/templates/sw-template.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -module.exports = `/** +export const swTemplate = `/** * Welcome to your Workbox-powered service worker! * * You'll need to register this file in your web app. diff --git a/packages/workbox-build/src/types.ts b/packages/workbox-build/src/types.ts new file mode 100644 index 000000000..c95c29871 --- /dev/null +++ b/packages/workbox-build/src/types.ts @@ -0,0 +1,246 @@ +import {PackageJson} from 'type-fest'; + +import {BroadcastCacheUpdateOptions} from 'workbox-broadcast-update/BroadcastCacheUpdate'; +import {GoogleAnalyticsInitializeOptions} from 'workbox-google-analytics/initialize'; +import {HTTPMethod} from 'workbox-routing/utils/constants'; +import {QueueOptions} from 'workbox-background-sync/Queue'; +import {RouteHandler, RouteMatchCallback} from 'workbox-core/types'; +import {CacheableResponseOptions} from 'workbox-cacheable-response/CacheableResponse'; +import {ExpirationPluginOptions} from 'workbox-expiration/ExpirationPlugin'; +import {WorkboxPlugin} from 'workbox-core/types'; + +export interface ManifestEntry { + integrity?: string; + revision: string | null; + url: string; +} + +type StrategyName = 'CacheFirst' | 'CacheOnly' | 'NetworkFirst' | 'NetworkOnly' | 'StaleWhileRevalidate'; + +export interface RuntimeCaching { + handler: RouteHandler | StrategyName; + method?: HTTPMethod; + options?: { + backgroundSync?: { + name: string; + options?: QueueOptions; + }; + broadcastUpdate?: { + // TODO: This option is ignored since we switched to using postMessage(). + // Remove it in the next major release. + channelName?: string; + options: BroadcastCacheUpdateOptions; + }; + cacheableResponse?: CacheableResponseOptions; + cacheName?: string | null; + expiration?: ExpirationPluginOptions; + networkTimeoutSeconds?: number; + plugins?: Array; + precacheFallback?: { + fallbackURL: string; + }; + fetchOptions?: RequestInit; + matchOptions?: CacheQueryOptions; + }; + urlPattern: RegExp | string | RouteMatchCallback; +} + +export interface ManifestTransformResult { + manifest: Array; + warnings?: Array; +} + +export type ManifestTransform = ( + manifestEntries: Array, + compilation?: unknown +) => Promise | ManifestTransformResult; + +export interface BasePartial { + additionalManifestEntries?: Array; + dontCacheBustURLsMatching?: RegExp; + manifestTransforms?: Array; + /** + * @default 2097152 + */ + maximumFileSizeToCacheInBytes?: number; + modifyURLPrefix?: { + [key: string]: string; + }; +} + +export interface GeneratePartial { + /** + * @default ["chrome >= 56"] + */ + babelPresetEnvTargets?: Array; + cacheId?: string | null; + /** + * @default false + */ + cleanupOutdatedCaches?: boolean; + /** + * @default false + */ + clientsClaim?: boolean; + directoryIndex?: string | null; + /** + * @default false + */ + disableDevLogs?: boolean; + ignoreURLParametersMatching?: Array; + importScripts?: Array; + /** + * @default false + */ + inlineWorkboxRuntime?: boolean; + /** + * @default "production" + */ + mode?: string | null; + /** + * @default null + */ + navigateFallback?: string | null; + navigateFallbackAllowlist?: Array; + navigateFallbackDenylist?: Array; + /** + * navigationPreload is only valid when runtimeCaching is configured. However, + * this can't be expressed via TypeScript, so it's enforced via runtime logic. + * @default false + */ + navigationPreload?: boolean; + /** + * @default false + */ + offlineGoogleAnalytics?: boolean | GoogleAnalyticsInitializeOptions; + runtimeCaching?: Array; + /** + * @default false + */ + skipWaiting?: boolean; + /** + * @default true + */ + sourcemap?: boolean; +} + +// This needs to be set when using GetManifest or InjectManifest, but is +// optional when using GenerateSW if runtimeCaching is also used. This is +// enforced via runtime validation, and needs to be documented. +export interface RequiredGlobDirectoryPartial { + globDirectory: string; +} + +export interface OptionalGlobDirectoryPartial { + globDirectory?: string; +} + +export interface GlobPartial { + /** + * @default true + */ + globFollow?: boolean; + /** + * @default ["**\/node_modules\/**\/*"] + */ + globIgnores?: Array; + /** + * @default ["**\/*.{js,css,html}"] + */ + globPatterns?: Array; + /** + * @default true + */ + globStrict?: boolean; + templatedURLs?: { + [key: string]: string | Array; + }; +} + +interface InjectPartial { + /** + * @default "self.__WB_MANIFEST" + */ + injectionPoint?: string; + swSrc: string; +} + +interface WebpackPartial { + chunks?: Array; + // We can't use the @default annotation here to assign the value via AJV, as + // an Array can't be serialized into JSON. + // The default value of [/\.map$/, /^manifest.*\.js$/] will be assigned by + // the validation function, and we need to reflect that in the docs. + exclude?: Array boolean)>; + excludeChunks?: Array; + include?: Array boolean)>; + mode?: string | null; +} + +export interface RequiredSWDestPartial { + swDest: string; +} + +interface WebpackGenerateSWPartial { + importScriptsViaChunks?: Array; + /** + * @default "service-worker.js" + */ + swDest?: string; +} + +interface WebpackInjectManifestPartial { + /** + * @default true + */ + compileSrc?: boolean; + // This doesn't have a hardcoded default value; instead, the default will be + // set at runtime to the swSrc basename, with the hardcoded extension .js. + swDest?: string; + // This can only be set if compileSrc is true, but that restriction can't be + // represented in TypeScript. It's enforced via custom runtime validation + // logic and needs to be documented. + webpackCompilationPlugins?: Array; +} + +export type GenerateSWOptions = BasePartial & GlobPartial & GeneratePartial & + RequiredSWDestPartial & OptionalGlobDirectoryPartial + +export type GetManifestOptions = BasePartial & GlobPartial & + RequiredGlobDirectoryPartial; + +export type InjectManifestOptions = BasePartial & GlobPartial & InjectPartial & + RequiredSWDestPartial & RequiredGlobDirectoryPartial; + +export type WebpackGenerateSWOptions = BasePartial & WebpackPartial & + GeneratePartial & WebpackGenerateSWPartial; + +export type WebpackInjectManifestOptions = BasePartial & WebpackPartial & + InjectPartial & WebpackInjectManifestPartial; + +export interface GetManifestResult { + count: number; + manifestEntries: Array; + size: number; + warnings: Array; +} + +export type BuildResult = Omit & { + filePaths: Array; +}; + +export interface FileDetails { + file: string; + hash: string; + size: number; +} + +export type BuildType = 'dev' | 'prod'; + +export interface WorkboxPackageJSON extends PackageJson { + workbox?: { + browserNamespace?: string; + packageType?: string; + prodOnly?: boolean; + }; +} diff --git a/packages/workbox-build/tsconfig.json b/packages/workbox-build/tsconfig.json new file mode 100644 index 000000000..ef0306782 --- /dev/null +++ b/packages/workbox-build/tsconfig.json @@ -0,0 +1,27 @@ +{ + "extends": "../../tsconfig", + "compilerOptions": { + "esModuleInterop": true, + "module": "CommonJS", + "outDir": "./build", + "resolveJsonModule": true, + "rootDir": "./src", + "target": "ES2018", + "tsBuildInfoFile": "./tsconfig.tsbuildinfo" + }, + "files": [ + "src/cdn-details.json" + ], + "include": [ + "src/**/*.ts", + "src/schema/*.json" + ], + "references": [ + {"path": "../workbox-background-sync/"}, + {"path": "../workbox-broadcast-update/"}, + {"path": "../workbox-cacheable-response/"}, + {"path": "../workbox-core/"}, + {"path": "../workbox-expiration/"}, + {"path": "../workbox-google-analytics/"} + ] +} diff --git a/packages/workbox-cli/src/app.ts b/packages/workbox-cli/src/app.ts index 6502039d2..dc142efea 100644 --- a/packages/workbox-cli/src/app.ts +++ b/packages/workbox-cli/src/app.ts @@ -7,12 +7,12 @@ */ import {oneLine as ol} from 'common-tags'; -import * as assert from 'assert'; -import * as meow from 'meow'; -import * as prettyBytes from 'pretty-bytes'; -import * as upath from 'upath'; -import * as watch from 'glob-watcher'; -import * as workboxBuild from 'workbox-build'; +import assert from 'assert'; +import GlobWatcher from 'glob-watcher'; +import meow from 'meow'; +import prettyBytes from 'pretty-bytes'; +import upath from 'upath'; +import workboxBuild from 'workbox-build'; import {constants} from './lib/constants.js'; import {errors} from './lib/errors.js'; @@ -22,51 +22,38 @@ import {runWizard} from './lib/run-wizard.js'; import {SupportedFlags} from './bin.js' interface BuildCommand { - command: 'generateSW'|'injectManifest'; + command: 'generateSW' | 'injectManifest'; config: any; watch: boolean; } - /** * Runs the specified build command with the provided configuration. * * @param {Object} options */ async function runBuildCommand({command, config, watch}: BuildCommand) { - try { - const {count, filePaths, size, warnings} = - await workboxBuild[command](config); - + const {count, filePaths, size, warnings} = await workboxBuild[command](config); - for (const warning of warnings) { - logger.warn(warning); - } + for (const warning of warnings) { + logger.warn(warning); + } - if (filePaths.length === 1) { - logger.log(`The service worker file was written to ${config.swDest}`); - } else { - const message = filePaths - .sort() - .map((filePath) => ` • ${filePath}`) - .join(`\n`); - logger.log(`The service worker files were written to:\n${message}`); - } + if (filePaths.length === 1) { + logger.log(`The service worker file was written to ${config.swDest}`); + } else { + const message = filePaths + .sort() + .map((filePath) => ` • ${filePath}`) + .join(`\n`); + logger.log(`The service worker files were written to:\n${message}`); + } - logger.log(`The service worker will precache ${count} URLs, ` + - `totaling ${prettyBytes(size)}.`); + logger.log(`The service worker will precache ${count} URLs, ` + + `totaling ${prettyBytes(size)}.`); - if (watch) { - logger.log(`\nWatching for changes...`); - } - } catch (error) { - // See https://github.com/hapijs/joi/blob/v11.3.4/API.md#errors - if (typeof error.annotate === 'function') { - throw new Error( - `${errors['config-validation-failed']}\n${error.annotate()}`); - } - logger.error(errors['workbox-build-runtime-error']); - throw error; + if (watch) { + logger.log(`\nWatching for changes...`); } } @@ -114,7 +101,7 @@ export const app = async (params: meow.Result) => { // Determine whether we're in --watch mode, or one-off mode. if (params.flags && params.flags.watch) { - const options: watch.WatchOptions = {ignoreInitial: false} + const options: GlobWatcher.WatchOptions = {ignoreInitial: false}; if (config.globIgnores) { options.ignored = config.globIgnores; } @@ -123,7 +110,7 @@ export const app = async (params: meow.Result) => { } if (config.globPatterns) { - watch(config.globPatterns, options, + GlobWatcher(config.globPatterns, options, () => runBuildCommand({command, config, watch: true})); } diff --git a/packages/workbox-cli/src/bin.ts b/packages/workbox-cli/src/bin.ts index 8c3cbfe62..0018d710c 100755 --- a/packages/workbox-cli/src/bin.ts +++ b/packages/workbox-cli/src/bin.ts @@ -9,8 +9,8 @@ */ -import * as meow from 'meow' -import * as updateNotifier from 'update-notifier'; +import meow from 'meow' +import updateNotifier from 'update-notifier'; import {app} from './app'; import {cleanupStackTrace} from './lib/cleanup-stack-trace.js'; diff --git a/packages/workbox-cli/src/lib/logger.ts b/packages/workbox-cli/src/lib/logger.ts index 866e66a5f..d4fb4b2da 100644 --- a/packages/workbox-cli/src/lib/logger.ts +++ b/packages/workbox-cli/src/lib/logger.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import * as chalk from 'chalk'; +import chalk from 'chalk'; export const logger = { debug: (...args: string[]) => console.log(chalk.gray(...args)), diff --git a/packages/workbox-cli/src/lib/questions/ask-config-location.ts b/packages/workbox-cli/src/lib/questions/ask-config-location.ts index 696b34b83..a4a762246 100644 --- a/packages/workbox-cli/src/lib/questions/ask-config-location.ts +++ b/packages/workbox-cli/src/lib/questions/ask-config-location.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import * as assert from 'assert'; +import assert from 'assert'; import {prompt} from 'inquirer'; import {oneLine as ol} from 'common-tags'; diff --git a/packages/workbox-cli/src/lib/questions/ask-extensions-to-cache.ts b/packages/workbox-cli/src/lib/questions/ask-extensions-to-cache.ts index 15d5ca63f..e895b55b2 100644 --- a/packages/workbox-cli/src/lib/questions/ask-extensions-to-cache.ts +++ b/packages/workbox-cli/src/lib/questions/ask-extensions-to-cache.ts @@ -6,11 +6,11 @@ https://opensource.org/licenses/MIT. */ -import * as assert from 'assert'; +import assert from 'assert'; import {prompt} from 'inquirer'; -import * as glob from 'glob'; -import * as ora from 'ora'; -import * as upath from 'upath'; +import glob from 'glob'; +import ora from 'ora'; +import upath from 'upath'; import {errors} from '../errors'; import {constants} from '../constants'; diff --git a/packages/workbox-cli/src/lib/questions/ask-root-of-web-app.ts b/packages/workbox-cli/src/lib/questions/ask-root-of-web-app.ts index ecd0a6d22..1c893aac9 100644 --- a/packages/workbox-cli/src/lib/questions/ask-root-of-web-app.ts +++ b/packages/workbox-cli/src/lib/questions/ask-root-of-web-app.ts @@ -6,9 +6,9 @@ https://opensource.org/licenses/MIT. */ -import * as assert from 'assert'; -import * as fse from 'fs-extra'; -import * as glob from 'glob'; +import assert from 'assert'; +import fse from 'fs-extra'; +import glob from 'glob'; import {prompt, Separator} from 'inquirer'; import {oneLine as ol} from 'common-tags'; diff --git a/packages/workbox-cli/src/lib/questions/ask-start_url-query-params.ts b/packages/workbox-cli/src/lib/questions/ask-start_url-query-params.ts index d9c90fcd6..6090a9f6c 100644 --- a/packages/workbox-cli/src/lib/questions/ask-start_url-query-params.ts +++ b/packages/workbox-cli/src/lib/questions/ask-start_url-query-params.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import * as assert from 'assert'; +import assert from 'assert'; import {prompt} from 'inquirer'; import {oneLine as ol} from 'common-tags'; diff --git a/packages/workbox-cli/src/lib/questions/ask-sw-dest.ts b/packages/workbox-cli/src/lib/questions/ask-sw-dest.ts index c5145438a..714a1b2b6 100644 --- a/packages/workbox-cli/src/lib/questions/ask-sw-dest.ts +++ b/packages/workbox-cli/src/lib/questions/ask-sw-dest.ts @@ -6,9 +6,9 @@ https://opensource.org/licenses/MIT. */ -import * as assert from 'assert'; +import assert from 'assert'; import {prompt} from 'inquirer'; -import * as upath from 'upath'; +import upath from 'upath'; import {errors} from '../errors'; diff --git a/packages/workbox-cli/src/lib/run-wizard.ts b/packages/workbox-cli/src/lib/run-wizard.ts index 41a3f2a0b..cdb2d9b8d 100644 --- a/packages/workbox-cli/src/lib/run-wizard.ts +++ b/packages/workbox-cli/src/lib/run-wizard.ts @@ -6,9 +6,9 @@ https://opensource.org/licenses/MIT. */ -import * as fse from 'fs-extra'; +import fse from 'fs-extra'; import {oneLine as ol} from 'common-tags'; -import * as stringifyObject from 'stringify-object'; +import stringifyObject from 'stringify-object'; import {askQuestions} from './questions/ask-questions'; import {logger} from './logger'; diff --git a/packages/workbox-cli/tsconfig.json b/packages/workbox-cli/tsconfig.json index 9b3e27942..b44a33c62 100644 --- a/packages/workbox-cli/tsconfig.json +++ b/packages/workbox-cli/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../tsconfig", "compilerOptions": { + "esModuleInterop": true, "module": "CommonJS", "outDir": "build", "rootDir": "src", @@ -9,5 +10,8 @@ }, "include": [ "src/**/*.ts" + ], + "references": [ + {"path": "../workbox-build/"}, ] } diff --git a/packages/workbox-core/src/_private/Deferred.ts b/packages/workbox-core/src/_private/Deferred.ts index f32512807..b93608ff6 100644 --- a/packages/workbox-core/src/_private/Deferred.ts +++ b/packages/workbox-core/src/_private/Deferred.ts @@ -19,7 +19,7 @@ import '../_version.js'; */ class Deferred { promise: Promise; - resolve!: (value?: T) => void; + resolve!: (value: T) => void; reject!: (reason?: any) => void; /** diff --git a/packages/workbox-expiration/src/ExpirationPlugin.ts b/packages/workbox-expiration/src/ExpirationPlugin.ts index 3a486b7a3..713b26e5b 100644 --- a/packages/workbox-expiration/src/ExpirationPlugin.ts +++ b/packages/workbox-expiration/src/ExpirationPlugin.ts @@ -19,6 +19,13 @@ import {CacheExpiration} from './CacheExpiration.js'; import './_version.js'; +export interface ExpirationPluginOptions { + maxEntries?: number; + maxAgeSeconds?: number; + matchOptions?: CacheQueryOptions; + purgeOnQuotaError?: boolean; +} + /** * This plugin can be used in a `workbox-strategy` to regularly enforce a * limit on the age and / or the number of cached requests. @@ -58,12 +65,7 @@ class ExpirationPlugin implements WorkboxPlugin { * @param {boolean} [config.purgeOnQuotaError] Whether to opt this cache in to * automatic deletion if the available storage quota has been exceeded. */ - constructor(config: { - maxEntries?: number; - maxAgeSeconds?: number; - matchOptions?: CacheQueryOptions; - purgeOnQuotaError?: boolean; - } = {}) { + constructor(config: ExpirationPluginOptions = {}) { if (process.env.NODE_ENV !== 'production') { if (!(config.maxEntries || config.maxAgeSeconds)) { throw new WorkboxError('max-entries-or-age-required', { diff --git a/packages/workbox-google-analytics/src/initialize.ts b/packages/workbox-google-analytics/src/initialize.ts index 3a13d5bc1..eac351466 100644 --- a/packages/workbox-google-analytics/src/initialize.ts +++ b/packages/workbox-google-analytics/src/initialize.ts @@ -29,7 +29,7 @@ import { import './_version.js'; -interface GoogleAnalyticsInitializeOptions { +export interface GoogleAnalyticsInitializeOptions { cacheName?: string; parameterOverrides?: {[paramName: string]: string}; hitFilter?: (params: URLSearchParams) => void; diff --git a/packages/workbox-strategies/src/StrategyHandler.ts b/packages/workbox-strategies/src/StrategyHandler.ts index 4d8974881..26b1d05c6 100644 --- a/packages/workbox-strategies/src/StrategyHandler.ts +++ b/packages/workbox-strategies/src/StrategyHandler.ts @@ -520,7 +520,7 @@ class StrategyHandler { * `waitUntil()` promises. */ destroy() { - this._handlerDeferred.resolve(); + this._handlerDeferred.resolve(null); } /** diff --git a/packages/workbox-webpack-plugin/src/generate-sw.js b/packages/workbox-webpack-plugin/src/generate-sw.js index 7395a5669..d68ec5aba 100644 --- a/packages/workbox-webpack-plugin/src/generate-sw.js +++ b/packages/workbox-webpack-plugin/src/generate-sw.js @@ -6,14 +6,13 @@ https://opensource.org/licenses/MIT. */ -const bundle = require('workbox-build/build/lib/bundle'); -const populateSWTemplate = +const {validateWebpackGenerateSWOptions} = + require('workbox-build/build/lib/validate-options'); +const {bundle} = require('workbox-build/build/lib/bundle'); +const {populateSWTemplate} = require('workbox-build/build/lib/populate-sw-template'); const prettyBytes = require('pretty-bytes'); -const validate = require('workbox-build/build/lib/validate-options'); const webpack = require('webpack'); -const webpackGenerateSWSchema = require( - 'workbox-build/build/options/schema/webpack-generate-sw'); const getScriptFilesForChunks = require('./lib/get-script-files-for-chunks'); const getManifestEntriesFromCompilation = @@ -272,7 +271,7 @@ class GenerateSW { // emit might be called multiple times; instead of modifying this.config, // use a validated copy. // See https://github.com/GoogleChrome/workbox/issues/2158 - config = validate(this.config, webpackGenerateSWSchema); + config = validateWebpackGenerateSWOptions(this.config); } catch (error) { throw new Error(`Please check your ${this.constructor.name} plugin ` + `configuration:\n${error.message}`); diff --git a/packages/workbox-webpack-plugin/src/inject-manifest.js b/packages/workbox-webpack-plugin/src/inject-manifest.js index bfc665cfe..ba2a18007 100644 --- a/packages/workbox-webpack-plugin/src/inject-manifest.js +++ b/packages/workbox-webpack-plugin/src/inject-manifest.js @@ -6,16 +6,15 @@ https://opensource.org/licenses/MIT. */ -const escapeRegexp = require('workbox-build/build/lib/escape-regexp'); +const {escapeRegExp} = require('workbox-build/build/lib/escape-regexp'); +const {replaceAndUpdateSourceMap} = + require('workbox-build/build/lib/replace-and-update-source-map'); +const {validateWebpackInjectManifestOptions} = + require('workbox-build/build/lib/validate-options'); const prettyBytes = require('pretty-bytes'); -const replaceAndUpdateSourceMap = require( - 'workbox-build/build/lib/replace-and-update-source-map'); const stringify = require('fast-json-stable-stringify'); const upath = require('upath'); -const validate = require('workbox-build/build/lib/validate-options'); const webpack = require('webpack'); -const webpackInjectManifestSchema = require( - 'workbox-build/build/options/schema/webpack-inject-manifest'); const getManifestEntriesFromCompilation = require('./lib/get-manifest-entries-from-compilation'); @@ -137,6 +136,8 @@ class InjectManifest { // there take precedence over derived properties from the compiler. this.config = Object.assign({ mode: compiler.mode, + // Use swSrc with a hardcoded .js extension, in case swSrc is a .ts file. + swDest: upath.parse(this.config.swSrc).name + '.js', }, this.config); } @@ -250,7 +251,7 @@ class InjectManifest { */ async handleMake(compilation, parentCompiler) { try { - this.config = validate(this.config, webpackInjectManifestSchema); + this.config = validateWebpackInjectManifestOptions(this.config); } catch (error) { throw new Error(`Please check your ${this.constructor.name} plugin ` + `configuration:\n${error.message}`); @@ -263,6 +264,13 @@ class InjectManifest { await this.performChildCompilation(compilation, parentCompiler); } else { this.addSrcToAssets(compilation, parentCompiler); + // This used to be a fatal error, but just warn at runtime because we + // can't validate it easily. + if (Array.isArray(this.config.webpackCompilationPlugins) && + this.config.webpackCompilationPlugins.length > 0) { + compilation.warnings.push(new Error('compileSrc is false, so the ' + + 'webpackCompilationPlugins option will be ignored.')); + } } } @@ -301,7 +309,7 @@ class InjectManifest { const swAsset = compilation.getAsset(config.swDest); const swAssetString = swAsset.source.source(); - const globalRegexp = new RegExp(escapeRegexp(config.injectionPoint), 'g'); + const globalRegexp = new RegExp(escapeRegExp(config.injectionPoint), 'g'); const injectionResults = swAssetString.match(globalRegexp); if (!injectionResults) { diff --git a/packages/workbox-webpack-plugin/src/lib/get-manifest-entries-from-compilation.js b/packages/workbox-webpack-plugin/src/lib/get-manifest-entries-from-compilation.js index 254150b53..a911230b7 100644 --- a/packages/workbox-webpack-plugin/src/lib/get-manifest-entries-from-compilation.js +++ b/packages/workbox-webpack-plugin/src/lib/get-manifest-entries-from-compilation.js @@ -7,7 +7,8 @@ */ const {matchPart} = require('webpack').ModuleFilenameHelpers; -const transformManifest = require('workbox-build/build/lib/transform-manifest'); +const {transformManifest} = + require('workbox-build/build/lib/transform-manifest'); const getAssetHash = require('./get-asset-hash'); const resolveWebpackURL = require('./resolve-webpack-url'); diff --git a/packages/workbox-window/src/Workbox.ts b/packages/workbox-window/src/Workbox.ts index b018b4c2c..71558e621 100644 --- a/packages/workbox-window/src/Workbox.ts +++ b/packages/workbox-window/src/Workbox.ts @@ -519,7 +519,7 @@ class Workbox extends WorkboxEventTarget { if (process.env.NODE_ENV !== 'production') { logger.log('Registered service worker now controlling this page.'); } - this._controllingDeferred.resolve(sw); + this._controllingDeferred.resolve(sw!); } } diff --git a/test/workbox-build/node/dependency-check.js b/test/workbox-build/node/dependency-check.js index 5308fb930..4b2814234 100644 --- a/test/workbox-build/node/dependency-check.js +++ b/test/workbox-build/node/dependency-check.js @@ -21,6 +21,7 @@ describe(`[workbox-build] Test Dependencies`, function() { ignoreMatches: [ '@babel/preset-env', '@babel/runtime', + 'type-fest', 'workbox-background-sync', 'workbox-broadcast-update', 'workbox-cacheable-response', diff --git a/test/workbox-build/node/generate-sw.js b/test/workbox-build/node/generate-sw.js index 866daefca..b19cfd68b 100644 --- a/test/workbox-build/node/generate-sw.js +++ b/test/workbox-build/node/generate-sw.js @@ -6,16 +6,21 @@ https://opensource.org/licenses/MIT. */ -const expect = require('chai').expect; +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); const fse = require('fs-extra'); const upath = require('upath'); const tempy = require('tempy'); +const {errors} = require('../../../packages/workbox-build/build/lib/errors'); +const {generateSW} = require('../../../packages/workbox-build/build/generate-sw'); +const {WorkboxConfigError} = require('../../../packages/workbox-build/build/lib/validate-options'); const confirmDirectoryContains = require('../../../infra/testing/confirm-directory-contains'); -const errors = require('../../../packages/workbox-build/src/lib/errors'); -const generateSW = require('../../../packages/workbox-build/src/generate-sw'); const validateServiceWorkerRuntime = require('../../../infra/testing/validator/service-worker-runtime'); +chai.use(chaiAsPromised); +const {expect} = chai; + describe(`[workbox-build] generate-sw.js (End to End)`, function() { const GLOB_DIR = upath.join(__dirname, '..', 'static', 'example-project-1'); const BASE_OPTIONS = { @@ -64,64 +69,46 @@ describe(`[workbox-build] generate-sw.js (End to End)`, function() { describe('[workbox-build] required parameters', function() { for (const requiredParam of REQUIRED_PARAMS) { - it(`should reject with a ValidationError when '${requiredParam}' is missing`, async function() { + it(`should fail validation when '${requiredParam}' is missing`, async function() { const options = Object.assign({}, BASE_OPTIONS); delete options[requiredParam]; - try { - await generateSW(options); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.name).to.eql('ValidationError', error.message); - expect(error.details[0].context.key).to.eql(requiredParam); - } + await expect(generateSW(options)).to.eventually.be.rejectedWith( + WorkboxConfigError, requiredParam); }); } }); describe('[workbox-build] unsupported parameters', function() { for (const unsupportedParam of UNSUPPORTED_PARAMS) { - it(`should reject with a ValidationError when '${unsupportedParam}' is present`, async function() { + it(`should fail validation when '${unsupportedParam}' is present`, async function() { const options = Object.assign({}, BASE_OPTIONS); options[unsupportedParam] = unsupportedParam; - try { - await generateSW(options); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.name).to.eql('ValidationError', error.message); - expect(error.details[0].context.key).to.eql(unsupportedParam); - } + await expect(generateSW(options)).to.eventually.be.rejectedWith( + WorkboxConfigError, unsupportedParam); }); } }); describe('[workbox-build] invalid parameter values', function() { for (const param of SUPPORTED_PARAMS) { - it(`should reject with a ValidationError when '${param}' is null`, async function() { + it(`should fail validation when '${param}' is an unexpected value`, async function() { const options = Object.assign({}, BASE_OPTIONS); - options[param] = null; - - try { - await generateSW(options); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.name).to.eql('ValidationError', error.message); - expect(error.details[0].context.key).to.eql(param); - } + options[param] = () => {}; + + await expect(generateSW(options)).to.eventually.be.rejectedWith( + WorkboxConfigError, param); }); } it(`should reject when there are no manifest entries or runtimeCaching`, async function() { const options = Object.assign({}, BASE_OPTIONS); - delete options.globDirectory; + // This temporary directory will be empty. + options.globDirectory = tempy.directory(); - try { - await generateSW(options); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.message).to.eql(errors['no-manifest-entries-or-runtime-caching']); - } + await expect(generateSW(options)).to.eventually.be.rejectedWith( + errors['no-manifest-entries-or-runtime-caching']); }); }); @@ -362,7 +349,10 @@ describe(`[workbox-build] generate-sw.js (End to End)`, function() { }, { url: 'webpackEntry.js', revision: /^[0-9a-f]{32}$/, - }, '/one', { + }, { + revision: null, + url: '/one', + }, { revision: null, url: '/two', }, { @@ -695,12 +685,8 @@ describe(`[workbox-build] generate-sw.js (End to End)`, function() { runtimeCaching: [{handler}], }); - try { - await generateSW(options); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.message).to.include(errors['urlPattern-is-required']); - } + await expect(generateSW(options)).to.eventually.be.rejectedWith( + WorkboxConfigError, 'urlPattern'); }); it(`should reject when 'handler' is missing from 'runtimeCaching'`, async function() { @@ -709,12 +695,8 @@ describe(`[workbox-build] generate-sw.js (End to End)`, function() { runtimeCaching: [{urlPattern}], }); - try { - await generateSW(options); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.message).to.include(errors['handler-is-required']); - } + await expect(generateSW(options)).to.eventually.be.rejectedWith( + WorkboxConfigError, 'handler'); }); it(`should reject when 'handler' is not a valid strategy name`, async function() { @@ -726,13 +708,8 @@ describe(`[workbox-build] generate-sw.js (End to End)`, function() { }], }); - try { - await generateSW(options); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.name).to.eql('ValidationError', error.message); - expect(error.details[0].context.key).to.eql('handler'); - } + await expect(generateSW(options)).to.eventually.be.rejectedWith( + WorkboxConfigError, 'handler'); }); // See https://github.com/GoogleChrome/workbox/issues/2078 @@ -940,12 +917,8 @@ describe(`[workbox-build] generate-sw.js (End to End)`, function() { swDest, }); - try { - await generateSW(options); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.message).to.include(errors['invalid-network-timeout-seconds']); - } + await expect(generateSW(options)).to.eventually.be.rejectedWith( + errors['invalid-network-timeout-seconds']); }); it(`should support 'networkTimeoutSeconds' when handler is 'NetworkFirst'`, async function() { @@ -1014,13 +987,8 @@ describe(`[workbox-build] generate-sw.js (End to End)`, function() { }], }); - try { - await generateSW(options); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.name).to.eql('ValidationError', error.message); - expect(error.details[0].context.main).to.eql('expiration'); - } + await expect(generateSW(options)).to.eventually.be.rejectedWith( + WorkboxConfigError, errors['cache-name-required']); }); it(`should ignore swDest and workbox-*.js when generating manifest entries`, async function() { @@ -1066,33 +1034,23 @@ describe(`[workbox-build] generate-sw.js (End to End)`, function() { }); describe(`[workbox-build] behavior with 'navigationPreload'`, function() { - it(`should reject with a ValidationError when 'navigationPreload' is true and 'runtimeCaching' is undefined`, async function() { + it(`should reject when 'navigationPreload' is true and 'runtimeCaching' is undefined`, async function() { const options = Object.assign({}, BASE_OPTIONS, { navigationPreload: true, }); - try { - await generateSW(options); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.name).to.eql('ValidationError', error.message); - expect(error.details[0].context.key).to.eql('runtimeCaching'); - } + await expect(generateSW(options)).to.eventually.be.rejectedWith( + WorkboxConfigError, errors['nav-preload-runtime-caching']); }); - it(`should reject with a ValidationError when 'navigationPreload' is true and 'runtimeCaching' is invalid`, async function() { + it(`should reject when 'navigationPreload' is true and 'runtimeCaching' is undefined`, async function() { const options = Object.assign({}, BASE_OPTIONS, { - runtimeCaching: 'invalid', + runtimeCaching: undefined, navigationPreload: true, }); - try { - await generateSW(options); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.name).to.eql('ValidationError', error.message); - expect(error.details[0].context.key).to.eql('runtimeCaching'); - } + await expect(generateSW(options)).to.eventually.be.rejectedWith( + WorkboxConfigError, errors['nav-preload-runtime-caching']); }); it(`should generate when 'navigationPreload' is true and 'runtimeCaching' is valid`, async function() { diff --git a/test/workbox-build/node/get-manifest.js b/test/workbox-build/node/get-manifest.js index 855dee7c3..6c8cd0f26 100644 --- a/test/workbox-build/node/get-manifest.js +++ b/test/workbox-build/node/get-manifest.js @@ -7,15 +7,18 @@ */ const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); const chaiMatchPattern = require('chai-match-pattern'); const fse = require('fs-extra'); const upath = require('upath'); const tempy = require('tempy'); +chai.use(chaiAsPromised); chai.use(chaiMatchPattern); const {expect} = chai; -const getManifest = require('../../../packages/workbox-build/src/get-manifest'); +const {getManifest} = require('../../../packages/workbox-build/build/get-manifest'); +const {WorkboxConfigError} = require('../../../packages/workbox-build/build/lib/validate-options'); describe(`[workbox-build] get-manifest.js (End to End)`, function() { const SRC_DIR = upath.join(__dirname, '..', 'static', 'example-project-1'); @@ -53,34 +56,24 @@ describe(`[workbox-build] get-manifest.js (End to End)`, function() { describe('[workbox-build] unsupported parameters', function() { for (const unsupportedParam of UNSUPPORTED_PARAMS) { - it(`should reject with a ValidationError when '${unsupportedParam}' is present`, async function() { + it(`should fail validation when '${unsupportedParam}' is present`, async function() { const options = Object.assign({}, BASE_OPTIONS); options[unsupportedParam] = unsupportedParam; - try { - await getManifest(options); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.name).to.eql('ValidationError'); - expect(error.details[0].context.key).to.eql(unsupportedParam); - } + await expect(getManifest(options)).to.eventually.be.rejectedWith( + WorkboxConfigError, unsupportedParam); }); } }); describe('[workbox-build] invalid parameter values', function() { for (const param of SUPPORTED_PARAMS) { - it(`should reject with a ValidationError when '${param}' is null`, async function() { + it(`should fail validation when '${param}' is an unexpected value`, async function() { const options = Object.assign({}, BASE_OPTIONS); - options[param] = null; - - try { - await getManifest(options); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.name).to.eql('ValidationError'); - expect(error.details[0].context.key).to.eql(param); - } + options[param] = () => {}; + + await expect(getManifest(options)).to.eventually.be.rejectedWith( + WorkboxConfigError, param); }); } }); @@ -330,13 +323,8 @@ describe(`[workbox-build] get-manifest.js (End to End)`, function() { [option]: value, }); - try { - await getManifest(options); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.name).to.eql('ValidationError'); - expect(error.details[0].context.key).to.eql(option); - } + await expect(getManifest(options)).to.eventually.be.rejectedWith( + WorkboxConfigError, option); }); } }); diff --git a/test/workbox-build/node/inject-manifest.js b/test/workbox-build/node/inject-manifest.js index 7ecd4f5ef..1af047230 100644 --- a/test/workbox-build/node/inject-manifest.js +++ b/test/workbox-build/node/inject-manifest.js @@ -6,15 +6,20 @@ https://opensource.org/licenses/MIT. */ -const expect = require('chai').expect; +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); const fse = require('fs-extra'); const upath = require('upath'); const tempy = require('tempy'); -const errors = require('../../../packages/workbox-build/src/lib/errors'); -const injectManifest = require('../../../packages/workbox-build/src/inject-manifest'); +const {errors} = require('../../../packages/workbox-build/build/lib/errors'); +const {injectManifest} = require('../../../packages/workbox-build/build/inject-manifest'); +const {WorkboxConfigError} = require('../../../packages/workbox-build/build/lib/validate-options'); const validateServiceWorkerRuntime = require('../../../infra/testing/validator/service-worker-runtime'); +chai.use(chaiAsPromised); +const {expect} = chai; + describe(`[workbox-build] inject-manifest.js (End to End)`, function() { const GLOB_DIR = upath.join(__dirname, '..', 'static', 'example-project-1'); const SW_SRC_DIR = upath.join(__dirname, '..', 'static', 'sw-injections'); @@ -57,51 +62,36 @@ describe(`[workbox-build] inject-manifest.js (End to End)`, function() { describe('[workbox-build] required parameters', function() { for (const requiredParam of REQUIRED_PARAMS) { - it(`should reject with a ValidationError when '${requiredParam}' is missing`, async function() { + it(`should reject when '${requiredParam}' is missing`, async function() { const options = Object.assign({}, BASE_OPTIONS); delete options[requiredParam]; - try { - await injectManifest(options); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.name).to.eql('ValidationError'); - expect(error.details[0].context.key).to.eql(requiredParam); - } + await expect(injectManifest(options)).to.eventually.be.rejectedWith( + WorkboxConfigError, requiredParam); }); } }); describe('[workbox-build] unsupported parameters', function() { for (const unsupportedParam of UNSUPPORTED_PARAMS) { - it(`should reject with a ValidationError when '${unsupportedParam}' is present`, async function() { + it(`should reject when '${unsupportedParam}' is present`, async function() { const options = Object.assign({}, BASE_OPTIONS); options[unsupportedParam] = unsupportedParam; - try { - await injectManifest(options); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.name).to.eql('ValidationError'); - expect(error.details[0].context.key).to.eql(unsupportedParam); - } + await expect(injectManifest(options)).to.eventually.be.rejectedWith( + WorkboxConfigError, unsupportedParam); }); } }); describe('[workbox-build] invalid parameter values', function() { for (const param of SUPPORTED_PARAMS) { - it(`should reject with a ValidationError when '${param}' is null`, async function() { + it(`should reject when '${param}' is null`, async function() { const options = Object.assign({}, BASE_OPTIONS); options[param] = null; - try { - await injectManifest(options); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.name).to.eql('ValidationError'); - expect(error.details[0].context.key).to.eql(param); - } + await expect(injectManifest(options)).to.eventually.be.rejectedWith( + WorkboxConfigError, param); }); } }); @@ -112,12 +102,8 @@ describe(`[workbox-build] inject-manifest.js (End to End)`, function() { swSrc: 'DOES_NOT_EXIST', }); - try { - await injectManifest(options); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.message).to.have.string(errors['invalid-sw-src']); - } + await expect(injectManifest(options)).to.eventually.be.rejectedWith( + errors['invalid-sw-src']); }); it(`should throw the expected error when there is no match for 'injectionPoint'`, async function() { @@ -125,12 +111,8 @@ describe(`[workbox-build] inject-manifest.js (End to End)`, function() { swSrc: upath.join(SW_SRC_DIR, 'bad-no-injection.js'), }); - try { - await injectManifest(options); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.message).to.have.string(errors['injection-point-not-found']); - } + await expect(injectManifest(options)).to.eventually.be.rejectedWith( + errors['injection-point-not-found']); }); it(`should throw the expected error when there is no match for 'injectionPoint' and 'swSrc' and 'swDest' are the same`, async function() { @@ -140,12 +122,8 @@ describe(`[workbox-build] inject-manifest.js (End to End)`, function() { swDest: swFile, }); - try { - await injectManifest(options); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.message).to.have.string(errors['same-src-and-dest']); - } + await expect(injectManifest(options)).to.eventually.be.rejectedWith( + errors['same-src-and-dest']); }); it(`should throw the expected error when there are multiple matches for 'injectionPoint'`, async function() { @@ -153,12 +131,8 @@ describe(`[workbox-build] inject-manifest.js (End to End)`, function() { swSrc: upath.join(SW_SRC_DIR, 'bad-multiple-injection.js'), }); - try { - await injectManifest(options); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.message).to.have.string(errors['multiple-injection-points']); - } + await expect(injectManifest(options)).to.eventually.be.rejectedWith( + errors['multiple-injection-points']); }); }); @@ -450,13 +424,8 @@ describe(`[workbox-build] inject-manifest.js (End to End)`, function() { [option]: value, }); - try { - await injectManifest(options); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.name).to.eql('ValidationError'); - expect(error.details[0].context.key).to.eql(option); - } + await expect(injectManifest(options)).to.eventually.be.rejectedWith( + WorkboxConfigError, option); }); } }); diff --git a/test/workbox-build/node/lib/additional-manifest-entries-transform.js b/test/workbox-build/node/lib/additional-manifest-entries-transform.js index 72b611577..67d0a82c2 100644 --- a/test/workbox-build/node/lib/additional-manifest-entries-transform.js +++ b/test/workbox-build/node/lib/additional-manifest-entries-transform.js @@ -8,10 +8,10 @@ const expect = require('chai').expect; -const errors = require('../../../../packages/workbox-build/src/lib/errors'); -const additionalManifestEntriesTransform = require('../../../../packages/workbox-build/src/lib/additional-manifest-entries-transform'); +const {errors} = require('../../../../packages/workbox-build/build/lib/errors'); +const {additionalManifestEntriesTransform} = require('../../../../packages/workbox-build/build/lib/additional-manifest-entries-transform'); -describe(`[workbox-build] lib/additional-manifest-entries-transform.js`, function() { +describe(`[workbox-build] lib/additional-manifest-entries-transform`, function() { function getManifest() { return [{ url: '/first', @@ -36,8 +36,8 @@ describe(`[workbox-build] lib/additional-manifest-entries-transform.js`, functio expect(transform(getManifest())).to.eql({ manifest: [ {url: '/first', revision: null}, - {url: '/second', revision: null}, - {url: '/third', revision: null}, + {url: '/second', size: 0, revision: null}, + {url: '/third', size: 0, revision: null}, ], warnings: [], }); @@ -52,8 +52,8 @@ describe(`[workbox-build] lib/additional-manifest-entries-transform.js`, functio expect(transform(getManifest())).to.eql({ manifest: [ {url: '/first', revision: null}, - '/second', - {url: '/third'}, + {url: '/second', size: 0, revision: null}, + {url: '/third', size: 0}, ], warnings: [errors['string-entry-warning'] + '\n - /second\n - /third\n'], }); diff --git a/test/workbox-build/node/lib/bundle.js b/test/workbox-build/node/lib/bundle.js index 3252a6702..c30360de1 100644 --- a/test/workbox-build/node/lib/bundle.js +++ b/test/workbox-build/node/lib/bundle.js @@ -10,8 +10,8 @@ const expect = require('chai').expect; const proxyquire = require('proxyquire'); const sinon = require('sinon'); -describe(`[workbox-build] lib/bundle.js`, function() { - const MODULE_PATH = '../../../../packages/workbox-build/src/lib/bundle'; +describe(`[workbox-build] lib/bundle`, function() { + const MODULE_PATH = '../../../../packages/workbox-build/build/lib/bundle'; let bundle; let stubs; @@ -19,16 +19,16 @@ describe(`[workbox-build] lib/bundle.js`, function() { const rollupStub = { generate: sinon.stub().resolves({output: [{ fileName: 'asset-filename', - isAsset: true, + type: 'asset', source: 'asset-source', }, { code: 'chunk1-code', fileName: 'chunk1-filename', - isAsset: false, + type: 'chunk', }, { code: 'chunk2-code', fileName: 'chunk2-filename', - isAsset: false, + type: 'chunk', map: 'sourcemap-contents', }]}), }; @@ -40,6 +40,7 @@ describe(`[workbox-build] lib/bundle.js`, function() { writeFile: sinon.stub().resolves(), }, 'upath': { + format: sinon.stub().callsFake((args) => `${args.dir}${args.base}`), parse: sinon.stub().returns({base: 'sw.js', dir: ''}), }, 'tempy': { @@ -47,7 +48,9 @@ describe(`[workbox-build] lib/bundle.js`, function() { }, '@rollup/plugin-node-resolve': sinon.stub(), '@rollup/plugin-replace': sinon.stub(), - '@rollup/plugin-babel': {babel: sinon.stub()}, + '@rollup/plugin-babel': { + babel: sinon.stub(), + }, 'rollup-plugin-terser': { terser: sinon.stub(), }, @@ -57,7 +60,7 @@ describe(`[workbox-build] lib/bundle.js`, function() { }, }; - bundle = proxyquire(MODULE_PATH, stubs); + bundle = proxyquire(MODULE_PATH, stubs).bundle; }); it(`should pass 'babelPresetEnvTargets' to @babel/preset-env`, async function() { @@ -67,17 +70,9 @@ describe(`[workbox-build] lib/bundle.js`, function() { babelPresetEnvTargets, }); - expect(stubs['@rollup/plugin-babel'].babel.args).to.eql([[{ - babelHelpers: 'bundled', - babelrc: false, - configFile: false, - presets: [[stubs['@babel/preset-env'], { - targets: { - browsers: babelPresetEnvTargets, - }, - loose: true, - }]], - }]]); + // This is ugly, but necessary due to the way babel() is configured. + const babelParams = stubs['@rollup/plugin-babel'].babel.args[0][0]; + expect(babelParams.presets[0][1].targets.browsers).to.eql(babelPresetEnvTargets); }); it(`should use loadz0r and configure manualChunks when 'inlineWorkboxRuntime' is false`, async function() { diff --git a/test/workbox-build/node/lib/cdn-utils.js b/test/workbox-build/node/lib/cdn-utils.js index b2d046de4..706f2417c 100644 --- a/test/workbox-build/node/lib/cdn-utils.js +++ b/test/workbox-build/node/lib/cdn-utils.js @@ -8,17 +8,12 @@ const expect = require('chai').expect; -const cdnUtils = require('../../../../packages/workbox-build/src/lib/cdn-utils'); -const errors = require('../../../../packages/workbox-build/src/lib/errors'); +const cdnUtils = require('../../../../packages/workbox-build/build/lib/cdn-utils'); +const {errors} = require('../../../../packages/workbox-build/build/lib/errors'); describe(`[workbox-build] lib/cdn-utils.js`, function() { const CDN_ORIGIN = 'https://storage.googleapis.com/workbox-cdn/releases'; - it(`getCDNOrigin() should return the expected base URL`, function() { - const url = cdnUtils.getCDNOrigin(); - expect(url).to.eql(CDN_ORIGIN); - }); - it(`getModuleURL() should throw when moduleName is undefined`, function() { expect( () => cdnUtils.getModuleURL(), diff --git a/test/workbox-build/node/lib/copy-workbox-libraries.js b/test/workbox-build/node/lib/copy-workbox-libraries.js index e34b85184..f1ab5051c 100644 --- a/test/workbox-build/node/lib/copy-workbox-libraries.js +++ b/test/workbox-build/node/lib/copy-workbox-libraries.js @@ -11,18 +11,18 @@ const upath = require('upath'); const proxyquire = require('proxyquire'); const sinon = require('sinon'); -const errors = require('../../../../packages/workbox-build/src/lib/errors'); +const {errors} = require('../../../../packages/workbox-build/build/lib/errors'); describe(`[workbox-build] lib/copy-workbox-libraries.js`, function() { - const MODULE_PATH = '../../../../packages/workbox-build/src/lib/copy-workbox-libraries'; + const MODULE_PATH = '../../../../packages/workbox-build/build/lib/copy-workbox-libraries'; const ABSOLUTE_DEST_DIRECTORY = upath.join('/', 'test-dir'); const RELATIVE_DEST_DIRECTORY = upath.join('.', 'test-dir'); it(`should reject with an error when the copy fails`, async function() { - const copyWorkboxLibraries = proxyquire(MODULE_PATH, { + const {copyWorkboxLibraries} = proxyquire(MODULE_PATH, { 'fs-extra': { ensureDir: sinon.stub().resolves(), - copy: sinon.stub().rejects(), + copy: sinon.stub().rejects('INJECTED_ERROR'), }, }); @@ -39,7 +39,7 @@ describe(`[workbox-build] lib/copy-workbox-libraries.js`, function() { const copyStub = sinon.stub().resolves(); const ensureDirStub = sinon.stub().resolves(); - const copyWorkboxLibraries = proxyquire(MODULE_PATH, { + const {copyWorkboxLibraries} = proxyquire(MODULE_PATH, { 'fs-extra': { copy: copyStub, ensureDir: ensureDirStub, diff --git a/test/workbox-build/node/lib/escape-regexp.js b/test/workbox-build/node/lib/escape-regexp.js index 7e5dc5e42..1aa342573 100644 --- a/test/workbox-build/node/lib/escape-regexp.js +++ b/test/workbox-build/node/lib/escape-regexp.js @@ -7,7 +7,7 @@ */ const expect = require('chai').expect; -const escapeRegexp = require('../../../../packages/workbox-build/src/lib/escape-regexp'); +const {escapeRegExp} = require('../../../../packages/workbox-build/build/lib/escape-regexp'); describe(`[workbox-build] lib/copy-workbox-libraries.js`, function() { const expectedValues = new Map([ @@ -19,7 +19,7 @@ describe(`[workbox-build] lib/copy-workbox-libraries.js`, function() { for (const [original, escaped] of expectedValues) { it(`should perform the expected escaping: ${original} => ${escaped}`, async function() { - expect(escapeRegexp(original)).to.eql(escaped); + expect(escapeRegExp(original)).to.eql(escaped); }); } }); diff --git a/test/workbox-build/node/lib/get-composite-details.js b/test/workbox-build/node/lib/get-composite-details.js index ee4d0f01b..71ea5360d 100644 --- a/test/workbox-build/node/lib/get-composite-details.js +++ b/test/workbox-build/node/lib/get-composite-details.js @@ -8,7 +8,7 @@ const expect = require('chai').expect; -const getCompositeDetails = require('../../../../packages/workbox-build/src/lib/get-composite-details'); +const {getCompositeDetails} = require('../../../../packages/workbox-build/build/lib/get-composite-details'); describe(`[workbox-build] lib/get-composite-details.js`, function() { const URL = '/test'; diff --git a/test/workbox-build/node/lib/get-file-details.js b/test/workbox-build/node/lib/get-file-details.js index 8cc7c5b57..9cea809c6 100644 --- a/test/workbox-build/node/lib/get-file-details.js +++ b/test/workbox-build/node/lib/get-file-details.js @@ -10,10 +10,10 @@ const expect = require('chai').expect; const upath = require('upath'); const proxyquire = require('proxyquire'); -const errors = require('../../../../packages/workbox-build/src/lib/errors'); +const {errors} = require('../../../../packages/workbox-build/build/lib/errors'); describe(`[workbox-build] lib/get-file-details.js`, function() { - const MODULE_PATH = '../../../../packages/workbox-build/src/lib/get-file-details'; + const MODULE_PATH = '../../../../packages/workbox-build/build/lib/get-file-details'; const GLOB_DIRECTORY = './'; const GLOB_PATTERN = 'file*'; const DIRECTORY = 'directory'; @@ -23,7 +23,7 @@ describe(`[workbox-build] lib/get-file-details.js`, function() { const HASH = 'example-hash'; it(`should throw when there's a glob.sync() error`, function() { - const getFileDetails = proxyquire(MODULE_PATH, { + const {getFileDetails} = proxyquire(MODULE_PATH, { glob: { sync: () => { throw new Error(); @@ -43,7 +43,7 @@ describe(`[workbox-build] lib/get-file-details.js`, function() { }); it(`should return a warning when the pattern doesn't match anything`, function() { - const getFileDetails = proxyquire(MODULE_PATH, { + const {getFileDetails} = proxyquire(MODULE_PATH, { glob: { sync: () => [], }, @@ -58,23 +58,27 @@ describe(`[workbox-build] lib/get-file-details.js`, function() { }); it(`should return array of file details, without null values`, function() { - const getFileDetails = proxyquire(MODULE_PATH, { + const {getFileDetails} = proxyquire(MODULE_PATH, { 'glob': { sync: () => { return [FILE1, FILE2, DIRECTORY]; }, }, - './get-file-size': (value) => { - if (upath.normalize(value) === upath.normalize(DIRECTORY)) { - return null; - } - return SIZE; + './get-file-size': { + getFileSize: (value) => { + if (upath.normalize(value) === upath.normalize(DIRECTORY)) { + return null; + } + return SIZE; + }, }, - './get-file-hash': (value) => { - if (upath.normalize(value) === upath.normalize(DIRECTORY)) { - throw new Error(`getFileHash(${DIRECTORY}) shouldn't have been called.`); - } - return HASH; + './get-file-hash': { + getFileHash: (value) => { + if (upath.normalize(value) === upath.normalize(DIRECTORY)) { + throw new Error(`getFileHash(${DIRECTORY}) shouldn't have been called.`); + } + return HASH; + }, }, }); @@ -83,7 +87,7 @@ describe(`[workbox-build] lib/get-file-details.js`, function() { globPattern: GLOB_PATTERN, }); - expect(warning).to.be.undefined; + expect(warning).to.eql(''); expect(globbedFileDetails).to.deep.equal([{ file: FILE1, hash: HASH, diff --git a/test/workbox-build/node/lib/get-file-hash.js b/test/workbox-build/node/lib/get-file-hash.js index cc4934111..d5954ba55 100644 --- a/test/workbox-build/node/lib/get-file-hash.js +++ b/test/workbox-build/node/lib/get-file-hash.js @@ -9,15 +9,15 @@ const expect = require('chai').expect; const proxyquire = require('proxyquire'); -const errors = require('../../../../packages/workbox-build/src/lib/errors'); +const {errors} = require('../../../../packages/workbox-build/build/lib/errors'); describe(`[workbox-build] lib/get-file-hash.js`, function() { - const MODULE_PATH = '../../../../packages/workbox-build/src/lib/get-file-hash'; + const MODULE_PATH = '../../../../packages/workbox-build/build/lib/get-file-hash'; const FILE = 'file.txt'; it(`should throw when there's a fs.readFileSync() error`, function() { - const getFileHash = proxyquire(MODULE_PATH, { - fs: { + const {getFileHash} = proxyquire(MODULE_PATH, { + 'fs-extra': { readFileSync: () => { throw new Error(); }, @@ -36,8 +36,8 @@ describe(`[workbox-build] lib/get-file-hash.js`, function() { const buffer = Buffer.alloc(10); const hashForBuffer = 'a63c90cc3684ad8b0a2176a6a8fe9005'; - const getFileHash = proxyquire(MODULE_PATH, { - fs: { + const {getFileHash} = proxyquire(MODULE_PATH, { + 'fs-extra': { readFileSync: (file) => { if (file !== FILE) { throw new Error(`Unexpected file name: ${file}`); diff --git a/test/workbox-build/node/lib/get-file-manifest-entries.js b/test/workbox-build/node/lib/get-file-manifest-entries.js index cf2521b36..bc04b7bc8 100644 --- a/test/workbox-build/node/lib/get-file-manifest-entries.js +++ b/test/workbox-build/node/lib/get-file-manifest-entries.js @@ -9,10 +9,10 @@ const expect = require('chai').expect; const proxyquire = require('proxyquire'); -const errors = require('../../../../packages/workbox-build/src/lib/errors'); +const {errors} = require('../../../../packages/workbox-build/build/lib/errors'); describe(`[workbox-build] Test getFileManifestEntries`, function() { - const MODULE_PATH = '../../../../packages/workbox-build/src/lib/get-file-manifest-entries'; + const MODULE_PATH = '../../../../packages/workbox-build/build/lib/get-file-manifest-entries'; const GLOB_DIRECTORY = './'; const GLOB_PATTERNS = ['invalid*']; const FILE = { @@ -22,7 +22,7 @@ describe(`[workbox-build] Test getFileManifestEntries`, function() { }; it(`should return empty info when neither globDirectory nor templatedURLs are provided`, async function() { - const getFileManifestEntries = require(MODULE_PATH); + const {getFileManifestEntries} = require(MODULE_PATH); const {count, size, manifestEntries} = await getFileManifestEntries({}); @@ -32,12 +32,14 @@ describe(`[workbox-build] Test getFileManifestEntries`, function() { }); it(`should not return the same file twice`, async function() { - const getFileManifestEntries = proxyquire(MODULE_PATH, { - './get-file-details': () => { - return { - globbedFileDetails: [FILE, FILE], - warning: undefined, - }; + const {getFileManifestEntries} = proxyquire(MODULE_PATH, { + './get-file-details': { + getFileDetails: () => { + return { + globbedFileDetails: [FILE, FILE], + warning: undefined, + }; + }, }, }); @@ -55,12 +57,14 @@ describe(`[workbox-build] Test getFileManifestEntries`, function() { }); it(`should throw when a templatedURL matches a globbed file`, async function() { - const getFileManifestEntries = proxyquire(MODULE_PATH, { - './get-file-details': () => { - return { - globbedFileDetails: [FILE], - warning: undefined, - }; + const {getFileManifestEntries} = proxyquire(MODULE_PATH, { + './get-file-details': { + getFileDetails: () => { + return { + globbedFileDetails: [FILE], + warning: undefined, + }; + }, }, }); @@ -80,9 +84,11 @@ describe(`[workbox-build] Test getFileManifestEntries`, function() { it(`should treat an exception thrown by getFileDetails() as a warning message`, async function() { const warningMessage = 'test warning'; - const getFileManifestEntries = proxyquire(MODULE_PATH, { - './get-file-details': () => { - throw new Error(warningMessage); + const {getFileManifestEntries} = proxyquire(MODULE_PATH, { + './get-file-details': { + getFileDetails: () => { + throw new Error(warningMessage); + }, }, }); @@ -98,10 +104,11 @@ describe(`[workbox-build] Test getFileManifestEntries`, function() { }); it(`should throw when a templatedURL contains a pattern that doesn't match anything`, async function() { - const getFileManifestEntries = require(MODULE_PATH); + const {getFileManifestEntries} = require(MODULE_PATH); try { await getFileManifestEntries({ + globDirectory: GLOB_DIRECTORY, templatedURLs: { [FILE.file]: GLOB_PATTERNS, }, @@ -117,12 +124,14 @@ describe(`[workbox-build] Test getFileManifestEntries`, function() { const url2 = '/path/to/url2'; const stringValue = 'string'; - const getFileManifestEntries = proxyquire(MODULE_PATH, { - './get-file-details': () => { - return { - globbedFileDetails: [FILE], - warning: undefined, - }; + const {getFileManifestEntries} = proxyquire(MODULE_PATH, { + './get-file-details': { + getFileDetails: () => { + return { + globbedFileDetails: [FILE], + warning: undefined, + }; + }, }, }); diff --git a/test/workbox-build/node/lib/get-file-size.js b/test/workbox-build/node/lib/get-file-size.js index 99cbe1c71..b1b7ef490 100644 --- a/test/workbox-build/node/lib/get-file-size.js +++ b/test/workbox-build/node/lib/get-file-size.js @@ -9,15 +9,15 @@ const expect = require('chai').expect; const proxyquire = require('proxyquire'); -const errors = require('../../../../packages/workbox-build/src/lib/errors.js'); +const {errors} = require('../../../../packages/workbox-build/build/lib/errors.js'); describe(`[workbox-build] lib/get-file-size.js`, function() { - const MODULE_PATH = '../../../../packages/workbox-build/src/lib/get-file-size'; + const MODULE_PATH = '../../../../packages/workbox-build/build/lib/get-file-size'; const FILE = 'file.txt'; it(`should throw when fs.statSync() fails`, function() { - const getFileSize = proxyquire(MODULE_PATH, { - fs: { + const {getFileSize} = proxyquire(MODULE_PATH, { + 'fs-extra': { statSync: () => { throw new Error(); }, @@ -33,8 +33,8 @@ describe(`[workbox-build] lib/get-file-size.js`, function() { }); it(`should return null for non-files`, function() { - const getFileSize = proxyquire(MODULE_PATH, { - fs: { + const {getFileSize} = proxyquire(MODULE_PATH, { + 'fs-extra': { statSync: () => { return { isFile: () => false, @@ -50,8 +50,8 @@ describe(`[workbox-build] lib/get-file-size.js`, function() { it(`should return the expected file size`, function() { const expectedSize = 1234; - const getFileSize = proxyquire(MODULE_PATH, { - fs: { + const {getFileSize} = proxyquire(MODULE_PATH, { + 'fs-extra': { statSync: () => { return { isFile: () => true, diff --git a/test/workbox-build/node/lib/get-string-details.js b/test/workbox-build/node/lib/get-string-details.js index 4d336d5db..6e0d326cc 100644 --- a/test/workbox-build/node/lib/get-string-details.js +++ b/test/workbox-build/node/lib/get-string-details.js @@ -8,7 +8,7 @@ const expect = require('chai').expect; -const getStringDetails = require('../../../../packages/workbox-build/src/lib/get-string-details'); +const {getStringDetails} = require('../../../../packages/workbox-build/build/lib/get-string-details'); describe(`[workbox-build] lib/get-string-details.js`, function() { it(`should return the expected details`, function() { diff --git a/test/workbox-build/node/lib/get-string-hash.js b/test/workbox-build/node/lib/get-string-hash.js index c251ba085..7c4826235 100644 --- a/test/workbox-build/node/lib/get-string-hash.js +++ b/test/workbox-build/node/lib/get-string-hash.js @@ -8,7 +8,7 @@ const expect = require('chai').expect; -const getStringHash = require('../../../../packages/workbox-build/src/lib/get-string-hash'); +const {getStringHash} = require('../../../../packages/workbox-build/build/lib/get-string-hash'); describe(`[workbox-build] lib/get-string-hash.js`, function() { it(`should return the expected hashes`, function() { diff --git a/test/workbox-build/node/lib/modify-url-prefix-transform.js b/test/workbox-build/node/lib/modify-url-prefix-transform.js index 7d6f16726..068f11406 100644 --- a/test/workbox-build/node/lib/modify-url-prefix-transform.js +++ b/test/workbox-build/node/lib/modify-url-prefix-transform.js @@ -8,8 +8,8 @@ const expect = require('chai').expect; -const errors = require('../../../../packages/workbox-build/src/lib/errors'); -const modifyURLPrefix = require('../../../../packages/workbox-build/src/lib/modify-url-prefix-transform'); +const {errors} = require('../../../../packages/workbox-build/build/lib/errors'); +const {modifyURLPrefixTransform} = require('../../../../packages/workbox-build/build/lib/modify-url-prefix-transform'); describe(`[workbox-build] lib/modify-url-prefix-transform.js`, function() { function getManifest() { @@ -35,7 +35,7 @@ describe(`[workbox-build] lib/modify-url-prefix-transform.js`, function() { '/example-2/multi-section/1234': '/example-2-altered/5678', }; - const transform = modifyURLPrefix(modifications); + const transform = modifyURLPrefixTransform(modifications); for (const badInput of badInputs) { expect( () => transform([{url: badInput}]), @@ -43,7 +43,7 @@ describe(`[workbox-build] lib/modify-url-prefix-transform.js`, function() { } }); - it(`should handle bad modifyURLPrefix input`, function() { + it(`should handle bad modifyURLPrefixTransform input`, function() { const badInputs = [ null, undefined, @@ -58,7 +58,7 @@ describe(`[workbox-build] lib/modify-url-prefix-transform.js`, function() { for (const badInput of badInputs) { expect( - () => modifyURLPrefix(badInput), + () => modifyURLPrefixTransform(badInput), ).to.throw(errors['modify-url-prefix-bad-prefixes']); } }); @@ -68,7 +68,7 @@ describe(`[workbox-build] lib/modify-url-prefix-transform.js`, function() { '/first-match': '', }; - const transform = modifyURLPrefix(modifications); + const transform = modifyURLPrefixTransform(modifications); expect(transform(getManifest())).to.eql({manifest: [{ url: '/12345/hello', }, { @@ -81,7 +81,7 @@ describe(`[workbox-build] lib/modify-url-prefix-transform.js`, function() { '': '/public', }; - const transform = modifyURLPrefix(modifications); + const transform = modifyURLPrefixTransform(modifications); expect(transform(getManifest())).to.eql({manifest: [{ url: '/public/first-match/12345/hello', }, { @@ -95,7 +95,7 @@ describe(`[workbox-build] lib/modify-url-prefix-transform.js`, function() { '/second-match': '/third-match', }; - const transform = modifyURLPrefix(modifications); + const transform = modifyURLPrefixTransform(modifications); expect(transform(getManifest())).to.eql({manifest: [{ url: '/second-match/12345/hello', }, { @@ -108,7 +108,7 @@ describe(`[workbox-build] lib/modify-url-prefix-transform.js`, function() { '/hello': '/altered', }; - const transform = modifyURLPrefix(modifications); + const transform = modifyURLPrefixTransform(modifications); expect(transform(getManifest())).to.eql({manifest: getManifest()}); }); }); diff --git a/test/workbox-build/node/lib/module-registry.js b/test/workbox-build/node/lib/module-registry.js index e0ae29ae6..0a665515d 100644 --- a/test/workbox-build/node/lib/module-registry.js +++ b/test/workbox-build/node/lib/module-registry.js @@ -7,9 +7,10 @@ */ const expect = require('chai').expect; -const ModuleRegistry = require('../../../../packages/workbox-build/src/lib/module-registry'); const upath = require('upath'); +const {ModuleRegistry} = require('../../../../packages/workbox-build/build/lib/module-registry'); + describe(`[workbox-build] lib/module-registry.js`, function() { let moduleRegistry; // We can't use proxyquire to override require.resolve(), so let's get the diff --git a/test/workbox-build/node/lib/no-revision-for-urls-matching-transform.js b/test/workbox-build/node/lib/no-revision-for-urls-matching-transform.js index a54762676..a536000ff 100644 --- a/test/workbox-build/node/lib/no-revision-for-urls-matching-transform.js +++ b/test/workbox-build/node/lib/no-revision-for-urls-matching-transform.js @@ -8,8 +8,8 @@ const expect = require('chai').expect; -const errors = require('../../../../packages/workbox-build/src/lib/errors'); -const noRevisionForURLsMatching = require('../../../../packages/workbox-build/src/lib/no-revision-for-urls-matching-transform'); +const {errors} = require('../../../../packages/workbox-build/build/lib/errors'); +const {noRevisionForURLsMatchingTransform} = require('../../../../packages/workbox-build/build/lib/no-revision-for-urls-matching-transform'); describe(`[workbox-build] lib/no-revision-for-urls-matching-transform.js`, function() { const MANIFEST = [{ @@ -32,7 +32,7 @@ describe(`[workbox-build] lib/no-revision-for-urls-matching-transform.js`, funct [], ]; - const transform = noRevisionForURLsMatching(/ignored/); + const transform = noRevisionForURLsMatchingTransform(/ignored/); for (const badInput of badInputs) { expect( () => transform([{url: badInput}]), @@ -55,13 +55,13 @@ describe(`[workbox-build] lib/no-revision-for-urls-matching-transform.js`, funct for (const badInput of badInputs) { expect( - () => noRevisionForURLsMatching(badInput), + () => noRevisionForURLsMatchingTransform(badInput), ).to.throw(errors['invalid-dont-cache-bust']); } }); it(`should set revision info to null in a single matching entry`, function() { - const transform = noRevisionForURLsMatching(/first-match/); + const transform = noRevisionForURLsMatchingTransform(/first-match/); expect(transform(MANIFEST)).to.eql({manifest: [{ url: '/first-match/12345/hello', revision: null, @@ -74,7 +74,7 @@ describe(`[workbox-build] lib/no-revision-for-urls-matching-transform.js`, funct }); it(`should set revision info to null in multiple matching entries`, function() { - const transform = noRevisionForURLsMatching(/12345/); + const transform = noRevisionForURLsMatchingTransform(/12345/); expect(transform(MANIFEST)).to.eql({manifest: [{ url: '/first-match/12345/hello', revision: null, @@ -88,7 +88,7 @@ describe(`[workbox-build] lib/no-revision-for-urls-matching-transform.js`, funct }); it(`should do nothing when there's a match for an entry without a revision`, function() { - const transform = noRevisionForURLsMatching(/third-match/); + const transform = noRevisionForURLsMatchingTransform(/third-match/); expect(transform(MANIFEST)).to.eql({manifest: MANIFEST}); }); }); diff --git a/test/workbox-build/node/lib/populate-sw-template.js b/test/workbox-build/node/lib/populate-sw-template.js index 1c61f01a2..b64f877e3 100644 --- a/test/workbox-build/node/lib/populate-sw-template.js +++ b/test/workbox-build/node/lib/populate-sw-template.js @@ -10,15 +10,15 @@ const expect = require('chai').expect; const proxyquire = require('proxyquire'); const sinon = require('sinon'); -const errors = require('../../../../packages/workbox-build/src/lib/errors'); +const {errors} = require('../../../../packages/workbox-build/build/lib/errors'); describe(`[workbox-build] lib/populate-sw-template.js`, function() { - const MODULE_PATH = '../../../../packages/workbox-build/src/lib/populate-sw-template'; + const MODULE_PATH = '../../../../packages/workbox-build/build/lib/populate-sw-template'; it(`should throw an error if templating fails`, function() { const manifestEntries = ['ignored']; - const populateSWTemplate = proxyquire(MODULE_PATH, { + const {populateSWTemplate} = proxyquire(MODULE_PATH, { 'lodash/template': () => { throw new Error(); }, @@ -33,7 +33,7 @@ describe(`[workbox-build] lib/populate-sw-template.js`, function() { }); it(`should throw an error if both manifestEntries and runtimeCaching are empty`, function() { - const populateSWTemplate = proxyquire(MODULE_PATH, { + const {populateSWTemplate} = proxyquire(MODULE_PATH, { 'lodash/template': () => {}, }); @@ -53,10 +53,12 @@ describe(`[workbox-build] lib/populate-sw-template.js`, function() { const innerStub = sinon.stub().returns(''); const outerStub = sinon.stub().returns(innerStub); - const populateSWTemplate = proxyquire(MODULE_PATH, { + const {populateSWTemplate} = proxyquire(MODULE_PATH, { 'lodash/template': outerStub, - './runtime-caching-converter': () => runtimeCachingPlaceholder, - '../templates/sw-template': swTemplate, + './runtime-caching-converter': { + runtimeCachingConverter: () => runtimeCachingPlaceholder, + }, + '../templates/sw-template': {swTemplate}, }); populateSWTemplate({manifestEntries}); @@ -113,10 +115,12 @@ describe(`[workbox-build] lib/populate-sw-template.js`, function() { // We need to stub out both of those steps to test the full flow. const templatePopulationStub = sinon.stub().returns(''); const templateCreationStub = sinon.stub().returns(templatePopulationStub); - const populateSWTemplate = proxyquire(MODULE_PATH, { + const {populateSWTemplate} = proxyquire(MODULE_PATH, { 'lodash/template': templateCreationStub, - './runtime-caching-converter': () => runtimeCachingPlaceholder, - '../templates/sw-template': swTemplate, + './runtime-caching-converter': { + runtimeCachingConverter: () => runtimeCachingPlaceholder, + }, + '../templates/sw-template': {swTemplate}, }); populateSWTemplate({ @@ -180,10 +184,12 @@ describe(`[workbox-build] lib/populate-sw-template.js`, function() { const innerStub = sinon.stub().returns(''); const outerStub = sinon.stub().returns(innerStub); - const populateSWTemplate = proxyquire(MODULE_PATH, { + const {populateSWTemplate} = proxyquire(MODULE_PATH, { 'lodash/template': outerStub, - './runtime-caching-converter': () => runtimeCachingPlaceholder, - '../templates/sw-template': swTemplate, + './runtime-caching-converter': { + runtimeCachingConverter: () => runtimeCachingPlaceholder, + }, + '../templates/sw-template': {swTemplate}, }); populateSWTemplate({manifestEntries, offlineGoogleAnalytics}); diff --git a/test/workbox-build/node/lib/replace-and-update-source-map.js b/test/workbox-build/node/lib/replace-and-update-source-map.js index 9247e17aa..4cbcf451b 100644 --- a/test/workbox-build/node/lib/replace-and-update-source-map.js +++ b/test/workbox-build/node/lib/replace-and-update-source-map.js @@ -8,7 +8,7 @@ const expect = require('chai').expect; -const replaceAndUpdateSourceMap = require('../../../../packages/workbox-build/src/lib/replace-and-update-source-map'); +const {replaceAndUpdateSourceMap} = require('../../../../packages/workbox-build/build/lib/replace-and-update-source-map'); describe(`[workbox-build] lib/replace-and-update-source-map`, function() { // Test case borrowed from https://github.com/Rich-Harris/magic-string/blob/a312519cfe9caa78ade7f09cc2b07459d3d17f4d/test/MagicString.js#L225 diff --git a/test/workbox-build/node/lib/runtime-caching-converter.js b/test/workbox-build/node/lib/runtime-caching-converter.js index 9b7834c47..c2cdef607 100644 --- a/test/workbox-build/node/lib/runtime-caching-converter.js +++ b/test/workbox-build/node/lib/runtime-caching-converter.js @@ -10,9 +10,9 @@ const expect = require('chai').expect; const sinon = require('sinon'); const vm = require('vm'); -const errors = require('../../../../packages/workbox-build/src/lib/errors'); -const ModuleRegistry = require('../../../../packages/workbox-build/src/lib/module-registry'); -const runtimeCachingConverter = require('../../../../packages/workbox-build/src/lib/runtime-caching-converter'); +const {errors} = require('../../../../packages/workbox-build/build/lib/errors'); +const {ModuleRegistry} = require('../../../../packages/workbox-build/build/lib/module-registry'); +const {runtimeCachingConverter} = require('../../../../packages/workbox-build/build/lib/runtime-caching-converter'); const moduleRegistry = new ModuleRegistry(); diff --git a/test/workbox-build/node/lib/transform-manifest.js b/test/workbox-build/node/lib/transform-manifest.js index e6d8ca226..720eed4b2 100644 --- a/test/workbox-build/node/lib/transform-manifest.js +++ b/test/workbox-build/node/lib/transform-manifest.js @@ -8,7 +8,7 @@ const expect = require('chai').expect; -const transformManifest = require('../../../../packages/workbox-build/src/lib/transform-manifest'); +const {transformManifest} = require('../../../../packages/workbox-build/build/lib/transform-manifest'); describe(`[workbox-build] lib/transform-manifest.js`, function() { const MAXIMUM_FILE_SIZE = 1234; diff --git a/test/workbox-build/node/lib/validate-options.js b/test/workbox-build/node/lib/validate-options.js index 028b32d76..e0b12b22f 100644 --- a/test/workbox-build/node/lib/validate-options.js +++ b/test/workbox-build/node/lib/validate-options.js @@ -7,35 +7,74 @@ */ const expect = require('chai').expect; +const proxyquire = require('proxyquire'); +const sinon = require('sinon'); -const validateOptions = require('../../../../packages/workbox-build/src/lib/validate-options'); - -describe(`[workbox-build] entry-points/options/validate.js`, function() { - const testOptions = { - ignored: 'test', - }; - - it(`should throw when the call to schema.validate() returns an error`, function() { - const error = 'dummy error'; - const schema = { - validate: (options) => { - expect(options).to.eql(testOptions); - return {error}; - }, - }; - - expect(() => validateOptions(testOptions, schema)).to.throw(error); - }); - - it(`should pass through the value when the call to schema.validate() doesn't return an error`, function() { - const schema = { - validate: (options) => { - expect(options).to.eql(testOptions); - return {value: options}; - }, - }; - - const value = validateOptions(testOptions, schema); - expect(value).to.eql(testOptions); - }); +class AJVFailsValidation { + compile() { + const stub = sinon.stub().returns(false); + stub.errors = []; + return stub; + } + addKeyword() { + // no-op + } +} + +class AJVPassesValidation { + compile() { + return sinon.stub().returns(true); + } + addKeyword() { + // no-op + } +} + +// The integration tests will exercise the actual validation logic. +describe(`[workbox-build] entry-points/options/validate-options.js`, function() { + const MODULE_PATH = '../../../../packages/workbox-build/build/lib/validate-options'; + const testCases = [ + 'validateGenerateSWOptions', + 'validateGetManifestOptions', + 'validateInjectManifestOptions', + ]; + + for (const func of testCases) { + it(`${func}() should throw when validation fails`, function() { + const validateOptions = proxyquire(MODULE_PATH, { + 'ajv': { + default: AJVFailsValidation, + }, + '@apideck/better-ajv-errors': { + betterAjvErrors: sinon.stub().returns([{ + message: 'message1', + path: 'path1', + suggestion: 'suggestion1', + }, { + message: 'message2', + path: 'path2', + suggestion: 'suggestion2', + }]), + }, + }); + + expect(() => validateOptions[func]()).to.throw( + validateOptions.WorkboxConfigError, + `[path1] message1. suggestion1\n\n[path2] message2. suggestion2`, + ); + }); + + it(`${func}() should not throw when validation passes`, function() { + const validateOptions = proxyquire(MODULE_PATH, { + 'ajv': { + default: AJVPassesValidation, + }, + }); + + const defaultOptions = validateOptions[func]({ + globDirectory: '.', + }); + expect(defaultOptions).to.be.an('object'); + }); + } }); diff --git a/test/workbox-build/node/lib/write-sw-using-default-template.js b/test/workbox-build/node/lib/write-sw-using-default-template.js index 0f0a2f47e..67b18b9f8 100644 --- a/test/workbox-build/node/lib/write-sw-using-default-template.js +++ b/test/workbox-build/node/lib/write-sw-using-default-template.js @@ -11,13 +11,13 @@ const upath = require('upath'); const proxyquire = require('proxyquire'); const sinon = require('sinon'); -const errors = require('../../../../packages/workbox-build/src/lib/errors'); +const {errors} = require('../../../../packages/workbox-build/build/lib/errors'); describe(`[workbox-build] lib/write-sw-using-default-template.js`, function() { - const MODULE_PATH = '../../../../packages/workbox-build/src/lib/write-sw-using-default-template'; + const MODULE_PATH = '../../../../packages/workbox-build/build/lib/write-sw-using-default-template'; it(`should reject with an error when fs-extra.mkdirp() fails`, async function() { - const writeSWUsingDefaultTemplate = proxyquire(MODULE_PATH, { + const {writeSWUsingDefaultTemplate} = proxyquire(MODULE_PATH, { 'upath': { dirname: () => 'ignored', }, @@ -35,7 +35,7 @@ describe(`[workbox-build] lib/write-sw-using-default-template.js`, function() { }); it(`should reject with an error when fs-extra.writeFile() fails`, async function() { - const writeSWUsingDefaultTemplate = proxyquire(MODULE_PATH, { + const {writeSWUsingDefaultTemplate} = proxyquire(MODULE_PATH, { 'upath': { dirname: () => 'ignored', }, @@ -57,7 +57,7 @@ describe(`[workbox-build] lib/write-sw-using-default-template.js`, function() { const eisdirError = new Error(); eisdirError.code = 'EISDIR'; - const writeSWUsingDefaultTemplate = proxyquire(MODULE_PATH, { + const {writeSWUsingDefaultTemplate} = proxyquire(MODULE_PATH, { 'upath': { dirname: () => 'ignored', }, @@ -66,10 +66,12 @@ describe(`[workbox-build] lib/write-sw-using-default-template.js`, function() { readFile: () => Promise.resolve(), writeFile: () => Promise.reject(eisdirError), }, - './bundle': async () => [{ - name: 'ignored', - contents: 'ignored', - }], + './bundle': { + bundle: async () => [{ + name: 'ignored', + contents: 'ignored', + }], + }, }); try { @@ -89,7 +91,7 @@ describe(`[workbox-build] lib/write-sw-using-default-template.js`, function() { const contents2 = 'contents2'; const writeFileStub = sinon.stub().returns(Promise.resolve()); - const writeSWUsingDefaultTemplate = proxyquire(MODULE_PATH, { + const {writeSWUsingDefaultTemplate} = proxyquire(MODULE_PATH, { 'fs-extra': { mkdirp: (path) => { expect(path).to.eql(expectedPath); @@ -97,14 +99,18 @@ describe(`[workbox-build] lib/write-sw-using-default-template.js`, function() { readFile: () => Promise.resolve(), writeFile: writeFileStub, }, - './bundle': async () => [{ - name: upath.join(expectedPath, file1), - contents: contents1, - }, { - name: upath.join(expectedPath, file2), - contents: contents2, - }], - './populate-sw-template': () => '', + './bundle': { + bundle: async () => [{ + name: upath.join(expectedPath, file1), + contents: contents1, + }, { + name: upath.join(expectedPath, file2), + contents: contents2, + }], + }, + './populate-sw-template': { + populateSWTemplate: () => '', + }, }); await writeSWUsingDefaultTemplate({swDest}); diff --git a/test/workbox-cli/node/app.js b/test/workbox-cli/node/app.js index e281b2ec4..f1029e3a3 100644 --- a/test/workbox-cli/node/app.js +++ b/test/workbox-cli/node/app.js @@ -6,20 +6,28 @@ https://opensource.org/licenses/MIT. */ -const expect = require('chai').expect; -const upath = require('upath'); +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); const proxyquire = require('proxyquire'); const sinon = require('sinon'); +const upath = require('upath'); const {constants} = require('../../../packages/workbox-cli/build/lib/constants'); const {errors} = require('../../../packages/workbox-cli/build/lib/errors'); +const {WorkboxConfigError} = require('../../../packages/workbox-build/build/lib/validate-options'); + +chai.use(chaiAsPromised); +const {expect} = chai; describe(`[workbox-cli] app.js`, function() { const MODULE_PATH = '../../../packages/workbox-cli/build/app'; const PROXIED_CONFIG_FILE = upath.resolve(process.cwd(), '/will/be/proxied'); const PROXIED_DEST_DIR = upath.resolve(process.cwd(), 'build'); const PROXIED_ERROR = new Error('proxied error message'); - const PROXIED_CONFIG = {}; + const PROXIED_CONFIG = { + globDirectory: '.', + swDest: 'sw.js', + }; const INVALID_CONFIG_FILE = upath.resolve(process.cwd(), upath.join('does', 'not', 'exist')); const UNKNOWN_COMMAND = 'unknown-command'; const WORKBOX_BUILD_COMMANDS = [ @@ -43,31 +51,18 @@ describe(`[workbox-cli] app.js`, function() { const {app} = require(MODULE_PATH); it(`should reject when both parameters are missing`, async function() { - try { - await app(); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.message).to.have.string(errors['missing-input']); - } + await expect(app()).to.eventually.be.rejectedWith( + errors['missing-input']); }); it(`should reject when the command is unknown and options is present`, async function() { - try { - await app({input: [UNKNOWN_COMMAND, PROXIED_CONFIG_FILE]}); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.message).to.have.string(errors['unknown-command']); - expect(error.message).to.have.string(UNKNOWN_COMMAND); - } + await expect(app({input: [UNKNOWN_COMMAND, PROXIED_CONFIG_FILE]})).to + .eventually.be.rejectedWith(errors['unknown-command']); }); it(`should reject when the command parameter is copyLibraries and options is missing`, async function() { - try { - await app({input: ['copyLibraries']}); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.message).to.have.string(errors['missing-dest-dir-param']); - } + await expect(app({input: ['copyLibraries']})).to + .eventually.be.rejectedWith(errors['missing-dest-dir-param']); }); for (const command of WORKBOX_BUILD_COMMANDS) { @@ -105,10 +100,13 @@ describe(`[workbox-cli] app.js`, function() { ]; for (const config of badConfigs) { - it(`should reject with a validation error when workbox-build.${command}(${JSON.stringify(config)}) is called`, async function() { + it(`should reject with a WorkboxConfigError when workbox-build.${command}(${JSON.stringify(config)}) is called`, async function() { const {app} = proxyquire(MODULE_PATH, { './lib/logger': { - logger: {log: sinon.stub()}, + logger: { + error: sinon.stub(), + log: sinon.stub(), + }, }, './lib/read-config': { readConfig: (options) => { @@ -118,22 +116,16 @@ describe(`[workbox-cli] app.js`, function() { }, }); - try { - await app({input: [command, PROXIED_CONFIG_FILE]}); - throw new Error('Unexpected success.'); - } catch (error) { - expect(error.message).to.have.string(errors['config-validation-failed']); - } + await expect(app({input: [command, PROXIED_CONFIG_FILE]})).to + .eventually.be.rejectedWith(WorkboxConfigError); }); } it(`should reject with a generic runtime error when the workbox-build.${command}() rejects for any other reason`, async function() { - const loggerErrorStub = sinon.stub(); const {app} = proxyquire(MODULE_PATH, { './lib/logger': { logger: { log: sinon.stub(), - error: loggerErrorStub, }, }, './lib/read-config': { @@ -143,23 +135,17 @@ describe(`[workbox-cli] app.js`, function() { }, }, 'workbox-build': { - [command]: (config) => { - expect(config).to.eql(PROXIED_CONFIG); - throw PROXIED_ERROR; + default: { + [command]: (config) => { + expect(config).to.eql(PROXIED_CONFIG); + throw PROXIED_ERROR; + }, }, }, }); - try { - await app({input: [command, PROXIED_CONFIG_FILE]}); - throw new Error('Unexpected success.'); - } catch (error) { - expect(loggerErrorStub.calledOnce).to.be.true; - expect( - loggerErrorStub.alwaysCalledWithExactly(errors['workbox-build-runtime-error']), - ).to.be.true; - expect(error).to.eql(PROXIED_ERROR); - } + await expect(app({input: [command, PROXIED_CONFIG_FILE]})).to + .eventually.be.rejectedWith(PROXIED_ERROR); }); } }); @@ -181,8 +167,10 @@ describe(`[workbox-cli] app.js`, function() { }, }, 'workbox-build': { - [command]: () => { - return WORKBOX_BUILD_NO_WARNINGS_RETURN_VALUE; + default: { + [command]: () => { + return WORKBOX_BUILD_NO_WARNINGS_RETURN_VALUE; + }, }, }, }); @@ -208,8 +196,10 @@ describe(`[workbox-cli] app.js`, function() { }, }, 'workbox-build': { - [command]: () => { - return WORKBOX_BUILD_WITH_WARNINGS_RETURN_VALUE; + default: { + [command]: () => { + return WORKBOX_BUILD_WITH_WARNINGS_RETURN_VALUE; + }, }, }, }); @@ -232,11 +222,14 @@ describe(`[workbox-cli] app.js`, function() { './lib/logger': { logger: { log: loggerLogStub, + error: loggerLogStub, }, }, 'workbox-build': { - [command]: () => { - return WORKBOX_BUILD_NO_WARNINGS_RETURN_VALUE; + default: { + [command]: () => { + return WORKBOX_BUILD_NO_WARNINGS_RETURN_VALUE; + }, }, }, }); @@ -255,9 +248,11 @@ describe(`[workbox-cli] app.js`, function() { }, }, 'workbox-build': { - copyWorkboxLibraries: (destDir) => { - expect(destDir).to.eql(PROXIED_DEST_DIR); - return upath.join(destDir, 'workbox'); + default: { + copyWorkboxLibraries: (destDir) => { + expect(destDir).to.eql(PROXIED_DEST_DIR); + return upath.join(destDir, 'workbox'); + }, }, }, }); diff --git a/test/workbox-webpack-plugin/node/v4/generate-sw.js b/test/workbox-webpack-plugin/node/v4/generate-sw.js index 3191c3c9f..0bbcc3911 100644 --- a/test/workbox-webpack-plugin/node/v4/generate-sw.js +++ b/test/workbox-webpack-plugin/node/v4/generate-sw.js @@ -66,7 +66,7 @@ describe(`[workbox-webpack-plugin] GenerateSW with webpack v4`, function() { const statsJson = stats.toJson(); expect(statsJson.warnings).to.be.empty; expect(statsJson.errors).to.have.members([ - `Please check your GenerateSW plugin configuration:\n"invalid" is not allowed`, + `Please check your GenerateSW plugin configuration:\n[WebpackGenerateSW] 'invalid' property is not expected to be here. Did you mean property 'include'?`, ]); done(); diff --git a/test/workbox-webpack-plugin/node/v4/inject-manifest.js b/test/workbox-webpack-plugin/node/v4/inject-manifest.js index 93515a106..5def25d05 100644 --- a/test/workbox-webpack-plugin/node/v4/inject-manifest.js +++ b/test/workbox-webpack-plugin/node/v4/inject-manifest.js @@ -72,7 +72,7 @@ describe(`[workbox-webpack-plugin] InjectManifest with webpack v4`, function() { const statsJson = stats.toJson(); expect(statsJson.warnings).to.be.empty; expect(statsJson.errors).to.have.members([ - `Please check your InjectManifest plugin configuration:\n"invalid" is not allowed`, + `Please check your InjectManifest plugin configuration:\n[WebpackInjectManifest] 'invalid' property is not expected to be here. Did you mean property 'include'?`, ]); done(); @@ -1757,7 +1757,7 @@ describe(`[workbox-webpack-plugin] InjectManifest with webpack v4`, function() { }); describe(`[workbox-webpack-plugin] Non-compilation scenarios`, function() { - it(`should error when compileSrc is false and webpackCompilationPlugins is used`, function(done) { + it(`should warn when compileSrc is false and webpackCompilationPlugins is used`, function(done) { const outputDir = tempy.directory(); const config = { @@ -1782,11 +1782,10 @@ describe(`[workbox-webpack-plugin] InjectManifest with webpack v4`, function() { try { expect(webpackError).not.to.exist; const statsJson = stats.toJson(); - expect(statsJson.warnings).to.be.empty; - expect(statsJson.errors).to.have.members([ - `Please check your InjectManifest plugin configuration:\n"webpackCompilationPlugins" is not allowed`, + expect(statsJson.errors).to.be.empty; + expect(statsJson.warnings).to.have.members([ + 'compileSrc is false, so the webpackCompilationPlugins option will be ignored.', ]); - done(); } catch (error) { done(error); diff --git a/test/workbox-webpack-plugin/node/v5/generate-sw.js b/test/workbox-webpack-plugin/node/v5/generate-sw.js index 8af930f70..c46b469ae 100644 --- a/test/workbox-webpack-plugin/node/v5/generate-sw.js +++ b/test/workbox-webpack-plugin/node/v5/generate-sw.js @@ -65,7 +65,9 @@ describe(`[workbox-webpack-plugin] GenerateSW with webpack v5`, function() { const statsJson = stats.toJson(); expect(statsJson.warnings).to.be.empty; expect(statsJson.errors).to.have.length(1); - expect(statsJson.errors[0].message).to.eql(`Please check your GenerateSW plugin configuration:\n"invalid" is not allowed`); + expect(statsJson.errors[0].message).to.eql( + `Please check your GenerateSW plugin configuration:\n[WebpackGenerateSW] 'invalid' property is not expected to be here. Did you mean property 'include'?`, + ); done(); } catch (error) { diff --git a/test/workbox-webpack-plugin/node/v5/inject-manifest.js b/test/workbox-webpack-plugin/node/v5/inject-manifest.js index 22b5959b7..68484cbbd 100644 --- a/test/workbox-webpack-plugin/node/v5/inject-manifest.js +++ b/test/workbox-webpack-plugin/node/v5/inject-manifest.js @@ -71,7 +71,7 @@ describe(`[workbox-webpack-plugin] InjectManifest with webpack v5`, function() { const statsJson = stats.toJson(); expect(statsJson.warnings).to.be.empty; expect(statsJson.errors[0].message).to.eql( - `Please check your InjectManifest plugin configuration:\n"invalid" is not allowed`, + `Please check your InjectManifest plugin configuration:\n[WebpackInjectManifest] 'invalid' property is not expected to be here. Did you mean property 'include'?`, ); done(); @@ -1726,7 +1726,7 @@ describe(`[workbox-webpack-plugin] InjectManifest with webpack v5`, function() { new InjectManifest({ compileSrc: false, swDest: 'injected-manifest.json', - swSrc: upath.join(__dirname, '..', 'static', 'injected-manifest.json'), + swSrc: upath.join(__dirname, '..', '..', 'static', 'injected-manifest.json'), webpackCompilationPlugins: [{}], }), ], @@ -1737,9 +1737,9 @@ describe(`[workbox-webpack-plugin] InjectManifest with webpack v5`, function() { try { expect(webpackError).not.to.exist; const statsJson = stats.toJson(); - expect(statsJson.warnings).to.be.empty; - expect(statsJson.errors[0].message).to.eql( - `Please check your InjectManifest plugin configuration:\n"webpackCompilationPlugins" is not allowed`, + expect(statsJson.errors).to.be.empty; + expect(statsJson.warnings[0].message).to.eql( + 'compileSrc is false, so the webpackCompilationPlugins option will be ignored.', ); done(); @@ -1788,45 +1788,45 @@ describe(`[workbox-webpack-plugin] InjectManifest with webpack v5`, function() { } }); }); - }); - it(`should support injecting a manifest into a CJS module`, function(done) { - const outputDir = tempy.directory(); - - const config = { - mode: 'production', - entry: upath.join(SRC_DIR, WEBPACK_ENTRY_FILENAME), - output: { - filename: '[name].[contenthash:20].js', - path: outputDir, - }, - plugins: [ - new InjectManifest({ - compileSrc: false, - swDest: 'injected-manifest.js', - swSrc: upath.join(__dirname, '..', '..', 'static', 'injected-manifest.js'), - }), - ], - }; - - const compiler = webpack(config); - compiler.run(async (webpackError, stats) => { - try { - webpackBuildCheck(webpackError, stats); - - const files = await globby('**', {cwd: outputDir}); - expect(files).to.have.length(2); - - const manifest = require(upath.join(outputDir, 'injected-manifest.js')); - expect(manifest).to.matchPattern([{ - revision: null, - url: /^main\.[0-9a-f]{20}\.js$/, - }]); - - done(); - } catch (error) { - done(error); - } + it(`should support injecting a manifest into a CJS module`, function(done) { + const outputDir = tempy.directory(); + + const config = { + mode: 'production', + entry: upath.join(SRC_DIR, WEBPACK_ENTRY_FILENAME), + output: { + filename: '[name].[contenthash:20].js', + path: outputDir, + }, + plugins: [ + new InjectManifest({ + compileSrc: false, + swDest: 'injected-manifest.js', + swSrc: upath.join(__dirname, '..', '..', 'static', 'injected-manifest.js'), + }), + ], + }; + + const compiler = webpack(config); + compiler.run(async (webpackError, stats) => { + try { + webpackBuildCheck(webpackError, stats); + + const files = await globby('**', {cwd: outputDir}); + expect(files).to.have.length(2); + + const manifest = require(upath.join(outputDir, 'injected-manifest.js')); + expect(manifest).to.matchPattern([{ + revision: null, + url: /^main\.[0-9a-f]{20}\.js$/, + }]); + + done(); + } catch (error) { + done(error); + } + }); }); }); }); diff --git a/tsconfig.json b/tsconfig.json index ac129dfed..0b2a24e36 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,12 +21,15 @@ "references": [ { "path": "./packages/workbox-broadcast-update/" }, { "path": "./packages/workbox-background-sync/" }, + { "path": "./packages/workbox-build/" }, { "path": "./packages/workbox-cacheable-response/" }, + { "path": "./packages/workbox-cli/" }, { "path": "./packages/workbox-core/" }, { "path": "./packages/workbox-expiration/" }, { "path": "./packages/workbox-navigation-preload/" }, { "path": "./packages/workbox-precaching/" }, { "path": "./packages/workbox-range-requests/" }, + { "path": "./packages/workbox-recipes/" }, { "path": "./packages/workbox-routing/" }, { "path": "./packages/workbox-strategies/" }, { "path": "./packages/workbox-streams/" }, diff --git a/typescript.eslintrc.js b/typescript.eslintrc.js index bebb7b91b..8b896ac3c 100644 --- a/typescript.eslintrc.js +++ b/typescript.eslintrc.js @@ -7,6 +7,7 @@ module.exports = { }, plugins: [ '@typescript-eslint', + 'jsdoc', ], extends: [ 'eslint:recommended', @@ -25,7 +26,9 @@ module.exports = { '@typescript-eslint/no-misused-promises': 'off', '@typescript-eslint/no-non-null-assertion': 'off', '@typescript-eslint/no-use-before-define': 'off', + '@typescript-eslint/no-var-requires': 'off', '@typescript-eslint/prefer-readonly': 'error', + '@typescript-eslint/prefer-regexp-exec': 'off', '@typescript-eslint/require-await': 'off', '@typescript-eslint/unbound-method': 'off', 'block-scoped-var': 'error',