@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.
- package/README.md +0 -36
- package/dist/index.d.ts +261 -0
- package/dist/index.js +1 -0
- package/dist/index.mjs +1 -0
- package/jest.config.js +7 -0
- package/package.json +31 -28
- package/src/index.js +77 -0
- package/src/lib/utils.test.js +9 -0
- package/{dist/esm/lib/utils.js → src/lib/utils.ts} +10 -11
- package/src/transforms/__testfixtures__/add-component-parameters/add-component-parameters.input.js +44 -0
- package/src/transforms/__testfixtures__/add-component-parameters/add-component-parameters.output.snapshot +68 -0
- package/src/transforms/__testfixtures__/csf-hoist-story-annotations/basic.input.js +25 -0
- package/src/transforms/__testfixtures__/csf-hoist-story-annotations/basic.output.snapshot +27 -0
- package/src/transforms/__testfixtures__/csf-hoist-story-annotations/overrides.input.js +25 -0
- package/src/transforms/__testfixtures__/csf-hoist-story-annotations/overrides.output.snapshot +28 -0
- package/src/transforms/__testfixtures__/csf-hoist-story-annotations/variable.input.js +13 -0
- package/src/transforms/__testfixtures__/csf-hoist-story-annotations/variable.output.snapshot +17 -0
- package/src/transforms/__testfixtures__/csf-to-mdx/basic.input.js +20 -0
- package/src/transforms/__testfixtures__/csf-to-mdx/basic.output.snapshot +18 -0
- package/src/transforms/__testfixtures__/csf-to-mdx/component-id.input.js +9 -0
- package/src/transforms/__testfixtures__/csf-to-mdx/component-id.output.snapshot +10 -0
- package/src/transforms/__testfixtures__/csf-to-mdx/decorators.input.js +13 -0
- package/src/transforms/__testfixtures__/csf-to-mdx/decorators.output.snapshot +12 -0
- package/src/transforms/__testfixtures__/csf-to-mdx/exclude-stories.input.js +23 -0
- package/src/transforms/__testfixtures__/csf-to-mdx/exclude-stories.output.snapshot +22 -0
- package/src/transforms/__testfixtures__/csf-to-mdx/parameters.input.js +16 -0
- package/src/transforms/__testfixtures__/csf-to-mdx/parameters.output.snapshot +17 -0
- package/src/transforms/__testfixtures__/csf-to-mdx/story-function.input.js +19 -0
- package/src/transforms/__testfixtures__/csf-to-mdx/story-function.output.snapshot +18 -0
- package/src/transforms/__testfixtures__/csf-to-mdx/story-parameters.input.js +24 -0
- package/src/transforms/__testfixtures__/csf-to-mdx/story-parameters.output.snapshot +22 -0
- package/src/transforms/__testfixtures__/mdx-to-csf/basic.input.js +18 -0
- package/src/transforms/__testfixtures__/mdx-to-csf/basic.output.snapshot +40 -0
- package/src/transforms/__testfixtures__/mdx-to-csf/component-id.input.js +6 -0
- package/src/transforms/__testfixtures__/mdx-to-csf/component-id.output.snapshot +17 -0
- package/src/transforms/__testfixtures__/mdx-to-csf/decorators.input.js +8 -0
- package/src/transforms/__testfixtures__/mdx-to-csf/decorators.output.snapshot +18 -0
- package/src/transforms/__testfixtures__/mdx-to-csf/exclude-stories.input.js +19 -0
- package/src/transforms/__testfixtures__/mdx-to-csf/exclude-stories.output.snapshot +39 -0
- package/src/transforms/__testfixtures__/mdx-to-csf/parameters.input.js +14 -0
- package/src/transforms/__testfixtures__/mdx-to-csf/parameters.output.snapshot +23 -0
- package/src/transforms/__testfixtures__/mdx-to-csf/plaintext.input.js +3 -0
- package/src/transforms/__testfixtures__/mdx-to-csf/plaintext.output.snapshot +11 -0
- package/src/transforms/__testfixtures__/mdx-to-csf/story-function.input.js +10 -0
- package/src/transforms/__testfixtures__/mdx-to-csf/story-function.output.snapshot +18 -0
- package/src/transforms/__testfixtures__/mdx-to-csf/story-parameters.input.js +18 -0
- package/src/transforms/__testfixtures__/mdx-to-csf/story-parameters.output.snapshot +32 -0
- package/src/transforms/__testfixtures__/mdx-to-csf/story-refs.input.js +22 -0
- package/src/transforms/__testfixtures__/mdx-to-csf/story-refs.output.snapshot +34 -0
- package/src/transforms/__testfixtures__/move-builtin-addons/default.input.js +2 -0
- package/src/transforms/__testfixtures__/move-builtin-addons/default.output.snapshot +8 -0
- package/src/transforms/__testfixtures__/move-builtin-addons/with-no-change.input.js +3 -0
- package/src/transforms/__testfixtures__/move-builtin-addons/with-no-change.output.snapshot +7 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/basic.input.js +18 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/basic.output.snapshot +45 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/collision.input.js +11 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/collision.output.snapshot +38 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/const.input.js +1 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/const.output.snapshot +13 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/decorators.input.js +9 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/decorators.output.snapshot +18 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/default.input.js +7 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/default.output.snapshot +17 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/digit.input.js +1 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/digit.output.js +5 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/digit.output.snapshot +9 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/export-destructuring.input.js +11 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/export-destructuring.output.snapshot +23 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/export-function.input.js +12 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/export-function.output.snapshot +23 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/export-names.input.js +14 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/export-names.output.snapshot +26 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/exports.input.js +2 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/exports.output.snapshot +16 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/module.input.js +12 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/module.output.snapshot +16 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/multi.input.js +14 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/multi.output.snapshot +39 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/parameters-as-var.input.js +8 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/parameters-as-var.output.snapshot +20 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/parameters.input.js +10 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/parameters.output.snapshot +23 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/storiesof-var.input.js +11 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/storiesof-var.output.snapshot +23 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/story-decorators.input.js +11 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/story-decorators.output.snapshot +29 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/story-parameters.input.js +14 -0
- package/src/transforms/__testfixtures__/storiesof-to-csf/story-parameters.output.snapshot +32 -0
- package/src/transforms/__testfixtures__/update-addon-info/update-addon-info.input.js +198 -0
- package/src/transforms/__testfixtures__/update-addon-info/update-addon-info.output.snapshot +198 -0
- package/src/transforms/__testfixtures__/update-organisation-name/update-organisation-name.input.js +19 -0
- package/src/transforms/__testfixtures__/update-organisation-name/update-organisation-name.output.snapshot +23 -0
- package/src/transforms/__testfixtures__/upgrade-hierarchy-separators/csf.input.js +3 -0
- package/src/transforms/__testfixtures__/upgrade-hierarchy-separators/csf.output.snapshot +7 -0
- package/src/transforms/__testfixtures__/upgrade-hierarchy-separators/dynamic-storiesof.input.js +5 -0
- package/src/transforms/__testfixtures__/upgrade-hierarchy-separators/dynamic-storiesof.output.snapshot +9 -0
- package/src/transforms/__testfixtures__/upgrade-hierarchy-separators/storiesof.input.js +8 -0
- package/src/transforms/__testfixtures__/upgrade-hierarchy-separators/storiesof.output.snapshot +12 -0
- package/src/transforms/__tests__/csf-2-to-3.test.ts +250 -0
- package/src/transforms/__tests__/transforms.tests.js +32 -0
- package/{dist/esm → src}/transforms/add-component-parameters.js +26 -21
- package/src/transforms/csf-2-to-3.ts +184 -0
- package/src/transforms/csf-hoist-story-annotations.js +97 -0
- package/src/transforms/csf-to-mdx.js +190 -0
- package/src/transforms/move-builtin-addons.js +32 -0
- package/src/transforms/storiesof-to-csf.js +277 -0
- package/{dist/esm → src}/transforms/update-addon-info.js +44 -24
- package/{dist/esm → src}/transforms/update-organisation-name.js +20 -23
- package/src/transforms/upgrade-hierarchy-separators.js +39 -0
- package/tsconfig.json +7 -0
- package/LICENSE +0 -21
- package/dist/cjs/index.js +0 -142
- package/dist/cjs/lib/utils.js +0 -45
- package/dist/cjs/transforms/add-component-parameters.js +0 -64
- package/dist/cjs/transforms/csf-2-to-3.js +0 -180
- package/dist/cjs/transforms/csf-hoist-story-annotations.js +0 -93
- package/dist/cjs/transforms/csf-to-mdx.js +0 -196
- package/dist/cjs/transforms/mdx-to-csf.js +0 -153
- package/dist/cjs/transforms/move-builtin-addons.js +0 -57
- package/dist/cjs/transforms/storiesof-to-csf.js +0 -300
- package/dist/cjs/transforms/update-addon-info.js +0 -101
- package/dist/cjs/transforms/update-organisation-name.js +0 -83
- package/dist/cjs/transforms/upgrade-hierarchy-separators.js +0 -42
- package/dist/esm/index.js +0 -99
- package/dist/esm/transforms/csf-2-to-3.js +0 -163
- package/dist/esm/transforms/csf-hoist-story-annotations.js +0 -86
- package/dist/esm/transforms/csf-to-mdx.js +0 -188
- package/dist/esm/transforms/mdx-to-csf.js +0 -139
- package/dist/esm/transforms/move-builtin-addons.js +0 -50
- package/dist/esm/transforms/storiesof-to-csf.js +0 -287
- package/dist/esm/transforms/upgrade-hierarchy-separators.js +0 -35
- package/dist/types/lib/utils.d.ts +0 -2
- 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
|
-
|
|
27
|
-
|
|
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
|
-
|
|
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 = [
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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/
|
|
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
|
-
|
|
25
|
-
|
|
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
|
-
|
|
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)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
67
|
+
return j(file.source)
|
|
68
|
+
.find(j.ImportDeclaration)
|
|
69
|
+
.replaceWith(updatePackageName)
|
|
70
|
+
.toSource({ quote: 'single' });
|
|
71
|
+
}
|