From e8e00773864cde34f10ed919be906b93f1f755dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Han=EF=BC=88=E3=83=8F=E3=83=B3=EF=BC=89?= Date: Sat, 16 Jan 2021 20:18:50 +0900 Subject: [PATCH] feat: add injectManifest strategies (#15) * feat: add injectManifest strategies * chore: simplify code Co-authored-by: Anthony Fu Co-authored-by: Anthony Fu --- example/package.json | 3 +- example/public/sw.js | 3 ++ example/vite.config.ts | 10 ++++--- pnpm-lock.yaml | 2 ++ src/config.ts | 64 ++++++++++++++++++++++++++++++++++++++++++ src/html.ts | 19 +++++++++++++ src/index.ts | 64 +++++++++--------------------------------- src/types.ts | 25 ++++++++++++++++- 8 files changed, 134 insertions(+), 56 deletions(-) create mode 100644 example/public/sw.js create mode 100644 src/config.ts create mode 100644 src/html.ts diff --git a/example/package.json b/example/package.json index 575ef893..60bc62b5 100644 --- a/example/package.json +++ b/example/package.json @@ -7,7 +7,8 @@ "build": "cross-env DEBUG=vite-plugin-pwa:* vite build" }, "dependencies": { - "vue": "^3.0.5" + "vue": "^3.0.5", + "workbox-precaching": "^6.0.2" }, "devDependencies": { "@vitejs/plugin-vue": "^1.0.5", diff --git a/example/public/sw.js b/example/public/sw.js new file mode 100644 index 00000000..9baf7ec2 --- /dev/null +++ b/example/public/sw.js @@ -0,0 +1,3 @@ +import { precacheAndRoute } from 'workbox-precaching' + +precacheAndRoute(self.__WB_MANIFEST) diff --git a/example/vite.config.ts b/example/vite.config.ts index 91bb407e..e9de5f56 100644 --- a/example/vite.config.ts +++ b/example/vite.config.ts @@ -3,12 +3,14 @@ import Vue from '@vitejs/plugin-vue' import { VitePWA } from 'vite-plugin-pwa' const config: UserConfig = { - build: { - base: 'test' - }, + // build: { + // base: 'test', + // }, plugins: [ Vue(), - VitePWA(), + VitePWA({ + strategies: 'injectManifest', + }), ], } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 68eaf12d..6c31b67e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,6 +32,7 @@ importers: example: dependencies: vue: 3.0.5 + workbox-precaching: 6.0.2 devDependencies: '@vitejs/plugin-vue': 1.0.5_@vue+compiler-sfc@3.0.5 '@vue/compiler-sfc': 3.0.5_vue@3.0.5 @@ -47,6 +48,7 @@ importers: vite: ^2.0.0-beta.23 vite-plugin-pwa: workspace:* vue: ^3.0.5 + workbox-precaching: ^6.0.2 lockfileVersion: 5.2 packages: /@antfu/eslint-config-basic/0.4.3_eslint@7.17.0: diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 00000000..548df177 --- /dev/null +++ b/src/config.ts @@ -0,0 +1,64 @@ +import fs from 'fs' +import { resolve } from 'path' +import { ResolvedConfig } from 'vite' +import { GenerateSWConfig, InjectManifestConfig } from 'workbox-build' +import { ManifestOptions, VitePWAOptions, ResolvedVitePWAOptions } from './types' +import { cachePreset } from './cache' + +export function resolveOptions(options: Partial, viteConfig: ResolvedConfig): ResolvedVitePWAOptions { + const root = viteConfig.root + const pkg = fs.existsSync('package.json') + ? JSON.parse(fs.readFileSync('package.json', 'utf-8')) + : {} + const { + srcDir = 'public', + outDir = viteConfig.build.outDir || 'dist', + filename = 'sw.js', + strategies = 'generateSW', + } = options + + const swSrc = resolve(root, srcDir, filename) + const swDest = resolve(root, outDir, filename) + const outDirRoot = resolve(root, outDir) + + const defaultWorkbox: GenerateSWConfig = { + swDest, + globDirectory: outDirRoot, + offlineGoogleAnalytics: false, + runtimeCaching: cachePreset, + // prevent tsup replacing `process.env` + // eslint-disable-next-line dot-notation + mode: process['env']['NODE_ENV'] || 'production', + } + + const defaultInjectManifest: InjectManifestConfig = { + swSrc, + swDest, + globDirectory: outDirRoot, + injectionPoint: 'self.__WB_MANIFEST', + } + + const defaultManifest: Partial = { + name: pkg.name, + short_name: pkg.name, + start_url: '/', + display: 'standalone', + background_color: '#ffffff', + lang: 'en', + } + + const workbox = Object.assign({}, defaultWorkbox, options.workbox || {}) + const manifest = Object.assign({}, defaultManifest, options.manifest || {}) + const injectManifest = Object.assign({}, defaultInjectManifest, options.injectManifest || {}) + + return { + swDest, + srcDir, + outDir, + filename, + strategies, + workbox, + manifest, + injectManifest, + } +} diff --git a/src/html.ts b/src/html.ts new file mode 100644 index 00000000..80fd90f0 --- /dev/null +++ b/src/html.ts @@ -0,0 +1,19 @@ +import { join } from 'path' +import { ResolvedVitePWAOptions } from './types' + +export function injectServiceWorker(html: string, base: string, options: ResolvedVitePWAOptions) { + const basePath = base.startsWith('/') ? `/${base}` : base + return html.replace( + '', + ` + + +`, + ) +} diff --git a/src/index.ts b/src/index.ts index c9e8f08f..f04761d2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,12 @@ -import fs from 'fs' -import { resolve } from 'path' import type { Plugin, ResolvedConfig } from 'vite' -import { generateSW, GenerateSWConfig } from 'workbox-build' -import { cachePreset } from './cache' -import { ManifestOptions, VitePWAOptions } from './types' +import { generateSW, injectManifest } from 'workbox-build' +import { injectServiceWorker } from './html' +import { ResolvedVitePWAOptions, VitePWAOptions } from './types' +import { resolveOptions } from './config' export function VitePWA(options: Partial = {}): Plugin { let viteConfig: ResolvedConfig | undefined - let workbox: GenerateSWConfig | undefined - let manifest: Partial = {} - let outDir = 'dist' + let resolvedOptions: ResolvedVitePWAOptions | undefined return { name: 'vite-plugin-pwa', @@ -17,50 +14,13 @@ export function VitePWA(options: Partial = {}): Plugin { apply: 'build', configResolved(config) { viteConfig = config - const root = viteConfig.root - const pkg = fs.existsSync('package.json') - ? JSON.parse(fs.readFileSync('package.json', 'utf-8')) - : {} - outDir = options.outDir || config.build.outDir || 'dist' - - const defaultWorkbox: GenerateSWConfig = { - swDest: resolve(root, `${outDir}/sw.js`), - globDirectory: resolve(root, outDir), - offlineGoogleAnalytics: false, - runtimeCaching: cachePreset, - // prevent tsup replacing `process.env` - // eslint-disable-next-line dot-notation - mode: process['env']['NODE_ENV'] || 'production', - } - - const defaultManifest: Partial = { - name: pkg.name, - short_name: pkg.name, - start_url: '/', - display: 'standalone', - background_color: '#ffffff', - lang: 'en', - } - - workbox = Object.assign({}, defaultWorkbox, options.workbox || {}) - manifest = Object.assign({}, defaultManifest, options.manifest || {}) + resolvedOptions = resolveOptions(options, viteConfig) }, transformIndexHtml: { enforce: 'post', transform(html) { - return html.replace( - '', - ` - - -`, - ) + const base = viteConfig!.build.base + return injectServiceWorker(html, base, resolvedOptions!) }, }, generateBundle(_, bundle) { @@ -68,12 +28,16 @@ export function VitePWA(options: Partial = {}): Plugin { isAsset: true, type: 'asset', name: undefined, - source: `${JSON.stringify(manifest, null, 2)}\n`, + source: `${JSON.stringify(resolvedOptions!.manifest, null, 2)}\n`, fileName: 'manifest.webmanifest', } }, buildEnd() { - generateSW(workbox!) + const strategies = resolvedOptions!.strategies + if (strategies === 'generateSW') + generateSW(resolvedOptions!.workbox) + if (strategies === 'injectManifest') + injectManifest(resolvedOptions!.injectManifest) }, } } diff --git a/src/types.ts b/src/types.ts index c73bceea..d92bcf75 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,4 @@ -import { GenerateSWConfig } from 'workbox-build' +import { GenerateSWConfig, InjectManifestConfig } from 'workbox-build' export interface ManifestOptions { /** @@ -59,7 +59,30 @@ export interface ManifestOptions { * Plugin options. */ export interface VitePWAOptions { + /** + * Default: 'public' + */ + srcDir?: string + /** + * Default: 'dist' + */ outDir?: string + /** + * Default: 'sw.js' + */ + filename?: string + /** + * Default: 'generateSW' + */ + strategies?: 'generateSW' | 'injectManifest' manifest: Partial workbox: Partial + injectManifest: Partial +} + +export interface ResolvedVitePWAOptions extends Required { + swDest: string + manifest: ManifestOptions + workbox: GenerateSWConfig + injectManifest: InjectManifestConfig }