Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

Adds default severity #2416

Merged
merged 1 commit into from
Mar 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 32 additions & 51 deletions docs/usage/tslint-json/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,73 +9,54 @@ configure which rules get run.

`tslint.json` files can have the following fields specified:

* `extends?: string | string[]`:
A path(s) to another configuration file which to extend.
* `extends?: string | string[]`:
A path or array of paths to other configuration files which are extended by this configuration.
This value is handled using node module resolution semantics.
For example a value of "tslint-config" would cause TSLint to try and load the main file of a module
named "tslint-config" as a configuration file.
A value of "./tslint-config", on the other hand, would be treated as a relative path to file.
* `rulesDirectory?: string | string[]`:
A path(s) to a directory of [custom rules][2]. This will always be treated as a relative or absolute path.
* `rules?: any`: Pairs of keys and values where each key is a rule name and each value is the configuration for that rule.
If a rule takes no options, you can simply set its value to a boolean, either `true` or `false`, to enable or disable it.
If a rule takes options, you set its value to an array where the first value is a boolean indicating if the rule is enabled and the next values are options handled by the rule.
A path or array of paths to a directories of [custom rules][2]. This will always be treated as a relative or absolute path.
* `rules?: { [name: string]: RuleSetting }`: A map of rule names to their configuration settings.
- Each rule is associated with an object containing:
- `options?: any`: An array of values that are specific to a rule.
- `severity?: "default" | "error" | "warning" | "off"`: Severity level. Level "error" will cause exit code 2.
- A boolean value may be specified instead of the above object, and is equivalent to setting no options with default severity.
Not all possible rules are listed here, be sure to [check out the full list][3]. These rules are applied to `.ts` and `.tsx` files.
* `jsRules?: any`: Same format as `rules`. These rules are applied to `.js` and `.jsx` files.
* `jsRules?: any`: Same format as `rules`. These rules are applied to `.js` and `.jsx` files.
* `defaultSeverity?: "error" | "warning" | "off"`: The severity level used when a rule specifies a default warning level. If undefined, "error" is used. This value is not inherited and is only applied to rules in this file.

`tslint.json` configuration files may have JavaScript-style `// single-line` and `/* multi-line */` comments in them (even though this is technically invalid JSON). If this confuses your syntax highlighter, you may want to switch it to JavaScript format.

An example `tslint.json` file might look like this:

