mirror of
https://github.com/denoland/deno.git
synced 2024-11-24 15:19:26 -05:00
refactor: integrate deno_graph into CLI (#12369)
This commit is contained in:
parent
5a8a989b78
commit
a7baf5f2bb
78 changed files with 2970 additions and 5116 deletions
411
Cargo.lock
generated
411
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -41,8 +41,8 @@ winres = "0.1.11"
|
|||
[dependencies]
|
||||
deno_ast = { version = "0.2.0", features = ["bundler", "codegen", "dep_graph", "module_specifier", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] }
|
||||
deno_core = { version = "0.102.0", path = "../core" }
|
||||
deno_doc = "0.14.0"
|
||||
deno_graph = "0.5.0"
|
||||
deno_doc = "0.15.0"
|
||||
deno_graph = "0.6.0"
|
||||
deno_lint = { version = "0.16.0", features = ["docs"] }
|
||||
deno_runtime = { version = "0.28.0", path = "../runtime" }
|
||||
deno_tls = { version = "0.7.0", path = "../ext/tls" }
|
||||
|
|
|
@ -4,6 +4,7 @@ use deno_ast::swc::bundler::ModuleRecord;
|
|||
use deno_ast::swc::common::Span;
|
||||
use deno_core::error::AnyError;
|
||||
|
||||
/// This contains the logic for Deno to rewrite the `import.meta` when bundling.
|
||||
pub struct BundleHook;
|
||||
|
||||
impl Hook for BundleHook {
|
||||
|
|
349
cli/cache.rs
Normal file
349
cli/cache.rs
Normal file
|
@ -0,0 +1,349 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::disk_cache::DiskCache;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::future;
|
||||
use deno_core::futures::FutureExt;
|
||||
use deno_core::serde::Deserialize;
|
||||
use deno_core::serde::Serialize;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_graph::source::CacheInfo;
|
||||
use deno_graph::source::LoadFuture;
|
||||
use deno_graph::source::LoadResponse;
|
||||
use deno_graph::source::Loader;
|
||||
use deno_runtime::permissions::Permissions;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct EmitMetadata {
|
||||
pub version_hash: String,
|
||||
}
|
||||
|
||||
pub(crate) enum CacheType {
|
||||
Declaration,
|
||||
Emit,
|
||||
SourceMap,
|
||||
TypeScriptBuildInfo,
|
||||
Version,
|
||||
}
|
||||
|
||||
/// A trait which provides a concise implementation to getting and setting
|
||||
/// values in a cache.
|
||||
pub(crate) trait Cacher {
|
||||
/// Get a value from the cache.
|
||||
fn get(
|
||||
&self,
|
||||
cache_type: CacheType,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Option<String>;
|
||||
/// Set a value in the cache.
|
||||
fn set(
|
||||
&mut self,
|
||||
cache_type: CacheType,
|
||||
specifier: &ModuleSpecifier,
|
||||
value: String,
|
||||
) -> Result<(), AnyError>;
|
||||
}
|
||||
|
||||
/// Combines the cacher trait along with the deno_graph Loader trait to provide
|
||||
/// a single interface to be able to load and cache modules when building a
|
||||
/// graph.
|
||||
pub(crate) trait CacherLoader: Cacher + Loader {
|
||||
fn as_cacher(&self) -> &dyn Cacher;
|
||||
fn as_mut_loader(&mut self) -> &mut dyn Loader;
|
||||
fn as_mut_cacher(&mut self) -> &mut dyn Cacher;
|
||||
}
|
||||
|
||||
/// A "wrapper" for the FileFetcher and DiskCache for the Deno CLI that provides
|
||||
/// a concise interface to the DENO_DIR when building module graphs.
|
||||
pub(crate) struct FetchCacher {
|
||||
disk_cache: DiskCache,
|
||||
dynamic_permissions: Permissions,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
root_permissions: Permissions,
|
||||
}
|
||||
|
||||
impl FetchCacher {
|
||||
pub fn new(
|
||||
disk_cache: DiskCache,
|
||||
file_fetcher: FileFetcher,
|
||||
root_permissions: Permissions,
|
||||
dynamic_permissions: Permissions,
|
||||
) -> Self {
|
||||
let file_fetcher = Arc::new(file_fetcher);
|
||||
|
||||
Self {
|
||||
disk_cache,
|
||||
dynamic_permissions,
|
||||
file_fetcher,
|
||||
root_permissions,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_emit_metadata(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Option<EmitMetadata> {
|
||||
let filename = self
|
||||
.disk_cache
|
||||
.get_cache_filename_with_extension(specifier, "meta")?;
|
||||
let bytes = self.disk_cache.get(&filename).ok()?;
|
||||
serde_json::from_slice(&bytes).ok()
|
||||
}
|
||||
|
||||
fn set_emit_metadata(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
data: EmitMetadata,
|
||||
) -> Result<(), AnyError> {
|
||||
let filename = self
|
||||
.disk_cache
|
||||
.get_cache_filename_with_extension(specifier, "meta")
|
||||
.unwrap();
|
||||
let bytes = serde_json::to_vec(&data)?;
|
||||
self.disk_cache.set(&filename, &bytes).map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Loader for FetchCacher {
|
||||
fn get_cache_info(&self, specifier: &ModuleSpecifier) -> Option<CacheInfo> {
|
||||
let local = self.file_fetcher.get_local_path(specifier)?;
|
||||
if local.is_file() {
|
||||
let location = &self.disk_cache.location;
|
||||
let emit = self
|
||||
.disk_cache
|
||||
.get_cache_filename_with_extension(specifier, "js")
|
||||
.map(|p| location.join(p))
|
||||
.filter(|p| p.is_file());
|
||||
let map = self
|
||||
.disk_cache
|
||||
.get_cache_filename_with_extension(specifier, "js.map")
|
||||
.map(|p| location.join(p))
|
||||
.filter(|p| p.is_file());
|
||||
Some(CacheInfo {
|
||||
local: Some(local),
|
||||
emit,
|
||||
map,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn load(
|
||||
&mut self,
|
||||
specifier: &ModuleSpecifier,
|
||||
is_dynamic: bool,
|
||||
) -> LoadFuture {
|
||||
let specifier = specifier.clone();
|
||||
let mut permissions = if is_dynamic {
|
||||
self.dynamic_permissions.clone()
|
||||
} else {
|
||||
self.root_permissions.clone()
|
||||
};
|
||||
let file_fetcher = self.file_fetcher.clone();
|
||||
|
||||
async move {
|
||||
let load_result = file_fetcher
|
||||
.fetch(&specifier, &mut permissions)
|
||||
.await
|
||||
.map_or_else(
|
||||
|err| {
|
||||
if let Some(err) = err.downcast_ref::<std::io::Error>() {
|
||||
if err.kind() == std::io::ErrorKind::NotFound {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
Err(err)
|
||||
},
|
||||
|file| {
|
||||
Ok(Some(LoadResponse {
|
||||
specifier: file.specifier,
|
||||
maybe_headers: file.maybe_headers,
|
||||
content: file.source,
|
||||
}))
|
||||
},
|
||||
);
|
||||
|
||||
(specifier, load_result)
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
impl Cacher for FetchCacher {
|
||||
fn get(
|
||||
&self,
|
||||
cache_type: CacheType,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Option<String> {
|
||||
let extension = match cache_type {
|
||||
CacheType::Declaration => "d.ts",
|
||||
CacheType::Emit => "js",
|
||||
CacheType::SourceMap => "js.map",
|
||||
CacheType::TypeScriptBuildInfo => "buildinfo",
|
||||
CacheType::Version => {
|
||||
return self.get_emit_metadata(specifier).map(|d| d.version_hash)
|
||||
}
|
||||
};
|
||||
let filename = self
|
||||
.disk_cache
|
||||
.get_cache_filename_with_extension(specifier, extension)?;
|
||||
self
|
||||
.disk_cache
|
||||
.get(&filename)
|
||||
.ok()
|
||||
.map(|b| String::from_utf8(b).ok())
|
||||
.flatten()
|
||||
}
|
||||
|
||||
fn set(
|
||||
&mut self,
|
||||
cache_type: CacheType,
|
||||
specifier: &ModuleSpecifier,
|
||||
value: String,
|
||||
) -> Result<(), AnyError> {
|
||||
let extension = match cache_type {
|
||||
CacheType::Declaration => "d.ts",
|
||||
CacheType::Emit => "js",
|
||||
CacheType::SourceMap => "js.map",
|
||||
CacheType::TypeScriptBuildInfo => "buildinfo",
|
||||
CacheType::Version => {
|
||||
let data = if let Some(mut data) = self.get_emit_metadata(specifier) {
|
||||
data.version_hash = value;
|
||||
data
|
||||
} else {
|
||||
EmitMetadata {
|
||||
version_hash: value,
|
||||
}
|
||||
};
|
||||
return self.set_emit_metadata(specifier, data);
|
||||
}
|
||||
};
|
||||
let filename = self
|
||||
.disk_cache
|
||||
.get_cache_filename_with_extension(specifier, extension)
|
||||
.unwrap();
|
||||
self
|
||||
.disk_cache
|
||||
.set(&filename, value.as_bytes())
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl CacherLoader for FetchCacher {
|
||||
fn as_cacher(&self) -> &dyn Cacher {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_mut_loader(&mut self) -> &mut dyn Loader {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_mut_cacher(&mut self) -> &mut dyn Cacher {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// An in memory cache that is used by the runtime `Deno.emit()` API to provide
|
||||
/// the same behavior as the disk cache when sources are provided.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct MemoryCacher {
|
||||
sources: HashMap<String, Arc<String>>,
|
||||
declarations: HashMap<ModuleSpecifier, String>,
|
||||
emits: HashMap<ModuleSpecifier, String>,
|
||||
maps: HashMap<ModuleSpecifier, String>,
|
||||
build_infos: HashMap<ModuleSpecifier, String>,
|
||||
versions: HashMap<ModuleSpecifier, String>,
|
||||
}
|
||||
|
||||
impl MemoryCacher {
|
||||
pub fn new(sources: HashMap<String, Arc<String>>) -> Self {
|
||||
Self {
|
||||
sources,
|
||||
declarations: HashMap::default(),
|
||||
emits: HashMap::default(),
|
||||
maps: HashMap::default(),
|
||||
build_infos: HashMap::default(),
|
||||
versions: HashMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Loader for MemoryCacher {
|
||||
fn load(
|
||||
&mut self,
|
||||
specifier: &ModuleSpecifier,
|
||||
_is_dynamic: bool,
|
||||
) -> LoadFuture {
|
||||
let mut specifier_str = specifier.to_string();
|
||||
if !self.sources.contains_key(&specifier_str) {
|
||||
specifier_str = specifier_str.replace("file:///", "/");
|
||||
if !self.sources.contains_key(&specifier_str) {
|
||||
specifier_str = specifier_str[3..].to_string();
|
||||
}
|
||||
}
|
||||
let response = self.sources.get(&specifier_str).map(|c| LoadResponse {
|
||||
specifier: specifier.clone(),
|
||||
maybe_headers: None,
|
||||
content: c.to_owned(),
|
||||
});
|
||||
Box::pin(future::ready((specifier.clone(), Ok(response))))
|
||||
}
|
||||
}
|
||||
|
||||
impl Cacher for MemoryCacher {
|
||||
fn get(
|
||||
&self,
|
||||
cache_type: CacheType,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Option<String> {
|
||||
match cache_type {
|
||||
CacheType::Declaration => self.declarations.get(specifier).cloned(),
|
||||
CacheType::Emit => self.emits.get(specifier).cloned(),
|
||||
CacheType::SourceMap => self.maps.get(specifier).cloned(),
|
||||
CacheType::TypeScriptBuildInfo => {
|
||||
self.build_infos.get(specifier).cloned()
|
||||
}
|
||||
CacheType::Version => self.versions.get(specifier).cloned(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set(
|
||||
&mut self,
|
||||
cache_type: CacheType,
|
||||
specifier: &ModuleSpecifier,
|
||||
value: String,
|
||||
) -> Result<(), AnyError> {
|
||||
match cache_type {
|
||||
CacheType::Declaration => {
|
||||
self.declarations.insert(specifier.clone(), value)
|
||||
}
|
||||
CacheType::Emit => self.emits.insert(specifier.clone(), value),
|
||||
CacheType::SourceMap => self.maps.insert(specifier.clone(), value),
|
||||
CacheType::TypeScriptBuildInfo => {
|
||||
self.build_infos.insert(specifier.clone(), value)
|
||||
}
|
||||
CacheType::Version => self.versions.insert(specifier.clone(), value),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CacherLoader for MemoryCacher {
|
||||
fn as_cacher(&self) -> &dyn Cacher {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_mut_loader(&mut self) -> &mut dyn Loader {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_mut_cacher(&mut self) -> &mut dyn Cacher {
|
||||
self
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ use deno_core::url::Url;
|
|||
use std::collections::HashMap;
|
||||
|
||||
static STD_NODE: &str = "https://deno.land/std/node/";
|
||||
static GLOBAL_MODULE: &str = "global.ts";
|
||||
|
||||
static SUPPORTED_MODULES: &[&str] = &[
|
||||
"assert",
|
||||
|
@ -50,8 +51,15 @@ static SUPPORTED_MODULES: &[&str] = &[
|
|||
"zlib",
|
||||
];
|
||||
|
||||
pub fn get_node_globals_url() -> Url {
|
||||
Url::parse(&format!("{}global.ts", STD_NODE)).unwrap()
|
||||
lazy_static::lazy_static! {
|
||||
static ref GLOBAL_URL_STR: String = format!("{}{}", STD_NODE, GLOBAL_MODULE);
|
||||
pub(crate) static ref GLOBAL_URL: Url = Url::parse(&GLOBAL_URL_STR).unwrap();
|
||||
static ref COMPAT_IMPORT_URL: Url = Url::parse("flags:compat").unwrap();
|
||||
}
|
||||
|
||||
/// Provide imports into a module graph when the compat flag is true.
|
||||
pub(crate) fn get_node_imports() -> Vec<(Url, Vec<String>)> {
|
||||
vec![(COMPAT_IMPORT_URL.clone(), vec![GLOBAL_URL_STR.clone()])]
|
||||
}
|
||||
|
||||
/// Create a map that can be used to update import map.
|
||||
|
|
|
@ -10,6 +10,7 @@ use deno_core::serde::Serializer;
|
|||
use deno_core::serde_json;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::serde_json::Value;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
|
@ -401,6 +402,19 @@ impl ConfigFile {
|
|||
}
|
||||
}
|
||||
|
||||
/// If the configuration file contains "extra" modules (like TypeScript
|
||||
/// `"types"`) options, return them as imports to be added to a module graph.
|
||||
pub fn to_maybe_imports(
|
||||
&self,
|
||||
) -> Option<Vec<(ModuleSpecifier, Vec<String>)>> {
|
||||
let compiler_options_value = self.json.compiler_options.as_ref()?;
|
||||
let compiler_options: CompilerOptions =
|
||||
serde_json::from_value(compiler_options_value.clone()).ok()?;
|
||||
let referrer = ModuleSpecifier::from_file_path(&self.path).ok()?;
|
||||
let types = compiler_options.types?;
|
||||
Some(vec![(referrer, types)])
|
||||
}
|
||||
|
||||
pub fn to_fmt_config(&self) -> Result<Option<FmtConfig>, AnyError> {
|
||||
if let Some(config) = self.json.fmt.clone() {
|
||||
let fmt_config: FmtConfig = serde_json::from_value(config)
|
||||
|
|
|
@ -6,9 +6,8 @@ use deno_core::serde::Deserialize;
|
|||
use deno_core::serde::Deserializer;
|
||||
use deno_core::serde::Serialize;
|
||||
use deno_core::serde::Serializer;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_graph::ModuleGraphError;
|
||||
use regex::Regex;
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
|
@ -353,20 +352,17 @@ impl Diagnostics {
|
|||
Diagnostics(diagnostics)
|
||||
}
|
||||
|
||||
pub fn extend_graph_errors(
|
||||
&mut self,
|
||||
errors: HashMap<ModuleSpecifier, String>,
|
||||
) {
|
||||
self.0.extend(errors.into_iter().map(|(s, e)| Diagnostic {
|
||||
pub fn extend_graph_errors(&mut self, errors: Vec<ModuleGraphError>) {
|
||||
self.0.extend(errors.into_iter().map(|err| Diagnostic {
|
||||
category: DiagnosticCategory::Error,
|
||||
code: 900001,
|
||||
start: None,
|
||||
end: None,
|
||||
message_text: Some(e),
|
||||
message_text: Some(err.to_string()),
|
||||
message_chain: None,
|
||||
source: None,
|
||||
source_line: None,
|
||||
file_name: Some(s.to_string()),
|
||||
file_name: Some(err.specifier().to_string()),
|
||||
related_information: None,
|
||||
}));
|
||||
}
|
||||
|
|
927
cli/emit.rs
Normal file
927
cli/emit.rs
Normal file
|
@ -0,0 +1,927 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
//! The collection of APIs to be able to take `deno_graph` module graphs and
|
||||
//! populate a cache, emit files, and transform a graph into the structures for
|
||||
//! loading into an isolate.
|
||||
|
||||
use crate::ast;
|
||||
use crate::cache::CacheType;
|
||||
use crate::cache::Cacher;
|
||||
use crate::colors;
|
||||
use crate::config_file::ConfigFile;
|
||||
use crate::config_file::IgnoredCompilerOptions;
|
||||
use crate::config_file::TsConfig;
|
||||
use crate::diagnostics::Diagnostics;
|
||||
use crate::tsc;
|
||||
use crate::version;
|
||||
|
||||
use deno_ast::swc;
|
||||
use deno_core::error::anyhow;
|
||||
use deno_core::error::custom_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::error::Context;
|
||||
use deno_core::serde::Deserialize;
|
||||
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_json::Value;
|
||||
use deno_core::ModuleSource;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_graph::MediaType;
|
||||
use deno_graph::ModuleGraph;
|
||||
use deno_graph::ModuleGraphError;
|
||||
use deno_graph::ResolutionError;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
use std::result;
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
|
||||
/// Represents the "default" type library that should be used when type
|
||||
/// checking the code in the module graph. Note that a user provided config
|
||||
/// of `"lib"` would override this value.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub(crate) enum TypeLib {
|
||||
DenoWindow,
|
||||
DenoWorker,
|
||||
UnstableDenoWindow,
|
||||
UnstableDenoWorker,
|
||||
}
|
||||
|
||||
impl Default for TypeLib {
|
||||
fn default() -> Self {
|
||||
Self::DenoWindow
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for TypeLib {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let value = match self {
|
||||
Self::DenoWindow => vec!["deno.window".to_string()],
|
||||
Self::DenoWorker => vec!["deno.worker".to_string()],
|
||||
Self::UnstableDenoWindow => {
|
||||
vec!["deno.window".to_string(), "deno.unstable".to_string()]
|
||||
}
|
||||
Self::UnstableDenoWorker => {
|
||||
vec!["deno.worker".to_string(), "deno.unstable".to_string()]
|
||||
}
|
||||
};
|
||||
Serialize::serialize(&value, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
type Modules = HashMap<ModuleSpecifier, Result<ModuleSource, AnyError>>;
|
||||
|
||||
/// A structure representing stats from an emit operation for a graph.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub(crate) struct Stats(pub Vec<(String, u32)>);
|
||||
|
||||
impl<'de> Deserialize<'de> for Stats {
|
||||
fn deserialize<D>(deserializer: D) -> result::Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let items: Vec<(String, u32)> = Deserialize::deserialize(deserializer)?;
|
||||
Ok(Stats(items))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Stats {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
Serialize::serialize(&self.0, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Stats {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(f, "Compilation statistics:")?;
|
||||
for (key, value) in self.0.clone() {
|
||||
writeln!(f, " {}: {}", key, value)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// An enum that represents the base tsc configuration to return.
|
||||
pub(crate) enum ConfigType {
|
||||
/// Return a configuration for bundling, using swc to emit the bundle. This is
|
||||
/// independent of type checking.
|
||||
Bundle,
|
||||
/// Return a configuration to use tsc to type check and optionally emit. This
|
||||
/// is independent of either bundling or just emitting via swc
|
||||
Check { lib: TypeLib, tsc_emit: bool },
|
||||
/// Return a configuration to use swc to emit single module files.
|
||||
Emit,
|
||||
/// Return a configuration as a base for the runtime `Deno.emit()` API.
|
||||
RuntimeEmit { tsc_emit: bool },
|
||||
}
|
||||
|
||||
/// For a given configuration type and optionally a configuration file, return a
|
||||
/// tuple of the resulting `TsConfig` struct and optionally any user
|
||||
/// configuration options that were ignored.
|
||||
pub(crate) fn get_ts_config(
|
||||
config_type: ConfigType,
|
||||
maybe_config_file: Option<&ConfigFile>,
|
||||
maybe_user_config: Option<&HashMap<String, Value>>,
|
||||
) -> Result<(TsConfig, Option<IgnoredCompilerOptions>), AnyError> {
|
||||
let mut ts_config = match config_type {
|
||||
ConfigType::Bundle => TsConfig::new(json!({
|
||||
"checkJs": false,
|
||||
"emitDecoratorMetadata": false,
|
||||
"importsNotUsedAsValues": "remove",
|
||||
"inlineSourceMap": false,
|
||||
"inlineSources": false,
|
||||
"sourceMap": false,
|
||||
"jsx": "react",
|
||||
"jsxFactory": "React.createElement",
|
||||
"jsxFragmentFactory": "React.Fragment",
|
||||
})),
|
||||
ConfigType::Check { tsc_emit, lib } => {
|
||||
let mut ts_config = TsConfig::new(json!({
|
||||
"allowJs": true,
|
||||
"experimentalDecorators": true,
|
||||
"incremental": true,
|
||||
"jsx": "react",
|
||||
"isolatedModules": true,
|
||||
"lib": lib,
|
||||
"module": "esnext",
|
||||
"strict": true,
|
||||
"target": "esnext",
|
||||
"tsBuildInfoFile": "deno:///.tsbuildinfo",
|
||||
"useDefineForClassFields": true,
|
||||
// TODO(@kitsonk) remove for Deno 2.0
|
||||
"useUnknownInCatchVariables": false,
|
||||
}));
|
||||
if tsc_emit {
|
||||
ts_config.merge(&json!({
|
||||
"emitDecoratorMetadata": false,
|
||||
"importsNotUsedAsValues": "remove",
|
||||
"inlineSourceMap": true,
|
||||
"inlineSources": true,
|
||||
"outDir": "deno://",
|
||||
"removeComments": true,
|
||||
}));
|
||||
} else {
|
||||
ts_config.merge(&json!({
|
||||
"noEmit": true,
|
||||
}));
|
||||
}
|
||||
ts_config
|
||||
}
|
||||
ConfigType::Emit => TsConfig::new(json!({
|
||||
"checkJs": false,
|
||||
"emitDecoratorMetadata": false,
|
||||
"importsNotUsedAsValues": "remove",
|
||||
"inlineSourceMap": true,
|
||||
// TODO(@kitsonk) make this actually work when https://github.com/swc-project/swc/issues/2218 addressed.
|
||||
"inlineSources": true,
|
||||
"sourceMap": false,
|
||||
"jsx": "react",
|
||||
"jsxFactory": "React.createElement",
|
||||
"jsxFragmentFactory": "React.Fragment",
|
||||
})),
|
||||
ConfigType::RuntimeEmit { tsc_emit } => {
|
||||
let mut ts_config = TsConfig::new(json!({
|
||||
"allowJs": true,
|
||||
"checkJs": false,
|
||||
"emitDecoratorMetadata": false,
|
||||
"experimentalDecorators": true,
|
||||
"importsNotUsedAsValues": "remove",
|
||||
"incremental": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "react",
|
||||
"jsxFactory": "React.createElement",
|
||||
"jsxFragmentFactory": "React.Fragment",
|
||||
"lib": TypeLib::DenoWindow,
|
||||
"module": "esnext",
|
||||
"removeComments": true,
|
||||
"inlineSourceMap": false,
|
||||
"inlineSources": false,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"target": "esnext",
|
||||
"tsBuildInfoFile": "deno:///.tsbuildinfo",
|
||||
"useDefineForClassFields": true,
|
||||
// TODO(@kitsonk) remove for Deno 2.0
|
||||
"useUnknownInCatchVariables": false,
|
||||
}));
|
||||
if tsc_emit {
|
||||
ts_config.merge(&json!({
|
||||
"importsNotUsedAsValues": "remove",
|
||||
"outDir": "deno://",
|
||||
}));
|
||||
} else {
|
||||
ts_config.merge(&json!({
|
||||
"noEmit": true,
|
||||
}));
|
||||
}
|
||||
ts_config
|
||||
}
|
||||
};
|
||||
let maybe_ignored_options = if let Some(user_options) = maybe_user_config {
|
||||
ts_config.merge_user_config(user_options)?
|
||||
} else {
|
||||
ts_config.merge_tsconfig_from_config_file(maybe_config_file)?
|
||||
};
|
||||
Ok((ts_config, maybe_ignored_options))
|
||||
}
|
||||
|
||||
/// Transform the graph into root specifiers that we can feed `tsc`. We have to
|
||||
/// provide the media type for root modules because `tsc` does not "resolve" the
|
||||
/// media type like other modules, as well as a root specifier needs any
|
||||
/// redirects resolved. If we aren't checking JavaScript, we need to include all
|
||||
/// the emittable files in the roots, so they get type checked and optionally
|
||||
/// emitted, otherwise they would be ignored if only imported into JavaScript.
|
||||
fn get_root_names(
|
||||
graph: &ModuleGraph,
|
||||
check_js: bool,
|
||||
) -> Vec<(ModuleSpecifier, MediaType)> {
|
||||
if !check_js {
|
||||
graph
|
||||
.specifiers()
|
||||
.into_iter()
|
||||
.filter_map(|(_, r)| match r {
|
||||
Ok((s, mt)) => match &mt {
|
||||
MediaType::TypeScript | MediaType::Tsx | MediaType::Jsx => {
|
||||
Some((s, mt))
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
graph
|
||||
.roots
|
||||
.iter()
|
||||
.filter_map(|s| graph.get(s).map(|m| (m.specifier.clone(), m.media_type)))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// A hashing function that takes the source code, version and optionally a
|
||||
/// user provided config and generates a string hash which can be stored to
|
||||
/// determine if the cached emit is valid or not.
|
||||
fn get_version(source_bytes: &[u8], config_bytes: &[u8]) -> String {
|
||||
crate::checksum::gen(&[
|
||||
source_bytes,
|
||||
version::deno().as_bytes(),
|
||||
config_bytes,
|
||||
])
|
||||
}
|
||||
|
||||
/// Determine if a given media type is emittable or not.
|
||||
fn is_emittable(media_type: &MediaType, include_js: bool) -> bool {
|
||||
match &media_type {
|
||||
MediaType::TypeScript | MediaType::Tsx | MediaType::Jsx => true,
|
||||
MediaType::JavaScript => include_js,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Options for performing a check of a module graph. Note that the decision to
|
||||
/// emit or not is determined by the `ts_config` settings.
|
||||
pub(crate) struct CheckOptions {
|
||||
/// Set the debug flag on the TypeScript type checker.
|
||||
pub debug: bool,
|
||||
/// If true, any files emitted will be cached, even if there are diagnostics
|
||||
/// produced. If false, if there are diagnostics, caching emitted files will
|
||||
/// be skipped.
|
||||
pub emit_with_diagnostics: bool,
|
||||
/// The module specifier to the configuration file, passed to tsc so that
|
||||
/// configuration related diagnostics are properly formed.
|
||||
pub maybe_config_specifier: Option<ModuleSpecifier>,
|
||||
/// The derived tsconfig that should be used when checking.
|
||||
pub ts_config: TsConfig,
|
||||
}
|
||||
|
||||
/// The result of a check or emit of a module graph. Note that the actual
|
||||
/// emitted sources are stored in the cache and are not returned in the result.
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct CheckEmitResult {
|
||||
pub diagnostics: Diagnostics,
|
||||
pub stats: Stats,
|
||||
}
|
||||
|
||||
/// Given a module graph, type check the module graph and optionally emit
|
||||
/// modules, updating the cache as appropriate. Emitting is determined by the
|
||||
/// `ts_config` supplied in the options, and if emitting, the files are stored
|
||||
/// in the cache.
|
||||
///
|
||||
/// It is expected that it is determined if a check and/or emit is validated
|
||||
/// before the function is called.
|
||||
pub(crate) fn check_and_maybe_emit(
|
||||
graph: Arc<ModuleGraph>,
|
||||
cache: &mut dyn Cacher,
|
||||
options: CheckOptions,
|
||||
) -> Result<CheckEmitResult, AnyError> {
|
||||
let check_js = options.ts_config.get_check_js();
|
||||
let root_names = get_root_names(&graph, check_js);
|
||||
// while there might be multiple roots, we can't "merge" the build info, so we
|
||||
// try to retrieve the build info for first root, which is the most common use
|
||||
// case.
|
||||
let maybe_tsbuildinfo =
|
||||
cache.get(CacheType::TypeScriptBuildInfo, &graph.roots[0]);
|
||||
// to make tsc build info work, we need to consistently hash modules, so that
|
||||
// tsc can better determine if an emit is still valid or not, so we provide
|
||||
// that data here.
|
||||
let hash_data = vec![
|
||||
options.ts_config.as_bytes(),
|
||||
version::deno().as_bytes().to_owned(),
|
||||
];
|
||||
let config_bytes = options.ts_config.as_bytes();
|
||||
|
||||
let response = tsc::exec(tsc::Request {
|
||||
config: options.ts_config,
|
||||
debug: options.debug,
|
||||
graph: graph.clone(),
|
||||
hash_data,
|
||||
maybe_config_specifier: options.maybe_config_specifier,
|
||||
maybe_tsbuildinfo,
|
||||
root_names,
|
||||
})?;
|
||||
|
||||
if let Some(info) = &response.maybe_tsbuildinfo {
|
||||
// while we retrieve the build info for just the first module, it can be
|
||||
// used for all the roots in the graph, so we will cache it for all roots
|
||||
for root in &graph.roots {
|
||||
cache.set(CacheType::TypeScriptBuildInfo, root, info.clone())?;
|
||||
}
|
||||
}
|
||||
// sometimes we want to emit when there are diagnostics, and sometimes we
|
||||
// don't. tsc will always return an emit if there are diagnostics
|
||||
if (response.diagnostics.is_empty() || options.emit_with_diagnostics)
|
||||
&& !response.emitted_files.is_empty()
|
||||
{
|
||||
for emit in response.emitted_files.into_iter() {
|
||||
if let Some(specifiers) = emit.maybe_specifiers {
|
||||
assert!(specifiers.len() == 1);
|
||||
// The emitted specifier might not be the file specifier we want, so we
|
||||
// resolve it via the graph.
|
||||
let specifier = graph.resolve(&specifiers[0]);
|
||||
let (media_type, source) = if let Some(module) = graph.get(&specifier) {
|
||||
(&module.media_type, module.source.clone())
|
||||
} else {
|
||||
log::debug!("module missing, skipping emit for {}", specifier);
|
||||
continue;
|
||||
};
|
||||
// Sometimes if `tsc` sees a CommonJS file it will _helpfully_ output it
|
||||
// to ESM, which we don't really want to do unless someone has enabled
|
||||
// check_js.
|
||||
if !check_js && *media_type == MediaType::JavaScript {
|
||||
log::debug!("skipping emit for {}", specifier);
|
||||
continue;
|
||||
}
|
||||
match emit.media_type {
|
||||
MediaType::JavaScript => {
|
||||
let version = get_version(source.as_bytes(), &config_bytes);
|
||||
cache.set(CacheType::Version, &specifier, version)?;
|
||||
cache.set(CacheType::Emit, &specifier, emit.data)?;
|
||||
}
|
||||
MediaType::SourceMap => {
|
||||
cache.set(CacheType::SourceMap, &specifier, emit.data)?;
|
||||
}
|
||||
// this only occurs with the runtime emit, but we are using the same
|
||||
// code paths, so we handle it here.
|
||||
MediaType::Dts => {
|
||||
cache.set(CacheType::Declaration, &specifier, emit.data)?;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(CheckEmitResult {
|
||||
diagnostics: response.diagnostics,
|
||||
stats: response.stats,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) enum BundleType {
|
||||
/// Return the emitted contents of the program as a single "flattened" ES
|
||||
/// module.
|
||||
Module,
|
||||
/// Return the emitted contents of the program as a single script that
|
||||
/// executes the program using an immediately invoked function execution
|
||||
/// (IIFE).
|
||||
Classic,
|
||||
}
|
||||
|
||||
impl From<BundleType> for swc::bundler::ModuleType {
|
||||
fn from(bundle_type: BundleType) -> Self {
|
||||
match bundle_type {
|
||||
BundleType::Classic => Self::Iife,
|
||||
BundleType::Module => Self::Es,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct BundleOptions {
|
||||
pub bundle_type: BundleType,
|
||||
pub ts_config: TsConfig,
|
||||
}
|
||||
|
||||
/// A module loader for swc which does the appropriate retrieval and transpiling
|
||||
/// of modules from the graph.
|
||||
struct BundleLoader<'a> {
|
||||
cm: Rc<swc::common::SourceMap>,
|
||||
emit_options: &'a ast::EmitOptions,
|
||||
globals: &'a deno_ast::swc::common::Globals,
|
||||
graph: &'a ModuleGraph,
|
||||
}
|
||||
|
||||
impl swc::bundler::Load for BundleLoader<'_> {
|
||||
fn load(
|
||||
&self,
|
||||
file_name: &swc::common::FileName,
|
||||
) -> Result<swc::bundler::ModuleData, AnyError> {
|
||||
match file_name {
|
||||
swc::common::FileName::Url(specifier) => {
|
||||
if let Some(m) = self.graph.get(specifier) {
|
||||
let (fm, module) = ast::transpile_module(
|
||||
specifier,
|
||||
&m.source,
|
||||
m.media_type,
|
||||
self.emit_options,
|
||||
self.globals,
|
||||
self.cm.clone(),
|
||||
)?;
|
||||
Ok(swc::bundler::ModuleData {
|
||||
fm,
|
||||
module,
|
||||
helpers: Default::default(),
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"Module \"{}\" unexpectedly missing when bundling.",
|
||||
specifier
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => unreachable!(
|
||||
"Received a request for unsupported filename {:?}",
|
||||
file_name
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A resolver implementation for swc that resolves specifiers from the graph.
|
||||
struct BundleResolver<'a>(&'a ModuleGraph);
|
||||
|
||||
impl swc::bundler::Resolve for BundleResolver<'_> {
|
||||
fn resolve(
|
||||
&self,
|
||||
referrer: &swc::common::FileName,
|
||||
specifier: &str,
|
||||
) -> Result<swc::common::FileName, AnyError> {
|
||||
let referrer = if let swc::common::FileName::Url(referrer) = referrer {
|
||||
referrer
|
||||
} else {
|
||||
unreachable!(
|
||||
"An unexpected referrer was passed when bundling: {:?}",
|
||||
referrer
|
||||
);
|
||||
};
|
||||
if let Some(specifier) =
|
||||
self.0.resolve_dependency(specifier, referrer, false)
|
||||
{
|
||||
Ok(deno_ast::swc::common::FileName::Url(specifier.clone()))
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"Cannot resolve \"{}\" from \"{}\".",
|
||||
specifier,
|
||||
referrer
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a module graph, generate and return a bundle of the graph and
|
||||
/// optionally its source map. Unlike emitting with `check_and_maybe_emit` and
|
||||
/// `emit`, which store the emitted modules in the cache, this function simply
|
||||
/// returns the output.
|
||||
pub(crate) fn bundle(
|
||||
graph: &ModuleGraph,
|
||||
options: BundleOptions,
|
||||
) -> Result<(String, Option<String>), AnyError> {
|
||||
let emit_options: ast::EmitOptions = options.ts_config.into();
|
||||
|
||||
let cm = Rc::new(swc::common::SourceMap::new(
|
||||
swc::common::FilePathMapping::empty(),
|
||||
));
|
||||
let globals = swc::common::Globals::new();
|
||||
let loader = BundleLoader {
|
||||
graph,
|
||||
emit_options: &emit_options,
|
||||
globals: &globals,
|
||||
cm: cm.clone(),
|
||||
};
|
||||
let resolver = BundleResolver(graph);
|
||||
let config = swc::bundler::Config {
|
||||
module: options.bundle_type.into(),
|
||||
..Default::default()
|
||||
};
|
||||
// This hook will rewrite the `import.meta` when bundling to give a consistent
|
||||
// behavior between bundled and unbundled code.
|
||||
let hook = Box::new(ast::BundleHook);
|
||||
let bundler = swc::bundler::Bundler::new(
|
||||
&globals,
|
||||
cm.clone(),
|
||||
loader,
|
||||
resolver,
|
||||
config,
|
||||
hook,
|
||||
);
|
||||
let mut entries = HashMap::new();
|
||||
entries.insert(
|
||||
"bundle".to_string(),
|
||||
swc::common::FileName::Url(graph.roots[0].clone()),
|
||||
);
|
||||
let output = bundler
|
||||
.bundle(entries)
|
||||
.context("Unable to output during bundling.")?;
|
||||
let mut buf = Vec::new();
|
||||
let mut srcmap = Vec::new();
|
||||
{
|
||||
let cfg = swc::codegen::Config { minify: false };
|
||||
let wr = Box::new(swc::codegen::text_writer::JsWriter::new(
|
||||
cm.clone(),
|
||||
"\n",
|
||||
&mut buf,
|
||||
Some(&mut srcmap),
|
||||
));
|
||||
let mut emitter = swc::codegen::Emitter {
|
||||
cfg,
|
||||
cm: cm.clone(),
|
||||
comments: None,
|
||||
wr,
|
||||
};
|
||||
emitter
|
||||
.emit_module(&output[0].module)
|
||||
.context("Unable to emit during bundling.")?;
|
||||
}
|
||||
let mut code =
|
||||
String::from_utf8(buf).context("Emitted code is an invalid string.")?;
|
||||
let mut maybe_map: Option<String> = None;
|
||||
{
|
||||
let mut buf = Vec::new();
|
||||
cm.build_source_map_from(&mut srcmap, None)
|
||||
.to_writer(&mut buf)?;
|
||||
if emit_options.inline_source_map {
|
||||
let encoded_map = format!(
|
||||
"//# sourceMappingURL=data:application/json;base64,{}\n",
|
||||
base64::encode(buf)
|
||||
);
|
||||
code.push_str(&encoded_map);
|
||||
} else if emit_options.source_map {
|
||||
maybe_map = Some(String::from_utf8(buf)?);
|
||||
}
|
||||
}
|
||||
|
||||
Ok((code, maybe_map))
|
||||
}
|
||||
|
||||
pub(crate) struct EmitOptions {
|
||||
pub ts_config: TsConfig,
|
||||
pub reload_exclusions: HashSet<ModuleSpecifier>,
|
||||
pub reload: bool,
|
||||
}
|
||||
|
||||
/// Given a module graph, emit any appropriate modules and cache them.
|
||||
pub(crate) fn emit(
|
||||
graph: &ModuleGraph,
|
||||
cache: &mut dyn Cacher,
|
||||
options: EmitOptions,
|
||||
) -> Result<CheckEmitResult, AnyError> {
|
||||
let start = Instant::now();
|
||||
let config_bytes = options.ts_config.as_bytes();
|
||||
let include_js = options.ts_config.get_check_js();
|
||||
let emit_options = options.ts_config.into();
|
||||
|
||||
let mut emit_count = 0_u32;
|
||||
let mut file_count = 0_u32;
|
||||
for module in graph.modules() {
|
||||
file_count += 1;
|
||||
if !is_emittable(&module.media_type, include_js) {
|
||||
continue;
|
||||
}
|
||||
let needs_reload =
|
||||
options.reload && !options.reload_exclusions.contains(&module.specifier);
|
||||
let version = get_version(module.source.as_bytes(), &config_bytes);
|
||||
let is_valid = cache
|
||||
.get(CacheType::Version, &module.specifier)
|
||||
.map_or(false, |v| {
|
||||
v == get_version(module.source.as_bytes(), &config_bytes)
|
||||
});
|
||||
if is_valid && !needs_reload {
|
||||
continue;
|
||||
}
|
||||
let (emit, maybe_map) =
|
||||
ast::transpile(&module.parsed_source, &emit_options)?;
|
||||
emit_count += 1;
|
||||
cache.set(CacheType::Emit, &module.specifier, emit)?;
|
||||
if let Some(map) = maybe_map {
|
||||
cache.set(CacheType::SourceMap, &module.specifier, map)?;
|
||||
}
|
||||
if !is_valid {
|
||||
cache.set(CacheType::Version, &module.specifier, version)?;
|
||||
}
|
||||
}
|
||||
|
||||
let stats = Stats(vec![
|
||||
("Files".to_string(), file_count),
|
||||
("Emitted".to_string(), emit_count),
|
||||
("Total time".to_string(), start.elapsed().as_millis() as u32),
|
||||
]);
|
||||
|
||||
Ok(CheckEmitResult {
|
||||
diagnostics: Diagnostics::default(),
|
||||
stats,
|
||||
})
|
||||
}
|
||||
|
||||
/// Check the sub-resource integrity of a module graph, exiting if the graph is
|
||||
/// not valid.
|
||||
pub(crate) fn lock(graph: &ModuleGraph) {
|
||||
if let Err(err) = graph.lock() {
|
||||
log::error!("{} {}", colors::red("error:"), err);
|
||||
std::process::exit(10);
|
||||
}
|
||||
}
|
||||
|
||||
/// Check a module graph to determine if the graph contains anything that
|
||||
/// is required to be emitted to be valid. It determines what modules in the
|
||||
/// graph are emittable and for those that are emittable, if there is currently
|
||||
/// a valid emit in the cache.
|
||||
pub(crate) fn valid_emit(
|
||||
graph: &ModuleGraph,
|
||||
cache: &dyn Cacher,
|
||||
ts_config: &TsConfig,
|
||||
reload: bool,
|
||||
reload_exclusions: &HashSet<ModuleSpecifier>,
|
||||
) -> bool {
|
||||
let config_bytes = ts_config.as_bytes();
|
||||
let emit_js = ts_config.get_check_js();
|
||||
graph
|
||||
.specifiers()
|
||||
.iter()
|
||||
.filter(|(_, r)| match r {
|
||||
Ok((_, MediaType::TypeScript))
|
||||
| Ok((_, MediaType::Tsx))
|
||||
| Ok((_, MediaType::Jsx)) => true,
|
||||
Ok((_, MediaType::JavaScript)) => emit_js,
|
||||
_ => false,
|
||||
})
|
||||
.all(|(_, r)| {
|
||||
if let Ok((s, _)) = r {
|
||||
if reload && !reload_exclusions.contains(s) {
|
||||
// we are reloading and the specifier isn't excluded from being
|
||||
// reloaded
|
||||
false
|
||||
} else if let Some(version) = cache.get(CacheType::Version, s) {
|
||||
if let Some(module) = graph.get(s) {
|
||||
version == get_version(module.source.as_bytes(), &config_bytes)
|
||||
} else {
|
||||
// We have a source module in the graph we can't find, so the emit is
|
||||
// clearly wrong
|
||||
false
|
||||
}
|
||||
} else {
|
||||
// A module that requires emitting doesn't have a version, so it doesn't
|
||||
// have a valid emit
|
||||
false
|
||||
}
|
||||
} else {
|
||||
// Something in the module graph is missing, but that doesn't mean the
|
||||
// emit is invalid
|
||||
true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// An adapter struct to make a deno_graph::ModuleGraphError display as expected
|
||||
/// in the Deno CLI.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct GraphError(pub ModuleGraphError);
|
||||
|
||||
impl std::error::Error for GraphError {}
|
||||
|
||||
impl From<ModuleGraphError> for GraphError {
|
||||
fn from(err: ModuleGraphError) -> Self {
|
||||
Self(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for GraphError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match &self.0 {
|
||||
ModuleGraphError::ResolutionError(err) => {
|
||||
if matches!(
|
||||
err,
|
||||
ResolutionError::InvalidDowngrade(_, _)
|
||||
| ResolutionError::InvalidLocalImport(_, _)
|
||||
) {
|
||||
write!(f, "{}", err.to_string_with_span())
|
||||
} else {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
_ => self.0.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a module graph to a map of "files", which are used by the runtime
|
||||
/// emit to be passed back to the caller.
|
||||
pub(crate) fn to_file_map(
|
||||
graph: &ModuleGraph,
|
||||
cache: &dyn Cacher,
|
||||
) -> HashMap<String, String> {
|
||||
let mut files = HashMap::new();
|
||||
for (_, result) in graph.specifiers().into_iter() {
|
||||
if let Ok((specifier, media_type)) = result {
|
||||
if let Some(emit) = cache.get(CacheType::Emit, &specifier) {
|
||||
files.insert(format!("{}.js", specifier), emit);
|
||||
if let Some(map) = cache.get(CacheType::SourceMap, &specifier) {
|
||||
files.insert(format!("{}.js.map", specifier), map);
|
||||
}
|
||||
} else if media_type == MediaType::JavaScript
|
||||
|| media_type == MediaType::Unknown
|
||||
{
|
||||
if let Some(module) = graph.get(&specifier) {
|
||||
files.insert(specifier.to_string(), module.source.to_string());
|
||||
}
|
||||
}
|
||||
if let Some(declaration) = cache.get(CacheType::Declaration, &specifier) {
|
||||
files.insert(format!("{}.d.ts", specifier), declaration);
|
||||
}
|
||||
}
|
||||
}
|
||||
files
|
||||
}
|
||||
|
||||
/// Convert a module graph to a map of module sources, which are used by
|
||||
/// `deno_core` to load modules into V8.
|
||||
pub(crate) fn to_module_sources(
|
||||
graph: &ModuleGraph,
|
||||
cache: &dyn Cacher,
|
||||
) -> Modules {
|
||||
graph
|
||||
.specifiers()
|
||||
.into_iter()
|
||||
.map(|(requested_specifier, r)| match r {
|
||||
Err(err) => (requested_specifier, Err(err.into())),
|
||||
Ok((found_specifier, media_type)) => {
|
||||
// First we check to see if there is an emitted file in the cache.
|
||||
if let Some(code) = cache.get(CacheType::Emit, &found_specifier) {
|
||||
(
|
||||
requested_specifier.clone(),
|
||||
Ok(ModuleSource {
|
||||
code,
|
||||
module_url_found: found_specifier.to_string(),
|
||||
module_url_specified: requested_specifier.to_string(),
|
||||
}),
|
||||
)
|
||||
// Then if the file is JavaScript (or unknown) and wasn't emitted, we
|
||||
// will load the original source code in the module.
|
||||
} else if media_type == MediaType::JavaScript
|
||||
|| media_type == MediaType::Unknown
|
||||
{
|
||||
if let Some(module) = graph.get(&found_specifier) {
|
||||
(
|
||||
requested_specifier.clone(),
|
||||
Ok(ModuleSource {
|
||||
code: module.source.as_str().to_string(),
|
||||
module_url_found: module.specifier.to_string(),
|
||||
module_url_specified: requested_specifier.to_string(),
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
unreachable!(
|
||||
"unexpected module missing from graph: {}",
|
||||
found_specifier
|
||||
)
|
||||
}
|
||||
// Otherwise we will add a not found error.
|
||||
} else {
|
||||
(
|
||||
requested_specifier.clone(),
|
||||
Err(custom_error(
|
||||
"NotFound",
|
||||
format!(
|
||||
"Emitted code for \"{}\" not found.",
|
||||
requested_specifier
|
||||
),
|
||||
)),
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::cache::MemoryCacher;
|
||||
|
||||
#[test]
|
||||
fn test_is_emittable() {
|
||||
assert!(is_emittable(&MediaType::TypeScript, false));
|
||||
assert!(!is_emittable(&MediaType::Dts, false));
|
||||
assert!(is_emittable(&MediaType::Tsx, false));
|
||||
assert!(!is_emittable(&MediaType::JavaScript, false));
|
||||
assert!(is_emittable(&MediaType::JavaScript, true));
|
||||
assert!(is_emittable(&MediaType::Jsx, false));
|
||||
assert!(!is_emittable(&MediaType::Json, false));
|
||||
}
|
||||
|
||||
async fn setup<S: AsRef<str>>(
|
||||
root: S,
|
||||
sources: Vec<(S, S)>,
|
||||
) -> (ModuleGraph, MemoryCacher) {
|
||||
let roots = vec![ModuleSpecifier::parse(root.as_ref()).unwrap()];
|
||||
let sources = sources
|
||||
.into_iter()
|
||||
.map(|(s, c)| (s.as_ref().to_string(), Arc::new(c.as_ref().to_string())))
|
||||
.collect();
|
||||
let mut cache = MemoryCacher::new(sources);
|
||||
let graph = deno_graph::create_graph(
|
||||
roots, false, None, &mut cache, None, None, None,
|
||||
)
|
||||
.await;
|
||||
(graph, cache)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_to_module_sources_emitted() {
|
||||
let (graph, mut cache) = setup(
|
||||
"https://example.com/a.ts",
|
||||
vec![("https://example.com/a.ts", r#"console.log("hello deno");"#)],
|
||||
)
|
||||
.await;
|
||||
let (ts_config, _) = get_ts_config(ConfigType::Emit, None, None).unwrap();
|
||||
emit(
|
||||
&graph,
|
||||
&mut cache,
|
||||
EmitOptions {
|
||||
ts_config,
|
||||
reload_exclusions: HashSet::default(),
|
||||
reload: false,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let modules = to_module_sources(&graph, &cache);
|
||||
assert_eq!(modules.len(), 1);
|
||||
let root = ModuleSpecifier::parse("https://example.com/a.ts").unwrap();
|
||||
let maybe_result = modules.get(&root);
|
||||
assert!(maybe_result.is_some());
|
||||
let result = maybe_result.unwrap();
|
||||
assert!(result.is_ok());
|
||||
let module_source = result.as_ref().unwrap();
|
||||
assert!(module_source
|
||||
.code
|
||||
.starts_with(r#"console.log("hello deno");"#));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_to_module_sources_not_emitted() {
|
||||
let (graph, mut cache) = setup(
|
||||
"https://example.com/a.js",
|
||||
vec![("https://example.com/a.js", r#"console.log("hello deno");"#)],
|
||||
)
|
||||
.await;
|
||||
let (ts_config, _) = get_ts_config(ConfigType::Emit, None, None).unwrap();
|
||||
emit(
|
||||
&graph,
|
||||
&mut cache,
|
||||
EmitOptions {
|
||||
ts_config,
|
||||
reload_exclusions: HashSet::default(),
|
||||
reload: false,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let modules = to_module_sources(&graph, &cache);
|
||||
assert_eq!(modules.len(), 1);
|
||||
let root = ModuleSpecifier::parse("https://example.com/a.js").unwrap();
|
||||
let maybe_result = modules.get(&root);
|
||||
assert!(maybe_result.is_some());
|
||||
let result = maybe_result.unwrap();
|
||||
assert!(result.is_ok());
|
||||
let module_source = result.as_ref().unwrap();
|
||||
assert_eq!(module_source.code, r#"console.log("hello deno");"#);
|
||||
}
|
||||
}
|
|
@ -9,8 +9,12 @@
|
|||
//! Diagnostics are compile-time type errors, whereas JsErrors are runtime
|
||||
//! exceptions.
|
||||
|
||||
use crate::emit::GraphError;
|
||||
|
||||
use deno_ast::Diagnostic;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_graph::ModuleGraphError;
|
||||
use deno_graph::ResolutionError;
|
||||
use import_map::ImportMapError;
|
||||
|
||||
fn get_import_map_error_class(_: &ImportMapError) -> &'static str {
|
||||
|
@ -21,6 +25,34 @@ fn get_diagnostic_class(_: &Diagnostic) -> &'static str {
|
|||
"SyntaxError"
|
||||
}
|
||||
|
||||
fn get_graph_error_class(err: &GraphError) -> &'static str {
|
||||
get_module_graph_error_class(&err.0)
|
||||
}
|
||||
|
||||
pub(crate) fn get_module_graph_error_class(
|
||||
err: &ModuleGraphError,
|
||||
) -> &'static str {
|
||||
match err {
|
||||
ModuleGraphError::LoadingErr(_, err) => get_error_class_name(err.as_ref()),
|
||||
ModuleGraphError::InvalidSource(_, _) => "SyntaxError",
|
||||
ModuleGraphError::ParseErr(_, diagnostic) => {
|
||||
get_diagnostic_class(diagnostic)
|
||||
}
|
||||
ModuleGraphError::ResolutionError(err) => get_resolution_error_class(err),
|
||||
ModuleGraphError::UnsupportedMediaType(_, _) => "TypeError",
|
||||
ModuleGraphError::Missing(_) => "NotFound",
|
||||
}
|
||||
}
|
||||
|
||||
fn get_resolution_error_class(err: &ResolutionError) -> &'static str {
|
||||
match err {
|
||||
ResolutionError::ResolverError(err, _, _) => {
|
||||
get_error_class_name(err.as_ref())
|
||||
}
|
||||
_ => "TypeError",
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_error_class_name(e: &AnyError) -> &'static str {
|
||||
deno_runtime::errors::get_error_class_name(e)
|
||||
.or_else(|| {
|
||||
|
@ -28,6 +60,15 @@ pub(crate) fn get_error_class_name(e: &AnyError) -> &'static str {
|
|||
.map(get_import_map_error_class)
|
||||
})
|
||||
.or_else(|| e.downcast_ref::<Diagnostic>().map(get_diagnostic_class))
|
||||
.or_else(|| e.downcast_ref::<GraphError>().map(get_graph_error_class))
|
||||
.or_else(|| {
|
||||
e.downcast_ref::<ModuleGraphError>()
|
||||
.map(get_module_graph_error_class)
|
||||
})
|
||||
.or_else(|| {
|
||||
e.downcast_ref::<ResolutionError>()
|
||||
.map(get_resolution_error_class)
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"Error '{}' contains boxed error of unknown type:{}",
|
||||
|
|
|
@ -371,7 +371,9 @@ impl FileFetcher {
|
|||
})?;
|
||||
let mut headers = HashMap::new();
|
||||
headers.insert("content-type".to_string(), content_type);
|
||||
self.http_cache.set(specifier, headers, source.as_bytes())?;
|
||||
self
|
||||
.http_cache
|
||||
.set(specifier, headers.clone(), source.as_bytes())?;
|
||||
|
||||
Ok(File {
|
||||
local,
|
||||
|
@ -379,7 +381,7 @@ impl FileFetcher {
|
|||
media_type,
|
||||
source: Arc::new(source),
|
||||
specifier: specifier.clone(),
|
||||
maybe_headers: None,
|
||||
maybe_headers: Some(headers),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -433,7 +435,9 @@ impl FileFetcher {
|
|||
})?;
|
||||
let mut headers = HashMap::new();
|
||||
headers.insert("content-type".to_string(), content_type);
|
||||
self.http_cache.set(specifier, headers, source.as_bytes())?;
|
||||
self
|
||||
.http_cache
|
||||
.set(specifier, headers.clone(), source.as_bytes())?;
|
||||
|
||||
Ok(File {
|
||||
local,
|
||||
|
@ -441,7 +445,7 @@ impl FileFetcher {
|
|||
media_type,
|
||||
source: Arc::new(source),
|
||||
specifier: specifier.clone(),
|
||||
maybe_headers: None,
|
||||
maybe_headers: Some(headers),
|
||||
})
|
||||
}
|
||||
/// Asynchronously fetch remote source file specified by the URL following
|
||||
|
@ -573,6 +577,14 @@ impl FileFetcher {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_local_path(&self, specifier: &ModuleSpecifier) -> Option<PathBuf> {
|
||||
if specifier.scheme() == "file" {
|
||||
specifier.to_file_path().ok()
|
||||
} else {
|
||||
self.http_cache.get_cache_filename(specifier)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the location of the current HTTP cache associated with the fetcher.
|
||||
pub fn get_http_cache_location(&self) -> PathBuf {
|
||||
self.http_cache.location.clone()
|
||||
|
|
513
cli/info.rs
513
cli/info.rs
|
@ -1,513 +0,0 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::colors;
|
||||
|
||||
use deno_ast::MediaType;
|
||||
use deno_core::resolve_url;
|
||||
use deno_core::serde::Serialize;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
use std::iter::Iterator;
|
||||
use std::path::PathBuf;
|
||||
|
||||
const SIBLING_CONNECTOR: char = '├';
|
||||
const LAST_SIBLING_CONNECTOR: char = '└';
|
||||
const CHILD_DEPS_CONNECTOR: char = '┬';
|
||||
const CHILD_NO_DEPS_CONNECTOR: char = '─';
|
||||
const VERTICAL_CONNECTOR: char = '│';
|
||||
const EMPTY_CONNECTOR: char = ' ';
|
||||
|
||||
#[derive(Debug, Serialize, Ord, PartialOrd, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModuleGraphInfoDep {
|
||||
pub specifier: String,
|
||||
#[serde(skip_serializing_if = "is_false")]
|
||||
pub is_dynamic: bool,
|
||||
#[serde(rename = "code", skip_serializing_if = "Option::is_none")]
|
||||
pub maybe_code: Option<ModuleSpecifier>,
|
||||
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
|
||||
pub maybe_type: Option<ModuleSpecifier>,
|
||||
}
|
||||
|
||||
fn is_false(b: &bool) -> bool {
|
||||
!b
|
||||
}
|
||||
|
||||
impl ModuleGraphInfoDep {
|
||||
fn write_info<S: AsRef<str> + fmt::Display + Clone>(
|
||||
&self,
|
||||
f: &mut fmt::Formatter,
|
||||
prefix: S,
|
||||
last: bool,
|
||||
modules: &[ModuleGraphInfoMod],
|
||||
seen: &mut HashSet<ModuleSpecifier>,
|
||||
) -> fmt::Result {
|
||||
let maybe_code = self
|
||||
.maybe_code
|
||||
.as_ref()
|
||||
.and_then(|s| modules.iter().find(|m| &m.specifier == s));
|
||||
let maybe_type = self
|
||||
.maybe_type
|
||||
.as_ref()
|
||||
.and_then(|s| modules.iter().find(|m| &m.specifier == s));
|
||||
match (maybe_code, maybe_type) {
|
||||
(Some(code), Some(types)) => {
|
||||
code.write_info(f, prefix.clone(), false, false, modules, seen)?;
|
||||
types.write_info(f, prefix, last, true, modules, seen)
|
||||
}
|
||||
(Some(code), None) => {
|
||||
code.write_info(f, prefix, last, false, modules, seen)
|
||||
}
|
||||
(None, Some(types)) => {
|
||||
types.write_info(f, prefix, last, true, modules, seen)
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Ord, PartialOrd, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModuleGraphInfoMod {
|
||||
pub specifier: ModuleSpecifier,
|
||||
pub dependencies: Vec<ModuleGraphInfoDep>,
|
||||
#[serde(rename = "typeDependency", skip_serializing_if = "Option::is_none")]
|
||||
pub maybe_type_dependency: Option<ModuleGraphInfoDep>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub size: Option<usize>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub media_type: Option<MediaType>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub local: Option<PathBuf>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub checksum: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub emit: Option<PathBuf>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub map: Option<PathBuf>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for ModuleGraphInfoMod {
|
||||
fn default() -> Self {
|
||||
ModuleGraphInfoMod {
|
||||
specifier: resolve_url("https://deno.land/x/mod.ts").unwrap(),
|
||||
dependencies: Vec::new(),
|
||||
maybe_type_dependency: None,
|
||||
size: None,
|
||||
media_type: None,
|
||||
local: None,
|
||||
checksum: None,
|
||||
emit: None,
|
||||
map: None,
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleGraphInfoMod {
|
||||
fn write_info<S: AsRef<str> + fmt::Display>(
|
||||
&self,
|
||||
f: &mut fmt::Formatter,
|
||||
prefix: S,
|
||||
last: bool,
|
||||
type_dep: bool,
|
||||
modules: &[ModuleGraphInfoMod],
|
||||
seen: &mut HashSet<ModuleSpecifier>,
|
||||
) -> fmt::Result {
|
||||
let was_seen = seen.contains(&self.specifier);
|
||||
let sibling_connector = if last {
|
||||
LAST_SIBLING_CONNECTOR
|
||||
} else {
|
||||
SIBLING_CONNECTOR
|
||||
};
|
||||
let child_connector = if (self.dependencies.is_empty()
|
||||
&& self.maybe_type_dependency.is_none())
|
||||
|| was_seen
|
||||
{
|
||||
CHILD_NO_DEPS_CONNECTOR
|
||||
} else {
|
||||
CHILD_DEPS_CONNECTOR
|
||||
};
|
||||
let (size, specifier) = if self.error.is_some() {
|
||||
(
|
||||
colors::red_bold(" (error)").to_string(),
|
||||
colors::red(&self.specifier).to_string(),
|
||||
)
|
||||
} else if was_seen {
|
||||
let name = if type_dep {
|
||||
colors::italic_gray(&self.specifier).to_string()
|
||||
} else {
|
||||
colors::gray(&self.specifier).to_string()
|
||||
};
|
||||
(colors::gray(" *").to_string(), name)
|
||||
} else {
|
||||
let name = if type_dep {
|
||||
colors::italic(&self.specifier).to_string()
|
||||
} else {
|
||||
self.specifier.to_string()
|
||||
};
|
||||
(
|
||||
colors::gray(format!(
|
||||
" ({})",
|
||||
human_size(self.size.unwrap_or(0) as f64)
|
||||
))
|
||||
.to_string(),
|
||||
name,
|
||||
)
|
||||
};
|
||||
|
||||
seen.insert(self.specifier.clone());
|
||||
|
||||
writeln!(
|
||||
f,
|
||||
"{} {}{}",
|
||||
colors::gray(format!(
|
||||
"{}{}─{}",
|
||||
prefix, sibling_connector, child_connector
|
||||
)),
|
||||
specifier,
|
||||
size
|
||||
)?;
|
||||
|
||||
if !was_seen {
|
||||
let mut prefix = prefix.to_string();
|
||||
if last {
|
||||
prefix.push(EMPTY_CONNECTOR);
|
||||
} else {
|
||||
prefix.push(VERTICAL_CONNECTOR);
|
||||
}
|
||||
prefix.push(EMPTY_CONNECTOR);
|
||||
let dep_count = self.dependencies.len();
|
||||
for (idx, dep) in self.dependencies.iter().enumerate() {
|
||||
dep.write_info(
|
||||
f,
|
||||
&prefix,
|
||||
idx == dep_count - 1 && self.maybe_type_dependency.is_none(),
|
||||
modules,
|
||||
seen,
|
||||
)?;
|
||||
}
|
||||
if let Some(dep) = &self.maybe_type_dependency {
|
||||
dep.write_info(f, &prefix, true, modules, seen)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModuleGraphInfo {
|
||||
pub root: ModuleSpecifier,
|
||||
pub modules: Vec<ModuleGraphInfoMod>,
|
||||
pub size: usize,
|
||||
}
|
||||
|
||||
impl fmt::Display for ModuleGraphInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let root = self
|
||||
.modules
|
||||
.iter()
|
||||
.find(|m| m.specifier == self.root)
|
||||
.unwrap();
|
||||
if let Some(err) = &root.error {
|
||||
writeln!(f, "{} {}", colors::red("error:"), err)
|
||||
} else {
|
||||
if let Some(local) = &root.local {
|
||||
writeln!(f, "{} {}", colors::bold("local:"), local.to_string_lossy())?;
|
||||
}
|
||||
if let Some(media_type) = &root.media_type {
|
||||
writeln!(f, "{} {}", colors::bold("type:"), media_type)?;
|
||||
}
|
||||
if let Some(emit) = &root.emit {
|
||||
writeln!(f, "{} {}", colors::bold("emit:"), emit.to_string_lossy())?;
|
||||
}
|
||||
if let Some(map) = &root.map {
|
||||
writeln!(f, "{} {}", colors::bold("map:"), map.to_string_lossy())?;
|
||||
}
|
||||
let dep_count = self.modules.len() - 1;
|
||||
writeln!(
|
||||
f,
|
||||
"{} {} unique {}",
|
||||
colors::bold("dependencies:"),
|
||||
dep_count,
|
||||
colors::gray(format!("(total {})", human_size(self.size as f64)))
|
||||
)?;
|
||||
writeln!(
|
||||
f,
|
||||
"\n{} {}",
|
||||
self.root,
|
||||
colors::gray(format!(
|
||||
"({})",
|
||||
human_size(root.size.unwrap_or(0) as f64)
|
||||
))
|
||||
)?;
|
||||
let mut seen = HashSet::new();
|
||||
let dep_len = root.dependencies.len();
|
||||
for (idx, dep) in root.dependencies.iter().enumerate() {
|
||||
dep.write_info(
|
||||
f,
|
||||
"",
|
||||
idx == dep_len - 1 && root.maybe_type_dependency.is_none(),
|
||||
&self.modules,
|
||||
&mut seen,
|
||||
)?;
|
||||
}
|
||||
if let Some(dep) = &root.maybe_type_dependency {
|
||||
dep.write_info(f, "", true, &self.modules, &mut seen)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An entry in the `ModuleInfoMap` the provides the size of the module and
|
||||
/// a vector of its dependencies, which should also be available as entries
|
||||
/// in the map.
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModuleInfoMapItem {
|
||||
pub deps: Vec<ModuleSpecifier>,
|
||||
pub size: usize,
|
||||
}
|
||||
|
||||
/// A function that converts a float to a string the represents a human
|
||||
/// readable version of that number.
|
||||
pub fn human_size(size: f64) -> String {
|
||||
let negative = if size.is_sign_positive() { "" } else { "-" };
|
||||
let size = size.abs();
|
||||
let units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||
if size < 1_f64 {
|
||||
return format!("{}{}{}", negative, size, "B");
|
||||
}
|
||||
let delimiter = 1024_f64;
|
||||
let exponent = std::cmp::min(
|
||||
(size.ln() / delimiter.ln()).floor() as i32,
|
||||
(units.len() - 1) as i32,
|
||||
);
|
||||
let pretty_bytes = format!("{:.2}", size / delimiter.powi(exponent))
|
||||
.parse::<f64>()
|
||||
.unwrap()
|
||||
* 1_f64;
|
||||
let unit = units[exponent as usize];
|
||||
format!("{}{}{}", negative, pretty_bytes, unit)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use deno_core::resolve_url;
|
||||
use deno_core::serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn human_size_test() {
|
||||
assert_eq!(human_size(16_f64), "16B");
|
||||
assert_eq!(human_size((16 * 1024) as f64), "16KB");
|
||||
assert_eq!(human_size((16 * 1024 * 1024) as f64), "16MB");
|
||||
assert_eq!(human_size(16_f64 * 1024_f64.powf(3.0)), "16GB");
|
||||
assert_eq!(human_size(16_f64 * 1024_f64.powf(4.0)), "16TB");
|
||||
assert_eq!(human_size(16_f64 * 1024_f64.powf(5.0)), "16PB");
|
||||
assert_eq!(human_size(16_f64 * 1024_f64.powf(6.0)), "16EB");
|
||||
assert_eq!(human_size(16_f64 * 1024_f64.powf(7.0)), "16ZB");
|
||||
assert_eq!(human_size(16_f64 * 1024_f64.powf(8.0)), "16YB");
|
||||
}
|
||||
|
||||
fn get_fixture() -> ModuleGraphInfo {
|
||||
let specifier_a = resolve_url("https://deno.land/x/a.ts").unwrap();
|
||||
let specifier_b = resolve_url("https://deno.land/x/b.ts").unwrap();
|
||||
let specifier_c_js = resolve_url("https://deno.land/x/c.js").unwrap();
|
||||
let specifier_c_dts = resolve_url("https://deno.land/x/c.d.ts").unwrap();
|
||||
let specifier_d_js = resolve_url("https://deno.land/x/d.js").unwrap();
|
||||
let specifier_d_dts = resolve_url("https://deno.land/x/d.d.ts").unwrap();
|
||||
let modules = vec![
|
||||
ModuleGraphInfoMod {
|
||||
specifier: specifier_a.clone(),
|
||||
dependencies: vec![ModuleGraphInfoDep {
|
||||
specifier: "./b.ts".to_string(),
|
||||
is_dynamic: false,
|
||||
maybe_code: Some(specifier_b.clone()),
|
||||
maybe_type: None,
|
||||
}],
|
||||
size: Some(123),
|
||||
media_type: Some(MediaType::TypeScript),
|
||||
local: Some(PathBuf::from("/cache/deps/https/deno.land/x/a.ts")),
|
||||
checksum: Some("abcdef".to_string()),
|
||||
emit: Some(PathBuf::from("/cache/emit/https/deno.land/x/a.js")),
|
||||
..Default::default()
|
||||
},
|
||||
ModuleGraphInfoMod {
|
||||
specifier: specifier_b,
|
||||
dependencies: vec![ModuleGraphInfoDep {
|
||||
specifier: "./c.js".to_string(),
|
||||
is_dynamic: false,
|
||||
maybe_code: Some(specifier_c_js.clone()),
|
||||
maybe_type: Some(specifier_c_dts.clone()),
|
||||
}],
|
||||
size: Some(456),
|
||||
media_type: Some(MediaType::TypeScript),
|
||||
local: Some(PathBuf::from("/cache/deps/https/deno.land/x/b.ts")),
|
||||
checksum: Some("def123".to_string()),
|
||||
emit: Some(PathBuf::from("/cache/emit/https/deno.land/x/b.js")),
|
||||
..Default::default()
|
||||
},
|
||||
ModuleGraphInfoMod {
|
||||
specifier: specifier_c_js,
|
||||
dependencies: vec![ModuleGraphInfoDep {
|
||||
specifier: "./d.js".to_string(),
|
||||
is_dynamic: false,
|
||||
maybe_code: Some(specifier_d_js.clone()),
|
||||
maybe_type: None,
|
||||
}],
|
||||
size: Some(789),
|
||||
media_type: Some(MediaType::JavaScript),
|
||||
local: Some(PathBuf::from("/cache/deps/https/deno.land/x/c.js")),
|
||||
checksum: Some("9876abcef".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
ModuleGraphInfoMod {
|
||||
specifier: specifier_c_dts,
|
||||
size: Some(999),
|
||||
media_type: Some(MediaType::Dts),
|
||||
local: Some(PathBuf::from("/cache/deps/https/deno.land/x/c.d.ts")),
|
||||
checksum: Some("a2b3c4d5".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
ModuleGraphInfoMod {
|
||||
specifier: specifier_d_js,
|
||||
size: Some(987),
|
||||
maybe_type_dependency: Some(ModuleGraphInfoDep {
|
||||
specifier: "/x/d.d.ts".to_string(),
|
||||
is_dynamic: false,
|
||||
maybe_code: None,
|
||||
maybe_type: Some(specifier_d_dts.clone()),
|
||||
}),
|
||||
media_type: Some(MediaType::JavaScript),
|
||||
local: Some(PathBuf::from("/cache/deps/https/deno.land/x/d.js")),
|
||||
checksum: Some("5k6j7h8g".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
ModuleGraphInfoMod {
|
||||
specifier: specifier_d_dts,
|
||||
size: Some(67),
|
||||
media_type: Some(MediaType::Dts),
|
||||
local: Some(PathBuf::from("/cache/deps/https/deno.land/x/d.d.ts")),
|
||||
checksum: Some("0h0h0h0h0h".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
];
|
||||
ModuleGraphInfo {
|
||||
root: specifier_a,
|
||||
modules,
|
||||
size: 99999,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn text_module_graph_info_display() {
|
||||
let fixture = get_fixture();
|
||||
let text = fixture.to_string();
|
||||
let actual = test_util::strip_ansi_codes(&text);
|
||||
let expected = r#"local: /cache/deps/https/deno.land/x/a.ts
|
||||
type: TypeScript
|
||||
emit: /cache/emit/https/deno.land/x/a.js
|
||||
dependencies: 5 unique (total 97.66KB)
|
||||
|
||||
https://deno.land/x/a.ts (123B)
|
||||
└─┬ https://deno.land/x/b.ts (456B)
|
||||
├─┬ https://deno.land/x/c.js (789B)
|
||||
│ └─┬ https://deno.land/x/d.js (987B)
|
||||
│ └── https://deno.land/x/d.d.ts (67B)
|
||||
└── https://deno.land/x/c.d.ts (999B)
|
||||
"#;
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_module_graph_info_json() {
|
||||
let fixture = get_fixture();
|
||||
let actual = json!(fixture);
|
||||
assert_eq!(
|
||||
actual,
|
||||
json!({
|
||||
"root": "https://deno.land/x/a.ts",
|
||||
"modules": [
|
||||
{
|
||||
"specifier": "https://deno.land/x/a.ts",
|
||||
"dependencies": [
|
||||
{
|
||||
"specifier": "./b.ts",
|
||||
"code": "https://deno.land/x/b.ts"
|
||||
}
|
||||
],
|
||||
"size": 123,
|
||||
"mediaType": "TypeScript",
|
||||
"local": "/cache/deps/https/deno.land/x/a.ts",
|
||||
"checksum": "abcdef",
|
||||
"emit": "/cache/emit/https/deno.land/x/a.js"
|
||||
},
|
||||
{
|
||||
"specifier": "https://deno.land/x/b.ts",
|
||||
"dependencies": [
|
||||
{
|
||||
"specifier": "./c.js",
|
||||
"code": "https://deno.land/x/c.js",
|
||||
"type": "https://deno.land/x/c.d.ts"
|
||||
}
|
||||
],
|
||||
"size": 456,
|
||||
"mediaType": "TypeScript",
|
||||
"local": "/cache/deps/https/deno.land/x/b.ts",
|
||||
"checksum": "def123",
|
||||
"emit": "/cache/emit/https/deno.land/x/b.js"
|
||||
},
|
||||
{
|
||||
"specifier": "https://deno.land/x/c.js",
|
||||
"dependencies": [
|
||||
{
|
||||
"specifier": "./d.js",
|
||||
"code": "https://deno.land/x/d.js"
|
||||
}
|
||||
],
|
||||
"size": 789,
|
||||
"mediaType": "JavaScript",
|
||||
"local": "/cache/deps/https/deno.land/x/c.js",
|
||||
"checksum": "9876abcef"
|
||||
},
|
||||
{
|
||||
"specifier": "https://deno.land/x/c.d.ts",
|
||||
"dependencies": [],
|
||||
"size": 999,
|
||||
"mediaType": "Dts",
|
||||
"local": "/cache/deps/https/deno.land/x/c.d.ts",
|
||||
"checksum": "a2b3c4d5"
|
||||
},
|
||||
{
|
||||
"specifier": "https://deno.land/x/d.js",
|
||||
"dependencies": [],
|
||||
"typeDependency": {
|
||||
"specifier": "/x/d.d.ts",
|
||||
"type": "https://deno.land/x/d.d.ts"
|
||||
},
|
||||
"size": 987,
|
||||
"mediaType": "JavaScript",
|
||||
"local": "/cache/deps/https/deno.land/x/d.js",
|
||||
"checksum": "5k6j7h8g"
|
||||
},
|
||||
{
|
||||
"specifier": "https://deno.land/x/d.d.ts",
|
||||
"dependencies": [],
|
||||
"size": 67,
|
||||
"mediaType": "Dts",
|
||||
"local": "/cache/deps/https/deno.land/x/d.d.ts",
|
||||
"checksum": "0h0h0h0h0h"
|
||||
}
|
||||
],
|
||||
"size": 99999
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,11 +1,16 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use log::debug;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::BTreeMap;
|
||||
use std::io::Result;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Lockfile {
|
||||
|
@ -81,6 +86,43 @@ impl Lockfile {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Locker(Option<Arc<Mutex<Lockfile>>>);
|
||||
|
||||
impl deno_graph::source::Locker for Locker {
|
||||
fn check_or_insert(
|
||||
&mut self,
|
||||
specifier: &ModuleSpecifier,
|
||||
source: &str,
|
||||
) -> bool {
|
||||
if let Some(lock_file) = &self.0 {
|
||||
let mut lock_file = lock_file.lock();
|
||||
lock_file.check_or_insert(specifier.as_str(), source)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn get_checksum(&self, content: &str) -> String {
|
||||
crate::checksum::gen(&[content.as_bytes()])
|
||||
}
|
||||
|
||||
fn get_filename(&self) -> Option<String> {
|
||||
let lock_file = self.0.as_ref()?.lock();
|
||||
lock_file.filename.to_str().map(|s| s.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_maybe_locker(
|
||||
lockfile: Option<Arc<Mutex<Lockfile>>>,
|
||||
) -> Option<Rc<RefCell<Box<dyn deno_graph::source::Locker>>>> {
|
||||
lockfile.as_ref().map(|lf| {
|
||||
Rc::new(RefCell::new(
|
||||
Box::new(Locker(Some(lf.clone()))) as Box<dyn deno_graph::source::Locker>
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -6,12 +6,12 @@ use super::tsc;
|
|||
use crate::ast;
|
||||
use crate::ast::Location;
|
||||
use crate::lsp::documents::DocumentData;
|
||||
use crate::module_graph::parse_deno_types;
|
||||
use crate::module_graph::parse_ts_reference;
|
||||
use crate::module_graph::TypeScriptReference;
|
||||
use crate::tools::lint::create_linter;
|
||||
|
||||
use deno_ast::swc::ast as swc_ast;
|
||||
use deno_ast::swc::common::comments::Comment;
|
||||
use deno_ast::swc::common::BytePos;
|
||||
use deno_ast::swc::common::Span;
|
||||
use deno_ast::swc::common::DUMMY_SP;
|
||||
use deno_ast::swc::visit::Node;
|
||||
use deno_ast::swc::visit::Visit;
|
||||
|
@ -68,10 +68,71 @@ lazy_static::lazy_static! {
|
|||
.collect();
|
||||
|
||||
static ref IMPORT_SPECIFIER_RE: Regex = Regex::new(r#"\sfrom\s+["']([^"']*)["']"#).unwrap();
|
||||
|
||||
static ref DENO_TYPES_RE: Regex =
|
||||
Regex::new(r#"(?i)^\s*@deno-types\s*=\s*(?:["']([^"']+)["']|(\S+))"#)
|
||||
.unwrap();
|
||||
static ref TRIPLE_SLASH_REFERENCE_RE: Regex =
|
||||
Regex::new(r"(?i)^/\s*<reference\s.*?/>").unwrap();
|
||||
static ref PATH_REFERENCE_RE: Regex =
|
||||
Regex::new(r#"(?i)\spath\s*=\s*["']([^"']*)["']"#).unwrap();
|
||||
static ref TYPES_REFERENCE_RE: Regex =
|
||||
Regex::new(r#"(?i)\stypes\s*=\s*["']([^"']*)["']"#).unwrap();
|
||||
|
||||
}
|
||||
|
||||
const SUPPORTED_EXTENSIONS: &[&str] = &[".ts", ".tsx", ".js", ".jsx", ".mjs"];
|
||||
|
||||
// TODO(@kitsonk) remove after deno_graph migration
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
enum TypeScriptReference {
|
||||
Path(String),
|
||||
Types(String),
|
||||
}
|
||||
|
||||
fn match_to_span(comment: &Comment, m: ®ex::Match) -> Span {
|
||||
Span {
|
||||
lo: comment.span.lo + BytePos((m.start() + 1) as u32),
|
||||
hi: comment.span.lo + BytePos((m.end() + 1) as u32),
|
||||
ctxt: comment.span.ctxt,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(@kitsonk) remove after deno_graph migration
|
||||
fn parse_deno_types(comment: &Comment) -> Option<(String, Span)> {
|
||||
let captures = DENO_TYPES_RE.captures(&comment.text)?;
|
||||
if let Some(m) = captures.get(1) {
|
||||
Some((m.as_str().to_string(), match_to_span(comment, &m)))
|
||||
} else if let Some(m) = captures.get(2) {
|
||||
Some((m.as_str().to_string(), match_to_span(comment, &m)))
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(@kitsonk) remove after deno_graph migration
|
||||
fn parse_ts_reference(
|
||||
comment: &Comment,
|
||||
) -> Option<(TypeScriptReference, Span)> {
|
||||
if !TRIPLE_SLASH_REFERENCE_RE.is_match(&comment.text) {
|
||||
None
|
||||
} else if let Some(captures) = PATH_REFERENCE_RE.captures(&comment.text) {
|
||||
let m = captures.get(1).unwrap();
|
||||
Some((
|
||||
TypeScriptReference::Path(m.as_str().to_string()),
|
||||
match_to_span(comment, &m),
|
||||
))
|
||||
} else {
|
||||
TYPES_REFERENCE_RE.captures(&comment.text).map(|captures| {
|
||||
let m = captures.get(1).unwrap();
|
||||
(
|
||||
TypeScriptReference::Types(m.as_str().to_string()),
|
||||
match_to_span(comment, &m),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Category of self-generated diagnostic messages (those not coming from)
|
||||
/// TypeScript.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
|
|
86
cli/lsp/cache.rs
Normal file
86
cli/lsp/cache.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::cache::CacherLoader;
|
||||
use crate::cache::FetchCacher;
|
||||
use crate::flags::Flags;
|
||||
use crate::proc_state::ProcState;
|
||||
use crate::resolver::ImportMapResolver;
|
||||
use crate::tokio_util::create_basic_runtime;
|
||||
|
||||
use deno_core::error::anyhow;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_runtime::permissions::Permissions;
|
||||
use import_map::ImportMap;
|
||||
use std::path::PathBuf;
|
||||
use std::thread;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
type Request = (Vec<ModuleSpecifier>, oneshot::Sender<Result<(), AnyError>>);
|
||||
|
||||
/// A "server" that handles requests from the language server to cache modules
|
||||
/// in its own thread.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CacheServer(mpsc::UnboundedSender<Request>);
|
||||
|
||||
impl CacheServer {
|
||||
pub async fn new(
|
||||
maybe_cache_path: Option<PathBuf>,
|
||||
maybe_import_map: Option<ImportMap>,
|
||||
) -> Self {
|
||||
let (tx, mut rx) = mpsc::unbounded_channel::<Request>();
|
||||
let _join_handle = thread::spawn(move || {
|
||||
let runtime = create_basic_runtime();
|
||||
runtime.block_on(async {
|
||||
let ps = ProcState::build(Flags {
|
||||
cache_path: maybe_cache_path,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
let maybe_resolver =
|
||||
maybe_import_map.as_ref().map(ImportMapResolver::new);
|
||||
let mut cache = FetchCacher::new(
|
||||
ps.dir.gen_cache.clone(),
|
||||
ps.file_fetcher.clone(),
|
||||
Permissions::allow_all(),
|
||||
Permissions::allow_all(),
|
||||
);
|
||||
|
||||
while let Some((roots, tx)) = rx.recv().await {
|
||||
let graph = deno_graph::create_graph(
|
||||
roots,
|
||||
false,
|
||||
None,
|
||||
cache.as_mut_loader(),
|
||||
maybe_resolver.as_ref().map(|r| r.as_resolver()),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
if tx.send(graph.valid().map_err(|err| err.into())).is_err() {
|
||||
log::warn!("cannot send to client");
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
Self(tx)
|
||||
}
|
||||
|
||||
/// Attempt to cache the supplied module specifiers and their dependencies in
|
||||
/// the current DENO_DIR, returning any errors, so they can be returned to the
|
||||
/// client.
|
||||
pub async fn cache(
|
||||
&self,
|
||||
roots: Vec<ModuleSpecifier>,
|
||||
) -> Result<(), AnyError> {
|
||||
let (tx, rx) = oneshot::channel::<Result<(), AnyError>>();
|
||||
if self.0.send((roots, tx)).is_err() {
|
||||
return Err(anyhow!("failed to send request to cache thread"));
|
||||
}
|
||||
rx.await?
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ use super::analysis::ts_changes_to_edit;
|
|||
use super::analysis::CodeActionCollection;
|
||||
use super::analysis::CodeActionData;
|
||||
use super::analysis::ResolvedDependency;
|
||||
use super::cache::CacheServer;
|
||||
use super::capabilities;
|
||||
use super::code_lens;
|
||||
use super::completions;
|
||||
|
@ -44,7 +45,6 @@ use super::parent_process_checker;
|
|||
use super::performance::Performance;
|
||||
use super::refactor;
|
||||
use super::registries;
|
||||
use super::sources;
|
||||
use super::sources::Sources;
|
||||
use super::text;
|
||||
use super::text::LineIndex;
|
||||
|
@ -99,6 +99,8 @@ pub(crate) struct Inner {
|
|||
/// An optional path to the DENO_DIR which has been specified in the client
|
||||
/// options.
|
||||
maybe_cache_path: Option<PathBuf>,
|
||||
/// A lazily created "server" for handling cache requests.
|
||||
maybe_cache_server: Option<CacheServer>,
|
||||
/// An optional configuration file which has been specified in the client
|
||||
/// options.
|
||||
maybe_config_file: Option<ConfigFile>,
|
||||
|
@ -149,6 +151,7 @@ impl Inner {
|
|||
diagnostics_server,
|
||||
documents: Default::default(),
|
||||
maybe_cache_path: None,
|
||||
maybe_cache_server: None,
|
||||
maybe_config_file: None,
|
||||
maybe_config_uri: None,
|
||||
maybe_import_map: None,
|
||||
|
@ -424,6 +427,7 @@ impl Inner {
|
|||
pub fn update_cache(&mut self) -> Result<(), AnyError> {
|
||||
let mark = self.performance.mark("update_cache", None::<()>);
|
||||
self.performance.measure(mark);
|
||||
self.maybe_cache_server = None;
|
||||
let (maybe_cache, maybe_root_uri) = {
|
||||
let config = &self.config;
|
||||
(
|
||||
|
@ -479,6 +483,7 @@ impl Inner {
|
|||
|
||||
pub async fn update_import_map(&mut self) -> Result<(), AnyError> {
|
||||
let mark = self.performance.mark("update_import_map", None::<()>);
|
||||
self.maybe_cache_server = None;
|
||||
let (maybe_import_map, maybe_root_uri) = {
|
||||
let config = &self.config;
|
||||
(
|
||||
|
@ -2630,34 +2635,30 @@ impl Inner {
|
|||
}
|
||||
|
||||
let mark = self.performance.mark("cache", Some(¶ms));
|
||||
if !params.uris.is_empty() {
|
||||
for identifier in ¶ms.uris {
|
||||
let specifier = self.url_map.normalize_url(&identifier.uri);
|
||||
sources::cache(
|
||||
&specifier,
|
||||
&self.maybe_import_map,
|
||||
&self.maybe_config_file,
|
||||
&self.maybe_cache_path,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("{}", err);
|
||||
LspError::internal_error()
|
||||
})?;
|
||||
}
|
||||
let roots = if !params.uris.is_empty() {
|
||||
params
|
||||
.uris
|
||||
.iter()
|
||||
.map(|t| self.url_map.normalize_url(&t.uri))
|
||||
.collect()
|
||||
} else {
|
||||
sources::cache(
|
||||
&referrer,
|
||||
&self.maybe_import_map,
|
||||
&self.maybe_config_file,
|
||||
&self.maybe_cache_path,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("{}", err);
|
||||
LspError::internal_error()
|
||||
})?;
|
||||
vec![referrer.clone()]
|
||||
};
|
||||
|
||||
if self.maybe_cache_server.is_none() {
|
||||
self.maybe_cache_server = Some(
|
||||
CacheServer::new(
|
||||
self.maybe_cache_path.clone(),
|
||||
self.maybe_import_map.clone(),
|
||||
)
|
||||
.await,
|
||||
);
|
||||
}
|
||||
let cache_server = self.maybe_cache_server.as_ref().unwrap();
|
||||
if let Err(err) = cache_server.cache(roots).await {
|
||||
self.client.show_message(MessageType::Warning, err).await;
|
||||
}
|
||||
|
||||
// now that we have dependencies loaded, we need to re-analyze them and
|
||||
// invalidate some diagnostics
|
||||
if self.documents.contains_key(&referrer) {
|
||||
|
|
|
@ -5,6 +5,7 @@ use lspower::LspService;
|
|||
use lspower::Server;
|
||||
|
||||
mod analysis;
|
||||
mod cache;
|
||||
mod capabilities;
|
||||
mod code_lens;
|
||||
mod completions;
|
||||
|
|
|
@ -6,18 +6,12 @@ use super::text::LineIndex;
|
|||
use super::tsc;
|
||||
use super::urls::INVALID_SPECIFIER;
|
||||
|
||||
use crate::config_file::ConfigFile;
|
||||
use crate::file_fetcher::get_source_from_bytes;
|
||||
use crate::file_fetcher::map_content_type;
|
||||
use crate::file_fetcher::SUPPORTED_SCHEMES;
|
||||
use crate::flags::Flags;
|
||||
use crate::http_cache;
|
||||
use crate::http_cache::HttpCache;
|
||||
use crate::module_graph::GraphBuilder;
|
||||
use crate::proc_state::ProcState;
|
||||
use crate::specifier_handler::FetchHandler;
|
||||
use crate::text_encoding;
|
||||
use import_map::ImportMap;
|
||||
|
||||
use deno_ast::MediaType;
|
||||
use deno_core::error::anyhow;
|
||||
|
@ -25,7 +19,7 @@ use deno_core::error::AnyError;
|
|||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_runtime::permissions::Permissions;
|
||||
use import_map::ImportMap;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
@ -34,27 +28,6 @@ use std::sync::Arc;
|
|||
use std::time::SystemTime;
|
||||
use tsc::NavigationTree;
|
||||
|
||||
pub async fn cache(
|
||||
specifier: &ModuleSpecifier,
|
||||
maybe_import_map: &Option<ImportMap>,
|
||||
maybe_config_file: &Option<ConfigFile>,
|
||||
maybe_cache_path: &Option<PathBuf>,
|
||||
) -> Result<(), AnyError> {
|
||||
let ps = ProcState::build(Flags {
|
||||
cache_path: maybe_cache_path.clone(),
|
||||
..Default::default()
|
||||
})
|
||||
.await?;
|
||||
let handler = Arc::new(Mutex::new(FetchHandler::new(
|
||||
&ps,
|
||||
Permissions::allow_all(),
|
||||
Permissions::allow_all(),
|
||||
)?));
|
||||
let mut builder = GraphBuilder::new(handler, maybe_import_map.clone(), None);
|
||||
builder.analyze_config_file(maybe_config_file).await?;
|
||||
builder.add(specifier, false).await
|
||||
}
|
||||
|
||||
fn get_remote_headers(
|
||||
cache_filename: &Path,
|
||||
) -> Option<HashMap<String, String>> {
|
||||
|
|
|
@ -44,12 +44,15 @@ use log::warn;
|
|||
use lspower::lsp;
|
||||
use regex::Captures;
|
||||
use regex::Regex;
|
||||
use std::borrow::Cow;
|
||||
use std::cmp;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::{borrow::Cow, cmp};
|
||||
use std::{collections::HashMap, path::Path};
|
||||
use text_size::{TextRange, TextSize};
|
||||
use text_size::TextRange;
|
||||
use text_size::TextSize;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
|
@ -3375,7 +3378,7 @@ mod tests {
|
|||
#[test]
|
||||
fn test_modify_sources() {
|
||||
let (mut runtime, state_snapshot, location) = setup(
|
||||
true,
|
||||
false,
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
|
|
365
cli/main.rs
365
cli/main.rs
|
@ -2,6 +2,7 @@
|
|||
|
||||
mod ast;
|
||||
mod auth_tokens;
|
||||
mod cache;
|
||||
mod checksum;
|
||||
mod compat;
|
||||
mod config_file;
|
||||
|
@ -9,6 +10,7 @@ mod deno_dir;
|
|||
mod diagnostics;
|
||||
mod diff;
|
||||
mod disk_cache;
|
||||
mod emit;
|
||||
mod errors;
|
||||
mod file_fetcher;
|
||||
mod file_watcher;
|
||||
|
@ -18,16 +20,14 @@ mod fmt_errors;
|
|||
mod fs_util;
|
||||
mod http_cache;
|
||||
mod http_util;
|
||||
mod info;
|
||||
mod lockfile;
|
||||
mod logger;
|
||||
mod lsp;
|
||||
mod module_graph;
|
||||
mod module_loader;
|
||||
mod ops;
|
||||
mod proc_state;
|
||||
mod resolver;
|
||||
mod source_maps;
|
||||
mod specifier_handler;
|
||||
mod standalone;
|
||||
mod text_encoding;
|
||||
mod tokio_util;
|
||||
|
@ -59,8 +59,8 @@ use crate::flags::UpgradeFlags;
|
|||
use crate::fmt_errors::PrettyJsError;
|
||||
use crate::module_loader::CliModuleLoader;
|
||||
use crate::proc_state::ProcState;
|
||||
use crate::resolver::ImportMapResolver;
|
||||
use crate::source_maps::apply_source_map;
|
||||
use crate::specifier_handler::FetchHandler;
|
||||
use crate::tools::installer::infer_name_from_url;
|
||||
use deno_ast::MediaType;
|
||||
use deno_core::error::generic_error;
|
||||
|
@ -68,7 +68,6 @@ use deno_core::error::AnyError;
|
|||
use deno_core::futures::future::FutureExt;
|
||||
use deno_core::futures::Future;
|
||||
use deno_core::located_script_name;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::resolve_url_or_path;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::serde_json::json;
|
||||
|
@ -299,7 +298,7 @@ where
|
|||
fn print_cache_info(
|
||||
state: &ProcState,
|
||||
json: bool,
|
||||
location: Option<deno_core::url::Url>,
|
||||
location: Option<&deno_core::url::Url>,
|
||||
) -> Result<(), AnyError> {
|
||||
let deno_dir = &state.dir.root;
|
||||
let modules_cache = &state.file_fetcher.get_http_cache_location();
|
||||
|
@ -402,19 +401,9 @@ async fn compile_command(
|
|||
"An executable name was not provided. One could not be inferred from the URL. Aborting.",
|
||||
))?;
|
||||
|
||||
let module_graph = create_module_graph_and_maybe_check(
|
||||
module_specifier.clone(),
|
||||
ps.clone(),
|
||||
debug,
|
||||
)
|
||||
.await?;
|
||||
|
||||
info!(
|
||||
"{} {}",
|
||||
colors::green("Bundle"),
|
||||
module_specifier.to_string()
|
||||
);
|
||||
let bundle_str = bundle_module_graph(module_graph, ps.clone(), flags, debug)?;
|
||||
let graph =
|
||||
create_graph_and_maybe_check(module_specifier.clone(), &ps, debug).await?;
|
||||
let (bundle_str, _) = bundle_module_graph(graph.as_ref(), &ps, &flags)?;
|
||||
|
||||
info!(
|
||||
"{} {}",
|
||||
|
@ -449,36 +438,38 @@ async fn info_command(
|
|||
flags: Flags,
|
||||
info_flags: InfoFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
let location = flags.location.clone();
|
||||
let ps = ProcState::build(flags).await?;
|
||||
if let Some(specifier) = info_flags.file {
|
||||
let specifier = resolve_url_or_path(&specifier)?;
|
||||
let handler = Arc::new(Mutex::new(specifier_handler::FetchHandler::new(
|
||||
&ps,
|
||||
// info accesses dynamically imported modules just for their information
|
||||
// so we allow access to all of them.
|
||||
let mut cache = cache::FetchCacher::new(
|
||||
ps.dir.gen_cache.clone(),
|
||||
ps.file_fetcher.clone(),
|
||||
Permissions::allow_all(),
|
||||
Permissions::allow_all(),
|
||||
)?));
|
||||
let mut builder = module_graph::GraphBuilder::new(
|
||||
handler,
|
||||
ps.maybe_import_map.clone(),
|
||||
ps.lockfile.clone(),
|
||||
);
|
||||
builder.add(&specifier, false).await?;
|
||||
builder.analyze_config_file(&ps.maybe_config_file).await?;
|
||||
let graph = builder.get_graph();
|
||||
let info = graph.info()?;
|
||||
let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone());
|
||||
let maybe_resolver =
|
||||
ps.maybe_import_map.as_ref().map(ImportMapResolver::new);
|
||||
let graph = deno_graph::create_graph(
|
||||
vec![specifier],
|
||||
false,
|
||||
None,
|
||||
&mut cache,
|
||||
maybe_resolver.as_ref().map(|r| r.as_resolver()),
|
||||
maybe_locker,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
if info_flags.json {
|
||||
write_json_to_stdout(&json!(info))
|
||||
write_json_to_stdout(&json!(graph))
|
||||
} else {
|
||||
write_to_stdout_ignore_sigpipe(info.to_string().as_bytes())
|
||||
write_to_stdout_ignore_sigpipe(graph.to_string().as_bytes())
|
||||
.map_err(|err| err.into())
|
||||
}
|
||||
} else {
|
||||
// If it was just "deno info" print location of caches and exit
|
||||
print_cache_info(&ps, info_flags.json, location)
|
||||
print_cache_info(&ps, info_flags.json, ps.flags.location.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -541,23 +532,25 @@ async fn cache_command(
|
|||
cache_flags: CacheFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
let lib = if flags.unstable {
|
||||
module_graph::TypeLib::UnstableDenoWindow
|
||||
emit::TypeLib::UnstableDenoWindow
|
||||
} else {
|
||||
module_graph::TypeLib::DenoWindow
|
||||
emit::TypeLib::DenoWindow
|
||||
};
|
||||
let ps = ProcState::build(flags).await?;
|
||||
|
||||
for file in cache_flags.files {
|
||||
let specifier = resolve_url_or_path(&file)?;
|
||||
ps.prepare_module_load(
|
||||
specifier,
|
||||
vec![specifier],
|
||||
false,
|
||||
lib.clone(),
|
||||
Permissions::allow_all(),
|
||||
Permissions::allow_all(),
|
||||
false,
|
||||
ps.maybe_import_map.clone(),
|
||||
)
|
||||
.await?;
|
||||
if let Some(graph_error) = ps.maybe_graph_error.lock().take() {
|
||||
return Err(graph_error.into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -567,8 +560,10 @@ async fn eval_command(
|
|||
flags: Flags,
|
||||
eval_flags: EvalFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
// Force TypeScript compile.
|
||||
let main_module = resolve_url_or_path("./$deno$eval.ts").unwrap();
|
||||
// deno_graph works off of extensions for local files to determine the media
|
||||
// type, and so our "fake" specifier needs to have the proper extension.
|
||||
let main_module =
|
||||
resolve_url_or_path(&format!("./$deno$eval.{}", eval_flags.ext)).unwrap();
|
||||
let permissions = Permissions::from_options(&flags.clone().into());
|
||||
let ps = ProcState::build(flags.clone()).await?;
|
||||
let mut worker =
|
||||
|
@ -584,15 +579,7 @@ async fn eval_command(
|
|||
let file = File {
|
||||
local: main_module.clone().to_file_path().unwrap(),
|
||||
maybe_types: None,
|
||||
media_type: if eval_flags.ext.as_str() == "ts" {
|
||||
MediaType::TypeScript
|
||||
} else if eval_flags.ext.as_str() == "tsx" {
|
||||
MediaType::Tsx
|
||||
} else if eval_flags.ext.as_str() == "js" {
|
||||
MediaType::JavaScript
|
||||
} else {
|
||||
MediaType::Jsx
|
||||
},
|
||||
media_type: MediaType::Unknown,
|
||||
source: Arc::new(String::from_utf8(source_code)?),
|
||||
specifier: main_module.clone(),
|
||||
maybe_headers: None,
|
||||
|
@ -603,9 +590,7 @@ async fn eval_command(
|
|||
ps.file_fetcher.insert_cached(file);
|
||||
debug!("main_module {}", &main_module);
|
||||
if flags.compat {
|
||||
worker
|
||||
.execute_side_module(&compat::get_node_globals_url())
|
||||
.await?;
|
||||
worker.execute_side_module(&compat::GLOBAL_URL).await?;
|
||||
}
|
||||
worker.execute_main_module(&main_module).await?;
|
||||
worker.execute_script(
|
||||
|
@ -620,75 +605,133 @@ async fn eval_command(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn create_module_graph_and_maybe_check(
|
||||
module_specifier: ModuleSpecifier,
|
||||
ps: ProcState,
|
||||
async fn create_graph_and_maybe_check(
|
||||
root: ModuleSpecifier,
|
||||
ps: &ProcState,
|
||||
debug: bool,
|
||||
) -> Result<module_graph::Graph, AnyError> {
|
||||
let handler = Arc::new(Mutex::new(FetchHandler::new(
|
||||
&ps,
|
||||
// when bundling, dynamic imports are only access for their type safety,
|
||||
// therefore we will allow the graph to access any module.
|
||||
) -> Result<Arc<deno_graph::ModuleGraph>, AnyError> {
|
||||
let mut cache = cache::FetchCacher::new(
|
||||
ps.dir.gen_cache.clone(),
|
||||
ps.file_fetcher.clone(),
|
||||
Permissions::allow_all(),
|
||||
Permissions::allow_all(),
|
||||
)?));
|
||||
let mut builder = module_graph::GraphBuilder::new(
|
||||
handler,
|
||||
ps.maybe_import_map.clone(),
|
||||
ps.lockfile.clone(),
|
||||
);
|
||||
builder.add(&module_specifier, false).await?;
|
||||
builder.analyze_config_file(&ps.maybe_config_file).await?;
|
||||
let module_graph = builder.get_graph();
|
||||
let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone());
|
||||
let maybe_imports = ps
|
||||
.maybe_config_file
|
||||
.as_ref()
|
||||
.map(|cf| cf.to_maybe_imports())
|
||||
.flatten();
|
||||
let maybe_resolver = ps.maybe_import_map.as_ref().map(ImportMapResolver::new);
|
||||
let graph = Arc::new(
|
||||
deno_graph::create_graph(
|
||||
vec![root],
|
||||
false,
|
||||
maybe_imports,
|
||||
&mut cache,
|
||||
maybe_resolver.as_ref().map(|r| r.as_resolver()),
|
||||
maybe_locker,
|
||||
None,
|
||||
)
|
||||
.await,
|
||||
);
|
||||
|
||||
// Ensure that all non-dynamic, non-type only imports are properly loaded and
|
||||
// if not, error with the first issue encountered.
|
||||
graph.valid().map_err(emit::GraphError::from)?;
|
||||
// If there was a locker, validate the integrity of all the modules in the
|
||||
// locker.
|
||||
emit::lock(graph.as_ref());
|
||||
|
||||
if !ps.flags.no_check {
|
||||
// TODO(@kitsonk) support bundling for workers
|
||||
graph.valid_types_only().map_err(emit::GraphError::from)?;
|
||||
let lib = if ps.flags.unstable {
|
||||
module_graph::TypeLib::UnstableDenoWindow
|
||||
emit::TypeLib::UnstableDenoWindow
|
||||
} else {
|
||||
module_graph::TypeLib::DenoWindow
|
||||
emit::TypeLib::DenoWindow
|
||||
};
|
||||
let result_info =
|
||||
module_graph.clone().check(module_graph::CheckOptions {
|
||||
debug,
|
||||
emit: false,
|
||||
let (ts_config, maybe_ignored_options) = emit::get_ts_config(
|
||||
emit::ConfigType::Check {
|
||||
tsc_emit: false,
|
||||
lib,
|
||||
maybe_config_file: ps.maybe_config_file.clone(),
|
||||
reload: ps.flags.reload,
|
||||
..Default::default()
|
||||
})?;
|
||||
|
||||
debug!("{}", result_info.stats);
|
||||
if let Some(ignored_options) = result_info.maybe_ignored_options {
|
||||
},
|
||||
ps.maybe_config_file.as_ref(),
|
||||
None,
|
||||
)?;
|
||||
log::info!("{} {}", colors::green("Check"), graph.roots[0]);
|
||||
if let Some(ignored_options) = maybe_ignored_options {
|
||||
eprintln!("{}", ignored_options);
|
||||
}
|
||||
if !result_info.diagnostics.is_empty() {
|
||||
return Err(generic_error(result_info.diagnostics.to_string()));
|
||||
let maybe_config_specifier = ps
|
||||
.maybe_config_file
|
||||
.as_ref()
|
||||
.map(|cf| ModuleSpecifier::from_file_path(&cf.path).unwrap());
|
||||
let check_result = emit::check_and_maybe_emit(
|
||||
graph.clone(),
|
||||
&mut cache,
|
||||
emit::CheckOptions {
|
||||
debug,
|
||||
emit_with_diagnostics: false,
|
||||
maybe_config_specifier,
|
||||
ts_config,
|
||||
},
|
||||
)?;
|
||||
debug!("{}", check_result.stats);
|
||||
if !check_result.diagnostics.is_empty() {
|
||||
return Err(check_result.diagnostics.into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(module_graph)
|
||||
Ok(graph)
|
||||
}
|
||||
|
||||
fn bundle_module_graph(
|
||||
module_graph: module_graph::Graph,
|
||||
ps: ProcState,
|
||||
flags: Flags,
|
||||
debug: bool,
|
||||
) -> Result<String, AnyError> {
|
||||
let (bundle, stats, maybe_ignored_options) =
|
||||
module_graph.bundle(module_graph::BundleOptions {
|
||||
debug,
|
||||
maybe_config_file: ps.maybe_config_file.clone(),
|
||||
})?;
|
||||
match maybe_ignored_options {
|
||||
Some(ignored_options) if flags.no_check => {
|
||||
graph: &deno_graph::ModuleGraph,
|
||||
ps: &ProcState,
|
||||
flags: &Flags,
|
||||
) -> Result<(String, Option<String>), AnyError> {
|
||||
info!("{} {}", colors::green("Bundle"), graph.roots[0]);
|
||||
|
||||
let (ts_config, maybe_ignored_options) = emit::get_ts_config(
|
||||
emit::ConfigType::Bundle,
|
||||
ps.maybe_config_file.as_ref(),
|
||||
None,
|
||||
)?;
|
||||
if flags.no_check {
|
||||
if let Some(ignored_options) = maybe_ignored_options {
|
||||
eprintln!("{}", ignored_options);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
debug!("{}", stats);
|
||||
Ok(bundle)
|
||||
|
||||
emit::bundle(
|
||||
graph,
|
||||
emit::BundleOptions {
|
||||
bundle_type: emit::BundleType::Module,
|
||||
ts_config,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// A function that converts a float to a string the represents a human
|
||||
/// readable version of that number.
|
||||
fn human_size(size: f64) -> String {
|
||||
let negative = if size.is_sign_positive() { "" } else { "-" };
|
||||
let size = size.abs();
|
||||
let units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||
if size < 1_f64 {
|
||||
return format!("{}{}{}", negative, size, "B");
|
||||
}
|
||||
let delimiter = 1024_f64;
|
||||
let exponent = std::cmp::min(
|
||||
(size.ln() / delimiter.ln()).floor() as i32,
|
||||
(units.len() - 1) as i32,
|
||||
);
|
||||
let pretty_bytes = format!("{:.2}", size / delimiter.powi(exponent))
|
||||
.parse::<f64>()
|
||||
.unwrap()
|
||||
* 1_f64;
|
||||
let unit = units[exponent as usize];
|
||||
format!("{}{}{}", negative, pretty_bytes, unit)
|
||||
}
|
||||
|
||||
async fn bundle_command(
|
||||
|
@ -707,17 +750,18 @@ async fn bundle_command(
|
|||
debug!(">>>>> bundle START");
|
||||
let ps = ProcState::build(flags.clone()).await?;
|
||||
|
||||
let module_graph = create_module_graph_and_maybe_check(
|
||||
module_specifier,
|
||||
ps.clone(),
|
||||
debug,
|
||||
)
|
||||
.await?;
|
||||
let graph =
|
||||
create_graph_and_maybe_check(module_specifier, &ps, debug).await?;
|
||||
|
||||
let mut paths_to_watch: Vec<PathBuf> = module_graph
|
||||
.get_modules()
|
||||
let mut paths_to_watch: Vec<PathBuf> = graph
|
||||
.specifiers()
|
||||
.iter()
|
||||
.filter_map(|specifier| specifier.to_file_path().ok())
|
||||
.filter_map(|(_, r)| {
|
||||
r.as_ref()
|
||||
.ok()
|
||||
.map(|(s, _)| s.to_file_path().ok())
|
||||
.flatten()
|
||||
})
|
||||
.collect();
|
||||
|
||||
if let Some(import_map) = ps.flags.import_map_path.as_ref() {
|
||||
|
@ -725,12 +769,12 @@ async fn bundle_command(
|
|||
.push(fs_util::resolve_from_cwd(std::path::Path::new(import_map))?);
|
||||
}
|
||||
|
||||
Ok((paths_to_watch, module_graph, ps))
|
||||
Ok((paths_to_watch, graph, ps))
|
||||
}
|
||||
.map(move |result| match result {
|
||||
Ok((paths_to_watch, module_graph, ps)) => ResolutionResult::Restart {
|
||||
Ok((paths_to_watch, graph, ps)) => ResolutionResult::Restart {
|
||||
paths_to_watch,
|
||||
result: Ok((ps, module_graph)),
|
||||
result: Ok((ps, graph)),
|
||||
},
|
||||
Err(e) => ResolutionResult::Restart {
|
||||
paths_to_watch: vec![PathBuf::from(source_file2)],
|
||||
|
@ -739,28 +783,43 @@ async fn bundle_command(
|
|||
})
|
||||
};
|
||||
|
||||
let operation = |(ps, module_graph): (ProcState, module_graph::Graph)| {
|
||||
let operation = |(ps, graph): (ProcState, Arc<deno_graph::ModuleGraph>)| {
|
||||
let flags = flags.clone();
|
||||
let out_file = bundle_flags.out_file.clone();
|
||||
async move {
|
||||
info!("{} {}", colors::green("Bundle"), module_graph.info()?.root);
|
||||
|
||||
let output = bundle_module_graph(module_graph, ps, flags, debug)?;
|
||||
|
||||
let (bundle_emit, maybe_bundle_map) =
|
||||
bundle_module_graph(graph.as_ref(), &ps, &flags)?;
|
||||
debug!(">>>>> bundle END");
|
||||
|
||||
if let Some(out_file) = out_file.as_ref() {
|
||||
let output_bytes = output.as_bytes();
|
||||
let output_bytes = bundle_emit.as_bytes();
|
||||
let output_len = output_bytes.len();
|
||||
fs_util::write_file(out_file, output_bytes, 0o644)?;
|
||||
info!(
|
||||
"{} {:?} ({})",
|
||||
colors::green("Emit"),
|
||||
out_file,
|
||||
colors::gray(&info::human_size(output_len as f64))
|
||||
colors::gray(human_size(output_len as f64))
|
||||
);
|
||||
if let Some(bundle_map) = maybe_bundle_map {
|
||||
let map_bytes = bundle_map.as_bytes();
|
||||
let map_len = map_bytes.len();
|
||||
let ext = if let Some(curr_ext) = out_file.extension() {
|
||||
format!("{}.map", curr_ext.to_string_lossy())
|
||||
} else {
|
||||
"map".to_string()
|
||||
};
|
||||
let map_out_file = out_file.with_extension(ext);
|
||||
fs_util::write_file(&map_out_file, map_bytes, 0o644)?;
|
||||
info!(
|
||||
"{} {:?} ({})",
|
||||
colors::green("Emit"),
|
||||
map_out_file,
|
||||
colors::gray(human_size(map_len as f64))
|
||||
);
|
||||
}
|
||||
} else {
|
||||
println!("{}", output);
|
||||
println!("{}", bundle_emit);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -825,9 +884,7 @@ async fn run_repl(flags: Flags, repl_flags: ReplFlags) -> Result<(), AnyError> {
|
|||
let mut worker =
|
||||
create_main_worker(&ps, main_module.clone(), permissions, None);
|
||||
if flags.compat {
|
||||
worker
|
||||
.execute_side_module(&compat::get_node_globals_url())
|
||||
.await?;
|
||||
worker.execute_side_module(&compat::GLOBAL_URL).await?;
|
||||
}
|
||||
worker.run_event_loop(false).await?;
|
||||
|
||||
|
@ -858,9 +915,7 @@ async fn run_from_stdin(flags: Flags) -> Result<(), AnyError> {
|
|||
|
||||
debug!("main_module {}", main_module);
|
||||
if flags.compat {
|
||||
worker
|
||||
.execute_side_module(&compat::get_node_globals_url())
|
||||
.await?;
|
||||
worker.execute_side_module(&compat::GLOBAL_URL).await?;
|
||||
}
|
||||
worker.execute_main_module(&main_module).await?;
|
||||
worker.execute_script(
|
||||
|
@ -883,25 +938,42 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<(), AnyError> {
|
|||
async move {
|
||||
let main_module = resolve_url_or_path(&script1)?;
|
||||
let ps = ProcState::build(flags).await?;
|
||||
let handler = Arc::new(Mutex::new(FetchHandler::new(
|
||||
&ps,
|
||||
let mut cache = cache::FetchCacher::new(
|
||||
ps.dir.gen_cache.clone(),
|
||||
ps.file_fetcher.clone(),
|
||||
Permissions::allow_all(),
|
||||
Permissions::allow_all(),
|
||||
)?));
|
||||
let mut builder = module_graph::GraphBuilder::new(
|
||||
handler,
|
||||
ps.maybe_import_map.clone(),
|
||||
ps.lockfile.clone(),
|
||||
);
|
||||
builder.add(&main_module, false).await?;
|
||||
builder.analyze_config_file(&ps.maybe_config_file).await?;
|
||||
let module_graph = builder.get_graph();
|
||||
let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone());
|
||||
let maybe_imports = ps
|
||||
.maybe_config_file
|
||||
.as_ref()
|
||||
.map(|cf| cf.to_maybe_imports())
|
||||
.flatten();
|
||||
let maybe_resolver =
|
||||
ps.maybe_import_map.as_ref().map(ImportMapResolver::new);
|
||||
let graph = deno_graph::create_graph(
|
||||
vec![main_module.clone()],
|
||||
false,
|
||||
maybe_imports,
|
||||
&mut cache,
|
||||
maybe_resolver.as_ref().map(|r| r.as_resolver()),
|
||||
maybe_locker,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
graph.valid()?;
|
||||
|
||||
// Find all local files in graph
|
||||
let mut paths_to_watch: Vec<PathBuf> = module_graph
|
||||
.get_modules()
|
||||
let mut paths_to_watch: Vec<PathBuf> = graph
|
||||
.specifiers()
|
||||
.iter()
|
||||
.filter_map(|specifier| specifier.to_file_path().ok())
|
||||
.filter_map(|(_, r)| {
|
||||
r.as_ref()
|
||||
.ok()
|
||||
.map(|(s, _)| s.to_file_path().ok())
|
||||
.flatten()
|
||||
})
|
||||
.collect();
|
||||
|
||||
if let Some(import_map) = ps.flags.import_map_path.as_ref() {
|
||||
|
@ -948,10 +1020,7 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<(), AnyError> {
|
|||
main_module: &ModuleSpecifier,
|
||||
) -> Result<(), AnyError> {
|
||||
if self.compat {
|
||||
self
|
||||
.worker
|
||||
.execute_side_module(&compat::get_node_globals_url())
|
||||
.await?;
|
||||
self.worker.execute_side_module(&compat::GLOBAL_URL).await?;
|
||||
}
|
||||
self.worker.execute_main_module(main_module).await?;
|
||||
self.worker.execute_script(
|
||||
|
@ -1046,9 +1115,7 @@ async fn run_command(
|
|||
|
||||
debug!("main_module {}", main_module);
|
||||
if flags.compat {
|
||||
worker
|
||||
.execute_side_module(&compat::get_node_globals_url())
|
||||
.await?;
|
||||
worker.execute_side_module(&compat::GLOBAL_URL).await?;
|
||||
}
|
||||
worker.execute_main_module(&main_module).await?;
|
||||
worker.execute_script(
|
||||
|
|
2834
cli/module_graph.rs
2834
cli/module_graph.rs
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,8 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::module_graph::TypeLib;
|
||||
use crate::emit::TypeLib;
|
||||
use crate::proc_state::ProcState;
|
||||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::future::FutureExt;
|
||||
use deno_core::futures::Future;
|
||||
|
@ -10,16 +11,12 @@ use deno_core::ModuleLoader;
|
|||
use deno_core::ModuleSpecifier;
|
||||
use deno_core::OpState;
|
||||
use deno_runtime::permissions::Permissions;
|
||||
use import_map::ImportMap;
|
||||
use std::cell::RefCell;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::str;
|
||||
|
||||
pub struct CliModuleLoader {
|
||||
/// When flags contains a `.import_map_path` option, the content of the
|
||||
/// import map file will be resolved and set.
|
||||
pub import_map: Option<ImportMap>,
|
||||
pub(crate) struct CliModuleLoader {
|
||||
pub lib: TypeLib,
|
||||
/// The initial set of permissions used to resolve the static imports in the
|
||||
/// worker. They are decoupled from the worker (dynamic) permissions since
|
||||
|
@ -36,10 +33,7 @@ impl CliModuleLoader {
|
|||
TypeLib::DenoWindow
|
||||
};
|
||||
|
||||
let import_map = ps.maybe_import_map.clone();
|
||||
|
||||
Rc::new(CliModuleLoader {
|
||||
import_map,
|
||||
lib,
|
||||
root_permissions: Permissions::allow_all(),
|
||||
ps,
|
||||
|
@ -54,7 +48,6 @@ impl CliModuleLoader {
|
|||
};
|
||||
|
||||
Rc::new(CliModuleLoader {
|
||||
import_map: None,
|
||||
lib,
|
||||
root_permissions: permissions,
|
||||
ps,
|
||||
|
@ -67,44 +60,25 @@ impl ModuleLoader for CliModuleLoader {
|
|||
&self,
|
||||
specifier: &str,
|
||||
referrer: &str,
|
||||
is_main: bool,
|
||||
_is_main: bool,
|
||||
) -> Result<ModuleSpecifier, AnyError> {
|
||||
// FIXME(bartlomieju): hacky way to provide compatibility with repl
|
||||
let referrer = if referrer.is_empty() && self.ps.flags.repl {
|
||||
deno_core::DUMMY_SPECIFIER
|
||||
} else {
|
||||
referrer
|
||||
};
|
||||
|
||||
// TODO(ry) I think we can remove this conditional. At the time of writing
|
||||
// we don't have any tests that fail if it was removed.
|
||||
// https://github.com/WICG/import-maps/issues/157
|
||||
if !is_main {
|
||||
if let Some(import_map) = &self.import_map {
|
||||
return import_map
|
||||
.resolve(specifier, referrer)
|
||||
.map_err(AnyError::from);
|
||||
}
|
||||
}
|
||||
|
||||
let module_specifier = deno_core::resolve_import(specifier, referrer)?;
|
||||
|
||||
Ok(module_specifier)
|
||||
self.ps.resolve(specifier, referrer)
|
||||
}
|
||||
|
||||
fn load(
|
||||
&self,
|
||||
module_specifier: &ModuleSpecifier,
|
||||
maybe_referrer: Option<ModuleSpecifier>,
|
||||
_is_dynamic: bool,
|
||||
is_dynamic: bool,
|
||||
) -> Pin<Box<deno_core::ModuleSourceFuture>> {
|
||||
let module_specifier = module_specifier.clone();
|
||||
let ps = self.ps.clone();
|
||||
|
||||
// NOTE: this block is async only because of `deno_core`
|
||||
// interface requirements; module was already loaded
|
||||
// when constructing module graph during call to `prepare_load`.
|
||||
async move { ps.load(module_specifier, maybe_referrer) }.boxed_local()
|
||||
// NOTE: this block is async only because of `deno_core` interface
|
||||
// requirements; module was already loaded when constructing module graph
|
||||
// during call to `prepare_load`.
|
||||
async move { ps.load(module_specifier, maybe_referrer, is_dynamic) }
|
||||
.boxed_local()
|
||||
}
|
||||
|
||||
fn prepare_load(
|
||||
|
@ -117,24 +91,31 @@ impl ModuleLoader for CliModuleLoader {
|
|||
) -> Pin<Box<dyn Future<Output = Result<(), AnyError>>>> {
|
||||
let specifier = specifier.clone();
|
||||
let ps = self.ps.clone();
|
||||
let maybe_import_map = self.import_map.clone();
|
||||
let state = op_state.borrow();
|
||||
|
||||
let root_permissions = self.root_permissions.clone();
|
||||
let dynamic_permissions = state.borrow::<Permissions>().clone();
|
||||
let root_permissions = if is_dynamic {
|
||||
dynamic_permissions.clone()
|
||||
} else {
|
||||
self.root_permissions.clone()
|
||||
};
|
||||
|
||||
let lib = self.lib.clone();
|
||||
let lib = match self.lib {
|
||||
TypeLib::DenoWindow => crate::emit::TypeLib::DenoWindow,
|
||||
TypeLib::DenoWorker => crate::emit::TypeLib::DenoWorker,
|
||||
TypeLib::UnstableDenoWindow => crate::emit::TypeLib::UnstableDenoWindow,
|
||||
TypeLib::UnstableDenoWorker => crate::emit::TypeLib::UnstableDenoWorker,
|
||||
};
|
||||
drop(state);
|
||||
|
||||
// TODO(bartlomieju): `prepare_module_load` should take `load_id` param
|
||||
async move {
|
||||
ps.prepare_module_load(
|
||||
specifier,
|
||||
vec![specifier],
|
||||
is_dynamic,
|
||||
lib,
|
||||
root_permissions,
|
||||
dynamic_permissions,
|
||||
is_dynamic,
|
||||
maybe_import_map,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
|
@ -1,27 +1,30 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::module_graph::BundleType;
|
||||
use crate::module_graph::EmitOptions;
|
||||
use crate::module_graph::GraphBuilder;
|
||||
use crate::cache;
|
||||
use crate::config_file::IgnoredCompilerOptions;
|
||||
use crate::diagnostics::Diagnostics;
|
||||
use crate::emit;
|
||||
use crate::errors::get_error_class_name;
|
||||
use crate::proc_state::ProcState;
|
||||
use crate::specifier_handler::FetchHandler;
|
||||
use crate::specifier_handler::MemoryHandler;
|
||||
use crate::specifier_handler::SpecifierHandler;
|
||||
use crate::resolver::ImportMapResolver;
|
||||
|
||||
use deno_core::error::custom_error;
|
||||
use deno_core::error::generic_error;
|
||||
use deno_core::error::type_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::error::Context;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::resolve_url_or_path;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::serde_json::Value;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_core::OpState;
|
||||
use deno_graph;
|
||||
use deno_runtime::permissions::Permissions;
|
||||
use import_map::ImportMap;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -37,6 +40,15 @@ enum RuntimeBundleType {
|
|||
Classic,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a RuntimeBundleType> for emit::BundleType {
|
||||
fn from(bundle_type: &'a RuntimeBundleType) -> Self {
|
||||
match bundle_type {
|
||||
RuntimeBundleType::Classic => Self::Classic,
|
||||
RuntimeBundleType::Module => Self::Module,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct EmitArgs {
|
||||
|
@ -52,10 +64,21 @@ struct EmitArgs {
|
|||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct EmitResult {
|
||||
diagnostics: crate::diagnostics::Diagnostics,
|
||||
diagnostics: Diagnostics,
|
||||
files: HashMap<String, String>,
|
||||
ignored_options: Option<crate::config_file::IgnoredCompilerOptions>,
|
||||
stats: crate::module_graph::Stats,
|
||||
#[serde(rename = "ignoredOptions")]
|
||||
maybe_ignored_options: Option<IgnoredCompilerOptions>,
|
||||
stats: emit::Stats,
|
||||
}
|
||||
|
||||
fn to_maybe_imports(
|
||||
referrer: &ModuleSpecifier,
|
||||
maybe_options: Option<&HashMap<String, Value>>,
|
||||
) -> Option<Vec<(ModuleSpecifier, Vec<String>)>> {
|
||||
let options = maybe_options.as_ref()?;
|
||||
let types_value = options.get("types")?;
|
||||
let types: Vec<String> = serde_json::from_value(types_value.clone()).ok()?;
|
||||
Some(vec![(referrer.clone(), types)])
|
||||
}
|
||||
|
||||
async fn op_emit(
|
||||
|
@ -65,23 +88,19 @@ async fn op_emit(
|
|||
) -> Result<EmitResult, AnyError> {
|
||||
deno_runtime::ops::check_unstable2(&state, "Deno.emit");
|
||||
let root_specifier = args.root_specifier;
|
||||
let ps = state.borrow().borrow::<ProcState>().clone();
|
||||
let mut runtime_permissions = {
|
||||
let state = state.borrow();
|
||||
state.borrow::<Permissions>().clone()
|
||||
};
|
||||
// when we are actually resolving modules without provided sources, we should
|
||||
// treat the root module as a dynamic import so that runtime permissions are
|
||||
// applied.
|
||||
let handler: Arc<Mutex<dyn SpecifierHandler>> =
|
||||
if let Some(sources) = args.sources {
|
||||
Arc::new(Mutex::new(MemoryHandler::new(sources)))
|
||||
let state = state.borrow();
|
||||
let ps = state.borrow::<ProcState>();
|
||||
let mut runtime_permissions = { state.borrow::<Permissions>().clone() };
|
||||
let mut cache: Box<dyn cache::CacherLoader> =
|
||||
if let Some(sources) = &args.sources {
|
||||
Box::new(cache::MemoryCacher::new(sources.clone()))
|
||||
} else {
|
||||
Arc::new(Mutex::new(FetchHandler::new(
|
||||
&ps,
|
||||
Box::new(cache::FetchCacher::new(
|
||||
ps.dir.gen_cache.clone(),
|
||||
ps.file_fetcher.clone(),
|
||||
runtime_permissions.clone(),
|
||||
runtime_permissions.clone(),
|
||||
)?))
|
||||
))
|
||||
};
|
||||
let maybe_import_map = if let Some(import_map_str) = args.import_map_path {
|
||||
let import_map_specifier = resolve_url_or_path(&import_map_str)
|
||||
|
@ -107,37 +126,125 @@ async fn op_emit(
|
|||
} else {
|
||||
None
|
||||
};
|
||||
let mut builder = GraphBuilder::new(handler, maybe_import_map, None);
|
||||
let root_specifier = resolve_url_or_path(&root_specifier)?;
|
||||
builder.add(&root_specifier, false).await.map_err(|_| {
|
||||
type_error(format!(
|
||||
"Unable to handle the given specifier: {}",
|
||||
&root_specifier
|
||||
))
|
||||
})?;
|
||||
builder
|
||||
.analyze_compiler_options(&args.compiler_options)
|
||||
.await?;
|
||||
let bundle_type = match args.bundle {
|
||||
Some(RuntimeBundleType::Module) => BundleType::Module,
|
||||
Some(RuntimeBundleType::Classic) => BundleType::Classic,
|
||||
None => BundleType::None,
|
||||
};
|
||||
let graph = builder.get_graph();
|
||||
let roots = vec![resolve_url_or_path(&root_specifier)?];
|
||||
let maybe_imports =
|
||||
to_maybe_imports(&roots[0], args.compiler_options.as_ref());
|
||||
let maybe_resolver = maybe_import_map.as_ref().map(ImportMapResolver::new);
|
||||
let graph = Arc::new(
|
||||
deno_graph::create_graph(
|
||||
roots,
|
||||
true,
|
||||
maybe_imports,
|
||||
cache.as_mut_loader(),
|
||||
maybe_resolver.as_ref().map(|r| r.as_resolver()),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await,
|
||||
);
|
||||
// There are certain graph errors that we want to return as an error of an op,
|
||||
// versus something that gets returned as a diagnostic of the op, this is
|
||||
// handled here.
|
||||
if let Err(err) = graph.valid() {
|
||||
let err: AnyError = err.into();
|
||||
if get_error_class_name(&err) == "PermissionDenied" {
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
let check = args.check.unwrap_or(true);
|
||||
let debug = ps.flags.log_level == Some(log::Level::Debug);
|
||||
let graph_errors = graph.get_errors();
|
||||
let (files, mut result_info) = graph.emit(EmitOptions {
|
||||
bundle_type,
|
||||
check: args.check.unwrap_or(true),
|
||||
debug,
|
||||
maybe_user_config: args.compiler_options,
|
||||
})?;
|
||||
result_info.diagnostics.extend_graph_errors(graph_errors);
|
||||
let tsc_emit = check && args.bundle.is_none();
|
||||
let (ts_config, maybe_ignored_options) = emit::get_ts_config(
|
||||
emit::ConfigType::RuntimeEmit { tsc_emit },
|
||||
None,
|
||||
args.compiler_options.as_ref(),
|
||||
)?;
|
||||
let (files, mut diagnostics, stats) = if check && args.bundle.is_none() {
|
||||
let (diagnostics, stats) = if args.sources.is_none()
|
||||
&& emit::valid_emit(
|
||||
graph.as_ref(),
|
||||
cache.as_cacher(),
|
||||
&ts_config,
|
||||
ps.flags.reload,
|
||||
&HashSet::default(),
|
||||
) {
|
||||
log::debug!(
|
||||
"cache is valid for \"{}\", skipping check/emit",
|
||||
root_specifier
|
||||
);
|
||||
(Diagnostics::default(), emit::Stats::default())
|
||||
} else {
|
||||
let emit_result = emit::check_and_maybe_emit(
|
||||
graph.clone(),
|
||||
cache.as_mut_cacher(),
|
||||
emit::CheckOptions {
|
||||
debug,
|
||||
emit_with_diagnostics: true,
|
||||
maybe_config_specifier: None,
|
||||
ts_config,
|
||||
},
|
||||
)?;
|
||||
(emit_result.diagnostics, emit_result.stats)
|
||||
};
|
||||
let files = emit::to_file_map(graph.as_ref(), cache.as_mut_cacher());
|
||||
(files, diagnostics, stats)
|
||||
} else if let Some(bundle) = &args.bundle {
|
||||
let (diagnostics, stats) = if check {
|
||||
if ts_config.get_declaration() {
|
||||
return Err(custom_error("TypeError", "The bundle option is set, but the compiler option of `declaration` is true which is not currently supported."));
|
||||
}
|
||||
let emit_result = emit::check_and_maybe_emit(
|
||||
graph.clone(),
|
||||
cache.as_mut_cacher(),
|
||||
emit::CheckOptions {
|
||||
debug,
|
||||
emit_with_diagnostics: true,
|
||||
maybe_config_specifier: None,
|
||||
ts_config: ts_config.clone(),
|
||||
},
|
||||
)?;
|
||||
(emit_result.diagnostics, emit_result.stats)
|
||||
} else {
|
||||
(Diagnostics::default(), Default::default())
|
||||
};
|
||||
let (emit, maybe_map) = emit::bundle(
|
||||
graph.as_ref(),
|
||||
emit::BundleOptions {
|
||||
bundle_type: bundle.into(),
|
||||
ts_config,
|
||||
},
|
||||
)?;
|
||||
let mut files = HashMap::new();
|
||||
files.insert("deno:///bundle.js".to_string(), emit);
|
||||
if let Some(map) = maybe_map {
|
||||
files.insert("deno:///bundle.js.map".to_string(), map);
|
||||
}
|
||||
(files, diagnostics, stats)
|
||||
} else {
|
||||
if ts_config.get_declaration() {
|
||||
return Err(custom_error("TypeError", "The option of `check` is false, but the compiler option of `declaration` is true which is not currently supported."));
|
||||
}
|
||||
let emit_result = emit::emit(
|
||||
graph.as_ref(),
|
||||
cache.as_mut_cacher(),
|
||||
emit::EmitOptions {
|
||||
reload: ps.flags.reload,
|
||||
ts_config,
|
||||
reload_exclusions: HashSet::default(),
|
||||
},
|
||||
)?;
|
||||
let files = emit::to_file_map(graph.as_ref(), cache.as_mut_cacher());
|
||||
(files, emit_result.diagnostics, emit_result.stats)
|
||||
};
|
||||
|
||||
// we want to add any errors that were returned as an `Err` earlier by adding
|
||||
// them to the diagnostics.
|
||||
diagnostics.extend_graph_errors(graph.errors());
|
||||
|
||||
Ok(EmitResult {
|
||||
diagnostics: result_info.diagnostics,
|
||||
diagnostics,
|
||||
files,
|
||||
ignored_options: result_info.maybe_ignored_options,
|
||||
stats: result_info.stats,
|
||||
maybe_ignored_options,
|
||||
stats,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,23 +1,24 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::cache;
|
||||
use crate::colors;
|
||||
use crate::compat;
|
||||
use crate::config_file::ConfigFile;
|
||||
use crate::deno_dir;
|
||||
use crate::emit;
|
||||
use crate::errors::get_module_graph_error_class;
|
||||
use crate::file_fetcher::CacheSetting;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::flags;
|
||||
use crate::http_cache;
|
||||
use crate::lockfile::as_maybe_locker;
|
||||
use crate::lockfile::Lockfile;
|
||||
use crate::module_graph::CheckOptions;
|
||||
use crate::module_graph::GraphBuilder;
|
||||
use crate::module_graph::TranspileOptions;
|
||||
use crate::module_graph::TypeLib;
|
||||
use crate::resolver::ImportMapResolver;
|
||||
use crate::source_maps::SourceMapGetter;
|
||||
use crate::specifier_handler::FetchHandler;
|
||||
use crate::version;
|
||||
|
||||
use deno_core::error::anyhow;
|
||||
use deno_core::error::custom_error;
|
||||
use deno_core::error::get_custom_error_class;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::error::Context;
|
||||
|
@ -36,9 +37,6 @@ use deno_tls::rustls::RootCertStore;
|
|||
use deno_tls::rustls_native_certs::load_native_certs;
|
||||
use deno_tls::webpki_roots::TLS_SERVER_ROOTS;
|
||||
use import_map::ImportMap;
|
||||
use log::debug;
|
||||
use log::info;
|
||||
use log::warn;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
|
@ -59,12 +57,25 @@ pub struct Inner {
|
|||
pub dir: deno_dir::DenoDir,
|
||||
pub coverage_dir: Option<String>,
|
||||
pub file_fetcher: FileFetcher,
|
||||
pub modules:
|
||||
Arc<Mutex<HashMap<ModuleSpecifier, Result<ModuleSource, AnyError>>>>,
|
||||
modules: Arc<Mutex<HashMap<ModuleSpecifier, Result<ModuleSource, AnyError>>>>,
|
||||
pub lockfile: Option<Arc<Mutex<Lockfile>>>,
|
||||
pub maybe_config_file: Option<ConfigFile>,
|
||||
pub maybe_import_map: Option<ImportMap>,
|
||||
pub maybe_inspector_server: Option<Arc<InspectorServer>>,
|
||||
// deno_graph detects all sorts of issues at build time (prepare_module_load)
|
||||
// but if they are errors at that stage, the don't cause the correct behaviors
|
||||
// so we cache the error and then surface it when appropriate (e.g. load)
|
||||
pub(crate) maybe_graph_error:
|
||||
Arc<Mutex<Option<deno_graph::ModuleGraphError>>>,
|
||||
// because the graph detects resolution issues early, but is build and dropped
|
||||
// during the `prepare_module_load` method, we need to extract out the module
|
||||
// resolution map so that those errors can be surfaced at the appropriate time
|
||||
resolution_map:
|
||||
Arc<Mutex<HashMap<ModuleSpecifier, HashMap<String, deno_graph::Resolved>>>>,
|
||||
// in some cases we want to provide the span where the resolution error
|
||||
// occurred but need to surface it on load, but on load we don't know who the
|
||||
// referrer and span was, so we need to cache those
|
||||
resolved_map: Arc<Mutex<HashMap<ModuleSpecifier, deno_graph::Span>>>,
|
||||
pub root_cert_store: Option<RootCertStore>,
|
||||
pub blob_store: BlobStore,
|
||||
pub broadcast_channel: InMemoryBroadcastChannel,
|
||||
|
@ -222,11 +233,11 @@ impl ProcState {
|
|||
let diagnostics = import_map.update_imports(node_builtins)?;
|
||||
|
||||
if !diagnostics.is_empty() {
|
||||
info!("Some Node built-ins were not added to the import map:");
|
||||
log::info!("Some Node built-ins were not added to the import map:");
|
||||
for diagnostic in diagnostics {
|
||||
info!(" - {}", diagnostic);
|
||||
log::info!(" - {}", diagnostic);
|
||||
}
|
||||
info!("If you want to use Node built-ins provided by Deno remove listed specifiers from \"imports\" mapping in the import map file.");
|
||||
log::info!("If you want to use Node built-ins provided by Deno remove listed specifiers from \"imports\" mapping in the import map file.");
|
||||
}
|
||||
|
||||
maybe_import_map = Some(import_map);
|
||||
|
@ -252,6 +263,9 @@ impl ProcState {
|
|||
maybe_config_file,
|
||||
maybe_import_map,
|
||||
maybe_inspector_server,
|
||||
maybe_graph_error: Default::default(),
|
||||
resolution_map: Default::default(),
|
||||
resolved_map: Default::default(),
|
||||
root_cert_store: Some(root_cert_store.clone()),
|
||||
blob_store,
|
||||
broadcast_channel,
|
||||
|
@ -260,72 +274,174 @@ impl ProcState {
|
|||
})))
|
||||
}
|
||||
|
||||
/// Prepares a set of module specifiers for loading in one shot.
|
||||
pub async fn prepare_module_graph(
|
||||
/// Return any imports that should be brought into the scope of the module
|
||||
/// graph.
|
||||
fn get_maybe_imports(&self) -> Option<Vec<(ModuleSpecifier, Vec<String>)>> {
|
||||
let mut imports = Vec::new();
|
||||
if let Some(config_file) = &self.maybe_config_file {
|
||||
if let Some(config_imports) = config_file.to_maybe_imports() {
|
||||
imports.extend(config_imports);
|
||||
}
|
||||
}
|
||||
if self.flags.compat {
|
||||
imports.extend(compat::get_node_imports());
|
||||
}
|
||||
if imports.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(imports)
|
||||
}
|
||||
}
|
||||
|
||||
/// This method is called when a module requested by the `JsRuntime` is not
|
||||
/// available, or in other sub-commands that need to "load" a module graph.
|
||||
/// The method will collect all the dependencies of the provided specifier,
|
||||
/// optionally checks their integrity, optionally type checks them, and
|
||||
/// ensures that any modules that needs to be transpiled is transpiled.
|
||||
///
|
||||
/// It then populates the `loadable_modules` with what can be loaded into v8.
|
||||
pub(crate) async fn prepare_module_load(
|
||||
&self,
|
||||
specifiers: Vec<ModuleSpecifier>,
|
||||
lib: TypeLib,
|
||||
roots: Vec<ModuleSpecifier>,
|
||||
is_dynamic: bool,
|
||||
lib: emit::TypeLib,
|
||||
root_permissions: Permissions,
|
||||
dynamic_permissions: Permissions,
|
||||
maybe_import_map: Option<ImportMap>,
|
||||
) -> Result<(), AnyError> {
|
||||
let handler = Arc::new(Mutex::new(FetchHandler::new(
|
||||
self,
|
||||
root_permissions,
|
||||
dynamic_permissions,
|
||||
)?));
|
||||
let mut cache = cache::FetchCacher::new(
|
||||
self.dir.gen_cache.clone(),
|
||||
self.file_fetcher.clone(),
|
||||
root_permissions.clone(),
|
||||
dynamic_permissions.clone(),
|
||||
);
|
||||
let maybe_locker = as_maybe_locker(self.lockfile.clone());
|
||||
let maybe_imports = self.get_maybe_imports();
|
||||
let maybe_resolver =
|
||||
self.maybe_import_map.as_ref().map(ImportMapResolver::new);
|
||||
let graph = deno_graph::create_graph(
|
||||
roots,
|
||||
is_dynamic,
|
||||
maybe_imports,
|
||||
&mut cache,
|
||||
maybe_resolver.as_ref().map(|im| im.as_resolver()),
|
||||
maybe_locker,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
// If there was a locker, validate the integrity of all the modules in the
|
||||
// locker.
|
||||
emit::lock(&graph);
|
||||
|
||||
let mut builder =
|
||||
GraphBuilder::new(handler, maybe_import_map, self.lockfile.clone());
|
||||
|
||||
for specifier in specifiers {
|
||||
builder.add(&specifier, false).await?;
|
||||
}
|
||||
builder.analyze_config_file(&self.maybe_config_file).await?;
|
||||
|
||||
let mut graph = builder.get_graph();
|
||||
let debug = self.flags.log_level == Some(log::Level::Debug);
|
||||
let maybe_config_file = self.maybe_config_file.clone();
|
||||
let reload_exclusions = {
|
||||
// Determine any modules that have already been emitted this session and
|
||||
// should be skipped.
|
||||
let reload_exclusions: HashSet<ModuleSpecifier> = {
|
||||
let modules = self.modules.lock();
|
||||
modules.keys().cloned().collect::<HashSet<_>>()
|
||||
modules.keys().cloned().collect()
|
||||
};
|
||||
|
||||
let result_modules = if self.flags.no_check {
|
||||
let result_info = graph.transpile(TranspileOptions {
|
||||
debug,
|
||||
maybe_config_file,
|
||||
reload: self.flags.reload,
|
||||
reload_exclusions,
|
||||
})?;
|
||||
debug!("{}", result_info.stats);
|
||||
if let Some(ignored_options) = result_info.maybe_ignored_options {
|
||||
warn!("{}", ignored_options);
|
||||
}
|
||||
result_info.loadable_modules
|
||||
let config_type = if self.flags.no_check {
|
||||
emit::ConfigType::Emit
|
||||
} else {
|
||||
let result_info = graph.check(CheckOptions {
|
||||
debug,
|
||||
emit: true,
|
||||
emit::ConfigType::Check {
|
||||
tsc_emit: true,
|
||||
lib,
|
||||
maybe_config_file,
|
||||
reload: self.flags.reload,
|
||||
reload_exclusions,
|
||||
})?;
|
||||
|
||||
debug!("{}", result_info.stats);
|
||||
if let Some(ignored_options) = result_info.maybe_ignored_options {
|
||||
eprintln!("{}", ignored_options);
|
||||
}
|
||||
if !result_info.diagnostics.is_empty() {
|
||||
return Err(anyhow!(result_info.diagnostics));
|
||||
}
|
||||
result_info.loadable_modules
|
||||
};
|
||||
|
||||
let mut loadable_modules = self.modules.lock();
|
||||
loadable_modules.extend(result_modules);
|
||||
let (ts_config, maybe_ignored_options) =
|
||||
emit::get_ts_config(config_type, self.maybe_config_file.as_ref(), None)?;
|
||||
let graph = Arc::new(graph);
|
||||
|
||||
// we will store this in proc state later, as if we were to return it from
|
||||
// prepare_load, some dynamic errors would not be catchable
|
||||
let maybe_graph_error = graph.valid().err();
|
||||
|
||||
if emit::valid_emit(
|
||||
graph.as_ref(),
|
||||
&cache,
|
||||
&ts_config,
|
||||
self.flags.reload,
|
||||
&reload_exclusions,
|
||||
) {
|
||||
if let Some(root) = graph.roots.get(0) {
|
||||
log::debug!("specifier \"{}\" and dependencies have valid emit, skipping checking and emitting", root);
|
||||
} else {
|
||||
log::debug!("rootless graph, skipping checking and emitting");
|
||||
}
|
||||
} else {
|
||||
if let Some(ignored_options) = maybe_ignored_options {
|
||||
log::warn!("{}", ignored_options);
|
||||
}
|
||||
let emit_result = if self.flags.no_check {
|
||||
let options = emit::EmitOptions {
|
||||
ts_config,
|
||||
reload_exclusions,
|
||||
reload: self.flags.reload,
|
||||
};
|
||||
emit::emit(graph.as_ref(), &mut cache, options)?
|
||||
} else {
|
||||
// here, we are type checking, so we want to error here if any of the
|
||||
// type only dependencies are missing or we have other errors with them
|
||||
// where as if we are not type checking, we shouldn't care about these
|
||||
// errors, and they don't get returned in `graph.valid()` above.
|
||||
graph.valid_types_only()?;
|
||||
|
||||
let maybe_config_specifier = self
|
||||
.maybe_config_file
|
||||
.as_ref()
|
||||
.map(|cf| ModuleSpecifier::from_file_path(&cf.path).unwrap());
|
||||
let options = emit::CheckOptions {
|
||||
debug: self.flags.log_level == Some(log::Level::Debug),
|
||||
emit_with_diagnostics: true,
|
||||
maybe_config_specifier,
|
||||
ts_config,
|
||||
};
|
||||
for root in &graph.roots {
|
||||
let root_str = root.to_string();
|
||||
// `$deno$` specifiers are internal specifiers, printing out that
|
||||
// they are being checked is confusing to a user, since they don't
|
||||
// actually exist, so we will simply indicate that a generated module
|
||||
// is being checked instead of the cryptic internal module
|
||||
if !root_str.contains("$deno$") {
|
||||
log::info!("{} {}", colors::green("Check"), root);
|
||||
} else {
|
||||
log::info!("{} a generated module", colors::green("Check"))
|
||||
}
|
||||
}
|
||||
emit::check_and_maybe_emit(graph.clone(), &mut cache, options)?
|
||||
};
|
||||
log::debug!("{}", emit_result.stats);
|
||||
// if the graph is not valid then the diagnostics returned are bogus and
|
||||
// should just be ignored so that module loading can proceed to allow the
|
||||
// "real" error to be surfaced
|
||||
if !emit_result.diagnostics.is_empty() && maybe_graph_error.is_none() {
|
||||
return Err(anyhow!(emit_result.diagnostics));
|
||||
}
|
||||
}
|
||||
|
||||
// we iterate over the graph, looking for any modules that were emitted, or
|
||||
// should be loaded as their un-emitted source and add them to the in memory
|
||||
// cache of modules for loading by deno_core.
|
||||
{
|
||||
let mut modules = self.modules.lock();
|
||||
modules.extend(emit::to_module_sources(graph.as_ref(), &cache));
|
||||
}
|
||||
|
||||
// since we can't store the graph in proc state, because proc state needs to
|
||||
// be thread safe because of the need to provide source map resolution and
|
||||
// the graph needs to not be thread safe (due to wasmbind_gen constraints),
|
||||
// we have no choice but to extract out other meta data from the graph to
|
||||
// provide the correct loading behaviors for CLI
|
||||
{
|
||||
let mut resolution_map = self.resolution_map.lock();
|
||||
resolution_map.extend(graph.resolution_map());
|
||||
}
|
||||
{
|
||||
let mut self_maybe_graph_error = self.maybe_graph_error.lock();
|
||||
*self_maybe_graph_error = maybe_graph_error;
|
||||
}
|
||||
|
||||
// any updates to the lockfile should be updated now
|
||||
if let Some(ref lockfile) = self.lockfile {
|
||||
let g = lockfile.lock();
|
||||
g.write()?;
|
||||
|
@ -334,127 +450,116 @@ impl ProcState {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// This function is called when new module load is initialized by the JsRuntime. Its
|
||||
/// resposibility is to collect all dependencies and if it is required then also perform TS
|
||||
/// typecheck and traspilation.
|
||||
pub async fn prepare_module_load(
|
||||
pub(crate) fn resolve(
|
||||
&self,
|
||||
specifier: ModuleSpecifier,
|
||||
lib: TypeLib,
|
||||
root_permissions: Permissions,
|
||||
dynamic_permissions: Permissions,
|
||||
is_dynamic: bool,
|
||||
maybe_import_map: Option<ImportMap>,
|
||||
) -> Result<(), AnyError> {
|
||||
let specifier = specifier.clone();
|
||||
let handler = Arc::new(Mutex::new(FetchHandler::new(
|
||||
self,
|
||||
root_permissions,
|
||||
dynamic_permissions,
|
||||
)?));
|
||||
let mut builder =
|
||||
GraphBuilder::new(handler, maybe_import_map, self.lockfile.clone());
|
||||
if self.flags.compat {
|
||||
builder.add(&compat::get_node_globals_url(), false).await?;
|
||||
}
|
||||
builder.add(&specifier, is_dynamic).await?;
|
||||
builder.analyze_config_file(&self.maybe_config_file).await?;
|
||||
let mut graph = builder.get_graph();
|
||||
let debug = self.flags.log_level == Some(log::Level::Debug);
|
||||
let maybe_config_file = self.maybe_config_file.clone();
|
||||
let reload_exclusions = {
|
||||
let modules = self.modules.lock();
|
||||
modules.keys().cloned().collect::<HashSet<_>>()
|
||||
};
|
||||
|
||||
let result_modules = if self.flags.no_check {
|
||||
let result_info = graph.transpile(TranspileOptions {
|
||||
debug,
|
||||
maybe_config_file,
|
||||
reload: self.flags.reload,
|
||||
reload_exclusions,
|
||||
})?;
|
||||
debug!("{}", result_info.stats);
|
||||
if let Some(ignored_options) = result_info.maybe_ignored_options {
|
||||
warn!("{}", ignored_options);
|
||||
specifier: &str,
|
||||
referrer: &str,
|
||||
) -> Result<ModuleSpecifier, AnyError> {
|
||||
let resolution_map = self.resolution_map.lock();
|
||||
if let Some((_, Some(map))) = deno_core::resolve_url_or_path(referrer)
|
||||
.ok()
|
||||
.map(|s| (s.clone(), resolution_map.get(&s)))
|
||||
{
|
||||
if let Some(resolved) = map.get(specifier) {
|
||||
match resolved {
|
||||
Some(Ok((specifier, span))) => {
|
||||
let mut resolved_map = self.resolved_map.lock();
|
||||
resolved_map.insert(specifier.clone(), span.clone());
|
||||
return Ok(specifier.clone());
|
||||
}
|
||||
Some(Err(err)) => {
|
||||
return Err(custom_error(
|
||||
"TypeError",
|
||||
format!("{}\n", err.to_string_with_span()),
|
||||
))
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
result_info.loadable_modules
|
||||
}
|
||||
// FIXME(bartlomieju): hacky way to provide compatibility with repl
|
||||
let referrer = if referrer.is_empty() && self.flags.repl {
|
||||
deno_core::DUMMY_SPECIFIER
|
||||
} else {
|
||||
let result_info = graph.check(CheckOptions {
|
||||
debug,
|
||||
emit: true,
|
||||
lib,
|
||||
maybe_config_file,
|
||||
reload: self.flags.reload,
|
||||
reload_exclusions,
|
||||
})?;
|
||||
|
||||
debug!("{}", result_info.stats);
|
||||
if let Some(ignored_options) = result_info.maybe_ignored_options {
|
||||
eprintln!("{}", ignored_options);
|
||||
}
|
||||
if !result_info.diagnostics.is_empty() {
|
||||
return Err(anyhow!(result_info.diagnostics));
|
||||
}
|
||||
result_info.loadable_modules
|
||||
referrer
|
||||
};
|
||||
|
||||
let mut loadable_modules = self.modules.lock();
|
||||
loadable_modules.extend(result_modules);
|
||||
|
||||
if let Some(ref lockfile) = self.lockfile {
|
||||
let g = lockfile.lock();
|
||||
g.write()?;
|
||||
if let Some(import_map) = &self.maybe_import_map {
|
||||
import_map
|
||||
.resolve(specifier, referrer)
|
||||
.map_err(|err| err.into())
|
||||
} else {
|
||||
deno_core::resolve_import(specifier, referrer).map_err(|err| err.into())
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load(
|
||||
&self,
|
||||
specifier: ModuleSpecifier,
|
||||
maybe_referrer: Option<ModuleSpecifier>,
|
||||
is_dynamic: bool,
|
||||
) -> Result<ModuleSource, AnyError> {
|
||||
log::debug!(
|
||||
"specifier: {} maybe_referrer: {} is_dynamic: {}",
|
||||
specifier,
|
||||
maybe_referrer
|
||||
.as_ref()
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(|| "<none>".to_string()),
|
||||
is_dynamic
|
||||
);
|
||||
let modules = self.modules.lock();
|
||||
modules
|
||||
.get(&specifier)
|
||||
.map(|r| match r {
|
||||
Ok(module_source) => Ok(module_source.clone()),
|
||||
Err(err) => {
|
||||
// TODO(@kitsonk) this feels a bit hacky but it works, without
|
||||
// introducing another enum to have to try to deal with.
|
||||
if get_custom_error_class(err) == Some("NotFound") {
|
||||
let message = if let Some(referrer) = &maybe_referrer {
|
||||
format!("{}\n From: {}\n If the source module contains only types, use `import type` and `export type` to import it instead.", err, referrer)
|
||||
} else {
|
||||
format!("{}\n If the source module contains only types, use `import type` and `export type` to import it instead.", err)
|
||||
};
|
||||
warn!("{}: {}", crate::colors::yellow("warning"), message);
|
||||
Ok(ModuleSource {
|
||||
code: "".to_string(),
|
||||
module_url_found: specifier.to_string(),
|
||||
module_url_specified: specifier.to_string(),
|
||||
})
|
||||
// this is the "pending" error we will return
|
||||
let err = if let Some(error_class) = get_custom_error_class(err) {
|
||||
if error_class == "NotFound" && maybe_referrer.is_some() && !is_dynamic {
|
||||
let resolved_map = self.resolved_map.lock();
|
||||
// in situations where we were to try to load a module that wasn't
|
||||
// emitted and we can't run the original source code (it isn't)
|
||||
// JavaScript, we will load a blank module instead. This is
|
||||
// usually caused by people exporting type only exports and not
|
||||
// type checking.
|
||||
if let Some(span) = resolved_map.get(&specifier) {
|
||||
log::warn!("{}: Cannot load module \"{}\".\n at {}\n If the source module contains only types, use `import type` and `export type` to import it instead.", colors::yellow("warning"), specifier, span);
|
||||
return Ok(ModuleSource {
|
||||
code: "".to_string(),
|
||||
module_url_found: specifier.to_string(),
|
||||
module_url_specified: specifier.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
custom_error(error_class, err.to_string())
|
||||
} else {
|
||||
// anyhow errors don't support cloning, so we have to manage this
|
||||
// ourselves
|
||||
Err(anyhow!(err.to_string()))
|
||||
anyhow!(err.to_string())
|
||||
};
|
||||
// if there is a pending graph error though we haven't returned, we
|
||||
// will return that one
|
||||
let mut maybe_graph_error = self.maybe_graph_error.lock();
|
||||
if let Some(graph_error) = maybe_graph_error.take() {
|
||||
log::debug!("returning cached graph error");
|
||||
let resolved_map = self.resolved_map.lock();
|
||||
if let Some(span) = resolved_map.get(&specifier) {
|
||||
if !span.specifier.as_str().contains("$deno") {
|
||||
return Err(custom_error(get_module_graph_error_class(&graph_error), format!("{}\n at {}", graph_error, span)));
|
||||
}
|
||||
}
|
||||
Err(graph_error.into())
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
if let Some(referrer) = maybe_referrer {
|
||||
Err(anyhow!(
|
||||
"Module \"{}\" is missing from the graph.\n From: {}",
|
||||
specifier,
|
||||
referrer
|
||||
))
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"Module \"{}\" is missing from the graph.",
|
||||
specifier
|
||||
))
|
||||
if maybe_referrer.is_some() && !is_dynamic {
|
||||
let resolved_map = self.resolved_map.lock();
|
||||
if let Some(span) = resolved_map.get(&specifier) {
|
||||
return Err(custom_error("NotFound", format!("Cannot load module \"{}\".\n at {}", specifier, span)));
|
||||
}
|
||||
}
|
||||
Err(custom_error("NotFound", format!("Cannot load module \"{}\".", specifier)))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -497,7 +602,7 @@ impl SourceMapGetter for ProcState {
|
|||
if let Some((code, maybe_map)) = self.get_emit(&specifier) {
|
||||
let code = String::from_utf8(code).unwrap();
|
||||
source_map_from_code(code).or(maybe_map)
|
||||
} else if let Ok(source) = self.load(specifier, None) {
|
||||
} else if let Ok(source) = self.load(specifier, None, false) {
|
||||
source_map_from_code(source.code)
|
||||
} else {
|
||||
None
|
||||
|
|
35
cli/resolver.rs
Normal file
35
cli/resolver.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_graph::source::Resolver;
|
||||
use import_map::ImportMap;
|
||||
|
||||
/// Wraps an import map to be used when building a deno_graph module graph.
|
||||
/// This is done to avoid having `import_map` be a direct dependency of
|
||||
/// `deno_graph`.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ImportMapResolver<'a>(&'a ImportMap);
|
||||
|
||||
impl<'a> ImportMapResolver<'a> {
|
||||
pub fn new(import_map: &'a ImportMap) -> Self {
|
||||
Self(import_map)
|
||||
}
|
||||
|
||||
pub fn as_resolver(&'a self) -> &'a dyn Resolver {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolver for ImportMapResolver<'_> {
|
||||
fn resolve(
|
||||
&self,
|
||||
specifier: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
) -> Result<ModuleSpecifier, AnyError> {
|
||||
self
|
||||
.0
|
||||
.resolve(specifier, referrer.as_str())
|
||||
.map_err(|err| err.into())
|
||||
}
|
||||
}
|
|
@ -1,776 +0,0 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::ast::Location;
|
||||
use crate::disk_cache::DiskCache;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::proc_state::ProcState;
|
||||
|
||||
use deno_ast::MediaType;
|
||||
use deno_core::error::custom_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::future;
|
||||
use deno_core::futures::Future;
|
||||
use deno_core::futures::FutureExt;
|
||||
use deno_core::serde::Deserialize;
|
||||
use deno_core::serde::Serialize;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_runtime::permissions::Permissions;
|
||||
use log::debug;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::path::PathBuf;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub type DependencyMap = HashMap<String, Dependency>;
|
||||
type FetchFutureOutput = Result<CachedModule, (ModuleSpecifier, AnyError)>;
|
||||
pub type FetchFuture = Pin<Box<dyn Future<Output = FetchFutureOutput> + Send>>;
|
||||
|
||||
/// A group of errors that represent errors that can occur with an
|
||||
/// an implementation of `SpecifierHandler`.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum HandlerError {
|
||||
/// A fetch error, where we have a location associated with it.
|
||||
FetchErrorWithLocation(String, Location),
|
||||
}
|
||||
|
||||
impl fmt::Display for HandlerError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
HandlerError::FetchErrorWithLocation(ref err, ref location) => {
|
||||
write!(f, "{}\n at {}", err, location)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for HandlerError {}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CachedModule {
|
||||
pub is_remote: bool,
|
||||
pub maybe_dependencies: Option<DependencyMap>,
|
||||
pub maybe_emit: Option<Emit>,
|
||||
pub maybe_emit_path: Option<(PathBuf, Option<PathBuf>)>,
|
||||
pub maybe_types: Option<String>,
|
||||
pub maybe_version: Option<String>,
|
||||
pub media_type: MediaType,
|
||||
pub requested_specifier: ModuleSpecifier,
|
||||
pub source: Arc<String>,
|
||||
pub source_path: PathBuf,
|
||||
pub specifier: ModuleSpecifier,
|
||||
}
|
||||
|
||||
impl Default for CachedModule {
|
||||
fn default() -> Self {
|
||||
let specifier = deno_core::resolve_url("file:///example.js").unwrap();
|
||||
CachedModule {
|
||||
is_remote: false,
|
||||
maybe_dependencies: None,
|
||||
maybe_emit: None,
|
||||
maybe_emit_path: None,
|
||||
maybe_types: None,
|
||||
maybe_version: None,
|
||||
media_type: MediaType::Unknown,
|
||||
requested_specifier: specifier.clone(),
|
||||
source: Arc::new(String::default()),
|
||||
source_path: PathBuf::new(),
|
||||
specifier,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An enum to own the a specific emit.
|
||||
///
|
||||
/// Currently there is only one type of emit that is cacheable, but this has
|
||||
/// been added to future proof the ability for the specifier handler
|
||||
/// implementations to be able to handle other types of emits, like form a
|
||||
/// runtime API which might have a different configuration.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum Emit {
|
||||
/// Code that was emitted for use by the CLI
|
||||
Cli((String, Option<String>)),
|
||||
}
|
||||
|
||||
impl Default for Emit {
|
||||
fn default() -> Self {
|
||||
Emit::Cli(("".to_string(), None))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Dependency {
|
||||
/// Flags if the dependency is a dynamic import or not.
|
||||
pub is_dynamic: bool,
|
||||
/// The location in the source code where the dependency statement occurred.
|
||||
pub location: Location,
|
||||
/// The module specifier that resolves to the runtime code dependency for the
|
||||
/// module.
|
||||
pub maybe_code: Option<ModuleSpecifier>,
|
||||
/// The module specifier that resolves to the type only dependency for the
|
||||
/// module.
|
||||
pub maybe_type: Option<ModuleSpecifier>,
|
||||
}
|
||||
|
||||
impl Dependency {
|
||||
pub fn new(location: Location) -> Self {
|
||||
Dependency {
|
||||
is_dynamic: false,
|
||||
location,
|
||||
maybe_code: None,
|
||||
maybe_type: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SpecifierHandler: Sync + Send {
|
||||
/// Instructs the handler to fetch a specifier or retrieve its value from the
|
||||
/// cache.
|
||||
fn fetch(
|
||||
&mut self,
|
||||
specifier: ModuleSpecifier,
|
||||
maybe_location: Option<Location>,
|
||||
is_dynamic: bool,
|
||||
) -> FetchFuture;
|
||||
|
||||
/// Get the optional build info from the cache for a given module specifier.
|
||||
/// Because build infos are only associated with the "root" modules, they are
|
||||
/// not expected to be cached for each module, but are "lazily" checked when
|
||||
/// a root module is identified. The `emit_type` also indicates what form
|
||||
/// of the module the build info is valid for.
|
||||
fn get_tsbuildinfo(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Result<Option<String>, AnyError>;
|
||||
|
||||
/// Set the emit for the module specifier.
|
||||
fn set_cache(
|
||||
&mut self,
|
||||
specifier: &ModuleSpecifier,
|
||||
emit: &Emit,
|
||||
) -> Result<(), AnyError>;
|
||||
|
||||
/// When parsed out of a JavaScript module source, the triple slash reference
|
||||
/// to the types should be stored in the cache.
|
||||
fn set_types(
|
||||
&mut self,
|
||||
specifier: &ModuleSpecifier,
|
||||
types: String,
|
||||
) -> Result<(), AnyError>;
|
||||
|
||||
/// Set the build info for a module specifier, also providing the cache type.
|
||||
fn set_tsbuildinfo(
|
||||
&mut self,
|
||||
specifier: &ModuleSpecifier,
|
||||
tsbuildinfo: String,
|
||||
) -> Result<(), AnyError>;
|
||||
|
||||
/// Set the graph dependencies for a given module specifier.
|
||||
fn set_deps(
|
||||
&mut self,
|
||||
specifier: &ModuleSpecifier,
|
||||
dependencies: DependencyMap,
|
||||
) -> Result<(), AnyError>;
|
||||
|
||||
/// Set the version of the source for a given module, which is used to help
|
||||
/// determine if a module needs to be re-emitted.
|
||||
fn set_version(
|
||||
&mut self,
|
||||
specifier: &ModuleSpecifier,
|
||||
version: String,
|
||||
) -> Result<(), AnyError>;
|
||||
}
|
||||
|
||||
impl fmt::Debug for dyn SpecifierHandler {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "SpecifierHandler {{ }}")
|
||||
}
|
||||
}
|
||||
|
||||
/// A representation of meta data for a compiled file.
|
||||
///
|
||||
/// *Note* this is currently just a copy of what is located in `tsc.rs` but will
|
||||
/// be refactored to be able to store dependencies and type information in the
|
||||
/// future.
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct CompiledFileMetadata {
|
||||
pub version_hash: String,
|
||||
}
|
||||
|
||||
impl CompiledFileMetadata {
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, AnyError> {
|
||||
let metadata_string = std::str::from_utf8(bytes)?;
|
||||
serde_json::from_str::<Self>(metadata_string).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
pub fn to_json_string(&self) -> Result<String, AnyError> {
|
||||
serde_json::to_string(self).map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// An implementation of the `SpecifierHandler` trait that integrates with the
|
||||
/// existing `file_fetcher` interface, which will eventually be refactored to
|
||||
/// align it more to the `SpecifierHandler` trait.
|
||||
pub struct FetchHandler {
|
||||
/// An instance of disk where generated (emitted) files are stored.
|
||||
disk_cache: DiskCache,
|
||||
/// The set permissions which are used for root modules (static imports).
|
||||
root_permissions: Permissions,
|
||||
/// The set of permissions which are used for dynamic imports.
|
||||
dynamic_permissions: Permissions,
|
||||
/// A clone of the `ps` file fetcher.
|
||||
file_fetcher: FileFetcher,
|
||||
}
|
||||
|
||||
impl FetchHandler {
|
||||
pub fn new(
|
||||
ps: &ProcState,
|
||||
root_permissions: Permissions,
|
||||
dynamic_permissions: Permissions,
|
||||
) -> Result<Self, AnyError> {
|
||||
let disk_cache = ps.dir.gen_cache.clone();
|
||||
let file_fetcher = ps.file_fetcher.clone();
|
||||
|
||||
Ok(FetchHandler {
|
||||
disk_cache,
|
||||
root_permissions,
|
||||
dynamic_permissions,
|
||||
file_fetcher,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl SpecifierHandler for FetchHandler {
|
||||
fn fetch(
|
||||
&mut self,
|
||||
requested_specifier: ModuleSpecifier,
|
||||
maybe_location: Option<Location>,
|
||||
is_dynamic: bool,
|
||||
) -> FetchFuture {
|
||||
// When the module graph fetches dynamic modules, the set of dynamic
|
||||
// permissions need to be applied. Other static imports have all
|
||||
// permissions.
|
||||
let mut permissions = if is_dynamic {
|
||||
self.dynamic_permissions.clone()
|
||||
} else {
|
||||
self.root_permissions.clone()
|
||||
};
|
||||
let file_fetcher = self.file_fetcher.clone();
|
||||
let disk_cache = self.disk_cache.clone();
|
||||
|
||||
async move {
|
||||
let source_file = file_fetcher
|
||||
.fetch(&requested_specifier, &mut permissions)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
let err = if let Some(e) = err.downcast_ref::<std::io::Error>() {
|
||||
if e.kind() == std::io::ErrorKind::NotFound {
|
||||
let message = if let Some(location) = &maybe_location {
|
||||
format!(
|
||||
"Cannot resolve module \"{}\" from \"{}\".",
|
||||
requested_specifier, location.specifier
|
||||
)
|
||||
} else {
|
||||
format!("Cannot resolve module \"{}\".", requested_specifier)
|
||||
};
|
||||
custom_error("NotFound", message)
|
||||
} else {
|
||||
err
|
||||
}
|
||||
} else {
|
||||
err
|
||||
};
|
||||
if let Some(location) = maybe_location {
|
||||
// Injected modules (like test and eval) come with locations, but
|
||||
// they are confusing to the user to print out the location because
|
||||
// they cannot actually get to the source code that is quoted, as
|
||||
// it only exists in the runtime memory of Deno.
|
||||
if !location.specifier.contains("$deno$") {
|
||||
(
|
||||
requested_specifier.clone(),
|
||||
HandlerError::FetchErrorWithLocation(err.to_string(), location)
|
||||
.into(),
|
||||
)
|
||||
} else {
|
||||
(requested_specifier.clone(), err)
|
||||
}
|
||||
} else {
|
||||
(requested_specifier.clone(), err)
|
||||
}
|
||||
})?;
|
||||
let url = &source_file.specifier;
|
||||
let is_remote = !(url.scheme() == "file"
|
||||
|| url.scheme() == "data"
|
||||
|| url.scheme() == "blob");
|
||||
let filename = disk_cache.get_cache_filename_with_extension(url, "meta");
|
||||
let maybe_version = if let Some(filename) = filename {
|
||||
if let Ok(bytes) = disk_cache.get(&filename) {
|
||||
if let Ok(compiled_file_metadata) =
|
||||
CompiledFileMetadata::from_bytes(&bytes)
|
||||
{
|
||||
Some(compiled_file_metadata.version_hash)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut maybe_map_path = None;
|
||||
let map_path =
|
||||
disk_cache.get_cache_filename_with_extension(url, "js.map");
|
||||
let maybe_map = if let Some(map_path) = map_path {
|
||||
if let Ok(map) = disk_cache.get(&map_path) {
|
||||
maybe_map_path = Some(disk_cache.location.join(map_path));
|
||||
Some(String::from_utf8(map).unwrap())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut maybe_emit = None;
|
||||
let mut maybe_emit_path = None;
|
||||
let emit_path = disk_cache.get_cache_filename_with_extension(url, "js");
|
||||
if let Some(emit_path) = emit_path {
|
||||
if let Ok(code) = disk_cache.get(&emit_path) {
|
||||
maybe_emit =
|
||||
Some(Emit::Cli((String::from_utf8(code).unwrap(), maybe_map)));
|
||||
maybe_emit_path =
|
||||
Some((disk_cache.location.join(emit_path), maybe_map_path));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(CachedModule {
|
||||
is_remote,
|
||||
maybe_dependencies: None,
|
||||
maybe_emit,
|
||||
maybe_emit_path,
|
||||
maybe_types: source_file.maybe_types,
|
||||
maybe_version,
|
||||
media_type: source_file.media_type,
|
||||
requested_specifier,
|
||||
source: source_file.source,
|
||||
source_path: source_file.local,
|
||||
specifier: source_file.specifier,
|
||||
})
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn get_tsbuildinfo(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Result<Option<String>, AnyError> {
|
||||
let filename = self
|
||||
.disk_cache
|
||||
.get_cache_filename_with_extension(specifier, "buildinfo");
|
||||
if let Some(filename) = filename {
|
||||
if let Ok(tsbuildinfo) = self.disk_cache.get(&filename) {
|
||||
Ok(Some(String::from_utf8(tsbuildinfo)?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_tsbuildinfo(
|
||||
&mut self,
|
||||
specifier: &ModuleSpecifier,
|
||||
tsbuildinfo: String,
|
||||
) -> Result<(), AnyError> {
|
||||
let filename = self
|
||||
.disk_cache
|
||||
.get_cache_filename_with_extension(specifier, "buildinfo")
|
||||
.unwrap();
|
||||
debug!("set_tsbuildinfo - filename {:?}", filename);
|
||||
self
|
||||
.disk_cache
|
||||
.set(&filename, tsbuildinfo.as_bytes())
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
fn set_cache(
|
||||
&mut self,
|
||||
specifier: &ModuleSpecifier,
|
||||
emit: &Emit,
|
||||
) -> Result<(), AnyError> {
|
||||
match emit {
|
||||
Emit::Cli((code, maybe_map)) => {
|
||||
let filename = self
|
||||
.disk_cache
|
||||
.get_cache_filename_with_extension(specifier, "js")
|
||||
.unwrap();
|
||||
self.disk_cache.set(&filename, code.as_bytes())?;
|
||||
|
||||
if let Some(map) = maybe_map {
|
||||
let filename = self
|
||||
.disk_cache
|
||||
.get_cache_filename_with_extension(specifier, "js.map")
|
||||
.unwrap();
|
||||
self.disk_cache.set(&filename, map.as_bytes())?;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_deps(
|
||||
&mut self,
|
||||
_specifier: &ModuleSpecifier,
|
||||
_dependencies: DependencyMap,
|
||||
) -> Result<(), AnyError> {
|
||||
// file_fetcher doesn't have the concept of caching dependencies
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_types(
|
||||
&mut self,
|
||||
_specifier: &ModuleSpecifier,
|
||||
_types: String,
|
||||
) -> Result<(), AnyError> {
|
||||
// file_fetcher doesn't have the concept of caching of the types
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_version(
|
||||
&mut self,
|
||||
specifier: &ModuleSpecifier,
|
||||
version_hash: String,
|
||||
) -> Result<(), AnyError> {
|
||||
let compiled_file_metadata = CompiledFileMetadata { version_hash };
|
||||
let filename = self
|
||||
.disk_cache
|
||||
.get_cache_filename_with_extension(specifier, "meta")
|
||||
.unwrap();
|
||||
|
||||
self
|
||||
.disk_cache
|
||||
.set(
|
||||
&filename,
|
||||
compiled_file_metadata.to_json_string()?.as_bytes(),
|
||||
)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MemoryHandler {
|
||||
sources: HashMap<String, Arc<String>>,
|
||||
}
|
||||
|
||||
impl MemoryHandler {
|
||||
pub fn new(sources: HashMap<String, Arc<String>>) -> Self {
|
||||
Self { sources }
|
||||
}
|
||||
}
|
||||
|
||||
impl SpecifierHandler for MemoryHandler {
|
||||
fn fetch(
|
||||
&mut self,
|
||||
specifier: ModuleSpecifier,
|
||||
_maybe_referrer: Option<Location>,
|
||||
_is_dynamic: bool,
|
||||
) -> FetchFuture {
|
||||
let mut specifier_text = specifier.to_string();
|
||||
if !self.sources.contains_key(&specifier_text) {
|
||||
specifier_text = specifier_text.replace("file:///", "/");
|
||||
if !self.sources.contains_key(&specifier_text) {
|
||||
// Convert `C:/a/path/file.ts` to `/a/path/file.ts`
|
||||
specifier_text = specifier_text[3..].to_string()
|
||||
}
|
||||
}
|
||||
let result = if let Some(source) = self.sources.get(&specifier_text) {
|
||||
let media_type = MediaType::from(&specifier);
|
||||
let is_remote = specifier.scheme() != "file";
|
||||
|
||||
Ok(CachedModule {
|
||||
source: source.clone(),
|
||||
requested_specifier: specifier.clone(),
|
||||
specifier,
|
||||
media_type,
|
||||
is_remote,
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
Err((
|
||||
specifier.clone(),
|
||||
custom_error(
|
||||
"NotFound",
|
||||
format!("Unable to find specifier in sources: {}", specifier),
|
||||
),
|
||||
))
|
||||
};
|
||||
|
||||
Box::pin(future::ready(result))
|
||||
}
|
||||
|
||||
fn get_tsbuildinfo(
|
||||
&self,
|
||||
_specifier: &ModuleSpecifier,
|
||||
) -> Result<Option<String>, AnyError> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn set_cache(
|
||||
&mut self,
|
||||
_specifier: &ModuleSpecifier,
|
||||
_emit: &Emit,
|
||||
) -> Result<(), AnyError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_types(
|
||||
&mut self,
|
||||
_specifier: &ModuleSpecifier,
|
||||
_types: String,
|
||||
) -> Result<(), AnyError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_tsbuildinfo(
|
||||
&mut self,
|
||||
_specifier: &ModuleSpecifier,
|
||||
_tsbuildinfo: String,
|
||||
) -> Result<(), AnyError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_deps(
|
||||
&mut self,
|
||||
_specifier: &ModuleSpecifier,
|
||||
_dependencies: DependencyMap,
|
||||
) -> Result<(), AnyError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_version(
|
||||
&mut self,
|
||||
_specifier: &ModuleSpecifier,
|
||||
_version: String,
|
||||
) -> Result<(), AnyError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use crate::deno_dir::DenoDir;
|
||||
use crate::file_fetcher::CacheSetting;
|
||||
use crate::http_cache::HttpCache;
|
||||
use deno_core::resolve_url_or_path;
|
||||
use deno_runtime::deno_web::BlobStore;
|
||||
use tempfile::TempDir;
|
||||
|
||||
macro_rules! map (
|
||||
{ $($key:expr => $value:expr),+ } => {
|
||||
{
|
||||
let mut m = ::std::collections::HashMap::new();
|
||||
$(
|
||||
m.insert($key, $value);
|
||||
)+
|
||||
m
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
fn setup() -> (TempDir, FetchHandler) {
|
||||
let temp_dir = TempDir::new().expect("could not setup");
|
||||
let deno_dir = DenoDir::new(Some(temp_dir.path().to_path_buf()))
|
||||
.expect("could not setup");
|
||||
|
||||
let file_fetcher = FileFetcher::new(
|
||||
HttpCache::new(&temp_dir.path().to_path_buf().join("deps")),
|
||||
CacheSetting::Use,
|
||||
true,
|
||||
None,
|
||||
BlobStore::default(),
|
||||
None,
|
||||
)
|
||||
.expect("could not setup");
|
||||
let disk_cache = deno_dir.gen_cache;
|
||||
|
||||
let fetch_handler = FetchHandler {
|
||||
disk_cache,
|
||||
root_permissions: Permissions::allow_all(),
|
||||
dynamic_permissions: Permissions::default(),
|
||||
file_fetcher,
|
||||
};
|
||||
|
||||
(temp_dir, fetch_handler)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_fetch_handler_fetch() {
|
||||
let _http_server_guard = test_util::http_server();
|
||||
let (_, mut file_fetcher) = setup();
|
||||
let specifier =
|
||||
resolve_url_or_path("http://localhost:4545/subdir/mod2.ts").unwrap();
|
||||
let cached_module: CachedModule = file_fetcher
|
||||
.fetch(specifier.clone(), None, false)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(cached_module.maybe_emit.is_none());
|
||||
assert!(cached_module.maybe_dependencies.is_none());
|
||||
assert_eq!(cached_module.media_type, MediaType::TypeScript);
|
||||
assert_eq!(
|
||||
cached_module.source.as_str(),
|
||||
"export { printHello } from \"./print_hello.ts\";\n"
|
||||
);
|
||||
assert_eq!(cached_module.specifier, specifier);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_fetch_handler_set_cache() {
|
||||
let _http_server_guard = test_util::http_server();
|
||||
let (_, mut file_fetcher) = setup();
|
||||
let specifier =
|
||||
resolve_url_or_path("http://localhost:4545/subdir/mod2.ts").unwrap();
|
||||
let cached_module: CachedModule = file_fetcher
|
||||
.fetch(specifier.clone(), None, false)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(cached_module.maybe_emit.is_none());
|
||||
let code = String::from("some code");
|
||||
file_fetcher
|
||||
.set_cache(&specifier, &Emit::Cli((code, None)))
|
||||
.expect("could not set cache");
|
||||
let cached_module: CachedModule = file_fetcher
|
||||
.fetch(specifier.clone(), None, false)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
cached_module.maybe_emit,
|
||||
Some(Emit::Cli(("some code".to_string(), None)))
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_fetch_handler_is_remote() {
|
||||
let _http_server_guard = test_util::http_server();
|
||||
let (_, mut file_fetcher) = setup();
|
||||
let specifier =
|
||||
resolve_url_or_path("http://localhost:4545/subdir/mod2.ts").unwrap();
|
||||
let cached_module: CachedModule =
|
||||
file_fetcher.fetch(specifier, None, false).await.unwrap();
|
||||
assert!(cached_module.is_remote);
|
||||
let specifier = resolve_url_or_path(
|
||||
test_util::testdata_path()
|
||||
.join("subdir/mod1.ts")
|
||||
.as_os_str()
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
let cached_module: CachedModule =
|
||||
file_fetcher.fetch(specifier, None, false).await.unwrap();
|
||||
assert!(!cached_module.is_remote);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_memory_handler_fetch() {
|
||||
let a_src = r#"
|
||||
import * as b from "./b.ts";
|
||||
console.log(b);
|
||||
"#;
|
||||
let b_src = r#"
|
||||
export const b = "b";
|
||||
"#;
|
||||
let c_src = r#"
|
||||
export const c = "c";
|
||||
"#;
|
||||
let d_src = r#"
|
||||
export const d: string;
|
||||
"#;
|
||||
let sources = map!(
|
||||
"/a.ts" => a_src,
|
||||
"/b.ts" => b_src,
|
||||
"https://deno.land/x/c.js" => c_src,
|
||||
"https://deno.land/x/d.d.ts" => d_src
|
||||
);
|
||||
let sources: HashMap<String, Arc<String>> = sources
|
||||
.iter()
|
||||
.map(|(k, v)| (k.to_string(), Arc::new(v.to_string())))
|
||||
.collect();
|
||||
let mut handler = MemoryHandler::new(sources);
|
||||
let specifier = resolve_url_or_path("file:///a.ts").unwrap();
|
||||
let actual: CachedModule = handler
|
||||
.fetch(specifier.clone(), None, false)
|
||||
.await
|
||||
.expect("could not fetch module");
|
||||
assert_eq!(actual.source.as_str(), a_src);
|
||||
assert_eq!(actual.requested_specifier, specifier);
|
||||
assert_eq!(actual.specifier, specifier);
|
||||
assert_eq!(actual.media_type, MediaType::TypeScript);
|
||||
assert!(!actual.is_remote);
|
||||
|
||||
let specifier = resolve_url_or_path("file:///b.ts").unwrap();
|
||||
let actual: CachedModule = handler
|
||||
.fetch(specifier.clone(), None, false)
|
||||
.await
|
||||
.expect("could not fetch module");
|
||||
assert_eq!(actual.source.as_str(), b_src);
|
||||
assert_eq!(actual.requested_specifier, specifier);
|
||||
assert_eq!(actual.specifier, specifier);
|
||||
assert_eq!(actual.media_type, MediaType::TypeScript);
|
||||
assert!(!actual.is_remote);
|
||||
|
||||
let specifier = resolve_url_or_path("https://deno.land/x/c.js").unwrap();
|
||||
let actual: CachedModule = handler
|
||||
.fetch(specifier.clone(), None, false)
|
||||
.await
|
||||
.expect("could not fetch module");
|
||||
assert_eq!(actual.source.as_str(), c_src);
|
||||
assert_eq!(actual.requested_specifier, specifier);
|
||||
assert_eq!(actual.specifier, specifier);
|
||||
assert_eq!(actual.media_type, MediaType::JavaScript);
|
||||
assert!(actual.is_remote);
|
||||
|
||||
let specifier = resolve_url_or_path("https://deno.land/x/d.d.ts").unwrap();
|
||||
let actual: CachedModule = handler
|
||||
.fetch(specifier.clone(), None, false)
|
||||
.await
|
||||
.expect("could not fetch module");
|
||||
assert_eq!(actual.source.as_str(), d_src);
|
||||
assert_eq!(actual.requested_specifier, specifier);
|
||||
assert_eq!(actual.specifier, specifier);
|
||||
assert_eq!(actual.media_type, MediaType::Dts);
|
||||
assert!(actual.is_remote);
|
||||
|
||||
let specifier =
|
||||
resolve_url_or_path("https://deno.land/x/missing.ts").unwrap();
|
||||
handler
|
||||
.fetch(specifier.clone(), None, false)
|
||||
.await
|
||||
.expect_err("should have errored");
|
||||
|
||||
let specifier = resolve_url_or_path("/a.ts").unwrap();
|
||||
let actual: CachedModule = handler
|
||||
.fetch(specifier.clone(), None, false)
|
||||
.await
|
||||
.expect("could not fetch module");
|
||||
assert_eq!(actual.source.as_str(), a_src);
|
||||
assert_eq!(actual.requested_specifier, specifier);
|
||||
assert_eq!(actual.specifier, specifier);
|
||||
assert_eq!(actual.media_type, MediaType::TypeScript);
|
||||
assert!(!actual.is_remote);
|
||||
|
||||
let specifier = resolve_url_or_path("file:///C:/a.ts").unwrap();
|
||||
let actual: CachedModule = handler
|
||||
.fetch(specifier.clone(), None, false)
|
||||
.await
|
||||
.expect("could not fetch module");
|
||||
assert_eq!(actual.source.as_str(), a_src);
|
||||
assert_eq!(actual.requested_specifier, specifier);
|
||||
assert_eq!(actual.specifier, specifier);
|
||||
assert_eq!(actual.media_type, MediaType::TypeScript);
|
||||
assert!(!actual.is_remote);
|
||||
}
|
||||
}
|
|
@ -2761,7 +2761,6 @@ fn lsp_diagnostics_warn() {
|
|||
.unwrap();
|
||||
assert!(maybe_err.is_none());
|
||||
assert!(maybe_res.is_some());
|
||||
|
||||
let (method, _) = client.read_notification::<Value>().unwrap();
|
||||
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||
let (method, _) = client.read_notification::<Value>().unwrap();
|
||||
|
|
|
@ -206,6 +206,7 @@ fn bundle_js_watch() {
|
|||
let (_stdout_lines, mut stderr_lines) = child_lines(&mut deno);
|
||||
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
assert_contains!(stderr_lines.next().unwrap(), "Check");
|
||||
assert_contains!(stderr_lines.next().unwrap(), "file_to_watch.js");
|
||||
assert_contains!(stderr_lines.next().unwrap(), "mod6.bundle.js");
|
||||
let file = PathBuf::from(&bundle);
|
||||
|
@ -214,6 +215,7 @@ fn bundle_js_watch() {
|
|||
|
||||
write(&file_to_watch, "console.log('Hello world2');").unwrap();
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
assert_contains!(stderr_lines.next().unwrap(), "Check");
|
||||
assert_contains!(stderr_lines.next().unwrap(), "File change detected!");
|
||||
assert_contains!(stderr_lines.next().unwrap(), "file_to_watch.js");
|
||||
assert_contains!(stderr_lines.next().unwrap(), "mod6.bundle.js");
|
||||
|
@ -261,6 +263,7 @@ fn bundle_watch_not_exit() {
|
|||
// Make sure the watcher actually restarts and works fine with the proper syntax
|
||||
write(&file_to_watch, "console.log(42);").unwrap();
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
assert_contains!(stderr_lines.next().unwrap(), "Check");
|
||||
assert_contains!(stderr_lines.next().unwrap(), "File change detected!");
|
||||
assert_contains!(stderr_lines.next().unwrap(), "file_to_watch.js");
|
||||
assert_contains!(stderr_lines.next().unwrap(), "target.js");
|
||||
|
|
1
cli/tests/testdata/041_dyn_import_eval.out
vendored
1
cli/tests/testdata/041_dyn_import_eval.out
vendored
|
@ -1 +1,2 @@
|
|||
[WILDCARD]
|
||||
Module { isMod4: true }
|
||||
|
|
72
cli/tests/testdata/055_info_file_json.out
vendored
72
cli/tests/testdata/055_info_file_json.out
vendored
|
@ -1,53 +1,91 @@
|
|||
{
|
||||
"root": "file://[WILDCARD]/005_more_imports.ts",
|
||||
"roots": [
|
||||
"file://[WILDCARD]/005_more_imports.ts"
|
||||
],
|
||||
"modules": [
|
||||
{
|
||||
"specifier": "file://[WILDCARD]/005_more_imports.ts",
|
||||
"dependencies": [
|
||||
{
|
||||
"specifier": "./subdir/mod1.ts",
|
||||
"code": "file://[WILDCARD]/subdir/mod1.ts"
|
||||
"code": {
|
||||
"specifier": "file://[WILDCARD]/subdir/mod1.ts",
|
||||
"span": {
|
||||
"start": {
|
||||
"line": 0,
|
||||
"character": 52
|
||||
},
|
||||
"end": {
|
||||
"line": 0,
|
||||
"character": 70
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"size": 211,
|
||||
"mediaType": "TypeScript",
|
||||
"local": "[WILDCARD]005_more_imports.ts",
|
||||
[WILDCARD]
|
||||
"mediaType": "TypeScript",
|
||||
[WILDCARD]
|
||||
"specifier": "file://[WILDCARD]/005_more_imports.ts"
|
||||
},
|
||||
{
|
||||
"specifier": "file://[WILDCARD]/subdir/mod1.ts",
|
||||
"dependencies": [
|
||||
{
|
||||
"specifier": "./subdir2/mod2.ts",
|
||||
"code": "file://[WILDCARD]/subdir/subdir2/mod2.ts"
|
||||
"code": {
|
||||
"specifier": "file://[WILDCARD]/subdir/subdir2/mod2.ts",
|
||||
"span": {
|
||||
"start": {
|
||||
"line": 0,
|
||||
"character": 40
|
||||
},
|
||||
"end": {
|
||||
"line": 0,
|
||||
"character": 59
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"size": 308,
|
||||
"mediaType": "TypeScript",
|
||||
"local": "[WILDCARD]mod1.ts",
|
||||
[WILDCARD]
|
||||
"mediaType": "TypeScript",
|
||||
[WILDCARD]
|
||||
"specifier": "file://[WILDCARD]/subdir/mod1.ts"
|
||||
},
|
||||
{
|
||||
"specifier": "file://[WILDCARD]/subdir/print_hello.ts",
|
||||
"dependencies": [],
|
||||
"size": 57,
|
||||
"mediaType": "TypeScript",
|
||||
"local": "[WILDCARD]print_hello.ts",
|
||||
[WILDCARD]
|
||||
"mediaType": "TypeScript",
|
||||
[WILDCARD]
|
||||
"specifier": "file://[WILDCARD]/subdir/print_hello.ts"
|
||||
},
|
||||
{
|
||||
"specifier": "file://[WILDCARD]/subdir/subdir2/mod2.ts",
|
||||
"dependencies": [
|
||||
{
|
||||
"specifier": "../print_hello.ts",
|
||||
"code": "file://[WILDCARD]/subdir/print_hello.ts"
|
||||
"code": {
|
||||
"specifier": "file://[WILDCARD]/subdir/print_hello.ts",
|
||||
"span": {
|
||||
"start": {
|
||||
"line": 0,
|
||||
"character": 27
|
||||
},
|
||||
"end": {
|
||||
"line": 0,
|
||||
"character": 46
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"size": 157,
|
||||
"mediaType": "TypeScript",
|
||||
"local": "[WILDCARD]mod2.ts",
|
||||
[WILDCARD]
|
||||
"mediaType": "TypeScript",
|
||||
[WILDCARD]
|
||||
"specifier": "file://[WILDCARD]/subdir/subdir2/mod2.ts"
|
||||
}
|
||||
],
|
||||
"size": 733
|
||||
"redirects": {}
|
||||
}
|
||||
|
|
144
cli/tests/testdata/076_info_json_deps_order.out
vendored
144
cli/tests/testdata/076_info_json_deps_order.out
vendored
|
@ -1,78 +1,164 @@
|
|||
{
|
||||
"root": "file://[WILDCARD]/076_info_json_deps_order.ts",
|
||||
"roots": [
|
||||
"file://[WILDCARD]/076_info_json_deps_order.ts"
|
||||
],
|
||||
"modules": [
|
||||
{
|
||||
"specifier": "file://[WILDCARD]/076_info_json_deps_order.ts",
|
||||
"dependencies": [
|
||||
{
|
||||
"specifier": "./recursive_imports/A.ts",
|
||||
"code": "file://[WILDCARD]/recursive_imports/A.ts"
|
||||
"code": {
|
||||
"specifier": "file://[WILDCARD]/recursive_imports/A.ts",
|
||||
"span": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"character": 18
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"character": 44
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"size": 81,
|
||||
"mediaType": "TypeScript",
|
||||
"local": "[WILDCARD]076_info_json_deps_order.ts",
|
||||
"checksum": "5dd40fe33e5924cca513489ce568e86c9b9fe318a87975403c8923629018680d"
|
||||
[WILDCARD]
|
||||
"mediaType": "TypeScript",
|
||||
[WILDCARD]
|
||||
"specifier": "file://[WILDCARD]/076_info_json_deps_order.ts"
|
||||
},
|
||||
{
|
||||
"specifier": "file://[WILDCARD]/recursive_imports/A.ts",
|
||||
"dependencies": [
|
||||
{
|
||||
"specifier": "./B.ts",
|
||||
"code": "file://[WILDCARD]/recursive_imports/B.ts"
|
||||
"code": {
|
||||
"specifier": "file://[WILDCARD]/recursive_imports/B.ts",
|
||||
"span": {
|
||||
"start": {
|
||||
"line": 0,
|
||||
"character": 18
|
||||
},
|
||||
"end": {
|
||||
"line": 0,
|
||||
"character": 26
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"specifier": "./common.ts",
|
||||
"code": "file://[WILDCARD]/recursive_imports/common.ts"
|
||||
"code": {
|
||||
"specifier": "file://[WILDCARD]/recursive_imports/common.ts",
|
||||
"span": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"character": 22
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"character": 35
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"size": 108,
|
||||
"mediaType": "TypeScript",
|
||||
"local": "[WILDCARD]A.ts",
|
||||
"checksum": "3b45a105d892584298490cb73372b2cac57118e1e42a677a1d5cacea704d8d3a"
|
||||
[WILDCARD]
|
||||
"mediaType": "TypeScript",
|
||||
[WILDCARD]
|
||||
"specifier": "file://[WILDCARD]/recursive_imports/A.ts"
|
||||
},
|
||||
{
|
||||
"specifier": "file://[WILDCARD]/recursive_imports/B.ts",
|
||||
"dependencies": [
|
||||
{
|
||||
"specifier": "./C.ts",
|
||||
"code": "file://[WILDCARD]/recursive_imports/C.ts"
|
||||
"code": {
|
||||
"specifier": "file://[WILDCARD]/recursive_imports/C.ts",
|
||||
"span": {
|
||||
"start": {
|
||||
"line": 0,
|
||||
"character": 18
|
||||
},
|
||||
"end": {
|
||||
"line": 0,
|
||||
"character": 26
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"specifier": "./common.ts",
|
||||
"code": "file://[WILDCARD]/recursive_imports/common.ts"
|
||||
"code": {
|
||||
"specifier": "file://[WILDCARD]/recursive_imports/common.ts",
|
||||
"span": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"character": 22
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"character": 35
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"size": 108,
|
||||
"mediaType": "TypeScript",
|
||||
"local": "[WILDCARD]B.ts",
|
||||
"checksum": "b12b0437ef9a91c4a4b1f66e8e4339f986b60bd8134031ccb296ce49df15b54e"
|
||||
[WILDCARD]
|
||||
"mediaType": "TypeScript",
|
||||
[WILDCARD]
|
||||
"specifier": "file://[WILDCARD]/recursive_imports/B.ts"
|
||||
},
|
||||
{
|
||||
"specifier": "file://[WILDCARD]/recursive_imports/C.ts",
|
||||
"dependencies": [
|
||||
{
|
||||
"specifier": "./A.ts",
|
||||
"code": "file://[WILDCARD]/recursive_imports/A.ts"
|
||||
"code": {
|
||||
"specifier": "file://[WILDCARD]/recursive_imports/A.ts",
|
||||
"span": {
|
||||
"start": {
|
||||
"line": 0,
|
||||
"character": 18
|
||||
},
|
||||
"end": {
|
||||
"line": 0,
|
||||
"character": 26
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"specifier": "./common.ts",
|
||||
"code": "file://[WILDCARD]/recursive_imports/common.ts"
|
||||
"code": {
|
||||
"specifier": "file://[WILDCARD]/recursive_imports/common.ts",
|
||||
"span": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"character": 22
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"character": 35
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"size": 126,
|
||||
"mediaType": "TypeScript",
|
||||
"local": "[WILDCARD]C.ts",
|
||||
"checksum": "605875a410741bfaeeade28cbccf45f219ad99d987ea695e35eda75d2c53a658"
|
||||
[WILDCARD]
|
||||
"mediaType": "TypeScript",
|
||||
[WILDCARD]
|
||||
"specifier": "file://[WILDCARD]/recursive_imports/C.ts"
|
||||
},
|
||||
{
|
||||
"specifier": "file://[WILDCARD]/recursive_imports/common.ts",
|
||||
"dependencies": [],
|
||||
"size": 28,
|
||||
"mediaType": "TypeScript",
|
||||
"local": "[WILDCARD]common.ts",
|
||||
"checksum": "c70025f0b936c02980c3be1fbd78f6f36b6241927c44ea67580821a6e664d8b3"
|
||||
[WILDCARD]
|
||||
"mediaType": "TypeScript",
|
||||
[WILDCARD]
|
||||
"specifier": "file://[WILDCARD]/recursive_imports/common.ts"
|
||||
}
|
||||
],
|
||||
"size": 451
|
||||
"redirects": {}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
[WILDCARD]error: Uncaught (in promise) TypeError: Relative import path "unmapped" not prefixed with / or ./ or ../ and not in import map from "[WILDCARD]"
|
||||
[WILDCARD]
|
||||
error: Uncaught (in promise) TypeError: Relative import path "unmapped" not prefixed with / or ./ or ../ and not in import map from "file://[WILDCARD]/092_import_map_unmapped_bare_specifier.ts"
|
||||
at file://[WILDCARD]/092_import_map_unmapped_bare_specifier.ts:1:14
|
||||
|
||||
await import("unmapped");
|
||||
^
|
||||
at [WILDCARD]
|
||||
at async file://[WILDCARD]/092_import_map_unmapped_bare_specifier.ts:1:1
|
||||
|
|
1
cli/tests/testdata/cafile_ts_fetch.ts.out
vendored
1
cli/tests/testdata/cafile_ts_fetch.ts.out
vendored
|
@ -1 +1,2 @@
|
|||
[WILDCARD]
|
||||
Hello
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
DANGER: TLS certificate validation is disabled for all hostnames
|
||||
[WILDCARD]
|
||||
Hello
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[WILDCARD]
|
||||
Some Node built-ins were not added to the import map:
|
||||
- "fs/promises" already exists and is mapped to "[WILDCARD]non_existent_file.js"
|
||||
- "fs/promises" already exists and is mapped to "file://[WILDCARD]/non_existent_file.js"
|
||||
If you want to use Node built-ins provided by Deno remove listed specifiers from "imports" mapping in the import map file.
|
||||
[WILDCARD]
|
||||
error: Cannot resolve module [WILDCARD]
|
||||
error: Cannot load module "file://[WILDCARD]/non_existent_file.js".
|
||||
at file://[WILDCARD]/fs_promises.js:1:16
|
||||
|
|
30
cli/tests/testdata/compiler_api_test.ts
vendored
30
cli/tests/testdata/compiler_api_test.ts
vendored
|
@ -313,10 +313,9 @@ Deno.test({
|
|||
Deno.test({
|
||||
name: "Deno.emit() - invalid syntax does not panic",
|
||||
async fn() {
|
||||
await assertThrowsAsync(async () => {
|
||||
await Deno.emit("/main.js", {
|
||||
sources: {
|
||||
"/main.js": `
|
||||
const { diagnostics } = await Deno.emit("/main.js", {
|
||||
sources: {
|
||||
"/main.js": `
|
||||
export class Foo {
|
||||
constructor() {
|
||||
console.log("foo");
|
||||
|
@ -325,9 +324,14 @@ Deno.test({
|
|||
console.log("bar");
|
||||
}
|
||||
}`,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
assertEquals(diagnostics.length, 1);
|
||||
assert(
|
||||
diagnostics[0].messageText!.startsWith(
|
||||
"The module's source code could not be parsed: Unexpected token `get`. Expected * for generator, private key, identifier or async at file:",
|
||||
),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -356,12 +360,10 @@ Deno.test({
|
|||
Deno.test({
|
||||
name: "Deno.emit() - Unknown media type does not panic",
|
||||
async fn() {
|
||||
await assertThrowsAsync(async () => {
|
||||
await Deno.emit("https://example.com/foo", {
|
||||
sources: {
|
||||
"https://example.com/foo": `let foo: string = "foo";`,
|
||||
},
|
||||
});
|
||||
await Deno.emit("https://example.com/foo", {
|
||||
sources: {
|
||||
"https://example.com/foo": `let foo: string = "foo";`,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
@ -487,7 +489,7 @@ Deno.test({
|
|||
code: 900001,
|
||||
start: null,
|
||||
end: null,
|
||||
messageText: "Unable to find specifier in sources: file:///b.ts",
|
||||
messageText: 'Cannot load module "file:///b.ts".',
|
||||
messageChain: null,
|
||||
source: null,
|
||||
sourceLine: null,
|
||||
|
@ -497,7 +499,7 @@ Deno.test({
|
|||
]);
|
||||
assert(
|
||||
Deno.formatDiagnostics(diagnostics).includes(
|
||||
"Unable to find specifier in sources: file:///b.ts",
|
||||
'Cannot load module "file:///b.ts".',
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
1
cli/tests/testdata/config.ts.out
vendored
1
cli/tests/testdata/config.ts.out
vendored
|
@ -1,6 +1,7 @@
|
|||
[WILDCARD]Unsupported compiler options in "[WILDCARD]config.tsconfig.json".
|
||||
The following options were ignored:
|
||||
module, target
|
||||
[WILDCARD]
|
||||
error: TS1219 [ERROR]: Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option in your 'tsconfig' or 'jsconfig' to remove this warning.
|
||||
a() {
|
||||
^
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
error: Modules imported via https are not allowed to import http modules.
|
||||
Importing: http://localhost:4545/001_hello.js
|
||||
at https://localhost:5545/disallow_http_from_https.js:2:0
|
||||
at https://localhost:5545/disallow_http_from_https.js:2:8
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
error: Modules imported via https are not allowed to import http modules.
|
||||
Importing: http://localhost:4545/001_hello.js
|
||||
at https://localhost:5545/disallow_http_from_https.ts:2:0
|
||||
at https://localhost:5545/disallow_http_from_https.ts:2:8
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: Uncaught (in promise) TypeError: Requires read access to "[WILDCARD]local_file.ts", run again with the --allow-read flag
|
||||
at blob:null/[WILDCARD]:1:0
|
||||
at blob:null/[WILDCARD]:1:8
|
||||
await import(URL.createObjectURL(blob));
|
||||
^
|
||||
at async file:///[WILDCARD]/dynamic_import/permissions_blob_local.ts:6:1
|
||||
at async file://[WILDCARD]/permissions_blob_local.ts:6:1
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: Uncaught (in promise) TypeError: Requires net access to "example.com", run again with the --allow-net flag
|
||||
at blob:null/[WILDCARD]:1:0
|
||||
at blob:null/[WILDCARD]:1:8
|
||||
await import(URL.createObjectURL(blob));
|
||||
^
|
||||
at async file:///[WILDCARD]/dynamic_import/permissions_blob_remote.ts:4:1
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: Uncaught (in promise) TypeError: Requires read access to "[WILDCARD]local_file.ts", run again with the --allow-read flag
|
||||
at data:application/javascript;base64,[WILDCARD]:1:0
|
||||
at data:application/javascript;base64,[WILDCARD]:1:8
|
||||
await import(`data:application/javascript;base64,${btoa(code)}`);
|
||||
^
|
||||
at async file:///[WILDCARD]/dynamic_import/permissions_data_local.ts:5:1
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: Uncaught (in promise) TypeError: Requires net access to "example.com", run again with the --allow-net flag
|
||||
at data:application/javascript;base64,aW1wb3J0ICJodHRwczovL2V4YW1wbGUuY29tL3NvbWUvZmlsZS50cyI7:1:0
|
||||
at data:application/javascript;base64,aW1wb3J0ICJodHRwczovL2V4YW1wbGUuY29tL3NvbWUvZmlsZS50cyI7:1:8
|
||||
await import(`data:application/javascript;base64,${btoa(code)}`);
|
||||
^
|
||||
at async file:///[WILDCARD]/dynamic_import/permissions_data_remote.ts:3:1
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: Uncaught (in promise) TypeError: Requires net access to "example.com", run again with the --allow-net flag
|
||||
at http://localhost:4545/dynamic_import/static_remote.ts:2:0
|
||||
at http://localhost:4545/dynamic_import/static_remote.ts:2:8
|
||||
await import(
|
||||
^
|
||||
at async file:///[WILDCARD]/dynamic_import/permissions_remote_remote.ts:1:1
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
[WILDCARD]error: Cannot resolve module "file:///[WILDCARD]/bad-module.ts" from "file:///[WILDCARD]/error_004_missing_module.ts".
|
||||
at file:///[WILDCARD]/error_004_missing_module.ts:1:0
|
||||
[WILDCARD]error: Cannot load module "file:///[WILDCARD]/bad-module.ts".
|
||||
at file:///[WILDCARD]/error_004_missing_module.ts:1:28
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: Uncaught (in promise) TypeError: Cannot resolve module "[WILDCARD]/bad-module.ts".
|
||||
error: Uncaught (in promise) TypeError: Cannot load module "[WILDCARD]/bad-module.ts".
|
||||
const _badModule = await import("./bad-module.ts");
|
||||
^
|
||||
at async file://[WILDCARD]/error_005_missing_dynamic_import.ts:2:22
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
[WILDCARD]error: Cannot resolve module "[WILDCARD]/non-existent" from "[WILDCARD]/error_006_import_ext_failure.ts".
|
||||
at file:///[WILDCARD]/error_006_import_ext_failure.ts:1:0
|
||||
[WILDCARD]error: Cannot load module "[WILDCARD]/non-existent".
|
||||
at file:///[WILDCARD]/error_006_import_ext_failure.ts:1:8
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
[WILDCARD]error: Relative import path "bad-module.ts" not prefixed with / or ./ or ../ from "[WILDCARD]/error_011_bad_module_specifier.ts"
|
||||
at [WILDCARD]/error_011_bad_module_specifier.ts:1:28
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
Check [WILDCARD]error_012_bad_dynamic_import_specifier.ts
|
||||
error: Uncaught (in promise) TypeError: Relative import path "bad-module.ts" not prefixed with / or ./ or ../ from "[WILDCARD]/error_012_bad_dynamic_import_specifier.ts"
|
||||
at [WILDCARD]/error_012_bad_dynamic_import_specifier.ts:2:35
|
||||
|
||||
const _badModule = await import("bad-module.ts");
|
||||
^
|
||||
at async file:///[WILDCARD]/error_012_bad_dynamic_import_specifier.ts:2:22
|
||||
at async [WILDCARD]/error_012_bad_dynamic_import_specifier.ts:2:22
|
||||
|
|
|
@ -1 +1 @@
|
|||
error: Cannot resolve module "[WILDCARD]missing_file_name".
|
||||
error: Cannot load module "[WILDCARD]missing_file_name".
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
Caught direct dynamic import error.
|
||||
TypeError: Relative import path "does not exist" not prefixed with / or ./ or ../ from "[WILDCARD]/error_014_catch_dynamic_import_error.js"
|
||||
at async file:///[WILDCARD]/error_014_catch_dynamic_import_error.js:3:5
|
||||
at [WILDCARD]/error_014_catch_dynamic_import_error.js:3:18
|
||||
|
||||
at async [WILDCARD]/error_014_catch_dynamic_import_error.js:3:5
|
||||
Caught indirect direct dynamic import error.
|
||||
TypeError: Relative import path "does not exist either" not prefixed with / or ./ or ../ from "[WILDCARD]/indirect_import_error.js"
|
||||
at async file:///[WILDCARD]/error_014_catch_dynamic_import_error.js:10:5
|
||||
TypeError: Relative import path "does not exist either" not prefixed with / or ./ or ../ from "[WILDCARD]/subdir/indirect_import_error.js"
|
||||
at [WILDCARD]/subdir/indirect_import_error.js:1:15
|
||||
|
||||
at async [WILDCARD]/error_014_catch_dynamic_import_error.js:10:5
|
||||
Caught error thrown by dynamically imported module.
|
||||
Error: An error
|
||||
at file:///[WILDCARD]/subdir/throws.js:6:7
|
||||
at [WILDCARD]/subdir/throws.js:6:7
|
||||
Caught error thrown indirectly by dynamically imported module.
|
||||
Error: An error
|
||||
at file:///[WILDCARD]/subdir/throws.js:6:7
|
||||
at [WILDCARD]/subdir/throws.js:6:7
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
error: Uncaught (in promise) TypeError: Requires net access to "localhost:4545", run again with the --allow-net flag
|
||||
at file://[WILDCARD]/error_015_dynamic_import_permissions.js:2:16
|
||||
await import("http://localhost:4545/subdir/mod4.js");
|
||||
^
|
||||
at async file:///[WILDCARD]/error_015_dynamic_import_permissions.js:2:3
|
||||
at async file://[WILDCARD]/error_015_dynamic_import_permissions.js:2:3
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
[WILDCARD]
|
||||
error: Remote modules are not allowed to import local modules. Consider using a dynamic import instead.
|
||||
error: Uncaught (in promise) TypeError: Remote modules are not allowed to import local modules. Consider using a dynamic import instead.
|
||||
Importing: file:///c:/etc/passwd
|
||||
at http://localhost:4545/subdir/evil_remote_import.js:3:0
|
||||
at http://localhost:4545/subdir/evil_remote_import.js:3:15
|
||||
|
||||
await import("http://localhost:4545/subdir/evil_remote_import.js");
|
||||
^
|
||||
at async file://[WILDCARD]/error_016_dynamic_import_permissions2.js:4:3
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[WILDCARD]
|
||||
error: Remote modules are not allowed to import local modules. Consider using a dynamic import instead.
|
||||
error: Remote modules are not allowed to import local modules. Consider using a dynamic import instead.
|
||||
Importing: file:///some/dir/file.js
|
||||
at http://localhost:4545/error_local_static_import_from_remote.js:1:0
|
||||
at http://localhost:4545/error_local_static_import_from_remote.js:1:8
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[WILDCARD]
|
||||
error: Remote modules are not allowed to import local modules. Consider using a dynamic import instead.
|
||||
error: Remote modules are not allowed to import local modules. Consider using a dynamic import instead.
|
||||
Importing: file:///some/dir/file.ts
|
||||
at http://localhost:4545/error_local_static_import_from_remote.ts:1:0
|
||||
at http://localhost:4545/error_local_static_import_from_remote.ts:1:8
|
||||
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
error: Cannot resolve module "[WILDCARD]/does_not_exist.js" from "[WILDCARD]/error_missing_module_named_import.ts".
|
||||
at [WILDCARD]/error_missing_module_named_import.ts:1:0
|
||||
[WILDCARD]
|
||||
error: Cannot load module "file://[WILDCARD]/does_not_exist.js".
|
||||
at file://[WILDCARD]/error_missing_module_named_import.ts:1:19
|
||||
|
|
2
cli/tests/testdata/error_syntax.js.out
vendored
2
cli/tests/testdata/error_syntax.js.out
vendored
|
@ -1 +1 @@
|
|||
error: Expected ,, got following at [WILDCARD]/error_syntax.js:3:6
|
||||
error: The module's source code could not be parsed: Expected ',', got 'following' at [WILDCARD]/error_syntax.js:3:6
|
||||
|
|
|
@ -1 +1 @@
|
|||
error: Unexpected eof at [WILDCARD]/error_syntax_empty_trailing_line.mjs:2:22
|
||||
error: The module's source code could not be parsed: Unexpected eof at [WILDCARD]/error_syntax_empty_trailing_line.mjs:2:22
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
error: Uncaught (in promise) TypeError: invalid URL: relative URL with a cannot-be-a-base base
|
||||
at blob:null/[WILDCARD]:1:19
|
||||
|
||||
const a = await import(url);
|
||||
^
|
||||
at async file://[WILDCARD]/import_blob_url_import_relative.ts:6:11
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
error: invalid URL: relative URL with a cannot-be-a-base base
|
||||
at data:application/javascript;base64,ZXhwb3J0IHsgYSB9IGZyb20gIi4vYS50cyI7Cg==:1:19
|
||||
|
||||
Caused by:
|
||||
relative URL with a cannot-be-a-base base
|
||||
|
|
4
cli/tests/testdata/info_missing_module.out
vendored
4
cli/tests/testdata/info_missing_module.out
vendored
|
@ -1,6 +1,6 @@
|
|||
local: [WILDCARD]error_009_missing_js_module.js
|
||||
type: JavaScript
|
||||
dependencies: 1 unique (total 26B)
|
||||
dependencies: 0 unique (total 26B)
|
||||
|
||||
file://[WILDCARD]/error_009_missing_js_module.js (26B)
|
||||
└── file://[WILDCARD]/bad-module.js (error)
|
||||
└── file://[WILDCARD]/bad-module.js (missing)
|
||||
|
|
2
cli/tests/testdata/lint/expected_json.out
vendored
2
cli/tests/testdata/lint/expected_json.out
vendored
|
@ -58,7 +58,7 @@
|
|||
"errors": [
|
||||
{
|
||||
"file_path": "[WILDCARD]malformed.js",
|
||||
"message": "Expected }, got <eof> at [WILDCARD]malformed.js:4:16"
|
||||
"message": "Expected '}', got '<eof>' at [WILDCARD]malformed.js:4:16"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
DANGER: TLS certificate validation is disabled for: deno.land
|
||||
error: error sending request for url (https://localhost:5545/subdir/mod2.ts): error trying to connect: invalid certificate: UnknownIssuer
|
||||
at [WILDCARD]/cafile_url_imports.ts:1:0
|
||||
at file://[WILDCARD]/cafile_url_imports.ts:1:28
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[WILDCARD]
|
||||
The source code is invalid, as it does not match the expected hash in the lock file.
|
||||
error: The source code is invalid, as it does not match the expected hash in the lock file.
|
||||
Specifier: http://127.0.0.1:4545/subdir/subdir2/mod2.ts
|
||||
Lock file: lock_check_err_with_bundle.json
|
||||
|
|
2
cli/tests/testdata/lock_dynamic_imports.out
vendored
2
cli/tests/testdata/lock_dynamic_imports.out
vendored
|
@ -1,4 +1,4 @@
|
|||
[WILDCARD]
|
||||
The source code is invalid, as it does not match the expected hash in the lock file.
|
||||
error: The source code is invalid, as it does not match the expected hash in the lock file.
|
||||
Specifier: http://127.0.0.1:4545/subdir/subdir2/mod2.ts
|
||||
Lock file: lock_dynamic_imports.json
|
||||
|
|
2
cli/tests/testdata/swc_syntax_error.ts.out
vendored
2
cli/tests/testdata/swc_syntax_error.ts.out
vendored
|
@ -1 +1 @@
|
|||
error: Unexpected token `}`. Expected an identifier, void, yield, null, await, break, a string literal, a numeric literal, true, false, `, -, import, this, typeof, {, [, ( at [WILDCARD]syntax_error.ts:4:1
|
||||
error: The module's source code could not be parsed: Unexpected token `}`. Expected an identifier, void, yield, null, await, break, a string literal, a numeric literal, true, false, `, -, import, this, typeof, {, [, ( at [WILDCARD]syntax_error.ts:4:1
|
||||
|
|
2
cli/tests/testdata/ts_decorators_bundle.out
vendored
2
cli/tests/testdata/ts_decorators_bundle.out
vendored
|
@ -1,5 +1,5 @@
|
|||
[WILDCARD]
|
||||
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
|
||||
[WILDCARD]
|
||||
new SomeClass().test();
|
||||
new SomeClass[WILDCARD].test();
|
||||
[WILDCARD]
|
|
@ -1,4 +1,4 @@
|
|||
Check [WILDCARD]ts_type_only_import.ts
|
||||
warning: Compiled module not found "[WILDCARD]ts_type_only_import.d.ts"
|
||||
From: [WILDCARD]ts_type_only_import.ts
|
||||
If the source module contains only types, use `import type` and `export type` to import it instead.
|
||||
Check file://[WILDCARD]/ts_type_only_import.ts
|
||||
warning: Cannot load module "file://[WILDCARD]/ts_type_only_import.d.ts".
|
||||
at file://[WILDCARD]/ts_type_only_import.ts:1:15
|
||||
If the source module contains only types, use `import type` and `export type` to import it instead.
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
[WILDCARD]error: Uncaught (in worker "") Cannot resolve module "file:///[WILDCARD]/workers/doesnt_exist.js".
|
||||
[WILDCARD]error: Uncaught (in worker "") Cannot load module "file:///[WILDCARD]/workers/doesnt_exist.js".
|
||||
error: Uncaught (in promise) Error: Unhandled error event in child worker.
|
||||
at Worker.#pollControl ([WILDCARD])
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: Uncaught (in worker "") Requires read access to "[WILDCARD]local_file.ts", run again with the --allow-read flag
|
||||
at blob:null/[WILDCARD]:1:0
|
||||
at blob:null/[WILDCARD]:1:8
|
||||
error: Uncaught (in promise) Error: Unhandled error event in child worker.
|
||||
at Worker.#pollControl ([WILDCARD])
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag
|
||||
at blob:null/[WILDCARD]:1:0
|
||||
at blob:null/[WILDCARD]:1:8
|
||||
error: Uncaught (in promise) Error: Unhandled error event in child worker.
|
||||
at Worker.#pollControl ([WILDCARD])
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: Uncaught (in worker "") Requires read access to "[WILDCARD]local_file.ts", run again with the --allow-read flag
|
||||
at data:application/javascript;base64,[WILDCARD]:1:0
|
||||
at data:application/javascript;base64,[WILDCARD]:1:8
|
||||
error: Uncaught (in promise) Error: Unhandled error event in child worker.
|
||||
at Worker.#pollControl ([WILDCARD])
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag
|
||||
at data:application/javascript;base64,aW1wb3J0ICJodHRwczovL2V4YW1wbGUuY29tL3NvbWUvZmlsZS50cyI7:1:0
|
||||
at data:application/javascript;base64,aW1wb3J0ICJodHRwczovL2V4YW1wbGUuY29tL3NvbWUvZmlsZS50cyI7:1:8
|
||||
error: Uncaught (in promise) Error: Unhandled error event in child worker.
|
||||
at Worker.#pollControl ([WILDCARD])
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
error: Uncaught (in worker "") (in promise) TypeError: Requires net access to "example.com", run again with the --allow-net flag
|
||||
at http://localhost:4545/workers/dynamic_remote.ts:2:14
|
||||
await import("https://example.com/some/file.ts");
|
||||
^
|
||||
at async http://localhost:4545/workers/dynamic_remote.ts:2:1
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag
|
||||
at http://localhost:4545/workers/static_remote.ts:2:0
|
||||
at http://localhost:4545/workers/static_remote.ts:2:8
|
||||
error: Uncaught (in promise) Error: Unhandled error event in child worker.
|
||||
at Worker.#pollControl ([WILDCARD])
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::colors;
|
||||
use crate::emit;
|
||||
use crate::flags::Flags;
|
||||
use crate::fs_util::collect_files;
|
||||
use crate::module_graph::TypeLib;
|
||||
use crate::proc_state::ProcState;
|
||||
use crate::source_maps::SourceMapGetter;
|
||||
|
||||
use deno_ast::swc::common::Span;
|
||||
use deno_ast::MediaType;
|
||||
use deno_core::error::AnyError;
|
||||
|
@ -687,16 +688,15 @@ pub async fn cover_files(
|
|||
let module_specifier =
|
||||
deno_core::resolve_url_or_path(&script_coverage.url)?;
|
||||
ps.prepare_module_load(
|
||||
module_specifier.clone(),
|
||||
TypeLib::UnstableDenoWindow,
|
||||
Permissions::allow_all(),
|
||||
Permissions::allow_all(),
|
||||
vec![module_specifier.clone()],
|
||||
false,
|
||||
ps.maybe_import_map.clone(),
|
||||
emit::TypeLib::UnstableDenoWindow,
|
||||
Permissions::allow_all(),
|
||||
Permissions::allow_all(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let module_source = ps.load(module_specifier.clone(), None)?;
|
||||
let module_source = ps.load(module_specifier.clone(), None, false)?;
|
||||
let script_source = &module_source.code;
|
||||
|
||||
let maybe_source_map = ps.get_source_map(&script_coverage.url);
|
||||
|
|
|
@ -106,7 +106,9 @@ pub async fn print_docs(
|
|||
let source_file_specifier =
|
||||
ModuleSpecifier::parse("deno://lib.deno.d.ts").unwrap();
|
||||
let graph = create_graph(
|
||||
source_file_specifier.clone(),
|
||||
vec![source_file_specifier.clone()],
|
||||
false,
|
||||
None,
|
||||
&mut loader,
|
||||
None,
|
||||
None,
|
||||
|
@ -142,7 +144,9 @@ pub async fn print_docs(
|
|||
import_map: ps.maybe_import_map.clone(),
|
||||
};
|
||||
let graph = create_graph(
|
||||
root_specifier.clone(),
|
||||
vec![root_specifier.clone()],
|
||||
false,
|
||||
None,
|
||||
&mut loader,
|
||||
Some(&resolver),
|
||||
None,
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::ast::Location;
|
||||
use crate::cache;
|
||||
use crate::cache::CacherLoader;
|
||||
use crate::colors;
|
||||
use crate::create_main_worker;
|
||||
use crate::emit;
|
||||
use crate::file_fetcher::File;
|
||||
use crate::file_watcher;
|
||||
use crate::file_watcher::ResolutionResult;
|
||||
|
@ -11,15 +14,13 @@ use crate::fs_util::collect_specifiers;
|
|||
use crate::fs_util::is_supported_test_ext;
|
||||
use crate::fs_util::is_supported_test_path;
|
||||
use crate::located_script_name;
|
||||
use crate::module_graph;
|
||||
use crate::module_graph::GraphBuilder;
|
||||
use crate::module_graph::Module;
|
||||
use crate::module_graph::TypeLib;
|
||||
use crate::lockfile;
|
||||
use crate::ops;
|
||||
use crate::proc_state::ProcState;
|
||||
use crate::resolver::ImportMapResolver;
|
||||
use crate::tokio_util;
|
||||
use crate::tools::coverage::CoverageCollector;
|
||||
use crate::FetchHandler;
|
||||
|
||||
use deno_ast::swc::common::comments::CommentKind;
|
||||
use deno_ast::MediaType;
|
||||
use deno_core::error::generic_error;
|
||||
|
@ -28,7 +29,6 @@ use deno_core::futures::future;
|
|||
use deno_core::futures::stream;
|
||||
use deno_core::futures::FutureExt;
|
||||
use deno_core::futures::StreamExt;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::JsRuntime;
|
||||
use deno_core::ModuleSpecifier;
|
||||
|
@ -500,7 +500,7 @@ async fn check_specifiers(
|
|||
ps: ProcState,
|
||||
permissions: Permissions,
|
||||
specifiers: Vec<(ModuleSpecifier, TestMode)>,
|
||||
lib: TypeLib,
|
||||
lib: emit::TypeLib,
|
||||
) -> Result<(), AnyError> {
|
||||
let inline_files = fetch_inline_files(
|
||||
ps.clone(),
|
||||
|
@ -527,12 +527,12 @@ async fn check_specifiers(
|
|||
ps.file_fetcher.insert_cached(file);
|
||||
}
|
||||
|
||||
ps.prepare_module_graph(
|
||||
ps.prepare_module_load(
|
||||
specifiers,
|
||||
false,
|
||||
lib.clone(),
|
||||
Permissions::allow_all(),
|
||||
permissions.clone(),
|
||||
ps.maybe_import_map.clone(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
@ -548,12 +548,12 @@ async fn check_specifiers(
|
|||
})
|
||||
.collect();
|
||||
|
||||
ps.prepare_module_graph(
|
||||
ps.prepare_module_load(
|
||||
module_specifiers,
|
||||
false,
|
||||
lib,
|
||||
Permissions::allow_all(),
|
||||
permissions,
|
||||
ps.maybe_import_map.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
@ -743,11 +743,14 @@ fn collect_specifiers_with_test_mode(
|
|||
Ok(specifiers_with_mode)
|
||||
}
|
||||
|
||||
/// Collects module and document specifiers with test modes via `collect_specifiers_with_test_mode`
|
||||
/// which are then pre-fetched and adjusted based on the media type.
|
||||
/// Collects module and document specifiers with test modes via
|
||||
/// `collect_specifiers_with_test_mode` which are then pre-fetched and adjusted
|
||||
/// based on the media type.
|
||||
///
|
||||
/// Specifiers that do not have a known media type that can be executed as a module are marked as
|
||||
/// `TestMode::Documentation`.
|
||||
/// Specifiers that do not have a known media type that can be executed as a
|
||||
/// module are marked as `TestMode::Documentation`. Type definition files
|
||||
/// cannot be run, and therefore need to be marked as `TestMode::Documentation`
|
||||
/// as well.
|
||||
async fn fetch_specifiers_with_test_mode(
|
||||
ps: ProcState,
|
||||
include: Vec<String>,
|
||||
|
@ -762,7 +765,9 @@ async fn fetch_specifiers_with_test_mode(
|
|||
.fetch(specifier, &mut Permissions::allow_all())
|
||||
.await?;
|
||||
|
||||
if file.media_type == MediaType::Unknown {
|
||||
if file.media_type == MediaType::Unknown
|
||||
|| file.media_type == MediaType::Dts
|
||||
{
|
||||
*mode = TestMode::Documentation
|
||||
}
|
||||
}
|
||||
|
@ -798,9 +803,9 @@ pub async fn run_tests(
|
|||
}
|
||||
|
||||
let lib = if flags.unstable {
|
||||
TypeLib::UnstableDenoWindow
|
||||
emit::TypeLib::UnstableDenoWindow
|
||||
} else {
|
||||
TypeLib::DenoWindow
|
||||
emit::TypeLib::DenoWindow
|
||||
};
|
||||
|
||||
check_specifiers(
|
||||
|
@ -845,26 +850,33 @@ pub async fn run_tests_with_watch(
|
|||
let permissions = Permissions::from_options(&flags.clone().into());
|
||||
|
||||
let lib = if flags.unstable {
|
||||
TypeLib::UnstableDenoWindow
|
||||
emit::TypeLib::UnstableDenoWindow
|
||||
} else {
|
||||
TypeLib::DenoWindow
|
||||
emit::TypeLib::DenoWindow
|
||||
};
|
||||
|
||||
let handler = Arc::new(Mutex::new(FetchHandler::new(
|
||||
&ps,
|
||||
Permissions::allow_all(),
|
||||
Permissions::allow_all(),
|
||||
)?));
|
||||
|
||||
let include = include.unwrap_or_else(|| vec![".".to_string()]);
|
||||
let paths_to_watch: Vec<_> = include.iter().map(PathBuf::from).collect();
|
||||
|
||||
let resolver = |changed: Option<Vec<PathBuf>>| {
|
||||
let mut cache = cache::FetchCacher::new(
|
||||
ps.dir.gen_cache.clone(),
|
||||
ps.file_fetcher.clone(),
|
||||
Permissions::allow_all(),
|
||||
Permissions::allow_all(),
|
||||
);
|
||||
|
||||
let paths_to_watch = paths_to_watch.clone();
|
||||
let paths_to_watch_clone = paths_to_watch.clone();
|
||||
|
||||
let handler = handler.clone();
|
||||
let ps = ps.clone();
|
||||
let maybe_resolver =
|
||||
ps.maybe_import_map.as_ref().map(ImportMapResolver::new);
|
||||
let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone());
|
||||
let maybe_imports = ps
|
||||
.maybe_config_file
|
||||
.as_ref()
|
||||
.map(|cf| cf.to_maybe_imports())
|
||||
.flatten();
|
||||
let files_changed = changed.is_some();
|
||||
let include = include.clone();
|
||||
let ignore = ignore.clone();
|
||||
|
@ -886,61 +898,51 @@ pub async fn run_tests_with_watch(
|
|||
.collect()
|
||||
};
|
||||
|
||||
let mut builder = GraphBuilder::new(
|
||||
handler,
|
||||
ps.maybe_import_map.clone(),
|
||||
ps.lockfile.clone(),
|
||||
);
|
||||
for specifier in test_modules.iter() {
|
||||
builder.add(specifier, false).await?;
|
||||
}
|
||||
builder.analyze_config_file(&ps.maybe_config_file).await?;
|
||||
let graph = builder.get_graph();
|
||||
let graph = deno_graph::create_graph(
|
||||
test_modules.clone(),
|
||||
false,
|
||||
maybe_imports,
|
||||
cache.as_mut_loader(),
|
||||
maybe_resolver.as_ref().map(|r| r.as_resolver()),
|
||||
maybe_locker,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
graph.valid()?;
|
||||
|
||||
// TODO(@kitsonk) - This should be totally derivable from the graph.
|
||||
for specifier in test_modules {
|
||||
fn get_dependencies<'a>(
|
||||
graph: &'a module_graph::Graph,
|
||||
module: &'a Module,
|
||||
graph: &'a deno_graph::ModuleGraph,
|
||||
maybe_module: Option<&'a deno_graph::Module>,
|
||||
// This needs to be accessible to skip getting dependencies if they're already there,
|
||||
// otherwise this will cause a stack overflow with circular dependencies
|
||||
output: &mut HashSet<&'a ModuleSpecifier>,
|
||||
) -> Result<(), AnyError> {
|
||||
for dep in module.dependencies.values() {
|
||||
if let Some(specifier) = &dep.maybe_code {
|
||||
if !output.contains(specifier) {
|
||||
output.insert(specifier);
|
||||
) {
|
||||
if let Some(module) = maybe_module {
|
||||
for dep in module.dependencies.values() {
|
||||
if let Some(specifier) = &dep.get_code() {
|
||||
if !output.contains(specifier) {
|
||||
output.insert(specifier);
|
||||
|
||||
get_dependencies(
|
||||
graph,
|
||||
graph.get_specifier(specifier)?,
|
||||
output,
|
||||
)?;
|
||||
get_dependencies(graph, graph.get(specifier), output);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(specifier) = &dep.maybe_type {
|
||||
if !output.contains(specifier) {
|
||||
output.insert(specifier);
|
||||
if let Some(specifier) = &dep.get_type() {
|
||||
if !output.contains(specifier) {
|
||||
output.insert(specifier);
|
||||
|
||||
get_dependencies(
|
||||
graph,
|
||||
graph.get_specifier(specifier)?,
|
||||
output,
|
||||
)?;
|
||||
get_dependencies(graph, graph.get(specifier), output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// This test module and all it's dependencies
|
||||
let mut modules = HashSet::new();
|
||||
modules.insert(&specifier);
|
||||
get_dependencies(
|
||||
&graph,
|
||||
graph.get_specifier(&specifier)?,
|
||||
&mut modules,
|
||||
)?;
|
||||
get_dependencies(&graph, graph.get(&specifier), &mut modules);
|
||||
|
||||
paths_to_watch.extend(
|
||||
modules
|
||||
|
|
222
cli/tsc.rs
222
cli/tsc.rs
|
@ -2,17 +2,14 @@
|
|||
|
||||
use crate::config_file::TsConfig;
|
||||
use crate::diagnostics::Diagnostics;
|
||||
use crate::module_graph::Graph;
|
||||
use crate::module_graph::Stats;
|
||||
use crate::emit;
|
||||
|
||||
use deno_ast::MediaType;
|
||||
use deno_core::error::anyhow;
|
||||
use deno_core::error::bail;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::error::Context;
|
||||
use deno_core::located_script_name;
|
||||
use deno_core::op_sync;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::resolve_url_or_path;
|
||||
use deno_core::serde::de;
|
||||
use deno_core::serde::Deserialize;
|
||||
|
@ -25,6 +22,7 @@ use deno_core::ModuleSpecifier;
|
|||
use deno_core::OpFn;
|
||||
use deno_core::RuntimeOptions;
|
||||
use deno_core::Snapshot;
|
||||
use deno_graph::ModuleGraph;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
@ -93,7 +91,7 @@ pub fn get_asset(asset: &str) -> Option<&'static str> {
|
|||
}
|
||||
|
||||
fn get_maybe_hash(
|
||||
maybe_source: &Option<String>,
|
||||
maybe_source: Option<&String>,
|
||||
hash_data: &[Vec<u8>],
|
||||
) -> Option<String> {
|
||||
if let Some(source) = maybe_source {
|
||||
|
@ -105,30 +103,15 @@ fn get_maybe_hash(
|
|||
}
|
||||
}
|
||||
|
||||
fn hash_data_url(
|
||||
specifier: &ModuleSpecifier,
|
||||
media_type: &MediaType,
|
||||
) -> String {
|
||||
assert_eq!(
|
||||
specifier.scheme(),
|
||||
"data",
|
||||
"Specifier must be a data: specifier."
|
||||
);
|
||||
/// Hash the URL so it can be sent to `tsc` in a supportable way
|
||||
fn hash_url(specifier: &ModuleSpecifier, media_type: &MediaType) -> String {
|
||||
let hash = crate::checksum::gen(&[specifier.path().as_bytes()]);
|
||||
format!("data:///{}{}", hash, media_type.as_ts_extension())
|
||||
}
|
||||
|
||||
fn hash_blob_url(
|
||||
specifier: &ModuleSpecifier,
|
||||
media_type: &MediaType,
|
||||
) -> String {
|
||||
assert_eq!(
|
||||
format!(
|
||||
"{}:///{}{}",
|
||||
specifier.scheme(),
|
||||
"blob",
|
||||
"Specifier must be a blob: specifier."
|
||||
);
|
||||
let hash = crate::checksum::gen(&[specifier.path().as_bytes()]);
|
||||
format!("blob:///{}{}", hash, media_type.as_ts_extension())
|
||||
hash,
|
||||
media_type.as_ts_extension()
|
||||
)
|
||||
}
|
||||
|
||||
/// tsc only supports `.ts`, `.tsx`, `.d.ts`, `.js`, or `.jsx` as root modules
|
||||
|
@ -180,7 +163,7 @@ pub struct Request {
|
|||
pub config: TsConfig,
|
||||
/// Indicates to the tsc runtime if debug logging should occur.
|
||||
pub debug: bool,
|
||||
pub graph: Arc<Mutex<Graph>>,
|
||||
pub graph: Arc<ModuleGraph>,
|
||||
pub hash_data: Vec<Vec<u8>>,
|
||||
pub maybe_config_specifier: Option<ModuleSpecifier>,
|
||||
pub maybe_tsbuildinfo: Option<String>,
|
||||
|
@ -190,7 +173,7 @@ pub struct Request {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Response {
|
||||
pub(crate) struct Response {
|
||||
/// Any diagnostics that have been returned from the checker.
|
||||
pub diagnostics: Diagnostics,
|
||||
/// Any files that were emitted during the check.
|
||||
|
@ -198,7 +181,7 @@ pub struct Response {
|
|||
/// If there was any build info associated with the exec request.
|
||||
pub maybe_tsbuildinfo: Option<String>,
|
||||
/// Statistics from the check.
|
||||
pub stats: Stats,
|
||||
pub stats: emit::Stats,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -206,7 +189,7 @@ struct State {
|
|||
data_url_map: HashMap<String, ModuleSpecifier>,
|
||||
hash_data: Vec<Vec<u8>>,
|
||||
emitted_files: Vec<EmittedFile>,
|
||||
graph: Arc<Mutex<Graph>>,
|
||||
graph: Arc<ModuleGraph>,
|
||||
maybe_config_specifier: Option<ModuleSpecifier>,
|
||||
maybe_tsbuildinfo: Option<String>,
|
||||
maybe_response: Option<RespondArgs>,
|
||||
|
@ -215,7 +198,7 @@ struct State {
|
|||
|
||||
impl State {
|
||||
pub fn new(
|
||||
graph: Arc<Mutex<Graph>>,
|
||||
graph: Arc<ModuleGraph>,
|
||||
hash_data: Vec<Vec<u8>>,
|
||||
maybe_config_specifier: Option<ModuleSpecifier>,
|
||||
maybe_tsbuildinfo: Option<String>,
|
||||
|
@ -334,8 +317,7 @@ fn op_exists(state: &mut State, args: ExistsArgs) -> Result<bool, AnyError> {
|
|||
if specifier.scheme() == "asset" || specifier.scheme() == "data" {
|
||||
Ok(true)
|
||||
} else {
|
||||
let graph = state.graph.lock();
|
||||
Ok(graph.contains(&specifier))
|
||||
Ok(state.graph.contains(&specifier))
|
||||
}
|
||||
} else {
|
||||
Ok(false)
|
||||
|
@ -366,11 +348,10 @@ fn op_load(state: &mut State, args: Value) -> Result<Value, AnyError> {
|
|||
} else if v.specifier.starts_with("asset:///") {
|
||||
let name = v.specifier.replace("asset:///", "");
|
||||
let maybe_source = get_asset(&name).map(String::from);
|
||||
hash = get_maybe_hash(&maybe_source, &state.hash_data);
|
||||
hash = get_maybe_hash(maybe_source.as_ref(), &state.hash_data);
|
||||
media_type = MediaType::from(&v.specifier);
|
||||
maybe_source
|
||||
} else {
|
||||
let graph = state.graph.lock();
|
||||
let specifier = if let Some(data_specifier) =
|
||||
state.data_url_map.get(&v.specifier)
|
||||
{
|
||||
|
@ -380,13 +361,14 @@ fn op_load(state: &mut State, args: Value) -> Result<Value, AnyError> {
|
|||
} else {
|
||||
specifier
|
||||
};
|
||||
let maybe_source = graph.get_source(&specifier).map(|t| t.to_string());
|
||||
media_type = if let Some(media_type) = graph.get_media_type(&specifier) {
|
||||
media_type
|
||||
let maybe_source = if let Some(module) = state.graph.get(&specifier) {
|
||||
media_type = module.media_type;
|
||||
Some(module.source.as_str().to_string())
|
||||
} else {
|
||||
MediaType::Unknown
|
||||
media_type = MediaType::Unknown;
|
||||
None
|
||||
};
|
||||
hash = get_maybe_hash(&maybe_source, &state.hash_data);
|
||||
hash = get_maybe_hash(maybe_source.as_ref(), &state.hash_data);
|
||||
maybe_source
|
||||
};
|
||||
|
||||
|
@ -425,48 +407,31 @@ fn op_resolve(state: &mut State, args: Value) -> Result<Value, AnyError> {
|
|||
MediaType::from(specifier).as_ts_extension().to_string(),
|
||||
));
|
||||
} else {
|
||||
let graph = state.graph.lock();
|
||||
match graph.resolve(specifier, &referrer, true) {
|
||||
Ok(resolved_specifier) => {
|
||||
let media_type = if let Some(media_type) =
|
||||
graph.get_media_type(&resolved_specifier)
|
||||
{
|
||||
media_type
|
||||
} else {
|
||||
bail!(
|
||||
"Unable to resolve media type for specifier: \"{}\"",
|
||||
resolved_specifier
|
||||
)
|
||||
};
|
||||
let resolved_specifier_str = match resolved_specifier.scheme() {
|
||||
"data" | "blob" => {
|
||||
let specifier_str = if resolved_specifier.scheme() == "data" {
|
||||
hash_data_url(&resolved_specifier, &media_type)
|
||||
} else {
|
||||
hash_blob_url(&resolved_specifier, &media_type)
|
||||
};
|
||||
state
|
||||
.data_url_map
|
||||
.insert(specifier_str.clone(), resolved_specifier);
|
||||
specifier_str
|
||||
}
|
||||
_ => resolved_specifier.to_string(),
|
||||
};
|
||||
resolved.push((
|
||||
resolved_specifier_str,
|
||||
media_type.as_ts_extension().into(),
|
||||
));
|
||||
}
|
||||
// in certain situations, like certain dynamic imports, we won't have
|
||||
// the source file in the graph, so we will return a fake module to
|
||||
// make tsc happy.
|
||||
Err(_) => {
|
||||
resolved.push((
|
||||
let resolved_dependency =
|
||||
match state.graph.resolve_dependency(specifier, &referrer, true) {
|
||||
Some(resolved_specifier) => {
|
||||
let media_type = state
|
||||
.graph
|
||||
.get(resolved_specifier)
|
||||
.map_or(&MediaType::Unknown, |m| &m.media_type);
|
||||
let resolved_specifier_str = match resolved_specifier.scheme() {
|
||||
"data" | "blob" => {
|
||||
let specifier_str = hash_url(resolved_specifier, media_type);
|
||||
state
|
||||
.data_url_map
|
||||
.insert(specifier_str.clone(), resolved_specifier.clone());
|
||||
specifier_str
|
||||
}
|
||||
_ => resolved_specifier.to_string(),
|
||||
};
|
||||
(resolved_specifier_str, media_type.as_ts_extension().into())
|
||||
}
|
||||
None => (
|
||||
"deno:///missing_dependency.d.ts".to_string(),
|
||||
".d.ts".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
),
|
||||
};
|
||||
resolved.push(resolved_dependency);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -476,7 +441,7 @@ fn op_resolve(state: &mut State, args: Value) -> Result<Value, AnyError> {
|
|||
#[derive(Debug, Deserialize, Eq, PartialEq)]
|
||||
struct RespondArgs {
|
||||
pub diagnostics: Diagnostics,
|
||||
pub stats: Stats,
|
||||
pub stats: emit::Stats,
|
||||
}
|
||||
|
||||
fn op_respond(state: &mut State, args: Value) -> Result<Value, AnyError> {
|
||||
|
@ -489,7 +454,7 @@ fn op_respond(state: &mut State, args: Value) -> Result<Value, AnyError> {
|
|||
/// 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.
|
||||
pub fn exec(request: Request) -> Result<Response, AnyError> {
|
||||
pub(crate) fn exec(request: Request) -> Result<Response, AnyError> {
|
||||
let mut runtime = JsRuntime::new(RuntimeOptions {
|
||||
startup_snapshot: Some(compiler_snapshot()),
|
||||
..Default::default()
|
||||
|
@ -505,11 +470,7 @@ pub fn exec(request: Request) -> Result<Response, AnyError> {
|
|||
.iter()
|
||||
.map(|(s, mt)| match s.scheme() {
|
||||
"data" | "blob" => {
|
||||
let specifier_str = if s.scheme() == "data" {
|
||||
hash_data_url(s, mt)
|
||||
} else {
|
||||
hash_blob_url(s, mt)
|
||||
};
|
||||
let specifier_str = hash_url(s, mt);
|
||||
data_url_map.insert(specifier_str.clone(), s.clone());
|
||||
specifier_str
|
||||
}
|
||||
|
@ -530,7 +491,7 @@ pub fn exec(request: Request) -> Result<Response, AnyError> {
|
|||
let op_state = runtime.op_state();
|
||||
let mut op_state = op_state.borrow_mut();
|
||||
op_state.put(State::new(
|
||||
request.graph.clone(),
|
||||
request.graph,
|
||||
request.hash_data.clone(),
|
||||
request.maybe_config_specifier.clone(),
|
||||
request.maybe_tsbuildinfo.clone(),
|
||||
|
@ -589,9 +550,39 @@ mod tests {
|
|||
use crate::config_file::TsConfig;
|
||||
use crate::diagnostics::Diagnostic;
|
||||
use crate::diagnostics::DiagnosticCategory;
|
||||
use crate::module_graph::tests::MockSpecifierHandler;
|
||||
use crate::module_graph::GraphBuilder;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use crate::emit::Stats;
|
||||
use deno_core::futures::future;
|
||||
use std::fs;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct MockLoader {
|
||||
pub fixtures: PathBuf,
|
||||
}
|
||||
|
||||
impl deno_graph::source::Loader for MockLoader {
|
||||
fn load(
|
||||
&mut self,
|
||||
specifier: &ModuleSpecifier,
|
||||
_is_dynamic: bool,
|
||||
) -> deno_graph::source::LoadFuture {
|
||||
let specifier_text = specifier
|
||||
.to_string()
|
||||
.replace(":///", "_")
|
||||
.replace("://", "_")
|
||||
.replace("/", "-");
|
||||
let source_path = self.fixtures.join(specifier_text);
|
||||
let response = fs::read_to_string(&source_path)
|
||||
.map(|c| {
|
||||
Some(deno_graph::source::LoadResponse {
|
||||
specifier: specifier.clone(),
|
||||
maybe_headers: None,
|
||||
content: Arc::new(c),
|
||||
})
|
||||
})
|
||||
.map_err(|err| err.into());
|
||||
Box::pin(future::ready((specifier.clone(), response)))
|
||||
}
|
||||
}
|
||||
|
||||
async fn setup(
|
||||
maybe_specifier: Option<ModuleSpecifier>,
|
||||
|
@ -602,16 +593,19 @@ mod tests {
|
|||
.unwrap_or_else(|| resolve_url_or_path("file:///main.ts").unwrap());
|
||||
let hash_data = maybe_hash_data.unwrap_or_else(|| vec![b"".to_vec()]);
|
||||
let fixtures = test_util::testdata_path().join("tsc2");
|
||||
let handler = Arc::new(Mutex::new(MockSpecifierHandler {
|
||||
fixtures,
|
||||
..MockSpecifierHandler::default()
|
||||
}));
|
||||
let mut builder = GraphBuilder::new(handler.clone(), None, None);
|
||||
builder
|
||||
.add(&specifier, false)
|
||||
.await
|
||||
.expect("module not inserted");
|
||||
let graph = Arc::new(Mutex::new(builder.get_graph()));
|
||||
let mut loader = MockLoader { fixtures };
|
||||
let graph = Arc::new(
|
||||
deno_graph::create_graph(
|
||||
vec![specifier],
|
||||
false,
|
||||
None,
|
||||
&mut loader,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await,
|
||||
);
|
||||
State::new(
|
||||
graph,
|
||||
hash_data,
|
||||
|
@ -627,13 +621,19 @@ mod tests {
|
|||
) -> Result<Response, AnyError> {
|
||||
let hash_data = vec![b"something".to_vec()];
|
||||
let fixtures = test_util::testdata_path().join("tsc2");
|
||||
let handler = Arc::new(Mutex::new(MockSpecifierHandler {
|
||||
fixtures,
|
||||
..Default::default()
|
||||
}));
|
||||
let mut builder = GraphBuilder::new(handler.clone(), None, None);
|
||||
builder.add(specifier, false).await?;
|
||||
let graph = Arc::new(Mutex::new(builder.get_graph()));
|
||||
let mut loader = MockLoader { fixtures };
|
||||
let graph = Arc::new(
|
||||
deno_graph::create_graph(
|
||||
vec![specifier.clone()],
|
||||
false,
|
||||
None,
|
||||
&mut loader,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await,
|
||||
);
|
||||
let config = TsConfig::new(json!({
|
||||
"allowJs": true,
|
||||
"checkJs": false,
|
||||
|
@ -695,12 +695,12 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_data_url() {
|
||||
fn test_hash_url() {
|
||||
let specifier = deno_core::resolve_url(
|
||||
"data:application/javascript,console.log(\"Hello%20Deno\");",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(hash_data_url(&specifier, &MediaType::JavaScript), "data:///d300ea0796bd72b08df10348e0b70514c021f2e45bfe59cec24e12e97cd79c58.js");
|
||||
assert_eq!(hash_url(&specifier, &MediaType::JavaScript), "data:///d300ea0796bd72b08df10348e0b70514c021f2e45bfe59cec24e12e97cd79c58.js");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Reference in a new issue