feat: add easy-slides CLI

This commit is contained in:
2026-08-29 18:59:33 +08:00
parent 2ca064d303
commit 33152a90dc
19 changed files with 1692 additions and 0 deletions
+109
View File
@@ -0,0 +1,109 @@
import { watch, type FSWatcher } from 'node:fs'
import path from 'node:path'
import type { DeckMetadata } from './content.ts'
import { formatStaticOverflowResult, staticOverflowFingerprint } from './overflow-report.ts'
import { StaticOverflowAuditor } from './static-overflow.ts'
interface DevOverflowAuditOptions {
cwd?: string
debounceMs?: number
deck: DeckMetadata
output?: (line: string) => void
}
export interface DevOverflowAuditController {
close: () => Promise<void>
ready: Promise<void>
}
export function startDevOverflowAudit(options: DevOverflowAuditOptions): DevOverflowAuditController {
const auditor = new StaticOverflowAuditor()
const cwd = options.cwd ?? process.cwd()
const debounceMs = options.debounceMs ?? 250
const output = options.output ?? console.log
let activeAudit: Promise<void> | undefined
let closed = false
let fingerprint = ''
let queued = false
let timer: ReturnType<typeof setTimeout> | undefined
let watcher: FSWatcher | undefined
function scheduleAudit(delay = debounceMs) {
if (closed)
return
if (timer)
clearTimeout(timer)
timer = setTimeout(() => {
timer = undefined
void runAudit()
}, delay)
}
async function runAudit() {
if (closed)
return
if (activeAudit) {
queued = true
return activeAudit
}
activeAudit = auditor.audit([options.deck])
.then((result) => {
if (closed)
return
const nextFingerprint = staticOverflowFingerprint(result)
if (nextFingerprint !== fingerprint) {
fingerprint = nextFingerprint
formatStaticOverflowResult(result, cwd).forEach(line => output(line))
}
})
.catch((error) => {
if (closed)
return
const message = error instanceof Error ? error.message : String(error)
const nextFingerprint = `error:${message}`
if (nextFingerprint !== fingerprint) {
fingerprint = nextFingerprint
output(`WARN ${path.relative(cwd, options.deck.entry)}: 无法完成静态内容检查:${message}`)
}
})
.finally(() => {
activeAudit = undefined
if (queued) {
queued = false
scheduleAudit(0)
}
})
return activeAudit
}
function startWatcher() {
const handleChange = () => scheduleAudit()
try {
watcher = watch(options.deck.directory, { recursive: true }, handleChange)
}
catch {
watcher = watch(options.deck.entry, handleChange)
}
}
const ready = (async () => {
output('\n检查开发内容中的静态溢出风险...')
startWatcher()
await runAudit()
})()
return {
ready,
async close() {
if (closed)
return
closed = true
watcher?.close()
if (timer)
clearTimeout(timer)
await ready.catch(() => undefined)
await activeAudit?.catch(() => undefined)
},
}
}