"Невідомий постачальник: aProvider <- a" Як знайти оригінального постачальника?


100

Коли я завантажую мінімізовану (через UglifyJS) версію програми AngularJS, я отримую таку помилку в консолі:

Unknown provider: aProvider <- a

Тепер я усвідомлюю, що це пов'язано зі зміною імені mangling. Безпрограмна версія працює просто чудово. Тим НЕ менше, я дійсно хочу , щоб використовувати ім'я змінної перекручуючи, як це різко знижує розмір нашого вихідного файлу JS.

З цієї причини ми використовуємо ngmin в процесі збирання, але це, здається, не вирішує цю проблему, навіть якщо вона нам добре служила в минулому.

Отже, щоб налагодити цю проблему, я ввімкнув вихідні карти в нашій нерівномірній грунтовій задачі. Вони генеруються тільки штраф і Chrome робить завантаження карти з сервера. Тим не менш, я все одно отримую те саме неприйнятне повідомлення про помилку, хоча я мав враження, що зараз я повинен бачити оригінальну назву постачальника.

Як змусити Chrome використовувати вихідні карти, щоб сказати мені, в чому тут проблема, або, як я можу дізнатися провайдера іншим способом?


Ви можете спробувати додати чіткі коментарі до кожного вихідного файлу JS (якщо це вже не так) і скористатися опцією збереженняComments UglifyJS: це дасть вам уявлення про те, який файл містить неправильний код.
JB Nizet

Чи трапляється ви, що використовуєте декоратори? Я виявив, що ngmin, здається, не переписує декораторів належним чином, коли я його використовував у минулому, що призводить до помилок, як ваші.
дхерман

@JBNizet: Ідея мені подобається, але додавання цієї директиви до параметрів, здається, не має ніякого ефекту.
Der Hochstapler

@dherman: Не могли б ви надати приклад декораторів? Я не впевнений, що б вони були в цьому контексті.
Der Hochstapler

Дивіться сторінку github.com/gruntjs/grunt-contrib-uglify (якщо ви використовуєте грунт). Значення параметра має бути "всім".
JB Nizet

Відповіді:


193

Я все ще хотів би знати, як я міг знайти місце у нашому вихідному коді, що спричинив цю проблему, але я з тих пір змогла знайти проблему вручну.

Існувала функція контролера, оголошена в глобальному масштабі, замість того, щоб використовувати .controller()виклик модуля програми.

Тож було щось подібне:

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

Це добре працює для AngularJS, але, щоб він працював правильно з mangling, мені довелося змінити його на:

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

Після подальших тестів я фактично виявив кількість контролерів, які також викликали проблеми. Ось як я знайшов джерело всіх вручну :

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

options : {
    beautify : true,
    mangle   : true
}

Потім я відкрив веб-сайт проекту в Chrome, відкривши DevTools. Це призводить до помилки, подібної до наведеної нижче:

введіть тут опис зображення

Метод у сліді дзвінків, який нас цікавить, - це той, який я позначив стрілкою. Це providerInjectorвinjector.js . Ви хочете розмістити точку перерви там, де вона кидає виняток:

введіть тут опис зображення

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

введіть тут опис зображення

localsПараметр (підігнані до dв моєму коді) дає досить гарне уявлення про те, який об'єкт в джерелі є проблемою:

введіть тут опис зображення

Швидкий пошук grepнашого джерела знаходить багато примірників modalInstance, але, переходячи звідти, було легко знайти це місце у джерелі:

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

Що потрібно змінити на:

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

У разі, якщо змінна не містить корисної інформації, ви також можете перейти далі в стек і вам слід натиснути на виклик invoke якого повинні бути додаткові підказки:

введіть тут опис зображення

Запобігайте цьому повторення

Тепер, коли ви сподіваємось, що ви знайшли проблему, я вважаю, що мені слід згадати, як найкраще уникнути цього в майбутньому.

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

Стережись! У випадку, якщо ви використовуєте Angular Batarang, StrictDI може не працювати для вас, оскільки Angular Batarang вводить у ваш код ненаголошений код (погано Batarang!).

Або ви могли дозволити ng-annotate подбати про це. Я настійно рекомендую робити це, оскільки це видаляє багато потенціалу для помилок у цій галузі, наприклад:

  • Відмітка про DI відсутня
  • Анотація DI неповна
  • Анотація DI в неправильному порядку

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

Він повинен добре інтегруватися у ваш процес збирання за допомогою grunt-ng-annotate та gulp-ng-annotate .


12
Це фантастичне написання, написане обережно. Я щойно стикався з цим питанням, здається, десь глибоко в ngmin. Ваші поради допомогли мені знати, де шукати. Врешті-решт, я просто «обмацував» всі свої кутові парами, і проблема пішла. Всі попередні збірки ng-мінімізовані просто чудово, і нічого значного не змінилося. Я не додав жодних глобальних функцій - це просто перестало працювати, загадково, керуючи деяким контролером / директивою / службою / фільтром?
Зенокон

