| 1234567891011121314151617181920212223242526 |
- import { readFileSync, readdirSync } from 'node:fs'
- const root = '.scratch/cockpit-shell-panel-layout/'
- const files = readdirSync(`${root}issues`).filter((file) => /^\d+-.*\.md$/.test(file))
- const rows = files.map((file) => {
- const text = readFileSync(`${root}issues/${file}`, 'utf8').replace(/^\uFEFF/, '')
- const title = /^#\s+(.+)$/m.exec(text)?.[1]
- const status = /^Status:\s*(.+)$/m.exec(text)?.[1]?.trim().toLowerCase()
- if (!title || !status) throw new Error(`Missing title/status: ${file}`)
- return { file, title, closed: ['resolved', 'closed', 'done', 'wontfix'].includes(status) }
- }).sort((a, b) => parseInt(a.file) - parseInt(b.file))
- console.log(JSON.stringify({ total: rows.length, closed: rows.filter((row) => row.closed).length }))
- const map = readFileSync(`${root}map.md`, 'utf8')
- if (process.argv.includes('--check')) {
- if (!/^## Destination$/m.test(map)) throw new Error('Destination missing')
- const matches = [...map.matchAll(/^- \[([ x])\] \[.+\]\(issues\/([^\s)]+)\)$/gm)]
- if (matches.length !== rows.length) throw new Error(`Checklist ${matches.length} != children ${rows.length}`)
- const references = new Map(matches.map((match) => [match[2], match[1] === 'x']))
- if (references.size !== rows.length) throw new Error('Duplicate checklist ticket')
- for (const row of rows) {
- if (!references.has(row.file) || references.get(row.file) !== row.closed) throw new Error(`Checklist mismatch: ${row.file}`)
- }
- console.log(`PASS checklist=${rows.length}, closed=${rows.filter((row) => row.closed).length}, Destination retained`)
- } else {
- console.log('## 任务清单\n\n本节为全部子票的唯一完整任务清单;每票一项,历史说明保持在原节,状态以票面和地图快照为准。\n')
- for (const row of rows) console.log(`- [${row.closed ? 'x' : ' '}] [${row.title}](issues/${row.file})`)
- }
|