// 棘轮:宿主侧的 HTML 写入必须走 @ui safeHtml,不能在组件里直接塞字符串。 import { describe, expect, it } from 'vitest' import { readFileSync, readdirSync, statSync } from 'node:fs' import { join, relative, dirname } from 'node:path' import { fileURLToPath } from 'node:url' const SRC = join(dirname(fileURLToPath(import.meta.url)), '..', 'src') function walk(dir: string, out: string[] = []): string[] { for (const name of readdirSync(dir)) { const p = join(dir, name) if (statSync(p).isDirectory()) walk(p, out) else if (/\.(ts|tsx)$/.test(name)) out.push(p) } return out } describe('html sinks', () => { it('frontend components do not write HTML themselves', () => { const hits: string[] = [] for (const file of walk(SRC)) { const text = readFileSync(file, 'utf8') const rel = relative(SRC, file) if (text.includes('dangerouslySetInnerHTML')) hits.push(`${rel}: dangerouslySetInnerHTML`) // 空白放进前瞻里,避免 \s* 回退到空格前面、把 innerHTML = '' 误判成写入。 if (/innerHTML\s*=(?!\s*['"]['"])/.test(text)) hits.push(`${rel}: innerHTML`) } expect(hits).toEqual([]) }) })