From dd7302b905593ce0045dab4aa246f46d6072aedc Mon Sep 17 00:00:00 2001 From: golopot Date: Fri, 12 Apr 2019 21:03:00 +0800 Subject: [PATCH 1/3] New: meta-property-ordering (fixes #62) --- .eslintrc.yml | 1 + README.md | 1 + docs/rules/meta-property-ordering.md | 51 +++++++++ lib/rules/meta-property-ordering.js | 78 +++++++++++++ tests/lib/rules/meta-property-ordering.js | 133 ++++++++++++++++++++++ 5 files changed, 264 insertions(+) create mode 100644 docs/rules/meta-property-ordering.md create mode 100644 lib/rules/meta-property-ordering.js create mode 100644 tests/lib/rules/meta-property-ordering.js diff --git a/.eslintrc.yml b/.eslintrc.yml index ce7ec1da..92759fea 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -8,6 +8,7 @@ extends: root: true rules: require-jsdoc: error + self/meta-property-ordering: off self/require-meta-docs-url: off self/report-message-format: - error diff --git a/README.md b/README.md index 865b227d..a077de2a 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ Name | ✔️ | 🛠 | Description ----- | ----- | ----- | ----- [consistent-output](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/consistent-output.md) | | | Enforce consistent use of output assertions in rule tests [fixer-return](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/fixer-return.md) | ✔️ | | Expected fixer function to always return a value. +[meta-property-ordering](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/meta-property-ordering.md) | | 🛠 | Enforces the order of meta properties [no-deprecated-context-methods](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-deprecated-context-methods.md) | | 🛠 | Disallows usage of deprecated methods on rule context objects [no-deprecated-report-api](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-deprecated-report-api.md) | ✔️ | 🛠 | disallow use of the deprecated context.report() API [no-identical-tests](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-identical-tests.md) | ✔️ | 🛠 | disallow identical tests diff --git a/docs/rules/meta-property-ordering.md b/docs/rules/meta-property-ordering.md new file mode 100644 index 00000000..d10b1b9e --- /dev/null +++ b/docs/rules/meta-property-ordering.md @@ -0,0 +1,51 @@ +# enforce ordering of meta properties in rule source (meta-property-ordering) + +(fixable) The `--fix` option on the [command line](../user-guide/command-line-interface#fix) automatically fixes problems reported by this rule. + +This rule enforces that the properties of rule meta are arranged in a consistent order. + +## Rule Details + +### Options + +This rule has an array option: + +* `['type', 'docs', 'fixable', 'schema', 'messages', 'deprecated', 'replacedBy']` (default): The properties of meta should be placed in a consistent order. + +Examples of **incorrect** code for this rule: + +```js + +/* eslint eslint-plugin/meta-property-ordering: ["error", + ["type", "docs", "fixable", "schema", "messages"] +] */ + +// invalid; wrong order +{ + fixable: false, + type: "problem", + docs: "", +} + +``` + +Examples of **correct** code for this rule: + +```js +/* eslint eslint-plugin/test-case-property-ordering: ["error", + ["type", "docs", "fixable", "schema", "messages"] +] */ + +// valid; +{ + type: "bar", + docs: "foo", + fooooooooo: "foo", + fixable: false, +} + +``` + +## When Not To Use It + +If don't want to enforce ordering of properies in meta, you can turn off this rule. diff --git a/lib/rules/meta-property-ordering.js b/lib/rules/meta-property-ordering.js new file mode 100644 index 00000000..02b79b6a --- /dev/null +++ b/lib/rules/meta-property-ordering.js @@ -0,0 +1,78 @@ +/** + * @fileoverview Enforces the order of meta properties + */ + +'use strict'; + +const { getKeyName, getRuleInfo } = require('../utils'); + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +module.exports = { + meta: { + docs: { + description: 'Enforces the order of meta properties', + category: 'Rules', + recommended: false, + }, + type: 'suggestion', + fixable: 'code', + schema: [{ + type: 'array', + elements: { type: 'string' }, + }], + }, + + create (context) { + const sourceCode = context.getSourceCode(); + const info = getRuleInfo(sourceCode.ast); + + const message = 'The meta properties should be placed in a consistent order: [{{order}}].'; + const order = context.options[0] || ['type', 'docs', 'fixable', 'schema', 'messages']; + + const orderMap = new Map(order.map((name, i) => [name, i])); + + return { + Program () { + if ( + !info || + !info.meta || + info.meta.properties.length < 2 + ) { + return; + } + + const propsActual = info.meta.properties + .filter(prop => orderMap.has(getKeyName(prop))); + + for (let i = 1, j = propsActual.length; i < j; i += 1) { + const last = propsActual[i - 1]; + const curr = propsActual[i]; + if (order.indexOf(getKeyName(last)) > order.indexOf(getKeyName(curr))) { + const propsExpected = propsActual + .slice() + .sort((a, b) => orderMap.get(getKeyName(a)) - orderMap.get(getKeyName(b))); + + context.report({ + node: curr, + message, + data: { + order: propsExpected.map(getKeyName).join(', '), + }, + fix (fixer) { + return propsActual.map((prop, k) => { + return fixer.replaceText( + prop, + sourceCode.getText(propsExpected[k]) + ); + }); + }, + }); + } + } + }, + }; + }, +}; diff --git a/tests/lib/rules/meta-property-ordering.js b/tests/lib/rules/meta-property-ordering.js new file mode 100644 index 00000000..2ca0508a --- /dev/null +++ b/tests/lib/rules/meta-property-ordering.js @@ -0,0 +1,133 @@ +/** + * @fileoverview Enforces the order of meta properties + */ + +'use strict'; + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +const rule = require('../../../lib/rules/meta-property-ordering'); +const RuleTester = require('eslint').RuleTester; + +// ------------------------------------------------------------------------------ +// Tests +// ------------------------------------------------------------------------------ + +/** + * @param {string[]} order + * @returns {string} + */ +function getMessage (order) { + return `The meta properties should be placed in a consistent order: [${order.join(', ')}].`; +} + +const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } }); +ruleTester.run('test-case-property-ordering', rule, { + valid: [ + ` + module.exports = { + meta: {type, docs, fixable, schema, messages}, + create() {}, + };`, + + ` + module.exports = { + meta: {docs, schema, messages}, + create() {}, + };`, + + ` + module.exports = { + meta: {docs, foo, bar, messages}, + create() {}, + };`, + + ` + module.exports = { + meta: { + type: 'problem', + docs: {}, + fixable: 'code', + schema: [], + messages: {} + }, + create() {}, + };`, + { + code: ` + module.exports = { + meta: {messages, schema, docs}, + create() {}, + };`, + options: [['schema', 'docs']], + }, + ` + module.exports = { + meta: {}, + create() {}, + };`, + ], + + invalid: [ + { + code: ` + module.exports = { + meta: { + docs, + fixable, + type: 'problem', + }, + create() {}, + };`, + + output: ` + module.exports = { + meta: { + type: 'problem', + docs, + fixable, + }, + create() {}, + };`, + errors: [{ message: getMessage(['type', 'docs', 'fixable']) }], + }, + { + code: ` + module.exports = { + meta: {schema, fixable, type, docs}, + create() {}, + };`, + + output: ` + module.exports = { + meta: {type, docs, fixable, schema}, + create() {}, + };`, + errors: [ + { message: getMessage(['type', 'docs', 'fixable', 'schema']) }, + { message: getMessage(['type', 'docs', 'fixable', 'schema']) }, + ], + }, + + { + code: ` + module.exports = { + meta: {fixable, fooooooooo, doc, type}, + create() {}, + };`, + + output: ` + module.exports = { + meta: {type, fooooooooo, doc, fixable}, + create() {}, + };`, + options: [['type', 'doc', 'fixable']], + errors: [ + { message: getMessage(['type', 'doc', 'fixable']) }, + { message: getMessage(['type', 'doc', 'fixable']) }, + ], + }, + ], +}); From 41a83345b70eeff0e3cf317ed1fc142a7b38ea92 Mon Sep 17 00:00:00 2001 From: golopot Date: Thu, 18 Apr 2019 13:50:50 +0800 Subject: [PATCH 2/3] Update: place extra properties at the end --- lib/rules/meta-property-ordering.js | 60 +++++++++++++---------- tests/lib/rules/meta-property-ordering.js | 6 +-- 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/lib/rules/meta-property-ordering.js b/lib/rules/meta-property-ordering.js index 02b79b6a..3d563225 100644 --- a/lib/rules/meta-property-ordering.js +++ b/lib/rules/meta-property-ordering.js @@ -44,33 +44,43 @@ module.exports = { return; } - const propsActual = info.meta.properties - .filter(prop => orderMap.has(getKeyName(prop))); + const props = info.meta.properties; - for (let i = 1, j = propsActual.length; i < j; i += 1) { - const last = propsActual[i - 1]; - const curr = propsActual[i]; - if (order.indexOf(getKeyName(last)) > order.indexOf(getKeyName(curr))) { - const propsExpected = propsActual - .slice() - .sort((a, b) => orderMap.get(getKeyName(a)) - orderMap.get(getKeyName(b))); + let last; - context.report({ - node: curr, - message, - data: { - order: propsExpected.map(getKeyName).join(', '), - }, - fix (fixer) { - return propsActual.map((prop, k) => { - return fixer.replaceText( - prop, - sourceCode.getText(propsExpected[k]) - ); - }); - }, - }); - } + const violatingProps = props.filter(prop => { + const curr = orderMap.has(getKeyName(prop)) + ? orderMap.get(getKeyName(prop)) + : Infinity; + return last > (last = curr); + }); + + if (violatingProps.length === 0) { + return; + } + + const knownProps = props + .filter(prop => orderMap.has(getKeyName(prop))) + .sort((a, b) => orderMap.get(getKeyName(a)) - orderMap.get(getKeyName(b))); + const unknownProps = props.filter(prop => !orderMap.has(getKeyName(prop))); + + for (const violatingProp of violatingProps) { + context.report({ + node: violatingProp, + message, + data: { + order: knownProps.map(getKeyName).join(', '), + }, + fix (fixer) { + const expectedProps = [...knownProps, ...unknownProps]; + return props.map((prop, k) => { + return fixer.replaceText( + prop, + sourceCode.getText(expectedProps[k]) + ); + }); + }, + }); } }, }; diff --git a/tests/lib/rules/meta-property-ordering.js b/tests/lib/rules/meta-property-ordering.js index 2ca0508a..c98cea63 100644 --- a/tests/lib/rules/meta-property-ordering.js +++ b/tests/lib/rules/meta-property-ordering.js @@ -40,7 +40,7 @@ ruleTester.run('test-case-property-ordering', rule, { ` module.exports = { - meta: {docs, foo, bar, messages}, + meta: {docs, messages, foo, bar}, create() {}, };`, @@ -58,7 +58,7 @@ ruleTester.run('test-case-property-ordering', rule, { { code: ` module.exports = { - meta: {messages, schema, docs}, + meta: {schema, docs, fixable}, create() {}, };`, options: [['schema', 'docs']], @@ -120,7 +120,7 @@ ruleTester.run('test-case-property-ordering', rule, { output: ` module.exports = { - meta: {type, fooooooooo, doc, fixable}, + meta: {type, doc, fixable, fooooooooo}, create() {}, };`, options: [['type', 'doc', 'fixable']], From 05ef0d1f0c624362bd20f44a158cfec98c225b53 Mon Sep 17 00:00:00 2001 From: golopot Date: Thu, 18 Apr 2019 14:10:53 +0800 Subject: [PATCH 3/3] Docs: update doc as suggested --- docs/rules/meta-property-ordering.md | 43 +++++++++++++++++++--------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/docs/rules/meta-property-ordering.md b/docs/rules/meta-property-ordering.md index d10b1b9e..29ce4c98 100644 --- a/docs/rules/meta-property-ordering.md +++ b/docs/rules/meta-property-ordering.md @@ -2,7 +2,7 @@ (fixable) The `--fix` option on the [command line](../user-guide/command-line-interface#fix) automatically fixes problems reported by this rule. -This rule enforces that the properties of rule meta are arranged in a consistent order. +This rule enforces that meta properties of a rule are placed in a consistent order. ## Rule Details @@ -10,7 +10,7 @@ This rule enforces that the properties of rule meta are arranged in a consistent This rule has an array option: -* `['type', 'docs', 'fixable', 'schema', 'messages', 'deprecated', 'replacedBy']` (default): The properties of meta should be placed in a consistent order. +* `['type', 'docs', 'fixable', 'schema', 'messages', 'deprecated', 'replacedBy']` (default): The order that the properties of `meta` should be placed in. Examples of **incorrect** code for this rule: @@ -20,13 +20,26 @@ Examples of **incorrect** code for this rule: ["type", "docs", "fixable", "schema", "messages"] ] */ -// invalid; wrong order -{ - fixable: false, - type: "problem", - docs: "", +// invalid; wrong order. +module.exports = { + meta: { + docs: "", + type: "problem", + fixable: "code", + }, + create() {}, } +// invalid; extra properties must be placed afterwards. +module.exports = { + meta: { + type: "problem", + fooooooooo: "foo", + docs: "", + fixable: "code", + }, + create() {}, +} ``` Examples of **correct** code for this rule: @@ -37,15 +50,17 @@ Examples of **correct** code for this rule: ] */ // valid; -{ - type: "bar", - docs: "foo", - fooooooooo: "foo", - fixable: false, +module.exports = { + meta: { + type: "bar", + docs: "foo", + messages: ["zoo"], + fooooooooo: "foo", + }, + create() {}, } - ``` ## When Not To Use It -If don't want to enforce ordering of properies in meta, you can turn off this rule. +If don't want to enforce ordering of meta properties, you can turn off this rule.