UNPKG

4.59 kBJavaScriptView Raw
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
12const astUtils = require("../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 *
27 * @param {ASTNode} node - The `Property` node to get.
28 * @returns {string|null} The property name or null.
29 * @private
30 */
31function getPropertyName(node) {
32 return astUtils.getStaticPropertyName(node) || node.key.name || null;
33}
34
35/**
36 * Functions which check that the given 2 names are in specific order.
37 *
38 * Postfix `I` is meant insensitive.
39 * Postfix `N` is meant natual.
40 *
41 * @private
42 */
43const isValidOrders = {
44 asc(a, b) {
45 return a <= b;
46 },
47 ascI(a, b) {
48 return a.toLowerCase() <= b.toLowerCase();
49 },
50 ascN(a, b) {
51 return naturalCompare(a, b) <= 0;
52 },
53 ascIN(a, b) {
54 return naturalCompare(a.toLowerCase(), b.toLowerCase()) <= 0;
55 },
56 desc(a, b) {
57 return isValidOrders.asc(b, a);
58 },
59 descI(a, b) {
60 return isValidOrders.ascI(b, a);
61 },
62 descN(a, b) {
63 return isValidOrders.ascN(b, a);
64 },
65 descIN(a, b) {
66 return isValidOrders.ascIN(b, a);
67 }
68};
69
70//------------------------------------------------------------------------------
71// Rule Definition
72//------------------------------------------------------------------------------
73
74module.exports = {
75 meta: {
76 docs: {
77 description: "require object keys to be sorted",
78 category: "Stylistic Issues",
79 recommended: false,
80 url: "https://eslint.org/docs/rules/sort-keys"
81 },
82 schema: [
83 {
84 enum: ["asc", "desc"]
85 },
86 {
87 type: "object",
88 properties: {
89 caseSensitive: {
90 type: "boolean"
91 },
92 natural: {
93 type: "boolean"
94 }
95 },
96 additionalProperties: false
97 }
98 ]
99 },
100
101 create(context) {
102
103 // Parse options.
104 const order = context.options[0] || "asc";
105 const options = context.options[1];
106 const insensitive = (options && options.caseSensitive) === false;
107 const natual = Boolean(options && options.natural);
108 const isValidOrder = isValidOrders[
109 order + (insensitive ? "I" : "") + (natual ? "N" : "")
110 ];
111
112 // The stack to save the previous property's name for each object literals.
113 let stack = null;
114
115 return {
116 ObjectExpression() {
117 stack = {
118 upper: stack,
119 prevName: null
120 };
121 },
122
123 "ObjectExpression:exit"() {
124 stack = stack.upper;
125 },
126
127 Property(node) {
128 if (node.parent.type === "ObjectPattern") {
129 return;
130 }
131
132 const prevName = stack.prevName;
133 const thisName = getPropertyName(node);
134
135 stack.prevName = thisName || prevName;
136
137 if (!prevName || !thisName) {
138 return;
139 }
140
141 if (!isValidOrder(prevName, thisName)) {
142 context.report({
143 node,
144 loc: node.key.loc,
145 message: "Expected object keys to be in {{natual}}{{insensitive}}{{order}}ending order. '{{thisName}}' should be before '{{prevName}}'.",
146 data: {
147 thisName,
148 prevName,
149 order,
150 insensitive: insensitive ? "insensitive " : "",
151 natual: natual ? "natural " : ""
152 }
153 });
154 }
155 }
156 };
157 }
158};