Skip to content

Instantly share code, notes, and snippets.

@hi-ogawa
Last active November 15, 2022 04:28
Show Gist options
  • Save hi-ogawa/aa33a19cd4e3b513ff2493a05f59e5c7 to your computer and use it in GitHub Desktop.
Save hi-ogawa/aa33a19cd4e3b513ff2493a05f59e5c7 to your computer and use it in GitHub Desktop.
jscodeshift script for converting number values to string values within specified forms of variable declarations
/* eslint-disable no-console */
//
// usage:
// npm i -D jscodeshift @types/jscodeshift recast
// npx jscodeshift --parser tsx --transform patch-transaction-editable.ts (...files)
//
/*
//// example input ////
const someVariable: TransactionEditable[] = [
{
vol: "0",
prc: -1,
fee: 1 * 2,
realized: SOME_CONSTANT,
}
]
//// example output ////
const someVariable: TransactionEditable[] = [
{
vol: "0",
prc: "-1",
fee: (1 * 2).toString(),
realized: SOME_CONSTANT.toString(),
}
]
*/
import type { API, FileInfo } from "jscodeshift";
import * as recast from "recast";
const TYPES = ["TransactionEditable", "TransactionCreateRequest"];
const NUMERIC_PROPERTY_NAMES = ["vol", "prc", "fee", "realized"];
export default function transformer(file: FileInfo, api: API): string {
const j = api.jscodeshift;
const $j = j(file.source);
for (const path of $j.find(j.VariableDeclaration).paths()) {
for (const decl of path.value.declarations) {
if (decl.type !== "VariableDeclarator") continue;
if (decl.id.type !== "Identifier") continue;
if (decl.id.typeAnnotation?.type !== "TSTypeAnnotation") continue;
// match e.g. `TransactionEditable`, `TransactionEditable[]`, `TransactionCreateRequest`, etc...
if (
j(decl.id.typeAnnotation)
.find(j.Identifier)
.filter(i => TYPES.includes(i.value.name)).length === 0
)
continue;
if (!decl.init) continue;
for (const prop of j(path).find(j.ObjectProperty).paths()) {
if (prop.value.key.type !== "Identifier") continue;
if (!NUMERIC_PROPERTY_NAMES.includes(prop.value.key.name)) continue;
// skip if string
if (prop.value.value.type === "StringLiteral") continue;
// skip if `toString`
const code = recast.print(prop.value).code.trim();
if (code.endsWith("toString()")) continue;
// debug log
console.error(
`${file.path}:${prop.value.loc?.start.line}:${prop.value.loc?.start.column} - ${code}`
);
// number literal to string literal
if (prop.value.value.type === "NumericLiteral") {
prop.replace(
j.objectProperty(
j.identifier(prop.value.key.name),
j.stringLiteral(prop.value.value.value.toString())
)
);
continue;
}
// negated number literal to string literal
if (
prop.value.value.type === "UnaryExpression" &&
prop.value.value.operator === "-" &&
prop.value.value.argument.type === "NumericLiteral"
) {
prop.replace(
j.objectProperty(
j.identifier(prop.value.key.name),
j.stringLiteral("-" + prop.value.value.argument.value.toString())
)
);
continue;
}
// otherwise call `toString` at runtime
if (recast.types.namedTypes.Expression.check(prop.value.value)) {
prop.replace(
j.objectProperty(
j.identifier(prop.value.key.name),
j.callExpression(
// TODO: type assersion seems not working
j.memberExpression(prop.value.value as any, j.identifier("toString")),
[]
)
)
);
}
continue;
}
}
}
return $j.toSource();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment