eslint-plugin-eslint-config
Version:
ESLint rules for ESLint config files
132 lines (105 loc) • 4.01 kB
JavaScript
;
var _experimentalUtils = require("@typescript-eslint/experimental-utils");
const getPropertyName = node => {
const key = node.key;
switch (key.type) {
case _experimentalUtils.AST_NODE_TYPES.Literal:
return String(key.value);
case _experimentalUtils.AST_NODE_TYPES.TemplateLiteral:
if (key.expressions.length === 0 && key.quasis.length === 1) {
return key.quasis[0].value.cooked;
}
break;
case _experimentalUtils.AST_NODE_TYPES.Identifier:
break;
// no default
}
return null;
};
const trimPluginName = rule => {
return rule.substring(rule.indexOf('/'));
};
const isRuleSorted = (a, b) => {
if (a.includes('/') && b.includes('/')) {
return a <= b;
}
return trimPluginName(a) <= trimPluginName(b);
};
const isPropertyInRulesConfig = node => {
var _node$parent, _node$parent$parent;
return Boolean(((_node$parent = node.parent) === null || _node$parent === void 0 ? void 0 : (_node$parent$parent = _node$parent.parent) === null || _node$parent$parent === void 0 ? void 0 : _node$parent$parent.type) === _experimentalUtils.AST_NODE_TYPES.Property && node.parent.parent.key.type === _experimentalUtils.AST_NODE_TYPES.Identifier && node.parent.parent.key.name === 'rules');
};
module.exports = _experimentalUtils.ESLintUtils.RuleCreator(name => name)({
name: __filename,
meta: {
type: 'problem',
docs: {
description: 'Ensures that rules are sorted in a consistent order',
category: 'Best Practices',
recommended: false
},
fixable: 'code',
messages: {
incorrectOrder: "Rules should be in ascending order, with core rules last. '{{ thisName }}' should be before '{{ prevName }}'."
},
schema: []
},
defaultOptions: [],
create(context) {
// The stack to save the previous property's name for each object literals.
let stack = null;
return {
[_experimentalUtils.AST_NODE_TYPES.ObjectExpression](node) {
stack = {
upper: stack,
prevName: null,
prevNode: null,
numKeys: node.properties.length
};
},
[`${_experimentalUtils.AST_NODE_TYPES.ObjectExpression}:exit`]() {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
stack = stack.upper;
},
[_experimentalUtils.AST_NODE_TYPES.SpreadElement](node) {
var _node$parent2;
if (((_node$parent2 = node.parent) === null || _node$parent2 === void 0 ? void 0 : _node$parent2.type) === _experimentalUtils.AST_NODE_TYPES.ObjectExpression && stack) {
stack.prevName = null;
}
},
[_experimentalUtils.AST_NODE_TYPES.Property](node) {
var _node$parent3, _stack$prevNode;
if (((_node$parent3 = node.parent) === null || _node$parent3 === void 0 ? void 0 : _node$parent3.type) === _experimentalUtils.AST_NODE_TYPES.ObjectPattern || !isPropertyInRulesConfig(node) || !stack) {
return;
}
const prevName = stack.prevName;
const prevNode = (_stack$prevNode = stack.prevNode) !== null && _stack$prevNode !== void 0 ? _stack$prevNode : node;
const thisName = getPropertyName(node);
if (thisName !== null) {
stack.prevName = thisName;
stack.prevNode = node;
}
if (prevName === null || thisName === null) {
return;
}
if (!isRuleSorted(prevName, thisName)) {
context.report({
messageId: 'incorrectOrder',
data: {
thisName,
prevName
},
node,
loc: node.key.loc,
fix(fixer) {
const sourceCode = context.getSourceCode();
const thisText = sourceCode.getText(node);
const prevText = sourceCode.getText(prevNode);
return [fixer.replaceText(node, prevText), fixer.replaceText(prevNode, thisText)];
}
});
}
}
};
}
});