function getUpdater(errorFn) {
  errorFn = errorFn || new Function();
  return updater;

  /**
   * The updater function for the esprima transform
   * @param {string} file The filename for the Browserify transform
   * @param {object} ast The esprima syntax tree
   * @returns {object} The transformed esprima syntax tree
   */
  function updater(file, ast) {
    if (ast.comments) {
      ast.comments
        .filter(testDocTag)
        .map(getAnnotatedNode)
        .concat(inferAngular(ast))    // find the items that are not explicitly annotated
        .filter(testFirstOccurrence)  // ensure unique values
        .forEach(processNode);
    } else {
      errorFn('Esprima AST is required to have top-level comments array');
    }
    return ast;
  }

  /**
   * Get the node that is annotated by the comment or throw if not present.
   * @throws {Error} Where comment does not annotate a node
   * @param {object} comment The comment node
   */
  function getAnnotatedNode(comment) {

    // find the first function declaration or expression following the annotation
    var result;
    if (comment.annotates) {
      var candidateTrees;

      // consider the context the block is in (i.e. what is its parent)
      var parent = comment.annotates.parent;

      // consider nodes from the annotated node forward
      //  include the first non-generated node and all generated nodes preceding it
      if (testNode.isBlockOrProgram(parent)) {
        var body = parent.body;
        var index = body.indexOf(comment.annotates);
        var candidates = body.slice(index);
        var length = candidates.map(testNode.isGeneratedCode).indexOf(false) + 1;
        candidateTrees = candidates.slice(0, length || candidates.length);
      }
      // otherwise we can only consider the given node
      else {
        candidateTrees = [comment.annotates];
      }

      // try the nodes
      while (!result && candidateTrees.length) {
        result = esprimaTools
          .orderNodes(candidateTrees.shift())
          .filter(testNode.isFunctionNotIFFE)
          .shift();
      }
    }

    // throw where not valid
    if (result) {
      return result;
    } else {
      errorFn('Doc-tag @ngInject does not annotate anything');
    }
  }
}
function getUpdater(errorFn) {
  errorFn = errorFn || new Function();
  return updater;

  
  function updater(file, ast) {
    if (ast.comments) {
      ast.comments
        .filter(testDocTag)
        .map(getAnnotatedNode)
        .concat(inferAngular(ast))    
        .filter(testFirstOccurrence)  
        .forEach(processNode);
    } else {
      errorFn('Esprima AST is required to have top-level comments array');
    }
    return ast;
  }

  
  function getAnnotatedNode(comment) {

    
    var result;
    if (comment.annotates) {
      var candidateTrees;

      
      var parent = comment.annotates.parent;

      
      
      if (testNode.isBlockOrProgram(parent)) {
        var body = parent.body;
        var index = body.indexOf(comment.annotates);
        var candidates = body.slice(index);
        var length = candidates.map(testNode.isGeneratedCode).indexOf(false) + 1;
        candidateTrees = candidates.slice(0, length || candidates.length);
      }
      
      else {
        candidateTrees = [comment.annotates];
      }

      
      while (!result && candidateTrees.length) {
        result = esprimaTools
          .orderNodes(candidateTrees.shift())
          .filter(testNode.isFunctionNotIFFE)
          .shift();
      }
    }

    
    if (result) {
      return result;
    } else {
      errorFn('Doc-tag @ngInject does not annotate anything');
    }
  }
}
function getUpdater ( errorFn ) { errorFn = errorFn || new Function ( ) ; return updater ; /**
   * The updater function for the esprima transform
   * @param {string} file The filename for the Browserify transform
   * @param {object} ast The esprima syntax tree
   * @returns {object} The transformed esprima syntax tree
   */ function updater ( file , ast ) { if ( ast . comments ) { ast . comments . filter ( testDocTag ) . map ( getAnnotatedNode ) . concat ( inferAngular ( ast ) ) // find the items that are not explicitly annotated . filter ( testFirstOccurrence ) // ensure unique values . forEach ( processNode ) ; } else { errorFn ( 'Esprima AST is required to have top-level comments array' ) ; } return ast ; } /**
   * Get the node that is annotated by the comment or throw if not present.
   * @throws {Error} Where comment does not annotate a node
   * @param {object} comment The comment node
   */ function getAnnotatedNode ( comment ) { // find the first function declaration or expression following the annotation var result ; if ( comment . annotates ) { var candidateTrees ; // consider the context the block is in (i.e. what is its parent) var parent = comment . annotates . parent ; // consider nodes from the annotated node forward //  include the first non-generated node and all generated nodes preceding it if ( testNode . isBlockOrProgram ( parent ) ) { var body = parent . body ; var index = body . indexOf ( comment . annotates ) ; var candidates = body . slice ( index ) ; var length = candidates . map ( testNode . isGeneratedCode ) . indexOf ( false ) + 1 ; candidateTrees = candidates . slice ( 0 , length || candidates . length ) ; } // otherwise we can only consider the given node else { candidateTrees = [ comment . annotates ] ; } // try the nodes while ( ! result && candidateTrees . length ) { result = esprimaTools . orderNodes ( candidateTrees . shift ( ) ) . filter ( testNode . isFunctionNotIFFE ) . shift ( ) ; } } // throw where not valid if ( result ) { return result ; } else { errorFn ( 'Doc-tag @ngInject does not annotate anything' ) ; } } }
Create an updater function for the esprima transform with the given error handler
@param {function} [errorFn] An optional error handler function
@returns {function(string, object):object} A method that transforms the esprima tree
Create an updater function for the esprima transform with the given error handler