transform-jsdoc-plugin.js 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. /**
  2. * Vite plugin that cleans up JSDoc comments in component source files for Storybook.
  3. *
  4. * **Why this exists:**
  5. * - Our codebase uses custom JSDoc tags (@description, @docsCategory, @docsPage) for documentation generation
  6. * - These custom tags clutter Storybook's auto-generated prop tables and component descriptions
  7. * - This plugin removes the tag labels while preserving the actual content
  8. * - TypeScript's react-docgen-typescript can then properly extract clean descriptions
  9. *
  10. * **What it does:**
  11. * - Removes the `@description` tag line (content is preserved as the first JSDoc paragraph)
  12. * - Removes `@docsCategory` and `@docsPage` tags entirely
  13. * - Keeps `@example` tags (these are extracted by extractJSDocPlugin)
  14. * - Only processes component source files (not node_modules or story files)
  15. *
  16. * **Example transformation:**
  17. * ```
  18. * Before:
  19. * /**
  20. * * @description
  21. * * A component for displaying an input with a prefix.
  22. * * @docsCategory form-components
  23. * *\/
  24. *
  25. * After:
  26. * /**
  27. * * A component for displaying an input with a prefix.
  28. * *\/
  29. * ```
  30. *
  31. * @returns {import('vite').Plugin}
  32. */
  33. export function transformJSDocPlugin() {
  34. return {
  35. name: 'transform-jsdoc',
  36. enforce: 'pre', // Run before other plugins
  37. transform(code, id) {
  38. // Only process source TypeScript/TSX files (not node_modules, not stories)
  39. if (!id.endsWith('.tsx') && !id.endsWith('.ts')) {
  40. return null;
  41. }
  42. if (id.includes('node_modules') || id.includes('.stories.')) {
  43. return null;
  44. }
  45. // Only process files with @description tag
  46. if (!code.includes('@description')) {
  47. return null;
  48. }
  49. // Transform JSDoc comments: remove @description/@docsCategory/@docsPage tags
  50. const transformed = code.replace(
  51. /\/\*\*([\s\S]*?)\*\//g,
  52. (match, content) => {
  53. // Only transform JSDoc blocks that have @description
  54. if (!content.includes('@description')) {
  55. return match;
  56. }
  57. const lines = content.split('\n');
  58. const transformedLines = [];
  59. let skipNextEmptyLine = false;
  60. for (let i = 0; i < lines.length; i++) {
  61. const line = lines[i];
  62. // Remove @description line entirely
  63. if (line.match(/\s*\*\s*@description\s*$/)) {
  64. skipNextEmptyLine = true;
  65. continue;
  66. }
  67. // Remove @docsCategory and @docsPage lines
  68. if (line.match(/\s*\*\s*@(docsCategory|docsPage)\s+[\w-]+/)) {
  69. continue;
  70. }
  71. // Skip empty line after @description
  72. if (skipNextEmptyLine && line.match(/^\s*\*\s*$/)) {
  73. skipNextEmptyLine = false;
  74. continue;
  75. }
  76. transformedLines.push(line);
  77. }
  78. return `/**${transformedLines.join('\n')}*/`;
  79. },
  80. );
  81. return {
  82. code: transformed,
  83. map: null,
  84. };
  85. },
  86. };
  87. }