Skip to content

Instantly share code, notes, and snippets.

@ashleydavis
Created April 30, 2020 08:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ashleydavis/0a9e03ec5f4e4f9ed4ab8c0d6b17154b to your computer and use it in GitHub Desktop.
Save ashleydavis/0a9e03ec5f4e4f9ed4ab8c0d6b17154b to your computer and use it in GitHub Desktop.
An example of using the TypeScript compiler from TypeScript
import * as path from "path";
import { IDiagnostic } from "./language-code-generator";
import { readJsonFile } from "../common/file";
//
// Result of compiling TypeScript code.
//
export interface ICompilationResult {
code?: string;
diagnostics: IDiagnostic[];
sourceMap?: any;
};
//
// Convert a TS diagnostic.
//
function convertDiagnostic(diagnostic: any, ts: any) {
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
const file = diagnostic.file!;
if (!file) {
return {
message: message,
source: "TypeScript",
};
}
let { line, character } = file.getLineAndCharacterOfPosition(diagnostic.start!);
return {
message: message,
location: {
fileName: file.fileName,
lineNumber: line + 1,
},
source: "TypeScript",
};
}
//
// Check and compile in-memory TypeScript code for errors.
//
export async function compileTypeScriptCode(code: string, projectPath: string, libs: string[], tsModule?: any): Promise<ICompilationResult> {
const ts = tsModule || require(path.join(projectPath, "node_modules/typescript"));
const tsConfigFilePath = path.join(projectPath, "tsconfig.json");
const tsConfigFile = await readJsonFile(tsConfigFilePath);
const parsedOptions = ts.convertCompilerOptionsFromJson(tsConfigFile.compilerOptions, "");
if (parsedOptions.errors && parsedOptions.errors.length > 0) {
return {
diagnostics: parsedOptions.errors
.map((diagnostic: any) => convertDiagnostic(diagnostic, ts)),
};
}
const tsConfig = parsedOptions.options;
tsConfig.sourceMap = true;
tsConfig.inlineSourceMap = false;
const realHost = ts.createCompilerHost(tsConfig, true);
const baseDummyFilePath = path.join(projectPath, tsConfig.rootDir, "in-memory-file").replace(/\\/g, '/');
const dummyFilePath = baseDummyFilePath + ".ts";
const outputFilePath = path.join(tsConfig.outDir, "in-memory-file.js").replace(/\\/g, '/');
const dummySourceFile = ts.createSourceFile(dummyFilePath, code, ts.ScriptTarget.ES5);
let outputCode: string | undefined = undefined;
let sourceMap: any | undefined = undefined;
const host = {
fileExists: (filePath: any) => filePath === dummyFilePath || realHost.fileExists(filePath),
directoryExists: realHost.directoryExists && realHost.directoryExists.bind(realHost),
getCurrentDirectory: () => projectPath,
getDirectories: realHost.getDirectories && realHost.getDirectories.bind(realHost),
getCanonicalFileName: realHost.getCanonicalFileName.bind(realHost),
getNewLine: realHost.getNewLine.bind(realHost),
getDefaultLibFileName: realHost.getDefaultLibFileName.bind(realHost),
getSourceFile: (fileName: any, languageVersion: any, onError: any, shouldCreateNewSourceFile: any) => fileName === dummyFilePath
? dummySourceFile
: realHost.getSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile),
readFile: (filePath: any) => filePath === dummyFilePath
? code
: realHost.readFile(filePath),
useCaseSensitiveFileNames: realHost.useCaseSensitiveFileNames.bind(realHost),
writeFile: (fileName: any, data: any) => {
if (fileName === outputFilePath) {
outputCode = data;
}
else if (fileName === outputFilePath + ".map") {
sourceMap = JSON.parse(data);
}
}
};
const program = ts.createProgram([dummyFilePath], tsConfig, host);
const emitResult = program.emit();
const diagnostics = ts.getPreEmitDiagnostics(program);
return {
code: outputCode,
diagnostics: emitResult.diagnostics
.concat(diagnostics)
.map((diagnostic: any) => convertDiagnostic(diagnostic, ts)),
sourceMap,
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment