"use strict";
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
    result["default"] = mod;
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const typescript_estree_1 = require("@typescript-eslint/typescript-estree");
const util = __importStar(require("../util"));
exports.default = util.createRule({
    name: 'no-type-alias',
    meta: {
        type: 'suggestion',
        docs: {
            description: 'Disallow the use of type aliases',
            tslintRuleName: 'interface-over-type-literal',
            category: 'Stylistic Issues',
            recommended: false,
        },
        messages: {
            noTypeAlias: 'Type {{alias}} are not allowed.',
            noCompositionAlias: '{{typeName}} in {{compositionType}} types are not allowed.',
        },
        schema: [
            {
                type: 'object',
                properties: {
                    allowAliases: {
                        enum: [
                            'always',
                            'never',
                            'in-unions',
                            'in-intersections',
                            'in-unions-and-intersections',
                        ],
                    },
                    allowCallbacks: {
                        enum: ['always', 'never'],
                    },
                    allowLiterals: {
                        enum: [
                            'always',
                            'never',
                            'in-unions',
                            'in-intersections',
                            'in-unions-and-intersections',
                        ],
                    },
                    allowMappedTypes: {
                        enum: [
                            'always',
                            'never',
                            'in-unions',
                            'in-intersections',
                            'in-unions-and-intersections',
                        ],
                    },
                },
                additionalProperties: false,
            },
        ],
    },
    defaultOptions: [
        {
            allowAliases: 'never',
            allowCallbacks: 'never',
            allowLiterals: 'never',
            allowMappedTypes: 'never',
        },
    ],
    create(context, [{ allowAliases, allowCallbacks, allowLiterals, allowMappedTypes }]) {
        const unions = ['always', 'in-unions', 'in-unions-and-intersections'];
        const intersections = [
            'always',
            'in-intersections',
            'in-unions-and-intersections',
        ];
        const compositions = [
            'in-unions',
            'in-intersections',
            'in-unions-and-intersections',
        ];
        const aliasTypes = [
            typescript_estree_1.AST_NODE_TYPES.TSArrayType,
            typescript_estree_1.AST_NODE_TYPES.TSTypeReference,
            typescript_estree_1.AST_NODE_TYPES.TSLiteralType,
            typescript_estree_1.AST_NODE_TYPES.TSTypeQuery,
        ];
        /**
         * Determines if the given node is a union or an intersection.
         */
        function isComposition(node) {
            return (node &&
                (node.type === typescript_estree_1.AST_NODE_TYPES.TSUnionType ||
                    node.type === typescript_estree_1.AST_NODE_TYPES.TSIntersectionType));
        }
        /**
         * Determines if the composition type is supported by the allowed flags.
         * @param isTopLevel a flag indicating this is the top level node.
         * @param compositionType the composition type (either TSUnionType or TSIntersectionType)
         * @param allowed the currently allowed flags.
         */
        function isSupportedComposition(isTopLevel, compositionType, allowed) {
            return (compositions.indexOf(allowed) === -1 ||
                (!isTopLevel &&
                    ((compositionType === typescript_estree_1.AST_NODE_TYPES.TSUnionType &&
                        unions.indexOf(allowed) > -1) ||
                        (compositionType === typescript_estree_1.AST_NODE_TYPES.TSIntersectionType &&
                            intersections.indexOf(allowed) > -1))));
        }
        /**
         * Determines if the given node is an alias type (keywords, arrays, type references and constants).
         * @param node the node to be evaluated.
         */
        function isAlias(node) {
            return (node &&
                (/Keyword$/.test(node.type) || aliasTypes.indexOf(node.type) > -1));
        }
        /**
         * Determines if the given node is a callback type.
         * @param node the node to be evaluated.
         */
        function isCallback(node) {
            return node && node.type === typescript_estree_1.AST_NODE_TYPES.TSFunctionType;
        }
        /**
         * Determines if the given node is a literal type (objects).
         * @param node the node to be evaluated.
         */
        function isLiteral(node) {
            return node && node.type === typescript_estree_1.AST_NODE_TYPES.TSTypeLiteral;
        }
        /**
         * Determines if the given node is a mapped type.
         * @param node the node to be evaluated.
         */
        function isMappedType(node) {
            return node && node.type === typescript_estree_1.AST_NODE_TYPES.TSMappedType;
        }
        /**
         * Gets the message to be displayed based on the node type and whether the node is a top level declaration.
         * @param node the location
         * @param compositionType the type of composition this alias is part of (undefined if not
         *                                  part of a composition)
         * @param isRoot a flag indicating we are dealing with the top level declaration.
         * @param type the kind of type alias being validated.
         */
        function getMessage(node, compositionType, isRoot, type) {
            if (isRoot) {
                return {
                    node,
                    messageId: 'noTypeAlias',
                    data: {
                        alias: type || 'aliases',
                    },
                };
            }
            return {
                node,
                messageId: 'noCompositionAlias',
                data: {
                    compositionType: compositionType === typescript_estree_1.AST_NODE_TYPES.TSUnionType
                        ? 'union'
                        : 'intersection',
                    typeName: util.upperCaseFirst(type),
                },
            };
        }
        /**
         * Validates the node looking for aliases, callbacks and literals.
         * @param node the node to be validated.
         * @param isTopLevel a flag indicating this is the top level node.
         * @param compositionType the type of composition this alias is part of (undefined if not
         *                                  part of a composition)
         */
        function validateTypeAliases(node, isTopLevel, compositionType) {
            if (isCallback(node)) {
                if (allowCallbacks === 'never') {
                    context.report(getMessage(node, compositionType, isTopLevel, 'callbacks'));
                }
            }
            else if (isLiteral(node)) {
                if (allowLiterals === 'never' ||
                    !isSupportedComposition(isTopLevel, compositionType, allowLiterals)) {
                    context.report(getMessage(node, compositionType, isTopLevel, 'literals'));
                }
            }
            else if (isMappedType(node)) {
                if (allowMappedTypes === 'never' ||
                    !isSupportedComposition(isTopLevel, compositionType, allowMappedTypes)) {
                    context.report(getMessage(node, compositionType, isTopLevel, 'mapped types'));
                }
            }
            else if (isAlias(node)) {
                if (allowAliases === 'never' ||
                    !isSupportedComposition(isTopLevel, compositionType, allowAliases)) {
                    context.report(getMessage(node, compositionType, isTopLevel, 'aliases'));
                }
            }
            else {
                context.report(getMessage(node, compositionType, isTopLevel));
            }
        }
        /**
         * Validates compositions (unions and/or intersections).
         */
        function validateCompositions(node) {
            node.types.forEach(type => {
                if (isComposition(type)) {
                    validateCompositions(type);
                }
                else {
                    validateTypeAliases(type, false, node.type);
                }
            });
        }
        return {
            TSTypeAliasDeclaration(node) {
                if (isComposition(node.typeAnnotation)) {
                    validateCompositions(node.typeAnnotation);
                }
                else {
                    validateTypeAliases(node.typeAnnotation, true);
                }
            },
        };
    },
});
//# sourceMappingURL=no-type-alias.js.map