From 97c08a48ce05faa3cdd5a12544fcd1d9c8bfb83a Mon Sep 17 00:00:00 2001 From: Saulo Vallory Date: Tue, 28 May 2024 12:37:45 -0300 Subject: [PATCH] [wip] fix: removing absolute paths --- .../src/codegen-importfn-script.ts | 28 ++----- .../src/utils/StoryIndexGenerator.test.ts | 76 +++++++++---------- .../src/utils/StoryIndexGenerator.ts | 26 +++++-- .../utils/__tests__/index-extraction.test.ts | 41 ++++++++++ .../src/utils/stories-json.test.ts | 18 ++--- .../telemetry/src/storybook-metadata.test.ts | 4 +- code/marko | 1 + 7 files changed, 118 insertions(+), 76 deletions(-) create mode 160000 code/marko diff --git a/code/builders/builder-vite/src/codegen-importfn-script.ts b/code/builders/builder-vite/src/codegen-importfn-script.ts index 3f23ae46808a..941519f49b4e 100644 --- a/code/builders/builder-vite/src/codegen-importfn-script.ts +++ b/code/builders/builder-vite/src/codegen-importfn-script.ts @@ -1,7 +1,6 @@ import * as path from 'path'; import type { Options } from '@storybook/types'; -import { logger } from '@storybook/node-logger'; import { listStories } from './list-stories'; @@ -15,7 +14,7 @@ import { listStories } from './list-stories'; * We want to deal in importPaths relative to the working dir, so we normalize */ function toImportPath(relativePath: string) { - return relativePath.startsWith('../') ? relativePath : `./${relativePath}`; + return /\.\.\/|virtual:/.test(relativePath) ? relativePath : `./${relativePath}`; } /** @@ -25,24 +24,12 @@ function toImportPath(relativePath: string) { * function and this is called by Storybook to fetch a story dynamically when needed. * @param stories An array of absolute story paths. */ -async function toImportFn(stories: string[], indexersMatchers: RegExp[]) { +async function toImportFn(stories: string[]) { const { normalizePath } = await import('vite'); const objectEntries = stories.map((file) => { const relativePath = normalizePath(path.relative(process.cwd(), file)); - if ( - !['.js', '.jsx', '.ts', '.tsx', '.mdx', '.svelte', '.vue'].includes(ext) && - !indexersMatchers.some((m) => m.test(file)) - ) { - logger.warn( - `Cannot process ${ext} file with storyStoreV7: ${relativePath}. No indexer found that can handle this file type.` - ); - } - - return [ - ` '${toImportPath(relativePath)}': async () => import('/@fs/${file}'),`, - ` '${file}': async () => import('/@fs/${file}')`, - ].join('\n'); + return ` '${toImportPath(relativePath)}': async () => import('/@fs/${file}')`; }); return ` @@ -51,12 +38,12 @@ async function toImportFn(stories: string[], indexersMatchers: RegExp[]) { }; export async function importFn(path) { - if (/^\0?virtual:/.test(path)) { + if (/^virtual:/.test(path)) { return import(/* @vite-ignore */ '/' + path); } if (!(path in importers)) { - throw new Error(\`No importer defined for "\${path}". Existing importers: \${Object.keys(importers)}\`); + throw new Error(\`Storybook generated import script does no have an importer for "\${path}". Existing importers: \${Object.keys(importers)}\`); } return importers[path](); @@ -68,9 +55,6 @@ export async function generateImportFnScriptCode(options: Options) { // First we need to get an array of stories and their absolute paths. const stories = await listStories(options); - const indexers = await options.presets.apply('experimental_indexers', []); - const matchers: RegExp[] = indexers?.map((i) => i.test) || []; - // We can then call toImportFn to create a function that can be used to load each story dynamically. - return (await toImportFn(stories, matchers)).trim(); + return (await toImportFn(stories)).trim(); } diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts index 0b08b2728544..c90573816273 100644 --- a/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts +++ b/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts @@ -78,7 +78,7 @@ describe('StoryIndexGenerator', () => { "entries": { "a--story-one": { "id": "a--story-one", - "importPath": "${path.join(workingDir, './src/A.stories.js')}", + "importPath": "'./src/A.stories.js'", "name": "Story One", "tags": [ "dev", @@ -110,7 +110,7 @@ describe('StoryIndexGenerator', () => { "entries": { "f--story-one": { "id": "f--story-one", - "importPath": "${path.join(workingDir, './src/F.story.ts')}", + "importPath": "'./src/F.story.ts'", "name": "Story One", "tags": [ "dev", @@ -141,7 +141,7 @@ describe('StoryIndexGenerator', () => { "entries": { "stories--story-one": { "id": "stories--story-one", - "importPath": "${path.join(workingDir, './src/stories.ts')}", + "importPath": "'./src/stories.ts'", "name": "Story One", "tags": [ "dev", @@ -172,7 +172,7 @@ describe('StoryIndexGenerator', () => { "entries": { "nested-button--story-one": { "id": "nested-button--story-one", - "importPath": "${path.join(workingDir, './src/nested/Button.stories.ts')}", + "importPath": "'./src/nested/Button.stories.ts'", "name": "Story One", "tags": [ "dev", @@ -184,7 +184,7 @@ describe('StoryIndexGenerator', () => { }, "second-nested-g--story-one": { "id": "second-nested-g--story-one", - "importPath": "${path.join(workingDir, './src/second-nested/G.stories.ts')}", + "importPath": "'./src/second-nested/G.stories.ts'", "name": "Story One", "tags": [ "dev", @@ -214,7 +214,7 @@ describe('StoryIndexGenerator', () => { "entries": { "a--story-one": { "id": "a--story-one", - "importPath": "${path.join(workingDir, './src/A.stories.js')}", + "importPath": "'./src/A.stories.js'", "name": "Story One", "tags": [ "dev", @@ -227,7 +227,7 @@ describe('StoryIndexGenerator', () => { }, "b--story-one": { "id": "b--story-one", - "importPath": "${path.join(workingDir, './src/B.stories.ts')}", + "importPath": "'./src/B.stories.ts'", "name": "Story One", "tags": [ "dev", @@ -239,7 +239,7 @@ describe('StoryIndexGenerator', () => { }, "d--story-one": { "id": "d--story-one", - "importPath": "${path.join(workingDir, './src/D.stories.jsx')}", + "importPath": "'./src/D.stories.jsx'", "name": "Story One", "tags": [ "dev", @@ -251,7 +251,7 @@ describe('StoryIndexGenerator', () => { }, "first-nested-deeply-f--story-one": { "id": "first-nested-deeply-f--story-one", - "importPath": "${path.join(workingDir, './src/first-nested/deeply/F.stories.js')}", + "importPath": "'./src/first-nested/deeply/F.stories.js'", "name": "Story One", "tags": [ "dev", @@ -262,7 +262,7 @@ describe('StoryIndexGenerator', () => { }, "h--story-one": { "id": "h--story-one", - "importPath": "${path.join(workingDir, './src/H.stories.mjs')}", + "importPath": "'./src/H.stories.mjs'", "name": "Story One", "tags": [ "dev", @@ -274,7 +274,7 @@ describe('StoryIndexGenerator', () => { }, "nested-button--story-one": { "id": "nested-button--story-one", - "importPath": "${path.join(workingDir, './src/nested/Button.stories.ts')}", + "importPath": "'./src/nested/Button.stories.ts'", "name": "Story One", "tags": [ "dev", @@ -286,7 +286,7 @@ describe('StoryIndexGenerator', () => { }, "second-nested-g--story-one": { "id": "second-nested-g--story-one", - "importPath": "${path.join(workingDir, './src/second-nested/G.stories.ts')}", + "importPath": "'./src/second-nested/G.stories.ts'", "name": "Story One", "tags": [ "dev", @@ -321,7 +321,7 @@ describe('StoryIndexGenerator', () => { "entries": { "a--story-one": { "id": "a--story-one", - "importPath": "${path.join(workingDir, './src/A.stories.js')}", + "importPath": "'./src/A.stories.js'", "name": "Story One", "tags": [ "dev", @@ -347,7 +347,7 @@ describe('StoryIndexGenerator', () => { }, "b--story-one": { "id": "b--story-one", - "importPath": "${path.join(workingDir, './src/B.stories.ts')}", + "importPath": "'./src/B.stories.ts'", "name": "Story One", "tags": [ "dev", @@ -372,7 +372,7 @@ describe('StoryIndexGenerator', () => { }, "d--story-one": { "id": "d--story-one", - "importPath": "${path.join(workingDir, './src/D.stories.jsx')}", + "importPath": "'./src/D.stories.jsx'", "name": "Story One", "tags": [ "dev", @@ -384,7 +384,7 @@ describe('StoryIndexGenerator', () => { }, "first-nested-deeply-f--story-one": { "id": "first-nested-deeply-f--story-one", - "importPath": "${path.join(workingDir, './src/first-nested/deeply/F.stories.js')}", + "importPath": "'./src/first-nested/deeply/F.stories.js'", "name": "Story One", "tags": [ "dev", @@ -408,7 +408,7 @@ describe('StoryIndexGenerator', () => { }, "h--story-one": { "id": "h--story-one", - "importPath": "${path.join(workingDir, './src/H.stories.mjs')}", + "importPath": "'./src/H.stories.mjs'", "name": "Story One", "tags": [ "dev", @@ -420,7 +420,7 @@ describe('StoryIndexGenerator', () => { }, "nested-button--story-one": { "id": "nested-button--story-one", - "importPath": "${path.join(workingDir, './src/nested/Button.stories.ts')}", + "importPath": "'./src/nested/Button.stories.ts'", "name": "Story One", "tags": [ "dev", @@ -432,7 +432,7 @@ describe('StoryIndexGenerator', () => { }, "second-nested-g--story-one": { "id": "second-nested-g--story-one", - "importPath": "${path.join(workingDir, './src/second-nested/G.stories.ts')}", + "importPath": "'./src/second-nested/G.stories.ts'", "name": "Story One", "tags": [ "dev", @@ -584,7 +584,7 @@ describe('StoryIndexGenerator', () => { }, "b--story-one": { "id": "b--story-one", - "importPath": "${path.join(workingDir, './src/B.stories.ts')}", + "importPath": "'./src/B.stories.ts'", "name": "Story One", "tags": [ "dev", @@ -647,7 +647,7 @@ describe('StoryIndexGenerator', () => { }, "b--story-one": { "id": "b--story-one", - "importPath": "${path.join(workingDir, './src/B.stories.ts')}", + "importPath": "'./src/B.stories.ts'", "name": "Story One", "tags": [ "dev", @@ -703,7 +703,7 @@ describe('StoryIndexGenerator', () => { }, "a--story-one": { "id": "a--story-one", - "importPath": "${path.join(workingDir, './src/A.stories.js')}", + "importPath": "'./src/A.stories.js'", "name": "Story One", "tags": [ "dev", @@ -750,7 +750,7 @@ describe('StoryIndexGenerator', () => { }, "duplicate-a--story-one": { "id": "duplicate-a--story-one", - "importPath": "${path.join(workingDir, './duplicate/A.stories.js')}", + "importPath": "'./duplicate/A.stories.js'", "name": "Story One", "tags": [ "dev", @@ -762,7 +762,7 @@ describe('StoryIndexGenerator', () => { }, "duplicate-a--story-two": { "id": "duplicate-a--story-two", - "importPath": "${path.join(workingDir, './duplicate/SecondA.stories.js')}", + "importPath": "'./duplicate/SecondA.stories.js'", "name": "Story Two", "tags": [ "dev", @@ -823,7 +823,7 @@ describe('StoryIndexGenerator', () => { }, "my-component-a--story-one": { "id": "my-component-a--story-one", - "importPath": "${path.join(workingDir, './docs-id-generation/A.stories.jsx')}", + "importPath": "'./docs-id-generation/A.stories.jsx'", "name": "Story One", "tags": [ "dev", @@ -853,7 +853,7 @@ describe('StoryIndexGenerator', () => { "importPath": "./src/docs2/MetaOf.mdx", "name": "MetaOf", "storiesImports": [ - "${path.join(workingDir, './src/A.stories.js')}", + "'./src/A.stories.js'", ], "tags": [ "dev", @@ -870,7 +870,7 @@ describe('StoryIndexGenerator', () => { "importPath": "./src/docs2/SecondMetaOf.mdx", "name": "Second Docs", "storiesImports": [ - "${path.join(workingDir, './src/A.stories.js')}", + "'./src/A.stories.js'", ], "tags": [ "dev", @@ -884,7 +884,7 @@ describe('StoryIndexGenerator', () => { }, "a--story-one": { "id": "a--story-one", - "importPath": "${path.join(workingDir, './src/A.stories.js')}", + "importPath": "'./src/A.stories.js'", "name": "Story One", "tags": [ "dev", @@ -986,7 +986,7 @@ describe('StoryIndexGenerator', () => { "importPath": "./src/docs2/MetaOf.mdx", "name": "MetaOf", "storiesImports": [ - "${path.join(workingDir, './src/A.stories.js')}", + "'./src/A.stories.js'", ], "tags": [ "dev", @@ -1003,7 +1003,7 @@ describe('StoryIndexGenerator', () => { "importPath": "./src/docs2/SecondMetaOf.mdx", "name": "Second Docs", "storiesImports": [ - "${path.join(workingDir, './src/A.stories.js')}", + "'./src/A.stories.js'", ], "tags": [ "dev", @@ -1017,7 +1017,7 @@ describe('StoryIndexGenerator', () => { }, "a--story-one": { "id": "a--story-one", - "importPath": "${path.join(workingDir, './src/A.stories.js')}", + "importPath": "'./src/A.stories.js'", "name": "Story One", "tags": [ "dev", @@ -1088,7 +1088,7 @@ describe('StoryIndexGenerator', () => { "entries": { "a--story-one": { "id": "a--story-one", - "importPath": "${path.join(workingDir, './src/A.stories.js')}", + "importPath": "'./src/A.stories.js'", "name": "Story One", "tags": [ "dev", @@ -1101,7 +1101,7 @@ describe('StoryIndexGenerator', () => { }, "b--story-one": { "id": "b--story-one", - "importPath": "${path.join(workingDir, './src/B.stories.ts')}", + "importPath": "'./src/B.stories.ts'", "name": "Story One", "tags": [ "dev", @@ -1116,8 +1116,8 @@ describe('StoryIndexGenerator', () => { "importPath": "./complex/TwoStoryReferences.mdx", "name": "TwoStoryReferences", "storiesImports": [ - "${path.join(workingDir, './src/B.stories.ts')}", - "${path.join(workingDir, './src/A.stories.js')}", + "'./src/B.stories.ts'", + "'./src/A.stories.js'", ], "tags": [ "dev", @@ -1156,7 +1156,7 @@ describe('StoryIndexGenerator', () => { "importPath": "./docs-id-generation/B.docs.mdx", "name": "docs", "storiesImports": [ - "${path.join(workingDir, './docs-id-generation/B.stories.jsx')}", + "'./docs-id-generation/B.stories.jsx'", ], "tags": [ "dev", @@ -1168,7 +1168,7 @@ describe('StoryIndexGenerator', () => { }, "my-component-b--story-one": { "id": "my-component-b--story-one", - "importPath": "${path.join(workingDir, './docs-id-generation/B.stories.jsx')}", + "importPath": "'./docs-id-generation/B.stories.jsx'", "name": "Story One", "tags": [ "dev", @@ -1266,7 +1266,7 @@ describe('StoryIndexGenerator', () => { await generator.initialize(); await expect(generator.getIndex()).rejects.toThrowErrorMatchingInlineSnapshot( - `[Error: Unable to index ${path.join(workingDir, './src/A.stories.js')},./errors/A.mdx]` + `[Error: Unable to index './src/A.stories.js',./errors/A.mdx]` ); }); it('errors when two duplicate stories exists, with duplicated entries details', async () => { diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.ts index f02f6bc5bebe..7ad2550c49fa 100644 --- a/code/lib/core-server/src/utils/StoryIndexGenerator.ts +++ b/code/lib/core-server/src/utils/StoryIndexGenerator.ts @@ -245,6 +245,11 @@ export class StoryIndexGenerator { ); return Object.values(cache).flatMap((entry): (IndexEntry | ErrorEntry)[] => { if (!entry) return []; + + if (entry.type === 'docs' || entry.type === 'stories') { + entry.importPath = specifier.importPath; + } + if (entry.type === 'docs') return [entry]; if (entry.type === 'error') return [entry]; @@ -283,8 +288,20 @@ export class StoryIndexGenerator { absolutePath: Path, projectTags: Tag[] = [] ): Promise { - const relativePath = path.relative(this.options.workingDir, absolutePath); - const importPath = slash(normalizeStoryPath(relativePath)); + const formatPath = (filePath: string) => { + if (filePath.startsWith('virtual:')) { + return filePath; + } + + if (path.isAbsolute(filePath)) { + filePath = path.resolve(this.options.workingDir, filePath); + } + + return slash(normalizeStoryPath(filePath)); + }; + + const importPath = formatPath(absolutePath); + const defaultMakeTitle = (userTitle?: string) => { const title = userOrAutoTitleFromSpecifier(importPath, specifier, userTitle); invariant( @@ -314,8 +331,7 @@ export class StoryIndexGenerator { metaId: input.metaId, name, title, - // We must use `||` instead of `??` since "" (empty string) is not a valid import path - importPath: input.importPath || importPath, + importPath: input.importPath ? formatPath(input.importPath) : importPath, tags, }; }); @@ -410,7 +426,7 @@ export class StoryIndexGenerator { invariant( csfEntry, dedent`Could not find or load CSF file at path "${result.of}" referenced by \`of={}\` in docs file "${relativePath}". - + - Does that file exist? - If so, is it a CSF file (\`.stories.*\`)? - If so, is it matched by the \`stories\` glob in \`main.js\`? diff --git a/code/lib/core-server/src/utils/__tests__/index-extraction.test.ts b/code/lib/core-server/src/utils/__tests__/index-extraction.test.ts index c1c3bdaf8528..20c2cc5f68aa 100644 --- a/code/lib/core-server/src/utils/__tests__/index-extraction.test.ts +++ b/code/lib/core-server/src/utils/__tests__/index-extraction.test.ts @@ -129,6 +129,47 @@ describe('story extraction', () => { `); }); + it('leaves virtual paths returned by indexers as is', async () => { + const relativePath = './src/first-nested/deeply/F.stories.js'; + const absolutePath = path.join(options.workingDir, relativePath); + const specifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(relativePath, options); + + const generator = new StoryIndexGenerator([specifier], { + ...options, + indexers: [ + { + test: /\.stories\.(m?js|ts)x?$/, + createIndex: async (fileName) => [ + { + exportName: 'StoryOne', + type: 'story', + // importPath: "virtual:custom-indexer", + }, + ], + }, + ], + }); + const result = await generator.extractStories(specifier, absolutePath); + + expect(result).toMatchInlineSnapshot(` + { + "dependents": [], + "entries": [ + { + "id": "f--story-one", + "importPath": "virtual:custom-indexer", + "metaId": undefined, + "name": "Story One", + "tags": [], + "title": "F", + "type": "story", + }, + ], + "type": "stories", + } + `); + }); + it('auto-generates title from indexer inputs without title', async () => { const relativePath = './src/first-nested/deeply/F.stories.js'; const absolutePath = path.join(options.workingDir, relativePath); diff --git a/code/lib/core-server/src/utils/stories-json.test.ts b/code/lib/core-server/src/utils/stories-json.test.ts index f155e381072c..02eb8449642a 100644 --- a/code/lib/core-server/src/utils/stories-json.test.ts +++ b/code/lib/core-server/src/utils/stories-json.test.ts @@ -111,7 +111,7 @@ describe('useStoriesJson', () => { "importPath": "./src/docs2/MetaOf.mdx", "name": "MetaOf", "storiesImports": [ - "${path.join(workingDir, './src/A.stories.js')}", + "'./src/A.stories.js'", ], "tags": [ "dev", @@ -128,7 +128,7 @@ describe('useStoriesJson', () => { "importPath": "./src/docs2/SecondMetaOf.mdx", "name": "Second Docs", "storiesImports": [ - "${path.join(workingDir, './src/A.stories.js')}", + "'./src/A.stories.js'", ], "tags": [ "dev", @@ -142,7 +142,7 @@ describe('useStoriesJson', () => { }, "a--story-one": { "id": "a--story-one", - "importPath": "${path.join(workingDir, './src/A.stories.js')}", + "importPath": "'./src/A.stories.js'", "name": "Story One", "tags": [ "dev", @@ -155,7 +155,7 @@ describe('useStoriesJson', () => { }, "b--story-one": { "id": "b--story-one", - "importPath": "${path.join(workingDir, './src/B.stories.ts')}", + "importPath": "'./src/B.stories.ts'", "name": "Story One", "tags": [ "dev", @@ -167,7 +167,7 @@ describe('useStoriesJson', () => { }, "d--story-one": { "id": "d--story-one", - "importPath": "${path.join(workingDir, './src/D.stories.jsx')}", + "importPath": "'./src/D.stories.jsx'", "name": "Story One", "tags": [ "dev", @@ -218,7 +218,7 @@ describe('useStoriesJson', () => { }, "first-nested-deeply-f--story-one": { "id": "first-nested-deeply-f--story-one", - "importPath": "${path.join(workingDir, './src/first-nested/deeply/F.stories.js')}", + "importPath": "'./src/first-nested/deeply/F.stories.js'", "name": "Story One", "tags": [ "dev", @@ -229,7 +229,7 @@ describe('useStoriesJson', () => { }, "h--story-one": { "id": "h--story-one", - "importPath": "${path.join(workingDir, './src/H.stories.mjs')}", + "importPath": "'./src/H.stories.mjs'", "name": "Story One", "tags": [ "dev", @@ -241,7 +241,7 @@ describe('useStoriesJson', () => { }, "nested-button--story-one": { "id": "nested-button--story-one", - "importPath": "${path.join(workingDir, './src/nested/Button.stories.ts')}", + "importPath": "'./src/nested/Button.stories.ts'", "name": "Story One", "tags": [ "dev", @@ -253,7 +253,7 @@ describe('useStoriesJson', () => { }, "second-nested-g--story-one": { "id": "second-nested-g--story-one", - "importPath": "${path.join(workingDir, './src/second-nested/G.stories.ts')}", + "importPath": "'./src/second-nested/G.stories.ts'", "name": "Story One", "tags": [ "dev", diff --git a/code/lib/telemetry/src/storybook-metadata.test.ts b/code/lib/telemetry/src/storybook-metadata.test.ts index 33b398b03d50..da9a37e368d0 100644 --- a/code/lib/telemetry/src/storybook-metadata.test.ts +++ b/code/lib/telemetry/src/storybook-metadata.test.ts @@ -348,8 +348,8 @@ describe('storybook-metadata', () => { mainConfig: { ...mainJsMock, refs: { - a: { id: '', version: '', title: '', url: '' }, - b: { id: '', version: '', title: '', url: '' }, + a: { title: '', url: '' }, + b: { title: '', url: '' }, }, }, }); diff --git a/code/marko b/code/marko new file mode 160000 index 000000000000..9ac8191cede8 --- /dev/null +++ b/code/marko @@ -0,0 +1 @@ +Subproject commit 9ac8191cede89260787defaabb9dadc286697220