feat: add tappable hooks

This commit is contained in:
Pooya Parsa 2017-07-03 15:41:40 +04:30
parent a587b798e4
commit b209c80d2b
5 changed files with 63 additions and 23 deletions

View File

@ -43,6 +43,9 @@ export default class Builder extends Tapable {
// Helper to resolve build paths // Helper to resolve build paths
this.relativeToBuild = (...args) => relativeTo(this.options.buildDir, ...args) this.relativeToBuild = (...args) => relativeTo(this.options.buildDir, ...args)
// Call builder plugin on parent nuxt to notify all modules of builder existence
this.nuxt.applyPluginsAsync('builder', this).catch(this.nuxt.errorHandler)
this._buildStatus = STATUS.INITIAL this._buildStatus = STATUS.INITIAL
} }
@ -66,6 +69,8 @@ export default class Builder extends Tapable {
// Wait for nuxt ready // Wait for nuxt ready
await this.nuxt.ready() await this.nuxt.ready()
await this.applyPluginsAsync('build', this)
// Check if pages dir exists and warn if not // Check if pages dir exists and warn if not
this._nuxtPages = typeof this.options.build.createRoutes !== 'function' this._nuxtPages = typeof this.options.build.createRoutes !== 'function'
if (this._nuxtPages) { if (this._nuxtPages) {
@ -95,6 +100,8 @@ export default class Builder extends Tapable {
// Start webpack build // Start webpack build
await this.webpackBuild() await this.webpackBuild()
await this.applyPluginsAsync('built', this)
// Flag to set that building is done // Flag to set that building is done
this._buildStatus = STATUS.BUILD_DONE this._buildStatus = STATUS.BUILD_DONE
@ -173,6 +180,9 @@ export default class Builder extends Tapable {
} else { } else {
templateVars.router.routes = this.options.build.createRoutes(this.options.srcDir) templateVars.router.routes = this.options.build.createRoutes(this.options.srcDir)
} }
await this.applyPluginsAsync('extendRoutes', {routes: templateVars.router.routes, templateVars, r})
// router.extendRoutes method // router.extendRoutes method
if (typeof this.options.router.extendRoutes === 'function') { if (typeof this.options.router.extendRoutes === 'function') {
// let the user extend the routes // let the user extend the routes
@ -216,6 +226,8 @@ export default class Builder extends Tapable {
}, t) }, t)
})) }))
await this.applyPluginsAsync('generate', { builder: this, templatesFiles, templateVars })
// Interpret and move template files to .nuxt/ // Interpret and move template files to .nuxt/
await Promise.all(templatesFiles.map(async ({ src, dst, options, custom }) => { await Promise.all(templatesFiles.map(async ({ src, dst, options, custom }) => {
// Add template to watchers // Add template to watchers
@ -246,9 +258,11 @@ export default class Builder extends Tapable {
const dateFS = Date.now() / 1000 - 1000 const dateFS = Date.now() / 1000 - 1000
return utimes(path, dateFS, dateFS) return utimes(path, dateFS, dateFS)
})) }))
await this.applyPluginsAsync('generated', this)
} }
webpackBuild () { async webpackBuild () {
debug('Building files...') debug('Building files...')
let compilersOptions = [] let compilersOptions = []
@ -291,7 +305,7 @@ export default class Builder extends Tapable {
}) })
// Run after each compile // Run after each compile
this.compiler.plugin('done', stats => { this.compiler.plugin('done', async stats => {
// Don't reload failed builds // Don't reload failed builds
/* istanbul ignore if */ /* istanbul ignore if */
if (stats.hasErrors() || stats.hasWarnings()) { if (stats.hasErrors() || stats.hasWarnings()) {
@ -301,6 +315,8 @@ export default class Builder extends Tapable {
if (this.nuxt.renderer) { if (this.nuxt.renderer) {
this.nuxt.renderer.loadResources(sharedFS || fs) this.nuxt.renderer.loadResources(sharedFS || fs)
} }
await this.applyPluginsAsync('compiled', { builder: this, stats })
}) })
// Add dev Stuff // Add dev Stuff
@ -308,6 +324,8 @@ export default class Builder extends Tapable {
this.webpackDev() this.webpackDev()
} }
await this.applyPluginsAsync('compile', { builder: this, compiler: this.compiler })
// Start Builds // Start Builds
return parallel(this.compiler.compilers, compiler => new Promise((resolve, reject) => { return parallel(this.compiler.compilers, compiler => new Promise((resolve, reject) => {
if (this.options.dev) { if (this.options.dev) {

View File

@ -15,6 +15,15 @@ export default class Generator extends Tapable {
this.nuxt = nuxt this.nuxt = nuxt
this.options = nuxt.options this.options = nuxt.options
this.builder = builder this.builder = builder
// Set variables
this.generateRoutes = resolve(this.options.srcDir, 'static')
this.srcBuiltPath = resolve(this.options.buildDir, 'dist')
this.distPath = resolve(this.options.rootDir, this.options.generate.dir)
this.distNuxtPath = join(this.distPath, (isUrl(this.options.build.publicPath) ? '' : this.options.build.publicPath))
// Call generator plugin on parent nuxt to notify all modules of generator existence
this.nuxt.applyPluginsAsync('generator', this).catch(this.nuxt.errorHandler)
} }
async generate (doBuild = true) { async generate (doBuild = true) {
@ -22,12 +31,6 @@ export default class Generator extends Tapable {
let errors = [] let errors = []
let generateRoutes = [] let generateRoutes = []
// Set variables
let srcStaticPath = resolve(this.options.srcDir, 'static')
let srcBuiltPath = resolve(this.options.buildDir, 'dist')
let distPath = resolve(this.options.rootDir, this.options.generate.dir)
let distNuxtPath = join(distPath, (isUrl(this.options.build.publicPath) ? '' : this.options.build.publicPath))
// Wait for nuxt be ready // Wait for nuxt be ready
await this.nuxt.ready() await this.nuxt.ready()
@ -36,16 +39,18 @@ export default class Generator extends Tapable {
await this.builder.build() await this.builder.build()
} }
await this.applyPluginsAsync('before-generate', this)
// Clean destination folder // Clean destination folder
await remove(distPath) await remove(this.distPath)
debug('Destination folder cleaned') debug('Destination folder cleaned')
// Copy static and built files // Copy static and built files
/* istanbul ignore if */ /* istanbul ignore if */
if (fs.existsSync(srcStaticPath)) { if (fs.existsSync(this.generateRoutes)) {
await copy(srcStaticPath, distPath) await copy(this.generateRoutes, this.distPath)
} }
await copy(srcBuiltPath, distNuxtPath) await copy(this.srcBuiltPath, this.distNuxtPath)
debug('Static & build files copied') debug('Static & build files copied')
// Resolve config.generate.routes promises before generating the routes // Resolve config.generate.routes promises before generating the routes
@ -53,6 +58,7 @@ export default class Generator extends Tapable {
try { try {
console.log('Generating routes') // eslint-disable-line no-console console.log('Generating routes') // eslint-disable-line no-console
generateRoutes = await promisifyRoute(this.options.generate.routes || []) generateRoutes = await promisifyRoute(this.options.generate.routes || [])
await this.applyPluginsAsync('generateRoutes', {generator: this, generateRoutes})
} catch (e) { } catch (e) {
console.error('Could not resolve routes') // eslint-disable-line no-console console.error('Could not resolve routes') // eslint-disable-line no-console
console.error(e) // eslint-disable-line no-console console.error(e) // eslint-disable-line no-console
@ -85,6 +91,8 @@ export default class Generator extends Tapable {
let routes = (this.options.router.mode === 'hash') ? ['/'] : flatRoutes(this.options.router.routes) let routes = (this.options.router.mode === 'hash') ? ['/'] : flatRoutes(this.options.router.routes)
routes = decorateWithPayloads(routes) routes = decorateWithPayloads(routes)
await this.applyPluginsAsync('generate', {generator: this, routes})
while (routes.length) { while (routes.length) {
let n = 0 let n = 0
await Promise.all(routes.splice(0, 500).map(async ({ route, payload }) => { await Promise.all(routes.splice(0, 500).map(async ({ route, payload }) => {
@ -111,7 +119,7 @@ export default class Generator extends Tapable {
let path = join(route, sep, 'index.html') // /about -> /about/index.html let path = join(route, sep, 'index.html') // /about -> /about/index.html
path = (path === '/404/index.html') ? '/404.html' : path // /404 -> /404.html path = (path === '/404/index.html') ? '/404.html' : path // /404 -> /404.html
debug('Generate file: ' + path) debug('Generate file: ' + path)
path = join(distPath, path) path = join(this.distPath, path)
// Make sure the sub folders are created // Make sure the sub folders are created
await mkdirp(dirname(path)) await mkdirp(dirname(path))
await writeFile(path, html, 'utf8') await writeFile(path, html, 'utf8')
@ -120,7 +128,7 @@ export default class Generator extends Tapable {
// Add .nojekyll file to let Github Pages add the _nuxt/ folder // Add .nojekyll file to let Github Pages add the _nuxt/ folder
// https://help.github.com/articles/files-that-start-with-an-underscore-are-missing/ // https://help.github.com/articles/files-that-start-with-an-underscore-are-missing/
const nojekyllPath = resolve(distPath, '.nojekyll') const nojekyllPath = resolve(this.distPath, '.nojekyll')
writeFile(nojekyllPath, '') writeFile(nojekyllPath, '')
const duration = Math.round((Date.now() - s) / 100) / 10 const duration = Math.round((Date.now() - s) / 100) / 10
debug(`HTML Files generated in ${duration}s`) debug(`HTML Files generated in ${duration}s`)
@ -137,6 +145,8 @@ export default class Generator extends Tapable {
console.error('==== Error report ==== \n' + report.join('\n\n')) // eslint-disable-line no-console console.error('==== Error report ==== \n' + report.join('\n\n')) // eslint-disable-line no-console
} }
await this.applyPluginsAsync('generated', this)
return { duration, errors } return { duration, errors }
} }
} }

View File

@ -15,8 +15,9 @@ export default class ModuleContainer extends Tapable {
this.options = nuxt.options this.options = nuxt.options
this.requiredModules = [] this.requiredModules = []
this.nuxt.plugin('beforeInit', () => { this.nuxt.plugin('beforeInit', async () => {
return sequence(this.options.modules, this.addModule.bind(this)) await sequence(this.options.modules, this.addModule.bind(this))
await this.applyPluginsAsync('ready', this)
}) })
} }
@ -81,11 +82,14 @@ export default class ModuleContainer extends Tapable {
return this.addModule(moduleOpts, true) return this.addModule(moduleOpts, true)
} }
addModule (moduleOpts, requireOnce) { async addModule (moduleOpts, requireOnce) {
/* istanbul ignore if */ /* istanbul ignore if */
if (!moduleOpts) { if (!moduleOpts) {
return return
} }
await this.applyPluginsAsync('add', {moduleOpts, requireOnce})
// Allow using babel style array options // Allow using babel style array options
if (Array.isArray(moduleOpts)) { if (Array.isArray(moduleOpts)) {
moduleOpts = { moduleOpts = {
@ -93,12 +97,13 @@ export default class ModuleContainer extends Tapable {
options: moduleOpts[1] options: moduleOpts[1]
} }
} }
// Allows passing runtime options to each module // Allows passing runtime options to each module
const options = moduleOpts.options || (typeof moduleOpts === 'object' ? moduleOpts : {}) const options = moduleOpts.options || (typeof moduleOpts === 'object' ? moduleOpts : {})
const originalSrc = moduleOpts.src || moduleOpts const originalSrc = moduleOpts.src || moduleOpts
// Resolve module // Resolve module
let module = originalSrc let module = originalSrc
if (typeof module === 'string') { if (typeof module === 'string') {
module = require(this.nuxt.resolvePath(module)) module = require(this.nuxt.resolvePath(module))
} }
@ -108,6 +113,7 @@ export default class ModuleContainer extends Tapable {
if (typeof module !== 'function') { if (typeof module !== 'function') {
throw new Error(`[nuxt] Module ${JSON.stringify(originalSrc)} should export a function`) throw new Error(`[nuxt] Module ${JSON.stringify(originalSrc)} should export a function`)
} }
// Module meta // Module meta
if (!module.meta) { if (!module.meta) {
module.meta = {} module.meta = {}
@ -121,6 +127,7 @@ export default class ModuleContainer extends Tapable {
this.requiredModules.push(module.meta.name) this.requiredModules.push(module.meta.name)
} }
} }
// Call module with `this` context and pass options // Call module with `this` context and pass options
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const result = module.call(this, options, err => { const result = module.call(this, options, err => {

View File

@ -79,18 +79,23 @@ export default class Nuxt extends Tapable {
} }
errorHandler /* istanbul ignore next */() { errorHandler /* istanbul ignore next */() {
// Apply plugins
// eslint-disable-next-line no-console
this.applyPluginsAsync('error', ...arguments).catch(console.error)
// Silent // Silent
if (this.options.errorHandler === false) { if (this.options.errorHandler === false) {
return return
} }
// Custom errorHandler // Custom errorHandler
if (typeof this.options.errorHandler === 'function') { if (typeof this.options.errorHandler === 'function') {
return this.options.errorHandler.apply(this, arguments) return this.options.errorHandler.apply(this, arguments)
} }
// Default handler // Default handler
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.error.apply(this, arguments) console.error(...arguments)
process.exit(1)
} }
resolvePath (path) { resolvePath (path) {

View File

@ -71,7 +71,7 @@ export default class Renderer extends Tapable {
} }
// Setup all middleWare // Setup all middleWare
this.setupMiddleware() await this.setupMiddleware()
// Load error template // Load error template
const errorTemplatePath = resolve(this.options.buildDir, 'views/error.html') const errorTemplatePath = resolve(this.options.buildDir, 'views/error.html')
@ -165,9 +165,9 @@ export default class Renderer extends Tapable {
} }
} }
setupMiddleware () { async setupMiddleware () {
// Apply setupMiddleware from modules first // Apply setupMiddleware from modules first
this.applyPlugins('setupMiddleware', this.app) await this.applyPluginsAsync('setupMiddleware', this.app)
// Gzip middleware for production // Gzip middleware for production
if (!this.options.dev && this.options.render.gzip) { if (!this.options.dev && this.options.render.gzip) {