0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-02-13 01:06:00 -05:00

refactor: remove tsc snapshot (#28056)

This reverts commit acdc7dcdcf
and relands 174e496847.

Closes https://github.com/denoland/deno/issues/28047
Closes https://github.com/denoland/deno/issues/28044
This commit is contained in:
Bartek Iwańczuk 2025-02-11 15:36:07 +01:00 committed by GitHub
parent 07410d19cf
commit 23540c2825
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 599 additions and 580 deletions

View file

@ -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<LoadResponse, JsErrorBox> {
let op_crate_libs = state.borrow::<HashMap<&str, PathBuf>>();
let path_dts = state.borrow::<PathBuf>();
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<v8::Function> =
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")
}

View file

@ -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 {

View file

@ -1399,7 +1399,7 @@ fn new_assets_map() -> Arc<Mutex<AssetsMap>> {
.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::<AssetsMap>();
@ -1430,29 +1430,16 @@ impl AssetsSnapshot {
/// multiple threads without needing to worry about race conditions.
#[derive(Debug, Clone)]
pub struct Assets {
ts_server: Arc<TsServer>,
assets: Arc<Mutex<AssetsMap>>,
}
impl Assets {
pub fn new(ts_server: Arc<TsServer>) -> 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<StateSnapshot>) {
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<StateSnapshot>,
) -> Vec<AssetDocument> {
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<String> {
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<Performance>,
@ -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<String>, 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::<Vec<_>>();
let mut expected_asset_names: Vec<String> = 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(

View file

@ -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;
}

View file

@ -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

View file

@ -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.

View file

@ -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<Box<[u8]>> = 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<Vec<AssetText>, 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::<Vec<AssetText>>(scope, local)?)
fn get_asset_texts() -> Result<Vec<AssetText>, 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<str>` instead of `String`
#[derive(Debug, Clone)]
pub enum MaybeStaticSource {
Computed(Arc<str>),
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<MaybeStaticSource> 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<str> for MaybeStaticSource {
fn as_ref(&self) -> &str {
match self {
MaybeStaticSource::Computed(arc) => arc.as_ref(),
MaybeStaticSource::Static(s) => s,
}
}
}
impl From<MaybeStaticSource> for String {
fn from(value: MaybeStaticSource) -> Self {
match value {
MaybeStaticSource::Computed(arc) => arc.to_string(),
MaybeStaticSource::Static(s) => s.into(),
}
}
}
impl From<MaybeStaticSource> for Arc<str> {
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",
"/// <reference types=\"npm:@types/node\" />\n",
StaticAssetSource::Uncompressed(
"/// <reference types=\"npm:@types/node\" />\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<MaybeStaticSource> {
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<String> {
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<Arc<str>>,
}
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<str> {
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<str> {
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<String, Url>,
remapped_specifiers: HashMap<String, Url>,
},
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<Response, ExecError> {
})
.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<String, Url>,
remapped_specifiers: HashMap<String, Url>,
},
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<Response, ExecError> {
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);
}

View file

@ -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",