@storybook/codemod 7.0.0-beta.5 → 7.0.0-beta.51
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 -39
- package/dist/chunk-HBPKIMKE.mjs +1 -0
- package/dist/chunk-YH46OF24.mjs +2 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/transforms/add-component-parameters.js +1 -1
- package/dist/transforms/csf-2-to-3.d.ts +5 -4
- package/dist/transforms/csf-2-to-3.js +3 -1
- package/dist/transforms/csf-2-to-3.mjs +2 -1
- package/dist/transforms/csf-hoist-story-annotations.js +1 -1
- package/dist/transforms/move-builtin-addons.js +1 -1
- package/dist/transforms/storiesof-to-csf.js +1 -1
- package/dist/transforms/storiesof-to-csf.mjs +1 -1
- package/dist/transforms/update-addon-info.js +1 -1
- package/dist/transforms/update-organisation-name.js +1 -1
- package/dist/transforms/upgrade-deprecated-types.d.ts +10 -0
- package/dist/transforms/upgrade-deprecated-types.js +2 -0
- package/dist/transforms/upgrade-deprecated-types.mjs +1 -0
- package/dist/transforms/upgrade-hierarchy-separators.js +1 -1
- package/jest.config.js +1 -0
- package/package.json +15 -10
- package/project.json +6 -0
- package/src/index.js +11 -1
- package/src/lib/utils.ts +2 -2
- package/src/transforms/__testfixtures__/storiesof-to-csf/decorators.input.js +1 -1
- package/src/transforms/__testfixtures__/storiesof-to-csf/export-function.input.js +1 -1
- package/src/transforms/__testfixtures__/storiesof-to-csf/story-decorators.input.js +1 -1
- package/src/transforms/__testfixtures__/update-addon-info/update-addon-info.input.js +34 -48
- package/src/transforms/__testfixtures__/update-addon-info/update-addon-info.output.snapshot +33 -47
- package/src/transforms/__tests__/csf-2-to-3.test.ts +175 -57
- package/src/transforms/__tests__/upgrade-deprecated-types.test.ts +170 -0
- package/src/transforms/csf-2-to-3.ts +161 -33
- package/src/transforms/upgrade-deprecated-types.ts +142 -0
- package/dist/chunk-CO6EPEMB.mjs +0 -1
- package/dist/transforms/csf-to-mdx.d.ts +0 -29
- package/dist/transforms/csf-to-mdx.js +0 -3
- package/dist/transforms/csf-to-mdx.mjs +0 -3
- package/src/transforms/__testfixtures__/csf-to-mdx/basic.input.js +0 -20
- package/src/transforms/__testfixtures__/csf-to-mdx/basic.output.snapshot +0 -18
- package/src/transforms/__testfixtures__/csf-to-mdx/component-id.input.js +0 -9
- package/src/transforms/__testfixtures__/csf-to-mdx/component-id.output.snapshot +0 -10
- package/src/transforms/__testfixtures__/csf-to-mdx/decorators.input.js +0 -13
- package/src/transforms/__testfixtures__/csf-to-mdx/decorators.output.snapshot +0 -12
- package/src/transforms/__testfixtures__/csf-to-mdx/exclude-stories.input.js +0 -23
- package/src/transforms/__testfixtures__/csf-to-mdx/exclude-stories.output.snapshot +0 -22
- package/src/transforms/__testfixtures__/csf-to-mdx/parameters.input.js +0 -16
- package/src/transforms/__testfixtures__/csf-to-mdx/parameters.output.snapshot +0 -17
- package/src/transforms/__testfixtures__/csf-to-mdx/story-function.input.js +0 -19
- package/src/transforms/__testfixtures__/csf-to-mdx/story-function.output.snapshot +0 -18
- package/src/transforms/__testfixtures__/csf-to-mdx/story-parameters.input.js +0 -24
- package/src/transforms/__testfixtures__/csf-to-mdx/story-parameters.output.snapshot +0 -22
- package/src/transforms/csf-to-mdx.js +0 -190
@@ -1,9 +1,14 @@
|
|
1
1
|
/* eslint-disable no-underscore-dangle */
|
2
2
|
import prettier from 'prettier';
|
3
3
|
import * as t from '@babel/types';
|
4
|
+
import { isIdentifier, isTSTypeAnnotation, isTSTypeReference } from '@babel/types';
|
4
5
|
import type { CsfFile } from '@storybook/csf-tools';
|
5
|
-
import {
|
6
|
-
import {
|
6
|
+
import { loadCsf } from '@storybook/csf-tools';
|
7
|
+
import type { API, FileInfo } from 'jscodeshift';
|
8
|
+
import type { BabelFile, NodePath } from '@babel/core';
|
9
|
+
import * as babel from '@babel/core';
|
10
|
+
import * as recast from 'recast';
|
11
|
+
import { upgradeDeprecatedTypes } from './upgrade-deprecated-types';
|
7
12
|
|
8
13
|
const logger = console;
|
9
14
|
|
@@ -89,19 +94,28 @@ const isReactGlobalRenderFn = (csf: CsfFile, storyFn: t.Expression) => {
|
|
89
94
|
const isSimpleCSFStory = (init: t.Expression, annotations: t.ObjectProperty[]) =>
|
90
95
|
annotations.length === 0 && t.isArrowFunctionExpression(init) && init.params.length === 0;
|
91
96
|
|
92
|
-
function transform(
|
97
|
+
export default function transform(info: FileInfo, api: API, options: { parser?: string }) {
|
93
98
|
const makeTitle = (userTitle?: string) => {
|
94
99
|
return userTitle || 'FIXME';
|
95
100
|
};
|
96
|
-
const csf = loadCsf(source, { makeTitle });
|
101
|
+
const csf = loadCsf(info.source, { makeTitle });
|
97
102
|
|
98
103
|
try {
|
99
104
|
csf.parse();
|
100
105
|
} catch (err) {
|
101
106
|
logger.log(`Error ${err}, skipping`);
|
102
|
-
return source;
|
107
|
+
return info.source;
|
103
108
|
}
|
104
109
|
|
110
|
+
// This allows for showing buildCodeFrameError messages
|
111
|
+
// @ts-expect-error File is not yet exposed, see https://github.com/babel/babel/issues/11350#issuecomment-644118606
|
112
|
+
const file: BabelFile = new babel.File(
|
113
|
+
{ filename: info.path },
|
114
|
+
{ code: info.source, ast: csf._ast }
|
115
|
+
);
|
116
|
+
|
117
|
+
const importHelper = new StorybookImportHelper(file, info);
|
118
|
+
|
105
119
|
const objectExports: Record<string, t.Statement> = {};
|
106
120
|
Object.entries(csf._storyExports).forEach(([key, decl]) => {
|
107
121
|
const annotations = Object.entries(csf._storyAnnotations[key]).map(([annotation, val]) => {
|
@@ -111,12 +125,15 @@ function transform({ source }: { source: string }, api: any, options: { parser?:
|
|
111
125
|
if (t.isVariableDeclarator(decl)) {
|
112
126
|
const { init, id } = decl;
|
113
127
|
// only replace arrow function expressions && template
|
114
|
-
// ignore no-arg stories without annotations
|
115
128
|
const template = getTemplateBindVariable(init);
|
116
|
-
if (
|
117
|
-
|
118
|
-
|
119
|
-
|
129
|
+
if (!t.isArrowFunctionExpression(init) && !template) return;
|
130
|
+
// Do change the type of no-arg stories without annotations to StoryFn when applicable
|
131
|
+
if (isSimpleCSFStory(init, annotations)) {
|
132
|
+
objectExports[key] = t.exportNamedDeclaration(
|
133
|
+
t.variableDeclaration('const', [
|
134
|
+
t.variableDeclarator(importHelper.updateTypeTo(id, 'StoryFn'), init),
|
135
|
+
])
|
136
|
+
);
|
120
137
|
return;
|
121
138
|
}
|
122
139
|
|
@@ -128,26 +145,24 @@ function transform({ source }: { source: string }, api: any, options: { parser?:
|
|
128
145
|
storyFn = init;
|
129
146
|
}
|
130
147
|
|
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
148
|
const renderAnnotation = isReactGlobalRenderFn(csf, storyFn)
|
139
149
|
? []
|
140
150
|
: [t.objectProperty(t.identifier('render'), storyFn)];
|
141
151
|
|
142
152
|
objectExports[key] = t.exportNamedDeclaration(
|
143
153
|
t.variableDeclaration('const', [
|
144
|
-
t.variableDeclarator(
|
154
|
+
t.variableDeclarator(
|
155
|
+
importHelper.updateTypeTo(id, 'StoryObj'),
|
156
|
+
t.objectExpression([...renderAnnotation, ...annotations])
|
157
|
+
),
|
145
158
|
])
|
146
159
|
);
|
147
160
|
}
|
148
161
|
});
|
149
162
|
|
150
|
-
|
163
|
+
importHelper.removeDeprecatedStoryImport();
|
164
|
+
|
165
|
+
csf._ast.program.body = csf._ast.program.body.reduce((acc, stmt) => {
|
151
166
|
// remove story annotations & template declarations
|
152
167
|
if (isStoryAnnotation(stmt, objectExports) || isTemplateDeclaration(stmt, csf._templates)) {
|
153
168
|
return acc;
|
@@ -164,21 +179,134 @@ function transform({ source }: { source: string }, api: any, options: { parser?:
|
|
164
179
|
acc.push(stmt);
|
165
180
|
return acc;
|
166
181
|
}, []);
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
182
|
+
|
183
|
+
upgradeDeprecatedTypes(file);
|
184
|
+
|
185
|
+
let output = recast.print(csf._ast, {}).code;
|
186
|
+
|
187
|
+
try {
|
188
|
+
const prettierConfig = prettier.resolveConfig.sync('.', { editorconfig: true }) || {
|
189
|
+
printWidth: 100,
|
190
|
+
tabWidth: 2,
|
191
|
+
bracketSpacing: true,
|
192
|
+
trailingComma: 'es5',
|
193
|
+
singleQuote: true,
|
194
|
+
};
|
195
|
+
|
196
|
+
output = prettier.format(output, {
|
197
|
+
...prettierConfig,
|
198
|
+
// This will infer the parser from the filename.
|
199
|
+
filepath: info.path,
|
200
|
+
});
|
201
|
+
} catch (e) {
|
202
|
+
logger.log(`Failed applying prettier to ${info.path}.`);
|
203
|
+
}
|
204
|
+
|
205
|
+
return output;
|
206
|
+
}
|
207
|
+
|
208
|
+
class StorybookImportHelper {
|
209
|
+
constructor(file: BabelFile, info: FileInfo) {
|
210
|
+
this.sbImportDeclarations = this.getAllSbImportDeclarations(file);
|
211
|
+
}
|
212
|
+
|
213
|
+
private sbImportDeclarations: NodePath<t.ImportDeclaration>[];
|
214
|
+
|
215
|
+
private getAllSbImportDeclarations = (file: BabelFile) => {
|
216
|
+
const found: NodePath<t.ImportDeclaration>[] = [];
|
217
|
+
|
218
|
+
file.path.traverse({
|
219
|
+
ImportDeclaration: (path) => {
|
220
|
+
const source = path.node.source.value;
|
221
|
+
if (source.startsWith('@storybook/csf') || !source.startsWith('@storybook')) return;
|
222
|
+
const isRendererImport = path.get('specifiers').some((specifier) => {
|
223
|
+
if (specifier.isImportNamespaceSpecifier()) {
|
224
|
+
throw path.buildCodeFrameError(
|
225
|
+
`This codemod does not support namespace imports for a ${path.node.source.value} package.\n` +
|
226
|
+
'Replace the namespace import with named imports and try again.'
|
227
|
+
);
|
228
|
+
}
|
229
|
+
if (!specifier.isImportSpecifier()) return false;
|
230
|
+
const imported = specifier.get('imported');
|
231
|
+
if (!imported.isIdentifier()) return false;
|
232
|
+
|
233
|
+
return [
|
234
|
+
'Story',
|
235
|
+
'StoryFn',
|
236
|
+
'StoryObj',
|
237
|
+
'Meta',
|
238
|
+
'ComponentStory',
|
239
|
+
'ComponentStoryFn',
|
240
|
+
'ComponentStoryObj',
|
241
|
+
'ComponentMeta',
|
242
|
+
].includes(imported.node.name);
|
243
|
+
});
|
244
|
+
|
245
|
+
if (isRendererImport) found.push(path);
|
246
|
+
},
|
247
|
+
});
|
248
|
+
return found;
|
176
249
|
};
|
177
250
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
251
|
+
getOrAddImport = (type: string): string | undefined => {
|
252
|
+
// prefer type import
|
253
|
+
const sbImport =
|
254
|
+
this.sbImportDeclarations.find((path) => path.node.importKind === 'type') ??
|
255
|
+
this.sbImportDeclarations[0];
|
256
|
+
if (sbImport == null) return undefined;
|
257
|
+
|
258
|
+
const specifiers = sbImport.get('specifiers');
|
259
|
+
const importSpecifier = specifiers.find((specifier) => {
|
260
|
+
if (!specifier.isImportSpecifier()) return false;
|
261
|
+
const imported = specifier.get('imported');
|
262
|
+
if (!imported.isIdentifier()) return false;
|
263
|
+
return imported.node.name === type;
|
264
|
+
});
|
265
|
+
if (importSpecifier) return importSpecifier.node.local.name;
|
266
|
+
specifiers[0].insertBefore(t.importSpecifier(t.identifier(type), t.identifier(type)));
|
267
|
+
return type;
|
268
|
+
};
|
269
|
+
|
270
|
+
removeDeprecatedStoryImport = () => {
|
271
|
+
const specifiers = this.sbImportDeclarations.flatMap((it) => it.get('specifiers'));
|
272
|
+
const storyImports = specifiers.filter((specifier) => {
|
273
|
+
if (!specifier.isImportSpecifier()) return false;
|
274
|
+
const imported = specifier.get('imported');
|
275
|
+
if (!imported.isIdentifier()) return false;
|
276
|
+
return imported.node.name === 'Story';
|
277
|
+
});
|
278
|
+
storyImports.forEach((path) => path.remove());
|
279
|
+
};
|
280
|
+
|
281
|
+
getAllLocalImports = () => {
|
282
|
+
return this.sbImportDeclarations
|
283
|
+
.flatMap((it) => it.get('specifiers'))
|
284
|
+
.map((it) => it.node.local.name);
|
285
|
+
};
|
286
|
+
|
287
|
+
updateTypeTo = (id: t.LVal, type: string): t.LVal => {
|
288
|
+
if (
|
289
|
+
isIdentifier(id) &&
|
290
|
+
isTSTypeAnnotation(id.typeAnnotation) &&
|
291
|
+
isTSTypeReference(id.typeAnnotation.typeAnnotation) &&
|
292
|
+
isIdentifier(id.typeAnnotation.typeAnnotation.typeName)
|
293
|
+
) {
|
294
|
+
const { name } = id.typeAnnotation.typeAnnotation.typeName;
|
295
|
+
if (this.getAllLocalImports().includes(name)) {
|
296
|
+
const localTypeImport = this.getOrAddImport(type);
|
297
|
+
return {
|
298
|
+
...id,
|
299
|
+
typeAnnotation: t.tsTypeAnnotation(
|
300
|
+
t.tsTypeReference(
|
301
|
+
t.identifier(localTypeImport),
|
302
|
+
id.typeAnnotation.typeAnnotation.typeParameters
|
303
|
+
)
|
304
|
+
),
|
305
|
+
};
|
306
|
+
}
|
307
|
+
}
|
308
|
+
return id;
|
309
|
+
};
|
182
310
|
}
|
183
311
|
|
184
|
-
export
|
312
|
+
export const parser = 'tsx';
|
@@ -0,0 +1,142 @@
|
|
1
|
+
/* eslint-disable no-underscore-dangle */
|
2
|
+
import prettier from 'prettier';
|
3
|
+
import type { API, FileInfo } from 'jscodeshift';
|
4
|
+
import type { BabelFile, NodePath } from '@babel/core';
|
5
|
+
import * as babel from '@babel/core';
|
6
|
+
import { loadCsf } from '@storybook/csf-tools';
|
7
|
+
import * as recast from 'recast';
|
8
|
+
import * as t from '@babel/types';
|
9
|
+
|
10
|
+
const logger = console;
|
11
|
+
|
12
|
+
const deprecatedTypes = [
|
13
|
+
'ComponentStory',
|
14
|
+
'ComponentStoryFn',
|
15
|
+
'ComponentStoryObj',
|
16
|
+
'ComponentMeta',
|
17
|
+
'Story',
|
18
|
+
];
|
19
|
+
|
20
|
+
function migrateType(oldType: string) {
|
21
|
+
if (oldType === 'Story' || oldType === 'ComponentStory') return 'StoryFn';
|
22
|
+
return oldType.replace('Component', '');
|
23
|
+
}
|
24
|
+
|
25
|
+
export default function transform(info: FileInfo, api: API, options: { parser?: string }) {
|
26
|
+
// TODO what do I need to with the title?
|
27
|
+
const fileNode = loadCsf(info.source, { makeTitle: (title) => title })._ast;
|
28
|
+
// @ts-expect-error File is not yet exposed, see https://github.com/babel/babel/issues/11350#issuecomment-644118606
|
29
|
+
const file: BabelFile = new babel.File(
|
30
|
+
{ filename: info.path },
|
31
|
+
{ code: info.source, ast: fileNode }
|
32
|
+
);
|
33
|
+
|
34
|
+
upgradeDeprecatedTypes(file);
|
35
|
+
|
36
|
+
let output = recast.print(file.path.node).code;
|
37
|
+
|
38
|
+
try {
|
39
|
+
const prettierConfig = prettier.resolveConfig.sync('.', { editorconfig: true }) || {
|
40
|
+
printWidth: 100,
|
41
|
+
tabWidth: 2,
|
42
|
+
bracketSpacing: true,
|
43
|
+
trailingComma: 'es5',
|
44
|
+
singleQuote: true,
|
45
|
+
};
|
46
|
+
|
47
|
+
output = prettier.format(output, { ...prettierConfig, filepath: info.path });
|
48
|
+
} catch (e) {
|
49
|
+
logger.log(`Failed applying prettier to ${info.path}.`);
|
50
|
+
}
|
51
|
+
|
52
|
+
return output;
|
53
|
+
}
|
54
|
+
|
55
|
+
export const parser = 'tsx';
|
56
|
+
|
57
|
+
export function upgradeDeprecatedTypes(file: BabelFile) {
|
58
|
+
const importedNamespaces: Set<string> = new Set();
|
59
|
+
const typeReferencesToUpdate: Set<string> = new Set();
|
60
|
+
const existingImports: { name: string; isAlias: boolean; path: NodePath }[] = [];
|
61
|
+
|
62
|
+
file.path.traverse({
|
63
|
+
ImportDeclaration: (path) => {
|
64
|
+
existingImports.push(
|
65
|
+
...path.get('specifiers').map((specifier) => ({
|
66
|
+
name: specifier.node.local.name,
|
67
|
+
isAlias: !(
|
68
|
+
specifier.isImportSpecifier() &&
|
69
|
+
t.isIdentifier(specifier.node.imported) &&
|
70
|
+
specifier.node.local.name === specifier.node.imported.name
|
71
|
+
),
|
72
|
+
path: specifier,
|
73
|
+
}))
|
74
|
+
);
|
75
|
+
|
76
|
+
const source = path.node.source.value;
|
77
|
+
if (!source.startsWith('@storybook')) return;
|
78
|
+
|
79
|
+
path.get('specifiers').forEach((specifier) => {
|
80
|
+
if (specifier.isImportNamespaceSpecifier()) {
|
81
|
+
importedNamespaces.add(specifier.node.local.name);
|
82
|
+
}
|
83
|
+
if (!specifier.isImportSpecifier()) return;
|
84
|
+
const imported = specifier.get('imported');
|
85
|
+
if (!imported.isIdentifier()) return;
|
86
|
+
|
87
|
+
// if we find a deprecated import
|
88
|
+
if (deprecatedTypes.includes(imported.node.name)) {
|
89
|
+
// we don't have to rewrite type references for aliased imports
|
90
|
+
if (imported.node.name === specifier.node.local.name) {
|
91
|
+
typeReferencesToUpdate.add(specifier.node.local.name);
|
92
|
+
}
|
93
|
+
|
94
|
+
const newType = migrateType(imported.node.name);
|
95
|
+
|
96
|
+
// replace the deprecated import type when the new type isn't yet imported
|
97
|
+
// note that we don't replace the local name of the specifier
|
98
|
+
if (!existingImports.some((it) => it.name === newType)) {
|
99
|
+
imported.replaceWith(t.identifier(newType));
|
100
|
+
existingImports.push({ name: newType, isAlias: false, path: specifier });
|
101
|
+
} else {
|
102
|
+
// if the existing import has the same local name but is an alias we throw
|
103
|
+
// we could have imported the type with an alias, but seems to much effort
|
104
|
+
const existingImport = existingImports.find((it) => it.name === newType && it.isAlias);
|
105
|
+
if (existingImport) {
|
106
|
+
throw existingImport.path.buildCodeFrameError(
|
107
|
+
'This codemod does not support local imports that are called the same as a storybook import.\n' +
|
108
|
+
'Rename this local import and try again.'
|
109
|
+
);
|
110
|
+
} else {
|
111
|
+
// if the type already exists, without being aliased
|
112
|
+
// we can safely remove the deprecated import now
|
113
|
+
specifier.remove();
|
114
|
+
}
|
115
|
+
}
|
116
|
+
}
|
117
|
+
});
|
118
|
+
},
|
119
|
+
});
|
120
|
+
|
121
|
+
file.path.traverse({
|
122
|
+
TSTypeReference: (path) => {
|
123
|
+
const typeName = path.get('typeName');
|
124
|
+
if (typeName.isIdentifier()) {
|
125
|
+
if (typeReferencesToUpdate.has(typeName.node.name)) {
|
126
|
+
typeName.replaceWith(t.identifier(migrateType(typeName.node.name)));
|
127
|
+
}
|
128
|
+
} else if (typeName.isTSQualifiedName()) {
|
129
|
+
// For example SB.StoryObj
|
130
|
+
const namespace = typeName.get('left');
|
131
|
+
if (namespace.isIdentifier()) {
|
132
|
+
if (importedNamespaces.has(namespace.node.name)) {
|
133
|
+
const right = typeName.get('right');
|
134
|
+
if (deprecatedTypes.includes(right.node.name)) {
|
135
|
+
right.replaceWith(t.identifier(migrateType(right.node.name)));
|
136
|
+
}
|
137
|
+
}
|
138
|
+
}
|
139
|
+
}
|
140
|
+
},
|
141
|
+
});
|
142
|
+
}
|
package/dist/chunk-CO6EPEMB.mjs
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
import camelCase from"lodash/camelCase";import upperFirst from"lodash/upperFirst";var sanitizeName=name=>{let key=upperFirst(camelCase(name));return/^\d/.test(key)&&(key=`_${key}`),/^\d/.test(key)&&(key=`_${key}`),key};function jscodeshiftToPrettierParser(parser){let parserMap={babylon:"babel",flow:"flow",ts:"typescript",tsx:"typescript"};return parser&&parserMap[parser]||"babel"}export{sanitizeName,jscodeshiftToPrettierParser};
|
@@ -1,29 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* Convert a component's module story file into an MDX file
|
3
|
-
*
|
4
|
-
* For example:
|
5
|
-
*
|
6
|
-
* ```
|
7
|
-
* input { Button } from './Button';
|
8
|
-
* export default {
|
9
|
-
* title: 'Button'
|
10
|
-
* }
|
11
|
-
* export const story = () => <Button label="The Button" />;
|
12
|
-
* ```
|
13
|
-
*
|
14
|
-
* Becomes:
|
15
|
-
*
|
16
|
-
* ```
|
17
|
-
* import { Meta, Story } from '@storybook/addon-docs';
|
18
|
-
* input { Button } from './Button';
|
19
|
-
*
|
20
|
-
* <Meta title='Button' />
|
21
|
-
*
|
22
|
-
* <Story name='story'>
|
23
|
-
* <Button label="The Button" />
|
24
|
-
* </Story>
|
25
|
-
* ```
|
26
|
-
*/
|
27
|
-
declare function transformer(file: any, api: any): any;
|
28
|
-
|
29
|
-
export { transformer as default };
|
@@ -1,3 +0,0 @@
|
|
1
|
-
var y=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var D=(s,r)=>{for(var t in r)y(s,t,{get:r[t],enumerable:!0})},M=(s,r,t,a)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of A(r))!C.call(s,o)&&o!==t&&y(s,o,{get:()=>r[o],enumerable:!(a=v(r,o))||a.enumerable});return s};var W=s=>M(y({},"__esModule",{value:!0}),s);var L={};D(L,{default:()=>g});module.exports=W(L);var E=require("recast"),x=require("@storybook/csf");function J(s,r){return s.__paths[0].node.program.body.map(a=>{let{code:o}=(0,E.prettyPrint)(a,r);return a.type==="JSXElement"?`${o}
|
2
|
-
`:o}).join(`
|
3
|
-
`)}function K(s){let{code:r}=(0,E.prettyPrint)(s,{});return(0,eval)(r)}function g(s,r){let t=r.jscodeshift,a=t(s.source),o={},p={};function f(n,e){return t.jsxAttribute(t.jsxIdentifier(n),e.type==="Literal"?e:t.jsxExpressionContainer(e))}function S(n){return n.type==="ArrowFunctionExpression"&&n.body.type==="JSXElement"?n.body:t.jsxExpressionContainer(n)}function b(n){let e=o[n];if(e){let i=e.properties.find(c=>c.key.name==="name");if(i&&i.value.type==="Literal")return i.value.value}return n}function I(n){let e=[],i=o[n];return i&&i.properties.forEach(c=>{let{key:l,value:d}=c;l.name!=="name"&&e.push(f(l.name,d))}),e}let u=a.find(t.ExportDefaultDeclaration).filter(n=>n.node.declaration.properties.map(e=>e.key.name).includes("title"));if(u.size()===0)return a.toSource();a.find(t.ImportDeclaration).at(-1).insertAfter(t.emptyStatement()).insertAfter(t.importDeclaration([t.importSpecifier(t.identifier("Meta")),t.importSpecifier(t.identifier("Story"))],t.literal("@storybook/addon-docs"))),a.find(t.ImportDeclaration).filter(n=>n.node.source.value==="react").remove(),u.forEach(n=>{n.node.declaration.properties.forEach(e=>{["includeStories","excludeStories"].includes(e.key.name)&&(p[e.key.name]=K(e.value))})});let j=a.find(t.ExportNamedDeclaration);j.forEach(n=>{let e=n.node.declaration.declarations[0].id.name;(0,x.isExportStory)(e,p)&&(o[e]=null)});let h=a.find(t.AssignmentExpression).filter(n=>{let{left:e}=n.node;return e.type==="MemberExpression"&&e.object.type==="Identifier"&&e.object.name in o&&e.property.type==="Identifier"&&e.property.name==="story"});return h.forEach(n=>{let{left:e,right:i}=n.node;o[e.object.name]=i}),h.remove(),u.replaceWith(n=>{let e=t.jsxIdentifier("Meta"),i=[];n.node.declaration.properties.forEach(l=>{let{key:d,value:m}=l;["includeStories","excludeStories"].includes(d.name)||i.push(f(d.name,m))});let c=t.jsxOpeningElement(e,i);return c.selfClosing=!0,t.jsxElement(c)}),j.replaceWith(n=>{let e=n.node.declaration.declarations[0].id.name;if(!(0,x.isExportStory)(e,p))return n.node;let i=t.jsxIdentifier("Story"),c=b(e),l=[f("name",t.literal(c)),...I(e)],d=t.jsxOpeningElement(i,l),m=t.jsxClosingElement(i),k=[S(n.node.declaration.declarations[0].init)];return t.jsxElement(d,m,k)}),J(a,{quote:"single",trailingComma:"true",tabWidth:2})}0&&(module.exports={});
|
@@ -1,3 +0,0 @@
|
|
1
|
-
import{prettyPrint}from"recast";import{isExportStory}from"@storybook/csf";function exportMdx(root,options){return root.__paths[0].node.program.body.map(n=>{let{code}=prettyPrint(n,options);return n.type==="JSXElement"?`${code}
|
2
|
-
`:code}).join(`
|
3
|
-
`)}function parseIncludeExclude(prop){let{code}=prettyPrint(prop,{});return(0,eval)(code)}function transformer(file,api){let j=api.jscodeshift,root=j(file.source),storyKeyToStory={},meta={};function makeAttr(key,val){return j.jsxAttribute(j.jsxIdentifier(key),val.type==="Literal"?val:j.jsxExpressionContainer(val))}function getStoryContents(node){return node.type==="ArrowFunctionExpression"&&node.body.type==="JSXElement"?node.body:j.jsxExpressionContainer(node)}function getName(storyKey){let story=storyKeyToStory[storyKey];if(story){let name=story.properties.find(prop=>prop.key.name==="name");if(name&&name.value.type==="Literal")return name.value.value}return storyKey}function getStoryAttrs(storyKey){let attrs=[],story=storyKeyToStory[storyKey];return story&&story.properties.forEach(prop=>{let{key,value}=prop;key.name!=="name"&&attrs.push(makeAttr(key.name,value))}),attrs}let defaultExportWithTitle=root.find(j.ExportDefaultDeclaration).filter(def=>def.node.declaration.properties.map(p=>p.key.name).includes("title"));if(defaultExportWithTitle.size()===0)return root.toSource();root.find(j.ImportDeclaration).at(-1).insertAfter(j.emptyStatement()).insertAfter(j.importDeclaration([j.importSpecifier(j.identifier("Meta")),j.importSpecifier(j.identifier("Story"))],j.literal("@storybook/addon-docs"))),root.find(j.ImportDeclaration).filter(decl=>decl.node.source.value==="react").remove(),defaultExportWithTitle.forEach(exp=>{exp.node.declaration.properties.forEach(p=>{["includeStories","excludeStories"].includes(p.key.name)&&(meta[p.key.name]=parseIncludeExclude(p.value))})});let namedExports=root.find(j.ExportNamedDeclaration);namedExports.forEach(exp=>{let storyKey=exp.node.declaration.declarations[0].id.name;isExportStory(storyKey,meta)&&(storyKeyToStory[storyKey]=null)});let storyAssignments=root.find(j.AssignmentExpression).filter(exp=>{let{left}=exp.node;return left.type==="MemberExpression"&&left.object.type==="Identifier"&&left.object.name in storyKeyToStory&&left.property.type==="Identifier"&&left.property.name==="story"});return storyAssignments.forEach(exp=>{let{left,right}=exp.node;storyKeyToStory[left.object.name]=right}),storyAssignments.remove(),defaultExportWithTitle.replaceWith(exp=>{let jsxId=j.jsxIdentifier("Meta"),attrs=[];exp.node.declaration.properties.forEach(prop=>{let{key,value}=prop;["includeStories","excludeStories"].includes(key.name)||attrs.push(makeAttr(key.name,value))});let opening=j.jsxOpeningElement(jsxId,attrs);return opening.selfClosing=!0,j.jsxElement(opening)}),namedExports.replaceWith(exp=>{let storyKey=exp.node.declaration.declarations[0].id.name;if(!isExportStory(storyKey,meta))return exp.node;let jsxId=j.jsxIdentifier("Story"),name=getName(storyKey),attributes=[makeAttr("name",j.literal(name)),...getStoryAttrs(storyKey)],opening=j.jsxOpeningElement(jsxId,attributes),closing=j.jsxClosingElement(jsxId),children=[getStoryContents(exp.node.declaration.declarations[0].init)];return j.jsxElement(opening,closing,children)}),exportMdx(root,{quote:"single",trailingComma:"true",tabWidth:2})}export{transformer as default};
|
@@ -1,20 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import Button from './Button';
|
3
|
-
import { action } from '@storybook/addon-actions';
|
4
|
-
|
5
|
-
export default {
|
6
|
-
title: 'Button',
|
7
|
-
};
|
8
|
-
|
9
|
-
export const story1 = () => <Button label="Story 1" />;
|
10
|
-
|
11
|
-
export const story2 = () => <Button label="Story 2" onClick={action('click')} />;
|
12
|
-
story2.story = { name: 'second story' };
|
13
|
-
|
14
|
-
export const story3 = () => (
|
15
|
-
<div>
|
16
|
-
<Button label="The Button" onClick={action('onClick')} />
|
17
|
-
<br />
|
18
|
-
</div>
|
19
|
-
);
|
20
|
-
story3.story = { name: 'complex story' };
|
@@ -1,18 +0,0 @@
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
|
-
|
3
|
-
exports[`csf-to-mdx transforms correctly using "basic.input.js" data 1`] = `
|
4
|
-
"import Button from './Button';
|
5
|
-
import { action } from '@storybook/addon-actions';
|
6
|
-
import { Meta, Story } from '@storybook/addon-docs';
|
7
|
-
|
8
|
-
<Meta title='Button' />
|
9
|
-
|
10
|
-
<Story name='story1'><Button label='Story 1' /></Story>
|
11
|
-
|
12
|
-
<Story name='second story'><Button label='Story 2' onClick={action('click')} /></Story>
|
13
|
-
|
14
|
-
<Story name='complex story'><div>
|
15
|
-
<Button label='The Button' onClick={action('onClick')} />
|
16
|
-
<br />
|
17
|
-
</div></Story>"
|
18
|
-
`;
|
@@ -1,10 +0,0 @@
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
|
-
|
3
|
-
exports[`csf-to-mdx transforms correctly using "component-id.input.js" data 1`] = `
|
4
|
-
"import Button from './Button';
|
5
|
-
import { Meta, Story } from '@storybook/addon-docs';
|
6
|
-
|
7
|
-
<Meta title='Button' id='button-id' />
|
8
|
-
|
9
|
-
<Story name='someStory'><Button label='Story 1' /></Story>"
|
10
|
-
`;
|
@@ -1,13 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import Button from './Button';
|
3
|
-
|
4
|
-
export default {
|
5
|
-
title: 'Some.Button',
|
6
|
-
decorators: [withKnobs, storyFn => <div className="foo">{storyFn}</div>],
|
7
|
-
};
|
8
|
-
|
9
|
-
export const story1 = () => <Button label="The Button" />;
|
10
|
-
story1.story = {
|
11
|
-
name: 'with decorator',
|
12
|
-
decorators: [withKnobs],
|
13
|
-
};
|
@@ -1,12 +0,0 @@
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
|
-
|
3
|
-
exports[`csf-to-mdx transforms correctly using "decorators.input.js" data 1`] = `
|
4
|
-
"import Button from './Button';
|
5
|
-
import { Meta, Story } from '@storybook/addon-docs';
|
6
|
-
|
7
|
-
<Meta
|
8
|
-
title='Some.Button'
|
9
|
-
decorators={[withKnobs, storyFn => <div className='foo'>{storyFn}</div>]} />
|
10
|
-
|
11
|
-
<Story name='with decorator' decorators={[withKnobs]}><Button label='The Button' /></Story>"
|
12
|
-
`;
|
@@ -1,23 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import Button from './Button';
|
3
|
-
import { action } from '@storybook/addon-actions';
|
4
|
-
|
5
|
-
export default {
|
6
|
-
title: 'Button',
|
7
|
-
excludeStories: /.*Data$/,
|
8
|
-
};
|
9
|
-
|
10
|
-
export const rowData = { col1: 'a', col2: 2 };
|
11
|
-
|
12
|
-
export const story1 = () => <Button label="Story 1" />;
|
13
|
-
|
14
|
-
export const story2 = () => <Button label="Story 2" onClick={action('click')} />;
|
15
|
-
story2.story = { name: 'second story' };
|
16
|
-
|
17
|
-
export const story3 = () => (
|
18
|
-
<div>
|
19
|
-
<Button label="The Button" onClick={action('onClick')} />
|
20
|
-
<br />
|
21
|
-
</div>
|
22
|
-
);
|
23
|
-
story3.story = { name: 'complex story' };
|
@@ -1,22 +0,0 @@
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
|
-
|
3
|
-
exports[`csf-to-mdx transforms correctly using "exclude-stories.input.js" data 1`] = `
|
4
|
-
"import Button from './Button';
|
5
|
-
import { action } from '@storybook/addon-actions';
|
6
|
-
import { Meta, Story } from '@storybook/addon-docs';
|
7
|
-
|
8
|
-
<Meta title='Button' />
|
9
|
-
|
10
|
-
export const rowData = {
|
11
|
-
col1: 'a',
|
12
|
-
col2: 2,
|
13
|
-
};
|
14
|
-
<Story name='story1'><Button label='Story 1' /></Story>
|
15
|
-
|
16
|
-
<Story name='second story'><Button label='Story 2' onClick={action('click')} /></Story>
|
17
|
-
|
18
|
-
<Story name='complex story'><div>
|
19
|
-
<Button label='The Button' onClick={action('onClick')} />
|
20
|
-
<br />
|
21
|
-
</div></Story>"
|
22
|
-
`;
|
@@ -1,16 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import Button from './Button';
|
3
|
-
|
4
|
-
import { storiesOf } from '@storybook/react';
|
5
|
-
|
6
|
-
export default {
|
7
|
-
title: 'Button',
|
8
|
-
component: Button,
|
9
|
-
parameters: {
|
10
|
-
foo: 1,
|
11
|
-
bar: 2,
|
12
|
-
},
|
13
|
-
};
|
14
|
-
|
15
|
-
export const story1 = () => <Button label="The Button" />;
|
16
|
-
story1.story = { name: 'with kind parameters' };
|
@@ -1,17 +0,0 @@
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
|
-
|
3
|
-
exports[`csf-to-mdx transforms correctly using "parameters.input.js" data 1`] = `
|
4
|
-
"import Button from './Button';
|
5
|
-
import { storiesOf } from '@storybook/react';
|
6
|
-
import { Meta, Story } from '@storybook/addon-docs';
|
7
|
-
|
8
|
-
<Meta
|
9
|
-
title='Button'
|
10
|
-
component={Button}
|
11
|
-
parameters={{
|
12
|
-
foo: 1,
|
13
|
-
bar: 2,
|
14
|
-
}} />
|
15
|
-
|
16
|
-
<Story name='with kind parameters'><Button label='The Button' /></Story>"
|
17
|
-
`;
|
@@ -1,19 +0,0 @@
|
|
1
|
-
import global from 'global';
|
2
|
-
|
3
|
-
const { document } = global;
|
4
|
-
|
5
|
-
export default {
|
6
|
-
title: 'Function',
|
7
|
-
};
|
8
|
-
|
9
|
-
export const functionStory = () => {
|
10
|
-
const btn = document.createElement('button');
|
11
|
-
btn.innerHTML = 'Hello Button';
|
12
|
-
btn.addEventListener('click', action('Click'));
|
13
|
-
return btn;
|
14
|
-
};
|
15
|
-
|
16
|
-
functionStory.story = {
|
17
|
-
name: 'function',
|
18
|
-
height: '100px',
|
19
|
-
};
|
@@ -1,18 +0,0 @@
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
|
-
|
3
|
-
exports[`csf-to-mdx transforms correctly using "story-function.input.js" data 1`] = `
|
4
|
-
"import global from 'global';
|
5
|
-
import { Meta, Story } from '@storybook/addon-docs';
|
6
|
-
|
7
|
-
const {
|
8
|
-
document,
|
9
|
-
} = global;
|
10
|
-
<Meta title='Function' />
|
11
|
-
|
12
|
-
<Story name='function' height='100px'>{() => {
|
13
|
-
const btn = document.createElement('button');
|
14
|
-
btn.innerHTML = 'Hello Button';
|
15
|
-
btn.addEventListener('click', action('Click'));
|
16
|
-
return btn;
|
17
|
-
}}</Story>"
|
18
|
-
`;
|