@atlaskit/codemod-cli 0.21.1 → 0.23.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 +321 -125
- package/dist/cjs/main.js +1 -1
- package/dist/cjs/presets/migrate-to-new-buttons/codemods/next-migrate-to-new-button-variants.js +1 -1
- package/dist/cjs/presets/migrate-to-new-buttons/utils/constants.js +1 -2
- package/dist/cjs/presets/migrate-to-new-buttons/utils/generate-new-button-element.js +74 -38
- package/dist/es2019/presets/migrate-to-new-buttons/codemods/next-migrate-to-new-button-variants.js +2 -2
- package/dist/es2019/presets/migrate-to-new-buttons/utils/constants.js +0 -1
- package/dist/es2019/presets/migrate-to-new-buttons/utils/generate-new-button-element.js +69 -33
- package/dist/esm/main.js +1 -1
- package/dist/esm/presets/migrate-to-new-buttons/codemods/next-migrate-to-new-button-variants.js +2 -2
- package/dist/esm/presets/migrate-to-new-buttons/utils/constants.js +0 -1
- package/dist/esm/presets/migrate-to-new-buttons/utils/generate-new-button-element.js +74 -38
- package/dist/types/presets/migrate-to-new-buttons/utils/constants.d.ts +0 -1
- package/dist/types/presets/migrate-to-new-buttons/utils/generate-new-button-element.d.ts +14 -1
- package/dist/types-ts4.5/presets/migrate-to-new-buttons/utils/constants.d.ts +0 -1
- package/dist/types-ts4.5/presets/migrate-to-new-buttons/utils/generate-new-button-element.d.ts +14 -1
- package/package.json +4 -3
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.
|
|
308
|
+
_process$env$_PACKAGE = "0.23.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) {
|
package/dist/cjs/presets/migrate-to-new-buttons/codemods/next-migrate-to-new-button-variants.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
46
|
-
|
|
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 (
|
|
50
|
-
|
|
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
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
114
|
+
handleIconAttributes(element, j, true);
|
|
79
115
|
|
|
80
116
|
// rename iconBefore/iconAfter to icon
|
|
81
117
|
iconAttrs[0].name.name = 'icon';
|
package/dist/es2019/presets/migrate-to-new-buttons/codemods/next-migrate-to-new-button-variants.js
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
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.
|
|
301
|
+
_process$env$_PACKAGE = "0.23.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) {
|
package/dist/esm/presets/migrate-to-new-buttons/codemods/next-migrate-to-new-button-variants.js
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
40
|
-
|
|
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 (
|
|
44
|
-
|
|
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
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.";
|
package/dist/types-ts4.5/presets/migrate-to-new-buttons/utils/generate-new-button-element.d.ts
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
3
|
+
"version": "0.23.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/"
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"bin": "./bin/codemod-cli.js",
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@atlaskit/codemod-utils": "^4.2.0",
|
|
43
|
-
"@atlaskit/tokens": "^1.
|
|
43
|
+
"@atlaskit/tokens": "^1.45.0",
|
|
44
44
|
"@babel/runtime": "^7.0.0",
|
|
45
45
|
"@codeshift/utils": "^0.2.4",
|
|
46
46
|
"@hypermod/utils": "^0.4.2",
|
|
@@ -55,8 +55,9 @@
|
|
|
55
55
|
"simple-git": "^3.16.0"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
+
"@af/formatting": "*",
|
|
58
59
|
"@atlassian/atlassian-frontend-prettier-config-1.0.0": "npm:@atlassian/atlassian-frontend-prettier-config@1.0.0",
|
|
59
|
-
"prettier": "^2.
|
|
60
|
+
"prettier": "^3.2.5",
|
|
60
61
|
"ts-node": "^10.9.1",
|
|
61
62
|
"typescript": "~5.4.2"
|
|
62
63
|
},
|