Skip to content

Commit

Permalink
New: exposes rule metadata to the Node API (fixes eslint#6582)
Browse files Browse the repository at this point in the history
  • Loading branch information
pmcelhaney committed Jul 4, 2016
1 parent ee7fcfa commit 4297749
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 5 deletions.
11 changes: 11 additions & 0 deletions docs/developer-guide/nodejs-api.md
Expand Up @@ -105,6 +105,16 @@ The `verify()` method returns an array of objects containing information about t
fix: {
range: [1, 15],
text: ";"
},
meta: {
docs: {
description: "disallow unnecessary semicolons",
category: "Possible Errors",
recommended: true,
uri: "http://eslint.org/docs/rules/semi"
},
fixable: "code",
schema: [] // no options
}
}
```
Expand All @@ -120,6 +130,7 @@ The information available for each linting message is:
* `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).
* `fix` - an object describing the fix for the problem (this property is omitted if no fix is available).
* `meta` - metadata from the rule definition

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

Expand Down
4 changes: 3 additions & 1 deletion docs/developer-guide/working-with-rules.md
Expand Up @@ -28,7 +28,8 @@ module.exports = {
docs: {
description: "disallow unnecessary semicolons",
category: "Possible Errors",
recommended: true
recommended: true,
uri: "http://eslint.org/docs/rules/semi"
},
fixable: "code",
schema: [] // no options
Expand All @@ -52,6 +53,7 @@ The source file for a rule exports an object with the following properties.
* `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
* `uri` (string) URI pointing to the documentation for the rule

In a custom rule or plugin, you can omit `docs` or include any properties that you need in it.

Expand Down
8 changes: 7 additions & 1 deletion lib/eslint.js
Expand Up @@ -455,9 +455,11 @@ function prepareConfig(config) {
globals: ConfigOps.merge({}, config.globals),
env: ConfigOps.merge({}, config.env || {}),
settings: ConfigOps.merge({}, config.settings || {}),
parserOptions: ConfigOps.merge(parserOptions, config.parserOptions || {})
parserOptions: ConfigOps.merge(parserOptions, config.parserOptions || {}),
meta: ConfigOps.merge({}, config.meta || {})

This comment has been minimized.

Copy link
@pmcelhaney

pmcelhaney Aug 2, 2016

Author Owner

This is waste of memory. We can pass the meta object by reference and it works just as well (passes the tests).

meta: config.meta

This comment has been minimized.

Copy link
@ilyavolodin

ilyavolodin Aug 2, 2016

This is done to cover the cases of the legacy rule format, where meta property doesn't exists on the config object.

This comment has been minimized.

Copy link
@pmcelhaney

pmcelhaney Aug 2, 2016

Author Owner

This is a line I added. I followed the pattern above, but I don't see any reason to make a copy of the meta object here. We can just point to it. If the rule doesn't have meta, that's fine. It will just evaluate to undefined and the problem object won't have a meta property. Clients of the API just need to know the meta property isn't guaranteed to exist.

};


if (preparedConfig.parserOptions.sourceType === "module") {
if (!preparedConfig.parserOptions.ecmaFeatures) {
preparedConfig.parserOptions.ecmaFeatures = {};
Expand Down Expand Up @@ -981,6 +983,10 @@ module.exports = (function() {
source: sourceCode.lines[location.line - 1] || ""
};

if (meta) {
problem.meta = meta;
}

// ensure there's range and text properties, otherwise it's not a valid fix
if (fix && Array.isArray(fix.range) && (typeof fix.text === "string")) {

Expand Down
59 changes: 56 additions & 3 deletions tests/lib/cli-engine.js
Expand Up @@ -256,7 +256,25 @@ describe("CLIEngine", function() {
line: 1,
column: 11,
nodeType: "Identifier",
source: "var bar = foo"
source: "var bar = foo",
meta: {
docs: {
category: "Variables",
description: "disallow the use of undeclared variables unless mentioned in `/*global */` comments",
recommended: true
},
schema: [
{
additionalProperties: false,
properties: {
typeof: {
type: "boolean"
}
},
type: "object"
}
]
}
}
],
errorCount: 1,
Expand Down Expand Up @@ -1042,6 +1060,7 @@ describe("CLIEngine", function() {

describe("Fix Mode", function() {


it("should return fixed text on multiple files when in fix mode", function() {

/**
Expand Down Expand Up @@ -1097,7 +1116,23 @@ describe("CLIEngine", function() {
nodeType: "BinaryExpression",
ruleId: "eqeqeq",
severity: 2,
source: "if (msg == \"hi\") {"
source: "if (msg == \"hi\") {",
meta: {
docs: {
category: "Best Practices",
description: "require the use of `===` and `!==`",
recommended: false
},
schema: [
{
enum: [
"always",
"smart",
"allow-null"
]
}
]
}
}
],
errorCount: 1,
Expand All @@ -1114,7 +1149,25 @@ describe("CLIEngine", function() {
nodeType: "Identifier",
ruleId: "no-undef",
severity: 2,
source: "var msg = \"hi\" + foo;"
source: "var msg = \"hi\" + foo;",
meta: {
docs: {
category: "Variables",
description: "disallow the use of undeclared variables unless mentioned in `/*global */` comments",
recommended: true
},
schema: [
{
additionalProperties: false,
properties: {
typeof: {
type: "boolean"
}
},
type: "object"
}
]
}
}
],
errorCount: 1,
Expand Down
33 changes: 33 additions & 0 deletions tests/lib/eslint.js
Expand Up @@ -1723,6 +1723,39 @@ describe("eslint", function() {
});
});

describe("When a URI is provided for the rule documentation", function() {
var RULE_URI = "http://path.to/docs/rules/test-doc-rule";

eslint.defineRule("test-plugin/test-doc-rule", {
meta: {
docs: {
uri: RULE_URI
}
},
create: function(context) {
return {
Literal: function(node) {
if (node.value === "trigger violation") {
context.report(node, "Reporting violation.");
}
}
};
}
});

it("should report the URI", function() {
var config = {
rules: {"test-plugin/test-doc-rule": 2}
};
var code = "var a = \"trigger violation\";";

eslint.reset();
var messages = eslint.verify(code, config, filename, false);

assert.equal(messages[0].meta.docs.uri, RULE_URI);
});
});

describe("when evaluating code with comments to enable rules", function() {

it("should report a violation", function() {
Expand Down

0 comments on commit 4297749

Please sign in to comment.