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() {
const src = this.options.srcDir
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)
const options = this.options.watchers.chokidar
const refreshFiles = debounce(() => this.generateRoutesAndFiles(), 200)
// Watch for src Files
this.watchers.files = chokidar
.watch(patterns, options)
.on('add', refreshFiles)
.on('unlink', refreshFiles)
this.createFileWatcher(patterns, ['add', 'unlink'], refreshFiles, this.assignWatcher('files'))
this.watchCustom(refreshFiles)
}
watchCustom(refreshFiles, refresh) {
const options = this.options.watchers.chokidar
// Watch for custom provided files
const customPatterns = uniq([
...this.options.build.watch,
@ -624,25 +646,7 @@ export default class Builder {
return
}
if (refresh) {
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)
}
})
}
this.createFileWatcher(customPatterns, ['change'], refreshFiles, this.assignWatcher('custom'))
}
watchRestart() {
@ -657,15 +661,18 @@ export default class Builder {
nuxtRestartWatch.push(this.ignore.ignoreFile)
}
this.watchers.restart = chokidar
.watch(nuxtRestartWatch, this.options.watchers.chokidar)
.on('all', (event, _path) => {
this.createFileWatcher(
nuxtRestartWatch,
['all'],
(event, _path) => {
if (['add', 'change', 'unlink'].includes(event) === false) {
return
}
this.nuxt.callHook('watch:fileChanged', this, _path) // Legacy
this.nuxt.callHook('watch:restart', { event, path: _path })
})
},
this.assignWatcher('restart')
)
}
unwatch() {

View File

@ -31,10 +31,10 @@ describe('builder: builder watch', () => {
middleware: '/var/nuxt/src/middleware'
}
nuxt.options.build.watch = []
nuxt.options.watchers = {
chokidar: { test: true }
}
const builder = new Builder(nuxt, {})
builder.createFileWatcher = jest.fn()
builder.assignWatcher = jest.fn(() => () => {})
r.mockImplementation((dir, src) => src)
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(5, '/var/nuxt/src/layouts/**/*.{vue,js,ts,tsx}', 4, patterns)
expect(chokidar.watch).toBeCalledTimes(1)
expect(chokidar.watch).toBeCalledWith(patterns, { test: true })
expect(chokidar.on).toBeCalledTimes(2)
expect(chokidar.on).nthCalledWith(1, 'add', expect.any(Function))
expect(chokidar.on).nthCalledWith(2, 'unlink', expect.any(Function))
expect(builder.createFileWatcher).toBeCalledTimes(1)
expect(builder.createFileWatcher).toBeCalledWith(patterns, ['add', 'unlink'], expect.any(Function), expect.any(Function))
expect(builder.assignWatcher).toBeCalledTimes(1)
})
test('should watch pages files', () => {
@ -81,6 +79,7 @@ describe('builder: builder watch', () => {
nuxt.options.watchers = {
chokidar: { test: true }
}
const builder = new Builder(nuxt, {})
builder._nuxtPages = true
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}')
})
test('should watch custom in watchClient', () => {
test('should invoke generateRoutesAndFiles on file refresh', () => {
const nuxt = createNuxt()
nuxt.options.srcDir = '/var/nuxt/src'
nuxt.options.dir = {
@ -119,14 +118,16 @@ describe('builder: builder watch', () => {
builder.generateRoutesAndFiles = jest.fn()
refreshFiles()
expect(builder.generateRoutesAndFiles).toBeCalled()
expect(builder.watchCustom).toBeCalledTimes(1)
expect(builder.watchCustom).toBeCalledWith(refreshFiles)
})
test('should watch custom patterns', () => {
const nuxt = createNuxt()
nuxt.options.watchers = {
chokidar: { test: true }
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.build.watch = [
'/var/nuxt/src/custom'
@ -135,61 +136,81 @@ describe('builder: builder watch', () => {
'/var/nuxt/src/style'
]
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(chokidar.watch).toBeCalledWith(
['/var/nuxt/src/custom', '/var/nuxt/src/style'],
{ test: true }
)
expect(chokidar.on).toBeCalledTimes(1)
expect(chokidar.on).toBeCalledWith('change', refreshFiles)
expect(builder.createFileWatcher).toBeCalledTimes(2)
expect(builder.createFileWatcher).toBeCalledWith(patterns, ['change'], expect.any(Function), expect.any(Function))
expect(builder.assignWatcher).toBeCalledTimes(2)
})
test('should call refreshFiles before watching custom patterns', () => {
test('should invoke chokidar to create watcher', () => {
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 = {
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 refreshFiles = jest.fn()
builder.createFileWatcher(patterns, events, listener, watcherCreatedCallback)
builder.watchCustom(refreshFiles, true)
expect(refreshFiles).toBeCalledTimes(1)
expect(chokidar.watch).toBeCalledTimes(1)
expect(chokidar.watch).toBeCalledWith(patterns, { test: true })
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()
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 = {
chokidar: { test: true },
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).nthCalledWith(2, 'raw', expect.any(Function))
const rewatchHandler = chokidar.on.mock.calls[1][1]
builder.watchCustom = jest.fn()
rewatchHandler('rename')
rewatchHandler('change')
expect(chokidar.close).toBeCalledTimes(1)
expect(builder.watchers.custom).toBeNull()
expect(builder.watchCustom).toBeCalledTimes(1)
expect(builder.watchCustom).toBeCalledWith(refreshFiles, true)
expect(watcherCreatedCallback).toBeCalledTimes(2)
})
test('should watch files for restarting server', () => {
@ -327,4 +348,18 @@ describe('builder: builder watch', () => {
expect(builder.unwatch).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)
})
})