"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
var Lint = require("tslint");
var ngWalker_1 = require("./angular/ngWalker");
var recursiveAngularExpressionVisitor_1 = require("./angular/templates/recursiveAngularExpressionVisitor");
var ast = require("@angular/compiler");
var unstrictEqualityOperator = '==';
var TemplateToNgTemplateVisitor = (function (_super) {
    __extends(TemplateToNgTemplateVisitor, _super);
    function TemplateToNgTemplateVisitor() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    TemplateToNgTemplateVisitor.prototype.visitBinary = function (expr, context) {
        if (!this.isAsyncBinding(expr.left)) {
            return _super.prototype.visitBinary.call(this, expr, context);
        }
        if (!(expr.right instanceof ast.LiteralPrimitive) || expr.right.value !== false || expr.operation !== unstrictEqualityOperator) {
            return _super.prototype.visitBinary.call(this, expr, context);
        }
        var operator = this.codeWithMap.code.slice(expr.left.span.end, expr.right.span.start);
        var operatorStart = (/^.*==/).exec(operator)[0].length - unstrictEqualityOperator.length;
        this.addFailure(this.createFailure(expr.span.start, expr.span.end - expr.span.start, "Async pipes must use strict equality `===` when comparing with `false`", [
            new Lint.Replacement(this.getSourcePosition(expr.left.span.end) + operatorStart, unstrictEqualityOperator.length, '==='),
        ]));
    };
    TemplateToNgTemplateVisitor.prototype.visitPrefixNot = function (expr, context) {
        if (!this.isAsyncBinding(expr.expression)) {
            return _super.prototype.visitPrefixNot.call(this, expr, context);
        }
        var width = expr.span.end - expr.span.start;
        var absoluteStart = this.getSourcePosition(expr.span.start);
        var expressionSource = this.codeWithMap.code.slice(expr.span.start, expr.span.end);
        var concreteWidth = width - (/ *$/).exec(expressionSource)[0].length;
        this.addFailure(this.createFailure(expr.span.start, width, "Async pipes can not be negated, use (observable | async) === false instead", [
            new Lint.Replacement(absoluteStart + concreteWidth, 1, ' === false '),
            new Lint.Replacement(absoluteStart, 1, ''),
        ]));
    };
    TemplateToNgTemplateVisitor.prototype.isAsyncBinding = function (expr) {
        return expr instanceof ast.BindingPipe && expr.name === 'async';
    };
    return TemplateToNgTemplateVisitor;
}(recursiveAngularExpressionVisitor_1.RecursiveAngularExpressionVisitor));
var Rule = (function (_super) {
    __extends(Rule, _super);
    function Rule() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    Rule.prototype.apply = function (sourceFile) {
        return this.applyWithWalker(new ngWalker_1.NgWalker(sourceFile, this.getOptions(), {
            expressionVisitorCtrl: TemplateToNgTemplateVisitor
        }));
    };
    Rule.metadata = {
        ruleName: 'templates-no-negated-async',
        type: 'functionality',
        description: "Ensures that strict equality is used when evaluating negations on async pipe outout.",
        rationale: "Async pipe evaluate to `null` before the observable or promise emits, which can lead to layout thrashing as" +
            " components load. Prefer strict `=== false` checks instead.",
        options: null,
        optionsDescription: "Not configurable.",
        typescriptOnly: true,
    };
    return Rule;
}(Lint.Rules.AbstractRule));
exports.Rule = Rule;
