Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions code/core/src/csf-tools/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './CsfFile.ts';
export * from './ConfigFile.ts';
export * from './getStorySortParameter.ts';
export * from './jsdoc.ts';
export * from './enrichCsf.ts';
export { babelParse } from 'storybook/internal/babel';
export { vitestTransform } from './vitest-plugin/transformer.ts';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { expect, it } from 'vitest';

import { dedent } from 'ts-dedent';

import { extractJSDocInfo } from './jsdocTags.ts';
import { extractJSDocInfo } from './jsdoc.ts';

it('should extract @summary tag', () => {
const code = dedent`description\n@summary\n my summary`;
Expand Down
72 changes: 72 additions & 0 deletions code/core/src/csf-tools/jsdoc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import type { Spec } from 'comment-parser';
import { parse } from 'comment-parser';

/** Compact JSDoc tag map: tag name → list of tag values (e.g. `@example a` → `{ example: ['a'] }`). */
export type JsDocTagMap = Record<string, string[]>;

function groupByTag(specs: Spec[]): Map<string, Spec[]> {
const groups = new Map<string, Spec[]>();
for (const spec of specs) {
const group = groups.get(spec.tag);
if (group) {
group.push(spec);
} else {
groups.set(spec.tag, [spec]);
}
}
return groups;
}

/**
* Splits a bare docblock body (no `/**` markers) into its description and a compact tag map.
*
* Shared by the server-side docgen providers so component descriptions and `@summary` / `@import`
* style tags read the same across renderers.
*/
export function extractJSDocInfo(jsdocComment: string) {
const lines = jsdocComment.split('\n');
const jsDoc = ['/**', ...lines.map((line) => ` * ${line}`), ' */'].join('\n');

// `comment-parser` applies one `spacing` mode to the whole block, so we parse twice on purpose.
// `preserve` keeps blank lines and line breaks in the block description so multi-paragraph
// component comments still render as Markdown (matching react-docgen's legacy `__docgenInfo`).
// `compact` collapses each tag value onto a single line, which is the shape tag consumers and
// snapshots already expect — using `preserve` for both would change multi-line tag values too.
const description = parse(jsDoc, { spacing: 'preserve' })[0].description;
const parsed = parse(jsDoc, { spacing: 'compact' });

return {
description,
tags: Object.fromEntries(
Array.from(groupByTag(parsed[0].tags), ([tag, specs]) => [
tag,
specs.map(
(spec) => (spec.type ? `{${spec.type}} ` : '') + `${spec.name} ${spec.description}`
),
])
) as JsDocTagMap,
};
}

/**
* Resolves the component-level description, summary, and tag map a docgen payload reports.
*
* The CSF `meta` docblock wins over the docgen engine's own component description, and an explicit
* `@describe` / `@desc` tag wins over the block description.
*/
export function extractComponentDescription(
metaJsDoc: string | undefined,
docgenDescription: string | undefined,
docgenJsDocTags?: JsDocTagMap
) {
const jsdocComment = metaJsDoc || docgenDescription;
const extracted = jsdocComment ? extractJSDocInfo(jsdocComment) : undefined;
const tags = docgenJsDocTags ?? extracted?.tags ?? {};
const description = extracted?.description;

return {
description: ((tags?.describe?.[0] || tags?.desc?.[0]) ?? description)?.trim(),
summary: tags.summary?.[0],
jsDocTags: tags,
};
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getComponentIdFromEntry } from 'storybook/internal/common';
import { extractDescription } from 'storybook/internal/csf-tools';
import { extractComponentDescription, extractDescription } from 'storybook/internal/csf-tools';
import type {
ComponentManifest,
ComponentSubcomponentManifest,
Expand All @@ -10,7 +10,6 @@ import type {
import path from 'pathe';

import type { ComponentDoc, PropItem } from './componentMeta/componentMetaExtractor.ts';
import { extractComponentDescription } from './extractComponentDescription.ts';
import { type ComponentRef, getImports } from './getComponentImports.ts';
import { type DocObj } from './reactDocgen.ts';
import { type ComponentDocWithExportName } from './reactDocgenTypescript.ts';
Expand Down

This file was deleted.

27 changes: 0 additions & 27 deletions code/renderers/react/src/componentManifest/jsdocTags.ts

This file was deleted.

2 changes: 1 addition & 1 deletion code/renderers/react/src/componentManifest/reactDocgen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { dirname, sep } from 'node:path';

import { babelParse, types as t } from 'storybook/internal/babel';
import { supportedExtensions } from 'storybook/internal/common';
import { extractJSDocInfo } from 'storybook/internal/csf-tools';
import { logger } from 'storybook/internal/node-logger';

import {
Expand All @@ -16,7 +17,6 @@ import { dedent } from 'ts-dedent';
import * as TsconfigPaths from 'tsconfig-paths';

import { type ComponentRef } from './getComponentImports.ts';
import { extractJSDocInfo } from './jsdocTags.ts';
import actualNameHandler from './reactDocgen/actualNameHandler.ts';
import { ReactDocgenResolveError } from './reactDocgen/docgenResolver.ts';
import exportNameHandler from './reactDocgen/exportNameHandler.ts';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { recast } from 'storybook/internal/babel';
import { storyNameFromExport } from 'storybook/internal/csf';
import { extractDescription, loadCsf } from 'storybook/internal/csf-tools';
import { extractDescription, extractJSDocInfo, loadCsf } from 'storybook/internal/csf-tools';

import { getCodeSnippet } from './generateCodeSnippet.ts';
import {
Expand All @@ -9,7 +9,6 @@ import {
type TypescriptOptions,
getComponents,
} from './getComponentImports.ts';
import { extractJSDocInfo } from './jsdocTags.ts';
import { extractDeclaredSubcomponents, findExactComponentMatch } from './subcomponents.ts';
import { cachedReadTextFileSync } from './utils.ts';

Expand Down