mirror of
https://github.com/nuxt/nuxt.git
synced 2025-01-30 23:32:38 +00:00
refactor(core): use hable (#6271)
This commit is contained in:
parent
81b92b6395
commit
9ad02c4017
@ -17,6 +17,7 @@
|
|||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
"esm": "^3.2.25",
|
"esm": "^3.2.25",
|
||||||
"fs-extra": "^8.1.0",
|
"fs-extra": "^8.1.0",
|
||||||
|
"hable": "^2.2.1",
|
||||||
"hash-sum": "^2.0.0",
|
"hash-sum": "^2.0.0",
|
||||||
"std-env": "^2.2.1"
|
"std-env": "^2.2.1"
|
||||||
},
|
},
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
|
|
||||||
import consola from 'consola'
|
|
||||||
|
|
||||||
import { sequence } from '@nuxt/utils'
|
|
||||||
|
|
||||||
export default class Hookable {
|
|
||||||
constructor () {
|
|
||||||
this._hooks = {}
|
|
||||||
this._deprecatedHooks = {}
|
|
||||||
|
|
||||||
this.hook = this.hook.bind(this)
|
|
||||||
this.callHook = this.callHook.bind(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
hook (name, fn) {
|
|
||||||
if (!name || typeof fn !== 'function') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._deprecatedHooks[name]) {
|
|
||||||
consola.warn(`${name} hook has been deprecated, please use ${this._deprecatedHooks[name]}`)
|
|
||||||
name = this._deprecatedHooks[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
this._hooks[name] = this._hooks[name] || []
|
|
||||||
this._hooks[name].push(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
async callHook (name, ...args) {
|
|
||||||
if (!this._hooks[name]) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await sequence(this._hooks[name], fn => fn(...args))
|
|
||||||
} catch (err) {
|
|
||||||
name !== 'error' && await this.callHook('error', err)
|
|
||||||
consola.fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clearHook (name) {
|
|
||||||
if (name) {
|
|
||||||
delete this._hooks[name]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clearHooks () {
|
|
||||||
this._hooks = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
flatHooks (configHooks, hooks = {}, parentName) {
|
|
||||||
Object.keys(configHooks).forEach((key) => {
|
|
||||||
const subHook = configHooks[key]
|
|
||||||
const name = parentName ? `${parentName}:${key}` : key
|
|
||||||
if (typeof subHook === 'object' && subHook !== null) {
|
|
||||||
this.flatHooks(subHook, hooks, name)
|
|
||||||
} else {
|
|
||||||
hooks[name] = subHook
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return hooks
|
|
||||||
}
|
|
||||||
|
|
||||||
addHooks (configHooks) {
|
|
||||||
const hooks = this.flatHooks(configHooks)
|
|
||||||
Object.keys(hooks).filter(Boolean).forEach((key) => {
|
|
||||||
[].concat(hooks[key]).forEach(h => this.hook(key, h))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
import isPlainObject from 'lodash/isPlainObject'
|
import isPlainObject from 'lodash/isPlainObject'
|
||||||
import consola from 'consola'
|
import consola from 'consola'
|
||||||
|
import Hookable from 'hable'
|
||||||
|
|
||||||
import { defineAlias } from '@nuxt/utils'
|
import { defineAlias } from '@nuxt/utils'
|
||||||
import { getNuxtConfig } from '@nuxt/config'
|
import { getNuxtConfig } from '@nuxt/config'
|
||||||
@ -9,12 +10,11 @@ import { Server } from '@nuxt/server'
|
|||||||
import { version } from '../package.json'
|
import { version } from '../package.json'
|
||||||
|
|
||||||
import ModuleContainer from './module'
|
import ModuleContainer from './module'
|
||||||
import Hookable from './hookable'
|
|
||||||
import Resolver from './resolver'
|
import Resolver from './resolver'
|
||||||
|
|
||||||
export default class Nuxt extends Hookable {
|
export default class Nuxt extends Hookable {
|
||||||
constructor (options = {}) {
|
constructor (options = {}) {
|
||||||
super()
|
super(consola)
|
||||||
|
|
||||||
// Assign options and apply defaults
|
// Assign options and apply defaults
|
||||||
this.options = getNuxtConfig(options)
|
this.options = getNuxtConfig(options)
|
||||||
@ -24,11 +24,11 @@ export default class Nuxt extends Hookable {
|
|||||||
this.moduleContainer = new ModuleContainer(this)
|
this.moduleContainer = new ModuleContainer(this)
|
||||||
|
|
||||||
// Deprecated hooks
|
// Deprecated hooks
|
||||||
this._deprecatedHooks = {
|
this.deprecateHooks({
|
||||||
'render:context': 'render:routeContext',
|
'render:context': 'render:routeContext',
|
||||||
'render:routeContext': 'vue-renderer:afterRender',
|
'render:routeContext': 'vue-renderer:afterRender',
|
||||||
'showReady': 'webpack:done' // Workaround to deprecate showReady
|
'showReady': 'webpack:done' // Workaround to deprecate showReady
|
||||||
}
|
})
|
||||||
|
|
||||||
// Add Legacy aliases
|
// Add Legacy aliases
|
||||||
defineAlias(this, this.resolver, ['resolveAlias', 'resolvePath'])
|
defineAlias(this, this.resolver, ['resolveAlias', 'resolvePath'])
|
||||||
|
@ -1,161 +0,0 @@
|
|||||||
import consola from 'consola'
|
|
||||||
import Hookable from '../src/hookable'
|
|
||||||
|
|
||||||
describe('core: hookable', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
consola.debug.mockClear()
|
|
||||||
consola.log.mockClear()
|
|
||||||
consola.error.mockClear()
|
|
||||||
consola.fatal.mockClear()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should construct hook object', () => {
|
|
||||||
const hook = new Hookable()
|
|
||||||
|
|
||||||
expect(hook._hooks).toEqual({})
|
|
||||||
expect(hook._deprecatedHooks).toEqual({})
|
|
||||||
expect(hook.hook).toBeInstanceOf(Function)
|
|
||||||
expect(hook.callHook).toBeInstanceOf(Function)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should register hook successfully', () => {
|
|
||||||
const hook = new Hookable()
|
|
||||||
hook.hook('test:hook', () => {})
|
|
||||||
hook.hook('test:hook', () => {})
|
|
||||||
|
|
||||||
expect(hook._hooks['test:hook']).toHaveLength(2)
|
|
||||||
expect(hook._hooks['test:hook']).toBeInstanceOf(Array)
|
|
||||||
expect(hook._hooks['test:hook']).toEqual([expect.any(Function), expect.any(Function)])
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should ignore empty hook name', () => {
|
|
||||||
const hook = new Hookable()
|
|
||||||
hook.hook(0, () => {})
|
|
||||||
hook.hook('', () => {})
|
|
||||||
hook.hook(undefined, () => {})
|
|
||||||
|
|
||||||
expect(hook._hooks[0]).toBeUndefined()
|
|
||||||
expect(hook._hooks['']).toBeUndefined()
|
|
||||||
expect(hook._hooks[undefined]).toBeUndefined()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should ignore non-function hook', () => {
|
|
||||||
const hook = new Hookable()
|
|
||||||
hook.hook('test:hook', '')
|
|
||||||
hook.hook('test:hook', undefined)
|
|
||||||
|
|
||||||
expect(hook._hooks['test:hook']).toBeUndefined()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should convert and display deprecated hook', () => {
|
|
||||||
const hook = new Hookable()
|
|
||||||
hook._deprecatedHooks['test:hook'] = 'test:before'
|
|
||||||
|
|
||||||
hook.hook('test:hook', () => {})
|
|
||||||
|
|
||||||
expect(consola.warn).toBeCalledWith('test:hook hook has been deprecated, please use test:before')
|
|
||||||
expect(hook._hooks['test:hook']).toBeUndefined()
|
|
||||||
expect(hook._hooks['test:before']).toEqual([expect.any(Function)])
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should call registered hook', async () => {
|
|
||||||
const hook = new Hookable()
|
|
||||||
hook.hook('test:hook', () => consola.log('test:hook called'))
|
|
||||||
|
|
||||||
await hook.callHook('test:hook')
|
|
||||||
|
|
||||||
expect(consola.log).toBeCalledWith('test:hook called')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should ignore unregistered hook', async () => {
|
|
||||||
const hook = new Hookable()
|
|
||||||
|
|
||||||
await hook.callHook('test:hook')
|
|
||||||
|
|
||||||
expect(consola.debug).not.toBeCalled()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should report hook error', async () => {
|
|
||||||
const hook = new Hookable()
|
|
||||||
const error = new Error('Hook Error')
|
|
||||||
hook.hook('test:hook', () => { throw error })
|
|
||||||
|
|
||||||
await hook.callHook('test:hook')
|
|
||||||
|
|
||||||
expect(consola.fatal).toBeCalledWith(error)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should call error hook', async () => {
|
|
||||||
const hook = new Hookable()
|
|
||||||
const error = new Error('Hook Error')
|
|
||||||
hook.hook('error', jest.fn())
|
|
||||||
hook.hook('test:hook', () => { throw error })
|
|
||||||
|
|
||||||
await hook.callHook('test:hook')
|
|
||||||
|
|
||||||
expect(hook._hooks.error[0]).toBeCalledWith(error)
|
|
||||||
expect(consola.fatal).toBeCalledWith(error)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should clear registered hooks', () => {
|
|
||||||
const hook = new Hookable()
|
|
||||||
hook.hook('test:hook', () => {})
|
|
||||||
|
|
||||||
expect(hook._hooks['test:hook']).toHaveLength(1)
|
|
||||||
expect(hook._hooks['test:before']).toBeUndefined()
|
|
||||||
|
|
||||||
hook.clearHook('test:hook')
|
|
||||||
hook.clearHook('test:before')
|
|
||||||
|
|
||||||
expect(hook._hooks['test:hook']).toBeUndefined()
|
|
||||||
expect(hook._hooks['test:before']).toBeUndefined()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should clear all registered hooks', () => {
|
|
||||||
const hook = new Hookable()
|
|
||||||
hook.hook('test:hook', () => {})
|
|
||||||
|
|
||||||
expect(hook._hooks['test:hook']).toHaveLength(1)
|
|
||||||
expect(hook._hooks['test:before']).toBeUndefined()
|
|
||||||
|
|
||||||
hook.clearHooks()
|
|
||||||
|
|
||||||
expect(hook._hooks['test:hook']).toBeUndefined()
|
|
||||||
expect(hook._hooks['test:before']).toBeUndefined()
|
|
||||||
expect(hook._hooks).toEqual({})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should return flat hooks', () => {
|
|
||||||
const hook = new Hookable()
|
|
||||||
const hooks = hook.flatHooks({
|
|
||||||
test: {
|
|
||||||
hook: () => {},
|
|
||||||
before: () => {}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(hooks).toEqual({
|
|
||||||
'test:hook': expect.any(Function),
|
|
||||||
'test:before': expect.any(Function)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should add object hooks', () => {
|
|
||||||
const hook = new Hookable()
|
|
||||||
hook.addHooks({
|
|
||||||
test: {
|
|
||||||
hook: () => {},
|
|
||||||
before: () => {},
|
|
||||||
after: null
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(hook._hooks).toEqual({
|
|
||||||
'test:hook': expect.any(Array),
|
|
||||||
'test:before': expect.any(Array),
|
|
||||||
'test:after': undefined
|
|
||||||
})
|
|
||||||
expect(hook._hooks['test:hook']).toHaveLength(1)
|
|
||||||
expect(hook._hooks['test:before']).toHaveLength(1)
|
|
||||||
})
|
|
||||||
})
|
|
@ -1,10 +1,10 @@
|
|||||||
import { defineAlias } from '@nuxt/utils'
|
import { defineAlias } from '@nuxt/utils'
|
||||||
import { getNuxtConfig } from '@nuxt/config'
|
import { getNuxtConfig } from '@nuxt/config'
|
||||||
import { Server } from '@nuxt/server'
|
import { Server } from '@nuxt/server'
|
||||||
|
import Hookable from 'hable'
|
||||||
|
|
||||||
import Nuxt from '../src/nuxt'
|
import Nuxt from '../src/nuxt'
|
||||||
import ModuleContainer from '../src/module'
|
import ModuleContainer from '../src/module'
|
||||||
import Hookable from '../src/hookable'
|
|
||||||
import Resolver from '../src/resolver'
|
import Resolver from '../src/resolver'
|
||||||
import { version } from '../package.json'
|
import { version } from '../package.json'
|
||||||
|
|
||||||
|
@ -5436,6 +5436,11 @@ gzip-size@^5.0.0:
|
|||||||
duplexer "^0.1.1"
|
duplexer "^0.1.1"
|
||||||
pify "^4.0.1"
|
pify "^4.0.1"
|
||||||
|
|
||||||
|
hable@^2.2.1:
|
||||||
|
version "2.2.1"
|
||||||
|
resolved "https://registry.npmjs.org/hable/-/hable-2.2.1.tgz#714ef141a7eda5bd8530d6e4e3b37c6807716290"
|
||||||
|
integrity sha512-CuGRGMNnCp8RJ70zeoTX37sX4ctoT8BpmkStI2TYQwBBtWzQQFXQ/jsBUCqd34grt3IJQ+hkUXAU84/IuErVLQ==
|
||||||
|
|
||||||
handlebars@^4.1.2:
|
handlebars@^4.1.2:
|
||||||
version "4.1.2"
|
version "4.1.2"
|
||||||
resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz#b6b37c1ced0306b221e094fc7aca3ec23b131b67"
|
resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz#b6b37c1ced0306b221e094fc7aca3ec23b131b67"
|
||||||
|
Loading…
Reference in New Issue
Block a user