Skip to content

Commit 03eb049

Browse files
author
Sébastien Chopin
authored
fix(builder): watch store dir and serverMiddleware paths (nuxt#5681)
* fix(builder): Watch store dir to restart Nuxt app when options.store=false * hotfix: Linting issues * hotfix: Use path.resolve instead of path.join * test: Update test for watcher * hotfix: revert to path.join and fix tests * hotfix: Fix coverage for hard to test condition * hotfix: Fix test for Windows * Update builder.js * fix lint error * fix: Cache serverMiddlewarePaths
1 parent f2bd2f5 commit 03eb049

5 files changed

Lines changed: 101 additions & 29 deletions

File tree

packages/builder/src/builder.js

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,16 @@ import {
2222
determineGlobals,
2323
stripWhitespace,
2424
isString,
25-
isIndexFileAndFolder
25+
isIndexFileAndFolder,
26+
isPureObject,
27+
clearRequireCache
2628
} from '@nuxt/utils'
2729

2830
import Ignore from './ignore'
2931
import BuildContext from './context/build'
3032
import TemplateContext from './context/template'
3133

3234
const glob = pify(Glob)
33-
3435
export default class Builder {
3536
constructor(nuxt, bundleBuilder) {
3637
this.nuxt = nuxt
@@ -616,10 +617,12 @@ export default class Builder {
616617

617618
let patterns = [
618619
r(src, this.options.dir.layouts),
619-
r(src, this.options.dir.store),
620620
r(src, this.options.dir.middleware),
621621
...rGlob(this.options.dir.layouts)
622622
]
623+
if (this.options.store) {
624+
patterns.push(r(src, this.options.dir.store))
625+
}
623626

624627
if (this._nuxtPages) {
625628
patterns.push(
@@ -648,17 +651,36 @@ export default class Builder {
648651
this.createFileWatcher(customPatterns, ['change'], refreshFiles, this.assignWatcher('custom'))
649652
}
650653

654+
getServerMiddlewarePaths() {
655+
return this.options.serverMiddleware
656+
.map((serverMiddleware) => {
657+
if (isString(serverMiddleware)) {
658+
return serverMiddleware
659+
}
660+
if (isPureObject(serverMiddleware) && isString(serverMiddleware.handler)) {
661+
return serverMiddleware.handler
662+
}
663+
})
664+
.filter(Boolean)
665+
.map(p => path.extname(p) ? p : this.nuxt.resolver.resolvePath(p))
666+
}
667+
651668
watchRestart() {
669+
const serverMiddlewarePaths = this.getServerMiddlewarePaths()
652670
const nuxtRestartWatch = [
653671
// Server middleware
654-
...this.options.serverMiddleware.filter(isString),
672+
...serverMiddlewarePaths,
655673
// Custom watchers
656674
...this.options.watch
657675
].map(this.nuxt.resolver.resolveAlias)
658676

659677
if (this.ignore.ignoreFile) {
660678
nuxtRestartWatch.push(this.ignore.ignoreFile)
661679
}
680+
// If store not activated, watch for a file in the directory
681+
if (!this.options.store) {
682+
nuxtRestartWatch.push(path.join(this.options.srcDir, this.options.dir.store))
683+
}
662684

663685
this.createFileWatcher(
664686
nuxtRestartWatch,
@@ -667,6 +689,11 @@ export default class Builder {
667689
if (['add', 'change', 'unlink'].includes(event) === false) {
668690
return
669691
}
692+
/* istanbul ignore if */
693+
if (serverMiddlewarePaths.includes(fileName)) {
694+
consola.debug(`Clear cache for ${fileName}`)
695+
clearRequireCache(fileName)
696+
}
670697
await this.nuxt.callHook('watch:fileChanged', this, fileName) // Legacy
671698
await this.nuxt.callHook('watch:restart', { event, path: fileName })
672699
},

packages/builder/test/__utils__/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export const createNuxt = () => ({
1010
callHook: jest.fn(),
1111
resolver: {
1212
requireModule: jest.fn(() => ({ template: 'builder-template' })),
13-
resolveAlias: jest.fn(src => `resolveAlias(${src})`)
13+
resolveAlias: jest.fn(src => `resolveAlias(${src})`),
14+
resolvePath: jest.fn(src => `resolvePath(${src})`)
1415
}
1516
})

packages/builder/test/builder.watch.test.js

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import path from 'path'
12
import chokidar from 'chokidar'
23
import upath from 'upath'
34
import debounce from 'lodash/debounce'
4-
import { r, isString } from '@nuxt/utils'
5+
import { r, isString, isPureObject } from '@nuxt/utils'
56

67
import Builder from '../src/builder'
78
import { createNuxt } from './__utils__'
@@ -41,31 +42,51 @@ describe('builder: builder watch', () => {
4142

4243
const patterns = [
4344
'/var/nuxt/src/layouts',
44-
'/var/nuxt/src/store',
4545
'/var/nuxt/src/middleware',
4646
'/var/nuxt/src/layouts/*.{vue,js,ts,tsx}',
4747
'/var/nuxt/src/layouts/**/*.{vue,js,ts,tsx}'
4848
]
4949

50-
expect(r).toBeCalledTimes(5)
50+
expect(r).toBeCalledTimes(4)
5151
expect(r).nthCalledWith(1, '/var/nuxt/src', '/var/nuxt/src/layouts')
52-
expect(r).nthCalledWith(2, '/var/nuxt/src', '/var/nuxt/src/store')
53-
expect(r).nthCalledWith(3, '/var/nuxt/src', '/var/nuxt/src/middleware')
54-
expect(r).nthCalledWith(4, '/var/nuxt/src', '/var/nuxt/src/layouts/*.{vue,js,ts,tsx}')
55-
expect(r).nthCalledWith(5, '/var/nuxt/src', '/var/nuxt/src/layouts/**/*.{vue,js,ts,tsx}')
52+
expect(r).nthCalledWith(2, '/var/nuxt/src', '/var/nuxt/src/middleware')
53+
expect(r).nthCalledWith(3, '/var/nuxt/src', '/var/nuxt/src/layouts/*.{vue,js,ts,tsx}')
54+
expect(r).nthCalledWith(4, '/var/nuxt/src', '/var/nuxt/src/layouts/**/*.{vue,js,ts,tsx}')
5655

57-
expect(upath.normalizeSafe).toBeCalledTimes(5)
56+
expect(upath.normalizeSafe).toBeCalledTimes(4)
5857
expect(upath.normalizeSafe).nthCalledWith(1, '/var/nuxt/src/layouts', 0, patterns)
59-
expect(upath.normalizeSafe).nthCalledWith(2, '/var/nuxt/src/store', 1, patterns)
60-
expect(upath.normalizeSafe).nthCalledWith(3, '/var/nuxt/src/middleware', 2, patterns)
61-
expect(upath.normalizeSafe).nthCalledWith(4, '/var/nuxt/src/layouts/*.{vue,js,ts,tsx}', 3, patterns)
62-
expect(upath.normalizeSafe).nthCalledWith(5, '/var/nuxt/src/layouts/**/*.{vue,js,ts,tsx}', 4, patterns)
58+
expect(upath.normalizeSafe).nthCalledWith(2, '/var/nuxt/src/middleware', 1, patterns)
59+
expect(upath.normalizeSafe).nthCalledWith(3, '/var/nuxt/src/layouts/*.{vue,js,ts,tsx}', 2, patterns)
60+
expect(upath.normalizeSafe).nthCalledWith(4, '/var/nuxt/src/layouts/**/*.{vue,js,ts,tsx}', 3, patterns)
6361

6462
expect(builder.createFileWatcher).toBeCalledTimes(1)
6563
expect(builder.createFileWatcher).toBeCalledWith(patterns, ['add', 'unlink'], expect.any(Function), expect.any(Function))
6664
expect(builder.assignWatcher).toBeCalledTimes(1)
6765
})
6866

67+
test('should watch store files', () => {
68+
const nuxt = createNuxt()
69+
nuxt.options.store = true
70+
nuxt.options.srcDir = '/var/nuxt/src'
71+
nuxt.options.dir = {
72+
layouts: '/var/nuxt/src/layouts',
73+
pages: '/var/nuxt/src/pages',
74+
store: '/var/nuxt/src/store',
75+
middleware: '/var/nuxt/src/middleware'
76+
}
77+
nuxt.options.build.watch = []
78+
79+
const builder = new Builder(nuxt, {})
80+
builder.createFileWatcher = jest.fn()
81+
builder.assignWatcher = jest.fn(() => () => {})
82+
r.mockImplementation((dir, src) => src)
83+
84+
builder.watchClient()
85+
86+
expect(r).toBeCalledTimes(5)
87+
expect(r).nthCalledWith(5, '/var/nuxt/src', '/var/nuxt/src/store')
88+
})
89+
6990
test('should watch pages files', () => {
7091
const nuxt = createNuxt()
7192
nuxt.options.srcDir = '/var/nuxt/src'
@@ -86,10 +107,10 @@ describe('builder: builder watch', () => {
86107

87108
builder.watchClient()
88109

89-
expect(r).toBeCalledTimes(8)
90-
expect(r).nthCalledWith(6, '/var/nuxt/src', '/var/nuxt/src/pages')
91-
expect(r).nthCalledWith(7, '/var/nuxt/src', '/var/nuxt/src/pages/*.{vue,js,ts,tsx}')
92-
expect(r).nthCalledWith(8, '/var/nuxt/src', '/var/nuxt/src/pages/**/*.{vue,js,ts,tsx}')
110+
expect(r).toBeCalledTimes(7)
111+
expect(r).nthCalledWith(5, '/var/nuxt/src', '/var/nuxt/src/pages')
112+
expect(r).nthCalledWith(6, '/var/nuxt/src', '/var/nuxt/src/pages/*.{vue,js,ts,tsx}')
113+
expect(r).nthCalledWith(7, '/var/nuxt/src', '/var/nuxt/src/pages/**/*.{vue,js,ts,tsx}')
93114
})
94115

95116
test('should invoke generateRoutesAndFiles on file refresh', () => {
@@ -215,28 +236,39 @@ describe('builder: builder watch', () => {
215236

216237
test('should watch files for restarting server', () => {
217238
const nuxt = createNuxt()
239+
nuxt.options.srcDir = '/var/nuxt/src'
240+
nuxt.options.dir = {
241+
layouts: '/var/nuxt/src/layouts',
242+
pages: '/var/nuxt/src/pages',
243+
store: '/var/nuxt/src/store',
244+
middleware: '/var/nuxt/src/middleware'
245+
}
218246
nuxt.options.watchers = {
219247
chokidar: { test: true }
220248
}
221249
nuxt.options.watch = [
222250
'/var/nuxt/src/watch/test'
223251
]
224252
nuxt.options.serverMiddleware = [
225-
'/var/nuxt/src/middleware/test',
253+
'/var/nuxt/src/serverMiddleware/test',
254+
{ path: '/test', handler: '/var/nuxt/src/serverMiddleware/test-handler' },
226255
{ obj: 'test' }
227256
]
228257
const builder = new Builder(nuxt, {})
229258
builder.ignore.ignoreFile = '/var/nuxt/src/.nuxtignore'
230-
isString.mockImplementationOnce(src => typeof src === 'string')
259+
isString.mockImplementation(src => typeof src === 'string')
260+
isPureObject.mockImplementation(obj => typeof obj === 'object')
231261

232262
builder.watchRestart()
233263

234264
expect(chokidar.watch).toBeCalledTimes(1)
235265
expect(chokidar.watch).toBeCalledWith(
236266
[
237-
'resolveAlias(/var/nuxt/src/middleware/test)',
267+
'resolveAlias(resolvePath(/var/nuxt/src/serverMiddleware/test))',
268+
'resolveAlias(resolvePath(/var/nuxt/src/serverMiddleware/test-handler))',
238269
'resolveAlias(/var/nuxt/src/watch/test)',
239-
'/var/nuxt/src/.nuxtignore'
270+
'/var/nuxt/src/.nuxtignore',
271+
path.join('/var/nuxt/src/var/nuxt/src/store') // because store == false + using path.join()
240272
],
241273
{ test: true }
242274
)
@@ -246,6 +278,13 @@ describe('builder: builder watch', () => {
246278

247279
test('should trigger restarting when files changed', async () => {
248280
const nuxt = createNuxt()
281+
nuxt.options.srcDir = '/var/nuxt/src'
282+
nuxt.options.dir = {
283+
layouts: '/var/nuxt/src/layouts',
284+
pages: '/var/nuxt/src/pages',
285+
store: '/var/nuxt/src/store',
286+
middleware: '/var/nuxt/src/middleware'
287+
}
249288
nuxt.options.watchers = {
250289
chokidar: { test: true }
251290
}
@@ -274,6 +313,13 @@ describe('builder: builder watch', () => {
274313

275314
test('should ignore other events in watchRestart', () => {
276315
const nuxt = createNuxt()
316+
nuxt.options.srcDir = '/var/nuxt/src'
317+
nuxt.options.dir = {
318+
layouts: '/var/nuxt/src/layouts',
319+
pages: '/var/nuxt/src/pages',
320+
store: '/var/nuxt/src/store',
321+
middleware: '/var/nuxt/src/middleware'
322+
}
277323
nuxt.options.watchers = {
278324
chokidar: { test: true }
279325
}

packages/cli/src/commands/dev.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export default {
4141
const nuxt = await cmd.getNuxt(config)
4242

4343
// Setup hooks
44-
nuxt.hook('watch:restart', payload => this.onWatchRestart(payload, { nuxt, builder, cmd, argv }))
44+
nuxt.hook('watch:restart', payload => this.onWatchRestart(payload, { nuxt, cmd, argv }))
4545
nuxt.hook('bundler:change', changedFileName => this.onBundlerChange(changedFileName))
4646

4747
// Wait for nuxt to be ready

packages/utils/src/lang.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@ export const isString = obj => typeof obj === 'string' || obj instanceof String
66

77
export const isNonEmptyString = obj => Boolean(obj && isString(obj))
88

9-
export const isPureObject = function isPureObject(o) {
10-
return !Array.isArray(o) && typeof o === 'object'
11-
}
9+
export const isPureObject = obj => !Array.isArray(obj) && typeof obj === 'object'
1210

1311
export const isUrl = function isUrl(url) {
1412
return ['http', '//'].some(str => url.startsWith(str))

0 commit comments

Comments
 (0)