Merge remote-tracking branch 'origin/next' into valentin/remove-preact-webpack-support

This commit is contained in:
Valentin Palkovic 2025-03-28 16:17:29 +01:00
commit 4740a15e69
95 changed files with 226 additions and 154 deletions

View File

@ -30,6 +30,7 @@
- [Angular = Require v18 and up](#angular--require-v18-and-up)
- [Next.js = Require v14 and up](#nextjs--require-v14-and-up)
- [Preact = Dropped webpack5 builder support](#preact--dropped-webpack5-builder-support)
- [Next.js = Vite builder stabilized](#nextjs--vite-builder-stabilized)
- [From version 8.5.x to 8.6.x](#from-version-85x-to-86x)
- [Angular: Support experimental zoneless support](#angular-support-experimental-zoneless-support)
- [Framework-specific Vite plugins have to be explicitly added](#framework-specific-vite-plugins-have-to-be-explicitly-added)
@ -843,6 +844,30 @@ export default {
};
```
#### Next.js = Vite builder stabilized
The experimental Next.js Vite builder (`@storybook/experimental-nextjs-vite`) has been stabilized and renamed to `@storybook/nextjs-vite`. If you were using the experimental package, you should update your dependencies to use the new stable package name.
```diff
{
"dependencies": {
- "@storybook/experimental-nextjs-vite": "^x.x.x"
+ "@storybook/nextjs-vite": "^9.0.0"
}
}
```
Also update your `.storybook/main.<js|ts>` file accordingly:
```diff
export default {
addons: [
- "@storybook/experimental-nextjs-vite",
+ "@storybook/nextjs-vite"
]
}
```
## From version 8.5.x to 8.6.x
### Angular: Support experimental zoneless support

View File

@ -18,7 +18,7 @@ export const COVERAGE_DIRECTORY = 'coverage';
export const SUPPORTED_FRAMEWORKS = [
'@storybook/nextjs',
'@storybook/experimental-nextjs-vite',
'@storybook/nextjs-vite',
'@storybook/sveltekit',
];

View File

@ -72,12 +72,12 @@ export default async function postInstall(options: PostinstallOptions) {
if (info.frameworkPackageName === '@storybook/nextjs' && !hasCustomWebpackConfig) {
const out =
options.yes || !isInteractive
? { migrateToExperimentalNextjsVite: !!options.yes }
? { migrateToNextjsVite: !!options.yes }
: await prompts({
type: 'confirm',
name: 'migrateToExperimentalNextjsVite',
name: 'migrateToNextjsVite',
message: dedent`
The addon requires the use of @storybook/experimental-nextjs-vite to work with Next.js.
The addon requires the use of @storybook/nextjs-vite to work with Next.js.
https://storybook.js.org/docs/writing-tests/test-addon#install-and-set-up
Do you want to migrate?
@ -85,9 +85,9 @@ export default async function postInstall(options: PostinstallOptions) {
initial: true,
});
if (out.migrateToExperimentalNextjsVite) {
if (out.migrateToNextjsVite) {
await packageManager.addDependencies({ installAsDevDependencies: true }, [
`@storybook/experimental-nextjs-vite@${versions['@storybook/experimental-nextjs-vite']}`,
`@storybook/nextjs-vite@${versions['@storybook/nextjs-vite']}`,
]);
await packageManager.removeDependencies({}, ['@storybook/nextjs']);
@ -96,21 +96,21 @@ export default async function postInstall(options: PostinstallOptions) {
traverse(config._ast, {
StringLiteral(path) {
if (path.node.value === '@storybook/nextjs') {
path.node.value = '@storybook/experimental-nextjs-vite';
path.node.value = '@storybook/nextjs-vite';
}
},
});
await writeConfig(config, mainJsPath);
info.frameworkPackageName = '@storybook/experimental-nextjs-vite';
info.frameworkPackageName = '@storybook/nextjs-vite';
info.builderPackageName = '@storybook/builder-vite';
}
}
const annotationsImport = SUPPORTED_FRAMEWORKS.includes(info.frameworkPackageName)
? info.frameworkPackageName === '@storybook/nextjs'
? '@storybook/experimental-nextjs-vite'
? '@storybook/nextjs-vite'
: info.frameworkPackageName
: info.rendererPackageName && SUPPORTED_RENDERERS.includes(info.rendererPackageName)
? info.rendererPackageName
@ -215,18 +215,16 @@ export default async function postInstall(options: PostinstallOptions) {
dedent`
It looks like you're using Next.js.
Adding ${picocolors.bold(colors.pink(`@storybook/experimental-nextjs-vite/vite-plugin`))} so you can use it with Vitest.
Adding ${picocolors.bold(colors.pink(`@storybook/nextjs-vite/vite-plugin`))} so you can use it with Vitest.
More info about the plugin at ${picocolors.cyan(`https://github.com/storybookjs/vite-plugin-storybook-nextjs`)}
`
);
try {
const storybookVersion = await packageManager.getInstalledVersion('storybook');
dependencies.push(`@storybook/experimental-nextjs-vite@^${storybookVersion}`);
dependencies.push(`@storybook/nextjs-vite@^${storybookVersion}`);
} catch (e) {
console.error(
'Failed to install @storybook/experimental-nextjs-vite. Please install it manually'
);
console.error('Failed to install @storybook/nextjs-vite. Please install it manually');
}
}

View File

@ -50,7 +50,7 @@ export const experimental_serverChannel = async (channel: Channel, options: Opti
if (!builderName?.includes('vite')) {
if (framework.includes('nextjs')) {
log(dedent`
You're using ${framework}, which is a Webpack-based builder. In order to use Storybook Test, with your project, you need to use '@storybook/experimental-nextjs-vite', a high performance Vite-based equivalent.
You're using ${framework}, which is a Webpack-based builder. In order to use Storybook Test, with your project, you need to use '@storybook/nextjs-vite', a high performance Vite-based equivalent.
Information on how to upgrade here: ${picocolors.yellow('https://storybook.js.org/docs/get-started/frameworks/nextjs#with-vite')}\n
`);

View File

@ -30,7 +30,7 @@ const INCLUDE_CANDIDATES = [
'@storybook/addon-themes',
'@storybook/addon-themes/preview',
'@storybook/blocks',
'@storybook/experimental-nextjs-vite/dist/preview.mjs',
'@storybook/nextjs-vite/dist/preview.mjs',
'@storybook/html',
'@storybook/html/dist/entry-preview-docs.mjs',
'@storybook/html/dist/entry-preview.mjs',

View File

@ -152,7 +152,7 @@ export const frameworkToDefaultBuilder: Record<
'html-webpack5': CoreBuilder.Webpack5,
nextjs: CoreBuilder.Webpack5,
nuxt: CoreBuilder.Vite,
'experimental-nextjs-vite': CoreBuilder.Vite,
'nextjs-vite': CoreBuilder.Vite,
'preact-vite': CoreBuilder.Vite,
qwik: CoreBuilder.Vite,
'react-native-web-vite': CoreBuilder.Vite,

View File

@ -11,7 +11,7 @@ export const frameworkToRenderer: Record<
'html-vite': 'html',
'html-webpack5': 'html',
nextjs: 'react',
'experimental-nextjs-vite': 'react',
'nextjs-vite': 'react',
'preact-vite': 'preact',
qwik: 'qwik',
'react-vite': 'react',

View File

@ -40,7 +40,7 @@ export const frameworkPackages: Record<string, SupportedFrameworks> = {
'@storybook/svelte-webpack5': 'svelte-webpack5',
'@storybook/sveltekit': 'sveltekit',
'@storybook/vue3-vite': 'vue3-vite',
'@storybook/experimental-nextjs-vite': 'experimental-nextjs-vite',
'@storybook/nextjs-vite': 'nextjs-vite',
'@storybook/react-native-web-vite': 'react-native-web-vite',
'@storybook/vue3-webpack5': 'vue3-webpack5',
'@storybook/web-components-vite': 'web-components-vite',

View File

@ -19,10 +19,10 @@ export default {
storybook: '9.0.0-alpha.11',
'@storybook/angular': '9.0.0-alpha.11',
'@storybook/ember': '9.0.0-alpha.11',
'@storybook/experimental-nextjs-vite': '9.0.0-alpha.11',
'@storybook/html-vite': '9.0.0-alpha.11',
'@storybook/html-webpack5': '9.0.0-alpha.11',
'@storybook/nextjs': '9.0.0-alpha.11',
'@storybook/nextjs-vite': '9.0.0-alpha.11',
'@storybook/preact-vite': '9.0.0-alpha.11',
'@storybook/react-native-web-vite': '9.0.0-alpha.11',
'@storybook/react-vite': '9.0.0-alpha.11',

View File

@ -12,7 +12,7 @@ globalPackages.forEach((key) => {
globalThis.sendTelemetryError = (error) => {
if (!shouldSkipError(error)) {
const channel = global.__STORYBOOK_ADDONS_CHANNEL__;
const channel = globalThis.__STORYBOOK_ADDONS_CHANNEL__;
channel.emit(TELEMETRY_ERROR, prepareForTelemetry(error));
}
};
@ -20,9 +20,9 @@ globalThis.sendTelemetryError = (error) => {
// handle all uncaught errors at the root of the application and log to telemetry
globalThis.addEventListener('error', (args) => {
const error = args.error || args;
global.sendTelemetryError(error);
globalThis.sendTelemetryError(error);
});
globalThis.addEventListener('unhandledrejection', ({ reason }) => {
global.sendTelemetryError(reason);
globalThis.sendTelemetryError(reason);
});

View File

@ -2,10 +2,10 @@
export type SupportedFrameworks =
| 'angular'
| 'ember'
| 'experimental-nextjs-vite'
| 'html-vite'
| 'html-webpack5'
| 'nextjs'
| 'nextjs-vite'
| 'preact-vite'
| 'react-native-web-vite'
| 'react-vite'

View File

@ -1,5 +1,5 @@
{
"name": "@storybook/experimental-nextjs-vite",
"name": "@storybook/nextjs-vite",
"version": "9.0.0-alpha.11",
"description": "Storybook for Next.js and Vite",
"keywords": [
@ -7,7 +7,7 @@
"nextjs",
"vite"
],
"homepage": "https://github.com/storybookjs/storybook/tree/next/code/frameworks/experimental-nextjs-vite",
"homepage": "https://github.com/storybookjs/storybook/tree/next/code/frameworks/nextjs-vite",
"bugs": {
"url": "https://github.com/storybookjs/storybook/issues"
},

View File

@ -1,5 +1,5 @@
{
"name": "experimental-nextjs-vite",
"name": "nextjs-vite",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "library",
"targets": {

View File

@ -3,7 +3,7 @@
// is the only way to achieve it actually being a singleton
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore we must ignore types here as during compilation they are not generated yet
import { headers } from '@storybook/experimental-nextjs-vite/headers.mock';
import { headers } from '@storybook/nextjs-vite/headers.mock';
import { RequestCookies } from 'next/dist/compiled/@edge-runtime/cookies';
import { fn } from 'storybook/test';

View File

@ -15,7 +15,7 @@ export * from './types';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
declare module '@storybook/experimental-nextjs-vite/vite-plugin' {
declare module '@storybook/nextjs-vite/vite-plugin' {
export const storybookNextJsPlugin: typeof vitePluginStorybookNextJs;
}

View File

@ -34,7 +34,7 @@ import * as nextJsAnnotations from './preview';
*
* ```jsx
* // setup-file.js
* import { setProjectAnnotations } from '@storybook/experimental-nextjs-vite';
* import { setProjectAnnotations } from '@storybook/nextjs-vite';
* import projectAnnotations from './.storybook/preview';
*
* setProjectAnnotations(projectAnnotations);
@ -70,7 +70,7 @@ const INTERNAL_DEFAULT_PROJECT_ANNOTATIONS: ProjectAnnotations<ReactRenderer> =
*
* ```jsx
* import { render } from '@testing-library/react';
* import { composeStory } from '@storybook/experimental-nextjs-vite';
* import { composeStory } from '@storybook/nextjs-vite';
* import Meta, { Primary as PrimaryStory } from './Button.stories';
*
* const Primary = composeStory(PrimaryStory, Meta);
@ -114,7 +114,7 @@ export function composeStory<TArgs extends Args = Args>(
*
* ```jsx
* import { render } from '@testing-library/react';
* import { composeStories } from '@storybook/experimental-nextjs-vite';
* import { composeStories } from '@storybook/nextjs-vite';
* import * as stories from './Button.stories';
*
* const { Primary, Secondary } = composeStories(stories);

View File

@ -29,7 +29,7 @@ export const core: PresetProperty<'core'> = async (config, options) => {
};
export const previewAnnotations: PresetProperty<'previewAnnotations'> = (entry = []) => {
const nextDir = dirname(require.resolve('@storybook/experimental-nextjs-vite/package.json'));
const nextDir = dirname(require.resolve('@storybook/nextjs-vite/package.json'));
const result = [...entry, join(nextDir, 'dist/preview.mjs')];
return result;
};

View File

@ -6,10 +6,10 @@ import type { ReactRenderer, StoryFn } from '@storybook/react';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore we must ignore types here as during compilation they are not generated yet
import { createNavigation } from '@storybook/experimental-nextjs-vite/navigation.mock';
import { createNavigation } from '@storybook/nextjs-vite/navigation.mock';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore we must ignore types here as during compilation they are not generated yet
import { createRouter } from '@storybook/experimental-nextjs-vite/router.mock';
import { createRouter } from '@storybook/nextjs-vite/router.mock';
import { isNextRouterError } from 'next/dist/client/components/is-next-router-error';

View File

@ -5,7 +5,7 @@ import React, { useMemo } from 'react';
// is the only way to achieve it actually being a singleton
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore we must ignore types here as during compilation they are not generated yet
import { getRouter } from '@storybook/experimental-nextjs-vite/navigation.mock';
import { getRouter } from '@storybook/nextjs-vite/navigation.mock';
import type { FlightRouterState } from 'next/dist/server/app-render/types';
import {

View File

@ -6,7 +6,7 @@ import React from 'react';
// is the only way to achieve it actually being a singleton
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore we must ignore types here as during compilation they are not generated yet
import { getRouter } from '@storybook/experimental-nextjs-vite/router.mock';
import { getRouter } from '@storybook/nextjs-vite/router.mock';
import { RouterContext } from 'next/dist/shared/lib/router-context.shared-runtime';

View File

@ -5,7 +5,7 @@ import type { StorybookConfig as StorybookConfigReactVite } from '@storybook/rea
import type { NextRouter } from 'next/router';
type FrameworkName = CompatibleString<'@storybook/experimental-nextjs-vite'>;
type FrameworkName = CompatibleString<'@storybook/nextjs-vite'>;
type BuilderName = CompatibleString<'@storybook/builder-vite'>;
export type FrameworkOptions = {

View File

@ -1,4 +1,4 @@
import type { Meta, StoryObj } from '@storybook/experimental-nextjs-vite';
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
import { fn } from 'storybook/test';

View File

@ -1,4 +1,4 @@
import type { Meta, StoryObj } from '@storybook/experimental-nextjs-vite';
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
import { fn } from 'storybook/test';

View File

@ -1,4 +1,4 @@
import type { Meta, StoryObj } from '@storybook/experimental-nextjs-vite';
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
import { expect, userEvent, within } from 'storybook/test';

View File

@ -1,6 +1,6 @@
import React, { Suspense } from 'react';
import type { Meta, StoryObj } from '@storybook/experimental-nextjs-vite';
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
import dynamic from 'next/dynamic';

View File

@ -1,4 +1,4 @@
import type { Meta, StoryObj } from '@storybook/experimental-nextjs-vite';
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
import Font from './Font';

View File

@ -1,6 +1,6 @@
import React from 'react';
import type { Meta, StoryObj } from '@storybook/experimental-nextjs-vite';
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
import { type ImageProps, getImageProps } from 'next/image';

View File

@ -1,7 +1,7 @@
import React from 'react';
import type { Meta } from '@storybook/experimental-nextjs-vite';
import type { StoryObj } from '@storybook/experimental-nextjs-vite';
import type { Meta } from '@storybook/nextjs-vite';
import type { StoryObj } from '@storybook/nextjs-vite';
import Head from 'next/head';
import { expect, waitFor } from 'storybook/test';

View File

@ -1,6 +1,6 @@
import React, { useRef, useState } from 'react';
import type { Meta, StoryObj } from '@storybook/experimental-nextjs-vite';
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
import Image from 'next/image';

View File

@ -1,6 +1,6 @@
import React from 'react';
import type { Meta, StoryObj } from '@storybook/experimental-nextjs-vite';
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
import Image from 'next/legacy/image';

View File

@ -1,6 +1,6 @@
import React from 'react';
import type { Meta, StoryObj } from '@storybook/experimental-nextjs-vite';
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
import Link from 'next/link';

View File

@ -1,7 +1,7 @@
import React from 'react';
import type { Meta, StoryObj } from '@storybook/experimental-nextjs-vite';
import { getRouter } from '@storybook/experimental-nextjs-vite/navigation.mock';
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
import { getRouter } from '@storybook/nextjs-vite/navigation.mock';
import {
useParams,

View File

@ -1,6 +1,6 @@
import type { Meta } from '@storybook/experimental-nextjs-vite';
import type { StoryObj } from '@storybook/experimental-nextjs-vite';
import { cookies, headers } from '@storybook/experimental-nextjs-vite/headers.mock';
import type { Meta } from '@storybook/nextjs-vite';
import type { StoryObj } from '@storybook/nextjs-vite';
import { cookies, headers } from '@storybook/nextjs-vite/headers.mock';
import { expect, userEvent, within } from 'storybook/test';

View File

@ -1,7 +1,7 @@
/* eslint-disable local-rules/no-uncategorized-errors */
import React from 'react';
import type { Meta, StoryObj } from '@storybook/experimental-nextjs-vite';
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
import { Nested, RSC } from './RSC';

View File

@ -1,6 +1,6 @@
import React from 'react';
import type { Meta, StoryObj } from '@storybook/experimental-nextjs-vite';
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
import { redirect } from 'next/navigation';
import { userEvent, within } from 'storybook/test';

View File

@ -1,7 +1,7 @@
import React from 'react';
import type { Meta, StoryObj } from '@storybook/experimental-nextjs-vite';
import { getRouter } from '@storybook/experimental-nextjs-vite/router.mock';
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
import { getRouter } from '@storybook/nextjs-vite/router.mock';
import Router, { useRouter } from 'next/router';
import { expect, userEvent, within } from 'storybook/test';

View File

@ -1,9 +1,9 @@
import React from 'react';
import type { Meta, StoryObj } from '@storybook/experimental-nextjs-vite';
import { revalidatePath } from '@storybook/experimental-nextjs-vite/cache.mock';
import { cookies } from '@storybook/experimental-nextjs-vite/headers.mock';
import { getRouter, redirect } from '@storybook/experimental-nextjs-vite/navigation.mock';
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
import { revalidatePath } from '@storybook/nextjs-vite/cache.mock';
import { cookies } from '@storybook/nextjs-vite/headers.mock';
import { getRouter, redirect } from '@storybook/nextjs-vite/navigation.mock';
import { expect, userEvent, waitFor, within } from 'storybook/test';

View File

@ -1,6 +1,6 @@
import React from 'react';
import type { Meta, StoryObj } from '@storybook/experimental-nextjs-vite';
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
const Component = () => (
<div>

View File

@ -11,6 +11,16 @@ import {
transformPackageJsonFiles,
} from './consolidated-imports';
// mock picocolors yellow and cyan
vi.mock('picocolors', () => {
return {
default: {
cyan: (str: string) => str,
red: (str: string) => str,
},
};
});
vi.mock('node:fs/promises');
vi.mock('globby', () => ({
globby: vi.fn(),
@ -104,6 +114,33 @@ describe('check', () => {
});
});
describe('prompt', () => {
it('should return a prompt', () => {
const result = consolidatedImports.prompt({
packageJsonFiles: ['test/package.json'],
consolidatedDeps: new Set(['@storybook/core-common', '@storybook/experimental-nextjs-vite']),
});
expect(result).toMatchInlineSnapshot(`
"Found package.json files that contain consolidated or renamed Storybook packages that need to be updated:
- test/package.json
We will automatically rename the following packages:
- @storybook/core-common -> storybook/internal/common
- @storybook/experimental-nextjs-vite -> storybook/nextjs-vite
These packages have been renamed or consolidated into the main storybook package and should be removed.
The main storybook package will be added to devDependencies if not already present.
Would you like to:
1. Update these package.json files
2. Scan your codebase and update any imports from these updated packages
This will ensure your project is properly updated to use the new updated package structure and to use the latest package names."
`);
});
});
describe('transformPackageJsonFiles', () => {
it('should transform package.json files', async () => {
const contents = JSON.stringify(mockPackageJson);

View File

@ -2,6 +2,7 @@ import { readFile, writeFile } from 'node:fs/promises';
import { commonGlobOptions, getProjectRoot } from 'storybook/internal/common';
import picocolors from 'picocolors';
import prompts from 'prompts';
import { dedent } from 'ts-dedent';
@ -10,28 +11,23 @@ import type { Fix, RunOptions } from '../types';
export interface ConsolidatedOptions {
packageJsonFiles: string[];
consolidatedDeps: Set<keyof typeof consolidatedPackages>;
}
function transformPackageJson(content: string): string | null {
const packageJson = JSON.parse(content);
let hasChanges = false;
// Check dependencies
if (packageJson.dependencies) {
for (const [dep, version] of Object.entries(packageJson.dependencies)) {
if (dep in consolidatedPackages) {
delete packageJson.dependencies[dep];
hasChanges = true;
}
}
}
// Check both dependencies and devDependencies
const depTypes = ['dependencies', 'devDependencies'] as const;
// Check devDependencies
if (packageJson.devDependencies) {
for (const [dep, version] of Object.entries(packageJson.devDependencies)) {
if (dep in consolidatedPackages) {
delete packageJson.devDependencies[dep];
hasChanges = true;
for (const depType of depTypes) {
if (packageJson[depType]) {
for (const [dep] of Object.entries(packageJson[depType])) {
if (dep in consolidatedPackages) {
delete packageJson[depType][dep];
hasChanges = true;
}
}
}
}
@ -120,38 +116,63 @@ export const consolidatedImports: Fix<ConsolidatedOptions> = {
gitignore: true,
});
// check if any of the package.json files have consolidated packages
const hasConsolidatedDependencies = await Promise.all(
const consolidatedDeps = new Set<keyof typeof consolidatedPackages>();
const affectedPackageJSONFiles = new Set<string>();
// Check all package.json files for consolidated packages
await Promise.all(
packageJsonFiles.map(async (file) => {
const contents = await readFile(file, 'utf-8');
const packageJson = JSON.parse(contents);
return (
Object.keys(packageJson.dependencies || {}).some((dep) => dep in consolidatedPackages) ||
Object.keys(packageJson.devDependencies || {}).some((dep) => dep in consolidatedPackages)
);
})
).then((results) => results.some(Boolean));
if (!hasConsolidatedDependencies) {
// Check both dependencies and devDependencies
const allDeps = {
...(packageJson.dependencies || {}),
...(packageJson.devDependencies || {}),
};
// Add any consolidated packages to the set
let hasConsolidatedDeps = false;
Object.keys(allDeps).forEach((dep) => {
if (dep in consolidatedPackages) {
consolidatedDeps.add(dep as keyof typeof consolidatedPackages);
hasConsolidatedDeps = true;
}
});
if (hasConsolidatedDeps) {
affectedPackageJSONFiles.add(file);
}
})
);
if (consolidatedDeps.size === 0) {
return null;
}
return {
packageJsonFiles,
consolidatedDeps,
packageJsonFiles: Array.from(affectedPackageJSONFiles),
};
},
prompt: (result: ConsolidatedOptions) => {
return dedent`
Found package.json files that contain consolidated Storybook packages that need to be updated:
Found package.json files that contain consolidated or renamed Storybook packages that need to be updated:
${result.packageJsonFiles.map((file) => `- ${file}`).join('\n')}
These packages have been consolidated into the main storybook package and should be removed.
The main storybook package will be added to devDependencies if not already present.
We will automatically rename the following packages:
${Array.from(result.consolidatedDeps)
.map((dep) => `- ${picocolors.red(dep)} -> ${picocolors.cyan(consolidatedPackages[dep])}`)
.join('\n')}
These packages have been renamed or consolidated into the main ${picocolors.cyan('storybook')} package and should be removed.
The main ${picocolors.cyan('storybook')} package will be added to devDependencies if not already present.
Would you like to:
1. Update these package.json files
2. Scan your codebase and update any imports from these consolidated packages
2. Scan your codebase and update any imports from these updated packages
This will ensure your project is properly updated to use the new consolidated package structure.
This will ensure your project is properly updated to use the new updated package structure and to use the latest package names.
`;
},
run: async (options: RunOptions<ConsolidatedOptions>) => {
@ -165,7 +186,7 @@ export const consolidatedImports: Fix<ConsolidatedOptions> = {
const projectRoot = getProjectRoot();
const defaultGlob = '**/*.{mjs,cjs,js,jsx,ts,tsx}';
const defaultGlob = '**/*.{mjs,cjs,js,jsx,ts,tsx,mdx}';
// Find all files matching the glob pattern
const { glob } = await prompts({
type: 'text',

View File

@ -26,6 +26,7 @@ export const consolidatedPackages = {
'@storybook/addon-controls': 'storybook/internal/controls',
'@storybook/addon-toolbars': 'storybook/internal/toolbars',
'@storybook/addon-viewport': 'storybook/viewport',
'@storybook/experimental-nextjs-vite': 'storybook/nextjs-vite',
} as const;
export type ConsolidatedPackage = keyof typeof consolidatedPackages;

View File

@ -210,25 +210,20 @@ export const baseTemplates = {
script:
'npx create-next-app@^14 {{beforeDir}} --eslint --tailwind --app --import-alias="@/*" --src-dir',
expected: {
framework: '@storybook/experimental-nextjs-vite',
framework: '@storybook/nextjs-vite',
renderer: '@storybook/react',
builder: '@storybook/builder-vite',
},
modifications: {
useCsfFactory: true,
mainConfig: {
framework: '@storybook/experimental-nextjs-vite',
framework: '@storybook/nextjs-vite',
features: {
experimentalRSC: true,
developmentModeForBuild: true,
},
},
extraDependencies: [
'server-only',
'@storybook/experimental-nextjs-vite',
'vite',
'prop-types',
],
extraDependencies: ['server-only', '@storybook/nextjs-vite', 'vite', 'prop-types'],
},
skipTasks: ['e2e-tests-dev', 'bench'],
},
@ -237,25 +232,20 @@ export const baseTemplates = {
script:
'npx create-next-app {{beforeDir}} --eslint --no-tailwind --app --import-alias="@/*" --src-dir',
expected: {
framework: '@storybook/experimental-nextjs-vite',
framework: '@storybook/nextjs-vite',
renderer: '@storybook/react',
builder: '@storybook/builder-vite',
},
modifications: {
useCsfFactory: true,
mainConfig: {
framework: '@storybook/experimental-nextjs-vite',
framework: '@storybook/nextjs-vite',
features: {
experimentalRSC: true,
developmentModeForBuild: true,
},
},
extraDependencies: [
'server-only',
'@storybook/experimental-nextjs-vite',
'vite',
'prop-types',
],
extraDependencies: ['server-only', '@storybook/nextjs-vite', 'vite', 'prop-types'],
},
skipTasks: ['e2e-tests-dev', 'bench'],
},

View File

@ -7,7 +7,7 @@ export const supportedFrameworks = [
'html-vite',
'html-webpack5',
'nextjs',
'experimental-nextjs-vite',
'nextjs-vite',
'nuxt',
'preact-vite',
'qwik',
@ -48,7 +48,7 @@ export const supportedFrameworksPackages = {
angular: '@storybook/angular',
ember: '@storybook/ember',
nextjs: '@storybook/nextjs',
'experimental-nextjs-vite': '@storybook/experimental-nextjs-vite',
'nextjs-vite': '@storybook/nextjs-vite',
nuxt: '@storybook/vue3-vite',
qwik: 'storybook-framework-qwik',
@ -76,7 +76,7 @@ export const supportedFrameworksNames = {
angular: 'Angular',
ember: 'Ember',
nextjs: 'NextJS',
'experimental-nextjs-vite': 'NextJS with Vite',
'nextjs-vite': 'NextJS with Vite',
nuxt: 'Nuxt',
qwik: 'Qwik',
solid: 'Solid',

View File

@ -12,13 +12,13 @@ export const SUPPORTED_FRAMEWORKS: Framework[] = [
'svelte-vite',
'web-components-vite',
'nextjs',
'experimental-nextjs-vite',
'nextjs-vite',
'sveltekit',
];
/**
* When selecting framework nextjs & intent includes test, prompt for experimental-nextjs-vite. When
* selecting another framework that doesn't support test addon, prompt for ignoring test intent.
* When selecting framework nextjs & intent includes test, prompt for nextjs-vite. When selecting
* another framework that doesn't support test addon, prompt for ignoring test intent.
*/
const name = 'Framework test compatibility';
export const frameworkTest: Check = {

View File

@ -1,5 +1,5 @@
{
"name": "experimental-nextjs-vite/default-ts",
"name": "nextjs-vite/default-ts",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
@ -8,7 +8,7 @@
"addon-links",
"addon-onboarding",
"blocks",
"experimental-nextjs-vite"
"nextjs-vite"
],
"targets": {
"sandbox": {},

View File

@ -7067,30 +7067,6 @@ __metadata:
languageName: node
linkType: hard
"@storybook/experimental-nextjs-vite@workspace:frameworks/experimental-nextjs-vite":
version: 0.0.0-use.local
resolution: "@storybook/experimental-nextjs-vite@workspace:frameworks/experimental-nextjs-vite"
dependencies:
"@storybook/builder-vite": "workspace:*"
"@storybook/react": "workspace:*"
"@storybook/react-vite": "workspace:*"
"@types/node": "npm:^22.0.0"
next: "npm:^15.0.3"
styled-jsx: "npm:5.1.6"
typescript: "npm:^5.7.3"
vite-plugin-storybook-nextjs: "npm:2.0.0--canary.33.7c1f48f.0"
peerDependencies:
next: ^14.1.0 || ^15.0.0
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
storybook: "workspace:^"
vite: ^5.0.0 || ^6.0.0
peerDependenciesMeta:
typescript:
optional: true
languageName: unknown
linkType: soft
"@storybook/global@npm:^5.0.0":
version: 5.0.0
resolution: "@storybook/global@npm:5.0.0"
@ -7176,6 +7152,30 @@ __metadata:
languageName: node
linkType: hard
"@storybook/nextjs-vite@workspace:frameworks/nextjs-vite":
version: 0.0.0-use.local
resolution: "@storybook/nextjs-vite@workspace:frameworks/nextjs-vite"
dependencies:
"@storybook/builder-vite": "workspace:*"
"@storybook/react": "workspace:*"
"@storybook/react-vite": "workspace:*"
"@types/node": "npm:^22.0.0"
next: "npm:^15.0.3"
styled-jsx: "npm:5.1.6"
typescript: "npm:^5.7.3"
vite-plugin-storybook-nextjs: "npm:2.0.0--canary.33.7c1f48f.0"
peerDependencies:
next: ^14.1.0 || ^15.0.0
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
storybook: "workspace:^"
vite: ^5.0.0 || ^6.0.0
peerDependenciesMeta:
typescript:
optional: true
languageName: unknown
linkType: soft
"@storybook/nextjs@workspace:*, @storybook/nextjs@workspace:frameworks/nextjs":
version: 0.0.0-use.local
resolution: "@storybook/nextjs@workspace:frameworks/nextjs"

View File

@ -33,7 +33,7 @@ The CSF Factories API is composed of four main functions to help you write stori
With CSF Factories, your [main Storybook config](../main-config/main-config.mdx) is specified by the `defineMain` function. This function is type-safe and will automatically infer types for your project.
```ts title=".storybook/main.js|ts"
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, experimental-nextjs-vite)
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
import { defineMain } from '@storybook/your-framework/node';
export default defineMain({
@ -52,7 +52,7 @@ Importantly, by specifying addons here, their types will be available throughout
You will import the result of this function, `preview`, in your story files to define the component meta.
```ts title=".storybook/preview.js|ts"
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, experimental-nextjs-vite)
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
import { definePreview } from '@storybook/your-framework';
import addonA11y from '@storybook/addon-a11y';
@ -145,7 +145,7 @@ To be able to consistently import the preview file from any location in your pro
Update your `.storybook/main.js|ts` file to use the new [`defineMain`](#definemain) function.
```diff title=".storybook/main.js|ts"
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, experimental-nextjs-vite)
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
+ import { defineMain } from '@storybook/your-framework/node';
- import { StorybookConfig } from '@storybook/your-framework';
@ -177,7 +177,7 @@ If an addon provides annotations (i.e. it distributes a `./preview` export), it
</details>
```diff title=".storybook/preview.js|ts"
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, experimental-nextjs-vite)
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
+ import { definePreview } from '@storybook/your-framework';
- import { Preview } from '@storybook/your-renderer';
// 👇 Import the addons you are using

View File

@ -34,7 +34,7 @@ sidebar:
<If renderer="react">
<Callout variant="warning">
**Using `Next.js`?** You can test your Next.js stories with Vitest by installing and setting up the `@storybook/experimental-nextjs-vite` which re-exports [vite-plugin-storybook-nextjs](https://github.com/storybookjs/vite-plugin-storybook-nextjs) package.
**Using `Next.js`?** You can test your Next.js stories with Vitest by installing and setting up the `@storybook/nextjs-vite` which re-exports [vite-plugin-storybook-nextjs](https://github.com/storybookjs/vite-plugin-storybook-nextjs) package.
</Callout>
</If>

View File

@ -78,7 +78,7 @@ Finally, if you were using Storybook plugins to integrate with Next.js, those ar
(⚠️ **Experimental**)
You can use our freshly baked, experimental `@storybook/experimental-nextjs-vite` framework, which is based on Vite and removes the need for Webpack and Babel. It supports all of the features documented here.
You can use our freshly baked, experimental `@storybook/nextjs-vite` framework, which is based on Vite and removes the need for Webpack and Babel. It supports all of the features documented here.
<Callout variant="info">
Using the Next.js framework with Vite requires Next.js 14.1.0 or later.
@ -195,7 +195,7 @@ const localRubikStorm = localFont({ src: './fonts/RubikStorm-Regular.ttf' });
#### `staticDir` mapping
<Callout variant="info">
You can safely skip this section if you are using [`@storybook/experimental-nextjs-vite`](#with-vite) instead of `@storybook/nextjs`. The Vite-based framework takes care of the mapping automatically.
You can safely skip this section if you are using [`@storybook/nextjs-vite`](#with-vite) instead of `@storybook/nextjs`. The Vite-based framework takes care of the mapping automatically.
</Callout>
You have to tell Storybook where the `fonts` directory is located, via the [`staticDirs` configuration](../../api/main-config/main-config-static-dirs.mdx#with-configuration-objects). The `from` value is relative to the `.storybook` directory. The `to` value is relative to the execution context of Storybook. Very likely it is the root of your project.
@ -751,7 +751,7 @@ Calls to `getConfig` would return the following object when called within Storyb
## Custom Webpack config
<Callout variant="info">
You can safely skip this section if you are using `@storybook/experimental-nextjs-vite` instead of `@storybook/nextjs`.
You can safely skip this section if you are using `@storybook/nextjs-vite` instead of `@storybook/nextjs`.
The Vite-based Next.js framework does not support Webpack settings.
</Callout>
@ -901,7 +901,7 @@ You might get this if you're using Yarn v2 or v3. See [Notes for Yarn v2 and v3
### What if I'm using the Vite builder?
We have introduced experimental Vite builder support. Just install the experimental framework package `@storybook/experimental-nextjs-vite` and replace all instances of `@storybook/nextjs` with `@storybook/experimental-nextjs-vite`.
We have introduced experimental Vite builder support. Just install the experimental framework package `@storybook/nextjs-vite` and replace all instances of `@storybook/nextjs` with `@storybook/nextjs-vite`.
### Error: You are importing avif images, but you don't have sharp installed. You have to install sharp in order to use image optimization features in Next.js.

View File

@ -40,7 +40,7 @@ Before installing, make sure your project meets the following requirements:
<If renderer="react">
<Callout variant="info" icon="">
**Using with Next.js** — The Test addon is supported in Next.js ≥ 14.1 projects, but you must be using the [`@storybook/experimental-nextjs-vite` framework](../get-started/frameworks/nextjs.mdx#with-vite). When you run the setup command below, you will be prompted to install and use the framework if you haven't already.
**Using with Next.js** — The Test addon is supported in Next.js ≥ 14.1 projects, but you must be using the [`@storybook/nextjs-vite` framework](../get-started/frameworks/nextjs.mdx#with-vite). When you run the setup command below, you will be prompted to install and use the framework if you haven't already.
</Callout>
</If>

View File

@ -409,7 +409,7 @@ export async function setupVitest(details: TemplateDetails, options: PassedOptio
const portableStoriesFrameworks = [
'@storybook/nextjs',
'@storybook/experimental-nextjs-vite',
'@storybook/nextjs-vite',
'@storybook/sveltekit',
// TODO: add angular once we enable their sandboxes
];

View File

@ -85,7 +85,7 @@ export const sandbox: Task = {
extraDeps.push('happy-dom', 'vitest', 'playwright', '@vitest/browser');
if (details.template.expected.framework.includes('nextjs')) {
extraDeps.push('@storybook/experimental-nextjs-vite', 'jsdom');
extraDeps.push('@storybook/nextjs-vite', 'jsdom');
}
// if (details.template.expected.renderer === '@storybook/svelte') {