mirror of
https://github.com/denoland/deno.git
synced 2025-01-11 08:33:43 -05:00
refactor(cli): cleanup compiler snapshot and tsc/module_graph (#8220)
This commit is contained in:
parent
40cd4db974
commit
d672e1405d
10 changed files with 397 additions and 673 deletions
|
@ -23,6 +23,8 @@ path = "./bench/main.rs"
|
|||
deno_core = { path = "../core", version = "0.66.0" }
|
||||
deno_web = { path = "../op_crates/web", version = "0.17.0" }
|
||||
deno_fetch = { path = "../op_crates/fetch", version = "0.9.0" }
|
||||
regex = "1.3.9"
|
||||
serde = { version = "1.0.116", features = ["derive"] }
|
||||
|
||||
[target.'cfg(windows)'.build-dependencies]
|
||||
winres = "0.1.11"
|
||||
|
|
169
cli/build.rs
169
cli/build.rs
|
@ -1,9 +1,13 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
mod op_fetch_asset;
|
||||
|
||||
use deno_core::error::custom_error;
|
||||
use deno_core::json_op_sync;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::JsRuntime;
|
||||
use deno_core::RuntimeOptions;
|
||||
use regex::Regex;
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
|
@ -46,46 +50,149 @@ fn create_runtime_snapshot(snapshot_path: &Path, files: Vec<PathBuf>) {
|
|||
create_snapshot(js_runtime, snapshot_path, files);
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct LoadArgs {
|
||||
/// The fully qualified specifier that should be loaded.
|
||||
specifier: String,
|
||||
}
|
||||
|
||||
fn create_compiler_snapshot(
|
||||
snapshot_path: &Path,
|
||||
files: Vec<PathBuf>,
|
||||
cwd: &Path,
|
||||
) {
|
||||
let mut custom_libs: HashMap<String, PathBuf> = HashMap::new();
|
||||
custom_libs
|
||||
.insert("lib.deno.web.d.ts".to_string(), deno_web::get_declaration());
|
||||
custom_libs.insert(
|
||||
"lib.deno.fetch.d.ts".to_string(),
|
||||
deno_fetch::get_declaration(),
|
||||
);
|
||||
custom_libs.insert(
|
||||
"lib.deno.window.d.ts".to_string(),
|
||||
cwd.join("dts/lib.deno.window.d.ts"),
|
||||
);
|
||||
custom_libs.insert(
|
||||
"lib.deno.worker.d.ts".to_string(),
|
||||
cwd.join("dts/lib.deno.worker.d.ts"),
|
||||
);
|
||||
custom_libs.insert(
|
||||
"lib.deno.shared_globals.d.ts".to_string(),
|
||||
cwd.join("dts/lib.deno.shared_globals.d.ts"),
|
||||
);
|
||||
custom_libs.insert(
|
||||
"lib.deno.ns.d.ts".to_string(),
|
||||
cwd.join("dts/lib.deno.ns.d.ts"),
|
||||
);
|
||||
custom_libs.insert(
|
||||
"lib.deno.unstable.d.ts".to_string(),
|
||||
cwd.join("dts/lib.deno.unstable.d.ts"),
|
||||
);
|
||||
// libs that are being provided by op crates.
|
||||
let mut op_crate_libs = HashMap::new();
|
||||
op_crate_libs.insert("deno.web", deno_web::get_declaration());
|
||||
op_crate_libs.insert("deno.fetch", deno_fetch::get_declaration());
|
||||
|
||||
// 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
|
||||
"es5",
|
||||
"es2015.collection",
|
||||
"es2015.core",
|
||||
"es2015",
|
||||
"es2015.generator",
|
||||
"es2015.iterable",
|
||||
"es2015.promise",
|
||||
"es2015.proxy",
|
||||
"es2015.reflect",
|
||||
"es2015.symbol",
|
||||
"es2015.symbol.wellknown",
|
||||
"es2016.array.include",
|
||||
"es2016",
|
||||
"es2017",
|
||||
"es2017.intl",
|
||||
"es2017.object",
|
||||
"es2017.sharedmemory",
|
||||
"es2017.string",
|
||||
"es2017.typedarrays",
|
||||
"es2018.asyncgenerator",
|
||||
"es2018.asynciterable",
|
||||
"es2018",
|
||||
"es2018.intl",
|
||||
"es2018.promise",
|
||||
"es2018.regexp",
|
||||
"es2019.array",
|
||||
"es2019",
|
||||
"es2019.object",
|
||||
"es2019.string",
|
||||
"es2019.symbol",
|
||||
"es2020.bigint",
|
||||
"es2020",
|
||||
"es2020.intl",
|
||||
"es2020.promise",
|
||||
"es2020.string",
|
||||
"es2020.symbol.wellknown",
|
||||
"esnext",
|
||||
"esnext.intl",
|
||||
"esnext.promise",
|
||||
"esnext.string",
|
||||
"esnext.weakref",
|
||||
];
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
let re_asset = Regex::new(r"asset:/{3}lib\.(\S+)\.d\.ts").expect("bad regex");
|
||||
let path_dts = cwd.join("dts");
|
||||
let build_specifier = "asset:///bootstrap.ts";
|
||||
|
||||
let mut js_runtime = JsRuntime::new(RuntimeOptions {
|
||||
will_snapshot: true,
|
||||
..Default::default()
|
||||
});
|
||||
js_runtime.register_op(
|
||||
"op_fetch_asset",
|
||||
op_fetch_asset::op_fetch_asset(custom_libs),
|
||||
"op_build_info",
|
||||
json_op_sync(move |_state, _args, _bufs| {
|
||||
Ok(json!({
|
||||
"buildSpecifier": build_specifier,
|
||||
"libs": build_libs,
|
||||
}))
|
||||
}),
|
||||
);
|
||||
// using the same op that is used in `tsc.rs` for loading modules and reading
|
||||
// files, but a slightly different implementation at build time.
|
||||
js_runtime.register_op(
|
||||
"op_load",
|
||||
json_op_sync(move |_state, args, _bufs| {
|
||||
let v: LoadArgs = serde_json::from_value(args)?;
|
||||
// we need a basic file to send to tsc to warm it up.
|
||||
if v.specifier == build_specifier {
|
||||
Ok(json!({
|
||||
"data": r#"console.log("hello deno!");"#,
|
||||
"hash": "1",
|
||||
// this corresponds to `ts.ScriptKind.TypeScript`
|
||||
"scriptKind": 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(&v.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) {
|
||||
op_crate_lib.clone()
|
||||
// otherwise we are will generate the path ourself
|
||||
} else {
|
||||
path_dts.join(format!("lib.{}.d.ts", lib))
|
||||
};
|
||||
let data = std::fs::read_to_string(path)?;
|
||||
Ok(json!({
|
||||
"data": data,
|
||||
"hash": "1",
|
||||
// this corresponds to `ts.ScriptKind.TypeScript`
|
||||
"scriptKind": 3
|
||||
}))
|
||||
} else {
|
||||
Err(custom_error(
|
||||
"InvalidSpecifier",
|
||||
format!("An invalid specifier was requested: {}", v.specifier),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Err(custom_error(
|
||||
"InvalidSpecifier",
|
||||
format!("An invalid specifier was requested: {}", v.specifier),
|
||||
))
|
||||
}
|
||||
}),
|
||||
);
|
||||
create_snapshot(js_runtime, snapshot_path, files);
|
||||
}
|
||||
|
|
23
cli/main.rs
23
cli/main.rs
|
@ -35,9 +35,8 @@ mod lint;
|
|||
mod lockfile;
|
||||
mod media_type;
|
||||
mod metrics;
|
||||
mod module_graph2;
|
||||
mod module_graph;
|
||||
mod module_loader;
|
||||
mod op_fetch_asset;
|
||||
mod ops;
|
||||
mod permissions;
|
||||
mod program_state;
|
||||
|
@ -49,7 +48,7 @@ mod specifier_handler;
|
|||
mod test_runner;
|
||||
mod text_encoding;
|
||||
mod tokio_util;
|
||||
mod tsc2;
|
||||
mod tsc;
|
||||
mod tsc_config;
|
||||
mod upgrade;
|
||||
mod version;
|
||||
|
@ -177,7 +176,7 @@ async fn info_command(
|
|||
// so we allow access to all of them.
|
||||
Permissions::allow_all(),
|
||||
)?));
|
||||
let mut builder = module_graph2::GraphBuilder2::new(
|
||||
let mut builder = module_graph::GraphBuilder::new(
|
||||
handler,
|
||||
program_state.maybe_import_map.clone(),
|
||||
program_state.lockfile.clone(),
|
||||
|
@ -241,9 +240,9 @@ async fn cache_command(
|
|||
files: Vec<String>,
|
||||
) -> Result<(), AnyError> {
|
||||
let lib = if flags.unstable {
|
||||
module_graph2::TypeLib::UnstableDenoWindow
|
||||
module_graph::TypeLib::UnstableDenoWindow
|
||||
} else {
|
||||
module_graph2::TypeLib::DenoWindow
|
||||
module_graph::TypeLib::DenoWindow
|
||||
};
|
||||
let program_state = ProgramState::new(flags)?;
|
||||
|
||||
|
@ -329,7 +328,7 @@ async fn bundle_command(
|
|||
// therefore we will allow the graph to access any module.
|
||||
Permissions::allow_all(),
|
||||
)?));
|
||||
let mut builder = module_graph2::GraphBuilder2::new(
|
||||
let mut builder = module_graph::GraphBuilder::new(
|
||||
handler,
|
||||
program_state.maybe_import_map.clone(),
|
||||
program_state.lockfile.clone(),
|
||||
|
@ -341,12 +340,12 @@ async fn bundle_command(
|
|||
if !flags.no_check {
|
||||
// TODO(@kitsonk) support bundling for workers
|
||||
let lib = if flags.unstable {
|
||||
module_graph2::TypeLib::UnstableDenoWindow
|
||||
module_graph::TypeLib::UnstableDenoWindow
|
||||
} else {
|
||||
module_graph2::TypeLib::DenoWindow
|
||||
module_graph::TypeLib::DenoWindow
|
||||
};
|
||||
let graph = graph.clone();
|
||||
let result_info = graph.check(module_graph2::CheckOptions {
|
||||
let result_info = graph.check(module_graph::CheckOptions {
|
||||
debug,
|
||||
emit: false,
|
||||
lib,
|
||||
|
@ -364,7 +363,7 @@ async fn bundle_command(
|
|||
}
|
||||
|
||||
let (output, stats, maybe_ignored_options) =
|
||||
graph.bundle(module_graph2::BundleOptions {
|
||||
graph.bundle(module_graph::BundleOptions {
|
||||
debug,
|
||||
maybe_config_path: flags.config_path,
|
||||
})?;
|
||||
|
@ -563,7 +562,7 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<(), AnyError> {
|
|||
&program_state,
|
||||
Permissions::allow_all(),
|
||||
)?));
|
||||
let mut builder = module_graph2::GraphBuilder2::new(
|
||||
let mut builder = module_graph::GraphBuilder::new(
|
||||
handler,
|
||||
program_state.maybe_import_map.clone(),
|
||||
program_state.lockfile.clone(),
|
||||
|
|
|
@ -22,7 +22,7 @@ use crate::specifier_handler::DependencyMap;
|
|||
use crate::specifier_handler::Emit;
|
||||
use crate::specifier_handler::FetchFuture;
|
||||
use crate::specifier_handler::SpecifierHandler;
|
||||
use crate::tsc2;
|
||||
use crate::tsc;
|
||||
use crate::tsc_config::IgnoredCompilerOptions;
|
||||
use crate::tsc_config::TsConfig;
|
||||
use crate::version;
|
||||
|
@ -122,12 +122,12 @@ struct BundleLoader<'a> {
|
|||
cm: Rc<swc_common::SourceMap>,
|
||||
emit_options: &'a ast::EmitOptions,
|
||||
globals: &'a swc_common::Globals,
|
||||
graph: &'a Graph2,
|
||||
graph: &'a Graph,
|
||||
}
|
||||
|
||||
impl<'a> BundleLoader<'a> {
|
||||
pub fn new(
|
||||
graph: &'a Graph2,
|
||||
graph: &'a Graph,
|
||||
emit_options: &'a ast::EmitOptions,
|
||||
globals: &'a swc_common::Globals,
|
||||
cm: Rc<swc_common::SourceMap>,
|
||||
|
@ -632,7 +632,7 @@ pub struct TranspileOptions {
|
|||
/// the builder will be loaded into the graph. Also provides an interface to
|
||||
/// be able to manipulate and handle the graph.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Graph2 {
|
||||
pub struct Graph {
|
||||
/// A reference to the specifier handler that will retrieve and cache modules
|
||||
/// for the graph.
|
||||
handler: Rc<RefCell<dyn SpecifierHandler>>,
|
||||
|
@ -658,7 +658,7 @@ pub struct Graph2 {
|
|||
maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
|
||||
}
|
||||
|
||||
impl Graph2 {
|
||||
impl Graph {
|
||||
/// Create a new instance of a graph, ready to have modules loaded it.
|
||||
///
|
||||
/// The argument `handler` is an instance of a structure that implements the
|
||||
|
@ -668,7 +668,7 @@ impl Graph2 {
|
|||
handler: Rc<RefCell<dyn SpecifierHandler>>,
|
||||
maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
|
||||
) -> Self {
|
||||
Graph2 {
|
||||
Graph {
|
||||
handler,
|
||||
maybe_tsbuildinfo: None,
|
||||
modules: HashMap::new(),
|
||||
|
@ -776,9 +776,9 @@ impl Graph2 {
|
|||
vec![config.as_bytes(), version::DENO.as_bytes().to_owned()];
|
||||
let graph = Rc::new(RefCell::new(self));
|
||||
|
||||
let response = tsc2::exec(
|
||||
let response = tsc::exec(
|
||||
js::compiler_isolate_init(),
|
||||
tsc2::Request {
|
||||
tsc::Request {
|
||||
config: config.clone(),
|
||||
debug: options.debug,
|
||||
graph: graph.clone(),
|
||||
|
@ -897,9 +897,9 @@ impl Graph2 {
|
|||
vec![config.as_bytes(), version::DENO.as_bytes().to_owned()];
|
||||
let graph = Rc::new(RefCell::new(self));
|
||||
|
||||
let response = tsc2::exec(
|
||||
let response = tsc::exec(
|
||||
js::compiler_isolate_init(),
|
||||
tsc2::Request {
|
||||
tsc::Request {
|
||||
config: config.clone(),
|
||||
debug: options.debug,
|
||||
graph: graph.clone(),
|
||||
|
@ -987,7 +987,7 @@ impl Graph2 {
|
|||
);
|
||||
let output = bundler
|
||||
.bundle(entries)
|
||||
.context("Unable to output bundle during Graph2::bundle().")?;
|
||||
.context("Unable to output bundle during Graph::bundle().")?;
|
||||
let mut buf = Vec::new();
|
||||
{
|
||||
let mut emitter = swc_ecmascript::codegen::Emitter {
|
||||
|
@ -1001,7 +1001,7 @@ impl Graph2 {
|
|||
|
||||
emitter
|
||||
.emit_module(&output[0].module)
|
||||
.context("Unable to emit bundle during Graph2::bundle().")?;
|
||||
.context("Unable to emit bundle during Graph::bundle().")?;
|
||||
}
|
||||
|
||||
String::from_utf8(buf).context("Emitted bundle is an invalid utf-8 string.")
|
||||
|
@ -1437,7 +1437,7 @@ impl Graph2 {
|
|||
}
|
||||
}
|
||||
|
||||
impl swc_bundler::Resolve for Graph2 {
|
||||
impl swc_bundler::Resolve for Graph {
|
||||
fn resolve(
|
||||
&self,
|
||||
referrer: &swc_common::FileName,
|
||||
|
@ -1459,14 +1459,14 @@ impl swc_bundler::Resolve for Graph2 {
|
|||
}
|
||||
|
||||
/// A structure for building a dependency graph of modules.
|
||||
pub struct GraphBuilder2 {
|
||||
pub struct GraphBuilder {
|
||||
fetched: HashSet<ModuleSpecifier>,
|
||||
graph: Graph2,
|
||||
graph: Graph,
|
||||
maybe_import_map: Option<Rc<RefCell<ImportMap>>>,
|
||||
pending: FuturesUnordered<FetchFuture>,
|
||||
}
|
||||
|
||||
impl GraphBuilder2 {
|
||||
impl GraphBuilder {
|
||||
pub fn new(
|
||||
handler: Rc<RefCell<dyn SpecifierHandler>>,
|
||||
maybe_import_map: Option<ImportMap>,
|
||||
|
@ -1477,8 +1477,8 @@ impl GraphBuilder2 {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
GraphBuilder2 {
|
||||
graph: Graph2::new(handler, maybe_lockfile),
|
||||
GraphBuilder {
|
||||
graph: Graph::new(handler, maybe_lockfile),
|
||||
fetched: HashSet::new(),
|
||||
maybe_import_map: internal_import_map,
|
||||
pending: FuturesUnordered::new(),
|
||||
|
@ -1605,7 +1605,7 @@ impl GraphBuilder2 {
|
|||
/// Move out the graph from the builder to be utilized further. An optional
|
||||
/// lockfile can be provided, where if the sources in the graph do not match
|
||||
/// the expected lockfile, an error will be logged and the process will exit.
|
||||
pub fn get_graph(self) -> Graph2 {
|
||||
pub fn get_graph(self) -> Graph {
|
||||
self.graph.lock();
|
||||
self.graph
|
||||
}
|
||||
|
@ -1737,14 +1737,14 @@ pub mod tests {
|
|||
|
||||
async fn setup(
|
||||
specifier: ModuleSpecifier,
|
||||
) -> (Graph2, Rc<RefCell<MockSpecifierHandler>>) {
|
||||
) -> (Graph, Rc<RefCell<MockSpecifierHandler>>) {
|
||||
let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
|
||||
let fixtures = c.join("tests/module_graph");
|
||||
let handler = Rc::new(RefCell::new(MockSpecifierHandler {
|
||||
fixtures,
|
||||
..MockSpecifierHandler::default()
|
||||
}));
|
||||
let mut builder = GraphBuilder2::new(handler.clone(), None, None);
|
||||
let mut builder = GraphBuilder::new(handler.clone(), None, None);
|
||||
builder
|
||||
.add(&specifier, false)
|
||||
.await
|
||||
|
@ -1756,13 +1756,13 @@ pub mod tests {
|
|||
async fn setup_memory(
|
||||
specifier: ModuleSpecifier,
|
||||
sources: HashMap<&str, &str>,
|
||||
) -> Graph2 {
|
||||
) -> Graph {
|
||||
let sources: HashMap<String, String> = sources
|
||||
.iter()
|
||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||
.collect();
|
||||
let handler = Rc::new(RefCell::new(MemoryHandler::new(sources)));
|
||||
let mut builder = GraphBuilder2::new(handler.clone(), None, None);
|
||||
let mut builder = GraphBuilder::new(handler.clone(), None, None);
|
||||
builder
|
||||
.add(&specifier, false)
|
||||
.await
|
||||
|
@ -1870,7 +1870,7 @@ pub mod tests {
|
|||
fixtures: fixtures.clone(),
|
||||
..MockSpecifierHandler::default()
|
||||
}));
|
||||
let mut builder = GraphBuilder2::new(handler.clone(), None, None);
|
||||
let mut builder = GraphBuilder::new(handler.clone(), None, None);
|
||||
builder
|
||||
.add(&specifier, false)
|
||||
.await
|
||||
|
@ -1904,6 +1904,7 @@ pub mod tests {
|
|||
.expect("should have checked");
|
||||
assert!(result_info.maybe_ignored_options.is_none());
|
||||
assert_eq!(result_info.stats.0.len(), 12);
|
||||
println!("{}", result_info.diagnostics);
|
||||
assert!(result_info.diagnostics.is_empty());
|
||||
let h = handler.borrow();
|
||||
assert_eq!(h.cache_calls.len(), 2);
|
||||
|
@ -2084,7 +2085,7 @@ pub mod tests {
|
|||
fixtures,
|
||||
..MockSpecifierHandler::default()
|
||||
}));
|
||||
let mut builder = GraphBuilder2::new(handler.clone(), None, None);
|
||||
let mut builder = GraphBuilder::new(handler.clone(), None, None);
|
||||
builder
|
||||
.add(&specifier, false)
|
||||
.await
|
||||
|
@ -2195,7 +2196,7 @@ pub mod tests {
|
|||
fixtures,
|
||||
..MockSpecifierHandler::default()
|
||||
}));
|
||||
let mut builder = GraphBuilder2::new(handler.clone(), None, maybe_lockfile);
|
||||
let mut builder = GraphBuilder::new(handler.clone(), None, maybe_lockfile);
|
||||
let specifier =
|
||||
ModuleSpecifier::resolve_url_or_path("file:///tests/main.ts")
|
||||
.expect("could not resolve module");
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::import_map::ImportMap;
|
||||
use crate::module_graph2::TypeLib;
|
||||
use crate::module_graph::TypeLib;
|
||||
use crate::permissions::Permissions;
|
||||
use crate::program_state::ProgramState;
|
||||
use deno_core::error::AnyError;
|
||||
|
|
|
@ -1,115 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
// Note: this module is used both in build.rs and main.rs.
|
||||
|
||||
pub use deno_core::v8_set_flags;
|
||||
use deno_core::BufVec;
|
||||
use deno_core::Op;
|
||||
use deno_core::OpState;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub fn get_asset(name: &str) -> Option<&'static str> {
|
||||
macro_rules! inc {
|
||||
($e:expr) => {
|
||||
Some(include_str!(concat!("dts/", $e)))
|
||||
};
|
||||
}
|
||||
match name {
|
||||
"bootstrap.ts" => Some("console.log(\"hello deno\");"),
|
||||
"typescript.d.ts" => inc!("typescript.d.ts"),
|
||||
"lib.dom.d.ts" => inc!("lib.dom.d.ts"),
|
||||
"lib.dom.iterable.d.ts" => inc!("lib.dom.iterable.d.ts"),
|
||||
"lib.es5.d.ts" => inc!("lib.es5.d.ts"),
|
||||
"lib.es6.d.ts" => inc!("lib.es6.d.ts"),
|
||||
"lib.esnext.d.ts" => inc!("lib.esnext.d.ts"),
|
||||
"lib.es2020.d.ts" => inc!("lib.es2020.d.ts"),
|
||||
"lib.es2020.full.d.ts" => inc!("lib.es2020.full.d.ts"),
|
||||
"lib.es2019.d.ts" => inc!("lib.es2019.d.ts"),
|
||||
"lib.es2019.full.d.ts" => inc!("lib.es2019.full.d.ts"),
|
||||
"lib.es2018.d.ts" => inc!("lib.es2018.d.ts"),
|
||||
"lib.es2018.full.d.ts" => inc!("lib.es2018.full.d.ts"),
|
||||
"lib.es2017.d.ts" => inc!("lib.es2017.d.ts"),
|
||||
"lib.es2017.full.d.ts" => inc!("lib.es2017.full.d.ts"),
|
||||
"lib.es2016.d.ts" => inc!("lib.es2016.d.ts"),
|
||||
"lib.es2016.full.d.ts" => inc!("lib.es2016.full.d.ts"),
|
||||
"lib.es2015.d.ts" => inc!("lib.es2015.d.ts"),
|
||||
"lib.es2015.collection.d.ts" => inc!("lib.es2015.collection.d.ts"),
|
||||
"lib.es2015.core.d.ts" => inc!("lib.es2015.core.d.ts"),
|
||||
"lib.es2015.generator.d.ts" => inc!("lib.es2015.generator.d.ts"),
|
||||
"lib.es2015.iterable.d.ts" => inc!("lib.es2015.iterable.d.ts"),
|
||||
"lib.es2015.promise.d.ts" => inc!("lib.es2015.promise.d.ts"),
|
||||
"lib.es2015.proxy.d.ts" => inc!("lib.es2015.proxy.d.ts"),
|
||||
"lib.es2015.reflect.d.ts" => inc!("lib.es2015.reflect.d.ts"),
|
||||
"lib.es2015.symbol.d.ts" => inc!("lib.es2015.symbol.d.ts"),
|
||||
"lib.es2015.symbol.wellknown.d.ts" => {
|
||||
inc!("lib.es2015.symbol.wellknown.d.ts")
|
||||
}
|
||||
"lib.es2016.array.include.d.ts" => inc!("lib.es2016.array.include.d.ts"),
|
||||
"lib.es2017.intl.d.ts" => inc!("lib.es2017.intl.d.ts"),
|
||||
"lib.es2017.object.d.ts" => inc!("lib.es2017.object.d.ts"),
|
||||
"lib.es2017.sharedmemory.d.ts" => inc!("lib.es2017.sharedmemory.d.ts"),
|
||||
"lib.es2017.string.d.ts" => inc!("lib.es2017.string.d.ts"),
|
||||
"lib.es2017.typedarrays.d.ts" => inc!("lib.es2017.typedarrays.d.ts"),
|
||||
"lib.es2018.asyncgenerator.d.ts" => inc!("lib.es2018.asyncgenerator.d.ts"),
|
||||
"lib.es2018.asynciterable.d.ts" => inc!("lib.es2018.asynciterable.d.ts"),
|
||||
"lib.es2018.intl.d.ts" => inc!("lib.es2018.intl.d.ts"),
|
||||
"lib.es2018.promise.d.ts" => inc!("lib.es2018.promise.d.ts"),
|
||||
"lib.es2018.regexp.d.ts" => inc!("lib.es2018.regexp.d.ts"),
|
||||
"lib.es2019.array.d.ts" => inc!("lib.es2019.array.d.ts"),
|
||||
"lib.es2019.object.d.ts" => inc!("lib.es2019.object.d.ts"),
|
||||
"lib.es2019.string.d.ts" => inc!("lib.es2019.string.d.ts"),
|
||||
"lib.es2019.symbol.d.ts" => inc!("lib.es2019.symbol.d.ts"),
|
||||
"lib.es2020.bigint.d.ts" => inc!("lib.es2020.bigint.d.ts"),
|
||||
"lib.es2020.intl.d.ts" => inc!("lib.es2020.intl.d.ts"),
|
||||
"lib.es2020.promise.d.ts" => inc!("lib.es2020.promise.d.ts"),
|
||||
"lib.es2020.string.d.ts" => inc!("lib.es2020.string.d.ts"),
|
||||
"lib.es2020.symbol.wellknown.d.ts" => {
|
||||
inc!("lib.es2020.symbol.wellknown.d.ts")
|
||||
}
|
||||
"lib.esnext.intl.d.ts" => inc!("lib.esnext.intl.d.ts"),
|
||||
"lib.esnext.promise.d.ts" => inc!("lib.esnext.promise.d.ts"),
|
||||
"lib.esnext.string.d.ts" => inc!("lib.esnext.string.d.ts"),
|
||||
"lib.esnext.weakref.d.ts" => inc!("lib.esnext.weakref.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")
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Warning: Returns a non-JSON op dispatcher. Must be manually attached to
|
||||
/// JsRuntime.
|
||||
///
|
||||
/// TODO(@kitsonk) this is only used when building the snapshot, and needs to
|
||||
/// be refactored somewhere else. It is no longer used by `main.rs` and
|
||||
/// therefore requires the allow unused.
|
||||
#[allow(unused)]
|
||||
pub fn op_fetch_asset(
|
||||
custom_assets: HashMap<String, PathBuf>,
|
||||
) -> impl Fn(Rc<RefCell<OpState>>, BufVec) -> Op {
|
||||
for (_, path) in custom_assets.iter() {
|
||||
println!("cargo:rerun-if-changed={}", path.display());
|
||||
}
|
||||
move |_state: Rc<RefCell<OpState>>, bufs: BufVec| -> Op {
|
||||
assert_eq!(bufs.len(), 1, "Invalid number of arguments");
|
||||
let name = std::str::from_utf8(&bufs[0]).unwrap();
|
||||
|
||||
let asset_code = if let Some(source_code) = get_asset(name) {
|
||||
source_code.to_string()
|
||||
} else if let Some(asset_path) = custom_assets.get(name) {
|
||||
let source_code_vec =
|
||||
std::fs::read(&asset_path).expect("Asset not found");
|
||||
let source_code = std::str::from_utf8(&source_code_vec).unwrap();
|
||||
source_code.to_string()
|
||||
} else {
|
||||
panic!("fetch_asset bad asset {}", name)
|
||||
};
|
||||
|
||||
let vec = asset_code.into_bytes();
|
||||
deno_core::Op::Sync(vec.into_boxed_slice())
|
||||
}
|
||||
}
|
|
@ -3,9 +3,9 @@
|
|||
use crate::ast;
|
||||
use crate::colors;
|
||||
use crate::media_type::MediaType;
|
||||
use crate::module_graph2::BundleType;
|
||||
use crate::module_graph2::EmitOptions;
|
||||
use crate::module_graph2::GraphBuilder2;
|
||||
use crate::module_graph::BundleType;
|
||||
use crate::module_graph::EmitOptions;
|
||||
use crate::module_graph::GraphBuilder;
|
||||
use crate::permissions::Permissions;
|
||||
use crate::specifier_handler::FetchHandler;
|
||||
use crate::specifier_handler::MemoryHandler;
|
||||
|
@ -65,7 +65,7 @@ async fn op_compile(
|
|||
runtime_permissions,
|
||||
)?))
|
||||
};
|
||||
let mut builder = GraphBuilder2::new(handler, None, None);
|
||||
let mut builder = GraphBuilder::new(handler, None, None);
|
||||
let specifier = ModuleSpecifier::resolve_url_or_path(&args.root_name)
|
||||
.context("The root specifier is invalid.")?;
|
||||
builder.add(&specifier, false).await?;
|
||||
|
|
|
@ -8,10 +8,10 @@ use crate::import_map::ImportMap;
|
|||
use crate::inspector::InspectorServer;
|
||||
use crate::lockfile::Lockfile;
|
||||
use crate::media_type::MediaType;
|
||||
use crate::module_graph2::CheckOptions;
|
||||
use crate::module_graph2::GraphBuilder2;
|
||||
use crate::module_graph2::TranspileOptions;
|
||||
use crate::module_graph2::TypeLib;
|
||||
use crate::module_graph::CheckOptions;
|
||||
use crate::module_graph::GraphBuilder;
|
||||
use crate::module_graph::TranspileOptions;
|
||||
use crate::module_graph::TypeLib;
|
||||
use crate::permissions::Permissions;
|
||||
use crate::source_maps::SourceMapGetter;
|
||||
use crate::specifier_handler::FetchHandler;
|
||||
|
@ -130,7 +130,7 @@ impl ProgramState {
|
|||
let handler =
|
||||
Rc::new(RefCell::new(FetchHandler::new(self, runtime_permissions)?));
|
||||
let mut builder =
|
||||
GraphBuilder2::new(handler, maybe_import_map, self.lockfile.clone());
|
||||
GraphBuilder::new(handler, maybe_import_map, self.lockfile.clone());
|
||||
builder.add(&specifier, is_dynamic).await?;
|
||||
let mut graph = builder.get_graph();
|
||||
let debug = self.flags.log_level == Some(log::Level::Debug);
|
||||
|
|
|
@ -2,10 +2,8 @@
|
|||
|
||||
use crate::diagnostics::Diagnostics;
|
||||
use crate::media_type::MediaType;
|
||||
use crate::module_graph2::Graph2;
|
||||
use crate::module_graph2::Stats;
|
||||
// TODO(@kitsonk) this needs to be refactored when we drop MG1
|
||||
use crate::op_fetch_asset::get_asset;
|
||||
use crate::module_graph::Graph;
|
||||
use crate::module_graph::Stats;
|
||||
use crate::tsc_config::TsConfig;
|
||||
|
||||
use deno_core::error::anyhow;
|
||||
|
@ -26,6 +24,32 @@ use std::cell::RefCell;
|
|||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// Provide static assets that are not preloaded in the compiler snapshot.
|
||||
fn get_asset(asset: &str) -> Option<&'static str> {
|
||||
macro_rules! inc {
|
||||
($e:expr) => {
|
||||
Some(include_str!(concat!("dts/", $e)))
|
||||
};
|
||||
}
|
||||
match asset {
|
||||
"lib.dom.d.ts" => inc!("lib.dom.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.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")
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_maybe_hash(
|
||||
maybe_source: &Option<String>,
|
||||
hash_data: &[Vec<u8>],
|
||||
|
@ -54,7 +78,7 @@ pub struct Request {
|
|||
pub config: TsConfig,
|
||||
/// Indicates to the tsc runtime if debug logging should occur.
|
||||
pub debug: bool,
|
||||
pub graph: Rc<RefCell<Graph2>>,
|
||||
pub graph: Rc<RefCell<Graph>>,
|
||||
pub hash_data: Vec<Vec<u8>>,
|
||||
pub maybe_tsbuildinfo: Option<String>,
|
||||
/// A vector of strings that represent the root/entry point modules for the
|
||||
|
@ -77,7 +101,7 @@ pub struct Response {
|
|||
struct State {
|
||||
hash_data: Vec<Vec<u8>>,
|
||||
emitted_files: Vec<EmittedFile>,
|
||||
graph: Rc<RefCell<Graph2>>,
|
||||
graph: Rc<RefCell<Graph>>,
|
||||
maybe_tsbuildinfo: Option<String>,
|
||||
maybe_response: Option<RespondArgs>,
|
||||
root_map: HashMap<String, ModuleSpecifier>,
|
||||
|
@ -85,7 +109,7 @@ struct State {
|
|||
|
||||
impl State {
|
||||
pub fn new(
|
||||
graph: Rc<RefCell<Graph2>>,
|
||||
graph: Rc<RefCell<Graph>>,
|
||||
hash_data: Vec<Vec<u8>>,
|
||||
maybe_tsbuildinfo: Option<String>,
|
||||
root_map: HashMap<String, ModuleSpecifier>,
|
||||
|
@ -382,8 +406,8 @@ mod tests {
|
|||
use crate::diagnostics::Diagnostic;
|
||||
use crate::diagnostics::DiagnosticCategory;
|
||||
use crate::js;
|
||||
use crate::module_graph2::tests::MockSpecifierHandler;
|
||||
use crate::module_graph2::GraphBuilder2;
|
||||
use crate::module_graph::tests::MockSpecifierHandler;
|
||||
use crate::module_graph::GraphBuilder;
|
||||
use crate::tsc_config::TsConfig;
|
||||
use std::cell::RefCell;
|
||||
use std::env;
|
||||
|
@ -404,7 +428,7 @@ mod tests {
|
|||
fixtures,
|
||||
..MockSpecifierHandler::default()
|
||||
}));
|
||||
let mut builder = GraphBuilder2::new(handler.clone(), None, None);
|
||||
let mut builder = GraphBuilder::new(handler.clone(), None, None);
|
||||
builder
|
||||
.add(&specifier, false)
|
||||
.await
|
||||
|
@ -423,7 +447,7 @@ mod tests {
|
|||
fixtures,
|
||||
..Default::default()
|
||||
}));
|
||||
let mut builder = GraphBuilder2::new(handler.clone(), None, None);
|
||||
let mut builder = GraphBuilder::new(handler.clone(), None, None);
|
||||
builder.add(&specifier, false).await?;
|
||||
let graph = Rc::new(RefCell::new(builder.get_graph()));
|
||||
let config = TsConfig::new(json!({
|
|
@ -1,19 +1,11 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// This module is the entry point for "compiler" isolate, ie. the one
|
||||
// that is created when Deno needs to compile TS/WASM to JS.
|
||||
//
|
||||
// It provides two functions that should be called by Rust:
|
||||
// - `startup`
|
||||
// This functions must be called when creating isolate
|
||||
// to properly setup runtime.
|
||||
// - `tsCompilerOnMessage`
|
||||
// This function must be called when sending a request
|
||||
// to the compiler.
|
||||
// that is created when Deno needs to type check TypeScript, and in some
|
||||
// instances convert TypeScript to JavaScript.
|
||||
|
||||
// Removes the `__proto__` for security reasons. This intentionally makes
|
||||
// Deno non compliant with ECMA-262 Annex B.2.2.1
|
||||
//
|
||||
delete Object.prototype.__proto__;
|
||||
|
||||
((window) => {
|
||||
|
@ -22,11 +14,6 @@ delete Object.prototype.__proto__;
|
|||
let logDebug = false;
|
||||
let logSource = "JS";
|
||||
|
||||
/** Instructs the host to behave in a legacy fashion, with the legacy
|
||||
* pipeline for handling code. Setting the value to `true` will cause the
|
||||
* host to behave in the modern way. */
|
||||
let legacy = true;
|
||||
|
||||
function setLogDebug(debug, source) {
|
||||
logDebug = debug;
|
||||
if (source) {
|
||||
|
@ -57,9 +44,7 @@ delete Object.prototype.__proto__;
|
|||
/** @type {Map<string, ts.SourceFile>} */
|
||||
const sourceFileCache = new Map();
|
||||
|
||||
/**
|
||||
* @param {import("../dts/typescript").DiagnosticRelatedInformation} diagnostic
|
||||
*/
|
||||
/** @param {ts.DiagnosticRelatedInformation} diagnostic */
|
||||
function fromRelatedInformation({
|
||||
start,
|
||||
length,
|
||||
|
@ -96,9 +81,7 @@ delete Object.prototype.__proto__;
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("../dts/typescript").Diagnostic[]} diagnostics
|
||||
*/
|
||||
/** @param {ts.Diagnostic[]} diagnostics */
|
||||
function fromTypeScriptDiagnostic(diagnostics) {
|
||||
return diagnostics.map(({ relatedInformation: ri, source, ...diag }) => {
|
||||
const value = fromRelatedInformation(diag);
|
||||
|
@ -110,426 +93,20 @@ delete Object.prototype.__proto__;
|
|||
});
|
||||
}
|
||||
|
||||
// We really don't want to depend on JSON dispatch during snapshotting, so
|
||||
// this op exchanges strings with Rust as raw byte arrays.
|
||||
function getAsset(name) {
|
||||
const opId = core.ops()["op_fetch_asset"];
|
||||
const sourceCodeBytes = core.dispatch(opId, core.encode(name));
|
||||
return core.decode(sourceCodeBytes);
|
||||
}
|
||||
|
||||
// Using incremental compile APIs requires that all
|
||||
// paths must be either relative or absolute. Since
|
||||
// analysis in Rust operates on fully resolved URLs,
|
||||
// it makes sense to use the same scheme here.
|
||||
const ASSETS = "asset:///";
|
||||
const OUT_DIR = "deno://";
|
||||
const CACHE = "cache:///";
|
||||
// This constant is passed to compiler settings when
|
||||
// doing incremental compiles. Contents of this
|
||||
// file are passed back to Rust and saved to $DENO_DIR.
|
||||
const TS_BUILD_INFO = "cache:///tsbuildinfo.json";
|
||||
|
||||
const DEFAULT_COMPILE_OPTIONS = {
|
||||
allowJs: false,
|
||||
allowNonTsExtensions: true,
|
||||
checkJs: false,
|
||||
esModuleInterop: true,
|
||||
jsx: ts.JsxEmit.React,
|
||||
module: ts.ModuleKind.ESNext,
|
||||
outDir: OUT_DIR,
|
||||
sourceMap: true,
|
||||
strict: true,
|
||||
removeComments: true,
|
||||
target: ts.ScriptTarget.ESNext,
|
||||
};
|
||||
|
||||
const CompilerHostTarget = {
|
||||
Main: "main",
|
||||
Runtime: "runtime",
|
||||
Worker: "worker",
|
||||
};
|
||||
|
||||
// Warning! The values in this enum are duplicated in `cli/msg.rs`
|
||||
// Update carefully!
|
||||
const MediaType = {
|
||||
0: "JavaScript",
|
||||
1: "JSX",
|
||||
2: "TypeScript",
|
||||
3: "Dts",
|
||||
4: "TSX",
|
||||
5: "Json",
|
||||
6: "Wasm",
|
||||
7: "TsBuildInfo",
|
||||
8: "SourceMap",
|
||||
9: "Unknown",
|
||||
JavaScript: 0,
|
||||
JSX: 1,
|
||||
TypeScript: 2,
|
||||
Dts: 3,
|
||||
TSX: 4,
|
||||
Json: 5,
|
||||
Wasm: 6,
|
||||
TsBuildInfo: 7,
|
||||
SourceMap: 8,
|
||||
Unknown: 9,
|
||||
};
|
||||
|
||||
function getExtension(fileName, mediaType) {
|
||||
switch (mediaType) {
|
||||
case MediaType.JavaScript:
|
||||
return ts.Extension.Js;
|
||||
case MediaType.JSX:
|
||||
return ts.Extension.Jsx;
|
||||
case MediaType.TypeScript:
|
||||
return ts.Extension.Ts;
|
||||
case MediaType.Dts:
|
||||
return ts.Extension.Dts;
|
||||
case MediaType.TSX:
|
||||
return ts.Extension.Tsx;
|
||||
case MediaType.Wasm:
|
||||
// Custom marker for Wasm type.
|
||||
return ts.Extension.Js;
|
||||
case MediaType.Unknown:
|
||||
default:
|
||||
throw TypeError(
|
||||
`Cannot resolve extension for "${fileName}" with mediaType "${
|
||||
MediaType[mediaType]
|
||||
}".`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** A global cache of module source files that have been loaded.
|
||||
* This cache will be rewritten to be populated on compiler startup
|
||||
* with files provided from Rust in request message.
|
||||
*/
|
||||
const SOURCE_FILE_CACHE = new Map();
|
||||
/** A map of maps which cache resolved specifier for each import in a file.
|
||||
* This cache is used so `resolveModuleNames` ops is called as few times
|
||||
* as possible.
|
||||
*
|
||||
* First map's key is "referrer" URL ("file://a/b/c/mod.ts")
|
||||
* Second map's key is "raw" import specifier ("./foo.ts")
|
||||
* Second map's value is resolved import URL ("file:///a/b/c/foo.ts")
|
||||
*/
|
||||
const RESOLVED_SPECIFIER_CACHE = new Map();
|
||||
|
||||
class SourceFile {
|
||||
constructor(json) {
|
||||
this.processed = false;
|
||||
Object.assign(this, json);
|
||||
this.extension = getExtension(this.url, this.mediaType);
|
||||
}
|
||||
|
||||
static addToCache(json) {
|
||||
if (SOURCE_FILE_CACHE.has(json.url)) {
|
||||
throw new TypeError("SourceFile already exists");
|
||||
}
|
||||
const sf = new SourceFile(json);
|
||||
SOURCE_FILE_CACHE.set(sf.url, sf);
|
||||
return sf;
|
||||
}
|
||||
|
||||
static getCached(url) {
|
||||
return SOURCE_FILE_CACHE.get(url);
|
||||
}
|
||||
|
||||
static cacheResolvedUrl(resolvedUrl, rawModuleSpecifier, containingFile) {
|
||||
containingFile = containingFile || "";
|
||||
let innerCache = RESOLVED_SPECIFIER_CACHE.get(containingFile);
|
||||
if (!innerCache) {
|
||||
innerCache = new Map();
|
||||
RESOLVED_SPECIFIER_CACHE.set(containingFile, innerCache);
|
||||
}
|
||||
innerCache.set(rawModuleSpecifier, resolvedUrl);
|
||||
}
|
||||
|
||||
static getResolvedUrl(moduleSpecifier, containingFile) {
|
||||
const containingCache = RESOLVED_SPECIFIER_CACHE.get(containingFile);
|
||||
if (containingCache) {
|
||||
return containingCache.get(moduleSpecifier);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function getAssetInternal(filename) {
|
||||
const lastSegment = filename.split("/").pop();
|
||||
const url = ts.libMap.has(lastSegment)
|
||||
? ts.libMap.get(lastSegment)
|
||||
: lastSegment;
|
||||
const sourceFile = SourceFile.getCached(url);
|
||||
if (sourceFile) {
|
||||
return sourceFile;
|
||||
}
|
||||
const name = url.includes(".") ? url : `${url}.d.ts`;
|
||||
const sourceCode = getAsset(name);
|
||||
return SourceFile.addToCache({
|
||||
url,
|
||||
filename: `${ASSETS}/${name}`,
|
||||
mediaType: MediaType.TypeScript,
|
||||
versionHash: "1",
|
||||
sourceCode,
|
||||
});
|
||||
}
|
||||
|
||||
/** There was some private state in the legacy host, that is moved out to
|
||||
* here which can then be refactored out later. */
|
||||
const legacyHostState = {
|
||||
buildInfo: "",
|
||||
target: CompilerHostTarget.Main,
|
||||
writeFile: (_fileName, _data, _sourceFiles) => {},
|
||||
};
|
||||
|
||||
/** @type {import("../dts/typescript").CompilerHost} */
|
||||
const host = {
|
||||
fileExists(fileName) {
|
||||
debug(`host.fileExists("${fileName}")`);
|
||||
return false;
|
||||
},
|
||||
readFile(specifier) {
|
||||
debug(`host.readFile("${specifier}")`);
|
||||
if (legacy) {
|
||||
if (specifier == TS_BUILD_INFO) {
|
||||
return legacyHostState.buildInfo;
|
||||
}
|
||||
return unreachable();
|
||||
} else {
|
||||
return core.jsonOpSync("op_load", { specifier }).data;
|
||||
}
|
||||
},
|
||||
getSourceFile(
|
||||
specifier,
|
||||
languageVersion,
|
||||
onError,
|
||||
shouldCreateNewSourceFile,
|
||||
) {
|
||||
debug(
|
||||
`host.getSourceFile("${specifier}", ${
|
||||
ts.ScriptTarget[languageVersion]
|
||||
})`,
|
||||
);
|
||||
if (legacy) {
|
||||
try {
|
||||
assert(!shouldCreateNewSourceFile);
|
||||
const sourceFile = specifier.startsWith(ASSETS)
|
||||
? getAssetInternal(specifier)
|
||||
: SourceFile.getCached(specifier);
|
||||
assert(sourceFile != null);
|
||||
if (!sourceFile.tsSourceFile) {
|
||||
assert(sourceFile.sourceCode != null);
|
||||
const tsSourceFileName = specifier.startsWith(ASSETS)
|
||||
? sourceFile.filename
|
||||
: specifier;
|
||||
|
||||
sourceFile.tsSourceFile = ts.createSourceFile(
|
||||
tsSourceFileName,
|
||||
sourceFile.sourceCode,
|
||||
languageVersion,
|
||||
);
|
||||
sourceFile.tsSourceFile.version = sourceFile.versionHash;
|
||||
delete sourceFile.sourceCode;
|
||||
|
||||
// This code is to support transition from the "legacy" compiler
|
||||
// to the new one, by populating the new source file cache.
|
||||
if (
|
||||
!sourceFileCache.has(specifier) && specifier.startsWith(ASSETS)
|
||||
) {
|
||||
sourceFileCache.set(specifier, sourceFile.tsSourceFile);
|
||||
}
|
||||
}
|
||||
return sourceFile.tsSourceFile;
|
||||
} catch (e) {
|
||||
if (onError) {
|
||||
onError(String(e));
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
} else {
|
||||
let sourceFile = sourceFileCache.get(specifier);
|
||||
if (sourceFile) {
|
||||
return sourceFile;
|
||||
}
|
||||
|
||||
/** @type {{ data: string; hash?: string; scriptKind: ts.ScriptKind }} */
|
||||
const { data, hash, scriptKind } = core.jsonOpSync(
|
||||
"op_load",
|
||||
{ specifier },
|
||||
);
|
||||
assert(
|
||||
data != null,
|
||||
`"data" is unexpectedly null for "${specifier}".`,
|
||||
);
|
||||
sourceFile = ts.createSourceFile(
|
||||
specifier,
|
||||
data,
|
||||
languageVersion,
|
||||
false,
|
||||
scriptKind,
|
||||
);
|
||||
sourceFile.moduleName = specifier;
|
||||
sourceFile.version = hash;
|
||||
sourceFileCache.set(specifier, sourceFile);
|
||||
return sourceFile;
|
||||
}
|
||||
},
|
||||
getDefaultLibFileName() {
|
||||
if (legacy) {
|
||||
switch (legacyHostState.target) {
|
||||
case CompilerHostTarget.Main:
|
||||
case CompilerHostTarget.Runtime:
|
||||
return `${ASSETS}/lib.deno.window.d.ts`;
|
||||
case CompilerHostTarget.Worker:
|
||||
return `${ASSETS}/lib.deno.worker.d.ts`;
|
||||
}
|
||||
} else {
|
||||
return `${ASSETS}/lib.esnext.d.ts`;
|
||||
}
|
||||
},
|
||||
getDefaultLibLocation() {
|
||||
return ASSETS;
|
||||
},
|
||||
writeFile(fileName, data, _writeByteOrderMark, _onError, sourceFiles) {
|
||||
debug(`host.writeFile("${fileName}")`);
|
||||
if (legacy) {
|
||||
legacyHostState.writeFile(fileName, data, sourceFiles);
|
||||
} else {
|
||||
let maybeSpecifiers;
|
||||
if (sourceFiles) {
|
||||
maybeSpecifiers = sourceFiles.map((sf) => sf.moduleName);
|
||||
}
|
||||
return core.jsonOpSync(
|
||||
"op_emit",
|
||||
{ maybeSpecifiers, fileName, data },
|
||||
);
|
||||
}
|
||||
},
|
||||
getCurrentDirectory() {
|
||||
return CACHE;
|
||||
},
|
||||
getCanonicalFileName(fileName) {
|
||||
return fileName;
|
||||
},
|
||||
useCaseSensitiveFileNames() {
|
||||
return true;
|
||||
},
|
||||
getNewLine() {
|
||||
return "\n";
|
||||
},
|
||||
resolveModuleNames(specifiers, base) {
|
||||
debug(`host.resolveModuleNames()`);
|
||||
debug(` base: ${base}`);
|
||||
debug(` specifiers: ${specifiers.join(", ")}`);
|
||||
if (legacy) {
|
||||
const resolved = specifiers.map((specifier) => {
|
||||
const maybeUrl = SourceFile.getResolvedUrl(specifier, base);
|
||||
|
||||
debug("compiler::host.resolveModuleNames maybeUrl", {
|
||||
specifier,
|
||||
maybeUrl,
|
||||
});
|
||||
|
||||
let sourceFile = undefined;
|
||||
|
||||
if (specifier.startsWith(ASSETS)) {
|
||||
sourceFile = getAssetInternal(specifier);
|
||||
} else if (typeof maybeUrl !== "undefined") {
|
||||
sourceFile = SourceFile.getCached(maybeUrl);
|
||||
}
|
||||
|
||||
if (!sourceFile) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
resolvedFileName: sourceFile.url,
|
||||
isExternalLibraryImport: specifier.startsWith(ASSETS),
|
||||
extension: sourceFile.extension,
|
||||
};
|
||||
});
|
||||
debug(resolved);
|
||||
return resolved;
|
||||
} else {
|
||||
/** @type {Array<[string, import("../dts/typescript").Extension]>} */
|
||||
const resolved = core.jsonOpSync("op_resolve", {
|
||||
specifiers,
|
||||
base,
|
||||
});
|
||||
let r = resolved.map(([resolvedFileName, extension]) => ({
|
||||
resolvedFileName,
|
||||
extension,
|
||||
isExternalLibraryImport: false,
|
||||
}));
|
||||
return r;
|
||||
}
|
||||
},
|
||||
createHash(data) {
|
||||
return core.jsonOpSync("op_create_hash", { data }).hash;
|
||||
},
|
||||
};
|
||||
|
||||
// This is a hacky way of adding our libs to the libs available in TypeScript()
|
||||
// as these are internal APIs of TypeScript which maintain valid libs
|
||||
ts.libs.push("deno.ns", "deno.window", "deno.worker", "deno.shared_globals");
|
||||
ts.libMap.set("deno.ns", "lib.deno.ns.d.ts");
|
||||
ts.libMap.set("deno.web", "lib.deno.web.d.ts");
|
||||
ts.libMap.set("deno.fetch", "lib.deno.fetch.d.ts");
|
||||
ts.libMap.set("deno.window", "lib.deno.window.d.ts");
|
||||
ts.libMap.set("deno.worker", "lib.deno.worker.d.ts");
|
||||
ts.libMap.set("deno.shared_globals", "lib.deno.shared_globals.d.ts");
|
||||
ts.libMap.set("deno.unstable", "lib.deno.unstable.d.ts");
|
||||
|
||||
// TODO(@kitsonk) remove once added to TypeScript
|
||||
ts.libs.push("esnext.weakref");
|
||||
ts.libMap.set("esnext.weakref", "lib.esnext.weakref.d.ts");
|
||||
|
||||
// this pre-populates the cache at snapshot time of our library files, so they
|
||||
// are available in the future when needed.
|
||||
host.getSourceFile(
|
||||
`${ASSETS}lib.deno.ns.d.ts`,
|
||||
ts.ScriptTarget.ESNext,
|
||||
);
|
||||
host.getSourceFile(
|
||||
`${ASSETS}lib.deno.web.d.ts`,
|
||||
ts.ScriptTarget.ESNext,
|
||||
);
|
||||
host.getSourceFile(
|
||||
`${ASSETS}lib.deno.fetch.d.ts`,
|
||||
ts.ScriptTarget.ESNext,
|
||||
);
|
||||
host.getSourceFile(
|
||||
`${ASSETS}lib.deno.window.d.ts`,
|
||||
ts.ScriptTarget.ESNext,
|
||||
);
|
||||
host.getSourceFile(
|
||||
`${ASSETS}lib.deno.worker.d.ts`,
|
||||
ts.ScriptTarget.ESNext,
|
||||
);
|
||||
host.getSourceFile(
|
||||
`${ASSETS}lib.deno.shared_globals.d.ts`,
|
||||
ts.ScriptTarget.ESNext,
|
||||
);
|
||||
host.getSourceFile(
|
||||
`${ASSETS}lib.deno.unstable.d.ts`,
|
||||
ts.ScriptTarget.ESNext,
|
||||
);
|
||||
|
||||
// We never use this program; it's only created
|
||||
// during snapshotting to hydrate and populate
|
||||
// source file cache with lib declaration files.
|
||||
const _TS_SNAPSHOT_PROGRAM = ts.createProgram({
|
||||
rootNames: [`${ASSETS}bootstrap.ts`],
|
||||
options: DEFAULT_COMPILE_OPTIONS,
|
||||
host,
|
||||
});
|
||||
|
||||
/** Diagnostics that are intentionally ignored when compiling TypeScript in
|
||||
* Deno, as they provide misleading or incorrect information. */
|
||||
const IGNORED_DIAGNOSTICS = [
|
||||
// TS2306: File 'file:///Users/rld/src/deno/cli/tests/subdir/amd_like.js' is
|
||||
// not a module.
|
||||
2306,
|
||||
// TS1208: All files must be modules when the '--isolatedModules' flag is
|
||||
// provided. We can ignore because we guarantuee that all files are
|
||||
// modules.
|
||||
1208,
|
||||
// TS1375: 'await' expressions are only allowed at the top level of a file
|
||||
// when that file is a module, but this file has no imports or exports.
|
||||
// Consider adding an empty 'export {}' to make this file a module.
|
||||
|
@ -537,6 +114,9 @@ delete Object.prototype.__proto__;
|
|||
// TS1103: 'for-await-of' statement is only allowed within an async function
|
||||
// or async generator.
|
||||
1103,
|
||||
// TS2306: File 'file:///Users/rld/src/deno/cli/tests/subdir/amd_like.js' is
|
||||
// not a module.
|
||||
2306,
|
||||
// TS2691: An import path cannot end with a '.ts' extension. Consider
|
||||
// importing 'bad-module' instead.
|
||||
2691,
|
||||
|
@ -557,14 +137,116 @@ delete Object.prototype.__proto__;
|
|||
7016,
|
||||
];
|
||||
|
||||
const IGNORED_COMPILE_DIAGNOSTICS = [
|
||||
// TS1208: All files must be modules when the '--isolatedModules' flag is
|
||||
// provided. We can ignore because we guarantuee that all files are
|
||||
// modules.
|
||||
1208,
|
||||
];
|
||||
const SNAPSHOT_COMPILE_OPTIONS = {
|
||||
esModuleInterop: true,
|
||||
jsx: ts.JsxEmit.React,
|
||||
module: ts.ModuleKind.ESNext,
|
||||
noEmit: true,
|
||||
strict: true,
|
||||
target: ts.ScriptTarget.ESNext,
|
||||
};
|
||||
|
||||
/** @type {Array<{ key: string, value: number }>} */
|
||||
/** An object literal of the incremental compiler host, which provides the
|
||||
* specific "bindings" to the Deno environment that tsc needs to work.
|
||||
*
|
||||
* @type {ts.CompilerHost} */
|
||||
const host = {
|
||||
fileExists(fileName) {
|
||||
debug(`host.fileExists("${fileName}")`);
|
||||
return false;
|
||||
},
|
||||
readFile(specifier) {
|
||||
debug(`host.readFile("${specifier}")`);
|
||||
return core.jsonOpSync("op_load", { specifier }).data;
|
||||
},
|
||||
getSourceFile(
|
||||
specifier,
|
||||
languageVersion,
|
||||
_onError,
|
||||
_shouldCreateNewSourceFile,
|
||||
) {
|
||||
debug(
|
||||
`host.getSourceFile("${specifier}", ${
|
||||
ts.ScriptTarget[languageVersion]
|
||||
})`,
|
||||
);
|
||||
let sourceFile = sourceFileCache.get(specifier);
|
||||
if (sourceFile) {
|
||||
return sourceFile;
|
||||
}
|
||||
|
||||
/** @type {{ data: string; hash?: string; scriptKind: ts.ScriptKind }} */
|
||||
const { data, hash, scriptKind } = core.jsonOpSync(
|
||||
"op_load",
|
||||
{ specifier },
|
||||
);
|
||||
assert(
|
||||
data != null,
|
||||
`"data" is unexpectedly null for "${specifier}".`,
|
||||
);
|
||||
sourceFile = ts.createSourceFile(
|
||||
specifier,
|
||||
data,
|
||||
languageVersion,
|
||||
false,
|
||||
scriptKind,
|
||||
);
|
||||
sourceFile.moduleName = specifier;
|
||||
sourceFile.version = hash;
|
||||
sourceFileCache.set(specifier, sourceFile);
|
||||
return sourceFile;
|
||||
},
|
||||
getDefaultLibFileName() {
|
||||
return `${ASSETS}/lib.esnext.d.ts`;
|
||||
},
|
||||
getDefaultLibLocation() {
|
||||
return ASSETS;
|
||||
},
|
||||
writeFile(fileName, data, _writeByteOrderMark, _onError, sourceFiles) {
|
||||
debug(`host.writeFile("${fileName}")`);
|
||||
let maybeSpecifiers;
|
||||
if (sourceFiles) {
|
||||
maybeSpecifiers = sourceFiles.map((sf) => sf.moduleName);
|
||||
}
|
||||
return core.jsonOpSync(
|
||||
"op_emit",
|
||||
{ maybeSpecifiers, fileName, data },
|
||||
);
|
||||
},
|
||||
getCurrentDirectory() {
|
||||
return CACHE;
|
||||
},
|
||||
getCanonicalFileName(fileName) {
|
||||
return fileName;
|
||||
},
|
||||
useCaseSensitiveFileNames() {
|
||||
return true;
|
||||
},
|
||||
getNewLine() {
|
||||
return "\n";
|
||||
},
|
||||
resolveModuleNames(specifiers, base) {
|
||||
debug(`host.resolveModuleNames()`);
|
||||
debug(` base: ${base}`);
|
||||
debug(` specifiers: ${specifiers.join(", ")}`);
|
||||
/** @type {Array<[string, ts.Extension]>} */
|
||||
const resolved = core.jsonOpSync("op_resolve", {
|
||||
specifiers,
|
||||
base,
|
||||
});
|
||||
let r = resolved.map(([resolvedFileName, extension]) => ({
|
||||
resolvedFileName,
|
||||
extension,
|
||||
isExternalLibraryImport: false,
|
||||
}));
|
||||
return r;
|
||||
},
|
||||
createHash(data) {
|
||||
return core.jsonOpSync("op_create_hash", { data }).hash;
|
||||
},
|
||||
};
|
||||
|
||||
/** @type {Array<[string, number]>} */
|
||||
const stats = [];
|
||||
let statsStart = 0;
|
||||
|
||||
|
@ -579,35 +261,31 @@ delete Object.prototype.__proto__;
|
|||
if ("getProgram" in program) {
|
||||
program = program.getProgram();
|
||||
}
|
||||
stats.push({ key: "Files", value: program.getSourceFiles().length });
|
||||
stats.push({ key: "Nodes", value: program.getNodeCount() });
|
||||
stats.push({ key: "Identifiers", value: program.getIdentifierCount() });
|
||||
stats.push({ key: "Symbols", value: program.getSymbolCount() });
|
||||
stats.push({ key: "Types", value: program.getTypeCount() });
|
||||
stats.push({
|
||||
key: "Instantiations",
|
||||
value: program.getInstantiationCount(),
|
||||
});
|
||||
stats.push(["Files", program.getSourceFiles().length]);
|
||||
stats.push(["Nodes", program.getNodeCount()]);
|
||||
stats.push(["Identifiers", program.getIdentifierCount()]);
|
||||
stats.push(["Symbols", program.getSymbolCount()]);
|
||||
stats.push(["Types", program.getTypeCount()]);
|
||||
stats.push(["Instantiations", program.getInstantiationCount()]);
|
||||
} else if (fileCount != null) {
|
||||
stats.push({ key: "Files", value: fileCount });
|
||||
stats.push(["Files", fileCount]);
|
||||
}
|
||||
const programTime = ts.performance.getDuration("Program");
|
||||
const bindTime = ts.performance.getDuration("Bind");
|
||||
const checkTime = ts.performance.getDuration("Check");
|
||||
const emitTime = ts.performance.getDuration("Emit");
|
||||
stats.push({ key: "Parse time", value: programTime });
|
||||
stats.push({ key: "Bind time", value: bindTime });
|
||||
stats.push({ key: "Check time", value: checkTime });
|
||||
stats.push({ key: "Emit time", value: emitTime });
|
||||
stats.push({
|
||||
key: "Total TS time",
|
||||
value: programTime + bindTime + checkTime + emitTime,
|
||||
});
|
||||
stats.push(["Parse time", programTime]);
|
||||
stats.push(["Bind time", bindTime]);
|
||||
stats.push(["Check time", checkTime]);
|
||||
stats.push(["Emit time", emitTime]);
|
||||
stats.push(
|
||||
["Total TS time", programTime + bindTime + checkTime + emitTime],
|
||||
);
|
||||
}
|
||||
|
||||
function performanceEnd() {
|
||||
const duration = new Date() - statsStart;
|
||||
stats.push({ key: "Compile time", value: duration });
|
||||
stats.push(["Compile time", duration]);
|
||||
return stats;
|
||||
}
|
||||
|
||||
|
@ -645,17 +323,12 @@ delete Object.prototype.__proto__;
|
|||
...program.getGlobalDiagnostics(),
|
||||
...program.getSemanticDiagnostics(),
|
||||
...emitDiagnostics,
|
||||
].filter(({ code }) =>
|
||||
!IGNORED_DIAGNOSTICS.includes(code) &&
|
||||
!IGNORED_COMPILE_DIAGNOSTICS.includes(code)
|
||||
);
|
||||
].filter(({ code }) => !IGNORED_DIAGNOSTICS.includes(code));
|
||||
performanceProgram({ program });
|
||||
|
||||
// TODO(@kitsonk) when legacy stats are removed, convert to just tuples
|
||||
let stats = performanceEnd().map(({ key, value }) => [key, value]);
|
||||
core.jsonOpSync("op_respond", {
|
||||
diagnostics: fromTypeScriptDiagnostic(diagnostics),
|
||||
stats,
|
||||
stats: performanceEnd(),
|
||||
});
|
||||
debug("<<< exec stop");
|
||||
}
|
||||
|
@ -665,7 +338,7 @@ delete Object.prototype.__proto__;
|
|||
/** Startup the runtime environment, setting various flags.
|
||||
* @param {{ debugFlag?: boolean; legacyFlag?: boolean; }} msg
|
||||
*/
|
||||
function startup({ debugFlag = false, legacyFlag = true }) {
|
||||
function startup({ debugFlag = false }) {
|
||||
if (hasStarted) {
|
||||
throw new Error("The compiler runtime already started.");
|
||||
}
|
||||
|
@ -673,9 +346,42 @@ delete Object.prototype.__proto__;
|
|||
core.ops();
|
||||
core.registerErrorClass("Error", Error);
|
||||
setLogDebug(!!debugFlag, "TS");
|
||||
legacy = legacyFlag;
|
||||
}
|
||||
|
||||
// Setup the compiler runtime during the build process.
|
||||
core.ops();
|
||||
core.registerErrorClass("Error", Error);
|
||||
|
||||
// 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[] }} */
|
||||
const { buildSpecifier, libs } = core.jsonOpSync("op_build_info", {});
|
||||
for (const lib of libs) {
|
||||
let 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}${specifier}`, ts.ScriptTarget.ESNext),
|
||||
);
|
||||
}
|
||||
// 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,
|
||||
});
|
||||
ts.getPreEmitDiagnostics(TS_SNAPSHOT_PROGRAM);
|
||||
|
||||
// exposes the two functions that are called by `tsc::exec()` when type
|
||||
// checking TypeScript.
|
||||
globalThis.startup = startup;
|
||||
globalThis.exec = exec;
|
||||
})(this);
|
||||
|
|
Loading…
Reference in a new issue