diff --git a/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/export-destructuring.input.js b/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/export-destructuring.input.js
new file mode 100644
index 000000000000..1c4f5faa706e
--- /dev/null
+++ b/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/export-destructuring.input.js
@@ -0,0 +1,11 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import React from 'react';
+import { storiesOf } from '@storybook/react';
+import ComponentRow from './ComponentRow';
+import * as SpecRowStories from './SpecRow.stories';
+
+export const { actions } = SpecRowStories;
+
+storiesOf('ComponentRow', module).add('pending', () => (
+
+));
diff --git a/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/export-destructuring.output.snapshot b/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/export-destructuring.output.snapshot
new file mode 100644
index 000000000000..5225069c4651
--- /dev/null
+++ b/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/export-destructuring.output.snapshot
@@ -0,0 +1,23 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`storiesof-to-csf transforms correctly using "export-destructuring.input.js" data 1`] = `
+"/* eslint-disable import/no-extraneous-dependencies */
+import React from 'react';
+import ComponentRow from './ComponentRow';
+import * as SpecRowStories from './SpecRow.stories';
+
+export const { actions } = SpecRowStories;
+
+export default {
+ title: 'ComponentRow',
+ excludeStories: ['actions'],
+};
+
+export const Pending = () => (
+
+);
+
+Pending.story = {
+ name: 'pending',
+};"
+`;
diff --git a/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/export-function.input.js b/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/export-function.input.js
new file mode 100644
index 000000000000..94149593ab91
--- /dev/null
+++ b/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/export-function.input.js
@@ -0,0 +1,12 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import React from 'react';
+import { storiesOf } from '@storybook/react';
+import ComponentItem from './ComponentItem';
+
+export function someHelper() {
+ return 5;
+}
+
+storiesOf('ComponentItem', module)
+ .addDecorator(storyFn =>
{storyFn()}
)
+ .add('loading', () => );
diff --git a/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/export-function.output.snapshot b/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/export-function.output.snapshot
new file mode 100644
index 000000000000..a190c7cfc9fa
--- /dev/null
+++ b/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/export-function.output.snapshot
@@ -0,0 +1,23 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`storiesof-to-csf transforms correctly using "export-function.input.js" data 1`] = `
+"/* eslint-disable import/no-extraneous-dependencies */
+import React from 'react';
+import ComponentItem from './ComponentItem';
+
+export function someHelper() {
+ return 5;
+}
+
+export default {
+ title: 'ComponentItem',
+ decorators: [storyFn => {storyFn()}
],
+ excludeStories: ['someHelper'],
+};
+
+export const Loading = () => ;
+
+Loading.story = {
+ name: 'loading',
+};"
+`;
diff --git a/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/export-names.input.js b/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/export-names.input.js
new file mode 100644
index 000000000000..c87d8695b8ee
--- /dev/null
+++ b/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/export-names.input.js
@@ -0,0 +1,14 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import React from 'react';
+import { storiesOf } from '@storybook/react';
+import FlexCenter from './FlexCenter';
+import { specs, urls } from './LiveView.stories';
+import { ignoredRegions } from './IgnoredRegions.stories';
+
+export { specs, urls, ignoredRegions };
+
+storiesOf('FlexCenter', module).add('2:1', () => (
+
+ 2:1
+
+));
diff --git a/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/export-names.output.snapshot b/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/export-names.output.snapshot
new file mode 100644
index 000000000000..95ddc5056ced
--- /dev/null
+++ b/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/export-names.output.snapshot
@@ -0,0 +1,26 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`storiesof-to-csf transforms correctly using "export-names.input.js" data 1`] = `
+"/* eslint-disable import/no-extraneous-dependencies */
+import React from 'react';
+import FlexCenter from './FlexCenter';
+import { specs, urls } from './LiveView.stories';
+import { ignoredRegions } from './IgnoredRegions.stories';
+
+export { specs, urls, ignoredRegions };
+
+export default {
+ title: 'FlexCenter',
+ excludeStories: ['specs', 'urls', 'ignoredRegions'],
+};
+
+export const _21 = () => (
+
+ 2:1
+
+);
+
+_21.story = {
+ name: '2:1',
+};"
+`;
diff --git a/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/parameters-as-var.input.js b/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/parameters-as-var.input.js
new file mode 100644
index 000000000000..6d8dcc497032
--- /dev/null
+++ b/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/parameters-as-var.input.js
@@ -0,0 +1,8 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import React from 'react';
+import { storiesOf } from '@storybook/react';
+import Canvas from './Canvas';
+
+const CHROMATIC_DELAY = { chromatic: { delay: 500 } };
+
+storiesOf('Canvas', module).add('loading', () => , CHROMATIC_DELAY);
diff --git a/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/parameters-as-var.output.snapshot b/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/parameters-as-var.output.snapshot
new file mode 100644
index 000000000000..b0489d3c5f3d
--- /dev/null
+++ b/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/parameters-as-var.output.snapshot
@@ -0,0 +1,20 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`storiesof-to-csf transforms correctly using "parameters-as-var.input.js" data 1`] = `
+"/* eslint-disable import/no-extraneous-dependencies */
+import React from 'react';
+import Canvas from './Canvas';
+
+const CHROMATIC_DELAY = { chromatic: { delay: 500 } };
+
+export default {
+ title: 'Canvas',
+};
+
+export const Loading = () => ;
+
+Loading.story = {
+ name: 'loading',
+ parameters: CHROMATIC_DELAY,
+};"
+`;
diff --git a/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/storiesof-var.input.js b/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/storiesof-var.input.js
new file mode 100644
index 000000000000..3a66045ea7b9
--- /dev/null
+++ b/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/storiesof-var.input.js
@@ -0,0 +1,11 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import React from 'react';
+import { storiesOf } from '@storybook/react';
+import Hero from './Hero';
+
+const chapter = storiesOf('Webapp screens/Marketing/LandingScreen/Hero', module).add(
+ 'default',
+ () =>
+);
+
+chapter.add('loading', () => );
diff --git a/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/storiesof-var.output.snapshot b/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/storiesof-var.output.snapshot
new file mode 100644
index 000000000000..4c0889c9137a
--- /dev/null
+++ b/lib/codemod/src/transforms/__testfixtures__/storiesof-to-csf/storiesof-var.output.snapshot
@@ -0,0 +1,23 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`storiesof-to-csf transforms correctly using "storiesof-var.input.js" data 1`] = `
+"/* eslint-disable import/no-extraneous-dependencies */
+import React from 'react';
+import Hero from './Hero';
+
+export default {
+ title: 'Webapp screens/Marketing/LandingScreen/Hero',
+};
+
+export const Default = () => ;
+
+Default.story = {
+ name: 'default',
+};
+
+export const Loading = () => ;
+
+Loading.story = {
+ name: 'loading',
+};"
+`;
diff --git a/lib/codemod/src/transforms/storiesof-to-csf.js b/lib/codemod/src/transforms/storiesof-to-csf.js
index d27842a1a855..482cf6819a73 100644
--- a/lib/codemod/src/transforms/storiesof-to-csf.js
+++ b/lib/codemod/src/transforms/storiesof-to-csf.js
@@ -32,6 +32,9 @@ export default function transformer(file, api, options) {
if (!parameters) {
return {};
}
+ if (!parameters.properties) {
+ return { storyParams: parameters };
+ }
let storyDecorators = parameters.properties.find(p => p.key.name === 'decorators');
if (!storyDecorators) {
return { storyParams: parameters };
@@ -177,9 +180,11 @@ export default function transformer(file, api, options) {
}
});
+ const stmt = path.parent.node.type === 'VariableDeclarator' ? path.parent.parent : path.parent;
+
statements.reverse();
- statements.forEach(s => path.parent.insertAfter(s));
- base.remove();
+ statements.forEach(s => stmt.insertAfter(s));
+ j(stmt).remove();
}
// Save the original storiesOf
@@ -202,9 +207,26 @@ export default function transformer(file, api, options) {
// Exclude all the original named exports
const originalExports = [];
- root
- .find(j.ExportNamedDeclaration)
- .forEach(exp => originalExports.push(exp.node.declaration.declarations[0].id.name));
+ root.find(j.ExportNamedDeclaration).forEach(exp => {
+ const { declaration, specifiers } = exp.node;
+ if (declaration) {
+ const { id, declarations } = declaration;
+ if (declarations) {
+ declarations.forEach(decl => {
+ const { name, properties } = decl.id;
+ if (name) {
+ originalExports.push(name);
+ } else if (properties) {
+ properties.forEach(prop => originalExports.push(prop.key.name));
+ }
+ });
+ } else if (id) {
+ originalExports.push(id.name);
+ }
+ } else if (specifiers) {
+ specifiers.forEach(spec => originalExports.push(spec.exported.name));
+ }
+ });
// each top-level add expression corresponds to the last "add" of the chain.
// replace it with the entire export statements
@@ -212,7 +234,7 @@ export default function transformer(file, api, options) {
.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')
+ .filter(add => ['ExpressionStatement', 'VariableDeclarator'].includes(add.parentPath.node.type))
.forEach(path => convertToModuleExports(path, originalExports));
// remove storiesOf import