text:
"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 __());
    };
})();
var __read = (this && this.__read) || function (o, n) {
    var m = typeof Symbol === "function" && o[Symbol.iterator];
    if (!m) return o;
    var i = m.call(o), r, ar = [], e;
    try {
        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
    }
    catch (error) { e = { error: error }; }
    finally {
        try {
            if (r && !r.done && (m = i["return"])) m.call(i);
        }
        finally { if (e) throw e.error; }
    }
    return ar;
};
var __spread = (this && this.__spread) || function () {
    for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
    return ar;
};
var __values = (this && this.__values) || function (o) {
    var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0;
    if (m) return m.call(o);
    return {
        next: function () {
            if (o && i >= o.length) o = void 0;
            return { value: o && o[i++], done: !o };
        }
    };
};
Object.defineProperty(exports, "__esModule", { value: true });
var path = require("path");
var source_map_1 = require("source-map");
var ts = require("typescript");
var decorator = require("./decorator-annotator");
var decorators_1 = require("./decorators");
var es5processor = require("./es5processor");
var jsdoc = require("./jsdoc");
var modules_manifest_1 = require("./modules_manifest");
var rewriter_1 = require("./rewriter");
var source_map_utils_1 = require("./source_map_utils");
var transformer_sourcemap_1 = require("./transformer_sourcemap");
var transformer_util_1 = require("./transformer_util");
var typeTranslator = require("./type-translator");
var util_1 = require("./util");
var decorator_annotator_1 = require("./decorator-annotator");
exports.convertDecorators = decorator_annotator_1.convertDecorators;
var modules_manifest_2 = require("./modules_manifest");
exports.ModulesManifest = modules_manifest_2.ModulesManifest;
/**
 * The header to be used in generated externs.  This is not included in the
 * output of annotate() because annotate() works one file at a time, and
 * typically you create one externs file from the entire compilation unit.
 */
exports.EXTERNS_HEADER = "/**\n * @externs\n * @suppress {duplicate,checkTypes}\n */\n// NOTE: generated by tsickle, do not edit.\n";
/**
 * Symbols that are already declared as externs in Closure, that should
 * be avoided by tsickle's "declare ..." => externs.js conversion.
 */
exports.closureExternsBlacklist = [
    'exports',
    'global',
    'module',
    // ErrorConstructor is the interface of the Error object itself.
    // tsickle detects that this is part of the TypeScript standard library
    // and assumes it's part of the Closure standard library, but this
    // assumption is wrong for ErrorConstructor.  To properly handle this
    // we'd somehow need to map methods defined on the ErrorConstructor
    // interface into properties on Closure's Error object, but for now it's
    // simpler to just blacklist it.
    'ErrorConstructor',
    'Symbol',
    'WorkerGlobalScope',
];
function formatDiagnostics(diags) {
    return diags
        .map(function (d) {
        var res = ts.DiagnosticCategory[d.category];
        if (d.file) {
            res += ' at ' + d.file.fileName + ':';
            if (d.start) {
                var _a = d.file.getLineAndCharacterOfPosition(d.start), line = _a.line, character = _a.character;
                res += (line + 1) + ':' + (character + 1) + ':';
            }
        }
        res += ' ' + ts.flattenDiagnosticMessageText(d.messageText, '\n');
        return res;
    })
        .join('\n');
}
exports.formatDiagnostics = formatDiagnostics;
/** @return true if node has the specified modifier flag set. */
function hasModifierFlag(node, flag) {
    return (ts.getCombinedModifierFlags(node) & flag) !== 0;
}
exports.hasModifierFlag = hasModifierFlag;
/** @return true if node has the specified modifier flag set. */
function isAmbient(node) {
    var current = node;
    while (current) {
        if (hasModifierFlag(current, ts.ModifierFlags.Ambient))
            return true;
        current = current.parent;
    }
    return false;
}
/**
 * TypeScript allows you to write identifiers quoted, like:
 *   interface Foo {
 *     'bar': string;
 *     'complex name': string;
 *   }
 *   Foo.bar;  // ok
 *   Foo['bar']  // ok
 *   Foo['complex name']  // ok
 *
 * In Closure-land, we want identify that the legal name 'bar' can become an
 * ordinary field, but we need to skip strings like 'complex name'.
 */
function isValidClosurePropertyName(name) {
    // In local experimentation, it appears that reserved words like 'var' and
    // 'if' are legal JS and still accepted by Closure.
    return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name);
}
function isDtsFileName(fileName) {
    return /\.d\.ts$/.test(fileName);
}
exports.isDtsFileName = isDtsFileName;
/** Returns the Closure name of a function parameter, special-casing destructuring. */
function getParameterName(param, index) {
    switch (param.name.kind) {
        case ts.SyntaxKind.Identifier:
            var name_1 = rewriter_1.getIdentifierText(param.name);
            // TypeScript allows parameters named "arguments", but Closure
            // disallows this, even in externs.
            if (name_1 === 'arguments')
                name_1 = 'tsickle_arguments';
            return name_1;
        case ts.SyntaxKind.ArrayBindingPattern:
        case ts.SyntaxKind.ObjectBindingPattern:
            // Closure crashes if you put a binding pattern in the externs.
            // Avoid this by just generating an unused name; the name is
            // ignored anyway.
            return "__" + index;
        default:
            // The above list of kinds is exhaustive.  param.name is 'never' at this point.
            var paramName = param.name;
            throw new Error("unhandled function parameter kind: " + ts.SyntaxKind[paramName.kind]);
    }
}
/** Flags that declare a field of the same name if set on a ctor parameter. */
var FIELD_DECLARATION_MODIFIERS = ts.ModifierFlags.Private |
    ts.ModifierFlags.Protected | ts.ModifierFlags.Public | ts.ModifierFlags.Readonly;
/**
 * A Rewriter subclass that adds Tsickle-specific (Closure translation) functionality.
 *
 * One Rewriter subclass manages .ts => .ts+Closure translation.
 * Another Rewriter subclass manages .ts => externs translation.
 */
