Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs: Use stories-mdx and autodocs tags instead of standalone: false in index #20424

Merged
merged 6 commits into from
Dec 30, 2022
Merged
Show file tree
Hide file tree
Changes from 2 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
32 changes: 17 additions & 15 deletions code/lib/core-server/src/utils/StoryIndexGenerator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,6 @@ describe('StoryIndexGenerator', () => {
"id": "page--docs",
"importPath": "./src/nested/Page.stories.mdx",
"name": "docs",
"standalone": false,
"storiesImports": Array [],
"tags": Array [
"stories-mdx",
Expand Down Expand Up @@ -336,7 +335,6 @@ describe('StoryIndexGenerator', () => {
"id": "b--docs",
"importPath": "./src/B.stories.ts",
"name": "docs",
"standalone": false,
"storiesImports": Array [],
"tags": Array [
"autodocs",
Expand All @@ -360,7 +358,6 @@ describe('StoryIndexGenerator', () => {
"id": "d--docs",
"importPath": "./src/D.stories.jsx",
"name": "docs",
"standalone": false,
"storiesImports": Array [],
"tags": Array [
"autodocs",
Expand Down Expand Up @@ -451,6 +448,21 @@ describe('StoryIndexGenerator', () => {
`);
});

it('adds the autodocs tag to the autogenerated docs entries', async () => {
const specifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
'./src/**/*.stories.(ts|js|jsx)',
options
);

const generator = new StoryIndexGenerator([specifier], autodocsTrueOptions);
await generator.initialize();

const index = await generator.getIndex();
expect(index.entries['first-nested-deeply-f--docs'].tags).toEqual(
expect.arrayContaining(['autodocs'])
);
});

it('throws an error if you attach a MetaOf entry to a tagged autodocs entry', async () => {
const csfSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
'./src/B.stories.ts',
Expand Down Expand Up @@ -494,7 +506,6 @@ describe('StoryIndexGenerator', () => {
"id": "a--docs",
"importPath": "./src/docs2/MetaOf.mdx",
"name": "docs",
"standalone": true,
"storiesImports": Array [
"./src/A.stories.js",
],
Expand Down Expand Up @@ -537,7 +548,6 @@ describe('StoryIndexGenerator', () => {
"id": "duplicate-a--docs",
"importPath": "./duplicate/A.stories.js",
"name": "docs",
"standalone": false,
"storiesImports": Array [
"./duplicate/SecondA.stories.js",
],
Expand Down Expand Up @@ -607,7 +617,6 @@ describe('StoryIndexGenerator', () => {
"id": "a--docs",
"importPath": "./src/docs2/MetaOf.mdx",
"name": "docs",
"standalone": true,
"storiesImports": Array [
"./src/A.stories.js",
],
Expand All @@ -621,7 +630,6 @@ describe('StoryIndexGenerator', () => {
"id": "a--second-docs",
"importPath": "./src/docs2/SecondMetaOf.mdx",
"name": "Second Docs",
"standalone": true,
"storiesImports": Array [
"./src/A.stories.js",
],
Expand All @@ -646,7 +654,6 @@ describe('StoryIndexGenerator', () => {
"id": "docs2-yabbadabbadooo--docs",
"importPath": "./src/docs2/Title.mdx",
"name": "docs",
"standalone": true,
"storiesImports": Array [],
"tags": Array [
"docs",
Expand All @@ -658,7 +665,6 @@ describe('StoryIndexGenerator', () => {
"id": "notitle--docs",
"importPath": "./src/docs2/NoTitle.mdx",
"name": "docs",
"standalone": true,
"storiesImports": Array [],
"tags": Array [
"docs",
Expand Down Expand Up @@ -746,7 +752,6 @@ describe('StoryIndexGenerator', () => {
"id": "a--info",
"importPath": "./src/docs2/MetaOf.mdx",
"name": "Info",
"standalone": true,
"storiesImports": Array [
"./src/A.stories.js",
],
Expand All @@ -760,7 +765,6 @@ describe('StoryIndexGenerator', () => {
"id": "a--second-docs",
"importPath": "./src/docs2/SecondMetaOf.mdx",
"name": "Second Docs",
"standalone": true,
"storiesImports": Array [
"./src/A.stories.js",
],
Expand All @@ -785,7 +789,6 @@ describe('StoryIndexGenerator', () => {
"id": "docs2-yabbadabbadooo--info",
"importPath": "./src/docs2/Title.mdx",
"name": "Info",
"standalone": true,
"storiesImports": Array [],
"tags": Array [
"docs",
Expand All @@ -797,7 +800,6 @@ describe('StoryIndexGenerator', () => {
"id": "notitle--info",
"importPath": "./src/docs2/NoTitle.mdx",
"name": "Info",
"standalone": true,
"storiesImports": Array [],
"tags": Array [
"docs",
Expand Down Expand Up @@ -825,7 +827,7 @@ describe('StoryIndexGenerator', () => {
});

describe('duplicates', () => {
it('warns when two standalone entries reference the same CSF file without a name', async () => {
it('warns when two MDX entries reference the same CSF file without a name', async () => {
const docsErrorSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
'./errors/DuplicateMetaOf.mdx',
options
Expand Down Expand Up @@ -853,7 +855,7 @@ describe('StoryIndexGenerator', () => {
);
});

it('warns when a standalone entry has the same name as a story', async () => {
it('warns when a MDX entry has the same name as a story', async () => {
const docsErrorSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
'./errors/MetaOfClashingName.mdx',
options
Expand Down
57 changes: 35 additions & 22 deletions code/lib/core-server/src/utils/StoryIndexGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import slash from 'slash';

import type {
IndexEntry,
StandaloneDocsIndexEntry,
StoryIndexEntry,
TemplateDocsIndexEntry,
DocsIndexEntry,
ComponentTitle,
NormalizedStoriesSpecifier,
StoryIndexer,
Expand All @@ -26,17 +25,25 @@ import { getStorySortParameter, NoMetaError } from '@storybook/csf-tools';
import { toId } from '@storybook/csf';
import { analyze } from '@storybook/docs-mdx';

/** A .mdx file will produce a "standalone" docs entry */
type DocsCacheEntry = StandaloneDocsIndexEntry;
/** A .mdx file will produce a docs entry */
type DocsCacheEntry = DocsIndexEntry;
/** A *.stories.* file will produce a list of stories and possibly a docs entry */
type StoriesCacheEntry = {
entries: (StoryIndexEntry | TemplateDocsIndexEntry)[];
entries: (StoryIndexEntry | DocsIndexEntry)[];
dependents: Path[];
type: 'stories';
};
type CacheEntry = false | StoriesCacheEntry | DocsCacheEntry;
type SpecifierStoriesCache = Record<Path, CacheEntry>;

export const AUTODOCS_TAG = 'autodocs';
export const STORIES_MDX_TAG = 'stories-mdx';

/** Was this docs entry generated by a .mdx file? (see discussion below) */
export function isMdxEntry({ tags }: DocsIndexEntry) {
return !tags?.includes(AUTODOCS_TAG) && !tags?.includes(STORIES_MDX_TAG);
}

export class DuplicateEntriesError extends Error {
entries: IndexEntry[];

Expand Down Expand Up @@ -67,13 +74,17 @@ const makeAbsolute = (otherImport: Path, normalizedPath: Path, workingDir: Path)
*
* A stories file is indexed by an indexer (passed in), which produces a list of stories.
* - If the stories have the `parameters.docsOnly` setting, they are disregarded.
* - If the indexer is a "docs template" indexer, OR autodocs is enabled,
* a templated docs entry is added pointing to the story file.
* - If the stories have the 'stories-mdx' tag (i.e. were generated by a .stories.mdx file),
* OR autodocs is enabled, a docs entry is added pointing to the story file.
*
* A (modern) docs (.mdx) file is indexed, a docs entry is added.
*
* A (modern) docs file is indexed, a standalone docs entry is added.
* In the preview, a docs entry with either the `autodocs` or `stories-mdx` tags will be rendered
* as a CSF file that exports an MDX template on the `docs.page` parameter, whereas
* other docs entries are rendered as MDX files directly.
*
* The entries are "uniq"-ed and sorted. Stories entries are preferred to docs entries and
* standalone docs entries are preferred to templates (with warnings).
* MDX docs entries are preferred to CSF templates (with warnings).
*/
export class StoryIndexGenerator {
// An internal cache mapping specifiers to a set of path=><set of stories>
Expand Down Expand Up @@ -236,12 +247,12 @@ export class StoryIndexGenerator {

if (!this.options.docs.disable && csf.stories.length) {
const { autodocs } = this.options.docs;
const autodocsOptedIn =
autodocs === true || (autodocs === 'tag' && componentTags.includes('autodocs'));
const componentAutodocs = componentTags.includes(AUTODOCS_TAG);
const autodocsOptedIn = autodocs === true || (autodocs === 'tag' && componentAutodocs);
// We need a docs entry attached to the CSF file if either:
// a) it is a stories.mdx transpiled to CSF, OR
// b) we have docs page enabled for this file
if (componentTags.includes('stories-mdx') || autodocsOptedIn) {
if (componentTags.includes(STORIES_MDX_TAG) || autodocsOptedIn) {
const name = this.options.docs.defaultName;
const id = toId(csf.meta.title, name);
entries.unshift({
Expand All @@ -250,9 +261,12 @@ export class StoryIndexGenerator {
name,
importPath,
type: 'docs',
tags: [...componentTags, 'docs'],
tags: [
...componentTags,
'docs',
...(autodocsOptedIn && !componentAutodocs ? [AUTODOCS_TAG] : []),
],
storiesImports: [],
standalone: false,
});
}
}
Expand Down Expand Up @@ -340,7 +354,6 @@ export class StoryIndexGenerator {
storiesImports: dependencies.map((dep) => dep.entries[0].importPath),
type: 'docs',
tags: [...(result.tags || []), 'docs'],
standalone: true,
};
return docsEntry;
} catch (err) {
Expand All @@ -353,7 +366,7 @@ export class StoryIndexGenerator {
let firstIsBetter = true;
if (secondEntry.type === 'story') {
firstIsBetter = false;
} else if (secondEntry.standalone && firstEntry.type === 'docs' && !firstEntry.standalone) {
} else if (isMdxEntry(secondEntry) && firstEntry.type === 'docs' && !isMdxEntry(firstEntry)) {
firstIsBetter = false;
}
const betterEntry = firstIsBetter ? firstEntry : secondEntry;
Expand All @@ -369,7 +382,7 @@ export class StoryIndexGenerator {
]);

if (betterEntry.type === 'story') {
const worseDescriptor = worseEntry.standalone
const worseDescriptor = isMdxEntry(worseEntry)
? `component docs page`
: `automatically generated docs page`;
if (betterEntry.name === this.options.docs.defaultName) {
Expand All @@ -381,18 +394,18 @@ export class StoryIndexGenerator {
`🚨 You have a story for ${betterEntry.title} with the same name as your ${worseDescriptor} (${worseEntry.name}), so the docs page is being dropped. ${changeDocsName}`
);
}
} else if (betterEntry.standalone) {
// Both entries are standalone but pointing at the same place
if (worseEntry.standalone) {
} else if (isMdxEntry(betterEntry)) {
// Both entries are MDX but pointing at the same place
if (isMdxEntry(worseEntry)) {
logger.warn(
`🚨 You have two component docs pages with the same name ${betterEntry.title}:${betterEntry.name}. ${changeDocsName}`
);
}

// If you link a file to a tagged CSF file, you have probably made a mistake
if (worseEntry.tags?.includes('autodocs'))
if (worseEntry.tags?.includes(AUTODOCS_TAG) && this.options.docs.autodocs !== true)
throw new Error(
`You created a component docs page for ${worseEntry.title} (${betterEntry.importPath}), but also tagged the CSF file (${worseEntry.importPath}) with 'autodocs'. This is probably a mistake.`
`You created a component docs page for ${worseEntry.title} (${betterEntry.importPath}), but also tagged the CSF file (${worseEntry.importPath}) with '${AUTODOCS_TAG}'. This is probably a mistake.`
);

// Otherwise the existing entry is created by `autodocs=true` which allowed to be overridden.
Expand Down
11 changes: 0 additions & 11 deletions code/lib/core-server/src/utils/stories-json.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ describe('useStoriesJson', () => {
"id": "a--docs",
"importPath": "./src/docs2/MetaOf.mdx",
"name": "docs",
"standalone": true,
"storiesImports": Array [
"./src/A.stories.js",
],
Expand All @@ -135,7 +134,6 @@ describe('useStoriesJson', () => {
"id": "a--second-docs",
"importPath": "./src/docs2/SecondMetaOf.mdx",
"name": "Second Docs",
"standalone": true,
"storiesImports": Array [
"./src/A.stories.js",
],
Expand Down Expand Up @@ -182,7 +180,6 @@ describe('useStoriesJson', () => {
"id": "docs2-notitle--docs",
"importPath": "./src/docs2/NoTitle.mdx",
"name": "docs",
"standalone": true,
"storiesImports": Array [],
"tags": Array [
"docs",
Expand All @@ -194,7 +191,6 @@ describe('useStoriesJson', () => {
"id": "docs2-yabbadabbadooo--docs",
"importPath": "./src/docs2/Title.mdx",
"name": "docs",
"standalone": true,
"storiesImports": Array [],
"tags": Array [
"docs",
Expand Down Expand Up @@ -227,7 +223,6 @@ describe('useStoriesJson', () => {
"id": "nested-page--docs",
"importPath": "./src/nested/Page.stories.mdx",
"name": "docs",
"standalone": false,
"storiesImports": Array [],
"tags": Array [
"stories-mdx",
Expand Down Expand Up @@ -292,7 +287,6 @@ describe('useStoriesJson', () => {
"docsOnly": true,
"fileName": "./src/docs2/MetaOf.mdx",
},
"standalone": true,
"storiesImports": Array [
"./src/A.stories.js",
],
Expand All @@ -312,7 +306,6 @@ describe('useStoriesJson', () => {
"docsOnly": true,
"fileName": "./src/docs2/SecondMetaOf.mdx",
},
"standalone": true,
"storiesImports": Array [
"./src/A.stories.js",
],
Expand Down Expand Up @@ -383,7 +376,6 @@ describe('useStoriesJson', () => {
"docsOnly": true,
"fileName": "./src/docs2/NoTitle.mdx",
},
"standalone": true,
"storiesImports": Array [],
"story": "docs",
"tags": Array [
Expand All @@ -401,7 +393,6 @@ describe('useStoriesJson', () => {
"docsOnly": true,
"fileName": "./src/docs2/Title.mdx",
},
"standalone": true,
"storiesImports": Array [],
"story": "docs",
"tags": Array [
Expand Down Expand Up @@ -452,7 +443,6 @@ describe('useStoriesJson', () => {
"docsOnly": true,
"fileName": "./src/nested/Page.stories.mdx",
},
"standalone": false,
"storiesImports": Array [],
"story": "docs",
"tags": Array [
Expand Down Expand Up @@ -944,7 +934,6 @@ describe('convertToIndexV3', () => {
storiesImports: ['./src/A.stories.js'],
title: 'A',
type: 'docs',
standalone: true,
},
'a--story-one': {
id: 'a--story-one',
Expand Down
8 changes: 5 additions & 3 deletions code/lib/core-server/src/utils/summarizeIndex.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { StoryIndex } from '@storybook/types';

import { STORIES_MDX_TAG, isMdxEntry, AUTODOCS_TAG } from './StoryIndexGenerator';

export function summarizeIndex(storyIndex: StoryIndex) {
let storyCount = 0;
let autodocsCount = 0;
Expand All @@ -9,11 +11,11 @@ export function summarizeIndex(storyIndex: StoryIndex) {
if (entry.type === 'story') {
storyCount += 1;
} else if (entry.type === 'docs') {
if (entry.standalone) {
if (isMdxEntry(entry)) {
mdxCount += 1;
} else if (entry.importPath.endsWith('.mdx')) {
} else if (entry.tags.includes(STORIES_MDX_TAG)) {
storiesMdxCount += 1;
} else {
} else if (entry.tags.includes(AUTODOCS_TAG)) {
autodocsCount += 1;
}
}
Expand Down
Loading