mirror of
https://github.com/denoland/deno.git
synced 2025-08-03 18:38:33 +00:00
refactor: add RootConfig
(#14985)
This commit is contained in:
parent
5b7bcefa11
commit
01adbb1efb
21 changed files with 917 additions and 819 deletions
|
@ -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 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_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_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_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_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_none() {
|
||||
let config_text = r#"{}"#;
|
||||
let config_specifier =
|
||||
ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap();
|
||||
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();
|
||||
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);
|
||||
assert_eq!(
|
||||
config_file
|
||||
.resolve_tasks_config()
|
||||
.err()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
expected_error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
438
cli/args/mod.rs
438
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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue