Files
bilin/tests/release/import-hygiene.test.ts
T
bing 958bb18539 fix(ui): make chapter bridge manual-only and add release gate
Remove auto chapter-bridge prompt on book/chapter open so users trigger it from the cockpit. Fix AiPanel duplicate import that broke dev builds, and add release smoke tests plus test:release/test:ci scripts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-09 19:09:18 +08:00

59 lines
1.8 KiB
TypeScript

import { readFileSync, readdirSync, statSync } from 'fs'
import { join } from 'path'
import { describe, it, expect } from 'vitest'
const ROOT = join(import.meta.dirname, '../..')
function collectSourceFiles(dir: string, acc: string[] = []): string[] {
for (const name of readdirSync(dir)) {
const full = join(dir, name)
const st = statSync(full)
if (st.isDirectory()) {
if (name === 'node_modules' || name === 'out') continue
collectSourceFiles(full, acc)
} else if (/\.(ts|tsx)$/.test(name)) {
acc.push(full)
}
}
return acc
}
function extractNamedImports(content: string): { from: string; names: string[] }[] {
const results: { from: string; names: string[] }[] = []
const re = /import\s+(?:type\s+)?\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/g
let m: RegExpExecArray | null
while ((m = re.exec(content)) !== null) {
const names = m[1]
.split(',')
.map((part) => part.trim())
.filter(Boolean)
.map((part) => part.split(/\s+as\s+/)[0]!.trim())
results.push({ from: m[2]!, names })
}
return results
}
describe('release gate: import hygiene', () => {
it('UT-RELEASE-01: no duplicate named imports from the same module', () => {
const files = collectSourceFiles(join(ROOT, 'src'))
const offenders: string[] = []
for (const file of files) {
const blocks = extractNamedImports(readFileSync(file, 'utf8'))
const seen = new Map<string, Set<string>>()
for (const { from, names } of blocks) {
if (!seen.has(from)) seen.set(from, new Set())
const bucket = seen.get(from)!
for (const name of names) {
if (bucket.has(name)) {
offenders.push(`${file}: duplicate import { ${name} } from '${from}'`)
}
bucket.add(name)
}
}
}
expect(offenders, offenders.join('\n')).toEqual([])
})
})