Last active
May 26, 2022 16:27
-
-
Save skovhus/c57367ce6ecbc3f70bb7c80f25727a11 to your computer and use it in GitHub Desktop.
Quick and dirty flow-to-typscript migration codemod
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# | |
# This is a super quick and dirty codemod for migration a codebase from Flow to TypeScript. | |
# | |
# Step 1: | |
# npm i -g jscodeshift | |
# Step 2: rename all .js files to .ts(x) | |
# find src -name "*.js" -exec sh -c 'mv "$0" "${0%.js}.ts"' {} \; | |
# find storybook -name "*.js" -exec sh -c 'mv "$0" "${0%.js}.ts"' {} \; | |
# find src/**/components -name "*.ts" -exec sh -c 'mv "$0" "${0%.ts}.tsx"' {} \; | |
# find src/**/screens -name "*.ts" -exec sh -c 'mv "$0" "${0%.ts}.tsx"' {} \; | |
# Step 3: | |
# sh flow-to-typescript-codemod.sh src/**/*.ts* | |
# Step 4: | |
# 1) fix linting issues | |
# 2) fix your tests | |
# 3) check the compiler output (./node_modules/.bin/tsc --noEmit src/App.tsx) | |
set -e | |
echo '\nRunning jscodeshift' | |
echo '===================' | |
jscodeshift -t flow-to-typescript-codemod.ts --parser=flow --ignore-pattern=node_modules --extensions=ts,tsx $@ | |
echo '\nRunning sed scripts' | |
echo '===================' | |
for file in "$@" | |
do | |
# basic stuff | |
sed -i "" "s| +| readonly |g" $file | |
sed -i "" "s|mixed|void|g" "$file" | |
sed -i "" "s|\$Shape|Partial|g" "$file" | |
sed -i "" "s|: \*|: any|g" "$file" | |
sed -i "" "s/\$Keys<typeof \([A-Z_a-z]*\)>/keyof typeof \1/g" "$file" | |
# remove exact | |
sed -i "" "s|{[|]|{|g" "$file" | |
sed -i "" "s|[|]}|}|g" "$file" | |
# https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines#null-and-undefined | |
sed -i "" "s|: ?|: undefined \| |g" "$file" | |
sed -i "" "s|= ?|= undefined \| |g" "$file" | |
sed -i "" "s|<?|<undefined \| |g" "$file" | |
sed -i "" "s|import type|import|g" "$file" | |
sed -i "" "s|React.Node|(JSX.Element \| JSX.Element[])[]|g" "$file" | |
sed -i "" "s|ViewPropTypes.style|ViewProps|g" "$file" | |
# unstable... | |
# sed -i "" "s/ \[\([A-Za-z]*\)\]: / \[key in \1\]: /g" "$file" | |
# fixup for codemod missing parentheses in arrow functions | |
sed -i "" "s/ arg: \([A-Z_a-z]*\)/ (arg: \1)/g" "$file" | |
# * --> any | |
done | |
echo '\nRunning prettier' | |
echo '===================' | |
./node_modules/.bin/prettier --write $@ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// See flow-to-typescript-codemod.sh for documentation | |
type FileInfo = { path: string; source: string } | |
export const parser = 'flow' | |
function logger(fileInfo: FileInfo, msg, node) { | |
const lineInfo = node && node.value.loc ? ` line ${node.value.loc.start.line}` : '' | |
console.warn(`warning: (${fileInfo.path}${lineInfo}) ${msg}`) | |
} | |
export default function flowToTypeScript(fileInfo: FileInfo, api: any, options: any) { | |
const j = api.jscodeshift | |
const ast = j(fileInfo.source) | |
const logWarning = (msg: string, node: any) => logger(fileInfo, msg, node) | |
const transforms = [ | |
function updateFunctionTypeParams() { | |
ast.find(j.FunctionTypeParam, { name: null }).forEach((p: any) => { | |
p.value.name = j.identifier('arg') | |
}) | |
}, | |
function fixDuplicateImports() { | |
const fileToImportCount = {} | |
const fileToSpecifiers = {} | |
ast.find(j.ImportDeclaration).forEach((p: any) => { | |
const file = p.value.source.value | |
if (!(file in fileToSpecifiers)) { | |
fileToSpecifiers[file] = [] | |
} | |
fileToSpecifiers[file] = [...fileToSpecifiers[file], ...p.value.specifiers] | |
if (!(file in fileToImportCount)) { | |
fileToImportCount[file] = 0 | |
} else { | |
p.prune() | |
} | |
fileToImportCount[file] += 1 | |
}) | |
Object.keys(fileToImportCount).map(file => { | |
if (fileToImportCount[file] > 1) { | |
ast.find(j.ImportDeclaration, { source: { value: file } }).forEach((p: any) => { | |
p.value.specifiers = fileToSpecifiers[file] | |
}) | |
} | |
}) | |
}, | |
function removeExactTypes() { | |
ast.find(j.ObjectTypeAnnotation, { exact: true }).forEach((p: any) => { | |
p.value.exact = false | |
p.value.inexact = true | |
}) | |
}, | |
] | |
transforms.forEach(t => t()) | |
return ast.toSource({ | |
arrowParensAlways: true, | |
flowObjectCommas: true, | |
quote: 'single', | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment