scripts: extract shared cordis-walk and md-fences helpers (jscpd gate)

Master's new duplication gate flagged 4 clones, all from the website scripts
reusing existing gate patterns by copy. Extracted the shared halves instead of
ignoring:
- scripts/cordis-walk.ts — cordisModuleBody / eventMembers / serviceClasses,
  one AST walk consumed by both gen-cordis-catalog and gen-website-api (the
  two renderers must agree on WHAT exists; now they structurally do).
- scripts/md-fences.ts — fence extractor with per-gate info-string
  classification, consumed by doc-typecheck and verify-website-yaml.

Behavior-neutral: regenerated catalogs and website pages are byte-identical
(--check green), doc-typecheck/yaml block counts unchanged. jscpd now reports
0 clones; full pre-push run 34/34 green.
This commit is contained in:
lintianle
2026-07-16 22:07:06 +08:00
parent 2cde2a9032
commit 9e9228270a
6 changed files with 223 additions and 168 deletions

View File

@@ -45,6 +45,7 @@ import { resolve } from 'node:path'
import * as yaml from 'js-yaml'
import ts from 'typescript'
import { collectConfigCatalog, type CatalogEntry } from './gen-config-catalog.ts'
import { extractFences } from './md-fences.ts'
const root = resolve(import.meta.dirname, '..')
@@ -80,32 +81,9 @@ interface Block {
/** Extract every ```yaml / ```yaml ignore-check block from one Markdown file. */
function extractBlocks(file: string): Block[] {
const text = readFileSync(resolve(root, file), 'utf8')
const lines = text.split('\n')
const blocks: Block[] = []
let open: { line: number; kind: Block['kind']; body: string[] } | null = null
lines.forEach((raw, i) => {
const fence = /^```(\s*)(\S.*)?$/.exec(raw)
if (!fence) {
if (open) open.body.push(raw)
return
}
if (open) {
// closing fence
blocks.push({ file, line: open.line, kind: open.kind, code: open.body.join('\n') })
open = null
return
}
// opening fence — only yaml blocks participate
const info = (fence[2] ?? '').trim()
const kind: Block['kind'] | null =
info === 'yaml' ? 'check'
: info === 'yaml ignore-check' ? 'ignore'
: null
if (kind) open = { line: i + 1, kind, body: [] }
})
return blocks
return extractFences(resolve(root, file), info =>
info === 'yaml' ? 'check' : info === 'yaml ignore-check' ? 'ignore' : null)
.map(f => ({ file, line: f.line, kind: f.kind, code: f.code }))
}
/** Every workspace package name: `packages/<group>/<pkg>` and `vendor/<pkg>`. */