summaryrefslogtreecommitdiff
path: root/cli/config_file.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/config_file.rs')
-rw-r--r--cli/config_file.rs174
1 files changed, 121 insertions, 53 deletions
diff --git a/cli/config_file.rs b/cli/config_file.rs
index 879db3451..363a215eb 100644
--- a/cli/config_file.rs
+++ b/cli/config_file.rs
@@ -1,8 +1,11 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
use crate::fs_util::canonicalize_path;
+use crate::fs_util::specifier_parent;
+use crate::fs_util::specifier_to_file_path;
use deno_core::anyhow::anyhow;
+use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::custom_error;
use deno_core::error::AnyError;
@@ -17,7 +20,6 @@ use std::collections::BTreeMap;
use std::collections::HashMap;
use std::fmt;
use std::path::Path;
-use std::path::PathBuf;
pub(crate) type MaybeImportsResult =
Result<Option<Vec<(ModuleSpecifier, Vec<String>)>>, AnyError>;
@@ -54,15 +56,15 @@ pub struct CompilerOptions {
#[derive(Debug, Clone, PartialEq)]
pub struct IgnoredCompilerOptions {
pub items: Vec<String>,
- pub maybe_path: Option<PathBuf>,
+ pub maybe_specifier: Option<ModuleSpecifier>,
}
impl fmt::Display for IgnoredCompilerOptions {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut codes = self.items.clone();
codes.sort();
- if let Some(path) = &self.maybe_path {
- write!(f, "Unsupported compiler options in \"{}\".\n The following options were ignored:\n {}", path.to_string_lossy(), codes.join(", "))
+ if let Some(specifier) = &self.maybe_specifier {
+ write!(f, "Unsupported compiler options in \"{}\".\n The following options were ignored:\n {}", specifier, codes.join(", "))
} else {
write!(f, "Unsupported compiler options provided.\n The following options were ignored:\n {}", codes.join(", "))
}
@@ -169,7 +171,7 @@ pub fn json_merge(a: &mut Value, b: &Value) {
fn parse_compiler_options(
compiler_options: &HashMap<String, Value>,
- maybe_path: Option<PathBuf>,
+ maybe_specifier: Option<ModuleSpecifier>,
is_runtime: bool,
) -> Result<(Value, Option<IgnoredCompilerOptions>), AnyError> {
let mut filtered: HashMap<String, Value> = HashMap::new();
@@ -187,7 +189,10 @@ fn parse_compiler_options(
}
let value = serde_json::to_value(filtered)?;
let maybe_ignored_options = if !items.is_empty() {
- Some(IgnoredCompilerOptions { items, maybe_path })
+ Some(IgnoredCompilerOptions {
+ items,
+ maybe_specifier,
+ })
} else {
None
};
@@ -292,27 +297,53 @@ struct SerializedFilesConfig {
}
impl SerializedFilesConfig {
- pub fn into_resolved(self, config_file_path: &Path) -> FilesConfig {
- let config_dir = config_file_path.parent().unwrap();
- FilesConfig {
+ pub fn into_resolved(
+ self,
+ config_file_specifier: &ModuleSpecifier,
+ ) -> Result<FilesConfig, AnyError> {
+ let config_dir = specifier_parent(config_file_specifier);
+ Ok(FilesConfig {
include: self
.include
.into_iter()
- .map(|p| config_dir.join(p))
- .collect(),
+ .map(|p| config_dir.join(&p))
+ .collect::<Result<Vec<ModuleSpecifier>, _>>()?,
exclude: self
.exclude
.into_iter()
- .map(|p| config_dir.join(p))
- .collect(),
- }
+ .map(|p| config_dir.join(&p))
+ .collect::<Result<Vec<ModuleSpecifier>, _>>()?,
+ })
}
}
#[derive(Clone, Debug, Default)]
pub struct FilesConfig {
- pub include: Vec<PathBuf>,
- pub exclude: Vec<PathBuf>,
+ pub include: Vec<ModuleSpecifier>,
+ pub exclude: Vec<ModuleSpecifier>,
+}
+
+impl FilesConfig {
+ /// Gets if the provided specifier is allowed based on the includes
+ /// and excludes in the configuration file.
+ pub fn matches_specifier(&self, specifier: &ModuleSpecifier) -> bool {
+ // Skip files which is in the exclude list.
+ let specifier_text = specifier.as_str();
+ if self
+ .exclude
+ .iter()
+ .any(|i| specifier_text.starts_with(i.as_str()))
+ {
+ return false;
+ }
+
+ // Ignore files not in the include list if it's not empty.
+ self.include.is_empty()
+ || self
+ .include
+ .iter()
+ .any(|i| specifier_text.starts_with(i.as_str()))
+ }
}
#[derive(Clone, Debug, Default, Deserialize)]
@@ -323,11 +354,14 @@ struct SerializedLintConfig {
}
impl SerializedLintConfig {
- pub fn into_resolved(self, config_file_path: &Path) -> LintConfig {
- LintConfig {
+ pub fn into_resolved(
+ self,
+ config_file_specifier: &ModuleSpecifier,
+ ) -> Result<LintConfig, AnyError> {
+ Ok(LintConfig {
rules: self.rules,
- files: self.files.into_resolved(config_file_path),
- }
+ files: self.files.into_resolved(config_file_specifier)?,
+ })
}
}
@@ -363,11 +397,14 @@ struct SerializedFmtConfig {
}
impl SerializedFmtConfig {
- pub fn into_resolved(self, config_file_path: &Path) -> FmtConfig {
- FmtConfig {
+ pub fn into_resolved(
+ self,
+ config_file_specifier: &ModuleSpecifier,
+ ) -> Result<FmtConfig, AnyError> {
+ Ok(FmtConfig {
options: self.options,
- files: self.files.into_resolved(config_file_path),
- }
+ files: self.files.into_resolved(config_file_specifier)?,
+ })
}
}
@@ -387,7 +424,7 @@ pub struct ConfigFileJson {
#[derive(Clone, Debug)]
pub struct ConfigFile {
- pub path: PathBuf,
+ pub specifier: ModuleSpecifier,
pub json: ConfigFileJson,
}
@@ -409,24 +446,46 @@ impl ConfigFile {
),
)
})?;
- let config_text = std::fs::read_to_string(config_path.clone())?;
- Self::new(&config_text, &config_path)
+ let config_specifier = ModuleSpecifier::from_file_path(&config_path)
+ .map_err(|_| {
+ anyhow!(
+ "Could not convert path to specifier. Path: {}",
+ config_path.display()
+ )
+ })?;
+ Self::from_specifier(&config_specifier)
+ }
+
+ pub fn from_specifier(specifier: &ModuleSpecifier) -> Result<Self, AnyError> {
+ let config_path = specifier_to_file_path(specifier)?;
+ let config_text = match std::fs::read_to_string(&config_path) {
+ Ok(text) => text,
+ Err(err) => bail!(
+ "Error reading config file {}: {}",
+ specifier,
+ err.to_string()
+ ),
+ };
+ Self::new(&config_text, specifier)
}
- pub fn new(text: &str, path: &Path) -> Result<Self, AnyError> {
+ pub fn new(
+ text: &str,
+ specifier: &ModuleSpecifier,
+ ) -> Result<Self, AnyError> {
let jsonc = match jsonc_parser::parse_to_serde_value(text) {
Ok(None) => json!({}),
Ok(Some(value)) if value.is_object() => value,
Ok(Some(_)) => {
return Err(anyhow!(
"config file JSON {:?} should be an object",
- path.to_str().unwrap()
+ specifier,
))
}
Err(e) => {
return Err(anyhow!(
"Unable to parse config file JSON {:?} because of {}",
- path.to_str().unwrap(),
+ specifier,
e.to_string()
))
}
@@ -434,7 +493,7 @@ impl ConfigFile {
let json: ConfigFileJson = serde_json::from_value(jsonc)?;
Ok(Self {
- path: path.to_owned(),
+ specifier: specifier.to_owned(),
json,
})
}
@@ -448,7 +507,7 @@ impl ConfigFile {
let options: HashMap<String, Value> =
serde_json::from_value(compiler_options)
.context("compilerOptions should be an object")?;
- parse_compiler_options(&options, Some(self.path.to_owned()), false)
+ parse_compiler_options(&options, Some(self.specifier.to_owned()), false)
} else {
Ok((json!({}), None))
}
@@ -458,7 +517,7 @@ impl ConfigFile {
if let Some(config) = self.json.lint.clone() {
let lint_config: SerializedLintConfig = serde_json::from_value(config)
.context("Failed to parse \"lint\" configuration")?;
- Ok(Some(lint_config.into_resolved(&self.path)))
+ Ok(Some(lint_config.into_resolved(&self.specifier)?))
} else {
Ok(None)
}
@@ -476,8 +535,6 @@ impl ConfigFile {
};
let compiler_options: CompilerOptions =
serde_json::from_value(compiler_options_value.clone())?;
- let referrer = ModuleSpecifier::from_file_path(&self.path)
- .map_err(|_| custom_error("TypeError", "bad config file specifier"))?;
if let Some(types) = compiler_options.types {
imports.extend(types);
}
@@ -493,6 +550,7 @@ impl ConfigFile {
));
}
if !imports.is_empty() {
+ let referrer = self.specifier.clone();
Ok(Some(vec![(referrer, imports)]))
} else {
Ok(None)
@@ -516,7 +574,7 @@ impl ConfigFile {
if let Some(config) = self.json.fmt.clone() {
let fmt_config: SerializedFmtConfig = serde_json::from_value(config)
.context("Failed to parse \"fmt\" configuration")?;
- Ok(Some(fmt_config.into_resolved(&self.path)))
+ Ok(Some(fmt_config.into_resolved(&self.specifier)?))
} else {
Ok(None)
}
@@ -603,9 +661,9 @@ mod tests {
}
}
}"#;
- let config_dir = PathBuf::from("/deno");
- let config_path = config_dir.join("tsconfig.json");
- let config_file = ConfigFile::new(config_text, &config_path).unwrap();
+ 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 (options_value, ignored) =
config_file.to_compiler_options().expect("error parsing");
assert!(options_value.is_object());
@@ -616,7 +674,7 @@ mod tests {
ignored,
Some(IgnoredCompilerOptions {
items: vec!["build".to_string()],
- maybe_path: Some(config_path),
+ maybe_specifier: Some(config_specifier),
}),
);
@@ -624,10 +682,13 @@ mod tests {
.to_lint_config()
.expect("error parsing lint object")
.expect("lint object should be defined");
- assert_eq!(lint_config.files.include, vec![config_dir.join("src")]);
+ assert_eq!(
+ lint_config.files.include,
+ vec![config_dir.join("src/").unwrap()]
+ );
assert_eq!(
lint_config.files.exclude,
- vec![config_dir.join("src/testdata/")]
+ vec![config_dir.join("src/testdata/").unwrap()]
);
assert_eq!(
lint_config.rules.include,
@@ -643,10 +704,13 @@ mod tests {
.to_fmt_config()
.expect("error parsing fmt object")
.expect("fmt object should be defined");
- assert_eq!(fmt_config.files.include, vec![config_dir.join("src")]);
+ assert_eq!(
+ fmt_config.files.include,
+ vec![config_dir.join("src/").unwrap()]
+ );
assert_eq!(
fmt_config.files.exclude,
- vec![config_dir.join("src/testdata/")]
+ vec![config_dir.join("src/testdata/").unwrap()]
);
assert_eq!(fmt_config.options.use_tabs, Some(true));
assert_eq!(fmt_config.options.line_width, Some(80));
@@ -657,8 +721,9 @@ mod tests {
#[test]
fn test_parse_config_with_empty_file() {
let config_text = "";
- let config_path = PathBuf::from("/deno/tsconfig.json");
- let config_file = ConfigFile::new(config_text, &config_path).unwrap();
+ let config_specifier =
+ ModuleSpecifier::parse("file:///deno/tsconfig.json").unwrap();
+ let config_file = ConfigFile::new(config_text, &config_specifier).unwrap();
let (options_value, _) =
config_file.to_compiler_options().expect("error parsing");
assert!(options_value.is_object());
@@ -667,8 +732,9 @@ mod tests {
#[test]
fn test_parse_config_with_commented_file() {
let config_text = r#"//{"foo":"bar"}"#;
- let config_path = PathBuf::from("/deno/tsconfig.json");
- let config_file = ConfigFile::new(config_text, &config_path).unwrap();
+ let config_specifier =
+ ModuleSpecifier::parse("file:///deno/tsconfig.json").unwrap();
+ let config_file = ConfigFile::new(config_text, &config_specifier).unwrap();
let (options_value, _) =
config_file.to_compiler_options().expect("error parsing");
assert!(options_value.is_object());
@@ -677,17 +743,19 @@ mod tests {
#[test]
fn test_parse_config_with_invalid_file() {
let config_text = "{foo:bar}";
- let config_path = PathBuf::from("/deno/tsconfig.json");
+ let config_specifier =
+ ModuleSpecifier::parse("file:///deno/tsconfig.json").unwrap();
// Emit error: Unable to parse config file JSON "<config_path>" because of Unexpected token on line 1 column 6.
- assert!(ConfigFile::new(config_text, &config_path).is_err());
+ assert!(ConfigFile::new(config_text, &config_specifier).is_err());
}
#[test]
fn test_parse_config_with_not_object_file() {
let config_text = "[]";
- let config_path = PathBuf::from("/deno/tsconfig.json");
+ let config_specifier =
+ ModuleSpecifier::parse("file:///deno/tsconfig.json").unwrap();
// Emit error: config file JSON "<config_path>" should be an object
- assert!(ConfigFile::new(config_text, &config_path).is_err());
+ assert!(ConfigFile::new(config_text, &config_specifier).is_err());
}
#[test]
@@ -717,7 +785,7 @@ mod tests {
maybe_ignored_options,
Some(IgnoredCompilerOptions {
items: vec!["build".to_string()],
- maybe_path: None
+ maybe_specifier: None
})
);
}