diff --git a/MIGRATION.md b/MIGRATION.md index 34c0ca87c685..8b431bd37e90 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,10 +1,12 @@

Migration

-- [From version 8.0 to 8.1.0](#from-version-80-to-810) +- [From version 8.0.x to 8.1.x](#from-version-80x-to-81x) - [Subtitle block and `parameters.componentSubtitle`](#subtitle-block-and-parameterscomponentsubtitle) - [Title block](#title-block) -- [From version 7.x to 8.0.0](#from-version-7x-to-800) - [Portable stories](#portable-stories) + - [@storybook/nextjs requires specific path aliases to be setup](#storybooknextjs-requires-specific-path-aliases-to-be-setup) +- [From version 7.x to 8.0.0](#from-version-7x-to-800) + - [Portable stories](#portable-stories-1) - [Project annotations are now merged instead of overwritten in composeStory](#project-annotations-are-now-merged-instead-of-overwritten-in-composestory) - [Type change in `composeStories` API](#type-change-in-composestories-api) - [Composed Vue stories are now components instead of functions](#composed-vue-stories-are-now-components-instead-of-functions) @@ -406,7 +408,27 @@ - [Packages renaming](#packages-renaming) - [Deprecated embedded addons](#deprecated-embedded-addons) -## From version 8.0 to 8.1.0 +## From version 8.0.x to 8.1.x + +### Portable stories + +#### @storybook/nextjs requires specific path aliases to be setup + +In order to properly mock the `next/router`, `next/header`, `next/navigation` and `next/cache` APIs, the `@storybook/nextjs` framework includes internal Webpack aliases to those modules. If you use portable stories in your Jest tests, you should set the aliases in your Jest config files `moduleNameMapper` property using the `getPackageAliases` helper from `@storybook/nextjs/export-mocks`: + +```js +const nextJest = require("next/jest.js"); +const { getPackageAliases } = require("@storybook/nextjs/export-mocks"); +const createJestConfig = nextJest(); +const customJestConfig = { + moduleNameMapper: { + ...getPackageAliases(), // Add aliases for @storybook/nextjs mocks + }, +}; +module.exports = createJestConfig(customJestConfig); +``` + +This will make sure you end using the correct implementation of the packages and avoid having issues in your tests. ### Subtitle block and `parameters.componentSubtitle` diff --git a/code/addons/actions/src/components/ActionLogger/index.tsx b/code/addons/actions/src/components/ActionLogger/index.tsx index d93496f604fc..8a5d84d41c0a 100644 --- a/code/addons/actions/src/components/ActionLogger/index.tsx +++ b/code/addons/actions/src/components/ActionLogger/index.tsx @@ -1,22 +1,23 @@ -import type { FC, PropsWithChildren } from 'react'; -import React, { Fragment } from 'react'; -import { styled, withTheme } from '@storybook/theming'; +import type { ElementRef, ReactNode } from 'react'; +import React, { forwardRef, Fragment, useEffect, useRef } from 'react'; import type { Theme } from '@storybook/theming'; +import { styled, withTheme } from '@storybook/theming'; import { Inspector } from 'react-inspector'; import { ActionBar, ScrollArea } from '@storybook/components'; -import { Action, InspectorContainer, Counter } from './style'; +import { Action, Counter, InspectorContainer } from './style'; import type { ActionDisplay } from '../../models'; -const UnstyledWrapped: FC> = ({ - children, - className, -}) => ( - - {children} - +const UnstyledWrapped = forwardRef( + ({ children, className }, ref) => ( + + {children} + + ) ); +UnstyledWrapped.displayName = 'UnstyledWrapped'; + export const Wrapper = styled(UnstyledWrapped)({ margin: 0, padding: '10px 5px 20px', @@ -39,24 +40,34 @@ interface ActionLoggerProps { onClear: () => void; } -export const ActionLogger = ({ actions, onClear }: ActionLoggerProps) => ( - - - {actions.map((action: ActionDisplay) => ( - - {action.count > 1 && {action.count}} - - - - - ))} - - - - -); +export const ActionLogger = ({ actions, onClear }: ActionLoggerProps) => { + const wrapperRef = useRef>(null); + const wrapper = wrapperRef.current; + const wasAtBottom = wrapper && wrapper.scrollHeight - wrapper.scrollTop === wrapper.clientHeight; + + useEffect(() => { + // Scroll to bottom, when the action panel was already scrolled down + if (wasAtBottom) wrapperRef.current.scrollTop = wrapperRef.current.scrollHeight; + }, [wasAtBottom, actions.length]); + + return ( + + + {actions.map((action: ActionDisplay) => ( + + {action.count > 1 && {action.count}} + + + + + ))} + + + + ); +}; diff --git a/code/addons/actions/src/containers/ActionLogger/index.tsx b/code/addons/actions/src/containers/ActionLogger/index.tsx index bb88fd7f5b2a..6f5d5f3a1366 100644 --- a/code/addons/actions/src/containers/ActionLogger/index.tsx +++ b/code/addons/actions/src/containers/ActionLogger/index.tsx @@ -63,12 +63,12 @@ export default class ActionLogger extends Component { this.setState((prevState: ActionLoggerState) => { const actions = [...prevState.actions]; - const previous = actions.length && actions[0]; + const previous = actions.length && actions[actions.length - 1]; if (previous && safeDeepEqual(previous.data, action.data)) { previous.count++; } else { action.count = 1; - actions.unshift(action); + actions.push(action); } return { actions: actions.slice(0, action.options.limit) }; }); diff --git a/code/addons/actions/src/loaders.ts b/code/addons/actions/src/loaders.ts index d49a048da231..eebb09acb71b 100644 --- a/code/addons/actions/src/loaders.ts +++ b/code/addons/actions/src/loaders.ts @@ -18,7 +18,26 @@ const logActionsWhenMockCalled: LoaderFunction = (context) => { typeof global.__STORYBOOK_TEST_ON_MOCK_CALL__ === 'function' ) { const onMockCall = global.__STORYBOOK_TEST_ON_MOCK_CALL__ as typeof onMockCallType; - onMockCall((mock, args) => action(mock.getMockName())(args)); + onMockCall((mock, args) => { + const name = mock.getMockName(); + + // TODO: Make this a configurable API in 8.2 + if ( + !/^next\/.*::/.test(name) || + [ + 'next/router::useRouter()', + 'next/navigation::useRouter()', + 'next/navigation::redirect', + 'next/cache::', + 'next/headers::cookies().set', + 'next/headers::cookies().delete', + 'next/headers::headers().set', + 'next/headers::headers().delete', + ].some((prefix) => name.startsWith(prefix)) + ) { + action(name)(args); + } + }); subscribed = true; } }; diff --git a/code/addons/actions/template/stories/spies.stories.ts b/code/addons/actions/template/stories/spies.stories.ts index 824494bda1c9..9d84cae3d361 100644 --- a/code/addons/actions/template/stories/spies.stories.ts +++ b/code/addons/actions/template/stories/spies.stories.ts @@ -1,24 +1,27 @@ import { global as globalThis } from '@storybook/global'; import { spyOn } from '@storybook/test'; -export default { +const meta = { component: globalThis.Components.Button, - loaders() { + beforeEach() { spyOn(console, 'log').mockName('console.log'); - }, - args: { - label: 'Button', - }, - parameters: { - chromatic: { disable: true }, + console.log('first'); }, }; +export default meta; + export const ShowSpyOnInActions = { + parameters: { + chromatic: { disable: true }, + }, + beforeEach() { + console.log('second'); + }, args: { + label: 'Button', onClick: () => { - console.log('first'); - console.log('second'); + console.log('third'); }, }, }; diff --git a/code/addons/links/package.json b/code/addons/links/package.json index fca532085ff4..c19d89b78b0a 100644 --- a/code/addons/links/package.json +++ b/code/addons/links/package.json @@ -67,7 +67,7 @@ "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.5", + "@storybook/csf": "^0.1.6", "@storybook/global": "^5.0.0", "ts-dedent": "^2.0.0" }, diff --git a/code/builders/builder-vite/src/vite-config.ts b/code/builders/builder-vite/src/vite-config.ts index 4a1c2fb3ee44..95d68543a22e 100644 --- a/code/builders/builder-vite/src/vite-config.ts +++ b/code/builders/builder-vite/src/vite-config.ts @@ -65,6 +65,7 @@ export async function commonConfig( base: './', plugins: await pluginConfig(options), resolve: { + conditions: ['storybook', 'stories', 'test'], preserveSymlinks: isPreservingSymlinks(), alias: { assert: require.resolve('browser-assert'), diff --git a/code/builders/builder-webpack5/src/preview/base-webpack.config.ts b/code/builders/builder-webpack5/src/preview/base-webpack.config.ts index 8a2d60743661..38961e89085d 100644 --- a/code/builders/builder-webpack5/src/preview/base-webpack.config.ts +++ b/code/builders/builder-webpack5/src/preview/base-webpack.config.ts @@ -85,6 +85,14 @@ export async function createDefaultWebpackConfig( }, resolve: { ...storybookBaseConfig.resolve, + // see https://github.com/webpack/webpack/issues/17692#issuecomment-1866272674 for the docs + conditionNames: [ + ...(storybookBaseConfig.resolve?.conditionNames ?? []), + 'storybook', + 'stories', + 'test', + '...', + ], fallback: { crypto: false, assert: false, diff --git a/code/e2e-tests/framework-nextjs.spec.ts b/code/e2e-tests/framework-nextjs.spec.ts index c1a15c2632ef..0b98e5428853 100644 --- a/code/e2e-tests/framework-nextjs.spec.ts +++ b/code/e2e-tests/framework-nextjs.spec.ts @@ -56,7 +56,7 @@ test.describe('Next.js', () => { await sbPage.viewAddonPanel('Actions'); const logItem = await page.locator('#storybook-panel-root #panel-tab-content', { - hasText: `nextNavigation.${action}`, + hasText: `useRouter().${action}`, }); await expect(logItem).toBeVisible(); }); @@ -91,7 +91,7 @@ test.describe('Next.js', () => { await sbPage.viewAddonPanel('Actions'); const logItem = await page.locator('#storybook-panel-root #panel-tab-content', { - hasText: `nextRouter.${action}`, + hasText: `useRouter().${action}`, }); await expect(logItem).toBeVisible(); }); diff --git a/code/e2e-tests/preview-web.spec.ts b/code/e2e-tests/preview-api.spec.ts similarity index 57% rename from code/e2e-tests/preview-web.spec.ts rename to code/e2e-tests/preview-api.spec.ts index b4a96c4b6001..662a638bc84c 100644 --- a/code/e2e-tests/preview-web.spec.ts +++ b/code/e2e-tests/preview-api.spec.ts @@ -5,7 +5,9 @@ import { SbPage } from './util'; const storybookUrl = process.env.STORYBOOK_URL || 'http://localhost:8001'; const templateName = process.env.STORYBOOK_TEMPLATE_NAME || ''; -test.describe('preview-web', () => { +const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + +test.describe('preview-api', () => { test.beforeEach(async ({ page }) => { await page.goto(storybookUrl); @@ -50,4 +52,40 @@ test.describe('preview-web', () => { await sbPage.previewRoot().getByRole('button').getByText('Submit').first().press('Alt+s'); await expect(sbPage.page.locator('.sidebar-container')).not.toBeVisible(); }); + + // if rerenders were interleaved the button would have rendered "Error: Interleaved loaders. Changed arg" + test('should only render once at a time when rapidly changing args', async ({ page }) => { + const sbPage = new SbPage(page); + await sbPage.navigateToStory('lib/preview-api/rendering', 'slow-loader'); + + const root = sbPage.previewRoot(); + + const labelControl = await sbPage.page.locator('#control-label'); + + await expect(root.getByText('Loaded. Click me')).toBeVisible(); + await expect(labelControl).toBeVisible(); + + await labelControl.fill(''); + await labelControl.type('Changed arg', { delay: 50 }); + await labelControl.blur(); + + await expect(root.getByText('Loaded. Changed arg')).toBeVisible(); + }); + + test('should reload plage when remounting while loading', async ({ page }) => { + const sbPage = new SbPage(page); + await sbPage.navigateToStory('lib/preview-api/rendering', 'slow-loader'); + + const root = sbPage.previewRoot(); + + await expect(root.getByText('Loaded. Click me')).toBeVisible(); + + await sbPage.page.getByRole('button', { name: 'Remount component' }).click(); + await wait(200); + await sbPage.page.getByRole('button', { name: 'Remount component' }).click(); + + // the loading spinner indicates the iframe is being fully reloaded + await expect(sbPage.previewIframe().locator('.sb-preparing-story > .sb-loader')).toBeVisible(); + await expect(root.getByText('Loaded. Click me')).toBeVisible(); + }); }); diff --git a/code/frameworks/nextjs/package.json b/code/frameworks/nextjs/package.json index e2a136996b8c..5d4153a44fb9 100644 --- a/code/frameworks/nextjs/package.json +++ b/code/frameworks/nextjs/package.json @@ -47,6 +47,31 @@ "require": "./dist/next-image-loader-stub.js", "import": "./dist/next-image-loader-stub.mjs" }, + "./export-mocks": { + "types": "./dist/export-mocks/index.d.ts", + "require": "./dist/export-mocks/index.js", + "import": "./dist/export-mocks/index.mjs" + }, + "./cache.mock": { + "types": "./dist/export-mocks/cache/index.d.ts", + "require": "./dist/export-mocks/cache/index.js", + "import": "./dist/export-mocks/cache/index.mjs" + }, + "./headers.mock": { + "types": "./dist/export-mocks/headers/index.d.ts", + "require": "./dist/export-mocks/headers/index.js", + "import": "./dist/export-mocks/headers/index.mjs" + }, + "./navigation.mock": { + "types": "./dist/export-mocks/navigation/index.d.ts", + "require": "./dist/export-mocks/navigation/index.js", + "import": "./dist/export-mocks/navigation/index.mjs" + }, + "./router.mock": { + "types": "./dist/export-mocks/router/index.d.ts", + "require": "./dist/export-mocks/router/index.js", + "import": "./dist/export-mocks/router/index.mjs" + }, "./package.json": "./package.json" }, "main": "dist/index.js", @@ -59,6 +84,21 @@ ], "dist/image-context": [ "dist/image-context.d.ts" + ], + "export-mocks": [ + "dist/export-mocks/index.d.ts" + ], + "cache.mock": [ + "dist/export-mocks/cache/index.d.ts" + ], + "headers.mock": [ + "dist/export-mocks/headers/index.d.ts" + ], + "router.mock": [ + "dist/export-mocks/router/index.d.ts" + ], + "navigation.mock": [ + "dist/export-mocks/navigation/index.d.ts" ] } }, @@ -89,7 +129,6 @@ "@babel/preset-typescript": "^7.24.1", "@babel/runtime": "^7.24.4", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", - "@storybook/addon-actions": "workspace:*", "@storybook/builder-webpack5": "workspace:*", "@storybook/core-common": "workspace:*", "@storybook/core-events": "workspace:*", @@ -97,6 +136,7 @@ "@storybook/preset-react-webpack": "workspace:*", "@storybook/preview-api": "workspace:*", "@storybook/react": "workspace:*", + "@storybook/test": "workspace:*", "@storybook/types": "workspace:*", "@types/node": "^18.0.0", "@types/semver": "^7.3.4", @@ -160,6 +200,11 @@ "./src/index.ts", "./src/preset.ts", "./src/preview.tsx", + "./src/export-mocks/index.ts", + "./src/export-mocks/cache/index.ts", + "./src/export-mocks/headers/index.ts", + "./src/export-mocks/router/index.ts", + "./src/export-mocks/navigation/index.ts", "./src/next-image-loader-stub.ts", "./src/images/decorator.tsx", "./src/images/next-legacy-image.tsx", diff --git a/code/frameworks/nextjs/src/export-mocks/cache/index.ts b/code/frameworks/nextjs/src/export-mocks/cache/index.ts new file mode 100644 index 000000000000..b9f2fa8dd431 --- /dev/null +++ b/code/frameworks/nextjs/src/export-mocks/cache/index.ts @@ -0,0 +1,17 @@ +import { fn } from '@storybook/test'; +import { unstable_cache } from 'next/dist/server/web/spec-extension/unstable-cache'; +import { unstable_noStore } from 'next/dist/server/web/spec-extension/unstable-no-store'; + +// mock utilities/overrides (as of Next v14.2.0) +const revalidatePath = fn().mockName('next/cache::revalidatePath'); +const revalidateTag = fn().mockName('next/cache::revalidateTag'); + +const cacheExports = { + unstable_cache, + revalidateTag, + revalidatePath, + unstable_noStore, +}; + +export default cacheExports; +export { unstable_cache, revalidateTag, revalidatePath, unstable_noStore }; diff --git a/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts b/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts new file mode 100644 index 000000000000..3d84ecba3885 --- /dev/null +++ b/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts @@ -0,0 +1,38 @@ +import { fn } from '@storybook/test'; +import { RequestCookies } from 'next/dist/compiled/@edge-runtime/cookies'; +// We need this import to be a singleton, and because it's used in multiple entrypoints +// both in ESM and CJS, importing it via the package name instead of having a local import +// is the only way to achieve it actually being a singleton +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore we must ignore types here as during compilation they are not generated yet +import { headers } from '@storybook/nextjs/headers.mock'; + +class RequestCookiesMock extends RequestCookies { + get = fn(super.get.bind(this)).mockName('next/headers::cookies().get'); + + getAll = fn(super.getAll.bind(this)).mockName('next/headers::cookies().getAll'); + + has = fn(super.has.bind(this)).mockName('next/headers::cookies().has'); + + set = fn(super.set.bind(this)).mockName('next/headers::cookies().set'); + + delete = fn(super.delete.bind(this)).mockName('next/headers::cookies().delete'); +} + +let requestCookiesMock: RequestCookiesMock; + +export const cookies = fn(() => { + if (!requestCookiesMock) { + requestCookiesMock = new RequestCookiesMock(headers()); + } + return requestCookiesMock; +}).mockName('next/headers::cookies()'); + +const originalRestore = cookies.mockRestore.bind(null); + +// will be called automatically by the test loader +cookies.mockRestore = () => { + originalRestore(); + headers.mockRestore(); + requestCookiesMock = new RequestCookiesMock(headers()); +}; diff --git a/code/frameworks/nextjs/src/export-mocks/headers/headers.ts b/code/frameworks/nextjs/src/export-mocks/headers/headers.ts new file mode 100644 index 000000000000..d9eb5177b447 --- /dev/null +++ b/code/frameworks/nextjs/src/export-mocks/headers/headers.ts @@ -0,0 +1,39 @@ +import { fn } from '@storybook/test'; + +import { HeadersAdapter } from 'next/dist/server/web/spec-extension/adapters/headers'; + +class HeadersAdapterMock extends HeadersAdapter { + constructor() { + super({}); + } + + append = fn(super.append.bind(this)).mockName('next/headers::headers().append'); + + delete = fn(super.delete.bind(this)).mockName('next/headers::headers().delete'); + + get = fn(super.get.bind(this)).mockName('next/headers::headers().get'); + + has = fn(super.has.bind(this)).mockName('next/headers::headers().has'); + + set = fn(super.set.bind(this)).mockName('next/headers::headers().set'); + + forEach = fn(super.forEach.bind(this)).mockName('next/headers::headers().forEach'); + + entries = fn(super.entries.bind(this)).mockName('next/headers::headers().entries'); + + keys = fn(super.keys.bind(this)).mockName('next/headers::headers().keys'); + + values = fn(super.values.bind(this)).mockName('next/headers::headers().values'); +} + +let headersAdapterMock: HeadersAdapterMock; + +export const headers = () => { + if (!headersAdapterMock) headersAdapterMock = new HeadersAdapterMock(); + return headersAdapterMock; +}; + +// This fn is called by ./cookies to restore the headers in the right order +headers.mockRestore = () => { + headersAdapterMock = new HeadersAdapterMock(); +}; diff --git a/code/frameworks/nextjs/src/export-mocks/headers/index.ts b/code/frameworks/nextjs/src/export-mocks/headers/index.ts new file mode 100644 index 000000000000..1ec2cbd2915f --- /dev/null +++ b/code/frameworks/nextjs/src/export-mocks/headers/index.ts @@ -0,0 +1,13 @@ +import { fn } from '@storybook/test'; +import * as originalHeaders from 'next/dist/client/components/headers'; + +// re-exports of the actual module +export * from 'next/dist/client/components/headers'; + +// mock utilities/overrides (as of Next v14.2.0) +export { headers } from './headers'; +export { cookies } from './cookies'; + +// passthrough mocks - keep original implementation but allow for spying +const draftMode = fn(originalHeaders.draftMode).mockName('draftMode'); +export { draftMode }; diff --git a/code/frameworks/nextjs/src/export-mocks/index.ts b/code/frameworks/nextjs/src/export-mocks/index.ts new file mode 100644 index 000000000000..111ad162ec3f --- /dev/null +++ b/code/frameworks/nextjs/src/export-mocks/index.ts @@ -0,0 +1 @@ +export { getPackageAliases } from './webpack'; diff --git a/code/frameworks/nextjs/src/export-mocks/navigation/index.ts b/code/frameworks/nextjs/src/export-mocks/navigation/index.ts new file mode 100644 index 000000000000..dd9e9a692e6f --- /dev/null +++ b/code/frameworks/nextjs/src/export-mocks/navigation/index.ts @@ -0,0 +1,89 @@ +import type { Mock } from '@storybook/test'; +import { fn } from '@storybook/test'; +import { NextjsRouterMocksNotAvailable } from '@storybook/core-events/preview-errors'; +import * as originalNavigation from 'next/dist/client/components/navigation'; + +let navigationAPI: { + push: Mock; + replace: Mock; + forward: Mock; + back: Mock; + prefetch: Mock; + refresh: Mock; +}; + +/** + * Creates a next/navigation router API mock. Used internally. + * @ignore + * @internal + * */ +export const createNavigation = (overrides: any) => { + const navigationActions = { + push: fn().mockName('next/navigation::useRouter().push'), + replace: fn().mockName('next/navigation::useRouter().replace'), + forward: fn().mockName('next/navigation::useRouter().forward'), + back: fn().mockName('next/navigation::useRouter().back'), + prefetch: fn().mockName('next/navigation::useRouter().prefetch'), + refresh: fn().mockName('next/navigation::useRouter().refresh'), + }; + + if (overrides) { + Object.keys(navigationActions).forEach((key) => { + if (key in overrides) { + (navigationActions as any)[key] = fn((...args: any[]) => { + return (overrides as any)[key](...args); + }).mockName(`useRouter().${key}`); + } + }); + } + + navigationAPI = navigationActions; + + return navigationAPI; +}; + +export const getRouter = () => { + if (!navigationAPI) { + throw new NextjsRouterMocksNotAvailable({ + importType: 'next/navigation', + }); + } + + return navigationAPI; +}; + +// re-exports of the actual module +export * from 'next/dist/client/components/navigation'; + +// mock utilities/overrides (as of Next v14.2.0) +export const redirect = fn().mockName('next/navigation::redirect'); + +// passthrough mocks - keep original implementation but allow for spying +export const useSearchParams = fn(originalNavigation.useSearchParams).mockName( + 'next/navigation::useSearchParams' +); +export const usePathname = fn(originalNavigation.usePathname).mockName( + 'next/navigation::usePathname' +); +export const useSelectedLayoutSegment = fn(originalNavigation.useSelectedLayoutSegment).mockName( + 'next/navigation::useSelectedLayoutSegment' +); +export const useSelectedLayoutSegments = fn(originalNavigation.useSelectedLayoutSegments).mockName( + 'next/navigation::useSelectedLayoutSegments' +); +export const useRouter = fn(originalNavigation.useRouter).mockName('next/navigation::useRouter'); +export const useServerInsertedHTML = fn(originalNavigation.useServerInsertedHTML).mockName( + 'next/navigation::useServerInsertedHTML' +); +export const notFound = fn(originalNavigation.notFound).mockName('next/navigation::notFound'); +export const permanentRedirect = fn(originalNavigation.permanentRedirect).mockName( + 'next/navigation::permanentRedirect' +); + +// Params, not exported by Next.js, is manually declared to avoid inference issues. +interface Params { + [key: string]: string | string[]; +} +export const useParams = fn<[], Params>(originalNavigation.useParams).mockName( + 'next/navigation::useParams' +); diff --git a/code/frameworks/nextjs/src/export-mocks/router/index.ts b/code/frameworks/nextjs/src/export-mocks/router/index.ts new file mode 100644 index 000000000000..b3c5e37faa43 --- /dev/null +++ b/code/frameworks/nextjs/src/export-mocks/router/index.ts @@ -0,0 +1,115 @@ +import type { Mock } from '@storybook/test'; +import { fn } from '@storybook/test'; +import { NextjsRouterMocksNotAvailable } from '@storybook/core-events/preview-errors'; +import type { NextRouter, SingletonRouter } from 'next/router'; +import singletonRouter, * as originalRouter from 'next/dist/client/router'; + +const defaultRouterState = { + route: '/', + asPath: '/', + basePath: '/', + pathname: '/', + query: {}, + isFallback: false, + isLocaleDomain: false, + isReady: true, + isPreview: false, +}; + +let routerAPI: { + push: Mock; + replace: Mock; + reload: Mock; + back: Mock; + forward: Mock; + prefetch: Mock; + beforePopState: Mock; + events: { + on: Mock; + off: Mock; + emit: Mock; + }; +} & typeof defaultRouterState; + +/** + * Creates a next/router router API mock. Used internally. + * @ignore + * @internal + * */ +export const createRouter = (overrides: Partial) => { + const routerActions: Partial = { + push: fn((..._args: any[]) => { + return Promise.resolve(true); + }).mockName('next/router::useRouter().push'), + replace: fn((..._args: any[]) => { + return Promise.resolve(true); + }).mockName('next/router::useRouter().replace'), + reload: fn((..._args: any[]) => {}).mockName('next/router::useRouter().reload'), + back: fn((..._args: any[]) => {}).mockName('next/router::useRouter().back'), + forward: fn(() => {}).mockName('next/router::useRouter().forward'), + prefetch: fn((..._args: any[]) => { + return Promise.resolve(); + }).mockName('next/router::useRouter().prefetch'), + beforePopState: fn((..._args: any[]) => {}).mockName('next/router::useRouter().beforePopState'), + }; + + const routerEvents: NextRouter['events'] = { + on: fn((..._args: any[]) => {}).mockName('next/router::useRouter().events.on'), + off: fn((..._args: any[]) => {}).mockName('next/router::useRouter().events.off'), + emit: fn((..._args: any[]) => {}).mockName('next/router::useRouter().events.emit'), + }; + + if (overrides) { + Object.keys(routerActions).forEach((key) => { + if (key in overrides) { + (routerActions as any)[key] = fn((...args: any[]) => { + return (overrides as any)[key](...args); + }).mockName(`useRouter().${key}`); + } + }); + } + + if (overrides?.events) { + Object.keys(routerEvents).forEach((key) => { + if (key in routerEvents) { + (routerEvents as any)[key] = fn((...args: any[]) => { + return (overrides.events as any)[key](...args); + }).mockName(`useRouter().events.${key}`); + } + }); + } + + routerAPI = { + ...defaultRouterState, + ...overrides, + ...routerActions, + // @ts-expect-error TODO improve typings + events: routerEvents, + }; + + // overwrite the singleton router from next/router + (singletonRouter as unknown as SingletonRouter).router = routerAPI as any; + (singletonRouter as unknown as SingletonRouter).readyCallbacks.forEach((cb) => cb()); + (singletonRouter as unknown as SingletonRouter).readyCallbacks = []; + + return routerAPI as unknown as NextRouter; +}; + +export const getRouter = () => { + if (!routerAPI) { + throw new NextjsRouterMocksNotAvailable({ + importType: 'next/router', + }); + } + + return routerAPI; +}; + +// re-exports of the actual module +export * from 'next/dist/client/router'; +export default singletonRouter; + +// mock utilities/overrides (as of Next v14.2.0) +// passthrough mocks - keep original implementation but allow for spying +export const useRouter = fn(originalRouter.useRouter).mockName('next/router::useRouter'); +export const withRouter = fn(originalRouter.withRouter).mockName('next/router::withRouter'); diff --git a/code/frameworks/nextjs/src/export-mocks/webpack.ts b/code/frameworks/nextjs/src/export-mocks/webpack.ts new file mode 100644 index 000000000000..789dc1df21cd --- /dev/null +++ b/code/frameworks/nextjs/src/export-mocks/webpack.ts @@ -0,0 +1,36 @@ +import { dirname, join } from 'path'; +import type { Configuration as WebpackConfig } from 'webpack'; + +export const getPackageAliases = ({ useESM = false }: { useESM?: boolean } = {}) => { + const extension = useESM ? 'mjs' : 'js'; + const packageLocation = dirname(require.resolve('@storybook/nextjs/package.json')); + // Use paths for both next/xyz and @storybook/nextjs/xyz imports + // to make sure they all serve the MJS version of the file + const headersPath = join(packageLocation, `/dist/export-mocks/headers/index.${extension}`); + const navigationPath = join(packageLocation, `/dist/export-mocks/navigation/index.${extension}`); + const cachePath = join(packageLocation, `/dist/export-mocks/cache/index.${extension}`); + const routerPath = join(packageLocation, `/dist/export-mocks/router/index.${extension}`); + + return { + 'next/headers': headersPath, + '@storybook/nextjs/headers.mock': headersPath, + + 'next/navigation': navigationPath, + '@storybook/nextjs/navigation.mock': navigationPath, + + 'next/router': routerPath, + '@storybook/nextjs/router.mock': routerPath, + + 'next/cache': cachePath, + '@storybook/nextjs/cache.mock': cachePath, + }; +}; + +export const configureNextExportMocks = (baseConfig: WebpackConfig): void => { + const resolve = baseConfig.resolve ?? {}; + + resolve.alias = { + ...resolve.alias, + ...getPackageAliases({ useESM: true }), + }; +}; diff --git a/code/frameworks/nextjs/src/preset.ts b/code/frameworks/nextjs/src/preset.ts index e76db8104d1b..0eb9abed9b04 100644 --- a/code/frameworks/nextjs/src/preset.ts +++ b/code/frameworks/nextjs/src/preset.ts @@ -23,6 +23,7 @@ import { configureBabelLoader } from './babel/loader'; import { configureFastRefresh } from './fastRefresh/webpack'; import { configureAliases } from './aliases/webpack'; import { logger } from '@storybook/node-logger'; +import { configureNextExportMocks } from './export-mocks/webpack'; export const addons: PresetProperty<'addons'> = [ dirname(require.resolve(join('@storybook/preset-react-webpack', 'package.json'))), @@ -134,6 +135,7 @@ export const webpackFinal: StorybookConfig['webpackFinal'] = async (baseConfig, configureStyledJsx(baseConfig); configureNodePolyfills(baseConfig); configureAliases(baseConfig); + configureNextExportMocks(baseConfig); if (isDevelopment) { configureFastRefresh(baseConfig); diff --git a/code/frameworks/nextjs/src/preview.tsx b/code/frameworks/nextjs/src/preview.tsx index 9f30af9d787d..8f141e7b1ef0 100644 --- a/code/frameworks/nextjs/src/preview.tsx +++ b/code/frameworks/nextjs/src/preview.tsx @@ -1,10 +1,23 @@ -import type { Addon_DecoratorFunction } from '@storybook/types'; +import type { Addon_DecoratorFunction, Addon_LoaderFunction } from '@storybook/types'; import './config/preview'; import { ImageDecorator } from './images/decorator'; import { RouterDecorator } from './routing/decorator'; import { StyledJsxDecorator } from './styledJsx/decorator'; import { HeadManagerDecorator } from './head-manager/decorator'; +// We need this import to be a singleton, and because it's used in multiple entrypoints +// both in ESM and CJS, importing it via the package name instead of having a local import +// is the only way to achieve it actually being a singleton +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore we must ignore types here as during compilation they are not generated yet +import { cookies, headers } from '@storybook/nextjs/headers.mock'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore we must ignore types here as during compilation they are not generated yet +import { createRouter } from '@storybook/nextjs/router.mock'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore we must ignore types here as during compilation they are not generated yet +import { createNavigation } from '@storybook/nextjs/navigation.mock'; + function addNextHeadCount() { const meta = document.createElement('meta'); meta.name = 'next-head-count'; @@ -21,6 +34,18 @@ export const decorators: Addon_DecoratorFunction[] = [ HeadManagerDecorator, ]; +export const loaders: Addon_LoaderFunction = async ({ globals, parameters }) => { + const { router, appDirectory } = parameters.nextjs ?? {}; + if (appDirectory) { + createNavigation(router); + } else { + createRouter({ + locale: globals.locale, + ...router, + }); + } +}; + export const parameters = { docs: { source: { diff --git a/code/frameworks/nextjs/src/routing/app-router-provider.tsx b/code/frameworks/nextjs/src/routing/app-router-provider.tsx index f81c29a5fe59..27041c896f8e 100644 --- a/code/frameworks/nextjs/src/routing/app-router-provider.tsx +++ b/code/frameworks/nextjs/src/routing/app-router-provider.tsx @@ -13,9 +13,14 @@ import { type Params } from 'next/dist/shared/lib/router/utils/route-matcher'; import { PAGE_SEGMENT_KEY } from 'next/dist/shared/lib/segment'; import type { FlightRouterState } from 'next/dist/server/app-render/types'; import type { RouteParams } from './types'; +// We need this import to be a singleton, and because it's used in multiple entrypoints +// both in ESM and CJS, importing it via the package name instead of having a local import +// is the only way to achieve it actually being a singleton +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore we must ignore types here as during compilation they are not generated yet +import { getRouter } from '@storybook/nextjs/navigation.mock'; type AppRouterProviderProps = { - action: (name: string) => (...args: any[]) => void; routeParams: RouteParams; }; @@ -57,10 +62,9 @@ const getParallelRoutes = (segmentsList: Array): FlightRouterState => { export const AppRouterProvider: React.FC> = ({ children, - action, routeParams, }) => { - const { pathname, query, segments = [], ...restRouteParams } = routeParams; + const { pathname, query, segments = [] } = routeParams; const tree: FlightRouterState = [pathname, { children: getParallelRoutes([...segments]) }]; const pathParams = useMemo(() => { @@ -88,29 +92,7 @@ export const AppRouterProvider: React.FC - { - action('nextNavigation.refresh')(); - }, - ...restRouteParams, - }} - > + { const nextAppDirectory = (parameters.nextjs?.appDirectory as NextAppDirectory | undefined) ?? false; @@ -23,7 +22,6 @@ export const RouterDecorator = ( } return ( + ); diff --git a/code/frameworks/nextjs/src/routing/page-router-provider.tsx b/code/frameworks/nextjs/src/routing/page-router-provider.tsx index 066e7f32b3fd..e536a16db547 100644 --- a/code/frameworks/nextjs/src/routing/page-router-provider.tsx +++ b/code/frameworks/nextjs/src/routing/page-router-provider.tsx @@ -1,69 +1,13 @@ -import type { Globals } from '@storybook/csf'; import { RouterContext } from 'next/dist/shared/lib/router-context.shared-runtime'; import type { PropsWithChildren } from 'react'; import React from 'react'; -import type { RouteParams } from './types'; +// We need this import to be a singleton, and because it's used in multiple entrypoints +// both in ESM and CJS, importing it via the package name instead of having a local import +// is the only way to achieve it actually being a singleton +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore we must ignore types here as during compilation they are not generated yet +import { getRouter } from '@storybook/nextjs/router.mock'; -type PageRouterProviderProps = { - action: (name: string) => (...args: any[]) => void; - routeParams: RouteParams; - globals: Globals; -}; - -export const PageRouterProvider: React.FC> = ({ - children, - action, - routeParams, - globals, -}) => ( - - {children} - +export const PageRouterProvider: React.FC = ({ children }) => ( + {children} ); diff --git a/code/frameworks/nextjs/template/stories_nextjs-default-ts/Navigation.stories.tsx b/code/frameworks/nextjs/template/stories_nextjs-default-ts/Navigation.stories.tsx index f675ad7181ef..8a120eb80e2a 100644 --- a/code/frameworks/nextjs/template/stories_nextjs-default-ts/Navigation.stories.tsx +++ b/code/frameworks/nextjs/template/stories_nextjs-default-ts/Navigation.stories.tsx @@ -8,6 +8,8 @@ import { } from 'next/navigation'; import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; +import { expect, userEvent, within } from '@storybook/test'; +import { getRouter } from '@storybook/nextjs/navigation.mock'; function Component() { const router = useRouter(); @@ -96,12 +98,32 @@ export default { query: { foo: 'bar', }, + prefetch: () => { + console.log('custom prefetch'); + }, }, }, }, } as Meta; -export const Default: Story = {}; +export const Default: StoryObj = { + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + const routerMock = getRouter(); + + await step('Asserts whether forward hook is called', async () => { + const forwardBtn = await canvas.findByText('Go forward'); + await userEvent.click(forwardBtn); + await expect(routerMock.forward).toHaveBeenCalled(); + }); + + await step('Asserts whether custom prefetch hook is called', async () => { + const prefetchBtn = await canvas.findByText('Prefetch'); + await userEvent.click(prefetchBtn); + await expect(routerMock.prefetch).toHaveBeenCalledWith('/prefetched-html'); + }); + }, +}; export const WithSegmentDefined: Story = { parameters: { diff --git a/code/frameworks/nextjs/template/stories_nextjs-default-ts/NextHeader.stories.tsx b/code/frameworks/nextjs/template/stories_nextjs-default-ts/NextHeader.stories.tsx new file mode 100644 index 000000000000..d8abfe11bdd1 --- /dev/null +++ b/code/frameworks/nextjs/template/stories_nextjs-default-ts/NextHeader.stories.tsx @@ -0,0 +1,46 @@ +import NextHeader from './NextHeader'; +import type { Meta } from '@storybook/react'; +import type { StoryObj } from '@storybook/react'; +import { expect, userEvent, within } from '@storybook/test'; +import { cookies, headers } from '@storybook/nextjs/headers.mock'; + +export default { + component: NextHeader, +} as Meta; + +type Story = StoryObj; + +export const Default: Story = { + loaders: async () => { + cookies().set('firstName', 'Jane'); + cookies().set({ + name: 'lastName', + value: 'Doe', + }); + headers().set('timezone', 'Central European Summer Time'); + }, + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + const headersMock = headers(); + const cookiesMock = cookies(); + await step('Cookie and header store apis are called upon rendering', async () => { + await expect(cookiesMock.getAll).toHaveBeenCalled(); + await expect(headersMock.entries).toHaveBeenCalled(); + }); + + await step('Upon clicking on submit, the user-id cookie is set', async () => { + const submitButton = await canvas.findByRole('button'); + await userEvent.click(submitButton); + + await expect(cookiesMock.set).toHaveBeenCalledWith('user-id', 'encrypted-id'); + }); + + await step('The user-id cookie is available in cookie and header stores', async () => { + await expect(headersMock.get('cookie')).toContain('user-id=encrypted-id'); + await expect(cookiesMock.get('user-id')).toEqual({ + name: 'user-id', + value: 'encrypted-id', + }); + }); + }, +}; diff --git a/code/frameworks/nextjs/template/stories_nextjs-default-ts/NextHeader.tsx b/code/frameworks/nextjs/template/stories_nextjs-default-ts/NextHeader.tsx new file mode 100644 index 000000000000..b93c9611c774 --- /dev/null +++ b/code/frameworks/nextjs/template/stories_nextjs-default-ts/NextHeader.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { cookies, headers } from 'next/headers'; + +export default async function Component() { + async function handleClick() { + 'use server'; + cookies().set('user-id', 'encrypted-id'); + } + + return ( + <> +

Cookies:

+ {cookies() + .getAll() + .map(({ name, value }) => { + return ( +

+ Name: {name} + Value: {value} +

+ ); + })} + +

Headers:

+ {Array.from(headers().entries()).map(([name, value]: [string, string]) => { + return ( +

+ Name: {name} + Value: {value} +

+ ); + })} + +
+ +
+ + ); +} diff --git a/code/frameworks/nextjs/template/stories_nextjs-default-ts/Router.stories.tsx b/code/frameworks/nextjs/template/stories_nextjs-default-ts/Router.stories.tsx index 20d2180a0895..f5a840241fb4 100644 --- a/code/frameworks/nextjs/template/stories_nextjs-default-ts/Router.stories.tsx +++ b/code/frameworks/nextjs/template/stories_nextjs-default-ts/Router.stories.tsx @@ -1,5 +1,8 @@ -import { useRouter } from 'next/router'; import React from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; +import { expect, within, userEvent } from '@storybook/test'; +import { getRouter } from '@storybook/nextjs/router.mock'; +import Router, { useRouter } from 'next/router'; function Component() { const router = useRouter(); @@ -32,6 +35,7 @@ function Component() { return (
+
Router pathname: {Router.pathname}
pathname: {router.pathname}
searchparams:{' '} @@ -63,9 +67,42 @@ export default { query: { foo: 'bar', }, + prefetch: () => { + console.log('custom prefetch'); + }, }, }, }, -}; +} as Meta; + +export const Default: StoryObj = { + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + const routerMock = getRouter(); + + await step('Router property overrides should be available in useRouter fn', async () => { + await expect(Router.pathname).toBe('/hello'); + await expect(Router.query).toEqual({ foo: 'bar' }); + }); -export const Default = {}; + await step( + 'Router property overrides should be available in default export from next/router', + async () => { + await expect(Router.pathname).toBe('/hello'); + await expect(Router.query).toEqual({ foo: 'bar' }); + } + ); + + await step('Asserts whether forward hook is called', async () => { + const forwardBtn = await canvas.findByText('Go forward'); + await userEvent.click(forwardBtn); + await expect(routerMock.forward).toHaveBeenCalled(); + }); + + await step('Asserts whether custom prefetch hook is called', async () => { + const prefetchBtn = await canvas.findByText('Prefetch'); + await userEvent.click(prefetchBtn); + await expect(routerMock.prefetch).toHaveBeenCalledWith('/prefetched-html'); + }); + }, +}; diff --git a/code/frameworks/nextjs/template/stories_nextjs-default-ts/ServerActions.stories.tsx b/code/frameworks/nextjs/template/stories_nextjs-default-ts/ServerActions.stories.tsx new file mode 100644 index 000000000000..17d364429726 --- /dev/null +++ b/code/frameworks/nextjs/template/stories_nextjs-default-ts/ServerActions.stories.tsx @@ -0,0 +1,75 @@ +import React from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; +import { expect, within, userEvent } from '@storybook/test'; +import { cookies } from '@storybook/nextjs/headers.mock'; +import { revalidatePath } from '@storybook/nextjs/cache.mock'; +import { redirect } from '@storybook/nextjs/navigation.mock'; + +import { accessRoute, login, logout } from './server-actions'; + +function Component() { + return ( +
+
+ +
+
+ +
+
+ +
+
+ ); +} + +export default { + component: Component, +} as Meta; + +export const Default: StoryObj = { + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + + const loginBtn = canvas.getByText('Login'); + const logoutBtn = canvas.getByText('Logout'); + const accessRouteBtn = canvas.getByText('Access protected route'); + + await step('accessRoute flow - logged out', async () => { + await userEvent.click(accessRouteBtn); + await expect(cookies().get).toHaveBeenCalledWith('user'); + await expect(redirect).toHaveBeenCalledWith('/'); + }); + + await step('accessRoute flow - logged', async () => { + cookies.mockRestore(); + cookies().set('user', 'storybookjs'); + await userEvent.click(accessRouteBtn); + await expect(cookies().get).toHaveBeenCalledWith('user'); + await expect(revalidatePath).toHaveBeenCalledWith('/'); + await expect(redirect).toHaveBeenCalledWith('/protected'); + }); + + await step('logout flow', async () => { + cookies.mockRestore(); + await userEvent.click(logoutBtn); + await expect(cookies().delete).toHaveBeenCalled(); + await expect(revalidatePath).toHaveBeenCalledWith('/'); + await expect(redirect).toHaveBeenCalledWith('/'); + }); + + await step('login flow', async () => { + cookies.mockRestore(); + await userEvent.click(loginBtn); + await expect(cookies().set).toHaveBeenCalledWith('user', 'storybookjs'); + await expect(revalidatePath).toHaveBeenCalledWith('/'); + await expect(redirect).toHaveBeenCalledWith('/'); + }); + }, +}; diff --git a/code/frameworks/nextjs/template/stories_nextjs-default-ts/server-actions.tsx b/code/frameworks/nextjs/template/stories_nextjs-default-ts/server-actions.tsx new file mode 100644 index 000000000000..ffeba72ab848 --- /dev/null +++ b/code/frameworks/nextjs/template/stories_nextjs-default-ts/server-actions.tsx @@ -0,0 +1,28 @@ +'use server'; + +import { cookies } from 'next/headers'; +import { revalidatePath } from 'next/cache'; +import { redirect } from 'next/navigation'; + +export async function accessRoute() { + const user = cookies().get('user'); + + if (!user) { + redirect('/'); + } + + revalidatePath('/'); + redirect(`/protected`); +} + +export async function logout() { + cookies().delete('user'); + revalidatePath('/'); + redirect('/'); +} + +export async function login() { + cookies().set('user', 'storybookjs'); + revalidatePath('/'); + redirect('/'); +} diff --git a/code/lib/codemod/package.json b/code/lib/codemod/package.json index 58a7064e039e..e1d2fdff65b5 100644 --- a/code/lib/codemod/package.json +++ b/code/lib/codemod/package.json @@ -57,7 +57,7 @@ "@babel/core": "^7.24.4", "@babel/preset-env": "^7.24.4", "@babel/types": "^7.24.0", - "@storybook/csf": "^0.1.5", + "@storybook/csf": "^0.1.6", "@storybook/csf-tools": "workspace:*", "@storybook/node-logger": "workspace:*", "@storybook/types": "workspace:*", diff --git a/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts b/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts index 8ed0de57e826..db4bb886c461 100644 --- a/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts +++ b/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts @@ -36,7 +36,8 @@ const CRITICAL_YARN2_ERROR_CODES = { YN0083: 'AUTOMERGE_GIT_ERROR', }; -// @ts-expect-error The error codes might be helpful in the future +// @ts-expect-error If we want a code to be parsed, we move from the list below to the list above +// Keep the codes here, they might be helpful in the future const YARN2_ERROR_CODES = { ...CRITICAL_YARN2_ERROR_CODES, YN0000: 'UNNAMED', @@ -80,7 +81,7 @@ const YARN2_ERROR_CODES = { YN0090: 'OFFLINE_MODE_ENABLED', }; -// This encompasses both yarn 2 and yarn 3 +// This encompasses Yarn Berry (v2+) export class Yarn2Proxy extends JsPackageManager { readonly type = 'yarn2'; diff --git a/code/lib/core-events/src/errors/preview-errors.ts b/code/lib/core-events/src/errors/preview-errors.ts index 1f30378a3bca..ca3cdc656901 100644 --- a/code/lib/core-events/src/errors/preview-errors.ts +++ b/code/lib/core-events/src/errors/preview-errors.ts @@ -254,6 +254,22 @@ export class NextJsSharpError extends StorybookError { } } +export class NextjsRouterMocksNotAvailable extends StorybookError { + readonly category = Category.FRAMEWORK_NEXTJS; + + readonly code = 2; + + constructor(public data: { importType: string }) { + super(); + } + + template() { + return dedent` + Tried to access router mocks from "${this.data.importType}" but they were not created yet. You might be running code in an unsupported environment. + `; + } +} + export class UnknownArgTypesError extends StorybookError { readonly category = Category.DOCS_TOOLS; diff --git a/code/lib/core-server/package.json b/code/lib/core-server/package.json index 80c97156f22a..baaabbbfb69b 100644 --- a/code/lib/core-server/package.json +++ b/code/lib/core-server/package.json @@ -63,7 +63,7 @@ "@storybook/channels": "workspace:*", "@storybook/core-common": "workspace:*", "@storybook/core-events": "workspace:*", - "@storybook/csf": "^0.1.5", + "@storybook/csf": "^0.1.6", "@storybook/csf-tools": "workspace:*", "@storybook/docs-mdx": "3.0.0", "@storybook/global": "^5.0.0", diff --git a/code/lib/csf-tools/package.json b/code/lib/csf-tools/package.json index 1931e9847a78..219ac1c3788e 100644 --- a/code/lib/csf-tools/package.json +++ b/code/lib/csf-tools/package.json @@ -46,7 +46,7 @@ "@babel/parser": "^7.24.4", "@babel/traverse": "^7.24.1", "@babel/types": "^7.24.0", - "@storybook/csf": "^0.1.5", + "@storybook/csf": "^0.1.6", "@storybook/types": "workspace:*", "fs-extra": "^11.1.0", "recast": "^0.23.5", diff --git a/code/lib/manager-api/package.json b/code/lib/manager-api/package.json index a111b3a232f6..0a68b927b820 100644 --- a/code/lib/manager-api/package.json +++ b/code/lib/manager-api/package.json @@ -47,7 +47,7 @@ "@storybook/channels": "workspace:*", "@storybook/client-logger": "workspace:*", "@storybook/core-events": "workspace:*", - "@storybook/csf": "^0.1.5", + "@storybook/csf": "^0.1.6", "@storybook/global": "^5.0.0", "@storybook/icons": "^1.2.5", "@storybook/router": "workspace:*", diff --git a/code/lib/preview-api/package.json b/code/lib/preview-api/package.json index d5678327d9e2..36797a9265e2 100644 --- a/code/lib/preview-api/package.json +++ b/code/lib/preview-api/package.json @@ -47,7 +47,7 @@ "@storybook/channels": "workspace:*", "@storybook/client-logger": "workspace:*", "@storybook/core-events": "workspace:*", - "@storybook/csf": "^0.1.5", + "@storybook/csf": "^0.1.6", "@storybook/global": "^5.0.0", "@storybook/types": "workspace:*", "@types/qs": "^6.9.5", diff --git a/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts b/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts index 664f71ee048d..f71c3ab31eaf 100644 --- a/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts +++ b/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts @@ -911,64 +911,74 @@ describe('PreviewWeb', () => { }); describe('while story is still rendering', () => { - it('runs loaders again', async () => { + it('runs loaders again after renderToCanvas is done', async () => { + // Arrange - set up a gate to control when the loaders run const [loadersRanGate, openLoadersRanGate] = createGate(); const [blockLoadersGate, openBlockLoadersGate] = createGate(); document.location.search = '?id=component-one--a'; - componentOneExports.default.loaders[0].mockImplementationOnce(async () => { + componentOneExports.default.loaders[0].mockImplementationOnce(async (input) => { openLoadersRanGate(); return blockLoadersGate; }); + // Act - render the first time await new PreviewWeb(importFn, getProjectAnnotations).ready(); await loadersRanGate; + // Assert - loader to be called the first time + expect(componentOneExports.default.loaders[0]).toHaveBeenCalledOnce(); expect(componentOneExports.default.loaders[0]).toHaveBeenCalledWith( expect.objectContaining({ args: { foo: 'a', one: 'mapped-1' }, }) ); - componentOneExports.default.loaders[0].mockClear(); + // Act - update the args (while loader is still running) emitter.emit(UPDATE_STORY_ARGS, { storyId: 'component-one--a', updatedArgs: { new: 'arg' }, }); - await waitForRender(); - expect(componentOneExports.default.loaders[0]).toHaveBeenCalledWith( - expect.objectContaining({ - args: { foo: 'a', new: 'arg', one: 'mapped-1' }, - }) - ); + // Arrange - open the gate to let the loader finish and wait for render + openBlockLoadersGate({ l: 8 }); + await waitForRender(); - // Story gets rendered with updated args - expect(projectAnnotations.renderToCanvas).toHaveBeenCalledTimes(1); + // Assert - renderToCanvas to be called the first time with initial args + expect(projectAnnotations.renderToCanvas).toHaveBeenCalledOnce(); expect(projectAnnotations.renderToCanvas).toHaveBeenCalledWith( expect.objectContaining({ - forceRemount: true, // Wasn't yet rendered so we need to force remount + forceRemount: true, storyContext: expect.objectContaining({ - loaded: { l: 7 }, // This is the value returned by the *second* loader call + loaded: { l: 8 }, // This is the value returned by the *first* loader call args: { foo: 'a', new: 'arg', one: 'mapped-1' }, }), }), 'story-element' ); + // Assert - loaders are not run again yet + expect(componentOneExports.default.loaders[0]).toHaveBeenCalledOnce(); - // Now let the first loader call resolve + // Arrange - wait for loading and rendering to finish a second time mockChannel.emit.mockClear(); - projectAnnotations.renderToCanvas.mockClear(); - openBlockLoadersGate({ l: 8 }); await waitForRender(); + // Assert - loader is called a second time with updated args + await vi.waitFor(() => { + expect(projectAnnotations.renderToCanvas).toHaveBeenCalledTimes(2); + expect(componentOneExports.default.loaders[0]).toHaveBeenCalledWith( + expect.objectContaining({ + args: { foo: 'a', new: 'arg', one: 'mapped-1' }, + }) + ); + }); - // Now the first call comes through, but picks up the new args - // Note this isn't a particularly realistic case (the second loader being quicker than the first) - expect(projectAnnotations.renderToCanvas).toHaveBeenCalledTimes(1); + // Assert - renderToCanvas is called a second time with updated args + expect(projectAnnotations.renderToCanvas).toHaveBeenCalledTimes(2); expect(projectAnnotations.renderToCanvas).toHaveBeenCalledWith( expect.objectContaining({ + forceRemount: false, storyContext: expect.objectContaining({ - loaded: { l: 8 }, + loaded: { l: 7 }, // This is the value returned by the *second* loader call args: { foo: 'a', new: 'arg', one: 'mapped-1' }, }), }), @@ -976,7 +986,7 @@ describe('PreviewWeb', () => { ); }); - it('renders a second time if renderToCanvas is running', async () => { + it('renders a second time after the already running renderToCanvas is done', async () => { const [gate, openGate] = createGate(); document.location.search = '?id=component-one--a'; @@ -990,11 +1000,9 @@ describe('PreviewWeb', () => { updatedArgs: { new: 'arg' }, }); - // Now let the renderToCanvas call resolve + // Now let the first renderToCanvas call resolve openGate(); - await waitForRender(); - - expect(projectAnnotations.renderToCanvas).toHaveBeenCalledTimes(2); + expect(projectAnnotations.renderToCanvas).toHaveBeenCalledTimes(1); expect(projectAnnotations.renderToCanvas).toHaveBeenCalledWith( expect.objectContaining({ forceRemount: true, @@ -1005,39 +1013,14 @@ describe('PreviewWeb', () => { }), 'story-element' ); - expect(projectAnnotations.renderToCanvas).toHaveBeenCalledWith( - expect.objectContaining({ - forceRemount: false, - storyContext: expect.objectContaining({ - loaded: { l: 7 }, - args: { foo: 'a', new: 'arg', one: 'mapped-1' }, - }), - }), - 'story-element' - ); - }); - it('works if it is called directly from inside non async renderToCanvas', async () => { - document.location.search = '?id=component-one--a'; - projectAnnotations.renderToCanvas.mockImplementation(() => { - emitter.emit(UPDATE_STORY_ARGS, { - storyId: 'component-one--a', - updatedArgs: { new: 'arg' }, - }); - }); - await createAndRenderPreview(); + // Wait for the second render to finish + mockChannel.emit.mockClear(); + await waitForRender(); + await waitForRenderPhase('rendering'); + // Expect the second render to have the updated args expect(projectAnnotations.renderToCanvas).toHaveBeenCalledTimes(2); - expect(projectAnnotations.renderToCanvas).toHaveBeenCalledWith( - expect.objectContaining({ - forceRemount: true, - storyContext: expect.objectContaining({ - loaded: { l: 7 }, - args: { foo: 'a', one: 'mapped-1' }, - }), - }), - 'story-element' - ); expect(projectAnnotations.renderToCanvas).toHaveBeenCalledWith( expect.objectContaining({ forceRemount: false, @@ -1506,6 +1489,9 @@ describe('PreviewWeb', () => { openGate(); await waitForRenderPhase('aborted'); + // allow teardown to complete its retries + vi.runOnlyPendingTimers(); + await waitForRenderPhase('rendering'); expect(projectAnnotations.renderToCanvas).toHaveBeenCalledTimes(2); @@ -2145,39 +2131,6 @@ describe('PreviewWeb', () => { window.location = { ...originalLocation, reload: originalLocation.reload }; }); - it('stops initial story after loaders if running', async () => { - const [gate, openGate] = createGate(); - componentOneExports.default.loaders[0].mockImplementationOnce(async () => gate); - - document.location.search = '?id=component-one--a'; - await new PreviewWeb(importFn, getProjectAnnotations).ready(); - await waitForRenderPhase('loading'); - - emitter.emit(SET_CURRENT_STORY, { - storyId: 'component-one--b', - viewMode: 'story', - }); - await waitForSetCurrentStory(); - await waitForRender(); - - // Now let the loader resolve - openGate({ l: 8 }); - await waitForRender(); - - // Story gets rendered with updated args - expect(projectAnnotations.renderToCanvas).toHaveBeenCalledTimes(1); - expect(projectAnnotations.renderToCanvas).toHaveBeenCalledWith( - expect.objectContaining({ - forceRemount: true, - storyContext: expect.objectContaining({ - id: 'component-one--b', - loaded: { l: 7 }, - }), - }), - 'story-element' - ); - }); - it('aborts render for initial story', async () => { const [gate, openGate] = createGate(); diff --git a/code/lib/preview-api/src/modules/preview-web/render/StoryRender.test.ts b/code/lib/preview-api/src/modules/preview-web/render/StoryRender.test.ts index df4b37b840ae..d327f6bb05f3 100644 --- a/code/lib/preview-api/src/modules/preview-web/render/StoryRender.test.ts +++ b/code/lib/preview-api/src/modules/preview-web/render/StoryRender.test.ts @@ -15,43 +15,18 @@ const entry = { importPath: './component.stories.ts', } as StoryIndexEntry; -const createGate = (): [Promise, (_?: any) => void] => { - let openGate = (_?: any) => {}; - const gate = new Promise((resolve) => { +const createGate = (): [Promise, () => void] => { + let openGate = () => {}; + const gate = new Promise((resolve) => { openGate = resolve; }); return [gate, openGate]; }; +const tick = () => new Promise((resolve) => setTimeout(resolve, 0)); -describe('StoryRender', () => { - it('throws PREPARE_ABORTED if torndown during prepare', async () => { - const [importGate, openImportGate] = createGate(); - const mockStore = { - loadStory: vi.fn(async () => { - await importGate; - return {}; - }), - cleanupStory: vi.fn(), - }; - - const render = new StoryRender( - new Channel({}), - mockStore as unknown as StoryStore, - vi.fn(), - {} as any, - entry.id, - 'story' - ); - - const preparePromise = render.prepare(); - - render.teardown(); - - openImportGate(); - - await expect(preparePromise).rejects.toThrowError(PREPARE_ABORTED); - }); +window.location = { reload: vi.fn() } as any; +describe('StoryRender', () => { it('does run play function if passed autoplay=true', async () => { const story = { id: 'id', @@ -59,6 +34,7 @@ describe('StoryRender', () => { name: 'name', tags: [], applyLoaders: vi.fn(), + applyBeforeEach: vi.fn(), unboundStoryFn: vi.fn(), playFunction: vi.fn(), prepareContext: vi.fn(), @@ -66,7 +42,7 @@ describe('StoryRender', () => { const render = new StoryRender( new Channel({}), - { getStoryContext: () => ({}) } as any, + { getStoryContext: () => ({}), addCleanupCallbacks: vi.fn() } as any, vi.fn() as any, {} as any, entry.id, @@ -86,6 +62,7 @@ describe('StoryRender', () => { name: 'name', tags: [], applyLoaders: vi.fn(), + applyBeforeEach: vi.fn(), unboundStoryFn: vi.fn(), playFunction: vi.fn(), prepareContext: vi.fn(), @@ -93,7 +70,7 @@ describe('StoryRender', () => { const render = new StoryRender( new Channel({}), - { getStoryContext: () => ({}) } as any, + { getStoryContext: () => ({}), addCleanupCallbacks: vi.fn() } as any, vi.fn() as any, {} as any, entry.id, @@ -105,4 +82,270 @@ describe('StoryRender', () => { await render.renderToElement({} as any); expect(story.playFunction).not.toHaveBeenCalled(); }); + + it('only rerenders once when triggered multiple times while pending', async () => { + // Arrange - setup StoryRender and async gate blocking applyLoaders + const [loaderGate, openLoaderGate] = createGate(); + const story = { + id: 'id', + title: 'title', + name: 'name', + tags: [], + applyLoaders: vi.fn(() => loaderGate), + applyBeforeEach: vi.fn(), + unboundStoryFn: vi.fn(), + playFunction: vi.fn(), + prepareContext: vi.fn(), + }; + const store = { + getStoryContext: () => ({}), + cleanupStory: vi.fn(), + addCleanupCallbacks: vi.fn(), + }; + const renderToScreen = vi.fn(); + const render = new StoryRender( + new Channel({}), + store as any, + renderToScreen, + {} as any, + entry.id, + 'story', + { autoplay: true }, + story as any + ); + // Arrange - render (blocked by loaders) + render.renderToElement({} as any); + expect(story.applyLoaders).toHaveBeenCalledOnce(); + expect(render.phase).toBe('loading'); + + // Act - rerender 3x + render.rerender(); + render.rerender(); + render.rerender(); + + // Assert - still loading, not yet rendered + expect(story.applyLoaders).toHaveBeenCalledOnce(); + expect(render.phase).toBe('loading'); + expect(renderToScreen).not.toHaveBeenCalled(); + + // Act - finish loading + openLoaderGate(); + + // Assert - loaded and rendered twice, played once + await vi.waitFor(async () => { + console.log(render.phase); + expect(story.applyLoaders).toHaveBeenCalledTimes(2); + expect(renderToScreen).toHaveBeenCalledTimes(2); + expect(story.playFunction).toHaveBeenCalledOnce(); + }); + }); + + describe('teardown', () => { + it('throws PREPARE_ABORTED if torndown during prepare', async () => { + const [importGate, openImportGate] = createGate(); + const mockStore = { + loadStory: vi.fn(async () => { + await importGate; + return {}; + }), + cleanupStory: vi.fn(), + }; + + const render = new StoryRender( + new Channel({}), + mockStore as unknown as StoryStore, + vi.fn(), + {} as any, + entry.id, + 'story' + ); + + const preparePromise = render.prepare(); + + render.teardown(); + + openImportGate(); + + await expect(preparePromise).rejects.toThrowError(PREPARE_ABORTED); + }); + + it('reloads the page when tearing down during loading', async () => { + // Arrange - setup StoryRender and async gate blocking applyLoaders + const [loaderGate] = createGate(); + const story = { + id: 'id', + title: 'title', + name: 'name', + tags: [], + applyLoaders: vi.fn(() => loaderGate), + applyBeforeEach: vi.fn(), + unboundStoryFn: vi.fn(), + playFunction: vi.fn(), + prepareContext: vi.fn(), + }; + const store = { + getStoryContext: () => ({}), + cleanupStory: vi.fn(), + addCleanupCallbacks: vi.fn(), + }; + const render = new StoryRender( + new Channel({}), + store as any, + vi.fn() as any, + {} as any, + entry.id, + 'story', + { autoplay: true }, + story as any + ); + + // Act - render (blocked by loaders), teardown + render.renderToElement({} as any); + expect(story.applyLoaders).toHaveBeenCalledOnce(); + expect(render.phase).toBe('loading'); + render.teardown(); + + // Assert - window is reloaded + await vi.waitFor(() => { + expect(window.location.reload).toHaveBeenCalledOnce(); + expect(store.cleanupStory).toHaveBeenCalledOnce(); + }); + }); + + it('reloads the page when tearing down during rendering', async () => { + // Arrange - setup StoryRender and async gate blocking renderToScreen + const [renderGate] = createGate(); + const story = { + id: 'id', + title: 'title', + name: 'name', + tags: [], + applyLoaders: vi.fn(), + applyBeforeEach: vi.fn(), + unboundStoryFn: vi.fn(), + playFunction: vi.fn(), + prepareContext: vi.fn(), + }; + const store = { + getStoryContext: () => ({}), + cleanupStory: vi.fn(), + addCleanupCallbacks: vi.fn(), + }; + const renderToScreen = vi.fn(() => renderGate); + + const render = new StoryRender( + new Channel({}), + store as any, + renderToScreen as any, + {} as any, + entry.id, + 'story', + { autoplay: true }, + story as any + ); + + // Act - render (blocked by renderToScreen), teardown + render.renderToElement({} as any); + await tick(); // go from 'loading' to 'rendering' phase + expect(renderToScreen).toHaveBeenCalledOnce(); + expect(render.phase).toBe('rendering'); + render.teardown(); + + // Assert - window is reloaded + await vi.waitFor(() => { + expect(window.location.reload).toHaveBeenCalledOnce(); + expect(store.cleanupStory).toHaveBeenCalledOnce(); + }); + }); + + it('reloads the page when tearing down during playing', async () => { + // Arrange - setup StoryRender and async gate blocking playing + const [playGate] = createGate(); + const story = { + id: 'id', + title: 'title', + name: 'name', + tags: [], + applyLoaders: vi.fn(), + applyBeforeEach: vi.fn(), + unboundStoryFn: vi.fn(), + playFunction: vi.fn(() => playGate), + prepareContext: vi.fn(), + }; + const store = { + getStoryContext: () => ({}), + cleanupStory: vi.fn(), + addCleanupCallbacks: vi.fn(), + }; + + const render = new StoryRender( + new Channel({}), + store as any, + vi.fn() as any, + {} as any, + entry.id, + 'story', + { autoplay: true }, + story as any + ); + + // Act - render (blocked by playFn), teardown + render.renderToElement({} as any); + await tick(); // go from 'loading' to 'beforeEach' phase + await tick(); // go from 'beforeEach' to 'playing' phase + expect(story.playFunction).toHaveBeenCalledOnce(); + expect(render.phase).toBe('playing'); + render.teardown(); + + // Assert - window is reloaded + await vi.waitFor(() => { + expect(window.location.reload).toHaveBeenCalledOnce(); + expect(store.cleanupStory).toHaveBeenCalledOnce(); + }); + }); + + it('reloads the page when remounting during loading', async () => { + // Arrange - setup StoryRender and async gate blocking applyLoaders + const [loaderGate] = createGate(); + const story = { + id: 'id', + title: 'title', + name: 'name', + tags: [], + applyLoaders: vi.fn(() => loaderGate), + applyBeforeEach: vi.fn(), + unboundStoryFn: vi.fn(), + playFunction: vi.fn(), + prepareContext: vi.fn(), + }; + const store = { + getStoryContext: () => ({}), + cleanupStory: vi.fn(), + addCleanupCallbacks: vi.fn(), + }; + const render = new StoryRender( + new Channel({}), + store as any, + vi.fn() as any, + {} as any, + entry.id, + 'story', + { autoplay: true }, + story as any + ); + + // Act - render, blocked by loaders + render.renderToElement({} as any); + expect(story.applyLoaders).toHaveBeenCalledOnce(); + expect(render.phase).toBe('loading'); + // Act - remount + render.remount(); + + // Assert - window is reloaded + await vi.waitFor(() => { + expect(window.location.reload).toHaveBeenCalledOnce(); + expect(store.cleanupStory).toHaveBeenCalledOnce(); + }); + }); + }); }); diff --git a/code/lib/preview-api/src/modules/preview-web/render/StoryRender.ts b/code/lib/preview-api/src/modules/preview-web/render/StoryRender.ts index ff3449b7a437..8ade66fc8942 100644 --- a/code/lib/preview-api/src/modules/preview-web/render/StoryRender.ts +++ b/code/lib/preview-api/src/modules/preview-web/render/StoryRender.ts @@ -27,6 +27,7 @@ const { AbortController } = globalThis; export type RenderPhase = | 'preparing' | 'loading' + | 'beforeEach' | 'rendering' | 'playing' | 'played' @@ -56,6 +57,8 @@ export class StoryRender implements Render {}; @@ -101,7 +104,7 @@ export class StoryRender implements Render); + await this.store.cleanupStory(this.story as PreparedStory); throw PREPARE_ABORTED; } } @@ -120,7 +123,7 @@ export class StoryRender implements Render implements Render implements Render); + // TODO add this to CSF + canvasElement, + } as unknown as StoryContextForLoaders); }); - if (abortSignal.aborted) { - return; - } + if (abortSignal.aborted) return; const renderStoryContext: StoryContext = { ...loadedContext!, @@ -187,6 +200,14 @@ export class StoryRender implements Render { + const cleanupCallbacks = await applyBeforeEach(renderStoryContext); + this.store.addCleanupCallbacks(story, cleanupCallbacks); + }); + + if (abortSignal.aborted) return; + const renderContext: RenderContext = { componentId, title, @@ -265,13 +286,31 @@ export class StoryRender implements Render implements Render { const { hooks } = store.getStoryContext(story) as { hooks: HooksContext }; hooks.clean = vi.fn(); - store.cleanupStory(story); + await store.cleanupStory(story); expect(hooks.clean).toHaveBeenCalled(); }); }); @@ -621,6 +621,7 @@ describe('StoryStore', () => { expect(store.raw()).toMatchInlineSnapshot(` [ { + "applyBeforeEach": [Function], "applyLoaders": [Function], "argTypes": { "a": { @@ -666,6 +667,7 @@ describe('StoryStore', () => { "undecoratedStoryFn": [Function], }, { + "applyBeforeEach": [Function], "applyLoaders": [Function], "argTypes": { "a": { @@ -711,6 +713,7 @@ describe('StoryStore', () => { "undecoratedStoryFn": [Function], }, { + "applyBeforeEach": [Function], "applyLoaders": [Function], "argTypes": { "a": { diff --git a/code/lib/preview-api/src/modules/store/StoryStore.ts b/code/lib/preview-api/src/modules/store/StoryStore.ts index 123ea5b984c8..ebfe582cdfe2 100644 --- a/code/lib/preview-api/src/modules/store/StoryStore.ts +++ b/code/lib/preview-api/src/modules/store/StoryStore.ts @@ -40,6 +40,7 @@ import { normalizeProjectAnnotations, prepareContext, } from './csf'; +import type { CleanupCallback } from '@storybook/csf'; // TODO -- what are reasonable values for these? const CSF_CACHE_SIZE = 1000; @@ -56,6 +57,8 @@ export class StoryStore { hooks: Record>; + cleanupCallbacks: Record; + cachedCSFFiles?: Record>; processCSFFileWithCache: typeof processCSFFile; @@ -79,6 +82,7 @@ export class StoryStore { this.args = new ArgsStore(); this.globals = new GlobalsStore({ globals, globalTypes }); this.hooks = {}; + this.cleanupCallbacks = {}; // We use a cache for these two functions for two reasons: // 1. For performance @@ -234,8 +238,17 @@ export class StoryStore { }); } - cleanupStory(story: PreparedStory): void { + addCleanupCallbacks(story: PreparedStory, callbacks: CleanupCallback[]) { + this.cleanupCallbacks[story.id] = callbacks; + } + + async cleanupStory(story: PreparedStory): Promise { this.hooks[story.id].clean(); + + const callbacks = this.cleanupCallbacks[story.id]; + if (callbacks) for (const callback of [...callbacks].reverse()) await callback(); + + delete this.cleanupCallbacks[story.id]; } extract( diff --git a/code/lib/preview-api/src/modules/store/csf/composeConfigs.test.ts b/code/lib/preview-api/src/modules/store/csf/composeConfigs.test.ts index 147038a5a8d2..bfad2ebe5f21 100644 --- a/code/lib/preview-api/src/modules/store/csf/composeConfigs.test.ts +++ b/code/lib/preview-api/src/modules/store/csf/composeConfigs.test.ts @@ -21,6 +21,7 @@ describe('composeConfigs', () => { globals: {}, globalTypes: {}, loaders: [], + beforeEach: [], runStep: expect.any(Function), }); }); @@ -45,6 +46,7 @@ describe('composeConfigs', () => { globals: {}, globalTypes: {}, loaders: [], + beforeEach: [], runStep: expect.any(Function), }); }); @@ -73,6 +75,7 @@ describe('composeConfigs', () => { globals: {}, globalTypes: {}, loaders: [], + beforeEach: [], runStep: expect.any(Function), }); }); @@ -107,6 +110,7 @@ describe('composeConfigs', () => { globals: { x: '2', y: '1', z: '2', obj: { a: '2', c: '2' } }, globalTypes: { x: '2', y: '1', z: '2', obj: { a: '2', c: '2' } }, loaders: [], + beforeEach: [], runStep: expect.any(Function), }); }); @@ -144,6 +148,7 @@ describe('composeConfigs', () => { globals: { x: '2', y: '1', z: '2', obj: { a: '2', c: '2' } }, globalTypes: { x: '2', y: '1', z: '2', obj: { a: '2', c: '2' } }, loaders: [], + beforeEach: [], runStep: expect.any(Function), }); }); @@ -172,6 +177,7 @@ describe('composeConfigs', () => { globals: {}, globalTypes: {}, loaders: ['1', '2', '3', '4'], + beforeEach: [], runStep: expect.any(Function), }); }); @@ -200,6 +206,7 @@ describe('composeConfigs', () => { globals: {}, globalTypes: {}, loaders: ['1', '2', '3'], + beforeEach: [], runStep: expect.any(Function), }); }); @@ -224,6 +231,7 @@ describe('composeConfigs', () => { globals: {}, globalTypes: {}, loaders: [], + beforeEach: [], runStep: expect.any(Function), }); }); @@ -249,6 +257,7 @@ describe('composeConfigs', () => { globals: {}, globalTypes: {}, loaders: [], + beforeEach: [], runStep: expect.any(Function), }); }); @@ -277,6 +286,7 @@ describe('composeConfigs', () => { globals: {}, globalTypes: {}, loaders: [], + beforeEach: [], render: 'render-2', renderToCanvas: 'renderToCanvas-2', applyDecorators: 'applyDecorators-2', diff --git a/code/lib/preview-api/src/modules/store/csf/composeConfigs.ts b/code/lib/preview-api/src/modules/store/csf/composeConfigs.ts index e5785a6a3f01..12bf9fc9e050 100644 --- a/code/lib/preview-api/src/modules/store/csf/composeConfigs.ts +++ b/code/lib/preview-api/src/modules/store/csf/composeConfigs.ts @@ -58,6 +58,7 @@ export function composeConfigs( globals: getObjectField(moduleExportList, 'globals'), globalTypes: getObjectField(moduleExportList, 'globalTypes'), loaders: getArrayField(moduleExportList, 'loaders'), + beforeEach: getArrayField(moduleExportList, 'beforeEach'), render: getSingletonField(moduleExportList, 'render'), renderToCanvas: getSingletonField(moduleExportList, 'renderToCanvas'), renderToDOM: getSingletonField(moduleExportList, 'renderToDOM'), // deprecated 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 e2e6a88db31e..60cca0023352 100644 --- a/code/lib/preview-api/src/modules/store/csf/normalizeProjectAnnotations.ts +++ b/code/lib/preview-api/src/modules/store/csf/normalizeProjectAnnotations.ts @@ -16,6 +16,7 @@ export function normalizeProjectAnnotations({ argTypesEnhancers, decorators, loaders, + beforeEach, ...annotations }: ProjectAnnotations): NormalizedProjectAnnotations { return { @@ -23,6 +24,7 @@ export function normalizeProjectAnnotations({ ...(globalTypes && { globalTypes: normalizeInputTypes(globalTypes) }), decorators: normalizeArrays(decorators), loaders: normalizeArrays(loaders), + beforeEach: normalizeArrays(beforeEach), argTypesEnhancers: [ ...(argTypesEnhancers || []), inferArgTypes, diff --git a/code/lib/preview-api/src/modules/store/csf/normalizeStory.test.ts b/code/lib/preview-api/src/modules/store/csf/normalizeStory.test.ts index a4bf5a04fdeb..f9e4b9cdca92 100644 --- a/code/lib/preview-api/src/modules/store/csf/normalizeStory.test.ts +++ b/code/lib/preview-api/src/modules/store/csf/normalizeStory.test.ts @@ -51,6 +51,7 @@ describe('normalizeStory', () => { { "argTypes": {}, "args": {}, + "beforeEach": [], "decorators": [], "id": "title--story-export", "loaders": [], @@ -120,6 +121,7 @@ describe('normalizeStory', () => { { "argTypes": {}, "args": {}, + "beforeEach": [], "decorators": [], "id": "title--story-export", "loaders": [], @@ -156,6 +158,7 @@ describe('normalizeStory', () => { "args": { "storyArg": "val", }, + "beforeEach": [], "decorators": [ [Function], ], @@ -211,6 +214,7 @@ describe('normalizeStory', () => { "storyArg": "val", "storyArg2": "legacy", }, + "beforeEach": [], "decorators": [ [Function], [Function], 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 9a96bc4a332e..fc228251d82e 100644 --- a/code/lib/preview-api/src/modules/store/csf/normalizeStory.ts +++ b/code/lib/preview-api/src/modules/store/csf/normalizeStory.ts @@ -54,6 +54,10 @@ export function normalizeStory( const args = { ...story?.args, ...storyObject.args }; const argTypes = { ...(story?.argTypes as ArgTypes), ...(storyObject.argTypes as ArgTypes) }; const loaders = [...normalizeArrays(storyObject.loaders), ...normalizeArrays(story?.loaders)]; + const beforeEach = [ + ...normalizeArrays(storyObject.beforeEach), + ...normalizeArrays(story?.beforeEach), + ]; const { render, play, tags = [] } = storyObject; // eslint-disable-next-line no-underscore-dangle @@ -68,6 +72,7 @@ export function normalizeStory( args, argTypes: normalizeInputTypes(argTypes), loaders, + beforeEach, ...(render && { render }), ...(userStoryFn && { userStoryFn }), ...(play && { play }), diff --git a/code/lib/preview-api/src/modules/store/csf/portable-stories.test.ts b/code/lib/preview-api/src/modules/store/csf/portable-stories.test.ts index a3aa544c4827..832ad437139f 100644 --- a/code/lib/preview-api/src/modules/store/csf/portable-stories.test.ts +++ b/code/lib/preview-api/src/modules/store/csf/portable-stories.test.ts @@ -167,6 +167,94 @@ describe('composeStory', () => { expect(spyFn).toHaveBeenCalled(); }); + it('should work with spies set up in beforeEach', async () => { + const spyFn = vi.fn(); + + const Story: Story = { + args: { + spyFn, + }, + beforeEach: async () => { + spyFn.mockReturnValue('mockedData'); + }, + render: (args) => { + const data = args.spyFn(); + expect(data).toBe('mockedData'); + }, + }; + + const composedStory = composeStory(Story, {}); + await composedStory.load(); + composedStory(); + expect(spyFn).toHaveBeenCalled(); + }); + + it('should call beforeEach from Project, Meta and Story level', async () => { + const beforeEachSpy = vi.fn(); + const cleanupSpy = vi.fn(); + + const metaObj: Meta = { + beforeEach: async () => { + beforeEachSpy('define from meta'); + + return () => { + cleanupSpy('cleanup from meta'); + }; + }, + }; + + const Story: Story = { + render: () => 'foo', + beforeEach: async () => { + beforeEachSpy('define from story'); + + return () => { + cleanupSpy('cleanup from story'); + }; + }, + }; + + const composedStory = composeStory(Story, metaObj, { + beforeEach: async () => { + beforeEachSpy('define from project'); + + return () => { + cleanupSpy('cleanup from project'); + }; + }, + }); + await composedStory.load(); + composedStory(); + expect(beforeEachSpy).toHaveBeenNthCalledWith(1, 'define from project'); + expect(beforeEachSpy).toHaveBeenNthCalledWith(2, 'define from meta'); + expect(beforeEachSpy).toHaveBeenNthCalledWith(3, 'define from story'); + + // simulate the next story's load to trigger cleanup + await composedStory.load(); + expect(cleanupSpy).toHaveBeenNthCalledWith(1, 'cleanup from story'); + expect(cleanupSpy).toHaveBeenNthCalledWith(2, 'cleanup from meta'); + expect(cleanupSpy).toHaveBeenNthCalledWith(3, 'cleanup from project'); + }); + + it('should call beforeEach after loaders', async () => { + const spyFn = vi.fn(); + + const Story: Story = { + render: () => 'foo', + loaders: async () => { + spyFn('from loaders'); + }, + beforeEach: async () => { + spyFn('from beforeEach'); + }, + }; + + const composedStory = composeStory(Story, {}); + await composedStory.load(); + expect(spyFn).toHaveBeenNthCalledWith(1, 'from loaders'); + expect(spyFn).toHaveBeenNthCalledWith(2, 'from beforeEach'); + }); + it('should throw an error if Story is undefined', () => { expect(() => { // @ts-expect-error (invalid input) diff --git a/code/lib/preview-api/src/modules/store/csf/portable-stories.ts b/code/lib/preview-api/src/modules/store/csf/portable-stories.ts index 57e8fcda9a2b..c4aa34874040 100644 --- a/code/lib/preview-api/src/modules/store/csf/portable-stories.ts +++ b/code/lib/preview-api/src/modules/store/csf/portable-stories.ts @@ -1,6 +1,6 @@ /* eslint-disable no-underscore-dangle */ /* eslint-disable @typescript-eslint/naming-convention */ -import { isExportStory } from '@storybook/csf'; +import { type CleanupCallback, isExportStory } from '@storybook/csf'; import dedent from 'ts-dedent'; import type { Renderer, @@ -47,6 +47,8 @@ export function setProjectAnnotations( globalProjectAnnotations = composeConfigs(annotations.map(extractAnnotation)); } +const cleanupCallbacks: CleanupCallback[] = []; + export function composeStory( storyAnnotations: LegacyStoryAnnotationsOrFn, componentAnnotations: ComponentAnnotations, @@ -126,8 +128,14 @@ export function composeStory { + // First run any registered cleanup function + for (const callback of [...cleanupCallbacks].reverse()) await callback(); + cleanupCallbacks.length = 0; + const loadedContext = await story.applyLoaders(context); context.loaded = loadedContext.loaded; + + cleanupCallbacks.push(...(await story.applyBeforeEach(context))); }, args: story.initialArgs as Partial, parameters: story.parameters as Parameters, 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 b05485e5a506..5d5389beeb36 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 @@ -718,6 +718,7 @@ describe('prepareMeta', () => { name: storyName, story, applyLoaders, + applyBeforeEach, originalStoryFn, unboundStoryFn, undecoratedStoryFn, 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 d4613db8eb71..84439187c4de 100644 --- a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts +++ b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts @@ -20,7 +20,7 @@ import type { StoryContextForLoaders, StrictArgTypes, } from '@storybook/types'; -import { includeConditionalArg } from '@storybook/csf'; +import { type CleanupCallback, includeConditionalArg } from '@storybook/csf'; import { applyHooks } from '../../addons'; import { combineParameters } from '../parameters'; @@ -65,9 +65,23 @@ export function prepareStory( const loaded: Record = Object.assign({}, ...loadResults); updatedContext = { ...updatedContext, loaded: { ...updatedContext.loaded, ...loaded } }; } + return updatedContext; }; + const applyBeforeEach = async (context: StoryContext): Promise => { + const cleanupCallbacks = new Array<() => unknown>(); + for (const beforeEach of [ + ...normalizeArrays(projectAnnotations.beforeEach), + ...normalizeArrays(componentAnnotations.beforeEach), + ...normalizeArrays(storyAnnotations.beforeEach), + ]) { + const cleanup = await beforeEach(context); + if (cleanup) cleanupCallbacks.push(cleanup); + } + return cleanupCallbacks; + }; + const undecoratedStoryFn = (context: StoryContext) => (render as ArgsStoryFn)(context.args, context); @@ -117,6 +131,7 @@ export function prepareStory( undecoratedStoryFn, unboundStoryFn, applyLoaders, + applyBeforeEach, playFunction, }; } diff --git a/code/lib/preview-api/template/stories/rendering.stories.ts b/code/lib/preview-api/template/stories/rendering.stories.ts index 477e07137987..aa521c6fed91 100644 --- a/code/lib/preview-api/template/stories/rendering.stories.ts +++ b/code/lib/preview-api/template/stories/rendering.stories.ts @@ -66,3 +66,34 @@ export const ChangeArgs = { await expect(button).toHaveFocus(); }, }; + +let loadedLabel = 'Initial'; + +/** + * This story demonstrates what happens when rendering (loaders) have side effects, and can possibly interleave with each other + * Triggering multiple force remounts quickly should only result in a single remount in the end + * and the label should be 'Loaded. Click Me' at the end. If loaders are interleaving it would result in a label of 'Error: Interleaved loaders. Click Me' + * Similarly, changing args rapidly should only cause one rerender at a time, producing the same result. + */ +export const SlowLoader = { + parameters: { + chromatic: { disable: true }, + }, + loaders: [ + async () => { + loadedLabel = 'Loading...'; + await new Promise((resolve) => setTimeout(resolve, 1000)); + loadedLabel = loadedLabel === 'Loading...' ? 'Loaded.' : 'Error: Interleaved loaders.'; + return { label: loadedLabel }; + }, + ], + decorators: [ + (storyFn: any, context: any) => + storyFn({ + args: { + ...context.args, + label: `${context.loaded.label} ${context.args.label}`, + }, + }), + ], +}; diff --git a/code/lib/source-loader/package.json b/code/lib/source-loader/package.json index 833850ad8d5e..4c8916ae6631 100644 --- a/code/lib/source-loader/package.json +++ b/code/lib/source-loader/package.json @@ -45,7 +45,7 @@ "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.5", + "@storybook/csf": "^0.1.6", "@storybook/types": "workspace:*", "estraverse": "^5.2.0", "lodash": "^4.17.21", diff --git a/code/lib/test/template/stories/before-each.stories.ts b/code/lib/test/template/stories/before-each.stories.ts new file mode 100644 index 000000000000..9af203a7baaf --- /dev/null +++ b/code/lib/test/template/stories/before-each.stories.ts @@ -0,0 +1,31 @@ +import { expect, mocked, getByRole, spyOn, userEvent } from '@storybook/test'; + +const meta = { + component: globalThis.Components.Button, + beforeEach() { + spyOn(console, 'log').mockName('console.log'); + console.log('first'); + }, +}; + +export default meta; + +export const BeforeEachOrder = { + parameters: { + chromatic: { disable: true }, + }, + beforeEach() { + console.log('second'); + }, + args: { + label: 'Button', + onClick: () => { + console.log('third'); + }, + }, + async play({ canvasElement }) { + await userEvent.click(getByRole(canvasElement, 'button')); + + await expect(mocked(console.log).mock.calls).toEqual([['first'], ['second'], ['third']]); + }, +}; diff --git a/code/lib/test/template/stories/module-mocking.stories.ts b/code/lib/test/template/stories/module-mocking.stories.ts new file mode 100644 index 000000000000..8332183005a2 --- /dev/null +++ b/code/lib/test/template/stories/module-mocking.stories.ts @@ -0,0 +1,28 @@ +import { global as globalThis } from '@storybook/global'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore This alias is set in the sandbox. Using ts-ignore instead of ts-expect-error to avoid build errors in the sandbox. +// eslint-disable-next-line import/no-unresolved +import { foo } from '#utils'; +import { expect, fn, isMockFunction, mocked } from '@storybook/test'; + +export default { + component: globalThis.Components.Button, + args: { + onClick: fn(), + label: 'Mock story', + }, + parameters: { + chromatic: { + disable: true, + }, + }, + beforeEach: () => { + mocked(foo).mockReturnValue('mocked'); + }, + async play() { + await expect(isMockFunction(foo)).toBe(true); + await expect(foo()).toBe('mocked'); + }, +}; + +export const Basic = {}; diff --git a/code/lib/test/template/stories/utils.mock.ts b/code/lib/test/template/stories/utils.mock.ts new file mode 100644 index 000000000000..15c648e88c00 --- /dev/null +++ b/code/lib/test/template/stories/utils.mock.ts @@ -0,0 +1,4 @@ +import { fn } from '@storybook/test'; +import * as utils from './utils'; + +export const foo = fn(utils.foo); diff --git a/code/lib/test/template/stories/utils.ts b/code/lib/test/template/stories/utils.ts new file mode 100644 index 000000000000..5a80b1903c1f --- /dev/null +++ b/code/lib/test/template/stories/utils.ts @@ -0,0 +1 @@ +export const foo = () => 'not mocked'; diff --git a/code/lib/types/package.json b/code/lib/types/package.json index d7469d928ae0..57eae04e53f5 100644 --- a/code/lib/types/package.json +++ b/code/lib/types/package.json @@ -49,7 +49,7 @@ "file-system-cache": "2.3.0" }, "devDependencies": { - "@storybook/csf": "^0.1.5", + "@storybook/csf": "^0.1.6", "@types/fs-extra": "^11.0.1", "@types/node": "^18.0.0", "typescript": "^5.3.2" diff --git a/code/lib/types/src/modules/story.ts b/code/lib/types/src/modules/story.ts index ee34ad43ce79..041aaed2e709 100644 --- a/code/lib/types/src/modules/story.ts +++ b/code/lib/types/src/modules/story.ts @@ -3,6 +3,7 @@ import type { ProjectAnnotations as CsfProjectAnnotations, DecoratorFunction, LoaderFunction, + CleanupCallback, } from '@storybook/csf'; import type { @@ -103,6 +104,7 @@ export type PreparedStory = applyLoaders: ( context: StoryContextForLoaders ) => Promise & { loaded: StoryContext['loaded'] }>; + applyBeforeEach: (context: StoryContext) => Promise; playFunction?: (context: StoryContext) => Promise | void; }; diff --git a/code/package.json b/code/package.json index 8d9a4bcd9d21..82d1541a338b 100644 --- a/code/package.json +++ b/code/package.json @@ -127,7 +127,7 @@ "@storybook/core-events": "workspace:*", "@storybook/core-server": "workspace:*", "@storybook/core-webpack": "workspace:*", - "@storybook/csf": "^0.1.5", + "@storybook/csf": "^0.1.6", "@storybook/csf-plugin": "workspace:*", "@storybook/csf-tools": "workspace:*", "@storybook/docs-tools": "workspace:*", diff --git a/code/renderers/server/package.json b/code/renderers/server/package.json index ef60a8071fbf..b045700b4373 100644 --- a/code/renderers/server/package.json +++ b/code/renderers/server/package.json @@ -46,7 +46,7 @@ "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.5", + "@storybook/csf": "^0.1.6", "@storybook/csf-tools": "workspace:*", "@storybook/global": "^5.0.0", "@storybook/preview-api": "workspace:*", diff --git a/code/ui/blocks/package.json b/code/ui/blocks/package.json index 5e59af48f6da..3369d91ea08e 100644 --- a/code/ui/blocks/package.json +++ b/code/ui/blocks/package.json @@ -48,7 +48,7 @@ "@storybook/client-logger": "workspace:*", "@storybook/components": "workspace:*", "@storybook/core-events": "workspace:*", - "@storybook/csf": "^0.1.5", + "@storybook/csf": "^0.1.6", "@storybook/docs-tools": "workspace:*", "@storybook/global": "^5.0.0", "@storybook/icons": "^1.2.5", diff --git a/code/ui/components/package.json b/code/ui/components/package.json index 16c950eecd42..b1ceb03faa85 100644 --- a/code/ui/components/package.json +++ b/code/ui/components/package.json @@ -62,7 +62,7 @@ "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-slot": "^1.0.2", "@storybook/client-logger": "workspace:*", - "@storybook/csf": "^0.1.5", + "@storybook/csf": "^0.1.6", "@storybook/global": "^5.0.0", "@storybook/icons": "^1.2.5", "@storybook/theming": "workspace:*", diff --git a/code/ui/components/src/components/ScrollArea/ScrollArea.tsx b/code/ui/components/src/components/ScrollArea/ScrollArea.tsx index f3f965af8783..cd697523d44d 100644 --- a/code/ui/components/src/components/ScrollArea/ScrollArea.tsx +++ b/code/ui/components/src/components/ScrollArea/ScrollArea.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { forwardRef } from 'react'; import { styled } from '@storybook/theming'; import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'; @@ -75,36 +75,35 @@ const ScrollAreaThumb = styled(ScrollAreaPrimitive.Thumb)(({ theme }) => ({ }, })); -export const ScrollArea = ({ - children, - horizontal = false, - vertical = false, - offset = 2, - scrollbarSize = 6, - className, -}: ScrollAreaProps) => ( - - {children} - {horizontal && ( - - - - )} - {vertical && ( - - - - )} - {horizontal && vertical && } - +export const ScrollArea = forwardRef( + ( + { children, horizontal = false, vertical = false, offset = 2, scrollbarSize = 6, className }, + ref + ) => ( + + {children} + {horizontal && ( + + + + )} + {vertical && ( + + + + )} + {horizontal && vertical && } + + ) ); +ScrollArea.displayName = 'ScrollArea'; diff --git a/code/yarn.lock b/code/yarn.lock index c1f7541f4378..c15614efb882 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -5323,7 +5323,7 @@ __metadata: dependencies: "@storybook/client-logger": "workspace:*" "@storybook/core-events": "workspace:*" - "@storybook/csf": "npm:^0.1.5" + "@storybook/csf": "npm:^0.1.6" "@storybook/global": "npm:^5.0.0" "@storybook/manager-api": "workspace:*" "@storybook/preview-api": "workspace:*" @@ -5591,7 +5591,7 @@ __metadata: "@storybook/client-logger": "workspace:*" "@storybook/components": "workspace:*" "@storybook/core-events": "workspace:*" - "@storybook/csf": "npm:^0.1.5" + "@storybook/csf": "npm:^0.1.6" "@storybook/docs-tools": "workspace:*" "@storybook/global": "npm:^5.0.0" "@storybook/icons": "npm:^1.2.5" @@ -5822,7 +5822,7 @@ __metadata: "@babel/core": "npm:^7.24.4" "@babel/preset-env": "npm:^7.24.4" "@babel/types": "npm:^7.24.0" - "@storybook/csf": "npm:^0.1.5" + "@storybook/csf": "npm:^0.1.6" "@storybook/csf-tools": "workspace:*" "@storybook/node-logger": "workspace:*" "@storybook/types": "workspace:*" @@ -5859,7 +5859,7 @@ __metadata: "@radix-ui/react-scroll-area": "npm:^1.0.5" "@radix-ui/react-slot": "npm:^1.0.2" "@storybook/client-logger": "workspace:*" - "@storybook/csf": "npm:^0.1.5" + "@storybook/csf": "npm:^0.1.6" "@storybook/global": "npm:^5.0.0" "@storybook/icons": "npm:^1.2.5" "@storybook/test": "workspace:*" @@ -5963,7 +5963,7 @@ __metadata: "@storybook/channels": "workspace:*" "@storybook/core-common": "workspace:*" "@storybook/core-events": "workspace:*" - "@storybook/csf": "npm:^0.1.5" + "@storybook/csf": "npm:^0.1.6" "@storybook/csf-tools": "workspace:*" "@storybook/docs-mdx": "npm:3.0.0" "@storybook/global": "npm:^5.0.0" @@ -6046,7 +6046,7 @@ __metadata: "@babel/parser": "npm:^7.24.4" "@babel/traverse": "npm:^7.24.1" "@babel/types": "npm:^7.24.0" - "@storybook/csf": "npm:^0.1.5" + "@storybook/csf": "npm:^0.1.6" "@storybook/types": "workspace:*" "@types/fs-extra": "npm:^11.0.1" "@types/js-yaml": "npm:^4.0.5" @@ -6067,12 +6067,12 @@ __metadata: languageName: node linkType: hard -"@storybook/csf@npm:^0.1.5": - version: 0.1.5 - resolution: "@storybook/csf@npm:0.1.5" +"@storybook/csf@npm:^0.1.5, @storybook/csf@npm:^0.1.6": + version: 0.1.6 + resolution: "@storybook/csf@npm:0.1.6" dependencies: type-fest: "npm:^2.19.0" - checksum: 10c0/d7a5514a2e985e4ff0a01716034474f41ac61b9c889e7ff0abc1a4a7941c9e78783b77aa98c6b127fbd1cab0a9e3f90acc15b9e476e95b86865272d3d7b913f8 + checksum: 10c0/81d1ee28a258381ed1cd5e0f9177f2ee06a3b7488ac2cfc9182ba4276662eee8b93f3941f4a141f8f11479991efee55696bf9f442137188255934bbd1de98226 languageName: node linkType: hard @@ -6244,7 +6244,7 @@ __metadata: "@storybook/channels": "workspace:*" "@storybook/client-logger": "workspace:*" "@storybook/core-events": "workspace:*" - "@storybook/csf": "npm:^0.1.5" + "@storybook/csf": "npm:^0.1.6" "@storybook/global": "npm:^5.0.0" "@storybook/icons": "npm:^1.2.5" "@storybook/router": "workspace:*" @@ -6332,7 +6332,6 @@ __metadata: "@babel/runtime": "npm:^7.24.4" "@babel/types": "npm:^7.24.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:*" @@ -6340,6 +6339,7 @@ __metadata: "@storybook/preset-react-webpack": "workspace:*" "@storybook/preview-api": "workspace:*" "@storybook/react": "workspace:*" + "@storybook/test": "workspace:*" "@storybook/types": "workspace:*" "@types/babel__core": "npm:^7" "@types/babel__plugin-transform-runtime": "npm:^7" @@ -6582,7 +6582,7 @@ __metadata: "@storybook/client-logger": "workspace:*" "@storybook/core-common": "workspace:*" "@storybook/core-events": "workspace:*" - "@storybook/csf": "npm:^0.1.5" + "@storybook/csf": "npm:^0.1.6" "@storybook/global": "npm:^5.0.0" "@storybook/types": "workspace:*" "@types/qs": "npm:^6.9.5" @@ -6767,7 +6767,7 @@ __metadata: "@storybook/core-events": "workspace:*" "@storybook/core-server": "workspace:*" "@storybook/core-webpack": "workspace:*" - "@storybook/csf": "npm:^0.1.5" + "@storybook/csf": "npm:^0.1.6" "@storybook/csf-plugin": "workspace:*" "@storybook/csf-tools": "workspace:*" "@storybook/docs-tools": "workspace:*" @@ -6922,7 +6922,7 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/server@workspace:renderers/server" dependencies: - "@storybook/csf": "npm:^0.1.5" + "@storybook/csf": "npm:^0.1.6" "@storybook/csf-tools": "workspace:*" "@storybook/global": "npm:^5.0.0" "@storybook/preview-api": "workspace:*" @@ -6939,7 +6939,7 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/source-loader@workspace:lib/source-loader" dependencies: - "@storybook/csf": "npm:^0.1.5" + "@storybook/csf": "npm:^0.1.6" "@storybook/types": "workspace:*" estraverse: "npm:^5.2.0" lodash: "npm:^4.17.21" @@ -7118,7 +7118,7 @@ __metadata: resolution: "@storybook/types@workspace:lib/types" dependencies: "@storybook/channels": "workspace:*" - "@storybook/csf": "npm:^0.1.5" + "@storybook/csf": "npm:^0.1.6" "@types/express": "npm:^4.7.0" "@types/fs-extra": "npm:^11.0.1" "@types/node": "npm:^18.0.0" diff --git a/docs/api/portable-stories-playwright.md b/docs/api/portable-stories-playwright.md index d043ca7abd59..6c1f2764bb34 100644 --- a/docs/api/portable-stories-playwright.md +++ b/docs/api/portable-stories-playwright.md @@ -219,7 +219,7 @@ If your stories behave differently based on [globals](../essentials/toolbars-and ```tsx // Button.portable.ts -import { test } from 'vitest'; +import { test } from 'playwright'; import { render } from '@testing-library/react'; import { composeStory } from '@storybook/react'; diff --git a/docs/get-started/vue3-webpack5.md b/docs/get-started/vue3-webpack5.md index 283ba4fbe2d3..d5aa4c7913a8 100644 --- a/docs/get-started/vue3-webpack5.md +++ b/docs/get-started/vue3-webpack5.md @@ -74,9 +74,9 @@ First, install the framework: @@ -118,8 +118,8 @@ Finally, update your `.storybook/main.js|ts` to change the framework property: diff --git a/scripts/tasks/sandbox-parts.ts b/scripts/tasks/sandbox-parts.ts index b4e3b535d79d..bee4513a4326 100644 --- a/scripts/tasks/sandbox-parts.ts +++ b/scripts/tasks/sandbox-parts.ts @@ -467,6 +467,12 @@ export const addStories: Task['run'] = async ( cwd, disableDocs, }); + + await linkPackageStories(await workspacePath('core package', '@storybook/test'), { + mainConfig, + cwd, + disableDocs, + }); } const mainAddons = (mainConfig.getSafeFieldValue(['addons']) || []).reduce( @@ -580,6 +586,19 @@ export const extendMain: Task['run'] = async ({ template, sandboxDir }, { disabl await writeConfig(mainConfig); }; +export async function setImportMap(cwd: string) { + const packageJson = await readJson(join(cwd, 'package.json')); + + packageJson.imports = { + '#utils': { + storybook: './template-stories/lib/test/utils.mock.ts', + default: './template-stories/lib/test/utils.ts', + }, + }; + + await writeJson(join(cwd, 'package.json'), packageJson, { spaces: 2 }); +} + /** * Sets compodoc option in angular.json projects to false. We have to generate compodoc * manually to avoid symlink issues related to the template-stories folder. diff --git a/scripts/tasks/sandbox.ts b/scripts/tasks/sandbox.ts index ee57d64a0c38..1d3a1e1af74f 100644 --- a/scripts/tasks/sandbox.ts +++ b/scripts/tasks/sandbox.ts @@ -37,7 +37,7 @@ export const sandbox: Task = { await remove(details.sandboxDir); } - const { create, install, addStories, extendMain, init, addExtraDependencies } = + const { create, install, addStories, extendMain, init, addExtraDependencies, setImportMap } = // @ts-expect-error esbuild for some reason exports a default object // eslint-disable-next-line import/extensions (await import('./sandbox-parts.ts')).default; @@ -84,6 +84,8 @@ export const sandbox: Task = { await extendMain(details, options); + await setImportMap(details.sandboxDir); + logger.info(`✅ Storybook sandbox created at ${details.sandboxDir}`); }, }; diff --git a/test-storybooks/portable-stories-kitchen-sink/nextjs/jest.config.js b/test-storybooks/portable-stories-kitchen-sink/nextjs/jest.config.js index 5689702fae31..3867a50e7bc7 100644 --- a/test-storybooks/portable-stories-kitchen-sink/nextjs/jest.config.js +++ b/test-storybooks/portable-stories-kitchen-sink/nextjs/jest.config.js @@ -1,4 +1,5 @@ const nextJest = require('next/jest.js'); +const { getPackageAliases } = require('@storybook/nextjs/export-mocks'); const createJestConfig = nextJest({ // Provide the path to your Next.js app to load next.config.js and .env files in your test environment @@ -11,6 +12,9 @@ const customJestConfig = { testEnvironment: 'jsdom', // Add more setup options before each test is run setupFilesAfterEnv: ['./jest.setup.ts'], + moduleNameMapper: { + ...getPackageAliases() + }, }; // createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async diff --git a/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/Navigation.stories.tsx b/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/Navigation.stories.tsx index f675ad7181ef..8a120eb80e2a 100644 --- a/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/Navigation.stories.tsx +++ b/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/Navigation.stories.tsx @@ -8,6 +8,8 @@ import { } from 'next/navigation'; import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; +import { expect, userEvent, within } from '@storybook/test'; +import { getRouter } from '@storybook/nextjs/navigation.mock'; function Component() { const router = useRouter(); @@ -96,12 +98,32 @@ export default { query: { foo: 'bar', }, + prefetch: () => { + console.log('custom prefetch'); + }, }, }, }, } as Meta; -export const Default: Story = {}; +export const Default: StoryObj = { + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + const routerMock = getRouter(); + + await step('Asserts whether forward hook is called', async () => { + const forwardBtn = await canvas.findByText('Go forward'); + await userEvent.click(forwardBtn); + await expect(routerMock.forward).toHaveBeenCalled(); + }); + + await step('Asserts whether custom prefetch hook is called', async () => { + const prefetchBtn = await canvas.findByText('Prefetch'); + await userEvent.click(prefetchBtn); + await expect(routerMock.prefetch).toHaveBeenCalledWith('/prefetched-html'); + }); + }, +}; export const WithSegmentDefined: Story = { parameters: { diff --git a/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/NextHeader.stories.tsx b/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/NextHeader.stories.tsx new file mode 100644 index 000000000000..1c75f64375ea --- /dev/null +++ b/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/NextHeader.stories.tsx @@ -0,0 +1,46 @@ +import type { Meta } from '@storybook/react'; +import type { StoryObj } from '@storybook/react'; +import { expect, userEvent, within } from '@storybook/test'; +import { cookies, headers } from '@storybook/nextjs/headers.mock'; +import NextHeader from './NextHeader'; + +export default { + component: NextHeader, +} as Meta; + +type Story = StoryObj; + +export const Default: Story = { + loaders: async () => { + cookies().set('firstName', 'Jane'); + cookies().set({ + name: 'lastName', + value: 'Doe', + }); + headers().set('timezone', 'Central European Summer Time'); + }, + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + const headersMock = headers(); + const cookiesMock = cookies(); + await step('Cookie and header store apis are called upon rendering', async () => { + await expect(cookiesMock.getAll).toHaveBeenCalled(); + await expect(headersMock.entries).toHaveBeenCalled(); + }); + + await step('Upon clicking on submit, the user-id cookie is set', async () => { + const submitButton = await canvas.findByRole('button'); + await userEvent.click(submitButton); + + await expect(cookiesMock.set).toHaveBeenCalledWith('user-id', 'encrypted-id'); + }); + + await step('The user-id cookie is available in cookie and header stores', async () => { + await expect(headersMock.get('cookie')).toContain('user-id=encrypted-id'); + await expect(cookiesMock.get('user-id')).toEqual({ + name: 'user-id', + value: 'encrypted-id', + }); + }); + }, +}; diff --git a/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/NextHeader.tsx b/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/NextHeader.tsx new file mode 100644 index 000000000000..8321a9820141 --- /dev/null +++ b/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/NextHeader.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { cookies, headers } from 'next/headers'; + +export default function Component() { + function handleClick() { + cookies().set('user-id', 'encrypted-id'); + } + + return ( + <> +

