generate-typescript-docs.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. /* tslint:disable:no-console */
  2. import fs from 'fs-extra';
  3. import klawSync from 'klaw-sync';
  4. import path, { extname } from 'path';
  5. import { deleteGeneratedDocs } from './docgen-utils';
  6. import { TypeMap } from './typescript-docgen-types';
  7. import { TypescriptDocsParser } from './typescript-docs-parser';
  8. import { TypescriptDocsRenderer } from './typescript-docs-renderer';
  9. interface DocsSectionConfig {
  10. sourceDirs: string[];
  11. exclude?: RegExp[];
  12. outputPath: string;
  13. }
  14. const sections: DocsSectionConfig[] = [
  15. {
  16. sourceDirs: [
  17. 'packages/core/src/',
  18. 'packages/common/src/',
  19. ],
  20. exclude: [
  21. /generated-shop-types/,
  22. ],
  23. outputPath: 'typescript-api',
  24. },
  25. {
  26. sourceDirs: ['packages/asset-server-plugin/src/'],
  27. outputPath: 'plugins',
  28. },
  29. {
  30. sourceDirs: ['packages/email-plugin/src/'],
  31. outputPath: 'plugins',
  32. },
  33. {
  34. sourceDirs: ['packages/admin-ui-plugin/src/'],
  35. outputPath: 'plugins',
  36. },
  37. {
  38. sourceDirs: ['packages/elasticsearch-plugin/src/'],
  39. outputPath: 'plugins',
  40. },
  41. ];
  42. generateTypescriptDocs(sections);
  43. const watchMode = !!process.argv.find(arg => arg === '--watch' || arg === '-w');
  44. if (watchMode) {
  45. console.log(`Watching for changes to source files...`);
  46. sections.forEach(section => {
  47. section.sourceDirs.forEach(dir => {
  48. fs.watch(dir, { recursive: true }, (eventType, file) => {
  49. if (extname(file) === '.ts') {
  50. console.log(`Changes detected in ${dir}`);
  51. generateTypescriptDocs([section], true);
  52. }
  53. });
  54. });
  55. });
  56. }
  57. /**
  58. * Uses the TypeScript compiler API to parse the given files and extract out the documentation
  59. * into markdown files
  60. */
  61. function generateTypescriptDocs(config: DocsSectionConfig[], isWatchMode: boolean = false) {
  62. const timeStart = +new Date();
  63. // This map is used to cache types and their corresponding Hugo path. It is used to enable
  64. // hyperlinking from a member's "type" to the definition of that type.
  65. const globalTypeMap: TypeMap = new Map();
  66. if (!isWatchMode) {
  67. for (const { outputPath, sourceDirs } of config) {
  68. deleteGeneratedDocs(absOutputPath(outputPath));
  69. }
  70. }
  71. for (const { outputPath, sourceDirs, exclude } of config) {
  72. const sourceFilePaths = getSourceFilePaths(sourceDirs, exclude);
  73. const docsPages = new TypescriptDocsParser().parse(sourceFilePaths);
  74. for (const page of docsPages) {
  75. const { category, fileName, declarations } = page;
  76. for (const declaration of declarations) {
  77. const pathToTypeDoc = `${outputPath}/${category ? category + '/' : ''}${
  78. fileName === '_index' ? '' : fileName
  79. }#${toHash(declaration.title)}`;
  80. globalTypeMap.set(declaration.title, pathToTypeDoc);
  81. }
  82. }
  83. const docsUrl = `/docs`;
  84. const generatedCount = new TypescriptDocsRenderer().render(
  85. docsPages,
  86. docsUrl,
  87. absOutputPath(outputPath),
  88. globalTypeMap,
  89. );
  90. if (generatedCount) {
  91. console.log(
  92. `Generated ${generatedCount} typescript api docs for "${outputPath}" in ${+new Date() -
  93. timeStart}ms`,
  94. );
  95. }
  96. }
  97. }
  98. function toHash(title: string): string {
  99. return title.replace(/\s/g, '').toLowerCase();
  100. }
  101. function absOutputPath(outputPath: string): string {
  102. return path.join(__dirname, '../../docs/content/docs/', outputPath);
  103. }
  104. function getSourceFilePaths(sourceDirs: string[], excludePatterns: RegExp[] = []): string[] {
  105. return sourceDirs
  106. .map(scanPath =>
  107. klawSync(path.join(__dirname, '../../', scanPath), {
  108. nodir: true,
  109. filter: item => {
  110. if (path.extname(item.path) === '.ts') {
  111. for (const pattern of excludePatterns) {
  112. if (pattern.test(item.path)) {
  113. return false;
  114. }
  115. }
  116. return true;
  117. }
  118. return false;
  119. },
  120. traverseAll: true,
  121. }),
  122. )
  123. .reduce((allFiles, files) => [...allFiles, ...files], [])
  124. .map(item => item.path);
  125. }