"Неизвестный поставщик: aProvider <- a" Как найти исходного поставщика?

Когда я загружаю мини-версию (через UglifyJS) моего приложения AngularJS, я получаю следующую ошибку в консоли:

Unknown provider: aProvider <- a

Теперь я понимаю, что это связано с изменением имени переменной. Неизвязанная версия работает отлично. Тем не менее, я хочу использовать переменную name mangling, так как она резко уменьшает размер нашего выходного файла JS.

По этой причине мы используем ngmin в нашем процессе сборки, но, похоже, это не разрешает эту проблему, даже хотя в прошлом он служил нам хорошо.

Итак, чтобы отладить эту проблему, я включил исходные карты в нашу задачу uglify grunt. Они генерируются просто отлично, и Chrome загружает карты с сервера. Тем не менее, я все равно получаю такое же бесполезное сообщение об ошибке, хотя у меня создалось впечатление, что теперь я должен увидеть исходное имя провайдера.

Как мне заставить Chrome использовать исходные карты, чтобы сообщить мне, какой провайдер является проблемой здесь, или, в качестве альтернативы, как я могу узнать поставщика по-другому?

Ответ 1

Мне все еще хотелось бы знать, как я мог найти место в нашем исходном коде, вызвавшее эту проблему, но с тех пор мне удалось найти проблему вручную.

В глобальной области действия была объявлена ​​функция контроллера, вместо использования вызова .controller() в прикладном модуле.

Так было что-то вроде этого:

function SomeController( $scope, i18n ) { /* ... */ }

Это отлично работает для AngularJS, но для того, чтобы он работал правильно с помощью mangling, мне пришлось изменить его на:

var applicationModule = angular.module( "example" );
function SomeController( $scope, i18n ) { /* ... */ }
applicationModule.controller( "SomeController", [ "$scope", "i18n", SomeController ] );

После дополнительных тестов я действительно обнаружил экземпляры большего количества контроллеров, которые также вызвали проблемы. Вот как я нашел источник всех из них вручную:

Прежде всего, я считаю весьма важным включить выделение украшений в опциях uglify. Для нашей задачи ворча, которая означала:

options : {
    beautify : true,
    mangle   : true
}

Затем я открыл веб-сайт проекта в Chrome, открыв DevTools. Это приводит к ошибке, подобной приведенной ниже:

enter image description here

Метод в интересующей нас трассе вызовов - это тот, который я обозначил стрелкой. Это providerInjector в injector.js. Вы захотите разместить точку останова, где она выдает исключение:

enter image description here

Когда вы снова запустите приложение, точка останова будет удалена, и вы сможете вскочить в стек вызовов. Будет вызван вызов invoke в injector.js, распознаваемый из строки "Неправильная инъекция":

enter image description here

Параметр locals (искаженный до d в моем коде) дает довольно хорошее представление о том, какой объект в вашем источнике является проблемой:

enter image description here

Быстрый grep над нашим источником находит много экземпляров modalInstance, но оттуда было легко найти это место в источнике:

var ModalCreateEditMeetingController = function( $scope, $modalInstance ) {
};

Которое должно быть изменено на:

var ModalCreateEditMeetingController = [ "$scope", "$modalInstance", function( $scope, $modalInstance ) {
} ];

Если переменная не содержит полезной информации, вы также можете перейти дальше по стеку, и вы должны нажать на вызов invoke, который должен иметь дополнительные подсказки:

enter image description here

Предотвратить повторение этого события

Теперь, когда вы, надеюсь, нашли проблему, я чувствую, что я должен упомянуть, как лучше избегать этого, чтобы это происходило снова в будущем.

Очевидно, что вы можете просто использовать встроенную аннотацию массива во всем мире или (в зависимости от ваших предпочтений) $inject аннотация свойств и попробуйте не забывать об этом в будущем. Если вы это сделаете, обязательно включите режим строгого режима вложения, чтобы ловить ошибки, подобные этому раньше.

Остерегайтесь! Если вы используете Angular Batarang, StrictDI может не работать для вас, так как Angular Batarang вводит неавтоматизированный код в ваш (плохой Batarang!).

Или вы можете позволить ng-annotate позаботиться об этом. Я очень рекомендую это сделать, поскольку он устраняет большой риск ошибок в этой области, например:

  • Отсутствие аннотации DI
  • Аннотации DI не завершены
  • Аннотации DI в неправильном порядке

Сохранение аннотаций в актуальном состоянии - это просто боль в заднице, и вам не нужно делать это, если это можно сделать автоматически. ng-annotate делает именно это.

Он должен хорошо интегрироваться в ваш процесс сборки с помощью grunt-ng-annotate и gulp-ng-annotate.

Ответ 2

Рецензия Оливера Зальцбурга была фантастической. Upvoted.

Совет для всех, кто может иметь эту ошибку. Мой просто был вызван тем, что забыл передать массив для директивного контроллера:

