mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-11 08:33:53 +00:00
fix: refactor file watchers (chokidar/linux workaround) (#4950)
This commit is contained in:
parent
68f6880f54
commit
5ec5932bad
@ -580,6 +580,37 @@ export default class Builder {
|
|||||||
// )
|
// )
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
createFileWatcher(patterns, events, listener, watcherCreatedCallback) {
|
||||||
|
const options = this.options.watchers.chokidar
|
||||||
|
const watcher = chokidar.watch(patterns, options)
|
||||||
|
|
||||||
|
for (const event of events) {
|
||||||
|
watcher.on(event, listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { rewatchOnRawEvents } = this.options.watchers
|
||||||
|
if (rewatchOnRawEvents && Array.isArray(rewatchOnRawEvents)) {
|
||||||
|
watcher.on('raw', (_event) => {
|
||||||
|
if (rewatchOnRawEvents.includes(_event)) {
|
||||||
|
watcher.close()
|
||||||
|
|
||||||
|
listener()
|
||||||
|
this.createFileWatcher(patterns, events, listener, watcherCreatedCallback)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof watcherCreatedCallback === 'function') {
|
||||||
|
watcherCreatedCallback(watcher)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assignWatcher(key) {
|
||||||
|
return (watcher) => {
|
||||||
|
this.watchers[key] = watcher
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
watchClient() {
|
watchClient() {
|
||||||
const src = this.options.srcDir
|
const src = this.options.srcDir
|
||||||
const rGlob = dir => ['*', '**/*'].map(glob => r(src, `${dir}/${glob}.{${this.supportedExtensions.join(',')}}`))
|
const rGlob = dir => ['*', '**/*'].map(glob => r(src, `${dir}/${glob}.{${this.supportedExtensions.join(',')}}`))
|
||||||
@ -600,20 +631,11 @@ export default class Builder {
|
|||||||
|
|
||||||
patterns = patterns.map(upath.normalizeSafe)
|
patterns = patterns.map(upath.normalizeSafe)
|
||||||
|
|
||||||
const options = this.options.watchers.chokidar
|
|
||||||
const refreshFiles = debounce(() => this.generateRoutesAndFiles(), 200)
|
const refreshFiles = debounce(() => this.generateRoutesAndFiles(), 200)
|
||||||
|
|
||||||
// Watch for src Files
|
// Watch for src Files
|
||||||
this.watchers.files = chokidar
|
this.createFileWatcher(patterns, ['add', 'unlink'], refreshFiles, this.assignWatcher('files'))
|
||||||
.watch(patterns, options)
|
|
||||||
.on('add', refreshFiles)
|
|
||||||
.on('unlink', refreshFiles)
|
|
||||||
|
|
||||||
this.watchCustom(refreshFiles)
|
|
||||||
}
|
|
||||||
|
|
||||||
watchCustom(refreshFiles, refresh) {
|
|
||||||
const options = this.options.watchers.chokidar
|
|
||||||
// Watch for custom provided files
|
// Watch for custom provided files
|
||||||
const customPatterns = uniq([
|
const customPatterns = uniq([
|
||||||
...this.options.build.watch,
|
...this.options.build.watch,
|
||||||
@ -624,25 +646,7 @@ export default class Builder {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refresh) {
|
this.createFileWatcher(customPatterns, ['change'], refreshFiles, this.assignWatcher('custom'))
|
||||||
refreshFiles()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.watchers.custom = chokidar
|
|
||||||
.watch(customPatterns, options)
|
|
||||||
.on('change', refreshFiles)
|
|
||||||
|
|
||||||
const { rewatchOnRawEvents } = this.options.watchers
|
|
||||||
if (rewatchOnRawEvents && Array.isArray(rewatchOnRawEvents)) {
|
|
||||||
this.watchers.custom.on('raw', (_event, _path, opts) => {
|
|
||||||
if (rewatchOnRawEvents.includes(_event)) {
|
|
||||||
this.watchers.custom.close()
|
|
||||||
this.watchers.custom = null
|
|
||||||
|
|
||||||
this.watchCustom(refreshFiles, true)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
watchRestart() {
|
watchRestart() {
|
||||||
@ -657,15 +661,18 @@ export default class Builder {
|
|||||||
nuxtRestartWatch.push(this.ignore.ignoreFile)
|
nuxtRestartWatch.push(this.ignore.ignoreFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.watchers.restart = chokidar
|
this.createFileWatcher(
|
||||||
.watch(nuxtRestartWatch, this.options.watchers.chokidar)
|
nuxtRestartWatch,
|
||||||
.on('all', (event, _path) => {
|
['all'],
|
||||||
|
(event, _path) => {
|
||||||
if (['add', 'change', 'unlink'].includes(event) === false) {
|
if (['add', 'change', 'unlink'].includes(event) === false) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.nuxt.callHook('watch:fileChanged', this, _path) // Legacy
|
this.nuxt.callHook('watch:fileChanged', this, _path) // Legacy
|
||||||
this.nuxt.callHook('watch:restart', { event, path: _path })
|
this.nuxt.callHook('watch:restart', { event, path: _path })
|
||||||
})
|
},
|
||||||
|
this.assignWatcher('restart')
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
unwatch() {
|
unwatch() {
|
||||||
|
@ -31,10 +31,10 @@ describe('builder: builder watch', () => {
|
|||||||
middleware: '/var/nuxt/src/middleware'
|
middleware: '/var/nuxt/src/middleware'
|
||||||
}
|
}
|
||||||
nuxt.options.build.watch = []
|
nuxt.options.build.watch = []
|
||||||
nuxt.options.watchers = {
|
|
||||||
chokidar: { test: true }
|
|
||||||
}
|
|
||||||
const builder = new Builder(nuxt, {})
|
const builder = new Builder(nuxt, {})
|
||||||
|
builder.createFileWatcher = jest.fn()
|
||||||
|
builder.assignWatcher = jest.fn(() => () => {})
|
||||||
r.mockImplementation((dir, src) => src)
|
r.mockImplementation((dir, src) => src)
|
||||||
|
|
||||||
builder.watchClient()
|
builder.watchClient()
|
||||||
@ -61,11 +61,9 @@ describe('builder: builder watch', () => {
|
|||||||
expect(upath.normalizeSafe).nthCalledWith(4, '/var/nuxt/src/layouts/*.{vue,js,ts,tsx}', 3, patterns)
|
expect(upath.normalizeSafe).nthCalledWith(4, '/var/nuxt/src/layouts/*.{vue,js,ts,tsx}', 3, patterns)
|
||||||
expect(upath.normalizeSafe).nthCalledWith(5, '/var/nuxt/src/layouts/**/*.{vue,js,ts,tsx}', 4, patterns)
|
expect(upath.normalizeSafe).nthCalledWith(5, '/var/nuxt/src/layouts/**/*.{vue,js,ts,tsx}', 4, patterns)
|
||||||
|
|
||||||
expect(chokidar.watch).toBeCalledTimes(1)
|
expect(builder.createFileWatcher).toBeCalledTimes(1)
|
||||||
expect(chokidar.watch).toBeCalledWith(patterns, { test: true })
|
expect(builder.createFileWatcher).toBeCalledWith(patterns, ['add', 'unlink'], expect.any(Function), expect.any(Function))
|
||||||
expect(chokidar.on).toBeCalledTimes(2)
|
expect(builder.assignWatcher).toBeCalledTimes(1)
|
||||||
expect(chokidar.on).nthCalledWith(1, 'add', expect.any(Function))
|
|
||||||
expect(chokidar.on).nthCalledWith(2, 'unlink', expect.any(Function))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should watch pages files', () => {
|
test('should watch pages files', () => {
|
||||||
@ -81,6 +79,7 @@ describe('builder: builder watch', () => {
|
|||||||
nuxt.options.watchers = {
|
nuxt.options.watchers = {
|
||||||
chokidar: { test: true }
|
chokidar: { test: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
const builder = new Builder(nuxt, {})
|
const builder = new Builder(nuxt, {})
|
||||||
builder._nuxtPages = true
|
builder._nuxtPages = true
|
||||||
r.mockImplementation((dir, src) => src)
|
r.mockImplementation((dir, src) => src)
|
||||||
@ -93,7 +92,7 @@ describe('builder: builder watch', () => {
|
|||||||
expect(r).nthCalledWith(8, '/var/nuxt/src', '/var/nuxt/src/pages/**/*.{vue,js,ts,tsx}')
|
expect(r).nthCalledWith(8, '/var/nuxt/src', '/var/nuxt/src/pages/**/*.{vue,js,ts,tsx}')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should watch custom in watchClient', () => {
|
test('should invoke generateRoutesAndFiles on file refresh', () => {
|
||||||
const nuxt = createNuxt()
|
const nuxt = createNuxt()
|
||||||
nuxt.options.srcDir = '/var/nuxt/src'
|
nuxt.options.srcDir = '/var/nuxt/src'
|
||||||
nuxt.options.dir = {
|
nuxt.options.dir = {
|
||||||
@ -119,14 +118,16 @@ describe('builder: builder watch', () => {
|
|||||||
builder.generateRoutesAndFiles = jest.fn()
|
builder.generateRoutesAndFiles = jest.fn()
|
||||||
refreshFiles()
|
refreshFiles()
|
||||||
expect(builder.generateRoutesAndFiles).toBeCalled()
|
expect(builder.generateRoutesAndFiles).toBeCalled()
|
||||||
expect(builder.watchCustom).toBeCalledTimes(1)
|
|
||||||
expect(builder.watchCustom).toBeCalledWith(refreshFiles)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should watch custom patterns', () => {
|
test('should watch custom patterns', () => {
|
||||||
const nuxt = createNuxt()
|
const nuxt = createNuxt()
|
||||||
nuxt.options.watchers = {
|
nuxt.options.srcDir = '/var/nuxt/src'
|
||||||
chokidar: { test: true }
|
nuxt.options.dir = {
|
||||||
|
layouts: '/var/nuxt/src/layouts',
|
||||||
|
pages: '/var/nuxt/src/pages',
|
||||||
|
store: '/var/nuxt/src/store',
|
||||||
|
middleware: '/var/nuxt/src/middleware'
|
||||||
}
|
}
|
||||||
nuxt.options.build.watch = [
|
nuxt.options.build.watch = [
|
||||||
'/var/nuxt/src/custom'
|
'/var/nuxt/src/custom'
|
||||||
@ -135,61 +136,81 @@ describe('builder: builder watch', () => {
|
|||||||
'/var/nuxt/src/style'
|
'/var/nuxt/src/style'
|
||||||
]
|
]
|
||||||
const builder = new Builder(nuxt, {})
|
const builder = new Builder(nuxt, {})
|
||||||
const refreshFiles = jest.fn()
|
builder.createFileWatcher = jest.fn()
|
||||||
|
builder.assignWatcher = jest.fn(() => () => {})
|
||||||
|
builder.watchClient()
|
||||||
|
|
||||||
builder.watchCustom(refreshFiles)
|
const patterns = [
|
||||||
|
'/var/nuxt/src/custom',
|
||||||
|
'/var/nuxt/src/style'
|
||||||
|
]
|
||||||
|
|
||||||
expect(chokidar.watch).toBeCalledTimes(1)
|
expect(builder.createFileWatcher).toBeCalledTimes(2)
|
||||||
expect(chokidar.watch).toBeCalledWith(
|
expect(builder.createFileWatcher).toBeCalledWith(patterns, ['change'], expect.any(Function), expect.any(Function))
|
||||||
['/var/nuxt/src/custom', '/var/nuxt/src/style'],
|
expect(builder.assignWatcher).toBeCalledTimes(2)
|
||||||
{ test: true }
|
|
||||||
)
|
|
||||||
expect(chokidar.on).toBeCalledTimes(1)
|
|
||||||
expect(chokidar.on).toBeCalledWith('change', refreshFiles)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should call refreshFiles before watching custom patterns', () => {
|
test('should invoke chokidar to create watcher', () => {
|
||||||
const nuxt = createNuxt()
|
const nuxt = createNuxt()
|
||||||
|
nuxt.options.srcDir = '/var/nuxt/src'
|
||||||
|
nuxt.options.dir = {
|
||||||
|
layouts: '/var/nuxt/src/layouts',
|
||||||
|
pages: '/var/nuxt/src/pages',
|
||||||
|
store: '/var/nuxt/src/store',
|
||||||
|
middleware: '/var/nuxt/src/middleware'
|
||||||
|
}
|
||||||
nuxt.options.watchers = {
|
nuxt.options.watchers = {
|
||||||
chokidar: { test: true }
|
chokidar: { test: true }
|
||||||
}
|
}
|
||||||
nuxt.options.build.watch = [
|
|
||||||
'/var/nuxt/src/custom'
|
const patterns = ['/patterns']
|
||||||
]
|
const events = ['event', 'another event']
|
||||||
|
const listener = jest.fn()
|
||||||
|
const watcherCreatedCallback = jest.fn()
|
||||||
|
|
||||||
const builder = new Builder(nuxt, {})
|
const builder = new Builder(nuxt, {})
|
||||||
const refreshFiles = jest.fn()
|
builder.createFileWatcher(patterns, events, listener, watcherCreatedCallback)
|
||||||
|
|
||||||
builder.watchCustom(refreshFiles, true)
|
expect(chokidar.watch).toBeCalledTimes(1)
|
||||||
|
expect(chokidar.watch).toBeCalledWith(patterns, { test: true })
|
||||||
expect(refreshFiles).toBeCalledTimes(1)
|
expect(chokidar.on).toBeCalledTimes(2)
|
||||||
|
expect(chokidar.on).nthCalledWith(1, 'event', listener)
|
||||||
|
expect(chokidar.on).nthCalledWith(2, 'another event', listener)
|
||||||
|
expect(watcherCreatedCallback).toBeCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should rewatch custom patterns when event is included in rewatchOnRawEvents', () => {
|
test('should restart watcher when event is included in rewatchOnRawEvents', () => {
|
||||||
const nuxt = createNuxt()
|
const nuxt = createNuxt()
|
||||||
|
nuxt.options.srcDir = '/var/nuxt/src'
|
||||||
|
nuxt.options.dir = {
|
||||||
|
layouts: '/var/nuxt/src/layouts',
|
||||||
|
pages: '/var/nuxt/src/pages',
|
||||||
|
store: '/var/nuxt/src/store',
|
||||||
|
middleware: '/var/nuxt/src/middleware'
|
||||||
|
}
|
||||||
nuxt.options.watchers = {
|
nuxt.options.watchers = {
|
||||||
chokidar: { test: true },
|
chokidar: { test: true },
|
||||||
rewatchOnRawEvents: ['rename']
|
rewatchOnRawEvents: ['rename']
|
||||||
}
|
}
|
||||||
nuxt.options.build.watch = [
|
|
||||||
'/var/nuxt/src/custom'
|
|
||||||
]
|
|
||||||
const builder = new Builder(nuxt, {})
|
|
||||||
const refreshFiles = jest.fn()
|
|
||||||
|
|
||||||
builder.watchCustom(refreshFiles)
|
const patterns = ['/pattern']
|
||||||
|
const events = ['event']
|
||||||
|
const listener = jest.fn()
|
||||||
|
const watcherCreatedCallback = jest.fn()
|
||||||
|
|
||||||
|
const builder = new Builder(nuxt, {})
|
||||||
|
builder.createFileWatcher(patterns, events, listener, watcherCreatedCallback)
|
||||||
|
|
||||||
expect(chokidar.on).toBeCalledTimes(2)
|
expect(chokidar.on).toBeCalledTimes(2)
|
||||||
expect(chokidar.on).nthCalledWith(2, 'raw', expect.any(Function))
|
expect(chokidar.on).nthCalledWith(2, 'raw', expect.any(Function))
|
||||||
|
|
||||||
const rewatchHandler = chokidar.on.mock.calls[1][1]
|
const rewatchHandler = chokidar.on.mock.calls[1][1]
|
||||||
builder.watchCustom = jest.fn()
|
|
||||||
rewatchHandler('rename')
|
rewatchHandler('rename')
|
||||||
rewatchHandler('change')
|
rewatchHandler('change')
|
||||||
|
|
||||||
expect(chokidar.close).toBeCalledTimes(1)
|
expect(chokidar.close).toBeCalledTimes(1)
|
||||||
expect(builder.watchers.custom).toBeNull()
|
expect(builder.watchers.custom).toBeNull()
|
||||||
expect(builder.watchCustom).toBeCalledTimes(1)
|
expect(watcherCreatedCallback).toBeCalledTimes(2)
|
||||||
expect(builder.watchCustom).toBeCalledWith(refreshFiles, true)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should watch files for restarting server', () => {
|
test('should watch files for restarting server', () => {
|
||||||
@ -327,4 +348,18 @@ describe('builder: builder watch', () => {
|
|||||||
expect(builder.unwatch).not.toBeCalled()
|
expect(builder.unwatch).not.toBeCalled()
|
||||||
expect(bundleBuilderClose).not.toBeCalled()
|
expect(bundleBuilderClose).not.toBeCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should assign watcher with key', () => {
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
const builder = new Builder(nuxt, {})
|
||||||
|
|
||||||
|
const key = 'key'
|
||||||
|
const watcher = 'watcher'
|
||||||
|
|
||||||
|
const fn = builder.assignWatcher(key)
|
||||||
|
fn(watcher)
|
||||||
|
|
||||||
|
expect(Boolean(builder.watchers[key])).toBe(true)
|
||||||
|
expect(builder.watchers[key]).toBe(watcher)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user