@atlaskit/codemod-cli 0.24.2 → 0.24.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +338 -323
  2. package/dist/cjs/main.js +1 -1
  3. package/dist/cjs/presets/migrate-to-new-buttons/codemods/next-migrate-to-new-button-variants.js +154 -56
  4. package/dist/cjs/presets/migrate-to-new-buttons/codemods/next-split-imports.js +18 -4
  5. package/dist/cjs/presets/migrate-to-new-buttons/utils/add-comment-for-custom-theme-buttons.js +1 -1
  6. package/dist/cjs/presets/migrate-to-new-buttons/utils/constants.js +16 -3
  7. package/dist/cjs/presets/migrate-to-new-buttons/utils/get-default-imports.js +14 -0
  8. package/dist/cjs/presets/migrate-to-new-buttons/utils/get-specifier-names.js +14 -0
  9. package/dist/cjs/presets/migrate-to-new-buttons/utils/if-variant-already-imported.js +15 -7
  10. package/dist/cjs/presets/migrate-to-new-buttons/utils/rename-elements.js +18 -0
  11. package/dist/es2019/presets/migrate-to-new-buttons/codemods/next-migrate-to-new-button-variants.js +143 -52
  12. package/dist/es2019/presets/migrate-to-new-buttons/codemods/next-split-imports.js +18 -4
  13. package/dist/es2019/presets/migrate-to-new-buttons/utils/add-comment-for-custom-theme-buttons.js +1 -1
  14. package/dist/es2019/presets/migrate-to-new-buttons/utils/constants.js +16 -2
  15. package/dist/es2019/presets/migrate-to-new-buttons/utils/get-default-imports.js +6 -0
  16. package/dist/es2019/presets/migrate-to-new-buttons/utils/get-specifier-names.js +6 -0
  17. package/dist/es2019/presets/migrate-to-new-buttons/utils/if-variant-already-imported.js +9 -3
  18. package/dist/es2019/presets/migrate-to-new-buttons/utils/rename-elements.js +12 -0
  19. package/dist/esm/main.js +1 -1
  20. package/dist/esm/presets/migrate-to-new-buttons/codemods/next-migrate-to-new-button-variants.js +154 -57
  21. package/dist/esm/presets/migrate-to-new-buttons/codemods/next-split-imports.js +18 -4
  22. package/dist/esm/presets/migrate-to-new-buttons/utils/add-comment-for-custom-theme-buttons.js +1 -1
  23. package/dist/esm/presets/migrate-to-new-buttons/utils/constants.js +15 -2
  24. package/dist/esm/presets/migrate-to-new-buttons/utils/get-default-imports.js +8 -0
  25. package/dist/esm/presets/migrate-to-new-buttons/utils/get-specifier-names.js +8 -0
  26. package/dist/esm/presets/migrate-to-new-buttons/utils/if-variant-already-imported.js +14 -6
  27. package/dist/esm/presets/migrate-to-new-buttons/utils/rename-elements.js +12 -0
  28. package/dist/types/main.d.ts +1 -1
  29. package/dist/types/presets/migrate-to-new-buttons/utils/constants.d.ts +11 -1
  30. package/dist/types/presets/migrate-to-new-buttons/utils/get-default-imports.d.ts +5 -0
  31. package/dist/types/presets/migrate-to-new-buttons/utils/get-specifier-names.d.ts +5 -0
  32. package/dist/types/presets/migrate-to-new-buttons/utils/if-variant-already-imported.d.ts +2 -1
  33. package/dist/types/presets/migrate-to-new-buttons/utils/migrate-fit-container-icon-button.d.ts +1 -1
  34. package/dist/types/presets/migrate-to-new-buttons/utils/rename-elements.d.ts +2 -0
  35. package/dist/types/presets/styled-to-emotion/styled-to-emotion.d.ts +1 -1
  36. package/dist/types/presets/theme-remove-deprecated-mixins/theme-remove-deprecated-mixins.d.ts +1 -1
  37. package/dist/types/presets/upgrade-pragmatic-drag-and-drop-to-stable/upgrade-pragmatic-drag-and-drop-to-stable.d.ts +1 -1
  38. package/dist/types/sinceRef.d.ts +1 -1
  39. package/dist/types/transforms.d.ts +2 -2
  40. package/dist/types/types.d.ts +1 -1
  41. package/dist/types-ts4.5/main.d.ts +1 -1
  42. package/dist/types-ts4.5/presets/migrate-to-new-buttons/utils/constants.d.ts +11 -1
  43. package/dist/types-ts4.5/presets/migrate-to-new-buttons/utils/get-default-imports.d.ts +5 -0
  44. package/dist/types-ts4.5/presets/migrate-to-new-buttons/utils/get-specifier-names.d.ts +5 -0
  45. package/dist/types-ts4.5/presets/migrate-to-new-buttons/utils/if-variant-already-imported.d.ts +2 -1
  46. package/dist/types-ts4.5/presets/migrate-to-new-buttons/utils/migrate-fit-container-icon-button.d.ts +1 -1
  47. package/dist/types-ts4.5/presets/migrate-to-new-buttons/utils/rename-elements.d.ts +2 -0
  48. package/dist/types-ts4.5/presets/styled-to-emotion/styled-to-emotion.d.ts +1 -1
  49. package/dist/types-ts4.5/presets/theme-remove-deprecated-mixins/theme-remove-deprecated-mixins.d.ts +1 -1
  50. package/dist/types-ts4.5/presets/upgrade-pragmatic-drag-and-drop-to-stable/upgrade-pragmatic-drag-and-drop-to-stable.d.ts +1 -1
  51. package/dist/types-ts4.5/sinceRef.d.ts +1 -1
  52. package/dist/types-ts4.5/transforms.d.ts +2 -2
  53. package/dist/types-ts4.5/types.d.ts +1 -1
  54. package/package.json +2 -4
