Skip to content

Commit

Permalink
Merge pull request #19680 from storybookjs/shilman/csf-plugin
Browse files Browse the repository at this point in the history
Addon-docs: Replace source-loader with csf-plugin
  • Loading branch information
shilman authored Nov 1, 2022
2 parents bf5ec56 + 498aa5f commit e8427c9
Show file tree
Hide file tree
Showing 22 changed files with 825 additions and 45 deletions.
28 changes: 28 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions code/addons/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ module.exports = {
options: {
configureJSX: true,
babelOptions: {},
sourceLoaderOptions: null,
csfPluginOptions: null,
transcludeMarkdown: true,
},
},
Expand All @@ -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.

Expand Down
4 changes: 2 additions & 2 deletions code/addons/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@
"@storybook/components": "7.0.0-alpha.46",
"@storybook/core-common": "7.0.0-alpha.46",
"@storybook/core-events": "7.0.0-alpha.46",
"@storybook/csf-plugin": "7.0.0-alpha.46",
"@storybook/csf-tools": "7.0.0-alpha.46",
"@storybook/docs-tools": "7.0.0-alpha.46",
"@storybook/mdx2-csf": "0.1.0-next.0",
"@storybook/mdx2-csf": "next",
"@storybook/node-logger": "7.0.0-alpha.46",
"@storybook/postinstall": "7.0.0-alpha.46",
"@storybook/preview-web": "7.0.0-alpha.46",
"@storybook/source-loader": "7.0.0-alpha.46",
"@storybook/store": "7.0.0-alpha.46",
"@storybook/theming": "7.0.0-alpha.46",
"@storybook/types": "7.0.0-alpha.46",
Expand Down
42 changes: 25 additions & 17 deletions code/addons/docs/src/preset.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import fs from 'fs-extra';
import remarkSlug from 'remark-slug';
import remarkExternalLinks from 'remark-external-links';
import { dedent } from 'ts-dedent';

import type {
CoreCommon_IndexerOptions,
CoreCommon_StoryIndexer,
DocsOptions,
Options,
} from '@storybook/types';
import { logger } from '@storybook/node-logger';
import type { CsfPluginOptions } from '@storybook/csf-plugin';
import { loadCsf } from '@storybook/csf-tools';

// for frameworks that are not working with react, we need to configure
Expand Down Expand Up @@ -48,7 +49,12 @@ function createBabelOptions({ babelOptions, mdxBabelOptions, configureJSX }: Bab
export async function webpack(
webpackConfig: any = {},
options: Options &
BabelParams & { sourceLoaderOptions: any; transcludeMarkdown: boolean } /* & Parameters<
BabelParams & {
/** @deprecated */
sourceLoaderOptions: any;
csfPluginOptions: CsfPluginOptions | null;
transcludeMarkdown: boolean;
} /* & Parameters<
typeof createCompiler
>[0] */
) {
Expand All @@ -62,7 +68,8 @@ export async function webpack(
babelOptions,
mdxBabelOptions,
configureJSX = true,
sourceLoaderOptions = { injectStoryParameters: true },
csfPluginOptions = {},
sourceLoaderOptions = null,
transcludeMarkdown = false,
} = options;

Expand All @@ -72,21 +79,17 @@ export async function webpack(
remarkPlugins: [remarkSlug, remarkExternalLinks],
};

logger.info(`Addon-docs: using MDX2`);
if (sourceLoaderOptions) {
throw new Error(dedent`
Addon-docs no longer uses source-loader in 7.0.
const mdxLoader = require.resolve('@storybook/mdx2-csf/loader');
To update your configuration, please see migration instructions here:
// 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',
},
]
: [];
https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#dropped-source-loader--storiesof-static-snippets
`);
}

const mdxLoader = require.resolve('@storybook/mdx2-csf/loader');

let rules = module.rules || [];
if (transcludeMarkdown) {
Expand All @@ -110,6 +113,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: [
Expand Down Expand Up @@ -144,7 +153,6 @@ export async function webpack(
},
],
},
...sourceLoader,
],
},
};
Expand Down
9 changes: 9 additions & 0 deletions code/addons/docs/template/stories/docspage/basic.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,24 @@ export default {
parameters: { chromatic: { disable: true } },
};

/**
* A basic button
*/
export const Basic = {
args: { label: 'Basic' },
};

/**
* Won't show up in DocsPage
*/
export const Disabled = {
args: { label: 'Disabled in DocsPage' },
parameters: { docs: { disable: true } },
};

