diff --git a/.circleci/config.yml b/.circleci/config.yml index 72adc93ef564..ec3b5b730914 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -601,6 +601,8 @@ jobs: name: Install dependencies command: yarn install --no-immutable working_directory: test-storybooks/portable-stories-kitchen-sink/<< parameters.directory >> + environment: + YARN_ENABLE_IMMUTABLE_INSTALLS: false - run: name: Run Jest tests command: yarn jest diff --git a/.github/workflows/canary-release-pr.yml b/.github/workflows/canary-release-pr.yml index 557a0331fe2d..1b97a35368da 100644 --- a/.github/workflows/canary-release-pr.yml +++ b/.github/workflows/canary-release-pr.yml @@ -59,6 +59,11 @@ jobs: uses: actions/setup-node@v4 with: node-version-file: ".nvmrc" + + - uses: oven-sh/setup-bun@v1 + with: + bun-version: 1.1.1 + - name: Cache dependencies uses: actions/cache@v4 with: diff --git a/.github/workflows/trigger-circle-ci-workflow.yml b/.github/workflows/trigger-circle-ci-workflow.yml index c8c8a4c0af9c..66bcc0f1a7ce 100644 --- a/.github/workflows/trigger-circle-ci-workflow.yml +++ b/.github/workflows/trigger-circle-ci-workflow.yml @@ -4,7 +4,7 @@ on: # Use pull_request_target, as we don't need to check out the actual code of the fork in this script. # And this is the only way to trigger the Circle CI API on forks as well. pull_request_target: - types: [opened, synchronize, labeled, unlabeled, reopened, converted_to_draft, ready_for_review] + types: [opened, synchronize, labeled, reopened] push: branches: - next @@ -39,7 +39,7 @@ jobs: trigger-normal-tests: runs-on: ubuntu-latest needs: get-branch - if: github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'ci:normal') + if: github.event_name == 'pull_request_target' && ((github.event.action == 'labeled' && github.event.label.name == 'ci:normal') || (github.event.action != 'labeled' && contains(github.event.pull_request.labels.*.name, 'ci:normal'))) steps: - name: Trigger Normal tests run: > @@ -58,7 +58,7 @@ jobs: trigger-docs-tests: runs-on: ubuntu-latest needs: get-branch - if: github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'ci:docs') + if: github.event_name == 'pull_request_target' && ((github.event.action == 'labeled' && github.event.label.name == 'ci:docs') || (github.event.action != 'labeled' && contains(github.event.pull_request.labels.*.name, 'ci:docs'))) steps: - name: Trigger docs tests run: > @@ -77,7 +77,7 @@ jobs: trigger-merged-tests: runs-on: ubuntu-latest needs: get-branch - if: github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'ci:merged') + if: github.event_name == 'push' || (github.event.action == 'labeled' && github.event.label.name == 'ci:merged') || (github.event.action != 'labeled' && contains(github.event.pull_request.labels.*.name, 'ci:merged')) steps: - name: Trigger merged tests run: > @@ -96,7 +96,7 @@ jobs: trigger-daily-tests: runs-on: ubuntu-latest needs: get-branch - if: github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'ci:daily') + if: github.event_name == 'pull_request_target' && ((github.event.action == 'labeled' && github.event.label.name == 'ci:daily') || (github.event.action != 'labeled' && contains(github.event.pull_request.labels.*.name, 'ci:daily'))) steps: - name: Trigger the daily tests run: > diff --git a/CHANGELOG.prerelease.md b/CHANGELOG.prerelease.md index f15a15ea9b0e..50ff75451556 100644 --- a/CHANGELOG.prerelease.md +++ b/CHANGELOG.prerelease.md @@ -1,3 +1,13 @@ +## 8.2.0-alpha.7 + +- Angular: Allow outputPath object syntax - [#28144](https://github.com/storybookjs/storybook/pull/28144), thanks @valentinpalkovic! +- Angular: Introduce preserveSymlink builder option - [#28145](https://github.com/storybookjs/storybook/pull/28145), thanks @valentinpalkovic! +- CLI: Fix typo in React Docgen migration - [#27536](https://github.com/storybookjs/storybook/pull/27536), thanks @jonniebigodes! +- CSF: Automatically extract componentPath - [#24396](https://github.com/storybookjs/storybook/pull/24396), thanks @shilman! +- Core: Remove more `.stories.mdx` handling - [#25973](https://github.com/storybookjs/storybook/pull/25973), thanks @JReinhold! +- Docs-tools: Replace `doctrine` with `jsdoc-type-pratt-parser` - [#26305](https://github.com/storybookjs/storybook/pull/26305), thanks @43081j! +- Test: Display toHaveBeenCalledWith expected / received values on failure - [#28088](https://github.com/storybookjs/storybook/pull/28088), thanks @kasperpeulen! + ## 8.2.0-alpha.6 - Addon-actions: Only log spies with names - [#28091](https://github.com/storybookjs/storybook/pull/28091), thanks @kasperpeulen! diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f8ae3a42c13c..cab252f7d26e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -61,6 +61,19 @@ yarn build --watch react core-server api addon-docs yarn task --task dev --template --start-from=publish ``` +### Making code changes when working on Angular-specific code + +If you are working on Angular-specific code, you will need to append `--prod` to the above mentioned commands to ensure that the Angular compiler is able to pick up the changes appropriately and doesn't fail. This will build all the packages in production mode. + +```sh +yarn task --prod +``` + +```bash +cd code +yarn build --prod --watch angular core-server api addon-docs +``` + ## Contributing to Storybook For further advice on how to contribute, please refer to our [NEW contributing guide on the Storybook website](https://storybook.js.org/docs/contribute). diff --git a/code/addons/docs/docs/recipes.md b/code/addons/docs/docs/recipes.md index 20faa8fba1a7..e98c89145b78 100644 --- a/code/addons/docs/docs/recipes.md +++ b/code/addons/docs/docs/recipes.md @@ -269,8 +269,8 @@ export const parameters = { transform: (src, storyContext) => { const match = SOURCE_REGEX.exec(src); return match ? match[1] : src; - } - } + }, + }, }, }; ``` diff --git a/code/addons/interactions/src/components/Interaction.tsx b/code/addons/interactions/src/components/Interaction.tsx index c5e5e41f3d5e..b50e1a8382a7 100644 --- a/code/addons/interactions/src/components/Interaction.tsx +++ b/code/addons/interactions/src/components/Interaction.tsx @@ -5,12 +5,12 @@ import { styled, typography } from '@storybook/theming'; import { transparentize } from 'polished'; import { ListUnorderedIcon } from '@storybook/icons'; -import { Expected, MatcherResult, Received } from './MatcherResult'; +import { MatcherResult } from './MatcherResult'; import { MethodCall } from './MethodCall'; import { StatusIcon } from './StatusIcon'; import type { Controls } from './InteractionsPanel'; -import { isJestError } from '../utils'; +import { isChaiError, isJestError } from '../utils'; const MethodCallWrapper = styled.div(() => ({ fontFamily: typography.fonts.mono, @@ -117,33 +117,23 @@ export const Exception = ({ exception }: { exception: Call['exception'] }) => { if (isJestError(exception)) { return ; } + if (isChaiError(exception)) { + return ( + + +

See the full stack trace in the browser console.

