text: "use strict";
  Object.defineProperty(exports, "__esModule", { value: true });
  /**
   * @license
   * Copyright Google Inc. All Rights Reserved.
   *
   * Use of this source code is governed by an MIT-style license that can be
   * found in the LICENSE file at https://angular.io/license
   */
  const ts = require("typescript");
  const ast_utils_1 = require("../helpers/ast-utils");
  function getPrefixFunctionsTransformer() {
      return (context) => {
          const transformer = (sf) => {
              const topLevelFunctions = findTopLevelFunctions(sf);
              const visitor = (node) => {
                  // Add pure function comment to top level functions.
                  if (topLevelFunctions.has(node)) {
                      const newNode = ast_utils_1.addPureComment(node);
                      // Replace node with modified one.
                      return ts.visitEachChild(newNode, visitor, context);
                  }
                  // Otherwise return node as is.
                  return ts.visitEachChild(node, visitor, context);
              };
              return ts.visitNode(sf, visitor);
          };
          return transformer;
      };
  }
  exports.getPrefixFunctionsTransformer = getPrefixFunctionsTransformer;
  function findTopLevelFunctions(parentNode) {
      const topLevelFunctions = new Set();
      function cb(node) {
          // Stop recursing into this branch if it's a definition construct.
          // These are function expression, function declaration, class, or arrow function (lambda).
          // The body of these constructs will not execute when loading the module, so we don't
          // need to mark function calls inside them as pure.
          // Class static initializers in ES2015 are an exception we don't cover. They would need similar
          // processing as enums to prevent property setting from causing the class to be retained.
          if (ts.isFunctionLike(node)
              || ts.isClassLike(node)
              || ts.isArrowFunction(node)
              || ts.isMethodDeclaration(node)) {
              return;
          }
          let noPureComment = !ast_utils_1.hasPureComment(node);
          let innerNode = node;
          while (innerNode && ts.isParenthesizedExpression(innerNode)) {
              innerNode = innerNode.expression;
              noPureComment = noPureComment && !ast_utils_1.hasPureComment(innerNode);
          }
          if (!innerNode) {
              return;
          }
          if ((ts.isFunctionExpression(innerNode) || ts.isArrowFunction(innerNode))
              && ts.isParenthesizedExpression(node)) {
              // pure functions can be wrapped in parentizes
              // we should not add pure comments to this sort of syntax.
              // example var foo = (() => x)
              return;
          }
          if (noPureComment) {
              if (ts.isNewExpression(innerNode)) {
                  topLevelFunctions.add(node);
              }
              else if (ts.isCallExpression(innerNode)) {
                  let expression = innerNode.expression;
                  if (ts.isIdentifier(expression) && ast_utils_1.isHelperName(expression.text)) {
                      return;
                  }
                  while (expression && ts.isParenthesizedExpression(expression)) {
                      expression = expression.expression;
                  }
                  if (expression) {
                      if (ts.isFunctionExpression(expression)) {
                          // Skip IIFE's with arguments
                          // This could be improved to check if there are any references to variables
                          if (innerNode.arguments.length === 0) {
                              topLevelFunctions.add(node);
                          }
                      }
                      else {
                          topLevelFunctions.add(node);
                      }
                  }
              }
          }
          ts.forEachChild(innerNode, cb);
      }
      ts.forEachChild(parentNode, cb);
      return topLevelFunctions;
  }
  exports.findTopLevelFunctions = findTopLevelFunctions;
  