Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: new custom service worker build #629

Merged
merged 11 commits into from
Feb 10, 2024
4 changes: 4 additions & 0 deletions examples/preact-router/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ if (process.env.SW === 'true') {
pwaOptions.strategies = 'injectManifest'
;(pwaOptions.manifest as Partial<ManifestOptions>).name = 'PWA Inject Manifest'
;(pwaOptions.manifest as Partial<ManifestOptions>).short_name = 'PWA Inject'
pwaOptions.injectManifest = {
minify: false,
enableWorkboxModulesLogs: true,
}
}

if (claims)
Expand Down
4 changes: 4 additions & 0 deletions examples/react-router/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ if (process.env.SW === 'true') {
pwaOptions.strategies = 'injectManifest'
;(pwaOptions.manifest as Partial<ManifestOptions>).name = 'PWA Inject Manifest'
;(pwaOptions.manifest as Partial<ManifestOptions>).short_name = 'PWA Inject'
pwaOptions.injectManifest = {
minify: false,
enableWorkboxModulesLogs: true,
}
}

if (claims)
Expand Down
4 changes: 4 additions & 0 deletions examples/solid-router/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ if (process.env.SW === 'true') {
pwaOptions.strategies = 'injectManifest'
;(pwaOptions.manifest as Partial<ManifestOptions>).name = 'PWA Inject Manifest'
;(pwaOptions.manifest as Partial<ManifestOptions>).short_name = 'PWA Inject'
pwaOptions.injectManifest = {
minify: false,
enableWorkboxModulesLogs: true,
}
}

if (claims)
Expand Down
4 changes: 4 additions & 0 deletions examples/svelte-routify/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ if (process.env.SW === 'true') {
pwaOptions.strategies = 'injectManifest'
pwaOptions.manifest.name = 'PWA Inject Manifest'
pwaOptions.manifest.short_name = 'PWA Inject'
pwaOptions.injectManifest = {
minify: false,
enableWorkboxModulesLogs: true,
}
}

