mirror of
https://github.com/denoland/deno.git
synced 2025-01-03 04:48:52 -05:00
fix(npm): resolve node_modules dir relative to package.json instead of cwd (#17885)
This commit is contained in:
parent
a31d8869ea
commit
ddc350780d
12 changed files with 219 additions and 141 deletions
|
@ -18,6 +18,7 @@ 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 std::borrow::Cow;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
@ -483,10 +484,21 @@ pub struct ConfigFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConfigFile {
|
impl ConfigFile {
|
||||||
pub fn discover(flags: &Flags) -> Result<Option<ConfigFile>, AnyError> {
|
pub fn discover(
|
||||||
|
flags: &Flags,
|
||||||
|
cwd: &Path,
|
||||||
|
) -> Result<Option<ConfigFile>, AnyError> {
|
||||||
match &flags.config_flag {
|
match &flags.config_flag {
|
||||||
ConfigFlag::Disabled => Ok(None),
|
ConfigFlag::Disabled => Ok(None),
|
||||||
ConfigFlag::Path(config_path) => Ok(Some(ConfigFile::read(config_path)?)),
|
ConfigFlag::Path(config_path) => {
|
||||||
|
let config_path = PathBuf::from(config_path);
|
||||||
|
let config_path = if config_path.is_absolute() {
|
||||||
|
config_path
|
||||||
|
} else {
|
||||||
|
cwd.join(config_path)
|
||||||
|
};
|
||||||
|
Ok(Some(ConfigFile::read(&config_path)?))
|
||||||
|
}
|
||||||
ConfigFlag::Discover => {
|
ConfigFlag::Discover => {
|
||||||
if let Some(config_path_args) = flags.config_path_args() {
|
if let Some(config_path_args) = flags.config_path_args() {
|
||||||
let mut checked = HashSet::new();
|
let mut checked = HashSet::new();
|
||||||
|
@ -508,8 +520,7 @@ impl ConfigFile {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// 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
|
||||||
let cwd = std::env::current_dir()?;
|
Self::discover_from(cwd, &mut checked)
|
||||||
Self::discover_from(&cwd, &mut checked)
|
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
@ -524,6 +535,14 @@ impl ConfigFile {
|
||||||
/// Filenames that Deno will recognize when discovering config.
|
/// Filenames that Deno will recognize when discovering config.
|
||||||
const CONFIG_FILE_NAMES: [&str; 2] = ["deno.json", "deno.jsonc"];
|
const CONFIG_FILE_NAMES: [&str; 2] = ["deno.json", "deno.jsonc"];
|
||||||
|
|
||||||
|
// todo(dsherret): in the future, we should force all callers
|
||||||
|
// to provide a resolved path
|
||||||
|
let start = if start.is_absolute() {
|
||||||
|
Cow::Borrowed(start)
|
||||||
|
} else {
|
||||||
|
Cow::Owned(std::env::current_dir()?.join(start))
|
||||||
|
};
|
||||||
|
|
||||||
for ancestor in start.ancestors() {
|
for ancestor in start.ancestors() {
|
||||||
if checked.insert(ancestor.to_path_buf()) {
|
if checked.insert(ancestor.to_path_buf()) {
|
||||||
for config_filename in CONFIG_FILE_NAMES {
|
for config_filename in CONFIG_FILE_NAMES {
|
||||||
|
@ -556,34 +575,29 @@ impl ConfigFile {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(path_ref: impl AsRef<Path>) -> Result<Self, AnyError> {
|
pub fn read(config_path: &Path) -> Result<Self, AnyError> {
|
||||||
let path = Path::new(path_ref.as_ref());
|
debug_assert!(config_path.is_absolute());
|
||||||
let config_file = if path.is_absolute() {
|
|
||||||
path.to_path_buf()
|
|
||||||
} else {
|
|
||||||
std::env::current_dir()?.join(path_ref)
|
|
||||||
};
|
|
||||||
|
|
||||||
// perf: Check if the config file exists before canonicalizing path.
|
// perf: Check if the config file exists before canonicalizing path.
|
||||||
if !config_file.exists() {
|
if !config_path.exists() {
|
||||||
return Err(
|
return Err(
|
||||||
std::io::Error::new(
|
std::io::Error::new(
|
||||||
std::io::ErrorKind::InvalidInput,
|
std::io::ErrorKind::InvalidInput,
|
||||||
format!(
|
format!(
|
||||||
"Could not find the config file: {}",
|
"Could not find the config file: {}",
|
||||||
config_file.to_string_lossy()
|
config_path.to_string_lossy()
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let config_path = canonicalize_path(&config_file).map_err(|_| {
|
let config_path = canonicalize_path(config_path).map_err(|_| {
|
||||||
std::io::Error::new(
|
std::io::Error::new(
|
||||||
std::io::ErrorKind::InvalidInput,
|
std::io::ErrorKind::InvalidInput,
|
||||||
format!(
|
format!(
|
||||||
"Could not find the config file: {}",
|
"Could not find the config file: {}",
|
||||||
config_file.to_string_lossy()
|
config_path.to_string_lossy()
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
@ -990,25 +1004,17 @@ mod tests {
|
||||||
use deno_core::serde_json::json;
|
use deno_core::serde_json::json;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_config_file_relative() {
|
|
||||||
let config_file =
|
|
||||||
ConfigFile::read("tests/testdata/module_graph/tsconfig.json")
|
|
||||||
.expect("Failed to load config file");
|
|
||||||
assert!(config_file.json.compiler_options.is_some());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn read_config_file_absolute() {
|
fn read_config_file_absolute() {
|
||||||
let path = test_util::testdata_path().join("module_graph/tsconfig.json");
|
let path = test_util::testdata_path().join("module_graph/tsconfig.json");
|
||||||
let config_file = ConfigFile::read(path.to_str().unwrap())
|
let config_file = ConfigFile::read(&path).unwrap();
|
||||||
.expect("Failed to load config file");
|
|
||||||
assert!(config_file.json.compiler_options.is_some());
|
assert!(config_file.json.compiler_options.is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn include_config_path_on_error() {
|
fn include_config_path_on_error() {
|
||||||
let error = ConfigFile::read("404.json").err().unwrap();
|
let path = test_util::testdata_path().join("404.json");
|
||||||
|
let error = ConfigFile::read(&path).err().unwrap();
|
||||||
assert!(error.to_string().contains("404.json"));
|
assert!(error.to_string().contains("404.json"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
164
cli/args/mod.rs
164
cli/args/mod.rs
|
@ -51,7 +51,6 @@ use deno_runtime::permissions::PermissionsOptions;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
@ -393,38 +392,35 @@ 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> {
|
||||||
pub fn discover_from(
|
fn discover_from(
|
||||||
start: &Path,
|
start: &Path,
|
||||||
checked: &mut HashSet<PathBuf>,
|
|
||||||
maybe_stop_at: Option<PathBuf>,
|
maybe_stop_at: Option<PathBuf>,
|
||||||
) -> Result<Option<PackageJson>, AnyError> {
|
) -> Result<Option<PackageJson>, AnyError> {
|
||||||
const PACKAGE_JSON_NAME: &str = "package.json";
|
const PACKAGE_JSON_NAME: &str = "package.json";
|
||||||
|
|
||||||
for ancestor in start.ancestors() {
|
for ancestor in start.ancestors() {
|
||||||
if checked.insert(ancestor.to_path_buf()) {
|
let path = ancestor.join(PACKAGE_JSON_NAME);
|
||||||
let path = ancestor.join(PACKAGE_JSON_NAME);
|
|
||||||
|
|
||||||
let source = match std::fs::read_to_string(&path) {
|
let source = match std::fs::read_to_string(&path) {
|
||||||
Ok(source) => source,
|
Ok(source) => source,
|
||||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||||
if let Some(stop_at) = maybe_stop_at.as_ref() {
|
if let Some(stop_at) = maybe_stop_at.as_ref() {
|
||||||
if ancestor == stop_at {
|
if ancestor == stop_at {
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
Err(err) => bail!(
|
continue;
|
||||||
"Error loading package.json at {}. {:#}",
|
}
|
||||||
path.display(),
|
Err(err) => bail!(
|
||||||
err
|
"Error loading package.json at {}. {:#}",
|
||||||
),
|
path.display(),
|
||||||
};
|
err
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
let package_json = PackageJson::load_from_string(path.clone(), source)?;
|
let package_json = PackageJson::load_from_string(path.clone(), source)?;
|
||||||
log::debug!("package.json file found at '{}'", path.display());
|
log::debug!("package.json file found at '{}'", path.display());
|
||||||
return Ok(Some(package_json));
|
return Ok(Some(package_json));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// No config file found.
|
// No config file found.
|
||||||
log::debug!("No package.json file found");
|
log::debug!("No package.json file found");
|
||||||
|
@ -434,21 +430,17 @@ fn discover_package_json(
|
||||||
// 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_arg) = flags.package_json_arg() {
|
if let crate::args::DenoSubcommand::Task(TaskFlags {
|
||||||
return discover_from(
|
cwd: Some(path), ..
|
||||||
&package_json_arg,
|
|
||||||
&mut HashSet::new(),
|
|
||||||
maybe_stop_at,
|
|
||||||
);
|
|
||||||
} else if let crate::args::DenoSubcommand::Task(TaskFlags {
|
|
||||||
cwd: Some(path),
|
|
||||||
..
|
|
||||||
}) = &flags.subcommand
|
}) = &flags.subcommand
|
||||||
{
|
{
|
||||||
// attempt to resolve the config file from the task subcommand's
|
// attempt to resolve the config file from the task subcommand's
|
||||||
// `--cwd` when specified
|
// `--cwd` when specified
|
||||||
let task_cwd = canonicalize_path(&PathBuf::from(path))?;
|
let task_cwd = canonicalize_path(&PathBuf::from(path))?;
|
||||||
return discover_from(&task_cwd, &mut HashSet::new(), None);
|
return discover_from(&task_cwd, None);
|
||||||
|
} else if let Some(package_json_arg) = flags.package_json_arg() {
|
||||||
|
let package_json_arg = canonicalize_path(&package_json_arg)?;
|
||||||
|
return discover_from(&package_json_arg, maybe_stop_at);
|
||||||
}
|
}
|
||||||
|
|
||||||
log::debug!("No package.json file found");
|
log::debug!("No package.json file found");
|
||||||
|
@ -542,9 +534,6 @@ pub fn get_root_cert_store(
|
||||||
const RESOLUTION_STATE_ENV_VAR_NAME: &str =
|
const RESOLUTION_STATE_ENV_VAR_NAME: &str =
|
||||||
"DENO_DONT_USE_INTERNAL_NODE_COMPAT_STATE";
|
"DENO_DONT_USE_INTERNAL_NODE_COMPAT_STATE";
|
||||||
|
|
||||||
static IS_NPM_MAIN: Lazy<bool> =
|
|
||||||
Lazy::new(|| std::env::var(RESOLUTION_STATE_ENV_VAR_NAME).is_ok());
|
|
||||||
|
|
||||||
static NPM_PROCESS_STATE: Lazy<Option<NpmProcessState>> = Lazy::new(|| {
|
static NPM_PROCESS_STATE: Lazy<Option<NpmProcessState>> = Lazy::new(|| {
|
||||||
let state = std::env::var(RESOLUTION_STATE_ENV_VAR_NAME).ok()?;
|
let state = std::env::var(RESOLUTION_STATE_ENV_VAR_NAME).ok()?;
|
||||||
let state: NpmProcessState = serde_json::from_str(&state).ok()?;
|
let state: NpmProcessState = serde_json::from_str(&state).ok()?;
|
||||||
|
@ -568,6 +557,7 @@ pub struct CliOptions {
|
||||||
// the source of the options is a detail the rest of the
|
// the source of the options is a detail the rest of the
|
||||||
// application need not concern itself with, so keep these private
|
// application need not concern itself with, so keep these private
|
||||||
flags: Flags,
|
flags: Flags,
|
||||||
|
maybe_node_modules_folder: Option<PathBuf>,
|
||||||
maybe_config_file: Option<ConfigFile>,
|
maybe_config_file: Option<ConfigFile>,
|
||||||
maybe_package_json: Option<PackageJson>,
|
maybe_package_json: Option<PackageJson>,
|
||||||
maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
|
maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
|
||||||
|
@ -577,10 +567,11 @@ pub struct CliOptions {
|
||||||
impl CliOptions {
|
impl CliOptions {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
flags: Flags,
|
flags: Flags,
|
||||||
|
initial_cwd: PathBuf,
|
||||||
maybe_config_file: Option<ConfigFile>,
|
maybe_config_file: Option<ConfigFile>,
|
||||||
maybe_lockfile: Option<Lockfile>,
|
maybe_lockfile: Option<Lockfile>,
|
||||||
maybe_package_json: Option<PackageJson>,
|
maybe_package_json: Option<PackageJson>,
|
||||||
) -> Self {
|
) -> Result<Self, AnyError> {
|
||||||
if let Some(insecure_allowlist) =
|
if let Some(insecure_allowlist) =
|
||||||
flags.unsafely_ignore_certificate_errors.as_ref()
|
flags.unsafely_ignore_certificate_errors.as_ref()
|
||||||
{
|
{
|
||||||
|
@ -596,18 +587,28 @@ impl CliOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
let maybe_lockfile = maybe_lockfile.map(|l| Arc::new(Mutex::new(l)));
|
let maybe_lockfile = maybe_lockfile.map(|l| Arc::new(Mutex::new(l)));
|
||||||
|
let maybe_node_modules_folder = resolve_local_node_modules_folder(
|
||||||
|
&initial_cwd,
|
||||||
|
&flags,
|
||||||
|
maybe_config_file.as_ref(),
|
||||||
|
maybe_package_json.as_ref(),
|
||||||
|
)
|
||||||
|
.with_context(|| "Resolving node_modules folder.")?;
|
||||||
|
|
||||||
Self {
|
Ok(Self {
|
||||||
|
flags,
|
||||||
maybe_config_file,
|
maybe_config_file,
|
||||||
maybe_lockfile,
|
maybe_lockfile,
|
||||||
maybe_package_json,
|
maybe_package_json,
|
||||||
flags,
|
maybe_node_modules_folder,
|
||||||
overrides: Default::default(),
|
overrides: Default::default(),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_flags(flags: Flags) -> Result<Self, AnyError> {
|
pub fn from_flags(flags: Flags) -> Result<Self, AnyError> {
|
||||||
let maybe_config_file = ConfigFile::discover(&flags)?;
|
let initial_cwd =
|
||||||
|
std::env::current_dir().with_context(|| "Failed getting cwd.")?;
|
||||||
|
let maybe_config_file = ConfigFile::discover(&flags, &initial_cwd)?;
|
||||||
|
|
||||||
let mut maybe_package_json = None;
|
let mut maybe_package_json = None;
|
||||||
if let Some(config_file) = &maybe_config_file {
|
if let Some(config_file) = &maybe_config_file {
|
||||||
|
@ -626,12 +627,13 @@ impl CliOptions {
|
||||||
}
|
}
|
||||||
let maybe_lock_file =
|
let maybe_lock_file =
|
||||||
lockfile::discover(&flags, maybe_config_file.as_ref())?;
|
lockfile::discover(&flags, maybe_config_file.as_ref())?;
|
||||||
Ok(Self::new(
|
Self::new(
|
||||||
flags,
|
flags,
|
||||||
|
initial_cwd,
|
||||||
maybe_config_file,
|
maybe_config_file,
|
||||||
maybe_lock_file,
|
maybe_lock_file,
|
||||||
maybe_package_json,
|
maybe_package_json,
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn maybe_config_file_specifier(&self) -> Option<ModuleSpecifier> {
|
pub fn maybe_config_file_specifier(&self) -> Option<ModuleSpecifier> {
|
||||||
|
@ -705,16 +707,8 @@ impl CliOptions {
|
||||||
.map(Some)
|
.map(Some)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_npm_process_state(&self) -> Option<&NpmProcessState> {
|
|
||||||
if !self.is_npm_main() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*NPM_PROCESS_STATE).as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_npm_resolution_snapshot(&self) -> Option<NpmResolutionSnapshot> {
|
pub fn get_npm_resolution_snapshot(&self) -> Option<NpmResolutionSnapshot> {
|
||||||
if let Some(state) = self.get_npm_process_state() {
|
if let Some(state) = &*NPM_PROCESS_STATE {
|
||||||
// TODO(bartlomieju): remove this clone
|
// TODO(bartlomieju): remove this clone
|
||||||
return Some(state.snapshot.clone());
|
return Some(state.snapshot.clone());
|
||||||
}
|
}
|
||||||
|
@ -727,7 +721,7 @@ impl CliOptions {
|
||||||
// for functionality like child_process.fork. Users should NOT depend
|
// for functionality like child_process.fork. Users should NOT depend
|
||||||
// on this functionality.
|
// on this functionality.
|
||||||
pub fn is_npm_main(&self) -> bool {
|
pub fn is_npm_main(&self) -> bool {
|
||||||
*IS_NPM_MAIN
|
NPM_PROCESS_STATE.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Overrides the import map specifier to use.
|
/// Overrides the import map specifier to use.
|
||||||
|
@ -735,36 +729,19 @@ impl CliOptions {
|
||||||
self.overrides.import_map_specifier = Some(path);
|
self.overrides.import_map_specifier = Some(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn node_modules_dir(&self) -> bool {
|
pub fn has_node_modules_dir(&self) -> bool {
|
||||||
if let Some(node_modules_dir) = self.flags.node_modules_dir {
|
self.maybe_node_modules_folder.is_some()
|
||||||
return node_modules_dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(npm_process_state) = self.get_npm_process_state() {
|
|
||||||
return npm_process_state.local_node_modules_path.is_some();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.maybe_package_json.is_some()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves the path to use for a local node_modules folder.
|
pub fn node_modules_dir_path(&self) -> Option<PathBuf> {
|
||||||
pub fn resolve_local_node_modules_folder(
|
self.maybe_node_modules_folder.clone()
|
||||||
&self,
|
}
|
||||||
) -> Result<Option<PathBuf>, AnyError> {
|
|
||||||
let path = if !self.node_modules_dir() {
|
pub fn node_modules_dir_specifier(&self) -> Option<ModuleSpecifier> {
|
||||||
return Ok(None);
|
self
|
||||||
} else if let Some(state) = self.get_npm_process_state() {
|
.maybe_node_modules_folder
|
||||||
return Ok(state.local_node_modules_path.as_ref().map(PathBuf::from));
|
|
||||||
} else if let Some(config_path) = self
|
|
||||||
.maybe_config_file
|
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|c| c.specifier.to_file_path().ok())
|
.map(|path| ModuleSpecifier::from_directory_path(path).unwrap())
|
||||||
{
|
|
||||||
config_path.parent().unwrap().join("node_modules")
|
|
||||||
} else {
|
|
||||||
std::env::current_dir()?.join("node_modules")
|
|
||||||
};
|
|
||||||
Ok(Some(canonicalize_path_maybe_not_exists(&path)?))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_root_cert_store(&self) -> Result<RootCertStore, AnyError> {
|
pub fn resolve_root_cert_store(&self) -> Result<RootCertStore, AnyError> {
|
||||||
|
@ -1081,6 +1058,33 @@ impl CliOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolves the path to use for a local node_modules folder.
|
||||||
|
fn resolve_local_node_modules_folder(
|
||||||
|
cwd: &Path,
|
||||||
|
flags: &Flags,
|
||||||
|
maybe_config_file: Option<&ConfigFile>,
|
||||||
|
maybe_package_json: Option<&PackageJson>,
|
||||||
|
) -> Result<Option<PathBuf>, AnyError> {
|
||||||
|
let path = if flags.node_modules_dir == Some(false) {
|
||||||
|
return Ok(None);
|
||||||
|
} else if let Some(state) = &*NPM_PROCESS_STATE {
|
||||||
|
return Ok(state.local_node_modules_path.as_ref().map(PathBuf::from));
|
||||||
|
} else if let Some(package_json_path) = maybe_package_json.map(|c| &c.path) {
|
||||||
|
// always auto-discover the local_node_modules_folder when a package.json exists
|
||||||
|
package_json_path.parent().unwrap().join("node_modules")
|
||||||
|
} else if flags.node_modules_dir.is_none() {
|
||||||
|
return Ok(None);
|
||||||
|
} else if let Some(config_path) = maybe_config_file
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|c| c.specifier.to_file_path().ok())
|
||||||
|
{
|
||||||
|
config_path.parent().unwrap().join("node_modules")
|
||||||
|
} else {
|
||||||
|
cwd.join("node_modules")
|
||||||
|
};
|
||||||
|
Ok(Some(canonicalize_path_maybe_not_exists(&path)?))
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_import_map_specifier(
|
fn resolve_import_map_specifier(
|
||||||
maybe_import_map_path: Option<&str>,
|
maybe_import_map_path: Option<&str>,
|
||||||
maybe_config_file: Option<&ConfigFile>,
|
maybe_config_file: Option<&ConfigFile>,
|
||||||
|
|
|
@ -14,7 +14,6 @@ use crate::resolver::CliGraphResolver;
|
||||||
use crate::tools::check;
|
use crate::tools::check;
|
||||||
|
|
||||||
use deno_core::anyhow::bail;
|
use deno_core::anyhow::bail;
|
||||||
use deno_core::anyhow::Context;
|
|
||||||
use deno_core::error::custom_error;
|
use deno_core::error::custom_error;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::ModuleSpecifier;
|
use deno_core::ModuleSpecifier;
|
||||||
|
@ -153,10 +152,7 @@ pub async fn create_graph_and_maybe_check(
|
||||||
ps.file_fetcher.clone(),
|
ps.file_fetcher.clone(),
|
||||||
PermissionsContainer::allow_all(),
|
PermissionsContainer::allow_all(),
|
||||||
PermissionsContainer::allow_all(),
|
PermissionsContainer::allow_all(),
|
||||||
ps.options
|
ps.options.node_modules_dir_specifier(),
|
||||||
.resolve_local_node_modules_folder()
|
|
||||||
.with_context(|| "Resolving local node_modules folder.")?
|
|
||||||
.map(|path| ModuleSpecifier::from_file_path(path).unwrap()),
|
|
||||||
);
|
);
|
||||||
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()?;
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use deno_ast::MediaType;
|
use deno_ast::MediaType;
|
||||||
use deno_core::anyhow::anyhow;
|
use deno_core::anyhow::anyhow;
|
||||||
|
use deno_core::anyhow::Context;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::resolve_url;
|
use deno_core::resolve_url;
|
||||||
use deno_core::serde_json;
|
use deno_core::serde_json;
|
||||||
|
@ -168,7 +169,7 @@ impl LanguageServer {
|
||||||
.map(|d| (d.specifier().clone(), d))
|
.map(|d| (d.specifier().clone(), d))
|
||||||
.collect::<HashMap<_, _>>();
|
.collect::<HashMap<_, _>>();
|
||||||
let ps = ProcState::from_options(Arc::new(cli_options)).await?;
|
let ps = ProcState::from_options(Arc::new(cli_options)).await?;
|
||||||
let mut inner_loader = ps.create_graph_loader()?;
|
let mut inner_loader = ps.create_graph_loader();
|
||||||
let mut loader = crate::lsp::documents::OpenDocumentsGraphLoader {
|
let mut loader = crate::lsp::documents::OpenDocumentsGraphLoader {
|
||||||
inner_loader: &mut inner_loader,
|
inner_loader: &mut inner_loader,
|
||||||
open_docs: &open_docs,
|
open_docs: &open_docs,
|
||||||
|
@ -191,11 +192,23 @@ impl LanguageServer {
|
||||||
match params.map(serde_json::from_value) {
|
match params.map(serde_json::from_value) {
|
||||||
Some(Ok(params)) => {
|
Some(Ok(params)) => {
|
||||||
// do as much as possible in a read, then do a write outside
|
// do as much as possible in a read, then do a write outside
|
||||||
let result = {
|
let maybe_cache_result = {
|
||||||
let inner = self.0.read().await; // ensure dropped
|
let inner = self.0.read().await; // ensure dropped
|
||||||
inner.prepare_cache(params)?
|
match inner.prepare_cache(params) {
|
||||||
|
Ok(maybe_cache_result) => maybe_cache_result,
|
||||||
|
Err(err) => {
|
||||||
|
self
|
||||||
|
.0
|
||||||
|
.read()
|
||||||
|
.await
|
||||||
|
.client
|
||||||
|
.show_message(MessageType::WARNING, err)
|
||||||
|
.await;
|
||||||
|
return Err(LspError::internal_error());
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
if let Some(result) = result {
|
if let Some(result) = maybe_cache_result {
|
||||||
let cli_options = result.cli_options;
|
let cli_options = result.cli_options;
|
||||||
let roots = result.roots;
|
let roots = result.roots;
|
||||||
let open_docs = result.open_docs;
|
let open_docs = result.open_docs;
|
||||||
|
@ -2993,7 +3006,7 @@ impl Inner {
|
||||||
fn prepare_cache(
|
fn prepare_cache(
|
||||||
&self,
|
&self,
|
||||||
params: lsp_custom::CacheParams,
|
params: lsp_custom::CacheParams,
|
||||||
) -> LspResult<Option<PrepareCacheResult>> {
|
) -> Result<Option<PrepareCacheResult>, AnyError> {
|
||||||
let referrer = self.url_map.normalize_url(¶ms.referrer.uri);
|
let referrer = self.url_map.normalize_url(¶ms.referrer.uri);
|
||||||
if !self.is_diagnosable(&referrer) {
|
if !self.is_diagnosable(&referrer) {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
|
@ -3021,12 +3034,13 @@ impl Inner {
|
||||||
unstable: true,
|
unstable: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
std::env::current_dir().with_context(|| "Failed getting cwd.")?,
|
||||||
self.maybe_config_file.clone(),
|
self.maybe_config_file.clone(),
|
||||||
// TODO(#16510): add support for lockfile
|
// TODO(#16510): add support for lockfile
|
||||||
None,
|
None,
|
||||||
// TODO(bartlomieju): handle package.json dependencies here
|
// TODO(bartlomieju): handle package.json dependencies here
|
||||||
None,
|
None,
|
||||||
);
|
)?;
|
||||||
cli_options.set_import_map_specifier(self.maybe_import_map_uri.clone());
|
cli_options.set_import_map_specifier(self.maybe_import_map_uri.clone());
|
||||||
|
|
||||||
let open_docs = self.documents.documents(true, true);
|
let open_docs = self.documents.documents(true, true);
|
||||||
|
|
|
@ -224,9 +224,7 @@ impl ProcState {
|
||||||
let npm_resolver = NpmPackageResolver::new_with_maybe_lockfile(
|
let npm_resolver = NpmPackageResolver::new_with_maybe_lockfile(
|
||||||
npm_cache.clone(),
|
npm_cache.clone(),
|
||||||
api,
|
api,
|
||||||
cli_options
|
cli_options.node_modules_dir_path(),
|
||||||
.resolve_local_node_modules_folder()
|
|
||||||
.with_context(|| "Resolving local node_modules folder.")?,
|
|
||||||
cli_options.get_npm_resolution_snapshot(),
|
cli_options.get_npm_resolution_snapshot(),
|
||||||
lockfile.as_ref().cloned(),
|
lockfile.as_ref().cloned(),
|
||||||
)
|
)
|
||||||
|
@ -329,11 +327,7 @@ impl ProcState {
|
||||||
self.file_fetcher.clone(),
|
self.file_fetcher.clone(),
|
||||||
root_permissions,
|
root_permissions,
|
||||||
dynamic_permissions,
|
dynamic_permissions,
|
||||||
self
|
self.options.node_modules_dir_specifier(),
|
||||||
.options
|
|
||||||
.resolve_local_node_modules_folder()
|
|
||||||
.with_context(|| "Resolving local node_modules folder.")?
|
|
||||||
.map(|path| ModuleSpecifier::from_file_path(path).unwrap()),
|
|
||||||
);
|
);
|
||||||
let maybe_imports = self.options.to_maybe_imports()?;
|
let maybe_imports = self.options.to_maybe_imports()?;
|
||||||
let graph_resolver = self.resolver.as_graph_resolver();
|
let graph_resolver = self.resolver.as_graph_resolver();
|
||||||
|
@ -632,25 +626,21 @@ impl ProcState {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates the default loader used for creating a graph.
|
/// Creates the default loader used for creating a graph.
|
||||||
pub fn create_graph_loader(&self) -> Result<cache::FetchCacher, AnyError> {
|
pub fn create_graph_loader(&self) -> cache::FetchCacher {
|
||||||
Ok(cache::FetchCacher::new(
|
cache::FetchCacher::new(
|
||||||
self.emit_cache.clone(),
|
self.emit_cache.clone(),
|
||||||
self.file_fetcher.clone(),
|
self.file_fetcher.clone(),
|
||||||
PermissionsContainer::allow_all(),
|
PermissionsContainer::allow_all(),
|
||||||
PermissionsContainer::allow_all(),
|
PermissionsContainer::allow_all(),
|
||||||
self
|
self.options.node_modules_dir_specifier(),
|
||||||
.options
|
)
|
||||||
.resolve_local_node_modules_folder()
|
|
||||||
.with_context(|| "Resolving local node_modules folder.")?
|
|
||||||
.map(|path| ModuleSpecifier::from_file_path(path).unwrap()),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_graph(
|
pub async fn create_graph(
|
||||||
&self,
|
&self,
|
||||||
roots: Vec<ModuleSpecifier>,
|
roots: Vec<ModuleSpecifier>,
|
||||||
) -> Result<deno_graph::ModuleGraph, AnyError> {
|
) -> Result<deno_graph::ModuleGraph, AnyError> {
|
||||||
let mut cache = self.create_graph_loader()?;
|
let mut cache = self.create_graph_loader();
|
||||||
self.create_graph_with_loader(roots, &mut cache).await
|
self.create_graph_with_loader(roots, &mut cache).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2753,6 +2753,8 @@ itest!(package_json_auto_discovered_for_local_script_log {
|
||||||
args: "run -L debug -A no_deno_json/main.ts",
|
args: "run -L debug -A no_deno_json/main.ts",
|
||||||
output: "run/with_package_json/no_deno_json/main.out",
|
output: "run/with_package_json/no_deno_json/main.out",
|
||||||
maybe_cwd: Some("run/with_package_json/"),
|
maybe_cwd: Some("run/with_package_json/"),
|
||||||
|
// prevent creating a node_modules dir in the code directory
|
||||||
|
copy_temp_dir: Some("run/with_package_json/"),
|
||||||
envs: env_vars_for_npm_tests_no_sync_download(),
|
envs: env_vars_for_npm_tests_no_sync_download(),
|
||||||
http_server: true,
|
http_server: true,
|
||||||
});
|
});
|
||||||
|
@ -2764,16 +2766,29 @@ itest!(
|
||||||
args: "run -L debug with_stop/some/nested/dir/main.ts",
|
args: "run -L debug with_stop/some/nested/dir/main.ts",
|
||||||
output: "run/with_package_json/with_stop/main.out",
|
output: "run/with_package_json/with_stop/main.out",
|
||||||
maybe_cwd: Some("run/with_package_json/"),
|
maybe_cwd: Some("run/with_package_json/"),
|
||||||
|
copy_temp_dir: Some("run/with_package_json/"),
|
||||||
envs: env_vars_for_npm_tests_no_sync_download(),
|
envs: env_vars_for_npm_tests_no_sync_download(),
|
||||||
http_server: true,
|
http_server: true,
|
||||||
exit_code: 1,
|
exit_code: 1,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
itest!(
|
||||||
|
package_json_auto_discovered_node_modules_relative_package_json {
|
||||||
|
args: "run -A main.js",
|
||||||
|
output: "run/with_package_json/no_deno_json/sub_dir/main.out",
|
||||||
|
maybe_cwd: Some("run/with_package_json/no_deno_json/sub_dir"),
|
||||||
|
copy_temp_dir: Some("run/with_package_json/"),
|
||||||
|
envs: env_vars_for_npm_tests_no_sync_download(),
|
||||||
|
http_server: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
itest!(package_json_auto_discovered_for_npm_binary {
|
itest!(package_json_auto_discovered_for_npm_binary {
|
||||||
args: "run -L debug -A npm:@denotest/bin/cli-esm this is a test",
|
args: "run -L debug -A npm:@denotest/bin/cli-esm this is a test",
|
||||||
output: "run/with_package_json/npm_binary/main.out",
|
output: "run/with_package_json/npm_binary/main.out",
|
||||||
maybe_cwd: Some("run/with_package_json/npm_binary/"),
|
maybe_cwd: Some("run/with_package_json/npm_binary/"),
|
||||||
|
copy_temp_dir: Some("run/with_package_json/"),
|
||||||
envs: env_vars_for_npm_tests_no_sync_download(),
|
envs: env_vars_for_npm_tests_no_sync_download(),
|
||||||
http_server: true,
|
http_server: true,
|
||||||
});
|
});
|
||||||
|
|
2
cli/tests/testdata/run/with_package_json/no_deno_json/sub_dir/main.js
vendored
Normal file
2
cli/tests/testdata/run/with_package_json/no_deno_json/sub_dir/main.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
console.log(Deno.cwd());
|
||||||
|
console.log(Deno.statSync("../node_modules"));
|
7
cli/tests/testdata/run/with_package_json/no_deno_json/sub_dir/main.out
vendored
Normal file
7
cli/tests/testdata/run/with_package_json/no_deno_json/sub_dir/main.out
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
Download http://[WILDCARD]
|
||||||
|
[WILDCARD]sub_dir
|
||||||
|
{
|
||||||
|
[WILDCARD]
|
||||||
|
isDirectory: true,
|
||||||
|
[WILDCARD]
|
||||||
|
}
|
|
@ -34,7 +34,7 @@ pub async fn info(flags: Flags, info_flags: InfoFlags) -> Result<(), AnyError> {
|
||||||
let ps = ProcState::build(flags).await?;
|
let ps = ProcState::build(flags).await?;
|
||||||
if let Some(specifier) = info_flags.file {
|
if let Some(specifier) = info_flags.file {
|
||||||
let specifier = resolve_url_or_path(&specifier)?;
|
let specifier = resolve_url_or_path(&specifier)?;
|
||||||
let mut loader = ps.create_graph_loader()?;
|
let mut loader = ps.create_graph_loader();
|
||||||
loader.enable_loading_cache_info(); // for displaying the cache information
|
loader.enable_loading_cache_info(); // for displaying the cache information
|
||||||
let graph = ps
|
let graph = ps
|
||||||
.create_graph_with_loader(vec![specifier], &mut loader)
|
.create_graph_with_loader(vec![specifier], &mut loader)
|
||||||
|
|
|
@ -463,7 +463,7 @@ impl ReplSession {
|
||||||
if !self.has_initialized_node_runtime {
|
if !self.has_initialized_node_runtime {
|
||||||
deno_node::initialize_runtime(
|
deno_node::initialize_runtime(
|
||||||
&mut self.worker.js_runtime,
|
&mut self.worker.js_runtime,
|
||||||
self.proc_state.options.node_modules_dir(),
|
self.proc_state.options.has_node_modules_dir(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
self.has_initialized_node_runtime = true;
|
self.has_initialized_node_runtime = true;
|
||||||
|
|
|
@ -304,7 +304,7 @@ impl CliMainWorker {
|
||||||
async fn initialize_main_module_for_node(&mut self) -> Result<(), AnyError> {
|
async fn initialize_main_module_for_node(&mut self) -> Result<(), AnyError> {
|
||||||
deno_node::initialize_runtime(
|
deno_node::initialize_runtime(
|
||||||
&mut self.worker.js_runtime,
|
&mut self.worker.js_runtime,
|
||||||
self.ps.options.node_modules_dir(),
|
self.ps.options.has_node_modules_dir(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
if let DenoSubcommand::Run(flags) = self.ps.options.sub_command() {
|
if let DenoSubcommand::Run(flags) = self.ps.options.sub_command() {
|
||||||
|
@ -631,7 +631,7 @@ fn create_web_worker_pre_execute_module_callback(
|
||||||
if ps.npm_resolver.has_packages() {
|
if ps.npm_resolver.has_packages() {
|
||||||
deno_node::initialize_runtime(
|
deno_node::initialize_runtime(
|
||||||
&mut worker.js_runtime,
|
&mut worker.js_runtime,
|
||||||
ps.options.node_modules_dir(),
|
ps.options.has_node_modules_dir(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ use std::mem::replace;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
|
use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::process::Child;
|
use std::process::Child;
|
||||||
|
@ -1923,12 +1924,17 @@ pub struct CheckOutputIntegrationTest<'a> {
|
||||||
pub envs: Vec<(String, String)>,
|
pub envs: Vec<(String, String)>,
|
||||||
pub env_clear: bool,
|
pub env_clear: bool,
|
||||||
pub temp_cwd: bool,
|
pub temp_cwd: bool,
|
||||||
// Relative to "testdata" directory
|
/// Copies the files at the specified directory in the "testdata" directory
|
||||||
|
/// to the temp folder and runs the test from there. This is useful when
|
||||||
|
/// the test creates files in the testdata directory (ex. a node_modules folder)
|
||||||
|
pub copy_temp_dir: Option<&'a str>,
|
||||||
|
/// Relative to "testdata" directory
|
||||||
pub maybe_cwd: Option<&'a str>,
|
pub maybe_cwd: Option<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CheckOutputIntegrationTest<'a> {
|
impl<'a> CheckOutputIntegrationTest<'a> {
|
||||||
pub fn run(&self) {
|
pub fn run(&self) {
|
||||||
|
let deno_dir = new_deno_dir(); // keep this alive for the test
|
||||||
let args = if self.args_vec.is_empty() {
|
let args = if self.args_vec.is_empty() {
|
||||||
std::borrow::Cow::Owned(self.args.split_whitespace().collect::<Vec<_>>())
|
std::borrow::Cow::Owned(self.args.split_whitespace().collect::<Vec<_>>())
|
||||||
} else {
|
} else {
|
||||||
|
@ -1938,7 +1944,15 @@ impl<'a> CheckOutputIntegrationTest<'a> {
|
||||||
);
|
);
|
||||||
std::borrow::Cow::Borrowed(&self.args_vec)
|
std::borrow::Cow::Borrowed(&self.args_vec)
|
||||||
};
|
};
|
||||||
let testdata_dir = testdata_path();
|
let testdata_dir = if let Some(temp_copy_dir) = &self.copy_temp_dir {
|
||||||
|
let test_data_path = testdata_path().join(temp_copy_dir);
|
||||||
|
let temp_copy_dir = deno_dir.path().join(temp_copy_dir);
|
||||||
|
std::fs::create_dir_all(&temp_copy_dir).unwrap();
|
||||||
|
copy_dir_recursive(&test_data_path, &temp_copy_dir).unwrap();
|
||||||
|
deno_dir.path().to_owned()
|
||||||
|
} else {
|
||||||
|
testdata_path()
|
||||||
|
};
|
||||||
let args = args
|
let args = args
|
||||||
.iter()
|
.iter()
|
||||||
.map(|arg| arg.replace("$TESTDATA", &testdata_dir.to_string_lossy()))
|
.map(|arg| arg.replace("$TESTDATA", &testdata_dir.to_string_lossy()))
|
||||||
|
@ -1953,7 +1967,6 @@ impl<'a> CheckOutputIntegrationTest<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let (mut reader, writer) = pipe().unwrap();
|
let (mut reader, writer) = pipe().unwrap();
|
||||||
let deno_dir = new_deno_dir(); // keep this alive for the test
|
|
||||||
let mut command = deno_cmd_with_deno_dir(&deno_dir);
|
let mut command = deno_cmd_with_deno_dir(&deno_dir);
|
||||||
let cwd = if self.temp_cwd {
|
let cwd = if self.temp_cwd {
|
||||||
deno_dir.path().to_owned()
|
deno_dir.path().to_owned()
|
||||||
|
@ -2328,6 +2341,37 @@ pub fn parse_max_mem(output: &str) -> Option<u64> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Copies a directory to another directory.
|
||||||
|
///
|
||||||
|
/// Note: Does not handle symlinks.
|
||||||
|
pub fn copy_dir_recursive(from: &Path, to: &Path) -> Result<(), anyhow::Error> {
|
||||||
|
use anyhow::Context;
|
||||||
|
|
||||||
|
std::fs::create_dir_all(to)
|
||||||
|
.with_context(|| format!("Creating {}", to.display()))?;
|
||||||
|
let read_dir = std::fs::read_dir(from)
|
||||||
|
.with_context(|| format!("Reading {}", from.display()))?;
|
||||||
|
|
||||||
|
for entry in read_dir {
|
||||||
|
let entry = entry?;
|
||||||
|
let file_type = entry.file_type()?;
|
||||||
|
let new_from = from.join(entry.file_name());
|
||||||
|
let new_to = to.join(entry.file_name());
|
||||||
|
|
||||||
|
if file_type.is_dir() {
|
||||||
|
copy_dir_recursive(&new_from, &new_to).with_context(|| {
|
||||||
|
format!("Dir {} to {}", new_from.display(), new_to.display())
|
||||||
|
})?;
|
||||||
|
} else if file_type.is_file() {
|
||||||
|
std::fs::copy(&new_from, &new_to).with_context(|| {
|
||||||
|
format!("Copying {} to {}", new_from.display(), new_to.display())
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
Loading…
Reference in a new issue