"use strict";
const ASSETS = "$asset$";
function main(configText, rootNames) {
ops = Deno.core.ops();
println(`>>> ts version ${ts.version}`);
println(`>>> rootNames ${rootNames}`);
const host = new Host();
assert(rootNames.length === 1);
const rootFile = rootNames[0];
const result = externalSpecifierRegEx.exec(rootFile);
let rootSpecifier = rootFile;
if (result) {
const [, specifier] = result;
const internalSpecifier = `$deno$${specifier}`;
moduleMap.set(internalSpecifier, rootFile);
rootSpecifier = internalSpecifier;
}
const { options, diagnostics } = configure(configText);
handleDiagnostics(host, diagnostics);
println(`>>> TS config: ${JSON.stringify(options)}`);
const program = ts.createProgram([rootSpecifier], options, host);
handleDiagnostics(
host,
ts.getPreEmitDiagnostics(program).filter(({ code }) => {
if (code === 1063) return false;
if (code === 2691) return false;
if (code === 5009) return false;
return true;
})
);
const emitResult = program.emit();
handleDiagnostics(host, emitResult.diagnostics);
dispatch(
"op_set_emit_result",
Object.assign(emitResult, { tsVersion: ts.version })
);
}
function println(...s) {
Deno.core.print(s.join(" ") + "\n");
}
function unreachable() {
throw Error("unreachable");
}
function assert(cond) {
if (!cond) {
throw Error("assert");
}
}
function decodeAscii(ui8) {
let out = "";
if (!ui8) {
return out;
}
for (let i = 0; i < ui8.length; i++) {
out += String.fromCharCode(ui8[i]);
}
return out;
}
function encode(str) {
const charCodes = str.split("").map((c) => c.charCodeAt(0));
const ui8 = new Uint8Array(charCodes);
return ui8;
}
let ops;
const moduleMap = new Map();
const externalSpecifierRegEx = /^file:\/{3}\S+\/js(\/\S+\.ts)$/;
class Host {
fileExists(_fileName) {
return true;
}
readFile(_fileName) {
unreachable();
}
useCaseSensitiveFileNames() {
return false;
}
getDefaultLibFileName(_options) {
return "lib.esnext.d.ts";
}
getDefaultLibLocation() {
return ASSETS;
}
getCurrentDirectory() {
return ".";
}
getSourceFile(
fileName,
languageVersion,
_onError,
shouldCreateNewSourceFile
) {
assert(!shouldCreateNewSourceFile);
if (fileName.startsWith("$typeRoots$")) {
assert(fileName.startsWith("$typeRoots$/"));
assert(fileName.endsWith("/index.d.ts"));
fileName = fileName
.replace("$typeRoots$/", "")
.replace("/index.d.ts", "");
}
const moduleUrl = moduleMap.has(fileName)
? moduleMap.get(fileName)
: fileName;
const { sourceCode } = dispatch("op_load_module", {
moduleUrl,
languageVersion,
shouldCreateNewSourceFile,
});
const sourceFile = ts.createSourceFile(
fileName,
sourceCode,
languageVersion
);
sourceFile.moduleName = fileName;
return sourceFile;
}
writeFile(
fileName,
data,
_writeByteOrderMark,
_onError = null,
sourceFiles = null
) {
if (sourceFiles == null) {
return;
}
const moduleName = sourceFiles[sourceFiles.length - 1].moduleName;
return dispatch("op_write_file", { fileName, moduleName, data });
}
getSourceFileByPath(
_fileName,
_path,
_languageVersion,
_onError,
_shouldCreateNewSourceFile
) {
unreachable();
}
getCanonicalFileName(fileName) {
return fileName;
}
getNewLine() {
return "\n";
}
resolveModuleNames(moduleNames, containingFile) {
containingFile = moduleMap.has(containingFile)
? moduleMap.get(containingFile)
: containingFile;
const resolvedNames = dispatch("op_resolve_module_names", {
moduleNames,
containingFile,
});
const r = resolvedNames.map((resolvedFileName) => {
const extension = getExtension(resolvedFileName);
if (!moduleMap.has(resolvedFileName)) {
const result = externalSpecifierRegEx.exec(resolvedFileName);
if (result) {
const [, specifier] = result;
const internalSpecifier = `$deno$${specifier}`;
moduleMap.set(internalSpecifier, resolvedFileName);
resolvedFileName = internalSpecifier;
}
}
return { resolvedFileName, extension };
});
return r;
}
}
function configure(configurationText) {
const { config, error } = ts.parseConfigFileTextToJson(
"tsconfig.json",
configurationText
);
if (error) {
return { options: {}, diagnostics: [error] };
}
const { options, errors } = ts.convertCompilerOptionsFromJson(
config.compilerOptions,
""
);
return {
options,
diagnostics: errors.length ? errors : undefined,
};
}
function dispatch(opName, obj) {
const opId = ops[opName];
if (!opId) {
throw new Error(`Unknown op: ${opName}`);
}
const s = JSON.stringify(obj);
const msg = encode(s);
const resUi8 = Deno.core.dispatch(opId, msg);
const resStr = decodeAscii(resUi8);
const res = JSON.parse(resStr);
if (!res["ok"]) {
throw Error(`${opName} failed ${res["err"]}. Args: ${JSON.stringify(obj)}`);
}
return res["ok"];
}
function exit(code) {
dispatch("op_exit2", { code });
return unreachable();
}
const MAX_ERRORS = 5;
function handleDiagnostics(host, diagnostics) {
if (diagnostics && diagnostics.length) {
let rest = 0;
if (diagnostics.length > MAX_ERRORS) {
rest = diagnostics.length - MAX_ERRORS;
diagnostics = diagnostics.slice(0, MAX_ERRORS);
}
const msg = ts.formatDiagnosticsWithColorAndContext(diagnostics, host);
println(msg);
if (rest) {
println(`And ${rest} other errors.`);
}
exit(1);
}
}
function getExtension(fileName) {
if (fileName.endsWith(".d.ts")) {
return ts.Extension.Dts;
} else if (fileName.endsWith(".ts")) {
return ts.Extension.Ts;
} else if (fileName.endsWith(".js")) {
return ts.Extension.Js;
} else {
throw TypeError(`Cannot resolve extension for ${fileName}`);
}
}