auto-generate.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. /**
  2. * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
  3. * SPDX-License-Identifier: MIT
  4. */
  5. // @ts-ignore
  6. import * as path from 'path';
  7. // @ts-ignore
  8. import * as fs from 'fs/promises'; // 使用 fs.promises 处理异步操作
  9. import { load } from 'typedoc-plugin-markdown';
  10. import { Application, TSConfigReader, ProjectReflection } from 'typedoc';
  11. import { patchGeneratedApiDocs } from './patch';
  12. import { docLabelMap, overviewMetaJson } from './constants';
  13. async function generateDocs() {
  14. // @ts-ignore
  15. const projectRoot = path.resolve(__dirname, '../../../'); // Rush 项目根目录
  16. // @ts-ignore
  17. const packagesDir = path.join(projectRoot, 'packages'); // packages 目录
  18. // @ts-ignore
  19. const outputDir = path.join(__dirname, '../src/zh/auto-docs'); // 输出目录
  20. const packages: string[] = [];
  21. // 读取 packages 目录
  22. const firstLevelFiles = await fs.readdir(packagesDir); // 使用 fs.promises 读取目录
  23. for (const firstLevel of firstLevelFiles) {
  24. const firstLevelPath = path.resolve(packagesDir, firstLevel);
  25. const packageNames = await fs.readdir(firstLevelPath); // 异步读取包目录
  26. for (const packageName of packageNames) {
  27. const packagePath = path.join(firstLevelPath, packageName);
  28. const packageJsonPath = path.join(packagePath, 'package.json');
  29. const tsconfigPath = path.join(packagePath, 'tsconfig.json');
  30. // 检查是否是有效的包
  31. if (!(await fileExists(packageJsonPath)) || !(await fileExists(tsconfigPath))) {
  32. // console.log(`Skipping ${packagePath}: Missing package.json or tsconfig.json`);
  33. } else {
  34. packages.push(packageName);
  35. console.log(`Generating docs for package: ${packageName}`);
  36. // 输出目录为 auto-docs/{packageName}
  37. const packageOutputDir = path.join(outputDir, packageName);
  38. // 创建 Typedoc 应用实例
  39. const app = new Application();
  40. app.options.addReader(new TSConfigReader());
  41. load(app);
  42. // 配置 Typedoc
  43. app.bootstrap({
  44. entryPoints: [path.join(packagePath, 'src')],
  45. tsconfig: tsconfigPath,
  46. out: packageOutputDir,
  47. plugin: ['typedoc-plugin-markdown'], // 使用 Markdown 插件
  48. theme: 'markdown', // Markdown 模式不依赖 HTML 主题
  49. exclude: ['**/__tests__/**', 'vitest.config.ts', 'vitest.setup.ts'],
  50. basePath: packagePath,
  51. excludePrivate: true,
  52. excludeProtected: true,
  53. disableSources: true,
  54. readme: 'none',
  55. githubPages: true,
  56. hideGenerator: true,
  57. skipErrorChecking: true,
  58. requiredToBeDocumented: ['Class', 'Function', 'Interface'],
  59. // @ts-expect-error MarkdownTheme has no export
  60. hideBreadcrumbs: true,
  61. hideMembersSymbol: true,
  62. allReflectionsHaveOwnDocument: true,
  63. });
  64. // 生成文档
  65. const project: ProjectReflection | undefined = app.convert();
  66. if (project) {
  67. await app.generateDocs(project, packageOutputDir);
  68. await patchGeneratedApiDocs(packageOutputDir);
  69. const files = await fs.readdir(packageOutputDir);
  70. const packageMetaJson: Record<string, string>[] = [];
  71. for (const file of files) {
  72. if (!['.nojekyll', 'README.md'].includes(file)) {
  73. packageMetaJson.push({
  74. type: 'dir',
  75. name: file,
  76. label: docLabelMap[file] || file,
  77. });
  78. }
  79. }
  80. await fs.writeFile(
  81. path.join(packageOutputDir, '_meta.json'),
  82. JSON.stringify(packageMetaJson),
  83. 'utf-8'
  84. );
  85. await fs.unlink(path.join(packageOutputDir, 'README.md')); // 删除 README.md 文件
  86. console.log(`Docs generated for ${packageName} at ${packageOutputDir}`);
  87. } else {
  88. console.error(`Failed to generate docs for ${packageName}: Conversion failed`);
  89. // @ts-ignore
  90. process.exit();
  91. }
  92. }
  93. }
  94. }
  95. // 写入 index.md 和 _meta.json
  96. await fs.writeFile(
  97. // @ts-ignore
  98. path.resolve(__dirname, '../src/zh/auto-docs/index.md'),
  99. overviewMetaJson,
  100. 'utf-8'
  101. );
  102. const metaJson: (string | Record<string, string>)[] = [];
  103. metaJson.push('index');
  104. packages.forEach((packageName) => {
  105. metaJson.push({
  106. type: 'dir',
  107. label: `@flowgram.ai/${packageName}`,
  108. name: packageName,
  109. });
  110. });
  111. await fs.writeFile(
  112. // @ts-ignore
  113. path.resolve(__dirname, '../src/zh/auto-docs/_meta.json'),
  114. JSON.stringify(metaJson),
  115. 'utf-8'
  116. );
  117. }
  118. // 检查文件是否存在
  119. async function fileExists(path: string): Promise<boolean> {
  120. try {
  121. await fs.access(path);
  122. return true;
  123. } catch {
  124. return false;
  125. }
  126. }
  127. generateDocs().catch((error) => {
  128. console.error('Error generating docs:', error);
  129. });