"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const utils_1 = require("@typescript-eslint/utils"); const eslint_utils_1 = require("@typescript-eslint/utils/eslint-utils"); const util_1 = require("../util"); var Group; (function (Group) { Group["conditional"] = "conditional"; Group["function"] = "function"; Group["import"] = "import"; Group["intersection"] = "intersection"; Group["keyword"] = "keyword"; Group["nullish"] = "nullish"; Group["literal"] = "literal"; Group["named"] = "named"; Group["object"] = "object"; Group["operator"] = "operator"; Group["tuple"] = "tuple"; Group["union"] = "union"; })(Group || (Group = {})); function getGroup(node) { switch (node.type) { case utils_1.AST_NODE_TYPES.TSConditionalType: return Group.conditional; case utils_1.AST_NODE_TYPES.TSConstructorType: case utils_1.AST_NODE_TYPES.TSFunctionType: return Group.function; case utils_1.AST_NODE_TYPES.TSImportType: return Group.import; case utils_1.AST_NODE_TYPES.TSIntersectionType: return Group.intersection; case utils_1.AST_NODE_TYPES.TSAnyKeyword: case utils_1.AST_NODE_TYPES.TSBigIntKeyword: case utils_1.AST_NODE_TYPES.TSBooleanKeyword: case utils_1.AST_NODE_TYPES.TSNeverKeyword: case utils_1.AST_NODE_TYPES.TSNumberKeyword: case utils_1.AST_NODE_TYPES.TSObjectKeyword: case utils_1.AST_NODE_TYPES.TSStringKeyword: case utils_1.AST_NODE_TYPES.TSSymbolKeyword: case utils_1.AST_NODE_TYPES.TSThisType: case utils_1.AST_NODE_TYPES.TSUnknownKeyword: case utils_1.AST_NODE_TYPES.TSIntrinsicKeyword: return Group.keyword; case utils_1.AST_NODE_TYPES.TSNullKeyword: case utils_1.AST_NODE_TYPES.TSUndefinedKeyword: case utils_1.AST_NODE_TYPES.TSVoidKeyword: return Group.nullish; case utils_1.AST_NODE_TYPES.TSLiteralType: case utils_1.AST_NODE_TYPES.TSTemplateLiteralType: return Group.literal; case utils_1.AST_NODE_TYPES.TSArrayType: case utils_1.AST_NODE_TYPES.TSIndexedAccessType: case utils_1.AST_NODE_TYPES.TSInferType: case utils_1.AST_NODE_TYPES.TSTypeReference: case utils_1.AST_NODE_TYPES.TSQualifiedName: return Group.named; case utils_1.AST_NODE_TYPES.TSMappedType: case utils_1.AST_NODE_TYPES.TSTypeLiteral: return Group.object; case utils_1.AST_NODE_TYPES.TSTypeOperator: case utils_1.AST_NODE_TYPES.TSTypeQuery: return Group.operator; case utils_1.AST_NODE_TYPES.TSTupleType: return Group.tuple; case utils_1.AST_NODE_TYPES.TSUnionType: return Group.union; // These types should never occur as part of a union/intersection case utils_1.AST_NODE_TYPES.TSAbstractKeyword: case utils_1.AST_NODE_TYPES.TSAsyncKeyword: case utils_1.AST_NODE_TYPES.TSDeclareKeyword: case utils_1.AST_NODE_TYPES.TSExportKeyword: case utils_1.AST_NODE_TYPES.TSNamedTupleMember: case utils_1.AST_NODE_TYPES.TSOptionalType: case utils_1.AST_NODE_TYPES.TSPrivateKeyword: case utils_1.AST_NODE_TYPES.TSProtectedKeyword: case utils_1.AST_NODE_TYPES.TSPublicKeyword: case utils_1.AST_NODE_TYPES.TSReadonlyKeyword: case utils_1.AST_NODE_TYPES.TSRestType: case utils_1.AST_NODE_TYPES.TSStaticKeyword: case utils_1.AST_NODE_TYPES.TSTypePredicate: /* istanbul ignore next */ throw new Error(`Unexpected Type ${node.type}`); } } exports.default = (0, util_1.createRule)({ name: 'sort-type-constituents', meta: { type: 'suggestion', docs: { description: 'Enforce constituents of a type union/intersection to be sorted alphabetically', }, fixable: 'code', hasSuggestions: true, messages: { notSorted: '{{type}} type constituents must be sorted.', notSortedNamed: '{{type}} type {{name}} constituents must be sorted.', suggestFix: 'Sort constituents of type (removes all comments).', }, schema: [ { type: 'object', additionalProperties: false, properties: { checkIntersections: { description: 'Whether to check intersection types.', type: 'boolean', }, checkUnions: { description: 'Whether to check union types.', type: 'boolean', }, groupOrder: { description: 'Ordering of the groups.', type: 'array', items: { type: 'string', enum: (0, util_1.getEnumNames)(Group), }, }, }, }, ], }, defaultOptions: [ { checkIntersections: true, checkUnions: true, groupOrder: [ Group.named, Group.keyword, Group.operator, Group.literal, Group.function, Group.import, Group.conditional, Group.object, Group.tuple, Group.intersection, Group.union, Group.nullish, ], }, ], create(context, [{ checkIntersections, checkUnions, groupOrder }]) { const sourceCode = (0, eslint_utils_1.getSourceCode)(context); const collator = new Intl.Collator('en', { sensitivity: 'base', numeric: true, }); function checkSorting(node) { const sourceOrder = node.types.map(type => { const group = groupOrder?.indexOf(getGroup(type)) ?? -1; return { group: group === -1 ? Number.MAX_SAFE_INTEGER : group, node: type, text: sourceCode.getText(type), }; }); const expectedOrder = [...sourceOrder].sort((a, b) => { if (a.group !== b.group) { return a.group - b.group; } return (collator.compare(a.text, b.text) || (a.text < b.text ? -1 : a.text > b.text ? 1 : 0)); }); const hasComments = node.types.some(type => { const count = sourceCode.getCommentsBefore(type).length + sourceCode.getCommentsAfter(type).length; return count > 0; }); for (let i = 0; i < expectedOrder.length; i += 1) { if (expectedOrder[i].node !== sourceOrder[i].node) { let messageId = 'notSorted'; const data = { name: '', type: node.type === utils_1.AST_NODE_TYPES.TSIntersectionType ? 'Intersection' : 'Union', }; if (node.parent.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) { messageId = 'notSortedNamed'; data.name = node.parent.id.name; } const fix = fixer => { const sorted = expectedOrder .map(t => (0, util_1.typeNodeRequiresParentheses)(t.node, t.text) || (node.type === utils_1.AST_NODE_TYPES.TSIntersectionType && t.node.type === utils_1.AST_NODE_TYPES.TSUnionType) ? `(${t.text})` : t.text) .join(node.type === utils_1.AST_NODE_TYPES.TSIntersectionType ? ' & ' : ' | '); return fixer.replaceText(node, sorted); }; return context.report({ node, messageId, data, // don't autofix if any of the types have leading/trailing comments // the logic for preserving them correctly is a pain - we may implement this later ...(hasComments ? { suggest: [ { messageId: 'suggestFix', fix, }, ], } : { fix }), }); } } } return { ...(checkIntersections && { TSIntersectionType(node) { checkSorting(node); }, }), ...(checkUnions && { TSUnionType(node) { checkSorting(node); }, }), }; }, }); //# sourceMappingURL=sort-type-constituents.js.map