]> git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/sort-keys.js
import 8.3.0 source
[pve-eslint.git] / eslint / lib / rules / sort-keys.js
1 /**
2 * @fileoverview Rule to require object keys to be sorted
3 * @author Toru Nagashima
4 */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Requirements
10 //------------------------------------------------------------------------------
11
12 const astUtils = require("./utils/ast-utils"),
13 naturalCompare = require("natural-compare");
14
15 //------------------------------------------------------------------------------
16 // Helpers
17 //------------------------------------------------------------------------------
18
19 /**
20 * Gets the property name of the given `Property` node.
21 *
22 * - If the property's key is an `Identifier` node, this returns the key's name
23 * whether it's a computed property or not.
24 * - If the property has a static name, this returns the static name.
25 * - Otherwise, this returns null.
26 * @param {ASTNode} node The `Property` node to get.
27 * @returns {string|null} The property name or null.
28 * @private
29 */
30 function getPropertyName(node) {
31 const staticName = astUtils.getStaticPropertyName(node);
32
33 if (staticName !== null) {
34 return staticName;
35 }
36
37 return node.key.name || null;
38 }
39
40 /**
41 * Functions which check that the given 2 names are in specific order.
42 *
43 * Postfix `I` is meant insensitive.
44 * Postfix `N` is meant natural.
45 * @private
46 */
47 const isValidOrders = {
48 asc(a, b) {
49 return a <= b;
50 },
51 ascI(a, b) {
52 return a.toLowerCase() <= b.toLowerCase();
53 },
54 ascN(a, b) {
55 return naturalCompare(a, b) <= 0;
56 },
57 ascIN(a, b) {
58 return naturalCompare(a.toLowerCase(), b.toLowerCase()) <= 0;
59 },
60 desc(a, b) {
61 return isValidOrders.asc(b, a);
62 },
63 descI(a, b) {
64 return isValidOrders.ascI(b, a);
65 },
66 descN(a, b) {
67 return isValidOrders.ascN(b, a);
68 },
69 descIN(a, b) {
70 return isValidOrders.ascIN(b, a);
71 }
72 };
73
74 //------------------------------------------------------------------------------
75 // Rule Definition
76 //------------------------------------------------------------------------------
77
78 module.exports = {
79 meta: {
80 type: "suggestion",
81
82 docs: {
83 description: "require object keys to be sorted",
84 recommended: false,
85 url: "https://eslint.org/docs/rules/sort-keys"
86 },
87
88 schema: [
89 {
90 enum: ["asc", "desc"]
91 },
92 {
93 type: "object",
94 properties: {
95 caseSensitive: {
96 type: "boolean",
97 default: true
98 },
99 natural: {
100 type: "boolean",
101 default: false
102 },
103 minKeys: {
104 type: "integer",
105 minimum: 2,
106 default: 2
107 }
108 },
109 additionalProperties: false
110 }
111 ],
112
113 messages: {
114 sortKeys: "Expected object keys to be in {{natural}}{{insensitive}}{{order}}ending order. '{{thisName}}' should be before '{{prevName}}'."
115 }
116 },
117
118 create(context) {
119
120 // Parse options.
121 const order = context.options[0] || "asc";
122 const options = context.options[1];
123 const insensitive = options && options.caseSensitive === false;
124 const natural = options && options.natural;
125 const minKeys = options && options.minKeys;
126 const isValidOrder = isValidOrders[
127 order + (insensitive ? "I" : "") + (natural ? "N" : "")
128 ];
129
130 // The stack to save the previous property's name for each object literals.
131 let stack = null;
132
133 return {
134 ObjectExpression(node) {
135 stack = {
136 upper: stack,
137 prevName: null,
138 numKeys: node.properties.length
139 };
140 },
141
142 "ObjectExpression:exit"() {
143 stack = stack.upper;
144 },
145
146 SpreadElement(node) {
147 if (node.parent.type === "ObjectExpression") {
148 stack.prevName = null;
149 }
150 },
151
152 Property(node) {
153 if (node.parent.type === "ObjectPattern") {
154 return;
155 }
156
157 const prevName = stack.prevName;
158 const numKeys = stack.numKeys;
159 const thisName = getPropertyName(node);
160
161 if (thisName !== null) {
162 stack.prevName = thisName;
163 }
164
165 if (prevName === null || thisName === null || numKeys < minKeys) {
166 return;
167 }
168
169 if (!isValidOrder(prevName, thisName)) {
170 context.report({
171 node,
172 loc: node.key.loc,
173 messageId: "sortKeys",
174 data: {
175 thisName,
176 prevName,
177 order,
178 insensitive: insensitive ? "insensitive " : "",
179 natural: natural ? "natural " : ""
180 }
181 });
182 }
183 }
184 };
185 }
186 };