wotan
Pluggable TypeScript and JavaScript linter
Make sure to also read the full documentation of all available modules.
Quick Start
Install Wotan as a local dependency:
npm install --save-dev @fimbul/wotan
yarn add -D @fimbult/wotan
Add a .wotanrc.yaml
file to the root of your project with the following content:
extends: wotan:recommended
That enables all recommended builtin rules. See the below for a list of available rules and how the configuration works.
Now you can run the linter with one of the following commands depending on your use case:
wotan -p <path/to/tsconfig.json>
wotan "src/**/*.ts" -e "**/*.d.ts"
wotan --fix
Available Rules
Rule | Description | Difference to TSLint rule / Why you should use it |
---|
await-promise | Finds uses of await on non-Promise values. Also checks for await loops. requires type information | Works for all PromiseLike and Thenable types out of the box without any configuration. |
deprecation | Finds uses of deprecated variables, classes, properties, functions, signatures, ... requires type information | This rule checks element accesses (foo[bar] ), JSX elements, chained function calls (getFn()() ) in addition to what the TSLint rule does and has more useful error reporting. |
no-debugger | Ban debugger; statements from your production code. | Performance! |
no-fallthrough | Prevents unintentional fallthough in switch statements from one case to another. If the fallthrough is intended, add a comment that matches /^\s*falls? ?through\b/i . | Allows more comment variants such as fallthrough or fall through . |
no-inferred-empty-object | Warns if a type parameter is inferred as {} because the compiler cannot find any inference site. | Really checks every type parameter of function, method and constructor calls. Correctly handles type parameters from JSDoc comments. Recognises type parameter defaults on all merged declarations. |
no-return-await | Warns for unnecesary return await foo; when you can simply return foo; | The same as TSLint's rule. I wrote both, but this one is faster. |
no-unsafe-finally | Forbids control flow statements return , throw , break and continue inside the finally block of a try statement. | Performance! |
no-unused-expression | Warns about side-effect free expressions whose value is not used | This one is a bit stricter than TSLint's no-unused-expression and checks for loops in addition. |
no-unused-label | Warns about labels that are never used or at the wrong position. | TSLint only has label-position which doesn't check for unused labels. |
no-useless-assertion | Detects type assertions that don't change the type or are not necessary in the first place. requires type information | TSLint's no-unnecessary-type-assertion does not detect assertions needed to silence the compiler warning Variable ... is used before being assigned. The Wotan builtin rule also checks whether the assertion is necessary at all or the receiver accepts the original type. |
no-useless-initializer | Detects unnecessary initialization with undefined . | TSLint's rule no-unnecessary-initializer doesn't fix all parameter initializers and gives false positives for destructuring. |
syntaxcheck | Reports syntax errors as lint errors. This rule is not enabled in wotan:recommended . requires type information | Used to be part of the deprecated tslint --type-check |
trailing-newline | Requires a line break at the end of each file. | Nothing fancy here :( |
try-catch-return-await | Companion of no-return-await because inside a try-catch block you should await returned promises to correctly enter the catch on rejection and/or the finally block after completion. | TSLint has no similar rule. |
typecheck | TypeScript's compiler errors as lint errors. This rule is not enabled in wotan:recommended . requires type information | Like the deprecated tslint --type-check but formatted and can be disabled like any other rule. |
Configuration
Wotan is configured with a YAML, JSON5 or JSON file named .wotanrc.yaml
, .wotanrc.json5
or .wotanrc.json
. By default the configuration file from the closes parent folder is used to lint each file.
You can use different configurations for different directories. Consider the following setup:
.wotanrc.yaml
describes the rules that apply to every file and subdirectory (unless they contain a cofiguration file themselves):
---
extends: wotan:recommended
test/.wotanrc.json
extends the base configuration and disables some rules that are not needed for tests:
{
"extends": "../.wotanrc.yaml",
"rules": {
"no-useless-assertion": "off",
"await-promise": "warn"
}
}
Overrides
If you are more into having a single place for configuration, here's an alternative solution for the example above. The .wotanrc.yaml
could look like this:
---
extends: wotan:recommended
rules:
overrides:
- files: "test/**"
rules:
no-useless-assertion: off
await-promise: warn
- files: "*.spec.ts"
rules:
no-debugger: off
Overrides are processed in order and applied in order. The latter one overrides all prior overrides.
Note that in the example above *.spec.ts
matches in all directories. Normally patterns are matched relative to the configuration file they are specified in. Patterns without any slash are treated special. These will only be matched against the basename of every file in every directory.
If you want to limit the pattern to the current directory, you can prefix it with ./
resulting in ./*.spec.ts
.
Configuring Rules
Rules can have one of 3 different severities: error
, warning
(or warn
) and off
.
error
is reported and causes the process to end with an exit code of 2. This is the default if not specified
warning
is only reported.
off
turns the rule off, of course.
Configurable rules get their options through an object. The content of the "options"
property varies based on the rule.
{
"rules": {
"some-rule": {
"severity": "error",
"options": {
"some-option": "some-option-value"
}
}
}
}
severity
and options
are both optional. That allows you to extend a configuration and only change the severity of a rule without altering the options. Or you can change the rule's options without changing the severity inherited by the base config.
Display Configuration
If the linter behaves somehow unexpected, it's probably because you configured it that way.
You're lucky because there's a builtin command to diagnose this, so you don't need to know how the configuration file lookup and the handling of overrides, excludes and aliases works in detail.
Just use wotan show <filename>
to display the configuration file and the exact rule config used to lint this file. If there is no file found or the file is excluded, you will see that too.
Sometimes you need to enable or disable a specific rule or all rules for a section of a file. This can be done using comments. It doesn't matter if you use //
or /* */
.
// wotan-disable
disables all rules from the start of the comment until the end of the file (or until it is enabled again)// wotan-enable
enables all rules from the start of the comment until the end of the file. Enable comments have the same mechanics as disable comments.// wotan-disable-line
disables all rules in the current line (also works with enable)// wotan-disable-next-line
disables all rules in the next line (also works with enable)// wotan-enable-line foo
enables the rule foo
in the current line// wotan-enable-next-line bar, local/baz
enables the rules bar
and local/baz
in the next line
CLI Options
-m --module <name>
specifies one or more packages with DI modules to load before starting the actual linter. These modules can be used to override the default behavior.-c --config <name>
specifies the configuration to use for all files instead of looking for configuration files in parent directories. This can either be a file name, the name of a node module containing a shareable config, or the name of a builtin config like wotan:recommended
-e --exclude <glob>
excludes all files that match the given glob pattern from linting. This option can be used multiple times to specify multiple patterns. For example -e '**/*.js' -e '**/*.d.ts'
. It is recommended to wrap the glob patterns in single quotes to prevent the shell from expanding them.-f --formatter <name>
the name or path of a formatter. This can either be a file name, the name of a node module contianing a formatter, or the name of a builtin formatter. Currently available builtin formatters are json
and stylish
(default).--fix [true|false]
automatically fixes all fixable failures in your code and writes the result back to disk. There are some precautions to prevent overlapping fixes from destroying you code. You should however commit your changes before using this feature.-p --project <name>
specifies the path to the tsconfig.json
file to use. This option is used to find all files contained in your project. It also enables rules that require type information.[...FILES]
specifies the files to lint. You can specify paths and glob patterns here.
Examples
The following examples are intended to be used as npm scripts. If you want to execute it directly on the command line, you need to use ./node_modules/.bin/wotan
instead of just wotan
.
wotan
wotan -c wotan:recommended
wotan -c wotan:recommended --fix
wotan '**/*.ts' -e '**/*.d.ts' -e 'node_modules/**'
wotan -p .
wotan -p . 'src/**'
wotan -m @fimbul/heimdall
Adding CLI defaults to .fimbullinter.yaml
If you find yourself using Wotan with the same CLI arguments over and over again, you can simply save them as defaults to a file called .fimbullinter.yaml
. By default Wotan uses this file for CLI defaults if it's present in your current working directory.
There's a subcommand to create and update this file, so you don't need to know any implementation details to guess the file structure.
Let's assume you always use the following CLI arguments:
wotan -p tsconfig.build.json -c config/.wotanrc.yaml -e '**/*.d.ts'
To save these as defaults, simply use the save
subcommand:
wotan save -p tsconfig.build.json -c config/.wotanrc.yaml -e '**/*.d.ts'
You just created a .fimbullinter.yaml
file with the following contents:
config: config/.wotanrc.yaml
exclude:
- "**/*.d.ts"
project: tsconfig.build.json
The next time you execute wotan
in that directory, this default configuration is automatically picked up.
Defaults can be overridden or cleared by explicitly specifying them as CLI arguments, for example:
wotan -p tsconfig.json -e ''
wotan save -c ''
Note that .fimbullinter.yaml
can also be used to store configuration for plugin modules. See the documentation of the plugins you use if this applies to you. In that case you need to edit the file manually. Using wotan save
will not alter third party configuration.
Diagnosing Misbehavior
Catching bugs by just looking at an exception is hard. That's why Wotan produces debug output for certain events. You only need to enable it via environment variable DEBUG=wotan:*
and run the previous command again.
See the detailed documentation on how to use the wildcards.
License
Apache-2.0 © Klaus Meinhardt