229 lines
6.9 KiB
TypeScript
229 lines
6.9 KiB
TypeScript
import {
|
|
type Expression,
|
|
type Node,
|
|
type ObjectLiteralExpression,
|
|
Project,
|
|
type SourceFile,
|
|
ts,
|
|
} from "ts-morph"
|
|
|
|
export function codemod(paths: string) {
|
|
const project = new Project()
|
|
project.addSourceFilesAtPaths(paths)
|
|
|
|
project.getSourceFiles().forEach((file) => {
|
|
processSourceFile(file)
|
|
file.saveSync()
|
|
})
|
|
}
|
|
|
|
export function processSourceFile(file: SourceFile): void {
|
|
file.getDescendantsOfKind(ts.SyntaxKind.CallExpression).forEach((call) => {
|
|
const expression = call.getExpression().getText()
|
|
|
|
if (expression === "intl.formatMessage") {
|
|
const formatMessageArgs = call.getArguments()
|
|
|
|
if (formatMessageArgs.length > 0) {
|
|
const firstFormatMessageArg = formatMessageArgs[0]
|
|
|
|
// Handle object literal in the first argument
|
|
if (
|
|
firstFormatMessageArg.getKind() ===
|
|
ts.SyntaxKind.ObjectLiteralExpression
|
|
) {
|
|
const expressionVariables = getVariableArguments(
|
|
firstFormatMessageArg.asKindOrThrow(
|
|
ts.SyntaxKind.ObjectLiteralExpression
|
|
)
|
|
)
|
|
if (expressionVariables === "{}") {
|
|
// No variables
|
|
transformObjectLiteral(
|
|
firstFormatMessageArg.asKindOrThrow(
|
|
ts.SyntaxKind.ObjectLiteralExpression
|
|
)
|
|
)
|
|
} else {
|
|
// Handle variables
|
|
const expressionReplacement = `intl.formatMessage(${transformObjectLiteralAndReturn(
|
|
firstFormatMessageArg.asKindOrThrow(
|
|
ts.SyntaxKind.ObjectLiteralExpression
|
|
)
|
|
)}, ${expressionVariables})`
|
|
|
|
call.replaceWithText(expressionReplacement)
|
|
}
|
|
}
|
|
|
|
// Handle ternary expressions in the first argument
|
|
else if (
|
|
firstFormatMessageArg.getKind() ===
|
|
ts.SyntaxKind.ConditionalExpression
|
|
) {
|
|
const conditional = firstFormatMessageArg.asKindOrThrow(
|
|
ts.SyntaxKind.ConditionalExpression
|
|
)
|
|
|
|
const whenTrue = conditional.getWhenTrue()
|
|
const whenFalse = conditional.getWhenFalse()
|
|
|
|
// Check for variables in message
|
|
const varArgsWhenTrue = getVariableArguments(whenTrue)
|
|
const varArgsWhenFalse = getVariableArguments(whenFalse)
|
|
|
|
// Replacements
|
|
const whenTrueReplacement =
|
|
varArgsWhenTrue !== "{}"
|
|
? `intl.formatMessage(${transformObjectLiteralAndReturn(whenTrue)}, ${varArgsWhenTrue})`
|
|
: `intl.formatMessage(${transformObjectLiteralAndReturn(whenTrue)})`
|
|
|
|
const whenFalseReplacement =
|
|
varArgsWhenFalse !== "{}"
|
|
? `intl.formatMessage(${transformObjectLiteralAndReturn(whenFalse)}, ${varArgsWhenFalse})`
|
|
: `intl.formatMessage(${transformObjectLiteralAndReturn(whenFalse)})`
|
|
|
|
// Replace the ternary expression
|
|
call.replaceWithText(
|
|
`${conditional.getCondition().getText()} ? ${whenTrueReplacement} : ${whenFalseReplacement}`
|
|
)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
// Format the file using its existing formatting rules
|
|
file.formatText({
|
|
indentSize: 2,
|
|
})
|
|
}
|
|
|
|
// Helper function to transform object literals
|
|
function transformObjectLiteral(objectLiteral: ObjectLiteralExpression): void {
|
|
const idProperty = objectLiteral
|
|
.getProperties()
|
|
.find((prop) => {
|
|
const p = prop.asKindOrThrow(ts.SyntaxKind.PropertyAssignment)
|
|
return p.getName() === "id"
|
|
})
|
|
?.asKindOrThrow(ts.SyntaxKind.PropertyAssignment)
|
|
|
|
if (idProperty) {
|
|
const idValue = idProperty.getInitializer()?.getText()
|
|
|
|
if (idValue) {
|
|
// Add defaultMessage
|
|
if (
|
|
!objectLiteral
|
|
.getProperties()
|
|
.some(
|
|
(prop) => "getName" in prop && prop.getName() === "defaultMessage"
|
|
)
|
|
) {
|
|
objectLiteral.addPropertyAssignment({
|
|
name: "defaultMessage",
|
|
initializer: idValue,
|
|
})
|
|
}
|
|
|
|
// Remove the id property
|
|
idProperty.remove()
|
|
}
|
|
}
|
|
|
|
// Add description if not present
|
|
// if (
|
|
// !objectLiteral
|
|
// .getProperties()
|
|
// .some((prop) => "getName" in prop && prop.getName() === "description")
|
|
// ) {
|
|
// objectLiteral.addPropertyAssignment({
|
|
// name: "description",
|
|
// initializer: `{}`,
|
|
// })
|
|
// }
|
|
|
|
// Extract variables from the defaultMessage if present
|
|
const defaultMessageProp = objectLiteral
|
|
.getProperties()
|
|
.find((prop) => "getName" in prop && prop.getName() === "defaultMessage")
|
|
?.asKindOrThrow(ts.SyntaxKind.PropertyAssignment)
|
|
|
|
if (defaultMessageProp) {
|
|
const defaultMessageValue = defaultMessageProp.getInitializer()?.getText()
|
|
|
|
if (defaultMessageValue) {
|
|
const extractedVariables =
|
|
extractVariablesFromTemplateString(defaultMessageValue)
|
|
if (extractedVariables.length > 0) {
|
|
// Replace the variables in the defaultMessage with FormatJS placeholders
|
|
const transformedMessage = replaceWithFormatJSPlaceholders(
|
|
defaultMessageValue,
|
|
extractedVariables
|
|
)
|
|
defaultMessageProp.setInitializer(`${transformedMessage}`)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Helper function to extract variables from a template string
|
|
function extractVariablesFromTemplateString(templateString: string): string[] {
|
|
const regex = /\${(.*?)}/g
|
|
const variables: string[] = []
|
|
let match
|
|
|
|
// Find all variable references in the template literal
|
|
while ((match = regex.exec(templateString)) !== null) {
|
|
variables.push(match[1].trim())
|
|
}
|
|
|
|
return variables
|
|
}
|
|
|
|
// Helper function to replace variables with FormatJS placeholders
|
|
function replaceWithFormatJSPlaceholders(
|
|
templateString: string,
|
|
variables: string[]
|
|
): string {
|
|
let transformedMessage = templateString
|
|
|
|
variables.forEach((variable) => {
|
|
transformedMessage = transformedMessage.replace(
|
|
`\${${variable}}`,
|
|
`{${variable}}`
|
|
)
|
|
})
|
|
|
|
return transformedMessage
|
|
}
|
|
|
|
// Helper function to get the variables from the ternary branch
|
|
function getVariableArguments(exp: Expression): string {
|
|
const idProp = exp
|
|
.asKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression)
|
|
.getProperties()
|
|
.find((prop) => "getName" in prop && prop.getName() === "id")
|
|
?.asKindOrThrow(ts.SyntaxKind.PropertyAssignment)
|
|
|
|
if (idProp) {
|
|
const extractedVariables = extractVariablesFromTemplateString(
|
|
idProp.getText()
|
|
)
|
|
if (extractedVariables.length > 0) {
|
|
return `{ ${extractedVariables.map((v) => `${v}: ${v}`).join(", ")} }`
|
|
}
|
|
}
|
|
return "{}" // Return empty if no variables found
|
|
}
|
|
|
|
// Helper function to transform object literals and return text
|
|
function transformObjectLiteralAndReturn(object: Node): string {
|
|
if (object.getKind() === ts.SyntaxKind.ObjectLiteralExpression) {
|
|
const obj = object.asKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression)
|
|
transformObjectLiteral(obj)
|
|
return obj.getText() // Return the transformed object literal as text
|
|
}
|
|
return object.getText()
|
|
}
|