lib/rules/sort-destructured-props.js (74 lines of code) (raw):
'use strict';
const stringNaturalCompare = require('string-natural-compare');
module.exports = {
meta: {
docs: {
category: 'Stylistic Issues',
description: 'Ensure destructured props are sorted',
githubIssue: null,
recommended: false,
},
messages: {
error:
'Destructured props must be sorted. Example: `const { Component, _a, b, c } = props;`',
},
fixable: 'code',
schema: [],
},
create: (context) => {
const EXCLUDED_TYPES = ['ExperimentalRestProperty', 'RestElement'];
const SOURCE_CODE = context.sourceCode ?? context.getSourceCode();
const getName = (prop) => prop.leftName;
const sort = (props) => {
return [...props].sort((a, b) => {
return stringNaturalCompare(getName(a), getName(b));
});
};
const sameOrder = (a, b) => {
return a.map(getName).toString() === b.map(getName).toString();
};
return {
ObjectPattern: (node) => {
const { properties } = node;
const props = properties
.map((property, index) => {
const { type, key, value } = property;
if (EXCLUDED_TYPES.includes(property.type)) {
return null;
}
let leftName = key.name;
let rightName = key.name !== value.name ? `: ${value.name}` : null;
if (value.type === 'ObjectPattern') {
leftName = key.name;
rightName = `: ${SOURCE_CODE.getText(value)}`;
} else if (value.left) {
const { left, right } = value;
leftName = left.name;
rightName = ` = ${SOURCE_CODE.getText(right)}`;
}
return {
leftName,
rightName,
};
})
.filter((prop) => prop !== null);
const sortedProps = sort(props);
if (!sameOrder(props, sortedProps)) {
context.report({
node,
messageId: 'error',
fix: (fixer) => {
return sortedProps.map((prop, index) => {
let newText = getName(prop);
if (prop.rightName) {
newText = `${newText}${prop.rightName}`;
}
return fixer.replaceText(properties[index], newText);
});
},
});
}
},
};
},
};