diff --git a/.circleci/config.yml b/.circleci/config.yml index b23edd89ef69..ad07b40429a4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -596,7 +596,7 @@ jobs: at: . - run: name: Install dependencies - command: yarn install + command: yarn install --no-immutable working_directory: test-storybooks/portable-stories-kitchen-sink/<< parameters.directory >> - run: name: Run Jest tests diff --git a/CHANGELOG.md b/CHANGELOG.md index 61ad5d1f5e1e..dc26d8f08de2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 8.0.7 + +- CLI: Add Visual Tests addon install auto-migration when upgrading to 8.0.x - [#26766](https://github.com/storybookjs/storybook/pull/26766), thanks @ndelangen! +- Next.js: Move sharp into optional deps - [#26787](https://github.com/storybookjs/storybook/pull/26787), thanks @shuta13! +- Vue: Disable controls for events, slots, and expose - [#26751](https://github.com/storybookjs/storybook/pull/26751), thanks @shilman! +- Webpack: Bump webpack-dev-middleware to patch high security issue - [#26655](https://github.com/storybookjs/storybook/pull/26655), thanks @jwilliams-met! + ## 8.0.6 - CLI: Add --config-dir flag to migrate command - [#26721](https://github.com/storybookjs/storybook/pull/26721), thanks @yannbf! diff --git a/CHANGELOG.prerelease.md b/CHANGELOG.prerelease.md index ada76261b3cb..5d6baeac2136 100644 --- a/CHANGELOG.prerelease.md +++ b/CHANGELOG.prerelease.md @@ -1,3 +1,12 @@ +## 8.1.0-alpha.7 + +- CLI: Add --config-dir flag to add command - [#26771](https://github.com/storybookjs/storybook/pull/26771), thanks @eric-blue! +- CLI: Add Visual Tests addon install auto-migration when upgrading to 8.0.x - [#26766](https://github.com/storybookjs/storybook/pull/26766), thanks @ndelangen! +- Controls: Add Channels API to search for files in the project root - [#26726](https://github.com/storybookjs/storybook/pull/26726), thanks @valentinpalkovic! +- Test: Make spies reactive so that they can be logged by addon-actions - [#26740](https://github.com/storybookjs/storybook/pull/26740), thanks @kasperpeulen! +- Vue: Disable controls for events, slots, and expose - [#26751](https://github.com/storybookjs/storybook/pull/26751), thanks @shilman! +- Webpack: Bump webpack-dev-middleware to patch high security issue - [#26655](https://github.com/storybookjs/storybook/pull/26655), thanks @jwilliams-met! + ## 8.1.0-alpha.6 - CLI: Add --config-dir flag to migrate command - [#26721](https://github.com/storybookjs/storybook/pull/26721), thanks @yannbf! diff --git a/MIGRATION.md b/MIGRATION.md index 345f0ff2baf7..eecaf94950a2 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -5477,7 +5477,7 @@ Currently there is no recommended way of accessing the component options of a st ## From version 4.0.x to 4.1.x -There are are a few migrations you should be aware of in 4.1, including one unintentionally breaking change for advanced addon usage. +There are a few migrations you should be aware of in 4.1, including one unintentionally breaking change for advanced addon usage. ### Private addon config diff --git a/code/.eslintignore b/code/.eslintignore index 3319047075e6..5ced53c96672 100644 --- a/code/.eslintignore +++ b/code/.eslintignore @@ -17,4 +17,5 @@ ember-output !.eslintrc.js !.eslintrc-markdown.js !.storybook -lib/core-common/templates/base-preview-head.html \ No newline at end of file +lib/core-common/templates/base-preview-head.html +lib/core-server/src/utils/__search-files-tests__ \ No newline at end of file diff --git a/code/addons/a11y/package.json b/code/addons/a11y/package.json index 496558d8250d..b4ead0743136 100644 --- a/code/addons/a11y/package.json +++ b/code/addons/a11y/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-a11y", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Test component compliance with web accessibility standards", "keywords": [ "a11y", diff --git a/code/addons/actions/package.json b/code/addons/actions/package.json index 723ad8083ace..ece8a2ec4e3b 100644 --- a/code/addons/actions/package.json +++ b/code/addons/actions/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-actions", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Get UI feedback when an action is performed on an interactive element", "keywords": [ "storybook", diff --git a/code/addons/actions/src/loaders.ts b/code/addons/actions/src/loaders.ts index 3acfa9795eef..d49a048da231 100644 --- a/code/addons/actions/src/loaders.ts +++ b/code/addons/actions/src/loaders.ts @@ -1,36 +1,26 @@ /* eslint-disable no-underscore-dangle */ import type { LoaderFunction } from '@storybook/types'; +import { global } from '@storybook/global'; +import type { onMockCall as onMockCallType } from '@storybook/test'; import { action } from './runtime'; -export const tinySpyInternalState = Symbol.for('tinyspy:spy'); +let subscribed = false; -const attachActionsToFunctionMocks: LoaderFunction = (context) => { +const logActionsWhenMockCalled: LoaderFunction = (context) => { const { - args, parameters: { actions }, } = context; if (actions?.disable) return; - Object.entries(args) - .filter( - ([, value]) => - typeof value === 'function' && '_isMockFunction' in value && value._isMockFunction - ) - .forEach(([key, value]) => { - // See this discussion for context: - // https://github.com/vitest-dev/vitest/pull/5352 - const previous = - value.getMockImplementation() ?? - (tinySpyInternalState in value ? value[tinySpyInternalState]?.getOriginal() : undefined); - if (previous?._actionAttached !== true && previous?.isAction !== true) { - const implementation = (...params: unknown[]) => { - action(key)(...params); - return previous?.(...params); - }; - implementation._actionAttached = true; - value.mockImplementation(implementation); - } - }); + if ( + !subscribed && + '__STORYBOOK_TEST_ON_MOCK_CALL__' in global && + typeof global.__STORYBOOK_TEST_ON_MOCK_CALL__ === 'function' + ) { + const onMockCall = global.__STORYBOOK_TEST_ON_MOCK_CALL__ as typeof onMockCallType; + onMockCall((mock, args) => action(mock.getMockName())(args)); + subscribed = true; + } }; -export const loaders: LoaderFunction[] = [attachActionsToFunctionMocks]; +export const loaders: LoaderFunction[] = [logActionsWhenMockCalled]; diff --git a/code/addons/actions/template/stories/spies.stories.ts b/code/addons/actions/template/stories/spies.stories.ts new file mode 100644 index 000000000000..824494bda1c9 --- /dev/null +++ b/code/addons/actions/template/stories/spies.stories.ts @@ -0,0 +1,24 @@ +import { global as globalThis } from '@storybook/global'; +import { spyOn } from '@storybook/test'; + +export default { + component: globalThis.Components.Button, + loaders() { + spyOn(console, 'log').mockName('console.log'); + }, + args: { + label: 'Button', + }, + parameters: { + chromatic: { disable: true }, + }, +}; + +export const ShowSpyOnInActions = { + args: { + onClick: () => { + console.log('first'); + console.log('second'); + }, + }, +}; diff --git a/code/addons/backgrounds/package.json b/code/addons/backgrounds/package.json index 7bb6d2ccf554..6a28feca7160 100644 --- a/code/addons/backgrounds/package.json +++ b/code/addons/backgrounds/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-backgrounds", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Switch backgrounds to view components in different settings", "keywords": [ "addon", diff --git a/code/addons/controls/package.json b/code/addons/controls/package.json index d13ea6560843..2a4619bcdf8a 100644 --- a/code/addons/controls/package.json +++ b/code/addons/controls/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-controls", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Interact with component inputs dynamically in the Storybook UI", "keywords": [ "addon", diff --git a/code/addons/docs/package.json b/code/addons/docs/package.json index 2e8cc475788c..77ccd00d086e 100644 --- a/code/addons/docs/package.json +++ b/code/addons/docs/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-docs", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Document component usage and properties in Markdown", "keywords": [ "addon", diff --git a/code/addons/essentials/package.json b/code/addons/essentials/package.json index 2d22b284ac3d..33112d9708df 100644 --- a/code/addons/essentials/package.json +++ b/code/addons/essentials/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-essentials", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Curated addons to bring out the best of Storybook", "keywords": [ "addon", diff --git a/code/addons/gfm/README.md b/code/addons/gfm/README.md index 038d46c44d40..8245b7975b2b 100644 --- a/code/addons/gfm/README.md +++ b/code/addons/gfm/README.md @@ -6,6 +6,6 @@ The "@storybook/addon-mdx-gfm" addon is meant as a migration assistant for Story > This addon will likely be removed in a future version. It's recommended you read this document and follow its instructions instead: -https://storybook.js.org/docs/react/writing-docs/mdx#lack-of-github-flavored-markdown-gfm +https://storybook.js.org/docs/writing-docs/mdx#markdown-tables-arent-rendering-correctly Once you've made the necessary changes, you can remove the addon from your package.json and storybook config. diff --git a/code/addons/gfm/package.json b/code/addons/gfm/package.json index 036723764399..5d2b909e149a 100644 --- a/code/addons/gfm/package.json +++ b/code/addons/gfm/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-mdx-gfm", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "GitHub Flavored Markdown in Storybook", "keywords": [ "addon", diff --git a/code/addons/highlight/package.json b/code/addons/highlight/package.json index c49692070e01..261aaaf561fa 100644 --- a/code/addons/highlight/package.json +++ b/code/addons/highlight/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-highlight", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Highlight DOM nodes within your stories", "keywords": [ "storybook-addons", diff --git a/code/addons/interactions/package.json b/code/addons/interactions/package.json index a4b2c834c006..467cac882d2b 100644 --- a/code/addons/interactions/package.json +++ b/code/addons/interactions/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-interactions", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Automate, test and debug user interactions", "keywords": [ "storybook-addons", diff --git a/code/addons/interactions/src/preview.test.ts b/code/addons/interactions/src/preview.test.ts deleted file mode 100644 index 5cfec9d19d03..000000000000 --- a/code/addons/interactions/src/preview.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { describe, expect, test } from 'vitest'; -import { fn, isMockFunction } from '@storybook/test'; -import { action } from '@storybook/addon-actions'; - -import { traverseArgs } from './preview'; - -describe('traverseArgs', () => { - const args = { - deep: { - deeper: { - fnKey: fn(), - actionKey: action('name'), - }, - }, - arg2: Object.freeze({ frozen: true }), - }; - - expect(args.deep.deeper.fnKey.getMockName()).toEqual('spy'); - - const traversed = traverseArgs(args) as typeof args; - - test('The same structure is maintained', () => - expect(traversed).toEqual({ - deep: { - deeper: { - fnKey: args.deep.deeper.fnKey, - actionKey: args.deep.deeper.actionKey, - }, - }, - // We don't mutate frozen objects, but we do insert them back in the tree - arg2: args.arg2, - })); - - test('The mock name is mutated to be the arg key', () => - expect(traversed.deep.deeper.fnKey.getMockName()).toEqual('fnKey')); - - const actionFn = traversed.deep.deeper.actionKey; - - test('Actions are wrapped in a spy', () => expect(isMockFunction(actionFn)).toBeTruthy()); - test('The spy of the action is also matching the arg key ', () => - expect(isMockFunction(actionFn) && actionFn.getMockName()).toEqual('actionKey')); -}); diff --git a/code/addons/interactions/src/preview.ts b/code/addons/interactions/src/preview.ts index 6e70166629aa..5ef2520b6b71 100644 --- a/code/addons/interactions/src/preview.ts +++ b/code/addons/interactions/src/preview.ts @@ -1,11 +1,4 @@ -import type { - ArgsEnhancer, - PlayFunction, - PlayFunctionContext, - Renderer, - StepLabel, -} from '@storybook/types'; -import { fn, isMockFunction } from '@storybook/test'; +import type { PlayFunction, PlayFunctionContext, StepLabel } from '@storybook/types'; import { instrument } from '@storybook/instrumenter'; export const { step: runStep } = instrument( @@ -16,50 +9,6 @@ export const { step: runStep } = instrument( { intercept: true } ); -export const traverseArgs = (value: unknown, depth = 0, key?: string): unknown => { - // Make sure to not get in infinite loops with self referencing args - if (depth > 5) return value; - if (value == null) return value; - if (isMockFunction(value)) { - // Makes sure we get the arg name in the interactions panel - if (key) value.mockName(key); - return value; - } - - // wrap explicit actions in a spy - if ( - typeof value === 'function' && - 'isAction' in value && - value.isAction && - !('implicit' in value && value.implicit) - ) { - const mock = fn(value as any); - if (key) mock.mockName(key); - return mock; - } - - if (Array.isArray(value)) { - depth++; - return value.map((item) => traverseArgs(item, depth)); - } - - if (typeof value === 'object' && value.constructor === Object) { - depth++; - for (const [k, v] of Object.entries(value)) { - if (Object.getOwnPropertyDescriptor(value, k).writable) { - // We have to mutate the original object for this to survive HMR. - (value as Record)[k] = traverseArgs(v, depth, k); - } - } - return value; - } - return value; -}; - -const wrapActionsInSpyFns: ArgsEnhancer = ({ initialArgs }) => traverseArgs(initialArgs); - -export const argsEnhancers = [wrapActionsInSpyFns]; - export const parameters = { throwPlayFunctionExceptions: false, }; diff --git a/code/addons/jest/package.json b/code/addons/jest/package.json index 29a9e24fd4a4..02d3455461a0 100644 --- a/code/addons/jest/package.json +++ b/code/addons/jest/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-jest", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "React storybook addon that show component jest report", "keywords": [ "addon", diff --git a/code/addons/links/package.json b/code/addons/links/package.json index c9ae4616934f..a540f6fa95d0 100644 --- a/code/addons/links/package.json +++ b/code/addons/links/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-links", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Link stories together to build demos and prototypes with your UI components", "keywords": [ "addon", diff --git a/code/addons/measure/package.json b/code/addons/measure/package.json index a194e99010cd..f5146ede8621 100644 --- a/code/addons/measure/package.json +++ b/code/addons/measure/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-measure", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Inspect layouts by visualizing the box model", "keywords": [ "storybook-addons", diff --git a/code/addons/onboarding/package.json b/code/addons/onboarding/package.json index 122a335e744f..8919cabdb5b6 100644 --- a/code/addons/onboarding/package.json +++ b/code/addons/onboarding/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-onboarding", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook Addon Onboarding - Introduces a new onboarding experience", "keywords": [ "storybook-addons", diff --git a/code/addons/outline/package.json b/code/addons/outline/package.json index 4a9edc796775..1a40a4d6c6c8 100644 --- a/code/addons/outline/package.json +++ b/code/addons/outline/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-outline", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Outline all elements with CSS to help with layout placement and alignment", "keywords": [ "storybook-addons", diff --git a/code/addons/storysource/package.json b/code/addons/storysource/package.json index cb04afaea3f8..e116bf93a52c 100644 --- a/code/addons/storysource/package.json +++ b/code/addons/storysource/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-storysource", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "View a story’s source code to see how it works and paste into your app", "keywords": [ "addon", diff --git a/code/addons/themes/package.json b/code/addons/themes/package.json index 34a98c77b33f..1203272b30db 100644 --- a/code/addons/themes/package.json +++ b/code/addons/themes/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-themes", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Switch between multiple themes for you components in Storybook", "keywords": [ "css", diff --git a/code/addons/toolbars/package.json b/code/addons/toolbars/package.json index 7e95a9f16d82..003fe05a7602 100644 --- a/code/addons/toolbars/package.json +++ b/code/addons/toolbars/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-toolbars", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Create your own toolbar items that control story rendering", "keywords": [ "addon", diff --git a/code/addons/viewport/README.md b/code/addons/viewport/README.md index 3766d555f8b3..7975688745fd 100644 --- a/code/addons/viewport/README.md +++ b/code/addons/viewport/README.md @@ -22,7 +22,7 @@ export default { }; ``` -You should now be able to see the viewport addon icon in the the toolbar at the top of the screen. +You should now be able to see the viewport addon icon in the toolbar at the top of the screen. ## Usage diff --git a/code/addons/viewport/package.json b/code/addons/viewport/package.json index f22e8be1d7c2..b1478832b2e7 100644 --- a/code/addons/viewport/package.json +++ b/code/addons/viewport/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-viewport", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Build responsive components by adjusting Storybook’s viewport size and orientation", "keywords": [ "addon", diff --git a/code/builders/builder-manager/package.json b/code/builders/builder-manager/package.json index 578714b83aee..432ee9cf3358 100644 --- a/code/builders/builder-manager/package.json +++ b/code/builders/builder-manager/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/builder-manager", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook manager builder", "keywords": [ "storybook" diff --git a/code/builders/builder-vite/package.json b/code/builders/builder-vite/package.json index 19e1b9e51b24..6422f5c72791 100644 --- a/code/builders/builder-vite/package.json +++ b/code/builders/builder-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/builder-vite", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "A plugin to run and build Storybooks with Vite", "homepage": "https://github.com/storybookjs/storybook/tree/next/code/builders/builder-vite/#readme", "bugs": { diff --git a/code/builders/builder-webpack5/package.json b/code/builders/builder-webpack5/package.json index 22678cfd37e2..4e0d3caf9cfc 100644 --- a/code/builders/builder-webpack5/package.json +++ b/code/builders/builder-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/builder-webpack5", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook framework-agnostic API", "keywords": [ "storybook" @@ -94,7 +94,7 @@ "util": "^0.12.4", "util-deprecate": "^1.0.2", "webpack": "5", - "webpack-dev-middleware": "^6.1.1", + "webpack-dev-middleware": "^6.1.2", "webpack-hot-middleware": "^2.25.1", "webpack-virtual-modules": "^0.5.0" }, diff --git a/code/e2e-tests/addon-actions.spec.ts b/code/e2e-tests/addon-actions.spec.ts index 3b93599e81ad..a67aa8599ce9 100644 --- a/code/e2e-tests/addon-actions.spec.ts +++ b/code/e2e-tests/addon-actions.spec.ts @@ -26,4 +26,26 @@ test.describe('addon-actions', () => { }); await expect(logItem).toBeVisible(); }); + + test('should show spies', async ({ page }) => { + test.skip( + templateName.includes('svelte') && templateName.includes('prerelease'), + 'Svelte 5 prerelase does not support automatic actions with our current example components yet' + ); + await page.goto(storybookUrl); + const sbPage = new SbPage(page); + sbPage.waitUntilLoaded(); + + await sbPage.navigateToStory('addons/actions/spies', 'show-spy-on-in-actions'); + + const root = sbPage.previewRoot(); + const button = root.locator('button', { hasText: 'Button' }); + await button.click(); + + await sbPage.viewAddonPanel('Actions'); + const logItem = await page.locator('#storybook-panel-root #panel-tab-content', { + hasText: 'console.log', + }); + await expect(logItem).toBeVisible(); + }); }); diff --git a/code/frameworks/angular/package.json b/code/frameworks/angular/package.json index 343562c86f33..6e6317037950 100644 --- a/code/frameworks/angular/package.json +++ b/code/frameworks/angular/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/angular", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for Angular: Develop Angular components in isolation with hot reloading.", "keywords": [ "storybook", diff --git a/code/frameworks/ember/package.json b/code/frameworks/ember/package.json index 787a93e9bea4..d1f6b92bfa41 100644 --- a/code/frameworks/ember/package.json +++ b/code/frameworks/ember/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/ember", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for Ember: Develop Ember Component in isolation with Hot Reloading.", "homepage": "https://github.com/storybookjs/storybook/tree/next/code/frameworks/ember", "bugs": { diff --git a/code/frameworks/html-vite/package.json b/code/frameworks/html-vite/package.json index 626332c9a48c..6fd46099d127 100644 --- a/code/frameworks/html-vite/package.json +++ b/code/frameworks/html-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/html-vite", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for HTML and Vite: Develop HTML in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/html-webpack5/package.json b/code/frameworks/html-webpack5/package.json index 194dbad864d0..6582c99aac3a 100644 --- a/code/frameworks/html-webpack5/package.json +++ b/code/frameworks/html-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/html-webpack5", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for HTML: View HTML snippets in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/nextjs/package.json b/code/frameworks/nextjs/package.json index b918ba86c2a0..608df58e0d3d 100644 --- a/code/frameworks/nextjs/package.json +++ b/code/frameworks/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/nextjs", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for Next.js", "keywords": [ "storybook", @@ -114,7 +114,6 @@ "resolve-url-loader": "^5.0.0", "sass-loader": "^12.4.0", "semver": "^7.3.5", - "sharp": "^0.32.6", "style-loader": "^3.3.1", "styled-jsx": "5.1.1", "ts-dedent": "^2.0.0", @@ -132,6 +131,9 @@ "typescript": "^5.3.2", "webpack": "^5.65.0" }, + "optionalDependencies": { + "sharp": "^0.33.3" + }, "peerDependencies": { "next": "^13.5.0 || ^14.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0", diff --git a/code/frameworks/nextjs/src/next-image-loader-stub.ts b/code/frameworks/nextjs/src/next-image-loader-stub.ts index 6f69e8e274d3..dbc65b8fde63 100644 --- a/code/frameworks/nextjs/src/next-image-loader-stub.ts +++ b/code/frameworks/nextjs/src/next-image-loader-stub.ts @@ -2,18 +2,27 @@ import { interpolateName } from 'loader-utils'; import imageSizeOf from 'image-size'; import type { RawLoaderDefinition } from 'webpack'; import type { NextConfig } from 'next'; -import sharp from 'sharp'; import { cpus } from 'os'; +import { NextJsSharpError } from '@storybook/core-events/preview-errors'; interface LoaderOptions { filename: string; nextConfig: NextConfig; } -if (sharp.concurrency() > 1) { - // Reducing concurrency reduces the memory usage too. - const divisor = process.env.NODE_ENV === 'development' ? 4 : 2; - sharp.concurrency(Math.floor(Math.max(cpus().length / divisor, 1))); +let sharp: typeof import('sharp') | undefined; + +try { + sharp = require('sharp'); + if (sharp && sharp.concurrency() > 1) { + // Reducing concurrency reduces the memory usage too. + const divisor = process.env.NODE_ENV === 'development' ? 4 : 2; + sharp.concurrency(Math.floor(Math.max(cpus().length / divisor, 1))); + } +} catch (e) { + console.warn( + 'You have to install sharp in order to use image optimization features in Next.js. AVIF support is also disabled.' + ); } const nextImageLoaderStub: RawLoaderDefinition = async function NextImageLoader( @@ -37,10 +46,14 @@ const nextImageLoaderStub: RawLoaderDefinition = async function N let height; if (extension === 'avif') { - const transformer = sharp(content); - const result = await transformer.metadata(); - width = result.width; - height = result.height; + if (sharp) { + const transformer = sharp(content); + const result = await transformer.metadata(); + width = result.width; + height = result.height; + } else { + throw new NextJsSharpError(); + } } else { const result = imageSizeOf(this.resourcePath); width = result.width; diff --git a/code/frameworks/preact-vite/package.json b/code/frameworks/preact-vite/package.json index ff1cf6612608..8f9dae800337 100644 --- a/code/frameworks/preact-vite/package.json +++ b/code/frameworks/preact-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preact-vite", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for Preact and Vite: Develop Preact components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/preact-webpack5/package.json b/code/frameworks/preact-webpack5/package.json index f139882e2c8d..dccfa9aa93d3 100644 --- a/code/frameworks/preact-webpack5/package.json +++ b/code/frameworks/preact-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preact-webpack5", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for Preact: Develop Preact Component in isolation.", "keywords": [ "storybook" diff --git a/code/frameworks/react-vite/package.json b/code/frameworks/react-vite/package.json index 0dc644e5fd9c..827e9479764b 100644 --- a/code/frameworks/react-vite/package.json +++ b/code/frameworks/react-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/react-vite", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for React and Vite: Develop React components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/react-webpack5/package.json b/code/frameworks/react-webpack5/package.json index 3c121d8c36c4..4ef8b479e4f6 100644 --- a/code/frameworks/react-webpack5/package.json +++ b/code/frameworks/react-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/react-webpack5", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for React: Develop React Component in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/server-webpack5/package.json b/code/frameworks/server-webpack5/package.json index d6b096958d97..c8966b299cbf 100644 --- a/code/frameworks/server-webpack5/package.json +++ b/code/frameworks/server-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/server-webpack5", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for Server: View HTML snippets from a server in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/svelte-vite/package.json b/code/frameworks/svelte-vite/package.json index 8f7455abc1df..173162058c00 100644 --- a/code/frameworks/svelte-vite/package.json +++ b/code/frameworks/svelte-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/svelte-vite", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for Svelte and Vite: Develop Svelte components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/svelte-webpack5/package.json b/code/frameworks/svelte-webpack5/package.json index 4d39fdd9d4ce..362fd576e7d3 100644 --- a/code/frameworks/svelte-webpack5/package.json +++ b/code/frameworks/svelte-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/svelte-webpack5", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for Svelte: Develop Svelte Component in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/sveltekit/package.json b/code/frameworks/sveltekit/package.json index 35a8659be347..3a9f86ae03de 100644 --- a/code/frameworks/sveltekit/package.json +++ b/code/frameworks/sveltekit/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/sveltekit", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for SvelteKit", "keywords": [ "storybook", diff --git a/code/frameworks/vue3-vite/package.json b/code/frameworks/vue3-vite/package.json index bc3bbd4e614f..a6d9e80ce048 100644 --- a/code/frameworks/vue3-vite/package.json +++ b/code/frameworks/vue3-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/vue3-vite", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for Vue3 and Vite: Develop Vue3 components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/vue3-webpack5/package.json b/code/frameworks/vue3-webpack5/package.json index e534562b57a6..6df4545ccb84 100644 --- a/code/frameworks/vue3-webpack5/package.json +++ b/code/frameworks/vue3-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/vue3-webpack5", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for Vue 3: Develop Vue 3 Components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/web-components-vite/package.json b/code/frameworks/web-components-vite/package.json index 620572741ca9..bca9dda246e9 100644 --- a/code/frameworks/web-components-vite/package.json +++ b/code/frameworks/web-components-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/web-components-vite", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for web-components and Vite: Develop Web Components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/web-components-webpack5/package.json b/code/frameworks/web-components-webpack5/package.json index 0b9f3d941004..2d0642ecbdd4 100644 --- a/code/frameworks/web-components-webpack5/package.json +++ b/code/frameworks/web-components-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/web-components-webpack5", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for web-components: View web components snippets in isolation with Hot Reloading.", "keywords": [ "lit", diff --git a/code/lib/channels/package.json b/code/lib/channels/package.json index 2d839f6d4674..57a8c53ab2e1 100644 --- a/code/lib/channels/package.json +++ b/code/lib/channels/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/channels", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "", "keywords": [ "storybook" diff --git a/code/lib/cli-sb/package.json b/code/lib/cli-sb/package.json index fbfa837b7aa9..9b6527789309 100644 --- a/code/lib/cli-sb/package.json +++ b/code/lib/cli-sb/package.json @@ -1,6 +1,6 @@ { "name": "sb", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook CLI", "keywords": [ "storybook" diff --git a/code/lib/cli-storybook/package.json b/code/lib/cli-storybook/package.json index 68f16db3a67e..363550761022 100644 --- a/code/lib/cli-storybook/package.json +++ b/code/lib/cli-storybook/package.json @@ -1,6 +1,6 @@ { "name": "storybook", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook CLI", "keywords": [ "storybook" diff --git a/code/lib/cli/package.json b/code/lib/cli/package.json index c59d373bdc2e..200eae0d10e8 100644 --- a/code/lib/cli/package.json +++ b/code/lib/cli/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/cli", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook's CLI - install, dev, build, upgrade, and more", "keywords": [ "cli", diff --git a/code/lib/cli/src/add.ts b/code/lib/cli/src/add.ts index 19a4b552fcc0..c0bb071dda5b 100644 --- a/code/lib/cli/src/add.ts +++ b/code/lib/cli/src/add.ts @@ -48,6 +48,12 @@ const checkInstalled = (addonName: string, main: any) => { const isCoreAddon = (addonName: string) => Object.hasOwn(versions, addonName); +type CLIOptions = { + packageManager?: PackageManagerName; + configDir?: string; + skipPostinstall: boolean; +}; + /** * Install the given addon package and add it to main.js * @@ -61,15 +67,18 @@ const isCoreAddon = (addonName: string) => Object.hasOwn(versions, addonName); */ export async function add( addon: string, - options: { packageManager: PackageManagerName; skipPostinstall: boolean }, + { packageManager: pkgMgr, skipPostinstall, configDir: userSpecifiedConfigDir }: CLIOptions, logger = console ) { - const { packageManager: pkgMgr } = options; const [addonName, inputVersion] = getVersionSpecifier(addon); const packageManager = JsPackageManagerFactory.getPackageManager({ force: pkgMgr }); const packageJson = await packageManager.retrievePackageJson(); - const { mainConfig, configDir } = getStorybookInfo(packageJson); + const { mainConfig, configDir: inferredConfigDir } = getStorybookInfo( + packageJson, + userSpecifiedConfigDir + ); + const configDir = userSpecifiedConfigDir || inferredConfigDir || '.storybook'; if (typeof configDir === 'undefined') { throw new Error(dedent` @@ -119,7 +128,7 @@ export async function add( main.appendValueToArray(['addons'], addonName); await writeConfig(main); - if (!options.skipPostinstall && isCoreAddon(addonName)) { + if (!skipPostinstall && isCoreAddon(addonName)) { await postinstallAddon(addonName, { packageManager: packageManager.type }); } } diff --git a/code/lib/cli/src/automigrate/fixes/index.ts b/code/lib/cli/src/automigrate/fixes/index.ts index 022074fa8301..72f3eb9a605d 100644 --- a/code/lib/cli/src/automigrate/fixes/index.ts +++ b/code/lib/cli/src/automigrate/fixes/index.ts @@ -26,6 +26,7 @@ import { removeJestTestingLibrary } from './remove-jest-testing-library'; import { addonsAPI } from './addons-api'; import { mdx1to3 } from './mdx-1-to-3'; import { addonPostCSS } from './addon-postcss'; +import { vta } from './vta'; import { upgradeStorybookRelatedDependencies } from './upgrade-storybook-related-dependencies'; export * from '../types'; @@ -58,6 +59,7 @@ export const allFixes: Fix[] = [ webpack5CompilerSetup, mdx1to3, upgradeStorybookRelatedDependencies, + vta, ]; export const initFixes: Fix[] = [eslintPlugin]; diff --git a/code/lib/cli/src/automigrate/fixes/vta.test.ts b/code/lib/cli/src/automigrate/fixes/vta.test.ts new file mode 100644 index 000000000000..ff1cc2109470 --- /dev/null +++ b/code/lib/cli/src/automigrate/fixes/vta.test.ts @@ -0,0 +1,61 @@ +import { describe, expect, it } from 'vitest'; +import type { StorybookConfig } from '@storybook/types'; +import { vta } from './vta'; + +const check = async ({ + packageManager, + main: mainConfig, + storybookVersion = '7.0.0', +}: { + packageManager: any; + main: Partial & Record; + storybookVersion?: string; +}) => { + return vta.check({ + packageManager, + configDir: '', + mainConfig: mainConfig as any, + storybookVersion, + }); +}; + +describe('no-ops', () => { + it('with addon setup', async () => { + await expect( + check({ + packageManager: {}, + main: { + addons: ['@chromatic-com/storybook'], + }, + }) + ).resolves.toBeFalsy(); + }); + it('with addon setup with options', async () => { + await expect( + check({ + packageManager: {}, + main: { + addons: [ + { + name: '@chromatic-com/storybook', + options: {}, + }, + ], + }, + }) + ).resolves.toBeFalsy(); + }); +}); + +describe('continue', () => { + it('no addons', async () => { + await expect( + check({ + packageManager: {}, + main: { + stories: ['**/*.stories.mdx'], + }, + }) + ).resolves.toBeTruthy(); + }); +}); diff --git a/code/lib/cli/src/automigrate/fixes/vta.ts b/code/lib/cli/src/automigrate/fixes/vta.ts new file mode 100644 index 000000000000..c86f957298a8 --- /dev/null +++ b/code/lib/cli/src/automigrate/fixes/vta.ts @@ -0,0 +1,55 @@ +import { dedent } from 'ts-dedent'; +import chalk from 'chalk'; +import { getAddonNames, updateMainConfig } from '../helpers/mainConfigFile'; +import type { Fix } from '../types'; + +const logger = console; + +interface Options {} + +/** + */ +export const vta: Fix = { + id: 'visual-tests-addon', + + versionRange: ['<8.0.7', '>=8.0.7'], + + async check({ mainConfig }) { + const hadAddonInstalled = getAddonNames(mainConfig).some((addon) => + addon.includes('@chromatic-com/storybook') + ); + + const skip = hadAddonInstalled; + + if (skip) { + return null; + } + + return {}; + }, + + prompt() { + return dedent` + New to Storybook 8: Storybook's Visual Tests addon helps you catch unintentional changes/bugs in your stories. The addon is powered by Chromatic, a cloud-based testing tool developed by Storybook's core team. + + Learn more: ${chalk.yellow('storybook.js.org/docs/writing-tests/visual-testing')} + + Install Visual Tests addon in your project? + `; + }, + + async run({ packageManager, dryRun, mainConfigPath, skipInstall }) { + if (!dryRun) { + const packageJson = await packageManager.retrievePackageJson(); + await packageManager.addDependencies( + { installAsDevDependencies: true, skipInstall, packageJson }, + [`@chromatic-com/storybook@^1`] + ); + + await updateMainConfig({ mainConfigPath, dryRun: !!dryRun }, async (main) => { + logger.info(`✅ Adding "@chromatic-com/storybook" addon`); + main.appendValueToArray(['addons'], '@chromatic-dom/storybook'); + }); + } + }, +}; diff --git a/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts b/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts index 3fd56fc8107b..11a13c127e0e 100644 --- a/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts +++ b/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts @@ -4,6 +4,7 @@ import { rendererPackages, frameworkPackages, builderPackages, + extractProperFrameworkName, } from '@storybook/core-common'; import type { StorybookConfigRaw, StorybookConfig } from '@storybook/types'; import type { ConfigFile } from '@storybook/csf-tools'; @@ -30,11 +31,7 @@ export const getFrameworkPackageName = (mainConfig?: StorybookConfigRaw) => { return null; } - const normalizedPath = path.normalize(packageNameOrPath).replace(new RegExp(/\\/, 'g'), '/'); - - return ( - Object.keys(frameworkPackages).find((pkg) => normalizedPath.endsWith(pkg)) || packageNameOrPath - ); + return extractProperFrameworkName(packageNameOrPath); }; /** diff --git a/code/lib/cli/src/generate.ts b/code/lib/cli/src/generate.ts index 36de347730a7..b5aa64f7f77d 100644 --- a/code/lib/cli/src/generate.ts +++ b/code/lib/cli/src/generate.ts @@ -68,6 +68,7 @@ command('add ') '--package-manager ', 'Force package manager for installing dependencies' ) + .option('-c, --config-dir ', 'Directory where to load Storybook configurations from') .option('-s --skip-postinstall', 'Skip package specific postinstall config modifications') .action((addonName: string, options: any) => add(addonName, options)); diff --git a/code/lib/cli/src/helpers.ts b/code/lib/cli/src/helpers.ts index ab3bf239592c..5c8351cca7b0 100644 --- a/code/lib/cli/src/helpers.ts +++ b/code/lib/cli/src/helpers.ts @@ -8,13 +8,13 @@ import stripJsonComments from 'strip-json-comments'; import findUp from 'find-up'; import invariant from 'tiny-invariant'; import { getCliDir, getRendererDir } from './dirs'; -import type { - JsPackageManager, - PackageJson, - PackageJsonWithDepsAndDevDeps, +import { + type JsPackageManager, + type PackageJson, + type PackageJsonWithDepsAndDevDeps, + frameworkToRenderer as CoreFrameworkToRenderer, } from '@storybook/core-common'; -import type { SupportedFrameworks } from '@storybook/types'; -import type { SupportedRenderers } from './project_types'; +import type { SupportedFrameworks, SupportedRenderers } from '@storybook/types'; import { CoreBuilder } from './project_types'; import { SupportedLanguage } from './project_types'; import { versions as storybookMonorepoPackages } from '@storybook/core-common'; @@ -132,40 +132,10 @@ type CopyTemplateFilesOptions = { destination?: string; }; -export const frameworkToRenderer: Record< - SupportedFrameworks | SupportedRenderers, - SupportedRenderers | 'vue' -> = { - // frameworks - angular: 'angular', - ember: 'ember', - 'html-vite': 'html', - 'html-webpack5': 'html', - nextjs: 'react', - 'preact-vite': 'preact', - 'preact-webpack5': 'preact', - qwik: 'qwik', - 'react-vite': 'react', - 'react-webpack5': 'react', - 'server-webpack5': 'server', - solid: 'solid', - 'svelte-vite': 'svelte', - 'svelte-webpack5': 'svelte', - sveltekit: 'svelte', - 'vue3-vite': 'vue3', - 'vue3-webpack5': 'vue3', - 'web-components-vite': 'web-components', - 'web-components-webpack5': 'web-components', - // renderers - html: 'html', - preact: 'preact', - 'react-native': 'react-native', - react: 'react', - server: 'server', - svelte: 'svelte', - vue3: 'vue3', - 'web-components': 'web-components', -}; +/** + * @deprecated Please use `frameworkToRenderer` from `@storybook/core-common` instead + */ +export const frameworkToRenderer = CoreFrameworkToRenderer; export const frameworkToDefaultBuilder: Record = { angular: CoreBuilder.Webpack5, diff --git a/code/lib/cli/src/project_types.ts b/code/lib/cli/src/project_types.ts index 3a5cda3781ef..f8f6973ab31a 100644 --- a/code/lib/cli/src/project_types.ts +++ b/code/lib/cli/src/project_types.ts @@ -1,5 +1,8 @@ import { minVersion, validRange } from 'semver'; -import type { SupportedFrameworks } from '@storybook/types'; +import type { + SupportedFrameworks, + SupportedRenderers as CoreSupportedFrameworks, +} from '@storybook/types'; function eqMajor(versionRange: string, major: number) { // Uses validRange to avoid a throw from minVersion if an invalid range gets passed @@ -22,20 +25,10 @@ export const externalFrameworks: ExternalFramework[] = [ { name: 'solid', frameworks: ['storybook-solidjs-vite'], renderer: 'storybook-solidjs' }, ]; -// Should match @storybook/ -export type SupportedRenderers = - | 'react' - | 'react-native' - | 'vue3' - | 'angular' - | 'ember' - | 'preact' - | 'svelte' - | 'qwik' - | 'html' - | 'web-components' - | 'server' - | 'solid'; +/** + * @deprecated Please use `SupportedFrameworks` from `@storybook/types` instead + */ +export type SupportedRenderers = CoreSupportedFrameworks; export const SUPPORTED_RENDERERS: SupportedRenderers[] = [ 'react', diff --git a/code/lib/client-logger/package.json b/code/lib/client-logger/package.json index 5820afd99ea5..80545b22643b 100644 --- a/code/lib/client-logger/package.json +++ b/code/lib/client-logger/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/client-logger", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "", "keywords": [ "storybook" diff --git a/code/lib/codemod/package.json b/code/lib/codemod/package.json index 56bf38c752fb..10351aaf7757 100644 --- a/code/lib/codemod/package.json +++ b/code/lib/codemod/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/codemod", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "A collection of codemod scripts written with JSCodeshift", "keywords": [ "storybook" diff --git a/code/lib/core-common/package.json b/code/lib/core-common/package.json index c64ce6092e3f..b40d3bba403e 100644 --- a/code/lib/core-common/package.json +++ b/code/lib/core-common/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/core-common", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook framework-agnostic API", "keywords": [ "storybook" diff --git a/code/lib/core-common/src/index.ts b/code/lib/core-common/src/index.ts index e8f02195ba9d..a568fa70ba4a 100644 --- a/code/lib/core-common/src/index.ts +++ b/code/lib/core-common/src/index.ts @@ -7,6 +7,7 @@ export * from './utils/cli'; export * from './utils/check-addon-order'; export * from './utils/envs'; export * from './utils/common-glob-options'; +export * from './utils/framework-to-renderer'; export * from './utils/get-builder-options'; export * from './utils/get-framework-name'; export * from './utils/get-renderer-name'; diff --git a/code/lib/core-common/src/utils/framework-to-renderer.ts b/code/lib/core-common/src/utils/framework-to-renderer.ts new file mode 100644 index 000000000000..a7c8532529a1 --- /dev/null +++ b/code/lib/core-common/src/utils/framework-to-renderer.ts @@ -0,0 +1,36 @@ +import type { SupportedFrameworks, SupportedRenderers } from '@storybook/types'; + +export const frameworkToRenderer: Record< + SupportedFrameworks | SupportedRenderers, + SupportedRenderers | 'vue' +> = { + // frameworks + angular: 'angular', + ember: 'ember', + 'html-vite': 'html', + 'html-webpack5': 'html', + nextjs: 'react', + 'preact-vite': 'preact', + 'preact-webpack5': 'preact', + qwik: 'qwik', + 'react-vite': 'react', + 'react-webpack5': 'react', + 'server-webpack5': 'server', + solid: 'solid', + 'svelte-vite': 'svelte', + 'svelte-webpack5': 'svelte', + sveltekit: 'svelte', + 'vue3-vite': 'vue3', + 'vue3-webpack5': 'vue3', + 'web-components-vite': 'web-components', + 'web-components-webpack5': 'web-components', + // renderers + html: 'html', + preact: 'preact', + 'react-native': 'react-native', + react: 'react', + server: 'server', + svelte: 'svelte', + vue3: 'vue3', + 'web-components': 'web-components', +}; diff --git a/code/lib/core-common/src/utils/get-framework-name.test.ts b/code/lib/core-common/src/utils/get-framework-name.test.ts new file mode 100644 index 000000000000..9f6a95ba19e7 --- /dev/null +++ b/code/lib/core-common/src/utils/get-framework-name.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from 'vitest'; +import { extractProperFrameworkName } from './get-framework-name'; + +describe('get-framework-name', () => { + describe('extractProperFrameworkName', () => { + it('should extract the proper framework name from the given framework field', () => { + expect(extractProperFrameworkName('@storybook/angular')).toBe('@storybook/angular'); + expect(extractProperFrameworkName('/path/to/@storybook/angular')).toBe('@storybook/angular'); + expect(extractProperFrameworkName('\\path\\to\\@storybook\\angular')).toBe( + '@storybook/angular' + ); + }); + + it('should return the given framework name if it is a third-party framework', () => { + expect(extractProperFrameworkName('@third-party/framework')).toBe('@third-party/framework'); + }); + }); +}); diff --git a/code/lib/core-common/src/utils/get-framework-name.ts b/code/lib/core-common/src/utils/get-framework-name.ts index e7191545b5a4..fcfef2efee42 100644 --- a/code/lib/core-common/src/utils/get-framework-name.ts +++ b/code/lib/core-common/src/utils/get-framework-name.ts @@ -1,5 +1,7 @@ import { dedent } from 'ts-dedent'; import type { Options } from '@storybook/types'; +import { frameworkPackages } from './get-storybook-info'; +import { normalizePath } from './normalize-path'; /** * Framework can be a string or an object. This utility always returns the string name. @@ -17,3 +19,17 @@ export async function getFrameworkName(options: Options) { return typeof framework === 'object' ? framework.name : framework; } + +/** + * Extracts the proper framework name from the given framework field. + * The framework field can be the framework package name or a path to the framework package. + * @example + * extractProperFrameworkName('/path/to/@storybook/angular') // => '@storybook/angular' + * extractProperFrameworkName('@third-party/framework') // => '@third-party/framework' + */ +export const extractProperFrameworkName = (framework: string) => { + const normalizedPath = normalizePath(framework); + const frameworkName = Object.keys(frameworkPackages).find((pkg) => normalizedPath.endsWith(pkg)); + + return frameworkName ?? framework; +}; diff --git a/code/lib/core-common/src/utils/get-renderer-name.test.ts b/code/lib/core-common/src/utils/get-renderer-name.test.ts new file mode 100644 index 000000000000..f5d10959405c --- /dev/null +++ b/code/lib/core-common/src/utils/get-renderer-name.test.ts @@ -0,0 +1,17 @@ +import { it } from 'node:test'; +import { describe, expect } from 'vitest'; +import { extractProperRendererNameFromFramework } from './get-renderer-name'; + +describe('get-renderer-name', () => { + describe('extractProperRendererNameFromFramework', () => { + it('should return the renderer name for a known framework', async () => { + const renderer = await extractProperRendererNameFromFramework('@storybook/react'); + expect(renderer).toEqual('react'); + }); + + it('should return null for an unknown framework', async () => { + const renderer = await extractProperRendererNameFromFramework('@third-party/framework'); + expect(renderer).toBeNull(); + }); + }); +}); diff --git a/code/lib/core-common/src/utils/get-renderer-name.ts b/code/lib/core-common/src/utils/get-renderer-name.ts index ce4a14891086..2acdcc6421bc 100644 --- a/code/lib/core-common/src/utils/get-renderer-name.ts +++ b/code/lib/core-common/src/utils/get-renderer-name.ts @@ -1,8 +1,11 @@ import type { Options } from '@storybook/types'; -import { getFrameworkName } from './get-framework-name'; +import { extractProperFrameworkName, getFrameworkName } from './get-framework-name'; +import { frameworkPackages } from './get-storybook-info'; +import { frameworkToRenderer } from './framework-to-renderer'; /** * Render is set as a string on core. It must be set by the framework + * It falls back to the framework name if not set */ export async function getRendererName(options: Options) { const core = await options.presets.apply('core', {}, options); @@ -15,3 +18,23 @@ export async function getRendererName(options: Options) { return core.renderer; } + +/** + * Extracts the proper renderer name from the given framework name. + * @param frameworkName The name of the framework. + * @returns The name of the renderer. + * @example + * extractProperRendererNameFromFramework('@storybook/react') // => 'react' + * extractProperRendererNameFromFramework('@storybook/angular') // => 'angular' + * extractProperRendererNameFromFramework('@third-party/framework') // => null + */ +export async function extractProperRendererNameFromFramework(frameworkName: string) { + const extractedFrameworkName = extractProperFrameworkName(frameworkName); + const framework = frameworkPackages[extractedFrameworkName]; + + if (!framework) { + return null; + } + + return frameworkToRenderer[framework as keyof typeof frameworkToRenderer]; +} diff --git a/code/lib/core-common/src/utils/normalize-path.test.ts b/code/lib/core-common/src/utils/normalize-path.test.ts new file mode 100644 index 000000000000..b2688660b6ef --- /dev/null +++ b/code/lib/core-common/src/utils/normalize-path.test.ts @@ -0,0 +1,11 @@ +import { normalizePath } from './normalize-path'; +import { describe, expect, it } from 'vitest'; + +describe('normalize-path', () => { + it('should normalize paths', () => { + expect(normalizePath('path/to/../file')).toBe('path/file'); + expect(normalizePath('path/to/./file')).toBe('path/to/file'); + expect(normalizePath('path\\to\\file')).toBe('path/to/file'); + expect(normalizePath('foo\\..\\bar')).toBe('bar'); + }); +}); diff --git a/code/lib/core-common/src/utils/normalize-path.ts b/code/lib/core-common/src/utils/normalize-path.ts new file mode 100644 index 000000000000..a6db3aa2c9de --- /dev/null +++ b/code/lib/core-common/src/utils/normalize-path.ts @@ -0,0 +1,14 @@ +import path from 'path'; + +/** + * Normalize a path to use forward slashes and remove .. and . + * @param p The path to normalize + * @returns The normalized path + * @example + * normalizePath('path/to/../file') // => 'path/file' + * normalizePath('path/to/./file') // => 'path/to/file' + * normalizePath('path\\to\\file') // => 'path/to/file' + */ +export function normalizePath(p: string) { + return path.posix.normalize(p.replace(/\\/g, '/')); +} diff --git a/code/lib/core-common/src/versions.ts b/code/lib/core-common/src/versions.ts index 13842212755e..3748c421b117 100644 --- a/code/lib/core-common/src/versions.ts +++ b/code/lib/core-common/src/versions.ts @@ -1,83 +1,83 @@ // auto generated file, do not edit export default { - '@storybook/addon-a11y': '8.1.0-alpha.6', - '@storybook/addon-actions': '8.1.0-alpha.6', - '@storybook/addon-backgrounds': '8.1.0-alpha.6', - '@storybook/addon-controls': '8.1.0-alpha.6', - '@storybook/addon-docs': '8.1.0-alpha.6', - '@storybook/addon-essentials': '8.1.0-alpha.6', - '@storybook/addon-highlight': '8.1.0-alpha.6', - '@storybook/addon-interactions': '8.1.0-alpha.6', - '@storybook/addon-jest': '8.1.0-alpha.6', - '@storybook/addon-links': '8.1.0-alpha.6', - '@storybook/addon-mdx-gfm': '8.1.0-alpha.6', - '@storybook/addon-measure': '8.1.0-alpha.6', - '@storybook/addon-onboarding': '8.1.0-alpha.6', - '@storybook/addon-outline': '8.1.0-alpha.6', - '@storybook/addon-storysource': '8.1.0-alpha.6', - '@storybook/addon-themes': '8.1.0-alpha.6', - '@storybook/addon-toolbars': '8.1.0-alpha.6', - '@storybook/addon-viewport': '8.1.0-alpha.6', - '@storybook/angular': '8.1.0-alpha.6', - '@storybook/blocks': '8.1.0-alpha.6', - '@storybook/builder-manager': '8.1.0-alpha.6', - '@storybook/builder-vite': '8.1.0-alpha.6', - '@storybook/builder-webpack5': '8.1.0-alpha.6', - '@storybook/channels': '8.1.0-alpha.6', - '@storybook/cli': '8.1.0-alpha.6', - '@storybook/client-logger': '8.1.0-alpha.6', - '@storybook/codemod': '8.1.0-alpha.6', - '@storybook/components': '8.1.0-alpha.6', - '@storybook/core-common': '8.1.0-alpha.6', - '@storybook/core-events': '8.1.0-alpha.6', - '@storybook/core-server': '8.1.0-alpha.6', - '@storybook/core-webpack': '8.1.0-alpha.6', - '@storybook/csf-plugin': '8.1.0-alpha.6', - '@storybook/csf-tools': '8.1.0-alpha.6', - '@storybook/docs-tools': '8.1.0-alpha.6', - '@storybook/ember': '8.1.0-alpha.6', - '@storybook/html': '8.1.0-alpha.6', - '@storybook/html-vite': '8.1.0-alpha.6', - '@storybook/html-webpack5': '8.1.0-alpha.6', - '@storybook/instrumenter': '8.1.0-alpha.6', - '@storybook/manager': '8.1.0-alpha.6', - '@storybook/manager-api': '8.1.0-alpha.6', - '@storybook/nextjs': '8.1.0-alpha.6', - '@storybook/node-logger': '8.1.0-alpha.6', - '@storybook/preact': '8.1.0-alpha.6', - '@storybook/preact-vite': '8.1.0-alpha.6', - '@storybook/preact-webpack5': '8.1.0-alpha.6', - '@storybook/preset-create-react-app': '8.1.0-alpha.6', - '@storybook/preset-html-webpack': '8.1.0-alpha.6', - '@storybook/preset-preact-webpack': '8.1.0-alpha.6', - '@storybook/preset-react-webpack': '8.1.0-alpha.6', - '@storybook/preset-server-webpack': '8.1.0-alpha.6', - '@storybook/preset-svelte-webpack': '8.1.0-alpha.6', - '@storybook/preset-vue3-webpack': '8.1.0-alpha.6', - '@storybook/preview': '8.1.0-alpha.6', - '@storybook/preview-api': '8.1.0-alpha.6', - '@storybook/react': '8.1.0-alpha.6', - '@storybook/react-dom-shim': '8.1.0-alpha.6', - '@storybook/react-vite': '8.1.0-alpha.6', - '@storybook/react-webpack5': '8.1.0-alpha.6', - '@storybook/router': '8.1.0-alpha.6', - '@storybook/server': '8.1.0-alpha.6', - '@storybook/server-webpack5': '8.1.0-alpha.6', - '@storybook/source-loader': '8.1.0-alpha.6', - '@storybook/svelte': '8.1.0-alpha.6', - '@storybook/svelte-vite': '8.1.0-alpha.6', - '@storybook/svelte-webpack5': '8.1.0-alpha.6', - '@storybook/sveltekit': '8.1.0-alpha.6', - '@storybook/telemetry': '8.1.0-alpha.6', - '@storybook/test': '8.1.0-alpha.6', - '@storybook/theming': '8.1.0-alpha.6', - '@storybook/types': '8.1.0-alpha.6', - '@storybook/vue3': '8.1.0-alpha.6', - '@storybook/vue3-vite': '8.1.0-alpha.6', - '@storybook/vue3-webpack5': '8.1.0-alpha.6', - '@storybook/web-components': '8.1.0-alpha.6', - '@storybook/web-components-vite': '8.1.0-alpha.6', - '@storybook/web-components-webpack5': '8.1.0-alpha.6', - sb: '8.1.0-alpha.6', - storybook: '8.1.0-alpha.6', + '@storybook/addon-a11y': '8.1.0-alpha.7', + '@storybook/addon-actions': '8.1.0-alpha.7', + '@storybook/addon-backgrounds': '8.1.0-alpha.7', + '@storybook/addon-controls': '8.1.0-alpha.7', + '@storybook/addon-docs': '8.1.0-alpha.7', + '@storybook/addon-essentials': '8.1.0-alpha.7', + '@storybook/addon-highlight': '8.1.0-alpha.7', + '@storybook/addon-interactions': '8.1.0-alpha.7', + '@storybook/addon-jest': '8.1.0-alpha.7', + '@storybook/addon-links': '8.1.0-alpha.7', + '@storybook/addon-mdx-gfm': '8.1.0-alpha.7', + '@storybook/addon-measure': '8.1.0-alpha.7', + '@storybook/addon-onboarding': '8.1.0-alpha.7', + '@storybook/addon-outline': '8.1.0-alpha.7', + '@storybook/addon-storysource': '8.1.0-alpha.7', + '@storybook/addon-themes': '8.1.0-alpha.7', + '@storybook/addon-toolbars': '8.1.0-alpha.7', + '@storybook/addon-viewport': '8.1.0-alpha.7', + '@storybook/angular': '8.1.0-alpha.7', + '@storybook/blocks': '8.1.0-alpha.7', + '@storybook/builder-manager': '8.1.0-alpha.7', + '@storybook/builder-vite': '8.1.0-alpha.7', + '@storybook/builder-webpack5': '8.1.0-alpha.7', + '@storybook/channels': '8.1.0-alpha.7', + '@storybook/cli': '8.1.0-alpha.7', + '@storybook/client-logger': '8.1.0-alpha.7', + '@storybook/codemod': '8.1.0-alpha.7', + '@storybook/components': '8.1.0-alpha.7', + '@storybook/core-common': '8.1.0-alpha.7', + '@storybook/core-events': '8.1.0-alpha.7', + '@storybook/core-server': '8.1.0-alpha.7', + '@storybook/core-webpack': '8.1.0-alpha.7', + '@storybook/csf-plugin': '8.1.0-alpha.7', + '@storybook/csf-tools': '8.1.0-alpha.7', + '@storybook/docs-tools': '8.1.0-alpha.7', + '@storybook/ember': '8.1.0-alpha.7', + '@storybook/html': '8.1.0-alpha.7', + '@storybook/html-vite': '8.1.0-alpha.7', + '@storybook/html-webpack5': '8.1.0-alpha.7', + '@storybook/instrumenter': '8.1.0-alpha.7', + '@storybook/manager': '8.1.0-alpha.7', + '@storybook/manager-api': '8.1.0-alpha.7', + '@storybook/nextjs': '8.1.0-alpha.7', + '@storybook/node-logger': '8.1.0-alpha.7', + '@storybook/preact': '8.1.0-alpha.7', + '@storybook/preact-vite': '8.1.0-alpha.7', + '@storybook/preact-webpack5': '8.1.0-alpha.7', + '@storybook/preset-create-react-app': '8.1.0-alpha.7', + '@storybook/preset-html-webpack': '8.1.0-alpha.7', + '@storybook/preset-preact-webpack': '8.1.0-alpha.7', + '@storybook/preset-react-webpack': '8.1.0-alpha.7', + '@storybook/preset-server-webpack': '8.1.0-alpha.7', + '@storybook/preset-svelte-webpack': '8.1.0-alpha.7', + '@storybook/preset-vue3-webpack': '8.1.0-alpha.7', + '@storybook/preview': '8.1.0-alpha.7', + '@storybook/preview-api': '8.1.0-alpha.7', + '@storybook/react': '8.1.0-alpha.7', + '@storybook/react-dom-shim': '8.1.0-alpha.7', + '@storybook/react-vite': '8.1.0-alpha.7', + '@storybook/react-webpack5': '8.1.0-alpha.7', + '@storybook/router': '8.1.0-alpha.7', + '@storybook/server': '8.1.0-alpha.7', + '@storybook/server-webpack5': '8.1.0-alpha.7', + '@storybook/source-loader': '8.1.0-alpha.7', + '@storybook/svelte': '8.1.0-alpha.7', + '@storybook/svelte-vite': '8.1.0-alpha.7', + '@storybook/svelte-webpack5': '8.1.0-alpha.7', + '@storybook/sveltekit': '8.1.0-alpha.7', + '@storybook/telemetry': '8.1.0-alpha.7', + '@storybook/test': '8.1.0-alpha.7', + '@storybook/theming': '8.1.0-alpha.7', + '@storybook/types': '8.1.0-alpha.7', + '@storybook/vue3': '8.1.0-alpha.7', + '@storybook/vue3-vite': '8.1.0-alpha.7', + '@storybook/vue3-webpack5': '8.1.0-alpha.7', + '@storybook/web-components': '8.1.0-alpha.7', + '@storybook/web-components-vite': '8.1.0-alpha.7', + '@storybook/web-components-webpack5': '8.1.0-alpha.7', + sb: '8.1.0-alpha.7', + storybook: '8.1.0-alpha.7', }; diff --git a/code/lib/core-events/package.json b/code/lib/core-events/package.json index 3b64fb0c9665..699640debd0d 100644 --- a/code/lib/core-events/package.json +++ b/code/lib/core-events/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/core-events", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Event names used in storybook core", "keywords": [ "storybook" diff --git a/code/lib/core-events/src/errors/preview-errors.ts b/code/lib/core-events/src/errors/preview-errors.ts index fb33c42688b3..218db70ef826 100644 --- a/code/lib/core-events/src/errors/preview-errors.ts +++ b/code/lib/core-events/src/errors/preview-errors.ts @@ -27,6 +27,7 @@ export enum Category { RENDERER_VUE = 'RENDERER_VUE', RENDERER_VUE3 = 'RENDERER_VUE3', RENDERER_WEB_COMPONENTS = 'RENDERER_WEB-COMPONENTS', + FRAMEWORK_NEXTJS = 'FRAMEWORK_NEXTJS', } export class MissingStoryAfterHmrError extends StorybookError { @@ -235,3 +236,19 @@ export class StoryStoreAccessedBeforeInitializationError extends StorybookError remove access to the store entirely`; } } + +export class NextJsSharpError extends StorybookError { + readonly category = Category.FRAMEWORK_NEXTJS; + + readonly code = 1; + + readonly documentation = 'https://storybook.js.org/docs/get-started/nextjs#faq'; + + template() { + return dedent` + You are importing avif images, but you don't have sharp installed. + + You have to install sharp in order to use image optimization features in Next.js. + `; + } +} diff --git a/code/lib/core-events/src/index.ts b/code/lib/core-events/src/index.ts index 7542d0aa4e57..82978a994d07 100644 --- a/code/lib/core-events/src/index.ts +++ b/code/lib/core-events/src/index.ts @@ -73,6 +73,8 @@ enum events { SET_WHATS_NEW_CACHE = 'setWhatsNewCache', TOGGLE_WHATS_NEW_NOTIFICATIONS = 'toggleWhatsNewNotifications', TELEMETRY_ERROR = 'telemetryError', + FILE_COMPONENT_SEARCH = 'fileComponentSearch', + FILE_COMPONENT_SEARCH_RESULT = 'fileComponentSearchResult', } // Enables: `import Events from ...` @@ -87,6 +89,8 @@ export const { CURRENT_STORY_WAS_SET, DOCS_PREPARED, DOCS_RENDERED, + FILE_COMPONENT_SEARCH, + FILE_COMPONENT_SEARCH_RESULT, FORCE_RE_RENDER, FORCE_REMOUNT, GLOBALS_UPDATED, diff --git a/code/lib/core-server/package.json b/code/lib/core-server/package.json index 58f6be3c9d46..9edbf6ea41e4 100644 --- a/code/lib/core-server/package.json +++ b/code/lib/core-server/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/core-server", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook framework-agnostic API", "keywords": [ "storybook" @@ -78,9 +78,11 @@ "@types/semver": "^7.3.4", "better-opn": "^3.0.2", "chalk": "^4.1.0", + "cjs-module-lexer": "^1.2.3", "cli-table3": "^0.6.1", "compression": "^1.7.4", "detect-port": "^1.3.0", + "es-module-lexer": "^1.5.0", "express": "^4.17.3", "fs-extra": "^11.1.0", "globby": "^14.0.1", diff --git a/code/lib/core-server/src/presets/common-preset.ts b/code/lib/core-server/src/presets/common-preset.ts index 6dcd6366eff0..fbc39465aad4 100644 --- a/code/lib/core-server/src/presets/common-preset.ts +++ b/code/lib/core-server/src/presets/common-preset.ts @@ -34,6 +34,7 @@ import invariant from 'tiny-invariant'; import { parseStaticDir } from '../utils/server-statics'; import { defaultStaticDirs } from '../utils/constants'; import { sendTelemetryError } from '../withTelemetry'; +import { initFileSearchChannel } from '../server-channel/file-search-channel'; const interpolate = (string: string, data: Record = {}) => Object.entries(data).reduce((acc, [k, v]) => acc.replace(new RegExp(`%${k}%`, 'g'), v), string); @@ -340,6 +341,8 @@ export const experimental_serverChannel = async ( } }); + initFileSearchChannel(channel, options); + return channel; }; diff --git a/code/lib/core-server/src/server-channel/file-search-channel.test.ts b/code/lib/core-server/src/server-channel/file-search-channel.test.ts new file mode 100644 index 000000000000..e967910dd6c7 --- /dev/null +++ b/code/lib/core-server/src/server-channel/file-search-channel.test.ts @@ -0,0 +1,165 @@ +import type { ChannelTransport } from '@storybook/channels'; +import { Channel } from '@storybook/channels'; +import { FILE_COMPONENT_SEARCH, FILE_COMPONENT_SEARCH_RESULT } from '@storybook/core-events'; +import { beforeEach, describe, expect, vi, it } from 'vitest'; + +import { initFileSearchChannel } from './file-search-channel'; + +const mocks = vi.hoisted(() => { + return { + searchFiles: vi.fn(), + }; +}); + +vi.mock('../utils/search-files', () => { + return { + searchFiles: mocks.searchFiles, + }; +}); + +vi.mock('@storybook/core-common', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + getFrameworkName: vi.fn().mockResolvedValue('@storybook/react'), + extractProperRendererNameFromFramework: vi.fn().mockResolvedValue('react'), + getProjectRoot: vi + .fn() + .mockReturnValue(require('path').join(__dirname, '..', 'utils', '__search-files-tests__')), + }; +}); + +describe('file-search-channel', () => { + const transport = { setHandler: vi.fn(), send: vi.fn() } satisfies ChannelTransport; + const mockChannel = new Channel({ transport }); + const searchResultChannelListener = vi.fn(); + + beforeEach(() => { + transport.setHandler.mockClear(); + transport.send.mockClear(); + searchResultChannelListener.mockClear(); + }); + + describe('initFileSearchChannel', async () => { + it('should emit search result event with the search result', async () => { + const mockOptions = {}; + const data = { searchQuery: 'commonjs' }; + + initFileSearchChannel(mockChannel, mockOptions as any); + + mockChannel.addListener(FILE_COMPONENT_SEARCH_RESULT, searchResultChannelListener); + mockChannel.emit(FILE_COMPONENT_SEARCH, data); + + mocks.searchFiles.mockImplementation(async (...args) => { + // @ts-expect-error Ignore type issue + return (await vi.importActual('../utils/search-files')).searchFiles(...args); + }); + + await vi.waitFor( + () => { + expect(searchResultChannelListener).toHaveBeenCalled(); + }, + { timeout: 2000 } + ); + + expect(searchResultChannelListener).toHaveBeenCalledWith({ + error: null, + result: { + files: [ + { + exportedComponents: [ + { + default: false, + name: './commonjs', + }, + ], + filepath: 'src/commonjs-module-default.js', + }, + { + exportedComponents: [ + { + default: false, + name: 'a', + }, + { + default: false, + name: 'b', + }, + { + default: false, + name: 'c', + }, + { + default: false, + name: 'd', + }, + { + default: false, + name: 'e', + }, + ], + filepath: 'src/commonjs-module.js', + }, + ], + searchQuery: 'commonjs', + }, + success: true, + }); + }); + + it('should emit search result event with an empty search result', async () => { + const mockOptions = {}; + const data = { searchQuery: 'no-file-for-search-query' }; + + initFileSearchChannel(mockChannel, mockOptions as any); + + mockChannel.addListener(FILE_COMPONENT_SEARCH_RESULT, searchResultChannelListener); + mockChannel.emit(FILE_COMPONENT_SEARCH, data); + + mocks.searchFiles.mockImplementation(async (...args) => { + // @ts-expect-error Ignore type issue + return (await vi.importActual('../utils/search-files')).searchFiles(...args); + }); + + await vi.waitFor( + () => { + expect(searchResultChannelListener).toHaveBeenCalled(); + }, + { timeout: 2000 } + ); + + expect(searchResultChannelListener).toHaveBeenCalledWith({ + error: null, + result: { + files: [], + searchQuery: 'no-file-for-search-query', + }, + success: true, + }); + }); + + it('should emit an error message if an error occurs while searching for components in the project', async () => { + const mockOptions = {}; + const data = { searchQuery: 'commonjs' }; + + initFileSearchChannel(mockChannel, mockOptions as any); + + mockChannel.addListener(FILE_COMPONENT_SEARCH_RESULT, searchResultChannelListener); + + mockChannel.emit(FILE_COMPONENT_SEARCH, data); + + mocks.searchFiles.mockRejectedValue(new Error('ENOENT: no such file or directory')); + + await vi.waitFor(() => { + expect(searchResultChannelListener).toHaveBeenCalled(); + }); + + expect(searchResultChannelListener).toHaveBeenCalledWith({ + error: + 'An error occurred while searching for components in the project.\nENOENT: no such file or directory', + result: null, + success: false, + }); + }); + }); +}); diff --git a/code/lib/core-server/src/server-channel/file-search-channel.ts b/code/lib/core-server/src/server-channel/file-search-channel.ts new file mode 100644 index 000000000000..3f2884867447 --- /dev/null +++ b/code/lib/core-server/src/server-channel/file-search-channel.ts @@ -0,0 +1,106 @@ +import type { Options, SupportedRenderers } from '@storybook/types'; +import type { Channel } from '@storybook/channels'; +import { + extractProperRendererNameFromFramework, + getFrameworkName, + getProjectRoot, +} from '@storybook/core-common'; +import path from 'path'; +import fs from 'fs/promises'; + +import { getParser } from '../utils/parser'; +import { searchFiles } from '../utils/search-files'; +import { FILE_COMPONENT_SEARCH, FILE_COMPONENT_SEARCH_RESULT } from '@storybook/core-events'; + +interface Data { + // A regular string or a glob pattern + searchQuery?: string; +} + +interface SearchResult { + success: true | false; + result: null | { + searchQuery: string; + files: Array<{ + // The filepath relative to the project root + filepath: string; + // The search query - Helps to identify the event on the frontend + searchQuery: string; + // A list of exported components + exportedComponents: Array<{ + // the name of the exported component + name: string; + // True, if the exported component is a default export + default: boolean; + }>; + }> | null; + }; + error: null | string; +} + +export function initFileSearchChannel(channel: Channel, options: Options) { + /** + * Listens for a search query event and searches for files in the project + */ + channel.on(FILE_COMPONENT_SEARCH, async (data: Data) => { + try { + const searchQuery = data?.searchQuery; + + if (!searchQuery) { + return; + } + + const frameworkName = await getFrameworkName(options); + + const rendererName = (await extractProperRendererNameFromFramework( + frameworkName + )) as SupportedRenderers; + + const projectRoot = getProjectRoot(); + + const files = await searchFiles({ + searchQuery, + cwd: projectRoot, + }); + + const entries = files.map(async (file) => { + const parser = getParser(rendererName); + + try { + const content = await fs.readFile(path.join(projectRoot, file), 'utf-8'); + const info = await parser.parse(content); + + return { + filepath: file, + exportedComponents: info.exports, + }; + } catch (e) { + return { + filepath: file, + exportedComponents: null, + }; + } + }); + + channel.emit(FILE_COMPONENT_SEARCH_RESULT, { + success: true, + result: { + searchQuery, + files: await Promise.all(entries), + }, + error: null, + } as SearchResult); + } catch (e: any) { + /** + * Emits the search result event with an error message + */ + channel.emit(FILE_COMPONENT_SEARCH_RESULT, { + success: false, + result: null, + error: `An error occurred while searching for components in the project.\n${e?.message}`, + } as SearchResult); + } + }); + + return channel; +} diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.ts index 09a5c95fff05..9f2c147c9a1f 100644 --- a/code/lib/core-server/src/utils/StoryIndexGenerator.ts +++ b/code/lib/core-server/src/utils/StoryIndexGenerator.ts @@ -509,7 +509,7 @@ export class StoryIndexGenerator { // Otherwise the existing entry is created by `autodocs=true` which allowed to be overridden. } else { // If both entries are templates (e.g. you have two CSF files with the same title), then - // we need to merge the entries. We'll use the the first one's name and importPath, + // we need to merge the entries. We'll use the first one's name and importPath, // but ensure we include both as storiesImports so they are both loaded before rendering // the story (for the block & friends) return { diff --git a/code/lib/core-server/src/utils/__search-files-tests__/.gitignore b/code/lib/core-server/src/utils/__search-files-tests__/.gitignore new file mode 100644 index 000000000000..7e4e8f40e4f1 --- /dev/null +++ b/code/lib/core-server/src/utils/__search-files-tests__/.gitignore @@ -0,0 +1 @@ +src/ignored.js \ No newline at end of file diff --git a/code/lib/core-server/src/utils/__search-files-tests__/README.md b/code/lib/core-server/src/utils/__search-files-tests__/README.md new file mode 100644 index 000000000000..926c54d82404 --- /dev/null +++ b/code/lib/core-server/src/utils/__search-files-tests__/README.md @@ -0,0 +1 @@ +The parent directory "\_\_tests\_\_ was created to unit test the search-file functionality diff --git a/code/lib/core-server/src/utils/__search-files-tests__/src/assets/asset.css b/code/lib/core-server/src/utils/__search-files-tests__/src/assets/asset.css new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/code/lib/core-server/src/utils/__search-files-tests__/src/assets/asset.json b/code/lib/core-server/src/utils/__search-files-tests__/src/assets/asset.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/code/lib/core-server/src/utils/__search-files-tests__/src/assets/asset.png b/code/lib/core-server/src/utils/__search-files-tests__/src/assets/asset.png new file mode 100644 index 000000000000..1914264c0878 Binary files /dev/null and b/code/lib/core-server/src/utils/__search-files-tests__/src/assets/asset.png differ diff --git a/code/lib/core-server/src/utils/__search-files-tests__/src/commonjs-module-default.js b/code/lib/core-server/src/utils/__search-files-tests__/src/commonjs-module-default.js new file mode 100644 index 000000000000..aed903ea7003 --- /dev/null +++ b/code/lib/core-server/src/utils/__search-files-tests__/src/commonjs-module-default.js @@ -0,0 +1 @@ +module.exports = require('./commonjs'); diff --git a/code/lib/core-server/src/utils/__search-files-tests__/src/commonjs-module.js b/code/lib/core-server/src/utils/__search-files-tests__/src/commonjs-module.js new file mode 100644 index 000000000000..7146ed966336 --- /dev/null +++ b/code/lib/core-server/src/utils/__search-files-tests__/src/commonjs-module.js @@ -0,0 +1,19 @@ +// named exports detection +module.exports.a = 'a'; + +(function () { + exports.b = 'b'; +})(); + +Object.defineProperty(exports, 'c', { value: 'c' }); +/* exports.d = 'not detected'; */ + +// reexports detection +if (maybe) module.exports = require('./dep1.js'); +if (another) module.exports = require('./dep2.js'); + +// literal exports assignments +module.exports = { a, b: c, d, e: f }; + +// __esModule detection +Object.defineProperty(module.exports, '__esModule', { value: true }); diff --git a/code/lib/core-server/src/utils/__search-files-tests__/src/es-module.js b/code/lib/core-server/src/utils/__search-files-tests__/src/es-module.js new file mode 100644 index 000000000000..34fc39774ae6 --- /dev/null +++ b/code/lib/core-server/src/utils/__search-files-tests__/src/es-module.js @@ -0,0 +1,16 @@ +/* eslint-disable import/no-unresolved */ +import * as ns from 'external2'; + +export var p = 5; + +export function q() {} + +export class C {} + +export { x as externalName } from 'external'; + +export { ns }; + +export default function () { + return 'default'; +} diff --git a/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.cjs b/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.cjs new file mode 100644 index 000000000000..970afab48505 --- /dev/null +++ b/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.cjs @@ -0,0 +1,3 @@ +export default function () { + return 'default'; +} diff --git a/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.cts b/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.cts new file mode 100644 index 000000000000..970afab48505 --- /dev/null +++ b/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.cts @@ -0,0 +1,3 @@ +export default function () { + return 'default'; +} diff --git a/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.js b/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.js new file mode 100644 index 000000000000..970afab48505 --- /dev/null +++ b/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.js @@ -0,0 +1,3 @@ +export default function () { + return 'default'; +} diff --git a/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.jsx b/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.jsx new file mode 100644 index 000000000000..970afab48505 --- /dev/null +++ b/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.jsx @@ -0,0 +1,3 @@ +export default function () { + return 'default'; +} diff --git a/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.mjs b/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.mjs new file mode 100644 index 000000000000..970afab48505 --- /dev/null +++ b/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.mjs @@ -0,0 +1,3 @@ +export default function () { + return 'default'; +} diff --git a/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.mts b/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.mts new file mode 100644 index 000000000000..970afab48505 --- /dev/null +++ b/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.mts @@ -0,0 +1,3 @@ +export default function () { + return 'default'; +} diff --git a/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.ts b/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.ts new file mode 100644 index 000000000000..970afab48505 --- /dev/null +++ b/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.ts @@ -0,0 +1,3 @@ +export default function () { + return 'default'; +} diff --git a/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.tsx b/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.tsx new file mode 100644 index 000000000000..970afab48505 --- /dev/null +++ b/code/lib/core-server/src/utils/__search-files-tests__/src/file-extensions/extension.tsx @@ -0,0 +1,3 @@ +export default function () { + return 'default'; +} diff --git a/code/lib/core-server/src/utils/__search-files-tests__/src/ignored.js b/code/lib/core-server/src/utils/__search-files-tests__/src/ignored.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/code/lib/core-server/src/utils/__search-files-tests__/src/no-export.js b/code/lib/core-server/src/utils/__search-files-tests__/src/no-export.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/code/lib/core-server/src/utils/__search-files-tests__/src/node_modules/file-in-common.js b/code/lib/core-server/src/utils/__search-files-tests__/src/node_modules/file-in-common.js new file mode 100644 index 000000000000..1626d1e76d96 --- /dev/null +++ b/code/lib/core-server/src/utils/__search-files-tests__/src/node_modules/file-in-common.js @@ -0,0 +1,3 @@ +export default () => { + return 'commonjs-default'; +} \ No newline at end of file diff --git a/code/lib/core-server/src/utils/__search-files-tests__/src/tests/some.spec.ts b/code/lib/core-server/src/utils/__search-files-tests__/src/tests/some.spec.ts new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/code/lib/core-server/src/utils/__search-files-tests__/src/tests/some.test.ts b/code/lib/core-server/src/utils/__search-files-tests__/src/tests/some.test.ts new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/code/lib/core-server/src/utils/parser/generic-parser.test.ts b/code/lib/core-server/src/utils/parser/generic-parser.test.ts new file mode 100644 index 000000000000..6d3ff96e15b0 --- /dev/null +++ b/code/lib/core-server/src/utils/parser/generic-parser.test.ts @@ -0,0 +1,70 @@ +import { describe, expect, it } from 'vitest'; +import path from 'path'; +import { GenericParser } from './generic-parser'; +import fs from 'fs'; + +const genericParser = new GenericParser(); + +const TEST_DIR = path.join(__dirname, '..', '__search-files-tests__'); + +describe('generic-parser', () => { + it('should correctly return exports from CommonJS files', async () => { + const content = fs.readFileSync(path.join(TEST_DIR, 'src', 'commonjs-module.js'), 'utf-8'); + const { exports } = await genericParser.parse(content); + + expect(exports).toEqual([ + { + default: false, + name: 'a', + }, + { + default: false, + name: 'b', + }, + { + default: false, + name: 'c', + }, + { + default: false, + name: 'd', + }, + { + default: false, + name: 'e', + }, + ]); + }); + + it('should correctly return exports from ES modules', async () => { + const content = fs.readFileSync(path.join(TEST_DIR, 'src', 'es-module.js'), 'utf-8'); + const { exports } = await genericParser.parse(content); + + expect(exports).toEqual([ + { + default: false, + name: 'p', + }, + { + default: false, + name: 'q', + }, + { + default: false, + name: 'C', + }, + { + default: false, + name: 'externalName', + }, + { + default: false, + name: 'ns', + }, + { + default: true, + name: 'default', + }, + ]); + }); +}); diff --git a/code/lib/core-server/src/utils/parser/generic-parser.ts b/code/lib/core-server/src/utils/parser/generic-parser.ts new file mode 100644 index 000000000000..e297c1e92eed --- /dev/null +++ b/code/lib/core-server/src/utils/parser/generic-parser.ts @@ -0,0 +1,53 @@ +import { parse as parseCjs, init as initCjsParser } from 'cjs-module-lexer'; +import { parse as parseEs } from 'es-module-lexer'; +import assert from 'node:assert'; + +import type { Parser } from './types'; + +/** + * A generic parser that can parse both ES and CJS modules. + */ +export class GenericParser implements Parser { + /** + * Parse the content of a file and return the exports + * @param content The content of the file + * @returns The exports of the file + */ + async parse(content: string) { + try { + // Do NOT remove await here. The types are wrong! It has to be awaited, + // otherwise it will return a Promise> when wasm isn't loaded. + const [, exports] = await parseEs(content); + + assert( + exports.length > 0, + 'No named exports found. Very likely that this is not a ES module.' + ); + + return { + exports: (exports ?? []).map((e) => { + const name = content.substring(e.s, e.e); + return { + name, + default: name === 'default', + }; + }), + }; + // Try to parse as CJS module + } catch { + await initCjsParser(); + + const { exports, reexports } = parseCjs(content); + const filteredExports = [...exports, ...reexports].filter((e: string) => e !== '__esModule'); + + assert(filteredExports.length > 0, 'No named exports found'); + + return { + exports: (filteredExports ?? []).map((name) => ({ + name, + default: name === 'default', + })), + }; + } + } +} diff --git a/code/lib/core-server/src/utils/parser/index.ts b/code/lib/core-server/src/utils/parser/index.ts new file mode 100644 index 000000000000..8f5183a49538 --- /dev/null +++ b/code/lib/core-server/src/utils/parser/index.ts @@ -0,0 +1,15 @@ +import type { SupportedRenderers } from '@storybook/types'; +import { GenericParser } from './generic-parser'; +import type { Parser } from './types'; + +/** + * Get the parser for a given renderer + * @param renderer The renderer to get the parser for + * @returns The parser for the renderer + */ +export function getParser(renderer: SupportedRenderers | null): Parser { + switch (renderer) { + default: + return new GenericParser(); + } +} diff --git a/code/lib/core-server/src/utils/parser/types.ts b/code/lib/core-server/src/utils/parser/types.ts new file mode 100644 index 000000000000..c3c664abc27d --- /dev/null +++ b/code/lib/core-server/src/utils/parser/types.ts @@ -0,0 +1,18 @@ +export type ParserResult = { + exports: Array<{ + name: string; + default: boolean; + }>; +}; + +/** + * A parser that can parse the exports of a file + */ +export interface Parser { + /** + * Parse the content of a file and return the exports + * @param content The content of the file + * @returns The result of the parsing. Contains the exports of the file + */ + parse: (content: string) => Promise; +} diff --git a/code/lib/core-server/src/utils/search-files.test.ts b/code/lib/core-server/src/utils/search-files.test.ts new file mode 100644 index 000000000000..6aaec136df74 --- /dev/null +++ b/code/lib/core-server/src/utils/search-files.test.ts @@ -0,0 +1,86 @@ +import { describe, expect, it } from 'vitest'; +import path from 'path'; +import { searchFiles } from './search-files'; + +describe('search-files', () => { + it('should automatically convert static search to a dynamic glob search', async (t) => { + const files = await searchFiles({ + searchQuery: 'ommonjs', + cwd: path.join(__dirname, '__search-files-tests__'), + }); + + expect(files).toEqual(['src/commonjs-module-default.js', 'src/commonjs-module.js']); + }); + + it('should automatically convert static search to a dynamic glob search (with file extension)', async (t) => { + const files = await searchFiles({ + searchQuery: 'module.js', + cwd: path.join(__dirname, '__search-files-tests__'), + }); + + expect(files).toEqual(['src/commonjs-module.js', 'src/es-module.js']); + }); + + it('should return all files if the search query matches the parent folder', async (t) => { + const files = await searchFiles({ + searchQuery: 'file-extensions', + cwd: path.join(__dirname, '__search-files-tests__'), + }); + + expect(files).toEqual([ + 'src/file-extensions/extension.cjs', + 'src/file-extensions/extension.cts', + 'src/file-extensions/extension.js', + 'src/file-extensions/extension.jsx', + 'src/file-extensions/extension.mjs', + 'src/file-extensions/extension.mts', + 'src/file-extensions/extension.ts', + 'src/file-extensions/extension.tsx', + ]); + }); + + it('should ignore files that do not have the allowed extensions', async (t) => { + const files = await searchFiles({ + searchQuery: 'asset', + cwd: path.join(__dirname, '__search-files-tests__'), + }); + + expect(files).toEqual([]); + }); + + it('should ignore test files (*.spec.*, *.test.*)', async (t) => { + const files = await searchFiles({ + searchQuery: 'tests', + cwd: path.join(__dirname, '__search-files-tests__'), + }); + + expect(files).toEqual([]); + }); + + it('should work with glob search patterns', async (t) => { + const files = await searchFiles({ + searchQuery: '**/commonjs-module.js', + cwd: path.join(__dirname, '__search-files-tests__'), + }); + + expect(files).toEqual(['src/commonjs-module.js']); + }); + + it('should ignore node_modules', async (t) => { + const files = await searchFiles({ + searchQuery: 'file-in-common.js', + cwd: path.join(__dirname, '__search-files-tests__'), + }); + + expect(files).toEqual([]); + }); + + it('should not return files outside of project root', async (t) => { + await expect(() => + searchFiles({ + searchQuery: '../**/*', + cwd: path.join(__dirname, '__search-files-tests__'), + }) + ).rejects.toThrowError(); + }); +}); diff --git a/code/lib/core-server/src/utils/search-files.ts b/code/lib/core-server/src/utils/search-files.ts new file mode 100644 index 000000000000..b6f1bd89ab25 --- /dev/null +++ b/code/lib/core-server/src/utils/search-files.ts @@ -0,0 +1,48 @@ +export type SearchResult = Array; + +/** + * File extensions that should be searched for + */ +const fileExtensions = ['js', 'mjs', 'cjs', 'jsx', 'mts', 'ts', 'tsx', 'cts']; + +/** + * Search for files in a directory that match the search query + * @param searchQuery The search query. This can be a glob pattern + * @param cwd The directory to search in + * @param renderer The renderer to use for parsing the files + * @returns A list of files that match the search query + */ +export async function searchFiles({ + searchQuery, + cwd, +}: { + searchQuery: string; + cwd: string; +}): Promise { + // Dynamically import globby because it is a pure ESM module + const { globby, isDynamicPattern } = await import('globby'); + + const hasSearchSpecialGlobChars = isDynamicPattern(searchQuery, { cwd }); + + const hasFileExtensionRegex = /(\.[a-z]+)$/i; + const searchQueryHasExtension = hasFileExtensionRegex.test(searchQuery); + const fileExtensionsPattern = `{${fileExtensions.join(',')}}`; + + const globbedSearchQuery = hasSearchSpecialGlobChars + ? searchQuery + : searchQueryHasExtension + ? [`**/*${searchQuery}*`, `**/*${searchQuery}*/**`] + : [ + `**/*${searchQuery}*.${fileExtensionsPattern}`, + `**/*${searchQuery}*/**/*.${fileExtensionsPattern}`, + ]; + + const entries = await globby(globbedSearchQuery, { + ignore: ['**/node_modules/**', '**/*.spec.*', '**/*.test.*'], + gitignore: true, + cwd, + objectMode: true, + }); + + return entries.map((entry) => entry.path); +} diff --git a/code/lib/core-webpack/package.json b/code/lib/core-webpack/package.json index 014f67638e2a..48b4c79c4d48 100644 --- a/code/lib/core-webpack/package.json +++ b/code/lib/core-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/core-webpack", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook framework-agnostic API", "keywords": [ "storybook" diff --git a/code/lib/csf-plugin/package.json b/code/lib/csf-plugin/package.json index eef290d037f9..2635decf0e3a 100644 --- a/code/lib/csf-plugin/package.json +++ b/code/lib/csf-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/csf-plugin", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Enrich CSF files via static analysis", "keywords": [ "storybook" diff --git a/code/lib/csf-tools/package.json b/code/lib/csf-tools/package.json index dfdb9038c9a0..ea2fd24cfe15 100644 --- a/code/lib/csf-tools/package.json +++ b/code/lib/csf-tools/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/csf-tools", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Parse and manipulate CSF and Storybook config files", "keywords": [ "storybook" diff --git a/code/lib/docs-tools/package.json b/code/lib/docs-tools/package.json index d368c58150cd..730a861c26e0 100644 --- a/code/lib/docs-tools/package.json +++ b/code/lib/docs-tools/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/docs-tools", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Shared utility functions for frameworks to implement docs", "keywords": [ "storybook" diff --git a/code/lib/instrumenter/package.json b/code/lib/instrumenter/package.json index 164e7301be8e..48fc836d9fdf 100644 --- a/code/lib/instrumenter/package.json +++ b/code/lib/instrumenter/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/instrumenter", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "", "keywords": [ "storybook" diff --git a/code/lib/manager-api/package.json b/code/lib/manager-api/package.json index 89a26f252b81..c4f0d14e6257 100644 --- a/code/lib/manager-api/package.json +++ b/code/lib/manager-api/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/manager-api", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Core Storybook Manager API & Context", "keywords": [ "storybook" diff --git a/code/lib/manager-api/src/version.ts b/code/lib/manager-api/src/version.ts index 6b08678ee926..d7314740b1ba 100644 --- a/code/lib/manager-api/src/version.ts +++ b/code/lib/manager-api/src/version.ts @@ -1 +1 @@ -export const version = '8.1.0-alpha.6'; +export const version = '8.1.0-alpha.7'; diff --git a/code/lib/node-logger/package.json b/code/lib/node-logger/package.json index 756d55e4d0d5..edded56bf9c1 100644 --- a/code/lib/node-logger/package.json +++ b/code/lib/node-logger/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/node-logger", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "", "keywords": [ "storybook" diff --git a/code/lib/preview-api/package.json b/code/lib/preview-api/package.json index 891b2b6a20c4..8ade774be38a 100644 --- a/code/lib/preview-api/package.json +++ b/code/lib/preview-api/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preview-api", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "", "keywords": [ "storybook" diff --git a/code/lib/preview/package.json b/code/lib/preview/package.json index 72fef466fdb5..aaaff8693593 100644 --- a/code/lib/preview/package.json +++ b/code/lib/preview/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preview", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "", "keywords": [ "storybook" diff --git a/code/lib/react-dom-shim/package.json b/code/lib/react-dom-shim/package.json index d54db0558a10..35b3bd868d21 100644 --- a/code/lib/react-dom-shim/package.json +++ b/code/lib/react-dom-shim/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/react-dom-shim", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "", "keywords": [ "storybook" diff --git a/code/lib/router/package.json b/code/lib/router/package.json index 7baf0bdf0b61..58d83e529778 100644 --- a/code/lib/router/package.json +++ b/code/lib/router/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/router", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Core Storybook Router", "keywords": [ "storybook" diff --git a/code/lib/source-loader/package.json b/code/lib/source-loader/package.json index 8f3acc5d0d7b..7bb4e1d5688b 100644 --- a/code/lib/source-loader/package.json +++ b/code/lib/source-loader/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/source-loader", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Source loader", "keywords": [ "lib", diff --git a/code/lib/telemetry/package.json b/code/lib/telemetry/package.json index 49ee940b1a3e..bad5ecca34c1 100644 --- a/code/lib/telemetry/package.json +++ b/code/lib/telemetry/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/telemetry", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Telemetry logging for crash reports and usage statistics", "keywords": [ "storybook" diff --git a/code/lib/test/package.json b/code/lib/test/package.json index 50728901ed5f..e9b34ccb2098 100644 --- a/code/lib/test/package.json +++ b/code/lib/test/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/test", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "", "keywords": [ "storybook" @@ -56,6 +56,7 @@ "util": "^0.12.4" }, "devDependencies": { + "tinyspy": "^2.2.0", "ts-dedent": "^2.2.0", "type-fest": "~2.19", "typescript": "^5.3.2" diff --git a/code/lib/test/src/index.test.ts b/code/lib/test/src/index.test.ts index bed58592c1d6..87f5b2206418 100644 --- a/code/lib/test/src/index.test.ts +++ b/code/lib/test/src/index.test.ts @@ -1,8 +1,46 @@ -import { it } from 'vitest'; -import { expect, fn } from '@storybook/test'; +import { describe, it, test } from 'vitest'; +import { expect, fn, isMockFunction, traverseArgs } from '@storybook/test'; +import { action } from '@storybook/addon-actions'; it('storybook expect and fn can be used in vitest test', () => { const spy = fn(); spy(1); expect(spy).toHaveBeenCalledWith(1); }); + +describe('traverseArgs', () => { + const args = { + deep: { + deeper: { + fnKey: fn(), + actionKey: action('name'), + }, + }, + arg2: Object.freeze({ frozen: true }), + }; + + expect(args.deep.deeper.fnKey.getMockName()).toEqual('spy'); + + const traversed = traverseArgs(args) as typeof args; + + test('The same structure is maintained', () => + expect(traversed).toEqual({ + deep: { + deeper: { + fnKey: args.deep.deeper.fnKey, + actionKey: args.deep.deeper.actionKey, + }, + }, + // We don't mutate frozen objects, but we do insert them back in the tree + arg2: args.arg2, + })); + + test('The mock name is mutated to be the arg key', () => + expect(traversed.deep.deeper.fnKey.getMockName()).toEqual('fnKey')); + + const actionFn = traversed.deep.deeper.actionKey; + + test('Actions are wrapped in a spy', () => expect(isMockFunction(actionFn)).toBeTruthy()); + test('The spy of the action is also matching the arg key ', () => + expect(isMockFunction(actionFn) && actionFn.getMockName()).toEqual('actionKey')); +}); diff --git a/code/lib/test/src/index.ts b/code/lib/test/src/index.ts index 7bd72666f341..1dcd78c457b8 100644 --- a/code/lib/test/src/index.ts +++ b/code/lib/test/src/index.ts @@ -3,7 +3,15 @@ import { type LoaderFunction } from '@storybook/csf'; import chai from 'chai'; import { global } from '@storybook/global'; import { expect as rawExpect } from './expect'; -import { clearAllMocks, resetAllMocks, restoreAllMocks } from './spy'; +import { + clearAllMocks, + fn, + isMockFunction, + onMockCall, + resetAllMocks, + restoreAllMocks, +} from './spy'; +import type { Renderer } from '@storybook/types'; export * from './spy'; @@ -36,6 +44,52 @@ const resetAllMocksLoader: LoaderFunction = ({ parameters }) => { } }; +export const traverseArgs = (value: unknown, depth = 0, key?: string): unknown => { + // Make sure to not get in infinite loops with self referencing args + if (depth > 5) return value; + if (value == null) return value; + if (isMockFunction(value)) { + // Makes sure we get the arg name in the interactions panel + if (key) value.mockName(key); + return value; + } + + // wrap explicit actions in a spy + if ( + typeof value === 'function' && + 'isAction' in value && + value.isAction && + !('implicit' in value && value.implicit) + ) { + const mock = fn(value as any); + if (key) mock.mockName(key); + return mock; + } + + if (Array.isArray(value)) { + depth++; + return value.map((item) => traverseArgs(item, depth)); + } + + if (typeof value === 'object' && value.constructor === Object) { + depth++; + for (const [k, v] of Object.entries(value)) { + if (Object.getOwnPropertyDescriptor(value, k)?.writable) { + // We have to mutate the original object for this to survive HMR. + (value as Record)[k] = traverseArgs(v, depth, k); + } + } + return value; + } + return value; +}; + +const nameSpiesAndWrapActionsInSpies: LoaderFunction = ({ initialArgs }) => { + traverseArgs(initialArgs); +}; + // We are using this as a default Storybook loader, when the test package is used. This avoids the need for optional peer dependency workarounds. // eslint-disable-next-line no-underscore-dangle -(global as any).__STORYBOOK_TEST_LOADERS__ = [resetAllMocksLoader]; +(global as any).__STORYBOOK_TEST_LOADERS__ = [resetAllMocksLoader, nameSpiesAndWrapActionsInSpies]; +// eslint-disable-next-line no-underscore-dangle +(global as any).__STORYBOOK_TEST_ON_MOCK_CALL__ = onMockCall; diff --git a/code/lib/test/src/spy.test.ts b/code/lib/test/src/spy.test.ts new file mode 100644 index 000000000000..5c3dcf13d924 --- /dev/null +++ b/code/lib/test/src/spy.test.ts @@ -0,0 +1,15 @@ +import { it, vi, expect, beforeEach } from 'vitest'; +import { fn, onMockCall } from './spy'; + +const vitestSpy = vi.fn(); + +beforeEach(() => { + const unsubscribe = onMockCall(vitestSpy); + return () => unsubscribe(); +}); + +it('mocks are reactive', () => { + const storybookSpy = fn(); + storybookSpy(1); + expect(vitestSpy).toHaveBeenCalledWith(storybookSpy, [1]); +}); diff --git a/code/lib/test/src/spy.ts b/code/lib/test/src/spy.ts index 3208df77ae76..6a97a19c390e 100644 --- a/code/lib/test/src/spy.ts +++ b/code/lib/test/src/spy.ts @@ -1,17 +1,58 @@ +/* eslint-disable @typescript-eslint/no-shadow */ +import type { MockInstance } from '@vitest/spy'; import { - spyOn, + spyOn as vitestSpyOn, isMockFunction, - fn, + fn as vitestFn, mocks, type MaybeMocked, type MaybeMockedDeep, type MaybePartiallyMocked, type MaybePartiallyMockedDeep, } from '@vitest/spy'; +import type { SpyInternalImpl } from 'tinyspy'; +import * as tinyspy from 'tinyspy'; export type * from '@vitest/spy'; -export { spyOn, isMockFunction, fn, mocks }; +export { isMockFunction, mocks }; + +type Listener = (mock: MockInstance, args: unknown[]) => void; +const listeners = new Set(); + +export function onMockCall(callback: Listener): () => void { + listeners.add(callback); + return () => void listeners.delete(callback); +} + +// @ts-expect-error Make sure we export the exact same type as @vitest/spy +export const spyOn: typeof vitestSpyOn = (...args) => { + const mock = vitestSpyOn(...(args as Parameters)); + return reactiveMock(mock); +}; + +// @ts-expect-error Make sure we export the exact same type as @vitest/spy +export const fn: typeof vitestFn = (implementation) => { + const mock = implementation ? vitestFn(implementation) : vitestFn(); + return reactiveMock(mock); +}; + +function reactiveMock(mock: MockInstance) { + const reactive = listenWhenCalled(mock); + const originalMockImplementation = reactive.mockImplementation.bind(null); + reactive.mockImplementation = (fn) => listenWhenCalled(originalMockImplementation(fn)); + return reactive; +} + +function listenWhenCalled(mock: MockInstance) { + const state = tinyspy.getInternalState(mock as unknown as SpyInternalImpl); + const impl = state.impl?.bind(null); + state.willCall((...args) => { + listeners.forEach((listener) => listener(mock, args)); + return impl?.(...args); + }); + return mock; +} /** * Calls [`.mockClear()`](https://vitest.dev/api/mock#mockclear) on every mocked function. This will only empty `.mock` state, it will not reset implementation. diff --git a/code/lib/theming/package.json b/code/lib/theming/package.json index 3af30d494a0b..d0b87b51c0c8 100644 --- a/code/lib/theming/package.json +++ b/code/lib/theming/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/theming", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Core Storybook Components", "keywords": [ "storybook" diff --git a/code/lib/types/package.json b/code/lib/types/package.json index 6d15f4c0d48c..94f137bfc62b 100644 --- a/code/lib/types/package.json +++ b/code/lib/types/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/types", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Core Storybook TS Types", "keywords": [ "storybook" diff --git a/code/lib/types/src/index.ts b/code/lib/types/src/index.ts index 21a5f9ea000c..523f2c3c184b 100644 --- a/code/lib/types/src/index.ts +++ b/code/lib/types/src/index.ts @@ -10,3 +10,4 @@ export * from './modules/indexer'; export * from './modules/composedStory'; export * from './modules/channelApi'; export * from './modules/frameworks'; +export * from './modules/renderers'; diff --git a/code/lib/types/src/modules/renderers.ts b/code/lib/types/src/modules/renderers.ts new file mode 100644 index 000000000000..4fcf0be99d87 --- /dev/null +++ b/code/lib/types/src/modules/renderers.ts @@ -0,0 +1,14 @@ +// Should match @storybook/ +export type SupportedRenderers = + | 'react' + | 'react-native' + | 'vue3' + | 'angular' + | 'ember' + | 'preact' + | 'svelte' + | 'qwik' + | 'html' + | 'web-components' + | 'server' + | 'solid'; diff --git a/code/package.json b/code/package.json index adca4665bd72..34840b62567a 100644 --- a/code/package.json +++ b/code/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/root", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "private": true, "description": "Storybook root", "homepage": "https://storybook.js.org/", diff --git a/code/presets/create-react-app/package.json b/code/presets/create-react-app/package.json index 4cb10db12770..3f7e99928679 100644 --- a/code/presets/create-react-app/package.json +++ b/code/presets/create-react-app/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-create-react-app", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for Create React App preset", "keywords": [ "storybook" diff --git a/code/presets/html-webpack/package.json b/code/presets/html-webpack/package.json index 6e9dc9ad9306..5209a5cabfb1 100644 --- a/code/presets/html-webpack/package.json +++ b/code/presets/html-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-html-webpack", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for HTML: View HTML snippets in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/presets/preact-webpack/package.json b/code/presets/preact-webpack/package.json index f85aff3fad1e..71d1cad823e2 100644 --- a/code/presets/preact-webpack/package.json +++ b/code/presets/preact-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-preact-webpack", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for Preact: Develop Preact Component in isolation.", "keywords": [ "storybook" diff --git a/code/presets/react-webpack/package.json b/code/presets/react-webpack/package.json index 7be3dd470be4..33e1c1abc278 100644 --- a/code/presets/react-webpack/package.json +++ b/code/presets/react-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-react-webpack", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for React: Develop React Component in isolation with Hot Reloading", "keywords": [ "storybook" diff --git a/code/presets/server-webpack/package.json b/code/presets/server-webpack/package.json index 4a3693fe29e6..79d58fccb7c3 100644 --- a/code/presets/server-webpack/package.json +++ b/code/presets/server-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-server-webpack", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for Server: View HTML snippets from a server in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/presets/svelte-webpack/package.json b/code/presets/svelte-webpack/package.json index dcbece766faf..7631ae956d49 100644 --- a/code/presets/svelte-webpack/package.json +++ b/code/presets/svelte-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-svelte-webpack", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for Svelte: Develop Svelte Component in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/presets/vue3-webpack/package.json b/code/presets/vue3-webpack/package.json index 29e5627ef0ff..500f80f54b1c 100644 --- a/code/presets/vue3-webpack/package.json +++ b/code/presets/vue3-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-vue3-webpack", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook for Vue 3: Develop Vue 3 Components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/renderers/html/package.json b/code/renderers/html/package.json index a64b2b6a6c45..def5200f6755 100644 --- a/code/renderers/html/package.json +++ b/code/renderers/html/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/html", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook HTML renderer", "keywords": [ "storybook" diff --git a/code/renderers/preact/package.json b/code/renderers/preact/package.json index 8b3453caa34e..09fc253432d9 100644 --- a/code/renderers/preact/package.json +++ b/code/renderers/preact/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preact", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook Preact renderer", "keywords": [ "storybook" diff --git a/code/renderers/react/package.json b/code/renderers/react/package.json index 6ee3ef51a284..c71f8628de5e 100644 --- a/code/renderers/react/package.json +++ b/code/renderers/react/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/react", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook React renderer", "keywords": [ "storybook" diff --git a/code/renderers/react/src/__test__/portable-stories.test.tsx b/code/renderers/react/src/__test__/portable-stories.test.tsx index 6c3c38055065..aab2585743d5 100644 --- a/code/renderers/react/src/__test__/portable-stories.test.tsx +++ b/code/renderers/react/src/__test__/portable-stories.test.tsx @@ -4,8 +4,6 @@ import { vi, it, expect, afterEach, describe } from 'vitest'; import { render, screen, cleanup } from '@testing-library/react'; import { addons } from '@storybook/preview-api'; -import * as addonInteractionsPreview from '@storybook/addon-interactions/preview'; - import * as addonActionsPreview from '@storybook/addon-actions/preview'; import type { Meta } from '@storybook/react'; import { expectTypeOf } from 'expect-type'; @@ -90,9 +88,9 @@ describe('projectAnnotations', () => { expect(buttonElement).not.toBeNull(); }); - it('has spies when addon-interactions annotations are added', async () => { - //@ts-expect-error TODO investigate - const Story = composeStory(stories.WithActionArg, stories.default, addonInteractionsPreview); + it('explicit action are spies when the test loader is loaded', async () => { + const Story = composeStory(stories.WithActionArg, stories.default); + await Story.load(); expect(vi.mocked(Story.args.someActionArg!).mock).toBeDefined(); const { container } = render(); diff --git a/code/renderers/server/package.json b/code/renderers/server/package.json index b41cf05c4164..5b8dac785d41 100644 --- a/code/renderers/server/package.json +++ b/code/renderers/server/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/server", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook Server renderer", "keywords": [ "storybook" diff --git a/code/renderers/svelte/package.json b/code/renderers/svelte/package.json index 04d13981993a..8240c18f4359 100644 --- a/code/renderers/svelte/package.json +++ b/code/renderers/svelte/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/svelte", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook Svelte renderer", "keywords": [ "storybook" diff --git a/code/renderers/vue3/package.json b/code/renderers/vue3/package.json index b8ec79e82964..b50d57c870ca 100644 --- a/code/renderers/vue3/package.json +++ b/code/renderers/vue3/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/vue3", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook Vue 3 renderer", "keywords": [ "storybook" diff --git a/code/renderers/web-components/package.json b/code/renderers/web-components/package.json index c98295a70951..0b14130edbac 100644 --- a/code/renderers/web-components/package.json +++ b/code/renderers/web-components/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/web-components", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook web-components renderer", "keywords": [ "lit", diff --git a/code/ui/blocks/package.json b/code/ui/blocks/package.json index 3984834d2a14..b09a79267482 100644 --- a/code/ui/blocks/package.json +++ b/code/ui/blocks/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/blocks", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Storybook Doc Blocks", "keywords": [ "storybook" diff --git a/code/ui/components/package.json b/code/ui/components/package.json index 7fecbf93625a..613e78bbf998 100644 --- a/code/ui/components/package.json +++ b/code/ui/components/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/components", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Core Storybook Components", "keywords": [ "storybook" diff --git a/code/ui/manager/package.json b/code/ui/manager/package.json index d67d082c5741..a5ccc976a202 100644 --- a/code/ui/manager/package.json +++ b/code/ui/manager/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/manager", - "version": "8.1.0-alpha.6", + "version": "8.1.0-alpha.7", "description": "Core Storybook UI", "keywords": [ "storybook" diff --git a/code/ui/manager/src/globals/exports.ts b/code/ui/manager/src/globals/exports.ts index 73881b9b5919..794741a69e8b 100644 --- a/code/ui/manager/src/globals/exports.ts +++ b/code/ui/manager/src/globals/exports.ts @@ -136,6 +136,8 @@ export default { 'CURRENT_STORY_WAS_SET', 'DOCS_PREPARED', 'DOCS_RENDERED', + 'FILE_COMPONENT_SEARCH', + 'FILE_COMPONENT_SEARCH_RESULT', 'FORCE_REMOUNT', 'FORCE_RE_RENDER', 'GLOBALS_UPDATED', diff --git a/code/yarn.lock b/code/yarn.lock index 82aebb4d724d..35c5ab163a6d 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -2213,6 +2213,15 @@ __metadata: languageName: node linkType: hard +"@emnapi/runtime@npm:^1.1.0": + version: 1.1.1 + resolution: "@emnapi/runtime@npm:1.1.1" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/c11ee57abf0ec643e64ccdace4b4fcc0b0c7b1117a191f969e84ae3669841aa90d2c17fa35b73f5a66fc0c843c8caca7bf11187faaeaa526bcfb7dbfb9b85de9 + languageName: node + linkType: hard + "@emotion/babel-plugin@npm:^11.11.0": version: 11.11.0 resolution: "@emotion/babel-plugin@npm:11.11.0" @@ -3143,6 +3152,181 @@ __metadata: languageName: node linkType: hard +"@img/sharp-darwin-arm64@npm:0.33.3": + version: 0.33.3 + resolution: "@img/sharp-darwin-arm64@npm:0.33.3" + dependencies: + "@img/sharp-libvips-darwin-arm64": "npm:1.0.2" + dependenciesMeta: + "@img/sharp-libvips-darwin-arm64": + optional: true + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-darwin-x64@npm:0.33.3": + version: 0.33.3 + resolution: "@img/sharp-darwin-x64@npm:0.33.3" + dependencies: + "@img/sharp-libvips-darwin-x64": "npm:1.0.2" + dependenciesMeta: + "@img/sharp-libvips-darwin-x64": + optional: true + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@img/sharp-libvips-darwin-arm64@npm:1.0.2": + version: 1.0.2 + resolution: "@img/sharp-libvips-darwin-arm64@npm:1.0.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-libvips-darwin-x64@npm:1.0.2": + version: 1.0.2 + resolution: "@img/sharp-libvips-darwin-x64@npm:1.0.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-arm64@npm:1.0.2": + version: 1.0.2 + resolution: "@img/sharp-libvips-linux-arm64@npm:1.0.2" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-arm@npm:1.0.2": + version: 1.0.2 + resolution: "@img/sharp-libvips-linux-arm@npm:1.0.2" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-s390x@npm:1.0.2": + version: 1.0.2 + resolution: "@img/sharp-libvips-linux-s390x@npm:1.0.2" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-x64@npm:1.0.2": + version: 1.0.2 + resolution: "@img/sharp-libvips-linux-x64@npm:1.0.2" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linuxmusl-arm64@npm:1.0.2": + version: 1.0.2 + resolution: "@img/sharp-libvips-linuxmusl-arm64@npm:1.0.2" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-libvips-linuxmusl-x64@npm:1.0.2": + version: 1.0.2 + resolution: "@img/sharp-libvips-linuxmusl-x64@npm:1.0.2" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-linux-arm64@npm:0.33.3": + version: 0.33.3 + resolution: "@img/sharp-linux-arm64@npm:0.33.3" + dependencies: + "@img/sharp-libvips-linux-arm64": "npm:1.0.2" + dependenciesMeta: + "@img/sharp-libvips-linux-arm64": + optional: true + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-arm@npm:0.33.3": + version: 0.33.3 + resolution: "@img/sharp-linux-arm@npm:0.33.3" + dependencies: + "@img/sharp-libvips-linux-arm": "npm:1.0.2" + dependenciesMeta: + "@img/sharp-libvips-linux-arm": + optional: true + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-s390x@npm:0.33.3": + version: 0.33.3 + resolution: "@img/sharp-linux-s390x@npm:0.33.3" + dependencies: + "@img/sharp-libvips-linux-s390x": "npm:1.0.2" + dependenciesMeta: + "@img/sharp-libvips-linux-s390x": + optional: true + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-x64@npm:0.33.3": + version: 0.33.3 + resolution: "@img/sharp-linux-x64@npm:0.33.3" + dependencies: + "@img/sharp-libvips-linux-x64": "npm:1.0.2" + dependenciesMeta: + "@img/sharp-libvips-linux-x64": + optional: true + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linuxmusl-arm64@npm:0.33.3": + version: 0.33.3 + resolution: "@img/sharp-linuxmusl-arm64@npm:0.33.3" + dependencies: + "@img/sharp-libvips-linuxmusl-arm64": "npm:1.0.2" + dependenciesMeta: + "@img/sharp-libvips-linuxmusl-arm64": + optional: true + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-linuxmusl-x64@npm:0.33.3": + version: 0.33.3 + resolution: "@img/sharp-linuxmusl-x64@npm:0.33.3" + dependencies: + "@img/sharp-libvips-linuxmusl-x64": "npm:1.0.2" + dependenciesMeta: + "@img/sharp-libvips-linuxmusl-x64": + optional: true + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-wasm32@npm:0.33.3": + version: 0.33.3 + resolution: "@img/sharp-wasm32@npm:0.33.3" + dependencies: + "@emnapi/runtime": "npm:^1.1.0" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@img/sharp-win32-ia32@npm:0.33.3": + version: 0.33.3 + resolution: "@img/sharp-win32-ia32@npm:0.33.3" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@img/sharp-win32-x64@npm:0.33.3": + version: 0.33.3 + resolution: "@img/sharp-win32-x64@npm:0.33.3" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@isaacs/cliui@npm:^8.0.2": version: 8.0.2 resolution: "@isaacs/cliui@npm:8.0.2" @@ -5355,7 +5539,7 @@ __metadata: util: "npm:^0.12.4" util-deprecate: "npm:^1.0.2" webpack: "npm:5" - webpack-dev-middleware: "npm:^6.1.1" + webpack-dev-middleware: "npm:^6.1.2" webpack-hot-middleware: "npm:^2.25.1" webpack-virtual-modules: "npm:^0.5.0" peerDependenciesMeta: @@ -5596,9 +5780,11 @@ __metadata: better-opn: "npm:^3.0.2" boxen: "npm:^7.1.1" chalk: "npm:^4.1.0" + cjs-module-lexer: "npm:^1.2.3" cli-table3: "npm:^0.6.1" compression: "npm:^1.7.4" detect-port: "npm:^1.3.0" + es-module-lexer: "npm:^1.5.0" express: "npm:^4.17.3" fs-extra: "npm:^11.1.0" globby: "npm:^14.0.1" @@ -5969,7 +6155,7 @@ __metadata: resolve-url-loader: "npm:^5.0.0" sass-loader: "npm:^12.4.0" semver: "npm:^7.3.5" - sharp: "npm:^0.32.6" + sharp: "npm:^0.33.3" style-loader: "npm:^3.3.1" styled-jsx: "npm:5.1.1" ts-dedent: "npm:^2.0.0" @@ -5982,6 +6168,9 @@ __metadata: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 webpack: ^5.0.0 + dependenciesMeta: + sharp: + optional: true peerDependenciesMeta: typescript: optional: true @@ -6667,6 +6856,7 @@ __metadata: "@vitest/expect": "npm:1.3.1" "@vitest/spy": "npm:^1.3.1" chai: "npm:^4.4.1" + tinyspy: "npm:^2.2.0" ts-dedent: "npm:^2.2.0" type-fest: "npm:~2.19" typescript: "npm:^5.3.2" @@ -9998,13 +10188,6 @@ __metadata: languageName: node linkType: hard -"b4a@npm:^1.6.4": - version: 1.6.4 - resolution: "b4a@npm:1.6.4" - checksum: 10c0/a0af707430c3643fd8d9418c732849d3626f1c9281489e021fcad969fb4808fb9f67b224de36b59c9c3b5a13d853482fee0c0eb53f7aec12d540fa67f63648b6 - languageName: node - linkType: hard - "babel-core@npm:^7.0.0-bridge.0": version: 7.0.0-bridge.0 resolution: "babel-core@npm:7.0.0-bridge.0" @@ -12520,13 +12703,6 @@ __metadata: languageName: node linkType: hard -"deep-extend@npm:^0.6.0": - version: 0.6.0 - resolution: "deep-extend@npm:0.6.0" - checksum: 10c0/1c6b0abcdb901e13a44c7d699116d3d4279fdb261983122a3783e7273844d5f2537dc2e1c454a23fcf645917f93fbf8d07101c1d03c015a87faa662755212566 - languageName: node - linkType: hard - "deep-is@npm:^0.1.3": version: 0.1.4 resolution: "deep-is@npm:0.1.4" @@ -12743,10 +12919,10 @@ __metadata: languageName: node linkType: hard -"detect-libc@npm:^2.0.0, detect-libc@npm:^2.0.2": - version: 2.0.2 - resolution: "detect-libc@npm:2.0.2" - checksum: 10c0/a9f4ffcd2701525c589617d98afe5a5d0676c8ea82bcc4ed6f3747241b79f781d36437c59a5e855254c864d36a3e9f8276568b6b531c28d6e53b093a15703f11 +"detect-libc@npm:^2.0.3": + version: 2.0.3 + resolution: "detect-libc@npm:2.0.3" + checksum: 10c0/88095bda8f90220c95f162bf92cad70bd0e424913e655c20578600e35b91edc261af27531cf160a331e185c0ced93944bc7e09939143225f56312d7fd800fdb7 languageName: node linkType: hard @@ -14569,13 +14745,6 @@ __metadata: languageName: node linkType: hard -"expand-template@npm:^2.0.3": - version: 2.0.3 - resolution: "expand-template@npm:2.0.3" - checksum: 10c0/1c9e7afe9acadf9d373301d27f6a47b34e89b3391b1ef38b7471d381812537ef2457e620ae7f819d2642ce9c43b189b3583813ec395e2938319abe356a9b2f51 - languageName: node - linkType: hard - "expand-tilde@npm:^2.0.2": version: 2.0.2 resolution: "expand-tilde@npm:2.0.2" @@ -14705,13 +14874,6 @@ __metadata: languageName: node linkType: hard -"fast-fifo@npm:^1.1.0, fast-fifo@npm:^1.2.0": - version: 1.3.2 - resolution: "fast-fifo@npm:1.3.2" - checksum: 10c0/d53f6f786875e8b0529f784b59b4b05d4b5c31c651710496440006a398389a579c8dbcd2081311478b5bf77f4b0b21de69109c5a4eabea9d8e8783d1eb864e4c - languageName: node - linkType: hard - "fast-glob@npm:3.2.7": version: 3.2.7 resolution: "fast-glob@npm:3.2.7" @@ -15762,13 +15924,6 @@ __metadata: languageName: node linkType: hard -"github-from-package@npm:0.0.0": - version: 0.0.0 - resolution: "github-from-package@npm:0.0.0" - checksum: 10c0/737ee3f52d0a27e26332cde85b533c21fcdc0b09fb716c3f8e522cfaa9c600d4a631dec9fcde179ec9d47cca89017b7848ed4d6ae6b6b78f936c06825b1fcc12 - languageName: node - linkType: hard - "github-release-from-changelog@npm:^2.1.1": version: 2.1.1 resolution: "github-release-from-changelog@npm:2.1.1" @@ -17009,7 +17164,7 @@ __metadata: languageName: node linkType: hard -"ini@npm:^1.3.4, ini@npm:^1.3.5, ini@npm:~1.3.0": +"ini@npm:^1.3.4, ini@npm:^1.3.5": version: 1.3.8 resolution: "ini@npm:1.3.8" checksum: 10c0/ec93838d2328b619532e4f1ff05df7909760b6f66d9c9e2ded11e5c1897d6f2f9980c54dd638f88654b00919ce31e827040631eab0a3969e4d1abefa0719516a @@ -20553,7 +20708,7 @@ __metadata: languageName: node linkType: hard -"minimist@npm:^1.0.0, minimist@npm:^1.2.0, minimist@npm:^1.2.3, minimist@npm:^1.2.5, minimist@npm:^1.2.6, minimist@npm:^1.2.8": +"minimist@npm:^1.0.0, minimist@npm:^1.2.0, minimist@npm:^1.2.5, minimist@npm:^1.2.6, minimist@npm:^1.2.8": version: 1.2.8 resolution: "minimist@npm:1.2.8" checksum: 10c0/19d3fcdca050087b84c2029841a093691a91259a47def2f18222f41e7645a0b7c44ef4b40e88a1e58a40c84d2ef0ee6047c55594d298146d0eb3f6b737c20ce6 @@ -20688,7 +20843,7 @@ __metadata: languageName: node linkType: hard -"mkdirp-classic@npm:^0.5.2, mkdirp-classic@npm:^0.5.3": +"mkdirp-classic@npm:^0.5.2": version: 0.5.3 resolution: "mkdirp-classic@npm:0.5.3" checksum: 10c0/95371d831d196960ddc3833cc6907e6b8f67ac5501a6582f47dfae5eb0f092e9f8ce88e0d83afcae95d6e2b61a01741ba03714eeafb6f7a6e9dcc158ac85b168 @@ -20869,13 +21024,6 @@ __metadata: languageName: node linkType: hard -"napi-build-utils@npm:^1.0.1": - version: 1.0.2 - resolution: "napi-build-utils@npm:1.0.2" - checksum: 10c0/37fd2cd0ff2ad20073ce78d83fd718a740d568b225924e753ae51cb69d68f330c80544d487e5e5bd18e28702ed2ca469c2424ad948becd1862c1b0209542b2e9 - languageName: node - linkType: hard - "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -20993,15 +21141,6 @@ __metadata: languageName: node linkType: hard -"node-abi@npm:^3.3.0": - version: 3.51.0 - resolution: "node-abi@npm:3.51.0" - dependencies: - semver: "npm:^7.3.5" - checksum: 10c0/2f0a56a9923bd21ff13650b6e8d2caebda211795761b40ae0ec3ea08e6687f6722535302cde87c928d6959a6afec196db3c54f8ae2db0bbaa401459dcf0f073c - languageName: node - linkType: hard - "node-abort-controller@npm:^3.0.1": version: 3.1.1 resolution: "node-abort-controller@npm:3.1.1" @@ -21018,15 +21157,6 @@ __metadata: languageName: node linkType: hard -"node-addon-api@npm:^6.1.0": - version: 6.1.0 - resolution: "node-addon-api@npm:6.1.0" - dependencies: - node-gyp: "npm:latest" - checksum: 10c0/d2699c4ad15740fd31482a3b6fca789af7723ab9d393adc6ac45250faaee72edad8f0b10b2b9d087df0de93f1bdc16d97afdd179b26b9ebc9ed68b569faa4bac - languageName: node - linkType: hard - "node-cleanup@npm:^2.1.2": version: 2.1.2 resolution: "node-cleanup@npm:2.1.2" @@ -22857,28 +22987,6 @@ __metadata: languageName: node linkType: hard -"prebuild-install@npm:^7.1.1": - version: 7.1.1 - resolution: "prebuild-install@npm:7.1.1" - dependencies: - detect-libc: "npm:^2.0.0" - expand-template: "npm:^2.0.3" - github-from-package: "npm:0.0.0" - minimist: "npm:^1.2.3" - mkdirp-classic: "npm:^0.5.3" - napi-build-utils: "npm:^1.0.1" - node-abi: "npm:^3.3.0" - pump: "npm:^3.0.0" - rc: "npm:^1.2.7" - simple-get: "npm:^4.0.0" - tar-fs: "npm:^2.0.0" - tunnel-agent: "npm:^0.6.0" - bin: - prebuild-install: bin.js - checksum: 10c0/6dc70f36b0f4adcb2fe0ed38d874ab28b571fb1a9725d769e8ba3f64a15831e58462de09f3e6e64569bcc4a3e03b9328b56faa0d45fe10ae1574478814536c76 - languageName: node - linkType: hard - "prelude-ls@npm:^1.2.1": version: 1.2.1 resolution: "prelude-ls@npm:1.2.1" @@ -23384,13 +23492,6 @@ __metadata: languageName: node linkType: hard -"queue-tick@npm:^1.0.1": - version: 1.0.1 - resolution: "queue-tick@npm:1.0.1" - checksum: 10c0/0db998e2c9b15215317dbcf801e9b23e6bcde4044e115155dae34f8e7454b9a783f737c9a725528d677b7a66c775eb7a955cf144fe0b87f62b575ce5bfd515a9 - languageName: node - linkType: hard - "queue@npm:6.0.2": version: 6.0.2 resolution: "queue@npm:6.0.2" @@ -23472,20 +23573,6 @@ __metadata: languageName: node linkType: hard -"rc@npm:^1.2.7": - version: 1.2.8 - resolution: "rc@npm:1.2.8" - dependencies: - deep-extend: "npm:^0.6.0" - ini: "npm:~1.3.0" - minimist: "npm:^1.2.0" - strip-json-comments: "npm:~2.0.1" - bin: - rc: ./cli.js - checksum: 10c0/24a07653150f0d9ac7168e52943cc3cb4b7a22c0e43c7dff3219977c2fdca5a2760a304a029c20811a0e79d351f57d46c9bde216193a0f73978496afc2b85b15 - languageName: node - linkType: hard - "react-colorful@npm:^5.1.2": version: 5.6.1 resolution: "react-colorful@npm:5.6.1" @@ -25304,7 +25391,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.6.0, semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.2.1, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.3, semver@npm:^7.5.4": +"semver@npm:7.6.0, semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.2.1, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0": version: 7.6.0 resolution: "semver@npm:7.6.0" dependencies: @@ -25475,20 +25562,72 @@ __metadata: languageName: node linkType: hard -"sharp@npm:^0.32.6": - version: 0.32.6 - resolution: "sharp@npm:0.32.6" - dependencies: +"sharp@npm:^0.33.3": + version: 0.33.3 + resolution: "sharp@npm:0.33.3" + dependencies: + "@img/sharp-darwin-arm64": "npm:0.33.3" + "@img/sharp-darwin-x64": "npm:0.33.3" + "@img/sharp-libvips-darwin-arm64": "npm:1.0.2" + "@img/sharp-libvips-darwin-x64": "npm:1.0.2" + "@img/sharp-libvips-linux-arm": "npm:1.0.2" + "@img/sharp-libvips-linux-arm64": "npm:1.0.2" + "@img/sharp-libvips-linux-s390x": "npm:1.0.2" + "@img/sharp-libvips-linux-x64": "npm:1.0.2" + "@img/sharp-libvips-linuxmusl-arm64": "npm:1.0.2" + "@img/sharp-libvips-linuxmusl-x64": "npm:1.0.2" + "@img/sharp-linux-arm": "npm:0.33.3" + "@img/sharp-linux-arm64": "npm:0.33.3" + "@img/sharp-linux-s390x": "npm:0.33.3" + "@img/sharp-linux-x64": "npm:0.33.3" + "@img/sharp-linuxmusl-arm64": "npm:0.33.3" + "@img/sharp-linuxmusl-x64": "npm:0.33.3" + "@img/sharp-wasm32": "npm:0.33.3" + "@img/sharp-win32-ia32": "npm:0.33.3" + "@img/sharp-win32-x64": "npm:0.33.3" color: "npm:^4.2.3" - detect-libc: "npm:^2.0.2" - node-addon-api: "npm:^6.1.0" - node-gyp: "npm:latest" - prebuild-install: "npm:^7.1.1" - semver: "npm:^7.5.4" - simple-get: "npm:^4.0.1" - tar-fs: "npm:^3.0.4" - tunnel-agent: "npm:^0.6.0" - checksum: 10c0/f6a756fec5051ef2f9341e0543cde1da4e822982dd5398010baad92e2262bd177e08b753eb19b2fbee30f2fcb0e8756f24088fafc48293a364e9a8f8dc65a300 + detect-libc: "npm:^2.0.3" + semver: "npm:^7.6.0" + dependenciesMeta: + "@img/sharp-darwin-arm64": + optional: true + "@img/sharp-darwin-x64": + optional: true + "@img/sharp-libvips-darwin-arm64": + optional: true + "@img/sharp-libvips-darwin-x64": + optional: true + "@img/sharp-libvips-linux-arm": + optional: true + "@img/sharp-libvips-linux-arm64": + optional: true + "@img/sharp-libvips-linux-s390x": + optional: true + "@img/sharp-libvips-linux-x64": + optional: true + "@img/sharp-libvips-linuxmusl-arm64": + optional: true + "@img/sharp-libvips-linuxmusl-x64": + optional: true + "@img/sharp-linux-arm": + optional: true + "@img/sharp-linux-arm64": + optional: true + "@img/sharp-linux-s390x": + optional: true + "@img/sharp-linux-x64": + optional: true + "@img/sharp-linuxmusl-arm64": + optional: true + "@img/sharp-linuxmusl-x64": + optional: true + "@img/sharp-wasm32": + optional: true + "@img/sharp-win32-ia32": + optional: true + "@img/sharp-win32-x64": + optional: true + checksum: 10c0/12f5203426595b4e64c807162a6d52358b591d25fbb414a51fe38861584759fba38485be951ed98d15be3dfe21f2def5336f78ca35bf8bbd22d88cc78ca03f2a languageName: node linkType: hard @@ -25586,24 +25725,6 @@ __metadata: languageName: node linkType: hard -"simple-concat@npm:^1.0.0": - version: 1.0.1 - resolution: "simple-concat@npm:1.0.1" - checksum: 10c0/62f7508e674414008910b5397c1811941d457dfa0db4fd5aa7fa0409eb02c3609608dfcd7508cace75b3a0bf67a2a77990711e32cd213d2c76f4fd12ee86d776 - languageName: node - linkType: hard - -"simple-get@npm:^4.0.0, simple-get@npm:^4.0.1": - version: 4.0.1 - resolution: "simple-get@npm:4.0.1" - dependencies: - decompress-response: "npm:^6.0.0" - once: "npm:^1.3.1" - simple-concat: "npm:^1.0.0" - checksum: 10c0/b0649a581dbca741babb960423248899203165769747142033479a7dc5e77d7b0fced0253c731cd57cf21e31e4d77c9157c3069f4448d558ebc96cf9e1eebcf0 - languageName: node - linkType: hard - "simple-swizzle@npm:^0.2.2": version: 0.2.2 resolution: "simple-swizzle@npm:0.2.2" @@ -26155,16 +26276,6 @@ __metadata: languageName: node linkType: hard -"streamx@npm:^2.15.0": - version: 2.15.1 - resolution: "streamx@npm:2.15.1" - dependencies: - fast-fifo: "npm:^1.1.0" - queue-tick: "npm:^1.0.1" - checksum: 10c0/e3b0e997726a2a499e1069efea7d720e54fc262011dfcb32e6704f90b5a31bb55b8f48964649d787be03d8718dcf9aa413d24ce48823d92fcbad06a3c337ec61 - languageName: node - linkType: hard - "strict-uri-encode@npm:^2.0.0": version: 2.0.0 resolution: "strict-uri-encode@npm:2.0.0" @@ -26395,13 +26506,6 @@ __metadata: languageName: node linkType: hard -"strip-json-comments@npm:~2.0.1": - version: 2.0.1 - resolution: "strip-json-comments@npm:2.0.1" - checksum: 10c0/b509231cbdee45064ff4f9fd73609e2bcc4e84a4d508e9dd0f31f70356473fde18abfb5838c17d56fb236f5a06b102ef115438de0600b749e818a35fbbc48c43 - languageName: node - linkType: hard - "strip-literal@npm:^1.3.0": version: 1.3.0 resolution: "strip-literal@npm:1.3.0" @@ -26749,7 +26853,7 @@ __metadata: languageName: node linkType: hard -"tar-fs@npm:^2.0.0, tar-fs@npm:^2.1.1": +"tar-fs@npm:^2.1.1": version: 2.1.1 resolution: "tar-fs@npm:2.1.1" dependencies: @@ -26761,17 +26865,6 @@ __metadata: languageName: node linkType: hard -"tar-fs@npm:^3.0.4": - version: 3.0.4 - resolution: "tar-fs@npm:3.0.4" - dependencies: - mkdirp-classic: "npm:^0.5.2" - pump: "npm:^3.0.0" - tar-stream: "npm:^3.1.5" - checksum: 10c0/120f026d891e5b4f7147a5ae5816e3a9b7f2c5b4ca61714dab3fe1244961607dccca40c11cafc584e625838c57d1308da5bb28b13d70b85ab566bc4c9f1c88b1 - languageName: node - linkType: hard - "tar-stream@npm:^2.1.4, tar-stream@npm:~2.2.0": version: 2.2.0 resolution: "tar-stream@npm:2.2.0" @@ -26785,17 +26878,6 @@ __metadata: languageName: node linkType: hard -"tar-stream@npm:^3.1.5": - version: 3.1.6 - resolution: "tar-stream@npm:3.1.6" - dependencies: - b4a: "npm:^1.6.4" - fast-fifo: "npm:^1.2.0" - streamx: "npm:^2.15.0" - checksum: 10c0/7d52d1a56eb25b8434c9544becb737eb6c4f0ed19d205e739fdd2537ad8d1d623a6c93f7f8e58d9028cb0cdf86c0d8b67164e070cd1702cc78b8ab7cba0f3702 - languageName: node - linkType: hard - "tar@npm:^6.1.11, tar@npm:^6.1.2, tar@npm:^6.2.0": version: 6.2.0 resolution: "tar@npm:6.2.0" @@ -27408,15 +27490,6 @@ __metadata: languageName: node linkType: hard -"tunnel-agent@npm:^0.6.0": - version: 0.6.0 - resolution: "tunnel-agent@npm:0.6.0" - dependencies: - safe-buffer: "npm:^5.0.1" - checksum: 10c0/4c7a1b813e7beae66fdbf567a65ec6d46313643753d0beefb3c7973d66fcec3a1e7f39759f0a0b4465883499c6dc8b0750ab8b287399af2e583823e40410a17a - languageName: node - linkType: hard - "tween-functions@npm:^1.2.0": version: 1.2.0 resolution: "tween-functions@npm:1.2.0" @@ -28950,7 +29023,7 @@ __metadata: languageName: node linkType: hard -"webpack-dev-middleware@npm:6.1.1, webpack-dev-middleware@npm:^6.1.1": +"webpack-dev-middleware@npm:6.1.1": version: 6.1.1 resolution: "webpack-dev-middleware@npm:6.1.1" dependencies: @@ -28983,6 +29056,24 @@ __metadata: languageName: node linkType: hard +"webpack-dev-middleware@npm:^6.1.2": + version: 6.1.2 + resolution: "webpack-dev-middleware@npm:6.1.2" + dependencies: + colorette: "npm:^2.0.10" + memfs: "npm:^3.4.12" + mime-types: "npm:^2.1.31" + range-parser: "npm:^1.2.1" + schema-utils: "npm:^4.0.0" + peerDependencies: + webpack: ^5.0.0 + peerDependenciesMeta: + webpack: + optional: true + checksum: 10c0/90c415a770c7db493f4a7d8f3308d761ff63249f628fa8a133eac5a61e849cdf658398e189fc2d95ce0ea884641363f964db6b269c6cea877765321dd7f14b9a + languageName: node + linkType: hard + "webpack-dev-server@npm:4.15.1": version: 4.15.1 resolution: "webpack-dev-server@npm:4.15.1" diff --git a/docs/api/cli-options.md b/docs/api/cli-options.md index 24c0c5d8b002..a1f8c4369fff 100644 --- a/docs/api/cli-options.md +++ b/docs/api/cli-options.md @@ -132,6 +132,7 @@ Options include: | Option | Description | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `-h`, `--help` | Output usage information
`storybook add --help` | +| `-c`, `--config-dir` | Directory where to load Storybook configurations from
`storybook migrate --config-dir .storybook` | | `--package-manager` | Sets the package manager to use when installing the addon.
Available package managers include `npm`, `yarn`, and `pnpm`
`storybook add [addon] --package-manager pnpm` | | `-s`, `--skip-postinstall` | Skips post-install configuration. Used only when you need to configure the addon yourself
`storybook add [addon] --skip-postinstall` | | `--debug` | Outputs more logs in the CLI to assist debugging
`storybook add --debug` | diff --git a/docs/faq.md b/docs/faq.md index aa597d0f4f57..49783da06df3 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -347,15 +347,17 @@ Vue 2 entered [End of Life](https://v2.vuejs.org/lts/) (EOL) on December 31, 202 -## Why aren't my code blocks highlighted with Storybook MDX +## Why aren't my code blocks highlighted with Storybook MDX? -Out of the box, Storybook provides syntax highlighting for a set of languages (e.g., Javascript, Markdown, CSS, HTML, Typescript, GraphQL) you can use with your code blocks. Currently, there's a known limitation when you try and register a custom language to get syntax highlighting. We're working on a fix for this And will update this section once it's available. +Out of the box, Storybook provides syntax highlighting for a set of languages (e.g., Javascript, Markdown, CSS, HTML, Typescript, GraphQL) you can use with your code blocks. Currently, there's a known limitation when you try to register a custom language to get syntax highlighting. We're working on a fix for this and will update this section once it's available. -## Why aren't my MDX stories working in Storybook? +## Why aren't my MDX styles working in Storybook? -MDX can be picky about how your code is formatted with line breaks. This is especially true with code blocks. For example, this will break: +Writing documentation with MDX can be troublesome, especially regarding how your code is formatted when using line breaks with code blocks. For example, this will break: -``` + + +```mdx - ``` + + But this will work: -``` + + +```mdx ``` + + See the following [issue](https://github.com/mdx-js/mdx/issues/1945) for more information. ## Why are my mocked GraphQL queries failing with Storybook's MSW addon? diff --git a/docs/get-started/nextjs.md b/docs/get-started/nextjs.md index 6cbd4ac91177..b17cf19661b3 100644 --- a/docs/get-started/nextjs.md +++ b/docs/get-started/nextjs.md @@ -865,6 +865,24 @@ You might get this if you're using Yarn v2 or v3. See [Notes for Yarn v2 and v3 The `@storybook/nextjs` package abstracts the Webpack 5 builder and provides all the necessary Webpack configuration needed (and used internally) by Next.js. Webpack is currently the official builder in Next.js, and Next.js does not support Vite, therefore it is not possible to use Vite with `@storybook/nextjs`. You can use `@storybook/react-vite` framework instead, but at the cost of having a degraded experience, and we won't be able to provide you official support. +### Error: You are importing avif images, but you don't have sharp installed. You have to install sharp in order to use image optimization features in Next.js. + +`sharp` is a dependency of Next.js's image optimization feature. If you see this error, you need to install `sharp` in your project. + +```bash +npm install sharp +``` + +```bash +yarn add sharp +``` + +```bash +pnpm add sharp +``` + +You can refer to the [Install `sharp` to Use Built-In Image Optimization](https://nextjs.org/docs/messages/install-sharp) in the Next.js documentation for more information. + ## API ### Parameters diff --git a/docs/snippets/common/storybook-automigration-mdx-legacy.npm.mdx b/docs/snippets/common/storybook-automigration-mdx-legacy.npm.mdx deleted file mode 100644 index b54577026015..000000000000 --- a/docs/snippets/common/storybook-automigration-mdx-legacy.npm.mdx +++ /dev/null @@ -1,3 +0,0 @@ -```shell -npx storybook@latest automigrate mdx1to2 -``` diff --git a/docs/snippets/common/storybook-automigration-mdx-legacy.pnpm.mdx b/docs/snippets/common/storybook-automigration-mdx-legacy.pnpm.mdx deleted file mode 100644 index 4c17825bf1a8..000000000000 --- a/docs/snippets/common/storybook-automigration-mdx-legacy.pnpm.mdx +++ /dev/null @@ -1,3 +0,0 @@ -```shell -pnpm dlx storybook@latest automigrate mdx1to2 -``` diff --git a/docs/snippets/common/storybook-automigration-mdx-legacy.yarn.mdx b/docs/snippets/common/storybook-automigration-mdx-legacy.yarn.mdx deleted file mode 100644 index 277d34f73467..000000000000 --- a/docs/snippets/common/storybook-automigration-mdx-legacy.yarn.mdx +++ /dev/null @@ -1,3 +0,0 @@ -```shell -yarn dlx storybook@latest automigrate mdx1to2 -``` diff --git a/docs/versions/next.json b/docs/versions/next.json index a2ba413b8e56..2f8990d20967 100644 --- a/docs/versions/next.json +++ b/docs/versions/next.json @@ -1 +1 @@ -{"version":"8.1.0-alpha.6","info":{"plain":"- CLI: Add --config-dir flag to migrate command - [#26721](https://github.com/storybookjs/storybook/pull/26721), thanks @yannbf!\n- Core: Add `duration` and `onClick` support to Notification API and improve Notification UI - [#26696](https://github.com/storybookjs/storybook/pull/26696), thanks @ghengeveld!\n- Dependency: Bump es-module-lexer - [#26737](https://github.com/storybookjs/storybook/pull/26737), thanks @valentinpalkovic!\n- Dependency: Update globby dependency - [#26733](https://github.com/storybookjs/storybook/pull/26733), thanks @valentinpalkovic!\n- Dependency: Update postcss-loader in Next.js framework - [#26707](https://github.com/storybookjs/storybook/pull/26707), thanks @valentinpalkovic!\n- Next.js: Fix next/font usage on Windows machines - [#26700](https://github.com/storybookjs/storybook/pull/26700), thanks @valentinpalkovic!\n- Webpack: Fix sourcemap generation in webpack react-docgen-loader - [#26676](https://github.com/storybookjs/storybook/pull/26676), thanks @valentinpalkovic!"}} +{"version":"8.1.0-alpha.7","info":{"plain":"- CLI: Add --config-dir flag to add command - [#26771](https://github.com/storybookjs/storybook/pull/26771), thanks @eric-blue!\n- CLI: Add Visual Tests addon install auto-migration when upgrading to 8.0.x - [#26766](https://github.com/storybookjs/storybook/pull/26766), thanks @ndelangen!\n- Controls: Add Channels API to search for files in the project root - [#26726](https://github.com/storybookjs/storybook/pull/26726), thanks @valentinpalkovic!\n- Test: Make spies reactive so that they can be logged by addon-actions - [#26740](https://github.com/storybookjs/storybook/pull/26740), thanks @kasperpeulen!\n- Vue: Disable controls for events, slots, and expose - [#26751](https://github.com/storybookjs/storybook/pull/26751), thanks @shilman!\n- Webpack: Bump webpack-dev-middleware to patch high security issue - [#26655](https://github.com/storybookjs/storybook/pull/26655), thanks @jwilliams-met!"}} diff --git a/docs/writing-docs/mdx.md b/docs/writing-docs/mdx.md index d40ae287025c..2dd8058f3706 100644 --- a/docs/writing-docs/mdx.md +++ b/docs/writing-docs/mdx.md @@ -121,7 +121,7 @@ The `Meta` block defines where the document will be placed in the sidebar. In th -MDX supports standard markdown ([”commonmark”](https://commonmark.org/)) by default and can be extended to support [GitHub-flavored markdown (GFM)](https://github.github.com/gfm) and other extensions (see [Breaking changes](#breaking-changes), below). +MDX supports standard markdown (["commonmark"](https://commonmark.org/)) by default and can be extended to support [GitHub Flavored Markdown (GFM)](https://github.github.com/gfm/) and other extensions (see the [Troubleshooting section](#troubleshooting) to learn more about some of the current limitations). @@ -325,11 +325,7 @@ By applying this pattern with the Controls addon, all anchors will be ignored in ### Markdown tables aren't rendering correctly -As of MDX 2, GFM is no longer included by default: - -“We turned off GFM features in MDX by default. GFM extends CommonMark to add autolink literals, footnotes, strikethrough, tables, and task lists. If you do want these features, you can use a plugin. How to do so is described in [our guide on GFM](https://mdxjs.com/guides/gfm/).” - -In Storybook, you can apply MDX options, including plugins, in the main configuration file: +If you're extending your documentation to include specific features (e.g., tables, footnotes), you may run into some issues rendering them correctly using the current MDX version supported by Storybook. We recommend enabling the [`remark-gfm`](https://github.com/remarkjs/remark-gfm) plugin in your configuration file (i.e., [`.storybook/main.js|ts`](../configure/index.md)) to render them correctly. @@ -344,7 +340,7 @@ paths={[ -The [`remark-gfm`](https://github.com/remarkjs/remark-gfm) package isn't provided by default during migration. We recommend installing it as a development dependency if you use its features. +The [`remark-gfm`](https://github.com/remarkjs/remark-gfm) package is not included by default with Storybook and must be installed separately as a development dependency. To learn more about how to use it and the other breaking changes introduced by MDX, refer to the [GFM guide](https://mdxjs.com/guides/gfm/) and the [migration guide](https://mdxjs.com/migrating/v2/) provided by the MDX team for more information. @@ -354,11 +350,11 @@ As Storybook relies on [MDX 3](https://mdxjs.com/) to render documentation, some #### Storybook doesn't create documentation for my component stories -If you run into a situation where Storybook is not able to detect and render the documentation for your component stories, it may be due to a misconfiguration in your Storybook. Check your configuration file (i.e., `.storybook/main.js|ts`) and ensure the `stories` configuration element provides the correct path to your stories location(e.g., `../src/**/*.stories.@(js|jsx|mjs|ts|tsx)`). +If you run into a situation where Storybook is not able to detect and render the documentation for your component stories, it may be due to a misconfiguration in your Storybook. Check your configuration file (i.e., `.storybook/main.js|ts`) and ensure the `stories` configuration element provides the correct path to your stories location (e.g., `../src/**/*.stories.@(js|jsx|mjs|ts|tsx)`). ### The migration seems flaky and keeps failing -By default, running the [migration command](#automigration) will try and migrate all existing MDX files in your project according to the MDX 2 specification. However, this might not always be possible, and you might run into issues during the migration. To help you troubleshoot those issues, we've prepared some recommendations that might help you. +By default, running the [migration](../configure/upgrading.md) command will prompt you to update the existing MDX files in your project according to the MDX version supported by Storybook. However, this might be a disruptive process, specifically if you're upgrading from a previous version of Storybook where you were using the legacy MDX format. To help you troubleshoot those issues, we've prepared some recommendations that might help you. Start by running the following command inside your project directory: diff --git a/docs/writing-tests/test-runner.md b/docs/writing-tests/test-runner.md index 40b50980517e..6a39b2362ad3 100644 --- a/docs/writing-tests/test-runner.md +++ b/docs/writing-tests/test-runner.md @@ -202,12 +202,12 @@ The test-runner renders a story and executes its [play function](../writing-stor The test-runner exports test hooks that can be overridden globally to enable use cases like visual or DOM snapshots. These hooks give you access to the test lifecycle _before_ and _after_ the story is rendered. Listed below are the available hooks and an overview of how to use them. -| Hook | Description | -| ----------- | --------------------------------------------------------------------------------------------------------------- | -| `prepare` | Prepares the browser for tests
`async prepare({ page, browserContext, testRunnerConfig }) {}` | -| `setup` | Executes once before all the tests run
`setup() {}` | +| Hook | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------ | +| `prepare` | Prepares the browser for tests
`async prepare({ page, browserContext, testRunnerConfig }) {}` | +| `setup` | Executes once before all the tests run
`setup() {}` | | `preVisit` | Executes before a story is initially visited and rendered in the browser
`async preVisit(page, context) {}` | -| `postVisit` | Executes after the story is is visited and fully rendered
`async postVisit(page, context) {}` | +| `postVisit` | Executes after the story is visited and fully rendered
`async postVisit(page, context) {}` | diff --git a/test-storybooks/portable-stories-kitchen-sink/nextjs/yarn.lock b/test-storybooks/portable-stories-kitchen-sink/nextjs/yarn.lock index 64f5d683addd..e621c6f62567 100644 --- a/test-storybooks/portable-stories-kitchen-sink/nextjs/yarn.lock +++ b/test-storybooks/portable-stories-kitchen-sink/nextjs/yarn.lock @@ -2404,12 +2404,16 @@ __metadata: "@storybook/addon-controls@file:../../../code/addons/controls::locator=portable-stories-nextjs%40workspace%3A.": version: 8.1.0-alpha.5 - resolution: "@storybook/addon-controls@file:../../../code/addons/controls#../../../code/addons/controls::hash=b0a330&locator=portable-stories-nextjs%40workspace%3A." + resolution: "@storybook/addon-controls@file:../../../code/addons/controls#../../../code/addons/controls::hash=358687&locator=portable-stories-nextjs%40workspace%3A." dependencies: "@storybook/blocks": "workspace:*" + "@storybook/core-common": "workspace:*" + cjs-module-lexer: "npm:^1.2.3" + es-module-lexer: "npm:^1.5.0" + globby: "npm:^14.0.1" lodash: "npm:^4.17.21" ts-dedent: "npm:^2.0.0" - checksum: 10/dcdc61154006c8575e612d77b7cfa0e2266f12429e2560afe49d4ca74b56a40ba4823e2b2a55e6803006e5fbad8ea848ed4d1ec753063763f01b53236b7ba223 + checksum: 10/feb098e18f942562769dfdfb4afc70655f67f11e6d14acf8e3ca4cfe9a22e09e89ede9f17be6ff1c07a56bed8a6fa1961fa044137a3e7a34471345bcccd091f2 languageName: node linkType: hard @@ -2644,7 +2648,7 @@ __metadata: "@storybook/cli@file:../../../code/lib/cli::locator=portable-stories-nextjs%40workspace%3A.": version: 8.1.0-alpha.5 - resolution: "@storybook/cli@file:../../../code/lib/cli#../../../code/lib/cli::hash=d07615&locator=portable-stories-nextjs%40workspace%3A." + resolution: "@storybook/cli@file:../../../code/lib/cli#../../../code/lib/cli::hash=49e395&locator=portable-stories-nextjs%40workspace%3A." dependencies: "@babel/core": "npm:^7.23.0" "@babel/types": "npm:^7.23.0" @@ -2685,7 +2689,7 @@ __metadata: bin: getstorybook: ./bin/index.js sb: ./bin/index.js - checksum: 10/ca2d93c888ae68445728b0dfad15917ca75e7f193c3da8e2e115386cee359246a701d4cad9edd71ebb1377bebaecf9147a267963e972c4085f10082ec4cccf93 + checksum: 10/37be78e7b11cfb9d712f9b948e3017665fcee970e1b3a4806f3f8307ea9db7e39f4b12fa919086a53efbe4e24a5e6196b2daafb958c11145e9e5d224fa70af55 languageName: node linkType: hard @@ -2743,7 +2747,7 @@ __metadata: "@storybook/core-common@file:../../../code/lib/core-common::locator=portable-stories-nextjs%40workspace%3A.": version: 8.1.0-alpha.5 - resolution: "@storybook/core-common@file:../../../code/lib/core-common#../../../code/lib/core-common::hash=d6f826&locator=portable-stories-nextjs%40workspace%3A." + resolution: "@storybook/core-common@file:../../../code/lib/core-common#../../../code/lib/core-common::hash=55d292&locator=portable-stories-nextjs%40workspace%3A." dependencies: "@storybook/core-events": "workspace:*" "@storybook/csf-tools": "workspace:*" @@ -2773,7 +2777,7 @@ __metadata: tiny-invariant: "npm:^1.3.1" ts-dedent: "npm:^2.0.0" util: "npm:^0.12.4" - checksum: 10/5a82e69805fd612b9bb7eaf370d93bbadf34f18401b96b16a1b33eaf8dc9c472fa143ab9dc62f0ca82c27555ac73245bb0fc681264d3485a281132de98458089 + checksum: 10/e9308c3683651e2c925823e4bee0f91156ba544b0dc72ee6291810e7bd2e17690bce3f426c10d942fd5e18eb545589e5a3347cd02eb6ecceb058dad7a56bb775 languageName: node linkType: hard @@ -3239,12 +3243,12 @@ __metadata: "@storybook/types@file:../../../code/lib/types::locator=portable-stories-nextjs%40workspace%3A.": version: 8.1.0-alpha.5 - resolution: "@storybook/types@file:../../../code/lib/types#../../../code/lib/types::hash=22b151&locator=portable-stories-nextjs%40workspace%3A." + resolution: "@storybook/types@file:../../../code/lib/types#../../../code/lib/types::hash=0524c9&locator=portable-stories-nextjs%40workspace%3A." dependencies: "@storybook/channels": "workspace:*" "@types/express": "npm:^4.7.0" file-system-cache: "npm:2.3.0" - checksum: 10/9029701cc4326e000e4f2866601633e54c3167bf5363c97b5190cec9ea762750dcbbc09767695a225bf0804cff6d0e8b9eb55143ef9207798800c6c1dfe7856a + checksum: 10/b2835c9386c22e535e62263fe03ead9c43a1c9762b6524ed8a9b1954887e8853311d580caa7711d57a1eecc9ce30cd7cfd9d814a45723e8434397b7adced1871 languageName: node linkType: hard diff --git a/test-storybooks/portable-stories-kitchen-sink/react/yarn.lock b/test-storybooks/portable-stories-kitchen-sink/react/yarn.lock index ca0b7a5ede87..e56c0e31080a 100644 --- a/test-storybooks/portable-stories-kitchen-sink/react/yarn.lock +++ b/test-storybooks/portable-stories-kitchen-sink/react/yarn.lock @@ -2583,6 +2583,10 @@ __metadata: resolution: "@storybook/addon-controls@portal:../../../code/addons/controls::locator=portable-stories-react%40workspace%3A." dependencies: "@storybook/blocks": "workspace:*" + "@storybook/core-common": "workspace:*" + cjs-module-lexer: "npm:^1.2.3" + es-module-lexer: "npm:^1.5.0" + globby: "npm:^14.0.1" lodash: "npm:^4.17.21" ts-dedent: "npm:^2.0.0" languageName: node @@ -5117,7 +5121,7 @@ __metadata: languageName: node linkType: hard -"cjs-module-lexer@npm:^1.0.0": +"cjs-module-lexer@npm:^1.0.0, cjs-module-lexer@npm:^1.2.3": version: 1.2.3 resolution: "cjs-module-lexer@npm:1.2.3" checksum: 10/f96a5118b0a012627a2b1c13bd2fcb92509778422aaa825c5da72300d6dcadfb47134dd2e9d97dfa31acd674891dd91642742772d19a09a8adc3e56bd2f5928c diff --git a/test-storybooks/portable-stories-kitchen-sink/svelte/yarn.lock b/test-storybooks/portable-stories-kitchen-sink/svelte/yarn.lock index c741bf702794..24ddbf299123 100644 --- a/test-storybooks/portable-stories-kitchen-sink/svelte/yarn.lock +++ b/test-storybooks/portable-stories-kitchen-sink/svelte/yarn.lock @@ -2217,6 +2217,10 @@ __metadata: resolution: "@storybook/addon-controls@portal:../../../code/addons/controls::locator=portable-stories-svelte%40workspace%3A." dependencies: "@storybook/blocks": "workspace:*" + "@storybook/core-common": "workspace:*" + cjs-module-lexer: "npm:^1.2.3" + es-module-lexer: "npm:^1.5.0" + globby: "npm:^14.0.1" lodash: "npm:^4.17.21" ts-dedent: "npm:^2.0.0" languageName: node @@ -4110,6 +4114,13 @@ __metadata: languageName: node linkType: hard +"cjs-module-lexer@npm:^1.2.3": + version: 1.2.3 + resolution: "cjs-module-lexer@npm:1.2.3" + checksum: 10/f96a5118b0a012627a2b1c13bd2fcb92509778422aaa825c5da72300d6dcadfb47134dd2e9d97dfa31acd674891dd91642742772d19a09a8adc3e56bd2f5928c + languageName: node + linkType: hard + "clean-stack@npm:^2.0.0": version: 2.2.0 resolution: "clean-stack@npm:2.2.0" diff --git a/test-storybooks/portable-stories-kitchen-sink/vue3/yarn.lock b/test-storybooks/portable-stories-kitchen-sink/vue3/yarn.lock index 6a3a4679f469..bab162b80aac 100644 --- a/test-storybooks/portable-stories-kitchen-sink/vue3/yarn.lock +++ b/test-storybooks/portable-stories-kitchen-sink/vue3/yarn.lock @@ -2249,6 +2249,10 @@ __metadata: resolution: "@storybook/addon-controls@portal:../../../code/addons/controls::locator=portable-stories-vue3%40workspace%3A." dependencies: "@storybook/blocks": "workspace:*" + "@storybook/core-common": "workspace:*" + cjs-module-lexer: "npm:^1.2.3" + es-module-lexer: "npm:^1.5.0" + globby: "npm:^14.0.1" lodash: "npm:^4.17.21" ts-dedent: "npm:^2.0.0" languageName: node @@ -4473,6 +4477,13 @@ __metadata: languageName: node linkType: hard +"cjs-module-lexer@npm:^1.2.3": + version: 1.2.3 + resolution: "cjs-module-lexer@npm:1.2.3" + checksum: 10/f96a5118b0a012627a2b1c13bd2fcb92509778422aaa825c5da72300d6dcadfb47134dd2e9d97dfa31acd674891dd91642742772d19a09a8adc3e56bd2f5928c + languageName: node + linkType: hard + "clean-stack@npm:^2.0.0": version: 2.2.0 resolution: "clean-stack@npm:2.2.0"