From 3f6ac9c63d95e745f7d24080800ebfd293f99146 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Mon, 6 Jun 2022 17:03:40 +1000 Subject: [PATCH 1/3] Make `stories.json` backwards compatibly to all v6 versions --- .../src/utils/stories-json.test.ts | 71 ++++++++++++++++++- lib/core-server/src/utils/stories-json.ts | 6 +- lib/store/src/StoryStore.test.ts | 18 +++++ lib/store/src/StoryStore.ts | 37 +++++----- lib/store/src/types.ts | 6 +- 5 files changed, 115 insertions(+), 23 deletions(-) diff --git a/lib/core-server/src/utils/stories-json.test.ts b/lib/core-server/src/utils/stories-json.test.ts index a1ceee0eeacf..1d38bf9006b9 100644 --- a/lib/core-server/src/utils/stories-json.test.ts +++ b/lib/core-server/src/utils/stories-json.test.ts @@ -3,8 +3,9 @@ import Watchpack from 'watchpack'; import path from 'path'; import debounce from 'lodash/debounce'; import Events from '@storybook/core-events'; +import type { StoryIndex } from '@storybook/store'; -import { useStoriesJson, DEBOUNCE } from './stories-json'; +import { useStoriesJson, DEBOUNCE, convertToIndexV3 } from './stories-json'; import { ServerChannel } from './get-server-channel'; import { StoryIndexGenerator } from './StoryIndexGenerator'; @@ -195,37 +196,49 @@ describe('useStoriesJson', () => { "a--story-one": Object { "id": "a--story-one", "importPath": "./src/A.stories.js", + "kind": "A", "name": "Story One", + "story": "Story One", "title": "A", }, "b--story-one": Object { "id": "b--story-one", "importPath": "./src/B.stories.ts", + "kind": "B", "name": "Story One", + "story": "Story One", "title": "B", }, "d--story-one": Object { "id": "d--story-one", "importPath": "./src/D.stories.jsx", + "kind": "D", "name": "Story One", + "story": "Story One", "title": "D", }, "first-nested-deeply-f--story-one": Object { "id": "first-nested-deeply-f--story-one", "importPath": "./src/first-nested/deeply/F.stories.js", + "kind": "first-nested/deeply/F", "name": "Story One", + "story": "Story One", "title": "first-nested/deeply/F", }, "nested-button--story-one": Object { "id": "nested-button--story-one", "importPath": "./src/nested/Button.stories.ts", + "kind": "nested/Button", "name": "Story One", + "story": "Story One", "title": "nested/Button", }, "second-nested-g--story-one": Object { "id": "second-nested-g--story-one", "importPath": "./src/second-nested/G.stories.ts", + "kind": "second-nested/G", "name": "Story One", + "story": "Story One", "title": "second-nested/G", }, }, @@ -476,3 +489,59 @@ describe('useStoriesJson', () => { }); }); }); + +describe('convertToIndexV3', () => { + it('converts v7 index.json to v6 stories.json', () => { + const indexJson: StoryIndex = { + v: 4, + entries: { + 'a--docs': { + id: 'a--docs', + importPath: './src/docs2/MetaOf.docs.mdx', + name: 'docs', + storiesImports: ['./src/A.stories.js'], + title: 'A', + type: 'docs', + }, + 'a--story-one': { + id: 'a--story-one', + importPath: './src/A.stories.js', + name: 'Story One', + title: 'A', + type: 'story', + }, + 'b--story-one': { + id: 'b--story-one', + importPath: './src/B.stories.ts', + name: 'Story One', + title: 'B', + type: 'story', + }, + }, + }; + + expect(convertToIndexV3(indexJson)).toMatchInlineSnapshot(` + Object { + "stories": Object { + "a--story-one": Object { + "id": "a--story-one", + "importPath": "./src/A.stories.js", + "kind": "A", + "name": "Story One", + "story": "Story One", + "title": "A", + }, + "b--story-one": Object { + "id": "b--story-one", + "importPath": "./src/B.stories.ts", + "kind": "B", + "name": "Story One", + "story": "Story One", + "title": "B", + }, + }, + "v": 3, + } + `); + }); +}); diff --git a/lib/core-server/src/utils/stories-json.ts b/lib/core-server/src/utils/stories-json.ts index 1a3ad5230bc2..78b1e39c971f 100644 --- a/lib/core-server/src/utils/stories-json.ts +++ b/lib/core-server/src/utils/stories-json.ts @@ -74,7 +74,11 @@ export const convertToIndexV3 = (index: StoryIndex): StoryIndexV3 => { const stories = Object.entries(entries).reduce((acc, [id, entry]) => { if (entry.type === 'story') { const { type, ...rest } = entry; - acc[id] = rest; + acc[id] = { + ...rest, + kind: rest.title, + story: rest.name, + }; } return acc; }, {} as StoryIndexV3['stories']); diff --git a/lib/store/src/StoryStore.test.ts b/lib/store/src/StoryStore.test.ts index 6c08fbdda6c3..fac613151f82 100644 --- a/lib/store/src/StoryStore.test.ts +++ b/lib/store/src/StoryStore.test.ts @@ -1000,19 +1000,37 @@ describe('StoryStore', () => { "component-one--a": Object { "id": "component-one--a", "importPath": "./src/ComponentOne.stories.js", + "kind": "Component One", "name": "A", + "parameters": Object { + "__isArgsStory": false, + "fileName": "./src/ComponentOne.stories.js", + }, + "story": "A", "title": "Component One", }, "component-one--b": Object { "id": "component-one--b", "importPath": "./src/ComponentOne.stories.js", + "kind": "Component One", "name": "B", + "parameters": Object { + "__isArgsStory": false, + "fileName": "./src/ComponentOne.stories.js", + }, + "story": "B", "title": "Component One", }, "component-two--c": Object { "id": "component-two--c", "importPath": "./src/ComponentTwo.stories.js", + "kind": "Component Two", "name": "C", + "parameters": Object { + "__isArgsStory": false, + "fileName": "./src/ComponentTwo.stories.js", + }, + "story": "C", "title": "Component Two", }, }, diff --git a/lib/store/src/StoryStore.ts b/lib/store/src/StoryStore.ts index 1915c7139bcc..dc8b2210645f 100644 --- a/lib/store/src/StoryStore.ts +++ b/lib/store/src/StoryStore.ts @@ -303,28 +303,29 @@ export class StoryStore { }; } + // NOTE: this is legacy `stories.json` data for the `extract` script. + // It is used to allow v7 Storybooks to be composed in v6 Storybooks, which expect a + // `stories.json` file with legacy fields (`kind` etc). getStoriesJsonData = (): StoryIndexV3 => { const value = this.getSetStoriesPayload(); const allowedParameters = ['fileName', 'docsOnly', 'framework', '__id', '__isArgsStory']; - const stories: Record | V2CompatIndexEntry> = mapValues( - value.stories, - (story) => { - const { importPath } = this.storyIndex.entries[story.id]; - return { - ...pick(story, ['id', 'name', 'title']), - importPath, - ...(!global.FEATURES?.breakingChangesV7 && { - kind: story.title, - story: story.name, - parameters: { - ...pick(story.parameters, allowedParameters), - fileName: importPath, - }, - }), - }; - } - ); + const stories: Record = mapValues(value.stories, (story) => { + const { importPath } = this.storyIndex.entries[story.id]; + return { + ...pick(story, ['id', 'name', 'title']), + importPath, + // These 3 fields were going to be dropped in v7, but instead we will keep them for the + // 7.x cycle so that v7 Storybooks can be composed successfully in v6 Storybook. + // In v8 we will (likely) completely drop support for `extract` and `getStoriesJsonData` + kind: story.title, + story: story.name, + parameters: { + ...pick(story.parameters, allowedParameters), + fileName: importPath, + }, + }; + }); return { v: 3, diff --git a/lib/store/src/types.ts b/lib/store/src/types.ts index e8afb7db1757..a193cf8e58f9 100644 --- a/lib/store/src/types.ts +++ b/lib/store/src/types.ts @@ -108,15 +108,15 @@ export type DocsIndexEntry = BaseIndexEntry & { }; export type IndexEntry = StoryIndexEntry | DocsIndexEntry; -export interface V2CompatIndexEntry extends StoryIndexEntry { +export interface V2CompatIndexEntry extends Omit { kind: StoryIndexEntry['title']; story: StoryIndexEntry['name']; - parameters: Parameters; + parameters?: Parameters; } export interface StoryIndexV3 { v: number; - stories: Record>; + stories: Record; } export interface StoryIndex { From f10be1e456017faa84c94e32643ebe43338eb0f5 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Mon, 6 Jun 2022 17:08:48 +1000 Subject: [PATCH 2/3] Remove superfluous test --- lib/store/src/StoryStore.test.ts | 53 -------------------------------- 1 file changed, 53 deletions(-) diff --git a/lib/store/src/StoryStore.test.ts b/lib/store/src/StoryStore.test.ts index fac613151f82..1fb4b17a2334 100644 --- a/lib/store/src/StoryStore.test.ts +++ b/lib/store/src/StoryStore.test.ts @@ -986,59 +986,6 @@ describe('StoryStore', () => { `); }); }); - - describe('in non-back-compat mode', () => { - it('maps stories list to payload correctly', async () => { - const store = new StoryStore(); - store.setProjectAnnotations(projectAnnotations); - store.initialize({ storyIndex, importFn, cache: false }); - await store.cacheAllCSFFiles(); - - expect(store.getStoriesJsonData()).toMatchInlineSnapshot(` - Object { - "stories": Object { - "component-one--a": Object { - "id": "component-one--a", - "importPath": "./src/ComponentOne.stories.js", - "kind": "Component One", - "name": "A", - "parameters": Object { - "__isArgsStory": false, - "fileName": "./src/ComponentOne.stories.js", - }, - "story": "A", - "title": "Component One", - }, - "component-one--b": Object { - "id": "component-one--b", - "importPath": "./src/ComponentOne.stories.js", - "kind": "Component One", - "name": "B", - "parameters": Object { - "__isArgsStory": false, - "fileName": "./src/ComponentOne.stories.js", - }, - "story": "B", - "title": "Component One", - }, - "component-two--c": Object { - "id": "component-two--c", - "importPath": "./src/ComponentTwo.stories.js", - "kind": "Component Two", - "name": "C", - "parameters": Object { - "__isArgsStory": false, - "fileName": "./src/ComponentTwo.stories.js", - }, - "story": "C", - "title": "Component Two", - }, - }, - "v": 3, - } - `); - }); - }); }); describe('cacheAllCsfFiles', () => { From 6513bb6aa95db7e6d9d5e39ca45660a12e89aba8 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Tue, 7 Jun 2022 13:22:56 +1000 Subject: [PATCH 3/3] Include all types of entries in v3 story index --- .../src/utils/stories-json.test.ts | 100 ++++++++++++++++++ lib/core-server/src/utils/stories-json.ts | 19 ++-- lib/store/src/types.ts | 2 +- 3 files changed, 112 insertions(+), 9 deletions(-) diff --git a/lib/core-server/src/utils/stories-json.test.ts b/lib/core-server/src/utils/stories-json.test.ts index 1d38bf9006b9..4dac46308c26 100644 --- a/lib/core-server/src/utils/stories-json.test.ts +++ b/lib/core-server/src/utils/stories-json.test.ts @@ -193,11 +193,32 @@ describe('useStoriesJson', () => { expect(JSON.parse(send.mock.calls[0][0])).toMatchInlineSnapshot(` Object { "stories": Object { + "a--docs": Object { + "id": "a--docs", + "importPath": "./src/docs2/MetaOf.docs.mdx", + "kind": "A", + "name": "docs", + "parameters": Object { + "__id": "a--docs", + "docsOnly": true, + "fileName": "./src/docs2/MetaOf.docs.mdx", + }, + "storiesImports": Array [ + "./src/A.stories.js", + ], + "story": "docs", + "title": "A", + }, "a--story-one": Object { "id": "a--story-one", "importPath": "./src/A.stories.js", "kind": "A", "name": "Story One", + "parameters": Object { + "__id": "a--story-one", + "docsOnly": false, + "fileName": "./src/A.stories.js", + }, "story": "Story One", "title": "A", }, @@ -206,6 +227,11 @@ describe('useStoriesJson', () => { "importPath": "./src/B.stories.ts", "kind": "B", "name": "Story One", + "parameters": Object { + "__id": "b--story-one", + "docsOnly": false, + "fileName": "./src/B.stories.ts", + }, "story": "Story One", "title": "B", }, @@ -214,14 +240,52 @@ describe('useStoriesJson', () => { "importPath": "./src/D.stories.jsx", "kind": "D", "name": "Story One", + "parameters": Object { + "__id": "d--story-one", + "docsOnly": false, + "fileName": "./src/D.stories.jsx", + }, "story": "Story One", "title": "D", }, + "docs2-notitle--docs": Object { + "id": "docs2-notitle--docs", + "importPath": "./src/docs2/NoTitle.docs.mdx", + "kind": "docs2/NoTitle", + "name": "docs", + "parameters": Object { + "__id": "docs2-notitle--docs", + "docsOnly": true, + "fileName": "./src/docs2/NoTitle.docs.mdx", + }, + "storiesImports": Array [], + "story": "docs", + "title": "docs2/NoTitle", + }, + "docs2-yabbadabbadooo--docs": Object { + "id": "docs2-yabbadabbadooo--docs", + "importPath": "./src/docs2/Title.docs.mdx", + "kind": "docs2/Yabbadabbadooo", + "name": "docs", + "parameters": Object { + "__id": "docs2-yabbadabbadooo--docs", + "docsOnly": true, + "fileName": "./src/docs2/Title.docs.mdx", + }, + "storiesImports": Array [], + "story": "docs", + "title": "docs2/Yabbadabbadooo", + }, "first-nested-deeply-f--story-one": Object { "id": "first-nested-deeply-f--story-one", "importPath": "./src/first-nested/deeply/F.stories.js", "kind": "first-nested/deeply/F", "name": "Story One", + "parameters": Object { + "__id": "first-nested-deeply-f--story-one", + "docsOnly": false, + "fileName": "./src/first-nested/deeply/F.stories.js", + }, "story": "Story One", "title": "first-nested/deeply/F", }, @@ -230,6 +294,11 @@ describe('useStoriesJson', () => { "importPath": "./src/nested/Button.stories.ts", "kind": "nested/Button", "name": "Story One", + "parameters": Object { + "__id": "nested-button--story-one", + "docsOnly": false, + "fileName": "./src/nested/Button.stories.ts", + }, "story": "Story One", "title": "nested/Button", }, @@ -238,6 +307,11 @@ describe('useStoriesJson', () => { "importPath": "./src/second-nested/G.stories.ts", "kind": "second-nested/G", "name": "Story One", + "parameters": Object { + "__id": "second-nested-g--story-one", + "docsOnly": false, + "fileName": "./src/second-nested/G.stories.ts", + }, "story": "Story One", "title": "second-nested/G", }, @@ -523,11 +597,32 @@ describe('convertToIndexV3', () => { expect(convertToIndexV3(indexJson)).toMatchInlineSnapshot(` Object { "stories": Object { + "a--docs": Object { + "id": "a--docs", + "importPath": "./src/docs2/MetaOf.docs.mdx", + "kind": "A", + "name": "docs", + "parameters": Object { + "__id": "a--docs", + "docsOnly": true, + "fileName": "./src/docs2/MetaOf.docs.mdx", + }, + "storiesImports": Array [ + "./src/A.stories.js", + ], + "story": "docs", + "title": "A", + }, "a--story-one": Object { "id": "a--story-one", "importPath": "./src/A.stories.js", "kind": "A", "name": "Story One", + "parameters": Object { + "__id": "a--story-one", + "docsOnly": false, + "fileName": "./src/A.stories.js", + }, "story": "Story One", "title": "A", }, @@ -536,6 +631,11 @@ describe('convertToIndexV3', () => { "importPath": "./src/B.stories.ts", "kind": "B", "name": "Story One", + "parameters": Object { + "__id": "b--story-one", + "docsOnly": false, + "fileName": "./src/B.stories.ts", + }, "story": "Story One", "title": "B", }, diff --git a/lib/core-server/src/utils/stories-json.ts b/lib/core-server/src/utils/stories-json.ts index 78b1e39c971f..13bf8907edef 100644 --- a/lib/core-server/src/utils/stories-json.ts +++ b/lib/core-server/src/utils/stories-json.ts @@ -72,14 +72,17 @@ export function useStoriesJson({ export const convertToIndexV3 = (index: StoryIndex): StoryIndexV3 => { const { entries } = index; const stories = Object.entries(entries).reduce((acc, [id, entry]) => { - if (entry.type === 'story') { - const { type, ...rest } = entry; - acc[id] = { - ...rest, - kind: rest.title, - story: rest.name, - }; - } + const { type, ...rest } = entry; + acc[id] = { + ...rest, + kind: rest.title, + story: rest.name, + parameters: { + __id: rest.id, + docsOnly: type === 'docs', + fileName: rest.importPath, + }, + }; return acc; }, {} as StoryIndexV3['stories']); return { diff --git a/lib/store/src/types.ts b/lib/store/src/types.ts index a193cf8e58f9..22cdbbbe5693 100644 --- a/lib/store/src/types.ts +++ b/lib/store/src/types.ts @@ -111,7 +111,7 @@ export type IndexEntry = StoryIndexEntry | DocsIndexEntry; export interface V2CompatIndexEntry extends Omit { kind: StoryIndexEntry['title']; story: StoryIndexEntry['name']; - parameters?: Parameters; + parameters: Parameters; } export interface StoryIndexV3 {