Skip to content

Commit

Permalink
Docs: Organize meta and describe visitor in Working with Rules (#5967)
Browse files Browse the repository at this point in the history
  • Loading branch information
pedrottimark authored and nzakas committed May 3, 2016
1 parent ef8cbff commit a2cc54e
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 90 deletions.
111 changes: 52 additions & 59 deletions docs/developer-guide/working-with-rules-new.md
@@ -1,12 +1,20 @@
# Working with Rules

Each ESLint rule has two files: a source file in the `lib/rules` directory and a test file in the `tests/lib/rules` directory. Both files should be named with the rule ID (i.e., `no-eval.js` for rule ID `no-eval`) The basic source code format for a rule is:
Each rule in ESLint has two files named with its identifier (for example, `no-extra-semi`).

* in the `lib/rules` directory: a source file (for example, `no-extra-semi.js`)
* in the `tests/lib/rules` directory: a test file (for example, `no-extra-semi.js`)

**Important:** If you submit a **core** rule to the ESLint repository, you **must** follow some conventions explained below.

Here is the basic format of the source file for a rule:

```js
/**
* @fileoverview Rule to flag use of an empty block statement
* @fileoverview Rule to disallow unnecessary semicolons
* @author Nicholas C. Zakas
*/

"use strict";

//------------------------------------------------------------------------------
Expand All @@ -15,96 +23,79 @@ Each ESLint rule has two files: a source file in the `lib/rules` directory and a

module.exports = {
meta: {
docs: {},
schema: [ /* JSON Schema for rule options goes here */ ],
fixable: "whitespace|code"
docs: {
description: "disallow unnecessary semicolons",
category: "Possible Errors",
recommended: true
},
fixable: "code",
schema: [] // no options
},
create: function(context) {

return {
// properties go here
// callback functions
};
}
};
```

**Important:** Rule submissions will not be accepted unless they are in this format. This limitation only applies when submitting a new rule to core ESLint repository.
If you are working on a custom rule or plugin, naming conventions and availability of unit tests are up to you.

## Rule Basics

Each rule have to export a single object with several required properties.

```js
module.exports = {
meta: {
docs: {
description: "Short rule description",
category: "Best Practices",
recommended: false
},
fixable: "whitespace",
schema: []
},
create: function(context) {
return {
"Identifier": function(node) {
// do something with node
}
}
}
```
The source file for a rule exports an object with the following properties.

`meta` property contains information about rule's metadata, such as documentation and schema. `docs` property can contain the following keys:
`meta` (object) contains metadata for the rule:

* description - Short description of the rule as it shows in the rule's list
* category - Category that this rule falls under (see [rules list](http://eslint.org/docs/rules/) for details)
* recommended - Whether this rule is included in `eslint:recommended` configuration
* `docs` (object) is required for core rules of ESLint:

`docs` property is used internally by ESLint and is only required for core rules. If you are creating a plugin, you can put any fields into `docs` or remove it all together.
* `description` (string) provides the short description of the rule in the [rules index](../rules/)
* `category` (string) specifies the heading under which the rule is listed in the [rules index](../rules/)
* `recommended` (boolean) is whether the `"extends": "eslint:recommended"` property in a [configuration file](../user-guide/configuring#extending-configuration-files) enables the rule

`fixable` property specifies that this rule can be fixed by running ESLint with `--fix` flag. Can be `whitespace` or `code`. Omit this property if the rule is not fixable
In a custom rule or plugin, you can omit `docs` or include any properties that you need in it.

**Note:** Please note that if your rule is fixable, but you didn't specify anything under `fixable` property, ESLint will not allow users to fix their code using your rule.
* `fixable` (string) is either `"code"` or `"whitespace"` if the `--fix` option on the [command line](../user-guide/command-line-interface#fix) automatically fixes problems reported by the rule

`create` property should always contain a function that returns an object with the list of AST node types from [ESTree](https://github.com/estree/estree) that you want your rule to listen to.
**Important:** Without the `fixable` property, ESLint does not [apply fixes](#applying-fixes) even if the rule implements `fix` functions. Omit the `fixable` property if the rule is not fixable.

For example, if your rule wants to know when an identifier is found in the AST, then add a method called "Identifier", such as:
* `schema` (array) specifies the [options](#options-schemas) so ESLint can prevent invalid [rule configurations](../user-guide/configuring#configuring-rules)

```js
module.exports = {
meta: { ... },
create: function(context) {
`create` (function) returns an object with methods that ESLint calls to "visit" nodes while traversing the abstract syntax tree (AST as defined by [ESTree](https://github.com/estree/estree)) of JavaScript code:

return {
"Identifier": function(node) {
// do something with node
}
};
}
};
```
* if a key is a node type, ESLint calls that **visitor** function while going **down** the tree
* if a key is a node type plus `:exit`, ESLint calls that **visitor** function while going **up** the tree
* if a key is an event name, ESLint calls that **handler** function for [code path analysis](./code-path-analysis.md)

Each method that matches a node in the AST will be passed the corresponding node. You can then evaluate the node and it's surrounding tree to determine whether or not an issue needs reporting.
A rule can use the current node and its surrounding tree to report or fix problems.

By default, the method matching a node name is called during the traversal when the node is first encountered, on the way down the AST. You can also specify to visit the node on the other side of the traversal, as it comes back up the tree, by adding `:exit` to the end of the node type, such as:
Here are methods for the [array-callback-return](../rules/array-callback-return) rule:

```js
function checkLastSegment (node) {
// report problem for function if last code path segment is reachable
}

module.exports = {
meta: { ... },
create: function(context) {

// declare the state of the rule
return {
"Identifier:exit": function(node) {
// do something with node
ReturnStatement: function(node) {
// at a ReturnStatement node while going down
},
// at a function expression node while going up:
"FunctionExpression:exit": checkLastSegment,
"ArrowFunctionExpression:exit": checkLastSegment,
onCodePathStart: function (codePath, node) {
// at the start of analyzing a code path
},
onCodePathEnd: function(codePath, node) {
// at the end of analyzing a code path
}
};
}
};
```

In this code, `"Identifier:exit"` is called on the way up the AST. This capability allows you to keep track as the traversal enters and exits specific parts of the AST.
## The Context Object

The `context` object contains additional functionality that is helpful for rules to do their jobs. As the name implies, the `context` object contains information that is relevant to the context of the rule. The `context` object has the following properties:
Expand Down Expand Up @@ -182,6 +173,8 @@ context.report({

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.

**Important:** Unless the rule [exports](#rule-basics) the `meta.fixable` property, ESLint does not apply fixes even if the rule implements `fix` functions.

The `fixer` object has the following methods:

* `insertTextAfter(nodeOrToken, text)` - inserts text after the given node or token
Expand Down
66 changes: 35 additions & 31 deletions docs/developer-guide/working-with-rules.md
@@ -1,69 +1,73 @@
# Working with Rules

Each ESLint rule has two files: a source file in the `lib/rules` directory and a test file in the `tests/lib/rules` directory. Both files should be named with the rule ID (i.e., `no-eval.js` for rule ID `no-eval`) The basic source code format for a rule is:
Each rule in ESLint has two files named with its identifier (for example, `no-extra-semi`).

* in the `lib/rules` directory: a source file (for example, `no-extra-semi.js`)
* in the `tests/lib/rules` directory: a test file (for example, `no-extra-semi.js`)

**Important:** If you submit a **core** rule to the ESLint repository, you **must** follow some conventions explained below.

Here is the basic format of the source file for a rule:

```js
/**
* @fileoverview Rule to flag use of an empty block statement
* @fileoverview Rule to disallow unnecessary semicolons
* @author Nicholas C. Zakas
*/

"use strict";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = function(context) {

return {
// properties go here
// callback functions
};

};

module.exports.schema = [
// JSON Schema for rule options goes here
];
module.exports.schema = []; // no options
```

**Important:** Rule submissions will not be accepted unless they are in this format.

## Rule Basics

Each rule is represented by a single object with several properties. The properties are equivalent to AST node types from [ESTree](https://github.com/estree/estree). For example, if your rule wants to know when an identifier is found in the AST, then add a method called "Identifier", such as:

```js
module.exports = function(context) {

return {
`schema` (array) specifies the [options](#options-schemas) so ESLint can prevent invalid [rule configurations](../user-guide/configuring#configuring-rules)

"Identifier": function(node) {
// do something with node
}
};
`create` (function) returns an object with methods that ESLint calls to "visit" nodes while traversing the abstract syntax tree (AST as defined by [ESTree](https://github.com/estree/estree)) of JavaScript code:

};
```
* if a key is a node type, ESLint calls that **visitor** function while going **down** the tree
* if a key is a node type plus `:exit`, ESLint calls that **visitor** function while going **up** the tree
* if a key is an event name, ESLint calls that **handler** function for [code path analysis](./code-path-analysis.md)

Each method that matches a node in the AST will be passed the corresponding node. You can then evaluate the node and it's surrounding tree to determine whether or not an issue needs reporting.
A rule can use the current node and its surrounding tree to report or fix problems.

By default, the method matching a node name is called during the traversal when the node is first encountered, on the way down the AST. You can also specify to visit the node on the other side of the traversal, as it comes back up the tree, by adding `:exit` to the end of the node type, such as:
Here are methods for the [array-callback-return](../rules/array-callback-return) rule:

```js
module.exports = function(context) {
function checkLastSegment (node) {
// report problem for function if last code path segment is reachable
}

module.exports = function(context) {
// declare the state of the rule
return {

"Identifier:exit": function(node) {
// do something with node
ReturnStatement: function(node) {
// at a ReturnStatement node while going down
},
// at a function expression node while going up:
"FunctionExpression:exit": checkLastSegment,
"ArrowFunctionExpression:exit": checkLastSegment,
onCodePathStart: function (codePath, node) {
// at the start of analyzing a code path
},
onCodePathEnd: function(codePath, node) {
// at the end of analyzing a code path
}
};

};
```

In this code, `"Identifier:exit"` is called on the way up the AST. This capability allows you to keep track as the traversal enters and exits specific parts of the AST.

## The Context Object

The `context` object contains additional functionality that is helpful for rules to do their jobs. As the name implies, the `context` object contains information that is relevant to the context of the rule. The `context` object has the following properties:
Expand Down

0 comments on commit a2cc54e

Please sign in to comment.