Skip to content

Commit

Permalink
feat(workbox): rewrite with better template support
Browse files Browse the repository at this point in the history
  • Loading branch information
Pooya Parsa committed Nov 16, 2017
1 parent 3c7aa7d commit b0af84e
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 30 deletions.
133 changes: 103 additions & 30 deletions packages/workbox/index.js
Original file line number Diff line number Diff line change
@@ -1,76 +1,149 @@
const path = require('path')
const swBuild = require('workbox-build')
const { readFileSync, writeFileSync } = require('fs')
const hashSum = require('hash-sum')
const debug = require('debug')('nuxt:pwa:workbox')

const fixUrl = url => url.replace(/\/\//g, '/').replace(':/', '://')
const isUrl = url => url.indexOf('http') === 0 || url.indexOf('//') === 0

module.exports = function nuxtWorkbox (options) {
// =============================================
// workboxModule
// =============================================

module.exports = function nuxtWorkbox (moduleOptions) {
const options = Object.assign({}, moduleOptions, this.options.workbox)
const ctx = { options }

if (this.options.dev) {
return
}

// routerBase and publicPath
this.nuxt.plugin('build', builder => {
debug('Adding workbox')
getRouterBase.call(this, ctx)
workboxInject.call(this, ctx)
emitAssets.call(this, ctx)
addTemplates.call(this, ctx)
})
}

// =============================================
// getRouterBase
// =============================================

function getRouterBase (ctx) {
const routerBase = this.options.router.base
let publicPath = fixUrl(`${routerBase}/${this.options.build.publicPath}`)
if (isUrl(this.options.build.publicPath)) { // CDN
publicPath = this.options.build.publicPath
if (publicPath.indexOf('//') === 0) {
publicPath = '/' + publicPath // escape fixUrl
publicPath = '/' + publicPath // Escape fixUrl
}
}

const swFileName = 'sw.js'
ctx.routerBase = routerBase
ctx.publicPath = publicPath
}

// =============================================
// addTemplates
// =============================================

function addTemplates (ctx) {
// Add sw.template.js
this.addTemplate({
src: path.resolve(__dirname, 'templates/sw.template.js'),
fileName: 'sw.template.js',
options: {
importPath: ctx.wbDst,
wb: ctx.workboxOptions
}
})

// Add sw.plugin.js
const swURL = `${ctx.routerBase}/${ctx.options.swURL || 'sw.js'}`
this.addPlugin({
src: path.resolve(__dirname, 'templates/sw.plugin.js'),
ssr: false,
fileName: 'sw.plugin.js',
options: {
swURL: fixUrl(swURL),
swScope: fixUrl(`${ctx.routerBase}/`)
}
})
}

// Use swBuild to generate sw file
// We set dest to static dir that is served as / to allow global sw scope
// =============================================
// emitAssets
// =============================================

function emitAssets (ctx) {
const assets = []
const emitAsset = (path, name, ext = 'js') => {
const source = readFileSync(path)
const hash = hashSum(source)
const dst = `${name}.${hash}.${ext}`
assets.push({source, dst})
return dst
}

// Write assets after build
this.nuxt.plugin('build', builder => {
builder.plugin('built', () => {
assets.forEach(({source, dst}) => {
writeFileSync(path.resolve(this.options.buildDir, 'dist', dst), source, 'utf-8')
})
})
})

// workbox.js
let wbPath = require.resolve('workbox-sw')
if (ctx.options.dev) {
wbPath = wbPath.replace(/prod/g, 'dev')
}
ctx.wbDst = fixUrl(ctx.publicPath + '/' + emitAsset(wbPath, 'workbox' + (ctx.options.dev ? '.dev' : '')))
}

// =============================================
// workboxInject
// =============================================

function workboxInject (ctx) {
// https://workboxjs.org/reference-docs/latest/module-workbox-build.html#.generateSW
const workboxOptions = Object.assign({
swDest: path.resolve(this.options.srcDir, 'static', swFileName),
ctx.workboxOptions = Object.assign({
swSrc: path.resolve(this.options.buildDir, 'sw.template.js'),
swDest: path.resolve(this.options.srcDir, 'static', 'sw.js'),
directoryIndex: '/',
cacheId: process.env.npm_package_name + '_' + process.env.npm_package_version,
clientsClaim: true,
globPatterns: ['**/*.{js,css}'],
globDirectory: path.resolve(this.options.buildDir, 'dist'),
modifyUrlPrefix: {
'': fixUrl(publicPath)
'': fixUrl(ctx.publicPath)
},
runtimeCaching: [
// Cache routes if offline
{
urlPattern: fixUrl(routerBase + '/**'),
urlPattern: fixUrl(ctx.routerBase + '/**'),
handler: 'networkFirst'
},
// Cache other _nuxt resources runtime
// They are hashed by webpack so are safe to loaded by cacheFirst handler
{
urlPattern: fixUrl(publicPath + '/**'),
urlPattern: fixUrl(ctx.publicPath + '/**'),
handler: 'cacheFirst'
}
]
}, options)
}, ctx.options)

// Use nuxt plugins to prevent race conditions with webpack plugin
// (https://github.com/nuxt-community/modules/issues/110)
this.nuxt.plugin('build', builder => {
builder.plugin('built', () => {
if (workboxOptions.swSrc) {
return swBuild.injectManifest(workboxOptions)
} else {
return swBuild.generateSW(workboxOptions)
}
const opts = Object.assign({}, ctx.workboxOptions)
delete opts.runtimeCaching
return swBuild.injectManifest(opts)
})
})

// Register runtime plugin
const swURL = `${routerBase}/${options.swURL || swFileName}`
this.addPlugin({
src: path.resolve(__dirname, 'plugin.js'),
ssr: false,
options: {
swURL: fixUrl(swURL),
swScope: fixUrl(`${routerBase}/`)
}
})
}

module.exports.meta = require('./package.json')
File renamed without changes.
13 changes: 13 additions & 0 deletions packages/workbox/templates/sw.template.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
importScripts('<%= options.importPath %>')

const workboxSW = new self.WorkboxSW({
cacheId: '<%= options.wb.cacheId %>',
clientsClaim: <%= options.wb.clientsClaim %>,
directoryIndex: '<%= options.wb.directoryIndex %>'
})

workboxSW.precache([])

<% options.wb.runtimeCaching.forEach(r => { %>
workboxSW.router.registerRoute('<%= r.urlPattern %>', workboxSW.strategies.<%= r.handler %>({}), 'GET')
<% }) %>

0 comments on commit b0af84e

Please sign in to comment.