/**
* Another button, just to show multiple stories
*/
export const Another = {
args: { label: 'Another' },
};
2 changes: 1 addition & 1 deletion code/lib/builder-vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"@storybook/client-api": "7.0.0-alpha.46",
"@storybook/client-logger": "7.0.0-alpha.46",
"@storybook/core-common": "7.0.0-alpha.46",
"@storybook/mdx2-csf": "0.1.0-next.0",
"@storybook/mdx2-csf": "next",
"@storybook/node-logger": "7.0.0-alpha.46",
"@storybook/preview-web": "7.0.0-alpha.46",
"@storybook/source-loader": "7.0.0-alpha.46",
Expand Down
26 changes: 26 additions & 0 deletions code/lib/csf-plugin/README.md
Original file line number Diff line number Diff line change
@@ -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.parameters = {
storySource: {
source: '() => <Button />',
},
...Basic.parameters,
};
```

This allows `@storybook/addon-docs` to display the static source snippet.
60 changes: 60 additions & 0 deletions code/lib/csf-plugin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"name": "@storybook/csf-plugin",
"version": "7.0.0-alpha.46",
"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.46",
"unplugin": "^0.10.2"
},
"devDependencies": {
"typescript": "~4.6.3"
},
"publishConfig": {
"access": "public"
},
"bundler": {
"entries": [
"./src/index.ts"
]
},
"gitHead": "3ef14366115c56c1d45c0359ff681cc47ed50532"
}
33 changes: 33 additions & 0 deletions code/lib/csf-plugin/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { createUnplugin } from 'unplugin';
import fs from 'fs/promises';
import { loadCsf, enrichCsf, formatCsf } from '@storybook/csf-tools';
import type { EnrichCsfOptions } from '@storybook/csf-tools';

export type CsfPluginOptions = EnrichCsfOptions;

const STORIES_REGEX = /\.(story|stories)\.[tj]sx?$/;

const logger = console;

export const unplugin = createUnplugin<CsfPluginOptions>((options) => {
return {
name: 'unplugin-csf',
enforce: 'pre',
loadInclude(id) {
return STORIES_REGEX.test(id);
},
async load(fname) {
const code = await fs.readFile(fname, 'utf-8');
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;
15 changes: 15 additions & 0 deletions code/lib/csf-plugin/tsconfig.json
Original file line number Diff line number Diff line change
@@ -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__/**"
]
}
1 change: 0 additions & 1 deletion code/lib/csf-tools/src/CsfFile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { dedent } from 'ts-dedent';
import yaml from 'js-yaml';
import { loadCsf } from './CsfFile';

// @ts-expect-error (Converted from ts-ignore)
expect.addSnapshotSerializer({
print: (val: any) => yaml.dump(val).trimEnd(),
test: (val) => typeof val !== 'string',
Expand Down
25 changes: 25 additions & 0 deletions code/lib/csf-tools/src/CsfFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ export class CsfFile {

_storyExports: Record<string, t.VariableDeclarator | t.FunctionDeclaration> = {};

_storyStatements: Record<string, t.ExportNamedDeclaration> = {};

_storyAnnotations: Record<string, Record<string, t.Node>> = {};

_templates: Record<string, t.Expression> = {};
Expand Down Expand Up @@ -215,6 +217,28 @@ export class CsfFile {
this._meta = meta;
}

getStoryExport(key: string) {
let node = this._storyExports[key] as t.Node;
node = t.isVariableDeclarator(node) ? node.init : node;
if (t.isCallExpression(node)) {
const { callee, arguments: bindArguments } = node;
if (
t.isMemberExpression(callee) &&
t.isIdentifier(callee.object) &&
t.isIdentifier(callee.property) &&
callee.property.name === 'bind' &&
(bindArguments.length === 0 ||
(bindArguments.length === 1 &&
t.isObjectExpression(bindArguments[0]) &&
bindArguments[0].properties.length === 0))
) {
const { name } = callee.object;
node = this._templates[name];
}
}
return node;
}

parse() {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
Expand Down Expand Up @@ -261,6 +285,7 @@ export class CsfFile {
return;
}
self._storyExports[exportName] = decl;
self._storyStatements[exportName] = node;
let name = storyNameFromExport(exportName);
if (self._storyAnnotations[exportName]) {
logger.warn(
Expand Down
Loading

0 comments on commit e8427c9

Please sign in to comment.