"Невідомий постачальник: 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.