Skip to content

Commit 588b60d

Browse files
committed
New: Add requireSchemaPropertyWhenOptionless option to require-meta-schema rule
1 parent 2f9b2b0 commit 588b60d

File tree

3 files changed

+101
-12
lines changed

3 files changed

+101
-12
lines changed

docs/rules/require-meta-schema.md

+6
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ module.exports = {
5959
};
6060
```
6161

62+
## Options
63+
64+
This rule takes an optional object containing:
65+
66+
* `boolean``requireSchemaPropertyWhenOptionless` — Whether the rule should require the `meta.schema` property to be specified (with `schema: []`) for rules that have no options. Defaults to `true`.
67+
6268
## Further Reading
6369

6470
* [working-with-rules#options-schemas](https://eslint.org/docs/developer-guide/working-with-rules#options-schemas)

lib/rules/require-meta-schema.js

+29-12
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,18 @@ module.exports = {
1616
recommended: false, // TODO: enable it in a major release.
1717
},
1818
fixable: 'code',
19-
schema: [],
19+
schema: [
20+
{
21+
type: 'object',
22+
properties: {
23+
requireSchemaPropertyWhenOptionless: {
24+
type: 'boolean',
25+
default: true,
26+
},
27+
},
28+
additionalProperties: false,
29+
},
30+
],
2031
messages: {
2132
foundOptionsUsage: '`meta.schema` has no schema defined but rule has options.',
2233
missing: '`meta.schema` is required (use [] if rule has no schema).',
@@ -33,27 +44,33 @@ module.exports = {
3344
}
3445

3546
let contextIdentifiers;
36-
let hasEmptySchema = false;
47+
const metaNode = info.meta;
3748
let schemaNode;
3849

50+
// Options
51+
const requireSchemaPropertyWhenOptionless = !context.options[0] || context.options[0].requireSchemaPropertyWhenOptionless;
52+
53+
let hasEmptySchema = false;
54+
3955
return {
4056
Program (ast) {
4157
contextIdentifiers = utils.getContextIdentifiers(context, ast);
4258

43-
const metaNode = info.meta;
4459
schemaNode =
4560
metaNode &&
4661
metaNode.properties &&
4762
metaNode.properties.find(p => p.type === 'Property' && utils.getKeyName(p) === 'schema');
4863

4964
if (!schemaNode) {
50-
context.report({
51-
node: metaNode,
52-
messageId: 'missing',
53-
fix (fixer) {
54-
return utils.insertProperty(fixer, metaNode, 'schema: []', sourceCode);
55-
},
56-
});
65+
if (requireSchemaPropertyWhenOptionless) {
66+
context.report({
67+
node: metaNode,
68+
messageId: 'missing',
69+
fix (fixer) {
70+
return utils.insertProperty(fixer, metaNode, 'schema: []', sourceCode);
71+
},
72+
});
73+
}
5774
return;
5875
}
5976

@@ -95,13 +112,13 @@ module.exports = {
95112
MemberExpression (node) {
96113
// Check if `context.options` was used when no options were defined in `meta.schema`.
97114
if (
98-
hasEmptySchema &&
115+
(hasEmptySchema || !schemaNode) &&
99116
node.object.type === 'Identifier' &&
100117
contextIdentifiers.has(node.object) &&
101118
node.property.type === 'Identifier' &&
102119
node.property.name === 'options'
103120
) {
104-
context.report({ node: schemaNode, messageId: 'foundOptionsUsage' });
121+
context.report({ node: schemaNode || metaNode, messageId: 'foundOptionsUsage' });
105122
}
106123
},
107124
};

tests/lib/rules/require-meta-schema.js

+66
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,17 @@ ruleTester.run('require-meta-schema', rule, {
8383
create
8484
};
8585
`,
86+
87+
{
88+
// requireSchemaPropertyWhenOptionless = false
89+
code: `
90+
module.exports = {
91+
meta: {},
92+
create(context) {}
93+
};
94+
`,
95+
options: [{ requireSchemaPropertyWhenOptionless: false }],
96+
},
8697
],
8798

8899
invalid: [
@@ -103,6 +114,25 @@ schema: []
103114
`,
104115
errors: [{ messageId: 'missing', type: 'ObjectExpression' }],
105116
},
117+
{
118+
// requireSchemaPropertyWhenOptionless = true.
119+
code: `
120+
module.exports = {
121+
meta: {},
122+
create(context) {}
123+
};
124+
`,
125+
output: `
126+
module.exports = {
127+
meta: {
128+
schema: []
129+
},
130+
create(context) {}
131+
};
132+
`,
133+
options: [{ requireSchemaPropertyWhenOptionless: true }],
134+
errors: [{ messageId: 'missing', type: 'ObjectExpression' }],
135+
},
106136
{
107137
code: `
108138
module.exports = {
@@ -129,6 +159,18 @@ schema: [] },
129159
output: null,
130160
errors: [{ messageId: 'wrongType', type: 'Literal' }],
131161
},
162+
{
163+
// requireSchemaPropertyWhenOptionless = false.
164+
code: `
165+
module.exports = {
166+
meta: { schema: null },
167+
create(context) {}
168+
};
169+
`,
170+
output: null,
171+
options: [{ requireSchemaPropertyWhenOptionless: false }],
172+
errors: [{ messageId: 'wrongType', type: 'Literal' }],
173+
},
132174
{
133175
code: `
134176
module.exports = {
@@ -172,5 +214,29 @@ schema: [] },
172214
output: null,
173215
errors: [{ messageId: 'foundOptionsUsage', type: 'Property' }],
174216
},
217+
{
218+
// Empty schema (object), but using rule options, requireSchemaPropertyWhenOptionless = false.
219+
code: `
220+
module.exports = {
221+
meta: { schema: {} },
222+
create(context) { const options = context.options; }
223+
};
224+
`,
225+
output: null,
226+
options: [{ requireSchemaPropertyWhenOptionless: false }],
227+
errors: [{ messageId: 'foundOptionsUsage', type: 'Property' }],
228+
},
229+
{
230+
// No schema, but using rule options, requireSchemaPropertyWhenOptionless = false.
231+
code: `
232+
module.exports = {
233+
meta: {},
234+
create(context) { const options = context.options; }
235+
};
236+
`,
237+
output: null,
238+
options: [{ requireSchemaPropertyWhenOptionless: false }],
239+
errors: [{ messageId: 'foundOptionsUsage', type: 'ObjectExpression' }],
240+
},
175241
],
176242
});

0 commit comments

Comments
 (0)