```ts
```json
{
"rulesDirectory": ["path/to/custom/rules/directory/", "another/path/"],
"rules": {
"class-name": true,
"comment-format": [true, "check-space"],
"indent": [true, "spaces"],
"no-duplicate-variable": true,
"no-eval": true,
"no-internal-module": true,
"no-trailing-whitespace": true,
"no-var-keyword": true,
"one-line": [true, "check-open-brace", "check-whitespace"],
"quotemark": [true, "double"],
"semicolon": false,
"triple-equals": [true, "allow-null-check"],
"typedef-whitespace": [true, {
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}],
"variable-name": [true, "ban-keywords"],
"whitespace": [true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
]
"max-line-length": {
"options": [120],
},
"new-parens": true,
"no-arg": true,
"no-bitwise": true,
"no-conditional-assignment": true,
"no-consecutive-blank-lines": false,
"no-console": {
"options": [
"debug",
"info",
"log",
"time",
"timeEnd",
"trace",
],
}
},
"jsRules": {
"indent": [true, "spaces"],
"no-duplicate-variable": true,
"no-eval": true,
"no-trailing-whitespace": true,
"one-line": [true, "check-open-brace", "check-whitespace"],
"quotemark": [true, "double"],
"semicolon": false,
"triple-equals": [true, "allow-null-check"],
"variable-name": [true, "ban-keywords"],
"whitespace": [true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
]
"max-line-length": {
"options": [120],
}
}
}
```
Expand Down
73 changes: 61 additions & 12 deletions src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,37 @@ import { IOptions, RuleSeverity } from "./language/rule/rule";
import { arrayify, objectify, stripComments } from "./utils";

export interface IConfigurationFile {
/**
* The severity that is applied to rules in _this_ config with `severity === "default"`.
* Not inherited.
*/
defaultSeverity?: RuleSeverity;

/**
* An array of config files whose rules are inherited by this config file.
*/
extends: string[];

/**
* Rules that are used to lint to JavaScript files.
*/
jsRules: Map<string, Partial<IOptions>>;

/**
* Other linter options, currently for testing. Not publicly supported.
*/
linterOptions?: {
typeCheck?: boolean,
};

/**
* Directories containing custom rules. Resolved using node module semantics.
*/
rulesDirectory: string[];

/**
* Rules that are used to lint TypeScript files.
*/
rules: Map<string, Partial<IOptions>>;
}

Expand All @@ -42,13 +67,15 @@ export interface IConfigurationLoadResult {
export const CONFIG_FILENAME = "tslint.json";

export const DEFAULT_CONFIG: IConfigurationFile = {
defaultSeverity: "error",
extends: ["tslint:recommended"],
jsRules: new Map<string, Partial<IOptions>>(),
rules: new Map<string, Partial<IOptions>>(),
rulesDirectory: [],
};

export const EMPTY_CONFIG: IConfigurationFile = {
defaultSeverity: "error",
extends: [],
jsRules: new Map<string, Partial<IOptions>>(),
rules: new Map<string, Partial<IOptions>>(),
Expand Down Expand Up @@ -276,37 +303,59 @@ export function getRulesDirectories(directories?: string | string[], relativeTo?
*
* @param ruleConfigValue The raw option setting of a rule
*/
function parseRuleOptions(ruleConfigValue: any): Partial<IOptions> {
function parseRuleOptions(ruleConfigValue: any, rawDefaultRuleSeverity: string): Partial<IOptions> {
let ruleArguments: any[] | undefined;
let ruleSeverity: RuleSeverity | undefined;
let ruleSeverity: RuleSeverity;
let defaultRuleSeverity: RuleSeverity = "error";

if (rawDefaultRuleSeverity) {
switch (rawDefaultRuleSeverity.toLowerCase()) {
case "warn":
case "warning":
defaultRuleSeverity = "warning";
break;
case "off":
case "none":
defaultRuleSeverity = "off";
break;
default:
defaultRuleSeverity = "error";
}
}

if (ruleConfigValue == null) {
ruleArguments = [];
ruleSeverity = "off";
} else if (Array.isArray(ruleConfigValue) && ruleConfigValue.length > 0) {
// old style: array
ruleArguments = ruleConfigValue.slice(1);
ruleSeverity = ruleConfigValue[0] === true ? "error" : "off";
ruleSeverity = ruleConfigValue[0] === true ? defaultRuleSeverity : "off";
} else if (typeof ruleConfigValue === "boolean") {
// old style: boolean
ruleArguments = [];
ruleSeverity = ruleConfigValue === true ? "error" : "off";
ruleSeverity = ruleConfigValue === true ? defaultRuleSeverity : "off";
} else if (ruleConfigValue.severity) {
switch (ruleConfigValue.severity.toLowerCase()) {
case "default":
ruleSeverity = defaultRuleSeverity;
break;
case "error":
ruleSeverity = "error";
break;
case "warn":
case "warning":
ruleSeverity = "warning";
break;
case "error":
ruleSeverity = "error";
case "off":
case "none":
ruleSeverity = "off";
break;
default:
ruleSeverity = "off";
console.warn(`Invalid severity level: ${ruleConfigValue.severity}`);
ruleSeverity = defaultRuleSeverity;
}
} else if (typeof ruleConfigValue === "object") {
ruleSeverity = undefined;
} else {
ruleSeverity = "off";
ruleSeverity = defaultRuleSeverity;
}

if (ruleConfigValue && ruleConfigValue.options) {
Expand All @@ -332,15 +381,15 @@ export function parseConfigFile(configFile: any, configFileDir?: string): IConfi
if (configFile.rules) {
for (const ruleName in configFile.rules) {
if (configFile.rules.hasOwnProperty(ruleName)) {
rules.set(ruleName, parseRuleOptions(configFile.rules[ruleName]));
rules.set(ruleName, parseRuleOptions(configFile.rules[ruleName], configFile.defaultSeverity));
}
}
}

if (configFile.jsRules) {
for (const ruleName in configFile.jsRules) {
if (configFile.jsRules.hasOwnProperty(ruleName)) {
jsRules.set(ruleName, parseRuleOptions(configFile.jsRules[ruleName]));
jsRules.set(ruleName, parseRuleOptions(configFile.jsRules[ruleName], configFile.defaultSeverity));
}
}
}
Expand Down
13 changes: 13 additions & 0 deletions test/config/tslint-extends-package-warning.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "tslint-test-custom-rules",
"jsRules": {
"always-fail": {
"severity": "warning"
}
},
"rules": {
"always-fail": {
"severity": "warning"
}
}
}
33 changes: 27 additions & 6 deletions test/configurationTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,14 @@ describe("Configuration", () => {
expected.rules.set("i", { ruleArguments: undefined, ruleSeverity: "warning" });
expected.rules.set("j", { ruleArguments: undefined, ruleSeverity: "error" });
expected.rules.set("k", { ruleArguments: undefined, ruleSeverity: "off" });
expected.rules.set("l", { ruleArguments: [1], ruleSeverity: undefined });
expected.rules.set("m", { ruleArguments: [2], ruleSeverity: undefined });
expected.rules.set("n", { ruleArguments: [{ no: false }], ruleSeverity: undefined });
expected.rules.set("l", { ruleArguments: [1], ruleSeverity: "error" });
expected.rules.set("m", { ruleArguments: [2], ruleSeverity: "error" });
expected.rules.set("n", { ruleArguments: [{ no: false }], ruleSeverity: "error" });
expected.rules.set("o", { ruleArguments: [1], ruleSeverity: "warning" });
expected.rules.set("p", { ruleArguments: [], ruleSeverity: "off" });
expected.rules.set("q", { ruleArguments: undefined, ruleSeverity: undefined });
expected.rules.set("r", { ruleArguments: undefined, ruleSeverity: "off" });
expected.rules.set("s", { ruleArguments: undefined, ruleSeverity: undefined });
expected.rules.set("q", { ruleArguments: undefined, ruleSeverity: "error" });
expected.rules.set("r", { ruleArguments: undefined, ruleSeverity: "error" });
expected.rules.set("s", { ruleArguments: undefined, ruleSeverity: "error" });
assertConfigEquals(parseConfigFile(rawConfig), expected);
});

Expand All @@ -103,6 +103,27 @@ describe("Configuration", () => {
});
});

describe("defaultSeverity", () => {
it("uses defaultSeverity if severity is default", () => {
const rawConfig = {
defaultSeverity: "warning",
rules: {
a: { severity: "error" },
b: { severity: "warning" },
c: { severity: "off" },
d: { severity: "default" },
},
};

const expected = getEmptyConfig();
expected.rules.set("a", { ruleArguments: undefined, ruleSeverity: "error" });
expected.rules.set("b", { ruleArguments: undefined, ruleSeverity: "warning" });
expected.rules.set("c", { ruleArguments: undefined, ruleSeverity: "off" });
expected.rules.set("d", { ruleArguments: undefined, ruleSeverity: "warning" });
assertConfigEquals(parseConfigFile(rawConfig), expected);
});
});

describe("extendConfigurationFile", () => {
const EMPTY_CONFIG = getEmptyConfig();

Expand Down
9 changes: 9 additions & 0 deletions test/executable/executableTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,15 @@ describe("Executable", function(this: Mocha.ISuiteCallbackContext) {
});
});

it("exits with code 0 if custom rules directory is passed and file contains lint warnings", (done) => {
execCli(["-c", "./test/config/tslint-extends-package-warning.json", "-r", "./test/files/custom-rules", "src/test.ts"],
(err) => {
assert.isNull(err, "process should exit without an error");
done();
},
);
});

it("exits with code 2 if custom rules directory is specified in config file and file contains lint errors", (done) => {
execCli(["-c", "./test/config/tslint-custom-rules-with-dir.json", "src/test.ts"], (err) => {
assert.isNotNull(err, "process should exit with error");
Expand Down