74 lines
2.9 KiB
TypeScript
74 lines
2.9 KiB
TypeScript
import { EventEmitter } from 'node:events'
|
|
import { fileURLToPath } from 'node:url'
|
|
import { dirname, resolve } from 'node:path'
|
|
import { describe, expect, it, vi } from 'vitest'
|
|
import { configWatchPlugin, featuresRuntimePlugin } from '../src/dev'
|
|
|
|
const here = dirname(fileURLToPath(import.meta.url))
|
|
const fixture = (p: string) => resolve(here, 'fixtures', p)
|
|
|
|
function mockServer() {
|
|
const watcher = new EventEmitter() as EventEmitter & { add: (paths: string[]) => void }
|
|
watcher.add = vi.fn()
|
|
const restart = vi.fn()
|
|
const server = { watcher, restart, config: { logger: { info: vi.fn() } } }
|
|
return { server, watcher, restart }
|
|
}
|
|
|
|
const callConfigureServer = (plugin: { configureServer?: unknown }, server: unknown) =>
|
|
(plugin.configureServer as (s: unknown) => void)(server)
|
|
|
|
describe('configWatchPlugin', () => {
|
|
it('applies only in serve mode', () => {
|
|
expect(configWatchPlugin([]).apply).toBe('serve')
|
|
})
|
|
|
|
it('watches layer config files and restarts on change', () => {
|
|
const plugin = configWatchPlugin([fixture('stack/app'), fixture('stack/base')])
|
|
const { server, watcher, restart } = mockServer()
|
|
callConfigureServer(plugin, server)
|
|
expect(watcher.add).toHaveBeenCalled()
|
|
watcher.emit('change', resolve(fixture('stack/base'), 'app.config.ts'))
|
|
expect(restart).toHaveBeenCalledTimes(1)
|
|
})
|
|
|
|
it('ignores unrelated file changes', () => {
|
|
const plugin = configWatchPlugin([fixture('stack/app')])
|
|
const { server, watcher, restart } = mockServer()
|
|
callConfigureServer(plugin, server)
|
|
watcher.emit('change', resolve(fixture('stack/app'), 'src', 'whatever.ts'))
|
|
expect(restart).not.toHaveBeenCalled()
|
|
})
|
|
})
|
|
|
|
const runTransform = (
|
|
plugin: { transform?: unknown },
|
|
code: string,
|
|
id = '/app/src/x.ts',
|
|
): { code: unknown; map?: unknown } | null => {
|
|
const t = plugin.transform as
|
|
| ((this: unknown, c: string, i: string) => { code: unknown; map?: unknown } | null)
|
|
| undefined
|
|
return t ? t.call({}, code, id) : null
|
|
}
|
|
|
|
describe('featuresRuntimePlugin', () => {
|
|
it('applies only in serve mode', () => {
|
|
expect(featuresRuntimePlugin({}).apply).toBe('serve')
|
|
})
|
|
|
|
it('prepends a module-local __FEATURES__ with a rolldown-generated sourcemap', () => {
|
|
const out = runTransform(featuresRuntimePlugin({ billing: true }), 'export const x = __FEATURES__.billing')
|
|
const code = String(out?.code)
|
|
expect(code).toContain('const __FEATURES__={"billing":true};')
|
|
expect(code).toContain('export const x = __FEATURES__.billing')
|
|
expect((out?.map as { mappings?: string })?.mappings).toBeTruthy() // real sourcemap
|
|
})
|
|
|
|
it('ignores property access (_ctx.__FEATURES__) and node_modules', () => {
|
|
const p = featuresRuntimePlugin({ billing: true })
|
|
expect(runTransform(p, 'const a = _ctx.__FEATURES__.billing')).toBeNull()
|
|
expect(runTransform(p, 'export const x = __FEATURES__.billing', '/x/node_modules/y.js')).toBeNull()
|
|
})
|
|
})
|