Merge branch 'next' into 7101-source-loader

This commit is contained in:
Michael Shilman 2019-06-24 17:39:20 +08:00
commit f77ce4aed3
54 changed files with 2050 additions and 73 deletions

View File

@ -13,6 +13,7 @@ const withTests = {
};
module.exports = {
ignore: ['./lib/codemod/src/transforms/__testfixtures__'],
presets: [
['@babel/preset-env', { shippedProposals: true, useBuiltIns: 'usage', corejs: '3' }],
'@babel/preset-typescript',

View File

@ -34,7 +34,7 @@
"@storybook/core-events": "5.2.0-alpha.23",
"@storybook/theming": "5.2.0-alpha.23",
"common-tags": "^1.8.0",
"core-js": "^2.6.5",
"core-js": "^3.0.1",
"global": "^4.3.2",
"react": "^16.8.3",
"use-image": "^1.0.3"

View File

@ -1,3 +1,88 @@
# Storybook Docs
Living documentation for your components.
- [Sneak peak article](https://medium.com/storybookjs/storybook-docs-sneak-peak-5be78445094a)
- [Technical preview guide](https://docs.google.com/document/d/1un6YX7xDKEKl5-MVb-egnOYN8dynb5Hf7mq0hipk8JE/edit?usp=sharing)
## View layer support
Docs supports all view layers that Storybook supports except for React Native (currently). There are some view-layer specific
features as well. This chart captures the current state of support
| | React | Vue | Angular | Polymer | Mithril | HTML | Marko | Svelte | Riot | Ember | Preact |
| -------------- | :---: | :-: | :-----: | :-----: | :-----: | :--: | :---: | :----: | :--: | :---: | :----: |
| MDX stories | + | + | + | + | + | + | + | + | + | + | + |
| Module stories | + | + | + | + | + | + | + | + | + | + | + |
| Legacy stories | + | + | + | + | + | + | + | + | + | + | + |
| Source \* | + | + | + | + | + | + | + | + | + | + | + |
| Notes / Info | + | + | + | + | + | + | + | + | + | + | + |
| Props table | + | # | # | | | | | | | | |
| Docgen | + | # | # | | | | | | | | |
| Inline stories | + | # | | | | | | | | | |
**Notes:**
- `#` denotes planned/WIP support
- \* Source supports legacy `JS storiesOf` and `MDX` stories. `Typescript` and new `module format` support is WIP
## Installation
First add the package. Make sure that the versions for your `@storybook/*` packages match:
```sh
yarn add -D @storybook/addon-docs
```
The add the following line to your `.storybook/presets.js` file:
```js
module.exports = ['@storybook/addon-docs/react/preset'];
```
Finally, import your stories and MDX files in `.storybook/config.js`:
```js
import { load } from '@storybook/react';
// standard configuration here
// ...
// wherever your story files are located
load(require.context('../src', true, /\.stories\.js$/), module);
load(require.context('../src', true, /\.stories\.mdx$/), module);
```
## Manual configuration
Docs uses Storybook presets as a configuration shortcut. To configure "the long way", first register the addon in `.storybook/addons.js`:
```js
import '@storybook/addon-docs/register';
```
Then configure Storybook's webpack loader to understand MDX files in `.storybook/webpack.config.js`:
```js
const createCompiler = require('@storybook/addon-docs/mdx-compiler-plugin');
module.exports = async ({ config }) => {
config.module.rules.push({
test: /\.mdx$/,
use: [
{
loader: 'babel-loader',
// may or may not need this line depending on your app's setup
plugins: ['@babel/plugin-transform-react-jsx'],
},
{
loader: '@mdx-js/loader',
options: {
compilers: [createCompiler({})],
},
},
],
});
return config;
};
```

View File

@ -0,0 +1,539 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`docs-mdx-compiler-plugin supports "smart" current story 1`] = `
"/* @jsx mdx */
import { DocsContainer } from '@storybook/addon-docs/blocks';
import { Story } from '@storybook/addon-docs/blocks';
const makeShortcode = name =>
function MDXDefaultShortcode(props) {
console.warn(
'Component ' +
name +
' was not imported, exported, or provided by MDXProvider as global scope'
);
return <div {...props} />;
};
const layoutProps = {};
const MDXLayout = 'wrapper';
function MDXContent({ components, ...props }) {
return (
<MDXLayout {...layoutProps} {...props} components={components} mdxType=\\"MDXLayout\\">
<h1>{\`Current story\`}</h1>
<Story id=\\".\\" mdxType=\\"Story\\" />
</MDXLayout>
);
}
MDXContent.isMDXComponent = true;
const componentMeta = {};
const mdxKind = componentMeta.title || componentMeta.displayName;
const WrappedMDXContent = ({ context }) => (
<DocsContainer context={{ ...context, mdxKind }} content={MDXContent} />
);
componentMeta.parameters = componentMeta.parameters || {};
componentMeta.parameters.docs = WrappedMDXContent;
export default componentMeta;
"
`;
exports[`docs-mdx-compiler-plugin supports decorators 1`] = `
"/* @jsx mdx */
import { DocsContainer } from '@storybook/addon-docs/blocks';
import { Button } from '@storybook/react/demo';
import { Story, Meta } from '@storybook/addon-docs/blocks';
const makeShortcode = name =>
function MDXDefaultShortcode(props) {
console.warn(
'Component ' +
name +
' was not imported, exported, or provided by MDXProvider as global scope'
);
return <div {...props} />;
};
const layoutProps = {};
const MDXLayout = 'wrapper';
function MDXContent({ components, ...props }) {
return (
<MDXLayout {...layoutProps} {...props} components={components} mdxType=\\"MDXLayout\\">
<Meta
title=\\"Button\\"
decorators={[
storyFn => (
<div
style={{
backgroundColor: 'yellow',
}}
>
{storyFn()}
</div>
),
]}
mdxType=\\"Meta\\"
/>
<h1>{\`Decorated story\`}</h1>
<Story name=\\"one\\" mdxType=\\"Story\\">
<Button mdxType=\\"Button\\">One</Button>
</Story>
</MDXLayout>
);
}
MDXContent.isMDXComponent = true;
export const one = () => <Button>One</Button>;
one.parameters = { mdxSource: \`<Button>One</Button>\` };
const componentMeta = {
title: 'Button',
decorators: [
storyFn => (
<div
style={{
backgroundColor: 'yellow',
}}
>
{storyFn()}
</div>
),
],
};
const mdxKind = componentMeta.title || componentMeta.displayName;
const WrappedMDXContent = ({ context }) => (
<DocsContainer context={{ ...context, mdxKind }} content={MDXContent} />
);
componentMeta.parameters = componentMeta.parameters || {};
componentMeta.parameters.docs = WrappedMDXContent;
export default componentMeta;
"
`;
exports[`docs-mdx-compiler-plugin supports object-style story definitions 1`] = `
"/* @jsx mdx */
import { DocsContainer } from '@storybook/addon-docs/blocks';
import { Story, Meta } from '@storybook/addon-docs/blocks';
import { Welcome, Button } from '@storybook/angular/demo';
import { linkTo } from '@storybook/addon-links';
const makeShortcode = name =>
function MDXDefaultShortcode(props) {
console.warn(
'Component ' +
name +
' was not imported, exported, or provided by MDXProvider as global scope'
);
return <div {...props} />;
};
const layoutProps = {};
const MDXLayout = 'wrapper';
function MDXContent({ components, ...props }) {
return (
<MDXLayout {...layoutProps} {...props} components={components} mdxType=\\"MDXLayout\\">
<Meta title=\\"MDX|Welcome\\" mdxType=\\"Meta\\" />
<h1>{\`Story object\`}</h1>
<Story name=\\"to storybook\\" height=\\"300px\\" mdxType=\\"Story\\">
{{
template: \`<storybook-welcome-component (showApp)=\\"showApp()\\"></storybook-welcome-component>\`,
props: {
showApp: linkTo('Button'),
},
moduleMetadata: {
declarations: [Welcome],
},
}}
</Story>
</MDXLayout>
);
}
MDXContent.isMDXComponent = true;
export const toStorybook = () => ({
template: \`<storybook-welcome-component (showApp)=\\"showApp()\\"></storybook-welcome-component>\`,
props: {
showApp: linkTo('Button'),
},
moduleMetadata: {
declarations: [Welcome],
},
});
toStorybook.title = 'to storybook';
toStorybook.parameters = {
mdxSource: \`{
template: \\\\\`<storybook-welcome-component (showApp)=\\"showApp()\\"></storybook-welcome-component>\\\\\`,
props: {
showApp: linkTo('Button')
},
moduleMetadata: {
declarations: [Welcome]
}
}\`,
};
const componentMeta = { title: 'MDX|Welcome' };
const mdxKind = componentMeta.title || componentMeta.displayName;
const WrappedMDXContent = ({ context }) => (
<DocsContainer context={{ ...context, mdxKind }} content={MDXContent} />
);
componentMeta.parameters = componentMeta.parameters || {};
componentMeta.parameters.docs = WrappedMDXContent;
export default componentMeta;
"
`;
exports[`docs-mdx-compiler-plugin supports parameters 1`] = `
"/* @jsx mdx */
import { DocsContainer } from '@storybook/addon-docs/blocks';
import { Button } from '@storybook/react/demo';
import { Story, Meta } from '@storybook/addon-docs/blocks';
const makeShortcode = name =>
function MDXDefaultShortcode(props) {
console.warn(
'Component ' +
name +
' was not imported, exported, or provided by MDXProvider as global scope'
);
return <div {...props} />;
};
const layoutProps = {};
const MDXLayout = 'wrapper';
function MDXContent({ components, ...props }) {
return (
<MDXLayout {...layoutProps} {...props} components={components} mdxType=\\"MDXLayout\\">
<Meta
title=\\"Button\\"
parameters={{
component: Button,
notes: 'component notes',
}}
mdxType=\\"Meta\\"
/>
<Story name=\\"component notes\\" mdxType=\\"Story\\">
<Button mdxType=\\"Button\\">Component notes</Button>
</Story>
<Story
name=\\"story notes\\"
parameters={{
notes: 'story notes',
}}
mdxType=\\"Story\\"
>
<Button mdxType=\\"Button\\">Story notes</Button>
</Story>
</MDXLayout>
);
}
MDXContent.isMDXComponent = true;
export const componentNotes = () => <Button>Component notes</Button>;
componentNotes.title = 'component notes';
componentNotes.parameters = { mdxSource: \`<Button>Component notes</Button>\` };
export const storyNotes = () => <Button>Story notes</Button>;
storyNotes.title = 'story notes';
storyNotes.parameters = {
mdxSource: \`<Button>Story notes</Button>\`,
...{
notes: 'story notes',
},
};
const componentMeta = {
title: 'Button',
parameters: {
component: Button,
notes: 'component notes',
},
};
const mdxKind = componentMeta.title || componentMeta.displayName;
const WrappedMDXContent = ({ context }) => (
<DocsContainer context={{ ...context, mdxKind }} content={MDXContent} />
);
componentMeta.parameters = componentMeta.parameters || {};
componentMeta.parameters.docs = WrappedMDXContent;
export default componentMeta;
"
`;
exports[`docs-mdx-compiler-plugin supports previews 1`] = `
"/* @jsx mdx */
import { DocsContainer } from '@storybook/addon-docs/blocks';
import { Button } from '@storybook/react/demo';
import { Preview, Story, Meta } from '@storybook/addon-docs/blocks';
const makeShortcode = name =>
function MDXDefaultShortcode(props) {
console.warn(
'Component ' +
name +
' was not imported, exported, or provided by MDXProvider as global scope'
);
return <div {...props} />;
};
const layoutProps = {};
const MDXLayout = 'wrapper';
function MDXContent({ components, ...props }) {
return (
<MDXLayout {...layoutProps} {...props} components={components} mdxType=\\"MDXLayout\\">
<Meta
title=\\"Button\\"
parameters={{
component: Button,
notes: 'component notes',
}}
mdxType=\\"Meta\\"
/>
<h1>{\`Preview\`}</h1>
<p>{\`Previews can contain normal components, stories, and story references\`}</p>
<Preview mdxType=\\"Preview\\">
<Button mdxType=\\"Button\\">Just a button</Button>
<Story name=\\"hello button\\" mdxType=\\"Story\\">
<Button mdxType=\\"Button\\">Hello button</Button>
</Story>
<Story name=\\"two\\" mdxType=\\"Story\\">
<Button mdxType=\\"Button\\">Two</Button>
</Story>
<Story id=\\"welcome--welcome\\" mdxType=\\"Story\\" />
</Preview>
</MDXLayout>
);
}
MDXContent.isMDXComponent = true;
export const helloButton = () => <Button>Hello button</Button>;
helloButton.title = 'hello button';
helloButton.parameters = { mdxSource: \`<Button>Hello button</Button>\` };
export const two = () => <Button>Two</Button>;
two.parameters = { mdxSource: \`<Button>Two</Button>\` };
const componentMeta = {
title: 'Button',
parameters: {
component: Button,
notes: 'component notes',
},
};
const mdxKind = componentMeta.title || componentMeta.displayName;
const WrappedMDXContent = ({ context }) => (
<DocsContainer context={{ ...context, mdxKind }} content={MDXContent} />
);
componentMeta.parameters = componentMeta.parameters || {};
componentMeta.parameters.docs = WrappedMDXContent;
export default componentMeta;
"
`;
exports[`docs-mdx-compiler-plugin supports story definitions 1`] = `
"/* @jsx mdx */
import { DocsContainer } from '@storybook/addon-docs/blocks';
import { Button } from '@storybook/react/demo';
import { Story, Meta } from '@storybook/addon-docs/blocks';
const makeShortcode = name =>
function MDXDefaultShortcode(props) {
console.warn(
'Component ' +
name +
' was not imported, exported, or provided by MDXProvider as global scope'
);
return <div {...props} />;
};
const layoutProps = {};
const MDXLayout = 'wrapper';
function MDXContent({ components, ...props }) {
return (
<MDXLayout {...layoutProps} {...props} components={components} mdxType=\\"MDXLayout\\">
<Meta title=\\"Button\\" mdxType=\\"Meta\\" />
<h1>{\`Story definition\`}</h1>
<Story name=\\"one\\" mdxType=\\"Story\\">
<Button mdxType=\\"Button\\">One</Button>
</Story>
<Story name=\\"hello story\\" mdxType=\\"Story\\">
<Button mdxType=\\"Button\\">Hello button</Button>
</Story>
</MDXLayout>
);
}
MDXContent.isMDXComponent = true;
export const one = () => <Button>One</Button>;
one.parameters = { mdxSource: \`<Button>One</Button>\` };
export const helloStory = () => <Button>Hello button</Button>;
helloStory.title = 'hello story';
helloStory.parameters = { mdxSource: \`<Button>Hello button</Button>\` };
const componentMeta = { title: 'Button' };
const mdxKind = componentMeta.title || componentMeta.displayName;
const WrappedMDXContent = ({ context }) => (
<DocsContainer context={{ ...context, mdxKind }} content={MDXContent} />
);
componentMeta.parameters = componentMeta.parameters || {};
componentMeta.parameters.docs = WrappedMDXContent;
export default componentMeta;
"
`;
exports[`docs-mdx-compiler-plugin supports story references 1`] = `
"/* @jsx mdx */
import { DocsContainer } from '@storybook/addon-docs/blocks';
import { Story } from '@storybook/addon-docs/blocks';
const makeShortcode = name =>
function MDXDefaultShortcode(props) {
console.warn(
'Component ' +
name +
' was not imported, exported, or provided by MDXProvider as global scope'
);
return <div {...props} />;
};
const layoutProps = {};
const MDXLayout = 'wrapper';
function MDXContent({ components, ...props }) {
return (
<MDXLayout {...layoutProps} {...props} components={components} mdxType=\\"MDXLayout\\">
<h1>{\`Story reference\`}</h1>
<Story id=\\"welcome--welcome\\" mdxType=\\"Story\\" />
</MDXLayout>
);
}
MDXContent.isMDXComponent = true;
const componentMeta = {};
const mdxKind = componentMeta.title || componentMeta.displayName;
const WrappedMDXContent = ({ context }) => (
<DocsContainer context={{ ...context, mdxKind }} content={MDXContent} />
);
componentMeta.parameters = componentMeta.parameters || {};
componentMeta.parameters.docs = WrappedMDXContent;
export default componentMeta;
"
`;
exports[`docs-mdx-compiler-plugin supports text-only story definitions 1`] = `
"/* @jsx mdx */
import { DocsContainer } from '@storybook/addon-docs/blocks';
import { Story, Meta } from '@storybook/addon-docs/blocks';
const makeShortcode = name =>
function MDXDefaultShortcode(props) {
console.warn(
'Component ' +
name +
' was not imported, exported, or provided by MDXProvider as global scope'
);
return <div {...props} />;
};
const layoutProps = {};
const MDXLayout = 'wrapper';
function MDXContent({ components, ...props }) {
return (
<MDXLayout {...layoutProps} {...props} components={components} mdxType=\\"MDXLayout\\">
<Meta title=\\"Text\\" mdxType=\\"Meta\\" />
<h1>{\`Story definition\`}</h1>
<Story name=\\"text\\" mdxType=\\"Story\\">
Plain text
</Story>
</MDXLayout>
);
}
MDXContent.isMDXComponent = true;
export const text = () => 'Plain text';
text.parameters = { mdxSource: \`'Plain text'\` };
const componentMeta = { title: 'Text' };
const mdxKind = componentMeta.title || componentMeta.displayName;
const WrappedMDXContent = ({ context }) => (
<DocsContainer context={{ ...context, mdxKind }} content={MDXContent} />
);
componentMeta.parameters = componentMeta.parameters || {};
componentMeta.parameters.docs = WrappedMDXContent;
export default componentMeta;
"
`;
exports[`docs-mdx-compiler-plugin supports vanilla mdx 1`] = `
"/* @jsx mdx */
import { DocsContainer } from '@storybook/addon-docs/blocks';
import { Button } from '@storybook/react/demo';
const makeShortcode = name =>
function MDXDefaultShortcode(props) {
console.warn(
'Component ' +
name +
' was not imported, exported, or provided by MDXProvider as global scope'
);
return <div {...props} />;
};
const layoutProps = {};
const MDXLayout = 'wrapper';
function MDXContent({ components, ...props }) {
return (
<MDXLayout {...layoutProps} {...props} components={components} mdxType=\\"MDXLayout\\">
<h1>{\`Hello MDX\`}</h1>
<p>{\`This is some random content.\`}</p>
<Button mdxType=\\"Button\\">Hello button</Button>
</MDXLayout>
);
}
MDXContent.isMDXComponent = true;
const componentMeta = {};
const mdxKind = componentMeta.title || componentMeta.displayName;
const WrappedMDXContent = ({ context }) => (
<DocsContainer context={{ ...context, mdxKind }} content={MDXContent} />
);
componentMeta.parameters = componentMeta.parameters || {};
componentMeta.parameters.docs = WrappedMDXContent;
export default componentMeta;
"
`;

View File

@ -0,0 +1,2 @@
// FIXME: move this to typescript and src/react folder
module.exports = require('../dist/lib/getPropDefs');

View File

@ -0,0 +1,82 @@
const createCompiler = require('../mdx-compiler-plugin');
function createBabelOptions({ babelOptions, configureJSX }) {
if (!configureJSX) {
return babelOptions;
}
return {
...babelOptions,
// for frameworks that are not working with react, we need to configure
// the jsx to transpile mdx, for now there will be a flag for that
// for more complex solutions we can find alone that we need to add '@babel/plugin-transform-react-jsx'
plugins: [...babelOptions.plugins, '@babel/plugin-transform-react-jsx'],
};
}
function webpack(webpackConfig = {}, options = {}) {
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 { babelOptions, configureJSX } = options;
return {
...webpackConfig,
module: {
...module,
rules: [
...(module.rules || []),
// {
// test: [/\.stories\.(jsx?$|ts?$)/],
// enforce: 'pre',
// use: [
// {
// loader: require.resolve('@storybook/addon-storysource/loader'),
// options: {
// injectParameters: true,
// },
// },
// ],
// },
{
test: /\.stories.mdx$/,
use: [
{
loader: 'babel-loader',
options: createBabelOptions({ babelOptions, configureJSX }),
},
{
loader: '@mdx-js/loader',
options: {
compilers: [createCompiler(options)],
},
},
],
},
{
test: /\.mdx$/,
exclude: /\.stories.mdx$/,
use: [
{
loader: 'babel-loader',
options: createBabelOptions({ babelOptions, configureJSX }),
},
{
loader: '@mdx-js/loader',
},
],
},
],
},
};
}
function addons(entry = []) {
return [
...entry,
// require.resolve('@storybook/addon-storysource/register'),
require.resolve('../register'),
];
}
module.exports = { webpack, addons };

View File

@ -0,0 +1,13 @@
import { Button } from '@storybook/react/demo';
import { Story, Meta } from '@storybook/addon-docs/blocks';
<Meta
title="Button"
decorators={[storyFn => <div style={{ backgroundColor: 'yellow' }}>{storyFn()}</div>]}
/>
# Decorated story
<Story name="one">
<Button>One</Button>
</Story>

View File

@ -0,0 +1,12 @@
import { Button } from '@storybook/react/demo';
import { Story, Meta } from '@storybook/addon-docs/blocks';
<Meta title="Button" parameters={{ component: Button, notes: 'component notes' }} />
<Story name="component notes">
<Button>Component notes</Button>
</Story>
<Story name="story notes" parameters={{ notes: 'story notes' }}>
<Button>Story notes</Button>
</Story>

View File

@ -0,0 +1,19 @@
import { Button } from '@storybook/react/demo';
import { Preview, Story, Meta } from '@storybook/addon-docs/blocks';
<Meta title="Button" parameters={{ component: Button, notes: 'component notes' }} />
# Preview
Previews can contain normal components, stories, and story references
<Preview>
<Button>Just a button</Button>
<Story name="hello button">
<Button>Hello button</Button>
</Story>
<Story name="two">
<Button>Two</Button>
</Story>
<Story id="welcome--welcome" />
</Preview>

View File

@ -0,0 +1,5 @@
import { Story } from '@storybook/addon-docs/blocks';
# Current story
<Story id="." />

View File

@ -0,0 +1,7 @@
import { Story, Meta } from '@storybook/addon-docs/blocks';
<Meta title="Text" />
# Story definition
<Story name="text">Plain text</Story>

View File

@ -0,0 +1,14 @@
import { Button } from '@storybook/react/demo';
import { Story, Meta } from '@storybook/addon-docs/blocks';
<Meta title="Button" />
# Story definition
<Story name="one">
<Button>One</Button>
</Story>
<Story name="hello story">
<Button>Hello button</Button>
</Story>

View File

@ -0,0 +1,10 @@
import { Button } from '@storybook/react/demo';
import { Story, Meta } from '@storybook/addon-docs/blocks';
<Meta title="Button" />
# Bad story
<Story>
<Button>One</Button>
</Story>

View File

@ -0,0 +1,19 @@
import { Story, Meta } from '@storybook/addon-docs/blocks';
import { Welcome, Button } from '@storybook/angular/demo';
import { linkTo } from '@storybook/addon-links';
<Meta title="MDX|Welcome" />
# Story object
<Story name="to storybook" height="300px">
{{
template: `<storybook-welcome-component (showApp)="showApp()"></storybook-welcome-component>`,
props: {
showApp: linkTo('Button'),
},
moduleMetadata: {
declarations: [Welcome],
},
}}
</Story>

View File

@ -0,0 +1,5 @@
import { Story } from '@storybook/addon-docs/blocks';
# Story reference
<Story id="welcome--welcome" />

View File

@ -0,0 +1,7 @@
import { Button } from '@storybook/react/demo';
# Hello MDX
This is some random content.
<Button>Hello button</Button>

View File

@ -0,0 +1,201 @@
const mdxToJsx = require('@mdx-js/mdx/mdx-hast-to-jsx');
const parser = require('@babel/parser');
const generate = require('@babel/generator').default;
const camelCase = require('lodash/camelCase');
// Generate the MDX as is, but append named exports for every
// story in the contents
const STORY_REGEX = /^<Story[\s>]/;
const PREVIEW_REGEX = /^<Preview[\s>]/;
const META_REGEX = /^<Meta[\s>]/;
const RESERVED = /^(?:do|if|in|for|let|new|try|var|case|else|enum|eval|false|null|this|true|void|with|await|break|catch|class|const|super|throw|while|yield|delete|export|import|public|return|static|switch|typeof|default|extends|finally|package|private|continue|debugger|function|arguments|interface|protected|implements|instanceof)$/;
function getAttr(elt, what) {
const attr = elt.attributes.find(n => n.name.name === what);
return attr && attr.value;
}
function getStoryFn(name, counter) {
if (name) {
const storyFn = camelCase(name.replace(/[^a-z0-9-]/g, '-'));
if (storyFn.length > 1 && !RESERVED.exec(storyFn)) {
return storyFn;
}
}
return `story${counter}`;
}
function genStoryExport(ast, counter) {
let storyName = getAttr(ast.openingElement, 'name');
let storyId = getAttr(ast.openingElement, 'id');
storyName = storyName && storyName.value;
storyId = storyId && storyId.value;
if (!storyId && !storyName) {
throw new Error('Expected a story name or ID attribute');
}
// We don't generate exports for story references or the smart "current story"
if (storyId || !storyName) {
return null;
}
// console.log('genStoryExport', JSON.stringify(ast, null, 2));
const statements = [];
const storyFn = getStoryFn(storyName, counter);
let body = ast.children.find(n => n.type !== 'JSXText');
let storyCode = null;
if (!body) {
// plain text node
const { code } = generate(ast.children[0], {});
storyCode = `'${code}'`;
} else {
if (body.type === 'JSXExpressionContainer') {
// FIXME: handle fragments
body = body.expression;
}
const { code } = generate(body, {});
storyCode = code;
}
statements.push(
`export const ${storyFn} = () => (
${storyCode}
);`
);
if (storyName !== storyFn) {
statements.push(`${storyFn}.title = '${storyName}';`);
}
let parameters = getAttr(ast.openingElement, 'parameters');
parameters = parameters && parameters.expression;
const source = `\`${storyCode.replace(/`/g, '\\`')}\``;
if (parameters) {
const { code: params } = generate(parameters, {});
// FIXME: hack in the story's source as a parameter
statements.push(`${storyFn}.parameters = { mdxSource: ${source}, ...${params} };`);
} else {
statements.push(`${storyFn}.parameters = { mdxSource: ${source} };`);
}
// console.log(statements);
return [statements.join('\n')];
}
function genPreviewExports(ast, counter) {
// console.log('genPreviewExports', JSON.stringify(ast, null, 2));
let localCounter = counter;
const previewExports = [];
for (let i = 0; i < ast.children.length; i += 1) {
const child = ast.children[i];
if (child.type === 'JSXElement' && child.openingElement.name.name === 'Story') {
const storyExport = genStoryExport(child, localCounter);
if (storyExport) {
previewExports.push(storyExport);
localCounter += 1;
}
}
}
return previewExports;
}
function genMetaExport(ast) {
let title = getAttr(ast.openingElement, 'title');
let parameters = getAttr(ast.openingElement, 'parameters');
let decorators = getAttr(ast.openingElement, 'decorators');
title = title && `title: '${title.value}',`;
if (parameters && parameters.expression) {
const { code: params } = generate(parameters.expression, {});
parameters = `parameters: ${params},`;
}
if (decorators && decorators.expression) {
const { code: decos } = generate(decorators.expression, {});
decorators = `decorators: ${decos},`;
}
return `const componentMeta = { ${title || ''} ${parameters || ''} ${decorators || ''} };`;
}
function getExports(node, counter) {
const { value, type } = node;
if (type === 'jsx') {
if (STORY_REGEX.exec(value)) {
// Single story
const ast = parser.parseExpression(value, { plugins: ['jsx'] });
const storyExport = genStoryExport(ast, counter);
return storyExport && { stories: [storyExport] };
}
if (PREVIEW_REGEX.exec(value)) {
// Preview, possibly containing multiple stories
const ast = parser.parseExpression(value, { plugins: ['jsx'] });
return { stories: genPreviewExports(ast, counter) };
}
if (META_REGEX.exec(value)) {
// Preview, possibly containing multiple stories
const ast = parser.parseExpression(value, { plugins: ['jsx'] });
return { meta: genMetaExport(ast) };
}
}
return null;
}
// insert `mdxKind` into the context so that we can know what "kind" we're rendering into
// when we render <Story name="xxx">...</Story>, since this MDX can be attached to any `selectedKind`!
const wrapperJs = `
const mdxKind = componentMeta.title || componentMeta.displayName;
const WrappedMDXContent = ({ context }) => <DocsContainer context={{...context, mdxKind}} content={MDXContent} />;
componentMeta.parameters = componentMeta.parameters || {};
componentMeta.parameters.docs = WrappedMDXContent;
`.trim();
function extractExports(node, options) {
// we're overriding default export
const defaultJsx = mdxToJsx.toJSX(node, {}, { ...options, skipExport: true });
const storyExports = [];
let metaExport = null;
let counter = 0;
node.children.forEach(n => {
const exports = getExports(n, counter);
if (exports) {
const { stories, meta } = exports;
if (stories) {
stories.forEach(story => {
storyExports.push(story);
counter += 1;
});
}
if (meta) {
if (metaExport) {
throw new Error('Meta can only be declared once');
}
metaExport = meta;
}
}
});
if (!metaExport) {
metaExport = 'const componentMeta = { };';
}
const fullJsx = [
'import { DocsContainer } from "@storybook/addon-docs/blocks";',
defaultJsx,
...storyExports,
metaExport,
wrapperJs,
'export default componentMeta;',
].join('\n\n');
return fullJsx;
}
function createCompiler(mdxOptions) {
return function compiler(options = {}) {
this.Compiler = tree => extractExports(tree, options, mdxOptions);
};
}
module.exports = createCompiler;

View File

@ -0,0 +1,71 @@
const path = require('path');
const fs = require('fs-extra');
const mdx = require('@mdx-js/mdx');
const prettier = require('prettier');
const plugin = require('./mdx-compiler-plugin');
function format(code) {
return prettier.format(code, {
parser: 'babel',
printWidth: 100,
tabWidth: 2,
bracketSpacing: true,
trailingComma: 'es5',
singleQuote: true,
});
}
async function generate(filePath) {
const content = await fs.readFile(filePath, 'utf8');
const result = mdx.sync(content, {
filepath: filePath,
compilers: [plugin({})],
});
return format(result);
}
describe('docs-mdx-compiler-plugin', () => {
it('supports vanilla mdx', async () => {
const code = await generate(path.resolve(__dirname, './fixtures/vanilla.mdx'));
expect(code).toMatchSnapshot();
});
it('supports story definitions', async () => {
const code = await generate(path.resolve(__dirname, './fixtures/story-definitions.mdx'));
expect(code).toMatchSnapshot();
});
it('supports text-only story definitions', async () => {
const code = await generate(path.resolve(__dirname, './fixtures/story-def-text-only.mdx'));
expect(code).toMatchSnapshot();
});
it('supports object-style story definitions', async () => {
const code = await generate(path.resolve(__dirname, './fixtures/story-object.mdx'));
expect(code).toMatchSnapshot();
});
it('supports story references', async () => {
const code = await generate(path.resolve(__dirname, './fixtures/story-references.mdx'));
expect(code).toMatchSnapshot();
});
it('supports "smart" current story', async () => {
const code = await generate(path.resolve(__dirname, './fixtures/story-current.mdx'));
expect(code).toMatchSnapshot();
});
it('supports previews', async () => {
const code = await generate(path.resolve(__dirname, './fixtures/previews.mdx'));
expect(code).toMatchSnapshot();
});
it('supports decorators', async () => {
const code = await generate(path.resolve(__dirname, './fixtures/decorators.mdx'));
expect(code).toMatchSnapshot();
});
it('supports parameters', async () => {
const code = await generate(path.resolve(__dirname, './fixtures/parameters.mdx'));
expect(code).toMatchSnapshot();
});
it('errors on missing story props', async () => {
await expect(
generate(path.resolve(__dirname, './fixtures/story-missing-props.mdx'))
).rejects.toThrow('Expected a story name or ID attribute');
});
});

View File

@ -23,6 +23,11 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@babel/generator": "^7.4.0",
"@babel/parser": "^7.4.2",
"@mdx-js/loader": "^1.0.0",
"@mdx-js/mdx": "^1.0.0",
"@mdx-js/react": "^1.0.16",
"@storybook/addons": "5.2.0-alpha.23",
"@storybook/api": "5.2.0-alpha.23",
"@storybook/components": "5.2.0-alpha.23",
@ -30,6 +35,7 @@
"@storybook/theming": "5.2.0-alpha.23",
"core-js": "^3.0.1",
"global": "^4.3.2",
"lodash": "^4.17.11",
"prop-types": "^15.7.2"
},
"devDependencies": {

View File

@ -0,0 +1 @@
module.exports = require('../common/index');

View File

@ -0,0 +1 @@
module.exports = require('../common/preset');

View File

@ -1,7 +1,7 @@
/* eslint-disable react/destructuring-assignment */
import React from 'react';
// import { MDXProvider } from '@mdx-js/react';
import { MDXProvider } from '@mdx-js/react';
import { Global, createGlobal, ThemeProvider, ensure as ensureTheme } from '@storybook/theming';
import { DocumentFormatting, DocsWrapper, DocsContent } from '@storybook/components';
import { DocsContextProps, DocsContext } from './DocsContext';
@ -39,13 +39,13 @@ export const DocsContainer: React.FunctionComponent<DocsContainerProps> = ({
<DocsContext.Provider value={context}>
<ThemeProvider theme={theme}>
<Global styles={globalWithOverflow} />
{/* <MDXProvider components={components}> */}
<DocsWrapper>
<DocsContent>
<MDXContent components={components} />
</DocsContent>
</DocsWrapper>
{/* </MDXProvider> */}
<MDXProvider components={components}>
<DocsWrapper>
<DocsContent>
<MDXContent components={components} />
</DocsContent>
</DocsWrapper>
</MDXProvider>
</ThemeProvider>
</DocsContext.Provider>
);

1
addons/docs/src/typings.d.ts vendored Normal file
View File

@ -0,0 +1 @@
declare module '@mdx-js/react';

1
addons/docs/vue/index.js Normal file
View File

@ -0,0 +1 @@
module.exports = require('../common/index');

View File

@ -0,0 +1 @@
module.exports = require('../common/preset');

View File

@ -30,7 +30,7 @@
"@storybook/core-events": "5.2.0-alpha.23",
"@storybook/theming": "5.2.0-alpha.23",
"common-tags": "^1.8.0",
"core-js": "^2.6.5",
"core-js": "^3.0.1",
"global": "^4.3.2",
"qs": "^6.6.0",
"react": "^16.8.3"

View File

@ -31,7 +31,7 @@
"@storybook/core-events": "5.2.0-alpha.23",
"@storybook/theming": "5.2.0-alpha.23",
"common-tags": "^1.8.0",
"core-js": "^2.6.5",
"core-js": "^3.0.1",
"global": "^4.3.2",
"react": "^16.8.3"
},

View File

@ -1,44 +1,44 @@
{
"name": "@storybook/addon-roundtrip",
"version": "5.2.0-alpha.23",
"description": "roundtrip addon for storybook",
"keywords": [
"devkit",
"addon",
"storybook",
"roundtrip"
],
"homepage": "https://github.com/storybookjs/storybook#readme",
"bugs": {
"url": "https://github.com/storybookjs/storybook/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/storybookjs/storybook.git",
"directory": "dev-kit/addon-roundtrip"
},
"license": "MIT",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.2.0-alpha.23",
"@storybook/api": "5.2.0-alpha.23",
"@storybook/client-logger": "5.2.0-alpha.23",
"@storybook/components": "5.2.0-alpha.23",
"@storybook/core-events": "5.2.0-alpha.23",
"@storybook/theming": "5.2.0-alpha.23",
"common-tags": "^1.8.0",
"core-js": "^2.6.5",
"global": "^4.3.2",
"react": "^16.8.3"
},
"devDependencies": {
"@types/common-tags": "^1.8.0"
},
"publishConfig": {
"access": "public"
}
"name": "@storybook/addon-roundtrip",
"version": "5.2.0-alpha.23",
"description": "roundtrip addon for storybook",
"keywords": [
"devkit",
"addon",
"storybook",
"roundtrip"
],
"homepage": "https://github.com/storybooks/storybook#readme",
"bugs": {
"url": "https://github.com/storybooks/storybook/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/storybooks/storybook.git",
"directory": "dev-kit/addon-roundtrip"
},
"license": "MIT",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.2.0-alpha.23",
"@storybook/api": "5.2.0-alpha.23",
"@storybook/client-logger": "5.2.0-alpha.23",
"@storybook/components": "5.2.0-alpha.23",
"@storybook/core-events": "5.2.0-alpha.23",
"@storybook/theming": "5.2.0-alpha.23",
"common-tags": "^1.8.0",
"core-js": "^3.0.1",
"global": "^4.3.2",
"react": "^16.8.3"
},
"devDependencies": {
"@types/common-tags": "^1.8.0"
},
"publishConfig": {
"access": "public"
}
}

