Created
May 10, 2024 20:23
-
-
Save ericclemmons/55f10b9715e5150dfb526c9807c859ae to your computer and use it in GitHub Desktop.
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
// @ts-check | |
/** | |
* Run `tsc` and pipe output to this script. All errors will write a `@ts-expect-error` comment into the source. | |
* | |
* Usage: | |
* | |
* tsc --noEmit --project ... | ./ts-expect-error.js | |
* | |
* Example (from the monorepo root): | |
* | |
* yarn tsc --noEmit --project packages/design-system | yarn workspace @honor/codemods codemod:ts-expect-error | |
* | |
* Finally, RUN IT AGAIN. | |
* | |
* If _any_ files are written again, then the affected file need manual review. | |
* If `prettier` moves the comment around, you can use [// prettier-ignore](https://prettier.io/docs/en/ignore.html) to prevent re-ordering of the comment. | |
*/ | |
import MagicString from 'magic-string'; | |
import {readFile, writeFile} from 'node:fs/promises'; | |
import {relative, resolve} from 'node:path'; | |
import {createInterface} from 'node:readline'; | |
// This is a wrapper for `prettier.format(source, { parser: 'typescript', ...prettierConfig })` | |
import {format} from '../utils/format.js'; | |
// Because the command output is piped to this script, we don't know what the true CWD is. | |
// So, I assume it's relative **from this script** to the the **root of the monorepo**. | |
const root = resolve('../../'); | |
const lines = createInterface({input: process.stdin}); | |
/** @type {Map<string, string>} */ | |
const sources = new Map(); | |
/** @type {Map<string, MagicString>} */ | |
const updates = new Map(); | |
for await (const line of lines) { | |
const match = line.match( | |
/(?<file>^.*)\((?<row>[\d]+),(?<column>[\d]+)\): error (?<tscode>TS\d+): (?<description>.*$)/, | |
); | |
if (!match) { | |
console.debug( | |
'\x1b[0m', // reset | |
`⏩ ${line}`, | |
); | |
continue; | |
} | |
console.debug( | |
'\x1b[1m\x1b[33m', // bold+yellow | |
`⏺️ ${line}`, | |
); | |
const { | |
groups: {file, tscode, description}, | |
} = match; | |
const row = Number(match.groups.row); | |
const filepath = resolve(root, file); | |
if (!sources.has(filepath)) { | |
const source = await readFile(filepath, 'utf-8'); | |
sources.set(filepath, source); | |
updates.set(filepath, new MagicString(source)); | |
} | |
const newline = '\n'; | |
const source = sources.get(filepath); | |
const lines = source.split(newline).slice(0, row - 1); | |
const lastLine = lines.at(-1); | |
const rowIndex = lines.join(newline).length; | |
// 🐛 Multiple errors for a single line should generate 1 comment, not multiple. This is rare, but causes another TSC error. | |
updates.get(filepath).prependLeft( | |
rowIndex, | |
/<\w+/.test(lastLine) | |
? // Looks like we're in JSX, so prettier will format it without an explicit \n | |
`\n{/* @ts-expect-error ${tscode}: ${description} */}` | |
: // Probably some regular JS | |
`\n// @ts-expect-error ${tscode}: ${description}`, | |
); | |
} | |
for (const [filepath, s] of updates) { | |
if (!s.hasChanged()) { | |
continue; | |
} | |
console.debug( | |
'\x1b[0m', // reset | |
'🔀 Rewriting', | |
'\x1b[1m\x1b[32m', // bold+green | |
relative(root, filepath), | |
'\x1b[0m', // reset | |
); | |
await writeFile(filepath, format(s.toString(), {filepath}), 'utf-8'); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment