UNPKG

9.19 kBJavaScriptView Raw
1"use strict";
2var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3 if (k2 === undefined) k2 = k;
4 Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5}) : (function(o, m, k, k2) {
6 if (k2 === undefined) k2 = k;
7 o[k2] = m[k];
8}));
9var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10 Object.defineProperty(o, "default", { enumerable: true, value: v });
11}) : function(o, v) {
12 o["default"] = v;
13});
14var __importStar = (this && this.__importStar) || function (mod) {
15 if (mod && mod.__esModule) return mod;
16 var result = {};
17 if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18 __setModuleDefault(result, mod);
19 return result;
20};
21Object.defineProperty(exports, "__esModule", { value: true });
22const utils_1 = require("@typescript-eslint/utils");
23const util = __importStar(require("../util"));
24const util_1 = require("../util");
25var Group;
26(function (Group) {
27 Group["conditional"] = "conditional";
28 Group["function"] = "function";
29 Group["import"] = "import";
30 Group["intersection"] = "intersection";
31 Group["keyword"] = "keyword";
32 Group["nullish"] = "nullish";
33 Group["literal"] = "literal";
34 Group["named"] = "named";
35 Group["object"] = "object";
36 Group["operator"] = "operator";
37 Group["tuple"] = "tuple";
38 Group["union"] = "union";
39})(Group || (Group = {}));
40function getGroup(node) {
41 switch (node.type) {
42 case utils_1.AST_NODE_TYPES.TSConditionalType:
43 return Group.conditional;
44 case utils_1.AST_NODE_TYPES.TSConstructorType:
45 case utils_1.AST_NODE_TYPES.TSFunctionType:
46 return Group.function;
47 case utils_1.AST_NODE_TYPES.TSImportType:
48 return Group.import;
49 case utils_1.AST_NODE_TYPES.TSIntersectionType:
50 return Group.intersection;
51 case utils_1.AST_NODE_TYPES.TSAnyKeyword:
52 case utils_1.AST_NODE_TYPES.TSBigIntKeyword:
53 case utils_1.AST_NODE_TYPES.TSBooleanKeyword:
54 case utils_1.AST_NODE_TYPES.TSNeverKeyword:
55 case utils_1.AST_NODE_TYPES.TSNumberKeyword:
56 case utils_1.AST_NODE_TYPES.TSObjectKeyword:
57 case utils_1.AST_NODE_TYPES.TSStringKeyword:
58 case utils_1.AST_NODE_TYPES.TSSymbolKeyword:
59 case utils_1.AST_NODE_TYPES.TSThisType:
60 case utils_1.AST_NODE_TYPES.TSUnknownKeyword:
61 case utils_1.AST_NODE_TYPES.TSIntrinsicKeyword:
62 return Group.keyword;
63 case utils_1.AST_NODE_TYPES.TSNullKeyword:
64 case utils_1.AST_NODE_TYPES.TSUndefinedKeyword:
65 case utils_1.AST_NODE_TYPES.TSVoidKeyword:
66 return Group.nullish;
67 case utils_1.AST_NODE_TYPES.TSLiteralType:
68 case utils_1.AST_NODE_TYPES.TSTemplateLiteralType:
69 return Group.literal;
70 case utils_1.AST_NODE_TYPES.TSArrayType:
71 case utils_1.AST_NODE_TYPES.TSIndexedAccessType:
72 case utils_1.AST_NODE_TYPES.TSInferType:
73 case utils_1.AST_NODE_TYPES.TSTypeReference:
74 return Group.named;
75 case utils_1.AST_NODE_TYPES.TSMappedType:
76 case utils_1.AST_NODE_TYPES.TSTypeLiteral:
77 return Group.object;
78 case utils_1.AST_NODE_TYPES.TSTypeOperator:
79 case utils_1.AST_NODE_TYPES.TSTypeQuery:
80 return Group.operator;
81 case utils_1.AST_NODE_TYPES.TSTupleType:
82 return Group.tuple;
83 case utils_1.AST_NODE_TYPES.TSUnionType:
84 return Group.union;
85 // These types should never occur as part of a union/intersection
86 case utils_1.AST_NODE_TYPES.TSNamedTupleMember:
87 case utils_1.AST_NODE_TYPES.TSOptionalType:
88 case utils_1.AST_NODE_TYPES.TSRestType:
89 case utils_1.AST_NODE_TYPES.TSTypePredicate:
90 /* istanbul ignore next */
91 throw new Error(`Unexpected Type ${node.type}`);
92 }
93}
94function requiresParentheses(node) {
95 return node.type === utils_1.AST_NODE_TYPES.TSFunctionType;
96}
97exports.default = util.createRule({
98 name: 'sort-type-union-intersection-members',
99 meta: {
100 type: 'suggestion',
101 docs: {
102 description: 'Enforces that members of a type union/intersection are sorted alphabetically',
103 recommended: false,
104 },
105 fixable: 'code',
106 hasSuggestions: true,
107 messages: {
108 notSorted: '{{type}} type members must be sorted.',
109 notSortedNamed: '{{type}} type {{name}} members must be sorted.',
110 suggestFix: 'Sort members of type (removes all comments).',
111 },
112 schema: [
113 {
114 type: 'object',
115 properties: {
116 checkIntersections: {
117 type: 'boolean',
118 },
119 checkUnions: {
120 type: 'boolean',
121 },
122 groupOrder: {
123 type: 'array',
124 items: {
125 type: 'string',
126 enum: (0, util_1.getEnumNames)(Group),
127 },
128 },
129 },
130 },
131 ],
132 },
133 defaultOptions: [
134 {
135 checkIntersections: true,
136 checkUnions: true,
137 groupOrder: [
138 Group.named,
139 Group.keyword,
140 Group.operator,
141 Group.literal,
142 Group.function,
143 Group.import,
144 Group.conditional,
145 Group.object,
146 Group.tuple,
147 Group.intersection,
148 Group.union,
149 Group.nullish,
150 ],
151 },
152 ],
153 create(context, [{ checkIntersections, checkUnions, groupOrder }]) {
154 const sourceCode = context.getSourceCode();
155 const collator = new Intl.Collator('en', {
156 sensitivity: 'base',
157 numeric: true,
158 });
159 function checkSorting(node) {
160 var _a;
161 const sourceOrder = node.types.map(type => {
162 var _a;
163 const group = (_a = groupOrder === null || groupOrder === void 0 ? void 0 : groupOrder.indexOf(getGroup(type))) !== null && _a !== void 0 ? _a : -1;
164 return {
165 group: group === -1 ? Number.MAX_SAFE_INTEGER : group,
166 node: type,
167 text: sourceCode.getText(type),
168 };
169 });
170 const expectedOrder = [...sourceOrder].sort((a, b) => {
171 if (a.group !== b.group) {
172 return a.group - b.group;
173 }
174 return (collator.compare(a.text, b.text) ||
175 (a.text < b.text ? -1 : a.text > b.text ? 1 : 0));
176 });
177 const hasComments = node.types.some(type => {
178 const count = sourceCode.getCommentsBefore(type).length +
179 sourceCode.getCommentsAfter(type).length;
180 return count > 0;
181 });
182 for (let i = 0; i < expectedOrder.length; i += 1) {
183 if (expectedOrder[i].node !== sourceOrder[i].node) {
184 let messageId = 'notSorted';
185 const data = {
186 name: '',
187 type: node.type === utils_1.AST_NODE_TYPES.TSIntersectionType
188 ? 'Intersection'
189 : 'Union',
190 };
191 if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) {
192 messageId = 'notSortedNamed';
193 data.name = node.parent.id.name;
194 }
195 const fix = fixer => {
196 const sorted = expectedOrder
197 .map(t => (requiresParentheses(t.node) ? `(${t.text})` : t.text))
198 .join(node.type === utils_1.AST_NODE_TYPES.TSIntersectionType ? ' & ' : ' | ');
199 return fixer.replaceText(node, sorted);
200 };
201 return context.report(Object.assign({ node,
202 messageId,
203 data }, (hasComments
204 ? {
205 suggest: [
206 {
207 messageId: 'suggestFix',
208 fix,
209 },
210 ],
211 }
212 : { fix })));
213 }
214 }
215 }
216 return Object.assign(Object.assign({}, (checkIntersections && {
217 TSIntersectionType(node) {
218 checkSorting(node);
219 },
220 })), (checkUnions && {
221 TSUnionType(node) {
222 checkSorting(node);
223 },
224 }));
225 },
226});
227//# sourceMappingURL=sort-type-union-intersection-members.js.map
\No newline at end of file