Skip to content

Commit 10b28f0

Browse files
bmishnot-an-aardvark
authored andcommitted
New: add new rule require-meta-schema (#87)
1 parent 38ad521 commit 10b28f0

File tree

7 files changed

+215
-19
lines changed

7 files changed

+215
-19
lines changed

Diff for: README.md

+1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ Name | ✔️ | 🛠 | Description
6464
[report-message-format](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/report-message-format.md) | | | enforce a consistent format for rule report messages
6565
[require-meta-docs-url](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/require-meta-docs-url.md) | | 🛠 | require rules to implement a meta.docs.url property
6666
[require-meta-fixable](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/require-meta-fixable.md) | ✔️ | | require rules to implement a meta.fixable property
67+
[require-meta-schema](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/require-meta-schema.md) | | 🛠 | require rules to implement a meta.schema property
6768
[require-meta-type](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/require-meta-type.md) | | | require rules to implement a meta.type property
6869
[test-case-property-ordering](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/test-case-property-ordering.md) | | 🛠 | Requires the properties of a test case to be placed in a consistent order
6970
[test-case-shorthand-strings](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/test-case-shorthand-strings.md) | | 🛠 | Enforce consistent usage of shorthand strings for test cases with no options

Diff for: docs/rules/require-meta-schema.md

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# require rules to implement a meta.schema property (require-meta-schema)
2+
3+
Defining a schema for each rule allows eslint to validate that configuration options are passed correctly. Even when there are no options for a rule, a schema should still be defined (as an empty array) so that eslint can validate that no data is passed to the rule.
4+
5+
## Rule Details
6+
7+
This rule requires ESLint rules to have a valid `meta.schema` property.
8+
9+
Examples of **incorrect** code for this rule:
10+
11+
```js
12+
/* eslint eslint-plugin/require-meta-schema: error */
13+
module.exports = {
14+
meta: {},
15+
create: function(context) { /* ... */}
16+
};
17+
18+
module.exports = {
19+
meta: { schema: null },
20+
create: function(context) { /* ... */}
21+
};
22+
```
23+
24+
Examples of **correct** code for this rule:
25+
26+
```js
27+
/* eslint eslint-plugin/require-meta-type: error */
28+
module.exports = {
29+
meta: { schema: [] }, // ensures no options are passed to the rule
30+
create: function(context) { /* ... */}
31+
};
32+
33+
module.exports = {
34+
meta: {
35+
schema: [
36+
{
37+
type: 'object',
38+
properties: {
39+
exceptRange: {
40+
type: 'boolean'
41+
}
42+
},
43+
additionalProperties: false
44+
}
45+
]
46+
},
47+
create: function(context) { /* ... */}
48+
};
49+
```
50+
51+
## Further Reading
52+
53+
* [working-with-rules#options-schemas](https://eslint.org/docs/developer-guide/working-with-rules#options-schemas)

Diff for: lib/rules/fixer-return.js

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ module.exports = {
2424
},
2525
type: 'problem',
2626
fixable: null,
27+
schema: [],
2728
},
2829

2930
create (context) {

Diff for: lib/rules/require-meta-docs-url.js

+2-19
Original file line numberDiff line numberDiff line change
@@ -64,23 +64,6 @@ module.exports = {
6464
);
6565
}
6666

67-
/**
68-
* Insert a given property into a given object literal.
69-
* @param {SourceCodeFixer} fixer The fixer.
70-
* @param {Node} node The ObjectExpression node to insert a property.
71-
* @param {string} propertyText The property code to insert.
72-
* @returns {void}
73-
*/
74-
function insertProperty (fixer, node, propertyText) {
75-
if (node.properties.length === 0) {
76-
return fixer.replaceText(node, `{\n${propertyText}\n}`);
77-
}
78-
return fixer.insertTextAfter(
79-
sourceCode.getLastToken(node.properties[node.properties.length - 1]),
80-
`,\n${propertyText}`
81-
);
82-
}
83-
8467
return {
8568
Program (node) {
8669
const info = util.getRuleInfo(node);
@@ -125,10 +108,10 @@ module.exports = {
125108
return fixer.replaceText(urlPropNode.value, urlString);
126109
}
127110
if (docsPropNode && docsPropNode.value.type === 'ObjectExpression') {
128-
return insertProperty(fixer, docsPropNode.value, `url: ${urlString}`);
111+
return util.insertProperty(fixer, docsPropNode.value, `url: ${urlString}`, sourceCode);
129112
}
130113
if (!docsPropNode && metaNode && metaNode.type === 'ObjectExpression') {
131-
return insertProperty(fixer, metaNode, `docs: {\nurl: ${urlString}\n}`);
114+
return util.insertProperty(fixer, metaNode, `docs: {\nurl: ${urlString}\n}`, sourceCode);
132115
}
133116
}
134117
return null;

Diff for: lib/rules/require-meta-schema.js

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
'use strict';
2+
3+
const utils = require('../utils');
4+
5+
// ------------------------------------------------------------------------------
6+
// Rule Definition
7+
// ------------------------------------------------------------------------------
8+
9+
module.exports = {
10+
meta: {
11+
docs: {
12+
description: 'require rules to implement a meta.schema property',
13+
category: 'Rules',
14+
recommended: false, // TODO: enable it in a major release.
15+
},
16+
type: 'suggestion',
17+
fixable: 'code',
18+
schema: [
19+
{
20+
type: 'object',
21+
properties: {
22+
exceptRange: {
23+
type: 'boolean',
24+
},
25+
},
26+
additionalProperties: false,
27+
},
28+
],
29+
messages: {
30+
missing: '`meta.schema` is required (use [] if rule has no schema).',
31+
wrongType: '`meta.schema` should be an array (use [] if rule has no schema).',
32+
},
33+
},
34+
35+
create (context) {
36+
const sourceCode = context.getSourceCode();
37+
const info = utils.getRuleInfo(sourceCode.ast, sourceCode.scopeManager);
38+
39+
return {
40+
Program () {
41+
if (info === null || info.meta === null) {
42+
return;
43+
}
44+
45+
const metaNode = info.meta;
46+
const schemaNode =
47+
metaNode &&
48+
metaNode.properties &&
49+
metaNode.properties.find(p => p.type === 'Property' && utils.getKeyName(p) === 'schema');
50+
51+
if (!schemaNode) {
52+
context.report({
53+
node: metaNode,
54+
messageId: 'missing',
55+
fix (fixer) {
56+
return utils.insertProperty(fixer, metaNode, 'schema: []', sourceCode);
57+
},
58+
});
59+
} else if (schemaNode.value.type !== 'ArrayExpression') {
60+
context.report({ node: schemaNode.value, messageId: 'wrongType' });
61+
}
62+
},
63+
};
64+
},
65+
};

Diff for: lib/utils.js

+17
Original file line numberDiff line numberDiff line change
@@ -322,4 +322,21 @@ module.exports = {
322322
.reduce((allRefs, refsForVariable) => allRefs.concat(refsForVariable), [])
323323
.map(ref => ref.identifier));
324324
},
325+
326+
/**
327+
* Insert a given property into a given object literal.
328+
* @param {SourceCodeFixer} fixer The fixer.
329+
* @param {Node} node The ObjectExpression node to insert a property.
330+
* @param {string} propertyText The property code to insert.
331+
* @returns {void}
332+
*/
333+
insertProperty (fixer, node, propertyText, sourceCode) {
334+
if (node.properties.length === 0) {
335+
return fixer.replaceText(node, `{\n${propertyText}\n}`);
336+
}
337+
return fixer.insertTextAfter(
338+
sourceCode.getLastToken(node.properties[node.properties.length - 1]),
339+
`,\n${propertyText}`
340+
);
341+
},
325342
};

