Merge remote-tracking branch 'origin/next' into valentin/remove-babel-from-webpack5-builder

This commit is contained in:
Valentin Palkovic 2024-01-09 13:34:01 +01:00
commit 10ad2a447e
173 changed files with 2219 additions and 4094 deletions

893
.yarn/releases/yarn-4.0.0.cjs generated vendored

File diff suppressed because one or more lines are too long

893
.yarn/releases/yarn-4.0.2.cjs generated vendored Executable file

File diff suppressed because one or more lines are too long

View File

@ -8,4 +8,4 @@ nodeLinker: node-modules
npmPublishAccess: public
yarnPath: .yarn/releases/yarn-4.0.0.cjs
yarnPath: .yarn/releases/yarn-4.0.2.cjs

View File

@ -25,6 +25,7 @@ If you run `yarn start` and encounter the following error, try rerunning `yarn s
```sh
> NX ENOENT: no such file or directory, open 'storybook/code/node_modules/nx/package.json'
```
If you are a Storybook contributor and still experience issues, it is recommended that you verify your local Storybook instance for any unintentional local changes. To do this, you can use the following command:
```sh
@ -37,11 +38,11 @@ By executing this command, you will be able to see which untracked or ignored fi
If you have forked the repository, you should [disable Github Actions for your repo](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository) as many of them (e.g. pushing to sandbox) will fail without proper authorization. In your Github repo, go to Settings > Actions > General > set the Actions Permissions to **Disable actions**.
# Running against different sandbox templates
## Running against different sandbox templates
You can also pick a specific template to use as your sandbox by running `yarn task`, which will prompt you to make further choices about which template you want and which task you want to run.
# Making code changes
## Making code changes
If you want to make code changes to Storybook packages while running a sandbox, you'll need to do the following:
@ -56,10 +57,10 @@ yarn build --watch react core-server api addon-docs
3. If you are running the sandbox in "unlinked" mode you'll need to re-run the sandbox from the `publish` step to see the changes:
```
```sh
yarn task --task dev --template <your template> --start-from=publish
```
# Contributing to Storybook
## Contributing to Storybook
For further advice on how to contribute, please refer to our [NEW contributing guide on the Storybook website](https://storybook.js.org/docs/contribute).

View File

@ -1,6 +1,7 @@
<h1>Migration</h1>
- [From version 7.x to 8.0.0](#from-version-7x-to-800)
- [Removal of `storiesOf`-API](#removal-of-storiesof-api)
- [Removed deprecated shim packages](#removed-deprecated-shim-packages)
- [Framework-specific Vite plugins have to be explicitly added](#framework-specific-vite-plugins-have-to-be-explicitly-added)
- [Implicit actions can not be used during rendering (for example in the play function)](#implicit-actions-can-not-be-used-during-rendering-for-example-in-the-play-function)
@ -367,6 +368,14 @@
## From version 7.x to 8.0.0
### Removal of `storiesOf`-API
The `storiesOf` API has been removed in Storybook 8.0.
If you need to dynamically create stories, you will need to implement this via the experimental `experimental_indexers` [API](#storyindexers-is-replaced-with-experimental_indexers).
For migrating to CSF, see: [`storyStoreV6` and `storiesOf` is deprecated](#storystorev6-and-storiesof-is-deprecated)
### Removed deprecated shim packages
In Storybook 7, these packages existed for backwards compatibility, but were marked as deprecated:

View File

@ -2,6 +2,8 @@ compressionLevel: 0
enableGlobalCache: true
installStatePath: ../.yarn/code-install-state.gz
logFilters:
- code: YN0005
level: discard
@ -23,7 +25,6 @@ plugins:
unsafeHttpWhitelist:
- localhost
yarnPath: ../.yarn/releases/yarn-4.0.0.cjs
installStatePath: '../.yarn/code-install-state.gz'
yarnPath: ../.yarn/releases/yarn-4.0.2.cjs
# Sometimes you get a "The remote archive doesn't match the expected checksum" error, uncommenting this line will fix it
# checksumBehavior: 'update'

View File

@ -60,7 +60,7 @@
"@storybook/client-logger": "workspace:*",
"@storybook/components": "workspace:*",
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.1",
"@storybook/icons": "^1.2.3",
"@storybook/manager-api": "workspace:*",
"@storybook/preview-api": "workspace:*",
"@storybook/theming": "workspace:*",

View File

@ -53,7 +53,7 @@
},
"dependencies": {
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.1",
"@storybook/icons": "^1.2.3",
"memoizerific": "^1.11.3",
"ts-dedent": "^2.0.0"
},

View File

@ -49,7 +49,7 @@
},
"dependencies": {
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.1",
"@storybook/icons": "^1.2.3",
"@storybook/types": "workspace:*",
"jest-mock": "^27.0.6",
"polished": "^4.2.2",

View File

@ -65,7 +65,7 @@
},
"dependencies": {
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.1",
"@storybook/icons": "^1.2.3",
"tiny-invariant": "^1.3.1"
},
"devDependencies": {

View File

@ -55,7 +55,7 @@
},
"dependencies": {
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.1",
"@storybook/icons": "^1.2.3",
"ts-dedent": "^2.0.0"
},
"devDependencies": {

View File

@ -1 +1 @@
import './dist/preset';
require('./dist/preset');

View File

@ -59,7 +59,7 @@
"@storybook/client-logger": "workspace:*",
"@storybook/components": "workspace:*",
"@storybook/core-events": "workspace:*",
"@storybook/icons": "^1.2.1",
"@storybook/icons": "^1.2.3",
"@storybook/manager-api": "workspace:*",
"@storybook/preview-api": "workspace:*",
"@storybook/theming": "workspace:*",

View File

@ -55,7 +55,7 @@
"@storybook/components": "workspace:*",
"@storybook/core-events": "workspace:*",
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.1",
"@storybook/icons": "^1.2.3",
"@storybook/manager-api": "workspace:*",
"@storybook/preview-api": "workspace:*",
"@storybook/theming": "workspace:*",

View File

@ -1,48 +0,0 @@
import { loadPreviewOrConfigFile } from '@storybook/core-common';
import type { Options } from '@storybook/types';
import slash from 'slash';
import { listStories } from './list-stories';
const absoluteFilesToImport = async (
files: string[],
name: string,
normalizePath: (id: string) => string
) =>
files
.map((el, i) => `import ${name ? `* as ${name}_${i} from ` : ''}'/@fs/${normalizePath(el)}'`)
.join('\n');
export async function generateVirtualStoryEntryCode(options: Options) {
const { normalizePath } = await import('vite');
const storyEntries = await listStories(options);
const resolveMap = storyEntries.reduce<Record<string, string>>(
(prev, entry) => ({ ...prev, [entry]: entry.replace(slash(process.cwd()), '.') }),
{}
);
const modules = storyEntries.map((entry, i) => `${JSON.stringify(entry)}: story_${i}`).join(',');
return `
${await absoluteFilesToImport(storyEntries, 'story', normalizePath)}
function loadable(key) {
return {${modules}}[key];
}
Object.assign(loadable, {
keys: () => (${JSON.stringify(Object.keys(resolveMap))}),
resolve: (key) => (${JSON.stringify(resolveMap)}[key])
});
export function configStories(configure) {
configure(loadable, { hot: import.meta.hot }, false);
}
`.trim();
}
export async function generatePreviewEntryCode({ configDir }: Options) {
const previewFile = loadPreviewOrConfigFile({ configDir });
if (!previewFile) return '';
return `import * as preview from '${slash(previewFile)}';
export default preview;`;
}

View File

@ -1,116 +0,0 @@
import { getRendererName } from '@storybook/core-common';
import type { Options, PreviewAnnotation } from '@storybook/types';
import { virtualPreviewFile, virtualStoriesFile } from './virtual-file-names';
import { processPreviewAnnotation } from './utils/process-preview-annotation';
export async function generateIframeScriptCode(options: Options, projectRoot: string) {
const { presets } = options;
const rendererName = await getRendererName(options);
const previewAnnotations = await presets.apply<PreviewAnnotation[]>(
'previewAnnotations',
[],
options
);
const configEntries = [...previewAnnotations]
.filter(Boolean)
.map((path) => processPreviewAnnotation(path, projectRoot));
const filesToImport = (files: string[], name: string) =>
files.map((el, i) => `import ${name ? `* as ${name}_${i} from ` : ''}'${el}'`).join('\n');
const importArray = (name: string, length: number) =>
new Array(length).fill(0).map((_, i) => `${name}_${i}`);
// noinspection UnnecessaryLocalVariableJS
/** @todo Inline variable and remove `noinspection` */
// language=JavaScript
const code = `
// Ensure that the client API is initialized by the framework before any other iframe code
// is loaded. That way our client-apis can assume the existence of the API+store
import { configure } from '${rendererName}';
import { logger } from '@storybook/client-logger';
import * as previewApi from "@storybook/preview-api";
${filesToImport(configEntries, 'config')}
import * as preview from '${virtualPreviewFile}';
import { configStories } from '${virtualStoriesFile}';
const {
addDecorator,
addParameters,
addLoader,
addArgs,
addArgTypes,
addStepRunner,
addArgTypesEnhancer,
addArgsEnhancer,
setGlobalRender,
} = previewApi;
const configs = [${importArray('config', configEntries.length)
.concat('preview.default')
.join(',')}].filter(Boolean)
configs.map(config => config.default ? config.default : config).forEach(config => {
Object.keys(config).forEach((key) => {
const value = config[key];
switch (key) {
case 'args': {
return addArgs(value);
}
case 'argTypes': {
return addArgTypes(value);
}
case 'decorators': {
return value.forEach((decorator) => addDecorator(decorator, false));
}
case 'loaders': {
return value.forEach((loader) => addLoader(loader, false));
}
case 'parameters': {
return addParameters({ ...value }, false);
}
case 'argTypesEnhancers': {
return value.forEach((enhancer) => addArgTypesEnhancer(enhancer));
}
case 'argsEnhancers': {
return value.forEach((enhancer) => addArgsEnhancer(enhancer))
}
case 'render': {
return setGlobalRender(value)
}
case 'globals':
case 'globalTypes': {
const v = {};
v[key] = value;
return addParameters(v, false);
}
case 'decorateStory':
case 'applyDecorators':
case 'renderToDOM': // deprecated
case 'renderToCanvas': {
return null; // This key is not handled directly in v6 mode.
}
case 'runStep': {
return addStepRunner(value);
}
default: {
// eslint-disable-next-line prefer-template
return console.log(key + ' was not supported :( !');
}
}
});
})
/* TODO: not quite sure what to do with this, to fix HMR
if (import.meta.hot) {
import.meta.hot.accept();
}
*/
configStories(configure);
`.trim();
return code;
}

View File

@ -1,7 +1,6 @@
import * as path from 'path';
import type { Options } from '@storybook/types';
import { logger } from '@storybook/node-logger';
import { listStories } from './list-stories';
@ -28,11 +27,7 @@ function toImportPath(relativePath: string) {
async function toImportFn(stories: string[]) {
const { normalizePath } = await import('vite');
const objectEntries = stories.map((file) => {
const ext = path.extname(file);
const relativePath = normalizePath(path.relative(process.cwd(), file));
if (!['.js', '.jsx', '.ts', '.tsx', '.mdx', '.svelte', '.vue'].includes(ext)) {
logger.warn(`Cannot process ${ext} file with storyStoreV7: ${relativePath}`);
}
return ` '${toImportPath(relativePath)}': async () => import('/@fs/${file}')`;
});

View File

@ -69,7 +69,6 @@ export async function generateModernIframeScriptCode(options: Options, projectRo
window.__STORYBOOK_PREVIEW__ = window.__STORYBOOK_PREVIEW__ || new PreviewWeb();
window.__STORYBOOK_STORY_STORE__ = window.__STORYBOOK_STORY_STORE__ || window.__STORYBOOK_PREVIEW__.storyStore;
window.__STORYBOOK_CLIENT_API__ = window.__STORYBOOK_CLIENT_API__ || new ClientApi({ storyStore: window.__STORYBOOK_PREVIEW__.storyStore });
window.__STORYBOOK_PREVIEW__.initialize({ importFn, getProjectAnnotations });
${generateHMRHandler(frameworkName)};

View File

@ -4,10 +4,8 @@ import * as fs from 'fs';
import type { Plugin } from 'vite';
import type { Options } from '@storybook/types';
import { transformIframeHtml } from '../transform-iframe-html';
import { generateIframeScriptCode } from '../codegen-iframe-script';
import { generateModernIframeScriptCode } from '../codegen-modern-iframe-script';
import { generateImportFnScriptCode } from '../codegen-importfn-script';
import { generateVirtualStoryEntryCode, generatePreviewEntryCode } from '../codegen-entries';
import { generateAddonSetupCode } from '../codegen-set-addon-channel';
import {
@ -90,27 +88,16 @@ export function codeGeneratorPlugin(options: Options): Plugin {
return undefined;
},
async load(id, config) {
const storyStoreV7 = options.features?.storyStoreV7;
if (id === virtualStoriesFile) {
if (storyStoreV7) {
return generateImportFnScriptCode(options);
}
return generateVirtualStoryEntryCode(options);
return generateImportFnScriptCode(options);
}
if (id === virtualAddonSetupFile) {
return generateAddonSetupCode();
}
if (id === virtualPreviewFile && !storyStoreV7) {
return generatePreviewEntryCode(options);
}
if (id === virtualFileId) {
if (storyStoreV7) {
return generateModernIframeScriptCode(options, projectRoot);
}
return generateIframeScriptCode(options, projectRoot);
return generateModernIframeScriptCode(options, projectRoot);
}
if (id === iframeId) {

View File

@ -1,16 +1,14 @@
import type { Options, PreviewAnnotation } from '@storybook/types';
import { join, resolve } from 'path';
import {
getBuilderOptions,
getRendererName,
handlebars,
interpolate,
loadPreviewOrConfigFile,
normalizeStories,
readTemplate,
} from '@storybook/core-common';
import type { Options, PreviewAnnotation } from '@storybook/types';
import { isAbsolute, join, resolve } from 'path';
import slash from 'slash';
import { toImportFn, toRequireContextString } from '@storybook/core-webpack';
import { toImportFn } from '@storybook/core-webpack';
import type { BuilderOptions } from '../types';
export const getVirtualModules = async (options: Options) => {
@ -37,79 +35,31 @@ export const getVirtualModules = async (options: Options) => {
return entry.absolute;
}
// TODO: Remove as soon as we drop support for disabled StoryStoreV7
if (isAbsolute(entry)) {
return entry;
}
return slash(entry);
}
),
loadPreviewOrConfigFile(options),
].filter(Boolean);
if (options.features?.storyStoreV7) {
const storiesFilename = 'storybook-stories.js';
const storiesPath = resolve(join(workingDir, storiesFilename));
const storiesFilename = 'storybook-stories.js';
const storiesPath = resolve(join(workingDir, storiesFilename));
const needPipelinedImport = !!builderOptions.lazyCompilation && !isProd;
virtualModules[storiesPath] = toImportFn(stories, { needPipelinedImport });
const configEntryPath = resolve(join(workingDir, 'storybook-config-entry.js'));
virtualModules[configEntryPath] = handlebars(
await readTemplate(
require.resolve(
'@storybook/builder-webpack5/templates/virtualModuleModernEntry.js.handlebars'
)
),
{
storiesFilename,
previewAnnotations,
}
// We need to double escape `\` for webpack. We may have some in windows paths
).replace(/\\/g, '\\\\');
entries.push(configEntryPath);
} else {
const rendererName = await getRendererName(options);
const rendererInitEntry = resolve(join(workingDir, 'storybook-init-renderer-entry.js'));
virtualModules[rendererInitEntry] = `import '${slash(rendererName)}';`;
entries.push(rendererInitEntry);
const entryTemplate = await readTemplate(
require.resolve('@storybook/builder-webpack5/templates/virtualModuleEntry.template.js')
);
previewAnnotations.forEach((previewAnnotationFilename: string | undefined) => {
if (!previewAnnotationFilename) return;
// Ensure that relative paths end up mapped to a filename in the cwd, so a later import
// of the `previewAnnotationFilename` in the template works.
const entryFilename = previewAnnotationFilename.startsWith('.')
? `${previewAnnotationFilename.replace(/(\w)(\/|\\)/g, '$1-')}-generated-config-entry.js`
: `${previewAnnotationFilename}-generated-config-entry.js`;
// NOTE: although this file is also from the `dist/cjs` directory, it is actually a ESM
// file, see https://github.com/storybookjs/storybook/pull/16727#issuecomment-986485173
virtualModules[entryFilename] = interpolate(entryTemplate, {
previewAnnotationFilename,
});
entries.push(entryFilename);
});
if (stories.length > 0) {
const storyTemplate = await readTemplate(
require.resolve('@storybook/builder-webpack5/templates/virtualModuleStory.template.js')
);
// NOTE: this file has a `.cjs` extension as it is a CJS file (from `dist/cjs`) and runs
// in the user's webpack mode, which may be strict about the use of require/import.
// See https://github.com/storybookjs/storybook/issues/14877
const storiesFilename = resolve(join(workingDir, `generated-stories-entry.cjs`));
virtualModules[storiesFilename] = interpolate(storyTemplate, {
rendererName,
})
// Make sure we also replace quotes for this one
.replace("'{{stories}}'", stories.map(toRequireContextString).join(','));
entries.push(storiesFilename);
const needPipelinedImport = !!builderOptions.lazyCompilation && !isProd;
virtualModules[storiesPath] = toImportFn(stories, { needPipelinedImport });
const configEntryPath = resolve(join(workingDir, 'storybook-config-entry.js'));
virtualModules[configEntryPath] = handlebars(
await readTemplate(
require.resolve(
'@storybook/builder-webpack5/templates/virtualModuleModernEntry.js.handlebars'
)
),
{
storiesFilename,
previewAnnotations,
}
}
// We need to double escape `\` for webpack. We may have some in windows paths
).replace(/\\/g, '\\\\');
entries.push(configEntryPath);
return {
virtualModules,

View File

@ -1,65 +0,0 @@
/* eslint-disable import/no-unresolved */
import {
addDecorator,
addParameters,
addLoader,
addArgs,
addArgTypes,
addStepRunner,
addArgsEnhancer,
addArgTypesEnhancer,
setGlobalRender,
} from '@storybook/preview-api';
import * as previewAnnotations from '{{previewAnnotationFilename}}';
const config = previewAnnotations.default ?? previewAnnotations;
Object.keys(config).forEach((key) => {
const value = config[key];
switch (key) {
case 'args': {
return addArgs(value);
}
case 'argTypes': {
return addArgTypes(value);
}
case 'decorators': {
return value.forEach((decorator) => addDecorator(decorator, false));
}
case 'loaders': {
return value.forEach((loader) => addLoader(loader, false));
}
case 'parameters': {
return addParameters({ ...value }, false);
}
case 'argTypesEnhancers': {
return value.forEach((enhancer) => addArgTypesEnhancer(enhancer));
}
case 'argsEnhancers': {
return value.forEach((enhancer) => addArgsEnhancer(enhancer));
}
case 'render': {
return setGlobalRender(value);
}
case 'globals':
case 'globalTypes': {
const v = {};
v[key] = value;
return addParameters(v, false);
}
case '__namedExportsOrder':
case 'decorateStory':
case 'renderToDOM': // deprecated
case 'renderToCanvas': {
return null; // This key is not handled directly in v6 mode.
}
case 'runStep': {
return addStepRunner(value);
}
default: {
return console.log(
`Unknown key '${key}' exported by preview annotation file '{{previewAnnotationFilename}}'`
);
}
}
});

View File

@ -20,7 +20,6 @@ const preview = new PreviewWeb();
window.__STORYBOOK_PREVIEW__ = preview;
window.__STORYBOOK_STORY_STORE__ = preview.storyStore;
window.__STORYBOOK_ADDONS_CHANNEL__ = channel;
window.__STORYBOOK_CLIENT_API__ = new ClientApi({ storyStore: preview.storyStore });
preview.initialize({ importFn, getProjectAnnotations });

View File

@ -1,3 +0,0 @@
const { configure } = require('{{rendererName}}');
configure(['{{stories}}'], module, false);

View File

@ -48,17 +48,6 @@ test.describe('addon-backgrounds', () => {
});
test('button should appear for unattached .mdx files', async ({ page }) => {
// SSv6 does not support .mdx files. There is a unattached stories.mdx file
// at /docs/addons-docs-stories-mdx-unattached--docs, but these are functionally
// really attached
// eslint-disable-next-line jest/no-disabled-tests
test.skip(
// eslint-disable-next-line jest/valid-title
templateName.includes('ssv6'),
'Only run this test for Sandboxes with StoryStoreV7 enabled'
);
const sbPage = new SbPage(page);
// We start on the introduction page by default.

View File

@ -1,9 +1,7 @@
/* eslint-disable jest/no-disabled-tests */
import { test, expect } from '@playwright/test';
import process from 'process';
const storybookUrl = process.env.STORYBOOK_URL || 'http://localhost:8001';
const templateName = process.env.STORYBOOK_TEMPLATE_NAME || '';
test.describe('JSON files', () => {
test.beforeEach(async ({ page }) => {
@ -11,11 +9,6 @@ test.describe('JSON files', () => {
});
test('should have index.json', async ({ page }) => {
test.skip(
// eslint-disable-next-line jest/valid-title
templateName.includes('ssv6'),
'Only run this test for Sandboxes with StoryStoreV7 enabled'
);
const json = await page.evaluate(() => fetch('/index.json').then((res) => res.json()));
expect(json).toEqual({

View File

@ -2,9 +2,6 @@
import './globals';
// eslint-disable-next-line import/export
export * from './public-api';
// eslint-disable-next-line import/export
export * from './public-types';
export type { StoryFnAngularReturnType as IStory } from './types';

View File

@ -1 +0,0 @@
export * from './public-types';

View File

@ -31,7 +31,7 @@ export type Meta<TArgs = Args> = ComponentAnnotations<AngularRenderer, Transform
export type StoryFn<TArgs = Args> = AnnotatedStoryFn<AngularRenderer, TransformEventType<TArgs>>;
/**
* Story function that represents a CSFv3 component example.
* Story object that represents a CSFv3 component example.
*
* @see [Named Story exports](https://storybook.js.org/docs/formats/component-story-format/#named-story-exports)
*/

View File

@ -6,7 +6,6 @@ declare var NODE_ENV: string | undefined;
declare var __STORYBOOK_ADDONS_CHANNEL__: any;
declare var __STORYBOOK_ADDONS_PREVIEW: any;
declare var __STORYBOOK_COMPODOC_JSON__: any;
declare var __STORYBOOK_CLIENT_API__: any;
declare var __STORYBOOK_PREVIEW__: any;
declare var __STORYBOOK_STORY_STORE__: any;
declare var CHANNEL_OPTIONS: any;

View File

@ -1 +0,0 @@
import './globals';

View File

@ -58,7 +58,7 @@
"typescript": "^5.3.2"
},
"engines": {
"node": "^14.18 || >=16"
"node": ">=18.0.0"
},
"publishConfig": {
"access": "public"

View File

@ -148,7 +148,7 @@
}
},
"engines": {
"node": ">=16.0.0"
"node": ">=18.0.0"
},
"publishConfig": {
"access": "public"

View File

@ -24,7 +24,7 @@ function Component() {
name: 'Prefetch',
},
{
// @ts-expect-error (a legacy nextjs api?)
// @ts-expect-error (old API)
cb: () => router.push('/push-html', { forceOptimisticNavigation: true }),
name: 'Push HTML',
},
@ -33,7 +33,7 @@ function Component() {
name: 'Refresh',
},
{
// @ts-expect-error (a legacy nextjs api?)
// @ts-expect-error (old API)
cb: () => router.replace('/replaced-html', { forceOptimisticNavigation: true }),
name: 'Replace',
},

View File

@ -60,7 +60,7 @@
"vite": "^4.0.0 || ^5.0.0"
},
"engines": {
"node": ">=16"
"node": ">=18.0.0"
},
"publishConfig": {
"access": "public"

View File

@ -65,7 +65,7 @@
"vite": "^4.0.0 || ^5.0.0"
},
"engines": {
"node": ">=16"
"node": ">=18.0.0"
},
"publishConfig": {
"access": "public"

View File

@ -68,7 +68,7 @@
"vite": "^4.0.0 || ^5.0.0"
},
"engines": {
"node": "^14.18 || >=16"
"node": ">=18.0.0"
},
"publishConfig": {
"access": "public"

View File

@ -68,7 +68,7 @@
"vite": "^4.0.0 || ^5.0.0"
},
"engines": {
"node": "^14.18 || >=16"
"node": ">=18.0.0"
},
"publishConfig": {
"access": "public"

View File

@ -62,7 +62,7 @@
"vite": "^4.0.0 || ^5.0.0"
},
"engines": {
"node": "^14.18 || >=16"
"node": ">=18.0.0"
},
"publishConfig": {
"access": "public"

View File

@ -58,7 +58,7 @@
"typescript": "^5.3.2"
},
"engines": {
"node": "^14.18 || >=16"
"node": ">=18.0.0"
},
"publishConfig": {
"access": "public"

View File

@ -12,11 +12,7 @@ import type { JsPackageManager } from '../js-package-manager';
import { getPackageDetails } from '../js-package-manager';
import packageVersions from '../versions';
import type { FrameworkOptions, GeneratorOptions } from './types';
import {
configureEslintPlugin,
extractEslintInfo,
suggestESLintPlugin,
} from '../automigrate/helpers/eslintPlugin';
import { configureEslintPlugin, extractEslintInfo } from '../automigrate/helpers/eslintPlugin';
import { detectBuilder } from '../detect';
const logger = console;
@ -173,14 +169,7 @@ const hasFrameworkTemplates = (framework?: SupportedFrameworks) =>
export async function baseGenerator(
packageManager: JsPackageManager,
npmOptions: NpmOptions,
{
language,
builder,
pnp,
frameworkPreviewParts,
yes: skipPrompts,
projectType,
}: GeneratorOptions,
{ language, builder, pnp, frameworkPreviewParts, projectType }: GeneratorOptions,
renderer: SupportedRenderers,
options: FrameworkOptions = defaultOptions,
framework?: SupportedFrameworks
@ -318,10 +307,8 @@ export async function baseGenerator(
);
if (hasEslint && !isStorybookPluginInstalled) {
if (skipPrompts || (await suggestESLintPlugin())) {
depsToInstall.push('eslint-plugin-storybook');
await configureEslintPlugin(eslintConfigFile ?? undefined, packageManager);
}
depsToInstall.push('eslint-plugin-storybook');
await configureEslintPlugin(eslintConfigFile ?? undefined, packageManager);
}
}
} catch (err) {

View File

@ -66,7 +66,7 @@ export type Template = {
inDevelopment?: boolean;
/**
* Some sandboxes might need extra modifications in the initialized Storybook,
* such as extend main.js, for setting specific feature flags like storyStoreV7, etc.
* such as extend main.js, for setting specific feature flags.
*/
modifications?: {
skipTemplateStories?: boolean;

View File

@ -29,9 +29,6 @@ const config: StorybookConfig = {
disableTelemetry: true,
},
logLevel: 'debug',
features: {
storyStoreV7: false,
},
framework: {
name: '@storybook/react-webpack5',
options: {

View File

@ -2,7 +2,7 @@ import chalk from 'chalk';
import { copy, emptyDir, ensureDir } from 'fs-extra';
import { dirname, join, relative, resolve } from 'path';
import { global } from '@storybook/global';
import { deprecate, logger } from '@storybook/node-logger';
import { logger } from '@storybook/node-logger';
import { getPrecedingUpgrade, telemetry } from '@storybook/telemetry';
import type { BuilderOptions, CLIOptions, LoadOptions, Options } from '@storybook/types';
import {
@ -13,7 +13,6 @@ import {
resolveAddonName,
} from '@storybook/core-common';
import dedent from 'ts-dedent';
import { outputStats } from './utils/output-stats';
import { copyAllStaticFilesRelativeToMain } from './utils/copy-all-static-files';
import { getBuilders } from './utils/get-builders';
@ -102,13 +101,6 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption
presets.apply('docs', {}),
]);
if (features?.storyStoreV7 === false) {
deprecate(
dedent`storyStoreV6 is deprecated, please migrate to storyStoreV7 instead.
- Refer to the migration guide at https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#storystorev6-and-storiesof-is-deprecated`
);
}
const fullOptions: Options = {
...options,
presets,
@ -138,7 +130,7 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption
let initializedStoryIndexGenerator: Promise<StoryIndexGenerator | undefined> =
Promise.resolve(undefined);
if ((features?.buildStoriesJson || features?.storyStoreV7) && !options.ignorePreview) {
if (!options.ignorePreview) {
const workingDir = process.cwd();
const directories = {
configDir: options.configDir,
@ -149,7 +141,6 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption
...directories,
indexers,
docs: docsOptions,
storyStoreV7: !!features?.storyStoreV7,
build,
});

View File

@ -5,9 +5,8 @@ import invariant from 'tiny-invariant';
import type { Options } from '@storybook/types';
import { logConfig } from '@storybook/core-common';
import { deprecate, logger } from '@storybook/node-logger';
import { logger } from '@storybook/node-logger';
import dedent from 'ts-dedent';
import { MissingBuilderError } from '@storybook/core-events/server-errors';
import { getMiddleware } from './utils/middleware';
import { getServerAddresses } from './utils/server-address';
@ -38,13 +37,6 @@ export async function storybookDevServer(options: Options) {
getServerChannel(server)
);
if (features?.storyStoreV7 === false) {
deprecate(
dedent`storyStoreV6 is deprecated, please migrate to storyStoreV7 instead.
- Refer to the migration guide at https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#storystorev6-and-storiesof-is-deprecated`
);
}
let indexError: Error | undefined;
// try get index generator, if failed, send telemetry without storyCount, then rethrow the error
const initializedStoryIndexGenerator: Promise<StoryIndexGenerator | undefined> =

View File

@ -187,8 +187,6 @@ export const previewAnnotations = async (base: any, options: Options) => {
export const features: PresetProperty<'features'> = async (existing) => ({
...existing,
buildStoriesJson: false,
storyStoreV7: true,
argTypeTargetsV7: true,
legacyDecoratorFileOrder: false,
disallowImplicitActionsInRenderV8: true,

View File

@ -43,7 +43,6 @@ const options: StoryIndexGeneratorOptions = {
configDir: path.join(__dirname, '__mockdata__'),
workingDir: path.join(__dirname, '__mockdata__'),
indexers: [csfIndexer],
storyStoreV7: true,
docs: { defaultName: 'docs', autodocs: false },
};

View File

@ -49,7 +49,6 @@ type SpecifierStoriesCache = Record<Path, CacheEntry>;
export type StoryIndexGeneratorOptions = {
workingDir: Path;
configDir: Path;
storyStoreV7: boolean;
indexers: Indexer[];
docs: DocsOptions;
build?: StorybookConfigRaw['build'];
@ -346,11 +345,6 @@ export class StoryIndexGenerator {
async extractDocs(specifier: NormalizedStoriesSpecifier, absolutePath: Path) {
const relativePath = path.relative(this.options.workingDir, absolutePath);
try {
invariant(
this.options.storyStoreV7,
`You cannot use \`.mdx\` files without using \`storyStoreV7\`.`
);
const normalizedPath = normalizeStoryPath(relativePath);
const importPath = slash(normalizedPath);
@ -530,13 +524,9 @@ export class StoryIndexGenerator {
async sortStories(entries: StoryIndex['entries']) {
const sortableStories = Object.values(entries);
// Skip sorting if we're in v6 mode because we don't have
// all the info we need here
if (this.options.storyStoreV7) {
const storySortParameter = await this.getStorySortParameter();
const fileNameOrder = this.storyFileNames();
sortStoriesV7(sortableStories, storySortParameter, fileNameOrder);
}
const storySortParameter = await this.getStorySortParameter();
const fileNameOrder = this.storyFileNames();
sortStoriesV7(sortableStories, storySortParameter, fileNameOrder);
return sortableStories.reduce((acc, item) => {
acc[item.id] = item;

View File

@ -16,7 +16,6 @@ const options: StoryIndexGeneratorOptions = {
configDir: path.join(__dirname, '..', '__mockdata__'),
workingDir: path.join(__dirname, '..', '__mockdata__'),
indexers: [],
storyStoreV7: true,
docs: { defaultName: 'docs', autodocs: false },
};

View File

@ -7,16 +7,11 @@ import { router } from './router';
export async function getStoryIndexGenerator(
features: {
buildStoriesJson?: boolean;
storyStoreV7?: boolean;
argTypeTargetsV7?: boolean;
},
options: Options,
serverChannel: ServerChannel
): Promise<StoryIndexGenerator | undefined> {
if (!features?.buildStoriesJson && !features?.storyStoreV7) {
return undefined;
}
const workingDir = process.cwd();
const directories = {
configDir: options.configDir,
@ -32,7 +27,6 @@ export async function getStoryIndexGenerator(
indexers: await indexers,
docs: await docsOptions,
workingDir,
storyStoreV7: features.storyStoreV7 ?? false,
});
const initializedStoryIndexGenerator = generator.initialize().then(() => generator);

View File

@ -45,7 +45,6 @@ const getInitializedStoryIndexGenerator = async (
indexers: [csfIndexer],
configDir: workingDir,
workingDir,
storyStoreV7: true,
docs: { defaultName: 'docs', autodocs: false },
...overrides,
};
@ -252,35 +251,6 @@ describe('useStoriesJson', () => {
`);
}, 20_000);
it('disallows .mdx files without storyStoreV7', async () => {
const mockServerChannel = { emit: vi.fn() } as any as ServerChannel;
useStoriesJson({
router,
initializedStoryIndexGenerator: getInitializedStoryIndexGenerator({
storyStoreV7: false,
}),
workingDir,
serverChannel: mockServerChannel,
normalizedStories,
});
expect(use).toHaveBeenCalledTimes(1);
const route = use.mock.calls[0][1];
await route(request, response);
expect(send).toHaveBeenCalledTimes(1);
expect(send.mock.calls[0][0]).toMatchInlineSnapshot(`
"Unable to index files:
- ./src/docs2/ComponentReference.mdx: Invariant failed: You cannot use \`.mdx\` files without using \`storyStoreV7\`.
- ./src/docs2/MetaOf.mdx: Invariant failed: You cannot use \`.mdx\` files without using \`storyStoreV7\`.
- ./src/docs2/NoTitle.mdx: Invariant failed: You cannot use \`.mdx\` files without using \`storyStoreV7\`.
- ./src/docs2/SecondMetaOf.mdx: Invariant failed: You cannot use \`.mdx\` files without using \`storyStoreV7\`.
- ./src/docs2/Template.mdx: Invariant failed: You cannot use \`.mdx\` files without using \`storyStoreV7\`.
- ./src/docs2/Title.mdx: Invariant failed: You cannot use \`.mdx\` files without using \`storyStoreV7\`."
`);
});
it('can handle simultaneous access', async () => {
const mockServerChannel = { emit: vi.fn() } as any as ServerChannel;

View File

@ -455,8 +455,7 @@ export class CsfFile {
throw new Error(dedent`
Unexpected \`storiesOf\` usage: ${formatLocation(node, self._fileName)}.
In SB7, we use the next-generation \`storyStoreV7\` by default, which does not support \`storiesOf\`.
More info, with details about how to opt-out here: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#storystorev7-enabled-by-default
SB8 does not support \`storiesOf\`.
`);
}
},

View File

@ -450,6 +450,31 @@ export default {
]
`);
});
it('storysort satisfies inline', () => {
expect(
getStorySortParameter(dedent`
enum ComponentGroups {
General = 'General'
}
export default {
parameters: {
options: {
storySort: {
order: ['General'] satisfies ComponentGroups[]
}
}
}
};
`)
).toMatchInlineSnapshot(`
{
"order": [
"General",
],
}
`);
});
});
describe('unsupported', () => {
it('bad default export', () => {

View File

@ -19,7 +19,9 @@ const getValue = (obj: t.ObjectExpression, key: string) => {
return value;
};
const parseValue = (expr: t.Expression): any => {
const parseValue = (value: t.Expression): any => {
const expr = stripTSModifiers(value);
if (t.isArrayExpression(expr)) {
return (expr.elements as t.Expression[]).map((o) => {
return parseValue(o);

View File

@ -58,7 +58,7 @@ import {
import type { ComposedRef } from '../index';
import type { ModuleFn } from '../lib/types';
const { FEATURES, fetch } = global;
const { fetch } = global;
const STORY_INDEX_PATH = './index.json';
type Direction = -1 | 1;
@ -881,10 +881,8 @@ export const init: ModuleFn<SubAPI, SubState> = ({
filters: config?.sidebar?.filters || {},
},
init: async () => {
if (FEATURES?.storyStoreV7) {
provider.channel?.on(STORY_INDEX_INVALIDATED, () => api.fetchIndex());
await api.fetchIndex();
}
provider.channel?.on(STORY_INDEX_INVALIDATED, () => api.fetchIndex());
await api.fetchIndex();
},
};
};

View File

@ -39,7 +39,6 @@ vi.mock('@storybook/global', () => ({
global: {
...globalThis,
fetch: vi.fn(() => ({ json: () => ({ v: 4, entries: mockGetEntries() }) })),
FEATURES: { storyStoreV7: true },
CONFIG_TYPE: 'DEVELOPMENT',
},
}));

View File

@ -10,34 +10,14 @@ The preview's job is:
3. Render the current selection to the web view in either story or docs mode.
## V7 Store vs Legacy (V6)
The story store is designed to load stories 'on demand', and will operate in this fashion if the `storyStoreV7` feature is enabled.
However, for back-compat reasons, in v6 mode, we need to load all stories, synchronously on bootup, emitting the `SET_STORIES` event.
In V7 mode we do not emit that event, instead preferring the `STORY_PREPARED` event, with the data for the single story being rendered.
## Initialization
The preview is `initialized` in two ways.
### V7 Mode:
- `importFn` - is an async `import()` function
- `getProjectAnnotations` - is a simple function that evaluations `preview.js` and addon config files and combines them. If it errors, the Preview will show the error.
- No `getStoryIndex` function is passed, instead the preview creates a `StoryIndexClient` that pulls `stories.json` from node and watches the event stream for invalidation events.
### V6 Mode
- `importFn` - is a simulated `import()` function, that is synchronous, see `client-api` for details.
- `getProjectAnnotations` - also evaluates `preview.js` et al, but watches for calls to `setStories`, and passes them to the `ClientApi`
- `getStoryIndex` is a local function (that must be called _after_ `getProjectAnnotations`) that gets the list of stories added.
See `client-api` for more details on this process.
## Story Rendering and interruptions
The Preview is split into three parts responsible for state management:

View File

@ -54,7 +54,6 @@
"lodash": "^4.17.21",
"memoizerific": "^1.11.3",
"qs": "^6.10.0",
"synchronous-promise": "^2.0.15",
"ts-dedent": "^2.0.0",
"util-deprecate": "^1.0.2"
},

View File

@ -1,4 +0,0 @@
/* eslint-disable @typescript-eslint/triple-slash-reference */
/// <reference path="typings.d.ts" />
export * from './modules/client-api';

View File

@ -1,4 +0,0 @@
/* eslint-disable @typescript-eslint/triple-slash-reference */
/// <reference path="typings.d.ts" />
export * from './modules/core-client';

View File

@ -41,22 +41,6 @@ export { DocsContext } from './preview-web';
*/
export { simulatePageLoad, simulateDOMContentLoaded } from './preview-web';
/**
* STORIES API
*/
export {
addArgTypes,
addArgTypesEnhancer,
addArgs,
addArgsEnhancer,
addDecorator,
addLoader,
addParameters,
addStepRunner,
} from './client-api';
export { getQueryParam, getQueryParams } from './client-api';
export { setGlobalRender } from './client-api';
export {
combineArgs,
combineParameters,
@ -83,8 +67,5 @@ export type { PropDescriptor } from './store';
/**
* STORIES API
*/
export { ClientApi } from './client-api';
export { StoryStore } from './store';
export { Preview, PreviewWeb, PreviewWithSelection, UrlStore, WebView } from './preview-web';
export type { SelectionStore, View } from './preview-web';
export { start } from './core-client';
export { Preview, PreviewWeb } from './preview-web';

View File

@ -1,214 +0,0 @@
/* eslint-disable no-underscore-dangle */
import { dedent } from 'ts-dedent';
import { global } from '@storybook/global';
import type {
Args,
StepRunner,
ArgTypes,
Renderer,
DecoratorFunction,
Parameters,
ArgTypesEnhancer,
ArgsEnhancer,
LoaderFunction,
Globals,
GlobalTypes,
Path,
ModuleImportFn,
ModuleExports,
} from '@storybook/types';
import type { StoryStore } from '../../store';
import { combineParameters, composeStepRunners, normalizeInputTypes } from '../../store';
import { StoryStoreFacade } from './StoryStoreFacade';
const warningAlternatives = {
addDecorator: `Instead, use \`export const decorators = [];\` in your \`preview.js\`.`,
addParameters: `Instead, use \`export const parameters = {};\` in your \`preview.js\`.`,
addLoader: `Instead, use \`export const loaders = [];\` in your \`preview.js\`.`,
addArgs: '',
addArgTypes: '',
addArgsEnhancer: '',
addArgTypesEnhancer: '',
addStepRunner: '',
getGlobalRender: '',
setGlobalRender: '',
};
const checkMethod = (method: keyof typeof warningAlternatives) => {
if (global.FEATURES?.storyStoreV7) {
throw new Error(
dedent`You cannot use \`${method}\` with the new Story Store.
${warningAlternatives[method]}`
);
}
if (!global.__STORYBOOK_CLIENT_API__) {
throw new Error(`Singleton client API not yet initialized, cannot call \`${method}\`.`);
}
};
export const addDecorator = (decorator: DecoratorFunction<Renderer>) => {
checkMethod('addDecorator');
global.__STORYBOOK_CLIENT_API__?.addDecorator(decorator);
};
export const addParameters = (parameters: Parameters) => {
checkMethod('addParameters');
global.__STORYBOOK_CLIENT_API__?.addParameters(parameters);
};
export const addLoader = (loader: LoaderFunction<Renderer>) => {
checkMethod('addLoader');
global.__STORYBOOK_CLIENT_API__?.addLoader(loader);
};
export const addArgs = (args: Args) => {
checkMethod('addArgs');
global.__STORYBOOK_CLIENT_API__?.addArgs(args);
};
export const addArgTypes = (argTypes: ArgTypes) => {
checkMethod('addArgTypes');
global.__STORYBOOK_CLIENT_API__?.addArgTypes(argTypes);
};
export const addArgsEnhancer = (enhancer: ArgsEnhancer<Renderer>) => {
checkMethod('addArgsEnhancer');
global.__STORYBOOK_CLIENT_API__?.addArgsEnhancer(enhancer);
};
export const addArgTypesEnhancer = (enhancer: ArgTypesEnhancer<Renderer>) => {
checkMethod('addArgTypesEnhancer');
global.__STORYBOOK_CLIENT_API__?.addArgTypesEnhancer(enhancer);
};
export const addStepRunner = (stepRunner: StepRunner) => {
checkMethod('addStepRunner');
global.__STORYBOOK_CLIENT_API__?.addStepRunner(stepRunner);
};
export const getGlobalRender = () => {
checkMethod('getGlobalRender');
return global.__STORYBOOK_CLIENT_API__?.facade.projectAnnotations.render;
};
export const setGlobalRender = (render: StoryStoreFacade<any>['projectAnnotations']['render']) => {
checkMethod('setGlobalRender');
if (global.__STORYBOOK_CLIENT_API__) {
global.__STORYBOOK_CLIENT_API__.facade.projectAnnotations.render = render;
}
};
export class ClientApi<TRenderer extends Renderer> {
facade: StoryStoreFacade<TRenderer>;
storyStore?: StoryStore<TRenderer>;
onImportFnChanged?: ({ importFn }: { importFn: ModuleImportFn }) => void;
// If we don't get passed modules so don't know filenames, we can
// just use numeric indexes
constructor({ storyStore }: { storyStore?: StoryStore<TRenderer> } = {}) {
this.facade = new StoryStoreFacade();
this.storyStore = storyStore;
}
importFn(path: Path) {
return this.facade.importFn(path);
}
getStoryIndex() {
if (!this.storyStore) {
throw new Error('Cannot get story index before setting storyStore');
}
return this.facade.getStoryIndex(this.storyStore);
}
addDecorator = (decorator: DecoratorFunction<TRenderer>) => {
this.facade.projectAnnotations.decorators?.push(decorator);
};
addParameters = ({
globals,
globalTypes,
...parameters
}: Parameters & { globals?: Globals; globalTypes?: GlobalTypes }) => {
this.facade.projectAnnotations.parameters = combineParameters(
this.facade.projectAnnotations.parameters,
parameters
);
if (globals) {
this.facade.projectAnnotations.globals = {
...this.facade.projectAnnotations.globals,
...globals,
};
}
if (globalTypes) {
this.facade.projectAnnotations.globalTypes = {
...this.facade.projectAnnotations.globalTypes,
...normalizeInputTypes(globalTypes),
};
}
};
addStepRunner = (stepRunner: StepRunner<TRenderer>) => {
this.facade.projectAnnotations.runStep = composeStepRunners(
[this.facade.projectAnnotations.runStep, stepRunner].filter(
Boolean
) as StepRunner<TRenderer>[]
);
};
addLoader = (loader: LoaderFunction<TRenderer>) => {
this.facade.projectAnnotations.loaders?.push(loader);
};
addArgs = (args: Args) => {
this.facade.projectAnnotations.args = {
...this.facade.projectAnnotations.args,
...args,
};
};
addArgTypes = (argTypes: ArgTypes) => {
this.facade.projectAnnotations.argTypes = {
...this.facade.projectAnnotations.argTypes,
...normalizeInputTypes(argTypes),
};
};
addArgsEnhancer = (enhancer: ArgsEnhancer<TRenderer>) => {
this.facade.projectAnnotations.argsEnhancers?.push(enhancer);
};
addArgTypesEnhancer = (enhancer: ArgTypesEnhancer<TRenderer>) => {
this.facade.projectAnnotations.argTypesEnhancers?.push(enhancer);
};
// Because of the API of `storiesOf().add()` we don't have a good "end" call for a
// storiesOf file to finish adding stories, and us to load it into the facade as a
// single psuedo-CSF file. So instead we just keep collecting the CSF files and load
// them all into the facade at the end.
_addedExports = {} as Record<Path, ModuleExports>;
_loadAddedExports() {
Object.entries(this._addedExports).forEach(([fileName, fileExports]) =>
this.facade.addStoriesFromExports(fileName, fileExports)
);
}
// @deprecated
raw = () => {
return this.storyStore?.raw();
};
// @deprecated
get _storyStore() {
return this.storyStore;
}
}

View File

@ -1,256 +0,0 @@
/* eslint-disable no-underscore-dangle */
import { global } from '@storybook/global';
import { dedent } from 'ts-dedent';
import { SynchronousPromise } from 'synchronous-promise';
import { toId, isExportStory, storyNameFromExport } from '@storybook/csf';
import type {
IndexEntry,
Renderer,
ComponentId,
DocsOptions,
Parameters,
Path,
ModuleExports,
NormalizedProjectAnnotations,
NormalizedStoriesSpecifier,
PreparedStory,
StoryIndex,
StoryId,
} from '@storybook/types';
import { logger } from '@storybook/client-logger';
import type { StoryStore } from '../../store';
import { userOrAutoTitle, sortStoriesV6 } from '../../store';
export const AUTODOCS_TAG = 'autodocs';
export const STORIES_MDX_TAG = 'stories-mdx';
export class StoryStoreFacade<TRenderer extends Renderer> {
projectAnnotations: NormalizedProjectAnnotations<TRenderer>;
entries: Record<StoryId, IndexEntry & { componentId?: ComponentId }>;
csfExports: Record<Path, ModuleExports>;
constructor() {
this.projectAnnotations = {
loaders: [],
decorators: [],
parameters: {},
argsEnhancers: [],
argTypesEnhancers: [],
args: {},
argTypes: {},
};
this.entries = {};
this.csfExports = {};
}
// This doesn't actually import anything because the client-api loads fully
// on startup, but this is a shim after all.
importFn(path: Path) {
return SynchronousPromise.resolve().then(() => {
const moduleExports = this.csfExports[path];
if (!moduleExports) throw new Error(`Unknown path: ${path}`);
return moduleExports;
});
}
getStoryIndex(store: StoryStore<TRenderer>) {
const fileNameOrder = Object.keys(this.csfExports);
const storySortParameter = this.projectAnnotations.parameters?.options?.storySort;
const storyEntries = Object.entries(this.entries);
// Add the kind parameters and global parameters to each entry
const sortableV6 = storyEntries.map(([storyId, { type, importPath, ...entry }]) => {
const exports = this.csfExports[importPath];
const csfFile = store.processCSFFileWithCache<TRenderer>(
exports,
importPath,
exports.default.title
);
let storyLike: PreparedStory<TRenderer>;
if (type === 'story') {
storyLike = store.storyFromCSFFile({ storyId, csfFile });
} else {
storyLike = {
...entry,
story: entry.name,
kind: entry.title,
componentId: toId(entry.componentId || entry.title),
parameters: { fileName: importPath },
} as any;
}
return [
storyId,
storyLike,
csfFile.meta.parameters,
this.projectAnnotations.parameters || {},
] as [StoryId, PreparedStory<TRenderer>, Parameters, Parameters];
});
// NOTE: the sortStoriesV6 version returns the v7 data format. confusing but more convenient!
let sortedV7: IndexEntry[];
try {
sortedV7 = sortStoriesV6(sortableV6, storySortParameter, fileNameOrder);
} catch (err: any) {
if (typeof storySortParameter === 'function') {
throw new Error(dedent`
Error sorting stories with sort parameter ${storySortParameter}:
> ${err.message}
Are you using a V7-style sort function in V6 compatibility mode?
More info: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#v7-style-story-sort
`);
}
throw err;
}
const entries = sortedV7.reduce((acc, s) => {
// We use the original entry we stored in `this.stories` because it is possible that the CSF file itself
// exports a `parameters.fileName` which can be different and mess up our `importFn`.
// NOTE: this doesn't actually change the story object, just the index.
acc[s.id] = this.entries[s.id];
return acc;
}, {} as StoryIndex['entries']);
return { v: 4, entries };
}
clearFilenameExports(fileName: Path) {
if (!this.csfExports[fileName]) {
return;
}
// Clear this module's stories from the storyList and existing exports
Object.entries(this.entries).forEach(([id, { importPath }]) => {
if (importPath === fileName) {
delete this.entries[id];
}
});
// We keep this as an empty record so we can use it to maintain component order
this.csfExports[fileName] = {};
}
// NOTE: we could potentially share some of this code with the stories.json generation
addStoriesFromExports(fileName: Path, fileExports: ModuleExports) {
if (fileName.match(/\.mdx$/) && !fileName.match(/\.stories\.mdx$/)) {
if (global.FEATURES?.storyStoreV7MdxErrors !== false) {
throw new Error(dedent`
Cannot index \`.mdx\` file (\`${fileName}\`) in \`storyStoreV7: false\` mode.
The legacy story store does not support new-style \`.mdx\` files. If the file above
is not intended to be indexed (i.e. displayed as an entry in the sidebar), either
exclude it from your \`stories\` glob, or add <Meta isTemplate /> to it.
If you wanted to index the file, you'll need to name it \`stories.mdx\` and stick to the
legacy (6.x) MDX API, or use the new store.`);
}
}
// if the export haven't changed since last time we added them, this is a no-op
if (this.csfExports[fileName] === fileExports) {
return;
}
// OTOH, if they have changed, let's clear them out first
this.clearFilenameExports(fileName);
// eslint-disable-next-line @typescript-eslint/naming-convention
const { default: defaultExport, __namedExportsOrder, ...namedExports } = fileExports;
// eslint-disable-next-line prefer-const
let { id: componentId, title, tags: componentTags = [] } = defaultExport || {};
const specifiers = (global.STORIES || []).map(
(specifier: NormalizedStoriesSpecifier & { importPathMatcher: string }) => ({
...specifier,
importPathMatcher: new RegExp(specifier.importPathMatcher),
})
);
title = userOrAutoTitle(fileName, specifiers, title);
if (!title) {
logger.info(
`Unexpected default export without title in '${fileName}': ${JSON.stringify(
fileExports.default
)}`
);
return;
}
this.csfExports[fileName] = {
...fileExports,
default: { ...defaultExport, title },
};
let sortedExports = namedExports;
// prefer a user/loader provided `__namedExportsOrder` array if supplied
// we do this as es module exports are always ordered alphabetically
// see https://github.com/storybookjs/storybook/issues/9136
if (Array.isArray(__namedExportsOrder)) {
sortedExports = {};
__namedExportsOrder.forEach((name) => {
const namedExport = namedExports[name];
if (namedExport) sortedExports[name] = namedExport;
});
}
const storyExports = Object.entries(sortedExports).filter(([key]) =>
isExportStory(key, defaultExport)
);
// NOTE: this logic is equivalent to the `extractStories` function of `StoryIndexGenerator`
const docsOptions = (global.DOCS_OPTIONS || {}) as DocsOptions;
const { autodocs } = docsOptions;
const componentAutodocs = componentTags.includes(AUTODOCS_TAG);
const autodocsOptedIn = autodocs === true || (autodocs === 'tag' && componentAutodocs);
if (storyExports.length) {
if (componentTags.includes(STORIES_MDX_TAG) || autodocsOptedIn) {
const name = docsOptions.defaultName;
const docsId = toId(componentId || title, name);
this.entries[docsId] = {
type: 'docs',
id: docsId,
title,
name,
importPath: fileName,
...(componentId && { componentId }),
tags: [
...componentTags,
'docs',
...(autodocsOptedIn && !componentAutodocs ? [AUTODOCS_TAG] : []),
],
storiesImports: [],
};
}
}
storyExports.forEach(([key, storyExport]: [string, any]) => {
const exportName = storyNameFromExport(key);
const id = storyExport.parameters?.__id || toId(componentId || title, exportName);
const name =
(typeof storyExport !== 'function' && storyExport.name) ||
storyExport.storyName ||
storyExport.story?.name ||
exportName;
if (!storyExport.parameters?.docsOnly) {
this.entries[id] = {
type: 'story',
id,
name,
title,
importPath: fileName,
...(componentId && { componentId }),
tags: [...(storyExport.tags || componentTags), 'story'],
};
}
});
}
}

View File

@ -1,16 +0,0 @@
export {
addArgs,
addArgsEnhancer,
addArgTypes,
addArgTypesEnhancer,
addDecorator,
addLoader,
addParameters,
addStepRunner,
ClientApi,
setGlobalRender,
} from './ClientApi';
export * from '../../store';
export * from './queryparams';

View File

@ -1,17 +0,0 @@
import { global } from '@storybook/global';
import { parse } from 'qs';
export const getQueryParams = () => {
const { document } = global;
// document.location is not defined in react-native
if (document && document.location && document.location.search) {
return parse(document.location.search, { ignoreQueryPrefix: true });
}
return {};
};
export const getQueryParam = (key: string) => {
const params = getQueryParams();
return params[key];
};

View File

@ -1,110 +0,0 @@
/// <reference types="node" />
/// <reference types="webpack-env" />
import { logger } from '@storybook/client-logger';
import type { Path, ModuleExports } from '@storybook/types';
export interface RequireContext {
keys: () => string[];
(id: string): any;
resolve(id: string): string;
}
export type LoaderFunction = () => void | any[];
export type Loadable = RequireContext | RequireContext[] | LoaderFunction;
/**
* Executes a Loadable (function that returns exports or require context(s))
* and returns a map of filename => module exports
*
* @param loadable Loadable
* @returns Map<Path, ModuleExports>
*/
export function executeLoadable(loadable: Loadable) {
let reqs = null;
// todo discuss / improve type check
if (Array.isArray(loadable)) {
reqs = loadable;
} else if ((loadable as RequireContext).keys) {
reqs = [loadable as RequireContext];
}
let exportsMap = new Map<Path, ModuleExports>();
if (reqs) {
reqs.forEach((req) => {
req.keys().forEach((filename: string) => {
try {
const fileExports = req(filename) as ModuleExports;
exportsMap.set(
typeof req.resolve === 'function' ? req.resolve(filename) : filename,
fileExports
);
} catch (error: any) {
const errorString =
error.message && error.stack ? `${error.message}\n ${error.stack}` : error.toString();
logger.error(`Unexpected error while loading ${filename}: ${errorString}`);
}
});
});
} else {
const exported = (loadable as LoaderFunction)();
if (Array.isArray(exported) && exported.every((obj) => obj.default != null)) {
exportsMap = new Map(
exported.map((fileExports, index) => [`exports-map-${index}`, fileExports])
);
} else if (exported) {
logger.warn(
`Loader function passed to 'configure' should return void or an array of module exports that all contain a 'default' export. Received: ${JSON.stringify(
exported
)}`
);
}
}
return exportsMap;
}
/**
* Executes a Loadable (function that returns exports or require context(s))
* and compares it's output to the last time it was run (as stored on a node module)
*
* @param loadable Loadable
* @param m NodeModule
* @returns { added: Map<Path, ModuleExports>, removed: Map<Path, ModuleExports> }
*/
export function executeLoadableForChanges(loadable: Loadable, m?: NodeModule) {
let lastExportsMap: ReturnType<typeof executeLoadable> =
m?.hot?.data?.lastExportsMap || new Map();
if (m?.hot?.dispose) {
m.hot.accept();
m.hot.dispose((data) => {
// eslint-disable-next-line no-param-reassign
data.lastExportsMap = lastExportsMap;
});
}
const exportsMap = executeLoadable(loadable);
const added = new Map<Path, ModuleExports>();
Array.from(exportsMap.entries())
// Ignore files that do not have a default export
.filter(([, fileExports]) => !!fileExports.default)
// Ignore exports that are equal (by reference) to last time, this means the file hasn't changed
.filter(([fileName, fileExports]) => lastExportsMap.get(fileName) !== fileExports)
.forEach(([fileName, fileExports]) => added.set(fileName, fileExports));
const removed = new Map<Path, ModuleExports>();
Array.from(lastExportsMap.keys())
.filter((fileName) => !exportsMap.has(fileName))
.forEach((fileName) => {
const value = lastExportsMap.get(fileName);
if (value) {
removed.set(fileName, value);
}
});
// Save the value for the dispose() call above
lastExportsMap = exportsMap;
return { added, removed };
}

View File

@ -1,5 +0,0 @@
import { ClientApi } from '../../client-api';
import { StoryStore } from '../../store';
import { start } from './start';
export { start, ClientApi, StoryStore };

View File

@ -1,627 +0,0 @@
/* eslint-disable no-underscore-dangle */
/**
* @vitest-environment jsdom
*/
import { describe, beforeEach, afterEach, it, expect, vi } from 'vitest';
import { STORY_RENDERED, STORY_UNCHANGED, SET_INDEX, CONFIG_ERROR } from '@storybook/core-events';
import type { ModuleExports, Path } from '@storybook/types';
import { global } from '@storybook/global';
import { setGlobalRender } from '../../client-api';
import {
waitForRender,
waitForEvents,
waitForQuiescence,
emitter,
mockChannel,
} from '../preview-web/PreviewWeb.mockdata';
import { start as realStart } from './start';
import type { Loadable } from './executeLoadable';
vi.mock('@storybook/global', () => ({
global: {
...globalThis,
window: globalThis,
history: { replaceState: vi.fn() },
document: {
location: {
pathname: 'pathname',
search: '?id=*',
},
},
DOCS_OPTIONS: {},
},
}));
// console.log(global);
vi.mock('@storybook/channels', () => ({
createBrowserChannel: () => mockChannel,
}));
vi.mock('@storybook/client-logger');
vi.mock('react-dom');
// for the auto-title test
vi.mock('../../store', async (importOriginal) => {
return {
...(await importOriginal<typeof import('../../store')>()),
userOrAutoTitle: (importPath: Path, specifier: any, userTitle?: string) =>
userTitle || 'auto-title',
};
});
vi.mock('../../preview-web', async (importOriginal) => {
const actualPreviewWeb = await importOriginal<typeof import('../../preview-web')>();
class OverloadPreviewWeb extends actualPreviewWeb.PreviewWeb<any> {
constructor() {
super();
// @ts-expect-error (incomplete)
this.view = {
...Object.fromEntries(
Object.getOwnPropertyNames(this.view.constructor.prototype).map((key) => [key, vi.fn()])
),
prepareForDocs: vi.fn().mockReturnValue('docs-root'),
prepareForStory: vi.fn().mockReturnValue('story-root'),
};
}
}
return {
...actualPreviewWeb,
PreviewWeb: OverloadPreviewWeb,
};
});
beforeEach(() => {
mockChannel.emit.mockClear();
// Preview doesn't clean itself up as it isn't designed to ever be stopped :shrug:
emitter.removeAllListeners();
});
const start: typeof realStart = (...args) => {
const result = realStart(...args);
const configure: typeof result['configure'] = (
framework: string,
loadable: Loadable,
m?: NodeModule,
disableBackwardCompatibility = false
) => result.configure(framework, loadable, m, disableBackwardCompatibility);
return {
...result,
configure,
};
};
afterEach(() => {
// I'm not sure why this is required (it seems just afterEach is required really)
mockChannel.emit.mockClear();
});
function makeRequireContext(importMap: Record<Path, ModuleExports>) {
const req = (path: Path) => importMap[path];
req.keys = () => Object.keys(importMap);
return req;
}
describe('start', () => {
beforeEach(() => {
global.DOCS_OPTIONS = {};
// @ts-expect-error (setting this to undefined is indeed what we want to do)
global.__STORYBOOK_CLIENT_API__ = undefined;
// @ts-expect-error (setting this to undefined is indeed what we want to do)
global.__STORYBOOK_PREVIEW__ = undefined;
// @ts-expect-error (setting this to undefined is indeed what we want to do)
global.IS_STORYBOOK = undefined;
});
const componentCExports = {
default: {
title: 'Component C',
tags: ['component-tag', 'autodocs'],
},
StoryOne: {
render: vi.fn(),
tags: ['story-tag'],
},
StoryTwo: vi.fn(),
};
describe('when configure is called with CSF only', () => {
it('loads and renders the first story correctly', async () => {
const renderToCanvas = vi.fn();
const { configure } = start(renderToCanvas);
configure('test', () => [componentCExports]);
await waitForRender();
expect(mockChannel.emit.mock.calls.find((call) => call[0] === SET_INDEX)?.[1])
.toMatchInlineSnapshot(`
{
"entries": {
"component-c--story-one": {
"argTypes": {},
"args": {},
"id": "component-c--story-one",
"importPath": "exports-map-0",
"initialArgs": {},
"name": "Story One",
"parameters": {
"__isArgsStory": false,
"fileName": "exports-map-0",
"renderer": "test",
},
"tags": [
"story-tag",
"story",
],
"title": "Component C",
"type": "story",
},
"component-c--story-two": {
"argTypes": {},
"args": {},
"id": "component-c--story-two",
"importPath": "exports-map-0",
"initialArgs": {},
"name": "Story Two",
"parameters": {
"__isArgsStory": false,
"fileName": "exports-map-0",
"renderer": "test",
},
"tags": [
"component-tag",
"autodocs",
"story",
],
"title": "Component C",
"type": "story",
},
},
"v": 4,
}
`);
await waitForRender();
expect(mockChannel.emit).toHaveBeenCalledWith(STORY_RENDERED, 'component-c--story-one');
expect(renderToCanvas).toHaveBeenCalledWith(
expect.objectContaining({
id: 'component-c--story-one',
}),
'story-root'
);
});
it('supports HMR when a story file changes', async () => {
const renderToCanvas = vi.fn(({ storyFn }) => storyFn());
let disposeCallback: (data: object) => void = () => {};
const module = {
id: 'file1',
hot: {
data: {},
accept: vi.fn(),
dispose(cb: () => void) {
disposeCallback = cb;
},
},
};
const { configure } = start(renderToCanvas);
configure('test', () => [componentCExports], module as any);
await waitForRender();
expect(mockChannel.emit).toHaveBeenCalledWith(STORY_RENDERED, 'component-c--story-one');
expect(componentCExports.StoryOne.render).toHaveBeenCalled();
expect(module.hot.accept).toHaveBeenCalled();
expect(disposeCallback).toBeDefined();
mockChannel.emit.mockClear();
disposeCallback(module.hot.data);
const secondImplementation = vi.fn();
configure(
'test',
() => [{ ...componentCExports, StoryOne: secondImplementation }],
module as any
);
await waitForRender();
expect(mockChannel.emit).toHaveBeenCalledWith(STORY_RENDERED, 'component-c--story-one');
expect(secondImplementation).toHaveBeenCalled();
});
it('re-emits SET_INDEX when a story is added', async () => {
const renderToCanvas = vi.fn(({ storyFn }) => storyFn());
let disposeCallback: (data: object) => void = () => {};
const module = {
id: 'file1',
hot: {
data: {},
accept: vi.fn(),
dispose(cb: () => void) {
disposeCallback = cb;
},
},
};
const { configure } = start(renderToCanvas);
configure('test', () => [componentCExports], module as any);
await waitForRender();
mockChannel.emit.mockClear();
disposeCallback(module.hot.data);
configure('test', () => [{ ...componentCExports, StoryThree: vi.fn() }], module as any);
await waitForEvents([SET_INDEX]);
expect(mockChannel.emit.mock.calls.find((call) => call[0] === SET_INDEX)?.[1])
.toMatchInlineSnapshot(`
{
"entries": {
"component-c--story-one": {
"argTypes": {},
"args": {},
"id": "component-c--story-one",
"importPath": "exports-map-0",
"initialArgs": {},
"name": "Story One",
"parameters": {
"__isArgsStory": false,
"fileName": "exports-map-0",
"renderer": "test",
},
"tags": [
"story-tag",
"story",
],
"title": "Component C",
"type": "story",
},
"component-c--story-three": {
"argTypes": {},
"args": {},
"id": "component-c--story-three",
"importPath": "exports-map-0",
"initialArgs": {},
"name": "Story Three",
"parameters": {
"__isArgsStory": false,
"fileName": "exports-map-0",
"renderer": "test",
},
"tags": [
"component-tag",
"autodocs",
"story",
],
"title": "Component C",
"type": "story",
},
"component-c--story-two": {
"argTypes": {},
"args": {},
"id": "component-c--story-two",
"importPath": "exports-map-0",
"initialArgs": {},
"name": "Story Two",
"parameters": {
"__isArgsStory": false,
"fileName": "exports-map-0",
"renderer": "test",
},
"tags": [
"component-tag",
"autodocs",
"story",
],
"title": "Component C",
"type": "story",
},
},
"v": 4,
}
`);
});
it('re-emits SET_INDEX when a story file is removed', async () => {
const renderToCanvas = vi.fn(({ storyFn }) => storyFn());
let disposeCallback: (data: object) => void = () => {};
const module = {
id: 'file1',
hot: {
data: {},
accept: vi.fn(),
dispose(cb: () => void) {
disposeCallback = cb;
},
},
};
const { configure } = start(renderToCanvas);
configure(
'test',
() => [componentCExports, { default: { title: 'Component D' }, StoryFour: vi.fn() }],
module as any
);
await waitForEvents([SET_INDEX]);
expect(mockChannel.emit.mock.calls.find((call) => call[0] === SET_INDEX)?.[1])
.toMatchInlineSnapshot(`
{
"entries": {
"component-c--story-one": {
"argTypes": {},
"args": {},
"id": "component-c--story-one",
"importPath": "exports-map-0",
"initialArgs": {},
"name": "Story One",
"parameters": {
"__isArgsStory": false,
"fileName": "exports-map-0",
"renderer": "test",
},
"tags": [
"story-tag",
"story",
],
"title": "Component C",
"type": "story",
},
"component-c--story-two": {
"argTypes": {},
"args": {},
"id": "component-c--story-two",
"importPath": "exports-map-0",
"initialArgs": {},
"name": "Story Two",
"parameters": {
"__isArgsStory": false,
"fileName": "exports-map-0",
"renderer": "test",
},
"tags": [
"component-tag",
"autodocs",
"story",
],
"title": "Component C",
"type": "story",
},
"component-d--story-four": {
"argTypes": {},
"args": {},
"id": "component-d--story-four",
"importPath": "exports-map-1",
"initialArgs": {},
"name": "Story Four",
"parameters": {
"__isArgsStory": false,
"fileName": "exports-map-1",
"renderer": "test",
},
"tags": [
"story",
],
"title": "Component D",
"type": "story",
},
},
"v": 4,
}
`);
await waitForRender();
mockChannel.emit.mockClear();
disposeCallback(module.hot.data);
configure('test', () => [componentCExports], module as any);
await waitForEvents([SET_INDEX]);
expect(mockChannel.emit.mock.calls.find((call) => call[0] === SET_INDEX)?.[1])
.toMatchInlineSnapshot(`
{
"entries": {
"component-c--story-one": {
"argTypes": {},
"args": {},
"id": "component-c--story-one",
"importPath": "exports-map-0",
"initialArgs": {},
"name": "Story One",
"parameters": {
"__isArgsStory": false,
"fileName": "exports-map-0",
"renderer": "test",
},
"tags": [
"story-tag",
"story",
],
"title": "Component C",
"type": "story",
},
"component-c--story-two": {
"argTypes": {},
"args": {},
"id": "component-c--story-two",
"importPath": "exports-map-0",
"initialArgs": {},
"name": "Story Two",
"parameters": {
"__isArgsStory": false,
"fileName": "exports-map-0",
"renderer": "test",
},
"tags": [
"component-tag",
"autodocs",
"story",
],
"title": "Component C",
"type": "story",
},
},
"v": 4,
}
`);
await waitForEvents([STORY_UNCHANGED]);
});
it('allows you to override the render function in project annotations', async () => {
const renderToCanvas = vi.fn(({ storyFn }) => storyFn());
const frameworkRender = vi.fn();
const { configure } = start(renderToCanvas, { render: frameworkRender });
const projectRender = vi.fn();
setGlobalRender(projectRender);
configure('test', () => {
return [
{
default: {
title: 'Component A',
component: vi.fn(),
},
StoryOne: {},
},
];
});
await waitForRender();
expect(mockChannel.emit).toHaveBeenCalledWith(STORY_RENDERED, 'component-a--story-one');
expect(frameworkRender).not.toHaveBeenCalled();
expect(projectRender).toHaveBeenCalled();
});
describe('docs', () => {
beforeEach(() => {
global.DOCS_OPTIONS = {};
});
// NOTE: MDX files are only ever passed as CSF
it('sends over docs only stories as entries', async () => {
const renderToCanvas = vi.fn();
const { configure } = start(renderToCanvas);
configure(
'test',
makeRequireContext({
'./Introduction.stories.mdx': {
default: { title: 'Introduction', tags: ['stories-mdx'] },
_Page: { name: 'Page', parameters: { docsOnly: true } },
},
})
);
await waitForEvents([SET_INDEX]);
expect(mockChannel.emit.mock.calls.find((call) => call[0] === SET_INDEX)?.[1])
.toMatchInlineSnapshot(`
{
"entries": {
"introduction": {
"id": "introduction",
"importPath": "./Introduction.stories.mdx",
"name": undefined,
"parameters": {
"fileName": "./Introduction.stories.mdx",
"renderer": "test",
},
"storiesImports": [],
"tags": [
"stories-mdx",
"docs",
],
"title": "Introduction",
"type": "docs",
},
},
"v": 4,
}
`);
// Wait a second to let the docs "render" finish (and maybe throw)
await waitForQuiescence();
});
it('errors on .mdx files', async () => {
const renderToCanvas = vi.fn();
const { configure } = start(renderToCanvas);
configure(
'test',
makeRequireContext({
'./Introduction.mdx': {
default: () => 'some mdx function',
},
})
);
await waitForEvents([CONFIG_ERROR]);
expect(mockChannel.emit.mock.calls.find((call) => call[0] === CONFIG_ERROR)?.[1])
.toMatchInlineSnapshot(`
[Error: Cannot index \`.mdx\` file (\`./Introduction.mdx\`) in \`storyStoreV7: false\` mode.
The legacy story store does not support new-style \`.mdx\` files. If the file above
is not intended to be indexed (i.e. displayed as an entry in the sidebar), either
exclude it from your \`stories\` glob, or add <Meta isTemplate /> to it.
If you wanted to index the file, you'll need to name it \`stories.mdx\` and stick to the
legacy (6.x) MDX API, or use the new store.]
`);
});
});
});
describe('auto-title', () => {
const componentDExports = {
default: {
component: 'Component D',
},
StoryOne: vi.fn(),
};
it('loads and renders the first story correctly', async () => {
const renderToCanvas = vi.fn();
const { configure } = start(renderToCanvas);
configure('test', () => [componentDExports]);
await waitForEvents([SET_INDEX]);
expect(mockChannel.emit.mock.calls.find((call) => call[0] === SET_INDEX)?.[1])
.toMatchInlineSnapshot(`
{
"entries": {
"auto-title--story-one": {
"argTypes": {},
"args": {},
"id": "auto-title--story-one",
"importPath": "exports-map-0",
"initialArgs": {},
"name": "Story One",
"parameters": {
"__isArgsStory": false,
"fileName": "exports-map-0",
"renderer": "test",
},
"tags": [
"story",
],
"title": "auto-title",
"type": "story",
},
},
"v": 4,
}
`);
await waitForRender();
});
});
});

View File

@ -1,170 +0,0 @@
/* eslint-disable no-underscore-dangle, @typescript-eslint/naming-convention */
import { global } from '@storybook/global';
import type { Renderer, ArgsStoryFn, Path, ProjectAnnotations } from '@storybook/types';
import { createBrowserChannel } from '@storybook/channels';
import { FORCE_RE_RENDER } from '@storybook/core-events';
import { addons } from '../../addons';
import { PreviewWeb } from '../../preview-web';
import { ClientApi } from '../../client-api';
import { executeLoadableForChanges } from './executeLoadable';
import type { Loadable } from './executeLoadable';
const { FEATURES } = global;
const removedApi = (name: string) => () => {
throw new Error(`@storybook/client-api:${name} was removed in storyStoreV7.`);
};
interface CoreClient_RendererImplementation<TRenderer extends Renderer> {
/**
* A function that applies decorators to a story.
* @template TRenderer The type of renderer used by the Storybook client API.
* @type {ProjectAnnotations<TRenderer>['applyDecorators']}
*/
decorateStory?: ProjectAnnotations<TRenderer>['applyDecorators'];
/**
* A function that renders a story with args.
* @template TRenderer The type of renderer used by the Storybook client API.
* @type {ArgsStoryFn<TRenderer>}
*/
render?: ArgsStoryFn<TRenderer>;
}
interface CoreClient_ClientAPIFacade {
/**
* The old way of retrieving the list of stories at runtime.
* @deprecated This method is deprecated and will be removed in a future version.
*/
raw: (...args: any[]) => never;
}
interface CoreClient_StartReturnValue<TRenderer extends Renderer> {
/**
* Forces a re-render of all stories in the Storybook preview.
* This function emits the `FORCE_RE_RENDER` event to the Storybook channel.
* @deprecated This method is deprecated and will be removed in a future version.
* @returns {void}
*/
forceReRender: () => void;
/**
* The old way of setting up storybook with runtime configuration.
* @deprecated This method is deprecated and will be removed in a future version.
* @returns {void}
*/
configure: any;
/**
* @deprecated This property is deprecated and will be removed in a future version.
* @type {ClientApi<TRenderer> | CoreClient_ClientAPIFacade}
*/
clientApi: ClientApi<TRenderer> | CoreClient_ClientAPIFacade;
}
/**
* Initializes the Storybook preview API.
* @template TRenderer The type of renderer used by the Storybook client API.
* @param {ProjectAnnotations<TRenderer>['renderToCanvas']} renderToCanvas A function that renders a story to a canvas.
* @param {CoreClient_RendererImplementation<TRenderer>} [options] Optional configuration options for the renderer implementation.
* @param {ProjectAnnotations<TRenderer>['applyDecorators']} [options.decorateStory] A function that applies decorators to a story.
* @param {ArgsStoryFn<TRenderer>} [options.render] A function that renders a story with arguments.
* @returns {CoreClient_StartReturnValue<TRenderer>} An object containing functions and objects related to the Storybook preview API.
*/
export function start<TRenderer extends Renderer>(
renderToCanvas: ProjectAnnotations<TRenderer>['renderToCanvas'],
{ decorateStory, render }: CoreClient_RendererImplementation<TRenderer> = {}
): CoreClient_StartReturnValue<TRenderer> {
if (global) {
// To enable user code to detect if it is running in Storybook
global.IS_STORYBOOK = true;
}
if (FEATURES?.storyStoreV7) {
return {
forceReRender: removedApi('forceReRender'),
configure: removedApi('configure'),
clientApi: {
raw: removedApi('raw'),
},
};
}
const channel = createBrowserChannel({ page: 'preview' });
addons.setChannel(channel);
const clientApi = global?.__STORYBOOK_CLIENT_API__ || new ClientApi<TRenderer>();
const preview = global?.__STORYBOOK_PREVIEW__ || new PreviewWeb<TRenderer>();
let initialized = false;
const importFn = (path: Path) => clientApi.importFn(path);
function onStoriesChanged() {
const storyIndex = clientApi.getStoryIndex();
preview.onStoriesChanged({ storyIndex, importFn });
}
// These two bits are a bit ugly, but due to dependencies, `ClientApi` cannot have
// direct reference to `PreviewWeb`, so we need to patch in bits
clientApi.onImportFnChanged = onStoriesChanged;
clientApi.storyStore = preview.storyStore;
if (global) {
global.__STORYBOOK_CLIENT_API__ = clientApi;
global.__STORYBOOK_ADDONS_CHANNEL__ = channel;
global.__STORYBOOK_PREVIEW__ = preview;
global.__STORYBOOK_STORY_STORE__ = preview.storyStore;
}
return {
forceReRender: () => channel.emit(FORCE_RE_RENDER),
clientApi,
// This gets called each time the user calls configure (i.e. once per HMR)
// The first time, it constructs the preview, subsequently it updates it
configure(
renderer: string,
loadable: Loadable,
m?: NodeModule,
disableBackwardCompatibility = true
) {
if (disableBackwardCompatibility) {
throw new Error('unexpected configure() call');
}
clientApi.addParameters({ renderer });
// We need to run the `executeLoadableForChanges` function *inside* the `getProjectAnnotations
// function in case it throws. So we also need to process its output there also
const getProjectAnnotations = () => {
const { added, removed } = executeLoadableForChanges(loadable, m);
clientApi._loadAddedExports();
Array.from(added.entries()).forEach(([fileName, fileExports]) =>
clientApi.facade.addStoriesFromExports(fileName, fileExports)
);
Array.from(removed.entries()).forEach(([fileName]) =>
clientApi.facade.clearFilenameExports(fileName)
);
return {
render,
...clientApi.facade.projectAnnotations,
renderToCanvas,
applyDecorators: decorateStory,
};
};
if (!initialized) {
preview.initialize({
getStoryIndex: () => clientApi.getStoryIndex(),
importFn,
getProjectAnnotations,
});
initialized = true;
} else {
// TODO -- why don't we care about the new annotations?
getProjectAnnotations();
onStoriesChanged();
}
},
};
}

View File

@ -1,6 +1,5 @@
import { dedent } from 'ts-dedent';
import { global } from '@storybook/global';
import { SynchronousPromise } from 'synchronous-promise';
import {
CONFIG_ERROR,
FORCE_REMOUNT,
@ -13,7 +12,7 @@ import {
UPDATE_GLOBALS,
UPDATE_STORY_ARGS,
} from '@storybook/core-events';
import { logger, deprecate } from '@storybook/client-logger';
import { logger } from '@storybook/client-logger';
import type { Channel } from '@storybook/channels';
import type {
Renderer,
@ -61,21 +60,14 @@ export class Preview<TRenderer extends Renderer> {
previewEntryError?: Error;
constructor(protected channel: Channel = addons.getChannel()) {
if (global.FEATURES?.storyStoreV7 && addons.hasServerChannel()) {
if (addons.hasServerChannel()) {
this.serverChannel = addons.getServerChannel();
}
this.storyStore = new StoryStore();
}
// INITIALIZATION
// NOTE: the reason that the preview and store's initialization code is written in a promise
// style and not `async-await`, and the use of `SynchronousPromise`s is in order to allow
// storyshots to immediately call `raw()` on the store without waiting for a later tick.
// (Even simple things like `Promise.resolve()` and `await` involve the callback happening
// in the next promise "tick").
// See the comment in `storyshots-core/src/api/index.ts` for more detail.
initialize({
async initialize({
getStoryIndex,
importFn,
getProjectAnnotations,
@ -93,9 +85,8 @@ export class Preview<TRenderer extends Renderer> {
this.setupListeners();
return this.getProjectAnnotationsOrRenderError(getProjectAnnotations).then(
(projectAnnotations) => this.initializeWithProjectAnnotations(projectAnnotations)
);
const projectAnnotations = await this.getProjectAnnotationsOrRenderError(getProjectAnnotations);
return this.initializeWithProjectAnnotations(projectAnnotations);
}
setupListeners() {
@ -107,57 +98,44 @@ export class Preview<TRenderer extends Renderer> {
this.channel.on(FORCE_REMOUNT, this.onForceRemount.bind(this));
}
getProjectAnnotationsOrRenderError(
async getProjectAnnotationsOrRenderError(
getProjectAnnotations: () => MaybePromise<ProjectAnnotations<TRenderer>>
): Promise<ProjectAnnotations<TRenderer>> {
return SynchronousPromise.resolve()
.then(getProjectAnnotations)
.then((projectAnnotations) => {
if (projectAnnotations.renderToDOM)
deprecate(`\`renderToDOM\` is deprecated, please rename to \`renderToCanvas\``);
try {
const projectAnnotations = await getProjectAnnotations();
this.renderToCanvas = projectAnnotations.renderToCanvas || projectAnnotations.renderToDOM;
if (!this.renderToCanvas) {
throw new Error(dedent`
this.renderToCanvas = projectAnnotations.renderToCanvas;
if (!this.renderToCanvas) {
throw new Error(dedent`
Expected your framework's preset to export a \`renderToCanvas\` field.
Perhaps it needs to be upgraded for Storybook 6.4?
More info: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#mainjs-framework-field
`);
}
return projectAnnotations;
})
.catch((err) => {
// This is an error extracting the projectAnnotations (i.e. evaluating the previewEntries) and
// needs to be show to the user as a simple error
this.renderPreviewEntryError('Error reading preview.js:', err);
throw err;
});
}
return projectAnnotations;
} catch (err) {
// This is an error extracting the projectAnnotations (i.e. evaluating the previewEntries) and
// needs to be show to the user as a simple error
this.renderPreviewEntryError('Error reading preview.js:', err as Error);
throw err;
}
}
// If initialization gets as far as project annotations, this function runs.
initializeWithProjectAnnotations(projectAnnotations: ProjectAnnotations<TRenderer>) {
async initializeWithProjectAnnotations(projectAnnotations: ProjectAnnotations<TRenderer>) {
this.storyStore.setProjectAnnotations(projectAnnotations);
this.setInitialGlobals();
let storyIndexPromise: Promise<StoryIndex>;
if (global.FEATURES?.storyStoreV7) {
storyIndexPromise = this.getStoryIndexFromServer();
} else {
if (!this.getStoryIndex) {
throw new Error('No `getStoryIndex` passed defined in v6 mode');
}
storyIndexPromise = SynchronousPromise.resolve().then(this.getStoryIndex);
try {
const storyIndex = await this.getStoryIndexFromServer();
return this.initializeWithStoryIndex(storyIndex);
} catch (err) {
this.renderPreviewEntryError('Error loading story index:', err as Error);
throw err;
}
return storyIndexPromise
.then((storyIndex: StoryIndex) => this.initializeWithStoryIndex(storyIndex))
.catch((err) => {
this.renderPreviewEntryError('Error loading story index:', err);
throw err;
});
}
async setInitialGlobals() {
@ -185,15 +163,11 @@ export class Preview<TRenderer extends Renderer> {
}
// If initialization gets as far as the story index, this function runs.
initializeWithStoryIndex(storyIndex: StoryIndex): PromiseLike<void> {
initializeWithStoryIndex(storyIndex: StoryIndex): void {
if (!this.importFn)
throw new Error(`Cannot call initializeWithStoryIndex before initialization`);
return this.storyStore.initialize({
storyIndex,
importFn: this.importFn,
cache: !global.FEATURES?.storyStoreV7,
});
this.storyStore.initialize({ storyIndex, importFn: this.importFn });
}
// EVENT HANDLERS
@ -212,7 +186,7 @@ export class Preview<TRenderer extends Renderer> {
return;
}
await this.storyStore.setProjectAnnotations(projectAnnotations);
this.storyStore.setProjectAnnotations(projectAnnotations);
this.emitGlobals();
}
@ -230,7 +204,7 @@ export class Preview<TRenderer extends Renderer> {
// This is the first time the story index worked, let's load it into the store
if (!this.storyStore.storyIndex) {
await this.initializeWithStoryIndex(storyIndex);
this.initializeWithStoryIndex(storyIndex);
}
// Update the store with the new stories.
@ -368,9 +342,7 @@ export class Preview<TRenderer extends Renderer> {
Do you have an error in your \`preview.js\`? Check your Storybook's browser console for errors.`);
}
if (global.FEATURES?.storyStoreV7) {
await this.storyStore.cacheAllCSFFiles();
}
await this.storyStore.cacheAllCSFFiles();
return this.storyStore.extract(options);
}

View File

@ -47,9 +47,6 @@ vi.mock('@storybook/global', () => ({
search: '?id=*',
},
},
FEATURES: {
storyStoreV7: true,
},
fetch: async () => ({ status: 200, json: async () => mockStoryIndex }),
},
}));
@ -76,7 +73,7 @@ beforeEach(() => {
vi.mocked(WebView.prototype).prepareForStory.mockReturnValue('story-element' as any);
});
describe('PreviewWeb', () => {
describe.skip('PreviewWeb', () => {
describe('initial render', () => {
it('renders story mode through the stack', async () => {
const { DocsRenderer } = await import('@storybook/addon-docs');

View File

@ -69,10 +69,6 @@ vi.mock('@storybook/global', async (importOriginal) => ({
search: '?id=*',
},
},
FEATURES: {
storyStoreV7: true,
// xxx
},
fetch: async () => mockFetchResult,
},
}));
@ -142,7 +138,7 @@ beforeEach(() => {
vi.mocked(WebView.prototype).prepareForStory.mockReturnValue('story-element' as any);
});
describe('PreviewWeb', () => {
describe.skip('PreviewWeb', () => {
describe('initialize', () => {
it('shows an error if getProjectAnnotations throws', async () => {
const err = new Error('meta error');

View File

@ -1,12 +1,10 @@
import { dedent } from 'ts-dedent';
import { global } from '@storybook/global';
import {
CURRENT_STORY_WAS_SET,
DOCS_PREPARED,
PRELOAD_ENTRIES,
PREVIEW_KEYDOWN,
SET_CURRENT_STORY,
SET_INDEX,
STORY_ARGS_UPDATED,
STORY_CHANGED,
STORY_ERRORED,
@ -115,14 +113,10 @@ export class PreviewWithSelection<TFramework extends Renderer> extends Preview<T
}
// If initialization gets as far as the story index, this function runs.
initializeWithStoryIndex(storyIndex: StoryIndex): PromiseLike<void> {
return super.initializeWithStoryIndex(storyIndex).then(() => {
if (!global.FEATURES?.storyStoreV7) {
this.channel.emit(SET_INDEX, this.storyStore.getSetIndexPayload());
}
async initializeWithStoryIndex(storyIndex: StoryIndex): Promise<void> {
await super.initializeWithStoryIndex(storyIndex);
return this.selectSpecifiedStory();
});
return this.selectSpecifiedStory();
}
// Use the selection specifier to choose a story, then render it
@ -204,10 +198,6 @@ export class PreviewWithSelection<TFramework extends Renderer> extends Preview<T
}) {
await super.onStoriesChanged({ importFn, storyIndex });
if (!global.FEATURES?.storyStoreV7) {
this.channel.emit(SET_INDEX, await this.storyStore.getSetIndexPayload());
}
if (this.selectionStore.selection) {
await this.renderSelection();
} else {
@ -396,15 +386,13 @@ export class PreviewWithSelection<TFramework extends Renderer> extends Preview<T
render.story
);
if (global.FEATURES?.storyStoreV7) {
this.channel.emit(STORY_PREPARED, {
id: storyId,
parameters,
initialArgs,
argTypes,
args: unmappedArgs,
});
}
this.channel.emit(STORY_PREPARED, {
id: storyId,
parameters,
initialArgs,
argTypes,
args: unmappedArgs,
});
// For v6 mode / compatibility
// If the implementation changed, or args were persisted, the args may have changed,
@ -412,8 +400,10 @@ export class PreviewWithSelection<TFramework extends Renderer> extends Preview<T
if (implementationChanged || persistedArgs) {
this.channel.emit(STORY_ARGS_UPDATED, { storyId, args: unmappedArgs });
}
} else if (global.FEATURES?.storyStoreV7) {
if (!this.storyStore.projectAnnotations) throw new Error('Store not initialized');
} else {
if (!this.storyStore.projectAnnotations) {
throw new Error('Store not initialized');
}
// Default to the project parameters for MDX docs
let { parameters } = this.storyStore.projectAnnotations;
@ -467,9 +457,7 @@ export class PreviewWithSelection<TFramework extends Renderer> extends Preview<T
Do you have an error in your \`preview.js\`? Check your Storybook's browser console for errors.`);
}
if (global.FEATURES?.storyStoreV7) {
await this.storyStore.cacheAllCSFFiles();
}
await this.storyStore.cacheAllCSFFiles();
return this.storyStore.extract(options);
}

View File

@ -86,7 +86,7 @@ describe('StoryStore', () => {
it('normalizes on initialization', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
expect(store.projectAnnotations!.globalTypes).toEqual({
a: { name: 'a', type: { name: 'string' } },
@ -99,7 +99,7 @@ describe('StoryStore', () => {
it('normalizes on updateGlobalAnnotations', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
store.setProjectAnnotations(projectAnnotations);
expect(store.projectAnnotations!.globalTypes).toEqual({
@ -115,7 +115,7 @@ describe('StoryStore', () => {
it('pulls the story via the importFn', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
importFn.mockClear();
expect(await store.loadStory({ storyId: 'component-one--a' })).toMatchObject({
@ -130,7 +130,7 @@ describe('StoryStore', () => {
it('uses a cache', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
const story = await store.loadStory({ storyId: 'component-one--a' });
expect(processCSFFile).toHaveBeenCalledTimes(1);
@ -158,7 +158,7 @@ describe('StoryStore', () => {
const loadPromise = store.loadStory({ storyId: 'component-one--a' });
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
expect(await loadPromise).toMatchObject({
id: 'component-one--a',
@ -175,7 +175,7 @@ describe('StoryStore', () => {
it('busts the loadStory cache', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
const story = await store.loadStory({ storyId: 'component-one--a' });
expect(processCSFFile).toHaveBeenCalledTimes(1);
@ -194,7 +194,7 @@ describe('StoryStore', () => {
it('busts the loadStory cache if the importFn returns a new module', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
const story = await store.loadStory({ storyId: 'component-one--a' });
expect(processCSFFile).toHaveBeenCalledTimes(1);
@ -216,7 +216,7 @@ describe('StoryStore', () => {
it('busts the loadStory cache if the csf file no longer appears in the index', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
await store.loadStory({ storyId: 'component-one--a' });
expect(processCSFFile).toHaveBeenCalledTimes(1);
@ -235,7 +235,7 @@ describe('StoryStore', () => {
it('reuses the cache if a story importPath has not changed', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
const story = await store.loadStory({ storyId: 'component-one--a' });
expect(processCSFFile).toHaveBeenCalledTimes(1);
@ -267,7 +267,7 @@ describe('StoryStore', () => {
it('imports with a new path for a story id if provided', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
await store.loadStory({ storyId: 'component-one--a' });
expect(importFn).toHaveBeenCalledWith(storyIndex.entries['component-one--a'].importPath);
@ -297,7 +297,7 @@ describe('StoryStore', () => {
it('re-caches stories if the were cached already', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
await store.cacheAllCSFFiles();
await store.loadStory({ storyId: 'component-one--a' });
@ -370,7 +370,7 @@ describe('StoryStore', () => {
it('returns all the stories in the file', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
const csfFile = await store.loadCSFFileByStoryId('component-one--a');
const stories = store.componentStoriesFromCSFFile({ csfFile });
@ -389,7 +389,7 @@ describe('StoryStore', () => {
'component-one--a': storyIndex.entries['component-one--a'],
},
};
store.initialize({ storyIndex: reversedIndex, importFn, cache: false });
store.initialize({ storyIndex: reversedIndex, importFn });
const csfFile = await store.loadCSFFileByStoryId('component-one--a');
const stories = store.componentStoriesFromCSFFile({ csfFile });
@ -403,7 +403,7 @@ describe('StoryStore', () => {
it('returns the args and globals correctly', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
const story = await store.loadStory({ storyId: 'component-one--a' });
@ -416,7 +416,7 @@ describe('StoryStore', () => {
it('returns the args and globals correctly when they change', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
const story = await store.loadStory({ storyId: 'component-one--a' });
@ -432,7 +432,7 @@ describe('StoryStore', () => {
it('can force initial args', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
const story = await store.loadStory({ storyId: 'component-one--a' });
@ -446,7 +446,7 @@ describe('StoryStore', () => {
it('returns the same hooks each time', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
const story = await store.loadStory({ storyId: 'component-one--a' });
@ -463,7 +463,7 @@ describe('StoryStore', () => {
it('cleans the hooks from the context', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
const story = await store.loadStory({ storyId: 'component-one--a' });
@ -478,7 +478,7 @@ describe('StoryStore', () => {
it('imports *all* csf files', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
importFn.mockClear();
const csfFiles = await store.loadAllCSFFiles();
@ -498,7 +498,7 @@ describe('StoryStore', () => {
});
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn: blockedImportFn, cache: false });
store.initialize({ storyIndex, importFn: blockedImportFn });
const promise = store.loadAllCSFFiles({ batchSize: 1 });
expect(blockedImportFn).toHaveBeenCalledTimes(1);
@ -513,7 +513,7 @@ describe('StoryStore', () => {
it('throws if you have not called cacheAllCSFFiles', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
expect(() => store.extract()).toThrow(/Cannot call extract/);
});
@ -521,7 +521,7 @@ describe('StoryStore', () => {
it('produces objects with functions and hooks stripped', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
await store.cacheAllCSFFiles();
expect(store.extract()).toMatchInlineSnapshot(`
@ -658,7 +658,6 @@ describe('StoryStore', () => {
store.initialize({
storyIndex,
importFn: docsOnlyImportFn,
cache: false,
});
await store.cacheAllCSFFiles();
@ -691,7 +690,6 @@ describe('StoryStore', () => {
store.initialize({
storyIndex: unnattachedStoryIndex,
importFn,
cache: false,
});
await store.cacheAllCSFFiles();
@ -713,7 +711,7 @@ describe('StoryStore', () => {
it('produces an array of stories', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
await store.cacheAllCSFFiles();
expect(store.raw()).toMatchInlineSnapshot(`
@ -862,7 +860,7 @@ describe('StoryStore', () => {
it('maps stories list to payload correctly', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
await store.cacheAllCSFFiles();
expect(store.getSetStoriesPayload()).toMatchInlineSnapshot(`
@ -1002,7 +1000,7 @@ describe('StoryStore', () => {
it('maps stories list to payload correctly', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
await store.cacheAllCSFFiles();
expect(store.getStoriesJsonData()).toMatchInlineSnapshot(`
@ -1052,116 +1050,6 @@ describe('StoryStore', () => {
});
});
describe('getSetIndexPayload', () => {
it('add parameters/args to index correctly', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
await store.cacheAllCSFFiles();
expect(store.getSetIndexPayload()).toMatchInlineSnapshot(`
{
"entries": {
"component-one--a": {
"argTypes": {
"a": {
"name": "a",
"type": {
"name": "string",
},
},
"foo": {
"name": "foo",
"type": {
"name": "string",
},
},
},
"args": {
"foo": "a",
},
"id": "component-one--a",
"importPath": "./src/ComponentOne.stories.js",
"initialArgs": {
"foo": "a",
},
"name": "A",
"parameters": {
"__isArgsStory": false,
"fileName": "./src/ComponentOne.stories.js",
},
"title": "Component One",
"type": "story",
},
"component-one--b": {
"argTypes": {
"a": {
"name": "a",
"type": {
"name": "string",
},
},
"foo": {
"name": "foo",
"type": {
"name": "string",
},
},
},
"args": {
"foo": "b",
},
"id": "component-one--b",
"importPath": "./src/ComponentOne.stories.js",
"initialArgs": {
"foo": "b",
},
"name": "B",
"parameters": {
"__isArgsStory": false,
"fileName": "./src/ComponentOne.stories.js",
},
"title": "Component One",
"type": "story",
},
"component-two--c": {
"argTypes": {
"a": {
"name": "a",
"type": {
"name": "string",
},
},
"foo": {
"name": "foo",
"type": {
"name": "string",
},
},
},
"args": {
"foo": "c",
},
"id": "component-two--c",
"importPath": "./src/ComponentTwo.stories.js",
"initialArgs": {
"foo": "c",
},
"name": "C",
"parameters": {
"__isArgsStory": false,
"fileName": "./src/ComponentTwo.stories.js",
},
"title": "Component Two",
"type": "story",
},
},
"v": 4,
}
`);
});
});
describe('cacheAllCsfFiles', () => {
describe('if the store is not yet initialized', () => {
it('waits for initialization', async () => {
@ -1171,7 +1059,7 @@ describe('StoryStore', () => {
const cachePromise = store.cacheAllCSFFiles();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
store.initialize({ storyIndex, importFn });
await expect(cachePromise).resolves.toEqual(undefined);
});

View File

@ -2,7 +2,6 @@ import memoize from 'memoizerific';
import type {
IndexEntry,
Renderer,
API_PreparedStoryIndex,
ComponentTitle,
Parameters,
Path,
@ -24,7 +23,6 @@ import type {
} from '@storybook/types';
import mapValues from 'lodash/mapValues.js';
import pick from 'lodash/pick.js';
import { SynchronousPromise } from 'synchronous-promise';
import { HooksContext } from '../addons';
import { StoryIndexStore } from './StoryIndexStore';
@ -63,9 +61,9 @@ export class StoryStore<TRenderer extends Renderer> {
prepareStoryWithCache: typeof prepareStory;
initializationPromise: SynchronousPromise<void>;
initializationPromise: Promise<void>;
// This *does* get set in the constructor but the semantics of `new SynchronousPromise` trip up TS
// This *does* get set in the constructor but the semantics of `new Promise` trip up TS
resolveInitializationPromise!: () => void;
constructor() {
@ -80,7 +78,7 @@ export class StoryStore<TRenderer extends Renderer> {
this.prepareStoryWithCache = memoize(STORY_CACHE_SIZE)(prepareStory) as typeof prepareStory;
// We cannot call `loadStory()` until we've been initialized properly. But we can wait for it.
this.initializationPromise = new SynchronousPromise((resolve) => {
this.initializationPromise = new Promise((resolve) => {
this.resolveInitializationPromise = resolve;
});
}
@ -100,19 +98,15 @@ export class StoryStore<TRenderer extends Renderer> {
initialize({
storyIndex,
importFn,
cache = false,
}: {
storyIndex?: StoryIndex;
importFn: ModuleImportFn;
cache?: boolean;
}): Promise<void> {
}): void {
this.storyIndex = new StoryIndexStore(storyIndex);
this.importFn = importFn;
// We don't need the cache to be loaded to call `loadStory`, we just need the index ready
this.resolveInitializationPromise();
return cache ? this.cacheAllCSFFiles() : SynchronousPromise.resolve();
}
// This means that one of the CSF files has changed.
@ -142,18 +136,18 @@ export class StoryStore<TRenderer extends Renderer> {
}
// To load a single CSF file to service a story we need to look up the importPath in the index
loadCSFFileByStoryId(storyId: StoryId): Promise<CSFFile<TRenderer>> {
async loadCSFFileByStoryId(storyId: StoryId): Promise<CSFFile<TRenderer>> {
if (!this.storyIndex || !this.importFn)
throw new Error(`loadCSFFileByStoryId called before initialization`);
const { importPath, title } = this.storyIndex.storyIdToEntry(storyId);
return this.importFn(importPath).then((moduleExports) =>
// We pass the title in here as it may have been generated by autoTitle on the server.
this.processCSFFileWithCache(moduleExports, importPath, title)
);
const moduleExports = await this.importFn(importPath);
// We pass the title in here as it may have been generated by autoTitle on the server.
return this.processCSFFileWithCache(moduleExports, importPath, title);
}
loadAllCSFFiles({ batchSize = EXTRACT_BATCH_SIZE } = {}): Promise<
async loadAllCSFFiles({ batchSize = EXTRACT_BATCH_SIZE } = {}): Promise<
StoryStore<TRenderer>['cachedCSFFiles']
> {
if (!this.storyIndex) throw new Error(`loadAllCSFFiles called before initialization`);
@ -163,41 +157,33 @@ export class StoryStore<TRenderer extends Renderer> {
storyId,
]);
const loadInBatches = (
const loadInBatches = async (
remainingImportPaths: typeof importPaths
): Promise<{ importPath: Path; csfFile: CSFFile<TRenderer> }[]> => {
if (remainingImportPaths.length === 0) return SynchronousPromise.resolve([]);
if (remainingImportPaths.length === 0) return Promise.resolve([]);
const csfFilePromiseList = remainingImportPaths
.slice(0, batchSize)
.map(([importPath, storyId]) =>
this.loadCSFFileByStoryId(storyId).then((csfFile) => ({
importPath,
csfFile,
}))
);
.map(async ([importPath, storyId]) => ({
importPath,
csfFile: await this.loadCSFFileByStoryId(storyId),
}));
return SynchronousPromise.all(csfFilePromiseList).then((firstResults) =>
loadInBatches(remainingImportPaths.slice(batchSize)).then((restResults) =>
firstResults.concat(restResults)
)
);
const firstResults = await Promise.all(csfFilePromiseList);
const restResults = await loadInBatches(remainingImportPaths.slice(batchSize));
return firstResults.concat(restResults);
};
return loadInBatches(importPaths).then((list) =>
list.reduce((acc, { importPath, csfFile }) => {
acc[importPath] = csfFile;
return acc;
}, {} as Record<Path, CSFFile<TRenderer>>)
);
const list = await loadInBatches(importPaths);
return list.reduce((acc, { importPath, csfFile }) => {
acc[importPath] = csfFile;
return acc;
}, {} as Record<Path, CSFFile<TRenderer>>);
}
cacheAllCSFFiles(): Promise<void> {
return this.initializationPromise.then(() =>
this.loadAllCSFFiles().then((csfFiles) => {
this.cachedCSFFiles = csfFiles;
})
);
async cacheAllCSFFiles(): Promise<void> {
await this.initializationPromise;
this.cachedCSFFiles = await this.loadAllCSFFiles();
}
preparedMetaFromCSFFile({ csfFile }: { csfFile: CSFFile<TRenderer> }): PreparedMeta<TRenderer> {
@ -393,38 +379,6 @@ export class StoryStore<TRenderer extends Renderer> {
};
};
getSetIndexPayload(): API_PreparedStoryIndex {
if (!this.storyIndex) throw new Error('getSetIndexPayload called before initialization');
if (!this.cachedCSFFiles)
throw new Error('Cannot call getSetIndexPayload() unless you call cacheAllCSFFiles() first');
const { cachedCSFFiles } = this;
const stories = this.extract({ includeDocsOnly: true });
return {
v: 4,
entries: Object.fromEntries(
Object.entries(this.storyIndex.entries).map(([id, entry]) => [
id,
stories[id]
? {
...entry,
args: stories[id].initialArgs,
initialArgs: stories[id].initialArgs,
argTypes: stories[id].argTypes,
parameters: stories[id].parameters,
}
: {
...entry,
parameters: this.preparedMetaFromCSFFile({
csfFile: cachedCSFFiles[entry.importPath],
}).parameters,
},
])
),
};
}
raw(): BoundStory<TRenderer>[] {
return Object.values(this.extract())
.map(({ id }: { id: StoryId }) => this.fromId(id))

View File

@ -177,6 +177,16 @@ describe('userOrAutoTitleFromSpecifier', () => {
).toMatchInlineSnapshot(`to/button`);
});
it('match with case-insensitive trailing duplicate', () => {
expect(
userOrAuto(
'./path/to/button/Button.stories.js',
normalizeStoriesEntry({ directory: './path' }, options),
undefined
)
).toMatchInlineSnapshot(`to/Button`);
});
it('match with trailing index', () => {
expect(
userOrAuto(

View File

@ -15,11 +15,12 @@ const sanitize = (parts: string[]) => {
if (parts.length === 1) return [lastStripped];
const nextToLast = parts[parts.length - 2];
if (lastStripped && nextToLast && lastStripped.toLowerCase() === nextToLast.toLowerCase()) {
return [...parts.slice(0, -2), lastStripped];
}
return lastStripped &&
nextToLast &&
(lastStripped === nextToLast ||
/^(story|stories)([.][^.]+)$/i.test(last) ||
/^index$/i.test(lastStripped))
(/^(story|stories)([.][^.]+)$/i.test(last) || /^index$/i.test(lastStripped))
? parts.slice(0, -1)
: [...parts.slice(0, -1), lastStripped];
};

View File

@ -19,7 +19,6 @@ declare var IS_STORYBOOK: boolean;
// relevant framework instantiates them via `start.js`. The good news is this happens right away.
declare var __STORYBOOK_ADDONS_CHANNEL__: any;
declare var __STORYBOOK_ADDONS_PREVIEW: any;
declare var __STORYBOOK_CLIENT_API__: import('./modules/client-api/ClientApi').ClientApi<any>;
declare var __STORYBOOK_PREVIEW__: import('./modules/preview-web/PreviewWeb').PreviewWeb<any>;
declare var __STORYBOOK_STORY_STORE__: any;
declare var STORYBOOK_HOOKS_CONTEXT: any;

View File

@ -10,34 +10,14 @@ The preview's job is:
3. Render the current selection to the web view in either story or docs mode.
## V7 Store vs Legacy (V6)
The story store is designed to load stories 'on demand', and will operate in this fashion if the `storyStoreV7` feature is enabled.
However, for back-compat reasons, in v6 mode, we need to load all stories, synchronously on bootup, emitting the `SET_STORIES` event.
In V7 mode we do not emit that event, instead preferring the `STORY_PREPARED` event, with the data for the single story being rendered.
## Initialization
The preview is `initialized` in two ways.
### V7 Mode:
- `importFn` - is an async `import()` function
- `getProjectAnnotations` - is a simple function that evaluations `preview.js` and addon config files and combines them. If it errors, the Preview will show the error.
- No `getStoryIndex` function is passed, instead the preview creates a `StoryIndexClient` that pulls `stories.json` from node and watches the event stream for invalidation events.
### V6 Mode
- `importFn` - is a simulated `import()` function, that is synchronous, see `client-api` for details.
- `getProjectAnnotations` - also evaluates `preview.js` et al, but watches for calls to `setStories`, and passes them to the `ClientApi`
- `getStoryIndex` is a local function (that must be called _after_ `getProjectAnnotations`) that gets the list of stories added.
See `client-api` for more details on this process.
## Story Rendering and interruptions
The Preview is split into three parts responsible for state management:

View File

@ -313,9 +313,7 @@ describe('storybook-metadata', () => {
});
it('should return user specified features', async () => {
const features = {
storyStoreV7: true,
};
const features = {};
const result = await computeStorybookMetadata({
packageJson: packageJsonMock,

View File

@ -341,22 +341,6 @@ export interface StorybookConfigRaw {
staticDirs?: (DirectoryMapping | string)[];
logLevel?: string;
features?: {
/**
* Build stories.json automatically on start/build
*/
buildStoriesJson?: boolean;
/**
* Activate on demand story store
*/
storyStoreV7?: boolean;
/**
* Do not throw errors if using `.mdx` files in SSv7
* (for internal use in sandboxes)
*/
storyStoreV7MdxErrors?: boolean;
/**
* Filter args with a "target" on the type from the render function (EXPERIMENTAL)
*/

View File

@ -256,7 +256,7 @@
"built": false
}
},
"packageManager": "yarn@4.0.0",
"packageManager": "yarn@4.0.2",
"engines": {
"node": ">=18.0.0"
},

View File

@ -29,7 +29,7 @@ export type Meta<TArgs = Args> = ComponentAnnotations<HtmlRenderer, TArgs>;
export type StoryFn<TArgs = Args> = AnnotatedStoryFn<HtmlRenderer, TArgs>;
/**
* Story function that represents a CSFv3 component example.
* Story object that represents a CSFv3 component example.
*
* @see [Named Story exports](https://storybook.js.org/docs/formats/component-story-format/#named-story-exports)
*/

View File

@ -29,7 +29,7 @@ export type Meta<TArgs = Args> = ComponentAnnotations<PreactRenderer, TArgs>;
export type StoryFn<TArgs = Args> = AnnotatedStoryFn<PreactRenderer, TArgs>;
/**
* Story function that represents a CSFv3 component example.
* Story object that represents a CSFv3 component example.
*
* @see [Named Story exports](https://storybook.js.org/docs/formats/component-story-format/#named-story-exports)
*/

View File

@ -37,7 +37,7 @@ export type StoryFn<TCmpOrArgs = Args> = [TCmpOrArgs] extends [ComponentType<any
: AnnotatedStoryFn<ReactRenderer, TCmpOrArgs>;
/**
* Story function that represents a CSFv3 component example.
* Story object that represents a CSFv3 component example.
*
* @see [Named Story exports](https://storybook.js.org/docs/formats/component-story-format/#named-story-exports)
*/

View File

@ -28,7 +28,7 @@ export type Meta<TArgs = Args> = ComponentAnnotations<ServerRenderer, TArgs>;
export type StoryFn<TArgs = Args> = AnnotatedStoryFn<ServerRenderer, TArgs>;
/**
* Story function that represents a CSFv3 component example.
* Story object that represents a CSFv3 component example.
*
* @see [Named Story exports](https://storybook.js.org/docs/formats/component-story-format/#named-story-exports)
*/

View File

@ -36,7 +36,7 @@ export type StoryFn<TCmpOrArgs = Args> = TCmpOrArgs extends SvelteComponentTyped
: AnnotatedStoryFn<SvelteRenderer, TCmpOrArgs>;
/**
* Story function that represents a CSFv3 component example.
* Story object that represents a CSFv3 component example.
*
* @see [Named Story exports](https://storybook.js.org/docs/formats/component-story-format/#named-story-exports)
*/

View File

@ -2,8 +2,8 @@
import './globals';
export * from './public-api';
export * from './public-types';
export { setup } from './render';
// optimization: stop HMR propagation in webpack
try {

View File

@ -1 +0,0 @@
export { setup } from './render';

View File

@ -40,7 +40,7 @@ export type StoryFn<TCmpOrArgs = Args> = AnnotatedStoryFn<
>;
/**
* Story function that represents a CSFv3 component example.
* Story object that represents a CSFv3 component example.
*
* @see [Named Story exports](https://storybook.js.org/docs/formats/component-story-format/#named-story-exports)
*/

View File

@ -29,7 +29,7 @@ export type Meta<TArgs = Args> = ComponentAnnotations<WebComponentsRenderer, TAr
export type StoryFn<TArgs = Args> = AnnotatedStoryFn<WebComponentsRenderer, TArgs>;
/**
* Story function that represents a CSFv3 component example.
* Story object that represents a CSFv3 component example.
*
* @see [Named Story exports](https://storybook.js.org/docs/formats/component-story-format/#named-story-exports)
*/

View File

@ -51,7 +51,7 @@
"@storybook/csf": "^0.1.2",
"@storybook/docs-tools": "workspace:*",
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.1",
"@storybook/icons": "^1.2.3",
"@storybook/manager-api": "workspace:*",
"@storybook/preview-api": "workspace:*",
"@storybook/theming": "workspace:*",
@ -60,7 +60,7 @@
"color-convert": "^2.0.1",
"dequal": "^2.0.2",
"lodash": "^4.17.21",
"markdown-to-jsx": "^7.1.8",
"markdown-to-jsx": "7.3.2",
"memoizerific": "^1.11.3",
"polished": "^4.2.2",
"react-colorful": "^5.1.2",

View File

@ -63,7 +63,7 @@
"@storybook/client-logger": "workspace:*",
"@storybook/csf": "^0.1.2",
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.1",
"@storybook/icons": "^1.2.3",
"@storybook/theming": "workspace:*",
"@storybook/types": "workspace:*",
"memoizerific": "^1.11.3",

View File

@ -79,7 +79,7 @@
"@storybook/components": "workspace:*",
"@storybook/core-events": "workspace:*",
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.1",
"@storybook/icons": "^1.2.3",
"@storybook/manager-api": "workspace:*",
"@storybook/router": "workspace:*",
"@storybook/test": "workspace:*",
@ -94,7 +94,7 @@
"fs-extra": "^11.1.0",
"fuse.js": "^3.6.1",
"lodash": "^4.17.21",
"markdown-to-jsx": "^7.1.8",
"markdown-to-jsx": "7.3.2",
"memoizerific": "^1.11.3",
"polished": "^4.2.2",
"qs": "^6.10.0",

View File

@ -18,8 +18,6 @@ import { FramesRenderer } from './FramesRenderer';
import type { PreviewProps } from './utils/types';
const { FEATURES } = global;
const getWrappers = (getFn: API['getElements']) => Object.values(getFn(types.PREVIEW));
const getTabs = (getFn: API['getElements']) => Object.values(getFn(types.TAB));
@ -160,7 +158,7 @@ const Canvas: FC<{ withLoader: boolean; baseUrl: string; children?: never }> = (
const [progress, setProgress] = useState(undefined);
useEffect(() => {
if (FEATURES?.storyStoreV7 && global.CONFIG_TYPE === 'DEVELOPMENT') {
if (global.CONFIG_TYPE === 'DEVELOPMENT') {
try {
const channel = addons.getServerChannel();

View File

@ -9,7 +9,7 @@ import { CHANNEL_CREATED } from '@storybook/core-events';
import Provider from './provider';
import { renderStorybookUI } from './index';
const { FEATURES, CONFIG_TYPE } = global;
const { CONFIG_TYPE } = global;
class ReactProvider extends Provider {
private addons: AddonStore;
@ -34,7 +34,7 @@ class ReactProvider extends Provider {
this.channel = channel;
global.__STORYBOOK_ADDONS_CHANNEL__ = channel;
if (FEATURES?.storyStoreV7 && CONFIG_TYPE === 'DEVELOPMENT') {
if (CONFIG_TYPE === 'DEVELOPMENT') {
this.serverChannel = this.channel;
addons.setServerChannel(this.serverChannel);
}

View File

@ -50,7 +50,7 @@ __metadata:
languageName: node
linkType: hard
"@angular-devkit/architect@npm:0.1700.5, @angular-devkit/architect@npm:^0.1700.5":
"@angular-devkit/architect@npm:0.1700.5":
version: 0.1700.5
resolution: "@angular-devkit/architect@npm:0.1700.5"
dependencies:
@ -60,6 +60,16 @@ __metadata:
languageName: node
linkType: hard
"@angular-devkit/architect@npm:^0.1700.5":
version: 0.1700.9
resolution: "@angular-devkit/architect@npm:0.1700.9"
dependencies:
"@angular-devkit/core": "npm:17.0.9"
rxjs: "npm:7.8.1"
checksum: e740d7d6b9318fd6a1b1642fa62d182f7509a021d75835082aba693f0312b369742267b3a0748426f7bee5b7d93b4e2cd17b7f9323a451f7e650efeb8c0b394b
languageName: node
linkType: hard
"@angular-devkit/build-angular@npm:^17.0.5":
version: 17.0.5
resolution: "@angular-devkit/build-angular@npm:17.0.5"
@ -181,7 +191,7 @@ __metadata:
languageName: node
linkType: hard
"@angular-devkit/core@npm:17.0.5, @angular-devkit/core@npm:^17.0.5":
"@angular-devkit/core@npm:17.0.5":
version: 17.0.5
resolution: "@angular-devkit/core@npm:17.0.5"
dependencies:
@ -200,6 +210,25 @@ __metadata:
languageName: node
linkType: hard
"@angular-devkit/core@npm:17.0.9, @angular-devkit/core@npm:^17.0.5":
version: 17.0.9
resolution: "@angular-devkit/core@npm:17.0.9"
dependencies:
ajv: "npm:8.12.0"
ajv-formats: "npm:2.1.1"
jsonc-parser: "npm:3.2.0"
picomatch: "npm:3.0.1"
rxjs: "npm:7.8.1"
source-map: "npm:0.7.4"
peerDependencies:
chokidar: ^3.5.2
peerDependenciesMeta:
chokidar:
optional: true
checksum: fb054e100d912fbf0c4570937993de706a71bb3338ad3cce57bf6a936ab1be1102f6c351024c1c18892d57ed80554305578593dc73ece37d74eb748f216c137c
languageName: node
linkType: hard
"@angular-devkit/schematics@npm:17.0.5":
version: 17.0.5
resolution: "@angular-devkit/schematics@npm:17.0.5"
@ -525,7 +554,7 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-define-polyfill-provider@npm:^0.4.3, @babel/helper-define-polyfill-provider@npm:^0.4.4":
"@babel/helper-define-polyfill-provider@npm:^0.4.4":
version: 0.4.4
resolution: "@babel/helper-define-polyfill-provider@npm:0.4.4"
dependencies:
@ -701,13 +730,13 @@ __metadata:
linkType: hard
"@babel/helpers@npm:^7.23.2, @babel/helpers@npm:^7.23.7":
version: 7.23.7
resolution: "@babel/helpers@npm:7.23.7"
version: 7.23.8
resolution: "@babel/helpers@npm:7.23.8"
dependencies:
"@babel/template": "npm:^7.22.15"
"@babel/traverse": "npm:^7.23.7"
"@babel/types": "npm:^7.23.6"
checksum: f74a61ad28a1bc1fdd9133ad571c07787b66d6db017c707b87c203b0cd06879cea8b33e9c6a8585765a4949efa5df3cc9e19b710fe867f11be38ee29fd4a0488
checksum: d9fce49278a31aaa017a40c1fcdaa450999c49e33582cce8138058c58b1acbe3a2d2488f010f28e91dedf0d35795ea32f0ee18745bbb6c7f54052ae0fd7e6a3f
languageName: node
linkType: hard
@ -1218,22 +1247,21 @@ __metadata:
languageName: node
linkType: hard
"@babel/plugin-transform-classes@npm:^7.22.15, @babel/plugin-transform-classes@npm:^7.23.5":
version: 7.23.5
resolution: "@babel/plugin-transform-classes@npm:7.23.5"
"@babel/plugin-transform-classes@npm:^7.22.15, @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.22.15"
"@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-optimise-call-expression": "npm:^7.22.5"
"@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: 07988f52b4893151887d1ea6ff79e5fe834078c5731bd09babd5659edbbae21ea4e2de326a02443a63fd776b4c945da6177f07875b56fe66e0b7899e830a9e92
checksum: 227ac5166501e04d9e7fbd5eda6869b084ffa4af6830ac12544ac6ea14953ca00eb1762b0df9349c0f6c8d2a799385910f558066cd0fb85b9ca437b1131a6043
languageName: node
linkType: hard
@ -1955,8 +1983,8 @@ __metadata:
linkType: hard
"@babel/preset-env@npm:^7.16.5, @babel/preset-env@npm:^7.23.2":
version: 7.23.7
resolution: "@babel/preset-env@npm:7.23.7"
version: 7.23.8
resolution: "@babel/preset-env@npm:7.23.8"
dependencies:
"@babel/compat-data": "npm:^7.23.5"
"@babel/helper-compilation-targets": "npm:^7.23.6"
@ -1991,7 +2019,7 @@ __metadata:
"@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.5"
"@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"
@ -2040,7 +2068,7 @@ __metadata:
semver: "npm:^6.3.1"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: ac9def873cec52ee02a550bde6e22eced16d1ae331bb8ebc82c03e4c91c12ac17e3e4027647e61612937bcc25ac46e71370aaf99dc2e85dbd11f7777ffeed54e
checksum: e602ad954645f1a509644e3d2c72b3c63bdc2273c377e7a83b78f076eca215887ea3624ffc36aaad03deb9ac8acd89e247fd4562b96e0f2b679485e20d8ff25f
languageName: node
linkType: hard
@ -2124,12 +2152,12 @@ __metadata:
linkType: hard
"@babel/runtime-corejs3@npm:^7.10.2":
version: 7.23.7
resolution: "@babel/runtime-corejs3@npm:7.23.7"
version: 7.23.8
resolution: "@babel/runtime-corejs3@npm:7.23.8"
dependencies:
core-js-pure: "npm:^3.30.2"
regenerator-runtime: "npm:^0.14.0"
checksum: 7230942b6dadddd68334283068f360323c6df205542811bd7e37384ebfc0b5dcc266470db99e5905a8c6e3bb9898f7f066dde145b33b560acd271118ed9b41b2
checksum: 2ccc006308dc0afb88dab5b91380be3a644a7616b9e33b6039eeceb11080541d566e8c29d1e81495c3983990c6843cb872bb150dd6c0f23f85fa9eb8d1fe20c5
languageName: node
linkType: hard
@ -2161,11 +2189,11 @@ __metadata:
linkType: hard
"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.14.8, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
version: 7.23.7
resolution: "@babel/runtime@npm:7.23.7"
version: 7.23.8
resolution: "@babel/runtime@npm:7.23.8"
dependencies:
regenerator-runtime: "npm:^0.14.0"
checksum: 3e304133ee55b0750e03e53cb4efb47fb2bdcdb5795f85bbffa10595196c34b9be60eb65bd6d833c87f49fc827f0365f86f95f51d85b188004d3128bb5129c93
checksum: ba5e8fbb32ef04f6cab5e89c54a0497c2fde7b730595cc1af93496270314f13ff2c6a9360fdb2f0bdd4d6b376752ce3cf85642bd6b876969a6a62954934c2df8
languageName: node
linkType: hard
@ -2467,15 +2495,15 @@ __metadata:
linkType: hard
"@emotion/serialize@npm:^1.1.2":
version: 1.1.2
resolution: "@emotion/serialize@npm:1.1.2"
version: 1.1.3
resolution: "@emotion/serialize@npm:1.1.3"
dependencies:
"@emotion/hash": "npm:^0.9.1"
"@emotion/memoize": "npm:^0.8.1"
"@emotion/unitless": "npm:^0.8.1"
"@emotion/utils": "npm:^1.2.1"
csstype: "npm:^3.0.2"
checksum: d243e0e5abce8d2183d25a32ec89bf650ee741ebadb29e6405abde05d4e2ed446ba5b3f725a29833ad709d0d08f0a5c8d0532fdcd43f4b23d931d8b6d4f218c1
checksum: 875241eafaa30e7d3b7cf9b585d8c1f224cbf627a674e87eb1d7662dafa76a8c8d67f14a79dbf7d1eaa017e9f68389962990fbcc699d5ad65035a1a047432a3f
languageName: node
linkType: hard
@ -4621,7 +4649,7 @@ __metadata:
"@storybook/client-logger": "workspace:*"
"@storybook/components": "workspace:*"
"@storybook/global": "npm:^5.0.0"
"@storybook/icons": "npm:^1.2.1"
"@storybook/icons": "npm:^1.2.3"
"@storybook/manager-api": "workspace:*"
"@storybook/preview-api": "workspace:*"
"@storybook/theming": "workspace:*"
@ -4668,7 +4696,7 @@ __metadata:
"@storybook/client-logger": "workspace:*"
"@storybook/components": "workspace:*"
"@storybook/global": "npm:^5.0.0"
"@storybook/icons": "npm:^1.2.1"
"@storybook/icons": "npm:^1.2.3"
"@storybook/manager-api": "workspace:*"
"@storybook/preview-api": "workspace:*"
"@storybook/theming": "workspace:*"
@ -4799,7 +4827,7 @@ __metadata:
"@storybook/core-common": "workspace:*"
"@storybook/core-events": "workspace:*"
"@storybook/global": "npm:^5.0.0"
"@storybook/icons": "npm:^1.2.1"
"@storybook/icons": "npm:^1.2.3"
"@storybook/instrumenter": "workspace:*"
"@storybook/jest": "npm:next"
"@storybook/manager-api": "workspace:*"
@ -4881,7 +4909,7 @@ __metadata:
"@storybook/components": "workspace:*"
"@storybook/core-events": "workspace:*"
"@storybook/global": "npm:^5.0.0"
"@storybook/icons": "npm:^1.2.1"
"@storybook/icons": "npm:^1.2.3"
"@storybook/manager-api": "workspace:*"
"@storybook/preview-api": "workspace:*"
"@storybook/types": "workspace:*"
@ -4900,7 +4928,7 @@ __metadata:
"@storybook/components": "workspace:*"
"@storybook/core-events": "workspace:*"
"@storybook/global": "npm:^5.0.0"
"@storybook/icons": "npm:^1.2.1"
"@storybook/icons": "npm:^1.2.3"
"@storybook/manager-api": "workspace:*"
"@storybook/preview-api": "workspace:*"
"@storybook/types": "workspace:*"
@ -4940,7 +4968,7 @@ __metadata:
"@storybook/client-logger": "workspace:*"
"@storybook/components": "workspace:*"
"@storybook/core-events": "workspace:*"
"@storybook/icons": "npm:^1.2.1"
"@storybook/icons": "npm:^1.2.3"
"@storybook/manager-api": "workspace:*"
"@storybook/preview-api": "workspace:*"
"@storybook/theming": "workspace:*"
@ -4973,7 +5001,7 @@ __metadata:
"@storybook/components": "workspace:*"
"@storybook/core-events": "workspace:*"
"@storybook/global": "npm:^5.0.0"
"@storybook/icons": "npm:^1.2.1"
"@storybook/icons": "npm:^1.2.3"
"@storybook/manager-api": "workspace:*"
"@storybook/preview-api": "workspace:*"
"@storybook/theming": "workspace:*"
@ -5092,7 +5120,7 @@ __metadata:
"@storybook/csf": "npm:^0.1.2"
"@storybook/docs-tools": "workspace:*"
"@storybook/global": "npm:^5.0.0"
"@storybook/icons": "npm:^1.2.1"
"@storybook/icons": "npm:^1.2.3"
"@storybook/manager-api": "workspace:*"
"@storybook/preview-api": "workspace:*"
"@storybook/test": "workspace:*"
@ -5103,7 +5131,7 @@ __metadata:
color-convert: "npm:^2.0.1"
dequal: "npm:^2.0.2"
lodash: "npm:^4.17.21"
markdown-to-jsx: "npm:^7.1.8"
markdown-to-jsx: "npm:7.3.2"
memoizerific: "npm:^1.11.3"
polished: "npm:^4.2.2"
react-colorful: "npm:^5.1.2"
@ -5362,7 +5390,7 @@ __metadata:
"@storybook/client-logger": "workspace:*"
"@storybook/csf": "npm:^0.1.2"
"@storybook/global": "npm:^5.0.0"
"@storybook/icons": "npm:^1.2.1"
"@storybook/icons": "npm:^1.2.3"
"@storybook/test": "workspace:*"
"@storybook/theming": "workspace:*"
"@storybook/types": "workspace:*"
@ -5693,7 +5721,7 @@ __metadata:
languageName: unknown
linkType: soft
"@storybook/icons@npm:^1.2.1":
"@storybook/icons@npm:^1.2.3":
version: 1.2.3
resolution: "@storybook/icons@npm:1.2.3"
peerDependencies:
@ -5799,7 +5827,7 @@ __metadata:
"@storybook/components": "workspace:*"
"@storybook/core-events": "workspace:*"
"@storybook/global": "npm:^5.0.0"
"@storybook/icons": "npm:^1.2.1"
"@storybook/icons": "npm:^1.2.3"
"@storybook/manager-api": "workspace:*"
"@storybook/router": "workspace:*"
"@storybook/test": "workspace:*"
@ -5814,7 +5842,7 @@ __metadata:
fs-extra: "npm:^11.1.0"
fuse.js: "npm:^3.6.1"
lodash: "npm:^4.17.21"
markdown-to-jsx: "npm:^7.1.8"
markdown-to-jsx: "npm:7.3.2"
memoizerific: "npm:^1.11.3"
polished: "npm:^4.2.2"
qs: "npm:^6.10.0"
@ -6105,7 +6133,6 @@ __metadata:
memoizerific: "npm:^1.11.3"
qs: "npm:^6.10.0"
slash: "npm:^5.0.0"
synchronous-promise: "npm:^2.0.15"
ts-dedent: "npm:^2.0.0"
util-deprecate: "npm:^1.0.2"
languageName: unknown
@ -8184,7 +8211,17 @@ __metadata:
languageName: node
linkType: hard
"@urql/core@npm:>=4.1.0, @urql/core@npm:^4.1.0":
"@urql/core@npm:>=4.1.0":
version: 4.2.2
resolution: "@urql/core@npm:4.2.2"
dependencies:
"@0no-co/graphql.web": "npm:^1.0.1"
wonka: "npm:^6.3.2"
checksum: f1db8f3ca6e0c8e6257a8eb5b7057959a47086a7390af1329ef509f04a6d15f4fb42f9e990bd519526abff504a5524b5b89babdff01d5d61d800ed23ca87c067
languageName: node
linkType: hard
"@urql/core@npm:^4.1.0":
version: 4.2.0
resolution: "@urql/core@npm:4.2.0"
dependencies:
@ -8428,6 +8465,19 @@ __metadata:
languageName: node
linkType: hard
"@vue/compiler-core@npm:3.4.5":
version: 3.4.5
resolution: "@vue/compiler-core@npm:3.4.5"
dependencies:
"@babel/parser": "npm:^7.23.6"
"@vue/shared": "npm:3.4.5"
entities: "npm:^4.5.0"
estree-walker: "npm:^2.0.2"
source-map-js: "npm:^1.0.2"
checksum: 31a4a431d515eb9b3783bc50ac08af9ca32be703453ed80a569ac29a83e896d7d6f876803db24eea9df0fbe2fc7e7e0fdf51013a6f6897ca7feb685583ae04d3
languageName: node
linkType: hard
"@vue/compiler-core@npm:^3.0.0":
version: 3.3.13
resolution: "@vue/compiler-core@npm:3.3.13"
@ -8450,7 +8500,7 @@ __metadata:
languageName: node
linkType: hard
"@vue/compiler-dom@npm:3.3.11, @vue/compiler-dom@npm:^3.2.0, @vue/compiler-dom@npm:^3.3.0":
"@vue/compiler-dom@npm:3.3.11":
version: 3.3.11
resolution: "@vue/compiler-dom@npm:3.3.11"
dependencies:
@ -8460,6 +8510,16 @@ __metadata:
languageName: node
linkType: hard
"@vue/compiler-dom@npm:3.4.5, @vue/compiler-dom@npm:^3.2.0, @vue/compiler-dom@npm:^3.3.0":
version: 3.4.5
resolution: "@vue/compiler-dom@npm:3.4.5"
dependencies:
"@vue/compiler-core": "npm:3.4.5"
"@vue/shared": "npm:3.4.5"
checksum: a2f8703792c97e4949d9ffce9de0d2d40a8d09d8412017c8f69fa4aab4f4dd46a2d10138bae3bdeab12fca4bf67d6a8e23114ca592f7d9a577f3b745fa7191a2
languageName: node
linkType: hard
"@vue/compiler-sfc@npm:3.0.0":
version: 3.0.0
resolution: "@vue/compiler-sfc@npm:3.0.0"
@ -8486,7 +8546,7 @@ __metadata:
languageName: node
linkType: hard
"@vue/compiler-sfc@npm:3.3.11, @vue/compiler-sfc@npm:^3.2.0, @vue/compiler-sfc@npm:^3.2.33":
"@vue/compiler-sfc@npm:3.3.11":
version: 3.3.11
resolution: "@vue/compiler-sfc@npm:3.3.11"
dependencies:
@ -8504,6 +8564,23 @@ __metadata:
languageName: node
linkType: hard
"@vue/compiler-sfc@npm:^3.2.0, @vue/compiler-sfc@npm:^3.2.33":
version: 3.4.5
resolution: "@vue/compiler-sfc@npm:3.4.5"
dependencies:
"@babel/parser": "npm:^7.23.6"
"@vue/compiler-core": "npm:3.4.5"
"@vue/compiler-dom": "npm:3.4.5"
"@vue/compiler-ssr": "npm:3.4.5"
"@vue/shared": "npm:3.4.5"
estree-walker: "npm:^2.0.2"
magic-string: "npm:^0.30.5"
postcss: "npm:^8.4.32"
source-map-js: "npm:^1.0.2"
checksum: fa3f0a64e2afa02836d188a9bb0314f51fd1b45bf8609d6fe401bee97edd492775dabf3da61d93a10a86a0eb2c794ef994cc2a7f8cd6b96de8a4ab0e52f6b126
languageName: node
linkType: hard
"@vue/compiler-ssr@npm:3.0.0":
version: 3.0.0
resolution: "@vue/compiler-ssr@npm:3.0.0"
@ -8524,6 +8601,16 @@ __metadata:
languageName: node
linkType: hard
"@vue/compiler-ssr@npm:3.4.5":
version: 3.4.5
resolution: "@vue/compiler-ssr@npm:3.4.5"
dependencies:
"@vue/compiler-dom": "npm:3.4.5"
"@vue/shared": "npm:3.4.5"
checksum: 4d62fb820e3794f44c16fc61d29544755a1398ced7ee11a38a67ad25b2e114bb0ae6e7066f0501103e31b88e04c49bbf82fbfb2a6735bbc9a186b579789ef906
languageName: node
linkType: hard
"@vue/language-core@npm:1.8.15":
version: 1.8.15
resolution: "@vue/language-core@npm:1.8.15"
@ -8637,7 +8724,7 @@ __metadata:
languageName: node
linkType: hard
"@vue/shared@npm:3.3.11, @vue/shared@npm:^3.3.0":
"@vue/shared@npm:3.3.11":
version: 3.3.11
resolution: "@vue/shared@npm:3.3.11"
checksum: 164b904ed6c5a6e9b0d4c6ac38a214770943d5b1196e8af7193f7f9f099706ffd7627e40c95a9b4de430ff7d3523a713c9236ef6e8ca48dd6865606efb7208f5
@ -8651,6 +8738,13 @@ __metadata:
languageName: node
linkType: hard
"@vue/shared@npm:3.4.5, @vue/shared@npm:^3.3.0":
version: 3.4.5
resolution: "@vue/shared@npm:3.4.5"
checksum: 4bd4f6a6369ab02b8a01ac3b93fb5d580d3ea0e9781dd2be8ab676b50521733acbc85fac6b48bc9cd3704dc9237d6365148d287da51c07e1d0568d6c0f7742a1
languageName: node
linkType: hard
"@vue/typescript@npm:1.8.15":
version: 1.8.15
resolution: "@vue/typescript@npm:1.8.15"
@ -8959,14 +9053,7 @@ __metadata:
languageName: node
linkType: hard
"acorn-walk@npm:^8.1.1":
version: 8.2.0
resolution: "acorn-walk@npm:8.2.0"
checksum: dbe92f5b2452c93e960c5594e666dd1fae141b965ff2cb4a1e1d0381e3e4db4274c5ce4ffa3d681a86ca2a8d4e29d5efc0670a08e23fd2800051ea387df56ca2
languageName: node
linkType: hard
"acorn-walk@npm:^8.3.0":
"acorn-walk@npm:^8.1.1, acorn-walk@npm:^8.3.0":
version: 8.3.1
resolution: "acorn-walk@npm:8.3.1"
checksum: a23d2f7c6b6cad617f4c77f14dfeb062a239208d61753e9ba808d916c550add92b39535467d2e6028280761ac4f5a904cc9df21530b84d3f834e3edef74ddde5
@ -9840,20 +9927,7 @@ __metadata:
languageName: node
linkType: hard
"babel-plugin-polyfill-corejs2@npm:^0.4.6":
version: 0.4.6
resolution: "babel-plugin-polyfill-corejs2@npm:0.4.6"
dependencies:
"@babel/compat-data": "npm:^7.22.6"
"@babel/helper-define-polyfill-provider": "npm:^0.4.3"
semver: "npm:^6.3.1"
peerDependencies:
"@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0
checksum: 64a98811f343492aa6970ab253760194e389c0417e5b830522f944009c1f0c78e1251975fd1b9869cd48cc4623111b20a3389cf6732a1d10ba0d19de6fa5114f
languageName: node
linkType: hard
"babel-plugin-polyfill-corejs2@npm:^0.4.7":
"babel-plugin-polyfill-corejs2@npm:^0.4.6, babel-plugin-polyfill-corejs2@npm:^0.4.7":
version: 0.4.7
resolution: "babel-plugin-polyfill-corejs2@npm:0.4.7"
dependencies:
@ -9866,19 +9940,7 @@ __metadata:
languageName: node
linkType: hard
"babel-plugin-polyfill-corejs3@npm:^0.8.5":
version: 0.8.6
resolution: "babel-plugin-polyfill-corejs3@npm:0.8.6"
dependencies:
"@babel/helper-define-polyfill-provider": "npm:^0.4.3"
core-js-compat: "npm:^3.33.1"
peerDependencies:
"@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0
checksum: 97d974c1dfbefdf27866e21a1ac757f6ab1626379b544d6f8ddb05f7bfa02173f8347b6140295b0f770394549f9321775d3048e466a9a02b99b88ad5f0346858
languageName: node
linkType: hard
"babel-plugin-polyfill-corejs3@npm:^0.8.7":
"babel-plugin-polyfill-corejs3@npm:^0.8.5, babel-plugin-polyfill-corejs3@npm:^0.8.7":
version: 0.8.7
resolution: "babel-plugin-polyfill-corejs3@npm:0.8.7"
dependencies:
@ -9890,18 +9952,7 @@ __metadata:
languageName: node
linkType: hard
"babel-plugin-polyfill-regenerator@npm:^0.5.3":
version: 0.5.3
resolution: "babel-plugin-polyfill-regenerator@npm:0.5.3"
dependencies:
"@babel/helper-define-polyfill-provider": "npm:^0.4.3"
peerDependencies:
"@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0
checksum: cc32313b9ebbf1d7bedc33524a861136b9e5d3b6e9be317ac360a1c2a59ae5ed1b465a6c68b2715cdefb089780ddfb0c11f4a148e49827a947beee76e43da598
languageName: node
linkType: hard
"babel-plugin-polyfill-regenerator@npm:^0.5.4":
"babel-plugin-polyfill-regenerator@npm:^0.5.3, babel-plugin-polyfill-regenerator@npm:^0.5.4":
version: 0.5.4
resolution: "babel-plugin-polyfill-regenerator@npm:0.5.4"
dependencies:
@ -11882,7 +11933,7 @@ __metadata:
languageName: node
linkType: hard
"cosmiconfig@npm:^8.2.0":
"cosmiconfig@npm:^8.2.0, cosmiconfig@npm:^8.3.5":
version: 8.3.6
resolution: "cosmiconfig@npm:8.3.6"
dependencies:
@ -13338,7 +13389,7 @@ __metadata:
languageName: node
linkType: hard
"entities@npm:^4.2.0, entities@npm:^4.3.0, entities@npm:^4.4.0":
"entities@npm:^4.2.0, entities@npm:^4.3.0, entities@npm:^4.4.0, entities@npm:^4.5.0":
version: 4.5.0
resolution: "entities@npm:4.5.0"
checksum: 5b039739f7621f5d1ad996715e53d964035f75ad3b9a4d38c6b3804bb226e282ffeae2443624d8fdd9c47d8e926ae9ac009c54671243f0c3294c26af7cc85250
@ -15027,7 +15078,17 @@ __metadata:
languageName: node
linkType: hard
"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.0, follow-redirects@npm:^1.15.0":
"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.0":
version: 1.15.4
resolution: "follow-redirects@npm:1.15.4"
peerDependenciesMeta:
debug:
optional: true
checksum: 5f37ed9170c9eb19448c5418fdb0f2b73f644b5364834e70791a76ecc7db215246f9773bbef4852cfae4067764ffc852e047f744b661b0211532155b73556a6a
languageName: node
linkType: hard
"follow-redirects@npm:^1.15.0":
version: 1.15.3
resolution: "follow-redirects@npm:1.15.3"
peerDependenciesMeta:
@ -15502,7 +15563,7 @@ __metadata:
languageName: node
linkType: hard
"get-func-name@npm:^2.0.0, get-func-name@npm:^2.0.1, get-func-name@npm:^2.0.2":
"get-func-name@npm:^2.0.1, get-func-name@npm:^2.0.2":
version: 2.0.2
resolution: "get-func-name@npm:2.0.2"
checksum: 89830fd07623fa73429a711b9daecdb304386d237c71268007f788f113505ef1d4cc2d0b9680e072c5082490aec9df5d7758bf5ac6f1c37062855e8e3dc0b9df
@ -17669,14 +17730,7 @@ __metadata:
languageName: node
linkType: hard
"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.0":
version: 3.2.0
resolution: "istanbul-lib-coverage@npm:3.2.0"
checksum: 10ecb00a50cac2f506af8231ce523ffa1ac1310db0435c8ffaabb50c1d72539906583aa13c84f8835dc103998b9989edc3c1de989d2e2a96a91a9ba44e5db6b9
languageName: node
linkType: hard
"istanbul-lib-coverage@npm:^3.2.2":
"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.0, istanbul-lib-coverage@npm:^3.2.2":
version: 3.2.2
resolution: "istanbul-lib-coverage@npm:3.2.2"
checksum: 6c7ff2106769e5f592ded1fb418f9f73b4411fd5a084387a5410538332b6567cd1763ff6b6cadca9b9eb2c443cce2f7ea7d7f1b8d315f9ce58539793b1e0922b
@ -17981,12 +18035,12 @@ __metadata:
languageName: node
linkType: hard
"jiti@npm:^1.18.2":
version: 1.20.0
resolution: "jiti@npm:1.20.0"
"jiti@npm:^1.18.2, jiti@npm:^1.20.0":
version: 1.21.0
resolution: "jiti@npm:1.21.0"
bin:
jiti: bin/jiti.js
checksum: e71999db5e436d38c32ca713c3688b5da2a686f264584d927dcca80a4eaece83af7dd32c047524e74084bb11bdfa148f5f91b7e9a0044b4803feffe3c2c30dbc
checksum: 7f361219fe6c7a5e440d5f1dba4ab763a5538d2df8708cdc22561cf25ea3e44b837687931fca7cdd8cdd9f567300e90be989dd1321650045012d8f9ed6aab07f
languageName: node
linkType: hard
@ -19049,16 +19103,7 @@ __metadata:
languageName: node
linkType: hard
"loupe@npm:^2.3.6":
version: 2.3.6
resolution: "loupe@npm:2.3.6"
dependencies:
get-func-name: "npm:^2.0.0"
checksum: a974841ce94ef2a35aac7144e7f9e789e3887f82286cd9ffe7ff00f2ac9d117481989948657465e2b0b102f23136d89ae0a18fd4a32d9015012cd64464453289
languageName: node
linkType: hard
"loupe@npm:^2.3.7":
"loupe@npm:^2.3.6, loupe@npm:^2.3.7":
version: 2.3.7
resolution: "loupe@npm:2.3.7"
dependencies:
@ -19347,7 +19392,7 @@ __metadata:
languageName: node
linkType: hard
"markdown-to-jsx@npm:^7.1.8":
"markdown-to-jsx@npm:7.3.2":
version: 7.3.2
resolution: "markdown-to-jsx@npm:7.3.2"
peerDependencies:
@ -22983,7 +23028,7 @@ __metadata:
languageName: node
linkType: hard
"postcss-loader@npm:7.3.3, postcss-loader@npm:^7.0.2":
"postcss-loader@npm:7.3.3":
version: 7.3.3
resolution: "postcss-loader@npm:7.3.3"
dependencies:
@ -22997,6 +23042,20 @@ __metadata:
languageName: node
linkType: hard
"postcss-loader@npm:^7.0.2":
version: 7.3.4
resolution: "postcss-loader@npm:7.3.4"
dependencies:
cosmiconfig: "npm:^8.3.5"
jiti: "npm:^1.20.0"
semver: "npm:^7.5.4"
peerDependencies:
postcss: ^7.0.0 || ^8.0.1
webpack: ^5.0.0
checksum: 1bf7614aeea9ad1f8ee6be3a5451576c059391688ea67f825aedc2674056369597faeae4e4a81fe10843884c9904a71403d9a54197e1f560e8fbb9e61f2a2680
languageName: node
linkType: hard
"postcss-modules-extract-imports@npm:^2.0.0":
version: 2.0.0
resolution: "postcss-modules-extract-imports@npm:2.0.0"
@ -23631,14 +23690,7 @@ __metadata:
languageName: node
linkType: hard
"punycode@npm:^2.1.0, punycode@npm:^2.1.1":
version: 2.3.0
resolution: "punycode@npm:2.3.0"
checksum: 8e6f7abdd3a6635820049e3731c623bbef3fedbf63bbc696b0d7237fdba4cefa069bc1fa62f2938b0fbae057550df7b5318f4a6bcece27f1907fc75c54160bee
languageName: node
linkType: hard
"punycode@npm:^2.3.1":
"punycode@npm:^2.1.0, punycode@npm:^2.1.1, punycode@npm:^2.3.1":
version: 2.3.1
resolution: "punycode@npm:2.3.1"
checksum: 14f76a8206bc3464f794fb2e3d3cc665ae416c01893ad7a02b23766eb07159144ee612ad67af5e84fa4479ccfe67678c4feb126b0485651b302babf66f04f9e9
@ -27124,13 +27176,6 @@ __metadata:
languageName: node
linkType: hard
"synchronous-promise@npm:^2.0.15":
version: 2.0.17
resolution: "synchronous-promise@npm:2.0.17"
checksum: 1babe643d8417789ef6e5a2f3d4b8abcda2de236acd09bbe2c98f6be82c0a2c92ed21a6e4f934845fa8de18b1435a9cba1e8c3d945032e8a532f076224c024b1
languageName: node
linkType: hard
"tapable@npm:^2.0.0, tapable@npm:^2.1.1, tapable@npm:^2.2.0, tapable@npm:^2.2.1":
version: 2.2.1
resolution: "tapable@npm:2.2.1"
@ -27272,7 +27317,7 @@ __metadata:
languageName: node
linkType: hard
"terser@npm:5.24.0, terser@npm:^5.10.0, terser@npm:^5.16.8":
"terser@npm:5.24.0, terser@npm:^5.16.8":
version: 5.24.0
resolution: "terser@npm:5.24.0"
dependencies:
@ -27286,6 +27331,20 @@ __metadata:
languageName: node
linkType: hard
"terser@npm:^5.10.0":
version: 5.26.0
resolution: "terser@npm:5.26.0"
dependencies:
"@jridgewell/source-map": "npm:^0.3.3"
acorn: "npm:^8.8.2"
commander: "npm:^2.20.0"
source-map-support: "npm:~0.5.20"
bin:
terser: bin/terser
checksum: 3906289c6bacd75804a47a583cdafefbd76c5edb39435369755c7b1592e57586fb2f4bddf6eb37a807d6e782171dbf0aa7bbdc80fd5b77b2f2b62196cac49b62
languageName: node
linkType: hard
"test-exclude@npm:^6.0.0":
version: 6.0.0
resolution: "test-exclude@npm:6.0.0"
@ -27896,11 +27955,11 @@ __metadata:
"typescript@patch:typescript@npm%3A^5.0.3#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.3.2#optional!builtin<compat/typescript>":
version: 5.3.3
resolution: "typescript@patch:typescript@npm%3A5.3.3#optional!builtin<compat/typescript>::version=5.3.3&hash=29ae49"
resolution: "typescript@patch:typescript@npm%3A5.3.3#optional!builtin<compat/typescript>::version=5.3.3&hash=e012d7"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: e22df47df9b2b2f2617b8bf511a29aea3d177f9f7a0756818230a76b01cbd7da988bf55f9463aaa1a4c1ff90b80f8dc5676460d4e9dfc010572cbba59b822b0c
checksum: 1d0a5f4ce496c42caa9a30e659c467c5686eae15d54b027ee7866744952547f1be1262f2d40de911618c242b510029d51d43ff605dba8fb740ec85ca2d3f9500
languageName: node
linkType: hard

View File

@ -124,14 +124,14 @@ Provides HTML class(es) to the preview element, for custom styling.
### `layout`
Type: `'padded' | 'centered' | 'fullscreen'`
Type: `'centered' | 'fullscreen' | 'padded'`
Default: `parameters.layout` or `parameters.docs.canvas.layout` or `'padded'`
Specifies how the canvas should layout the story.
- **padded**: Add padding to the story
- **centered**: Center the story within the canvas
- **padded**: (default) Add padding to the story
- **fullscreen**: Show the story as-is, without padding
In addition to the `parameters.docs.canvas.layout` property or the `layout` prop, the `Canvas` block will respect the `parameters.layout` value that defines [how a story is laid out](../configure/story-layout.md) in the regular story view.

View File

@ -79,6 +79,12 @@ An overview of all available API references for Storybook.
about args that are not explicitly set.
</td>
</tr>
<tr>
<td><a href="../api/parameters">Parameters</a></td>
<td>
Parameters are static metadata used to configure your <a href="../get-started/whats-a-story.md">stories</a> <a href="../addons/introduction.md">addons</a> in Storybook. They are specified at the story, meta (component), project (global) levels.
</td>
</tr>
</tbody>
</table>

View File

@ -9,34 +9,13 @@ Type:
```ts
{
argTypeTargetsV7?: boolean;
buildStoriesJson?: boolean;
legacyDecoratorFileOrder?: boolean;
legacyMdx1?: boolean;
storyStoreV7?: boolean;
}
```
Enables Storybook's additional features.
## `buildStoriesJson`
Type: `boolean`
Default: `true`, when [`storyStoreV7`](#storystorev7) is `true`
Generates a `index.json` and `stories.json` files to help story loading with the on-demand mode.
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/main-config-features-build-stories-json.js.mdx',
'common/main-config-features-build-stories-json.ts.mdx',
]}
/>
<!-- prettier-ignore-end -->
## `legacyDecoratorFileOrder`
Type: `boolean`
@ -54,25 +33,6 @@ Apply decorators from preview.js before decorators from addons or frameworks. [M
<!-- prettier-ignore-end -->
## `storyStoreV7`
Type: `boolean`
Default: `true`
Opts out of [on-demand story loading](#on-demand-story-loading); loads all stories at build time.
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/main-config-features-story-store-v7.js.mdx',
'common/main-config-features-story-store-v7.ts.mdx',
]}
/>
<!-- prettier-ignore-end -->
## `argTypeTargetsV7`
(⚠️ **Experimental**)
@ -91,25 +51,3 @@ Filter args with a "target" on the type from the render function.
/>
<!-- prettier-ignore-end -->
## On-demand story loading
As your Storybook grows, it gets challenging to load all of your stories performantly, slowing down the loading times and yielding a large bundle. Out of the box, Storybook loads your stories on demand rather than during boot-up to improve the performance of your Storybook. If you need to load all of your stories during boot-up, you can disable this feature by setting the `storyStoreV7` feature flag to `false` in your configuration as follows:
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/main-config-features-story-store-v7.js.mdx',
'common/main-config-features-story-store-v7.ts.mdx',
]}
/>
<!-- prettier-ignore-end -->
### Known limitations
Because of the way stories are currently indexed in Storybook, loading stories on demand with `storyStoreV7` has a couple of minor limitations at the moment:
- [CSF formats](../api/csf.md) from version 1 to version 3 are supported. The `storiesOf` construct is not.
- Custom [`storySort` functions](../writing-stories/naming-components-and-hierarchy.md#sorting-stories) receive more limited arguments.

View File

@ -117,7 +117,7 @@ When [auto-titling](../configure/sidebar-and-urls.md#csf-30-auto-titles), prefix
<Callout variant="info" icon="💡">
With [`storyStoreV7`](./main-config-features.md#storystorev7) (the default in Storybook 7), Storybook now statically analyzes the configuration file to improve performance. Loading stories with a custom implementation may de-optimize or break this ability.
💡 Storybook now statically analyzes the configuration file to improve performance. Loading stories with a custom implementation may de-optimize or break this ability.
</Callout>

Some files were not shown because too many files have changed in this diff Show More