View File

@ -63,3 +63,4 @@ addParameters({
load(require.context('../../lib/ui/src', true, /\.stories\.js$/), module);
load(require.context('../../lib/components/src', true, /\.stories\.tsx?$/), module);
load(require.context('./stories', true, /\.stories\.js$/), module);
load(require.context('./stories', true, /\.stories\.mdx$/), module);

View File

@ -0,0 +1 @@
module.exports = ['@storybook/addon-docs/react/preset'];

View File

@ -1,8 +1,37 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import notes from './notes/notes.md';
import mdxNotes from './notes/notes.mdx';
import DocgenButton from '../components/DocgenButton';
storiesOf('Addons|Docs', module)
.addParameters({
docs: () => <div>Hello docs</div>,
})
.add('default', () => <div>Click the docs tab to see the docs</div>);
export default {
title: 'Addons|Docs/stories',
parameters: {
component: DocgenButton,
},
};
export const basic = () => <div>Click docs tab to see basic docs</div>;
export const withNotes = () => <div>Click docs tab to see DocsPage docs</div>;
withNotes.title = 'with notes';
withNotes.parameters = {
notes,
};
export const withInfo = () => <div>Click docs tab to see DocsPage docs</div>;
withInfo.title = 'with info';
withInfo.parameters = {
info: 'some user info string',
};
export const mdxOverride = () => <div>Click docs tab to see MDX-overridden docs</div>;
mdxOverride.title = 'mdx override';
mdxOverride.parameters = {
docs: mdxNotes,
};
export const jsxOverride = () => <div>Click docs tab to see JSX-overridden docs</div>;
jsxOverride.title = 'jsx override';
jsxOverride.parameters = {
docs: () => <div>Hello docs</div>,
};

View File

@ -0,0 +1,80 @@
import {
Story,
Preview,
Props,
Source,
Description,
ColorPalette,
ColorItem,
Meta,
} from '@storybook/addon-docs/blocks';
import { action } from '@storybook/addon-actions';
import { Button } from '@storybook/react/demo';
<Meta
title="Addons|Docs/mdx"
decorators={[storyFn => <div style={{ backgroundColor: 'yellow' }}>{storyFn()}</div>]}
parameters={{ component: Button, notes: 'component notes' }}
/>
# Selected
<Story id="." />
<Source id="." />
<Props of="." />
## A random color ColorPalette
<ColorPalette>
<ColorItem
title="theme.color.greyscale"
subtitle="Some of the greys"
colors={['#FFFFFF', '#F8F8F8', '#F3F3F3']}
/>
<ColorItem title="theme.color.primary" subtitle="Coral" colors={['#FF4785']} />
<ColorItem title="theme.color.secondary" subtitle="Ocean" colors={['#1EA7FD']} />
<ColorItem
title="theme.color.positive"
subtitle="Green"
colors={[
'rgba(102,191,60,1)',
'rgba(102,191,60,.8)',
'rgba(102,191,60,.6)',
'rgba(102,191,60,.3)',
]}
/>
</ColorPalette>
## Getting into details
<Button>just a button, not a story</Button>
<Preview>
<Story name="hello story">
<Button onClick={action('clicked')}>hello world</Button>
</Story>
<Story name="goodbye">
<Button onClick={action('clicked')}>goodbye world</Button>
</Story>
<Story name="with icons">
<Button onClick={action('clicked')}>🤘🚀💯</Button>
</Story>
<Story name="notes" parameters={{ notes: 'story notes' }}>
<Button onClick={action('clicked')}>goodbye world</Button>
</Story>
<Story name="plaintext">Plain text</Story>
</Preview>
<Source name="hello story" />
## Configurable height
<Story id="basics-button--all-buttons" height="400px" />
## Description
<Description markdown="this is _markdown_" />
<Description of={Button} />

View File

@ -0,0 +1,3 @@
# Welcome!
Let's just write markdown without stories

View File

@ -1,5 +1,6 @@
import program from 'commander';
import chalk from 'chalk';
import envinfo from 'envinfo';
import pkg from '../package.json';
import initiate from '../lib/initiate';
import { codeLog } from '../lib/helpers';
@ -31,6 +32,21 @@ if (process.argv[1].includes('getstorybook')) {
.option('-N --use-npm', 'Use NPM to build the Storybook server')
.action((addonName, options) => add(addonName, options));
program
.command('info')
.description('Prints debugging information about the local environment')
.action(() => {
console.log(chalk.bold('\nEnvironment Info:'));
envinfo
.run({
System: ['OS', 'CPU'],
Binaries: ['Node', 'Yarn', 'npm'],
Browsers: ['Chrome', 'Edge', 'Firefox', 'Safari'],
npmGlobalPackages: ['@storybook/cli'],
})
.then(console.log);
});
program.command('*', { noHelp: true }).action(cmd => {
logger.error('Invalid command: %s.\nSee --help for a list of available commands.', cmd);
process.exit(1);

View File

@ -35,6 +35,7 @@
"commander": "^2.19.0",
"core-js": "^3.0.1",
"cross-spawn": "^6.0.5",
"envinfo": "^7.3.1",
"fs-extra": "^8.0.1",
"inquirer": "^6.2.0",
"jscodeshift": "^0.6.3",

View File

@ -132,3 +132,51 @@ Heuristics:
- The storiesOf "kind" name must be Button
- Button must be imported in the file
### convert-to-module-format
This converts all of your "old-style" `storiesOf` stories into component module format, which uses standard ES6 modules.
```sh
./node_modules/.bin/jscodeshift -t ./node_modules/@storybook/codemod/dist/transforms/convert-to-module-format.js . --ignore-pattern "node_modules|dist"
```
For example:
```js
storiesOf('Button', module)
.add('story', () => <Button label="Story 1" />)
.add('second story', () => <Button label="Story 2" onClick={action('click')} />)
.add('complex story', () => (
<div>
<Button label="The Button" onClick={action('onClick')} />
<br />
</div>
));
```
Becomes:
```js
export default {
title: 'Button',
};
export const story = () => <Button label="Story 1" />;
export const story2 = () => <Button label="Story 2" onClick={action('click')} />;
story2.title = 'second story';
export const story3 = () => (
<div>
<Button label="The Button" onClick={action('onClick')} />
<br />
</div>
);
story3.title = 'complex story';
```
Heuristics:
- If a file has any default export, it will be skipped
- If a file has multiple `storiesOf` declarations, it will convert each one seperately. This generates invalid ES6, but you can edit the file by hand to split it into multiple files (or whatever is appropriate).

View File

@ -23,6 +23,7 @@
"dependencies": {
"core-js": "^3.0.1",
"jscodeshift": "^0.6.3",
"prettier": "^1.16.4",
"regenerator-runtime": "^0.12.1"
},
"publishConfig": {

View File

@ -0,0 +1,16 @@
/* eslint-disable */
import React from 'react';
import Button from './Button';
import { storiesOf, configure } from '@storybook/react';
import { action } from '@storybook/addon-actions';
storiesOf('Button', module)
.add('story1', () => <Button label="Story 1" />)
.add('second story', () => <Button label="Story 2" onClick={action('click')} />)
.add('complex story', () => (
<div>
<Button label="The Button" onClick={action('onClick')} />
<br />
</div>
));

View File

@ -0,0 +1,23 @@
/* eslint-disable */
import React from 'react';
import Button from './Button';
import { configure } from '@storybook/react';
import { action } from '@storybook/addon-actions';
export default {
title: 'Button',
};
export const story1 = () => <Button label="Story 1" />;
export const story2 = () => <Button label="Story 2" onClick={action('click')} />;
story2.title = 'second story';
export const story3 = () => (
<div>
<Button label="The Button" onClick={action('onClick')} />
<br />
</div>
);
story3.title = 'complex story';

View File

@ -0,0 +1,9 @@
/* eslint-disable */
import React from 'react';
import Button from './Button';
// This isn't a valid story, but it tests the `import { comp } from ...` case
storiesOf('Some.Button', module)
.addDecorator(withKnobs)
.addDecorator(storyFn => <div className="foo">{storyFn}</div>)
.add('with decorator', () => <Button label="The Button" />);

View File

@ -0,0 +1,11 @@
/* eslint-disable */
import React from 'react';
import Button from './Button';
export default {
title: 'Some.Button',
decorators: [withKnobs, storyFn => <div className="foo">{storyFn}</div>],
};
export const story1 = () => <Button label="The Button" />;
story1.title = 'with decorator';

View File

@ -0,0 +1,12 @@
/* eslint-disable */
import React from 'react';
import Button from './Button';
import { storiesOf } from '@storybook/react';
export default {
title: 'foo',
};
const bar = 1;
storiesOf('foo', module).add('bar', () => <Button />);

View File

@ -0,0 +1,12 @@
/* eslint-disable */
import React from 'react';
import Button from './Button';
import { storiesOf } from '@storybook/react';
export default {
title: 'foo',
};
const bar = 1;
storiesOf('foo', module).add('bar', () => <Button />);

View File

@ -0,0 +1,14 @@
/* eslint-disable */
import React from 'react';
import Button from './Button';
// If we have multiple storiesOf calls, export multiple defaults. It's not valid
// JS but will still save the user time in converting.
storiesOf('Button1', module)
.add('story1', () => <Button label="Button1.1" />)
.add('story2', () => <Button label="Button1.2" />);
storiesOf('Button2', module)
.add('story1', () => <Button label="Button2.1" />)
.add('story2', () => <Button label="Button2.2" />);

View File

@ -0,0 +1,17 @@
/* eslint-disable */
import React from 'react';
import Button from './Button';
export default {
title: 'Button1',
};
export const story1 = () => <Button label="Button1.1" />;
export const story2 = () => <Button label="Button1.2" />;
export default {
title: 'Button2',
};
export const story1 = () => <Button label="Button2.1" />;
export const story2 = () => <Button label="Button2.2" />;

View File

@ -0,0 +1,10 @@
/* eslint-disable */
import React from 'react';
import Button from './Button';
import { storiesOf } from '@storybook/react';
storiesOf('Button', module)
.addParameters({ component: Button, foo: 1 })
.addParameters({ bar: 2 })
.add('with kind parameters', () => <Button label="The Button" />);

View File

@ -0,0 +1,16 @@
/* eslint-disable */
import React from 'react';
import Button from './Button';
export default {
title: 'Button',
parameters: {
component: Button,
foo: 1,
bar: 2,
},
};
export const story1 = () => <Button label="The Button" />;
story1.title = 'with kind parameters';

View File

@ -0,0 +1,14 @@
/* eslint-disable */
import React from 'react';
import Button from './Button';
import { storiesOf } from '@storybook/react';
storiesOf('Button', module)
.add('with story parameters', () => <Button label="The Button" />, {
header: false,
inline: true,
})
.add('foo', () => <Button label="Foo" />, {
bar: 1,
});

View File

@ -0,0 +1,21 @@
/* eslint-disable */
import React from 'react';
import Button from './Button';
export default {
title: 'Button',
};
export const story1 = () => <Button label="The Button" />;
story1.title = 'with story parameters';
story1.parameters = {
header: false,
inline: true,
};
export const foo = () => <Button label="Foo" />;
foo.parameters = {
bar: 1,
};

View File

@ -0,0 +1,7 @@
import { defineTest } from 'jscodeshift/dist/testUtils';
const testNames = ['basic', 'decorators', 'parameters', 'story-parameters', 'module', 'multi'];
testNames.forEach(testName => {
defineTest(__dirname, `convert-to-module-format`, null, `convert-to-module-format/${testName}`);
});

View File

@ -0,0 +1,202 @@
import prettier from 'prettier';
/**
* Convert a legacy story file to module format
*
* For example:
*
* ```
* input { Button } from './Button';
* storiesOf('Button', module).add('story', () => <Button label="The Button" />);
* ```
*
* Becomes:
*
* ```
* input { Button } from './Button';
* export default {
* title: 'Button'
* }
* export const story = () => <Button label="The Button" />;
*
* NOTES: only support chained storiesOf() calls
*/
export default function transformer(file, api, options) {
const j = api.jscodeshift;
const root = j(file.source);
function convertToModuleExports(path) {
const base = j(path);
let counter = 1;
const statements = [];
const extraExports = [];
// .addDecorator
const decorators = [];
base
.find(j.CallExpression)
.filter(
call => call.node.callee.property && call.node.callee.property.name === 'addDecorator'
)
.forEach(add => {
const decorator = add.node.arguments[0];
decorators.push(decorator);
});
if (decorators.length > 0) {
decorators.reverse();
extraExports.push(
j.property('init', j.identifier('decorators'), j.arrayExpression(decorators))
);
}
// .addParameters
const parameters = [];
base
.find(j.CallExpression)
.filter(
call => call.node.callee.property && call.node.callee.property.name === 'addParameters'
)
.forEach(add => {
// jscodeshift gives us the find results in reverse, but these args come in
// order, so we double reverse here. ugh.
const params = [...add.node.arguments[0].properties];
params.reverse();
params.forEach(prop => parameters.push(prop));
});
if (parameters.length > 0) {
parameters.reverse();
extraExports.push(
j.property('init', j.identifier('parameters'), j.objectExpression(parameters))
);
}
// storiesOf(...)
base
.find(j.CallExpression)
.filter(call => call.node.callee.name === 'storiesOf')
.filter(call => call.node.arguments.length > 0 && call.node.arguments[0].type === 'Literal')
.forEach(storiesOf => {
const title = storiesOf.node.arguments[0].value;
statements.push(
j.exportDefaultDeclaration(
j.objectExpression([
j.property('init', j.identifier('title'), j.literal(title)),
...extraExports,
])
)
);
});
// .add(...)
const adds = [];
base
.find(j.CallExpression)
.filter(add => add.node.callee.property && add.node.callee.property.name === 'add')
.filter(add => add.node.arguments.length >= 2 && add.node.arguments[0].type === 'Literal')
.forEach(add => adds.push(add));
adds.reverse();
adds.push(path);
adds.forEach(add => {
let name = add.node.arguments[0].value;
let title = null;
if (/\s/.exec(name)) {
title = name;
name = `story${counter}`;
}
const val = add.node.arguments[1];
statements.push(
j.exportDeclaration(
false,
j.variableDeclaration('const', [j.variableDeclarator(j.identifier(name), val)])
)
);
if (title) {
statements.push(
j.assignmentStatement(
'=',
j.memberExpression(j.identifier(name), j.identifier('title')),
j.literal(title)
)
);
}
if (add.node.arguments.length > 2) {
const storyParams = add.node.arguments[2];
statements.push(
j.assignmentStatement(
'=',
j.memberExpression(j.identifier(name), j.identifier('parameters')),
storyParams
)
);
}
counter += 1;
});
statements.reverse();
statements.forEach(s => path.parent.insertAfter(s));
base.remove();
}
// Save the original storiesOf
const initialStoriesOf = root
.find(j.CallExpression)
.filter(call => call.node.callee.name === 'storiesOf');
const defaultExports = root.find(j.ExportDefaultDeclaration);
// If there's already a default export
if (defaultExports.size() > 0) {
if (initialStoriesOf.size() > 0) {
// eslint-disable-next-line no-console
console.log(
`Found ${initialStoriesOf.size()} 'storiesOf' calls but existing default export, SKIPPING: '${
file.path
}'`
);
}
return root.toSource();
}
// each top-level add expression corresponds to the last "add" of the chain.
// replace it with the entire export statements
root
.find(j.CallExpression)
.filter(add => add.node.callee.property && add.node.callee.property.name === 'add')
.filter(add => add.node.arguments.length >= 2 && add.node.arguments[0].type === 'Literal')
.filter(add => add.parentPath.node.type === 'ExpressionStatement')
.forEach(convertToModuleExports);
// remove storiesOf import
root
.find(j.ImportSpecifier)
.filter(
spec =>
spec.node.imported.name === 'storiesOf' &&
spec.parent.node.source.value.startsWith('@storybook/')
)
.forEach(spec => {
const toRemove = spec.parent.node.specifiers.length > 1 ? spec : spec.parent;
j(toRemove).remove();
});
const source = root.toSource({ trailingComma: true, quote: 'single', tabWidth: 2 });
if (initialStoriesOf.size() > 1) {
// eslint-disable-next-line no-console
console.log(
`Found ${initialStoriesOf.size()} 'storiesOf' calls, PLEASE FIX BY HAND: '${file.path}'`
);
return source;
}
return prettier.format(source, {
parser: options.parser || 'babel',
// FIXME: storybook defaults
printWidth: 100,
tabWidth: 2,
bracketSpacing: true,
trailingComma: 'es5',
singleQuote: true,
});
}

View File

@ -162,7 +162,13 @@ export default function start(render, { decorateStory } = {}) {
kind === previousKind &&
name === previousStory
) {
addons.getChannel().emit(Events.STORY_UNCHANGED, id);
addons.getChannel().emit(Events.STORY_UNCHANGED, {
id,
revision,
kind,
name,
viewMode,
});
return;
}
@ -271,7 +277,9 @@ export default function start(render, { decorateStory } = {}) {
if (!fileExports.default.title) {
throw new Error(
`Unexpected default export without title: ${JSON.stringify(fileExports.default)}`
`Unexpected default export without title in '${filename}': ${JSON.stringify(
fileExports.default
)}`
);
}

View File

@ -93,7 +93,7 @@ const Background = styled.div(
top: 0,
left: 0,
width: '100%',
height: '100%',
minHeight: '100%',
transition: 'background .1s linear',
iframe: {
width: '100%',

324
yarn.lock
View File

@ -539,7 +539,7 @@
esutils "^2.0.2"
js-tokens "^4.0.0"
"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.4.3", "@babel/parser@^7.4.4", "@babel/parser@^7.4.5":
"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.4.2", "@babel/parser@^7.4.3", "@babel/parser@^7.4.4", "@babel/parser@^7.4.5":
version "7.4.5"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.4.5.tgz#04af8d5d5a2b044a2a1bffacc1e5e6673544e872"
integrity sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==
@ -3144,6 +3144,41 @@
npmlog "^4.1.2"
write-file-atomic "^2.3.0"
"@mdx-js/loader@^1.0.0":
version "1.0.21"
resolved "https://registry.yarnpkg.com/@mdx-js/loader/-/loader-1.0.21.tgz#72fa5422ead35ac68aa452a032a84dd4f49e845b"
integrity sha512-1e/OZSGRSJHjp/ICroPVgja3v97q4pKm1TTFo/Hn1G5DWihOIFf+PUZgqD58Y1mbWhSHZrP4ZLmySVjmCjgUog==
dependencies:
"@mdx-js/mdx" "^1.0.21"
"@mdx-js/react" "^1.0.21"
loader-utils "^1.1.0"
"@mdx-js/mdx@^1.0.0", "@mdx-js/mdx@^1.0.21":
version "1.0.21"
resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.0.21.tgz#028a7975fff026222f7ace19c8c130290c649025"
integrity sha512-B+n3PvrtdUcaCgDmWFaBf4n/zsls5hoyNPkWe2CzUx3ggR0SoD4UqCQR7iIZZ//fUjAwFODGf+2H0aJ3tIlB7w==
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-syntax-jsx" "^7.2.0"
"@babel/plugin-syntax-object-rest-spread" "^7.2.0"
change-case "^3.0.2"
detab "^2.0.0"
hast-util-raw "^5.0.0"
lodash.uniq "^4.5.0"
mdast-util-to-hast "^4.0.0"
remark-mdx "^1.0.21"
remark-parse "^6.0.0"
remark-squeeze-paragraphs "^3.0.1"
to-style "^1.3.3"
unified "^7.0.0"
unist-builder "^1.0.1"
unist-util-visit "^1.3.0"
"@mdx-js/react@^1.0.16", "@mdx-js/react@^1.0.21":
version "1.0.21"
resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-1.0.21.tgz#fc569b86d19f426774591fa401d999ddf6ea5948"
integrity sha512-+7H1UfIytxKN/nSdojCRzUM/ZeQ6/EUg8F9bnY2NnPGuF+67cug6vZBAx9famtAey0Ms0dr2QAuCOd8k3SkhJg==
"@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
@ -7645,7 +7680,7 @@ callsites@^3.0.0:
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
camel-case@3.0.x:
camel-case@3.0.x, camel-case@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=
@ -7807,6 +7842,30 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
strip-ansi "^3.0.0"
supports-color "^2.0.0"
change-case@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/change-case/-/change-case-3.1.0.tgz#0e611b7edc9952df2e8513b27b42de72647dd17e"
integrity sha512-2AZp7uJZbYEzRPsFoa+ijKdvp9zsrnnt6+yFokfwEpeJm0xuJDVoxiRCAaTzyJND8GJkofo2IcKWaUZ/OECVzw==
dependencies:
camel-case "^3.0.0"
constant-case "^2.0.0"
dot-case "^2.1.0"
header-case "^1.0.0"
is-lower-case "^1.1.0"
is-upper-case "^1.1.0"
lower-case "^1.1.1"
lower-case-first "^1.0.0"
no-case "^2.3.2"
param-case "^2.1.0"
pascal-case "^2.0.0"
path-case "^2.1.0"
sentence-case "^2.1.0"
snake-case "^2.1.0"
swap-case "^1.1.0"
title-case "^2.1.0"
upper-case "^1.1.1"
upper-case-first "^1.1.0"
change-emitter@^0.1.2:
version "0.1.6"
resolved "https://registry.yarnpkg.com/change-emitter/-/change-emitter-0.1.6.tgz#e8b2fe3d7f1ab7d69a32199aff91ea6931409515"
@ -8230,7 +8289,7 @@ codemirror@^5.26.0:
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.47.0.tgz#c13a521ae5660d3acc655af252f4955065293789"
integrity sha512-kV49Fr+NGFHFc/Imsx6g180hSlkGhuHxTSDDmDHOuyln0MQYFLixDY4+bFkBVeCEiepYfDimAF/e++9jPJk4QA==
collapse-white-space@^1.0.2, collapse-white-space@^1.0.4:
collapse-white-space@^1.0.0, collapse-white-space@^1.0.2, collapse-white-space@^1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.5.tgz#c2495b699ab1ed380d29a1091e01063e75dbbe3a"
integrity sha512-703bOOmytCYAX9cXYqoikYIx6twmFCXsnzRQheBcTG3nzKYBR4P/+wkYeH+Mvj7qUz8zZDtdyzbxfnEi/kYzRQ==
@ -8580,6 +8639,14 @@ consolidate@^0.15.1:
dependencies:
bluebird "^3.1.1"
constant-case@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-2.0.0.tgz#4175764d389d3fa9c8ecd29186ed6005243b6a46"
integrity sha1-QXV2TTidP6nI7NKRhu1gBSQ7akY=
dependencies:
snake-case "^2.1.0"
upper-case "^1.1.1"
constants-browserify@^1.0.0, constants-browserify@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
@ -9233,7 +9300,7 @@ css-what@2.1, css-what@^2.1.2:
resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==
css@^2.2.1:
css@2.2.4, css@^2.2.1:
version "2.2.4"
resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929"
integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==
@ -9802,6 +9869,13 @@ destroy@~1.0.4:
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
detab@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/detab/-/detab-2.0.2.tgz#074970d1a807b045d0258a4235df5928dd683561"
integrity sha512-Q57yPrxScy816TTE1P/uLRXLDKjXhvYTbfxS/e6lPD+YrqghbsMlGB9nQzj/zVtSPaF0DFPSdO916EWO4sQUyQ==
dependencies:
repeat-string "^1.5.4"
detect-file@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7"
@ -10064,6 +10138,13 @@ domutils@^1.5.1, domutils@^1.7.0:
dom-serializer "0"
domelementtype "1"
dot-case@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-2.1.1.tgz#34dcf37f50a8e93c2b3bca8bb7fb9155c7da3bee"
integrity sha1-NNzzf1Co6TwrO8qLt/uRVcfaO+4=
dependencies:
no-case "^2.2.0"
dot-prop@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-3.0.0.tgz#1b708af094a49c9a0e7dbcad790aba539dac1177"
@ -10740,6 +10821,11 @@ envinfo@^5.7.0:
resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-5.12.1.tgz#83068c33e0972eb657d6bc69a6df30badefb46ef"
integrity sha512-pwdo0/G3CIkQ0y6PCXq4RdkvId2elvtPCJMG0konqlrfkWQbf1DWeH9K2b/cvu2YgGvPPTOnonZxXM1gikFu1w==
envinfo@^7.3.1:
version "7.3.1"
resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.3.1.tgz#892e42f7bf858b3446d9414ad240dbaf8da52f09"
integrity sha512-GvXiDTqLYrORVSCuJCsWHPXF5BFvoWMQA9xX4YVjPT1jyS3aZEHUBwjzxU/6LTPF9ReHgVEbX7IEN5UvSXHw/A==
enzyme-adapter-react-16@^1.9.1:
version "1.13.1"
resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.13.1.tgz#2e8ee300e38674b9914ae52b04af9493050355e2"
@ -13773,6 +13859,18 @@ hashids@1.1.4:
resolved "https://registry.yarnpkg.com/hashids/-/hashids-1.1.4.tgz#e4ff92ad66b684a3bd6aace7c17d66618ee5fa21"
integrity sha512-U/fnTE3edW0AV92ZI/BfEluMZuVcu3MDOopsN7jS+HqDYcarQo8rXQiWlsBlm0uX48/taYSdxRsfzh2HRg5Z6w==
hast-to-hyperscript@^7.0.0:
version "7.0.2"
resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-7.0.2.tgz#e9237c88c6069999ad38aec847fefc296f484c4c"
integrity sha512-NBMMst0hkDR21uSH75m9W2DkljBrLoMQEhGiLMLNij4HIzEDJMC1UG+CFR6EAjHi2zs3NHBoaAHJOHxftoIN2g==
dependencies:
comma-separated-tokens "^1.0.0"
property-information "^5.0.0"
space-separated-tokens "^1.0.0"
style-to-object "^0.2.1"
unist-util-is "^3.0.0"
web-namespaces "^1.1.2"
hast-util-from-parse5@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-5.0.0.tgz#a505a05766e0f96e389bfb0b1dd809eeefcef47b"
@ -13789,6 +13887,31 @@ hast-util-parse-selector@^2.2.0:
resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.1.tgz#4ddbae1ae12c124e3eb91b581d2556441766f0ab"
integrity sha512-Xyh0v+nHmQvrOqop2Jqd8gOdyQtE8sIP9IQf7mlVDqp924W4w/8Liuguk2L2qei9hARnQSG2m+wAOCxM7npJVw==
hast-util-raw@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-5.0.1.tgz#b39539cf4b9f7ccdc131f72a583502a7911b99ee"
integrity sha512-iHo7G6BjRc/GU1Yun5CIEXjil0wVnIbz11C6k0JdDichSDMtYi2+NNtk6YN7EOP0JfPstX30d3pRLfaJv5CkdA==
dependencies:
hast-util-from-parse5 "^5.0.0"
hast-util-to-parse5 "^5.0.0"
html-void-elements "^1.0.1"
parse5 "^5.0.0"
unist-util-position "^3.0.0"
web-namespaces "^1.0.0"
xtend "^4.0.1"
zwitch "^1.0.0"
hast-util-to-parse5@^5.0.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-5.1.1.tgz#cabf2dbe9ed988a5128fc708457b37cdf535a2e8"
integrity sha512-ivCeAd5FCXr7bapJIVsWMnx/EmbjkkW2TU2hd1prq+jGwiaUoK+FcpjyPNwsC5ogzCwWO669tOqIovGeLc/ntg==
dependencies:
hast-to-hyperscript "^7.0.0"
property-information "^5.0.0"
web-namespaces "^1.0.0"
xtend "^4.0.1"
zwitch "^1.0.0"
hastscript@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-5.0.0.tgz#fee10382c1bc4ba3f1be311521d368c047d2c43a"
@ -13804,6 +13927,14 @@ he@1.2.x, he@^1.1.0, he@^1.1.1:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
header-case@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/header-case/-/header-case-1.0.1.tgz#9535973197c144b09613cd65d317ef19963bd02d"
integrity sha1-lTWXMZfBRLCWE81l0xfvGZY70C0=
dependencies:
no-case "^2.2.0"
upper-case "^1.1.3"
heimdalljs-fs-monitor@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/heimdalljs-fs-monitor/-/heimdalljs-fs-monitor-0.2.2.tgz#a76d98f52dbf3aa1b7c20cebb0132e2f5eeb9204"
@ -13976,6 +14107,11 @@ html-tags@^2.0.0:
resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b"
integrity sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=
html-void-elements@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.4.tgz#95e8bb5ecd6b88766569c2645f2b5f1591db9ba5"
integrity sha512-yMk3naGPLrfvUV9TdDbuYXngh/TpHbA6TrOw3HL9kS8yhwx7i309BReNg7CbAJXGE+UMJ6je5OqJ7lC63o6YuQ==
html-webpack-plugin@4.0.0-alpha.2:
version "4.0.0-alpha.2"
resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.0.0-alpha.2.tgz#7745967e389a57a098e26963f328ebe4c19b598d"
@ -14655,7 +14791,7 @@ is-accessor-descriptor@^1.0.0:
dependencies:
kind-of "^6.0.0"
is-alphabetical@^1.0.0:
is-alphabetical@^1.0.0, is-alphabetical@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.3.tgz#eb04cc47219a8895d8450ace4715abff2258a1f8"
integrity sha512-eEMa6MKpHFzw38eKm56iNNi6GJ7lf6aLLio7Kr23sJPAECscgRtZvOBYybejWDQ2bM949Y++61PY+udzj5QMLA==
@ -14905,6 +15041,13 @@ is-installed-globally@^0.1.0:
global-dirs "^0.1.0"
is-path-inside "^1.0.0"
is-lower-case@^1.1.0:
version "1.1.3"
resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-1.1.3.tgz#7e147be4768dc466db3bfb21cc60b31e6ad69393"
integrity sha1-fhR75HaNxGbbO/shzGCzHmrWk5M=
dependencies:
lower-case "^1.1.0"
is-npm@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-3.0.0.tgz#ec9147bfb629c43f494cf67936a961edec7e8053"
@ -15136,6 +15279,13 @@ is-unc-path@^0.1.1:
dependencies:
unc-path-regex "^0.1.0"
is-upper-case@^1.1.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/is-upper-case/-/is-upper-case-1.1.2.tgz#8d0b1fa7e7933a1e58483600ec7d9661cbaf756f"
integrity sha1-jQsfp+eTOh5YSDYA7H2WYcuvdW8=
dependencies:
upper-case "^1.1.0"
is-utf8@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
@ -17616,7 +17766,14 @@ loud-rejection@^1.0.0:
currently-unhandled "^0.4.1"
signal-exit "^3.0.0"
lower-case@^1.1.1:
lower-case-first@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-1.0.2.tgz#e5da7c26f29a7073be02d52bac9980e5922adfa1"
integrity sha1-5dp8JvKacHO+AtUrrJmA5ZIq36E=
dependencies:
lower-case "^1.1.2"
lower-case@^1.1.0, lower-case@^1.1.1, lower-case@^1.1.2:
version "1.1.4"
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
@ -17970,6 +18127,13 @@ mdast-comment-marker@^1.0.0:
resolved "https://registry.yarnpkg.com/mdast-comment-marker/-/mdast-comment-marker-1.1.1.tgz#9c9c18e1ed57feafc1965d92b028f37c3c8da70d"
integrity sha512-TWZDaUtPLwKX1pzDIY48MkSUQRDwX/HqbTB4m3iYdL/zosi/Z6Xqfdv0C0hNVKvzrPjZENrpWDt4p4odeVO0Iw==
mdast-squeeze-paragraphs@^3.0.0:
version "3.0.5"
resolved "https://registry.yarnpkg.com/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-3.0.5.tgz#f428b6b944f8faef454db9b58f170c4183cb2e61"
integrity sha512-xX6Vbe348Y/rukQlG4W3xH+7v4ZlzUbSY4HUIQCuYrF2DrkcHx584mCaFxkWoDZKNUfyLZItHC9VAqX3kIP7XA==
dependencies:
unist-util-remove "^1.0.0"
mdast-util-compact@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/mdast-util-compact/-/mdast-util-compact-1.0.3.tgz#98a25cc8a7865761a41477b3a87d1dcef0b1e79d"
@ -17977,11 +18141,35 @@ mdast-util-compact@^1.0.0:
dependencies:
unist-util-visit "^1.1.0"
mdast-util-definitions@^1.2.0:
version "1.2.4"
resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-1.2.4.tgz#2b54ad4eecaff9d9fcb6bf6f9f6b68b232d77ca7"
integrity sha512-HfUArPog1j4Z78Xlzy9Q4aHLnrF/7fb57cooTHypyGoe2XFNbcx/kWZDoOz+ra8CkUzvg3+VHV434yqEd1DRmA==
dependencies:
unist-util-visit "^1.0.0"
mdast-util-heading-style@^1.0.2:
version "1.0.5"
resolved "https://registry.yarnpkg.com/mdast-util-heading-style/-/mdast-util-heading-style-1.0.5.tgz#81b2e60d76754198687db0e8f044e42376db0426"
integrity sha512-8zQkb3IUwiwOdUw6jIhnwM6DPyib+mgzQuHAe7j2Hy1rIarU4VUxe472bp9oktqULW3xqZE+Kz6OD4Gi7IA3vw==
mdast-util-to-hast@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-4.0.0.tgz#d8467ce28ea73b4648667bc389aa39dfa9f67f18"
integrity sha512-yOTZSxR1aPvWRUxVeLaLZ1sCYrK87x2Wusp1bDM/Ao2jETBhYUKITI3nHvgy+HkZW54HuCAhHnS0mTcbECD5Ig==
dependencies:
collapse-white-space "^1.0.0"
detab "^2.0.0"
mdast-util-definitions "^1.2.0"
mdurl "^1.0.1"
trim "0.0.1"
trim-lines "^1.0.0"
unist-builder "^1.0.1"
unist-util-generated "^1.1.0"
unist-util-position "^3.0.0"
unist-util-visit "^1.1.0"
xtend "^4.0.1"
mdast-util-to-string@^1.0.2:
version "1.0.6"
resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.0.6.tgz#7d85421021343b33de1552fc71cb8e5b4ae7536d"
@ -19066,7 +19254,7 @@ nice-try@^1.0.4:
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
no-case@^2.2.0:
no-case@^2.2.0, no-case@^2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==
@ -20132,7 +20320,7 @@ parallel-transform@^1.1.0:
inherits "^2.0.3"
readable-stream "^2.1.5"
param-case@2.1.x:
param-case@2.1.x, param-case@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247"
integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc=
@ -20347,6 +20535,14 @@ parseurl@~1.3.2, parseurl@~1.3.3:
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
pascal-case@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-2.0.1.tgz#2d578d3455f660da65eca18ef95b4e0de912761e"
integrity sha1-LVeNNFX2YNpl7KGO+VtODekSdh4=
dependencies:
camel-case "^3.0.0"
upper-case-first "^1.1.0"
pascalcase@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
@ -20372,6 +20568,13 @@ path-browserify@~0.0.0:
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a"
integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==
path-case@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/path-case/-/path-case-2.1.1.tgz#94b8037c372d3fe2906e465bb45e25d226e8eea5"
integrity sha1-lLgDfDctP+KQbkZbtF4l0ibo7qU=
dependencies:
no-case "^2.2.0"
path-dirname@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
@ -23739,6 +23942,19 @@ remark-lint@^6.0.0, remark-lint@^6.0.4:
dependencies:
remark-message-control "^4.0.0"
remark-mdx@^1.0.21:
version "1.0.21"
resolved "https://registry.yarnpkg.com/remark-mdx/-/remark-mdx-1.0.21.tgz#cf5a59ad68cce6c1f687953dcf504212fbd32555"
integrity sha512-paYs43yHPkxEuhyWXvRGJdupdurua1ttmGeu5GLqU/qc17BaZklCdNSEjCNXRa2LM0pOFRv0KVJigfA2vfaDEQ==
dependencies:
"@babel/core" "^7.2.2"
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-proposal-object-rest-spread" "^7.3.2"
"@babel/plugin-syntax-jsx" "^7.2.0"
is-alphabetical "^1.0.2"
remark-parse "^6.0.0"
unified "^7.0.0"
remark-message-control@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/remark-message-control/-/remark-message-control-4.1.1.tgz#a3f0b08dffda484e7196f0539de1488220f1d251"
@ -23791,6 +24007,13 @@ remark-preset-lint-recommended@^3.0.2:
remark-lint-no-unused-definitions "^1.0.0"
remark-lint-ordered-list-marker-style "^1.0.0"
remark-squeeze-paragraphs@^3.0.1:
version "3.0.4"
resolved "https://registry.yarnpkg.com/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-3.0.4.tgz#9fe50c3bf3b572dd88754cd426ada007c0b8dc5f"
integrity sha512-Wmz5Yj9q+W1oryo8BV17JrOXZgUKVcpJ2ApE2pwnoHwhFKSk4Wp2PmFNbmJMgYSqAdFwfkoe+TSYop5Fy8wMgA==
dependencies:
mdast-squeeze-paragraphs "^3.0.0"
remark-stringify@^6.0.0:
version "6.0.4"
resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-6.0.4.tgz#16ac229d4d1593249018663c7bddf28aafc4e088"
@ -24626,6 +24849,14 @@ send@0.17.1:
range-parser "~1.2.1"
statuses "~1.5.0"
sentence-case@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-2.1.1.tgz#1f6e2dda39c168bf92d13f86d4a918933f667ed4"
integrity sha1-H24t2jnBaL+S0T+G1KkYkz9mftQ=
dependencies:
no-case "^2.2.0"
upper-case-first "^1.1.2"
serialize-error@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a"
@ -25003,6 +25234,13 @@ smart-buffer@4.0.2:
resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.0.2.tgz#5207858c3815cc69110703c6b94e46c15634395d"
integrity sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw==
snake-case@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f"
integrity sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8=
dependencies:
no-case "^2.2.0"
snapdragon-node@^2.0.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
@ -25815,6 +26053,13 @@ style-loader@0.23.1, style-loader@^0.23.1:
loader-utils "^1.1.0"
schema-utils "^1.0.0"
style-to-object@^0.2.1:
version "0.2.2"
resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.2.2.tgz#3ea3b276bd3fa9da1195fcdcdd03bc52aa2aae01"
integrity sha512-GcbtvfsqyKmIPpHeOHZ5Rmwsx2MDJct4W9apmTGcbPTbpA2FcgTFl2Z43Hm4Qb61MWGPNK8Chki7ITiY7lLOow==
dependencies:
css "2.2.4"
style-unit@^0.6.5:
version "0.6.5"
resolved "https://registry.npmjs.org/style-unit/-/style-unit-0.6.5.tgz#4c471b1429a3b951bfd881fbf512ca20a19047da"
@ -26014,6 +26259,14 @@ svgo@^1.0.0, svgo@^1.2.1:
unquote "~1.1.1"
util.promisify "~1.0.0"
swap-case@^1.1.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/swap-case/-/swap-case-1.1.2.tgz#c39203a4587385fad3c850a0bd1bcafa081974e3"
integrity sha1-w5IDpFhzhfrTyFCgvRvK+ggZdOM=
dependencies:
lower-case "^1.1.1"
upper-case "^1.1.1"
symbol-observable@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4"
@ -26432,6 +26685,14 @@ tinycolor2@^1.4.1:
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8"
integrity sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=
title-case@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/title-case/-/title-case-2.1.1.tgz#3e127216da58d2bc5becf137ab91dae3a7cd8faa"
integrity sha1-PhJyFtpY0rxb7PE3q5Ha46fNj6o=
dependencies:
no-case "^2.2.0"
upper-case "^1.0.3"
tmp@0.0.28:
version "0.0.28"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.28.tgz#172735b7f614ea7af39664fa84cf0de4e515d120"
@ -26520,6 +26781,11 @@ to-regex@^3.0.1, to-regex@^3.0.2:
regex-not "^1.0.2"
safe-regex "^1.1.0"
to-style@^1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/to-style/-/to-style-1.3.3.tgz#63a2b70a6f4a7d4fdc2ed57a0be4e7235cb6699c"
integrity sha1-Y6K3Cm9KfU/cLtV6C+TnI1y2aZw=
to-vfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/to-vfile/-/to-vfile-4.0.0.tgz#465ade5fc2b9e97e6c80b854d378a5d0f4b5d04a"
@ -26636,6 +26902,11 @@ tree-sync@^1.2.2:
quick-temp "^0.1.5"
walk-sync "^0.3.3"
trim-lines@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-1.1.2.tgz#c8adbdbdae21bb5c2766240a661f693afe23e59b"
integrity sha512-3GOuyNeTqk3FAqc3jOJtw7FTjYl94XBR5aD9QnDbK/T4CA9sW/J0l9RoaRPE9wyPP7NF331qnHnvJFBJ+IDkmQ==
trim-newlines@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
@ -27231,6 +27502,13 @@ unique-string@^1.0.0:
dependencies:
crypto-random-string "^1.0.0"
unist-builder@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-1.0.4.tgz#e1808aed30bd72adc3607f25afecebef4dd59e17"
integrity sha512-v6xbUPP7ILrT15fHGrNyHc1Xda8H3xVhP7/HAIotHOhVPjH5dCXA097C3Rry1Q2O+HbOLCao4hfPB+EYEjHgVg==
dependencies:
object-assign "^4.1.0"
unist-util-generated@^1.1.0:
version "1.1.4"
resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.4.tgz#2261c033d9fc23fae41872cdb7663746e972c1a7"
@ -27248,6 +27526,11 @@ unist-util-is@^2.1.2:
resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-2.1.3.tgz#459182db31f4742fceaea88d429693cbf0043d20"
integrity sha512-4WbQX2iwfr/+PfM4U3zd2VNXY+dWtZsN1fLnWEi2QQXA4qyDYAZcDMfXUX0Cu6XZUHHAO9q4nyxxLT4Awk1qUA==
unist-util-is@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-3.0.0.tgz#d9e84381c2468e82629e4a5be9d7d05a2dd324cd"
integrity sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==
unist-util-position@^3.0.0:
version "3.0.3"
resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-3.0.3.tgz#fff942b879538b242096c148153826664b1ca373"
@ -27260,6 +27543,13 @@ unist-util-remove-position@^1.0.0:
dependencies:
unist-util-visit "^1.1.0"
unist-util-remove@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/unist-util-remove/-/unist-util-remove-1.0.3.tgz#58ec193dfa84b52d5a055ffbc58e5444eb8031a3"
integrity sha512-mB6nCHCQK0pQffUAcCVmKgIWzG/AXs/V8qpS8K72tMPtOSCMSjDeMc5yN+Ye8rB0FhcE+JvW++o1xRNc0R+++g==
dependencies:
unist-util-is "^3.0.0"
unist-util-stringify-position@^1.0.0, unist-util-stringify-position@^1.1.1, unist-util-stringify-position@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz#3f37fcf351279dcbca7480ab5889bb8a832ee1c6"
@ -27279,7 +27569,7 @@ unist-util-visit-parents@^2.0.0:
dependencies:
unist-util-is "^2.1.2"
unist-util-visit@^1.0.0, unist-util-visit@^1.1.0, unist-util-visit@^1.1.1, unist-util-visit@^1.4.0:
unist-util-visit@^1.0.0, unist-util-visit@^1.1.0, unist-util-visit@^1.1.1, unist-util-visit@^1.3.0, unist-util-visit@^1.4.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-1.4.1.tgz#4724aaa8486e6ee6e26d7ff3c8685960d560b1e3"
integrity sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==
@ -27369,7 +27659,14 @@ update-notifier@^3.0.0:
semver-diff "^2.0.0"
xdg-basedir "^3.0.0"
upper-case@^1.1.1:
upper-case-first@^1.1.0, upper-case-first@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-1.1.2.tgz#5d79bedcff14419518fd2edb0a0507c9b6859115"
integrity sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU=
dependencies:
upper-case "^1.1.1"
upper-case@^1.0.3, upper-case@^1.1.0, upper-case@^1.1.1, upper-case@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598"
integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=
@ -27886,7 +28183,7 @@ weak@^1.0.1:
bindings "^1.2.1"
nan "^2.0.5"
web-namespaces@^1.1.2:
web-namespaces@^1.0.0, web-namespaces@^1.1.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.3.tgz#9bbf5c99ff0908d2da031f1d732492a96571a83f"
integrity sha512-r8sAtNmgR0WKOKOxzuSgk09JsHlpKlB+uHi937qypOu3PZ17UxPrierFKDye/uNHjNTTEshu5PId8rojIPj/tA==
@ -29264,3 +29561,8 @@ zone.js@^0.8.29:
version "0.8.29"
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.8.29.tgz#8dce92aa0dd553b50bc5bfbb90af9986ad845a12"
integrity sha512-mla2acNCMkWXBD+c+yeUrBUrzOxYMNFdQ6FGfigGGtEVBPJx07BQeJekjt9DmH1FtZek4E9rE1eRR9qQpxACOQ==
zwitch@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.4.tgz#93b1b993b13c8926753a41afaf8f27bbfac6be8b"
integrity sha512-YO803/X+13GNaZB7fVopjvHH0uWQKgJkgKnU1YCjxShjKGVuN9PPHHW8g+uFDpkHpSTNi3rCMKMewIcbC1BAYg==