@storybook/codemod 7.0.0-alpha.8 → 7.0.0-beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. package/README.md +0 -36
  2. package/dist/index.d.ts +261 -0
  3. package/dist/index.js +1 -0
  4. package/dist/index.mjs +1 -0
  5. package/jest.config.js +7 -0
  6. package/package.json +32 -29
  7. package/src/index.js +77 -0
  8. package/src/lib/utils.test.js +9 -0
  9. package/{dist/esm/lib/utils.js → src/lib/utils.ts} +10 -11
  10. package/src/transforms/__testfixtures__/add-component-parameters/add-component-parameters.input.js +44 -0
  11. package/src/transforms/__testfixtures__/add-component-parameters/add-component-parameters.output.snapshot +68 -0
  12. package/src/transforms/__testfixtures__/csf-hoist-story-annotations/basic.input.js +25 -0
  13. package/src/transforms/__testfixtures__/csf-hoist-story-annotations/basic.output.snapshot +27 -0
  14. package/src/transforms/__testfixtures__/csf-hoist-story-annotations/overrides.input.js +25 -0
  15. package/src/transforms/__testfixtures__/csf-hoist-story-annotations/overrides.output.snapshot +28 -0
  16. package/src/transforms/__testfixtures__/csf-hoist-story-annotations/variable.input.js +13 -0
  17. package/src/transforms/__testfixtures__/csf-hoist-story-annotations/variable.output.snapshot +17 -0
  18. package/src/transforms/__testfixtures__/csf-to-mdx/basic.input.js +20 -0
  19. package/src/transforms/__testfixtures__/csf-to-mdx/basic.output.snapshot +18 -0
  20. package/src/transforms/__testfixtures__/csf-to-mdx/component-id.input.js +9 -0
  21. package/src/transforms/__testfixtures__/csf-to-mdx/component-id.output.snapshot +10 -0
  22. package/src/transforms/__testfixtures__/csf-to-mdx/decorators.input.js +13 -0
  23. package/src/transforms/__testfixtures__/csf-to-mdx/decorators.output.snapshot +12 -0
  24. package/src/transforms/__testfixtures__/csf-to-mdx/exclude-stories.input.js +23 -0
  25. package/src/transforms/__testfixtures__/csf-to-mdx/exclude-stories.output.snapshot +22 -0
  26. package/src/transforms/__testfixtures__/csf-to-mdx/parameters.input.js +16 -0
  27. package/src/transforms/__testfixtures__/csf-to-mdx/parameters.output.snapshot +17 -0
  28. package/src/transforms/__testfixtures__/csf-to-mdx/story-function.input.js +19 -0
  29. package/src/transforms/__testfixtures__/csf-to-mdx/story-function.output.snapshot +18 -0
  30. package/src/transforms/__testfixtures__/csf-to-mdx/story-parameters.input.js +24 -0
  31. package/src/transforms/__testfixtures__/csf-to-mdx/story-parameters.output.snapshot +22 -0
  32. package/src/transforms/__testfixtures__/mdx-to-csf/basic.input.js +18 -0
  33. package/src/transforms/__testfixtures__/mdx-to-csf/basic.output.snapshot +40 -0
  34. package/src/transforms/__testfixtures__/mdx-to-csf/component-id.input.js +6 -0
  35. package/src/transforms/__testfixtures__/mdx-to-csf/component-id.output.snapshot +17 -0
  36. package/src/transforms/__testfixtures__/mdx-to-csf/decorators.input.js +8 -0
  37. package/src/transforms/__testfixtures__/mdx-to-csf/decorators.output.snapshot +18 -0
  38. package/src/transforms/__testfixtures__/mdx-to-csf/exclude-stories.input.js +19 -0
  39. package/src/transforms/__testfixtures__/mdx-to-csf/exclude-stories.output.snapshot +39 -0
  40. package/src/transforms/__testfixtures__/mdx-to-csf/parameters.input.js +14 -0
  41. package/src/transforms/__testfixtures__/mdx-to-csf/parameters.output.snapshot +23 -0
  42. package/src/transforms/__testfixtures__/mdx-to-csf/plaintext.input.js +3 -0
  43. package/src/transforms/__testfixtures__/mdx-to-csf/plaintext.output.snapshot +11 -0
  44. package/src/transforms/__testfixtures__/mdx-to-csf/story-function.input.js +10 -0
  45. package/src/transforms/__testfixtures__/mdx-to-csf/story-function.output.snapshot +18 -0
  46. package/src/transforms/__testfixtures__/mdx-to-csf/story-parameters.input.js +18 -0
  47. package/src/transforms/__testfixtures__/mdx-to-csf/story-parameters.output.snapshot +32 -0
  48. package/src/transforms/__testfixtures__/mdx-to-csf/story-refs.input.js +22 -0
  49. package/src/transforms/__testfixtures__/mdx-to-csf/story-refs.output.snapshot +34 -0
  50. package/src/transforms/__testfixtures__/move-builtin-addons/default.input.js +2 -0
  51. package/src/transforms/__testfixtures__/move-builtin-addons/default.output.snapshot +8 -0
  52. package/src/transforms/__testfixtures__/move-builtin-addons/with-no-change.input.js +3 -0
  53. package/src/transforms/__testfixtures__/move-builtin-addons/with-no-change.output.snapshot +7 -0
  54. package/src/transforms/__testfixtures__/storiesof-to-csf/basic.input.js +18 -0
  55. package/src/transforms/__testfixtures__/storiesof-to-csf/basic.output.snapshot +45 -0
  56. package/src/transforms/__testfixtures__/storiesof-to-csf/collision.input.js +11 -0
  57. package/src/transforms/__testfixtures__/storiesof-to-csf/collision.output.snapshot +38 -0
  58. package/src/transforms/__testfixtures__/storiesof-to-csf/const.input.js +1 -0
  59. package/src/transforms/__testfixtures__/storiesof-to-csf/const.output.snapshot +13 -0
  60. package/src/transforms/__testfixtures__/storiesof-to-csf/decorators.input.js +9 -0
  61. package/src/transforms/__testfixtures__/storiesof-to-csf/decorators.output.snapshot +18 -0
  62. package/src/transforms/__testfixtures__/storiesof-to-csf/default.input.js +7 -0
  63. package/src/transforms/__testfixtures__/storiesof-to-csf/default.output.snapshot +17 -0
  64. package/src/transforms/__testfixtures__/storiesof-to-csf/digit.input.js +1 -0
  65. package/src/transforms/__testfixtures__/storiesof-to-csf/digit.output.js +5 -0
  66. package/src/transforms/__testfixtures__/storiesof-to-csf/digit.output.snapshot +9 -0
  67. package/src/transforms/__testfixtures__/storiesof-to-csf/export-destructuring.input.js +11 -0
  68. package/src/transforms/__testfixtures__/storiesof-to-csf/export-destructuring.output.snapshot +23 -0
  69. package/src/transforms/__testfixtures__/storiesof-to-csf/export-function.input.js +12 -0
  70. package/src/transforms/__testfixtures__/storiesof-to-csf/export-function.output.snapshot +23 -0
  71. package/src/transforms/__testfixtures__/storiesof-to-csf/export-names.input.js +14 -0
  72. package/src/transforms/__testfixtures__/storiesof-to-csf/export-names.output.snapshot +26 -0
  73. package/src/transforms/__testfixtures__/storiesof-to-csf/exports.input.js +2 -0
  74. package/src/transforms/__testfixtures__/storiesof-to-csf/exports.output.snapshot +16 -0
  75. package/src/transforms/__testfixtures__/storiesof-to-csf/module.input.js +12 -0
  76. package/src/transforms/__testfixtures__/storiesof-to-csf/module.output.snapshot +16 -0
  77. package/src/transforms/__testfixtures__/storiesof-to-csf/multi.input.js +14 -0
  78. package/src/transforms/__testfixtures__/storiesof-to-csf/multi.output.snapshot +39 -0
  79. package/src/transforms/__testfixtures__/storiesof-to-csf/parameters-as-var.input.js +8 -0
  80. package/src/transforms/__testfixtures__/storiesof-to-csf/parameters-as-var.output.snapshot +20 -0
  81. package/src/transforms/__testfixtures__/storiesof-to-csf/parameters.input.js +10 -0
  82. package/src/transforms/__testfixtures__/storiesof-to-csf/parameters.output.snapshot +23 -0
  83. package/src/transforms/__testfixtures__/storiesof-to-csf/storiesof-var.input.js +11 -0
  84. package/src/transforms/__testfixtures__/storiesof-to-csf/storiesof-var.output.snapshot +23 -0
  85. package/src/transforms/__testfixtures__/storiesof-to-csf/story-decorators.input.js +11 -0
  86. package/src/transforms/__testfixtures__/storiesof-to-csf/story-decorators.output.snapshot +29 -0
  87. package/src/transforms/__testfixtures__/storiesof-to-csf/story-parameters.input.js +14 -0
  88. package/src/transforms/__testfixtures__/storiesof-to-csf/story-parameters.output.snapshot +32 -0
  89. package/src/transforms/__testfixtures__/update-addon-info/update-addon-info.input.js +198 -0
  90. package/src/transforms/__testfixtures__/update-addon-info/update-addon-info.output.snapshot +198 -0
  91. package/src/transforms/__testfixtures__/update-organisation-name/update-organisation-name.input.js +19 -0
  92. package/src/transforms/__testfixtures__/update-organisation-name/update-organisation-name.output.snapshot +23 -0
  93. package/src/transforms/__testfixtures__/upgrade-hierarchy-separators/csf.input.js +3 -0
  94. package/src/transforms/__testfixtures__/upgrade-hierarchy-separators/csf.output.snapshot +7 -0
  95. package/src/transforms/__testfixtures__/upgrade-hierarchy-separators/dynamic-storiesof.input.js +5 -0
  96. package/src/transforms/__testfixtures__/upgrade-hierarchy-separators/dynamic-storiesof.output.snapshot +9 -0
  97. package/src/transforms/__testfixtures__/upgrade-hierarchy-separators/storiesof.input.js +8 -0
  98. package/src/transforms/__testfixtures__/upgrade-hierarchy-separators/storiesof.output.snapshot +12 -0
  99. package/src/transforms/__tests__/csf-2-to-3.test.ts +250 -0
  100. package/src/transforms/__tests__/transforms.tests.js +32 -0
  101. package/{dist/esm → src}/transforms/add-component-parameters.js +26 -21
  102. package/src/transforms/csf-2-to-3.ts +184 -0
  103. package/src/transforms/csf-hoist-story-annotations.js +97 -0
  104. package/src/transforms/csf-to-mdx.js +190 -0
  105. package/src/transforms/move-builtin-addons.js +32 -0
  106. package/src/transforms/storiesof-to-csf.js +277 -0
  107. package/{dist/esm → src}/transforms/update-addon-info.js +44 -24
  108. package/{dist/esm → src}/transforms/update-organisation-name.js +21 -24
  109. package/src/transforms/upgrade-hierarchy-separators.js +39 -0
  110. package/tsconfig.json +7 -0
  111. package/LICENSE +0 -21
  112. package/dist/cjs/index.js +0 -142
  113. package/dist/cjs/lib/utils.js +0 -45
  114. package/dist/cjs/transforms/add-component-parameters.js +0 -64
  115. package/dist/cjs/transforms/csf-2-to-3.js +0 -180
  116. package/dist/cjs/transforms/csf-hoist-story-annotations.js +0 -93
  117. package/dist/cjs/transforms/csf-to-mdx.js +0 -196
  118. package/dist/cjs/transforms/mdx-to-csf.js +0 -153
  119. package/dist/cjs/transforms/move-builtin-addons.js +0 -57
  120. package/dist/cjs/transforms/storiesof-to-csf.js +0 -300
  121. package/dist/cjs/transforms/update-addon-info.js +0 -101
  122. package/dist/cjs/transforms/update-organisation-name.js +0 -83
  123. package/dist/cjs/transforms/upgrade-hierarchy-separators.js +0 -42
  124. package/dist/esm/index.js +0 -99
  125. package/dist/esm/transforms/csf-2-to-3.js +0 -163
  126. package/dist/esm/transforms/csf-hoist-story-annotations.js +0 -86
  127. package/dist/esm/transforms/csf-to-mdx.js +0 -188
  128. package/dist/esm/transforms/mdx-to-csf.js +0 -139
  129. package/dist/esm/transforms/move-builtin-addons.js +0 -50
  130. package/dist/esm/transforms/storiesof-to-csf.js +0 -287
  131. package/dist/esm/transforms/upgrade-hierarchy-separators.js +0 -35
  132. package/dist/types/lib/utils.d.ts +0 -2
  133. package/dist/types/transforms/csf-2-to-3.d.ts +0 -6
