Add csf-plugin to replace source-loader

This commit is contained in:
Michael Shilman 2022-10-29 11:00:44 +08:00
parent ff964c09e9
commit f7edd49250
18 changed files with 261 additions and 53 deletions

View File

@ -37,6 +37,7 @@
- [Configuring the Docs Container](#configuring-the-docs-container)
- [External Docs](#external-docs)
- [MDX2 upgrade](#mdx2-upgrade)
- [Dropped source loader / storiesOf static snippets](#dropped-source-loader--storiesof-static-snippets)
- [Dropped addon-docs manual configuration](#dropped-addon-docs-manual-configuration)
- [7.0 Deprecations](#70-deprecations)
- [`Story` type deprecated](#story-type-deprecated)
@ -737,6 +738,33 @@ We will update this section with specific pointers based on user feedback during
As part of the upgrade we deleted the codemod `mdx-to-csf` and will be replacing it with a more sophisticated version prior to release.
#### Dropped source loader / storiesOf static snippets
In SB 6.x, Storybook Docs used a webpack loader called `source-loader` to help display static code snippets. This was configurable using the `options.sourceLoaderOptions` field.
In SB 7.0, we've moved to a faster, simpler alternative called `csf-plugin` that **only supports CSF**. It is configurable using the `options.csfPluginOptions` field.
If you're using `storiesOf` and want to restore the previous behavior, you can add `source-loader` by hand to your webpack config using the following snippet in `main.js`:
```js
module.exports = {
webpackFinal: (config) => {
config.modules.rules.push({
test: /\.stories\.[tj]sx?$/,
use: [
{
loader: require.resolve('@storybook/source-loader'),
options: {} /* your sourceLoaderOptions here */
},
],
enforce: 'pre',
})
return config;
}
}
```
#### Dropped addon-docs manual configuration
Storybook Docs 5.x shipped with instructions for how to manually configure webpack and storybook without the use of Storybook's "presets" feature. Over time, these docs went out of sync. Now in Storybook 7 we have removed support for manual configuration entirely.

View File

@ -142,7 +142,7 @@ module.exports = {
options: {
configureJSX: true,
babelOptions: {},
sourceLoaderOptions: null,
csfPluginOptions: null,
transcludeMarkdown: true,
},
},
@ -152,7 +152,7 @@ module.exports = {
The `configureJSX` option is useful when you're writing your docs in MDX and your project's babel config isn't already set up to handle JSX files. `babelOptions` is a way to further configure the babel processor when you're using `configureJSX`.
`sourceLoaderOptions` is an object for configuring `@storybook/source-loader`. When set to `null` it tells docs not to run the `source-loader` at all, which can be used as an optimization, or if you're already using `source-loader` in your `main.js`.
`csfPluginOptions` is an object for configuring `@storybook/csf-plugin`. When set to `null` it tells docs not to run the `csf-plugin` at all, which can be used as an optimization, or if you're already using `csf-plugin` in your `main.js`.
The `transcludeMarkdown` option enables mdx files to import `.md` files and render them as a component.

View File

@ -58,13 +58,13 @@
"@storybook/components": "7.0.0-alpha.45",
"@storybook/core-common": "7.0.0-alpha.45",
"@storybook/core-events": "7.0.0-alpha.45",
"@storybook/csf-plugin": "7.0.0-alpha.45",
"@storybook/csf-tools": "7.0.0-alpha.45",
"@storybook/docs-tools": "7.0.0-alpha.45",
"@storybook/mdx2-csf": "0.1.0-next.0",
"@storybook/mdx2-csf": "next",
"@storybook/node-logger": "7.0.0-alpha.45",
"@storybook/postinstall": "7.0.0-alpha.45",
"@storybook/preview-web": "7.0.0-alpha.45",
"@storybook/source-loader": "7.0.0-alpha.45",
"@storybook/store": "7.0.0-alpha.45",
"@storybook/theming": "7.0.0-alpha.45",
"@storybook/types": "7.0.0-alpha.45",

View File

@ -8,6 +8,7 @@ import type {
DocsOptions,
Options,
} from '@storybook/types';
import type { CsfPluginOptions } from '@storybook/csf-plugin';
import { logger } from '@storybook/node-logger';
import { loadCsf } from '@storybook/csf-tools';
@ -48,7 +49,10 @@ function createBabelOptions({ babelOptions, mdxBabelOptions, configureJSX }: Bab
export async function webpack(
webpackConfig: any = {},
options: Options &
BabelParams & { sourceLoaderOptions: any; transcludeMarkdown: boolean } /* & Parameters<
BabelParams & {
csfPluginOptions: CsfPluginOptions | null;
transcludeMarkdown: boolean;
} /* & Parameters<
typeof createCompiler
>[0] */
) {
@ -62,7 +66,7 @@ export async function webpack(
babelOptions,
mdxBabelOptions,
configureJSX = true,
sourceLoaderOptions = { injectStoryParameters: true },
csfPluginOptions = {},
transcludeMarkdown = false,
} = options;
@ -76,18 +80,6 @@ export async function webpack(
const mdxLoader = require.resolve('@storybook/mdx2-csf/loader');
// set `sourceLoaderOptions` to `null` to disable for manual configuration
const sourceLoader = sourceLoaderOptions
? [
{
test: /\.(stories|story)\.[tj]sx?$/,
loader: require.resolve('@storybook/source-loader'),
options: { ...sourceLoaderOptions, inspectLocalDependencies: true },
enforce: 'pre',
},
]
: [];
let rules = module.rules || [];
if (transcludeMarkdown) {
rules = [
@ -110,6 +102,12 @@ export async function webpack(
const result = {
...webpackConfig,
plugins: [
...(webpackConfig.plugins || []),
// eslint-disable-next-line global-require
...(csfPluginOptions ? [require('@storybook/csf-plugin').webpack(csfPluginOptions)] : []),
],
module: {
...module,
rules: [
@ -144,7 +142,6 @@ export async function webpack(
},
],
},
...sourceLoader,
],
},
};

View File

@ -22,7 +22,7 @@
"@storybook/client-api": "7.0.0-alpha.45",
"@storybook/client-logger": "7.0.0-alpha.45",
"@storybook/core-common": "7.0.0-alpha.45",
"@storybook/mdx2-csf": "0.1.0-next.0",
"@storybook/mdx2-csf": "next",
"@storybook/node-logger": "7.0.0-alpha.45",
"@storybook/preview-web": "7.0.0-alpha.45",
"@storybook/source-loader": "7.0.0-alpha.45",

View File

@ -0,0 +1,26 @@
# CSF Plugin
The CSF plugin reads CSF files and enriches their content via static analysis.
It supports Webpack, Vite, and other bundlers using [unplugin](https://github.com/unjs/unplugin).
## Source snippets
CSF plugin can add static source snippets to each story. For example:
```js
export const Basic = () => <Button />;
```
Would be transformed to:
```js
export const Basic = () => <Button />;
Basic.parameers = {
storySource: {
source: '() => <Button />',
},
...Basic.parameters,
};
```
This allows `@storybook/addon-docs` to display the static source snippet.

View File

@ -0,0 +1,60 @@
{
"name": "@storybook/csf-plugin",
"version": "7.0.0-alpha.45",
"description": "Enrich CSF files via static analysis",
"keywords": [
"storybook"
],
"homepage": "https://github.com/storybookjs/storybook/tree/main/lib/csf-plugin",
"bugs": {
"url": "https://github.com/storybookjs/storybook/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/storybookjs/storybook.git",
"directory": "lib/csf-plugin"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/storybook"
},
"license": "MIT",
"sideEffects": false,
"exports": {
".": {
"require": "./dist/cjs/index.js",
"import": "./dist/esm/index.mjs",
"types": "./dist/types/index.d.ts"
},
"./package.json": "./package.json"
},
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/types/index.d.ts",
"files": [
"dist/**/*",
"README.md",
"*.js",
"*.d.ts"
],
"scripts": {
"check": "../../../scripts/node_modules/.bin/tsc --noEmit",
"prep": "node ../../../scripts/prepare.js"
},
"dependencies": {
"@storybook/csf-tools": "7.0.0-alpha.45",
"unplugin": "^0.10.2"
},
"devDependencies": {
"typescript": "~4.6.3"
},
"publishConfig": {
"access": "public"
},
"bundler": {
"entries": [
"./src/index.ts"
]
},
"gitHead": "3ef14366115c56c1d45c0359ff681cc47ed50532"
}

View File

View File

@ -0,0 +1,31 @@
import { createUnplugin } from 'unplugin';
import { loadCsf, enrichCsf, formatCsf } from '@storybook/csf-tools';
export interface CsfPluginOptions {
source?: boolean;
}
const STORIES_REGEX = /\.(story|stories)\.[tj]sx?$/;
const logger = console;
export const unplugin = createUnplugin((options: CsfPluginOptions) => {
return {
name: 'unplugin-csf',
transformInclude(id) {
return STORIES_REGEX.test(id);
},
transform(code) {
try {
const csf = loadCsf(code, { makeTitle: (userTitle) => userTitle || 'default' }).parse();
enrichCsf(csf);
return formatCsf(csf);
} catch (err: any) {
logger.warn(err.message);
return code;
}
},
};
});
export const { vite, rollup, webpack, esbuild } = unplugin;

View File

@ -0,0 +1,15 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"strict": true
},
"include": ["src/**/*"],
"exclude": [
"src/**/*.test.*",
"src/**/tests/**/*",
"src/**/__tests__/**/*",
"src/**/*.stories.*",
"src/**/*.mockdata.*",
"src/**/__testfixtures__/**"
]
}

View File

@ -2,7 +2,7 @@
/* eslint-disable no-underscore-dangle */
import { dedent } from 'ts-dedent';
import { loadCsf } from './CsfFile';
import { loadCsf, formatCsf } from './CsfFile';
import { enrichCsf, extractSource } from './enrichCsf';
expect.addSnapshotSerializer({
@ -10,11 +10,19 @@ expect.addSnapshotSerializer({
test: (val) => true,
});
const enrich = (code: string) => {
// we don't actually care about the title
const csf = loadCsf(code, { makeTitle: (userTitle) => userTitle || 'default' }).parse();
enrichCsf(csf);
return formatCsf(csf);
};
describe('enrichCsf', () => {
describe('source', () => {
it('csf1', () => {
expect(
enrichCsf(dedent`
enrich(dedent`
export default {
title: 'Button',
}
@ -35,7 +43,7 @@ describe('enrichCsf', () => {
});
it('csf2', () => {
expect(
enrichCsf(dedent`
enrich(dedent`
export default {
title: 'Button',
}
@ -62,7 +70,7 @@ describe('enrichCsf', () => {
});
it('csf3', () => {
expect(
enrichCsf(dedent`
enrich(dedent`
export default {
title: 'Button',
}
@ -89,7 +97,7 @@ describe('enrichCsf', () => {
});
it('multiple stories', () => {
expect(
enrichCsf(dedent`
enrich(dedent`
export default {
title: 'Button',
}

View File

@ -2,11 +2,9 @@
import * as t from '@babel/types';
import generate from '@babel/generator';
import template from '@babel/template';
import { formatCsf, loadCsf } from './CsfFile';
export const enrichCsf = (code: string) => {
const csf = loadCsf(code, { makeTitle: (userTitle) => userTitle }).parse();
import { CsfFile, formatCsf, loadCsf } from './CsfFile';
export const enrichCsf = (csf: CsfFile) => {
Object.keys(csf._storyExports).forEach((key) => {
const storyExport = csf.getStoryExport(key);
const source = extractSource(storyExport);
@ -18,8 +16,6 @@ export const enrichCsf = (code: string) => {
}) as t.Statement;
csf._ast.program.body.push(addParameter);
});
return formatCsf(csf);
};
export const extractSource = (node: t.Node) => {

View File

@ -183,6 +183,7 @@
"@storybook/core-events": "workspace:*",
"@storybook/core-server": "workspace:*",
"@storybook/core-webpack": "workspace:*",
"@storybook/csf-plugin": "workspace:*",
"@storybook/csf-tools": "workspace:*",
"@storybook/docs-tools": "workspace:*",
"@storybook/ember": "workspace:*",

View File

@ -1,3 +1,4 @@
import { vite as csfPlugin } from '@storybook/csf-plugin';
import type { StorybookConfig } from '../../frameworks/react-vite/dist';
const isBlocksOnly = process.env.BLOCKS_ONLY === 'true';
@ -32,6 +33,10 @@ const config: StorybookConfig = {
core: {
disableTelemetry: true,
},
viteFinal: (vite) => ({
...vite,
plugins: [...(vite.plugins || []), csfPlugin({})],
}),
};
export default config;

View File

@ -191,6 +191,11 @@
"root": "lib/core-webpack",
"type": "library"
},
"@storybook/csf-plugin": {
"implicitDependencies": [],
"root": "lib/csf-plugin",
"type": "library"
},
"@storybook/csf-tools": {
"implicitDependencies": [],
"root": "lib/csf-tools",

View File

@ -2119,7 +2119,7 @@ __metadata:
languageName: node
linkType: hard
"@babel/template@npm:^7.16.7, @babel/template@npm:^7.18.10, @babel/template@npm:^7.3.3, @babel/template@npm:^7.4.0, @babel/template@npm:^7.7.0, @babel/template@npm:^7.8.6":
"@babel/template@npm:^7.12.11, @babel/template@npm:^7.16.7, @babel/template@npm:^7.18.10, @babel/template@npm:^7.3.3, @babel/template@npm:^7.4.0, @babel/template@npm:^7.7.0, @babel/template@npm:^7.8.6":
version: 7.18.10
resolution: "@babel/template@npm:7.18.10"
dependencies:
@ -2159,6 +2159,17 @@ __metadata:
languageName: node
linkType: hard
"@babel/types@npm:^7.14.8":
version: 7.20.0
resolution: "@babel/types@npm:7.20.0"
dependencies:
"@babel/helper-string-parser": ^7.19.4
"@babel/helper-validator-identifier": ^7.19.1
to-fast-properties: ^2.0.0
checksum: 8b9c960eb013142eaf6294d77b75e469b7e97461bd7ad939e625ed74865fbf5a1c20b7989ec3357d0f4ffd93dd79d6daead08c0c06647815d8bbe94dae445f5c
languageName: node
linkType: hard
"@base2/pretty-print-object@npm:1.0.1":
version: 1.0.1
resolution: "@base2/pretty-print-object@npm:1.0.1"
@ -5507,9 +5518,10 @@ __metadata:
"@storybook/components": 7.0.0-alpha.45
"@storybook/core-common": 7.0.0-alpha.45
"@storybook/core-events": 7.0.0-alpha.45
"@storybook/csf-plugin": 7.0.0-alpha.45
"@storybook/csf-tools": 7.0.0-alpha.45
"@storybook/docs-tools": 7.0.0-alpha.45
"@storybook/mdx2-csf": 0.1.0-next.0
"@storybook/mdx2-csf": next
"@storybook/node-logger": 7.0.0-alpha.45
"@storybook/postinstall": 7.0.0-alpha.45
"@storybook/preview-web": 7.0.0-alpha.45
@ -6182,7 +6194,7 @@ __metadata:
"@storybook/client-api": 7.0.0-alpha.45
"@storybook/client-logger": 7.0.0-alpha.45
"@storybook/core-common": 7.0.0-alpha.45
"@storybook/mdx2-csf": 0.1.0-next.0
"@storybook/mdx2-csf": next
"@storybook/node-logger": 7.0.0-alpha.45
"@storybook/preview-web": 7.0.0-alpha.45
"@storybook/source-loader": 7.0.0-alpha.45
@ -6617,12 +6629,23 @@ __metadata:
languageName: unknown
linkType: soft
"@storybook/csf-plugin@7.0.0-alpha.45, @storybook/csf-plugin@workspace:*, @storybook/csf-plugin@workspace:lib/csf-plugin":
version: 0.0.0-use.local
resolution: "@storybook/csf-plugin@workspace:lib/csf-plugin"
dependencies:
"@storybook/csf-tools": 7.0.0-alpha.45
typescript: ~4.6.3
unplugin: ^0.10.2
languageName: unknown
linkType: soft
"@storybook/csf-tools@7.0.0-alpha.45, @storybook/csf-tools@workspace:*, @storybook/csf-tools@workspace:lib/csf-tools":
version: 0.0.0-use.local
resolution: "@storybook/csf-tools@workspace:lib/csf-tools"
dependencies:
"@babel/generator": ^7.12.11
"@babel/parser": ^7.12.11
"@babel/template": ^7.12.11
"@babel/traverse": ^7.12.11
"@babel/types": ^7.12.11
"@storybook/csf": next
@ -6922,19 +6945,20 @@ __metadata:
languageName: unknown
linkType: soft
"@storybook/mdx2-csf@npm:0.1.0-next.0":
version: 0.1.0-next.0
resolution: "@storybook/mdx2-csf@npm:0.1.0-next.0"
"@storybook/mdx2-csf@npm:next":
version: 0.1.0-next.3
resolution: "@storybook/mdx2-csf@npm:0.1.0-next.3"
dependencies:
"@babel/generator": ^7.12.11
"@babel/parser": ^7.12.11
"@babel/types": ^7.14.8
"@mdx-js/mdx": ^2.0.0
estree-to-babel: ^4.9.0
hast-util-to-estree: ^2.0.2
js-string-escape: ^1.0.1
loader-utils: ^2.0.0
lodash: ^4.17.21
checksum: 116292c2bc658ad575dbc31c1aa6530f57e7c392ee1728143a8b31686e38ecb3fac6ccea860ce9860fe78167dabe6e09bed5ca5089594851b7733091ad0f91e9
checksum: b8cf49e6549507b0e176191ce58f2fb9ab152383e54c73d665414bd5013dc0b90d3cddc814e396563e6af7164b762f75a6a488091f2b46aa33b3fcbd89a12e70
languageName: node
linkType: hard
@ -7425,6 +7449,7 @@ __metadata:
"@storybook/core-events": "workspace:*"
"@storybook/core-server": "workspace:*"
"@storybook/core-webpack": "workspace:*"
"@storybook/csf-plugin": "workspace:*"
"@storybook/csf-tools": "workspace:*"
"@storybook/docs-tools": "workspace:*"
"@storybook/ember": "workspace:*"
@ -34888,6 +34913,18 @@ __metadata:
languageName: node
linkType: hard
"unplugin@npm:^0.10.2":
version: 0.10.2
resolution: "unplugin@npm:0.10.2"
dependencies:
acorn: ^8.8.0
chokidar: ^3.5.3
webpack-sources: ^3.2.3
webpack-virtual-modules: ^0.4.5
checksum: 3e326b470df042af62c1ea1febe43b0681554663ad50257ad2ae14cf261740d0137570d97d2067ee3cdd6b819a4249f1aa3dff05a8c7476ad12715263b0b1ae8
languageName: node
linkType: hard
"unset-value@npm:^1.0.0":
version: 1.0.0
resolution: "unset-value@npm:1.0.0"
@ -36031,7 +36068,7 @@ __metadata:
languageName: node
linkType: hard
"webpack-virtual-modules@npm:^0.4.3":
"webpack-virtual-modules@npm:^0.4.3, webpack-virtual-modules@npm:^0.4.5":
version: 0.4.5
resolution: "webpack-virtual-modules@npm:0.4.5"
checksum: 2a7c2dbb2e5e32b06b4ab3778609ce70d1ba4bbc05d3231a3da5219dc03131d555dda4a7d4694d100a344f87e458378ea7ba241ba2a8b81b1866e53c99af5aee

View File

@ -81,18 +81,18 @@ Below is an abridged configuration and table with all the available options for
<!-- prettier-ignore-end -->
| Addon | Configuration element | Description |
| ------------------------------ | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `@storybook/addon-actions` | N/A | N/A |
| `@storybook/addon-viewport` | N/A | N/A |
| `@storybook/addon-docs` | `configureJSX` | Enables JSX support in MDX for projects that aren't configured to handle the format. <br/> `configureJSX: true` |
| | `babelOptions` | Provides additional Babel configurations for file transpilation. <br/> `babelOptions: { plugins: [], presets: []}` <br/> Extends `configureJSX`. |
| | `sourceLoaderOptions` | Provides additional configuration for Storybook's source loader. <br/> `sourceLoaderOptions: null` . <br/> Required for [`@storybook/addon-storysource`](https://storybook.js.org/addons/@storybook/addon-storysource). |
| | `transcludeMarkdown` | Enables Markdown file support into MDX and render them as components. <br/> `transcludeMarkdown: true` |
| `@storybook/addon-controls` | N/A | N/A |
| `@storybook/addon-backgrounds` | N/A | N/A |
| `@storybook/addon-toolbars` | N/A | N/A |
| `@storybook/addon-measure` | N/A | N/A |
| Addon | Configuration element | Description |
| ------------------------------ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `@storybook/addon-actions` | N/A | N/A |
| `@storybook/addon-viewport` | N/A | N/A |
| `@storybook/addon-docs` | `configureJSX` | Enables JSX support in MDX for projects that aren't configured to handle the format. <br/> `configureJSX: true` |
| | `babelOptions` | Provides additional Babel configurations for file transpilation. <br/> `babelOptions: { plugins: [], presets: []}` <br/> Extends `configureJSX`. |
| | `csfPluginOptions` | Provides additional configuration for Storybook's CSF plugin. Can be disabled with `null` |
| | `transcludeMarkdown` | Enables Markdown file support into MDX and render them as components. <br/> `transcludeMarkdown: true` |
| `@storybook/addon-controls` | N/A | N/A |
| `@storybook/addon-backgrounds` | N/A | N/A |
| `@storybook/addon-toolbars` | N/A | N/A |
| `@storybook/addon-measure` | N/A | N/A |
When you start Storybook, your custom configuration will override the default.

View File

@ -12,7 +12,6 @@ module.exports = {
options: {
configureJSX: true,
babelOptions: {},
sourceLoaderOptions: null,
transcludeMarkdown: true,
},
},
@ -23,4 +22,4 @@ module.exports = {
'@storybook/addon-outline',
],
};
```
```