generate-legacy-admin-inventory.mjs 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. #!/usr/bin/env node
  2. import { mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
  3. import { dirname, join, relative } from 'node:path';
  4. function argument(name) {
  5. const index = process.argv.indexOf(name);
  6. return index >= 0 ? process.argv[index + 1] : '';
  7. }
  8. const source = argument('--source');
  9. const output = argument('--output');
  10. if (!source || !output) throw new Error('用法:node scripts/generate-legacy-admin-inventory.mjs --source <ILSpy输出目录> --output <md>');
  11. const files = [];
  12. function walk(directory) {
  13. for (const entry of readdirSync(directory, { withFileTypes: true })) {
  14. const path = join(directory, entry.name);
  15. if (entry.isDirectory()) walk(path);
  16. else if (entry.name.endsWith('.cs') && path.includes('Areas.Admin.Controllers')) files.push(path);
  17. }
  18. }
  19. walk(source);
  20. const returnTypes = 'Task<[^>]+>|IActionResult|ActionResult|JsonResult|ContentResult|ViewResult|string|void|object|DataTable|PartialViewResult';
  21. const methodPattern = new RegExp(`public\\s+(?:async\\s+)?(?:${returnTypes})\\s+([A-Za-z0-9_]+)\\s*\\(`, 'g');
  22. const rows = [];
  23. for (const file of files) {
  24. const code = readFileSync(file, 'utf8');
  25. const controller = code.match(/public class ([A-Za-z0-9_]+)Controller/)?.[1];
  26. if (!controller) continue;
  27. const actions = [...code.matchAll(methodPattern)].map((match) => match[1]).filter((name) => !['OnActionExecuting', 'OnActionExecutionAsync'].includes(name));
  28. const sourcePath = relative(source, file);
  29. const moduleName = sourcePath.replace(/^ZoomLaCMS\.Areas\.Admin\.Controllers(?:\.|\/)?/, '').replace(/Controller\.cs$/, '').replaceAll('/', ' / ') || controller;
  30. rows.push({ controller, moduleName, actions, source: sourcePath });
  31. }
  32. rows.sort((left, right) => left.controller.localeCompare(right.controller, 'en'));
  33. const totalActions = rows.reduce((sum, row) => sum + row.actions.length, 0);
  34. const lines = [
  35. '# 旧 ASP.NET 管理后台控制器与动作清单',
  36. '',
  37. '> 本文件由 `scripts/generate-legacy-admin-inventory.mjs` 从 `ZoomLaCMS.dll` 的 ILSpy 反编译结果生成。发布包没有 `.sln`、`.csproj` 和原始 Razor 视图,因此这是后端动作基线,不等同于可恢复的页面像素基线。',
  38. '',
  39. `- 管理控制器:${rows.length}`,
  40. `- 可识别公开动作:${totalActions}`,
  41. '- 生成日期:2026-08-18',
  42. '',
  43. '| 模块 / 控制器 | 动作数 | 公开动作 |',
  44. '|---|---:|---|',
  45. ...rows.map((row) => `| ${row.moduleName} | ${row.actions.length} | ${row.actions.map((action) => `\`${action}\``).join('、')} |`),
  46. '',
  47. ];
  48. mkdirSync(dirname(output), { recursive: true });
  49. writeFileSync(output, lines.join('\n'));
  50. console.log(`Generated ${output}: ${rows.length} controllers, ${totalActions} actions.`);