|
1 | 1 | const path = require('path')
|
2 | 2 | const swBuild = require('workbox-build')
|
| 3 | +const { readFileSync, writeFileSync } = require('fs') |
| 4 | +const hashSum = require('hash-sum') |
| 5 | +const debug = require('debug')('nuxt:pwa:workbox') |
3 | 6 |
|
4 | 7 | const fixUrl = url => url.replace(/\/\//g, '/').replace(':/', '://')
|
5 | 8 | const isUrl = url => url.indexOf('http') === 0 || url.indexOf('//') === 0
|
6 | 9 |
|
7 |
| -module.exports = function nuxtWorkbox (options) { |
| 10 | +// ============================================= |
| 11 | +// workboxModule |
| 12 | +// ============================================= |
| 13 | + |
| 14 | +module.exports = function nuxtWorkbox (moduleOptions) { |
| 15 | + const options = Object.assign({}, moduleOptions, this.options.workbox) |
| 16 | + const ctx = { options } |
| 17 | + |
8 | 18 | if (this.options.dev) {
|
9 | 19 | return
|
10 | 20 | }
|
11 | 21 |
|
12 |
| - // routerBase and publicPath |
| 22 | + this.nuxt.plugin('build', builder => { |
| 23 | + debug('Adding workbox') |
| 24 | + getRouterBase.call(this, ctx) |
| 25 | + workboxInject.call(this, ctx) |
| 26 | + emitAssets.call(this, ctx) |
| 27 | + addTemplates.call(this, ctx) |
| 28 | + }) |
| 29 | +} |
| 30 | + |
| 31 | +// ============================================= |
| 32 | +// getRouterBase |
| 33 | +// ============================================= |
| 34 | + |
| 35 | +function getRouterBase (ctx) { |
13 | 36 | const routerBase = this.options.router.base
|
14 | 37 | let publicPath = fixUrl(`${routerBase}/${this.options.build.publicPath}`)
|
15 | 38 | if (isUrl(this.options.build.publicPath)) { // CDN
|
16 | 39 | publicPath = this.options.build.publicPath
|
17 | 40 | if (publicPath.indexOf('//') === 0) {
|
18 |
| - publicPath = '/' + publicPath // escape fixUrl |
| 41 | + publicPath = '/' + publicPath // Escape fixUrl |
19 | 42 | }
|
20 | 43 | }
|
21 | 44 |
|
22 |
| - const swFileName = 'sw.js' |
| 45 | + ctx.routerBase = routerBase |
| 46 | + ctx.publicPath = publicPath |
| 47 | +} |
| 48 | + |
| 49 | +// ============================================= |
| 50 | +// addTemplates |
| 51 | +// ============================================= |
| 52 | + |
| 53 | +function addTemplates (ctx) { |
| 54 | + // Add sw.template.js |
| 55 | + this.addTemplate({ |
| 56 | + src: path.resolve(__dirname, 'templates/sw.template.js'), |
| 57 | + fileName: 'sw.template.js', |
| 58 | + options: { |
| 59 | + importPath: ctx.wbDst, |
| 60 | + wb: ctx.workboxOptions |
| 61 | + } |
| 62 | + }) |
| 63 | + |
| 64 | + // Add sw.plugin.js |
| 65 | + const swURL = `${ctx.routerBase}/${ctx.options.swURL || 'sw.js'}` |
| 66 | + this.addPlugin({ |
| 67 | + src: path.resolve(__dirname, 'templates/sw.plugin.js'), |
| 68 | + ssr: false, |
| 69 | + fileName: 'sw.plugin.js', |
| 70 | + options: { |
| 71 | + swURL: fixUrl(swURL), |
| 72 | + swScope: fixUrl(`${ctx.routerBase}/`) |
| 73 | + } |
| 74 | + }) |
| 75 | +} |
23 | 76 |
|
24 |
| - // Use swBuild to generate sw file |
25 |
| - // We set dest to static dir that is served as / to allow global sw scope |
| 77 | +// ============================================= |
| 78 | +// emitAssets |
| 79 | +// ============================================= |
| 80 | + |
| 81 | +function emitAssets (ctx) { |
| 82 | + const assets = [] |
| 83 | + const emitAsset = (path, name, ext = 'js') => { |
| 84 | + const source = readFileSync(path) |
| 85 | + const hash = hashSum(source) |
| 86 | + const dst = `${name}.${hash}.${ext}` |
| 87 | + assets.push({source, dst}) |
| 88 | + return dst |
| 89 | + } |
| 90 | + |
| 91 | + // Write assets after build |
| 92 | + this.nuxt.plugin('build', builder => { |
| 93 | + builder.plugin('built', () => { |
| 94 | + assets.forEach(({source, dst}) => { |
| 95 | + writeFileSync(path.resolve(this.options.buildDir, 'dist', dst), source, 'utf-8') |
| 96 | + }) |
| 97 | + }) |
| 98 | + }) |
| 99 | + |
| 100 | + // workbox.js |
| 101 | + let wbPath = require.resolve('workbox-sw') |
| 102 | + if (ctx.options.dev) { |
| 103 | + wbPath = wbPath.replace(/prod/g, 'dev') |
| 104 | + } |
| 105 | + ctx.wbDst = fixUrl(ctx.publicPath + '/' + emitAsset(wbPath, 'workbox' + (ctx.options.dev ? '.dev' : ''))) |
| 106 | +} |
| 107 | + |
| 108 | +// ============================================= |
| 109 | +// workboxInject |
| 110 | +// ============================================= |
| 111 | + |
| 112 | +function workboxInject (ctx) { |
26 | 113 | // https://workboxjs.org/reference-docs/latest/module-workbox-build.html#.generateSW
|
27 |
| - const workboxOptions = Object.assign({ |
28 |
| - swDest: path.resolve(this.options.srcDir, 'static', swFileName), |
| 114 | + ctx.workboxOptions = Object.assign({ |
| 115 | + swSrc: path.resolve(this.options.buildDir, 'sw.template.js'), |
| 116 | + swDest: path.resolve(this.options.srcDir, 'static', 'sw.js'), |
29 | 117 | directoryIndex: '/',
|
30 | 118 | cacheId: process.env.npm_package_name + '_' + process.env.npm_package_version,
|
31 | 119 | clientsClaim: true,
|
32 | 120 | globPatterns: ['**/*.{js,css}'],
|
33 | 121 | globDirectory: path.resolve(this.options.buildDir, 'dist'),
|
34 | 122 | modifyUrlPrefix: {
|
35 |
| - '': fixUrl(publicPath) |
| 123 | + '': fixUrl(ctx.publicPath) |
36 | 124 | },
|
37 | 125 | runtimeCaching: [
|
38 | 126 | // Cache routes if offline
|
39 | 127 | {
|
40 |
| - urlPattern: fixUrl(routerBase + '/**'), |
| 128 | + urlPattern: fixUrl(ctx.routerBase + '/**'), |
41 | 129 | handler: 'networkFirst'
|
42 | 130 | },
|
43 | 131 | // Cache other _nuxt resources runtime
|
44 | 132 | // They are hashed by webpack so are safe to loaded by cacheFirst handler
|
45 | 133 | {
|
46 |
| - urlPattern: fixUrl(publicPath + '/**'), |
| 134 | + urlPattern: fixUrl(ctx.publicPath + '/**'), |
47 | 135 | handler: 'cacheFirst'
|
48 | 136 | }
|
49 | 137 | ]
|
50 |
| - }, options) |
| 138 | + }, ctx.options) |
51 | 139 |
|
52 |
| - // Use nuxt plugins to prevent race conditions with webpack plugin |
53 |
| - // (https://github.com/nuxt-community/modules/issues/110) |
54 | 140 | this.nuxt.plugin('build', builder => {
|
55 | 141 | builder.plugin('built', () => {
|
56 |
| - if (workboxOptions.swSrc) { |
57 |
| - return swBuild.injectManifest(workboxOptions) |
58 |
| - } else { |
59 |
| - return swBuild.generateSW(workboxOptions) |
60 |
| - } |
| 142 | + const opts = Object.assign({}, ctx.workboxOptions) |
| 143 | + delete opts.runtimeCaching |
| 144 | + return swBuild.injectManifest(opts) |
61 | 145 | })
|
62 | 146 | })
|
63 |
| - |
64 |
| - // Register runtime plugin |
65 |
| - const swURL = `${routerBase}/${options.swURL || swFileName}` |
66 |
| - this.addPlugin({ |
67 |
| - src: path.resolve(__dirname, 'plugin.js'), |
68 |
| - ssr: false, |
69 |
| - options: { |
70 |
| - swURL: fixUrl(swURL), |
71 |
| - swScope: fixUrl(`${routerBase}/`) |
72 |
| - } |
73 |
| - }) |
74 | 147 | }
|
75 | 148 |
|
76 | 149 | module.exports.meta = require('./package.json')
|
0 commit comments