/** * 软著源程序文档生成脚本 * 规则:每页 60 行有效代码;超过 60 页时取前 30 页 + 后 30 页; * 自动清理文件头第三方署名/版权行;排除声明文件与空文件。 * 用法:node docs/软著/gen_source_doc.mjs */ import fs from 'fs'; import path from 'path'; const ROOT = path.resolve(import.meta.dirname, '../../assets/script'); const OUT_DIR = import.meta.dirname; const OUT = path.join(OUT_DIR, '像素大勇者游戏软件_V1.0_源程序.txt'); const LINES_PER_PAGE = 60; const KEEP = 30; // 前 30 页 + 后 30 页 const SOFTWARE = '像素大勇者游戏软件 V1.0'; /** 需要从文件头清除的第三方署名 / 编辑器元数据行 */ const BAD_PATTERNS = [ /dgflash/i, /落日故人/, /QQ\s*\d+/, /Copyright \(c\)/i, /@(Author|LastEditors?|Date|LastEditTime)/i, ]; function walk(dir) { const out = []; for (const e of fs.readdirSync(dir, { withFileTypes: true })) { const p = path.join(dir, e.name); if (e.isDirectory()) out.push(...walk(p)); else out.push(p); } return out; } const rel = (f) => path.relative(ROOT, f).replace(/\\/g, '/'); // 1. 收集源文件:仅 .ts,排除 .d.ts 声明文件、空文件、非脚本目录 let files = walk(ROOT).filter( (f) => f.endsWith('.ts') && !f.endsWith('.d.ts') && !rel(f).includes('hit-flash-white/shaders') && fs.readFileSync(f, 'utf8').trim().length > 0 ); // 2. 排序:入口 Main.ts 优先,其次初始化模块,其余按路径字母序 files.sort((a, b) => { const rank = (r) => r === 'Main.ts' ? 0 : r.startsWith('game/initialize') ? 1 : 2; return rank(rel(a)) - rank(rel(b)) || rel(a).localeCompare(rel(b)); }); // 3. 清理文件头(前 40 行)中的第三方署名行,统一换行符与缩进 function cleanFile(content) { const lines = content .replace(/\r\n/g, '\n') .replace(/\t/g, ' ') .split('\n'); const head = lines.slice(0, 40).filter( (l) => !BAD_PATTERNS.some((rx) => rx.test(l)) ); return [...head, ...lines.slice(40)]; } const all = []; for (const f of files) { all.push(`// ===================== 文件: ${rel(f)} =====================`); all.push(...cleanFile(fs.readFileSync(f, 'utf8'))); } // 4. 统计(审校用) const total = all.length; const blank = all.filter((l) => l.trim() === '').length; const comment = all.filter((l) => /^\s*(\/\/|\/\*|\*|\*\/)/.test(l)).length; const tooLong = all.filter((l) => l.length > 84).length; const totalPages = Math.ceil(total / LINES_PER_PAGE); // 5. 分页:超过 60 页取前 30 + 后 30 const pageIdx = totalPages > 60 ? [ ...Array.from({ length: KEEP }, (_, i) => i), ...Array.from({ length: KEEP }, (_, i) => totalPages - KEEP + i), ] : Array.from({ length: totalPages }, (_, i) => i); const submitPages = pageIdx.length; // 6. 生成文档(页眉:左侧软件全称+版本号,右侧页码) const out = []; for (const pi of pageIdx) { const pageNo = pageIdx.indexOf(pi) + 1; const start = pi * LINES_PER_PAGE; const body = all.slice(start, start + LINES_PER_PAGE); out.push(`${SOFTWARE}            第 ${pageNo} 页 共 ${submitPages} 页`); out.push('='.repeat(76)); out.push(...body); out.push(''); } fs.mkdirSync(OUT_DIR, { recursive: true }); fs.writeFileSync(OUT, out.join('\n'), 'utf8'); console.log('=== 源程序文档生成完成 ==='); console.log(`文件数: ${files.length}`); console.log(`总行数: ${total}(含文件分隔行)`); console.log(`总页数: ${totalPages},提交页数: ${submitPages}`); console.log(`空行占比: ${((blank / total) * 100).toFixed(1)}%`); console.log(`注释行占比: ${((comment / total) * 100).toFixed(1)}%`); console.log(`超 84 字符行数: ${tooLong}`); console.log(`输出: ${OUT}`);