Skip to content

Commit 9779dd3

Browse files
committed
test(serve): cover watched-file matcher
1 parent 0652292 commit 9779dd3

3 files changed

Lines changed: 50 additions & 10 deletions

File tree

src/serve.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { readFileSync } from 'node:fs'
2-
import { dirname, resolve, relative, basename, matchesGlob } from 'node:path'
2+
import { dirname, resolve, basename } from 'node:path'
33
import { fileURLToPath } from 'node:url'
44
import { createRequire } from 'node:module'
55
import { createServer, createLogger, type ViteDevServer } from 'vite'
@@ -18,6 +18,7 @@ import { serveCompatibility } from './server/compatibility.ts'
1818
import { serveLint } from './server/linter.ts'
1919
import { sendEmail } from './server/email.ts'
2020
import { normalizeComponentSources } from './utils/componentSources.ts'
21+
import { createWatchedFileMatcher } from './utils/watchPaths.ts'
2122
import type { MaizzleConfig } from './types/index.ts'
2223

2324
const __dirname = dirname(fileURLToPath(import.meta.url))
@@ -166,15 +167,7 @@ function maizzleDevPlugin(
166167

167168
const userWatchPaths = config.server?.watch ?? []
168169
const watchPaths = [...defaultWatchPaths, ...userWatchPaths]
169-
// Strip a leading "./" so chokidar-style relative globs match correctly.
170-
const normalizePattern = (p: string) => p.replace(/^\.\//, '')
171-
// Chokidar emits absolute paths on change. Match against project-relative
172-
// form so relative globs like "locales/**" actually hit.
173-
const cwd = config.root ?? process.cwd()
174-
const isWatchedFile = (file: string) => {
175-
const rel = relative(cwd, file).replace(/\\/g, '/')
176-
return watchPaths.some(p => matchesGlob(rel, normalizePattern(p)))
177-
}
170+
const isWatchedFile = createWatchedFileMatcher(watchPaths, config.root ?? process.cwd())
178171

179172
for (const watchPath of watchPaths) {
180173
server.watcher.add(watchPath)

src/tests/utils/watchPaths.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { describe, it, expect } from 'vitest'
2+
import { createWatchedFileMatcher } from '../../utils/watchPaths.ts'
3+
4+
describe('createWatchedFileMatcher', () => {
5+
const cwd = '/project'
6+
7+
it('matches an absolute path against a project-relative glob', () => {
8+
const matcher = createWatchedFileMatcher(['locales/**'], cwd)
9+
expect(matcher('/project/locales/en.json')).toBe(true)
10+
expect(matcher('/project/locales/nested/en.json')).toBe(true)
11+
})
12+
13+
it('strips a leading "./" from user-supplied patterns', () => {
14+
const matcher = createWatchedFileMatcher(['./locales/**/*.json'], cwd)
15+
expect(matcher('/project/locales/en.json')).toBe(true)
16+
})
17+
18+
it('matches top-level files like maizzle.config.ts', () => {
19+
const matcher = createWatchedFileMatcher(['maizzle.config.ts'], cwd)
20+
expect(matcher('/project/maizzle.config.ts')).toBe(true)
21+
})
22+
23+
it('returns false for files outside any watched pattern', () => {
24+
const matcher = createWatchedFileMatcher(['locales/**'], cwd)
25+
expect(matcher('/project/emails/welcome.vue')).toBe(false)
26+
})
27+
28+
it('returns false for files outside the project root', () => {
29+
const matcher = createWatchedFileMatcher(['locales/**'], cwd)
30+
expect(matcher('/elsewhere/locales/en.json')).toBe(false)
31+
})
32+
})

src/utils/watchPaths.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { matchesGlob, relative } from 'node:path'
2+
3+
/**
4+
* Build a predicate that tells whether an absolute file path emitted by
5+
* chokidar matches any of the given globs. Patterns are interpreted as
6+
* project-relative; a leading `./` is stripped so user-supplied globs like
7+
* `./locales/**` behave identically to `locales/**`.
8+
*/
9+
export function createWatchedFileMatcher(patterns: string[], cwd: string) {
10+
const normalized = patterns.map(p => p.replace(/^\.\//, ''))
11+
return (file: string) => {
12+
const rel = relative(cwd, file).replace(/\\/g, '/')
13+
return normalized.some(p => matchesGlob(rel, p))
14+
}
15+
}

0 commit comments

Comments
 (0)