@atlaskit/codemod-cli 0.21.1 → 0.22.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @atlaskit/codemod-cli
2
2
 
3
+ ## 0.22.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#91603](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/91603) [`7cb9b3b277be`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/7cb9b3b277be) - Update button mod to prefer render props over UNSAFE\_ APIs
8
+
3
9
  ## 0.21.1
4
10
 
5
11
  ### Patch Changes
package/dist/cjs/main.js CHANGED
@@ -305,7 +305,7 @@ function _main() {
305
305
  case 4:
306
306
  _yield$parseArgs = _context5.sent;
307
307
  packages = _yield$parseArgs.packages;
308
- _process$env$_PACKAGE = "0.21.1", _PACKAGE_VERSION_ = _process$env$_PACKAGE === void 0 ? '0.0.0-dev' : _process$env$_PACKAGE;
308
+ _process$env$_PACKAGE = "0.22.0", _PACKAGE_VERSION_ = _process$env$_PACKAGE === void 0 ? '0.0.0-dev' : _process$env$_PACKAGE;
309
309
  logger.log(_chalk.default.bgBlue(_chalk.default.black("\uD83D\uDCDA Atlassian-Frontend codemod library @ ".concat(_PACKAGE_VERSION_, " \uD83D\uDCDA"))));
310
310
  if (packages && packages.length > 0) {
311
311
  logger.log(_chalk.default.gray("Searching for codemods for newer versions of the following packages: ".concat(packages.map(function (pkg) {
@@ -58,7 +58,7 @@ var transformer = function transformer(file, api) {
58
58
  var isDefaultButtonWithAnIcon = !isLinkIconButton && !isIconButton && !isFitContainerIconButton && hasIcon;
59
59
  var isLoadingButton = element.value.openingElement.name.type === 'JSXIdentifier' && element.value.openingElement.name.name === loadingButtonImportName;
60
60
  if (isDefaultButtonWithAnIcon) {
61
- (0, _generateNewButtonElement.moveSizeAndLabelAttributes)(element.value, j);
61
+ (0, _generateNewButtonElement.handleIconAttributes)(element.value, j);
62
62
  }
63
63
  if (isFitContainerIconButton) {
64
64
  var migratedToIconButton = (0, _migrateFitContainerIconButton.migrateFitContainerIconButton)(element, j);
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.unsupportedProps = exports.migrateFitContainerButtonToIconButtonComment = exports.migrateFitContainerButtonToDefaultButtonComment = exports.linkButtonMissingHrefComment = exports.iconPropsNoLongerSupportedComment = exports.entryPointsMapping = exports.buttonPropsNoLongerSupportedComment = exports.PRINT_SETTINGS = exports.NEW_BUTTON_VARIANTS = exports.NEW_BUTTON_ENTRY_POINT = exports.BUTTON_TYPES = void 0;
6
+ exports.unsupportedProps = exports.migrateFitContainerButtonToIconButtonComment = exports.migrateFitContainerButtonToDefaultButtonComment = exports.linkButtonMissingHrefComment = exports.entryPointsMapping = exports.buttonPropsNoLongerSupportedComment = exports.PRINT_SETTINGS = exports.NEW_BUTTON_VARIANTS = exports.NEW_BUTTON_ENTRY_POINT = exports.BUTTON_TYPES = void 0;
7
7
  var PRINT_SETTINGS = exports.PRINT_SETTINGS = {
8
8
  quote: 'single'
9
9
  };
@@ -23,7 +23,6 @@ var entryPointsMapping = exports.entryPointsMapping = {
23
23
  var BUTTON_TYPES = exports.BUTTON_TYPES = ['BaseOwnProps', 'BaseProps', 'ButtonProps', 'LoadingButtonProps', 'LoadingButtonOwnProps', 'ThemeTokens', 'ThemeProps', 'InteractionState', 'CustomThemeButtonProps', 'CustomThemeButtonOwnProps'];
24
24
  var unsupportedProps = exports.unsupportedProps = ['component', 'css', 'style'];
25
25
  var linkButtonMissingHrefComment = exports.linkButtonMissingHrefComment = "\"link\" and \"subtle-link\" appearances are only available in LinkButton, please either provide a href prop then migrate to LinkButton, or remove the appearance from the default button.";
26
- var iconPropsNoLongerSupportedComment = exports.iconPropsNoLongerSupportedComment = "\"glyph\", \"primaryColor\", \"secondaryColor\" and \"testId\" are no longer supported in button icons, please refactor the code and/or revisit the UI.";
27
26
  var buttonPropsNoLongerSupportedComment = exports.buttonPropsNoLongerSupportedComment = "Buttons with \"component\", \"css\" or \"style\" prop can't be automatically migrated with codemods. Please migrate it manually.";
28
27
  var migrateFitContainerButtonToDefaultButtonComment = exports.migrateFitContainerButtonToDefaultButtonComment = "Migrated to a default button with text which is from the icon label.";
29
28
  var migrateFitContainerButtonToIconButtonComment = exports.migrateFitContainerButtonToIconButtonComment = "\"shouldFitContainer\" is not available in icon buttons, please consider using a default button with text.";
@@ -3,8 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.moveSizeAndLabelAttributes = exports.getIconElement = exports.getIconAttributes = exports.generateNewElement = void 0;
7
- var _codemodUtils = require("@atlaskit/codemod-utils");
6
+ exports.handleIconAttributes = exports.getIconElement = exports.getIconAttributes = exports.generateNewElement = void 0;
8
7
  var _constants = require("../utils/constants");
9
8
  var getIconAttributes = exports.getIconAttributes = function getIconAttributes(attributes) {
10
9
  var iconAttr = attributes === null || attributes === void 0 ? void 0 : attributes.filter(function (attribute) {
@@ -22,51 +21,88 @@ var getIconElement = exports.getIconElement = function getIconElement(iconAttr)
22
21
  }
23
22
  return null;
24
23
  };
25
- var moveSizeAndLabelAttributes = exports.moveSizeAndLabelAttributes = function moveSizeAndLabelAttributes(element, j) {
24
+
25
+ /**
26
+ * We need to do a couple of things here:
27
+ *
28
+ * 1. If an icon attribute has a label, elevate it to the root button element
29
+ * 2. If an icon doesn't have any other attributes, move to bounded API:
30
+ * {<MoreIcon />} -> {MoreIcon}
31
+ * 3. If an icon has attributes other than label, move to renderProp:
32
+ * {<MoreIcon primaryColor />} -> {() => <MoreIcon primaryColor />}
33
+ *
34
+ * @param element
35
+ * @param j
36
+ * @param iconRenamed
37
+ */
38
+ var handleIconAttributes = exports.handleIconAttributes = function handleIconAttributes(element, j) {
26
39
  var iconRenamed = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
27
- var attributes = element.openingElement.attributes;
28
- var iconAttrs = attributes && getIconAttributes(attributes);
29
- iconAttrs === null || iconAttrs === void 0 || iconAttrs.forEach(function (iconAttr) {
30
- var _iconAttr$value2;
31
- var iconElement = getIconElement(iconAttr);
40
+ var buttonAttributes = element.openingElement.attributes;
41
+
42
+ // Get iconBefore and iconAfter attributes
43
+ var buttonIconAttributes = buttonAttributes && getIconAttributes(buttonAttributes);
44
+ buttonIconAttributes === null || buttonIconAttributes === void 0 || buttonIconAttributes.forEach(function (iconAttribute) {
45
+ var iconElement = getIconElement(iconAttribute);
32
46
  if (!iconElement) {
33
47
  return;
34
48
  }
35
- var iconJSXElementAttributes = iconElement.openingElement.attributes;
36
- // add inlined comment to the icon if it has any attributes other than label and size
37
- if (Array.isArray(iconJSXElementAttributes)) {
38
- var ifIconAttributeEitherLabelOrSize = iconJSXElementAttributes === null || iconJSXElementAttributes === void 0 ? void 0 : iconJSXElementAttributes.every(function (attribute) {
39
- return attribute.type === 'JSXAttribute' && typeof attribute.name.name === 'string' && (attribute.name.name === 'size' || attribute.name.name === 'label');
40
- });
41
- if (!ifIconAttributeEitherLabelOrSize) {
42
- (0, _codemodUtils.addCommentBefore)(j, j(iconAttr), _constants.iconPropsNoLongerSupportedComment, 'line');
43
- }
49
+ var iconAttributes = iconElement.openingElement.attributes;
50
+ if (!Array.isArray(iconAttributes)) {
51
+ return;
52
+ }
53
+
54
+ // 1. Move label to root button element, only if label doesn't exist already. Button label
55
+ // takes precedence over icon label.
44
56
 
45
- // move label and size attributes from icon to the root Button prop
46
- var buttonAlreadyHasLabelProp = attributes === null || attributes === void 0 ? void 0 : attributes.find(function (attribute) {
57
+ var buttonAlreadyHasLabelProp = buttonAttributes === null || buttonAttributes === void 0 ? void 0 : buttonAttributes.find(function (buttonAttribute) {
58
+ return buttonAttribute.type === 'JSXAttribute' && buttonAttribute.name.name === 'label';
59
+ });
60
+ if (!buttonAlreadyHasLabelProp) {
61
+ var labelAttribute = iconAttributes.find(function (attribute) {
47
62
  return attribute.type === 'JSXAttribute' && attribute.name.name === 'label';
48
63
  });
49
- if (!buttonAlreadyHasLabelProp) {
50
- var labelAttribute = iconJSXElementAttributes.find(function (attribute) {
51
- return attribute.type === 'JSXAttribute' && attribute.name.name === 'label';
52
- });
53
- if (labelAttribute && labelAttribute.type === 'JSXAttribute' && iconRenamed) {
54
- attributes === null || attributes === void 0 || attributes.push(labelAttribute);
55
- }
56
- }
57
- var sizeAttribute = iconJSXElementAttributes.find(function (attribute) {
58
- var _attribute$value, _attribute$value2;
59
- return attribute.type === 'JSXAttribute' && attribute.name.name === 'size' && ((_attribute$value = attribute.value) === null || _attribute$value === void 0 ? void 0 : _attribute$value.type) === 'StringLiteral' && ((_attribute$value2 = attribute.value) === null || _attribute$value2 === void 0 ? void 0 : _attribute$value2.value) !== 'medium';
60
- });
61
- if (sizeAttribute && sizeAttribute.type === 'JSXAttribute') {
62
- sizeAttribute.name.name = iconRenamed ? 'UNSAFE_size' : "UNSAFE_".concat(iconAttr.name.name, "_size");
63
- attributes === null || attributes === void 0 || attributes.push(sizeAttribute);
64
+ if (labelAttribute && labelAttribute.type === 'JSXAttribute' && iconRenamed) {
65
+ buttonAttributes === null || buttonAttributes === void 0 || buttonAttributes.unshift(labelAttribute);
64
66
  }
65
67
  }
66
68
 
67
- // replace JSXElement with identifier {<MoreIcon />} => {MoreIcon}
68
- if (iconElement.openingElement.name.type === 'JSXIdentifier' && ((_iconAttr$value2 = iconAttr.value) === null || _iconAttr$value2 === void 0 ? void 0 : _iconAttr$value2.type) === 'JSXExpressionContainer') {
69
- iconAttr.value.expression = j.identifier(iconElement.openingElement.name.name);
69
+ // 2. If there are any other props on icon, move to render prop
70
+ var attributesOtherThanLabelOrMediumSize = iconAttributes.filter(function (iconAttribute) {
71
+ var _iconAttribute$value, _iconAttribute$value2;
72
+ // Exclude size="medium"
73
+ if (iconAttribute.type === 'JSXAttribute' && iconAttribute.name.name === 'size' && ((_iconAttribute$value = iconAttribute.value) === null || _iconAttribute$value === void 0 ? void 0 : _iconAttribute$value.type) === 'StringLiteral' && ((_iconAttribute$value2 = iconAttribute.value) === null || _iconAttribute$value2 === void 0 ? void 0 : _iconAttribute$value2.value) === 'medium') {
74
+ return false;
75
+ }
76
+
77
+ // Exclude label
78
+ if (iconAttribute.type === 'JSXAttribute' && iconAttribute.name.name === 'label') {
79
+ return false;
80
+ }
81
+ return true;
82
+ });
83
+ if (attributesOtherThanLabelOrMediumSize.length > 0) {
84
+ // Move to render prop: `<MoreIcon primaryColor />` -> `(props) => <MoreIcon {...props} primaryColor />`
85
+
86
+ // Remove label and size="medium" attributes
87
+ j(iconElement.openingElement).find(j.JSXAttribute).filter(function (attribute) {
88
+ return attribute.value.name.name === 'label' || attribute.value.type === 'JSXAttribute' && attribute.value.name.name === 'size' && attribute.value.value && attribute.value.value.type === 'StringLiteral' && attribute.value.value.value === 'medium' || false;
89
+ }).remove();
90
+
91
+ // Add spread props
92
+ iconAttributes.unshift(j.jsxSpreadAttribute(j.identifier('iconProps')));
93
+
94
+ // Create new arrow function (renderProp)
95
+ iconAttribute.value = j.jsxExpressionContainer(j.arrowFunctionExpression.from({
96
+ params: [j.identifier('iconProps')],
97
+ body: iconElement,
98
+ expression: true
99
+ }));
100
+ } else {
101
+ var _iconAttribute$value3;
102
+ // Move to bounded API: {<MoreIcon />} => {MoreIcon}
103
+ if (iconElement.openingElement.name.type === 'JSXIdentifier' && ((_iconAttribute$value3 = iconAttribute.value) === null || _iconAttribute$value3 === void 0 ? void 0 : _iconAttribute$value3.type) === 'JSXExpressionContainer') {
104
+ iconAttribute.value.expression = j.identifier(iconElement.openingElement.name.name);
105
+ }
70
106
  }
71
107
  });
72
108
  };
@@ -75,7 +111,7 @@ var generateNewElement = exports.generateNewElement = function generateNewElemen
75
111
  var iconAttrs = attributes && getIconAttributes(attributes);
76
112
  var isIconOrLinkIcon = variant === _constants.NEW_BUTTON_VARIANTS.icon || variant === _constants.NEW_BUTTON_VARIANTS.linkIcon;
77
113
  if (isIconOrLinkIcon && iconAttrs !== null && iconAttrs !== void 0 && iconAttrs.length) {
78
- moveSizeAndLabelAttributes(element, j, true);
114
+ handleIconAttributes(element, j, true);
79
115
 
80
116
  // rename iconBefore/iconAfter to icon
81
117
  iconAttrs[0].name.name = 'icon';
@@ -1,7 +1,7 @@
1
1
  import { addCommentBefore } from '@atlaskit/codemod-utils';
2
2
  import { getDefaultImportSpecifierName } from '@hypermod/utils';
3
3
  import { PRINT_SETTINGS, NEW_BUTTON_VARIANTS, entryPointsMapping, NEW_BUTTON_ENTRY_POINT, linkButtonMissingHrefComment, buttonPropsNoLongerSupportedComment, unsupportedProps } from '../utils/constants';
4
- import { generateNewElement, moveSizeAndLabelAttributes } from '../utils/generate-new-button-element';
4
+ import { generateNewElement, handleIconAttributes } from '../utils/generate-new-button-element';
5
5
  import { ifHasUnsupportedProps } from '../utils/has-unsupported-props';
6
6
  import { checkIfVariantAlreadyImported } from '../utils/if-variant-already-imported';
7
7
  import { renameDefaultButtonToLegacyButtonImport } from '../utils/rename-default-button-to-legacy-button';
@@ -44,7 +44,7 @@ const transformer = (file, api) => {
44
44
  const isDefaultButtonWithAnIcon = !isLinkIconButton && !isIconButton && !isFitContainerIconButton && hasIcon;
45
45
  const isLoadingButton = element.value.openingElement.name.type === 'JSXIdentifier' && element.value.openingElement.name.name === loadingButtonImportName;
46
46
  if (isDefaultButtonWithAnIcon) {
47
- moveSizeAndLabelAttributes(element.value, j);
47
+ handleIconAttributes(element.value, j);
48
48
  }
49
49
  if (isFitContainerIconButton) {
50
50
  const migratedToIconButton = migrateFitContainerIconButton(element, j);
@@ -17,7 +17,6 @@ export const entryPointsMapping = {
17
17
  export const BUTTON_TYPES = ['BaseOwnProps', 'BaseProps', 'ButtonProps', 'LoadingButtonProps', 'LoadingButtonOwnProps', 'ThemeTokens', 'ThemeProps', 'InteractionState', 'CustomThemeButtonProps', 'CustomThemeButtonOwnProps'];
18
18
  export const unsupportedProps = ['component', 'css', 'style'];
19
19
  export const linkButtonMissingHrefComment = `"link" and "subtle-link" appearances are only available in LinkButton, please either provide a href prop then migrate to LinkButton, or remove the appearance from the default button.`;
20
- export const iconPropsNoLongerSupportedComment = `"glyph", "primaryColor", "secondaryColor" and "testId" are no longer supported in button icons, please refactor the code and/or revisit the UI.`;
21
20
  export const buttonPropsNoLongerSupportedComment = `Buttons with "component", "css" or "style" prop can't be automatically migrated with codemods. Please migrate it manually.`;
22
21
  export const migrateFitContainerButtonToDefaultButtonComment = `Migrated to a default button with text which is from the icon label.`;
23
22
  export const migrateFitContainerButtonToIconButtonComment = `"shouldFitContainer" is not available in icon buttons, please consider using a default button with text.`;
@@ -1,5 +1,4 @@
1
- import { addCommentBefore } from '@atlaskit/codemod-utils';
2
- import { NEW_BUTTON_VARIANTS, iconPropsNoLongerSupportedComment } from '../utils/constants';
1
+ import { NEW_BUTTON_VARIANTS } from '../utils/constants';
3
2
  export const getIconAttributes = attributes => {
4
3
  const iconAttr = attributes === null || attributes === void 0 ? void 0 : attributes.filter(attribute => attribute.type === 'JSXAttribute' && (attribute.name.name === 'iconBefore' || attribute.name.name === 'iconAfter'));
5
4
  if (iconAttr !== null && iconAttr !== void 0 && iconAttr.length) {
@@ -14,46 +13,83 @@ export const getIconElement = iconAttr => {
14
13
  }
15
14
  return null;
16
15
  };
17
- export const moveSizeAndLabelAttributes = (element, j, iconRenamed = false) => {
16
+
17
+ /**
18
+ * We need to do a couple of things here:
19
+ *
20
+ * 1. If an icon attribute has a label, elevate it to the root button element
21
+ * 2. If an icon doesn't have any other attributes, move to bounded API:
22
+ * {<MoreIcon />} -> {MoreIcon}
23
+ * 3. If an icon has attributes other than label, move to renderProp:
24
+ * {<MoreIcon primaryColor />} -> {() => <MoreIcon primaryColor />}
25
+ *
26
+ * @param element
27
+ * @param j
28
+ * @param iconRenamed
29
+ */
30
+ export const handleIconAttributes = (element, j, iconRenamed = false) => {
18
31
  const {
19
- attributes
32
+ attributes: buttonAttributes
20
33
  } = element.openingElement;
21
- const iconAttrs = attributes && getIconAttributes(attributes);
22
- iconAttrs === null || iconAttrs === void 0 ? void 0 : iconAttrs.forEach(iconAttr => {
23
- var _iconAttr$value2;
24
- const iconElement = getIconElement(iconAttr);
34
+
35
+ // Get iconBefore and iconAfter attributes
36
+ const buttonIconAttributes = buttonAttributes && getIconAttributes(buttonAttributes);
37
+ buttonIconAttributes === null || buttonIconAttributes === void 0 ? void 0 : buttonIconAttributes.forEach(iconAttribute => {
38
+ let iconElement = getIconElement(iconAttribute);
25
39
  if (!iconElement) {
26
40
  return;
27
41
  }
28
- const iconJSXElementAttributes = iconElement.openingElement.attributes;
29
- // add inlined comment to the icon if it has any attributes other than label and size
30
- if (Array.isArray(iconJSXElementAttributes)) {
31
- const ifIconAttributeEitherLabelOrSize = iconJSXElementAttributes === null || iconJSXElementAttributes === void 0 ? void 0 : iconJSXElementAttributes.every(attribute => attribute.type === 'JSXAttribute' && typeof attribute.name.name === 'string' && (attribute.name.name === 'size' || attribute.name.name === 'label'));
32
- if (!ifIconAttributeEitherLabelOrSize) {
33
- addCommentBefore(j, j(iconAttr), iconPropsNoLongerSupportedComment, 'line');
42
+ const iconAttributes = iconElement.openingElement.attributes;
43
+ if (!Array.isArray(iconAttributes)) {
44
+ return;
45
+ }
46
+
47
+ // 1. Move label to root button element, only if label doesn't exist already. Button label
48
+ // takes precedence over icon label.
49
+
50
+ const buttonAlreadyHasLabelProp = buttonAttributes === null || buttonAttributes === void 0 ? void 0 : buttonAttributes.find(buttonAttribute => buttonAttribute.type === 'JSXAttribute' && buttonAttribute.name.name === 'label');
51
+ if (!buttonAlreadyHasLabelProp) {
52
+ const labelAttribute = iconAttributes.find(attribute => attribute.type === 'JSXAttribute' && attribute.name.name === 'label');
53
+ if (labelAttribute && labelAttribute.type === 'JSXAttribute' && iconRenamed) {
54
+ buttonAttributes === null || buttonAttributes === void 0 ? void 0 : buttonAttributes.unshift(labelAttribute);
34
55
  }
56
+ }
35
57
 
36
- // move label and size attributes from icon to the root Button prop
37
- const buttonAlreadyHasLabelProp = attributes === null || attributes === void 0 ? void 0 : attributes.find(attribute => attribute.type === 'JSXAttribute' && attribute.name.name === 'label');
38
- if (!buttonAlreadyHasLabelProp) {
39
- const labelAttribute = iconJSXElementAttributes.find(attribute => attribute.type === 'JSXAttribute' && attribute.name.name === 'label');
40
- if (labelAttribute && labelAttribute.type === 'JSXAttribute' && iconRenamed) {
41
- attributes === null || attributes === void 0 ? void 0 : attributes.push(labelAttribute);
42
- }
58
+ // 2. If there are any other props on icon, move to render prop
59
+ const attributesOtherThanLabelOrMediumSize = iconAttributes.filter(iconAttribute => {
60
+ var _iconAttribute$value, _iconAttribute$value2;
61
+ // Exclude size="medium"
62
+ if (iconAttribute.type === 'JSXAttribute' && iconAttribute.name.name === 'size' && ((_iconAttribute$value = iconAttribute.value) === null || _iconAttribute$value === void 0 ? void 0 : _iconAttribute$value.type) === 'StringLiteral' && ((_iconAttribute$value2 = iconAttribute.value) === null || _iconAttribute$value2 === void 0 ? void 0 : _iconAttribute$value2.value) === 'medium') {
63
+ return false;
43
64
  }
44
- const sizeAttribute = iconJSXElementAttributes.find(attribute => {
45
- var _attribute$value, _attribute$value2;
46
- return attribute.type === 'JSXAttribute' && attribute.name.name === 'size' && ((_attribute$value = attribute.value) === null || _attribute$value === void 0 ? void 0 : _attribute$value.type) === 'StringLiteral' && ((_attribute$value2 = attribute.value) === null || _attribute$value2 === void 0 ? void 0 : _attribute$value2.value) !== 'medium';
47
- });
48
- if (sizeAttribute && sizeAttribute.type === 'JSXAttribute') {
49
- sizeAttribute.name.name = iconRenamed ? 'UNSAFE_size' : `UNSAFE_${iconAttr.name.name}_size`;
50
- attributes === null || attributes === void 0 ? void 0 : attributes.push(sizeAttribute);
65
+
66
+ // Exclude label
67
+ if (iconAttribute.type === 'JSXAttribute' && iconAttribute.name.name === 'label') {
68
+ return false;
51
69
  }
52
- }
70
+ return true;
71
+ });
72
+ if (attributesOtherThanLabelOrMediumSize.length > 0) {
73
+ // Move to render prop: `<MoreIcon primaryColor />` -> `(props) => <MoreIcon {...props} primaryColor />`
53
74
 
54
- // replace JSXElement with identifier {<MoreIcon />} => {MoreIcon}
55
- if (iconElement.openingElement.name.type === 'JSXIdentifier' && ((_iconAttr$value2 = iconAttr.value) === null || _iconAttr$value2 === void 0 ? void 0 : _iconAttr$value2.type) === 'JSXExpressionContainer') {
56
- iconAttr.value.expression = j.identifier(iconElement.openingElement.name.name);
75
+ // Remove label and size="medium" attributes
76
+ j(iconElement.openingElement).find(j.JSXAttribute).filter(attribute => attribute.value.name.name === 'label' || attribute.value.type === 'JSXAttribute' && attribute.value.name.name === 'size' && attribute.value.value && attribute.value.value.type === 'StringLiteral' && attribute.value.value.value === 'medium' || false).remove();
77
+
78
+ // Add spread props
79
+ iconAttributes.unshift(j.jsxSpreadAttribute(j.identifier('iconProps')));
80
+
81
+ // Create new arrow function (renderProp)
82
+ iconAttribute.value = j.jsxExpressionContainer(j.arrowFunctionExpression.from({
83
+ params: [j.identifier('iconProps')],
84
+ body: iconElement,
85
+ expression: true
86
+ }));
87
+ } else {
88
+ var _iconAttribute$value3;
89
+ // Move to bounded API: {<MoreIcon />} => {MoreIcon}
90
+ if (iconElement.openingElement.name.type === 'JSXIdentifier' && ((_iconAttribute$value3 = iconAttribute.value) === null || _iconAttribute$value3 === void 0 ? void 0 : _iconAttribute$value3.type) === 'JSXExpressionContainer') {
91
+ iconAttribute.value.expression = j.identifier(iconElement.openingElement.name.name);
92
+ }
57
93
  }
58
94
  });
59
95
  };
@@ -64,7 +100,7 @@ export const generateNewElement = (variant, element, j) => {
64
100
  const iconAttrs = attributes && getIconAttributes(attributes);
65
101
  const isIconOrLinkIcon = variant === NEW_BUTTON_VARIANTS.icon || variant === NEW_BUTTON_VARIANTS.linkIcon;
66
102
  if (isIconOrLinkIcon && iconAttrs !== null && iconAttrs !== void 0 && iconAttrs.length) {
67
- moveSizeAndLabelAttributes(element, j, true);
103
+ handleIconAttributes(element, j, true);
68
104
 
69
105
  // rename iconBefore/iconAfter to icon
70
106
  iconAttrs[0].name.name = 'icon';
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.21.1", _PACKAGE_VERSION_ = _process$env$_PACKAGE === void 0 ? '0.0.0-dev' : _process$env$_PACKAGE;
301
+ _process$env$_PACKAGE = "0.22.0", _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) {
@@ -1,7 +1,7 @@
1
1
  import { addCommentBefore } from '@atlaskit/codemod-utils';
2
2
  import { getDefaultImportSpecifierName } from '@hypermod/utils';
3
3
  import { PRINT_SETTINGS, NEW_BUTTON_VARIANTS, entryPointsMapping, NEW_BUTTON_ENTRY_POINT, linkButtonMissingHrefComment, buttonPropsNoLongerSupportedComment, unsupportedProps } from '../utils/constants';
4
- import { generateNewElement, moveSizeAndLabelAttributes } from '../utils/generate-new-button-element';
4
+ import { generateNewElement, handleIconAttributes } from '../utils/generate-new-button-element';
5
5
  import { ifHasUnsupportedProps } from '../utils/has-unsupported-props';
6
6
  import { checkIfVariantAlreadyImported } from '../utils/if-variant-already-imported';
7
7
  import { renameDefaultButtonToLegacyButtonImport } from '../utils/rename-default-button-to-legacy-button';
@@ -52,7 +52,7 @@ var transformer = function transformer(file, api) {
52
52
  var isDefaultButtonWithAnIcon = !isLinkIconButton && !isIconButton && !isFitContainerIconButton && hasIcon;
53
53
  var isLoadingButton = element.value.openingElement.name.type === 'JSXIdentifier' && element.value.openingElement.name.name === loadingButtonImportName;
54
54
  if (isDefaultButtonWithAnIcon) {
55
- moveSizeAndLabelAttributes(element.value, j);
55
+ handleIconAttributes(element.value, j);
56
56
  }
57
57
  if (isFitContainerIconButton) {
58
58
  var migratedToIconButton = migrateFitContainerIconButton(element, j);
@@ -17,7 +17,6 @@ export var entryPointsMapping = {
17
17
  export var BUTTON_TYPES = ['BaseOwnProps', 'BaseProps', 'ButtonProps', 'LoadingButtonProps', 'LoadingButtonOwnProps', 'ThemeTokens', 'ThemeProps', 'InteractionState', 'CustomThemeButtonProps', 'CustomThemeButtonOwnProps'];
18
18
  export var unsupportedProps = ['component', 'css', 'style'];
19
19
  export var linkButtonMissingHrefComment = "\"link\" and \"subtle-link\" appearances are only available in LinkButton, please either provide a href prop then migrate to LinkButton, or remove the appearance from the default button.";
20
- export var iconPropsNoLongerSupportedComment = "\"glyph\", \"primaryColor\", \"secondaryColor\" and \"testId\" are no longer supported in button icons, please refactor the code and/or revisit the UI.";
21
20
  export var buttonPropsNoLongerSupportedComment = "Buttons with \"component\", \"css\" or \"style\" prop can't be automatically migrated with codemods. Please migrate it manually.";
22
21
  export var migrateFitContainerButtonToDefaultButtonComment = "Migrated to a default button with text which is from the icon label.";
23
22
  export var migrateFitContainerButtonToIconButtonComment = "\"shouldFitContainer\" is not available in icon buttons, please consider using a default button with text.";
@@ -1,5 +1,4 @@
1
- import { addCommentBefore } from '@atlaskit/codemod-utils';
2
- import { NEW_BUTTON_VARIANTS, iconPropsNoLongerSupportedComment } from '../utils/constants';
1
+ import { NEW_BUTTON_VARIANTS } from '../utils/constants';
3
2
  export var getIconAttributes = function getIconAttributes(attributes) {
4
3
  var iconAttr = attributes === null || attributes === void 0 ? void 0 : attributes.filter(function (attribute) {
5
4
  return attribute.type === 'JSXAttribute' && (attribute.name.name === 'iconBefore' || attribute.name.name === 'iconAfter');
@@ -16,51 +15,88 @@ export var getIconElement = function getIconElement(iconAttr) {
16
15
  }
17
16
  return null;
18
17
  };
19
- export var moveSizeAndLabelAttributes = function moveSizeAndLabelAttributes(element, j) {
18
+
19
+ /**
20
+ * We need to do a couple of things here:
21
+ *
22
+ * 1. If an icon attribute has a label, elevate it to the root button element
23
+ * 2. If an icon doesn't have any other attributes, move to bounded API:
24
+ * {<MoreIcon />} -> {MoreIcon}
25
+ * 3. If an icon has attributes other than label, move to renderProp:
26
+ * {<MoreIcon primaryColor />} -> {() => <MoreIcon primaryColor />}
27
+ *
28
+ * @param element
29
+ * @param j
30
+ * @param iconRenamed
31
+ */
32
+ export var handleIconAttributes = function handleIconAttributes(element, j) {
20
33
  var iconRenamed = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
21
- var attributes = element.openingElement.attributes;
22
- var iconAttrs = attributes && getIconAttributes(attributes);
23
- iconAttrs === null || iconAttrs === void 0 || iconAttrs.forEach(function (iconAttr) {
24
- var _iconAttr$value2;
25
- var iconElement = getIconElement(iconAttr);
34
+ var buttonAttributes = element.openingElement.attributes;
35
+
36
+ // Get iconBefore and iconAfter attributes
37
+ var buttonIconAttributes = buttonAttributes && getIconAttributes(buttonAttributes);
38
+ buttonIconAttributes === null || buttonIconAttributes === void 0 || buttonIconAttributes.forEach(function (iconAttribute) {
39
+ var iconElement = getIconElement(iconAttribute);
26
40
  if (!iconElement) {
27
41
  return;
28
42
  }
29
- var iconJSXElementAttributes = iconElement.openingElement.attributes;
30
- // add inlined comment to the icon if it has any attributes other than label and size
31
- if (Array.isArray(iconJSXElementAttributes)) {
32
- var ifIconAttributeEitherLabelOrSize = iconJSXElementAttributes === null || iconJSXElementAttributes === void 0 ? void 0 : iconJSXElementAttributes.every(function (attribute) {
33
- return attribute.type === 'JSXAttribute' && typeof attribute.name.name === 'string' && (attribute.name.name === 'size' || attribute.name.name === 'label');
34
- });
35
- if (!ifIconAttributeEitherLabelOrSize) {
36
- addCommentBefore(j, j(iconAttr), iconPropsNoLongerSupportedComment, 'line');
37
- }
43
+ var iconAttributes = iconElement.openingElement.attributes;
44
+ if (!Array.isArray(iconAttributes)) {
45
+ return;
46
+ }
47
+
48
+ // 1. Move label to root button element, only if label doesn't exist already. Button label
49
+ // takes precedence over icon label.
38
50
 
39
- // move label and size attributes from icon to the root Button prop
40
- var buttonAlreadyHasLabelProp = attributes === null || attributes === void 0 ? void 0 : attributes.find(function (attribute) {
51
+ var buttonAlreadyHasLabelProp = buttonAttributes === null || buttonAttributes === void 0 ? void 0 : buttonAttributes.find(function (buttonAttribute) {
52
+ return buttonAttribute.type === 'JSXAttribute' && buttonAttribute.name.name === 'label';
53
+ });
54
+ if (!buttonAlreadyHasLabelProp) {
55
+ var labelAttribute = iconAttributes.find(function (attribute) {
41
56
  return attribute.type === 'JSXAttribute' && attribute.name.name === 'label';
42
57
  });
43
- if (!buttonAlreadyHasLabelProp) {
44
- var labelAttribute = iconJSXElementAttributes.find(function (attribute) {
45
- return attribute.type === 'JSXAttribute' && attribute.name.name === 'label';
46
- });
47
- if (labelAttribute && labelAttribute.type === 'JSXAttribute' && iconRenamed) {
48
- attributes === null || attributes === void 0 || attributes.push(labelAttribute);
49
- }
50
- }
51
- var sizeAttribute = iconJSXElementAttributes.find(function (attribute) {
52
- var _attribute$value, _attribute$value2;
53
- return attribute.type === 'JSXAttribute' && attribute.name.name === 'size' && ((_attribute$value = attribute.value) === null || _attribute$value === void 0 ? void 0 : _attribute$value.type) === 'StringLiteral' && ((_attribute$value2 = attribute.value) === null || _attribute$value2 === void 0 ? void 0 : _attribute$value2.value) !== 'medium';
54
- });
55
- if (sizeAttribute && sizeAttribute.type === 'JSXAttribute') {
56
- sizeAttribute.name.name = iconRenamed ? 'UNSAFE_size' : "UNSAFE_".concat(iconAttr.name.name, "_size");
57
- attributes === null || attributes === void 0 || attributes.push(sizeAttribute);
58
+ if (labelAttribute && labelAttribute.type === 'JSXAttribute' && iconRenamed) {
59
+ buttonAttributes === null || buttonAttributes === void 0 || buttonAttributes.unshift(labelAttribute);
58
60
  }
59
61
  }
60
62
 
61
- // replace JSXElement with identifier {<MoreIcon />} => {MoreIcon}
62
- if (iconElement.openingElement.name.type === 'JSXIdentifier' && ((_iconAttr$value2 = iconAttr.value) === null || _iconAttr$value2 === void 0 ? void 0 : _iconAttr$value2.type) === 'JSXExpressionContainer') {
63
- iconAttr.value.expression = j.identifier(iconElement.openingElement.name.name);
63
+ // 2. If there are any other props on icon, move to render prop
64
+ var attributesOtherThanLabelOrMediumSize = iconAttributes.filter(function (iconAttribute) {
65
+ var _iconAttribute$value, _iconAttribute$value2;
66
+ // Exclude size="medium"
67
+ if (iconAttribute.type === 'JSXAttribute' && iconAttribute.name.name === 'size' && ((_iconAttribute$value = iconAttribute.value) === null || _iconAttribute$value === void 0 ? void 0 : _iconAttribute$value.type) === 'StringLiteral' && ((_iconAttribute$value2 = iconAttribute.value) === null || _iconAttribute$value2 === void 0 ? void 0 : _iconAttribute$value2.value) === 'medium') {
68
+ return false;
69
+ }
70
+
71
+ // Exclude label
72
+ if (iconAttribute.type === 'JSXAttribute' && iconAttribute.name.name === 'label') {
73
+ return false;
74
+ }
75
+ return true;
76
+ });
77
+ if (attributesOtherThanLabelOrMediumSize.length > 0) {
78
+ // Move to render prop: `<MoreIcon primaryColor />` -> `(props) => <MoreIcon {...props} primaryColor />`
79
+
80
+ // Remove label and size="medium" attributes
81
+ j(iconElement.openingElement).find(j.JSXAttribute).filter(function (attribute) {
82
+ return attribute.value.name.name === 'label' || attribute.value.type === 'JSXAttribute' && attribute.value.name.name === 'size' && attribute.value.value && attribute.value.value.type === 'StringLiteral' && attribute.value.value.value === 'medium' || false;
83
+ }).remove();
84
+
85
+ // Add spread props
86
+ iconAttributes.unshift(j.jsxSpreadAttribute(j.identifier('iconProps')));
87
+
88
+ // Create new arrow function (renderProp)
89
+ iconAttribute.value = j.jsxExpressionContainer(j.arrowFunctionExpression.from({
90
+ params: [j.identifier('iconProps')],
91
+ body: iconElement,
92
+ expression: true
93
+ }));
94
+ } else {
95
+ var _iconAttribute$value3;
96
+ // Move to bounded API: {<MoreIcon />} => {MoreIcon}
97
+ if (iconElement.openingElement.name.type === 'JSXIdentifier' && ((_iconAttribute$value3 = iconAttribute.value) === null || _iconAttribute$value3 === void 0 ? void 0 : _iconAttribute$value3.type) === 'JSXExpressionContainer') {
98
+ iconAttribute.value.expression = j.identifier(iconElement.openingElement.name.name);
99
+ }
64
100
  }
65
101
  });
66
102
  };
@@ -69,7 +105,7 @@ export var generateNewElement = function generateNewElement(variant, element, j)
69
105
  var iconAttrs = attributes && getIconAttributes(attributes);
70
106
  var isIconOrLinkIcon = variant === NEW_BUTTON_VARIANTS.icon || variant === NEW_BUTTON_VARIANTS.linkIcon;
71
107
  if (isIconOrLinkIcon && iconAttrs !== null && iconAttrs !== void 0 && iconAttrs.length) {
72
- moveSizeAndLabelAttributes(element, j, true);
108
+ handleIconAttributes(element, j, true);
73
109
 
74
110
  // rename iconBefore/iconAfter to icon
75
111
  iconAttrs[0].name.name = 'icon';
@@ -11,7 +11,6 @@ export declare const entryPointsMapping: {
11
11
  export declare const BUTTON_TYPES: string[];
12
12
  export declare const unsupportedProps: string[];
13
13
  export declare const linkButtonMissingHrefComment = "\"link\" and \"subtle-link\" appearances are only available in LinkButton, please either provide a href prop then migrate to LinkButton, or remove the appearance from the default button.";
14
- export declare const iconPropsNoLongerSupportedComment = "\"glyph\", \"primaryColor\", \"secondaryColor\" and \"testId\" are no longer supported in button icons, please refactor the code and/or revisit the UI.";
15
14
  export declare const buttonPropsNoLongerSupportedComment = "Buttons with \"component\", \"css\" or \"style\" prop can't be automatically migrated with codemods. Please migrate it manually.";
16
15
  export declare const migrateFitContainerButtonToDefaultButtonComment = "Migrated to a default button with text which is from the icon label.";
17
16
  export declare const migrateFitContainerButtonToIconButtonComment = "\"shouldFitContainer\" is not available in icon buttons, please consider using a default button with text.";
@@ -2,5 +2,18 @@ import { API, JSXElement, JSXAttribute, JSXSpreadAttribute } from 'jscodeshift';
2
2
  import { NEW_BUTTON_VARIANTS } from '../utils/constants';
3
3
  export declare const getIconAttributes: (attributes: (JSXAttribute | JSXSpreadAttribute)[]) => JSXAttribute[] | null;
4
4
  export declare const getIconElement: (iconAttr: JSXAttribute) => JSXElement | null;
5
- export declare const moveSizeAndLabelAttributes: (element: JSXElement, j: API['jscodeshift'], iconRenamed?: boolean) => void;
5
+ /**
6
+ * We need to do a couple of things here:
7
+ *
8
+ * 1. If an icon attribute has a label, elevate it to the root button element
9
+ * 2. If an icon doesn't have any other attributes, move to bounded API:
10
+ * {<MoreIcon />} -> {MoreIcon}
11
+ * 3. If an icon has attributes other than label, move to renderProp:
12
+ * {<MoreIcon primaryColor />} -> {() => <MoreIcon primaryColor />}
13
+ *
14
+ * @param element
15
+ * @param j
16
+ * @param iconRenamed
17
+ */
18
+ export declare const handleIconAttributes: (element: JSXElement, j: API['jscodeshift'], iconRenamed?: boolean) => void;
6
19
  export declare const generateNewElement: (variant: (typeof NEW_BUTTON_VARIANTS)[keyof typeof NEW_BUTTON_VARIANTS], element: JSXElement, j: API['jscodeshift']) => JSXElement;
@@ -11,7 +11,6 @@ export declare const entryPointsMapping: {
11
11
  export declare const BUTTON_TYPES: string[];
12
12
  export declare const unsupportedProps: string[];
13
13
  export declare const linkButtonMissingHrefComment = "\"link\" and \"subtle-link\" appearances are only available in LinkButton, please either provide a href prop then migrate to LinkButton, or remove the appearance from the default button.";
14
- export declare const iconPropsNoLongerSupportedComment = "\"glyph\", \"primaryColor\", \"secondaryColor\" and \"testId\" are no longer supported in button icons, please refactor the code and/or revisit the UI.";
15
14
  export declare const buttonPropsNoLongerSupportedComment = "Buttons with \"component\", \"css\" or \"style\" prop can't be automatically migrated with codemods. Please migrate it manually.";
16
15
  export declare const migrateFitContainerButtonToDefaultButtonComment = "Migrated to a default button with text which is from the icon label.";
17
16
  export declare const migrateFitContainerButtonToIconButtonComment = "\"shouldFitContainer\" is not available in icon buttons, please consider using a default button with text.";
@@ -2,5 +2,18 @@ import { API, JSXElement, JSXAttribute, JSXSpreadAttribute } from 'jscodeshift';
2
2
  import { NEW_BUTTON_VARIANTS } from '../utils/constants';
3
3
  export declare const getIconAttributes: (attributes: (JSXAttribute | JSXSpreadAttribute)[]) => JSXAttribute[] | null;
4
4
  export declare const getIconElement: (iconAttr: JSXAttribute) => JSXElement | null;
5
- export declare const moveSizeAndLabelAttributes: (element: JSXElement, j: API['jscodeshift'], iconRenamed?: boolean) => void;
5
+ /**
6
+ * We need to do a couple of things here:
7
+ *
8
+ * 1. If an icon attribute has a label, elevate it to the root button element
9
+ * 2. If an icon doesn't have any other attributes, move to bounded API:
10
+ * {<MoreIcon />} -> {MoreIcon}
11
+ * 3. If an icon has attributes other than label, move to renderProp:
12
+ * {<MoreIcon primaryColor />} -> {() => <MoreIcon primaryColor />}
13
+ *
14
+ * @param element
15
+ * @param j
16
+ * @param iconRenamed
17
+ */
18
+ export declare const handleIconAttributes: (element: JSXElement, j: API['jscodeshift'], iconRenamed?: boolean) => void;
6
19
  export declare const generateNewElement: (variant: (typeof NEW_BUTTON_VARIANTS)[keyof typeof NEW_BUTTON_VARIANTS], element: JSXElement, j: API['jscodeshift']) => JSXElement;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/codemod-cli",
3
- "version": "0.21.1",
3
+ "version": "0.22.0",
4
4
  "description": "A cli for distributing codemods for atlassian-frontend components and services",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"