@storybook/codemod 7.0.0-alpha.6 → 7.0.0-alpha.60

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. package/README.md +0 -36
  2. package/dist/index.d.ts +261 -0
  3. package/dist/index.js +1 -0
  4. package/dist/index.mjs +1 -0
  5. package/jest.config.js +7 -0
  6. package/package.json +32 -29
  7. package/src/index.js +77 -0
  8. package/src/lib/utils.test.js +9 -0
  9. package/{dist/esm/lib/utils.js → src/lib/utils.ts} +10 -11
  10. package/src/transforms/__testfixtures__/add-component-parameters/add-component-parameters.input.js +44 -0
  11. package/src/transforms/__testfixtures__/add-component-parameters/add-component-parameters.output.snapshot +68 -0
  12. package/src/transforms/__testfixtures__/csf-hoist-story-annotations/basic.input.js +25 -0
  13. package/src/transforms/__testfixtures__/csf-hoist-story-annotations/basic.output.snapshot +27 -0
  14. package/src/transforms/__testfixtures__/csf-hoist-story-annotations/overrides.input.js +25 -0
  15. package/src/transforms/__testfixtures__/csf-hoist-story-annotations/overrides.output.snapshot +28 -0
  16. package/src/transforms/__testfixtures__/csf-hoist-story-annotations/variable.input.js +13 -0
  17. package/src/transforms/__testfixtures__/csf-hoist-story-annotations/variable.output.snapshot +17 -0
  18. package/src/transforms/__testfixtures__/csf-to-mdx/basic.input.js +20 -0
  19. package/src/transforms/__testfixtures__/csf-to-mdx/basic.output.snapshot +18 -0
  20. package/src/transforms/__testfixtures__/csf-to-mdx/component-id.input.js +9 -0
  21. package/src/transforms/__testfixtures__/csf-to-mdx/component-id.output.snapshot +10 -0
  22. package/src/transforms/__testfixtures__/csf-to-mdx/decorators.input.js +13 -0
  23. package/src/transforms/__testfixtures__/csf-to-mdx/decorators.output.snapshot +12 -0
  24. package/src/transforms/__testfixtures__/csf-to-mdx/exclude-stories.input.js +23 -0
  25. package/src/transforms/__testfixtures__/csf-to-mdx/exclude-stories.output.snapshot +22 -0
  26. package/src/transforms/__testfixtures__/csf-to-mdx/parameters.input.js +16 -0
  27. package/src/transforms/__testfixtures__/csf-to-mdx/parameters.output.snapshot +17 -0
  28. package/src/transforms/__testfixtures__/csf-to-mdx/story-function.input.js +19 -0
  29. package/src/transforms/__testfixtures__/csf-to-mdx/story-function.output.snapshot +18 -0
  30. package/src/transforms/__testfixtures__/csf-to-mdx/story-parameters.input.js +24 -0
  31. package/src/transforms/__testfixtures__/csf-to-mdx/story-parameters.output.snapshot +22 -0
  32. package/src/transforms/__testfixtures__/mdx-to-csf/basic.input.js +18 -0
  33. package/src/transforms/__testfixtures__/mdx-to-csf/basic.output.snapshot +40 -0
  34. package/src/transforms/__testfixtures__/mdx-to-csf/component-id.input.js +6 -0
  35. package/src/transforms/__testfixtures__/mdx-to-csf/component-id.output.snapshot +17 -0
  36. package/src/transforms/__testfixtures__/mdx-to-csf/decorators.input.js +8 -0
  37. package/src/transforms/__testfixtures__/mdx-to-csf/decorators.output.snapshot +18 -0
  38. package/src/transforms/__testfixtures__/mdx-to-csf/exclude-stories.input.js +19 -0
  39. package/src/transforms/__testfixtures__/mdx-to-csf/exclude-stories.output.snapshot +39 -0
  40. package/src/transforms/__testfixtures__/mdx-to-csf/parameters.input.js +14 -0
  41. package/src/transforms/__testfixtures__/mdx-to-csf/parameters.output.snapshot +23 -0
  42. package/src/transforms/__testfixtures__/mdx-to-csf/plaintext.input.js +3 -0
  43. package/src/transforms/__testfixtures__/mdx-to-csf/plaintext.output.snapshot +11 -0
  44. package/src/transforms/__testfixtures__/mdx-to-csf/story-function.input.js +10 -0
  45. package/src/transforms/__testfixtures__/mdx-to-csf/story-function.output.snapshot +18 -0
  46. package/src/transforms/__testfixtures__/mdx-to-csf/story-parameters.input.js +18 -0
  47. package/src/transforms/__testfixtures__/mdx-to-csf/story-parameters.output.snapshot +32 -0
  48. package/src/transforms/__testfixtures__/mdx-to-csf/story-refs.input.js +22 -0
  49. package/src/transforms/__testfixtures__/mdx-to-csf/story-refs.output.snapshot +34 -0
  50. package/src/transforms/__testfixtures__/move-builtin-addons/default.input.js +2 -0
  51. package/src/transforms/__testfixtures__/move-builtin-addons/default.output.snapshot +8 -0
  52. package/src/transforms/__testfixtures__/move-builtin-addons/with-no-change.input.js +3 -0
  53. package/src/transforms/__testfixtures__/move-builtin-addons/with-no-change.output.snapshot +7 -0
  54. package/src/transforms/__testfixtures__/storiesof-to-csf/basic.input.js +18 -0
  55. package/src/transforms/__testfixtures__/storiesof-to-csf/basic.output.snapshot +45 -0
  56. package/src/transforms/__testfixtures__/storiesof-to-csf/collision.input.js +11 -0
  57. package/src/transforms/__testfixtures__/storiesof-to-csf/collision.output.snapshot +38 -0
  58. package/src/transforms/__testfixtures__/storiesof-to-csf/const.input.js +1 -0
  59. package/src/transforms/__testfixtures__/storiesof-to-csf/const.output.snapshot +13 -0
  60. package/src/transforms/__testfixtures__/storiesof-to-csf/decorators.input.js +9 -0
  61. package/src/transforms/__testfixtures__/storiesof-to-csf/decorators.output.snapshot +18 -0
  62. package/src/transforms/__testfixtures__/storiesof-to-csf/default.input.js +7 -0
  63. package/src/transforms/__testfixtures__/storiesof-to-csf/default.output.snapshot +17 -0
  64. package/src/transforms/__testfixtures__/storiesof-to-csf/digit.input.js +1 -0
  65. package/src/transforms/__testfixtures__/storiesof-to-csf/digit.output.js +5 -0
  66. package/src/transforms/__testfixtures__/storiesof-to-csf/digit.output.snapshot +9 -0
  67. package/src/transforms/__testfixtures__/storiesof-to-csf/export-destructuring.input.js +11 -0
  68. package/src/transforms/__testfixtures__/storiesof-to-csf/export-destructuring.output.snapshot +23 -0
  69. package/src/transforms/__testfixtures__/storiesof-to-csf/export-function.input.js +12 -0
  70. package/src/transforms/__testfixtures__/storiesof-to-csf/export-function.output.snapshot +23 -0
  71. package/src/transforms/__testfixtures__/storiesof-to-csf/export-names.input.js +14 -0
  72. package/src/transforms/__testfixtures__/storiesof-to-csf/export-names.output.snapshot +26 -0
  73. package/src/transforms/__testfixtures__/storiesof-to-csf/exports.input.js +2 -0
  74. package/src/transforms/__testfixtures__/storiesof-to-csf/exports.output.snapshot +16 -0
  75. package/src/transforms/__testfixtures__/storiesof-to-csf/module.input.js +12 -0
  76. package/src/transforms/__testfixtures__/storiesof-to-csf/module.output.snapshot +16 -0
  77. package/src/transforms/__testfixtures__/storiesof-to-csf/multi.input.js +14 -0
  78. package/src/transforms/__testfixtures__/storiesof-to-csf/multi.output.snapshot +39 -0
  79. package/src/transforms/__testfixtures__/storiesof-to-csf/parameters-as-var.input.js +8 -0
  80. package/src/transforms/__testfixtures__/storiesof-to-csf/parameters-as-var.output.snapshot +20 -0
  81. package/src/transforms/__testfixtures__/storiesof-to-csf/parameters.input.js +10 -0
  82. package/src/transforms/__testfixtures__/storiesof-to-csf/parameters.output.snapshot +23 -0
  83. package/src/transforms/__testfixtures__/storiesof-to-csf/storiesof-var.input.js +11 -0
  84. package/src/transforms/__testfixtures__/storiesof-to-csf/storiesof-var.output.snapshot +23 -0
  85. package/src/transforms/__testfixtures__/storiesof-to-csf/story-decorators.input.js +11 -0
  86. package/src/transforms/__testfixtures__/storiesof-to-csf/story-decorators.output.snapshot +29 -0
  87. package/src/transforms/__testfixtures__/storiesof-to-csf/story-parameters.input.js +14 -0
  88. package/src/transforms/__testfixtures__/storiesof-to-csf/story-parameters.output.snapshot +32 -0
  89. package/src/transforms/__testfixtures__/update-addon-info/update-addon-info.input.js +198 -0
  90. package/src/transforms/__testfixtures__/update-addon-info/update-addon-info.output.snapshot +198 -0
  91. package/src/transforms/__testfixtures__/update-organisation-name/update-organisation-name.input.js +19 -0
  92. package/src/transforms/__testfixtures__/update-organisation-name/update-organisation-name.output.snapshot +23 -0
  93. package/src/transforms/__testfixtures__/upgrade-hierarchy-separators/csf.input.js +3 -0
  94. package/src/transforms/__testfixtures__/upgrade-hierarchy-separators/csf.output.snapshot +7 -0
  95. package/src/transforms/__testfixtures__/upgrade-hierarchy-separators/dynamic-storiesof.input.js +5 -0
  96. package/src/transforms/__testfixtures__/upgrade-hierarchy-separators/dynamic-storiesof.output.snapshot +9 -0
  97. package/src/transforms/__testfixtures__/upgrade-hierarchy-separators/storiesof.input.js +8 -0
  98. package/src/transforms/__testfixtures__/upgrade-hierarchy-separators/storiesof.output.snapshot +12 -0
  99. package/src/transforms/__tests__/csf-2-to-3.test.ts +250 -0
  100. package/src/transforms/__tests__/transforms.tests.js +32 -0
  101. package/{dist/esm → src}/transforms/add-component-parameters.js +26 -21
  102. package/src/transforms/csf-2-to-3.ts +184 -0
  103. package/src/transforms/csf-hoist-story-annotations.js +97 -0
  104. package/src/transforms/csf-to-mdx.js +190 -0
  105. package/src/transforms/move-builtin-addons.js +32 -0
  106. package/src/transforms/storiesof-to-csf.js +277 -0
  107. package/{dist/esm → src}/transforms/update-addon-info.js +44 -24
  108. package/{dist/esm → src}/transforms/update-organisation-name.js +21 -24
  109. package/src/transforms/upgrade-hierarchy-separators.js +39 -0
  110. package/tsconfig.json +7 -0
  111. package/LICENSE +0 -21
  112. package/dist/cjs/index.js +0 -142
  113. package/dist/cjs/lib/utils.js +0 -45
  114. package/dist/cjs/transforms/add-component-parameters.js +0 -64
  115. package/dist/cjs/transforms/csf-2-to-3.js +0 -180
  116. package/dist/cjs/transforms/csf-hoist-story-annotations.js +0 -93
  117. package/dist/cjs/transforms/csf-to-mdx.js +0 -196
  118. package/dist/cjs/transforms/mdx-to-csf.js +0 -153
  119. package/dist/cjs/transforms/move-builtin-addons.js +0 -57
  120. package/dist/cjs/transforms/storiesof-to-csf.js +0 -300
  121. package/dist/cjs/transforms/update-addon-info.js +0 -101
  122. package/dist/cjs/transforms/update-organisation-name.js +0 -83
  123. package/dist/cjs/transforms/upgrade-hierarchy-separators.js +0 -42
  124. package/dist/esm/index.js +0 -99
  125. package/dist/esm/transforms/csf-2-to-3.js +0 -163
  126. package/dist/esm/transforms/csf-hoist-story-annotations.js +0 -86
  127. package/dist/esm/transforms/csf-to-mdx.js +0 -188
  128. package/dist/esm/transforms/mdx-to-csf.js +0 -139
  129. package/dist/esm/transforms/move-builtin-addons.js +0 -50
  130. package/dist/esm/transforms/storiesof-to-csf.js +0 -287
  131. package/dist/esm/transforms/upgrade-hierarchy-separators.js +0 -35
  132. package/dist/types/lib/utils.d.ts +0 -2
  133. package/dist/types/transforms/csf-2-to-3.d.ts +0 -6
