/
modules.ts
153 lines (133 loc) · 4.6 KB
/
modules.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import { dirname, resolve } from 'path'
import { promises as fs } from 'fs'
import { fileURLToPath } from 'url'
import type { BuildResult } from 'workbox-build'
import type { ResolvedConfig } from 'vite'
import type { ResolvedVitePWAOptions } from './types'
import { logWorkboxResult } from './log'
import { defaultInjectManifestVitePlugins } from './constants'
const _dirname = typeof __dirname !== 'undefined'
? __dirname
: dirname(fileURLToPath(import.meta.url))
async function loadWorkboxBuild(): Promise<typeof import('workbox-build')> {
// Uses require to lazy load.
// "workbox-build" is very large and it makes config loading slow.
// Since it is not always used, load this when it is needed.
try {
const workbox = await import('workbox-build')
return workbox.default ?? workbox
}
catch (_) {
return require('workbox-build')
}
}
async function loadRollupReplacePlugin() {
// Uses require to lazy load.
try {
const { createRequire } = await import('module').then(m => m.default || m)
const nodeRequire = createRequire(_dirname)
return nodeRequire('@rollup/plugin-replace')
}
catch (_) {
return require('@rollup/plugin-replace')
}
}
export async function generateRegisterSW(options: ResolvedVitePWAOptions, mode: 'build' | 'dev', source = 'register') {
const sw = options.base + options.filename
const scope = options.scope
const content = await fs.readFile(resolve(_dirname, `client/${mode}/${source}.mjs`), 'utf-8')
return content
.replace(/__SW__/g, sw)
.replace('__SCOPE__', scope)
.replace('__SW_AUTO_UPDATE__', `${options.registerType === 'autoUpdate'}`)
.replace('__SW_SELF_DESTROYING__', `${options.selfDestroying}`)
.replace('__TYPE__', `${options.devOptions.enabled ? options.devOptions.type : 'classic'}`)
}
export async function generateServiceWorker(options: ResolvedVitePWAOptions, viteOptions: ResolvedConfig): Promise<BuildResult> {
if (options.selfDestroying) {
const selfDestroyingSW = `
self.addEventListener('install', function(e) {
self.skipWaiting();
});
self.addEventListener('activate', function(e) {
self.registration.unregister()
.then(function() {
return self.clients.matchAll();
})
.then(function(clients) {
clients.forEach(client => client.navigate(client.url))
});
});
`
await fs.writeFile(options.swDest.replace(/\\/g, '/'), selfDestroyingSW, { encoding: 'utf8' })
return {
count: 1,
size: selfDestroyingSW.length,
warnings: [],
filePaths: [options.filename],
}
}
const { generateSW } = await loadWorkboxBuild()
// generate the service worker
const buildResult = await generateSW(options.workbox)
// log workbox result
logWorkboxResult('generateSW', buildResult, viteOptions)
return buildResult
}
export async function generateInjectManifest(options: ResolvedVitePWAOptions, viteOptions: ResolvedConfig) {
const { selfDestroying } = options
if (selfDestroying) {
await generateServiceWorker(options, viteOptions)
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 vitePlugins = options.vitePlugins
const includedPluginNames: string[] = []
if (typeof vitePlugins === 'function')
includedPluginNames.push(...vitePlugins(viteOptions.plugins.map(p => p.name)))
else
includedPluginNames.push(...vitePlugins)
if (includedPluginNames.length === 0)
includedPluginNames.push(...defaultInjectManifestVitePlugins)
const replace = await loadRollupReplacePlugin()
const plugins = [
replace({
'preventAssignment': true,
'process.env.NODE_ENV': JSON.stringify(options.mode),
}),
...viteOptions.plugins.filter(p => includedPluginNames.includes(p.name)),
]
const { rollup } = await import('rollup')
const bundle = await rollup({
input: options.swSrc,
plugins,
})
try {
await bundle.write({
format: options.rollupFormat,
exports: 'none',
inlineDynamicImports: true,
file: options.injectManifest.swDest,
sourcemap: viteOptions.build.sourcemap,
})
}
finally {
await bundle.close()
}
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)
}