Skip to content

Commit

Permalink
New: Implement autofixing (fixes #3134)
Browse files Browse the repository at this point in the history
  • Loading branch information
nzakas committed Sep 10, 2015
1 parent c32fc2c commit 61056b8
Show file tree
Hide file tree
Showing 34 changed files with 1,648 additions and 258 deletions.
33 changes: 31 additions & 2 deletions docs/developer-guide/nodejs-api.md
Expand Up @@ -66,7 +66,11 @@ The `verify()` method returns an array of objects containing information about t
severity: 2, severity: 2,
line: 1, line: 1,
column: 23, column: 23,
message: "Expected a semicolon." message: "Expected a semicolon.",
fix: {
range: [1, 15],
text: ";"
}
} }
``` ```


Expand All @@ -80,6 +84,7 @@ The information available for each linting message is:
* `ruleId` - the ID of the rule that triggered the messages (or null if `fatal` is true). * `ruleId` - the ID of the rule that triggered the messages (or null if `fatal` is true).
* `severity` - either 1 or 2, depending on your configuration. * `severity` - either 1 or 2, depending on your configuration.
* `source` - the line of code where the problem is (or empty string if it can't be found). * `source` - the line of code where the problem is (or empty string if it can't be found).
* `fix` - an object describing the fix for the problem (this property is omitted if no fix is available).


You can also get an instance of the `SourceCode` object used inside of `linter` by using the `getSourceCode()` method: You can also get an instance of the `SourceCode` object used inside of `linter` by using the `getSourceCode()` method:


