Skip to content

Instantly share code, notes, and snippets.

@rnag
Last active February 3, 2023 22:33
Show Gist options
  • Save rnag/0d8fe2e72dc7b48743c13f9ca8837a4c to your computer and use it in GitHub Desktop.
Save rnag/0d8fe2e72dc7b48743c13f9ca8837a4c to your computer and use it in GitHub Desktop.
TypeScript Compiler API - Example
// TypeScript code to programatically run the equivalent of `tsc`
//
// With Slight modifications, such as *Colorized* output being displayed.
//
// However, the nice one-line Error Summary that `tsc` gives you (e.g. Errors in X files) is currently missing.
//
// Refs:
// - https://github.com/microsoft/TypeScript-wiki/blob/main/Using-the-Compiler-API.md
// - https://github.com/Microsoft/TypeScript/issues/6387#issuecomment-169739615
import { readFileSync } from 'node:fs';
import { dirname } from 'node:path';
import { exit } from 'process';
import ts from 'typescript';
// TODO
const rootDir = '.';
const {
useCaseSensitiveFileNames,
// getCurrentDirectory,
newLine,
} = ts.sys;
const getCanonicalPath: (path: string) => string =
useCaseSensitiveFileNames ? (v) => v : (v) => v.toLowerCase();
function getNewLine(): string {
return newLine;
}
function reportDiagnostics(
diagnostics: ts.Diagnostic[],
curDir = rootDir
) {
if (diagnostics.length == 0) return;
const formatDiagnosticsHost: ts.FormatDiagnosticsHost = {
getCurrentDirectory: () => curDir,
getCanonicalFileName: getCanonicalPath,
getNewLine: getNewLine,
};
// De-duplicate diagnostics, as mentioned here:
// https://github.com/microsoft/TypeScript/issues/20876
const allDiagnostics =
ts.sortAndDeduplicateDiagnostics(diagnostics);
console.log(
ts.formatDiagnosticsWithColorAndContext(
allDiagnostics,
formatDiagnosticsHost
)
);
// allDiagnostics.forEach((diagnostic) => {
// if (diagnostic.file) {
// const { line, character } =
// diagnostic.file.getLineAndCharacterOfPosition(
// diagnostic.start!
// );
// const message = ts.flattenDiagnosticMessageText(
// diagnostic.messageText,
// '\n'
// );
// console.log(
// `${diagnostic.file.fileName} (${line + 1},${
// character + 1
// }): ${message}`
// );
// } else {
// console.log(
// ts.flattenDiagnosticMessageText(
// diagnostic.messageText,
// '\n'
// )
// );
// }
// });
}
function readConfigFile(configFileName: string) {
// Read config file
const configFileText = readFileSync(configFileName).toString();
// Parse JSON, after removing comments. Just fancier JSON.parse
const result = ts.parseConfigFileTextToJson(
configFileName,
configFileText
);
const configObject = result.config;
if (!configObject) {
reportDiagnostics([result.error!]);
exit(1);
}
// Extract config infromation
const configParseResult = ts.parseJsonConfigFileContent(
configObject,
ts.sys,
dirname(configFileName)
);
if (configParseResult.errors.length > 0) {
reportDiagnostics(configParseResult.errors);
exit(1);
}
return configParseResult;
}
const compile = (configFileName: string) => {
// Extract configuration from config file
const config = readConfigFile(configFileName);
// Compile
const program = ts.createProgram(
config.fileNames,
config.options
);
const emitResult = program.emit();
const allDiagnostics = ts
.getPreEmitDiagnostics(program)
.concat(emitResult.diagnostics);
// Report errors
reportDiagnostics(allDiagnostics);
// Check return code
if (emitResult.emitSkipped) {
const exitCode = 1;
console.log(`Process exiting with code '${exitCode}'.`);
exit(exitCode);
}
};
// TODO
compile(process.argv[2]);
// run with:
// npx ts-node ts-compile.ts tsconfig.json
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment