diff --git a/package.json b/package.json index 8526d9fedfb..51b7ebeae55 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "@cypress/code-coverage": "^3.10.0", "@semantic-release/github": "^10.0.0", "@testing-library/cypress": "^10.0.0", + "@types/jscodeshift": "^0.11.11", "@types/node": "^20.0.0", "@types/react": "npm:types-react@rc", "@types/react-dom": "npm:types-react-dom@rc", diff --git a/packages/cli/src/scripts/codemod/transforms/v2/codemodConfig.json b/packages/cli/src/scripts/codemod/transforms/v2/codemodConfig.json new file mode 100644 index 00000000000..2c0f44b0f4b --- /dev/null +++ b/packages/cli/src/scripts/codemod/transforms/v2/codemodConfig.json @@ -0,0 +1,549 @@ +{ + "components": { + "DONOTUSE_TEMPLATE": { + "newComponent": "", + "changedProps": { + "oldName": "newName" + }, + "removedProps": ["propName"] + }, + "ActionSheet": { + "changedProps": { + "onAfterClose": "onClose", + "onAfterOpen": "onOpen", + "placementType": "placement" + } + }, + "Avatar": {}, + "Badge": { + "newComponent": "Tag" + }, + "Bar": {}, + "BarcodeScannerDialog": {}, + "Breadcrumbs": { + "changedProps": { + "separatorStyle": "separators" + } + }, + "BusyIndicator": { + "renamedEnums": { + "size": "BusyIndicatorSize" + } + }, + "Button": { + "comment": "new slot endIcon", + "removedProps": ["iconEnd"] + }, + "Calendar": { + "changedProps": { + "onSelectedDatesChange": "onSelectionChange" + } + }, + "CalendarDate": {}, + "CalendarLegend": {}, + "CalendarLegendItem": {}, + "Card": {}, + "CardHeader": { + "changedProps": { + "status": "additionalText" + } + }, + "Carousel": { + "comment": "combine s, m and l into a single string S M L", + "changedProps": { + "itemsPerPageS": "itemsPerPage", + "itemsPerPageM": "itemsPerPage", + "itemsPerPageL": "itemsPerPage", + "pageIndicatorStyle": "pageIndicatorType" + } + }, + "CheckBox": { + "renamedEnums": { + "valueState": "ValueState" + } + }, + "ColorPalette": {}, + "ColorPaletteItem": {}, + "ColorPalettePopover": {}, + "ColorPicker": { + "changedProps": { + "color": "value" + } + }, + "ComboBox": { + "renamedEnums": { + "valueState": "ValueState" + } + }, + "ComboBoxGroupItem": { + "newComponent": "ComboBoxItemGroup" + }, + "CustomListItem": { + "renamedEnums": { + "highlight": "HighlightTypes" + } + }, + "DatePicker": { + "renamedEnums": { + "valueState": "ValueState" + } + }, + "DateRangePicker": { + "renamedEnums": { + "valueState": "ValueState" + } + }, + "DateTimePicker": { + "renamedEnums": { + "valueState": "ValueState" + } + }, + "Dialog": { + "changedProps": { + "onAfterClose": "onClose", + "onAfterOpen": "onOpen" + }, + "renamedEnums": { + "state": "ValueState" + } + }, + "DynamicSideContent": {}, + "FileUploader": { + "renamedEnums": { + "valueState": "ValueState" + } + }, + "FilterItem": {}, + "FilterItemOption": {}, + "GroupHeaderListItem": { + "newComponent": "ListItemGroup" + }, + "Icon": { + "comment": "interactive=true becomes mode=IconMode.Interactive", + "changedProps": { + "interactive": "mode" + }, + "removedProps": ["accessibleRole"] + }, + "IllustratedMessage": { + "changedProps": { + "size": "design" + }, + "removedProps": ["titleLevel"] + }, + "Input": { + "changedProps": { + "onSuggestionItemPreview": "onSelectionChange" + }, + "removedProps": ["onSuggestionItemSelect"], + "renamedEnums": { + "valueState": "ValueState" + } + }, + "Label": {}, + "Link": { + "changedProps": { + "title": "tooltip" + } + }, + "List": { + "changedProps": { + "mode": "selectionMode", + "busy": "loading" + }, + "renamedEnums": { + "mode": "ListMode" + } + }, + "ListItemGroup": {}, + "MediaGallery": {}, + "MediaGalleryItem": {}, + "Menu": { + "changedProps": { + "busy": "loading", + "busyDelay": "loadingDelay", + "onAfterClose": "onClose", + "onAfterOpen": "onOpen" + } + }, + "MenuItem": { + "changedProps": { + "busy": "loading", + "busyDelay": "loadingDelay" + } + }, + "MessageItem": { + "renamedEnums": { + "type": "ValueState" + } + }, + "MessageViewButton": { + "renamedEnums": { + "type": "ValueState" + } + }, + "MessageStrip": { + "renamedEnums": { + "design": "MessageStripDesign" + } + }, + "MultiComboBox": { + "changedProps": { + "allowCustomValues": "noValidation" + }, + "renamedEnums": { + "valueState": "ValueState" + } + }, + "MultiComboBoxGroupItem": {}, + "MultiComboBoxItem": {}, + "MultiInput": { + "changedProps": { + "onSuggestionItemPreview": "onSelectionChange" + }, + "removedProps": ["onSuggestionItemSelect"], + "renamedEnums": { + "valueState": "ValueState" + } + }, + "NotificationAction": {}, + "NotificationListGroupItem": { + "changedProps": { + "busy": "loading", + "busyDelay": "loadingDelay" + }, + "removedProps": ["actions", "priority", "showClose", "showCounter"] + }, + "NotificationListItem": { + "changedProps": { + "busy": "loading", + "busyDelay": "loadingDelay", + "actions": "menu", + "priority": "importance" + } + }, + "ObjectStatus": { + "renamedEnums": { + "state": "ValueState" + } + }, + "Option": { + "changedProps": { + "title": "tooltip" + }, + "removedProps": ["disabled"] + }, + "Page": { + "comment": "logic for floatingFooter is inverted", + "changedProps": { + "disableScrolling": "noScrolling", + "floatingFooter": "fixedFooter" + }, + "removedProps": ["disabled"] + }, + "Panel": {}, + "Popover": { + "changedProps": { + "placementType": "placement", + "onAfterClose": "onClose", + "onAfterOpen": "onOpen" + }, + "removedProps": ["hideBackdrop"], + "renamedEnums": { + "placementType": "PopoverPlacementType", + "horizontalAlign": "PopoverHorizontalAlign" + } + }, + "ProductSwitch": {}, + "ProductSwitchItem": {}, + "ProgressIndicator": { + "removedProps": ["disabled"], + "renamedEnums": { + "valueState": "ValueState" + } + }, + "RadioButton": { + "renamedEnums": { + "valueState": "ValueState" + } + }, + "RangeSlider": {}, + "RatingIndicator": {}, + "ResponsivePopover": { + "changedProps": { + "placementType": "placement", + "onAfterClose": "onClose", + "onAfterOpen": "onOpen" + }, + "removedProps": ["hideBackdrop"], + "renamedEnums": { + "placementType": "PopoverPlacementType", + "horizontalAlign": "PopoverHorizontalAlign" + } + }, + "SegmentedButton": { + "changedProps": { + "mode": "selectionMode" + }, + "renamedEnums": { + "mode": "SegmentedButtonMode" + } + }, + "SegmentedButtonItem": { + "changedProps": { + "pressed": "selected" + } + }, + "Select": { + "removedProps": ["menu"], + "renamedEnums": { + "valueState": "ValueState" + } + }, + "SelectDialog": { + "changedProps": { + "mode": "selectionMode", + "onAfterClose": "onClose", + "onAfterOpen": "onOpen" + } + }, + "SelectMenu": {}, + "SelectMenuOption": {}, + "ShellBar": { + "removedProps": ["showCoPilot", "onCoPilotClick"] + }, + "ShellBarItem": {}, + "SideNavigation": {}, + "SideNavigationGroup": { + "changedProps": { + "title": "tooltip" + } + }, + "SideNavigationItem": { + "changedProps": { + "title": "tooltip" + } + }, + "SideNavigationSubItem": { + "changedProps": { + "title": "tooltip" + } + }, + "Slider": {}, + "SortItem": {}, + "SpecialCalendarDate": {}, + "SplitButton": { + "removedProps": ["activeIcon"] + }, + "StandardListItem": { + "renamedEnums": { + "highlight": "HighlightTypes", + "additionalTextState": "ValueState" + } + }, + "StepInput": { + "renamedEnums": { + "valueState": "ValueState" + } + }, + "SuggestionGroupItem": {}, + "SuggestionItem": { + "renamedEnums": { + "additionalTextState": "ValueState" + } + }, + "Switch": {}, + "Tab": { + "changedProps": { + "subTabs": "items" + } + }, + "TabContainer": { + "changedProps": { + "tabsOverflowMode": "overflowMode" + }, + "removedProps": ["fixed", "showOverflow"] + }, + "TabSeparator": {}, + "TextArea": { + "changedProps": { + "growingMaxLines": "growingMaxRows" + }, + "renamedEnums": { + "valueState": "ValueState" + } + }, + "TimePicker": { + "renamedEnums": { + "valueState": "ValueState" + } + }, + "Timeline": {}, + "TimelineItem": {}, + "Title": {}, + "Toast": { + "changedProps": { + "onAfterClose": "onClose" + } + }, + "ToggleButton": { + "removedProps": ["iconEnd"] + }, + "Token": { + "removedProps": ["disabled"] + }, + "Toolbar": {}, + "ToolbarButton": { + "removedProps": ["iconEnd"] + }, + "ToolbarSelect": { + "renamedEnums": { + "valueState": "ValueState" + } + }, + "ToolbarSelectOption": {}, + "ToolbarSeparator": {}, + "ToolbarSpacer": {}, + "Tree": { + "changedProps": { + "mode": "selectionMode" + }, + "renamedEnums": { + "mode": "ListMode" + } + }, + "TreeItem": { + "renamedEnums": { + "highlight": "HighlightTypes", + "additionalTextState": "ValueState" + } + }, + "TreeItemCustom": { + "renamedEnums": { + "highlight": "HighlightTypes", + "additionalTextState": "ValueState" + } + }, + "UploadCollection": { + "changedProps": { + "mode": "selectionMode" + }, + "renamedEnums": { + "highlight": "HighlightTypes", + "mode": "ListMode" + } + }, + "UploadCollectionItem": {}, + "ViewSettingsDialog": {}, + "Wizard": {}, + "WizardStep": {} + }, + "enums": { + "AvatarColorScheme": "@ui5/webcomponents/dist/types/AvatarColorScheme.js", + "AvatarGroupType": "@ui5/webcomponents/dist/types/AvatarGroupType.js", + "AvatarShape": "@ui5/webcomponents/dist/types/AvatarShape.js", + "AvatarSize": "@ui5/webcomponents/dist/types/AvatarSize.js", + "BadgeDesign": "@ui5/webcomponents/dist/types/TagDesign.js", + "BarDesign": "@ui5/webcomponents/dist/types/BarDesign.js", + "BackgroundDesign": "@ui5/webcomponents/dist/types/BackgroundDesign.js", + "BorderDesign": "@ui5/webcomponents/dist/types/BorderDesign.js", + "BreadcrumbsDesign": "@ui5/webcomponents/dist/types/BreadcrumbsDesign.js", + "BreadcrumbsSeparatorStyle": "@ui5/webcomponents/dist/types/BreadcrumbsSeparator.js", + "BusyIndicatorSize": "@ui5/webcomponents/dist/types/BusyIndicatorSize.js", + "BusyIndicatorTextPlacement": "@ui5/webcomponents/dist/types/BusyIndicatorTextPlacement.js", + "ButtonDesign": "@ui5/webcomponents/dist/types/ButtonDesign.js", + "ButtonType": "@ui5/webcomponents/dist/types/ButtonType.js", + "CalendarLegendItemType": "@ui5/webcomponents/dist/types/CalendarLegendItemType.js", + "CalendarSelection": "@ui5/webcomponents/dist/types/CalendarSelectionMode.js", + "CalendarSelectionMode": "@ui5/webcomponents/dist/types/CalendarSelectionMode.js", + "CalendarType": "@ui5/webcomponents-base/dist/types/CalendarType.js", + "CarouselPageIndicatorStyle": "@ui5/webcomponents/dist/types/CarouselPageIndicatorType.js", + "ComboBoxFilter": "@ui5/webcomponents/dist/types/ComboBoxFilter.js", + "FCLLayout": "@ui5/webcomponents-fiori/dist/types/FCLLayout.js", + "HighlightTypes": "@ui5/webcomponents/dist/types/Highlight.js", + "IconDesign": "@ui5/webcomponents/dist/types/IconDesign.js", + "IllustrationMessageSize": "@ui5/webcomponents-fiori/dist/types/IllustrationMessageSize.js", + "IllustrationMessageType": "@ui5/webcomponents-fiori/dist/types/IllustrationMessageType.js", + "InputType": "@ui5/webcomponents/dist/types/InputType.js", + "LinkDesign": "@ui5/webcomponents/dist/types/LinkDesign.js", + "ListItemType": "@ui5/webcomponents/dist/types/ListItemType.js", + "ListMode": "@ui5/webcomponents/dist/types/ListSelectionMode.js", + "ListGrowingMode": "@ui5/webcomponents/dist/types/ListGrowingMode.js", + "ListSeparators": "@ui5/webcomponents/dist/types/ListSeparators.js", + "MediaGalleryItemLayout": "@ui5/webcomponents-fiori/dist/types/MediaGalleryItemLayout.js", + "MediaGalleryLayout": "@ui5/webcomponents-fiori/dist/types/MediaGalleryLayout.js", + "MediaGalleryMenuHorizontalAlign": "@ui5/webcomponents-fiori/dist/types/MediaGalleryMenuHorizontalAlign.js", + "MediaGalleryMenuVerticalAlign": "@ui5/webcomponents-fiori/dist/types/MediaGalleryMenuVerticalAlign.js", + "MessageStripDesign": "@ui5/webcomponents/dist/types/MessageStripDesign.js", + "Priority": "@ui5/webcomponents/dist/types/Priority.js", + "PageBackgroundDesign": "@ui5/webcomponents-fiori/dist/types/PageBackgroundDesign.js", + "PanelAccessibleRole": "@ui5/webcomponents/dist/types/PanelAccessibleRole.js", + "PopoverHorizontalAlign": "@ui5/webcomponents/dist/types/PopoverHorizontalAlign.js", + "PopoverPlacementType": "@ui5/webcomponents/dist/types/PopoverPlacement.js", + "PopoverPlacement": "@ui5/webcomponents/dist/types/PopoverPlacement.js", + "PopoverVerticalAlign": "@ui5/webcomponents/dist/types/PopoverVerticalAlign.js", + "PopupAccessibleRole": "@ui5/webcomponents/dist/types/PopupAccessibleRole.js", + "SegmentedButtonMode": "@ui5/webcomponents/dist/types/SegmentedButtonSelectionMode.js", + "SemanticColor": "@ui5/webcomponents/dist/types/SemanticColor.js", + "SideContentFallDown": "@ui5/webcomponents-fiori/dist/types/SideContentFallDown.js", + "SideContentPosition": "@ui5/webcomponents-fiori/dist/types/SideContentPosition.js", + "SideContentVisibility": "@ui5/webcomponents-fiori/dist/types/SideContentVisibility.js", + "SwitchDesign": "@ui5/webcomponents/dist/types/SwitchDesign.js", + "TabContainerBackgroundDesign": "@ui5/webcomponents/dist/types/BackgroundDesign.js", + "TabLayout": "@ui5/webcomponents/dist/types/TabLayout.js", + "TabsOverflowMode": "@ui5/webcomponents/dist/types/OverflowMode.js", + "TableColumnPopinDisplay": "@ui5/webcomponents-compat/dist/types/TableColumnPopinDisplay.js", + "TableGrowingMode": "@ui5/webcomponents-compat/dist/types/TableGrowingMode.js", + "TableMode": "@ui5/webcomponents-compat/dist/types/TableMode.js", + "TableRowType": "@ui5/webcomponents-compat/dist/types/TableRowType.js", + "TimelineLayout": "@ui5/webcomponents-fiori/dist/types/TimelineLayout.js", + "TitleLevel": "@ui5/webcomponents/dist/types/TitleLevel.js", + "ToastPlacement": "@ui5/webcomponents/dist/types/ToastPlacement.js", + "ToolbarAlign": "@ui5/webcomponents/dist/types/ToolbarAlign.js", + "ToolbarItemOverflowBehavior": "@ui5/webcomponents/dist/types/ToolbarItemOverflowBehavior.js", + "UploadState": "@ui5/webcomponents-fiori/dist/types/UploadState.js", + "ValueState": "@ui5/webcomponents-base/dist/types/ValueState.js", + "WizardContentLayout": "@ui5/webcomponents-fiori/dist/types/WizardContentLayout.js", + "WrappingType": "@ui5/webcomponents/dist/types/WrappingType.js" + }, + "enumProperties": { + "BusyIndicatorSize": { + "Small": "S", + "Medium": "M", + "Large": "L" + }, + "ValueState": { + "Success": "Positive", + "Warning": "Critical", + "Error": "Negative" + }, + "HighlightTypes": { + "Success": "Positive", + "Warning": "Critical", + "Error": "Negative" + }, + "ListMode": { + "SingleSelectBegin": "SingleStart", + "SingleSelect": "Single", + "MultiSelect": "Multiple", + "SingleSelectEnd": "SingleEnd", + "SingleSelectAuto": "SingleAuto" + }, + "MessageStripDesign": { + "Warning": "Critical" + }, + "PopoverPlacementType": { + "Left": "Start", + "Right": "End" + }, + "PopoverHorizontalAlign": { + "Left": "Start", + "Right": "End" + }, + "SegmentedButtonMode": { + "SingleSelect": "Single", + "MultiSelect": "Multiple" + } + }, + "wrappingTypeComponents": ["Badge", "CheckBox", "Label", "Link", "Title", "RadioButton", "NotificationListItem"] +} diff --git a/packages/cli/src/scripts/codemod/transforms/v2/main.cts b/packages/cli/src/scripts/codemod/transforms/v2/main.cts new file mode 100644 index 00000000000..c804b4162f7 --- /dev/null +++ b/packages/cli/src/scripts/codemod/transforms/v2/main.cts @@ -0,0 +1,280 @@ +import type { API, Collection, FileInfo, JSCodeshift, Options } from 'jscodeshift'; + +const config = require('./codemodConfig.json'); + +interface ComponentTransformConfig { + newComponent?: string; + changedProps?: Record; + removedProps?: string[]; + renamedEnums?: Record; +} + +function componentIsImportedFromWebComponentsReact(j: JSCodeshift, root: Collection, componentName: string): boolean { + const imports = root.find(j.ImportDeclaration); + const importStatement = imports.find(j.ImportSpecifier, { local: { name: componentName } }); + + if (importStatement.length === 0) return false; + + const importedFrom = importStatement.get().parentPath.parentPath.value.source.value; + + return importedFrom.startsWith('@ui5/webcomponents-react'); +} + +function addWebComponentsReactImport(j: JSCodeshift, root: Collection, importName: string) { + const imports = root.find(j.ImportDeclaration); + const n = imports.length; + + const isAlreadyImported = componentIsImportedFromWebComponentsReact(j, root, importName); + + if (isAlreadyImported) { + return; + } + + const importStatement = j.importDeclaration( + [j.importSpecifier(j.identifier(importName), j.identifier(importName))], + j.literal('@ui5/webcomponents-react') + ); + + if (n) { + imports + .at(n - 1) + .get() + .insertAfter(importStatement); + } else { + root.get().node.program.body.unshift(importStatement); + } +} + +export default function transform(file: FileInfo, api: API, options?: Options): string | undefined { + const j = api.jscodeshift; + const root = j(file.source); + + let isDirty = false; + + // components + Object.entries(config.components).forEach(([componentName, changes]) => { + const jsxElements = root.findJSXElements(componentName); + + if (jsxElements.length === 0) { + return; + } + + const validMatch = componentIsImportedFromWebComponentsReact(j, root, componentName); + + if (!validMatch) { + return; + } + + // Special Handling for logic inversions, etc. + if (componentName === 'Button') { + jsxElements.forEach((el) => { + const icon = j(el).find(j.JSXAttribute, { name: { name: 'icon' } }); + const iconEnd = j(el).find(j.JSXAttribute, { name: { name: 'iconEnd' } }); + if (icon.size() > 0 && iconEnd.size() > 0) { + if (iconEnd.get().value.value === null || iconEnd.get().value.value.expression.value) { + icon.find(j.JSXIdentifier, { name: 'icon' }).replaceWith(j.jsxIdentifier('endIcon')); + } + iconEnd.remove(); + isDirty = true; + } + }); + } + + if (componentName === 'Carousel') { + jsxElements.forEach((el) => { + const itemsPerPageS = j(el).find(j.JSXAttribute, { name: { name: 'itemsPerPageS' } }); + const itemsPerPageM = j(el).find(j.JSXAttribute, { name: { name: 'itemsPerPageM' } }); + const itemsPerPageL = j(el).find(j.JSXAttribute, { name: { name: 'itemsPerPageL' } }); + + const sizeValues: string[] = []; + + if (itemsPerPageS.size()) { + const s = itemsPerPageS.get(); + const stringLiteral = itemsPerPageS.find(j.StringLiteral); + const numericLiteral = itemsPerPageS.find(j.NumericLiteral); + + if (stringLiteral.size() > 0) { + sizeValues.push(`S${stringLiteral.get().value.value}`); + } else if (numericLiteral.size() > 0) { + sizeValues.push(`S${numericLiteral.get().value.value}`); + } else { + console.warn(`Unable to read value for prop 'itemsPerPageS' (Carousel). Please check the code manually.`); + } + } + + if (itemsPerPageM.size()) { + const stringLiteral = itemsPerPageM.find(j.StringLiteral); + const numericLiteral = itemsPerPageM.find(j.NumericLiteral); + if (stringLiteral.size() > 0) { + sizeValues.push(`M${stringLiteral.get().value.value}`); + } else if (numericLiteral.size() > 0) { + sizeValues.push(`M${numericLiteral.get().value.value}`); + } else { + console.warn(`Unable to read value for prop 'itemsPerPageM' (Carousel). Please check the code manually.`); + } + } + + if (itemsPerPageL.size()) { + const stringLiteral = itemsPerPageL.find(j.StringLiteral); + const numericLiteral = itemsPerPageL.find(j.NumericLiteral); + if (stringLiteral.size() > 0) { + sizeValues.push(`L${stringLiteral.get().value.value}`); + } else if (numericLiteral.size() > 0) { + sizeValues.push(`L${numericLiteral.get().value.value}`); + } else { + console.warn(`Unable to read value for prop 'itemsPerPageL' (Carousel). Please check the code manually.`); + } + } + + if (sizeValues.length > 0) { + [itemsPerPageS, itemsPerPageM, itemsPerPageL].forEach((e) => e.remove()); + j(el) + .find(j.JSXOpeningElement) + .get() + .value.attributes.push( + j.jsxAttribute(j.jsxIdentifier('itemsPerPage'), j.stringLiteral(sizeValues.join(' '))) + ); + isDirty = true; + } + }); + } + + if (componentName === 'Icon') { + jsxElements.forEach((el) => { + const interactive = j(el).find(j.JSXAttribute, { name: { name: 'interactive' } }); + if (interactive.size() > 0) { + if (interactive.get().value.value === null || interactive.get().value.value.expression.value) { + j(el) + .find(j.JSXOpeningElement) + .get() + .value.attributes.push(j.jsxAttribute(j.jsxIdentifier('mode'), j.stringLiteral('Interactive'))); + } + interactive.remove(); + isDirty = true; + } + }); + } + + if (componentName === 'Page') { + jsxElements.forEach((el) => { + const floatingFooter = j(el).find(j.JSXAttribute, { name: { name: 'floatingFooter' } }); + if ( + floatingFooter.size() === 0 || + !(floatingFooter.get().value.value === null || floatingFooter.get().value.value.expression.value) + ) { + j(el) + .find(j.JSXOpeningElement) + .get() + .value.attributes.push(j.jsxAttribute(j.jsxIdentifier('fixedFooter'), null)); + } + floatingFooter.remove(); + isDirty = true; + }); + } + + // before renaming any values, replace hard coded enum values + Object.entries(changes.renamedEnums ?? {}).forEach(([propName, enumRef]) => { + jsxElements.forEach((el) => { + const prop = j(el).find(j.JSXAttribute, { name: { name: propName } }); + if (prop.size() > 0) { + const enumMapping = config.enumProperties[enumRef]; + Object.entries(enumMapping).forEach(([oldEnumValue, newEnumValue]) => { + const literalToReplace = prop.find(j.StringLiteral, { value: oldEnumValue }); + if (literalToReplace.size() > 0) { + literalToReplace.replaceWith(j.stringLiteral(newEnumValue)); + isDirty = true; + } + }); + } + }); + }); + + // change wrapping type + if (config.wrappingTypeComponents.includes(componentName)) { + jsxElements.forEach((el) => { + const wrappingType = j(el).find(j.JSXAttribute, { name: { name: 'wrappingType' } }); + if (wrappingType.size() === 0) { + j(el) + .find(j.JSXOpeningElement) + .get() + .value.attributes.push(j.jsxAttribute(j.jsxIdentifier('wrappingType'), j.stringLiteral('None'))); + isDirty = true; + } + }); + } + + if (typeof changes.newComponent === 'string') { + jsxElements.find(j.Identifier, { name: componentName }).replaceWith(j.jsxIdentifier(changes.newComponent)); + const importSpecifier = root.find(j.ImportSpecifier, { local: { name: componentName } }); + const importedFrom = importSpecifier.get().parentPath.parentPath.value.source.value; + if (importedFrom === '@ui5/webcomponents-react') { + importSpecifier.replaceWith( + j.importSpecifier(j.identifier(changes.newComponent), j.identifier(changes.newComponent)) + ); + } + isDirty = true; + } + + Object.entries(changes.changedProps ?? {}).forEach(([oldName, newName]) => { + const jsxAttributes = jsxElements.find(j.JSXAttribute, { name: { name: oldName } }); + if (!jsxAttributes.length) { + return; + } + jsxAttributes.find(j.JSXIdentifier, { name: oldName }).replaceWith(j.jsxIdentifier(newName)); + isDirty = true; + }); + + (changes.removedProps ?? []).forEach((propToRemove) => { + const jsxAttributes = jsxElements.find(j.JSXAttribute, { name: { name: propToRemove } }); + if (!jsxAttributes.length) { + return; + } + jsxAttributes.remove(); + isDirty = true; + }); + }); + + Object.entries(config.enums).forEach(([enumName, newImport]) => { + const currentImportSpecifier = root.find(j.ImportSpecifier, { local: { name: enumName } }); + if (currentImportSpecifier.paths().length) { + const importedFrom = currentImportSpecifier.get().parentPath.parentPath.value.source.value; + + if (importedFrom === '@ui5/webcomponents-react') { + currentImportSpecifier.remove(); + + const imports = root.find(j.ImportDeclaration); + const currentImportStatementsLength = imports.length; + + const newImportDeclaration = j.importDeclaration( + [j.importSpecifier(j.identifier(enumName), j.identifier(enumName))], + j.literal(newImport) + ); + + if (currentImportStatementsLength) { + imports.at(-1).get().insertAfter(newImportDeclaration); + } else { + root.get().node.program.body.unshift(newImportDeclaration); + } + isDirty = true; + } + } + }); + + Object.entries>(config.enumProperties).forEach(([changedEnum, changedValues]) => { + Object.entries(changedValues ?? {}).forEach(([oldValue, newValue]) => { + const enumValueToReplace = root + .find(j.MemberExpression, { + object: { name: changedEnum }, + property: { name: oldValue } + }) + .find(j.Identifier, { name: oldValue }); + + if (enumValueToReplace.length) { + enumValueToReplace.replaceWith(j.identifier(newValue)); + isDirty = true; + } + }); + }); + + return isDirty ? root.toSource() : undefined; +} diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index c70c38d5a71..d43f5335060 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -14,5 +14,5 @@ "types": ["node"], "skipLibCheck": true }, - "include": ["src"] + "include": ["src", "**/*.json"] } diff --git a/yarn.lock b/yarn.lock index c254b677027..684aff50938 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6906,6 +6906,16 @@ __metadata: languageName: node linkType: hard +"@types/jscodeshift@npm:^0.11.11": + version: 0.11.11 + resolution: "@types/jscodeshift@npm:0.11.11" + dependencies: + ast-types: "npm:^0.14.1" + recast: "npm:^0.20.3" + checksum: 10c0/b3d2be46d523ae679a2c986d7f98232aabaa761c960423105286bfd682fb57f9366f6afed1e1d6b35e4923b7e038c0aa539032d7e7fd430754683078032cd578 + languageName: node + linkType: hard + "@types/json-schema@npm:^7.0.12": version: 7.0.12 resolution: "@types/json-schema@npm:7.0.12" @@ -8859,6 +8869,15 @@ __metadata: languageName: node linkType: hard +"ast-types@npm:0.14.2, ast-types@npm:^0.14.1": + version: 0.14.2 + resolution: "ast-types@npm:0.14.2" + dependencies: + tslib: "npm:^2.0.1" + checksum: 10c0/5d66d89b6c07fe092087454b6042dbaf81f2882b176db93861e2b986aafe0bce49e1f1ff59aac775d451c1426ad1e967d250e9e3548f5166ea8a3475e66c169d + languageName: node + linkType: hard + "ast-types@npm:^0.16.1": version: 0.16.1 resolution: "ast-types@npm:0.16.1" @@ -21070,6 +21089,18 @@ __metadata: languageName: node linkType: hard +"recast@npm:^0.20.3": + version: 0.20.5 + resolution: "recast@npm:0.20.5" + dependencies: + ast-types: "npm:0.14.2" + esprima: "npm:~4.0.0" + source-map: "npm:~0.6.1" + tslib: "npm:^2.0.1" + checksum: 10c0/7810216ff36c7376eddd66d3ce6b2df421305fdc983f2122711837911712177d52d804419655e1f29d4bb93016c178cffe442af410bdcf726050ca19af6fed32 + languageName: node + linkType: hard + "recast@npm:^0.23.3": version: 0.23.4 resolution: "recast@npm:0.23.4" @@ -23715,6 +23746,7 @@ __metadata: "@storybook/react-vite": "npm:8.1.6" "@storybook/theming": "npm:8.1.6" "@testing-library/cypress": "npm:^10.0.0" + "@types/jscodeshift": "npm:^0.11.11" "@types/node": "npm:^20.0.0" "@types/react": "npm:types-react@rc" "@types/react-dom": "npm:types-react-dom@rc"