diff --git a/cli/build.rs b/cli/build.rs index 742f227ec9dc5c..c8e156a265d300 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -13,48 +13,13 @@ mod ts { use std::path::PathBuf; use deno_core::op2; + use deno_core::v8; use deno_core::OpState; use deno_error::JsErrorBox; use serde::Serialize; use super::*; - #[derive(Debug, Serialize)] - #[serde(rename_all = "camelCase")] - struct BuildInfoResponse { - build_specifier: String, - libs: Vec, - } - - #[op2] - #[serde] - fn op_build_info(state: &mut OpState) -> BuildInfoResponse { - let build_specifier = "asset:///bootstrap.ts".to_string(); - let build_libs = state - .borrow::>() - .iter() - .map(|s| s.to_string()) - .collect(); - BuildInfoResponse { - build_specifier, - libs: build_libs, - } - } - - #[op2(fast)] - fn op_is_node_file() -> bool { - false - } - - #[op2] - #[string] - fn op_script_version( - _state: &mut OpState, - #[string] _arg: &str, - ) -> Result, JsErrorBox> { - Ok(Some("1".to_string())) - } - #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] struct LoadResponse { @@ -74,19 +39,10 @@ mod ts { let op_crate_libs = state.borrow::>(); let path_dts = state.borrow::(); let re_asset = lazy_regex::regex!(r"asset:/{3}lib\.(\S+)\.d\.ts"); - let build_specifier = "asset:///bootstrap.ts"; - - // we need a basic file to send to tsc to warm it up. - if load_specifier == build_specifier { - Ok(LoadResponse { - data: r#"Deno.writeTextFile("hello.txt", "hello deno!");"#.to_string(), - version: "1".to_string(), - // this corresponds to `ts.ScriptKind.TypeScript` - script_kind: 3, - }) - // specifiers come across as `asset:///lib.{lib_name}.d.ts` and we need to - // parse out just the name so we can lookup the asset. - } else if let Some(caps) = re_asset.captures(load_specifier) { + + // specifiers come across as `asset:///lib.{lib_name}.d.ts` and we need to + // parse out just the name so we can lookup the asset. + if let Some(caps) = re_asset.captures(load_specifier) { if let Some(lib) = caps.get(1).map(|m| m.as_str()) { // if it comes from an op crate, we were supplied with the path to the // file. @@ -100,28 +56,25 @@ mod ts { }; let data = std::fs::read_to_string(path).map_err(JsErrorBox::from_err)?; - Ok(LoadResponse { + return Ok(LoadResponse { data, version: "1".to_string(), // this corresponds to `ts.ScriptKind.TypeScript` script_kind: 3, - }) - } else { - Err(JsErrorBox::new( - "InvalidSpecifier", - format!("An invalid specifier was requested: {}", load_specifier), - )) + }); } - } else { - Err(JsErrorBox::new( - "InvalidSpecifier", - format!("An invalid specifier was requested: {}", load_specifier), - )) } + + Err(JsErrorBox::new( + "InvalidSpecifier", + format!("An invalid specifier was requested: {}", load_specifier), + )) } deno_core::extension!(deno_tsc, - ops = [op_build_info, op_is_node_file, op_load, op_script_version], + ops = [ + op_load, + ], esm_entry_point = "ext:deno_tsc/99_main_compiler.js", esm = [ dir "tsc", @@ -277,6 +230,28 @@ mod ts { ) .unwrap(); + // Leak to satisfy type-checker. It's okay since it's only run once for a build script. + let build_libs_ = Box::leak(Box::new(build_libs.clone())); + let runtime_cb = Box::new(|rt: &mut deno_core::JsRuntimeForSnapshot| { + let scope = &mut rt.handle_scope(); + + let context = scope.get_current_context(); + let global = context.global(scope); + + let name = v8::String::new(scope, "snapshot").unwrap(); + let snapshot_fn_val = global.get(scope, name.into()).unwrap(); + let snapshot_fn: v8::Local = + snapshot_fn_val.try_into().unwrap(); + let undefined = v8::undefined(scope); + let build_libs = build_libs_.clone(); + let build_libs_v8 = + deno_core::serde_v8::to_v8(scope, build_libs).unwrap(); + + snapshot_fn + .call(scope, undefined.into(), &[build_libs_v8]) + .unwrap(); + }); + let output = create_snapshot( CreateSnapshotOptions { cargo_manifest_dir: env!("CARGO_MANIFEST_DIR"), @@ -287,7 +262,7 @@ mod ts { path_dts, )], extension_transpiler: None, - with_runtime_cb: None, + with_runtime_cb: Some(runtime_cb), skip_op_registration: false, }, None, diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js index c99cfce9a238c3..872b9c5479e5a9 100644 --- a/cli/tsc/99_main_compiler.js +++ b/cli/tsc/99_main_compiler.js @@ -22,7 +22,6 @@ import { getAssets, host, setLogDebug, - SOURCE_FILE_CACHE, } from "./97_ts_host.js"; import { serverMainLoop } from "./98_lsp.js"; @@ -39,16 +38,6 @@ const ops = core.ops; /** @type {Map} */ const normalizedToOriginalMap = new Map(); -const SNAPSHOT_COMPILE_OPTIONS = { - esModuleInterop: true, - jsx: ts.JsxEmit.React, - module: ts.ModuleKind.ESNext, - noEmit: true, - strict: true, - target: ts.ScriptTarget.ESNext, - lib: ["lib.deno.window.d.ts"], -}; - /** @type {Array<[string, number]>} */ const stats = []; let statsStart = 0; @@ -225,44 +214,26 @@ function exec({ config, debug: debugFlag, rootNames, localOnly }) { debug("<<< exec stop"); } -// A build time only op that provides some setup information that is used to -// ensure the snapshot is setup properly. -/** @type {{ buildSpecifier: string; libs: string[]; nodeBuiltInModuleNames: string[] }} */ -const { buildSpecifier, libs } = ops.op_build_info(); - -for (const lib of libs) { - const specifier = `lib.${lib}.d.ts`; - // we are using internal APIs here to "inject" our custom libraries into - // tsc, so things like `"lib": [ "deno.ns" ]` are supported. - if (!ts.libs.includes(lib)) { - ts.libs.push(lib); - ts.libMap.set(lib, `lib.${lib}.d.ts`); +globalThis.snapshot = function (libs) { + for (const lib of libs) { + const specifier = `lib.${lib}.d.ts`; + // we are using internal APIs here to "inject" our custom libraries into + // tsc, so things like `"lib": [ "deno.ns" ]` are supported. + if (!ts.libs.includes(lib)) { + ts.libs.push(lib); + ts.libMap.set(lib, `lib.${lib}.d.ts`); + } + // we are caching in memory common type libraries that will be re-used by + // tsc on when the snapshot is restored + assert( + !!host.getSourceFile( + `${ASSETS_URL_PREFIX}${specifier}`, + ts.ScriptTarget.ESNext, + ), + `failed to load '${ASSETS_URL_PREFIX}${specifier}'`, + ); } - // we are caching in memory common type libraries that will be re-used by - // tsc on when the snapshot is restored - assert( - !!host.getSourceFile( - `${ASSETS_URL_PREFIX}${specifier}`, - ts.ScriptTarget.ESNext, - ), - `failed to load '${ASSETS_URL_PREFIX}${specifier}'`, - ); -} -// this helps ensure as much as possible is in memory that is re-usable -// before the snapshotting is done, which helps unsure fast "startup" for -// subsequent uses of tsc in Deno. -const TS_SNAPSHOT_PROGRAM = ts.createProgram({ - rootNames: [buildSpecifier], - options: SNAPSHOT_COMPILE_OPTIONS, - host, -}); -assert( - ts.getPreEmitDiagnostics(TS_SNAPSHOT_PROGRAM).length === 0, - "lib.d.ts files have errors", -); - -// remove this now that we don't need it anymore for warming up tsc -SOURCE_FILE_CACHE.delete(buildSpecifier); +}; // exposes the functions that are called by `tsc::exec()` when type // checking TypeScript.