Tools live under tools/<role>/<domain> (checks, generate, release, ci, dev), every tool is a workspace named @open-pencil/<domain>-tools, a shared tools/tsconfig.json backs the new check:tools gate that fixed 55 latent type errors, test:tools runs through bun --filter, the placement check is its own checks/test-homes package, and every tool resolves the repository through resolveWorkspaceRoot. Bun, Node, and mdast types live in a tools-root workspace so they never reach the app program.
68 lines
2.6 KiB
TypeScript
68 lines
2.6 KiB
TypeScript
import { expect, test } from 'bun:test'
|
|
import { once } from 'node:events'
|
|
import { mkdtemp, mkdir, realpath, rm, writeFile } from 'node:fs/promises'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
|
|
import { createDevServerOptions } from '#vite-config/server'
|
|
import { createServer, type ViteDevServer } from 'vite'
|
|
|
|
const WATCH_TIMEOUT_MS = 5000
|
|
|
|
async function waitFor(check: () => boolean | Promise<boolean>, description: string) {
|
|
const deadline = Date.now() + WATCH_TIMEOUT_MS
|
|
while (!(await check())) {
|
|
if (Date.now() >= deadline) throw new Error(description)
|
|
await Bun.sleep(10)
|
|
}
|
|
}
|
|
|
|
test('Vite invalidates source inside an active worktree while excluding nested checkouts', async () => {
|
|
const directory = await realpath(await mkdtemp(join(tmpdir(), 'open-pencil-watch-')))
|
|
let server: ViteDevServer | undefined
|
|
try {
|
|
const root = join(directory, '.worktrees', 'active')
|
|
const sourceDir = join(root, 'src')
|
|
const source = join(sourceDir, 'entry.ts')
|
|
const nestedSourceDir = join(root, '.worktrees', 'other', 'src')
|
|
await mkdir(sourceDir, { recursive: true })
|
|
await mkdir(nestedSourceDir, { recursive: true })
|
|
await writeFile(source, 'export const version = 1\n')
|
|
await writeFile(join(nestedSourceDir, 'entry.ts'), 'export const nested = true\n')
|
|
const activeServer = await createServer({
|
|
configFile: false,
|
|
root,
|
|
logLevel: 'silent',
|
|
server: { ...createDevServerOptions(undefined, root), port: 0 },
|
|
optimizeDeps: { noDiscovery: true }
|
|
})
|
|
server = activeServer
|
|
await activeServer.listen()
|
|
expect((await activeServer.transformRequest('/src/entry.ts'))?.code).toContain('version = 1')
|
|
await waitFor(
|
|
() => activeServer.watcher.getWatched()[sourceDir]?.includes('entry.ts') ?? false,
|
|
'Active worktree source is not watched'
|
|
)
|
|
const changed = once(activeServer.watcher, 'change', {
|
|
signal: AbortSignal.timeout(WATCH_TIMEOUT_MS)
|
|
})
|
|
await writeFile(source, 'export const version = 2\n')
|
|
const [changedPath] = await changed
|
|
expect(changedPath).toBe(source)
|
|
// Vite's asynchronous change handler must invalidate its transform cache too.
|
|
await waitFor(
|
|
async () =>
|
|
(await activeServer.transformRequest('/src/entry.ts'))?.code.includes('version = 2') ??
|
|
false,
|
|
'Vite still serves the cached transform after a source change'
|
|
)
|
|
expect(activeServer.watcher.getWatched()[nestedSourceDir]).toBeUndefined()
|
|
} finally {
|
|
try {
|
|
await server?.close()
|
|
} finally {
|
|
await rm(directory, { recursive: true, force: true })
|
|
}
|
|
}
|
|
})
|