Last active
December 7, 2025 23:32
-
-
Save dannyhw/1ea1fe055594367063f00fe4b9c2cd3a to your computer and use it in GitHub Desktop.
get react native list of stories with node
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { normalizeStories, loadMainConfig } from 'storybook/internal/common'; | |
| import { readFileSync } from 'node:fs'; | |
| import { sync as globSync } from 'glob'; | |
| import path from 'path'; | |
| import { CsfFile, loadCsf } from 'storybook/internal/csf-tools'; | |
| import { toId } from 'storybook/internal/csf'; | |
| import { | |
| type StoryIndex, | |
| type IndexedCSFFile, | |
| type NormalizedStoriesSpecifier, | |
| } from 'storybook/internal/types'; | |
| import { userOrAutoTitleFromSpecifier } from 'storybook/internal/preview-api'; | |
| const cwd = process.cwd(); | |
| const makeTitle = (fileName: string, specifier: NormalizedStoriesSpecifier, userTitle: string) => { | |
| const title = userOrAutoTitleFromSpecifier(fileName, specifier, userTitle); | |
| if (title) { | |
| return title.replace('./', ''); | |
| } else if (userTitle) { | |
| return userTitle.replace('./', ''); | |
| } else { | |
| console.error('Could not generate title!!'); | |
| process.exit(1); | |
| } | |
| }; | |
| function ensureRelativePathHasDot(relativePath: string) { | |
| return relativePath.startsWith('.') ? relativePath : `./${relativePath}`; | |
| } | |
| export async function buildIndex({ configPath }: { configPath: string }) { | |
| const main = await loadMainConfig({ configDir: configPath, cwd }); | |
| if (!main.stories || !Array.isArray(main.stories)) { | |
| throw new Error('No stories found'); | |
| } | |
| const storiesSpecifiers = normalizeStories(main.stories, { | |
| configDir: configPath, | |
| workingDir: cwd, | |
| }); | |
| const specifierStoryPaths = storiesSpecifiers.map((specifier) => { | |
| return globSync(specifier.files, { | |
| cwd: path.resolve(process.cwd(), specifier.directory), | |
| absolute: true, | |
| // default to always ignore (exclude) anything in node_modules | |
| ignore: ['**/node_modules'], | |
| }).map((storyPath: string) => { | |
| const normalizePathForWindows = (str: string) => | |
| path.sep === '\\' ? str.replace(/\\/g, '/') : str; | |
| return normalizePathForWindows(storyPath); | |
| }); | |
| }); | |
| const csfStories = specifierStoryPaths.reduce((acc, specifierStoryPathList, specifierIndex) => { | |
| const paths = specifierStoryPathList.map((storyPath) => { | |
| const code = readFileSync(storyPath, { encoding: 'utf-8' }).toString(); | |
| const relativePath = ensureRelativePathHasDot(path.posix.relative(cwd, storyPath)); | |
| return { | |
| result: loadCsf(code, { | |
| fileName: storyPath, | |
| makeTitle: (userTitle) => | |
| makeTitle(relativePath, storiesSpecifiers[specifierIndex], userTitle), | |
| }).parse(), | |
| specifier: storiesSpecifiers[specifierIndex], | |
| fileName: relativePath, | |
| }; | |
| }); | |
| return [...acc, ...paths]; | |
| }, new Array<{ result: CsfFile & IndexedCSFFile; specifier: NormalizedStoriesSpecifier; fileName: string }>()); | |
| const index: StoryIndex = { | |
| v: 5, | |
| entries: {}, | |
| }; | |
| for (const { result, specifier, fileName } of csfStories) { | |
| const { meta, stories } = result; | |
| // console.log({ meta, stories, specifier, fileName }); | |
| if (stories && stories.length > 0) { | |
| for (const story of stories) { | |
| const id = toId(meta.title, story.name); | |
| index.entries[id] = { | |
| type: 'story', | |
| id, | |
| name: story.name, | |
| title: meta.title, | |
| importPath: `${specifier.directory}/${path.posix.relative(specifier.directory, fileName)}`, | |
| tags: ['story'], | |
| subtype: 'story', | |
| }; | |
| } | |
| } else { | |
| console.log(`No stories found for ${fileName}`); | |
| process.exit(1); | |
| } | |
| } | |
| return index; | |
| } | |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
saving this for future reference, this makes it possible to get the story index without depending on storybooks standalone buildIndex which currently doesn't work consistently for react native storybook.