From f279b63f9fb9940f175c62f4a607ca39f3471cbc Mon Sep 17 00:00:00 2001 From: aveline Date: Fri, 25 Aug 2023 10:14:12 -0700 Subject: [PATCH 01/22] Add remove prop util --- polaris-migrator/src/utilities/jsx.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/polaris-migrator/src/utilities/jsx.ts b/polaris-migrator/src/utilities/jsx.ts index a46c9e608c0..ba32c67f8a4 100644 --- a/polaris-migrator/src/utilities/jsx.ts +++ b/polaris-migrator/src/utilities/jsx.ts @@ -175,6 +175,21 @@ export function renameProps( } } +export function removeProp( + j: core.JSCodeshift, + source: Collection, + componentName: string, + prop: string, +) { + source + .findJSXElements(componentName) + .find(j.JSXAttribute) + .filter((path) => path.node.name.name === prop) + .remove(); + + return source; +} + export function insertJSXComment( j: core.JSCodeshift, element: ASTPath, From 9df6b7b76136e031176491912ccd8bc37c83a055 Mon Sep 17 00:00:00 2001 From: aveline Date: Fri, 25 Aug 2023 10:14:25 -0700 Subject: [PATCH 02/22] scaffold out button migration --- .../tests/transform.test.ts | 20 ++++++++++++ ...2-react-update-button-components.input.tsx | 21 ++++++++++++ ...-react-update-button-components.output.tsx | 19 +++++++++++ .../transform.ts | 32 +++++++++++++++++++ 4 files changed, 92 insertions(+) create mode 100644 polaris-migrator/src/migrations/v12-react-update-button-components/tests/transform.test.ts create mode 100644 polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.input.tsx create mode 100644 polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx create mode 100644 polaris-migrator/src/migrations/v12-react-update-button-components/transform.ts diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/transform.test.ts b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/transform.test.ts new file mode 100644 index 00000000000..9f8c8682b53 --- /dev/null +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/transform.test.ts @@ -0,0 +1,20 @@ +import {check} from '../../../utilities/check'; + +const transform = 'v12-react-update-button-components'; +const fixtures = [ + { + name: 'v12-react-update-button-components', + options: { + componentName: 'Button', + removeProp: 'outline', + }, + }, +]; + +for (const fixture of fixtures) { + check(__dirname, { + fixture: fixture.name, + transform, + options: fixture.options, + }); +} diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.input.tsx b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.input.tsx new file mode 100644 index 00000000000..9c1b52195c8 --- /dev/null +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.input.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import {Button} from '@shopify/polaris'; +import {PhoneMajor} from '@shopify/polaris-icons'; + +const MyButton = Button; + +export function App() { + return ( + <> + + hello + + + + + ); +} diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx new file mode 100644 index 00000000000..94eb3f660fe --- /dev/null +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import {Button} from '@shopify/polaris'; +import {PhoneMajor} from '@shopify/polaris-icons'; + +const MyButton = Button; + +export function App() { + return ( + <> + hello + + + + ); +} diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/transform.ts b/polaris-migrator/src/migrations/v12-react-update-button-components/transform.ts new file mode 100644 index 00000000000..ae1a0be3a00 --- /dev/null +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/transform.ts @@ -0,0 +1,32 @@ +import type {API, FileInfo, Options} from 'jscodeshift'; + +import {removeProp} from '../../utilities/jsx'; + +export interface MigrationOptions extends Options { + removeProp: string; +} + +export default function transformer( + file: FileInfo, + {jscodeshift: j}: API, + options: MigrationOptions, +) { + const componentParts = options.componentName?.split('.'); + if ( + !options.componentName || + !options.removeProp || + componentParts?.length > 2 + ) { + throw new Error( + 'Missing required options: componentName, from, to, or your compound component exceeds 2 levels', + ); + } + + const source = j(file.source); + const componentName = options.componentName; + const prop = options.removeProp; + + removeProp(j, source, componentName, prop); + + return source.toSource(); +} From 444cf5c2fee7a9eb365dca20f0b032a2b596c717 Mon Sep 17 00:00:00 2001 From: aveline Date: Fri, 25 Aug 2023 14:01:04 -0700 Subject: [PATCH 03/22] Button migration remove prop --- ...2-react-update-button-components.input.tsx | 7 +----- ...-react-update-button-components.output.tsx | 5 +--- .../transform.ts | 23 ++++--------------- 3 files changed, 6 insertions(+), 29 deletions(-) diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.input.tsx b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.input.tsx index 9c1b52195c8..c5d5d56d448 100644 --- a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.input.tsx +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.input.tsx @@ -2,15 +2,10 @@ import React from 'react'; import {Button} from '@shopify/polaris'; import {PhoneMajor} from '@shopify/polaris-icons'; -const MyButton = Button; - export function App() { return ( <> - - hello - - + + ); } diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx index 475eae6e9fb..ba45017e676 100644 --- a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx @@ -11,6 +11,10 @@ export function App() { + + ); } diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-primary-plain-components.input.tsx b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-primary-plain-components.input.tsx new file mode 100644 index 00000000000..5c7e0528e62 --- /dev/null +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-primary-plain-components.input.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import {Button} from '@shopify/polaris'; +import {PhoneMajor} from '@shopify/polaris-icons'; + +export function App() { + const isPolarisUplift = true; + return ( + <> + + + + + ); +} diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-primary-plain-components.output.tsx b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-primary-plain-components.output.tsx new file mode 100644 index 00000000000..418f7d74bd1 --- /dev/null +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-primary-plain-components.output.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import {Button} from '@shopify/polaris'; +import {PhoneMajor} from '@shopify/polaris-icons'; + +export function App() { + const isPolarisUplift = true; + return ( + <> + + {/* polaris-migrator: Unable to migrate the following expression. Please upgrade manually. */} + + {/* polaris-migrator: Unable to migrate the following expression. Please upgrade manually. */} + + + ); +} diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/transform.ts b/polaris-migrator/src/migrations/v12-react-update-button-components/transform.ts index 721bf326399..932e1c94f06 100644 --- a/polaris-migrator/src/migrations/v12-react-update-button-components/transform.ts +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/transform.ts @@ -1,15 +1,61 @@ -import type {API, FileInfo, Options} from 'jscodeshift'; +import type { + API, + FileInfo, + JSXAttribute, + JSXOpeningElement, + Options, +} from 'jscodeshift'; -import {removeProp} from '../../utilities/jsx'; +import {POLARIS_MIGRATOR_COMMENT} from '../../utilities/constants'; +import {hasImportDeclaration} from '../../utilities/imports'; +import { + insertJSXComment, + removeProp, + renameProps, + insertJSXAttribute, + removeJSXAttributes, +} from '../../utilities/jsx'; export default function transformer( - file: FileInfo, + fileInfo: FileInfo, {jscodeshift: j}: API, _: Options, ) { - const source = j(file.source); + const source = j(fileInfo.source); const componentName = 'Button'; + if (!hasImportDeclaration(j, source, '@shopify/polaris')) { + return fileInfo.source; + } + + // Rename `primary` `plain` to `variant=`tertiary` + source.findJSXElements(componentName).forEach((element) => { + const allAttributes = + (j(element).find(j.JSXOpeningElement).get().value as JSXOpeningElement) + .attributes ?? []; + const jsxAttributes = allAttributes as JSXAttribute[]; + const primaryAttribute = jsxAttributes.find( + (attribute) => attribute.name.name === 'primary', + ); + const plainAttribute = jsxAttributes.find( + (attribute) => attribute.name.name === 'plain', + ); + + if (primaryAttribute && plainAttribute) { + if ( + primaryAttribute.value?.type === 'JSXExpressionContainer' || + plainAttribute.value?.type === 'JSXExpressionContainer' + ) { + insertJSXComment(j, element, POLARIS_MIGRATOR_COMMENT); + return; + } + + insertJSXAttribute(j, element, 'variant', 'tertiary'); + removeJSXAttributes(j, element, 'plain'); + removeJSXAttributes(j, element, 'primary'); + } + }); + // Remove `outline` prop from Button removeProp(j, source, componentName, 'outline'); From ae9ab668b6d81782a63e29e42db5e48bcabb893f Mon Sep 17 00:00:00 2001 From: aveline Date: Mon, 28 Aug 2023 14:02:04 -0700 Subject: [PATCH 05/22] Handle `Button` `plain` `monochrome` bools to variant --- .../steps/replace-bool-props-with-variant.ts | 72 +++++++++++++++++++ .../tests/transform.test.ts | 1 + ...2-react-update-button-components.input.tsx | 3 - ...-react-update-button-components.output.tsx | 3 - ...tton-plain-monochrome-components.input.tsx | 24 +++++++ ...ton-plain-monochrome-components.output.tsx | 22 ++++++ ...-button-primary-plain-components.input.tsx | 4 ++ ...button-primary-plain-components.output.tsx | 2 + .../transform.ts | 65 ++++++----------- 9 files changed, 148 insertions(+), 48 deletions(-) create mode 100644 polaris-migrator/src/migrations/v12-react-update-button-components/steps/replace-bool-props-with-variant.ts create mode 100644 polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-plain-monochrome-components.input.tsx create mode 100644 polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-plain-monochrome-components.output.tsx diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/steps/replace-bool-props-with-variant.ts b/polaris-migrator/src/migrations/v12-react-update-button-components/steps/replace-bool-props-with-variant.ts new file mode 100644 index 00000000000..377ae26039a --- /dev/null +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/steps/replace-bool-props-with-variant.ts @@ -0,0 +1,72 @@ +import type { + JSXOpeningElement, + JSXAttribute, + ASTPath, + Collection, +} from 'jscodeshift'; +import type core from 'jscodeshift'; + +import {POLARIS_MIGRATOR_COMMENT} from '../../../utilities/constants'; +import { + insertJSXComment, + insertJSXAttribute, + removeJSXAttributes, +} from '../../../utilities/jsx'; + +export function replaceBoolPropsWithVariant( + j: core.JSCodeshift, + source: Collection, + componentName: string, + prop1: string, + prop2: string, + variantValue: string, +) { + source.findJSXElements(componentName).forEach((element: ASTPath) => { + const allAttributes = + (j(element).find(j.JSXOpeningElement).get().value as JSXOpeningElement) + .attributes ?? []; + const jsxAttributes = allAttributes as JSXAttribute[]; + const attribute1 = jsxAttributes.find( + (attribute) => attribute.name.name === prop1, + ); + const attribute2 = jsxAttributes.find( + (attribute) => attribute.name.name === prop2, + ); + + if (attribute1 && attribute2) { + if ( + attribute1.value?.type === 'JSXExpressionContainer' || + attribute2.value?.type === 'JSXExpressionContainer' + ) { + insertJSXComment(j, element, POLARIS_MIGRATOR_COMMENT); + return; + } + + // Handle self-closing elements + if ( + element.node.openingElement.selfClosing === true && + element.node.openingElement.name.type === 'JSXIdentifier' + ) { + // Add variant prop + element.node.openingElement.attributes = [ + ...element.node.openingElement.attributes, + j.jsxAttribute( + j.jsxIdentifier('variant'), + j.stringLiteral(variantValue), + ), + ]; + + attribute1.name.name = ''; + attribute1.value = null; + attribute2.name.name = ''; + attribute2.value = null; + return; + } + + // Handle regular elements + insertJSXAttribute(j, element, 'variant', variantValue); + removeJSXAttributes(j, element, prop2); + removeJSXAttributes(j, element, prop1); + } + }); +} diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/transform.test.ts b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/transform.test.ts index ed6b76ee9ef..86df8f2a4bd 100644 --- a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/transform.test.ts +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/transform.test.ts @@ -4,6 +4,7 @@ const transform = 'v12-react-update-button-components'; const fixtures = [ 'v12-react-update-button-components', 'v12-react-update-button-primary-plain-components', + 'v12-react-update-button-plain-monochrome-components', ]; for (const fixture of fixtures) { diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.input.tsx b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.input.tsx index 5a0d0603670..706152ef981 100644 --- a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.input.tsx +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.input.tsx @@ -11,9 +11,6 @@ export function App() { - ); diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx index ba45017e676..7b43210190a 100644 --- a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx @@ -11,9 +11,6 @@ export function App() { - ); diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-plain-monochrome-components.input.tsx b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-plain-monochrome-components.input.tsx new file mode 100644 index 00000000000..f9f2bf5aeaa --- /dev/null +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-plain-monochrome-components.input.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import {Button} from '@shopify/polaris'; +import {PhoneMajor} from '@shopify/polaris-icons'; + +export function App() { + const isPolarisUplift = true; + return ( + <> + + + + + + + {/* polaris-migrator: Unable to migrate the following expression. Please upgrade manually. */} + + {/* polaris-migrator: Unable to migrate the following expression. Please upgrade manually. */} + + + + {/* polaris-migrator: Unable to migrate the following expression. Please upgrade manually. */} + + + ); } diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx index 7b43210190a..3a2993840fb 100644 --- a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx @@ -8,10 +8,12 @@ export function App() { - + + ); } diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/transform.ts b/polaris-migrator/src/migrations/v12-react-update-button-components/transform.ts index f5b362e6510..069551bc5d4 100644 --- a/polaris-migrator/src/migrations/v12-react-update-button-components/transform.ts +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/transform.ts @@ -39,6 +39,7 @@ export default function transformer( // Remove `outline` prop from Button removeProp(j, source, componentName, 'outline'); + removeProp(j, source, componentName, 'monochrome'); return source.toSource(); } From 2df9a4ca7e0b31fa8477f123d81bb41feb70d151 Mon Sep 17 00:00:00 2001 From: aveline Date: Wed, 30 Aug 2023 10:23:02 -0700 Subject: [PATCH 07/22] Update test cases --- .../v12-react-update-button-components.input.tsx | 7 +++++++ .../v12-react-update-button-components.output.tsx | 11 +++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.input.tsx b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.input.tsx index 2d16f87cf17..df494d7d1be 100644 --- a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.input.tsx +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.input.tsx @@ -14,6 +14,13 @@ export function App() { + + + ); } diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx index 3a2993840fb..54fe1a7b3b1 100644 --- a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx @@ -5,15 +5,22 @@ import {PhoneMajor} from '@shopify/polaris-icons'; export function App() { return ( <> - - + + + + ); } From 947a8d36930fbbdda03a11dd35a4502562b4b0a6 Mon Sep 17 00:00:00 2001 From: aveline Date: Wed, 30 Aug 2023 13:39:38 -0700 Subject: [PATCH 08/22] Update test cases --- .../tests/v12-react-update-button-components.output.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx index 54fe1a7b3b1..c32c081af91 100644 --- a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx @@ -14,11 +14,11 @@ export function App() { - - From 18478cfcce5adbb9df108857ffec7e8201d6c9d0 Mon Sep 17 00:00:00 2001 From: aveline Date: Wed, 30 Aug 2023 13:41:35 -0700 Subject: [PATCH 09/22] Refactor migration Co-Authored-By: Aaron Casanova <32409546+aaronccasanova@users.noreply.github.com> --- .../transform.ts | 283 ++++++++++++++++-- polaris-migrator/src/utilities/jsx.ts | 15 - 2 files changed, 256 insertions(+), 42 deletions(-) diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/transform.ts b/polaris-migrator/src/migrations/v12-react-update-button-components/transform.ts index 069551bc5d4..63bdd63d46e 100644 --- a/polaris-migrator/src/migrations/v12-react-update-button-components/transform.ts +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/transform.ts @@ -1,9 +1,15 @@ -import type {API, FileInfo, Options} from 'jscodeshift'; +import type { + API, + ASTPath, + FileInfo, + JSXAttribute, + JSXOpeningElement, + Options, +} from 'jscodeshift'; +import {POLARIS_MIGRATOR_COMMENT} from '../../utilities/constants'; import {hasImportDeclaration} from '../../utilities/imports'; -import {removeProp} from '../../utilities/jsx'; - -import {replaceBoolPropsWithVariant} from './steps/replace-bool-props-with-variant'; +import {insertJSXComment} from '../../utilities/jsx'; export default function transformer( fileInfo: FileInfo, @@ -17,29 +23,252 @@ export default function transformer( return fileInfo.source; } - // Rename `primary` `plain` to `variant=`tertiary` - replaceBoolPropsWithVariant( - j, - source, - componentName, - 'plain', - 'primary', - 'tertiary', - ); - - // Rename `plain` `monochrome` to `variant=`plainMonochrome` - replaceBoolPropsWithVariant( - j, - source, - componentName, - 'plain', - 'monochrome', - 'monochromePlain', - ); - - // Remove `outline` prop from Button - removeProp(j, source, componentName, 'outline'); - removeProp(j, source, componentName, 'monochrome'); + // For each instance of a `Button` get all the attributes and update accordingly + source.findJSXElements(componentName).forEach((element: ASTPath) => { + const openingElement = j(element).find(j.JSXOpeningElement).get() + .value as JSXOpeningElement; + + const allAttributes = openingElement.attributes ?? []; + const jsxAttributes = allAttributes as JSXAttribute[]; + + const plain = jsxAttributes.find( + (attribute) => attribute.name.name === 'plain', + ); + const isPlainValid = plain?.value === null; + const monochrome = jsxAttributes.find( + (attribute) => attribute.name.name === 'monochrome', + ); + const isMonochromeValid = monochrome?.value === null; + const primary = jsxAttributes.find( + (attribute) => attribute.name.name === 'primary', + ); + const isPrimaryValid = primary?.value === null; + const primarySuccess = jsxAttributes.find( + (attribute) => attribute.name.name === 'primarySuccess', + ); + const isPrimarySuccessValid = primarySuccess?.value === null; + const outline = jsxAttributes.find( + (attribute) => attribute.name.name === 'outline', + ); + const isOutlineValid = outline?.value === null; + const destructive = jsxAttributes.find( + (attribute) => attribute.name.name === 'destructive', + ); + const isDestructiveValid = destructive?.value === null; + + const variantAttributes = [ + plain, + monochrome, + primary, + outline, + primarySuccess, + ].filter(Boolean); + + let canRemovePlain = false; + let canRemoveMonochrome = false; + let canRemovePrimary = false; + let canRemoveOutline = false; + let canRemovePrimarySuccess = false; + let canRemoveDestructive = false; + let variantValue = ''; + let toneValue = ''; + + if (plain) { + if (variantAttributes.length > 2) { + insertJSXComment(j, element, POLARIS_MIGRATOR_COMMENT); + return; + } + if (plain && primary && isPlainValid && isPrimaryValid) { + canRemovePlain = true; + canRemovePrimary = true; + variantValue = 'tertiary'; + } else if (plain && monochrome && isPlainValid && isMonochromeValid) { + canRemovePlain = true; + canRemoveMonochrome = true; + variantValue = 'monochromePlain'; + } else if (plain && destructive && isPlainValid && isDestructiveValid) { + canRemovePlain = true; + canRemoveDestructive = true; + variantValue = 'plain'; + toneValue = 'critical'; + } else if (plain && isPlainValid) { + canRemovePlain = true; + variantValue = 'plain'; + } else { + insertJSXComment(j, element, POLARIS_MIGRATOR_COMMENT); + return; + } + } + + if (monochrome) { + if (variantAttributes.length > 2) { + insertJSXComment(j, element, POLARIS_MIGRATOR_COMMENT); + return; + } + if (monochrome && outline && isMonochromeValid && isOutlineValid) { + canRemoveMonochrome = true; + canRemoveOutline = true; + } else if (plain && monochrome && isPlainValid && isMonochromeValid) { + canRemovePlain = true; + canRemoveMonochrome = true; + variantValue = 'monochromePlain'; + } else if (monochrome && isMonochromeValid) { + canRemoveMonochrome = true; + } else { + insertJSXComment(j, element, POLARIS_MIGRATOR_COMMENT); + return; + } + } + + if (primary) { + if (variantAttributes.length > 2) { + insertJSXComment(j, element, POLARIS_MIGRATOR_COMMENT); + return; + } + if (plain && primary && isPlainValid && isPrimaryValid) { + canRemovePlain = true; + canRemovePrimary = true; + variantValue = 'tertiary'; + } else if ( + primary && + destructive && + isPrimaryValid && + isDestructiveValid + ) { + canRemovePrimary = true; + canRemoveDestructive = true; + variantValue = 'primary'; + toneValue = 'critical'; + } else if (primary && isPrimaryValid) { + canRemovePrimary = true; + variantValue = 'primary'; + } else { + insertJSXComment(j, element, POLARIS_MIGRATOR_COMMENT); + return; + } + } + + if (outline) { + if (variantAttributes.length > 2) { + insertJSXComment(j, element, POLARIS_MIGRATOR_COMMENT); + return; + } + if (outline && destructive && isOutlineValid && isDestructiveValid) { + canRemoveOutline = true; + canRemoveDestructive = true; + toneValue = 'critical'; + } else if (monochrome && outline && isMonochromeValid && isOutlineValid) { + canRemoveMonochrome = true; + canRemoveOutline = true; + } else if (outline && isOutlineValid) { + canRemoveOutline = true; + } else { + insertJSXComment(j, element, POLARIS_MIGRATOR_COMMENT); + return; + } + } + + if (primarySuccess) { + if (variantAttributes.length > 2) { + insertJSXComment(j, element, POLARIS_MIGRATOR_COMMENT); + return; + } + if (primarySuccess && isPrimarySuccessValid) { + canRemovePrimarySuccess = true; + variantValue = 'primary'; + toneValue = 'success'; + } else { + insertJSXComment(j, element, POLARIS_MIGRATOR_COMMENT); + return; + } + } + + if (destructive) { + if (plain && destructive && isPlainValid && isDestructiveValid) { + canRemovePlain = true; + canRemoveDestructive = true; + variantValue = 'plain'; + toneValue = 'critical'; + } else if ( + outline && + destructive && + isOutlineValid && + isDestructiveValid + ) { + canRemoveOutline = true; + canRemoveDestructive = true; + toneValue = 'critical'; + } else if ( + primary && + destructive && + isPrimaryValid && + isDestructiveValid + ) { + canRemovePrimary = true; + canRemoveDestructive = true; + variantValue = 'primary'; + toneValue = 'critical'; + } else if ( + destructive && + isDestructiveValid && + variantAttributes.length === 0 + ) { + canRemoveDestructive = true; + toneValue = 'critical'; + variantValue = 'primary'; + } else if (destructive && isDestructiveValid) { + canRemoveDestructive = true; + toneValue = 'critical'; + } else { + insertJSXComment(j, element, POLARIS_MIGRATOR_COMMENT); + return; + } + } + + if (plain && canRemovePlain) { + plain.name.name = ''; + plain.value = null; + } + + if (monochrome && canRemoveMonochrome) { + monochrome.name.name = ''; + monochrome.value = null; + } + + if (primary && canRemovePrimary) { + primary.name.name = ''; + primary.value = null; + } + + if (outline && canRemoveOutline) { + outline.name.name = ''; + outline.value = null; + } + + if (primarySuccess && canRemovePrimarySuccess) { + primarySuccess.name.name = ''; + primarySuccess.value = null; + } + + if (destructive && canRemoveDestructive) { + destructive.name.name = ''; + destructive.value = null; + } + + if (variantValue && openingElement && openingElement.attributes) { + openingElement.attributes.push( + j.jsxAttribute( + j.jsxIdentifier('variant'), + j.stringLiteral(variantValue), + ), + ); + } + if (toneValue && openingElement && openingElement.attributes) { + openingElement.attributes.push( + j.jsxAttribute(j.jsxIdentifier('tone'), j.stringLiteral(toneValue)), + ); + } + }); return source.toSource(); } diff --git a/polaris-migrator/src/utilities/jsx.ts b/polaris-migrator/src/utilities/jsx.ts index ba32c67f8a4..a46c9e608c0 100644 --- a/polaris-migrator/src/utilities/jsx.ts +++ b/polaris-migrator/src/utilities/jsx.ts @@ -175,21 +175,6 @@ export function renameProps( } } -export function removeProp( - j: core.JSCodeshift, - source: Collection, - componentName: string, - prop: string, -) { - source - .findJSXElements(componentName) - .find(j.JSXAttribute) - .filter((path) => path.node.name.name === prop) - .remove(); - - return source; -} - export function insertJSXComment( j: core.JSCodeshift, element: ASTPath, From 70bc2897faa2175f06f678f8d8cccf2bc6c061f9 Mon Sep 17 00:00:00 2001 From: aveline Date: Wed, 30 Aug 2023 14:39:09 -0700 Subject: [PATCH 10/22] Add more test cases --- ...2-react-update-button-components.input.tsx | 33 ++++++++++++++++ ...-react-update-button-components.output.tsx | 38 +++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.input.tsx b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.input.tsx index df494d7d1be..04983fb4dd3 100644 --- a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.input.tsx +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.input.tsx @@ -3,6 +3,10 @@ import {Button} from '@shopify/polaris'; import {PhoneMajor} from '@shopify/polaris-icons'; export function App() { + const hasFormError = false; + const polarisSummerEditions2023Enabled = true; + const disabled = false; + const primary = true; return ( <> + + + + ); } diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx index c32c081af91..a9a043a8209 100644 --- a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-components.output.tsx @@ -3,6 +3,10 @@ import {Button} from '@shopify/polaris'; import {PhoneMajor} from '@shopify/polaris-icons'; export function App() { + const hasFormError = false; + const polarisSummerEditions2023Enabled = true; + const disabled = false; + const primary = true; return ( <> + + {/* polaris-migrator: Unable to migrate the following expression. Please upgrade manually. */} + + {/* polaris-migrator: Unable to migrate the following expression. Please upgrade manually. */} + + {/* polaris-migrator: Unable to migrate the following expression. Please upgrade manually. */} + ); } From 6645fee831768d5e5d9ba4cfeec9ad9e990bb010 Mon Sep 17 00:00:00 2001 From: aveline Date: Thu, 31 Aug 2023 09:16:51 -0700 Subject: [PATCH 11/22] Add tests for self-closing case --- .../tests/transform.test.ts | 1 + ...react-update-button-self-closing.input.tsx | 45 +++++++++++++++++ ...eact-update-button-self-closing.output.tsx | 48 +++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-self-closing.input.tsx create mode 100644 polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-self-closing.output.tsx diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/transform.test.ts b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/transform.test.ts index 86df8f2a4bd..2e5a43f1225 100644 --- a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/transform.test.ts +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/transform.test.ts @@ -5,6 +5,7 @@ const fixtures = [ 'v12-react-update-button-components', 'v12-react-update-button-primary-plain-components', 'v12-react-update-button-plain-monochrome-components', + 'v12-react-update-button-self-closing', ]; for (const fixture of fixtures) { diff --git a/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-self-closing.input.tsx b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-self-closing.input.tsx new file mode 100644 index 00000000000..12d4a38b810 --- /dev/null +++ b/polaris-migrator/src/migrations/v12-react-update-button-components/tests/v12-react-update-button-self-closing.input.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import {Button} from '@shopify/polaris'; +import {PhoneMajor} from '@shopify/polaris-icons'; + +export function App() { + const hasFormError = false; + const polarisSummerEditions2023Enabled = true; + const disabled = false; + const primary = true; + return ( + <> + + My Button + ); } diff --git a/polaris-migrator/src/migrations/v12-react-update-button-component/tests/v12-react-update-button-component.output.tsx b/polaris-migrator/src/migrations/v12-react-update-button-component/tests/v12-react-update-button-component.output.tsx index a9a043a8209..c6f068b38f4 100644 --- a/polaris-migrator/src/migrations/v12-react-update-button-component/tests/v12-react-update-button-component.output.tsx +++ b/polaris-migrator/src/migrations/v12-react-update-button-component/tests/v12-react-update-button-component.output.tsx @@ -1,12 +1,21 @@ import React from 'react'; +import type {ButtonProps} from '@shopify/polaris'; import {Button} from '@shopify/polaris'; import {PhoneMajor} from '@shopify/polaris-icons'; +declare function CustomButton( + props: /* polaris-migrator: Unable to migrate the following expression. Please upgrade manually. */ + ButtonProps, +): JSX.Element; + export function App() { const hasFormError = false; const polarisSummerEditions2023Enabled = true; const disabled = false; const primary = true; + const MyButton = + /* polaris-migrator: Unable to migrate the following expression. Please upgrade manually. */ + Button; return ( <> + My Button + ); } diff --git a/polaris-migrator/src/migrations/v12-react-update-button-component/transform.ts b/polaris-migrator/src/migrations/v12-react-update-button-component/transform.ts index e0f386aa9b4..9c8a427cd93 100644 --- a/polaris-migrator/src/migrations/v12-react-update-button-component/transform.ts +++ b/polaris-migrator/src/migrations/v12-react-update-button-component/transform.ts @@ -13,7 +13,7 @@ import { hasImportDeclaration, hasImportSpecifier, } from '../../utilities/imports'; -import {insertJSXComment} from '../../utilities/jsx'; +import {insertCommentBefore, insertJSXComment} from '../../utilities/jsx'; export default function transformer( fileInfo: FileInfo, @@ -26,7 +26,8 @@ export default function transformer( if ( !( hasImportDeclaration(j, source, '@shopify/polaris') && - hasImportSpecifier(j, source, 'Button', '@shopify/polaris') + (hasImportSpecifier(j, source, 'Button', '@shopify/polaris') || + hasImportSpecifier(j, source, 'ButtonProps', '@shopify/polaris')) ) ) { return fileInfo.source; @@ -35,6 +36,13 @@ export default function transformer( const localElementName = getImportSpecifierName(j, source, 'Button', '@shopify/polaris') || 'Button'; + const localElementTypeName = getImportSpecifierName( + j, + source, + 'ButtonProps', + '@shopify/polaris', + ); + // For each instance of a `Button` get all the attributes and update accordingly source.findJSXElements(localElementName).forEach((element: ASTPath) => { const openingElement = j(element).find(j.JSXOpeningElement).get() @@ -294,5 +302,20 @@ export default function transformer( ); } }); + + source + .find(j.Identifier) + .filter( + (path) => + path.node.name === localElementName || + path.node.name === localElementTypeName, + ) + .forEach((path) => { + if (path.node.type !== 'Identifier') return; + + if (path.parent.value.type === 'ImportSpecifier') return; + + insertCommentBefore(j, path, POLARIS_MIGRATOR_COMMENT); + }); return source.toSource(); } From 32352e9e88c4694169a2aaf362f8751febe8da3d Mon Sep 17 00:00:00 2001 From: aveline Date: Wed, 6 Sep 2023 12:42:08 -0700 Subject: [PATCH 22/22] Add check to not capture children of `MemberExpression` Co-Authored-By: Aaron Casanova <32409546+aaronccasanova@users.noreply.github.com> --- .../tests/v12-react-update-button-component.input.tsx | 4 ++++ .../tests/v12-react-update-button-component.output.tsx | 4 ++++ .../v12-react-update-button-component/transform.ts | 7 ++++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/polaris-migrator/src/migrations/v12-react-update-button-component/tests/v12-react-update-button-component.input.tsx b/polaris-migrator/src/migrations/v12-react-update-button-component/tests/v12-react-update-button-component.input.tsx index c548eaafc9b..56c14974592 100644 --- a/polaris-migrator/src/migrations/v12-react-update-button-component/tests/v12-react-update-button-component.input.tsx +++ b/polaris-migrator/src/migrations/v12-react-update-button-component/tests/v12-react-update-button-component.input.tsx @@ -4,6 +4,9 @@ import {Button} from '@shopify/polaris'; import {PhoneMajor} from '@shopify/polaris-icons'; declare function CustomButton(props: ButtonProps): JSX.Element; +declare const Styles: { + [className: string]: string; +}; export function App() { const hasFormError = false; @@ -60,6 +63,7 @@ export function App() { My Button +
Fake Button
); } diff --git a/polaris-migrator/src/migrations/v12-react-update-button-component/tests/v12-react-update-button-component.output.tsx b/polaris-migrator/src/migrations/v12-react-update-button-component/tests/v12-react-update-button-component.output.tsx index c6f068b38f4..ed3a7974399 100644 --- a/polaris-migrator/src/migrations/v12-react-update-button-component/tests/v12-react-update-button-component.output.tsx +++ b/polaris-migrator/src/migrations/v12-react-update-button-component/tests/v12-react-update-button-component.output.tsx @@ -7,6 +7,9 @@ declare function CustomButton( props: /* polaris-migrator: Unable to migrate the following expression. Please upgrade manually. */ ButtonProps, ): JSX.Element; +declare const Styles: { + [className: string]: string; +}; export function App() { const hasFormError = false; @@ -70,6 +73,7 @@ export function App() { My Button +
Fake Button
); } diff --git a/polaris-migrator/src/migrations/v12-react-update-button-component/transform.ts b/polaris-migrator/src/migrations/v12-react-update-button-component/transform.ts index 9c8a427cd93..fc6f5ff0968 100644 --- a/polaris-migrator/src/migrations/v12-react-update-button-component/transform.ts +++ b/polaris-migrator/src/migrations/v12-react-update-button-component/transform.ts @@ -313,7 +313,12 @@ export default function transformer( .forEach((path) => { if (path.node.type !== 'Identifier') return; - if (path.parent.value.type === 'ImportSpecifier') return; + if ( + path.parent.value.type === 'ImportSpecifier' || + path.parent.value.type === 'MemberExpression' + ) { + return; + } insertCommentBefore(j, path, POLARIS_MIGRATOR_COMMENT); });