var ClosureRewriter = (function (_super) {
    __extends(ClosureRewriter, _super);
    function ClosureRewriter(typeChecker, file, host, sourceMapper) {
        var _this = _super.call(this, file, sourceMapper) || this;
        _this.typeChecker = typeChecker;
        _this.host = host;
        /**
         * A mapping of aliases for symbols in the current file, used when emitting types.
         * TypeScript emits imported symbols with unpredictable prefixes. To generate correct type
         * annotations, tsickle creates its own aliases for types, and registers them in this map (see
         * `emitImportDeclaration` and `forwardDeclare()` below). The aliases are then used when emitting
         * types.
         */
        _this.symbolsToAliasedNames = new Map();
        return _this;
    }
    /**
     * Get the ts.Symbol at a location or throw.
     * The TypeScript API can return undefined when fetching a symbol, but
     * in many contexts we know it won't (e.g. our input is already type-checked).
     */
    ClosureRewriter.prototype.mustGetSymbolAtLocation = function (node) {
        var sym = this.typeChecker.getSymbolAtLocation(node);
        if (!sym)
            throw new Error('no symbol');
        return sym;
    };
    /**
     * Handles emittng the jsdoc for methods, including overloads.
     * If overloaded, merges the signatures in the list of SignatureDeclarations into a single jsdoc.
     * - Total number of parameters will be the maximum count found across all variants.
     * - Different names at the same parameter index will be joined with "_or_"
     * - Variable args (...type[] in TypeScript) will be output as "...type",
     *    except if found at the same index as another argument.
     * @param  fnDecls Pass > 1 declaration for overloads of same name
     * @return The list of parameter names that should be used to emit the actual
     *    function statement; for overloads, name will have been merged.
     */
    ClosureRewriter.prototype.emitFunctionType = function (fnDecls, extraTags) {
        if (extraTags === void 0) { extraTags = []; }
        var typeChecker = this.typeChecker;
        var newDoc = extraTags;
        var lens = fnDecls.map(function (fnDecl) { return fnDecl.parameters.length; });
        var minArgsCount = Math.min.apply(Math, __spread(lens));
        var maxArgsCount = Math.max.apply(Math, __spread(lens));
        var isConstructor = fnDecls.find(function (d) { return d.kind === ts.SyntaxKind.Constructor; }) !== undefined;
        // For each parameter index i, paramTags[i] is an array of parameters
        // that can be found at index i.  E.g.
        //    function foo(x: string)
        //    function foo(y: number, z: string)
        // then paramTags[0] = [info about x, info about y].
        var paramTags = [];
        var returnTags = [];
        var typeParameterNames = new Set();
        try {
            for (var fnDecls_1 = __values(fnDecls), fnDecls_1_1 = fnDecls_1.next(); !fnDecls_1_1.done; fnDecls_1_1 = fnDecls_1.next()) {
                var fnDecl = fnDecls_1_1.value;
                // Construct the JSDoc comment by reading the existing JSDoc, if
                // any, and merging it with the known types of the function
                // parameters and return type.
                var docTags = this.getJSDoc(fnDecl) || [];
                try {
                    // Copy all the tags other than @param/@return into the new
                    // JSDoc without any change; @param/@return are handled specially.
                    // TODO: there may be problems if an annotation doesn't apply to all overloads;
                    // is it worth checking for this and erroring?
                    for (var docTags_1 = __values(docTags), docTags_1_1 = docTags_1.next(); !docTags_1_1.done; docTags_1_1 = docTags_1.next()) {
                        var tag = docTags_1_1.value;
                        if (tag.tagName === 'param' || tag.tagName === 'return')
                            continue;
                        newDoc.push(tag);
                    }
                }
                catch (e_1_1) { e_1 = { error: e_1_1 }; }
                finally {
                    try {
                        if (docTags_1_1 && !docTags_1_1.done && (_a = docTags_1.return)) _a.call(docTags_1);
                    }
                    finally { if (e_1) throw e_1.error; }
                }
                // Add @abstract on "abstract" declarations.
                if (hasModifierFlag(fnDecl, ts.ModifierFlags.Abstract)) {
                    newDoc.push({ tagName: 'abstract' });
                }
                // Add any @template tags.
                // Multiple declarations with the same template variable names should work:
                // the declarations get turned into union types, and Closure Compiler will need
                // to find a union where all type arguments are satisfied.
                if (fnDecl.typeParameters) {
                    try {
                        for (var _b = __values(fnDecl.typeParameters), _c = _b.next(); !_c.done; _c = _b.next()) {
                            var tp = _c.value;
                            typeParameterNames.add(rewriter_1.getIdentifierText(tp.name));
                        }
                    }
                    catch (e_2_1) { e_2 = { error: e_2_1 }; }
                    finally {
                        try {
                            if (_c && !_c.done && (_d = _b.return)) _d.call(_b);
                        }
                        finally { if (e_2) throw e_2.error; }
                    }
                }
                // Merge the parameters into a single list of merged names and list of types
                var sig = typeChecker.getSignatureFromDeclaration(fnDecl);
                if (!sig)
                    throw new Error("invalid signature " + fnDecl.name);
                for (var i = 0; i < sig.declaration.parameters.length; i++) {
                    var paramNode = sig.declaration.parameters[i];
                    var name_2 = getParameterName(paramNode, i);
                    var isThisParam = name_2 === 'this';
                    var newTag = {
                        tagName: isThisParam ? 'this' : 'param',
                        optional: paramNode.initializer !== undefined || paramNode.questionToken !== undefined,
                        parameterName: isThisParam ? undefined : name_2,
                    };
                    var type = typeChecker.getTypeAtLocation(paramNode);
                    if (paramNode.dotDotDotToken !== undefined) {
                        newTag.restParam = true;
                        // In TypeScript you write "...x: number[]", but in Closure
                        // you don't write the array: "@param {...number} x".  Unwrap
                        // the Array<> wrapper.
                        var typeRef = type;
                        if (!typeRef.typeArguments)
                            throw new Error('invalid rest param');
                        type = typeRef.typeArguments[0];
                    }
                    newTag.type = this.typeToClosure(fnDecl, type);
                    try {
                        for (var docTags_2 = __values(docTags), docTags_2_1 = docTags_2.next(); !docTags_2_1.done; docTags_2_1 = docTags_2.next()) {
                            var _e = docTags_2_1.value, tagName = _e.tagName, parameterName = _e.parameterName, text = _e.text;
                            if (tagName === 'param' && parameterName === newTag.parameterName) {
                                newTag.text = text;
                                break;
                            }
                        }
                    }
                    catch (e_3_1) { e_3 = { error: e_3_1 }; }
                    finally {
                        try {
                            if (docTags_2_1 && !docTags_2_1.done && (_f = docTags_2.return)) _f.call(docTags_2);
                        }
                        finally { if (e_3) throw e_3.error; }
                    }
                    if (!paramTags[i])
                        paramTags.push([]);
                    paramTags[i].push(newTag);
                }
                // Return type.
                if (!isConstructor) {
                    var retType = typeChecker.getReturnTypeOfSignature(sig);
                    var retTypeString = this.typeToClosure(fnDecl, retType);
                    var returnDoc = void 0;
                    try {
                        for (var docTags_3 = __values(docTags), docTags_3_1 = docTags_3.next(); !docTags_3_1.done; docTags_3_1 = docTags_3.next()) {
                            var _g = docTags_3_1.value, tagName = _g.tagName, text = _g.text;
                            if (tagName === 'return') {
                                returnDoc = text;
                                break;
                            }
                        }
                    }
                    catch (e_4_1) { e_4 = { error: e_4_1 }; }
                    finally {
                        try {
                            if (docTags_3_1 && !docTags_3_1.done && (_h = docTags_3.return)) _h.call(docTags_3);
                        }
                        finally { if (e_4) throw e_4.error; }
                    }
                    returnTags.push({
                        tagName: 'return',
                        type: retTypeString,
                        text: returnDoc,
                    });
                }
            }
        }
        catch (e_5_1) { e_5 = { error: e_5_1 }; }
        finally {
            try {
                if (fnDecls_1_1 && !fnDecls_1_1.done && (_j = fnDecls_1.return)) _j.call(fnDecls_1);
            }
            finally { if (e_5) throw e_5.error; }
        }
        if (typeParameterNames.size > 0) {
            newDoc.push({ tagName: 'template', text: Array.from(typeParameterNames.values()).join(', ') });
        }
        // Merge the JSDoc tags for each overloaded parameter.
        // Ensure each parameter has a unique name; the merging process can otherwise
        // accidentally generate the same parameter name twice.
        var paramNames = new Set();
        var foundOptional = false;
        for (var i = 0; i < maxArgsCount; i++) {
            var paramTag = jsdoc.merge(paramTags[i]);
            if (paramNames.has(paramTag.parameterName)) {
                paramTag.parameterName += i.toString();
            }
            paramNames.add(paramTag.parameterName);
            // If the tag is optional, mark parameters following optional as optional,
            // even if they are not, since Closure restricts this, see
            // https://github.com/google/closure-compiler/issues/2314
            if (!paramTag.restParam && (paramTag.optional || foundOptional || i >= minArgsCount)) {
                foundOptional = true;
                paramTag.optional = true;
            }
            newDoc.push(paramTag);
            if (paramTag.restParam) {
                // Cannot have any parameters after a rest param.
                // Just dump the remaining parameters.
                break;
            }
        }
        // Merge the JSDoc tags for each overloaded return.
        if (!isConstructor) {
            newDoc.push(jsdoc.merge(returnTags));
        }
        this.emit('\n' + jsdoc.toString(newDoc));
        return newDoc.filter(function (t) { return t.tagName === 'param'; }).map(function (t) { return t.parameterName; });
        var e_5, _j, e_1, _a, e_2, _d, e_3, _f, e_4, _h;
    };
    /**
     * Returns null if there is no existing comment.
     */
    ClosureRewriter.prototype.getJSDoc = function (node) {
        var text = node.getFullText();
        var comments = ts.getLeadingCommentRanges(text, 0);
        if (!comments || comments.length === 0)
            return null;
        // We need to search backwards for the first JSDoc comment to avoid ignoring such when another
        // code-level comment is between that comment and the function declaration (see
        // testfiles/doc_params for an example).
        var docRelativePos = 0;
        var parsed = null;
        for (var i = comments.length - 1; i >= 0; i--) {
            var _a = comments[i], pos = _a.pos, end = _a.end;
            // end is relative within node.getFullText(), add getFullStart to obtain coordinates that are
            // comparable to node positions.
            var docRelativeEnd = end + node.getFullStart();
            if (docRelativeEnd <= this.file.getStart() &&
                this.file.text.substring(docRelativeEnd).startsWith('\n\n')) {
                // This comment is at the very beginning of the file and there's an empty line between the
                // comment and this node. That means we should treat it as a file-level comment, not
                // attached to this code node.
                return null;
            }
            var comment = text.substring(pos, end);
            parsed = jsdoc.parse(comment);
            if (parsed) {
                docRelativePos = node.getFullStart() + pos;
                break;
            }
        }
        if (!parsed)
            return null;
        if (parsed.warnings) {
            var start = docRelativePos;
            this.diagnostics.push({
                file: this.file,
                start: start,
                length: node.getStart() - start,
                messageText: parsed.warnings.join('\n'),
                category: ts.DiagnosticCategory.Warning,
                code: 0,
            });
        }
        return parsed.tags;
    };
    ClosureRewriter.prototype.maybeAddTemplateClause = function (docTags, decl) {
        var _this = this;
        if (!decl.typeParameters)
            return;
        // Closure does not support template constraints (T extends X).
        docTags.push({
            tagName: 'template',
            text: decl.typeParameters
                .map(function (tp) {
                if (tp.constraint) {
                    _this.emit('\n// unsupported: template constraints.');
                }
                return rewriter_1.getIdentifierText(tp.name);
            })
                .join(', ')
        });
    };
    ClosureRewriter.prototype.maybeAddHeritageClauses = function (docTags, decl) {
        if (!decl.heritageClauses)
            return;
        try {
            for (var _a = __values(decl.heritageClauses), _b = _a.next(); !_b.done; _b = _a.next()) {
                var heritage = _b.value;
                if (!heritage.types)
                    continue;
                var isClass = decl.kind === ts.SyntaxKind.ClassDeclaration;
                if (isClass && heritage.token !== ts.SyntaxKind.ImplementsKeyword && !isAmbient(decl)) {
                    // If a class has "extends Foo", that is preserved in the ES6 output
                    // and we don't need to do anything.  But if it has "implements Foo",
                    // that is a TS-specific thing and we need to translate it to the
                    // the Closure "@implements {Foo}".
                    // However for ambient declarations, we only emit externs, and in those we do need to
                    // add "@extends {Foo}" as they use ES5 syntax.
                    continue;
                }
                try {
                    for (var _c = __values(heritage.types), _d = _c.next(); !_d.done; _d = _c.next()) {
                        var impl = _d.value;
                        var tagName = decl.kind === ts.SyntaxKind.InterfaceDeclaration ? 'extends' : 'implements';
                        // We can only @implements an interface, not a class.
                        // But it's fine to translate TS "implements Class" into Closure
                        // "@extends {Class}" because this is just a type hint.
                        var typeChecker = this.typeChecker;
                        var sym = this.mustGetSymbolAtLocation(impl.expression);
                        var alias = sym;
                        if (sym.flags & ts.SymbolFlags.TypeAlias) {
                            // It's implementing a type alias.  Follow the type alias back
                            // to the original symbol to check whether it's a type or a value.
                            var type = this.typeChecker.getDeclaredTypeOfSymbol(sym);
                            if (!type.symbol) {
                                // It's not clear when this can happen, but if it does all we
                                // do is fail to emit the @implements, which isn't so harmful.
                                continue;
                            }
                            alias = type.symbol;
                        }
                        if (alias.flags & ts.SymbolFlags.Alias) {
                            alias = typeChecker.getAliasedSymbol(alias);
                        }
                        var typeTranslator_1 = this.newTypeTranslator(impl.expression);
                        if (typeTranslator_1.isBlackListed(alias)) {
                            continue;
                        }
                        if (alias.flags & ts.SymbolFlags.Class) {
                            if (!isClass) {
                                // Only classes can extend classes in TS. Ignoring the heritage clause should be safe,
                                // as interfaces are @record anyway, so should prevent property disambiguation.
                                // Problem: validate that methods are there?
                                continue;
                            }
                            tagName = 'extends';
                        }
                        else if (alias.flags & ts.SymbolFlags.Value) {
                            // If the symbol was already in the value namespace, then it will
                            // not be a type in the Closure output (because Closure collapses
                            // the type and value namespaces).  Just ignore the implements.
                            continue;
                        }
                        // typeToClosure includes nullability modifiers, so call symbolToString directly here.
                        docTags.push({ tagName: tagName, type: typeTranslator_1.symbolToString(sym, true) });
                    }
                }
                catch (e_6_1) { e_6 = { error: e_6_1 }; }
                finally {
                    try {
                        if (_d && !_d.done && (_e = _c.return)) _e.call(_c);
                    }
                    finally { if (e_6) throw e_6.error; }
                }
            }
        }
        catch (e_7_1) { e_7 = { error: e_7_1 }; }
        finally {
            try {
                if (_b && !_b.done && (_f = _a.return)) _f.call(_a);
            }
            finally { if (e_7) throw e_7.error; }
        }
        var e_7, _f, e_6, _e;
    };
    /** Emits a type annotation in JSDoc, or {?} if the type is unavailable. */
    ClosureRewriter.prototype.emitJSDocType = function (node, additionalDocTag, type) {
        this.emit(' /**');
        if (additionalDocTag) {
            this.emit(' ' + additionalDocTag);
        }
        this.emit(" @type {" + this.typeToClosure(node, type) + "} */");
    };
    /**
     * Convert a TypeScript ts.Type into the equivalent Closure type.
     *
     * @param context The ts.Node containing the type reference; used for resolving symbols
     *     in context.
     * @param type The type to translate; if not provided, the Node's type will be used.
     */
    ClosureRewriter.prototype.typeToClosure = function (context, type) {
        if (this.host.untyped) {
            return '?';
        }
        var typeChecker = this.typeChecker;
        if (!type) {
            type = typeChecker.getTypeAtLocation(context);
        }
        return this.newTypeTranslator(context).translate(type);
    };
    ClosureRewriter.prototype.newTypeTranslator = function (context) {
        var _this = this;
        var translator = new typeTranslator.TypeTranslator(this.typeChecker, context, this.host.typeBlackListPaths, this.symbolsToAliasedNames);
        translator.warn = function (msg) { return _this.debugWarn(context, msg); };
        return translator;
    };
    /**
     * debug logs a debug warning.  These should only be used for cases
     * where tsickle is making a questionable judgement about what to do.
     * By default, tsickle does not report any warnings to the caller,
     * and warnings are hidden behind a debug flag, as warnings are only
     * for tsickle to debug itself.
     */
    ClosureRewriter.prototype.debugWarn = function (node, messageText) {
        if (!this.host.logWarning)
            return;
        // Use a ts.Diagnosic so that the warning includes context and file offets.
        var diagnostic = {
            file: this.file,
            start: node.getStart(),
            length: node.getEnd() - node.getStart(),
            messageText: messageText,
            category: ts.DiagnosticCategory.Warning,
            code: 0,
        };
        this.host.logWarning(diagnostic);
    };
    return ClosureRewriter;
}(rewriter_1.Rewriter));
// Matches common extensions of TypeScript input filenames
var extension = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
var FILEOVERVIEW_COMMENTS = new Set(['fileoverview', 'externs', 'modName', 'mods', 'pintomodule']);
/** Annotator translates a .ts to a .ts with Closure annotations. */
var Annotator = (function (_super) {
    __extends(Annotator, _super);
    function Annotator(typeChecker, file, host, tsHost, tsOpts, sourceMapper) {
        var _this = _super.call(this, typeChecker, file, host, sourceMapper) || this;
        _this.tsHost = tsHost;
        _this.tsOpts = tsOpts;
        /** Exported symbol names that have been generated by expanding an "export * from ...". */
        _this.generatedExports = new Set();
        /** Collection of Identifiers used in an `import {foo}` declaration with their Symbol */
        _this.importedNames = [];
        _this.templateSpanStackCount = 0;
        _this.polymerBehaviorStackCount = 0;
        _this.forwardDeclareCounter = 0;
        return _this;
    }
    Annotator.prototype.annotate = function () {
        this.visit(this.file);
        return this.getOutput();
    };
    Annotator.prototype.getExportDeclarationNames = function (node) {
        var _this = this;
        switch (node.kind) {
            case ts.SyntaxKind.VariableStatement:
                var varDecl = node;
                return varDecl.declarationList.declarations.map(function (d) { return _this.getExportDeclarationNames(d)[0]; });
            case ts.SyntaxKind.VariableDeclaration:
            case ts.SyntaxKind.FunctionDeclaration:
            case ts.SyntaxKind.InterfaceDeclaration:
            case ts.SyntaxKind.ClassDeclaration:
            case ts.SyntaxKind.ModuleDeclaration:
                var decl = node;
                if (!decl.name || decl.name.kind !== ts.SyntaxKind.Identifier) {
                    break;
                }
                return [decl.name];
            case ts.SyntaxKind.TypeAliasDeclaration:
                var typeAlias = node;
                return [typeAlias.name];
            default:
                break;
        }
        this.error(node, "unsupported export declaration " + ts.SyntaxKind[node.kind] + ": " + node.getText());
        return [];
    };
    /**
     * Emits an ES6 export for the ambient declaration behind node, if it is indeed exported.
     */
    Annotator.prototype.maybeEmitAmbientDeclarationExport = function (node) {
        // In TypeScript, `export declare` simply generates no code in the exporting module, but does
        // generate a regular import in the importing module.
        // For Closure Compiler, such declarations must still be exported, so that importing code in
        // other modules can reference them. Because tsickle generates global symbols for such types,
        // the appropriate semantics are referencing the global name.
        if (this.host.untyped || !hasModifierFlag(node, ts.ModifierFlags.Export)) {
            return;
        }
        var declNames = this.getExportDeclarationNames(node);
        try {
            for (var declNames_1 = __values(declNames), declNames_1_1 = declNames_1.next(); !declNames_1_1.done; declNames_1_1 = declNames_1.next()) {
                var decl = declNames_1_1.value;
                var sym = this.mustGetSymbolAtLocation(decl);
                var isValue = sym.flags & ts.SymbolFlags.Value;
                var declName = rewriter_1.getIdentifierText(decl);
                if (node.kind === ts.SyntaxKind.VariableStatement) {
                    // For variables, TypeScript rewrites every reference to the variable name as an
                    // "exports." access, to maintain mutable ES6 exports semantics. Indirecting through the
                    // window object means we reference the correct global symbol. Closure Compiler does
                    // understand that "var foo" in externs corresponds to "window.foo".
                    this.emit("\nexports." + declName + " = window." + declName + ";\n");
                }
                else if (!isValue) {
                    // Do not emit re-exports for ModuleDeclarations.
                    // Ambient ModuleDeclarations are always referenced as global symbols, so they don't need to
                    // be exported.
                    if (node.kind === ts.SyntaxKind.ModuleDeclaration)
                        continue;
                    // Non-value objects do not exist at runtime, so we cannot access the symbol (it only
                    // exists in externs). Export them as a typedef, which forwards to the type in externs.
                    this.emit("\n/** @typedef {" + declName + "} */\nexports." + declName + ";\n");
                }
                else {
                    this.emit("\nexports." + declName + " = " + declName + ";\n");
                }
            }
        }
        catch (e_8_1) { e_8 = { error: e_8_1 }; }
        finally {
            try {
                if (declNames_1_1 && !declNames_1_1.done && (_a = declNames_1.return)) _a.call(declNames_1);
            }
            finally { if (e_8) throw e_8.error; }
        }
        var e_8, _a;
    };
    /**
     * Examines a ts.Node and decides whether to do special processing of it for output.
     *
     * @return True if the ts.Node has been handled, false if we should
     *     emit it as is and visit its children.
     */
    Annotator.prototype.maybeProcess = function (node) {
        var _this = this;
        if (hasModifierFlag(node, ts.ModifierFlags.Ambient) || isDtsFileName(this.file.fileName)) {
            // An ambient declaration declares types for TypeScript's benefit, so we want to skip Tsickle
            // conversion of its contents.
            this.writeRange(node, node.getFullStart(), node.getEnd());
            // ... but it might need to be exported for downstream importing code.
            this.maybeEmitAmbientDeclarationExport(node);
            return true;
        }
        if (this.currentDecoratorConverter) {
            this.currentDecoratorConverter.beforeProcessNode(node);
        }
        switch (node.kind) {
            case ts.SyntaxKind.SourceFile:
                this.handleSourceFile(node);
                return true;
            case ts.SyntaxKind.ImportDeclaration:
                (_a = this.importedNames).push.apply(_a, __spread(decorator.collectImportedNames(this.typeChecker, node)));
                return this.emitImportDeclaration(node);
            case ts.SyntaxKind.ExportDeclaration:
                var exportDecl = node;
                this.writeLeadingTrivia(node);
                this.emit('export');
                var exportedSymbols = [];
                if (!exportDecl.exportClause && exportDecl.moduleSpecifier) {
                    // It's an "export * from ..." statement.
                    // Rewrite it to re-export each exported symbol directly.
                    exportedSymbols = this.expandSymbolsFromExportStar(exportDecl);
                    var exportSymbolsToEmit = exportedSymbols.filter(function (s) { return _this.shouldEmitExportSymbol(s.sym); });
                    this.emit(" {" + exportSymbolsToEmit.map(function (e) { return rewriter_1.unescapeName(e.name); }).join(',') + "}");
                }
                else {
                    if (exportDecl.exportClause) {
                        exportedSymbols = this.getNamedSymbols(exportDecl.exportClause.elements);
                        this.visit(exportDecl.exportClause);
                    }
                }
                if (exportDecl.moduleSpecifier) {
                    this.emit(" from '" + this.resolveModuleSpecifier(exportDecl.moduleSpecifier) + "';");
                }
                else {
                    // export {...};
                    this.emit(';');
                }
                this.addSourceMapping(node);
                if (exportDecl.moduleSpecifier) {
                    this.forwardDeclare(exportDecl.moduleSpecifier, exportedSymbols);
                }
                if (exportedSymbols.length) {
                    this.emitTypeDefExports(exportedSymbols);
                }
                return true;
            case ts.SyntaxKind.InterfaceDeclaration:
                this.emitInterface(node);
                // Emit the TS interface verbatim, with no tsickle processing of properties.
                this.writeRange(node, node.getFullStart(), node.getEnd());
                return true;
            case ts.SyntaxKind.VariableDeclaration:
                var varDecl = node;
                // Only emit a type annotation when it's a plain variable and
                // not a binding pattern, as Closure doesn't(?) have a syntax
                // for annotating binding patterns.  See issue #128.
                // Don't emit type annotation when the variable statement is a @polymerBehavior,
                // as otherwise the polymer closure checker will fail.
                // See b/64389806
                if (this.polymerBehaviorStackCount === 0 &&
                    varDecl.name.kind === ts.SyntaxKind.Identifier) {
                    this.emitJSDocType(varDecl);
                }
                return false;
            case ts.SyntaxKind.ClassDeclaration:
                var classNode = node;
                this.visitClassDeclaration(classNode);
                return true;
            case ts.SyntaxKind.PublicKeyword:
            case ts.SyntaxKind.PrivateKeyword:
                // The "public"/"private" keywords are encountered in two places:
                // 1) In class fields (which don't appear in the transformed output).
                // 2) In "parameter properties", e.g.
                //      constructor(/** @export */ public foo: string).
                // In case 2 it's important to not emit that JSDoc in the generated
                // constructor, as this is illegal for Closure.  It's safe to just
                // always skip comments preceding the 'public' keyword.
                // See test_files/parameter_properties.ts.
                this.writeNode(node, /* skipComments */ true);
                return true;
            case ts.SyntaxKind.Constructor:
                var ctor = node;
                this.emitFunctionType([ctor]);
                // Write the "constructor(...) {" bit, but iterate through any
                // parameters if given so that we can examine them more closely.
                this.writeNodeFrom(ctor, ctor.getStart());
                return true;
            case ts.SyntaxKind.ArrowFunction:
                // It's difficult to annotate arrow functions due to a bug in
                // TypeScript (see tsickle issue 57).  For now, just pass them
                // through unannotated.
                return false;
            case ts.SyntaxKind.FunctionDeclaration:
            case ts.SyntaxKind.MethodDeclaration:
            case ts.SyntaxKind.GetAccessor:
            case ts.SyntaxKind.SetAccessor:
                var fnDecl = node;
                var tags = decorators_1.hasExportingDecorator(node, this.typeChecker) ? [{ tagName: 'export' }] : [];
                if (!fnDecl.body) {
                    // Two cases: abstract methods and overloaded methods/functions.
                    // Abstract methods are handled in emitTypeAnnotationsHandler.
                    // Overloads are union-ized into the shared type in emitFunctionType.
                    return false;
                }
                this.emitFunctionType([fnDecl], tags);
                this.writeNodeFrom(fnDecl, fnDecl.getStart());
                return true;
            case ts.SyntaxKind.TypeAliasDeclaration:
                this.writeNode(node);
                this.visitTypeAlias(node);
                return true;
            case ts.SyntaxKind.EnumDeclaration:
                this.processEnum(node);
                return true;
            case ts.SyntaxKind.TemplateSpan:
                this.templateSpanStackCount++;
                this.writeNode(node);
                this.templateSpanStackCount--;
                return true;
            case ts.SyntaxKind.TypeAssertionExpression:
            case ts.SyntaxKind.AsExpression:
                // Both of these cases are AssertionExpressions.
                var typeAssertion = node;
                if (this.polymerBehaviorStackCount > 0) {
                    // Don't emit type casts for Polymer behaviors that are declared
                    // by calling the Polymer function
                    // as the Polymer closure plugin does not work when emitting them.
                    // See b/64389806.
                    // Note: This only matters in the transformer version of tsickle,
                    // as the non transformer version never emitted type casts due to
                    // https://github.com/Microsoft/TypeScript/issues/9873 (see below).
                    return false;
                }
                // When using a type casts in template expressions,
                // closure requires another pair of parens, otherwise it will
                // complain with "Misplaced type annotation. Type annotations are not allowed here."
                if (this.templateSpanStackCount > 0) {
                    this.emit('(');
                }
                this.emitJSDocType(typeAssertion);
                // When TypeScript emits JS, it removes one layer of "redundant"
                // parens, but we need them for the Closure type assertion.  Work
                // around this by using two parens.  See test_files/coerce.*.
                // This is needed in both, the transformer and non transformer version.
                // TODO: in the non transformer version, the comment is currently dropped
                //  alltegether from pure assignments due to
                //  https://github.com/Microsoft/TypeScript/issues/9873.
                this.emit('((');
                this.writeNode(node);
                this.emit('))');
                if (this.templateSpanStackCount > 0) {
                    this.emit(')');
                }
                return true;
            case ts.SyntaxKind.NonNullExpression:
                var nnexpr = node;
                var type = this.typeChecker.getTypeAtLocation(nnexpr.expression);
                if (type.flags & ts.TypeFlags.Union) {
                    var nonNullUnion = type
                        .types.filter(function (t) { return (t.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) === 0; });
                    var typeCopy = Object.assign({}, type);
                    typeCopy.types = nonNullUnion;
                    type = typeCopy;
                }
                // See comment above.
                if (this.templateSpanStackCount > 0) {
                    this.emit('(');
                }
                this.emitJSDocType(nnexpr, undefined, type);
                // See comment above.
                this.emit('((');
                this.writeNode(nnexpr.expression);
                this.emit('))');
                if (this.templateSpanStackCount > 0) {
                    this.emit(')');
                }
                return true;
            case ts.SyntaxKind.PropertyDeclaration:
            case ts.SyntaxKind.VariableStatement:
                var docTags = this.getJSDoc(node) || [];
                if (decorators_1.hasExportingDecorator(node, this.typeChecker)) {
                    docTags.push({ tagName: 'export' });
                }
                if (docTags.length > 0 && node.getFirstToken()) {
                    this.emit('\n');
                    this.emit(jsdoc.toString(docTags));
                    var isPolymerBehavior = docTags.some(function (t) { return t.tagName === 'polymerBehavior'; });
                    if (isPolymerBehavior) {
                        this.polymerBehaviorStackCount++;
                    }
                    this.writeNodeFrom(node, node.getStart());
                    if (isPolymerBehavior) {
                        this.polymerBehaviorStackCount--;
                    }
                    return true;
                }
                break;
            case ts.SyntaxKind.PropertyAssignment:
                var pa = node;
                if (isPolymerBehaviorPropertyInCallExpression(pa)) {
                    this.polymerBehaviorStackCount++;
                    this.writeNodeFrom(node, node.getStart());
                    this.polymerBehaviorStackCount--;
                    return true;
                }
                return false;
            case ts.SyntaxKind.ElementAccessExpression:
                // Warn for quoted accesses to properties that have a symbol declared.
                // Mixing quoted and non-quoted access to a symbol (x['foo'] and x.foo) risks breaking
                // Closure Compiler renaming. Quoted access is more cumbersome to write than dotted access
                // though, so chances are users did intend to avoid renaming. The better fix is to use
                // `declare interface` though.
                var eae = node;
                if (!eae.argumentExpression ||
                    eae.argumentExpression.kind !== ts.SyntaxKind.StringLiteral) {
                    return false;
                }
                var quotedPropSym = this.typeChecker.getSymbolAtLocation(eae.argumentExpression);
                // If it has a symbol, it's actually a regular declared property.
                if (!quotedPropSym)
                    return false;
                var declarationHasQuotes = !quotedPropSym.declarations || quotedPropSym.declarations.some(function (d) {
                    var decl = d;
                    if (!decl.name)
                        return false;
                    return decl.name.kind === ts.SyntaxKind.StringLiteral;
                });
                // If the property is declared with quotes, it should also be accessed with them.
                if (declarationHasQuotes)
                    return false;
                var propName = eae.argumentExpression.text;
                // Properties containing non-JS identifier names can only be accessed with quotes.
                if (!isValidClosurePropertyName(propName))
                    return false;
                var symName = this.typeChecker.symbolToString(quotedPropSym);
                this.debugWarn(eae, "Declared property " + symName + " accessed with quotes. " +
                    "This can lead to renaming bugs. A better fix is to use 'declare interface' " +
                    "on the declaration.");
                // Previously, the code below changed the quoted into a non-quoted access.
                // this.writeNode(eae.expression);
                // this.emit(`.${propName}`);
                return false;
            case ts.SyntaxKind.PropertyAccessExpression:
                // Convert dotted accesses to types that have an index type declared to quoted accesses, to
                // avoid Closure renaming one access but not the other.
                // This can happen because TS allows dotted access to string index types.
                var pae = node;
                var t = this.typeChecker.getTypeAtLocation(pae.expression);
                if (!t.getStringIndexType())
                    return false;
                // Types can have string index signatures and declared properties (of the matching type).
                // These properties have a symbol, as opposed to pure string index types.
                var propSym = this.typeChecker.getSymbolAtLocation(pae.name);
                // The decision to return below is a judgement call. Presumably, in most situations, dotted
                // access to a property is correct, and should not be turned into quoted access even if
                // there is a string index on the type. However it is possible to construct programs where
                // this is incorrect, e.g. where user code assigns into a property through the index access
                // in another location.
                if (propSym)
                    return false;
                this.debugWarn(pae, this.typeChecker.typeToString(t) +
                    " has a string index type but is accessed using dotted access. " +
                    "Quoting the access.");
                this.writeNode(pae.expression);
                this.emit('["');
                this.writeNode(pae.name);
                this.emit('"]');
                return true;
            case ts.SyntaxKind.Decorator:
                if (this.currentDecoratorConverter) {
                    return this.currentDecoratorConverter.maybeProcessDecorator(node);
                }
                return false;
            default:
                break;
        }
        return false;
        var _a;
    };
    Annotator.prototype.shouldEmitExportSymbol = function (sym) {
        if (sym.flags & ts.SymbolFlags.Alias) {
            sym = this.typeChecker.getAliasedSymbol(sym);
        }
        if ((sym.flags & ts.SymbolFlags.Value) === 0) {
            // Note: We create explicit reexports via closure at another place in
            return false;
        }
        if (sym.flags & ts.SymbolFlags.ConstEnum) {
            return false;
        }
        return true;
    };
    Annotator.prototype.handleSourceFile = function (sf) {
        var start = this.emitSuppressChecktypes(sf);
        this.writeNodeFrom(sf, start);
    };
    /**
     * Emits an \@suppress {checkTypes} fileoverview comment.
     * Returns the place from where to start emitting the source file.
     */
    Annotator.prototype.emitSuppressChecktypes = function (sf) {
        var comments = ts.getLeadingCommentRanges(sf.getFullText(), 0) || [];
        var fileoverviewIdx = -1;
        for (var i = comments.length - 1; i >= 0; i--) {
            var parsed_1 = jsdoc.parse(sf.getFullText().substring(comments[i].pos, comments[i].end));
            if (parsed_1 !== null && parsed_1.tags.some(function (t) { return FILEOVERVIEW_COMMENTS.has(t.tagName); })) {
                fileoverviewIdx = i;
                break;
            }
        }
        // Add a @suppress {checkTypes} tag to each source file's JSDoc comment,
        // being careful to retain existing comments and their @suppress'ions.
        // Closure Compiler considers the *last* comment with @fileoverview (or @externs or @nocompile)
        // that has not been attached to some other tree node to be the file overview comment, and
        // only applies @suppress tags from it.
        // AJD considers *any* comment mentioning @fileoverview.
        if (fileoverviewIdx === -1) {
            // No existing comment to merge with, just emit a new one.
            this.emit(jsdoc.toString([
                { tagName: 'fileoverview', text: 'added by tsickle' },
                { tagName: 'suppress', type: 'checkTypes', text: 'checked by tsc' },
            ]));
            this.emit('\n');
            return sf.getFullStart();
        }
        var comment = comments[fileoverviewIdx];
        this.writeRange(sf, 0, comment.pos);
        var parsed = jsdoc.parse(sf.getFullText().substring(comment.pos, comment.end));
        if (!parsed)
            throw new Error('internal error: JSDoc comment does not parse');
        var tags = parsed.tags;
        // Add @suppress {checkTypes}, or add to the list in an existing @suppress tag.
        // Closure compiler barfs if there's a duplicated @suppress tag in a file, so the tag must
        // only appear once and be merged.
        var suppressIdx = tags.findIndex(function (t) { return t.tagName === 'suppress'; });
        if (suppressIdx !== -1) {
            var suppressions = tags[suppressIdx].type || '';
            var suppressionsList = suppressions.split(',').map(function (s) { return s.trim(); });
            if (suppressionsList.indexOf('checkTypes') === -1) {
                suppressionsList.push('checkTypes');
            }
            tags[suppressIdx].type = suppressionsList.join(',');
        }
        else {
            tags.push({
                tagName: 'suppress',
                type: 'checkTypes',
                text: 'checked by tsc',
            });
        }
        this.emit(jsdoc.toString(tags));
        if (sf.getFullText().substring(comment.end, comment.end + 2) !== '\n\n') {
            this.emit('\n\n'); // separate from file body to avoid being dropped by tsc.
        }
        // Return this comment's end, so that subsequent code does not emit this comment multiple times,
        // which can be an error in case of @fileoverview comments.
        // Known issue: if this comment is not the last comment in the file's leading trivia, this will
        // swallow comments before the next node, unless they are recognized as JSDoc comments on the
        // node. That's because while each node will print its leading trivia when visited, they will
        // not print areas that are partially blocked (like this, except for JSDoc). This is hard to fix
        // as the node being emitted cannot know which range should still be printed. Generally speaking
        // swallowing a comment is less risky than emitting one twice, so we take that into account.
        return comment.end;
    };
    /**
     * Given a "export * from ..." statement, gathers the symbol names it actually
     * exports to be used in a statement like "export {foo, bar, baz} from ...".
     *
     * This is necessary because TS transpiles "export *" by just doing a runtime loop
     * over the target module's exports, which means Closure won't see the declarations/types
     * that are exported.
     */
    Annotator.prototype.expandSymbolsFromExportStar = function (exportDecl) {
        // You can't have an "export *" without a module specifier.
        var moduleSpecifier = exportDecl.moduleSpecifier;
        // Gather the names of local exports, to avoid reexporting any
        // names that are already locally exported.
        var moduleSymbol = this.typeChecker.getSymbolAtLocation(this.file);
        var moduleExports = moduleSymbol && moduleSymbol.exports || new Map();
        // Expand the export list, then filter it to the symbols we want to reexport.
        var exports = this.typeChecker.getExportsOfModule(this.mustGetSymbolAtLocation(moduleSpecifier));
        var reexports = new Set();
        try {
            for (var exports_1 = __values(exports), exports_1_1 = exports_1.next(); !exports_1_1.done; exports_1_1 = exports_1.next()) {
                var sym = exports_1_1.value;
                var name_3 = rewriter_1.unescapeName(sym.name);
                if (moduleExports.has(name_3)) {
                    // This name is shadowed by a local definition, such as:
                    // - export var foo ...
                    // - export {foo} from ...
                    // - export {bar as foo} from ...
                    continue;
                }
                if (this.generatedExports.has(name_3)) {
                    // Already exported via an earlier expansion of an "export * from ...".
                    continue;
                }
                this.generatedExports.add(name_3);
                reexports.add(sym);
            }
        }
        catch (e_9_1) { e_9 = { error: e_9_1 }; }
        finally {
            try {
                if (exports_1_1 && !exports_1_1.done && (_a = exports_1.return)) _a.call(exports_1);
            }
            finally { if (e_9) throw e_9.error; }
        }
        return util_1.toArray(reexports.keys()).map(function (sym) {
            return { name: sym.name, sym: sym };
        });
        var e_9, _a;
    };
    /**
     * Write an `exports.` assignment for each type alias exported in the given `exports`.
     * TypeScript by itself does not export non-value symbols (e.g. interfaces, typedefs), as it
     * expects to remove those entirely for runtime. For Closure, types must be
     * exported as downstream code will import the type.
     *
     * The tsickle pass turns interfaces into values by generating a `function MyInterface() {}` for
     * them, so in the second conversion pass, TypeScript does export a value for them. However for
     * pure typedefs, tsickle only generates a property access with a JSDoc comment, so they need to
     * be exported explicitly here.
     */
    Annotator.prototype.emitTypeDefExports = function (exports) {
        if (this.host.untyped)
            return;
        try {
            for (var exports_2 = __values(exports), exports_2_1 = exports_2.next(); !exports_2_1.done; exports_2_1 = exports_2.next()) {
                var exp = exports_2_1.value;
                if (exp.sym.flags & ts.SymbolFlags.Alias)
                    exp.sym = this.typeChecker.getAliasedSymbol(exp.sym);
                var isTypeAlias = ((exp.sym.flags & ts.SymbolFlags.TypeAlias) !== 0 &&
                    (exp.sym.flags & ts.SymbolFlags.Value) === 0) ||
                    (exp.sym.flags & ts.SymbolFlags.Interface) !== 0 &&
                        (exp.sym.flags & ts.SymbolFlags.Value) === 0;
                if (!isTypeAlias)
                    continue;
                var typeName = this.symbolsToAliasedNames.get(exp.sym) || exp.sym.name;
                this.emit("\n/** @typedef {" + typeName + "} */\nexports." + exp.name + "; // re-export typedef");
            }
        }
        catch (e_10_1) { e_10 = { error: e_10_1 }; }
        finally {
            try {
                if (exports_2_1 && !exports_2_1.done && (_a = exports_2.return)) _a.call(exports_2);
            }
            finally { if (e_10) throw e_10.error; }
        }
        var e_10, _a;
    };
    /**
     * Convert from implicit `import {} from 'pkg'` to `import {} from 'pkg/index'.
     * TypeScript supports the shorthand, but not all ES6 module loaders do.
     * Workaround for https://github.com/Microsoft/TypeScript/issues/12597
     */
    Annotator.prototype.resolveModuleSpecifier = function (moduleSpecifier) {
        if (moduleSpecifier.kind !== ts.SyntaxKind.StringLiteral) {
            throw new Error("unhandled moduleSpecifier kind: " + ts.SyntaxKind[moduleSpecifier.kind]);
        }
        var moduleId = moduleSpecifier.text;
        if (this.host.convertIndexImportShorthand) {
            if (!this.tsOpts || !this.tsHost) {
                throw new Error('option convertIndexImportShorthand requires that annotate be called with a TypeScript host and options.');
            }
            var resolved = ts.resolveModuleName(moduleId, this.file.fileName, this.tsOpts, this.tsHost);
            if (resolved && resolved.resolvedModule) {
                var requestedModule = moduleId.replace(extension, '');
                var resolvedModule = resolved.resolvedModule.resolvedFileName.replace(extension, '');
                if (resolvedModule.indexOf('node_modules') === -1 &&
                    requestedModule.substr(requestedModule.lastIndexOf('/')) !==
                        resolvedModule.substr(resolvedModule.lastIndexOf('/'))) {
                    moduleId = './' +
                        path.relative(path.dirname(this.file.fileName), resolvedModule)
                            .replace(path.sep, '/');
                }
            }
        }
        return moduleId;
    };
    /**
     * Handles emit of an "import ..." statement.
     * We need to do a bit of rewriting so that imported types show up under the
     * correct name in JSDoc.
     * @return true if the decl was handled, false to allow default processing.
     */
    Annotator.prototype.emitImportDeclaration = function (decl) {
        this.writeLeadingTrivia(decl);
        this.emit('import');
        var importPath = this.resolveModuleSpecifier(decl.moduleSpecifier);
        var importClause = decl.importClause;
        if (!importClause) {
            // import './foo';
            this.emit("'" + importPath + "';");
            this.addSourceMapping(decl);
            return true;
        }
        else if (importClause.name ||
            (importClause.namedBindings &&
                importClause.namedBindings.kind === ts.SyntaxKind.NamedImports)) {
            this.visit(importClause);
            this.emit(" from '" + importPath + "';");
            this.addSourceMapping(decl);
            // importClause.name implies
            //   import a from ...;
            // namedBindings being NamedImports implies
            //   import {a as b} from ...;
            //
            // Both of these forms create a local name "a", which after TypeScript CommonJS compilation
            // will become some renamed variable like "module_1.default" or "module_1.a" (for default vs
            // named bindings, respectively).
            // tsickle references types in JSDoc. Because the module prefixes are not predictable, and
            // because TypeScript might remove imports entirely if they are only for types, the code below
            // inserts an artificial `const prefix = goog.require` call for the module, and then registers
            // all symbols from this import to be prefixed.
            if (!this.host.untyped) {
                var symbols = [];
                if (importClause.name) {
                    // import a from ...;
                    symbols = [{
                            name: rewriter_1.getIdentifierText(importClause.name),
                            sym: this.mustGetSymbolAtLocation(importClause.name),
                        }];
                }
                else {
                    // import {a as b} from ...;
                    if (!importClause.namedBindings ||
                        importClause.namedBindings.kind !== ts.SyntaxKind.NamedImports) {
                        throw new Error('unreached'); // Guaranteed by if check above.
                    }
                    symbols = this.getNamedSymbols(importClause.namedBindings.elements);
                }
                this.forwardDeclare(decl.moduleSpecifier, symbols, !!importClause.name);
            }
            return true;
        }
        else if (importClause.namedBindings &&
            importClause.namedBindings.kind === ts.SyntaxKind.NamespaceImport) {
            // import * as foo from ...;
            this.visit(importClause);
            this.emit(" from '" + importPath + "';");
            this.addSourceMapping(decl);
            return true;
        }
        else {
            this.errorUnimplementedKind(decl, 'unexpected kind of import');
            return false; // Use default processing.
        }
    };
    Annotator.prototype.getNamedSymbols = function (specifiers) {
        var _this = this;
        return specifiers.map(function (e) {
            return {
                // e.name might be renaming symbol as in `export {Foo as Bar}`, where e.name would be 'Bar'
                // and != sym.name. Store away the name so forwardDeclare below can emit the right name.
                name: rewriter_1.getIdentifierText(e.name),
                sym: _this.mustGetSymbolAtLocation(e.name),
            };
        });
    };
    /**
     * Emits a `goog.forwardDeclare` alias for each symbol from the given list.
     * @param specifier the import specifier, i.e. module path ("from '...'").
     */
    Annotator.prototype.forwardDeclare = function (specifier, exportedSymbols, isDefaultImport) {
        if (isDefaultImport === void 0) { isDefaultImport = false; }
        if (this.host.untyped)
            return;
        var importPath = this.resolveModuleSpecifier(specifier);
        var nsImport = es5processor.extractGoogNamespaceImport(importPath);
        var forwardDeclarePrefix = "tsickle_forward_declare_" + ++this.forwardDeclareCounter;
        var moduleNamespace = nsImport !== null ? nsImport : this.host.pathToModuleName(this.file.fileName, importPath);
        var moduleSymbol = this.typeChecker.getSymbolAtLocation(specifier);
        // Scripts do not have a symbol. Scripts can still be imported, either as side effect imports or
        // with an empty import set ("{}"). TypeScript does not emit a runtime load for an import with
        // an empty list of symbols, but the import forces any global declarations from the library to
        // be visible, which is what users use this for. No symbols from the script need forward
        // declaration, so just return.
        if (!moduleSymbol)
            return;
        var exports = this.typeChecker.getExportsOfModule(moduleSymbol);
        // In TypeScript, importing a module for use in a type annotation does not cause a runtime load.
        // In Closure Compiler, goog.require'ing a module causes a runtime load, so emitting requires
        // here would cause a change in load order, which is observable (and can lead to errors).
        // Instead, goog.forwardDeclare types, which allows using them in type annotations without
        // causing a load. See below for the exception to the rule.
        this.emit("\nconst " + forwardDeclarePrefix + " = goog.forwardDeclare(\"" + moduleNamespace + "\");");
        var hasValues = exports.some(function (e) { return (e.flags & ts.SymbolFlags.Value) !== 0; });
        if (!hasValues) {
            // Closure Compiler's toolchain will drop files that are never goog.require'd *before* type
            // checking (e.g. when using --closure_entry_point or similar tools). This causes errors
            // complaining about values not matching 'NoResolvedType', or modules not having a certain
            // member.
            // To fix, explicitly goog.require() modules that only export types. This should usually not
            // cause breakages due to load order (as no symbols are accessible from the module - though
            // contrived code could observe changes in side effects).
            // This is a heuristic - if the module exports some values, but those are never imported,
            // the file will still end up not being imported. Hopefully modules that export values are
            // imported for their value in some place.
            this.emit("\ngoog.require(\"" + moduleNamespace + "\"); // force type-only module to be loaded");
        }
        try {
            for (var exportedSymbols_1 = __values(exportedSymbols), exportedSymbols_1_1 = exportedSymbols_1.next(); !exportedSymbols_1_1.done; exportedSymbols_1_1 = exportedSymbols_1.next()) {
                var exp = exportedSymbols_1_1.value;
                if (exp.sym.flags & ts.SymbolFlags.Alias)
                    exp.sym = this.typeChecker.getAliasedSymbol(exp.sym);
                // goog: imports don't actually use the .default property that TS thinks they have.
                var qualifiedName = nsImport && isDefaultImport ? forwardDeclarePrefix :
                    forwardDeclarePrefix + '.' + exp.sym.name;
                this.symbolsToAliasedNames.set(exp.sym, qualifiedName);
            }
        }
        catch (e_11_1) { e_11 = { error: e_11_1 }; }
        finally {
            try {
                if (exportedSymbols_1_1 && !exportedSymbols_1_1.done && (_a = exportedSymbols_1.return)) _a.call(exportedSymbols_1);
            }
            finally { if (e_11) throw e_11.error; }
        }
        var e_11, _a;
    };
    Annotator.prototype.visitClassDeclaration = function (classDecl) {
        this.addSourceMapping(classDecl);
        var oldDecoratorConverter = this.currentDecoratorConverter;
        this.currentDecoratorConverter =
            new decorator.DecoratorClassVisitor(this.typeChecker, this, classDecl, this.importedNames);
        var docTags = this.getJSDoc(classDecl) || [];
        if (hasModifierFlag(classDecl, ts.ModifierFlags.Abstract)) {
            docTags.push({ tagName: 'abstract' });
        }
        if (!this.host.untyped) {
            this.maybeAddTemplateClause(docTags, classDecl);
            this.maybeAddHeritageClauses(docTags, classDecl);
        }
        this.emit('\n');
        if (docTags.length > 0)
            this.emit(jsdoc.toString(docTags));
        decorator.visitClassContentIncludingDecorators(classDecl, this, this.currentDecoratorConverter);
        this.emitTypeAnnotationsHelper(classDecl);
        this.currentDecoratorConverter = oldDecoratorConverter;
        return true;
    };
    Annotator.prototype.emitInterface = function (iface) {
        // If this symbol is both a type and a value, we cannot emit both into Closure's
        // single namespace.
        var sym = this.mustGetSymbolAtLocation(iface.name);
        if (sym.flags & ts.SymbolFlags.Value)
            return;
        var docTags = this.getJSDoc(iface) || [];
        docTags.push({ tagName: 'record' });
        if (!this.host.untyped) {
            this.maybeAddTemplateClause(docTags, iface);
            this.maybeAddHeritageClauses(docTags, iface);
        }
        this.emit('\n');
        this.emit(jsdoc.toString(docTags));
        if (hasModifierFlag(iface, ts.ModifierFlags.Export))
            this.emit('export ');
        var name = rewriter_1.getIdentifierText(iface.name);
        this.emit("function " + name + "() {}\n");
        this.emit("\n\nfunction " + name + "_tsickle_Closure_declarations() {\n");
        var memberNamespace = [name, 'prototype'];
        try {
            for (var _a = __values(iface.members), _b = _a.next(); !_b.done; _b = _a.next()) {
                var elem = _b.value;
                var isOptional = elem.questionToken != null;
                this.visitProperty(memberNamespace, elem, isOptional);
            }
        }
        catch (e_12_1) { e_12 = { error: e_12_1 }; }
        finally {
            try {
                if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
            }
            finally { if (e_12) throw e_12.error; }
        }
        this.emit("}\n");
        var e_12, _c;
    };
    /**
     * emitTypeAnnotationsHelper produces a _tsickle_typeAnnotationsHelper() where
     * none existed in the original source. It's necessary in the case where
     * TypeScript syntax specifies there are additional properties on the class,
     * because to declare these in Closure you must declare these in a method
     * somewhere.
     */
    Annotator.prototype.emitTypeAnnotationsHelper = function (classDecl) {
        var _this = this;
        // Gather parameter properties from the constructor, if it exists.
        var ctors = [];
        var paramProps = [];
        var nonStaticProps = [];
        var staticProps = [];
        var abstractMethods = [];
        try {
            for (var _a = __values(classDecl.members), _b = _a.next(); !_b.done; _b = _a.next()) {
                var member = _b.value;
                if (member.kind === ts.SyntaxKind.Constructor) {
                    ctors.push(member);
                }
                else if (member.kind === ts.SyntaxKind.PropertyDeclaration) {
                    var prop = member;
                    var isStatic = hasModifierFlag(prop, ts.ModifierFlags.Static);
                    if (isStatic) {
                        staticProps.push(prop);
                    }
                    else {
                        nonStaticProps.push(prop);
                    }
                }
                else if (hasModifierFlag(member, ts.ModifierFlags.Abstract) &&
                    (member.kind === ts.SyntaxKind.MethodDeclaration ||
                        member.kind === ts.SyntaxKind.GetAccessor ||
                        member.kind === ts.SyntaxKind.SetAccessor)) {
                    abstractMethods.push(member);
                }
            }
        }
        catch (e_13_1) { e_13 = { error: e_13_1 }; }
        finally {
            try {
                if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
            }
            finally { if (e_13) throw e_13.error; }
        }
        if (ctors.length > 0) {
            var ctor = ctors[0];
            paramProps = ctor.parameters.filter(function (p) { return hasModifierFlag(p, FIELD_DECLARATION_MODIFIERS); });
        }
        if (nonStaticProps.length === 0 && paramProps.length === 0 && staticProps.length === 0 &&
            abstractMethods.length === 0 &&
            !(this.currentDecoratorConverter && this.currentDecoratorConverter.foundDecorators())) {
            // There are no members so we don't need to emit any type
            // annotations helper.
            return;
        }
        if (!classDecl.name)
            return;
        var className = rewriter_1.getIdentifierText(classDecl.name);
        this.emit("\n\nfunction " + className + "_tsickle_Closure_declarations() {\n");
        if (this.currentDecoratorConverter) {
            this.currentDecoratorConverter.emitMetadataTypeAnnotationsHelpers();
        }
        staticProps.forEach(function (p) { return _this.visitProperty([className], p); });
        var memberNamespace = [className, 'prototype'];
        nonStaticProps.forEach(function (p) { return _this.visitProperty(memberNamespace, p); });
        paramProps.forEach(function (p) { return _this.visitProperty(memberNamespace, p); });
        try {
            for (var abstractMethods_1 = __values(abstractMethods), abstractMethods_1_1 = abstractMethods_1.next(); !abstractMethods_1_1.done; abstractMethods_1_1 = abstractMethods_1.next()) {
                var fnDecl = abstractMethods_1_1.value;
                var name_4 = this.propertyName(fnDecl);
                if (!name_4) {
                    this.error(fnDecl, 'anonymous abstract function');
                    continue;
                }
                var tags = decorators_1.hasExportingDecorator(fnDecl, this.typeChecker) ? [{ tagName: 'export' }] : [];
                var paramNames = this.emitFunctionType([fnDecl], tags);
                // memberNamespace because abstract methods cannot be static in TypeScript.
                this.emit(memberNamespace.join('.') + "." + name_4 + " = function(" + paramNames.join(', ') + ") {};\n");
            }
        }
        catch (e_14_1) { e_14 = { error: e_14_1 }; }
        finally {
            try {
                if (abstractMethods_1_1 && !abstractMethods_1_1.done && (_d = abstractMethods_1.return)) _d.call(abstractMethods_1);
            }
            finally { if (e_14) throw e_14.error; }
        }
        this.emit("}\n");
        var e_13, _c, e_14, _d;
    };
    Annotator.prototype.propertyName = function (prop) {
        if (!prop.name)
            return null;
        switch (prop.name.kind) {
            case ts.SyntaxKind.Identifier:
                return rewriter_1.getIdentifierText(prop.name);
            case ts.SyntaxKind.StringLiteral:
                // E.g. interface Foo { 'bar': number; }
                // If 'bar' is a name that is not valid in Closure then there's nothing we can do.
                var text = prop.name.text;
                if (!isValidClosurePropertyName(text))
                    return null;
                return text;
            default:
                return null;
        }
    };
    /**
     * @param optional If true, property is optional (e.g. written "foo?: string").
     */
    Annotator.prototype.visitProperty = function (namespace, prop, optional) {
        if (optional === void 0) { optional = false; }
        var name = this.propertyName(prop);
        if (!name) {
            this.emit("/* TODO: handle strange member:\n" + this.escapeForComment(prop.getText()) + "\n*/\n");
            return;
        }
        var type = this.typeToClosure(prop);
        // When a property is optional, e.g.
        //   foo?: string;
        // Then the TypeScript type of the property is string|undefined, the
        // typeToClosure translation handles it correctly, and string|undefined is
        // how you write an optional property in Closure.
        //
        // But in the special case of an optional property with type any:
        //   foo?: any;
        // The TypeScript type of the property is just "any" (because any includes
        // undefined as well) so our default translation of the type is just "?".
        // To mark the property as optional in Closure it must have "|undefined",
        // so the Closure type must be ?|undefined.
        if (optional && type === '?')
            type += '|undefined';
        var tags = this.getJSDoc(prop) || [];
        tags.push({ tagName: 'type', type: type });
        if (decorators_1.hasExportingDecorator(prop, this.typeChecker)) {
            tags.push({ tagName: 'export' });
        }
        // Avoid printing annotations that can conflict with @type
        // This avoids Closure's error "type annotation incompatible with other annotations"
        this.emit(jsdoc.toString(tags, new Set(['param', 'return'])));
        namespace = namespace.concat([name]);
        this.emit(namespace.join('.') + ";\n");
    };
    Annotator.prototype.visitTypeAlias = function (node) {
        if (this.host.untyped)
            return;
        // If the type is also defined as a value, skip emitting it. Closure collapses type & value
        // namespaces, the two emits would conflict if tsickle emitted both.
        var sym = this.mustGetSymbolAtLocation(node.name);
        if (sym.flags & ts.SymbolFlags.Value)
            return;
        // Write a Closure typedef, which involves an unused "var" declaration.
        // Note: in the case of an export, we cannot emit a literal "var" because
        // TypeScript drops exports that are never assigned to (and Closure
        // requires us to not assign to typedef exports).  Instead, emit the
        // "exports.foo;" line directly in that case.
        this.emit("\n/** @typedef {" + this.typeToClosure(node) + "} */\n");
        if (hasModifierFlag(node, ts.ModifierFlags.Export)) {
            this.emit('exports.');
        }
        else {
            this.emit('var ');
        }
        this.emit(node.name.getText() + ";\n");
    };
    /**
     * getEnumType computes the Closure type of an enum, by iterating through the members
     * and gathering their types.
     */
    Annotator.prototype.getEnumType = function (enumDecl) {
        var hasNumber = false;
        var hasString = false;
        try {
            for (var _a = __values(enumDecl.members), _b = _a.next(); !_b.done; _b = _a.next()) {
                var member = _b.value;
                if (member.initializer) {
                    var type = this.typeChecker.getTypeAtLocation(member.initializer);
                    // Note: checking against 'NumberLike' instead of just 'Number' means this code
                    // handles both
                    //   MEMBER = 3,  // TypeFlags.NumberLiteral
                    // and
                    //   MEMBER = someFunction(),  // TypeFlags.Number
                    if (type.flags & ts.TypeFlags.NumberLike) {
                        hasNumber = true;
                    }
                    else if (type.flags & ts.TypeFlags.StringLike) {
                        hasString = true;
                    }
                    else {
                        // Enum contains something other than a string or a number; bail.
                        return '?';
                    }
                }
                else {
                    // Members without initializers default to numeric.
                    hasNumber = true;
                }
            }
        }
        catch (e_15_1) { e_15 = { error: e_15_1 }; }
        finally {
            try {
                if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
            }
            finally { if (e_15) throw e_15.error; }
        }
        if (hasNumber && hasString) {
            return 'number|string';
        }
        else if (hasNumber) {
            return 'number';
        }
        else if (hasString) {
            return 'string';
        }
        else {
            // Perhaps an empty enum?
            return '?';
        }
        var e_15, _c;
    };
    /**
     * Processes an EnumDeclaration into a Closure type. Always emits a Closure type, even in untyped
     * mode, as that should be harmless (it only ever uses the number type).
     */
    Annotator.prototype.processEnum = function (node) {
        // Emit the enum declaration, which looks like:
        //   /** @enum {number} */
        //   const Foo = {BAR: 0, BAZ: 1, ...};
        //   export {Foo};  // even if originally exported on one line.
        // This declares an enum type for Closure Compiler (and Closure JS users of this TS code).
        // Splitting the enum into declaration and export is required so that local references to the
        // type resolve ("@type {Foo}").
        this.emit('\n');
        var name = node.name.getText();
        var enumType = this.getEnumType(node);
        this.emit("/** @enum {" + enumType + "} */\n");
        this.emit("const " + name + ": DontTypeCheckMe = {");
        // Emit enum values ('BAR: 0,').
        var enumIndex = 0;
        try {
            for (var _a = __values(node.members), _b = _a.next(); !_b.done; _b = _a.next()) {
                var member = _b.value;
                var memberName = member.name.getText();
                // Emit any comments and leading whitespace on the enum value definition.
                this.writeLeadingTrivia(member);
                this.emit(memberName + ": ");
                if (member.initializer) {
                    var enumConstValue = this.typeChecker.getConstantValue(member);
                    if (typeof enumConstValue === 'number') {
                        enumIndex = enumConstValue + 1;
                        this.emit(enumConstValue.toString());
                    }
                    else {
                        // Non-numeric enum value (string or an expression).
                        // Emit this initializer expression as-is.
                        // Note: if the member's initializer expression refers to another
                        // value within the enum (e.g. something like
                        //   enum Foo {
                        //     Field1,
                        //     Field2 = Field1 + something(),
                        //   }
                        // Then when we emit the initializer we produce invalid code because
                        // on the Closure side the reference to Field1 has to be namespaced,
                        // e.g. written "Foo.Field1 + something()".
                        // Hopefully this doesn't come up often -- if the enum instead has
                        // something like
                        //     Field2 = Field1 + 3,
                        // then it's still a constant expression and we inline the constant
                        // value in the above branch of this "if" statement.
                        this.visit(member.initializer);
                    }
                }
                else {
                    this.emit(enumIndex.toString());
                    enumIndex++;
                }
                this.emit(',');
            }
        }
        catch (e_16_1) { e_16 = { error: e_16_1 }; }
        finally {
            try {
                if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
            }
            finally { if (e_16) throw e_16.error; }
        }
        this.emit('};\n');
        var isExported = hasModifierFlag(node, ts.ModifierFlags.Export);
        if (isExported)
            this.emit("export {" + name + "};\n");
        if (hasModifierFlag(node, ts.ModifierFlags.Const)) {
            // By TypeScript semantics, const enums disappear after TS compilation.
            // We still need to generate the runtime value above to make Closure Compiler's type system
            // happy and allow refering to enums from JS code, but we should at least not emit string
            // value mappings.
            return;
        }
        // Emit the reverse mapping of foo[foo.BAR] = 'BAR'; lines for number enums.
        if (enumType === 'number') {
            try {
                for (var _d = __values(node.members), _e = _d.next(); !_e.done; _e = _d.next()) {
                    var member = _e.value;
                    var memberName = member.name.getText();
                    this.emit(name + "[" + name + "." + memberName + "] = \"" + memberName + "\";\n");
                }
            }
            catch (e_17_1) { e_17 = { error: e_17_1 }; }
            finally {
                try {
                    if (_e && !_e.done && (_f = _d.return)) _f.call(_d);
                }
                finally { if (e_17) throw e_17.error; }
            }
        }
        var e_16, _c, e_17, _f;
    };
    return Annotator;
}(ClosureRewriter));
/** ExternsWriter generates Closure externs from TypeScript source. */
var ExternsWriter = (function (_super) {
    __extends(ExternsWriter, _super);
    function ExternsWriter() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    ExternsWriter.prototype.process = function () {
        var _this = this;
        this.findExternRoots().forEach(function (node) { return _this.visit(node); });
        return this.getOutput();
    };
    ExternsWriter.prototype.newTypeTranslator = function (context) {
        var tt = _super.prototype.newTypeTranslator.call(this, context);
        tt.isForExterns = true;
        return tt;
    };
    ExternsWriter.prototype.findExternRoots = function () {
        if (isDtsFileName(this.file.fileName)) {
            return [this.file];
        }
        return this.file.statements.filter(function (stmt) { return hasModifierFlag(stmt, ts.ModifierFlags.Ambient); });
    };
    /** visit is the main entry point.  It generates externs from a ts.Node. */
    ExternsWriter.prototype.visit = function (node, namespace) {
        if (namespace === void 0) { namespace = []; }
        switch (node.kind) {
            case ts.SyntaxKind.SourceFile:
                var sourceFile = node;
                try {
                    for (var _a = __values(sourceFile.statements), _b = _a.next(); !_b.done; _b = _a.next()) {
                        var stmt = _b.value;
                        this.visit(stmt, namespace);
                    }
                }
                catch (e_18_1) { e_18 = { error: e_18_1 }; }
                finally {
                    try {
                        if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
                    }
                    finally { if (e_18) throw e_18.error; }
                }
                break;
            case ts.SyntaxKind.ModuleDeclaration:
                var decl = node;
                switch (decl.name.kind) {
                    case ts.SyntaxKind.Identifier:
                        // E.g. "declare namespace foo {"
                        var name_5 = rewriter_1.getIdentifierText(decl.name);
                        if (name_5 === 'global') {
                            // E.g. "declare global { ... }".  Reset to the outer namespace.
                            namespace = [];
                        }
                        else {
                            if (this.isFirstDeclaration(decl)) {
                                this.emit('/** @const */\n');
                                this.writeExternsVariable(name_5, namespace, '{}');
                            }
                            namespace = namespace.concat(name_5);
                        }
                        if (decl.body)
                            this.visit(decl.body, namespace);
                        break;
                    case ts.SyntaxKind.StringLiteral:
                        // E.g. "declare module 'foo' {" (note the quotes).
                        // We still want to emit externs for this module, but
                        // Closure doesn't really provide a mechanism for
                        // module-scoped externs.  For now, ignore the enclosing
                        // namespace (because this is declaring a top-level module)
                        // and emit into a fake namespace.
                        // Declare the top-level "tsickle_declare_module".
                        this.emit('/** @const */\n');
                        this.writeExternsVariable('tsickle_declare_module', [], '{}');
                        namespace = ['tsickle_declare_module'];
                        // Declare the inner "tsickle_declare_module.foo", if it's not
                        // declared already elsewhere.
                        var importName = decl.name.text;
                        this.emit("// Derived from: declare module \"" + importName + "\"\n");
                        // We also don't care about the actual name of the module ("foo"
                        // in the above example), except that we want it to not conflict.
                        importName = importName.replace(/_/, '__').replace(/[^A-Za-z]/g, '_');
                        if (this.isFirstDeclaration(decl)) {
                            this.emit('/** @const */\n');
                            this.writeExternsVariable(importName, namespace, '{}');
                        }
                        // Declare the contents inside the "tsickle_declare_module.foo".
                        if (decl.body)
                            this.visit(decl.body, namespace.concat(importName));
                        break;
                    default:
                        this.errorUnimplementedKind(decl.name, 'externs generation of namespace');
                }
                break;
            case ts.SyntaxKind.ModuleBlock:
                var block = node;
                try {
                    for (var _d = __values(block.statements), _e = _d.next(); !_e.done; _e = _d.next()) {
                        var stmt = _e.value;
                        this.visit(stmt, namespace);
                    }
                }
                catch (e_19_1) { e_19 = { error: e_19_1 }; }
                finally {
                    try {
                        if (_e && !_e.done && (_f = _d.return)) _f.call(_d);
                    }
                    finally { if (e_19) throw e_19.error; }
                }
                break;
            case ts.SyntaxKind.ClassDeclaration:
            case ts.SyntaxKind.InterfaceDeclaration:
                this.writeExternsType(node, namespace);
                break;
            case ts.SyntaxKind.FunctionDeclaration:
                var fnDecl = node;
                var name_6 = fnDecl.name;
                if (!name_6) {
                    this.error(fnDecl, 'anonymous function in externs');
                    break;
                }
                // Gather up all overloads of this function.
                var sym = this.mustGetSymbolAtLocation(name_6);
                var decls = sym.declarations.filter(function (d) { return d.kind === ts.SyntaxKind.FunctionDeclaration; });
                // Only emit the first declaration of each overloaded function.
                if (fnDecl !== decls[0])
                    break;
                var params = this.emitFunctionType(decls);
                this.writeExternsFunction(name_6, params, namespace);
                break;
            case ts.SyntaxKind.VariableStatement:
                try {
                    for (var _g = __values(node.declarationList.declarations), _h = _g.next(); !_h.done; _h = _g.next()) {
                        var decl_1 = _h.value;
                        this.writeExternsVariableDecl(decl_1, namespace);
                    }
                }
                catch (e_20_1) { e_20 = { error: e_20_1 }; }
                finally {
                    try {
                        if (_h && !_h.done && (_j = _g.return)) _j.call(_g);
                    }
                    finally { if (e_20) throw e_20.error; }
                }
                break;
            case ts.SyntaxKind.EnumDeclaration:
                this.writeExternsEnum(node, namespace);
                break;
            case ts.SyntaxKind.TypeAliasDeclaration:
                this.writeExternsTypeAlias(node, namespace);
                break;
            default:
                this.emit("\n/* TODO: " + ts.SyntaxKind[node.kind] + " in " + namespace.join('.') + " */\n");
                break;
        }
        var e_18, _c, e_19, _f, e_20, _j;
    };
    /**
     * isFirstDeclaration returns true if decl is the first declaration
     * of its symbol.  E.g. imagine
     *   interface Foo { x: number; }
     *   interface Foo { y: number; }
     * we only want to emit the "@record" for Foo on the first one.
     */
    ExternsWriter.prototype.isFirstDeclaration = function (decl) {
        if (!decl.name)
            return true;
        var sym = this.mustGetSymbolAtLocation(decl.name);
        if (!sym.declarations || sym.declarations.length < 2)
            return true;
        return decl === sym.declarations[0];
    };
    ExternsWriter.prototype.writeExternsType = function (decl, namespace) {
        var name = decl.name;
        if (!name) {
            this.error(decl, 'anonymous type in externs');
            return;
        }
        var typeName = namespace.concat([name.getText()]).join('.');
        if (exports.closureExternsBlacklist.indexOf(typeName) >= 0)
            return;
        if (this.isFirstDeclaration(decl)) {
            var paramNames = [];
            var jsdocTags = [];
            var writeJsDoc = true;
            this.maybeAddHeritageClauses(jsdocTags, decl);
            if (decl.kind === ts.SyntaxKind.ClassDeclaration) {
                jsdocTags.push({ tagName: 'constructor' });
                jsdocTags.push({ tagName: 'struct' });
                var ctors = decl
                    .members.filter(function (m) { return m.kind === ts.SyntaxKind.Constructor; });
                if (ctors.length) {
                    writeJsDoc = false;
                    var firstCtor = ctors[0];
                    var ctorTags = [{ tagName: 'constructor' }, { tagName: 'struct' }];
                    if (ctors.length > 1) {
                        paramNames = this.emitFunctionType(ctors, ctorTags);
                    }
                    else {
                        paramNames = this.emitFunctionType([firstCtor], ctorTags);
                    }
                }
            }
            else {
                jsdocTags.push({ tagName: 'record' });
                jsdocTags.push({ tagName: 'struct' });
            }
            if (writeJsDoc)
                this.emit(jsdoc.toString(jsdocTags));
            this.writeExternsFunction(name, paramNames, namespace);
        }
        // Process everything except (MethodSignature|MethodDeclaration|Constructor)
        var methods = new Map();
        try {
            for (var _a = __values(decl.members), _b = _a.next(); !_b.done; _b = _a.next()) {
                var member = _b.value;
                switch (member.kind) {
                    case ts.SyntaxKind.PropertySignature:
                    case ts.SyntaxKind.PropertyDeclaration:
                        var prop = member;
                        if (prop.name.kind === ts.SyntaxKind.Identifier) {
                            this.emitJSDocType(prop);
                            if (hasModifierFlag(prop, ts.ModifierFlags.Static)) {
                                this.emit("\n" + typeName + "." + prop.name.getText() + ";\n");
                            }
                            else {
                                this.emit("\n" + typeName + ".prototype." + prop.name.getText() + ";\n");
                            }
                            continue;
                        }
                        // TODO: For now property names other than Identifiers are not handled; e.g.
                        //    interface Foo { "123bar": number }
                        break;
                    case ts.SyntaxKind.MethodSignature:
                    case ts.SyntaxKind.MethodDeclaration:
                        var method = member;
                        var isStatic = hasModifierFlag(method, ts.ModifierFlags.Static);
                        var methodSignature = method.name.getText() + "$$$" + (isStatic ? 'static' : 'instance');
                        if (methods.has(methodSignature)) {
                            methods.get(methodSignature).push(method);
                        }
                        else {
                            methods.set(methodSignature, [method]);
                        }
                        continue;
                    case ts.SyntaxKind.Constructor:
                        continue; // Handled above.
                    default:
                        // Members can include things like index signatures, for e.g.
                        //   interface Foo { [key: string]: number; }
                        // For now, just skip it.
                        break;
                }
                // If we get here, the member wasn't handled in the switch statement.
                var memberName = namespace;
                if (member.name) {
                    memberName = memberName.concat([member.name.getText()]);
                }
                this.emit("\n/* TODO: " + ts.SyntaxKind[member.kind] + ": " + memberName.join('.') + " */\n");
            }
        }
        catch (e_21_1) { e_21 = { error: e_21_1 }; }
        finally {
            try {
                if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
            }
            finally { if (e_21) throw e_21.error; }
        }
        try {
            // Handle method declarations/signatures separately, since we need to deal with overloads.
            for (var _d = __values(Array.from(methods.values())), _e = _d.next(); !_e.done; _e = _d.next()) {
                var methodVariants = _e.value;
                var firstMethodVariant = methodVariants[0];
                var parameterNames = void 0;
                if (methodVariants.length > 1) {
                    parameterNames = this.emitFunctionType(methodVariants);
                }
                else {
                    parameterNames = this.emitFunctionType([firstMethodVariant]);
                }
                var methodNamespace = namespace.concat([name.getText()]);
                // If the method is static, don't add the prototype.
                if (!hasModifierFlag(firstMethodVariant, ts.ModifierFlags.Static)) {
                    methodNamespace.push('prototype');
                }
                this.writeExternsFunction(firstMethodVariant.name, parameterNames, methodNamespace);
            }
        }
        catch (e_22_1) { e_22 = { error: e_22_1 }; }
        finally {
            try {
                if (_e && !_e.done && (_f = _d.return)) _f.call(_d);
            }
            finally { if (e_22) throw e_22.error; }
        }
        var e_21, _c, e_22, _f;
    };
    ExternsWriter.prototype.writeExternsVariableDecl = function (decl, namespace) {
        if (decl.name.kind === ts.SyntaxKind.Identifier) {
            var name_7 = rewriter_1.getIdentifierText(decl.name);
            if (exports.closureExternsBlacklist.indexOf(name_7) >= 0)
                return;
            this.emitJSDocType(decl);
            this.emit('\n');
            this.writeExternsVariable(name_7, namespace);
        }
        else {
            this.errorUnimplementedKind(decl.name, 'externs for variable');
        }
    };
    ExternsWriter.prototype.writeExternsVariable = function (name, namespace, value) {
        var qualifiedName = namespace.concat([name]).join('.');
        if (namespace.length === 0)
            this.emit("var ");
        this.emit(qualifiedName);
        if (value)
            this.emit(" = " + value);
        this.emit(';\n');
    };
    ExternsWriter.prototype.writeExternsFunction = function (name, params, namespace) {
        var paramsStr = params.join(', ');
        if (namespace.length > 0) {
            var fqn = namespace.join('.');
            if (name.kind === ts.SyntaxKind.Identifier) {
                fqn += '.'; // computed names include [ ] in their getText() representation.
            }
            fqn += name.getText();
            this.emit(fqn + " = function(" + paramsStr + ") {};\n");
        }
        else {
            if (name.kind !== ts.SyntaxKind.Identifier) {
                this.error(name, 'Non-namespaced computed name in externs');
            }
            this.emit("function " + name.getText() + "(" + paramsStr + ") {}\n");
        }
    };
    ExternsWriter.prototype.writeExternsEnum = function (decl, namespace) {
        var name = rewriter_1.getIdentifierText(decl.name);
        this.emit('\n/** @const */\n');
        this.writeExternsVariable(name, namespace, '{}');
        namespace = namespace.concat([name]);
        try {
            for (var _a = __values(decl.members), _b = _a.next(); !_b.done; _b = _a.next()) {
                var member = _b.value;
                var memberName = void 0;
                switch (member.name.kind) {
                    case ts.SyntaxKind.Identifier:
                        memberName = rewriter_1.getIdentifierText(member.name);
                        break;
                    case ts.SyntaxKind.StringLiteral:
                        var text = member.name.text;
                        if (isValidClosurePropertyName(text))
                            memberName = text;
                        break;
                    default:
                        break;
                }
                if (!memberName) {
                    this.emit("\n/* TODO: " + ts.SyntaxKind[member.name.kind] + ": " + member.name.getText() + " */\n");
                    continue;
                }
                this.emit('/** @const {number} */\n');
                this.writeExternsVariable(memberName, namespace);
            }
        }
        catch (e_23_1) { e_23 = { error: e_23_1 }; }
        finally {
            try {
                if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
            }
            finally { if (e_23) throw e_23.error; }
        }
        var e_23, _c;
    };
    ExternsWriter.prototype.writeExternsTypeAlias = function (decl, namespace) {
        this.emit("\n/** @typedef {" + this.typeToClosure(decl) + "} */\n");
        this.writeExternsVariable(rewriter_1.getIdentifierText(decl.name), namespace);
    };
    return ExternsWriter;
}(ClosureRewriter));
function isPolymerBehaviorPropertyInCallExpression(pa) {
    var parentParent = pa.parent && pa.parent.parent;
    if (pa.name.kind !== ts.SyntaxKind.Identifier ||
        pa.name.text !== 'behaviors' || !pa.parent || !pa.parent.parent ||
        pa.parent.parent.kind !== ts.SyntaxKind.CallExpression) {
        return false;
    }
    var expr = parentParent.expression;
    return expr.kind === ts.SyntaxKind.Identifier && expr.text === 'Polymer';
}
function annotate(typeChecker, file, host, tsHost, tsOpts, sourceMapper) {
    return new Annotator(typeChecker, file, host, tsHost, tsOpts, sourceMapper).annotate();
}
exports.annotate = annotate;
function writeExterns(typeChecker, file, host) {
    return new ExternsWriter(typeChecker, file, host).process();
}
exports.writeExterns = writeExterns;
/** Concatenate all generated externs definitions together into a string. */
function getGeneratedExterns(externs) {
    var allExterns = exports.EXTERNS_HEADER;
    try {
        for (var _a = __values(Object.keys(externs)), _b = _a.next(); !_b.done; _b = _a.next()) {
            var fileName = _b.value;
            allExterns += "// externs from " + fileName + ":\n";
            allExterns += externs[fileName];
        }
    }
    catch (e_24_1) { e_24 = { error: e_24_1 }; }
    finally {
        try {
            if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
        }
        finally { if (e_24) throw e_24.error; }
    }
    return allExterns;
    var e_24, _c;
}
exports.getGeneratedExterns = getGeneratedExterns;
function mergeEmitResults(emitResults) {
    var diagnostics = [];
    var emitSkipped = true;
    var emittedFiles = [];
    var externs = {};
    var modulesManifest = new modules_manifest_1.ModulesManifest();
    try {
        for (var emitResults_1 = __values(emitResults), emitResults_1_1 = emitResults_1.next(); !emitResults_1_1.done; emitResults_1_1 = emitResults_1.next()) {
            var er = emitResults_1_1.value;
            diagnostics.push.apply(diagnostics, __spread(er.diagnostics));
            emitSkipped = emitSkipped || er.emitSkipped;
            emittedFiles.push.apply(emittedFiles, __spread(er.emittedFiles));
            Object.assign(externs, er.externs);
            modulesManifest.addManifest(er.modulesManifest);
        }
    }
    catch (e_25_1) { e_25 = { error: e_25_1 }; }
    finally {
        try {
            if (emitResults_1_1 && !emitResults_1_1.done && (_a = emitResults_1.return)) _a.call(emitResults_1);
        }
        finally { if (e_25) throw e_25.error; }
    }
    return { diagnostics: diagnostics, emitSkipped: emitSkipped, emittedFiles: emittedFiles, externs: externs, modulesManifest: modulesManifest };
    var e_25, _a;
}
exports.mergeEmitResults = mergeEmitResults;
function emitWithTsickle(program, host, tsHost, tsOptions, targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers) {
    if (customTransformers === void 0) { customTransformers = {}; }
    var tsickleDiagnostics = [];
    var typeChecker = program.getTypeChecker();
    var tsickleSourceTransformers = [];
    // add tsickle transformers
    if (host.transformTypesToClosure) {
        // Note: tsickle.annotate can also lower decorators in the same run.
        tsickleSourceTransformers.push(transformer_sourcemap_1.createTransformerFromSourceMap(function (sourceFile, sourceMapper) {
            var _a = annotate(typeChecker, sourceFile, host, tsHost, tsOptions, sourceMapper), output = _a.output, diagnostics = _a.diagnostics;
            tsickleDiagnostics.push.apply(tsickleDiagnostics, __spread(diagnostics));
            return output;
        }));
    }
    else if (host.transformDecorators) {
        tsickleSourceTransformers.push(transformer_sourcemap_1.createTransformerFromSourceMap(function (sourceFile, sourceMapper) {
            var _a = decorator.convertDecorators(typeChecker, sourceFile, sourceMapper), output = _a.output, diagnostics = _a.diagnostics;
            tsickleDiagnostics.push.apply(tsickleDiagnostics, __spread(diagnostics));
            return output;
        }));
    }
    // // For debugging: transformer that just emits the same text.
    // beforeTsTransformers.push(createTransformer(host, typeChecker, (sourceFile, sourceMapper) => {
    //   sourceMapper.addMapping(sourceFile, {position: 0, line: 0, column: 0}, {position: 0, line: 0,
    //   column: 0}, sourceFile.text.length); return sourceFile.text;
    // }));
    var tsickleTransformers = transformer_util_1.createCustomTransformers({ before: tsickleSourceTransformers });
    var tsTransformers = {
        before: __spread((customTransformers.beforeTsickle || []), (tsickleTransformers.before || []).map(function (tf) { return skipTransformForSourceFileIfNeeded(host, tf); }), (customTransformers.beforeTs || [])),
        after: __spread((customTransformers.afterTs || []), (tsickleTransformers.after || []).map(function (tf) { return skipTransformForSourceFileIfNeeded(host, tf); }))
    };
    var writeFileDelegate = writeFile || tsHost.writeFile.bind(tsHost);
    var modulesManifest = new modules_manifest_1.ModulesManifest();
    var writeFileImpl = function (fileName, content, writeByteOrderMark, onError, sourceFiles) {
        if (path.extname(fileName) !== '.map') {
            if (tsOptions.inlineSourceMap) {
                content = combineInlineSourceMaps(program, fileName, content);
            }
            else {
                content = source_map_utils_1.removeInlineSourceMap(content);
            }
            content = es5processor.convertCommonJsToGoogModuleIfNeeded(host, modulesManifest, fileName, content);
        }
        else {
            content = combineSourceMaps(program, fileName, content);
        }
        writeFileDelegate(fileName, content, writeByteOrderMark, onError, sourceFiles);
    };
    var _a = program.emit(targetSourceFile, writeFileImpl, cancellationToken, emitOnlyDtsFiles, tsTransformers), tsDiagnostics = _a.diagnostics, emitSkipped = _a.emitSkipped, emittedFiles = _a.emittedFiles;
    var externs = {};
    if (host.transformTypesToClosure) {
        var sourceFiles = targetSourceFile ? [targetSourceFile] : program.getSourceFiles();
        sourceFiles.forEach(function (sf) {
            if (isDtsFileName(sf.fileName) && host.shouldSkipTsickleProcessing(sf.fileName)) {
                return;
            }
            var _a = writeExterns(typeChecker, sf, host), output = _a.output, diagnostics = _a.diagnostics;
            if (output) {
                externs[sf.fileName] = output;
            }
            if (diagnostics) {
                tsickleDiagnostics.push.apply(tsickleDiagnostics, __spread(diagnostics));
            }
        });
    }
    // All diagnostics (including warnings) are treated as errors.
    // If the host decides to ignore warnings, just discard them.
    // Warnings include stuff like "don't use @type in your jsdoc"; tsickle
    // warns and then fixes up the code to be Closure-compatible anyway.
    tsickleDiagnostics = tsickleDiagnostics.filter(function (d) { return d.category === ts.DiagnosticCategory.Error ||
        !host.shouldIgnoreWarningsForPath(d.file.fileName); });
    return {
        modulesManifest: modulesManifest,
        emitSkipped: emitSkipped,
        emittedFiles: emittedFiles || [],
        diagnostics: __spread(tsDiagnostics, tsickleDiagnostics),
        externs: externs
    };
}
exports.emitWithTsickle = emitWithTsickle;
function skipTransformForSourceFileIfNeeded(host, delegateFactory) {
    return function (context) {
        var delegate = delegateFactory(context);
        return function (sourceFile) {
            if (host.shouldSkipTsickleProcessing(sourceFile.fileName)) {
                return sourceFile;
            }
            return delegate(sourceFile);
        };
    };
}
function combineInlineSourceMaps(program, filePath, compiledJsWithInlineSourceMap) {
    if (isDtsFileName(filePath)) {
        return compiledJsWithInlineSourceMap;
    }
    var sourceMapJson = source_map_utils_1.extractInlineSourceMap(compiledJsWithInlineSourceMap);
    compiledJsWithInlineSourceMap = source_map_utils_1.removeInlineSourceMap(compiledJsWithInlineSourceMap);
    var composedSourceMap = combineSourceMaps(program, filePath, sourceMapJson);
    return source_map_utils_1.setInlineSourceMap(compiledJsWithInlineSourceMap, composedSourceMap);
}
function combineSourceMaps(program, filePath, tscSourceMapText) {
    var tscSourceMap = source_map_utils_1.parseSourceMap(tscSourceMapText);
    if (tscSourceMap.sourcesContent) {
        // strip incoming sourcemaps from the sources in the sourcemap
        // to reduce the size of the sourcemap.
        tscSourceMap.sourcesContent = tscSourceMap.sourcesContent.map(function (content) {
            if (source_map_utils_1.containsInlineSourceMap(content)) {
                content = source_map_utils_1.removeInlineSourceMap(content);
            }
            return content;
        });
    }
    var fileDir = path.dirname(filePath);
    var tscSourceMapGenerator;
    try {
        for (var _a = __values(tscSourceMap.sources), _b = _a.next(); !_b.done; _b = _a.next()) {
            var sourceFileName = _b.value;
            var sourceFile = program.getSourceFile(path.resolve(fileDir, sourceFileName));
            if (!sourceFile || !source_map_utils_1.containsInlineSourceMap(sourceFile.text)) {
                continue;
            }
            var preexistingSourceMapText = source_map_utils_1.extractInlineSourceMap(sourceFile.text);
            if (!tscSourceMapGenerator) {
                tscSourceMapGenerator = source_map_1.SourceMapGenerator.fromSourceMap(new source_map_1.SourceMapConsumer(tscSourceMap));
            }
            tscSourceMapGenerator.applySourceMap(new source_map_1.SourceMapConsumer(source_map_utils_1.parseSourceMap(preexistingSourceMapText, sourceFileName)));
        }
    }
    catch (e_26_1) { e_26 = { error: e_26_1 }; }
    finally {
        try {
            if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
        }
        finally { if (e_26) throw e_26.error; }
    }
    return tscSourceMapGenerator ? tscSourceMapGenerator.toString() : tscSourceMapText;
    var e_26, _c;
}

//# sourceMappingURL=tsickle.js.map