@@ -0,0 +1,190 @@
1
+ import { prettyPrint } from 'recast';
2
+ import { isExportStory } from '@storybook/csf';
3
+
4
+ function exportMdx(root, options) {
5
+ // eslint-disable-next-line no-underscore-dangle
6
+ const path = root.__paths[0];
7
+
8
+ // FIXME: insert the title as markdown after all of the imports
9
+ return path.node.program.body
10
+ .map((n) => {
11
+ const { code } = prettyPrint(n, options);
12
+ if (n.type === 'JSXElement') {
13
+ return `${code}\n`;
14
+ }
15
+ return code;
16
+ })
17
+ .join('\n');
18
+ }
19
+
20
+ function parseIncludeExclude(prop) {
21
+ const { code } = prettyPrint(prop, {});
22
+ // eslint-disable-next-line no-eval
23
+ return (0, eval)(code);
24
+ }
25
+
26
+ /**
27
+ * Convert a component's module story file into an MDX file
28
+ *
29
+ * For example:
30
+ *
31
+ * ```
32
+ * input { Button } from './Button';
33
+ * export default {
34
+ * title: 'Button'
35
+ * }
36
+ * export const story = () => <Button label="The Button" />;
37
+ * ```
38
+ *
39
+ * Becomes:
40
+ *
41
+ * ```
42
+ * import { Meta, Story } from '@storybook/addon-docs';
43
+ * input { Button } from './Button';
44
+ *
45
+ * <Meta title='Button' />
46
+ *
47
+ * <Story name='story'>
48
+ * <Button label="The Button" />
49
+ * </Story>
50
+ * ```
51
+ */
52
+ export default function transformer(file, api) {
53
+ const j = api.jscodeshift;
54
+ const root = j(file.source);
55
+
56
+ // FIXME: save out all the storyFn.story = { ... }
57
+ const storyKeyToStory = {};
58
+ // save out includeStories / excludeStories
59
+ const meta = {};
60
+
61
+ function makeAttr(key, val) {
62
+ return j.jsxAttribute(
63
+ j.jsxIdentifier(key),
64
+ val.type === 'Literal' ? val : j.jsxExpressionContainer(val)
65
+ );
66
+ }
67
+
68
+ function getStoryContents(node) {
69
+ return node.type === 'ArrowFunctionExpression' && node.body.type === 'JSXElement'
70
+ ? node.body
71
+ : j.jsxExpressionContainer(node);
72
+ }
73
+
74
+ function getName(storyKey) {
75
+ const story = storyKeyToStory[storyKey];
76
+ if (story) {
77
+ const name = story.properties.find((prop) => prop.key.name === 'name');
78
+ if (name && name.value.type === 'Literal') {
79
+ return name.value.value;
80
+ }
81
+ }
82
+ return storyKey;
83
+ }
84
+
85
+ function getStoryAttrs(storyKey) {
86
+ const attrs = [];
87
+ const story = storyKeyToStory[storyKey];
88
+ if (story) {
89
+ story.properties.forEach((prop) => {
90
+ const { key, value } = prop;
91
+ if (key.name !== 'name') {
92
+ attrs.push(makeAttr(key.name, value));
93
+ }
94
+ });
95
+ }
96
+ return attrs;
97
+ }
98
+
99
+ // 1. If the program does not have `export default { title: '....' }, skip it
100
+ const defaultExportWithTitle = root
101
+ .find(j.ExportDefaultDeclaration)
102
+ .filter((def) => def.node.declaration.properties.map((p) => p.key.name).includes('title'));
103
+ if (defaultExportWithTitle.size() === 0) {
104
+ return root.toSource();
105
+ }
106
+
107
+ // 2a. Add imports from '@storybook/addon-docs'
108
+ root
109
+ .find(j.ImportDeclaration)
110
+ .at(-1)
111
+ .insertAfter(j.emptyStatement())
112
+ .insertAfter(
113
+ j.importDeclaration(
114
+ [j.importSpecifier(j.identifier('Meta')), j.importSpecifier(j.identifier('Story'))],
115
+ j.literal('@storybook/addon-docs')
116
+ )
117
+ );
118
+ // 2b. Remove react import which is implicit
119
+ root
120
+ .find(j.ImportDeclaration)
121
+ .filter((decl) => decl.node.source.value === 'react')
122
+ .remove();
123
+
124
+ // 3. Save out all the excluded stories
125
+ defaultExportWithTitle.forEach((exp) => {
126
+ exp.node.declaration.properties.forEach((p) => {
127
+ if (['includeStories', 'excludeStories'].includes(p.key.name)) {
128
+ meta[p.key.name] = parseIncludeExclude(p.value);
129
+ }
130
+ });
131
+ });
132
+
133
+ // 4. Collect all the story exports in storyKeyToStory[key] = null;
134
+ const namedExports = root.find(j.ExportNamedDeclaration);
135
+ namedExports.forEach((exp) => {
136
+ const storyKey = exp.node.declaration.declarations[0].id.name;
137
+ if (isExportStory(storyKey, meta)) {
138
+ storyKeyToStory[storyKey] = null;
139
+ }
140
+ });
141
+
142
+ // 5. Collect all the storyKey.story in storyKeyToStory and also remove them
143
+ const storyAssignments = root.find(j.AssignmentExpression).filter((exp) => {
144
+ const { left } = exp.node;
145
+ return (
146
+ left.type === 'MemberExpression' &&
147
+ left.object.type === 'Identifier' &&
148
+ left.object.name in storyKeyToStory &&
149
+ left.property.type === 'Identifier' &&
150
+ left.property.name === 'story'
151
+ );
152
+ });
153
+ storyAssignments.forEach((exp) => {
154
+ const { left, right } = exp.node;
155
+ storyKeyToStory[left.object.name] = right;
156
+ });
157
+ storyAssignments.remove();
158
+
159
+ // 6. Convert the default export to <Meta />
160
+ defaultExportWithTitle.replaceWith((exp) => {
161
+ const jsxId = j.jsxIdentifier('Meta');
162
+ const attrs = [];
163
+ exp.node.declaration.properties.forEach((prop) => {
164
+ const { key, value } = prop;
165
+ if (!['includeStories', 'excludeStories'].includes(key.name)) {
166
+ attrs.push(makeAttr(key.name, value));
167
+ }
168
+ });
169
+ const opening = j.jsxOpeningElement(jsxId, attrs);
170
+ opening.selfClosing = true;
171
+ return j.jsxElement(opening);
172
+ });
173
+
174
+ // 7. Convert all the named exports to <Story>...</Story>
175
+ namedExports.replaceWith((exp) => {
176
+ const storyKey = exp.node.declaration.declarations[0].id.name;
177
+ if (!isExportStory(storyKey, meta)) {
178
+ return exp.node;
179
+ }
180
+ const jsxId = j.jsxIdentifier('Story');
181
+ const name = getName(storyKey);
182
+ const attributes = [makeAttr('name', j.literal(name)), ...getStoryAttrs(storyKey)];
183
+ const opening = j.jsxOpeningElement(jsxId, attributes);
184
+ const closing = j.jsxClosingElement(jsxId);
185
+ const children = [getStoryContents(exp.node.declaration.declarations[0].init)];
186
+ return j.jsxElement(opening, closing, children);
187
+ });
188
+
189
+ return exportMdx(root, { quote: 'single', trailingComma: 'true', tabWidth: 2 });
190
+ }
@@ -0,0 +1,32 @@
1
+ export default function transformer(file, api) {
2
+ const j = api.jscodeshift;
3
+
4
+ const createImportDeclaration = (specifiers, source) =>
5
+ j.importDeclaration(
6
+ specifiers.map((s) => j.importSpecifier(j.identifier(s))),
7
+ j.literal(source)
8
+ );
9
+
10
+ const deprecates = {
11
+ action: [['action'], '@storybook/addon-actions'],
12
+ linkTo: [['linkTo'], '@storybook/addon-links'],
13
+ };
14
+
15
+ const transform = j(file.source)
16
+ .find(j.ImportDeclaration)
17
+ .filter((i) => i.value.source.value === '@storybook/react')
18
+ .forEach((i) => {
19
+ const importStatement = i.value;
20
+ importStatement.specifiers = importStatement.specifiers.filter((specifier) => {
21
+ const item = deprecates[specifier.local.name];
22
+ if (item) {
23
+ const [specifiers, moduleName] = item;
24
+ i.insertAfter(createImportDeclaration(specifiers, moduleName));
25
+ return false;
26
+ }
27
+ return specifier;
28
+ });
29
+ });
30
+
31
+ return transform.toSource({ quote: 'single' });
32
+ }
@@ -0,0 +1,277 @@
1
+ import prettier from 'prettier';
2
+ import { logger } from '@storybook/node-logger';
3
+ import { storyNameFromExport } from '@storybook/csf';
4
+ import { sanitizeName, jscodeshiftToPrettierParser } from '../lib/utils';
5
+
6
+ /**
7
+ * Convert a legacy story API to component story format
8
+ *
9
+ * For example:
10
+ *
11
+ * ```
12
+ * input { Button } from './Button';
13
+ * storiesOf('Button', module).add('story', () => <Button label="The Button" />);
14
+ * ```
15
+ *
16
+ * Becomes:
17
+ *
18
+ * ```
19
+ * input { Button } from './Button';
20
+ * export default {
21
+ * title: 'Button'
22
+ * }
23
+ * export const story = () => <Button label="The Button" />;
24
+ *
25
+ * NOTES: only support chained storiesOf() calls
26
+ */
27
+ export default function transformer(file, api, options) {
28
+ const LITERAL = ['ts', 'tsx'].includes(options.parser) ? 'StringLiteral' : 'Literal';
29
+
30
+ const j = api.jscodeshift;
31
+ const root = j(file.source);
32
+
33
+ function extractDecorators(parameters) {
34
+ if (!parameters) {
35
+ return {};
36
+ }
37
+ if (!parameters.properties) {
38
+ return { storyParams: parameters };
39
+ }
40
+ let storyDecorators = parameters.properties.find((p) => p.key.name === 'decorators');
41
+ if (!storyDecorators) {
42
+ return { storyParams: parameters };
43
+ }
44
+ storyDecorators = storyDecorators.value;
45
+ const storyParams = { ...parameters };
46
+ storyParams.properties = storyParams.properties.filter((p) => p.key.name !== 'decorators');
47
+ if (storyParams.properties.length === 0) {
48
+ return { storyDecorators };
49
+ }
50
+ return { storyParams, storyDecorators };
51
+ }
52
+
53
+ function convertToModuleExports(path, originalExports) {
54
+ const base = j(path);
55
+
56
+ const statements = [];
57
+ const extraExports = [];
58
+
59
+ // .addDecorator
60
+ const decorators = [];
61
+ base
62
+ .find(j.CallExpression)
63
+ .filter(
64
+ (call) => call.node.callee.property && call.node.callee.property.name === 'addDecorator'
65
+ )
66
+ .forEach((add) => {
67
+ const decorator = add.node.arguments[0];
68
+ decorators.push(decorator);
69
+ });
70
+ if (decorators.length > 0) {
71
+ decorators.reverse();
72
+ extraExports.push(
73
+ j.property('init', j.identifier('decorators'), j.arrayExpression(decorators))
74
+ );
75
+ }
76
+
77
+ // .addParameters
78
+ const parameters = [];
79
+ base
80
+ .find(j.CallExpression)
81
+ .filter(
82
+ (call) => call.node.callee.property && call.node.callee.property.name === 'addParameters'
83
+ )
84
+ .forEach((add) => {
85
+ // jscodeshift gives us the find results in reverse, but these args come in
86
+ // order, so we double reverse here. ugh.
87
+ const params = [...add.node.arguments[0].properties];
88
+ params.reverse();
89
+ params.forEach((prop) => parameters.push(prop));
90
+ });
91
+ if (parameters.length > 0) {
92
+ parameters.reverse();
93
+ extraExports.push(
94
+ j.property('init', j.identifier('parameters'), j.objectExpression(parameters))
95
+ );
96
+ }
97
+
98
+ if (originalExports.length > 0) {
99
+ extraExports.push(
100
+ j.property(
101
+ 'init',
102
+ j.identifier('excludeStories'),
103
+ j.arrayExpression(originalExports.map((exp) => j.literal(exp)))
104
+ )
105
+ );
106
+ }
107
+
108
+ // storiesOf(...)
109
+ base
110
+ .find(j.CallExpression)
111
+ .filter((call) => call.node.callee.name === 'storiesOf')
112
+ .filter((call) => call.node.arguments.length > 0 && call.node.arguments[0].type === LITERAL)
113
+ .forEach((storiesOf) => {
114
+ const title = storiesOf.node.arguments[0].value;
115
+ statements.push(
116
+ j.exportDefaultDeclaration(
117
+ j.objectExpression([
118
+ j.property('init', j.identifier('title'), j.literal(title)),
119
+ ...extraExports,
120
+ ])
121
+ )
122
+ );
123
+ });
124
+
125
+ // .add(...)
126
+ const adds = [];
127
+ base
128
+ .find(j.CallExpression)
129
+ .filter((add) => add.node.callee.property && add.node.callee.property.name === 'add')
130
+ .filter((add) => add.node.arguments.length >= 2 && add.node.arguments[0].type === LITERAL)
131
+ .forEach((add) => adds.push(add));
132
+
133
+ adds.reverse();
134
+ adds.push(path);
135
+
136
+ const identifiers = new Set();
137
+ root.find(j.Identifier).forEach(({ value }) => identifiers.add(value.name));
138
+ adds.forEach((add) => {
139
+ let name = add.node.arguments[0].value;
140
+ let key = sanitizeName(name);
141
+ while (identifiers.has(key)) {
142
+ key = `_${key}`;
143
+ }
144
+ identifiers.add(key);
145
+ if (storyNameFromExport(key) === name) {
146
+ name = null;
147
+ }
148
+
149
+ const val = add.node.arguments[1];
150
+ statements.push(
151
+ j.exportDeclaration(
152
+ false,
153
+ j.variableDeclaration('const', [j.variableDeclarator(j.identifier(key), val)])
154
+ )
155
+ );
156
+
157
+ const storyAnnotations = [];
158
+
159
+ if (name) {
160
+ storyAnnotations.push(j.property('init', j.identifier('name'), j.literal(name)));
161
+ }
162
+
163
+ if (add.node.arguments.length > 2) {
164
+ const originalStoryParams = add.node.arguments[2];
165
+ const { storyParams, storyDecorators } = extractDecorators(originalStoryParams);
166
+ if (storyParams) {
167
+ storyAnnotations.push(j.property('init', j.identifier('parameters'), storyParams));
168
+ }
169
+ if (storyDecorators) {
170
+ storyAnnotations.push(j.property('init', j.identifier('decorators'), storyDecorators));
171
+ }
172
+ }
173
+
174
+ if (storyAnnotations.length > 0) {
175
+ statements.push(
176
+ j.assignmentStatement(
177
+ '=',
178
+ j.memberExpression(j.identifier(key), j.identifier('story')),
179
+ j.objectExpression(storyAnnotations)
180
+ )
181
+ );
182
+ }
183
+ });
184
+
185
+ const stmt = path.parent.node.type === 'VariableDeclarator' ? path.parent.parent : path.parent;
186
+
187
+ statements.reverse();
188
+ statements.forEach((s) => stmt.insertAfter(s));
189
+ j(stmt).remove();
190
+ }
191
+
192
+ // Save the original storiesOf
193
+ const initialStoriesOf = root
194
+ .find(j.CallExpression)
195
+ .filter((call) => call.node.callee.name === 'storiesOf');
196
+
197
+ const defaultExports = root.find(j.ExportDefaultDeclaration);
198
+ // If there's already a default export
199
+ if (defaultExports.size() > 0) {
200
+ if (initialStoriesOf.size() > 0) {
201
+ logger.warn(
202
+ `Found ${initialStoriesOf.size()} 'storiesOf' calls but existing default export, SKIPPING: '${
203
+ file.path
204
+ }'`
205
+ );
206
+ }
207
+ return root.toSource();
208
+ }
209
+
210
+ // Exclude all the original named exports
211
+ const originalExports = [];
212
+ root.find(j.ExportNamedDeclaration).forEach((exp) => {
213
+ const { declaration, specifiers } = exp.node;
214
+ if (declaration) {
215
+ const { id, declarations } = declaration;
216
+ if (declarations) {
217
+ declarations.forEach((decl) => {
218
+ const { name, properties } = decl.id;
219
+ if (name) {
220
+ originalExports.push(name);
221
+ } else if (properties) {
222
+ properties.forEach((prop) => originalExports.push(prop.key.name));
223
+ }
224
+ });
225
+ } else if (id) {
226
+ originalExports.push(id.name);
227
+ }
228
+ } else if (specifiers) {
229
+ specifiers.forEach((spec) => originalExports.push(spec.exported.name));
230
+ }
231
+ });
232
+
233
+ // each top-level add expression corresponds to the last "add" of the chain.
234
+ // replace it with the entire export statements
235
+ root
236
+ .find(j.CallExpression)
237
+ .filter((add) => add.node.callee.property && add.node.callee.property.name === 'add')
238
+ .filter((add) => add.node.arguments.length >= 2 && add.node.arguments[0].type === LITERAL)
239
+ .filter((add) =>
240
+ ['ExpressionStatement', 'VariableDeclarator'].includes(add.parentPath.node.type)
241
+ )
242
+ .forEach((path) => convertToModuleExports(path, originalExports));
243
+
244
+ // remove storiesOf import
245
+ root
246
+ .find(j.ImportSpecifier)
247
+ .filter(
248
+ (spec) =>
249
+ spec.node.imported.name === 'storiesOf' &&
250
+ spec.parent.node.source.value.startsWith('@storybook/')
251
+ )
252
+ .forEach((spec) => {
253
+ const toRemove = spec.parent.node.specifiers.length > 1 ? spec : spec.parent;
254
+ j(toRemove).remove();
255
+ });
256
+
257
+ const source = root.toSource({ trailingComma: true, quote: 'single', tabWidth: 2 });
258
+ if (initialStoriesOf.size() > 1) {
259
+ logger.warn(
260
+ `Found ${initialStoriesOf.size()} 'storiesOf' calls, PLEASE FIX BY HAND: '${file.path}'`
261
+ );
262
+ return source;
263
+ }
264
+
265
+ const prettierConfig = prettier.resolveConfig.sync('.', { editorconfig: true }) || {
266
+ printWidth: 100,
267
+ tabWidth: 2,
268
+ bracketSpacing: true,
269
+ trailingComma: 'es5',
270
+ singleQuote: true,
271
+ };
272
+
273
+ return prettier.format(source, {
274
+ ...prettierConfig,
275
+ parser: jscodeshiftToPrettierParser(options.parser),
276
+ });
277
+ }
@@ -23,8 +23,9 @@
23
23
  * )))
