diff --git a/docs/guide/development.md b/docs/guide/development.md index de29b326..aebf0fd5 100644 --- a/docs/guide/development.md +++ b/docs/guide/development.md @@ -68,9 +68,21 @@ export interface DevOptions { * **WARNING**: this option will only be used when using `injectManifest` strategy. */ navigateFallback?: string + /** + * On dev mode the `manifest.webmanifest` file can be on other path. + * + * For example, **SvelteKit** will request `/_app/manifest.webmanifest`. + * + * @default `${vite.base}${pwaOptions.manifestFilename}` + */ + webManifestUrl?: string } ``` +## manifest.webmanifest + +Since version `0.12.1` the `manifest.webmanifest` is also served on development mode: you can now check it on `dev tools`. + ## generateSW strategy When using this strategy, the `navigateFallback` on development options will be ignored. The PWA plugin will check if @@ -98,6 +110,19 @@ devOptions: { > **Warning**: when building the application, the PWA Plugin will always register your service worker with `type: 'classic'` for compatibility with all browsers. +> **Warning**: if using version `0.12.1+`, you will need to exclude the `manifest.webmanifest` from the service worker precache manifest if you want to check it using the browser (on `dev tools` it will be ok): +> ```ts +> let denylist: undefined | RegExp[] +> if (import.meta.env.DEV) +> denylist = [/^\/manifest.webmanifest$/] +> +> // to allow work offline +> registerRoute(new NavigationRoute( +> createHandlerBoundToURL('index.html'), +> { denylist } +> )) +> ``` + When using this strategy, the plugin will delegate the service worker compilation to `Vite`, so if you're using `import` statements instead `importScripts` in your custom service worker, you should configure `type: 'module'` on development options. @@ -138,3 +163,7 @@ To run the example, you must build the PWA plugin (`pnpm run build` from root fo (`cd examples/vue-router`) and run it: - `generateSW` strategy: `pnpm run dev` - `injectManifest` strategy: `pnpm run dev-claims` + +Since version `0.12.1`, you also have the development scripts for all other frameworks as well. + +The instructions for running the `dev` or `dev-claims` scripts are the same as for `vue-router` but running them in the corresponding framework directory. diff --git a/examples/preact-router/src/claims-sw.ts b/examples/preact-router/src/claims-sw.ts index 210d79f2..7953aaf3 100644 --- a/examples/preact-router/src/claims-sw.ts +++ b/examples/preact-router/src/claims-sw.ts @@ -10,8 +10,15 @@ precacheAndRoute(self.__WB_MANIFEST) // clean old assets cleanupOutdatedCaches() +let denylist: undefined | RegExp[] +if (import.meta.env.DEV) + denylist = [/^\/manifest.webmanifest$/] + // to allow work offline -registerRoute(new NavigationRoute(createHandlerBoundToURL('index.html'))) +registerRoute(new NavigationRoute( + createHandlerBoundToURL('index.html'), + { denylist }, +)) self.skipWaiting() clientsClaim() diff --git a/examples/react-router/src/claims-sw.ts b/examples/react-router/src/claims-sw.ts index 210d79f2..7953aaf3 100644 --- a/examples/react-router/src/claims-sw.ts +++ b/examples/react-router/src/claims-sw.ts @@ -10,8 +10,15 @@ precacheAndRoute(self.__WB_MANIFEST) // clean old assets cleanupOutdatedCaches() +let denylist: undefined | RegExp[] +if (import.meta.env.DEV) + denylist = [/^\/manifest.webmanifest$/] + // to allow work offline -registerRoute(new NavigationRoute(createHandlerBoundToURL('index.html'))) +registerRoute(new NavigationRoute( + createHandlerBoundToURL('index.html'), + { denylist }, +)) self.skipWaiting() clientsClaim() diff --git a/examples/solid-router/src/claims-sw.ts b/examples/solid-router/src/claims-sw.ts index 210d79f2..7953aaf3 100644 --- a/examples/solid-router/src/claims-sw.ts +++ b/examples/solid-router/src/claims-sw.ts @@ -10,8 +10,15 @@ precacheAndRoute(self.__WB_MANIFEST) // clean old assets cleanupOutdatedCaches() +let denylist: undefined | RegExp[] +if (import.meta.env.DEV) + denylist = [/^\/manifest.webmanifest$/] + // to allow work offline -registerRoute(new NavigationRoute(createHandlerBoundToURL('index.html'))) +registerRoute(new NavigationRoute( + createHandlerBoundToURL('index.html'), + { denylist }, +)) self.skipWaiting() clientsClaim() diff --git a/examples/svelte-routify/src/claims-sw.ts b/examples/svelte-routify/src/claims-sw.ts index 210d79f2..9ed36f75 100644 --- a/examples/svelte-routify/src/claims-sw.ts +++ b/examples/svelte-routify/src/claims-sw.ts @@ -10,8 +10,16 @@ precacheAndRoute(self.__WB_MANIFEST) // clean old assets cleanupOutdatedCaches() + +let denylist: undefined | RegExp[] +if (import.meta.env.DEV) + denylist = [/^\/manifest.webmanifest$/] + // to allow work offline -registerRoute(new NavigationRoute(createHandlerBoundToURL('index.html'))) +registerRoute(new NavigationRoute( + createHandlerBoundToURL('index.html'), + { denylist }, +)) self.skipWaiting() clientsClaim() diff --git a/examples/sveltekit-pwa/src/app.html b/examples/sveltekit-pwa/src/app.html index 2238847d..638c3f1e 100644 --- a/examples/sveltekit-pwa/src/app.html +++ b/examples/sveltekit-pwa/src/app.html @@ -8,9 +8,9 @@ - %svelte.head% + %sveltekit.head% -
%svelte.body%
+
%sveltekit.body%
diff --git a/examples/sveltekit-pwa/src/claims-sw.ts b/examples/sveltekit-pwa/src/claims-sw.ts index c269cac8..6d70e782 100644 --- a/examples/sveltekit-pwa/src/claims-sw.ts +++ b/examples/sveltekit-pwa/src/claims-sw.ts @@ -10,8 +10,16 @@ precacheAndRoute(self.__WB_MANIFEST) // clean old assets cleanupOutdatedCaches() + +let denylist: undefined | RegExp[] +if (import.meta.env.DEV) + denylist = [/^\/_app\/manifest.webmanifest$/] + // to allow work offline -registerRoute(new NavigationRoute(createHandlerBoundToURL('/'))) +registerRoute(new NavigationRoute( + createHandlerBoundToURL('/'), + { denylist }, +)) self.skipWaiting() clientsClaim() diff --git a/examples/vue-router/src/claims-sw.ts b/examples/vue-router/src/claims-sw.ts index 210d79f2..7953aaf3 100644 --- a/examples/vue-router/src/claims-sw.ts +++ b/examples/vue-router/src/claims-sw.ts @@ -10,8 +10,15 @@ precacheAndRoute(self.__WB_MANIFEST) // clean old assets cleanupOutdatedCaches() +let denylist: undefined | RegExp[] +if (import.meta.env.DEV) + denylist = [/^\/manifest.webmanifest$/] + // to allow work offline -registerRoute(new NavigationRoute(createHandlerBoundToURL('index.html'))) +registerRoute(new NavigationRoute( + createHandlerBoundToURL('index.html'), + { denylist }, +)) self.skipWaiting() clientsClaim() diff --git a/package.json b/package.json index d9c14bf4..c6925d89 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "@types/workbox-build": "^5.0.1", "@typescript-eslint/eslint-plugin": "^5.27.0", "eslint": "^8.17.0", - "esno": "^0.16.3", + "esno": "^0.14.1", "kolorist": "^1.5.1", "pnpm": "^7.1.8", "preact": "^10.7.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2ab5a199..118f4fc3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,7 +13,7 @@ importers: '@typescript-eslint/eslint-plugin': ^5.27.0 debug: ^4.3.4 eslint: ^8.17.0 - esno: ^0.16.3 + esno: ^0.14.1 fast-glob: ^3.2.11 kolorist: ^1.5.1 pnpm: ^7.1.8 @@ -46,7 +46,7 @@ importers: '@types/workbox-build': 5.0.1 '@typescript-eslint/eslint-plugin': 5.27.0_ud6rd4xtew5bv4yhvkvu24pzm4 eslint: 8.17.0 - esno: 0.16.3 + esno: 0.14.1 kolorist: 1.5.1 pnpm: 7.1.8 preact: 10.7.3 @@ -2297,27 +2297,6 @@ packages: algoliasearch: 4.10.3 dev: true - /@esbuild-kit/cjs-loader/2.0.1: - resolution: {integrity: sha512-KmE8XouKm6m05jPIsf5CTIZZ4171GHd+PUts1mtti2tzoiD228qCRjpkCwg540c3fMUweKupO+PIpkJ9+Z7vPg==} - dependencies: - '@esbuild-kit/core-utils': 1.2.1 - get-tsconfig: 3.0.1 - dev: true - - /@esbuild-kit/core-utils/1.2.1: - resolution: {integrity: sha512-zNtrwZ/754OMs6joIQIxsdOA3rjhde7Vt/IzkgNnCcQrwUDG5iHmlv9vhlHzyg+BZ6Cp519lrkMKlTCEW00Kpw==} - dependencies: - esbuild: 0.14.38 - dev: true - - /@esbuild-kit/esm-loader/2.1.4: - resolution: {integrity: sha512-acfzciUGugu1TV1QHJOAOe6F5miEjQLDYMWhoD4mVPFmwHA8Iwp5UCJidyruj67qp2ybu+Zc++7+th9VnYaZMA==} - dependencies: - '@esbuild-kit/core-utils': 1.2.1 - es-module-lexer: 0.10.5 - get-tsconfig: 3.0.1 - dev: true - /@eslint/eslintrc/1.3.0: resolution: {integrity: sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -4241,10 +4220,6 @@ packages: string.prototype.trimstart: 1.0.5 unbox-primitive: 1.0.2 - /es-module-lexer/0.10.5: - resolution: {integrity: sha512-+7IwY/kiGAacQfY+YBhKMvEmyAJnw5grTUgjG85Pe7vcUI/6b7pZjZG8nQ7+48YhzEAEqrEgD2dCz/JIK+AYvw==} - dev: true - /es-shim-unscopables/1.0.0: resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} dependencies: @@ -4533,6 +4508,12 @@ packages: dev: true optional: true + /esbuild-node-loader/0.6.5: + resolution: {integrity: sha512-uPP+dllWm38cFvDysdocutN3lfe5pTIbddAHp1ENyLzpHYqE2r+3Wo+pfg9X3p8DFWwzIisft5YkeBIthIcixw==} + dependencies: + esbuild: 0.14.38 + dev: true + /esbuild-openbsd-64/0.14.36: resolution: {integrity: sha512-NvGB2Chf8GxuleXRGk8e9zD3aSdRO5kLt9coTQbCg7WMGXeX471sBgh4kSg8pjx0yTXRt0MlrUDnjVYnetyivg==} engines: {node: '>=12'} @@ -4557,6 +4538,14 @@ packages: esbuild: '>=0.12 <1' dev: true + /esbuild-register/3.3.2_esbuild@0.14.38: + resolution: {integrity: sha512-jceAtTO6zxPmCfSD5cBb3rgIK1vmuqCKYwgylHiS1BF4pq0jJiJb4K2QMuqF4BEw7XDBRatYzip0upyTzfkgsQ==} + peerDependencies: + esbuild: '>=0.12 <1' + dependencies: + esbuild: 0.14.38 + dev: true + /esbuild-sunos-64/0.14.36: resolution: {integrity: sha512-VkUZS5ftTSjhRjuRLp+v78auMO3PZBXu6xl4ajomGenEm2/rGuWlhFSjB7YbBNErOchj51Jb2OK8lKAo8qdmsQ==} engines: {node: '>=12'} @@ -5113,11 +5102,15 @@ packages: engines: {node: '>=6'} dev: true - /esno/0.16.3: - resolution: {integrity: sha512-6slSBEV1lMKcX13DBifvnDFpNno5WXhw4j/ff7RI0y51BZiDqEe5dNhhjhIQ3iCOQuzsm2MbVzmwqbN78BBhPg==} + /esno/0.14.1: + resolution: {integrity: sha512-yDFYw6dGUjCT1qKsdG7WOc/RzIh/qwxUEVZ+ohCltaxBxEFMNqeqbQL9xjRl6Yvdwrfc5OCjUA9JbFmuu/8BKg==} hasBin: true dependencies: - tsx: 3.4.2 + cross-spawn: 7.0.3 + esbuild: 0.14.38 + esbuild-node-loader: 0.6.5 + esbuild-register: 3.3.2_esbuild@0.14.38 + import-meta-resolve: 1.1.1 dev: true /espree/9.3.2: @@ -5418,10 +5411,6 @@ packages: call-bind: 1.0.2 get-intrinsic: 1.1.1 - /get-tsconfig/3.0.1: - resolution: {integrity: sha512-+m30eQjbcf3xMNdnacXH5IDAKUMbI7Mhbf3e1BHif1FzBlUhBzBlmOVc7kL4+kB035l8OCyBdI3dNXZ3of9HqA==} - dev: true - /glob-parent/5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -5673,6 +5662,12 @@ packages: resolve-from: 5.0.0 dev: true + /import-meta-resolve/1.1.1: + resolution: {integrity: sha512-JiTuIvVyPaUg11eTrNDx5bgQ/yMKMZffc7YSjvQeSMXy58DO2SQ8BtAf3xteZvmzvjYh14wnqNjL8XVeDy2o9A==} + dependencies: + builtins: 4.0.0 + dev: true + /imurmurhash/0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -7699,17 +7694,6 @@ packages: typescript: 4.7.3 dev: true - /tsx/3.4.2: - resolution: {integrity: sha512-Rd1gm2noOUiVynF+VFxo4bVBNbzS6haWKWtlQ0bEfCLLEqm+GG3R98D3Rqk6foQ3NnJk6JAWOx1ragwcAPj4Lg==} - hasBin: true - dependencies: - '@esbuild-kit/cjs-loader': 2.0.1 - '@esbuild-kit/core-utils': 1.2.1 - '@esbuild-kit/esm-loader': 2.1.4 - optionalDependencies: - fsevents: 2.3.2 - dev: true - /type-check/0.3.2: resolution: {integrity: sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=} engines: {node: '>= 0.8.0'} diff --git a/src/dev.ts b/src/dev.ts index b87e955c..e8b53ab8 100644 --- a/src/dev.ts +++ b/src/dev.ts @@ -58,6 +58,8 @@ export async function loadDev(id: string, options: ResolvedVitePWAOptions, viteC // the sw precache (self.__SW_MANIFEST) will be empty since we're using `dev-dist` folder // we only need to add the navigateFallback if configured const navigateFallback = options.workbox.navigateFallback + // we need to exclude the manifest.webmanifest from the sw precache: avoid writing it to the dev-dist folder + const webManifestUrl = options.devOptions.webManifestUrl ?? `${options.base}${options.manifestFilename}` const { filePaths } = await generateServiceWorker( Object.assign( {}, @@ -65,6 +67,7 @@ export async function loadDev(id: string, options: ResolvedVitePWAOptions, viteC { workbox: { ...options.workbox, + navigateFallbackDenylist: [new RegExp(`^${webManifestUrl}$`)], runtimeCaching: options.devOptions.disableRuntimeConfig ? undefined : options.workbox.runtimeCaching, // we only include navigateFallback additionalManifestEntries: navigateFallback ? [navigateFallback] : undefined, diff --git a/src/html.ts b/src/html.ts index 228cf359..ffc5d6f2 100644 --- a/src/html.ts +++ b/src/html.ts @@ -36,7 +36,8 @@ export function injectServiceWorker(html: string, options: ResolvedVitePWAOption export function injectDevManifest(html: string, options: ResolvedVitePWAOptions) { const crossorigin = options.useCredentials ? ' crossorigin="use-credentials"' : '' - const manifest = options.manifest ? `` : '' + const name = options.devOptions.webManifestUrl ?? `${options.base}${options.manifestFilename}` + const manifest = options.manifest ? `` : '' return html.replace( '', diff --git a/src/index.ts b/src/index.ts index 51c36c43..b451cb19 100644 --- a/src/index.ts +++ b/src/index.ts @@ -154,9 +154,9 @@ export function VitePWA(userOptions: Partial = {}): Plugin[] { if (!options.disable && options.manifest && options.devOptions.enabled) { const name = options.devOptions.webManifestUrl ?? `${options.base}${options.manifestFilename}` server.middlewares.use((req, res, next) => { - const url = req.url - if (url === name) { + if (req.url === name) { res.statusCode = 200 + res.setHeader('Content-Type', 'application/manifest+json') res.write(generateWebManifestFile(options), 'utf-8') res.end() }