Cookies:

+ {cookies() + .getAll() + .map(({ name, value }) => { + return ( +

+ Name: {name} + Value: {value} +

+ ); + })} + +

Headers:

+ {Array.from(headers()).map(([name, value]: [string, string]) => { + return ( +

+ Name: {name} + Value: {value} +

+ ); + })} + + + + ); +} diff --git a/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/Router.stories.tsx b/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/Router.stories.tsx index 2ea7511a2f4e..f5a840241fb4 100644 --- a/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/Router.stories.tsx +++ b/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/Router.stories.tsx @@ -1,5 +1,8 @@ -import { useRouter } from 'next/router'; import React from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; +import { expect, within, userEvent } from '@storybook/test'; +import { getRouter } from '@storybook/nextjs/router.mock'; +import Router, { useRouter } from 'next/router'; function Component() { const router = useRouter(); @@ -19,10 +22,12 @@ function Component() { name: 'Prefetch', }, { + // @ts-expect-error (old API) cb: () => router.push('/push-html', { forceOptimisticNavigation: true }), name: 'Push HTML', }, { + // @ts-expect-error (old API) cb: () => router.replace('/replaced-html', { forceOptimisticNavigation: true }), name: 'Replace', }, @@ -30,6 +35,7 @@ function Component() { return (
+
Router pathname: {Router.pathname}
pathname: {router.pathname}
searchparams:{' '} @@ -61,9 +67,42 @@ export default { query: { foo: 'bar', }, + prefetch: () => { + console.log('custom prefetch'); + }, }, }, }, -}; +} as Meta; + +export const Default: StoryObj = { + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + const routerMock = getRouter(); + + await step('Router property overrides should be available in useRouter fn', async () => { + await expect(Router.pathname).toBe('/hello'); + await expect(Router.query).toEqual({ foo: 'bar' }); + }); -export const Default = {}; + await step( + 'Router property overrides should be available in default export from next/router', + async () => { + await expect(Router.pathname).toBe('/hello'); + await expect(Router.query).toEqual({ foo: 'bar' }); + } + ); + + await step('Asserts whether forward hook is called', async () => { + const forwardBtn = await canvas.findByText('Go forward'); + await userEvent.click(forwardBtn); + await expect(routerMock.forward).toHaveBeenCalled(); + }); + + await step('Asserts whether custom prefetch hook is called', async () => { + const prefetchBtn = await canvas.findByText('Prefetch'); + await userEvent.click(prefetchBtn); + await expect(routerMock.prefetch).toHaveBeenCalledWith('/prefetched-html'); + }); + }, +}; diff --git a/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/__snapshots__/portable-stories.test.tsx.snap b/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/__snapshots__/portable-stories.test.tsx.snap index 92790424407b..5e046e9b7a0b 100644 --- a/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/__snapshots__/portable-stories.test.tsx.snap +++ b/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/__snapshots__/portable-stories.test.tsx.snap @@ -945,6 +945,100 @@ exports[`renders navigationStories stories renders WithSegmentDefinedForParams 1 `; +exports[`renders nextHeaderStories stories renders Default 1`] = ` + +
+
+ Global Decorator +
+

+ Cookies: +

+

+ + Name: + + + + firstName + + + Value: + + + + Jane + +

+

+ + Name: + + + + lastName + + + Value: + + + + Doe + +

+

+ Headers: +

+

+ + Name: + + + + cookie + + + Value: + + + + firstName=Jane; lastName=Doe + +

+

+ + Name: + + + + timezone + + + Value: + + + + Central European Summer Time + +

+ +
+
+ +`; + exports[`renders routerStories stories renders Default 1`] = `
@@ -954,6 +1048,10 @@ exports[`renders routerStories stories renders Default 1`] = ` Global Decorator
+
+ Router pathname: + /hello +
pathname: /hello diff --git a/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/portable-stories.test.tsx b/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/portable-stories.test.tsx index c17fd43fc746..79684dc595e0 100644 --- a/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/portable-stories.test.tsx +++ b/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/portable-stories.test.tsx @@ -11,6 +11,7 @@ import * as styledJsxStories from './StyledJsx.stories'; import * as dynamicImportStories from './DynamicImport.stories'; import * as fontStories from './Font.stories'; import * as headStories from './Head.stories'; +import * as nextHeaderStories from './NextHeader.stories'; import * as getImagePropsStories from './GetImageProps.stories'; // example with composeStories, returns an object with all stories composed with args/decorators @@ -28,8 +29,9 @@ const runTests = (name: string, storiesModule: any) => { }) } -// // example with composeStory, returns a single story composed with args/decorators +// example with composeStory, returns a single story composed with args/decorators describe('renders', () => { + runTests('nextHeaderStories', nextHeaderStories); runTests('navigationStories', navigationStories); runTests('linkStories', linkStories); runTests('routerStories', routerStories); diff --git a/test-storybooks/portable-stories-kitchen-sink/nextjs/yarn.lock b/test-storybooks/portable-stories-kitchen-sink/nextjs/yarn.lock index c6b4f419a9d9..34d3f97d1569 100644 --- a/test-storybooks/portable-stories-kitchen-sink/nextjs/yarn.lock +++ b/test-storybooks/portable-stories-kitchen-sink/nextjs/yarn.lock @@ -1,9 +1,6 @@ -# This file is generated by running "yarn install" inside your project. -# Manual changes might be lost - proceed with caution! - __metadata: version: 8 - cacheKey: 10 + cacheKey: merged "@aashutoshrathi/word-wrap@npm:^1.2.3": version: 1.2.6 @@ -60,6 +57,13 @@ __metadata: languageName: node linkType: hard +"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.23.3, @babel/compat-data@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/compat-data@npm:7.23.5" + checksum: 10/088f14f646ecbddd5ef89f120a60a1b3389a50a9705d44603dca77662707d0175a5e0e0da3943c3298f1907a4ab871468656fbbf74bb7842cd8b0686b2c19736 + languageName: node + linkType: hard + "@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.23.5": version: 7.23.5 resolution: "@babel/compat-data@npm:7.23.5" @@ -74,6 +78,29 @@ __metadata: languageName: node linkType: hard +"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.18.9, @babel/core@npm:^7.23.0, @babel/core@npm:^7.23.2, @babel/core@npm:^7.23.9": + version: 7.23.9 + resolution: "@babel/core@npm:7.23.9" + dependencies: + "@ampproject/remapping": "npm:^2.2.0" + "@babel/code-frame": "npm:^7.23.5" + "@babel/generator": "npm:^7.23.6" + "@babel/helper-compilation-targets": "npm:^7.23.6" + "@babel/helper-module-transforms": "npm:^7.23.3" + "@babel/helpers": "npm:^7.23.9" + "@babel/parser": "npm:^7.23.9" + "@babel/template": "npm:^7.23.9" + "@babel/traverse": "npm:^7.23.9" + "@babel/types": "npm:^7.23.9" + convert-source-map: "npm:^2.0.0" + debug: "npm:^4.1.0" + gensync: "npm:^1.0.0-beta.2" + json5: "npm:^2.2.3" + semver: "npm:^6.3.1" + checksum: 10/268cdbb86bef1b8ea5b1300f2f325e56a1740a5051360cb228ffeaa0f80282b6674f3a2b4d6466adb0691183759b88d4c37b4a4f77232c84a49ed771c84cdc27 + languageName: node + linkType: hard + "@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.18.9, @babel/core@npm:^7.23.0, @babel/core@npm:^7.23.9": version: 7.23.9 resolution: "@babel/core@npm:7.23.9" @@ -120,6 +147,18 @@ __metadata: languageName: node linkType: hard +"@babel/generator@npm:^7.23.0, @babel/generator@npm:^7.23.6, @babel/generator@npm:^7.7.2": + version: 7.23.6 + resolution: "@babel/generator@npm:7.23.6" + dependencies: + "@babel/types": "npm:^7.23.6" + "@jridgewell/gen-mapping": "npm:^0.3.2" + "@jridgewell/trace-mapping": "npm:^0.3.17" + jsesc: "npm:^2.5.1" + checksum: 10/864090d5122c0aa3074471fd7b79d8a880c1468480cbd28925020a3dcc7eb6e98bedcdb38983df299c12b44b166e30915b8085a7bc126e68fa7e2aadc7bd1ac5 + languageName: node + linkType: hard + "@babel/generator@npm:^7.23.6, @babel/generator@npm:^7.7.2": version: 7.23.6 resolution: "@babel/generator@npm:7.23.6" @@ -162,6 +201,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-compilation-targets@npm:^7.22.15, @babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.23.6": + version: 7.23.6 + resolution: "@babel/helper-compilation-targets@npm:7.23.6" + dependencies: + "@babel/compat-data": "npm:^7.23.5" + "@babel/helper-validator-option": "npm:^7.23.5" + browserslist: "npm:^4.22.2" + lru-cache: "npm:^5.1.1" + semver: "npm:^6.3.1" + checksum: 10/05595cd73087ddcd81b82d2f3297aac0c0422858dfdded43d304786cf680ec33e846e2317e6992d2c964ee61d93945cbf1fa8ec80b55aee5bfb159227fb02cb9 + languageName: node + linkType: hard + "@babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.23.6": version: 7.23.6 resolution: "@babel/helper-compilation-targets@npm:7.23.6" @@ -226,6 +278,21 @@ __metadata: languageName: node linkType: hard +"@babel/helper-define-polyfill-provider@npm:^0.5.0": + version: 0.5.0 + resolution: "@babel/helper-define-polyfill-provider@npm:0.5.0" + dependencies: + "@babel/helper-compilation-targets": "npm:^7.22.6" + "@babel/helper-plugin-utils": "npm:^7.22.5" + debug: "npm:^4.1.1" + lodash.debounce: "npm:^4.0.8" + resolve: "npm:^1.14.2" + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 10/f849e816ec4b182a3e8fa8e09ff016f88bb95259cd6b2190b815c48f83c3d3b68e973a8ec72acc5086bfe93705cbd46ec089c06476421d858597780e42235a03 + languageName: node + linkType: hard + "@babel/helper-define-polyfill-provider@npm:^0.6.1, @babel/helper-define-polyfill-provider@npm:^0.6.2": version: 0.6.2 resolution: "@babel/helper-define-polyfill-provider@npm:0.6.2" @@ -505,6 +572,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10/ddbaf2c396b7780f15e80ee01d6dd790db076985f3dfeb6527d1a8d4cacf370e49250396a3aa005b2c40233cac214a106232f83703d5e8491848bde273938232 + languageName: node + linkType: hard + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.24.1" @@ -516,6 +594,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.22.5" + "@babel/plugin-transform-optional-chaining": "npm:^7.23.3" + peerDependencies: + "@babel/core": ^7.13.0 + checksum: 10/434b9d710ae856fa1a456678cc304fbc93915af86d581ee316e077af746a709a741ea39d7e1d4f5b98861b629cc7e87f002d3138f5e836775632466d4c74aef2 + languageName: node + linkType: hard + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.24.1" @@ -529,6 +620,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:^7.23.7": + version: 7.23.7 + resolution: "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:7.23.7" + dependencies: + "@babel/helper-environment-visitor": "npm:^7.22.20" + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10/3b0c9554cd0048e6e7341d7b92f29d400dbc6a5a4fc2f86dbed881d32e02ece9b55bc520387bae2eac22a5ab38a0b205c29b52b181294d99b4dd75e27309b548 + languageName: node + linkType: hard + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:7.24.1" @@ -627,6 +730,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-import-assertions@npm:^7.22.5, @babel/plugin-syntax-import-assertions@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-syntax-import-assertions@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/883e6b35b2da205138caab832d54505271a3fee3fc1e8dc0894502434fc2b5d517cbe93bbfbfef8068a0fb6ec48ebc9eef3f605200a489065ba43d8cddc1c9a7 + languageName: node + linkType: hard + "@babel/plugin-syntax-import-assertions@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-syntax-import-assertions@npm:7.24.1" @@ -638,6 +752,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-import-attributes@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-syntax-import-attributes@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/9aed7661ffb920ca75df9f494757466ca92744e43072e0848d87fa4aa61a3f2ee5a22198ac1959856c036434b5614a8f46f1fb70298835dbe28220cdd1d4c11e + languageName: node + linkType: hard + "@babel/plugin-syntax-import-attributes@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-syntax-import-attributes@npm:7.24.1" @@ -815,6 +940,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-arrow-functions@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-arrow-functions@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/1e99118176e5366c2636064d09477016ab5272b2a92e78b8edb571d20bc3eaa881789a905b20042942c3c2d04efc530726cf703f937226db5ebc495f5d067e66 + languageName: node + linkType: hard + "@babel/plugin-transform-arrow-functions@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-arrow-functions@npm:7.24.1" @@ -826,6 +962,20 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-async-generator-functions@npm:^7.23.9": + version: 7.23.9 + resolution: "@babel/plugin-transform-async-generator-functions@npm:7.23.9" + dependencies: + "@babel/helper-environment-visitor": "npm:^7.22.20" + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-remap-async-to-generator": "npm:^7.22.20" + "@babel/plugin-syntax-async-generators": "npm:^7.8.4" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/d402494087a6b803803eb5ab46b837aab100a04c4c5148e38bfa943ea1bbfc1ecfb340f1ced68972564312d3580f550c125f452372e77607a558fbbaf98c31c0 + languageName: node + linkType: hard + "@babel/plugin-transform-async-generator-functions@npm:^7.24.3": version: 7.24.3 resolution: "@babel/plugin-transform-async-generator-functions@npm:7.24.3" @@ -840,6 +990,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-async-to-generator@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-async-to-generator@npm:7.23.3" + dependencies: + "@babel/helper-module-imports": "npm:^7.22.15" + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-remap-async-to-generator": "npm:^7.22.20" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/2e9d9795d4b3b3d8090332104e37061c677f29a1ce65bcbda4099a32d243e5d9520270a44bbabf0fb1fb40d463bd937685b1a1042e646979086c546d55319c3c + languageName: node + linkType: hard + "@babel/plugin-transform-async-to-generator@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-async-to-generator@npm:7.24.1" @@ -853,6 +1016,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-block-scoped-functions@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/e63b16d94ee5f4d917e669da3db5ea53d1e7e79141a2ec873c1e644678cdafe98daa556d0d359963c827863d6b3665d23d4938a94a4c5053a1619c4ebd01d020 + languageName: node + linkType: hard + "@babel/plugin-transform-block-scoped-functions@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.24.1" @@ -864,6 +1038,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-block-scoping@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-block-scoping@npm:7.23.4" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/bbb965a3acdfb03559806d149efbd194ac9c983b260581a60efcb15eb9fbe20e3054667970800146d867446db1c1398f8e4ee87f4454233e49b8f8ce947bd99b + languageName: node + linkType: hard + "@babel/plugin-transform-block-scoping@npm:^7.24.4": version: 7.24.4 resolution: "@babel/plugin-transform-block-scoping@npm:7.24.4" @@ -887,6 +1072,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-class-properties@npm:^7.22.5, @babel/plugin-transform-class-properties@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-class-properties@npm:7.23.3" + dependencies: + "@babel/helper-create-class-features-plugin": "npm:^7.22.15" + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/9c6f8366f667897541d360246de176dd29efc7a13d80a5b48361882f7173d9173be4646c3b7d9b003ccc0e01e25df122330308f33db921fa553aa17ad544b3fc + languageName: node + linkType: hard + "@babel/plugin-transform-class-properties@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-class-properties@npm:7.24.1" @@ -899,6 +1096,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-class-static-block@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-class-static-block@npm:7.23.4" + dependencies: + "@babel/helper-create-class-features-plugin": "npm:^7.22.15" + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/plugin-syntax-class-static-block": "npm:^7.14.5" + peerDependencies: + "@babel/core": ^7.12.0 + checksum: 10/c8bfaba19a674fc2eb54edad71e958647360474e3163e8226f1acd63e4e2dbec32a171a0af596c1dc5359aee402cc120fea7abd1fb0e0354b6527f0fc9e8aa1e + languageName: node + linkType: hard + "@babel/plugin-transform-class-static-block@npm:^7.24.4": version: 7.24.4 resolution: "@babel/plugin-transform-class-static-block@npm:7.24.4" @@ -912,6 +1122,24 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-classes@npm:^7.23.8": + version: 7.23.8 + resolution: "@babel/plugin-transform-classes@npm:7.23.8" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.22.5" + "@babel/helper-compilation-targets": "npm:^7.23.6" + "@babel/helper-environment-visitor": "npm:^7.22.20" + "@babel/helper-function-name": "npm:^7.23.0" + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-replace-supers": "npm:^7.22.20" + "@babel/helper-split-export-declaration": "npm:^7.22.6" + globals: "npm:^11.1.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/4bb4b19e7a39871c4414fb44fc5f2cc47c78f993b74c43238dfb99c9dac2d15cb99b43f8a3d42747580e1807d2b8f5e13ce7e95e593fd839bd176aa090bf9a23 + languageName: node + linkType: hard + "@babel/plugin-transform-classes@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-classes@npm:7.24.1" @@ -930,6 +1158,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-computed-properties@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-computed-properties@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/template": "npm:^7.22.15" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/e75593e02c5ea473c17839e3c9d597ce3697bf039b66afe9a4d06d086a87fb3d95850b4174476897afc351dc1b46a9ec3165ee6e8fbad3732c0d65f676f855ad + languageName: node + linkType: hard + "@babel/plugin-transform-computed-properties@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-computed-properties@npm:7.24.1" @@ -942,6 +1182,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-destructuring@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-destructuring@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/5abd93718af5a61f8f6a97d2ccac9139499752dd5b2c533d7556fb02947ae01b2f51d4c4f5e64df569e8783d3743270018eb1fa979c43edec7dd1377acf107ed + languageName: node + linkType: hard + "@babel/plugin-transform-destructuring@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-destructuring@npm:7.24.1" @@ -953,6 +1204,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-dotall-regex@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-dotall-regex@npm:7.23.3" + dependencies: + "@babel/helper-create-regexp-features-plugin": "npm:^7.22.15" + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/a2dbbf7f1ea16a97948c37df925cb364337668c41a3948b8d91453f140507bd8a3429030c7ce66d09c299987b27746c19a2dd18b6f17dcb474854b14fd9159a3 + languageName: node + linkType: hard + "@babel/plugin-transform-dotall-regex@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-dotall-regex@npm:7.24.1" @@ -965,6 +1228,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-duplicate-keys@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-duplicate-keys@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/c2a21c34dc0839590cd945192cbc46fde541a27e140c48fe1808315934664cdbf18db64889e23c4eeb6bad9d3e049482efdca91d29de5734ffc887c4fbabaa16 + languageName: node + linkType: hard + "@babel/plugin-transform-duplicate-keys@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-duplicate-keys@npm:7.24.1" @@ -976,6 +1250,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-dynamic-import@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-dynamic-import@npm:7.23.4" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/plugin-syntax-dynamic-import": "npm:^7.8.3" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/57a722604c430d9f3dacff22001a5f31250e34785d4969527a2ae9160fa86858d0892c5b9ff7a06a04076f8c76c9e6862e0541aadca9c057849961343aab0845 + languageName: node + linkType: hard + "@babel/plugin-transform-dynamic-import@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-dynamic-import@npm:7.24.1" @@ -988,6 +1274,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-exponentiation-operator@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.23.3" + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor": "npm:^7.22.15" + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/00d05ab14ad0f299160fcf9d8f55a1cc1b740e012ab0b5ce30207d2365f091665115557af7d989cd6260d075a252d9e4283de5f2b247dfbbe0e42ae586e6bf66 + languageName: node + linkType: hard + "@babel/plugin-transform-exponentiation-operator@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.24.1" @@ -1000,6 +1298,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-export-namespace-from@npm:^7.22.11, @babel/plugin-transform-export-namespace-from@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-export-namespace-from@npm:7.23.4" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/plugin-syntax-export-namespace-from": "npm:^7.8.3" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/9f770a81bfd03b48d6ba155d452946fd56d6ffe5b7d871e9ec2a0b15e0f424273b632f3ed61838b90015b25bbda988896b7a46c7d964fbf8f6feb5820b309f93 + languageName: node + linkType: hard + "@babel/plugin-transform-export-namespace-from@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-export-namespace-from@npm:7.24.1" @@ -1024,6 +1334,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-for-of@npm:^7.23.6": + version: 7.23.6 + resolution: "@babel/plugin-transform-for-of@npm:7.23.6" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/b84ef1f26a2db316237ae6d10fa7c22c70ac808ed0b8e095a8ecf9101551636cbb026bee9fb95a0a7944f3b8278ff9636a9088cb4a4ac5b84830a13829242735 + languageName: node + linkType: hard + "@babel/plugin-transform-for-of@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-for-of@npm:7.24.1" @@ -1036,6 +1358,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-function-name@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-function-name@npm:7.23.3" + dependencies: + "@babel/helper-compilation-targets": "npm:^7.22.15" + "@babel/helper-function-name": "npm:^7.23.0" + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/355c6dbe07c919575ad42b2f7e020f320866d72f8b79181a16f8e0cd424a2c761d979f03f47d583d9471b55dcd68a8a9d829b58e1eebcd572145b934b48975a6 + languageName: node + linkType: hard + "@babel/plugin-transform-function-name@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-function-name@npm:7.24.1" @@ -1049,6 +1384,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-json-strings@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-json-strings@npm:7.23.4" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/plugin-syntax-json-strings": "npm:^7.8.3" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/f9019820233cf8955d8ba346df709a0683c120fe86a24ed1c9f003f2db51197b979efc88f010d558a12e1491210fc195a43cd1c7fee5e23b92da38f793a875de + languageName: node + linkType: hard + "@babel/plugin-transform-json-strings@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-json-strings@npm:7.24.1" @@ -1061,6 +1408,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-literals@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-literals@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/519a544cd58586b9001c4c9b18da25a62f17d23c48600ff7a685d75ca9eb18d2c5e8f5476f067f0a8f1fea2a31107eff950b9864833061e6076dcc4bdc3e71ed + languageName: node + linkType: hard + "@babel/plugin-transform-literals@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-literals@npm:7.24.1" @@ -1072,6 +1430,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-logical-assignment-operators@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.23.4" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/plugin-syntax-logical-assignment-operators": "npm:^7.10.4" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/2ae1dc9b4ff3bf61a990ff3accdecb2afe3a0ca649b3e74c010078d1cdf29ea490f50ac0a905306a2bcf9ac177889a39ac79bdcc3a0fdf220b3b75fac18d39b5 + languageName: node + linkType: hard + "@babel/plugin-transform-logical-assignment-operators@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.24.1" @@ -1084,6 +1454,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-member-expression-literals@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-member-expression-literals@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/95cec13c36d447c5aa6b8e4c778b897eeba66dcb675edef01e0d2afcec9e8cb9726baf4f81b4bbae7a782595aed72e6a0d44ffb773272c3ca180fada99bf92db + languageName: node + linkType: hard + "@babel/plugin-transform-member-expression-literals@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-member-expression-literals@npm:7.24.1" @@ -1095,6 +1476,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-modules-amd@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-modules-amd@npm:7.23.3" + dependencies: + "@babel/helper-module-transforms": "npm:^7.23.3" + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/48c87dee2c7dae8ed40d16901f32c9e58be4ef87bf2c3985b51dd2e78e82081f3bad0a39ee5cf6e8909e13e954e2b4bedef0a8141922f281ed833ddb59ed9be2 + languageName: node + linkType: hard + "@babel/plugin-transform-modules-amd@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-modules-amd@npm:7.24.1" @@ -1133,6 +1526,20 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-modules-systemjs@npm:^7.23.9": + version: 7.23.9 + resolution: "@babel/plugin-transform-modules-systemjs@npm:7.23.9" + dependencies: + "@babel/helper-hoist-variables": "npm:^7.22.5" + "@babel/helper-module-transforms": "npm:^7.23.3" + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-validator-identifier": "npm:^7.22.20" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/4bb800e5a9d0d668d7421ae3672fccff7d5f2a36621fd87414d7ece6d6f4d93627f9644cfecacae934bc65ffc131c8374242aaa400cca874dcab9b281a21aff0 + languageName: node + linkType: hard + "@babel/plugin-transform-modules-systemjs@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-modules-systemjs@npm:7.24.1" @@ -1147,6 +1554,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-modules-umd@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-modules-umd@npm:7.23.3" + dependencies: + "@babel/helper-module-transforms": "npm:^7.23.3" + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/e3f3af83562d687899555c7826b3faf0ab93ee7976898995b1d20cbe7f4451c55e05b0e17bfb3e549937cbe7573daf5400b752912a241b0a8a64d2457c7626e5 + languageName: node + linkType: hard + "@babel/plugin-transform-modules-umd@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-modules-umd@npm:7.24.1" @@ -1171,6 +1590,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-new-target@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-new-target@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/e5053389316fce73ad5201b7777437164f333e24787fbcda4ae489cd2580dbbbdfb5694a7237bad91fabb46b591d771975d69beb1c740b82cb4761625379f00b + languageName: node + linkType: hard + "@babel/plugin-transform-new-target@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-new-target@npm:7.24.1" @@ -1194,6 +1624,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.22.11, @babel/plugin-transform-nullish-coalescing-operator@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.23.4" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/plugin-syntax-nullish-coalescing-operator": "npm:^7.8.3" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/a27d73ea134d3d9560a6b2e26ab60012fba15f1db95865aa0153c18f5ec82cfef6a7b3d8df74e3c2fca81534fa5efeb6cacaf7b08bdb7d123e3dafdd079886a3 + languageName: node + linkType: hard + "@babel/plugin-transform-nullish-coalescing-operator@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.24.1" @@ -1206,6 +1648,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-numeric-separator@npm:^7.22.11, @babel/plugin-transform-numeric-separator@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-numeric-separator@npm:7.23.4" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/plugin-syntax-numeric-separator": "npm:^7.10.4" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/6ba0e5db3c620a3ec81f9e94507c821f483c15f196868df13fa454cbac719a5449baf73840f5b6eb7d77311b24a2cf8e45db53700d41727f693d46f7caf3eec3 + languageName: node + linkType: hard + "@babel/plugin-transform-numeric-separator@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-numeric-separator@npm:7.24.1" @@ -1218,6 +1672,21 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-object-rest-spread@npm:^7.22.15, @babel/plugin-transform-object-rest-spread@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-object-rest-spread@npm:7.23.4" + dependencies: + "@babel/compat-data": "npm:^7.23.3" + "@babel/helper-compilation-targets": "npm:^7.22.15" + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/plugin-syntax-object-rest-spread": "npm:^7.8.3" + "@babel/plugin-transform-parameters": "npm:^7.23.3" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/656f09c4ec629856e807d5b386559166ae417ff75943abce19656b2c6de5101dfd0aaf23f9074e854339370b4e09f57518d3202457046ee5b567ded531005479 + languageName: node + linkType: hard + "@babel/plugin-transform-object-rest-spread@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-object-rest-spread@npm:7.24.1" @@ -1232,6 +1701,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-object-super@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-object-super@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-replace-supers": "npm:^7.22.20" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/e495497186f621fa79026e183b4f1fbb172fd9df812cbd2d7f02c05b08adbe58012b1a6eb6dd58d11a30343f6ec80d0f4074f9b501d70aa1c94df76d59164c53 + languageName: node + linkType: hard + "@babel/plugin-transform-object-super@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-object-super@npm:7.24.1" @@ -1244,6 +1725,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-optional-catch-binding@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.23.4" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/plugin-syntax-optional-catch-binding": "npm:^7.8.3" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/d50b5ee142cdb088d8b5de1ccf7cea85b18b85d85b52f86618f6e45226372f01ad4cdb29abd4fd35ea99a71fefb37009e0107db7a787dcc21d4d402f97470faf + languageName: node + linkType: hard + "@babel/plugin-transform-optional-catch-binding@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.24.1" @@ -1269,6 +1762,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-optional-chaining@npm:^7.23.0, @babel/plugin-transform-optional-chaining@npm:^7.23.3, @babel/plugin-transform-optional-chaining@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-optional-chaining@npm:7.23.4" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.22.5" + "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/0ef24e889d6151428953fc443af5f71f4dae73f373dc1b7f5dd3f6a61d511296eb77e9b870e8c2c02a933e3455ae24c1fa91738c826b72a4ff87e0337db527e8 + languageName: node + linkType: hard + "@babel/plugin-transform-optional-chaining@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-optional-chaining@npm:7.24.1" @@ -1282,6 +1788,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-parameters@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-parameters@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/a8c36c3fc25f9daa46c4f6db47ea809c395dc4abc7f01c4b1391f6e5b0cd62b83b6016728b02a6a8ac21aca56207c9ec66daefc0336e9340976978de7e6e28df + languageName: node + linkType: hard + "@babel/plugin-transform-parameters@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-parameters@npm:7.24.1" @@ -1305,6 +1822,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-private-methods@npm:^7.22.5, @babel/plugin-transform-private-methods@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-private-methods@npm:7.23.3" + dependencies: + "@babel/helper-create-class-features-plugin": "npm:^7.22.15" + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/cedc1285c49b5a6d9a3d0e5e413b756ac40b3ac2f8f68bdfc3ae268bc8d27b00abd8bb0861c72756ff5dd8bf1eb77211b7feb5baf4fdae2ebbaabe49b9adc1d0 + languageName: node + linkType: hard + "@babel/plugin-transform-private-methods@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-private-methods@npm:7.24.1" @@ -1317,6 +1846,20 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-private-property-in-object@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-private-property-in-object@npm:7.23.4" + 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-private-property-in-object": "npm:^7.14.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/02eef2ee98fa86ee5052ed9bf0742d6d22b510b5df2fcce0b0f5615d6001f7786c6b31505e7f1c2f446406d8fb33603a5316d957cfa5b8365cbf78ddcc24fa42 + languageName: node + linkType: hard + "@babel/plugin-transform-private-property-in-object@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-private-property-in-object@npm:7.24.1" @@ -1331,6 +1874,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-property-literals@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-property-literals@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/16b048c8e87f25095f6d53634ab7912992f78e6997a6ff549edc3cf519db4fca01c7b4e0798530d7f6a05228ceee479251245cdd850a5531c6e6f404104d6cc9 + languageName: node + linkType: hard + "@babel/plugin-transform-property-literals@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-property-literals@npm:7.24.1" @@ -1342,6 +1896,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-react-display-name@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-react-display-name@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/7f86964e8434d3ddbd3c81d2690c9b66dbf1cd8bd9512e2e24500e9fa8cf378bc52c0853270b3b82143aba5965aec04721df7abdb768f952b44f5c6e0b198779 + languageName: node + linkType: hard + "@babel/plugin-transform-react-display-name@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-react-display-name@npm:7.24.1" @@ -1364,6 +1929,21 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-react-jsx@npm:^7.22.15, @babel/plugin-transform-react-jsx@npm:^7.22.5": + version: 7.23.4 + resolution: "@babel/plugin-transform-react-jsx@npm:7.23.4" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.22.5" + "@babel/helper-module-imports": "npm:^7.22.15" + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/plugin-syntax-jsx": "npm:^7.23.3" + "@babel/types": "npm:^7.23.4" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/d83806701349addfb77b8347b4f0dc8e76fb1c9ac21bdef69f4002394fce2396d61facfc6e1a3de54cbabcdadf991a1f642e69edb5116ac14f95e33d9f7c221d + languageName: node + linkType: hard + "@babel/plugin-transform-react-jsx@npm:^7.22.5, @babel/plugin-transform-react-jsx@npm:^7.23.4": version: 7.23.4 resolution: "@babel/plugin-transform-react-jsx@npm:7.23.4" @@ -1379,6 +1959,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-react-pure-annotations@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.23.3" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/9ea3698b1d422561d93c0187ac1ed8f2367e4250b10e259785ead5aa643c265830fd0f4cf5087a5bedbc4007444c06da2f2006686613220acf0949895f453666 + languageName: node + linkType: hard + "@babel/plugin-transform-react-pure-annotations@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.24.1" @@ -1391,6 +1983,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-regenerator@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-regenerator@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + regenerator-transform: "npm:^0.15.2" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/7fdacc7b40008883871b519c9e5cdea493f75495118ccc56ac104b874983569a24edd024f0f5894ba1875c54ee2b442f295d6241c3280e61c725d0dd3317c8e6 + languageName: node + linkType: hard + "@babel/plugin-transform-regenerator@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-regenerator@npm:7.24.1" @@ -1399,7 +2003,18 @@ __metadata: regenerator-transform: "npm:^0.15.2" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/a04319388a0a7931c3f8e15715d01444c32519692178b70deccc86d53304e74c0f589a4268f6c68578d86f75e934dd1fe6e6ed9071f54ee8379f356f88ef6e42 + checksum: 10/a04319388a0a7931c3f8e15715d01444c32519692178b70deccc86d53304e74c0f589a4268f6c68578d86f75e934dd1fe6e6ed9071f54ee8379f356f88ef6e42 + languageName: node + linkType: hard + +"@babel/plugin-transform-reserved-words@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-reserved-words@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/298c4440ddc136784ff920127cea137168e068404e635dc946ddb5d7b2a27b66f1dd4c4acb01f7184478ff7d5c3e7177a127279479926519042948fb7fa0fa48 languageName: node linkType: hard @@ -1414,6 +2029,22 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-runtime@npm:^7.23.2": + version: 7.23.9 + resolution: "@babel/plugin-transform-runtime@npm:7.23.9" + dependencies: + "@babel/helper-module-imports": "npm:^7.22.15" + "@babel/helper-plugin-utils": "npm:^7.22.5" + babel-plugin-polyfill-corejs2: "npm:^0.4.8" + babel-plugin-polyfill-corejs3: "npm:^0.9.0" + babel-plugin-polyfill-regenerator: "npm:^0.5.5" + semver: "npm:^6.3.1" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/d942e5852f100d0de5021c4d1fda9e30c28b94aa846e09588476dd82c058fb6869a30be0cf915362bf23b5f3504aa150ca3c3b0299dbd0a86b3b1f5f744c2333 + languageName: node + linkType: hard + "@babel/plugin-transform-runtime@npm:^7.24.3": version: 7.24.3 resolution: "@babel/plugin-transform-runtime@npm:7.24.3" @@ -1430,6 +2061,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-shorthand-properties@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-shorthand-properties@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/5d677a03676f9fff969b0246c423d64d77502e90a832665dc872a5a5e05e5708161ce1effd56bb3c0f2c20a1112fca874be57c8a759d8b08152755519281f326 + languageName: node + linkType: hard + "@babel/plugin-transform-shorthand-properties@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-shorthand-properties@npm:7.24.1" @@ -1441,6 +2083,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-spread@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-spread@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/c6372d2f788fd71d85aba12fbe08ee509e053ed27457e6674a4f9cae41ff885e2eb88aafea8fadd0ccf990601fc69ec596fa00959e05af68a15461a8d97a548d + languageName: node + linkType: hard + "@babel/plugin-transform-spread@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-spread@npm:7.24.1" @@ -1453,6 +2107,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-sticky-regex@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-sticky-regex@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/53e55eb2575b7abfdb4af7e503a2bf7ef5faf8bf6b92d2cd2de0700bdd19e934e5517b23e6dfed94ba50ae516b62f3f916773ef7d9bc81f01503f585051e2949 + languageName: node + linkType: hard + "@babel/plugin-transform-sticky-regex@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-sticky-regex@npm:7.24.1" @@ -1464,6 +2129,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-template-literals@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-template-literals@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/b16c5cb0b8796be0118e9c144d15bdc0d20a7f3f59009c6303a6e9a8b74c146eceb3f05186f5b97afcba7cfa87e34c1585a22186e3d5b22f2fd3d27d959d92b2 + languageName: node + linkType: hard + "@babel/plugin-transform-template-literals@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-template-literals@npm:7.24.1" @@ -1475,6 +2151,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-typeof-symbol@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-typeof-symbol@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/0af7184379d43afac7614fc89b1bdecce4e174d52f4efaeee8ec1a4f2c764356c6dba3525c0685231f1cbf435b6dd4ee9e738d7417f3b10ce8bbe869c32f4384 + languageName: node + linkType: hard + "@babel/plugin-transform-typeof-symbol@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-typeof-symbol@npm:7.24.1" @@ -1514,6 +2201,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-unicode-escapes@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-unicode-escapes@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/561c429183a54b9e4751519a3dfba6014431e9cdc1484fad03bdaf96582dfc72c76a4f8661df2aeeae7c34efd0fa4d02d3b83a2f63763ecf71ecc925f9cc1f60 + languageName: node + linkType: hard + "@babel/plugin-transform-unicode-escapes@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-unicode-escapes@npm:7.24.1" @@ -1525,6 +2223,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-unicode-property-regex@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-unicode-property-regex@npm:7.23.3" + dependencies: + "@babel/helper-create-regexp-features-plugin": "npm:^7.22.15" + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/2298461a194758086d17c23c26c7de37aa533af910f9ebf31ebd0893d4aa317468043d23f73edc782ec21151d3c46cf0ff8098a83b725c49a59de28a1d4d6225 + languageName: node + linkType: hard + "@babel/plugin-transform-unicode-property-regex@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-unicode-property-regex@npm:7.24.1" @@ -1537,6 +2247,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-unicode-regex@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-unicode-regex@npm:7.23.3" + dependencies: + "@babel/helper-create-regexp-features-plugin": "npm:^7.22.15" + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/c5f835d17483ba899787f92e313dfa5b0055e3deab332f1d254078a2bba27ede47574b6599fcf34d3763f0c048ae0779dc21d2d8db09295edb4057478dc80a9a + languageName: node + linkType: hard + "@babel/plugin-transform-unicode-regex@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-unicode-regex@npm:7.24.1" @@ -1549,6 +2271,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-unicode-sets-regex@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-unicode-sets-regex@npm:7.23.3" + dependencies: + "@babel/helper-create-regexp-features-plugin": "npm:^7.22.15" + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10/79d0b4c951955ca68235c87b91ab2b393c96285f8aeaa34d6db416d2ddac90000c9bd6e8c4d82b60a2b484da69930507245035f28ba63c6cae341cf3ba68fdef + languageName: node + linkType: hard + "@babel/plugin-transform-unicode-sets-regex@npm:^7.24.1": version: 7.24.1 resolution: "@babel/plugin-transform-unicode-sets-regex@npm:7.24.1" @@ -1561,6 +2295,96 @@ __metadata: languageName: node linkType: hard +"@babel/preset-env@npm:^7.23.2": + version: 7.23.9 + resolution: "@babel/preset-env@npm:7.23.9" + dependencies: + "@babel/compat-data": "npm:^7.23.5" + "@babel/helper-compilation-targets": "npm:^7.23.6" + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-validator-option": "npm:^7.23.5" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "npm:^7.23.3" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "npm:^7.23.3" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "npm:^7.23.7" + "@babel/plugin-proposal-private-property-in-object": "npm:7.21.0-placeholder-for-preset-env.2" + "@babel/plugin-syntax-async-generators": "npm:^7.8.4" + "@babel/plugin-syntax-class-properties": "npm:^7.12.13" + "@babel/plugin-syntax-class-static-block": "npm:^7.14.5" + "@babel/plugin-syntax-dynamic-import": "npm:^7.8.3" + "@babel/plugin-syntax-export-namespace-from": "npm:^7.8.3" + "@babel/plugin-syntax-import-assertions": "npm:^7.23.3" + "@babel/plugin-syntax-import-attributes": "npm:^7.23.3" + "@babel/plugin-syntax-import-meta": "npm:^7.10.4" + "@babel/plugin-syntax-json-strings": "npm:^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators": "npm:^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator": "npm:^7.8.3" + "@babel/plugin-syntax-numeric-separator": "npm:^7.10.4" + "@babel/plugin-syntax-object-rest-spread": "npm:^7.8.3" + "@babel/plugin-syntax-optional-catch-binding": "npm:^7.8.3" + "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3" + "@babel/plugin-syntax-private-property-in-object": "npm:^7.14.5" + "@babel/plugin-syntax-top-level-await": "npm:^7.14.5" + "@babel/plugin-syntax-unicode-sets-regex": "npm:^7.18.6" + "@babel/plugin-transform-arrow-functions": "npm:^7.23.3" + "@babel/plugin-transform-async-generator-functions": "npm:^7.23.9" + "@babel/plugin-transform-async-to-generator": "npm:^7.23.3" + "@babel/plugin-transform-block-scoped-functions": "npm:^7.23.3" + "@babel/plugin-transform-block-scoping": "npm:^7.23.4" + "@babel/plugin-transform-class-properties": "npm:^7.23.3" + "@babel/plugin-transform-class-static-block": "npm:^7.23.4" + "@babel/plugin-transform-classes": "npm:^7.23.8" + "@babel/plugin-transform-computed-properties": "npm:^7.23.3" + "@babel/plugin-transform-destructuring": "npm:^7.23.3" + "@babel/plugin-transform-dotall-regex": "npm:^7.23.3" + "@babel/plugin-transform-duplicate-keys": "npm:^7.23.3" + "@babel/plugin-transform-dynamic-import": "npm:^7.23.4" + "@babel/plugin-transform-exponentiation-operator": "npm:^7.23.3" + "@babel/plugin-transform-export-namespace-from": "npm:^7.23.4" + "@babel/plugin-transform-for-of": "npm:^7.23.6" + "@babel/plugin-transform-function-name": "npm:^7.23.3" + "@babel/plugin-transform-json-strings": "npm:^7.23.4" + "@babel/plugin-transform-literals": "npm:^7.23.3" + "@babel/plugin-transform-logical-assignment-operators": "npm:^7.23.4" + "@babel/plugin-transform-member-expression-literals": "npm:^7.23.3" + "@babel/plugin-transform-modules-amd": "npm:^7.23.3" + "@babel/plugin-transform-modules-commonjs": "npm:^7.23.3" + "@babel/plugin-transform-modules-systemjs": "npm:^7.23.9" + "@babel/plugin-transform-modules-umd": "npm:^7.23.3" + "@babel/plugin-transform-named-capturing-groups-regex": "npm:^7.22.5" + "@babel/plugin-transform-new-target": "npm:^7.23.3" + "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.23.4" + "@babel/plugin-transform-numeric-separator": "npm:^7.23.4" + "@babel/plugin-transform-object-rest-spread": "npm:^7.23.4" + "@babel/plugin-transform-object-super": "npm:^7.23.3" + "@babel/plugin-transform-optional-catch-binding": "npm:^7.23.4" + "@babel/plugin-transform-optional-chaining": "npm:^7.23.4" + "@babel/plugin-transform-parameters": "npm:^7.23.3" + "@babel/plugin-transform-private-methods": "npm:^7.23.3" + "@babel/plugin-transform-private-property-in-object": "npm:^7.23.4" + "@babel/plugin-transform-property-literals": "npm:^7.23.3" + "@babel/plugin-transform-regenerator": "npm:^7.23.3" + "@babel/plugin-transform-reserved-words": "npm:^7.23.3" + "@babel/plugin-transform-shorthand-properties": "npm:^7.23.3" + "@babel/plugin-transform-spread": "npm:^7.23.3" + "@babel/plugin-transform-sticky-regex": "npm:^7.23.3" + "@babel/plugin-transform-template-literals": "npm:^7.23.3" + "@babel/plugin-transform-typeof-symbol": "npm:^7.23.3" + "@babel/plugin-transform-unicode-escapes": "npm:^7.23.3" + "@babel/plugin-transform-unicode-property-regex": "npm:^7.23.3" + "@babel/plugin-transform-unicode-regex": "npm:^7.23.3" + "@babel/plugin-transform-unicode-sets-regex": "npm:^7.23.3" + "@babel/preset-modules": "npm:0.1.6-no-external-plugins" + babel-plugin-polyfill-corejs2: "npm:^0.4.8" + babel-plugin-polyfill-corejs3: "npm:^0.9.0" + babel-plugin-polyfill-regenerator: "npm:^0.5.5" + core-js-compat: "npm:^3.31.0" + semver: "npm:^6.3.1" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/0214ac9434a2496eac7f56c0c91164421232ff2083a66e1ccab633ca91e262828e54a5cbdb9036e8fe53d53530b6597aa98c99de8ff07b5193ffd95f21dc9d2c + languageName: node + linkType: hard + "@babel/preset-env@npm:^7.24.4": version: 7.24.4 resolution: "@babel/preset-env@npm:7.24.4" @@ -1678,6 +2502,22 @@ __metadata: languageName: node linkType: hard +"@babel/preset-react@npm:^7.22.15": + version: 7.23.3 + resolution: "@babel/preset-react@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-validator-option": "npm:^7.22.15" + "@babel/plugin-transform-react-display-name": "npm:^7.23.3" + "@babel/plugin-transform-react-jsx": "npm:^7.22.15" + "@babel/plugin-transform-react-jsx-development": "npm:^7.22.5" + "@babel/plugin-transform-react-pure-annotations": "npm:^7.23.3" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/ef6aef131b2f36e2883e9da0d832903643cb3c9ad4f32e04fb3eecae59e4221d583139e8d8f973e25c28d15aafa6b3e60fe9f25c5fd09abd3e2df03b8637bdd2 + languageName: node + linkType: hard + "@babel/preset-react@npm:^7.24.1": version: 7.24.1 resolution: "@babel/preset-react@npm:7.24.1" @@ -1709,6 +2549,21 @@ __metadata: languageName: node linkType: hard +"@babel/preset-typescript@npm:^7.23.0, @babel/preset-typescript@npm:^7.23.2": + 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: 10/c4add0f3fcbb3f4a305c48db9ccb32694f1308ed9971ccbc1a8a3c76d5a13726addb3c667958092287d7aa080186c5c83dbfefa55eacf94657e6cde39e172848 + languageName: node + linkType: hard + "@babel/preset-typescript@npm:^7.24.1": version: 7.24.1 resolution: "@babel/preset-typescript@npm:7.24.1" @@ -1746,6 +2601,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2": + version: 7.23.9 + resolution: "@babel/runtime@npm:7.23.9" + dependencies: + regenerator-runtime: "npm:^0.14.0" + checksum: 10/9a520fe1bf72249f7dd60ff726434251858de15cccfca7aa831bd19d0d3fb17702e116ead82724659b8da3844977e5e13de2bae01eb8a798f2823a669f122be6 + languageName: node + linkType: hard + "@babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2": version: 7.23.9 resolution: "@babel/runtime@npm:7.23.9" @@ -1786,6 +2650,24 @@ __metadata: languageName: node linkType: hard +"@babel/traverse@npm:^7.18.9, @babel/traverse@npm:^7.23.2, @babel/traverse@npm:^7.23.9": + version: 7.23.9 + resolution: "@babel/traverse@npm:7.23.9" + dependencies: + "@babel/code-frame": "npm:^7.23.5" + "@babel/generator": "npm:^7.23.6" + "@babel/helper-environment-visitor": "npm:^7.22.20" + "@babel/helper-function-name": "npm:^7.23.0" + "@babel/helper-hoist-variables": "npm:^7.22.5" + "@babel/helper-split-export-declaration": "npm:^7.22.6" + "@babel/parser": "npm:^7.23.9" + "@babel/types": "npm:^7.23.9" + debug: "npm:^4.3.1" + globals: "npm:^11.1.0" + checksum: 10/e2bb845f7f229feb7c338f7e150f5f1abc5395dcd3a6a47f63a25242ec3ec6b165f04a6df7d4849468547faee34eb3cf52487eb0bd867a7d3c42fec2a648266f + languageName: node + linkType: hard + "@babel/traverse@npm:^7.18.9, @babel/traverse@npm:^7.23.9": version: 7.23.9 resolution: "@babel/traverse@npm:7.23.9" @@ -1893,6 +2775,7 @@ __metadata: "@esbuild/aix-ppc64@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/aix-ppc64@npm:0.20.1" + checksum: 10/undefined conditions: os=aix & cpu=ppc64 languageName: node linkType: hard @@ -1900,6 +2783,7 @@ __metadata: "@esbuild/android-arm64@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/android-arm64@npm:0.20.1" + checksum: 10/undefined conditions: os=android & cpu=arm64 languageName: node linkType: hard @@ -1907,6 +2791,7 @@ __metadata: "@esbuild/android-arm@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/android-arm@npm:0.20.1" + checksum: 10/undefined conditions: os=android & cpu=arm languageName: node linkType: hard @@ -1914,6 +2799,7 @@ __metadata: "@esbuild/android-x64@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/android-x64@npm:0.20.1" + checksum: 10/undefined conditions: os=android & cpu=x64 languageName: node linkType: hard @@ -1921,6 +2807,7 @@ __metadata: "@esbuild/darwin-arm64@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/darwin-arm64@npm:0.20.1" + checksum: 10/undefined conditions: os=darwin & cpu=arm64 languageName: node linkType: hard @@ -1928,6 +2815,7 @@ __metadata: "@esbuild/darwin-x64@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/darwin-x64@npm:0.20.1" + checksum: 10/undefined conditions: os=darwin & cpu=x64 languageName: node linkType: hard @@ -1935,6 +2823,7 @@ __metadata: "@esbuild/freebsd-arm64@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/freebsd-arm64@npm:0.20.1" + checksum: 10/undefined conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard @@ -1942,6 +2831,7 @@ __metadata: "@esbuild/freebsd-x64@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/freebsd-x64@npm:0.20.1" + checksum: 10/undefined conditions: os=freebsd & cpu=x64 languageName: node linkType: hard @@ -1949,6 +2839,7 @@ __metadata: "@esbuild/linux-arm64@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/linux-arm64@npm:0.20.1" + checksum: 10/undefined conditions: os=linux & cpu=arm64 languageName: node linkType: hard @@ -1956,6 +2847,7 @@ __metadata: "@esbuild/linux-arm@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/linux-arm@npm:0.20.1" + checksum: 10/undefined conditions: os=linux & cpu=arm languageName: node linkType: hard @@ -1963,6 +2855,7 @@ __metadata: "@esbuild/linux-ia32@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/linux-ia32@npm:0.20.1" + checksum: 10/undefined conditions: os=linux & cpu=ia32 languageName: node linkType: hard @@ -1970,6 +2863,7 @@ __metadata: "@esbuild/linux-loong64@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/linux-loong64@npm:0.20.1" + checksum: 10/undefined conditions: os=linux & cpu=loong64 languageName: node linkType: hard @@ -1977,6 +2871,7 @@ __metadata: "@esbuild/linux-mips64el@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/linux-mips64el@npm:0.20.1" + checksum: 10/undefined conditions: os=linux & cpu=mips64el languageName: node linkType: hard @@ -1984,6 +2879,7 @@ __metadata: "@esbuild/linux-ppc64@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/linux-ppc64@npm:0.20.1" + checksum: 10/undefined conditions: os=linux & cpu=ppc64 languageName: node linkType: hard @@ -1991,6 +2887,7 @@ __metadata: "@esbuild/linux-riscv64@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/linux-riscv64@npm:0.20.1" + checksum: 10/undefined conditions: os=linux & cpu=riscv64 languageName: node linkType: hard @@ -1998,6 +2895,7 @@ __metadata: "@esbuild/linux-s390x@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/linux-s390x@npm:0.20.1" + checksum: 10/undefined conditions: os=linux & cpu=s390x languageName: node linkType: hard @@ -2005,6 +2903,7 @@ __metadata: "@esbuild/linux-x64@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/linux-x64@npm:0.20.1" + checksum: 10/undefined conditions: os=linux & cpu=x64 languageName: node linkType: hard @@ -2012,6 +2911,7 @@ __metadata: "@esbuild/netbsd-x64@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/netbsd-x64@npm:0.20.1" + checksum: 10/undefined conditions: os=netbsd & cpu=x64 languageName: node linkType: hard @@ -2019,6 +2919,7 @@ __metadata: "@esbuild/openbsd-x64@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/openbsd-x64@npm:0.20.1" + checksum: 10/undefined conditions: os=openbsd & cpu=x64 languageName: node linkType: hard @@ -2026,6 +2927,7 @@ __metadata: "@esbuild/sunos-x64@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/sunos-x64@npm:0.20.1" + checksum: 10/undefined conditions: os=sunos & cpu=x64 languageName: node linkType: hard @@ -2033,6 +2935,7 @@ __metadata: "@esbuild/win32-arm64@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/win32-arm64@npm:0.20.1" + checksum: 10/undefined conditions: os=win32 & cpu=arm64 languageName: node linkType: hard @@ -2040,6 +2943,7 @@ __metadata: "@esbuild/win32-ia32@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/win32-ia32@npm:0.20.1" + checksum: 10/undefined conditions: os=win32 & cpu=ia32 languageName: node linkType: hard @@ -2047,6 +2951,7 @@ __metadata: "@esbuild/win32-x64@npm:0.20.1": version: 0.20.1 resolution: "@esbuild/win32-x64@npm:0.20.1" + checksum: 10/undefined conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -2133,6 +3038,7 @@ __metadata: dependenciesMeta: "@img/sharp-libvips-darwin-arm64": optional: true + checksum: 10/undefined conditions: os=darwin & cpu=arm64 languageName: node linkType: hard @@ -2145,6 +3051,7 @@ __metadata: dependenciesMeta: "@img/sharp-libvips-darwin-x64": optional: true + checksum: 10/undefined conditions: os=darwin & cpu=x64 languageName: node linkType: hard @@ -2152,6 +3059,7 @@ __metadata: "@img/sharp-libvips-darwin-arm64@npm:1.0.2": version: 1.0.2 resolution: "@img/sharp-libvips-darwin-arm64@npm:1.0.2" + checksum: 10/undefined conditions: os=darwin & cpu=arm64 languageName: node linkType: hard @@ -2159,6 +3067,7 @@ __metadata: "@img/sharp-libvips-darwin-x64@npm:1.0.2": version: 1.0.2 resolution: "@img/sharp-libvips-darwin-x64@npm:1.0.2" + checksum: 10/undefined conditions: os=darwin & cpu=x64 languageName: node linkType: hard @@ -2166,6 +3075,7 @@ __metadata: "@img/sharp-libvips-linux-arm64@npm:1.0.2": version: 1.0.2 resolution: "@img/sharp-libvips-linux-arm64@npm:1.0.2" + checksum: 10/undefined conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard @@ -2173,6 +3083,7 @@ __metadata: "@img/sharp-libvips-linux-arm@npm:1.0.2": version: 1.0.2 resolution: "@img/sharp-libvips-linux-arm@npm:1.0.2" + checksum: 10/undefined conditions: os=linux & cpu=arm & libc=glibc languageName: node linkType: hard @@ -2180,6 +3091,7 @@ __metadata: "@img/sharp-libvips-linux-s390x@npm:1.0.2": version: 1.0.2 resolution: "@img/sharp-libvips-linux-s390x@npm:1.0.2" + checksum: 10/undefined conditions: os=linux & cpu=s390x & libc=glibc languageName: node linkType: hard @@ -2187,6 +3099,7 @@ __metadata: "@img/sharp-libvips-linux-x64@npm:1.0.2": version: 1.0.2 resolution: "@img/sharp-libvips-linux-x64@npm:1.0.2" + checksum: 10/undefined conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard @@ -2194,6 +3107,7 @@ __metadata: "@img/sharp-libvips-linuxmusl-arm64@npm:1.0.2": version: 1.0.2 resolution: "@img/sharp-libvips-linuxmusl-arm64@npm:1.0.2" + checksum: 10/undefined conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard @@ -2201,6 +3115,7 @@ __metadata: "@img/sharp-libvips-linuxmusl-x64@npm:1.0.2": version: 1.0.2 resolution: "@img/sharp-libvips-linuxmusl-x64@npm:1.0.2" + checksum: 10/undefined conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard @@ -2213,6 +3128,7 @@ __metadata: dependenciesMeta: "@img/sharp-libvips-linux-arm64": optional: true + checksum: 10/undefined conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard @@ -2225,6 +3141,7 @@ __metadata: dependenciesMeta: "@img/sharp-libvips-linux-arm": optional: true + checksum: 10/undefined conditions: os=linux & cpu=arm & libc=glibc languageName: node linkType: hard @@ -2237,6 +3154,7 @@ __metadata: dependenciesMeta: "@img/sharp-libvips-linux-s390x": optional: true + checksum: 10/undefined conditions: os=linux & cpu=s390x & libc=glibc languageName: node linkType: hard @@ -2249,6 +3167,7 @@ __metadata: dependenciesMeta: "@img/sharp-libvips-linux-x64": optional: true + checksum: 10/undefined conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard @@ -2261,6 +3180,7 @@ __metadata: dependenciesMeta: "@img/sharp-libvips-linuxmusl-arm64": optional: true + checksum: 10/undefined conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard @@ -2273,6 +3193,7 @@ __metadata: dependenciesMeta: "@img/sharp-libvips-linuxmusl-x64": optional: true + checksum: 10/undefined conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard @@ -2282,6 +3203,7 @@ __metadata: resolution: "@img/sharp-wasm32@npm:0.33.3" dependencies: "@emnapi/runtime": "npm:^1.1.0" + checksum: 10/undefined conditions: cpu=wasm32 languageName: node linkType: hard @@ -2289,6 +3211,7 @@ __metadata: "@img/sharp-win32-ia32@npm:0.33.3": version: 0.33.3 resolution: "@img/sharp-win32-ia32@npm:0.33.3" + checksum: 10/undefined conditions: os=win32 & cpu=ia32 languageName: node linkType: hard @@ -2296,6 +3219,7 @@ __metadata: "@img/sharp-win32-x64@npm:0.33.3": version: 0.33.3 resolution: "@img/sharp-win32-x64@npm:0.33.3" + checksum: 10/undefined conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -2677,6 +3601,7 @@ __metadata: "@next/swc-darwin-arm64@npm:14.2.2": version: 14.2.2 resolution: "@next/swc-darwin-arm64@npm:14.2.2" + checksum: 10/undefined conditions: os=darwin & cpu=arm64 languageName: node linkType: hard @@ -2684,6 +3609,7 @@ __metadata: "@next/swc-darwin-x64@npm:14.2.2": version: 14.2.2 resolution: "@next/swc-darwin-x64@npm:14.2.2" + checksum: 10/undefined conditions: os=darwin & cpu=x64 languageName: node linkType: hard @@ -2691,6 +3617,7 @@ __metadata: "@next/swc-linux-arm64-gnu@npm:14.2.2": version: 14.2.2 resolution: "@next/swc-linux-arm64-gnu@npm:14.2.2" + checksum: 10/undefined conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard @@ -2698,6 +3625,7 @@ __metadata: "@next/swc-linux-arm64-musl@npm:14.2.2": version: 14.2.2 resolution: "@next/swc-linux-arm64-musl@npm:14.2.2" + checksum: 10/undefined conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard @@ -2705,6 +3633,7 @@ __metadata: "@next/swc-linux-x64-gnu@npm:14.2.2": version: 14.2.2 resolution: "@next/swc-linux-x64-gnu@npm:14.2.2" + checksum: 10/undefined conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard @@ -2712,6 +3641,7 @@ __metadata: "@next/swc-linux-x64-musl@npm:14.2.2": version: 14.2.2 resolution: "@next/swc-linux-x64-musl@npm:14.2.2" + checksum: 10/undefined conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard @@ -2719,6 +3649,7 @@ __metadata: "@next/swc-win32-arm64-msvc@npm:14.2.2": version: 14.2.2 resolution: "@next/swc-win32-arm64-msvc@npm:14.2.2" + checksum: 10/undefined conditions: os=win32 & cpu=arm64 languageName: node linkType: hard @@ -2726,6 +3657,7 @@ __metadata: "@next/swc-win32-ia32-msvc@npm:14.2.2": version: 14.2.2 resolution: "@next/swc-win32-ia32-msvc@npm:14.2.2" + checksum: 10/undefined conditions: os=win32 & cpu=ia32 languageName: node linkType: hard @@ -2733,6 +3665,7 @@ __metadata: "@next/swc-win32-x64-msvc@npm:14.2.2": version: 14.2.2 resolution: "@next/swc-win32-x64-msvc@npm:14.2.2" + checksum: 10/undefined conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -3058,6 +3991,22 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-slot@npm:^1.0.2": + version: 1.0.2 + resolution: "@radix-ui/react-slot@npm:1.0.2" + dependencies: + "@babel/runtime": "npm:^7.13.10" + "@radix-ui/react-compose-refs": "npm:1.0.1" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/734866561e991438fbcf22af06e56b272ed6ee8f7b536489ee3bf2f736f8b53bf6bc14ebde94834aa0aceda854d018a0ce20bb171defffbaed1f566006cbb887 + languageName: node + linkType: hard + "@radix-ui/react-use-callback-ref@npm:1.0.1": version: 1.0.1 resolution: "@radix-ui/react-use-callback-ref@npm:1.0.1" @@ -3683,6 +4632,15 @@ __metadata: languageName: node linkType: hard +"@storybook/csf@npm:^0.1.6": + version: 0.1.6 + resolution: "@storybook/csf@npm:0.1.6" + dependencies: + type-fest: "npm:^2.19.0" + checksum: 10/0e5fa962eaa325e80755f22c28e9b3e37d94b173eb7862323a9b2d0e67041487e7989acc505ada29b8fd72855520720a20d3607184c817bbc7ef0d72a5d92eeb + languageName: node + linkType: hard + "@storybook/docs-mdx@npm:3.0.0": version: 3.0.0 resolution: "@storybook/docs-mdx@npm:3.0.0" @@ -5493,6 +6451,19 @@ __metadata: languageName: node linkType: hard +"babel-plugin-polyfill-corejs2@npm:^0.4.8": + version: 0.4.8 + resolution: "babel-plugin-polyfill-corejs2@npm:0.4.8" + dependencies: + "@babel/compat-data": "npm:^7.22.6" + "@babel/helper-define-polyfill-provider": "npm:^0.5.0" + semver: "npm:^6.3.1" + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 10/6b5a79bdc1c43edf857fd3a82966b3c7ff4a90eee00ca8d663e0a98304d6e285a05759d64a4dbc16e04a2a5ea1f248673d8bf789711be5e694e368f19884887c + languageName: node + linkType: hard + "babel-plugin-polyfill-corejs3@npm:^0.10.1, babel-plugin-polyfill-corejs3@npm:^0.10.4": version: 0.10.4 resolution: "babel-plugin-polyfill-corejs3@npm:0.10.4" @@ -5505,6 +6476,29 @@ __metadata: languageName: node linkType: hard +"babel-plugin-polyfill-corejs3@npm:^0.9.0": + version: 0.9.0 + resolution: "babel-plugin-polyfill-corejs3@npm:0.9.0" + dependencies: + "@babel/helper-define-polyfill-provider": "npm:^0.5.0" + core-js-compat: "npm:^3.34.0" + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 10/efdf9ba82e7848a2c66e0522adf10ac1646b16f271a9006b61a22f976b849de22a07c54c8826887114842ccd20cc9a4617b61e8e0789227a74378ab508e715cd + languageName: node + linkType: hard + +"babel-plugin-polyfill-regenerator@npm:^0.5.5": + version: 0.5.5 + resolution: "babel-plugin-polyfill-regenerator@npm:0.5.5" + dependencies: + "@babel/helper-define-polyfill-provider": "npm:^0.5.0" + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 10/3a9b4828673b23cd648dcfb571eadcd9d3fadfca0361d0a7c6feeb5a30474e92faaa49f067a6e1c05e49b6a09812879992028ff3ef3446229ff132d6e1de7eb6 + languageName: node + linkType: hard + "babel-plugin-polyfill-regenerator@npm:^0.6.1": version: 0.6.2 resolution: "babel-plugin-polyfill-regenerator@npm:0.6.2" @@ -5779,6 +6773,20 @@ __metadata: languageName: node linkType: hard +"browserslist@npm:^4.21.10, browserslist@npm:^4.22.2, browserslist@npm:^4.22.3": + version: 4.23.0 + resolution: "browserslist@npm:4.23.0" + dependencies: + caniuse-lite: "npm:^1.0.30001587" + electron-to-chromium: "npm:^1.4.668" + node-releases: "npm:^2.0.14" + update-browserslist-db: "npm:^1.0.13" + bin: + browserslist: cli.js + checksum: 10/496c3862df74565dd942b4ae65f502c575cbeba1fa4a3894dad7aa3b16130dc3033bc502d8848147f7b625154a284708253d9598bcdbef5a1e34cf11dc7bad8e + languageName: node + linkType: hard + "browserslist@npm:^4.21.10, browserslist@npm:^4.22.2, browserslist@npm:^4.22.3, browserslist@npm:^4.23.0": version: 4.23.0 resolution: "browserslist@npm:4.23.0" @@ -5937,6 +6945,13 @@ __metadata: languageName: node linkType: hard +"caniuse-lite@npm:^1.0.30001579, caniuse-lite@npm:^1.0.30001587": + version: 1.0.30001589 + resolution: "caniuse-lite@npm:1.0.30001589" + checksum: 10/5e1d2eb7c32d48c52204227bc1377f0f4c758ef889c53b9b479e28470e7f82eb1db5853e7754be9600ee662ae32a1d58e8bef0fde6edab06322ddbabfd9d212f + languageName: node + linkType: hard + "caniuse-lite@npm:^1.0.30001587": version: 1.0.30001589 resolution: "caniuse-lite@npm:1.0.30001589" @@ -6389,6 +7404,15 @@ __metadata: languageName: node linkType: hard +"core-js-compat@npm:^3.31.0, core-js-compat@npm:^3.34.0": + version: 3.36.0 + resolution: "core-js-compat@npm:3.36.0" + dependencies: + browserslist: "npm:^4.22.3" + checksum: 10/633c49a254fe48981057e33651e5a74a0a14f14731aa5afed5d2e61fbe3c5cbc116ffd4feaa158c683c40d6dc4fd2e6aa0ebe12c45d157cfa571309d08400c98 + languageName: node + linkType: hard + "core-js-compat@npm:^3.36.1": version: 3.37.0 resolution: "core-js-compat@npm:3.37.0" @@ -8077,6 +9101,7 @@ __metadata: resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" dependencies: node-gyp: "npm:latest" + checksum: 10/undefined conditions: os=darwin languageName: node linkType: hard @@ -10094,6 +11119,17 @@ __metadata: languageName: node linkType: hard +"loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0": + version: 1.4.0 + resolution: "loose-envify@npm:1.4.0" + dependencies: + js-tokens: "npm:^3.0.0 || ^4.0.0" + bin: + loose-envify: cli.js + checksum: 10/6517e24e0cad87ec9888f500c5b5947032cdfe6ef65e1c1936a0c48a524b81e65542c9c3edc91c97d5bddc806ee2a985dbc79be89215d613b1de5db6d1cfe6f4 + languageName: node + linkType: hard + "loupe@npm:^2.3.6, loupe@npm:^2.3.7": version: 2.3.7 resolution: "loupe@npm:2.3.7" @@ -11355,6 +12391,7 @@ __metadata: react-dom: "npm:^18.2.0" storybook: "npm:^8.0.0" typescript: "npm:^5.2.2" + checksum: 10/undefined languageName: unknown linkType: soft @@ -13392,6 +14429,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.4.0": + version: 2.6.2 + resolution: "tslib@npm:2.6.2" + checksum: 10/bd26c22d36736513980091a1e356378e8b662ded04204453d353a7f34a4c21ed0afc59b5f90719d4ba756e581a162ecbf93118dc9c6be5acf70aa309188166ca + languageName: node + linkType: hard + "tsutils@npm:^3.21.0": version: 3.21.0 resolution: "tsutils@npm:3.21.0"