1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-21 15:04:11 -05:00

feat(cli): flatten deno.json configuaration (#17799)

This commit is contained in:
scarf 2023-04-27 12:02:36 +09:00 committed by GitHub
parent 4192978c3a
commit 90a5ef5e34
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 535 additions and 149 deletions

View file

@ -279,7 +279,7 @@ impl Serialize for TsConfig {
}
}
#[derive(Clone, Debug, Default, Deserialize)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
#[serde(default, deny_unknown_fields)]
pub struct LintRulesConfig {
pub tags: Option<Vec<String>>,
@ -287,7 +287,7 @@ pub struct LintRulesConfig {
pub exclude: Option<Vec<String>>,
}
#[derive(Clone, Debug, Default, Deserialize)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
#[serde(default, deny_unknown_fields)]
struct SerializedFilesConfig {
pub include: Vec<String>,
@ -319,6 +319,10 @@ impl SerializedFilesConfig {
.collect::<Result<Vec<_>, _>>()?,
})
}
pub fn is_empty(&self) -> bool {
self.include.is_empty() && self.exclude.is_empty()
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
@ -346,11 +350,57 @@ impl FilesConfig {
}
}
#[derive(Clone, Debug, Default, Deserialize)]
/// Choose between flat and nested files configuration.
///
/// `files` has precedence over `deprecated_files`.
/// when `deprecated_files` is present, a warning is logged.
///
/// caveat: due to default values, it's not possible to distinguish between
/// an empty configuration and a configuration with default values.
/// `{ "files": {} }` is equivalent to `{ "files": { "include": [], "exclude": [] } }`
/// and it wouldn't be able to emit warning for `{ "files": {}, "exclude": [] }`.
///
/// # Arguments
///
/// * `files` - Flat configuration.
/// * `deprecated_files` - Nested configuration. ("Files")
fn choose_files(
files: SerializedFilesConfig,
deprecated_files: SerializedFilesConfig,
) -> SerializedFilesConfig {
const DEPRECATED_FILES: &str =
"Warning: \"files\" configuration is deprecated";
const FLAT_CONFIG: &str = "\"include\" and \"exclude\"";
let (files_nonempty, deprecated_files_nonempty) =
(!files.is_empty(), !deprecated_files.is_empty());
match (files_nonempty, deprecated_files_nonempty) {
(true, true) => {
log::warn!("{DEPRECATED_FILES} and ignored by {FLAT_CONFIG}.");
files
}
(true, false) => files,
(false, true) => {
log::warn!("{DEPRECATED_FILES}. Please use {FLAT_CONFIG} instead.");
deprecated_files
}
(false, false) => SerializedFilesConfig::default(),
}
}
/// `lint` config representation for serde
///
/// fields `include` and `exclude` are expanded from [SerializedFilesConfig].
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
#[serde(default, deny_unknown_fields)]
struct SerializedLintConfig {
pub rules: LintRulesConfig,
pub files: SerializedFilesConfig,
pub include: Vec<String>,
pub exclude: Vec<String>,
#[serde(rename = "files")]
pub deprecated_files: SerializedFilesConfig,
pub report: Option<String>,
}
@ -359,22 +409,26 @@ impl SerializedLintConfig {
self,
config_file_specifier: &ModuleSpecifier,
) -> Result<LintConfig, AnyError> {
let (include, exclude) = (self.include, self.exclude);
let files = SerializedFilesConfig { include, exclude };
Ok(LintConfig {
rules: self.rules,
files: self.files.into_resolved(config_file_specifier)?,
files: choose_files(files, self.deprecated_files)
.into_resolved(config_file_specifier)?,
report: self.report,
})
}
}
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug, Default, PartialEq)]
pub struct LintConfig {
pub rules: LintRulesConfig,
pub files: FilesConfig,
pub report: Option<String>,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub enum ProseWrap {
Always,
@ -382,7 +436,7 @@ pub enum ProseWrap {
Preserve,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
#[serde(default, deny_unknown_fields, rename_all = "camelCase")]
pub struct FmtOptionsConfig {
pub use_tabs: Option<bool>,
@ -393,11 +447,75 @@ pub struct FmtOptionsConfig {
pub semi_colons: Option<bool>,
}
#[derive(Clone, Debug, Default, Deserialize)]
#[serde(default, deny_unknown_fields)]
impl FmtOptionsConfig {
pub fn is_empty(&self) -> bool {
self.use_tabs.is_none()
&& self.line_width.is_none()
&& self.indent_width.is_none()
&& self.single_quote.is_none()
&& self.prose_wrap.is_none()
&& self.semi_colons.is_none()
}
}
/// Choose between flat and nested fmt options.
///
/// `options` has precedence over `deprecated_options`.
/// when `deprecated_options` is present, a warning is logged.
///
/// caveat: due to default values, it's not possible to distinguish between
/// an empty configuration and a configuration with default values.
/// `{ "fmt": {} } is equivalent to `{ "fmt": { "options": {} } }`
/// and it wouldn't be able to emit warning for `{ "fmt": { "options": {}, "semiColons": "false" } }`.
///
/// # Arguments
///
/// * `options` - Flat options.
/// * `deprecated_options` - Nested files configuration ("option").
fn choose_fmt_options(
options: FmtOptionsConfig,
deprecated_options: FmtOptionsConfig,
) -> FmtOptionsConfig {
const DEPRECATED_OPTIONS: &str =
"Warning: \"options\" configuration is deprecated";
const FLAT_OPTION: &str = "\"flat\" options";
let (options_nonempty, deprecated_options_nonempty) =
(!options.is_empty(), !deprecated_options.is_empty());
match (options_nonempty, deprecated_options_nonempty) {
(true, true) => {
log::warn!("{DEPRECATED_OPTIONS} and ignored by {FLAT_OPTION}.");
options
}
(true, false) => options,
(false, true) => {
log::warn!("{DEPRECATED_OPTIONS}. Please use {FLAT_OPTION} instead.");
deprecated_options
}
(false, false) => FmtOptionsConfig::default(),
}
}
/// `fmt` config representation for serde
///
/// fields from `use_tabs`..`semi_colons` are expanded from [FmtOptionsConfig].
/// fields `include` and `exclude` are expanded from [SerializedFilesConfig].
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
#[serde(default, deny_unknown_fields, rename_all = "camelCase")]
struct SerializedFmtConfig {
pub options: FmtOptionsConfig,
pub files: SerializedFilesConfig,
pub use_tabs: Option<bool>,
pub line_width: Option<u32>,
pub indent_width: Option<u8>,
pub single_quote: Option<bool>,
pub prose_wrap: Option<ProseWrap>,
pub semi_colons: Option<bool>,
#[serde(rename = "options")]
pub deprecated_options: FmtOptionsConfig,
pub include: Vec<String>,
pub exclude: Vec<String>,
#[serde(rename = "files")]
pub deprecated_files: SerializedFilesConfig,
}
impl SerializedFmtConfig {
@ -405,23 +523,41 @@ impl SerializedFmtConfig {
self,
config_file_specifier: &ModuleSpecifier,
) -> Result<FmtConfig, AnyError> {
let (include, exclude) = (self.include, self.exclude);
let files = SerializedFilesConfig { include, exclude };
let options = FmtOptionsConfig {
use_tabs: self.use_tabs,
line_width: self.line_width,
indent_width: self.indent_width,
single_quote: self.single_quote,
prose_wrap: self.prose_wrap,
semi_colons: self.semi_colons,
};
Ok(FmtConfig {
options: self.options,
files: self.files.into_resolved(config_file_specifier)?,
options: choose_fmt_options(options, self.deprecated_options),
files: choose_files(files, self.deprecated_files)
.into_resolved(config_file_specifier)?,
})
}
}
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug, Default, PartialEq)]
pub struct FmtConfig {
pub options: FmtOptionsConfig,
pub files: FilesConfig,
}
#[derive(Clone, Debug, Default, Deserialize)]
/// `test` config representation for serde
///
/// fields `include` and `exclude` are expanded from [SerializedFilesConfig].
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
#[serde(default, deny_unknown_fields)]
struct SerializedTestConfig {
pub files: SerializedFilesConfig,
pub include: Vec<String>,
pub exclude: Vec<String>,
#[serde(rename = "files")]
pub deprecated_files: SerializedFilesConfig,
}
impl SerializedTestConfig {
@ -429,21 +565,31 @@ impl SerializedTestConfig {
self,
config_file_specifier: &ModuleSpecifier,
) -> Result<TestConfig, AnyError> {
let (include, exclude) = (self.include, self.exclude);
let files = SerializedFilesConfig { include, exclude };
Ok(TestConfig {
files: self.files.into_resolved(config_file_specifier)?,
files: choose_files(files, self.deprecated_files)
.into_resolved(config_file_specifier)?,
})
}
}
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug, Default, PartialEq)]
pub struct TestConfig {
pub files: FilesConfig,
}
#[derive(Clone, Debug, Default, Deserialize)]
/// `bench` config representation for serde
///
/// fields `include` and `exclude` are expanded from [SerializedFilesConfig].
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
#[serde(default, deny_unknown_fields)]
struct SerializedBenchConfig {
pub files: SerializedFilesConfig,
pub include: Vec<String>,
pub exclude: Vec<String>,
#[serde(rename = "files")]
pub deprecated_files: SerializedFilesConfig,
}
impl SerializedBenchConfig {
@ -451,18 +597,22 @@ impl SerializedBenchConfig {
self,
config_file_specifier: &ModuleSpecifier,
) -> Result<BenchConfig, AnyError> {
let (include, exclude) = (self.include, self.exclude);
let files = SerializedFilesConfig { include, exclude };
Ok(BenchConfig {
files: self.files.into_resolved(config_file_specifier)?,
files: choose_files(files, self.deprecated_files)
.into_resolved(config_file_specifier)?,
})
}
}
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug, Default, PartialEq)]
pub struct BenchConfig {
pub files: FilesConfig,
}
#[derive(Clone, Debug, Deserialize)]
#[derive(Clone, Debug, Deserialize, PartialEq)]
#[serde(untagged)]
pub enum LockConfig {
Bool(bool),
@ -999,6 +1149,12 @@ mod tests {
use deno_core::serde_json::json;
use pretty_assertions::assert_eq;
fn unpack_object<T>(result: Result<Option<T>, AnyError>, name: &str) -> T {
result
.unwrap_or_else(|err| panic!("error parsing {name} object but got {err}"))
.unwrap_or_else(|| panic!("{name} object should be defined"))
}
#[test]
fn read_config_file_absolute() {
let path = test_util::testdata_path().join("module_graph/tsconfig.json");
@ -1043,27 +1199,21 @@ mod tests {
"strict": true
},
"lint": {
"files": {
"include": ["src/"],
"exclude": ["src/testdata/"]
},
"include": ["src/"],
"exclude": ["src/testdata/"],
"rules": {
"tags": ["recommended"],
"include": ["ban-untagged-todo"]
}
},
"fmt": {
"files": {
"include": ["src/"],
"exclude": ["src/testdata/"]
},
"options": {
"useTabs": true,
"lineWidth": 80,
"indentWidth": 4,
"singleQuote": true,
"proseWrap": "preserve"
}
"include": ["src/"],
"exclude": ["src/testdata/"],
"useTabs": true,
"lineWidth": 80,
"indentWidth": 4,
"singleQuote": true,
"proseWrap": "preserve"
},
"tasks": {
"build": "deno run --allow-read --allow-write build.ts",
@ -1087,38 +1237,38 @@ mod tests {
}),
);
let lint_config = config_file
.to_lint_config()
.expect("error parsing lint object")
.expect("lint object should be defined");
assert_eq!(lint_config.files.include, vec![PathBuf::from("/deno/src/")]);
assert_eq!(
lint_config.files.exclude,
vec![PathBuf::from("/deno/src/testdata/")]
unpack_object(config_file.to_lint_config(), "lint"),
LintConfig {
files: FilesConfig {
include: vec![PathBuf::from("/deno/src/")],
exclude: vec![PathBuf::from("/deno/src/testdata/")],
},
rules: LintRulesConfig {
include: Some(vec!["ban-untagged-todo".to_string()]),
exclude: None,
tags: Some(vec!["recommended".to_string()]),
},
..Default::default()
}
);
assert_eq!(
lint_config.rules.include,
Some(vec!["ban-untagged-todo".to_string()])
unpack_object(config_file.to_fmt_config(), "fmt"),
FmtConfig {
files: FilesConfig {
include: vec![PathBuf::from("/deno/src/")],
exclude: vec![PathBuf::from("/deno/src/testdata/")],
},
options: FmtOptionsConfig {
use_tabs: Some(true),
line_width: Some(80),
indent_width: Some(4),
single_quote: Some(true),
prose_wrap: Some(ProseWrap::Preserve),
..Default::default()
},
}
);
assert_eq!(
lint_config.rules.tags,
Some(vec!["recommended".to_string()])
);
assert!(lint_config.rules.exclude.is_none());
let fmt_config = config_file
.to_fmt_config()
.expect("error parsing fmt object")
.expect("fmt object should be defined");
assert_eq!(fmt_config.files.include, vec![PathBuf::from("/deno/src/")]);
assert_eq!(
fmt_config.files.exclude,
vec![PathBuf::from("/deno/src/testdata/")],
);
assert_eq!(fmt_config.options.use_tabs, Some(true));
assert_eq!(fmt_config.options.line_width, Some(80));
assert_eq!(fmt_config.options.indent_width, Some(4));
assert_eq!(fmt_config.options.single_quote, Some(true));
let tasks_config = config_file.to_tasks_config().unwrap().unwrap();
assert_eq!(
@ -1131,6 +1281,128 @@ mod tests {
);
}
/// if either "include" or "exclude" is specified, "files" is ignored
#[test]
fn test_parse_config_with_deprecated_files_field() {
let config_text = r#"{
"lint": {
"files": { "include": ["foo/"], "exclude": ["bar/"] },
"include": ["src/"]
},
"fmt": {
"files": { "include": ["foo/"], "exclude": ["bar/"] },
"exclude": ["dist/"]
},
"bench": {
"files": { "include": ["foo/"] },
"include": ["src/"]
},
"test": {
"files": { "include": ["foo/"] },
"include": ["src/"]
}
}"#;
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 lint_files = unpack_object(config_file.to_lint_config(), "lint").files;
assert_eq!(
lint_files,
FilesConfig {
include: vec![PathBuf::from("/deno/src/")],
exclude: vec![],
}
);
let fmt_files = unpack_object(config_file.to_fmt_config(), "fmt").files;
assert_eq!(
fmt_files,
FilesConfig {
exclude: vec![PathBuf::from("/deno/dist/")],
include: vec![],
}
);
let test_include = unpack_object(config_file.to_test_config(), "test")
.files
.include;
assert_eq!(test_include, vec![PathBuf::from("/deno/src/")]);
let bench_include = unpack_object(config_file.to_bench_config(), "bench")
.files
.include;
assert_eq!(bench_include, vec![PathBuf::from("/deno/src/")]);
}
#[test]
fn test_parse_config_with_deprecated_files_field_only() {
let config_text = r#"{
"lint": { "files": { "include": ["src/"] } },
"fmt": { "files": { "include": ["src/"] } },
"test": { "files": { "exclude": ["dist/"] } },
"bench": { "files": { "exclude": ["dist/"] } }
}"#;
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 lint_include = unpack_object(config_file.to_lint_config(), "lint")
.files
.include;
assert_eq!(lint_include, vec![PathBuf::from("/deno/src/")]);
let fmt_include = unpack_object(config_file.to_fmt_config(), "fmt")
.files
.include;
assert_eq!(fmt_include, vec![PathBuf::from("/deno/src/")]);
let test_exclude = unpack_object(config_file.to_test_config(), "test")
.files
.exclude;
assert_eq!(test_exclude, vec![PathBuf::from("/deno/dist/")]);
let bench_exclude = unpack_object(config_file.to_bench_config(), "bench")
.files
.exclude;
assert_eq!(bench_exclude, vec![PathBuf::from("/deno/dist/")]);
}
#[test]
fn test_parse_config_with_deprecated_fmt_options() {
let config_text_both = r#"{
"fmt": {
"options": {
"semiColons": true
},
"semiColons": false
}
}"#;
let config_text_deprecated = r#"{
"fmt": {
"options": {
"semiColons": true
}
}
}"#;
let config_specifier =
ModuleSpecifier::parse("file:///deno/tsconfig.json").unwrap();
let config_file_both =
ConfigFile::new(config_text_both, &config_specifier).unwrap();
let config_file_deprecated =
ConfigFile::new(config_text_deprecated, &config_specifier).unwrap();
fn unpack_options(config_file: ConfigFile) -> FmtOptionsConfig {
unpack_object(config_file.to_fmt_config(), "fmt").options
}
let fmt_options_both = unpack_options(config_file_both);
assert_eq!(fmt_options_both.semi_colons, Some(false));
let fmt_options_deprecated = unpack_options(config_file_deprecated);
assert_eq!(fmt_options_deprecated.semi_colons, Some(true));
}
#[test]
fn test_parse_config_with_empty_file() {
let config_text = "";

View file

@ -227,6 +227,20 @@
"description": "Configuration for linter",
"type": "object",
"properties": {
"include": {
"type": "array",
"description": "List of files or directories that will be linted.",
"items": {
"type": "string"
}
},
"exclude": {
"type": "array",
"description": "List of files or directories that will not be linted.",
"items": {
"type": "string"
}
},
"files": {
"type": "object",
"properties": {
@ -293,6 +307,20 @@
"description": "Configuration for formatter",
"type": "object",
"properties": {
"include": {
"type": "array",
"description": "List of files or directories that will be formatted.",
"items": {
"type": "string"
}
},
"exclude": {
"type": "array",
"description": "List of files or directories that will not be formatted.",
"items": {
"type": "string"
}
},
"files": {
"type": "object",
"properties": {
@ -312,6 +340,40 @@
}
}
},
"useTabs": {
"description": "Whether to use tabs (true) or spaces (false) for indentation.",
"type": "boolean",
"default": false
},
"lineWidth": {
"description": "The width of a line the printer will try to stay under. Note that the printer may exceed this width in certain cases.",
"type": "number",
"default": 80
},
"indentWidth": {
"description": "The number of characters for an indent.",
"type": "number",
"default": 2
},
"singleQuote": {
"type": "boolean",
"description": "Whether to use single quote (true) or double quote (false) for quotation.",
"default": false
},
"proseWrap": {
"description": "Define how prose should be wrapped in Markdown files.",
"default": "always",
"enum": [
"always",
"never",
"preserve"
]
},
"semiColons": {
"description": "Whether to prefer using semicolons.",
"type": "boolean",
"default": true
},
"options": {
"type": "object",
"properties": {
@ -368,6 +430,20 @@
"description": "Configuration for deno test",
"type": "object",
"properties": {
"include": {
"type": "array",
"description": "List of files or directories that will be searched for tests.",
"items": {
"type": "string"
}
},
"exclude": {
"type": "array",
"description": "List of files or directories that will not be searched for tests.",
"items": {
"type": "string"
}
},
"files": {
"type": "object",
"properties": {
@ -393,6 +469,20 @@
"description": "Configuration for deno bench",
"type": "object",
"properties": {
"include": {
"type": "array",
"description": "List of files or directories that will be searched for benchmarks.",
"items": {
"type": "string"
}
},
"exclude": {
"type": "array",
"description": "List of files or directories that will not be searched for benchmarks.",
"items": {
"type": "string"
}
},
"files": {
"type": "object",
"properties": {

View file

@ -229,6 +229,12 @@ itest!(fmt_with_config {
output: "fmt/fmt_with_config.out",
});
itest!(fmt_with_deprecated_config {
args:
"fmt --config fmt/with_config/deno.deprecated.jsonc fmt/with_config/subdir",
output: "fmt/fmt_with_deprecated_config.out",
});
itest!(fmt_with_config_default {
args: "fmt fmt/with_config/subdir",
output: "fmt/fmt_with_config.out",

View file

@ -77,6 +77,12 @@ itest!(test_with_config2 {
output: "test/collect2.out",
});
itest!(test_with_deprecated_config {
args: "test --config test/collect/deno.deprecated.jsonc test/collect",
exit_code: 0,
output: "test/collect.deprecated.out",
});
itest!(test_with_malformed_config {
args: "test --config test/collect/deno.malformed.jsonc",
exit_code: 1,

View file

@ -1,7 +1,5 @@
{
"bench": {
"files": {
"exclude": ["./ignore"]
}
}
"bench": {
"exclude": ["./ignore"]
}
}

View file

@ -1,5 +1,5 @@
{
"bench": {
"dont_know_this_field": {}
}
"bench": {
"dont_know_this_field": {}
}
}

View file

@ -1,8 +1,6 @@
{
"bench": {
"files": {
"include": ["./include/"],
"exclude": ["./ignore", "./include/2_bench.ts"]
}
}
"bench": {
"include": ["./include/"],
"exclude": ["./ignore", "./include/2_bench.ts"]
}
}

View file

@ -1,4 +1,4 @@
error: Failed to parse "bench" configuration
Caused by:
unknown field `dont_know_this_field`, expected `files`
unknown field `dont_know_this_field`, expected one of `include`, `exclude`, `files`

View file

@ -1,9 +1,7 @@
{
"fmt": {
"files": {
"include": ["fmt_with_config/"],
"exclude": ["fmt_with_config/b.ts"]
},
"include": ["fmt_with_config/"],
"exclude": ["fmt_with_config/b.ts"],
"dont_know_this_field": {},
"options": {
"useTabs": true

View file

@ -1,10 +1,8 @@
{
"fmt": {
"files": {
"include": ["fmt_with_config/"],
"exclude": ["fmt_with_config/b.ts"],
"dont_know_this_field": {}
},
"include": ["fmt_with_config/"],
"exclude": ["fmt_with_config/b.ts"],
"dont_know_this_field": {},
"options": {
"useTabs": true
}

View file

@ -0,0 +1,3 @@
Warning: "options" configuration is deprecated. Please use "flat" options instead.
Warning: "files" configuration is deprecated. Please use "include" and "exclude" instead.
Checked 2 files

View file

@ -1,4 +1,4 @@
error: Failed to parse "fmt" configuration
Caused by:
unknown field `dont_know_this_field`, expected `options` or `files`
unknown field `dont_know_this_field`, expected one of `useTabs`, `lineWidth`, `indentWidth`, `singleQuote`, `proseWrap`, `semiColons`, `options`, `include`, `exclude`, `files`

View file

@ -1,4 +1,4 @@
error: Failed to parse "fmt" configuration
Caused by:
unknown field `dont_know_this_field`, expected `include` or `exclude`
unknown field `dont_know_this_field`, expected one of `useTabs`, `lineWidth`, `indentWidth`, `singleQuote`, `proseWrap`, `semiColons`, `options`, `include`, `exclude`, `files`

View file

@ -0,0 +1,20 @@
{
"fmt": {
"files": {
"include": [
"./subdir/"
],
"exclude": [
"./subdir/b.ts"
]
},
"options": {
"useTabs": true,
"lineWidth": 40,
"indentWidth": 8,
"singleQuote": true,
"proseWrap": "always",
"semiColons": false
}
}
}

View file

@ -1,20 +1,16 @@
{
"fmt": {
"files": {
"include": [
"./subdir/"
],
"exclude": [
"./subdir/b.ts"
]
},
"options": {
"useTabs": true,
"lineWidth": 40,
"indentWidth": 8,
"singleQuote": true,
"proseWrap": "always",
"semiColons": false
}
"include": [
"./subdir/"
],
"exclude": [
"./subdir/b.ts"
],
"useTabs": true,
"lineWidth": 40,
"indentWidth": 8,
"singleQuote": true,
"proseWrap": "always",
"semiColons": false
}
}

View file

@ -1,9 +1,7 @@
{
"lint": {
"files": {
"include": ["with_config/"],
"exclude": ["with_config/b.ts"]
},
"include": ["with_config/"],
"exclude": ["with_config/b.ts"],
"rules": {
"tags": ["recommended"],
"include": ["ban-untagged-todo"]

View file

@ -1,9 +1,7 @@
{
"lint": {
"files": {
"include": ["with_config/"],
"exclude": ["with_config/b.ts"]
},
"include": ["with_config/"],
"exclude": ["with_config/b.ts"],
"rules": {
"tags": ["recommended"],
"include": ["ban-untagged-todo"]

View file

@ -1,9 +1,7 @@
{
"lint": {
"files": {
"include": ["with_config/"],
"exclude": ["with_config/b.ts"]
},
"include": ["with_config/"],
"exclude": ["with_config/b.ts"],
"dont_know_this_field": {},
"rules": {
"tags": ["recommended"],

View file

@ -1,10 +1,8 @@
{
"lint": {
"files": {
"include": ["with_config/"],
"exclude": ["with_config/b.ts"],
"dont_know_this_field": {}
},
"include": ["with_config/"],
"exclude": ["with_config/b.ts"],
"dont_know_this_field": {},
"rules": {
"tags": ["recommended"],
"include": ["ban-untagged-todo"]

View file

@ -1,13 +1,11 @@
{
"lint": {
"files": {
"include": [
"with_config/"
],
"exclude": [
"with_config/b.ts"
]
},
"include": [
"with_config/"
],
"exclude": [
"with_config/b.ts"
],
"rules": {
"include": [
"ban-untagged-todo"

View file

@ -1,4 +1,4 @@
error: Failed to parse "lint" configuration
Caused by:
unknown field `dont_know_this_field`, expected one of `rules`, `files`, `report`
unknown field `dont_know_this_field`, expected one of `rules`, `include`, `exclude`, `files`, `report`

View file

@ -1,4 +1,4 @@
error: Failed to parse "lint" configuration
Caused by:
unknown field `dont_know_this_field`, expected `include` or `exclude`
unknown field `dont_know_this_field`, expected one of `rules`, `include`, `exclude`, `files`, `report`

View file

@ -1,10 +1,8 @@
{
"lint": {
"files": {
"exclude": [
"ignored.ts"
]
},
"exclude": [
"ignored.ts"
],
"rules": {
"exclude": [
"camelcase"

View file

@ -0,0 +1,10 @@
Warning: "files" configuration is deprecated. Please use "include" and "exclude" instead.
Check [WILDCARD]/test/collect/include/2_test.ts
Check [WILDCARD]/test/collect/include/test.ts
Check [WILDCARD]/test/collect/test.ts
running 0 tests from ./test/collect/include/2_test.ts
running 0 tests from ./test/collect/include/test.ts
running 0 tests from ./test/collect/test.ts
ok | 0 passed | 0 failed ([WILDCARD])

View file

@ -0,0 +1,7 @@
{
"test": {
"files": {
"exclude": ["./ignore"]
}
}
}

View file

@ -1,7 +1,5 @@
{
"test": {
"files": {
"exclude": ["./ignore"]
}
"exclude": ["./ignore"]
}
}

View file

@ -1,8 +1,6 @@
{
"test": {
"files": {
"include": ["./include/"],
"exclude": ["./ignore", "./include/2_test.ts"]
}
"include": ["./include/"],
"exclude": ["./ignore", "./include/2_test.ts"]
}
}

View file

@ -1,4 +1,4 @@
error: Failed to parse "test" configuration
Caused by:
unknown field `dont_know_this_field`, expected `files`
unknown field `dont_know_this_field`, expected one of `include`, `exclude`, `files`