Diff for: tests/lib/rules/require-meta-schema.js

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
'use strict';
2+
3+
// ------------------------------------------------------------------------------
4+
// Requirements
5+
// ------------------------------------------------------------------------------
6+
7+
const rule = require('../../../lib/rules/require-meta-schema');
8+
const RuleTester = require('eslint').RuleTester;
9+
10+
// ------------------------------------------------------------------------------
11+
// Tests
12+
// ------------------------------------------------------------------------------
13+
14+
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
15+
ruleTester.run('require-meta-schema', rule, {
16+
valid: [
17+
`
18+
module.exports = {
19+
meta: { schema: [] },
20+
create(context) {}
21+
};
22+
`,
23+
`
24+
module.exports = {
25+
meta: { schema: [ { "enum": ["always", "never"] } ] },
26+
create(context) {}
27+
};
28+
`,
29+
],
30+
31+
invalid: [
32+
{
33+
code: `
34+
module.exports = {
35+
meta: {},
36+
create(context) {}
37+
};
38+
`,
39+
output: `
40+
module.exports = {
41+
meta: {
42+
schema: []
43+
},
44+
create(context) {}
45+
};
46+
`,
47+
errors: [{ messageId: 'missing', type: 'ObjectExpression' }],
48+
},
49+
{
50+
code: `
51+
module.exports = {
52+
meta: { type: 'problem' },
53+
create(context) {}
54+
};
55+
`,
56+
output: `
57+
module.exports = {
58+
meta: { type: 'problem',
59+
schema: [] },
60+
create(context) {}
61+
};
62+
`,
63+
errors: [{ messageId: 'missing', type: 'ObjectExpression' }],
64+
},
65+
{
66+
code: `
67+
module.exports = {
68+
meta: { schema: null },
69+
create(context) {}
70+
};
71+
`,
72+
output: null,
73+
errors: [{ messageId: 'wrongType', type: 'Literal' }],
74+
},
75+
],
76+
});

0 commit comments

Comments
 (0)