package/README.md CHANGED
@@ -240,42 +240,6 @@ import { Meta, Story } from '@storybook/addon-docs';
240
240
  </Story>
241
241
  ```
242
242
 
243
- ### mdx-to-csf
244
-
245
- This converts all your MDX stories into Component Story Format.
246
-
247
- ```sh
248
- ./node_modules/.bin/jscodeshift -t ./node_modules/@storybook/codemod/dist/transforms/mdx-to-csf.js . --ignore-pattern "node_modules|dist" --extensions=mdx
249
- ```
250
-
251
- For example:
252
-
253
- ```js
254
- import React from 'react';
255
- import Button from './Button';
256
- import { Meta, Story } from '@storybook/addon-docs';
257
-
258
- <Meta title='Button' />
259
-
260
- <Story name='basic stories'><Button label='The Button' /></Story>
261
- ```
262
-
263
- Becomes:
264
-
265
- ```js
266
- import React from 'react';
267
- import Button from './Button';
268
-
269
- export default {
270
- title: 'Button',
271
- };
272
-
273
- export const basicStory = () => <Button label="The Button" />;
274
- basicStory.story = {
275
- name: 'basic stories',
276
- };
277
- ```
278
-
279
243
  ### upgrade-hierarchy-separators
280
244
 
281
245
  Starting in 5.3, Storybook is moving to using a single path separator, `/`, to specify the story hierarchy. It previously defaulted to `|` for story "roots" (optional) and either `/` or `.` for denoting paths. This codemod updates the old default to the new default.
@@ -0,0 +1,261 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { promisify } from 'util';
4
+ import globby from 'globby';
5
+ import { sync } from 'cross-spawn';
6
+
7
+ declare function jscodeshiftToPrettierParser(parser?: string): string;
8
+
9
+ const packageNames = {
10
+ '@kadira/react-storybook-decorator-centered': '@storybook/addon-centered',
11
+ '@kadira/storybook-addons': '@storybook/preview-api',
12
+ '@kadira/storybook-addon-actions': '@storybook/addon-actions',
13
+ '@kadira/storybook-addon-comments': '@storybook/addon-comments',
14
+ '@kadira/storybook-addon-graphql': '@storybook/addon-graphql',
15
+ '@kadira/storybook-addon-info': '@storybook/addon-info',
16
+ '@kadira/storybook-addon-knobs': '@storybook/addon-knobs',
17
+ '@kadira/storybook-addon-links': '@storybook/addon-links',
18
+ '@kadira/storybook-addon-notes': '@storybook/addon-notes',
19
+ '@kadira/storybook-addon-options': '@storybook/addon-options',
20
+ '@kadira/storybook-channels': '@storybook/channels',
21
+ '@kadira/storybook-channel-postmsg': '@storybook/channel-postmessage',
22
+ '@kadira/storybook-channel-websocket': '@storybook/channel-websocket',
23
+ '@kadira/storybook-ui': '@storybook/manager',
24
+ '@kadira/react-native-storybook': '@storybook/react-native',
25
+ '@kadira/react-storybook': '@storybook/react',
26
+ '@kadira/getstorybook': '@storybook/cli',
27
+ '@kadira/storybook': '@storybook/react',
28
+ storyshots: '@storybook/addon-storyshots',
29
+ getstorybook: '@storybook/cli',
30
+ };
31
+
32
+ function transformer$1(file, api) {
33
+ const j = api.jscodeshift;
34
+
35
+ const packageNamesKeys = Object.keys(packageNames);
36
+
37
+ /**
38
+ * Checks whether the node value matches a Storybook package
39
+ * @param {string} the import declaration node
40
+ * @returns {string} whether the node value matches a Storybook package
41
+ */
42
+ const getMatch = (oldpart) => packageNamesKeys.find((newpart) => oldpart.match(newpart));
43
+
44
+ /**
45
+ * Returns the name of the Storybook packages with the organisation name,
46
+ * replacing the old `@kadira/` prefix.
47
+ * @param {string} oldPackageName the name of the old package
48
+ * @return {string} the new package name
49
+ * @example
50
+ * // returns '@storybook/storybook'
51
+ * getNewPackageName('@kadira/storybook')
52
+ */
53
+ const getNewPackageName = (oldPackageName) => {
54
+ const match = getMatch(oldPackageName);
55
+
56
+ if (match) {
57
+ const replacement = packageNames[match];
58
+ return oldPackageName.replace(match, replacement);
59
+ }
60
+ return oldPackageName;
61
+ };
62
+
63
+ /**
64
+ * updatePackageName - updates the source name of the Storybook packages
65
+ * @param {ImportDeclaration} declaration the import declaration
66
+ * @returns {ImportDeclaration.Node} the import declaration node
67
+ */
68
+ const updatePackageName = (declaration) => {
69
+ // eslint-disable-next-line no-param-reassign
70
+ declaration.node.source.value = getNewPackageName(declaration.node.source.value);
71
+
72
+ return declaration.node;
73
+ };
74
+
75
+ return j(file.source)
76
+ .find(j.ImportDeclaration)
77
+ .replaceWith(updatePackageName)
78
+ .toSource({ quote: 'single' });
79
+ }
80
+
81
+ /**
82
+ * Takes the deprecated addon-info API, addWithInfo, and
83
+ * converts to the new withInfo API.
84
+ *
85
+ * Example of deprecated addWithInfo API:
86
+ *
87
+ * storiesOf('Button')
88
+ * .addWithInfo(
89
+ * 'story name',
90
+ * 'Story description.',
91
+ * () => (
92
+ * <Button label="The Button" />
93
+ * )
94
+ * )
95
+ *
96
+ * Converts to the new withInfo API:
97
+ *
98
+ * storiesOf('Button')
99
+ * .add('story name', withInfo(
100
+ * 'Story description.'
101
+ * )(() => (
102
+ * <Button label="The Button" />
103
+ * )))
104
+ */
105
+ function transformer(file, api) {
106
+ const j = api.jscodeshift;
107
+ const root = j(file.source);
108
+
109
+ /**
110
+ * Returns a list of parameters for the withInfo function. The contents
111
+ * of this list is either the second argument from the original
112
+ * addWithInfo function, if no additional options were used, or a
113
+ * combined object of all the options from the original function.
114
+ * @param {list} args - original addWithInfo function parameters
115
+ * @return {list} the modified list of parameters for the new function
116
+ */
117
+ const getOptions = (args) => {
118
+ if (args[3] === undefined) {
119
+ if (args[2] === undefined) {
120
+ // when the optional description string is not supplied for addWithInfo, use story name
121
+ return [args[0]];
122
+ }
123
+ return [args[1]];
124
+ }
125
+ return [
126
+ j.objectExpression([
127
+ j.property('init', j.identifier('text'), args[1]),
128
+ ...args[3].properties,
129
+ ]),
130
+ ];
131
+ };
132
+
133
+ /**
134
+ * Constructs the new withInfo function from the parameters of the
135
+ * original addWithInfo function.
136
+ * @param {CallExpression} addWithInfoExpression - original function
137
+ * @returns {CallExpression} the new withInfo function
138
+ */
139
+ const withInfo = (addWithInfoExpression) => {
140
+ const { node } = addWithInfoExpression;
141
+ const args = node.arguments;
142
+
143
+ // if optional description string is not supplied, the story component becomes second arg
144
+ const storyComponent = args[2] ? args[2] : args[1];
145
+
146
+ node.callee.property.name = 'add';
147
+ node.arguments = [
148
+ args[0],
149
+ j.callExpression(j.callExpression(j.identifier('withInfo'), getOptions(args)), [
150
+ storyComponent,
151
+ ]),
152
+ ];
153
+
154
+ return node;
155
+ };
156
+
157
+ /**
158
+ * Checks for - import { withInfo } from "@storybook/addon-info";
159
+ * Adds the import if necessary.
160
+ */
161
+ const checkWithInfoImport = () => {
162
+ const importExists = root
163
+ .find(j.ImportDeclaration)
164
+ .filter((imp) => imp.node.source.value === '@storybook/addon-info')
165
+ .size();
166
+
167
+ if (importExists) return;
168
+
169
+ root
170
+ .find(j.ImportDeclaration)
171
+ .at(-1)
172
+ .insertAfter(
173
+ j.importDeclaration(
174
+ [j.importSpecifier(j.identifier('withInfo'))],
175
+ j.literal('@storybook/addon-info')
176
+ )
177
+ );
178
+ };
179
+
180
+ const addWithInfoExpressions = root.find(j.CallExpression, {
181
+ callee: {
182
+ property: {
183
+ name: 'addWithInfo',
184
+ },
185
+ },
186
+ });
187
+
188
+ if (addWithInfoExpressions.size()) {
189
+ checkWithInfoImport();
190
+ addWithInfoExpressions.replaceWith(withInfo);
191
+ }
192
+
193
+ return root.toSource();
194
+ }
195
+
196
+ /* eslint import/prefer-default-export: "off" */
197
+
198
+ const TRANSFORM_DIR = `${__dirname}/transforms`;
199
+
200
+ function listCodemods() {
201
+ return fs
202
+ .readdirSync(TRANSFORM_DIR)
203
+ .filter((fname) => fname.endsWith('.js'))
204
+ .map((fname) => fname.slice(0, -3));
205
+ }
206
+
207
+ const renameAsync = promisify(fs.rename);
208
+
209
+ async function renameFile(file, from, to, { logger }) {
210
+ const newFile = file.replace(from, to);
211
+ logger.log(`Rename: ${file} ${newFile}`);
212
+ return renameAsync(file, newFile);
213
+ }
214
+
215
+ async function runCodemod(codemod, { glob, logger, dryRun, rename, parser }) {
216
+ const codemods = listCodemods();
217
+ if (!codemods.includes(codemod)) {
218
+ throw new Error(`Unknown codemod ${codemod}. Run --list for options.`);
219
+ }
220
+
221
+ let renameParts = null;
222
+ if (rename) {
223
+ renameParts = rename.split(':');
224
+ if (renameParts.length !== 2) {
225
+ throw new Error(`Codemod rename: expected format "from:to", got "${rename}"`);
226
+ }
227
+ }
228
+
229
+ // jscodeshift/prettier know how to handle .ts/.tsx extensions,
230
+ // so if the user uses one of those globs, we can auto-infer
231
+ let inferredParser = parser;
232
+ if (!parser) {
233
+ const extension = path.extname(glob).slice(1);
234
+ const knownParser = jscodeshiftToPrettierParser(extension);
235
+ if (knownParser !== 'babel') inferredParser = extension;
236
+ }
237
+
238
+ const files = await globby([glob, '!**/node_modules', '!**/dist']);
239
+ logger.log(`=> Applying ${codemod}: ${files.length} files`);
240
+ if (!dryRun) {
241
+ const parserArgs = inferredParser ? ['--parser', inferredParser] : [];
242
+ sync(
243
+ 'npx',
244
+ ['jscodeshift', '-t', `${TRANSFORM_DIR}/${codemod}.js`, ...parserArgs, ...files],
245
+ {
246
+ stdio: 'inherit',
247
+ shell: true,
248
+ }
249
+ );
250
+ }
251
+
252
+ if (renameParts) {
253
+ const [from, to] = renameParts;
254
+ logger.log(`=> Renaming ${rename}: ${files.length} files`);
255
+ await Promise.all(
256
+ files.map((file) => renameFile(file, new RegExp(`${from}$`), to, { logger }))
257
+ );
258
+ }
259
+ }
260
+
261
+ export { listCodemods, packageNames, runCodemod, transformer as updateAddonInfo, transformer$1 as updateOrganisationName };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ var R=Object.create;var l=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var A=Object.getPrototypeOf,C=Object.prototype.hasOwnProperty;var S=(o,r)=>{for(var t in r)l(o,t,{get:r[t],enumerable:!0})},h=(o,r,t,a)=>{if(r&&typeof r=="object"||typeof r=="function")for(let n of v(r))!C.call(o,n)&&n!==t&&l(o,n,{get:()=>r[n],enumerable:!(a=N(r,n))||a.enumerable});return o};var p=(o,r,t)=>(t=o!=null?R(A(o)):{},h(r||!o||!o.__esModule?l(t,"default",{value:o,enumerable:!0}):t,o)),W=o=>h(l({},"__esModule",{value:!0}),o);var z={};S(z,{listCodemods:()=>E,packageNames:()=>m,runCodemod:()=>q,updateAddonInfo:()=>b,updateOrganisationName:()=>y});module.exports=W(z);var u=p(require("fs")),x=p(require("path")),w=require("util"),$=p(require("globby")),I=require("cross-spawn");var _=p(require("lodash/camelCase")),D=p(require("lodash/upperFirst"));function g(o){let r={babylon:"babel",flow:"flow",ts:"typescript",tsx:"typescript"};return o&&r[o]||"babel"}var m={"@kadira/react-storybook-decorator-centered":"@storybook/addon-centered","@kadira/storybook-addons":"@storybook/preview-api","@kadira/storybook-addon-actions":"@storybook/addon-actions","@kadira/storybook-addon-comments":"@storybook/addon-comments","@kadira/storybook-addon-graphql":"@storybook/addon-graphql","@kadira/storybook-addon-info":"@storybook/addon-info","@kadira/storybook-addon-knobs":"@storybook/addon-knobs","@kadira/storybook-addon-links":"@storybook/addon-links","@kadira/storybook-addon-notes":"@storybook/addon-notes","@kadira/storybook-addon-options":"@storybook/addon-options","@kadira/storybook-channels":"@storybook/channels","@kadira/storybook-channel-postmsg":"@storybook/channel-postmessage","@kadira/storybook-channel-websocket":"@storybook/channel-websocket","@kadira/storybook-ui":"@storybook/manager","@kadira/react-native-storybook":"@storybook/react-native","@kadira/react-storybook":"@storybook/react","@kadira/getstorybook":"@storybook/cli","@kadira/storybook":"@storybook/react",storyshots:"@storybook/addon-storyshots",getstorybook:"@storybook/cli"};function y(o,r){let t=r.jscodeshift,a=Object.keys(m),n=s=>a.find(e=>s.match(e)),c=s=>{let e=n(s);if(e){let i=m[e];return s.replace(e,i)}return s},f=s=>(s.node.source.value=c(s.node.source.value),s.node);return t(o.source).find(t.ImportDeclaration).replaceWith(f).toSource({quote:"single"})}function b(o,r){let t=r.jscodeshift,a=t(o.source),n=e=>e[3]===void 0?e[2]===void 0?[e[0]]:[e[1]]:[t.objectExpression([t.property("init",t.identifier("text"),e[1]),...e[3].properties])],c=e=>{let{node:i}=e,d=i.arguments,k=d[2]?d[2]:d[1];return i.callee.property.name="add",i.arguments=[d[0],t.callExpression(t.callExpression(t.identifier("withInfo"),n(d)),[k])],i},f=()=>{a.find(t.ImportDeclaration).filter(i=>i.node.source.value==="@storybook/addon-info").size()||a.find(t.ImportDeclaration).at(-1).insertAfter(t.importDeclaration([t.importSpecifier(t.identifier("withInfo"))],t.literal("@storybook/addon-info")))},s=a.find(t.CallExpression,{callee:{property:{name:"addWithInfo"}}});return s.size()&&(f(),s.replaceWith(c)),a.toSource()}var j=`${__dirname}/transforms`;function E(){return u.default.readdirSync(j).filter(o=>o.endsWith(".js")).map(o=>o.slice(0,-3))}var F=(0,w.promisify)(u.default.rename);async function O(o,r,t,{logger:a}){let n=o.replace(r,t);return a.log(`Rename: ${o} ${n}`),F(o,n)}async function q(o,{glob:r,logger:t,dryRun:a,rename:n,parser:c}){if(!E().includes(o))throw new Error(`Unknown codemod ${o}. Run --list for options.`);let s=null;if(n&&(s=n.split(":"),s.length!==2))throw new Error(`Codemod rename: expected format "from:to", got "${n}"`);let e=c;if(!c){let d=x.default.extname(r).slice(1);g(d)!=="babel"&&(e=d)}let i=await(0,$.default)([r,"!**/node_modules","!**/dist"]);if(t.log(`=> Applying ${o}: ${i.length} files`),!a){let d=e?["--parser",e]:[];(0,I.sync)("npx",["jscodeshift","-t",`${j}/${o}.js`,...d,...i],{stdio:"inherit",shell:!0})}if(s){let[d,k]=s;t.log(`=> Renaming ${n}: ${i.length} files`),await Promise.all(i.map(P=>O(P,new RegExp(`${d}$`),k,{logger:t})))}}0&&(module.exports={listCodemods,packageNames,runCodemod,updateAddonInfo,updateOrganisationName});
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ import fs from"fs";import path from"path";import{promisify}from"util";import globby from"globby";import{sync as spawnSync}from"cross-spawn";import camelCase from"lodash/camelCase";import upperFirst from"lodash/upperFirst";function jscodeshiftToPrettierParser(parser){let parserMap={babylon:"babel",flow:"flow",ts:"typescript",tsx:"typescript"};return parser&&parserMap[parser]||"babel"}var packageNames={"@kadira/react-storybook-decorator-centered":"@storybook/addon-centered","@kadira/storybook-addons":"@storybook/preview-api","@kadira/storybook-addon-actions":"@storybook/addon-actions","@kadira/storybook-addon-comments":"@storybook/addon-comments","@kadira/storybook-addon-graphql":"@storybook/addon-graphql","@kadira/storybook-addon-info":"@storybook/addon-info","@kadira/storybook-addon-knobs":"@storybook/addon-knobs","@kadira/storybook-addon-links":"@storybook/addon-links","@kadira/storybook-addon-notes":"@storybook/addon-notes","@kadira/storybook-addon-options":"@storybook/addon-options","@kadira/storybook-channels":"@storybook/channels","@kadira/storybook-channel-postmsg":"@storybook/channel-postmessage","@kadira/storybook-channel-websocket":"@storybook/channel-websocket","@kadira/storybook-ui":"@storybook/manager","@kadira/react-native-storybook":"@storybook/react-native","@kadira/react-storybook":"@storybook/react","@kadira/getstorybook":"@storybook/cli","@kadira/storybook":"@storybook/react",storyshots:"@storybook/addon-storyshots",getstorybook:"@storybook/cli"};function transformer(file,api){let j=api.jscodeshift,packageNamesKeys=Object.keys(packageNames),getMatch=oldpart=>packageNamesKeys.find(newpart=>oldpart.match(newpart)),getNewPackageName=oldPackageName=>{let match=getMatch(oldPackageName);if(match){let replacement=packageNames[match];return oldPackageName.replace(match,replacement)}return oldPackageName},updatePackageName=declaration=>(declaration.node.source.value=getNewPackageName(declaration.node.source.value),declaration.node);return j(file.source).find(j.ImportDeclaration).replaceWith(updatePackageName).toSource({quote:"single"})}function transformer2(file,api){let j=api.jscodeshift,root=j(file.source),getOptions=args=>args[3]===void 0?args[2]===void 0?[args[0]]:[args[1]]:[j.objectExpression([j.property("init",j.identifier("text"),args[1]),...args[3].properties])],withInfo=addWithInfoExpression=>{let{node}=addWithInfoExpression,args=node.arguments,storyComponent=args[2]?args[2]:args[1];return node.callee.property.name="add",node.arguments=[args[0],j.callExpression(j.callExpression(j.identifier("withInfo"),getOptions(args)),[storyComponent])],node},checkWithInfoImport=()=>{root.find(j.ImportDeclaration).filter(imp=>imp.node.source.value==="@storybook/addon-info").size()||root.find(j.ImportDeclaration).at(-1).insertAfter(j.importDeclaration([j.importSpecifier(j.identifier("withInfo"))],j.literal("@storybook/addon-info")))},addWithInfoExpressions=root.find(j.CallExpression,{callee:{property:{name:"addWithInfo"}}});return addWithInfoExpressions.size()&&(checkWithInfoImport(),addWithInfoExpressions.replaceWith(withInfo)),root.toSource()}var TRANSFORM_DIR=`${__dirname}/transforms`;function listCodemods(){return fs.readdirSync(TRANSFORM_DIR).filter(fname=>fname.endsWith(".js")).map(fname=>fname.slice(0,-3))}var renameAsync=promisify(fs.rename);async function renameFile(file,from,to,{logger}){let newFile=file.replace(from,to);return logger.log(`Rename: ${file} ${newFile}`),renameAsync(file,newFile)}async function runCodemod(codemod,{glob,logger,dryRun,rename,parser}){if(!listCodemods().includes(codemod))throw new Error(`Unknown codemod ${codemod}. Run --list for options.`);let renameParts=null;if(rename&&(renameParts=rename.split(":"),renameParts.length!==2))throw new Error(`Codemod rename: expected format "from:to", got "${rename}"`);let inferredParser=parser;if(!parser){let extension=path.extname(glob).slice(1);jscodeshiftToPrettierParser(extension)!=="babel"&&(inferredParser=extension)}let files=await globby([glob,"!**/node_modules","!**/dist"]);if(logger.log(`=> Applying ${codemod}: ${files.length} files`),!dryRun){let parserArgs=inferredParser?["--parser",inferredParser]:[];spawnSync("npx",["jscodeshift","-t",`${TRANSFORM_DIR}/${codemod}.js`,...parserArgs,...files],{stdio:"inherit",shell:!0})}if(renameParts){let[from,to]=renameParts;logger.log(`=> Renaming ${rename}: ${files.length} files`),await Promise.all(files.map(file=>renameFile(file,new RegExp(`${from}$`),to,{logger})))}}export{listCodemods,packageNames,runCodemod,transformer2 as updateAddonInfo,transformer as updateOrganisationName};
package/jest.config.js ADDED
@@ -0,0 +1,7 @@
1
+ const path = require('path');
2
+ const baseConfig = require('../../jest.config.node');
3
+
4
+ module.exports = {
5
+ ...baseConfig,
6
+ displayName: __dirname.split(path.sep).slice(-2).join(path.posix.sep),
7
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storybook/codemod",
3
- "version": "7.0.0-alpha.8",
3
+ "version": "7.0.0-beta.1",
4
4
  "description": "A collection of codemod scripts written with JSCodeshift",
5
5
  "keywords": [
6
6
  "storybook"
@@ -20,46 +20,49 @@
20
20
  },
21
21
  "license": "MIT",
22
22
  "sideEffects": false,
23
- "main": "dist/cjs/index.js",
24
- "module": "dist/esm/index.js",
25
- "jsnext:main": "src/index.js",
26
- "typesVersions": {
27
- "<3.8": {
28
- "*": [
29
- "ts3.4/*"
30
- ]
31
- }
23
+ "exports": {
24
+ ".": {
25
+ "require": "./dist/index.js",
26
+ "import": "./dist/index.mjs",
27
+ "types": "./dist/index.d.ts"
28
+ },
29
+ "./package.json": "./package.json"
32
30
  },
33
- "files": [
34
- "dist/**/*",
35
- "README.md",
36
- "*.js",
37
- "*.d.ts",
38
- "!__testfixtures__"
39
- ],
31
+ "main": "dist/index.js",
32
+ "module": "dist/index.mjs",
33
+ "jsnext:main": "src/index.js",
34
+ "types": "dist/index.d.ts",
40
35
  "scripts": {
41
- "prepare": "node ../../scripts/prepare.js"
36
+ "check": "../../../scripts/node_modules/.bin/tsc --noEmit",
37
+ "prep": "../../../scripts/prepare/bundle.ts"
42
38
  },
43
39
  "dependencies": {
44
- "@babel/types": "^7.12.11",
45
- "@mdx-js/mdx": "^1.6.22",
46
- "@storybook/csf": "0.0.2--canary.4566f4d.1",
47
- "@storybook/csf-tools": "7.0.0-alpha.8",
48
- "@storybook/node-logger": "7.0.0-alpha.8",
49
- "core-js": "^3.8.2",
40
+ "@babel/types": "^7.20.2",
41
+ "@storybook/csf": "next",
42
+ "@storybook/csf-tools": "7.0.0-beta.1",
43
+ "@storybook/node-logger": "7.0.0-beta.1",
44
+ "@storybook/types": "7.0.0-beta.1",
50
45
  "cross-spawn": "^7.0.3",
51
46
  "globby": "^11.0.2",
52
47
  "jscodeshift": "^0.13.1",
53
48
  "lodash": "^4.17.21",
54
- "prettier": ">=2.2.1 <=2.3.0",
55
- "recast": "^0.19.0"
49
+ "prettier": "^2.8.0",
50
+ "recast": "^0.19.0",
51
+ "util": "^0.12.4"
56
52
  },
57
53
  "devDependencies": {
58
- "jest": "^26.6.3",
59
- "jest-specific-snapshot": "^4.0.0"
54
+ "jest": "^29.3.1",
55
+ "jest-specific-snapshot": "^7.0.0",
56
+ "typescript": "~4.9.3"
60
57
  },
61
58
  "publishConfig": {
62
59
  "access": "public"
63
60
  },
64
- "gitHead": "24725501c32a073cebc6bf2674a47357136fbe3a"
61
+ "bundler": {
62
+ "platform": "node",
63
+ "entries": [
64
+ "./src/index.js"
65
+ ]
66
+ },
67
+ "gitHead": "42c08678ac06d9c2c8e7a4c31a91e0a14bf5c2cd"
65
68
  }
package/src/index.js ADDED
@@ -0,0 +1,77 @@
1
+ /* eslint import/prefer-default-export: "off" */
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { promisify } from 'util';
5
+ import globby from 'globby';
6
+ import { sync as spawnSync } from 'cross-spawn';
7
+ import { jscodeshiftToPrettierParser } from './lib/utils';
8
+
9
+ export {
10
+ default as updateOrganisationName,
11
+ packageNames,
12
+ } from './transforms/update-organisation-name';
13
+
14
+ export { default as updateAddonInfo } from './transforms/update-addon-info';
15
+
16
+ const TRANSFORM_DIR = `${__dirname}/transforms`;
17
+
18
+ export function listCodemods() {
19
+ return fs
20
+ .readdirSync(TRANSFORM_DIR)
21
+ .filter((fname) => fname.endsWith('.js'))
22
+ .map((fname) => fname.slice(0, -3));
23
+ }
24
+
25
+ const renameAsync = promisify(fs.rename);
26
+
27
+ async function renameFile(file, from, to, { logger }) {
28
+ const newFile = file.replace(from, to);
29
+ logger.log(`Rename: ${file} ${newFile}`);
30
+ return renameAsync(file, newFile);
31
+ }
32
+
33
+ export async function runCodemod(codemod, { glob, logger, dryRun, rename, parser }) {
34
+ const codemods = listCodemods();
35
+ if (!codemods.includes(codemod)) {
36
+ throw new Error(`Unknown codemod ${codemod}. Run --list for options.`);
37
+ }
38
+
39
+ let renameParts = null;
40
+ if (rename) {
41
+ renameParts = rename.split(':');
42
+ if (renameParts.length !== 2) {
43
+ throw new Error(`Codemod rename: expected format "from:to", got "${rename}"`);
44
+ }
45
+ }
46
+
47
+ // jscodeshift/prettier know how to handle .ts/.tsx extensions,
48
+ // so if the user uses one of those globs, we can auto-infer
49
+ let inferredParser = parser;
50
+ if (!parser) {
51
+ const extension = path.extname(glob).slice(1);
52
+ const knownParser = jscodeshiftToPrettierParser(extension);
53
+ if (knownParser !== 'babel') inferredParser = extension;
54
+ }
55
+
56
+ const files = await globby([glob, '!**/node_modules', '!**/dist']);
57
+ logger.log(`=> Applying ${codemod}: ${files.length} files`);
58
+ if (!dryRun) {
59
+ const parserArgs = inferredParser ? ['--parser', inferredParser] : [];
60
+ spawnSync(
61
+ 'npx',
62
+ ['jscodeshift', '-t', `${TRANSFORM_DIR}/${codemod}.js`, ...parserArgs, ...files],
63
+ {
64
+ stdio: 'inherit',
65
+ shell: true,
66
+ }
67
+ );
68
+ }
69
+
70
+ if (renameParts) {
71
+ const [from, to] = renameParts;
72
+ logger.log(`=> Renaming ${rename}: ${files.length} files`);
73
+ await Promise.all(
74
+ files.map((file) => renameFile(file, new RegExp(`${from}$`), to, { logger }))
75
+ );
76
+ }
77
+ }
@@ -0,0 +1,9 @@
1
+ import { sanitizeName } from './utils';
2
+
3
+ it('should sanitize names', () => {
4
+ expect(sanitizeName('basic')).toMatchInlineSnapshot(`"Basic"`);
5
+ expect(sanitizeName('with space')).toMatchInlineSnapshot(`"WithSpace"`);
6
+ expect(sanitizeName('default')).toMatchInlineSnapshot(`"Default"`);
7
+ expect(sanitizeName('w/punctuation')).toMatchInlineSnapshot(`"WPunctuation"`);
8
+ expect(sanitizeName('5')).toMatchInlineSnapshot(`"_5"`);
9
+ });
@@ -1,30 +1,29 @@
1
1
  import camelCase from 'lodash/camelCase';
2
2
  import upperFirst from 'lodash/upperFirst';
3
- export var sanitizeName = function (name) {
4
- var key = upperFirst(camelCase(name)); // prepend _ if name starts with a digit
5
3
 
4
+ export const sanitizeName = (name: string) => {
5
+ let key = upperFirst(camelCase(name));
6
+ // prepend _ if name starts with a digit
6
7
  if (/^\d/.test(key)) {
7
8
  key = `_${key}`;
8
- } // prepend _ if name starts with a digit
9
-
10
-
9
+ }
10
+ // prepend _ if name starts with a digit
11
11
  if (/^\d/.test(key)) {
12
12
  key = `_${key}`;
13
13
  }
14
-
15
14
  return key;
16
15
  };
17
- export function jscodeshiftToPrettierParser(parser) {
18
- var parserMap = {
16
+
17
+ export function jscodeshiftToPrettierParser(parser?: string) {
18
+ const parserMap: Record<string, string> = {
19
19
  babylon: 'babel',
20
20
  flow: 'flow',
21
21
  ts: 'typescript',
22
- tsx: 'typescript'
22
+ tsx: 'typescript',
23
23
  };
24
24
 
25
25
  if (!parser) {
26
26
  return 'babel';
27
27
  }
28
-
29
28
  return parserMap[parser] || 'babel';
30
- }
29
+ }
@@ -0,0 +1,44 @@
1
+ /* eslint-disable */
2
+ import React from 'react';
3
+ import Button from './Button';
4
+
5
+ import { storiesOf, configure } from '@storybook/react';
6
+ import { action } from '@storybook/addon-actions';
7
+
8
+ storiesOf('Button', module).add('basic', () => <Button label="The Button" />);
9
+
10
+ storiesOf('Button').add('no module', () => <Button label="The Button" />);
11
+
12
+ storiesOf('Button', module).add('with story parameters', () => <Button label="The Button" />, {
13
+ header: false,
14
+ inline: true,
15
+ });
16
+
17
+ storiesOf('Button', module)
18
+ .addParameters({ foo: 1 })
19
+ .add('with kind parameters', () => <Button label="The Button" />);
20
+
21
+ storiesOf('Button', module)
22
+ .addParameters({ component: Button })
23
+ .add('with existing component parameters', () => <Button label="The Button" />);
24
+
25
+ storiesOf('Button', module).add('complex story', () => (
26
+ <div>
27
+ <Button label="The Button" onClick={action('onClick')} />
28
+ <br />
29
+ </div>
30
+ ));
31
+
32
+ storiesOf('Root|Some/Button', module).add('with path', () => <Button label="The Button" />);
33
+
34
+ storiesOf('Some.Button', module).add('with dot-path', () => <Button label="The Button" />);
35
+
36
+ storiesOf('Some.Button', module)
37
+ .addDecorator(withKnobs)
38
+ .add('with decorator', () => <Button label="The Button" />);
39
+
40
+ // This isn't a valid story, but it tests the `import { comp } from ...` case
41
+ storiesOf('action', module).add('non-default component export', () => <action />);
42
+
43
+ // This shouldn't get modified since the story name doesn't match
44
+ storiesOf('something', module).add('non-matching story', () => <Button label="The Button" />);
@@ -0,0 +1,68 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`add-component-parameters transforms correctly using "add-component-parameters.input.js" data 1`] = `
4
+ "/* eslint-disable */
5
+ import React from 'react';
6
+ import Button from './Button';
7
+
8
+ import { storiesOf, configure } from '@storybook/react';
9
+ import { action } from '@storybook/addon-actions';
10
+
11
+ storiesOf('Button', module).addParameters({
12
+ component: Button
13
+ }).add('basic', () => <Button label=\\"The Button\\" />);
14
+
15
+ storiesOf('Button').addParameters({
16
+ component: Button
17
+ }).add('no module', () => <Button label=\\"The Button\\" />);
18
+
19
+ storiesOf('Button', module).addParameters({
20
+ component: Button
21
+ }).add('with story parameters', () => <Button label=\\"The Button\\" />, {
22
+ header: false,
23
+ inline: true,
24
+ });
25
+
26
+ storiesOf('Button', module).addParameters({
27
+ component: Button
28
+ })
29
+ .addParameters({ foo: 1 })
30
+ .add('with kind parameters', () => <Button label=\\"The Button\\" />);
31
+
32
+ storiesOf('Button', module).addParameters({
33
+ component: Button
34
+ })
35
+ .addParameters({ component: Button })
36
+ .add('with existing component parameters', () => <Button label=\\"The Button\\" />);
37
+
38
+ storiesOf('Button', module).addParameters({
39
+ component: Button
40
+ }).add('complex story', () => (
41
+ <div>
42
+ <Button label=\\"The Button\\" onClick={action('onClick')} />
43
+ <br />
44
+ </div>
45
+ ));
46
+
47
+ storiesOf('Root|Some/Button', module).addParameters({
48
+ component: Button
49
+ }).add('with path', () => <Button label=\\"The Button\\" />);
50
+
51
+ storiesOf('Some.Button', module).addParameters({
52
+ component: Button
53
+ }).add('with dot-path', () => <Button label=\\"The Button\\" />);
54
+
55
+ storiesOf('Some.Button', module).addParameters({
56
+ component: Button
57
+ })
58
+ .addDecorator(withKnobs)
59
+ .add('with decorator', () => <Button label=\\"The Button\\" />);
60
+
61
+ // This isn't a valid story, but it tests the \`import { comp } from ...\` case
62
+ storiesOf('action', module).addParameters({
63
+ component: action
64
+ }).add('non-default component export', () => <action />);
65
+
66
+ // This shouldn't get modified since the story name doesn't match
67
+ storiesOf('something', module).add('non-matching story', () => <Button label=\\"The Button\\" />);"
68
+ `;