Skip to content

Commit

Permalink
feat: add new allowLineSeparatedGroups option
Browse files Browse the repository at this point in the history
Adapted from eslint/eslint#16138
  • Loading branch information
forivall committed Oct 24, 2022
1 parent 19b4a6f commit ce76d95
Show file tree
Hide file tree
Showing 3 changed files with 272 additions and 8 deletions.
196 changes: 196 additions & 0 deletions __tests__/lib/rules/sort-keys.js
Expand Up @@ -172,6 +172,202 @@ const test = {
// desc, natural, insensitive, minKeys should ignore unsorted keys when number of keys is less than minKeys
{ code: 'var obj = {a:1, _:2, b:3}', options: ['desc', { natural: true, caseSensitive: false, minKeys: 4 }] },

// allowLineSeparatedGroups option
{
code: `
var obj = {
e: 1,
f: 2,
g: 3,
a: 4,
b: 5,
c: 6
}
`,
options: ['asc', { allowLineSeparatedGroups: true }],
},
{
code: `
var obj = {
b: 1,
// comment
a: 2,
c: 3
}
`,
options: ['asc', { allowLineSeparatedGroups: true }],
},
{
code: `
var obj = {
b: 1
,
// comment
a: 2,
c: 3
}
`,
options: ['asc', { allowLineSeparatedGroups: true }],
},
{
code: `
var obj = {
c: 1,
d: 2,
b() {
},
e: 4
}
`,
options: ['asc', { allowLineSeparatedGroups: true }],
parserOptions: { ecmaVersion: 6 },
},
{
code: `
var obj = {
c: 1,
d: 2,
// comment
// comment
b() {
},
e: 4
}
`,
options: ['asc', { allowLineSeparatedGroups: true }],
parserOptions: { ecmaVersion: 6 },
},
{
code: `
var obj = {
b,
[a+b]: 1,
a
}
`,
options: ['asc', { allowLineSeparatedGroups: true }],
parserOptions: { ecmaVersion: 6 },
},
{
code: `
var obj = {
c: 1,
d: 2,
a() {
},
// abce
f: 3,
/*
*/
[a+b]: 1,
cc: 1,
e: 2
}
`,
options: ['asc', { allowLineSeparatedGroups: true }],
parserOptions: { ecmaVersion: 6 },
},
{
code: `
var obj = {
b: "/*",
a: "*/",
}
`,
options: ['asc', { allowLineSeparatedGroups: true }],
},
{
code: `
var obj = {
b,
/*
*/ //
a
}
`,
options: ['asc', { allowLineSeparatedGroups: true }],
parserOptions: { ecmaVersion: 6 },
},
{
code: `
var obj = {
b,
/*
*/ //
a
}
`,
options: ['asc', { allowLineSeparatedGroups: true }],
parserOptions: { ecmaVersion: 6 },
},
{
code: `
var obj = {
b: 1
,a: 2
};
`,
options: ['asc', { allowLineSeparatedGroups: true }],
parserOptions: { ecmaVersion: 6 },
},
{
code: `
var obj = {
b: 1
// comment before comma
,
a: 2
};
`,
options: ['asc', { allowLineSeparatedGroups: true }],
parserOptions: { ecmaVersion: 6 },
},
{
code: `
var obj = {
b,
a,
...z,
c
}
`,
options: ['asc', { allowLineSeparatedGroups: true }],
parserOptions: { ecmaVersion: 2018 },
},
{
code: `
var obj = {
b,
[foo()]: [
],
a
}
`,
options: ['asc', { allowLineSeparatedGroups: true }],
parserOptions: { ecmaVersion: 2018 },
},
{ code: 'var obj = {a:1, _:2, b:3}', options: ['desc', { natural: true, caseSensitive: false, minKeys: 4 }] },

// ALL_CAPS first
{ code: 'var obj = {CA: 0, b_:1, Ca:3, ca:2}', options: ['asc', { caseSensitive: false, allCaps: 'first' }] },
{ code: 'var obj = {CA: 0, b_:1, ca:3, Ca:2}', options: ['asc', { caseSensitive: false, allCaps: 'first' }] },
Expand Down
81 changes: 74 additions & 7 deletions lib/rules/sort-keys.js
Expand Up @@ -30,9 +30,22 @@ const astUtils = require('./utils/ast-utils'),
* @property {boolean} [caseSensitive] Use case sensitive sorting
* @property {boolean} [natural] Use natural sorting
* @property {number} [minKeys] Minimum Keys
* @property {boolean} [allowLineSeparatedGroups] Allow Line Separated Groups
* @property {'first' | 'last' | 'ignore'} [allCaps] All Caps option
* @property {Override[]} [overrides] Overrides options
*/
/** @typedef {(a: string, b: string) => boolean | null} IsValidOrder */
/**
* @typedef Stack
* @property {Stack | null} upper
* @property {import('estree').Property & import('eslint').Rule.NodeParentExtension | null} prevNode
* @property {boolean} prevBlankLine
* @property {string | null} prevName
* @property {number} numKeys
* @property {string} parentName
* @property {Override} override
* @property {IsValidOrder} isValidOrder
*/

/**
* Combinatorial combination fn
Expand Down Expand Up @@ -74,6 +87,43 @@ function getPropertyName(node) {
return (key && key.name) || null
}

/**
* test blank lines
* @param {import('eslint').Rule.RuleContext} context context
* @param {import('eslint').Rule.Node} node node
* @param {import('eslint').Rule.Node} prevNode prevNode
* @returns {boolean} if there is a blank line between the prevNode and current node
*/
function hasBlankLineBetweenNodes(context, node, prevNode) {
const sourceCode = context.getSourceCode()

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

if (!tokens) {
return false
}
let previousToken

// check blank line between tokens
for (const token of tokens) {
if (previousToken && token.loc.start.line - previousToken.loc.end.line > 1) {
return true
}
previousToken = token
}

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

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

/**
* Function to check that 2 names are proper all caps order
* @param {string} a first value
Expand All @@ -93,8 +143,7 @@ function isValidAllCapsTest(a, b) {
*
* Postfix `I` is meant insensitive.
* Postfix `N` is meant natural.
* Postfix `C` is all caps/constants first
* @type {Record<`${'asc'|'desc'}${''|'I'}${''|'N'}`, (a: string, b: string) => boolean>}
* @type {Record<`${'asc'|'desc'}${''|'I'}${''|'N'}`, IsValidOrder>}
* @private
*/
// @ts-ignore
Expand Down Expand Up @@ -214,6 +263,10 @@ module.exports = {
minimum: 2,
default: 2,
},
allowLineSeparatedGroups: {
type: 'boolean',
default: false,
},
allCaps: {
enum: ['first', 'last', 'ignore'],
default: 'ignore',
Expand Down Expand Up @@ -261,6 +314,7 @@ module.exports = {
const insensitive = options && options.caseSensitive === false
const natural = options && options.natural
const minKeys = (options && options.minKeys) || 2
const allowLineSeparatedGroups = (options && options.allowLineSeparatedGroups) || false
const allCaps = (options && options.allCaps) || 'ignore'
/** @type {Override[]} */
const overrides = (options && options.overrides) || []
Expand Down Expand Up @@ -288,11 +342,15 @@ module.exports = {
})
})
const isValidOrderBase = isValidOrders[`${order}${insensitive ? 'I' : ''}${natural ? 'N' : ''}`]
/** @type {IsValidOrder} */
const isValidOrderAllCaps =
// eslint-disable-next-line no-nested-ternary
allCaps === 'ignore' ? () => null : allCaps === 'last' ? (a, b) => isValidAllCapsTest(b, a) : isValidAllCapsTest

// The stack to save the previous property's name for each object literals.
/**
* The stack to save the previous property's name for each object literals.
* @type {Stack | null}
*/
let stack = null

/**
Expand All @@ -302,8 +360,8 @@ module.exports = {
*/
function SpreadElement(node) {
if (node.parent.type === 'ObjectExpression') {
stack.prevName = null
stack.prevNode = null
stack.prevName = null
}
}

Expand Down Expand Up @@ -335,8 +393,9 @@ module.exports = {

stack = {
upper: stack,
prevName: null,
prevNode: null,
prevBlankLine: false,
prevName: null,
numKeys: node.properties.length,
parentName,
override,
Expand All @@ -355,14 +414,22 @@ module.exports = {
return
}

const prevName = stack.prevName
const prevNode = stack.prevNode
const prevName = stack.prevName
const numKeys = stack.numKeys
const thisName = getPropertyName(node)
const isBlankLineBetweenNodes =
stack.prevBlankLine || (allowLineSeparatedGroups && hasBlankLineBetweenNodes(context, node, prevNode))

stack.prevNode = node

if (thisName !== null) {
stack.prevName = thisName
stack.prevNode = node || prevNode
}

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

if (prevName === null || thisName === null || numKeys < minKeys) {
Expand Down
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -13,7 +13,8 @@
],
"author": "Emily M Klassen <forivall@gmail.com>",
"contributors": [
"Leonid Buneev <buneev.leonid@gmail.com>"
"Leonid Buneev <buneev.leonid@gmail.com>",
"ESLint Contributors <https://github.com/eslint/eslint/blob/main/lib/rules/sort-keys.js>"
],
"main": "lib/index.js",
"scripts": {
Expand Down

0 comments on commit ce76d95

Please sign in to comment.