Skip to content

Commit

Permalink
[WIP] Transpile only and options
Browse files Browse the repository at this point in the history
  • Loading branch information
kitsonk committed Dec 6, 2019
1 parent b75d30c commit e64ecdf
Show file tree
Hide file tree
Showing 12 changed files with 393 additions and 109 deletions.
1 change: 1 addition & 0 deletions cli/compilers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod wasm;
pub use js::JsCompiler;
pub use json::JsonCompiler;
pub use ts::runtime_compile_async;
pub use ts::runtime_transpile_async;
pub use ts::TsCompiler;
pub use wasm::WasmCompiler;

Expand Down
28 changes: 28 additions & 0 deletions cli/compilers/ts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,34 @@ pub fn runtime_compile_async(
.boxed()
}

pub fn runtime_transpile_async(
global_state: ThreadSafeGlobalState,
sources: &HashMap<String, String>,
options: &Option<String>,
) -> Pin<Box<CompilationResultFuture>> {
let req_msg = json!({
"type": msg::CompilerRequestType::RuntimeTranspile as i32,
"sources": sources,
"options": options,
})
.to_string()
.into_boxed_str()
.into_boxed_bytes();

let worker = TsCompiler::setup_worker(global_state.clone());
let worker_ = worker.clone();

async move {
worker.post_message(req_msg).await?;
worker.await?;
debug!("Sent message to worker");
let msg = (worker_.get_message().await?).unwrap();
let json_str = std::str::from_utf8(&msg).unwrap();
Ok(json!(json_str))
}
.boxed()
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
201 changes: 173 additions & 28 deletions cli/js/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import "./ts_global.d.ts";

import { emitBundle, setRootExports } from "./bundler.ts";
import { bold, cyan, yellow } from "./colors.ts";
import { CompilerOptions } from "./compiler_api.ts";
import { CompilerOptions, TranspileOnlyResult } from "./compiler_api.ts";
import { Console } from "./console.ts";
import { core } from "./core.ts";
import { Diagnostic, DiagnosticItem } from "./diagnostics.ts";
import { Diagnostic } from "./diagnostics.ts";
import { fromTypeScriptDiagnostic } from "./diagnostics_util.ts";
import { cwd } from "./dir.ts";
import * as dispatch from "./dispatch.ts";
Expand Down Expand Up @@ -68,14 +68,21 @@ interface CompilerRequestRuntimeCompile {
rootName: string;
sources?: Record<string, string>;
bundle: boolean;
options?: CompilerOptions;
options?: string;
}

interface CompilerRequestRuntimeTranspile {
type: CompilerRequestType.RuntimeTranspile;
sources: Record<string, string>;
options?: string;
}

/** The format of the work message payload coming from the privileged side */
type CompilerRequest =
| CompilerRequestCompile
| CompilerRequestBundle
| CompilerRequestRuntimeCompile;
| CompilerRequestRuntimeCompile
| CompilerRequestRuntimeTranspile;

interface ConfigureResponse {
ignoredOptions?: string[];
Expand All @@ -87,8 +94,6 @@ interface EmitResult {
diagnostics?: Diagnostic;
}

type CompilationResult = [DiagnosticItem[] | undefined, Record<string, string>];

// Startup boilerplate. This is necessary because the compiler has its own
// snapshot. (It would be great if we could remove these things or centralize
// them somewhere else.)
Expand Down Expand Up @@ -167,6 +172,116 @@ const ignoredCompilerOptions: readonly string[] = [
"watch"
];

/** Default options used by the compiler Host when compiling. */
const defaultCompileOptions: ts.CompilerOptions = {
allowJs: true,
allowNonTsExtensions: true,
// TODO(#3324) Enable strict mode for user code.
// strict: true,
checkJs: false,
esModuleInterop: true,
module: ts.ModuleKind.ESNext,
outDir: OUT_DIR,
resolveJsonModule: true,
sourceMap: true,
stripComments: true,
target: ts.ScriptTarget.ESNext,
jsx: ts.JsxEmit.React
};

/** Default options used when doing a transpile only. */
const defaultTranspileOptions: ts.CompilerOptions = {
esModuleInterop: true,
module: ts.ModuleKind.ESNext,
sourceMap: true,
scriptComments: true,
target: ts.ScriptTarget.ESNext
};

function convertCompilerOptions(str: string): ts.CompilerOptions {
const options: CompilerOptions = JSON.parse(str);
const out: Record<string, unknown> = {};
const keys = Object.keys(options) as Array<keyof CompilerOptions>;
for (const key of keys) {
switch (key) {
case "jsx":
const value = options[key];
if (value === "preserve") {
out[key] = ts.JsxEmit.Preserve;
} else if (value === "react") {
out[key] = ts.JsxEmit.React;
} else {
out[key] = ts.JsxEmit.ReactNative;
}
break;
case "module":
switch (options[key]) {
case "amd":
out[key] = ts.ModuleKind.AMD;
break;
case "commonjs":
out[key] = ts.ModuleKind.CommonJS;
break;
case "es2015":
case "es6":
out[key] = ts.ModuleKind.ES2015;
break;
case "esnext":
out[key] = ts.ModuleKind.ESNext;
break;
case "none":
out[key] = ts.ModuleKind.None;
break;
case "system":
out[key] = ts.ModuleKind.System;
break;
case "umd":
out[key] = ts.ModuleKind.UMD;
break;
default:
throw new TypeError("Unexpected module type");
}
break;
case "target":
switch (options[key]) {
case "es3":
out[key] = ts.ScriptTarget.ES3;
break;
case "es5":
out[key] = ts.ScriptTarget.ES5;
break;
case "es6":
case "es2015":
out[key] = ts.ScriptTarget.ES2015;
break;
case "es2016":
out[key] = ts.ScriptTarget.ES2016;
break;
case "es2017":
out[key] = ts.ScriptTarget.ES2017;
break;
case "es2018":
out[key] = ts.ScriptTarget.ES2018;
break;
case "es2019":
out[key] = ts.ScriptTarget.ES2019;
break;
case "es2020":
out[key] = ts.ScriptTarget.ES2020;
break;
case "esnext":
out[key] = ts.ScriptTarget.ESNext;
break;
default:
throw new TypeError("Unexpected emit target.");
}
default:
out[key] = options[key];
}
}
return out as ts.CompilerOptions;
}

/** An array of TypeScript diagnostic types we ignore. */
const ignoredDiagnostics = [
// TS1103: 'for-await-of' statement is only allowed within an async function
Expand Down Expand Up @@ -312,6 +427,11 @@ class SourceFile {
}
}

function resolveModules(specifiers: string[], referrer?: string): string[] {
util.log("compiler::resolveModules", { specifiers, referrer });
return sendSync(dispatch.OP_RESOLVE_MODULES, { specifiers, referrer });
}

/** Ops to Rust to resolve special static assets. */
function fetchAsset(name: string): string {
return sendSync(dispatch.OP_FETCH_ASSET, { name });
Expand Down Expand Up @@ -400,7 +520,8 @@ async function processImports(
return [];
}
const sources = specifiers.map(([, moduleSpecifier]) => moduleSpecifier);
const sourceFiles = await fetchSourceFiles(sources, referrer);
const resolveSources = resolveModules(sources, referrer);
const sourceFiles = await fetchSourceFiles(resolveSources, referrer);
assert(sourceFiles.length === specifiers.length);
for (let i = 0; i < sourceFiles.length; i++) {
const sourceFileJson = sourceFiles[i];
Expand Down Expand Up @@ -457,21 +578,7 @@ class Host implements ts.CompilerHost {
private _requestType: CompilerRequestType;
private _rootNames: string[];

private readonly _options: ts.CompilerOptions = {
allowJs: true,
allowNonTsExtensions: true,
// TODO(#3324) Enable strict mode for user code.
// strict: true,
checkJs: false,
esModuleInterop: true,
module: ts.ModuleKind.ESNext,
outDir: OUT_DIR,
resolveJsonModule: true,
sourceMap: true,
stripComments: true,
target: ts.ScriptTarget.ESNext,
jsx: ts.JsxEmit.React
};
private readonly _options = defaultCompileOptions;

private _getAsset(filename: string): SourceFile {
const sourceFile = SourceFile.get(filename);
Expand Down Expand Up @@ -804,16 +911,20 @@ self.compilerMain = function compilerMain(): void {
break;
}
case CompilerRequestType.RuntimeCompile: {
const { rootName, sources } = request;
const { rootName, sources, options } = request;

util.log(">>> runtime compile start", {
rootName,
sources: sources ? Object.keys(sources) : undefined
});

const resolvedRootName = sources
? rootName
: resolveModules([rootName])[0];

const rootNames = sources
? processLocalImports(sources, [[rootName, rootName]])
: await processImports([[rootName, rootName]]);
? processLocalImports(sources, [[resolvedRootName, resolvedRootName]])
: await processImports([[resolvedRootName, resolvedRootName]]);

const emitMap: Record<string, string> = {};

Expand All @@ -826,10 +937,20 @@ self.compilerMain = function compilerMain(): void {
cache: sources ? false : true
});

host.configure({ outDir: undefined });
const compilerOptions = options
? Object.assign(
{},
{ outDir: undefined },
convertCompilerOptions(options)
)
: { outDir: undefined };
host.configure(compilerOptions);

const options = host.getCompilationSettings();
const program = ts.createProgram(rootNames, options, host);
const program = ts.createProgram(
rootNames,
host.getCompilationSettings(),
host
);

const diagnostics = ts
.getPreEmitDiagnostics(program)
Expand All @@ -852,6 +973,30 @@ self.compilerMain = function compilerMain(): void {

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
}
);
result[fileName] = { source, map };
postMessage(result);
}

break;
}
default:
util.log(
`!!! unhandled CompilerRequestType: ${
Expand Down
Loading

0 comments on commit e64ecdf

Please sign in to comment.