Skip to content

Instantly share code, notes, and snippets.

@henrahmagix
Last active January 4, 2017 16:09
Show Gist options
  • Save henrahmagix/5c18085685ee459cfb31b6e1f04c9424 to your computer and use it in GitHub Desktop.
Save henrahmagix/5c18085685ee459cfb31b6e1f04c9424 to your computer and use it in GitHub Desktop.
Find non-annotated Angular v1 things that will break when minified. Can be used with ngmin/ng-annotate.

Find Angular v1 things without annotated dependencies

Annoyed by this error?

Error: [$injector:unpr] Unknown provider: cProvider <- c

This will help find non-annotated Angular things that will break when minified. Can also be used with ngmin/ng-annotate and ng-strict-di to find instances where they didn't apply a fix or throw an informative error.

To do this, we must edit angular.js.

In the following code blocks, the expression assigned to fnArgs is taken from the annotate function. The variables should be available within the angular.js IIFE.

How does this work?

We will find Angular things declared without an array of string dependencies around the definition (Inline Array Annotation) or on an $inject property ($inject Property Annotation) and log them to the console. Without declared dependency names, the "magic" method (Implicit Annotation) is used to determine them, which breaks on minified code.

ngmin/ng-annotate solve this issue by finding such things with undeclared dependencies and annotating them before they're minified. However, if you're having a problem with ngmin/ng-annotate (see example) or not using them at all, this can help you.

Make sure your minifier does not remove console logs: they are used to notify of unannotated things.

Fix

in providers

Find the instantiate function in the $injector definition and add the following before invoke is called, where Type is the first argument to instantiate.

if (isFunction(Type) && (!Type.$inject || !Type.$inject.length)) {
  var fnArgs = Type.toString().replace(STRIP_COMMENTS, '').match(FN_ARGS)[1].split(FN_ARG_SPLIT);
  if (fnArgs && fnArgs.length && fnArgs[0].length) {
    console.log('provider needs inline deps', fnArgs, Type);
  }
}

You can search for @name $injector and scroll down, or search directly for the instantiate function definition if your editor allows.

in controllers

Find the $ControllerProvider() function and add the following before $injector.instantiate is called, where expression and locals are the first and second arguments to the returned function in this.$get.

if (isFunction(expression) && (!expression.$inject || !expression.$inject.length)) {
  var fnArgs = expression.toString().replace(STRIP_COMMENTS, '').match(FN_ARGS)[1].split(FN_ARG_SPLIT);
  if (fnArgs && fnArgs.length) {
    console.log('$controller needs inline deps', fnArgs, expression, locals);
  }
}

Example

  • angular@1.2.15
  • grunt-ngmin@0.0.3

The following directive controller is found by ngmin and annotated correctly.

myModule.directive('myDirective', function () {
  return {
    controller: function ($scope) {
      $scope.name = 'foo';
    },
    template: '<div>My name is {{name}}</div>'
  };
});

However, when the directive is directly annotated, ngmin fails to annotate the controller within.

myModule.directive('myDirective', [
  'myService',
  function (myService) {
    return {
      controller: function ($scope) {
        $scope.name = 'foo';
      },
      template: '<div>My name is {{name}}</div>'
    };
  }
]);

The same occurs with using ng-strict-di on the same element as ng-app (see the AngularJS guide).

ng-annotate has no problem with the above example: it finds and annotates the controller correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment