mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-06 15:31:16 +08:00
Merge pull request #20797 from storybookjs/feat/default-exports-in-main
CLI: generate main config with default exports
This commit is contained in:
commit
768d4324f9
43
MIGRATION.md
43
MIGRATION.md
@ -3,6 +3,7 @@
|
|||||||
- [From version 6.5.x to 7.0.0](#from-version-65x-to-700)
|
- [From version 6.5.x to 7.0.0](#from-version-65x-to-700)
|
||||||
- [7.0 breaking changes](#70-breaking-changes)
|
- [7.0 breaking changes](#70-breaking-changes)
|
||||||
- [Dropped support for Node 15 and below](#dropped-support-for-node-15-and-below)
|
- [Dropped support for Node 15 and below](#dropped-support-for-node-15-and-below)
|
||||||
|
- [ESM format in Main.js](#esm-format-in-mainjs)
|
||||||
- [Modern browser support](#modern-browser-support)
|
- [Modern browser support](#modern-browser-support)
|
||||||
- [React peer dependencies required](#react-peer-dependencies-required)
|
- [React peer dependencies required](#react-peer-dependencies-required)
|
||||||
- [start-storybook / build-storybook binaries removed](#start-storybook--build-storybook-binaries-removed)
|
- [start-storybook / build-storybook binaries removed](#start-storybook--build-storybook-binaries-removed)
|
||||||
@ -290,6 +291,48 @@ For avoiding that, this change passes the mapped args instead of raw args at `re
|
|||||||
|
|
||||||
Storybook 7.0 requires **Node 16** or above. If you are using an older version of Node, you will need to upgrade or keep using Storybook 6 in the meantime.
|
Storybook 7.0 requires **Node 16** or above. If you are using an older version of Node, you will need to upgrade or keep using Storybook 6 in the meantime.
|
||||||
|
|
||||||
|
#### ESM format in Main.js
|
||||||
|
|
||||||
|
Storybook 7.0 supports ESM in `.storybook/main.js`, and the configurations can be part of a default export. The default export will be the recommended way going forward.
|
||||||
|
|
||||||
|
If your main.js file looks like this:
|
||||||
|
|
||||||
|
```js
|
||||||
|
module.exports = {
|
||||||
|
stories: ['../stories/**/*.stories.mdx', '../stories/**/*.stories.@(js|jsx|ts|tsx)'],
|
||||||
|
framework: { name: '@storybook/react-vite' },
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Or like this:
|
||||||
|
|
||||||
|
```js
|
||||||
|
export const stories = ['../stories/**/*.stories.mdx', '../stories/**/*.stories.@(js|jsx|ts|tsx)'];
|
||||||
|
export const framework = { name: '@storybook/react-vite' };
|
||||||
|
```
|
||||||
|
|
||||||
|
Please migrate them to be default exported instead:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const config = {
|
||||||
|
stories: ['../stories/**/*.stories.mdx', '../stories/**/*.stories.@(js|jsx|ts|tsx)'],
|
||||||
|
framework: { name: '@storybook/react-vite' },
|
||||||
|
};
|
||||||
|
export default config;
|
||||||
|
```
|
||||||
|
|
||||||
|
For Typescript users, we introduced types for that default export, so you can import it in your main.ts file. The `StorybookConfig` type will come from the Storybook package for the framework you are using, which relates to the package in the "framework" field you have in your main.ts file. For example, if you are using React Vite, you will import it from `@storybook/react-vite`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { StorybookConfig } from '@storybook/react-vite';
|
||||||
|
|
||||||
|
const config: StorybookConfig = {
|
||||||
|
stories: ['../stories/**/*.stories.mdx', '../stories/**/*.stories.@(js|jsx|ts|tsx)'],
|
||||||
|
framework: { name: '@storybook/react-vite' },
|
||||||
|
};
|
||||||
|
export default config;
|
||||||
|
```
|
||||||
|
|
||||||
#### Modern browser support
|
#### Modern browser support
|
||||||
|
|
||||||
Starting in Storybook 7.0, Storybook will no longer support IE11, amongst other legacy browser versions.
|
Starting in Storybook 7.0, Storybook will no longer support IE11, amongst other legacy browser versions.
|
||||||
|
@ -56,12 +56,15 @@ const generator: Generator<{ projectName: string }> = async (
|
|||||||
.join('');
|
.join('');
|
||||||
|
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
`${storybookFolder}/main.js`,
|
`${storybookFolder}/main.ts`,
|
||||||
dedent(`
|
dedent(`
|
||||||
const mainRoot = require('${rootReferencePathFromStorybookFolder}../.storybook/main.js');
|
import { StorybookConfig } from'@storybook/angular';
|
||||||
module.exports = {
|
import mainRoot from'${rootReferencePathFromStorybookFolder}../.storybook/main';
|
||||||
|
|
||||||
|
const config: StorybookConfig = {
|
||||||
...mainRoot
|
...mainRoot
|
||||||
};
|
};
|
||||||
|
export default config;
|
||||||
`)
|
`)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import type { Generator } from '../types';
|
|||||||
const generator: Generator = async (packageManager, npmOptions, options) => {
|
const generator: Generator = async (packageManager, npmOptions, options) => {
|
||||||
await baseGenerator(packageManager, npmOptions, options, 'svelte', {
|
await baseGenerator(packageManager, npmOptions, options, 'svelte', {
|
||||||
extensions: ['js', 'jsx', 'ts', 'tsx', 'svelte'],
|
extensions: ['js', 'jsx', 'ts', 'tsx', 'svelte'],
|
||||||
commonJs: true,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ const defaultOptions: FrameworkOptions = {
|
|||||||
framework: undefined,
|
framework: undefined,
|
||||||
extensions: undefined,
|
extensions: undefined,
|
||||||
componentsDestinationPath: undefined,
|
componentsDestinationPath: undefined,
|
||||||
commonJs: false,
|
|
||||||
storybookConfigFolder: '.storybook',
|
storybookConfigFolder: '.storybook',
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -123,13 +122,7 @@ const hasFrameworkTemplates = (framework?: SupportedFrameworks) =>
|
|||||||
export async function baseGenerator(
|
export async function baseGenerator(
|
||||||
packageManager: JsPackageManager,
|
packageManager: JsPackageManager,
|
||||||
npmOptions: NpmOptions,
|
npmOptions: NpmOptions,
|
||||||
{
|
{ language, builder = CoreBuilder.Webpack5, pnp, frameworkPreviewParts }: GeneratorOptions,
|
||||||
language,
|
|
||||||
builder = CoreBuilder.Webpack5,
|
|
||||||
pnp,
|
|
||||||
commonJs,
|
|
||||||
frameworkPreviewParts,
|
|
||||||
}: GeneratorOptions,
|
|
||||||
renderer: SupportedRenderers,
|
renderer: SupportedRenderers,
|
||||||
options: FrameworkOptions = defaultOptions,
|
options: FrameworkOptions = defaultOptions,
|
||||||
framework?: SupportedFrameworks
|
framework?: SupportedFrameworks
|
||||||
@ -232,7 +225,7 @@ export async function baseGenerator(
|
|||||||
docs: { autodocs: 'tag' },
|
docs: { autodocs: 'tag' },
|
||||||
addons: pnp ? addons.map(wrapForPnp) : addons,
|
addons: pnp ? addons.map(wrapForPnp) : addons,
|
||||||
extensions,
|
extensions,
|
||||||
commonJs,
|
language,
|
||||||
...(staticDir ? { staticDirs: [path.join('..', staticDir)] } : null),
|
...(staticDir ? { staticDirs: [path.join('..', staticDir)] } : null),
|
||||||
...extraMain,
|
...extraMain,
|
||||||
...(type !== 'framework'
|
...(type !== 'framework'
|
||||||
@ -245,7 +238,7 @@ export async function baseGenerator(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await configurePreview({ frameworkPreviewParts, storybookConfigFolder });
|
await configurePreview({ frameworkPreviewParts, storybookConfigFolder, language });
|
||||||
|
|
||||||
// FIXME: temporary workaround for https://github.com/storybookjs/storybook/issues/17516
|
// FIXME: temporary workaround for https://github.com/storybookjs/storybook/issues/17516
|
||||||
if (
|
if (
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import fse from 'fs-extra';
|
import fse from 'fs-extra';
|
||||||
import { dedent } from 'ts-dedent';
|
import { dedent } from 'ts-dedent';
|
||||||
|
import { SupportedLanguage } from '../project_types';
|
||||||
|
|
||||||
interface ConfigureMainOptions {
|
interface ConfigureMainOptions {
|
||||||
addons: string[];
|
addons: string[];
|
||||||
extensions?: string[];
|
extensions?: string[];
|
||||||
commonJs?: boolean;
|
|
||||||
staticDirs?: string[];
|
staticDirs?: string[];
|
||||||
storybookConfigFolder: string;
|
storybookConfigFolder: string;
|
||||||
|
language: SupportedLanguage;
|
||||||
/**
|
/**
|
||||||
* Extra values for main.js
|
* Extra values for main.js
|
||||||
*
|
*
|
||||||
@ -26,46 +27,52 @@ export interface FrameworkPreviewParts {
|
|||||||
interface ConfigurePreviewOptions {
|
interface ConfigurePreviewOptions {
|
||||||
frameworkPreviewParts?: FrameworkPreviewParts;
|
frameworkPreviewParts?: FrameworkPreviewParts;
|
||||||
storybookConfigFolder: string;
|
storybookConfigFolder: string;
|
||||||
|
language: SupportedLanguage;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function configureMain({
|
export async function configureMain({
|
||||||
addons,
|
addons,
|
||||||
extensions = ['js', 'jsx', 'ts', 'tsx'],
|
extensions = ['js', 'jsx', 'ts', 'tsx'],
|
||||||
commonJs = false,
|
|
||||||
storybookConfigFolder,
|
storybookConfigFolder,
|
||||||
|
language,
|
||||||
...custom
|
...custom
|
||||||
}: ConfigureMainOptions) {
|
}: ConfigureMainOptions) {
|
||||||
const prefix = (await fse.pathExists('./src')) ? '../src' : '../stories';
|
const prefix = (await fse.pathExists('./src')) ? '../src' : '../stories';
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
stories: [`${prefix}/**/*.mdx`, `${prefix}/**/*.stories.@(${extensions.join('|')})`],
|
stories: [`${prefix}/**/*.mdx`, `${prefix}/**/*.stories.@(${extensions.join('|')})`],
|
||||||
addons,
|
addons,
|
||||||
...custom,
|
...custom,
|
||||||
};
|
};
|
||||||
|
|
||||||
// replace escaped values and delimiters
|
const isTypescript =
|
||||||
const stringified = `module.exports = ${JSON.stringify(config, null, 2)
|
language === SupportedLanguage.TYPESCRIPT || language === SupportedLanguage.TYPESCRIPT_LEGACY;
|
||||||
.replace(/\\"/g, '"')
|
|
||||||
.replace(/['"]%%/g, '')
|
const tsTemplate = dedent`<<import>>const config<<type>> = <<mainContents>>;
|
||||||
.replace(/%%['"]/g, '')
|
export default config;`;
|
||||||
.replace(/\\n/g, '\r\n')}`;
|
|
||||||
// main.js isn't actually JSON, but we used JSON.stringify to convert the runtime-object into code.
|
const jsTemplate = dedent`export default <<mainContents>>;`;
|
||||||
// un-stringify the value for referencing packages by string
|
|
||||||
// .replaceAll(/"(path\.dirname\(require\.resolve\(path\.join\('.*\))"/g, (_, a) => a)}`;
|
const finalTemplate = isTypescript ? tsTemplate : jsTemplate;
|
||||||
|
|
||||||
|
const mainJsContents = finalTemplate
|
||||||
|
.replace('<<import>>', `import { StorybookConfig } from '${custom.framework.name}';\n\n`)
|
||||||
|
.replace('<<type>>', ': StorybookConfig')
|
||||||
|
.replace('<<mainContents>>', JSON.stringify(config, null, 2));
|
||||||
|
|
||||||
await fse.writeFile(
|
await fse.writeFile(
|
||||||
`./${storybookConfigFolder}/main.${commonJs ? 'cjs' : 'js'}`,
|
`./${storybookConfigFolder}/main.${isTypescript ? 'ts' : 'js'}`,
|
||||||
dedent`
|
dedent(mainJsContents),
|
||||||
const path = require('path');
|
|
||||||
${stringified}
|
|
||||||
`,
|
|
||||||
{ encoding: 'utf8' }
|
{ encoding: 'utf8' }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function configurePreview(options: ConfigurePreviewOptions) {
|
export async function configurePreview(options: ConfigurePreviewOptions) {
|
||||||
const { prefix = '' } = options?.frameworkPreviewParts || {};
|
const { prefix = '' } = options.frameworkPreviewParts || {};
|
||||||
const previewPath = `./${options.storybookConfigFolder}/preview.js`;
|
const isTypescript =
|
||||||
|
options.language === SupportedLanguage.TYPESCRIPT ||
|
||||||
|
options.language === SupportedLanguage.TYPESCRIPT_LEGACY;
|
||||||
|
|
||||||
|
const previewPath = `./${options.storybookConfigFolder}/preview.${isTypescript ? 'ts' : 'js'}`;
|
||||||
|
|
||||||
// If the framework template included a preview then we have nothing to do
|
// If the framework template included a preview then we have nothing to do
|
||||||
if (await fse.pathExists(previewPath)) {
|
if (await fse.pathExists(previewPath)) {
|
||||||
|
@ -8,7 +8,6 @@ export type GeneratorOptions = {
|
|||||||
builder: Builder;
|
builder: Builder;
|
||||||
linkable: boolean;
|
linkable: boolean;
|
||||||
pnp: boolean;
|
pnp: boolean;
|
||||||
commonJs: boolean;
|
|
||||||
frameworkPreviewParts?: FrameworkPreviewParts;
|
frameworkPreviewParts?: FrameworkPreviewParts;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -24,7 +23,6 @@ export interface FrameworkOptions {
|
|||||||
extraMain?: any;
|
extraMain?: any;
|
||||||
extensions?: string[];
|
extensions?: string[];
|
||||||
framework?: Record<string, any>;
|
framework?: Record<string, any>;
|
||||||
commonJs?: boolean;
|
|
||||||
storybookConfigFolder?: string;
|
storybookConfigFolder?: string;
|
||||||
componentsDestinationPath?: string;
|
componentsDestinationPath?: string;
|
||||||
}
|
}
|
||||||
@ -49,7 +47,6 @@ export type CommandOptions = {
|
|||||||
yes?: boolean;
|
yes?: boolean;
|
||||||
builder?: Builder;
|
builder?: Builder;
|
||||||
linkable?: boolean;
|
linkable?: boolean;
|
||||||
commonJs?: boolean;
|
|
||||||
disableTelemetry?: boolean;
|
disableTelemetry?: boolean;
|
||||||
enableCrashReports?: boolean;
|
enableCrashReports?: boolean;
|
||||||
debug?: boolean;
|
debug?: boolean;
|
||||||
|
@ -61,7 +61,6 @@ const installStorybook = <Project extends ProjectType>(
|
|||||||
language,
|
language,
|
||||||
builder: options.builder || detectBuilder(packageManager),
|
builder: options.builder || detectBuilder(packageManager),
|
||||||
linkable: !!options.linkable,
|
linkable: !!options.linkable,
|
||||||
commonJs: options.commonJs,
|
|
||||||
pnp: options.usePnp,
|
pnp: options.usePnp,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -294,7 +293,6 @@ async function doInitiate(options: CommandOptions, pkg: PackageJson): Promise<vo
|
|||||||
const done = commandLog(infoText);
|
const done = commandLog(infoText);
|
||||||
|
|
||||||
const packageJson = packageManager.retrievePackageJson();
|
const packageJson = packageManager.retrievePackageJson();
|
||||||
const isEsm = packageJson && packageJson.type === 'module';
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (projectTypeProvided) {
|
if (projectTypeProvided) {
|
||||||
@ -329,10 +327,11 @@ async function doInitiate(options: CommandOptions, pkg: PackageJson): Promise<vo
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const installResult = await installStorybook(projectType as ProjectType, packageManager, {
|
const installResult = await installStorybook(
|
||||||
...options,
|
projectType as ProjectType,
|
||||||
...(isEsm ? { commonJs: true } : undefined),
|
packageManager,
|
||||||
}).catch((e) => {
|
options
|
||||||
|
).catch((e) => {
|
||||||
process.exit();
|
process.exit();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
"extends": "../tsconfig.app.json",
|
"extends": "../tsconfig.app.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"types": ["node"],
|
"types": ["node"],
|
||||||
"allowSyntheticDefaultImports": true
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"resolveJsonModule": true
|
||||||
},
|
},
|
||||||
"exclude": ["../src/test.ts", "../src/**/*.spec.ts"],
|
"exclude": ["../src/test.ts", "../src/**/*.spec.ts"],
|
||||||
"include": ["../src/**/*"],
|
"include": ["../src/**/*", "./preview.ts"],
|
||||||
"files": ["./typings.d.ts"]
|
"files": ["./typings.d.ts"]
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user