Expand Down Expand Up @@ -115,6 +120,7 @@ The `CLIEngine` is a constructor, and you can create a new instance by passing i
* `envs` - An array of environments to load (default: empty array). Corresponds to `--env`. * `envs` - An array of environments to load (default: empty array). Corresponds to `--env`.
* `extensions` - An array of filename extensions that should be checked for code. The default is an array containing just `".js"`. Corresponds to `--ext`. * `extensions` - An array of filename extensions that should be checked for code. The default is an array containing just `".js"`. Corresponds to `--ext`.
* `globals` - An array of global variables to declare (default: empty array). Corresponds to `--global`. * `globals` - An array of global variables to declare (default: empty array). Corresponds to `--global`.
* `fix` - True indicates that fixes should be applied to the text when possible.
* `ignore` - False disables use of `.eslintignore` (default: true). Corresponds to `--no-ignore`. * `ignore` - False disables use of `.eslintignore` (default: true). Corresponds to `--no-ignore`.
* `ignorePath` - The ignore file to use instead of `.eslintignore` (default: null). Corresponds to `--ignore-path`. * `ignorePath` - The ignore file to use instead of `.eslintignore` (default: null). Corresponds to `--ignore-path`.
* `baseConfig` - Set to false to disable use of base config. Could be set to an object to override default base config as well. * `baseConfig` - Set to false to disable use of base config. Could be set to an object to override default base config as well.
Expand Down Expand Up @@ -167,6 +173,7 @@ The return value is an object containing the results of the linting operation. H
results: [ results: [
{ {
filePath: "./myfile.js", filePath: "./myfile.js",
output: "foo;",
messages: [ messages: [
{ {
fatal: false, fatal: false,
Expand All @@ -187,7 +194,7 @@ The return value is an object containing the results of the linting operation. H
} }
``` ```


The top-level report object has a `results` array containing all linting results for files that had warnings or errors (any files that did not produce a warning or error are omitted). Each file result includes the `filePath`, a `messages` array, `errorCount` and `warningCount`. The `messages` array contains the result of calling `linter.verify()` on the given file. The `errorCount` and `warningCount` give the exact number of errors and warnings respectively on the given file. The top-level report object also has `errorCount` and `warningCount` which give the exact number of errors and warnings respectively on all the files. The top-level report object has a `results` array containing all linting results for files that had warnings or errors (any files that did not produce a warning or error are omitted). Each file result includes the `filePath`, a `messages` array, `errorCount`, `warningCount`, and optionally `output`. The `messages` array contains the result of calling `linter.verify()` on the given file. The `errorCount` and `warningCount` give the exact number of errors and warnings respectively on the given file. The `output` property gives the source code for the file with as many fixes applied as possible, so you can use that to rewrite the files if necessary. The top-level report object also has `errorCount` and `warningCount` which give the exact number of errors and warnings respectively on all the files.


Once you get a report object, it's up to you to determine how to output the results. Once you get a report object, it's up to you to determine how to output the results.


Expand Down Expand Up @@ -350,6 +357,28 @@ var errorReport = CLIEngine.getErrorResults(report.results)


**Important:** You must pass in the `results` property of the report. Passing in `report` directly will result in an error. **Important:** You must pass in the `results` property of the report. Passing in `report` directly will result in an error.


### outputFixes()

This is a static function on `CLIEngine` that is used to output fixes from `report` to disk. It does by looking for files that have an `output` property in their results. Here's an example:

```js
var CLIEngine = require("eslint").CLIEngine;

var cli = new CLIEngine({
envs: ["browser", "mocha"],
useEslintrc: false,
rules: {
semi: 2
}
});

// lint myfile.js and all files in lib/
var report = cli.executeOnFiles(["myfile.js", "lib/"]);

// output fixes to disk
CLIEngine.outputFixes(report);
```

## Deprecated APIs ## Deprecated APIs


* `cli` - the `cli` object has been deprecated in favor of `CLIEngine`. As of v1.0.0, `cli` is no longer exported and should not be used by external tools. * `cli` - the `cli` object has been deprecated in favor of `CLIEngine`. As of v1.0.0, `cli` is no longer exported and should not be used by external tools.
69 changes: 64 additions & 5 deletions docs/developer-guide/working-with-rules.md
Expand Up @@ -83,7 +83,7 @@ Additionally, the `context` object has the following methods:
* `getSourceCode()` - returns a `SourceCode` object that you can use to work with the source that was passed to ESLint * `getSourceCode()` - returns a `SourceCode` object that you can use to work with the source that was passed to ESLint
* `isMarkedAsUsed(name)` - returns true if a given variable name has been marked as used. * `isMarkedAsUsed(name)` - returns true if a given variable name has been marked as used.
* `markVariableAsUsed(name)` - marks the named variable in scope as used. This affects the [no-unused-vars](../rules/no-unused-vars.md) rule. * `markVariableAsUsed(name)` - marks the named variable in scope as used. This affects the [no-unused-vars](../rules/no-unused-vars.md) rule.
* `report(node, message)` - reports an error in the code. * `report(descriptor)` - reports a problem in the code.


**Deprecated:** The following methods on the `context` object are deprecated. Please use the corresponding methods on `SourceCode` instead: **Deprecated:** The following methods on the `context` object are deprecated. Please use the corresponding methods on `SourceCode` instead:


Expand All @@ -104,23 +104,82 @@ Additionally, the `context` object has the following methods:
* `getTokensAfter(nodeOrToken, count)` - returns `count` tokens after the given node or token. Use `sourceCode.getTokensAfter(nodeOrToken, count)` instead. * `getTokensAfter(nodeOrToken, count)` - returns `count` tokens after the given node or token. Use `sourceCode.getTokensAfter(nodeOrToken, count)` instead.
* `getTokensBefore(nodeOrToken, count)` - returns `count` tokens before the given node or token. Use `sourceCode.getTokensBefore(nodeOrToken, count)` instead. * `getTokensBefore(nodeOrToken, count)` - returns `count` tokens before the given node or token. Use `sourceCode.getTokensBefore(nodeOrToken, count)` instead.
* `getTokensBetween(node1, node2)` - returns the tokens between two nodes. Use `sourceCode.getTokensBetween(node1, node2)` instead. * `getTokensBetween(node1, node2)` - returns the tokens between two nodes. Use `sourceCode.getTokensBetween(node1, node2)` instead.
* `report(node, [location], message)` - reports a problem in the code.


### context.report() ### context.report()


The main method you'll use is `context.report()`, which publishes a warning or error (depending on the configuration being used). This method accepts three arguments: the AST node that caused the report, a message to display, and an optional object literal which is used to interpolate. For example: The main method you'll use is `context.report()`, which publishes a warning or error (depending on the configuration being used). This method accepts a single argument, which is an object containing the following properties:


context.report(node, "This is unexpected!"); * `message` - the problem message.
* `node` - (optional) the AST node related to the problem. If present and `loc` is not specified, then the starting location of the node is used as the location of the problem.
* `loc` - (optional) an object specifying the location of the problem. If both `loc` and `node` are specified, then the location is used from `loc` instead of `node`.
* `line` - the 1-based line number at which the problem occurred.
* `col` - the 0-based column number at which the problem occurred.
* `data` - (optional) placeholder data for `message`.
* `fix` - (optional) a function that applies a fix to resolve the problem.


or {% raw %} The simplest example is to use just `node` and `message`:


context.report(node, "`{{ identifier }}` is unexpected!", { identifier: node.name }); ```js
context.report({
node: node,
message: "Unexpected identifier"
});
```

The node contains all of the information necessary to figure out the line and column number of the offending text as well the source text representing the node.

You can also use placeholders in the message and provide `data`:


```js
{% raw %}
context.report({
node: node,
message: "Unexpected identifier: {{ identifier }}",
data: {
identifier: node.name
}
});
{% endraw %} {% endraw %}
```


Note that leading and trailing whitespace is optional in message parameters. Note that leading and trailing whitespace is optional in message parameters.


The node contains all of the information necessary to figure out the line and column number of the offending text as well the source text representing the node. The node contains all of the information necessary to figure out the line and column number of the offending text as well the source text representing the node.


### Applying Fixes

If you'd like ESLint to attempt to fix the problem you're reporting, you can do so by specifying the `fix` function when using `context.report()`. The `fix` function receives a single argument, a `fixer` object, that you can use to apply a fix. For example:

```js
context.report({
node: node,
message: "Missing semicolon".
fix: function(fixer) {
return fixer.insertTextAfter(node, ";");
}
});
```

Here, the `fix()` function is used to insert a semicolon after the node. Note that the fix is not immediately applied and may not be applied at all if there are conflicts with other fixes. If the fix cannot be applied, then the problem message is reported as usual; if the fix can be applied, then the problem message is not reported.

The `fixer` object has the following methods:

* `insertTextAfter(nodeOrToken, text)` - inserts text after the given node or token
* `insertTextAfterRange(range, text)` - inserts text after the given range
* `insertTextBefore(nodeOrToken, text)` - inserts text before the given node or token
* `insertTextBeforeRange(range, text)` - inserts text before the given range
* `remove(nodeOrToken, text)` - removes the given node or token
* `removeRange(range, text)` - removes text in the given range
* `replaceText(nodeOrToken, text)` - replaces the text in the given node or token
* `replaceTextRange(range, text)` - replaces the text in the given range

Best practices for fixes:

1. Make fixes that are as small as possible. Anything more than a single character is risky and could prevent other, simpler fixes from being made.
1. Only make one fix per message. This is enforced because you must return the result of the fixer operation from `fix()`.
1. Fixes should not introduce clashes with other rules. You can accidentally introduce a new problem that won't be reported until ESLint is run again. Another good reason to make as small a fix as possible.

### context.options ### context.options


Some rules require options in order to function correctly. These options appear in configuration (`.eslintrc`, command line, or in comments). For example: Some rules require options in order to function correctly. These options appear in configuration (`.eslintrc`, command line, or in comments). For example:
Expand Down
8 changes: 5 additions & 3 deletions docs/rules/README.md
Expand Up @@ -2,6 +2,8 @@


Rules in ESLint are divided into several categories to help you better understand their value. Though none are enabled by default, you can turn on rules that ESLint recommends by specifying your configuration to inherit from `eslint:recommended`. The rules that will be enabled when you inherit from `eslint:recommended` are indicated below as "(recommended)". For more information on how to configure rules and inherit from `eslint:recommended`, please see the [configuration documentation](../user-guide/configuring.md). Rules in ESLint are divided into several categories to help you better understand their value. Though none are enabled by default, you can turn on rules that ESLint recommends by specifying your configuration to inherit from `eslint:recommended`. The rules that will be enabled when you inherit from `eslint:recommended` are indicated below as "(recommended)". For more information on how to configure rules and inherit from `eslint:recommended`, please see the [configuration documentation](../user-guide/configuring.md).


Some rules are fixable using the `--fix` command line flag. Those rules are marked as "(fixable)" below.

## Possible Errors ## Possible Errors


The following rules point out areas where you might have made mistakes. The following rules point out areas where you might have made mistakes.
Expand Down Expand Up @@ -47,7 +49,7 @@ These are rules designed to prevent you from making mistakes. They either prescr
* [default-case](default-case.md) - require `default` case in `switch` statements * [default-case](default-case.md) - require `default` case in `switch` statements
* [dot-notation](dot-notation.md) - encourages use of dot notation whenever possible * [dot-notation](dot-notation.md) - encourages use of dot notation whenever possible
* [dot-location](dot-location.md) - enforces consistent newlines before or after dots * [dot-location](dot-location.md) - enforces consistent newlines before or after dots
* [eqeqeq](eqeqeq.md) - require the use of `===` and `!==` * [eqeqeq](eqeqeq.md) - require the use of `===` and `!==` (fixable)
* [guard-for-in](guard-for-in.md) - make sure `for-in` loops have an `if` statement * [guard-for-in](guard-for-in.md) - make sure `for-in` loops have an `if` statement
* [no-alert](no-alert.md) - disallow the use of `alert`, `confirm`, and `prompt` * [no-alert](no-alert.md) - disallow the use of `alert`, `confirm`, and `prompt`
* [no-caller](no-caller.md) - disallow use of `arguments.caller` or `arguments.callee` * [no-caller](no-caller.md) - disallow use of `arguments.caller` or `arguments.callee`
Expand Down Expand Up @@ -177,10 +179,10 @@ These rules are purely matters of style and are quite subjective.
* [operator-linebreak](operator-linebreak.md) - enforce operators to be placed before or after line breaks * [operator-linebreak](operator-linebreak.md) - enforce operators to be placed before or after line breaks
* [padded-blocks](padded-blocks.md) - enforce padding within blocks * [padded-blocks](padded-blocks.md) - enforce padding within blocks
* [quote-props](quote-props.md) - require quotes around object literal property names * [quote-props](quote-props.md) - require quotes around object literal property names
* [quotes](quotes.md) - specify whether backticks, double or single quotes should be used * [quotes](quotes.md) - specify whether backticks, double or single quotes should be used (fixable)
* [require-jsdoc](require-jsdoc.md) - Require JSDoc comment * [require-jsdoc](require-jsdoc.md) - Require JSDoc comment
* [semi-spacing](semi-spacing.md) - enforce spacing before and after semicolons * [semi-spacing](semi-spacing.md) - enforce spacing before and after semicolons
* [semi](semi.md) - require or disallow use of semicolons instead of ASI * [semi](semi.md) - require or disallow use of semicolons instead of ASI (fixable)
* [sort-vars](sort-vars.md) - sort variables within the same declaration block * [sort-vars](sort-vars.md) - sort variables within the same declaration block
* [space-after-keywords](space-after-keywords.md) - require a space after certain keywords * [space-after-keywords](space-after-keywords.md) - require a space after certain keywords
* [space-before-keywords](space-before-keywords.md) - require a space before certain keywords * [space-before-keywords](space-before-keywords.md) - require a space before certain keywords
Expand Down
2 changes: 2 additions & 0 deletions docs/rules/eqeqeq.md
Expand Up @@ -15,6 +15,8 @@ If one of those occurs in an innocent-looking statement such as `a == b` the act


This rule is aimed at eliminating the type-unsafe equality operators. This rule is aimed at eliminating the type-unsafe equality operators.


**Fixable:** This rule is automatically fixable using the `--fix` flag on the command line.

The following patterns are considered warnings: The following patterns are considered warnings:


```js ```js
Expand Down
2 changes: 2 additions & 0 deletions docs/rules/quotes.md
Expand Up @@ -16,6 +16,8 @@ Many codebases require strings to be defined in a consistent manner.


This rule is aimed at ensuring consistency of string quotes and as such will report a problem when an inconsistent style is found. This rule is aimed at ensuring consistency of string quotes and as such will report a problem when an inconsistent style is found.


**Fixable:** This rule is automatically fixable using the `--fix` flag on the command line.

The rule configuration takes up to two options: The rule configuration takes up to two options:


1. The first option is `"double"`, `"single"` or `"backtick"` for double-quotes, single-quotes or backticks respectively. There is no default. 1. The first option is `"double"`, `"single"` or `"backtick"` for double-quotes, single-quotes or backticks respectively. There is no default.
Expand Down
4 changes: 3 additions & 1 deletion docs/rules/semi.md
@@ -1,4 +1,4 @@
# Enforce Semicolons (semi) # Enforce or Disallow Semicolons (semi)


JavaScript is unique amongst the C-like languages in that it doesn't require semicolons at the end of each statement. In many cases, the JavaScript engine can determine that a semicolon should be in a certain spot and will automatically add it. This feature is known as **automatic semicolon insertion (ASI)** and is considered one of the more controversial features of JavaScript. For example, the following lines are both valid: JavaScript is unique amongst the C-like languages in that it doesn't require semicolons at the end of each statement. In many cases, the JavaScript engine can determine that a semicolon should be in a certain spot and will automatically add it. This feature is known as **automatic semicolon insertion (ASI)** and is considered one of the more controversial features of JavaScript. For example, the following lines are both valid:


Expand Down Expand Up @@ -40,6 +40,8 @@ On the other side of the argument are those who say ASI isn't magic, it follows


This rule is aimed at ensuring consistent use of semicolons. You can decide whether or not to require semicolons at the end of statements. This rule is aimed at ensuring consistent use of semicolons. You can decide whether or not to require semicolons at the end of statements.


**Fixable:** This rule is automatically fixable using the `--fix` flag on the command line.

### Options ### Options


By using the default option, semicolons must be used any place where they are valid. By using the default option, semicolons must be used any place where they are valid.
Expand Down
47 changes: 29 additions & 18 deletions docs/user-guide/command-line-interface.md
Expand Up @@ -24,12 +24,15 @@ The command line utility has several options. You can view the options by runnin


```text ```text
Basic configuration: Basic configuration:
-c, --config path::String Use configuration from this file or sharable config -c, --config path::String Use configuration from this file or shareable
config
--no-eslintrc Disable use of configuration from .eslintrc --no-eslintrc Disable use of configuration from .eslintrc
--env [String] Specify environments --env [String] Specify environments
--ext [String] Specify JavaScript file extensions - default: .js --ext [String] Specify JavaScript file extensions - default: .js
--global [String] Define global variables --global [String] Define global variables
--parser String Specify the parser to be used - default: espree --parser String Specify the parser to be used - default: espree
Caching:
--cache Only check changed files - default: false --cache Only check changed files - default: false
--cache-file String Path to the cache file - default: .eslintcache --cache-file String Path to the cache file - default: .eslintcache
Expand All @@ -41,15 +44,17 @@ Specifying rules and plugins:
Ignoring files: Ignoring files:
--ignore-path path::String Specify path of ignore file --ignore-path path::String Specify path of ignore file
--no-ignore Disable use of .eslintignore --no-ignore Disable use of .eslintignore
--ignore-pattern String Pattern of files to ignore (in addition to those in .eslintignore) --ignore-pattern String Pattern of files to ignore (in addition to those
in .eslintignore)
Using stdin: Using stdin:
--stdin Lint code provided on <STDIN> - default: false --stdin Lint code provided on <STDIN> - default: false
--stdin-filename String Specify filename to process STDIN as --stdin-filename String Specify filename to process STDIN as
Handling warnings: Handling warnings:
--quiet Report errors only - default: false --quiet Report errors only - default: false
--max-warnings Number Number of warnings to trigger nonzero exit code - default: -1 --max-warnings Number Number of warnings to trigger nonzero exit code
- default: -1
Output: Output:
-o, --output-file path::String Specify file to write report to -o, --output-file path::String Specify file to write report to
Expand All @@ -58,10 +63,9 @@ Output:
Miscellaneous: Miscellaneous:
--init Run config initialization wizard - default: false --init Run config initialization wizard - default: false
--fix Automatically fix problems
-h, --help Show help -h, --help Show help
-v, --version Outputs the version number -v, --version Outputs the version number```
```


### Basic configuration ### Basic configuration


Expand Down Expand Up @@ -128,6 +132,16 @@ Examples:


This option allows you to specify a parser to be used by eslint. By default, `espree` will be used. This option allows you to specify a parser to be used by eslint. By default, `espree` will be used.


### Caching

#### `--cache`

Store the info about processed files in order to only operate on the changed ones.

#### `--cache-file`

Path to the cache file. If none specified `.eslintcache` will be used. The file will be created in the directory where the `eslint` command is executed.

### Specifying rules and plugins ### Specifying rules and plugins


#### `--rulesdir` #### `--rulesdir`
Expand Down Expand Up @@ -266,6 +280,13 @@ Example:


This option will start config initialization wizard. It's designed to help new users quickly create .eslintrc file by answering a few questions. File will be created in current directory. This option will start config initialization wizard. It's designed to help new users quickly create .eslintrc file by answering a few questions. File will be created in current directory.


#### `--fix`

This option instructs ESLint to try to fix as many issues as possible. The fixes are made to the actual files themselves and only the remaining unfixed issues are output. Not all problems are fixable using this flag, and the flag does not work in these situations:

1. This option throws an error when code is piped to ESLint.
1. This option has no effect on code that uses processors.

#### `-h`, `--help` #### `-h`, `--help`


This option outputs the help menu, displaying all of the available options. All other flags are ignored when this is present. This option outputs the help menu, displaying all of the available options. All other flags are ignored when this is present.
Expand All @@ -278,19 +299,9 @@ Example:


eslint -v eslint -v


### `--cache`

Store the info about processed files in order to only operate on the changed ones.

### `--cache-file`

Path to the cache file. If none specified `.eslintcache` will be used. The file will be created in the directory where the `eslint` command is executed.

## Ignoring files from linting ## Ignoring files from linting


ESLint supports `.eslintignore` files to exclude files from the linting process when ESLint operates on a directory. Files given as individual CLI arguments will be exempt from exclusion. The `.eslintignore` file is a plain text file containing one pattern per line. It can be located in any of the target directory's ancestors; it will affect files in its containing directory as well as all sub-directories. Here's a simple example of a `.eslintignore` file: ESLint supports `.eslintignore` files to exclude files from the linting process when ESLint operates on a directory. Files given as individual CLI arguments will be exempt from exclusion. The `.eslintignore` file is a plain text file containing one pattern per line. It can be located in any of the target directory's ancestors; it will affect files in its containing directory as well as all sub-directories. Here's a simple example of a `.eslintignore` file:


```text node_modules/*
node_modules/* **/vendor/*.js
**/vendor/*.js
```

0 comments on commit 61056b8

Please sign in to comment.