BAD

return {
    restrict: "E",
    scope: {                
    },
    controller: ExampleDirectiveController,
    templateUrl: "template/url/here.html"
};

ХОРОШИЕ

return {
    restrict: "E",
    scope: {                
    },
    controller: ["$scope", ExampleDirectiveController],
    templateUrl: "template/url/here.html"
};

Ответ 3

используйте ng-strict-di с ng-app

Если вы используете Angular 1.3, вы можете сэкономить себе мир с помощью ngStrictDi с ngApp:

<html lang="en" ng-app="myUglifiablyGreatApp" ng-strict-di>

Теперь - предварительная минификация - все, что не использует аннотации, взорвет вашу консоль, и вы можете увидеть имя friggin без поиска через чередующиеся трассировки стека.

В документах:

приложение не сможет вызывать функции, которые не используют явную аннотацию функций (и поэтому не подходят для минимизации)

Предостережение, оно обнаруживает только аннотации, а не завершение аннотаций.

Значение:

['ThingOne', function(ThingA, ThingB) { … }]

Не поймает, что ThingB не является частью аннотации.

Кредит для этого наконечника относится к ng-annotate людям, которые рекомендуют использовать устаревший ngMin.

Ответ 4

Чтобы минимизировать angular все, что вам нужно - это изменить объявление в режим объявления "массив", например:

From:

var demoApp= angular.module('demoApp', []);
demoApp.controller(function demoCtrl($scope) {
} );

Для

var demoApp= angular.module('demoApp', []);
demoApp.controller(["$scope",function demoCtrl($scope) {
}]);

Как объявить factory услуги?

demoApp.factory('demoFactory', ['$q', '$http', function ($q, $http) {
    return {
          //some object
    };
}]);

Ответ 5

У меня была одна и та же проблема и разрешила ее, просто заменив ngmin (теперь устаревшим) на ng-annotate для моей задачи сборки grunt.

Кажется, что yoman angular также был обновлен, чтобы использовать ng-annotate в качестве этого коммита: https://github.com/yeoman/generator-angular/commit/3eea4cbeb010eeaaf797c17604b4a3ab5371eccb

Однако, если вы используете более старую версию yoman angular, как я, просто замените ng-min на ng-annotate в вашем пакете. json:

-    "grunt-ngmin": "^0.0.3",
+    "grunt-ng-annotate": "^0.3.0",

запустите npm install (затем необязательно npm prune) и следуйте изменениям commit, чтобы отредактировать Gruntfile.js.

Ответ 6

чтобы узнать, что такое имя исходной переменной, вы можете изменить способ обнуления переменных:

../node_modules/grunt-contrib-uglify/node_modulesuglify-js/lib/scope.js

SymbolDef.prototype = {
  unmangleable: [...],
  mangle: function(options) {
    [...]
    this.mangled_name = s.next_mangled(options, this)+"_orig_"+this.orig[0].name;
    [...]
  }
};

и теперь ошибка гораздо более очевидна.

Error: [$injector:unpr] Unknown provider: a_orig_$stateProvider
http://errors.angularjs.org/1.3.7/$injector/unpr?p0=a_orig_%24stateProvider
at eval (eval at <anonymous> (http://example.com/:64:17), <anonymous>:3155:20)

ИЗМЕНИТЬ

Теперь так очевидно...

Gruntfile.js

uglify: {
  example: {
    options: {
      beautify: true,
      mangle: true
    },
    [...]
  },
  [...]
}

../node_modules/grunt-contrib-uglify/node_modulesuglify-js/lib/scope.js

var numberOfVariables = 1;
SymbolDef.prototype = {
  unmangleable: [...],
  mangle: function(options) {
    [...]
    this.mangled_name = s.next_mangled(options, this)+"_orig_"+this.orig[0].name+"_"+numberOfVariables++;
    [...]
  }
};

теперь каждая переменная искажается до уникального значения, которое также содержит оригинал... просто откройте миниатюрный javascript и найдите "a_orig_ $stateProvider_91212" или что-то еще... вы увидите его в этом исходном контексте...

не может быть проще...

Ответ 7

Также не забудьте свойство resolve маршрута. Он также должен быть определен как массив:

$routeProvider.when('/foo', {
    resolve: {
        bar: ['myService1', function(myService1) {
            return myService1.getThis();
        }],
        baz: ['myService2', function(myService2) {
            return myService2.getThat();
        }]
    }
});

Ответ 8

Быстрое и грязное исправление для этого, если вы не требуете, чтобы Uglify уменьшал/сокращал имена переменных, нужно установить mangle = false в свой файл Gruntfile

    uglify: {
        compile: {
            options: {
                mangle   : false,
                ...
            },
        }
    }

Ответ 9

С генератором gulp - angular:

   /** @ngInject */
    function SomeController($scope, myCoolService) {

}

Напишите /** @ngInject */ перед каждым контроллером, службой, директивой.