Search code examples
javascriptgoogle-closure-compiler

Closure Compiler, missing method with advanced optimizations?


I'm using the closure compiler in an angularjs application. My JS compiles without errors (or warnings) and works fine with SIMPLE optimizations. Specifically, I have the following warnings/checks enabled:

--jscomp_warning=checkTypes \
--jscomp_error=checkVars \
--jscomp_warning=deprecated \
--jscomp_error=duplicate \
--jscomp_warning=globalThis \
--jscomp_warning=missingProperties \
--jscomp_warning=undefinedNames \
--jscomp_error=undefinedVars \

However, when I try to compile using ADVANCED OPTIMIZATIONS, I get the following error:

TypeError: a.handleEvent is not a function
    at Sj.Nh.a.(anonymous function).a.(anonymous function) (http://localhost:10080/main/pattern.dots-0-7-7-258310cc.compiled.js:118:273)
    at $h (http://app.js:120:424)
    at R (http://app.js:119:337)
    at lj (http://app.js:144:380)
    at Sj.f.re (http://app.js:151:622)
    at mo (http://app.js:302:171)
    at to (http://app.js:316:78)
    at link (http://app.js:308:335)
    ...

which appears to be related to event handling code (from looking at the source map):

/**
 * @param {Object|Function} listener The listener function or an
 *     object that contains handleEvent method.
 * @return {!Function} Either the original function or a function that
 *     calls obj.handleEvent. If the same listener is passed to this
 *     function more than once, the same function is guaranteed to be
 *     returned.
 */
goog.events.wrapListener = function(listener) {
  goog.asserts.assert(listener, 'Listener can not be null.');

  if (goog.isFunction(listener)) {
    return listener;
  }

  goog.asserts.assert(
      listener.handleEvent, 'An object listener must have handleEvent method.');
  if (!listener[goog.events.LISTENER_WRAPPER_PROP_]) {
    listener[goog.events.LISTENER_WRAPPER_PROP_] =
        function(e) { return listener.handleEvent(e); };
  }
  return listener[goog.events.LISTENER_WRAPPER_PROP_];
};

But this seems really odd. After all, there's an assert a couple lines up that (apparently) passes. I think I've tried to keep the assert (-D goog.asserts.ENABLE_ASSERTS) and even if the assert is optimized away, I don't understand why it would work with SIMPLE optimizations (where the assert would still be present). Also, the code still works if I compile with advanced optimizations and --debug, which looks like it starts the process of namespace collapsing, but doesn't go all the way.

Interestingly enough, if I try adding some console.log statements:

goog.events.wrapListener = function(listener) {
  goog.asserts.assert(listener, 'Listener can not be null.');

  if (goog.isFunction(listener)) {
    return listener;
  }

  goog.asserts.assert(
      listener.handleEvent, 'An object listener must have handleEvent method.');
  if (!listener[goog.events.LISTENER_WRAPPER_PROP_]) {
    listener[goog.events.LISTENER_WRAPPER_PROP_] =
        function(e) {
          console.log(e);
          console.log(listener);
          console.log(typeof listener);
          console.log(listener.handleEvent);
          console.log(typeof listener.handleEvent);
          return listener.handleEvent(e);
        };
  }
  return listener[goog.events.LISTENER_WRAPPER_PROP_];
};

And I see that the typeof listener is 'function'. But if that's the case, how did we get here in the first place? (certainly goog.isFunction(listener) should have returned true at that point ...).

I'm a bit at a loss as to what might be going on here...


Solution

  • Well, It seems that after countless hours of trying to debug this one, the answer falls into my lap right after I post the question (again).

    I was unlucky enough to have goog.isFunction compile down to the symbol ga -- which conflicts with the global usage tracking library "Google Analytics".

    The solution for me was to just include the universal_analytics_api.js in my externs and all seems to be well with the world.