diff --git a/docs/rules/require-meta-schema.md b/docs/rules/require-meta-schema.md index af5f773c..1dad0f6b 100644 --- a/docs/rules/require-meta-schema.md +++ b/docs/rules/require-meta-schema.md @@ -59,6 +59,12 @@ module.exports = { }; ``` +## Options + +This rule takes an optional object containing: + +* `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`. + ## Further Reading * [working-with-rules#options-schemas](https://eslint.org/docs/developer-guide/working-with-rules#options-schemas) diff --git a/lib/rules/require-meta-schema.js b/lib/rules/require-meta-schema.js index 8be7d6c2..6ac881f5 100644 --- a/lib/rules/require-meta-schema.js +++ b/lib/rules/require-meta-schema.js @@ -16,7 +16,18 @@ module.exports = { recommended: false, // TODO: enable it in a major release. }, fixable: 'code', - schema: [], + schema: [ + { + type: 'object', + properties: { + requireSchemaPropertyWhenOptionless: { + type: 'boolean', + default: true, + }, + }, + additionalProperties: false, + }, + ], messages: { foundOptionsUsage: '`meta.schema` has no schema defined but rule has options.', missing: '`meta.schema` is required (use [] if rule has no schema).', @@ -33,27 +44,33 @@ module.exports = { } let contextIdentifiers; - let hasEmptySchema = false; + const metaNode = info.meta; let schemaNode; + // Options + const requireSchemaPropertyWhenOptionless = !context.options[0] || context.options[0].requireSchemaPropertyWhenOptionless; + + let hasEmptySchema = false; + return { Program (ast) { contextIdentifiers = utils.getContextIdentifiers(context, ast); - const metaNode = info.meta; schemaNode = metaNode && metaNode.properties && metaNode.properties.find(p => p.type === 'Property' && utils.getKeyName(p) === 'schema'); if (!schemaNode) { - context.report({ - node: metaNode, - messageId: 'missing', - fix (fixer) { - return utils.insertProperty(fixer, metaNode, 'schema: []', sourceCode); - }, - }); + if (requireSchemaPropertyWhenOptionless) { + context.report({ + node: metaNode, + messageId: 'missing', + fix (fixer) { + return utils.insertProperty(fixer, metaNode, 'schema: []', sourceCode); + }, + }); + } return; } @@ -95,13 +112,13 @@ module.exports = { MemberExpression (node) { // Check if `context.options` was used when no options were defined in `meta.schema`. if ( - hasEmptySchema && + (hasEmptySchema || !schemaNode) && node.object.type === 'Identifier' && contextIdentifiers.has(node.object) && node.property.type === 'Identifier' && node.property.name === 'options' ) { - context.report({ node: schemaNode, messageId: 'foundOptionsUsage' }); + context.report({ node: schemaNode || metaNode, messageId: 'foundOptionsUsage' }); } }, }; diff --git a/tests/lib/rules/require-meta-schema.js b/tests/lib/rules/require-meta-schema.js index 90c5f85a..562f1a90 100644 --- a/tests/lib/rules/require-meta-schema.js +++ b/tests/lib/rules/require-meta-schema.js @@ -83,6 +83,17 @@ ruleTester.run('require-meta-schema', rule, { create }; `, + + { + // requireSchemaPropertyWhenOptionless = false + code: ` + module.exports = { + meta: {}, + create(context) {} + }; + `, + options: [{ requireSchemaPropertyWhenOptionless: false }], + }, ], invalid: [ @@ -103,6 +114,25 @@ schema: [] `, errors: [{ messageId: 'missing', type: 'ObjectExpression' }], }, + { + // requireSchemaPropertyWhenOptionless = true. + code: ` + module.exports = { + meta: {}, + create(context) {} + }; + `, + output: ` + module.exports = { + meta: { +schema: [] +}, + create(context) {} + }; + `, + options: [{ requireSchemaPropertyWhenOptionless: true }], + errors: [{ messageId: 'missing', type: 'ObjectExpression' }], + }, { code: ` module.exports = { @@ -129,6 +159,18 @@ schema: [] }, output: null, errors: [{ messageId: 'wrongType', type: 'Literal' }], }, + { + // requireSchemaPropertyWhenOptionless = false. + code: ` + module.exports = { + meta: { schema: null }, + create(context) {} + }; + `, + output: null, + options: [{ requireSchemaPropertyWhenOptionless: false }], + errors: [{ messageId: 'wrongType', type: 'Literal' }], + }, { code: ` module.exports = { @@ -172,5 +214,29 @@ schema: [] }, output: null, errors: [{ messageId: 'foundOptionsUsage', type: 'Property' }], }, + { + // Empty schema (object), but using rule options, requireSchemaPropertyWhenOptionless = false. + code: ` + module.exports = { + meta: { schema: {} }, + create(context) { const options = context.options; } + }; + `, + output: null, + options: [{ requireSchemaPropertyWhenOptionless: false }], + errors: [{ messageId: 'foundOptionsUsage', type: 'Property' }], + }, + { + // No schema, but using rule options, requireSchemaPropertyWhenOptionless = false. + code: ` + module.exports = { + meta: {}, + create(context) { const options = context.options; } + }; + `, + output: null, + options: [{ requireSchemaPropertyWhenOptionless: false }], + errors: [{ messageId: 'foundOptionsUsage', type: 'ObjectExpression' }], + }, ], });