Skip to content

Commit

Permalink
New: internal-rules/consistent-docs-url (refs #6582)
Browse files Browse the repository at this point in the history
Add a lint rule to ensure that each rule has a meta.docs.url property with the correct value.
  • Loading branch information
Patrick McElhaney authored and Patrick McElhaney committed Dec 29, 2017
1 parent 3a40794 commit b84dacb
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 2 deletions.
1 change: 1 addition & 0 deletions lib/rules/.eslintrc.yml
@@ -1,3 +1,4 @@
rules:
rulesdir/no-invalid-meta: "error"
rulesdir/consistent-docs-description: "error"
rulesdir/consistent-docs-url: "error"
84 changes: 84 additions & 0 deletions tests/tools/internal-rules/consistent-docs-url.js
@@ -0,0 +1,84 @@
/**
* @fileoverview Tests for internal-consistent-docs-url rule.
* @author Patrick McElhaney
*/

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const rule = require("../../../tools/internal-rules/consistent-docs-url"),
RuleTester = require("../../../lib/testers/rule-tester");

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

const ruleTester = new RuleTester();

ruleTester.run("consistent-docs-description", rule, {
valid: [

// wrong exports format: "internal-no-invalid-meta" reports this already
[
"module.exports = function(context) {",
" return {",
" Program: function(node) {}",
" };",
"};"
].join("\n"),
[
"module.exports = {",
" meta: {",
" docs: {",
" url: 'https://eslint.org/docs/rules/<input>'",
" }",
" },",
" create: function(context) {",
" return {};",
" }",
"};"
].join("\n")
],
invalid: [
{
code: [
"module.exports = {",
" meta: {",
" docs: {}",
" },",

" create: function(context) {",
" return {};",
" }",
"};"
].join("\n"),
errors: [{
message: "Rule is missing a meta.docs.url property",
line: 3,
column: 9
}]
},
{
code: [
"module.exports = {",
" meta: {",
" docs: {",
" url: 'http://example.com/wrong-url'",
" }",
" },",
" create: function(context) {",
" return {};",
" }",
"};"
].join("\n"),
errors: [{
message: "Incorrect url. Expected \"https://eslint.org/docs/rules/<input>\" but got \"http://example.com/wrong-url\"",
line: 4,
column: 18
}]
}
]
});
1 change: 1 addition & 0 deletions tools/internal-rules/.eslintrc.yml
@@ -1,3 +1,4 @@
rules:
rulesdir/no-invalid-meta: "error"
rulesdir/consistent-docs-description: "error"
rulesdir/consistent-docs-url: "error"
2 changes: 1 addition & 1 deletion tools/internal-rules/consistent-docs-description.js
Expand Up @@ -104,7 +104,7 @@ function checkMetaDocsDescription(context, exportsNode) {

module.exports = {
meta: {
docs: {
docs: {// eslint-disable-line rulesdir/consistent-docs-url
description: "enforce correct conventions of `meta.docs.description` property in core rules",
category: "Internal",
recommended: false
Expand Down
101 changes: 101 additions & 0 deletions tools/internal-rules/consistent-docs-url.js
@@ -0,0 +1,101 @@
/**
* @fileoverview Internal rule to enforce meta.docs.url conventions.
* @author Patrick McElhaney
*/

"use strict";

const path = require("path");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
* Gets the property of the Object node passed in that has the name specified.
*
* @param {string} property Name of the property to return.
* @param {ASTNode} node The ObjectExpression node.
* @returns {ASTNode} The Property node or null if not found.
*/
function getPropertyFromObject(property, node) {
const properties = node.properties;

for (let i = 0; i < properties.length; i++) {
if (properties[i].key.name === property) {
return properties[i];
}
}

return null;
}

/**
* Verifies that the meta.docs.url property is present and has the correct value.
*
* @param {RuleContext} context The ESLint rule context.
* @param {ASTNode} exportsNode ObjectExpression node that the rule exports.
* @returns {void}
*/
function checkMetaDocsUrl(context, exportsNode) {
if (exportsNode.type !== "ObjectExpression") {

// if the exported node is not the correct format, "internal-no-invalid-meta" will already report this.
return;
}

const metaProperty = getPropertyFromObject("meta", exportsNode);
const metaDocs = metaProperty && getPropertyFromObject("docs", metaProperty.value);
const metaDocsUrl = metaDocs && getPropertyFromObject("url", metaDocs.value);

if (!metaDocsUrl) {
context.report({
node: metaDocs,
message: "Rule is missing a meta.docs.url property"
});
return;
}

const ruleId = path.basename(context.getFilename().replace(/.js$/, ""));
const expected = `https://eslint.org/docs/rules/${ruleId}`;
const url = metaDocsUrl.value.value;

if (url !== expected) {
context.report({
node: metaDocsUrl.value,
message: `Incorrect url. Expected "${expected}" but got "${url}"`
});
}

}

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

module.exports = {
meta: {
docs: {// eslint-disable-line rulesdir/consistent-docs-url
description: "enforce correct conventions of `meta.docs.url` property in core rules",
category: "Internal",
recommended: false
},

schema: []
},

create(context) {
return {
AssignmentExpression(node) {
if (node.left &&
node.right &&
node.left.type === "MemberExpression" &&
node.left.object.name === "module" &&
node.left.property.name === "exports") {

checkMetaDocsUrl(context, node.right);
}
}
};
}
};
2 changes: 1 addition & 1 deletion tools/internal-rules/no-invalid-meta.js
Expand Up @@ -151,7 +151,7 @@ function isCorrectExportsFormat(node) {

module.exports = {
meta: {
docs: {
docs: {// eslint-disable-line rulesdir/consistent-docs-url
description: "enforce correct use of `meta` property in core rules",
category: "Internal",
recommended: false
Expand Down

0 comments on commit b84dacb

Please sign in to comment.