summaryrefslogtreecommitdiff
path: root/cli/args
diff options
context:
space:
mode:
Diffstat (limited to 'cli/args')
-rw-r--r--cli/args/config_file.rs315
-rw-r--r--cli/args/flags.rs10
-rw-r--r--cli/args/mod.rs438
3 files changed, 578 insertions, 185 deletions
diff --git a/cli/args/config_file.rs b/cli/args/config_file.rs
index bed155d32..570aeba0d 100644
--- a/cli/args/config_file.rs
+++ b/cli/args/config_file.rs
@@ -11,7 +11,6 @@ use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::custom_error;
use deno_core::error::AnyError;
-use deno_core::normalize_path;
use deno_core::serde::Deserialize;
use deno_core::serde::Serialize;
use deno_core::serde::Serializer;
@@ -161,66 +160,6 @@ pub const IGNORED_RUNTIME_COMPILER_OPTIONS: &[&str] = &[
"watch",
];
-/// Filenames that Deno will recognize when discovering config.
-const CONFIG_FILE_NAMES: [&str; 2] = ["deno.json", "deno.jsonc"];
-
-pub fn discover(flags: &Flags) -> Result<Option<ConfigFile>, AnyError> {
- match &flags.config_flag {
- ConfigFlag::Disabled => Ok(None),
- ConfigFlag::Path(config_path) => Ok(Some(ConfigFile::read(config_path)?)),
- ConfigFlag::Discover => {
- if let Some(config_path_args) = flags.config_path_args() {
- let mut checked = HashSet::new();
- for f in config_path_args {
- if let Some(cf) = discover_from(&f, &mut checked)? {
- return Ok(Some(cf));
- }
- }
- // From CWD walk up to root looking for deno.json or deno.jsonc
- let cwd = std::env::current_dir()?;
- discover_from(&cwd, &mut checked)
- } else {
- Ok(None)
- }
- }
- }
-}
-
-pub fn discover_from(
- start: &Path,
- checked: &mut HashSet<PathBuf>,
-) -> Result<Option<ConfigFile>, AnyError> {
- for ancestor in start.ancestors() {
- if checked.insert(ancestor.to_path_buf()) {
- for config_filename in CONFIG_FILE_NAMES {
- let f = ancestor.join(config_filename);
- match ConfigFile::read(f) {
- Ok(cf) => {
- return Ok(Some(cf));
- }
- Err(e) => {
- if let Some(ioerr) = e.downcast_ref::<std::io::Error>() {
- use std::io::ErrorKind::*;
- match ioerr.kind() {
- InvalidInput | PermissionDenied | NotFound => {
- // ok keep going
- }
- _ => {
- return Err(e); // Unknown error. Stop.
- }
- }
- } else {
- return Err(e); // Parse error or something else. Stop.
- }
- }
- }
- }
- }
- }
- // No config file found.
- Ok(None)
-}
-
/// A function that works like JavaScript's `Object.assign()`.
pub fn json_merge(a: &mut Value, b: &Value) {
match (a, b) {
@@ -235,56 +174,6 @@ pub fn json_merge(a: &mut Value, b: &Value) {
}
}
-/// Based on an optional command line import map path and an optional
-/// configuration file, return a resolved module specifier to an import map.
-pub fn resolve_import_map_specifier(
- maybe_import_map_path: Option<&str>,
- maybe_config_file: Option<&ConfigFile>,
-) -> Result<Option<ModuleSpecifier>, AnyError> {
- if let Some(import_map_path) = maybe_import_map_path {
- if let Some(config_file) = &maybe_config_file {
- if config_file.to_import_map_path().is_some() {
- log::warn!("{} the configuration file \"{}\" contains an entry for \"importMap\" that is being ignored.", crate::colors::yellow("Warning"), config_file.specifier);
- }
- }
- let specifier = deno_core::resolve_url_or_path(import_map_path)
- .context(format!("Bad URL (\"{}\") for import map.", import_map_path))?;
- return Ok(Some(specifier));
- } else if let Some(config_file) = &maybe_config_file {
- // when the import map is specifier in a config file, it needs to be
- // resolved relative to the config file, versus the CWD like with the flag
- // and with config files, we support both local and remote config files,
- // so we have treat them differently.
- if let Some(import_map_path) = config_file.to_import_map_path() {
- let specifier =
- // with local config files, it might be common to specify an import
- // map like `"importMap": "import-map.json"`, which is resolvable if
- // the file is resolved like a file path, so we will coerce the config
- // file into a file path if possible and join the import map path to
- // the file path.
- if let Ok(config_file_path) = config_file.specifier.to_file_path() {
- let import_map_file_path = normalize_path(config_file_path
- .parent()
- .ok_or_else(|| {
- anyhow!("Bad config file specifier: {}", config_file.specifier)
- })?
- .join(&import_map_path));
- ModuleSpecifier::from_file_path(import_map_file_path).unwrap()
- // otherwise if the config file is remote, we have no choice but to
- // use "import resolution" with the config file as the base.
- } else {
- deno_core::resolve_import(&import_map_path, config_file.specifier.as_str())
- .context(format!(
- "Bad URL (\"{}\") for import map.",
- import_map_path
- ))?
- };
- return Ok(Some(specifier));
- }
- }
- Ok(None)
-}
-
fn parse_compiler_options(
compiler_options: &HashMap<String, Value>,
maybe_specifier: Option<ModuleSpecifier>,
@@ -547,6 +436,66 @@ pub struct ConfigFile {
}
impl ConfigFile {
+ pub fn discover(flags: &Flags) -> Result<Option<ConfigFile>, AnyError> {
+ match &flags.config_flag {
+ ConfigFlag::Disabled => Ok(None),
+ ConfigFlag::Path(config_path) => Ok(Some(ConfigFile::read(config_path)?)),
+ ConfigFlag::Discover => {
+ if let Some(config_path_args) = flags.config_path_args() {
+ let mut checked = HashSet::new();
+ for f in config_path_args {
+ if let Some(cf) = Self::discover_from(&f, &mut checked)? {
+ return Ok(Some(cf));
+ }
+ }
+ // 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)
+ } else {
+ Ok(None)
+ }
+ }
+ }
+ }
+
+ pub fn discover_from(
+ start: &Path,
+ checked: &mut HashSet<PathBuf>,
+ ) -> Result<Option<ConfigFile>, AnyError> {
+ /// Filenames that Deno will recognize when discovering config.
+ const CONFIG_FILE_NAMES: [&str; 2] = ["deno.json", "deno.jsonc"];
+
+ for ancestor in start.ancestors() {
+ if checked.insert(ancestor.to_path_buf()) {
+ for config_filename in CONFIG_FILE_NAMES {
+ let f = ancestor.join(config_filename);
+ match ConfigFile::read(f) {
+ Ok(cf) => {
+ return Ok(Some(cf));
+ }
+ Err(e) => {
+ if let Some(ioerr) = e.downcast_ref::<std::io::Error>() {
+ use std::io::ErrorKind::*;
+ match ioerr.kind() {
+ InvalidInput | PermissionDenied | NotFound => {
+ // ok keep going
+ }
+ _ => {
+ return Err(e); // Unknown error. Stop.
+ }
+ }
+ } else {
+ return Err(e); // Parse error or something else. Stop.
+ }
+ }
+ }
+ }
+ }
+ }
+ // No config file found.
+ Ok(None)
+ }
+
pub fn read(path_ref: impl AsRef<Path>) -> Result<Self, AnyError> {
let path = Path::new(path_ref.as_ref());
let config_file = if path.is_absolute() {
@@ -744,12 +693,36 @@ impl ConfigFile {
Ok(None)
}
}
+
+ pub fn resolve_tasks_config(
+ &self,
+ ) -> Result<BTreeMap<String, String>, AnyError> {
+ let maybe_tasks_config = self.to_tasks_config()?;
+ if let Some(tasks_config) = maybe_tasks_config {
+ for key in tasks_config.keys() {
+ if key.is_empty() {
+ bail!("Configuration file task names cannot be empty");
+ } else if !key
+ .chars()
+ .all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '-' | ':'))
+ {
+ bail!("Configuration file task names must only contain alpha-numeric characters, colons (:), underscores (_), or dashes (-). Task: {}", key);
+ } else if !key.chars().next().unwrap().is_ascii_alphabetic() {
+ bail!("Configuration file task names must start with an alphabetic character. Task: {}", key);
+ }
+ }
+ Ok(tasks_config)
+ } else {
+ bail!("No tasks found in configuration file")
+ }
+ }
}
#[cfg(test)]
mod tests {
use super::*;
use deno_core::serde_json::json;
+ use pretty_assertions::assert_eq;
#[test]
fn read_config_file_relative() {
@@ -996,7 +969,9 @@ mod tests {
let testdata = test_util::testdata_path();
let c_md = testdata.join("fmt/with_config/subdir/c.md");
let mut checked = HashSet::new();
- let config_file = discover_from(&c_md, &mut checked).unwrap().unwrap();
+ let config_file = ConfigFile::discover_from(&c_md, &mut checked)
+ .unwrap()
+ .unwrap();
assert!(checked.contains(c_md.parent().unwrap()));
assert!(!checked.contains(&testdata));
let fmt_config = config_file.to_fmt_config().unwrap().unwrap();
@@ -1012,7 +987,9 @@ mod tests {
}
// If we call discover_from again starting at testdata, we ought to get None.
- assert!(discover_from(&testdata, &mut checked).unwrap().is_none());
+ assert!(ConfigFile::discover_from(&testdata, &mut checked)
+ .unwrap()
+ .is_none());
}
#[test]
@@ -1020,83 +997,71 @@ mod tests {
let testdata = test_util::testdata_path();
let d = testdata.join("malformed_config/");
let mut checked = HashSet::new();
- let err = discover_from(&d, &mut checked).unwrap_err();
+ let err = ConfigFile::discover_from(&d, &mut checked).unwrap_err();
assert!(err.to_string().contains("Unable to parse config file"));
}
- #[cfg(not(windows))]
#[test]
- fn resolve_import_map_config_file() {
- let config_text = r#"{
- "importMap": "import_map.json"
- }"#;
- let config_specifier =
- ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap();
- let config_file = ConfigFile::new(config_text, &config_specifier).unwrap();
- let actual = resolve_import_map_specifier(None, Some(&config_file));
- assert!(actual.is_ok());
- let actual = actual.unwrap();
- assert_eq!(
- actual,
- Some(ModuleSpecifier::parse("file:///deno/import_map.json").unwrap())
- );
+ fn tasks_no_tasks() {
+ run_task_error_test(r#"{}"#, "No tasks found in configuration file");
}
#[test]
- fn resolve_import_map_config_file_remote() {
- let config_text = r#"{
- "importMap": "./import_map.json"
- }"#;
- let config_specifier =
- ModuleSpecifier::parse("https://example.com/deno.jsonc").unwrap();
- let config_file = ConfigFile::new(config_text, &config_specifier).unwrap();
- let actual = resolve_import_map_specifier(None, Some(&config_file));
- assert!(actual.is_ok());
- let actual = actual.unwrap();
- assert_eq!(
- actual,
- Some(
- ModuleSpecifier::parse("https://example.com/import_map.json").unwrap()
- )
+ fn task_name_invalid_chars() {
+ run_task_error_test(
+ r#"{
+ "tasks": {
+ "build": "deno test",
+ "some%test": "deno bundle mod.ts"
+ }
+ }"#,
+ concat!(
+ "Configuration file task names must only contain alpha-numeric ",
+ "characters, colons (:), underscores (_), or dashes (-). Task: some%test",
+ ),
);
}
#[test]
- fn resolve_import_map_flags_take_precedence() {
- let config_text = r#"{
- "importMap": "import_map.json"
- }"#;
- let config_specifier =
- ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap();
- let config_file = ConfigFile::new(config_text, &config_specifier).unwrap();
- let actual =
- resolve_import_map_specifier(Some("import-map.json"), Some(&config_file));
- let import_map_path =
- std::env::current_dir().unwrap().join("import-map.json");
- let expected_specifier =
- ModuleSpecifier::from_file_path(&import_map_path).unwrap();
- assert!(actual.is_ok());
- let actual = actual.unwrap();
- assert_eq!(actual, Some(expected_specifier));
+ fn task_name_non_alpha_starting_char() {
+ run_task_error_test(
+ r#"{
+ "tasks": {
+ "build": "deno test",
+ "1test": "deno bundle mod.ts"
+ }
+ }"#,
+ concat!(
+ "Configuration file task names must start with an ",
+ "alphabetic character. Task: 1test",
+ ),
+ );
}
#[test]
- fn resolve_import_map_none() {
- let config_text = r#"{}"#;
- let config_specifier =
- ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap();
- let config_file = ConfigFile::new(config_text, &config_specifier).unwrap();
- let actual = resolve_import_map_specifier(None, Some(&config_file));
- assert!(actual.is_ok());
- let actual = actual.unwrap();
- assert_eq!(actual, None);
+ fn task_name_empty() {
+ run_task_error_test(
+ r#"{
+ "tasks": {
+ "build": "deno test",
+ "": "deno bundle mod.ts"
+ }
+ }"#,
+ "Configuration file task names cannot be empty",
+ );
}
- #[test]
- fn resolve_import_map_no_config() {
- let actual = resolve_import_map_specifier(None, None);
- assert!(actual.is_ok());
- let actual = actual.unwrap();
- assert_eq!(actual, None);
+ fn run_task_error_test(config_text: &str, expected_error: &str) {
+ let config_dir = ModuleSpecifier::parse("file:///deno/").unwrap();
+ let config_specifier = config_dir.join("tsconfig.json").unwrap();
+ let config_file = ConfigFile::new(config_text, &config_specifier).unwrap();
+ assert_eq!(
+ config_file
+ .resolve_tasks_config()
+ .err()
+ .unwrap()
+ .to_string(),
+ expected_error,
+ );
}
}
diff --git a/cli/args/flags.rs b/cli/args/flags.rs
index f61ead385..8b8cf8d86 100644
--- a/cli/args/flags.rs
+++ b/cli/args/flags.rs
@@ -234,7 +234,7 @@ impl Default for DenoSubcommand {
}
}
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, Copy, PartialEq)]
pub enum TypeCheckMode {
/// Type-check all modules.
All,
@@ -305,7 +305,6 @@ pub struct Flags {
pub compat: bool,
pub no_prompt: bool,
pub reload: bool,
- pub repl: bool,
pub seed: Option<u64>,
pub unstable: bool,
pub unsafely_ignore_certificate_errors: Option<Vec<String>>,
@@ -571,7 +570,6 @@ pub fn flags_from_vec(args: Vec<String>) -> clap::Result<Flags> {
}
fn handle_repl_flags(flags: &mut Flags, repl_flags: ReplFlags) {
- flags.repl = true;
flags.subcommand = DenoSubcommand::Repl(repl_flags);
flags.allow_net = Some(vec![]);
flags.allow_env = Some(vec![]);
@@ -4022,7 +4020,6 @@ mod tests {
assert_eq!(
r.unwrap(),
Flags {
- repl: true,
subcommand: DenoSubcommand::Repl(ReplFlags {
eval_files: None,
eval: None
@@ -4047,7 +4044,6 @@ mod tests {
assert_eq!(
r.unwrap(),
Flags {
- repl: true,
subcommand: DenoSubcommand::Repl(ReplFlags {
eval_files: None,
eval: None
@@ -4085,7 +4081,6 @@ mod tests {
assert_eq!(
r.unwrap(),
Flags {
- repl: true,
subcommand: DenoSubcommand::Repl(ReplFlags {
eval_files: None,
eval: Some("console.log('hello');".to_string()),
@@ -4110,7 +4105,6 @@ mod tests {
assert_eq!(
r.unwrap(),
Flags {
- repl: true,
subcommand: DenoSubcommand::Repl(ReplFlags {
eval_files: Some(vec![
"./a.js".to_string(),
@@ -4770,7 +4764,6 @@ mod tests {
assert_eq!(
r.unwrap(),
Flags {
- repl: true,
subcommand: DenoSubcommand::Repl(ReplFlags {
eval_files: None,
eval: Some("console.log('hello');".to_string()),
@@ -4845,7 +4838,6 @@ mod tests {
assert_eq!(
r.unwrap(),
Flags {
- repl: true,
subcommand: DenoSubcommand::Repl(ReplFlags {
eval_files: None,
eval: None
diff --git a/cli/args/mod.rs b/cli/args/mod.rs
index 375a53122..757c6a8f4 100644
--- a/cli/args/mod.rs
+++ b/cli/args/mod.rs
@@ -5,5 +5,441 @@ pub mod flags;
mod flags_allow_net;
-pub use config_file::*;
+pub use config_file::CompilerOptions;
+pub use config_file::ConfigFile;
+pub use config_file::EmitConfigOptions;
+pub use config_file::FmtConfig;
+pub use config_file::FmtOptionsConfig;
+pub use config_file::LintConfig;
+pub use config_file::LintRulesConfig;
+pub use config_file::MaybeImportsResult;
+pub use config_file::ProseWrap;
+pub use config_file::TsConfig;
pub use flags::*;
+
+use deno_ast::ModuleSpecifier;
+use deno_core::anyhow::anyhow;
+use deno_core::anyhow::bail;
+use deno_core::anyhow::Context;
+use deno_core::error::AnyError;
+use deno_core::normalize_path;
+use deno_core::url::Url;
+use deno_runtime::colors;
+use deno_runtime::deno_tls::rustls::RootCertStore;
+use deno_runtime::inspector_server::InspectorServer;
+use deno_runtime::permissions::PermissionsOptions;
+use std::collections::BTreeMap;
+use std::env;
+use std::net::SocketAddr;
+use std::path::PathBuf;
+
+use crate::compat;
+use crate::deno_dir::DenoDir;
+use crate::emit::get_ts_config_for_emit;
+use crate::emit::TsConfigType;
+use crate::emit::TsConfigWithIgnoredOptions;
+use crate::emit::TsTypeLib;
+use crate::file_fetcher::get_root_cert_store;
+use crate::file_fetcher::CacheSetting;
+use crate::lockfile::Lockfile;
+use crate::version;
+
+/// Holds the common configuration used by many sub commands
+/// and provides some helper function for creating common objects.
+pub struct RootConfig {
+ // the source of the configuration is a detail the rest of the
+ // application need not concern itself with, so keep these private
+ flags: Flags,
+ maybe_config_file: Option<ConfigFile>,
+}
+
+impl RootConfig {
+ pub fn from_flags(flags: Flags) -> Result<Self, AnyError> {
+ if let Some(insecure_allowlist) =
+ flags.unsafely_ignore_certificate_errors.as_ref()
+ {
+ let domains = if insecure_allowlist.is_empty() {
+ "for all hostnames".to_string()
+ } else {
+ format!("for: {}", insecure_allowlist.join(", "))
+ };
+ let msg =
+ format!("DANGER: TLS certificate validation is disabled {}", domains);
+ eprintln!("{}", colors::yellow(msg));
+ }
+
+ let maybe_config_file = ConfigFile::discover(&flags)?;
+ Ok(Self {
+ maybe_config_file,
+ flags,
+ })
+ }
+
+ pub fn maybe_config_file_specifier(&self) -> Option<ModuleSpecifier> {
+ self.maybe_config_file.as_ref().map(|f| f.specifier.clone())
+ }
+
+ pub fn ts_type_lib_window(&self) -> TsTypeLib {
+ if self.flags.unstable {
+ TsTypeLib::UnstableDenoWindow
+ } else {
+ TsTypeLib::DenoWindow
+ }
+ }
+
+ pub fn ts_type_lib_worker(&self) -> TsTypeLib {
+ if self.flags.unstable {
+ TsTypeLib::UnstableDenoWorker
+ } else {
+ TsTypeLib::DenoWorker
+ }
+ }
+
+ pub fn cache_setting(&self) -> CacheSetting {
+ if self.flags.cached_only {
+ CacheSetting::Only
+ } else if !self.flags.cache_blocklist.is_empty() {
+ CacheSetting::ReloadSome(self.flags.cache_blocklist.clone())
+ } else if self.flags.reload {
+ CacheSetting::ReloadAll
+ } else {
+ CacheSetting::Use
+ }
+ }
+
+ pub fn resolve_deno_dir(&self) -> Result<DenoDir, AnyError> {
+ Ok(DenoDir::new(self.maybe_custom_root())?)
+ }
+
+ /// Based on an optional command line import map path and an optional
+ /// configuration file, return a resolved module specifier to an import map.
+ pub fn resolve_import_map_path(
+ &self,
+ ) -> Result<Option<ModuleSpecifier>, AnyError> {
+ resolve_import_map_specifier(
+ self.flags.import_map_path.as_deref(),
+ self.maybe_config_file.as_ref(),
+ )
+ }
+
+ pub fn resolve_root_cert_store(&self) -> Result<RootCertStore, AnyError> {
+ get_root_cert_store(
+ None,
+ self.flags.ca_stores.clone(),
+ self.flags.ca_file.clone(),
+ )
+ }
+
+ pub fn resolve_ts_config_for_emit(
+ &self,
+ config_type: TsConfigType,
+ ) -> Result<TsConfigWithIgnoredOptions, AnyError> {
+ get_ts_config_for_emit(config_type, self.maybe_config_file.as_ref())
+ }
+
+ /// Resolves the storage key to use based on the current flags, config, or main module.
+ pub fn resolve_storage_key(
+ &self,
+ main_module: &ModuleSpecifier,
+ ) -> Option<String> {
+ if let Some(location) = &self.flags.location {
+ // if a location is set, then the ascii serialization of the location is
+ // used, unless the origin is opaque, and then no storage origin is set, as
+ // we can't expect the origin to be reproducible
+ let storage_origin = location.origin().ascii_serialization();
+ if storage_origin == "null" {
+ None
+ } else {
+ Some(storage_origin)
+ }
+ } else if let Some(config_file) = &self.maybe_config_file {
+ // otherwise we will use the path to the config file
+ Some(config_file.specifier.to_string())
+ } else {
+ // otherwise we will use the path to the main module
+ Some(main_module.to_string())
+ }
+ }
+
+ pub fn resolve_inspector_server(&self) -> Option<InspectorServer> {
+ let maybe_inspect_host = self.flags.inspect.or(self.flags.inspect_brk);
+ maybe_inspect_host
+ .map(|host| InspectorServer::new(host, version::get_user_agent()))
+ }
+
+ pub fn resolve_lock_file(&self) -> Result<Option<Lockfile>, AnyError> {
+ if let Some(filename) = &self.flags.lock {
+ let lockfile = Lockfile::new(filename.clone(), self.flags.lock_write)?;
+ Ok(Some(lockfile))
+ } else {
+ Ok(None)
+ }
+ }
+
+ pub fn resolve_tasks_config(
+ &self,
+ ) -> Result<BTreeMap<String, String>, AnyError> {
+ if let Some(config_file) = &self.maybe_config_file {
+ config_file.resolve_tasks_config()
+ } else {
+ bail!("No config file found")
+ }
+ }
+
+ /// Return the implied JSX import source module.
+ pub fn to_maybe_jsx_import_source_module(&self) -> Option<String> {
+ self
+ .maybe_config_file
+ .as_ref()
+ .and_then(|c| c.to_maybe_jsx_import_source_module())
+ }
+
+ /// Return any imports that should be brought into the scope of the module
+ /// graph.
+ pub fn to_maybe_imports(&self) -> MaybeImportsResult {
+ let mut imports = Vec::new();
+ if let Some(config_file) = &self.maybe_config_file {
+ if let Some(config_imports) = config_file.to_maybe_imports()? {
+ imports.extend(config_imports);
+ }
+ }
+ if self.flags.compat {
+ imports.extend(compat::get_node_imports());
+ }
+ if imports.is_empty() {
+ Ok(None)
+ } else {
+ Ok(Some(imports))
+ }
+ }
+
+ pub fn to_lint_config(&self) -> Result<Option<LintConfig>, AnyError> {
+ if let Some(config_file) = &self.maybe_config_file {
+ config_file.to_lint_config()
+ } else {
+ Ok(None)
+ }
+ }
+
+ pub fn to_fmt_config(&self) -> Result<Option<FmtConfig>, AnyError> {
+ if let Some(config) = &self.maybe_config_file {
+ config.to_fmt_config()
+ } else {
+ Ok(None)
+ }
+ }
+
+ /// Vector of user script CLI arguments.
+ pub fn argv(&self) -> &Vec<String> {
+ &self.flags.argv
+ }
+
+ pub fn check_js(&self) -> bool {
+ self
+ .maybe_config_file
+ .as_ref()
+ .map(|cf| cf.get_check_js())
+ .unwrap_or(false)
+ }
+
+ pub fn compat(&self) -> bool {
+ self.flags.compat
+ }
+
+ pub fn coverage_dir(&self) -> Option<&String> {
+ self.flags.coverage_dir.as_ref()
+ }
+
+ pub fn enable_testing_features(&self) -> bool {
+ self.flags.enable_testing_features
+ }
+
+ pub fn inspect_brk(&self) -> Option<SocketAddr> {
+ self.flags.inspect_brk
+ }
+
+ pub fn log_level(&self) -> Option<log::Level> {
+ self.flags.log_level
+ }
+
+ pub fn location_flag(&self) -> Option<&Url> {
+ self.flags.location.as_ref()
+ }
+
+ pub fn maybe_custom_root(&self) -> Option<PathBuf> {
+ self
+ .flags
+ .cache_path
+ .clone()
+ .or_else(|| env::var("DENO_DIR").map(String::into).ok())
+ }
+
+ pub fn no_clear_screen(&self) -> bool {
+ self.flags.no_clear_screen
+ }
+
+ pub fn no_remote(&self) -> bool {
+ self.flags.no_remote
+ }
+
+ pub fn permissions_options(&self) -> PermissionsOptions {
+ self.flags.permissions_options()
+ }
+
+ pub fn reload_flag(&self) -> bool {
+ self.flags.reload
+ }
+
+ pub fn seed(&self) -> Option<u64> {
+ self.flags.seed
+ }
+
+ pub fn sub_command(&self) -> &DenoSubcommand {
+ &self.flags.subcommand
+ }
+
+ pub fn type_check_mode(&self) -> TypeCheckMode {
+ self.flags.type_check_mode
+ }
+
+ pub fn unsafely_ignore_certificate_errors(&self) -> Option<&Vec<String>> {
+ self.flags.unsafely_ignore_certificate_errors.as_ref()
+ }
+
+ pub fn unstable(&self) -> bool {
+ self.flags.unstable
+ }
+
+ pub fn watch_paths(&self) -> Option<&Vec<PathBuf>> {
+ self.flags.watch.as_ref()
+ }
+}
+
+fn resolve_import_map_specifier(
+ maybe_import_map_path: Option<&str>,
+ maybe_config_file: Option<&ConfigFile>,
+) -> Result<Option<ModuleSpecifier>, AnyError> {
+ if let Some(import_map_path) = maybe_import_map_path {
+ if let Some(config_file) = &maybe_config_file {
+ if config_file.to_import_map_path().is_some() {
+ log::warn!("{} the configuration file \"{}\" contains an entry for \"importMap\" that is being ignored.", crate::colors::yellow("Warning"), config_file.specifier);
+ }
+ }
+ let specifier = deno_core::resolve_url_or_path(import_map_path)
+ .context(format!("Bad URL (\"{}\") for import map.", import_map_path))?;
+ return Ok(Some(specifier));
+ } else if let Some(config_file) = &maybe_config_file {
+ // when the import map is specifier in a config file, it needs to be
+ // resolved relative to the config file, versus the CWD like with the flag
+ // and with config files, we support both local and remote config files,
+ // so we have treat them differently.
+ if let Some(import_map_path) = config_file.to_import_map_path() {
+ let specifier =
+ // with local config files, it might be common to specify an import
+ // map like `"importMap": "import-map.json"`, which is resolvable if
+ // the file is resolved like a file path, so we will coerce the config
+ // file into a file path if possible and join the import map path to
+ // the file path.
+ if let Ok(config_file_path) = config_file.specifier.to_file_path() {
+ let import_map_file_path = normalize_path(config_file_path
+ .parent()
+ .ok_or_else(|| {
+ anyhow!("Bad config file specifier: {}", config_file.specifier)
+ })?
+ .join(&import_map_path));
+ ModuleSpecifier::from_file_path(import_map_file_path).unwrap()
+ // otherwise if the config file is remote, we have no choice but to
+ // use "import resolution" with the config file as the base.
+ } else {
+ deno_core::resolve_import(&import_map_path, config_file.specifier.as_str())
+ .context(format!(
+ "Bad URL (\"{}\") for import map.",
+ import_map_path
+ ))?
+ };
+ return Ok(Some(specifier));
+ }
+ }
+ Ok(None)
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[cfg(not(windows))]
+ #[test]
+ fn resolve_import_map_config_file() {
+ let config_text = r#"{
+ "importMap": "import_map.json"
+ }"#;
+ let config_specifier =
+ ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap();
+ let config_file = ConfigFile::new(config_text, &config_specifier).unwrap();
+ let actual = resolve_import_map_specifier(None, Some(&config_file));
+ assert!(actual.is_ok());
+ let actual = actual.unwrap();
+ assert_eq!(
+ actual,
+ Some(ModuleSpecifier::parse("file:///deno/import_map.json").unwrap())
+ );
+ }
+
+ #[test]
+ fn resolve_import_map_config_file_remote() {
+ let config_text = r#"{
+ "importMap": "./import_map.json"
+ }"#;
+ let config_specifier =
+ ModuleSpecifier::parse("https://example.com/deno.jsonc").unwrap();
+ let config_file = ConfigFile::new(config_text, &config_specifier).unwrap();
+ let actual = resolve_import_map_specifier(None, Some(&config_file));
+ assert!(actual.is_ok());
+ let actual = actual.unwrap();
+ assert_eq!(
+ actual,
+ Some(
+ ModuleSpecifier::parse("https://example.com/import_map.json").unwrap()
+ )
+ );
+ }
+
+ #[test]
+ fn resolve_import_map_flags_take_precedence() {
+ let config_text = r#"{
+ "importMap": "import_map.json"
+ }"#;
+ let config_specifier =
+ ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap();
+ let config_file = ConfigFile::new(config_text, &config_specifier).unwrap();
+ let actual =
+ resolve_import_map_specifier(Some("import-map.json"), Some(&config_file));
+ let import_map_path =
+ std::env::current_dir().unwrap().join("import-map.json");
+ let expected_specifier =
+ ModuleSpecifier::from_file_path(&import_map_path).unwrap();
+ assert!(actual.is_ok());
+ let actual = actual.unwrap();
+ assert_eq!(actual, Some(expected_specifier));
+ }
+
+ #[test]
+ fn resolve_import_map_none() {
+ let config_text = r#"{}"#;
+ let config_specifier =
+ ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap();
+ let config_file = ConfigFile::new(config_text, &config_specifier).unwrap();
+ let actual = resolve_import_map_specifier(None, Some(&config_file));
+ assert!(actual.is_ok());
+ let actual = actual.unwrap();
+ assert_eq!(actual, None);
+ }
+
+ #[test]
+ fn resolve_import_map_no_config() {
+ let actual = resolve_import_map_specifier(None, None);
+ assert!(actual.is_ok());
+ let actual = actual.unwrap();
+ assert_eq!(actual, None);
+ }
+}