if (claims)
Expand Down
5 changes: 4 additions & 1 deletion examples/vanilla-ts-no-ip/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@
"private": true,
"scripts": {
"dev": "rimraf dev-dist && DEBUG=vite-plugin-pwa SW_DEV=true vite --force",
"dev-custom": "rimraf dev-dist && DEBUG=vite-plugin-pwa SW=true SW_DEV=true vite --force",
"run-build-sw": "DEBUG=vite-plugin-pwa BASE_URL=/ SOURCE_MAP=true vite build",
"run-build-custom-sw": "DEBUG=vite-plugin-pwa BASE_URL=/ SOURCE_MAP=true SW=true vite build",
"start-sw": "nr run-build-sw && nr serve",
"serve": "serve dist",
"start-preview": "vite preview --port=4173",
"test-custom-sw": "DEBUG=vite-plugin-pwa BASE_URL=/ SOURCE_MAP=true SW=true vite build && SW=true vitest run && SW=true playwright test",
"test-custom-sw": "nr run-build-custom-sw && SW=true vitest run && SW=true playwright test",
"test-generate-sw": "nr run-build-sw && vitest run && playwright test",
"test": "nr test-generate-sw && nr test-custom-sw"
},
"devDependencies": {
"lodash-es": "^4.17.21",
"rimraf": "^5.0.5",
"typescript": "^5.2.2",
"vite": "^5.0.0",
Expand Down
3 changes: 3 additions & 0 deletions examples/vanilla-ts-no-ip/src/custom-sw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { registerRoute } from 'workbox-routing'
import { CacheFirst, NetworkFirst, StaleWhileRevalidate } from 'workbox-strategies'
import { CacheableResponsePlugin } from 'workbox-cacheable-response'
import { ExpirationPlugin } from 'workbox-expiration'
import orderBy from 'lodash-es/orderBy.js'

console.log(orderBy)

declare let self: ServiceWorkerGlobalScope

Expand Down
3 changes: 3 additions & 0 deletions examples/vanilla-ts-no-ip/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { VitePWA } from 'vite-plugin-pwa'
const customSW = process.env.SW === 'true'

export default defineConfig({
mode: 'development',
logLevel: 'info',
define: {
__DATE__: `'${new Date().toISOString()}'`,
Expand Down Expand Up @@ -51,6 +52,8 @@ export default defineConfig({
skipWaiting: true,
},
injectManifest: {
minify: false,
enableWorkboxModulesLogs: true,
injectionPoint: undefined,
},
devOptions: {
Expand Down
4 changes: 4 additions & 0 deletions examples/vue-router/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ if (process.env.SW === 'true') {
pwaOptions.strategies = 'injectManifest'
;(pwaOptions.manifest as Partial<ManifestOptions>).name = 'PWA Inject Manifest'
;(pwaOptions.manifest as Partial<ManifestOptions>).short_name = 'PWA Inject'
pwaOptions.injectManifest = {
minify: false,
enableWorkboxModulesLogs: true,
}
}

if (claims)
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "vite-plugin-pwa",
"type": "module",
"version": "0.17.4",
"packageManager": "pnpm@8.11.0",
"packageManager": "pnpm@8.12.1",
"description": "Zero-config PWA for Vite",
"author": "antfu <[email protected]>",
"license": "MIT",
Expand Down Expand Up @@ -86,7 +86,7 @@
"lint": "eslint .",
"lint-fix": "nr lint --fix",
"dev": "esno scripts/dev.ts",
"build": "esno scripts/build.ts",
"build": "rimraf dist && esno scripts/build.ts",
"prepublishOnly": "npm run build",
"release": "bumpp && npm publish",
"examples": "esno scripts/run-examples.ts",
Expand Down
7 changes: 7 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 38 additions & 6 deletions src/log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,33 @@ import type { BuildResult } from 'workbox-build'
import type { ResolvedConfig } from 'vite'
import { cyan, dim, green, magenta, yellow } from 'kolorist'
import { version } from '../package.json'
import { normalizePath } from './utils'
import type { ResolvedVitePWAOptions } from './types'

export function logWorkboxResult(strategy: string, buildResult: BuildResult, viteOptions: ResolvedConfig) {
export function logSWViteBuild(
swName: string,
viteOptions: ResolvedConfig,
format: 'es' | 'iife',
) {
const { logLevel = 'info' } = viteOptions
if (logLevel === 'silent')
return

if (logLevel === 'info') {
console.info([
'',
`${cyan(`PWA v${version}`)}`,
`Building ${magenta(swName)} service worker ("${magenta(format)}" format)...`,
].join('\n'))
}
}

export function logWorkboxResult(
strategy: ResolvedVitePWAOptions['strategies'],
buildResult: BuildResult,
viteOptions: ResolvedConfig,
format: 'es' | 'iife' | 'none' = 'none',
) {
const { root, logLevel = 'info' } = viteOptions

if (logLevel === 'silent')
Expand All @@ -14,14 +39,21 @@ export function logWorkboxResult(strategy: string, buildResult: BuildResult, vit
const { count, size, filePaths, warnings } = buildResult

if (logLevel === 'info') {
console.info([
const entries = [
'',
`${cyan(`PWA v${version}`)}`,
`mode ${magenta(strategy)}`,
`precache ${green(`${count} entries`)} ${dim(`(${(size / 1024).toFixed(2)} KiB)`)}`,
'files generated',
...filePaths.map(p => ` ${dim(relative(root, p))}`),
].join('\n'))
]
if (strategy === 'injectManifest')
entries.push(`format: ${magenta(format)}`)

entries.push(
`precache ${green(`${count} entries`)} ${dim(`(${(size / 1024).toFixed(2)} KiB)`)}`,
'files generated',
...filePaths.map(p => ` ${dim(normalizePath(relative(root, p)))}`),
)

console.info(entries.join('\n'))
}

// log build warning
Expand Down
64 changes: 5 additions & 59 deletions src/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,64 +94,10 @@ export async function generateInjectManifest(options: ResolvedVitePWAOptions, vi
return
}

// we will have something like this from swSrc:
/*
// sw.js
import { precacheAndRoute } from 'workbox-precaching'
// self.__WB_MANIFEST is default injection point
precacheAndRoute(self.__WB_MANIFEST)
*/
const [workbox, buildSW] = await Promise.all([
loadWorkboxBuild(),
import('./vite-build').then(({ buildSW }) => buildSW),
])

const { build } = await import('vite')

const define: Record<string, any> = { ...(viteOptions.define ?? {}) }
define['process.env.NODE_ENV'] = JSON.stringify(options.mode)

const { format, plugins, rollupOptions } = options.injectManifestRollupOptions

await build({
root: viteOptions.root,
base: viteOptions.base,
resolve: viteOptions.resolve,
// don't copy anything from public folder
publicDir: false,
build: {
sourcemap: viteOptions.build.sourcemap,
lib: {
entry: options.swSrc,
name: 'app',
formats: [format],
},
rollupOptions: {
...rollupOptions,
plugins,
output: {
entryFileNames: options.filename,
},
},
outDir: options.outDir,
emptyOutDir: false,
},
configFile: false,
define,
})

// don't force user to include injection point
if (!options.injectManifest.injectionPoint)
return

await options.integration?.beforeBuildServiceWorker?.(options)

const injectManifestOptions = {
...options.injectManifest,
// this will not fail since there is an injectionPoint
swSrc: options.injectManifest.swDest,
}

const { injectManifest } = await loadWorkboxBuild()

// inject the manifest
const buildResult = await injectManifest(injectManifestOptions)
// log workbox result
logWorkboxResult('injectManifest', buildResult, viteOptions)
await buildSW(options, viteOptions, workbox)
}
10 changes: 10 additions & 0 deletions src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ export async function resolveOptions(options: Partial<VitePWAOptions>, viteConfi
plugins = [],
rollupOptions = {},
rollupFormat = 'es',
target = viteConfig.build.target,
minify: minifySW = viteConfig.build.minify,
sourcemap = viteConfig.build.sourcemap,
enableWorkboxModulesLogs,
...userInjectManifest
} = options.injectManifest || {}
const injectManifest = Object.assign({}, defaultInjectManifest, userInjectManifest)
Expand Down Expand Up @@ -198,6 +202,12 @@ export async function resolveOptions(options: Partial<VitePWAOptions>, viteConfi
rollupOptions,
format: rollupFormat,
},
injectManifestBuildOptions: {
target,
minify: minifySW,
sourcemap,
enableWorkboxModulesLogs,
},
}

// calculate hash only when required
Expand Down
54 changes: 52 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Plugin, ResolvedConfig } from 'vite'
import type { BuildOptions, Plugin, ResolvedConfig } from 'vite'
import type { GenerateSWOptions, InjectManifestOptions, ManifestEntry } from 'workbox-build'
import type { OutputBundle, RollupOptions } from 'rollup'

Expand All @@ -10,6 +10,39 @@ export type CustomInjectManifestOptions = InjectManifestOptions & {
* @default 'es'
*/
rollupFormat?: 'es' | 'iife'
/**
* Configure the custom Vite build target option.
*
* @default Vite build target option
* @since v0.18.0
*/
target?: BuildOptions['target']
/**
* Configure the custom Vite build minify option.
*
* @default Vite build minify option
* @since v0.18.0
*/
minify?: BuildOptions['minify']
/**
* Configure the custom Vite build sourcemap option.
*
* @default Vite build sourcemap option
* @since v0.18.0
*/
sourcemap?: BuildOptions['sourcemap']
/**
* Should use `process.env.NODE_ENV` to remove dead code?
*
* If you want to keep logs from `workbox` modules, you can set this option to `true`,
* the plugin will configure `process.env.NODE_ENV` to `"development"`.
*
* If this option is not configured, the plugin will use `process.env.NODE_ENV`.
*
* @default `process.env.NODE_ENV === 'production'`
* @since v0.18.0
*/
enableWorkboxModulesLogs?: true
/**
* `Vite` plugin ids to use on `Rollup` build.
*
Expand Down Expand Up @@ -48,8 +81,19 @@ export interface PWAIntegration {
*/
export interface VitePWAOptions {
/**
* Build mode
* Build mode.
*
* From `v0.18.0` this option is ignored when using `injectManifest` strategy:
* - the new Vite build will use the same mode as the application when using `injectManifest` strategy.
* - if you don't want to minify your service worker, configure `injectManifest.minify = false` in your PWA configuration.
* - if you want the sourcemap only for the service worker, configure `injectManifest.sourcemap = true` in your PWA configuration.
* - if you want workbox logs in your service worker when using production build, configure `injectManifest.enableWorkboxModulesLogs = true` in your PWA configuration.
* - you can use `import.meta.env.MODE` to access the Vite mode inside your service worker.
* - you can use `import.meta.env.DEV` or `import.meta.env.PROD` to check if the service worker is
* running on development or production (equivalent to `process.env.NODE_ENV`),
* check Vite [NODE_ENV and Modes](https://vitejs.dev/guide/env-and-mode#node-env-and-modes) docs.
*
* @see https://vitejs.dev/guide/env-and-mode#node-env-and-modes
* @default process.env.NODE_ENV or "production"
*/
mode?: 'development' | 'production'
Expand Down Expand Up @@ -202,6 +246,12 @@ export interface ResolvedVitePWAOptions extends Required<VitePWAOptions> {
rollupFormat: 'es' | 'iife'
vitePlugins: InjectManifestVitePlugins
injectManifestRollupOptions: ResolvedServiceWorkerOptions
injectManifestBuildOptions: {
target?: BuildOptions['target']
minify?: BuildOptions['minify']
sourcemap?: BuildOptions['sourcemap']
enableWorkboxModulesLogs?: true
}
}

export interface ShareTargetFiles {
Expand Down
Loading