@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,250 @@
|
|
|
1
|
+
import { describe, it, expect } from '@jest/globals';
|
|
2
|
+
import { dedent } from 'ts-dedent';
|
|
3
|
+
import _transform from '../csf-2-to-3';
|
|
4
|
+
|
|
5
|
+
expect.addSnapshotSerializer({
|
|
6
|
+
print: (val: any) => val,
|
|
7
|
+
test: () => true,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const jsTransform = (source: string) => _transform({ source }, null, {}).trim();
|
|
11
|
+
const tsTransform = (source: string) => _transform({ source }, null, { parser: 'tsx' }).trim();
|
|
12
|
+
|
|
13
|
+
describe('csf-2-to-3', () => {
|
|
14
|
+
describe('javascript', () => {
|
|
15
|
+
it('should replace non-simple function exports with objects', () => {
|
|
16
|
+
expect(
|
|
17
|
+
jsTransform(dedent`
|
|
18
|
+
export default { title: 'Cat' };
|
|
19
|
+
export const A = () => <Cat />;
|
|
20
|
+
export const B = (args) => <Button {...args} />;
|
|
21
|
+
`)
|
|
22
|
+
).toMatchInlineSnapshot(`
|
|
23
|
+
export default {
|
|
24
|
+
title: 'Cat',
|
|
25
|
+
};
|
|
26
|
+
export const A = () => <Cat />;
|
|
27
|
+
export const B = {
|
|
28
|
+
render: (args) => <Button {...args} />,
|
|
29
|
+
};
|
|
30
|
+
`);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should move annotations into story objects', () => {
|
|
34
|
+
expect(
|
|
35
|
+
jsTransform(dedent`
|
|
36
|
+
export default { title: 'Cat' };
|
|
37
|
+
export const A = () => <Cat />;
|
|
38
|
+
A.storyName = 'foo';
|
|
39
|
+
A.parameters = { bar: 2 };
|
|
40
|
+
A.play = () => {};
|
|
41
|
+
`)
|
|
42
|
+
).toMatchInlineSnapshot(`
|
|
43
|
+
export default {
|
|
44
|
+
title: 'Cat',
|
|
45
|
+
};
|
|
46
|
+
export const A = {
|
|
47
|
+
render: () => <Cat />,
|
|
48
|
+
name: 'foo',
|
|
49
|
+
parameters: {
|
|
50
|
+
bar: 2,
|
|
51
|
+
},
|
|
52
|
+
play: () => {},
|
|
53
|
+
};
|
|
54
|
+
`);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should ignore non-story exports, statements', () => {
|
|
58
|
+
expect(
|
|
59
|
+
jsTransform(dedent`
|
|
60
|
+
export default { title: 'components/Fruit', includeStories: ['A'] };
|
|
61
|
+
export const A = (args) => <Apple {...args} />;
|
|
62
|
+
export const B = (args) => <Banana {...args} />;
|
|
63
|
+
const C = (args) => <Cherry {...args} />;
|
|
64
|
+
`)
|
|
65
|
+
).toMatchInlineSnapshot(`
|
|
66
|
+
export default {
|
|
67
|
+
title: 'components/Fruit',
|
|
68
|
+
includeStories: ['A'],
|
|
69
|
+
};
|
|
70
|
+
export const A = {
|
|
71
|
+
render: (args) => <Apple {...args} />,
|
|
72
|
+
};
|
|
73
|
+
export const B = (args) => <Banana {...args} />;
|
|
74
|
+
const C = (args) => <Cherry {...args} />;
|
|
75
|
+
`);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should do nothing when there is no meta', () => {
|
|
79
|
+
expect(
|
|
80
|
+
jsTransform(dedent`
|
|
81
|
+
export const A = () => <Apple />;
|
|
82
|
+
export const B = (args) => <Banana {...args} />;
|
|
83
|
+
`)
|
|
84
|
+
).toMatchInlineSnapshot(`
|
|
85
|
+
export const A = () => <Apple />;
|
|
86
|
+
export const B = (args) => <Banana {...args} />;
|
|
87
|
+
`);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should remove implicit global render function (react)', () => {
|
|
91
|
+
expect(
|
|
92
|
+
jsTransform(dedent`
|
|
93
|
+
export default { title: 'Cat', component: Cat };
|
|
94
|
+
export const A = (args) => <Cat {...args} />;
|
|
95
|
+
export const B = (args) => <Banana {...args} />;
|
|
96
|
+
`)
|
|
97
|
+
).toMatchInlineSnapshot(`
|
|
98
|
+
export default {
|
|
99
|
+
title: 'Cat',
|
|
100
|
+
component: Cat,
|
|
101
|
+
};
|
|
102
|
+
export const A = {};
|
|
103
|
+
export const B = {
|
|
104
|
+
render: (args) => <Banana {...args} />,
|
|
105
|
+
};
|
|
106
|
+
`);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should ignore object exports', () => {
|
|
110
|
+
expect(
|
|
111
|
+
jsTransform(dedent`
|
|
112
|
+
export default { title: 'Cat', component: Cat };
|
|
113
|
+
export const A = {
|
|
114
|
+
render: (args) => <Cat {...args} />
|
|
115
|
+
};
|
|
116
|
+
`)
|
|
117
|
+
).toMatchInlineSnapshot(`
|
|
118
|
+
export default {
|
|
119
|
+
title: 'Cat',
|
|
120
|
+
component: Cat,
|
|
121
|
+
};
|
|
122
|
+
export const A = {
|
|
123
|
+
render: (args) => <Cat {...args} />,
|
|
124
|
+
};
|
|
125
|
+
`);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should hoist template.bind (if there is only one)', () => {
|
|
129
|
+
expect(
|
|
130
|
+
jsTransform(dedent`
|
|
131
|
+
export default { title: 'Cat' };
|
|
132
|
+
const Template = (args) => <Cat {...args} />;
|
|
133
|
+
export const A = Template.bind({});
|
|
134
|
+
A.args = { isPrimary: false };
|
|
135
|
+
`)
|
|
136
|
+
).toMatchInlineSnapshot(`
|
|
137
|
+
export default {
|
|
138
|
+
title: 'Cat',
|
|
139
|
+
};
|
|
140
|
+
export const A = {
|
|
141
|
+
render: (args) => <Cat {...args} />,
|
|
142
|
+
args: {
|
|
143
|
+
isPrimary: false,
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
`);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should remove implicit global render for template.bind', () => {
|
|
150
|
+
expect(
|
|
151
|
+
jsTransform(dedent`
|
|
152
|
+
export default { title: 'Cat', component: Cat };
|
|
153
|
+
const Template = (args) => <Cat {...args} />;
|
|
154
|
+
export const A = Template.bind({});
|
|
155
|
+
A.args = { isPrimary: false };
|
|
156
|
+
const Template2 = (args) => <Banana {...args} />;
|
|
157
|
+
export const B = Template2.bind({});
|
|
158
|
+
B.args = { isPrimary: true };
|
|
159
|
+
`)
|
|
160
|
+
).toMatchInlineSnapshot(`
|
|
161
|
+
export default {
|
|
162
|
+
title: 'Cat',
|
|
163
|
+
component: Cat,
|
|
164
|
+
};
|
|
165
|
+
export const A = {
|
|
166
|
+
args: {
|
|
167
|
+
isPrimary: false,
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
export const B = {
|
|
171
|
+
render: (args) => <Banana {...args} />,
|
|
172
|
+
args: {
|
|
173
|
+
isPrimary: true,
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
`);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should ignore no-arg stories without annotations', () => {
|
|
180
|
+
expect(
|
|
181
|
+
jsTransform(dedent`
|
|
182
|
+
export default { title: 'Cat', component: Cat };
|
|
183
|
+
export const A = (args) => <Cat {...args} />;
|
|
184
|
+
export const B = () => <Cat name="frisky" />;
|
|
185
|
+
export const C = () => <Cat name="fluffy" />;
|
|
186
|
+
C.parameters = { foo: 2 };
|
|
187
|
+
`)
|
|
188
|
+
).toMatchInlineSnapshot(`
|
|
189
|
+
export default {
|
|
190
|
+
title: 'Cat',
|
|
191
|
+
component: Cat,
|
|
192
|
+
};
|
|
193
|
+
export const A = {};
|
|
194
|
+
export const B = () => <Cat name="frisky" />;
|
|
195
|
+
export const C = {
|
|
196
|
+
render: () => <Cat name="fluffy" />,
|
|
197
|
+
parameters: {
|
|
198
|
+
foo: 2,
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
`);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('should work for v1-style annotations', () => {
|
|
205
|
+
expect(
|
|
206
|
+
jsTransform(dedent`
|
|
207
|
+
export default { title: 'Cat' };
|
|
208
|
+
export const A = (args) => <Cat {...args} />;
|
|
209
|
+
A.story = {
|
|
210
|
+
parameters: { foo: 2 }
|
|
211
|
+
};
|
|
212
|
+
`)
|
|
213
|
+
).toMatchInlineSnapshot(`
|
|
214
|
+
export default {
|
|
215
|
+
title: 'Cat',
|
|
216
|
+
};
|
|
217
|
+
export const A = {
|
|
218
|
+
render: (args) => <Cat {...args} />,
|
|
219
|
+
parameters: {
|
|
220
|
+
foo: 2,
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
`);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe('typescript', () => {
|
|
228
|
+
it('should replace function exports with objects', () => {
|
|
229
|
+
expect(
|
|
230
|
+
tsTransform(dedent`
|
|
231
|
+
export default { title: 'Cat' } as Meta<CatProps>;
|
|
232
|
+
export const A: Story<CatProps> = (args) => <Cat {...args} />;
|
|
233
|
+
export const B: any = (args) => <Button {...args} />;
|
|
234
|
+
export const C: Story<CatProps> = () => <Cat />;
|
|
235
|
+
`)
|
|
236
|
+
).toMatchInlineSnapshot(`
|
|
237
|
+
export default {
|
|
238
|
+
title: 'Cat',
|
|
239
|
+
} as Meta<CatProps>;
|
|
240
|
+
export const A: Story<CatProps> = {
|
|
241
|
+
render: (args) => <Cat {...args} />,
|
|
242
|
+
};
|
|
243
|
+
export const B: any = {
|
|
244
|
+
render: (args) => <Button {...args} />,
|
|
245
|
+
};
|
|
246
|
+
export const C: Story<CatProps> = () => <Cat />;
|
|
247
|
+
`);
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import 'jest-specific-snapshot';
|
|
4
|
+
import { applyTransform } from 'jscodeshift/dist/testUtils';
|
|
5
|
+
|
|
6
|
+
jest.mock('@storybook/node-logger');
|
|
7
|
+
|
|
8
|
+
const inputRegExp = /\.input\.js$/;
|
|
9
|
+
|
|
10
|
+
const fixturesDir = path.resolve(__dirname, '../__testfixtures__');
|
|
11
|
+
fs.readdirSync(fixturesDir).forEach((transformName) => {
|
|
12
|
+
// FIXME: delete after https://github.com/storybookjs/storybook/issues/19497
|
|
13
|
+
if (transformName === 'mdx-to-csf') return;
|
|
14
|
+
|
|
15
|
+
const transformFixturesDir = path.join(fixturesDir, transformName);
|
|
16
|
+
describe(`${transformName}`, () => {
|
|
17
|
+
fs.readdirSync(transformFixturesDir)
|
|
18
|
+
.filter((fileName) => inputRegExp.test(fileName))
|
|
19
|
+
.forEach((fileName) => {
|
|
20
|
+
const inputPath = path.join(transformFixturesDir, fileName);
|
|
21
|
+
it(`transforms correctly using "${fileName}" data`, () =>
|
|
22
|
+
expect(
|
|
23
|
+
applyTransform(
|
|
24
|
+
// eslint-disable-next-line global-require,import/no-dynamic-require
|
|
25
|
+
require(path.join(__dirname, '..', transformName)),
|
|
26
|
+
null,
|
|
27
|
+
{ path: inputPath, source: fs.readFileSync(inputPath, 'utf8') }
|
|
28
|
+
)
|
|
29
|
+
).toMatchSpecificSnapshot(inputPath.replace(inputRegExp, '.output.snapshot')));
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -18,40 +18,45 @@
|
|
|
18
18
|
* - Button must be imported in the file
|
|
19
19
|
*/
|
|
20
20
|
export default function transformer(file, api) {
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
const j = api.jscodeshift;
|
|
22
|
+
const root = j(file.source);
|
|
23
|
+
|
|
24
|
+
// Create a dictionary whose keys are all the named imports in the file.
|
|
23
25
|
// For instance:
|
|
24
26
|
//
|
|
25
27
|
// import foo from 'foo';
|
|
26
28
|
// import { bar, baz } from 'zoo';
|
|
27
29
|
//
|
|
28
30
|
// => { foo: true, bar: true, baz: true }
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return imp.node.specifiers.forEach(function (spec) {
|
|
31
|
+
const importMap = {};
|
|
32
|
+
root.find(j.ImportDeclaration).forEach((imp) =>
|
|
33
|
+
imp.node.specifiers.forEach((spec) => {
|
|
33
34
|
importMap[spec.local.name] = true;
|
|
34
|
-
})
|
|
35
|
-
|
|
35
|
+
})
|
|
36
|
+
);
|
|
36
37
|
|
|
37
38
|
function getLeafName(string) {
|
|
38
|
-
|
|
39
|
+
const parts = string.split(/\/|\.|\|/);
|
|
39
40
|
return parts[parts.length - 1];
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
function addComponentParameter(call) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return j.callExpression(j.memberExpression(node, j.identifier('addParameters')), [
|
|
44
|
+
const { node } = call;
|
|
45
|
+
const leafName = getLeafName(node.arguments[0].value);
|
|
46
|
+
return j.callExpression(j.memberExpression(node, j.identifier('addParameters')), [
|
|
47
|
+
j.objectExpression([j.property('init', j.identifier('component'), j.identifier(leafName))]),
|
|
48
|
+
]);
|
|
46
49
|
}
|
|
47
50
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
root
|
|
52
|
+
.find(j.CallExpression)
|
|
53
|
+
.filter((call) => call.node.callee.name === 'storiesOf')
|
|
54
|
+
.filter((call) => call.node.arguments.length > 0 && call.node.arguments[0].type === 'Literal')
|
|
55
|
+
.filter((call) => {
|
|
56
|
+
const leafName = getLeafName(call.node.arguments[0].value);
|
|
57
|
+
return importMap[leafName];
|
|
58
|
+
})
|
|
59
|
+
.replaceWith(addComponentParameter);
|
|
60
|
+
|
|
56
61
|
return root.toSource();
|
|
57
|
-
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/* eslint-disable no-underscore-dangle */
|
|
2
|
+
import prettier from 'prettier';
|
|
3
|
+
import * as t from '@babel/types';
|
|
4
|
+
import type { CsfFile } from '@storybook/csf-tools';
|
|
5
|
+
import { formatCsf, loadCsf } from '@storybook/csf-tools';
|
|
6
|
+
import { jscodeshiftToPrettierParser } from '../lib/utils';
|
|
7
|
+
|
|
8
|
+
const logger = console;
|
|
9
|
+
|
|
10
|
+
const renameAnnotation = (annotation: string) => {
|
|
11
|
+
return annotation === 'storyName' ? 'name' : annotation;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const getTemplateBindVariable = (init: t.Expression) =>
|
|
15
|
+
t.isCallExpression(init) &&
|
|
16
|
+
t.isMemberExpression(init.callee) &&
|
|
17
|
+
t.isIdentifier(init.callee.object) &&
|
|
18
|
+
t.isIdentifier(init.callee.property) &&
|
|
19
|
+
init.callee.property.name === 'bind' &&
|
|
20
|
+
(init.arguments.length === 0 ||
|
|
21
|
+
(init.arguments.length === 1 &&
|
|
22
|
+
t.isObjectExpression(init.arguments[0]) &&
|
|
23
|
+
init.arguments[0].properties.length === 0))
|
|
24
|
+
? init.callee.object.name
|
|
25
|
+
: null;
|
|
26
|
+
|
|
27
|
+
// export const A = ...
|
|
28
|
+
// A.parameters = { ... }; <===
|
|
29
|
+
const isStoryAnnotation = (stmt: t.Statement, objectExports: Record<string, any>) =>
|
|
30
|
+
t.isExpressionStatement(stmt) &&
|
|
31
|
+
t.isAssignmentExpression(stmt.expression) &&
|
|
32
|
+
t.isMemberExpression(stmt.expression.left) &&
|
|
33
|
+
t.isIdentifier(stmt.expression.left.object) &&
|
|
34
|
+
objectExports[stmt.expression.left.object.name];
|
|
35
|
+
|
|
36
|
+
const isTemplateDeclaration = (stmt: t.Statement, templates: Record<string, any>) =>
|
|
37
|
+
t.isVariableDeclaration(stmt) &&
|
|
38
|
+
stmt.declarations.length === 1 &&
|
|
39
|
+
t.isIdentifier(stmt.declarations[0].id) &&
|
|
40
|
+
templates[stmt.declarations[0].id.name];
|
|
41
|
+
|
|
42
|
+
const getNewExport = (stmt: t.Statement, objectExports: Record<string, any>) => {
|
|
43
|
+
if (
|
|
44
|
+
t.isExportNamedDeclaration(stmt) &&
|
|
45
|
+
t.isVariableDeclaration(stmt.declaration) &&
|
|
46
|
+
stmt.declaration.declarations.length === 1
|
|
47
|
+
) {
|
|
48
|
+
const decl = stmt.declaration.declarations[0];
|
|
49
|
+
if (t.isVariableDeclarator(decl) && t.isIdentifier(decl.id)) {
|
|
50
|
+
return objectExports[decl.id.name];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Remove render function when it matches the global render function in react
|
|
57
|
+
// export default { component: Cat };
|
|
58
|
+
// export const A = (args) => <Cat {...args} />;
|
|
59
|
+
const isReactGlobalRenderFn = (csf: CsfFile, storyFn: t.Expression) => {
|
|
60
|
+
if (
|
|
61
|
+
csf._meta?.component &&
|
|
62
|
+
t.isArrowFunctionExpression(storyFn) &&
|
|
63
|
+
storyFn.params.length === 1 &&
|
|
64
|
+
t.isJSXElement(storyFn.body)
|
|
65
|
+
) {
|
|
66
|
+
const { openingElement } = storyFn.body;
|
|
67
|
+
if (
|
|
68
|
+
openingElement.selfClosing &&
|
|
69
|
+
t.isJSXIdentifier(openingElement.name) &&
|
|
70
|
+
openingElement.attributes.length === 1
|
|
71
|
+
) {
|
|
72
|
+
const attr = openingElement.attributes[0];
|
|
73
|
+
const param = storyFn.params[0];
|
|
74
|
+
if (
|
|
75
|
+
t.isJSXSpreadAttribute(attr) &&
|
|
76
|
+
t.isIdentifier(attr.argument) &&
|
|
77
|
+
t.isIdentifier(param) &&
|
|
78
|
+
param.name === attr.argument.name &&
|
|
79
|
+
csf._meta.component === openingElement.name.name
|
|
80
|
+
) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// A simple CSF story is a no-arg story without any extra annotations (params, args, etc.)
|
|
89
|
+
const isSimpleCSFStory = (init: t.Expression, annotations: t.ObjectProperty[]) =>
|
|
90
|
+
annotations.length === 0 && t.isArrowFunctionExpression(init) && init.params.length === 0;
|
|
91
|
+
|
|
92
|
+
function transform({ source }: { source: string }, api: any, options: { parser?: string }) {
|
|
93
|
+
const makeTitle = (userTitle?: string) => {
|
|
94
|
+
return userTitle || 'FIXME';
|
|
95
|
+
};
|
|
96
|
+
const csf = loadCsf(source, { makeTitle });
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
csf.parse();
|
|
100
|
+
} catch (err) {
|
|
101
|
+
logger.log(`Error ${err}, skipping`);
|
|
102
|
+
return source;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const objectExports: Record<string, t.Statement> = {};
|
|
106
|
+
Object.entries(csf._storyExports).forEach(([key, decl]) => {
|
|
107
|
+
const annotations = Object.entries(csf._storyAnnotations[key]).map(([annotation, val]) => {
|
|
108
|
+
return t.objectProperty(t.identifier(renameAnnotation(annotation)), val as t.Expression);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
if (t.isVariableDeclarator(decl)) {
|
|
112
|
+
const { init, id } = decl;
|
|
113
|
+
// only replace arrow function expressions && template
|
|
114
|
+
// ignore no-arg stories without annotations
|
|
115
|
+
const template = getTemplateBindVariable(init);
|
|
116
|
+
if (
|
|
117
|
+
(!t.isArrowFunctionExpression(init) && !template) ||
|
|
118
|
+
isSimpleCSFStory(init, annotations)
|
|
119
|
+
) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Remove the render function when we can hoist the template
|
|
124
|
+
// const Template = (args) => <Cat {...args} />;
|
|
125
|
+
// export const A = Template.bind({});
|
|
126
|
+
let storyFn = template && csf._templates[template];
|
|
127
|
+
if (!storyFn) {
|
|
128
|
+
storyFn = init;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const keyId = t.identifier(key);
|
|
132
|
+
// @ts-expect-error (Converted from ts-ignore)
|
|
133
|
+
const { typeAnnotation } = id;
|
|
134
|
+
if (typeAnnotation) {
|
|
135
|
+
keyId.typeAnnotation = typeAnnotation;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const renderAnnotation = isReactGlobalRenderFn(csf, storyFn)
|
|
139
|
+
? []
|
|
140
|
+
: [t.objectProperty(t.identifier('render'), storyFn)];
|
|
141
|
+
|
|
142
|
+
objectExports[key] = t.exportNamedDeclaration(
|
|
143
|
+
t.variableDeclaration('const', [
|
|
144
|
+
t.variableDeclarator(keyId, t.objectExpression([...renderAnnotation, ...annotations])),
|
|
145
|
+
])
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const updatedBody = csf._ast.program.body.reduce((acc, stmt) => {
|
|
151
|
+
// remove story annotations & template declarations
|
|
152
|
+
if (isStoryAnnotation(stmt, objectExports) || isTemplateDeclaration(stmt, csf._templates)) {
|
|
153
|
+
return acc;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// replace story exports with new object exports
|
|
157
|
+
const newExport = getNewExport(stmt, objectExports);
|
|
158
|
+
if (newExport) {
|
|
159
|
+
acc.push(newExport);
|
|
160
|
+
return acc;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// include unknown statements
|
|
164
|
+
acc.push(stmt);
|
|
165
|
+
return acc;
|
|
166
|
+
}, []);
|
|
167
|
+
csf._ast.program.body = updatedBody;
|
|
168
|
+
const output = formatCsf(csf);
|
|
169
|
+
|
|
170
|
+
const prettierConfig = prettier.resolveConfig.sync('.', { editorconfig: true }) || {
|
|
171
|
+
printWidth: 100,
|
|
172
|
+
tabWidth: 2,
|
|
173
|
+
bracketSpacing: true,
|
|
174
|
+
trailingComma: 'es5',
|
|
175
|
+
singleQuote: true,
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
return prettier.format(output, {
|
|
179
|
+
...prettierConfig,
|
|
180
|
+
parser: jscodeshiftToPrettierParser(options?.parser),
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export default transform;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const getContainingStatement = (n) => {
|
|
2
|
+
if (n.node.type.endsWith('Statement')) {
|
|
3
|
+
return n;
|
|
4
|
+
}
|
|
5
|
+
return getContainingStatement(n.parent);
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Hoist CSF .story annotations
|
|
10
|
+
*
|
|
11
|
+
* For example:
|
|
12
|
+
*
|
|
13
|
+
* ```
|
|
14
|
+
* export const Basic = () => <Button />
|
|
15
|
+
* Basic.story = {
|
|
16
|
+
* name: 'foo',
|
|
17
|
+
* parameters: { ... },
|
|
18
|
+
* decorators = [ ... ],
|
|
19
|
+
* };
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* Becomes:
|
|
23
|
+
*
|
|
24
|
+
* ```
|
|
25
|
+
* export const Basic = () => <Button />
|
|
26
|
+
* Basic.storyName = 'foo';
|
|
27
|
+
* Basic.parameters = { ... };
|
|
28
|
+
* Basic.decorators = [ ... ];
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export default function transformer(file, api) {
|
|
32
|
+
const j = api.jscodeshift;
|
|
33
|
+
const root = j(file.source);
|
|
34
|
+
|
|
35
|
+
const renameKey = (exp) =>
|
|
36
|
+
exp.type === 'Identifier' && exp.name === 'name' ? j.identifier('storyName') : exp;
|
|
37
|
+
|
|
38
|
+
// 1. If the program does not have `export default { title: '....' }, skip it
|
|
39
|
+
const defaultExportWithTitle = root
|
|
40
|
+
.find(j.ExportDefaultDeclaration)
|
|
41
|
+
.filter(
|
|
42
|
+
(def) =>
|
|
43
|
+
def.node.declaration.type === 'ObjectExpression' &&
|
|
44
|
+
def.node.declaration.properties.map((p) => p.key.name).includes('title')
|
|
45
|
+
);
|
|
46
|
+
if (defaultExportWithTitle.size() === 0) {
|
|
47
|
+
return root.toSource();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 2. Replace each Foo.story = { x: xVal } with Foo.x = xVal;
|
|
51
|
+
const storyAssignments = root.find(j.AssignmentExpression).filter((exp) => {
|
|
52
|
+
const { left, right } = exp.node;
|
|
53
|
+
return (
|
|
54
|
+
left.type === 'MemberExpression' &&
|
|
55
|
+
left.object.type === 'Identifier' &&
|
|
56
|
+
left.property.type === 'Identifier' &&
|
|
57
|
+
left.property.name === 'story' &&
|
|
58
|
+
right.type === 'ObjectExpression'
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
storyAssignments.forEach((exp) => {
|
|
62
|
+
const { left, right } = exp.node;
|
|
63
|
+
right.properties.forEach((prop) => {
|
|
64
|
+
const stmt = getContainingStatement(exp);
|
|
65
|
+
stmt.insertBefore(
|
|
66
|
+
j.assignmentStatement('=', j.memberExpression(left.object, renameKey(prop.key)), prop.value)
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// 3. Remove the .story annotations
|
|
72
|
+
storyAssignments.remove();
|
|
73
|
+
|
|
74
|
+
// 4. Replace each Foo.story.x with Foo.x;
|
|
75
|
+
const storyOverrides = root.find(j.AssignmentExpression).filter((exp) => {
|
|
76
|
+
const { left } = exp.node;
|
|
77
|
+
return (
|
|
78
|
+
left.type === 'MemberExpression' &&
|
|
79
|
+
left.object.type === 'MemberExpression' &&
|
|
80
|
+
left.object.property.type === 'Identifier' &&
|
|
81
|
+
left.object.property.name === 'story' &&
|
|
82
|
+
left.property.type === 'Identifier'
|
|
83
|
+
// ?? ANNOTATION_FIELDS.includes(right.property.name)
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
storyOverrides.replaceWith((exp) => {
|
|
87
|
+
const { left, right } = exp.node;
|
|
88
|
+
return j.assignmentExpression(
|
|
89
|
+
'=',
|
|
90
|
+
j.memberExpression(left.object.object, renameKey(left.property)),
|
|
91
|
+
right
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// 4. Render the updated tree
|
|
96
|
+
return root.toSource({ quote: 'single' });
|
|
97
|
+
}
|