Skip to content

Commit

Permalink
Update: Flag a violation when rule options are used but an empty sche…
Browse files Browse the repository at this point in the history
…ma is present in `require-meta-schema` rule (#138)

This new check is similar to how:
* `require-meta-fixable` checks that `fixable: true` is specified when an autofixer is found
* `require-meta-has-suggestions` checks that `hasSuggestions: true` is specified when suggestions are provided

Note: this is *not* a breaking change, since ESLint itself already prevents rules from using options when an empty rule schema is specified, so it's not possible that this new violation could be flagged on existing rules.
  • Loading branch information
bmish authored Jun 20, 2021
1 parent c7f8bee commit 6ffddd7
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 5 deletions.
7 changes: 7 additions & 0 deletions docs/rules/require-meta-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ module.exports = {
meta: { schema: null },
create (context) {/* ... */},
};

module.exports = {
meta: { schema: [] },
create (context) {
const options = context.options; /* using options when schema is empty */
},
};
```

Examples of **correct** code for this rule:
Expand Down
37 changes: 32 additions & 5 deletions lib/rules/require-meta-schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ module.exports = {
},
],
messages: {
foundOptionsUsage: 'Found usage of rule options but no options are defined in `meta.schema`.',
missing: '`meta.schema` is required (use [] if rule has no schema).',
wrongType: '`meta.schema` should be an array or object (use [] if rule has no schema).',
},
Expand All @@ -37,15 +38,20 @@ module.exports = {
const sourceCode = context.getSourceCode();
const { scopeManager } = sourceCode;
const info = utils.getRuleInfo(sourceCode);
if (info === null || info.meta === null) {
return {};
}

let contextIdentifiers;
let hasEmptySchema = false;
let schemaNode;

return {
Program () {
if (info === null || info.meta === null) {
return;
}
Program (ast) {
contextIdentifiers = utils.getContextIdentifiers(context, ast);

const metaNode = info.meta;
const schemaNode =
schemaNode =
metaNode &&
metaNode.properties &&
metaNode.properties.find(p => p.type === 'Property' && utils.getKeyName(p) === 'schema');
Expand Down Expand Up @@ -83,10 +89,31 @@ module.exports = {
value = variable.defs[0].node.init;
}

if (
(value.type === 'ArrayExpression' && value.elements.length === 0) ||
(value.type === 'ObjectExpression' && value.properties.length === 0)
) {
// Schema is explicitly defined as having no options.
hasEmptySchema = true;
}

if (!['ArrayExpression', 'ObjectExpression'].includes(value.type)) {
context.report({ node: value, messageId: 'wrongType' });
}
},

MemberExpression (node) {
// Check if `context.options` was used when no options were defined in `meta.schema`.
if (
hasEmptySchema &&
node.object.type === 'Identifier' &&
contextIdentifiers.has(node.object) &&
node.property.type === 'Identifier' &&
node.property.name === 'options'
) {
context.report({ node: schemaNode, messageId: 'foundOptionsUsage' });
}
},
};
},
};
43 changes: 43 additions & 0 deletions tests/lib/rules/require-meta-schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,27 @@ ruleTester.run('require-meta-schema', rule, {
create(context) {}
};
`,
// Schema with options and using `context.options`.
`
module.exports = {
meta: { schema: { "enum": ["always", "never"] } },
create(context) { const options = context.options; }
};
`,
// Empty schema, using arbitrary property of `context`.
`
module.exports = {
meta: { schema: [] },
create(context) { const foo = context.foo; }
};
`,
// Empty schema, using arbitrary `options` property.
`
module.exports = {
meta: { schema: [] },
create(context) { const options = foo.options; }
};
`,
`
const schema = [];
module.exports = {
Expand Down Expand Up @@ -119,5 +140,27 @@ schema: [] },
output: null,
errors: [{ messageId: 'wrongType', type: 'Literal' }],
},
{
// Empty schema (array), but using rule options.
code: `
module.exports = {
meta: { schema: [] },
create(context) { const options = context.options; }
};
`,
output: null,
errors: [{ messageId: 'foundOptionsUsage', type: 'Property' }],
},
{
// Empty schema (object), but using rule options.
code: `
module.exports = {
meta: { schema: {} },
create(context) { const options = context.options; }
};
`,
output: null,
errors: [{ messageId: 'foundOptionsUsage', type: 'Property' }],
},
],
});

0 comments on commit 6ffddd7

Please sign in to comment.