@@ -1,6 +1,8 @@
1
1
  import { addCommentBefore } from '@atlaskit/codemod-utils';
2
2
  import { getDefaultImportSpecifierName } from '@hypermod/utils';
3
- import { PRINT_SETTINGS, NEW_BUTTON_VARIANTS, entryPointsMapping, NEW_BUTTON_ENTRY_POINT, linkButtonMissingHrefComment, buttonPropsNoLongerSupportedComment, unsupportedProps } from '../utils/constants';
3
+ import { PRINT_SETTINGS, OLD_BUTTON_VARIANTS, NEW_BUTTON_VARIANTS, entryPointsMapping, OLD_BUTTON_ENTRY_POINT, NEW_BUTTON_ENTRY_POINT, linkButtonMissingHrefComment, buttonPropsNoLongerSupportedComment, unsupportedProps, loadingButtonComment } from '../utils/constants';
4
+ import getDefaultImports from '../utils/get-default-imports';
5
+ import getSpecifierNames from '../utils/get-specifier-names';
4
6
  import { generateNewElement, handleIconAttributes } from '../utils/generate-new-button-element';
5
7
  import { ifHasUnsupportedProps } from '../utils/has-unsupported-props';
6
8
  import { checkIfVariantAlreadyImported } from '../utils/if-variant-already-imported';
