diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts index 004803168e20..cb89b28b6094 100644 --- a/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts +++ b/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts @@ -8,12 +8,12 @@ import path from 'path'; import fs from 'fs-extra'; import { normalizeStoriesEntry } from '@storybook/core-common'; -import type { NormalizedStoriesSpecifier, StoryIndexer } from '@storybook/types'; +import type { NormalizedStoriesSpecifier, StoryIndexer, StoryIndexEntry } from '@storybook/types'; import { loadCsf, getStorySortParameter } from '@storybook/csf-tools'; import { toId } from '@storybook/csf'; import { logger } from '@storybook/node-logger'; -import { StoryIndexGenerator } from './StoryIndexGenerator'; +import { StoryIndexGenerator, DuplicateEntriesError } from './StoryIndexGenerator'; jest.mock('@storybook/csf-tools'); jest.mock('@storybook/csf', () => { @@ -902,6 +902,27 @@ describe('StoryIndexGenerator', () => { `"🚨 You have a story for A with the same name as your default docs entry name (Story One), so the docs page is being dropped. Consider changing the story name."` ); }); + it('warns when two duplicate stories exists, with duplicated entries details', async () => { + const generator = new StoryIndexGenerator([storiesSpecifier, docsSpecifier], { + ...options, + }); + await generator.initialize(); + const mockEntry: StoryIndexEntry = { + id: 'StoryId', + name: 'StoryName', + title: 'ComponentTitle', + importPath: 'Path', + type: 'story', + }; + expect(() => { + generator.chooseDuplicate(mockEntry, mockEntry); + }).toThrow( + new DuplicateEntriesError(`Duplicate stories with id: ${mockEntry.id}`, [ + mockEntry, + mockEntry, + ]) + ); + }); }); }); diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.ts index 0d5a8f7b6208..e070440166c6 100644 --- a/code/lib/core-server/src/utils/StoryIndexGenerator.ts +++ b/code/lib/core-server/src/utils/StoryIndexGenerator.ts @@ -37,6 +37,16 @@ type StoriesCacheEntry = { type CacheEntry = false | StoriesCacheEntry | DocsCacheEntry; type SpecifierStoriesCache = Record; +export class DuplicateEntriesError extends Error { + entries: IndexEntry[]; + + constructor(message: string, entries: IndexEntry[]) { + super(); + this.message = message; + this.entries = entries; + } +} + const makeAbsolute = (otherImport: Path, normalizedPath: Path, workingDir: Path) => otherImport.startsWith('.') ? slash( @@ -352,7 +362,11 @@ export class StoryIndexGenerator { const changeDocsName = 'Use `` to distinguish them.'; // This shouldn't be possible, but double check and use for typing - if (worseEntry.type === 'story') throw new Error(`Duplicate stories with id: ${firstEntry.id}`); + if (worseEntry.type === 'story') + throw new DuplicateEntriesError(`Duplicate stories with id: ${firstEntry.id}`, [ + firstEntry, + secondEntry, + ]); if (betterEntry.type === 'story') { const worseDescriptor = worseEntry.standalone