diff --git a/cli/build.rs b/cli/build.rs index 2e7227aba2..5b7d02722d 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -1,318 +1,183 @@ // Copyright 2018-2025 the Deno authors. MIT license. use std::env; -use std::path::PathBuf; +use std::io::Write; +use std::path::Path; -use deno_core::snapshot::*; use deno_runtime::*; -mod ts { - use std::collections::HashMap; - use std::io::Write; - use std::path::Path; - 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 LoadResponse { - data: String, - version: String, - script_kind: i32, +fn compress_decls(out_dir: &Path) { + let decls = [ + "lib.deno_webgpu.d.ts", + "lib.deno.ns.d.ts", + "lib.deno.unstable.d.ts", + "lib.deno.window.d.ts", + "lib.deno.worker.d.ts", + "lib.deno.shared_globals.d.ts", + "lib.deno.ns.d.ts", + "lib.deno.unstable.d.ts", + "lib.decorators.d.ts", + "lib.decorators.legacy.d.ts", + "lib.dom.asynciterable.d.ts", + "lib.dom.d.ts", + "lib.dom.extras.d.ts", + "lib.dom.iterable.d.ts", + "lib.es2015.collection.d.ts", + "lib.es2015.core.d.ts", + "lib.es2015.d.ts", + "lib.es2015.generator.d.ts", + "lib.es2015.iterable.d.ts", + "lib.es2015.promise.d.ts", + "lib.es2015.proxy.d.ts", + "lib.es2015.reflect.d.ts", + "lib.es2015.symbol.d.ts", + "lib.es2015.symbol.wellknown.d.ts", + "lib.es2016.array.include.d.ts", + "lib.es2016.d.ts", + "lib.es2016.full.d.ts", + "lib.es2016.intl.d.ts", + "lib.es2017.arraybuffer.d.ts", + "lib.es2017.d.ts", + "lib.es2017.date.d.ts", + "lib.es2017.full.d.ts", + "lib.es2017.intl.d.ts", + "lib.es2017.object.d.ts", + "lib.es2017.sharedmemory.d.ts", + "lib.es2017.string.d.ts", + "lib.es2017.typedarrays.d.ts", + "lib.es2018.asyncgenerator.d.ts", + "lib.es2018.asynciterable.d.ts", + "lib.es2018.d.ts", + "lib.es2018.full.d.ts", + "lib.es2018.intl.d.ts", + "lib.es2018.promise.d.ts", + "lib.es2018.regexp.d.ts", + "lib.es2019.array.d.ts", + "lib.es2019.d.ts", + "lib.es2019.full.d.ts", + "lib.es2019.intl.d.ts", + "lib.es2019.object.d.ts", + "lib.es2019.string.d.ts", + "lib.es2019.symbol.d.ts", + "lib.es2020.bigint.d.ts", + "lib.es2020.d.ts", + "lib.es2020.date.d.ts", + "lib.es2020.full.d.ts", + "lib.es2020.intl.d.ts", + "lib.es2020.number.d.ts", + "lib.es2020.promise.d.ts", + "lib.es2020.sharedmemory.d.ts", + "lib.es2020.string.d.ts", + "lib.es2020.symbol.wellknown.d.ts", + "lib.es2021.d.ts", + "lib.es2021.full.d.ts", + "lib.es2021.intl.d.ts", + "lib.es2021.promise.d.ts", + "lib.es2021.string.d.ts", + "lib.es2021.weakref.d.ts", + "lib.es2022.array.d.ts", + "lib.es2022.d.ts", + "lib.es2022.error.d.ts", + "lib.es2022.full.d.ts", + "lib.es2022.intl.d.ts", + "lib.es2022.object.d.ts", + "lib.es2022.regexp.d.ts", + "lib.es2022.string.d.ts", + "lib.es2023.array.d.ts", + "lib.es2023.collection.d.ts", + "lib.es2023.d.ts", + "lib.es2023.full.d.ts", + "lib.es2023.intl.d.ts", + "lib.es2024.arraybuffer.d.ts", + "lib.es2024.collection.d.ts", + "lib.es2024.d.ts", + "lib.es2024.full.d.ts", + "lib.es2024.object.d.ts", + "lib.es2024.promise.d.ts", + "lib.es2024.regexp.d.ts", + "lib.es2024.sharedmemory.d.ts", + "lib.es2024.string.d.ts", + "lib.es5.d.ts", + "lib.es6.d.ts", + "lib.esnext.array.d.ts", + "lib.esnext.collection.d.ts", + "lib.esnext.d.ts", + "lib.esnext.decorators.d.ts", + "lib.esnext.disposable.d.ts", + "lib.esnext.full.d.ts", + "lib.esnext.intl.d.ts", + "lib.esnext.iterator.d.ts", + "lib.scripthost.d.ts", + "lib.webworker.asynciterable.d.ts", + "lib.webworker.d.ts", + "lib.webworker.importscripts.d.ts", + "lib.webworker.iterable.d.ts", + ]; + for decl in decls { + let file = format!("./tsc/dts/{decl}"); + compress_source(out_dir, &file); } - - #[op2] - #[serde] - // using the same op that is used in `tsc.rs` for loading modules and reading - // files, but a slightly different implementation at build time. - fn op_load( - state: &mut OpState, - #[string] load_specifier: &str, - ) -> Result { - let op_crate_libs = state.borrow::>(); - let path_dts = state.borrow::(); - let re_asset = lazy_regex::regex!(r"asset:/{3}lib\.(\S+)\.d\.ts"); - - // 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. - let path = if let Some(op_crate_lib) = op_crate_libs.get(lib) { - PathBuf::from(op_crate_lib) - .canonicalize() - .map_err(JsErrorBox::from_err)? - // otherwise we will generate the path ourself - } else { - path_dts.join(format!("lib.{lib}.d.ts")) - }; - let data = - std::fs::read_to_string(path).map_err(JsErrorBox::from_err)?; - return Ok(LoadResponse { - data, - version: "1".to_string(), - // this corresponds to `ts.ScriptKind.TypeScript` - script_kind: 3, - }); - } - } - - Err(JsErrorBox::new( - "InvalidSpecifier", - format!("An invalid specifier was requested: {}", load_specifier), - )) + let ext_decls = [ + "console/lib.deno_console.d.ts", + "url/lib.deno_url.d.ts", + "web/lib.deno_web.d.ts", + "fetch/lib.deno_fetch.d.ts", + "websocket/lib.deno_websocket.d.ts", + "webstorage/lib.deno_webstorage.d.ts", + "canvas/lib.deno_canvas.d.ts", + "crypto/lib.deno_crypto.d.ts", + "cache/lib.deno_cache.d.ts", + "net/lib.deno_net.d.ts", + "broadcast_channel/lib.deno_broadcast_channel.d.ts", + ]; + for ext_decl in ext_decls { + let file = format!("../ext/{ext_decl}"); + compress_source(out_dir, &file); } +} - deno_core::extension!(deno_tsc, - ops = [ - op_load, - ], - esm_entry_point = "ext:deno_tsc/99_main_compiler.js", - esm = [ - dir "tsc", - "97_ts_host.js", - "98_lsp.js", - "99_main_compiler.js", - ], - js = [ - dir "tsc", - "00_typescript.js", - ], - options = { - op_crate_libs: HashMap<&'static str, PathBuf>, - build_libs: Vec<&'static str>, - path_dts: PathBuf, - }, - state = |state, options| { - state.put(options.op_crate_libs); - state.put(options.build_libs); - state.put(options.path_dts); - }, - ); +fn compress_source(out_dir: &Path, file: &str) { + let path = Path::new(file) + .canonicalize() + .unwrap_or_else(|_| panic!("expected file \"{file}\" to exist")); + let contents = std::fs::read(&path).unwrap(); - pub fn create_compiler_snapshot(snapshot_path: PathBuf, cwd: &Path) { - // libs that are being provided by op crates. - let mut op_crate_libs = HashMap::new(); - op_crate_libs.insert("deno.cache", deno_cache::get_declaration()); - op_crate_libs.insert("deno.console", deno_console::get_declaration()); - op_crate_libs.insert("deno.url", deno_url::get_declaration()); - op_crate_libs.insert("deno.web", deno_web::get_declaration()); - op_crate_libs.insert("deno.fetch", deno_fetch::get_declaration()); - op_crate_libs.insert("deno.webgpu", deno_webgpu_get_declaration()); - op_crate_libs.insert("deno.websocket", deno_websocket::get_declaration()); - op_crate_libs.insert("deno.webstorage", deno_webstorage::get_declaration()); - op_crate_libs.insert("deno.canvas", deno_canvas::get_declaration()); - op_crate_libs.insert("deno.crypto", deno_crypto::get_declaration()); - op_crate_libs.insert( - "deno.broadcast_channel", - deno_broadcast_channel::get_declaration(), - ); - op_crate_libs.insert("deno.net", deno_net::get_declaration()); + println!("cargo:rerun-if-changed={}", path.display()); - // ensure we invalidate the build properly. - for (_, path) in op_crate_libs.iter() { - println!("cargo:rerun-if-changed={}", path.display()); - } - - // libs that should be loaded into the isolate before snapshotting. - let libs = vec![ - // Deno custom type libraries - "deno.window", - "deno.worker", - "deno.shared_globals", - "deno.ns", - "deno.unstable", - // Deno built-in type libraries - "decorators", - "decorators.legacy", - "dom.asynciterable", - "dom", - "dom.extras", - "dom.iterable", - "es2015.collection", - "es2015.core", - "es2015", - "es2015.generator", - "es2015.iterable", - "es2015.promise", - "es2015.proxy", - "es2015.reflect", - "es2015.symbol", - "es2015.symbol.wellknown", - "es2016.array.include", - "es2016", - "es2016.full", - "es2016.intl", - "es2017.arraybuffer", - "es2017", - "es2017.date", - "es2017.full", - "es2017.intl", - "es2017.object", - "es2017.sharedmemory", - "es2017.string", - "es2017.typedarrays", - "es2018.asyncgenerator", - "es2018.asynciterable", - "es2018", - "es2018.full", - "es2018.intl", - "es2018.promise", - "es2018.regexp", - "es2019.array", - "es2019", - "es2019.full", - "es2019.intl", - "es2019.object", - "es2019.string", - "es2019.symbol", - "es2020.bigint", - "es2020", - "es2020.date", - "es2020.full", - "es2020.intl", - "es2020.number", - "es2020.promise", - "es2020.sharedmemory", - "es2020.string", - "es2020.symbol.wellknown", - "es2021", - "es2021.full", - "es2021.intl", - "es2021.promise", - "es2021.string", - "es2021.weakref", - "es2022.array", - "es2022", - "es2022.error", - "es2022.full", - "es2022.intl", - "es2022.object", - "es2022.regexp", - "es2022.string", - "es2023.array", - "es2023.collection", - "es2023", - "es2023.full", - "es2023.intl", - "es2024.arraybuffer", - "es2024.collection", - "es2024", - "es2024.full", - "es2024.object", - "es2024.promise", - "es2024.regexp", - "es2024.sharedmemory", - "es2024.string", - "es5", - "es6", - "esnext.array", - "esnext.collection", - "esnext", - "esnext.decorators", - "esnext.disposable", - "esnext.full", - "esnext.intl", - "esnext.iterator", - "scripthost", - "webworker.asynciterable", - "webworker", - "webworker.importscripts", - "webworker.iterable", - ]; - - let path_dts = cwd.join("tsc/dts"); - // ensure we invalidate the build properly. - for name in libs.iter() { - println!( - "cargo:rerun-if-changed={}", - path_dts.join(format!("lib.{name}.d.ts")).display() - ); - } - - // create a copy of the vector that includes any op crate libs to be passed - // to the JavaScript compiler to build into the snapshot - let mut build_libs = libs.clone(); - for (op_lib, _) in op_crate_libs.iter() { - build_libs.push(op_lib.to_owned()); - } - - // used in the tests to verify that after snapshotting it has the same number - // of lib files loaded and hasn't included any ones lazily loaded from Rust - std::fs::write( - PathBuf::from(env::var_os("OUT_DIR").unwrap()) - .join("lib_file_names.json"), - serde_json::to_string(&build_libs).unwrap(), - ) + let compressed = zstd::bulk::compress(&contents, 19).unwrap(); + let mut out = out_dir.join(file.trim_start_matches("../")); + let mut ext = out + .extension() + .map(|s| s.to_string_lossy()) + .unwrap_or_default() + .into_owned(); + ext.push_str(".zstd"); + out.set_extension(ext); + std::fs::create_dir_all(out.parent().unwrap()).unwrap(); + let mut file = std::fs::OpenOptions::new() + .create(true) + .truncate(true) + .write(true) + .open(out) + .unwrap(); + file + .write_all(&(contents.len() as u32).to_le_bytes()) .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(); + file.write_all(&compressed).unwrap(); +} - let context = scope.get_current_context(); - let global = context.global(scope); +fn compress_sources(out_dir: &Path) { + compress_decls(out_dir); - 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"), - startup_snapshot: None, - extensions: vec![deno_tsc::init_ops_and_esm( - op_crate_libs, - build_libs, - path_dts, - )], - extension_transpiler: None, - with_runtime_cb: Some(runtime_cb), - skip_op_registration: false, - }, - None, - ) - .unwrap(); - - // NOTE(bartlomieju): Compressing the TSC snapshot in debug build took - // ~45s on M1 MacBook Pro; without compression it took ~1s. - // Thus we're not using compressed snapshot, trading off - // a lot of build time for some startup time in debug build. - let mut file = std::fs::File::create(snapshot_path).unwrap(); - if cfg!(debug_assertions) { - file.write_all(&output.output).unwrap(); - } else { - let mut vec = Vec::with_capacity(output.output.len()); - vec.extend((output.output.len() as u32).to_le_bytes()); - vec.extend_from_slice( - &zstd::bulk::compress(&output.output, 22) - .expect("snapshot compression failed"), - ); - file.write_all(&vec).unwrap(); - } - - for path in output.files_loaded_during_snapshot { - println!("cargo:rerun-if-changed={}", path.display()); - } + let ext_sources = [ + "./tsc/99_main_compiler.js", + "./tsc/97_ts_host.js", + "./tsc/98_lsp.js", + "./tsc/00_typescript.js", + ]; + for ext_source in ext_sources { + compress_source(out_dir, ext_source); } } @@ -337,6 +202,12 @@ fn main() { // To debug snapshot issues uncomment: // op_fetch_asset::trace_serializer(); + if !cfg!(debug_assertions) { + let out_dir = + std::path::PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); + compress_sources(&out_dir); + } + if let Ok(c) = env::var("DENO_CANARY") { println!("cargo:rustc-env=DENO_CANARY={c}"); } @@ -345,12 +216,6 @@ fn main() { println!("cargo:rustc-env=TARGET={}", env::var("TARGET").unwrap()); println!("cargo:rustc-env=PROFILE={}", env::var("PROFILE").unwrap()); - let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); - let o = PathBuf::from(env::var_os("OUT_DIR").unwrap()); - - let compiler_snapshot_path = o.join("COMPILER_SNAPSHOT.bin"); - ts::create_compiler_snapshot(compiler_snapshot_path, &c); - #[cfg(target_os = "windows")] { let mut res = winres::WindowsResource::new(); @@ -362,11 +227,3 @@ fn main() { res.compile().unwrap(); } } - -fn deno_webgpu_get_declaration() -> PathBuf { - let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")); - manifest_dir - .join("tsc") - .join("dts") - .join("lib.deno_webgpu.d.ts") -} diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 203b88f47f..c8d8e83e06 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -606,7 +606,7 @@ impl Inner { ts_server.clone(), diagnostics_state.clone(), ); - let assets = Assets::new(ts_server.clone()); + let assets = Assets::new(); let initial_cwd = std::env::current_dir().unwrap_or_else(|_| { panic!("Could not resolve current working directory") }); @@ -913,7 +913,6 @@ impl Inner { .await?; self.ts_fixable_diagnostics = fixable_diagnostics; } - self.assets.initialize(self.snapshot()).await; self.performance.measure(mark); Ok(InitializeResult { diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index 62f6cdc584..6a6d3162c5 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -1399,7 +1399,7 @@ fn new_assets_map() -> Arc> { .map(|(k, v)| { let url_str = format!("asset:///{k}"); let specifier = resolve_url(&url_str).unwrap(); - let asset = AssetDocument::new(specifier.clone(), v); + let asset = AssetDocument::new(specifier.clone(), v.get()); (specifier, asset) }) .collect::(); @@ -1430,29 +1430,16 @@ impl AssetsSnapshot { /// multiple threads without needing to worry about race conditions. #[derive(Debug, Clone)] pub struct Assets { - ts_server: Arc, assets: Arc>, } impl Assets { - pub fn new(ts_server: Arc) -> Self { + pub fn new() -> Self { Self { - ts_server, assets: new_assets_map(), } } - /// Initializes with the assets in the isolate. - pub async fn initialize(&self, state_snapshot: Arc) { - let assets = get_isolate_assets(&self.ts_server, state_snapshot).await; - let mut assets_map = self.assets.lock(); - for asset in assets { - if !assets_map.contains_key(asset.specifier()) { - assets_map.insert(asset.specifier().clone(), asset); - } - } - } - pub fn snapshot(&self) -> AssetsSnapshot { // it's ok to not make a complete copy for snapshotting purposes // because assets are static @@ -1477,39 +1464,6 @@ impl Assets { } } -/// Get all the assets stored in the tsc isolate. -async fn get_isolate_assets( - ts_server: &TsServer, - state_snapshot: Arc, -) -> Vec { - let req = TscRequest::GetAssets; - let res: Value = ts_server - .request(state_snapshot, req, None, &Default::default()) - .await - .unwrap(); - let response_assets = match res { - Value::Array(value) => value, - _ => unreachable!(), - }; - let mut assets = Vec::with_capacity(response_assets.len()); - - for asset in response_assets { - let mut obj = match asset { - Value::Object(obj) => obj, - _ => unreachable!(), - }; - let specifier_str = obj.get("specifier").unwrap().as_str().unwrap(); - let specifier = ModuleSpecifier::parse(specifier_str).unwrap(); - let text = match obj.remove("text").unwrap() { - Value::String(text) => text, - _ => unreachable!(), - }; - assets.push(AssetDocument::new(specifier, text)); - } - - assets -} - fn get_tag_body_text( tag: &JsDocTagInfo, language_server: &language_server::Inner, @@ -4547,6 +4501,21 @@ fn op_is_node_file(state: &mut OpState, #[string] path: String) -> bool { r } +#[op2] +#[serde] +fn op_libs() -> Vec { + let mut out = + Vec::with_capacity(crate::tsc::LAZILY_LOADED_STATIC_ASSETS.len()); + for key in crate::tsc::LAZILY_LOADED_STATIC_ASSETS.keys() { + let lib = key + .replace("lib.", "") + .replace(".d.ts", "") + .replace("deno_", "deno."); + out.push(lib); + } + out +} + #[derive(Debug, thiserror::Error, deno_error::JsError)] enum LoadError { #[error("{0}")] @@ -4947,13 +4916,13 @@ fn run_tsc_thread( // supplied snapshot is an isolate that contains the TypeScript language // server. let mut tsc_runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![deno_tsc::init_ops( + extensions: vec![deno_tsc::init_ops_and_esm( performance, specifier_map, request_rx, )], create_params: create_isolate_create_params(), - startup_snapshot: Some(tsc::compiler_snapshot()), + startup_snapshot: None, inspector: has_inspector_server, ..Default::default() }); @@ -5035,6 +5004,7 @@ deno_core::extension!(deno_tsc, op_script_version, op_project_version, op_poll_requests, + op_libs, ], options = { performance: Arc, @@ -5049,6 +5019,14 @@ deno_core::extension!(deno_tsc, options.request_rx, )); }, + customizer = |ext: &mut deno_core::Extension| { + use deno_core::ExtensionFileSource; + ext.esm_files.to_mut().push(ExtensionFileSource::new_computed("ext:deno_tsc/99_main_compiler.js", crate::tsc::MAIN_COMPILER_SOURCE.get().into())); + ext.esm_files.to_mut().push(ExtensionFileSource::new_computed("ext:deno_tsc/97_ts_host.js", crate::tsc::TS_HOST_SOURCE.get().into())); + ext.esm_files.to_mut().push(ExtensionFileSource::new_computed("ext:deno_tsc/98_lsp.js", crate::tsc::LSP_SOURCE.get().into())); + ext.js_files.to_mut().push(ExtensionFileSource::new_computed("ext:deno_cli_tsc/00_typescript.js", crate::tsc::TYPESCRIPT_SOURCE.get().into())); + ext.esm_entry_point = Some("ext:deno_tsc/99_main_compiler.js"); + } ); #[derive(Debug, Clone, Deserialize_repr, Serialize_repr)] @@ -5428,7 +5406,6 @@ pub struct JsNull; #[derive(Debug, Clone, Serialize)] pub enum TscRequest { GetDiagnostics((Vec, usize)), - GetAssets, CleanupSemanticCache, // https://github.com/denoland/deno/blob/v1.37.1/cli/tsc/dts/typescript.d.ts#L6230 @@ -5634,7 +5611,6 @@ impl TscRequest { ("provideInlayHints", Some(serde_v8::to_v8(scope, args)?)) } TscRequest::CleanupSemanticCache => ("cleanupSemanticCache", None), - TscRequest::GetAssets => ("$getAssets", None), }; Ok(args) @@ -5679,7 +5655,6 @@ impl TscRequest { TscRequest::GetSignatureHelpItems(_) => "getSignatureHelpItems", TscRequest::GetNavigateToItems(_) => "getNavigateToItems", TscRequest::ProvideInlayHints(_) => "provideInlayHints", - TscRequest::GetAssets => "$getAssets", } } } @@ -6065,52 +6040,6 @@ mod tests { ); } - #[tokio::test] - async fn test_request_assets() { - let (_, ts_server, snapshot, _) = setup(json!({}), &[]).await; - let assets = get_isolate_assets(&ts_server, snapshot).await; - let mut asset_names = assets - .iter() - .map(|a| { - a.specifier() - .to_string() - .replace("asset:///lib.", "") - .replace(".d.ts", "") - }) - .collect::>(); - let mut expected_asset_names: Vec = serde_json::from_str( - include_str!(concat!(env!("OUT_DIR"), "/lib_file_names.json")), - ) - .unwrap(); - asset_names.sort(); - - // if this test fails, update build.rs - expected_asset_names.sort(); - assert_eq!(asset_names, expected_asset_names); - - // get some notification when the size of the assets grows - let mut total_size = 0; - for asset in &assets { - total_size += asset.text().len(); - } - assert!(total_size > 0); - #[allow(clippy::print_stderr)] - // currently as of TS 5.7, it's 3MB - if total_size > 3_500_000 { - let mut sizes = Vec::new(); - for asset in &assets { - sizes.push((asset.specifier(), asset.text().len())); - } - sizes.sort_by_cached_key(|(_, size)| *size); - sizes.reverse(); - for (specifier, size) in &sizes { - eprintln!("{}: {}", specifier, size); - } - eprintln!("Total size: {}", total_size); - panic!("Assets were quite large."); - } - } - #[tokio::test] async fn test_modify_sources() { let (temp_dir, ts_server, snapshot, cache) = setup( diff --git a/cli/tsc/97_ts_host.js b/cli/tsc/97_ts_host.js index 9bafe82dbb..50d8f869f9 100644 --- a/cli/tsc/97_ts_host.js +++ b/cli/tsc/97_ts_host.js @@ -855,17 +855,3 @@ const setTypesNodeIgnorableNames = new Set([ "WritableStreamDefaultWriter", ]); ts.deno.setTypesNodeIgnorableNames(setTypesNodeIgnorableNames); - -export function getAssets() { - /** @type {{ specifier: string; text: string; }[]} */ - const assets = []; - for (const sourceFile of SOURCE_FILE_CACHE.values()) { - if (sourceFile.fileName.startsWith(ASSETS_URL_PREFIX)) { - assets.push({ - specifier: sourceFile.fileName, - text: sourceFile.text, - }); - } - } - return assets; -} diff --git a/cli/tsc/98_lsp.js b/cli/tsc/98_lsp.js index 09b4472124..0525b1d65f 100644 --- a/cli/tsc/98_lsp.js +++ b/cli/tsc/98_lsp.js @@ -8,7 +8,6 @@ import { error, filterMapDiagnostic, fromTypeScriptDiagnostics, - getAssets, getCreateSourceFileOptions, host, IS_NODE_SOURCE_FILE_CACHE, @@ -446,9 +445,6 @@ function serverRequest(id, method, args, scope, maybeChange) { ts.getSupportedCodeFixes(), ); } - case "$getAssets": { - return respond(id, getAssets()); - } case "$getDiagnostics": { const projectVersion = args[1]; // there's a possibility that we receive a change notification diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js index 872b9c5479..2ec1cd19e9 100644 --- a/cli/tsc/99_main_compiler.js +++ b/cli/tsc/99_main_compiler.js @@ -13,13 +13,10 @@ delete Object.prototype.__proto__; import { - assert, AssertionError, - ASSETS_URL_PREFIX, debug, filterMapDiagnostic, fromTypeScriptDiagnostics, - getAssets, host, setLogDebug, } from "./97_ts_host.js"; @@ -214,31 +211,20 @@ function exec({ config, debug: debugFlag, rootNames, localOnly }) { debug("<<< exec stop"); } -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}'`, - ); +const libs = ops.op_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, specifier); } -}; +} // exposes the functions that are called by `tsc::exec()` when type // checking TypeScript. globalThis.exec = exec; -globalThis.getAssets = getAssets; // exposes the functions that are called when the compiler is used as a // language service. diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs index 1a3e1b11e9..66e6af6e2c 100644 --- a/cli/tsc/mod.rs +++ b/cli/tsc/mod.rs @@ -5,10 +5,10 @@ use std::collections::HashMap; use std::fmt; use std::path::PathBuf; use std::sync::Arc; +use std::sync::OnceLock; use deno_ast::MediaType; use deno_core::anyhow::Context; -use deno_core::ascii_str; use deno_core::error::AnyError; use deno_core::located_script_name; use deno_core::op2; @@ -18,7 +18,6 @@ use deno_core::serde::Deserializer; use deno_core::serde::Serialize; use deno_core::serde::Serializer; use deno_core::serde_json::json; -use deno_core::serde_v8; use deno_core::url::Url; use deno_core::JsRuntime; use deno_core::ModuleSpecifier; @@ -59,33 +58,8 @@ pub use self::diagnostics::DiagnosticCategory; pub use self::diagnostics::Diagnostics; pub use self::diagnostics::Position; -pub static COMPILER_SNAPSHOT: Lazy> = Lazy::new( - #[cold] - #[inline(never)] - || { - static COMPRESSED_COMPILER_SNAPSHOT: &[u8] = - include_bytes!(concat!(env!("OUT_DIR"), "/COMPILER_SNAPSHOT.bin")); - - // NOTE(bartlomieju): Compressing the TSC snapshot in debug build took - // ~45s on M1 MacBook Pro; without compression it took ~1s. - // Thus we're not using compressed snapshot, trading off - // a lot of build time for some startup time in debug build. - #[cfg(debug_assertions)] - return COMPRESSED_COMPILER_SNAPSHOT.to_vec().into_boxed_slice(); - - #[cfg(not(debug_assertions))] - zstd::bulk::decompress( - &COMPRESSED_COMPILER_SNAPSHOT[4..], - u32::from_le_bytes(COMPRESSED_COMPILER_SNAPSHOT[0..4].try_into().unwrap()) - as usize, - ) - .unwrap() - .into_boxed_slice() - }, -); - pub fn get_types_declaration_file_text() -> String { - let mut assets = get_asset_texts_from_new_runtime() + let mut assets = get_asset_texts() .unwrap() .into_iter() .map(|a| (a.specifier, a.text)) @@ -120,41 +94,146 @@ pub fn get_types_declaration_file_text() -> String { .join("\n") } -fn get_asset_texts_from_new_runtime() -> Result, AnyError> { - deno_core::extension!( - deno_cli_tsc, - ops = [ - op_create_hash, - op_emit, - op_is_node_file, - op_load, - op_remap_specifier, - op_resolve, - op_respond, - ] - ); - - // the assets are stored within the typescript isolate, so take them out of there - let mut runtime = JsRuntime::new(RuntimeOptions { - startup_snapshot: Some(compiler_snapshot()), - extensions: vec![deno_cli_tsc::init_ops()], - ..Default::default() - }); - let global = runtime - .execute_script("get_assets.js", ascii_str!("globalThis.getAssets()"))?; - let scope = &mut runtime.handle_scope(); - let local = deno_core::v8::Local::new(scope, global); - Ok(serde_v8::from_v8::>(scope, local)?) +fn get_asset_texts() -> Result, AnyError> { + let mut out = Vec::with_capacity(LAZILY_LOADED_STATIC_ASSETS.len()); + for (name, text) in LAZILY_LOADED_STATIC_ASSETS.iter() { + out.push(AssetText { + specifier: format!("asset:///{name}"), + text: text.to_string(), + }); + } + Ok(out) } -pub fn compiler_snapshot() -> &'static [u8] { - &COMPILER_SNAPSHOT +macro_rules! maybe_compressed_source { + ($file: expr) => {{ + #[cfg(debug_assertions)] + { + StaticAssetSource::Uncompressed(include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/", + $file + ))) + } + #[cfg(not(debug_assertions))] + { + StaticAssetSource::Compressed(CompressedSource::new(include_bytes!( + concat!(env!("OUT_DIR"), "/", $file, ".zstd") + ))) + } + }}; + (compressed = $comp: expr, uncompressed = $uncomp: expr) => {{ + #[cfg(debug_assertions)] + { + StaticAssetSource::Uncompressed(include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/", + $uncomp + ))) + } + #[cfg(not(debug_assertions))] + { + StaticAssetSource::Compressed(CompressedSource::new(include_bytes!( + concat!(env!("OUT_DIR"), "/", $comp, ".zstd") + ))) + } + }}; } -macro_rules! inc { - ($e:expr) => { - include_str!(concat!("./dts/", $e)) +macro_rules! maybe_compressed_lib { + ($name: expr, $file: expr) => { + ($name, maybe_compressed_source!(concat!("tsc/dts/", $file))) }; + ($e: expr) => { + maybe_compressed_lib!($e, $e) + }; +} + +macro_rules! maybe_compressed_ext_lib { + ($name: expr, $file: expr) => { + ( + $name, + maybe_compressed_source!( + compressed = concat!("ext/", $file), + uncompressed = concat!("../ext/", $file) + ), + ) + }; +} + +#[derive(Clone)] +pub enum StaticAssetSource { + #[cfg_attr(debug_assertions, allow(dead_code))] + Compressed(CompressedSource), + Uncompressed(&'static str), +} + +/// Like a `Cow` but the owned form is an `Arc` instead of `String` +#[derive(Debug, Clone)] +pub enum MaybeStaticSource { + Computed(Arc), + Static(&'static str), +} + +impl std::fmt::Display for MaybeStaticSource { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + MaybeStaticSource::Computed(arc) => write!(f, "{}", arc), + MaybeStaticSource::Static(s) => write!(f, "{}", s), + } + } +} + +impl From for Cow<'static, str> { + fn from(value: MaybeStaticSource) -> Self { + match value { + MaybeStaticSource::Computed(arc) => Cow::Owned(arc.to_string()), + MaybeStaticSource::Static(s) => Cow::Borrowed(s), + } + } +} + +impl AsRef for MaybeStaticSource { + fn as_ref(&self) -> &str { + match self { + MaybeStaticSource::Computed(arc) => arc.as_ref(), + MaybeStaticSource::Static(s) => s, + } + } +} + +impl From for String { + fn from(value: MaybeStaticSource) -> Self { + match value { + MaybeStaticSource::Computed(arc) => arc.to_string(), + MaybeStaticSource::Static(s) => s.into(), + } + } +} + +impl From for Arc { + fn from(value: MaybeStaticSource) -> Self { + match value { + MaybeStaticSource::Computed(arc) => arc, + MaybeStaticSource::Static(s) => Arc::from(s), + } + } +} + +impl StaticAssetSource { + pub fn get(&self) -> MaybeStaticSource { + match self { + StaticAssetSource::Compressed(compressed_source) => { + MaybeStaticSource::Computed(compressed_source.get()) + } + StaticAssetSource::Uncompressed(src) => MaybeStaticSource::Static(src), + } + } + + #[allow(clippy::inherent_to_string)] + pub fn to_string(&self) -> String { + self.get().into() + } } /// Contains static assets that are not preloaded in the compiler snapshot. @@ -162,40 +241,154 @@ macro_rules! inc { /// We lazily load these because putting them in the compiler snapshot will /// increase memory usage when not used (last time checked by about 0.5MB). pub static LAZILY_LOADED_STATIC_ASSETS: Lazy< - HashMap<&'static str, &'static str>, + HashMap<&'static str, StaticAssetSource>, > = Lazy::new(|| { ([ - ( - "lib.dom.asynciterable.d.ts", - inc!("lib.dom.asynciterable.d.ts"), + // compressed in build.rs + maybe_compressed_ext_lib!( + "lib.deno.console.d.ts", + "console/lib.deno_console.d.ts" ), - ("lib.dom.d.ts", inc!("lib.dom.d.ts")), - ("lib.dom.extras.d.ts", inc!("lib.dom.extras.d.ts")), - ("lib.dom.iterable.d.ts", inc!("lib.dom.iterable.d.ts")), - ("lib.es6.d.ts", inc!("lib.es6.d.ts")), - ("lib.es2016.full.d.ts", inc!("lib.es2016.full.d.ts")), - ("lib.es2017.full.d.ts", inc!("lib.es2017.full.d.ts")), - ("lib.es2018.full.d.ts", inc!("lib.es2018.full.d.ts")), - ("lib.es2019.full.d.ts", inc!("lib.es2019.full.d.ts")), - ("lib.es2020.full.d.ts", inc!("lib.es2020.full.d.ts")), - ("lib.es2021.full.d.ts", inc!("lib.es2021.full.d.ts")), - ("lib.es2022.full.d.ts", inc!("lib.es2022.full.d.ts")), - ("lib.esnext.full.d.ts", inc!("lib.esnext.full.d.ts")), - ("lib.scripthost.d.ts", inc!("lib.scripthost.d.ts")), - ("lib.webworker.d.ts", inc!("lib.webworker.d.ts")), - ( - "lib.webworker.importscripts.d.ts", - inc!("lib.webworker.importscripts.d.ts"), + maybe_compressed_ext_lib!("lib.deno.url.d.ts", "url/lib.deno_url.d.ts"), + maybe_compressed_ext_lib!("lib.deno.web.d.ts", "web/lib.deno_web.d.ts"), + maybe_compressed_ext_lib!( + "lib.deno.fetch.d.ts", + "fetch/lib.deno_fetch.d.ts" ), - ( - "lib.webworker.iterable.d.ts", - inc!("lib.webworker.iterable.d.ts"), + maybe_compressed_ext_lib!( + "lib.deno.websocket.d.ts", + "websocket/lib.deno_websocket.d.ts" ), + maybe_compressed_ext_lib!( + "lib.deno.webstorage.d.ts", + "webstorage/lib.deno_webstorage.d.ts" + ), + maybe_compressed_ext_lib!( + "lib.deno.canvas.d.ts", + "canvas/lib.deno_canvas.d.ts" + ), + maybe_compressed_ext_lib!( + "lib.deno.crypto.d.ts", + "crypto/lib.deno_crypto.d.ts" + ), + maybe_compressed_ext_lib!( + "lib.deno.broadcast_channel.d.ts", + "broadcast_channel/lib.deno_broadcast_channel.d.ts" + ), + maybe_compressed_ext_lib!("lib.deno.net.d.ts", "net/lib.deno_net.d.ts"), + maybe_compressed_ext_lib!( + "lib.deno.cache.d.ts", + "cache/lib.deno_cache.d.ts" + ), + maybe_compressed_lib!("lib.deno.webgpu.d.ts", "lib.deno_webgpu.d.ts"), + maybe_compressed_lib!("lib.deno.window.d.ts"), + maybe_compressed_lib!("lib.deno.worker.d.ts"), + maybe_compressed_lib!("lib.deno.shared_globals.d.ts"), + maybe_compressed_lib!("lib.deno.ns.d.ts"), + maybe_compressed_lib!("lib.deno.unstable.d.ts"), + maybe_compressed_lib!("lib.decorators.d.ts"), + maybe_compressed_lib!("lib.decorators.legacy.d.ts"), + maybe_compressed_lib!("lib.dom.asynciterable.d.ts"), + maybe_compressed_lib!("lib.dom.d.ts"), + maybe_compressed_lib!("lib.dom.extras.d.ts"), + maybe_compressed_lib!("lib.dom.iterable.d.ts"), + maybe_compressed_lib!("lib.es2015.collection.d.ts"), + maybe_compressed_lib!("lib.es2015.core.d.ts"), + maybe_compressed_lib!("lib.es2015.d.ts"), + maybe_compressed_lib!("lib.es2015.generator.d.ts"), + maybe_compressed_lib!("lib.es2015.iterable.d.ts"), + maybe_compressed_lib!("lib.es2015.promise.d.ts"), + maybe_compressed_lib!("lib.es2015.proxy.d.ts"), + maybe_compressed_lib!("lib.es2015.reflect.d.ts"), + maybe_compressed_lib!("lib.es2015.symbol.d.ts"), + maybe_compressed_lib!("lib.es2015.symbol.wellknown.d.ts"), + maybe_compressed_lib!("lib.es2016.array.include.d.ts"), + maybe_compressed_lib!("lib.es2016.d.ts"), + maybe_compressed_lib!("lib.es2016.full.d.ts"), + maybe_compressed_lib!("lib.es2016.intl.d.ts"), + maybe_compressed_lib!("lib.es2017.arraybuffer.d.ts"), + maybe_compressed_lib!("lib.es2017.d.ts"), + maybe_compressed_lib!("lib.es2017.date.d.ts"), + maybe_compressed_lib!("lib.es2017.full.d.ts"), + maybe_compressed_lib!("lib.es2017.intl.d.ts"), + maybe_compressed_lib!("lib.es2017.object.d.ts"), + maybe_compressed_lib!("lib.es2017.sharedmemory.d.ts"), + maybe_compressed_lib!("lib.es2017.string.d.ts"), + maybe_compressed_lib!("lib.es2017.typedarrays.d.ts"), + maybe_compressed_lib!("lib.es2018.asyncgenerator.d.ts"), + maybe_compressed_lib!("lib.es2018.asynciterable.d.ts"), + maybe_compressed_lib!("lib.es2018.d.ts"), + maybe_compressed_lib!("lib.es2018.full.d.ts"), + maybe_compressed_lib!("lib.es2018.intl.d.ts"), + maybe_compressed_lib!("lib.es2018.promise.d.ts"), + maybe_compressed_lib!("lib.es2018.regexp.d.ts"), + maybe_compressed_lib!("lib.es2019.array.d.ts"), + maybe_compressed_lib!("lib.es2019.d.ts"), + maybe_compressed_lib!("lib.es2019.full.d.ts"), + maybe_compressed_lib!("lib.es2019.intl.d.ts"), + maybe_compressed_lib!("lib.es2019.object.d.ts"), + maybe_compressed_lib!("lib.es2019.string.d.ts"), + maybe_compressed_lib!("lib.es2019.symbol.d.ts"), + maybe_compressed_lib!("lib.es2020.bigint.d.ts"), + maybe_compressed_lib!("lib.es2020.d.ts"), + maybe_compressed_lib!("lib.es2020.date.d.ts"), + maybe_compressed_lib!("lib.es2020.full.d.ts"), + maybe_compressed_lib!("lib.es2020.intl.d.ts"), + maybe_compressed_lib!("lib.es2020.number.d.ts"), + maybe_compressed_lib!("lib.es2020.promise.d.ts"), + maybe_compressed_lib!("lib.es2020.sharedmemory.d.ts"), + maybe_compressed_lib!("lib.es2020.string.d.ts"), + maybe_compressed_lib!("lib.es2020.symbol.wellknown.d.ts"), + maybe_compressed_lib!("lib.es2021.d.ts"), + maybe_compressed_lib!("lib.es2021.full.d.ts"), + maybe_compressed_lib!("lib.es2021.intl.d.ts"), + maybe_compressed_lib!("lib.es2021.promise.d.ts"), + maybe_compressed_lib!("lib.es2021.string.d.ts"), + maybe_compressed_lib!("lib.es2021.weakref.d.ts"), + maybe_compressed_lib!("lib.es2022.array.d.ts"), + maybe_compressed_lib!("lib.es2022.d.ts"), + maybe_compressed_lib!("lib.es2022.error.d.ts"), + maybe_compressed_lib!("lib.es2022.full.d.ts"), + maybe_compressed_lib!("lib.es2022.intl.d.ts"), + maybe_compressed_lib!("lib.es2022.object.d.ts"), + maybe_compressed_lib!("lib.es2022.regexp.d.ts"), + maybe_compressed_lib!("lib.es2022.string.d.ts"), + maybe_compressed_lib!("lib.es2023.array.d.ts"), + maybe_compressed_lib!("lib.es2023.collection.d.ts"), + maybe_compressed_lib!("lib.es2023.d.ts"), + maybe_compressed_lib!("lib.es2023.full.d.ts"), + maybe_compressed_lib!("lib.es2023.intl.d.ts"), + maybe_compressed_lib!("lib.es2024.arraybuffer.d.ts"), + maybe_compressed_lib!("lib.es2024.collection.d.ts"), + maybe_compressed_lib!("lib.es2024.d.ts"), + maybe_compressed_lib!("lib.es2024.full.d.ts"), + maybe_compressed_lib!("lib.es2024.object.d.ts"), + maybe_compressed_lib!("lib.es2024.promise.d.ts"), + maybe_compressed_lib!("lib.es2024.regexp.d.ts"), + maybe_compressed_lib!("lib.es2024.sharedmemory.d.ts"), + maybe_compressed_lib!("lib.es2024.string.d.ts"), + maybe_compressed_lib!("lib.es5.d.ts"), + maybe_compressed_lib!("lib.es6.d.ts"), + maybe_compressed_lib!("lib.esnext.array.d.ts"), + maybe_compressed_lib!("lib.esnext.collection.d.ts"), + maybe_compressed_lib!("lib.esnext.d.ts"), + maybe_compressed_lib!("lib.esnext.decorators.d.ts"), + maybe_compressed_lib!("lib.esnext.disposable.d.ts"), + maybe_compressed_lib!("lib.esnext.full.d.ts"), + maybe_compressed_lib!("lib.esnext.intl.d.ts"), + maybe_compressed_lib!("lib.esnext.iterator.d.ts"), + maybe_compressed_lib!("lib.scripthost.d.ts"), + maybe_compressed_lib!("lib.webworker.asynciterable.d.ts"), + maybe_compressed_lib!("lib.webworker.d.ts"), + maybe_compressed_lib!("lib.webworker.importscripts.d.ts"), + maybe_compressed_lib!("lib.webworker.iterable.d.ts"), ( // Special file that can be used to inject the @types/node package. // This is used for `node:` specifiers. "node_types.d.ts", - "/// \n", + StaticAssetSource::Uncompressed( + "/// \n", + ), ), ]) .iter() @@ -245,8 +438,8 @@ pub struct AssetText { } /// Retrieve a static asset that are included in the binary. -fn get_lazily_loaded_asset(asset: &str) -> Option<&'static str> { - LAZILY_LOADED_STATIC_ASSETS.get(asset).map(|s| s.to_owned()) +fn get_lazily_loaded_asset(asset: &str) -> Option { + LAZILY_LOADED_STATIC_ASSETS.get(asset).map(|s| s.get()) } fn get_maybe_hash( @@ -603,10 +796,10 @@ fn op_load_inner( } else if load_specifier == MISSING_DEPENDENCY_SPECIFIER { None } else if let Some(name) = load_specifier.strip_prefix("asset:///") { - let maybe_source = get_lazily_loaded_asset(name); - hash = get_maybe_hash(maybe_source, state.hash_data); + let maybe_source = get_lazily_loaded_asset(name).map(Cow::from); + hash = get_maybe_hash(maybe_source.as_deref(), state.hash_data); media_type = MediaType::from_str(load_specifier); - maybe_source.map(Cow::Borrowed) + maybe_source } else { let specifier = if let Some(remapped_specifier) = state.maybe_remapped_specifier(load_specifier) @@ -748,6 +941,20 @@ fn op_remap_specifier( .map(|url| url.to_string()) } +#[op2] +#[serde] +fn op_libs() -> Vec { + let mut out = Vec::with_capacity(LAZILY_LOADED_STATIC_ASSETS.len()); + for key in LAZILY_LOADED_STATIC_ASSETS.keys() { + let lib = key + .replace("lib.", "") + .replace(".d.ts", "") + .replace("deno_", "deno."); + out.push(lib); + } + out +} + #[op2] #[serde] fn op_resolve( @@ -1087,6 +1294,84 @@ pub enum ExecError { Core(deno_core::error::CoreError), } +#[derive(Clone)] +pub(crate) struct CompressedSource { + bytes: &'static [u8], + uncompressed: OnceLock>, +} + +impl CompressedSource { + #[cfg_attr(debug_assertions, allow(dead_code))] + pub(crate) const fn new(bytes: &'static [u8]) -> Self { + Self { + bytes, + uncompressed: OnceLock::new(), + } + } + pub(crate) fn get(&self) -> Arc { + self + .uncompressed + .get_or_init(|| decompress_source(self.bytes)) + .clone() + } +} + +pub(crate) static MAIN_COMPILER_SOURCE: StaticAssetSource = + maybe_compressed_source!("tsc/99_main_compiler.js"); +pub(crate) static LSP_SOURCE: StaticAssetSource = + maybe_compressed_source!("tsc/98_lsp.js"); +pub(crate) static TS_HOST_SOURCE: StaticAssetSource = + maybe_compressed_source!("tsc/97_ts_host.js"); +pub(crate) static TYPESCRIPT_SOURCE: StaticAssetSource = + maybe_compressed_source!("tsc/00_typescript.js"); + +pub(crate) fn decompress_source(contents: &[u8]) -> Arc { + let len_bytes = contents[0..4].try_into().unwrap(); + let len = u32::from_le_bytes(len_bytes); + let uncompressed = + zstd::bulk::decompress(&contents[4..], len as usize).unwrap(); + String::from_utf8(uncompressed).unwrap().into() +} + +deno_core::extension!(deno_cli_tsc, + ops = [ + op_create_hash, + op_emit, + op_is_node_file, + op_load, + op_remap_specifier, + op_resolve, + op_respond, + op_libs, + ], + options = { + request: Request, + root_map: HashMap, + remapped_specifiers: HashMap, + }, + state = |state, options| { + state.put(State::new( + options.request.graph, + options.request.hash_data, + options.request.maybe_npm, + options.request.maybe_tsbuildinfo, + options.root_map, + options.remapped_specifiers, + std::env::current_dir() + .context("Unable to get CWD") + .unwrap(), + )); + }, + customizer = |ext: &mut deno_core::Extension| { + use deno_core::ExtensionFileSource; + ext.esm_files.to_mut().push(ExtensionFileSource::new_computed("ext:deno_cli_tsc/99_main_compiler.js", crate::tsc::MAIN_COMPILER_SOURCE.get().into())); + ext.esm_files.to_mut().push(ExtensionFileSource::new_computed("ext:deno_cli_tsc/97_ts_host.js", crate::tsc::TS_HOST_SOURCE.get().into())); + ext.esm_files.to_mut().push(ExtensionFileSource::new_computed("ext:deno_cli_tsc/98_lsp.js", crate::tsc::LSP_SOURCE.get().into())); + ext.js_files.to_mut().push(ExtensionFileSource::new_computed("ext:deno_cli_tsc/00_typescript.js", crate::tsc::TYPESCRIPT_SOURCE.get().into())); + ext.esm_entry_point = Some("ext:deno_cli_tsc/99_main_compiler.js"); + } +); + /// Execute a request on the supplied snapshot, returning a response which /// contains information, like any emitted files, diagnostics, statistics and /// optionally an updated TypeScript build info. @@ -1117,36 +1402,6 @@ pub fn exec(request: Request) -> Result { }) .collect(); - deno_core::extension!(deno_cli_tsc, - ops = [ - op_create_hash, - op_emit, - op_is_node_file, - op_load, - op_remap_specifier, - op_resolve, - op_respond, - ], - options = { - request: Request, - root_map: HashMap, - remapped_specifiers: HashMap, - }, - state = |state, options| { - state.put(State::new( - options.request.graph, - options.request.hash_data, - options.request.maybe_npm, - options.request.maybe_tsbuildinfo, - options.root_map, - options.remapped_specifiers, - std::env::current_dir() - .context("Unable to get CWD") - .unwrap(), - )); - }, - ); - let request_value = json!({ "config": request.config, "debug": request.debug, @@ -1156,8 +1411,7 @@ pub fn exec(request: Request) -> Result { let exec_source = format!("globalThis.exec({request_value})"); let mut runtime = JsRuntime::new(RuntimeOptions { - startup_snapshot: Some(compiler_snapshot()), - extensions: vec![deno_cli_tsc::init_ops( + extensions: vec![deno_cli_tsc::init_ops_and_esm( request, root_map, remapped_specifiers, @@ -1319,7 +1573,21 @@ mod tests { #[tokio::test] async fn test_compiler_snapshot() { let mut js_runtime = JsRuntime::new(RuntimeOptions { - startup_snapshot: Some(compiler_snapshot()), + startup_snapshot: None, + extensions: vec![super::deno_cli_tsc::init_ops_and_esm( + Request { + check_mode: TypeCheckMode::All, + config: Arc::new(TsConfig(json!({}))), + debug: false, + graph: Arc::new(ModuleGraph::new(GraphKind::TypesOnly)), + hash_data: 0, + maybe_npm: None, + maybe_tsbuildinfo: None, + root_names: vec![], + }, + HashMap::new(), + HashMap::new(), + )], ..Default::default() }); js_runtime @@ -1402,7 +1670,7 @@ mod tests { .expect("should have invoked op") .expect("load should have succeeded"); let expected = get_lazily_loaded_asset("lib.dom.d.ts").unwrap(); - assert_eq!(actual.data, expected); + assert_eq!(actual.data, expected.to_string()); assert!(actual.version.is_some()); assert_eq!(actual.script_kind, 3); } diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index 39f828db20..b526004a55 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -11612,14 +11612,12 @@ fn lsp_performance() { "lsp.update_diagnostics_lint", "lsp.update_diagnostics_ts", "lsp.update_global_cache", - "tsc.host.$getAssets", "tsc.host.$getDiagnostics", "tsc.host.$getSupportedCodeFixes", "tsc.host.getQuickInfoAtPosition", "tsc.op.op_is_node_file", "tsc.op.op_load", "tsc.op.op_script_names", - "tsc.request.$getAssets", "tsc.request.$getDiagnostics", "tsc.request.$getSupportedCodeFixes", "tsc.request.getQuickInfoAtPosition",