Merge branch 'next' into norbert/sb-1107-sb20220-bug-sb-babelrc-produces-empty

This commit is contained in:
Norbert de Langen 2022-12-14 13:36:54 +01:00
commit 238bf10a49
No known key found for this signature in database
GPG Key ID: FD0E78AF9A837762
29 changed files with 519 additions and 161 deletions

View File

@ -611,25 +611,25 @@ workflows:
jobs:
- build
- create-sandboxes:
parallelism: 25
parallelism: 26
requires:
- build
# - smoke-test-sandboxes: # disabled for now
# requires:
# - create-sandboxes
- build-sandboxes:
parallelism: 25
parallelism: 26
requires:
- create-sandboxes
- test-runner-sandboxes:
parallelism: 25
parallelism: 26
requires:
- build-sandboxes
- chromatic-sandboxes:
parallelism: 25
parallelism: 26
requires:
- build-sandboxes
- e2e-sandboxes:
parallelism: 25
parallelism: 26
requires:
- build-sandboxes

View File

@ -23,10 +23,10 @@
- [7.0 feature flags removed](#70-feature-flags-removed)
- [CLI option `--use-npm` deprecated](#cli-option---use-npm-deprecated)
- [Vite builder uses vite config automatically](#vite-builder-uses-vite-config-automatically)
- [Vite cache moved to node\_modules/.cache/.vite-storybook](#vite-cache-moved-to-node_modulescachevite-storybook)
- [Vite cache moved to node_modules/.cache/.vite-storybook](#vite-cache-moved-to-node_modulescachevite-storybook)
- [SvelteKit needs the `@storybook/sveltekit` framework](#sveltekit-needs-the-storybooksveltekit-framework)
- [Removed docs.getContainer and getPage parameters](#removed-docsgetcontainer-and-getpage-parameters)
- [Removed STORYBOOK\_REACT\_CLASSES global](#removed-storybook_react_classes-global)
- [Removed STORYBOOK_REACT_CLASSES global](#removed-storybook_react_classes-global)
- [Icons API changed](#icons-api-changed)
- ['config' preset entry replaced with 'previewAnnotations'](#config-preset-entry-replaced-with-previewannotations)
- [Dropped support for Angular 12 and below](#dropped-support-for-angular-12-and-below)
@ -34,6 +34,7 @@
- [Addon-docs: Removed deprecated blocks.js entry](#addon-docs-removed-deprecated-blocksjs-entry)
- [Addon-a11y: Removed deprecated withA11y decorator](#addon-a11y-removed-deprecated-witha11y-decorator)
- [Stories glob matches MDX files](#stories-glob-matches-mdx-files)
- [Add strict mode](#add-strict-mode)
- [Docs Changes](#docs-changes)
- [Standalone docs files](#standalone-docs-files)
- [Referencing stories in docs files](#referencing-stories-in-docs-files)
@ -275,6 +276,7 @@ To upgrade manually, add any version of `react` and `react-dom` as devDependenci
```
npm add react react-dom --dev
```
#### Postcss removed
Storybook 6.x installed postcss by default. In 7.0 built-in support has been removed. IF you need it, you can add it back using [`@storybook/addon-postcss`](https://github.com/storybookjs/addon-postcss).
@ -586,7 +588,7 @@ When using a [Vite-based framework](#framework-field-mandatory), Storybook will
Some settings will be overridden by storybook so that it can function properly, and the merged settings can be modified using `viteFinal` in `.storybook/main.js` (see the [Storybook Vite configuration docs](https://storybook.js.org/docs/react/builders/vite#configuration)).
If you were using `viteFinal` in 6.5 to simply merge in your project's standard vite config, you can now remove it.
For Svelte projects this means that the `svelteOptions` property in the `main.js` config can be omitted in most cases, as it will be loaded automatically via the project's `vite.config.js`. An exception to this is when the project needs different Svelte options for Storybook than the Vite config provides for the application itself.
For Svelte projects this means that the `svelteOptions` property in the `main.js` config should be omitted, as it will be loaded automatically via the project's `vite.config.js`. An exception to this is when the project needs different Svelte options for Storybook than the Vite config provides for the application itself.
#### Vite cache moved to node_modules/.cache/.vite-storybook
@ -677,6 +679,12 @@ export default {
};
```
#### Add strict mode
Starting in 7.0, Storybook's build tools add [`"use strict"`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) to the compiled JS output.
If user code in `.storybook/preview.js` or stories relies on "sloppy" mode behavior, it will need to be updated. As a workaround, it is sometimes possible to move the sloppy mode code inside a script tag in `.storybook/preview-head.html`.
### Docs Changes
The information hierarchy of docs in Storybook has changed in 7.0. The main difference is that each docs is listed in the sidebar as a separate entry, rather than attached to individual stories.

View File

@ -41,6 +41,11 @@
"import": "./dist/preset.mjs",
"types": "./dist/preset.d.ts"
},
"./blocks": {
"require": "./dist/blocks.js",
"import": "./dist/blocks.mjs",
"types": "./dist/blocks.d.ts"
},
"./dist/preview": {
"require": "./dist/preview.js",
"import": "./dist/preview.mjs",
@ -132,6 +137,7 @@
"./src/index.ts",
"./src/preset.ts",
"./src/preview.ts",
"./src/blocks.ts",
"./src/shims/mdx-react-shim.ts"
]
},

View File

@ -0,0 +1,7 @@
import { deprecate } from '@storybook/client-logger';
deprecate(
"Import from '@storybook/addon-docs/blocks' is deprecated. Please import from '@storybook/blocks' instead."
);
export * from '@storybook/blocks';

View File

@ -12,32 +12,9 @@ import { loadCsf } from '@storybook/csf-tools';
// for more complex solutions we can find alone that we need to add '@babel/plugin-transform-react-jsx'
type BabelParams = {
mdxBabelOptions?: any;
/** @deprecated */
configureJSX?: boolean;
};
function createBabelOptions({ mdxBabelOptions, configureJSX }: BabelParams) {
const babelPlugins = mdxBabelOptions?.plugins || [];
const filteredBabelPlugins = babelPlugins.filter((p: any) => {
const name = Array.isArray(p) ? p[0] : p;
if (typeof name === 'string') {
return !name.includes('plugin-transform-react-jsx');
}
return true;
});
const jsxPlugin = [
require.resolve('@babel/plugin-transform-react-jsx'),
{ pragma: 'React.createElement', pragmaFrag: 'React.Fragment' },
];
const plugins = configureJSX ? [...filteredBabelPlugins, jsxPlugin] : babelPlugins;
return {
// don't use the root babelrc by default (users can override this in mdxBabelOptions)
babelrc: false,
configFile: false,
...mdxBabelOptions,
plugins,
};
}
async function webpack(
webpackConfig: any = {},
@ -51,15 +28,12 @@ async function webpack(
typeof createCompiler
>[0] */
) {
const resolvedBabelLoader = await options.presets.apply('babelLoaderRef');
const { module = {} } = webpackConfig;
// it will reuse babel options that are already in use in storybook
// also, these babel options are chained with other presets.
const {
mdxBabelOptions,
configureJSX = true,
csfPluginOptions = {},
sourceLoaderOptions = null,
transcludeMarkdown = false,
@ -69,6 +43,7 @@ async function webpack(
skipCsf: true,
mdxCompileOptions: {
providerImportSource: '@storybook/addon-docs/mdx-react-shim',
remarkPlugins: [remarkSlug, remarkExternalLinks],
},
});
@ -92,10 +67,6 @@ async function webpack(
{
test: /\.md$/,
use: [
{
loader: resolvedBabelLoader,
options: createBabelOptions({ mdxBabelOptions, configureJSX }),
},
{
loader: mdxLoader,
options: mdxLoaderOptions,
@ -120,14 +91,11 @@ async function webpack(
{
test: /(stories|story)\.mdx$/,
use: [
{
loader: resolvedBabelLoader,
options: createBabelOptions({ mdxBabelOptions, configureJSX }),
},
{
loader: mdxLoader,
options: {
...mdxLoaderOptions,
mdxBabelOptions,
skipCsf: false,
},
},
@ -137,10 +105,6 @@ async function webpack(
test: /\.mdx$/,
exclude: /(stories|story)\.mdx$/,
use: [
{
loader: resolvedBabelLoader,
options: createBabelOptions({ mdxBabelOptions, configureJSX }),
},
{
loader: mdxLoader,
options: mdxLoaderOptions,

View File

@ -1,2 +1 @@
export * from '@storybook/react';
export * from './types';

View File

@ -3,7 +3,6 @@ import { DefinePlugin } from 'webpack';
import { PHASE_DEVELOPMENT_SERVER } from 'next/constants';
import findUp from 'find-up';
import { pathExists } from 'fs-extra';
import dedent from 'ts-dedent';
import type { Configuration as WebpackConfig } from 'webpack';
import type { NextConfig } from 'next';
import { pathToFileURL } from 'node:url';
@ -45,13 +44,7 @@ export const resolveNextConfig = async ({
const nextConfigFile = nextConfigPath || (await findNextConfigFile(configDir));
if (!nextConfigFile || (await pathExists(nextConfigFile)) === false) {
throw new Error(
dedent`
Could not find or resolve your Next config file. Please provide the next config file path as a framework option.
More info: https://github.com/storybookjs/storybook/blob/next/code/frameworks/nextjs/README.md#options
`
);
return {};
}
const nextConfigExport = await import(pathToFileURL(nextConfigFile).href);

View File

@ -13,6 +13,8 @@ Check out our [Frameworks API](https://storybook.js.org/blog/framework-api/) ann
- [In a project with Storybook](#in-a-project-with-storybook)
- [Automatic migration](#automatic-migration)
- [Manual migration](#manual-migration)
- [Troubleshooting](#troubleshooting)
- [Error: `ERR! SyntaxError: Identifier '__esbuild_register_import_meta_url__' has already been declared` when starting Storybook](#error-err-syntaxerror-identifier-__esbuild_register_import_meta_url__-has-already-been-declared-when-starting-storybook)
- [Acknowledgements](#acknowledgements)
## Supported features
@ -66,6 +68,8 @@ npx storybook@next upgrade --prerelease
When running the `upgrade` command above you should get a prompt asking you to migrate to `@storybook/sveltekit`, which should handle everything for you. In some cases it can't migrate for you, eg. if your existing Storybook setup is based on Webpack. In such cases, refer to the manual migration below.
Storybook 7.0 automatically loads your Vite config, and by extension your Svelte config. If you have a `svelteOptions` property in `.storybook/main.cjs` you need to remove that. See [Troubleshooting](#error-about-__esbuild_register_import_meta_url__-when-starting-storybook) below. We're working on doing this automatically soon.
#### Manual migration
Install the framework:
@ -84,7 +88,7 @@ module.exports = {
};
```
Storybook 7.0 automatically loads your Vite config, and by extension your Svelte config. If you have a `svelteOptions` property in `main.cjs` you should remove that, unless you explicitly want different options between your app and Storybook.
Storybook 7.0 automatically loads your Vite config, and by extension your Svelte config. If you have a `svelteOptions` property in `.storybook/main.cjs` you need to remove that. See [Troubleshooting](#error-about-__esbuild_register_import_meta_url__-when-starting-storybook) below.
Remove any redundant dependencies, if you have them:
@ -95,6 +99,18 @@ yarn remove storybook-builder-vite
yarn remove @storybook/builder-vite
```
## Troubleshooting
### Error: `ERR! SyntaxError: Identifier '__esbuild_register_import_meta_url__' has already been declared` when starting Storybook
> When starting Storybook after upgrading to v7.0, it breaks with the following error:
>
> ```
> ERR! SyntaxError: Identifier '__esbuild_register_import_meta_url__' has already been declared
> ```
You'll get this error when upgrading from 6.5 to 7.0. You need to remove the `svelteOptions` property in `.storybook/main.cjs`, as that is not supported by Storybook 7.0 + SvelteKit. The property is also not necessary anymore because the Vite and Svelte configurations are loaded automatically in Storybook 7.0.
## Acknowledgements
Integrating with SvelteKit would not have been possible if it weren't for the fantastic efforts by the Svelte core team - especially [Ben McCann](https://twitter.com/benjaminmccann) - to make integrations with the wider ecosystem possible.

View File

@ -25,7 +25,7 @@
<!-- [BODY HTML SNIPPET HERE] -->
<div id="storybook-root"></div>
<div id="storybook-docs"></div>
<script type="module" src="/sb-preview/runtime.mjs"></script>
<script type="module" src="./sb-preview/runtime.mjs"></script>
<script type="module" src="/virtual:/@storybook/builder-vite/vite-app.js"></script>
</body>
</html>

View File

@ -50,7 +50,6 @@
"@storybook/preview": "7.0.0-beta.6",
"@storybook/preview-api": "7.0.0-beta.6",
"@storybook/types": "7.0.0-beta.6",
"@vitejs/plugin-react": "^2.0.0",
"browser-assert": "^1.2.1",
"es-module-lexer": "^0.9.3",
"express": "^4.17.3",
@ -71,12 +70,16 @@
},
"peerDependencies": {
"@preact/preset-vite": "*",
"typescript": ">= 4.3.x",
"vite-plugin-glimmerx": "*"
},
"peerDependenciesMeta": {
"@preact/preset-vite": {
"optional": true
},
"typescript": {
"optional": true
},
"vite-plugin-glimmerx": {
"optional": true
}

View File

@ -15,7 +15,7 @@ export async function build(options: ExtendedOptions) {
sourcemap: true,
rollupOptions: {
// Do not try to bundle the storybook runtime, it is copied into the output dir after the build.
external: ['/sb-preview/runtime.mjs'],
external: ['./sb-preview/runtime.mjs'],
},
},
}).build;

View File

@ -69,14 +69,8 @@ function iframeMiddleware(options: ExtendedOptions, server: ViteDevServer): Requ
let server: ViteDevServer;
export async function bail(e?: Error): Promise<void> {
try {
return await server.close();
} catch (err) {
console.warn('unable to close vite server');
}
throw e;
export async function bail(): Promise<void> {
return server?.close();
}
export const start: ViteBuilder['start'] = async ({

View File

@ -1,25 +1,9 @@
import type { Options } from '@storybook/types';
import type { Plugin } from 'vite';
import { createFilter } from 'vite';
import reactVite from '@vitejs/plugin-react';
const isStorybookMdx = (id: string) => id.endsWith('stories.mdx') || id.endsWith('story.mdx');
function injectRenderer(code: string) {
return `
import React from 'react';
${code}
`;
}
// HACK: find a better way to do this, ideally avoiding @vitejs/plugin-react entirely.
// We're just using it to run the mdx with jsx through babel
// @ts-expect-error We're forcing the plugin shape here
const viteBabel: Plugin | undefined = reactVite({ fastRefresh: false }).find(
// @ts-expect-error we know these have names, and what the shape will be
(p) => p.name === 'vite:react-babel'
);
/**
* Storybook uses two different loaders when dealing with MDX:
*
@ -35,7 +19,7 @@ export function mdxPlugin(options: Options): Plugin {
return {
name: 'storybook:mdx-plugin',
enforce: 'pre',
async transform(src, id, transformOptions) {
async transform(src, id) {
if (!filter(id)) return undefined;
const { compile } = await import('@storybook/mdx2-csf');
@ -46,33 +30,16 @@ export function mdxPlugin(options: Options): Plugin {
},
});
const mdxCode = String(
const code = String(
await compile(src, {
skipCsf: !isStorybookMdx(id),
...mdxLoaderOptions,
})
);
const modifiedCode = injectRenderer(mdxCode);
// Hooks in recent rollup versions can be functions or objects, and though react hasn't changed, the typescript defs have
const rTransform = viteBabel?.transform;
const transform = rTransform && 'handler' in rTransform ? rTransform.handler : rTransform;
// It's safe to disable this, because we know it'll be there, since we added it ourselves.
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const result = await transform!.call(this, modifiedCode, `${id}.jsx`, transformOptions);
if (!result) return modifiedCode;
if (typeof result === 'string') return result;
const { code, map: resultMap } = result;
return {
code,
map:
!resultMap || typeof resultMap === 'string' ? resultMap : { ...resultMap, sources: [id] },
map: null, // TODO: update mdx2-csf to return the map
};
},
};

View File

@ -29,7 +29,5 @@ export const babel = async (config: any, options: any) => ({
],
});
export const babelLoaderRef = () => require.resolve('babel-loader');
export const previewMainTemplate = () =>
require.resolve('@storybook/builder-webpack5/templates/preview.ejs');

View File

@ -147,8 +147,6 @@ export default async (
previewAnnotations.forEach((previewAnnotationFilename: string | undefined) => {
if (!previewAnnotationFilename) return;
const previewApi = storybookPaths['@storybook/preview-api'];
const clientLogger = storybookPaths['@storybook/client-logger'];
// Ensure that relative paths end up mapped to a filename in the cwd, so a later import
// of the `previewAnnotationFilename` in the template works.
@ -159,8 +157,6 @@ export default async (
// file, see https://github.com/storybookjs/storybook/pull/16727#issuecomment-986485173
virtualModuleMapping[entryFilename] = interpolate(entryTemplate, {
previewAnnotationFilename,
previewApi,
clientLogger,
});
entries.push(entryFilename);
});

View File

@ -9,7 +9,7 @@ import {
addArgsEnhancer,
addArgTypesEnhancer,
setGlobalRender,
} from '{{previewApi}}';
} from '@storybook/preview-api';
import * as previewAnnotations from '{{previewAnnotationFilename}}';
Object.keys(previewAnnotations).forEach((key) => {

View File

@ -9,6 +9,7 @@ import { eslintPlugin } from './eslint-plugin';
import { builderVite } from './builder-vite';
import { sbScripts } from './sb-scripts';
import { sbBinary } from './sb-binary';
import { nextjsFramework } from './nextjs-framework';
import { newFrameworks } from './new-frameworks';
import { removedGlobalClientAPIs } from './remove-global-client-apis';
import { mdx1to2 } from './mdx-1-to-2';
@ -30,6 +31,7 @@ export const fixes: Fix[] = [
sbBinary,
sbScripts,
newFrameworks,
nextjsFramework,
removedGlobalClientAPIs,
mdx1to2,
docsPageAutomatic,

View File

@ -0,0 +1,183 @@
/* eslint-disable no-underscore-dangle */
import path from 'path';
import type { JsPackageManager } from '../../js-package-manager';
import { nextjsFramework } from './nextjs-framework';
// eslint-disable-next-line global-require, jest/no-mocks-import
jest.mock('fs-extra', () => require('../../../../../__mocks__/fs-extra'));
const checkNextjsFramework = async ({ packageJson, main }: any) => {
if (main) {
// eslint-disable-next-line global-require
require('fs-extra').__setMockFiles({
[path.join('.storybook', 'main.js')]: `module.exports = ${JSON.stringify(main)};`,
});
}
const packageManager = {
retrievePackageJson: () => ({ dependencies: {}, devDependencies: {}, ...packageJson }),
} as JsPackageManager;
return nextjsFramework.check({ packageManager });
};
describe('nextjs-framework fix', () => {
describe('should no-op', () => {
it('in sb < 7', async () => {
const packageJson = { dependencies: { '@storybook/react': '^6.2.0' } };
await expect(
checkNextjsFramework({
packageJson,
main: {},
})
).resolves.toBeFalsy();
});
it('in sb 7 with no main', async () => {
const packageJson = { dependencies: { '@storybook/react': '^7.0.0' } };
await expect(
checkNextjsFramework({
packageJson,
main: undefined,
})
).resolves.toBeFalsy();
});
it('in sb 7 with no framework field in main', async () => {
const packageJson = { dependencies: { '@storybook/react': '^7.0.0' } };
await expect(
checkNextjsFramework({
packageJson,
main: {},
})
).resolves.toBeFalsy();
});
it('in sb 7 in non-nextjs projects', async () => {
const packageJson = { dependencies: { '@storybook/react': '^7.0.0' } };
await expect(
checkNextjsFramework({
packageJson,
main: {
framework: '@storybook/react',
},
})
).resolves.toBeFalsy();
});
it('in sb 7 with unsupported package', async () => {
const packageJson = { dependencies: { '@storybook/riot': '^7.0.0' } };
await expect(
checkNextjsFramework({
packageJson,
main: {
framework: '@storybook/riot',
core: {
builder: 'webpack5',
},
},
})
).resolves.toBeFalsy();
});
});
describe('sb >= 7', () => {
it('should update from @storybook/react-webpack5 to @storybook/nextjs', async () => {
const packageJson = {
dependencies: {
'@storybook/react': '^7.0.0-alpha.0',
'@storybook/react-webpack5': '^7.0.0-alpha.0',
next: '^12.0.0',
},
};
await expect(
checkNextjsFramework({
packageJson,
main: {
framework: '@storybook/react-webpack5',
},
})
).resolves.toEqual(expect.objectContaining({}));
});
it('should remove legacy addons', async () => {
const packageJson = {
dependencies: {
'@storybook/react': '^7.0.0-alpha.0',
'@storybook/react-webpack5': '^7.0.0-alpha.0',
next: '^12.0.0',
'storybook-addon-next': '^1.0.0',
'storybook-addon-next-router': '^1.0.0',
},
};
await expect(
checkNextjsFramework({
packageJson,
main: {
framework: '@storybook/react-webpack5',
addons: ['storybook-addon-next', 'storybook-addon-next-router'],
},
})
).resolves.toEqual(
expect.objectContaining({
addonsToRemove: ['storybook-addon-next', 'storybook-addon-next-router'],
})
);
});
it('should move nextjs addon options to frameworkOptions', async () => {
const packageJson = {
dependencies: {
'@storybook/react': '^7.0.0-alpha.0',
'@storybook/react-webpack5': '^7.0.0-alpha.0',
next: '^12.0.0',
'storybook-addon-next': '^1.0.0',
},
};
await expect(
checkNextjsFramework({
packageJson,
main: {
framework: { name: '@storybook/react-webpack5', options: { fastRefresh: true } },
addons: [
{
name: 'storybook-addon-next',
options: {
nextConfigPath: '../next.config.js',
},
},
],
},
})
).resolves.toEqual(
expect.objectContaining({
addonsToRemove: ['storybook-addon-next'],
frameworkOptions: {
fastRefresh: true,
nextConfigPath: '../next.config.js',
},
})
);
});
it('should warn for @storybook/react-vite users', async () => {
const consoleSpy = jest.spyOn(console, 'info');
const packageJson = {
dependencies: {
'@storybook/react': '^7.0.0-alpha.0',
'@storybook/react-vite': '^7.0.0-alpha.0',
next: '^12.0.0',
'storybook-addon-next': '^1.0.0',
},
};
await expect(
checkNextjsFramework({
packageJson,
main: {
framework: { name: '@storybook/react-vite' },
},
})
).resolves.toBeFalsy();
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Vite builder'));
});
});
});

View File

@ -0,0 +1,199 @@
import chalk from 'chalk';
import dedent from 'ts-dedent';
import semver from 'semver';
import type { ConfigFile } from '@storybook/csf-tools';
import { readConfig, writeConfig } from '@storybook/csf-tools';
import { getStorybookInfo } from '@storybook/core-common';
import type { Fix } from '../types';
import type { PackageJsonWithDepsAndDevDeps } from '../../js-package-manager';
import { getStorybookVersionSpecifier } from '../../helpers';
const logger = console;
interface NextjsFrameworkRunOptions {
main: ConfigFile;
packageJson: PackageJsonWithDepsAndDevDeps;
addonsToRemove: string[];
frameworkOptions: Record<string, any>;
}
type Addon = string | { name: string; options?: Record<string, any> };
export const getNextjsAddonOptions = (addons: Addon[]) => {
const nextjsAddon = addons?.find((addon) =>
typeof addon === 'string'
? addon === 'storybook-addon-next'
: addon.name === 'storybook-addon-next'
);
if (!nextjsAddon || typeof nextjsAddon === 'string') {
return {};
}
return nextjsAddon.options || {};
};
/**
* Does the user have a nextjs project but is not using the @storybook/nextjs framework package?
*
* If so:
* - Remove the dependencies if webpack (@storybook/react-webpack5)
* - Install the nextjs package (@storybook/nextjs)
* - Uninstall existing legacy addons: storybook-addon-next and storybook-addon-next-router
* - Update StorybookConfig type import (if it exists) from react-webpack5 to nextjs
* - Update the main config to use the new framework
* -- removing legacy addons: storybook-addon-next and storybook-addon-next-router
* -- moving storybook-addon-next options into frameworkOptions
*/
export const nextjsFramework: Fix<NextjsFrameworkRunOptions> = {
id: 'nextjsFramework',
async check({ packageManager }) {
const packageJson = packageManager.retrievePackageJson();
const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies };
if (!allDeps.next) {
return null;
}
const { mainConfig, version: storybookVersion } = getStorybookInfo(packageJson);
if (!mainConfig) {
logger.warn('Unable to find storybook main.js config, skipping');
return null;
}
const storybookCoerced = storybookVersion && semver.coerce(storybookVersion)?.version;
if (!storybookCoerced) {
logger.warn(dedent`
Unable to determine storybook version, skipping ${chalk.cyan('nextjsFramework')} fix.
🤔 Are you running automigrate from your project directory?
`);
return null;
}
if (!semver.gte(storybookCoerced, '7.0.0')) {
return null;
}
const main = await readConfig(mainConfig);
const frameworkPackage = main.getFieldValue(['framework']);
if (!frameworkPackage) {
return null;
}
const frameworkPackageName =
typeof frameworkPackage === 'string' ? frameworkPackage : frameworkPackage.name;
if (frameworkPackageName === '@storybook/react-vite') {
logger.info(dedent`
We've detected you are using Storybook in a Next.js project.
In Storybook 7, we introduced a new framework package for Next.js projects: @storybook/nextjs.
This package provides a better experience for Next.js users, however it is only compatible with the webpack 5 builder, so we can't automigrate for you, as you are using the Vite builder.
If you are interested in using this package, see: ${chalk.yellow(
'https://github.com/storybookjs/storybook/blob/next/code/frameworks/nextjs/README.md'
)}
`);
return null;
}
// we only migrate from react-webpack5 projects
if (frameworkPackageName !== '@storybook/react-webpack5') {
return null;
}
const addonOptions = getNextjsAddonOptions(main.getFieldValue(['addons']));
const frameworkOptions = main.getFieldValue(['framework', 'options']) || {};
const addonsToRemove = ['storybook-addon-next', 'storybook-addon-next-router'].filter(
(dep) => allDeps[dep]
);
return {
main,
addonsToRemove,
frameworkOptions: {
...frameworkOptions,
...addonOptions,
},
packageJson,
};
},
prompt({ addonsToRemove }) {
let addonsMessage = '';
if (addonsToRemove.length > 0) {
addonsMessage = `
This package also supports features provided by the following packages, which can now be removed:
${addonsToRemove.map((dep) => `- ${chalk.cyan(dep)}`).join(', ')}
`;
}
return dedent`
We've detected you are using Storybook in a ${chalk.bold('Next.js')} project.
In Storybook 7, we introduced a new framework package for Next.js projects: ${chalk.magenta(
'@storybook/nextjs'
)}.
This package is a replacement for ${chalk.magenta(
'@storybook/react-webpack5'
)} and provides a better experience for Next.js users.
${addonsMessage}
To learn more about it, see: ${chalk.yellow(
'https://github.com/storybookjs/storybook/blob/next/code/frameworks/nextjs/README.md'
)}
`;
},
async run({
result: { addonsToRemove, main, frameworkOptions, packageJson },
packageManager,
dryRun,
}) {
const dependenciesToRemove = [...addonsToRemove, '@storybook/react-webpack5'];
if (dependenciesToRemove.length > 0) {
logger.info(`✅ Removing redundant packages: ${dependenciesToRemove.join(', ')}`);
if (!dryRun) {
packageManager.removeDependencies({ skipInstall: true, packageJson }, dependenciesToRemove);
const existingAddons = main.getFieldValue(['addons']) as Addon[];
const updatedAddons = existingAddons.filter((addon) => {
if (typeof addon === 'string') {
return !addonsToRemove.includes(addon);
}
if (addon.name) {
return !addonsToRemove.includes(addon.name);
}
return false;
});
main.setFieldValue(['addons'], updatedAddons);
}
}
logger.info(`✅ Installing new dependencies: @storybook/nextjs`);
if (!dryRun) {
const versionToInstall = getStorybookVersionSpecifier(packageJson);
packageManager.addDependencies({ installAsDevDependencies: true, packageJson }, [
`@storybook/nextjs@${versionToInstall}`,
]);
}
logger.info(`✅ Updating framework field in main.js`);
if (!dryRun) {
main.setFieldValue(['framework', 'options'], frameworkOptions);
main.setFieldValue(['framework', 'name'], '@storybook/nextjs');
await writeConfig(main);
}
},
};

View File

@ -353,6 +353,7 @@ export const daily: TemplateKey[] = [
'lit-vite/default-js',
'svelte-kit/skeleton-js',
'svelte-vite/default-js',
'nextjs/12-js',
'nextjs/default-js',
'preact-webpack5/default-js',
];

View File

@ -192,6 +192,12 @@ export const doUpgrade = async ({
}).output.toString();
logger.info(check);
const checkSb = spawnSync('npx', ['npm-check-updates@latest', 'sb', ...flags], {
stdio: 'pipe',
shell: true,
}).output.toString();
logger.info(checkSb);
if (!dryRun) {
commandLog(`Installing upgrades`);
packageManager.installDependencies();

View File

@ -77,11 +77,6 @@
"type-fest": "^2.19.0",
"typescript": "~4.9.3"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
},
"publishConfig": {
"access": "public"
},

View File

@ -76,28 +76,18 @@
"ws": "^8.2.3"
},
"devDependencies": {
"@storybook/builder-webpack5": "7.0.0-beta.6",
"@types/compression": "^1.7.0",
"@types/ip": "^1.1.0",
"@types/serve-favicon": "^2.5.2",
"@types/ws": "^8",
"jest-os-detection": "^1.3.1",
"jest-specific-snapshot": "^7.0.0",
"typescript": "~4.9.3",
"webpack": "5"
"typescript": "~4.9.3"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"@storybook/builder-webpack5": {
"optional": true
},
"typescript": {
"optional": true
}
},
"publishConfig": {
"access": "public"
},

View File

@ -23,7 +23,11 @@ export const unplugin = createUnplugin<CsfPluginOptions>((options) => {
enrichCsf(csf, options);
return formatCsf(csf);
} catch (err: any) {
logger.warn(err.message);
// This can be called on legacy storiesOf files, so just ignore
// those errors. But warn about other errors.
if (!err.message?.startsWith('CSF:')) {
logger.warn(err.message);
}
return code;
}
},

View File

@ -378,6 +378,36 @@ describe('CsfFile', () => {
`);
});
it('docs-only story with local vars', () => {
expect(
parse(
dedent`
export const TestControl = () => _jsx("p", {
children: "Hello"
});
export default { title: 'foo/bar', tags: ['mdx'], includeStories: ["__page"] };
export const __page = () => {};
__page.parameters = { docsOnly: true };
`,
true
)
).toMatchInlineSnapshot(`
meta:
title: foo/bar
tags:
- mdx
includeStories:
- __page
stories:
- id: foo-bar--page
name: Page
parameters:
__isArgsStory: false
__id: foo-bar--page
docsOnly: true
`);
});
it('title variable', () => {
expect(
parse(

View File

@ -458,7 +458,11 @@ export class CsfFile {
if (isExportStory(key, self._meta)) {
const id = toId(self._meta.id || self._meta.title, storyNameFromExport(key));
const parameters: Record<string, any> = { ...story.parameters, __id: id };
if (entries.length === 1 && key === '__page') {
const { includeStories } = self._meta || {};
if (
key === '__page' &&
(entries.length === 1 || (Array.isArray(includeStories) && includeStories.length === 1))
) {
parameters.docsOnly = true;
}
acc[key] = { ...story, id, parameters };

View File

@ -71,11 +71,15 @@ const createCanvas = (id: string, baseUrl = 'iframe.html', withLoader = true): A
useEffect(() => {
if (global.CONFIG_TYPE === 'DEVELOPMENT') {
const channel = addons.getServerChannel();
try {
const channel = addons.getServerChannel();
channel.on(PREVIEW_BUILDER_PROGRESS, (options) => {
setProgress(options);
});
channel.on(PREVIEW_BUILDER_PROGRESS, (options) => {
setProgress(options);
});
} catch {
//
}
}
}, []);

View File

@ -6028,7 +6028,6 @@ __metadata:
"@storybook/types": 7.0.0-beta.6
"@types/express": ^4.17.13
"@types/node": ^16.0.0
"@vitejs/plugin-react": ^2.0.0
browser-assert: ^1.2.1
es-module-lexer: ^0.9.3
express: ^4.17.3
@ -6043,10 +6042,13 @@ __metadata:
vite: ^3.0.0
peerDependencies:
"@preact/preset-vite": "*"
typescript: ">= 4.3.x"
vite-plugin-glimmerx: "*"
peerDependenciesMeta:
"@preact/preset-vite":
optional: true
typescript:
optional: true
vite-plugin-glimmerx:
optional: true
languageName: unknown
@ -6333,9 +6335,6 @@ __metadata:
ts-dedent: ^2.0.0
type-fest: ^2.19.0
typescript: ~4.9.3
peerDependenciesMeta:
typescript:
optional: true
languageName: unknown
linkType: soft
@ -6363,7 +6362,6 @@ __metadata:
"@aw-web-design/x-default-browser": 1.4.88
"@discoveryjs/json-ext": ^0.5.3
"@storybook/builder-manager": 7.0.0-beta.6
"@storybook/builder-webpack5": 7.0.0-beta.6
"@storybook/core-common": 7.0.0-beta.6
"@storybook/core-events": 7.0.0-beta.6
"@storybook/csf": next
@ -6408,16 +6406,10 @@ __metadata:
typescript: ~4.9.3
util-deprecate: ^1.0.2
watchpack: ^2.2.0
webpack: 5
ws: ^8.2.3
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
peerDependenciesMeta:
"@storybook/builder-webpack5":
optional: true
typescript:
optional: true
languageName: unknown
linkType: soft
@ -6493,9 +6485,9 @@ __metadata:
linkType: hard
"@storybook/docs-mdx@npm:next":
version: 0.0.1-next.4
resolution: "@storybook/docs-mdx@npm:0.0.1-next.4"
checksum: 111cc2948feb94800b3b245689c6595049f8a0dcc7bcc17d33676cbb48759bee9470980224d8f4a9600c840ae908f2f95f44c79f1b53f4a41990344ab32be545
version: 0.0.1-next.5
resolution: "@storybook/docs-mdx@npm:0.0.1-next.5"
checksum: ef2a477fc9a23e6d8b27af365510d5a4a4f2a71ce89f12562eaf8d12df5393faad6f6e35673fc183aa117f61c068beea6f323f3f7a4cdf7d67d12f064340e151
languageName: node
linkType: hard
@ -6780,11 +6772,11 @@ __metadata:
linkType: soft
"@storybook/mdx2-csf@npm:next":
version: 0.1.0-next.7
resolution: "@storybook/mdx2-csf@npm:0.1.0-next.7"
version: 1.0.0-next.0
resolution: "@storybook/mdx2-csf@npm:1.0.0-next.0"
dependencies:
loader-utils: ^2.0.4
checksum: c0d3b6c5d2261079ac8db0e2bb8e773ab11f6f363c60f4d4b76332c1361c981d5abffd49ea6f2e1b67f5d1fbdfc121e207044fe52b8be1e8322cacc42e41f747
checksum: 520b26977bce390e4a5a3e0f81dec3a99497f13950e0726f7da62087041df715903ef92121ee568f1cac5f8d422f96a95d0bd24fbe6ffc7d76f5a81c05f21e3b
languageName: node
linkType: hard

View File

@ -3,19 +3,16 @@
import { createViteServer } from './vite-server';
let server: ViteDevServer;
export async function bail(): Promise<void> {
return server?.close();
}
export const start: ViteBuilder['start'] = async ({ options, server: devServer }) => {
// Remainder implementation goes here
const server = await createViteServer(options as ExtendedOptions, devServer);
async function bail(e?: Error): Promise<void> {
try {
return await server.close();
} catch (err) {
console.warn('unable to close the server');
}
throw e;
}
server = await createViteServer(options as ExtendedOptions, devServer);
return {
bail,
totalTime: process.hrtime(startTime),