auto-generate.ts 5.1 KB

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