#7192 ✂ I have removed the storysource older loader

This commit is contained in:
libetl 2019-07-03 23:56:56 +02:00
parent 3b2ef9bc80
commit 053aa06bc6
30 changed files with 23 additions and 1351 deletions

View File

@ -424,7 +424,7 @@ Publish failed
- Addon-docs: Docs page bugfix
- Addon-docs: Fix source block for legacy stories
NOTE: use `@storybook/addon-storysource/loader` with option `injectParameters: true` for legacy source
NOTE: use `@storybook/source-loader` with option `injectParameters: true` for legacy source
## 5.2.0-alpha.6 (May 14, 2019)

View File

@ -26,7 +26,7 @@ Use this hook to a custom webpack.config. This will generate a decorator call in
module.exports = function({ config }) {
config.module.rules.push({
test: /\.stories\.jsx?$/,
loaders: [require.resolve('@storybook/addon-storysource/loader')],
loaders: [require.resolve('@storybook/source-loader')],
enforce: 'pre',
});
@ -56,7 +56,7 @@ module.exports = function({ config }) {
test: /\.stories\.jsx?$/,
loaders: [
{
loader: require.resolve('@storybook/addon-storysource/loader'),
loader: require.resolve('@storybook/source-loader'),
options: { parser: 'typescript' },
},
],
@ -91,7 +91,7 @@ module.exports = function({ config }) {
test: /\.stories\.jsx?$/,
loaders: [
{
loader: require.resolve('@storybook/addon-storysource/loader'),
loader: require.resolve('@storybook/source-loader'),
options: {
prettierConfig: {
printWidth: 100,
@ -125,7 +125,7 @@ module.exports = function({ config }) {
test: /\.stories\.jsx?$/,
loaders: [
{
loader: require.resolve('@storybook/addon-storysource/loader'),
loader: require.resolve('@storybook/source-loader'),
options: {
uglyCommentsRegex: [/^eslint-.*/, /^global.*/],
},
@ -152,7 +152,7 @@ module.exports = function({ config }) {
test: /\.stories\.jsx?$/,
loaders: [
{
loader: require.resolve('@storybook/addon-storysource/loader'),
loader: require.resolve('@storybook/source-loader'),
options: { injectDecorator: false },
},
],

View File

@ -1 +1 @@
module.exports = require('./dist/loader');
module.exports = require('@storybook/source-loader');

View File

@ -35,7 +35,8 @@
"regenerator-runtime": "^0.12.1"
},
"peerDependencies": {
"react": "*"
"react": "*",
"@storybook/source-loader": "*"
},
"publishConfig": {
"access": "public"

View File

@ -66,9 +66,8 @@ export default class StoryPanel extends Component {
this.selectedStoryRef = ref;
};
listener = ({ source, currentLocation, locationsMap }) => {
listener = ({ edition: { source }, location: { currentLocation, locationsMap } }) => {
const locationsKeys = getLocationKeys(locationsMap);
this.setState({
source,
currentLocation,

View File

@ -1,3 +1,3 @@
export const ADDON_ID = 'storybook/storysource';
export const ADDON_ID = 'storybook/source-loader';
export const PANEL_ID = `${ADDON_ID}/panel`;
export const EVENT_ID = `${ADDON_ID}/set`;

View File

@ -1,732 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`inject-decorator injectDecorator option is false - angular does not inject stories decorator after the all "storiesOf" functions 1`] = `
"import { Component } from '@angular/core';
import { storiesOf } from '@storybook/angular';
@Component({
selector: 'storybook-with-ng-content',
template: \`<div style=\\"color: #1e88e5;\\"><ng-content></ng-content></div>\`,
})
class WithNgContentComponent {}
storiesOf('Custom|ng-content', module).add('Default', () => ({
template: \`<storybook-with-ng-content><h1>This is rendered in ng-content</h1></storybook-with-ng-content>\`,
moduleMetadata: {
declarations: [WithNgContentComponent],
},
}));
"
`;
exports[`inject-decorator injectDecorator option is false - flow does not inject stories decorator after the all "storiesOf" functions 1`] = `
"// @flow
import React from 'react';
import { storiesOf } from '@storybook/react';
import { withInfo } from '@storybook/addon-info';
import TableComponent from '../components/TableComponent';
import type { JssClasses } from '../types';
type State = {
value: any,
};
type Props = {
classes: JssClasses,
name: string,
};
class Table extends React.Component<Props, State> {
constructor(props) {
super(props);
this.state = {
value: undefined,
};
}
state: State;
render() {
return <TableComponent />;
}
}
const stories = storiesOf('Table', module);
stories.add('Flow Class', withInfo('Lorum Ipsum Nem')(() => <Table />));
"
`;
exports[`inject-decorator injectDecorator option is false - ts does not inject stories decorator after the all "storiesOf" functions 1`] = `
"import { Component } from '@angular/core';
import { Store, StoreModule } from '@ngrx/store';
import { storiesOf, moduleMetadata } from '@storybook/angular';
@Component({
selector: 'storybook-comp-with-store',
template: '<div>{{this.getSotreState()}}</div>',
})
class WithStoreComponent {
private store: Store<any>;
constructor(store: Store<any>) {
this.store = store;
}
getSotreState(): string {
return this.store === undefined ? 'Store is NOT injected' : 'Store is injected';
}
}
storiesOf('ngrx|Store', module)
.addDecorator(
moduleMetadata({
imports: [StoreModule.forRoot({})],
declarations: [WithStoreComponent],
})
)
.add('With component', () => {
return {
component: WithStoreComponent,
};
});"
`;
exports[`inject-decorator injectDecorator option is false does not inject stories decorator after the all "storiesOf" functions 1`] = `
"import React from 'react';
import { storiesOf } from '@storybook/react';
import { withInfo } from '@storybook/addon-info';
import { action } from '@storybook/addon-actions';
import DocgenButton from '../components/DocgenButton';
import FlowTypeButton from '../components/FlowTypeButton';
import BaseButton from '../components/BaseButton';
import TableComponent from '../components/TableComponent';
storiesOf('Addons|Info.React Docgen', module)
.add(
'Comments from PropType declarations',
withInfo(
'Comments above the PropType declarations should be extracted from the React component file itself and rendered in the Info Addon prop table'
)(() => <DocgenButton onClick={action('clicked')} label=\\"Docgen Button\\" />)
)
.add(
'Comments from Flow declarations',
withInfo(
'Comments above the Flow declarations should be extracted from the React component file itself and rendered in the Info Addon prop table'
)(() => <FlowTypeButton onClick={action('clicked')} label=\\"Flow Typed Button\\" />)
)
.add(
'Comments from component declaration',
withInfo(
'Comments above the component declaration should be extracted from the React component file itself and rendered below the Info Addon heading'
)(() => <BaseButton onClick={action('clicked')} label=\\"Button\\" />)
);
const markdownDescription = \`
#### You can use markdown in your withInfo() description.
Sometimes you might want to manually include some code examples:
~~~js
const Button = () => <button />;
~~~
Maybe include a [link](http://storybook.js.org) to your project as well.
\`;
storiesOf('Addons|Info.Markdown', module).add(
'Displays Markdown in description',
withInfo(markdownDescription)(() => <BaseButton onClick={action('clicked')} label=\\"Button\\" />)
);
storiesOf('Addons|Info.Options.inline', module).add(
'Inlines component inside story',
withInfo({
text: 'Component should be inlined between description and PropType table',
inline: true, // Displays info inline vs click button to view
})(() => <BaseButton label=\\"Button\\" />)
);
storiesOf('Addons|Info.Options.header', module).add(
'Shows or hides Info Addon header',
withInfo({
text: 'The Info Addon header should be hidden',
header: false, // Toggles display of header with component name and description
})(() => <BaseButton label=\\"Button\\" />)
);
storiesOf('Addons|Info.Options.source', module).add(
'Shows or hides Info Addon source',
withInfo({
text: 'The Info Addon source section should be hidden',
source: false, // Displays the source of story Component
})(() => <BaseButton label=\\"Button\\" />)
);
storiesOf('Addons|Info.Options.propTables', module).add(
'Shows additional component prop tables',
withInfo({
text: 'There should be a prop table added for a component not included in the story',
propTables: [FlowTypeButton],
})(() => <BaseButton label=\\"Button\\" />)
);
storiesOf('Addons|Info.Options.propTablesExclude', module).add(
'Exclude component from prop tables',
withInfo({
text: 'This can exclude extraneous components from being displayed in prop tables.',
propTablesExclude: [FlowTypeButton],
})(() => (
<div>
<BaseButton label=\\"Button\\" />
<FlowTypeButton label=\\"Flow Typed Button\\" />
</div>
))
);
storiesOf('Addons|Info.Options.styles', module)
.add(
'Extend info styles with an object',
withInfo({
styles: {
button: {
base: {
background: 'purple',
},
},
header: {
h1: {
color: 'green',
},
},
},
})(() => <BaseButton label=\\"Button\\" />)
)
.add(
'Full control over styles using a function',
withInfo({
styles: stylesheet => ({
...stylesheet,
header: {
...stylesheet.header,
h1: {
...stylesheet.header.h1,
color: 'red',
},
},
}),
})(() => <BaseButton label=\\"Button\\" />)
);
storiesOf('Addons|Info.Options.TableComponent', module).add(
'Use a custom component for the table',
withInfo({
TableComponent,
})(() => <BaseButton label=\\"Button\\" />)
);
storiesOf('Addons|Info.Decorator', module)
.addDecorator((story, context) =>
withInfo('Info could be used as a global or local decorator as well.')(story)(context)
)
.add('Use Info as story decorator', () => <BaseButton label=\\"Button\\" />);
const hoc = WrapComponent => ({ ...props }) => <WrapComponent {...props} />;
const Input = hoc(() => <input type=\\"text\\" />);
const TextArea = hoc(({ children }) => <textarea>{children}</textarea>);
storiesOf('Addons|Info.GitHub issues', module).add(
'#1814',
withInfo('Allow Duplicate DisplayNames for HOC #1814')(() => (
<div>
<Input />
<TextArea />
</div>
))
);
"
`;
exports[`inject-decorator positive - angular calculates "adds" map 1`] = `
Object {
"custom-ng-content--default": Object {
"endLoc": Object {
"col": 2,
"line": 17,
},
"startLoc": Object {
"col": 43,
"line": 12,
},
},
}
`;
exports[`inject-decorator positive - angular injects stories decorator after the all "storiesOf" functions 1`] = `
"import { Component } from '@angular/core';
import { storiesOf } from '@storybook/angular';
@Component({
selector: 'storybook-with-ng-content',
template: \`<div style=\\"color: #1e88e5;\\"><ng-content></ng-content></div>\`,
})
class WithNgContentComponent {}
storiesOf('Custom|ng-content', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__)).add('Default', () => ({
template: \`<storybook-with-ng-content><h1>This is rendered in ng-content</h1></storybook-with-ng-content>\`,
moduleMetadata: {
declarations: [WithNgContentComponent],
},
}));
"
`;
exports[`inject-decorator positive - flow calculates "adds" map 1`] = `Object {}`;
exports[`inject-decorator positive - flow injects stories decorator after the all "storiesOf" functions 1`] = `
"// @flow
import React from 'react';
import { storiesOf } from '@storybook/react';
import { withInfo } from '@storybook/addon-info';
import TableComponent from '../components/TableComponent';
import type { JssClasses } from '../types';
type State = {
value: any,
};
type Props = {
classes: JssClasses,
name: string,
};
class Table extends React.Component<Props, State> {
constructor(props) {
super(props);
this.state = {
value: undefined,
};
}
state: State;
render() {
return <TableComponent />;
}
}
const stories = storiesOf('Table', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__));
stories.add('Flow Class', withInfo('Lorum Ipsum Nem')(() => <Table />));
"
`;
exports[`inject-decorator positive - ts calculates "adds" map 1`] = `
Object {
"ngrx-store--with-component": Object {
"endLoc": Object {
"col": 3,
"line": 32,
},
"startLoc": Object {
"col": 7,
"line": 28,
},
},
}
`;
exports[`inject-decorator positive - ts injects stories decorator after the all "storiesOf" functions 1`] = `
"import { Component } from '@angular/core';
import { Store, StoreModule } from '@ngrx/store';
import { storiesOf, moduleMetadata } from '@storybook/angular';
@Component({
selector: 'storybook-comp-with-store',
template: '<div>{{this.getSotreState()}}</div>',
})
class WithStoreComponent {
private store: Store<any>;
constructor(store: Store<any>) {
this.store = store;
}
getSotreState(): string {
return this.store === undefined ? 'Store is NOT injected' : 'Store is injected';
}
}
storiesOf('ngrx|Store', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__))
.addDecorator(
moduleMetadata({
imports: [StoreModule.forRoot({})],
declarations: [WithStoreComponent],
})
)
.add('With component', () => {
return {
component: WithStoreComponent,
};
});"
`;
exports[`inject-decorator positive calculates "adds" map 1`] = `
Object {
"addons-info-decorator--use-info-as-story-decorator": Object {
"endLoc": Object {
"col": 73,
"line": 137,
},
"startLoc": Object {
"col": 7,
"line": 137,
},
},
"addons-info-github-issues--1814": Object {
"endLoc": Object {
"col": 4,
"line": 152,
},
"startLoc": Object {
"col": 2,
"line": 146,
},
},
"addons-info-markdown--displays-markdown-in-description": Object {
"endLoc": Object {
"col": 96,
"line": 44,
},
"startLoc": Object {
"col": 2,
"line": 43,
},
},
"addons-info-options-header--shows-or-hides-info-addon-header": Object {
"endLoc": Object {
"col": 41,
"line": 60,
},
"startLoc": Object {
"col": 2,
"line": 56,
},
},
"addons-info-options-inline--inlines-component-inside-story": Object {
"endLoc": Object {
"col": 41,
"line": 52,
},
"startLoc": Object {
"col": 2,
"line": 48,
},
},
"addons-info-options-proptables--shows-additional-component-prop-tables": Object {
"endLoc": Object {
"col": 41,
"line": 76,
},
"startLoc": Object {
"col": 2,
"line": 72,
},
},
"addons-info-options-proptablesexclude--exclude-component-from-prop-tables": Object {
"endLoc": Object {
"col": 4,
"line": 89,
},
"startLoc": Object {
"col": 2,
"line": 80,
},
},
"addons-info-options-source--shows-or-hides-info-addon-source": Object {
"endLoc": Object {
"col": 41,
"line": 68,
},
"startLoc": Object {
"col": 2,
"line": 64,
},
},
"addons-info-options-styles--extend-info-styles-with-an-object": Object {
"endLoc": Object {
"col": 43,
"line": 108,
},
"startLoc": Object {
"col": 4,
"line": 94,
},
},
"addons-info-options-styles--full-control-over-styles-using-a-function": Object {
"endLoc": Object {
"col": 43,
"line": 123,
},
"startLoc": Object {
"col": 4,
"line": 111,
},
},
"addons-info-options-tablecomponent--use-a-custom-component-for-the-table": Object {
"endLoc": Object {
"col": 41,
"line": 130,
},
"startLoc": Object {
"col": 2,
"line": 127,
},
},
"addons-info-react-docgen--comments-from-component-declaration": Object {
"endLoc": Object {
"col": 70,
"line": 28,
},
"startLoc": Object {
"col": 4,
"line": 25,
},
},
"addons-info-react-docgen--comments-from-flow-declarations": Object {
"endLoc": Object {
"col": 85,
"line": 22,
},
"startLoc": Object {
"col": 4,
"line": 19,
},
},
"addons-info-react-docgen--comments-from-proptype-declarations": Object {
"endLoc": Object {
"col": 79,
"line": 16,
},
"startLoc": Object {
"col": 4,
"line": 13,
},
},
}
`;
exports[`inject-decorator positive injects stories decorator after the all "storiesOf" functions 1`] = `
"import React from 'react';
import { storiesOf } from '@storybook/react';
import { withInfo } from '@storybook/addon-info';
import { action } from '@storybook/addon-actions';
import DocgenButton from '../components/DocgenButton';
import FlowTypeButton from '../components/FlowTypeButton';
import BaseButton from '../components/BaseButton';
import TableComponent from '../components/TableComponent';
storiesOf('Addons|Info.React Docgen', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__))
.add(
'Comments from PropType declarations',
withInfo(
'Comments above the PropType declarations should be extracted from the React component file itself and rendered in the Info Addon prop table'
)(() => <DocgenButton onClick={action('clicked')} label=\\"Docgen Button\\" />)
)
.add(
'Comments from Flow declarations',
withInfo(
'Comments above the Flow declarations should be extracted from the React component file itself and rendered in the Info Addon prop table'
)(() => <FlowTypeButton onClick={action('clicked')} label=\\"Flow Typed Button\\" />)
)
.add(
'Comments from component declaration',
withInfo(
'Comments above the component declaration should be extracted from the React component file itself and rendered below the Info Addon heading'
)(() => <BaseButton onClick={action('clicked')} label=\\"Button\\" />)
);
const markdownDescription = \`
#### You can use markdown in your withInfo() description.
Sometimes you might want to manually include some code examples:
~~~js
const Button = () => <button />;
~~~
Maybe include a [link](http://storybook.js.org) to your project as well.
\`;
storiesOf('Addons|Info.Markdown', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__)).add(
'Displays Markdown in description',
withInfo(markdownDescription)(() => <BaseButton onClick={action('clicked')} label=\\"Button\\" />)
);
storiesOf('Addons|Info.Options.inline', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__)).add(
'Inlines component inside story',
withInfo({
text: 'Component should be inlined between description and PropType table',
inline: true, // Displays info inline vs click button to view
})(() => <BaseButton label=\\"Button\\" />)
);
storiesOf('Addons|Info.Options.header', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__)).add(
'Shows or hides Info Addon header',
withInfo({
text: 'The Info Addon header should be hidden',
header: false, // Toggles display of header with component name and description
})(() => <BaseButton label=\\"Button\\" />)
);
storiesOf('Addons|Info.Options.source', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__)).add(
'Shows or hides Info Addon source',
withInfo({
text: 'The Info Addon source section should be hidden',
source: false, // Displays the source of story Component
})(() => <BaseButton label=\\"Button\\" />)
);
storiesOf('Addons|Info.Options.propTables', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__)).add(
'Shows additional component prop tables',
withInfo({
text: 'There should be a prop table added for a component not included in the story',
propTables: [FlowTypeButton],
})(() => <BaseButton label=\\"Button\\" />)
);
storiesOf('Addons|Info.Options.propTablesExclude', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__)).add(
'Exclude component from prop tables',
withInfo({
text: 'This can exclude extraneous components from being displayed in prop tables.',
propTablesExclude: [FlowTypeButton],
})(() => (
<div>
<BaseButton label=\\"Button\\" />
<FlowTypeButton label=\\"Flow Typed Button\\" />
</div>
))
);
storiesOf('Addons|Info.Options.styles', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__))
.add(
'Extend info styles with an object',
withInfo({
styles: {
button: {
base: {
background: 'purple',
},
},
header: {
h1: {
color: 'green',
},
},
},
})(() => <BaseButton label=\\"Button\\" />)
)
.add(
'Full control over styles using a function',
withInfo({
styles: stylesheet => ({
...stylesheet,
header: {
...stylesheet.header,
h1: {
...stylesheet.header.h1,
color: 'red',
},
},
}),
})(() => <BaseButton label=\\"Button\\" />)
);
storiesOf('Addons|Info.Options.TableComponent', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__)).add(
'Use a custom component for the table',
withInfo({
TableComponent,
})(() => <BaseButton label=\\"Button\\" />)
);
storiesOf('Addons|Info.Decorator', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__))
.addDecorator((story, context) =>
withInfo('Info could be used as a global or local decorator as well.')(story)(context)
)
.add('Use Info as story decorator', () => <BaseButton label=\\"Button\\" />);
const hoc = WrapComponent => ({ ...props }) => <WrapComponent {...props} />;
const Input = hoc(() => <input type=\\"text\\" />);
const TextArea = hoc(({ children }) => <textarea>{children}</textarea>);
storiesOf('Addons|Info.GitHub issues', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__)).add(
'#1814',
withInfo('Allow Duplicate DisplayNames for HOC #1814')(() => (
<div>
<Input />
<TextArea />
</div>
))
);
"
`;
exports[`inject-decorator stories with ugly comments in ts should delete ugly comments from the generated story source 1`] = `
"import React from 'react';
@Component({
selector: 'storybook-comp-with-store',
template: '<div>{{this.getSotreState()}}</div>',
})
class WithStoreComponent {
private store: Store<any>;
constructor(store: Store<any>) {
this.store = store;
}
getSotreState(): string {
return this.store === undefined ? 'Store is NOT injected' : 'Store is injected';
}
}
import { storiesOf } from '@storybook/react';
const x = 0;
storiesOf('Foo', module).add('bar', () => <div>baz</div>);
/*
This is actually a good comment that will help
users to understand what's going on here.
*/
"
`;
exports[`inject-decorator stories with ugly comments should delete ugly comments from the generated story source 1`] = `
"import React from 'react';
import { storiesOf } from '@storybook/react';
const x = 0;
storiesOf('Foo', module).add('bar', () => <div>baz</div>);
/*
This is actually a good comment that will help
users to understand what's going on here.
*/
"
`;
exports[`inject-decorator will not change the source when there are no "storiesOf" functions 1`] = `
"while(true) {
console.log(\\"it's a kind of magic\\");
}"
`;

View File

@ -1,12 +0,0 @@
const defaultOptions = {
prettierConfig: {
printWidth: 100,
tabWidth: 2,
bracketSpacing: true,
trailingComma: 'es5',
singleQuote: true,
},
uglyCommentsRegex: [/^eslint-.*/, /^global.*/],
};
export default defaultOptions;

View File

@ -1,99 +0,0 @@
import prettier from 'prettier';
import { patchNode } from './parse-helpers';
import { splitSTORYOF, findAddsMap } from './traverse-helpers';
import getParser from './parsers';
function isUglyComment(comment, uglyCommentsRegex) {
return uglyCommentsRegex.some(regex => regex.test(comment));
}
function generateSourceWithoutUglyComments(source, { comments, uglyCommentsRegex }) {
let lastIndex = 0;
const parts = [source];
comments
.filter(comment => isUglyComment(comment.value.trim(), uglyCommentsRegex))
.map(patchNode)
.forEach(comment => {
parts.pop();
const start = source.slice(lastIndex, comment.start);
const end = source.slice(comment.end);
parts.push(start, end);
lastIndex = comment.end;
});
return parts.join('');
}
function prettifyCode(source, { prettierConfig, parser, filepath }) {
let config = prettierConfig;
if (!config.parser) {
if (parser) {
config = {
...prettierConfig,
parser: parser === 'javascript' ? 'babel' : parser,
};
} else if (filepath) {
config = {
...prettierConfig,
filepath,
};
} else {
config = {
...prettierConfig,
parser: 'babel',
};
}
}
return prettier.format(source, config);
}
export function generateSourceWithDecorators(source, decorator, parserType) {
const parser = getParser(parserType);
const ast = parser.parse(source);
const { comments = [] } = ast;
const parts = splitSTORYOF(ast, source);
const newSource = parts.join(decorator);
return {
changed: parts.length > 1,
source: newSource,
comments,
};
}
export function generateSourceWithoutDecorators(source, parserType) {
const parser = getParser(parserType);
const ast = parser.parse(source);
const { comments = [] } = ast;
return {
changed: true,
source,
comments,
};
}
export function generateAddsMap(source, parserType) {
const parser = getParser(parserType);
const ast = parser.parse(source);
return findAddsMap(ast);
}
export function generateStorySource({ source, ...options }) {
let storySource = source;
storySource = generateSourceWithoutUglyComments(storySource, options);
storySource = prettifyCode(storySource, options);
return storySource;
}

View File

@ -1,29 +0,0 @@
import { getOptions } from 'loader-utils';
import injectDecorator from './inject-decorator';
const ADD_DECORATOR_STATEMENT = '.addDecorator(withStorySource(__STORY__, __ADDS_MAP__))';
function transform(source) {
const options = getOptions(this) || {};
const result = injectDecorator(source, ADD_DECORATOR_STATEMENT, this.resourcePath, options);
if (!result.changed) {
return source;
}
const sourceJson = JSON.stringify(result.storySource)
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');
const addsMap = JSON.stringify(result.addsMap);
return `
export var withStorySource = require('@storybook/addon-storysource').withStorySource;
export var __STORY__ = ${sourceJson};
export var __ADDS_MAP__ = ${addsMap};
${result.source}
`;
}
export default transform;

View File

@ -1,46 +0,0 @@
import defaultOptions from './default-options';
import {
generateSourceWithDecorators,
generateSourceWithoutDecorators,
generateStorySource,
generateAddsMap,
} from './generate-helpers';
function extendOptions(source, comments, filepath, options) {
return {
...defaultOptions,
...options,
source,
comments,
filepath,
};
}
function inject(source, decorator, filepath, options = {}) {
const { injectDecorator = true } = options;
const { changed, source: newSource, comments } =
injectDecorator === true
? generateSourceWithDecorators(source, decorator, options.parser)
: generateSourceWithoutDecorators(source, options.parser);
if (!changed) {
return {
source: newSource,
addsMap: {},
changed,
};
}
const storySource = generateStorySource(extendOptions(source, comments, filepath, options));
const addsMap = generateAddsMap(storySource, options.parser);
return {
source: newSource,
storySource,
addsMap,
changed,
};
}
export default inject;

View File

@ -1,216 +0,0 @@
import fs from 'fs';
import path from 'path';
import injectDecorator from './inject-decorator';
const ADD_DECORATOR_STATEMENT = '.addDecorator(withStorySource(__STORY__, __ADDS_MAP__))';
describe('inject-decorator', () => {
describe('positive', () => {
const mockFilePath = './__mocks__/inject-decorator.stories.txt';
const source = fs.readFileSync(mockFilePath, 'utf-8');
const result = injectDecorator(
source,
ADD_DECORATOR_STATEMENT,
path.resolve(__dirname, mockFilePath),
{ parser: 'javascript' }
);
it('returns "changed" flag', () => {
expect(result.changed).toBeTruthy();
});
it('injects stories decorator after the all "storiesOf" functions', () => {
expect(result.source).toMatchSnapshot();
});
it('calculates "adds" map', () => {
expect(result.addsMap).toMatchSnapshot();
});
});
describe('positive - angular', () => {
const mockFilePath = './__mocks__/inject-decorator.angular-stories.txt';
const source = fs.readFileSync(mockFilePath, 'utf-8');
const result = injectDecorator(
source,
ADD_DECORATOR_STATEMENT,
path.resolve(__dirname, mockFilePath),
{ parser: 'typescript' }
);
it('returns "changed" flag', () => {
expect(result.changed).toBeTruthy();
});
it('injects stories decorator after the all "storiesOf" functions', () => {
expect(result.source).toMatchSnapshot();
});
it('calculates "adds" map', () => {
expect(result.addsMap).toMatchSnapshot();
});
});
describe('positive - flow', () => {
const mockFilePath = './__mocks__/inject-decorator.flow-stories.txt';
const source = fs.readFileSync(mockFilePath, 'utf-8');
const result = injectDecorator(
source,
ADD_DECORATOR_STATEMENT,
path.resolve(__dirname, mockFilePath),
{ parser: 'flow' }
);
it('returns "changed" flag', () => {
expect(result.changed).toBeTruthy();
});
it('injects stories decorator after the all "storiesOf" functions', () => {
expect(result.source).toMatchSnapshot();
});
it('calculates "adds" map', () => {
expect(result.addsMap).toMatchSnapshot();
});
});
describe('positive - ts', () => {
const mockFilePath = './__mocks__/inject-decorator.ts.txt';
const source = fs.readFileSync(mockFilePath, 'utf-8');
const result = injectDecorator(
source,
ADD_DECORATOR_STATEMENT,
path.resolve(__dirname, mockFilePath),
{ parser: 'typescript' }
);
it('returns "changed" flag', () => {
expect(result.changed).toBeTruthy();
});
it('injects stories decorator after the all "storiesOf" functions', () => {
expect(result.source).toMatchSnapshot();
});
it('calculates "adds" map', () => {
expect(result.addsMap).toMatchSnapshot();
});
});
describe('stories with ugly comments', () => {
const mockFilePath = './__mocks__/inject-decorator.ugly-comments-stories.txt';
const source = fs.readFileSync(mockFilePath, 'utf-8');
const result = injectDecorator(
source,
ADD_DECORATOR_STATEMENT,
path.resolve(__dirname, mockFilePath),
{ parser: 'javascript' }
);
it('should delete ugly comments from the generated story source', () => {
expect(result.storySource).toMatchSnapshot();
});
});
describe('stories with ugly comments in ts', () => {
const mockFilePath = './__mocks__/inject-decorator.ts.ugly-comments-stories.txt';
const source = fs.readFileSync(mockFilePath, 'utf-8');
const result = injectDecorator(
source,
ADD_DECORATOR_STATEMENT,
path.resolve(__dirname, mockFilePath),
{ parser: 'typescript' }
);
it('should delete ugly comments from the generated story source', () => {
expect(result.storySource).toMatchSnapshot();
});
});
it('will not change the source when there are no "storiesOf" functions', () => {
const mockFilePath = './__mocks__/inject-decorator.no-stories.txt';
const source = fs.readFileSync(mockFilePath, 'utf-8');
const result = injectDecorator(
source,
ADD_DECORATOR_STATEMENT,
path.resolve(__dirname, mockFilePath)
);
expect(result.changed).toBeFalsy();
expect(result.addsMap).toEqual({});
expect(result.source).toMatchSnapshot();
});
describe('injectDecorator option is false', () => {
const mockFilePath = './__mocks__/inject-decorator.stories.txt';
const source = fs.readFileSync(mockFilePath, 'utf-8');
const result = injectDecorator(
source,
ADD_DECORATOR_STATEMENT,
path.resolve(__dirname, mockFilePath),
{
injectDecorator: false,
parser: 'javascript',
}
);
it('does not inject stories decorator after the all "storiesOf" functions', () => {
expect(result.source).toMatchSnapshot();
});
});
describe('injectDecorator option is false - angular', () => {
const mockFilePath = './__mocks__/inject-decorator.angular-stories.txt';
const source = fs.readFileSync(mockFilePath, 'utf-8');
const result = injectDecorator(
source,
ADD_DECORATOR_STATEMENT,
path.resolve(__dirname, mockFilePath),
{
injectDecorator: false,
parser: 'typescript',
}
);
it('does not inject stories decorator after the all "storiesOf" functions', () => {
expect(result.source).toMatchSnapshot();
});
});
describe('injectDecorator option is false - flow', () => {
const mockFilePath = './__mocks__/inject-decorator.flow-stories.txt';
const source = fs.readFileSync(mockFilePath, 'utf-8');
const result = injectDecorator(
source,
ADD_DECORATOR_STATEMENT,
path.resolve(__dirname, mockFilePath),
{
injectDecorator: false,
parser: 'flow',
}
);
it('does not inject stories decorator after the all "storiesOf" functions', () => {
expect(result.source).toMatchSnapshot();
});
});
describe('injectDecorator option is false - ts', () => {
const mockFilePath = './__mocks__/inject-decorator.ts.txt';
const source = fs.readFileSync(mockFilePath, 'utf-8');
const result = injectDecorator(
source,
ADD_DECORATOR_STATEMENT,
path.resolve(__dirname, mockFilePath),
{
injectDecorator: false,
parser: 'typescript',
}
);
it('does not inject stories decorator after the all "storiesOf" functions', () => {
expect(result.source).toMatchSnapshot();
});
});
});

View File

@ -1,110 +0,0 @@
const { toId } = require('@storybook/router/utils');
const STORIES_OF = 'storiesOf';
function pushParts(source, parts, from, to) {
const start = source.slice(from, to);
parts.push(start);
const end = source.slice(to);
parts.push(end);
}
function getKindFromStoryOfNode(object) {
if (object.arguments.length < 1) {
return '';
}
const kindArgument = object.arguments[0];
if (kindArgument.type === 'Literal' || kindArgument.type === 'StringLiteral') {
return kindArgument.value;
}
if (kindArgument.type === 'TemplateLiteral') {
// we can generate template, but it will not be a real value
// until the full template compilation. probably won't fix.
return '';
}
// other options may include some complex usages.
return '';
}
function findRelatedKind(object) {
if (!object || !object.callee) {
return '';
}
if (object.callee.name === STORIES_OF) {
return getKindFromStoryOfNode(object);
}
return findRelatedKind(object.callee.object);
}
export function patchNode(node) {
if (node.range && node.range.length === 2 && node.start === undefined && node.end === undefined) {
const [start, end] = node.range;
// eslint-disable-next-line no-param-reassign
node.start = start;
// eslint-disable-next-line no-param-reassign
node.end = end;
}
if (!node.range && node.start !== undefined && node.end !== undefined) {
// eslint-disable-next-line no-param-reassign
node.range = [node.start, node.end];
}
return node;
}
export function handleADD(node, parent, adds) {
if (!node.property || !node.property.name || node.property.name.indexOf('add') !== 0) {
return;
}
const addArgs = parent.arguments;
if (!addArgs || addArgs.length < 2) {
return;
}
const storyName = addArgs[0];
const lastArg = addArgs[addArgs.length - 1];
if (storyName.type !== 'Literal' && storyName.type !== 'StringLiteral') {
// if story name is not literal, it's much harder to extract it
return;
}
const kind = findRelatedKind(node.object) || '';
if (kind && storyName.value) {
const key = toId(kind, storyName.value);
// eslint-disable-next-line no-param-reassign
adds[key] = {
// Debug: code: source.slice(storyName.start, lastArg.end),
startLoc: {
col: storyName.loc.start.column,
line: storyName.loc.start.line,
},
endLoc: {
col: lastArg.loc.end.column,
line: lastArg.loc.end.line,
},
};
}
}
export function handleSTORYOF(node, parts, source, lastIndex) {
if (!node.callee || !node.callee.name || node.callee.name !== STORIES_OF) {
return lastIndex;
}
parts.pop();
pushParts(source, parts, lastIndex, node.end);
return node.end;
}

View File

@ -1,20 +0,0 @@
function getParser(type) {
if (type === 'javascript' || !type) {
// eslint-disable-next-line global-require
return require('./parser-js').default;
}
if (type === 'typescript') {
// eslint-disable-next-line global-require
return require('./parser-ts').default;
}
if (type === 'flow') {
// eslint-disable-next-line global-require
return require('./parser-flow').default;
}
throw new Error(`Parser of type "${type}" is not supported`);
}
export default getParser;

View File

@ -1,9 +0,0 @@
import parseFlow from 'prettier/parser-flow';
function parse(source) {
return parseFlow.parsers.flow.parse(source);
}
export default {
parse,
};

View File

@ -1,9 +0,0 @@
import parseJs from 'prettier/parser-babylon';
function parse(source) {
return parseJs.parsers.babel.parse(source);
}
export default {
parse,
};

View File

@ -1,9 +0,0 @@
import parseTs from 'prettier/parser-typescript';
function parse(source) {
return parseTs.parsers.typescript.parse(source);
}
export default {
parse,
};

View File

@ -1,38 +0,0 @@
import { handleADD, handleSTORYOF, patchNode } from './parse-helpers';
const estraverse = require('estraverse');
export function splitSTORYOF(ast, source) {
let lastIndex = 0;
const parts = [source];
estraverse.traverse(ast, {
fallback: 'iteration',
enter: node => {
patchNode(node);
if (node.type === 'CallExpression') {
lastIndex = handleSTORYOF(node, parts, source, lastIndex);
}
},
});
return parts;
}
export function findAddsMap(ast) {
const adds = {};
estraverse.traverse(ast, {
fallback: 'iteration',
enter: (node, parent) => {
patchNode(node);
if (node.type === 'MemberExpression') {
handleADD(node, parent, adds);
}
},
});
return adds;
}

View File

@ -5,7 +5,7 @@ module.exports = async ({ config }: { config: any }) => {
test: [/\.stories\.tsx?$/, /index\.ts$/],
loaders: [
{
loader: require.resolve('@storybook/addon-storysource/loader'),
loader: require.resolve('@storybook/source-loader'),
options: {
parser: 'typescript',
},

View File

@ -47,6 +47,7 @@
"@storybook/addon-storysource": "5.2.0-alpha.35",
"@storybook/addons": "5.2.0-alpha.35",
"@storybook/angular": "5.2.0-alpha.35",
"@storybook/source-loader": "5.2.0-alpha.35",
"@types/core-js": "^2.5.0",
"@types/jest": "^24.0.11",
"@types/node": "~12.0.2",

View File

@ -3,7 +3,7 @@ const path = require('path');
module.exports = async ({ config }) => {
config.module.rules.push({
test: [/\.stories\.js$/, /index\.js$/],
loaders: [require.resolve('@storybook/addon-storysource/loader')],
loaders: [require.resolve('@storybook/source-loader')],
include: [path.resolve(__dirname, '../')],
enforce: 'pre',
});

View File

@ -3,7 +3,7 @@ const path = require('path');
module.exports = async ({ config }) => {
config.module.rules.push({
test: [/\.stories\.js$/, /index\.js$/],
loaders: [require.resolve('@storybook/addon-storysource/loader')],
loaders: [require.resolve('@storybook/source-loader')],
include: [path.resolve(__dirname, '../stories')],
enforce: 'pre',
});

View File

@ -3,7 +3,7 @@ const path = require('path');
module.exports = async ({ config }) => {
config.module.rules.push({
test: [/\.stories\.js$/],
loaders: [require.resolve('@storybook/addon-storysource/loader')],
loaders: [require.resolve('@storybook/source-loader')],
include: [path.resolve(__dirname, '../src')],
enforce: 'pre',
});

View File

@ -3,7 +3,7 @@ const path = require('path');
module.exports = async ({ config }) => {
config.module.rules.push({
test: [/\.stories\.js$/],
loaders: [require.resolve('@storybook/addon-storysource/loader')],
loaders: [require.resolve('@storybook/source-loader')],
include: [path.resolve(__dirname, '../src')],
enforce: 'pre',
});

View File

@ -4,7 +4,7 @@ const webpack = require('webpack');
module.exports = async ({ config }) => {
config.module.rules.push({
test: [/\.stories\.js$/, /index\.js$/],
loaders: [require.resolve('@storybook/addon-storysource/loader')],
loaders: [require.resolve('@storybook/source-loader')],
include: [path.resolve(__dirname, '../src')],
enforce: 'pre',
});

View File

@ -3,7 +3,7 @@ const path = require('path');
module.exports = ({ config }) => {
config.module.rules.push({
test: [/\.stories\.js$/],
loaders: [require.resolve('@storybook/addon-storysource/loader')],
loaders: [require.resolve('@storybook/source-loader')],
include: [path.resolve(__dirname, '../src')],
enforce: 'pre',
});

View File

@ -5,7 +5,7 @@ module.exports = {
rules: [
{
test: [/\.stories\.js$/, /index\.js$/],
loaders: [require.resolve('@storybook/addon-storysource/loader')],
loaders: [require.resolve('@storybook/source-loader')],
include: [path.resolve(__dirname, '../src')],
enforce: 'pre',
},

View File

@ -3,7 +3,7 @@ const path = require('path');
module.exports = async ({ config }) => {
config.module.rules.push({
test: [/\.stories\.js$/, /index\.js$/],
loaders: [require.resolve('@storybook/addon-storysource/loader')],
loaders: [require.resolve('@storybook/source-loader')],
include: [path.resolve(__dirname, '../src')],
enforce: 'pre',
});

View File

@ -3,7 +3,7 @@ const path = require('path');
module.exports = async ({ config }) => {
config.module.rules.push({
test: [/\.stories\.js$/, /index\.js$/],
loaders: [require.resolve('@storybook/addon-storysource/loader')],
loaders: [require.resolve('@storybook/source-loader')],
include: [path.resolve(__dirname, '../src')],
enforce: 'pre',
});

View File

@ -8,7 +8,7 @@ module.exports = async ({ config }) => {
});
config.module.rules.push({
test: [/\.stories\.js$/, /index\.js$/],
loaders: [require.resolve('@storybook/addon-storysource/loader')],
loaders: [require.resolve('@storybook/source-loader')],
include: [path.resolve(__dirname, '../src')],
enforce: 'pre',
});