mirror of
https://github.com/denoland/deno.git
synced 2024-11-28 16:20:57 -05:00
feat(npm): support bare specifiers from package.json in more subcommands and language server (#17891)
This commit is contained in:
parent
214bdbbc2b
commit
344317ec50
41 changed files with 472 additions and 95 deletions
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
use crate::args::ConfigFlag;
|
use crate::args::ConfigFlag;
|
||||||
use crate::args::Flags;
|
use crate::args::Flags;
|
||||||
use crate::args::TaskFlags;
|
|
||||||
use crate::util::fs::canonicalize_path;
|
use crate::util::fs::canonicalize_path;
|
||||||
use crate::util::path::specifier_parent;
|
use crate::util::path::specifier_parent;
|
||||||
use crate::util::path::specifier_to_file_path;
|
use crate::util::path::specifier_to_file_path;
|
||||||
|
@ -508,18 +507,6 @@ impl ConfigFile {
|
||||||
return Ok(Some(cf));
|
return Ok(Some(cf));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// attempt to resolve the config file from the task subcommand's
|
|
||||||
// `--cwd` when specified
|
|
||||||
if let crate::args::DenoSubcommand::Task(TaskFlags {
|
|
||||||
cwd: Some(path),
|
|
||||||
..
|
|
||||||
}) = &flags.subcommand
|
|
||||||
{
|
|
||||||
let task_cwd = canonicalize_path(&PathBuf::from(path))?;
|
|
||||||
if let Some(path) = Self::discover_from(&task_cwd, &mut checked)? {
|
|
||||||
return Ok(Some(path));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// From CWD walk up to root looking for deno.json or deno.jsonc
|
// From CWD walk up to root looking for deno.json or deno.jsonc
|
||||||
Self::discover_from(cwd, &mut checked)
|
Self::discover_from(cwd, &mut checked)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -19,6 +19,8 @@ use std::num::NonZeroUsize;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use crate::util::fs::canonicalize_path;
|
||||||
|
|
||||||
use super::flags_allow_net;
|
use super::flags_allow_net;
|
||||||
|
|
||||||
static LONG_VERSION: Lazy<String> = Lazy::new(|| {
|
static LONG_VERSION: Lazy<String> = Lazy::new(|| {
|
||||||
|
@ -499,6 +501,16 @@ impl Flags {
|
||||||
Some(vec![])
|
Some(vec![])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Task(TaskFlags {
|
||||||
|
cwd: Some(path), ..
|
||||||
|
}) => {
|
||||||
|
// attempt to resolve the config file from the task subcommand's
|
||||||
|
// `--cwd` when specified
|
||||||
|
match canonicalize_path(&PathBuf::from(path)) {
|
||||||
|
Ok(path) => Some(vec![path]),
|
||||||
|
Err(_) => Some(vec![]),
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => Some(vec![]),
|
_ => Some(vec![]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -533,7 +545,8 @@ impl Flags {
|
||||||
.to_file_path()
|
.to_file_path()
|
||||||
.ok()
|
.ok()
|
||||||
}
|
}
|
||||||
Task(TaskFlags { cwd: None, .. }) => std::env::current_dir().ok(),
|
Task(_) | Check(_) | Coverage(_) | Cache(_) | Info(_) | Eval(_)
|
||||||
|
| Test(_) | Bench(_) => std::env::current_dir().ok(),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -391,49 +391,13 @@ fn discover_package_json(
|
||||||
flags: &Flags,
|
flags: &Flags,
|
||||||
maybe_stop_at: Option<PathBuf>,
|
maybe_stop_at: Option<PathBuf>,
|
||||||
) -> Result<Option<PackageJson>, AnyError> {
|
) -> Result<Option<PackageJson>, AnyError> {
|
||||||
fn discover_from(
|
|
||||||
start: &Path,
|
|
||||||
maybe_stop_at: Option<PathBuf>,
|
|
||||||
) -> Result<Option<PackageJson>, AnyError> {
|
|
||||||
const PACKAGE_JSON_NAME: &str = "package.json";
|
|
||||||
|
|
||||||
// note: ancestors() includes the `start` path
|
|
||||||
for ancestor in start.ancestors() {
|
|
||||||
let path = ancestor.join(PACKAGE_JSON_NAME);
|
|
||||||
|
|
||||||
let source = match std::fs::read_to_string(&path) {
|
|
||||||
Ok(source) => source,
|
|
||||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
|
||||||
if let Some(stop_at) = maybe_stop_at.as_ref() {
|
|
||||||
if ancestor == stop_at {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Err(err) => bail!(
|
|
||||||
"Error loading package.json at {}. {:#}",
|
|
||||||
path.display(),
|
|
||||||
err
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
let package_json = PackageJson::load_from_string(path.clone(), source)?;
|
|
||||||
log::debug!("package.json file found at '{}'", path.display());
|
|
||||||
return Ok(Some(package_json));
|
|
||||||
}
|
|
||||||
// No config file found.
|
|
||||||
log::debug!("No package.json file found");
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(bartlomieju): discover for all subcommands, but print warnings that
|
// TODO(bartlomieju): discover for all subcommands, but print warnings that
|
||||||
// `package.json` is ignored in bundle/compile/etc.
|
// `package.json` is ignored in bundle/compile/etc.
|
||||||
|
|
||||||
if let Some(package_json_dir) = flags.package_json_search_dir() {
|
if let Some(package_json_dir) = flags.package_json_search_dir() {
|
||||||
let package_json_dir =
|
let package_json_dir =
|
||||||
canonicalize_path_maybe_not_exists(&package_json_dir)?;
|
canonicalize_path_maybe_not_exists(&package_json_dir)?;
|
||||||
return discover_from(&package_json_dir, maybe_stop_at);
|
return package_json::discover_from(&package_json_dir, maybe_stop_at);
|
||||||
}
|
}
|
||||||
|
|
||||||
log::debug!("No package.json file found");
|
log::debug!("No package.json file found");
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use deno_core::anyhow::anyhow;
|
use deno_core::anyhow::anyhow;
|
||||||
use deno_core::anyhow::bail;
|
use deno_core::anyhow::bail;
|
||||||
|
@ -83,6 +85,44 @@ pub fn get_local_package_json_version_reqs(
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempts to discover the package.json file, maybe stopping when it
|
||||||
|
/// reaches the specified `maybe_stop_at` directory.
|
||||||
|
pub fn discover_from(
|
||||||
|
start: &Path,
|
||||||
|
maybe_stop_at: Option<PathBuf>,
|
||||||
|
) -> Result<Option<PackageJson>, AnyError> {
|
||||||
|
const PACKAGE_JSON_NAME: &str = "package.json";
|
||||||
|
|
||||||
|
// note: ancestors() includes the `start` path
|
||||||
|
for ancestor in start.ancestors() {
|
||||||
|
let path = ancestor.join(PACKAGE_JSON_NAME);
|
||||||
|
|
||||||
|
let source = match std::fs::read_to_string(&path) {
|
||||||
|
Ok(source) => source,
|
||||||
|
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||||
|
if let Some(stop_at) = maybe_stop_at.as_ref() {
|
||||||
|
if ancestor == stop_at {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Err(err) => bail!(
|
||||||
|
"Error loading package.json at {}. {:#}",
|
||||||
|
path.display(),
|
||||||
|
err
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
let package_json = PackageJson::load_from_string(path.clone(), source)?;
|
||||||
|
log::debug!("package.json file found at '{}'", path.display());
|
||||||
|
return Ok(Some(package_json));
|
||||||
|
}
|
||||||
|
|
||||||
|
log::debug!("No package.json file found");
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
24
cli/cache/mod.rs
vendored
24
cli/cache/mod.rs
vendored
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use crate::errors::get_error_class_name;
|
use crate::errors::get_error_class_name;
|
||||||
use crate::file_fetcher::FileFetcher;
|
use crate::file_fetcher::FileFetcher;
|
||||||
|
use crate::util::fs::canonicalize_path;
|
||||||
|
|
||||||
use deno_core::futures;
|
use deno_core::futures;
|
||||||
use deno_core::futures::FutureExt;
|
use deno_core::futures::FutureExt;
|
||||||
|
@ -101,12 +102,23 @@ impl Loader for FetchCacher {
|
||||||
is_dynamic: bool,
|
is_dynamic: bool,
|
||||||
) -> LoadFuture {
|
) -> LoadFuture {
|
||||||
if let Some(node_modules_url) = self.maybe_local_node_modules_url.as_ref() {
|
if let Some(node_modules_url) = self.maybe_local_node_modules_url.as_ref() {
|
||||||
if specifier.as_str().starts_with(node_modules_url.as_str()) {
|
// The specifier might be in a completely different symlinked tree than
|
||||||
return Box::pin(futures::future::ready(Ok(Some(
|
// what the resolved node_modules_url is in (ex. `/my-project-1/node_modules`
|
||||||
LoadResponse::External {
|
// symlinked to `/my-project-2/node_modules`), so first check if the path
|
||||||
specifier: specifier.clone(),
|
// is in a node_modules dir to avoid needlessly canonicalizing, then compare
|
||||||
},
|
// against the canonicalized specifier.
|
||||||
))));
|
if specifier.path().contains("/node_modules/") {
|
||||||
|
let specifier = specifier
|
||||||
|
.to_file_path()
|
||||||
|
.ok()
|
||||||
|
.and_then(|path| canonicalize_path(&path).ok())
|
||||||
|
.and_then(|path| ModuleSpecifier::from_file_path(path).ok())
|
||||||
|
.unwrap_or_else(|| specifier.clone());
|
||||||
|
if specifier.as_str().starts_with(node_modules_url.as_str()) {
|
||||||
|
return Box::pin(futures::future::ready(Ok(Some(
|
||||||
|
LoadResponse::External { specifier },
|
||||||
|
))));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -156,6 +156,9 @@ pub async fn create_graph_and_maybe_check(
|
||||||
);
|
);
|
||||||
let maybe_imports = ps.options.to_maybe_imports()?;
|
let maybe_imports = ps.options.to_maybe_imports()?;
|
||||||
let maybe_package_json_deps = ps.options.maybe_package_json_deps()?;
|
let maybe_package_json_deps = ps.options.maybe_package_json_deps()?;
|
||||||
|
ps.npm_resolver
|
||||||
|
.add_package_json_deps(maybe_package_json_deps.as_ref())
|
||||||
|
.await?;
|
||||||
let cli_resolver = CliGraphResolver::new(
|
let cli_resolver = CliGraphResolver::new(
|
||||||
ps.options.to_maybe_jsx_import_source_config(),
|
ps.options.to_maybe_jsx_import_source_config(),
|
||||||
ps.maybe_import_map.clone(),
|
ps.maybe_import_map.clone(),
|
||||||
|
|
|
@ -5,6 +5,7 @@ use super::text::LineIndex;
|
||||||
use super::tsc;
|
use super::tsc;
|
||||||
use super::tsc::AssetDocument;
|
use super::tsc::AssetDocument;
|
||||||
|
|
||||||
|
use crate::args::package_json;
|
||||||
use crate::args::ConfigFile;
|
use crate::args::ConfigFile;
|
||||||
use crate::args::JsxImportSourceConfig;
|
use crate::args::JsxImportSourceConfig;
|
||||||
use crate::cache::CachedUrlMetadata;
|
use crate::cache::CachedUrlMetadata;
|
||||||
|
@ -13,6 +14,7 @@ use crate::cache::HttpCache;
|
||||||
use crate::file_fetcher::get_source_from_bytes;
|
use crate::file_fetcher::get_source_from_bytes;
|
||||||
use crate::file_fetcher::map_content_type;
|
use crate::file_fetcher::map_content_type;
|
||||||
use crate::file_fetcher::SUPPORTED_SCHEMES;
|
use crate::file_fetcher::SUPPORTED_SCHEMES;
|
||||||
|
use crate::lsp::logging::lsp_log;
|
||||||
use crate::node;
|
use crate::node;
|
||||||
use crate::node::node_resolve_npm_reference;
|
use crate::node::node_resolve_npm_reference;
|
||||||
use crate::node::NodeResolution;
|
use crate::node::NodeResolution;
|
||||||
|
@ -37,9 +39,11 @@ use deno_graph::npm::NpmPackageReqReference;
|
||||||
use deno_graph::GraphImport;
|
use deno_graph::GraphImport;
|
||||||
use deno_graph::Resolution;
|
use deno_graph::Resolution;
|
||||||
use deno_runtime::deno_node::NodeResolutionMode;
|
use deno_runtime::deno_node::NodeResolutionMode;
|
||||||
|
use deno_runtime::deno_node::PackageJson;
|
||||||
use deno_runtime::permissions::PermissionsContainer;
|
use deno_runtime::permissions::PermissionsContainer;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
@ -818,8 +822,10 @@ pub struct Documents {
|
||||||
/// A resolver that takes into account currently loaded import map and JSX
|
/// A resolver that takes into account currently loaded import map and JSX
|
||||||
/// settings.
|
/// settings.
|
||||||
resolver: CliGraphResolver,
|
resolver: CliGraphResolver,
|
||||||
/// The npm package requirements.
|
/// The npm package requirements found in a package.json file.
|
||||||
npm_reqs: Arc<HashSet<NpmPackageReq>>,
|
npm_package_json_reqs: Arc<Vec<NpmPackageReq>>,
|
||||||
|
/// The npm package requirements found in npm specifiers.
|
||||||
|
npm_specifier_reqs: Arc<Vec<NpmPackageReq>>,
|
||||||
/// Gets if any document had a node: specifier such that a @types/node package
|
/// Gets if any document had a node: specifier such that a @types/node package
|
||||||
/// should be injected.
|
/// should be injected.
|
||||||
has_injected_types_node_package: bool,
|
has_injected_types_node_package: bool,
|
||||||
|
@ -838,7 +844,8 @@ impl Documents {
|
||||||
resolver_config_hash: 0,
|
resolver_config_hash: 0,
|
||||||
imports: Default::default(),
|
imports: Default::default(),
|
||||||
resolver: CliGraphResolver::default(),
|
resolver: CliGraphResolver::default(),
|
||||||
npm_reqs: Default::default(),
|
npm_package_json_reqs: Default::default(),
|
||||||
|
npm_specifier_reqs: Default::default(),
|
||||||
has_injected_types_node_package: false,
|
has_injected_types_node_package: false,
|
||||||
specifier_resolver: Arc::new(SpecifierResolver::new(location)),
|
specifier_resolver: Arc::new(SpecifierResolver::new(location)),
|
||||||
}
|
}
|
||||||
|
@ -970,9 +977,15 @@ impl Documents {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a collection of npm package requirements.
|
/// Returns a collection of npm package requirements.
|
||||||
pub fn npm_package_reqs(&mut self) -> HashSet<NpmPackageReq> {
|
pub fn npm_package_reqs(&mut self) -> Vec<NpmPackageReq> {
|
||||||
self.calculate_dependents_if_dirty();
|
self.calculate_dependents_if_dirty();
|
||||||
(*self.npm_reqs).clone()
|
let mut reqs = Vec::with_capacity(
|
||||||
|
self.npm_package_json_reqs.len() + self.npm_specifier_reqs.len(),
|
||||||
|
);
|
||||||
|
// resolve the package.json reqs first, then the npm specifiers
|
||||||
|
reqs.extend(self.npm_package_json_reqs.iter().cloned());
|
||||||
|
reqs.extend(self.npm_specifier_reqs.iter().cloned());
|
||||||
|
reqs
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns if a @types/node package was injected into the npm
|
/// Returns if a @types/node package was injected into the npm
|
||||||
|
@ -1150,12 +1163,14 @@ impl Documents {
|
||||||
&mut self,
|
&mut self,
|
||||||
maybe_import_map: Option<Arc<import_map::ImportMap>>,
|
maybe_import_map: Option<Arc<import_map::ImportMap>>,
|
||||||
maybe_config_file: Option<&ConfigFile>,
|
maybe_config_file: Option<&ConfigFile>,
|
||||||
|
maybe_package_json: Option<&PackageJson>,
|
||||||
npm_registry_api: NpmRegistryApi,
|
npm_registry_api: NpmRegistryApi,
|
||||||
npm_resolution: NpmResolution,
|
npm_resolution: NpmResolution,
|
||||||
) {
|
) {
|
||||||
fn calculate_resolver_config_hash(
|
fn calculate_resolver_config_hash(
|
||||||
maybe_import_map: Option<&import_map::ImportMap>,
|
maybe_import_map: Option<&import_map::ImportMap>,
|
||||||
maybe_jsx_config: Option<&JsxImportSourceConfig>,
|
maybe_jsx_config: Option<&JsxImportSourceConfig>,
|
||||||
|
maybe_package_json_deps: Option<&HashMap<String, NpmPackageReq>>,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let mut hasher = FastInsecureHasher::default();
|
let mut hasher = FastInsecureHasher::default();
|
||||||
if let Some(import_map) = maybe_import_map {
|
if let Some(import_map) = maybe_import_map {
|
||||||
|
@ -1165,23 +1180,46 @@ impl Documents {
|
||||||
if let Some(jsx_config) = maybe_jsx_config {
|
if let Some(jsx_config) = maybe_jsx_config {
|
||||||
hasher.write_hashable(&jsx_config);
|
hasher.write_hashable(&jsx_config);
|
||||||
}
|
}
|
||||||
|
if let Some(deps) = maybe_package_json_deps {
|
||||||
|
let deps = deps.iter().collect::<BTreeMap<_, _>>();
|
||||||
|
hasher.write_hashable(&deps);
|
||||||
|
}
|
||||||
hasher.finish()
|
hasher.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let maybe_package_json_deps = maybe_package_json.and_then(|package_json| {
|
||||||
|
match package_json::get_local_package_json_version_reqs(package_json) {
|
||||||
|
Ok(deps) => Some(deps),
|
||||||
|
Err(err) => {
|
||||||
|
lsp_log!("Error parsing package.json deps: {err:#}");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
let maybe_jsx_config =
|
let maybe_jsx_config =
|
||||||
maybe_config_file.and_then(|cf| cf.to_maybe_jsx_import_source_config());
|
maybe_config_file.and_then(|cf| cf.to_maybe_jsx_import_source_config());
|
||||||
let new_resolver_config_hash = calculate_resolver_config_hash(
|
let new_resolver_config_hash = calculate_resolver_config_hash(
|
||||||
maybe_import_map.as_deref(),
|
maybe_import_map.as_deref(),
|
||||||
maybe_jsx_config.as_ref(),
|
maybe_jsx_config.as_ref(),
|
||||||
|
maybe_package_json_deps.as_ref(),
|
||||||
);
|
);
|
||||||
// TODO(bartlomieju): handle package.json dependencies here
|
self.npm_package_json_reqs = Arc::new({
|
||||||
|
match &maybe_package_json_deps {
|
||||||
|
Some(deps) => {
|
||||||
|
let mut reqs = deps.values().cloned().collect::<Vec<_>>();
|
||||||
|
reqs.sort();
|
||||||
|
reqs
|
||||||
|
}
|
||||||
|
None => Vec::new(),
|
||||||
|
}
|
||||||
|
});
|
||||||
self.resolver = CliGraphResolver::new(
|
self.resolver = CliGraphResolver::new(
|
||||||
maybe_jsx_config,
|
maybe_jsx_config,
|
||||||
maybe_import_map,
|
maybe_import_map,
|
||||||
false,
|
false,
|
||||||
npm_registry_api,
|
npm_registry_api,
|
||||||
npm_resolution,
|
npm_resolution,
|
||||||
None,
|
maybe_package_json_deps,
|
||||||
);
|
);
|
||||||
self.imports = Arc::new(
|
self.imports = Arc::new(
|
||||||
if let Some(Ok(imports)) =
|
if let Some(Ok(imports)) =
|
||||||
|
@ -1306,7 +1344,11 @@ impl Documents {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.dependents_map = Arc::new(doc_analyzer.dependents_map);
|
self.dependents_map = Arc::new(doc_analyzer.dependents_map);
|
||||||
self.npm_reqs = Arc::new(npm_reqs);
|
self.npm_specifier_reqs = Arc::new({
|
||||||
|
let mut reqs = npm_reqs.into_iter().collect::<Vec<_>>();
|
||||||
|
reqs.sort();
|
||||||
|
reqs
|
||||||
|
});
|
||||||
self.dirty = false;
|
self.dirty = false;
|
||||||
file_system_docs.dirty = false;
|
file_system_docs.dirty = false;
|
||||||
}
|
}
|
||||||
|
@ -1589,6 +1631,7 @@ console.log(b, "hello deno");
|
||||||
documents.update_config(
|
documents.update_config(
|
||||||
Some(Arc::new(import_map)),
|
Some(Arc::new(import_map)),
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
npm_registry_api.clone(),
|
npm_registry_api.clone(),
|
||||||
npm_resolution.clone(),
|
npm_resolution.clone(),
|
||||||
);
|
);
|
||||||
|
@ -1627,6 +1670,7 @@ console.log(b, "hello deno");
|
||||||
documents.update_config(
|
documents.update_config(
|
||||||
Some(Arc::new(import_map)),
|
Some(Arc::new(import_map)),
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
npm_registry_api,
|
npm_registry_api,
|
||||||
npm_resolution,
|
npm_resolution,
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,12 +9,14 @@ use deno_core::serde_json;
|
||||||
use deno_core::serde_json::json;
|
use deno_core::serde_json::json;
|
||||||
use deno_core::serde_json::Value;
|
use deno_core::serde_json::Value;
|
||||||
use deno_core::ModuleSpecifier;
|
use deno_core::ModuleSpecifier;
|
||||||
|
use deno_runtime::deno_node::PackageJson;
|
||||||
use deno_runtime::deno_web::BlobStore;
|
use deno_runtime::deno_web::BlobStore;
|
||||||
use import_map::ImportMap;
|
use import_map::ImportMap;
|
||||||
use log::error;
|
use log::error;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use serde_json::from_value;
|
use serde_json::from_value;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fmt::Write as _;
|
use std::fmt::Write as _;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
@ -58,6 +60,7 @@ use super::tsc::AssetsSnapshot;
|
||||||
use super::tsc::TsServer;
|
use super::tsc::TsServer;
|
||||||
use super::urls;
|
use super::urls;
|
||||||
use crate::args::get_root_cert_store;
|
use crate::args::get_root_cert_store;
|
||||||
|
use crate::args::package_json;
|
||||||
use crate::args::resolve_import_map_from_specifier;
|
use crate::args::resolve_import_map_from_specifier;
|
||||||
use crate::args::CaData;
|
use crate::args::CaData;
|
||||||
use crate::args::CacheSetting;
|
use crate::args::CacheSetting;
|
||||||
|
@ -130,6 +133,8 @@ pub struct Inner {
|
||||||
maybe_import_map: Option<Arc<ImportMap>>,
|
maybe_import_map: Option<Arc<ImportMap>>,
|
||||||
/// The URL for the import map which is used to determine relative imports.
|
/// The URL for the import map which is used to determine relative imports.
|
||||||
maybe_import_map_uri: Option<Url>,
|
maybe_import_map_uri: Option<Url>,
|
||||||
|
/// An optional package.json configuration file.
|
||||||
|
maybe_package_json: Option<PackageJson>,
|
||||||
/// Configuration for formatter which has been taken from specified config file.
|
/// Configuration for formatter which has been taken from specified config file.
|
||||||
fmt_options: FmtOptions,
|
fmt_options: FmtOptions,
|
||||||
/// An optional configuration for linter which has been taken from specified config file.
|
/// An optional configuration for linter which has been taken from specified config file.
|
||||||
|
@ -376,6 +381,7 @@ impl Inner {
|
||||||
maybe_config_file: None,
|
maybe_config_file: None,
|
||||||
maybe_import_map: None,
|
maybe_import_map: None,
|
||||||
maybe_import_map_uri: None,
|
maybe_import_map_uri: None,
|
||||||
|
maybe_package_json: None,
|
||||||
fmt_options: Default::default(),
|
fmt_options: Default::default(),
|
||||||
lint_options: Default::default(),
|
lint_options: Default::default(),
|
||||||
maybe_testing_server: None,
|
maybe_testing_server: None,
|
||||||
|
@ -456,8 +462,6 @@ impl Inner {
|
||||||
Ok(navigation_tree)
|
Ok(navigation_tree)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a tuple with parsed `ConfigFile` and `Url` pointing to that file.
|
|
||||||
/// If there's no config file specified in settings returns `None`.
|
|
||||||
fn get_config_file(&self) -> Result<Option<ConfigFile>, AnyError> {
|
fn get_config_file(&self) -> Result<Option<ConfigFile>, AnyError> {
|
||||||
let workspace_settings = self.config.get_workspace_settings();
|
let workspace_settings = self.config.get_workspace_settings();
|
||||||
let maybe_config = workspace_settings.config;
|
let maybe_config = workspace_settings.config;
|
||||||
|
@ -501,6 +505,28 @@ impl Inner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_package_json(
|
||||||
|
&self,
|
||||||
|
maybe_config_file: Option<&ConfigFile>,
|
||||||
|
) -> Result<Option<PackageJson>, AnyError> {
|
||||||
|
// It is possible that root_uri is not set, for example when having a single
|
||||||
|
// file open and not a workspace. In those situations we can't
|
||||||
|
// automatically discover the configuration
|
||||||
|
if let Some(root_uri) = &self.config.root_uri {
|
||||||
|
let root_path = specifier_to_file_path(root_uri)?;
|
||||||
|
let maybe_package_json = package_json::discover_from(
|
||||||
|
&root_path,
|
||||||
|
maybe_config_file.and_then(|f| f.specifier.to_file_path().ok()),
|
||||||
|
)?;
|
||||||
|
Ok(maybe_package_json.map(|c| {
|
||||||
|
lsp_log!(" Auto-resolved package.json: \"{}\"", c.specifier());
|
||||||
|
c
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn is_diagnosable(&self, specifier: &ModuleSpecifier) -> bool {
|
fn is_diagnosable(&self, specifier: &ModuleSpecifier) -> bool {
|
||||||
if specifier.scheme() == "asset" {
|
if specifier.scheme() == "asset" {
|
||||||
matches!(
|
matches!(
|
||||||
|
@ -785,6 +811,7 @@ impl Inner {
|
||||||
|
|
||||||
fn update_config_file(&mut self) -> Result<(), AnyError> {
|
fn update_config_file(&mut self) -> Result<(), AnyError> {
|
||||||
self.maybe_config_file = None;
|
self.maybe_config_file = None;
|
||||||
|
self.maybe_package_json = None;
|
||||||
self.fmt_options = Default::default();
|
self.fmt_options = Default::default();
|
||||||
self.lint_options = Default::default();
|
self.lint_options = Default::default();
|
||||||
|
|
||||||
|
@ -814,6 +841,15 @@ impl Inner {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Updates the package.json. Always ensure this is done after updating
|
||||||
|
/// the configuration file as the resolution of this depends on that.
|
||||||
|
fn update_package_json(&mut self) -> Result<(), AnyError> {
|
||||||
|
self.maybe_package_json = None;
|
||||||
|
self.maybe_package_json =
|
||||||
|
self.get_package_json(self.maybe_config_file.as_ref())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn update_tsconfig(&mut self) -> Result<(), AnyError> {
|
async fn update_tsconfig(&mut self) -> Result<(), AnyError> {
|
||||||
let mark = self.performance.mark("update_tsconfig", None::<()>);
|
let mark = self.performance.mark("update_tsconfig", None::<()>);
|
||||||
let mut tsconfig = TsConfig::new(json!({
|
let mut tsconfig = TsConfig::new(json!({
|
||||||
|
@ -923,6 +959,9 @@ impl Inner {
|
||||||
if let Err(err) = self.update_config_file() {
|
if let Err(err) = self.update_config_file() {
|
||||||
self.client.show_message(MessageType::WARNING, err).await;
|
self.client.show_message(MessageType::WARNING, err).await;
|
||||||
}
|
}
|
||||||
|
if let Err(err) = self.update_package_json() {
|
||||||
|
self.client.show_message(MessageType::WARNING, err).await;
|
||||||
|
}
|
||||||
if let Err(err) = self.update_tsconfig().await {
|
if let Err(err) = self.update_tsconfig().await {
|
||||||
self.client.show_message(MessageType::WARNING, err).await;
|
self.client.show_message(MessageType::WARNING, err).await;
|
||||||
}
|
}
|
||||||
|
@ -950,6 +989,7 @@ impl Inner {
|
||||||
self.documents.update_config(
|
self.documents.update_config(
|
||||||
self.maybe_import_map.clone(),
|
self.maybe_import_map.clone(),
|
||||||
self.maybe_config_file.as_ref(),
|
self.maybe_config_file.as_ref(),
|
||||||
|
self.maybe_package_json.as_ref(),
|
||||||
self.npm_resolver.api().clone(),
|
self.npm_resolver.api().clone(),
|
||||||
self.npm_resolver.resolution().clone(),
|
self.npm_resolver.resolution().clone(),
|
||||||
);
|
);
|
||||||
|
@ -1129,6 +1169,9 @@ impl Inner {
|
||||||
if let Err(err) = self.update_config_file() {
|
if let Err(err) = self.update_config_file() {
|
||||||
self.client.show_message(MessageType::WARNING, err).await;
|
self.client.show_message(MessageType::WARNING, err).await;
|
||||||
}
|
}
|
||||||
|
if let Err(err) = self.update_package_json() {
|
||||||
|
self.client.show_message(MessageType::WARNING, err).await;
|
||||||
|
}
|
||||||
if let Err(err) = self.update_import_map().await {
|
if let Err(err) = self.update_import_map().await {
|
||||||
self.client.show_message(MessageType::WARNING, err).await;
|
self.client.show_message(MessageType::WARNING, err).await;
|
||||||
}
|
}
|
||||||
|
@ -1139,6 +1182,7 @@ impl Inner {
|
||||||
self.documents.update_config(
|
self.documents.update_config(
|
||||||
self.maybe_import_map.clone(),
|
self.maybe_import_map.clone(),
|
||||||
self.maybe_config_file.as_ref(),
|
self.maybe_config_file.as_ref(),
|
||||||
|
self.maybe_package_json.as_ref(),
|
||||||
self.npm_resolver.api().clone(),
|
self.npm_resolver.api().clone(),
|
||||||
self.npm_resolver.resolution().clone(),
|
self.npm_resolver.resolution().clone(),
|
||||||
);
|
);
|
||||||
|
@ -1155,7 +1199,7 @@ impl Inner {
|
||||||
.performance
|
.performance
|
||||||
.mark("did_change_watched_files", Some(¶ms));
|
.mark("did_change_watched_files", Some(¶ms));
|
||||||
let mut touched = false;
|
let mut touched = false;
|
||||||
let changes: Vec<Url> = params
|
let changes: HashSet<Url> = params
|
||||||
.changes
|
.changes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|f| self.url_map.normalize_url(&f.uri))
|
.map(|f| self.url_map.normalize_url(&f.uri))
|
||||||
|
@ -1163,7 +1207,7 @@ impl Inner {
|
||||||
|
|
||||||
// if the current tsconfig has changed, we need to reload it
|
// if the current tsconfig has changed, we need to reload it
|
||||||
if let Some(config_file) = &self.maybe_config_file {
|
if let Some(config_file) = &self.maybe_config_file {
|
||||||
if changes.iter().any(|uri| config_file.specifier == *uri) {
|
if changes.contains(&config_file.specifier) {
|
||||||
if let Err(err) = self.update_config_file() {
|
if let Err(err) = self.update_config_file() {
|
||||||
self.client.show_message(MessageType::WARNING, err).await;
|
self.client.show_message(MessageType::WARNING, err).await;
|
||||||
}
|
}
|
||||||
|
@ -1173,10 +1217,18 @@ impl Inner {
|
||||||
touched = true;
|
touched = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(package_json) = &self.maybe_package_json {
|
||||||
|
if changes.contains(&package_json.specifier()) {
|
||||||
|
if let Err(err) = self.update_package_json() {
|
||||||
|
self.client.show_message(MessageType::WARNING, err).await;
|
||||||
|
}
|
||||||
|
touched = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
// if the current import map, or config file has changed, we need to reload
|
// if the current import map, or config file has changed, we need to reload
|
||||||
// reload the import map
|
// reload the import map
|
||||||
if let Some(import_map_uri) = &self.maybe_import_map_uri {
|
if let Some(import_map_uri) = &self.maybe_import_map_uri {
|
||||||
if changes.iter().any(|uri| import_map_uri == uri) || touched {
|
if changes.contains(import_map_uri) || touched {
|
||||||
if let Err(err) = self.update_import_map().await {
|
if let Err(err) = self.update_import_map().await {
|
||||||
self.client.show_message(MessageType::WARNING, err).await;
|
self.client.show_message(MessageType::WARNING, err).await;
|
||||||
}
|
}
|
||||||
|
@ -1187,6 +1239,7 @@ impl Inner {
|
||||||
self.documents.update_config(
|
self.documents.update_config(
|
||||||
self.maybe_import_map.clone(),
|
self.maybe_import_map.clone(),
|
||||||
self.maybe_config_file.as_ref(),
|
self.maybe_config_file.as_ref(),
|
||||||
|
self.maybe_package_json.as_ref(),
|
||||||
self.npm_resolver.api().clone(),
|
self.npm_resolver.api().clone(),
|
||||||
self.npm_resolver.resolution().clone(),
|
self.npm_resolver.resolution().clone(),
|
||||||
);
|
);
|
||||||
|
|
|
@ -179,6 +179,10 @@ impl ReadonlyNpmCache {
|
||||||
Self::new(dir.npm_folder_path())
|
Self::new(dir.npm_folder_path())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn root_dir_url(&self) -> &Url {
|
||||||
|
&self.root_dir_url
|
||||||
|
}
|
||||||
|
|
||||||
pub fn package_folder_for_id(
|
pub fn package_folder_for_id(
|
||||||
&self,
|
&self,
|
||||||
folder_id: &NpmPackageCacheFolderId,
|
folder_id: &NpmPackageCacheFolderId,
|
||||||
|
@ -345,6 +349,10 @@ impl NpmCache {
|
||||||
&self.cache_setting
|
&self.cache_setting
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn root_dir_url(&self) -> &Url {
|
||||||
|
self.readonly.root_dir_url()
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks if the cache should be used for the provided name and version.
|
/// Checks if the cache should be used for the provided name and version.
|
||||||
/// NOTE: Subsequent calls for the same package will always return `true`
|
/// NOTE: Subsequent calls for the same package will always return `true`
|
||||||
/// to ensure a package is only downloaded once per run of the CLI. This
|
/// to ensure a package is only downloaded once per run of the CLI. This
|
||||||
|
|
|
@ -292,17 +292,18 @@ impl NpmResolution {
|
||||||
|
|
||||||
pub async fn set_package_reqs(
|
pub async fn set_package_reqs(
|
||||||
&self,
|
&self,
|
||||||
package_reqs: HashSet<NpmPackageReq>,
|
package_reqs: Vec<NpmPackageReq>,
|
||||||
) -> Result<(), AnyError> {
|
) -> Result<(), AnyError> {
|
||||||
let inner = &self.0;
|
let inner = &self.0;
|
||||||
// only allow one thread in here at a time
|
// only allow one thread in here at a time
|
||||||
let _permit = inner.update_semaphore.acquire().await?;
|
let _permit = inner.update_semaphore.acquire().await?;
|
||||||
let snapshot = inner.snapshot.read().clone();
|
let snapshot = inner.snapshot.read().clone();
|
||||||
|
|
||||||
|
let reqs_set = package_reqs.iter().collect::<HashSet<_>>();
|
||||||
let has_removed_package = !snapshot
|
let has_removed_package = !snapshot
|
||||||
.package_reqs
|
.package_reqs
|
||||||
.keys()
|
.keys()
|
||||||
.all(|req| package_reqs.contains(req));
|
.all(|req| reqs_set.contains(req));
|
||||||
// if any packages were removed, we need to completely recreate the npm resolution snapshot
|
// if any packages were removed, we need to completely recreate the npm resolution snapshot
|
||||||
let snapshot = if has_removed_package {
|
let snapshot = if has_removed_package {
|
||||||
NpmResolutionSnapshot::default()
|
NpmResolutionSnapshot::default()
|
||||||
|
@ -311,7 +312,7 @@ impl NpmResolution {
|
||||||
};
|
};
|
||||||
let snapshot = add_package_reqs_to_snapshot(
|
let snapshot = add_package_reqs_to_snapshot(
|
||||||
&inner.api,
|
&inner.api,
|
||||||
package_reqs.into_iter().collect(),
|
package_reqs,
|
||||||
snapshot,
|
snapshot,
|
||||||
self.0.maybe_lockfile.clone(),
|
self.0.maybe_lockfile.clone(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -20,6 +20,9 @@ use crate::npm::NpmResolutionPackage;
|
||||||
/// Part of the resolution that interacts with the file system.
|
/// Part of the resolution that interacts with the file system.
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait NpmPackageFsResolver: Send + Sync {
|
pub trait NpmPackageFsResolver: Send + Sync {
|
||||||
|
/// Specifier for the root directory.
|
||||||
|
fn root_dir_url(&self) -> &Url;
|
||||||
|
|
||||||
fn resolve_package_folder_from_deno_module(
|
fn resolve_package_folder_from_deno_module(
|
||||||
&self,
|
&self,
|
||||||
id: &NpmPackageId,
|
id: &NpmPackageId,
|
||||||
|
|
|
@ -68,6 +68,10 @@ impl GlobalNpmPackageResolver {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl NpmPackageFsResolver for GlobalNpmPackageResolver {
|
impl NpmPackageFsResolver for GlobalNpmPackageResolver {
|
||||||
|
fn root_dir_url(&self) -> &Url {
|
||||||
|
self.cache.root_dir_url()
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_package_folder_from_deno_module(
|
fn resolve_package_folder_from_deno_module(
|
||||||
&self,
|
&self,
|
||||||
id: &NpmPackageId,
|
id: &NpmPackageId,
|
||||||
|
|
|
@ -44,7 +44,7 @@ pub struct LocalNpmPackageResolver {
|
||||||
resolution: NpmResolution,
|
resolution: NpmResolution,
|
||||||
registry_url: Url,
|
registry_url: Url,
|
||||||
root_node_modules_path: PathBuf,
|
root_node_modules_path: PathBuf,
|
||||||
root_node_modules_specifier: ModuleSpecifier,
|
root_node_modules_url: Url,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LocalNpmPackageResolver {
|
impl LocalNpmPackageResolver {
|
||||||
|
@ -58,10 +58,8 @@ impl LocalNpmPackageResolver {
|
||||||
cache,
|
cache,
|
||||||
resolution,
|
resolution,
|
||||||
registry_url,
|
registry_url,
|
||||||
root_node_modules_specifier: ModuleSpecifier::from_directory_path(
|
root_node_modules_url: Url::from_directory_path(&node_modules_folder)
|
||||||
&node_modules_folder,
|
.unwrap(),
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
root_node_modules_path: node_modules_folder,
|
root_node_modules_path: node_modules_folder,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,8 +90,7 @@ impl LocalNpmPackageResolver {
|
||||||
&self,
|
&self,
|
||||||
specifier: &ModuleSpecifier,
|
specifier: &ModuleSpecifier,
|
||||||
) -> Option<PathBuf> {
|
) -> Option<PathBuf> {
|
||||||
let relative_url =
|
let relative_url = self.root_node_modules_url.make_relative(specifier)?;
|
||||||
self.root_node_modules_specifier.make_relative(specifier)?;
|
|
||||||
if relative_url.starts_with("../") {
|
if relative_url.starts_with("../") {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -126,6 +123,10 @@ impl LocalNpmPackageResolver {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl NpmPackageFsResolver for LocalNpmPackageResolver {
|
impl NpmPackageFsResolver for LocalNpmPackageResolver {
|
||||||
|
fn root_dir_url(&self) -> &Url {
|
||||||
|
&self.root_node_modules_url
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_package_folder_from_deno_module(
|
fn resolve_package_folder_from_deno_module(
|
||||||
&self,
|
&self,
|
||||||
node_id: &NpmPackageId,
|
node_id: &NpmPackageId,
|
||||||
|
|
|
@ -19,7 +19,7 @@ use deno_runtime::deno_node::RequireNpmResolver;
|
||||||
use global::GlobalNpmPackageResolver;
|
use global::GlobalNpmPackageResolver;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -214,9 +214,9 @@ impl NpmPackageResolver {
|
||||||
|
|
||||||
/// Gets if the provided specifier is in an npm package.
|
/// Gets if the provided specifier is in an npm package.
|
||||||
pub fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
|
pub fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
|
||||||
self
|
let root_dir_url = self.fs_resolver.root_dir_url();
|
||||||
.resolve_package_folder_from_specifier(specifier)
|
debug_assert!(root_dir_url.as_str().ends_with('/'));
|
||||||
.is_ok()
|
specifier.as_ref().starts_with(root_dir_url.as_str())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If the resolver has resolved any npm packages.
|
/// If the resolver has resolved any npm packages.
|
||||||
|
@ -224,6 +224,19 @@ impl NpmPackageResolver {
|
||||||
self.resolution.has_packages()
|
self.resolution.has_packages()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds the package reqs from a package.json if they exist.
|
||||||
|
pub async fn add_package_json_deps(
|
||||||
|
&self,
|
||||||
|
maybe_package_json_deps: Option<&HashMap<String, NpmPackageReq>>,
|
||||||
|
) -> Result<(), AnyError> {
|
||||||
|
if let Some(deps) = maybe_package_json_deps {
|
||||||
|
let mut package_reqs = deps.values().cloned().collect::<Vec<_>>();
|
||||||
|
package_reqs.sort(); // deterministic resolution
|
||||||
|
self.add_package_reqs(package_reqs).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Adds package requirements to the resolver and ensures everything is setup.
|
/// Adds package requirements to the resolver and ensures everything is setup.
|
||||||
pub async fn add_package_reqs(
|
pub async fn add_package_reqs(
|
||||||
&self,
|
&self,
|
||||||
|
@ -250,7 +263,7 @@ impl NpmPackageResolver {
|
||||||
/// This will retrieve and resolve package information, but not cache any package files.
|
/// This will retrieve and resolve package information, but not cache any package files.
|
||||||
pub async fn set_package_reqs(
|
pub async fn set_package_reqs(
|
||||||
&self,
|
&self,
|
||||||
packages: HashSet<NpmPackageReq>,
|
packages: Vec<NpmPackageReq>,
|
||||||
) -> Result<(), AnyError> {
|
) -> Result<(), AnyError> {
|
||||||
self.resolution.set_package_reqs(packages).await
|
self.resolution.set_package_reqs(packages).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -237,12 +237,10 @@ impl ProcState {
|
||||||
cli_options.resolve_inspector_server().map(Arc::new);
|
cli_options.resolve_inspector_server().map(Arc::new);
|
||||||
|
|
||||||
let maybe_package_json_deps = cli_options.maybe_package_json_deps()?;
|
let maybe_package_json_deps = cli_options.maybe_package_json_deps()?;
|
||||||
if let Some(deps) = &maybe_package_json_deps {
|
// resolve the package.json npm requirements ahead of time
|
||||||
// resolve the package.json npm requirements ahead of time
|
npm_resolver
|
||||||
let mut package_reqs = deps.values().cloned().collect::<Vec<_>>();
|
.add_package_json_deps(maybe_package_json_deps.as_ref())
|
||||||
package_reqs.sort(); // deterministic resolution
|
.await?;
|
||||||
npm_resolver.add_package_reqs(package_reqs).await?;
|
|
||||||
}
|
|
||||||
let resolver = Arc::new(CliGraphResolver::new(
|
let resolver = Arc::new(CliGraphResolver::new(
|
||||||
cli_options.to_maybe_jsx_import_source_config(),
|
cli_options.to_maybe_jsx_import_source_config(),
|
||||||
maybe_import_map.clone(),
|
maybe_import_map.clone(),
|
||||||
|
@ -639,14 +637,18 @@ impl ProcState {
|
||||||
) -> Result<deno_graph::ModuleGraph, AnyError> {
|
) -> Result<deno_graph::ModuleGraph, AnyError> {
|
||||||
let maybe_imports = self.options.to_maybe_imports()?;
|
let maybe_imports = self.options.to_maybe_imports()?;
|
||||||
|
|
||||||
|
let maybe_package_json_deps = self.options.maybe_package_json_deps()?;
|
||||||
|
self
|
||||||
|
.npm_resolver
|
||||||
|
.add_package_json_deps(maybe_package_json_deps.as_ref())
|
||||||
|
.await?;
|
||||||
let cli_resolver = CliGraphResolver::new(
|
let cli_resolver = CliGraphResolver::new(
|
||||||
self.options.to_maybe_jsx_import_source_config(),
|
self.options.to_maybe_jsx_import_source_config(),
|
||||||
self.maybe_import_map.clone(),
|
self.maybe_import_map.clone(),
|
||||||
self.options.no_npm(),
|
self.options.no_npm(),
|
||||||
self.npm_resolver.api().clone(),
|
self.npm_resolver.api().clone(),
|
||||||
self.npm_resolver.resolution().clone(),
|
self.npm_resolver.resolution().clone(),
|
||||||
// TODO(bartlomieju): this should use dependencies from `package.json`?
|
maybe_package_json_deps,
|
||||||
None,
|
|
||||||
);
|
);
|
||||||
let graph_resolver = cli_resolver.as_graph_resolver();
|
let graph_resolver = cli_resolver.as_graph_resolver();
|
||||||
let graph_npm_resolver = cli_resolver.as_graph_npm_resolver();
|
let graph_npm_resolver = cli_resolver.as_graph_npm_resolver();
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use deno_core::url::Url;
|
use deno_core::url::Url;
|
||||||
use test_util as util;
|
use test_util as util;
|
||||||
|
use util::env_vars_for_npm_tests;
|
||||||
|
|
||||||
itest!(overloads {
|
itest!(overloads {
|
||||||
args: "bench bench/overloads.ts",
|
args: "bench bench/overloads.ts",
|
||||||
|
@ -216,3 +217,13 @@ fn file_protocol() {
|
||||||
})
|
})
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
itest!(package_json_basic {
|
||||||
|
args: "bench",
|
||||||
|
output: "package_json/basic/main.bench.out",
|
||||||
|
envs: env_vars_for_npm_tests(),
|
||||||
|
http_server: true,
|
||||||
|
cwd: Some("package_json/basic"),
|
||||||
|
copy_temp_dir: Some("package_json/basic"),
|
||||||
|
exit_code: 0,
|
||||||
|
});
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use test_util::env_vars_for_npm_tests;
|
||||||
|
|
||||||
itest!(_036_import_map_fetch {
|
itest!(_036_import_map_fetch {
|
||||||
args:
|
args:
|
||||||
"cache --quiet --reload --import-map=import_maps/import_map.json import_maps/test.ts",
|
"cache --quiet --reload --import-map=import_maps/import_map.json import_maps/test.ts",
|
||||||
|
@ -95,3 +97,13 @@ itest!(json_import {
|
||||||
// should not error
|
// should not error
|
||||||
args: "cache --quiet cache/json_import/main.ts",
|
args: "cache --quiet cache/json_import/main.ts",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
itest!(package_json_basic {
|
||||||
|
args: "cache main.ts",
|
||||||
|
output: "package_json/basic/main.cache.out",
|
||||||
|
envs: env_vars_for_npm_tests(),
|
||||||
|
http_server: true,
|
||||||
|
cwd: Some("package_json/basic"),
|
||||||
|
copy_temp_dir: Some("package_json/basic"),
|
||||||
|
exit_code: 0,
|
||||||
|
});
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
use test_util as util;
|
use test_util as util;
|
||||||
|
use util::env_vars_for_npm_tests;
|
||||||
|
use util::env_vars_for_npm_tests_no_sync_download;
|
||||||
use util::TempDir;
|
use util::TempDir;
|
||||||
|
|
||||||
itest!(_095_check_with_bare_import {
|
itest!(_095_check_with_bare_import {
|
||||||
|
@ -229,3 +231,23 @@ fn ts_no_recheck_on_redirect() {
|
||||||
|
|
||||||
assert!(std::str::from_utf8(&output.stderr).unwrap().is_empty());
|
assert!(std::str::from_utf8(&output.stderr).unwrap().is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
itest!(package_json_basic {
|
||||||
|
args: "check main.ts",
|
||||||
|
output: "package_json/basic/main.check.out",
|
||||||
|
envs: env_vars_for_npm_tests(),
|
||||||
|
http_server: true,
|
||||||
|
cwd: Some("package_json/basic"),
|
||||||
|
copy_temp_dir: Some("package_json/basic"),
|
||||||
|
exit_code: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
itest!(package_json_fail_check {
|
||||||
|
args: "check --quiet fail_check.ts",
|
||||||
|
output: "package_json/basic/fail_check.check.out",
|
||||||
|
envs: env_vars_for_npm_tests_no_sync_download(),
|
||||||
|
http_server: true,
|
||||||
|
cwd: Some("package_json/basic"),
|
||||||
|
copy_temp_dir: Some("package_json/basic"),
|
||||||
|
exit_code: 1,
|
||||||
|
});
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use test_util as util;
|
use test_util as util;
|
||||||
use test_util::TempDir;
|
use test_util::TempDir;
|
||||||
|
use util::env_vars_for_npm_tests_no_sync_download;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn info_with_compiled_source() {
|
fn info_with_compiled_source() {
|
||||||
|
@ -127,3 +128,13 @@ itest!(with_config_override {
|
||||||
args: "info info/with_config/test.ts --config info/with_config/deno-override.json --import-map info/with_config/import_map.json",
|
args: "info info/with_config/test.ts --config info/with_config/deno-override.json --import-map info/with_config/import_map.json",
|
||||||
output: "info/with_config/with_config.out",
|
output: "info/with_config/with_config.out",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
itest!(package_json_basic {
|
||||||
|
args: "info --quiet main.ts",
|
||||||
|
output: "package_json/basic/main.info.out",
|
||||||
|
envs: env_vars_for_npm_tests_no_sync_download(),
|
||||||
|
http_server: true,
|
||||||
|
cwd: Some("package_json/basic"),
|
||||||
|
copy_temp_dir: Some("package_json/basic"),
|
||||||
|
exit_code: 0,
|
||||||
|
});
|
||||||
|
|
|
@ -1565,3 +1565,23 @@ itest!(create_require {
|
||||||
envs: env_vars_for_npm_tests(),
|
envs: env_vars_for_npm_tests(),
|
||||||
http_server: true,
|
http_server: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
itest!(node_modules_import_run {
|
||||||
|
args: "run --quiet main.ts",
|
||||||
|
output: "npm/node_modules_import/main.out",
|
||||||
|
envs: env_vars_for_npm_tests(),
|
||||||
|
http_server: true,
|
||||||
|
cwd: Some("npm/node_modules_import/"),
|
||||||
|
copy_temp_dir: Some("npm/node_modules_import/"),
|
||||||
|
exit_code: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
itest!(node_modules_import_check {
|
||||||
|
args: "check --quiet main.ts",
|
||||||
|
output: "npm/node_modules_import/main_check.out",
|
||||||
|
envs: env_vars_for_npm_tests(),
|
||||||
|
http_server: true,
|
||||||
|
cwd: Some("npm/node_modules_import/"),
|
||||||
|
copy_temp_dir: Some("npm/node_modules_import/"),
|
||||||
|
exit_code: 1,
|
||||||
|
});
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use deno_core::url::Url;
|
use deno_core::url::Url;
|
||||||
use test_util as util;
|
use test_util as util;
|
||||||
|
use util::env_vars_for_npm_tests;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_color() {
|
fn no_color() {
|
||||||
|
@ -452,3 +453,13 @@ itest!(parallel_output {
|
||||||
output: "test/parallel_output.out",
|
output: "test/parallel_output.out",
|
||||||
exit_code: 1,
|
exit_code: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
itest!(package_json_basic {
|
||||||
|
args: "test",
|
||||||
|
output: "package_json/basic/main.test.out",
|
||||||
|
envs: env_vars_for_npm_tests(),
|
||||||
|
http_server: true,
|
||||||
|
cwd: Some("package_json/basic"),
|
||||||
|
copy_temp_dir: Some("package_json/basic"),
|
||||||
|
exit_code: 0,
|
||||||
|
});
|
||||||
|
|
2
cli/tests/testdata/npm/node_modules_import/main.out
vendored
Normal file
2
cli/tests/testdata/npm/node_modules_import/main.out
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
2
|
||||||
|
2
|
13
cli/tests/testdata/npm/node_modules_import/main.ts
vendored
Normal file
13
cli/tests/testdata/npm/node_modules_import/main.ts
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import * as myImport1 from "@denotest/esm-basic";
|
||||||
|
import * as myImport2 from "./node_modules/@denotest/esm-basic/main.mjs";
|
||||||
|
|
||||||
|
myImport1.setValue(5);
|
||||||
|
myImport2.setValue(2);
|
||||||
|
|
||||||
|
// these should both give type errors
|
||||||
|
const value1: string = myImport1.getValue();
|
||||||
|
const value2: string = myImport2.getValue();
|
||||||
|
|
||||||
|
// these should both be equal because it should be mutating the same module
|
||||||
|
console.log(value1);
|
||||||
|
console.log(value2);
|
11
cli/tests/testdata/npm/node_modules_import/main_check.out
vendored
Normal file
11
cli/tests/testdata/npm/node_modules_import/main_check.out
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
error: TS2322 [ERROR]: Type 'number' is not assignable to type 'string'.
|
||||||
|
const value1: string = myImport1.getValue();
|
||||||
|
~~~~~~
|
||||||
|
at file:///[WILDCARD]/npm/node_modules_import/main.ts:8:7
|
||||||
|
|
||||||
|
TS2322 [ERROR]: Type 'number' is not assignable to type 'string'.
|
||||||
|
const value2: string = myImport2.getValue();
|
||||||
|
~~~~~~
|
||||||
|
at file:///[WILDCARD]/npm/node_modules_import/main.ts:9:7
|
||||||
|
|
||||||
|
Found 2 errors.
|
5
cli/tests/testdata/npm/node_modules_import/package.json
vendored
Normal file
5
cli/tests/testdata/npm/node_modules_import/package.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"@denotest/esm-basic": "^1"
|
||||||
|
}
|
||||||
|
}
|
2
cli/tests/testdata/npm/registry/@denotest/esm-basic/1.0.0/main.d.mts
vendored
Normal file
2
cli/tests/testdata/npm/registry/@denotest/esm-basic/1.0.0/main.d.mts
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export declare function setValue(val: number): void;
|
||||||
|
export declare function getValue(): number;
|
9
cli/tests/testdata/npm/registry/@denotest/esm-basic/1.0.0/main.mjs
vendored
Normal file
9
cli/tests/testdata/npm/registry/@denotest/esm-basic/1.0.0/main.mjs
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
let value = 0;
|
||||||
|
|
||||||
|
export function setValue(newValue) {
|
||||||
|
value = newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
7
cli/tests/testdata/npm/registry/@denotest/esm-basic/1.0.0/package.json
vendored
Normal file
7
cli/tests/testdata/npm/registry/@denotest/esm-basic/1.0.0/package.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"name": "@denotest/esm-basic",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"main": "main.mjs",
|
||||||
|
"types": "main.d.mts"
|
||||||
|
}
|
4
cli/tests/testdata/package_json/basic/fail_check.check.out
vendored
Normal file
4
cli/tests/testdata/package_json/basic/fail_check.check.out
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
error: TS2322 [ERROR]: Type 'number' is not assignable to type 'string'.
|
||||||
|
const _test: string = getValue();
|
||||||
|
~~~~~
|
||||||
|
at file:///[WILDCARD]/fail_check.ts:3:7
|
3
cli/tests/testdata/package_json/basic/fail_check.ts
vendored
Normal file
3
cli/tests/testdata/package_json/basic/fail_check.ts
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import { getValue } from "./main.ts";
|
||||||
|
|
||||||
|
const _test: string = getValue();
|
11
cli/tests/testdata/package_json/basic/main.bench.out
vendored
Normal file
11
cli/tests/testdata/package_json/basic/main.bench.out
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
Download http://localhost:4545/npm/registry/@denotest/esm-basic
|
||||||
|
Download http://localhost:4545/npm/registry/@denotest/esm-basic/1.0.0.tgz
|
||||||
|
Check file:///[WILDCARD]/main.bench.ts
|
||||||
|
0
|
||||||
|
cpu: [WILDCARD]
|
||||||
|
runtime: [WILDCARD]
|
||||||
|
|
||||||
|
file:///[WILDCARD]/main.bench.ts
|
||||||
|
[WILDCARD]
|
||||||
|
-------------------------------------------------- -----------------------------
|
||||||
|
should add [WILDCARD]
|
7
cli/tests/testdata/package_json/basic/main.bench.ts
vendored
Normal file
7
cli/tests/testdata/package_json/basic/main.bench.ts
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { add } from "./main.ts";
|
||||||
|
|
||||||
|
Deno.bench("should add", () => {
|
||||||
|
if (add(1, 2) !== 3) {
|
||||||
|
throw new Error("Fail");
|
||||||
|
}
|
||||||
|
});
|
2
cli/tests/testdata/package_json/basic/main.cache.out
vendored
Normal file
2
cli/tests/testdata/package_json/basic/main.cache.out
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Download http://localhost:4545/npm/registry/@denotest/esm-basic
|
||||||
|
Download http://localhost:4545/npm/registry/@denotest/esm-basic/1.0.0.tgz
|
3
cli/tests/testdata/package_json/basic/main.check.out
vendored
Normal file
3
cli/tests/testdata/package_json/basic/main.check.out
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Download http://localhost:4545/npm/registry/@denotest/esm-basic
|
||||||
|
Download http://localhost:4545/npm/registry/@denotest/esm-basic/1.0.0.tgz
|
||||||
|
Check file://[WILDCARD]/main.ts
|
7
cli/tests/testdata/package_json/basic/main.info.out
vendored
Normal file
7
cli/tests/testdata/package_json/basic/main.info.out
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
local: [WILDCARD]main.ts
|
||||||
|
type: TypeScript
|
||||||
|
dependencies: 1 unique
|
||||||
|
size: [WILDCARD]
|
||||||
|
|
||||||
|
file://[WILDCARD]/package_json/basic/main.ts ([WILDCARD])
|
||||||
|
└── npm:@denotest/esm-basic@1.0.0 ([WILDCARD])
|
9
cli/tests/testdata/package_json/basic/main.test.out
vendored
Normal file
9
cli/tests/testdata/package_json/basic/main.test.out
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
Download http://localhost:4545/npm/registry/@denotest/esm-basic
|
||||||
|
Download http://localhost:4545/npm/registry/@denotest/esm-basic/1.0.0.tgz
|
||||||
|
Check file://[WILDCARD]/main.test.ts
|
||||||
|
0
|
||||||
|
running 1 test from [WILDCARD]main.test.ts
|
||||||
|
should add ... ok ([WILDCARD])
|
||||||
|
|
||||||
|
ok | 1 passed | 0 failed ([WILDCARD])
|
||||||
|
|
7
cli/tests/testdata/package_json/basic/main.test.ts
vendored
Normal file
7
cli/tests/testdata/package_json/basic/main.test.ts
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { add } from "./main.ts";
|
||||||
|
|
||||||
|
Deno.test("should add", () => {
|
||||||
|
if (add(1, 2) !== 3) {
|
||||||
|
throw new Error("Fail");
|
||||||
|
}
|
||||||
|
});
|
11
cli/tests/testdata/package_json/basic/main.ts
vendored
Normal file
11
cli/tests/testdata/package_json/basic/main.ts
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import * as test from "@denotest/esm-basic";
|
||||||
|
|
||||||
|
console.log(test.getValue());
|
||||||
|
|
||||||
|
export function add(a: number, b: number) {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getValue() {
|
||||||
|
return test.getValue();
|
||||||
|
}
|
5
cli/tests/testdata/package_json/basic/package.json
vendored
Normal file
5
cli/tests/testdata/package_json/basic/package.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"@denotest/esm-basic": "*"
|
||||||
|
}
|
||||||
|
}
|
|
@ -556,7 +556,17 @@ fn op_load(state: &mut OpState, args: Value) -> Result<Value, AnyError> {
|
||||||
media_type = MediaType::Json;
|
media_type = MediaType::Json;
|
||||||
Some(Cow::Borrowed(&*module.source))
|
Some(Cow::Borrowed(&*module.source))
|
||||||
}
|
}
|
||||||
Module::External(_) | Module::Npm(_) | Module::Node(_) => None,
|
Module::Npm(_) | Module::Node(_) => None,
|
||||||
|
Module::External(module) => {
|
||||||
|
// means it's Deno code importing an npm module
|
||||||
|
media_type = MediaType::from(&module.specifier);
|
||||||
|
let file_path = specifier.to_file_path().unwrap();
|
||||||
|
let code =
|
||||||
|
std::fs::read_to_string(&file_path).with_context(|| {
|
||||||
|
format!("Unable to load {}", file_path.display())
|
||||||
|
})?;
|
||||||
|
Some(Cow::Owned(code))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if state
|
} else if state
|
||||||
.maybe_npm_resolver
|
.maybe_npm_resolver
|
||||||
|
@ -718,7 +728,16 @@ fn resolve_graph_specifier_types(
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(Module::External(_) | Module::Node(_)) | None => Ok(None),
|
Some(Module::External(module)) => {
|
||||||
|
// we currently only use "External" for when the module is in an npm package
|
||||||
|
Ok(state.maybe_npm_resolver.as_ref().map(|npm_resolver| {
|
||||||
|
NodeResolution::into_specifier_and_media_type(
|
||||||
|
node::url_to_node_resolution(module.specifier.clone(), npm_resolver)
|
||||||
|
.ok(),
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
Some(Module::Node(_)) | None => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ use deno_core::error::AnyError;
|
||||||
use deno_core::serde_json;
|
use deno_core::serde_json;
|
||||||
use deno_core::serde_json::Map;
|
use deno_core::serde_json::Map;
|
||||||
use deno_core::serde_json::Value;
|
use deno_core::serde_json::Value;
|
||||||
|
use deno_core::ModuleSpecifier;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
@ -205,6 +206,10 @@ impl PackageJson {
|
||||||
self.main.as_ref()
|
self.main.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn specifier(&self) -> ModuleSpecifier {
|
||||||
|
ModuleSpecifier::from_file_path(&self.path).unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_conditional_exports_main_sugar(exports: &Value) -> bool {
|
fn is_conditional_exports_main_sugar(exports: &Value) -> bool {
|
||||||
|
|
Loading…
Reference in a new issue