From 32f8b8d089056dfa7531a9ebebea15c3a56809b7 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Thu, 29 Aug 2024 09:33:22 -0500 Subject: [PATCH] feat: add icon codemods (#6958) --- .../__tests__/__snapshots__/icon.test.ts.snap | 45 + .../src/s1-to-s2/__tests__/icon.test.ts | 44 + .../src/s1-to-s2/src/codemods/codemod.ts | 53 + .../dev/codemods/src/s1-to-s2/src/iconMap.ts | 1159 +++++++++++++++++ 4 files changed, 1301 insertions(+) create mode 100644 packages/dev/codemods/src/s1-to-s2/src/iconMap.ts diff --git a/packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/icon.test.ts.snap b/packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/icon.test.ts.snap index 110f7181298..4d4f76ba7a7 100644 --- a/packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/icon.test.ts.snap +++ b/packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/icon.test.ts.snap @@ -1,5 +1,50 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Does not affect existing S2 icons 1`] = ` +"import Add from '@react-spectrum/s2/icons/Add'; + +;" +`; + +exports[`Leave comment if no matching S2 icon found 1`] = ` +"import AssetCheck from '@spectrum-icons/workflow/AssetCheck'; + +// TODO(S2-upgrade): A Spectrum 2 equivalent to 'AssetCheck' was not found. Please update this icon manually. +;" +`; + +exports[`Migrate S1 icon with different name to S2 1`] = ` +"import AlertTriangle from "@react-spectrum/s2/icons/AlertTriangle"; + +;" +`; + +exports[`Migrate S1 icon with different name to S2. Keep name if already taken in scope. 1`] = ` +"import Alert from "@react-spectrum/s2/icons/AlertTriangle"; +import AlertTriangle from 'elsewhere'; + +
+ + +
" +`; + +exports[`Migrate S1 icon with same name to S2 1`] = ` +"import Add from "@react-spectrum/s2/icons/Add"; + +;" +`; + +exports[`Migrate custom-named S1 icon to S2. Keep name as custom name. 1`] = ` +"import AlertIcon from "@react-spectrum/s2/icons/AlertTriangle"; +import Alert from 'elsewhere'; + +
+ + +
" +`; + exports[`Remove Icon and tells people to use compiler? import as svg? 1`] = ` "import {Icon} from '@adobe/react-spectrum'; diff --git a/packages/dev/codemods/src/s1-to-s2/__tests__/icon.test.ts b/packages/dev/codemods/src/s1-to-s2/__tests__/icon.test.ts index 7d0e4cd2645..1854d26d5cf 100644 --- a/packages/dev/codemods/src/s1-to-s2/__tests__/icon.test.ts +++ b/packages/dev/codemods/src/s1-to-s2/__tests__/icon.test.ts @@ -19,3 +19,47 @@ function CustomIcon(props) { ); } `); + +test('Migrate S1 icon with same name to S2', ` +import Add from '@spectrum-icons/workflow/Add'; + +; +`); + +test('Migrate S1 icon with different name to S2', ` +import Alert from '@spectrum-icons/workflow/Alert'; + +; +`); + +test('Migrate custom-named S1 icon to S2. Keep name as custom name.', ` +import AlertIcon from '@spectrum-icons/workflow/Alert'; +import Alert from 'elsewhere'; + +
+ + +
+`); + +test('Migrate S1 icon with different name to S2. Keep name if already taken in scope.', ` +import Alert from '@spectrum-icons/workflow/Alert'; +import AlertTriangle from 'elsewhere'; + +
+ + +
+`); + +test('Leave comment if no matching S2 icon found', ` +import AssetCheck from '@spectrum-icons/workflow/AssetCheck'; + +; +`); + +test('Does not affect existing S2 icons', ` +import Add from '@react-spectrum/s2/icons/Add'; + +; +`); diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts index d3ce5ed19e9..7f5761557ac 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts @@ -4,6 +4,7 @@ import {API, FileInfo} from 'jscodeshift'; import {changes as changesJSON} from './changes'; import {functionMap} from './transforms'; import {getComponents} from '../getComponents'; +import {iconMap} from '../iconMap'; import * as t from '@babel/types'; import {transformStyleProps} from './styleProps'; import traverse, {Binding, NodePath} from '@babel/traverse'; @@ -39,6 +40,7 @@ export default function transformer(file: FileInfo, api: API, options: Options) let importedComponents = new Map(); let elements: [string, NodePath][] = []; let lastImportPath: NodePath | null = null; + let iconImports: Map, newName: string | null}> = new Map(); const leadingComments = root.find(j.Program).get('body', 0).node.leadingComments; traverse(root.paths()[0].node, { ImportDeclaration(path) { @@ -90,6 +92,22 @@ export default function transformer(file: FileInfo, api: API, options: Options) } } } + } else if (path.node.source.value.startsWith('@spectrum-icons/workflow/')) { + let importSource = path.node.source.value; + let iconName = importSource.split('/').pop(); + if (!iconName) {return;} + + let specifier = path.node.specifiers[0]; + if (!specifier || !t.isImportDefaultSpecifier(specifier)) {return;} + + let localName = specifier.local.name; + + if (iconMap.has(iconName)) { + let newIconName = iconMap.get(iconName)!; + iconImports.set(localName, {path, newName: newIconName}); + } else { + iconImports.set(localName, {path, newName: null}); + } } }, Import(path) { @@ -109,6 +127,41 @@ export default function transformer(file: FileInfo, api: API, options: Options) // TODO: implement this. could be a bit challenging. punting for now. addComment(call.node, ' TODO(S2-upgrade): check this dynamic import'); + }, + JSXOpeningElement(path) { + let name = path.node.name; + if (t.isJSXIdentifier(name) && iconImports.has(name.name)) { + let iconInfo = iconImports.get(name.name)!; + if (iconInfo.newName === null) { + addComment(path.node, ` TODO(S2-upgrade): A Spectrum 2 equivalent to '${name.name}' was not found. Please update this icon manually.`); + } + } + } + }); + + iconImports.forEach((iconInfo, localName) => { + let {path, newName} = iconInfo; + if (newName) { + let newImportSource = `@react-spectrum/s2/icons/${newName}`; + + // Check if we can update local name + let newLocalName = localName; + if (localName === path.node.source.value.split('/').pop() && localName !== newName) { + let binding = path.scope.getBinding(localName); + if (binding && !path.scope.hasBinding(newName)) { + newLocalName = newName; + // Rename all references + binding.referencePaths.forEach(refPath => { + if (t.isJSXIdentifier(refPath.node)) { + refPath.node.name = newName; + } + }); + } + } + + // Update the import + path.node.source = t.stringLiteral(newImportSource); + path.node.specifiers = [t.importDefaultSpecifier(t.identifier(newLocalName))]; } }); diff --git a/packages/dev/codemods/src/s1-to-s2/src/iconMap.ts b/packages/dev/codemods/src/s1-to-s2/src/iconMap.ts new file mode 100644 index 00000000000..adcf5824eb2 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/iconMap.ts @@ -0,0 +1,1159 @@ +// Mappings between Spectrum 1 and Spectrum 2 icons +export const iconMap = new Map([ + [ + '123', + 'TextNumbers' + ], + [ + '3DMaterials', + '3DMaterial' + ], + [ + 'Add', + 'Add' + ], + [ + 'AddCircle', + 'AddCircle' + ], + [ + 'Alert', + 'AlertTriangle' + ], + [ + 'AlertCircle', + 'AlertDiamond' + ], + [ + 'AlertCircleFilled', + 'AlertDiamond' + ], + [ + 'AlignBottom', + 'AlignBottom' + ], + [ + 'AlignCenter', + 'AlignCenter' + ], + [ + 'AlignLeft', + 'AlignLeft' + ], + [ + 'AlignMiddle', + 'AlignMiddle' + ], + [ + 'AlignRight', + 'AlignRight' + ], + [ + 'AlignTop', + 'AlignTop' + ], + [ + 'AnchorSelect', + 'DirectSelect' + ], + [ + 'Apps', + 'AppsAll' + ], + [ + 'Archive', + 'Archive' + ], + [ + 'Artboard', + 'Artboard' + ], + [ + 'Asset', + 'Asset' + ], + [ + 'AssetsAdded', + 'AddContent' + ], + [ + 'At', + 'Mention' + ], + [ + 'Attach', + 'Attach' + ], + [ + 'Audio', + 'MusicNote' + ], + [ + 'Beaker', + 'BetaApp' + ], + [ + 'Bell', + 'Bell' + ], + [ + 'Blur', + 'Blur' + ], + [ + 'Boolean', + 'Toggle' + ], + [ + 'Border', + 'EffectBorder' + ], + [ + 'Briefcase', + 'Briefcase' + ], + [ + 'Browse', + 'Binoculars' + ], + [ + 'Brush', + 'Brush' + ], + [ + 'Bug', + 'Bug' + ], + [ + 'Calendar', + 'Calendar' + ], + [ + 'CalendarAdd', + 'CalendarAdd' + ], + [ + 'Camera', + 'Camera' + ], + [ + 'Cancel', + 'Cancel' + ], + [ + 'Capitals', + 'TextCapsAll' + ], + [ + 'CCLibrary', + 'CCLibrary' + ], + [ + 'Channel', + 'Channel' + ], + [ + 'ChatAdd', + 'Channel' + ], + [ + 'Checkmark', + 'CheckmarkSize300' + ], + [ + 'CheckmarkCircle', + 'CheckmarkCircle' + ], + [ + 'CheckmarkCircleOutline', + 'CheckmarkCircle' + ], + [ + 'ChevronDown', + 'ChevronDownSize300' + ], + [ + 'ChevronLeft', + 'ChevronLeft' + ], + [ + 'ChevronRight', + 'ChevronRight' + ], + [ + 'Circle', + 'Circle' + ], + [ + 'CircleFilled', + 'Circle' + ], + [ + 'ClassicGridView', + 'ViewGrid' + ], + [ + 'Clock', + 'Clock' + ], + [ + 'CloneStamp', + 'StampClone' + ], + [ + 'Close', + 'Close' + ], + [ + 'CloseCaptions', + 'CloseCaptions' + ], + [ + 'CloseCircle', + 'CloseCircle' + ], + [ + 'Cloud', + 'Cloud' + ], + [ + 'CloudOutline', + 'Cloud' + ], + [ + 'Collection', + 'Collection' + ], + [ + 'ColorFill', + 'ColorFill' + ], + [ + 'ColorPalette', + 'Color' + ], + [ + 'Comment', + 'Comment' + ], + [ + 'Compass', + 'Discover' + ], + [ + 'Contrast', + 'Contrast' + ], + [ + 'Copy', + 'Copy' + ], + [ + 'Crop', + 'Crop' + ], + [ + 'CropRotate', + 'CropRotate' + ], + [ + 'Cut', + 'Cut' + ], + [ + 'Data', + 'Data' + ], + [ + 'DataAdd', + 'DataAdd' + ], + [ + 'DataSettings', + 'DataSettings' + ], + [ + 'DataUpload', + 'DataUpload' + ], + [ + 'Date', + 'CalendarDay' + ], + [ + 'Delete', + 'Delete' + ], + [ + 'DeleteOutline', + 'Delete' + ], + [ + 'Deselect', + 'SelectNo' + ], + [ + 'DesktopAndMobile', + 'DeviceDesktopMobile' + ], + [ + 'DeviceDesktop', + 'DeviceDesktop' + ], + [ + 'DeviceLaptop', + 'DeviceLaptop' + ], + [ + 'DevicePhone', + 'DevicePhone' + ], + [ + 'Devices', + 'DeviceMobile' + ], + [ + 'DeviceTablet', + 'DeviceTablet' + ], + [ + 'DistributeBottomEdge', + 'DistributeBottomEdge' + ], + [ + 'DistributeHorizontalCenter', + 'DistributeHorizontalCenter' + ], + [ + 'DistributeHorizontally', + 'DistributeSpaceHorizontally' + ], + [ + 'DistributeLeftEdge', + 'DistributeLeftEdge' + ], + [ + 'DistributeRightEdge', + 'DistributeRightEdge' + ], + [ + 'DistributeSpaceHoriz', + 'DistributeSpaceHorizontally' + ], + [ + 'DistributeSpaceVert', + 'DistributeSpaceVertically' + ], + [ + 'DistributeTopEdge', + 'DistributeTopEdge' + ], + [ + 'DistributeVerticalCenter', + 'DistributeVerticalCenter' + ], + [ + 'DistributeVertically', + 'DistributeSpaceVertically' + ], + [ + 'Document', + 'File' + ], + [ + 'DocumentOutline', + 'File' + ], + [ + 'Download', + 'Download' + ], + [ + 'Draw', + 'Draw' + ], + [ + 'Duplicate', + 'Duplicate' + ], + [ + 'Edit', + 'Edit' + ], + [ + 'Education', + 'Education' + ], + [ + 'Effects', + 'Effects' + ], + [ + 'Email', + 'Email' + ], + [ + 'EmailOutline', + 'Email' + ], + [ + 'Engagement', + 'Interaction' + ], + [ + 'Erase', + 'Erase' + ], + [ + 'Events', + 'CursorClick' + ], + [ + 'Export', + 'ExportTo' + ], + [ + 'ExportOriginal', + 'ExportTo' + ], + [ + 'Exposure', + 'Exposure' + ], + [ + 'Feedback', + 'Feedback' + ], + [ + 'FileAdd', + 'FileAdd' + ], + [ + 'FileTxt', + 'FileText' + ], + [ + 'FileUser', + 'FileUser' + ], + [ + 'Filter', + 'Filter' + ], + [ + 'FindAndReplace', + 'FindAndReplace' + ], + [ + 'Flag', + 'Flag' + ], + [ + 'FlipHorizontal', + 'FlipHorizontal' + ], + [ + 'FlipVertical', + 'FlipVertical' + ], + [ + 'Folder', + 'Folder' + ], + [ + 'FolderAdd', + 'FolderAdd' + ], + [ + 'FolderOpen', + 'FolderOpen' + ], + [ + 'FolderOpenOutline', + 'FolderOpen' + ], + [ + 'FolderOutline', + 'Folder' + ], + [ + 'FullScreen', + 'FullScreen' + ], + [ + 'FullScreenExit', + 'FullScreenExit' + ], + [ + 'Gift', + 'Gift' + ], + [ + 'Globe', + 'GlobeGrid' + ], + [ + 'GlobeGrid', + 'GlobeGrid' + ], + [ + 'Gradient', + 'Gradient' + ], + [ + 'Group', + 'Group' + ], + [ + 'Hand', + 'Hand' + ], + [ + 'Heart', + 'Heart' + ], + [ + 'Help', + 'HelpCircle' + ], + [ + 'HelpOutline', + 'HelpCircle' + ], + [ + 'History', + 'History' + ], + [ + 'Home', + 'Home' + ], + [ + 'Homepage', + 'WebPage' + ], + [ + 'Image', + 'Image' + ], + [ + 'ImageAdd', + 'ImageAdd' + ], + [ + 'ImageProfile', + 'UserAvatar' + ], + [ + 'Images', + 'Images' + ], + [ + 'Info', + 'InfoCircle' + ], + [ + 'InfoOutline', + 'InfoCircle' + ], + [ + 'InvertAdj', + 'Invert' + ], + [ + 'Invite', + 'Invite' + ], + [ + 'Keyboard', + 'Keyboard' + ], + [ + 'Label', + 'Tag' + ], + [ + 'Landscape', + 'OrientationLandscape' + ], + [ + 'Layers', + 'Layers' + ], + [ + 'Light', + 'Lighten' + ], + [ + 'Line', + 'Line' + ], + [ + 'LinearGradient', + 'GradientHorizontal' + ], + [ + 'LineHeight', + 'LineHeight' + ], + [ + 'Link', + 'Link' + ], + [ + 'LinkOff', + 'UnLink' + ], + [ + 'LinkOut', + 'ExportTo' + ], + [ + 'LinkOutLight', + 'ExportTo' + ], + [ + 'Location', + 'Location' + ], + [ + 'LockClosed', + 'Lock' + ], + [ + 'LockOpen', + 'LockOpen' + ], + [ + 'Looks', + 'Filters' + ], + [ + 'MagicWand', + 'MagicWand' + ], + [ + 'Magnify', + 'Search' + ], + [ + 'Maximize', + 'Maximize' + ], + [ + 'Measure', + 'Ruler' + ], + [ + 'Minimize', + 'Minimize' + ], + [ + 'More', + 'More' + ], + [ + 'MoreSmall', + 'More' + ], + [ + 'Move', + 'Move' + ], + [ + 'MoveTo', + 'ExportTo' + ], + [ + 'MovieCamera', + 'MovieCamera' + ], + [ + 'NamingOrder', + 'NamingOrder' + ], + [ + 'NewItem', + 'New' + ], + [ + 'NoEdit', + 'EditNo' + ], + [ + 'OpenIn', + 'OpenIn' + ], + [ + 'OpenInLight', + 'OpenIn' + ], + [ + 'Organisations', + 'Buildings' + ], + [ + 'Pan', + 'Hand' + ], + [ + 'Paste', + 'Paste' + ], + [ + 'Pattern', + 'Pattern' + ], + [ + 'Pause', + 'Pause' + ], + [ + 'Pending', + 'ClockPending' + ], + [ + 'Play', + 'Play' + ], + [ + 'Polygon', + 'Polygon6' + ], + [ + 'Portrait', + 'OrientationPortrait' + ], + [ + 'Preview', + 'Preview' + ], + [ + 'Print', + 'Print' + ], + [ + 'PrintPreview', + 'Preview' + ], + [ + 'Project', + 'Project' + ], + [ + 'ProjectAdd', + 'ProjectCreate' + ], + [ + 'Promote', + 'Promote' + ], + [ + 'Properties', + 'Properties' + ], + [ + 'PublishRemove', + 'PublishNo' + ], + [ + 'RadialGradient', + 'GradientRadial' + ], + [ + 'Rail', + 'MenuHamburger' + ], + [ + 'RealTimeCustomerProfile', + 'UserAvatar' + ], + [ + 'Rectangle', + 'RectangleHoriz' + ], + [ + 'RectSelect', + 'SelectRectangle' + ], + [ + 'Redo', + 'Redo' + ], + [ + 'Refresh', + 'Refresh' + ], + [ + 'RegionSelect', + 'LassoSelect' + ], + [ + 'RemoveCircle', + 'RemoveCircle' + ], + [ + 'Rename', + 'Rename' + ], + [ + 'Resize', + 'Resize' + ], + [ + 'Revert', + 'Revert' + ], + [ + 'Ribbon', + 'Ribbon' + ], + [ + 'RotateCCW', + 'RotateCCW' + ], + [ + 'RotateCCWBold', + 'RotateCCW' + ], + [ + 'RotateCW', + 'RotateCW' + ], + [ + 'RotateCWBold', + 'RotateCW' + ], + [ + 'RotateRight', + 'RotateOrientation' + ], + [ + 'RotateRightOutline', + 'RotateOrientation' + ], + [ + 'Sampler', + 'Eyedropper' + ], + [ + 'SaveTo', + 'Download' + ], + [ + 'SaveToLight', + 'Download' + ], + [ + 'Search', + 'Search' + ], + [ + 'Select', + 'Select' + ], + [ + 'SelectBox', + 'CheckBox' + ], + [ + 'SelectBoxAll', + 'SelectMulti' + ], + [ + 'Send', + 'Send' + ], + [ + 'Settings', + 'Settings' + ], + [ + 'Shapes', + 'Shapes' + ], + [ + 'Share', + 'Share' + ], + [ + 'ShareAndroid', + 'ShareAndroid' + ], + [ + 'ShareLight', + 'Share' + ], + [ + 'Shop', + 'Market' + ], + [ + 'ShoppingCart', + 'ShoppingCart' + ], + [ + 'ShowMenu', + 'MenuHamburger' + ], + [ + 'Shuffle', + 'Shuffle' + ], + [ + 'SmallCaps', + 'TextCapsSmall' + ], + [ + 'SocialNetwork', + 'SocialNetwork' + ], + [ + 'SortOrderDown', + 'SortDown' + ], + [ + 'SortOrderUp', + 'SortUp' + ], + [ + 'Star', + 'StarFilled' + ], + [ + 'StarOutline', + 'Star' + ], + [ + 'StepBackward', + 'StepBackward' + ], + [ + 'StepForward', + 'StepForward' + ], + [ + 'StrokeWidth', + 'StrokeWidth' + ], + [ + 'Switch', + 'Switch' + ], + [ + 'Table', + 'Table' + ], + [ + 'Target', + 'Target' + ], + [ + 'TaskList', + 'ListMultiSelect' + ], + [ + 'Temperature', + 'Temperature' + ], + [ + 'Text', + 'Text' + ], + [ + 'TextAdd', + 'TextAdd' + ], + [ + 'TextAlignCenter', + 'TextAlignCenter' + ], + [ + 'TextAlignJustify', + 'TextAlignJustify' + ], + [ + 'TextAlignLeft', + 'TextAlignLeft' + ], + [ + 'TextAlignRight', + 'TextAlignRight' + ], + [ + 'TextBold', + 'TextBold' + ], + [ + 'TextBulleted', + 'ListBulleted' + ], + [ + 'TextItalic', + 'TextItalic' + ], + [ + 'TextNumbered', + 'ListNumbered' + ], + [ + 'TextParagraph', + 'TextParagraph' + ], + [ + 'TextSize', + 'TextSize' + ], + [ + 'TextStrikethrough', + 'TextStrikeThrough' + ], + [ + 'TextSubscript', + 'TextSubscript' + ], + [ + 'TextSuperscript', + 'TextSuperscript' + ], + [ + 'TextUnderline', + 'TextUnderline' + ], + [ + 'ThumbDown', + 'ThumbDown' + ], + [ + 'ThumbDownOutline', + 'ThumbDown' + ], + [ + 'ThumbUp', + 'ThumbUp' + ], + [ + 'ThumbUpOutline', + 'ThumbUp' + ], + [ + 'Tips', + 'Lightbulb' + ], + [ + 'Transparency', + 'ViewTransparency' + ], + [ + 'Underline', + 'TextUnderline' + ], + [ + 'Undo', + 'Undo' + ], + [ + 'Ungroup', + 'GroupNo' + ], + [ + 'Unlink', + 'UnLink' + ], + [ + 'UploadToCloud', + 'UploadToCloud' + ], + [ + 'UploadToCloudOutline', + 'UploadToCloud' + ], + [ + 'User', + 'User' + ], + [ + 'UserAdd', + 'UserAdd' + ], + [ + 'UserEdit', + 'UserEdit' + ], + [ + 'UserGroup', + 'UserGroup' + ], + [ + 'UserLock', + 'UserLock' + ], + [ + 'UsersLock', + 'UsersLock' + ], + [ + 'VectorDraw', + 'VectorDraw' + ], + [ + 'VideoFilled', + 'Filmstrip' + ], + [ + 'VideoOutline', + 'Filmstrip' + ], + [ + 'ViewDay', + 'CalendarDay' + ], + [ + 'ViewGrid', + 'ViewGrid' + ], + [ + 'ViewList', + 'ViewList' + ], + [ + 'ViewWeek', + 'CalendarWeek' + ], + [ + 'Visibility', + 'Visibility' + ], + [ + 'VisibilityOff', + 'VisibilityOff' + ], + [ + 'VoiceOver', + 'Microphone' + ], + [ + 'VolumeMute', + 'VolumeOff' + ], + [ + 'VolumeOne', + 'VolumeOne' + ], + [ + 'VolumeTwo', + 'VolumeTwo' + ], + [ + 'WebPage', + 'WebPage' + ], + [ + 'ZoomIn', + 'ZoomIn' + ], + [ + 'ZoomOut', + 'ZoomOut' + ] +]);