Це було чудовим джерелом допомоги. Я не знав, що ви повинні використовувати синтаксис масиву (вбудованого) також для інших функцій, таких як вирішення маршрутизатора, .run, .config тощо
VDest,

4
У моєму випадку це був контролер в директиві. Якщо у змінній 'd' ви побачите $ attr, це, мабуть, та сама проблема. Ви повинні загортати парами у дужки масиву для внутрішнього контролера директив. контролер: ["$ obseg", функція ($ область) {...}] замість контролера: функція ($ область) {...}
alex naumov

Дякую вам за ваше написання та рішення, використовуючи безпечне позначення ін'єкції / масиву для посилання на функцію var. У мене теж була ця помилка, і через ваше рішення я зміг продовжувати рухатися вперед. ти рок!
Frankie Loscavio

1
Кожен раз, коли у мене є це питання, я читаю це знову і хочу голосувати за це знову. Btw, ось як налаштувати версію gulpuglify({ output : { beautify : true }})
Євген Глухотеренко

30

Списання Олівера Зальцбурга було фантастичним. Отримано.

Порада для всіх, хто може мати цю помилку. Міна була просто спричинена тим, що забули передати масив директора-контролеру:

БАД

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

ДОБРО

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

2
Це був такий нахабний ... Uglify не спричиняв цього для мене до недавнього оновлення!
SamMorrowDrums

Моє питання було таким же, але виявляється, що мені потрібно було додати /* @ngInject */до функції. Здається зробити складну частину ін'єкції, не потребуючи
набору

25

використовувати ng-strict-di з ng-додатком

Якщо ви використовуєте ANGULAR 1.3 ви можете врятувати себе світ болю, використовуючи ngStrictDi директиву з ngApp:

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

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

Документи:

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

Одне застереження , воно лише виявляє, що є анотації, а не, що анотації є повними.

Значення:

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

Не вдасться зрозуміти, що ThingB не є частиною анотації.

Заслуга за цю пораду належить людям ng-annotate , який рекомендується застосовувати для тепер уже застарілих ngMin.


Для цього потрібно більше коштів. Це чудово підходить для налагодження програми, яка ніколи не використовувала ngInject або синтаксис рядкового масиву.
Майкл Пірсон

11

Щоб мінімізувати кутовий, все, що вам потрібно зробити, - це змінити свою декларацію на режим "оголошення масиву" на "наприклад":

Від:

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

До

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

Як декларувати фабричні послуги?

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

Я знаю. Ось чому ми використовуємо ngmin. Я підозрюю, що у нього є проблема з якоюсь частиною нашого джерела або його залежностями. Саме тому я намагаюся докорінитись до цього питання.
Der Hochstapler

1
Моя рекомендація полягає в тому, щоб ви створили свій код таким чином. Таким чином, ви можете використовувати будь-який мініфікатор
Dalorzo

3
Я буду створювати наш код таким чином. Але у нас є зовнішні залежності, яких немає. У минулому ngmin добре вирішив цю проблему для нас. Я припускаю, що нещодавні зміни створили цю проблему. Зараз я хотів би знайти джерело цієї проблеми, щоб я міг правильно виправити це в нашому коді, нашій залежності або, можливо, у самому ngmin.
Der Hochstapler

Оскільки питання звучить як дуже специфічний для певного компонента чи коду, важко дати керівництво, принаймні з мого кінця
Dalorzo

ngmin не вимагає використання режиму декларування масиву, він додає безліч марних оголошень.
Наноком

8

Я просто мав ту саму проблему і вирішив її, просто замінивши ngmin (тепер застарілий) на ng-annotate для моєї задачі зі створення грунту.

Схоже, yeoman angular також було оновлено для використання ng-annotate з цього зобов’язання: https://github.com/yeoman/generator-angular/commit/3eea4cbeb010eeaaf797c17604b4a3ab5371eccb

Однак якщо ви використовуєте старішу версію yeoman angular, як я, просто замініть ng-min на ng-annotate у своєму пакеті.json:

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

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


7

для того, щоб знати, яким було початкове ім'я змінної, ви можете змінити, як зневажити маніпулювання змінними:

../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)

EDIT

Так очевидно зараз ...

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" або що завгодно ... ви побачите це в оригінальному контексті ...

не могло бути легше ...


4

Також не забувайте про resolveвластивість маршруту. Він також повинен бути визначений як масив:

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

Це сталося зі мною, коли я додав до своїх маршрутів купу дозволів. Ви потенційно врятували мене години болісної налагодження, дякую.
Пол Макклін

3

З генератором-заглибленням-кутом:

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

}

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


2

Швидке та брудне виправлення цього, якщо вам не потрібно Uglify для маніпулювання / скорочення змінних імен, - це встановити mangle = false у вашому Gruntfile

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

Це може вирішити проблему, однак результат розміру збільшиться, оскільки вимкнено mangle.
NotABot

все ж менше, ніж зовсім не зводити нанівець
mjwrazor
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.