refacto: Hooks

This commit is contained in:
Atinux 2017-10-30 18:41:22 +01:00
parent 83815de91b
commit 6648695015
5 changed files with 85 additions and 76 deletions

View File

@ -25,7 +25,7 @@ debug.color = 2 // Force green color
const glob = pify(Glob) const glob = pify(Glob)
export default class Builder extends Tapable { export default class Builder extends Tapable {
constructor (nuxt) { constructor(nuxt) {
super() super()
this.nuxt = nuxt this.nuxt = nuxt
this.isStatic = false // Flag to know if the build is for a generated app this.isStatic = false // Flag to know if the build is for a generated app
@ -57,9 +57,12 @@ export default class Builder extends Tapable {
this.vueLoader = vueLoaderConfig.bind(this) this.vueLoader = vueLoaderConfig.bind(this)
this._buildStatus = STATUS.INITIAL this._buildStatus = STATUS.INITIAL
// Call class hook
this.nuxt.applyPlugins('builder', this)
} }
get plugins () { get plugins() {
return this.options.plugins.map((p, i) => { return this.options.plugins.map((p, i) => {
if (typeof p === 'string') p = { src: p } if (typeof p === 'string') p = { src: p }
return { return {
@ -70,7 +73,7 @@ export default class Builder extends Tapable {
}) })
} }
vendor () { vendor() {
return [ return [
'vue', 'vue',
'vue-router', 'vue-router',
@ -79,7 +82,7 @@ export default class Builder extends Tapable {
].concat(this.options.build.vendor).filter(v => v) ].concat(this.options.build.vendor).filter(v => v)
} }
vendorEntries () { vendorEntries() {
// Used for dll // Used for dll
const vendor = this.vendor() const vendor = this.vendor()
const vendorEntries = {} const vendorEntries = {}
@ -94,11 +97,11 @@ export default class Builder extends Tapable {
return vendorEntries return vendorEntries
} }
forGenerate () { forGenerate() {
this.isStatic = true this.isStatic = true
} }
async build () { async build() {
// Avoid calling build() method multiple times when dev:true // Avoid calling build() method multiple times when dev:true
/* istanbul ignore if */ /* istanbul ignore if */
if (this._buildStatus === STATUS.BUILD_DONE && this.options.dev) { if (this._buildStatus === STATUS.BUILD_DONE && this.options.dev) {
@ -119,7 +122,7 @@ export default class Builder extends Tapable {
await this.nuxt.ready() await this.nuxt.ready()
// Wait for build plugins // Wait for build plugins
await this.nuxt.applyPluginsAsync('build', this) await this.applyPluginsAsync('build', this.options.build)
// Babel options // Babel options
this.babelOptions = _.defaults(this.options.build.babel, { this.babelOptions = _.defaults(this.options.build.babel, {
@ -177,15 +180,15 @@ 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
await this.applyPluginsAsync('built')
return this return this
} }
async generateRoutesAndFiles () { async generateRoutesAndFiles() {
debug('Generating files...') debug('Generating files...')
// -- Templates -- // -- Templates --
let templatesFiles = [ let templatesFiles = [
@ -264,7 +267,7 @@ export default class Builder extends Tapable {
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 }) await this.applyPluginsAsync('extendRoutes', templateVars.router.routes)
// router.extendRoutes method // router.extendRoutes method
if (typeof this.options.router.extendRoutes === 'function') { if (typeof this.options.router.extendRoutes === 'function') {
@ -332,7 +335,7 @@ export default class Builder extends Tapable {
} }
} }
await this.applyPluginsAsync('generate', { builder: this, templatesFiles, templateVars }) await this.applyPluginsAsync('generate', { templatesFiles, templateVars, resolve: r })
// 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 }) => {
@ -372,10 +375,10 @@ export default class Builder extends Tapable {
return utimes(path, dateFS, dateFS) return utimes(path, dateFS, dateFS)
})) }))
await this.applyPluginsAsync('generated', this) await this.applyPluginsAsync('generated')
} }
async webpackBuild () { async webpackBuild() {
debug('Building files...') debug('Building files...')
const compilersOptions = [] const compilersOptions = []
@ -433,11 +436,11 @@ export default class Builder extends Tapable {
// Start Builds // Start Builds
await sequence(this.compilers, (compiler) => new Promise(async (resolve, reject) => { await sequence(this.compilers, (compiler) => new Promise(async (resolve, reject) => {
const name = compiler.options.name const name = compiler.options.name
await this.applyPluginsAsync('compile', { builder: this, compiler, name }) await this.applyPluginsAsync('compile', { name, compiler })
// Resolve only when compiler emit done event // Resolve only when compiler emit done event
compiler.plugin('done', async (stats) => { compiler.plugin('done', async (stats) => {
await this.applyPluginsAsync('compiled', { builder: this, compiler, name, stats }) await this.applyPluginsAsync('compiled', { name, compiler, stats })
process.nextTick(resolve) process.nextTick(resolve)
}) })
// --- Dev Build --- // --- Dev Build ---
@ -484,7 +487,7 @@ export default class Builder extends Tapable {
} }
} }
webpackDev (compiler) { webpackDev(compiler) {
debug('Adding webpack middleware...') debug('Adding webpack middleware...')
// Create webpack dev middleware // Create webpack dev middleware
@ -516,7 +519,7 @@ export default class Builder extends Tapable {
this.watchFiles() this.watchFiles()
} }
watchFiles () { watchFiles() {
const patterns = [ const patterns = [
r(this.options.srcDir, 'layouts'), r(this.options.srcDir, 'layouts'),
r(this.options.srcDir, 'store'), r(this.options.srcDir, 'store'),

View File

@ -9,7 +9,7 @@ import Debug from 'debug'
const debug = Debug('nuxt:generate') const debug = Debug('nuxt:generate')
export default class Generator extends Tapable { export default class Generator extends Tapable {
constructor (nuxt, builder) { constructor(nuxt, builder) {
super() super()
this.nuxt = nuxt this.nuxt = nuxt
this.options = nuxt.options this.options = nuxt.options
@ -20,9 +20,12 @@ export default class Generator extends Tapable {
this.srcBuiltPath = resolve(this.options.buildDir, 'dist') this.srcBuiltPath = resolve(this.options.buildDir, 'dist')
this.distPath = resolve(this.options.rootDir, this.options.generate.dir) this.distPath = resolve(this.options.rootDir, this.options.generate.dir)
this.distNuxtPath = join(this.distPath, (isUrl(this.options.build.publicPath) ? '' : this.options.build.publicPath)) this.distNuxtPath = join(this.distPath, (isUrl(this.options.build.publicPath) ? '' : this.options.build.publicPath))
// Call class hook
this.nuxt.applyPlugins('generator', this)
} }
async generate ({ build = true, init = true } = {}) { async generate({ build = true, init = true } = {}) {
const s = Date.now() const s = Date.now()
let errors = [] let errors = []
@ -50,10 +53,8 @@ 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
throw e // eslint-disable-line no-unreachable throw e // eslint-disable-line no-unreachable
} }
} }
@ -62,7 +63,7 @@ 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 = this.decorateWithPayloads(routes, generateRoutes) routes = this.decorateWithPayloads(routes, generateRoutes)
await this.applyPluginsAsync('generate', { generator: this, routes }) await this.applyPluginsAsync('generate', routes)
// Start generate process // Start generate process
while (routes.length) { while (routes.length) {
@ -86,6 +87,8 @@ export default class Generator extends Tapable {
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`)
await this.applyPluginsAsync('generated', errors)
if (errors.length) { if (errors.length) {
const report = errors.map(({ type, route, error }) => { const report = errors.map(({ type, route, error }) => {
/* istanbul ignore if */ /* istanbul ignore if */
@ -98,12 +101,10 @@ 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 }
} }
async initDist () { async initDist() {
// Clean destination folder // Clean destination folder
await remove(this.distPath) await remove(this.distPath)
debug('Destination folder cleaned') debug('Destination folder cleaned')
@ -137,7 +138,7 @@ export default class Generator extends Tapable {
debug('Static & build files copied') debug('Static & build files copied')
} }
decorateWithPayloads (routes, generateRoutes) { decorateWithPayloads(routes, generateRoutes) {
let routeMap = {} let routeMap = {}
// Fill routeMap for known routes // Fill routeMap for known routes
routes.forEach((route) => { routes.forEach((route) => {
@ -148,7 +149,7 @@ export default class Generator extends Tapable {
}) })
// Fill routeMap with given generate.routes // Fill routeMap with given generate.routes
generateRoutes.forEach((route) => { generateRoutes.forEach((route) => {
// route is either a string or like {route : "/my_route/1"} // route is either a string or like { route : '/my_route/1', payload: {} }
const path = _.isString(route) ? route : route.route const path = _.isString(route) ? route : route.route
routeMap[path] = { routeMap[path] = {
route: path, route: path,
@ -158,7 +159,7 @@ export default class Generator extends Tapable {
return _.values(routeMap) return _.values(routeMap)
} }
async generateRoute ({ route, payload = {}, errors = [] }) { async generateRoute({ route, payload = {}, errors = [] }) {
let html let html
try { try {

View File

@ -9,19 +9,24 @@ import Debug from 'debug'
const debug = Debug('nuxt:module') const debug = Debug('nuxt:module')
export default class ModuleContainer extends Tapable { export default class ModuleContainer extends Tapable {
constructor (nuxt) { constructor(nuxt) {
super() super()
this.nuxt = nuxt this.nuxt = nuxt
this.options = nuxt.options this.options = nuxt.options
this.requiredModules = [] this.requiredModules = []
// Call class hook
console.log('call module container')
this.nuxt.applyPlugins('moduleContainer', this)
} }
async _ready () { async ready() {
// Load every module in sequence
await sequence(this.options.modules, this.addModule.bind(this)) await sequence(this.options.modules, this.addModule.bind(this))
await this.applyPluginsAsync('ready', this) await this.applyPluginsAsync('ready')
} }
addVendor (vendor) { addVendor(vendor) {
/* istanbul ignore if */ /* istanbul ignore if */
if (!vendor) { if (!vendor) {
return return
@ -29,7 +34,7 @@ export default class ModuleContainer extends Tapable {
this.options.build.vendor = uniq(this.options.build.vendor.concat(vendor)) this.options.build.vendor = uniq(this.options.build.vendor.concat(vendor))
} }
addTemplate (template) { addTemplate(template) {
/* istanbul ignore if */ /* istanbul ignore if */
if (!template) { if (!template) {
return return
@ -56,7 +61,7 @@ export default class ModuleContainer extends Tapable {
return templateObj return templateObj
} }
addPlugin (template) { addPlugin(template) {
const { dst } = this.addTemplate(template) const { dst } = this.addTemplate(template)
// Add to nuxt plugins // Add to nuxt plugins
this.options.plugins.unshift({ this.options.plugins.unshift({
@ -65,31 +70,29 @@ export default class ModuleContainer extends Tapable {
}) })
} }
addServerMiddleware (middleware) { addServerMiddleware(middleware) {
this.options.serverMiddleware.push(middleware) this.options.serverMiddleware.push(middleware)
} }
extendBuild (fn) { extendBuild(fn) {
this.options.build.extend = chainFn(this.options.build.extend, fn) this.options.build.extend = chainFn(this.options.build.extend, fn)
} }
extendRoutes (fn) { extendRoutes(fn) {
this.options.router.extendRoutes = chainFn(this.options.router.extendRoutes, fn) this.options.router.extendRoutes = chainFn(this.options.router.extendRoutes, fn)
} }
requireModule (moduleOpts) { requireModule(moduleOpts) {
// Require once // Require once
return this.addModule(moduleOpts, true) return this.addModule(moduleOpts, true)
} }
async 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 = {
@ -129,13 +132,13 @@ export default class ModuleContainer extends Tapable {
} }
// Call module with `this` context and pass options // Call module with `this` context and pass options
return new Promise((resolve, reject) => { const m = await new Promise((resolve, reject) => {
const result = module.call(this, options, err => { const result = module.call(this, options, (err, m) => {
/* istanbul ignore if */ /* istanbul ignore if */
if (err) { if (err) {
return reject(err) return reject(err)
} }
resolve(module) resolve(m)
}) })
// If module send back a promise // If module send back a promise
if (result && result.then instanceof Function) { if (result && result.then instanceof Function) {
@ -146,5 +149,6 @@ export default class ModuleContainer extends Tapable {
return resolve(module) return resolve(module)
} }
}) })
await this.applyPluginsAsync('module', { meta: module.meta, module: m })
} }
} }

View File

@ -12,10 +12,10 @@ const debug = Debug('nuxt:')
debug.color = 5 debug.color = 5
export default class Nuxt extends Tapable { export default class Nuxt extends Tapable {
constructor (_options = {}) { constructor(options = {}) {
super() super()
this.options = Options.from(_options) this.options = Options.from(options)
// Paths for resolving requires from `rootDir` // Paths for resolving requires from `rootDir`
this.nodeModulePaths = Module._nodeModulePaths(this.options.rootDir) this.nodeModulePaths = Module._nodeModulePaths(this.options.rootDir)
@ -35,20 +35,20 @@ export default class Nuxt extends Tapable {
this._ready = this.ready().catch(this.errorHandler) this._ready = this.ready().catch(this.errorHandler)
} }
async ready () { async ready() {
if (this._ready) { if (this._ready) {
return this._ready return this._ready
} }
await this.moduleContainer._ready() await this.moduleContainer.ready()
await this.applyPluginsAsync('ready') await this.applyPluginsAsync('ready')
await this.renderer._ready() await this.renderer.ready()
this.initialized = true this.initialized = true
return this return this
} }
listen (port = 3000, host = 'localhost') { listen(port = 3000, host = 'localhost') {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const server = this.renderer.app.listen({ port, host, exclusive: false }, err => { const server = this.renderer.app.listen({ port, host, exclusive: false }, err => {
/* istanbul ignore if */ /* istanbul ignore if */
@ -81,7 +81,7 @@ export default class Nuxt extends Tapable {
}) })
} }
errorHandler /* istanbul ignore next */() { errorHandler/* istanbul ignore next */() {
// Apply plugins // Apply plugins
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
this.applyPluginsAsync('error', ...arguments).catch(console.error) this.applyPluginsAsync('error', ...arguments).catch(console.error)
@ -101,7 +101,7 @@ export default class Nuxt extends Tapable {
console.error(...arguments) console.error(...arguments)
} }
resolvePath (path) { resolvePath(path) {
// Try to resolve using NPM resolve path first // Try to resolve using NPM resolve path first
try { try {
let resolvedPath = Module._resolveFilename(path, { paths: this.nodeModulePaths }) let resolvedPath = Module._resolveFilename(path, { paths: this.nodeModulePaths })
@ -118,7 +118,7 @@ export default class Nuxt extends Tapable {
return resolve(this.options.srcDir, path) return resolve(this.options.srcDir, path)
} }
async close (callback) { async close(callback) {
await this.applyPluginsAsync('close') await this.applyPluginsAsync('close')
/* istanbul ignore if */ /* istanbul ignore if */

View File

@ -25,7 +25,7 @@ setAnsiColors(ansiHTML)
let jsdom = null let jsdom = null
export default class Renderer extends Tapable { export default class Renderer extends Tapable {
constructor (nuxt) { constructor(nuxt) {
super() super()
this.nuxt = nuxt this.nuxt = nuxt
this.options = nuxt.options this.options = nuxt.options
@ -49,11 +49,12 @@ export default class Renderer extends Tapable {
spaTemplate: null, spaTemplate: null,
errorTemplate: parseTemplate('Nuxt.js Internal Server Error') errorTemplate: parseTemplate('Nuxt.js Internal Server Error')
} }
// Call class hook
this.nuxt.applyPlugins('renderer', this)
} }
async _ready () { async ready() {
await this.nuxt.applyPluginsAsync('renderer', this)
// Setup nuxt middleware // Setup nuxt middleware
await this.setupMiddleware() await this.setupMiddleware()
@ -63,10 +64,10 @@ export default class Renderer extends Tapable {
} }
// Call ready plugin // Call ready plugin
await this.applyPluginsAsync('ready', this) await this.applyPluginsAsync('ready')
} }
async loadResources (_fs = fs) { async loadResources(_fs = fs) {
let distPath = resolve(this.options.buildDir, 'dist') let distPath = resolve(this.options.buildDir, 'dist')
let updated = [] let updated = []
@ -115,11 +116,11 @@ export default class Renderer extends Tapable {
} }
} }
get noSSR () { get noSSR() {
return this.options.render.ssr === false return this.options.render.ssr === false
} }
get isReady () { get isReady() {
if (this.noSSR) { if (this.noSSR) {
return Boolean(this.resources.spaTemplate) return Boolean(this.resources.spaTemplate)
} }
@ -127,7 +128,7 @@ export default class Renderer extends Tapable {
return Boolean(this.bundleRenderer && this.resources.ssrTemplate) return Boolean(this.bundleRenderer && this.resources.ssrTemplate)
} }
get isResourcesAvailable () { get isResourcesAvailable() {
// Required for both // Required for both
if (!this.resources.clientManifest) { if (!this.resources.clientManifest) {
return false return false
@ -142,7 +143,7 @@ export default class Renderer extends Tapable {
return Boolean(this.resources.ssrTemplate && this.resources.serverBundle) return Boolean(this.resources.ssrTemplate && this.resources.serverBundle)
} }
createRenderer () { createRenderer() {
// Ensure resources are available // Ensure resources are available
if (!this.isResourcesAvailable) { if (!this.isResourcesAvailable) {
return return
@ -164,7 +165,7 @@ export default class Renderer extends Tapable {
}, this.options.render.bundleRenderer)) }, this.options.render.bundleRenderer))
} }
useMiddleware (m) { useMiddleware(m) {
// Resolve // Resolve
const $m = m const $m = m
let src let src
@ -188,11 +189,11 @@ export default class Renderer extends Tapable {
this.app.use(path, handler) this.app.use(path, handler)
} }
get publicPath () { get publicPath() {
return isUrl(this.options.build.publicPath) ? Options.defaults.build.publicPath : this.options.build.publicPath return isUrl(this.options.build.publicPath) ? Options.defaults.build.publicPath : this.options.build.publicPath
} }
async setupMiddleware () { async setupMiddleware() {
// Apply setupMiddleware from modules first // Apply setupMiddleware from modules first
await this.applyPluginsAsync('setupMiddleware', this.app) await this.applyPluginsAsync('setupMiddleware', this.app)
@ -229,7 +230,7 @@ export default class Renderer extends Tapable {
if (this.options.debug) { if (this.options.debug) {
this.useMiddleware({ this.useMiddleware({
path: '_open', path: '_open',
handler (req, res) { handler(req, res) {
// Lazy load open-in-editor // Lazy load open-in-editor
const openInEditor = require('open-in-editor') const openInEditor = require('open-in-editor')
const editor = openInEditor.configure(_this.options.editor) const editor = openInEditor.configure(_this.options.editor)
@ -280,9 +281,10 @@ export default class Renderer extends Tapable {
this.useMiddleware(this.errorMiddleware.bind(this)) this.useMiddleware(this.errorMiddleware.bind(this))
} }
async nuxtMiddleware (req, res, next) { async nuxtMiddleware(req, res, next) {
// Get context // Get context
const context = getContext(req, res) const context = getContext(req, res)
res.statusCode = 200 res.statusCode = 200
try { try {
const { html, error, redirected, resourceHints } = await this.renderRoute(req.url, context) const { html, error, redirected, resourceHints } = await this.renderRoute(req.url, context)
@ -338,7 +340,7 @@ export default class Renderer extends Tapable {
} }
} }
errorMiddleware (err, req, res, next) { errorMiddleware(err, req, res, next) {
// ensure statusCode, message and name fields // ensure statusCode, message and name fields
err.statusCode = err.statusCode || 500 err.statusCode = err.statusCode || 500
err.message = err.message || 'Nuxt Server Error' err.message = err.message || 'Nuxt Server Error'
@ -390,7 +392,7 @@ export default class Renderer extends Tapable {
} }
} }
async readSource (frame) { async readSource(frame) {
const serverBundle = this.resources.serverBundle const serverBundle = this.resources.serverBundle
// Remove webpack:/// & query string from the end // Remove webpack:/// & query string from the end
@ -468,7 +470,7 @@ export default class Renderer extends Tapable {
} }
} }
async renderRoute (url, context = {}) { async renderRoute(url, context = {}) {
/* istanbul ignore if */ /* istanbul ignore if */
if (!this.isReady) { if (!this.isReady) {
return new Promise(resolve => { return new Promise(resolve => {
@ -546,7 +548,7 @@ export default class Renderer extends Tapable {
} }
} }
async renderAndGetWindow (url, opts = {}) { async renderAndGetWindow(url, opts = {}) {
/* istanbul ignore if */ /* istanbul ignore if */
if (!jsdom) { if (!jsdom) {
try { try {
@ -561,10 +563,9 @@ export default class Renderer extends Tapable {
let options = { let options = {
resources: 'usable', // load subresources (https://github.com/tmpvar/jsdom#loading-subresources) resources: 'usable', // load subresources (https://github.com/tmpvar/jsdom#loading-subresources)
runScripts: 'dangerously', runScripts: 'dangerously',
beforeParse (window) { beforeParse(window) {
// Mock window.scrollTo // Mock window.scrollTo
window.scrollTo = () => { window.scrollTo = () => {}
}
} }
} }
if (opts.virtualConsole !== false) { if (opts.virtualConsole !== false) {