summaryrefslogtreecommitdiff
path: root/deno_typescript/compiler_main.js
diff options
context:
space:
mode:
Diffstat (limited to 'deno_typescript/compiler_main.js')
-rw-r--r--deno_typescript/compiler_main.js320
1 files changed, 320 insertions, 0 deletions
diff --git a/deno_typescript/compiler_main.js b/deno_typescript/compiler_main.js
new file mode 100644
index 000000000..74b6a8dd5
--- /dev/null
+++ b/deno_typescript/compiler_main.js
@@ -0,0 +1,320 @@
+// Because we're bootstrapping the TS compiler without dependencies on Node,
+// this is written in JS.
+
+const ASSETS = "$asset$";
+
+let replacements;
+
+function main(configText, rootNames, replacements_) {
+ println(`>>> ts version ${ts.version}`);
+ println(`>>> rootNames ${rootNames}`);
+
+ replacements = replacements_;
+ replacements["DENO_REPLACE_TS_VERSION"] = ts.version;
+ println(`>>> replacements ${JSON.stringify(replacements)}`);
+
+ const host = new Host();
+
+ assert(rootNames.length > 0);
+
+ let { options, diagnostics } = configure(configText);
+ handleDiagnostics(host, diagnostics);
+
+ println(`>>> TS config: ${JSON.stringify(options)}`);
+
+ const program = ts.createProgram(rootNames, options, host);
+
+ diagnostics = ts.getPreEmitDiagnostics(program).filter(({ code }) => {
+ // TS2691: An import path cannot end with a '.ts' extension. Consider
+ // importing 'bad-module' instead.
+ if (code === 2691) return false;
+ // TS5009: Cannot find the common subdirectory path for the input files.
+ if (code === 5009) return false;
+ return true;
+ });
+ handleDiagnostics(host, diagnostics);
+
+ const emitResult = program.emit();
+ handleDiagnostics(host, emitResult.diagnostics);
+
+ dispatch("setEmitResult", emitResult);
+}
+
+function println(...s) {
+ Deno.core.print(s.join(" ") + "\n");
+}
+
+function unreachable() {
+ throw Error("unreachable");
+}
+
+function assert(cond) {
+ if (!cond) {
+ throw Error("assert");
+ }
+}
+
+// decode(Uint8Array): string
+function decodeAscii(ui8) {
+ let 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;
+}
+
+// Warning! The op_id values below are shared between this code and
+// the Rust side. Update with care!
+const ops = {
+ readFile: 49,
+ exit: 50,
+ writeFile: 51,
+ resolveModuleNames: 52,
+ setEmitResult: 53
+};
+
+// interface CompilerHost extends ModuleResolutionHost {
+class Host {
+ // fileExists(fileName: string): boolean;
+ fileExists(fileName) {
+ return true;
+ }
+
+ // readFile(fileName: string): string | undefined;
+ readFile() {
+ unreachable();
+ }
+
+ // trace?(s: string): void;
+ // directoryExists?(directoryName: string): boolean;
+ // realpath?(path: string): string;
+ // getCurrentDirectory?(): string;
+ // getDirectories?(path: string): string[];
+
+ // useCaseSensitiveFileNames(): boolean;
+ useCaseSensitiveFileNames() {
+ return false;
+ }
+
+ // getDefaultLibFileName(options: CompilerOptions): string;
+ getDefaultLibFileName(options) {
+ return "lib.deno_core.d.ts";
+ }
+
+ // getDefaultLibLocation?(): string;
+ getDefaultLibLocation() {
+ return ASSETS;
+ }
+
+ // getCurrentDirectory(): string;
+ getCurrentDirectory() {
+ return ".";
+ }
+
+ // getCanonicalFileName(fileName: string): string
+ getCanonicalFileName(fileName) {
+ unreachable();
+ }
+
+ // getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?:
+ // (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile
+ // | undefined;
+ getSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile) {
+ assert(!shouldCreateNewSourceFile); // We haven't yet encountered this.
+
+ // This hacks around the fact that TypeScript tries to magically guess the
+ // d.ts filename.
+ if (fileName.startsWith("$typeRoots$")) {
+ assert(fileName.startsWith("$typeRoots$/"));
+ assert(fileName.endsWith("/index.d.ts"));
+ fileName = fileName
+ .replace("$typeRoots$/", "")
+ .replace("/index.d.ts", "");
+ }
+
+ let { sourceCode, moduleName } = dispatch("readFile", {
+ fileName,
+ languageVersion,
+ shouldCreateNewSourceFile
+ });
+
+ // TODO(ry) A terrible hack. Please remove ASAP.
+ if (fileName.endsWith("typescript.d.ts")) {
+ sourceCode = sourceCode.replace("export = ts;", "");
+ }
+
+ // TODO(ry) A terrible hack. Please remove ASAP.
+ for (let key of Object.keys(replacements)) {
+ let val = replacements[key];
+ sourceCode = sourceCode.replace(key, val);
+ }
+
+ let sourceFile = ts.createSourceFile(fileName, sourceCode, languageVersion);
+ sourceFile.moduleName = moduleName;
+ return sourceFile;
+ }
+
+ /*
+ writeFile(
+ fileName: string,
+ data: string,
+ writeByteOrderMark: boolean,
+ onError?: (message: string) => void,
+ sourceFiles?: ReadonlyArray<ts.SourceFile>
+ ): void
+ */
+ writeFile(
+ fileName,
+ data,
+ writeByteOrderMark,
+ onError = null,
+ sourceFiles = null
+ ) {
+ const moduleName = sourceFiles[sourceFiles.length - 1].moduleName;
+ return dispatch("writeFile", { fileName, moduleName, data });
+ }
+
+ // getSourceFileByPath?(fileName: string, path: Path, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined;
+ getSourceFileByPath(
+ fileName,
+ path,
+ languageVersion,
+ onError,
+ shouldCreateNewSourceFile
+ ) {
+ unreachable();
+ }
+
+ // getCancellationToken?(): CancellationToken;
+ getCancellationToken() {
+ unreachable();
+ }
+
+ // getCanonicalFileName(fileName: string): string;
+ getCanonicalFileName(fileName) {
+ return fileName;
+ }
+
+ // getNewLine(): string
+ getNewLine() {
+ return "\n";
+ }
+
+ // readDirectory?(rootDir: string, extensions: ReadonlyArray<string>, excludes: ReadonlyArray<string> | undefined, includes: ReadonlyArray<string>, depth?: number): string[];
+ readDirectory() {
+ unreachable();
+ }
+
+ // resolveModuleNames?(
+ // moduleNames: string[],
+ // containingFile: string,
+ // reusedNames?: string[],
+ // redirectedReference?: ResolvedProjectReference
+ // ): (ResolvedModule | undefined)[];
+ resolveModuleNames(moduleNames, containingFile) {
+ const resolvedNames = dispatch("resolveModuleNames", {
+ moduleNames,
+ containingFile
+ });
+ const r = resolvedNames.map(resolvedFileName => {
+ const extension = getExtension(resolvedFileName);
+ return { resolvedFileName, extension };
+ });
+ return r;
+ }
+
+ // resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference): (ResolvedTypeReferenceDirective | undefined)[];
+ /*
+ resolveTypeReferenceDirectives() {
+ unreachable();
+ }
+ */
+
+ // getEnvironmentVariable?(name: string): string | undefined;
+ getEnvironmentVariable() {
+ unreachable();
+ }
+
+ // createHash?(data: string): string;
+ createHash() {
+ unreachable();
+ }
+
+ // getParsedCommandLine?(fileName: string): ParsedCommandLine | undefined;
+ getParsedCommandLine() {
+ unreachable();
+ }
+}
+
+function configure(configurationText) {
+ const { config, error } = ts.parseConfigFileTextToJson(
+ "tsconfig.json",
+ configurationText
+ );
+ if (error) {
+ return { diagnostics: [error] };
+ }
+ const { options, errors } = ts.convertCompilerOptionsFromJson(
+ config.compilerOptions,
+ ""
+ );
+ return {
+ options,
+ diagnostics: errors.length ? errors : undefined
+ };
+}
+
+function dispatch(opName, obj) {
+ const s = JSON.stringify(obj);
+ const msg = encode(s);
+ const resUi8 = Deno.core.dispatch(ops[opName], 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("exit", { code });
+ unreachable();
+}
+
+// Maximum number of diagnostics to display.
+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);
+ }
+}
+
+/** Returns the TypeScript Extension enum for a given media type. */
+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}`);
+ }
+}