Skip to content

Commit 31d28ab

Browse files
authored
Merge pull request #23691 from storybookjs/indexer-api
Indexing: Introduce new experimental `indexer` API - NESTED PR!
2 parents 5490454 + 454e551 commit 31d28ab

32 files changed

+2975
-425
lines changed

code/addons/docs/src/preset.ts

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,7 @@ import remarkSlug from 'remark-slug';
33
import remarkExternalLinks from 'remark-external-links';
44
import { dedent } from 'ts-dedent';
55

6-
import type {
7-
IndexerOptions,
8-
StoryIndexer,
9-
DocsOptions,
10-
Options,
11-
StorybookConfig,
12-
} from '@storybook/types';
6+
import type { DocsOptions, Indexer, Options, StorybookConfig } from '@storybook/types';
137
import type { CsfPluginOptions } from '@storybook/csf-plugin';
148
import type { JSXOptions, CompileOptions } from '@storybook/mdx2-csf';
159
import { global } from '@storybook/global';
@@ -135,23 +129,35 @@ async function webpack(
135129
return result;
136130
}
137131

138-
const storyIndexers = (indexers: StoryIndexer[] | null) => {
139-
const mdxIndexer = async (fileName: string, opts: IndexerOptions) => {
132+
export const createStoriesMdxIndexer = (legacyMdx1?: boolean): Indexer => ({
133+
test: /(stories|story)\.mdx$/,
134+
index: async (fileName, opts) => {
140135
let code = (await fs.readFile(fileName, 'utf-8')).toString();
141-
const { compile } = global.FEATURES?.legacyMdx1
136+
const { compile } = legacyMdx1
142137
? await import('@storybook/mdx1-csf')
143138
: await import('@storybook/mdx2-csf');
144139
code = await compile(code, {});
145-
return loadCsf(code, { ...opts, fileName }).parse();
146-
};
147-
return [
148-
{
149-
test: /(stories|story)\.mdx$/,
150-
indexer: mdxIndexer,
151-
},
152-
...(indexers || []),
153-
];
154-
};
140+
const csf = loadCsf(code, { ...opts, fileName }).parse();
141+
142+
const { indexInputs, stories } = csf;
143+
144+
return indexInputs.map((input, index) => {
145+
const docsOnly = stories[index].parameters?.docsOnly;
146+
const tags = input.tags ? input.tags : [];
147+
if (docsOnly) {
148+
tags.push('stories-mdx-docsOnly');
149+
}
150+
// the mdx-csf compiler automatically adds the 'stories-mdx' tag to meta, here' we're just making sure it is always there
151+
if (!tags.includes('stories-mdx')) {
152+
tags.push('stories-mdx');
153+
}
154+
return { ...input, tags };
155+
});
156+
},
157+
});
158+
159+
const indexers: StorybookConfig['experimental_indexers'] = (existingIndexers) =>
160+
[createStoriesMdxIndexer(global.FEATURES?.legacyMdx1)].concat(existingIndexers || []);
155161

156162
const docs = (docsOptions: DocsOptions) => {
157163
return {
@@ -170,9 +176,9 @@ export const addons: StorybookConfig['addons'] = [
170176
* something down the dependency chain is using typescript namespaces, which are not supported by rollup-plugin-dts
171177
*/
172178
const webpackX = webpack as any;
173-
const storyIndexersX = storyIndexers as any;
179+
const indexersX = indexers as any;
174180
const docsX = docs as any;
175181

176182
ensureReactPeerDeps();
177183

178-
export { webpackX as webpack, storyIndexersX as storyIndexers, docsX as docs };
184+
export { webpackX as webpack, indexersX as experimental_indexers, docsX as docs };

code/lib/cli/src/generators/SERVER/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { Generator } from '../types';
33

44
const generator: Generator = async (packageManager, npmOptions, options) => {
55
await baseGenerator(packageManager, npmOptions, options, 'server', {
6-
extensions: ['json'],
6+
extensions: ['json', 'yaml', 'yml'],
77
});
88
};
99

code/lib/core-common/src/utils/validate-config.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ const renderers = ['html', 'preact', 'react', 'server', 'svelte', 'vue', 'vue3',
66

77
const rendererNames = [...renderers, ...renderers.map((renderer) => `@storybook/${renderer}`)];
88

9-
export function validateFrameworkName(frameworkName: string | undefined) {
9+
export function validateFrameworkName(
10+
frameworkName: string | undefined
11+
): asserts frameworkName is string {
1012
const automigrateMessage = `Please run 'npx storybook@next automigrate' to automatically fix your config.
1113
1214
See the migration guide for more information:

code/lib/core-server/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
"ws": "^8.2.3"
9999
},
100100
"devDependencies": {
101+
"@storybook/addon-docs": "workspace:*",
101102
"@types/compression": "^1.7.0",
102103
"@types/ip": "^1.1.0",
103104
"@types/node-fetch": "^2.5.7",

code/lib/core-server/src/build-dev.ts

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,15 @@ export async function buildDevStandalone(
3131
options: CLIOptions & LoadOptions & BuilderOptions
3232
): Promise<{ port: number; address: string; networkAddress: string }> {
3333
const { packageJson, versionUpdates } = options;
34-
const { version } = packageJson;
35-
invariant(version !== undefined, 'Expected package.json version to be defined.');
34+
invariant(
35+
packageJson.version !== undefined,
36+
`Expected package.json#version to be defined in the "${packageJson.name}" package}`
37+
);
3638
// updateInfo are cached, so this is typically pretty fast
3739
const [port, versionCheck] = await Promise.all([
3840
getServerPort(options.port),
3941
versionUpdates
40-
? updateCheck(version)
42+
? updateCheck(packageJson.version)
4143
: Promise.resolve({ success: false, cached: false, data: {}, time: Date.now() }),
4244
]);
4345

@@ -64,10 +66,9 @@ export async function buildDevStandalone(
6466

6567
const config = await loadMainConfig(options);
6668
const { framework } = config;
67-
invariant(framework, 'framework is required in Storybook v7');
6869
const corePresets = [];
6970

70-
const frameworkName = typeof framework === 'string' ? framework : framework.name;
71+
const frameworkName = typeof framework === 'string' ? framework : framework?.name;
7172
validateFrameworkName(frameworkName);
7273

7374
corePresets.push(join(frameworkName, 'preset'));
@@ -84,7 +85,8 @@ export async function buildDevStandalone(
8485
});
8586

8687
const { renderer, builder, disableTelemetry } = await presets.apply<CoreConfig>('core', {});
87-
invariant(builder, 'no builder configured!');
88+
89+
invariant(builder, 'No builder configured in core.builder');
8890

8991
if (!options.disableTelemetry && !disableTelemetry) {
9092
if (versionCheck.success && !versionCheck.cached) {
@@ -98,9 +100,8 @@ export async function buildDevStandalone(
98100
getManagerBuilder(),
99101
]);
100102

101-
const resolvedRenderer = renderer
102-
? resolveAddonName(options.configDir, renderer, options)
103-
: undefined;
103+
const resolvedRenderer = renderer && resolveAddonName(options.configDir, renderer, options);
104+
104105
// Load second pass: all presets are applied in order
105106
presets = await loadAllPresets({
106107
corePresets: [
@@ -128,10 +129,10 @@ export async function buildDevStandalone(
128129
fullOptions
129130
);
130131

131-
const previewTotalTime = previewResult && previewResult.totalTime;
132-
const managerTotalTime = managerResult ? managerResult.totalTime : undefined;
133-
const previewStats = previewResult && previewResult.stats;
134-
const managerStats = managerResult && managerResult.stats;
132+
const previewTotalTime = previewResult?.totalTime;
133+
const managerTotalTime = managerResult?.totalTime;
134+
const previewStats = previewResult?.stats;
135+
const managerStats = managerResult?.stats;
135136

136137
if (options.webpackStatsJson) {
137138
const target = options.webpackStatsJson === true ? options.outputDir : options.webpackStatsJson;
@@ -162,7 +163,7 @@ export async function buildDevStandalone(
162163
if (!options.quiet) {
163164
outputStartupInformation({
164165
updateInfo: versionCheck,
165-
version,
166+
version: packageJson.version,
166167
name,
167168
address,
168169
networkAddress,

code/lib/core-server/src/build-static.ts

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,16 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption
107107
...options,
108108
});
109109

110-
const [features, core, staticDirs, storyIndexers, stories, docsOptions] = await Promise.all([
111-
presets.apply<StorybookConfig['features']>('features'),
112-
presets.apply<CoreConfig>('core'),
113-
presets.apply<StorybookConfig['staticDirs']>('staticDirs'),
114-
presets.apply('storyIndexers', []),
115-
presets.apply('stories'),
116-
presets.apply<DocsOptions>('docs', {}),
117-
]);
110+
const [features, core, staticDirs, indexers, deprecatedStoryIndexers, stories, docsOptions] =
111+
await Promise.all([
112+
presets.apply<StorybookConfig['features']>('features'),
113+
presets.apply<CoreConfig>('core'),
114+
presets.apply<StorybookConfig['staticDirs']>('staticDirs'),
115+
presets.apply('experimental_indexers', []),
116+
presets.apply('storyIndexers', []),
117+
presets.apply('stories'),
118+
presets.apply<DocsOptions>('docs', {}),
119+
]);
118120

119121
const fullOptions: Options = {
120122
...options,
@@ -164,27 +166,27 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption
164166
const normalizedStories = normalizeStories(stories, directories);
165167
const generator = new StoryIndexGenerator(normalizedStories, {
166168
...directories,
167-
storyIndexers,
169+
storyIndexers: deprecatedStoryIndexers,
170+
indexers,
168171
docs: docsOptions,
169172
storiesV2Compatibility: !features?.storyStoreV7,
170173
storyStoreV7: !!features?.storyStoreV7,
171174
});
172175

173-
const initializedStoryIndexGeneratorPromise = generator.initialize().then(() => generator);
176+
initializedStoryIndexGenerator = generator.initialize().then(() => generator);
174177
effects.push(
175178
extractStoriesJson(
176179
join(options.outputDir, 'stories.json'),
177-
initializedStoryIndexGeneratorPromise,
180+
initializedStoryIndexGenerator as Promise<StoryIndexGenerator>,
178181
convertToIndexV3
179182
)
180183
);
181184
effects.push(
182185
extractStoriesJson(
183186
join(options.outputDir, 'index.json'),
184-
initializedStoryIndexGeneratorPromise
187+
initializedStoryIndexGenerator as Promise<StoryIndexGenerator>
185188
)
186189
);
187-
initializedStoryIndexGenerator = initializedStoryIndexGeneratorPromise;
188190
}
189191

190192
if (!core?.disableProjectJson) {

code/lib/core-server/src/presets/common-preset.ts

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,12 @@ import {
1111
import type {
1212
CLIOptions,
1313
CoreConfig,
14-
IndexerOptions,
14+
Indexer,
1515
Options,
1616
PresetPropertyFn,
1717
StorybookConfig,
18-
StoryIndexer,
1918
} from '@storybook/types';
20-
import { loadCsf, printConfig, readConfig } from '@storybook/csf-tools';
19+
import { printConfig, readConfig, readCsf } from '@storybook/csf-tools';
2120
import { join } from 'path';
2221
import { dedent } from 'ts-dedent';
2322
import fetch from 'node-fetch';
@@ -195,20 +194,15 @@ export const features = async (
195194
legacyDecoratorFileOrder: false,
196195
});
197196

198-
export const storyIndexers = async (indexers?: StoryIndexer[]) => {
199-
const csfIndexer = async (fileName: string, opts: IndexerOptions) => {
200-
const code = (await readFile(fileName, 'utf-8')).toString();
201-
return loadCsf(code, { ...opts, fileName }).parse();
202-
};
203-
return [
204-
{
205-
test: /(stories|story)\.(m?js|ts)x?$/,
206-
indexer: csfIndexer,
207-
},
208-
...(indexers || []),
209-
];
197+
export const csfIndexer: Indexer = {
198+
test: /\.stories\.(m?js|ts)x?$/,
199+
index: async (fileName, options) => (await readCsf(fileName, options)).parse().indexInputs,
210200
};
211201

202+
// eslint-disable-next-line @typescript-eslint/naming-convention
203+
export const experimental_indexers: StorybookConfig['experimental_indexers'] = (existingIndexers) =>
204+
[csfIndexer].concat(existingIndexers || []);
205+
212206
export const frameworkOptions = async (
213207
_: never,
214208
options: Options

0 commit comments

Comments
 (0)