Skip to content

Commit 9a825c9

Browse files
authored
fix: support build cache (#371)
* fix: add compatibility for static build cache * refactor: use existsSync * fix: call nuxtMetaRuntime with main module too * improve runtime meta module * use emitAsset utility * add emitAsset * refactor: avoid using bound fns
1 parent 0aceb41 commit 9a825c9

File tree

11 files changed

+150
-176
lines changed

11 files changed

+150
-176
lines changed

lib/icon/module.js

Lines changed: 27 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,11 @@ const { fork } = require('child_process')
33
const { join } = require('path')
44
const fs = require('fs-extra')
55
const hasha = require('hasha')
6-
const { joinUrl, getRouteParams, sizeName } = require('../utils')
6+
const { joinUrl, getRouteParams, sizeName, emitAsset } = require('../utils')
77
const { version } = require('../../package.json')
88

9-
module.exports = function (pwa) {
10-
this.nuxt.hook('build:before', () => run.call(this, pwa))
11-
}
12-
13-
async function run (pwa) {
14-
const { publicPath } = getRouteParams(this.options)
9+
module.exports = async function (nuxt, pwa, moduleContainer) {
10+
const { publicPath } = getRouteParams(nuxt.options)
1511

1612
// Defaults
1713
const defaults = {
@@ -33,7 +29,7 @@ async function run (pwa) {
3329
fileName: 'icon.png',
3430
source: null,
3531
purpose: ['any', 'maskable'],
36-
cacheDir: join(this.nuxt.options.rootDir, 'node_modules/.cache/pwa/icon'),
32+
cacheDir: join(nuxt.options.rootDir, 'node_modules/.cache/pwa/icon'),
3733

3834
targetDir: 'icons',
3935

@@ -55,12 +51,12 @@ async function run (pwa) {
5551
}
5652

5753
// Find source
58-
options.source = await findIcon.call(this, options)
54+
options.source = await findIcon(nuxt, options)
5955

6056
// Disable module if no icon specified
6157
if (!options.source) {
6258
// eslint-disable-next-line no-console
63-
console.warn('[pwa] [icon] Icon not found in ' + path.resolve(this.options.srcDir, this.options.dir.static, options.fileName))
59+
console.warn('[pwa] [icon] Icon not found in ' + path.resolve(nuxt.options.srcDir, nuxt.options.dir.static, options.fileName))
6460
return
6561
}
6662

@@ -77,42 +73,42 @@ async function run (pwa) {
7773
}
7874

7975
// Generate icons
80-
await generateIcons.call(this, options)
76+
await generateIcons(nuxt, options)
8177

8278
// Add manifest
83-
addManifest.call(this, options, pwa)
79+
addManifest(nuxt, options, pwa)
8480

8581
// Add plugin
8682
if (options.plugin) {
87-
addPlugin.call(this, options)
83+
addPlugin(nuxt, options, moduleContainer)
8884
}
8985

9086
// Emit assets in background
91-
emitAssets.call(this, options)
87+
emitAssets(nuxt, options)
9288
}
9389

94-
async function findIcon (options) {
90+
function findIcon (nuxt, options) {
9591
const iconSearchPath = [
9692
options.source,
97-
path.resolve(this.options.srcDir, this.options.dir.static, options.fileName),
98-
path.resolve(this.options.srcDir, this.options.dir.assets, options.fileName)
93+
path.resolve(nuxt.options.srcDir, nuxt.options.dir.static, options.fileName),
94+
path.resolve(nuxt.options.srcDir, nuxt.options.dir.assets, options.fileName)
9995
].filter(p => p)
10096

10197
for (const source of iconSearchPath) {
102-
if (await fs.exists(source)) {
98+
if (fs.existsSync(source)) {
10399
return source
104100
}
105101
}
106102
}
107103

108-
function addPlugin (options) {
104+
function addPlugin (_nuxt, options, moduleContainer) {
109105
const icons = {}
110106
for (const asset of options._assets) {
111107
icons[asset.name] = joinUrl(options.publicPath, asset.target)
112108
}
113109

114110
if (options.plugin) {
115-
this.addPlugin({
111+
moduleContainer.addPlugin({
116112
src: path.resolve(__dirname, './plugin.js'),
117113
fileName: 'pwa/icons.js',
118114
options: {
@@ -123,7 +119,7 @@ function addPlugin (options) {
123119
}
124120
}
125121

126-
async function generateIcons (options) {
122+
async function generateIcons (_nuxt, options) {
127123
// Get hash of source image
128124
if (!options.iconHash) {
129125
options.iconHash = await hasha.fromFile(options.source).then(h => h.substring(0, 6))
@@ -157,7 +153,7 @@ async function generateIcons (options) {
157153
}
158154
}
159155

160-
function addManifest (options, pwa) {
156+
function addManifest (_nuxt, options, pwa) {
161157
if (!pwa.manifest) {
162158
pwa.manifest = {}
163159
}
@@ -172,30 +168,17 @@ function addManifest (options, pwa) {
172168
}
173169
}
174170

175-
function emitAssets (options) {
171+
function emitAssets (nuxt, options) {
176172
// Start resize task in background
177-
const resizePromise = resizeIcons.call(this, options)
178-
179-
// Register webpack plugin to emit icons
180-
this.extendBuild((config, { isClient }) => {
181-
if (isClient) {
182-
config.plugins.push({
183-
apply (compiler) {
184-
compiler.hooks.emit.tapPromise('nuxt-pwa-icon', async (compilation) => {
185-
await resizePromise
186-
await Promise.all(options._assets.map(async ({ name, target }) => {
187-
const srcFileName = path.join(options.cacheDir, `${name}.png`)
188-
const src = await fs.readFile(srcFileName)
189-
compilation.assets[target] = { source: () => src, size: () => src.length }
190-
}))
191-
})
192-
}
193-
})
194-
}
195-
})
173+
const resizePromise = resizeIcons(nuxt, options)
174+
175+
for (const { name, target } of options._assets) {
176+
const srcFileName = path.join(options.cacheDir, `${name}.png`)
177+
emitAsset(nuxt, target, resizePromise.then(() => fs.readFile(srcFileName)))
178+
}
196179
}
197180

198-
async function resizeIcons (options) {
181+
async function resizeIcons (_nuxt, options) {
199182
const resizeOpts = JSON.stringify({
200183
version,
201184
input: options.source,
@@ -208,7 +191,7 @@ async function resizeIcons (options) {
208191

209192
const integrityFile = path.join(options.cacheDir, '.' + hasha(resizeOpts).substr(0, 8))
210193

211-
if (await fs.exists(integrityFile)) {
194+
if (fs.existsSync(integrityFile)) {
212195
return
213196
}
214197
await fs.remove(options.cacheDir)

lib/manifest/module.js

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
const hash = require('hasha')
22

3-
const { joinUrl, getRouteParams } = require('../utils')
3+
const { joinUrl, getRouteParams, emitAsset } = require('../utils')
44

5-
module.exports = function nuxtManifest (pwa) {
6-
this.nuxt.hook('build:before', () => addManifest.call(this, pwa))
7-
}
8-
9-
function addManifest (pwa) {
10-
const { routerBase, publicPath } = getRouteParams(this.options)
5+
module.exports = function nuxtManifest (nuxt, pwa) {
6+
const { routerBase, publicPath } = getRouteParams(nuxt.options)
117

128
// Combine sources
139
const defaults = {
@@ -40,23 +36,14 @@ function addManifest (pwa) {
4036
.replace('[ext]', options.useWebmanifestExtension ? 'webmanifest' : 'json')
4137

4238
// Merge final manifest into options.manifest for other modules
43-
if (!this.options.manifest) {
44-
this.options.manifest = {}
39+
if (!nuxt.options.manifest) {
40+
nuxt.options.manifest = {}
4541
}
46-
Object.assign(this.options.manifest, manifest)
42+
Object.assign(nuxt.options.manifest, manifest)
4743

4844
// Register webpack plugin to emit manifest
4945
const manifestSource = JSON.stringify(manifest, null, 2)
50-
this.options.build.plugins.push({
51-
apply (compiler) {
52-
compiler.hooks.emit.tap('nuxt-pwa-manifest', (compilation) => {
53-
compilation.assets[manifestFileName] = {
54-
source: () => manifestSource,
55-
size: () => manifestSource.length
56-
}
57-
})
58-
}
59-
})
46+
emitAsset(nuxt, manifestFileName, manifestSource)
6047

6148
// Add manifest meta
6249
const manifestMeta = { rel: 'manifest', href: joinUrl(options.publicPath, manifestFileName), hid: 'manifest' }

lib/meta/module.js

Lines changed: 6 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,9 @@
11
const { join, resolve } = require('path')
22
const { existsSync } = require('fs')
33
const { isUrl } = require('../utils')
4-
const mergeMeta = require('./meta.merge')
5-
6-
module.exports = function nuxtMeta (pwa) {
7-
const { nuxt } = this
8-
9-
nuxt.hook('build:before', () => {
10-
generateMeta.call(this, pwa)
11-
})
12-
13-
// SPA Support
14-
nuxt.hook('build:done', () => {
15-
SPASupport.call(this, pwa)
16-
})
17-
if (nuxt.options.target === 'static') {
18-
nuxt.hook('generate:extendRoutes', () => SPASupport.call(this, pwa))
19-
} else if (!nuxt.options._build) {
20-
SPASupport.call(this, pwa)
21-
}
22-
}
23-
24-
function generateMeta (pwa) {
25-
const { nuxt } = this
4+
const nuxtMetaRuntime = require('./module.runtime')
265

6+
module.exports = function nuxtMeta (nuxt, pwa, moduleContainer) {
277
// Defaults
288
const defaults = {
299
name: process.env.npm_package_name,
@@ -274,34 +254,23 @@ function generateMeta (pwa) {
274254
head.link.push(pwa._manifestMeta)
275255
}
276256

277-
this.addPlugin({
257+
moduleContainer.addPlugin({
278258
src: resolve(__dirname, './plugin.js'),
279259
fileName: 'pwa/meta.js',
280260
options: {}
281261
})
282262

283-
this.addTemplate({
263+
moduleContainer.addTemplate({
284264
src: resolve(__dirname, 'meta.json'),
285265
fileName: 'pwa/meta.json',
286266
options: { head }
287267
})
288268

289-
this.addTemplate({
269+
moduleContainer.addTemplate({
290270
src: resolve(__dirname, 'meta.merge.js'),
291271
fileName: 'pwa/meta.merge.js',
292272
options: { head }
293273
})
294-
}
295274

296-
function SPASupport (_pwa) {
297-
const { nuxt } = this
298-
const metaJSON = resolve(nuxt.options.buildDir, 'pwa/meta.json')
299-
if (existsSync(metaJSON)) {
300-
// eslint-disable-next-line no-console
301-
console.debug('[PWA] Loading meta from ' + metaJSON)
302-
mergeMeta(nuxt.options.head, require(metaJSON))
303-
} else {
304-
// eslint-disable-next-line no-console
305-
console.warn('[PWA] Cannot load meta from ' + metaJSON)
306-
}
275+
nuxtMetaRuntime(nuxt)
307276
}

lib/meta/module.runtime.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const { resolve } = require('path')
2+
const { existsSync } = require('fs')
3+
const mergeMeta = require('./meta.merge')
4+
5+
module.exports = function nuxtMetaRuntime (nuxt) {
6+
const spaSupport = () => {
7+
const metaJSON = resolve(nuxt.options.buildDir, 'pwa/meta.json')
8+
if (existsSync(metaJSON)) {
9+
mergeMeta(nuxt.options.head, require(metaJSON))
10+
}
11+
}
12+
13+
nuxt.hook('render:resourcesLoaded', () => spaSupport())
14+
}

lib/module.js

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,36 @@
11
module.exports = async function nuxtPWA (moduleOptions) {
2+
const { nuxt } = this
3+
const moduleContainer = this // TODO: remove dependency when module-utils
4+
5+
const isBuild = nuxt.options._build
6+
const isGenerate = nuxt.options.target === 'static' && !nuxt.options.dev
7+
const isRuntime = !isBuild && !isGenerate
8+
9+
if (isRuntime) {
10+
// Load meta.json for SPA renderer
11+
require('./meta/module.runtime')(nuxt)
12+
return
13+
}
14+
215
const modules = ['icon', 'manifest', 'meta', 'workbox']
316

417
// Shared options context
5-
this.options.pwa = { ...(this.options.pwa || {}), ...(moduleOptions || {}) }
6-
const { pwa } = this.options
18+
nuxt.options.pwa = { ...(nuxt.options.pwa || {}), ...(moduleOptions || {}) }
19+
const { pwa } = nuxt.options
720

821
// Normalize options
922
for (const name of modules) {
1023
// Skip disabled modules
11-
if (pwa[name] === false || this.options[name] === false) {
24+
if (pwa[name] === false || nuxt.options[name] === false) {
1225
continue
1326
}
1427
// Ensure options are an object
1528
if (pwa[name] === undefined) {
1629
pwa[name] = {}
1730
}
1831
// Backward compatibility for top-level options
19-
if (this.options[name] !== undefined) {
20-
pwa[name] = { ...this.options[name], ...pwa[name] }
32+
if (nuxt.options[name] !== undefined) {
33+
pwa[name] = { ...nuxt.options[name], ...pwa[name] }
2134
}
2235
}
2336

@@ -26,8 +39,7 @@ module.exports = async function nuxtPWA (moduleOptions) {
2639
if (pwa[name] === false) {
2740
continue
2841
}
29-
const moduleFn = require(`./${name}/module.js`)
30-
await moduleFn.call(this, pwa)
42+
await require(`./${name}/module.js`)(nuxt, pwa, moduleContainer)
3143
}
3244
}
3345

lib/utils/index.js

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
const path = require('path').posix
1+
const { posix, resolve, dirname } = require('path')
2+
const { writeFile, mkdirp } = require('fs-extra')
23

34
function isUrl (url) {
45
return url.indexOf('http') === 0 || url.indexOf('//') === 0
56
}
67

78
function joinUrl (...args) {
8-
return path.join(...args).replace(':/', '://')
9+
return posix.join(...args).replace(':/', '://')
910
}
1011

1112
function normalizeSize (size) {
@@ -48,11 +49,32 @@ function startCase (str) {
4849
return typeof str === 'string' ? str[0].toUpperCase() + str.substr(1) : str
4950
}
5051

52+
async function writeData (path, data) {
53+
path = path.split('?')[0]
54+
await mkdirp(dirname(path))
55+
await writeFile(path, await data)
56+
}
57+
58+
function emitAsset (nuxt, fileName, data) {
59+
const emitAsset = async () => {
60+
const buildPath = resolve(nuxt.options.buildDir, 'dist/client', fileName)
61+
await writeData(buildPath, data)
62+
}
63+
64+
nuxt.hook('build:done', () => emitAsset())
65+
66+
const isGenerate = nuxt.options.target === 'static' && !nuxt.options.dev
67+
if (isGenerate) {
68+
nuxt.hook('modules:done', () => emitAsset())
69+
}
70+
}
71+
5172
module.exports = {
5273
isUrl,
5374
joinUrl,
5475
getRouteParams,
5576
startCase,
5677
normalizeSize,
57-
sizeName
78+
sizeName,
79+
emitAsset
5880
}

0 commit comments

Comments
 (0)