@@ -12,23 +14,38 @@ const transformer = (file, api) => {
12
14
  const j = api.jscodeshift;
13
15
  const fileSource = j(file.source);
14
16
  addCommentForCustomThemeButtons(fileSource, j);
15
- const buttonImports = fileSource.find(j.ImportDeclaration).filter(path => path.node.source.value === entryPointsMapping.Button || path.node.source.value === entryPointsMapping.LoadingButton || path.node.source.value === NEW_BUTTON_ENTRY_POINT);
16
- if (!buttonImports.length) {
17
- return fileSource.toSource();
18
- }
19
- const defaultButtonImport = buttonImports.find(j.Specifier).filter(path => path.node.type === 'ImportDefaultSpecifier');
20
- if (defaultButtonImport.length === 0) {
17
+
18
+ // Find old buttons
19
+ const oldButtonImports = fileSource.find(j.ImportDeclaration).filter(path => path.node.source.value === entryPointsMapping.Button || path.node.source.value === entryPointsMapping.LoadingButton || path.node.source.value === OLD_BUTTON_ENTRY_POINT);
20
+ if (!oldButtonImports.length) {
21
21
  return fileSource.toSource();
22
22
  }
23
- const specifierIdentifier = defaultButtonImport.get(0).node.local.name;
24
- let hasLinkIconButton = checkIfVariantAlreadyImported(NEW_BUTTON_VARIANTS.linkIcon, fileSource, j);
25
- let hasLinkButton = checkIfVariantAlreadyImported(NEW_BUTTON_VARIANTS.link, fileSource, j);
26
- let hasIconButton = checkIfVariantAlreadyImported(NEW_BUTTON_VARIANTS.icon, fileSource, j);
27
- let hasDefaultLoadingButton = false;
28
- const allButtons = fileSource.find(j.JSXElement).filter(path => path.value.openingElement.name.type === 'JSXIdentifier' && path.value.openingElement.name.name === specifierIdentifier);
29
- const buttonsWithoutUnsupportedProps = allButtons.filter(path => !ifHasUnsupportedProps(path.value.openingElement.attributes));
30
- const loadingButtonImportName = getDefaultImportSpecifierName(j, fileSource, entryPointsMapping.LoadingButton);
31
- buttonsWithoutUnsupportedProps.forEach(element => {
23
+ const oldDefaultDefaultImports = getDefaultImports(oldButtonImports, j);
24
+ const oldDefaultImportSpecifiers = getSpecifierNames(oldDefaultDefaultImports);
25
+
26
+ // Find new buttons
27
+ const newButtonImports = fileSource.find(j.ImportDeclaration).filter(path => path.node.source.value === NEW_BUTTON_ENTRY_POINT);
28
+ const newDefaultDefaultImports = getDefaultImports(newButtonImports, j);
29
+ const newDefaultImportSpecifiers = getSpecifierNames(newDefaultDefaultImports);
30
+ const loadingButtonDirectImportName = getDefaultImportSpecifierName(j, fileSource, entryPointsMapping.LoadingButton);
31
+
32
+ /**
33
+ * Which variants should be in this file?
34
+ *
35
+ * Any variants enabled here will be added to the final import statement.
36
+ * Initial values check if the variant is already imported in the file.
37
+ */
38
+ let hasVariant = {
39
+ defaultButton: checkIfVariantAlreadyImported(NEW_BUTTON_VARIANTS.default, NEW_BUTTON_ENTRY_POINT, fileSource, j, true),
40
+ linkButton: checkIfVariantAlreadyImported(NEW_BUTTON_VARIANTS.link, NEW_BUTTON_ENTRY_POINT, fileSource, j),
41
+ iconButton: checkIfVariantAlreadyImported(NEW_BUTTON_VARIANTS.icon, NEW_BUTTON_ENTRY_POINT, fileSource, j),
42
+ linkIconButton: checkIfVariantAlreadyImported(NEW_BUTTON_VARIANTS.linkIcon, NEW_BUTTON_ENTRY_POINT, fileSource, j)
43
+ };
44
+ const oldButtonElements = fileSource.find(j.JSXElement).filter(path => {
45
+ return path.value.openingElement.name.type === 'JSXIdentifier' && ((oldDefaultImportSpecifiers === null || oldDefaultImportSpecifiers === void 0 ? void 0 : oldDefaultImportSpecifiers.includes(path.value.openingElement.name.name)) || Object.values(OLD_BUTTON_VARIANTS).includes(path.value.openingElement.name.name));
46
+ });
47
+ const oldButtonsWithoutUnsupportedProps = oldButtonElements.filter(path => !ifHasUnsupportedProps(path.value.openingElement.attributes));
48
+ oldButtonsWithoutUnsupportedProps.forEach(element => {
32
49
  var _element$value$childr;
33
50
  const {
34
51
  attributes
@@ -44,9 +61,14 @@ const transformer = (file, api) => {
44
61
  const isLinkIconButton = hasHref && hasIcon && hasNoChildren && !isFitContainerIconButton;
45
62
  const isLinkButton = hasHref && !isLinkIconButton;
46
63
  let isIconButton = !hasHref && hasIcon && hasNoChildren && !isFitContainerIconButton;
47
- const isDefaultButtonWithAnIcon = !isLinkIconButton && !isIconButton && !isFitContainerIconButton && hasIcon;
48
- const isLoadingButton = element.value.openingElement.name.type === 'JSXIdentifier' && element.value.openingElement.name.name === loadingButtonImportName;
49
- if (isDefaultButtonWithAnIcon) {
64
+ const isDefaultButton = !isLinkButton && !isLinkIconButton && !isIconButton && !isFitContainerIconButton;
65
+ const isDefaultVariantWithAnIcon = !isLinkIconButton && !isIconButton && !isFitContainerIconButton && hasIcon;
66
+ const isLoadingButton = element.value.openingElement.name.type === 'JSXIdentifier' && element.value.openingElement.name.name === loadingButtonDirectImportName;
67
+ const linkAppearanceAttribute = attributes.find(node => {
68
+ var _node$value, _node$name, _node$value2, _node$value3;
69
+ return node.type === 'JSXAttribute' && ((_node$value = node.value) === null || _node$value === void 0 ? void 0 : _node$value.type) === 'StringLiteral' && (node === null || node === void 0 ? void 0 : (_node$name = node.name) === null || _node$name === void 0 ? void 0 : _node$name.name) === 'appearance' && ((node === null || node === void 0 ? void 0 : (_node$value2 = node.value) === null || _node$value2 === void 0 ? void 0 : _node$value2.value) === 'link' || (node === null || node === void 0 ? void 0 : (_node$value3 = node.value) === null || _node$value3 === void 0 ? void 0 : _node$value3.value) === 'subtle-link');
70
+ });
71
+ if (isDefaultVariantWithAnIcon) {
50
72
  handleIconAttributes(element.value, j);
51
73
  }
52
74
  if (isFitContainerIconButton) {
@@ -55,66 +77,97 @@ const transformer = (file, api) => {
55
77
  isIconButton = true;
56
78
  }
57
79
  }
58
- if (isLinkIconButton) {
59
- hasLinkIconButton = true;
80
+ if (isLinkIconButton && !isLoadingButton) {
81
+ hasVariant.linkIconButton = true;
60
82
  j(element).replaceWith(generateNewElement(NEW_BUTTON_VARIANTS.linkIcon, element.value, j));
61
83
  }
62
- if (isIconButton) {
63
- hasIconButton = true;
84
+ if (isIconButton && !isLoadingButton) {
85
+ hasVariant.iconButton = true;
64
86
  j(element).replaceWith(generateNewElement(NEW_BUTTON_VARIANTS.icon, element.value, j));
65
87
  }
66
- if (isLinkButton) {
67
- hasLinkButton = true;
88
+ if (isLinkButton && !isLoadingButton) {
89
+ hasVariant.linkButton = true;
68
90
  j(element).replaceWith(generateNewElement(NEW_BUTTON_VARIANTS.link, element.value, j));
69
91
  }
70
- if (isLoadingButton && !isIconButton) {
71
- hasDefaultLoadingButton = true;
72
- j(element).replaceWith(generateNewElement(NEW_BUTTON_VARIANTS.default, element.value, j));
92
+ if (isDefaultButton && !isLoadingButton) {
93
+ hasVariant.defaultButton = true;
94
+ j(element).replaceWith(generateNewElement(newDefaultImportSpecifiers !== null && newDefaultImportSpecifiers !== void 0 && newDefaultImportSpecifiers.length ?
95
+ // If new button already has a default import, use that incase it's aliased
96
+ newDefaultImportSpecifiers[0] : NEW_BUTTON_VARIANTS.default, element.value, j));
73
97
  }
74
- const linkAppearanceAttribute = attributes.find(node => {
75
- var _node$value, _node$name, _node$value2, _node$value3;
76
- return node.type === 'JSXAttribute' && ((_node$value = node.value) === null || _node$value === void 0 ? void 0 : _node$value.type) === 'StringLiteral' && (node === null || node === void 0 ? void 0 : (_node$name = node.name) === null || _node$name === void 0 ? void 0 : _node$name.name) === 'appearance' && ((node === null || node === void 0 ? void 0 : (_node$value2 = node.value) === null || _node$value2 === void 0 ? void 0 : _node$value2.value) === 'link' || (node === null || node === void 0 ? void 0 : (_node$value3 = node.value) === null || _node$value3 === void 0 ? void 0 : _node$value3.value) === 'subtle-link');
77
- });
78
- if (!hasHref && linkAppearanceAttribute) {
98
+ if (isLoadingButton) {
99
+ const newElement = generateNewElement(NEW_BUTTON_VARIANTS[isIconButton || isLinkIconButton ? 'icon' : 'default'], element.value, j);
100
+ if (isIconButton || isLinkIconButton) {
101
+ hasVariant.iconButton = true;
102
+ } else {
103
+ hasVariant.defaultButton = true;
104
+
105
+ // rename existing Button to LegacyButton
106
+ const existingDefaultButtonSpecifier = fileSource.find(j.ImportDefaultSpecifier).filter(path => {
107
+ var _path$value$local;
108
+ return ((_path$value$local = path.value.local) === null || _path$value$local === void 0 ? void 0 : _path$value$local.name) === NEW_BUTTON_VARIANTS.default;
109
+ });
110
+ if (existingDefaultButtonSpecifier.length > 0) {
111
+ fileSource.find(j.JSXElement).filter(path => path.value.openingElement.name.type === 'JSXIdentifier' && path.value.openingElement.name.name === NEW_BUTTON_VARIANTS.default).forEach(element => {
112
+ var _element$value$childr2, _element$value$childr3;
113
+ // find all default <Button> JSX elements and replace with <LegacyButton>
114
+ j(element).replaceWith(j.jsxElement(j.jsxOpeningElement(j.jsxIdentifier('LegacyButton'), element.value.openingElement.attributes, ((_element$value$childr2 = element.value.children) === null || _element$value$childr2 === void 0 ? void 0 : _element$value$childr2.length) === 0), ((_element$value$childr3 = element.value.children) === null || _element$value$childr3 === void 0 ? void 0 : _element$value$childr3.length) === 0 ? null : j.jsxClosingElement(j.jsxIdentifier('LegacyButton')), element.value.children));
115
+ });
116
+
117
+ // rename Button to LegacyButton in all call expressions i.e. render(Button), find(Button)
118
+ fileSource.find(j.CallExpression).find(j.Identifier).forEach(path => {
119
+ if (path.node.name === NEW_BUTTON_VARIANTS.default) {
120
+ path.node.name = 'LegacyButton';
121
+ }
122
+ });
123
+
124
+ // rename Button to LegacyButton in import declaration
125
+ existingDefaultButtonSpecifier.forEach(specifier => j(specifier).replaceWith(j.importDefaultSpecifier(j.identifier('LegacyButton'))));
126
+ }
127
+ }
128
+ j(element).replaceWith(newElement);
129
+ if (hasHref || linkAppearanceAttribute) {
130
+ addCommentBefore(j, j(newElement), loadingButtonComment({
131
+ hasHref,
132
+ hasLinkAppearance: Boolean(linkAppearanceAttribute)
133
+ }), 'block');
134
+ }
135
+ } else if (!hasHref && linkAppearanceAttribute) {
79
136
  addCommentBefore(j, j(linkAppearanceAttribute), linkButtonMissingHrefComment, 'line');
80
137
  }
81
138
  });
82
139
 
83
140
  // modify import declarations
84
141
  let specifiers = [];
85
- [hasLinkButton ? 'link' : null, hasIconButton ? 'icon' : null, hasLinkIconButton ? 'linkIcon' : null].forEach(variant => {
142
+ [hasVariant.linkButton ? 'link' : null, hasVariant.iconButton ? 'icon' : null, hasVariant.linkIconButton ? 'linkIcon' : null].forEach(variant => {
86
143
  if (variant) {
87
144
  specifiers.push(j.importSpecifier(j.identifier(NEW_BUTTON_VARIANTS[variant])));
88
145
  }
89
146
  });
90
- const oldButtonImport = fileSource.find(j.ImportDeclaration).filter(path => path.node.source.value === NEW_BUTTON_ENTRY_POINT || path.node.source.value === entryPointsMapping.Button || path.node.source.value === entryPointsMapping.LoadingButton);
91
- const remainingDefaultButtons = fileSource.find(j.JSXElement).filter(path => path.value.openingElement.name.type === 'JSXIdentifier' && path.value.openingElement.name.name === specifierIdentifier && !ifHasUnsupportedProps(path.value.openingElement.attributes)).length > 0 || fileSource.find(j.CallExpression).filter(path => path.node.arguments.map(argument => argument.type === 'Identifier' && (argument === null || argument === void 0 ? void 0 : argument.name)).includes(specifierIdentifier)).length > 0;
147
+ const remainingDefaultButtons = fileSource.find(j.JSXElement).filter(path => path.value.openingElement.name.type === 'JSXIdentifier' && Boolean(oldDefaultImportSpecifiers === null || oldDefaultImportSpecifiers === void 0 ? void 0 : oldDefaultImportSpecifiers.includes(path.value.openingElement.name.name)) && !ifHasUnsupportedProps(path.value.openingElement.attributes)).length > 0 || fileSource.find(j.CallExpression).filter(path => path.node.arguments.map(argument => argument.type === 'Identifier' && (argument === null || argument === void 0 ? void 0 : argument.name)).some(name => name && (oldDefaultImportSpecifiers === null || oldDefaultImportSpecifiers === void 0 ? void 0 : oldDefaultImportSpecifiers.includes(name)))).length > 0;
92
148
 
93
149
  // Loading buttons map back to default imports
94
- if (specifierIdentifier === loadingButtonImportName) {
150
+ if (loadingButtonDirectImportName && oldDefaultImportSpecifiers !== null && oldDefaultImportSpecifiers !== void 0 && oldDefaultImportSpecifiers.includes(loadingButtonDirectImportName)) {
95
151
  fileSource
96
152
  // rename LoadingButton to Button in all call expressions i.e. render(LoadingButton), find(LoadingButton)
97
153
  .find(j.CallExpression).find(j.Identifier).forEach(path => {
98
- if (path.node.name === loadingButtonImportName) {
154
+ if (path.node.name === loadingButtonDirectImportName) {
99
155
  path.node.name = NEW_BUTTON_VARIANTS.default;
100
156
  }
101
157
  });
102
-
103
- // Only add the Button import if we found a default button, not icon only
104
- if (hasDefaultLoadingButton) {
105
- specifiers.push(j.importDefaultSpecifier(j.identifier(NEW_BUTTON_VARIANTS.default)));
106
- }
107
158
  }
108
- if (!specifiers.find(specifier => specifier.type === 'ImportDefaultSpecifier') && remainingDefaultButtons) {
159
+
160
+ // Only add the Button import if we found a default button, not icon only
161
+ if (hasVariant.defaultButton || !specifiers.find(specifier => specifier.type === 'ImportDefaultSpecifier') && remainingDefaultButtons) {
109
162
  specifiers.push(j.importDefaultSpecifier(j.identifier(NEW_BUTTON_VARIANTS.default)));
110
163
  }
111
164
 
112
165
  // update import path for types imports
113
- specifiers = importTypesFromNewEntryPoint(buttonImports, specifiers, j, fileSource);
114
- const buttonsWithUnsupportedProps = allButtons.filter(path => ifHasUnsupportedProps(path.value.openingElement.attributes));
115
- if (buttonsWithUnsupportedProps.length) {
166
+ specifiers = importTypesFromNewEntryPoint(oldButtonImports, specifiers, j, fileSource);
167
+ const oldButtonsWithUnsupportedProps = oldButtonElements.filter(path => ifHasUnsupportedProps(path.value.openingElement.attributes));
168
+ if (oldButtonsWithUnsupportedProps.length) {
116
169
  // add comment to all buttons with unsupported props: "component", "style", "css"
117
- buttonsWithUnsupportedProps.forEach(element => {
170
+ oldButtonsWithUnsupportedProps.forEach(element => {
118
171
  var _element$value$openin;
119
172
  const attribute = (_element$value$openin = element.value.openingElement.attributes) === null || _element$value$openin === void 0 ? void 0 : _element$value$openin.find(node => node.type === 'JSXAttribute' && typeof node.name.name === 'string' && unsupportedProps.includes(node.name.name));
120
173
  if (attribute) {
@@ -124,20 +177,58 @@ const transformer = (file, api) => {
124
177
 
125
178
  // rename all buttons with unsupported props to LegacyButton if default new button is imported
126
179
  if (specifiers.find(specifier => specifier.type === 'ImportDefaultSpecifier')) {
127
- renameDefaultButtonToLegacyButtonImport(oldButtonImport, buttonsWithUnsupportedProps, j);
180
+ renameDefaultButtonToLegacyButtonImport(oldButtonImports, oldButtonsWithUnsupportedProps, j);
128
181
  }
129
182
  }
130
183
  if (specifiers.length) {
131
- const newButtonImport = j.importDeclaration(specifiers, j.stringLiteral(NEW_BUTTON_ENTRY_POINT));
132
- oldButtonImport.forEach(path => {
184
+ const existingNewButtonImports = fileSource.find(j.ImportDeclaration).filter(path => path.node.source.value === NEW_BUTTON_ENTRY_POINT);
185
+ let newButtonImport = j.importDeclaration(specifiers, j.stringLiteral(NEW_BUTTON_ENTRY_POINT));
186
+ oldButtonImports.forEach(path => {
133
187
  var _path$node;
134
188
  newButtonImport.comments = path !== null && path !== void 0 && (_path$node = path.node) !== null && _path$node !== void 0 && _path$node.comments ? path.node.comments : undefined;
135
189
  });
136
- oldButtonImport.replaceWith(newButtonImport);
190
+ if (existingNewButtonImports.length) {
191
+ existingNewButtonImports.forEach(path => {
192
+ // Merge specifiers from existing new button import with added specifiers
193
+ const mergedSpecifiers = [...specifiers.filter(specifier => {
194
+ var _path$node$specifiers;
195
+ if (specifier.type !== 'ImportDefaultSpecifier') {
196
+ return true;
197
+ }
198
+
199
+ // Ensure we don't add a duplicate default specifier if one is already imported
200
+ return !((_path$node$specifiers = path.node.specifiers) !== null && _path$node$specifiers !== void 0 && _path$node$specifiers.find(s => s.type === 'ImportDefaultSpecifier'));
201
+ }), ...(path.node.specifiers ? path.node.specifiers : [])]
202
+ // Filter duplicates
203
+ .filter(specifier => {
204
+ if (specifier.type === 'ImportDefaultSpecifier') {
205
+ return true;
206
+ }
207
+ const isAlreadyImported = specifiers.find(s => {
208
+ var _specifier$local;
209
+ return s.type === 'ImportSpecifier' && s.imported.name === ((_specifier$local = specifier.local) === null || _specifier$local === void 0 ? void 0 : _specifier$local.name);
210
+ });
211
+ return !isAlreadyImported;
212
+ });
213
+ newButtonImport = j.importDeclaration(mergedSpecifiers, j.stringLiteral(NEW_BUTTON_ENTRY_POINT));
214
+ });
215
+ oldButtonImports.remove();
216
+ existingNewButtonImports.replaceWith(newButtonImport);
217
+ } else {
218
+ oldButtonImports.replaceWith((_, index) => {
219
+ // Only replace the first import
220
+ if (index === 0) {
221
+ return newButtonImport;
222
+ }
223
+ // Remove the rest
224
+ return null;
225
+ });
226
+ }
137
227
  }
138
228
 
139
229
  // remove empty import declarations
140
230
  fileSource.find(j.ImportDeclaration).filter(path => (path.node.source.value === '@atlaskit/button' || path.node.source.value === '@atlaskit/button/types') && !!path.node.specifiers && path.node.specifiers.length === 0).remove();
231
+ addCommentForCustomThemeButtons(fileSource, j);
141
232
  return fileSource.toSource(PRINT_SETTINGS);
142
233
  };
143
234
  export default transformer;
@@ -1,4 +1,6 @@
1
1
  import { PRINT_SETTINGS, entryPointsMapping, BUTTON_TYPES, NEW_BUTTON_ENTRY_POINT } from '../utils/constants';
2
+ import renameElements from '../utils/rename-elements';
3
+ import { getDefaultImportSpecifierName } from '@hypermod/utils';
2
4
  const transformer = (file, api) => {
3
5
  const j = api.jscodeshift;
4
6
  const fileSource = j(file.source);
@@ -21,8 +23,14 @@ const transformer = (file, api) => {
21
23
  }
22
24
  const defaultSpecifier = specifiers === null || specifiers === void 0 ? void 0 : specifiers.find(specifier => specifier.type === 'ImportDefaultSpecifier');
23
25
  if (defaultSpecifier && defaultSpecifier.local) {
24
- const defaultButtonImport = j.importDeclaration([j.importDefaultSpecifier(j.identifier(defaultSpecifier.local.name))], j.stringLiteral(entryPointsMapping.Button));
25
- j(node).insertAfter(defaultButtonImport);
26
+ const existingSpecifier = getDefaultImportSpecifierName(j, fileSource, entryPointsMapping.Button);
27
+ if (existingSpecifier) {
28
+ var _defaultSpecifier$loc;
29
+ renameElements((_defaultSpecifier$loc = defaultSpecifier.local) === null || _defaultSpecifier$loc === void 0 ? void 0 : _defaultSpecifier$loc.name, existingSpecifier, fileSource, j);
30
+ } else {
31
+ const newImport = j.importDeclaration([j.importDefaultSpecifier(j.identifier(defaultSpecifier.local.name))], j.stringLiteral(entryPointsMapping.Button));
32
+ j(node).insertAfter(newImport);
33
+ }
26
34
  }
27
35
  const namedSpecifiers = specifiers === null || specifiers === void 0 ? void 0 : specifiers.filter(specifier => specifier.type === 'ImportSpecifier');
28
36
  const newTypeSpecifier = namedSpecifiers === null || namedSpecifiers === void 0 ? void 0 : namedSpecifiers.filter(specifier => specifier.type === 'ImportSpecifier' && (specifier.imported.name === 'Appearance' || specifier.imported.name === 'Spacing'));
@@ -38,8 +46,14 @@ const transformer = (file, api) => {
38
46
  if (namedSpecifiers !== null && namedSpecifiers !== void 0 && namedSpecifiers.length) {
39
47
  namedSpecifiers.forEach(specifier => {
40
48
  if (specifier.local && specifier.type === 'ImportSpecifier' && specifier.local.name && entryPointsMapping[specifier.imported.name]) {
41
- const newImport = j.importDeclaration([j.importDefaultSpecifier(j.identifier(specifier.local.name))], j.stringLiteral(entryPointsMapping[specifier.imported.name]));
42
- j(node).insertAfter(newImport);
49
+ const existingSpecifier = getDefaultImportSpecifierName(j, fileSource, entryPointsMapping[specifier.imported.name]);
50
+ if (existingSpecifier) {
51
+ var _specifier$local;
52
+ renameElements((_specifier$local = specifier.local) === null || _specifier$local === void 0 ? void 0 : _specifier$local.name, existingSpecifier, fileSource, j);
53
+ } else {
54
+ const newImport = j.importDeclaration([j.importDefaultSpecifier(j.identifier(specifier.local.name))], j.stringLiteral(entryPointsMapping[specifier.imported.name]));
55
+ j(node).insertAfter(newImport);
56
+ }
43
57
  }
44
58
  });
45
59
  }
@@ -19,5 +19,5 @@ export const addCommentForCustomThemeButtons = (fileSource, j) => {
19
19
  if (!customThemeButtonElement.length) {
20
20
  return;
21
21
  }
22
- addCommentBefore(j, j(customThemeButtonElement.get(0).node.openingElement), customThemeButtonComment, 'line');
22
+ addCommentBefore(j, j(customThemeButtonElement.get(0).node.openingElement), customThemeButtonComment, 'block');
23
23
  };
@@ -1,13 +1,21 @@
1
1
  export const PRINT_SETTINGS = {
2
2
  quote: 'single'
3
3
  };
4
+
5
+ /** NEW button **/
6
+ export const NEW_BUTTON_ENTRY_POINT = '@atlaskit/button/new';
4
7
  export const NEW_BUTTON_VARIANTS = {
5
8
  default: 'Button',
6
9
  link: 'LinkButton',
7
10
  icon: 'IconButton',
8
11
  linkIcon: 'LinkIconButton'
9
12
  };
10
- export const NEW_BUTTON_ENTRY_POINT = '@atlaskit/button/new';
13
+
14
+ /** OLD button **/
15
+ export const OLD_BUTTON_ENTRY_POINT = '@atlaskit/button';
16
+ export const OLD_BUTTON_VARIANTS = {
17
+ loading: 'LoadingButton'
18
+ };
11
19
  export const entryPointsMapping = {
12
20
  Button: '@atlaskit/button/standard-button',
13
21
  LoadingButton: '@atlaskit/button/loading-button',
@@ -25,4 +33,10 @@ export const linkButtonMissingHrefComment = `"link" and "subtle-link" appearance
25
33
  export const buttonPropsNoLongerSupportedComment = `Buttons with "component", "css" or "style" prop can't be automatically migrated with codemods. Please migrate it manually.`;
26
34
  export const migrateFitContainerButtonToDefaultButtonComment = `Migrated to a default button with text which is from the icon label.`;
27
35
  export const migrateFitContainerButtonToIconButtonComment = `"shouldFitContainer" is not available in icon buttons, please consider using a default button with text.`;
28
- export const customThemeButtonComment = `CustomThemeButton will be deprecated. Please consider migrating to Pressable or Anchor Primitives with custom styles.`;
36
+ export const customThemeButtonComment = `CustomThemeButton will be deprecated. Please consider migrating to Pressable or Anchor Primitives with custom styles.`;
37
+ export const loadingButtonComment = ({
38
+ hasLinkAppearance,
39
+ hasHref
40
+ }) => {
41
+ return `This should be migrated to a new Button with a \`isLoading\` prop. ${hasLinkAppearance ? `"link" and "subtle-link" appearances are not available for new loading buttons."` : ''}${hasHref ? `The \`href\` attribute it not compatible with new loading buttons, because links should not need loading states.` : ''} Please reconsider the design or change the appearance of the button.`;
42
+ };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Returns default import specifiers from the given import declarations.
3
+ */
4
+ export default function getDefaultImports(importDeclarations, j) {
5
+ return importDeclarations.find(j.Specifier).filter(path => path.node.type === 'ImportDefaultSpecifier');
6
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Returns a list of specifier names from the given specifiers.
3
+ */
4
+ export default function getSpecifierNames(specifiers) {
5
+ return specifiers.length ? specifiers.paths().map(path => path.get().value.local.name) : undefined;
6
+ }
@@ -1,4 +1,10 @@
1
- import { NEW_BUTTON_ENTRY_POINT } from './constants';
2
- export const checkIfVariantAlreadyImported = (variant, fileSource, j) => {
3
- return fileSource.find(j.ImportDeclaration).filter(path => path.node.source.value === NEW_BUTTON_ENTRY_POINT).find(j.ImportSpecifier).filter(path => path.node.imported.name === variant).length > 0;
1
+ export const findVariantAlreadyImported = (variant, entryPoint, fileSource, j, isDefaultSpecifier = false) => {
2
+ const imports = fileSource.find(j.ImportDeclaration).filter(path => path.node.source.value === entryPoint);
3
+ if (isDefaultSpecifier) {
4
+ return imports.find(j.ImportDefaultSpecifier);
5
+ }
6
+ return imports.find(j.ImportSpecifier).filter(path => path.node.imported.name === variant);
7
+ };
8
+ export const checkIfVariantAlreadyImported = (variant, entryPoint, fileSource, j, isDefaultSpecifier = false) => {
9
+ return findVariantAlreadyImported(variant, entryPoint, fileSource, j, isDefaultSpecifier).length > 0;
4
10
  };
@@ -0,0 +1,12 @@
1
+ export default function renameElements(elementName, newElementName, fileSource, j) {
2
+ const oldElements = fileSource.find(j.JSXElement).filter(path => {
3
+ return path.value.openingElement.name.type === 'JSXIdentifier' && path.value.openingElement.name.name === elementName;
4
+ });
5
+
6
+ // Rename elements to match existing import name
7
+ oldElements.forEach(element => {
8
+ var _element$value$childr, _element$value$childr2;
9
+ const newElement = j.jsxElement(j.jsxOpeningElement(j.jsxIdentifier(newElementName), element.value.openingElement.attributes, ((_element$value$childr = element.value.children) === null || _element$value$childr === void 0 ? void 0 : _element$value$childr.length) === 0), ((_element$value$childr2 = element.value.children) === null || _element$value$childr2 === void 0 ? void 0 : _element$value$childr2.length) === 0 ? null : j.jsxClosingElement(j.jsxIdentifier(newElementName)), element.value.children);
10
+ j(element).replaceWith(newElement);
11
+ });
12
+ }
package/dist/esm/main.js CHANGED
@@ -298,7 +298,7 @@ function _main() {
298
298
  case 4:
299
299
  _yield$parseArgs = _context5.sent;
300
300
  packages = _yield$parseArgs.packages;
301
- _process$env$_PACKAGE = "0.24.2", _PACKAGE_VERSION_ = _process$env$_PACKAGE === void 0 ? '0.0.0-dev' : _process$env$_PACKAGE;
301
+ _process$env$_PACKAGE = "0.24.4", _PACKAGE_VERSION_ = _process$env$_PACKAGE === void 0 ? '0.0.0-dev' : _process$env$_PACKAGE;
302
302
  logger.log(chalk.bgBlue(chalk.black("\uD83D\uDCDA Atlassian-Frontend codemod library @ ".concat(_PACKAGE_VERSION_, " \uD83D\uDCDA"))));
303
303
  if (packages && packages.length > 0) {
304
304
  logger.log(chalk.gray("Searching for codemods for newer versions of the following packages: ".concat(packages.map(function (pkg) {