-
-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
1 parent
c16bd2e
commit 114d2c7
Showing
5 changed files
with
974 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
# require rules to implement a meta.docs.url property (require-meta-docs-url) | ||
|
||
`meta.docs.url` property is the official location to store a URL to their documentation in the rule metadata. | ||
Some integration tools will show the URL to users to understand rules. | ||
|
||
## Rule Details | ||
|
||
This rule aims to require ESLint rules to have a `meta.docs.url` property. | ||
|
||
This rule has an option. | ||
|
||
```json | ||
{ | ||
"eslint-plugin/require-meta-docs-url": ["error", { | ||
"pattern": "https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/{{name}}.md" | ||
}] | ||
} | ||
``` | ||
|
||
- `pattern` (`string`) ... A pattern to enforce rule's document URL. It replaces `{{name}}` placeholder by each rule name. The rule name is the basename of each rule file. Default is undefined. | ||
|
||
If you set the `pattern` option, this rule adds `meta.docs.url` property automatically when you execute `eslint --fix` command. | ||
|
||
The following patterns are considered warnings: | ||
|
||
```js | ||
|
||
/* eslint eslint-plugin/require-meta-docs-url: "error" */ | ||
|
||
module.exports = { | ||
meta: {}, | ||
create(context) { | ||
} | ||
}; | ||
|
||
``` | ||
|
||
```js | ||
|
||
/* eslint eslint-plugin/require-meta-docs-url: "error" */ | ||
|
||
module.exports = { | ||
meta: { | ||
docs: { | ||
url: undefined | ||
} | ||
}, | ||
create(context) { | ||
} | ||
}; | ||
|
||
``` | ||
|
||
```js | ||
|
||
/* eslint eslint-plugin/require-meta-docs-url: ["error", {"pattern": "path/to/{{name}}.md"}] */ | ||
|
||
module.exports = { | ||
meta: { | ||
docs: { | ||
url: "wrong URL" | ||
} | ||
}, | ||
create(context) { | ||
} | ||
}; | ||
|
||
``` | ||
|
||
The following patterns are not warnings: | ||
|
||
```js | ||
|
||
/* eslint eslint-plugin/require-meta-docs-url: "error" */ | ||
|
||
module.exports = { | ||
meta: { | ||
docs: { | ||
url: "a URL" | ||
} | ||
}, | ||
create(context) { | ||
} | ||
}; | ||
|
||
``` | ||
|
||
```js | ||
|
||
/* eslint eslint-plugin/require-meta-docs-url: ["error", {"pattern": "path/to/{{name}}.md"}] */ | ||
|
||
module.exports = { | ||
meta: { | ||
docs: { | ||
url: "path/to/rule-name.md" | ||
} | ||
}, | ||
create(context) { | ||
} | ||
}; | ||
|
||
``` | ||
|
||
## Version specific URL | ||
|
||
If you want to enforce version-specific URLs, it's feasible easily with `.eslintrc.js` and `npm version <type>` script. | ||
For example: | ||
|
||
**.eslintrc.js**: | ||
|
||
```js | ||
"use strict" | ||
|
||
const version = require("./package.json").version | ||
|
||
module.exports = { | ||
plugins: ["eslint-plugin"], | ||
// ... leaving out ... | ||
rules: { | ||
"eslint-plugin/require-meta-docs-url": ["error", { | ||
pattern: `path/to/v${version}/docs/rules/{{name}}.md`, | ||
}], | ||
} | ||
} | ||
``` | ||
|
||
**package.json**: | ||
|
||
```json | ||
{ | ||
"version": "1.0.0", | ||
"scripts": { | ||
"pretest": "eslint .", | ||
"test": "... leaving out ...", | ||
"preversion": "npm test", | ||
"version": "eslint . --fix && git add ." | ||
}, | ||
// ... leaving out ... | ||
} | ||
``` | ||
|
||
Then `npm version <type>` command will update every rule to the new version's URL. | ||
|
||
> npm runs `preversion` script on the current version, runs `version` script on the new version, and commits and makes a tag. | ||
> | ||
> Further reading: https://docs.npmjs.com/cli/version | ||
## When Not To Use It | ||
|
||
If you do not plan to provide rule's documentation in website, you can turn off this rule. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
/** | ||
* @author Toru Nagashima <https://github.com/mysticatea> | ||
*/ | ||
|
||
'use strict'; | ||
|
||
// ----------------------------------------------------------------------------- | ||
// Requirements | ||
// ----------------------------------------------------------------------------- | ||
|
||
const path = require('path'); | ||
const util = require('../utils'); | ||
|
||
// ----------------------------------------------------------------------------- | ||
// Rule Definition | ||
// ----------------------------------------------------------------------------- | ||
|
||
module.exports = { | ||
meta: { | ||
docs: { | ||
description: 'require rules to implement a meta.docs.url property', | ||
category: 'Rules', | ||
recommended: false, | ||
}, | ||
fixable: 'code', | ||
schema: [{ | ||
type: 'object', | ||
properties: { | ||
pattern: { type: 'string' }, | ||
}, | ||
additionalProperties: false, | ||
}], | ||
}, | ||
|
||
/** | ||
* Creates AST event handlers for require-meta-docs-url. | ||
* @param {RuleContext} context - The rule context. | ||
* @returns {Object} AST event handlers. | ||
*/ | ||
create (context) { | ||
const options = context.options[0] || {}; | ||
const sourceCode = context.getSourceCode(); | ||
const filename = context.getFilename(); | ||
const ruleName = filename === '<input>' ? undefined : path.basename(filename, '.js'); | ||
const expectedUrl = !options.pattern || !ruleName | ||
? undefined | ||
: options.pattern.replace(/{{\s*name\s*}}/g, ruleName); | ||
|
||
/** | ||
* Check whether a given node is the expected URL. | ||
* @param {Node} node The node of property value to check. | ||
* @returns {boolean} `true` if the node is the expected URL. | ||
*/ | ||
function isExpectedUrl (node) { | ||
return Boolean( | ||
node && | ||
node.type === 'Literal' && | ||
typeof node.value === 'string' && | ||
( | ||
expectedUrl === undefined || | ||
node.value === expectedUrl | ||
) | ||
); | ||
} | ||
|
||
/** | ||
* Insert a given property into a given object literal. | ||
* @param {SourceCodeFixer} fixer The fixer. | ||
* @param {Node} node The ObjectExpression node to insert a property. | ||
* @param {string} propertyText The property code to insert. | ||
* @returns {void} | ||
*/ | ||
function insertProperty (fixer, node, propertyText) { | ||
if (node.properties.length === 0) { | ||
return fixer.replaceText(node, `{\n${propertyText}\n}`); | ||
} | ||
return fixer.insertTextAfter( | ||
sourceCode.getLastToken(node.properties[node.properties.length - 1]), | ||
`,\n${propertyText}` | ||
); | ||
} | ||
|
||
return { | ||
Program (node) { | ||
const info = util.getRuleInfo(node); | ||
if (info === null) { | ||
return; | ||
} | ||
|
||
const metaNode = info.meta; | ||
const docsPropNode = | ||
metaNode && | ||
metaNode.properties && | ||
metaNode.properties.find(p => p.type === 'Property' && util.getKeyName(p) === 'docs'); | ||
const urlPropNode = | ||
docsPropNode && | ||
docsPropNode.value.properties && | ||
docsPropNode.value.properties.find(p => p.type === 'Property' && util.getKeyName(p) === 'url'); | ||
|
||
if (isExpectedUrl(urlPropNode && urlPropNode.value)) { | ||
return; | ||
} | ||
|
||
context.report({ | ||
loc: | ||
(urlPropNode && urlPropNode.value.loc) || | ||
(docsPropNode && docsPropNode.value.loc) || | ||
(metaNode && metaNode.loc) || | ||
node.loc.start, | ||
|
||
message: | ||
!urlPropNode ? 'Rules should export a `meta.docs.url` property.' : | ||
!expectedUrl ? '`meta.docs.url` property must be a string.' : | ||
/* otherwise */ '`meta.docs.url` property must be `{{expectedUrl}}`.', | ||
|
||
data: { | ||
expectedUrl, | ||
}, | ||
|
||
fix (fixer) { | ||
if (expectedUrl) { | ||
const urlString = JSON.stringify(expectedUrl); | ||
if (urlPropNode) { | ||
return fixer.replaceText(urlPropNode.value, urlString); | ||
} | ||
if (docsPropNode && docsPropNode.value.type === 'ObjectExpression') { | ||
return insertProperty(fixer, docsPropNode.value, `url: ${urlString}`); | ||
} | ||
if (!docsPropNode && metaNode && metaNode.type === 'ObjectExpression') { | ||
return insertProperty(fixer, metaNode, `docs: {\nurl: ${urlString}\n}`); | ||
} | ||
} | ||
return null; | ||
}, | ||
}); | ||
}, | ||
}; | ||
}, | ||
}; |
Oops, something went wrong.