diff --git a/frontend/scripts/codemod.sh b/frontend/scripts/codemod.sh new file mode 100755 index 000000000000..dc0386a4b966 --- /dev/null +++ b/frontend/scripts/codemod.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +echo "Running codemod on: '$1'" +npx jscodeshift --extensions=tsx,jsx -t="scripts/jscodeshift-transform-conditionallyrender.js" $1 +npx jscodeshift --extensions=tsx,jsx -t="scripts/jscodeshift-transform-conditionallyrender.js" $1 + +./node_modules/.bin/biome lint src --write --unsafe +./node_modules/.bin/biome format src --write diff --git a/frontend/scripts/jscodeshift-transform-conditionallyrender.js b/frontend/scripts/jscodeshift-transform-conditionallyrender.js new file mode 100644 index 000000000000..5422ad8600d6 --- /dev/null +++ b/frontend/scripts/jscodeshift-transform-conditionallyrender.js @@ -0,0 +1,82 @@ +// @ts-check + +export const parser = 'tsx'; + +const getAttr = (j, node, attribute) => { + const attributes = node.value?.openingElement?.attributes || []; + const attr = attributes.find( + (attr) => j.JSXAttribute.check(attr) && attr.name.name === attribute, + ); + + if (!attr) { + return null; + } + + const value = attr.value; + + if (value.type === 'StringLiteral') { + return value; + } + + return value?.expression || null; +}; + +/** @type {import('jscodeshift').Transform} */ +const transform = (file, api, options) => { + const j = api.jscodeshift; + const root = j(file.source); + + root.findJSXElements('ConditionallyRender') + .forEach((path) => { + const attributes = path.node.openingElement.attributes; + + attributes?.forEach((attr) => { + if ( + j.JSXAttribute.check(attr) && + (attr.name.name === 'show' || attr.name.name === 'elseShow') + ) { + const attrValue = attr.value; + + // Check if the attribute value is an arrow function returning JSX + if ( + j.JSXExpressionContainer.check(attrValue) && + j.ArrowFunctionExpression.check(attrValue.expression) + ) { + const arrowFunctionBody = attrValue.expression.body; + + if ( + j.JSXElement.check(arrowFunctionBody) || + j.JSXFragment.check(arrowFunctionBody) + ) { + // Replace the arrow function with the direct JSX element or fragment + attr.value = + j.jsxExpressionContainer(arrowFunctionBody); + } + } + } + }); + }) + .replaceWith((node) => { + const isInJSX = ['JSXElement', 'JSXFragment'].includes( + node.parent.value.type, + ); + + const condition = getAttr(j, node, 'condition'); + const show = getAttr(j, node, 'show'); + const elseShow = getAttr(j, node, 'elseShow'); + const alternate = elseShow === null ? j.nullLiteral() : elseShow; + + return isInJSX + ? j.jsxExpressionContainer({ + type: 'ConditionalExpression', + test: condition, + consequent: show, + alternate, + }) + : j.conditionalExpression(condition, show, alternate); + }); + + return root.toSource(); +}; + +export default transform; diff --git a/frontend/src/component/common/ConditionallyRender/ConditionallyRender.tsx b/frontend/src/component/common/ConditionallyRender/ConditionallyRender.tsx index dea335707db8..12a97754cee6 100644 --- a/frontend/src/component/common/ConditionallyRender/ConditionallyRender.tsx +++ b/frontend/src/component/common/ConditionallyRender/ConditionallyRender.tsx @@ -15,6 +15,10 @@ type TargetElement = type RenderFunc = () => JSX.Element; +/** + * @deprecated Use a ternary operator (`condition ? show : elseShow`) + * @see https://docs.getunleash.io/contributing/ADRs/front-end/jsx-conditionals + */ export const ConditionallyRender = ({ condition, show,