diff --git a/.circleci/config.yml b/.circleci/config.yml index 6bf65d75ed68..04ef5634826e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -322,7 +322,7 @@ jobs: at: . - run: name: Starting Event Collector - command: yarn ts-node ./event-log-collector.ts + command: node --loader esbuild-register/loader -r esbuild-register ./event-log-collector.ts working_directory: scripts background: true - run: @@ -334,7 +334,7 @@ jobs: TEMPLATE=$(yarn get-template --cadence << pipeline.parameters.workflow >> --task build) if [[ $TEMPLATE != bench/* ]] then - yarn ts-node ./event-log-checker build $TEMPLATE + node --loader esbuild-register/loader -r esbuild-register ./event-log-checker build $TEMPLATE fi working_directory: scripts - report-workflow-on-failure: @@ -645,4 +645,3 @@ workflows: # parallelism: 4 # requires: # - create-sandboxes - diff --git a/.github/DISCUSSION_TEMPLATE/help.yml b/.github/DISCUSSION_TEMPLATE/help.yml new file mode 100644 index 000000000000..2b74e6d0dcf8 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/help.yml @@ -0,0 +1,40 @@ +body: +- type: markdown + id: intro + attributes: + value: | + Thanks for taking the time to start a new discussion! + + ### Before you post + Check if someone has already asked/answered your question in a previous discussion. + + ### When you're ready to post + Add labels to your discussion (e.g. React, Vue, Vite) to make it clearer for other users. + +- type: textarea + id: summary + attributes: + label: Summary + description: How can we help? + validations: + required: true + +- type: textarea + id: additional-info + attributes: + label: Additional information + description: | + Share Your Storybook configuration (`main.js` or `main.ts`), your Storybook version number, any error messages, and any relevant dependencies. These help us get a clearer understanding of what might be going wrong. + + P.S. Please [share code as text](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks) rather than as a screenshot! It makes debugging much easier and faster. + validations: + required: false + +- type: input + id: reproduction + attributes: + label: Create a reproduction + description: | + Help us debug by creating a minimal reproduction with [https://storybook.new](https://storybook.new). Learn more about creating a reproduction [here](https://storybook.js.org/docs/react/contribute/how-to-reproduce). + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 95be4a49c705..75238b19d615 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -3,15 +3,18 @@ contact_links: - name: View documentation ๐Ÿ“š url: https://storybook.js.org/docs/ about: Check out the official docs for answers to common questions. - - name: Feature Requests ๐Ÿ’ก + - name: Feature requests ๐Ÿ’ก url: https://github.com/storybookjs/storybook/discussions/new?category=ideas about: Suggest a feature idea for this project. - name: Open an RFC ๐Ÿฆ„ url: https://github.com/storybookjs/storybook/discussions/new?category=rfc about: Do you want to propose a more involved change to Storybook? Open an RFC (Request for Comments) to start a discussion. - - name: Questions & discussions ๐Ÿค” + - name: Questions ๐Ÿ’ญ + url: https://github.com/storybookjs/storybook/discussions/new?category=help + about: Need support with a Storybook problem? Open up a help request. + - name: Discussions ๐Ÿ™Œ url: https://github.com/storybookjs/storybook/discussions - about: Ask questions, show off your Storybook, etc. - - name: Community Discord ๐Ÿ’ฌ + about: Show off your Storybook or discuss the project. + - name: Community Discord ๐ŸŽ‰ url: https://discord.gg/storybook - about: Community discussions, interactive support, contributor help + about: Meet other community members and get contributor help. diff --git a/.github/workflows/generate-sandboxes-main.yml b/.github/workflows/generate-sandboxes-main.yml index 7d9328578493..53814040f610 100644 --- a/.github/workflows/generate-sandboxes-main.yml +++ b/.github/workflows/generate-sandboxes-main.yml @@ -30,7 +30,10 @@ jobs: git config --global user.name "Storybook Bot" git config --global user.email "bot@storybook.js.org" - name: Install dependencies - run: node ./scripts/check-dependencies.js + run: | + cd ./scripts + node --experimental-modules ./check-dependencies.js + cd .. - name: Compile Storybook libraries run: yarn task --task compile --start-from=auto --no-link - name: Publishing to local registry diff --git a/.github/workflows/generate-sandboxes-next.yml b/.github/workflows/generate-sandboxes-next.yml index 8a28c4cbb23a..d9355f593dc8 100644 --- a/.github/workflows/generate-sandboxes-next.yml +++ b/.github/workflows/generate-sandboxes-next.yml @@ -30,7 +30,10 @@ jobs: git config --global user.name "Storybook Bot" git config --global user.email "bot@storybook.js.org" - name: Install dependencies - run: node ./scripts/check-dependencies.js + run: | + cd ./scripts + node --experimental-modules ./check-dependencies.js + cd .. - name: Compile Storybook libraries run: yarn task --task compile --start-from=auto --no-link - name: Publishing to local registry diff --git a/CHANGELOG.prerelease.md b/CHANGELOG.prerelease.md index ead15edae9d9..c09c035b151c 100644 --- a/CHANGELOG.prerelease.md +++ b/CHANGELOG.prerelease.md @@ -1,3 +1,28 @@ +## 7.6.0-alpha.7 + +- Actions: Warn on implicit actions - [#24856](https://github.com/storybookjs/storybook/pull/24856), thanks [@kasperpeulen](https://github.com/kasperpeulen)! +- Addons, core: Make `react` and Storybook packages `devDependencies` where possible - ATTEMPT 2 - [#24834](https://github.com/storybookjs/storybook/pull/24834), thanks [@JReinhold](https://github.com/JReinhold)! +- CLI: Add "doctor" command - [#22236](https://github.com/storybookjs/storybook/pull/22236), thanks [@yannbf](https://github.com/yannbf)! +- Core: Add deprecation notice for Vite + CommonJS - [#23950](https://github.com/storybookjs/storybook/pull/23950), thanks [@JReinhold](https://github.com/JReinhold)! +- Core: Detect no matching export error in storybook start and build - [#24877](https://github.com/storybookjs/storybook/pull/24877), thanks [@yannbf](https://github.com/yannbf)! +- Core: Fix `useStoryPrepared` hook failing with `undefined` data - [#22631](https://github.com/storybookjs/storybook/pull/22631), thanks [@SpookyJelly](https://github.com/SpookyJelly)! +- Core: Gracefully handle error when parsing preview.js file - [#24858](https://github.com/storybookjs/storybook/pull/24858), thanks [@yannbf](https://github.com/yannbf)! +- Core: Make warnOnIncompatibleAddons fault-tolerant - [#24880](https://github.com/storybookjs/storybook/pull/24880), thanks [@taozhou-glean](https://github.com/taozhou-glean)! +- Dependency: Fix Yarn 4 failing to install due to jscodeshift dependency issue - [#24914](https://github.com/storybookjs/storybook/pull/24914), thanks [@samvv](https://github.com/samvv)! +- Dependency: Update jscodeshift to v0.15.1 - [#24882](https://github.com/storybookjs/storybook/pull/24882), thanks [@epreston](https://github.com/epreston)! +- FastBuild: Fix disabledAddons filter - [#24924](https://github.com/storybookjs/storybook/pull/24924), thanks [@IanVS](https://github.com/IanVS)! +- ManagerAPI: Fix setting status without index, crashes storybook - [#24866](https://github.com/storybookjs/storybook/pull/24866), thanks [@ndelangen](https://github.com/ndelangen)! +- Next.js: Add back image context CommonJS export - [#24885](https://github.com/storybookjs/storybook/pull/24885), thanks [@martinnabhan](https://github.com/martinnabhan)! +- Next.js: Add experimental SWC support - [#24852](https://github.com/storybookjs/storybook/pull/24852), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)! +- Next.js: Fix import path in swc loader - [#24922](https://github.com/storybookjs/storybook/pull/24922), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)! +- Svelte: Fix decorators always running twice - [#24921](https://github.com/storybookjs/storybook/pull/24921), thanks [@paoloricciuti](https://github.com/paoloricciuti)! +- SvelteKit: Add experimental page and navigation mocking - [#24795](https://github.com/storybookjs/storybook/pull/24795), thanks [@paoloricciuti](https://github.com/paoloricciuti)! +- Test: Model loaders as before each and restore mocks properly - [#24948](https://github.com/storybookjs/storybook/pull/24948), thanks [@kasperpeulen](https://github.com/kasperpeulen)! +- TestBuild: Add env-variable support to `--test` CLI-flag - [#24862](https://github.com/storybookjs/storybook/pull/24862), thanks [@ndelangen](https://github.com/ndelangen)! +- TestBuild: Add tests and rename to camelCase - [#24911](https://github.com/storybookjs/storybook/pull/24911), thanks [@ndelangen](https://github.com/ndelangen)! +- TestBuild: Fix indexer bug - [#24890](https://github.com/storybookjs/storybook/pull/24890), thanks [@ndelangen](https://github.com/ndelangen)! +- Typescript: Add 'skipCompiler' option to TypeScript presets - [#24847](https://github.com/storybookjs/storybook/pull/24847), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)! + ## 7.6.0-alpha.6 - Addon: Move Visual Test addon to the code directory - [#24771](https://github.com/storybookjs/storybook/pull/24771), thanks [@cdedreuille](https://github.com/cdedreuille)! diff --git a/MIGRATION.md b/MIGRATION.md index ca14c266b621..4495d71096a8 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,6 +1,9 @@

Migration

- [From version 7.5.0 to 7.6.0](#from-version-750-to-760) + - [CommonJS with Vite is deprecated](#commonjs-with-vite-is-deprecated) + - [Using implicit actions during rendering is deprecated](#using-implicit-actions-during-rendering-is-deprecated) + - [typescript.skipBabel deprecated](#typescriptskipbabel-deprecated) - [Primary doc block accepts of prop](#primary-doc-block-accepts-of-prop) - [Addons no longer need a peer dependency on React](#addons-no-longer-need-a-peer-dependency-on-react) - [From version 7.4.0 to 7.5.0](#from-version-740-to-750) @@ -310,6 +313,65 @@ ## From version 7.5.0 to 7.6.0 +#### CommonJS with Vite is deprecated + +Using CommonJS in the `main` configuration with `main.cjs` or `main.cts` is deprecated, and will be removed in Storybook 8.0. This is a necessary change because [Vite will remove support for CommonJS in an upcoming release](https://github.com/vitejs/vite/discussions/13928). + +You can address this by converting your `main` configuration file to ESM syntax and renaming it to `main.mjs` or `main.mts` if your project does not have `"type": "module"` in its `package.json`. To convert the config file to ESM you will need to replace any CommonJS syntax like `require()`, `module.exports`, or `__dirname`. If you haven't already, you may also consider adding `"type": "module"` to your package.json and converting your project to ESM. + +#### Using implicit actions during rendering is deprecated + +In Storybook 7, we inferred if the component accepts any action props, +by checking if it starts with `onX` (for example `onClick`), or as configured by `actions.argTypesRegex`. +If that was the case, we would fill in jest spies for those args automatically. + +```ts +export default { + component: Button, +}; + +export const ButtonClick = { + play: async ({ args, canvasElement }) => { + await userEvent.click(within(canvasElement).getByRole('button')); + // args.onClick is a jest spy in 7.0 + await expect(args.onClick).toHaveBeenCalled(); + }, +}; +``` + +In Storybook 8 this feature will be removed, and spies have to added explicitly: + +```ts +import { fn } from '@storybook/test'; + +export default { + component: Button, + args: { + onClick: fn(), + }, +}; + +export const ButtonClick = { + play: async ({ args, canvasElement }) => { + await userEvent.click(within(canvasElement).getByRole('button')); + await expect(args.onClick).toHaveBeenCalled(); + }, +}; +``` + +For more context, see this RFC: +https://github.com/storybookjs/storybook/discussions/23649 + +To summarize: + +- This makes CSF files less magical and more portable, so that CSF files will render the same in a test environment where docgen is not available. +- This allows users and (test) integrators to run or build storybook without docgen, boosting the user performance and allows tools to give quicker feedback. +- This will make sure that we can one day lazy load docgen, without changing how stories are rendered. + +#### typescript.skipBabel deprecated + +We will remove the `typescript.skipBabel` option in Storybook 8.0.0. Please use `typescirpt.skipCompiler` instead. + #### Primary doc block accepts of prop The `Primary` doc block now also accepts an `of` prop as described in the [Doc Blocks](#doc-blocks) section. It still accepts being passed `name` or no props at all. diff --git a/code/.eslintrc.js b/code/.eslintrc.js index 35086680644d..953ccaf79d25 100644 --- a/code/.eslintrc.js +++ b/code/.eslintrc.js @@ -1,10 +1,22 @@ const path = require('path'); +const fs = require('fs'); const scriptPath = path.join(__dirname, '..', 'scripts'); +const addonsPackages = fs + .readdirSync(path.join(__dirname, 'addons')) + .filter((p) => fs.statSync(path.join(__dirname, 'addons', p)).isDirectory()); +const libPackages = fs + .readdirSync(path.join(__dirname, 'lib')) + .filter((p) => fs.statSync(path.join(__dirname, 'lib', p)).isDirectory()); +const uiPackages = fs + .readdirSync(path.join(__dirname, 'ui')) + .filter((p) => fs.statSync(path.join(__dirname, 'ui', p)).isDirectory()) + .filter((p) => !p.startsWith('.')); + module.exports = { root: true, - extends: [path.join(scriptPath, '.eslintrc.js')], + extends: [path.join(scriptPath, '.eslintrc.cjs')], parserOptions: { tsconfigRootDir: __dirname, project: ['./tsconfig.json'], @@ -53,7 +65,6 @@ module.exports = { project: null, }, rules: { - // '@typescript-eslint/no-var-requires': 'off', '@typescript-eslint/dot-notation': 'off', '@typescript-eslint/no-implied-eval': 'off', '@typescript-eslint/no-throw-literal': 'off', @@ -77,15 +88,7 @@ module.exports = { }, { // these packages use pre-bundling, dependencies will be bundled, and will be in devDepenencies - files: [ - 'addons/**/*', - 'frameworks/**/*', - 'lib/**/*', - 'builders/**/*', - 'deprecated/**/*', - 'renderers/**/*', - 'ui/**/*', - ], + files: ['frameworks/**/*', 'builders/**/*', 'deprecated/**/*', 'renderers/**/*'], excludedFiles: ['frameworks/angular/**/*', 'frameworks/ember/**/*', 'lib/core-server/**/*'], rules: { 'import/no-extraneous-dependencies': [ @@ -95,11 +98,47 @@ module.exports = { }, }, { - files: ['**/ui/*', '**/ui/.storybook/*'], + files: ['**/ui/.storybook/**'], rules: { - 'import/no-extraneous-dependencies': ['error', { packageDir: __dirname }], + 'import/no-extraneous-dependencies': [ + 'error', + { packageDir: [__dirname], devDependencies: true }, + ], }, }, + ...addonsPackages.map((directory) => ({ + files: [path.join('**', 'addons', directory, '**', '*.*')], + rules: { + 'import/no-extraneous-dependencies': [ + 'error', + { + packageDir: [__dirname, path.join(__dirname, 'addons', directory)], + devDependencies: true, + }, + ], + }, + })), + ...uiPackages.map((directory) => ({ + files: [path.join('**', 'ui', directory, '**', '*.*')], + rules: { + 'import/no-extraneous-dependencies': [ + 'error', + { packageDir: [__dirname, path.join(__dirname, 'ui', directory)], devDependencies: true }, + ], + }, + })), + ...libPackages.map((directory) => ({ + files: [path.join('**', 'lib', directory, '**', '*.*')], + rules: { + 'import/no-extraneous-dependencies': [ + 'error', + { + packageDir: [__dirname, path.join(__dirname, 'lib', directory)], + devDependencies: true, + }, + ], + }, + })), { files: [ '**/__tests__/**', @@ -121,16 +160,24 @@ module.exports = { 'react/require-default-props': 'off', }, }, - { files: '**/.storybook/config.js', rules: { 'global-require': 'off' } }, { files: ['**/*.stories.*'], rules: { 'no-console': 'off', }, }, + { + files: ['**/renderers/preact/**/*'], + rules: { + 'react/react-in-jsx-scope': 'off', + 'react/prop-types': 'off', + }, + }, { files: ['**/*.tsx', '**/*.ts'], rules: { + 'no-shadow': 'off', + '@typescript-eslint/ban-types': 'warn', // should become error, in the future 'react/require-default-props': 'off', 'react/prop-types': 'off', // we should use types 'react/forbid-prop-types': 'off', // we should use types diff --git a/code/addons/a11y/package.json b/code/addons/a11y/package.json index e56e906b771e..7d4ea664869b 100644 --- a/code/addons/a11y/package.json +++ b/code/addons/a11y/package.json @@ -32,21 +32,9 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./manager": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, - "./preview": { - "types": "./dist/preview.d.ts", - "require": "./dist/preview.js", - "import": "./dist/preview.mjs" - }, - "./register": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, + "./manager": "./dist/manager.js", + "./preview": "./dist/preview.js", + "./register": "./dist/manager.js", "./package.json": "./package.json" }, "main": "dist/index.js", @@ -60,48 +48,41 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts" }, "dependencies": { "@storybook/addon-highlight": "workspace:*", + "axe-core": "^4.2.0" + }, + "devDependencies": { "@storybook/channels": "workspace:*", "@storybook/client-logger": "workspace:*", "@storybook/components": "workspace:*", - "@storybook/core-events": "workspace:*", "@storybook/global": "^5.0.0", "@storybook/manager-api": "workspace:*", "@storybook/preview-api": "workspace:*", "@storybook/theming": "workspace:*", "@storybook/types": "workspace:*", - "axe-core": "^4.2.0", - "lodash": "^4.17.21", - "react-resize-detector": "^7.1.2" - }, - "devDependencies": { "@testing-library/react": "^11.2.2", + "lodash": "^4.17.21", + "react": "^16.8.0", + "react-dom": "^16.8.0", + "react-resize-detector": "^7.1.2", "resize-observer-polyfill": "^1.5.1", "typescript": "~4.9.3" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - }, "publishConfig": { "access": "public" }, "bundler": { - "entries": [ - "./src/index.ts", - "./src/manager.tsx", + "exportEntries": [ + "./src/index.ts" + ], + "managerEntries": [ + "./src/manager.tsx" + ], + "previewEntries": [ "./src/preview.tsx" ] }, diff --git a/code/addons/actions/package.json b/code/addons/actions/package.json index 2a9233d558f8..217f53c468ad 100644 --- a/code/addons/actions/package.json +++ b/code/addons/actions/package.json @@ -33,21 +33,9 @@ "require": "./dist/decorator.js", "import": "./dist/decorator.mjs" }, - "./manager": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, - "./preview": { - "types": "./dist/preview.d.ts", - "require": "./dist/preview.js", - "import": "./dist/preview.mjs" - }, - "./register.js": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, + "./manager": "./dist/manager.js", + "./preview": "./dist/preview.js", + "./register.js": "./dist/manager.js", "./package.json": "./package.json" }, "main": "dist/index.js", @@ -58,14 +46,8 @@ "*": [ "dist/index.d.ts" ], - "manager": [ - "dist/manager.d.ts" - ], "decorator": [ "dist/decorator.d.ts" - ], - "preview": [ - "dist/preview.d.ts" ] } }, @@ -77,52 +59,42 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts" }, "dependencies": { + "@storybook/global": "^5.0.0", + "@types/uuid": "^9.0.1", + "dequal": "^2.0.2", + "polished": "^4.2.2", + "uuid": "^9.0.0" + }, + "devDependencies": { "@storybook/client-logger": "workspace:*", "@storybook/components": "workspace:*", "@storybook/core-events": "workspace:*", - "@storybook/global": "^5.0.0", "@storybook/manager-api": "workspace:*", "@storybook/preview-api": "workspace:*", "@storybook/theming": "workspace:*", "@storybook/types": "workspace:*", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "polished": "^4.2.2", - "prop-types": "^15.7.2", + "react": "^16.8.0", + "react-dom": "^16.8.0", "react-inspector": "^6.0.0", "telejson": "^7.2.0", - "ts-dedent": "^2.0.0", - "uuid": "^9.0.0" - }, - "devDependencies": { - "@types/lodash": "^4.14.167", - "@types/uuid": "^9.0.1", "typescript": "~4.9.3" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - }, "publishConfig": { "access": "public" }, "bundler": { - "entries": [ - "./src/index.ts", + "exportEntries": [ "./src/decorator.ts", - "./src/manager.tsx", + "./src/index.ts" + ], + "managerEntries": [ + "./src/manager.tsx" + ], + "previewEntries": [ "./src/preview.ts" ] }, diff --git a/code/addons/actions/src/addArgs.ts b/code/addons/actions/src/addArgs.ts index db14aee0ce3d..5742bd8627c0 100644 --- a/code/addons/actions/src/addArgs.ts +++ b/code/addons/actions/src/addArgs.ts @@ -1,12 +1,7 @@ import type { ArgsEnhancer } from '@storybook/types'; -import { - addActionsFromArgTypes, - attachActionsToFunctionMocks, - inferActionsFromArgTypesRegex, -} from './addArgsHelpers'; +import { addActionsFromArgTypes, inferActionsFromArgTypesRegex } from './addArgsHelpers'; export const argsEnhancers: ArgsEnhancer[] = [ addActionsFromArgTypes, inferActionsFromArgTypesRegex, - attachActionsToFunctionMocks, ]; diff --git a/code/addons/actions/src/addArgsHelpers.ts b/code/addons/actions/src/addArgsHelpers.ts index 0dcb56c32f55..ab8fd3b36a0b 100644 --- a/code/addons/actions/src/addArgsHelpers.ts +++ b/code/addons/actions/src/addArgsHelpers.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-underscore-dangle,no-param-reassign */ import type { Args, Renderer, ArgsEnhancer } from '@storybook/types'; import { action } from './runtime/action'; @@ -19,6 +18,7 @@ export const inferActionsFromArgTypesRegex: ArgsEnhancer = (context) = const { initialArgs, argTypes, + id, parameters: { actions }, } = context; if (!actions || actions.disable || !actions.argTypesRegex || !argTypes) { @@ -32,7 +32,7 @@ export const inferActionsFromArgTypesRegex: ArgsEnhancer = (context) = return argTypesMatchingRegex.reduce((acc, [name, argType]) => { if (isInInitialArgs(name, initialArgs)) { - acc[name] = action(name, { implicit: true }); + acc[name] = action(name, { implicit: true, id }); } return acc; }, {} as Args); @@ -62,33 +62,3 @@ export const addActionsFromArgTypes: ArgsEnhancer = (context) => { return acc; }, {} as Args); }; - -export const attachActionsToFunctionMocks: ArgsEnhancer = (context) => { - const { - initialArgs, - argTypes, - parameters: { actions }, - } = context; - if (actions?.disable || !argTypes) { - return {}; - } - - const argTypesWithAction = Object.entries(initialArgs).filter( - ([, value]) => - typeof value === 'function' && - '_isMockFunction' in value && - value._isMockFunction && - !value._actionAttached - ); - - return argTypesWithAction.reduce((acc, [key, value]) => { - const previous = value.getMockImplementation(); - value.mockImplementation((...args: unknown[]) => { - action(key)(...args); - return previous?.(...args); - }); - // this enhancer is being called multiple times - value._actionAttached = true; - return acc; - }, {} as Args); -}; diff --git a/code/addons/actions/src/loaders.ts b/code/addons/actions/src/loaders.ts new file mode 100644 index 000000000000..99e51287a893 --- /dev/null +++ b/code/addons/actions/src/loaders.ts @@ -0,0 +1,30 @@ +/* eslint-disable no-underscore-dangle */ +import type { LoaderFunction } from '@storybook/types'; +import { action } from './runtime'; + +const attachActionsToFunctionMocks: 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]) => { + const previous = value.getMockImplementation(); + if (previous?._actionAttached !== true) { + const implementation = (...params: unknown[]) => { + action(key)(...params); + return previous?.(...params); + }; + implementation._actionAttached = true; + value.mockImplementation(implementation); + } + }); +}; + +export const loaders: LoaderFunction[] = [attachActionsToFunctionMocks]; diff --git a/code/addons/actions/src/models/ActionOptions.ts b/code/addons/actions/src/models/ActionOptions.ts index b503df069d5c..ffd8d373b3fa 100644 --- a/code/addons/actions/src/models/ActionOptions.ts +++ b/code/addons/actions/src/models/ActionOptions.ts @@ -5,6 +5,7 @@ interface Options { clearOnStoryChange: boolean; limit: number; implicit: boolean; + id: string; } export type ActionOptions = Partial & Partial; diff --git a/code/addons/actions/src/preview.ts b/code/addons/actions/src/preview.ts index 7a06751b46dd..bfafaa7faf4e 100644 --- a/code/addons/actions/src/preview.ts +++ b/code/addons/actions/src/preview.ts @@ -1 +1,2 @@ export * from './addArgs'; +export * from './loaders'; diff --git a/code/addons/actions/src/runtime/action.ts b/code/addons/actions/src/runtime/action.ts index 9cb6055a5feb..9955f27637dc 100644 --- a/code/addons/actions/src/runtime/action.ts +++ b/code/addons/actions/src/runtime/action.ts @@ -1,5 +1,9 @@ import { v4 as uuidv4 } from 'uuid'; +import type { PreviewWeb } from '@storybook/preview-api'; import { addons } from '@storybook/preview-api'; +import type { Renderer } from '@storybook/types'; +import { global } from '@storybook/global'; +import { ImplicitActionsDuringRendering } from '@storybook/core-events/preview-errors'; import { EVENT_ID } from '../constants'; import type { ActionDisplay, ActionOptions, HandlerFunction } from '../models'; import { config } from './configureActions'; @@ -54,22 +58,31 @@ export function action(name: string, options: ActionOptions = {}): HandlerFuncti }; const handler = function actionHandler(...args: any[]) { - // TODO: Enable once codemods are finished - // if (options.implicit) { - // const preview = - // '__STORYBOOK_PREVIEW__' in global - // ? (global.__STORYBOOK_PREVIEW__ as PreviewWeb) - // : undefined; - // if ( - // preview?.storyRenders.some( - // (render) => render.phase === 'playing' || render.phase === 'rendering' - // ) - // ) { - // console.warn( - // 'Can not use implicit actions during rendering or playing of a story.' - // ); - // } - // } + if (options.implicit) { + const preview = + '__STORYBOOK_PREVIEW__' in global + ? // eslint-disable-next-line no-underscore-dangle + (global.__STORYBOOK_PREVIEW__ as PreviewWeb) + : undefined; + const storyRenderer = preview?.storyRenders.find( + (render) => render.phase === 'playing' || render.phase === 'rendering' + ); + + if (storyRenderer) { + const deprecated = !window?.FEATURES?.disallowImplicitActionsInRenderV8; + const error = new ImplicitActionsDuringRendering({ + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + phase: storyRenderer.phase!, + name, + deprecated, + }); + if (deprecated) { + console.warn(error); + } else { + throw error; + } + } + } const channel = addons.getChannel(); // this makes sure that in js enviroments like react native you can still get an id diff --git a/code/addons/actions/src/typings.d.ts b/code/addons/actions/src/typings.d.ts new file mode 100644 index 000000000000..fba51cec4ab6 --- /dev/null +++ b/code/addons/actions/src/typings.d.ts @@ -0,0 +1 @@ +declare var FEATURES: import('@storybook/types').StorybookConfig['features']; diff --git a/code/addons/backgrounds/package.json b/code/addons/backgrounds/package.json index f4c897725aed..a8fa906f6f5a 100644 --- a/code/addons/backgrounds/package.json +++ b/code/addons/backgrounds/package.json @@ -32,39 +32,14 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./manager": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, - "./preview": { - "types": "./dist/preview.d.ts", - "require": "./dist/preview.js", - "import": "./dist/preview.mjs" - }, - "./register": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, + "./manager": "./dist/manager.js", + "./preview": "./dist/preview.js", + "./register": "./dist/manager.js", "./package.json": "./package.json" }, "main": "dist/index.js", "module": "dist/index.mjs", "types": "dist/index.d.ts", - "typesVersions": { - "*": { - "*": [ - "dist/index.d.ts" - ], - "manager": [ - "dist/manager.d.ts" - ], - "preview": [ - "dist/preview.d.ts" - ] - } - }, "files": [ "dist/**/*", "README.md", @@ -73,43 +48,36 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts" }, "dependencies": { + "@storybook/global": "^5.0.0", + "memoizerific": "^1.11.3", + "ts-dedent": "^2.0.0" + }, + "devDependencies": { "@storybook/client-logger": "workspace:*", "@storybook/components": "workspace:*", - "@storybook/core-events": "workspace:*", - "@storybook/global": "^5.0.0", "@storybook/manager-api": "workspace:*", "@storybook/preview-api": "workspace:*", "@storybook/theming": "workspace:*", "@storybook/types": "workspace:*", - "memoizerific": "^1.11.3", - "ts-dedent": "^2.0.0" - }, - "devDependencies": { + "react": "^16.8.0", + "react-dom": "^16.8.0", "typescript": "~4.9.3" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - }, "publishConfig": { "access": "public" }, "bundler": { - "entries": [ - "./src/index.ts", - "./src/manager.tsx", + "exportEntries": [ + "./src/index.ts" + ], + "managerEntries": [ + "./src/manager.tsx" + ], + "previewEntries": [ "./src/preview.tsx" ] }, diff --git a/code/addons/controls/package.json b/code/addons/controls/package.json index d1a53964e8e8..59a5d753ac6e 100644 --- a/code/addons/controls/package.json +++ b/code/addons/controls/package.json @@ -32,31 +32,13 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./manager": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, - "./register": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, + "./manager": "./dist/manager.js", + "./register": "./dist/manager.js", "./package.json": "./package.json" }, "main": "dist/index.js", "module": "dist/index.mjs", "types": "dist/index.d.ts", - "typesVersions": { - "*": { - "*": [ - "dist/index.d.ts" - ], - "manager": [ - "dist/manager.d.ts" - ] - } - }, "files": [ "dist/**/*", "README.md", @@ -65,44 +47,36 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts" }, "dependencies": { "@storybook/blocks": "workspace:*", + "lodash": "^4.17.21", + "ts-dedent": "^2.0.0" + }, + "devDependencies": { "@storybook/client-logger": "workspace:*", "@storybook/components": "workspace:*", "@storybook/core-common": "workspace:*", - "@storybook/core-events": "workspace:*", "@storybook/manager-api": "workspace:*", "@storybook/node-logger": "workspace:*", "@storybook/preview-api": "workspace:*", "@storybook/theming": "workspace:*", "@storybook/types": "workspace:*", - "lodash": "^4.17.21", - "ts-dedent": "^2.0.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } + "react": "^16.8.0", + "react-dom": "^16.8.0" }, "publishConfig": { "access": "public" }, "bundler": { - "entries": [ - "./src/index.ts", - "./src/manager.tsx" + "exportEntries": [ + "./src/index.ts" ], - "platform": "browser" + "managerEntries": [ + "./src/manager.tsx" + ] }, "gitHead": "e6a7fd8a655c69780bc20b9749c2699e44beae17", "storybook": { diff --git a/code/addons/docs/README.md b/code/addons/docs/README.md index 025ff9bbd697..9a9a3debad2a 100644 --- a/code/addons/docs/README.md +++ b/code/addons/docs/README.md @@ -156,10 +156,8 @@ export default { `csfPluginOptions` is an object for configuring `@storybook/csf-plugin`. When set to `null` it tells docs not to run the `csf-plugin` at all, which can be used as an optimization, or if you're already using `csf-plugin` in your `main.js`. - > With the release of version 7.0, it is no longer possible to import `.md` files directly into Storybook using the `transcludeMarkdown` [option](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#importing-plain-markdown-files-with-transcludemarkdown-has-changed). Instead, we recommend using the [`Markdown`](https://storybook.js.org/docs/react/api/doc-block-markdown) Doc Block for importing Markdown files into your Storybook documentation. - ## TypeScript configuration As of SB6 [TypeScript is zero-config](https://storybook.js.org/docs/react/configure/typescript) and should work with SB Docs out of the box. For advanced configuration options, refer to the [Props documentation](https://github.com/storybookjs/storybook/tree/next/code/addons/docs/docs/props-tables.md). diff --git a/code/addons/docs/package.json b/code/addons/docs/package.json index 188a64c25830..c6b01de32890 100644 --- a/code/addons/docs/package.json +++ b/code/addons/docs/package.json @@ -94,8 +94,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@jest/transform": "^29.3.1", diff --git a/code/addons/docs/src/ensure-react-peer-deps.ts b/code/addons/docs/src/ensure-react-peer-deps.ts index 6958b7e40653..37520dff343e 100644 --- a/code/addons/docs/src/ensure-react-peer-deps.ts +++ b/code/addons/docs/src/ensure-react-peer-deps.ts @@ -7,13 +7,14 @@ export function ensureReactPeerDeps() { require.resolve('react-dom'); } catch (e) { logger.error(dedent` - Starting in 7.0, react and react-dom are now required peer dependencies of Storybook. + Starting in 7.0, react and react-dom are now required peer dependencies of @storybook/addon-docs. https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#react-peer-dependencies-required - It seems that you haven't run Storybook's CLI to upgrade to the latest version. - The upgrade command will install the required peer dependencies for you and will take - care of other important auto migrations as well. + You can continue to use Storybook without addon-docs, or you can install react and react-dom in your project: + You can use the upgrade command in Storybook's CLI to automatically install the required + peer dependencies for you. + If you want to upgrade to the latest prerelease version, please run: $ npx storybook@next upgrade --prerelease diff --git a/code/addons/docs/src/preset.ts b/code/addons/docs/src/preset.ts index 22b5834c9e9a..c716775bdfa9 100644 --- a/code/addons/docs/src/preset.ts +++ b/code/addons/docs/src/preset.ts @@ -194,4 +194,12 @@ const docsX = docs as any; ensureReactPeerDeps(); -export { webpackX as webpack, indexersX as experimental_indexers, docsX as docs }; +const optimizeViteDeps = [ + '@mdx-js/react', + '@storybook/addon-docs > acorn-jsx', + '@storybook/addon-docs', + '@storybook/addon-essentials/docs/mdx-react-shim', + 'markdown-to-jsx', +]; + +export { webpackX as webpack, indexersX as experimental_indexers, docsX as docs, optimizeViteDeps }; diff --git a/code/addons/essentials/package.json b/code/addons/essentials/package.json index 262aac64aaaf..726026d690bf 100644 --- a/code/addons/essentials/package.json +++ b/code/addons/essentials/package.json @@ -28,81 +28,21 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./actions/preview": { - "types": "./dist/actions/preview.d.ts", - "require": "./dist/actions/preview.js", - "import": "./dist/actions/preview.mjs" - }, - "./actions/manager": { - "types": "./dist/actions/manager.d.ts", - "require": "./dist/actions/manager.js", - "import": "./dist/actions/manager.mjs" - }, - "./backgrounds/preview": { - "types": "./dist/backgrounds/preview.d.ts", - "require": "./dist/backgrounds/preview.js", - "import": "./dist/backgrounds/preview.mjs" - }, - "./backgrounds/manager": { - "types": "./dist/backgrounds/manager.d.ts", - "require": "./dist/backgrounds/manager.js", - "import": "./dist/backgrounds/manager.mjs" - }, - "./controls/manager": { - "types": "./dist/controls/manager.d.ts", - "require": "./dist/controls/manager.js", - "import": "./dist/controls/manager.mjs" - }, - "./docs/preview": { - "types": "./dist/docs/preview.d.ts", - "require": "./dist/docs/preview.js", - "import": "./dist/docs/preview.mjs" - }, - "./docs/preset": { - "types": "./dist/docs/preset.d.ts", - "require": "./dist/docs/preset.js", - "import": "./dist/docs/preset.mjs" - }, - "./docs/mdx-react-shim": { - "types": "./dist/docs/mdx-react-shim.d.ts", - "require": "./dist/docs/mdx-react-shim.js", - "import": "./dist/docs/mdx-react-shim.mjs" - }, - "./highlight/preview": { - "types": "./dist/highlight/preview.d.ts", - "require": "./dist/highlight/preview.js", - "import": "./dist/highlight/preview.mjs" - }, - "./measure/preview": { - "types": "./dist/measure/preview.d.ts", - "require": "./dist/measure/preview.js", - "import": "./dist/measure/preview.mjs" - }, - "./measure/manager": { - "types": "./dist/measure/manager.d.ts", - "require": "./dist/measure/manager.js", - "import": "./dist/measure/manager.mjs" - }, - "./outline/preview": { - "types": "./dist/outline/preview.d.ts", - "require": "./dist/outline/preview.js", - "import": "./dist/outline/preview.mjs" - }, - "./outline/manager": { - "types": "./dist/outline/manager.d.ts", - "require": "./dist/outline/manager.js", - "import": "./dist/outline/manager.mjs" - }, - "./toolbars/manager": { - "types": "./dist/toolbars/manager.d.ts", - "require": "./dist/toolbars/manager.js", - "import": "./dist/toolbars/manager.mjs" - }, - "./viewport/manager": { - "types": "./dist/viewport/manager.d.ts", - "require": "./dist/viewport/manager.js", - "import": "./dist/viewport/manager.mjs" - }, + "./actions/preview": "./dist/actions/preview.js", + "./actions/manager": "./dist/actions/manager.js", + "./backgrounds/preview": "./dist/backgrounds/preview.js", + "./backgrounds/manager": "./dist/backgrounds/manager.js", + "./controls/manager": "./dist/controls/manager.js", + "./docs/preview": "./dist/docs/preview.js", + "./docs/preset": "./dist/docs/preset.js", + "./docs/mdx-react-shim": "./dist/docs/mdx-react-shim.js", + "./highlight/preview": "./dist/highlight/preview.js", + "./measure/preview": "./dist/measure/preview.js", + "./measure/manager": "./dist/measure/manager.js", + "./outline/preview": "./dist/outline/preview.js", + "./outline/manager": "./dist/outline/manager.js", + "./toolbars/manager": "./dist/toolbars/manager.js", + "./viewport/manager": "./dist/viewport/manager.js", "./package.json": "./package.json" }, "main": "dist/index.js", @@ -116,8 +56,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts" }, "dependencies": { "@storybook/addon-actions": "workspace:*", @@ -147,25 +87,28 @@ "access": "public" }, "bundler": { - "entries": [ + "nodeEntries": [ "./src/index.ts", - "./src/actions/preview.ts", + "./src/docs/preset.ts", + "./src/docs/mdx-react-shim.ts" + ], + "managerEntries": [ "./src/actions/manager.ts", - "./src/backgrounds/preview.ts", "./src/backgrounds/manager.ts", "./src/controls/manager.ts", - "./src/docs/preview.ts", - "./src/docs/preset.ts", - "./src/docs/mdx-react-shim.ts", - "./src/highlight/preview.ts", - "./src/measure/preview.ts", "./src/measure/manager.ts", - "./src/outline/preview.ts", "./src/outline/manager.ts", "./src/toolbars/manager.ts", "./src/viewport/manager.ts" ], - "platform": "node" + "previewEntries": [ + "./src/actions/preview.ts", + "./src/backgrounds/preview.ts", + "./src/docs/preview.ts", + "./src/highlight/preview.ts", + "./src/measure/preview.ts", + "./src/outline/preview.ts" + ] }, "gitHead": "e6a7fd8a655c69780bc20b9749c2699e44beae17" } diff --git a/code/addons/essentials/src/actions/preview.ts b/code/addons/essentials/src/actions/preview.ts index 86c6d5f2e656..12b8c76bf2cb 100644 --- a/code/addons/essentials/src/actions/preview.ts +++ b/code/addons/essentials/src/actions/preview.ts @@ -1,4 +1,3 @@ -/* eslint-disable import/export */ // TODO: We need to configure an eslint-import typescript resolver for export maps to be considered export * from '@storybook/addon-actions/preview'; diff --git a/code/addons/essentials/src/backgrounds/manager.ts b/code/addons/essentials/src/backgrounds/manager.ts index 930d5ee38181..9da6a432be39 100644 --- a/code/addons/essentials/src/backgrounds/manager.ts +++ b/code/addons/essentials/src/backgrounds/manager.ts @@ -1 +1,2 @@ +// @ts-expect-error (no types needed for this) export * from '@storybook/addon-backgrounds/manager'; diff --git a/code/addons/essentials/src/backgrounds/preview.ts b/code/addons/essentials/src/backgrounds/preview.ts index 5e56121e4a03..cf24112788f3 100644 --- a/code/addons/essentials/src/backgrounds/preview.ts +++ b/code/addons/essentials/src/backgrounds/preview.ts @@ -1,4 +1,2 @@ -/* eslint-disable import/export */ -// TODO: We need to configure an eslint-import typescript resolver for export maps to be considered - +// @ts-expect-error (no types needed for this) export * from '@storybook/addon-backgrounds/preview'; diff --git a/code/addons/essentials/src/controls/manager.ts b/code/addons/essentials/src/controls/manager.ts index c902436bf5ff..933adbd4e101 100644 --- a/code/addons/essentials/src/controls/manager.ts +++ b/code/addons/essentials/src/controls/manager.ts @@ -1 +1,2 @@ +// @ts-expect-error (no types needed for this) export * from '@storybook/addon-controls/manager'; diff --git a/code/addons/essentials/src/highlight/preview.ts b/code/addons/essentials/src/highlight/preview.ts index ffcf62555af2..e124e7a1374a 100644 --- a/code/addons/essentials/src/highlight/preview.ts +++ b/code/addons/essentials/src/highlight/preview.ts @@ -1,4 +1,2 @@ -/* eslint-disable import/export */ -// TODO: We need to configure an eslint-import typescript resolver for export maps to be considered - +// @ts-expect-error (no types needed for this) export * from '@storybook/addon-highlight/preview'; diff --git a/code/addons/essentials/src/measure/manager.ts b/code/addons/essentials/src/measure/manager.ts index ceef38cd36ad..688ffbed236d 100644 --- a/code/addons/essentials/src/measure/manager.ts +++ b/code/addons/essentials/src/measure/manager.ts @@ -1 +1,2 @@ +// @ts-expect-error (no types needed for this) export * from '@storybook/addon-measure/manager'; diff --git a/code/addons/essentials/src/measure/preview.ts b/code/addons/essentials/src/measure/preview.ts index f18769ff001a..647ef4345a6d 100644 --- a/code/addons/essentials/src/measure/preview.ts +++ b/code/addons/essentials/src/measure/preview.ts @@ -1,4 +1,2 @@ -/* eslint-disable import/export */ -// TODO: We need to configure an eslint-import typescript resolver for export maps to be considered - +// @ts-expect-error (no types needed for this) export * from '@storybook/addon-measure/preview'; diff --git a/code/addons/essentials/src/outline/manager.ts b/code/addons/essentials/src/outline/manager.ts index 9f46ef8cbae4..d3a29db6d98b 100644 --- a/code/addons/essentials/src/outline/manager.ts +++ b/code/addons/essentials/src/outline/manager.ts @@ -1 +1,2 @@ +// @ts-expect-error (no types needed for this) export * from '@storybook/addon-outline/manager'; diff --git a/code/addons/essentials/src/outline/preview.ts b/code/addons/essentials/src/outline/preview.ts index 1cc44b9689a8..3fe09381fe8f 100644 --- a/code/addons/essentials/src/outline/preview.ts +++ b/code/addons/essentials/src/outline/preview.ts @@ -1,4 +1,2 @@ -/* eslint-disable import/export */ -// TODO: We need to configure an eslint-import typescript resolver for export maps to be considered - +// @ts-expect-error (no types needed for this) export * from '@storybook/addon-outline/preview'; diff --git a/code/addons/essentials/src/toolbars/manager.ts b/code/addons/essentials/src/toolbars/manager.ts index fde1c66907a1..888dfe31937d 100644 --- a/code/addons/essentials/src/toolbars/manager.ts +++ b/code/addons/essentials/src/toolbars/manager.ts @@ -1 +1,2 @@ +// @ts-expect-error (no types needed for this) export * from '@storybook/addon-toolbars/manager'; diff --git a/code/addons/essentials/src/viewport/manager.ts b/code/addons/essentials/src/viewport/manager.ts index ccbe283d4101..48bc7a850de6 100644 --- a/code/addons/essentials/src/viewport/manager.ts +++ b/code/addons/essentials/src/viewport/manager.ts @@ -1 +1,2 @@ +// @ts-expect-error (no types needed for this) export * from '@storybook/addon-viewport/manager'; diff --git a/code/addons/gfm/package.json b/code/addons/gfm/package.json index f538d901299e..67e82c3a0bc9 100644 --- a/code/addons/gfm/package.json +++ b/code/addons/gfm/package.json @@ -25,16 +25,8 @@ }, "license": "MIT", "exports": { - ".": { - "types": "./dist/index.d.ts", - "node": "./dist/index.js", - "require": "./dist/index.js" - }, - "./preset": { - "types": "./dist/index.d.ts", - "node": "./dist/index.js", - "require": "./dist/index.js" - }, + ".": "./dist/index.js", + "./preset": "./dist/index.js", "./package.json": "./package.json" }, "main": "dist/index.js", @@ -48,8 +40,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts" }, "dependencies": { "@storybook/node-logger": "workspace:*", @@ -63,11 +55,8 @@ "access": "public" }, "bundler": { - "entries": [ + "nodeEntries": [ "./src/index.ts" - ], - "formats": [ - "cjs" ] }, "gitHead": "e6a7fd8a655c69780bc20b9749c2699e44beae17" diff --git a/code/addons/highlight/package.json b/code/addons/highlight/package.json index b111290f6dcb..759150c8fbe8 100644 --- a/code/addons/highlight/package.json +++ b/code/addons/highlight/package.json @@ -30,26 +30,12 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./preview": { - "types": "./dist/preview.d.ts", - "require": "./dist/preview.js", - "import": "./dist/preview.mjs" - }, + "./preview": "./dist/preview.js", "./package.json": "./package.json" }, "main": "dist/index.js", "module": "dist/index.mjs", "types": "dist/index.d.ts", - "typesVersions": { - "*": { - "*": [ - "dist/index.d.ts" - ], - "preview": [ - "dist/preview.d.ts" - ] - } - }, "files": [ "dist/**/*", "README.md", @@ -58,15 +44,15 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts" }, "dependencies": { - "@storybook/core-events": "workspace:*", - "@storybook/global": "^5.0.0", - "@storybook/preview-api": "workspace:*" + "@storybook/global": "^5.0.0" }, "devDependencies": { + "@storybook/core-events": "workspace:*", + "@storybook/preview-api": "workspace:*", "@types/webpack-env": "^1.16.0", "typescript": "~4.9.3" }, @@ -74,8 +60,10 @@ "access": "public" }, "bundler": { - "entries": [ - "./src/index.ts", + "exportEntries": [ + "./src/index.ts" + ], + "previewEntries": [ "./src/preview.ts" ] }, diff --git a/code/addons/interactions/package.json b/code/addons/interactions/package.json index b4f3c5f8af93..42be6cfd4a9d 100644 --- a/code/addons/interactions/package.json +++ b/code/addons/interactions/package.json @@ -28,39 +28,14 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./manager": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, - "./preview": { - "types": "./dist/preview.d.ts", - "require": "./dist/preview.js", - "import": "./dist/preview.mjs" - }, - "./register.js": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, + "./manager": "./dist/manager.js", + "./preview": "./dist/preview.js", + "./register.js": "./dist/manager.js", "./package.json": "./package.json" }, "main": "dist/index.js", "module": "dist/index.mjs", "types": "dist/index.d.ts", - "typesVersions": { - "*": { - "*": [ - "dist/index.d.ts" - ], - "manager": [ - "dist/manager.d.ts" - ], - "preview": [ - "dist/preview.d.ts" - ] - } - }, "files": [ "dist/**/*", "README.md", @@ -69,19 +44,11 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts" }, "dependencies": { - "@storybook/client-logger": "workspace:*", - "@storybook/components": "workspace:*", - "@storybook/core-common": "workspace:*", - "@storybook/core-events": "workspace:*", "@storybook/global": "^5.0.0", - "@storybook/instrumenter": "workspace:*", - "@storybook/manager-api": "workspace:*", - "@storybook/preview-api": "workspace:*", - "@storybook/theming": "workspace:*", "@storybook/types": "workspace:*", "jest-mock": "^27.0.6", "polished": "^4.2.2", @@ -89,32 +56,36 @@ }, "devDependencies": { "@devtools-ds/object-inspector": "^1.1.2", + "@storybook/client-logger": "workspace:*", + "@storybook/components": "workspace:*", + "@storybook/core-common": "workspace:*", + "@storybook/core-events": "workspace:*", + "@storybook/instrumenter": "workspace:*", "@storybook/jest": "next", + "@storybook/manager-api": "workspace:*", + "@storybook/preview-api": "workspace:*", "@storybook/testing-library": "next", + "@storybook/theming": "workspace:*", "@types/node": "^18.0.0", "formik": "^2.2.9", + "react": "^16.8.0", + "react-dom": "^16.8.0", "typescript": "~4.9.3" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - }, "publishConfig": { "access": "public" }, "bundler": { - "entries": [ - "./src/index.ts", - "./src/manager.tsx", - "./src/preview.ts", + "exportEntries": [ + "./src/index.ts" + ], + "managerEntries": [ + "./src/manager.tsx" + ], + "previewEntries": [ + "./src/preview.ts" + ], + "nodeEntries": [ "./src/preset.ts" ] }, diff --git a/code/addons/jest/package.json b/code/addons/jest/package.json index 33e31ee646f1..ede75c93d24f 100644 --- a/code/addons/jest/package.json +++ b/code/addons/jest/package.json @@ -34,31 +34,13 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./manager": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, - "./register": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, + "./manager": "./dist/manager.js", + "./register": "./dist/manager.js", "./package.json": "./package.json" }, "main": "dist/index.js", "module": "dist/index.mjs", "types": "dist/index.d.ts", - "typesVersions": { - "*": { - "*": [ - "dist/index.d.ts" - ], - "manager": [ - "dist/manager.d.ts" - ] - } - }, "files": [ "dist/**/*", "README.md", @@ -67,45 +49,37 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts" }, "dependencies": { + "@storybook/global": "^5.0.0", + "tiny-invariant": "^1.3.1", + "ts-dedent": "^2.0.0", + "upath": "^2.0.1" + }, + "devDependencies": { "@storybook/client-logger": "workspace:*", "@storybook/components": "workspace:*", "@storybook/core-events": "workspace:*", - "@storybook/global": "^5.0.0", "@storybook/manager-api": "workspace:*", "@storybook/preview-api": "workspace:*", "@storybook/theming": "workspace:*", + "react": "^16.8.0", + "react-dom": "^16.8.0", "react-resize-detector": "^7.1.2", - "tiny-invariant": "^1.3.1", - "upath": "^2.0.1" - }, - "devDependencies": { "typescript": "~4.9.3" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - }, "publishConfig": { "access": "public" }, "bundler": { - "entries": [ - "./src/index.ts", - "./src/manager.tsx" + "exportEntries": [ + "./src/index.ts" ], - "platform": "browser" + "managerEntries": [ + "./src/manager.tsx" + ] }, "gitHead": "e6a7fd8a655c69780bc20b9749c2699e44beae17", "storybook": { diff --git a/code/addons/links/package.json b/code/addons/links/package.json index 4526d5d0bbfc..a2b252eee6c2 100644 --- a/code/addons/links/package.json +++ b/code/addons/links/package.json @@ -28,26 +28,14 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./manager": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, - "./preview": { - "types": "./dist/preview.d.ts", - "require": "./dist/preview.js", - "import": "./dist/preview.mjs" - }, "./react": { "types": "./dist/react/index.d.ts", "require": "./dist/react/index.js", "import": "./dist/react/index.mjs" }, - "./register": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, + "./manager": "./dist/manager.js", + "./preview": "./dist/preview.js", + "./register": "./dist/manager.js", "./package.json": "./package.json" }, "main": "dist/index.js", @@ -58,12 +46,6 @@ "*": [ "dist/index.d.ts" ], - "manager": [ - "dist/manager.d.ts" - ], - "preview": [ - "dist/preview.d.ts" - ], "react": [ "dist/react/index.d.ts" ] @@ -77,46 +59,45 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts" }, "dependencies": { + "@storybook/csf": "^0.1.2", + "@storybook/global": "^5.0.0", + "ts-dedent": "^2.0.0" + }, + "devDependencies": { "@storybook/client-logger": "workspace:*", "@storybook/core-events": "workspace:*", - "@storybook/csf": "^0.1.0", - "@storybook/global": "^5.0.0", "@storybook/manager-api": "workspace:*", "@storybook/preview-api": "workspace:*", "@storybook/router": "workspace:*", "@storybook/types": "workspace:*", - "prop-types": "^15.7.2", - "ts-dedent": "^2.0.0" - }, - "devDependencies": { "fs-extra": "^11.1.0", "typescript": "~4.9.3" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" }, "peerDependenciesMeta": { "react": { "optional": true - }, - "react-dom": { - "optional": true } }, "publishConfig": { "access": "public" }, "bundler": { - "entries": [ - "./src/index.ts", - "./src/manager.ts", - "./src/preview.ts", - "./src/react/index.ts" + "exportEntries": [ + "./src/react/index.ts", + "./src/index.ts" + ], + "managerEntries": [ + "./src/manager.ts" + ], + "previewEntries": [ + "./src/preview.ts" ], "post": "./scripts/fix-preview-api-reference.ts" }, diff --git a/code/addons/measure/package.json b/code/addons/measure/package.json index b38d09b85892..20b1620451bc 100644 --- a/code/addons/measure/package.json +++ b/code/addons/measure/package.json @@ -31,21 +31,9 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./manager": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, - "./preview": { - "types": "./dist/preview.d.ts", - "require": "./dist/preview.js", - "import": "./dist/preview.mjs" - }, - "./register": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, + "./manager": "./dist/manager.js", + "./preview": "./dist/preview.js", + "./register": "./dist/manager.js", "./package.json": "./package.json" }, "main": "dist/index.js", @@ -72,41 +60,35 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts" }, "dependencies": { + "@storybook/global": "^5.0.0", + "tiny-invariant": "^1.3.1" + }, + "devDependencies": { "@storybook/client-logger": "workspace:*", "@storybook/components": "workspace:*", "@storybook/core-events": "workspace:*", - "@storybook/global": "^5.0.0", "@storybook/manager-api": "workspace:*", "@storybook/preview-api": "workspace:*", "@storybook/types": "workspace:*", - "tiny-invariant": "^1.3.1" - }, - "devDependencies": { + "react": "^16.8.0", + "react-dom": "^16.8.0", "typescript": "~4.9.3" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - }, "publishConfig": { "access": "public" }, "bundler": { - "entries": [ - "./src/index.ts", - "./src/manager.tsx", + "exportEntries": [ + "./src/index.ts" + ], + "managerEntries": [ + "./src/manager.tsx" + ], + "previewEntries": [ "./src/preview.tsx" ] }, diff --git a/code/addons/outline/package.json b/code/addons/outline/package.json index 7dc8e144054e..37d91d2c8bd9 100644 --- a/code/addons/outline/package.json +++ b/code/addons/outline/package.json @@ -34,39 +34,14 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./manager": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, - "./preview": { - "types": "./dist/preview.d.ts", - "require": "./dist/preview.js", - "import": "./dist/preview.mjs" - }, - "./register": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, + "./manager": "./dist/manager.js", + "./preview": "./dist/preview.js", + "./register": "./dist/manager.js", "./package.json": "./package.json" }, "main": "dist/index.js", "module": "dist/index.mjs", "types": "dist/index.d.ts", - "typesVersions": { - "*": { - "*": [ - "dist/index.d.ts" - ], - "manager": [ - "dist/manager.d.ts" - ], - "preview": [ - "dist/preview.d.ts" - ] - } - }, "files": [ "dist/**/*", "README.md", @@ -75,41 +50,35 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts" }, "dependencies": { + "@storybook/global": "^5.0.0", + "ts-dedent": "^2.0.0" + }, + "devDependencies": { "@storybook/client-logger": "workspace:*", "@storybook/components": "workspace:*", "@storybook/core-events": "workspace:*", - "@storybook/global": "^5.0.0", "@storybook/manager-api": "workspace:*", "@storybook/preview-api": "workspace:*", "@storybook/types": "workspace:*", - "ts-dedent": "^2.0.0" - }, - "devDependencies": { + "react": "^16.8.0", + "react-dom": "^16.8.0", "typescript": "~4.9.3" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - }, "publishConfig": { "access": "public" }, "bundler": { - "entries": [ - "./src/index.ts", - "./src/manager.tsx", + "exportEntries": [ + "./src/index.ts" + ], + "managerEntries": [ + "./src/manager.tsx" + ], + "previewEntries": [ "./src/preview.tsx" ] }, diff --git a/code/addons/storyshots-core/package.json b/code/addons/storyshots-core/package.json index b82f8596a2fe..32daad4a0c65 100644 --- a/code/addons/storyshots-core/package.json +++ b/code/addons/storyshots-core/package.json @@ -33,8 +33,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/tsc.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/tsc.ts" }, "dependencies": { "@jest/transform": "^29.3.1", diff --git a/code/addons/storyshots-puppeteer/package.json b/code/addons/storyshots-puppeteer/package.json index 4652242eb34a..4685d9a82237 100644 --- a/code/addons/storyshots-puppeteer/package.json +++ b/code/addons/storyshots-puppeteer/package.json @@ -32,12 +32,12 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/tsc.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/tsc.ts" }, "dependencies": { "@axe-core/puppeteer": "^4.2.0", - "@storybook/csf": "^0.1.0", + "@storybook/csf": "^0.1.2", "@storybook/node-logger": "workspace:*", "@storybook/types": "workspace:*", "@types/jest-image-snapshot": "^6.0.0", diff --git a/code/addons/storysource/package.json b/code/addons/storysource/package.json index 8bcf86278daf..8690cb8ef663 100644 --- a/code/addons/storysource/package.json +++ b/code/addons/storysource/package.json @@ -28,15 +28,8 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./preset": { - "types": "./dist/preset.d.ts", - "require": "./dist/preset.js" - }, - "./manager": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, + "./preset": "./dist/preset.js", + "./manager": "./dist/manager.js", "./package.json": "./package.json" }, "main": "dist/index.js", @@ -50,10 +43,14 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts" }, "dependencies": { + "estraverse": "^5.2.0", + "tiny-invariant": "^1.3.1" + }, + "devDependencies": { "@storybook/client-logger": "workspace:*", "@storybook/components": "workspace:*", "@storybook/manager-api": "workspace:*", @@ -61,35 +58,24 @@ "@storybook/router": "workspace:*", "@storybook/source-loader": "workspace:*", "@storybook/theming": "workspace:*", - "estraverse": "^5.2.0", - "prop-types": "^15.7.2", - "react-syntax-highlighter": "^15.5.0", - "tiny-invariant": "^1.3.1" - }, - "devDependencies": { "@types/react": "^16.14.34", "@types/react-syntax-highlighter": "11.0.5", + "react": "^16.8.0", + "react-dom": "^16.8.0", + "react-syntax-highlighter": "^15.5.0", "typescript": "~4.9.3" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - }, "publishConfig": { "access": "public" }, "bundler": { - "entries": [ - "./src/index.ts", - "./src/manager.tsx", + "exportEntries": [ + "./src/index.ts" + ], + "managerEntries": [ + "./src/manager.tsx" + ], + "nodeEntries": [ "./src/preset.ts" ] }, diff --git a/code/addons/themes/package.json b/code/addons/themes/package.json index 3338bf3ed398..bdd8020997fc 100644 --- a/code/addons/themes/package.json +++ b/code/addons/themes/package.json @@ -33,35 +33,14 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./manager": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, - "./preview": { - "types": "./dist/preview.d.ts", - "require": "./dist/preview.js", - "import": "./dist/preview.mjs" - }, + "./manager": "./dist/manager.js", + "./preview": "./dist/preview.js", "./package.json": "./package.json", "./postinstall": "./postinstall.js" }, "main": "dist/index.js", "module": "dist/index.mjs", "types": "dist/index.d.ts", - "typesVersions": { - "*": { - "*": [ - "dist/index.d.ts" - ], - "manager": [ - "dist/manager.d.ts" - ], - "preview": [ - "dist/preview.d.ts" - ] - } - }, "files": [ "dist/**/*", "README.md", @@ -70,10 +49,13 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts" }, "dependencies": { + "ts-dedent": "^2.0.0" + }, + "devDependencies": { "@storybook/client-logger": "workspace:*", "@storybook/components": "workspace:*", "@storybook/core-events": "workspace:*", @@ -81,30 +63,19 @@ "@storybook/preview-api": "workspace:*", "@storybook/theming": "workspace:*", "@storybook/types": "workspace:*", - "ts-dedent": "^2.0.0" - }, - "devDependencies": { "typescript": "~4.9.3" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - }, "publishConfig": { "access": "public" }, "bundler": { - "entries": [ - "./src/index.ts", - "./src/manager.tsx", + "exportEntries": [ + "./src/index.ts" + ], + "managerEntries": [ + "./src/manager.tsx" + ], + "previewEntries": [ "./src/preview.tsx" ] }, diff --git a/code/addons/toolbars/package.json b/code/addons/toolbars/package.json index b84276721ae8..913f0cde1584 100644 --- a/code/addons/toolbars/package.json +++ b/code/addons/toolbars/package.json @@ -32,31 +32,13 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./manager": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, - "./register": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, + "./manager": "./dist/manager.js", + "./register": "./dist/manager.js", "./package.json": "./package.json" }, "main": "dist/index.js", "module": "dist/index.mjs", "types": "dist/index.d.ts", - "typesVersions": { - "*": { - "*": [ - "dist/index.d.ts" - ], - "manager": [ - "dist/manager.d.ts" - ] - } - }, "files": [ "dist/**/*", "README.md", @@ -65,40 +47,29 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts" }, - "dependencies": { + "devDependencies": { "@storybook/client-logger": "workspace:*", "@storybook/components": "workspace:*", "@storybook/manager-api": "workspace:*", "@storybook/preview-api": "workspace:*", - "@storybook/theming": "workspace:*" - }, - "devDependencies": { + "@storybook/theming": "workspace:*", + "react": "^16.8.0", + "react-dom": "^16.8.0", "typescript": "~4.9.3" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - }, "publishConfig": { "access": "public" }, "bundler": { - "entries": [ - "./src/index.ts", - "./src/manager.tsx" + "exportEntries": [ + "./src/index.ts" ], - "platform": "browser" + "managerEntries": [ + "./src/manager.tsx" + ] }, "gitHead": "e6a7fd8a655c69780bc20b9749c2699e44beae17", "storybook": { diff --git a/code/addons/viewport/package.json b/code/addons/viewport/package.json index de4a9bfc95e9..ea14a7af6b11 100644 --- a/code/addons/viewport/package.json +++ b/code/addons/viewport/package.json @@ -29,39 +29,14 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./manager": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, - "./preview": { - "types": "./dist/preview.d.ts", - "require": "./dist/preview.ts", - "import": "./dist/preview.mjs" - }, - "./register": { - "types": "./dist/manager.d.ts", - "require": "./dist/manager.js", - "import": "./dist/manager.mjs" - }, + "./manager": "./dist/manager.js", + "./preview": "./dist/preview.ts", + "./register": "./dist/manager.js", "./package.json": "./package.json" }, "main": "dist/index.js", "module": "dist/index.mjs", "types": "dist/index.d.ts", - "typesVersions": { - "*": { - "*": [ - "dist/index.d.ts" - ], - "manager": [ - "dist/manager.d.ts" - ], - "preview": [ - "dist/preview.d.ts" - ] - } - }, "files": [ "dist/**/*", "README.md", @@ -70,10 +45,13 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts" }, "dependencies": { + "memoizerific": "^1.11.3" + }, + "devDependencies": { "@storybook/client-logger": "workspace:*", "@storybook/components": "workspace:*", "@storybook/core-events": "workspace:*", @@ -81,32 +59,22 @@ "@storybook/manager-api": "workspace:*", "@storybook/preview-api": "workspace:*", "@storybook/theming": "workspace:*", - "memoizerific": "^1.11.3", - "prop-types": "^15.7.2" - }, - "devDependencies": { + "react": "^16.8.0", + "react-dom": "^16.8.0", "typescript": "~4.9.3" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - }, "publishConfig": { "access": "public" }, "bundler": { - "entries": [ - "./src/index.ts", + "exportEntries": [ "./src/models/index.ts", - "./src/manager.tsx", + "./src/index.ts" + ], + "managerEntries": [ + "./src/manager.tsx" + ], + "previewEntries": [ "./src/preview.ts" ] }, diff --git a/code/builders/builder-manager/package.json b/code/builders/builder-manager/package.json index 00b9f03c1602..ea6c50d5cc37 100644 --- a/code/builders/builder-manager/package.json +++ b/code/builders/builder-manager/package.json @@ -40,8 +40,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@fal-works/esbuild-plugin-global-externals": "^2.1.2", diff --git a/code/builders/builder-manager/src/index.ts b/code/builders/builder-manager/src/index.ts index eb869bb54f2a..89b9846184e6 100644 --- a/code/builders/builder-manager/src/index.ts +++ b/code/builders/builder-manager/src/index.ts @@ -9,8 +9,8 @@ import { pnpPlugin } from '@yarnpkg/esbuild-plugin-pnp'; import aliasPlugin from 'esbuild-plugin-alias'; import { stringifyProcessEnvs } from '@storybook/core-common'; +import { globalsModuleInfoMap } from '@storybook/manager/globals-module-info'; import { getTemplatePath, renderHTML } from './utils/template'; -import { definitions } from './utils/globals'; import { wrapManagerEntries } from './utils/managerEntries'; import type { BuilderBuildResult, @@ -89,7 +89,7 @@ export const getConfig: ManagerBuilder['getConfig'] = async (options) => { util: require.resolve('util/util.js'), assert: require.resolve('browser-assert'), }), - globalExternals(definitions), + globalExternals(globalsModuleInfoMap), pnpPlugin(), ], diff --git a/code/builders/builder-manager/src/utils/globals.ts b/code/builders/builder-manager/src/utils/globals.ts deleted file mode 100644 index 08fda5f5a44b..000000000000 --- a/code/builders/builder-manager/src/utils/globals.ts +++ /dev/null @@ -1 +0,0 @@ -export { definitions } from '@storybook/manager/dist/globals'; diff --git a/code/builders/builder-vite/package.json b/code/builders/builder-vite/package.json index c0c5b67fc2ad..54032ef7d858 100644 --- a/code/builders/builder-vite/package.json +++ b/code/builders/builder-vite/package.json @@ -39,8 +39,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/channels": "workspace:*", diff --git a/code/builders/builder-vite/src/optimizeDeps.ts b/code/builders/builder-vite/src/optimizeDeps.ts index ddb32c800403..1972fde8e8e7 100644 --- a/code/builders/builder-vite/src/optimizeDeps.ts +++ b/code/builders/builder-vite/src/optimizeDeps.ts @@ -8,21 +8,9 @@ const INCLUDE_CANDIDATES = [ '@emotion/core', '@emotion/is-prop-valid', '@emotion/styled', - '@mdx-js/react', - '@storybook/addon-docs > acorn-jsx', - '@storybook/addon-docs', - '@storybook/addon-essentials/docs/mdx-react-shim', - '@storybook/channels', - '@storybook/client-api', - '@storybook/client-logger', - '@storybook/core/client', - '@storybook/global', - '@storybook/preview-api', - '@storybook/preview-web', '@storybook/react > acorn-jsx', '@storybook/react', '@storybook/svelte', - '@storybook/types', '@storybook/vue3', 'acorn-jsx', 'acorn-walk', @@ -79,7 +67,6 @@ const INCLUDE_CANDIDATES = [ 'lodash/uniq', 'lodash/upperFirst.js', 'lodash/upperFirst', - 'markdown-to-jsx', 'memoizerific', 'overlayscrollbars', 'polished', @@ -126,6 +113,8 @@ const asyncFilter = async (arr: string[], predicate: (val: string) => Promise arr.filter((_v, index) => results[index])); export async function getOptimizeDeps(config: ViteInlineConfig, options: Options) { + const extraOptimizeDeps = await options.presets.apply('optimizeViteDeps', []); + const { root = process.cwd() } = config; const { normalizePath, resolveConfig } = await import('vite'); const absoluteStories = await listStories(options); @@ -136,7 +125,10 @@ export async function getOptimizeDeps(config: ViteInlineConfig, options: Options // This function converts ids which might include ` > ` to a real path, if it exists on disk. // See https://github.com/vitejs/vite/blob/67d164392e8e9081dc3f0338c4b4b8eea6c5f7da/packages/vite/src/node/optimizer/index.ts#L182-L199 const resolve = resolvedConfig.createResolver({ asSrc: false }); - const include = await asyncFilter(INCLUDE_CANDIDATES, async (id) => Boolean(await resolve(id))); + const include = await asyncFilter( + Array.from(new Set([...INCLUDE_CANDIDATES, ...extraOptimizeDeps])), + async (id) => Boolean(await resolve(id)) + ); const optimizeDeps: UserConfig['optimizeDeps'] = { ...config.optimizeDeps, diff --git a/code/builders/builder-vite/src/vite-config.ts b/code/builders/builder-vite/src/vite-config.ts index 6beb0ec73aba..def3e631cb65 100644 --- a/code/builders/builder-vite/src/vite-config.ts +++ b/code/builders/builder-vite/src/vite-config.ts @@ -8,7 +8,7 @@ import type { InlineConfig, } from 'vite'; import { isPreservingSymlinks, getFrameworkName, getBuilderOptions } from '@storybook/core-common'; -import { globals } from '@storybook/preview/globals'; +import { globalsNameReferenceMap } from '@storybook/preview/globals'; import type { Options } from '@storybook/types'; import { codeGeneratorPlugin, @@ -79,8 +79,10 @@ export async function pluginConfig(options: Options) { const frameworkName = await getFrameworkName(options); const build = await options.presets.apply('build'); + const externals: Record = globalsNameReferenceMap; + if (build?.test?.disableBlocks) { - globals['@storybook/blocks'] = '__STORYBOOK_BLOCKS_EMPTY_MODULE__'; + externals['@storybook/blocks'] = '__STORYBOOK_BLOCKS_EMPTY_MODULE__'; } const plugins = [ @@ -101,7 +103,7 @@ export async function pluginConfig(options: Options) { } }, }, - await externalGlobalsPlugin(globals), + await externalGlobalsPlugin(externals), ] as PluginOption[]; // TODO: framework doesn't exist, should move into framework when/if built diff --git a/code/builders/builder-webpack5/package.json b/code/builders/builder-webpack5/package.json index 12efe03a6f9b..c249b317183d 100644 --- a/code/builders/builder-webpack5/package.json +++ b/code/builders/builder-webpack5/package.json @@ -57,8 +57,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@babel/core": "^7.23.2", diff --git a/code/builders/builder-webpack5/src/preview/iframe-webpack.config.ts b/code/builders/builder-webpack5/src/preview/iframe-webpack.config.ts index 13b02df7264f..ad8fe95f4b39 100644 --- a/code/builders/builder-webpack5/src/preview/iframe-webpack.config.ts +++ b/code/builders/builder-webpack5/src/preview/iframe-webpack.config.ts @@ -1,4 +1,4 @@ -import { dirname, isAbsolute, join, resolve } from 'path'; +import { dirname, join, resolve } from 'path'; import { DefinePlugin, HotModuleReplacementPlugin, ProgressPlugin, ProvidePlugin } from 'webpack'; import type { Configuration } from 'webpack'; import HtmlWebpackPlugin from 'html-webpack-plugin'; @@ -7,25 +7,20 @@ import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin'; import TerserWebpackPlugin from 'terser-webpack-plugin'; import VirtualModulePlugin from 'webpack-virtual-modules'; import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'; -import slash from 'slash'; import type { TransformOptions as EsbuildOptions } from 'esbuild'; import type { JsMinifyOptions as SwcOptions } from '@swc/core'; -import type { Options, CoreConfig, DocsOptions, PreviewAnnotation } from '@storybook/types'; -import { globals } from '@storybook/preview/globals'; +import type { Options, CoreConfig, DocsOptions } from '@storybook/types'; +import { globalsNameReferenceMap } from '@storybook/preview/globals'; import { getBuilderOptions, - getRendererName, stringifyProcessEnvs, - handlebars, - interpolate, normalizeStories, - readTemplate, - loadPreviewOrConfigFile, isPreservingSymlinks, } from '@storybook/core-common'; -import { toRequireContextString, toImportFn } from '@storybook/core-webpack'; +import type { BuilderOptions } from '@storybook/core-webpack'; +import { getVirtualModuleMapping } from '@storybook/core-webpack'; import { dedent } from 'ts-dedent'; -import type { BuilderOptions, TypescriptOptions } from '../types'; +import type { TypescriptOptions } from '../types'; import { createBabelLoader, createSWCLoader } from './loaders'; const getAbsolutePath = (input: I): I => @@ -114,93 +109,8 @@ export default async ( const builderOptions = await getBuilderOptions(options); - const previewAnnotations = [ - ...(await presets.apply('previewAnnotations', [], options)).map( - (entry) => { - // If entry is an object, use the absolute import specifier. - // This is to maintain back-compat with community addons that bundle other addons - // and package managers that "hide" sub dependencies (e.g. pnpm / yarn pnp) - // The vite builder uses the bare import specifier. - if (typeof entry === 'object') { - return entry.absolute; - } - - // TODO: Remove as soon as we drop support for disabled StoryStoreV7 - if (isAbsolute(entry)) { - return entry; - } - - return slash(entry); - } - ), - loadPreviewOrConfigFile(options), - ].filter(Boolean); - - const virtualModuleMapping: Record = {}; - if (features?.storyStoreV7) { - const storiesFilename = 'storybook-stories.js'; - const storiesPath = resolve(join(workingDir, storiesFilename)); - - const needPipelinedImport = !!builderOptions.lazyCompilation && !isProd; - virtualModuleMapping[storiesPath] = toImportFn(stories, { needPipelinedImport }); - const configEntryPath = resolve(join(workingDir, 'storybook-config-entry.js')); - virtualModuleMapping[configEntryPath] = handlebars( - await readTemplate( - require.resolve( - '@storybook/builder-webpack5/templates/virtualModuleModernEntry.js.handlebars' - ) - ), - { - storiesFilename, - previewAnnotations, - } - // We need to double escape `\` for webpack. We may have some in windows paths - ).replace(/\\/g, '\\\\'); - entries.push(configEntryPath); - } else { - const rendererName = await getRendererName(options); - - const rendererInitEntry = resolve(join(workingDir, 'storybook-init-renderer-entry.js')); - virtualModuleMapping[rendererInitEntry] = `import '${slash(rendererName)}';`; - entries.push(rendererInitEntry); - - const entryTemplate = await readTemplate( - join(__dirname, '..', '..', 'templates', 'virtualModuleEntry.template.js') - ); - - previewAnnotations.forEach((previewAnnotationFilename: string | undefined) => { - if (!previewAnnotationFilename) return; - - // Ensure that relative paths end up mapped to a filename in the cwd, so a later import - // of the `previewAnnotationFilename` in the template works. - const entryFilename = previewAnnotationFilename.startsWith('.') - ? `${previewAnnotationFilename.replace(/(\w)(\/|\\)/g, '$1-')}-generated-config-entry.js` - : `${previewAnnotationFilename}-generated-config-entry.js`; - // NOTE: although this file is also from the `dist/cjs` directory, it is actually a ESM - // file, see https://github.com/storybookjs/storybook/pull/16727#issuecomment-986485173 - virtualModuleMapping[entryFilename] = interpolate(entryTemplate, { - previewAnnotationFilename, - }); - entries.push(entryFilename); - }); - if (stories.length > 0) { - const storyTemplate = await readTemplate( - join(__dirname, '..', '..', 'templates', 'virtualModuleStory.template.js') - ); - // NOTE: this file has a `.cjs` extension as it is a CJS file (from `dist/cjs`) and runs - // in the user's webpack mode, which may be strict about the use of require/import. - // See https://github.com/storybookjs/storybook/issues/14877 - const storiesFilename = resolve(join(workingDir, `generated-stories-entry.cjs`)); - virtualModuleMapping[storiesFilename] = interpolate(storyTemplate, { - rendererName, - }) - // Make sure we also replace quotes for this one - .replace("'{{stories}}'", stories.map(toRequireContextString).join(',')); - entries.push(storiesFilename); - } - } - - const shouldCheckTs = typescriptOptions.check && !typescriptOptions.skipBabel; + const shouldCheckTs = + typescriptOptions.check && !typescriptOptions.skipBabel && !typescriptOptions.skipCompiler; const tsCheckOptions = typescriptOptions.checkOptions || {}; const cacheConfig = builderOptions.fsCache ? { cache: { type: 'filesystem' as const } } : {}; @@ -220,10 +130,17 @@ export default async ( `); } + const externals: Record = globalsNameReferenceMap; if (build?.test?.disableBlocks) { - globals['@storybook/blocks'] = '__STORYBOOK_BLOCKS_EMPTY_MODULE__'; + externals['@storybook/blocks'] = '__STORYBOOK_BLOCKS_EMPTY_MODULE__'; } + const virtualModuleMapping = await getVirtualModuleMapping(options); + + Object.keys(virtualModuleMapping).forEach((key) => { + entries.push(key); + }); + return { name: 'preview', mode: isProd ? 'production' : 'development', @@ -242,7 +159,7 @@ export default async ( watchOptions: { ignored: /node_modules/, }, - externals: globals, + externals, ignoreWarnings: [ { message: /export '\S+' was not found in 'global'/, diff --git a/code/builders/builder-webpack5/src/preview/loaders.ts b/code/builders/builder-webpack5/src/preview/loaders.ts index 26943cd90e06..f10bd93a5ebc 100644 --- a/code/builders/builder-webpack5/src/preview/loaders.ts +++ b/code/builders/builder-webpack5/src/preview/loaders.ts @@ -10,6 +10,7 @@ export const createBabelLoader = ( typescriptOptions: TypescriptOptions, excludes: string[] = [] ) => { + logger.info(dedent`Using Babel compiler`); return { test: typescriptOptions.skipBabel ? /\.(mjs|jsx?)$/ : /\.(mjs|tsx?|jsx?)$/, use: [ @@ -24,9 +25,7 @@ export const createBabelLoader = ( }; export const createSWCLoader = async (excludes: string[] = [], options: Options) => { - logger.warn(dedent` - The SWC loader is an experimental feature and may change or even be removed at any time. - `); + logger.info(dedent`Using SWC compiler`); const swc = await options.presets.apply('swc', {}, options); const typescriptOptions = await options.presets.apply<{ skipCompiler?: boolean }>( @@ -49,12 +48,8 @@ export const createSWCLoader = async (excludes: string[] = [], options: Options) }; return { test: typescriptOptions.skipCompiler ? /\.(mjs|cjs|jsx?)$/ : /\.(mjs|cjs|tsx?|jsx?)$/, - use: [ - { - loader: require.resolve('swc-loader'), - options: config, - }, - ], + loader: require.resolve('swc-loader'), + options: config, include: [getProjectRoot()], exclude: [/node_modules/, ...excludes], }; diff --git a/code/deprecated/addons/package.json b/code/deprecated/addons/package.json index 6aab52de8641..2506f3af7c4c 100644 --- a/code/deprecated/addons/package.json +++ b/code/deprecated/addons/package.json @@ -40,18 +40,14 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/manager-api": "workspace:*", "@storybook/preview-api": "workspace:*", "@storybook/types": "workspace:*" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, "publishConfig": { "access": "public" }, diff --git a/code/deprecated/channel-postmessage/package.json b/code/deprecated/channel-postmessage/package.json index d2f482a44d9e..f4ab905d3b0e 100644 --- a/code/deprecated/channel-postmessage/package.json +++ b/code/deprecated/channel-postmessage/package.json @@ -40,8 +40,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/channels": "workspace:*", diff --git a/code/deprecated/channel-websocket/package.json b/code/deprecated/channel-websocket/package.json index 249f1c7dc2fb..547a60e2e7f8 100644 --- a/code/deprecated/channel-websocket/package.json +++ b/code/deprecated/channel-websocket/package.json @@ -40,8 +40,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/facade.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/facade.ts" }, "dependencies": { "@storybook/channels": "workspace:*", diff --git a/code/deprecated/client-api/package.json b/code/deprecated/client-api/package.json index edc3eeebf5b7..db964d08e85f 100644 --- a/code/deprecated/client-api/package.json +++ b/code/deprecated/client-api/package.json @@ -39,8 +39,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/facade.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/facade.ts" }, "dependencies": { "@storybook/client-logger": "workspace:*", diff --git a/code/deprecated/core-client/package.json b/code/deprecated/core-client/package.json index 31a014832e83..82e2986e8f14 100644 --- a/code/deprecated/core-client/package.json +++ b/code/deprecated/core-client/package.json @@ -31,8 +31,8 @@ "module": "dist/entry.mjs", "types": "dist/entry.d.ts", "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/facade.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/facade.ts" }, "dependencies": { "@storybook/client-logger": "workspace:*", diff --git a/code/deprecated/manager-api-shim/package.json b/code/deprecated/manager-api-shim/package.json index c190ccefa7db..4c4f2c34779d 100644 --- a/code/deprecated/manager-api-shim/package.json +++ b/code/deprecated/manager-api-shim/package.json @@ -39,25 +39,13 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/facade.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/facade.ts" }, "dependencies": { "@storybook/client-logger": "workspace:*", "@storybook/manager-api": "workspace:*" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - }, "publishConfig": { "access": "public" }, diff --git a/code/deprecated/preview-web/package.json b/code/deprecated/preview-web/package.json index 57ebc960025f..30fbb375488b 100644 --- a/code/deprecated/preview-web/package.json +++ b/code/deprecated/preview-web/package.json @@ -39,8 +39,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/facade.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/facade.ts" }, "dependencies": { "@storybook/client-logger": "workspace:*", diff --git a/code/deprecated/store/package.json b/code/deprecated/store/package.json index 37413adc6c40..9173b1b4ee5d 100644 --- a/code/deprecated/store/package.json +++ b/code/deprecated/store/package.json @@ -39,8 +39,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/facade.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/facade.ts" }, "dependencies": { "@storybook/client-logger": "workspace:*", diff --git a/code/e2e-tests/addon-controls.spec.ts b/code/e2e-tests/addon-controls.spec.ts index df376782aae8..2c388d7ef598 100644 --- a/code/e2e-tests/addon-controls.spec.ts +++ b/code/e2e-tests/addon-controls.spec.ts @@ -31,18 +31,22 @@ test.describe('addon-controls', () => { ); const toggle = sbPage.panelContent().locator('input[name=primary]'); await toggle.click(); - await expect(sbPage.previewRoot().locator('button')).toHaveCSS( - 'background-color', - 'rgba(0, 0, 0, 0)' - ); + await expect(async () => { + await expect(sbPage.previewRoot().locator('button')).toHaveCSS( + 'background-color', + 'rgba(0, 0, 0, 0)' + ); + }).toPass(); // Color picker: Background color const color = sbPage.panelContent().locator('input[placeholder="Choose color..."]'); await color.fill('red'); - await expect(sbPage.previewRoot().locator('button')).toHaveCSS( - 'background-color', - 'rgb(255, 0, 0)' - ); + await expect(async () => { + await expect(sbPage.previewRoot().locator('button')).toHaveCSS( + 'background-color', + 'rgb(255, 0, 0)' + ); + }).toPass(); // TODO: enable this once the controls for size are aligned in all CLI templates. // Radio buttons: Size diff --git a/code/e2e-tests/framework-nextjs.spec.ts b/code/e2e-tests/framework-nextjs.spec.ts index 440c079d1704..4ca67544db74 100644 --- a/code/e2e-tests/framework-nextjs.spec.ts +++ b/code/e2e-tests/framework-nextjs.spec.ts @@ -30,7 +30,7 @@ test.describe('Next.js', () => { // TODO: Test is flaky, investigate why test.skip('should lazy load images by default', async () => { - await sbPage.navigateToStory('frameworks/nextjs/Image', 'lazy'); + await sbPage.navigateToStory('stories/frameworks/nextjs/Image', 'lazy'); const img = sbPage.previewRoot().locator('img'); @@ -39,7 +39,7 @@ test.describe('Next.js', () => { // TODO: Test is flaky, investigate why test.skip('should eager load images when loading parameter is set to eager', async () => { - await sbPage.navigateToStory('frameworks/nextjs/Image', 'eager'); + await sbPage.navigateToStory('stories/frameworks/nextjs/Image', 'eager'); const img = sbPage.previewRoot().locator('img'); @@ -67,7 +67,10 @@ test.describe('Next.js', () => { test.beforeEach(async ({ page }) => { sbPage = new SbPage(page); - await sbPage.navigateToStory('frameworks/nextjs-nextjs-default-js/Navigation', 'default'); + await sbPage.navigateToStory( + 'stories/frameworks/nextjs-nextjs-default-js/Navigation', + 'default' + ); root = sbPage.previewRoot(); }); @@ -99,7 +102,7 @@ test.describe('Next.js', () => { test.beforeEach(async ({ page }) => { sbPage = new SbPage(page); - await sbPage.navigateToStory('frameworks/nextjs-nextjs-default-js/Router', 'default'); + await sbPage.navigateToStory('stories/frameworks/nextjs-nextjs-default-js/Router', 'default'); root = sbPage.previewRoot(); }); diff --git a/code/e2e-tests/framework-svelte.spec.ts b/code/e2e-tests/framework-svelte.spec.ts index a208aa982d9c..ceffacc525d7 100644 --- a/code/e2e-tests/framework-svelte.spec.ts +++ b/code/e2e-tests/framework-svelte.spec.ts @@ -1,12 +1,16 @@ /* eslint-disable jest/no-disabled-tests */ import { test, expect } from '@playwright/test'; import process from 'process'; -import dedent from 'ts-dedent'; import { SbPage } from './util'; const storybookUrl = process.env.STORYBOOK_URL || 'http://localhost:6006'; const templateName = process.env.STORYBOOK_TEMPLATE_NAME; +test.beforeEach(async ({ page }) => { + await page.goto(storybookUrl); + await new SbPage(page).waitUntilLoaded(); +}); + test.describe('Svelte', () => { test.skip( // eslint-disable-next-line jest/valid-title @@ -14,11 +18,6 @@ test.describe('Svelte', () => { 'Only run this test on Svelte' ); - test.beforeEach(async ({ page }) => { - await page.goto(storybookUrl); - await new SbPage(page).waitUntilLoaded(); - }); - test('JS story has auto-generated args table', async ({ page }) => { const sbPage = new SbPage(page); @@ -50,4 +49,46 @@ test.describe('Svelte', () => { const expectedSource = ''; await expect(sourceCode.textContent()).resolves.toContain(expectedSource); }); + + test('Decorators runs only once', async ({ page }) => { + const sbPage = new SbPage(page); + const lines: string[] = []; + page.on('console', (msg) => { + const text = msg.text(); + if (text === 'decorator called') { + lines.push(text); + } + }); + + await sbPage.navigateToStory('stories/renderers/svelte/decorators-runs-once', 'default'); + expect(lines).toHaveLength(1); + }); +}); + +test.describe('SvelteKit', () => { + test.skip( + // eslint-disable-next-line jest/valid-title + !templateName?.includes('svelte-kit'), + 'Only run this test on SvelteKit' + ); + + test('Links are logged in Actions panel', async ({ page }) => { + const sbPage = new SbPage(page); + + await sbPage.navigateToStory('stories/sveltekit/modules/hrefs', 'default-actions'); + const root = sbPage.previewRoot(); + const link = root.locator('a', { hasText: 'Link to /basic-href' }); + await link.click(); + + await sbPage.viewAddonPanel('Actions'); + const basicLogItem = await page.locator('#storybook-panel-root #panel-tab-content', { + hasText: `/basic-href`, + }); + + await expect(basicLogItem).toBeVisible(); + const complexLogItem = await page.locator('#storybook-panel-root #panel-tab-content', { + hasText: `/deep/nested`, + }); + await expect(complexLogItem).toBeVisible(); + }); }); diff --git a/code/frameworks/angular/package.json b/code/frameworks/angular/package.json index 4f7b5fcd58cf..ee935a9bb43e 100644 --- a/code/frameworks/angular/package.json +++ b/code/frameworks/angular/package.json @@ -33,8 +33,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/node_modules/.bin/tsc", - "prep": "../../../scripts/prepare/tsc.ts" + "check": "node ../../../scripts/node_modules/.bin/tsc", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/tsc.ts" }, "dependencies": { "@storybook/builder-webpack5": "workspace:*", @@ -46,7 +46,6 @@ "@storybook/core-webpack": "workspace:*", "@storybook/docs-tools": "workspace:*", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "workspace:*", "@storybook/node-logger": "workspace:*", "@storybook/preview-api": "workspace:*", "@storybook/telemetry": "workspace:*", @@ -102,8 +101,6 @@ "@angular/platform-browser": ">=14.1.0 < 18.0.0", "@angular/platform-browser-dynamic": ">=14.1.0 < 18.0.0", "@babel/core": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", "rxjs": "^6.0.0 || ^7.4.0", "typescript": "^4.0.0 || ^5.0.0", "zone.js": ">= 0.11.1 < 1.0.0" diff --git a/code/frameworks/angular/src/preset.ts b/code/frameworks/angular/src/preset.ts index ce5796d0c222..f093cb6cc8b8 100644 --- a/code/frameworks/angular/src/preset.ts +++ b/code/frameworks/angular/src/preset.ts @@ -41,5 +41,6 @@ export const typescript: PresetProperty<'typescript', StorybookConfig> = async ( return { ...config, skipBabel: true, + skipCompiler: true, }; }; diff --git a/code/frameworks/ember/package.json b/code/frameworks/ember/package.json index 5888508a61c4..e6fe8d981596 100644 --- a/code/frameworks/ember/package.json +++ b/code/frameworks/ember/package.json @@ -28,8 +28,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/tsc.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/tsc.ts" }, "dependencies": { "@storybook/builder-webpack5": "workspace:*", @@ -49,9 +49,7 @@ "@types/ember__component": "4.0.8", "babel-plugin-ember-modules-api-polyfill": "^2.12.0", "babel-plugin-htmlbars-inline-precompile": "2 || 3", - "ember-source": "~3.28.1", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "ember-source": "~3.28.1" }, "engines": { "node": ">=16.0.0" diff --git a/code/frameworks/html-vite/package.json b/code/frameworks/html-vite/package.json index dabea12cca6e..8d7f738eb60c 100644 --- a/code/frameworks/html-vite/package.json +++ b/code/frameworks/html-vite/package.json @@ -43,8 +43,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/addons": "workspace:*", diff --git a/code/frameworks/html-webpack5/package.json b/code/frameworks/html-webpack5/package.json index 895caa236f59..864a2f1fb55b 100644 --- a/code/frameworks/html-webpack5/package.json +++ b/code/frameworks/html-webpack5/package.json @@ -43,8 +43,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/builder-webpack5": "workspace:*", @@ -58,9 +58,7 @@ "typescript": "~4.9.3" }, "peerDependencies": { - "@babel/core": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@babel/core": "*" }, "engines": { "node": ">=16.0.0" diff --git a/code/frameworks/nextjs/README.md b/code/frameworks/nextjs/README.md index 03cc1cbebc62..2a834fb7ee4a 100644 --- a/code/frameworks/nextjs/README.md +++ b/code/frameworks/nextjs/README.md @@ -122,7 +122,12 @@ export default { framework: { // name: '@storybook/react-webpack5', // Remove this name: '@storybook/nextjs', // Add this - options: {}, + options: { + builder: { + // Set useSWC to true if you want to try out the experimental SWC compiler in Next.js >= 14.0.0 + useSWC: true, + }, + }, }, }; ``` diff --git a/code/frameworks/nextjs/package.json b/code/frameworks/nextjs/package.json index 1da948a664dc..ba32244e8c5d 100644 --- a/code/frameworks/nextjs/package.json +++ b/code/frameworks/nextjs/package.json @@ -29,18 +29,19 @@ }, "./dist/image-context": { "types": "./dist/image-context.d.ts", - "require": "./dist/image-context.mjs", + "require": "./dist/image-context.js", "import": "./dist/image-context.mjs" }, "./preset": { "types": "./dist/preset.d.ts", "require": "./dist/preset.js" }, - "./preview.js": { - "types": "./dist/preview.d.ts", - "require": "./dist/preview.js", - "import": "./dist/preview.mjs" + "./font/webpack/loader/storybook-nextjs-font-loader": { + "types": "./dist/font/webpack/loader/storybook-nextjs-font-loader.d.ts", + "require": "./dist/font/webpack/loader/storybook-nextjs-font-loader.js", + "import": "./dist/font/webpack/loader/storybook-nextjs-font-loader.mjs" }, + "./dist/preview.mjs": "./dist/preview.mjs", "./next-image-loader-stub.js": { "types": "./dist/next-image-loader-stub.d.ts", "require": "./dist/next-image-loader-stub.js", @@ -70,8 +71,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@babel/core": "^7.23.2", @@ -87,10 +88,12 @@ "@babel/preset-react": "^7.22.15", "@babel/preset-typescript": "^7.23.2", "@babel/runtime": "^7.23.2", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", "@storybook/addon-actions": "workspace:*", "@storybook/builder-webpack5": "workspace:*", "@storybook/core-common": "workspace:*", "@storybook/core-events": "workspace:*", + "@storybook/core-webpack": "workspace:*", "@storybook/node-logger": "workspace:*", "@storybook/preset-react-webpack": "workspace:*", "@storybook/preview-api": "workspace:*", @@ -121,7 +124,7 @@ "@types/babel__plugin-transform-runtime": "^7", "@types/babel__preset-env": "^7", "@types/loader-utils": "^2.0.5", - "next": "^14.0.0", + "next": "^14.0.2", "typescript": "^4.9.3", "webpack": "^5.65.0" }, @@ -136,9 +139,6 @@ "@next/font": { "optional": true }, - "@storybook/addon-actions": { - "optional": true - }, "typescript": { "optional": true }, @@ -163,7 +163,8 @@ "./src/images/next-future-image.tsx", "./src/images/next-legacy-image.tsx", "./src/images/next-image.tsx", - "./src/font/webpack/loader/storybook-nextjs-font-loader.ts" + "./src/font/webpack/loader/storybook-nextjs-font-loader.ts", + "./src/swc/next-swc-loader-patch.ts" ], "externals": [ "sb-original/next/image", diff --git a/code/frameworks/nextjs/src/css/webpack.ts b/code/frameworks/nextjs/src/css/webpack.ts index 8f5ed1bfcae3..75718527e7dd 100644 --- a/code/frameworks/nextjs/src/css/webpack.ts +++ b/code/frameworks/nextjs/src/css/webpack.ts @@ -34,6 +34,9 @@ export const configureCss = (baseConfig: WebpackConfig, nextConfig: NextConfig): }, require.resolve('postcss-loader'), ], + // We transform the "target.css" files from next.js into Javascript + // for Next.js to support fonts, so it should be ignored by the css-loader. + exclude: /next\/.*\/target.css$/, }; } }); diff --git a/code/frameworks/nextjs/src/font/webpack/configureNextFont.ts b/code/frameworks/nextjs/src/font/webpack/configureNextFont.ts index 47723fed4034..b1e3851611d6 100644 --- a/code/frameworks/nextjs/src/font/webpack/configureNextFont.ts +++ b/code/frameworks/nextjs/src/font/webpack/configureNextFont.ts @@ -1,14 +1,22 @@ import type { Configuration } from 'webpack'; -export function configureNextFont(baseConfig: Configuration) { - baseConfig.plugins = [...(baseConfig.plugins || [])]; - baseConfig.resolveLoader = { - ...baseConfig.resolveLoader, - alias: { - ...baseConfig.resolveLoader?.alias, - 'storybook-nextjs-font-loader': require.resolve( - './font/webpack/loader/storybook-nextjs-font-loader' - ), - }, - }; +export function configureNextFont(baseConfig: Configuration, isSWC?: boolean) { + const fontLoaderPath = require.resolve( + '@storybook/nextjs/font/webpack/loader/storybook-nextjs-font-loader' + ); + + if (isSWC) { + baseConfig.module?.rules?.push({ + test: /next\/.*\/target.css$/, + loader: fontLoaderPath, + }); + } else { + baseConfig.resolveLoader = { + ...baseConfig.resolveLoader, + alias: { + ...baseConfig.resolveLoader?.alias, + 'storybook-nextjs-font-loader': fontLoaderPath, + }, + }; + } } diff --git a/code/frameworks/nextjs/src/font/webpack/loader/local/get-font-face-declarations.ts b/code/frameworks/nextjs/src/font/webpack/loader/local/get-font-face-declarations.ts index 006c7f126f5b..d7d26ae55a37 100644 --- a/code/frameworks/nextjs/src/font/webpack/loader/local/get-font-face-declarations.ts +++ b/code/frameworks/nextjs/src/font/webpack/loader/local/get-font-face-declarations.ts @@ -1,6 +1,7 @@ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error import loaderUtils from 'next/dist/compiled/loader-utils3'; +import { getProjectRoot } from '@storybook/core-common'; import path from 'path'; import type { LoaderOptions } from '../types'; @@ -11,7 +12,9 @@ export async function getFontFaceDeclarations(options: LoaderOptions, rootContex const localFontSrc = options.props.src as LocalFontSrc; // Parent folder relative to the root context - const parentFolder = path.dirname(options.filename).replace(rootContext, ''); + const parentFolder = path + .dirname(path.join(getProjectRoot(), options.filename)) + .replace(rootContext, ''); const { validateData } = require('../utils/local-font-utils'); const { weight, style, variable } = validateData('', options.props); diff --git a/code/frameworks/nextjs/src/font/webpack/loader/storybook-nextjs-font-loader.ts b/code/frameworks/nextjs/src/font/webpack/loader/storybook-nextjs-font-loader.ts index 85076ecd7201..8b7c08894f72 100644 --- a/code/frameworks/nextjs/src/font/webpack/loader/storybook-nextjs-font-loader.ts +++ b/code/frameworks/nextjs/src/font/webpack/loader/storybook-nextjs-font-loader.ts @@ -14,18 +14,34 @@ type FontFaceDeclaration = { }; export default async function storybookNextjsFontLoader(this: any) { - const options = this.getOptions() as LoaderOptions; + const loaderOptions = this.getOptions() as LoaderOptions; + let options; + + if (Object.keys(loaderOptions).length > 0) { + // handles Babel mode + options = loaderOptions; + } else { + // handles SWC mode + const importQuery = JSON.parse(this.resourceQuery.slice(1)); + + options = { + filename: importQuery.path, + fontFamily: importQuery.import, + props: importQuery.arguments[0], + source: this.context.replace(this.rootContext, ''), + }; + } // get execution context const rootCtx = this.rootContext; let fontFaceDeclaration: FontFaceDeclaration | undefined; - if (options.source === 'next/font/google' || options.source === '@next/font/google') { + if (options.source.endsWith('next/font/google') || options.source.endsWith('@next/font/google')) { fontFaceDeclaration = await getGoogleFontFaceDeclarations(options); } - if (options.source === 'next/font/local' || options.source === '@next/font/local') { + if (options.source.endsWith('next/font/local') || options.source.endsWith('@next/font/local')) { fontFaceDeclaration = await getLocalFontFaceDeclarations(options, rootCtx); } diff --git a/code/frameworks/nextjs/src/nextImport/webpack.ts b/code/frameworks/nextjs/src/nextImport/webpack.ts index fc5d359ef8e2..35f39a7069ed 100644 --- a/code/frameworks/nextjs/src/nextImport/webpack.ts +++ b/code/frameworks/nextjs/src/nextImport/webpack.ts @@ -24,7 +24,8 @@ export function configureNextImport(baseConfig: WebpackConfig) { baseConfig.plugins.push( new IgnorePlugin({ // ignore next/dist/shared/lib/hooks-client-context and next/legacy/image imports - resourceRegExp: /(next\/dist\/shared\/lib\/hooks-client-context|next\/legacy\/image)$/, + resourceRegExp: + /(next\/dist\/shared\/lib\/hooks-client-context|next\/dist\/shared\/lib\/hooks-client-context\.shared-runtime|next\/legacy\/image)$/, }) ); } diff --git a/code/frameworks/nextjs/src/preset.ts b/code/frameworks/nextjs/src/preset.ts index fd726835293c..e4fdd7936fb9 100644 --- a/code/frameworks/nextjs/src/preset.ts +++ b/code/frameworks/nextjs/src/preset.ts @@ -17,6 +17,7 @@ import { configureNextFont } from './font/webpack/configureNextFont'; import nextBabelPreset from './babel/preset'; import { configureNodePolyfills } from './nodePolyfills/webpack'; import { configureAliasing } from './dependency-map'; +import { configureSWCLoader } from './swc/loader'; export const addons: PresetProperty<'addons', StorybookConfig> = [ dirname(require.resolve(join('@storybook/preset-react-webpack', 'package.json'))), @@ -61,7 +62,9 @@ export const core: PresetProperty<'core', StorybookConfig> = async (config, opti name: dirname( require.resolve(join('@storybook/builder-webpack5', 'package.json')) ) as '@storybook/builder-webpack5', - options: typeof framework === 'string' ? {} : framework.options.builder || {}, + options: { + ...(typeof framework === 'string' ? {} : framework.options.builder || {}), + }, }, renderer: dirname(require.resolve(join('@storybook/react', 'package.json'))), }; @@ -69,7 +72,7 @@ export const core: PresetProperty<'core', StorybookConfig> = async (config, opti export const previewAnnotations: StorybookConfig['previewAnnotations'] = (entry = []) => [ ...entry, - require.resolve('@storybook/nextjs/preview.js'), + join(dirname(require.resolve('@storybook/nextjs/package.json')), 'dist/preview.mjs'), ]; // Not even sb init - automigrate - running dev @@ -135,7 +138,7 @@ export const webpackFinal: StorybookConfig['webpackFinal'] = async (baseConfig, const frameworkOptions = await options.presets.apply<{ options: FrameworkOptions }>( 'frameworkOptions' ); - const { options: { nextConfigPath } = {} } = frameworkOptions; + const { options: { nextConfigPath, builder } = {} } = frameworkOptions; const nextConfig = await configureConfig({ baseConfig, nextConfigPath, @@ -143,7 +146,7 @@ export const webpackFinal: StorybookConfig['webpackFinal'] = async (baseConfig, }); configureAliasing(baseConfig); - configureNextFont(baseConfig); + configureNextFont(baseConfig, builder?.useSWC); configureNextImport(baseConfig); configureRuntimeNextjsVersionResolution(baseConfig); configureImports({ baseConfig, configDir: options.configDir }); @@ -152,5 +155,10 @@ export const webpackFinal: StorybookConfig['webpackFinal'] = async (baseConfig, configureStyledJsx(baseConfig); configureNodePolyfills(baseConfig); + // TODO: In Storybook 8.0, we have to check whether the babel-compiler addon is used. Otherwise, swc should be used. + if (builder?.useSWC) { + await configureSWCLoader(baseConfig, options, nextConfig); + } + return baseConfig; }; diff --git a/code/frameworks/nextjs/src/routing/app-router-provider.tsx b/code/frameworks/nextjs/src/routing/app-router-provider.tsx index a7f0bd326f9b..c8b1c96c80a3 100644 --- a/code/frameworks/nextjs/src/routing/app-router-provider.tsx +++ b/code/frameworks/nextjs/src/routing/app-router-provider.tsx @@ -1,48 +1,16 @@ import React from 'react'; -import type { - LayoutRouterContext as TLayoutRouterContext, - AppRouterContext as TAppRouterContext, - GlobalLayoutRouterContext as TGlobalLayoutRouterContext, +import { + LayoutRouterContext, + AppRouterContext, + GlobalLayoutRouterContext, } from 'next/dist/shared/lib/app-router-context.shared-runtime'; -import type { - PathnameContext as TPathnameContext, - SearchParamsContext as TSearchParamsContext, +import { + PathnameContext, + SearchParamsContext, } from 'next/dist/shared/lib/hooks-client-context.shared-runtime'; import type { FlightRouterState } from 'next/dist/server/app-render/types'; import type { RouteParams } from './types'; -/** - * Normally dynamic imports are necessary because otherwise - * older versions of Next.js will throw an error - * because AppRouterProviders only exists in Next.js > v13 - * Using React.lazy though is currently not supported in SB decorators - * therefore using the try/catch workaround - */ -let AppRouterContext: typeof TAppRouterContext; -let LayoutRouterContext: typeof TLayoutRouterContext; -let PathnameContext: typeof TPathnameContext; -let SearchParamsContext: typeof TSearchParamsContext; -let GlobalLayoutRouterContext: typeof TGlobalLayoutRouterContext; - -try { - AppRouterContext = - require('next/dist/shared/lib/app-router-context.shared-runtime').AppRouterContext; - LayoutRouterContext = - require('next/dist/shared/lib/app-router-context.shared-runtime').LayoutRouterContext; - PathnameContext = - require('next/dist/shared/lib/hooks-client-context.shared-runtime').PathnameContext; - SearchParamsContext = - require('next/dist/shared/lib/hooks-client-context.shared-runtime').SearchParamsContext; - GlobalLayoutRouterContext = - require('next/dist/shared/lib/app-router-context.shared-runtime').GlobalLayoutRouterContext; -} catch { - AppRouterContext = React.Fragment as any; - LayoutRouterContext = React.Fragment as any; - PathnameContext = React.Fragment as any; - SearchParamsContext = React.Fragment as any; - GlobalLayoutRouterContext = React.Fragment as any; -} - type AppRouterProviderProps = { action: (name: string) => (...args: any[]) => void; routeParams: RouteParams; @@ -58,7 +26,11 @@ const getParallelRoutes = (segmentsList: Array): FlightRouterState => { return [] as any; }; -const AppRouterProvider: React.FC = ({ children, action, routeParams }) => { +export const AppRouterProvider: React.FC = ({ + children, + action, + routeParams, +}) => { const { pathname, query, segments = [], ...restRouteParams } = routeParams; const tree: FlightRouterState = [pathname, { children: getParallelRoutes([...segments]) }]; @@ -121,5 +93,3 @@ const AppRouterProvider: React.FC = ({ children, action, ); }; - -export default AppRouterProvider; diff --git a/code/frameworks/nextjs/src/routing/decorator.tsx b/code/frameworks/nextjs/src/routing/decorator.tsx index 8312b8e9d80c..059e378c2521 100644 --- a/code/frameworks/nextjs/src/routing/decorator.tsx +++ b/code/frameworks/nextjs/src/routing/decorator.tsx @@ -1,20 +1,10 @@ import * as React from 'react'; -// this will be aliased by webpack at runtime (this is just for typing) -import type { action as originalAction } from '@storybook/addon-actions'; import type { Addon_StoryContext } from '@storybook/types'; -import AppRouterProvider from './app-router-provider'; - -import PageRouterProvider from './page-router-provider'; +import { action } from '@storybook/addon-actions'; +import { PageRouterProvider } from './page-router-provider'; +import type { AppRouterProvider as TAppRouterProvider } from './app-router-provider'; import type { RouteParams, NextAppDirectory } from './types'; -let action: typeof originalAction; - -try { - action = require('@storybook/addon-actions').action; -} catch { - action = () => () => {}; -} - const defaultRouterParams: RouteParams = { pathname: '/', query: {}, @@ -27,7 +17,23 @@ export const RouterDecorator = ( const nextAppDirectory = (parameters.nextjs?.appDirectory as NextAppDirectory | undefined) ?? false; + const [AppRouterProvider, setAppRouterProvider] = React.useState< + typeof TAppRouterProvider | undefined + >(); + + React.useEffect(() => { + if (!nextAppDirectory) { + return; + } + import('./app-router-provider').then((exports) => + setAppRouterProvider(() => exports.AppRouterProvider) + ); + }, [nextAppDirectory]); + if (nextAppDirectory) { + if (!AppRouterProvider) { + return null; + } return ( = ({ +export const PageRouterProvider: React.FC = ({ children, action, routeParams, @@ -66,5 +66,3 @@ const PageRouterProvider: React.FC = ({ {children} ); - -export default PageRouterProvider; diff --git a/code/frameworks/nextjs/src/swc/loader.ts b/code/frameworks/nextjs/src/swc/loader.ts new file mode 100644 index 000000000000..687f60d8aefe --- /dev/null +++ b/code/frameworks/nextjs/src/swc/loader.ts @@ -0,0 +1,68 @@ +import { getProjectRoot } from '@storybook/core-common'; +import { getVirtualModuleMapping } from '@storybook/core-webpack'; +import type { Options } from '@storybook/types'; +import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin'; +import type { NextConfig } from 'next'; +import path from 'path'; +import type { RuleSetRule } from 'webpack'; +import semver from 'semver'; +import { NextjsSWCNotSupportedError } from '@storybook/core-events/server-errors'; +import { getNextjsVersion } from '../utils'; + +export const configureSWCLoader = async ( + baseConfig: any, + options: Options, + nextConfig: NextConfig +) => { + const isDevelopment = options.configType !== 'PRODUCTION'; + const version = getNextjsVersion(); + + if (semver.lt(version, '14.0.0')) { + throw new NextjsSWCNotSupportedError(); + } + + const dir = getProjectRoot(); + + baseConfig.plugins = [ + ...baseConfig.plugins, + new ReactRefreshWebpackPlugin({ + overlay: { + sockIntegration: 'whm', + }, + }), + ]; + + const virtualModules = await getVirtualModuleMapping(options); + + baseConfig.module.rules = [ + // TODO: Remove filtering in Storybook 8.0 + ...baseConfig.module.rules.filter((r: RuleSetRule) => { + return !r.loader?.includes('swc-loader'); + }), + { + test: /\.(m?(j|t)sx?)$/, + include: [getProjectRoot()], + exclude: [/(node_modules)/, ...Object.keys(virtualModules)], + enforce: 'post', + use: { + // we use our own patch because we need to remove tracing from the original code + // which is not possible otherwise + loader: require.resolve('./swc/next-swc-loader-patch.js'), + options: { + isServer: false, + rootDir: dir, + pagesDir: `${dir}/pages`, + appDir: `${dir}/apps`, + hasReactRefresh: isDevelopment, + nextConfig, + supportedBrowsers: require('next/dist/build/utils').getSupportedBrowsers( + dir, + isDevelopment + ), + swcCacheDir: path.join(dir, nextConfig?.distDir ?? '.next', 'cache', 'swc'), + bundleTarget: 'default', + }, + }, + }, + ]; +}; diff --git a/code/frameworks/nextjs/src/swc/next-swc-loader-patch.ts b/code/frameworks/nextjs/src/swc/next-swc-loader-patch.ts new file mode 100644 index 000000000000..114daaddc293 --- /dev/null +++ b/code/frameworks/nextjs/src/swc/next-swc-loader-patch.ts @@ -0,0 +1,193 @@ +// THIS IS A PATCH over the original code from Next 14.0.0 +// we use our own patch because we need to remove tracing from the original code +// which is not possible otherwise + +/* eslint-disable no-restricted-syntax */ +/* +Copyright (c) 2017 The swc Project Developers +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +import type { NextConfig } from 'next'; +import { isWasm, transform } from 'next/dist/build/swc'; +import { getLoaderSWCOptions } from 'next/dist/build/swc/options'; +import path, { isAbsolute } from 'path'; + +export interface SWCLoaderOptions { + rootDir: string; + isServer: boolean; + pagesDir?: string; + appDir?: string; + hasReactRefresh: boolean; + optimizeServerReact?: boolean; + nextConfig: NextConfig; + jsConfig: any; + supportedBrowsers: string[] | undefined; + swcCacheDir: string; + serverComponents?: boolean; + isReactServerLayer?: boolean; +} + +const mockCurrentTraceSpan = { + traceChild: (name: string) => mockCurrentTraceSpan, + traceAsyncFn: async (fn: any) => fn(), +}; + +async function loaderTransform(this: any, parentTrace: any, source?: string, inputSourceMap?: any) { + // Make the loader async + const filename = this.resourcePath; + + const loaderOptions: SWCLoaderOptions = this.getOptions() || {}; + + const { + isServer, + rootDir, + pagesDir, + appDir, + hasReactRefresh, + nextConfig, + jsConfig, + supportedBrowsers, + swcCacheDir, + serverComponents, + isReactServerLayer, + } = loaderOptions; + const isPageFile = filename.startsWith(pagesDir); + const relativeFilePathFromRoot = path.relative(rootDir, filename); + + const swcOptions = getLoaderSWCOptions({ + pagesDir, + appDir, + filename, + isServer, + isPageFile, + development: this.mode === 'development', + hasReactRefresh, + modularizeImports: nextConfig?.modularizeImports, + optimizePackageImports: nextConfig?.experimental?.optimizePackageImports, + swcPlugins: nextConfig?.experimental?.swcPlugins, + compilerOptions: nextConfig?.compiler, + optimizeServerReact: nextConfig?.experimental?.optimizeServerReact, + jsConfig, + supportedBrowsers, + swcCacheDir, + relativeFilePathFromRoot, + serverComponents, + isReactServerLayer, + }); + + const programmaticOptions = { + ...swcOptions, + filename, + inputSourceMap: inputSourceMap ? JSON.stringify(inputSourceMap) : undefined, + + // Set the default sourcemap behavior based on Webpack's mapping flag, + sourceMaps: this.sourceMap, + inlineSourcesContent: this.sourceMap, + + // Ensure that Webpack will get a full absolute path in the sourcemap + // so that it can properly map the module back to its internal cached + // modules. + sourceFileName: filename, + }; + + if (!programmaticOptions.inputSourceMap) { + delete programmaticOptions.inputSourceMap; + } + + // auto detect development mode + if ( + this.mode && + programmaticOptions.jsc && + programmaticOptions.jsc.transform && + programmaticOptions.jsc.transform.react && + !Object.prototype.hasOwnProperty.call(programmaticOptions.jsc.transform.react, 'development') + ) { + programmaticOptions.jsc.transform.react.development = this.mode === 'development'; + } + + const swcSpan = parentTrace.traceChild('next-swc-transform'); + return swcSpan.traceAsyncFn(() => + transform(source as any, programmaticOptions).then((output) => { + if (output.eliminatedPackages && this.eliminatedPackages) { + for (const pkg of JSON.parse(output.eliminatedPackages)) { + this.eliminatedPackages.add(pkg); + } + } + return [output.code, output.map ? JSON.parse(output.map) : undefined]; + }) + ); +} + +const EXCLUDED_PATHS = /[\\/](cache[\\/][^\\/]+\.zip[\\/]node_modules|__virtual__)[\\/]/g; + +export function pitch(this: any) { + const callback = this.async(); + (async () => { + if ( + // TODO: investigate swc file reading in PnP mode? + !process.versions.pnp && + !EXCLUDED_PATHS.test(this.resourcePath) && + this.loaders.length - 1 === this.loaderIndex && + isAbsolute(this.resourcePath) && + !(await isWasm()) + ) { + const loaderSpan = mockCurrentTraceSpan.traceChild('next-swc-loader'); + this.addDependency(this.resourcePath); + return loaderSpan.traceAsyncFn(() => loaderTransform.call(this, loaderSpan)); + } + + return null; + })().then((r) => { + if (r) return callback(null, ...r); + callback(); + return null; + }, callback); +} + +function sanitizeSourceMap(rawSourceMap: any): any { + const { sourcesContent, ...sourceMap } = rawSourceMap ?? {}; + + // JSON parse/stringify trick required for swc to accept the SourceMap + return JSON.parse(JSON.stringify(sourceMap)); +} + +export default function swcLoader(this: any, inputSource: string, inputSourceMap: any) { + const loaderSpan = mockCurrentTraceSpan.traceChild('next-swc-loader'); + const callback = this.async(); + loaderSpan + .traceAsyncFn(() => + loaderTransform.call(this, loaderSpan, inputSource, sanitizeSourceMap(inputSourceMap)) + ) + .then( + ([transformedSource, outputSourceMap]: any) => { + callback(null, transformedSource, outputSourceMap || inputSourceMap); + }, + (err: Error) => { + callback(err); + } + ); +} + +// accept Buffers instead of strings +export const raw = true; diff --git a/code/frameworks/preact-vite/package.json b/code/frameworks/preact-vite/package.json index 7c1bc3d481b1..1e6f1de1bdfa 100644 --- a/code/frameworks/preact-vite/package.json +++ b/code/frameworks/preact-vite/package.json @@ -43,8 +43,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@preact/preset-vite": "^2.0.0", diff --git a/code/frameworks/preact-webpack5/package.json b/code/frameworks/preact-webpack5/package.json index 0fb47b46f46e..262ae0d009da 100644 --- a/code/frameworks/preact-webpack5/package.json +++ b/code/frameworks/preact-webpack5/package.json @@ -43,8 +43,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/builder-webpack5": "workspace:*", @@ -59,9 +59,7 @@ }, "peerDependencies": { "@babel/core": "*", - "preact": "^8.0.0||^10.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "preact": "^8.0.0||^10.0.0" }, "engines": { "node": ">=16.0.0" diff --git a/code/frameworks/react-vite/package.json b/code/frameworks/react-vite/package.json index 593b9528b17b..a030d757ae67 100644 --- a/code/frameworks/react-vite/package.json +++ b/code/frameworks/react-vite/package.json @@ -43,8 +43,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@joshwooding/vite-plugin-react-docgen-typescript": "0.3.0", diff --git a/code/frameworks/react-webpack5/package.json b/code/frameworks/react-webpack5/package.json index be3be827f43a..d21fdeb743c8 100644 --- a/code/frameworks/react-webpack5/package.json +++ b/code/frameworks/react-webpack5/package.json @@ -43,8 +43,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/builder-webpack5": "workspace:*", diff --git a/code/frameworks/server-webpack5/package.json b/code/frameworks/server-webpack5/package.json index 74c81c1fff8d..b500f6ce39c3 100644 --- a/code/frameworks/server-webpack5/package.json +++ b/code/frameworks/server-webpack5/package.json @@ -43,8 +43,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/builder-webpack5": "workspace:*", @@ -56,10 +56,6 @@ "devDependencies": { "typescript": "~4.9.3" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, "engines": { "node": ">=16.0.0" }, diff --git a/code/frameworks/svelte-vite/package.json b/code/frameworks/svelte-vite/package.json index 6a7adfdebe56..5e63fa241181 100644 --- a/code/frameworks/svelte-vite/package.json +++ b/code/frameworks/svelte-vite/package.json @@ -43,8 +43,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/builder-vite": "workspace:*", diff --git a/code/frameworks/svelte-webpack5/package.json b/code/frameworks/svelte-webpack5/package.json index 3b9de5647bbc..f088ac59f8a3 100644 --- a/code/frameworks/svelte-webpack5/package.json +++ b/code/frameworks/svelte-webpack5/package.json @@ -43,8 +43,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/builder-webpack5": "workspace:*", @@ -59,8 +59,6 @@ }, "peerDependencies": { "@babel/core": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", "svelte": "^3.48.0 || ^4.0.0", "svelte-loader": "*" }, diff --git a/code/frameworks/sveltekit/README.md b/code/frameworks/sveltekit/README.md index fd103c8764b1..d9242efcdbf1 100644 --- a/code/frameworks/sveltekit/README.md +++ b/code/frameworks/sveltekit/README.md @@ -13,6 +13,8 @@ Check out our [Frameworks API](https://storybook.js.org/blog/framework-api/) ann - [In a project with Storybook](#in-a-project-with-storybook) - [Automatic migration](#automatic-migration) - [Manual migration](#manual-migration) +- [How to mock](#how-to-mock) + - [Mocking links](#mocking-links) - [Troubleshooting](#troubleshooting) - [Error: `ERR! SyntaxError: Identifier '__esbuild_register_import_meta_url__' has already been declared` when starting Storybook](#error-err-syntaxerror-identifier-__esbuild_register_import_meta_url__-has-already-been-declared-when-starting-storybook) - [Error: `Cannot read properties of undefined (reading 'disable_scroll_handling')` in preview](#error-cannot-read-properties-of-undefined-reading-disable_scroll_handling-in-preview) @@ -26,10 +28,10 @@ However SvelteKit has some [Kit-specific modules](https://kit.svelte.dev/docs/mo | **Module** | **Status** | **Note** | | ---------------------------------------------------------------------------------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | [`$app/environment`](https://kit.svelte.dev/docs/modules#$app-environment) | โœ… Supported | `version` is always empty in Storybook. | -| [`$app/forms`](https://kit.svelte.dev/docs/modules#$app-forms) | โณ Future | Will use mocks. Tracked in [#20999](https://github.com/storybookjs/storybook/issues/20999) | -| [`$app/navigation`](https://kit.svelte.dev/docs/modules#$app-navigation) | โณ Future | Will use mocks. Tracked in [#20999](https://github.com/storybookjs/storybook/issues/20999) | +| [`$app/forms`](https://kit.svelte.dev/docs/modules#$app-forms) | โœ… Supported | See [How to mock](#how-to-mock) | +| [`$app/navigation`](https://kit.svelte.dev/docs/modules#$app-navigation) | โœ… Supported | See [How to mock](#how-to-mock) | | [`$app/paths`](https://kit.svelte.dev/docs/modules#$app-paths) | โœ… Supported | Requires SvelteKit 1.4.0 or newer | -| [`$app/stores`](https://kit.svelte.dev/docs/modules#$app-stores) | โœ… Supported | Mocks planned, so you can set different store values per story. | +| [`$app/stores`](https://kit.svelte.dev/docs/modules#$app-stores) | โœ… Supported | See [How to mock](#how-to-mock) | | [`$env/dynamic/private`](https://kit.svelte.dev/docs/modules#$env-dynamic-private) | โ›” Not supported | They are meant to only be available server-side, and Storybook renders all components on the client. | | [`$env/dynamic/public`](https://kit.svelte.dev/docs/modules#$env-dynamic-public) | ๐Ÿšง Partially supported | Only supported in development mode. Storybook is built as a static app with no server-side API so cannot dynamically serve content. | | [`$env/static/private`](https://kit.svelte.dev/docs/modules#$env-static-private) | โ›” Not supported | They are meant to only be available server-side, and Storybook renders all components on the client. | @@ -100,6 +102,77 @@ yarn remove storybook-builder-vite yarn remove @storybook/builder-vite ``` +## How to mock + +To mock a SvelteKit import you can set it on `parameters.sveltekit_experimental`: + +```ts +export const MyStory = { + parameters: { + sveltekit_experimental: { + stores: { + page: { + data: { + test: 'passed', + }, + }, + navigating: { + route: { + id: '/storybook', + }, + }, + updated: true, + }, + }, + }, +}; +``` + +You can add the name of the module you want to mock to `parameters.sveltekit_experimental` (in the example above we are mocking the `stores` module which correspond to `$app/stores`) and then pass the following kind of objects: + +| Module | Path in parameters | Kind of objects | +| ------------------------------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- | +| `import { page } from "$app/stores"` | `parameters.sveltekit_experimental.stores.page` | A Partial of the page store | +| `import { navigating } from "$app/stores"` | `parameters.sveltekit_experimental.stores.navigating` | A Partial of the navigating store | +| `import { updated } from "$app/stores"` | `parameters.sveltekit_experimental.stores.updated` | A boolean representing the value of updated (you can also access `check()` which will be a noop) | +| `import { goto } from "$app/navigation"` | `parameters.sveltekit_experimental.navigation.goto` | A callback that will be called whenever goto is called | +| `import { invalidate } from "$app/navigation"` | `parameters.sveltekit_experimental.navigation.invalidate` | A callback that will be called whenever invalidate is called | +| `import { invalidateAll } from "$app/navigation"` | `parameters.sveltekit_experimental.navigation.invalidateAll` | A callback that will be called whenever invalidateAll is called | +| `import { afterNavigate } from "$app/navigation"` | `parameters.sveltekit_experimental.navigation.afterNavigate` | An object that will be passed to the afterNavigate function (which will be invoked onMount) called | +| `import { enhance } from "$app/forms"` | `parameters.sveltekit_experimental.forms.enhance` | A callback that will called when a form with `use:enhance` is submitted | + +All the other functions are still exported as `noop` from the mocked modules so that your application will still work. + +### Mocking links + +The default link-handling behavior (ie. clicking an `` tag with an `href` attribute) is to log an action to the Actions panel. + +You can override this by setting an object on `parameter.sveltekit_experimental.hrefs`, where the keys are strings representing an href and the values are objects typed as `{ callback: (href, event) => void, asRegex?: boolean }`. + +If you have an `` tag inside your code with the `href` attribute that matches one or more of the links defined (treated as regex based on the `asRegex` property) the corresponding `callback` will be called. + +Example: + +```ts +export const MyStory = { + parameters: { + sveltekit_experimental: { + hrefs: { + '/basic-href': (to, event) => { + console.log(to, event); + }, + '/root.*': { + callback: (to, event) => { + console.log(to, event); + }, + asRegex: true, + }, + }, + }, + }, +}; +``` + ## Troubleshooting ### Error: `ERR! SyntaxError: Identifier '__esbuild_register_import_meta_url__' has already been declared` when starting Storybook @@ -125,3 +198,4 @@ You'll experience this if anything in your story is importing from `$app/forms` ## Acknowledgements Integrating with SvelteKit would not have been possible if it weren't for the fantastic efforts by the Svelte core team - especially [Ben McCann](https://twitter.com/benjaminmccann) - to make integrations with the wider ecosystem possible. +A big thank you also goes out to [Paolo Ricciuti](https://twitter.com/PaoloRicciuti) for improving the mocking capabilities. diff --git a/code/frameworks/sveltekit/package.json b/code/frameworks/sveltekit/package.json index 3f61b8e4313e..a6550677ab25 100644 --- a/code/frameworks/sveltekit/package.json +++ b/code/frameworks/sveltekit/package.json @@ -29,6 +29,9 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, + "./dist/preview.mjs": { + "import": "./dist/preview.mjs" + }, "./preset": { "types": "./dist/preset.d.ts", "require": "./dist/preset.js" @@ -43,13 +46,14 @@ "README.md", "*.js", "*.d.ts", - "!src/**/*" + "src/mocks/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { + "@storybook/addon-actions": "workspace:*", "@storybook/builder-vite": "workspace:*", "@storybook/svelte": "workspace:*", "@storybook/svelte-vite": "workspace:*" @@ -72,6 +76,7 @@ "bundler": { "entries": [ "./src/index.ts", + "./src/preview.ts", "./src/preset.ts" ], "platform": "node" diff --git a/code/frameworks/sveltekit/src/mocks/app/forms.ts b/code/frameworks/sveltekit/src/mocks/app/forms.ts new file mode 100644 index 000000000000..d1b26867c7d6 --- /dev/null +++ b/code/frameworks/sveltekit/src/mocks/app/forms.ts @@ -0,0 +1,17 @@ +export function enhance(form: HTMLFormElement) { + const listener = (e: Event) => { + e.preventDefault(); + const event = new CustomEvent('storybook:enhance'); + window.dispatchEvent(event); + }; + form.addEventListener('submit', listener); + return { + destroy() { + form.removeEventListener('submit', listener); + }, + }; +} + +export function applyAction() {} + +export function deserialize() {} diff --git a/code/frameworks/sveltekit/src/mocks/app/navigation.ts b/code/frameworks/sveltekit/src/mocks/app/navigation.ts new file mode 100644 index 000000000000..8d23ddbea46a --- /dev/null +++ b/code/frameworks/sveltekit/src/mocks/app/navigation.ts @@ -0,0 +1,43 @@ +import { getContext, onMount, setContext } from 'svelte'; + +export async function goto(...args: any[]) { + const event = new CustomEvent('storybook:goto', { + detail: args, + }); + window.dispatchEvent(event); +} + +export function setAfterNavigateArgument(afterNavigateArgs: any) { + setContext('after-navigate-args', afterNavigateArgs); +} + +export function afterNavigate(cb: any) { + const argument = getContext('after-navigate-args'); + onMount(() => { + if (cb && cb instanceof Function) { + cb(argument); + } + }); +} + +export function onNavigate() {} + +export function beforeNavigate() {} + +export function disableScrollHandling() {} + +export async function invalidate(...args: any[]) { + const event = new CustomEvent('storybook:invalidate', { + detail: args, + }); + window.dispatchEvent(event); +} + +export async function invalidateAll() { + const event = new CustomEvent('storybook:invalidateAll'); + window.dispatchEvent(event); +} + +export function preloadCode() {} + +export function preloadData() {} diff --git a/code/frameworks/sveltekit/src/mocks/app/stores.ts b/code/frameworks/sveltekit/src/mocks/app/stores.ts new file mode 100644 index 000000000000..5f47acb1df6c --- /dev/null +++ b/code/frameworks/sveltekit/src/mocks/app/stores.ts @@ -0,0 +1,32 @@ +import { getContext, setContext } from 'svelte'; + +function createMockedStore(contextName: string) { + return [ + { + subscribe(runner: any) { + const page = getContext(contextName); + runner(page); + return () => {}; + }, + }, + (value: unknown) => { + setContext(contextName, value); + }, + ] as const; +} + +export const [page, setPage] = createMockedStore('page-ctx'); +export const [navigating, setNavigating] = createMockedStore('navigating-ctx'); +const [updated, setUpdated] = createMockedStore('updated-ctx'); + +(updated as any).check = () => {}; + +export { updated, setUpdated }; + +export function getStores() { + return { + page, + navigating, + updated, + }; +} diff --git a/code/frameworks/sveltekit/src/plugins/config-overrides.ts b/code/frameworks/sveltekit/src/plugins/config-overrides.ts index db5294a13242..d132764d6e5c 100644 --- a/code/frameworks/sveltekit/src/plugins/config-overrides.ts +++ b/code/frameworks/sveltekit/src/plugins/config-overrides.ts @@ -1,4 +1,4 @@ -import type { Plugin } from 'vite'; +import { type Plugin } from 'vite'; export function configOverrides() { return { diff --git a/code/frameworks/sveltekit/src/plugins/mock-sveltekit-stores.ts b/code/frameworks/sveltekit/src/plugins/mock-sveltekit-stores.ts new file mode 100644 index 000000000000..873ce8bf3517 --- /dev/null +++ b/code/frameworks/sveltekit/src/plugins/mock-sveltekit-stores.ts @@ -0,0 +1,17 @@ +import { resolve } from 'node:path'; +import { mergeConfig, type Plugin } from 'vite'; + +export function mockSveltekitStores() { + return { + name: 'storybook:sveltekit-mock-stores', + enforce: 'post', + config: (config) => + mergeConfig(config, { + resolve: { + alias: { + $app: resolve(__dirname, '../src/mocks/app/'), + }, + }, + }), + } satisfies Plugin; +} diff --git a/code/frameworks/sveltekit/src/preset.ts b/code/frameworks/sveltekit/src/preset.ts index e92e45079f63..45cfe7d0a6dd 100644 --- a/code/frameworks/sveltekit/src/preset.ts +++ b/code/frameworks/sveltekit/src/preset.ts @@ -4,6 +4,7 @@ import type { PresetProperty } from '@storybook/types'; import { withoutVitePlugins } from '@storybook/builder-vite'; import { dirname, join } from 'path'; import { configOverrides } from './plugins/config-overrides'; +import { mockSveltekitStores } from './plugins/mock-sveltekit-stores'; import { type StorybookConfig } from './types'; const getAbsolutePath = (input: I): I => @@ -13,6 +14,10 @@ export const core: PresetProperty<'core', StorybookConfig> = { builder: getAbsolutePath('@storybook/builder-vite'), renderer: getAbsolutePath('@storybook/svelte'), }; +export const previewAnnotations: StorybookConfig['previewAnnotations'] = (entry = []) => [ + ...entry, + join(dirname(require.resolve('@storybook/sveltekit/package.json')), 'dist/preview.mjs'), +]; export const viteFinal: NonNullable = async (config, options) => { const baseConfig = await svelteViteFinal(config, options); @@ -25,7 +30,9 @@ export const viteFinal: NonNullable = async (confi 'vite-plugin-sveltekit-compile', 'vite-plugin-sveltekit-guard', ]) - ).concat(configOverrides()); + ) + .concat(configOverrides()) + .concat(mockSveltekitStores()); return { ...baseConfig, plugins }; }; diff --git a/code/frameworks/sveltekit/src/preview.ts b/code/frameworks/sveltekit/src/preview.ts new file mode 100644 index 000000000000..a43431b5103d --- /dev/null +++ b/code/frameworks/sveltekit/src/preview.ts @@ -0,0 +1,124 @@ +import type { Decorator } from '@storybook/svelte'; +import { action } from '@storybook/addon-actions'; +import { onMount } from 'svelte'; +import { setAfterNavigateArgument } from './mocks/app/navigation'; +import { setNavigating, setPage, setUpdated } from './mocks/app/stores'; +import type { HrefConfig, NormalizedHrefConfig, SvelteKitParameters } from './types'; + +const normalizeHrefConfig = (hrefConfig: HrefConfig): NormalizedHrefConfig => { + if (typeof hrefConfig === 'function') { + return { callback: hrefConfig, asRegex: false }; + } + return hrefConfig; +}; + +export const decorators: Decorator[] = [ + (Story, ctx) => { + const svelteKitParameters: SvelteKitParameters = ctx.parameters?.sveltekit_experimental ?? {}; + setPage(svelteKitParameters?.stores?.page); + setNavigating(svelteKitParameters?.stores?.navigating); + setUpdated(svelteKitParameters?.stores?.updated); + setAfterNavigateArgument(svelteKitParameters?.navigation?.afterNavigate); + + onMount(() => { + const globalClickListener = (e: MouseEvent) => { + // we add a global click event listener and we check if there's a link in the composedPath + const path = e.composedPath(); + const element = path.findLast((el) => el instanceof HTMLElement && el.tagName === 'A'); + if (element && element instanceof HTMLAnchorElement) { + // if the element is an a-tag we get the href of the element + // and compare it to the hrefs-parameter set by the user + const to = element.getAttribute('href'); + if (!to) { + return; + } + e.preventDefault(); + const defaultActionCallback = () => action('navigate')(to, e); + if (!svelteKitParameters.hrefs) { + defaultActionCallback(); + return; + } + + let callDefaultCallback = true; + // we loop over every href set by the user and check if the href matches + // if it does we call the callback provided by the user and disable the default callback + Object.entries(svelteKitParameters.hrefs).forEach(([href, hrefConfig]) => { + const { callback, asRegex } = normalizeHrefConfig(hrefConfig); + const isMatch = asRegex ? new RegExp(href).test(to) : to === href; + if (isMatch) { + callDefaultCallback = false; + callback?.(to, e); + } + }); + if (callDefaultCallback) { + defaultActionCallback(); + } + } + }; + + /** + * Function that create and add listeners for the event that are emitted by + * the mocked functions. The event name is based on the function name + * + * eg. storybook:goto, storybook:invalidateAll + * @param baseModule the base module where the function lives (navigation|forms) + * @param functions the list of functions in that module that emit events + * @returns a function to remove all the listener added + */ + function createListeners(baseModule: keyof SvelteKitParameters, functions: string[]) { + // the array of every added listener, we can use this in the return function + // to clean them + const toRemove: Array<{ + eventType: string; + listener: (event: { detail: any[] }) => void; + }> = []; + functions.forEach((func) => { + // we loop over every function and check if the user actually passed + // a function in sveltekit_experimental[baseModule][func] eg. sveltekit_experimental.navigation.goto + if ( + (svelteKitParameters as any)[baseModule]?.[func] && + (svelteKitParameters as any)[baseModule][func] instanceof Function + ) { + // we create the listener that will just get the detail array from the custom element + // and call the user provided function spreading this args in...this will basically call + // the function that the user provide with the same arguments the function is invoked to + + // eg. if it calls goto("/my-route") inside the component the function sveltekit_experimental.navigation.goto + // it provided to storybook will be called with "/my-route" + const listener = ({ detail = [] as any[] }) => { + const args = Array.isArray(detail) ? detail : []; + (svelteKitParameters as any)[baseModule][func](...args); + }; + const eventType = `storybook:${func}`; + toRemove.push({ eventType, listener }); + // add the listener to window + (window.addEventListener as any)(eventType, listener); + } + }); + return () => { + // loop over every listener added and remove them + toRemove.forEach(({ eventType, listener }) => { + // @ts-expect-error apparently you can't remove a custom listener to the window with TS + window.removeEventListener(eventType, listener); + }); + }; + } + + const removeNavigationListeners = createListeners('navigation', [ + 'goto', + 'invalidate', + 'invalidateAll', + ]); + const removeFormsListeners = createListeners('forms', ['enhance']); + window.addEventListener('click', globalClickListener); + + return () => { + window.removeEventListener('click', globalClickListener); + removeNavigationListeners(); + removeFormsListeners(); + }; + }); + + return Story(); + }, +]; diff --git a/code/frameworks/sveltekit/src/types.ts b/code/frameworks/sveltekit/src/types.ts index 647ea6fb5653..c3f04a22bc82 100644 --- a/code/frameworks/sveltekit/src/types.ts +++ b/code/frameworks/sveltekit/src/types.ts @@ -1,5 +1,7 @@ +import type { BuilderOptions, StorybookConfigVite } from '@storybook/builder-vite'; import type { StorybookConfig as StorybookConfigBase } from '@storybook/types'; -import type { StorybookConfigVite, BuilderOptions } from '@storybook/builder-vite'; +import type { enhance } from './mocks/app/forms'; +import type { goto, invalidate, invalidateAll } from './mocks/app/navigation'; type FrameworkName = '@storybook/sveltekit'; type BuilderName = '@storybook/builder-vite'; @@ -25,12 +27,34 @@ type StorybookConfigFramework = { }; }; -/** - * The interface for Storybook configuration in `main.ts` files. - */ export type StorybookConfig = Omit< StorybookConfigBase, keyof StorybookConfigVite | keyof StorybookConfigFramework > & StorybookConfigVite & StorybookConfigFramework; + +export type NormalizedHrefConfig = { + callback: (to: string, event: Event) => void; + asRegex?: boolean; +}; + +export type HrefConfig = NormalizedHrefConfig | NormalizedHrefConfig['callback']; + +export type SvelteKitParameters = Partial<{ + hrefs: Record; + stores: { + page: Record; + navigating: Record; + updated: boolean; + }; + navigation: { + goto: typeof goto; + invalidate: typeof invalidate; + invalidateAll: typeof invalidateAll; + afterNavigate: Record; + }; + forms: { + enhance: typeof enhance; + }; +}>; diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/Forms.svelte b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/Forms.svelte new file mode 100644 index 000000000000..371a17656bea --- /dev/null +++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/Forms.svelte @@ -0,0 +1,7 @@ + + +
+ +
\ No newline at end of file diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/Hrefs.svelte b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/Hrefs.svelte new file mode 100644 index 000000000000..4e7d69e0e051 --- /dev/null +++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/Hrefs.svelte @@ -0,0 +1,8 @@ +
diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/Navigation.svelte b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/Navigation.svelte new file mode 100644 index 000000000000..f857ae36a843 --- /dev/null +++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/Navigation.svelte @@ -0,0 +1,26 @@ + + + + + + + diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/Stores.svelte b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/Stores.svelte new file mode 100644 index 000000000000..164b00f7fa8b --- /dev/null +++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/Stores.svelte @@ -0,0 +1,17 @@ + + +

Directly importing

+
{JSON.stringify($page, null, 2)}
+
{JSON.stringify($navigating, null, 2)}
+
{JSON.stringify($updated, null, 2)}
+ +

With getStores

+
{JSON.stringify($pageStore, null, 2)}
+
{JSON.stringify($navigatingStore, null, 2)}
+
{JSON.stringify($updatedStore, null, 2)}
diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/forms.stories.js b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/forms.stories.js new file mode 100644 index 000000000000..72b584baef76 --- /dev/null +++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/forms.stories.js @@ -0,0 +1,26 @@ +import { expect, fn, within } from '@storybook/test'; +import Forms from './Forms.svelte'; + +export default { + title: 'stories/sveltekit/modules/forms', + component: Forms, + tags: ['autodocs'], +}; + +const enhance = fn(); + +export const Enhance = { + async play({ canvasElement }) { + const canvas = within(canvasElement); + const button = canvas.getByText('enhance'); + button.click(); + expect(enhance).toHaveBeenCalled(); + }, + parameters: { + sveltekit_experimental: { + forms: { + enhance, + }, + }, + }, +}; diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/hrefs.stories.js b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/hrefs.stories.js new file mode 100644 index 000000000000..f1cbf4973534 --- /dev/null +++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/hrefs.stories.js @@ -0,0 +1,53 @@ +import { expect, fn, within } from '@storybook/test'; +import Hrefs from './Hrefs.svelte'; + +export default { + title: 'stories/sveltekit/modules/hrefs', + component: Hrefs, + tags: ['autodocs'], +}; + +export const DefaultActions = { + async play({ canvasElement }) { + const canvas = within(canvasElement); + // eslint-disable-next-line no-undef + const initialUrl = window.location.toString(); + + const basicHref = canvas.getByText('/basic-href'); + basicHref.click(); + + const complexHref = canvas.getByText( + '/deep/nested/link?with=true&multiple-params=200#and-an-id' + ); + complexHref.click(); + + // eslint-disable-next-line no-undef + const finalUrl = window.location.toString(); + expect(finalUrl).toBe(initialUrl); + }, +}; + +const basicStringMatch = fn(); +const noMatch = fn(); +const exactStringMatch = fn(); +const regexMatch = fn(); + +export const Callbacks = { + parameters: { + sveltekit_experimental: { + hrefs: { + '/basic-href': basicStringMatch, + '/basic': noMatch, + '/deep/nested/link?with=true&multiple-params=200#and-an-id': exactStringMatch, + 'nested/link\\?with': { callback: regexMatch, asRegex: true }, + }, + }, + }, + play: async (ctx) => { + await DefaultActions.play(ctx); + expect(basicStringMatch).toHaveBeenCalledTimes(1); + expect(noMatch).not.toHaveBeenCalled(); + expect(exactStringMatch).toHaveBeenCalledTimes(1); + expect(regexMatch).toHaveBeenCalledTimes(1); + }, +}; diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/navigation.stories.js b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/navigation.stories.js new file mode 100644 index 000000000000..529997126f7c --- /dev/null +++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/navigation.stories.js @@ -0,0 +1,82 @@ +import { expect, fn, within } from '@storybook/test'; +import Navigation from './Navigation.svelte'; + +export default { + title: 'stories/sveltekit/modules/navigation', + component: Navigation, + tags: ['autodocs'], +}; + +const goto = fn(); + +export const Goto = { + async play({ canvasElement }) { + const canvas = within(canvasElement); + const button = canvas.getByText('goto'); + button.click(); + expect(goto).toHaveBeenCalledWith('/storybook'); + }, + parameters: { + sveltekit_experimental: { + navigation: { + goto, + }, + }, + }, +}; + +const invalidate = fn(); + +export const Invalidate = { + async play({ canvasElement }) { + const canvas = within(canvasElement); + const button = canvas.getByText('invalidate', { exact: true }); + button.click(); + expect(invalidate).toHaveBeenCalledWith('/storybook'); + }, + parameters: { + sveltekit_experimental: { + navigation: { + invalidate, + }, + }, + }, +}; + +const invalidateAll = fn(); + +export const InvalidateAll = { + async play({ canvasElement }) { + const canvas = within(canvasElement); + const button = canvas.getByText('invalidateAll'); + button.click(); + expect(invalidateAll).toHaveBeenCalledWith(); + }, + parameters: { + sveltekit_experimental: { + navigation: { + invalidateAll, + }, + }, + }, +}; + +const afterNavigateFn = fn(); + +export const AfterNavigate = { + async play() { + expect(afterNavigateFn).toHaveBeenCalledWith({ test: 'passed' }); + }, + args: { + afterNavigateFn, + }, + parameters: { + sveltekit_experimental: { + navigation: { + afterNavigate: { + test: 'passed', + }, + }, + }, + }, +}; diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/stores.stories.js b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/stores.stories.js new file mode 100644 index 000000000000..7f7401cf8bee --- /dev/null +++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/stores.stories.js @@ -0,0 +1,116 @@ +import Stores from './Stores.svelte'; + +export default { + title: 'stories/sveltekit/modules/stores', + component: Stores, + tags: ['autodocs'], +}; + +export const AllUndefined = {}; + +export const PageStore = { + parameters: { + sveltekit_experimental: { + stores: { + page: { + data: { + test: 'passed', + }, + }, + }, + }, + }, +}; + +export const NavigatingStore = { + parameters: { + sveltekit_experimental: { + stores: { + navigating: { + route: { + id: '/storybook', + }, + }, + }, + }, + }, +}; + +export const UpdatedStore = { + parameters: { + sveltekit_experimental: { + stores: { + updated: true, + }, + }, + }, +}; + +export const PageAndNavigatingStore = { + parameters: { + sveltekit_experimental: { + stores: { + page: { + data: { + test: 'passed', + }, + }, + navigating: { + route: { + id: '/storybook', + }, + }, + }, + }, + }, +}; + +export const PageAndUpdatedStore = { + parameters: { + sveltekit_experimental: { + stores: { + page: { + data: { + test: 'passed', + }, + }, + updated: true, + }, + }, + }, +}; + +export const NavigatingAndUpdatedStore = { + parameters: { + sveltekit_experimental: { + stores: { + navigating: { + route: { + id: '/storybook', + }, + }, + updated: true, + }, + }, + }, +}; + +export const AllThreeStores = { + parameters: { + sveltekit_experimental: { + stores: { + page: { + data: { + test: 'passed', + }, + }, + navigating: { + route: { + id: '/storybook', + }, + }, + updated: true, + }, + }, + }, +}; diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/Forms.svelte b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/Forms.svelte new file mode 100644 index 000000000000..371a17656bea --- /dev/null +++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/Forms.svelte @@ -0,0 +1,7 @@ + + +
+ +
\ No newline at end of file diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/Hrefs.svelte b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/Hrefs.svelte new file mode 100644 index 000000000000..4e7d69e0e051 --- /dev/null +++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/Hrefs.svelte @@ -0,0 +1,8 @@ + diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/Navigation.svelte b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/Navigation.svelte new file mode 100644 index 000000000000..d97b6fe8a2df --- /dev/null +++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/Navigation.svelte @@ -0,0 +1,25 @@ + + + + + + + diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/Stores.svelte b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/Stores.svelte new file mode 100644 index 000000000000..164b00f7fa8b --- /dev/null +++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/Stores.svelte @@ -0,0 +1,17 @@ + + +

Directly importing

+
{JSON.stringify($page, null, 2)}
+
{JSON.stringify($navigating, null, 2)}
+
{JSON.stringify($updated, null, 2)}
+ +

With getStores

+
{JSON.stringify($pageStore, null, 2)}
+
{JSON.stringify($navigatingStore, null, 2)}
+
{JSON.stringify($updatedStore, null, 2)}
diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/forms.stories.js b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/forms.stories.js new file mode 100644 index 000000000000..72b584baef76 --- /dev/null +++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/forms.stories.js @@ -0,0 +1,26 @@ +import { expect, fn, within } from '@storybook/test'; +import Forms from './Forms.svelte'; + +export default { + title: 'stories/sveltekit/modules/forms', + component: Forms, + tags: ['autodocs'], +}; + +const enhance = fn(); + +export const Enhance = { + async play({ canvasElement }) { + const canvas = within(canvasElement); + const button = canvas.getByText('enhance'); + button.click(); + expect(enhance).toHaveBeenCalled(); + }, + parameters: { + sveltekit_experimental: { + forms: { + enhance, + }, + }, + }, +}; diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/hrefs.stories.js b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/hrefs.stories.js new file mode 100644 index 000000000000..f1cbf4973534 --- /dev/null +++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/hrefs.stories.js @@ -0,0 +1,53 @@ +import { expect, fn, within } from '@storybook/test'; +import Hrefs from './Hrefs.svelte'; + +export default { + title: 'stories/sveltekit/modules/hrefs', + component: Hrefs, + tags: ['autodocs'], +}; + +export const DefaultActions = { + async play({ canvasElement }) { + const canvas = within(canvasElement); + // eslint-disable-next-line no-undef + const initialUrl = window.location.toString(); + + const basicHref = canvas.getByText('/basic-href'); + basicHref.click(); + + const complexHref = canvas.getByText( + '/deep/nested/link?with=true&multiple-params=200#and-an-id' + ); + complexHref.click(); + + // eslint-disable-next-line no-undef + const finalUrl = window.location.toString(); + expect(finalUrl).toBe(initialUrl); + }, +}; + +const basicStringMatch = fn(); +const noMatch = fn(); +const exactStringMatch = fn(); +const regexMatch = fn(); + +export const Callbacks = { + parameters: { + sveltekit_experimental: { + hrefs: { + '/basic-href': basicStringMatch, + '/basic': noMatch, + '/deep/nested/link?with=true&multiple-params=200#and-an-id': exactStringMatch, + 'nested/link\\?with': { callback: regexMatch, asRegex: true }, + }, + }, + }, + play: async (ctx) => { + await DefaultActions.play(ctx); + expect(basicStringMatch).toHaveBeenCalledTimes(1); + expect(noMatch).not.toHaveBeenCalled(); + expect(exactStringMatch).toHaveBeenCalledTimes(1); + expect(regexMatch).toHaveBeenCalledTimes(1); + }, +}; diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/navigation.stories.js b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/navigation.stories.js new file mode 100644 index 000000000000..529997126f7c --- /dev/null +++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/navigation.stories.js @@ -0,0 +1,82 @@ +import { expect, fn, within } from '@storybook/test'; +import Navigation from './Navigation.svelte'; + +export default { + title: 'stories/sveltekit/modules/navigation', + component: Navigation, + tags: ['autodocs'], +}; + +const goto = fn(); + +export const Goto = { + async play({ canvasElement }) { + const canvas = within(canvasElement); + const button = canvas.getByText('goto'); + button.click(); + expect(goto).toHaveBeenCalledWith('/storybook'); + }, + parameters: { + sveltekit_experimental: { + navigation: { + goto, + }, + }, + }, +}; + +const invalidate = fn(); + +export const Invalidate = { + async play({ canvasElement }) { + const canvas = within(canvasElement); + const button = canvas.getByText('invalidate', { exact: true }); + button.click(); + expect(invalidate).toHaveBeenCalledWith('/storybook'); + }, + parameters: { + sveltekit_experimental: { + navigation: { + invalidate, + }, + }, + }, +}; + +const invalidateAll = fn(); + +export const InvalidateAll = { + async play({ canvasElement }) { + const canvas = within(canvasElement); + const button = canvas.getByText('invalidateAll'); + button.click(); + expect(invalidateAll).toHaveBeenCalledWith(); + }, + parameters: { + sveltekit_experimental: { + navigation: { + invalidateAll, + }, + }, + }, +}; + +const afterNavigateFn = fn(); + +export const AfterNavigate = { + async play() { + expect(afterNavigateFn).toHaveBeenCalledWith({ test: 'passed' }); + }, + args: { + afterNavigateFn, + }, + parameters: { + sveltekit_experimental: { + navigation: { + afterNavigate: { + test: 'passed', + }, + }, + }, + }, +}; diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/stores.stories.js b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/stores.stories.js new file mode 100644 index 000000000000..7f7401cf8bee --- /dev/null +++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/stores.stories.js @@ -0,0 +1,116 @@ +import Stores from './Stores.svelte'; + +export default { + title: 'stories/sveltekit/modules/stores', + component: Stores, + tags: ['autodocs'], +}; + +export const AllUndefined = {}; + +export const PageStore = { + parameters: { + sveltekit_experimental: { + stores: { + page: { + data: { + test: 'passed', + }, + }, + }, + }, + }, +}; + +export const NavigatingStore = { + parameters: { + sveltekit_experimental: { + stores: { + navigating: { + route: { + id: '/storybook', + }, + }, + }, + }, + }, +}; + +export const UpdatedStore = { + parameters: { + sveltekit_experimental: { + stores: { + updated: true, + }, + }, + }, +}; + +export const PageAndNavigatingStore = { + parameters: { + sveltekit_experimental: { + stores: { + page: { + data: { + test: 'passed', + }, + }, + navigating: { + route: { + id: '/storybook', + }, + }, + }, + }, + }, +}; + +export const PageAndUpdatedStore = { + parameters: { + sveltekit_experimental: { + stores: { + page: { + data: { + test: 'passed', + }, + }, + updated: true, + }, + }, + }, +}; + +export const NavigatingAndUpdatedStore = { + parameters: { + sveltekit_experimental: { + stores: { + navigating: { + route: { + id: '/storybook', + }, + }, + updated: true, + }, + }, + }, +}; + +export const AllThreeStores = { + parameters: { + sveltekit_experimental: { + stores: { + page: { + data: { + test: 'passed', + }, + }, + navigating: { + route: { + id: '/storybook', + }, + }, + updated: true, + }, + }, + }, +}; diff --git a/code/frameworks/vue-vite/package.json b/code/frameworks/vue-vite/package.json index 3dd6ed9e9a50..5a69fa09080b 100644 --- a/code/frameworks/vue-vite/package.json +++ b/code/frameworks/vue-vite/package.json @@ -43,8 +43,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/builder-vite": "workspace:*", @@ -60,8 +60,6 @@ "vue": "^2.7.10" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", "vite": "^3.0.0 || ^4.0.0 || ^5.0.0", "vue": "^2.7.0" }, diff --git a/code/frameworks/vue-vite/src/preset.ts b/code/frameworks/vue-vite/src/preset.ts index bf5b7d45ea5b..3030587afaaa 100644 --- a/code/frameworks/vue-vite/src/preset.ts +++ b/code/frameworks/vue-vite/src/preset.ts @@ -20,11 +20,6 @@ export const core: PresetProperty<'core', StorybookConfig> = async (config, opti }; }; -export const typescript: PresetProperty<'typescript', StorybookConfig> = async (config) => ({ - ...config, - skipBabel: true, -}); - export const viteFinal: StorybookConfig['viteFinal'] = async (config, { presets }) => { return mergeConfig(config, { plugins: [vueDocgen()], diff --git a/code/frameworks/vue-webpack5/package.json b/code/frameworks/vue-webpack5/package.json index 765760d03b66..1d00f0aeefc8 100644 --- a/code/frameworks/vue-webpack5/package.json +++ b/code/frameworks/vue-webpack5/package.json @@ -43,8 +43,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/builder-webpack5": "workspace:*", @@ -63,8 +63,6 @@ "@babel/core": "*", "babel-loader": "^7.0.0 || ^8.0.0 || ^9.0.0", "css-loader": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", "vue": "^2.6.8", "vue-loader": "^15.7.0", "vue-template-compiler": "^2.6.8" diff --git a/code/frameworks/vue-webpack5/src/preset.ts b/code/frameworks/vue-webpack5/src/preset.ts index 661053fddefb..1e493e9a7f99 100644 --- a/code/frameworks/vue-webpack5/src/preset.ts +++ b/code/frameworks/vue-webpack5/src/preset.ts @@ -25,4 +25,5 @@ export const core: PresetProperty<'core', StorybookConfig> = async (config, opti export const typescript: PresetProperty<'typescript', StorybookConfig> = async (config) => ({ ...config, skipBabel: true, + skipCompiler: true, }); diff --git a/code/frameworks/vue3-vite/package.json b/code/frameworks/vue3-vite/package.json index b26bde1ccfe4..0583b0f69ed3 100644 --- a/code/frameworks/vue3-vite/package.json +++ b/code/frameworks/vue3-vite/package.json @@ -43,8 +43,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/builder-vite": "workspace:*", @@ -60,8 +60,6 @@ "vite": "^4.0.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" }, "engines": { diff --git a/code/frameworks/vue3-webpack5/package.json b/code/frameworks/vue3-webpack5/package.json index 484fb41997c4..07dc51a82eb1 100644 --- a/code/frameworks/vue3-webpack5/package.json +++ b/code/frameworks/vue3-webpack5/package.json @@ -43,8 +43,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/builder-webpack5": "workspace:*", @@ -62,8 +62,6 @@ "@babel/core": "*", "@vue/compiler-sfc": "^3.0.0", "babel-loader": "^7.0.0 || ^8.0.0 || ^9.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", "vue": "^3.0.0" }, "engines": { diff --git a/code/frameworks/vue3-webpack5/src/preset.ts b/code/frameworks/vue3-webpack5/src/preset.ts index 0dd1e931ddd4..1714cc16075e 100644 --- a/code/frameworks/vue3-webpack5/src/preset.ts +++ b/code/frameworks/vue3-webpack5/src/preset.ts @@ -25,4 +25,5 @@ export const core: PresetProperty<'core', StorybookConfig> = async (config, opti export const typescript: PresetProperty<'typescript', StorybookConfig> = async (config) => ({ ...config, skipBabel: true, + skipCompiler: true, }); diff --git a/code/frameworks/web-components-vite/package.json b/code/frameworks/web-components-vite/package.json index 865d3f38942f..eb456a8e0e95 100644 --- a/code/frameworks/web-components-vite/package.json +++ b/code/frameworks/web-components-vite/package.json @@ -43,8 +43,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/builder-vite": "workspace:*", @@ -57,10 +57,6 @@ "@types/node": "^18.0.0", "typescript": "~4.9.3" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, "engines": { "node": "^14.18 || >=16" }, diff --git a/code/frameworks/web-components-webpack5/package.json b/code/frameworks/web-components-webpack5/package.json index edf78bc93eb6..21a9cdc0f030 100644 --- a/code/frameworks/web-components-webpack5/package.json +++ b/code/frameworks/web-components-webpack5/package.json @@ -46,8 +46,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@babel/preset-env": "^7.23.2", @@ -62,9 +62,7 @@ "typescript": "~4.9.3" }, "peerDependencies": { - "lit": "^2.0.0 || ^3.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "lit": "^2.0.0 || ^3.0.0" }, "engines": { "node": ">=16.0.0" diff --git a/code/lib/channels/package.json b/code/lib/channels/package.json index 7271f4434d95..1119993e9bad 100644 --- a/code/lib/channels/package.json +++ b/code/lib/channels/package.json @@ -65,8 +65,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/client-logger": "workspace:*", diff --git a/code/lib/cli/package.json b/code/lib/cli/package.json index e50ef1155f3c..8923fa6400ca 100644 --- a/code/lib/cli/package.json +++ b/code/lib/cli/package.json @@ -50,8 +50,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts", + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts", "test": "jest test/**/*.test.js" }, "dependencies": { @@ -83,7 +83,7 @@ "get-port": "^5.1.1", "giget": "^1.0.0", "globby": "^11.0.2", - "jscodeshift": "^0.14.0", + "jscodeshift": "^0.15.1", "leven": "^3.1.0", "ora": "^5.4.1", "prettier": "^2.8.0", diff --git a/code/lib/cli/src/automigrate/fixes/incompatible-addons.test.ts b/code/lib/cli/src/automigrate/fixes/incompatible-addons.test.ts index 46978eba8396..fd4705acf879 100644 --- a/code/lib/cli/src/automigrate/fixes/incompatible-addons.test.ts +++ b/code/lib/cli/src/automigrate/fixes/incompatible-addons.test.ts @@ -24,7 +24,7 @@ const check = async ({ describe('incompatible-addons fix', () => { afterEach(jest.restoreAllMocks); - it('should show incompatible addons', async () => { + it('should show incompatible addons registered in main.js', async () => { await expect( check({ packageManager: { @@ -38,6 +38,7 @@ describe('incompatible-addons fix', () => { return Promise.resolve(null); } }, + getAllDependencies: async () => ({}), }, main: { addons: ['@storybook/essentials', '@storybook/addon-info'] }, }) @@ -51,6 +52,37 @@ describe('incompatible-addons fix', () => { }); }); + it('should show incompatible addons from package.json', async () => { + await expect( + check({ + packageManager: { + getPackageVersion(packageName, basePath) { + switch (packageName) { + case '@storybook/addon-essentials': + return Promise.resolve('7.0.0'); + case '@storybook/addon-info': + return Promise.resolve('5.3.21'); + default: + return Promise.resolve(null); + } + }, + getAllDependencies: async () => ({ + '@storybook/addon-essentials': '7.0.0', + '@storybook/addon-info': '5.3.21', + }), + }, + main: { addons: [] }, + }) + ).resolves.toEqual({ + incompatibleAddonList: [ + { + name: '@storybook/addon-info', + version: '5.3.21', + }, + ], + }); + }); + it('no-op when there are no incompatible addons', async () => { await expect( check({ @@ -63,6 +95,7 @@ describe('incompatible-addons fix', () => { return Promise.resolve(null); } }, + getAllDependencies: async () => ({}), }, main: { addons: ['@storybook/essentials'] }, }) diff --git a/code/lib/cli/src/automigrate/fixes/incompatible-addons.ts b/code/lib/cli/src/automigrate/fixes/incompatible-addons.ts index 3455408f279d..ce9d01ccd96a 100644 --- a/code/lib/cli/src/automigrate/fixes/incompatible-addons.ts +++ b/code/lib/cli/src/automigrate/fixes/incompatible-addons.ts @@ -1,7 +1,7 @@ import chalk from 'chalk'; import dedent from 'ts-dedent'; import type { Fix } from '../types'; -import { getIncompatibleAddons } from '../helpers/getIncompatibleAddons'; +import { getIncompatibleAddons } from '../../doctor/getIncompatibleAddons'; interface IncompatibleAddonsOptions { incompatibleAddonList: { name: string; version: string }[]; @@ -19,7 +19,7 @@ export const incompatibleAddons: Fix = { prompt({ incompatibleAddonList }) { return dedent` ${chalk.bold( - chalk.red('Attention') + 'Attention' )}: We've detected that you're using the following addons in versions which are known to be incompatible with Storybook 7: ${incompatibleAddonList diff --git a/code/lib/cli/src/automigrate/helpers/getMigrationSummary.test.ts b/code/lib/cli/src/automigrate/helpers/getMigrationSummary.test.ts index f5569ae2f49d..8a28a6c4fe23 100644 --- a/code/lib/cli/src/automigrate/helpers/getMigrationSummary.test.ts +++ b/code/lib/cli/src/automigrate/helpers/getMigrationSummary.test.ts @@ -139,16 +139,25 @@ describe('getMigrationSummary', () => { @storybook/instrumenter: 6.0.0, 7.1.0 - Attention: The following dependencies are duplicated which might cause unexpected behavior: - @storybook/core-common: 6.0.0, 7.1.0 + + + + Attention: The following dependencies are duplicated which might cause unexpected behavior: + @storybook/addon-essentials: 7.0.0, 7.1.0 + + + You can find more information for a given dependency by running yarn why + + + Please try de-duplicating these dependencies by running yarn dedupe" `); }); diff --git a/code/lib/cli/src/automigrate/helpers/getMigrationSummary.ts b/code/lib/cli/src/automigrate/helpers/getMigrationSummary.ts index 6a16e2ae3291..583d1b9170ee 100644 --- a/code/lib/cli/src/automigrate/helpers/getMigrationSummary.ts +++ b/code/lib/cli/src/automigrate/helpers/getMigrationSummary.ts @@ -1,13 +1,12 @@ import chalk from 'chalk'; import boxen from 'boxen'; -import { frameworkPackages, rendererPackages } from '@storybook/core-common'; import dedent from 'ts-dedent'; import type { FixSummary } from '../types'; import { FixStatus } from '../types'; -import { hasMultipleVersions } from './hasMultipleVersions'; import type { InstallationMetadata } from '../../js-package-manager/types'; +import { getDuplicatedDepsWarnings } from '../../doctor/getDuplicatedDepsWarnings'; -const messageDivider = '\n\n'; +export const messageDivider = '\n\n'; const segmentDivider = '\n\nโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\n'; function getGlossaryMessages( @@ -76,11 +75,10 @@ export function getMigrationSummary({ And reach out on Discord if you need help: ${chalk.yellow('https://discord.gg/storybook')} `); - if ( - installationMetadata?.duplicatedDependencies && - Object.keys(installationMetadata.duplicatedDependencies).length > 0 - ) { - messages.push(getWarnings(installationMetadata).join(messageDivider)); + const duplicatedDepsMessage = getDuplicatedDepsWarnings(installationMetadata); + + if (duplicatedDepsMessage) { + messages.push(duplicatedDepsMessage.join(messageDivider)); } const hasNoFixes = Object.values(fixResults).every((r) => r === FixStatus.UNNECESSARY); @@ -102,93 +100,3 @@ export function getMigrationSummary({ borderColor: hasFailures ? 'red' : 'green', }); } - -// These packages are aliased by Storybook, so it doesn't matter if they're duplicated -const allowList = [ - '@storybook/csf', - // see this file for more info: code/lib/preview/src/globals/types.ts - '@storybook/addons', - '@storybook/channel-postmessage', // @deprecated: remove in 8.0 - '@storybook/channel-websocket', // @deprecated: remove in 8.0 - '@storybook/channels', - '@storybook/client-api', - '@storybook/client-logger', - '@storybook/core-client', - '@storybook/core-events', - '@storybook/preview-web', - '@storybook/preview-api', - '@storybook/store', - - // see this file for more info: code/ui/manager/src/globals/types.ts - '@storybook/components', - '@storybook/router', - '@storybook/theming', - '@storybook/api', // @deprecated: remove in 8.0 - '@storybook/manager-api', -]; - -// These packages definitely will cause issues if they're duplicated -const disallowList = [ - Object.keys(rendererPackages), - Object.keys(frameworkPackages), - '@storybook/instrumenter', -]; - -function getWarnings(installationMetadata: InstallationMetadata) { - const messages = []; - - const { critical, trivial } = Object.entries( - installationMetadata?.duplicatedDependencies - ).reduce<{ - critical: string[]; - trivial: string[]; - }>( - (acc, [dep, versions]) => { - if (allowList.includes(dep)) { - return acc; - } - - const hasMultipleMajorVersions = hasMultipleVersions(versions); - - if (disallowList.includes(dep) && hasMultipleMajorVersions) { - acc.critical.push(`${chalk.redBright(dep)}:\n${versions.join(', ')}`); - } else { - acc.trivial.push(`${chalk.hex('#ff9800')(dep)}:\n${versions.join(', ')}`); - } - - return acc; - }, - { critical: [], trivial: [] } - ); - - if (critical.length > 0) { - messages.push( - `${chalk.bold( - 'Critical:' - )} The following dependencies are duplicated and WILL cause unexpected behavior:` - ); - messages.push(critical.join(messageDivider)); - } - - if (trivial.length > 0) { - messages.push( - `${chalk.bold( - 'Attention:' - )} The following dependencies are duplicated which might cause unexpected behavior:` - ); - messages.push(trivial.join(messageDivider)); - } - - messages.push( - `You can find more information for a given dependency by running ${chalk.cyan( - `${installationMetadata.infoCommand} ` - )}` - ); - messages.push( - `Please try de-duplicating these dependencies by running ${chalk.cyan( - `${installationMetadata.dedupeCommand}` - )}` - ); - - return messages; -} diff --git a/code/lib/cli/src/doctor/getDuplicatedDepsWarnings.ts b/code/lib/cli/src/doctor/getDuplicatedDepsWarnings.ts new file mode 100644 index 000000000000..0015798988de --- /dev/null +++ b/code/lib/cli/src/doctor/getDuplicatedDepsWarnings.ts @@ -0,0 +1,119 @@ +import chalk from 'chalk'; +import { frameworkPackages, rendererPackages } from '@storybook/core-common'; +import { hasMultipleVersions } from './hasMultipleVersions'; +import type { InstallationMetadata } from '../js-package-manager/types'; + +export const messageDivider = '\n\n'; + +// These packages are aliased by Storybook, so it doesn't matter if they're duplicated +export const allowList = [ + '@storybook/csf', + // see this file for more info: code/lib/preview/src/globals/types.ts + '@storybook/addons', + '@storybook/channel-postmessage', + '@storybook/channel-websocket', + '@storybook/client-api', + '@storybook/client-logger', + '@storybook/core-client', + '@storybook/preview-web', + '@storybook/preview-api', + '@storybook/store', + + // see this file for more info: code/ui/manager/src/globals/types.ts + '@storybook/components', + '@storybook/router', + '@storybook/theming', + '@storybook/api', + '@storybook/manager-api', +]; + +// These packages definitely will cause issues if they're duplicated +export const disallowList = [ + Object.keys(rendererPackages), + Object.keys(frameworkPackages), + '@storybook/core-events', + '@storybook/instrumenter', + '@storybook/core-common', + '@storybook/core-server', + '@storybook/manager', + '@storybook/preview', +]; + +export function getDuplicatedDepsWarnings( + installationMetadata?: InstallationMetadata +): string[] | undefined { + try { + if ( + !installationMetadata?.duplicatedDependencies || + Object.keys(installationMetadata.duplicatedDependencies).length === 0 + ) { + return undefined; + } + + const messages: string[] = []; + + const { critical, trivial } = Object.entries( + installationMetadata?.duplicatedDependencies + ).reduce<{ + critical: string[]; + trivial: string[]; + }>( + (acc, [dep, packageVersions]) => { + if (allowList.includes(dep)) { + return acc; + } + + const hasMultipleMajorVersions = hasMultipleVersions(packageVersions); + + if (disallowList.includes(dep) && hasMultipleMajorVersions) { + acc.critical.push(`${chalk.redBright(dep)}:\n${packageVersions.join(', ')}`); + } else { + acc.trivial.push(`${chalk.hex('#ff9800')(dep)}:\n${packageVersions.join(', ')}`); + } + + return acc; + }, + { critical: [], trivial: [] } + ); + + if (critical.length === 0 && trivial.length === 0) { + return messages; + } + + if (critical.length > 0) { + messages.push( + `${chalk.bold( + 'Critical:' + )} The following dependencies are duplicated and WILL cause unexpected behavior:` + ); + messages.push(critical.join(messageDivider), '\n'); + } + + if (trivial.length > 0) { + messages.push( + `${chalk.bold( + 'Attention:' + )} The following dependencies are duplicated which might cause unexpected behavior:` + ); + messages.push(trivial.join(messageDivider)); + } + + messages.push( + '\n', + `You can find more information for a given dependency by running ${chalk.cyan( + `${installationMetadata.infoCommand} ` + )}` + ); + + messages.push( + '\n', + `Please try de-duplicating these dependencies by running ${chalk.cyan( + `${installationMetadata.dedupeCommand}` + )}` + ); + + return messages; + } catch (err) { + return undefined; + } +} diff --git a/code/lib/cli/src/automigrate/helpers/getIncompatibleAddons.ts b/code/lib/cli/src/doctor/getIncompatibleAddons.ts similarity index 84% rename from code/lib/cli/src/automigrate/helpers/getIncompatibleAddons.ts rename to code/lib/cli/src/doctor/getIncompatibleAddons.ts index d6fc28ed776b..135865f949c7 100644 --- a/code/lib/cli/src/automigrate/helpers/getIncompatibleAddons.ts +++ b/code/lib/cli/src/doctor/getIncompatibleAddons.ts @@ -1,7 +1,7 @@ import type { StorybookConfig } from '@storybook/types'; import semver from 'semver'; -import { getAddonNames } from './mainConfigFile'; -import { JsPackageManagerFactory } from '../../js-package-manager'; +import { getAddonNames } from '../automigrate/helpers/mainConfigFile'; +import { JsPackageManagerFactory } from '../js-package-manager'; export const getIncompatibleAddons = async ( mainConfig: StorybookConfig, @@ -38,12 +38,13 @@ export const getIncompatibleAddons = async ( const addons = getAddonNames(mainConfig).filter((addon) => addon in incompatibleList); - if (addons.length === 0) { - return []; - } + const dependencies = await packageManager.getAllDependencies(); + const storybookPackages = Object.keys(dependencies).filter((dep) => dep.includes('storybook')); + + const packagesToCheck = [...new Set([...addons, ...storybookPackages])]; const addonVersions = await Promise.all( - addons.map( + packagesToCheck.map( async (addon) => ({ name: addon, @@ -52,6 +53,10 @@ export const getIncompatibleAddons = async ( ) ); + if (addonVersions.length === 0) { + return []; + } + const incompatibleAddons: { name: string; version: string }[] = []; addonVersions.forEach(({ name, version: installedVersion }) => { if (installedVersion === null) return; diff --git a/code/lib/cli/src/doctor/getMismatchingVersionsWarning.ts b/code/lib/cli/src/doctor/getMismatchingVersionsWarning.ts new file mode 100644 index 000000000000..9aa0d424e01d --- /dev/null +++ b/code/lib/cli/src/doctor/getMismatchingVersionsWarning.ts @@ -0,0 +1,95 @@ +import chalk from 'chalk'; +import semver from 'semver'; +import { frameworkPackages } from '@storybook/core-common'; +import type { InstallationMetadata } from '../js-package-manager/types'; +import storybookCorePackages from '../versions'; + +function getPrimaryVersion(name: string, installationMetadata?: InstallationMetadata) { + const packageMetadata = installationMetadata?.dependencies[name]; + if (!packageMetadata) { + return undefined; + } + + return packageMetadata[0]?.version; +} + +export function getMismatchingVersionsWarnings( + installationMetadata?: InstallationMetadata, + allDependencies?: Record +): string | undefined { + const messages: string[] = []; + try { + const frameworkPackageName = Object.keys(installationMetadata?.dependencies).find( + (packageName) => { + return Object.keys(frameworkPackages).includes(packageName); + } + ); + const cliVersion = getPrimaryVersion('@storybook/cli', installationMetadata); + const frameworkVersion = getPrimaryVersion(frameworkPackageName, installationMetadata); + + if (!cliVersion || !frameworkVersion || semver.eq(cliVersion, frameworkVersion)) { + return undefined; + } + + messages.push( + `${chalk.bold( + 'Attention:' + )} There seems to be a mismatch between your Storybook package versions. This can result in a broken Storybook installation.` + ); + + let versionToCompare: string; + let packageToDisplay: string; + if (semver.lt(cliVersion, frameworkVersion)) { + versionToCompare = frameworkVersion; + packageToDisplay = frameworkPackageName; + } else { + versionToCompare = cliVersion; + packageToDisplay = 'storybook'; + } + + messages.push( + `The version of your storybook core packages should align with ${chalk.yellow( + versionToCompare + )} (from the ${chalk.cyan(packageToDisplay)} package) or higher.` + ); + + const filteredDependencies = Object.entries(installationMetadata?.dependencies).filter( + ([name, packages]) => { + if (Object.keys(storybookCorePackages).includes(name)) { + const packageVersion = packages[0].version; + return packageVersion !== versionToCompare; + } + + return false; + } + ); + + if (filteredDependencies.length > 0) { + messages.push( + `Based on your lockfile, these dependencies should be upgraded:`, + filteredDependencies + .map( + ([name, dep]) => + `${chalk.hex('#ff9800')(name)}: ${dep[0].version} ${ + allDependencies[name] ? '(in your package.json)' : '' + }` + ) + .join('\n') + ); + } + + messages.push( + `You can run ${chalk.cyan( + 'npx storybook@latest upgrade' + )} to upgrade all of your Storybook packages to the latest version. + + Alternatively you can try manually changing the versions to match in your package.json. We also recommend regenerating your lockfile, or running the following command to possibly deduplicate your Storybook package versions: ${chalk.cyan( + installationMetadata.dedupeCommand + )}` + ); + + return messages.join('\n\n'); + } catch (err) { + return undefined; + } +} diff --git a/code/lib/cli/src/automigrate/helpers/hasMultipleVersions.ts b/code/lib/cli/src/doctor/hasMultipleVersions.ts similarity index 100% rename from code/lib/cli/src/automigrate/helpers/hasMultipleVersions.ts rename to code/lib/cli/src/doctor/hasMultipleVersions.ts diff --git a/code/lib/cli/src/doctor/index.ts b/code/lib/cli/src/doctor/index.ts new file mode 100644 index 000000000000..cfafb899f3a4 --- /dev/null +++ b/code/lib/cli/src/doctor/index.ts @@ -0,0 +1,131 @@ +import chalk from 'chalk'; +import boxen from 'boxen'; +import { createWriteStream, move, remove } from 'fs-extra'; +import tempy from 'tempy'; +import dedent from 'ts-dedent'; +import { join } from 'path'; + +import { JsPackageManagerFactory } from '../js-package-manager'; +import type { PackageManagerName } from '../js-package-manager'; +import { getStorybookData } from '../automigrate/helpers/mainConfigFile'; +import { cleanLog } from '../automigrate/helpers/cleanLog'; +import { incompatibleAddons } from '../automigrate/fixes/incompatible-addons'; +import { getDuplicatedDepsWarnings } from './getDuplicatedDepsWarnings'; +import { getIncompatibleAddons } from './getIncompatibleAddons'; +import { getMismatchingVersionsWarnings } from './getMismatchingVersionsWarning'; + +const logger = console; +const LOG_FILE_NAME = 'doctor-storybook.log'; +const LOG_FILE_PATH = join(process.cwd(), LOG_FILE_NAME); +let TEMP_LOG_FILE_PATH = ''; + +const originalStdOutWrite = process.stdout.write.bind(process.stdout); +const originalStdErrWrite = process.stderr.write.bind(process.stdout); + +const augmentLogsToFile = () => { + TEMP_LOG_FILE_PATH = tempy.file({ name: LOG_FILE_NAME }); + const logStream = createWriteStream(TEMP_LOG_FILE_PATH); + + process.stdout.write = (d: string) => { + originalStdOutWrite(d); + return logStream.write(cleanLog(d)); + }; + process.stderr.write = (d: string) => { + return logStream.write(cleanLog(d)); + }; +}; + +const cleanup = () => { + process.stdout.write = originalStdOutWrite; + process.stderr.write = originalStdErrWrite; +}; + +type DoctorOptions = { + configDir?: string; + packageManager?: PackageManagerName; +}; + +export const doctor = async ({ + configDir: userSpecifiedConfigDir, + packageManager: pkgMgr, +}: DoctorOptions = {}) => { + augmentLogsToFile(); + const diagnosticMessages: string[] = []; + + logger.info('๐Ÿฉบ checking the health of your Storybook..'); + + const packageManager = JsPackageManagerFactory.getPackageManager({ force: pkgMgr }); + let storybookVersion; + let mainConfig; + + try { + const storybookData = await getStorybookData({ + configDir: userSpecifiedConfigDir, + packageManager, + }); + storybookVersion = storybookData.storybookVersion; + mainConfig = storybookData.mainConfig; + } catch (err) { + if (err.message.includes('No configuration files have been found')) { + logger.info( + dedent`[Storybook doctor] Could not find or evaluate your Storybook main.js config directory at ${chalk.blue( + userSpecifiedConfigDir || '.storybook' + )} so the doctor command cannot proceed. You might be running this command in a monorepo or a non-standard project structure. If that is the case, please rerun this command by specifying the path to your Storybook config directory via the --config-dir option.` + ); + } + logger.info(dedent`[Storybook doctor] โŒ ${err.message}`); + logger.info('Please fix the error and try again.'); + } + + if (!storybookVersion) { + logger.info(dedent` + [Storybook doctor] โŒ Unable to determine Storybook version so the command will not proceed. + ๐Ÿค” Are you running storybook doctor from your project directory? Please specify your Storybook config directory with the --config-dir flag. + `); + process.exit(1); + } + + const incompatibleAddonList = await getIncompatibleAddons(mainConfig); + if (incompatibleAddonList.length > 0) { + diagnosticMessages.push(incompatibleAddons.prompt({ incompatibleAddonList })); + } + + const installationMetadata = await packageManager.findInstallations([ + '@storybook/*', + 'storybook', + ]); + + const allDependencies = await packageManager.getAllDependencies(); + const mismatchingVersionMessage = getMismatchingVersionsWarnings( + installationMetadata, + allDependencies + ); + if (mismatchingVersionMessage) { + diagnosticMessages.push(mismatchingVersionMessage); + } else { + diagnosticMessages.push(getDuplicatedDepsWarnings(installationMetadata)?.join('\n')); + } + logger.info(); + + const finalMessages = diagnosticMessages.filter(Boolean); + + if (finalMessages.length > 0) { + finalMessages.push(`You can find the full logs in ${chalk.cyan(LOG_FILE_PATH)}`); + + logger.info( + boxen(finalMessages.join('\n\n-------\n\n'), { + borderStyle: 'round', + padding: 1, + title: 'Diagnostics', + borderColor: 'red', + }) + ); + await move(TEMP_LOG_FILE_PATH, join(process.cwd(), LOG_FILE_NAME), { overwrite: true }); + } else { + logger.info('๐Ÿฅณ Your Storybook project looks good!'); + await remove(TEMP_LOG_FILE_PATH); + } + logger.info(); + + cleanup(); +}; diff --git a/code/lib/cli/src/generate.ts b/code/lib/cli/src/generate.ts index 76c00eca2d2a..8f37de772933 100644 --- a/code/lib/cli/src/generate.ts +++ b/code/lib/cli/src/generate.ts @@ -23,6 +23,7 @@ import { build } from './build'; import { parseList, getEnvConfig } from './utils'; import versions from './versions'; import { JsPackageManagerFactory } from './js-package-manager'; +import { doctor } from './doctor'; addToGlobalContext('cliVersion', versions.storybook); @@ -170,7 +171,7 @@ command('link ') ); command('automigrate [fixId]') - .description('Check storybook for known problems or migrations and apply fixes') + .description('Check storybook for incompatibilities or migrations and apply fixes') .option('-y --yes', 'Skip prompting the user') .option('-n --dry-run', 'Only check for fixes, do not actually run them') .option('--package-manager ', 'Force package manager') @@ -189,6 +190,17 @@ command('automigrate [fixId]') }); }); +command('doctor') + .description('Check Storybook for known problems and provide suggestions or fixes') + .option('--package-manager ', 'Force package manager') + .option('-c, --config-dir ', 'Directory of Storybook configuration') + .action(async (options) => { + await doctor(options).catch((e) => { + logger.error(e); + process.exit(1); + }); + }); + command('dev') .option('-p, --port ', 'Port to run Storybook', (str) => parseInt(str, 10)) .option('-h, --host ', 'Host to run Storybook') @@ -273,7 +285,11 @@ command('build') configDir: 'SBCONFIG_CONFIG_DIR', }); - await build({ ...options, packageJson: pkg }).catch(() => process.exit(1)); + await build({ + ...options, + packageJson: pkg, + test: !!options.test || process.env.SB_TESTBUILD === 'true', + }).catch(() => process.exit(1)); }); program.on('command:*', ([invalidCmd]) => { diff --git a/code/lib/cli/src/generators/ANGULAR/index.ts b/code/lib/cli/src/generators/ANGULAR/index.ts index 3c4d9a1f0ca9..c8c7f3f288b3 100644 --- a/code/lib/cli/src/generators/ANGULAR/index.ts +++ b/code/lib/cli/src/generators/ANGULAR/index.ts @@ -1,5 +1,4 @@ import { join } from 'path'; -import semver from 'semver'; import { baseGenerator } from '../baseGenerator'; import type { Generator } from '../types'; import { CoreBuilder } from '../../project_types'; @@ -13,10 +12,6 @@ const generator: Generator<{ projectName: string }> = async ( options, commandOptions ) => { - const angularVersion = await packageManager.getPackageVersion('@angular/core'); - const isWebpack5 = angularVersion && semver.gte(angularVersion, '12.0.0'); - const updatedOptions = isWebpack5 ? { ...options, builder: CoreBuilder.Webpack5 } : options; - const angularJSON = new AngularJSON(); if ( @@ -62,7 +57,8 @@ const generator: Generator<{ projectName: string }> = async ( packageManager, npmOptions, { - ...updatedOptions, + ...options, + builder: CoreBuilder.Webpack5, ...(useCompodoc && { frameworkPreviewParts: { prefix: compoDocPreviewPrefix, diff --git a/code/lib/cli/src/generators/HTML/index.ts b/code/lib/cli/src/generators/HTML/index.ts index 781d580d2067..c4e0af830fbd 100755 --- a/code/lib/cli/src/generators/HTML/index.ts +++ b/code/lib/cli/src/generators/HTML/index.ts @@ -1,8 +1,11 @@ +import { CoreBuilder } from '../../project_types'; import { baseGenerator } from '../baseGenerator'; import type { Generator } from '../types'; const generator: Generator = async (packageManager, npmOptions, options) => { - await baseGenerator(packageManager, npmOptions, options, 'html'); + await baseGenerator(packageManager, npmOptions, options, 'html', { + useSWC: ({ builder }) => builder === CoreBuilder.Webpack5, + }); }; export default generator; diff --git a/code/lib/cli/src/generators/PREACT/index.ts b/code/lib/cli/src/generators/PREACT/index.ts index e5bf286467a6..6bbab88e16a3 100644 --- a/code/lib/cli/src/generators/PREACT/index.ts +++ b/code/lib/cli/src/generators/PREACT/index.ts @@ -1,8 +1,11 @@ +import { CoreBuilder } from '../../project_types'; import { baseGenerator } from '../baseGenerator'; import type { Generator } from '../types'; const generator: Generator = async (packageManager, npmOptions, options) => { - await baseGenerator(packageManager, npmOptions, options, 'preact'); + await baseGenerator(packageManager, npmOptions, options, 'preact', { + useSWC: ({ builder }) => builder === CoreBuilder.Webpack5, + }); }; export default generator; diff --git a/code/lib/cli/src/generators/REACT/index.ts b/code/lib/cli/src/generators/REACT/index.ts index 208c62bf25d4..046860356c56 100644 --- a/code/lib/cli/src/generators/REACT/index.ts +++ b/code/lib/cli/src/generators/REACT/index.ts @@ -1,5 +1,5 @@ import { detectLanguage } from '../../detect'; -import { SupportedLanguage } from '../../project_types'; +import { CoreBuilder, SupportedLanguage } from '../../project_types'; import { baseGenerator } from '../baseGenerator'; import type { Generator } from '../types'; @@ -10,6 +10,7 @@ const generator: Generator = async (packageManager, npmOptions, options) => { await baseGenerator(packageManager, npmOptions, options, 'react', { extraPackages, + useSWC: ({ builder }) => builder === CoreBuilder.Webpack5, extraAddons: ['@storybook/addon-onboarding'], }); }; diff --git a/code/lib/cli/src/generators/REACT_SCRIPTS/index.ts b/code/lib/cli/src/generators/REACT_SCRIPTS/index.ts index 866b2210664a..2177a9d5090e 100644 --- a/code/lib/cli/src/generators/REACT_SCRIPTS/index.ts +++ b/code/lib/cli/src/generators/REACT_SCRIPTS/index.ts @@ -59,6 +59,7 @@ const generator: Generator = async (packageManager, npmOptions, options) => { { ...options, builder: CoreBuilder.Webpack5 }, 'react', { + useSWC: () => true, extraAddons, extraPackages, staticDir: fs.existsSync(path.resolve('./public')) ? 'public' : undefined, diff --git a/code/lib/cli/src/generators/SERVER/index.ts b/code/lib/cli/src/generators/SERVER/index.ts index c99b69e4902d..7ced80f7441c 100755 --- a/code/lib/cli/src/generators/SERVER/index.ts +++ b/code/lib/cli/src/generators/SERVER/index.ts @@ -9,6 +9,7 @@ const generator: Generator = async (packageManager, npmOptions, options) => { { ...options, builder: CoreBuilder.Webpack5 }, 'server', { + useSWC: () => true, extensions: ['json', 'yaml', 'yml'], } ); diff --git a/code/lib/cli/src/generators/SFC_VUE/index.ts b/code/lib/cli/src/generators/SFC_VUE/index.ts index e2849a352d90..a3e2f15ea604 100644 --- a/code/lib/cli/src/generators/SFC_VUE/index.ts +++ b/code/lib/cli/src/generators/SFC_VUE/index.ts @@ -1,8 +1,11 @@ +import { CoreBuilder } from '../../project_types'; import { baseGenerator } from '../baseGenerator'; import type { Generator } from '../types'; const generator: Generator = async (packageManager, npmOptions, options) => { - await baseGenerator(packageManager, npmOptions, options, 'vue'); + await baseGenerator(packageManager, npmOptions, options, 'vue', { + useSWC: ({ builder }) => builder === CoreBuilder.Webpack5, + }); }; export default generator; diff --git a/code/lib/cli/src/generators/VUE/index.ts b/code/lib/cli/src/generators/VUE/index.ts index c1869a539695..6b771973a283 100644 --- a/code/lib/cli/src/generators/VUE/index.ts +++ b/code/lib/cli/src/generators/VUE/index.ts @@ -7,6 +7,7 @@ const generator: Generator = async (packageManager, npmOptions, options) => { extraPackages: async ({ builder }) => { return builder === CoreBuilder.Webpack5 ? ['vue-loader@^15.7.0'] : []; }, + useSWC: ({ builder }) => builder === CoreBuilder.Webpack5, }); }; diff --git a/code/lib/cli/src/generators/VUE3/index.ts b/code/lib/cli/src/generators/VUE3/index.ts index 63dbddede7b5..ec20a5b946a1 100644 --- a/code/lib/cli/src/generators/VUE3/index.ts +++ b/code/lib/cli/src/generators/VUE3/index.ts @@ -9,6 +9,7 @@ const generator: Generator = async (packageManager, npmOptions, options) => { ? ['vue-loader@^17.0.0', '@vue/compiler-sfc@^3.2.0'] : []; }, + useSWC: ({ builder }) => builder === CoreBuilder.Webpack5, }); }; diff --git a/code/lib/cli/src/generators/WEB-COMPONENTS/index.ts b/code/lib/cli/src/generators/WEB-COMPONENTS/index.ts index 667395096743..bf5ceee43c15 100755 --- a/code/lib/cli/src/generators/WEB-COMPONENTS/index.ts +++ b/code/lib/cli/src/generators/WEB-COMPONENTS/index.ts @@ -1,9 +1,11 @@ +import { CoreBuilder } from '../../project_types'; import { baseGenerator } from '../baseGenerator'; import type { Generator } from '../types'; const generator: Generator = async (packageManager, npmOptions, options) => { return baseGenerator(packageManager, npmOptions, options, 'web-components', { extraPackages: ['lit'], + useSWC: ({ builder }) => builder === CoreBuilder.Webpack5, }); }; diff --git a/code/lib/cli/src/generators/WEBPACK_REACT/index.ts b/code/lib/cli/src/generators/WEBPACK_REACT/index.ts index 867bd82b80c2..94e8babee466 100644 --- a/code/lib/cli/src/generators/WEBPACK_REACT/index.ts +++ b/code/lib/cli/src/generators/WEBPACK_REACT/index.ts @@ -1,9 +1,11 @@ +import { CoreBuilder } from '../../project_types'; import { baseGenerator } from '../baseGenerator'; import type { Generator } from '../types'; const generator: Generator = async (packageManager, npmOptions, options) => { await baseGenerator(packageManager, npmOptions, options, 'react', { extraAddons: ['@storybook/addon-onboarding'], + useSWC: ({ builder }) => builder === CoreBuilder.Webpack5, }); }; diff --git a/code/lib/cli/src/generators/baseGenerator.ts b/code/lib/cli/src/generators/baseGenerator.ts index a7bdb1764668..6083fae9e873 100644 --- a/code/lib/cli/src/generators/baseGenerator.ts +++ b/code/lib/cli/src/generators/baseGenerator.ts @@ -29,6 +29,7 @@ const defaultOptions: FrameworkOptions = { addMainFile: true, addComponents: true, skipBabel: false, + useSWC: () => false, extraMain: undefined, framework: undefined, extensions: undefined, @@ -194,6 +195,21 @@ export async function baseGenerator( builder = await detectBuilder(packageManager, projectType); } + const { + packages: frameworkPackages, + type, + rendererId, + framework: frameworkInclude, + builder: builderInclude, + } = getFrameworkDetails( + renderer, + builder, + pnp, + language, + framework, + shouldApplyRequireWrapperOnPackageNames + ); + const { extraAddons: extraAddonPackages, extraPackages, @@ -201,30 +217,26 @@ export async function baseGenerator( addScripts, addMainFile, addComponents, - skipBabel, extraMain, extensions, storybookConfigFolder, componentsDestinationPath, + useSWC, } = { ...defaultOptions, ...options, }; - const { - packages: frameworkPackages, - type, - rendererId, - framework: frameworkInclude, - builder: builderInclude, - } = getFrameworkDetails( - renderer, - builder, - pnp, - language, - framework, - shouldApplyRequireWrapperOnPackageNames - ); + let { skipBabel } = { + ...defaultOptions, + ...options, + }; + + const swc = useSWC({ builder }); + + if (swc) { + skipBabel = true; + } const extraAddonsToInstall = typeof extraAddonPackages === 'function' @@ -401,7 +413,18 @@ export async function baseGenerator( : []; await configureMain({ - framework: { name: frameworkInclude, options: options.framework || {} }, + framework: { + name: frameworkInclude, + options: swc + ? { + ...(options.framework ?? {}), + builder: { + ...(options.framework?.builder ?? {}), + useSWC: true, + }, + } + : options.framework || {}, + }, prefixes, storybookConfigFolder, docs: { autodocs: 'tag' }, diff --git a/code/lib/cli/src/generators/types.ts b/code/lib/cli/src/generators/types.ts index 1711505e0bdc..2f97a34df126 100644 --- a/code/lib/cli/src/generators/types.ts +++ b/code/lib/cli/src/generators/types.ts @@ -24,6 +24,7 @@ export interface FrameworkOptions { addMainFile?: boolean; addComponents?: boolean; skipBabel?: boolean; + useSWC?: ({ builder }: { builder: Builder }) => boolean; extraMain?: any; extensions?: string[]; framework?: Record; diff --git a/code/lib/cli/src/initiate.ts b/code/lib/cli/src/initiate.ts index e2966a0c1798..3afd703a1e08 100644 --- a/code/lib/cli/src/initiate.ts +++ b/code/lib/cli/src/initiate.ts @@ -57,7 +57,7 @@ const installStorybook = async ( linkable: !!options.linkable, pnp: pnp || options.usePnp, yes: options.yes, - projectType: options.type, + projectType, }; const runGenerator: () => Promise = async () => { diff --git a/code/lib/cli/src/js-package-manager/NPMProxy.ts b/code/lib/cli/src/js-package-manager/NPMProxy.ts index 1613bac9bb5d..da6a83acec8b 100644 --- a/code/lib/cli/src/js-package-manager/NPMProxy.ts +++ b/code/lib/cli/src/js-package-manager/NPMProxy.ts @@ -184,7 +184,7 @@ export class NPMProxy extends JsPackageManager { await this.executeCommand({ command: 'npm', args: ['install', ...args, ...this.getInstallArgs()], - stdio: ['ignore', logStream, logStream], + stdio: process.env.CI ? 'inherit' : ['ignore', logStream, logStream], }); } catch (err) { const stdout = await readLogFile(); diff --git a/code/lib/cli/src/js-package-manager/PNPMProxy.ts b/code/lib/cli/src/js-package-manager/PNPMProxy.ts index 57fb2ae9b075..33571c0e4dda 100644 --- a/code/lib/cli/src/js-package-manager/PNPMProxy.ts +++ b/code/lib/cli/src/js-package-manager/PNPMProxy.ts @@ -195,7 +195,7 @@ export class PNPMProxy extends JsPackageManager { await this.executeCommand({ command: 'pnpm', args: ['add', ...args, ...this.getInstallArgs()], - stdio: ['ignore', logStream, logStream], + stdio: process.env.CI ? 'inherit' : ['ignore', logStream, logStream], }); } catch (err) { const stdout = await readLogFile(); diff --git a/code/lib/cli/src/js-package-manager/Yarn1Proxy.ts b/code/lib/cli/src/js-package-manager/Yarn1Proxy.ts index 8d24e3676f4b..2dd3251ead61 100644 --- a/code/lib/cli/src/js-package-manager/Yarn1Proxy.ts +++ b/code/lib/cli/src/js-package-manager/Yarn1Proxy.ts @@ -134,7 +134,7 @@ export class Yarn1Proxy extends JsPackageManager { await this.executeCommand({ command: 'yarn', args: ['add', ...this.getInstallArgs(), ...args], - stdio: ['ignore', logStream, logStream], + stdio: process.env.CI ? 'inherit' : ['ignore', logStream, logStream], }); } catch (err) { const stdout = await readLogFile(); diff --git a/code/lib/cli/src/js-package-manager/Yarn2Proxy.ts b/code/lib/cli/src/js-package-manager/Yarn2Proxy.ts index ebf7928d8aea..b1780c474404 100644 --- a/code/lib/cli/src/js-package-manager/Yarn2Proxy.ts +++ b/code/lib/cli/src/js-package-manager/Yarn2Proxy.ts @@ -210,7 +210,7 @@ export class Yarn2Proxy extends JsPackageManager { await this.executeCommand({ command: 'yarn', args: ['add', ...this.getInstallArgs(), ...args], - stdio: ['ignore', logStream, logStream], + stdio: process.env.CI ? 'inherit' : ['ignore', logStream, logStream], }); } catch (err) { const stdout = await readLogFile(); diff --git a/code/lib/cli/src/sandbox-templates.ts b/code/lib/cli/src/sandbox-templates.ts index 7047dc138e16..600a01044e56 100644 --- a/code/lib/cli/src/sandbox-templates.ts +++ b/code/lib/cli/src/sandbox-templates.ts @@ -120,7 +120,8 @@ const baseTemplates = { }, 'nextjs/default-js': { name: 'Next.js Latest (Webpack | JavaScript)', - script: 'yarn create next-app {{beforeDir}} --javascript --eslint', + script: + 'yarn create next-app {{beforeDir}} --javascript --eslint --tailwind --app --import-alias="@/*" --src-dir', expected: { framework: '@storybook/nextjs', renderer: '@storybook/react', @@ -130,7 +131,8 @@ const baseTemplates = { }, 'nextjs/default-ts': { name: 'Next.js Latest (Webpack | TypeScript)', - script: 'yarn create next-app {{beforeDir}} --typescript --eslint', + script: + 'yarn create next-app {{beforeDir}} --typescript --eslint --tailwind --app --import-alias="@/*" --src-dir', expected: { framework: '@storybook/nextjs', renderer: '@storybook/react', @@ -301,7 +303,7 @@ const baseTemplates = { 'angular-cli/prerelease': { name: 'Angular CLI Prerelease (Webpack | TypeScript)', script: - 'npx -p @angular/cli@next ng new angular-v16 --directory {{beforeDir}} --routing=true --minimal=true --style=scss --strict --skip-git --skip-install --package-manager=yarn', + 'npx -p @angular/cli@next ng new angular-v16 --directory {{beforeDir}} --routing=true --minimal=true --style=scss --strict --skip-git --skip-install --package-manager=yarn --ssr', expected: { framework: '@storybook/angular', renderer: '@storybook/angular', @@ -312,7 +314,7 @@ const baseTemplates = { 'angular-cli/default-ts': { name: 'Angular CLI Latest (Webpack | TypeScript)', script: - 'npx -p @angular/cli ng new angular-latest --directory {{beforeDir}} --routing=true --minimal=true --style=scss --strict --skip-git --skip-install --package-manager=yarn', + 'npx -p @angular/cli ng new angular-latest --directory {{beforeDir}} --routing=true --minimal=true --style=scss --strict --skip-git --skip-install --package-manager=yarn --ssr', expected: { framework: '@storybook/angular', renderer: '@storybook/angular', diff --git a/code/lib/client-logger/package.json b/code/lib/client-logger/package.json index 7df3afa29db9..931a11a851d3 100644 --- a/code/lib/client-logger/package.json +++ b/code/lib/client-logger/package.json @@ -40,8 +40,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/global": "^5.0.0" diff --git a/code/lib/codemod/package.json b/code/lib/codemod/package.json index bbc740764575..f1914cd9d17e 100644 --- a/code/lib/codemod/package.json +++ b/code/lib/codemod/package.json @@ -48,27 +48,27 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@babel/core": "^7.23.2", "@babel/preset-env": "^7.23.2", "@babel/types": "^7.23.0", - "@storybook/csf": "^0.1.0", + "@storybook/csf": "^0.1.2", "@storybook/csf-tools": "workspace:*", "@storybook/node-logger": "workspace:*", "@storybook/types": "workspace:*", "@types/cross-spawn": "^6.0.2", "cross-spawn": "^7.0.3", "globby": "^11.0.2", - "jscodeshift": "^0.14.0", + "jscodeshift": "^0.15.1", "lodash": "^4.17.21", "prettier": "^2.8.0", "recast": "^0.23.1" }, "devDependencies": { - "@types/jscodeshift": "^0.11.6", + "@types/jscodeshift": "^0.11.10", "ansi-regex": "^5.0.1", "jest": "^29.7.0", "jest-specific-snapshot": "^8.0.0", diff --git a/code/lib/core-common/package.json b/code/lib/core-common/package.json index a17f5e053947..ccfc2eb051d3 100644 --- a/code/lib/core-common/package.json +++ b/code/lib/core-common/package.json @@ -40,8 +40,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/core-events": "workspace:*", diff --git a/code/lib/core-common/src/presets.test.ts b/code/lib/core-common/src/presets.test.ts index 667f1d01781e..6a283f566b8c 100644 --- a/code/lib/core-common/src/presets.test.ts +++ b/code/lib/core-common/src/presets.test.ts @@ -655,4 +655,68 @@ describe('loadPreset', () => { ] `); }); + + it('should filter out disabledAddons', async () => { + const loaded = await loadPreset( + { + name: '', + type: 'virtual', + framework: '@storybook/react', + presets: ['@storybook/preset-typescript'], + addons: ['@storybook/addon-docs', 'addon-bar'], + }, + 0, + { + build: { + test: { + disabledAddons: ['@storybook/addon-docs'], + }, + }, + } + ); + + // addon-docs should not be at the top level, but addon-bar and others should be. + expect(loaded).toMatchInlineSnapshot(` + Array [ + Object { + "name": "@storybook/preset-typescript", + "options": Object {}, + "preset": Object {}, + }, + Object { + "name": "@storybook/addon-interactions/preset", + "options": Object {}, + "preset": Object {}, + }, + Object { + "name": "@storybook/addon-cool", + "options": Object {}, + "preset": Object {}, + }, + Object { + "name": "addon-bar", + "options": Object {}, + "preset": Object {}, + }, + Object { + "name": Object { + "addons": Array [ + "@storybook/addon-docs", + "addon-bar", + ], + "framework": "@storybook/react", + "name": "", + "presets": Array [ + "@storybook/preset-typescript", + ], + "type": "virtual", + }, + "options": Object {}, + "preset": Object { + "framework": "@storybook/react", + }, + }, + ] + `); + }); }); diff --git a/code/lib/core-common/src/presets.ts b/code/lib/core-common/src/presets.ts index 8cc613a33b3f..407ff1775d3c 100644 --- a/code/lib/core-common/src/presets.ts +++ b/code/lib/core-common/src/presets.ts @@ -261,7 +261,7 @@ export async function loadPreset( // @ts-expect-error (Converted from ts-ignore) const name = i.name ? i.name : i; - return !!storybookOptions.build?.test?.disabledAddons?.find((n) => name.includes(n)); + return !storybookOptions.build?.test?.disabledAddons?.find((n) => name.includes(n)); }; } diff --git a/code/lib/core-common/src/utils/normalize-stories.ts b/code/lib/core-common/src/utils/normalize-stories.ts index 9801fbeae307..1a69fb1ce689 100644 --- a/code/lib/core-common/src/utils/normalize-stories.ts +++ b/code/lib/core-common/src/utils/normalize-stories.ts @@ -9,7 +9,7 @@ import { normalizeStoryPath } from './paths'; import { globToRegexp } from './glob-to-regexp'; const DEFAULT_TITLE_PREFIX = ''; -const DEFAULT_FILES = '**/*.@(mdx|stories.@(js|jsx|mjs|ts|tsx))'; +const DEFAULT_FILES_PATTERN = '**/*.@(mdx|stories.@(js|jsx|mjs|ts|tsx))'; const isDirectory = (configDir: string, entry: string) => { try { @@ -34,7 +34,7 @@ export const getDirectoryFromWorkingDir = ({ export const normalizeStoriesEntry = ( entry: StoriesEntry, - { configDir, workingDir }: NormalizeOptions + { configDir, workingDir, defaultFilesPattern = DEFAULT_FILES_PATTERN }: NormalizeOptions ): NormalizedStoriesSpecifier => { let specifierWithoutMatcher: Omit; @@ -53,7 +53,7 @@ export const normalizeStoriesEntry = ( specifierWithoutMatcher = { titlePrefix: DEFAULT_TITLE_PREFIX, directory: entry, - files: DEFAULT_FILES, + files: defaultFilesPattern, }; } else { specifierWithoutMatcher = { @@ -65,7 +65,7 @@ export const normalizeStoriesEntry = ( } else { specifierWithoutMatcher = { titlePrefix: DEFAULT_TITLE_PREFIX, - files: DEFAULT_FILES, + files: defaultFilesPattern, ...entry, }; } @@ -99,6 +99,7 @@ export const normalizeStoriesEntry = ( interface NormalizeOptions { configDir: string; workingDir: string; + defaultFilesPattern?: string; } export const normalizeStories = (entries: StoriesEntry[], options: NormalizeOptions) => { diff --git a/code/lib/core-events/package.json b/code/lib/core-events/package.json index f438816a6f45..212ced708113 100644 --- a/code/lib/core-events/package.json +++ b/code/lib/core-events/package.json @@ -74,8 +74,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "ts-dedent": "^2.0.0" diff --git a/code/lib/core-events/src/errors/preview-errors.ts b/code/lib/core-events/src/errors/preview-errors.ts index 84161db505b8..50cb0609861a 100644 --- a/code/lib/core-events/src/errors/preview-errors.ts +++ b/code/lib/core-events/src/errors/preview-errors.ts @@ -48,3 +48,29 @@ export class MissingStoryAfterHmrError extends StorybookError { - Also check the browser console and terminal for potential error messages.`; } } + +export class ImplicitActionsDuringRendering extends StorybookError { + readonly category = Category.PREVIEW_API; + + readonly code = 2; + + readonly documentation = + 'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#using-implicit-actions-during-rendering-is-deprecated-for-example-in-the-play-function'; + + constructor(public data: { phase: string; name: string; deprecated: boolean }) { + super(); + } + + template() { + return dedent` + We detected that you use an implicit action arg during ${this.data.phase} of your story. + ${this.data.deprecated ? `\nThis is deprecated and won't work in Storybook 8 anymore.\n` : ``} + Please provide an explicit spy to your args like this: + import { fn } from '@storybook/test'; + ... + args: { + ${this.data.name}: fn() + } + `; + } +} diff --git a/code/lib/core-events/src/errors/server-errors.ts b/code/lib/core-events/src/errors/server-errors.ts index 951a482e9570..f4ecab544773 100644 --- a/code/lib/core-events/src/errors/server-errors.ts +++ b/code/lib/core-events/src/errors/server-errors.ts @@ -368,3 +368,45 @@ export class GoogleFontsLoadingError extends StorybookError { `; } } + +export class NextjsSWCNotSupportedError extends StorybookError { + readonly category = Category.FRAMEWORK_NEXTJS; + + readonly code = 3; + + public readonly documentation = + 'https://github.com/storybookjs/storybook/blob/next/code/frameworks/nextjs/README.md#manual-migration'; + + template() { + return dedent` + You have activated the SWC mode for Next.js, but you are not using Next.js 14.0.0 or higher. + SWC is only supported in Next.js 14.0.0 and higher. Please go to your .storybook/main. file + and remove the { framework: { options: { builder: { useSWC: true } } } } option or upgrade to Next.js v14 or later. + `; + } +} + +export class NoMatchingExportError extends StorybookError { + readonly category = Category.CORE_SERVER; + + readonly code = 4; + + constructor(public data: { error: unknown | Error }) { + super(); + } + + template() { + return dedent` + There was an exports mismatch error when trying to build Storybook. + Please check whether the versions of your Storybook packages match whenever possible, as this might be the cause. + + Problematic example: + { "@storybook/react": "7.5.3", "@storybook/react-vite": "7.4.5", "storybook": "7.3.0" } + + Correct example: + { "@storybook/react": "7.5.3", "@storybook/react-vite": "7.5.3", "storybook": "7.5.3" } + + Please run \`npx storybook@latest doctor\` for guidance on how to fix this issue. + `; + } +} diff --git a/code/lib/core-server/package.json b/code/lib/core-server/package.json index a2dc053443d8..a56640e1de4f 100644 --- a/code/lib/core-server/package.json +++ b/code/lib/core-server/package.json @@ -56,8 +56,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@aw-web-design/x-default-browser": "1.4.126", @@ -66,7 +66,7 @@ "@storybook/channels": "workspace:*", "@storybook/core-common": "workspace:*", "@storybook/core-events": "workspace:*", - "@storybook/csf": "^0.1.0", + "@storybook/csf": "^0.1.2", "@storybook/csf-tools": "workspace:*", "@storybook/docs-mdx": "^0.1.0", "@storybook/global": "^5.0.0", diff --git a/code/lib/core-server/src/build-dev.ts b/code/lib/core-server/src/build-dev.ts index 1e321a7d2933..96bbc120fc7f 100644 --- a/code/lib/core-server/src/build-dev.ts +++ b/code/lib/core-server/src/build-dev.ts @@ -11,6 +11,7 @@ import { loadMainConfig, resolveAddonName, resolvePathInStorybookCache, + serverResolve, validateFrameworkName, } from '@storybook/core-common'; import prompts from 'prompts'; @@ -19,6 +20,9 @@ import { global } from '@storybook/global'; import { telemetry } from '@storybook/telemetry'; import { join, resolve } from 'path'; +import { deprecate } from '@storybook/node-logger'; +import dedent from 'ts-dedent'; +import { readFile } from 'fs-extra'; import { MissingBuilderError } from '@storybook/core-events/server-errors'; import { storybookDevServer } from './dev-server'; import { outputStats } from './utils/output-stats'; @@ -27,6 +31,7 @@ import { updateCheck } from './utils/update-check'; import { getServerPort, getServerChannelUrl } from './utils/server-address'; import { getManagerBuilder, getPreviewBuilder } from './utils/get-builders'; import { warnOnIncompatibleAddons } from './utils/warnOnIncompatibleAddons'; +import { buildOrThrow } from './utils/build-or-throw'; export async function buildDevStandalone( options: CLIOptions & LoadOptions & BuilderOptions @@ -74,7 +79,11 @@ export async function buildDevStandalone( corePresets.push(join(frameworkName, 'preset')); - await warnOnIncompatibleAddons(config); + try { + await warnOnIncompatibleAddons(config); + } catch (e) { + console.warn('Storybook failed to check addon compatibility', e); + } // Load first pass: We need to determine the builder // We need to do this because builders might introduce 'overridePresets' which we need to take into account @@ -106,6 +115,24 @@ export async function buildDevStandalone( getManagerBuilder(), ]); + if (builderName.includes('builder-vite')) { + const deprecationMessage = + dedent(`Using CommonJS in your main configuration file is deprecated with Vite. + - Refer to the migration guide at https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#commonjs-with-vite-is-deprecated`); + + const mainJsPath = serverResolve(resolve(options.configDir || '.storybook', 'main')) as string; + if (/\.c[jt]s$/.test(mainJsPath)) { + deprecate(deprecationMessage); + } + const mainJsContent = await readFile(mainJsPath, 'utf-8'); + // Regex that matches any CommonJS-specific syntax, stolen from Vite: https://github.com/vitejs/vite/blob/91a18c2f7da796ff8217417a4bf189ddda719895/packages/vite/src/node/ssr/ssrExternal.ts#L87 + const CJS_CONTENT_REGEX = + /\bmodule\.exports\b|\bexports[.[]|\brequire\s*\(|\bObject\.(?:defineProperty|defineProperties|assign)\s*\(\s*exports\b/; + if (CJS_CONTENT_REGEX.test(mainJsContent)) { + deprecate(deprecationMessage); + } + } + const resolvedRenderer = renderer && resolveAddonName(options.configDir, renderer, options); // Load second pass: all presets are applied in order @@ -134,8 +161,8 @@ export async function buildDevStandalone( features, }; - const { address, networkAddress, managerResult, previewResult } = await storybookDevServer( - fullOptions + const { address, networkAddress, managerResult, previewResult } = await buildOrThrow(async () => + storybookDevServer(fullOptions) ); const previewTotalTime = previewResult?.totalTime; diff --git a/code/lib/core-server/src/build-static.ts b/code/lib/core-server/src/build-static.ts index e6775f519829..cde136c47413 100644 --- a/code/lib/core-server/src/build-static.ts +++ b/code/lib/core-server/src/build-static.ts @@ -35,6 +35,7 @@ import { extractStorybookMetadata } from './utils/metadata'; import { StoryIndexGenerator } from './utils/StoryIndexGenerator'; import { summarizeIndex } from './utils/summarizeIndex'; import { defaultStaticDirs } from './utils/constants'; +import { buildOrThrow } from './utils/build-or-throw'; export type BuildStaticStandaloneOptions = CLIOptions & LoadOptions & @@ -146,7 +147,9 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption global.FEATURES = features; - await managerBuilder.build({ startTime: process.hrtime(), options: fullOptions }); + await buildOrThrow(async () => + managerBuilder.build({ startTime: process.hrtime(), options: fullOptions }) + ); if (staticDirs) { effects.push( diff --git a/code/lib/core-server/src/presets/common-override-preset.ts b/code/lib/core-server/src/presets/common-override-preset.ts index 5696701a88cd..8cc740aea7d7 100644 --- a/code/lib/core-server/src/presets/common-override-preset.ts +++ b/code/lib/core-server/src/presets/common-override-preset.ts @@ -1,8 +1,5 @@ import type { Options, PresetProperty, StorybookConfig, TestBuildFlags } from '@storybook/types'; -import { normalizeStories, commonGlobOptions } from '@storybook/core-common'; -import { isAbsolute, join } from 'path'; -import slash from 'slash'; -import { glob } from 'glob'; +import { removeMDXEntries } from '../utils/remove-mdx-entries'; export const framework: PresetProperty<'framework', StorybookConfig> = async (config) => { // This will get called with the values from the user's main config, but before @@ -19,30 +16,7 @@ export const framework: PresetProperty<'framework', StorybookConfig> = async (co export const stories: PresetProperty<'stories', StorybookConfig> = async (entries, options) => { if (options?.build?.test?.disableMDXEntries) { - return ( - await Promise.all( - normalizeStories(entries, { - configDir: options.configDir, - workingDir: options.configDir, - }).map(({ directory, files }) => { - const pattern = join(directory, files); - const absolutePattern = isAbsolute(pattern) ? pattern : join(options.configDir, pattern); - - return glob(slash(absolutePattern), { - ...commonGlobOptions(absolutePattern), - follow: true, - }); - }) - ) - ).flatMap((expanded, i) => { - const filteredEntries = expanded.filter((s) => !s.endsWith('.mdx')); - // only return the filtered entries when there is something to filter - // as webpack is faster with unexpanded globs - if (filteredEntries.length < expanded.length) { - return filteredEntries; - } - return entries[i]; - }); + return removeMDXEntries(entries, options); } return entries; }; diff --git a/code/lib/core-server/src/presets/common-preset.ts b/code/lib/core-server/src/presets/common-preset.ts index 6854a8f48682..9512038cc3cb 100644 --- a/code/lib/core-server/src/presets/common-preset.ts +++ b/code/lib/core-server/src/presets/common-preset.ts @@ -194,6 +194,7 @@ export const features = async ( storyStoreV7: true, argTypeTargetsV7: true, legacyDecoratorFileOrder: false, + disallowImplicitActionsInRenderV8: false, }); export const csfIndexer: Indexer = { diff --git a/code/lib/core-server/src/utils/__tests__/remove-mdx-stories.test.ts b/code/lib/core-server/src/utils/__tests__/remove-mdx-stories.test.ts new file mode 100644 index 000000000000..93280240e74e --- /dev/null +++ b/code/lib/core-server/src/utils/__tests__/remove-mdx-stories.test.ts @@ -0,0 +1,248 @@ +import { glob as globlOriginal } from 'glob'; +import { type StoriesEntry } from '@storybook/types'; +import { normalizeStoriesEntry } from '@storybook/core-common'; +import { join } from 'path'; +import slash from 'slash'; +import { removeMDXEntries } from '../remove-mdx-entries'; + +const glob = globlOriginal as jest.MockedFunction; + +const configDir = '/configDir/'; +const workingDir = '/'; + +jest.mock('glob', () => ({ glob: jest.fn() })); + +const createList = (list: { entry: StoriesEntry; result: string[] }[]) => { + return list.reduce>( + (acc, { entry, result }) => { + const { directory, files } = normalizeStoriesEntry(entry, { + configDir, + workingDir, + }); + acc[slash(join('/', directory, files))] = { result, entry }; + return acc; + }, + {} + ); +}; + +const createGlobMock = (input: ReturnType) => { + return async (k: string | string[]) => { + if (Array.isArray(k)) { + throw new Error('do not pass an array to glob during tests'); + } + if (input[slash(k)]) { + return input[slash(k)]?.result; + } + + throw new Error('can not find key in input'); + }; +}; + +test('empty', async () => { + const list = createList([]); + glob.mockImplementation(createGlobMock(list)); + + await expect(() => removeMDXEntries(Object.keys(list), { configDir })).rejects + .toThrowErrorMatchingInlineSnapshot(` + "Storybook could not index your stories. + Your main configuration somehow does not contain a 'stories' field, or it resolved to an empty array. + + Please check your main configuration file and make sure it exports a 'stories' field that is not an empty array. + + More info: https://storybook.js.org/docs/react/faq#can-i-have-a-storybook-with-no-local-stories + " + `); +}); + +test('minimal', async () => { + const list = createList([{ entry: '*.js', result: [] }]); + glob.mockImplementation(createGlobMock(list)); + + const result = await removeMDXEntries( + Object.values(list).map((e) => e.entry), + { configDir } + ); + + expect(result).toMatchInlineSnapshot(` + Array [ + Object { + "directory": ".", + "files": "*.js", + "titlePrefix": "", + }, + ] + `); +}); + +test('multiple', async () => { + const list = createList([ + { entry: '*.ts', result: [] }, + { entry: '*.js', result: [] }, + ]); + glob.mockImplementation(createGlobMock(list)); + + const result = await removeMDXEntries( + Object.values(list).map((e) => e.entry), + { configDir } + ); + + expect(result).toMatchInlineSnapshot(` + Array [ + Object { + "directory": ".", + "files": "*.ts", + "titlePrefix": "", + }, + Object { + "directory": ".", + "files": "*.js", + "titlePrefix": "", + }, + ] + `); +}); + +test('mdx but not matching any files', async () => { + const list = createList([ + { entry: '*.mdx', result: [] }, + { entry: '*.js', result: [] }, + ]); + glob.mockImplementation(createGlobMock(list)); + + const result = await removeMDXEntries( + Object.values(list).map((e) => e.entry), + { configDir } + ); + + expect(result).toMatchInlineSnapshot(` + Array [ + Object { + "directory": ".", + "files": "*.mdx", + "titlePrefix": "", + }, + Object { + "directory": ".", + "files": "*.js", + "titlePrefix": "", + }, + ] + `); +}); + +test('removes entries that only yield mdx files', async () => { + const list = createList([ + { entry: '*.mdx', result: ['/configDir/my-file.mdx'] }, + { entry: '*.js', result: [] }, + ]); + glob.mockImplementation(createGlobMock(list)); + + const result = await removeMDXEntries( + Object.values(list).map((e) => e.entry), + { configDir } + ); + + expect(result).toMatchInlineSnapshot(` + Array [ + Object { + "directory": ".", + "files": "*.js", + "titlePrefix": "", + }, + ] + `); +}); + +test('expands entries that only yield mixed files', async () => { + const list = createList([ + { entry: '*.@(mdx|ts)', result: ['/configDir/my-file.mdx', '/configDir/my-file.ts'] }, + { entry: '*.js', result: [] }, + ]); + glob.mockImplementation(createGlobMock(list)); + + const result = await removeMDXEntries( + Object.values(list).map((e) => e.entry), + { configDir } + ); + + expect(result).toMatchInlineSnapshot(` + Array [ + Object { + "directory": ".", + "files": "**/my-file.ts", + "titlePrefix": "", + }, + Object { + "directory": ".", + "files": "*.js", + "titlePrefix": "", + }, + ] + `); +}); + +test('passes titlePrefix', async () => { + const list = createList([ + { + entry: { files: '*.@(mdx|ts)', directory: '.', titlePrefix: 'foo' }, + result: ['/configDir/my-file.mdx', '/configDir/my-file.ts'], + }, + ]); + glob.mockImplementation(createGlobMock(list)); + + const result = await removeMDXEntries( + Object.values(list).map((e) => e.entry), + { configDir } + ); + + expect(result).toMatchInlineSnapshot(` + Array [ + Object { + "directory": ".", + "files": "**/my-file.ts", + "titlePrefix": "foo", + }, + ] + `); +}); + +test('expands to multiple entries', async () => { + const list = createList([ + { + entry: { files: '*.@(mdx|ts)', directory: '.', titlePrefix: 'foo' }, + result: [ + '/configDir/my-file.mdx', + '/configDir/my-file1.ts', + '/configDir/my-file2.ts', + '/configDir/my-file3.ts', + ], + }, + ]); + glob.mockImplementation(createGlobMock(list)); + + const result = await removeMDXEntries( + Object.values(list).map((e) => e.entry), + { configDir } + ); + + expect(result).toMatchInlineSnapshot(` + Array [ + Object { + "directory": ".", + "files": "**/my-file1.ts", + "titlePrefix": "foo", + }, + Object { + "directory": ".", + "files": "**/my-file2.ts", + "titlePrefix": "foo", + }, + Object { + "directory": ".", + "files": "**/my-file3.ts", + "titlePrefix": "foo", + }, + ] + `); +}); diff --git a/code/lib/core-server/src/utils/build-or-throw.ts b/code/lib/core-server/src/utils/build-or-throw.ts new file mode 100644 index 000000000000..5c1746a0be2a --- /dev/null +++ b/code/lib/core-server/src/utils/build-or-throw.ts @@ -0,0 +1,20 @@ +import { NoMatchingExportError } from '@storybook/core-events/server-errors'; + +export async function buildOrThrow(callback: () => Promise): Promise { + try { + return await callback(); + } catch (err: any) { + const builderErrors = err.errors as { text: string }[]; + if (builderErrors) { + const inconsistentVersionsError = builderErrors.find((er) => + er.text?.includes('No matching export') + ); + + if (inconsistentVersionsError) { + throw new NoMatchingExportError(err); + } + } + + throw err; + } +} diff --git a/code/lib/core-server/src/utils/remove-mdx-entries.ts b/code/lib/core-server/src/utils/remove-mdx-entries.ts new file mode 100644 index 000000000000..ed93c1bc8d64 --- /dev/null +++ b/code/lib/core-server/src/utils/remove-mdx-entries.ts @@ -0,0 +1,57 @@ +import type { Options, StoriesEntry } from '@storybook/types'; +import { normalizeStories, commonGlobOptions } from '@storybook/core-common'; +import { isAbsolute, join, relative } from 'path'; +import slash from 'slash'; +import { glob } from 'glob'; + +export async function removeMDXEntries( + entries: StoriesEntry[], + options: Pick +): Promise { + const list = normalizeStories(entries, { + configDir: options.configDir, + workingDir: options.configDir, + defaultFilesPattern: '**/*.@(stories.@(js|jsx|mjs|ts|tsx))', + }); + const result = ( + await Promise.all( + list.map(async ({ directory, files, titlePrefix }) => { + const pattern = join(directory, files); + const absolutePattern = isAbsolute(pattern) ? pattern : join(options.configDir, pattern); + const absoluteDirectory = isAbsolute(directory) + ? directory + : join(options.configDir, directory); + + return { + files: ( + await glob(slash(absolutePattern), { + ...commonGlobOptions(absolutePattern), + follow: true, + }) + ).map((f) => relative(absoluteDirectory, f)), + directory, + titlePrefix, + }; + }) + ) + ).flatMap(({ directory, files, titlePrefix }, i) => { + const filteredEntries = files.filter((s) => !s.endsWith('.mdx')); + // only return the filtered entries when there is something to filter + // as webpack is faster with unexpanded globs + let items = []; + if (filteredEntries.length < files.length) { + items = filteredEntries.map((k) => ({ + directory, + titlePrefix, + files: `**/${k}`, + })); + } else { + items = [ + { directory: list[i].directory, titlePrefix: list[i].titlePrefix, files: list[i].files }, + ]; + } + + return items; + }); + return result; +} diff --git a/code/lib/core-server/src/utils/warnOnIncompatibleAddons.ts b/code/lib/core-server/src/utils/warnOnIncompatibleAddons.ts index a850d49809ad..fd4aaac39286 100644 --- a/code/lib/core-server/src/utils/warnOnIncompatibleAddons.ts +++ b/code/lib/core-server/src/utils/warnOnIncompatibleAddons.ts @@ -1,9 +1,9 @@ import type { StorybookConfig } from '@storybook/types'; -import { logger } from '@storybook/client-logger'; +import { logger } from '@storybook/node-logger'; import chalk from 'chalk'; import dedent from 'ts-dedent'; -import { getIncompatibleAddons } from '../../../cli/src/automigrate/helpers/getIncompatibleAddons'; +import { getIncompatibleAddons } from '../../../cli/src/doctor/getIncompatibleAddons'; export const warnOnIncompatibleAddons = async (config: StorybookConfig) => { const incompatibleAddons = await getIncompatibleAddons(config); diff --git a/code/lib/core-webpack/package.json b/code/lib/core-webpack/package.json index 3fa0d1298d1e..fd69a427abbd 100644 --- a/code/lib/core-webpack/package.json +++ b/code/lib/core-webpack/package.json @@ -40,8 +40,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/core-common": "workspace:*", @@ -51,6 +51,7 @@ "ts-dedent": "^2.0.0" }, "devDependencies": { + "slash": "^5.1.0", "typescript": "~4.9.3", "webpack": "5" }, diff --git a/code/lib/core-webpack/src/index.ts b/code/lib/core-webpack/src/index.ts index 370187367538..562860cbe1a6 100644 --- a/code/lib/core-webpack/src/index.ts +++ b/code/lib/core-webpack/src/index.ts @@ -4,3 +4,4 @@ export * from './check-webpack-version'; export * from './merge-webpack-config'; export * from './to-importFn'; export * from './to-require-context'; +export * from './virtual-module-mapping'; diff --git a/code/lib/core-webpack/src/types.ts b/code/lib/core-webpack/src/types.ts index 012b95f41cf6..1028c08a0a4d 100644 --- a/code/lib/core-webpack/src/types.ts +++ b/code/lib/core-webpack/src/types.ts @@ -22,6 +22,12 @@ export interface WebpackConfiguration { devtool?: false | string; } +export type BuilderOptions = { + fsCache?: boolean; + useSWC?: boolean; + lazyCompilation?: boolean; +}; + export type StorybookConfig = StorybookConfigBase & { /** * Modify or return a custom Webpack config after the Storybook's default configuration diff --git a/code/lib/core-webpack/src/virtual-module-mapping.ts b/code/lib/core-webpack/src/virtual-module-mapping.ts new file mode 100644 index 000000000000..09941ba859a3 --- /dev/null +++ b/code/lib/core-webpack/src/virtual-module-mapping.ts @@ -0,0 +1,111 @@ +import type { Options, PreviewAnnotation } from '@storybook/types'; +import { isAbsolute, join, resolve } from 'path'; +import { + getBuilderOptions, + getRendererName, + handlebars, + interpolate, + loadPreviewOrConfigFile, + normalizeStories, + readTemplate, +} from '@storybook/core-common'; +import slash from 'slash'; +import type { BuilderOptions } from './types'; +import { toImportFn } from './to-importFn'; +import { toRequireContextString } from './to-require-context'; + +export const getVirtualModuleMapping = async (options: Options) => { + const virtualModuleMapping: Record = {}; + const builderOptions = await getBuilderOptions(options); + const workingDir = process.cwd(); + const isProd = options.configType === 'PRODUCTION'; + const nonNormalizedStories = await options.presets.apply('stories', []); + + const stories = normalizeStories(nonNormalizedStories, { + configDir: options.configDir, + workingDir, + }); + + const previewAnnotations = [ + ...(await options.presets.apply('previewAnnotations', [], options)).map( + (entry) => { + // If entry is an object, use the absolute import specifier. + // This is to maintain back-compat with community addons that bundle other addons + // and package managers that "hide" sub dependencies (e.g. pnpm / yarn pnp) + // The vite builder uses the bare import specifier. + if (typeof entry === 'object') { + return entry.absolute; + } + + // TODO: Remove as soon as we drop support for disabled StoryStoreV7 + if (isAbsolute(entry)) { + return entry; + } + + return slash(entry); + } + ), + loadPreviewOrConfigFile(options), + ].filter(Boolean); + + if (options.features?.storyStoreV7) { + const storiesFilename = 'storybook-stories.js'; + const storiesPath = resolve(join(workingDir, storiesFilename)); + + const needPipelinedImport = !!builderOptions.lazyCompilation && !isProd; + virtualModuleMapping[storiesPath] = toImportFn(stories, { needPipelinedImport }); + const configEntryPath = resolve(join(workingDir, 'storybook-config-entry.js')); + virtualModuleMapping[configEntryPath] = handlebars( + await readTemplate( + require.resolve( + '@storybook/builder-webpack5/templates/virtualModuleModernEntry.js.handlebars' + ) + ), + { + storiesFilename, + previewAnnotations, + } + // We need to double escape `\` for webpack. We may have some in windows paths + ).replace(/\\/g, '\\\\'); + } else { + const rendererName = await getRendererName(options); + + const rendererInitEntry = resolve(join(workingDir, 'storybook-init-renderer-entry.js')); + virtualModuleMapping[rendererInitEntry] = `import '${slash(rendererName)}';`; + + const entryTemplate = await readTemplate( + join(__dirname, '..', 'templates', 'virtualModuleEntry.template.js') + ); + + previewAnnotations.forEach((previewAnnotationFilename: string | undefined) => { + if (!previewAnnotationFilename) return; + + // Ensure that relative paths end up mapped to a filename in the cwd, so a later import + // of the `previewAnnotationFilename` in the template works. + const entryFilename = previewAnnotationFilename.startsWith('.') + ? `${previewAnnotationFilename.replace(/(\w)(\/|\\)/g, '$1-')}-generated-config-entry.js` + : `${previewAnnotationFilename}-generated-config-entry.js`; + // NOTE: although this file is also from the `dist/cjs` directory, it is actually a ESM + // file, see https://github.com/storybookjs/storybook/pull/16727#issuecomment-986485173 + virtualModuleMapping[entryFilename] = interpolate(entryTemplate, { + previewAnnotationFilename, + }); + }); + if (stories.length > 0) { + const storyTemplate = await readTemplate( + join(__dirname, '..', 'templates', 'virtualModuleStory.template.js') + ); + // NOTE: this file has a `.cjs` extension as it is a CJS file (from `dist/cjs`) and runs + // in the user's webpack mode, which may be strict about the use of require/import. + // See https://github.com/storybookjs/storybook/issues/14877 + const storiesFilename = resolve(join(workingDir, `generated-stories-entry.cjs`)); + virtualModuleMapping[storiesFilename] = interpolate(storyTemplate, { + rendererName, + }) + // Make sure we also replace quotes for this one + .replace("'{{stories}}'", stories.map(toRequireContextString).join(',')); + } + } + + return virtualModuleMapping; +}; diff --git a/code/builders/builder-webpack5/templates/virtualModuleEntry.template.js b/code/lib/core-webpack/templates/virtualModuleEntry.template.js similarity index 100% rename from code/builders/builder-webpack5/templates/virtualModuleEntry.template.js rename to code/lib/core-webpack/templates/virtualModuleEntry.template.js diff --git a/code/builders/builder-webpack5/templates/virtualModuleStory.template.js b/code/lib/core-webpack/templates/virtualModuleStory.template.js similarity index 100% rename from code/builders/builder-webpack5/templates/virtualModuleStory.template.js rename to code/lib/core-webpack/templates/virtualModuleStory.template.js diff --git a/code/lib/csf-plugin/package.json b/code/lib/csf-plugin/package.json index d88f1fe7744d..6bc4a5874da1 100644 --- a/code/lib/csf-plugin/package.json +++ b/code/lib/csf-plugin/package.json @@ -40,8 +40,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/csf-tools": "workspace:*", diff --git a/code/lib/csf-tools/package.json b/code/lib/csf-tools/package.json index 85a3460f2a72..9620c543e085 100644 --- a/code/lib/csf-tools/package.json +++ b/code/lib/csf-tools/package.json @@ -38,15 +38,15 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@babel/generator": "^7.23.0", "@babel/parser": "^7.23.0", "@babel/traverse": "^7.23.2", "@babel/types": "^7.23.0", - "@storybook/csf": "^0.1.0", + "@storybook/csf": "^0.1.2", "@storybook/types": "workspace:*", "fs-extra": "^11.1.0", "recast": "^0.23.1", diff --git a/code/lib/csf-tools/src/ConfigFile.ts b/code/lib/csf-tools/src/ConfigFile.ts index e31750a2fed6..eb2921b4e06d 100644 --- a/code/lib/csf-tools/src/ConfigFile.ts +++ b/code/lib/csf-tools/src/ConfigFile.ts @@ -1,5 +1,6 @@ /* eslint-disable no-underscore-dangle */ import fs from 'fs-extra'; +import dedent from 'ts-dedent'; import * as t from '@babel/types'; @@ -12,6 +13,30 @@ import { babelParse } from './babelParse'; const logger = console; +const getCsfParsingErrorMessage = ({ + expectedType, + foundType, + node, +}: { + expectedType: string; + foundType: string | undefined; + node: any | undefined; +}) => { + let nodeInfo = ''; + if (node) { + try { + nodeInfo = JSON.stringify(node); + } catch (e) { + // + } + } + + return dedent` + CSF Parsing error: Expected '${expectedType}' but found '${foundType}' instead in '${node?.type}'. + ${nodeInfo} + `; +}; + const propKey = (p: t.ObjectProperty) => { if (t.isIdentifier(p.key)) return p.key.name; if (t.isStringLiteral(p.key)) return p.key.value; @@ -163,7 +188,13 @@ export class ConfigFile { } }); } else { - logger.warn(`Unexpected ${JSON.stringify(node)}`); + logger.warn( + getCsfParsingErrorMessage({ + expectedType: 'ObjectExpression', + foundType: decl?.type, + node: decl || node.declaration, + }) + ); } }, }, @@ -183,7 +214,13 @@ export class ConfigFile { } }); } else { - logger.warn(`Unexpected ${JSON.stringify(node)}`); + logger.warn( + getCsfParsingErrorMessage({ + expectedType: 'VariableDeclaration', + foundType: node.declaration?.type, + node: node.declaration, + }) + ); } }, }, @@ -223,7 +260,13 @@ export class ConfigFile { } }); } else { - logger.warn(`Unexpected ${JSON.stringify(node)}`); + logger.warn( + getCsfParsingErrorMessage({ + expectedType: 'ObjectExpression', + foundType: exportObject?.type, + node: exportObject, + }) + ); } } } diff --git a/code/lib/docs-tools/package.json b/code/lib/docs-tools/package.json index f1de756926d2..f8e3bf5769e0 100644 --- a/code/lib/docs-tools/package.json +++ b/code/lib/docs-tools/package.json @@ -40,8 +40,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/core-common": "workspace:*", diff --git a/code/lib/instrumenter/package.json b/code/lib/instrumenter/package.json index 72637a13dd52..65c03ab46a98 100644 --- a/code/lib/instrumenter/package.json +++ b/code/lib/instrumenter/package.json @@ -40,8 +40,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/channels": "workspace:*", diff --git a/code/lib/manager-api/package.json b/code/lib/manager-api/package.json index 641e7fe4805d..02a7cac6d7ad 100644 --- a/code/lib/manager-api/package.json +++ b/code/lib/manager-api/package.json @@ -39,14 +39,14 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/channels": "workspace:*", "@storybook/client-logger": "workspace:*", "@storybook/core-events": "workspace:*", - "@storybook/csf": "^0.1.0", + "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", "@storybook/router": "workspace:*", "@storybook/theming": "workspace:*", @@ -65,18 +65,20 @@ "@types/qs": "^6", "flush-promises": "^1.0.2", "qs": "^6.10.0", + "react": "^16.8.0", + "react-dom": "^16.8.0", "typescript": "~4.9.3" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, "publishConfig": { "access": "public" }, "bundler": { "entries": [ "./src/index.tsx" + ], + "externals": [ + "react", + "react-dom" ] }, "gitHead": "e6a7fd8a655c69780bc20b9749c2699e44beae17" diff --git a/code/lib/manager-api/src/index.tsx b/code/lib/manager-api/src/index.tsx index 951dc494a93a..d44b49002ca7 100644 --- a/code/lib/manager-api/src/index.tsx +++ b/code/lib/manager-api/src/index.tsx @@ -67,6 +67,8 @@ import * as whatsnew from './modules/whatsnew'; import * as globals from './modules/globals'; import type { ModuleFn } from './lib/types'; +import { types } from './lib/addons'; + export * from './lib/shortcut'; const { ActiveTabs } = layout; @@ -506,5 +508,14 @@ export function useArgTypes(): ArgTypes { export { addons } from './lib/addons'; +/** + * We need to rename this so it's not compiled to a straight re-export + * Our globalization plugin can't handle an import and export of the same name in different lines + * @deprecated + */ +const typesX = types; + +export { typesX as types }; + /* deprecated */ -export { mockChannel, types, type Addon, type AddonStore } from './lib/addons'; +export { mockChannel, type Addon, type AddonStore } from './lib/addons'; diff --git a/code/lib/manager-api/src/modules/stories.ts b/code/lib/manager-api/src/modules/stories.ts index 795f9460b108..72c4198a3863 100644 --- a/code/lib/manager-api/src/modules/stories.ts +++ b/code/lib/manager-api/src/modules/stories.ts @@ -314,6 +314,9 @@ export const init: ModuleFn = ({ }, isPrepared: (storyId, refId) => { const data = api.getData(storyId, refId); + if (!data) { + return false; + } return data.type === 'story' ? data.prepared : true; }, resolveStory: (storyId, refId) => { @@ -612,7 +615,9 @@ export const init: ModuleFn = ({ }); await store.setState({ status: newStatus }, { persistence: 'session' }); - await api.setIndex(index); + if (index) { + await api.setIndex(index); + } }, experimental_setFilter: async (id, filterFunction) => { const { internal_index: index } = store.getState(); diff --git a/code/lib/manager-api/src/tests/stories.test.ts b/code/lib/manager-api/src/tests/stories.test.ts index 76213e10be25..7733f569e5c5 100644 --- a/code/lib/manager-api/src/tests/stories.test.ts +++ b/code/lib/manager-api/src/tests/stories.test.ts @@ -1251,6 +1251,32 @@ describe('stories API', () => { } `); }); + it('skips updating index, if index is unset', async () => { + const moduleArgs = createMockModuleArgs({}); + const { api } = initStories(moduleArgs as unknown as ModuleArgs); + const { store } = moduleArgs; + + await expect( + api.experimental_updateStatus('a-addon-id', { + 'a-story-id': { + status: 'pending', + title: 'an addon title', + description: 'an addon description', + }, + }) + ).resolves.not.toThrow(); + expect(store.getState().status).toMatchInlineSnapshot(` + Object { + "a-story-id": Object { + "a-addon-id": Object { + "description": "an addon description", + "status": "pending", + "title": "an addon title", + }, + }, + } + `); + }); it('updates multiple stories', async () => { const moduleArgs = createMockModuleArgs({}); const { api } = initStories(moduleArgs as unknown as ModuleArgs); diff --git a/code/lib/node-logger/package.json b/code/lib/node-logger/package.json index b7908b6117d1..c69cab36b3b8 100644 --- a/code/lib/node-logger/package.json +++ b/code/lib/node-logger/package.json @@ -38,8 +38,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "devDependencies": { "@types/npmlog": "^4.1.2", diff --git a/code/lib/postinstall/package.json b/code/lib/postinstall/package.json index 347b79ff543c..9e2a19920e07 100644 --- a/code/lib/postinstall/package.json +++ b/code/lib/postinstall/package.json @@ -41,13 +41,13 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "devDependencies": { "jest": "^29.7.0", "jest-specific-snapshot": "^8.0.0", - "jscodeshift": "^0.14.0", + "jscodeshift": "^0.15.1", "typescript": "~4.9.3" }, "publishConfig": { diff --git a/code/lib/preview-api/package.json b/code/lib/preview-api/package.json index d5865075785a..a43fecb7a446 100644 --- a/code/lib/preview-api/package.json +++ b/code/lib/preview-api/package.json @@ -64,14 +64,14 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/channels": "workspace:*", "@storybook/client-logger": "workspace:*", "@storybook/core-events": "workspace:*", - "@storybook/csf": "^0.1.0", + "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", "@storybook/types": "workspace:*", "@types/qs": "^6.9.5", @@ -87,7 +87,6 @@ "@jest/globals": "^29.5.0", "@storybook/core-common": "workspace:*", "ansi-to-html": "^0.6.11", - "react": "^16.14.0", "slash": "^5.0.0" }, "publishConfig": { diff --git a/code/lib/preview-api/src/modules/client-api/ClientApi.ts b/code/lib/preview-api/src/modules/client-api/ClientApi.ts index 83a2ba0cd6b0..d68bf30b47cd 100644 --- a/code/lib/preview-api/src/modules/client-api/ClientApi.ts +++ b/code/lib/preview-api/src/modules/client-api/ClientApi.ts @@ -168,9 +168,11 @@ export class ClientApi { } }; - addStepRunner = (stepRunner: StepRunner) => { + addStepRunner = (stepRunner: StepRunner) => { this.facade.projectAnnotations.runStep = composeStepRunners( - [this.facade.projectAnnotations.runStep, stepRunner].filter(Boolean) as StepRunner[] + [this.facade.projectAnnotations.runStep, stepRunner].filter( + Boolean + ) as StepRunner[] ); }; @@ -297,7 +299,7 @@ export class ClientApi { this._addedExports[fileName] = { default: meta }; let counter = 0; - api.add = (storyName: string, storyFn: StoryFn, parameters: Parameters = {}) => { + api.add = (storyName: string, storyFn: StoryFn, parameters: Parameters = {}) => { hasAdded = true; if (typeof storyName !== 'string') { diff --git a/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts b/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts index 6796913596bd..40ea0ccbaa03 100644 --- a/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts +++ b/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts @@ -34,7 +34,7 @@ export class DocsContext implements DocsContextProps constructor( public channel: Channel, protected store: StoryStore, - public renderStoryToElement: DocsContextProps['renderStoryToElement'], + public renderStoryToElement: DocsContextProps['renderStoryToElement'], /** The CSF files known (via the index) to be refererenced by this docs file */ csfFiles: CSFFile[] ) { diff --git a/code/lib/preview-api/src/modules/preview-web/render/CsfDocsRender.ts b/code/lib/preview-api/src/modules/preview-web/render/CsfDocsRender.ts index a4fbb47b9e01..019d43d97893 100644 --- a/code/lib/preview-api/src/modules/preview-web/render/CsfDocsRender.ts +++ b/code/lib/preview-api/src/modules/preview-web/render/CsfDocsRender.ts @@ -95,7 +95,7 @@ export class CsfDocsRender implements Render['renderStoryToElement']) { if (!this.csfFiles) throw new Error('Cannot render docs before preparing'); const docsContext = new DocsContext( this.channel, @@ -112,7 +112,7 @@ export class CsfDocsRender implements Render['renderStoryToElement'] ) { if (!this.story || !this.csfFiles) throw new Error('Cannot render docs before preparing'); diff --git a/code/lib/preview-api/src/modules/preview-web/render/MdxDocsRender.ts b/code/lib/preview-api/src/modules/preview-web/render/MdxDocsRender.ts index ca81e01d438a..987dc7680192 100644 --- a/code/lib/preview-api/src/modules/preview-web/render/MdxDocsRender.ts +++ b/code/lib/preview-api/src/modules/preview-web/render/MdxDocsRender.ts @@ -79,7 +79,7 @@ export class MdxDocsRender implements Render['renderStoryToElement']) { if (!this.csfFiles) throw new Error('Cannot render docs before preparing'); // NOTE we do *not* attach any CSF file yet. We wait for `referenceMeta(..., true)` @@ -94,7 +94,7 @@ export class MdxDocsRender implements Render['renderStoryToElement'] ) { if (!this.exports || !this.csfFiles || !this.store.projectAnnotations) throw new Error('Cannot render docs before preparing'); diff --git a/code/lib/preview-api/src/modules/store/csf/normalizeArrays.ts b/code/lib/preview-api/src/modules/store/csf/normalizeArrays.ts new file mode 100644 index 000000000000..96344e6fac1d --- /dev/null +++ b/code/lib/preview-api/src/modules/store/csf/normalizeArrays.ts @@ -0,0 +1,4 @@ +export const normalizeArrays = (array: T[] | T | undefined): T[] => { + if (Array.isArray(array)) return array; + return array ? [array] : []; +}; diff --git a/code/lib/preview-api/src/modules/store/csf/normalizeProjectAnnotations.ts b/code/lib/preview-api/src/modules/store/csf/normalizeProjectAnnotations.ts index a93bf3584395..e2e6a88db31e 100644 --- a/code/lib/preview-api/src/modules/store/csf/normalizeProjectAnnotations.ts +++ b/code/lib/preview-api/src/modules/store/csf/normalizeProjectAnnotations.ts @@ -8,16 +8,21 @@ import type { import { inferArgTypes } from '../inferArgTypes'; import { inferControls } from '../inferControls'; import { normalizeInputTypes } from './normalizeInputTypes'; +import { normalizeArrays } from './normalizeArrays'; export function normalizeProjectAnnotations({ argTypes, globalTypes, argTypesEnhancers, + decorators, + loaders, ...annotations }: ProjectAnnotations): NormalizedProjectAnnotations { return { ...(argTypes && { argTypes: normalizeInputTypes(argTypes as ArgTypes) }), ...(globalTypes && { globalTypes: normalizeInputTypes(globalTypes) }), + decorators: normalizeArrays(decorators), + loaders: normalizeArrays(loaders), argTypesEnhancers: [ ...(argTypesEnhancers || []), inferArgTypes, diff --git a/code/lib/preview-api/src/modules/store/csf/normalizeStory.ts b/code/lib/preview-api/src/modules/store/csf/normalizeStory.ts index 6666a95217b5..9a96bc4a332e 100644 --- a/code/lib/preview-api/src/modules/store/csf/normalizeStory.ts +++ b/code/lib/preview-api/src/modules/store/csf/normalizeStory.ts @@ -13,6 +13,7 @@ import { dedent } from 'ts-dedent'; import { logger } from '@storybook/client-logger'; import deprecate from 'util-deprecate'; import { normalizeInputTypes } from './normalizeInputTypes'; +import { normalizeArrays } from './normalizeArrays'; const deprecatedStoryAnnotation = dedent` CSF .story annotations deprecated; annotate story functions directly: @@ -44,11 +45,15 @@ export function normalizeStory( storyObject.storyName || story?.name || exportName; - const decorators = [...(storyObject.decorators || []), ...(story?.decorators || [])]; + + const decorators = [ + ...normalizeArrays(storyObject.decorators), + ...normalizeArrays(story?.decorators), + ]; const parameters = { ...story?.parameters, ...storyObject.parameters }; const args = { ...story?.args, ...storyObject.args }; const argTypes = { ...(story?.argTypes as ArgTypes), ...(storyObject.argTypes as ArgTypes) }; - const loaders = [...(storyObject.loaders || []), ...(story?.loaders || [])]; + const loaders = [...normalizeArrays(storyObject.loaders), ...normalizeArrays(story?.loaders)]; const { render, play, tags = [] } = storyObject; // eslint-disable-next-line no-underscore-dangle diff --git a/code/lib/preview-api/src/modules/store/csf/prepareStory.test.ts b/code/lib/preview-api/src/modules/store/csf/prepareStory.test.ts index a7cb1d971e6d..b15c3a955659 100644 --- a/code/lib/preview-api/src/modules/store/csf/prepareStory.test.ts +++ b/code/lib/preview-api/src/modules/store/csf/prepareStory.test.ts @@ -343,9 +343,9 @@ describe('prepareStory', () => { ); const storyContext = { context: 'value' } as any; - const loadedContext = await applyLoaders(storyContext); + const loadedContext = await applyLoaders({ ...storyContext }); - expect(loader).toHaveBeenCalledWith(storyContext); + expect(loader).toHaveBeenCalledWith({ ...storyContext, loaded: {} }); expect(loadedContext).toEqual({ context: 'value', loaded: { foo: 7 }, diff --git a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts index e4bfb1937114..311a4467c789 100644 --- a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts +++ b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts @@ -1,24 +1,25 @@ +/* eslint-disable no-restricted-syntax,no-await-in-loop,@typescript-eslint/no-loop-func,no-underscore-dangle */ import { global } from '@storybook/global'; import type { - Renderer, Args, ArgsStoryFn, LegacyStoryFn, - Parameters, - PlayFunction, - PlayFunctionContext, - StepLabel, + ModuleExport, NormalizedComponentAnnotations, NormalizedProjectAnnotations, NormalizedStoryAnnotations, + Parameters, + PlayFunction, + PlayFunctionContext, + PreparedMeta, PreparedStory, + Renderer, + StepLabel, StoryContext, StoryContextForEnhancers, StoryContextForLoaders, StrictArgTypes, - PreparedMeta, - ModuleExport, } from '@storybook/types'; import { includeConditionalArg } from '@storybook/csf'; @@ -26,6 +27,7 @@ import { applyHooks } from '../../addons'; import { combineParameters } from '../parameters'; import { defaultDecorateStory } from '../decorators'; import { groupArgsByTarget, UNTARGETED } from '../args'; +import { normalizeArrays } from './normalizeArrays'; // Combine all the metadata about a story (both direct and inherited from the component/global scope) // into a "renderable" story function, with all decorators applied, parameters passed as context etc @@ -48,15 +50,23 @@ export function prepareStory( projectAnnotations ); - const loaders = [ - ...(projectAnnotations.loaders || []), - ...(componentAnnotations.loaders || []), - ...(storyAnnotations?.loaders || []), - ]; - const applyLoaders = async (context: StoryContextForLoaders) => { - const loadResults = await Promise.all(loaders.map((loader) => loader(context))); - const loaded = Object.assign({}, ...loadResults); - return { ...context, loaded }; + const applyLoaders = async ( + context: StoryContextForLoaders + ): Promise & { loaded: StoryContext['loaded'] }> => { + let updatedContext = { ...context, loaded: {} }; + for (const loaders of [ + ...('__STORYBOOK_TEST_LOADERS__' in global && Array.isArray(global.__STORYBOOK_TEST_LOADERS__) + ? [global.__STORYBOOK_TEST_LOADERS__] + : []), + normalizeArrays(projectAnnotations.loaders), + normalizeArrays(componentAnnotations.loaders), + normalizeArrays(storyAnnotations.loaders), + ]) { + const loadResults = await Promise.all(loaders.map((loader) => loader(updatedContext))); + const loaded: Record = Object.assign({}, ...loadResults); + updatedContext = { ...updatedContext, loaded: { ...updatedContext.loaded, ...loaded } }; + } + return updatedContext; }; const undecoratedStoryFn: LegacyStoryFn = (context: StoryContext) => { @@ -70,9 +80,9 @@ export function prepareStory( const { applyDecorators = defaultDecorateStory, runStep } = projectAnnotations; const decorators = [ - ...(storyAnnotations?.decorators || []), - ...(componentAnnotations.decorators || []), - ...(projectAnnotations.decorators || []), + ...normalizeArrays(storyAnnotations?.decorators), + ...normalizeArrays(componentAnnotations?.decorators), + ...normalizeArrays(projectAnnotations?.decorators), ]; // The render function on annotations *has* to be an `ArgsStoryFn`, so when we normalize @@ -115,7 +125,6 @@ export function prepareStory( playFunction, }; } - export function prepareMeta( componentAnnotations: NormalizedComponentAnnotations, projectAnnotations: NormalizedProjectAnnotations, @@ -164,7 +173,6 @@ function preparePartialAnnotations( const { passArgsFirst = true } = parameters; - // eslint-disable-next-line no-underscore-dangle parameters.__isArgsStory = passArgsFirst && render && render.length > 0; } diff --git a/code/lib/preview-api/src/modules/store/csf/testing-utils/index.ts b/code/lib/preview-api/src/modules/store/csf/testing-utils/index.ts index 10468737e6dc..c9a3731f4bd6 100644 --- a/code/lib/preview-api/src/modules/store/csf/testing-utils/index.ts +++ b/code/lib/preview-api/src/modules/store/csf/testing-utils/index.ts @@ -82,7 +82,7 @@ export function composeStory)); }, { storyName, diff --git a/code/lib/preview-api/src/modules/store/sortStories.ts b/code/lib/preview-api/src/modules/store/sortStories.ts index 4335b6f72103..8fc6950fbd40 100644 --- a/code/lib/preview-api/src/modules/store/sortStories.ts +++ b/code/lib/preview-api/src/modules/store/sortStories.ts @@ -9,6 +9,7 @@ import type { Parameters, Path, PreparedStory, + Renderer, } from '@storybook/types'; import { storySort } from './storySort'; @@ -58,8 +59,8 @@ const toIndexEntry = (story: any): StoryIndexEntry => { return { id, title, name, importPath: parameters.fileName, type }; }; -export const sortStoriesV6 = ( - stories: [string, PreparedStory, Parameters, Parameters][], +export const sortStoriesV6 = ( + stories: [string, PreparedStory, Parameters, Parameters][], storySortParameter: Addon_StorySortParameter, fileNameOrder: Path[] ) => { diff --git a/code/lib/preview/package.json b/code/lib/preview/package.json index 5b18427da1d5..ed4a088114bf 100644 --- a/code/lib/preview/package.json +++ b/code/lib/preview/package.json @@ -27,6 +27,7 @@ }, "./globals": { "types": "./dist/globals.d.ts", + "import": "./dist/globals.js", "require": "./dist/globals.js" }, "./package.json": "./package.json" @@ -52,8 +53,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/esm-bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/esm-bundle.ts" }, "devDependencies": { "@storybook/channels": "workspace:*", @@ -62,6 +63,8 @@ "@storybook/global": "^5.0.0", "@storybook/preview-api": "workspace:*", "browser-dtector": "^3.4.0", + "fs-extra": "^11.1.0", + "ts-dedent": "^2.0.0", "typescript": "~4.9.3" }, "publishConfig": { diff --git a/code/lib/preview/src/globals.ts b/code/lib/preview/src/globals.ts index fb7419cf52ed..3b9b2321b6f0 100644 --- a/code/lib/preview/src/globals.ts +++ b/code/lib/preview/src/globals.ts @@ -1 +1 @@ -export * from './globals/types'; +export * from './globals/globals'; diff --git a/code/lib/preview/src/globals/types.ts b/code/lib/preview/src/globals/globals.ts similarity index 73% rename from code/lib/preview/src/globals/types.ts rename to code/lib/preview/src/globals/globals.ts index 176d4582833d..3cd77282fe49 100644 --- a/code/lib/preview/src/globals/types.ts +++ b/code/lib/preview/src/globals/globals.ts @@ -1,6 +1,6 @@ -// Here we map the name of a module to their NAME in the global scope. -// eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle -const _globals = { +// Here we map the name of a module to their REFERENCE in the global scope. + +export const globalsNameReferenceMap = { '@storybook/addons': '__STORYBOOK_MODULE_ADDONS__', '@storybook/global': '__STORYBOOK_MODULE_GLOBAL__', '@storybook/channel-postmessage': '__STORYBOOK_MODULE_CHANNEL_POSTMESSAGE__', // @deprecated: remove in 8.0 @@ -13,6 +13,9 @@ const _globals = { '@storybook/preview-web': '__STORYBOOK_MODULE_PREVIEW_WEB__', '@storybook/preview-api': '__STORYBOOK_MODULE_PREVIEW_API__', '@storybook/store': '__STORYBOOK_MODULE_STORE__', -}; + '@storybook/types': '__STORYBOOK_MODULE_TYPES__', +} as const; -export const globals: typeof _globals & Record = _globals; +export const globalPackages = Object.keys(globalsNameReferenceMap) as Array< + keyof typeof globalsNameReferenceMap +>; diff --git a/code/lib/preview/src/globals/runtime.ts b/code/lib/preview/src/globals/runtime.ts index ad078c4afa8d..f05dd3ef24cd 100644 --- a/code/lib/preview/src/globals/runtime.ts +++ b/code/lib/preview/src/globals/runtime.ts @@ -1,9 +1,8 @@ -import * as CHANNEL_POSTMESSAGE from '@storybook/channels/dist/postmessage/index'; -import * as CHANNEL_WEBSOCKET from '@storybook/channels/dist/websocket/index'; import * as CHANNELS from '@storybook/channels'; import * as CLIENT_LOGGER from '@storybook/client-logger'; import * as CORE_EVENTS from '@storybook/core-events'; import * as PREVIEW_API from '@storybook/preview-api'; +import * as TYPES from '@storybook/types'; import * as GLOBAL from '@storybook/global'; // DEPRECATED, remove in 8.0 @@ -12,20 +11,23 @@ import * as CLIENT_API from '@storybook/preview-api/dist/client-api'; import * as CORE_CLIENT from '@storybook/preview-api/dist/core-client'; import * as PREVIEW_WEB from '@storybook/preview-api/dist/preview-web'; import * as STORE from '@storybook/preview-api/dist/store'; +import * as CHANNEL_POSTMESSAGE from '@storybook/channels/dist/postmessage/index'; +import * as CHANNEL_WEBSOCKET from '@storybook/channels/dist/websocket/index'; -import type { globals } from './types'; +import type { globalsNameReferenceMap } from './globals'; // Here we map the name of a module to their VALUE in the global scope. -export const values: Required> = { - '@storybook/channel-postmessage': CHANNEL_POSTMESSAGE, // @deprecated: remove in 8.0 - '@storybook/channel-websocket': CHANNEL_WEBSOCKET, // @deprecated: remove in 8.0 +export const globalsNameValueMap: Required> = { '@storybook/channels': CHANNELS, '@storybook/client-logger': CLIENT_LOGGER, '@storybook/core-events': CORE_EVENTS, '@storybook/preview-api': PREVIEW_API, '@storybook/global': GLOBAL, + '@storybook/types': TYPES, // DEPRECATED, remove in 8.0 + '@storybook/channel-postmessage': CHANNEL_POSTMESSAGE, + '@storybook/channel-websocket': CHANNEL_WEBSOCKET, '@storybook/addons': ADDONS, '@storybook/client-api': CLIENT_API, '@storybook/core-client': CORE_CLIENT, diff --git a/code/lib/preview/src/runtime.ts b/code/lib/preview/src/runtime.ts index 603c586b9fe3..6b2900b68820 100644 --- a/code/lib/preview/src/runtime.ts +++ b/code/lib/preview/src/runtime.ts @@ -1,15 +1,13 @@ import { TELEMETRY_ERROR } from '@storybook/core-events'; import { global } from '@storybook/global'; +import { globalPackages, globalsNameReferenceMap } from './globals/globals'; +import { globalsNameValueMap } from './globals/runtime'; -import { values } from './globals/runtime'; -import { globals } from './globals/types'; import { prepareForTelemetry } from './utils'; -const getKeys = Object.keys as (obj: T) => Array; - // Apply all the globals -getKeys(globals).forEach((key) => { - (global as any)[globals[key]] = values[key]; +globalPackages.forEach((key) => { + (global as any)[globalsNameReferenceMap[key]] = globalsNameValueMap[key]; }); global.sendTelemetryError = (error: any) => { diff --git a/code/lib/react-dom-shim/package.json b/code/lib/react-dom-shim/package.json index fbcb223150d7..abbe4e836247 100644 --- a/code/lib/react-dom-shim/package.json +++ b/code/lib/react-dom-shim/package.json @@ -50,8 +50,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "devDependencies": { "@storybook/types": "workspace:*", diff --git a/code/lib/router/package.json b/code/lib/router/package.json index 49eec650b67d..61c2f6e4f361 100644 --- a/code/lib/router/package.json +++ b/code/lib/router/package.json @@ -45,8 +45,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/client-logger": "workspace:*", @@ -57,14 +57,12 @@ "@storybook/global": "^5.0.0", "dequal": "^2.0.2", "lodash": "^4.17.21", + "react": "^16.8.0", + "react-dom": "^16.8.0", "react-router-dom": "6.0.2", "ts-dedent": "^2.0.0", "typescript": "~4.9.3" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, "publishConfig": { "access": "public" }, @@ -73,6 +71,20 @@ "entries": [ "./src/index.ts", "./src/utils.ts" + ], + "externals": [ + "react", + "react-dom", + "@storybook/addons", + "@storybook/api", + "@storybook/channels", + "@storybook/client-logger", + "@storybook/components", + "@storybook/core-events", + "@storybook/manager-api", + "@storybook/router", + "@storybook/theming", + "@storybook/types" ] }, "gitHead": "e6a7fd8a655c69780bc20b9749c2699e44beae17" diff --git a/code/lib/source-loader/package.json b/code/lib/source-loader/package.json index 38459e761aad..9c93afba521f 100644 --- a/code/lib/source-loader/package.json +++ b/code/lib/source-loader/package.json @@ -41,11 +41,11 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { - "@storybook/csf": "^0.1.0", + "@storybook/csf": "^0.1.2", "@storybook/types": "workspace:*", "estraverse": "^5.2.0", "lodash": "^4.17.21", @@ -55,10 +55,6 @@ "jest-specific-snapshot": "^8.0.0", "typescript": "~4.9.3" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, "publishConfig": { "access": "public" }, diff --git a/code/lib/telemetry/package.json b/code/lib/telemetry/package.json index ce48721ee219..e089b214bea3 100644 --- a/code/lib/telemetry/package.json +++ b/code/lib/telemetry/package.json @@ -40,8 +40,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/client-logger": "workspace:*", diff --git a/code/lib/telemetry/src/storybook-metadata.ts b/code/lib/telemetry/src/storybook-metadata.ts index 2c70b566f97d..782c4d4a4b78 100644 --- a/code/lib/telemetry/src/storybook-metadata.ts +++ b/code/lib/telemetry/src/storybook-metadata.ts @@ -164,13 +164,17 @@ export const computeStorybookMetadata = async ({ const storybookInfo = getStorybookInfo(packageJson); - const { previewConfig } = storybookInfo; - if (previewConfig) { - const config = await readConfig(previewConfig); - const usesGlobals = !!( - config.getFieldNode(['globals']) || config.getFieldNode(['globalTypes']) - ); - metadata.preview = { ...metadata.preview, usesGlobals }; + try { + const { previewConfig } = storybookInfo; + if (previewConfig) { + const config = await readConfig(previewConfig); + const usesGlobals = !!( + config.getFieldNode(['globals']) || config.getFieldNode(['globalTypes']) + ); + metadata.preview = { ...metadata.preview, usesGlobals }; + } + } catch (e) { + // gracefully handle error, as it's not critical information and AST parsing can cause trouble } const storybookVersion = storybookPackages[storybookInfo.frameworkPackage]?.version; diff --git a/code/lib/test/package.json b/code/lib/test/package.json index 869c3cd3df9e..41c3d658bad1 100644 --- a/code/lib/test/package.json +++ b/code/lib/test/package.json @@ -19,7 +19,7 @@ "url": "https://opencollective.com/storybook" }, "license": "MIT", - "sideEffects": false, + "sideEffects": true, "exports": { ".": { "types": "./dist/index.d.ts", @@ -39,8 +39,8 @@ "*.d.ts" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/client-logger": "workspace:*", diff --git a/code/lib/test/src/index.ts b/code/lib/test/src/index.ts index 34d59391676e..600c68f1d3b4 100644 --- a/code/lib/test/src/index.ts +++ b/code/lib/test/src/index.ts @@ -1,18 +1,11 @@ import { instrument } from '@storybook/instrumenter'; -import * as spy from '@vitest/spy'; +import { type LoaderFunction } from '@storybook/csf'; import chai from 'chai'; -import { FORCE_REMOUNT, STORY_RENDER_PHASE_CHANGED } from '@storybook/core-events'; -import { addons } from '@storybook/preview-api'; +import { global } from '@storybook/global'; import { expect as rawExpect } from './expect'; +import { clearAllMocks, resetAllMocks, restoreAllMocks } from './spy'; -export * from '@vitest/spy'; - -const channel = addons.getChannel(); - -channel.on(FORCE_REMOUNT, () => spy.spies.forEach((mock) => mock.mockClear())); -channel.on(STORY_RENDER_PHASE_CHANGED, ({ newPhase }) => { - if (newPhase === 'loading') spy.spies.forEach((mock) => mock.mockClear()); -}); +export * from './spy'; export const { expect } = instrument( { expect: rawExpect }, @@ -32,3 +25,17 @@ export const { expect } = instrument( ); export * from './testing-library'; + +const resetAllMocksLoader: LoaderFunction = ({ parameters }) => { + if (parameters?.test?.mockReset === true) { + resetAllMocks(); + } else if (parameters?.test?.clearMocks === true) { + clearAllMocks(); + } else if (parameters?.test?.restoreMocks !== false) { + restoreAllMocks(); + } +}; + +// @ts-expect-error 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.__STORYBOOK_TEST_LOADERS__ = [resetAllMocksLoader]; diff --git a/code/lib/test/src/spy.ts b/code/lib/test/src/spy.ts new file mode 100644 index 000000000000..8b0ce5f38e56 --- /dev/null +++ b/code/lib/test/src/spy.ts @@ -0,0 +1,66 @@ +import { + spyOn, + isMockFunction, + fn, + spies as mocks, + type MaybeMocked, + type MaybeMockedDeep, + type MaybePartiallyMocked, + type MaybePartiallyMockedDeep, +} from '@vitest/spy'; + +export type * from '@vitest/spy'; + +export { spyOn, isMockFunction, fn, mocks }; + +/** + * Calls [`.mockClear()`](https://vitest.dev/api/mock#mockclear) on every mocked function. This will only empty `.mock` state, it will not reset implementation. + * + * It is useful if you need to clean up mock between different assertions. + */ +export function clearAllMocks() { + mocks.forEach((spy) => spy.mockClear()); +} + +/** + * Calls [`.mockReset()`](https://vitest.dev/api/mock#mockreset) on every mocked function. This will empty `.mock` state, reset "once" implementations and force the base implementation to return `undefined` when invoked. + * + * This is useful when you want to completely reset a mock to the default state. + */ +export function resetAllMocks() { + mocks.forEach((spy) => spy.mockReset()); +} + +/** + * Calls [`.mockRestore()`](https://vitest.dev/api/mock#mockrestore) on every mocked function. This will restore all original implementations. + */ +export function restoreAllMocks() { + mocks.forEach((spy) => spy.mockRestore()); +} + +/** + * Type helper for TypeScript. Just returns the object that was passed. + * + * When `partial` is `true` it will expect a `Partial` as a return value. By default, this will only make TypeScript believe that + * the first level values are mocked. You can pass down `{ deep: true }` as a second argument to tell TypeScript that the whole object is mocked, if it actually is. + * + * @param item Anything that can be mocked + * @param deep If the object is deeply mocked + * @param options If the object is partially or deeply mocked + */ +export function mocked(item: T, deep?: false): MaybeMocked; +export function mocked(item: T, deep: true): MaybeMockedDeep; +export function mocked(item: T, options: { partial?: false; deep?: false }): MaybeMocked; +export function mocked(item: T, options: { partial?: false; deep: true }): MaybeMockedDeep; +export function mocked( + item: T, + options: { partial: true; deep?: false } +): MaybePartiallyMocked; +export function mocked( + item: T, + options: { partial: true; deep: true } +): MaybePartiallyMockedDeep; +export function mocked(item: T): MaybeMocked; +export function mocked(item: T, _options = {}): MaybeMocked { + return item as any; +} diff --git a/code/lib/theming/package.json b/code/lib/theming/package.json index 4c9ff26433e5..e22205ff2c75 100644 --- a/code/lib/theming/package.json +++ b/code/lib/theming/package.json @@ -45,8 +45,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", @@ -79,6 +79,20 @@ "./src/index.ts", "./src/create.ts" ], + "externals": [ + "react", + "react-dom", + "@storybook/addons", + "@storybook/api", + "@storybook/channels", + "@storybook/client-logger", + "@storybook/components", + "@storybook/core-events", + "@storybook/manager-api", + "@storybook/router", + "@storybook/theming", + "@storybook/types" + ], "post": "./scripts/fix-theme-type-export.ts" }, "gitHead": "e6a7fd8a655c69780bc20b9749c2699e44beae17" diff --git a/code/lib/types/package.json b/code/lib/types/package.json index 9c88dcc60004..e14551c1822c 100644 --- a/code/lib/types/package.json +++ b/code/lib/types/package.json @@ -40,8 +40,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/channels": "workspace:*", @@ -50,7 +50,7 @@ "file-system-cache": "2.3.0" }, "devDependencies": { - "@storybook/csf": "^0.1.0", + "@storybook/csf": "^0.1.2", "@types/fs-extra": "^11.0.1", "@types/node": "^18.0.0", "typescript": "~4.9.3" diff --git a/code/lib/types/src/modules/core-common.ts b/code/lib/types/src/modules/core-common.ts index f5043c362d7c..2f7077fc5d88 100644 --- a/code/lib/types/src/modules/core-common.ts +++ b/code/lib/types/src/modules/core-common.ts @@ -223,8 +223,16 @@ export interface TypescriptOptions { * Disable parsing typescript files through babel. * * @default `false` + * @deprecated use `skipCompiler` instead */ skipBabel: boolean; + + /** + * Disable parsing typescript files through compiler. + * + * @default `false` + */ + skipCompiler: boolean; } export type Preset = @@ -356,6 +364,13 @@ export interface StorybookConfig { * Apply decorators from preview.js before decorators from addons or frameworks */ legacyDecoratorFileOrder?: boolean; + + /** + * Disallow implicit actions during rendering. This will be the default in Storybook 8. + * + * This will make sure that your story renders the same no matter if docgen is enabled or not. + */ + disallowImplicitActionsInRenderV8?: boolean; }; build?: TestBuildConfig; diff --git a/code/lib/types/src/modules/story.ts b/code/lib/types/src/modules/story.ts index c1a5b9d6ffdd..cd4e9f1c9d16 100644 --- a/code/lib/types/src/modules/story.ts +++ b/code/lib/types/src/modules/story.ts @@ -1,4 +1,9 @@ -import type { Renderer, ProjectAnnotations as CsfProjectAnnotations } from '@storybook/csf'; +import type { + Renderer, + ProjectAnnotations as CsfProjectAnnotations, + DecoratorFunction, + LoaderFunction, +} from '@storybook/csf'; import type { ComponentAnnotations, @@ -42,23 +47,31 @@ export type ProjectAnnotations = CsfProjectAnnotatio renderToDOM?: RenderToCanvas; }; -export type NormalizedProjectAnnotations = - ProjectAnnotations & { - argTypes?: StrictArgTypes; - globalTypes?: StrictGlobalTypes; - }; +export type NormalizedProjectAnnotations = Omit< + ProjectAnnotations, + 'decorators' | 'loaders' +> & { + argTypes?: StrictArgTypes; + globalTypes?: StrictGlobalTypes; + decorators?: DecoratorFunction[]; + loaders?: LoaderFunction[]; +}; -export type NormalizedComponentAnnotations = - ComponentAnnotations & { - // Useful to guarantee that id & title exists - id: ComponentId; - title: ComponentTitle; - argTypes?: StrictArgTypes; - }; +export type NormalizedComponentAnnotations = Omit< + ComponentAnnotations, + 'decorators' | 'loaders' +> & { + // Useful to guarantee that id & title exists + id: ComponentId; + title: ComponentTitle; + argTypes?: StrictArgTypes; + decorators?: DecoratorFunction[]; + loaders?: LoaderFunction[]; +}; export type NormalizedStoryAnnotations = Omit< StoryAnnotations, - 'storyName' | 'story' + 'storyName' | 'story' | 'decorators' | 'loaders' > & { moduleExport: ModuleExport; // You cannot actually set id on story annotations, but we normalize it to be there for convience @@ -66,6 +79,8 @@ export type NormalizedStoryAnnotations = argTypes?: StrictArgTypes; name: StoryName; userStoryFn?: StoryFn; + decorators?: DecoratorFunction[]; + loaders?: LoaderFunction[]; }; export type CSFFile = { diff --git a/code/package.json b/code/package.json index f1ac76ed7b71..f9be9959024a 100644 --- a/code/package.json +++ b/code/package.json @@ -26,13 +26,13 @@ }, "scripts": { "await-serve-storybooks": "wait-on http://localhost:8001", - "build": "NODE_ENV=production ts-node --swc --esm ../scripts/build-package.ts", + "build": "NODE_ENV=production yarn --cwd ../scripts build-package", "changelog": "pr-log --sloppy --cherry-pick", "changelog:next": "pr-log --sloppy --since-prerelease", - "check": "NODE_ENV=production node ../scripts/check-package.js", + "check": "NODE_ENV=production yarn --cwd ../scripts check-package", "ci-tests": "yarn task --task check --no-link --start-from=install && yarn lint && yarn test && cd ../scripts && yarn test", "danger": "danger", - "generate-sandboxes": "ts-node --swc ../scripts/sandbox/generate.ts", + "generate-sandboxes": "yarn --cwd ../scripts generate-sandboxes", "github-release": "github-release-from-changelog", "lint": "yarn lint:js && yarn lint:md", "lint:ejs": "ejslint **/*.ejs", @@ -41,15 +41,15 @@ "lint:md": "../scripts/node_modules/.bin/remark -q .", "lint:other": "prettier --write '**/*.{css,html,json,md,yml}'", "lint:package": "sort-package-json", - "local-registry": "ts-node --swc --project=../scripts/tsconfig.json ../scripts/run-registry.ts", - "publish-sandboxes": "ts-node --swc ../scripts/sandbox/publish.ts", + "local-registry": "yarn --cwd ../scripts local-registry", + "publish-sandboxes": "yarn --cwd ../scripts publish", "storybook:blocks": "STORYBOOK_BLOCKS_ONLY=true yarn storybook:ui", "storybook:blocks:build": "STORYBOOK_BLOCKS_ONLY=true yarn storybook:ui:build", "storybook:blocks:chromatic": "STORYBOOK_BLOCKS_ONLY=true yarn storybook:ui:chromatic --project-token=${CHROMATIC_TOKEN_STORYBOOK_BLOCKS:-MISSING_PROJECT_TOKEN}", "storybook:ui": "NODE_OPTIONS=\"--preserve-symlinks --preserve-symlinks-main\" ./lib/cli/bin/index.js dev --port 6006 --config-dir ./ui/.storybook", "storybook:ui:build": "NODE_OPTIONS=\"--preserve-symlinks --preserve-symlinks-main\" ./lib/cli/bin/index.js build --config-dir ./ui/.storybook", "storybook:ui:chromatic": "yarn chromatic --build-script-name storybook:ui:build --storybook-config-dir ./ui/.storybook --storybook-base-dir ./code --project-token=${CHROMATIC_TOKEN_STORYBOOK_UI:-MISSING_PROJECT_TOKEN} --only-changed --exit-zero-on-changes --exit-once-uploaded", - "task": "cd .. && yarn task", + "task": "yarn --cwd ../scripts task", "test": "NODE_OPTIONS=--max_old_space_size=4096 jest --config ./jest.config.js", "test:cli": "npm --prefix lib/cli run test" }, @@ -140,7 +140,7 @@ "@storybook/core-events": "workspace:*", "@storybook/core-server": "workspace:*", "@storybook/core-webpack": "workspace:*", - "@storybook/csf": "^0.1.0", + "@storybook/csf": "^0.1.2", "@storybook/csf-plugin": "workspace:*", "@storybook/csf-tools": "workspace:*", "@storybook/docs-tools": "workspace:*", @@ -212,7 +212,7 @@ "@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/experimental-utils": "^5.45.0", "@typescript-eslint/parser": "^5.45.0", - "@vitejs/plugin-react": "^2.1.0", + "@vitejs/plugin-react": "^3.0.1", "babel-eslint": "^10.1.0", "babel-loader": "^9.1.2", "chromatic": "7.1.0", @@ -328,5 +328,6 @@ "Dependency Upgrades" ] ] - } + }, + "deferredNextVersion": "7.6.0-alpha.7" } diff --git a/code/presets/create-react-app/README.md b/code/presets/create-react-app/README.md index c20a9d2e7672..de48890d4c8a 100644 --- a/code/presets/create-react-app/README.md +++ b/code/presets/create-react-app/README.md @@ -6,7 +6,7 @@ This preset is designed to use alongside [`@storybook/react`](https://github.com ## Compatibility -This version (4.x) of `@storybook/preset-create-react-app` is compatibly with Create React App version 5 and above. Earlier versions are compatible with earlier version of the preset. +From version 4.0.0 onwards, the `@storybook/preset-create-react-app` is compatible with Create React App version 5 and later. If you're using an earlier version of Create React App, you can still utilize the preset's previous versions. ## Basic usage diff --git a/code/presets/create-react-app/package.json b/code/presets/create-react-app/package.json index 823905784767..3c197b345ddc 100644 --- a/code/presets/create-react-app/package.json +++ b/code/presets/create-react-app/package.json @@ -45,8 +45,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@pmmmwh/react-refresh-webpack-plugin": "^0.5.1", diff --git a/code/presets/html-webpack/package.json b/code/presets/html-webpack/package.json index 7d0be5a2b3cd..cec79e6729e7 100644 --- a/code/presets/html-webpack/package.json +++ b/code/presets/html-webpack/package.json @@ -45,8 +45,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/core-webpack": "workspace:*", diff --git a/code/presets/preact-webpack/package.json b/code/presets/preact-webpack/package.json index bae4b5c310dc..fc9f3ded289e 100644 --- a/code/presets/preact-webpack/package.json +++ b/code/presets/preact-webpack/package.json @@ -45,8 +45,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@babel/plugin-transform-react-jsx": "^7.22.15", diff --git a/code/presets/react-webpack/package.json b/code/presets/react-webpack/package.json index 3505f3b07a59..615bcf560611 100644 --- a/code/presets/react-webpack/package.json +++ b/code/presets/react-webpack/package.json @@ -65,8 +65,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@babel/preset-flow": "^7.22.15", diff --git a/code/presets/server-webpack/package.json b/code/presets/server-webpack/package.json index 2a71116c946e..0112b3297810 100644 --- a/code/presets/server-webpack/package.json +++ b/code/presets/server-webpack/package.json @@ -50,8 +50,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/core-server": "workspace:*", diff --git a/code/presets/svelte-webpack/package.json b/code/presets/svelte-webpack/package.json index 43453d287129..fce916f2ac7d 100644 --- a/code/presets/svelte-webpack/package.json +++ b/code/presets/svelte-webpack/package.json @@ -60,8 +60,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/core-webpack": "workspace:*", diff --git a/code/presets/vue-webpack/package.json b/code/presets/vue-webpack/package.json index 79007a06fda8..58cc54bdcd68 100644 --- a/code/presets/vue-webpack/package.json +++ b/code/presets/vue-webpack/package.json @@ -55,8 +55,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/core-webpack": "workspace:*", diff --git a/code/presets/vue3-webpack/package.json b/code/presets/vue3-webpack/package.json index 2c84d6e63ab3..28a60b682322 100644 --- a/code/presets/vue3-webpack/package.json +++ b/code/presets/vue3-webpack/package.json @@ -55,8 +55,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/core-webpack": "workspace:*", diff --git a/code/presets/web-components-webpack/package.json b/code/presets/web-components-webpack/package.json index 9fe830b13f62..2df5a10d7653 100644 --- a/code/presets/web-components-webpack/package.json +++ b/code/presets/web-components-webpack/package.json @@ -48,8 +48,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@babel/plugin-syntax-dynamic-import": "^7.8.3", diff --git a/code/prettier.config.js b/code/prettier.config.js index 574656f01614..4c8f0237382a 100644 --- a/code/prettier.config.js +++ b/code/prettier.config.js @@ -1 +1 @@ -module.exports = require('../scripts/prettier.config'); +module.exports = require('../scripts/prettier.config.cjs'); diff --git a/code/renderers/html/package.json b/code/renderers/html/package.json index caa8707ade5d..195887358137 100644 --- a/code/renderers/html/package.json +++ b/code/renderers/html/package.json @@ -43,8 +43,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/core-client": "workspace:*", diff --git a/code/renderers/preact/package.json b/code/renderers/preact/package.json index ffe02fe2b8ab..14dde6ad4e19 100644 --- a/code/renderers/preact/package.json +++ b/code/renderers/preact/package.json @@ -43,8 +43,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/core-client": "workspace:*", diff --git a/code/renderers/preact/template/cli/Button.jsx b/code/renderers/preact/template/cli/Button.jsx index eefda2bffe3f..9bcecb3710d7 100644 --- a/code/renderers/preact/template/cli/Button.jsx +++ b/code/renderers/preact/template/cli/Button.jsx @@ -1,10 +1,21 @@ -import PropTypes from 'prop-types'; import './button.css'; /** * Primary UI component for user interaction + * @param {object} props + * @param {string} [props.primary=false] + * @param {string} [props.backgroundColor] + * @param {('small' | 'medium' | 'large')} [props.size='medium'] + * @param {string} props.label + * @param {function} props.onClick */ -export const Button = ({ primary, backgroundColor, size, label, ...props }) => { +export const Button = ({ + primary = false, + backgroundColor = null, + size = 'medium', + label, + ...props +}) => { const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary'; return ( ); }; - -Button.propTypes = { - /** - * Is this the principal call to action on the page? - */ - primary: PropTypes.bool, - /** - * What background color to use - */ - backgroundColor: PropTypes.string, - /** - * How large should the button be? - */ - size: PropTypes.oneOf(['small', 'medium', 'large']), - /** - * Button contents - */ - label: PropTypes.string.isRequired, - /** - * Optional click handler - */ - onClick: PropTypes.func, -}; - -Button.defaultProps = { - backgroundColor: null, - primary: false, - size: 'medium', - onClick: undefined, -}; diff --git a/code/renderers/preact/template/cli/Header.jsx b/code/renderers/preact/template/cli/Header.jsx index 8a722e57d331..c87a04c4837e 100644 --- a/code/renderers/preact/template/cli/Header.jsx +++ b/code/renderers/preact/template/cli/Header.jsx @@ -1,9 +1,16 @@ -import PropTypes from 'prop-types'; - import { Button } from './Button'; import './header.css'; -export const Header = ({ user, onLogin, onLogout, onCreateAccount }) => ( +/** + * Header component + * @param {object} props + * @param {object} [props.user] + * @param {string} props.user.name + * @param {function} props.onLogin + * @param {function} props.onLogout + * @param {function} props.onCreateAccount + */ +export const Header = ({ user = null, onLogin, onLogout, onCreateAccount }) => (
@@ -43,16 +50,3 @@ export const Header = ({ user, onLogin, onLogout, onCreateAccount }) => (
); - -Header.propTypes = { - user: PropTypes.shape({ - name: PropTypes.string.isRequired, - }), - onLogin: PropTypes.func.isRequired, - onLogout: PropTypes.func.isRequired, - onCreateAccount: PropTypes.func.isRequired, -}; - -Header.defaultProps = { - user: null, -}; diff --git a/code/renderers/preact/template/cli/Page.jsx b/code/renderers/preact/template/cli/Page.jsx index 12ca119d0b04..a5bc82d9d105 100644 --- a/code/renderers/preact/template/cli/Page.jsx +++ b/code/renderers/preact/template/cli/Page.jsx @@ -2,6 +2,9 @@ import { useState } from 'preact/hooks'; import { Header } from './Header'; import './page.css'; +/** + * Simple page component + */ export const Page = () => { const [user, setUser] = useState(); diff --git a/code/renderers/preact/template/components/Button.jsx b/code/renderers/preact/template/components/Button.jsx index cd3c13987c32..e79f89f5d4b3 100644 --- a/code/renderers/preact/template/components/Button.jsx +++ b/code/renderers/preact/template/components/Button.jsx @@ -1,14 +1,11 @@ -/* eslint-disable react/react-in-jsx-scope */ -// eslint-disable-next-line import/no-extraneous-dependencies -import PropTypes from 'prop-types'; - +/** + * Button component + * @param {object} props + * @param {string} props.label + * @param {function} props.onClick + */ export const Button = ({ onClick, label }) => ( ); - -Button.propTypes = { - onClick: PropTypes.func.isRequired, - label: PropTypes.node.isRequired, -}; diff --git a/code/renderers/preact/template/components/Form.jsx b/code/renderers/preact/template/components/Form.jsx index fb58cbb10508..b2283d26ac10 100644 --- a/code/renderers/preact/template/components/Form.jsx +++ b/code/renderers/preact/template/components/Form.jsx @@ -1,8 +1,14 @@ -/* eslint-disable react/react-in-jsx-scope */ -// eslint-disable-next-line import/no-extraneous-dependencies -import PropTypes from 'prop-types'; import { useState } from 'preact/hooks'; +/** + * Header component + * @param {object} props + * @param {object} [props.user] + * @param {string} props.user.name + * @param {function} props.onLogin + * @param {function} props.onLogout + * @param {function} props.onCreateAccount + */ export const Form = ({ onSuccess }) => { const [value, setValue] = useState(''); const [complete, setComplete] = useState(false); @@ -32,7 +38,3 @@ export const Form = ({ onSuccess }) => { ); }; - -Form.propTypes = { - onSuccess: PropTypes.func.isRequired, -}; diff --git a/code/renderers/preact/template/components/Html.jsx b/code/renderers/preact/template/components/Html.jsx index 36cff8218890..b2c1f966f9e3 100644 --- a/code/renderers/preact/template/components/Html.jsx +++ b/code/renderers/preact/template/components/Html.jsx @@ -1,10 +1 @@ -/* eslint-disable react/react-in-jsx-scope */ -// eslint-disable-next-line import/no-extraneous-dependencies -import PropTypes from 'prop-types'; - -// eslint-disable-next-line react/no-danger export const Html = ({ content }) =>
; - -Html.propTypes = { - content: PropTypes.string.isRequired, -}; diff --git a/code/renderers/preact/template/components/Pre.jsx b/code/renderers/preact/template/components/Pre.jsx index 7efec93be9d6..3bf143581fac 100644 --- a/code/renderers/preact/template/components/Pre.jsx +++ b/code/renderers/preact/template/components/Pre.jsx @@ -1,21 +1,12 @@ -/* eslint-disable react/react-in-jsx-scope */ -// eslint-disable-next-line import/no-extraneous-dependencies -import PropTypes from 'prop-types'; - +/** + * Pre component + * @param {object} props + * @param {object} [props.style] + * @param {object} [props.object] + * @param {string} [props.text] + */ export const Pre = ({ style, object, text }) => (
     {object ? JSON.stringify(object, null, 2) : text}
   
); - -Pre.propTypes = { - style: PropTypes.shape({}), - object: PropTypes.shape({}), - text: PropTypes.string, -}; - -Pre.defaultProps = { - style: {}, - object: null, - text: '', -}; diff --git a/code/renderers/preact/template/stories/React.js b/code/renderers/preact/template/stories/React.js index 0c72668ebe74..1d1fa97c55d8 100644 --- a/code/renderers/preact/template/stories/React.js +++ b/code/renderers/preact/template/stories/React.js @@ -1,8 +1,11 @@ // eslint-disable-next-line import/no-extraneous-dependencies import React from 'react'; -// eslint-disable-next-line import/no-extraneous-dependencies -import PropTypes from 'prop-types'; +/** + * ReactFunctionalComponent component + * @param {object} props + * @param {string} props.label + */ export const ReactFunctionalComponent = ({ label }) => { const [clicks, setValue] = React.useState(0); return ( @@ -19,10 +22,11 @@ export const ReactFunctionalComponent = ({ label }) => { ); }; -ReactFunctionalComponent.propTypes = { - label: PropTypes.string.isRequired, -}; - +/** + * ReactClassComponent component + * @param {object} props + * @param {string} props.label + */ export class ReactClassComponent extends React.Component { state = { clicks: 0, @@ -45,7 +49,3 @@ export class ReactClassComponent extends React.Component { ); } } - -ReactClassComponent.propTypes = { - label: PropTypes.string.isRequired, -}; diff --git a/code/renderers/preact/template/stories/react-compat.stories.js b/code/renderers/preact/template/stories/react-compat.stories.js index b7895c1e8d0f..33f1078d3154 100644 --- a/code/renderers/preact/template/stories/react-compat.stories.js +++ b/code/renderers/preact/template/stories/react-compat.stories.js @@ -1,4 +1,3 @@ -/* eslint-disable react/react-in-jsx-scope */ import { ReactFunctionalComponent, ReactClassComponent } from './React'; export default { diff --git a/code/renderers/react/package.json b/code/renderers/react/package.json index 9bdb54bf85d3..a3ec6b71768b 100644 --- a/code/renderers/react/package.json +++ b/code/renderers/react/package.json @@ -43,8 +43,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/client-logger": "workspace:*", diff --git a/code/renderers/server/package.json b/code/renderers/server/package.json index 1db05b78d182..c20511b1feff 100644 --- a/code/renderers/server/package.json +++ b/code/renderers/server/package.json @@ -42,12 +42,12 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/core-client": "workspace:*", - "@storybook/csf": "^0.1.0", + "@storybook/csf": "^0.1.2", "@storybook/csf-tools": "workspace:*", "@storybook/global": "^5.0.0", "@storybook/preview-api": "workspace:*", diff --git a/code/renderers/svelte/package.json b/code/renderers/svelte/package.json index f745fda938fa..a30f5c8963f7 100644 --- a/code/renderers/svelte/package.json +++ b/code/renderers/svelte/package.json @@ -48,7 +48,7 @@ ], "scripts": { "check": "svelte-check", - "prep": "../../../scripts/prepare/bundle.ts" + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/client-logger": "workspace:*", @@ -59,6 +59,7 @@ "@storybook/preview-api": "workspace:*", "@storybook/types": "workspace:*", "sveltedoc-parser": "^4.2.1", + "ts-dedent": "^2.0.0", "type-fest": "~2.19" }, "devDependencies": { diff --git a/code/renderers/svelte/template/stories/decorators-runs-once.stories.js b/code/renderers/svelte/template/stories/decorators-runs-once.stories.js new file mode 100644 index 000000000000..5186ccb14d88 --- /dev/null +++ b/code/renderers/svelte/template/stories/decorators-runs-once.stories.js @@ -0,0 +1,16 @@ +import ButtonJavaScript from './views/ButtonJavaScript.svelte'; + +export default { + component: ButtonJavaScript, + args: { + primary: true, + }, + decorators: [ + (Story) => { + console.log('decorator called'); + return Story(); + }, + ], +}; + +export const Default = {}; diff --git a/code/renderers/svelte/templates/PreviewRender.svelte b/code/renderers/svelte/templates/PreviewRender.svelte index 7747cd8fcaba..581aa4d78e05 100644 --- a/code/renderers/svelte/templates/PreviewRender.svelte +++ b/code/renderers/svelte/templates/PreviewRender.svelte @@ -17,8 +17,25 @@ on, } = storyFn(); + let firstTime = true; + + // the first time we don't want to call storyFn two times so we just return the values + // we already have from the previous call. If storyFn changes this function will run + // again but this time firstTime will be false + function getStoryFnValue(storyFn){ + if(firstTime){ + firstTime = false; + return { + Component, + props, + on, + } + } + return storyFn(); + } + // reactive, re-render on storyFn change - $: ({ Component, props = {}, on } = storyFn()); + $: ({ Component, props = {}, on } = getStoryFnValue(storyFn)); const eventsFromArgTypes = Object.fromEntries( Object.entries(storyContext.argTypes) diff --git a/code/renderers/vue/package.json b/code/renderers/vue/package.json index d085597863d8..535ed37f9ef0 100644 --- a/code/renderers/vue/package.json +++ b/code/renderers/vue/package.json @@ -44,7 +44,7 @@ ], "scripts": { "check": "vue-tsc --noEmit", - "prep": "../../../scripts/prepare/bundle.ts" + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/client-logger": "workspace:*", diff --git a/code/renderers/vue3/package.json b/code/renderers/vue3/package.json index 8905958c8595..31da23bcc0a2 100644 --- a/code/renderers/vue3/package.json +++ b/code/renderers/vue3/package.json @@ -44,7 +44,7 @@ ], "scripts": { "check": "vue-tsc --noEmit", - "prep": "../../../scripts/prepare/bundle.ts" + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/core-client": "workspace:*", diff --git a/code/renderers/web-components/package.json b/code/renderers/web-components/package.json index a12abcc60075..14485f6a3ddd 100644 --- a/code/renderers/web-components/package.json +++ b/code/renderers/web-components/package.json @@ -46,8 +46,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/client-logger": "workspace:*", diff --git a/code/ui/.storybook/main.ts b/code/ui/.storybook/main.ts index 347f21929530..4800dda1e181 100644 --- a/code/ui/.storybook/main.ts +++ b/code/ui/.storybook/main.ts @@ -8,6 +8,7 @@ const isBlocksOnly = process.env.STORYBOOK_BLOCKS_ONLY === 'true'; const allStories = [ { directory: '../manager/src', + files: '**/*.stories.@(js|jsx|mjs|ts|tsx|mdx)', titlePrefix: '@manager', }, { @@ -53,6 +54,11 @@ const config: StorybookConfig = { '@storybook/addon-designs', '@chromaui/addon-visual-tests', ], + build: { + test: { + disableBlocks: false, + }, + }, framework: { name: '@storybook/react-vite', options: {}, diff --git a/code/ui/blocks/package.json b/code/ui/blocks/package.json index 2b4df74d9756..b2dbcfb882f7 100644 --- a/code/ui/blocks/package.json +++ b/code/ui/blocks/package.json @@ -40,15 +40,15 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@storybook/channels": "workspace:*", "@storybook/client-logger": "workspace:*", "@storybook/components": "workspace:*", "@storybook/core-events": "workspace:*", - "@storybook/csf": "^0.1.0", + "@storybook/csf": "^0.1.2", "@storybook/docs-tools": "workspace:*", "@storybook/global": "^5.0.0", "@storybook/manager-api": "workspace:*", diff --git a/code/ui/components/package.json b/code/ui/components/package.json index d69c914f3d05..9954c55392ef 100644 --- a/code/ui/components/package.json +++ b/code/ui/components/package.json @@ -55,14 +55,14 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { "@radix-ui/react-select": "^1.2.2", "@radix-ui/react-toolbar": "^1.0.4", "@storybook/client-logger": "workspace:*", - "@storybook/csf": "^0.1.0", + "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", "@storybook/theming": "workspace:*", "@storybook/types": "workspace:*", diff --git a/code/ui/components/src/components/typography/DocumentFormattingSample.mdx b/code/ui/components/src/components/typography/DocumentFormattingSample.mdx deleted file mode 100644 index 65b5e6fbe2ce..000000000000 --- a/code/ui/components/src/components/typography/DocumentFormattingSample.mdx +++ /dev/null @@ -1,143 +0,0 @@ -# h1 Heading - -## h2 Heading - -### h3 Heading - -#### h4 Heading - -##### h5 Heading - -###### h6 Heading - -## Typographic replacements - -Enable typographer option to see result. - -(c) (C) (r) (R) (tm) (TM) (p) (P) +- - -test.. test... test..... test?..... test!.... - -!!!!!! ???? ,, -- --- - -"Smartypants, double quotes" and 'single quotes' - -## Emphasis - -**This is bold text** - -**This is bold text** - -_This is italic text_ - -_This is italic text_ - -_**This is bold italic text**_ - -~~Strikethrough~~ - -## Blockquotes - -> Blockquotes can also be nested... -> -> > ...by using additional greater-than signs right next to each other... -> > -> > > ...or with spaces between arrows. - -## Lists - -Unordered - -- Create a list by starting a line with `+`, `-`, or `*` -- Sub-lists are made by indenting 2 spaces: - - Marker character change forces new list start: - - Ac tristique libero volutpat at - * Facilisis in pretium nisl aliquet - - Nulla volutpat aliquam velit -- Hooray! - -Ordered - -1. Lorem ipsum dolor sit amet -2. Consectetur adipiscing elit -3. Integer molestie lorem at massa - -1) You can use sequential numbers... -1) ...or keep all the numbers as `1.` - -Start numbering with offset: - -57. foo -1. bar - -## Code - -Inline `code` - -Indented code - - // Some comments - line 1 of code - line 2 of code - line 3 of code - -Block code "fences" - -``` -Sample text here... -``` - -Syntax highlighting - -```js -var foo = function (bar) { - return bar++; -}; - -console.log(foo(5)); -``` - -## Horizontal Rule - ---- - -## Tables - -| Option | Description | -| ------ | ------------------------------------------------------------------------- | -| data | path to data files to supply the data that will be passed into templates. | -| engine | engine to be used for processing templates. Handlebars is the default. | -| ext | extension to be used for dest files. | - -Right aligned columns - -| Option | Description | -| -----: | ------------------------------------------------------------------------: | -| data | path to data files to supply the data that will be passed into templates. | -| engine | engine to be used for processing templates. Handlebars is the default. | -| ext | extension to be used for dest files. | - -## Links - -[link text](http://dev.nodeca.com) - -[link with title](http://nodeca.github.io/pica/demo/ 'title text!') - -Autoconverted link https://github.com/nodeca/pica (enable linkify to see) - -# [Link](https://storybook.js.org/) in heading - -## [Link](https://storybook.js.org/) in heading - -### [Link](https://storybook.js.org/) in heading - -#### [Link](https://storybook.js.org/) in heading - -##### [Link](https://storybook.js.org/) in heading - -###### [Link](https://storybook.js.org/) in heading - -## Images - -![Minion](https://octodex.github.com/images/minion.png) -![Stormtroopocat](https://octodex.github.com/images/stormtroopocat.jpg 'The Stormtroopocat') diff --git a/code/ui/components/src/components/typography/DocumentWrapper.stories.tsx b/code/ui/components/src/components/typography/DocumentWrapper.stories.tsx index f11de8ede5f5..4a886c31bc40 100644 --- a/code/ui/components/src/components/typography/DocumentWrapper.stories.tsx +++ b/code/ui/components/src/components/typography/DocumentWrapper.stories.tsx @@ -1,19 +1,12 @@ import React from 'react'; import { DocumentWrapper } from './DocumentWrapper'; -import MarkdownSample from './DocumentFormattingSample.mdx'; export default { component: DocumentWrapper, decorators: [(storyFn: any) =>
{storyFn()}
], }; -export const WithMarkdown = () => ( - - - -); - export const WithDOM = () => (

h1 Heading

diff --git a/code/ui/manager/package.json b/code/ui/manager/package.json index 6bbd3c58949e..cd74544bcc15 100644 --- a/code/ui/manager/package.json +++ b/code/ui/manager/package.json @@ -24,12 +24,17 @@ "types": "./dist/index.d.ts", "import": "./dist/index.js" }, - "./dist/runtime": { + "./runtime": { "types": "./dist/runtime.d.ts", "import": "./dist/runtime.js" }, - "./dist/globals": { + "./globals-module-info": { + "types": "./dist/globals-module-info.d.ts", + "require": "./dist/globals-module-info.js" + }, + "./globals": { "types": "./dist/globals.d.ts", + "import": "./dist/globals.js", "require": "./dist/globals.js" }, "./paths": "./paths.js", @@ -38,6 +43,22 @@ "main": "dist/index.js", "module": "dist/index.js", "types": "dist/index.d.ts", + "typesVersions": { + "*": { + "*": [ + "dist/index.d.ts" + ], + "runtime": [ + "dist/runtime.d.ts" + ], + "globals": [ + "dist/globals.d.ts" + ], + "globals-module-info": [ + "dist/globals-module-info.d.ts" + ] + } + }, "files": [ "dist/**/*", "static/**/*", @@ -47,8 +68,8 @@ "!src/**/*" ], "scripts": { - "check": "../../../scripts/prepare/check.ts", - "prep": "../../../scripts/prepare/esm-bundle.ts" + "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", + "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/esm-bundle.ts" }, "devDependencies": { "@fal-works/esbuild-plugin-global-externals": "^2.1.2", @@ -97,7 +118,8 @@ "./src/runtime.ts" ], "nodeEntries": [ - "./src/globals.ts" + "./src/globals.ts", + "./src/globals-module-info.ts" ] }, "gitHead": "e6a7fd8a655c69780bc20b9749c2699e44beae17" diff --git a/code/ui/manager/scripts/generate-exports-file.ts b/code/ui/manager/scripts/generate-exports-file.ts index afdbea8ff6e6..ece5ac50391b 100644 --- a/code/ui/manager/scripts/generate-exports-file.ts +++ b/code/ui/manager/scripts/generate-exports-file.ts @@ -3,7 +3,7 @@ import fs from 'fs-extra'; import path from 'path'; import { dedent } from 'ts-dedent'; import { ESLint } from '../../../../scripts/node_modules/eslint'; -import { values } from '../src/globals/runtime'; +import { globalsNameValueMap } from '../src/globals/runtime'; const location = path.join(__dirname, '..', 'src', 'globals', 'exports.ts'); let attempts = 0; @@ -29,10 +29,13 @@ async function generate(text: string) { } const run = async () => { - const data = Object.entries(values).reduce>((acc, [key, value]) => { - acc[key] = Object.keys(value).filter(removeDefault); - return acc; - }, {}); + const data = Object.entries(globalsNameValueMap).reduce>( + (acc, [key, value]) => { + acc[key] = Object.keys(value).filter(removeDefault); + return acc; + }, + {} + ); console.log('Generating...'); diff --git a/code/ui/manager/src/globals-module-info.ts b/code/ui/manager/src/globals-module-info.ts new file mode 100644 index 000000000000..4bcbf259af79 --- /dev/null +++ b/code/ui/manager/src/globals-module-info.ts @@ -0,0 +1 @@ +export * from './globals/globals-module-info'; diff --git a/code/ui/manager/src/globals.ts b/code/ui/manager/src/globals.ts index d516acc55516..3b9b2321b6f0 100644 --- a/code/ui/manager/src/globals.ts +++ b/code/ui/manager/src/globals.ts @@ -1 +1 @@ -export * from './globals/definitions'; +export * from './globals/globals'; diff --git a/code/ui/manager/src/globals/exports.ts b/code/ui/manager/src/globals/exports.ts index ef2523f0e06a..f56ba153dd82 100644 --- a/code/ui/manager/src/globals/exports.ts +++ b/code/ui/manager/src/globals/exports.ts @@ -278,4 +278,5 @@ export default { ], '@storybook/addons': ['addons', 'types', 'mockChannel'], '@storybook/client-logger': ['deprecate', 'logger', 'once', 'pretty'], + '@storybook/types': ['Addon_TypesEnum'], } as const; diff --git a/code/ui/manager/src/globals/definitions.ts b/code/ui/manager/src/globals/globals-module-info.ts similarity index 65% rename from code/ui/manager/src/globals/definitions.ts rename to code/ui/manager/src/globals/globals-module-info.ts index 9122fca70262..616148e80ca4 100644 --- a/code/ui/manager/src/globals/definitions.ts +++ b/code/ui/manager/src/globals/globals-module-info.ts @@ -1,7 +1,6 @@ import type { ModuleInfo } from '@fal-works/esbuild-plugin-global-externals'; import Exports from './exports'; -import { Keys } from './types'; -import type { Definitions } from './types'; +import { globalPackages, globalsNameReferenceMap } from './globals'; /* * We create a map of a module's name to a ModuleInfo. @@ -18,18 +17,16 @@ import type { Definitions } from './types'; * * If you forget to do either, TypeScript will complain. * - * This `definitions.ts` file is consumed by the `builder-manager` package, + * This `globals-module-info.ts` file is consumed by the `builder-manager` package, * The `runtime.ts` file is used inside the manager's browser code runtime. */ -const createModuleInfo = (m: keyof typeof Keys): Required => ({ - type: 'esm', - varName: Keys[m], - namedExports: Exports[m], - defaultExport: true, -}); - -export const definitions = Object.keys(Keys).reduce((acc, key) => { - acc[key as keyof typeof Keys] = createModuleInfo(key as keyof typeof Keys); +export const globalsModuleInfoMap = globalPackages.reduce((acc, key) => { + acc[key] = { + type: 'esm', + varName: globalsNameReferenceMap[key], + namedExports: Exports[key], + defaultExport: true, + }; return acc; -}, {} as Definitions); +}, {} as Required>>); diff --git a/code/ui/manager/src/globals/globals.ts b/code/ui/manager/src/globals/globals.ts new file mode 100644 index 000000000000..1ef93c38bc94 --- /dev/null +++ b/code/ui/manager/src/globals/globals.ts @@ -0,0 +1,19 @@ +// Here we map the name of a module to their REFERENCE in the global scope. +export const globalsNameReferenceMap = { + react: '__REACT__', + 'react-dom': '__REACT_DOM__', + '@storybook/components': '__STORYBOOK_COMPONENTS__', + '@storybook/channels': '__STORYBOOK_CHANNELS__', + '@storybook/core-events': '__STORYBOOK_CORE_EVENTS__', + '@storybook/router': '__STORYBOOK_ROUTER__', + '@storybook/theming': '__STORYBOOK_THEMING__', + '@storybook/api': '__STORYBOOK_API__', // deprecated, remove in 8.0 + '@storybook/manager-api': '__STORYBOOK_API__', + '@storybook/addons': '__STORYBOOK_ADDONS__', + '@storybook/client-logger': '__STORYBOOK_CLIENT_LOGGER__', + '@storybook/types': '__STORYBOOK_TYPES__', +} as const; + +export const globalPackages = Object.keys(globalsNameReferenceMap) as Array< + keyof typeof globalsNameReferenceMap +>; diff --git a/code/ui/manager/src/globals/runtime.ts b/code/ui/manager/src/globals/runtime.ts index 5850699f171d..59f5f8fc1a09 100644 --- a/code/ui/manager/src/globals/runtime.ts +++ b/code/ui/manager/src/globals/runtime.ts @@ -1,32 +1,34 @@ import * as REACT from 'react'; -import * as REACTDOM from 'react-dom'; +import * as REACT_DOM from 'react-dom'; -import * as STORYBOOKCOMPONENTS from '@storybook/components'; -import * as STORYBOOKCHANNELS from '@storybook/channels'; -import * as STORYBOOKEVENTS from '@storybook/core-events'; -import * as STORYBOOKROUTER from '@storybook/router'; -import * as STORYBOOKTHEMING from '@storybook/theming'; -import * as STORYBOOKMANAGERAPI from '@storybook/manager-api'; -import * as STORYBOOKCLIENTLOGGER from '@storybook/client-logger'; +import * as COMPONENTS from '@storybook/components'; +import * as CHANNELS from '@storybook/channels'; +import * as EVENTS from '@storybook/core-events'; +import * as ROUTER from '@storybook/router'; +import * as THEMING from '@storybook/theming'; +import * as MANAGER_API from '@storybook/manager-api'; +import * as TYPES from '@storybook/types'; +import * as CLIENT_LOGGER from '@storybook/client-logger'; -import type { Keys } from './types'; +import type { globalsNameReferenceMap } from './globals'; // Here we map the name of a module to their VALUE in the global scope. -export const values: Required> = { - react: REACT as any, - 'react-dom': REACTDOM, - '@storybook/components': STORYBOOKCOMPONENTS, - '@storybook/channels': STORYBOOKCHANNELS, - '@storybook/core-events': STORYBOOKEVENTS, - '@storybook/router': STORYBOOKROUTER, - '@storybook/theming': STORYBOOKTHEMING, - '@storybook/api': STORYBOOKMANAGERAPI, // deprecated, remove in 8.0 - '@storybook/manager-api': STORYBOOKMANAGERAPI, +export const globalsNameValueMap: Required> = { + react: REACT, + 'react-dom': REACT_DOM, + '@storybook/components': COMPONENTS, + '@storybook/channels': CHANNELS, + '@storybook/core-events': EVENTS, + '@storybook/router': ROUTER, + '@storybook/theming': THEMING, + '@storybook/api': MANAGER_API, // deprecated, remove in 8.0 + '@storybook/manager-api': MANAGER_API, // backwards compatibility '@storybook/addons': { - addons: STORYBOOKMANAGERAPI.addons, - types: STORYBOOKMANAGERAPI.types, - mockChannel: STORYBOOKMANAGERAPI.mockChannel, + addons: MANAGER_API.addons, + types: MANAGER_API.types, + mockChannel: MANAGER_API.mockChannel, }, - '@storybook/client-logger': STORYBOOKCLIENTLOGGER, + '@storybook/client-logger': CLIENT_LOGGER, + '@storybook/types': TYPES, }; diff --git a/code/ui/manager/src/globals/types.ts b/code/ui/manager/src/globals/types.ts deleted file mode 100644 index 2861e45f632a..000000000000 --- a/code/ui/manager/src/globals/types.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { ModuleInfo } from '@fal-works/esbuild-plugin-global-externals'; - -// Here we map the name of a module to their NAME in the global scope. -export enum Keys { - 'react' = '__REACT__', - 'react-dom' = '__REACTDOM__', - '@storybook/components' = '__STORYBOOKCOMPONENTS__', - '@storybook/channels' = '__STORYBOOKCHANNELS__', - '@storybook/core-events' = '__STORYBOOKCOREEVENTS__', - '@storybook/router' = '__STORYBOOKROUTER__', - '@storybook/theming' = '__STORYBOOKTHEMING__', - '@storybook/api' = '__STORYBOOKAPI__', // deprecated, remove in 8.0 - '@storybook/manager-api' = '__STORYBOOKAPI__', - '@storybook/addons' = '__STORYBOOKADDONS__', - '@storybook/client-logger' = '__STORYBOOKCLIENTLOGGER__', -} - -export type Definitions = Required>>; diff --git a/code/ui/manager/src/runtime.ts b/code/ui/manager/src/runtime.ts index efa348ac47e0..f66bde4de3d3 100644 --- a/code/ui/manager/src/runtime.ts +++ b/code/ui/manager/src/runtime.ts @@ -9,8 +9,8 @@ import { CHANNEL_CREATED, TELEMETRY_ERROR } from '@storybook/core-events'; import Provider from './provider'; import { renderStorybookUI } from './index'; -import { values } from './globals/runtime'; -import { Keys } from './globals/types'; +import { globalsNameValueMap } from './globals/runtime'; +import { globalPackages, globalsNameReferenceMap } from './globals/globals'; import { prepareForTelemetry, shouldSkipError } from './utils/prepareForTelemetry'; const { FEATURES, CONFIG_TYPE } = global; @@ -58,8 +58,8 @@ class ReactProvider extends Provider { } // Apply all the globals -Object.keys(Keys).forEach((key: keyof typeof Keys) => { - global[Keys[key]] = values[key]; +globalPackages.forEach((key) => { + global[globalsNameReferenceMap[key]] = globalsNameValueMap[key]; }); global.sendTelemetryError = (error) => { diff --git a/code/ui/manager/src/typings.d.ts b/code/ui/manager/src/typings.d.ts index 2ff3df07e63e..bce29ea466a7 100644 --- a/code/ui/manager/src/typings.d.ts +++ b/code/ui/manager/src/typings.d.ts @@ -15,15 +15,15 @@ declare var VERSIONCHECK: any; declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; declare var __REACT__: any; -declare var __REACTDOM__: any; -declare var __STORYBOOKCOMPONENTS__: any; -declare var __STORYBOOKCOMPONENTSEXPERIMENTAL__: any; -declare var __STORYBOOKCHANNELS__: any; -declare var __STORYBOOKCOREEVENTS__: any; -declare var __STORYBOOKROUTER__: any; -declare var __STORYBOOKTHEMING__: any; -declare var __STORYBOOKAPI__: any; -declare var __STORYBOOKADDONS__: any; -declare var __STORYBOOKCLIENTLOGGER__: any; +declare var __REACT_DOM__: any; +declare var __STORYBOOK_COMPONENTS__: any; +declare var __STORYBOOK_CHANNELS__: any; +declare var __STORYBOOK_CORE_EVENTS__: any; +declare var __STORYBOOK_ROUTER__: any; +declare var __STORYBOOK_THEMING__: any; +declare var __STORYBOOK_API__: any; +declare var __STORYBOOK_ADDONS__: any; +declare var __STORYBOOK_CLIENT_LOGGER__: any; declare var __STORYBOOK_ADDONS_CHANNEL__: any; +declare var __STORYBOOK_TYPES__: any; declare var sendTelemetryError: (error: any) => void; diff --git a/code/yarn.lock b/code/yarn.lock index 95a491aaf8ad..44f7f1428778 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -612,6 +612,21 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-transforms@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/helper-module-transforms@npm:7.23.3" + dependencies: + "@babel/helper-environment-visitor": "npm:^7.22.20" + "@babel/helper-module-imports": "npm:^7.22.15" + "@babel/helper-simple-access": "npm:^7.22.5" + "@babel/helper-split-export-declaration": "npm:^7.22.6" + "@babel/helper-validator-identifier": "npm:^7.22.20" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 211e1399d0c4993671e8e5c2b25383f08bee40004ace5404ed4065f0e9258cc85d99c1b82fd456c030ce5cfd4d8f310355b54ef35de9924eabfc3dff1331d946 + languageName: node + linkType: hard + "@babel/helper-optimise-call-expression@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-optimise-call-expression@npm:7.22.5" @@ -1024,6 +1039,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-jsx@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-syntax-jsx@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 563bb7599b868773f1c7c1d441ecc9bc53aeb7832775da36752c926fc402a1fa5421505b39e724f71eb217c13e4b93117e081cac39723b0e11dac4c897f33c3e + languageName: node + linkType: hard + "@babel/plugin-syntax-logical-assignment-operators@npm:^7.10.4, @babel/plugin-syntax-logical-assignment-operators@npm:^7.8.3": version: 7.10.4 resolution: "@babel/plugin-syntax-logical-assignment-operators@npm:7.10.4" @@ -1123,6 +1149,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-typescript@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-syntax-typescript@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 4d6e9cdb9d0bfb9bd9b220fc951d937fce2ca69135ec121153572cebe81d86abc9a489208d6b69ee5f10cadcaeffa10d0425340a5029e40e14a6025021b90948 + languageName: node + linkType: hard + "@babel/plugin-syntax-unicode-sets-regex@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-syntax-unicode-sets-regex@npm:7.18.6" @@ -1442,6 +1479,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-modules-commonjs@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.23.3" + dependencies: + "@babel/helper-module-transforms": "npm:^7.23.3" + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-simple-access": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 5c8840c5c9ecba39367ae17c973ed13dbc43234147b77ae780eec65010e2a9993c5d717721b23e8179f7cf49decdd325c509b241d69cfbf92aa647a1d8d5a37d + languageName: node + linkType: hard + "@babel/plugin-transform-modules-systemjs@npm:^7.22.11, @babel/plugin-transform-modules-systemjs@npm:^7.22.5, @babel/plugin-transform-modules-systemjs@npm:^7.23.0": version: 7.23.0 resolution: "@babel/plugin-transform-modules-systemjs@npm:7.23.0" @@ -1637,7 +1687,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-react-jsx-development@npm:^7.16.7, @babel/plugin-transform-react-jsx-development@npm:^7.18.6, @babel/plugin-transform-react-jsx-development@npm:^7.22.5": +"@babel/plugin-transform-react-jsx-development@npm:^7.16.7, @babel/plugin-transform-react-jsx-development@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-react-jsx-development@npm:7.22.5" dependencies: @@ -1670,7 +1720,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-react-jsx@npm:^7.14.9, @babel/plugin-transform-react-jsx@npm:^7.19.0, @babel/plugin-transform-react-jsx@npm:^7.22.15, @babel/plugin-transform-react-jsx@npm:^7.22.5": +"@babel/plugin-transform-react-jsx@npm:^7.14.9, @babel/plugin-transform-react-jsx@npm:^7.22.15, @babel/plugin-transform-react-jsx@npm:^7.22.5": version: 7.22.15 resolution: "@babel/plugin-transform-react-jsx@npm:7.22.15" dependencies: @@ -1838,6 +1888,20 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-typescript@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-typescript@npm:7.23.3" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.22.5" + "@babel/helper-create-class-features-plugin": "npm:^7.22.15" + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/plugin-syntax-typescript": "npm:^7.23.3" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: a3c738efcf491ceb1eee646f57c44990ee0c80465527b88fcfa0b7602688c4ff8c165a4c5b62caf05d968b095212018fd30a02879c12d37c657081f57b31fb26 + languageName: node + linkType: hard + "@babel/plugin-transform-unicode-escapes@npm:^7.22.10, @babel/plugin-transform-unicode-escapes@npm:^7.22.5": version: 7.22.10 resolution: "@babel/plugin-transform-unicode-escapes@npm:7.22.10" @@ -2237,6 +2301,21 @@ __metadata: languageName: node linkType: hard +"@babel/preset-typescript@npm:^7.23.0": + version: 7.23.3 + resolution: "@babel/preset-typescript@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-validator-option": "npm:^7.22.15" + "@babel/plugin-syntax-jsx": "npm:^7.23.3" + "@babel/plugin-transform-modules-commonjs": "npm:^7.23.3" + "@babel/plugin-transform-typescript": "npm:^7.23.3" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: e72b654c7f0f08b35d7e1c0e3a59c0c13037f295c425760b8b148aa7dde01e6ddd982efc525710f997a1494fafdd55cb525738c016609e7e4d703d02014152b7 + languageName: node + linkType: hard + "@babel/preset-typescript@npm:^7.23.2": version: 7.23.2 resolution: "@babel/preset-typescript@npm:7.23.2" @@ -2252,7 +2331,7 @@ __metadata: languageName: node linkType: hard -"@babel/register@npm:^7.13.16": +"@babel/register@npm:^7.13.16, @babel/register@npm:^7.22.15": version: 7.22.15 resolution: "@babel/register@npm:7.22.15" dependencies: @@ -3952,72 +4031,72 @@ __metadata: languageName: node linkType: hard -"@next/env@npm:14.0.0": - version: 14.0.0 - resolution: "@next/env@npm:14.0.0" - checksum: c43e81dbd162a29a4b380342e416209d69d731e8ced7688d09668ec8196f543e358ed65adad81a26e943c63a293d7a018552f8389b6b1ac95cd0f63f4ef257c0 +"@next/env@npm:14.0.2": + version: 14.0.2 + resolution: "@next/env@npm:14.0.2" + checksum: 9fad703ce13b7b7fecf898d3c239f8976f2ec7f3c7c461c06da70898a0221775c48e1a2e2c76740216c4093c2db9bd7adaacd196586cd4283e09eb89de4c1db6 languageName: node linkType: hard -"@next/swc-darwin-arm64@npm:14.0.0": - version: 14.0.0 - resolution: "@next/swc-darwin-arm64@npm:14.0.0" +"@next/swc-darwin-arm64@npm:14.0.2": + version: 14.0.2 + resolution: "@next/swc-darwin-arm64@npm:14.0.2" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@next/swc-darwin-x64@npm:14.0.0": - version: 14.0.0 - resolution: "@next/swc-darwin-x64@npm:14.0.0" +"@next/swc-darwin-x64@npm:14.0.2": + version: 14.0.2 + resolution: "@next/swc-darwin-x64@npm:14.0.2" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@next/swc-linux-arm64-gnu@npm:14.0.0": - version: 14.0.0 - resolution: "@next/swc-linux-arm64-gnu@npm:14.0.0" +"@next/swc-linux-arm64-gnu@npm:14.0.2": + version: 14.0.2 + resolution: "@next/swc-linux-arm64-gnu@npm:14.0.2" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@next/swc-linux-arm64-musl@npm:14.0.0": - version: 14.0.0 - resolution: "@next/swc-linux-arm64-musl@npm:14.0.0" +"@next/swc-linux-arm64-musl@npm:14.0.2": + version: 14.0.2 + resolution: "@next/swc-linux-arm64-musl@npm:14.0.2" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@next/swc-linux-x64-gnu@npm:14.0.0": - version: 14.0.0 - resolution: "@next/swc-linux-x64-gnu@npm:14.0.0" +"@next/swc-linux-x64-gnu@npm:14.0.2": + version: 14.0.2 + resolution: "@next/swc-linux-x64-gnu@npm:14.0.2" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@next/swc-linux-x64-musl@npm:14.0.0": - version: 14.0.0 - resolution: "@next/swc-linux-x64-musl@npm:14.0.0" +"@next/swc-linux-x64-musl@npm:14.0.2": + version: 14.0.2 + resolution: "@next/swc-linux-x64-musl@npm:14.0.2" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@next/swc-win32-arm64-msvc@npm:14.0.0": - version: 14.0.0 - resolution: "@next/swc-win32-arm64-msvc@npm:14.0.0" +"@next/swc-win32-arm64-msvc@npm:14.0.2": + version: 14.0.2 + resolution: "@next/swc-win32-arm64-msvc@npm:14.0.2" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@next/swc-win32-ia32-msvc@npm:14.0.0": - version: 14.0.0 - resolution: "@next/swc-win32-ia32-msvc@npm:14.0.0" +"@next/swc-win32-ia32-msvc@npm:14.0.2": + version: 14.0.2 + resolution: "@next/swc-win32-ia32-msvc@npm:14.0.2" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@next/swc-win32-x64-msvc@npm:14.0.0": - version: 14.0.0 - resolution: "@next/swc-win32-x64-msvc@npm:14.0.0" +"@next/swc-win32-x64-msvc@npm:14.0.2": + version: 14.0.2 + resolution: "@next/swc-win32-x64-msvc@npm:14.0.2" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -5347,7 +5426,6 @@ __metadata: "@storybook/channels": "workspace:*" "@storybook/client-logger": "workspace:*" "@storybook/components": "workspace:*" - "@storybook/core-events": "workspace:*" "@storybook/global": "npm:^5.0.0" "@storybook/manager-api": "workspace:*" "@storybook/preview-api": "workspace:*" @@ -5356,17 +5434,11 @@ __metadata: "@testing-library/react": "npm:^11.2.2" axe-core: "npm:^4.2.0" lodash: "npm:^4.17.21" + react: "npm:^16.8.0" + react-dom: "npm:^16.8.0" react-resize-detector: "npm:^7.1.2" resize-observer-polyfill: "npm:^1.5.1" typescript: "npm:~4.9.3" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true languageName: unknown linkType: soft @@ -5382,25 +5454,15 @@ __metadata: "@storybook/preview-api": "workspace:*" "@storybook/theming": "workspace:*" "@storybook/types": "workspace:*" - "@types/lodash": "npm:^4.14.167" "@types/uuid": "npm:^9.0.1" dequal: "npm:^2.0.2" - lodash: "npm:^4.17.21" polished: "npm:^4.2.2" - prop-types: "npm:^15.7.2" + react: "npm:^16.8.0" + react-dom: "npm:^16.8.0" react-inspector: "npm:^6.0.0" telejson: "npm:^7.2.0" - ts-dedent: "npm:^2.0.0" typescript: "npm:~4.9.3" uuid: "npm:^9.0.0" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true languageName: unknown linkType: soft @@ -5410,23 +5472,16 @@ __metadata: dependencies: "@storybook/client-logger": "workspace:*" "@storybook/components": "workspace:*" - "@storybook/core-events": "workspace:*" "@storybook/global": "npm:^5.0.0" "@storybook/manager-api": "workspace:*" "@storybook/preview-api": "workspace:*" "@storybook/theming": "workspace:*" "@storybook/types": "workspace:*" memoizerific: "npm:^1.11.3" + react: "npm:^16.8.0" + react-dom: "npm:^16.8.0" ts-dedent: "npm:^2.0.0" typescript: "npm:~4.9.3" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true languageName: unknown linkType: soft @@ -5438,22 +5493,15 @@ __metadata: "@storybook/client-logger": "workspace:*" "@storybook/components": "workspace:*" "@storybook/core-common": "workspace:*" - "@storybook/core-events": "workspace:*" "@storybook/manager-api": "workspace:*" "@storybook/node-logger": "workspace:*" "@storybook/preview-api": "workspace:*" "@storybook/theming": "workspace:*" "@storybook/types": "workspace:*" lodash: "npm:^4.17.21" + react: "npm:^16.8.0" + react-dom: "npm:^16.8.0" ts-dedent: "npm:^2.0.0" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true languageName: unknown linkType: soft @@ -5573,16 +5621,10 @@ __metadata: formik: "npm:^2.2.9" jest-mock: "npm:^27.0.6" polished: "npm:^4.2.2" + react: "npm:^16.8.0" + react-dom: "npm:^16.8.0" ts-dedent: "npm:^2.2.0" typescript: "npm:~4.9.3" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true languageName: unknown linkType: soft @@ -5597,18 +5639,13 @@ __metadata: "@storybook/manager-api": "workspace:*" "@storybook/preview-api": "workspace:*" "@storybook/theming": "workspace:*" + react: "npm:^16.8.0" + react-dom: "npm:^16.8.0" react-resize-detector: "npm:^7.1.2" tiny-invariant: "npm:^1.3.1" + ts-dedent: "npm:^2.0.0" typescript: "npm:~4.9.3" upath: "npm:^2.0.1" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true languageName: unknown linkType: soft @@ -5618,24 +5655,20 @@ __metadata: dependencies: "@storybook/client-logger": "workspace:*" "@storybook/core-events": "workspace:*" - "@storybook/csf": "npm:^0.1.0" + "@storybook/csf": "npm:^0.1.2" "@storybook/global": "npm:^5.0.0" "@storybook/manager-api": "workspace:*" "@storybook/preview-api": "workspace:*" "@storybook/router": "workspace:*" "@storybook/types": "workspace:*" fs-extra: "npm:^11.1.0" - prop-types: "npm:^15.7.2" ts-dedent: "npm:^2.0.0" typescript: "npm:~4.9.3" peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 peerDependenciesMeta: react: optional: true - react-dom: - optional: true languageName: unknown linkType: soft @@ -5661,16 +5694,10 @@ __metadata: "@storybook/manager-api": "workspace:*" "@storybook/preview-api": "workspace:*" "@storybook/types": "workspace:*" + react: "npm:^16.8.0" + react-dom: "npm:^16.8.0" tiny-invariant: "npm:^1.3.1" typescript: "npm:~4.9.3" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true languageName: unknown linkType: soft @@ -5685,16 +5712,10 @@ __metadata: "@storybook/manager-api": "workspace:*" "@storybook/preview-api": "workspace:*" "@storybook/types": "workspace:*" + react: "npm:^16.8.0" + react-dom: "npm:^16.8.0" ts-dedent: "npm:^2.0.0" typescript: "npm:~4.9.3" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true languageName: unknown linkType: soft @@ -5703,7 +5724,7 @@ __metadata: resolution: "@storybook/addon-storyshots-puppeteer@workspace:addons/storyshots-puppeteer" dependencies: "@axe-core/puppeteer": "npm:^4.2.0" - "@storybook/csf": "npm:^0.1.0" + "@storybook/csf": "npm:^0.1.2" "@storybook/node-logger": "workspace:*" "@storybook/types": "workspace:*" "@types/jest-image-snapshot": "npm:^6.0.0" @@ -5822,18 +5843,11 @@ __metadata: "@types/react": "npm:^16.14.34" "@types/react-syntax-highlighter": "npm:11.0.5" estraverse: "npm:^5.2.0" - prop-types: "npm:^15.7.2" + react: "npm:^16.8.0" + react-dom: "npm:^16.8.0" react-syntax-highlighter: "npm:^15.5.0" tiny-invariant: "npm:^1.3.1" typescript: "npm:~4.9.3" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true languageName: unknown linkType: soft @@ -5850,14 +5864,6 @@ __metadata: "@storybook/types": "workspace:*" ts-dedent: "npm:^2.0.0" typescript: "npm:~4.9.3" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true languageName: unknown linkType: soft @@ -5870,15 +5876,9 @@ __metadata: "@storybook/manager-api": "workspace:*" "@storybook/preview-api": "workspace:*" "@storybook/theming": "workspace:*" + react: "npm:^16.8.0" + react-dom: "npm:^16.8.0" typescript: "npm:~4.9.3" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true languageName: unknown linkType: soft @@ -5894,16 +5894,9 @@ __metadata: "@storybook/preview-api": "workspace:*" "@storybook/theming": "workspace:*" memoizerific: "npm:^1.11.3" - prop-types: "npm:^15.7.2" + react: "npm:^16.8.0" + react-dom: "npm:^16.8.0" typescript: "npm:~4.9.3" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true languageName: unknown linkType: soft @@ -5914,9 +5907,6 @@ __metadata: "@storybook/manager-api": "workspace:*" "@storybook/preview-api": "workspace:*" "@storybook/types": "workspace:*" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 languageName: unknown linkType: soft @@ -5945,7 +5935,6 @@ __metadata: "@storybook/core-webpack": "workspace:*" "@storybook/docs-tools": "workspace:*" "@storybook/global": "npm:^5.0.0" - "@storybook/manager-api": "workspace:*" "@storybook/node-logger": "workspace:*" "@storybook/preview-api": "workspace:*" "@storybook/telemetry": "workspace:*" @@ -5985,8 +5974,6 @@ __metadata: "@angular/platform-browser": ">=14.1.0 < 18.0.0" "@angular/platform-browser-dynamic": ">=14.1.0 < 18.0.0" "@babel/core": "*" - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 rxjs: ^6.0.0 || ^7.4.0 typescript: ^4.0.0 || ^5.0.0 zone.js: ">= 0.11.1 < 1.0.0" @@ -6002,14 +5989,6 @@ __metadata: dependencies: "@storybook/client-logger": "workspace:*" "@storybook/manager-api": "workspace:*" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true languageName: unknown linkType: soft @@ -6054,7 +6033,7 @@ __metadata: "@storybook/client-logger": "workspace:*" "@storybook/components": "workspace:*" "@storybook/core-events": "workspace:*" - "@storybook/csf": "npm:^0.1.0" + "@storybook/csf": "npm:^0.1.2" "@storybook/docs-tools": "workspace:*" "@storybook/global": "npm:^5.0.0" "@storybook/manager-api": "workspace:*" @@ -6272,7 +6251,7 @@ __metadata: get-port: "npm:^5.1.1" giget: "npm:^1.0.0" globby: "npm:^11.0.2" - jscodeshift: "npm:^0.14.0" + jscodeshift: "npm:^0.15.1" leven: "npm:^3.1.0" ora: "npm:^5.4.1" prettier: "npm:^2.8.0" @@ -6302,12 +6281,12 @@ __metadata: languageName: unknown linkType: soft -"@storybook/client-logger@npm:7.4.6": - version: 7.4.6 - resolution: "@storybook/client-logger@npm:7.4.6" +"@storybook/client-logger@npm:7.5.0": + version: 7.5.0 + resolution: "@storybook/client-logger@npm:7.5.0" dependencies: "@storybook/global": "npm:^5.0.0" - checksum: 170ad58c17e2608639533fe24aaa96ddd4d77d23b4b28f265b2cb67510fef966fc20b029e070fdc7216ba1cdb724d1210b2f8edc8aa538de32fd6e549f9010cf + checksum: 90326c49a224bf21680c04ffee94725bf75658086093ccb839a8aae39476929c4719eafb18e498a148cf0dd956d4e9a5d3b2a34d09ca4fd25e2af553458558ac languageName: node linkType: hard @@ -6327,18 +6306,18 @@ __metadata: "@babel/core": "npm:^7.23.2" "@babel/preset-env": "npm:^7.23.2" "@babel/types": "npm:^7.23.0" - "@storybook/csf": "npm:^0.1.0" + "@storybook/csf": "npm:^0.1.2" "@storybook/csf-tools": "workspace:*" "@storybook/node-logger": "workspace:*" "@storybook/types": "workspace:*" "@types/cross-spawn": "npm:^6.0.2" - "@types/jscodeshift": "npm:^0.11.6" + "@types/jscodeshift": "npm:^0.11.10" ansi-regex: "npm:^5.0.1" cross-spawn: "npm:^7.0.3" globby: "npm:^11.0.2" jest: "npm:^29.7.0" jest-specific-snapshot: "npm:^8.0.0" - jscodeshift: "npm:^0.14.0" + jscodeshift: "npm:^0.15.1" lodash: "npm:^4.17.21" mdast-util-mdx-jsx: "npm:^2.1.2" mdast-util-mdxjs-esm: "npm:^1.3.1" @@ -6365,7 +6344,7 @@ __metadata: "@radix-ui/react-select": "npm:^1.2.2" "@radix-ui/react-toolbar": "npm:^1.0.4" "@storybook/client-logger": "workspace:*" - "@storybook/csf": "npm:^0.1.0" + "@storybook/csf": "npm:^0.1.2" "@storybook/global": "npm:^5.0.0" "@storybook/icons": "npm:^1.1.6" "@storybook/theming": "workspace:*" @@ -6455,7 +6434,7 @@ __metadata: "@storybook/channels": "workspace:*" "@storybook/core-common": "workspace:*" "@storybook/core-events": "workspace:*" - "@storybook/csf": "npm:^0.1.0" + "@storybook/csf": "npm:^0.1.2" "@storybook/csf-tools": "workspace:*" "@storybook/docs-mdx": "npm:^0.1.0" "@storybook/global": "npm:^5.0.0" @@ -6511,6 +6490,7 @@ __metadata: "@storybook/node-logger": "workspace:*" "@storybook/types": "workspace:*" "@types/node": "npm:^18.0.0" + slash: "npm:^5.1.0" ts-dedent: "npm:^2.0.0" typescript: "npm:~4.9.3" webpack: "npm:5" @@ -6535,7 +6515,7 @@ __metadata: "@babel/parser": "npm:^7.23.0" "@babel/traverse": "npm:^7.23.2" "@babel/types": "npm:^7.23.0" - "@storybook/csf": "npm:^0.1.0" + "@storybook/csf": "npm:^0.1.2" "@storybook/types": "workspace:*" "@types/fs-extra": "npm:^11.0.1" "@types/js-yaml": "npm:^4.0.5" @@ -6556,12 +6536,12 @@ __metadata: languageName: node linkType: hard -"@storybook/csf@npm:^0.1.0": - version: 0.1.1 - resolution: "@storybook/csf@npm:0.1.1" +"@storybook/csf@npm:^0.1.2": + version: 0.1.2 + resolution: "@storybook/csf@npm:0.1.2" dependencies: type-fest: "npm:^2.19.0" - checksum: 999bb87fbbe047a559bbaa5baf2ed84872fcd5cdcae3c1169f8e4c641eefe8759d09a09034a78ed114032c0e5cf6301b7fa89e5e3ce60d75cf0bd5e33ec0a6e7 + checksum: b51a55292e5d2af8b1d135a28ecaa94f8860ddfedcb393adfa2cca1ee23853156066f737d8be1cb5412f572781aa525dc0b2f6e4a6f6ce805489f0149efe837c languageName: node linkType: hard @@ -6636,8 +6616,6 @@ __metadata: babel-plugin-ember-modules-api-polyfill: ^2.12.0 babel-plugin-htmlbars-inline-precompile: 2 || 3 ember-source: ~3.28.1 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 languageName: unknown linkType: soft @@ -6696,8 +6674,6 @@ __metadata: typescript: "npm:~4.9.3" peerDependencies: "@babel/core": "*" - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 languageName: unknown linkType: soft @@ -6789,7 +6765,7 @@ __metadata: "@storybook/channels": "workspace:*" "@storybook/client-logger": "workspace:*" "@storybook/core-events": "workspace:*" - "@storybook/csf": "npm:^0.1.0" + "@storybook/csf": "npm:^0.1.2" "@storybook/global": "npm:^5.0.0" "@storybook/router": "workspace:*" "@storybook/theming": "workspace:*" @@ -6801,14 +6777,13 @@ __metadata: lodash: "npm:^4.17.21" memoizerific: "npm:^1.11.3" qs: "npm:^6.10.0" + react: "npm:^16.8.0" + react-dom: "npm:^16.8.0" semver: "npm:^7.3.7" store2: "npm:^2.14.2" telejson: "npm:^7.2.0" ts-dedent: "npm:^2.0.0" typescript: "npm:~4.9.3" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 languageName: unknown linkType: soft @@ -6879,10 +6854,12 @@ __metadata: "@babel/preset-typescript": "npm:^7.23.2" "@babel/runtime": "npm:^7.23.2" "@babel/types": "npm:^7.23.0" + "@pmmmwh/react-refresh-webpack-plugin": "npm:^0.5.11" "@storybook/addon-actions": "workspace:*" "@storybook/builder-webpack5": "workspace:*" "@storybook/core-common": "workspace:*" "@storybook/core-events": "workspace:*" + "@storybook/core-webpack": "workspace:*" "@storybook/node-logger": "workspace:*" "@storybook/preset-react-webpack": "workspace:*" "@storybook/preview-api": "workspace:*" @@ -6897,7 +6874,7 @@ __metadata: fs-extra: "npm:^11.1.0" image-size: "npm:^1.0.0" loader-utils: "npm:^3.2.1" - next: "npm:^14.0.0" + next: "npm:^14.0.2" node-polyfill-webpack-plugin: "npm:^2.0.1" pnp-webpack-plugin: "npm:^1.7.0" postcss: "npm:^8.4.21" @@ -6922,8 +6899,6 @@ __metadata: peerDependenciesMeta: "@next/font": optional: true - "@storybook/addon-actions": - optional: true typescript: optional: true webpack: @@ -6950,7 +6925,7 @@ __metadata: dependencies: jest: "npm:^29.7.0" jest-specific-snapshot: "npm:^8.0.0" - jscodeshift: "npm:^0.14.0" + jscodeshift: "npm:^0.15.1" typescript: "npm:~4.9.3" languageName: unknown linkType: soft @@ -6985,8 +6960,6 @@ __metadata: peerDependencies: "@babel/core": "*" preact: ^8.0.0||^10.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 languageName: unknown linkType: soft @@ -7201,7 +7174,7 @@ __metadata: "@storybook/client-logger": "workspace:*" "@storybook/core-common": "workspace:*" "@storybook/core-events": "workspace:*" - "@storybook/csf": "npm:^0.1.0" + "@storybook/csf": "npm:^0.1.2" "@storybook/global": "npm:^5.0.0" "@storybook/types": "workspace:*" "@types/qs": "npm:^6.9.5" @@ -7210,7 +7183,6 @@ __metadata: lodash: "npm:^4.17.21" memoizerific: "npm:^1.11.3" qs: "npm:^6.10.0" - react: "npm:^16.14.0" slash: "npm:^5.0.0" synchronous-promise: "npm:^2.0.15" ts-dedent: "npm:^2.0.0" @@ -7237,6 +7209,8 @@ __metadata: "@storybook/global": "npm:^5.0.0" "@storybook/preview-api": "workspace:*" browser-dtector: "npm:^3.4.0" + fs-extra: "npm:^11.1.0" + ts-dedent: "npm:^2.0.0" typescript: "npm:~4.9.3" languageName: unknown linkType: soft @@ -7409,7 +7383,7 @@ __metadata: "@storybook/core-events": "workspace:*" "@storybook/core-server": "workspace:*" "@storybook/core-webpack": "workspace:*" - "@storybook/csf": "npm:^0.1.0" + "@storybook/csf": "npm:^0.1.2" "@storybook/csf-plugin": "workspace:*" "@storybook/csf-tools": "workspace:*" "@storybook/docs-tools": "workspace:*" @@ -7481,7 +7455,7 @@ __metadata: "@typescript-eslint/eslint-plugin": "npm:^5.45.0" "@typescript-eslint/experimental-utils": "npm:^5.45.0" "@typescript-eslint/parser": "npm:^5.45.0" - "@vitejs/plugin-react": "npm:^2.1.0" + "@vitejs/plugin-react": "npm:^3.0.1" babel-eslint: "npm:^10.1.0" babel-loader: "npm:^9.1.2" chromatic: "npm:7.1.0" @@ -7556,12 +7530,11 @@ __metadata: lodash: "npm:^4.17.21" memoizerific: "npm:^1.11.3" qs: "npm:^6.10.0" + react: "npm:^16.8.0" + react-dom: "npm:^16.8.0" react-router-dom: "npm:6.0.2" ts-dedent: "npm:^2.0.0" typescript: "npm:~4.9.3" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 languageName: unknown linkType: soft @@ -7575,9 +7548,6 @@ __metadata: "@storybook/server": "workspace:*" "@types/node": "npm:^18.0.0" typescript: "npm:~4.9.3" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 languageName: unknown linkType: soft @@ -7586,7 +7556,7 @@ __metadata: resolution: "@storybook/server@workspace:renderers/server" dependencies: "@storybook/core-client": "workspace:*" - "@storybook/csf": "npm:^0.1.0" + "@storybook/csf": "npm:^0.1.2" "@storybook/csf-tools": "workspace:*" "@storybook/global": "npm:^5.0.0" "@storybook/preview-api": "workspace:*" @@ -7603,16 +7573,13 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/source-loader@workspace:lib/source-loader" dependencies: - "@storybook/csf": "npm:^0.1.0" + "@storybook/csf": "npm:^0.1.2" "@storybook/types": "workspace:*" estraverse: "npm:^5.2.0" jest-specific-snapshot: "npm:^8.0.0" lodash: "npm:^4.17.21" prettier: "npm:^2.8.0" typescript: "npm:~4.9.3" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 languageName: unknown linkType: soft @@ -7660,8 +7627,6 @@ __metadata: typescript: "npm:~4.9.3" peerDependencies: "@babel/core": "*" - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 svelte: ^3.48.0 || ^4.0.0 svelte-loader: "*" languageName: unknown @@ -7682,6 +7647,7 @@ __metadata: svelte: "npm:^4.0.0" svelte-check: "npm:3.4.6" sveltedoc-parser: "npm:^4.2.1" + ts-dedent: "npm:^2.0.0" type-fest: "npm:~2.19" typescript: "npm:^5.0.4" peerDependencies: @@ -7693,6 +7659,7 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/sveltekit@workspace:frameworks/sveltekit" dependencies: + "@storybook/addon-actions": "workspace:*" "@storybook/builder-vite": "workspace:*" "@storybook/svelte": "workspace:*" "@storybook/svelte-vite": "workspace:*" @@ -7757,17 +7724,17 @@ __metadata: linkType: hard "@storybook/theming@npm:^7.0.2": - version: 7.4.6 - resolution: "@storybook/theming@npm:7.4.6" + version: 7.5.0 + resolution: "@storybook/theming@npm:7.5.0" dependencies: "@emotion/use-insertion-effect-with-fallbacks": "npm:^1.0.0" - "@storybook/client-logger": "npm:7.4.6" + "@storybook/client-logger": "npm:7.5.0" "@storybook/global": "npm:^5.0.0" memoizerific: "npm:^1.11.3" peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 6250a413c346971792623bf5a907811fc009ff4a36b8f292d0f45c677269b2a50c29d84ab1e869ada7df3eb23d49614e1342bd2c88e71d4467702b92ebc42f2d + checksum: 57da8e27c748cbec4dc1661cdd2d449949d97476d8e97933696b31d07c7361cbbcca8d7225cc00ca078daa160023b8965ddec7c23519ce0a4ef2658246b062e7 languageName: node linkType: hard @@ -7801,7 +7768,7 @@ __metadata: resolution: "@storybook/types@workspace:lib/types" dependencies: "@storybook/channels": "workspace:*" - "@storybook/csf": "npm:^0.1.0" + "@storybook/csf": "npm:^0.1.2" "@types/babel__core": "npm:^7.0.0" "@types/express": "npm:^4.7.0" "@types/fs-extra": "npm:^11.0.1" @@ -7825,8 +7792,6 @@ __metadata: vue: "npm:^2.7.10" vue-docgen-api: "npm:^4.40.0" peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 vite: ^3.0.0 || ^4.0.0 || ^5.0.0 vue: ^2.7.0 languageName: unknown @@ -7849,8 +7814,6 @@ __metadata: "@babel/core": "*" babel-loader: ^7.0.0 || ^8.0.0 || ^9.0.0 css-loader: "*" - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 vue: ^2.6.8 vue-loader: ^15.7.0 vue-template-compiler: ^2.6.8 @@ -7871,8 +7834,6 @@ __metadata: vite: "npm:^4.0.0" vue-docgen-api: "npm:^4.40.0" peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 vite: ^3.0.0 || ^4.0.0 || ^5.0.0 languageName: unknown linkType: soft @@ -7893,8 +7854,6 @@ __metadata: "@babel/core": "*" "@vue/compiler-sfc": ^3.0.0 babel-loader: ^7.0.0 || ^8.0.0 || ^9.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 vue: ^3.0.0 languageName: unknown linkType: soft @@ -7963,9 +7922,6 @@ __metadata: "@types/node": "npm:^18.0.0" magic-string: "npm:^0.30.0" typescript: "npm:~4.9.3" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 languageName: unknown linkType: soft @@ -7983,8 +7939,6 @@ __metadata: typescript: "npm:~4.9.3" peerDependencies: lit: ^2.0.0 || ^3.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 languageName: unknown linkType: soft @@ -8986,13 +8940,13 @@ __metadata: languageName: node linkType: hard -"@types/jscodeshift@npm:^0.11.6": - version: 0.11.7 - resolution: "@types/jscodeshift@npm:0.11.7" +"@types/jscodeshift@npm:^0.11.10": + version: 0.11.10 + resolution: "@types/jscodeshift@npm:0.11.10" dependencies: ast-types: "npm:^0.14.1" recast: "npm:^0.20.3" - checksum: a2c26f8e64950296bae6176c52e832e1f5c5eb3672adad3c1cdc63e23b8bd3de47890ac8eaae7eb0788feea7628ce540513ff5189379f79e882ddcfa1c855cfc + checksum: 1d477ea1addd62a5949f028ef16bac3226341d65052e4f51d61e51789c6c7aa17e953dac34eb6d1e5a2b761fc4c7920df875e20e85cdf4122fc08836e7da547a languageName: node linkType: hard @@ -9805,23 +9759,6 @@ __metadata: languageName: node linkType: hard -"@vitejs/plugin-react@npm:^2.1.0": - version: 2.2.0 - resolution: "@vitejs/plugin-react@npm:2.2.0" - dependencies: - "@babel/core": "npm:^7.19.6" - "@babel/plugin-transform-react-jsx": "npm:^7.19.0" - "@babel/plugin-transform-react-jsx-development": "npm:^7.18.6" - "@babel/plugin-transform-react-jsx-self": "npm:^7.18.6" - "@babel/plugin-transform-react-jsx-source": "npm:^7.19.6" - magic-string: "npm:^0.26.7" - react-refresh: "npm:^0.14.0" - peerDependencies: - vite: ^3.0.0 - checksum: 85fe5c740fbe8aa5dd4c3516a02a937dff0e2b0858cfa7cf8a69b998b7d05e08c296a087fde66f9171367f5c9d10d6e4bc026df1fa1e2ec528f49e7eaabeeae1 - languageName: node - linkType: hard - "@vitejs/plugin-react@npm:^3.0.1": version: 3.1.0 resolution: "@vitejs/plugin-react@npm:3.1.0" @@ -11204,15 +11141,6 @@ __metadata: languageName: node linkType: hard -"ast-types@npm:0.15.2": - version: 0.15.2 - resolution: "ast-types@npm:0.15.2" - dependencies: - tslib: "npm:^2.0.1" - checksum: 5b26e3656e9e8d1db8c8d14971d0cb88ca0138aacce72171cb4cd4555fc8dc53c07e821c568e57fe147366931708fefd25cb9d7e880d42ce9cb569947844c962 - languageName: node - linkType: hard - "ast-types@npm:^0.16.1": version: 0.16.1 resolution: "ast-types@npm:0.16.1" @@ -20177,19 +20105,20 @@ __metadata: languageName: node linkType: hard -"jscodeshift@npm:^0.14.0": - version: 0.14.0 - resolution: "jscodeshift@npm:0.14.0" +"jscodeshift@npm:^0.15.1": + version: 0.15.1 + resolution: "jscodeshift@npm:0.15.1" dependencies: - "@babel/core": "npm:^7.13.16" - "@babel/parser": "npm:^7.13.16" - "@babel/plugin-proposal-class-properties": "npm:^7.13.0" - "@babel/plugin-proposal-nullish-coalescing-operator": "npm:^7.13.8" - "@babel/plugin-proposal-optional-chaining": "npm:^7.13.12" - "@babel/plugin-transform-modules-commonjs": "npm:^7.13.8" - "@babel/preset-flow": "npm:^7.13.13" - "@babel/preset-typescript": "npm:^7.13.0" - "@babel/register": "npm:^7.13.16" + "@babel/core": "npm:^7.23.0" + "@babel/parser": "npm:^7.23.0" + "@babel/plugin-transform-class-properties": "npm:^7.22.5" + "@babel/plugin-transform-modules-commonjs": "npm:^7.23.0" + "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.22.11" + "@babel/plugin-transform-optional-chaining": "npm:^7.23.0" + "@babel/plugin-transform-private-methods": "npm:^7.22.5" + "@babel/preset-flow": "npm:^7.22.15" + "@babel/preset-typescript": "npm:^7.23.0" + "@babel/register": "npm:^7.22.15" babel-core: "npm:^7.0.0-bridge.0" chalk: "npm:^4.1.2" flow-parser: "npm:0.*" @@ -20197,14 +20126,17 @@ __metadata: micromatch: "npm:^4.0.4" neo-async: "npm:^2.5.0" node-dir: "npm:^0.1.17" - recast: "npm:^0.21.0" + recast: "npm:^0.23.3" temp: "npm:^0.8.4" write-file-atomic: "npm:^2.3.0" peerDependencies: "@babel/preset-env": ^7.1.6 + peerDependenciesMeta: + "@babel/preset-env": + optional: true bin: jscodeshift: bin/jscodeshift.js - checksum: dab63bdb4b7e67d79634fcd3f5dc8b227146e9f68aa88700bc49c5a45b6339d05bd934a98aa53d29abd04f81237d010e7e037799471b2aab66ec7b9a7d752786 + checksum: 334de6ffa776a68b3f59f2f18a285ea977f3339d85e3517f3854761e65769ffa7e453c35cde320fc969106d573df39bd3fb08b23db54ae17c1b1516e5bf05742 languageName: node linkType: hard @@ -21278,15 +21210,6 @@ __metadata: languageName: node linkType: hard -"magic-string@npm:^0.26.7": - version: 0.26.7 - resolution: "magic-string@npm:0.26.7" - dependencies: - sourcemap-codec: "npm:^1.4.8" - checksum: 950035b344fe2a8163668980bc4a215a0b225086e6e22100fd947e7647053c6ba6b4f11a04de83a97a276526ccb602ef53b173725dbb1971fb146cff5a5e14f6 - languageName: node - linkType: hard - "magic-string@npm:^0.27.0": version: 0.27.0 resolution: "magic-string@npm:0.27.0" @@ -22792,20 +22715,20 @@ __metadata: languageName: node linkType: hard -"next@npm:^14.0.0": - version: 14.0.0 - resolution: "next@npm:14.0.0" +"next@npm:^14.0.2": + version: 14.0.2 + resolution: "next@npm:14.0.2" dependencies: - "@next/env": "npm:14.0.0" - "@next/swc-darwin-arm64": "npm:14.0.0" - "@next/swc-darwin-x64": "npm:14.0.0" - "@next/swc-linux-arm64-gnu": "npm:14.0.0" - "@next/swc-linux-arm64-musl": "npm:14.0.0" - "@next/swc-linux-x64-gnu": "npm:14.0.0" - "@next/swc-linux-x64-musl": "npm:14.0.0" - "@next/swc-win32-arm64-msvc": "npm:14.0.0" - "@next/swc-win32-ia32-msvc": "npm:14.0.0" - "@next/swc-win32-x64-msvc": "npm:14.0.0" + "@next/env": "npm:14.0.2" + "@next/swc-darwin-arm64": "npm:14.0.2" + "@next/swc-darwin-x64": "npm:14.0.2" + "@next/swc-linux-arm64-gnu": "npm:14.0.2" + "@next/swc-linux-arm64-musl": "npm:14.0.2" + "@next/swc-linux-x64-gnu": "npm:14.0.2" + "@next/swc-linux-x64-musl": "npm:14.0.2" + "@next/swc-win32-arm64-msvc": "npm:14.0.2" + "@next/swc-win32-ia32-msvc": "npm:14.0.2" + "@next/swc-win32-x64-msvc": "npm:14.0.2" "@swc/helpers": "npm:0.5.2" busboy: "npm:1.6.0" caniuse-lite: "npm:^1.0.30001406" @@ -22843,7 +22766,7 @@ __metadata: optional: true bin: next: dist/bin/next - checksum: cfb18a72d6e1d875efb1bb3806f9a06551f482c5cb87231e77e179a71d26f3d43700290988ad27e739302bfa7ff8ac8081aafd5456c39a2819fdd315617e5acf + checksum: 65ae7a09f1643bc3deafdbdae9ce0c02326346c4a60a7c739f8f6b154b2226b8fcc5efb984cdcb4ef100116910d4c1013089135800d30c7a50cf98c9d22e5a26 languageName: node linkType: hard @@ -25801,7 +25724,7 @@ __metadata: languageName: node linkType: hard -"react@npm:^16.14.0": +"react@npm:^16.14.0, react@npm:^16.8.0": version: 16.14.0 resolution: "react@npm:16.14.0" dependencies: @@ -25969,19 +25892,7 @@ __metadata: languageName: node linkType: hard -"recast@npm:^0.21.0": - version: 0.21.5 - resolution: "recast@npm:0.21.5" - dependencies: - ast-types: "npm:0.15.2" - esprima: "npm:~4.0.0" - source-map: "npm:~0.6.1" - tslib: "npm:^2.0.1" - checksum: a45168c82195f24fa2c70293a624fece0069a2e8e8adb637f9963777735f81cb3bb62e55172db677ec3573b08b2daaf1eddd85b74da6fe0bd37c9b15eeaf94b4 - languageName: node - linkType: hard - -"recast@npm:^0.23.1": +"recast@npm:^0.23.1, recast@npm:^0.23.3": version: 0.23.4 resolution: "recast@npm:0.23.4" dependencies: @@ -27534,7 +27445,7 @@ __metadata: languageName: node linkType: hard -"slash@npm:^5.0.0": +"slash@npm:^5.0.0, slash@npm:^5.1.0": version: 5.1.0 resolution: "slash@npm:5.1.0" checksum: eb48b815caf0bdc390d0519d41b9e0556a14380f6799c72ba35caf03544d501d18befdeeef074bc9c052acf69654bc9e0d79d7f1de0866284137a40805299eb3 diff --git a/docs/api/cli-options.md b/docs/api/cli-options.md index a89d55a94d14..1ccbb4d1f124 100644 --- a/docs/api/cli-options.md +++ b/docs/api/cli-options.md @@ -61,19 +61,20 @@ storybook build [options] Options include: -| Option | Description | -| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `-h`, `--help` | Output usage information
`storybook build --help` | -| `-V`, `--version` | Output the version number
`storybook build -V` | -| `-s`, `--static-dir` | **Deprecated** [see note](#static-dir-deprecation).
Directory where to load static files from, comma-separated list
`storybook build -s public` | -| `-o`, `--output-dir [dir-name]` | Directory where to store built files
`storybook build -o /my-deployed-storybook` | -| `-c`, `--config-dir [dir-name]` | Directory where to load Storybook configurations from
`storybook build -c .storybook` | -| `--loglevel [level]` | Controls level of logging during build.
Available options: `silly`, `verbose`, `info` (default), `warn`, `error`, `silent`
`storybook build --loglevel warn` | -| `--quiet` | Suppress verbose build output
`storybook build --quiet` | -| `--debug-webpack` | Display final webpack configurations for debugging purposes
`storybook build --debug-webpack` | -| `--webpack-stats-json` | Write Webpack Stats JSON to disk
`storybook build --webpack-stats-json /my-storybook/webpack-stats` | -| `--docs` | Builds Storybook in documentation mode. Learn more about it in [here](../writing-docs/build-documentation.md#publish-storybooks-documentation)
`storybook build --docs` | -| `--disable-telemetry` | Disables Storybook's telemetry. Learn more about it [here](../configure/telemetry.md).
`storybook build --disable-telemetry` | +| Option | Description | +| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `-h`, `--help` | Output usage information
`storybook build --help` | +| `-V`, `--version` | Output the version number
`storybook build -V` | +| `-s`, `--static-dir` | **Deprecated** [see note](#static-dir-deprecation).
Directory where to load static files from, comma-separated list
`storybook build -s public` | +| `-o`, `--output-dir [dir-name]` | Directory where to store built files
`storybook build -o /my-deployed-storybook` | +| `-c`, `--config-dir [dir-name]` | Directory where to load Storybook configurations from
`storybook build -c .storybook` | +| `--loglevel [level]` | Controls level of logging during build.
Available options: `silly`, `verbose`, `info` (default), `warn`, `error`, `silent`
`storybook build --loglevel warn` | +| `--quiet` | Suppress verbose build output
`storybook build --quiet` | +| `--debug-webpack` | Display final webpack configurations for debugging purposes
`storybook build --debug-webpack` | +| `--webpack-stats-json` | Write Webpack Stats JSON to disk
`storybook build --webpack-stats-json /my-storybook/webpack-stats` | +| `--docs` | Builds Storybook in documentation mode. Learn more about it in [here](../writing-docs/build-documentation.md#publish-storybooks-documentation)
`storybook build --docs` | +| `--disable-telemetry` | Disables Storybook's telemetry. Learn more about it [here](../configure/telemetry.md).
`storybook build --disable-telemetry` | +| `--test` | Optimize Storybook's production build for performance and tests by removing unnecessary features with the `test` option. Learn more [here](../api/main-config-build.md).
`storybook build --test` | diff --git a/docs/api/main-config-build.md b/docs/api/main-config-build.md new file mode 100644 index 000000000000..a206662bae4a --- /dev/null +++ b/docs/api/main-config-build.md @@ -0,0 +1,153 @@ +--- +title: 'build' +--- + +Parent: [main.js|ts configuration](./main-config.md) + +Type: `TestBuildConfig` + +Provides configuration options to optimize Storybook's production build output. + +## `test` + +Type: `TestBuildFlags` + +```ts +{ + disableBlocks?: boolean; + disabledAddons?: string[]; + disableMDXEntries?: boolean; + disableAutoDocs?: boolean; + disableDocgen?: boolean; + disableSourcemaps?: boolean; + disableTreeShaking?: boolean; + +} +``` + +Configures Storybook's production builds for performance testing purposes by disabling certain features from the build. When running ' build-storybook ', this feature is enabled by setting the `--test` [flag](./cli-options.md#build). + + + +Enabling these features can cause build or runtime errors with Storybook. We recommend enabling only the features you need for your project. + + + +### `test.disableBlocks` + +Type: `boolean` + +Excludes the `@storybook/blocks` package from the build, which generates automatic documentation with [Docs Blocks](../writing-docs/doc-blocks.md). + + + + + + + +### `test.disabledAddons` + +Type: `string[]` + +Sets the list of addons that will disabled in the build output. + + + + + + + +### `test.disableMDXEntries` + +Type: `boolean` + +Enabling this option removes user-written documentation entries in MDX format from the build. + + + + + + + +### `test.disableAutoDocs` + +Type: `boolean` + +Prevents automatic documentation generated with the [autodocs](../writing-docs/autodocs.md) feature from being included in the build. + + + + + + + +### `test.disableDocgen` + +Type: `boolean` + +Disables [automatic argType](./arg-types.md#automatic-argtype-inference) and component property inference with any of the supported static analysis tools based on the framework you are using. + + + + + + + +### `test.disableSourcemaps` + +Type: `boolean` + +Overrides the default behavior of generating source maps for the build. + + + + + + + +### `test.disableTreeShaking` + +Type: `boolean` + +Disables [tree shaking](https://developer.mozilla.org/en-US/docs/Glossary/Tree_shaking) in the build. + + + + + + diff --git a/docs/api/main-config-typescript.md b/docs/api/main-config-typescript.md index 425f8703777a..89f11b1cbd5a 100644 --- a/docs/api/main-config-typescript.md +++ b/docs/api/main-config-typescript.md @@ -90,9 +90,11 @@ Only available for React Storybook projects. Options to pass to react-docgen-typ ## `skipBabel` +Deprecated: Will be removed in Storybook 8.0. Use `skipCompiler` instead. + Type: `boolean` -Disable parsing of TypeScript files through babel. +Disable parsing of TypeScript files through Babel. @@ -103,3 +105,19 @@ Disable parsing of TypeScript files through babel. /> + +## `skipCompiler` + +Type: `boolean` + +Disable parsing of TypeScript files through the compiler, which is used for Webpack5. + + + + + + diff --git a/docs/api/main-config.md b/docs/api/main-config.md index 69372067bdf8..e0988eec381f 100644 --- a/docs/api/main-config.md +++ b/docs/api/main-config.md @@ -30,6 +30,7 @@ An object to configure Storybook containing the following properties: - [`addons`](./main-config-addons.md) - [`babel`](./main-config-babel.md) - [`babelDefault`](./main-config-babel-default.md) +- [`build`](./main-config-build.md) - [`core`](./main-config-core.md) - [`docs`](./main-config-docs.md) - [`env`](./main-config-env.md) diff --git a/docs/configure/overview.md b/docs/configure/overview.md index 4e11a8f5c440..ee38fd9a4569 100644 --- a/docs/configure/overview.md +++ b/docs/configure/overview.md @@ -31,21 +31,22 @@ This configuration file is a [preset](../addons/addon-types.md) and, as such, ha -| Configuration element | Description | -| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `stories` | The array of globs that indicates the [location of your story files](#configure-story-loading), relative to `main.js` | -| `staticDirs` | Sets a list of directories of [static files](./images-and-assets.md#serving-static-files-via-storybook-configuration) to be loaded by Storybook
`staticDirs: ['../public']` | -| `addons` | Sets the list of [addons](https://storybook.js.org/integrations) loaded by Storybook
`addons: ['@storybook/addon-essentials']` | -| `typescript` | Configures how Storybook handles [TypeScript files](./typescript.md)
`typescript: { check: false, checkOptions: {} }` | -| `framework` | Configures Storybook based on a set of [framework-specific](./frameworks.md) settings
`framework: { name: '@storybook/svelte-vite', options:{} }` | -| `core` | Configures Storybook's [internal features](../api/main-config-core.md)
`core: { disableTelemetry: true, }` | -| `docs` | Configures Storybook's [auto-generated documentation](../writing-docs/autodocs.md)
`docs: { autodocs: 'tag' }` | -| `features` | Enables Storybook's [additional features](../api/main-config-features.md)
See table below for a list of available features `features: { storyStoreV7: true }` | -| `refs` | Configures [Storybook composition](../sharing/storybook-composition.md)
`refs:{ example: { title: 'ExampleStorybook', url:'https://your-url.com' } }` | -| `logLevel` | Configures Storybook's logs in the browser terminal. Useful for debugging
`logLevel: 'debug'` | -| `webpackFinal` | Customize Storybook's [Webpack](../builders/webpack.md) setup
`webpackFinal: async (config:any) => { return config; }` | -| `viteFinal` | Customize Storybook's Vite setup when using the [vite builder](https://github.com/storybookjs/builder-vite)
`viteFinal: async (config: Vite.InlineConfig, options: Options) => { return config; }` | -| `env` | Defines custom Storybook [environment variables](./environment-variables.md#using-storybook-configuration).
`env: (config) => ({...config, EXAMPLE_VAR: 'Example var' }),` | +| Configuration element | Description | +| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `stories` | The array of globs that indicates the [location of your story files](#configure-story-loading), relative to `main.js` | +| `staticDirs` | Sets a list of directories of [static files](./images-and-assets.md#serving-static-files-via-storybook-configuration) to be loaded by Storybook
`staticDirs: ['../public']` | +| `addons` | Sets the list of [addons](https://storybook.js.org/integrations) loaded by Storybook
`addons: ['@storybook/addon-essentials']` | +| `typescript` | Configures how Storybook handles [TypeScript files](./typescript.md)
`typescript: { check: false, checkOptions: {} }` | +| `framework` | Configures Storybook based on a set of [framework-specific](./frameworks.md) settings
`framework: { name: '@storybook/svelte-vite', options:{} }` | +| `core` | Configures Storybook's [internal features](../api/main-config-core.md)
`core: { disableTelemetry: true, }` | +| `docs` | Configures Storybook's [auto-generated documentation](../writing-docs/autodocs.md)
`docs: { autodocs: 'tag' }` | +| `features` | Enables Storybook's [additional features](../api/main-config-features.md)
See table below for a list of available features `features: { storyStoreV7: true }` | +| `refs` | Configures [Storybook composition](../sharing/storybook-composition.md)
`refs:{ example: { title: 'ExampleStorybook', url:'https://your-url.com' } }` | +| `logLevel` | Configures Storybook's logs in the browser terminal. Useful for debugging
`logLevel: 'debug'` | +| `webpackFinal` | Customize Storybook's [Webpack](../builders/webpack.md) setup
`webpackFinal: async (config:any) => { return config; }` | +| `viteFinal` | Customize Storybook's Vite setup when using the [vite builder](https://github.com/storybookjs/builder-vite)
`viteFinal: async (config: Vite.InlineConfig, options: Options) => { return config; }` | +| `env` | Defines custom Storybook [environment variables](./environment-variables.md#using-storybook-configuration).
`env: (config) => ({...config, EXAMPLE_VAR: 'Example var' }),` | +| `build` | Optimizes Storybook's production [build](../api/main-config-build.md) for performance by excluding specific features from the bundle. Useful when decreased build times are a priority.
`build:ย { test: {} }` | ### Feature flags diff --git a/docs/contribute/code.md b/docs/contribute/code.md index 7ce0068157bb..1ce7726caf3e 100644 --- a/docs/contribute/code.md +++ b/docs/contribute/code.md @@ -6,7 +6,7 @@ Contribute a new feature or bug fix to [Storybook's monorepo](https://github.com ## Prerequisites -- Ensure you have node version 16 installed (suggestion: v16.17.1). +- Ensure you have Node version 18 installed (suggestion: v18.16.0). - Ensure if you are using Windows to use the Windows Subsystem for Linux (WSL). ## Initial setup @@ -18,7 +18,7 @@ git clone https://github.com/your-username/storybook.git cd storybook ``` -Storybook uses the [yarn](https://v3.yarnpkg.com/) package manager. Use [Corepack](https://github.com/nodejs/corepack) to set up the correct version for use with Storybook. +Storybook uses the [Yarn](https://yarnpkg.com/) package manager. Use [Corepack](https://github.com/nodejs/corepack) to set up the correct version for use with Storybook. ```shell corepack enable @@ -201,7 +201,7 @@ npx storybook@next link --local /path/to/local-repro-directory -The `storybook link` command relies on [Yarn 2 linking](https://yarnpkg.com/cli/link/) under the hood. It requires your local reproduction to be using [Yarn 2](https://yarnpkg.com/) as well, which is the case if you're already enabled it with the [`storybook sandbox`](./how-to-reproduce.md) command per our contribution guidelines. The process will fail if you're trying to link a non-Yarn 2 project. +The `storybook link` command relies on [Yarn linking](https://yarnpkg.com/cli/link/) under the hood. It requires your local reproduction to be using [Yarn 2 or higher](https://yarnpkg.com/) as well, which is the case if you've already enabled it with the [`storybook sandbox`](./how-to-reproduce.md) command per our contribution guidelines. The process will fail if you're trying to link a non-Yarn 2 project. diff --git a/docs/faq.md b/docs/faq.md index 6539f6a3431f..387986d9cfbc 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -344,6 +344,30 @@ We're only covering versions 5.3 and 5.0 as they were important milestones for S | | @storybook/blocks/useOf | [See current documentation](./api/doc-block-useof.md) | Non existing feature or undocumented | Non existing feature or undocumented | | | Stories/Component Story Format | [See current documentation](./api/csf.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/formats/component-story-format) | Non existing feature or undocumented | | | Stories/StoriesOF format (see note below) | [See current documentation](https://github.com/storybookjs/storybook/blob/main/code/lib/preview-api/docs/storiesOf.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/formats/storiesof-api) | Non existing feature or undocumented | +| | ArgTypes | [See current documentation](./api/arg-types.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/Overview | [See current documentation](./api/main-config.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/framework | [See current documentation](./api/main-config-framework.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/stories | [See current documentation](./api/main-config-stories.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/addons | [See current documentation](./api/main-config-addons.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/babel | [See current documentation](./api/main-config-babel.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/babelDefault | [See current documentation](./api/main-config-babel-default.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/build | [See current documentation](./api/main-config-build.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/core | [See current documentation](./api/main-config-core.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/docs | [See current documentation](./api/main-config-docs.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/env | [See current documentation](./api/main-config-env.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/features | [See current documentation](./api/main-config-features.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/indexers | [See current documentation](./api/main-config-indexers.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/logLevel | [See current documentation](./api/main-config-log-level.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/managerHead | [See current documentation](./api/main-config-manager-head.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/previewAnnotations | [See current documentation](./api/main-config-preview-annotations.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/previewBody | [See current documentation](./api/main-config-preview-body.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/previewHead | [See current documentation](./api/main-config-preview-head.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/refs | [See current documentation](./api/main-config-refs.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/staticDirs | [See current documentation](./api/main-config-static-dirs.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/typescript | [See current documentation](./api/main-config-typescript.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/viteFinal | [See current documentation](./api/main-config-vite-final.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/webpackFinal | [See current documentation](./api/main-config-webpack-final.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/config | [See current documentation](./api/main-config-config.md) | Non existing feature or undocumented | Non existing feature or undocumented | | | Frameworks | [See current documentation](./api/new-frameworks.md) | Non existing feature or undocumented | Non existing feature or undocumented | | | CLI options | [See current documentation](./api/cli-options.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/cli-options) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/cli-options) | diff --git a/docs/sharing/design-integrations.md b/docs/sharing/design-integrations.md index d2211a7b52d9..ca0842f18b10 100644 --- a/docs/sharing/design-integrations.md +++ b/docs/sharing/design-integrations.md @@ -57,7 +57,7 @@ Once they're connected, you'll be able to view the story by clicking the link in ### Embed Figma in Storybook with the addon -[Design addon](https://storybook.js.org/addons/storybook-addon-designs) allows you to embed Figma files and prototypes in Storybook. +[Designs addon](https://storybook.js.org/addons/@storybook/addon-designs) allows you to embed Figma files and prototypes in Storybook. ![Storybook addon figma](./storybook-figma-addon.png) diff --git a/docs/sharing/publish-storybook.md b/docs/sharing/publish-storybook.md index ca98bb2d8264..cf6b20b2e1bf 100644 --- a/docs/sharing/publish-storybook.md +++ b/docs/sharing/publish-storybook.md @@ -50,6 +50,22 @@ Storybook will create a static web application capable of being served by any we +### Customizing the build for performance + +By default, Storybook's production build will encapsulate all stories and documentation into the production bundle. This is ideal for small projects but can cause performance issues for larger projects or when decreased build times are a priority (e.g., testing, CI/CD). If you need, you can customize the production build with the [`test` option](../api/main-config-build.md#test) in your `main.js|ts` configuration file and adjust your build script to enable the optimizations with the `--test` [flag](../api/cli-options.md#build). + + + + + + + ## Publish Storybook with Chromatic Once you've built your Storybook as a static web application, you can publish it to your web host. We recommend [Chromatic](https://www.chromatic.com/?utm_source=storybook_website&utm_medium=link&utm_campaign=storybook), a free publishing service made for Storybook that documents, versions, and indexes your UI components securely in the cloud. @@ -137,7 +153,6 @@ Since Storybook is built as a static web application, you can also publish it to To deploy Storybook on GitHub Pages, use the community-built [Deploy Storybook to GitHub Pages](https://github.com/bitovi/github-actions-storybook-to-github-pages) Action. To enable it, create a new workflow file inside your `.github/workflows` directory with the following content: - -

Component Publishing Protocol (CPP)

diff --git a/docs/snippets/common/main-config-test-disable-autodocs.js.mdx b/docs/snippets/common/main-config-test-disable-autodocs.js.mdx new file mode 100644 index 000000000000..86b7de8fb7b3 --- /dev/null +++ b/docs/snippets/common/main-config-test-disable-autodocs.js.mdx @@ -0,0 +1,14 @@ +```js +// .storybook/main.js + +export default { + // Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) + framework: '@storybook/your-framework', + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + build: { + test: { + disableAutoDocs: true, + }, + }, +}; +``` diff --git a/docs/snippets/common/main-config-test-disable-autodocs.ts.mdx b/docs/snippets/common/main-config-test-disable-autodocs.ts.mdx new file mode 100644 index 000000000000..1eb9e7218197 --- /dev/null +++ b/docs/snippets/common/main-config-test-disable-autodocs.ts.mdx @@ -0,0 +1,18 @@ +```ts +// .storybook/main.ts + +// Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) +import type { StorybookConfig } from '@storybook/your-framework'; + +const config: StorybookConfig = { + framework: '@storybook/your-framework', + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + build: { + test: { + disableAutoDocs: true, + }, + }, +}; + +export default config; +``` diff --git a/docs/snippets/common/main-config-test-disable-blocks.js.mdx b/docs/snippets/common/main-config-test-disable-blocks.js.mdx new file mode 100644 index 000000000000..9b4aa8c9a478 --- /dev/null +++ b/docs/snippets/common/main-config-test-disable-blocks.js.mdx @@ -0,0 +1,14 @@ +```js +// .storybook/main.js + +export default { + // Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) + framework: '@storybook/your-framework', + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + build: { + test: { + disableBlocks: true, + }, + }, +}; +``` diff --git a/docs/snippets/common/main-config-test-disable-blocks.ts.mdx b/docs/snippets/common/main-config-test-disable-blocks.ts.mdx new file mode 100644 index 000000000000..0cb4b4235bba --- /dev/null +++ b/docs/snippets/common/main-config-test-disable-blocks.ts.mdx @@ -0,0 +1,18 @@ +```ts +// .storybook/main.ts + +// Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) +import type { StorybookConfig } from '@storybook/your-framework'; + +const config: StorybookConfig = { + framework: '@storybook/your-framework', + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + build: { + test: { + disableBlocks: true, + }, + }, +}; + +export default config; +``` diff --git a/docs/snippets/common/main-config-test-disable-disableaddons.js.mdx b/docs/snippets/common/main-config-test-disable-disableaddons.js.mdx new file mode 100644 index 000000000000..62edb30799b6 --- /dev/null +++ b/docs/snippets/common/main-config-test-disable-disableaddons.js.mdx @@ -0,0 +1,20 @@ +```js +// .storybook/main.js + +export default { + // Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) + framework: '@storybook/your-framework', + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + addons: [ + '@storybook/addon-links', + '@storybook/addon-essentials', + '@storybook/addon-interactions', + '@storybook/addon-a11y', + ], + build: { + test: { + disabledAddons: ['@storybook/addon-a11y'], + }, + }, +}; +``` diff --git a/docs/snippets/common/main-config-test-disable-disableaddons.ts.mdx b/docs/snippets/common/main-config-test-disable-disableaddons.ts.mdx new file mode 100644 index 000000000000..eb05b42ec95c --- /dev/null +++ b/docs/snippets/common/main-config-test-disable-disableaddons.ts.mdx @@ -0,0 +1,24 @@ +```ts +// .storybook/main.ts + +// Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) +import type { StorybookConfig } from '@storybook/your-framework'; + +const config: StorybookConfig = { + framework: '@storybook/your-framework', + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + addons: [ + '@storybook/addon-links', + '@storybook/addon-essentials', + '@storybook/addon-interactions', + '@storybook/addon-a11y', + ], + build: { + test: { + disabledAddons: ['@storybook/addon-a11y'], + }, + }, +}; + +export default config; +``` diff --git a/docs/snippets/common/main-config-test-disable-docgen.js.mdx b/docs/snippets/common/main-config-test-disable-docgen.js.mdx new file mode 100644 index 000000000000..ffe4bdc2f866 --- /dev/null +++ b/docs/snippets/common/main-config-test-disable-docgen.js.mdx @@ -0,0 +1,14 @@ +```js +// .storybook/main.js + +export default { + // Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) + framework: '@storybook/your-framework', + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + build: { + test: { + disableDocgen: true, + }, + }, +}; +``` diff --git a/docs/snippets/common/main-config-test-disable-docgen.ts.mdx b/docs/snippets/common/main-config-test-disable-docgen.ts.mdx new file mode 100644 index 000000000000..1d0eb3c10d9d --- /dev/null +++ b/docs/snippets/common/main-config-test-disable-docgen.ts.mdx @@ -0,0 +1,18 @@ +```ts +// .storybook/main.ts + +// Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) +import type { StorybookConfig } from '@storybook/your-framework'; + +const config: StorybookConfig = { + framework: '@storybook/your-framework', + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + build: { + test: { + disableDocgen: true, + }, + }, +}; + +export default config; +``` diff --git a/docs/snippets/common/main-config-test-disable-mdx.js.mdx b/docs/snippets/common/main-config-test-disable-mdx.js.mdx new file mode 100644 index 000000000000..f6204594c1ef --- /dev/null +++ b/docs/snippets/common/main-config-test-disable-mdx.js.mdx @@ -0,0 +1,14 @@ +```js +// .storybook/main.js + +export default { + // Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) + framework: '@storybook/your-framework', + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + build: { + test: { + disableMDXEntries: true, + }, + }, +}; +``` diff --git a/docs/snippets/common/main-config-test-disable-mdx.ts.mdx b/docs/snippets/common/main-config-test-disable-mdx.ts.mdx new file mode 100644 index 000000000000..e5327602aed8 --- /dev/null +++ b/docs/snippets/common/main-config-test-disable-mdx.ts.mdx @@ -0,0 +1,18 @@ +```ts +// .storybook/main.ts + +// Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) +import type { StorybookConfig } from '@storybook/your-framework'; + +const config: StorybookConfig = { + framework: '@storybook/your-framework', + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + build: { + test: { + disableMDXEntries: true, + }, + }, +}; + +export default config; +``` diff --git a/docs/snippets/common/main-config-test-disable-sourcemaps.js.mdx b/docs/snippets/common/main-config-test-disable-sourcemaps.js.mdx new file mode 100644 index 000000000000..9207b9317099 --- /dev/null +++ b/docs/snippets/common/main-config-test-disable-sourcemaps.js.mdx @@ -0,0 +1,14 @@ +```js +// .storybook/main.js + +export default { + // Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) + framework: '@storybook/your-framework', + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + build: { + test: { + disableSourcemaps: true, + }, + }, +}; +``` diff --git a/docs/snippets/common/main-config-test-disable-sourcemaps.ts.mdx b/docs/snippets/common/main-config-test-disable-sourcemaps.ts.mdx new file mode 100644 index 000000000000..79b0abab693d --- /dev/null +++ b/docs/snippets/common/main-config-test-disable-sourcemaps.ts.mdx @@ -0,0 +1,18 @@ +```ts +// .storybook/main.ts + +// Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) +import type { StorybookConfig } from '@storybook/your-framework'; + +const config: StorybookConfig = { + framework: '@storybook/your-framework', + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + build: { + test: { + disableSourcemaps: true, + }, + }, +}; + +export default config; +``` diff --git a/docs/snippets/common/main-config-test-disable-treeshaking.js.mdx b/docs/snippets/common/main-config-test-disable-treeshaking.js.mdx new file mode 100644 index 000000000000..f0957f7e5536 --- /dev/null +++ b/docs/snippets/common/main-config-test-disable-treeshaking.js.mdx @@ -0,0 +1,14 @@ +```js +// .storybook/main.js + +export default { + // Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) + framework: '@storybook/your-framework', + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + build: { + test: { + disableTreeShaking: true, + }, + }, +}; +``` diff --git a/docs/snippets/common/main-config-test-disable-treeshaking.ts.mdx b/docs/snippets/common/main-config-test-disable-treeshaking.ts.mdx new file mode 100644 index 000000000000..b053cfa03435 --- /dev/null +++ b/docs/snippets/common/main-config-test-disable-treeshaking.ts.mdx @@ -0,0 +1,18 @@ +```ts +// .storybook/main.ts + +// Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) +import type { StorybookConfig } from '@storybook/your-framework'; + +const config: StorybookConfig = { + framework: '@storybook/your-framework', + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + build: { + test: { + disableTreeShaking: true, + }, + }, +}; + +export default config; +``` diff --git a/docs/snippets/common/main-config-typescript-skip-compiler.ts.mdx b/docs/snippets/common/main-config-typescript-skip-compiler.ts.mdx new file mode 100644 index 000000000000..85c216504c27 --- /dev/null +++ b/docs/snippets/common/main-config-typescript-skip-compiler.ts.mdx @@ -0,0 +1,16 @@ +```ts +// .storybook/main.ts + +// Replace your-framework with the framework you are using (e.g., react-webpack5) +import type { StorybookConfig } from '@storybook/your-framework'; + +const config: StorybookConfig = { + framework: '@storybook/your-framework', + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + typescript: { + skipCompiler: true, + }, +}; + +export default config; +``` diff --git a/docs/snippets/common/my-component-exclude-tags.story.js.mdx b/docs/snippets/common/my-component-exclude-tags.story.js.mdx new file mode 100644 index 000000000000..32eebb42518d --- /dev/null +++ b/docs/snippets/common/my-component-exclude-tags.story.js.mdx @@ -0,0 +1,15 @@ +```js +// MyComponent.stories.js|jsx + +import { MyComponent } from './MyComponent'; + +export default { + component: MyComponent, + tags: ['no-tests'], // ๐Ÿ‘ˆ Provides the `no-tests` tag to all stories in this file +}; + +export const ExcludeStory = { + //๐Ÿ‘‡ Adds the `no-tests` tag to this story to exclude it from the tests when enabled in the test-runner configuration + tags: ['no-tests'], +}; +``` diff --git a/docs/snippets/common/my-component-exclude-tags.story.ts.mdx b/docs/snippets/common/my-component-exclude-tags.story.ts.mdx new file mode 100644 index 000000000000..79a62510ad3b --- /dev/null +++ b/docs/snippets/common/my-component-exclude-tags.story.ts.mdx @@ -0,0 +1,21 @@ +```ts +// MyComponent.stories.ts|tsx + +// Replace your-framework with the name of your framework +import type { Meta, StoryObj } from '@storybook/your-framework'; + +import { MyComponent } from './MyComponent'; + +const meta: Meta = { + component: MyComponent, + tags: ['no-tests'], // ๐Ÿ‘ˆ Provides the `no-tests` tag to all stories in this file +}; + +export default meta; +type Story = StoryObj; + +export const ExcludeStory: Story = { + //๐Ÿ‘‡ Adds the `no-tests` tag to this story to exclude it from the tests when enabled in the test-runner configuration + tags: ['no-tests'], +}; +``` diff --git a/docs/snippets/common/my-component-include-tags.story.js.mdx b/docs/snippets/common/my-component-include-tags.story.js.mdx new file mode 100644 index 000000000000..1e667e43c716 --- /dev/null +++ b/docs/snippets/common/my-component-include-tags.story.js.mdx @@ -0,0 +1,15 @@ +```js +// MyComponent.stories.js|jsx + +import { MyComponent } from './MyComponent'; + +export default { + component: MyComponent, + tags: ['test-only'], // ๐Ÿ‘ˆ Provides the `test-only` tag to all stories in this file +}; + +export const IncludeStory = { + //๐Ÿ‘‡ Adds the `test-only` tag to this story to be included in the tests when enabled in the test-runner configuration + tags: ['test-only'], +}; +``` diff --git a/docs/snippets/common/my-component-include-tags.story.ts.mdx b/docs/snippets/common/my-component-include-tags.story.ts.mdx new file mode 100644 index 000000000000..6b3f71446d8f --- /dev/null +++ b/docs/snippets/common/my-component-include-tags.story.ts.mdx @@ -0,0 +1,21 @@ +```ts +// MyComponent.stories.ts|tsx + +// Replace your-framework with the name of your framework +import type { Meta, StoryObj } from '@storybook/your-framework'; + +import { MyComponent } from './MyComponent'; + +const meta: Meta = { + component: MyComponent, + tags: ['test-only'], // ๐Ÿ‘ˆ Provides the `test-only` tag to all stories in this file +}; + +export default meta; +type Story = StoryObj; + +export const IncludeStory: Story = { + //๐Ÿ‘‡ Adds the `test-only` tag to this story to be included in the tests when enabled in the test-runner configuration + tags: ['test-only'], +}; +``` diff --git a/docs/snippets/common/my-component-skip-tags.story.js.mdx b/docs/snippets/common/my-component-skip-tags.story.js.mdx new file mode 100644 index 000000000000..c59991aace99 --- /dev/null +++ b/docs/snippets/common/my-component-skip-tags.story.js.mdx @@ -0,0 +1,15 @@ +```js +// MyComponent.stories.js|jsx + +import { MyComponent } from './MyComponent'; + +export default { + component: MyComponent, + tags: ['skip-test'], // ๐Ÿ‘ˆ Provides the `skip-test` tag to all stories in this file +}; + +export const SkipStory = { + //๐Ÿ‘‡ Adds the `skip-test` tag to this story to allow it to be skipped in the tests when enabled in the test-runner configuration + tags: ['skip-test'], +}; +``` diff --git a/docs/snippets/common/my-component-skip-tags.story.ts.mdx b/docs/snippets/common/my-component-skip-tags.story.ts.mdx new file mode 100644 index 000000000000..3513e3d33c43 --- /dev/null +++ b/docs/snippets/common/my-component-skip-tags.story.ts.mdx @@ -0,0 +1,21 @@ +```ts +// MyComponent.stories.ts|tsx + +// Replace your-framework with the name of your framework +import type { Meta, StoryObj } from '@storybook/your-framework'; + +import { MyComponent } from './MyComponent'; + +const meta: Meta = { + component: MyComponent, + tags: ['skip-test'], // ๐Ÿ‘ˆ Provides the `skip-test` tag to all stories in this file +}; + +export default meta; +type Story = StoryObj; + +export const SkipStory: Story = { + //๐Ÿ‘‡ Adds the `skip-test` tag to this story to allow it to be skipped in the tests when enabled in the test-runner configuration + tags: ['skip-test'], +}; +``` diff --git a/docs/snippets/common/storybook-build-test-flag.npm.js.mdx b/docs/snippets/common/storybook-build-test-flag.npm.js.mdx new file mode 100644 index 000000000000..202b2cdb3188 --- /dev/null +++ b/docs/snippets/common/storybook-build-test-flag.npm.js.mdx @@ -0,0 +1,3 @@ +```shell +npm run build-storybook -- --test +``` diff --git a/docs/snippets/common/storybook-build-test-flag.pnpm.js.mdx b/docs/snippets/common/storybook-build-test-flag.pnpm.js.mdx new file mode 100644 index 000000000000..ae1a481577d1 --- /dev/null +++ b/docs/snippets/common/storybook-build-test-flag.pnpm.js.mdx @@ -0,0 +1,3 @@ +```shell +pnpm run build-storybook --test +``` diff --git a/docs/snippets/common/storybook-build-test-flag.yarn.js.mdx b/docs/snippets/common/storybook-build-test-flag.yarn.js.mdx new file mode 100644 index 000000000000..cb2007d3e8b8 --- /dev/null +++ b/docs/snippets/common/storybook-build-test-flag.yarn.js.mdx @@ -0,0 +1,3 @@ +```shell +yarn build-storybook --test +``` diff --git a/docs/snippets/common/storybook-preview-sort-function.ts.mdx b/docs/snippets/common/storybook-preview-sort-function.ts.mdx index 3deb961445be..a0054ba7b8cd 100644 --- a/docs/snippets/common/storybook-preview-sort-function.ts.mdx +++ b/docs/snippets/common/storybook-preview-sort-function.ts.mdx @@ -7,6 +7,7 @@ import { Preview } from '@storybook/your-framework'; const preview: Preview = { parameters: { options: { + // The `a` and `b` arguments in this function have a type of `import('@storybook/types').IndexEntry`. Remember that the function is executed in a JavaScript environment, so use JSDoc for IntelliSense to introspect it. storySort: (a, b) => a.id === b.id ? 0 : a.id.localeCompare(b.id, undefined, { numeric: true }), }, diff --git a/docs/snippets/common/test-runner-a11y-config.js.mdx b/docs/snippets/common/test-runner-a11y-config.js.mdx index d7afb9b77456..9ce64c1e3f09 100644 --- a/docs/snippets/common/test-runner-a11y-config.js.mdx +++ b/docs/snippets/common/test-runner-a11y-config.js.mdx @@ -4,14 +4,14 @@ const { injectAxe, checkA11y } = require('axe-playwright'); /* - * See https://storybook.js.org/docs/react/writing-tests/test-runner#test-hook-api-experimental + * See https://storybook.js.org/docs/react/writing-tests/test-runner#test-hook-api * to learn more about the test-runner hooks API. */ module.exports = { - async preRender(page) { + async preVisit(page) { await injectAxe(page); }, - async postRender(page) { + async postVisit(page) { await checkA11y(page, '#storybook-root', { detailedReport: true, detailedReportOptions: { diff --git a/docs/snippets/common/test-runner-a11y-config.ts.mdx b/docs/snippets/common/test-runner-a11y-config.ts.mdx index 9339eb7e2ae0..4bc91082506f 100644 --- a/docs/snippets/common/test-runner-a11y-config.ts.mdx +++ b/docs/snippets/common/test-runner-a11y-config.ts.mdx @@ -1,19 +1,18 @@ ```ts // .storybook/test-runner.ts -import { injectAxe, checkA11y } from 'axe-playwright'; - import type { TestRunnerConfig } from '@storybook/test-runner'; +import { injectAxe, checkA11y } from 'axe-playwright'; /* - * See https://storybook.js.org/docs/react/writing-tests/test-runner#test-hook-api-experimental + * See https://storybook.js.org/docs/react/writing-tests/test-runner#test-hook-api * to learn more about the test-runner hooks API. */ -const a11yConfig: TestRunnerConfig = { - async preRender(page) { +const config: TestRunnerConfig = { + async preVisit(page) { await injectAxe(page); }, - async postRender(page) { + async postVisit(page) { await checkA11y(page, '#storybook-root', { detailedReport: true, detailedReportOptions: { @@ -23,5 +22,5 @@ const a11yConfig: TestRunnerConfig = { }, }; -module.exports = a11yConfig; +export default config; ``` diff --git a/docs/snippets/common/test-runner-a11y-configure.js.mdx b/docs/snippets/common/test-runner-a11y-configure.js.mdx index e7ddb352d5ea..abd0a20b4089 100644 --- a/docs/snippets/common/test-runner-a11y-configure.js.mdx +++ b/docs/snippets/common/test-runner-a11y-configure.js.mdx @@ -6,14 +6,14 @@ const { injectAxe, checkA11y, configureAxe } = require('axe-playwright'); const { getStoryContext } = require('@storybook/test-runner'); /* - * See https://storybook.js.org/docs/react/writing-tests/test-runner#test-hook-api-experimental + * See https://storybook.js.org/docs/react/writing-tests/test-runner#test-hook-api * to learn more about the test-runner hooks API. */ module.exports = { - async preRender(page) { + async preVisit(page) { await injectAxe(page); }, - async postRender(page, context) { + async postVisit(page, context) { // Get the entire context of a story, including parameters, args, argTypes, etc. const storyContext = await getStoryContext(page, context); diff --git a/docs/snippets/common/test-runner-a11y-configure.ts.mdx b/docs/snippets/common/test-runner-a11y-configure.ts.mdx index 8b1f4d31037e..5af07ea7d760 100644 --- a/docs/snippets/common/test-runner-a11y-configure.ts.mdx +++ b/docs/snippets/common/test-runner-a11y-configure.ts.mdx @@ -1,21 +1,20 @@ ```ts // .storybook/test-runner.ts -import { injectAxe, checkA11y, configureAxe } from 'axe-playwright'; - +import type { TestRunnerConfig } from '@storybook/test-runner'; import { getStoryContext } from '@storybook/test-runner'; -import type { TestRunnerConfig } from '@storybook/test-runner'; +import { injectAxe, checkA11y, configureAxe } from 'axe-playwright'; /* - * See https://storybook.js.org/docs/react/writing-tests/test-runner#test-hook-api-experimental + * See https://storybook.js.org/docs/react/writing-tests/test-runner#test-hook-api * to learn more about the test-runner hooks API. */ -const a11yConfig: TestRunnerConfig = { - async preRender(page) { +const config: TestRunnerConfig = { + async preVisit(page) { await injectAxe(page); }, - async postRender(page, context) { + async postVisit(page, context) { // Get the entire context of a story, including parameters, args, argTypes, etc. const storyContext = await getStoryContext(page, context); @@ -33,5 +32,5 @@ const a11yConfig: TestRunnerConfig = { }, }; -module.exports = a11yConfig; +export default config; ``` diff --git a/docs/snippets/common/test-runner-a11y-disable.js.mdx b/docs/snippets/common/test-runner-a11y-disable.js.mdx index edb12dab0bfa..d6177c9ce38f 100644 --- a/docs/snippets/common/test-runner-a11y-disable.js.mdx +++ b/docs/snippets/common/test-runner-a11y-disable.js.mdx @@ -1,19 +1,18 @@ ```js // .storybook/test-runner.js -const { injectAxe, checkA11y } = require('axe-playwright'); - const { getStoryContext } = require('@storybook/test-runner'); +const { injectAxe, checkA11y } = require('axe-playwright'); /* - * See https://storybook.js.org/docs/react/writing-tests/test-runner#test-hook-api-experimental + * See https://storybook.js.org/docs/react/writing-tests/test-runner#test-hook-api * to learn more about the test-runner hooks API. */ module.exports = { - async preRender(page) { + async preVisit(page) { await injectAxe(page); }, - async postRender(page, context) { + async postVisit(page, context) { // Get the entire context of a story, including parameters, args, argTypes, etc. const storyContext = await getStoryContext(page, context); diff --git a/docs/snippets/common/test-runner-a11y-disable.ts.mdx b/docs/snippets/common/test-runner-a11y-disable.ts.mdx index 3297e8ea0b7b..1be273fee210 100644 --- a/docs/snippets/common/test-runner-a11y-disable.ts.mdx +++ b/docs/snippets/common/test-runner-a11y-disable.ts.mdx @@ -1,21 +1,20 @@ ```ts // .storybook/test-runner.ts -import { injectAxe, checkA11y } from 'axe-playwright'; - +import type { TestRunnerConfig } from '@storybook/test-runner'; import { getStoryContext } from '@storybook/test-runner'; -import type { TestRunnerConfig } from '@storybook/test-runner'; +import { injectAxe, checkA11y } from 'axe-playwright'; /* - * See https://storybook.js.org/docs/react/writing-tests/test-runner#test-hook-api-experimental + * See https://storybook.js.org/docs/react/writing-tests/test-runner#test-hook-api * to learn more about the test-runner hooks API. */ -const a11yConfig: TestRunnerConfig = { - async preRender(page) { +const config: TestRunnerConfig = { + async preVisit(page) { await injectAxe(page); }, - async postRender(page, context) { + async postVisit(page, context) { // Get the entire context of a story, including parameters, args, argTypes, etc. const storyContext = await getStoryContext(page, context); @@ -32,5 +31,5 @@ const a11yConfig: TestRunnerConfig = { }, }; -module.exports = a11yConfig; +export default config; ``` diff --git a/docs/snippets/common/test-runner-auth.js.mdx b/docs/snippets/common/test-runner-auth.js.mdx new file mode 100644 index 000000000000..d4858de23165 --- /dev/null +++ b/docs/snippets/common/test-runner-auth.js.mdx @@ -0,0 +1,12 @@ +```js +// .storybook/test-runner.js + +module.exports = { + getHttpHeaders: async (url) => { + const token = url.includes('prod') ? 'XYZ' : 'ABC'; + return { + Authorization: `Bearer ${token}`, + }; + }, +}; +``` diff --git a/docs/snippets/common/test-runner-auth.ts.mdx b/docs/snippets/common/test-runner-auth.ts.mdx new file mode 100644 index 000000000000..c4fd82d8726e --- /dev/null +++ b/docs/snippets/common/test-runner-auth.ts.mdx @@ -0,0 +1,16 @@ +```ts +// .storybook/test-runner.ts + +import type { TestRunnerConfig } from '@storybook/test-runner'; + +const config: TestRunnerConfig = { + getHttpHeaders: async (url) => { + const token = url.includes('prod') ? 'prod-token' : 'dev-token'; + return { + Authorization: `Bearer ${token}`, + }; + }, +}; + +export default config; +``` diff --git a/docs/snippets/common/test-runner-custom-page-viewport.js.mdx b/docs/snippets/common/test-runner-custom-page-viewport.js.mdx new file mode 100644 index 000000000000..b334755d1f74 --- /dev/null +++ b/docs/snippets/common/test-runner-custom-page-viewport.js.mdx @@ -0,0 +1,32 @@ +```js +// .storybook/test-runner.js + +const { getStoryContext } = require('@storybook/test-runner'); +const { MINIMAL_VIEWPORTS } = require('@storybook/addon-viewport'); + +const DEFAULT_VIEWPORT_SIZE = { width: 1280, height: 720 }; + +module.exports = { + async preVisit(page, story) { + // Accesses the story's parameters and retrieves the viewport used to render it + const context = await getStoryContext(page, story); + const viewportName = context.parameters?.viewport?.defaultViewport; + const viewportParameter = MINIMAL_VIEWPORTS[viewportName]; + + if (viewportParameter) { + const viewportSize = Object.entries(viewportParameter.styles).reduce( + (acc, [screen, size]) => ({ + ...acc, + // Converts the viewport size from percentages to numbers + [screen]: parseInt(size), + }), + {} + ); + // Configures the Playwright page to use the viewport size + page.setViewportSize(viewportSize); + } else { + page.setViewportSize(DEFAULT_VIEWPORT_SIZE); + } + }, +}; +``` diff --git a/docs/snippets/common/test-runner-custom-page-viewport.ts.mdx b/docs/snippets/common/test-runner-custom-page-viewport.ts.mdx new file mode 100644 index 000000000000..5db491a51335 --- /dev/null +++ b/docs/snippets/common/test-runner-custom-page-viewport.ts.mdx @@ -0,0 +1,36 @@ +```ts +// .storybook/test-runner.js + +import type { TestRunnerConfig } from '@storybook/test-runner'; +import { getStoryContext } from '@storybook/test-runner'; + +const { MINIMAL_VIEWPORTS } = require('@storybook/addon-viewport'); + +const DEFAULT_VIEWPORT_SIZE = { width: 1280, height: 720 }; + +const config: TestRunnerConfig = { + async preVisit(page, story) { + // Accesses the story's parameters and retrieves the viewport used to render it + const context = await getStoryContext(page, story); + const viewportName = context.parameters?.viewport?.defaultViewport; + const viewportParameter = MINIMAL_VIEWPORTS[viewportName]; + + if (viewportParameter) { + const viewportSize = Object.entries(viewportParameter.styles).reduce( + (acc, [screen, size]) => ({ + ...acc, + // Converts the viewport size from percentages to numbers + [screen]: parseInt(size), + }), + {} + ); + // Configures the Playwright page to use the viewport size + page.setViewportSize(viewportSize); + } else { + page.setViewportSize(DEFAULT_VIEWPORT_SIZE); + } + }, +}; + +export default config; +``` diff --git a/docs/snippets/common/test-runner-helper-function.js.mdx b/docs/snippets/common/test-runner-helper-function.js.mdx index 6ce0e1bf4123..9e7827f029ab 100644 --- a/docs/snippets/common/test-runner-helper-function.js.mdx +++ b/docs/snippets/common/test-runner-helper-function.js.mdx @@ -1,28 +1,31 @@ ```js // .storybook/test-runner.js -const { getStoryContext } = require('@storybook/test-runner'); +const { getStoryContext, waitForPageReady } = require('@storybook/test-runner'); module.exports = { // Hook that is executed before the test runner starts running tests setup() { // Add your configuration here. }, - /* Hook to execute before a story is rendered. + /* Hook to execute before a story is initially visited before being rendered in the browser. * The page argument is the Playwright's page object for the story. * The context argument is a Storybook object containing the story's id, title, and name. */ - async preRender(page, context) { + async preVisit(page, context) { // Add your configuration here. }, - /* Hook to execute after a story is rendered. + /* Hook to execute after a story is visited and fully rendered. * The page argument is the Playwright's page object for the story * The context argument is a Storybook object containing the story's id, title, and name. */ - async postRender(page, context) { + async postVisit(page, context) { // Get the entire context of a story, including parameters, args, argTypes, etc. const storyContext = await getStoryContext(page, context); + // This utility function is designed for image snapshot testing. It will wait for the page to be fully loaded, including all the async items (e.g., images, fonts, etc.). + await waitForPageReady(page); + // Add your configuration here. }, }; diff --git a/docs/snippets/common/test-runner-helper-function.ts.mdx b/docs/snippets/common/test-runner-helper-function.ts.mdx index 94a9824a68a0..af40e58b2dee 100644 --- a/docs/snippets/common/test-runner-helper-function.ts.mdx +++ b/docs/snippets/common/test-runner-helper-function.ts.mdx @@ -2,32 +2,34 @@ // .storybook/test-runner.ts import type { TestRunnerConfig } from '@storybook/test-runner'; - -import { getStoryContext } from '@storybook/test-runner'; +import { getStoryContext, waitForPageReady } from '@storybook/test-runner'; const config: TestRunnerConfig = { // Hook that is executed before the test runner starts running tests setup() { // Add your configuration here. }, - /* Hook to execute before a story is rendered. + /* Hook to execute before a story is initially visited before being rendered in the browser. * The page argument is the Playwright's page object for the story. * The context argument is a Storybook object containing the story's id, title, and name. */ - async preRender(page, context) { + async preVisit(page, context) { // Add your configuration here. }, - /* Hook to execute after a story is rendered. + /* Hook to execute after a story is visited and fully rendered. * The page argument is the Playwright's page object for the story * The context argument is a Storybook object containing the story's id, title, and name. */ - async postRender(page, context) { + async postVisit(page, context) { // Get the entire context of a story, including parameters, args, argTypes, etc. const storyContext = await getStoryContext(page, context); + // This utility function is designed for image snapshot testing. It will wait for the page to be fully loaded, including all the async items (e.g., images, fonts, etc.). + await waitForPageReady(page); + // Add your configuration here. }, }; -module.exports = config; +export default config; ``` diff --git a/docs/snippets/common/test-runner-hooks-example.js.mdx b/docs/snippets/common/test-runner-hooks-example.js.mdx index e3d52b738645..18876266bf31 100644 --- a/docs/snippets/common/test-runner-hooks-example.js.mdx +++ b/docs/snippets/common/test-runner-hooks-example.js.mdx @@ -6,18 +6,18 @@ module.exports = { setup() { // Add your configuration here. }, - /* Hook to execute before a story is rendered. + /* Hook to execute before a story is initially visited before being rendered in the browser. * The page argument is the Playwright's page object for the story. * The context argument is a Storybook object containing the story's id, title, and name. */ - async preRender(page, context) { + async preVisit(page, context) { // Add your configuration here. }, - /* Hook to execute after a story is rendered. + /* Hook to execute after a story is visited and fully rendered. * The page argument is the Playwright's page object for the story * The context argument is a Storybook object containing the story's id, title, and name. */ - async postRender(page, context) { + async postVisit(page, context) { // Add your configuration here. }, }; diff --git a/docs/snippets/common/test-runner-hooks-example.ts.mdx b/docs/snippets/common/test-runner-hooks-example.ts.mdx index d05ddb64801c..f06d4c71b063 100644 --- a/docs/snippets/common/test-runner-hooks-example.ts.mdx +++ b/docs/snippets/common/test-runner-hooks-example.ts.mdx @@ -8,21 +8,21 @@ const config: TestRunnerConfig = { setup() { // Add your configuration here. }, - /* Hook to execute before a story is rendered. + /* Hook to execute before a story is initially visited before being rendered in the browser. * The page argument is the Playwright's page object for the story. * The context argument is a Storybook object containing the story's id, title, and name. */ - async preRender(page, context) { + async preVisit(page, context) { // Add your configuration here. }, - /* Hook to execute after a story is rendered. + /* Hook to execute after a story is visited and fully rendered. * The page argument is the Playwright's page object for the story * The context argument is a Storybook object containing the story's id, title, and name. */ - async postRender(page, context) { + async postVisit(page, context) { // Add your configuration here. }, }; -module.exports = config; +export default config; ``` diff --git a/docs/snippets/common/test-runner-tags-config.js.mdx b/docs/snippets/common/test-runner-tags-config.js.mdx new file mode 100644 index 000000000000..6ebfbb6c9fb4 --- /dev/null +++ b/docs/snippets/common/test-runner-tags-config.js.mdx @@ -0,0 +1,11 @@ +```js +// .storybook/test-runner.js + +module.exports = { + tags: { + include: ['test-only', 'pages'], + exclude: ['no-tests', 'tokens'], + skip: ['skip-test', 'layout'], + }, +}; +``` diff --git a/docs/snippets/common/test-runner-tags-config.ts.mdx b/docs/snippets/common/test-runner-tags-config.ts.mdx new file mode 100644 index 000000000000..023b13e77cc9 --- /dev/null +++ b/docs/snippets/common/test-runner-tags-config.ts.mdx @@ -0,0 +1,15 @@ +```ts +// .storybook/test-runner.ts + +import type { TestRunnerConfig } from '@storybook/test-runner'; + +const config: TestRunnerConfig = { + tags: { + include: ['test-only', 'pages'], + exclude: ['no-tests', 'tokens'], + skip: ['skip-test', 'layout'], + }, +}; + +export default config; +``` diff --git a/docs/snippets/common/test-runner-tags-exclude.config.js.mdx b/docs/snippets/common/test-runner-tags-exclude.config.js.mdx new file mode 100644 index 000000000000..5b73bb9bdf60 --- /dev/null +++ b/docs/snippets/common/test-runner-tags-exclude.config.js.mdx @@ -0,0 +1,9 @@ +```js +// .storybook/test-runner.js + +module.exports = { + tags: { + exclude: ['no-tests'], + }, +}; +``` diff --git a/docs/snippets/common/test-runner-tags-exclude.config.ts.mdx b/docs/snippets/common/test-runner-tags-exclude.config.ts.mdx new file mode 100644 index 000000000000..9712292fe33c --- /dev/null +++ b/docs/snippets/common/test-runner-tags-exclude.config.ts.mdx @@ -0,0 +1,13 @@ +```ts +// .storybook/test-runner.ts + +import type { TestRunnerConfig } from '@storybook/test-runner'; + +const config: TestRunnerConfig = { + tags: { + exclude: ['no-tests'], + }, +}; + +export default config; +``` diff --git a/docs/snippets/common/test-runner-tags-include.config.js.mdx b/docs/snippets/common/test-runner-tags-include.config.js.mdx new file mode 100644 index 000000000000..2b51222fed40 --- /dev/null +++ b/docs/snippets/common/test-runner-tags-include.config.js.mdx @@ -0,0 +1,9 @@ +```js +// .storybook/test-runner.js + +module.exports = { + tags: { + include: ['test-only'], + }, +}; +``` diff --git a/docs/snippets/common/test-runner-tags-include.config.ts.mdx b/docs/snippets/common/test-runner-tags-include.config.ts.mdx new file mode 100644 index 000000000000..b69e402aff92 --- /dev/null +++ b/docs/snippets/common/test-runner-tags-include.config.ts.mdx @@ -0,0 +1,13 @@ +```ts +// .storybook/test-runner.ts + +import type { TestRunnerConfig } from '@storybook/test-runner'; + +const config: TestRunnerConfig = { + tags: { + include: ['test-only'], + }, +}; + +export default config; +``` diff --git a/docs/snippets/common/test-runner-tags-skip.config.js.mdx b/docs/snippets/common/test-runner-tags-skip.config.js.mdx new file mode 100644 index 000000000000..bc82ef46d752 --- /dev/null +++ b/docs/snippets/common/test-runner-tags-skip.config.js.mdx @@ -0,0 +1,9 @@ +```js +// .storybook/test-runner.js + +module.exports = { + tags: { + skip: ['skip-test'], + }, +}; +``` diff --git a/docs/snippets/common/test-runner-tags-skip.config.ts.mdx b/docs/snippets/common/test-runner-tags-skip.config.ts.mdx new file mode 100644 index 000000000000..37a03bf7b1c4 --- /dev/null +++ b/docs/snippets/common/test-runner-tags-skip.config.ts.mdx @@ -0,0 +1,13 @@ +```ts +// .storybook/test-runner.ts + +import type { TestRunnerConfig } from '@storybook/test-runner'; + +const config: TestRunnerConfig = { + tags: { + skip: ['skip-test'], + }, +}; + +export default config; +``` diff --git a/docs/snippets/common/test-runner-waitpageready.js.mdx b/docs/snippets/common/test-runner-waitpageready.js.mdx new file mode 100644 index 000000000000..1bb3df563c00 --- /dev/null +++ b/docs/snippets/common/test-runner-waitpageready.js.mdx @@ -0,0 +1,25 @@ +```js +// .storybook/test-runner.js + +const { waitForPageReady } = require('@storybook/test-runner'); +const { toMatchImageSnapshot } = require('jest-image-snapshot'); + +const customSnapshotsDir = `${process.cwd()}/__snapshots__`; + +module.exports = { + setup() { + expect.extend({ toMatchImageSnapshot }); + }, + async postVisit(page, context) { + // Awaits for the page to be loaded and available including assets (e.g., fonts) + await waitForPageReady(page); + + // Generates a snapshot file based on the story identifier + const image = await page.screenshot(); + expect(image).toMatchImageSnapshot({ + customSnapshotsDir, + customSnapshotIdentifier: context.id, + }); + }, +}; +``` diff --git a/docs/snippets/common/test-runner-waitpageready.ts.mdx b/docs/snippets/common/test-runner-waitpageready.ts.mdx new file mode 100644 index 000000000000..2da14b2c7185 --- /dev/null +++ b/docs/snippets/common/test-runner-waitpageready.ts.mdx @@ -0,0 +1,29 @@ +```ts +// .storybook/test-runner.ts + +import type { TestRunnerConfig } from '@storybook/test-runner'; +import { waitForPageReady } from '@storybook/test-runner'; + +import { toMatchImageSnapshot } from 'jest-image-snapshot'; + +const customSnapshotsDir = `${process.cwd()}/__snapshots__`; + +const config: TestRunnerConfig = { + setup() { + expect.extend({ toMatchImageSnapshot }); + }, + async postVisit(page, context) { + // Awaits for the page to be loaded and available including assets (e.g., fonts) + await waitForPageReady(page); + + // Generates a snapshot file based on the story identifier + const image = await page.screenshot(); + expect(image).toMatchImageSnapshot({ + customSnapshotsDir, + customSnapshotIdentifier: context.id, + }); + }, +}; + +export default config; +``` diff --git a/docs/toc.js b/docs/toc.js index 9c773a493244..13d66fee3bab 100644 --- a/docs/toc.js +++ b/docs/toc.js @@ -506,6 +506,11 @@ module.exports = { title: 'babelDefault', pathSegment: 'main-config-babel-default', type: 'link', + }, + { + title: 'build', + pathSegment: 'main-config-build', + type: 'link', }, { title: 'core', diff --git a/docs/versions/next.json b/docs/versions/next.json index 3b203dc04f4a..2c346435e0e0 100644 --- a/docs/versions/next.json +++ b/docs/versions/next.json @@ -1 +1 @@ -{"version":"7.6.0-alpha.6","info":{"plain":"- Addon: Move Visual Test addon to the code directory - [#24771](https://github.com/storybookjs/storybook/pull/24771), thanks [@cdedreuille](https://github.com/cdedreuille)!\n- FastBuild: Improve config loading & naming - [#24837](https://github.com/storybookjs/storybook/pull/24837), thanks [@ndelangen](https://github.com/ndelangen)!\n- TestBuild: Revert defaulting to SWC in test build, but keep using esbuild for minification - [#24843](https://github.com/storybookjs/storybook/pull/24843), thanks [@kasperpeulen](https://github.com/kasperpeulen)!\n- Viewport: Add newer device viewports - [#24777](https://github.com/storybookjs/storybook/pull/24777), thanks [@Tomo5524](https://github.com/Tomo5524)!\n- Vite: Prevent non-deterministic build output - [#24833](https://github.com/storybookjs/storybook/pull/24833), thanks [@henkerik](https://github.com/henkerik)!"}} +{"version":"7.6.0-alpha.7","info":{"plain":"- Actions: Warn on implicit actions - [#24856](https://github.com/storybookjs/storybook/pull/24856), thanks [@kasperpeulen](https://github.com/kasperpeulen)!\n- Addons, core: Make `react` and Storybook packages `devDependencies` where possible - ATTEMPT 2 - [#24834](https://github.com/storybookjs/storybook/pull/24834), thanks [@JReinhold](https://github.com/JReinhold)!\n- CLI: Add \\\"doctor\\\" command - [#22236](https://github.com/storybookjs/storybook/pull/22236), thanks [@yannbf](https://github.com/yannbf)!\n- Core: Add deprecation notice for Vite + CommonJS - [#23950](https://github.com/storybookjs/storybook/pull/23950), thanks [@JReinhold](https://github.com/JReinhold)!\n- Core: Detect no matching export error in storybook start and build - [#24877](https://github.com/storybookjs/storybook/pull/24877), thanks [@yannbf](https://github.com/yannbf)!\n- Core: Fix `useStoryPrepared` hook failing with `undefined` data - [#22631](https://github.com/storybookjs/storybook/pull/22631), thanks [@SpookyJelly](https://github.com/SpookyJelly)!\n- Core: Gracefully handle error when parsing preview.js file - [#24858](https://github.com/storybookjs/storybook/pull/24858), thanks [@yannbf](https://github.com/yannbf)!\n- Core: Make warnOnIncompatibleAddons fault-tolerant - [#24880](https://github.com/storybookjs/storybook/pull/24880), thanks [@taozhou-glean](https://github.com/taozhou-glean)!\n- Dependency: Fix Yarn 4 failing to install due to jscodeshift dependency issue - [#24914](https://github.com/storybookjs/storybook/pull/24914), thanks [@samvv](https://github.com/samvv)!\n- Dependency: Update jscodeshift to v0.15.1 - [#24882](https://github.com/storybookjs/storybook/pull/24882), thanks [@epreston](https://github.com/epreston)!\n- FastBuild: Fix disabledAddons filter - [#24924](https://github.com/storybookjs/storybook/pull/24924), thanks [@IanVS](https://github.com/IanVS)!\n- ManagerAPI: Fix setting status without index, crashes storybook - [#24866](https://github.com/storybookjs/storybook/pull/24866), thanks [@ndelangen](https://github.com/ndelangen)!\n- Next.js: Add back image context CommonJS export - [#24885](https://github.com/storybookjs/storybook/pull/24885), thanks [@martinnabhan](https://github.com/martinnabhan)!\n- Next.js: Add experimental SWC support - [#24852](https://github.com/storybookjs/storybook/pull/24852), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!\n- Next.js: Fix import path in swc loader - [#24922](https://github.com/storybookjs/storybook/pull/24922), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!\n- Svelte: Fix decorators always running twice - [#24921](https://github.com/storybookjs/storybook/pull/24921), thanks [@paoloricciuti](https://github.com/paoloricciuti)!\n- SvelteKit: Add experimental page and navigation mocking - [#24795](https://github.com/storybookjs/storybook/pull/24795), thanks [@paoloricciuti](https://github.com/paoloricciuti)!\n- Test: Model loaders as before each and restore mocks properly - [#24948](https://github.com/storybookjs/storybook/pull/24948), thanks [@kasperpeulen](https://github.com/kasperpeulen)!\n- TestBuild: Add env-variable support to `--test` CLI-flag - [#24862](https://github.com/storybookjs/storybook/pull/24862), thanks [@ndelangen](https://github.com/ndelangen)!\n- TestBuild: Add tests and rename to camelCase - [#24911](https://github.com/storybookjs/storybook/pull/24911), thanks [@ndelangen](https://github.com/ndelangen)!\n- TestBuild: Fix indexer bug - [#24890](https://github.com/storybookjs/storybook/pull/24890), thanks [@ndelangen](https://github.com/ndelangen)!\n- Typescript: Add 'skipCompiler' option to TypeScript presets - [#24847](https://github.com/storybookjs/storybook/pull/24847), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!"}} diff --git a/docs/writing-tests/accessibility-testing.md b/docs/writing-tests/accessibility-testing.md index 5d404fb3248f..36c17f89860c 100644 --- a/docs/writing-tests/accessibility-testing.md +++ b/docs/writing-tests/accessibility-testing.md @@ -89,7 +89,7 @@ Out of the box, Storybook's accessibility addon includes a set of accessibility #### Global a11y configuration -If you need to dismiss an accessibility rule or modify its settings across all stories, you can add the following to your [storybook/preview.js](../configure/overview.md#configure-story-rendering): +If you need to dismiss an accessibility rule or modify its settings across all stories, you can add the following to your [`storybook/preview.js|ts`](../configure/overview.md#configure-story-rendering): @@ -170,7 +170,7 @@ Disable accessibility testing for stories or components by adding the following The most accurate way to check accessibility is manually on real devices. However, you can use automated tools to catch common accessibility issues. For example, [Axe](https://www.deque.com/axe/), on average, catches upwards to [57% of WCAG issues](https://www.deque.com/blog/automated-testing-study-identifies-57-percent-of-digital-accessibility-issues/) automatically. -These tools work by auditing the rendered DOM against heuristics based on [WCAG](https://www.w3.org/WAI/standards-guidelines/wcag/) rules and other industry-accepted best practices. You can then integrate these tools into your test automation pipeline using the Storybook [test runner](./test-runner.md#test-hook-api-experimental) and [axe-playwright](https://github.com/abhinaba-ghosh/axe-playwright). +These tools work by auditing the rendered DOM against heuristics based on [WCAG](https://www.w3.org/WAI/standards-guidelines/wcag/) rules and other industry-accepted best practices. You can then integrate these tools into your test automation pipeline using the Storybook [test runner](./test-runner.md#test-hook-api) and [axe-playwright](https://github.com/abhinaba-ghosh/axe-playwright). ### Setup @@ -190,7 +190,7 @@ Run the following command to install the required dependencies. -Add a new [configuration file](./test-runner.md#test-hook-api-experimental) inside your Storybook directory with the following inside: +Add a new [configuration file](./test-runner.md#test-hook-api) inside your Storybook directory with the following inside: @@ -205,7 +205,7 @@ Add a new [configuration file](./test-runner.md#test-hook-api-experimental) insi -`preRender` and `postRender` are convenient hooks that allow you to extend the test runner's default configuration. They are **experimental** and subject to changes. Read more about them [here](./test-runner.md#test-hook-api-experimental). +`preVisit` and `postVisit` are convenient hooks that allow you to extend the test runner's default configuration. Read more about them [here](./test-runner.md#test-hook-api). diff --git a/docs/writing-tests/test-runner.md b/docs/writing-tests/test-runner.md index 96b08331cd75..531fa1d789c8 100644 --- a/docs/writing-tests/test-runner.md +++ b/docs/writing-tests/test-runner.md @@ -81,30 +81,33 @@ Test runner offers zero-config support for Storybook. However, you can run `test The test-runner is powered by [Jest](https://jestjs.io/) and accepts a subset of its [CLI options](https://jestjs.io/docs/cli) (for example, `--watch`, `--maxWorkers`). If you're already using any of those flags in your project, you should be able to migrate them into Storybook's test-runner without any issues. Listed below are all the available flags and examples of using them. -| Options | Description | -| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `--help` | Output usage information
`test-storybook --help` | -| `-s`, `--index-json` | Run in index json mode. Automatically detected (requires a compatible Storybook)
`test-storybook --index-json` | -| `--no-index-json` | Disables index json mode
`test-storybook --no-index-json` | -| `-c`, `--config-dir [dir-name]` | Directory where to load Storybook configurations from
`test-storybook -c .storybook` | -| `--watch` | Run in watch mode
`test-storybook --watch` | -| `--watchAll` | Watch files for changes and rerun all tests when something changes.
`test-storybook --watchAll` | -| `--coverage` | Runs [coverage tests](./test-coverage.md) on your stories and components
`test-storybook --coverage` | -| `--coverageDirectory` | Directory where to write coverage report output
`test-storybook --coverage --coverageDirectory coverage/ui/storybook` | -| `--url` | Define the URL to run tests in. Useful for custom Storybook URLs
`test-storybook --url http://the-storybook-url-here.com` | -| `--browsers` | Define browsers to run tests in. One or multiple of: chromium, firefox, webkit
`test-storybook --browsers firefox chromium` | -| `--maxWorkers [amount]` | Specifies the maximum number of workers the worker-pool will spawn for running tests
`test-storybook --maxWorkers=2` | -| `--no-cache` | Disable the cache
`test-storybook --no-cache` | -| `--clearCache` | Deletes the Jest cache directory and then exits without running tests
`test-storybook --clearCache` | -| `--verbose` | Display individual test results with the test suite hierarchy
`test-storybook --verbose` | -| `-u`, `--updateSnapshot` | Use this flag to re-record every snapshot that fails during this test run
`test-storybook -u` | -| `--eject` | Creates a local configuration file to override defaults of the test-runner
`test-storybook --eject` | -| `--json` | Prints the test results in JSON. This mode will send all other test output and user messages to stderr.
`test-storybook --json` | -| `--outputFile` | Write test results to a file when the --json option is also specified.
`test-storybook --json --outputFile results.json` | -| `--junit` | Indicates that test information should be reported in a junit file.
`test-storybook --**junit**` | -| `--ci` | Instead of the regular behavior of storing a new snapshot automatically, it will fail the test and require Jest to be run with `--updateSnapshot`.
`test-storybook --ci` | -| `--shard [index/count]` | Requires CI. Splits the test suite execution into multiple machines
`test-storybook --shard=1/8` | -| `--failOnConsole` | Makes tests fail on browser console errors
`test-storybook --failOnConsole` | +| Options | Description | +| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `--help` | Output usage information
`test-storybook --help` | +| `-s`, `--index-json` | Run in index json mode. Automatically detected (requires a compatible Storybook)
`test-storybook --index-json` | +| `--no-index-json` | Disables index json mode
`test-storybook --no-index-json` | +| `-c`, `--config-dir [dir-name]` | Directory where to load Storybook configurations from
`test-storybook -c .storybook` | +| `--watch` | Run in watch mode
`test-storybook --watch` | +| `--watchAll` | Watch files for changes and rerun all tests when something changes.
`test-storybook --watchAll` | +| `--coverage` | Runs [coverage tests](./test-coverage.md) on your stories and components
`test-storybook --coverage` | +| `--coverageDirectory` | Directory where to write coverage report output
`test-storybook --coverage --coverageDirectory coverage/ui/storybook` | +| `--url` | Define the URL to run tests in. Useful for custom Storybook URLs
`test-storybook --url http://the-storybook-url-here.com` | +| `--browsers` | Define browsers to run tests in. One or multiple of: chromium, firefox, webkit
`test-storybook --browsers firefox chromium` | +| `--maxWorkers [amount]` | Specifies the maximum number of workers the worker-pool will spawn for running tests
`test-storybook --maxWorkers=2` | +| `--no-cache` | Disable the cache
`test-storybook --no-cache` | +| `--clearCache` | Deletes the Jest cache directory and then exits without running tests
`test-storybook --clearCache` | +| `--verbose` | Display individual test results with the test suite hierarchy
`test-storybook --verbose` | +| `-u`, `--updateSnapshot` | Use this flag to re-record every snapshot that fails during this test run
`test-storybook -u` | +| `--eject` | Creates a local configuration file to override defaults of the test-runner
`test-storybook --eject` | +| `--json` | Prints the test results in JSON. This mode will send all other test output and user messages to stderr.
`test-storybook --json` | +| `--outputFile` | Write test results to a file when the --json option is also specified.
`test-storybook --json --outputFile results.json` | +| `--junit` | Indicates that test information should be reported in a junit file.
`test-storybook --**junit**` | +| `--ci` | Instead of the regular behavior of storing a new snapshot automatically, it will fail the test and require Jest to be run with `--updateSnapshot`.
`test-storybook --ci` | +| `--shard [index/count]` | Requires CI. Splits the test suite execution into multiple machines
`test-storybook --shard=1/8` | +| `--failOnConsole` | Makes tests fail on browser console errors
`test-storybook --failOnConsole` | +| `--includeTags` | Experimental feature
Defines a subset of stories to be tested if they match the enabled [tags](#experimental-filter-tests).
`test-storybook --includeTags="test-only, pages"` | +| `--excludeTags` | Experimental feature
Prevents stories from being tested if they match the provided [tags](#experimental-filter-tests).
`test-storybook --excludeTags="no-tests, tokens"` | +| `--skipTags` | Experimental feature
Configures the test runner to skip running tests for stories that match the provided [tags](#experimental-filter-tests).
`test-storybook --skipTags="skip-test, layout"` | @@ -141,7 +144,7 @@ You can also configure the test-runner to run tests on a CI environment. Documen ### Run against deployed Storybooks via Github Actions deployment -If you're publishing your Storybook with services such as [Vercel](https://vercel.com/) or [Netlify](https://www.netlify.com/), they emit a `deployment_status` event in GitHub Actions. You can use it and set the `deployment_status.target_url` as the `TARGET_URL` environment variable. Here's how: +If you're publishing your Storybook with services such as [Vercel](https://vercel.com/) or [Netlify](https://docs.netlify.com/site-deploys/notifications/#github-commit-statuses), they emit a `deployment_status` event in GitHub Actions. You can use it and set the `deployment_status.target_url` as the `TARGET_URL` environment variable. Here's how: @@ -175,7 +178,7 @@ You can use your CI provider (for example, [GitHub Actions](https://github.com/f -By default Storybook outputs the [build](../sharing/publish-storybook.md#build-storybook-as-a-static-web-application) to the `storybook-static` directory. If you're using a different build directory, you'll need to adjust the recipe accordingly. +By default, Storybook outputs the [build](../sharing/publish-storybook.md#build-storybook-as-a-static-web-application) to the `storybook-static` directory. If you're using a different build directory, you'll need to adjust the recipe accordingly. @@ -192,19 +195,19 @@ However, you might want to pair the test runner and Chromatic in some cases. ## Advanced configuration -### Test hook API (experimental) +### Test hook API The test-runner renders a story and executes its [play function](../writing-stories/play-function.md) if one exists. However, certain behaviors are impossible to achieve via the play function, which executes in the browser. For example, if you want the test-runner to take visual snapshots for you, this is possible via Playwright/Jest but must be executed in Node. 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() {}` | -| `preRender` | Executes before a story is rendered
`async preRender(page, context) {}` | -| `postRender` | Executes after the story is rendered
`async postRender(page, context) {}` | +| 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) {}` | @@ -227,7 +230,7 @@ To enable the hooks API, you'll need to add a new configuration file inside your -Except for the `setup` function, all other functions run asynchronously. Both `preRender` and `postRender` functions include two additional arguments, a [Playwright page](https://playwright.dev/docs/pages) and a context object which contains the `id`, `title`, and the `name` of the story. +Except for the `setup` function, all other functions run asynchronously. Both `preVisit` and `postVisit` functions include two additional arguments, a [Playwright page](https://playwright.dev/docs/pages) and a context object which contains the `id`, `title`, and the `name` of the story. @@ -236,9 +239,108 @@ When the test-runner executes, your existing tests will go through the following - The `setup` function is executed before all the tests run. - The context object is generated containing the required information. - Playwright navigates to the story's page. -- The `preRender` function is executed. +- The `preVisit` function is executed. - The story is rendered, and any existing `play` functions are executed. -- The `postRender` function is executed. +- The `postVisit` function is executed. + +### (Experimental) Filter tests + +When you run the test-runner on Storybook, it tests every story by default. However, if you want to filter the tests, you can use the `tags` configuration option. Storybook originally introduced this feature to generate [automatic documentation](../writing-docs/autodocs.md) for stories. But it can be further extended to configure the test-runner to run tests according to the provided tags using a similar configuration option or via CLI flags (e.g., `--includeTags`, `--excludeTags`, `--skipTags`), only available with the latest stable release (`0.15` or higher). Listed below are the available options and an overview of how to use them. + +| Option | Description | +| --------- | ----------------------------------------------------------------------------- | +| `exclude` | Prevents stories if they match the provided tags from being tested. | +| `include` | Defines a subset of stories only to be tested if they match the enabled tags. | +| `skip` | Skips testing on stories if they match the provided tags. | + + + + + + + + + +Running tests with the CLI flags takes precedence over the options provided in the configuration file and will override the available options in the configuration file. + + + +#### Disabling tests + +If you want to prevent specific stories from being tested by the test-runner, you can configure your story with a custom tag, enable it to the test-runner configuration file or run the test-runner with the `--excludeTags` [CLI](#cli-options) flag and exclude them from testing. This is helpful when you want to exclude stories that are not yet ready for testing or are irrelevant to your tests. For example: + + + + + + + +#### Run tests for a subset of stories + +To allow the test-runner only to run tests on a specific story or subset of stories, you can configure the story with a custom tag, enable it in the test-runner configuration file or run the test-runner with the `--includeTags` [CLI](#cli-options) flag and include them in your tests. For example, if you wanted to run tests based on the `test-only` tag, you can adjust your configuration as follows: + + + + + + + + + +Applying tags for the component's stories should either be done at the component level (using `meta`) or at the story level. Importing tags across stories is not supported in Storybook and won't work as intended. + + + +#### Skip tests + +If you want to skip running tests on a particular story or subset of stories, you can configure your story with a custom tag, enable it in the test-runner configuration file, or run the test-runner with the `--skipTags` [CLI](#cli-options) flag. Running tests with this option will cause the test-runner to ignore and flag them accordingly in the test results, indicating that the tests are temporarily disabled. For example: + + + + + + + +### Authentication for deployed Storybooks + +If you use a secure hosting provider that requires authentication to host your Storybook, you may need to set HTTP headers. This is mainly because of how the test runner checks the status of the instance and the index of its stories through fetch requests and Playwright. To do this, you can modify the test-runner configuration file to include the `getHttpHeaders` function. This function takes the URL of the fetch calls and page visits as input and returns an object containing the headers that need to be set. + + + + + + ### Helpers @@ -255,6 +357,36 @@ The test-runner exports a few helpers that can be used to make your tests more r +#### Accessing story information with the test-runner + +If you need to access information about the story, such as its parameters, the test-runner includes a helper function named `getStoryContext` that you can use to retrieve it. You can then use it to customize your tests further as needed. For example, if you need to configure Playwright's page [viewport size](https://playwright.dev/docs/api/class-page#page-set-viewport-size) to use the viewport size defined in the story's parameters, you can do so as follows: + + + + + + + +#### Working with assets + +If you're running a specific set of tests (e.g., image snapshot testing), the test-runner provides a helper function named `waitForPageReady` that you can use to ensure the page is fully loaded and ready before running the test. For example: + + + + + + + ### Index.json mode The test-runner transforms your story files into tests when testing a local Storybook. For a remote Storybook, it uses the Storybook's [index.json](../configure/overview.md#feature-flags) (formerly `stories.json`) file (a static index of all the stories) to run the tests. @@ -277,7 +409,7 @@ Suppose you run into a situation where the local and remote Storybooks appear ou -The `index.json` mode is not compatible with watch mode. +The `index.json` mode is not compatible with the watch mode. @@ -329,6 +461,10 @@ By default, the test runner truncates error outputs at 1000 characters, and you As the test runner is based on Playwright, you might need to use specific docker images or other configurations depending on your CI setup. In that case, you can refer to the [Playwright CI docs](https://playwright.dev/docs/ci) for more information. +### Tests filtered by tags are incorrectly executed + +If you've enabled filtering tests with tags and provided similar tags to the `include` and `exclude` lists, the test-runner will execute the tests based on the `exclude` list and ignore the `include` list. To avoid this, make sure the tags provided to the `include` and `exclude` lists differ. + #### Learn about other UI tests - Test runner to automate test execution diff --git a/package.json b/package.json index 7564dbbfd79f..3e7e7cadb807 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "nx": "cd code; yarn nx", "pretty-docs": "cd scripts; yarn install >/dev/null; yarn docs:prettier:write", "start": "yarn task --task dev --template react-vite/default-ts --start-from=install", - "task": "echo 'Installing Script Dependencies...'; cd scripts; yarn install >/dev/null; yarn task", + "task": "echo 'Installing Script Dependencies...'; cd scripts; yarn install >/dev/null; cd ..; yarn --cwd=./scripts task", "test": "cd code; yarn test", "upload-bench": "cd scripts; yarn upload-bench" }, diff --git a/scripts/.babelrc.js b/scripts/.babelrc.cjs similarity index 100% rename from scripts/.babelrc.js rename to scripts/.babelrc.cjs diff --git a/scripts/.eslintignore b/scripts/.eslintignore index db97fa57090f..9224e493860f 100644 --- a/scripts/.eslintignore +++ b/scripts/.eslintignore @@ -21,6 +21,6 @@ ember-output !.babelrc.js !.eslintrc.js !.eslintrc-markdown.js -!.jest.config.js +!.jest.config.cjs !.storybook diff --git a/scripts/.eslintrc.js b/scripts/.eslintrc.cjs similarity index 100% rename from scripts/.eslintrc.js rename to scripts/.eslintrc.cjs diff --git a/scripts/build-package.ts b/scripts/build-package.ts index 5a2d43f79e76..7ee1002f65cc 100644 --- a/scripts/build-package.ts +++ b/scripts/build-package.ts @@ -1,13 +1,11 @@ -#!/usr/bin/env node - -/* eslint-disable global-require */ import { resolve, posix, sep } from 'path'; import { readJSON } from 'fs-extra'; import prompts from 'prompts'; import program from 'commander'; import chalk from 'chalk'; +import windowSize from 'window-size'; +import { execaCommand } from 'execa'; import { getWorkspaces } from './utils/workspace'; -import { getExeca } from './utils/exec'; async function run() { const packages = await getWorkspaces(); @@ -95,7 +93,7 @@ async function run() { min: 1, hint: 'You can also run directly with package name like `yarn build core`, or `yarn build --all` for all packages!', // @ts-expect-error @types incomplete - optionsPerPage: require('window-size').height - 3, // 3 lines for extra info + optionsPerPage: windowSize.height - 3, // 3 lines for extra info choices: packages.map(({ name: key }) => ({ value: key, title: tasks[key].name || key, @@ -117,15 +115,13 @@ async function run() { } selection?.filter(Boolean).forEach(async (v) => { - const commmand = (await readJSON(resolve(v.location, 'package.json'))).scripts.prep + const commmand = (await readJSON(resolve('../code', v.location, 'package.json'))).scripts.prep .split(posix.sep) .join(sep); const cwd = resolve(__dirname, '..', 'code', v.location); - const tsNode = require.resolve('ts-node/dist/bin'); - const execa = await getExeca(); - const sub = execa.execaCommand( - `node ${tsNode} ${commmand}${watchMode ? ' --watch' : ''}${prodMode ? ' --optimized' : ''}`, + const sub = execaCommand( + `${commmand}${watchMode ? ' --watch' : ''}${prodMode ? ' --optimized' : ''}`, { cwd, buffer: false, diff --git a/scripts/check-dependencies.js b/scripts/check-dependencies.js old mode 100755 new mode 100644 index c8bd9114faf9..0bad76f507cc --- a/scripts/check-dependencies.js +++ b/scripts/check-dependencies.js @@ -1,7 +1,72 @@ -#!/usr/bin/env node -const { checkDependencies } = require('./utils/cli-utils'); +/** + * This file needs to be run before any other script to ensure dependencies are installed + * Therefore, we cannot transform this file to Typescript, because it would require esbuild to be installed + */ +import { spawn } from 'child_process'; +import { join } from 'path'; +import { existsSync } from 'fs'; +import * as url from 'url'; + +const logger = console; + +const filename = url.fileURLToPath(import.meta.url); +const dirname = url.fileURLToPath(new URL('.', import.meta.url)); + +const checkDependencies = async () => { + const scriptsPath = join(dirname); + const codePath = join(dirname, '..', 'code'); + + const tasks = []; + + if (!existsSync(join(scriptsPath, 'node_modules'))) { + tasks.push( + spawn('yarn', ['install'], { + cwd: scriptsPath, + shell: true, + stdio: ['inherit', 'inherit', 'inherit'], + }) + ); + } + if (!existsSync(join(codePath, 'node_modules'))) { + tasks.push( + spawn('yarn', ['install'], { + cwd: codePath, + shell: true, + stdio: ['inherit', 'inherit', 'inherit'], + }) + ); + } + + if (tasks.length > 0) { + logger.log('installing dependencies'); + + await Promise.all( + tasks.map( + (t) => + new Promise((res, rej) => { + t.on('exit', (code) => { + if (code !== 0) { + rej(); + } else { + res(); + } + }); + }) + ) + ).catch(() => { + tasks.forEach((t) => t.kill()); + throw new Error('Failed to install dependencies'); + }); + + // give the filesystem some time + await new Promise((res) => { + setTimeout(res, 1000); + }); + } +}; checkDependencies().catch((e) => { + // eslint-disable-next-line no-console console.error(e); process.exit(1); }); diff --git a/scripts/check-package.js b/scripts/check-package.ts similarity index 70% rename from scripts/check-package.js rename to scripts/check-package.ts index 7fc8bc88f198..6248ad54797a 100644 --- a/scripts/check-package.js +++ b/scripts/check-package.ts @@ -1,38 +1,39 @@ -#!/usr/bin/env node - -/* eslint-disable global-require */ -const { resolve } = require('path'); -const { readJSON } = require('fs-extra'); - -const getStorybookPackages = async () => { - const workspaceJSON = await readJSON(resolve(__dirname, '..', 'code', 'workspace.json')); - return Object.entries(workspaceJSON.projects).map(([k, v]) => ({ - location: v.root, - name: k, - })); -}; +import { resolve } from 'path'; +import { readJSON } from 'fs-extra'; +import prompts from 'prompts'; +import program from 'commander'; +import chalk from 'chalk'; +import windowSize from 'window-size'; +import { execaCommand } from 'execa'; +import { getWorkspaces } from './utils/workspace'; async function run() { - const prompts = require('prompts'); - const program = require('commander'); - const chalk = require('chalk'); - - const packages = await getStorybookPackages(); + const packages = await getWorkspaces(); const packageTasks = packages - .map((package) => { + .map((pkg) => { return { - ...package, - suffix: package.name.replace('@storybook/', ''), + ...pkg, + suffix: pkg.name.replace('@storybook/', ''), defaultValue: false, - helpText: `check only the ${package.name} package`, + helpText: `check only the ${pkg.name} package`, }; }) .reduce((acc, next) => { acc[next.name] = next; return acc; - }, {}); + }, {} as Record); - const tasks = { + const tasks: Record< + string, + { + name: string; + defaultValue: boolean; + suffix: string; + helpText: string; + value?: any; + location?: string; + } + > = { watch: { name: `watch`, defaultValue: false, @@ -76,14 +77,15 @@ async function run() { name: 'todo', min: 1, hint: 'You can also run directly with package name like `yarn check core`, or `yarn check --all` for all packages!', - optionsPerPage: require('window-size').height - 3, // 3 lines for extra info + // @ts-expect-error @types incomplete + optionsPerPage: windowSize.height - 3, // 3 lines for extra info choices: packages.map(({ name: key }) => ({ value: key, title: tasks[key].name || key, selected: (tasks[key] && tasks[key].defaultValue) || false, })), }, - ]).then(({ mode, todo }) => { + ]).then(({ mode, todo }: { mode: boolean; todo: Array }) => { watchMode = mode; return todo?.map((key) => tasks[key]); }); @@ -96,9 +98,8 @@ async function run() { } selection?.filter(Boolean).forEach(async (v) => { - const commmand = (await readJSON(resolve(v.location, 'package.json'))).scripts.check; + const commmand = (await readJSON(resolve('../code', v.location, 'package.json'))).scripts.check; const cwd = resolve(__dirname, '..', 'code', v.location); - const { execaCommand } = await import('execa'); const sub = execaCommand(`${commmand}${watchMode ? ' --watch' : ''}`, { cwd, buffer: false, diff --git a/scripts/combine-compodoc.ts b/scripts/combine-compodoc.ts index f09f85bb9870..c355cc242831 100755 --- a/scripts/combine-compodoc.ts +++ b/scripts/combine-compodoc.ts @@ -6,7 +6,8 @@ import { join, resolve } from 'path'; import { realpath, readFile, writeFile, lstat } from 'fs-extra'; import { globSync } from 'glob'; import { directory } from 'tempy'; -import { execaCommand } from './utils/exec'; +import { execaCommand } from 'execa'; +import { esMain } from './utils/esmain'; const logger = console; @@ -67,7 +68,7 @@ async function run(cwd: string) { await writeFile(join(cwd, 'documentation.json'), JSON.stringify(documentation)); } -if (require.main === module) { +if (esMain(import.meta.url)) { run(resolve(process.argv[2])) .then(() => process.exit(0)) .catch((err) => { diff --git a/scripts/event-log-checker.ts b/scripts/event-log-checker.ts index eabfd8d9a209..681e54f7dfe4 100644 --- a/scripts/event-log-checker.ts +++ b/scripts/event-log-checker.ts @@ -2,6 +2,7 @@ import chalk from 'chalk'; import assert from 'assert'; import fetch from 'node-fetch'; +import { esMain } from './utils/esmain'; import { allTemplates } from '../code/lib/cli/src/sandbox-templates'; import versions from '../code/lib/cli/src/versions'; import { oneWayHash } from '../code/lib/telemetry/src/one-way-hash'; @@ -115,7 +116,7 @@ async function run() { export {}; -if (require.main === module) { +if (esMain(import.meta.url)) { run() .then(() => process.exit(0)) .catch((err) => { diff --git a/scripts/get-report-message.ts b/scripts/get-report-message.ts index 47b24cfba47b..d118bfed3b61 100644 --- a/scripts/get-report-message.ts +++ b/scripts/get-report-message.ts @@ -1,8 +1,9 @@ /* eslint-disable no-console */ import { readJson } from 'fs-extra'; import { join } from 'path'; +import { execaCommand } from 'execa'; +import { esMain } from './utils/esmain'; import { CODE_DIRECTORY } from './utils/constants'; -import { execaCommand } from './utils/exec'; type Branch = 'main' | 'next' | 'alpha' | 'next-release' | 'latest-release'; type Workflow = 'merged' | 'daily'; @@ -27,7 +28,7 @@ const getFooter = async (branch: Branch, workflow: Workflow, job: string) => { : // show last 24h merges for daily workflow `git log --merges --since="24 hours ago" --pretty=format:"\`%h\` %<(12)%ar %s [%an]"`; - const result = await execaCommand(mergeCommits, { shell: true }); + const result = await execaCommand(mergeCommits, { shell: true, cleanup: true }); const formattedResult = result.stdout // discord needs escaped line breaks .replace(/\n/g, '\\n') @@ -63,7 +64,7 @@ async function run() { console.log(`${title}${body}${footer}`.replace(/\n/g, '\\n')); } -if (require.main === module) { +if (esMain(import.meta.url)) { run().catch((err) => { console.error(err); process.exit(1); diff --git a/scripts/get-template.ts b/scripts/get-template.ts index c56418f7a0b4..ee3b81b13025 100644 --- a/scripts/get-template.ts +++ b/scripts/get-template.ts @@ -4,6 +4,7 @@ import { program } from 'commander'; import dedent from 'ts-dedent'; import chalk from 'chalk'; import yaml from 'yaml'; +import { esMain } from './utils/esmain'; import { allTemplates, templatesByCadence, @@ -179,7 +180,7 @@ async function run({ cadence, task, check }: RunOptions) { ); } -if (require.main === module) { +if (esMain(import.meta.url)) { program .description('Retrieve the template to run for a given cadence and task') .option('--cadence ', 'Which cadence you want to run the script for') diff --git a/scripts/jest.config.cjs b/scripts/jest.config.cjs new file mode 100644 index 000000000000..571d0ab11232 --- /dev/null +++ b/scripts/jest.config.cjs @@ -0,0 +1,21 @@ +module.exports = { + transform: { + '^.+\\.(t|j)sx?$': [ + '@swc/jest', + { + jsc: { + parser: { + syntax: 'typescript', + tsx: false, + decorators: false, + dynamicImport: false, + }, + experimental: { + plugins: [['jest_workaround', {}]], + }, + }, + }, + ], + }, + transformIgnorePatterns: [], +}; diff --git a/scripts/jest.config.js b/scripts/jest.config.js deleted file mode 100644 index f053ebf7976e..000000000000 --- a/scripts/jest.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = {}; diff --git a/scripts/package.json b/scripts/package.json index b428f9b9f228..23c7cb7b10fd 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -2,36 +2,42 @@ "name": "@storybook/scripts", "version": "7.0.0-alpha.16", "private": true, + "type": "module", "scripts": { - "check": "./prepare/check-scripts.ts", + "build-package": "node --loader esbuild-register/loader -r esbuild-register ./build-package.ts", + "check": "node --loader esbuild-register/loader -r esbuild-register ./prepare/check-scripts.ts", + "check-package": "node --loader esbuild-register/loader -r esbuild-register ./check-package.ts", "docs:prettier:check": "cd ../docs && prettier --check ./snippets", "docs:prettier:write": "cd ../docs && prettier --write ./snippets", - "get-report-message": "ts-node --swc ./get-report-message.ts", - "get-template": "ts-node --swc ./get-template.ts", + "generate-sandboxes": "node --loader esbuild-register/loader -r esbuild-register ./sandbox/generate.ts", + "get-report-message": "node --loader esbuild-register/loader -r esbuild-register ./get-report-message.ts", + "get-template": "node --loader esbuild-register/loader -r esbuild-register ./get-template.ts", "lint": "yarn lint:js && yarn lint:md", "lint:js": "yarn lint:js:cmd . --quiet", "lint:js:cmd": "cross-env NODE_ENV=production eslint --cache --cache-location=../.cache/eslint --ext .js,.jsx,.json,.html,.ts,.tsx,.mjs --report-unused-disable-directives", "lint:package": "sort-package-json", - "release:cancel-preparation-runs": "ts-node --swc ./release/cancel-preparation-runs.ts", - "release:ensure-next-ahead": "ts-node --swc ./release/ensure-next-ahead.ts", - "release:generate-pr-description": "ts-node --swc ./release/generate-pr-description.ts", - "release:get-changelog-from-file": "ts-node --swc ./release/get-changelog-from-file.ts", - "release:get-current-version": "ts-node --swc ./release/get-current-version.ts", - "release:get-version-changelog": "ts-node --swc ./release/get-version-changelog.ts", - "release:is-pr-frozen": "ts-node --swc ./release/is-pr-frozen.ts", - "release:is-prerelease": "ts-node --swc ./release/is-prerelease.ts", - "release:is-version-published": "ts-node --swc ./release/is-version-published.ts", - "release:label-patches": "ts-node --swc ./release/label-patches.ts", - "release:pick-patches": "ts-node --swc ./release/pick-patches.ts", - "release:publish": "ts-node --swc ./release/publish.ts", - "release:unreleased-changes-exists": "ts-node --swc ./release/unreleased-changes-exists.ts", - "release:version": "ts-node --swc ./release/version.ts", - "release:write-changelog": "ts-node --swc ./release/write-changelog.ts", - "strict-ts": "node --require esbuild-register ./strict-ts.ts", - "task": "ts-node --swc ./task.ts", - "test": "jest --config ./jest.config.js", - "upgrade": "ts-node --swc ./task.ts", - "upload-bench": "ts-node ./upload-bench.ts" + "local-registry": "node --loader esbuild-register/loader -r esbuild-register ./run-registry.ts", + "publish": "node --loader esbuild-register/loader -r esbuild-register ./sandbox/publish.ts", + "release:cancel-preparation-runs": "node --loader esbuild-register/loader -r esbuild-register ./release/cancel-preparation-runs.ts", + "release:ensure-next-ahead": "node --loader esbuild-register/loader -r esbuild-register ./release/ensure-next-ahead.ts", + "release:generate-pr-description": "node --loader esbuild-register/loader -r esbuild-register ./release/generate-pr-description.ts", + "release:get-changelog-from-file": "node --loader esbuild-register/loader -r esbuild-register ./release/get-changelog-from-file.ts", + "release:get-current-version": "node --loader esbuild-register/loader -r esbuild-register ./release/get-current-version.ts", + "release:get-version-changelog": "node --loader esbuild-register/loader -r esbuild-register ./release/get-version-changelog.ts", + "release:is-pr-frozen": "node --loader esbuild-register/loader -r esbuild-register ./release/is-pr-frozen.ts", + "release:is-prerelease": "node --loader esbuild-register/loader -r esbuild-register ./release/is-prerelease.ts", + "release:is-version-published": "node --loader esbuild-register/loader -r esbuild-register ./release/is-version-published.ts", + "release:label-patches": "node --loader esbuild-register/loader -r esbuild-register ./release/label-patches.ts", + "release:pick-patches": "node --loader esbuild-register/loader -r esbuild-register ./release/pick-patches.ts", + "release:publish": "node --loader esbuild-register/loader -r esbuild-register ./release/publish.ts", + "release:unreleased-changes-exists": "node --loader esbuild-register/loader -r esbuild-register ./release/unreleased-changes-exists.ts", + "release:version": "node --loader esbuild-register/loader -r esbuild-register ./release/version.ts", + "release:write-changelog": "node --loader esbuild-register/loader -r esbuild-register ./release/write-changelog.ts", + "strict-ts": "node --loader esbuild-register/loader -r esbuild-register ./strict-ts.ts", + "task": "node --loader esbuild-register/loader -r esbuild-register ./task.ts", + "test": "jest --config ./jest.config.cjs", + "upgrade": "node --loader esbuild-register/loader -r esbuild-register ./task.ts", + "upload-bench": "node --loader esbuild-register/loader -r esbuild-register ./upload-bench.ts" }, "husky": { "hooks": { @@ -79,7 +85,8 @@ "@storybook/jest": "next", "@storybook/linter-config": "^3.1.2", "@storybook/testing-library": "next", - "@swc/core": "1.3.82", + "@swc/core": "^1.3.95", + "@swc/jest": "^0.2.29", "@testing-library/dom": "^9.3.0", "@testing-library/jest-dom": "^5.11.9", "@testing-library/react": "^14.0.0", @@ -104,6 +111,7 @@ "@types/serve-static": "^1.13.8", "@types/uuid": "^9.0.1", "@types/wait-on": "^5.3.1", + "@types/window-size": "^1.1.3", "@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/experimental-utils": "^5.45.0", "@typescript-eslint/parser": "^5.45.0", @@ -111,6 +119,7 @@ "babel-eslint": "^10.1.0", "babel-loader": "^9.1.2", "boxen": "^5.1.2", + "browser-assert": "^1.2.1", "chalk": "^4.1.0", "codecov": "^3.8.1", "commander": "^6.2.1", @@ -146,6 +155,7 @@ "jest-os-detection": "^1.3.1", "jest-serializer-html": "^7.1.0", "jest-watch-typeahead": "^2.2.1", + "jest_workaround": "^0.79.19", "json5": "^2.2.3", "junit-xml": "^1.2.0", "lint-staged": "^10.5.4", @@ -183,10 +193,9 @@ "tiny-invariant": "^1.3.1", "trash": "^7.0.0", "ts-dedent": "^2.0.0", - "ts-node": "^10.9.1", "tsup": "^6.7.0", "type-fest": "~2.19", - "typescript": "5.1.6", + "typescript": "5.2.2", "util": "^0.12.4", "uuid": "^9.0.0", "wait-on": "^7.0.1", diff --git a/scripts/prepare/addon-bundle.ts b/scripts/prepare/addon-bundle.ts new file mode 100755 index 000000000000..757402c4600d --- /dev/null +++ b/scripts/prepare/addon-bundle.ts @@ -0,0 +1,285 @@ +#!/usr/bin/env ../../node_modules/.bin/ts-node + +import * as fs from 'fs-extra'; +import path, { dirname, join, relative } from 'path'; +import type { Options } from 'tsup'; +import type { PackageJson } from 'type-fest'; +import { build } from 'tsup'; +import aliasPlugin from 'esbuild-plugin-alias'; +import dedent from 'ts-dedent'; +import slash from 'slash'; +import { exec } from '../utils/exec'; + +import { globalPackages as globalPreviewPackages } from '../../code/lib/preview/src/globals/globals'; +import { globalPackages as globalManagerPackages } from '../../code/ui/manager/src/globals/globals'; + +/* TYPES */ + +type Formats = 'esm' | 'cjs'; +type BundlerConfig = { + previewEntries: string[]; + managerEntries: string[]; + nodeEntries: string[]; + exportEntries: string[]; + externals: string[]; + pre: string; + post: string; + formats: Formats[]; +}; +type PackageJsonWithBundlerConfig = PackageJson & { + bundler: BundlerConfig; +}; +type DtsConfigSection = Pick; + +/* MAIN */ + +const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => { + const { + name, + dependencies, + peerDependencies, + bundler: { + managerEntries = [], + previewEntries = [], + nodeEntries = [], + exportEntries = [], + externals: extraExternals = [], + pre, + post, + formats = ['esm', 'cjs'], + }, + } = (await fs.readJson(join(cwd, 'package.json'))) as PackageJsonWithBundlerConfig; + + if (pre) { + await exec(`node -r ${__dirname}/../node_modules/esbuild-register/register.js ${pre}`, { cwd }); + } + + const reset = hasFlag(flags, 'reset'); + const watch = hasFlag(flags, 'watch'); + const optimized = hasFlag(flags, 'optimized'); + + if (reset) { + await fs.emptyDir(join(process.cwd(), 'dist')); + } + + const tasks: Promise[] = []; + + const commonOptions: Options = { + outDir: join(process.cwd(), 'dist'), + silent: true, + treeshake: true, + shims: false, + watch, + clean: false, + esbuildPlugins: [ + aliasPlugin({ + process: require.resolve('../node_modules/process/browser.js'), + util: require.resolve('../node_modules/util/util.js'), + assert: require.resolve('browser-assert'), + }), + ], + }; + + const commonExternals = [ + name, + ...extraExternals, + ...Object.keys(dependencies || {}), + ...Object.keys(peerDependencies || {}), + ]; + + if (exportEntries.length > 0) { + const { dtsConfig, tsConfigExists } = await getDTSConfigs({ + formats, + entries: exportEntries, + optimized, + }); + + tasks.push( + build({ + ...commonOptions, + ...(optimized ? dtsConfig : {}), + entry: exportEntries, + format: ['esm'], + target: ['chrome100', 'safari15', 'firefox91'], + platform: 'browser', + external: [...commonExternals, ...globalManagerPackages, ...globalPreviewPackages], + esbuildOptions: (options) => { + /* eslint-disable no-param-reassign */ + options.conditions = ['module']; + options.platform = 'browser'; + Object.assign(options, getESBuildOptions(optimized)); + /* eslint-enable no-param-reassign */ + }, + }), + build({ + ...commonOptions, + ...(optimized ? dtsConfig : {}), + entry: exportEntries, + format: ['cjs'], + target: 'node16', + platform: 'node', + external: commonExternals, + esbuildOptions: (options) => { + /* eslint-disable no-param-reassign */ + options.conditions = ['module']; + options.platform = 'node'; + Object.assign(options, getESBuildOptions(optimized)); + /* eslint-enable no-param-reassign */ + }, + }) + ); + + if (tsConfigExists && !optimized) { + tasks.push(...exportEntries.map(generateDTSMapperFile)); + } + } + + if (managerEntries.length > 0) { + tasks.push( + build({ + ...commonOptions, + entry: managerEntries.map((e: string) => slash(join(cwd, e))), + outExtension: () => ({ + js: '.js', + }), + format: ['esm'], + target: ['chrome100', 'safari15', 'firefox91'], + platform: 'browser', + external: [...commonExternals, ...globalManagerPackages], + esbuildOptions: (options) => { + /* eslint-disable no-param-reassign */ + options.conditions = ['module']; + options.platform = 'browser'; + Object.assign(options, getESBuildOptions(optimized)); + /* eslint-enable no-param-reassign */ + }, + }) + ); + } + if (previewEntries.length > 0) { + tasks.push( + build({ + ...commonOptions, + entry: previewEntries.map((e: string) => slash(join(cwd, e))), + outExtension: () => ({ + js: '.js', + }), + format: ['esm'], + target: ['chrome100', 'safari15', 'firefox91'], + platform: 'browser', + external: [...commonExternals, ...globalPreviewPackages], + esbuildOptions: (c) => { + /* eslint-disable no-param-reassign */ + c.conditions = ['module']; + c.platform = 'browser'; + Object.assign(c, getESBuildOptions(optimized)); + /* eslint-enable no-param-reassign */ + }, + }) + ); + } + if (nodeEntries.length > 0) { + tasks.push( + build({ + ...commonOptions, + entry: nodeEntries.map((e: string) => slash(join(cwd, e))), + format: ['cjs'], + target: 'node16', + platform: 'node', + external: commonExternals, + esbuildOptions: (c) => { + /* eslint-disable no-param-reassign */ + c.platform = 'node'; + Object.assign(c, getESBuildOptions(optimized)); + /* eslint-enable no-param-reassign */ + }, + }) + ); + } + + await Promise.all(tasks); + + if (post) { + await exec( + `node -r ${__dirname}/../node_modules/esbuild-register/register.js ${post}`, + { cwd }, + { debug: true } + ); + } + + console.log('done'); +}; + +/* UTILS */ + +// keep in sync with code/lib/manager-api/src/index.ts + +async function getDTSConfigs({ + formats, + entries, + optimized, +}: { + formats: Formats[]; + entries: string[]; + optimized: boolean; +}) { + const tsConfigPath = join(cwd, 'tsconfig.json'); + const tsConfigExists = await fs.pathExists(tsConfigPath); + + const dtsBuild = optimized && formats[0] && tsConfigExists ? formats[0] : undefined; + + const dtsConfig: DtsConfigSection = { + tsconfig: tsConfigPath, + dts: { + entry: entries, + resolve: true, + }, + }; + + return { dtsBuild, dtsConfig, tsConfigExists }; +} + +function getESBuildOptions(optimized: boolean) { + return { + logLevel: 'error', + legalComments: 'none', + minifyWhitespace: optimized, + minifyIdentifiers: false, + minifySyntax: optimized, + }; +} + +async function generateDTSMapperFile(file: string) { + const { name: entryName, dir } = path.parse(file); + + const pathName = join(process.cwd(), dir.replace('./src', 'dist'), `${entryName}.d.ts`); + const srcName = join(process.cwd(), file); + const rel = relative(dirname(pathName), dirname(srcName)).split(path.sep).join(path.posix.sep); + + await fs.ensureFile(pathName); + await fs.writeFile( + pathName, + dedent` + // dev-mode + export * from '${rel}/${entryName}'; + `, + { encoding: 'utf-8' } + ); +} + +const hasFlag = (flags: string[], name: string) => !!flags.find((s) => s.startsWith(`--${name}`)); + +/* SELF EXECUTION */ + +const flags = process.argv.slice(2); +const cwd = process.cwd(); + +run({ cwd, flags }).catch((err: unknown) => { + // We can't let the stack try to print, it crashes in a way that sets the exit code to 0. + // Seems to have something to do with running JSON.parse() on binary / base64 encoded sourcemaps + // in @cspotcode/source-map-support + if (err instanceof Error) { + console.error(err.stack); + } + process.exit(1); +}); diff --git a/scripts/prepare/bundle.ts b/scripts/prepare/bundle.ts index 1228e445a034..e71d2b1a2da6 100755 --- a/scripts/prepare/bundle.ts +++ b/scripts/prepare/bundle.ts @@ -1,5 +1,3 @@ -#!/usr/bin/env ../../node_modules/.bin/ts-node - import * as fs from 'fs-extra'; import path, { dirname, join, relative } from 'path'; import type { Options } from 'tsup'; diff --git a/scripts/prepare/check-scripts.ts b/scripts/prepare/check-scripts.ts index 86418acd93ba..261c070c1ca6 100755 --- a/scripts/prepare/check-scripts.ts +++ b/scripts/prepare/check-scripts.ts @@ -1,7 +1,5 @@ -#!/usr/bin/env ./node_modules/.bin/ts-node-script - import { join } from 'path'; -import * as ts from 'typescript'; +import ts from 'typescript'; const run = async ({ cwd }: { cwd: string }) => { const { options, fileNames } = getTSFilesAndConfig('tsconfig.json'); diff --git a/scripts/prepare/check.ts b/scripts/prepare/check.ts index d200e921ea0b..fe75515f95b1 100755 --- a/scripts/prepare/check.ts +++ b/scripts/prepare/check.ts @@ -1,8 +1,6 @@ -#!/usr/bin/env ../../node_modules/.bin/ts-node-script - import { join } from 'path'; import fs from 'fs-extra'; -import * as ts from 'typescript'; +import ts from 'typescript'; const run = async ({ cwd }: { cwd: string }) => { const { diff --git a/scripts/prepare/esm-bundle.ts b/scripts/prepare/esm-bundle.ts index e75ddc75b679..6ec3e434e4e9 100755 --- a/scripts/prepare/esm-bundle.ts +++ b/scripts/prepare/esm-bundle.ts @@ -1,5 +1,3 @@ -#!/usr/bin/env ../../node_modules/.bin/ts-node - import * as fs from 'fs-extra'; import path, { dirname, join, relative } from 'path'; import type { Options } from 'tsup'; diff --git a/scripts/prepare/facade.ts b/scripts/prepare/facade.ts index d43d47ef89a1..2a1a62d73b52 100755 --- a/scripts/prepare/facade.ts +++ b/scripts/prepare/facade.ts @@ -1,5 +1,3 @@ -#!/usr/bin/env ../../node_modules/.bin/ts-node - import { join, parse } from 'path'; import fs from 'fs-extra'; import dedent from 'ts-dedent'; @@ -18,10 +16,9 @@ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => { } = await fs.readJson(join(cwd, 'package.json')); const optimized = hasFlag(flags, 'optimized'); - const tsnodePath = join(__dirname, '..', 'node_modules', '.bin', 'ts-node'); if (pre) { - await exec(`${tsnodePath} ${pre}`, { cwd }); + await exec(`node --loader esbuild-register/loader -r esbuild-register ${pre}`, { cwd }); } await Promise.all([ @@ -73,7 +70,7 @@ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => { ]); if (pre) { - await exec(`${tsnodePath} ${post}`, { cwd }); + await exec(`node --loader esbuild-register/loader -r esbuild-register ${post}`, { cwd }); } }; diff --git a/scripts/prepare/tsc.ts b/scripts/prepare/tsc.ts index cc248979ef70..62179c5b29ec 100755 --- a/scripts/prepare/tsc.ts +++ b/scripts/prepare/tsc.ts @@ -1,5 +1,3 @@ -#!/usr/bin/env ../../node_modules/.bin/ts-node - import { join } from 'path'; import fs, { move } from 'fs-extra'; import * as ts from 'typescript'; diff --git a/scripts/prettier.config.js b/scripts/prettier.config.cjs similarity index 100% rename from scripts/prettier.config.js rename to scripts/prettier.config.cjs diff --git a/scripts/release/__tests__/version.test.ts b/scripts/release/__tests__/version.test.ts index 98069ffaba74..22a2cb7ca8a1 100644 --- a/scripts/release/__tests__/version.test.ts +++ b/scripts/release/__tests__/version.test.ts @@ -11,8 +11,8 @@ jest.mock('../../../code/lib/cli/src/versions', () => ({ '@storybook/addon-a11y': '7.1.0-alpha.29', })); -jest.mock('../../utils/exec'); -const { execaCommand } = require('../../utils/exec'); +jest.mock('execa'); +const { execaCommand } = require('execa'); jest.mock('../../utils/workspace', () => ({ getWorkspaces: jest.fn().mockResolvedValue([ @@ -288,6 +288,7 @@ describe('Version', () => { ); expect(execaCommand).toHaveBeenCalledWith('yarn install --mode=update-lockfile', { cwd: path.join(CODE_DIR_PATH), + cleanup: true, stdio: undefined, }); } diff --git a/scripts/release/cancel-preparation-runs.ts b/scripts/release/cancel-preparation-runs.ts index 630bfb4847b3..56cca697d1d9 100644 --- a/scripts/release/cancel-preparation-runs.ts +++ b/scripts/release/cancel-preparation-runs.ts @@ -6,6 +6,7 @@ import chalk from 'chalk'; import program from 'commander'; import dedent from 'ts-dedent'; +import { esMain } from '../utils/esmain'; import { githubRestClient } from './utils/github-client'; program @@ -99,7 +100,7 @@ export const run = async () => { } }; -if (require.main === module) { +if (esMain(import.meta.url)) { run().catch((err) => { console.error(err); // this is non-critical work, so we don't want to fail the CI build if this fails diff --git a/scripts/release/ensure-next-ahead.ts b/scripts/release/ensure-next-ahead.ts index 1aa32bfd932c..308004f031cf 100644 --- a/scripts/release/ensure-next-ahead.ts +++ b/scripts/release/ensure-next-ahead.ts @@ -14,6 +14,7 @@ import program from 'commander'; import semver from 'semver'; import { z } from 'zod'; import { readJson } from 'fs-extra'; +import { esMain } from '../utils/esmain'; import { run as bumpVersion } from './version'; import { git } from './utils/git-client'; @@ -92,7 +93,7 @@ export const run = async (options: unknown) => { ); }; -if (require.main === module) { +if (esMain(import.meta.url)) { const parsed = program.parse(); run(parsed.opts()).catch((err) => { console.error(err); diff --git a/scripts/release/generate-pr-description.ts b/scripts/release/generate-pr-description.ts index d437e8dc87bc..438c13fb4a91 100644 --- a/scripts/release/generate-pr-description.ts +++ b/scripts/release/generate-pr-description.ts @@ -5,6 +5,7 @@ import { z } from 'zod'; import dedent from 'ts-dedent'; import semver from 'semver'; import { setOutput } from '@actions/core'; +import { esMain } from '../utils/esmain'; import type { Change } from './utils/get-changes'; import { getChanges, LABELS_BY_IMPORTANCE, RELEASED_LABELS } from './utils/get-changes'; import { getCurrentVersion } from './get-current-version'; @@ -295,7 +296,7 @@ export const run = async (rawOptions: unknown) => { } }; -if (require.main === module) { +if (esMain(import.meta.url)) { const parsed = program.parse(); run(parsed.opts()).catch((err) => { console.error(err); diff --git a/scripts/release/get-changelog-from-file.ts b/scripts/release/get-changelog-from-file.ts index e01efe959032..d400ad581507 100644 --- a/scripts/release/get-changelog-from-file.ts +++ b/scripts/release/get-changelog-from-file.ts @@ -6,6 +6,7 @@ import { readFile } from 'fs-extra'; import path from 'path'; import semver from 'semver'; import dedent from 'ts-dedent'; +import { esMain } from '../utils/esmain'; import { getCurrentVersion } from './get-current-version'; program @@ -54,7 +55,7 @@ export const getChangelogFromFile = async (args: { return result; }; -if (require.main === module) { +if (esMain(import.meta.url)) { const parsed = program.parse(); getChangelogFromFile({ version: parsed.args[0], diff --git a/scripts/release/get-current-version.ts b/scripts/release/get-current-version.ts index 413c101640b6..fffa703ab088 100644 --- a/scripts/release/get-current-version.ts +++ b/scripts/release/get-current-version.ts @@ -3,6 +3,7 @@ import chalk from 'chalk'; import { setOutput } from '@actions/core'; import path from 'path'; import { readJson } from 'fs-extra'; +import { esMain } from '../utils/esmain'; const CODE_DIR_PATH = path.join(__dirname, '..', '..', 'code'); const CODE_PACKAGE_JSON_PATH = path.join(CODE_DIR_PATH, 'package.json'); @@ -17,7 +18,7 @@ export const getCurrentVersion = async () => { return version; }; -if (require.main === module) { +if (esMain(import.meta.url)) { getCurrentVersion().catch((err) => { console.error(err); process.exit(1); diff --git a/scripts/release/get-version-changelog.ts b/scripts/release/get-version-changelog.ts index 3ece5480236d..ce0464d62517 100644 --- a/scripts/release/get-version-changelog.ts +++ b/scripts/release/get-version-changelog.ts @@ -2,6 +2,7 @@ import { setOutput } from '@actions/core'; import chalk from 'chalk'; import { program } from 'commander'; +import { esMain } from '../utils/esmain'; import { getCurrentVersion } from './get-current-version'; import { getChanges } from './utils/get-changes'; @@ -26,7 +27,7 @@ export const getVersionChangelog = async (args: { version?: string; verbose?: bo return changelogText; }; -if (require.main === module) { +if (esMain(import.meta.url)) { const parsed = program.parse(); getVersionChangelog({ version: parsed.args[0], verbose: parsed.opts().verbose }).catch((err) => { console.error(err); diff --git a/scripts/release/is-pr-frozen.ts b/scripts/release/is-pr-frozen.ts index e81610a451b9..70d04c9509f2 100644 --- a/scripts/release/is-pr-frozen.ts +++ b/scripts/release/is-pr-frozen.ts @@ -4,6 +4,7 @@ import program from 'commander'; import { setOutput } from '@actions/core'; import path from 'path'; import { readJson } from 'fs-extra'; +import { esMain } from '../utils/esmain'; import { getPullInfoFromCommit } from './utils/get-github-info'; import { git } from './utils/git-client'; @@ -98,7 +99,7 @@ export const run = async (options: unknown) => { return isFrozen; }; -if (require.main === module) { +if (esMain(import.meta.url)) { const parsed = program.parse(); run(parsed.opts()).catch((err) => { console.error(err); diff --git a/scripts/release/is-prerelease.ts b/scripts/release/is-prerelease.ts index 25722c098cd6..b35f423820ea 100644 --- a/scripts/release/is-prerelease.ts +++ b/scripts/release/is-prerelease.ts @@ -3,6 +3,7 @@ import chalk from 'chalk'; import program from 'commander'; import { setOutput } from '@actions/core'; import semver from 'semver'; +import { esMain } from '../utils/esmain'; import { getCurrentVersion } from './get-current-version'; program @@ -38,7 +39,7 @@ export const isPrerelease = async (args: { version?: string; verbose?: boolean } return result; }; -if (require.main === module) { +if (esMain(import.meta.url)) { const parsed = program.parse(); isPrerelease({ version: parsed.args[0], diff --git a/scripts/release/is-version-published.ts b/scripts/release/is-version-published.ts index b5acd8c90ab7..54c19a465eb7 100644 --- a/scripts/release/is-version-published.ts +++ b/scripts/release/is-version-published.ts @@ -3,6 +3,7 @@ import chalk from 'chalk'; import program from 'commander'; import { setOutput } from '@actions/core'; import fetch from 'node-fetch'; +import { esMain } from '../utils/esmain'; import { getCurrentVersion } from './get-current-version'; program @@ -77,7 +78,7 @@ export const run = async (args: unknown[], options: unknown) => { return isAlreadyPublished; }; -if (require.main === module) { +if (esMain(import.meta.url)) { const parsed = program.parse(); run(parsed.args, parsed.opts()).catch((err) => { console.error(err); diff --git a/scripts/release/label-patches.ts b/scripts/release/label-patches.ts index 1e9305b9806c..453e64105abf 100644 --- a/scripts/release/label-patches.ts +++ b/scripts/release/label-patches.ts @@ -1,6 +1,7 @@ import program from 'commander'; import { v4 as uuidv4 } from 'uuid'; import ora from 'ora'; +import { esMain } from '../utils/esmain'; import { getLabelIds, githubGraphQlClient, getUnpickedPRs } from './utils/github-client'; import { getPullInfoFromCommits, getRepo } from './utils/get-changes'; import { getLatestTag, git } from './utils/git-client'; @@ -93,7 +94,7 @@ export const run = async (options: unknown) => { } }; -if (require.main === module) { +if (esMain(import.meta.url)) { const options = program.parse().opts(); run(options).catch((err) => { console.error(err); diff --git a/scripts/release/pick-patches.ts b/scripts/release/pick-patches.ts index 9d3169d78c8a..be713f5f5d51 100644 --- a/scripts/release/pick-patches.ts +++ b/scripts/release/pick-patches.ts @@ -5,6 +5,7 @@ import chalk from 'chalk'; import ora from 'ora'; import { setOutput } from '@actions/core'; import invariant from 'tiny-invariant'; +import { esMain } from '../utils/esmain'; import { git } from './utils/git-client'; import { getUnpickedPRs } from './utils/github-client'; @@ -85,7 +86,7 @@ export const run = async (_: unknown) => { } }; -if (require.main === module) { +if (esMain(import.meta.url)) { const options = program.parse(process.argv); run(options).catch((err) => { console.error(err); diff --git a/scripts/release/publish.ts b/scripts/release/publish.ts index eeb8ab2f9349..cd69f5260ce8 100644 --- a/scripts/release/publish.ts +++ b/scripts/release/publish.ts @@ -7,7 +7,9 @@ import { z } from 'zod'; import { readJson } from 'fs-extra'; import fetch from 'node-fetch'; import dedent from 'ts-dedent'; -import { execaCommand } from '../utils/exec'; +import pRetry from 'p-retry'; +import { execaCommand } from 'execa'; +import { esMain } from '../utils/esmain'; program .name('publish') @@ -109,6 +111,7 @@ const buildAllPackages = async () => { console.log(`๐Ÿ—๏ธ Building all packages...`); await execaCommand('yarn task --task=compile --start-from=compile --no-link', { stdio: 'inherit', + cleanup: true, cwd: CODE_DIR_PATH, }); console.log(`๐Ÿ—๏ธ Packages successfully built`); @@ -134,15 +137,6 @@ const publishAllPackages = async ({ return; } - // Note this is to fool `ts-node` into not turning the `import()` into a `require()`. - // See: https://github.com/TypeStrong/ts-node/discussions/1290 - // prettier-ignore - const pRetry = ( - // eslint-disable-next-line @typescript-eslint/no-implied-eval - (await new Function('specifier', 'return import(specifier)')( - 'p-retry' - )) as typeof import('p-retry') - ).default; /** * 'yarn npm publish' will fail if just one package fails to publish. * But it will continue through with all the other packages, and --tolerate-republish makes it okay to publish the same version again. @@ -153,6 +147,7 @@ const publishAllPackages = async ({ () => execaCommand(command, { stdio: 'inherit', + cleanup: true, cwd: CODE_DIR_PATH, }), { @@ -197,7 +192,7 @@ export const run = async (options: unknown) => { ); }; -if (require.main === module) { +if (esMain(import.meta.url)) { const parsed = program.parse(); run(parsed.opts()).catch((err) => { console.error(err); diff --git a/scripts/release/unreleased-changes-exists.ts b/scripts/release/unreleased-changes-exists.ts index 59adedbdbb0e..7944edbac73f 100644 --- a/scripts/release/unreleased-changes-exists.ts +++ b/scripts/release/unreleased-changes-exists.ts @@ -4,6 +4,7 @@ import program from 'commander'; import { z } from 'zod'; import { setOutput } from '@actions/core'; import { intersection } from 'lodash'; +import { esMain } from '../utils/esmain'; import type { Change } from './utils/get-changes'; import { RELEASED_LABELS, getChanges } from './utils/get-changes'; import { getCurrentVersion } from './get-current-version'; @@ -77,7 +78,7 @@ ${chalk.blue(changesToRelease.map(({ title, pull }) => ` #${pull}: ${title}`).j return { changesToRelease, hasChangesToRelease }; }; -if (require.main === module) { +if (esMain(import.meta.url)) { const parsed = program.parse(); run(parsed.opts()).catch((err) => { console.error(err); diff --git a/scripts/release/version.ts b/scripts/release/version.ts index 3b34d288a8c7..c56d7afa5b54 100644 --- a/scripts/release/version.ts +++ b/scripts/release/version.ts @@ -6,9 +6,10 @@ import path from 'path'; import program from 'commander'; import semver from 'semver'; import { z } from 'zod'; +import { execaCommand } from 'execa'; +import { esMain } from '../utils/esmain'; import type { Workspace } from '../utils/workspace'; import { getWorkspaces } from '../utils/workspace'; -import { execaCommand } from '../utils/exec'; program .name('version') @@ -283,6 +284,7 @@ export const run = async (options: unknown) => { await execaCommand(`yarn install --mode=update-lockfile`, { cwd: path.join(CODE_DIR_PATH), stdio: verbose ? 'inherit' : undefined, + cleanup: true, }); console.log(`โœ… Updated lock file with ${chalk.blue('yarn install --mode=update-lockfile')}`); } @@ -293,7 +295,7 @@ export const run = async (options: unknown) => { } }; -if (require.main === module) { +if (esMain(import.meta.url)) { const options = program.parse().opts(); run(options).catch((err) => { console.error(err); diff --git a/scripts/release/write-changelog.ts b/scripts/release/write-changelog.ts index 41bc9b72bd36..5af64eba3a8e 100644 --- a/scripts/release/write-changelog.ts +++ b/scripts/release/write-changelog.ts @@ -5,6 +5,7 @@ import program from 'commander'; import semver from 'semver'; import { z } from 'zod'; import { readFile, writeFile, writeJson } from 'fs-extra'; +import { esMain } from '../utils/esmain'; import { getChanges } from './utils/get-changes'; program @@ -132,7 +133,7 @@ export const run = async (args: unknown[], options: unknown) => { console.log(`โœ… Wrote Changelog to file`); }; -if (require.main === module) { +if (esMain(import.meta.url)) { const parsed = program.parse(); run(parsed.args, parsed.opts()).catch((err) => { console.error(err); diff --git a/scripts/run-registry.ts b/scripts/run-registry.ts index 8a7017b34fa1..71ae3dd334de 100755 --- a/scripts/run-registry.ts +++ b/scripts/run-registry.ts @@ -89,7 +89,10 @@ const publish = async (packages: { name: string; location: string }[], url: stri ); const tarballFilename = `${name.replace('@', '').replace('/', '-')}.tgz`; - const command = `cd ${location} && yarn pack --out=${PACKS_DIRECTORY}/${tarballFilename} && cd ${PACKS_DIRECTORY} && npm publish ./${tarballFilename} --registry ${url} --force --access restricted --ignore-scripts`; + const command = `cd ${path.resolve( + '../code', + location + )} && yarn pack --out=${PACKS_DIRECTORY}/${tarballFilename} && cd ${PACKS_DIRECTORY} && npm publish ./${tarballFilename} --registry ${url} --force --access restricted --ignore-scripts`; exec(command, (e) => { if (e) { rej(e); diff --git a/scripts/sandbox/generate.ts b/scripts/sandbox/generate.ts index 22a4eccf4d14..e14b8bac5f04 100755 --- a/scripts/sandbox/generate.ts +++ b/scripts/sandbox/generate.ts @@ -6,7 +6,8 @@ import prettyTime from 'pretty-hrtime'; import { copy, emptyDir, ensureDir, move, remove, rename, writeFile } from 'fs-extra'; import { program } from 'commander'; import { directory } from 'tempy'; -import { execaCommand } from '../utils/exec'; +import { execaCommand } from 'execa'; +import { esMain } from '../utils/esmain'; import type { OptionValues } from '../utils/options'; import { createOptions } from '../utils/options'; @@ -14,8 +15,6 @@ import { allTemplates as sandboxTemplates } from '../../code/lib/cli/src/sandbox import storybookVersions from '../../code/lib/cli/src/versions'; import { JsPackageManagerFactory } from '../../code/lib/cli/src/js-package-manager/JsPackageManagerFactory'; -import { maxConcurrentTasks } from '../utils/maxConcurrentTasks'; - // eslint-disable-next-line import/no-cycle import { localizeYarnConfigFiles, setupYarn } from './utils/yarn'; import type { GeneratorConfig } from './utils/types'; @@ -70,23 +69,31 @@ const addStorybook = async ({ }) => { const beforeDir = join(baseDir, BEFORE_DIR_NAME); const afterDir = join(baseDir, AFTER_DIR_NAME); - const tmpDir = join(baseDir, 'tmp'); - - await ensureDir(tmpDir); - await emptyDir(tmpDir); - await copy(beforeDir, tmpDir); + const tmpDir = directory(); - const packageManager = JsPackageManagerFactory.getPackageManager({}, tmpDir); - if (localRegistry) { - await withLocalRegistry(packageManager, async () => { - await packageManager.addPackageResolutions(storybookVersions); + try { + await copy(beforeDir, tmpDir); + + const packageManager = JsPackageManagerFactory.getPackageManager({}, tmpDir); + if (localRegistry) { + await withLocalRegistry(packageManager, async () => { + await packageManager.addPackageResolutions({ + ...storybookVersions, + // Yarn1 Issue: https://github.com/storybookjs/storybook/issues/22431 + jackspeak: '2.1.1', + }); + await sbInit(tmpDir, flags, debug); + }); + } else { await sbInit(tmpDir, flags, debug); - }); - } else { - await sbInit(tmpDir, flags, debug); + } + } catch (e) { + await remove(tmpDir); + throw e; } + await rename(tmpDir, afterDir); }; @@ -98,6 +105,7 @@ export const runCommand = async (script: string, options: ExecaOptions, debug = return execaCommand(script, { stdout: debug ? 'inherit' : 'ignore', shell: true, + cleanup: true, ...options, }); }; @@ -129,9 +137,9 @@ const runGenerators = async ( console.log('Debug mode enabled. Verbose logs will be printed to the console.'); } - console.log(`๐Ÿคนโ€โ™‚๏ธ Generating sandboxes with a concurrency of ${maxConcurrentTasks}`); + console.log(`๐Ÿคนโ€โ™‚๏ธ Generating sandboxes with a concurrency of ${1}`); - const limit = pLimit(maxConcurrentTasks); + const limit = pLimit(1); await Promise.all( generators.map(({ dirName, name, script, expected }) => @@ -250,7 +258,7 @@ export const generate = async ({ await runGenerators(generatorConfigs, localRegistry, debug); }; -if (require.main === module) { +if (esMain(import.meta.url)) { program .description('Generate sandboxes from a set of possible templates') .option('--templates [templates...]', 'Space-delimited list of templates to include') diff --git a/scripts/sandbox/publish.ts b/scripts/sandbox/publish.ts index a40fc2bd0540..e4307690efdd 100755 --- a/scripts/sandbox/publish.ts +++ b/scripts/sandbox/publish.ts @@ -3,7 +3,7 @@ import { join } from 'path'; import { existsSync } from 'fs'; import * as tempy from 'tempy'; import { copy, emptyDir, readdir, remove, stat, writeFile } from 'fs-extra'; -import { execaCommand } from '../utils/exec'; +import { execaCommand } from 'execa'; import { getTemplatesData, renderTemplate } from './utils/template'; // eslint-disable-next-line import/no-cycle @@ -26,8 +26,8 @@ const publish = async (options: PublishOptions & { tmpFolder: string }) => { const templatesData = await getTemplatesData(branch === 'main' ? 'main' : 'next'); logger.log(`๐Ÿ‘ฏโ€โ™‚๏ธ Cloning the repository ${remote} in branch ${branch}`); - await execaCommand(`git clone ${remote} .`, { cwd: tmpFolder }); - await execaCommand(`git checkout ${branch}`, { cwd: tmpFolder }); + await execaCommand(`git clone ${remote} .`, { cwd: tmpFolder, cleanup: true }); + await execaCommand(`git checkout ${branch}`, { cwd: tmpFolder, cleanup: true }); // otherwise old files will stick around and result inconsistent states logger.log(`๐Ÿ—‘ Delete existing template dirs from clone`); diff --git a/scripts/sandbox/utils/git.ts b/scripts/sandbox/utils/git.ts index 84fe02f25cdb..71de33aa38fe 100644 --- a/scripts/sandbox/utils/git.ts +++ b/scripts/sandbox/utils/git.ts @@ -1,11 +1,11 @@ import fetch from 'node-fetch'; import invariant from 'tiny-invariant'; -import { execaCommand } from '../../utils/exec'; +import { execaCommand } from 'execa'; // eslint-disable-next-line import/no-cycle import { logger } from '../publish'; -const { version: storybookVersion } = require('../../../code/package.json'); +import { version as storybookVersion } from '../../../code/package.json'; const getTheLastCommitHashThatUpdatedTheSandboxRepo = async (branch: string) => { const owner = 'storybookjs'; @@ -50,9 +50,9 @@ export async function commitAllToGit({ cwd, branch }: { cwd: string; branch: str try { logger.log(`๐Ÿ’ช Committing everything to the repository`); - await execaCommand('git add .', { cwd }); + await execaCommand('git add .', { cwd, cleanup: true }); - const currentCommitHash = (await execaCommand('git rev-parse HEAD')).stdout + const currentCommitHash = (await execaCommand('git rev-parse HEAD', { cleanup: true })).stdout .toString() .slice(0, 12); @@ -63,7 +63,8 @@ export async function commitAllToGit({ cwd, branch }: { cwd: string; branch: str const previousCommitHash = await getTheLastCommitHashThatUpdatedTheSandboxRepo(branch); const mergeCommits = ( await execaCommand( - `git log ${previousCommitHash}..${currentCommitHash} --merges --pretty=%s` + `git log ${previousCommitHash}..${currentCommitHash} --merges --pretty=%s`, + { cleanup: true } ) ).stdout .toString() @@ -95,6 +96,7 @@ export async function commitAllToGit({ cwd, branch }: { cwd: string; branch: str await execaCommand(gitCommitCommand, { shell: true, + cleanup: true, cwd, }); } catch (e) { diff --git a/scripts/tasks/bench.ts b/scripts/tasks/bench.ts index 664f345d541e..cead08852edb 100644 --- a/scripts/tasks/bench.ts +++ b/scripts/tasks/bench.ts @@ -1,11 +1,12 @@ +/* eslint-disable import/extensions */ +import prettyBytes from 'pretty-bytes'; +import prettyTime from 'pretty-ms'; + import type { Task } from '../task'; import { PORT as devPort, dev } from './dev'; import { PORT as servePort, serve } from './serve'; -// eslint-disable-next-line @typescript-eslint/no-implied-eval -const dynamicImport = new Function('specifier', 'return import(specifier)'); - const logger = console; export const bench: Task = { @@ -19,10 +20,10 @@ export const bench: Task = { const controllers: AbortController[] = []; try { const { disableDocs } = options; - const { browse } = await import('../bench/browse'); - const { saveBench, loadBench } = await import('../bench/utils'); - const { default: prettyBytes } = await dynamicImport('pretty-bytes'); - const { default: prettyTime } = await dynamicImport('pretty-ms'); + // @ts-expect-error Default import required for dynamic import processed by esbuild + const { browse } = (await import('../bench/browse.ts')).default; + // @ts-expect-error Default import required for dynamic import processed by esbuild + const { saveBench, loadBench } = (await import('../bench/utils.ts')).default; const devController = await dev.run(details, { ...options, debug: false }); if (!devController) { @@ -30,6 +31,7 @@ export const bench: Task = { } controllers.push(devController); const devBrowseResult = await browse(`http://localhost:${devPort}`, { disableDocs }); + devController.abort(); const serveController = await serve.run(details, { ...options, debug: false }); @@ -37,6 +39,7 @@ export const bench: Task = { throw new Error('serve: controller is null'); } controllers.push(serveController); + const buildBrowseResult = await browse(`http://localhost:${servePort}`, { disableDocs }); serveController.abort(); diff --git a/scripts/tasks/dev.ts b/scripts/tasks/dev.ts index 92e885f52078..138bb912cef8 100644 --- a/scripts/tasks/dev.ts +++ b/scripts/tasks/dev.ts @@ -1,4 +1,5 @@ import detectFreePort from 'detect-port'; +import waitOn from 'wait-on'; import type { Task } from '../task'; import { exec } from '../utils/exec'; @@ -18,7 +19,6 @@ export const dev: Task = { async run({ sandboxDir, selectedTask }, { dryRun, debug }) { const controller = new AbortController(); const devCommand = `yarn storybook --port ${PORT}${selectedTask === 'dev' ? '' : ' --ci'}`; - const { default: waitOn } = await import('wait-on'); const start = now(); exec( diff --git a/scripts/tasks/install.ts b/scripts/tasks/install.ts index 653eda568381..e78130ab514f 100644 --- a/scripts/tasks/install.ts +++ b/scripts/tasks/install.ts @@ -1,6 +1,7 @@ import { pathExists, remove } from 'fs-extra'; import { join } from 'path'; import type { Task } from '../task'; +import { checkDependencies } from '../utils/cli-utils'; export const install: Task = { description: 'Install the dependencies of the monorepo', @@ -8,8 +9,7 @@ export const install: Task = { return pathExists(join(codeDir, 'node_modules')); }, async run({ codeDir }) { - // eslint-disable-next-line global-require - await require('../utils/cli-utils').checkDependencies(); + await checkDependencies(); // these are webpack4 types, we we should never use await remove(join(codeDir, 'node_modules', '@types', 'webpack')); diff --git a/scripts/tasks/sandbox-parts.ts b/scripts/tasks/sandbox-parts.ts index 393169b244f8..45cc2039ef0c 100644 --- a/scripts/tasks/sandbox-parts.ts +++ b/scripts/tasks/sandbox-parts.ts @@ -12,8 +12,9 @@ import { writeJson, } from 'fs-extra'; import { join, resolve, sep } from 'path'; - +import { createRequire } from 'module'; import slash from 'slash'; + import type { Task } from '../task'; import { executeCLIStep, steps } from '../utils/cli-step'; import { @@ -92,6 +93,25 @@ export const install: Task['run'] = async ({ sandboxDir }, { link, dryRun, debug await addPackageResolutions({ cwd, dryRun, debug }); await configureYarn2ForVerdaccio({ cwd, dryRun, debug }); + // Add vite plugin workarounds for frameworks that need it + // (to support vite 5 without peer dep errors) + if ( + [ + 'bench-react-vite-default-ts', + 'bench-react-vite-default-ts-nodocs', + 'bench-react-vite-default-ts-test-build', + 'internal-ssv6-vite', + 'react-vite-default-js', + 'react-vite-default-ts', + 'svelte-vite-default-js', + 'svelte-vite-default-ts', + 'vue3-vite-default-js', + 'vue3-vite-default-ts', + ].includes(sandboxDir.split(sep).at(-1)) + ) { + await addWorkaroundResolutions({ cwd, dryRun, debug }); + } + await exec( 'yarn install', { cwd }, @@ -167,6 +187,7 @@ export const init: Task['run'] = async ( // loader for such files. NOTE this isn't necessary for Vite, as far as we know. function addEsbuildLoaderToStories(mainConfig: ConfigFile) { // NOTE: the test regexp here will apply whether the path is symlink-preserved or otherwise + const require = createRequire(import.meta.url); const esbuildLoaderPath = require.resolve('../../code/node_modules/esbuild-loader'); const storiesMdxLoaderPath = require.resolve( '../../code/node_modules/@storybook/mdx2-csf/loader' @@ -579,7 +600,8 @@ async function prepareAngularSandbox(cwd: string) { packageJson.scripts = { ...packageJson.scripts, - 'docs:json': 'DIR=$PWD; cd ../../scripts; yarn ts-node combine-compodoc $DIR', + 'docs:json': + 'DIR=$PWD; cd ../../scripts; node --loader esbuild-register/loader -r esbuild-register combine-compodoc $DIR', storybook: `yarn docs:json && ${packageJson.scripts.storybook}`, 'build-storybook': `yarn docs:json && ${packageJson.scripts['build-storybook']}`, }; diff --git a/scripts/tasks/sandbox.ts b/scripts/tasks/sandbox.ts index 58e7d92fe421..5e3bade5d5d3 100644 --- a/scripts/tasks/sandbox.ts +++ b/scripts/tasks/sandbox.ts @@ -55,9 +55,10 @@ export const sandbox: Task = { await remove(details.sandboxDir); } - const { create, install, addStories, extendMain, init, addExtraDependencies } = await import( - './sandbox-parts' - ); + const { create, install, addStories, extendMain, init, addExtraDependencies } = + // @ts-expect-error esbuild for some reason exports a default object + // eslint-disable-next-line import/extensions + (await import('./sandbox-parts.ts')).default; let startTime = now(); await create(details, options); diff --git a/scripts/tasks/serve.ts b/scripts/tasks/serve.ts index f5e637f33bb9..2b3a5cc8c4b9 100644 --- a/scripts/tasks/serve.ts +++ b/scripts/tasks/serve.ts @@ -1,4 +1,5 @@ import detectFreePort from 'detect-port'; +import waitOn from 'wait-on'; import type { Task } from '../task'; import { exec } from '../utils/exec'; @@ -26,7 +27,6 @@ export const serve: Task = { throw err; } }); - const { default: waitOn } = await import('wait-on'); await waitOn({ resources: [`http://localhost:${PORT}`], interval: 16 }); return controller; diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json index c82e14a95108..9f5821bab723 100644 --- a/scripts/tsconfig.json +++ b/scripts/tsconfig.json @@ -5,15 +5,15 @@ "incremental": false, "noImplicitAny": true, "jsx": "react", - "moduleResolution": "Node", - "target": "ES2020", - "module": "CommonJS", + "moduleResolution": "bundler", + "target": "es2022", + "module": "ES2022", "skipLibCheck": true, "allowSyntheticDefaultImports": true, "esModuleInterop": true, "isolatedModules": true, "strictBindCallApply": true, - "lib": ["dom", "esnext"], + "lib": ["dom", "es2023"], "types": ["node", "jest"], "strict": true, "strictNullChecks": false, @@ -25,12 +25,5 @@ "resolveJsonModule": true }, "exclude": ["dist", "**/dist", "node_modules", "**/node_modules"], - "include": ["./**/*", "./.eslintrc.js"], - "ts-node": { - "transpileOnly": true, - "files": true, - "compilerOptions": { - "types": ["node"] - } - } + "include": ["./**/*", "./.eslintrc.js"] } diff --git a/scripts/upload-bench.ts b/scripts/upload-bench.ts index e841ec363c33..250c6154ce25 100644 --- a/scripts/upload-bench.ts +++ b/scripts/upload-bench.ts @@ -1,10 +1,10 @@ import { join } from 'path'; import { BigQuery } from '@google-cloud/bigquery'; +import { execaCommand } from 'execa'; import type { BenchResults } from './bench/types'; import { loadBench } from './bench/utils'; import { SANDBOX_DIRECTORY } from './utils/constants'; -import { execaCommand } from './utils/exec'; const templateKey = process.argv[2]; @@ -64,8 +64,11 @@ const uploadBench = async () => { const row = { ...defaults, branch: - process.env.CIRCLE_BRANCH || (await execaCommand('git rev-parse --abbrev-ref HEAD')).stdout, - commit: process.env.CIRCLE_SHA1 || (await execaCommand('git rev-parse HEAD')).stdout, + process.env.CIRCLE_BRANCH || + (await execaCommand('git rev-parse --abbrev-ref HEAD', { cleanup: true })).stdout, + commit: + process.env.CIRCLE_SHA1 || + (await execaCommand('git rev-parse HEAD', { cleanup: true })).stdout, timestamp: new Date().toISOString(), label: templateKey, ...results, diff --git a/scripts/utils/cli-step.ts b/scripts/utils/cli-step.ts index d990597619f3..9e736f30b2a4 100644 --- a/scripts/utils/cli-step.ts +++ b/scripts/utils/cli-step.ts @@ -1,7 +1,9 @@ +import { createRequire } from 'module'; import type { OptionSpecifier, OptionValues } from './options'; import { createOptions, getCommand } from './options'; import { exec } from './exec'; +const require = createRequire(import.meta.url); const cliExecutable = require.resolve('../../code/lib/cli/bin/index.js'); export type CLIStep = { diff --git a/scripts/utils/cli-utils.js b/scripts/utils/cli-utils.ts similarity index 76% rename from scripts/utils/cli-utils.js rename to scripts/utils/cli-utils.ts index ba52d9338875..c37f2a950d74 100644 --- a/scripts/utils/cli-utils.js +++ b/scripts/utils/cli-utils.ts @@ -1,14 +1,14 @@ -const { spawn } = require('child_process'); -const { join } = require('path'); -const { existsSync } = require('fs'); +import { spawn } from 'child_process'; +import { join } from 'path'; +import { existsSync } from 'fs'; const logger = console; -const checkDependencies = async () => { +export const checkDependencies = async () => { const scriptsPath = join(__dirname, '..'); const codePath = join(__dirname, '..', '..', 'code'); - const tasks = []; + const tasks: Array = []; if (!existsSync(join(scriptsPath, 'node_modules'))) { tasks.push( @@ -35,8 +35,8 @@ const checkDependencies = async () => { await Promise.all( tasks.map( (t) => - new Promise((res, rej) => { - t.on('exit', (code) => { + new Promise((res, rej) => { + t.on('exit', (code: number) => { if (code !== 0) { rej(); } else { @@ -51,12 +51,8 @@ const checkDependencies = async () => { }); // give the filesystem some time - await new Promise((res, rej) => { + await new Promise((res) => { setTimeout(res, 1000); }); } }; - -module.exports = { - checkDependencies, -}; diff --git a/scripts/utils/concurrency.ts b/scripts/utils/concurrency.ts index ab49dc41e8f5..781c7c0d6787 100644 --- a/scripts/utils/concurrency.ts +++ b/scripts/utils/concurrency.ts @@ -1,9 +1,7 @@ -const os = require('os'); +import os from 'os'; /** * The maximum number of concurrent tasks we want to have on some CLI and CI tasks. * The amount of CPUS minus one, arbitrary limited to 15 to not overload CI executors. - * @type {number} */ export const maxConcurrentTasks = Math.min(Math.max(1, os.cpus().length - 1), 15); - diff --git a/scripts/utils/esmain.ts b/scripts/utils/esmain.ts new file mode 100644 index 000000000000..edc9d07b4322 --- /dev/null +++ b/scripts/utils/esmain.ts @@ -0,0 +1,40 @@ +import path from 'path'; +import process from 'process'; +import { createRequire } from 'module'; +import { fileURLToPath } from 'url'; + +/** + * Strip the extension from a filename if it has one. + * @param {string} name A filename. + * @return {string} The filename without a path. + */ +export function stripExt(name: string) { + const extension = path.extname(name); + if (!extension) { + return name; + } + + return name.slice(0, -extension.length); +} + +/** + * Check if a module was run directly with node as opposed to being + * imported from another module. + */ +export function esMain(url: string) { + if (!url || !process.argv[1]) { + return false; + } + + const require = createRequire(url); + const scriptPath = require.resolve(process.argv[1]); + + const modulePath = fileURLToPath(url); + + const extension = path.extname(scriptPath); + if (extension) { + return modulePath === scriptPath; + } + + return stripExt(modulePath) === scriptPath; +} diff --git a/scripts/utils/exec.ts b/scripts/utils/exec.ts index 74a886189420..536c36aecf14 100644 --- a/scripts/utils/exec.ts +++ b/scripts/utils/exec.ts @@ -1,6 +1,7 @@ /* eslint-disable no-await-in-loop, no-restricted-syntax */ import type { ExecaChildProcess, Options } from 'execa'; import chalk from 'chalk'; +import { execa } from 'execa'; const logger = console; @@ -12,32 +13,11 @@ type StepOptions = { signal?: AbortSignal; }; -// Note this is to fool `ts-node` into not turning the `import()` into a `require()`. -// See: https://github.com/TypeStrong/ts-node/discussions/1290 -// eslint-disable-next-line @typescript-eslint/no-implied-eval -const dynamicImport = new Function('specifier', 'return import(specifier)'); -export const getExeca = async () => (await dynamicImport('execa')) as typeof import('execa'); - -// Reimplementation of `execaCommand` to use `getExeca` -export const execaCommand = async ( - command: string, - options: Options = {} -): Promise> => { - const execa = await getExeca(); - // We await here because execaCommand returns a promise, but that's not what the user expects - // eslint-disable-next-line @typescript-eslint/return-await - return await execa.execaCommand(command, { - cleanup: true, - ...options, - }); -}; - export const exec = async ( command: string | string[], options: Options = {}, { startMessage, errorMessage, dryRun, debug, signal }: StepOptions = {} ): Promise => { - const execa = await getExeca(); logger.info(); if (startMessage) logger.info(startMessage); @@ -57,12 +37,12 @@ export const exec = async ( try { if (typeof command === 'string') { logger.debug(`> ${command}`); - currentChild = execa.execaCommand(command, { ...defaultOptions, ...options }); + currentChild = execa(command, { ...defaultOptions, ...options }); await currentChild; } else { for (const subcommand of command) { logger.debug(`> ${subcommand}`); - currentChild = execa.execaCommand(subcommand, { ...defaultOptions, ...options }); + currentChild = execa(subcommand, { ...defaultOptions, ...options }); await currentChild; } } diff --git a/scripts/utils/workspace.ts b/scripts/utils/workspace.ts index a9af2eb46fa2..7edef2d14f16 100644 --- a/scripts/utils/workspace.ts +++ b/scripts/utils/workspace.ts @@ -1,6 +1,6 @@ import memoize from 'memoizerific'; +import { execaCommand } from 'execa'; import { CODE_DIRECTORY } from './constants'; -import { execaCommand } from './exec'; export type Workspace = { name: string; location: string }; diff --git a/scripts/utils/yarn.ts b/scripts/utils/yarn.ts index fcefed46ade1..19f87d1c0182 100644 --- a/scripts/utils/yarn.ts +++ b/scripts/utils/yarn.ts @@ -67,7 +67,10 @@ export const addWorkaroundResolutions = async ({ cwd, dryRun }: YarnOptions) => const packageJson = await readJSON(packageJsonPath); packageJson.resolutions = { ...packageJson.resolutions, - '@vitejs/plugin-react': '^4.0.0', // due to conflicting version in @storybook/vite-react + // Due to our support of older vite versions + '@vitejs/plugin-react': '4.2.0', + '@sveltejs/vite-plugin-svelte': '3.0.1', + '@vitejs/plugin-vue': '4.5.0', }; await writeJSON(packageJsonPath, packageJson, { spaces: 2 }); }; diff --git a/scripts/yarn.lock b/scripts/yarn.lock index 411298c2d9b9..dfb879c649f2 100644 --- a/scripts/yarn.lock +++ b/scripts/yarn.lock @@ -1680,15 +1680,6 @@ __metadata: languageName: node linkType: hard -"@cspotcode/source-map-support@npm:^0.8.0": - version: 0.8.1 - resolution: "@cspotcode/source-map-support@npm:0.8.1" - dependencies: - "@jridgewell/trace-mapping": "npm:0.3.9" - checksum: 05c5368c13b662ee4c122c7bfbe5dc0b613416672a829f3e78bc49a357a197e0218d6e74e7c66cfcd04e15a179acab080bd3c69658c9fbefd0e1ccd950a07fc6 - languageName: node - linkType: hard - "@emotion/css-prettifier@npm:^1.1.3": version: 1.1.3 resolution: "@emotion/css-prettifier@npm:1.1.3" @@ -2170,6 +2161,15 @@ __metadata: languageName: node linkType: hard +"@jest/create-cache-key-function@npm:^27.4.2": + version: 27.5.1 + resolution: "@jest/create-cache-key-function@npm:27.5.1" + dependencies: + "@jest/types": "npm:^27.5.1" + checksum: 1890ac93fad852e0a98c31de1e5f2c548974aefd36e838d27b70834dda1654a153ed6a52258447ebacfd47463e9bdb83750631bee827797c7b9973c083998a96 + languageName: node + linkType: hard + "@jest/environment@npm:^29.7.0": version: 29.7.0 resolution: "@jest/environment@npm:29.7.0" @@ -2378,7 +2378,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/resolve-uri@npm:^3.0.3, @jridgewell/resolve-uri@npm:^3.1.0": +"@jridgewell/resolve-uri@npm:^3.1.0": version: 3.1.1 resolution: "@jridgewell/resolve-uri@npm:3.1.1" checksum: 0dbc9e29bc640bbbdc5b9876d2859c69042bfcf1423c1e6421bcca53e826660bff4e41c7d4bcb8dbea696404231a6f902f76ba41835d049e20f2dd6cffb713bf @@ -2399,16 +2399,6 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:0.3.9": - version: 0.3.9 - resolution: "@jridgewell/trace-mapping@npm:0.3.9" - dependencies: - "@jridgewell/resolve-uri": "npm:^3.0.3" - "@jridgewell/sourcemap-codec": "npm:^1.4.10" - checksum: fa425b606d7c7ee5bfa6a31a7b050dd5814b4082f318e0e4190f991902181b4330f43f4805db1dd4f2433fd0ed9cc7a7b9c2683f1deeab1df1b0a98b1e24055b - languageName: node - linkType: hard - "@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.9": version: 0.3.19 resolution: "@jridgewell/trace-mapping@npm:0.3.19" @@ -3113,7 +3103,8 @@ __metadata: "@storybook/jest": "npm:next" "@storybook/linter-config": "npm:^3.1.2" "@storybook/testing-library": "npm:next" - "@swc/core": "npm:1.3.82" + "@swc/core": "npm:^1.3.95" + "@swc/jest": "npm:^0.2.29" "@testing-library/dom": "npm:^9.3.0" "@testing-library/jest-dom": "npm:^5.11.9" "@testing-library/react": "npm:^14.0.0" @@ -3138,6 +3129,7 @@ __metadata: "@types/serve-static": "npm:^1.13.8" "@types/uuid": "npm:^9.0.1" "@types/wait-on": "npm:^5.3.1" + "@types/window-size": "npm:^1.1.3" "@typescript-eslint/eslint-plugin": "npm:^5.45.0" "@typescript-eslint/experimental-utils": "npm:^5.45.0" "@typescript-eslint/parser": "npm:^5.45.0" @@ -3146,6 +3138,7 @@ __metadata: babel-eslint: "npm:^10.1.0" babel-loader: "npm:^9.1.2" boxen: "npm:^5.1.2" + browser-assert: "npm:^1.2.1" chalk: "npm:^4.1.0" codecov: "npm:^3.8.1" commander: "npm:^6.2.1" @@ -3181,6 +3174,7 @@ __metadata: jest-os-detection: "npm:^1.3.1" jest-serializer-html: "npm:^7.1.0" jest-watch-typeahead: "npm:^2.2.1" + jest_workaround: "npm:^0.79.19" json5: "npm:^2.2.3" junit-xml: "npm:^1.2.0" lint-staged: "npm:^10.5.4" @@ -3219,10 +3213,9 @@ __metadata: trash: "npm:^7.0.0" ts-dedent: "npm:^2.0.0" ts-loader: "npm:^9.4.2" - ts-node: "npm:^10.9.1" tsup: "npm:^6.7.0" type-fest: "npm:~2.19" - typescript: "npm:5.1.6" + typescript: "npm:5.2.2" util: "npm:^0.12.4" uuid: "npm:^9.0.0" verdaccio: "npm:^5.19.1" @@ -3261,91 +3254,92 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.3.82": - version: 1.3.82 - resolution: "@swc/core-darwin-arm64@npm:1.3.82" +"@swc/core-darwin-arm64@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-darwin-arm64@npm:1.3.95" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.3.82": - version: 1.3.82 - resolution: "@swc/core-darwin-x64@npm:1.3.82" +"@swc/core-darwin-x64@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-darwin-x64@npm:1.3.95" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.3.82": - version: 1.3.82 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.82" +"@swc/core-linux-arm-gnueabihf@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.95" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.3.82": - version: 1.3.82 - resolution: "@swc/core-linux-arm64-gnu@npm:1.3.82" +"@swc/core-linux-arm64-gnu@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-linux-arm64-gnu@npm:1.3.95" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.3.82": - version: 1.3.82 - resolution: "@swc/core-linux-arm64-musl@npm:1.3.82" +"@swc/core-linux-arm64-musl@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-linux-arm64-musl@npm:1.3.95" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.3.82": - version: 1.3.82 - resolution: "@swc/core-linux-x64-gnu@npm:1.3.82" +"@swc/core-linux-x64-gnu@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-linux-x64-gnu@npm:1.3.95" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.3.82": - version: 1.3.82 - resolution: "@swc/core-linux-x64-musl@npm:1.3.82" +"@swc/core-linux-x64-musl@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-linux-x64-musl@npm:1.3.95" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.3.82": - version: 1.3.82 - resolution: "@swc/core-win32-arm64-msvc@npm:1.3.82" +"@swc/core-win32-arm64-msvc@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-win32-arm64-msvc@npm:1.3.95" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.3.82": - version: 1.3.82 - resolution: "@swc/core-win32-ia32-msvc@npm:1.3.82" +"@swc/core-win32-ia32-msvc@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-win32-ia32-msvc@npm:1.3.95" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.3.82": - version: 1.3.82 - resolution: "@swc/core-win32-x64-msvc@npm:1.3.82" +"@swc/core-win32-x64-msvc@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-win32-x64-msvc@npm:1.3.95" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@swc/core@npm:1.3.82": - version: 1.3.82 - resolution: "@swc/core@npm:1.3.82" +"@swc/core@npm:^1.3.95": + version: 1.3.95 + resolution: "@swc/core@npm:1.3.95" dependencies: - "@swc/core-darwin-arm64": "npm:1.3.82" - "@swc/core-darwin-x64": "npm:1.3.82" - "@swc/core-linux-arm-gnueabihf": "npm:1.3.82" - "@swc/core-linux-arm64-gnu": "npm:1.3.82" - "@swc/core-linux-arm64-musl": "npm:1.3.82" - "@swc/core-linux-x64-gnu": "npm:1.3.82" - "@swc/core-linux-x64-musl": "npm:1.3.82" - "@swc/core-win32-arm64-msvc": "npm:1.3.82" - "@swc/core-win32-ia32-msvc": "npm:1.3.82" - "@swc/core-win32-x64-msvc": "npm:1.3.82" - "@swc/types": "npm:^0.1.4" + "@swc/core-darwin-arm64": "npm:1.3.95" + "@swc/core-darwin-x64": "npm:1.3.95" + "@swc/core-linux-arm-gnueabihf": "npm:1.3.95" + "@swc/core-linux-arm64-gnu": "npm:1.3.95" + "@swc/core-linux-arm64-musl": "npm:1.3.95" + "@swc/core-linux-x64-gnu": "npm:1.3.95" + "@swc/core-linux-x64-musl": "npm:1.3.95" + "@swc/core-win32-arm64-msvc": "npm:1.3.95" + "@swc/core-win32-ia32-msvc": "npm:1.3.95" + "@swc/core-win32-x64-msvc": "npm:1.3.95" + "@swc/counter": "npm:^0.1.1" + "@swc/types": "npm:^0.1.5" peerDependencies: "@swc/helpers": ^0.5.0 dependenciesMeta: @@ -3372,14 +3366,33 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: cee1f56e969074fe9e23fe51348d7da0c921949f5845e57030824d0e32d09fcefc647371c982e82836cc72c27c5fc5370c6aaff4f8f1c527d1c39c5ab6203e17 + checksum: f0386b7cc1288d97ef3636fff06c1798914e453b574934778d6c90019acf6893bf566c805369ab74b7c7b559243de37e3fe8bb2bbe9b1ac3214a0575cc4f7335 languageName: node linkType: hard -"@swc/types@npm:^0.1.4": - version: 0.1.4 - resolution: "@swc/types@npm:0.1.4" - checksum: f506cb23a08c604c6343c51f47e399d2c59f2e4a7df0689849284915a9f726ca65ce86d5358b5ed88c6ba1fce135a25614b1adda1beedf570a24c230466d6a46 +"@swc/counter@npm:^0.1.1": + version: 0.1.2 + resolution: "@swc/counter@npm:0.1.2" + checksum: 18be012107d4ba1f79776c48d83391ca2159103d7d31a59ff52fcc8024db51b71c5f46714a9fb73981739bc8a38dc6f385a046b71cc08f6043f3c47f5c409eab + languageName: node + linkType: hard + +"@swc/jest@npm:^0.2.29": + version: 0.2.29 + resolution: "@swc/jest@npm:0.2.29" + dependencies: + "@jest/create-cache-key-function": "npm:^27.4.2" + jsonc-parser: "npm:^3.2.0" + peerDependencies: + "@swc/core": "*" + checksum: 10f34341f9bc8003cec44f91a88b531ba44094aad97b2f8410fb2f94db9eb3b8fc7f6d14ba867eb9c1dc6ba29cc46058244b8280d673a7c681062fe0dc73c3f0 + languageName: node + linkType: hard + +"@swc/types@npm:^0.1.5": + version: 0.1.5 + resolution: "@swc/types@npm:0.1.5" + checksum: b35f93fe896a2240f6f10544e408f9648c2bd4bcff9bd8d022d9a6942d31cf859f86119fb0bbb04a12eefa1f6a6745ffc7d18f3a490d76d7b6a074a7c9608144 languageName: node linkType: hard @@ -3462,34 +3475,6 @@ __metadata: languageName: node linkType: hard -"@tsconfig/node10@npm:^1.0.7": - version: 1.0.9 - resolution: "@tsconfig/node10@npm:1.0.9" - checksum: c176a2c1e1b16be120c328300ea910df15fb9a5277010116d26818272341a11483c5a80059389d04edacf6fd2d03d4687ad3660870fdd1cc0b7109e160adb220 - languageName: node - linkType: hard - -"@tsconfig/node12@npm:^1.0.7": - version: 1.0.11 - resolution: "@tsconfig/node12@npm:1.0.11" - checksum: dddca2b553e2bee1308a056705103fc8304e42bb2d2cbd797b84403a223b25c78f2c683ec3e24a095e82cd435387c877239bffcb15a590ba817cd3f6b9a99fd9 - languageName: node - linkType: hard - -"@tsconfig/node14@npm:^1.0.0": - version: 1.0.3 - resolution: "@tsconfig/node14@npm:1.0.3" - checksum: 67c1316d065fdaa32525bc9449ff82c197c4c19092b9663b23213c8cbbf8d88b6ed6a17898e0cbc2711950fbfaf40388938c1c748a2ee89f7234fc9e7fe2bf44 - languageName: node - linkType: hard - -"@tsconfig/node16@npm:^1.0.2": - version: 1.0.4 - resolution: "@tsconfig/node16@npm:1.0.4" - checksum: 05f8f2734e266fb1839eb1d57290df1664fe2aa3b0fdd685a9035806daa635f7519bf6d5d9b33f6e69dd545b8c46bd6e2b5c79acb2b1f146e885f7f11a42a5bb - languageName: node - linkType: hard - "@types/aria-query@npm:^5.0.1": version: 5.0.1 resolution: "@types/aria-query@npm:5.0.1" @@ -4089,6 +4074,15 @@ __metadata: languageName: node linkType: hard +"@types/window-size@npm:^1.1.3": + version: 1.1.3 + resolution: "@types/window-size@npm:1.1.3" + dependencies: + "@types/node": "npm:*" + checksum: 0ec54d81fd160a6ba50136a8fab4d682cf790723e7a6f758cfb1bc08fddc29e0443fbe1817894cc438cb1b4bf57e1edb7e768114221a0bd1682438e25aa7d8c8 + languageName: node + linkType: hard + "@types/yargs-parser@npm:*": version: 21.0.0 resolution: "@types/yargs-parser@npm:21.0.0" @@ -4565,7 +4559,7 @@ __metadata: languageName: node linkType: hard -"acorn-walk@npm:^8.0.2, acorn-walk@npm:^8.1.1": +"acorn-walk@npm:^8.0.2": version: 8.2.0 resolution: "acorn-walk@npm:8.2.0" checksum: dbe92f5b2452c93e960c5594e666dd1fae141b965ff2cb4a1e1d0381e3e4db4274c5ce4ffa3d681a86ca2a8d4e29d5efc0670a08e23fd2800051ea387df56ca2 @@ -4581,7 +4575,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.1.0, acorn@npm:^8.4.1, acorn@npm:^8.8.1, acorn@npm:^8.9.0": +"acorn@npm:^8.1.0, acorn@npm:^8.8.1, acorn@npm:^8.9.0": version: 8.10.0 resolution: "acorn@npm:8.10.0" bin: @@ -4814,13 +4808,6 @@ __metadata: languageName: node linkType: hard -"arg@npm:^4.1.0": - version: 4.1.3 - resolution: "arg@npm:4.1.3" - checksum: 070ff801a9d236a6caa647507bdcc7034530604844d64408149a26b9e87c2f97650055c0f049abd1efc024b334635c01f29e0b632b371ac3f26130f4cf65997a - languageName: node - linkType: hard - "argparse@npm:^1.0.7": version: 1.0.10 resolution: "argparse@npm:1.0.10" @@ -5467,6 +5454,13 @@ __metadata: languageName: node linkType: hard +"browser-assert@npm:^1.2.1": + version: 1.2.1 + resolution: "browser-assert@npm:1.2.1" + checksum: 902abf999f92c9c951fdb6d7352c09eea9a84706258699655f7e7906e42daa06a1ae286398a755872740e05a6a71c43c5d1a0c0431d67a8cdb66e5d859a3fc0c + languageName: node + linkType: hard + "browserslist@npm:^4.21.10, browserslist@npm:^4.21.9": version: 4.21.10 resolution: "browserslist@npm:4.21.10" @@ -6267,13 +6261,6 @@ __metadata: languageName: node linkType: hard -"create-require@npm:^1.1.0": - version: 1.1.1 - resolution: "create-require@npm:1.1.1" - checksum: 157cbc59b2430ae9a90034a5f3a1b398b6738bf510f713edc4d4e45e169bc514d3d99dd34d8d01ca7ae7830b5b8b537e46ae8f3c8f932371b0875c0151d7ec91 - languageName: node - linkType: hard - "cross-env@npm:^7.0.3": version: 7.0.3 resolution: "cross-env@npm:7.0.3" @@ -6789,13 +6776,6 @@ __metadata: languageName: node linkType: hard -"diff@npm:^4.0.1": - version: 4.0.2 - resolution: "diff@npm:4.0.2" - checksum: 81b91f9d39c4eaca068eb0c1eb0e4afbdc5bb2941d197f513dd596b820b956fef43485876226d65d497bebc15666aa2aa82c679e84f65d5f2bfbf14ee46e32c1 - languageName: node - linkType: hard - "diff@npm:^5.0.0": version: 5.1.0 resolution: "diff@npm:5.1.0" @@ -10789,6 +10769,16 @@ __metadata: languageName: node linkType: hard +"jest_workaround@npm:^0.79.19": + version: 0.79.19 + resolution: "jest_workaround@npm:0.79.19" + peerDependencies: + "@swc/core": ^1.3.68 + "@swc/jest": ^0.2.26 + checksum: 387ce514e7ef60129891a4647e5431f1e4179bde70de100d50fa4acc8a4f3c072db9cf65c8a7d971ab11437ace78aa77959c65cdb2e55e718c971905c55e62fd + languageName: node + linkType: hard + "jju@npm:^1.4.0": version: 1.4.0 resolution: "jju@npm:1.4.0" @@ -10995,7 +10985,7 @@ __metadata: languageName: node linkType: hard -"jsonc-parser@npm:3.2.0, jsonc-parser@npm:^3.0.0": +"jsonc-parser@npm:3.2.0, jsonc-parser@npm:^3.0.0, jsonc-parser@npm:^3.2.0": version: 3.2.0 resolution: "jsonc-parser@npm:3.2.0" checksum: 5a12d4d04dad381852476872a29dcee03a57439574e4181d91dca71904fcdcc5e8e4706c0a68a2c61ad9810e1e1c5806b5100d52d3e727b78f5cdc595401045b @@ -11646,13 +11636,6 @@ __metadata: languageName: node linkType: hard -"make-error@npm:^1.1.1": - version: 1.3.6 - resolution: "make-error@npm:1.3.6" - checksum: 171e458d86854c6b3fc46610cfacf0b45149ba043782558c6875d9f42f222124384ad0b468c92e996d815a8a2003817a710c0a160e49c1c394626f76fa45396f - languageName: node - linkType: hard - "make-fetch-happen@npm:^11.0.3": version: 11.1.1 resolution: "make-fetch-happen@npm:11.1.1" @@ -16275,44 +16258,6 @@ __metadata: languageName: node linkType: hard -"ts-node@npm:^10.9.1": - version: 10.9.1 - resolution: "ts-node@npm:10.9.1" - dependencies: - "@cspotcode/source-map-support": "npm:^0.8.0" - "@tsconfig/node10": "npm:^1.0.7" - "@tsconfig/node12": "npm:^1.0.7" - "@tsconfig/node14": "npm:^1.0.0" - "@tsconfig/node16": "npm:^1.0.2" - acorn: "npm:^8.4.1" - acorn-walk: "npm:^8.1.1" - arg: "npm:^4.1.0" - create-require: "npm:^1.1.0" - diff: "npm:^4.0.1" - make-error: "npm:^1.1.1" - v8-compile-cache-lib: "npm:^3.0.1" - yn: "npm:3.1.1" - peerDependencies: - "@swc/core": ">=1.2.50" - "@swc/wasm": ">=1.2.50" - "@types/node": "*" - typescript: ">=2.7" - peerDependenciesMeta: - "@swc/core": - optional: true - "@swc/wasm": - optional: true - bin: - ts-node: dist/bin.js - ts-node-cwd: dist/bin-cwd.js - ts-node-esm: dist/bin-esm.js - ts-node-script: dist/bin-script.js - ts-node-transpile-only: dist/bin-transpile.js - ts-script: dist/bin-script-deprecated.js - checksum: 95187932fb83f3901e22546bd2feeac7d2feb4f412f42ac3a595f049a23e8dcf70516dffb51866391228ea2dbcfaea039e250fb2bb334d48a86ab2b6aea0ae2d - languageName: node - linkType: hard - "tsconfig-paths@npm:^3.14.2": version: 3.14.2 resolution: "tsconfig-paths@npm:3.14.2" @@ -16521,23 +16466,23 @@ __metadata: languageName: node linkType: hard -"typescript@npm:5.1.6": - version: 5.1.6 - resolution: "typescript@npm:5.1.6" +"typescript@npm:5.2.2": + version: 5.2.2 + resolution: "typescript@npm:5.2.2" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 45ac28e2df8365fd28dac42f5d62edfe69a7203d5ec646732cadc04065331f34f9078f81f150fde42ed9754eed6fa3b06a8f3523c40b821e557b727f1992e025 + checksum: 91ae3e6193d0ddb8656d4c418a033f0f75dec5e077ebbc2bd6d76439b93f35683936ee1bdc0e9cf94ec76863aa49f27159b5788219b50e1cd0cd6d110aa34b07 languageName: node linkType: hard -"typescript@patch:typescript@npm%3A5.1.6#optional!builtin": - version: 5.1.6 - resolution: "typescript@patch:typescript@npm%3A5.1.6#optional!builtin::version=5.1.6&hash=5da071" +"typescript@patch:typescript@npm%3A5.2.2#optional!builtin": + version: 5.2.2 + resolution: "typescript@patch:typescript@npm%3A5.2.2#optional!builtin::version=5.2.2&hash=f3b441" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: c2bded58ab897a8341fdbb0c1d92ea2362f498cfffebdc8a529d03e15ea2454142dfbf122dabbd9a5cb79b7123790d27def16e11844887d20636226773ed329a + checksum: 062c1cee1990e6b9419ce8a55162b8dc917eb87f807e4de0327dbc1c2fa4e5f61bc0dd4e034d38ff541d1ed0479b53bcee8e4de3a4075c51a1724eb6216cb6f5 languageName: node linkType: hard @@ -17128,13 +17073,6 @@ __metadata: languageName: node linkType: hard -"v8-compile-cache-lib@npm:^3.0.1": - version: 3.0.1 - resolution: "v8-compile-cache-lib@npm:3.0.1" - checksum: bdc36fb8095d3b41df197f5fb6f11e3a26adf4059df3213e3baa93810d8f0cc76f9a74aaefc18b73e91fe7e19154ed6f134eda6fded2e0f1c8d2272ed2d2d391 - languageName: node - linkType: hard - "v8-compile-cache@npm:2.3.0": version: 2.3.0 resolution: "v8-compile-cache@npm:2.3.0" @@ -17893,13 +17831,6 @@ __metadata: languageName: node linkType: hard -"yn@npm:3.1.1": - version: 3.1.1 - resolution: "yn@npm:3.1.1" - checksum: 0732468dd7622ed8a274f640f191f3eaf1f39d5349a1b72836df484998d7d9807fbea094e2f5486d6b0cd2414aad5775972df0e68f8604db89a239f0f4bf7443 - languageName: node - linkType: hard - "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0"