24
24
  */
25
25
  export default function transformer(file, api) {
26
- var j = api.jscodeshift;
27
- var root = j(file.source);
26
+ const j = api.jscodeshift;
27
+ const root = j(file.source);
28
+
28
29
  /**
29
30
  * Returns a list of parameters for the withInfo function. The contents
30
31
  * of this list is either the second argument from the original
@@ -33,56 +34,75 @@ export default function transformer(file, api) {
33
34
  * @param {list} args - original addWithInfo function parameters
34
35
  * @return {list} the modified list of parameters for the new function
35
36
  */
36
-
37
- var getOptions = function (args) {
37
+ const getOptions = (args) => {
38
38
  if (args[3] === undefined) {
39
39
  if (args[2] === undefined) {
40
40
  // when the optional description string is not supplied for addWithInfo, use story name
41
41
  return [args[0]];
42
42
  }
43
-
44
43
  return [args[1]];
45
44
  }
46
-
47
- return [j.objectExpression([j.property('init', j.identifier('text'), args[1]), ...args[3].properties])];
45
+ return [
46
+ j.objectExpression([
47
+ j.property('init', j.identifier('text'), args[1]),
48
+ ...args[3].properties,
49
+ ]),
50
+ ];
48
51
  };
52
+
49
53
  /**
50
54
  * Constructs the new withInfo function from the parameters of the
51
55
  * original addWithInfo function.
52
56
  * @param {CallExpression} addWithInfoExpression - original function
53
57
  * @returns {CallExpression} the new withInfo function
54
58
  */
59
+ const withInfo = (addWithInfoExpression) => {
60
+ const { node } = addWithInfoExpression;
61
+ const args = node.arguments;
55
62
 
63
+ // if optional description string is not supplied, the story component becomes second arg
64
+ const storyComponent = args[2] ? args[2] : args[1];
56
65
 
57
- var withInfo = function (addWithInfoExpression) {
58
- var node = addWithInfoExpression.node;
59
- var args = node.arguments; // if optional description string is not supplied, the story component becomes second arg
60
-
61
- var storyComponent = args[2] ? args[2] : args[1];
62
66
  node.callee.property.name = 'add';
63
- node.arguments = [args[0], j.callExpression(j.callExpression(j.identifier('withInfo'), getOptions(args)), [storyComponent])];
67
+ node.arguments = [
68
+ args[0],
69
+ j.callExpression(j.callExpression(j.identifier('withInfo'), getOptions(args)), [
70
+ storyComponent,
71
+ ]),
72
+ ];
73
+
64
74
  return node;
65
75
  };
76
+
66
77
  /**
67
78
  * Checks for - import { withInfo } from "@storybook/addon-info";
68
79
  * Adds the import if necessary.
69
80
  */
81
+ const checkWithInfoImport = () => {
82
+ const importExists = root
83
+ .find(j.ImportDeclaration)
84
+ .filter((imp) => imp.node.source.value === '@storybook/addon-info')
85
+ .size();
70
86
 
71
-
72
- var checkWithInfoImport = function () {
73
- var importExists = root.find(j.ImportDeclaration).filter(function (imp) {
74
- return imp.node.source.value === '@storybook/addon-info';
75
- }).size();
76
87
  if (importExists) return;
77
- root.find(j.ImportDeclaration).at(-1).insertAfter(j.importDeclaration([j.importSpecifier(j.identifier('withInfo'))], j.literal('@storybook/addon-info')));
88
+
89
+ root
90
+ .find(j.ImportDeclaration)
91
+ .at(-1)
92
+ .insertAfter(
93
+ j.importDeclaration(
94
+ [j.importSpecifier(j.identifier('withInfo'))],
95
+ j.literal('@storybook/addon-info')
96
+ )
97
+ );
78
98
  };
79
99
 
80
- var addWithInfoExpressions = root.find(j.CallExpression, {
100
+ const addWithInfoExpressions = root.find(j.CallExpression, {
81
101
  callee: {
82
102
  property: {
83
- name: 'addWithInfo'
84
- }
85
- }
103
+ name: 'addWithInfo',
104
+ },
105
+ },
86
106
  });
87
107
 
88
108
  if (addWithInfoExpressions.size()) {
@@ -91,4 +111,4 @@ export default function transformer(file, api) {
91
111
  }
92
112
 
93
113
  return root.toSource();
94
- }
114
+ }
@@ -1,6 +1,6 @@
1
- export var packageNames = {
1
+ export const packageNames = {
2
2
  '@kadira/react-storybook-decorator-centered': '@storybook/addon-centered',
3
- '@kadira/storybook-addons': '@storybook/addons',
3
+ '@kadira/storybook-addons': '@storybook/preview-api',
4
4
  '@kadira/storybook-addon-actions': '@storybook/addon-actions',
5
5
  '@kadira/storybook-addon-comments': '@storybook/addon-comments',
6
6
  '@kadira/storybook-addon-graphql': '@storybook/addon-graphql',
@@ -12,28 +12,27 @@ export var packageNames = {
12
12
  '@kadira/storybook-channels': '@storybook/channels',
13
13
  '@kadira/storybook-channel-postmsg': '@storybook/channel-postmessage',
14
14
  '@kadira/storybook-channel-websocket': '@storybook/channel-websocket',
15
- '@kadira/storybook-ui': '@storybook/ui',
15
+ '@kadira/storybook-ui': '@storybook/manager',
16
16
  '@kadira/react-native-storybook': '@storybook/react-native',
17
17
  '@kadira/react-storybook': '@storybook/react',
18
18
  '@kadira/getstorybook': '@storybook/cli',
19
19
  '@kadira/storybook': '@storybook/react',
20
20
  storyshots: '@storybook/addon-storyshots',
21
- getstorybook: '@storybook/cli'
21
+ getstorybook: '@storybook/cli',
22
22
  };
23
+
23
24
  export default function transformer(file, api) {
24
- var j = api.jscodeshift;
25
- var packageNamesKeys = Object.keys(packageNames);
25
+ const j = api.jscodeshift;
26
+
27
+ const packageNamesKeys = Object.keys(packageNames);
28
+
26
29
  /**
27
30
  * Checks whether the node value matches a Storybook package
28
31
  * @param {string} the import declaration node
29
32
  * @returns {string} whether the node value matches a Storybook package
30
33
  */
34
+ const getMatch = (oldpart) => packageNamesKeys.find((newpart) => oldpart.match(newpart));
31
35
 
32
- var getMatch = function (oldpart) {
33
- return packageNamesKeys.find(function (newpart) {
34
- return oldpart.match(newpart);
35
- });
36
- };
37
36
  /**
38
37
  * Returns the name of the Storybook packages with the organisation name,
39
38
  * replacing the old `@kadira/` prefix.
@@ -43,32 +42,30 @@ export default function transformer(file, api) {
43
42
  * // returns '@storybook/storybook'
44
43
  * getNewPackageName('@kadira/storybook')
45
44
  */
46
-
47
-
48
- var getNewPackageName = function (oldPackageName) {
49
- var match = getMatch(oldPackageName);
45
+ const getNewPackageName = (oldPackageName) => {
46
+ const match = getMatch(oldPackageName);
50
47
 
51
48
  if (match) {
52
- var replacement = packageNames[match];
49
+ const replacement = packageNames[match];
53
50
  return oldPackageName.replace(match, replacement);
54
51
  }
55
-
56
52
  return oldPackageName;
57
53
  };
54
+
58
55
  /**
59
56
  * updatePackageName - updates the source name of the Storybook packages
60
57
  * @param {ImportDeclaration} declaration the import declaration
61
58
  * @returns {ImportDeclaration.Node} the import declaration node
62
59
  */
63
-
64
-
65
- var updatePackageName = function (declaration) {
60
+ const updatePackageName = (declaration) => {
66
61
  // eslint-disable-next-line no-param-reassign
67
62
  declaration.node.source.value = getNewPackageName(declaration.node.source.value);
63
+
68
64
  return declaration.node;
69
65
  };
70
66
 
71
- return j(file.source).find(j.ImportDeclaration).replaceWith(updatePackageName).toSource({
72
- quote: 'single'
73
- });
74
- }
67
+ return j(file.source)
68
+ .find(j.ImportDeclaration)
69
+ .replaceWith(updatePackageName)
70
+ .toSource({ quote: 'single' });
71
+ }