import { defineNuxtModule } from '@nuxt/kit' import type { VitePWAOptions, VitePluginPWAAPI } from 'vite-plugin-pwa' import { VitePWA } from 'vite-plugin-pwa' import type { Plugin } from 'vite' import type { VitePWANuxtOptions } from './types' import { configurePWAOptions } from './config' export * from './types' export default defineNuxtModule({ meta: { name: 'pwa', configKey: 'pwa', }, defaults: nuxt => ({ base: nuxt.options.app.baseURL, scope: nuxt.options.app.baseURL, }), async setup(options, nuxt) { let vitePwaClientPlugin: Plugin | undefined const resolveVitePluginPWAAPI = (): VitePluginPWAAPI | undefined => { return vitePwaClientPlugin?.api } // TODO: combine with configurePWAOptions? nuxt.hook('nitro:init', (nitro) => { options.outDir = nitro.options.output.publicDir options.injectManifest = options.injectManifest || {} options.injectManifest.globDirectory = nitro.options.output.publicDir }) nuxt.hook('vite:extend', ({ config }) => { const plugin = config.plugins?.find(p => p && typeof p === 'object' && 'name' in p && p.name === 'vite-plugin-pwa') if (plugin) throw new Error('Remove vite-plugin-pwa plugin from Vite Plugins entry in Nuxt config file!') }) nuxt.hook('vite:extendConfig', async (viteInlineConfig, { isClient }) => { viteInlineConfig.plugins = viteInlineConfig.plugins || [] const plugin = viteInlineConfig.plugins.find(p => p && typeof p === 'object' && 'name' in p && p.name === 'vite-plugin-pwa') if (plugin) throw new Error('Remove vite-plugin-pwa plugin from Vite Plugins entry in Nuxt config file!') const resolvedOptions: Partial = { ...options, manifest: options.manifest ? await options.manifest() : undefined, } configurePWAOptions(resolvedOptions, nuxt) const plugins = VitePWA(resolvedOptions) viteInlineConfig.plugins.push(plugins) if (isClient) vitePwaClientPlugin = plugins.find(p => p.name === 'vite-plugin-pwa') as Plugin }) if (nuxt.options.dev) { const webManifest = `${nuxt.options.app.baseURL}${options.devOptions?.webManifestUrl ?? options.manifestFilename ?? 'manifest.webmanifest'}` const devSw = `${nuxt.options.app.baseURL}dev-sw.js?dev-sw` const workbox = `${nuxt.options.app.baseURL}workbox-` // @ts-expect-error just ignore const emptyHandle = (_req, _res, next) => { next() } nuxt.hook('vite:serverCreated', (viteServer, { isServer }) => { if (isServer) return viteServer.middlewares.stack.push({ route: webManifest, handle: emptyHandle }) viteServer.middlewares.stack.push({ route: devSw, handle: emptyHandle }) }) if (!options.strategies || options.strategies === 'generateSW') { nuxt.hook('vite:serverCreated', (viteServer, { isServer }) => { if (isServer) return viteServer.middlewares.stack.push({ route: workbox, handle: emptyHandle }) }) nuxt.hook('close', async () => { // todo: cleanup dev-dist folder }) } } else { nuxt.hook('nitro:init', (nitro) => { nitro.hooks.hook('rollup:before', async () => { await resolveVitePluginPWAAPI()?.generateSW() }) }) } }, })