summaryrefslogtreecommitdiff
path: root/cli/js/compiler.ts
diff options
context:
space:
mode:
Diffstat (limited to 'cli/js/compiler.ts')
-rw-r--r--cli/js/compiler.ts520
1 files changed, 265 insertions, 255 deletions
diff --git a/cli/js/compiler.ts b/cli/js/compiler.ts
index 2807422e2..394c6cf52 100644
--- a/cli/js/compiler.ts
+++ b/cli/js/compiler.ts
@@ -1,12 +1,20 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
// TODO(ry) Combine this implementation with //deno_typescript/compiler_main.js
-// these are imported for their side effects
-import "./globals.ts";
+// This module is the entry point for "compiler" isolate, ie. the one
+// that is created when Deno needs to compile TS/WASM to JS.
+//
+// It provides a two functions that should be called by Rust:
+// - `bootstrapTsCompilerRuntime`
+// - `bootstrapWasmCompilerRuntime`
+// Either of these functions must be called when creating isolate
+// to properly setup runtime.
+
+// NOTE: this import has side effects!
import "./ts_global.d.ts";
import { TranspileOnlyResult } from "./compiler_api.ts";
-import { oldProgram } from "./compiler_bootstrap.ts";
+import { TS_SNAPSHOT_PROGRAM } from "./compiler_bootstrap.ts";
import { setRootExports } from "./compiler_bundler.ts";
import {
CompilerHostTarget,
@@ -30,16 +38,12 @@ import {
} from "./compiler_util.ts";
import { Diagnostic } from "./diagnostics.ts";
import { fromTypeScriptDiagnostic } from "./diagnostics_util.ts";
-import * as os from "./os.ts";
import { assert } from "./util.ts";
import * as util from "./util.ts";
import {
- postMessage,
- workerClose,
- bootstrapWorkerRuntime
-} from "./worker_main.ts";
-
-const self = globalThis;
+ bootstrapWorkerRuntime,
+ runWorkerMessageLoop
+} from "./runtime_worker.ts";
interface CompilerRequestCompile {
type: CompilerRequestType.Compile;
@@ -80,274 +84,280 @@ interface CompileResult {
diagnostics?: Diagnostic;
}
-// bootstrap the runtime environment, this gets called as the isolate is setup
-self.bootstrapCompilerRuntime = function bootstrapCompilerRuntime(
- compilerType: string
-): void {
- os.start(true, compilerType);
-};
-
-// bootstrap the worker environment, this gets called as the isolate is setup
-self.bootstrapWorkerRuntime = bootstrapWorkerRuntime;
-
-// provide the "main" function that will be called by the privileged side when
-// lazy instantiating the compiler web worker
-self.bootstrapTsCompiler = function tsCompilerMain(): void {
- // bootstrapWorkerRuntime should have already been called since a compiler is a worker.
- self.onmessage = async ({
- data: request
- }: {
- data: CompilerRequest;
- }): Promise<void> => {
- switch (request.type) {
- // `Compile` are requests from the internals to Deno, generated by both
- // the `run` and `bundle` sub command.
- case CompilerRequestType.Compile: {
- const {
- bundle,
- config,
- configPath,
- outFile,
- rootNames,
- target
- } = request;
- util.log(">>> compile start", {
+// TODO(bartlomieju): refactor this function into multiple functions
+// per CompilerRequestType
+async function tsCompilerOnMessage({
+ data: request
+}: {
+ data: CompilerRequest;
+}): Promise<void> {
+ switch (request.type) {
+ // `Compile` are requests from the internals to Deno, generated by both
+ // the `run` and `bundle` sub command.
+ case CompilerRequestType.Compile: {
+ const {
+ bundle,
+ config,
+ configPath,
+ outFile,
+ rootNames,
+ target
+ } = request;
+ util.log(">>> compile start", {
+ rootNames,
+ type: CompilerRequestType[request.type]
+ });
+
+ // This will recursively analyse all the code for other imports,
+ // requesting those from the privileged side, populating the in memory
+ // cache which will be used by the host, before resolving.
+ const resolvedRootModules = await processImports(
+ rootNames.map(rootName => [rootName, rootName])
+ );
+
+ // When a programme is emitted, TypeScript will call `writeFile` with
+ // each file that needs to be emitted. The Deno compiler host delegates
+ // this, to make it easier to perform the right actions, which vary
+ // based a lot on the request. For a `Compile` request, we need to
+ // cache all the files in the privileged side if we aren't bundling,
+ // and if we are bundling we need to enrich the bundle and either write
+ // out the bundle or log it to the console.
+ const state: WriteFileState = {
+ type: request.type,
+ bundle,
+ host: undefined,
+ outFile,
+ rootNames
+ };
+ const writeFile = createWriteFile(state);
+
+ const host = (state.host = new Host({
+ bundle,
+ target,
+ writeFile
+ }));
+ let diagnostics: readonly ts.Diagnostic[] | undefined;
+
+ // if there is a configuration supplied, we need to parse that
+ if (config && config.length && configPath) {
+ const configResult = host.configure(configPath, config);
+ diagnostics = processConfigureResponse(configResult, configPath);
+ }
+
+ let emitSkipped = true;
+ // if there was a configuration and no diagnostics with it, we will continue
+ // to generate the program and possibly emit it.
+ if (!diagnostics || (diagnostics && diagnostics.length === 0)) {
+ const options = host.getCompilationSettings();
+ const program = ts.createProgram({
rootNames,
- type: CompilerRequestType[request.type]
+ options,
+ host,
+ oldProgram: TS_SNAPSHOT_PROGRAM
});
- // This will recursively analyse all the code for other imports,
- // requesting those from the privileged side, populating the in memory
- // cache which will be used by the host, before resolving.
- const resolvedRootModules = await processImports(
- rootNames.map(rootName => [rootName, rootName])
- );
-
- // When a programme is emitted, TypeScript will call `writeFile` with
- // each file that needs to be emitted. The Deno compiler host delegates
- // this, to make it easier to perform the right actions, which vary
- // based a lot on the request. For a `Compile` request, we need to
- // cache all the files in the privileged side if we aren't bundling,
- // and if we are bundling we need to enrich the bundle and either write
- // out the bundle or log it to the console.
- const state: WriteFileState = {
- type: request.type,
- bundle,
- host: undefined,
- outFile,
- rootNames
- };
- const writeFile = createWriteFile(state);
-
- const host = (state.host = new Host({
- bundle,
- target,
- writeFile
- }));
- let diagnostics: readonly ts.Diagnostic[] | undefined;
-
- // if there is a configuration supplied, we need to parse that
- if (config && config.length && configPath) {
- const configResult = host.configure(configPath, config);
- diagnostics = processConfigureResponse(configResult, configPath);
- }
+ diagnostics = ts
+ .getPreEmitDiagnostics(program)
+ .filter(({ code }) => !ignoredDiagnostics.includes(code));
- let emitSkipped = true;
- // if there was a configuration and no diagnostics with it, we will continue
- // to generate the program and possibly emit it.
- if (!diagnostics || (diagnostics && diagnostics.length === 0)) {
- const options = host.getCompilationSettings();
- const program = ts.createProgram({
- rootNames,
- options,
- host,
- oldProgram
- });
-
- diagnostics = ts
- .getPreEmitDiagnostics(program)
- .filter(({ code }) => !ignoredDiagnostics.includes(code));
-
- // We will only proceed with the emit if there are no diagnostics.
- if (diagnostics && diagnostics.length === 0) {
- if (bundle) {
- // we only support a single root module when bundling
- assert(resolvedRootModules.length === 1);
- // warning so it goes to stderr instead of stdout
- console.warn(`Bundling "${resolvedRootModules[0]}"`);
- setRootExports(program, resolvedRootModules[0]);
- }
- const emitResult = program.emit();
- emitSkipped = emitResult.emitSkipped;
- // emitResult.diagnostics is `readonly` in TS3.5+ and can't be assigned
- // without casting.
- diagnostics = emitResult.diagnostics;
+ // We will only proceed with the emit if there are no diagnostics.
+ if (diagnostics && diagnostics.length === 0) {
+ if (bundle) {
+ // we only support a single root module when bundling
+ assert(resolvedRootModules.length === 1);
+ // warning so it goes to stderr instead of stdout
+ console.warn(`Bundling "${resolvedRootModules[0]}"`);
+ setRootExports(program, resolvedRootModules[0]);
}
+ const emitResult = program.emit();
+ emitSkipped = emitResult.emitSkipped;
+ // emitResult.diagnostics is `readonly` in TS3.5+ and can't be assigned
+ // without casting.
+ diagnostics = emitResult.diagnostics;
}
+ }
- const result: CompileResult = {
- emitSkipped,
- diagnostics: diagnostics.length
- ? fromTypeScriptDiagnostic(diagnostics)
- : undefined
- };
- postMessage(result);
-
- util.log("<<< compile end", {
- rootNames,
- type: CompilerRequestType[request.type]
- });
- break;
+ const result: CompileResult = {
+ emitSkipped,
+ diagnostics: diagnostics.length
+ ? fromTypeScriptDiagnostic(diagnostics)
+ : undefined
+ };
+ globalThis.postMessage(result);
+
+ util.log("<<< compile end", {
+ rootNames,
+ type: CompilerRequestType[request.type]
+ });
+ break;
+ }
+ case CompilerRequestType.RuntimeCompile: {
+ // `RuntimeCompile` are requests from a runtime user, both compiles and
+ // bundles. The process is similar to a request from the privileged
+ // side, but also returns the output to the on message.
+ const { rootName, sources, options, bundle, target } = request;
+
+ util.log(">>> runtime compile start", {
+ rootName,
+ bundle,
+ sources: sources ? Object.keys(sources) : undefined
+ });
+
+ const resolvedRootName = sources
+ ? rootName
+ : resolveModules([rootName])[0];
+
+ const rootNames = sources
+ ? processLocalImports(sources, [[resolvedRootName, resolvedRootName]])
+ : await processImports([[resolvedRootName, resolvedRootName]]);
+
+ const state: WriteFileState = {
+ type: request.type,
+ bundle,
+ host: undefined,
+ rootNames,
+ sources,
+ emitMap: {},
+ emitBundle: undefined
+ };
+ const writeFile = createWriteFile(state);
+
+ const host = (state.host = new Host({
+ bundle,
+ target,
+ writeFile
+ }));
+ const compilerOptions = [defaultRuntimeCompileOptions];
+ if (options) {
+ compilerOptions.push(convertCompilerOptions(options));
}
- case CompilerRequestType.RuntimeCompile: {
- // `RuntimeCompile` are requests from a runtime user, both compiles and
- // bundles. The process is similar to a request from the privileged
- // side, but also returns the output to the on message.
- const { rootName, sources, options, bundle, target } = request;
-
- util.log(">>> runtime compile start", {
- rootName,
- bundle,
- sources: sources ? Object.keys(sources) : undefined
- });
+ if (bundle) {
+ compilerOptions.push(defaultBundlerOptions);
+ }
+ host.mergeOptions(...compilerOptions);
- const resolvedRootName = sources
- ? rootName
- : resolveModules([rootName])[0];
+ const program = ts.createProgram({
+ rootNames,
+ options: host.getCompilationSettings(),
+ host,
+ oldProgram: TS_SNAPSHOT_PROGRAM
+ });
- const rootNames = sources
- ? processLocalImports(sources, [[resolvedRootName, resolvedRootName]])
- : await processImports([[resolvedRootName, resolvedRootName]]);
+ if (bundle) {
+ setRootExports(program, rootNames[0]);
+ }
- const state: WriteFileState = {
- type: request.type,
- bundle,
- host: undefined,
- rootNames,
- sources,
- emitMap: {},
- emitBundle: undefined
- };
- const writeFile = createWriteFile(state);
-
- const host = (state.host = new Host({
- bundle,
- target,
- writeFile
- }));
- const compilerOptions = [defaultRuntimeCompileOptions];
- if (options) {
- compilerOptions.push(convertCompilerOptions(options));
- }
- if (bundle) {
- compilerOptions.push(defaultBundlerOptions);
- }
- host.mergeOptions(...compilerOptions);
+ const diagnostics = ts
+ .getPreEmitDiagnostics(program)
+ .filter(({ code }) => !ignoredDiagnostics.includes(code));
- const program = ts.createProgram({
- rootNames,
- options: host.getCompilationSettings(),
- host,
- oldProgram
- });
+ const emitResult = program.emit();
- if (bundle) {
- setRootExports(program, rootNames[0]);
- }
+ assert(emitResult.emitSkipped === false, "Unexpected skip of the emit.");
+ const result = [
+ diagnostics.length ? fromTypeScriptDiagnostic(diagnostics) : undefined,
+ bundle ? state.emitBundle : state.emitMap
+ ];
+ globalThis.postMessage(result);
- const diagnostics = ts
- .getPreEmitDiagnostics(program)
- .filter(({ code }) => !ignoredDiagnostics.includes(code));
+ assert(state.emitMap);
+ util.log("<<< runtime compile finish", {
+ rootName,
+ sources: sources ? Object.keys(sources) : undefined,
+ bundle,
+ emitMap: Object.keys(state.emitMap)
+ });
- const emitResult = program.emit();
-
- assert(
- emitResult.emitSkipped === false,
- "Unexpected skip of the emit."
+ break;
+ }
+ case CompilerRequestType.RuntimeTranspile: {
+ const result: Record<string, TranspileOnlyResult> = {};
+ const { sources, options } = request;
+ const compilerOptions = options
+ ? Object.assign(
+ {},
+ defaultTranspileOptions,
+ convertCompilerOptions(options)
+ )
+ : defaultTranspileOptions;
+
+ for (const [fileName, inputText] of Object.entries(sources)) {
+ const { outputText: source, sourceMapText: map } = ts.transpileModule(
+ inputText,
+ {
+ fileName,
+ compilerOptions
+ }
);
- const result = [
- diagnostics.length
- ? fromTypeScriptDiagnostic(diagnostics)
- : undefined,
- bundle ? state.emitBundle : state.emitMap
- ];
- postMessage(result);
-
- assert(state.emitMap);
- util.log("<<< runtime compile finish", {
- rootName,
- sources: sources ? Object.keys(sources) : undefined,
- bundle,
- emitMap: Object.keys(state.emitMap)
- });
-
- break;
+ result[fileName] = { source, map };
}
- case CompilerRequestType.RuntimeTranspile: {
- const result: Record<string, TranspileOnlyResult> = {};
- const { sources, options } = request;
- const compilerOptions = options
- ? Object.assign(
- {},
- defaultTranspileOptions,
- convertCompilerOptions(options)
- )
- : defaultTranspileOptions;
-
- for (const [fileName, inputText] of Object.entries(sources)) {
- const { outputText: source, sourceMapText: map } = ts.transpileModule(
- inputText,
- {
- fileName,
- compilerOptions
- }
- );
- result[fileName] = { source, map };
- }
- postMessage(result);
+ globalThis.postMessage(result);
- break;
- }
- default:
- util.log(
- `!!! unhandled CompilerRequestType: ${
- (request as CompilerRequest).type
- } (${CompilerRequestType[(request as CompilerRequest).type]})`
- );
+ break;
}
+ default:
+ util.log(
+ `!!! unhandled CompilerRequestType: ${
+ (request as CompilerRequest).type
+ } (${CompilerRequestType[(request as CompilerRequest).type]})`
+ );
+ }
+
+ // The compiler isolate exits after a single message.
+ globalThis.workerClose();
+}
+
+async function wasmCompilerOnMessage({
+ data: binary
+}: {
+ data: string;
+}): Promise<void> {
+ const buffer = util.base64ToUint8Array(binary);
+ // @ts-ignore
+ const compiled = await WebAssembly.compile(buffer);
+
+ util.log(">>> WASM compile start");
- // The compiler isolate exits after a single message.
- workerClose();
- };
-};
-
-self.bootstrapWasmCompiler = function wasmCompilerMain(): void {
- // bootstrapWorkerRuntime should have already been called since a compiler is a worker.
- self.onmessage = async ({
- data: binary
- }: {
- data: string;
- }): Promise<void> => {
- const buffer = util.base64ToUint8Array(binary);
+ const importList = Array.from(
// @ts-ignore
- const compiled = await WebAssembly.compile(buffer);
+ new Set(WebAssembly.Module.imports(compiled).map(({ module }) => module))
+ );
+ const exportList = Array.from(
+ // @ts-ignore
+ new Set(WebAssembly.Module.exports(compiled).map(({ name }) => name))
+ );
- util.log(">>> WASM compile start");
+ globalThis.postMessage({ importList, exportList });
- const importList = Array.from(
- // @ts-ignore
- new Set(WebAssembly.Module.imports(compiled).map(({ module }) => module))
- );
- const exportList = Array.from(
- // @ts-ignore
- new Set(WebAssembly.Module.exports(compiled).map(({ name }) => name))
- );
+ util.log("<<< WASM compile end");
- postMessage({ importList, exportList });
+ // The compiler isolate exits after a single message.
+ globalThis.workerClose();
+}
- util.log("<<< WASM compile end");
+function bootstrapTsCompilerRuntime(): void {
+ bootstrapWorkerRuntime("TS");
+ globalThis.onmessage = tsCompilerOnMessage;
+ runWorkerMessageLoop();
+}
+
+function bootstrapWasmCompilerRuntime(): void {
+ bootstrapWorkerRuntime("WASM");
+ globalThis.onmessage = wasmCompilerOnMessage;
+ runWorkerMessageLoop();
+}
- // The compiler isolate exits after a single message.
- workerClose();
- };
-};
+Object.defineProperties(globalThis, {
+ bootstrapWasmCompilerRuntime: {
+ value: bootstrapWasmCompilerRuntime,
+ enumerable: false,
+ writable: false,
+ configurable: false
+ },
+ bootstrapTsCompilerRuntime: {
+ value: bootstrapTsCompilerRuntime,
+ enumerable: false,
+ writable: false,
+ configurable: false
+ }
+});