feat: add easy-slides CLI
This commit is contained in:
@@ -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)
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user