Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: support build cache #371

Merged
merged 7 commits into from
Oct 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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