@storybook/codemod 7.0.0-alpha.5 → 7.0.0-alpha.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +31 -28
  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 +20 -23
  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,4 +1,4 @@
1
- export var packageNames = {
1
+ export const packageNames = {
2
2
  '@kadira/react-storybook-decorator-centered': '@storybook/addon-centered',
3
3
  '@kadira/storybook-addons': '@storybook/addons',
4
4
  '@kadira/storybook-addon-actions': '@storybook/addon-actions',
@@ -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
+ }