+
+ ); + } + const paragraphs = exception.message.split('\n\n'); const more = paragraphs.length > 1; return (
{paragraphs[0]}
- {exception.showDiff && exception.diff ? ( - <> -
- - - ) : ( -
-          
- {exception.expected && ( - <> - Expected: -
- - )} - {exception.actual && ( - <> - Received: -
- - )} -
- )} {more &&

See the full stack trace in the browser console.

}
); diff --git a/code/addons/interactions/src/components/InteractionsPanel.stories.tsx b/code/addons/interactions/src/components/InteractionsPanel.stories.tsx index 5f8dd331935b..65d16a1b68a6 100644 --- a/code/addons/interactions/src/components/InteractionsPanel.stories.tsx +++ b/code/addons/interactions/src/components/InteractionsPanel.stories.tsx @@ -120,11 +120,11 @@ export const Failed: Story = { }, }; -export const NoInteractions: Story = { - args: { - interactions: [], - }, -}; +// export const NoInteractions: Story = { +// args: { +// interactions: [], +// }, +// }; export const CaughtException: Story = { args: { diff --git a/code/addons/interactions/src/components/MatcherResult.stories.tsx b/code/addons/interactions/src/components/MatcherResult.stories.tsx index c81ac36eb201..4ac31a71ecdb 100644 --- a/code/addons/interactions/src/components/MatcherResult.stories.tsx +++ b/code/addons/interactions/src/components/MatcherResult.stories.tsx @@ -29,11 +29,18 @@ export default { export const Expected = { args: { message: dedent` - expect(jest.fn()).lastCalledWith(...expected) - - Expected: {"email": "michael@chromatic.com", "password": "testpasswordthatwontfail"} - - Number of calls: 0 + expected last "spy" call to have been called with [ { …(2) } ] + + - Expected: + Array [ + Object { + "email": "michael@chromatic.com", + "password": "testpasswordthatwontfail", + }, + ] + + + Received: + undefined `, }, }; @@ -41,12 +48,18 @@ export const Expected = { export const ExpectedReceived = { args: { message: dedent` - expect(jest.fn()).toHaveBeenCalledWith(...expected) - - Expected: called with 0 arguments - Received: {"email": "michael@chromatic.com", "password": "testpasswordthatwontfail"} - - Number of calls: 1 + expected last "spy" call to have been called with [] + + - Expected + + Received + + - Array [] + + Array [ + + Object { + + "email": "michael@chromatic.com", + + "password": "testpasswordthatwontfail", + + }, + + ] `, }, }; @@ -54,10 +67,21 @@ export const ExpectedReceived = { export const ExpectedNumberOfCalls = { args: { message: dedent` - expect(jest.fn()).toHaveBeenCalledTimes(expected) - - Expected number of calls: 1 - Received number of calls: 0 + expected "spy" to not be called at all, but actually been called 1 times + + Received: + + 1st spy call: + + Array [ + Object { + "email": "michael@chromatic.com", + "password": "testpasswordthatwontfail", + }, + ] + + + Number of calls: 1 `, }, }; @@ -65,17 +89,21 @@ export const ExpectedNumberOfCalls = { export const Diff = { args: { message: dedent` - expect(jest.fn()).toHaveBeenCalledWith(...expected) - - - Expected - + Received - - Object { - - "email": "michael@chromatic.com", - + "email": "michael@chromaui.com", - "password": "testpasswordthatwontfail", - }, - + expected "spy" to be called with arguments: [ { …(2) } ] + + Received: + + 1st spy call: + + Array [ + Object { + - "email": "michael@chromaui.com", + + "email": "michael@chromatic.com", + "password": "testpasswordthatwontfail", + }, + ] + + Number of calls: 1 `, }, diff --git a/code/addons/interactions/src/components/MatcherResult.tsx b/code/addons/interactions/src/components/MatcherResult.tsx index ec75109a0598..fdd1fbdc4b83 100644 --- a/code/addons/interactions/src/components/MatcherResult.tsx +++ b/code/addons/interactions/src/components/MatcherResult.tsx @@ -85,7 +85,7 @@ export const MatcherResult = ({ if (line.match(/^\s*- /)) { return [,
]; } - if (line.match(/^\s*\+ /)) { + if (line.match(/^\s*\+ /) || line.match(/^Received: $/)) { return [,
]; } diff --git a/code/frameworks/angular/src/builders/build-storybook/index.ts b/code/frameworks/angular/src/builders/build-storybook/index.ts index 25f7faeb5268..892547aabe80 100644 --- a/code/frameworks/angular/src/builders/build-storybook/index.ts +++ b/code/frameworks/angular/src/builders/build-storybook/index.ts @@ -40,6 +40,7 @@ export type StorybookBuilderOptions = JsonObject & { enableProdMode?: boolean; styles?: StyleElement[]; stylePreprocessorOptions?: StylePreprocessorOptions; + preserveSymlinks?: boolean; assets?: AssetPattern[]; sourceMap?: SourceMapUnion; } & Pick< @@ -102,6 +103,7 @@ const commandBuilder: BuilderHandlerFn = ( assets, previewUrl, sourceMap = false, + preserveSymlinks = false, } = options; const standaloneOptions: StandaloneBuildOptions = { @@ -121,6 +123,7 @@ const commandBuilder: BuilderHandlerFn = ( ...(styles ? { styles } : {}), ...(assets ? { assets } : {}), sourceMap, + preserveSymlinks, }, tsConfig, webpackStatsJson, diff --git a/code/frameworks/angular/src/builders/build-storybook/schema.json b/code/frameworks/angular/src/builders/build-storybook/schema.json index 6b614e574172..dbc2a734417e 100644 --- a/code/frameworks/angular/src/builders/build-storybook/schema.json +++ b/code/frameworks/angular/src/builders/build-storybook/schema.json @@ -19,6 +19,11 @@ "description": "Directory where to store built files.", "default": "storybook-static" }, + "preserveSymlinks": { + "type": "boolean", + "description": "Do not use the real path when resolving modules. If true, symlinks are resolved to their real path, if false, symlinks are resolved to their symlinked path.", + "default": false + }, "configDir": { "type": "string", "description": "Directory where to load Storybook configurations from.", diff --git a/code/frameworks/angular/src/builders/start-storybook/index.ts b/code/frameworks/angular/src/builders/start-storybook/index.ts index 2ecbb63c8a0f..7700cc3acdbc 100644 --- a/code/frameworks/angular/src/builders/start-storybook/index.ts +++ b/code/frameworks/angular/src/builders/start-storybook/index.ts @@ -37,6 +37,7 @@ export type StorybookBuilderOptions = JsonObject & { styles?: StyleElement[]; stylePreprocessorOptions?: StylePreprocessorOptions; assets?: AssetPattern[]; + preserveSymlinks?: boolean; sourceMap?: SourceMapUnion; } & Pick< // makes sure the option exists @@ -118,6 +119,7 @@ const commandBuilder: BuilderHandlerFn = (options, cont statsJson, previewUrl, sourceMap = false, + preserveSymlinks = false, } = options; const standaloneOptions: StandaloneOptions = { @@ -141,6 +143,7 @@ const commandBuilder: BuilderHandlerFn = (options, cont ...(stylePreprocessorOptions ? { stylePreprocessorOptions } : {}), ...(styles ? { styles } : {}), ...(assets ? { assets } : {}), + preserveSymlinks, sourceMap, }, tsConfig, diff --git a/code/frameworks/angular/src/builders/start-storybook/schema.json b/code/frameworks/angular/src/builders/start-storybook/schema.json index 2b98bab9039c..a2eef03e4b08 100644 --- a/code/frameworks/angular/src/builders/start-storybook/schema.json +++ b/code/frameworks/angular/src/builders/start-storybook/schema.json @@ -19,6 +19,11 @@ "type": "string", "description": "The full path for the TypeScript configuration file, relative to the current workspace." }, + "preserveSymlinks": { + "type": "boolean", + "description": "Do not use the real path when resolving modules. If true, symlinks are resolved to their real path, if false, symlinks are resolved to their symlinked path.", + "default": false + }, "port": { "type": "number", "description": "Port to listen on.", diff --git a/code/frameworks/angular/src/builders/utils/standalone-options.ts b/code/frameworks/angular/src/builders/utils/standalone-options.ts index ef73d78f01b5..83fc0090985c 100644 --- a/code/frameworks/angular/src/builders/utils/standalone-options.ts +++ b/code/frameworks/angular/src/builders/utils/standalone-options.ts @@ -18,6 +18,7 @@ export type StandaloneOptions = CLIOptions & stylePreprocessorOptions?: StylePreprocessorOptions; assets?: AssetPattern[]; sourceMap?: SourceMapUnion; + preserveSymlinks?: boolean; }; angularBuilderContext?: BuilderContext | null; tsConfig?: string; diff --git a/code/frameworks/angular/src/server/angular-cli-webpack.js b/code/frameworks/angular/src/server/angular-cli-webpack.js index 621680125536..f4e667fee6ee 100644 --- a/code/frameworks/angular/src/server/angular-cli-webpack.js +++ b/code/frameworks/angular/src/server/angular-cli-webpack.js @@ -68,6 +68,10 @@ exports.getWebpackConfig = async (baseConfig, { builderOptions, builderContext } styles: builderOptions.styles ?.map((style) => (typeof style === 'string' ? style : style.input)) .filter((style) => typeof style === 'string' || style.inject !== false), + outputPath: + typeof builderOptions.outputPath === 'string' + ? builderOptions.outputPath + : builderOptions.outputPath?.base, // Fixed options optimization: false, diff --git a/code/lib/cli/src/automigrate/fixes/react-docgen.ts b/code/lib/cli/src/automigrate/fixes/react-docgen.ts index 41e0875f475d..9359a96bacd9 100644 --- a/code/lib/cli/src/automigrate/fixes/react-docgen.ts +++ b/code/lib/cli/src/automigrate/fixes/react-docgen.ts @@ -62,7 +62,7 @@ export const reactDocgen: Fix = { For known "react-docgen" limitations, see: ${chalk.yellow('https://github.com/storybookjs/storybook/issues/26606')} - Press Y to revert to ${chalk.cyan('react-docgen-typesript')}, press N to use ${chalk.cyan( + Press Y to revert to ${chalk.cyan('react-docgen-typescript')}, press N to use ${chalk.cyan( 'react-docgen' )} `; diff --git a/code/lib/core-common/src/utils/__tests__/normalize-stories.test.ts b/code/lib/core-common/src/utils/__tests__/normalize-stories.test.ts index 8bf64a548ed5..9cefd9052bf4 100644 --- a/code/lib/core-common/src/utils/__tests__/normalize-stories.test.ts +++ b/code/lib/core-common/src/utils/__tests__/normalize-stories.test.ts @@ -42,172 +42,172 @@ const options = { describe('normalizeStoriesEntry', () => { it('direct file path', () => { - const specifier = normalizeStoriesEntry('../path/to/file.stories.mdx', options); + const specifier = normalizeStoriesEntry('../path/to/file.stories.jsx', options); expect(specifier).toMatchInlineSnapshot(` { "titlePrefix": "", "directory": "./path/to", - "files": "file.stories.mdx", + "files": "file.stories.jsx", "importPathMatcher": {} } `); - expect(specifier.importPathMatcher).toMatchPaths(['./path/to/file.stories.mdx']); + expect(specifier.importPathMatcher).toMatchPaths(['./path/to/file.stories.jsx']); expect(specifier.importPathMatcher).not.toMatchPaths([ './path/to/file.stories.js', - './file.stories.mdx', - '../file.stories.mdx', + './file.stories.jsx', + '../file.stories.jsx', ]); }); it('story in config dir', () => { - const specifier = normalizeStoriesEntry('./file.stories.mdx', options); + const specifier = normalizeStoriesEntry('./file.stories.jsx', options); expect(specifier).toMatchInlineSnapshot(` { "titlePrefix": "", "directory": "./.storybook", - "files": "file.stories.mdx", + "files": "file.stories.jsx", "importPathMatcher": {} } `); - expect(specifier.importPathMatcher).toMatchPaths(['./.storybook/file.stories.mdx']); + expect(specifier.importPathMatcher).toMatchPaths(['./.storybook/file.stories.jsx']); expect(specifier.importPathMatcher).not.toMatchPaths([ - '.storybook/file.stories.mdx', - './file.stories.mdx', - '../file.stories.mdx', + '.storybook/file.stories.jsx', + './file.stories.jsx', + '../file.stories.jsx', ]); }); it('non-recursive files glob', () => { - const specifier = normalizeStoriesEntry('../*/*.stories.mdx', options); + const specifier = normalizeStoriesEntry('../*/*.stories.jsx', options); expect(specifier).toMatchInlineSnapshot(` { "titlePrefix": "", "directory": ".", - "files": "*/*.stories.mdx", + "files": "*/*.stories.jsx", "importPathMatcher": {} } `); expect(specifier.importPathMatcher).toMatchPaths([ - './path/file.stories.mdx', - './second-path/file.stories.mdx', + './path/file.stories.jsx', + './second-path/file.stories.jsx', ]); expect(specifier.importPathMatcher).not.toMatchPaths([ './path/file.stories.js', - './path/to/file.stories.mdx', - './file.stories.mdx', - '../file.stories.mdx', + './path/to/file.stories.jsx', + './file.stories.jsx', + '../file.stories.jsx', ]); }); it('double non-recursive directory/files glob', () => { - const specifier = normalizeStoriesEntry('../*/*/*.stories.mdx', options); + const specifier = normalizeStoriesEntry('../*/*/*.stories.jsx', options); expect(specifier).toMatchInlineSnapshot(` { "titlePrefix": "", "directory": ".", - "files": "*/*/*.stories.mdx", + "files": "*/*/*.stories.jsx", "importPathMatcher": {} } `); expect(specifier.importPathMatcher).toMatchPaths([ - './path/to/file.stories.mdx', - './second-path/to/file.stories.mdx', + './path/to/file.stories.jsx', + './second-path/to/file.stories.jsx', ]); expect(specifier.importPathMatcher).not.toMatchPaths([ - './file.stories.mdx', - './path/file.stories.mdx', - './path/to/third/file.stories.mdx', + './file.stories.jsx', + './path/file.stories.jsx', + './path/to/third/file.stories.jsx', './path/to/file.stories.js', - '../file.stories.mdx', + '../file.stories.jsx', ]); }); it('directory/files glob', () => { - const specifier = normalizeStoriesEntry('../**/*.stories.mdx', options); + const specifier = normalizeStoriesEntry('../**/*.stories.jsx', options); expect(specifier).toMatchInlineSnapshot(` { "titlePrefix": "", "directory": ".", - "files": "**/*.stories.mdx", + "files": "**/*.stories.jsx", "importPathMatcher": {} } `); expect(specifier.importPathMatcher).toMatchPaths([ - './file.stories.mdx', - './path/file.stories.mdx', - './path/to/file.stories.mdx', - './path/to/third/file.stories.mdx', + './file.stories.jsx', + './path/file.stories.jsx', + './path/to/file.stories.jsx', + './path/to/third/file.stories.jsx', ]); expect(specifier.importPathMatcher).not.toMatchPaths([ './file.stories.js', - '../file.stories.mdx', + '../file.stories.jsx', ]); }); it('double stars glob', () => { - const specifier = normalizeStoriesEntry('../**/foo/**/*.stories.mdx', options); + const specifier = normalizeStoriesEntry('../**/foo/**/*.stories.jsx', options); expect(specifier).toMatchInlineSnapshot(` { "titlePrefix": "", "directory": ".", - "files": "**/foo/**/*.stories.mdx", + "files": "**/foo/**/*.stories.jsx", "importPathMatcher": {} } `); expect(specifier.importPathMatcher).toMatchPaths([ - './foo/file.stories.mdx', - './path/to/foo/file.stories.mdx', - './path/to/foo/third/fourth/file.stories.mdx', + './foo/file.stories.jsx', + './path/to/foo/file.stories.jsx', + './path/to/foo/third/fourth/file.stories.jsx', ]); expect(specifier.importPathMatcher).not.toMatchPaths([ - './file.stories.mdx', + './file.stories.jsx', './file.stories.js', - '../file.stories.mdx', + '../file.stories.jsx', ]); }); it('intermediate directory glob', () => { - const specifier = normalizeStoriesEntry('../**/foo/*.stories.mdx', options); + const specifier = normalizeStoriesEntry('../**/foo/*.stories.jsx', options); expect(specifier).toMatchInlineSnapshot(` { "titlePrefix": "", "directory": ".", - "files": "**/foo/*.stories.mdx", + "files": "**/foo/*.stories.jsx", "importPathMatcher": {} } `); expect(specifier.importPathMatcher).toMatchPaths([ - './path/to/foo/file.stories.mdx', - './foo/file.stories.mdx', + './path/to/foo/file.stories.jsx', + './foo/file.stories.jsx', ]); expect(specifier.importPathMatcher).not.toMatchPaths([ - './file.stories.mdx', + './file.stories.jsx', './file.stories.js', - './path/to/foo/third/fourth/file.stories.mdx', - '../file.stories.mdx', + './path/to/foo/third/fourth/file.stories.jsx', + '../file.stories.jsx', ]); }); it('directory outside of working dir', () => { - const specifier = normalizeStoriesEntry('../../src/*.stories.mdx', options); + const specifier = normalizeStoriesEntry('../../src/*.stories.jsx', options); expect(specifier).toMatchInlineSnapshot(` { "titlePrefix": "", "directory": "../src", - "files": "*.stories.mdx", + "files": "*.stories.jsx", "importPathMatcher": {} } `); - expect(specifier.importPathMatcher).toMatchPaths(['../src/file.stories.mdx']); + expect(specifier.importPathMatcher).toMatchPaths(['../src/file.stories.jsx']); expect(specifier.importPathMatcher).not.toMatchPaths([ - './src/file.stories.mdx', + './src/file.stories.jsx', '../src/file.stories.js', ]); }); @@ -237,11 +237,11 @@ describe('normalizeStoriesEntry', () => { }); it('directory/files specifier', () => { - const specifier = normalizeStoriesEntry({ directory: '..', files: '*.stories.mdx' }, options); + const specifier = normalizeStoriesEntry({ directory: '..', files: '*.stories.jsx' }, options); expect(specifier).toMatchInlineSnapshot(` { "titlePrefix": "", - "files": "*.stories.mdx", + "files": "*.stories.jsx", "directory": ".", "importPathMatcher": {} } @@ -262,13 +262,13 @@ describe('normalizeStoriesEntry', () => { it('directory/titlePrefix/files specifier', () => { const specifier = normalizeStoriesEntry( - { directory: '..', titlePrefix: 'atoms', files: '*.stories.mdx' }, + { directory: '..', titlePrefix: 'atoms', files: '*.stories.jsx' }, options ); expect(specifier).toMatchInlineSnapshot(` { "titlePrefix": "atoms", - "files": "*.stories.mdx", + "files": "*.stories.jsx", "directory": ".", "importPathMatcher": {} } @@ -276,25 +276,25 @@ describe('normalizeStoriesEntry', () => { }); it('globs with negation', () => { - const specifier = normalizeStoriesEntry('../!(negation)/*.stories.mdx', options); + const specifier = normalizeStoriesEntry('../!(negation)/*.stories.jsx', options); expect(specifier).toMatchInlineSnapshot(` { "titlePrefix": "", "directory": ".", - "files": "!(negation)/*.stories.mdx", + "files": "!(negation)/*.stories.jsx", "importPathMatcher": {} } `); expect(specifier.importPathMatcher).toMatchPaths([ - './path/file.stories.mdx', - './second-path/file.stories.mdx', + './path/file.stories.jsx', + './second-path/file.stories.jsx', ]); expect(specifier.importPathMatcher).not.toMatchPaths([ './path/file.stories.js', - './path/to/file.stories.mdx', - './file.stories.mdx', - '../file.stories.mdx', + './path/to/file.stories.jsx', + './file.stories.jsx', + '../file.stories.jsx', ]); }); }); diff --git a/code/lib/core-server/package.json b/code/lib/core-server/package.json index 9f832722a45a..a9c5033a1866 100644 --- a/code/lib/core-server/package.json +++ b/code/lib/core-server/package.json @@ -96,6 +96,7 @@ "telejson": "^7.2.0", "tiny-invariant": "^1.3.1", "ts-dedent": "^2.0.0", + "tsconfig-paths": "^4.2.0", "util": "^0.12.4", "util-deprecate": "^1.0.2", "watchpack": "^2.2.0", diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts index ccce3fe481bc..ca89c4a861a8 100644 --- a/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts +++ b/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts @@ -75,6 +75,7 @@ describe('StoryIndexGenerator', () => { { "entries": { "a--story-one": { + "componentPath": undefined, "id": "a--story-one", "importPath": "./src/A.stories.js", "name": "Story One", @@ -107,6 +108,7 @@ describe('StoryIndexGenerator', () => { { "entries": { "f--story-one": { + "componentPath": undefined, "id": "f--story-one", "importPath": "./src/F.story.ts", "name": "Story One", @@ -138,6 +140,7 @@ describe('StoryIndexGenerator', () => { { "entries": { "stories--story-one": { + "componentPath": undefined, "id": "stories--story-one", "importPath": "./src/stories.ts", "name": "Story One", @@ -168,7 +171,44 @@ describe('StoryIndexGenerator', () => { expect(await generator.getIndex()).toMatchInlineSnapshot(` { "entries": { + "componentpath-extension--story-one": { + "componentPath": "./src/componentPath/component.js", + "id": "componentpath-extension--story-one", + "importPath": "./src/componentPath/extension.stories.js", + "name": "Story One", + "tags": [ + "dev", + "test", + ], + "title": "componentPath/extension", + "type": "story", + }, + "componentpath-noextension--story-one": { + "componentPath": "./src/componentPath/component.js", + "id": "componentpath-noextension--story-one", + "importPath": "./src/componentPath/noExtension.stories.js", + "name": "Story One", + "tags": [ + "dev", + "test", + ], + "title": "componentPath/noExtension", + "type": "story", + }, + "componentpath-package--story-one": { + "componentPath": "component-package", + "id": "componentpath-package--story-one", + "importPath": "./src/componentPath/package.stories.js", + "name": "Story One", + "tags": [ + "dev", + "test", + ], + "title": "componentPath/package", + "type": "story", + }, "nested-button--story-one": { + "componentPath": undefined, "id": "nested-button--story-one", "importPath": "./src/nested/Button.stories.ts", "name": "Story One", @@ -181,6 +221,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, "second-nested-g--story-one": { + "componentPath": undefined, "id": "second-nested-g--story-one", "importPath": "./src/second-nested/G.stories.ts", "name": "Story One", @@ -211,6 +252,7 @@ describe('StoryIndexGenerator', () => { { "entries": { "a--story-one": { + "componentPath": undefined, "id": "a--story-one", "importPath": "./src/A.stories.js", "name": "Story One", @@ -224,6 +266,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, "b--story-one": { + "componentPath": undefined, "id": "b--story-one", "importPath": "./src/B.stories.ts", "name": "Story One", @@ -235,7 +278,44 @@ describe('StoryIndexGenerator', () => { "title": "B", "type": "story", }, + "componentpath-extension--story-one": { + "componentPath": "./src/componentPath/component.js", + "id": "componentpath-extension--story-one", + "importPath": "./src/componentPath/extension.stories.js", + "name": "Story One", + "tags": [ + "dev", + "test", + ], + "title": "componentPath/extension", + "type": "story", + }, + "componentpath-noextension--story-one": { + "componentPath": "./src/componentPath/component.js", + "id": "componentpath-noextension--story-one", + "importPath": "./src/componentPath/noExtension.stories.js", + "name": "Story One", + "tags": [ + "dev", + "test", + ], + "title": "componentPath/noExtension", + "type": "story", + }, + "componentpath-package--story-one": { + "componentPath": "component-package", + "id": "componentpath-package--story-one", + "importPath": "./src/componentPath/package.stories.js", + "name": "Story One", + "tags": [ + "dev", + "test", + ], + "title": "componentPath/package", + "type": "story", + }, "d--story-one": { + "componentPath": undefined, "id": "d--story-one", "importPath": "./src/D.stories.jsx", "name": "Story One", @@ -248,6 +328,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, "first-nested-deeply-f--story-one": { + "componentPath": undefined, "id": "first-nested-deeply-f--story-one", "importPath": "./src/first-nested/deeply/F.stories.js", "name": "Story One", @@ -259,6 +340,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, "h--story-one": { + "componentPath": undefined, "id": "h--story-one", "importPath": "./src/H.stories.mjs", "name": "Story One", @@ -271,6 +353,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, "nested-button--story-one": { + "componentPath": undefined, "id": "nested-button--story-one", "importPath": "./src/nested/Button.stories.ts", "name": "Story One", @@ -283,6 +366,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, "second-nested-g--story-one": { + "componentPath": undefined, "id": "second-nested-g--story-one", "importPath": "./src/second-nested/G.stories.ts", "name": "Story One", @@ -318,6 +402,7 @@ describe('StoryIndexGenerator', () => { { "entries": { "a--story-one": { + "componentPath": undefined, "id": "a--story-one", "importPath": "./src/A.stories.js", "name": "Story One", @@ -344,6 +429,7 @@ describe('StoryIndexGenerator', () => { "type": "docs", }, "b--story-one": { + "componentPath": undefined, "id": "b--story-one", "importPath": "./src/B.stories.ts", "name": "Story One", @@ -355,6 +441,42 @@ describe('StoryIndexGenerator', () => { "title": "B", "type": "story", }, + "componentpath-extension--story-one": { + "componentPath": "./src/componentPath/component.js", + "id": "componentpath-extension--story-one", + "importPath": "./src/componentPath/extension.stories.js", + "name": "Story One", + "tags": [ + "dev", + "test", + ], + "title": "componentPath/extension", + "type": "story", + }, + "componentpath-noextension--story-one": { + "componentPath": "./src/componentPath/component.js", + "id": "componentpath-noextension--story-one", + "importPath": "./src/componentPath/noExtension.stories.js", + "name": "Story One", + "tags": [ + "dev", + "test", + ], + "title": "componentPath/noExtension", + "type": "story", + }, + "componentpath-package--story-one": { + "componentPath": "component-package", + "id": "componentpath-package--story-one", + "importPath": "./src/componentPath/package.stories.js", + "name": "Story One", + "tags": [ + "dev", + "test", + ], + "title": "componentPath/package", + "type": "story", + }, "d--docs": { "id": "d--docs", "importPath": "./src/D.stories.jsx", @@ -369,6 +491,7 @@ describe('StoryIndexGenerator', () => { "type": "docs", }, "d--story-one": { + "componentPath": undefined, "id": "d--story-one", "importPath": "./src/D.stories.jsx", "name": "Story One", @@ -381,6 +504,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, "first-nested-deeply-f--story-one": { + "componentPath": undefined, "id": "first-nested-deeply-f--story-one", "importPath": "./src/first-nested/deeply/F.stories.js", "name": "Story One", @@ -405,6 +529,7 @@ describe('StoryIndexGenerator', () => { "type": "docs", }, "h--story-one": { + "componentPath": undefined, "id": "h--story-one", "importPath": "./src/H.stories.mjs", "name": "Story One", @@ -417,6 +542,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, "nested-button--story-one": { + "componentPath": undefined, "id": "nested-button--story-one", "importPath": "./src/nested/Button.stories.ts", "name": "Story One", @@ -429,6 +555,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, "second-nested-g--story-one": { + "componentPath": undefined, "id": "second-nested-g--story-one", "importPath": "./src/second-nested/G.stories.ts", "name": "Story One", @@ -471,6 +598,12 @@ describe('StoryIndexGenerator', () => { "d--story-one", "h--docs", "h--story-one", + "componentpath-extension--docs", + "componentpath-extension--story-one", + "componentpath-noextension--docs", + "componentpath-noextension--story-one", + "componentpath-package--docs", + "componentpath-package--story-one", "first-nested-deeply-f--docs", "first-nested-deeply-f--story-one", "nested-button--docs", @@ -581,6 +714,7 @@ describe('StoryIndexGenerator', () => { "type": "docs", }, "b--story-one": { + "componentPath": undefined, "id": "b--story-one", "importPath": "./src/B.stories.ts", "name": "Story One", @@ -644,6 +778,7 @@ describe('StoryIndexGenerator', () => { "type": "docs", }, "b--story-one": { + "componentPath": undefined, "id": "b--story-one", "importPath": "./src/B.stories.ts", "name": "Story One", @@ -700,6 +835,7 @@ describe('StoryIndexGenerator', () => { "type": "docs", }, "a--story-one": { + "componentPath": undefined, "id": "a--story-one", "importPath": "./src/A.stories.js", "name": "Story One", @@ -747,6 +883,7 @@ describe('StoryIndexGenerator', () => { "type": "docs", }, "duplicate-a--story-one": { + "componentPath": undefined, "id": "duplicate-a--story-one", "importPath": "./duplicate/A.stories.js", "name": "Story One", @@ -759,6 +896,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, "duplicate-a--story-two": { + "componentPath": undefined, "id": "duplicate-a--story-two", "importPath": "./duplicate/SecondA.stories.js", "name": "Story Two", @@ -820,6 +958,7 @@ describe('StoryIndexGenerator', () => { "type": "docs", }, "my-component-a--story-one": { + "componentPath": undefined, "id": "my-component-a--story-one", "importPath": "./docs-id-generation/A.stories.jsx", "name": "Story One", @@ -881,6 +1020,7 @@ describe('StoryIndexGenerator', () => { "type": "docs", }, "a--story-one": { + "componentPath": undefined, "id": "a--story-one", "importPath": "./src/A.stories.js", "name": "Story One", @@ -1014,6 +1154,7 @@ describe('StoryIndexGenerator', () => { "type": "docs", }, "a--story-one": { + "componentPath": undefined, "id": "a--story-one", "importPath": "./src/A.stories.js", "name": "Story One", @@ -1085,6 +1226,7 @@ describe('StoryIndexGenerator', () => { { "entries": { "a--story-one": { + "componentPath": undefined, "id": "a--story-one", "importPath": "./src/A.stories.js", "name": "Story One", @@ -1098,6 +1240,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, "b--story-one": { + "componentPath": undefined, "id": "b--story-one", "importPath": "./src/B.stories.ts", "name": "Story One", @@ -1165,6 +1308,7 @@ describe('StoryIndexGenerator', () => { "type": "docs", }, "my-component-b--story-one": { + "componentPath": undefined, "id": "my-component-b--story-one", "importPath": "./docs-id-generation/B.stories.jsx", "name": "Story One", @@ -1349,6 +1493,9 @@ describe('StoryIndexGenerator', () => { "componentreference--docs", "notitle--docs", "h--story-one", + "componentpath-extension--story-one", + "componentpath-noextension--story-one", + "componentpath-package--story-one", "first-nested-deeply-f--story-one", ] `); @@ -1367,7 +1514,7 @@ describe('StoryIndexGenerator', () => { const generator = new StoryIndexGenerator([specifier], options); await generator.initialize(); await generator.getIndex(); - expect(readCsfMock).toHaveBeenCalledTimes(7); + expect(readCsfMock).toHaveBeenCalledTimes(10); readCsfMock.mockClear(); await generator.getIndex(); @@ -1424,7 +1571,7 @@ describe('StoryIndexGenerator', () => { const generator = new StoryIndexGenerator([specifier], options); await generator.initialize(); await generator.getIndex(); - expect(readCsfMock).toHaveBeenCalledTimes(7); + expect(readCsfMock).toHaveBeenCalledTimes(10); generator.invalidate(specifier, './src/B.stories.ts', false); @@ -1509,7 +1656,7 @@ describe('StoryIndexGenerator', () => { const generator = new StoryIndexGenerator([specifier], options); await generator.initialize(); await generator.getIndex(); - expect(readCsfMock).toHaveBeenCalledTimes(7); + expect(readCsfMock).toHaveBeenCalledTimes(10); generator.invalidate(specifier, './src/B.stories.ts', true); @@ -1548,7 +1695,7 @@ describe('StoryIndexGenerator', () => { const generator = new StoryIndexGenerator([specifier], options); await generator.initialize(); await generator.getIndex(); - expect(readCsfMock).toHaveBeenCalledTimes(7); + expect(readCsfMock).toHaveBeenCalledTimes(10); generator.invalidate(specifier, './src/B.stories.ts', true); diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.ts index 3330a7e21a54..487447d18f4f 100644 --- a/code/lib/core-server/src/utils/StoryIndexGenerator.ts +++ b/code/lib/core-server/src/utils/StoryIndexGenerator.ts @@ -3,6 +3,8 @@ import chalk from 'chalk'; import fs from 'fs-extra'; import slash from 'slash'; import invariant from 'tiny-invariant'; +import * as TsconfigPaths from 'tsconfig-paths'; +import findUp from 'find-up'; import type { IndexEntry, @@ -52,9 +54,8 @@ export type StoryIndexGeneratorOptions = { }; export const AUTODOCS_TAG = 'autodocs'; -export const STORIES_MDX_TAG = 'stories-mdx'; -export const UNATTACHED_MDX_TAG = 'unattached-mdx'; export const ATTACHED_MDX_TAG = 'attached-mdx'; +export const UNATTACHED_MDX_TAG = 'unattached-mdx'; export const PLAY_FN_TAG = 'play-fn'; /** Was this docs entry generated by a .mdx file? (see discussion below) */ @@ -82,12 +83,11 @@ const makeAbsolute = (otherImport: Path, normalizedPath: Path, workingDir: Path) * * A stories file is indexed by an indexer (passed in), which produces a list of stories. * - If the stories have the `parameters.docsOnly` setting, they are disregarded. - * - If the stories have the 'stories-mdx' tag (i.e. were generated by a .stories.mdx file), - * OR autodocs is enabled, a docs entry is added pointing to the story file. + * - If the stories have autodocs enabled, a docs entry is added pointing to the story file. * * A (modern) docs (.mdx) file is indexed, a docs entry is added. * - * In the preview, a docs entry with either the `autodocs` or `stories-mdx` tags will be rendered + * In the preview, a docs entry with the `autodocs` tag will be rendered * as a CSF file that exports an MDX template on the `docs.page` parameter, whereas * other docs entries are rendered as MDX files directly. * @@ -278,6 +278,35 @@ export class StoryIndexGenerator { ); } + /** + * Try to find the component path from a raw import string and return it in + * the same format as `importPath`. Respect tsconfig paths if available. + * + * If no such file exists, assume that the import is from a package and + * return the raw path. + */ + resolveComponentPath( + rawComponentPath: Path, + absolutePath: Path, + matchPath: TsconfigPaths.MatchPath | undefined + ) { + let rawPath = rawComponentPath; + if (matchPath) { + rawPath = matchPath(rawPath) ?? rawPath; + } + + const absoluteComponentPath = path.resolve(path.dirname(absolutePath), rawPath); + const existing = ['', '.js', '.ts', '.jsx', '.tsx', '.mjs', '.mts'] + .map((ext) => `${absoluteComponentPath}${ext}`) + .find((candidate) => fs.existsSync(candidate)); + if (existing) { + const relativePath = path.relative(this.options.workingDir, existing); + return slash(normalizeStoryPath(relativePath)); + } + + return rawComponentPath; + } + async extractStories( specifier: NormalizedStoriesSpecifier, absolutePath: Path, @@ -299,11 +328,25 @@ export class StoryIndexGenerator { invariant(indexer, `No matching indexer found for ${absolutePath}`); const indexInputs = await indexer.createIndex(absolutePath, { makeTitle: defaultMakeTitle }); + const tsconfigPath = await findUp('tsconfig.json', { cwd: this.options.workingDir }); + const tsconfig = TsconfigPaths.loadConfig(tsconfigPath); + let matchPath: TsconfigPaths.MatchPath | undefined; + if (tsconfig.resultType === 'success') { + matchPath = TsconfigPaths.createMatchPath(tsconfig.absoluteBaseUrl, tsconfig.paths, [ + 'browser', + 'module', + 'main', + ]); + } const entries: ((StoryIndexEntryWithMetaId | DocsCacheEntry) & { tags: Tag[] })[] = indexInputs.map((input) => { const name = input.name ?? storyNameFromExport(input.exportName); + const componentPath = + input.rawComponentPath && + this.resolveComponentPath(input.rawComponentPath, absolutePath, matchPath); const title = input.title ?? defaultMakeTitle(); + // eslint-disable-next-line no-underscore-dangle const id = input.__id ?? toId(input.metaId ?? title, storyNameFromExport(input.exportName)); const tags = combineTags(...projectTags, ...(input.tags ?? [])); @@ -315,16 +358,16 @@ export class StoryIndexGenerator { name, title, importPath, + componentPath, tags, }; }); // We need a docs entry attached to the CSF file if either: - // a) we have autodocs enabled for this file - // b) it is a stories.mdx transpiled to CSF + // a) autodocs is globally enabled + // b) we have autodocs enabled for this file const hasAutodocsTag = entries.some((entry) => entry.tags.includes(AUTODOCS_TAG)); - const isStoriesMdx = entries.some((entry) => entry.tags.includes(STORIES_MDX_TAG)); - const createDocEntry = (hasAutodocsTag && this.options.docs.autodocs !== false) || isStoriesMdx; + const createDocEntry = hasAutodocsTag && this.options.docs.autodocs !== false; if (createDocEntry && this.options.build?.test?.disableAutoDocs !== true) { const name = this.options.docs.defaultName ?? 'Docs'; @@ -344,12 +387,8 @@ export class StoryIndexGenerator { }); } - const entriesWithoutDocsOnlyStories = entries.filter( - (entry) => !(entry.type === 'story' && entry.tags.includes('stories-mdx-docsOnly')) - ); - return { - entries: entriesWithoutDocsOnlyStories, + entries, dependents: [], type: 'stories', }; diff --git a/code/lib/core-server/src/utils/__mockdata__/src/componentPath/component.js b/code/lib/core-server/src/utils/__mockdata__/src/componentPath/component.js new file mode 100644 index 000000000000..ff8b4c56321a --- /dev/null +++ b/code/lib/core-server/src/utils/__mockdata__/src/componentPath/component.js @@ -0,0 +1 @@ +export default {}; diff --git a/code/lib/core-server/src/utils/__mockdata__/src/componentPath/extension.stories.js b/code/lib/core-server/src/utils/__mockdata__/src/componentPath/extension.stories.js new file mode 100644 index 000000000000..8a2b7cff9b7e --- /dev/null +++ b/code/lib/core-server/src/utils/__mockdata__/src/componentPath/extension.stories.js @@ -0,0 +1,7 @@ +import component from './component.js'; + +export default { + component, +}; + +export const StoryOne = {}; diff --git a/code/lib/core-server/src/utils/__mockdata__/src/componentPath/noExtension.stories.js b/code/lib/core-server/src/utils/__mockdata__/src/componentPath/noExtension.stories.js new file mode 100644 index 000000000000..4bb5febc075f --- /dev/null +++ b/code/lib/core-server/src/utils/__mockdata__/src/componentPath/noExtension.stories.js @@ -0,0 +1,7 @@ +import component from './component'; + +export default { + component, +}; + +export const StoryOne = {}; diff --git a/code/lib/core-server/src/utils/__mockdata__/src/componentPath/package.stories.js b/code/lib/core-server/src/utils/__mockdata__/src/componentPath/package.stories.js new file mode 100644 index 000000000000..20509edcf2be --- /dev/null +++ b/code/lib/core-server/src/utils/__mockdata__/src/componentPath/package.stories.js @@ -0,0 +1,8 @@ +// eslint-disable-next-line import/no-unresolved +import component from 'component-package'; + +export default { + component, +}; + +export const StoryOne = {}; diff --git a/code/lib/core-server/src/utils/__tests__/autoName.test.ts b/code/lib/core-server/src/utils/__tests__/autoName.test.ts index 9aefb7ec3eed..64954fc55d17 100644 --- a/code/lib/core-server/src/utils/__tests__/autoName.test.ts +++ b/code/lib/core-server/src/utils/__tests__/autoName.test.ts @@ -2,9 +2,9 @@ import { it, expect } from 'vitest'; import { autoName } from '../autoName'; it('pulls name from named MDX files', () => { - expect(autoName('Conventions.mdx', 'Button.stories.mdx', 'Docs')).toEqual('Conventions'); + expect(autoName('Conventions.mdx', 'Button.mdx', 'Docs')).toEqual('Conventions'); }); it('falls back for default named MDX files', () => { - expect(autoName('Button.mdx', 'Button.stories.mdx', 'Docs')).toEqual('Docs'); + expect(autoName('Button.mdx', 'Button.stories.jsx', 'Docs')).toEqual('Docs'); }); 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 a7bc19ae8ab9..9ab35f518034 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 @@ -8,7 +8,7 @@ import { normalizeStoriesEntry } from '@storybook/core-common'; import type { NormalizedStoriesSpecifier } from '@storybook/types'; import type { StoryIndexGeneratorOptions } from '../StoryIndexGenerator'; -import { AUTODOCS_TAG, STORIES_MDX_TAG, StoryIndexGenerator } from '../StoryIndexGenerator'; +import { AUTODOCS_TAG, StoryIndexGenerator } from '../StoryIndexGenerator'; vi.mock('@storybook/node-logger'); @@ -64,6 +64,7 @@ describe('story extraction', () => { "dependents": [], "entries": [ { + "componentPath": undefined, "id": "a--story-one", "importPath": "./src/A.stories.js", "metaId": "a", @@ -75,6 +76,7 @@ describe('story extraction', () => { "type": "story", }, { + "componentPath": undefined, "id": "some-fully-custom-id", "importPath": "./src/A.stories.js", "metaId": "custom-id", @@ -118,6 +120,7 @@ describe('story extraction', () => { "dependents": [], "entries": [ { + "componentPath": undefined, "id": "f--story-one", "importPath": "./src/first-nested/deeply/F.stories.js", "metaId": undefined, @@ -163,6 +166,7 @@ describe('story extraction', () => { "dependents": [], "entries": [ { + "componentPath": undefined, "id": "a--story-one", "importPath": "./src/first-nested/deeply/F.stories.js", "metaId": "a", @@ -210,6 +214,7 @@ describe('story extraction', () => { "dependents": [], "entries": [ { + "componentPath": undefined, "id": "a--story-one", "importPath": "./src/A.stories.js", "metaId": "a", @@ -275,6 +280,7 @@ describe('story extraction', () => { "dependents": [], "entries": [ { + "componentPath": undefined, "id": "a--story-one", "importPath": "./src/A.stories.js", "metaId": undefined, @@ -286,6 +292,7 @@ describe('story extraction', () => { "type": "story", }, { + "componentPath": undefined, "id": "custom-title--story-two", "importPath": "./src/A.stories.js", "metaId": undefined, @@ -297,6 +304,7 @@ describe('story extraction', () => { "type": "story", }, { + "componentPath": undefined, "id": "custom-meta-id--story-three", "importPath": "./src/A.stories.js", "metaId": "custom-meta-id", @@ -341,6 +349,7 @@ describe('story extraction', () => { "dependents": [], "entries": [ { + "componentPath": undefined, "id": "a--story-one", "importPath": "./src/A.stories.js", "metaId": undefined, @@ -390,6 +399,7 @@ describe('docs entries from story extraction', () => { "dependents": [], "entries": [ { + "componentPath": undefined, "id": "a--story-one", "importPath": "./src/A.stories.js", "metaId": undefined, @@ -449,6 +459,7 @@ describe('docs entries from story extraction', () => { "type": "docs", }, { + "componentPath": undefined, "id": "a--story-one", "importPath": "./src/A.stories.js", "metaId": undefined, @@ -497,6 +508,7 @@ describe('docs entries from story extraction', () => { "dependents": [], "entries": [ { + "componentPath": undefined, "id": "a--story-one", "importPath": "./src/A.stories.js", "metaId": undefined, @@ -513,64 +525,4 @@ describe('docs entries from story extraction', () => { } `); }); - it(`adds docs entry when an entry has the "${STORIES_MDX_TAG}" tag`, async () => { - const relativePath = './src/A.stories.js'; - const absolutePath = path.join(options.workingDir, relativePath); - const specifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(relativePath, options); - - const generator = new StoryIndexGenerator([specifier], { - ...options, - docs: { defaultName: 'docs', autodocs: false }, - indexers: [ - { - test: /\.stories\.(m?js|ts)x?$/, - createIndex: async (fileName) => [ - { - exportName: 'StoryOne', - __id: 'a--story-one', - name: 'Story One', - title: 'A', - tags: [STORIES_MDX_TAG, 'story-tag-from-indexer'], - importPath: fileName, - type: 'story', - }, - ], - }, - ], - }); - const result = await generator.extractStories(specifier, absolutePath); - - expect(result).toMatchInlineSnapshot(` - { - "dependents": [], - "entries": [ - { - "id": "a--docs", - "importPath": "./src/A.stories.js", - "name": "docs", - "storiesImports": [], - "tags": [ - "stories-mdx", - "story-tag-from-indexer", - ], - "title": "A", - "type": "docs", - }, - { - "id": "a--story-one", - "importPath": "./src/A.stories.js", - "metaId": undefined, - "name": "Story One", - "tags": [ - "stories-mdx", - "story-tag-from-indexer", - ], - "title": "A", - "type": "story", - }, - ], - "type": "stories", - } - `); - }); }); 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 0a7a2f4a3b5c..5ba134673c83 100644 --- a/code/lib/core-server/src/utils/stories-json.test.ts +++ b/code/lib/core-server/src/utils/stories-json.test.ts @@ -165,6 +165,42 @@ describe('useStoriesJson', () => { "title": "B", "type": "story", }, + "componentpath-extension--story-one": { + "componentPath": "./src/componentPath/component.js", + "id": "componentpath-extension--story-one", + "importPath": "./src/componentPath/extension.stories.js", + "name": "Story One", + "tags": [ + "dev", + "test", + ], + "title": "componentPath/extension", + "type": "story", + }, + "componentpath-noextension--story-one": { + "componentPath": "./src/componentPath/component.js", + "id": "componentpath-noextension--story-one", + "importPath": "./src/componentPath/noExtension.stories.js", + "name": "Story One", + "tags": [ + "dev", + "test", + ], + "title": "componentPath/noExtension", + "type": "story", + }, + "componentpath-package--story-one": { + "componentPath": "component-package", + "id": "componentpath-package--story-one", + "importPath": "./src/componentPath/package.stories.js", + "name": "Story One", + "tags": [ + "dev", + "test", + ], + "title": "componentPath/package", + "type": "story", + }, "d--story-one": { "id": "d--story-one", "importPath": "./src/D.stories.jsx", diff --git a/code/lib/core-server/src/utils/summarizeIndex.test.ts b/code/lib/core-server/src/utils/summarizeIndex.test.ts index 8d71317f3918..e1be61027c22 100644 --- a/code/lib/core-server/src/utils/summarizeIndex.test.ts +++ b/code/lib/core-server/src/utils/summarizeIndex.test.ts @@ -144,7 +144,6 @@ describe('summarizeIndex', () => { "onboardingStoryCount": 0, "pageStoryCount": 0, "playStoryCount": 0, - "storiesMdxCount": 0, "storyCount": 0, "version": 5, } @@ -202,7 +201,6 @@ describe('summarizeIndex', () => { "onboardingStoryCount": 1, "pageStoryCount": 0, "playStoryCount": 0, - "storiesMdxCount": 0, "storyCount": 0, "version": 5, } @@ -258,7 +256,6 @@ describe('summarizeIndex', () => { "onboardingStoryCount": 0, "pageStoryCount": 0, "playStoryCount": 0, - "storiesMdxCount": 0, "storyCount": 4, "version": 5, } @@ -315,69 +312,11 @@ describe('summarizeIndex', () => { "onboardingStoryCount": 0, "pageStoryCount": 1, "playStoryCount": 1, - "storiesMdxCount": 0, "storyCount": 1, "version": 5, } `); }); - it('storiesMdx', () => { - expect( - summarizeIndex({ - v: 5, - entries: { - 'stories-renderers-react-react-mdx--docs': { - id: 'stories-renderers-react-react-mdx--docs', - title: 'stories/renderers/react/react-mdx', - name: 'Docs', - importPath: './src/stories/renderers/react/react-mdx.stories.mdx', - type: 'docs', - tags: ['stories-mdx', 'docs'], - storiesImports: [], - }, - 'stories-renderers-react-react-mdx--primary': { - id: 'stories-renderers-react-react-mdx--primary', - title: 'stories/renderers/react/react-mdx', - name: 'Primary', - importPath: './src/stories/renderers/react/react-mdx.stories.mdx', - tags: ['stories-mdx', 'story'], - type: 'story', - }, - 'stories-renderers-react-react-mdx--secondary': { - id: 'stories-renderers-react-react-mdx--secondary', - title: 'stories/renderers/react/react-mdx', - name: 'Secondary', - importPath: './src/stories/renderers/react/react-mdx.stories.mdx', - tags: ['stories-mdx', 'story'], - type: 'story', - }, - 'stories-renderers-react-react-mdx--from-template': { - id: 'stories-renderers-react-react-mdx--from-template', - title: 'stories/renderers/react/react-mdx', - name: 'From Template', - importPath: './src/stories/renderers/react/react-mdx.stories.mdx', - tags: ['stories-mdx', 'story'], - type: 'story', - }, - }, - }) - ).toMatchInlineSnapshot(` - { - "autodocsCount": 0, - "componentCount": 1, - "exampleDocsCount": 0, - "exampleStoryCount": 0, - "mdxCount": 0, - "onboardingDocsCount": 0, - "onboardingStoryCount": 0, - "pageStoryCount": 0, - "playStoryCount": 0, - "storiesMdxCount": 1, - "storyCount": 3, - "version": 5, - } - `); - }); it('autodocs', () => { expect( summarizeIndex({ @@ -430,7 +369,6 @@ describe('summarizeIndex', () => { "onboardingStoryCount": 0, "pageStoryCount": 0, "playStoryCount": 0, - "storiesMdxCount": 0, "storyCount": 0, "version": 5, } @@ -481,7 +419,6 @@ describe('summarizeIndex', () => { "onboardingStoryCount": 0, "pageStoryCount": 0, "playStoryCount": 0, - "storiesMdxCount": 0, "storyCount": 0, "version": 5, } diff --git a/code/lib/core-server/src/utils/summarizeIndex.ts b/code/lib/core-server/src/utils/summarizeIndex.ts index 091038cdaccf..8b74ad8b7f5f 100644 --- a/code/lib/core-server/src/utils/summarizeIndex.ts +++ b/code/lib/core-server/src/utils/summarizeIndex.ts @@ -1,6 +1,6 @@ import type { IndexEntry, StoryIndex } from '@storybook/types'; -import { STORIES_MDX_TAG, isMdxEntry, AUTODOCS_TAG, PLAY_FN_TAG } from './StoryIndexGenerator'; +import { isMdxEntry, AUTODOCS_TAG, PLAY_FN_TAG } from './StoryIndexGenerator'; const PAGE_REGEX = /(page|screen)/i; @@ -44,7 +44,6 @@ export function summarizeIndex(storyIndex: StoryIndex) { let pageStoryCount = 0; let playStoryCount = 0; let autodocsCount = 0; - let storiesMdxCount = 0; let mdxCount = 0; Object.values(storyIndex.entries).forEach((entry) => { if (isCLIExampleEntry(entry)) { @@ -65,8 +64,6 @@ export function summarizeIndex(storyIndex: StoryIndex) { } else if (entry.type === 'docs') { if (isMdxEntry(entry)) { mdxCount += 1; - } else if (entry.tags?.includes(STORIES_MDX_TAG)) { - storiesMdxCount += 1; } else if (entry.tags?.includes(AUTODOCS_TAG)) { autodocsCount += 1; } @@ -79,7 +76,6 @@ export function summarizeIndex(storyIndex: StoryIndex) { pageStoryCount, playStoryCount, autodocsCount, - storiesMdxCount, mdxCount, exampleStoryCount, exampleDocsCount, diff --git a/code/lib/core-server/src/utils/watch-story-specifiers.test.ts b/code/lib/core-server/src/utils/watch-story-specifiers.test.ts index 22d3e0901de8..4026de15fa11 100644 --- a/code/lib/core-server/src/utils/watch-story-specifiers.test.ts +++ b/code/lib/core-server/src/utils/watch-story-specifiers.test.ts @@ -83,7 +83,7 @@ describe('watchStorySpecifiers', () => { }); it('watches single file globs', async () => { - const specifier = normalizeStoriesEntry('../src/nested/Button.stories.mdx', options); + const specifier = normalizeStoriesEntry('../src/nested/Button.mdx', options); const onInvalidate = vi.fn(); close = watchStorySpecifiers([specifier], { workingDir }, onInvalidate); @@ -98,33 +98,33 @@ describe('watchStorySpecifiers', () => { // File changed, matching onInvalidate.mockClear(); - await onChange('src/nested/Button.stories.mdx', 1234); - expect(onInvalidate).toHaveBeenCalledWith(specifier, `./src/nested/Button.stories.mdx`, false); + await onChange('src/nested/Button.mdx', 1234); + expect(onInvalidate).toHaveBeenCalledWith(specifier, `./src/nested/Button.mdx`, false); // File changed, NOT matching onInvalidate.mockClear(); - await onChange('src/nested/Button.mdx', 1234); + await onChange('src/nested/Button.tsx', 1234); expect(onInvalidate).not.toHaveBeenCalled(); // File removed, matching onInvalidate.mockClear(); - await onRemove('src/nested/Button.stories.mdx'); - expect(onInvalidate).toHaveBeenCalledWith(specifier, `./src/nested/Button.stories.mdx`, true); + await onRemove('src/nested/Button.mdx'); + expect(onInvalidate).toHaveBeenCalledWith(specifier, `./src/nested/Button.mdx`, true); // File removed, NOT matching onInvalidate.mockClear(); - await onRemove('src/nested/Button.mdx'); + await onRemove('src/nested/Button.tsx'); expect(onInvalidate).not.toHaveBeenCalled(); // File moved out, matching onInvalidate.mockClear(); - await onChange('src/nested/Button.stories.mdx', null); - expect(onInvalidate).toHaveBeenCalledWith(specifier, `./src/nested/Button.stories.mdx`, true); + await onChange('src/nested/Button.mdx', null); + expect(onInvalidate).toHaveBeenCalledWith(specifier, `./src/nested/Button.mdx`, true); }); it('multiplexes between two specifiers on the same directory', async () => { const globSpecifier = normalizeStoriesEntry('../src/**/*.stories.@(ts|js)', options); - const fileSpecifier = normalizeStoriesEntry('../src/nested/Button.stories.mdx', options); + const fileSpecifier = normalizeStoriesEntry('../src/nested/Button.mdx', options); const onInvalidate = vi.fn(); close = watchStorySpecifiers([globSpecifier, fileSpecifier], { workingDir }, onInvalidate); @@ -145,11 +145,7 @@ describe('watchStorySpecifiers', () => { ); onInvalidate.mockClear(); - await onChange('src/nested/Button.stories.mdx', 1234); - expect(onInvalidate).toHaveBeenCalledWith( - fileSpecifier, - `./src/nested/Button.stories.mdx`, - false - ); + await onChange('src/nested/Button.mdx', 1234); + expect(onInvalidate).toHaveBeenCalledWith(fileSpecifier, `./src/nested/Button.mdx`, false); }); }); diff --git a/code/lib/core-webpack/src/to-require-context.test.ts b/code/lib/core-webpack/src/to-require-context.test.ts index 90fab1d15f97..e6dbd3c8a90a 100644 --- a/code/lib/core-webpack/src/to-require-context.test.ts +++ b/code/lib/core-webpack/src/to-require-context.test.ts @@ -61,21 +61,21 @@ const testCases = [ }, // INVALID GLOB { - glob: '../src/stories/**/*.stories.(js|mdx)', + glob: '../src/stories/**/*.stories.(js|ts)', recursive: true, validPaths: [ '../src/stories/components/Icon.stories.js', '../src/stories/Icon.stories.js', - '../src/stories/Icon.stories.mdx', + '../src/stories/Icon.stories.ts', '../src/stories/components/Icon/Icon.stories.js', - '../src/stories/components/Icon.stories/Icon.stories.mdx', + '../src/stories/components/Icon.stories/Icon.stories.ts', ], invalidPaths: [ './stories.js', './src/stories/Icon.stories.js', './Icon.stories.js', - '../src/Icon.stories.mdx', - '../src/stories/components/Icon/Icon.stories.ts', + '../src/Icon.stories.ts', + '../src/stories/components/Icon/Icon.stories.tsx', '../src/stories/components/Icon/Icon.mdx', ], }, @@ -89,59 +89,59 @@ const testCases = [ invalidPaths: ['./dirname/../stories.js', './dirname/../App.stories.js'], }, { - glob: '../src/stories/**/@(*.stories.js|*.stories.mdx)', + glob: '../src/stories/**/@(*.stories.js|*.stories.ts)', recursive: true, validPaths: [ '../src/stories/components/Icon.stories.js', '../src/stories/Icon.stories.js', - '../src/stories/Icon.stories.mdx', + '../src/stories/Icon.stories.ts', '../src/stories/components/Icon/Icon.stories.js', - '../src/stories/components/Icon.stories/Icon.stories.mdx', + '../src/stories/components/Icon.stories/Icon.stories.ts', ], invalidPaths: [ './stories.js', './src/stories/Icon.stories.js', './Icon.stories.js', - '../src/Icon.stories.mdx', - '../src/stories/components/Icon/Icon.stories.ts', + '../src/Icon.stories.ts', + '../src/stories/components/Icon/Icon.stories.tsx', '../src/stories/components/Icon/Icon.mdx', ], }, { - glob: '../src/stories/**/*.stories.+(js|mdx)', + glob: '../src/stories/**/*.stories.+(js|ts)', recursive: true, validPaths: [ '../src/stories/components/Icon.stories.js', '../src/stories/Icon.stories.js', - '../src/stories/Icon.stories.mdx', + '../src/stories/Icon.stories.ts', '../src/stories/components/Icon/Icon.stories.js', - '../src/stories/components/Icon.stories/Icon.stories.mdx', + '../src/stories/components/Icon.stories/Icon.stories.ts', ], invalidPaths: [ './stories.js', './src/stories/Icon.stories.js', './Icon.stories.js', - '../src/Icon.stories.mdx', - '../src/stories/components/Icon/Icon.stories.ts', + '../src/Icon.stories.tsx', + '../src/stories/components/Icon/Icon.stories.tsx', '../src/stories/components/Icon/Icon.mdx', ], }, { - glob: '../src/stories/**/*.stories.*(js|mdx)', + glob: '../src/stories/**/*.stories.*(js|ts)', recursive: true, validPaths: [ '../src/stories/components/Icon.stories.js', '../src/stories/Icon.stories.js', - '../src/stories/Icon.stories.mdx', + '../src/stories/Icon.stories.ts', '../src/stories/components/Icon/Icon.stories.js', - '../src/stories/components/Icon.stories/Icon.stories.mdx', + '../src/stories/components/Icon.stories/Icon.stories.ts', ], invalidPaths: [ './stories.js', './src/stories/Icon.stories.js', './Icon.stories.js', - '../src/Icon.stories.mdx', - '../src/stories/components/Icon/Icon.stories.ts', + '../src/Icon.stories.ts', + '../src/stories/components/Icon/Icon.stories.tsx', '../src/stories/components/Icon/Icon.mdx', ], }, @@ -150,13 +150,13 @@ const testCases = [ recursive: false, validPaths: ['../src/stories/components/Icon.stories.js'], invalidPaths: [ - '../src/Icon.stories.mdx', - '../src/stories/components/Icon.stories/Icon.stories.mdx', + '../src/Icon.stories.tsx', + '../src/stories/components/Icon.stories/Icon.stories.tsx', '../src/stories/components/Icon/Icon.mdx', '../src/stories/components/Icon/Icon.stories.js', '../src/stories/components/Icon/Icon.stories.ts', '../src/stories/Icon.stories.js', - '../src/stories/Icon.stories.mdx', + '../src/stories/Icon.stories.tsx', './Icon.stories.js', './src/stories/Icon.stories.js', './stories.js', @@ -191,7 +191,7 @@ const testCases = [ './stories.js', './src/stories/Icon.stories.js', './Icon.stories.js', - '../src/Icon.stories.mdx', + '../src/Icon.stories.tsx', '../src/stories/components/Icon/Icon.stories.ts', '../src/stories/components/Icon/Icon.mdx', ], @@ -205,7 +205,7 @@ const testCases = [ './stories.js', './src/stories/Icon.stories.js', './Icon.stories.js', - '../src/Icon.stories.mdx', + '../src/Icon.stories.tsx', '../src/stories/components/Icon/Icon.stories.ts', '../src/stories/components/Icon/Icon.mdx', ], @@ -219,7 +219,7 @@ const testCases = [ './stories.js', './src/stories/Icon.stories.js', './Icon.stories.js', - '../src/Icon.stories.mdx', + '../src/Icon.stories.tsx', '../components/icon/Icon.stories.js', '../components/basics/simple/icon/Icon.stories.js', '../src/stories/components/Icon/Icon.stories.ts', @@ -235,7 +235,7 @@ const testCases = [ './stories.js', './src/stories/Icon.stories.js', './Icon.stories.js', - '../src/Icon.stories.mdx', + '../src/Icon.stories.tsx', '../src/stories/components/Icon/Icon.stories.ts', '../src/stories/components/Icon/Icon.mdx', ], diff --git a/code/lib/csf-tools/src/CsfFile.test.ts b/code/lib/csf-tools/src/CsfFile.test.ts index 669109d268ac..4c87576d2474 100644 --- a/code/lib/csf-tools/src/CsfFile.test.ts +++ b/code/lib/csf-tools/src/CsfFile.test.ts @@ -442,7 +442,7 @@ describe('CsfFile', () => { export const TestControl = () => _jsx("p", { children: "Hello" }); - export default { title: 'foo/bar', tags: ['stories-mdx'], includeStories: ["__page"] }; + export default { title: 'foo/bar', includeStories: ["__page"] }; export const __page = () => {}; __page.parameters = { docsOnly: true }; `, @@ -451,8 +451,6 @@ describe('CsfFile', () => { ).toMatchInlineSnapshot(` meta: title: foo/bar - tags: - - stories-mdx includeStories: - __page stories: @@ -1203,4 +1201,116 @@ describe('CsfFile', () => { `); }); }); + + describe('componenent paths', () => { + it('no component', () => { + const { indexInputs } = loadCsf( + dedent` + import { Component } from '../src/Component.js'; + export default { + title: 'custom foo title', + }; + + export const A = { + render: () => {}, + }; + `, + { makeTitle, fileName: 'foo/bar.stories.js' } + ).parse(); + + expect(indexInputs).toMatchInlineSnapshot(` + - type: story + importPath: foo/bar.stories.js + exportName: A + name: A + title: custom foo title + tags: [] + __id: custom-foo-title--a + `); + }); + + it('local component', () => { + const { indexInputs } = loadCsf( + dedent` + const Component = (props) =>
hello
; + + export default { + title: 'custom foo title', + component: Component, + }; + + export const A = { + render: () => {}, + }; + `, + { makeTitle, fileName: 'foo/bar.stories.js' } + ).parse(); + + expect(indexInputs).toMatchInlineSnapshot(` + - type: story + importPath: foo/bar.stories.js + exportName: A + name: A + title: custom foo title + tags: [] + __id: custom-foo-title--a + `); + }); + + it('imported component from file', () => { + const { indexInputs } = loadCsf( + dedent` + import { Component } from '../src/Component.js'; + export default { + title: 'custom foo title', + component: Component, + }; + + export const A = { + render: () => {}, + }; + `, + { makeTitle, fileName: 'foo/bar.stories.js' } + ).parse(); + + expect(indexInputs).toMatchInlineSnapshot(` + - type: story + importPath: foo/bar.stories.js + rawComponentPath: ../src/Component.js + exportName: A + name: A + title: custom foo title + tags: [] + __id: custom-foo-title--a + `); + }); + + it('imported component from library', () => { + const { indexInputs } = loadCsf( + dedent` + import { Component } from 'some-library'; + export default { + title: 'custom foo title', + component: Component, + }; + + export const A = { + render: () => {}, + }; + `, + { makeTitle, fileName: 'foo/bar.stories.js' } + ).parse(); + + expect(indexInputs).toMatchInlineSnapshot(` + - type: story + importPath: foo/bar.stories.js + rawComponentPath: some-library + exportName: A + name: A + title: custom foo title + tags: [] + __id: custom-foo-title--a + `); + }); + }); }); diff --git a/code/lib/csf-tools/src/CsfFile.ts b/code/lib/csf-tools/src/CsfFile.ts index ce0db70e3393..8e2757733545 100644 --- a/code/lib/csf-tools/src/CsfFile.ts +++ b/code/lib/csf-tools/src/CsfFile.ts @@ -142,6 +142,8 @@ export class CsfFile { _fileName: string; + _rawComponentPath?: string; + _makeTitle: (title: string) => string; _meta?: StaticMeta; @@ -202,6 +204,21 @@ export class CsfFile { } else if (['includeStories', 'excludeStories'].includes(p.key.name)) { (meta as any)[p.key.name] = parseIncludeExclude(p.value); } else if (p.key.name === 'component') { + const n = p.value; + if (t.isIdentifier(n)) { + const id = n.name; + const importStmt = program.body.find( + (stmt) => + t.isImportDeclaration(stmt) && + stmt.specifiers.find((spec) => spec.local.name === id) + ) as t.ImportDeclaration; + if (importStmt) { + const { source } = importStmt; + if (t.isStringLiteral(source)) { + this._rawComponentPath = source.value; + } + } + } const { code } = recast.print(p.value, {}); meta.component = code; } else if (p.key.name === 'tags') { @@ -564,6 +581,7 @@ export class CsfFile { return { type: 'story', importPath: this._fileName, + rawComponentPath: this._rawComponentPath, exportName, name: story.name, title: this.meta?.title, diff --git a/code/lib/docs-tools/package.json b/code/lib/docs-tools/package.json index af9fa6cd2fc8..1cfa025dd440 100644 --- a/code/lib/docs-tools/package.json +++ b/code/lib/docs-tools/package.json @@ -48,9 +48,8 @@ "@storybook/core-events": "workspace:*", "@storybook/preview-api": "workspace:*", "@storybook/types": "workspace:*", - "@types/doctrine": "^0.0.3", - "assert": "^2.1.0", - "doctrine": "^3.0.0", + "comment-parser": "^1.4.1", + "jsdoc-type-pratt-parser": "^4.0.0", "lodash": "^4.17.21" }, "devDependencies": { diff --git a/code/lib/docs-tools/src/argTypes/jsdocParser.test.ts b/code/lib/docs-tools/src/argTypes/jsdocParser.test.ts index 1460f70bf813..e0f83617605d 100644 --- a/code/lib/docs-tools/src/argTypes/jsdocParser.test.ts +++ b/code/lib/docs-tools/src/argTypes/jsdocParser.test.ts @@ -10,7 +10,7 @@ describe('parseJsDoc', () => { expect(extractedTags).toBeUndefined(); }); - it('should set includesJsDocto to false when the value dont contains JSDoc', () => { + it('should set includesJsDoc to false when the value dont contains JSDoc', () => { const { includesJsDoc, description, extractedTags } = parseJsDoc('Hey!'); expect(includesJsDoc).toBeFalsy(); @@ -68,7 +68,7 @@ describe('parseJsDoc', () => { expect(extractedTags?.params).not.toBeNull(); expect(extractedTags?.params?.[0].name).toBe('event'); expect(extractedTags?.params?.[0].type).not.toBeNull(); - expect(extractedTags?.params?.[0].type.name).toBe('SyntheticEvent'); + expect(extractedTags?.params?.[0].type.value).toBe('SyntheticEvent'); expect(extractedTags?.params?.[0].description).toBeNull(); }); @@ -78,7 +78,7 @@ describe('parseJsDoc', () => { expect(extractedTags?.params).not.toBeNull(); expect(extractedTags?.params?.[0].name).toBe('event'); expect(extractedTags?.params?.[0].type).not.toBeNull(); - expect(extractedTags?.params?.[0].type.name).toBe('SyntheticEvent'); + expect(extractedTags?.params?.[0].type.value).toBe('SyntheticEvent'); expect(extractedTags?.params?.[0].description).toBe('React event'); }); @@ -90,7 +90,7 @@ describe('parseJsDoc', () => { ['event1', 'event2', 'event3'].forEach((x, i) => { expect(extractedTags?.params?.[i].name).toBe(x); expect(extractedTags?.params?.[i].type).not.toBeNull(); - expect(extractedTags?.params?.[i].type.name).toBe('SyntheticEvent'); + expect(extractedTags?.params?.[i].type.value).toBe('SyntheticEvent'); expect(extractedTags?.params?.[i].description).toBe('React event'); }); }); @@ -129,7 +129,7 @@ describe('parseJsDoc', () => { expect(extractedTags?.params).not.toBeNull(); expect(extractedTags?.params?.[0].name).toBe('event'); expect(extractedTags?.params?.[0].type).not.toBeNull(); - expect(extractedTags?.params?.[0].type.name).toBe('SyntheticEvent'); + expect(extractedTags?.params?.[0].type.value).toBe('SyntheticEvent'); expect(extractedTags?.params?.[0].description).toBe('React event'); }); }); @@ -251,7 +251,7 @@ describe('parseJsDoc', () => { expect(extractedTags?.returns).not.toBeNull(); expect(extractedTags?.returns?.type).not.toBeNull(); - expect(extractedTags?.returns?.type.name).toBe('string'); + expect(extractedTags?.returns?.type.value).toBe('string'); }); it('should return a @returns with a type and a description', () => { @@ -259,7 +259,7 @@ describe('parseJsDoc', () => { expect(extractedTags?.returns).not.toBeNull(); expect(extractedTags?.returns?.type).not.toBeNull(); - expect(extractedTags?.returns?.type.name).toBe('string'); + expect(extractedTags?.returns?.type.value).toBe('string'); expect(extractedTags?.returns?.description).toBe('A bar description'); }); @@ -270,7 +270,7 @@ describe('parseJsDoc', () => { expect(extractedTags?.returns).not.toBeNull(); expect(extractedTags?.returns?.type).not.toBeNull(); - expect(extractedTags?.returns?.type.name).toBe('string'); + expect(extractedTags?.returns?.type.value).toBe('string'); expect(extractedTags?.returns?.description).toBe('This is\na multiline\ndescription'); }); @@ -279,7 +279,7 @@ describe('parseJsDoc', () => { expect(extractedTags?.returns).not.toBeNull(); expect(extractedTags?.returns?.type).not.toBeNull(); - expect(extractedTags?.returns?.type.name).toBe('number'); + expect(extractedTags?.returns?.type.value).toBe('number'); }); describe('getTypeName', () => { @@ -353,9 +353,9 @@ describe('parseJsDoc', () => { expect(extractedTags?.params).not.toBeNull(); expect(Object.keys(extractedTags?.params ?? []).length).toBe(1); expect(extractedTags?.params?.[0].name).toBe('event'); - expect(extractedTags?.params?.[0].type.name).toBe('SyntheticEvent'); + expect(extractedTags?.params?.[0].type.value).toBe('SyntheticEvent'); expect(extractedTags?.params?.[0].description).toBe('Original event.'); expect(extractedTags?.returns).not.toBeNull(); - expect(extractedTags?.returns?.type.name).toBe('string'); + expect(extractedTags?.returns?.type.value).toBe('string'); }); }); diff --git a/code/lib/docs-tools/src/argTypes/jsdocParser.ts b/code/lib/docs-tools/src/argTypes/jsdocParser.ts index 5a2b6258fc64..0990865a14c4 100644 --- a/code/lib/docs-tools/src/argTypes/jsdocParser.ts +++ b/code/lib/docs-tools/src/argTypes/jsdocParser.ts @@ -1,5 +1,11 @@ -import type { Annotation } from 'doctrine'; -import doctrine from 'doctrine'; +import type { Block, Spec } from 'comment-parser'; +import type { RootResult as JSDocType } from 'jsdoc-type-pratt-parser'; +import { parse as parseJSDoc } from 'comment-parser'; +import { + parse as parseJSDocType, + transform as transformJSDocType, + stringifyRules, +} from 'jsdoc-type-pratt-parser'; import type { JsDocParam, JsDocReturns } from './docgen'; export interface ExtractedJsDocParam extends JsDocParam { @@ -40,21 +46,25 @@ function containsJsDoc(value?: string | null): boolean { return value != null && value.includes('@'); } -function parse(content: string | null, tags?: string[]): Annotation { - let ast; +function parse(content: string | null): Block { + const contentString = content ?? ''; + const mappedLines = contentString + .split('\n') + .map((line) => ` * ${line}`) + .join('\n'); + const normalisedContent = '/**\n' + mappedLines + '\n*/'; - try { - ast = doctrine.parse(content ?? '', { - tags, - sloppy: true, - }); - } catch (e) { - console.error(e); + const ast = parseJSDoc(normalisedContent, { + spacing: 'preserve', + }); + if (!ast || ast.length === 0) { throw new Error('Cannot parse JSDoc tags.'); } - return ast; + // Return the first block, since we shouldn't ever really encounter + // multiple blocks of JSDoc + return ast[0]; } const DEFAULT_OPTIONS = { @@ -69,9 +79,9 @@ export const parseJsDoc: ParseJsDoc = (value, options = DEFAULT_OPTIONS) => { }; } - const jsDocAst = parse(value, options.tags); + const jsDocAst = parse(value); - const extractedTags = extractJsDocTags(jsDocAst); + const extractedTags = extractJsDocTags(jsDocAst, options.tags); if (extractedTags.ignore) { // There is no point in doing other stuff since this prop will not be rendered. @@ -85,12 +95,12 @@ export const parseJsDoc: ParseJsDoc = (value, options = DEFAULT_OPTIONS) => { includesJsDoc: true, ignore: false, // Always use the parsed description to ensure JSDoc is removed from the description. - description: jsDocAst.description, + description: jsDocAst.description.trim(), extractedTags, }; }; -function extractJsDocTags(ast: doctrine.Annotation): ExtractedJsDoc { +function extractJsDocTags(ast: Block, tags?: string[]): ExtractedJsDoc { const extractedTags: ExtractedJsDoc = { params: null, deprecated: null, @@ -98,20 +108,23 @@ function extractJsDocTags(ast: doctrine.Annotation): ExtractedJsDoc { ignore: false, }; - for (let i = 0; i < ast.tags.length; i += 1) { - const tag = ast.tags[i]; + for (const tagSpec of ast.tags) { + // Skip any tags we don't care about + if (tags !== undefined && !tags.includes(tagSpec.tag)) { + continue; + } - if (tag.title === 'ignore') { + if (tagSpec.tag === 'ignore') { extractedTags.ignore = true; // Once we reach an @ignore tag, there is no point in parsing the other tags since we will not render the prop. break; } else { - switch (tag.title) { + switch (tagSpec.tag) { // arg & argument are aliases for param. case 'param': case 'arg': case 'argument': { - const paramTag = extractParam(tag); + const paramTag = extractParam(tagSpec); if (paramTag != null) { if (extractedTags.params == null) { extractedTags.params = []; @@ -121,14 +134,14 @@ function extractJsDocTags(ast: doctrine.Annotation): ExtractedJsDoc { break; } case 'deprecated': { - const deprecatedTag = extractDeprecated(tag); + const deprecatedTag = extractDeprecated(tagSpec); if (deprecatedTag != null) { extractedTags.deprecated = deprecatedTag; } break; } case 'returns': { - const returnsTag = extractReturns(tag); + const returnsTag = extractReturns(tagSpec); if (returnsTag != null) { extractedTags.returns = returnsTag; } @@ -143,49 +156,61 @@ function extractJsDocTags(ast: doctrine.Annotation): ExtractedJsDoc { return extractedTags; } -function extractParam(tag: doctrine.Tag): ExtractedJsDocParam | null { - const paramName = tag.name; - - // When the @param doesn't have a name but have a type and a description, "null-null" is returned. - if (paramName != null && paramName !== 'null-null') { - return { - name: tag.name, - type: tag.type, - description: tag.description, - getPrettyName: () => { - if (paramName.includes('null')) { - // There is a few cases in which the returned param name contains "null". - // - @param {SyntheticEvent} event- Original SyntheticEvent - // - @param {SyntheticEvent} event.\n@returns {string} - return paramName.replace('-null', '').replace('.null', ''); - } +function normaliseParamName(name: string): string { + return name.replace(/[\.-]$/, ''); +} - return tag.name; - }, - getTypeName: () => { - return tag.type != null ? extractTypeName(tag.type) : null; - }, - }; +function extractParam(tag: Spec): ExtractedJsDocParam | null { + // Ignore tags with empty names or `-`. + // We ignore `-` since it means a comment was likely missing a name but + // using separators. For example: `@param {foo} - description` + if (!tag.name || tag.name === '-') { + return null; } - return null; + const type = extractType(tag.type); + + return { + name: tag.name, + type: type, + description: normaliseDescription(tag.description), + getPrettyName: () => { + return normaliseParamName(tag.name); + }, + getTypeName: () => { + return type ? extractTypeName(type) : null; + }, + }; } -function extractDeprecated(tag: doctrine.Tag): string | null { - if (tag.title != null) { - return tag.description; +function extractDeprecated(tag: Spec): string | null { + if (tag.name) { + return joinNameAndDescription(tag.name, tag.description); } return null; } -function extractReturns(tag: doctrine.Tag): ExtractedJsDocReturns | null { - if (tag.type != null) { +function joinNameAndDescription(name: string, desc: string): string | null { + const joined = name === '' ? desc : `${name} ${desc}`; + return normaliseDescription(joined); +} + +function normaliseDescription(text: string): string | null { + const normalised = text.replace(/^- /g, '').trim(); + + return normalised === '' ? null : normalised; +} + +function extractReturns(tag: Spec): ExtractedJsDocReturns | null { + const type = extractType(tag.type); + + if (type) { return { - type: tag.type, - description: tag.description, + type: type, + description: joinNameAndDescription(tag.name, tag.description), getTypeName: () => { - return extractTypeName(tag.type); + return extractTypeName(type); }, }; } @@ -193,57 +218,25 @@ function extractReturns(tag: doctrine.Tag): ExtractedJsDocReturns | null { return null; } -function extractTypeName(type?: doctrine.Type | null): string | null { - if (type?.type === 'NameExpression') { - return type.name; - } - - if (type?.type === 'RecordType') { - const recordFields = type.fields.map((field: doctrine.Type) => { - if (field.type === 'FieldType' && field.value != null) { - const valueTypeName = extractTypeName(field.value); - - return `${field.key}: ${valueTypeName}`; - } - - return (field as doctrine.type.FieldType).key; - }); - - return `({${recordFields.join(', ')}})`; - } - - if (type?.type === 'UnionType') { - const unionElements = type.elements.map(extractTypeName); - - return `(${unionElements.join('|')})`; - } - - // Only support untyped array: []. Might add more support later if required. - if (type?.type === 'ArrayType') { - return '[]'; - } - - if (type?.type === 'TypeApplication') { - if (type.expression != null) { - if ((type.expression as doctrine.type.NameExpression).name === 'Array') { - const arrayType = extractTypeName(type.applications[0]); - - return `${arrayType}[]`; - } - } - } - - if ( - type?.type === 'NullableType' || - type?.type === 'NonNullableType' || - type?.type === 'OptionalType' - ) { - return extractTypeName(type.expression); - } - - if (type?.type === 'AllLiteral') { - return 'any'; +const jsdocStringifyRules = stringifyRules(); +const originalJsdocStringifyObject = jsdocStringifyRules.JsdocTypeObject; +jsdocStringifyRules.JsdocTypeAny = () => 'any'; +jsdocStringifyRules.JsdocTypeObject = (result, transform) => + `(${originalJsdocStringifyObject(result, transform)})`; +jsdocStringifyRules.JsdocTypeOptional = (result, transform) => transform(result.element); +jsdocStringifyRules.JsdocTypeNullable = (result, transform) => transform(result.element); +jsdocStringifyRules.JsdocTypeNotNullable = (result, transform) => transform(result.element); +jsdocStringifyRules.JsdocTypeUnion = (result, transform) => + result.elements.map(transform).join('|'); + +function extractType(typeString: string): JSDocType | null { + try { + return parseJSDocType(typeString, 'typescript'); + } catch (_err) { + return null; } +} - return null; +function extractTypeName(type: JSDocType): string { + return transformJSDocType(jsdocStringifyRules, type); } diff --git a/code/lib/manager-api/src/tests/mockStoriesEntries.ts b/code/lib/manager-api/src/tests/mockStoriesEntries.ts index 703b6e6efb76..a820307f4ef2 100644 --- a/code/lib/manager-api/src/tests/mockStoriesEntries.ts +++ b/code/lib/manager-api/src/tests/mockStoriesEntries.ts @@ -53,7 +53,6 @@ export const docsEntries: StoryIndex['entries'] = { name: 'Docs', importPath: './path/to/component-b.ts', storiesImports: [], - tags: ['stories-mdx'], }, 'component-c--story-4': { type: 'story', diff --git a/code/lib/preview-api/src/modules/preview-web/PreviewWithSelection.tsx b/code/lib/preview-api/src/modules/preview-web/PreviewWithSelection.tsx index 193a5f52358a..9b1a8ed9348a 100644 --- a/code/lib/preview-api/src/modules/preview-web/PreviewWithSelection.tsx +++ b/code/lib/preview-api/src/modules/preview-web/PreviewWithSelection.tsx @@ -53,7 +53,6 @@ function focusInInput(event: Event) { } export const AUTODOCS_TAG = 'autodocs'; -export const STORIES_MDX_TAG = 'stories-mdx'; export const ATTACHED_MDX_TAG = 'attached-mdx'; export const UNATTACHED_MDX_TAG = 'unattached-mdx'; diff --git a/code/lib/preview-api/src/modules/preview-web/render/CsfDocsRender.ts b/code/lib/preview-api/src/modules/preview-web/render/CsfDocsRender.ts index 019d43d97893..0be1ce5821c3 100644 --- a/code/lib/preview-api/src/modules/preview-web/render/CsfDocsRender.ts +++ b/code/lib/preview-api/src/modules/preview-web/render/CsfDocsRender.ts @@ -25,8 +25,6 @@ import { DocsContext } from '../docs-context/DocsContext'; * * Use cases: * - Autodocs, where there is no story, and we fall back to the globally defined template. - * - *.stories.mdx files, where the MDX compiler produces a CSF file with a `.parameter.docs.page` - * parameter containing the compiled content of the MDX file. */ export class CsfDocsRender implements Render { public readonly type: RenderType = 'docs'; diff --git a/code/lib/theming/src/convert.ts b/code/lib/theming/src/convert.ts index fc23399d4a38..9765adc03d61 100644 --- a/code/lib/theming/src/convert.ts +++ b/code/lib/theming/src/convert.ts @@ -178,7 +178,7 @@ export const convert = (inherit: ThemeVars = themes[getPreferredColorScheme()]): }), // Addon actions theme - // API example https://github.com/xyc/react-inspector/blob/master/src/styles/themes/chromeLight.js + // API example https://github.com/storybookjs/react-inspector/blob/master/src/styles/themes/chromeLight.tsx addonActionsTheme: { ...(base === 'light' ? chromeLight : chromeDark), diff --git a/code/lib/types/src/modules/indexer.ts b/code/lib/types/src/modules/indexer.ts index 56d435cdb533..a34a2422766c 100644 --- a/code/lib/types/src/modules/indexer.ts +++ b/code/lib/types/src/modules/indexer.ts @@ -93,6 +93,8 @@ export type IndexEntry = StoryIndexEntry | DocsIndexEntry; export type BaseIndexInput = { /** The file to import from e.g. the story file. */ importPath: Path; + /** The raw path/package of the file that provides meta.component, if one exists */ + rawComponentPath?: Path; /** The name of the export to import. */ exportName: ExportName; /** The name of the entry, auto-generated from {@link exportName} if unspecified. */ diff --git a/code/package.json b/code/package.json index e9414600048a..8fe04e1d8ce0 100644 --- a/code/package.json +++ b/code/package.json @@ -297,5 +297,6 @@ "Dependency Upgrades" ] ] - } + }, + "deferredNextVersion": "8.2.0-alpha.7" } diff --git a/code/renderers/react/template/stories/docgen-components/jsdoc/argTypes.snapshot b/code/renderers/react/template/stories/docgen-components/jsdoc/argTypes.snapshot index 492640c6e1be..09c821398dad 100644 --- a/code/renderers/react/template/stories/docgen-components/jsdoc/argTypes.snapshot +++ b/code/renderers/react/template/stories/docgen-components/jsdoc/argTypes.snapshot @@ -101,20 +101,10 @@ "name": "case13", "table": { "defaultValue": undefined, - "jsDocTags": { - "deprecated": null, - "ignore": false, - "params": [ - { - "description": null, - "name": "SyntheticEvent", - }, - ], - "returns": null, - }, + "jsDocTags": undefined, "type": { "detail": undefined, - "summary": "(SyntheticEvent)", + "summary": "func", }, }, "type": { @@ -208,8 +198,8 @@ "description": null, "getTypeName": [Function], "type": { - "name": "string", - "type": "NameExpression", + "type": "JsdocTypeName", + "value": "string", }, }, }, @@ -391,8 +381,8 @@ description", "description": null, "getTypeName": [Function], "type": { - "name": "SyntheticEvent", - "type": "NameExpression", + "type": "JsdocTypeName", + "value": "SyntheticEvent", }, }, }, @@ -419,8 +409,8 @@ description", "description": "React's original event", "getTypeName": [Function], "type": { - "name": "SyntheticEvent", - "type": "NameExpression", + "type": "JsdocTypeName", + "value": "SyntheticEvent", }, }, }, @@ -452,8 +442,8 @@ description", "description": "React's original event", "getTypeName": [Function], "type": { - "name": "SyntheticEvent", - "type": "NameExpression", + "type": "JsdocTypeName", + "value": "SyntheticEvent", }, }, }, @@ -489,8 +479,8 @@ description", "description": "React's original event", "getTypeName": [Function], "type": { - "name": "SyntheticEvent", - "type": "NameExpression", + "type": "JsdocTypeName", + "value": "SyntheticEvent", }, }, }, @@ -517,8 +507,8 @@ description", "description": "Second returns", "getTypeName": [Function], "type": { - "name": "string", - "type": "NameExpression", + "type": "JsdocTypeName", + "value": "string", }, }, }, @@ -699,7 +689,7 @@ description", }, "type": { "detail": undefined, - "summary": "(myType)", + "summary": "(myType: ...number)", }, }, "type": { @@ -798,25 +788,38 @@ description", "description": null, "getTypeName": [Function], "type": { - "fields": [ + "elements": [ { "key": "a", - "type": "FieldType", - "value": { - "name": "number", - "type": "NameExpression", + "meta": { + "quote": undefined, + }, + "optional": false, + "readonly": false, + "right": { + "type": "JsdocTypeName", + "value": "number", }, + "type": "JsdocTypeObjectField", }, { "key": "b", - "type": "FieldType", - "value": { - "name": "string", - "type": "NameExpression", + "meta": { + "quote": undefined, }, + "optional": false, + "readonly": false, + "right": { + "type": "JsdocTypeName", + "value": "string", + }, + "type": "JsdocTypeObjectField", }, ], - "type": "RecordType", + "meta": { + "separator": "comma", + }, + "type": "JsdocTypeObject", }, }, }, @@ -843,17 +846,21 @@ description", "description": null, "getTypeName": [Function], "type": { - "applications": [ + "elements": [ { - "name": "string", - "type": "NameExpression", + "type": "JsdocTypeName", + "value": "string", }, ], - "expression": { - "name": "Array", - "type": "NameExpression", + "left": { + "type": "JsdocTypeName", + "value": "Array", + }, + "meta": { + "brackets": "square", + "dot": false, }, - "type": "TypeApplication", + "type": "JsdocTypeGeneric", }, }, }, @@ -909,17 +916,20 @@ description", "description": null, "getTypeName": [Function], "type": { - "elements": [ - { - "name": "number", - "type": "NameExpression", - }, - { - "name": "boolean", - "type": "NameExpression", - }, - ], - "type": "UnionType", + "element": { + "elements": [ + { + "type": "JsdocTypeName", + "value": "number", + }, + { + "type": "JsdocTypeName", + "value": "boolean", + }, + ], + "type": "JsdocTypeUnion", + }, + "type": "JsdocTypeParenthesis", }, }, }, @@ -946,7 +956,7 @@ description", "description": null, "getTypeName": [Function], "type": { - "type": "AllLiteral", + "type": "JsdocTypeAny", }, }, }, @@ -973,8 +983,8 @@ description", "description": null, "getTypeName": [Function], "type": { - "name": "string", - "type": "NameExpression", + "type": "JsdocTypeName", + "value": "string", }, }, }, @@ -1001,8 +1011,8 @@ description", "description": null, "getTypeName": [Function], "type": { - "name": "void", - "type": "NameExpression", + "type": "JsdocTypeName", + "value": "void", }, }, }, diff --git a/code/renderers/react/template/stories/docgen-components/jsdoc/properties.snapshot b/code/renderers/react/template/stories/docgen-components/jsdoc/properties.snapshot index cf7888853878..cef2829d44dc 100644 --- a/code/renderers/react/template/stories/docgen-components/jsdoc/properties.snapshot +++ b/code/renderers/react/template/stories/docgen-components/jsdoc/properties.snapshot @@ -227,17 +227,6 @@ description", { "defaultValue": null, "description": "param with type", - "jsDocTags": { - "deprecated": null, - "ignore": false, - "params": [ - { - "description": null, - "name": "SyntheticEvent", - }, - ], - "returns": null, - }, "name": "case13", "required": false, "sbType": { @@ -245,7 +234,7 @@ description", }, "type": { "detail": undefined, - "summary": "(SyntheticEvent)", + "summary": "func", }, }, { @@ -325,8 +314,8 @@ description", "description": null, "getTypeName": [Function], "type": { - "name": "string", - "type": "NameExpression", + "type": "JsdocTypeName", + "value": "string", }, }, }, @@ -470,8 +459,8 @@ description", "description": null, "getTypeName": [Function], "type": { - "name": "SyntheticEvent", - "type": "NameExpression", + "type": "JsdocTypeName", + "value": "SyntheticEvent", }, }, }, @@ -496,8 +485,8 @@ description", "description": "React's original event", "getTypeName": [Function], "type": { - "name": "SyntheticEvent", - "type": "NameExpression", + "type": "JsdocTypeName", + "value": "SyntheticEvent", }, }, }, @@ -527,8 +516,8 @@ description", "description": "React's original event", "getTypeName": [Function], "type": { - "name": "SyntheticEvent", - "type": "NameExpression", + "type": "JsdocTypeName", + "value": "SyntheticEvent", }, }, }, @@ -562,8 +551,8 @@ description", "description": "React's original event", "getTypeName": [Function], "type": { - "name": "SyntheticEvent", - "type": "NameExpression", + "type": "JsdocTypeName", + "value": "SyntheticEvent", }, }, }, @@ -588,8 +577,8 @@ description", "description": "Second returns", "getTypeName": [Function], "type": { - "name": "string", - "type": "NameExpression", + "type": "JsdocTypeName", + "value": "string", }, }, }, @@ -744,7 +733,7 @@ description", }, "type": { "detail": undefined, - "summary": "(myType)", + "summary": "(myType: ...number)", }, }, { @@ -830,25 +819,38 @@ description", "description": null, "getTypeName": [Function], "type": { - "fields": [ + "elements": [ { "key": "a", - "type": "FieldType", - "value": { - "name": "number", - "type": "NameExpression", + "meta": { + "quote": undefined, + }, + "optional": false, + "readonly": false, + "right": { + "type": "JsdocTypeName", + "value": "number", }, + "type": "JsdocTypeObjectField", }, { "key": "b", - "type": "FieldType", - "value": { - "name": "string", - "type": "NameExpression", + "meta": { + "quote": undefined, }, + "optional": false, + "readonly": false, + "right": { + "type": "JsdocTypeName", + "value": "string", + }, + "type": "JsdocTypeObjectField", }, ], - "type": "RecordType", + "meta": { + "separator": "comma", + }, + "type": "JsdocTypeObject", }, }, }, @@ -873,17 +875,21 @@ description", "description": null, "getTypeName": [Function], "type": { - "applications": [ + "elements": [ { - "name": "string", - "type": "NameExpression", + "type": "JsdocTypeName", + "value": "string", }, ], - "expression": { - "name": "Array", - "type": "NameExpression", + "left": { + "type": "JsdocTypeName", + "value": "Array", + }, + "meta": { + "brackets": "square", + "dot": false, }, - "type": "TypeApplication", + "type": "JsdocTypeGeneric", }, }, }, @@ -908,17 +914,20 @@ description", "description": null, "getTypeName": [Function], "type": { - "elements": [ - { - "name": "number", - "type": "NameExpression", - }, - { - "name": "boolean", - "type": "NameExpression", - }, - ], - "type": "UnionType", + "element": { + "elements": [ + { + "type": "JsdocTypeName", + "value": "number", + }, + { + "type": "JsdocTypeName", + "value": "boolean", + }, + ], + "type": "JsdocTypeUnion", + }, + "type": "JsdocTypeParenthesis", }, }, }, @@ -943,7 +952,7 @@ description", "description": null, "getTypeName": [Function], "type": { - "type": "AllLiteral", + "type": "JsdocTypeAny", }, }, }, @@ -968,8 +977,8 @@ description", "description": null, "getTypeName": [Function], "type": { - "name": "string", - "type": "NameExpression", + "type": "JsdocTypeName", + "value": "string", }, }, }, @@ -994,8 +1003,8 @@ description", "description": null, "getTypeName": [Function], "type": { - "name": "void", - "type": "NameExpression", + "type": "JsdocTypeName", + "value": "void", }, }, }, diff --git a/code/ui/.storybook/main.ts b/code/ui/.storybook/main.ts index 03dea1aea08d..72ec5445cb60 100644 --- a/code/ui/.storybook/main.ts +++ b/code/ui/.storybook/main.ts @@ -30,6 +30,10 @@ const allStories = [ directory: '../../addons/onboarding/src', titlePrefix: '@addons/onboarding', }, + { + directory: '../../addons/interactions/src', + titlePrefix: '@addons/interactions', + }, ]; /** diff --git a/code/yarn.lock b/code/yarn.lock index 0d2f161d0f94..6e6ecb7b69a6 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -501,7 +501,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-create-class-features-plugin@npm:^7.18.6, @babel/helper-create-class-features-plugin@npm:^7.21.0, @babel/helper-create-class-features-plugin@npm:^7.24.0, @babel/helper-create-class-features-plugin@npm:^7.24.1, @babel/helper-create-class-features-plugin@npm:^7.24.4": +"@babel/helper-create-class-features-plugin@npm:^7.18.6, @babel/helper-create-class-features-plugin@npm:^7.21.0, @babel/helper-create-class-features-plugin@npm:^7.24.1, @babel/helper-create-class-features-plugin@npm:^7.24.4": version: 7.24.4 resolution: "@babel/helper-create-class-features-plugin@npm:7.24.4" dependencies: @@ -520,6 +520,25 @@ __metadata: languageName: node linkType: hard +"@babel/helper-create-class-features-plugin@npm:^7.24.0": + version: 7.24.0 + resolution: "@babel/helper-create-class-features-plugin@npm:7.24.0" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.22.5" + "@babel/helper-environment-visitor": "npm:^7.22.20" + "@babel/helper-function-name": "npm:^7.23.0" + "@babel/helper-member-expression-to-functions": "npm:^7.23.0" + "@babel/helper-optimise-call-expression": "npm:^7.22.5" + "@babel/helper-replace-supers": "npm:^7.22.20" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.22.5" + "@babel/helper-split-export-declaration": "npm:^7.22.6" + semver: "npm:^6.3.1" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/341548496df202805489422a160bba75b111d994c64d788a397c35f01784632af48bf06023af8aa2fe72c2c254f8c885b4e0f7f3df5ef17a37370f2feaf80328 + languageName: node + linkType: hard + "@babel/helper-create-regexp-features-plugin@npm:^7.18.6, @babel/helper-create-regexp-features-plugin@npm:^7.22.15, @babel/helper-create-regexp-features-plugin@npm:^7.22.5": version: 7.22.15 resolution: "@babel/helper-create-regexp-features-plugin@npm:7.22.15" @@ -548,6 +567,21 @@ __metadata: languageName: node linkType: hard +"@babel/helper-define-polyfill-provider@npm:^0.6.0": + version: 0.6.0 + resolution: "@babel/helper-define-polyfill-provider@npm:0.6.0" + dependencies: + "@babel/helper-compilation-targets": "npm:^7.22.6" + "@babel/helper-plugin-utils": "npm:^7.22.5" + debug: "npm:^4.1.1" + lodash.debounce: "npm:^4.0.8" + resolve: "npm:^1.14.2" + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 10c0/bf6af52fadbbebc5bf71166b91eac4fc21431ec9b0d2a94063f3a3d900ed44aa1384ad23e920a85e7a657fcf3e80edb2eaaac9d902bd1e632f3b50c836b45c53 + languageName: node + linkType: hard + "@babel/helper-define-polyfill-provider@npm:^0.6.1, @babel/helper-define-polyfill-provider@npm:^0.6.2": version: 0.6.2 resolution: "@babel/helper-define-polyfill-provider@npm:0.6.2" @@ -589,7 +623,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-member-expression-to-functions@npm:^7.23.0": +"@babel/helper-member-expression-to-functions@npm:^7.22.15, @babel/helper-member-expression-to-functions@npm:^7.23.0": version: 7.23.0 resolution: "@babel/helper-member-expression-to-functions@npm:7.23.0" dependencies: @@ -598,7 +632,16 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.16.7, @babel/helper-module-imports@npm:^7.22.15, @babel/helper-module-imports@npm:^7.24.1, @babel/helper-module-imports@npm:^7.24.3, @babel/helper-module-imports@npm:^7.8.3": +"@babel/helper-module-imports@npm:^7.16.7, @babel/helper-module-imports@npm:^7.22.15, @babel/helper-module-imports@npm:^7.8.3": + version: 7.22.15 + resolution: "@babel/helper-module-imports@npm:7.22.15" + dependencies: + "@babel/types": "npm:^7.22.15" + checksum: 10c0/4e0d7fc36d02c1b8c8b3006dfbfeedf7a367d3334a04934255de5128115ea0bafdeb3e5736a2559917f0653e4e437400d54542da0468e08d3cbc86d3bbfa8f30 + languageName: node + linkType: hard + +"@babel/helper-module-imports@npm:^7.24.1, @babel/helper-module-imports@npm:^7.24.3": version: 7.24.3 resolution: "@babel/helper-module-imports@npm:7.24.3" dependencies: @@ -651,6 +694,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-replace-supers@npm:^7.22.20": + version: 7.22.20 + resolution: "@babel/helper-replace-supers@npm:7.22.20" + dependencies: + "@babel/helper-environment-visitor": "npm:^7.22.20" + "@babel/helper-member-expression-to-functions": "npm:^7.22.15" + "@babel/helper-optimise-call-expression": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/6b0858811ad46873817c90c805015d63300e003c5a85c147a17d9845fa2558a02047c3cc1f07767af59014b2dd0fa75b503e5bc36e917f360e9b67bb6f1e79f4 + languageName: node + linkType: hard + "@babel/helper-replace-supers@npm:^7.24.1": version: 7.24.1 resolution: "@babel/helper-replace-supers@npm:7.24.1" @@ -746,7 +802,16 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.11.5, @babel/parser@npm:^7.13.16, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.4, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.23.5, @babel/parser@npm:^7.23.6, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.24.0, @babel/parser@npm:^7.24.1, @babel/parser@npm:^7.24.4, @babel/parser@npm:^7.4.5, @babel/parser@npm:^7.6.0, @babel/parser@npm:^7.9.6": +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.11.5, @babel/parser@npm:^7.13.16, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.4, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.23.5, @babel/parser@npm:^7.23.6, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.24.0, @babel/parser@npm:^7.4.5, @babel/parser@npm:^7.6.0, @babel/parser@npm:^7.9.6": + version: 7.24.0 + resolution: "@babel/parser@npm:7.24.0" + bin: + parser: ./bin/babel-parser.js + checksum: 10c0/77593d0b9de9906823c4d653bb6cda1c7593837598516330f655f70cba6224a37def7dbe5b4dad0038482d407d8d209eb8be5f48ca9a13357d769f829c5adb8e + languageName: node + linkType: hard + +"@babel/parser@npm:^7.24.1, @babel/parser@npm:^7.24.4": version: 7.24.4 resolution: "@babel/parser@npm:7.24.4" bin: @@ -1218,7 +1283,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-block-scoping@npm:^7.23.4, @babel/plugin-transform-block-scoping@npm:^7.24.4, @babel/plugin-transform-block-scoping@npm:^7.8.3": +"@babel/plugin-transform-block-scoping@npm:^7.23.4, @babel/plugin-transform-block-scoping@npm:^7.8.3": + version: 7.23.4 + resolution: "@babel/plugin-transform-block-scoping@npm:7.23.4" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/83006804dddf980ab1bcd6d67bc381e24b58c776507c34f990468f820d0da71dba3697355ca4856532fa2eeb2a1e3e73c780f03760b5507a511cbedb0308e276 + languageName: node + linkType: hard + +"@babel/plugin-transform-block-scoping@npm:^7.24.4": version: 7.24.4 resolution: "@babel/plugin-transform-block-scoping@npm:7.24.4" dependencies: @@ -6002,6 +6078,7 @@ __metadata: telejson: "npm:^7.2.0" tiny-invariant: "npm:^1.3.1" ts-dedent: "npm:^2.0.0" + tsconfig-paths: "npm:^4.2.0" typescript: "npm:^5.3.2" util: "npm:^0.12.4" util-deprecate: "npm:^1.0.2" @@ -6089,10 +6166,9 @@ __metadata: "@storybook/core-events": "workspace:*" "@storybook/preview-api": "workspace:*" "@storybook/types": "workspace:*" - "@types/doctrine": "npm:^0.0.3" - assert: "npm:^2.1.0" babel-plugin-react-docgen: "npm:4.2.1" - doctrine: "npm:^3.0.0" + comment-parser: "npm:^1.4.1" + jsdoc-type-pratt-parser: "npm:^4.0.0" lodash: "npm:^4.17.21" require-from-string: "npm:^2.0.2" typescript: "npm:^5.3.2" @@ -7761,13 +7837,6 @@ __metadata: languageName: node linkType: hard -"@types/doctrine@npm:^0.0.3": - version: 0.0.3 - resolution: "@types/doctrine@npm:0.0.3" - checksum: 10c0/566dcdc988c97ff01d14493ceb2223643347f07cf0a88c86cd7cb7c2821cfc837fd39295e6809a29614fdfdc6c4e981408155ca909b2e5da5d947af939b6c966 - languageName: node - linkType: hard - "@types/doctrine@npm:^0.0.9": version: 0.0.9 resolution: "@types/doctrine@npm:0.0.9" @@ -10241,7 +10310,7 @@ __metadata: languageName: node linkType: hard -"assert@npm:^2.0.0, assert@npm:^2.1.0": +"assert@npm:^2.0.0": version: 2.1.0 resolution: "assert@npm:2.1.0" dependencies: @@ -10576,7 +10645,7 @@ __metadata: languageName: node linkType: hard -"babel-plugin-polyfill-corejs2@npm:^0.4.10, babel-plugin-polyfill-corejs2@npm:^0.4.8": +"babel-plugin-polyfill-corejs2@npm:^0.4.10": version: 0.4.11 resolution: "babel-plugin-polyfill-corejs2@npm:0.4.11" dependencies: @@ -10589,6 +10658,19 @@ __metadata: languageName: node linkType: hard +"babel-plugin-polyfill-corejs2@npm:^0.4.8": + version: 0.4.9 + resolution: "babel-plugin-polyfill-corejs2@npm:0.4.9" + dependencies: + "@babel/compat-data": "npm:^7.22.6" + "@babel/helper-define-polyfill-provider": "npm:^0.6.0" + semver: "npm:^6.3.1" + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 10c0/2cd47af763eb40aa41f1d6d9cbf1bdd217ff6c28f614b057c0328ee42a4d82cbcdcbc7d081d93e2a2d80446c899f25c3ebec048a63d260ef65a0a364134f71cd + languageName: node + linkType: hard + "babel-plugin-polyfill-corejs3@npm:^0.10.1, babel-plugin-polyfill-corejs3@npm:^0.10.4": version: 0.10.4 resolution: "babel-plugin-polyfill-corejs3@npm:0.10.4" @@ -12184,6 +12266,13 @@ __metadata: languageName: node linkType: hard +"comment-parser@npm:^1.4.1": + version: 1.4.1 + resolution: "comment-parser@npm:1.4.1" + checksum: 10c0/d6c4be3f5be058f98b24f2d557f745d8fe1cc9eb75bebbdccabd404a0e1ed41563171b16285f593011f8b6a5ec81f564fb1f2121418ac5cbf0f49255bf0840dd + languageName: node + linkType: hard + "common-path-prefix@npm:^3.0.0": version: 3.0.0 resolution: "common-path-prefix@npm:3.0.0" @@ -18506,6 +18595,13 @@ __metadata: languageName: node linkType: hard +"jsdoc-type-pratt-parser@npm:^4.0.0": + version: 4.0.0 + resolution: "jsdoc-type-pratt-parser@npm:4.0.0" + checksum: 10c0/b23ef7bbbe2f56d72630d1c5a233dc9fecaff399063d373c57bef136908c1b05e723dac107177303c03ccf8d75aa51507510b282aa567600477479c5ea0c36d1 + languageName: node + linkType: hard + "jsdom@npm:^23.0.1": version: 23.0.1 resolution: "jsdom@npm:23.0.1" @@ -27898,23 +27994,23 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.0.3, typescript@npm:^5.3.2, typescript@npm:^5.4.3": - version: 5.4.3 - resolution: "typescript@npm:5.4.3" +"typescript@npm:^5.0.3, typescript@npm:^5.3.2, typescript@npm:~5.3.2": + version: 5.3.3 + resolution: "typescript@npm:5.3.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10c0/22443a8760c3668e256c0b34b6b45c359ef6cecc10c42558806177a7d500ab1a7d7aac1f976d712e26989ddf6731d2fbdd3212b7c73290a45127c1c43ba2005a + checksum: 10c0/e33cef99d82573624fc0f854a2980322714986bc35b9cb4d1ce736ed182aeab78e2cb32b385efa493b2a976ef52c53e20d6c6918312353a91850e2b76f1ea44f languageName: node linkType: hard -"typescript@npm:~5.3.2": - version: 5.3.3 - resolution: "typescript@npm:5.3.3" +"typescript@npm:^5.4.3": + version: 5.4.3 + resolution: "typescript@npm:5.4.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10c0/e33cef99d82573624fc0f854a2980322714986bc35b9cb4d1ce736ed182aeab78e2cb32b385efa493b2a976ef52c53e20d6c6918312353a91850e2b76f1ea44f + checksum: 10c0/22443a8760c3668e256c0b34b6b45c359ef6cecc10c42558806177a7d500ab1a7d7aac1f976d712e26989ddf6731d2fbdd3212b7c73290a45127c1c43ba2005a languageName: node linkType: hard @@ -27928,23 +28024,23 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^5.0.3#optional!builtin, typescript@patch:typescript@npm%3A^5.3.2#optional!builtin, typescript@patch:typescript@npm%3A^5.4.3#optional!builtin": - version: 5.4.3 - resolution: "typescript@patch:typescript@npm%3A5.4.3#optional!builtin::version=5.4.3&hash=5adc0c" +"typescript@patch:typescript@npm%3A^5.0.3#optional!builtin, typescript@patch:typescript@npm%3A^5.3.2#optional!builtin, typescript@patch:typescript@npm%3A~5.3.2#optional!builtin": + version: 5.3.3 + resolution: "typescript@patch:typescript@npm%3A5.3.3#optional!builtin::version=5.3.3&hash=e012d7" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10c0/6e51f8b7e6ec55b897b9e56b67e864fe8f44e30f4a14357aad5dc0f7432db2f01efc0522df0b6c36d361c51f2dc3dcac5c832efd96a404cfabf884e915d38828 + checksum: 10c0/1d0a5f4ce496c42caa9a30e659c467c5686eae15d54b027ee7866744952547f1be1262f2d40de911618c242b510029d51d43ff605dba8fb740ec85ca2d3f9500 languageName: node linkType: hard -"typescript@patch:typescript@npm%3A~5.3.2#optional!builtin": - version: 5.3.3 - resolution: "typescript@patch:typescript@npm%3A5.3.3#optional!builtin::version=5.3.3&hash=e012d7" +"typescript@patch:typescript@npm%3A^5.4.3#optional!builtin": + version: 5.4.3 + resolution: "typescript@patch:typescript@npm%3A5.4.3#optional!builtin::version=5.4.3&hash=5adc0c" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10c0/1d0a5f4ce496c42caa9a30e659c467c5686eae15d54b027ee7866744952547f1be1262f2d40de911618c242b510029d51d43ff605dba8fb740ec85ca2d3f9500 + checksum: 10c0/6e51f8b7e6ec55b897b9e56b67e864fe8f44e30f4a14357aad5dc0f7432db2f01efc0522df0b6c36d361c51f2dc3dcac5c832efd96a404cfabf884e915d38828 languageName: node linkType: hard diff --git a/docs/api/main-config-indexers.md b/docs/api/main-config-indexers.md index 79e7fa2f2651..a2b17648ed40 100644 --- a/docs/api/main-config-indexers.md +++ b/docs/api/main-config-indexers.md @@ -97,6 +97,7 @@ Type: exportName: string; importPath: string; type: 'story'; + rawComponentPath?: string; metaId?: string; name?: string; tags?: string[]; @@ -133,6 +134,12 @@ Type: `'story'` The type of entry. +##### `rawComponentPath` + +Type: `string` + +The raw path/package of the file that provides `meta.component`, if one exists. + ##### `metaId` Type: `string` diff --git a/docs/configure/telemetry.md b/docs/configure/telemetry.md index 291fa53e18d3..bd86f613ff0a 100644 --- a/docs/configure/telemetry.md +++ b/docs/configure/telemetry.md @@ -71,7 +71,6 @@ Will generate the following output: "pageStoryCount": 0, "playStoryCount": 0, "autodocsCount": 0, - "storiesMdxCount": 0, "mdxCount": 0, "exampleStoryCount": 8, "exampleDocsCount": 3, diff --git a/docs/configure/theming.md b/docs/configure/theming.md index 5ac07a9b5a61..f1e745646594 100644 --- a/docs/configure/theming.md +++ b/docs/configure/theming.md @@ -196,7 +196,7 @@ Here's how you might insert a custom `` block: Some addons require specific theme variables that a Storybook user must add. If you share your theme with the community, make sure to support the official API and other popular addons, so your users have a consistent experience. -For example, the popular Actions addon uses [react-inspector](https://github.com/xyc/react-inspector/blob/master/src/styles/themes/chromeLight.js), which has themes of its own. Supply additional theme variables to style it like so: +For example, the popular Actions addon uses [react-inspector](https://github.com/storybookjs/react-inspector/blob/master/src/styles/themes/chromeLight.tsx), which has themes of its own. Supply additional theme variables to style it like so: diff --git a/docs/versions/next.json b/docs/versions/next.json index 26523a287829..73926469e917 100644 --- a/docs/versions/next.json +++ b/docs/versions/next.json @@ -1 +1 @@ -{"version":"8.2.0-alpha.6","info":{"plain":"- Addon-actions: Only log spies with names - [#28091](https://github.com/storybookjs/storybook/pull/28091), thanks @kasperpeulen!\n- Build: Change require/import order, so that import has higher prio if both are specified - [#27730](https://github.com/storybookjs/storybook/pull/27730), thanks @kasperpeulen!\n- CLI: Only log the UpgradeStorybookToSameVersionError but continue the upgrade as normal - [#27217](https://github.com/storybookjs/storybook/pull/27217), thanks @kasperpeulen!\n- Core: Replace ip function with a small helper function to address security concerns - [#27529](https://github.com/storybookjs/storybook/pull/27529), thanks @tony19!\n- Portable Stories: Add tags to composed story - [#27708](https://github.com/storybookjs/storybook/pull/27708), thanks @yannbf!\n- Test: Upgrade deps of @storybook/test - [#27862](https://github.com/storybookjs/storybook/pull/27862), thanks @kasperpeulen!\n- Vite: Fix stats-plugin to normalize file names with posix paths - [#27218](https://github.com/storybookjs/storybook/pull/27218), thanks @AlexAtVista!"}} +{"version":"8.2.0-alpha.7","info":{"plain":"- Angular: Allow outputPath object syntax - [#28144](https://github.com/storybookjs/storybook/pull/28144), thanks @valentinpalkovic!\n- Angular: Introduce preserveSymlink builder option - [#28145](https://github.com/storybookjs/storybook/pull/28145), thanks @valentinpalkovic!\n- CLI: Fix typo in React Docgen migration - [#27536](https://github.com/storybookjs/storybook/pull/27536), thanks @jonniebigodes!\n- CSF: Automatically extract componentPath - [#24396](https://github.com/storybookjs/storybook/pull/24396), thanks @shilman!\n- Core: Remove more `.stories.mdx` handling - [#25973](https://github.com/storybookjs/storybook/pull/25973), thanks @JReinhold!\n- Docs-tools: Replace `doctrine` with `jsdoc-type-pratt-parser` - [#26305](https://github.com/storybookjs/storybook/pull/26305), thanks @43081j!\n- Test: Display toHaveBeenCalledWith expected / received values on failure - [#28088](https://github.com/storybookjs/storybook/pull/28088), thanks @kasperpeulen!"}} diff --git a/scripts/task.ts b/scripts/task.ts index 1247cc89351a..2d1225ddbe6c 100644 --- a/scripts/task.ts +++ b/scripts/task.ts @@ -157,6 +157,11 @@ export const options = createOptions({ inverse: true, promptType: false, }, + prod: { + type: 'boolean', + description: 'Build code for production', + promptType: false, + }, dryRun: { type: 'boolean', description: "Don't execute commands, just list them (dry run)?", diff --git a/scripts/tasks/compile.ts b/scripts/tasks/compile.ts index af220b9f27a4..0275a363e222 100644 --- a/scripts/tasks/compile.ts +++ b/scripts/tasks/compile.ts @@ -33,9 +33,9 @@ export const compile: Task = { return false; } }, - async run({ codeDir }, { link, dryRun, debug }) { + async run({ codeDir }, { link, dryRun, debug, prod }) { return exec( - link ? linkCommand : noLinkCommand, + link && !prod ? linkCommand : noLinkCommand, { cwd: codeDir }, { startMessage: '🥾 Bootstrapping', diff --git a/scripts/tasks/sandbox-parts.ts b/scripts/tasks/sandbox-parts.ts index b80c1d5e734b..8bc2566a21a7 100644 --- a/scripts/tasks/sandbox-parts.ts +++ b/scripts/tasks/sandbox-parts.ts @@ -620,18 +620,24 @@ export async function setImportMap(cwd: string) { await writeJson(join(cwd, 'package.json'), packageJson, { spaces: 2 }); } -/** - * Sets compodoc option in angular.json projects to false. We have to generate compodoc - * manually to avoid symlink issues related to the template-stories folder. - * In a second step a docs:json script is placed into the package.json to generate the - * Compodoc documentation.json, which respects symlinks - * */ async function prepareAngularSandbox(cwd: string, templateName: string) { const angularJson = await readJson(join(cwd, 'angular.json')); Object.keys(angularJson.projects).forEach((projectName: string) => { + /** + * Sets compodoc option in angular.json projects to false. We have to generate compodoc + * manually to avoid symlink issues related to the template-stories folder. + * In a second step a docs:json script is placed into the package.json to generate the + * Compodoc documentation.json, which respects symlinks + */ angularJson.projects[projectName].architect.storybook.options.compodoc = false; angularJson.projects[projectName].architect['build-storybook'].options.compodoc = false; + /** + * Sets preserveSymlinks option in angular.json projects to true. This is necessary to + * respect symlinks so that Angular doesn't complain about wrong types in @storybook/* packages + */ + angularJson.projects[projectName].architect.storybook.options.preserveSymlinks = true; + angularJson.projects[projectName].architect['build-storybook'].options.preserveSymlinks = true; }); await writeJson(join(cwd, 'angular.json'), angularJson, { spaces: 2 });