summaryrefslogtreecommitdiff
path: root/cli/args
diff options
context:
space:
mode:
Diffstat (limited to 'cli/args')
-rw-r--r--cli/args/flags.rs160
-rw-r--r--cli/args/import_map.rs118
-rw-r--r--cli/args/mod.rs1027
-rw-r--r--cli/args/package_json.rs128
4 files changed, 657 insertions, 776 deletions
diff --git a/cli/args/flags.rs b/cli/args/flags.rs
index 5f58911c2..56fb4f09d 100644
--- a/cli/args/flags.rs
+++ b/cli/args/flags.rs
@@ -9,11 +9,13 @@ use clap::ArgMatches;
use clap::ColorChoice;
use clap::Command;
use clap::ValueHint;
+use deno_config::glob::FilePatterns;
use deno_config::glob::PathOrPatternSet;
use deno_config::ConfigFlag;
use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
+use deno_core::normalize_path;
use deno_core::resolve_url_or_path;
use deno_core::url::Url;
use deno_graph::GraphKind;
@@ -34,6 +36,7 @@ use std::path::PathBuf;
use std::str::FromStr;
use crate::args::resolve_no_prompt;
+use crate::util::collections::CheckedSet;
use crate::util::fs::canonicalize_path;
use super::flags_net;
@@ -45,6 +48,29 @@ pub struct FileFlags {
pub include: Vec<String>,
}
+impl FileFlags {
+ pub fn as_file_patterns(
+ &self,
+ base: &Path,
+ ) -> Result<FilePatterns, AnyError> {
+ Ok(FilePatterns {
+ include: if self.include.is_empty() {
+ None
+ } else {
+ Some(PathOrPatternSet::from_include_relative_path_or_patterns(
+ base,
+ &self.include,
+ )?)
+ },
+ exclude: PathOrPatternSet::from_exclude_relative_path_or_patterns(
+ base,
+ &self.ignore,
+ )?,
+ base: base.to_path_buf(),
+ })
+ }
+}
+
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct AddFlags {
pub packages: Vec<String>,
@@ -156,7 +182,7 @@ pub struct EvalFlags {
pub code: String,
}
-#[derive(Clone, Debug, Eq, PartialEq)]
+#[derive(Clone, Default, Debug, Eq, PartialEq)]
pub struct FmtFlags {
pub check: bool,
pub files: FileFlags,
@@ -235,7 +261,7 @@ pub struct UninstallFlags {
pub kind: UninstallKind,
}
-#[derive(Clone, Debug, Eq, PartialEq)]
+#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct LintFlags {
pub files: FileFlags,
pub rules: bool,
@@ -323,7 +349,7 @@ pub struct TaskFlags {
pub task: Option<String>,
}
-#[derive(Clone, Debug, Default, Eq, PartialEq)]
+#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum TestReporterConfig {
#[default]
Pretty,
@@ -838,30 +864,54 @@ impl Flags {
args
}
- /// Extract path arguments for config search paths.
- /// If it returns Some(vec), the config should be discovered
- /// from the passed `current_dir` after trying to discover from each entry in
- /// the returned vector.
- /// If it returns None, the config file shouldn't be discovered at all.
+ /// Extract the directory paths the config file should be discovered from.
+ ///
+ /// Returns `None` if the config file should not be auto-discovered.
pub fn config_path_args(&self, current_dir: &Path) -> Option<Vec<PathBuf>> {
- use DenoSubcommand::*;
+ fn resolve_multiple_files(
+ files: &[String],
+ current_dir: &Path,
+ ) -> Vec<PathBuf> {
+ let mut seen = CheckedSet::with_capacity(files.len());
+ let result = files
+ .iter()
+ .filter_map(|p| {
+ let path = normalize_path(current_dir.join(p).parent()?);
+ if seen.insert(&path) {
+ Some(path)
+ } else {
+ None
+ }
+ })
+ .collect::<Vec<_>>();
+ if result.is_empty() {
+ vec![current_dir.to_path_buf()]
+ } else {
+ result
+ }
+ }
+ use DenoSubcommand::*;
match &self.subcommand {
Fmt(FmtFlags { files, .. }) => {
- Some(files.include.iter().map(|p| current_dir.join(p)).collect())
+ Some(resolve_multiple_files(&files.include, current_dir))
}
Lint(LintFlags { files, .. }) => {
- Some(files.include.iter().map(|p| current_dir.join(p)).collect())
+ Some(resolve_multiple_files(&files.include, current_dir))
}
- Run(RunFlags { script, .. }) => {
+ Run(RunFlags { script, .. })
+ | Compile(CompileFlags {
+ source_file: script,
+ ..
+ }) => {
if let Ok(module_specifier) = resolve_url_or_path(script, current_dir) {
if module_specifier.scheme() == "file"
|| module_specifier.scheme() == "npm"
{
if let Ok(p) = module_specifier.to_file_path() {
- Some(vec![p])
+ Some(vec![p.parent().unwrap().to_path_buf()])
} else {
- Some(vec![])
+ Some(vec![current_dir.to_path_buf()])
}
} else {
// When the entrypoint doesn't have file: scheme (it's the remote
@@ -869,7 +919,7 @@ impl Flags {
None
}
} else {
- Some(vec![])
+ Some(vec![current_dir.to_path_buf()])
}
}
Task(TaskFlags {
@@ -880,57 +930,10 @@ impl Flags {
// `--cwd` when specified
match canonicalize_path(&PathBuf::from(path)) {
Ok(path) => Some(vec![path]),
- Err(_) => Some(vec![]),
- }
- }
- _ => Some(vec![]),
- }
- }
-
- /// Extract path argument for `package.json` search paths.
- /// If it returns Some(path), the `package.json` should be discovered
- /// from the `path` dir.
- /// If it returns None, the `package.json` file shouldn't be discovered at
- /// all.
- pub fn package_json_search_dir(&self, current_dir: &Path) -> Option<PathBuf> {
- use DenoSubcommand::*;
-
- match &self.subcommand {
- Run(RunFlags { script, .. }) | Serve(ServeFlags { script, .. }) => {
- let module_specifier = resolve_url_or_path(script, current_dir).ok()?;
- if module_specifier.scheme() == "file" {
- let p = module_specifier
- .to_file_path()
- .unwrap()
- .parent()?
- .to_owned();
- Some(p)
- } else if module_specifier.scheme() == "npm" {
- Some(current_dir.to_path_buf())
- } else {
- None
- }
- }
- Task(TaskFlags { cwd: Some(cwd), .. }) => {
- resolve_url_or_path(cwd, current_dir)
- .ok()?
- .to_file_path()
- .ok()
- }
- Task(_) | Check(_) | Coverage(_) | Cache(_) | Info(_) | Eval(_)
- | Test(_) | Bench(_) | Repl(_) | Compile(_) | Publish(_) => {
- Some(current_dir.to_path_buf())
- }
- Add(_) | Bundle(_) | Completions(_) | Doc(_) | Fmt(_) | Init(_)
- | Uninstall(_) | Jupyter(_) | Lsp | Lint(_) | Types | Upgrade(_)
- | Vendor(_) => None,
- Install(_) => {
- if *DENO_FUTURE {
- Some(current_dir.to_path_buf())
- } else {
- None
+ Err(_) => Some(vec![current_dir.to_path_buf()]),
}
}
+ _ => Some(vec![current_dir.to_path_buf()]),
}
}
@@ -9271,7 +9274,15 @@ mod tests {
fn test_config_path_args() {
let flags = flags_from_vec(svec!["deno", "run", "foo.js"]).unwrap();
let cwd = std::env::current_dir().unwrap();
- assert_eq!(flags.config_path_args(&cwd), Some(vec![cwd.join("foo.js")]));
+
+ assert_eq!(flags.config_path_args(&cwd), Some(vec![cwd.clone()]));
+
+ let flags = flags_from_vec(svec!["deno", "run", "sub_dir/foo.js"]).unwrap();
+ let cwd = std::env::current_dir().unwrap();
+ assert_eq!(
+ flags.config_path_args(&cwd),
+ Some(vec![cwd.join("sub_dir").clone()])
+ );
let flags =
flags_from_vec(svec!["deno", "run", "https://example.com/foo.js"])
@@ -9279,20 +9290,27 @@ mod tests {
assert_eq!(flags.config_path_args(&cwd), None);
let flags =
- flags_from_vec(svec!["deno", "lint", "dir/a.js", "dir/b.js"]).unwrap();
+ flags_from_vec(svec!["deno", "lint", "dir/a/a.js", "dir/b/b.js"])
+ .unwrap();
assert_eq!(
flags.config_path_args(&cwd),
- Some(vec![cwd.join("dir/a.js"), cwd.join("dir/b.js")])
+ Some(vec![cwd.join("dir/a/"), cwd.join("dir/b/")])
);
let flags = flags_from_vec(svec!["deno", "lint"]).unwrap();
- assert!(flags.config_path_args(&cwd).unwrap().is_empty());
+ assert_eq!(flags.config_path_args(&cwd), Some(vec![cwd.clone()]));
- let flags =
- flags_from_vec(svec!["deno", "fmt", "dir/a.js", "dir/b.js"]).unwrap();
+ let flags = flags_from_vec(svec![
+ "deno",
+ "fmt",
+ "dir/a/a.js",
+ "dir/a/a2.js",
+ "dir/b.js"
+ ])
+ .unwrap();
assert_eq!(
flags.config_path_args(&cwd),
- Some(vec![cwd.join("dir/a.js"), cwd.join("dir/b.js")])
+ Some(vec![cwd.join("dir/a/"), cwd.join("dir/")])
);
}
diff --git a/cli/args/import_map.rs b/cli/args/import_map.rs
index 2dc5a21d1..7a16ab215 100644
--- a/cli/args/import_map.rs
+++ b/cli/args/import_map.rs
@@ -1,127 +1,25 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-use deno_core::anyhow::Context;
use deno_core::error::AnyError;
use deno_core::serde_json;
use deno_core::url::Url;
use deno_runtime::deno_permissions::PermissionsContainer;
-use import_map::ImportMap;
-use import_map::ImportMapDiagnostic;
-use log::warn;
-use super::ConfigFile;
use crate::file_fetcher::FileFetcher;
-pub async fn resolve_import_map(
- specified_specifier: Option<&Url>,
- maybe_config_file: Option<&ConfigFile>,
+pub async fn resolve_import_map_value_from_specifier(
+ specifier: &Url,
file_fetcher: &FileFetcher,
-) -> Result<Option<ImportMap>, AnyError> {
- if let Some(specifier) = specified_specifier {
- resolve_import_map_from_specifier(specifier.clone(), file_fetcher)
- .await
- .with_context(|| format!("Unable to load '{}' import map", specifier))
- .map(Some)
- } else if let Some(config_file) = maybe_config_file {
- let maybe_url_and_value = config_file
- .to_import_map_value(|specifier| {
- let specifier = specifier.clone();
- async move {
- let file = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await?
- .into_text_decoded()?;
- Ok(file.source.to_string())
- }
- })
- .await
- .with_context(|| {
- format!(
- "Unable to resolve import map in '{}'",
- config_file.specifier
- )
- })?;
- match maybe_url_and_value {
- Some((url, value)) => {
- import_map_from_value(url.into_owned(), value).map(Some)
- }
- None => Ok(None),
- }
- } else {
- Ok(None)
- }
-}
-
-async fn resolve_import_map_from_specifier(
- specifier: Url,
- file_fetcher: &FileFetcher,
-) -> Result<ImportMap, AnyError> {
- let value: serde_json::Value = if specifier.scheme() == "data" {
+) -> Result<serde_json::Value, AnyError> {
+ if specifier.scheme() == "data" {
let data_url_text =
- deno_graph::source::RawDataUrl::parse(&specifier)?.decode()?;
- serde_json::from_str(&data_url_text)?
+ deno_graph::source::RawDataUrl::parse(specifier)?.decode()?;
+ Ok(serde_json::from_str(&data_url_text)?)
} else {
let file = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
+ .fetch(specifier, &PermissionsContainer::allow_all())
.await?
.into_text_decoded()?;
- serde_json::from_str(&file.source)?
- };
- import_map_from_value(specifier, value)
-}
-
-pub fn import_map_from_value(
- specifier: Url,
- json_value: serde_json::Value,
-) -> Result<ImportMap, AnyError> {
- debug_assert!(
- !specifier.as_str().contains("../"),
- "Import map specifier incorrectly contained ../: {}",
- specifier.as_str()
- );
- let result = import_map::parse_from_value(specifier, json_value)?;
- print_import_map_diagnostics(&result.diagnostics);
- Ok(result.import_map)
-}
-
-fn print_import_map_diagnostics(diagnostics: &[ImportMapDiagnostic]) {
- if !diagnostics.is_empty() {
- warn!(
- "Import map diagnostics:\n{}",
- diagnostics
- .iter()
- .map(|d| format!(" - {d}"))
- .collect::<Vec<_>>()
- .join("\n")
- );
+ Ok(serde_json::from_str(&file.source)?)
}
}
-
-pub fn enhance_import_map_value_with_workspace_members(
- mut import_map_value: serde_json::Value,
- workspace_members: &[deno_config::WorkspaceMemberConfig],
-) -> serde_json::Value {
- let mut imports =
- if let Some(imports) = import_map_value.get("imports").as_ref() {
- imports.as_object().unwrap().clone()
- } else {
- serde_json::Map::new()
- };
-
- for workspace_member in workspace_members {
- let name = &workspace_member.package_name;
- let version = &workspace_member.package_version;
- // Don't override existings, explicit imports
- if imports.contains_key(name) {
- continue;
- }
-
- imports.insert(
- name.to_string(),
- serde_json::Value::String(format!("jsr:{}@^{}", name, version)),
- );
- }
-
- import_map_value["imports"] = serde_json::Value::Object(imports);
- ::import_map::ext::expand_import_map_value(import_map_value)
-}
diff --git a/cli/args/mod.rs b/cli/args/mod.rs
index bf52c460f..f747271b8 100644
--- a/cli/args/mod.rs
+++ b/cli/args/mod.rs
@@ -5,21 +5,30 @@ mod flags;
mod flags_net;
mod import_map;
mod lockfile;
-pub mod package_json;
+mod package_json;
-pub use self::import_map::resolve_import_map;
-use ::import_map::ImportMap;
use deno_ast::SourceMapOption;
-use deno_config::package_json::PackageJsonDeps;
+use deno_config::workspace::CreateResolverOptions;
+use deno_config::workspace::PackageJsonDepResolution;
+use deno_config::workspace::Workspace;
+use deno_config::workspace::WorkspaceDiscoverOptions;
+use deno_config::workspace::WorkspaceDiscoverStart;
+use deno_config::workspace::WorkspaceMemberContext;
+use deno_config::workspace::WorkspaceResolver;
+use deno_config::WorkspaceLintConfig;
+use deno_core::normalize_path;
use deno_core::resolve_url_or_path;
use deno_graph::GraphKind;
use deno_npm::npm_rc::NpmRc;
use deno_npm::npm_rc::ResolvedNpmRc;
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
use deno_npm::NpmSystemInfo;
+use deno_runtime::deno_fs::DenoConfigFsAdapter;
+use deno_runtime::deno_fs::RealFs;
+use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::deno_tls::RootCertStoreProvider;
use deno_semver::npm::NpmPackageReqReference;
-use indexmap::IndexMap;
+use import_map::resolve_import_map_value_from_specifier;
pub use deno_config::glob::FilePatterns;
pub use deno_config::BenchConfig;
@@ -32,10 +41,9 @@ pub use deno_config::TsConfig;
pub use deno_config::TsConfigForEmit;
pub use deno_config::TsConfigType;
pub use deno_config::TsTypeLib;
-pub use deno_config::WorkspaceConfig;
pub use flags::*;
pub use lockfile::CliLockfile;
-pub use package_json::PackageJsonDepsProvider;
+pub use package_json::PackageJsonInstallDepsProvider;
use deno_ast::ModuleSpecifier;
use deno_core::anyhow::bail;
@@ -68,7 +76,6 @@ use std::path::PathBuf;
use std::sync::Arc;
use thiserror::Error;
-use crate::args::import_map::enhance_import_map_value_with_workspace_members;
use crate::cache;
use crate::file_fetcher::FileFetcher;
use crate::util::fs::canonicalize_path_maybe_not_exists;
@@ -243,37 +250,45 @@ impl CacheSetting {
}
}
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct BenchOptions {
- pub files: FilePatterns,
+pub struct WorkspaceBenchOptions {
pub filter: Option<String>,
pub json: bool,
pub no_run: bool,
}
+impl WorkspaceBenchOptions {
+ pub fn resolve(bench_flags: &BenchFlags) -> Self {
+ Self {
+ filter: bench_flags.filter.clone(),
+ json: bench_flags.json,
+ no_run: bench_flags.no_run,
+ }
+ }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct BenchOptions {
+ pub files: FilePatterns,
+}
+
impl BenchOptions {
pub fn resolve(
- maybe_bench_config: Option<BenchConfig>,
- maybe_bench_flags: Option<BenchFlags>,
- initial_cwd: &Path,
+ bench_config: BenchConfig,
+ bench_flags: &BenchFlags,
+ maybe_flags_base: Option<&Path>,
) -> Result<Self, AnyError> {
- let bench_flags = maybe_bench_flags.unwrap_or_default();
Ok(Self {
files: resolve_files(
- maybe_bench_config.map(|c| c.files),
- Some(bench_flags.files),
- initial_cwd,
+ bench_config.files,
+ &bench_flags.files,
+ maybe_flags_base,
)?,
- filter: bench_flags.filter,
- json: bench_flags.json,
- no_run: bench_flags.no_run,
})
}
}
#[derive(Clone, Debug)]
pub struct FmtOptions {
- pub check: bool,
pub options: FmtOptionsConfig,
pub files: FilePatterns,
}
@@ -287,79 +302,66 @@ impl Default for FmtOptions {
impl FmtOptions {
pub fn new_with_base(base: PathBuf) -> Self {
Self {
- check: false,
options: FmtOptionsConfig::default(),
files: FilePatterns::new_with_base(base),
}
}
pub fn resolve(
- maybe_fmt_config: Option<FmtConfig>,
- maybe_fmt_flags: Option<FmtFlags>,
- initial_cwd: &Path,
+ fmt_config: FmtConfig,
+ fmt_flags: &FmtFlags,
+ maybe_flags_base: Option<&Path>,
) -> Result<Self, AnyError> {
- let (maybe_config_options, maybe_config_files) =
- maybe_fmt_config.map(|c| (c.options, c.files)).unzip();
-
Ok(Self {
- check: maybe_fmt_flags.as_ref().map(|f| f.check).unwrap_or(false),
- options: resolve_fmt_options(
- maybe_fmt_flags.as_ref(),
- maybe_config_options,
- ),
+ options: resolve_fmt_options(fmt_flags, fmt_config.options),
files: resolve_files(
- maybe_config_files,
- maybe_fmt_flags.map(|f| f.files),
- initial_cwd,
+ fmt_config.files,
+ &fmt_flags.files,
+ maybe_flags_base,
)?,
})
}
}
fn resolve_fmt_options(
- fmt_flags: Option<&FmtFlags>,
- options: Option<FmtOptionsConfig>,
+ fmt_flags: &FmtFlags,
+ mut options: FmtOptionsConfig,
) -> FmtOptionsConfig {
- let mut options = options.unwrap_or_default();
-
- if let Some(fmt_flags) = fmt_flags {
- if let Some(use_tabs) = fmt_flags.use_tabs {
- options.use_tabs = Some(use_tabs);
- }
+ if let Some(use_tabs) = fmt_flags.use_tabs {
+ options.use_tabs = Some(use_tabs);
+ }
- if let Some(line_width) = fmt_flags.line_width {
- options.line_width = Some(line_width.get());
- }
+ if let Some(line_width) = fmt_flags.line_width {
+ options.line_width = Some(line_width.get());
+ }
- if let Some(indent_width) = fmt_flags.indent_width {
- options.indent_width = Some(indent_width.get());
- }
+ if let Some(indent_width) = fmt_flags.indent_width {
+ options.indent_width = Some(indent_width.get());
+ }
- if let Some(single_quote) = fmt_flags.single_quote {
- options.single_quote = Some(single_quote);
- }
+ if let Some(single_quote) = fmt_flags.single_quote {
+ options.single_quote = Some(single_quote);
+ }
- if let Some(prose_wrap) = &fmt_flags.prose_wrap {
- options.prose_wrap = Some(match prose_wrap.as_str() {
- "always" => ProseWrap::Always,
- "never" => ProseWrap::Never,
- "preserve" => ProseWrap::Preserve,
- // validators in `flags.rs` makes other values unreachable
- _ => unreachable!(),
- });
- }
+ if let Some(prose_wrap) = &fmt_flags.prose_wrap {
+ options.prose_wrap = Some(match prose_wrap.as_str() {
+ "always" => ProseWrap::Always,
+ "never" => ProseWrap::Never,
+ "preserve" => ProseWrap::Preserve,
+ // validators in `flags.rs` makes other values unreachable
+ _ => unreachable!(),
+ });
+ }
- if let Some(no_semis) = &fmt_flags.no_semicolons {
- options.semi_colons = Some(!no_semis);
- }
+ if let Some(no_semis) = &fmt_flags.no_semicolons {
+ options.semi_colons = Some(!no_semis);
}
options
}
-#[derive(Clone)]
-pub struct TestOptions {
- pub files: FilePatterns,
+#[derive(Clone, Debug)]
+pub struct WorkspaceTestOptions {
pub doc: bool,
pub no_run: bool,
pub fail_fast: Option<NonZeroUsize>,
@@ -372,37 +374,47 @@ pub struct TestOptions {
pub junit_path: Option<String>,
}
-impl TestOptions {
- pub fn resolve(
- maybe_test_config: Option<TestConfig>,
- maybe_test_flags: Option<TestFlags>,
- initial_cwd: &Path,
- ) -> Result<Self, AnyError> {
- let test_flags = maybe_test_flags.unwrap_or_default();
-
- Ok(Self {
- files: resolve_files(
- maybe_test_config.map(|c| c.files),
- Some(test_flags.files),
- initial_cwd,
- )?,
+impl WorkspaceTestOptions {
+ pub fn resolve(test_flags: &TestFlags) -> Self {
+ Self {
allow_none: test_flags.allow_none,
concurrent_jobs: test_flags
.concurrent_jobs
.unwrap_or_else(|| NonZeroUsize::new(1).unwrap()),
doc: test_flags.doc,
fail_fast: test_flags.fail_fast,
- filter: test_flags.filter,
+ filter: test_flags.filter.clone(),
no_run: test_flags.no_run,
shuffle: test_flags.shuffle,
trace_leaks: test_flags.trace_leaks,
reporter: test_flags.reporter,
- junit_path: test_flags.junit_path,
+ junit_path: test_flags.junit_path.clone(),
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct TestOptions {
+ pub files: FilePatterns,
+}
+
+impl TestOptions {
+ pub fn resolve(
+ test_config: TestConfig,
+ test_flags: TestFlags,
+ maybe_flags_base: Option<&Path>,
+ ) -> Result<Self, AnyError> {
+ Ok(Self {
+ files: resolve_files(
+ test_config.files,
+ &test_flags.files,
+ maybe_flags_base,
+ )?,
})
}
}
-#[derive(Clone, Default, Debug)]
+#[derive(Clone, Copy, Default, Debug)]
pub enum LintReporterKind {
#[default]
Pretty,
@@ -411,10 +423,45 @@ pub enum LintReporterKind {
}
#[derive(Clone, Debug)]
+pub struct WorkspaceLintOptions {
+ pub reporter_kind: LintReporterKind,
+}
+
+impl WorkspaceLintOptions {
+ pub fn resolve(
+ lint_config: &WorkspaceLintConfig,
+ lint_flags: &LintFlags,
+ ) -> Result<Self, AnyError> {
+ let mut maybe_reporter_kind = if lint_flags.json {
+ Some(LintReporterKind::Json)
+ } else if lint_flags.compact {
+ Some(LintReporterKind::Compact)
+ } else {
+ None
+ };
+
+ if maybe_reporter_kind.is_none() {
+ // Flag not set, so try to get lint reporter from the config file.
+ maybe_reporter_kind = match lint_config.report.as_deref() {
+ Some("json") => Some(LintReporterKind::Json),
+ Some("compact") => Some(LintReporterKind::Compact),
+ Some("pretty") => Some(LintReporterKind::Pretty),
+ Some(_) => {
+ bail!("Invalid lint report type in config file")
+ }
+ None => None,
+ }
+ }
+ Ok(Self {
+ reporter_kind: maybe_reporter_kind.unwrap_or_default(),
+ })
+ }
+}
+
+#[derive(Clone, Debug)]
pub struct LintOptions {
pub rules: LintRulesConfig,
pub files: FilePatterns,
- pub reporter_kind: LintReporterKind,
pub fix: bool,
}
@@ -429,99 +476,51 @@ impl LintOptions {
Self {
rules: Default::default(),
files: FilePatterns::new_with_base(base),
- reporter_kind: Default::default(),
fix: false,
}
}
pub fn resolve(
- maybe_lint_config: Option<LintConfig>,
- maybe_lint_flags: Option<LintFlags>,
- initial_cwd: &Path,
+ lint_config: LintConfig,
+ lint_flags: LintFlags,
+ maybe_flags_base: Option<&Path>,
) -> Result<Self, AnyError> {
- let fix = maybe_lint_flags.as_ref().map(|f| f.fix).unwrap_or(false);
- let mut maybe_reporter_kind =
- maybe_lint_flags.as_ref().and_then(|lint_flags| {
- if lint_flags.json {
- Some(LintReporterKind::Json)
- } else if lint_flags.compact {
- Some(LintReporterKind::Compact)
- } else {
- None
- }
- });
-
- if maybe_reporter_kind.is_none() {
- // Flag not set, so try to get lint reporter from the config file.
- if let Some(lint_config) = &maybe_lint_config {
- maybe_reporter_kind = match lint_config.report.as_deref() {
- Some("json") => Some(LintReporterKind::Json),
- Some("compact") => Some(LintReporterKind::Compact),
- Some("pretty") => Some(LintReporterKind::Pretty),
- Some(_) => {
- bail!("Invalid lint report type in config file")
- }
- None => None,
- }
- }
- }
-
- let (
- maybe_file_flags,
- maybe_rules_tags,
- maybe_rules_include,
- maybe_rules_exclude,
- ) = maybe_lint_flags
- .map(|f| {
- (
- f.files,
- f.maybe_rules_tags,
- f.maybe_rules_include,
- f.maybe_rules_exclude,
- )
- })
- .unwrap_or_default();
-
- let (maybe_config_files, maybe_config_rules) =
- maybe_lint_config.map(|c| (c.files, c.rules)).unzip();
Ok(Self {
- reporter_kind: maybe_reporter_kind.unwrap_or_default(),
files: resolve_files(
- maybe_config_files,
- Some(maybe_file_flags),
- initial_cwd,
+ lint_config.files,
+ &lint_flags.files,
+ maybe_flags_base,
)?,
rules: resolve_lint_rules_options(
- maybe_config_rules,
- maybe_rules_tags,
- maybe_rules_include,
- maybe_rules_exclude,
+ lint_config.rules,
+ lint_flags.maybe_rules_tags,
+ lint_flags.maybe_rules_include,
+ lint_flags.maybe_rules_exclude,
),
- fix,
+ fix: lint_flags.fix,
})
}
}
fn resolve_lint_rules_options(
- maybe_lint_rules_config: Option<LintRulesConfig>,
+ config_rules: LintRulesConfig,
mut maybe_rules_tags: Option<Vec<String>>,
mut maybe_rules_include: Option<Vec<String>>,
mut maybe_rules_exclude: Option<Vec<String>>,
) -> LintRulesConfig {
- if let Some(config_rules) = maybe_lint_rules_config {
- // Try to get configured rules. CLI flags take precedence
- // over config file, i.e. if there's `rules.include` in config file
- // and `--rules-include` CLI flag, only the flag value is taken into account.
- if maybe_rules_include.is_none() {
- maybe_rules_include = config_rules.include;
- }
- if maybe_rules_exclude.is_none() {
- maybe_rules_exclude = config_rules.exclude;
- }
- if maybe_rules_tags.is_none() {
- maybe_rules_tags = config_rules.tags;
- }
+ // Try to get configured rules. CLI flags take precedence
+ // over config file, i.e. if there's `rules.include` in config file
+ // and `--rules-include` CLI flag, only the flag value is taken into account.
+ if maybe_rules_include.is_none() {
+ maybe_rules_include = config_rules.include;
+ }
+ if maybe_rules_exclude.is_none() {
+ maybe_rules_exclude = config_rules.exclude;
+ }
+ if maybe_rules_tags.is_none() {
+ maybe_rules_tags = config_rules.tags;
}
+
LintRulesConfig {
exclude: maybe_rules_exclude,
include: maybe_rules_include,
@@ -529,24 +528,6 @@ fn resolve_lint_rules_options(
}
}
-/// Discover `package.json` file. If `maybe_stop_at` is provided, we will stop
-/// crawling up the directory tree at that path.
-fn discover_package_json(
- flags: &Flags,
- maybe_stop_at: Option<PathBuf>,
- current_dir: &Path,
-) -> Result<Option<Arc<PackageJson>>, AnyError> {
- // TODO(bartlomieju): discover for all subcommands, but print warnings that
- // `package.json` is ignored in bundle/compile/etc.
-
- if let Some(package_json_dir) = flags.package_json_search_dir(current_dir) {
- return package_json::discover_from(&package_json_dir, maybe_stop_at);
- }
-
- log::debug!("No package.json file found");
- Ok(None)
-}
-
/// Discover `.npmrc` file - currently we only support it next to `package.json`
/// or next to `deno.json`.
///
@@ -798,12 +779,10 @@ pub struct CliOptions {
initial_cwd: PathBuf,
maybe_node_modules_folder: Option<PathBuf>,
maybe_vendor_folder: Option<PathBuf>,
- maybe_config_file: Option<ConfigFile>,
- maybe_package_json: Option<Arc<PackageJson>>,
npmrc: Arc<ResolvedNpmRc>,
maybe_lockfile: Option<Arc<CliLockfile>>,
overrides: CliOptionOverrides,
- maybe_workspace_config: Option<WorkspaceConfig>,
+ pub workspace: Arc<Workspace>,
pub disable_deprecated_api_warning: bool,
pub verbose_deprecated_api_warning: bool,
}
@@ -812,10 +791,9 @@ impl CliOptions {
pub fn new(
flags: Flags,
initial_cwd: PathBuf,
- maybe_config_file: Option<ConfigFile>,
maybe_lockfile: Option<Arc<CliLockfile>>,
- maybe_package_json: Option<Arc<PackageJson>>,
npmrc: Arc<ResolvedNpmRc>,
+ workspace: Arc<Workspace>,
force_global_cache: bool,
) -> Result<Self, AnyError> {
if let Some(insecure_allowlist) =
@@ -836,24 +814,23 @@ impl CliOptions {
}
let maybe_lockfile = maybe_lockfile.filter(|_| !force_global_cache);
+ let root_folder = workspace.root_folder().1;
let maybe_node_modules_folder = resolve_node_modules_folder(
&initial_cwd,
&flags,
- maybe_config_file.as_ref(),
- maybe_package_json.as_deref(),
+ root_folder.deno_json.as_deref(),
+ root_folder.pkg_json.as_deref(),
)
.with_context(|| "Resolving node_modules folder.")?;
let maybe_vendor_folder = if force_global_cache {
None
} else {
- resolve_vendor_folder(&initial_cwd, &flags, maybe_config_file.as_ref())
+ resolve_vendor_folder(
+ &initial_cwd,
+ &flags,
+ root_folder.deno_json.as_deref(),
+ )
};
- let maybe_workspace_config =
- if let Some(config_file) = maybe_config_file.as_ref() {
- config_file.to_workspace_config()?
- } else {
- None
- };
if let Some(env_file_name) = &flags.env_file {
match from_filename(env_file_name) {
@@ -879,14 +856,12 @@ impl CliOptions {
Ok(Self {
flags,
initial_cwd,
- maybe_config_file,
maybe_lockfile,
- maybe_package_json,
npmrc,
maybe_node_modules_folder,
maybe_vendor_folder,
overrides: Default::default(),
- maybe_workspace_config,
+ workspace,
disable_deprecated_api_warning,
verbose_deprecated_api_warning,
})
@@ -895,50 +870,71 @@ impl CliOptions {
pub fn from_flags(flags: Flags) -> Result<Self, AnyError> {
let initial_cwd =
std::env::current_dir().with_context(|| "Failed getting cwd.")?;
- let additional_config_file_names =
- if matches!(flags.subcommand, DenoSubcommand::Publish(..)) {
- Some(vec!["jsr.json", "jsr.jsonc"])
- } else {
- None
+ let config_fs_adapter = DenoConfigFsAdapter::new(&RealFs);
+ let resolve_workspace_discover_options = || {
+ let additional_config_file_names: &'static [&'static str] =
+ if matches!(flags.subcommand, DenoSubcommand::Publish(..)) {
+ &["jsr.json", "jsr.jsonc"]
+ } else {
+ &[]
+ };
+ let config_parse_options = deno_config::ConfigParseOptions {
+ include_task_comments: matches!(
+ flags.subcommand,
+ DenoSubcommand::Task(..)
+ ),
};
- let parse_options = deno_config::ParseOptions {
- include_task_comments: matches!(
- flags.subcommand,
- DenoSubcommand::Task(..)
- ),
+ let discover_pkg_json = flags.config_flag
+ != deno_config::ConfigFlag::Disabled
+ && !flags.no_npm
+ && !has_flag_env_var("DENO_NO_PACKAGE_JSON");
+ if !discover_pkg_json {
+ log::debug!("package.json auto-discovery is disabled");
+ }
+ WorkspaceDiscoverOptions {
+ fs: &config_fs_adapter,
+ pkg_json_cache: Some(
+ &deno_runtime::deno_node::PackageJsonThreadLocalCache,
+ ),
+ config_parse_options,
+ additional_config_file_names,
+ discover_pkg_json,
+ }
};
- let maybe_config_file = ConfigFile::discover(
- &flags.config_flag,
- flags.config_path_args(&initial_cwd),
- &initial_cwd,
- additional_config_file_names,
- &parse_options,
- )?;
- let mut maybe_package_json = None;
- if flags.config_flag == deno_config::ConfigFlag::Disabled
- || flags.no_npm
- || has_flag_env_var("DENO_NO_PACKAGE_JSON")
- {
- log::debug!("package.json auto-discovery is disabled")
- } else if let Some(config_file) = &maybe_config_file {
- let specifier = config_file.specifier.clone();
- if specifier.scheme() == "file" {
- let maybe_stop_at = specifier
- .to_file_path()
- .unwrap()
- .parent()
- .map(|p| p.to_path_buf());
-
- maybe_package_json =
- discover_package_json(&flags, maybe_stop_at, &initial_cwd)?;
+ let workspace = match &flags.config_flag {
+ deno_config::ConfigFlag::Discover => {
+ if let Some(start_dirs) = flags.config_path_args(&initial_cwd) {
+ Workspace::discover(
+ WorkspaceDiscoverStart::Dirs(&start_dirs),
+ &resolve_workspace_discover_options(),
+ )?
+ } else {
+ Workspace::empty(Arc::new(
+ ModuleSpecifier::from_directory_path(&initial_cwd).unwrap(),
+ ))
+ }
}
- } else {
- maybe_package_json = discover_package_json(&flags, None, &initial_cwd)?;
+ deno_config::ConfigFlag::Path(path) => {
+ let config_path = normalize_path(initial_cwd.join(path));
+ Workspace::discover(
+ WorkspaceDiscoverStart::ConfigFile(&config_path),
+ &resolve_workspace_discover_options(),
+ )?
+ }
+ deno_config::ConfigFlag::Disabled => Workspace::empty(Arc::new(
+ ModuleSpecifier::from_directory_path(&initial_cwd).unwrap(),
+ )),
+ };
+
+ for diagnostic in workspace.diagnostics() {
+ log::warn!("{}", colors::yellow(diagnostic));
}
+
+ let root_folder = workspace.root_folder().1;
let (npmrc, _) = discover_npmrc(
- maybe_package_json.as_ref().map(|p| p.path.clone()),
- maybe_config_file.as_ref().and_then(|cf| {
+ root_folder.pkg_json.as_ref().map(|p| p.path.clone()),
+ root_folder.deno_json.as_ref().and_then(|cf| {
if cf.specifier.scheme() == "file" {
Some(cf.specifier.to_file_path().unwrap())
} else {
@@ -949,16 +945,18 @@ impl CliOptions {
let maybe_lock_file = CliLockfile::discover(
&flags,
- maybe_config_file.as_ref(),
- maybe_package_json.as_deref(),
+ root_folder.deno_json.as_deref(),
+ root_folder.pkg_json.as_deref(),
)?;
+
+ log::debug!("Finished config loading.");
+
Self::new(
flags,
initial_cwd,
- maybe_config_file,
maybe_lock_file.map(Arc::new),
- maybe_package_json,
npmrc,
+ Arc::new(workspace),
false,
)
}
@@ -968,10 +966,6 @@ impl CliOptions {
&self.initial_cwd
}
- pub fn maybe_config_file_specifier(&self) -> Option<ModuleSpecifier> {
- self.maybe_config_file.as_ref().map(|f| f.specifier.clone())
- }
-
pub fn graph_kind(&self) -> GraphKind {
match self.sub_command() {
DenoSubcommand::Cache(_) => GraphKind::All,
@@ -1057,70 +1051,78 @@ impl CliOptions {
Some(maybe_url) => Ok(maybe_url),
None => resolve_import_map_specifier(
self.flags.import_map_path.as_deref(),
- self.maybe_config_file.as_ref(),
+ self.workspace.root_folder().1.deno_json.as_deref(),
&self.initial_cwd,
),
}
}
- pub async fn resolve_import_map(
+ pub async fn create_workspace_resolver(
&self,
file_fetcher: &FileFetcher,
- ) -> Result<Option<ImportMap>, AnyError> {
- if let Some(workspace_config) = self.maybe_workspace_config.as_ref() {
- let root_config_file = self.maybe_config_file.as_ref().unwrap();
- let base_import_map_config = ::import_map::ext::ImportMapConfig {
- base_url: root_config_file.specifier.clone(),
- import_map_value: root_config_file.to_import_map_value_from_imports(),
- };
- let children_configs = workspace_config
- .members
- .iter()
- .map(|member| ::import_map::ext::ImportMapConfig {
- base_url: member.config_file.specifier.clone(),
- import_map_value: member
- .config_file
- .to_import_map_value_from_imports(),
- })
- .collect();
-
- let (import_map_url, import_map) =
- ::import_map::ext::create_synthetic_import_map(
- base_import_map_config,
- children_configs,
- );
- let import_map = enhance_import_map_value_with_workspace_members(
- import_map,
- &workspace_config.members,
- );
- log::debug!(
- "Workspace config generated this import map {}",
- serde_json::to_string_pretty(&import_map).unwrap()
- );
- let maybe_import_map_result =
- import_map::import_map_from_value(import_map_url, import_map).map(Some);
-
- return maybe_import_map_result;
- }
-
- if self
+ ) -> Result<WorkspaceResolver, AnyError> {
+ let overrode_no_import_map = self
.overrides
.import_map_specifier
.as_ref()
.map(|s| s.is_none())
- == Some(true)
- {
- // overrode to not use an import map
- return Ok(None);
- }
-
- let import_map_specifier = self.resolve_specified_import_map_specifier()?;
- resolve_import_map(
- import_map_specifier.as_ref(),
- self.maybe_config_file().as_ref(),
- file_fetcher,
+ == Some(true);
+ let cli_arg_specified_import_map = if overrode_no_import_map {
+ // use a fake empty import map
+ Some(deno_config::workspace::SpecifiedImportMap {
+ base_url: self
+ .workspace
+ .root_folder()
+ .0
+ .join("import_map.json")
+ .unwrap(),
+ value: serde_json::Value::Object(Default::default()),
+ })
+ } else {
+ let maybe_import_map_specifier =
+ self.resolve_specified_import_map_specifier()?;
+ match maybe_import_map_specifier {
+ Some(specifier) => {
+ let value =
+ resolve_import_map_value_from_specifier(&specifier, file_fetcher)
+ .await
+ .with_context(|| {
+ format!("Unable to load '{}' import map", specifier)
+ })?;
+ Some(deno_config::workspace::SpecifiedImportMap {
+ base_url: specifier,
+ value,
+ })
+ }
+ None => None,
+ }
+ };
+ Ok(
+ self
+ .workspace
+ .create_resolver(
+ CreateResolverOptions {
+ // todo(dsherret): this should be false for nodeModulesDir: true
+ pkg_json_dep_resolution: if self.use_byonm() {
+ PackageJsonDepResolution::Disabled
+ } else {
+ PackageJsonDepResolution::Enabled
+ },
+ specified_import_map: cli_arg_specified_import_map,
+ },
+ |specifier| {
+ let specifier = specifier.clone();
+ async move {
+ let file = file_fetcher
+ .fetch(&specifier, &PermissionsContainer::allow_all())
+ .await?
+ .into_text_decoded()?;
+ Ok(file.source.to_string())
+ }
+ },
+ )
+ .await?,
)
- .await
}
pub fn node_ipc_fd(&self) -> Option<i64> {
@@ -1155,22 +1157,18 @@ impl CliOptions {
}
pub fn resolve_main_module(&self) -> Result<ModuleSpecifier, AnyError> {
- match &self.flags.subcommand {
+ let main_module = match &self.flags.subcommand {
DenoSubcommand::Bundle(bundle_flags) => {
- resolve_url_or_path(&bundle_flags.source_file, self.initial_cwd())
- .map_err(AnyError::from)
+ resolve_url_or_path(&bundle_flags.source_file, self.initial_cwd())?
}
DenoSubcommand::Compile(compile_flags) => {
- resolve_url_or_path(&compile_flags.source_file, self.initial_cwd())
- .map_err(AnyError::from)
+ resolve_url_or_path(&compile_flags.source_file, self.initial_cwd())?
}
DenoSubcommand::Eval(_) => {
- resolve_url_or_path("./$deno$eval", self.initial_cwd())
- .map_err(AnyError::from)
+ resolve_url_or_path("./$deno$eval", self.initial_cwd())?
}
DenoSubcommand::Repl(_) => {
- resolve_url_or_path("./$deno$repl.ts", self.initial_cwd())
- .map_err(AnyError::from)
+ resolve_url_or_path("./$deno$repl.ts", self.initial_cwd())?
}
DenoSubcommand::Run(run_flags) => {
if run_flags.is_stdin() {
@@ -1179,25 +1177,24 @@ impl CliOptions {
.and_then(|cwd| {
resolve_url_or_path("./$deno$stdin.ts", &cwd)
.map_err(AnyError::from)
- })
+ })?
} else if run_flags.watch.is_some() {
- resolve_url_or_path(&run_flags.script, self.initial_cwd())
- .map_err(AnyError::from)
+ resolve_url_or_path(&run_flags.script, self.initial_cwd())?
} else if NpmPackageReqReference::from_str(&run_flags.script).is_ok() {
- ModuleSpecifier::parse(&run_flags.script).map_err(AnyError::from)
+ ModuleSpecifier::parse(&run_flags.script)?
} else {
- resolve_url_or_path(&run_flags.script, self.initial_cwd())
- .map_err(AnyError::from)
+ resolve_url_or_path(&run_flags.script, self.initial_cwd())?
}
}
DenoSubcommand::Serve(run_flags) => {
- resolve_url_or_path(&run_flags.script, self.initial_cwd())
- .map_err(AnyError::from)
+ resolve_url_or_path(&run_flags.script, self.initial_cwd())?
}
_ => {
bail!("No main module.")
}
- }
+ };
+
+ Ok(main_module)
}
pub fn resolve_file_header_overrides(
@@ -1266,11 +1263,9 @@ impl CliOptions {
initial_cwd: self.initial_cwd.clone(),
maybe_node_modules_folder: Some(path),
maybe_vendor_folder: self.maybe_vendor_folder.clone(),
- maybe_config_file: self.maybe_config_file.clone(),
- maybe_package_json: self.maybe_package_json.clone(),
npmrc: self.npmrc.clone(),
maybe_lockfile: self.maybe_lockfile.clone(),
- maybe_workspace_config: self.maybe_workspace_config.clone(),
+ workspace: self.workspace.clone(),
overrides: self.overrides.clone(),
disable_deprecated_api_warning: self.disable_deprecated_api_warning,
verbose_deprecated_api_warning: self.verbose_deprecated_api_warning,
@@ -1278,12 +1273,10 @@ impl CliOptions {
}
pub fn node_modules_dir_enablement(&self) -> Option<bool> {
- self.flags.node_modules_dir.or_else(|| {
- self
- .maybe_config_file
- .as_ref()
- .and_then(|c| c.json.node_modules_dir)
- })
+ self
+ .flags
+ .node_modules_dir
+ .or_else(|| self.workspace.node_modules_dir())
}
pub fn vendor_dir_path(&self) -> Option<&PathBuf> {
@@ -1304,10 +1297,7 @@ impl CliOptions {
&self,
config_type: TsConfigType,
) -> Result<TsConfigForEmit, AnyError> {
- let result = deno_config::get_ts_config_for_emit(
- config_type,
- self.maybe_config_file.as_ref(),
- );
+ let result = self.workspace.resolve_ts_config_for_emit(config_type);
match result {
Ok(mut ts_config_for_emit) => {
@@ -1346,101 +1336,83 @@ impl CliOptions {
self.maybe_lockfile.clone()
}
- pub fn resolve_tasks_config(
- &self,
- ) -> Result<IndexMap<String, deno_config::Task>, AnyError> {
- if let Some(config_file) = &self.maybe_config_file {
- config_file.resolve_tasks_config()
- } else if self.maybe_package_json.is_some() {
- Ok(Default::default())
- } else {
- bail!("deno task couldn't find deno.json(c). See https://deno.land/manual@v{}/getting_started/configuration_file", env!("CARGO_PKG_VERSION"))
- }
- }
-
- /// Return the JSX import source configuration.
- pub fn to_maybe_jsx_import_source_config(
- &self,
- ) -> Result<Option<JsxImportSourceConfig>, AnyError> {
- match self.maybe_config_file.as_ref() {
- Some(config) => config.to_maybe_jsx_import_source_config(),
- None => Ok(None),
- }
- }
-
/// Return any imports that should be brought into the scope of the module
/// graph.
pub fn to_maybe_imports(
&self,
) -> Result<Vec<deno_graph::ReferrerImports>, AnyError> {
- if let Some(config_file) = &self.maybe_config_file {
- config_file.to_maybe_imports().map(|maybe_imports| {
- maybe_imports
- .into_iter()
- .map(|(referrer, imports)| deno_graph::ReferrerImports {
- referrer,
- imports,
- })
- .collect()
- })
- } else {
- Ok(Vec::new())
- }
- }
-
- pub fn maybe_config_file(&self) -> &Option<ConfigFile> {
- &self.maybe_config_file
- }
-
- pub fn maybe_workspace_config(&self) -> &Option<WorkspaceConfig> {
- &self.maybe_workspace_config
- }
-
- pub fn maybe_package_json(&self) -> Option<&Arc<PackageJson>> {
- self.maybe_package_json.as_ref()
+ self.workspace.to_maybe_imports().map(|maybe_imports| {
+ maybe_imports
+ .into_iter()
+ .map(|(referrer, imports)| deno_graph::ReferrerImports {
+ referrer,
+ imports,
+ })
+ .collect()
+ })
}
pub fn npmrc(&self) -> &Arc<ResolvedNpmRc> {
&self.npmrc
}
- pub fn maybe_package_json_deps(&self) -> Option<PackageJsonDeps> {
- if matches!(
- self.flags.subcommand,
- DenoSubcommand::Task(TaskFlags { task: None, .. })
- ) {
- // don't have any package json dependencies for deno task with no args
- None
- } else {
- self
- .maybe_package_json()
- .as_ref()
- .map(|p| p.resolve_local_package_json_version_reqs())
+ pub fn resolve_fmt_options_for_members(
+ &self,
+ fmt_flags: &FmtFlags,
+ ) -> Result<Vec<FmtOptions>, AnyError> {
+ let cli_arg_patterns =
+ fmt_flags.files.as_file_patterns(self.initial_cwd())?;
+ let member_ctxs =
+ self.workspace.resolve_ctxs_from_patterns(&cli_arg_patterns);
+ let mut result = Vec::with_capacity(member_ctxs.len());
+ for member_ctx in &member_ctxs {
+ let options = self.resolve_fmt_options(fmt_flags, member_ctx)?;
+ result.push(options);
}
+ Ok(result)
}
pub fn resolve_fmt_options(
&self,
- fmt_flags: FmtFlags,
+ fmt_flags: &FmtFlags,
+ ctx: &WorkspaceMemberContext,
) -> Result<FmtOptions, AnyError> {
- let maybe_fmt_config = if let Some(config_file) = &self.maybe_config_file {
- config_file.to_fmt_config()?
- } else {
- None
- };
- FmtOptions::resolve(maybe_fmt_config, Some(fmt_flags), &self.initial_cwd)
+ let fmt_config = ctx.to_fmt_config()?;
+ FmtOptions::resolve(fmt_config, fmt_flags, Some(&self.initial_cwd))
+ }
+
+ pub fn resolve_workspace_lint_options(
+ &self,
+ lint_flags: &LintFlags,
+ ) -> Result<WorkspaceLintOptions, AnyError> {
+ let lint_config = self.workspace.to_lint_config()?;
+ WorkspaceLintOptions::resolve(&lint_config, lint_flags)
+ }
+
+ pub fn resolve_lint_options_for_members(
+ &self,
+ lint_flags: &LintFlags,
+ ) -> Result<Vec<(WorkspaceMemberContext, LintOptions)>, AnyError> {
+ let cli_arg_patterns =
+ lint_flags.files.as_file_patterns(self.initial_cwd())?;
+ let member_ctxs =
+ self.workspace.resolve_ctxs_from_patterns(&cli_arg_patterns);
+ let mut result = Vec::with_capacity(member_ctxs.len());
+ for member_ctx in member_ctxs {
+ let options =
+ self.resolve_lint_options(lint_flags.clone(), &member_ctx)?;
+ result.push((member_ctx, options));
+ }
+ Ok(result)
}
pub fn resolve_lint_options(
&self,
lint_flags: LintFlags,
+ ctx: &WorkspaceMemberContext,
) -> Result<LintOptions, AnyError> {
- let maybe_lint_config = if let Some(config_file) = &self.maybe_config_file {
- config_file.to_lint_config()?
- } else {
- None
- };
- LintOptions::resolve(maybe_lint_config, Some(lint_flags), &self.initial_cwd)
+ let lint_config = ctx.to_lint_config()?;
+ LintOptions::resolve(lint_config, lint_flags, Some(&self.initial_cwd))
}
pub fn resolve_lint_config(
@@ -1464,104 +1436,80 @@ impl CliOptions {
})
}
- pub fn resolve_config_excludes(&self) -> Result<PathOrPatternSet, AnyError> {
- let maybe_config_files = if let Some(config_file) = &self.maybe_config_file
- {
- Some(config_file.to_files_config()?)
- } else {
- None
- };
- Ok(maybe_config_files.map(|f| f.exclude).unwrap_or_default())
+ pub fn resolve_workspace_test_options(
+ &self,
+ test_flags: &TestFlags,
+ ) -> WorkspaceTestOptions {
+ WorkspaceTestOptions::resolve(test_flags)
+ }
+
+ pub fn resolve_test_options_for_members(
+ &self,
+ test_flags: &TestFlags,
+ ) -> Result<Vec<(WorkspaceMemberContext, TestOptions)>, AnyError> {
+ let cli_arg_patterns =
+ test_flags.files.as_file_patterns(self.initial_cwd())?;
+ let member_ctxs =
+ self.workspace.resolve_ctxs_from_patterns(&cli_arg_patterns);
+ let mut result = Vec::with_capacity(member_ctxs.len());
+ for member_ctx in member_ctxs {
+ let options =
+ self.resolve_test_options(test_flags.clone(), &member_ctx)?;
+ result.push((member_ctx, options));
+ }
+ Ok(result)
+ }
+
+ pub fn resolve_workspace_bench_options(
+ &self,
+ bench_flags: &BenchFlags,
+ ) -> WorkspaceBenchOptions {
+ WorkspaceBenchOptions::resolve(bench_flags)
}
pub fn resolve_test_options(
&self,
test_flags: TestFlags,
+ ctx: &WorkspaceMemberContext,
) -> Result<TestOptions, AnyError> {
- let maybe_test_config = if let Some(config_file) = &self.maybe_config_file {
- config_file.to_test_config()?
- } else {
- None
- };
- TestOptions::resolve(maybe_test_config, Some(test_flags), &self.initial_cwd)
+ let test_config = ctx.to_test_config()?;
+ TestOptions::resolve(test_config, test_flags, Some(&self.initial_cwd))
+ }
+
+ pub fn resolve_bench_options_for_members(
+ &self,
+ bench_flags: &BenchFlags,
+ ) -> Result<Vec<(WorkspaceMemberContext, BenchOptions)>, AnyError> {
+ let cli_arg_patterns =
+ bench_flags.files.as_file_patterns(self.initial_cwd())?;
+ let member_ctxs =
+ self.workspace.resolve_ctxs_from_patterns(&cli_arg_patterns);
+ let mut result = Vec::with_capacity(member_ctxs.len());
+ for member_ctx in member_ctxs {
+ let options = self.resolve_bench_options(bench_flags, &member_ctx)?;
+ result.push((member_ctx, options));
+ }
+ Ok(result)
}
pub fn resolve_bench_options(
&self,
- bench_flags: BenchFlags,
+ bench_flags: &BenchFlags,
+ ctx: &WorkspaceMemberContext,
) -> Result<BenchOptions, AnyError> {
- let maybe_bench_config = if let Some(config_file) = &self.maybe_config_file
- {
- config_file.to_bench_config()?
- } else {
- None
- };
- BenchOptions::resolve(
- maybe_bench_config,
- Some(bench_flags),
- &self.initial_cwd,
- )
+ let bench_config = ctx.to_bench_config()?;
+ BenchOptions::resolve(bench_config, bench_flags, Some(&self.initial_cwd))
}
pub fn resolve_deno_graph_workspace_members(
&self,
) -> Result<Vec<deno_graph::WorkspaceMember>, AnyError> {
- fn workspace_config_to_workspace_members(
- workspace_config: &deno_config::WorkspaceConfig,
- ) -> Result<Vec<deno_graph::WorkspaceMember>, AnyError> {
- workspace_config
- .members
- .iter()
- .map(|member| {
- config_to_workspace_member(&member.config_file).with_context(|| {
- format!(
- "Failed to resolve configuration for '{}' workspace member at '{}'",
- member.member_name,
- member.config_file.specifier.as_str()
- )
- })
- })
- .collect()
- }
-
- fn config_to_workspace_member(
- config: &ConfigFile,
- ) -> Result<deno_graph::WorkspaceMember, AnyError> {
- let nv = deno_semver::package::PackageNv {
- name: match &config.json.name {
- Some(name) => name.clone(),
- None => bail!("Missing 'name' field in config file."),
- },
- version: match &config.json.version {
- Some(name) => deno_semver::Version::parse_standard(name)?,
- None => bail!("Missing 'version' field in config file."),
- },
- };
- Ok(deno_graph::WorkspaceMember {
- base: config.specifier.join("./").unwrap(),
- nv,
- exports: config.to_exports_config()?.into_map(),
- })
- }
-
- let maybe_workspace_config = self.maybe_workspace_config();
- if let Some(wc) = maybe_workspace_config {
- workspace_config_to_workspace_members(wc)
- } else {
- Ok(
- self
- .maybe_config_file()
- .as_ref()
- .and_then(|c| match config_to_workspace_member(c) {
- Ok(m) => Some(vec![m]),
- Err(e) => {
- log::debug!("Deno config was not a package: {:#}", e);
- None
- }
- })
- .unwrap_or_default(),
- )
- }
+ self
+ .workspace
+ .jsr_packages()
+ .into_iter()
+ .map(|pkg| config_to_deno_graph_workspace_member(&pkg.config_file))
+ .collect::<Result<Vec<_>, _>>()
}
/// Vector of user script CLI arguments.
@@ -1578,11 +1526,7 @@ impl CliOptions {
}
pub fn check_js(&self) -> bool {
- self
- .maybe_config_file
- .as_ref()
- .map(|cf| cf.get_check_js())
- .unwrap_or(false)
+ self.workspace.check_js()
}
pub fn coverage_dir(&self) -> Option<String> {
@@ -1729,17 +1673,17 @@ impl CliOptions {
pub fn unstable_bare_node_builtins(&self) -> bool {
self.flags.unstable_config.bare_node_builtins
- || self
- .maybe_config_file()
- .as_ref()
- .map(|c| c.has_unstable("bare-node-builtins"))
- .unwrap_or(false)
+ || self.workspace.has_unstable("bare-node-builtins")
}
pub fn use_byonm(&self) -> bool {
if self.enable_future_features()
&& self.node_modules_dir_enablement().is_none()
- && self.maybe_package_json.is_some()
+ && self
+ .workspace
+ .config_folders()
+ .values()
+ .any(|f| f.pkg_json.is_some())
{
return true;
}
@@ -1750,28 +1694,16 @@ impl CliOptions {
.as_ref()
.map(|s| matches!(s.kind, NpmProcessStateKind::Byonm))
.unwrap_or(false)
- || self
- .maybe_config_file()
- .as_ref()
- .map(|c| c.has_unstable("byonm"))
- .unwrap_or(false)
+ || self.workspace.has_unstable("byonm")
}
pub fn unstable_sloppy_imports(&self) -> bool {
self.flags.unstable_config.sloppy_imports
- || self
- .maybe_config_file()
- .as_ref()
- .map(|c| c.has_unstable("sloppy-imports"))
- .unwrap_or(false)
+ || self.workspace.has_unstable("sloppy-imports")
}
pub fn unstable_features(&self) -> Vec<String> {
- let mut from_config_file = self
- .maybe_config_file()
- .as_ref()
- .map(|c| c.json.unstable.clone())
- .unwrap_or_default();
+ let mut from_config_file = self.workspace.unstable_features().to_vec();
self
.flags
@@ -1824,12 +1756,18 @@ impl CliOptions {
{
full_paths.push(import_map_path);
}
- if let Some(specifier) = self.maybe_config_file_specifier() {
- if specifier.scheme() == "file" {
- if let Ok(path) = specifier.to_file_path() {
- full_paths.push(path);
+
+ for (_, folder) in self.workspace.config_folders() {
+ if let Some(deno_json) = &folder.deno_json {
+ if deno_json.specifier.scheme() == "file" {
+ if let Ok(path) = deno_json.specifier.to_file_path() {
+ full_paths.push(path);
+ }
}
}
+ if let Some(pkg_json) = &folder.pkg_json {
+ full_paths.push(pkg_json.path.clone());
+ }
}
full_paths
}
@@ -1938,8 +1876,9 @@ impl StorageKeyResolver {
// otherwise we will use the path to the config file or None to
// fall back to using the main module's path
options
- .maybe_config_file
- .as_ref()
+ .workspace
+ .resolve_start_ctx()
+ .maybe_deno_json()
.map(|config_file| Some(config_file.specifier.to_string()))
})
}
@@ -1967,29 +1906,25 @@ impl StorageKeyResolver {
/// over config file, i.e. if there's `files.ignore` in config file
/// and `--ignore` CLI flag, only the flag value is taken into account.
fn resolve_files(
- maybe_files_config: Option<FilePatterns>,
- maybe_file_flags: Option<FileFlags>,
- initial_cwd: &Path,
+ mut files_config: FilePatterns,
+ file_flags: &FileFlags,
+ maybe_flags_base: Option<&Path>,
) -> Result<FilePatterns, AnyError> {
- let mut maybe_files_config = maybe_files_config
- .unwrap_or_else(|| FilePatterns::new_with_base(initial_cwd.to_path_buf()));
- if let Some(file_flags) = maybe_file_flags {
- if !file_flags.include.is_empty() {
- maybe_files_config.include =
- Some(PathOrPatternSet::from_include_relative_path_or_patterns(
- initial_cwd,
- &file_flags.include,
- )?);
- }
- if !file_flags.ignore.is_empty() {
- maybe_files_config.exclude =
- PathOrPatternSet::from_exclude_relative_path_or_patterns(
- initial_cwd,
- &file_flags.ignore,
- )?;
- }
+ if !file_flags.include.is_empty() {
+ files_config.include =
+ Some(PathOrPatternSet::from_include_relative_path_or_patterns(
+ maybe_flags_base.unwrap_or(&files_config.base),
+ &file_flags.include,
+ )?);
+ }
+ if !file_flags.ignore.is_empty() {
+ files_config.exclude =
+ PathOrPatternSet::from_exclude_relative_path_or_patterns(
+ maybe_flags_base.unwrap_or(&files_config.base),
+ &file_flags.ignore,
+ )?;
}
- Ok(maybe_files_config)
+ Ok(files_config)
}
/// Resolves the no_prompt value based on the cli flags and environment.
@@ -2009,6 +1944,26 @@ pub fn npm_pkg_req_ref_to_binary_command(
binary_name.to_string()
}
+pub fn config_to_deno_graph_workspace_member(
+ config: &ConfigFile,
+) -> Result<deno_graph::WorkspaceMember, AnyError> {
+ let nv = deno_semver::package::PackageNv {
+ name: match &config.json.name {
+ Some(name) => name.clone(),
+ None => bail!("Missing 'name' field in config file."),
+ },
+ version: match &config.json.version {
+ Some(name) => deno_semver::Version::parse_standard(name)?,
+ None => bail!("Missing 'version' field in config file."),
+ },
+ };
+ Ok(deno_graph::WorkspaceMember {
+ base: config.specifier.join("./").unwrap(),
+ nv,
+ exports: config.to_exports_config()?.into_map(),
+ })
+}
+
#[cfg(test)]
mod test {
use crate::util::fs::FileCollector;
@@ -2027,7 +1982,7 @@ mod test {
let config_file = ConfigFile::new(
config_text,
config_specifier,
- &deno_config::ParseOptions::default(),
+ &deno_config::ConfigParseOptions::default(),
)
.unwrap();
let actual = resolve_import_map_specifier(
@@ -2051,7 +2006,7 @@ mod test {
let config_file = ConfigFile::new(
config_text,
config_specifier,
- &deno_config::ParseOptions::default(),
+ &deno_config::ConfigParseOptions::default(),
)
.unwrap();
let actual = resolve_import_map_specifier(
@@ -2130,7 +2085,7 @@ mod test {
assert!(error.to_string().starts_with("Failed to expand glob"));
let resolved_files = resolve_files(
- Some(FilePatterns {
+ FilePatterns {
base: temp_dir_path.to_path_buf(),
include: Some(
PathOrPatternSet::from_include_relative_path_or_patterns(
@@ -2149,9 +2104,9 @@ mod test {
&["nested/**/*bazz.ts".to_string()],
)
.unwrap(),
- }),
- None,
- temp_dir_path,
+ },
+ &Default::default(),
+ Some(temp_dir_path),
)
.unwrap();
diff --git a/cli/args/package_json.rs b/cli/args/package_json.rs
index b6ccb33a4..eb1c41c5d 100644
--- a/cli/args/package_json.rs
+++ b/cli/args/package_json.rs
@@ -1,77 +1,87 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
-use deno_config::package_json::PackageJsonDeps;
-use deno_core::anyhow::bail;
-use deno_core::error::AnyError;
-use deno_runtime::deno_fs::RealFs;
-use deno_runtime::deno_node::load_pkg_json;
-use deno_runtime::deno_node::PackageJson;
+use deno_config::package_json::PackageJsonDepValue;
+use deno_config::workspace::Workspace;
use deno_semver::package::PackageReq;
-#[derive(Debug, Default)]
-pub struct PackageJsonDepsProvider(Option<PackageJsonDeps>);
-
-impl PackageJsonDepsProvider {
- pub fn new(deps: Option<PackageJsonDeps>) -> Self {
- Self(deps)
- }
-
- pub fn deps(&self) -> Option<&PackageJsonDeps> {
- self.0.as_ref()
- }
-
- pub fn reqs(&self) -> Option<Vec<&PackageReq>> {
- match &self.0 {
- Some(deps) => {
- let mut package_reqs = deps
- .values()
- .filter_map(|r| r.as_ref().ok())
- .collect::<Vec<_>>();
- package_reqs.sort(); // deterministic resolution
- Some(package_reqs)
- }
- None => None,
- }
- }
+#[derive(Debug)]
+pub struct InstallNpmWorkspacePkg {
+ pub alias: String,
+ pub pkg_dir: PathBuf,
}
-/// 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<Arc<PackageJson>>, AnyError> {
- const PACKAGE_JSON_NAME: &str = "package.json";
+// todo(#24419): this is not correct, but it's good enough for now.
+// We need deno_npm to be able to understand workspace packages and
+// then have a way to properly lay them out on the file system
+#[derive(Debug, Default)]
+pub struct PackageJsonInstallDepsProvider {
+ remote_pkg_reqs: Vec<PackageReq>,
+ workspace_pkgs: Vec<InstallNpmWorkspacePkg>,
+}
- // note: ancestors() includes the `start` path
- for ancestor in start.ancestors() {
- let path = ancestor.join(PACKAGE_JSON_NAME);
+impl PackageJsonInstallDepsProvider {
+ pub fn empty() -> Self {
+ Self::default()
+ }
- let package_json = match load_pkg_json(&RealFs, &path) {
- Ok(Some(package_json)) => package_json,
- Ok(None) => {
- if let Some(stop_at) = maybe_stop_at.as_ref() {
- if ancestor == stop_at {
- break;
+ pub fn from_workspace(workspace: &Arc<Workspace>) -> Self {
+ let mut workspace_pkgs = Vec::new();
+ let mut remote_pkg_reqs = Vec::new();
+ let workspace_npm_pkgs = workspace.npm_packages();
+ for pkg_json in workspace.package_jsons() {
+ let deps = pkg_json.resolve_local_package_json_deps();
+ let mut pkg_reqs = Vec::with_capacity(deps.len());
+ for (alias, dep) in deps {
+ let Ok(dep) = dep else {
+ continue;
+ };
+ match dep {
+ PackageJsonDepValue::Req(pkg_req) => {
+ if let Some(pkg) = workspace_npm_pkgs
+ .iter()
+ .find(|pkg| pkg.matches_req(&pkg_req))
+ {
+ workspace_pkgs.push(InstallNpmWorkspacePkg {
+ alias,
+ pkg_dir: pkg.pkg_json.dir_path().to_path_buf(),
+ });
+ } else {
+ pkg_reqs.push(pkg_req)
+ }
+ }
+ PackageJsonDepValue::Workspace(version_req) => {
+ if let Some(pkg) = workspace_npm_pkgs.iter().find(|pkg| {
+ pkg.matches_name_and_version_req(&alias, &version_req)
+ }) {
+ workspace_pkgs.push(InstallNpmWorkspacePkg {
+ alias,
+ pkg_dir: pkg.pkg_json.dir_path().to_path_buf(),
+ });
+ }
}
}
- continue;
}
- Err(err) => bail!(
- "Error loading package.json at {}. {:#}",
- path.display(),
- err
- ),
- };
+ // sort within each package
+ pkg_reqs.sort();
- log::debug!("package.json file found at '{}'", path.display());
- return Ok(Some(package_json));
+ remote_pkg_reqs.extend(pkg_reqs);
+ }
+ remote_pkg_reqs.shrink_to_fit();
+ workspace_pkgs.shrink_to_fit();
+ Self {
+ remote_pkg_reqs,
+ workspace_pkgs,
+ }
+ }
+
+ pub fn remote_pkg_reqs(&self) -> &Vec<PackageReq> {
+ &self.remote_pkg_reqs
}
- log::debug!("No package.json file found");
- Ok(None)
+ pub fn workspace_pkgs(&self) -> &Vec<InstallNpmWorkspacePkg> {
+ &self.workspace_pkgs
+ }
}