Skip to content

Commit e64ecdf

Browse files
committed
[WIP] Transpile only and options
1 parent b75d30c commit e64ecdf

12 files changed

+393
-109
lines changed

cli/compilers/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ mod wasm;
1111
pub use js::JsCompiler;
1212
pub use json::JsonCompiler;
1313
pub use ts::runtime_compile_async;
14+
pub use ts::runtime_transpile_async;
1415
pub use ts::TsCompiler;
1516
pub use wasm::WasmCompiler;
1617

cli/compilers/ts.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,34 @@ pub fn runtime_compile_async(
639639
.boxed()
640640
}
641641

642+
pub fn runtime_transpile_async(
643+
global_state: ThreadSafeGlobalState,
644+
sources: &HashMap<String, String>,
645+
options: &Option<String>,
646+
) -> Pin<Box<CompilationResultFuture>> {
647+
let req_msg = json!({
648+
"type": msg::CompilerRequestType::RuntimeTranspile as i32,
649+
"sources": sources,
650+
"options": options,
651+
})
652+
.to_string()
653+
.into_boxed_str()
654+
.into_boxed_bytes();
655+
656+
let worker = TsCompiler::setup_worker(global_state.clone());
657+
let worker_ = worker.clone();
658+
659+
async move {
660+
worker.post_message(req_msg).await?;
661+
worker.await?;
662+
debug!("Sent message to worker");
663+
let msg = (worker_.get_message().await?).unwrap();
664+
let json_str = std::str::from_utf8(&msg).unwrap();
665+
Ok(json!(json_str))
666+
}
667+
.boxed()
668+
}
669+
642670
#[cfg(test)]
643671
mod tests {
644672
use super::*;

cli/js/compiler.ts

Lines changed: 173 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import "./ts_global.d.ts";
88

99
import { emitBundle, setRootExports } from "./bundler.ts";
1010
import { bold, cyan, yellow } from "./colors.ts";
11-
import { CompilerOptions } from "./compiler_api.ts";
11+
import { CompilerOptions, TranspileOnlyResult } from "./compiler_api.ts";
1212
import { Console } from "./console.ts";
1313
import { core } from "./core.ts";
14-
import { Diagnostic, DiagnosticItem } from "./diagnostics.ts";
14+
import { Diagnostic } from "./diagnostics.ts";
1515
import { fromTypeScriptDiagnostic } from "./diagnostics_util.ts";
1616
import { cwd } from "./dir.ts";
1717
import * as dispatch from "./dispatch.ts";
@@ -68,14 +68,21 @@ interface CompilerRequestRuntimeCompile {
6868
rootName: string;
6969
sources?: Record<string, string>;
7070
bundle: boolean;
71-
options?: CompilerOptions;
71+
options?: string;
72+
}
73+
74+
interface CompilerRequestRuntimeTranspile {
75+
type: CompilerRequestType.RuntimeTranspile;
76+
sources: Record<string, string>;
77+
options?: string;
7278
}
7379

7480
/** The format of the work message payload coming from the privileged side */
7581
type CompilerRequest =
7682
| CompilerRequestCompile
7783
| CompilerRequestBundle
78-
| CompilerRequestRuntimeCompile;
84+
| CompilerRequestRuntimeCompile
85+
| CompilerRequestRuntimeTranspile;
7986

8087
interface ConfigureResponse {
8188
ignoredOptions?: string[];
@@ -87,8 +94,6 @@ interface EmitResult {
8794
diagnostics?: Diagnostic;
8895
}
8996

90-
type CompilationResult = [DiagnosticItem[] | undefined, Record<string, string>];
91-
9297
// Startup boilerplate. This is necessary because the compiler has its own
9398
// snapshot. (It would be great if we could remove these things or centralize
9499
// them somewhere else.)
@@ -167,6 +172,116 @@ const ignoredCompilerOptions: readonly string[] = [
167172
"watch"
168173
];
169174

175+
/** Default options used by the compiler Host when compiling. */
176+
const defaultCompileOptions: ts.CompilerOptions = {
177+
allowJs: true,
178+
allowNonTsExtensions: true,
179+
// TODO(#3324) Enable strict mode for user code.
180+
// strict: true,
181+
checkJs: false,
182+
esModuleInterop: true,
183+
module: ts.ModuleKind.ESNext,
184+
outDir: OUT_DIR,
185+
resolveJsonModule: true,
186+
sourceMap: true,
187+
stripComments: true,
188+
target: ts.ScriptTarget.ESNext,
189+
jsx: ts.JsxEmit.React
190+
};
191+
192+
/** Default options used when doing a transpile only. */
193+
const defaultTranspileOptions: ts.CompilerOptions = {
194+
esModuleInterop: true,
195+
module: ts.ModuleKind.ESNext,
196+
sourceMap: true,
197+
scriptComments: true,
198+
target: ts.ScriptTarget.ESNext
199+
};
200+
201+
function convertCompilerOptions(str: string): ts.CompilerOptions {
202+
const options: CompilerOptions = JSON.parse(str);
203+
const out: Record<string, unknown> = {};
204+
const keys = Object.keys(options) as Array<keyof CompilerOptions>;
205+
for (const key of keys) {
206+
switch (key) {
207+
case "jsx":
208+
const value = options[key];
209+
if (value === "preserve") {
210+
out[key] = ts.JsxEmit.Preserve;
211+
} else if (value === "react") {
212+
out[key] = ts.JsxEmit.React;
213+
} else {
214+
out[key] = ts.JsxEmit.ReactNative;
215+
}
216+
break;
217+
case "module":
218+
switch (options[key]) {
219+
case "amd":
220+
out[key] = ts.ModuleKind.AMD;
221+
break;
222+
case "commonjs":
223+
out[key] = ts.ModuleKind.CommonJS;
224+
break;
225+
case "es2015":
226+
case "es6":
227+
out[key] = ts.ModuleKind.ES2015;
228+
break;
229+
case "esnext":
230+
out[key] = ts.ModuleKind.ESNext;
231+
break;
232+
case "none":
233+
out[key] = ts.ModuleKind.None;
234+
break;
235+
case "system":
236+
out[key] = ts.ModuleKind.System;
237+
break;
238+
case "umd":
239+
out[key] = ts.ModuleKind.UMD;
240+
break;
241+
default:
242+
throw new TypeError("Unexpected module type");
243+
}
244+
break;
245+
case "target":
246+
switch (options[key]) {
247+
case "es3":
248+
out[key] = ts.ScriptTarget.ES3;
249+
break;
250+
case "es5":
251+
out[key] = ts.ScriptTarget.ES5;
252+
break;
253+
case "es6":
254+
case "es2015":
255+
out[key] = ts.ScriptTarget.ES2015;
256+
break;
257+
case "es2016":
258+
out[key] = ts.ScriptTarget.ES2016;
259+
break;
260+
case "es2017":
261+
out[key] = ts.ScriptTarget.ES2017;
262+
break;
263+
case "es2018":
264+
out[key] = ts.ScriptTarget.ES2018;
265+
break;
266+
case "es2019":
267+
out[key] = ts.ScriptTarget.ES2019;
268+
break;
269+
case "es2020":
270+
out[key] = ts.ScriptTarget.ES2020;
271+
break;
272+
case "esnext":
273+
out[key] = ts.ScriptTarget.ESNext;
274+
break;
275+
default:
276+
throw new TypeError("Unexpected emit target.");
277+
}
278+
default:
279+
out[key] = options[key];
280+
}
281+
}
282+
return out as ts.CompilerOptions;
283+
}
284+
170285
/** An array of TypeScript diagnostic types we ignore. */
171286
const ignoredDiagnostics = [
172287
// TS1103: 'for-await-of' statement is only allowed within an async function
@@ -312,6 +427,11 @@ class SourceFile {
312427
}
313428
}
314429

430+
function resolveModules(specifiers: string[], referrer?: string): string[] {
431+
util.log("compiler::resolveModules", { specifiers, referrer });
432+
return sendSync(dispatch.OP_RESOLVE_MODULES, { specifiers, referrer });
433+
}
434+
315435
/** Ops to Rust to resolve special static assets. */
316436
function fetchAsset(name: string): string {
317437
return sendSync(dispatch.OP_FETCH_ASSET, { name });
@@ -400,7 +520,8 @@ async function processImports(
400520
return [];
401521
}
402522
const sources = specifiers.map(([, moduleSpecifier]) => moduleSpecifier);
403-
const sourceFiles = await fetchSourceFiles(sources, referrer);
523+
const resolveSources = resolveModules(sources, referrer);
524+
const sourceFiles = await fetchSourceFiles(resolveSources, referrer);
404525
assert(sourceFiles.length === specifiers.length);
405526
for (let i = 0; i < sourceFiles.length; i++) {
406527
const sourceFileJson = sourceFiles[i];
@@ -457,21 +578,7 @@ class Host implements ts.CompilerHost {
457578
private _requestType: CompilerRequestType;
458579
private _rootNames: string[];
459580

460-
private readonly _options: ts.CompilerOptions = {
461-
allowJs: true,
462-
allowNonTsExtensions: true,
463-
// TODO(#3324) Enable strict mode for user code.
464-
// strict: true,
465-
checkJs: false,
466-
esModuleInterop: true,
467-
module: ts.ModuleKind.ESNext,
468-
outDir: OUT_DIR,
469-
resolveJsonModule: true,
470-
sourceMap: true,
471-
stripComments: true,
472-
target: ts.ScriptTarget.ESNext,
473-
jsx: ts.JsxEmit.React
474-
};
581+
private readonly _options = defaultCompileOptions;
475582

476583
private _getAsset(filename: string): SourceFile {
477584
const sourceFile = SourceFile.get(filename);
@@ -804,16 +911,20 @@ self.compilerMain = function compilerMain(): void {
804911
break;
805912
}
806913
case CompilerRequestType.RuntimeCompile: {
807-
const { rootName, sources } = request;
914+
const { rootName, sources, options } = request;
808915

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

921+
const resolvedRootName = sources
922+
? rootName
923+
: resolveModules([rootName])[0];
924+
814925
const rootNames = sources
815-
? processLocalImports(sources, [[rootName, rootName]])
816-
: await processImports([[rootName, rootName]]);
926+
? processLocalImports(sources, [[resolvedRootName, resolvedRootName]])
927+
: await processImports([[resolvedRootName, resolvedRootName]]);
817928

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

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

829-
host.configure({ outDir: undefined });
940+
const compilerOptions = options
941+
? Object.assign(
942+
{},
943+
{ outDir: undefined },
944+
convertCompilerOptions(options)
945+
)
946+
: { outDir: undefined };
947+
host.configure(compilerOptions);
830948

831-
const options = host.getCompilationSettings();
832-
const program = ts.createProgram(rootNames, options, host);
949+
const program = ts.createProgram(
950+
rootNames,
951+
host.getCompilationSettings(),
952+
host
953+
);
833954

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

853974
break;
854975
}
976+
case CompilerRequestType.RuntimeTranspile: {
977+
const result: Record<string, TranspileOnlyResult> = {};
978+
const { sources, options } = request;
979+
const compilerOptions = options
980+
? Object.assign(
981+
{},
982+
defaultTranspileOptions,
983+
convertCompilerOptions(options)
984+
)
985+
: defaultTranspileOptions;
986+
for (const [fileName, inputText] of Object.entries(sources)) {
987+
const { outputText: source, sourceMapText: map } = ts.transpileModule(
988+
inputText,
989+
{
990+
fileName,
991+
compilerOptions
992+
}
993+
);
994+
result[fileName] = { source, map };
995+
postMessage(result);
996+
}
997+
998+
break;
999+
}
8551000
default:
8561001
util.log(
8571002
`!!! unhandled CompilerRequestType: ${

0 commit comments

Comments
 (0)