123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- /**
- * @fileoverview Rule to require object keys to be sorted
- * @author Toru Nagashima
- */
- "use strict";
- //------------------------------------------------------------------------------
- // Requirements
- //------------------------------------------------------------------------------
- const astUtils = require("./utils/ast-utils"),
- naturalCompare = require("natural-compare");
- //------------------------------------------------------------------------------
- // Helpers
- //------------------------------------------------------------------------------
- /**
- * Gets the property name of the given `Property` node.
- *
- * - If the property's key is an `Identifier` node, this returns the key's name
- * whether it's a computed property or not.
- * - If the property has a static name, this returns the static name.
- * - Otherwise, this returns null.
- * @param {ASTNode} node The `Property` node to get.
- * @returns {string|null} The property name or null.
- * @private
- */
- function getPropertyName(node) {
- const staticName = astUtils.getStaticPropertyName(node);
- if (staticName !== null) {
- return staticName;
- }
- return node.key.name || null;
- }
- /**
- * Functions which check that the given 2 names are in specific order.
- *
- * Postfix `I` is meant insensitive.
- * Postfix `N` is meant natural.
- * @private
- */
- const isValidOrders = {
- asc(a, b) {
- return a <= b;
- },
- ascI(a, b) {
- return a.toLowerCase() <= b.toLowerCase();
- },
- ascN(a, b) {
- return naturalCompare(a, b) <= 0;
- },
- ascIN(a, b) {
- return naturalCompare(a.toLowerCase(), b.toLowerCase()) <= 0;
- },
- desc(a, b) {
- return isValidOrders.asc(b, a);
- },
- descI(a, b) {
- return isValidOrders.ascI(b, a);
- },
- descN(a, b) {
- return isValidOrders.ascN(b, a);
- },
- descIN(a, b) {
- return isValidOrders.ascIN(b, a);
- }
- };
- //------------------------------------------------------------------------------
- // Rule Definition
- //------------------------------------------------------------------------------
- module.exports = {
- meta: {
- type: "suggestion",
- docs: {
- description: "require object keys to be sorted",
- category: "Stylistic Issues",
- recommended: false,
- url: "https://eslint.org/docs/rules/sort-keys"
- },
- schema: [
- {
- enum: ["asc", "desc"]
- },
- {
- type: "object",
- properties: {
- caseSensitive: {
- type: "boolean",
- default: true
- },
- natural: {
- type: "boolean",
- default: false
- },
- minKeys: {
- type: "integer",
- minimum: 2,
- default: 2
- }
- },
- additionalProperties: false
- }
- ],
- messages: {
- sortKeys: "Expected object keys to be in {{natural}}{{insensitive}}{{order}}ending order. '{{thisName}}' should be before '{{prevName}}'."
- }
- },
- create(context) {
- // Parse options.
- const order = context.options[0] || "asc";
- const options = context.options[1];
- const insensitive = options && options.caseSensitive === false;
- const natural = options && options.natural;
- const minKeys = options && options.minKeys;
- const isValidOrder = isValidOrders[
- order + (insensitive ? "I" : "") + (natural ? "N" : "")
- ];
- // The stack to save the previous property's name for each object literals.
- let stack = null;
- return {
- ObjectExpression(node) {
- stack = {
- upper: stack,
- prevName: null,
- numKeys: node.properties.length
- };
- },
- "ObjectExpression:exit"() {
- stack = stack.upper;
- },
- SpreadElement(node) {
- if (node.parent.type === "ObjectExpression") {
- stack.prevName = null;
- }
- },
- Property(node) {
- if (node.parent.type === "ObjectPattern") {
- return;
- }
- const prevName = stack.prevName;
- const numKeys = stack.numKeys;
- const thisName = getPropertyName(node);
- if (thisName !== null) {
- stack.prevName = thisName;
- }
- if (prevName === null || thisName === null || numKeys < minKeys) {
- return;
- }
- if (!isValidOrder(prevName, thisName)) {
- context.report({
- node,
- loc: node.key.loc,
- messageId: "sortKeys",
- data: {
- thisName,
- prevName,
- order,
- insensitive: insensitive ? "insensitive " : "",
- natural: natural ? "natural " : ""
- }
- });
- }
- }
- };
- }
- };
|