fix: refactor file watchers (chokidar/linux workaround) (#4950)

This commit is contained in:
Pim 2019-02-06 19:46:17 +01:00 committed by Pooya Parsa
parent 68f6880f54
commit 5ec5932bad
2 changed files with 115 additions and 73 deletions

View File

@ -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() {

View File

@ -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)
})
}) })