Skip to content

Commit

Permalink
Merge pull request #20424 from storybookjs/replace-standalone-with-au…
Browse files Browse the repository at this point in the history
…todocs

Docs: Use `stories-mdx` and `autodocs` tags instead of `standalone: false` in index
  • Loading branch information
shilman authored Dec 30, 2022
2 parents 35d7010 + c57c189 commit 72dac4e
Show file tree
Hide file tree
Showing 20 changed files with 129 additions and 324 deletions.
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
12 changes: 0 additions & 12 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 Expand Up @@ -976,7 +965,6 @@ describe('convertToIndexV3', () => {
"docsOnly": true,
"fileName": "./src/docs2/MetaOf.mdx",
},
"standalone": true,
"storiesImports": Array [
"./src/A.stories.js",
],
Expand Down
Loading

0 comments on commit 72dac4e

Please sign in to comment.