Skip to content

Commit aebf1cf

Browse files
golopotnot-an-aardvark
authored andcommitted
New: meta-property-ordering (fixes #62) (#80)
1 parent 9bab974 commit aebf1cf

File tree

5 files changed

+289
-0
lines changed

5 files changed

+289
-0
lines changed

.eslintrc.yml

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ extends:
88
root: true
99
rules:
1010
require-jsdoc: error
11+
self/meta-property-ordering: off
1112
self/require-meta-docs-url: off
1213
self/report-message-format:
1314
- error

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ Name | ✔️ | 🛠 | Description
5151
----- | ----- | ----- | -----
5252
[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
5353
[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.
54+
[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
5455
[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
5556
[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
5657
[no-identical-tests](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-identical-tests.md) | ✔️ | 🛠 | disallow identical tests

docs/rules/meta-property-ordering.md

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# enforce ordering of meta properties in rule source (meta-property-ordering)
2+
3+
(fixable) The `--fix` option on the [command line](../user-guide/command-line-interface#fix) automatically fixes problems reported by this rule.
4+
5+
This rule enforces that meta properties of a rule are placed in a consistent order.
6+
7+
## Rule Details
8+
9+
### Options
10+
11+
This rule has an array option:
12+
13+
* `['type', 'docs', 'fixable', 'schema', 'messages', 'deprecated', 'replacedBy']` (default): The order that the properties of `meta` should be placed in.
14+
15+
Examples of **incorrect** code for this rule:
16+
17+
```js
18+
19+
/* eslint eslint-plugin/meta-property-ordering: ["error",
20+
["type", "docs", "fixable", "schema", "messages"]
21+
] */
22+
23+
// invalid; wrong order.
24+
module.exports = {
25+
meta: {
26+
docs: "",
27+
type: "problem",
28+
fixable: "code",
29+
},
30+
create() {},
31+
}
32+
33+
// invalid; extra properties must be placed afterwards.
34+
module.exports = {
35+
meta: {
36+
type: "problem",
37+
fooooooooo: "foo",
38+
docs: "",
39+
fixable: "code",
40+
},
41+
create() {},
42+
}
43+
```
44+
45+
Examples of **correct** code for this rule:
46+
47+
```js
48+
/* eslint eslint-plugin/test-case-property-ordering: ["error",
49+
["type", "docs", "fixable", "schema", "messages"]
50+
] */
51+
52+
// valid;
53+
module.exports = {
54+
meta: {
55+
type: "bar",
56+
docs: "foo",
57+
messages: ["zoo"],
58+
fooooooooo: "foo",
59+
},
60+
create() {},
61+
}
62+
```
63+
64+
## When Not To Use It
65+
66+
If don't want to enforce ordering of meta properties, you can turn off this rule.

lib/rules/meta-property-ordering.js

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* @fileoverview Enforces the order of meta properties
3+
*/
4+
5+
'use strict';
6+
7+
const { getKeyName, getRuleInfo } = require('../utils');
8+
9+
// ------------------------------------------------------------------------------
10+
// Rule Definition
11+
// ------------------------------------------------------------------------------
12+
13+
module.exports = {
14+
meta: {
15+
docs: {
16+
description: 'Enforces the order of meta properties',
17+
category: 'Rules',
18+
recommended: false,
19+
},
20+
type: 'suggestion',
21+
fixable: 'code',
22+
schema: [{
23+
type: 'array',
24+
elements: { type: 'string' },
25+
}],
26+
},
27+
28+
create (context) {
29+
const sourceCode = context.getSourceCode();
30+
const info = getRuleInfo(sourceCode.ast);
31+
32+
const message = 'The meta properties should be placed in a consistent order: [{{order}}].';
33+
const order = context.options[0] || ['type', 'docs', 'fixable', 'schema', 'messages'];
34+
35+
const orderMap = new Map(order.map((name, i) => [name, i]));
36+
37+
return {
38+
Program () {
39+
if (
40+
!info ||
41+
!info.meta ||
42+
info.meta.properties.length < 2
43+
) {
44+
return;
45+
}
46+
47+
const props = info.meta.properties;
48+
49+
let last;
50+
51+
const violatingProps = props.filter(prop => {
52+
const curr = orderMap.has(getKeyName(prop))
53+
? orderMap.get(getKeyName(prop))
54+
: Infinity;
55+
return last > (last = curr);
56+
});
57+
58+
if (violatingProps.length === 0) {
59+
return;
60+
}
61+
62+
const knownProps = props
63+
.filter(prop => orderMap.has(getKeyName(prop)))
64+
.sort((a, b) => orderMap.get(getKeyName(a)) - orderMap.get(getKeyName(b)));
65+
const unknownProps = props.filter(prop => !orderMap.has(getKeyName(prop)));
66+
67+
for (const violatingProp of violatingProps) {
68+
context.report({
69+
node: violatingProp,
70+
message,
71+
data: {
72+
order: knownProps.map(getKeyName).join(', '),
73+
},
74+
fix (fixer) {
75+
const expectedProps = [...knownProps, ...unknownProps];
76+
return props.map((prop, k) => {
77+
return fixer.replaceText(
78+
prop,
79+
sourceCode.getText(expectedProps[k])
80+
);
81+
});
82+
},
83+
});
84+
}
85+
},
86+
};
87+
},
88+
};
+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/**
2+
* @fileoverview Enforces the order of meta properties
3+
*/
4+
5+
'use strict';
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const rule = require('../../../lib/rules/meta-property-ordering');
12+
const RuleTester = require('eslint').RuleTester;
13+
14+
// ------------------------------------------------------------------------------
15+
// Tests
16+
// ------------------------------------------------------------------------------
17+
18+
/**
19+
* @param {string[]} order
20+
* @returns {string}
21+
*/
22+
function getMessage (order) {
23+
return `The meta properties should be placed in a consistent order: [${order.join(', ')}].`;
24+
}
25+
26+
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
27+
ruleTester.run('test-case-property-ordering', rule, {
28+
valid: [
29+
`
30+
module.exports = {
31+
meta: {type, docs, fixable, schema, messages},
32+
create() {},
33+
};`,
34+
35+
`
36+
module.exports = {
37+
meta: {docs, schema, messages},
38+
create() {},
39+
};`,
40+
41+
`
42+
module.exports = {
43+
meta: {docs, messages, foo, bar},
44+
create() {},
45+
};`,
46+
47+
`
48+
module.exports = {
49+
meta: {
50+
type: 'problem',
51+
docs: {},
52+
fixable: 'code',
53+
schema: [],
54+
messages: {}
55+
},
56+
create() {},
57+
};`,
58+
{
59+
code: `
60+
module.exports = {
61+
meta: {schema, docs, fixable},
62+
create() {},
63+
};`,
64+
options: [['schema', 'docs']],
65+
},
66+
`
67+
module.exports = {
68+
meta: {},
69+
create() {},
70+
};`,
71+
],
72+
73+
invalid: [
74+
{
75+
code: `
76+
module.exports = {
77+
meta: {
78+
docs,
79+
fixable,
80+
type: 'problem',
81+
},
82+
create() {},
83+
};`,
84+
85+
output: `
86+
module.exports = {
87+
meta: {
88+
type: 'problem',
89+
docs,
90+
fixable,
91+
},
92+
create() {},
93+
};`,
94+
errors: [{ message: getMessage(['type', 'docs', 'fixable']) }],
95+
},
96+
{
97+
code: `
98+
module.exports = {
99+
meta: {schema, fixable, type, docs},
100+
create() {},
101+
};`,
102+
103+
output: `
104+
module.exports = {
105+
meta: {type, docs, fixable, schema},
106+
create() {},
107+
};`,
108+
errors: [
109+
{ message: getMessage(['type', 'docs', 'fixable', 'schema']) },
110+
{ message: getMessage(['type', 'docs', 'fixable', 'schema']) },
111+
],
112+
},
113+
114+
{
115+
code: `
116+
module.exports = {
117+
meta: {fixable, fooooooooo, doc, type},
118+
create() {},
119+
};`,
120+
121+
output: `
122+
module.exports = {
123+
meta: {type, doc, fixable, fooooooooo},
124+
create() {},
125+
};`,
126+
options: [['type', 'doc', 'fixable']],
127+
errors: [
128+
{ message: getMessage(['type', 'doc', 'fixable']) },
129+
{ message: getMessage(['type', 'doc', 'fixable']) },
130+
],
131+
},
132+
],
133+
});

0 commit comments

Comments
 (0)