Skip to content

Commit

Permalink
fix: support build cache (#371)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
pi0 committed Oct 13, 2020
1 parent 0aceb41 commit 9a825c9
Show file tree
Hide file tree
Showing 11 changed files with 150 additions and 176 deletions.
71 changes: 27 additions & 44 deletions lib/icon/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,11 @@ const { fork } = require('child_process')
const { join } = require('path')
const fs = require('fs-extra')
const hasha = require('hasha')
const { joinUrl, getRouteParams, sizeName } = require('../utils')
const { joinUrl, getRouteParams, sizeName, emitAsset } = require('../utils')
const { version } = require('../../package.json')

module.exports = function (pwa) {
this.nuxt.hook('build:before', () => run.call(this, pwa))
}

async function run (pwa) {
const { publicPath } = getRouteParams(this.options)
module.exports = async function (nuxt, pwa, moduleContainer) {
const { publicPath } = getRouteParams(nuxt.options)

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

targetDir: 'icons',

Expand All @@ -55,12 +51,12 @@ async function run (pwa) {
}

// Find source
options.source = await findIcon.call(this, options)
options.source = await findIcon(nuxt, options)

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

Expand All @@ -77,42 +73,42 @@ async function run (pwa) {
}

// Generate icons
await generateIcons.call(this, options)
await generateIcons(nuxt, options)

// Add manifest
addManifest.call(this, options, pwa)
addManifest(nuxt, options, pwa)

// Add plugin
if (options.plugin) {
addPlugin.call(this, options)
addPlugin(nuxt, options, moduleContainer)
}

// Emit assets in background
emitAssets.call(this, options)
emitAssets(nuxt, options)
}

async function findIcon (options) {
function findIcon (nuxt, options) {
const iconSearchPath = [
options.source,
path.resolve(this.options.srcDir, this.options.dir.static, options.fileName),
path.resolve(this.options.srcDir, this.options.dir.assets, options.fileName)
path.resolve(nuxt.options.srcDir, nuxt.options.dir.static, options.fileName),
path.resolve(nuxt.options.srcDir, nuxt.options.dir.assets, options.fileName)
].filter(p => p)

for (const source of iconSearchPath) {
if (await fs.exists(source)) {
if (fs.existsSync(source)) {
return source
}
}
}

function addPlugin (options) {
function addPlugin (_nuxt, options, moduleContainer) {
const icons = {}
for (const asset of options._assets) {
icons[asset.name] = joinUrl(options.publicPath, asset.target)
}

if (options.plugin) {
this.addPlugin({
moduleContainer.addPlugin({
src: path.resolve(__dirname, './plugin.js'),
fileName: 'pwa/icons.js',
options: {
Expand All @@ -123,7 +119,7 @@ function addPlugin (options) {
}
}

async function generateIcons (options) {
async function generateIcons (_nuxt, options) {
// Get hash of source image
if (!options.iconHash) {
options.iconHash = await hasha.fromFile(options.source).then(h => h.substring(0, 6))
Expand Down Expand Up @@ -157,7 +153,7 @@ async function generateIcons (options) {
}
}

function addManifest (options, pwa) {
function addManifest (_nuxt, options, pwa) {
if (!pwa.manifest) {
pwa.manifest = {}
}
Expand All @@ -172,30 +168,17 @@ function addManifest (options, pwa) {
}
}

function emitAssets (options) {
function emitAssets (nuxt, options) {
// Start resize task in background
const resizePromise = resizeIcons.call(this, options)

// Register webpack plugin to emit icons
this.extendBuild((config, { isClient }) => {
if (isClient) {
config.plugins.push({
apply (compiler) {
compiler.hooks.emit.tapPromise('nuxt-pwa-icon', async (compilation) => {
await resizePromise
await Promise.all(options._assets.map(async ({ name, target }) => {
const srcFileName = path.join(options.cacheDir, `${name}.png`)
const src = await fs.readFile(srcFileName)
compilation.assets[target] = { source: () => src, size: () => src.length }
}))
})
}
})
}
})
const resizePromise = resizeIcons(nuxt, options)

for (const { name, target } of options._assets) {
const srcFileName = path.join(options.cacheDir, `${name}.png`)
emitAsset(nuxt, target, resizePromise.then(() => fs.readFile(srcFileName)))
}
}

async function resizeIcons (options) {
async function resizeIcons (_nuxt, options) {
const resizeOpts = JSON.stringify({
version,
input: options.source,
Expand All @@ -208,7 +191,7 @@ async function resizeIcons (options) {

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

if (await fs.exists(integrityFile)) {
if (fs.existsSync(integrityFile)) {
return
}
await fs.remove(options.cacheDir)
Expand Down
27 changes: 7 additions & 20 deletions lib/manifest/module.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
const hash = require('hasha')

const { joinUrl, getRouteParams } = require('../utils')
const { joinUrl, getRouteParams, emitAsset } = require('../utils')

module.exports = function nuxtManifest (pwa) {
this.nuxt.hook('build:before', () => addManifest.call(this, pwa))
}

function addManifest (pwa) {
const { routerBase, publicPath } = getRouteParams(this.options)
module.exports = function nuxtManifest (nuxt, pwa) {
const { routerBase, publicPath } = getRouteParams(nuxt.options)

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

// Merge final manifest into options.manifest for other modules
if (!this.options.manifest) {
this.options.manifest = {}
if (!nuxt.options.manifest) {
nuxt.options.manifest = {}
}
Object.assign(this.options.manifest, manifest)
Object.assign(nuxt.options.manifest, manifest)

// Register webpack plugin to emit manifest
const manifestSource = JSON.stringify(manifest, null, 2)
this.options.build.plugins.push({
apply (compiler) {
compiler.hooks.emit.tap('nuxt-pwa-manifest', (compilation) => {
compilation.assets[manifestFileName] = {
source: () => manifestSource,
size: () => manifestSource.length
}
})
}
})
emitAsset(nuxt, manifestFileName, manifestSource)

// Add manifest meta
const manifestMeta = { rel: 'manifest', href: joinUrl(options.publicPath, manifestFileName), hid: 'manifest' }
Expand Down
43 changes: 6 additions & 37 deletions lib/meta/module.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,9 @@
const { join, resolve } = require('path')
const { existsSync } = require('fs')
const { isUrl } = require('../utils')
const mergeMeta = require('./meta.merge')

module.exports = function nuxtMeta (pwa) {
const { nuxt } = this

nuxt.hook('build:before', () => {
generateMeta.call(this, pwa)
})

// SPA Support
nuxt.hook('build:done', () => {
SPASupport.call(this, pwa)
})
if (nuxt.options.target === 'static') {
nuxt.hook('generate:extendRoutes', () => SPASupport.call(this, pwa))
} else if (!nuxt.options._build) {
SPASupport.call(this, pwa)
}
}

function generateMeta (pwa) {
const { nuxt } = this
const nuxtMetaRuntime = require('./module.runtime')

module.exports = function nuxtMeta (nuxt, pwa, moduleContainer) {
// Defaults
const defaults = {
name: process.env.npm_package_name,
Expand Down Expand Up @@ -274,34 +254,23 @@ function generateMeta (pwa) {
head.link.push(pwa._manifestMeta)
}

this.addPlugin({
moduleContainer.addPlugin({
src: resolve(__dirname, './plugin.js'),
fileName: 'pwa/meta.js',
options: {}
})

this.addTemplate({
moduleContainer.addTemplate({
src: resolve(__dirname, 'meta.json'),
fileName: 'pwa/meta.json',
options: { head }
})

this.addTemplate({
moduleContainer.addTemplate({
src: resolve(__dirname, 'meta.merge.js'),
fileName: 'pwa/meta.merge.js',
options: { head }
})
}

function SPASupport (_pwa) {
const { nuxt } = this
const metaJSON = resolve(nuxt.options.buildDir, 'pwa/meta.json')
if (existsSync(metaJSON)) {
// eslint-disable-next-line no-console
console.debug('[PWA] Loading meta from ' + metaJSON)
mergeMeta(nuxt.options.head, require(metaJSON))
} else {
// eslint-disable-next-line no-console
console.warn('[PWA] Cannot load meta from ' + metaJSON)
}
nuxtMetaRuntime(nuxt)
}
14 changes: 14 additions & 0 deletions lib/meta/module.runtime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const { resolve } = require('path')
const { existsSync } = require('fs')
const mergeMeta = require('./meta.merge')

module.exports = function nuxtMetaRuntime (nuxt) {
const spaSupport = () => {
const metaJSON = resolve(nuxt.options.buildDir, 'pwa/meta.json')
if (existsSync(metaJSON)) {
mergeMeta(nuxt.options.head, require(metaJSON))
}
}

nuxt.hook('render:resourcesLoaded', () => spaSupport())
}
26 changes: 19 additions & 7 deletions lib/module.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,36 @@
module.exports = async function nuxtPWA (moduleOptions) {
const { nuxt } = this
const moduleContainer = this // TODO: remove dependency when module-utils

const isBuild = nuxt.options._build
const isGenerate = nuxt.options.target === 'static' && !nuxt.options.dev
const isRuntime = !isBuild && !isGenerate

if (isRuntime) {
// Load meta.json for SPA renderer
require('./meta/module.runtime')(nuxt)
return
}

const modules = ['icon', 'manifest', 'meta', 'workbox']

// Shared options context
this.options.pwa = { ...(this.options.pwa || {}), ...(moduleOptions || {}) }
const { pwa } = this.options
nuxt.options.pwa = { ...(nuxt.options.pwa || {}), ...(moduleOptions || {}) }
const { pwa } = nuxt.options

// Normalize options
for (const name of modules) {
// Skip disabled modules
if (pwa[name] === false || this.options[name] === false) {
if (pwa[name] === false || nuxt.options[name] === false) {
continue
}
// Ensure options are an object
if (pwa[name] === undefined) {
pwa[name] = {}
}
// Backward compatibility for top-level options
if (this.options[name] !== undefined) {
pwa[name] = { ...this.options[name], ...pwa[name] }
if (nuxt.options[name] !== undefined) {
pwa[name] = { ...nuxt.options[name], ...pwa[name] }
}
}

Expand All @@ -26,8 +39,7 @@ module.exports = async function nuxtPWA (moduleOptions) {
if (pwa[name] === false) {
continue
}
const moduleFn = require(`./${name}/module.js`)
await moduleFn.call(this, pwa)
await require(`./${name}/module.js`)(nuxt, pwa, moduleContainer)
}
}

Expand Down
28 changes: 25 additions & 3 deletions lib/utils/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
const path = require('path').posix
const { posix, resolve, dirname } = require('path')
const { writeFile, mkdirp } = require('fs-extra')

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

function joinUrl (...args) {
return path.join(...args).replace(':/', '://')
return posix.join(...args).replace(':/', '://')
}

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

async function writeData (path, data) {
path = path.split('?')[0]
await mkdirp(dirname(path))
await writeFile(path, await data)
}

function emitAsset (nuxt, fileName, data) {
const emitAsset = async () => {
const buildPath = resolve(nuxt.options.buildDir, 'dist/client', fileName)
await writeData(buildPath, data)
}

nuxt.hook('build:done', () => emitAsset())

const isGenerate = nuxt.options.target === 'static' && !nuxt.options.dev
if (isGenerate) {
nuxt.hook('modules:done', () => emitAsset())
}
}

module.exports = {
isUrl,
joinUrl,
getRouteParams,
startCase,
normalizeSize,
sizeName
sizeName,
emitAsset
}
Loading

0 comments on commit 9a825c9

Please sign in to comment.