Skip to content

Commit

Permalink
feat: add new allowLineSeparatedGroups option to the sort-keys ru…
Browse files Browse the repository at this point in the history
…le (#16138)
  • Loading branch information
snitin315 committed Jul 30, 2022
1 parent 1cdcbca commit c461542
Show file tree
Hide file tree
Showing 3 changed files with 652 additions and 1 deletion.
123 changes: 123 additions & 0 deletions docs/src/rules/sort-keys.md
Expand Up @@ -92,6 +92,7 @@ The 2nd option is an object which has 3 properties.
* `caseSensitive` - if `true`, enforce properties to be in case-sensitive order. Default is `true`.
* `minKeys` - Specifies the minimum number of keys that an object should have in order for the object's unsorted keys to produce an error. Default is `2`, which means by default all objects with unsorted keys will result in lint errors.
* `natural` - if `true`, enforce properties to be in natural order. Default is `false`. Natural Order compares strings containing combination of letters and numbers in the way a human being would sort. It basically sorts numerically, instead of sorting alphabetically. So the number 10 comes after the number 3 in Natural Sorting.
* `allowLineSeparatedGroups` - if `true`, the rule allows to group object keys through line breaks. In other words, a blank line after a property will reset the sorting of keys. Default is `false`.

Example for a list:

Expand Down Expand Up @@ -263,6 +264,128 @@ let obj = {

:::

### allowLineSeparatedGroups

Examples of **incorrect** code for the `{allowLineSeparatedGroups: true}` option:

::: incorrect

```js
/*eslint sort-keys: ["error", "asc", {allowLineSeparatedGroups: true}]*/
/*eslint-env es6*/

let obj1 = {
b: 1,
c () {

},
a: 3
}

let obj2 = {
b: 1,
c: 2,

z () {

},
y: 3
}

let obj3 = {
b: 1,
c: 2,

z () {

},
// comment
y: 3,
}

let obj4 = {
b: 1
// comment before comma
, a: 2
};
```

:::

Examples of **correct** code for the `{allowLineSeparatedGroups: true}` option:

::: correct

```js
/*eslint sort-keys: ["error", "asc", {allowLineSeparatedGroups: true}]*/
/*eslint-env es6*/

let obj = {
e: 1,
f: 2,
g: 3,

a: 4,
b: 5,
c: 6
}

let obj = {
b: 1,

// comment
a: 4,
c: 5,
}

let obj = {
c: 1,
d: 2,

b () {

},
e: 3,
}

let obj = {
c: 1,
d: 2,
// comment

// comment
b() {

},
e: 4
}

let obj = {
b,

[foo + bar]: 1,
a
}

let obj = {
b: 1
// comment before comma

,
a: 2
};

var obj = {
b: 1,

a: 2,
...z,
c: 3
}
```

:::

## When Not To Use It

If you don't want to notify about properties' order, then it's safe to disable this rule.
Expand Down
43 changes: 43 additions & 0 deletions lib/rules/sort-keys.js
Expand Up @@ -105,6 +105,10 @@ module.exports = {
type: "integer",
minimum: 2,
default: 2
},
allowLineSeparatedGroups: {
type: "boolean",
default: false
}
},
additionalProperties: false
Expand All @@ -124,17 +128,21 @@ module.exports = {
const insensitive = options && options.caseSensitive === false;
const natural = options && options.natural;
const minKeys = options && options.minKeys;
const allowLineSeparatedGroups = options && options.allowLineSeparatedGroups || false;
const isValidOrder = isValidOrders[
order + (insensitive ? "I" : "") + (natural ? "N" : "")
];

// The stack to save the previous property's name for each object literals.
let stack = null;
const sourceCode = context.getSourceCode();

return {
ObjectExpression(node) {
stack = {
upper: stack,
prevNode: null,
prevBlankLine: false,
prevName: null,
numKeys: node.properties.length
};
Expand All @@ -159,10 +167,45 @@ module.exports = {
const numKeys = stack.numKeys;
const thisName = getPropertyName(node);

// Get tokens between current node and previous node
const tokens = stack.prevNode && sourceCode
.getTokensBetween(stack.prevNode, node, { includeComments: true });

let isBlankLineBetweenNodes = stack.prevBlankLine;

if (tokens) {

// check blank line between tokens
tokens.forEach((token, index) => {
const previousToken = tokens[index - 1];

if (previousToken && (token.loc.start.line - previousToken.loc.end.line > 1)) {
isBlankLineBetweenNodes = true;
}
});

// check blank line between the current node and the last token
if (!isBlankLineBetweenNodes && (node.loc.start.line - tokens[tokens.length - 1].loc.end.line > 1)) {
isBlankLineBetweenNodes = true;
}

// check blank line between the first token and the previous node
if (!isBlankLineBetweenNodes && (tokens[0].loc.start.line - stack.prevNode.loc.end.line > 1)) {
isBlankLineBetweenNodes = true;
}
}

stack.prevNode = node;

if (thisName !== null) {
stack.prevName = thisName;
}

if (allowLineSeparatedGroups && isBlankLineBetweenNodes) {
stack.prevBlankLine = thisName === null;
return;
}

if (prevName === null || thisName === null || numKeys < minKeys) {
return;
}
Expand Down

0 comments on commit c461542

Please sign in to comment.