@atlaskit/codemod-cli 0.17.2 → 0.17.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 (37) hide show
  1. package/CHANGELOG.md +12 -0
  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 +69 -44
  4. package/dist/cjs/presets/migrate-to-new-buttons/utils/constants.js +9 -2
  5. package/dist/cjs/presets/migrate-to-new-buttons/utils/generate-new-button-element.js +34 -14
  6. package/dist/cjs/presets/migrate-to-new-buttons/utils/has-unsupported-props.js +12 -0
  7. package/dist/cjs/presets/migrate-to-new-buttons/utils/if-variant-already-imported.js +14 -0
  8. package/dist/cjs/presets/migrate-to-new-buttons/utils/migrate-fit-container-icon-button.js +41 -0
  9. package/dist/cjs/presets/migrate-to-new-buttons/utils/rename-default-button-to-legacy-button.js +15 -0
  10. package/dist/es2019/presets/migrate-to-new-buttons/codemods/next-migrate-to-new-button-variants.js +59 -32
  11. package/dist/es2019/presets/migrate-to-new-buttons/utils/constants.js +8 -1
  12. package/dist/es2019/presets/migrate-to-new-buttons/utils/generate-new-button-element.js +32 -14
  13. package/dist/es2019/presets/migrate-to-new-buttons/utils/has-unsupported-props.js +2 -0
  14. package/dist/es2019/presets/migrate-to-new-buttons/utils/if-variant-already-imported.js +4 -0
  15. package/dist/es2019/presets/migrate-to-new-buttons/utils/migrate-fit-container-icon-button.js +27 -0
  16. package/dist/es2019/presets/migrate-to-new-buttons/utils/rename-default-button-to-legacy-button.js +9 -0
  17. package/dist/esm/main.js +1 -1
  18. package/dist/esm/presets/migrate-to-new-buttons/codemods/next-migrate-to-new-button-variants.js +70 -45
  19. package/dist/esm/presets/migrate-to-new-buttons/utils/constants.js +8 -1
  20. package/dist/esm/presets/migrate-to-new-buttons/utils/generate-new-button-element.js +34 -14
  21. package/dist/esm/presets/migrate-to-new-buttons/utils/has-unsupported-props.js +6 -0
  22. package/dist/esm/presets/migrate-to-new-buttons/utils/if-variant-already-imported.js +8 -0
  23. package/dist/esm/presets/migrate-to-new-buttons/utils/migrate-fit-container-icon-button.js +35 -0
  24. package/dist/esm/presets/migrate-to-new-buttons/utils/rename-default-button-to-legacy-button.js +9 -0
  25. package/dist/types/presets/migrate-to-new-buttons/utils/constants.d.ts +7 -0
  26. package/dist/types/presets/migrate-to-new-buttons/utils/generate-new-button-element.d.ts +3 -1
  27. package/dist/types/presets/migrate-to-new-buttons/utils/has-unsupported-props.d.ts +2 -0
  28. package/dist/types/presets/migrate-to-new-buttons/utils/if-variant-already-imported.d.ts +2 -0
  29. package/dist/types/presets/migrate-to-new-buttons/utils/migrate-fit-container-icon-button.d.ts +3 -0
  30. package/dist/types/presets/migrate-to-new-buttons/utils/rename-default-button-to-legacy-button.d.ts +2 -0
  31. package/dist/types-ts4.5/presets/migrate-to-new-buttons/utils/constants.d.ts +7 -0
  32. package/dist/types-ts4.5/presets/migrate-to-new-buttons/utils/generate-new-button-element.d.ts +3 -1
  33. package/dist/types-ts4.5/presets/migrate-to-new-buttons/utils/has-unsupported-props.d.ts +2 -0
  34. package/dist/types-ts4.5/presets/migrate-to-new-buttons/utils/if-variant-already-imported.d.ts +2 -0
  35. package/dist/types-ts4.5/presets/migrate-to-new-buttons/utils/migrate-fit-container-icon-button.d.ts +3 -0
  36. package/dist/types-ts4.5/presets/migrate-to-new-buttons/utils/rename-default-button-to-legacy-button.d.ts +2 -0
  37. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @atlaskit/codemod-cli
2
2
 
3
+ ## 0.17.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [#59085](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/59085) [`360ae69766f9`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/360ae69766f9) - small fix in button migration codemod - don't move the size prop from icon if it is medium.
8
+
9
+ ## 0.17.3
10
+
11
+ ### Patch Changes
12
+
13
+ - [#57531](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/57531) [`18f167967f89`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/18f167967f89) - Handle button migrations with unsupported props.
14
+
3
15
  ## 0.17.2
4
16
 
5
17
  ### 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.17.2", _PACKAGE_VERSION_ = _process$env$_PACKAGE === void 0 ? '0.0.0-dev' : _process$env$_PACKAGE;
308
+ _process$env$_PACKAGE = "0.17.4", _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) {
@@ -7,6 +7,10 @@ exports.default = void 0;
7
7
  var _codemodUtils = require("@atlaskit/codemod-utils");
8
8
  var _constants = require("../utils/constants");
9
9
  var _generateNewButtonElement = require("../utils/generate-new-button-element");
10
+ var _hasUnsupportedProps = require("../utils/has-unsupported-props");
11
+ var _ifVariantAlreadyImported = require("../utils/if-variant-already-imported");
12
+ var _renameDefaultButtonToLegacyButton = require("../utils/rename-default-button-to-legacy-button");
13
+ var _migrateFitContainerIconButton = require("../utils/migrate-fit-container-icon-button");
10
14
  var transformer = function transformer(file, api, options) {
11
15
  var j = api.jscodeshift;
12
16
  var fileSource = j(file.source);
@@ -23,33 +27,17 @@ var transformer = function transformer(file, api, options) {
23
27
  return fileSource.toSource();
24
28
  }
25
29
  var specifierIdentifier = defaultButtonImport.get(0).node.local.name;
26
- var attributes = fileSource.find(j.JSXElement).filter(function (path) {
27
- return path.value.openingElement.name.type === 'JSXIdentifier' && path.value.openingElement.name.name === specifierIdentifier;
28
- }).find(j.JSXAttribute);
29
- var hasCustomComponent = attributes.filter(function (attribute) {
30
- return attribute.node.name.name === 'component';
31
- }).length > 0;
32
- var hasCssProp = attributes.filter(function (attribute) {
33
- return attribute.node.name.name === 'css';
34
- }).length > 0;
35
- if (hasCustomComponent || hasCssProp) {
36
- return fileSource.toSource(_constants.PRINT_SETTINGS);
37
- }
38
- var checkIfVariantAlreadyImported = function checkIfVariantAlreadyImported(variant) {
39
- return fileSource.find(j.ImportDeclaration).filter(function (path) {
40
- return path.node.source.value === _constants.NEW_BUTTON_ENTRY_POINT;
41
- }).find(j.ImportSpecifier).filter(function (path) {
42
- return path.node.imported.name === variant;
43
- }).length > 0;
44
- };
45
- var hasLinkIconButton = checkIfVariantAlreadyImported(_constants.NEW_BUTTON_VARIANTS.linkIcon.import);
46
- var hasLinkButton = checkIfVariantAlreadyImported(_constants.NEW_BUTTON_VARIANTS.link.import);
47
- var hasIconButton = checkIfVariantAlreadyImported(_constants.NEW_BUTTON_VARIANTS.icon.import);
30
+ var hasLinkIconButton = (0, _ifVariantAlreadyImported.checkIfVariantAlreadyImported)(_constants.NEW_BUTTON_VARIANTS.linkIcon.import, fileSource, j);
31
+ var hasLinkButton = (0, _ifVariantAlreadyImported.checkIfVariantAlreadyImported)(_constants.NEW_BUTTON_VARIANTS.link.import, fileSource, j);
32
+ var hasIconButton = (0, _ifVariantAlreadyImported.checkIfVariantAlreadyImported)(_constants.NEW_BUTTON_VARIANTS.icon.import, fileSource, j);
48
33
  var allButtons = fileSource.find(j.JSXElement).filter(function (path) {
49
34
  return path.value.openingElement.name.type === 'JSXIdentifier' && path.value.openingElement.name.name === specifierIdentifier;
50
35
  });
51
- allButtons.forEach(function (element) {
52
- var _element$value$childr, _element$value$childr2;
36
+ var buttonsWithoutUnsupportedProps = allButtons.filter(function (path) {
37
+ return !(0, _hasUnsupportedProps.ifHasUnsupportedProps)(path.value.openingElement.attributes);
38
+ });
39
+ buttonsWithoutUnsupportedProps.forEach(function (element) {
40
+ var _element$value$childr;
53
41
  var attributes = element.value.openingElement.attributes;
54
42
  if (!attributes) {
55
43
  return;
@@ -59,18 +47,21 @@ var transformer = function transformer(file, api, options) {
59
47
  });
60
48
  var hasHref = buttonAttributes.includes('href');
61
49
  var hasIcon = buttonAttributes.includes('iconBefore') || buttonAttributes.includes('iconAfter');
62
- var isLinkIconButton = hasHref && hasIcon && ((_element$value$childr = element.value.children) === null || _element$value$childr === void 0 ? void 0 : _element$value$childr.length) === 0;
50
+ var hasNoChildren = ((_element$value$childr = element.value.children) === null || _element$value$childr === void 0 ? void 0 : _element$value$childr.length) === 0;
51
+ var isFitContainerIconButton = hasIcon && hasNoChildren && buttonAttributes.includes('shouldFitContainer');
52
+ var isLinkIconButton = hasHref && hasIcon && hasNoChildren && !isFitContainerIconButton;
63
53
  var isLinkButton = hasHref && !isLinkIconButton;
64
- // TODO: add checks for unsupported icon props except label and size, e.g. primaryColor, testId, or spread props
65
- // and don't migrate these buttons.
66
- var isIconButton = !hasHref && hasIcon && ((_element$value$childr2 = element.value.children) === null || _element$value$childr2 === void 0 ? void 0 : _element$value$childr2.length) === 0;
67
- var isDefaultButtonWithAnIcon = !isLinkIconButton && !isIconButton && hasIcon;
68
-
69
- // TODO: add checks for unsupported icon props except label and size, e.g. primaryColor, testId, or spread props
70
- // and don't migrate these buttons.
54
+ var isIconButton = !hasHref && hasIcon && hasNoChildren && !isFitContainerIconButton;
55
+ var isDefaultButtonWithAnIcon = !isLinkIconButton && !isIconButton && !isFitContainerIconButton && hasIcon;
71
56
  if (isDefaultButtonWithAnIcon) {
72
57
  (0, _generateNewButtonElement.moveSizeAndLabelAttributes)(element.value, j);
73
58
  }
59
+ if (isFitContainerIconButton) {
60
+ var migratedToIconButton = (0, _migrateFitContainerIconButton.migrateFitContainerIconButton)(element, j);
61
+ if (migratedToIconButton) {
62
+ isIconButton = true;
63
+ }
64
+ }
74
65
  if (isLinkIconButton) {
75
66
  hasLinkIconButton = true;
76
67
  j(element).replaceWith((0, _generateNewButtonElement.generateNewElement)(_constants.NEW_BUTTON_VARIANTS.linkIcon.as, element.value, j));
@@ -83,32 +74,66 @@ var transformer = function transformer(file, api, options) {
83
74
  hasLinkButton = true;
84
75
  j(element).replaceWith((0, _generateNewButtonElement.generateNewElement)(_constants.NEW_BUTTON_VARIANTS.link.as, element.value, j));
85
76
  }
77
+ if (!hasHref && attributes.find(function (node) {
78
+ var _node$value, _node$name, _node$value2, _node$value3;
79
+ return node.type === 'JSXAttribute' && ((_node$value = node.value) === null || _node$value === void 0 ? void 0 : _node$value.type) === 'Literal' && (node === null || node === void 0 || (_node$name = node.name) === null || _node$name === void 0 ? void 0 : _node$name.name) === 'appearance' && ((node === null || node === void 0 || (_node$value2 = node.value) === null || _node$value2 === void 0 ? void 0 : _node$value2.value) === 'link' || (node === null || node === void 0 || (_node$value3 = node.value) === null || _node$value3 === void 0 ? void 0 : _node$value3.value) === 'subtle-link');
80
+ })) {
81
+ // @ts-ignore
82
+ (0, _codemodUtils.addCommentBefore)(j, [element], _constants.linkButtonMissingHrefComment);
83
+ }
86
84
  });
87
85
  var specifiers = [];
88
- if (hasLinkButton) {
89
- specifiers.push(j.importSpecifier(j.identifier(_constants.NEW_BUTTON_VARIANTS.link.import), j.identifier(_constants.NEW_BUTTON_VARIANTS.link.as)));
90
- }
91
- if (hasIconButton) {
92
- specifiers.push(j.importSpecifier(j.identifier(_constants.NEW_BUTTON_VARIANTS.icon.import), j.identifier(_constants.NEW_BUTTON_VARIANTS.icon.as)));
93
- }
94
- if (hasLinkIconButton) {
95
- specifiers.push(j.importSpecifier(j.identifier(_constants.NEW_BUTTON_VARIANTS.linkIcon.import), j.identifier(_constants.NEW_BUTTON_VARIANTS.linkIcon.as)));
96
- }
86
+ [hasLinkButton ? 'link' : null, hasIconButton ? 'icon' : null, hasLinkIconButton ? 'linkIcon' : null].forEach(function (variant) {
87
+ if (variant) {
88
+ specifiers.push(j.importSpecifier(j.identifier(_constants.NEW_BUTTON_VARIANTS[variant].import), j.identifier(_constants.NEW_BUTTON_VARIANTS[variant].as)));
89
+ }
90
+ });
97
91
  var oldButtonImport = fileSource.find(j.ImportDeclaration).filter(function (path) {
98
92
  return path.node.source.value === _constants.NEW_BUTTON_ENTRY_POINT || path.node.source.value === _constants.entryPointsMapping.Button;
99
93
  });
100
94
  var remainingDefaultButtons = fileSource.find(j.JSXElement).filter(function (path) {
101
- return path.value.openingElement.name.type === 'JSXIdentifier' && path.value.openingElement.name.name === specifierIdentifier;
95
+ return path.value.openingElement.name.type === 'JSXIdentifier' && path.value.openingElement.name.name === specifierIdentifier && !(0, _hasUnsupportedProps.ifHasUnsupportedProps)(path.value.openingElement.attributes);
102
96
  }).length > 0 || fileSource.find(j.CallExpression).filter(function (path) {
103
97
  return path.node.arguments.map(function (argument) {
104
98
  return argument.type === 'Identifier' && (argument === null || argument === void 0 ? void 0 : argument.name);
105
99
  }).includes(specifierIdentifier);
106
100
  }).length > 0;
101
+ if (remainingDefaultButtons) {
102
+ specifiers.push(j.importSpecifier(j.identifier(_constants.NEW_BUTTON_VARIANTS.default.import), j.identifier(specifierIdentifier)));
103
+ }
104
+ var buttonsWithUnsupportedProps = allButtons.filter(function (path) {
105
+ return (0, _hasUnsupportedProps.ifHasUnsupportedProps)(path.value.openingElement.attributes);
106
+ });
107
+ if (buttonsWithUnsupportedProps.length) {
108
+ // add comment to all buttons with unsupported props: "component", "style", "css"
109
+ buttonsWithUnsupportedProps.forEach(function (element) {
110
+ var _element$value$openin;
111
+ var attribute = (_element$value$openin = element.value.openingElement.attributes) === null || _element$value$openin === void 0 ? void 0 : _element$value$openin.find(function (node) {
112
+ return node.type === 'JSXAttribute' && typeof node.name.name === 'string' && _constants.unsupportedProps.includes(node.name.name);
113
+ });
114
+ if (attribute) {
115
+ (0, _codemodUtils.addCommentBefore)(j, j(attribute), _constants.buttonPropsNoLongerSupportedComment, 'line');
116
+ }
117
+ });
118
+
119
+ // rename all buttons with unsupported props to LegacyButton if default new button is imported
120
+ if (specifiers.find(function (specifier) {
121
+ return specifier.imported.name === _constants.NEW_BUTTON_VARIANTS.default.import;
122
+ })) {
123
+ (0, _renameDefaultButtonToLegacyButton.renameDefaultButtonToLegacyButtonImport)(oldButtonImport, buttonsWithUnsupportedProps, j);
124
+ }
125
+ }
107
126
  if (specifiers.length || remainingDefaultButtons) {
108
- if (remainingDefaultButtons) {
109
- specifiers.push(j.importSpecifier(j.identifier(_constants.NEW_BUTTON_VARIANTS.default.import), j.identifier(specifierIdentifier)));
127
+ // split the type imports into a separate import
128
+ var typeSpecifier = buttonImports.find(j.ImportSpecifier).filter(function (path) {
129
+ return path.node.imported.name === _constants.ButtonPropsTypeName;
130
+ });
131
+ if (typeSpecifier.length > 0) {
132
+ oldButtonImport.insertBefore(j.importDeclaration([j.importSpecifier(j.identifier(_constants.ButtonPropsTypeName), j.identifier(typeSpecifier.get(0).node.local.name))], j.stringLiteral(_constants.entryPointsMapping.Button), 'type'));
110
133
  }
111
134
  oldButtonImport.replaceWith(j.importDeclaration(specifiers, j.stringLiteral(_constants.NEW_BUTTON_ENTRY_POINT)));
135
+
136
+ // add eslint-disable-next-line @atlaskit/design-system/no-banned-imports comment if unsafe import is allowed in custom options
112
137
  if (_constants.NEW_BUTTON_ENTRY_POINT.includes('unsafe') && (options === null || options === void 0 ? void 0 : options.allowUnsafeImport) === true) {
113
138
  (0, _codemodUtils.addCommentBefore)(j, fileSource.find(j.ImportDeclaration).filter(function (path) {
114
139
  return path.node.source.value === _constants.NEW_BUTTON_ENTRY_POINT;
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.eslintDisableComment = exports.entryPointsMapping = 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.iconPropsNoLongerSupportedComment = exports.eslintDisableComment = exports.entryPointsMapping = exports.buttonPropsNoLongerSupportedComment = exports.PRINT_SETTINGS = exports.NEW_BUTTON_VARIANTS = exports.NEW_BUTTON_ENTRY_POINT = exports.ButtonPropsTypeName = exports.BUTTON_TYPES = void 0;
7
7
  var PRINT_SETTINGS = exports.PRINT_SETTINGS = {
8
8
  quote: 'single'
9
9
  };
@@ -33,4 +33,11 @@ var entryPointsMapping = exports.entryPointsMapping = {
33
33
  CustomThemeButton: '@atlaskit/button/custom-theme-button'
34
34
  };
35
35
  var BUTTON_TYPES = exports.BUTTON_TYPES = ['Appearance', 'Spacing', 'BaseOwnProps', 'BaseProps', 'ButtonProps', 'LoadingButtonProps', 'LoadingButtonOwnProps', 'ThemeTokens', 'ThemeProps', 'InteractionState', 'CustomThemeButtonProps', 'CustomThemeButtonOwnProps'];
36
- var eslintDisableComment = exports.eslintDisableComment = 'eslint-disable-next-line @atlaskit/design-system/no-banned-imports';
36
+ var eslintDisableComment = exports.eslintDisableComment = 'eslint-disable-next-line @atlaskit/design-system/no-banned-imports';
37
+ var ButtonPropsTypeName = exports.ButtonPropsTypeName = 'ButtonProps';
38
+ var unsupportedProps = exports.unsupportedProps = ['component', 'css', 'style'];
39
+ 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.";
40
+ 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.";
41
+ var buttonPropsNoLongerSupportedComment = exports.buttonPropsNoLongerSupportedComment = "Buttons with \"component\", \"css\" or \"style\" prop can't be automatically migrated with codemods. Please migrate it manually.";
42
+ var migrateFitContainerButtonToDefaultButtonComment = exports.migrateFitContainerButtonToDefaultButtonComment = "Migrated to a default button with text which is from the icon label.";
43
+ var migrateFitContainerButtonToIconButtonComment = exports.migrateFitContainerButtonToIconButtonComment = "\"shouldFitContainer\" is not available in icon buttons, please consider using a default button with text.";
@@ -3,26 +3,46 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.moveSizeAndLabelAttributes = exports.generateNewElement = void 0;
6
+ exports.moveSizeAndLabelAttributes = exports.getIconElement = exports.getIconAttributes = exports.generateNewElement = void 0;
7
+ var _codemodUtils = require("@atlaskit/codemod-utils");
7
8
  var _constants = require("../utils/constants");
8
- var getIconAttribute = function getIconAttribute(attributes) {
9
+ var getIconAttributes = exports.getIconAttributes = function getIconAttributes(attributes) {
9
10
  var iconAttr = attributes === null || attributes === void 0 ? void 0 : attributes.filter(function (attribute) {
10
11
  return attribute.type === 'JSXAttribute' && (attribute.name.name === 'iconBefore' || attribute.name.name === 'iconAfter');
11
12
  });
12
- if (attributes && iconAttr !== null && iconAttr !== void 0 && iconAttr.length && iconAttr[0].type === 'JSXAttribute') {
13
- return iconAttr[0];
13
+ if (iconAttr !== null && iconAttr !== void 0 && iconAttr.length) {
14
+ return iconAttr;
14
15
  }
15
16
  return null;
16
17
  };
17
- var moveSizeAndLabelAttributes = exports.moveSizeAndLabelAttributes = function moveSizeAndLabelAttributes(element, j) {
18
+ var getIconElement = exports.getIconElement = function getIconElement(iconAttr) {
18
19
  var _iconAttr$value;
20
+ if (iconAttr && ((_iconAttr$value = iconAttr.value) === null || _iconAttr$value === void 0 ? void 0 : _iconAttr$value.type) === 'JSXExpressionContainer' && iconAttr.value.expression.type === 'JSXElement') {
21
+ return iconAttr.value.expression;
22
+ }
23
+ return null;
24
+ };
25
+ var moveSizeAndLabelAttributes = exports.moveSizeAndLabelAttributes = function moveSizeAndLabelAttributes(element, j) {
19
26
  var iconRenamed = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
20
27
  var attributes = element.openingElement.attributes;
21
- var iconAttr = attributes && getIconAttribute(attributes);
22
- if (iconAttr && ((_iconAttr$value = iconAttr.value) === null || _iconAttr$value === void 0 ? void 0 : _iconAttr$value.type) === 'JSXExpressionContainer' && iconAttr.value.expression.type === 'JSXElement') {
23
- var iconElement = iconAttr.value.expression;
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);
32
+ if (!iconElement) {
33
+ return;
34
+ }
24
35
  var iconJSXElementAttributes = iconElement.openingElement.attributes;
36
+ // add inlined comment to the icon if it has any attributes other than label and size
25
37
  if (Array.isArray(iconJSXElementAttributes)) {
38
+ var _sizeAttribute$value;
39
+ var ifIconAttributeEitherLabelOrSize = iconJSXElementAttributes === null || iconJSXElementAttributes === void 0 ? void 0 : iconJSXElementAttributes.every(function (attribute) {
40
+ return attribute.type === 'JSXAttribute' && typeof attribute.name.name === 'string' && (attribute.name.name === 'size' || attribute.name.name === 'label');
41
+ });
42
+ if (!ifIconAttributeEitherLabelOrSize) {
43
+ (0, _codemodUtils.addCommentBefore)(j, j(iconAttr), _constants.iconPropsNoLongerSupportedComment, 'line');
44
+ }
45
+
26
46
  // move label and size attributes from icon to the root Button prop
27
47
  var labelAttribute = iconJSXElementAttributes.find(function (attribute) {
28
48
  return attribute.type === 'JSXAttribute' && attribute.name.name === 'label';
@@ -33,27 +53,27 @@ var moveSizeAndLabelAttributes = exports.moveSizeAndLabelAttributes = function m
33
53
  var sizeAttribute = iconJSXElementAttributes.find(function (attribute) {
34
54
  return attribute.type === 'JSXAttribute' && attribute.name.name === 'size';
35
55
  });
36
- if (sizeAttribute && sizeAttribute.type === 'JSXAttribute') {
56
+ if (sizeAttribute && sizeAttribute.type === 'JSXAttribute' && ((_sizeAttribute$value = sizeAttribute.value) === null || _sizeAttribute$value === void 0 ? void 0 : _sizeAttribute$value.type) === 'Literal' && sizeAttribute.value.value !== 'medium') {
37
57
  sizeAttribute.name.name = iconRenamed ? 'UNSAFE_size' : "UNSAFE_".concat(iconAttr.name.name, "_size");
38
58
  attributes === null || attributes === void 0 || attributes.push(sizeAttribute);
39
59
  }
40
60
  }
41
61
 
42
62
  // replace JSXElement with identifier {<MoreIcon />} => {MoreIcon}
43
- if (iconElement.openingElement.name.type === 'JSXIdentifier') {
63
+ if (iconElement.openingElement.name.type === 'JSXIdentifier' && ((_iconAttr$value2 = iconAttr.value) === null || _iconAttr$value2 === void 0 ? void 0 : _iconAttr$value2.type) === 'JSXExpressionContainer') {
44
64
  iconAttr.value.expression = j.identifier(iconElement.openingElement.name.name);
45
65
  }
46
- }
66
+ });
47
67
  };
48
68
  var generateNewElement = exports.generateNewElement = function generateNewElement(variant, element, j) {
49
69
  var attributes = element.openingElement.attributes;
50
- var iconAttr = attributes && getIconAttribute(attributes);
70
+ var iconAttrs = attributes && getIconAttributes(attributes);
51
71
  var isIconOrLinkIcon = variant === _constants.NEW_BUTTON_VARIANTS.icon.as || variant === _constants.NEW_BUTTON_VARIANTS.linkIcon.as;
52
- if (isIconOrLinkIcon && iconAttr) {
72
+ if (isIconOrLinkIcon && iconAttrs !== null && iconAttrs !== void 0 && iconAttrs.length) {
53
73
  moveSizeAndLabelAttributes(element, j, true);
54
74
 
55
75
  // rename iconBefore/iconAfter to icon
56
- iconAttr.name.name = 'icon';
76
+ iconAttrs[0].name.name = 'icon';
57
77
  }
58
78
  return j.jsxElement(
59
79
  // self closing if it's an icon button or icon link button
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ifHasUnsupportedProps = void 0;
7
+ var _constants = require("./constants");
8
+ var ifHasUnsupportedProps = exports.ifHasUnsupportedProps = function ifHasUnsupportedProps(attributes) {
9
+ return Boolean(attributes && (attributes === null || attributes === void 0 ? void 0 : attributes.some(function (node) {
10
+ return node.type === 'JSXAttribute' && _constants.unsupportedProps.includes(String(node.name.name));
11
+ })));
12
+ };
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.checkIfVariantAlreadyImported = void 0;
7
+ var _constants = require("./constants");
8
+ var checkIfVariantAlreadyImported = exports.checkIfVariantAlreadyImported = function checkIfVariantAlreadyImported(variant, fileSource, j) {
9
+ return fileSource.find(j.ImportDeclaration).filter(function (path) {
10
+ return path.node.source.value === _constants.NEW_BUTTON_ENTRY_POINT;
11
+ }).find(j.ImportSpecifier).filter(function (path) {
12
+ return path.node.imported.name === variant;
13
+ }).length > 0;
14
+ };
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.migrateFitContainerIconButton = void 0;
7
+ var _codemodUtils = require("@atlaskit/codemod-utils");
8
+ var _generateNewButtonElement = require("../utils/generate-new-button-element");
9
+ var _constants = require("./constants");
10
+ var migrateFitContainerIconButton = exports.migrateFitContainerIconButton = function migrateFitContainerIconButton(element, j) {
11
+ var _iconElement$openingE, _labelAttribute$value;
12
+ var attributes = element.value.openingElement.attributes;
13
+ var iconAttrs = attributes && (0, _generateNewButtonElement.getIconAttributes)(attributes);
14
+ var iconElement = iconAttrs && iconAttrs[0] && (0, _generateNewButtonElement.getIconElement)(iconAttrs[0]);
15
+ var labelAttribute = iconElement && Array.isArray(iconElement.openingElement.attributes) && ((_iconElement$openingE = iconElement.openingElement) === null || _iconElement$openingE === void 0 ? void 0 : _iconElement$openingE.attributes.find(function (path) {
16
+ return path.type === 'JSXAttribute' && (path.name.name === 'label' || path.name.name === 'aria-label' || path.name.name === 'aria-labelledby');
17
+ }));
18
+ var migratedToIconButton;
19
+ if (labelAttribute && labelAttribute.type === 'JSXAttribute' && ((_labelAttribute$value = labelAttribute.value) === null || _labelAttribute$value === void 0 ? void 0 : _labelAttribute$value.type) === 'Literal' && typeof labelAttribute.value.value === 'string') {
20
+ migratedToIconButton = false;
21
+ var label = labelAttribute.value.value;
22
+ var formattedLabel = "".concat(label.charAt(0).toUpperCase()).concat(label.slice(1)).split('-').join(' ');
23
+ j(element).find(j.JSXAttribute).filter(function (path) {
24
+ return path.node.name.name === 'iconBefore' || path.node.name.name === 'iconAfter';
25
+ }).remove();
26
+ var newButton = j.jsxElement(j.jsxOpeningElement(j.jsxIdentifier(_constants.NEW_BUTTON_VARIANTS.default.as), attributes), j.jsxClosingElement(j.jsxIdentifier(_constants.NEW_BUTTON_VARIANTS.default.as)), [j.jsxText(formattedLabel)]);
27
+ j(element).replaceWith(newButton);
28
+ (0, _codemodUtils.addCommentBefore)(j, j(newButton).find(j.JSXAttribute).filter(function (path) {
29
+ return path.node.name.name === 'shouldFitContainer';
30
+ }), _constants.migrateFitContainerButtonToDefaultButtonComment, 'line');
31
+ } else {
32
+ migratedToIconButton = true;
33
+ (0, _codemodUtils.addCommentBefore)(j, j(element).find(j.JSXAttribute).filter(function (path) {
34
+ return path.node.name.name === 'iconBefore' || path.node.name.name === 'iconAfter';
35
+ }), _constants.migrateFitContainerButtonToIconButtonComment, 'line');
36
+ j(element).find(j.JSXAttribute).filter(function (path) {
37
+ return path.node.name.name === 'shouldFitContainer';
38
+ }).remove();
39
+ }
40
+ return migratedToIconButton;
41
+ };
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.renameDefaultButtonToLegacyButtonImport = void 0;
7
+ var _constants = require("../utils/constants");
8
+ var renameDefaultButtonToLegacyButtonImport = exports.renameDefaultButtonToLegacyButtonImport = function renameDefaultButtonToLegacyButtonImport(oldButtonImport, oldButtonElements, j) {
9
+ oldButtonImport.insertBefore(j.importDeclaration([j.importDefaultSpecifier(j.identifier('LegacyButton'))], j.stringLiteral(_constants.entryPointsMapping.Button)));
10
+ oldButtonElements.forEach(function (element) {
11
+ var _element$value$childr, _element$value$childr2;
12
+ var legacyElement = j.jsxElement(j.jsxOpeningElement(j.jsxIdentifier('LegacyButton'), 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('LegacyButton')), element.value.children);
13
+ j(element).replaceWith(legacyElement);
14
+ });
15
+ };
@@ -1,6 +1,10 @@
1
1
  import { addCommentBefore } from '@atlaskit/codemod-utils';
2
- import { PRINT_SETTINGS, NEW_BUTTON_VARIANTS, entryPointsMapping, NEW_BUTTON_ENTRY_POINT, eslintDisableComment } from '../utils/constants';
2
+ import { PRINT_SETTINGS, NEW_BUTTON_VARIANTS, entryPointsMapping, NEW_BUTTON_ENTRY_POINT, eslintDisableComment, ButtonPropsTypeName, linkButtonMissingHrefComment, buttonPropsNoLongerSupportedComment, unsupportedProps } from '../utils/constants';
3
3
  import { generateNewElement, moveSizeAndLabelAttributes } from '../utils/generate-new-button-element';
4
+ import { ifHasUnsupportedProps } from '../utils/has-unsupported-props';
5
+ import { checkIfVariantAlreadyImported } from '../utils/if-variant-already-imported';
6
+ import { renameDefaultButtonToLegacyButtonImport } from '../utils/rename-default-button-to-legacy-button';
7
+ import { migrateFitContainerIconButton } from '../utils/migrate-fit-container-icon-button';
4
8
  const transformer = (file, api, options) => {
5
9
  const j = api.jscodeshift;
6
10
  const fileSource = j(file.source);
@@ -13,19 +17,13 @@ const transformer = (file, api, options) => {
13
17
  return fileSource.toSource();
14
18
  }
15
19
  const specifierIdentifier = defaultButtonImport.get(0).node.local.name;
16
- const attributes = fileSource.find(j.JSXElement).filter(path => path.value.openingElement.name.type === 'JSXIdentifier' && path.value.openingElement.name.name === specifierIdentifier).find(j.JSXAttribute);
17
- const hasCustomComponent = attributes.filter(attribute => attribute.node.name.name === 'component').length > 0;
18
- const hasCssProp = attributes.filter(attribute => attribute.node.name.name === 'css').length > 0;
19
- if (hasCustomComponent || hasCssProp) {
20
- return fileSource.toSource(PRINT_SETTINGS);
21
- }
22
- const checkIfVariantAlreadyImported = variant => 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;
23
- let hasLinkIconButton = checkIfVariantAlreadyImported(NEW_BUTTON_VARIANTS.linkIcon.import);
24
- let hasLinkButton = checkIfVariantAlreadyImported(NEW_BUTTON_VARIANTS.link.import);
25
- let hasIconButton = checkIfVariantAlreadyImported(NEW_BUTTON_VARIANTS.icon.import);
20
+ let hasLinkIconButton = checkIfVariantAlreadyImported(NEW_BUTTON_VARIANTS.linkIcon.import, fileSource, j);
21
+ let hasLinkButton = checkIfVariantAlreadyImported(NEW_BUTTON_VARIANTS.link.import, fileSource, j);
22
+ let hasIconButton = checkIfVariantAlreadyImported(NEW_BUTTON_VARIANTS.icon.import, fileSource, j);
26
23
  const allButtons = fileSource.find(j.JSXElement).filter(path => path.value.openingElement.name.type === 'JSXIdentifier' && path.value.openingElement.name.name === specifierIdentifier);
27
- allButtons.forEach(element => {
28
- var _element$value$childr, _element$value$childr2;
24
+ const buttonsWithoutUnsupportedProps = allButtons.filter(path => !ifHasUnsupportedProps(path.value.openingElement.attributes));
25
+ buttonsWithoutUnsupportedProps.forEach(element => {
26
+ var _element$value$childr;
29
27
  const {
30
28
  attributes
31
29
  } = element.value.openingElement;
@@ -35,18 +33,21 @@ const transformer = (file, api, options) => {
35
33
  const buttonAttributes = attributes.map(node => node.type === 'JSXAttribute' && node.name.name);
36
34
  const hasHref = buttonAttributes.includes('href');
37
35
  const hasIcon = buttonAttributes.includes('iconBefore') || buttonAttributes.includes('iconAfter');
38
- const isLinkIconButton = hasHref && hasIcon && ((_element$value$childr = element.value.children) === null || _element$value$childr === void 0 ? void 0 : _element$value$childr.length) === 0;
36
+ const hasNoChildren = ((_element$value$childr = element.value.children) === null || _element$value$childr === void 0 ? void 0 : _element$value$childr.length) === 0;
37
+ const isFitContainerIconButton = hasIcon && hasNoChildren && buttonAttributes.includes('shouldFitContainer');
38
+ const isLinkIconButton = hasHref && hasIcon && hasNoChildren && !isFitContainerIconButton;
39
39
  const isLinkButton = hasHref && !isLinkIconButton;
40
- // TODO: add checks for unsupported icon props except label and size, e.g. primaryColor, testId, or spread props
41
- // and don't migrate these buttons.
42
- const isIconButton = !hasHref && hasIcon && ((_element$value$childr2 = element.value.children) === null || _element$value$childr2 === void 0 ? void 0 : _element$value$childr2.length) === 0;
43
- const isDefaultButtonWithAnIcon = !isLinkIconButton && !isIconButton && hasIcon;
44
-
45
- // TODO: add checks for unsupported icon props except label and size, e.g. primaryColor, testId, or spread props
46
- // and don't migrate these buttons.
40
+ let isIconButton = !hasHref && hasIcon && hasNoChildren && !isFitContainerIconButton;
41
+ const isDefaultButtonWithAnIcon = !isLinkIconButton && !isIconButton && !isFitContainerIconButton && hasIcon;
47
42
  if (isDefaultButtonWithAnIcon) {
48
43
  moveSizeAndLabelAttributes(element.value, j);
49
44
  }
45
+ if (isFitContainerIconButton) {
46
+ const migratedToIconButton = migrateFitContainerIconButton(element, j);
47
+ if (migratedToIconButton) {
48
+ isIconButton = true;
49
+ }
50
+ }
50
51
  if (isLinkIconButton) {
51
52
  hasLinkIconButton = true;
52
53
  j(element).replaceWith(generateNewElement(NEW_BUTTON_VARIANTS.linkIcon.as, element.value, j));
@@ -59,24 +60,50 @@ const transformer = (file, api, options) => {
59
60
  hasLinkButton = true;
60
61
  j(element).replaceWith(generateNewElement(NEW_BUTTON_VARIANTS.link.as, element.value, j));
61
62
  }
63
+ if (!hasHref && attributes.find(node => {
64
+ var _node$value, _node$name, _node$value2, _node$value3;
65
+ return node.type === 'JSXAttribute' && ((_node$value = node.value) === null || _node$value === void 0 ? void 0 : _node$value.type) === 'Literal' && (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');
66
+ })) {
67
+ // @ts-ignore
68
+ addCommentBefore(j, [element], linkButtonMissingHrefComment);
69
+ }
62
70
  });
63
71
  const specifiers = [];
64
- if (hasLinkButton) {
65
- specifiers.push(j.importSpecifier(j.identifier(NEW_BUTTON_VARIANTS.link.import), j.identifier(NEW_BUTTON_VARIANTS.link.as)));
66
- }
67
- if (hasIconButton) {
68
- specifiers.push(j.importSpecifier(j.identifier(NEW_BUTTON_VARIANTS.icon.import), j.identifier(NEW_BUTTON_VARIANTS.icon.as)));
72
+ [hasLinkButton ? 'link' : null, hasIconButton ? 'icon' : null, hasLinkIconButton ? 'linkIcon' : null].forEach(variant => {
73
+ if (variant) {
74
+ specifiers.push(j.importSpecifier(j.identifier(NEW_BUTTON_VARIANTS[variant].import), j.identifier(NEW_BUTTON_VARIANTS[variant].as)));
75
+ }
76
+ });
77
+ const oldButtonImport = fileSource.find(j.ImportDeclaration).filter(path => path.node.source.value === NEW_BUTTON_ENTRY_POINT || path.node.source.value === entryPointsMapping.Button);
78
+ 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;
79
+ if (remainingDefaultButtons) {
80
+ specifiers.push(j.importSpecifier(j.identifier(NEW_BUTTON_VARIANTS.default.import), j.identifier(specifierIdentifier)));
69
81
  }
70
- if (hasLinkIconButton) {
71
- specifiers.push(j.importSpecifier(j.identifier(NEW_BUTTON_VARIANTS.linkIcon.import), j.identifier(NEW_BUTTON_VARIANTS.linkIcon.as)));
82
+ const buttonsWithUnsupportedProps = allButtons.filter(path => ifHasUnsupportedProps(path.value.openingElement.attributes));
83
+ if (buttonsWithUnsupportedProps.length) {
84
+ // add comment to all buttons with unsupported props: "component", "style", "css"
85
+ buttonsWithUnsupportedProps.forEach(element => {
86
+ var _element$value$openin;
87
+ 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));
88
+ if (attribute) {
89
+ addCommentBefore(j, j(attribute), buttonPropsNoLongerSupportedComment, 'line');
90
+ }
91
+ });
92
+
93
+ // rename all buttons with unsupported props to LegacyButton if default new button is imported
94
+ if (specifiers.find(specifier => specifier.imported.name === NEW_BUTTON_VARIANTS.default.import)) {
95
+ renameDefaultButtonToLegacyButtonImport(oldButtonImport, buttonsWithUnsupportedProps, j);
96
+ }
72
97
  }
73
- const oldButtonImport = fileSource.find(j.ImportDeclaration).filter(path => path.node.source.value === NEW_BUTTON_ENTRY_POINT || path.node.source.value === entryPointsMapping.Button);
74
- const remainingDefaultButtons = fileSource.find(j.JSXElement).filter(path => path.value.openingElement.name.type === 'JSXIdentifier' && path.value.openingElement.name.name === specifierIdentifier).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;
75
98
  if (specifiers.length || remainingDefaultButtons) {
76
- if (remainingDefaultButtons) {
77
- specifiers.push(j.importSpecifier(j.identifier(NEW_BUTTON_VARIANTS.default.import), j.identifier(specifierIdentifier)));
99
+ // split the type imports into a separate import
100
+ const typeSpecifier = buttonImports.find(j.ImportSpecifier).filter(path => path.node.imported.name === ButtonPropsTypeName);
101
+ if (typeSpecifier.length > 0) {
102
+ oldButtonImport.insertBefore(j.importDeclaration([j.importSpecifier(j.identifier(ButtonPropsTypeName), j.identifier(typeSpecifier.get(0).node.local.name))], j.stringLiteral(entryPointsMapping.Button), 'type'));
78
103
  }
79
104
  oldButtonImport.replaceWith(j.importDeclaration(specifiers, j.stringLiteral(NEW_BUTTON_ENTRY_POINT)));
105
+
106
+ // add eslint-disable-next-line @atlaskit/design-system/no-banned-imports comment if unsafe import is allowed in custom options
80
107
  if (NEW_BUTTON_ENTRY_POINT.includes('unsafe') && (options === null || options === void 0 ? void 0 : options.allowUnsafeImport) === true) {
81
108
  addCommentBefore(j, fileSource.find(j.ImportDeclaration).filter(path => path.node.source.value === NEW_BUTTON_ENTRY_POINT), eslintDisableComment, 'line', '');
82
109
  }
@@ -27,4 +27,11 @@ export const entryPointsMapping = {
27
27
  CustomThemeButton: '@atlaskit/button/custom-theme-button'
28
28
  };
29
29
  export const BUTTON_TYPES = ['Appearance', 'Spacing', 'BaseOwnProps', 'BaseProps', 'ButtonProps', 'LoadingButtonProps', 'LoadingButtonOwnProps', 'ThemeTokens', 'ThemeProps', 'InteractionState', 'CustomThemeButtonProps', 'CustomThemeButtonOwnProps'];
30
- export const eslintDisableComment = 'eslint-disable-next-line @atlaskit/design-system/no-banned-imports';
30
+ export const eslintDisableComment = 'eslint-disable-next-line @atlaskit/design-system/no-banned-imports';
31
+ export const ButtonPropsTypeName = 'ButtonProps';
32
+ export const unsupportedProps = ['component', 'css', 'style'];
33
+ 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.`;
34
+ export const iconPropsNoLongerSupportedComment = `"glyph", "primaryColor", "secondaryColor" and "testId" are no longer supported in button icons, please refactor the code and/or revisit the UI.`;
35
+ export const buttonPropsNoLongerSupportedComment = `Buttons with "component", "css" or "style" prop can't be automatically migrated with codemods. Please migrate it manually.`;
36
+ export const migrateFitContainerButtonToDefaultButtonComment = `Migrated to a default button with text which is from the icon label.`;
37
+ export const migrateFitContainerButtonToIconButtonComment = `"shouldFitContainer" is not available in icon buttons, please consider using a default button with text.`;
@@ -1,50 +1,68 @@
1
- import { NEW_BUTTON_VARIANTS } from '../utils/constants';
2
- const getIconAttribute = attributes => {
1
+ import { addCommentBefore } from '@atlaskit/codemod-utils';
2
+ import { NEW_BUTTON_VARIANTS, iconPropsNoLongerSupportedComment } from '../utils/constants';
3
+ export const getIconAttributes = attributes => {
3
4
  const iconAttr = attributes === null || attributes === void 0 ? void 0 : attributes.filter(attribute => attribute.type === 'JSXAttribute' && (attribute.name.name === 'iconBefore' || attribute.name.name === 'iconAfter'));
4
- if (attributes && iconAttr !== null && iconAttr !== void 0 && iconAttr.length && iconAttr[0].type === 'JSXAttribute') {
5
- return iconAttr[0];
5
+ if (iconAttr !== null && iconAttr !== void 0 && iconAttr.length) {
6
+ return iconAttr;
6
7
  }
7
8
  return null;
8
9
  };
9
- export const moveSizeAndLabelAttributes = (element, j, iconRenamed = false) => {
10
+ export const getIconElement = iconAttr => {
10
11
  var _iconAttr$value;
12
+ if (iconAttr && ((_iconAttr$value = iconAttr.value) === null || _iconAttr$value === void 0 ? void 0 : _iconAttr$value.type) === 'JSXExpressionContainer' && iconAttr.value.expression.type === 'JSXElement') {
13
+ return iconAttr.value.expression;
14
+ }
15
+ return null;
16
+ };
17
+ export const moveSizeAndLabelAttributes = (element, j, iconRenamed = false) => {
11
18
  const {
12
19
  attributes
13
20
  } = element.openingElement;
14
- const iconAttr = attributes && getIconAttribute(attributes);
15
- if (iconAttr && ((_iconAttr$value = iconAttr.value) === null || _iconAttr$value === void 0 ? void 0 : _iconAttr$value.type) === 'JSXExpressionContainer' && iconAttr.value.expression.type === 'JSXElement') {
16
- const iconElement = iconAttr.value.expression;
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);
25
+ if (!iconElement) {
26
+ return;
27
+ }
17
28
  const iconJSXElementAttributes = iconElement.openingElement.attributes;
29
+ // add inlined comment to the icon if it has any attributes other than label and size
18
30
  if (Array.isArray(iconJSXElementAttributes)) {
31
+ var _sizeAttribute$value;
32
+ 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'));
33
+ if (!ifIconAttributeEitherLabelOrSize) {
34
+ addCommentBefore(j, j(iconAttr), iconPropsNoLongerSupportedComment, 'line');
35
+ }
36
+
19
37
  // move label and size attributes from icon to the root Button prop
20
38
  const labelAttribute = iconJSXElementAttributes.find(attribute => attribute.type === 'JSXAttribute' && attribute.name.name === 'label');
21
39
  if (labelAttribute && labelAttribute.type === 'JSXAttribute' && iconRenamed) {
22
40
  attributes === null || attributes === void 0 ? void 0 : attributes.push(labelAttribute);
23
41
  }
24
42
  const sizeAttribute = iconJSXElementAttributes.find(attribute => attribute.type === 'JSXAttribute' && attribute.name.name === 'size');
25
- if (sizeAttribute && sizeAttribute.type === 'JSXAttribute') {
43
+ if (sizeAttribute && sizeAttribute.type === 'JSXAttribute' && ((_sizeAttribute$value = sizeAttribute.value) === null || _sizeAttribute$value === void 0 ? void 0 : _sizeAttribute$value.type) === 'Literal' && sizeAttribute.value.value !== 'medium') {
26
44
  sizeAttribute.name.name = iconRenamed ? 'UNSAFE_size' : `UNSAFE_${iconAttr.name.name}_size`;
27
45
  attributes === null || attributes === void 0 ? void 0 : attributes.push(sizeAttribute);
28
46
  }
29
47
  }
30
48
 
31
49
  // replace JSXElement with identifier {<MoreIcon />} => {MoreIcon}
32
- if (iconElement.openingElement.name.type === 'JSXIdentifier') {
50
+ if (iconElement.openingElement.name.type === 'JSXIdentifier' && ((_iconAttr$value2 = iconAttr.value) === null || _iconAttr$value2 === void 0 ? void 0 : _iconAttr$value2.type) === 'JSXExpressionContainer') {
33
51
  iconAttr.value.expression = j.identifier(iconElement.openingElement.name.name);
34
52
  }
35
- }
53
+ });
36
54
  };
37
55
  export const generateNewElement = (variant, element, j) => {
38
56
  const {
39
57
  attributes
40
58
  } = element.openingElement;
41
- const iconAttr = attributes && getIconAttribute(attributes);
59
+ const iconAttrs = attributes && getIconAttributes(attributes);
42
60
  const isIconOrLinkIcon = variant === NEW_BUTTON_VARIANTS.icon.as || variant === NEW_BUTTON_VARIANTS.linkIcon.as;
43
- if (isIconOrLinkIcon && iconAttr) {
61
+ if (isIconOrLinkIcon && iconAttrs !== null && iconAttrs !== void 0 && iconAttrs.length) {
44
62
  moveSizeAndLabelAttributes(element, j, true);
45
63
 
46
64
  // rename iconBefore/iconAfter to icon
47
- iconAttr.name.name = 'icon';
65
+ iconAttrs[0].name.name = 'icon';
48
66
  }
49
67
  return j.jsxElement(
50
68
  // self closing if it's an icon button or icon link button
@@ -0,0 +1,2 @@
1
+ import { unsupportedProps } from './constants';
2
+ export const ifHasUnsupportedProps = attributes => Boolean(attributes && (attributes === null || attributes === void 0 ? void 0 : attributes.some(node => node.type === 'JSXAttribute' && unsupportedProps.includes(String(node.name.name)))));
@@ -0,0 +1,4 @@
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;
4
+ };
@@ -0,0 +1,27 @@
1
+ import { addCommentBefore } from '@atlaskit/codemod-utils';
2
+ import { getIconAttributes, getIconElement } from '../utils/generate-new-button-element';
3
+ import { NEW_BUTTON_VARIANTS, migrateFitContainerButtonToDefaultButtonComment, migrateFitContainerButtonToIconButtonComment } from './constants';
4
+ export const migrateFitContainerIconButton = (element, j) => {
5
+ var _iconElement$openingE, _labelAttribute$value;
6
+ const {
7
+ attributes
8
+ } = element.value.openingElement;
9
+ const iconAttrs = attributes && getIconAttributes(attributes);
10
+ const iconElement = iconAttrs && iconAttrs[0] && getIconElement(iconAttrs[0]);
11
+ const labelAttribute = iconElement && Array.isArray(iconElement.openingElement.attributes) && ((_iconElement$openingE = iconElement.openingElement) === null || _iconElement$openingE === void 0 ? void 0 : _iconElement$openingE.attributes.find(path => path.type === 'JSXAttribute' && (path.name.name === 'label' || path.name.name === 'aria-label' || path.name.name === 'aria-labelledby')));
12
+ let migratedToIconButton;
13
+ if (labelAttribute && labelAttribute.type === 'JSXAttribute' && ((_labelAttribute$value = labelAttribute.value) === null || _labelAttribute$value === void 0 ? void 0 : _labelAttribute$value.type) === 'Literal' && typeof labelAttribute.value.value === 'string') {
14
+ migratedToIconButton = false;
15
+ const label = labelAttribute.value.value;
16
+ const formattedLabel = `${label.charAt(0).toUpperCase()}${label.slice(1)}`.split('-').join(' ');
17
+ j(element).find(j.JSXAttribute).filter(path => path.node.name.name === 'iconBefore' || path.node.name.name === 'iconAfter').remove();
18
+ const newButton = j.jsxElement(j.jsxOpeningElement(j.jsxIdentifier(NEW_BUTTON_VARIANTS.default.as), attributes), j.jsxClosingElement(j.jsxIdentifier(NEW_BUTTON_VARIANTS.default.as)), [j.jsxText(formattedLabel)]);
19
+ j(element).replaceWith(newButton);
20
+ addCommentBefore(j, j(newButton).find(j.JSXAttribute).filter(path => path.node.name.name === 'shouldFitContainer'), migrateFitContainerButtonToDefaultButtonComment, 'line');
21
+ } else {
22
+ migratedToIconButton = true;
23
+ addCommentBefore(j, j(element).find(j.JSXAttribute).filter(path => path.node.name.name === 'iconBefore' || path.node.name.name === 'iconAfter'), migrateFitContainerButtonToIconButtonComment, 'line');
24
+ j(element).find(j.JSXAttribute).filter(path => path.node.name.name === 'shouldFitContainer').remove();
25
+ }
26
+ return migratedToIconButton;
27
+ };
@@ -0,0 +1,9 @@
1
+ import { entryPointsMapping } from '../utils/constants';
2
+ export const renameDefaultButtonToLegacyButtonImport = (oldButtonImport, oldButtonElements, j) => {
3
+ oldButtonImport.insertBefore(j.importDeclaration([j.importDefaultSpecifier(j.identifier('LegacyButton'))], j.stringLiteral(entryPointsMapping.Button)));
4
+ oldButtonElements.forEach(element => {
5
+ var _element$value$childr, _element$value$childr2;
6
+ const legacyElement = j.jsxElement(j.jsxOpeningElement(j.jsxIdentifier('LegacyButton'), 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('LegacyButton')), element.value.children);
7
+ j(element).replaceWith(legacyElement);
8
+ });
9
+ };
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.17.2", _PACKAGE_VERSION_ = _process$env$_PACKAGE === void 0 ? '0.0.0-dev' : _process$env$_PACKAGE;
301
+ _process$env$_PACKAGE = "0.17.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) {
@@ -1,6 +1,10 @@
1
1
  import { addCommentBefore } from '@atlaskit/codemod-utils';
2
- import { PRINT_SETTINGS, NEW_BUTTON_VARIANTS, entryPointsMapping, NEW_BUTTON_ENTRY_POINT, eslintDisableComment } from '../utils/constants';
2
+ import { PRINT_SETTINGS, NEW_BUTTON_VARIANTS, entryPointsMapping, NEW_BUTTON_ENTRY_POINT, eslintDisableComment, ButtonPropsTypeName, linkButtonMissingHrefComment, buttonPropsNoLongerSupportedComment, unsupportedProps } from '../utils/constants';
3
3
  import { generateNewElement, moveSizeAndLabelAttributes } from '../utils/generate-new-button-element';
4
+ import { ifHasUnsupportedProps } from '../utils/has-unsupported-props';
5
+ import { checkIfVariantAlreadyImported } from '../utils/if-variant-already-imported';
6
+ import { renameDefaultButtonToLegacyButtonImport } from '../utils/rename-default-button-to-legacy-button';
7
+ import { migrateFitContainerIconButton } from '../utils/migrate-fit-container-icon-button';
4
8
  var transformer = function transformer(file, api, options) {
5
9
  var j = api.jscodeshift;
6
10
  var fileSource = j(file.source);
@@ -17,33 +21,17 @@ var transformer = function transformer(file, api, options) {
17
21
  return fileSource.toSource();
18
22
  }
19
23
  var specifierIdentifier = defaultButtonImport.get(0).node.local.name;
20
- var attributes = fileSource.find(j.JSXElement).filter(function (path) {
21
- return path.value.openingElement.name.type === 'JSXIdentifier' && path.value.openingElement.name.name === specifierIdentifier;
22
- }).find(j.JSXAttribute);
23
- var hasCustomComponent = attributes.filter(function (attribute) {
24
- return attribute.node.name.name === 'component';
25
- }).length > 0;
26
- var hasCssProp = attributes.filter(function (attribute) {
27
- return attribute.node.name.name === 'css';
28
- }).length > 0;
29
- if (hasCustomComponent || hasCssProp) {
30
- return fileSource.toSource(PRINT_SETTINGS);
31
- }
32
- var checkIfVariantAlreadyImported = function checkIfVariantAlreadyImported(variant) {
33
- return fileSource.find(j.ImportDeclaration).filter(function (path) {
34
- return path.node.source.value === NEW_BUTTON_ENTRY_POINT;
35
- }).find(j.ImportSpecifier).filter(function (path) {
36
- return path.node.imported.name === variant;
37
- }).length > 0;
38
- };
39
- var hasLinkIconButton = checkIfVariantAlreadyImported(NEW_BUTTON_VARIANTS.linkIcon.import);
40
- var hasLinkButton = checkIfVariantAlreadyImported(NEW_BUTTON_VARIANTS.link.import);
41
- var hasIconButton = checkIfVariantAlreadyImported(NEW_BUTTON_VARIANTS.icon.import);
24
+ var hasLinkIconButton = checkIfVariantAlreadyImported(NEW_BUTTON_VARIANTS.linkIcon.import, fileSource, j);
25
+ var hasLinkButton = checkIfVariantAlreadyImported(NEW_BUTTON_VARIANTS.link.import, fileSource, j);
26
+ var hasIconButton = checkIfVariantAlreadyImported(NEW_BUTTON_VARIANTS.icon.import, fileSource, j);
42
27
  var allButtons = fileSource.find(j.JSXElement).filter(function (path) {
43
28
  return path.value.openingElement.name.type === 'JSXIdentifier' && path.value.openingElement.name.name === specifierIdentifier;
44
29
  });
45
- allButtons.forEach(function (element) {
46
- var _element$value$childr, _element$value$childr2;
30
+ var buttonsWithoutUnsupportedProps = allButtons.filter(function (path) {
31
+ return !ifHasUnsupportedProps(path.value.openingElement.attributes);
32
+ });
33
+ buttonsWithoutUnsupportedProps.forEach(function (element) {
34
+ var _element$value$childr;
47
35
  var attributes = element.value.openingElement.attributes;
48
36
  if (!attributes) {
49
37
  return;
@@ -53,18 +41,21 @@ var transformer = function transformer(file, api, options) {
53
41
  });
54
42
  var hasHref = buttonAttributes.includes('href');
55
43
  var hasIcon = buttonAttributes.includes('iconBefore') || buttonAttributes.includes('iconAfter');
56
- var isLinkIconButton = hasHref && hasIcon && ((_element$value$childr = element.value.children) === null || _element$value$childr === void 0 ? void 0 : _element$value$childr.length) === 0;
44
+ var hasNoChildren = ((_element$value$childr = element.value.children) === null || _element$value$childr === void 0 ? void 0 : _element$value$childr.length) === 0;
45
+ var isFitContainerIconButton = hasIcon && hasNoChildren && buttonAttributes.includes('shouldFitContainer');
46
+ var isLinkIconButton = hasHref && hasIcon && hasNoChildren && !isFitContainerIconButton;
57
47
  var isLinkButton = hasHref && !isLinkIconButton;
58
- // TODO: add checks for unsupported icon props except label and size, e.g. primaryColor, testId, or spread props
59
- // and don't migrate these buttons.
60
- var isIconButton = !hasHref && hasIcon && ((_element$value$childr2 = element.value.children) === null || _element$value$childr2 === void 0 ? void 0 : _element$value$childr2.length) === 0;
61
- var isDefaultButtonWithAnIcon = !isLinkIconButton && !isIconButton && hasIcon;
62
-
63
- // TODO: add checks for unsupported icon props except label and size, e.g. primaryColor, testId, or spread props
64
- // and don't migrate these buttons.
48
+ var isIconButton = !hasHref && hasIcon && hasNoChildren && !isFitContainerIconButton;
49
+ var isDefaultButtonWithAnIcon = !isLinkIconButton && !isIconButton && !isFitContainerIconButton && hasIcon;
65
50
  if (isDefaultButtonWithAnIcon) {
66
51
  moveSizeAndLabelAttributes(element.value, j);
67
52
  }
53
+ if (isFitContainerIconButton) {
54
+ var migratedToIconButton = migrateFitContainerIconButton(element, j);
55
+ if (migratedToIconButton) {
56
+ isIconButton = true;
57
+ }
58
+ }
68
59
  if (isLinkIconButton) {
69
60
  hasLinkIconButton = true;
70
61
  j(element).replaceWith(generateNewElement(NEW_BUTTON_VARIANTS.linkIcon.as, element.value, j));
@@ -77,32 +68,66 @@ var transformer = function transformer(file, api, options) {
77
68
  hasLinkButton = true;
78
69
  j(element).replaceWith(generateNewElement(NEW_BUTTON_VARIANTS.link.as, element.value, j));
79
70
  }
71
+ if (!hasHref && attributes.find(function (node) {
72
+ var _node$value, _node$name, _node$value2, _node$value3;
73
+ return node.type === 'JSXAttribute' && ((_node$value = node.value) === null || _node$value === void 0 ? void 0 : _node$value.type) === 'Literal' && (node === null || node === void 0 || (_node$name = node.name) === null || _node$name === void 0 ? void 0 : _node$name.name) === 'appearance' && ((node === null || node === void 0 || (_node$value2 = node.value) === null || _node$value2 === void 0 ? void 0 : _node$value2.value) === 'link' || (node === null || node === void 0 || (_node$value3 = node.value) === null || _node$value3 === void 0 ? void 0 : _node$value3.value) === 'subtle-link');
74
+ })) {
75
+ // @ts-ignore
76
+ addCommentBefore(j, [element], linkButtonMissingHrefComment);
77
+ }
80
78
  });
81
79
  var specifiers = [];
82
- if (hasLinkButton) {
83
- specifiers.push(j.importSpecifier(j.identifier(NEW_BUTTON_VARIANTS.link.import), j.identifier(NEW_BUTTON_VARIANTS.link.as)));
84
- }
85
- if (hasIconButton) {
86
- specifiers.push(j.importSpecifier(j.identifier(NEW_BUTTON_VARIANTS.icon.import), j.identifier(NEW_BUTTON_VARIANTS.icon.as)));
87
- }
88
- if (hasLinkIconButton) {
89
- specifiers.push(j.importSpecifier(j.identifier(NEW_BUTTON_VARIANTS.linkIcon.import), j.identifier(NEW_BUTTON_VARIANTS.linkIcon.as)));
90
- }
80
+ [hasLinkButton ? 'link' : null, hasIconButton ? 'icon' : null, hasLinkIconButton ? 'linkIcon' : null].forEach(function (variant) {
81
+ if (variant) {
82
+ specifiers.push(j.importSpecifier(j.identifier(NEW_BUTTON_VARIANTS[variant].import), j.identifier(NEW_BUTTON_VARIANTS[variant].as)));
83
+ }
84
+ });
91
85
  var oldButtonImport = fileSource.find(j.ImportDeclaration).filter(function (path) {
92
86
  return path.node.source.value === NEW_BUTTON_ENTRY_POINT || path.node.source.value === entryPointsMapping.Button;
93
87
  });
94
88
  var remainingDefaultButtons = fileSource.find(j.JSXElement).filter(function (path) {
95
- return path.value.openingElement.name.type === 'JSXIdentifier' && path.value.openingElement.name.name === specifierIdentifier;
89
+ return path.value.openingElement.name.type === 'JSXIdentifier' && path.value.openingElement.name.name === specifierIdentifier && !ifHasUnsupportedProps(path.value.openingElement.attributes);
96
90
  }).length > 0 || fileSource.find(j.CallExpression).filter(function (path) {
97
91
  return path.node.arguments.map(function (argument) {
98
92
  return argument.type === 'Identifier' && (argument === null || argument === void 0 ? void 0 : argument.name);
99
93
  }).includes(specifierIdentifier);
100
94
  }).length > 0;
95
+ if (remainingDefaultButtons) {
96
+ specifiers.push(j.importSpecifier(j.identifier(NEW_BUTTON_VARIANTS.default.import), j.identifier(specifierIdentifier)));
97
+ }
98
+ var buttonsWithUnsupportedProps = allButtons.filter(function (path) {
99
+ return ifHasUnsupportedProps(path.value.openingElement.attributes);
100
+ });
101
+ if (buttonsWithUnsupportedProps.length) {
102
+ // add comment to all buttons with unsupported props: "component", "style", "css"
103
+ buttonsWithUnsupportedProps.forEach(function (element) {
104
+ var _element$value$openin;
105
+ var attribute = (_element$value$openin = element.value.openingElement.attributes) === null || _element$value$openin === void 0 ? void 0 : _element$value$openin.find(function (node) {
106
+ return node.type === 'JSXAttribute' && typeof node.name.name === 'string' && unsupportedProps.includes(node.name.name);
107
+ });
108
+ if (attribute) {
109
+ addCommentBefore(j, j(attribute), buttonPropsNoLongerSupportedComment, 'line');
110
+ }
111
+ });
112
+
113
+ // rename all buttons with unsupported props to LegacyButton if default new button is imported
114
+ if (specifiers.find(function (specifier) {
115
+ return specifier.imported.name === NEW_BUTTON_VARIANTS.default.import;
116
+ })) {
117
+ renameDefaultButtonToLegacyButtonImport(oldButtonImport, buttonsWithUnsupportedProps, j);
118
+ }
119
+ }
101
120
  if (specifiers.length || remainingDefaultButtons) {
102
- if (remainingDefaultButtons) {
103
- specifiers.push(j.importSpecifier(j.identifier(NEW_BUTTON_VARIANTS.default.import), j.identifier(specifierIdentifier)));
121
+ // split the type imports into a separate import
122
+ var typeSpecifier = buttonImports.find(j.ImportSpecifier).filter(function (path) {
123
+ return path.node.imported.name === ButtonPropsTypeName;
124
+ });
125
+ if (typeSpecifier.length > 0) {
126
+ oldButtonImport.insertBefore(j.importDeclaration([j.importSpecifier(j.identifier(ButtonPropsTypeName), j.identifier(typeSpecifier.get(0).node.local.name))], j.stringLiteral(entryPointsMapping.Button), 'type'));
104
127
  }
105
128
  oldButtonImport.replaceWith(j.importDeclaration(specifiers, j.stringLiteral(NEW_BUTTON_ENTRY_POINT)));
129
+
130
+ // add eslint-disable-next-line @atlaskit/design-system/no-banned-imports comment if unsafe import is allowed in custom options
106
131
  if (NEW_BUTTON_ENTRY_POINT.includes('unsafe') && (options === null || options === void 0 ? void 0 : options.allowUnsafeImport) === true) {
107
132
  addCommentBefore(j, fileSource.find(j.ImportDeclaration).filter(function (path) {
108
133
  return path.node.source.value === NEW_BUTTON_ENTRY_POINT;
@@ -27,4 +27,11 @@ export var entryPointsMapping = {
27
27
  CustomThemeButton: '@atlaskit/button/custom-theme-button'
28
28
  };
29
29
  export var BUTTON_TYPES = ['Appearance', 'Spacing', 'BaseOwnProps', 'BaseProps', 'ButtonProps', 'LoadingButtonProps', 'LoadingButtonOwnProps', 'ThemeTokens', 'ThemeProps', 'InteractionState', 'CustomThemeButtonProps', 'CustomThemeButtonOwnProps'];
30
- export var eslintDisableComment = 'eslint-disable-next-line @atlaskit/design-system/no-banned-imports';
30
+ export var eslintDisableComment = 'eslint-disable-next-line @atlaskit/design-system/no-banned-imports';
31
+ export var ButtonPropsTypeName = 'ButtonProps';
32
+ export var unsupportedProps = ['component', 'css', 'style'];
33
+ 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.";
34
+ export var iconPropsNoLongerSupportedComment = "\"glyph\", \"primaryColor\", \"secondaryColor\" and \"testId\" are no longer supported in button icons, please refactor the code and/or revisit the UI.";
35
+ export var buttonPropsNoLongerSupportedComment = "Buttons with \"component\", \"css\" or \"style\" prop can't be automatically migrated with codemods. Please migrate it manually.";
36
+ export var migrateFitContainerButtonToDefaultButtonComment = "Migrated to a default button with text which is from the icon label.";
37
+ export var migrateFitContainerButtonToIconButtonComment = "\"shouldFitContainer\" is not available in icon buttons, please consider using a default button with text.";
@@ -1,22 +1,42 @@
1
- import { NEW_BUTTON_VARIANTS } from '../utils/constants';
2
- var getIconAttribute = function getIconAttribute(attributes) {
1
+ import { addCommentBefore } from '@atlaskit/codemod-utils';
2
+ import { NEW_BUTTON_VARIANTS, iconPropsNoLongerSupportedComment } from '../utils/constants';
3
+ export var getIconAttributes = function getIconAttributes(attributes) {
3
4
  var iconAttr = attributes === null || attributes === void 0 ? void 0 : attributes.filter(function (attribute) {
4
5
  return attribute.type === 'JSXAttribute' && (attribute.name.name === 'iconBefore' || attribute.name.name === 'iconAfter');
5
6
  });
6
- if (attributes && iconAttr !== null && iconAttr !== void 0 && iconAttr.length && iconAttr[0].type === 'JSXAttribute') {
7
- return iconAttr[0];
7
+ if (iconAttr !== null && iconAttr !== void 0 && iconAttr.length) {
8
+ return iconAttr;
8
9
  }
9
10
  return null;
10
11
  };
11
- export var moveSizeAndLabelAttributes = function moveSizeAndLabelAttributes(element, j) {
12
+ export var getIconElement = function getIconElement(iconAttr) {
12
13
  var _iconAttr$value;
14
+ if (iconAttr && ((_iconAttr$value = iconAttr.value) === null || _iconAttr$value === void 0 ? void 0 : _iconAttr$value.type) === 'JSXExpressionContainer' && iconAttr.value.expression.type === 'JSXElement') {
15
+ return iconAttr.value.expression;
16
+ }
17
+ return null;
18
+ };
19
+ export var moveSizeAndLabelAttributes = function moveSizeAndLabelAttributes(element, j) {
13
20
  var iconRenamed = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
14
21
  var attributes = element.openingElement.attributes;
15
- var iconAttr = attributes && getIconAttribute(attributes);
16
- if (iconAttr && ((_iconAttr$value = iconAttr.value) === null || _iconAttr$value === void 0 ? void 0 : _iconAttr$value.type) === 'JSXExpressionContainer' && iconAttr.value.expression.type === 'JSXElement') {
17
- var iconElement = iconAttr.value.expression;
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);
26
+ if (!iconElement) {
27
+ return;
28
+ }
18
29
  var iconJSXElementAttributes = iconElement.openingElement.attributes;
30
+ // add inlined comment to the icon if it has any attributes other than label and size
19
31
  if (Array.isArray(iconJSXElementAttributes)) {
32
+ var _sizeAttribute$value;
33
+ var ifIconAttributeEitherLabelOrSize = iconJSXElementAttributes === null || iconJSXElementAttributes === void 0 ? void 0 : iconJSXElementAttributes.every(function (attribute) {
34
+ return attribute.type === 'JSXAttribute' && typeof attribute.name.name === 'string' && (attribute.name.name === 'size' || attribute.name.name === 'label');
35
+ });
36
+ if (!ifIconAttributeEitherLabelOrSize) {
37
+ addCommentBefore(j, j(iconAttr), iconPropsNoLongerSupportedComment, 'line');
38
+ }
39
+
20
40
  // move label and size attributes from icon to the root Button prop
21
41
  var labelAttribute = iconJSXElementAttributes.find(function (attribute) {
22
42
  return attribute.type === 'JSXAttribute' && attribute.name.name === 'label';
@@ -27,27 +47,27 @@ export var moveSizeAndLabelAttributes = function moveSizeAndLabelAttributes(elem
27
47
  var sizeAttribute = iconJSXElementAttributes.find(function (attribute) {
28
48
  return attribute.type === 'JSXAttribute' && attribute.name.name === 'size';
29
49
  });
30
- if (sizeAttribute && sizeAttribute.type === 'JSXAttribute') {
50
+ if (sizeAttribute && sizeAttribute.type === 'JSXAttribute' && ((_sizeAttribute$value = sizeAttribute.value) === null || _sizeAttribute$value === void 0 ? void 0 : _sizeAttribute$value.type) === 'Literal' && sizeAttribute.value.value !== 'medium') {
31
51
  sizeAttribute.name.name = iconRenamed ? 'UNSAFE_size' : "UNSAFE_".concat(iconAttr.name.name, "_size");
32
52
  attributes === null || attributes === void 0 || attributes.push(sizeAttribute);
33
53
  }
34
54
  }
35
55
 
36
56
  // replace JSXElement with identifier {<MoreIcon />} => {MoreIcon}
37
- if (iconElement.openingElement.name.type === 'JSXIdentifier') {
57
+ if (iconElement.openingElement.name.type === 'JSXIdentifier' && ((_iconAttr$value2 = iconAttr.value) === null || _iconAttr$value2 === void 0 ? void 0 : _iconAttr$value2.type) === 'JSXExpressionContainer') {
38
58
  iconAttr.value.expression = j.identifier(iconElement.openingElement.name.name);
39
59
  }
40
- }
60
+ });
41
61
  };
42
62
  export var generateNewElement = function generateNewElement(variant, element, j) {
43
63
  var attributes = element.openingElement.attributes;
44
- var iconAttr = attributes && getIconAttribute(attributes);
64
+ var iconAttrs = attributes && getIconAttributes(attributes);
45
65
  var isIconOrLinkIcon = variant === NEW_BUTTON_VARIANTS.icon.as || variant === NEW_BUTTON_VARIANTS.linkIcon.as;
46
- if (isIconOrLinkIcon && iconAttr) {
66
+ if (isIconOrLinkIcon && iconAttrs !== null && iconAttrs !== void 0 && iconAttrs.length) {
47
67
  moveSizeAndLabelAttributes(element, j, true);
48
68
 
49
69
  // rename iconBefore/iconAfter to icon
50
- iconAttr.name.name = 'icon';
70
+ iconAttrs[0].name.name = 'icon';
51
71
  }
52
72
  return j.jsxElement(
53
73
  // self closing if it's an icon button or icon link button
@@ -0,0 +1,6 @@
1
+ import { unsupportedProps } from './constants';
2
+ export var ifHasUnsupportedProps = function ifHasUnsupportedProps(attributes) {
3
+ return Boolean(attributes && (attributes === null || attributes === void 0 ? void 0 : attributes.some(function (node) {
4
+ return node.type === 'JSXAttribute' && unsupportedProps.includes(String(node.name.name));
5
+ })));
6
+ };
@@ -0,0 +1,8 @@
1
+ import { NEW_BUTTON_ENTRY_POINT } from './constants';
2
+ export var checkIfVariantAlreadyImported = function checkIfVariantAlreadyImported(variant, fileSource, j) {
3
+ return fileSource.find(j.ImportDeclaration).filter(function (path) {
4
+ return path.node.source.value === NEW_BUTTON_ENTRY_POINT;
5
+ }).find(j.ImportSpecifier).filter(function (path) {
6
+ return path.node.imported.name === variant;
7
+ }).length > 0;
8
+ };
@@ -0,0 +1,35 @@
1
+ import { addCommentBefore } from '@atlaskit/codemod-utils';
2
+ import { getIconAttributes, getIconElement } from '../utils/generate-new-button-element';
3
+ import { NEW_BUTTON_VARIANTS, migrateFitContainerButtonToDefaultButtonComment, migrateFitContainerButtonToIconButtonComment } from './constants';
4
+ export var migrateFitContainerIconButton = function migrateFitContainerIconButton(element, j) {
5
+ var _iconElement$openingE, _labelAttribute$value;
6
+ var attributes = element.value.openingElement.attributes;
7
+ var iconAttrs = attributes && getIconAttributes(attributes);
8
+ var iconElement = iconAttrs && iconAttrs[0] && getIconElement(iconAttrs[0]);
9
+ var labelAttribute = iconElement && Array.isArray(iconElement.openingElement.attributes) && ((_iconElement$openingE = iconElement.openingElement) === null || _iconElement$openingE === void 0 ? void 0 : _iconElement$openingE.attributes.find(function (path) {
10
+ return path.type === 'JSXAttribute' && (path.name.name === 'label' || path.name.name === 'aria-label' || path.name.name === 'aria-labelledby');
11
+ }));
12
+ var migratedToIconButton;
13
+ if (labelAttribute && labelAttribute.type === 'JSXAttribute' && ((_labelAttribute$value = labelAttribute.value) === null || _labelAttribute$value === void 0 ? void 0 : _labelAttribute$value.type) === 'Literal' && typeof labelAttribute.value.value === 'string') {
14
+ migratedToIconButton = false;
15
+ var label = labelAttribute.value.value;
16
+ var formattedLabel = "".concat(label.charAt(0).toUpperCase()).concat(label.slice(1)).split('-').join(' ');
17
+ j(element).find(j.JSXAttribute).filter(function (path) {
18
+ return path.node.name.name === 'iconBefore' || path.node.name.name === 'iconAfter';
19
+ }).remove();
20
+ var newButton = j.jsxElement(j.jsxOpeningElement(j.jsxIdentifier(NEW_BUTTON_VARIANTS.default.as), attributes), j.jsxClosingElement(j.jsxIdentifier(NEW_BUTTON_VARIANTS.default.as)), [j.jsxText(formattedLabel)]);
21
+ j(element).replaceWith(newButton);
22
+ addCommentBefore(j, j(newButton).find(j.JSXAttribute).filter(function (path) {
23
+ return path.node.name.name === 'shouldFitContainer';
24
+ }), migrateFitContainerButtonToDefaultButtonComment, 'line');
25
+ } else {
26
+ migratedToIconButton = true;
27
+ addCommentBefore(j, j(element).find(j.JSXAttribute).filter(function (path) {
28
+ return path.node.name.name === 'iconBefore' || path.node.name.name === 'iconAfter';
29
+ }), migrateFitContainerButtonToIconButtonComment, 'line');
30
+ j(element).find(j.JSXAttribute).filter(function (path) {
31
+ return path.node.name.name === 'shouldFitContainer';
32
+ }).remove();
33
+ }
34
+ return migratedToIconButton;
35
+ };
@@ -0,0 +1,9 @@
1
+ import { entryPointsMapping } from '../utils/constants';
2
+ export var renameDefaultButtonToLegacyButtonImport = function renameDefaultButtonToLegacyButtonImport(oldButtonImport, oldButtonElements, j) {
3
+ oldButtonImport.insertBefore(j.importDeclaration([j.importDefaultSpecifier(j.identifier('LegacyButton'))], j.stringLiteral(entryPointsMapping.Button)));
4
+ oldButtonElements.forEach(function (element) {
5
+ var _element$value$childr, _element$value$childr2;
6
+ var legacyElement = j.jsxElement(j.jsxOpeningElement(j.jsxIdentifier('LegacyButton'), 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('LegacyButton')), element.value.children);
7
+ j(element).replaceWith(legacyElement);
8
+ });
9
+ };
@@ -13,3 +13,10 @@ export declare const entryPointsMapping: {
13
13
  };
14
14
  export declare const BUTTON_TYPES: string[];
15
15
  export declare const eslintDisableComment = "eslint-disable-next-line @atlaskit/design-system/no-banned-imports";
16
+ export declare const ButtonPropsTypeName = "ButtonProps";
17
+ export declare const unsupportedProps: string[];
18
+ 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.";
19
+ 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.";
20
+ export declare const buttonPropsNoLongerSupportedComment = "Buttons with \"component\", \"css\" or \"style\" prop can't be automatically migrated with codemods. Please migrate it manually.";
21
+ export declare const migrateFitContainerButtonToDefaultButtonComment = "Migrated to a default button with text which is from the icon label.";
22
+ export declare const migrateFitContainerButtonToIconButtonComment = "\"shouldFitContainer\" is not available in icon buttons, please consider using a default button with text.";
@@ -1,4 +1,6 @@
1
- import type { API, JSXElement } from 'jscodeshift';
1
+ import type { API, JSXElement, JSXAttribute, JSXSpreadAttribute } from 'jscodeshift';
2
2
  import { NEW_BUTTON_VARIANTS } from '../utils/constants';
3
+ export declare const getIconAttributes: (attributes: (JSXAttribute | JSXSpreadAttribute)[]) => JSXAttribute[] | null;
4
+ export declare const getIconElement: (iconAttr: JSXAttribute) => JSXElement | null;
3
5
  export declare const moveSizeAndLabelAttributes: (element: JSXElement, j: API['jscodeshift'], iconRenamed?: boolean) => void;
4
6
  export declare const generateNewElement: (variant: (typeof NEW_BUTTON_VARIANTS)[keyof typeof NEW_BUTTON_VARIANTS]['as'], element: JSXElement, j: API['jscodeshift']) => JSXElement;
@@ -0,0 +1,2 @@
1
+ import type { JSXAttribute, JSXSpreadAttribute } from 'jscodeshift';
2
+ export declare const ifHasUnsupportedProps: (attributes: (JSXAttribute | JSXSpreadAttribute)[] | undefined) => boolean;
@@ -0,0 +1,2 @@
1
+ import type { API, Collection } from 'jscodeshift';
2
+ export declare const checkIfVariantAlreadyImported: (variant: string, fileSource: Collection<any>, j: API['jscodeshift']) => boolean;
@@ -0,0 +1,3 @@
1
+ import type { API, ASTPath } from 'jscodeshift';
2
+ import { JSXElement } from 'jscodeshift';
3
+ export declare const migrateFitContainerIconButton: (element: ASTPath<JSXElement>, j: API['jscodeshift']) => boolean;
@@ -0,0 +1,2 @@
1
+ import type { API, Collection, ImportDeclaration, JSXElement } from 'jscodeshift';
2
+ export declare const renameDefaultButtonToLegacyButtonImport: (oldButtonImport: Collection<ImportDeclaration>, oldButtonElements: Collection<JSXElement>, j: API['jscodeshift']) => void;
@@ -13,3 +13,10 @@ export declare const entryPointsMapping: {
13
13
  };
14
14
  export declare const BUTTON_TYPES: string[];
15
15
  export declare const eslintDisableComment = "eslint-disable-next-line @atlaskit/design-system/no-banned-imports";
16
+ export declare const ButtonPropsTypeName = "ButtonProps";
17
+ export declare const unsupportedProps: string[];
18
+ 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.";
19
+ 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.";
20
+ export declare const buttonPropsNoLongerSupportedComment = "Buttons with \"component\", \"css\" or \"style\" prop can't be automatically migrated with codemods. Please migrate it manually.";
21
+ export declare const migrateFitContainerButtonToDefaultButtonComment = "Migrated to a default button with text which is from the icon label.";
22
+ export declare const migrateFitContainerButtonToIconButtonComment = "\"shouldFitContainer\" is not available in icon buttons, please consider using a default button with text.";
@@ -1,4 +1,6 @@
1
- import type { API, JSXElement } from 'jscodeshift';
1
+ import type { API, JSXElement, JSXAttribute, JSXSpreadAttribute } from 'jscodeshift';
2
2
  import { NEW_BUTTON_VARIANTS } from '../utils/constants';
3
+ export declare const getIconAttributes: (attributes: (JSXAttribute | JSXSpreadAttribute)[]) => JSXAttribute[] | null;
4
+ export declare const getIconElement: (iconAttr: JSXAttribute) => JSXElement | null;
3
5
  export declare const moveSizeAndLabelAttributes: (element: JSXElement, j: API['jscodeshift'], iconRenamed?: boolean) => void;
4
6
  export declare const generateNewElement: (variant: (typeof NEW_BUTTON_VARIANTS)[keyof typeof NEW_BUTTON_VARIANTS]['as'], element: JSXElement, j: API['jscodeshift']) => JSXElement;
@@ -0,0 +1,2 @@
1
+ import type { JSXAttribute, JSXSpreadAttribute } from 'jscodeshift';
2
+ export declare const ifHasUnsupportedProps: (attributes: (JSXAttribute | JSXSpreadAttribute)[] | undefined) => boolean;
@@ -0,0 +1,2 @@
1
+ import type { API, Collection } from 'jscodeshift';
2
+ export declare const checkIfVariantAlreadyImported: (variant: string, fileSource: Collection<any>, j: API['jscodeshift']) => boolean;
@@ -0,0 +1,3 @@
1
+ import type { API, ASTPath } from 'jscodeshift';
2
+ import { JSXElement } from 'jscodeshift';
3
+ export declare const migrateFitContainerIconButton: (element: ASTPath<JSXElement>, j: API['jscodeshift']) => boolean;
@@ -0,0 +1,2 @@
1
+ import type { API, Collection, ImportDeclaration, JSXElement } from 'jscodeshift';
2
+ export declare const renameDefaultButtonToLegacyButtonImport: (oldButtonImport: Collection<ImportDeclaration>, oldButtonElements: Collection<JSXElement>, j: API['jscodeshift']) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/codemod-cli",
3
- "version": "0.17.2",
3
+ "version": "0.17.4",
4
4
  "description": "A cli for distributing codemods for atlassian-frontend components and services",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"