Skip to content

Commit

Permalink
Optimize the implementation to perform a single AST traversal
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronccasanova committed Oct 13, 2023
1 parent bf14319 commit ddf045f
Showing 1 changed file with 121 additions and 103 deletions.
224 changes: 121 additions & 103 deletions polaris-migrator/src/migrations/react-update-component-prop/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,23 +85,77 @@ export default function transformer(
return;
}

for (const replacementMapEntry of Object.entries(replacementMaps)) {
const [componentName, replacementOptions] = replacementMapEntry;
interface LocalElementConfig {
componentNames: string[];
replacementOptions: ReplacementOptions[];
}

const localElementConfigs = Object.fromEntries(
Object.entries(replacementMaps)
.map((replacementMapEntry): null | [string, LocalElementConfig] => {
const [componentName, replacementOptions] = replacementMapEntry;

const componentNames = componentName.split('.');
const targetComponentName = componentNames[0];

const sourcePaths = normalizeImportSourcePaths(j, source, {
relative: options.relative,
from: targetComponentName,
to: targetComponentName,
});

if (
!sourcePaths ||
!hasImportSpecifier(j, source, targetComponentName, sourcePaths.from)
) {
return null;
}

const componentNames = componentName.split('.');
const targetComponentName = componentNames[0];
const localElementName =
getImportSpecifierName(
j,
source,
targetComponentName,
sourcePaths.from,
) || targetComponentName;

componentNames[0] = localElementName;

return [
localElementName,
{
componentNames,
replacementOptions,
},
];
})
.filter(Boolean) as [string, LocalElementConfig][],
);

source.find(j.JSXElement).forEach((element) => {
const elementNames = getElementNames(element.node.openingElement.name);

const localElementConfig = localElementConfigs[elementNames[0]];

if (!localElementConfig) return;

const {componentNames, replacementOptions} = localElementConfig;

if (
elementNames.length !== componentNames.length ||
!componentNames.every((name, index) => name === elementNames[index])
) {
return;
}

const sourcePaths = normalizeImportSourcePaths(j, source, {
relative: options.relative,
from: targetComponentName,
to: targetComponentName,
});
const allAttributes = element.node.openingElement.attributes ?? [];

if (
!sourcePaths ||
!hasImportSpecifier(j, source, targetComponentName, sourcePaths.from)
// Early exit on spread operators
allAttributes.some((attribute) => attribute.type !== 'JSXAttribute')
) {
continue;
insertJSXComment(j, element, POLARIS_MIGRATOR_COMMENT);
return;
}

for (const replacementOption of replacementOptions) {
Expand All @@ -112,117 +166,81 @@ export default function transformer(
fromValue,
toValue,
} = replacementOption;

const localElementName =
getImportSpecifierName(
j,
source,
targetComponentName,
sourcePaths.from,
) || targetComponentName;

componentNames[0] = localElementName;

source.find(j.JSXElement).forEach((element) => {
const jsxAttributes = allAttributes as JSXAttribute[];

const fromPropAttribute = jsxAttributes.find(
(attribute) => attribute.name.name === fromProp,
);

if (
fromPropAttribute &&
(fromPropType === 'boolean'
? fromPropAttribute.value !== null
: fromPropAttribute.value?.type !== 'StringLiteral')
) {
insertJSXComment(j, element, POLARIS_MIGRATOR_COMMENT);
return;
}

jsxAttributes.forEach((jsxAttribute) => {
if (
element.node.openingElement.name.type === 'JSXIdentifier' &&
element.node.openingElement.name.name !== localElementName
!(
jsxAttribute.type === 'JSXAttribute' &&
jsxAttribute.name.name === fromProp
)
) {
return;
return jsxAttribute;
}

const nameChain = getNameChain(element.node.openingElement.name);
const attributeValueValue =
jsxAttribute.value?.type === 'StringLiteral'
? jsxAttribute.value.value
: null;

if (
nameChain.length === componentNames.length &&
componentNames.every((name, index) => name === nameChain[index])
toProp &&
toProp !== fromProp &&
(!fromValue || fromValue === attributeValueValue)
) {
const allAttributes = element.node.openingElement.attributes ?? [];

if (
// Early exit on spread operators
allAttributes.some((attribute) => attribute.type !== 'JSXAttribute')
) {
insertJSXComment(j, element, POLARIS_MIGRATOR_COMMENT);
return;
}

const jsxAttributes = allAttributes as JSXAttribute[];

const fromPropAttribute = jsxAttributes.find(
(attribute) => attribute.name.name === fromProp,
);

if (
fromPropAttribute &&
(fromPropType === 'boolean'
? fromPropAttribute.value !== null
: fromPropAttribute.value?.type !== 'StringLiteral')
) {
insertJSXComment(j, element, POLARIS_MIGRATOR_COMMENT);
return;
}

jsxAttributes.forEach((jsxAttribute) => {
if (
!(
jsxAttribute.type === 'JSXAttribute' &&
jsxAttribute.name.name === fromProp
)
) {
return jsxAttribute;
}

const attributeValueValue =
jsxAttribute.value?.type === 'StringLiteral'
? jsxAttribute.value.value
: null;

if (
toProp &&
toProp !== fromProp &&
(!fromValue || fromValue === attributeValueValue)
) {
jsxAttribute.name.name = toProp;
}

if (fromValue && fromValue !== attributeValueValue)
return jsxAttribute;

jsxAttribute.value = toValue
? j.stringLiteral(toValue)
: jsxAttribute.value;
});
jsxAttribute.name.name = toProp;
}

if (fromValue && fromValue !== attributeValueValue) return jsxAttribute;

jsxAttribute.value = toValue
? j.stringLiteral(toValue)
: jsxAttribute.value;
});
}
});

source
.find(j.Identifier)
.filter((path) => path.node.name === localElementName)
.forEach((path) => {
if (path.node.type !== 'Identifier') return;
Object.keys(localElementConfigs).forEach((localElementName) => {
source
.find(j.Identifier)
.filter((path) => path.node.name === localElementName)
.forEach((path) => {
if (path.node.type !== 'Identifier') return;

if (
path.parent.value.type === 'ImportSpecifier' ||
path.parent.value.type === 'MemberExpression'
) {
return;
}
if (
path.parent.value.type === 'ImportSpecifier' ||
path.parent.value.type === 'MemberExpression'
) {
return;
}

insertCommentBefore(j, path, POLARIS_MIGRATOR_COMMENT);
});
}
}
insertCommentBefore(j, path, POLARIS_MIGRATOR_COMMENT);
});
});

return source.toSource();
}

function getNameChain(name: JSXOpeningElement['name']): string[] {
function getElementNames(name: JSXOpeningElement['name']): string[] {
if (name.type === 'JSXIdentifier') {
return [name.name];
}
if (name.type === 'JSXMemberExpression') {
return [...getNameChain(name.object), name.property.name];
return [...getElementNames(name.object), name.property.name];
}
return [];
}

0 comments on commit ddf045f

Please sign in to comment.