“Penyedia tidak dikenal: aProvider <- a” Bagaimana cara menemukan penyedia asli?

100

Saat saya memuat versi yang diperkecil (melalui UglifyJS) dari aplikasi AngularJS saya, saya mendapatkan kesalahan berikut di konsol:

Unknown provider: aProvider <- a

Sekarang, saya menyadari bahwa ini karena nama variabel mangling. Versi yang tidak diubah berfungsi dengan baik. Namun, saya tidak ingin menggunakan variabel nama mangling, karena secara drastis mengurangi ukuran file output JS kami.

Oleh karena itu, kami menggunakan ngmin dalam proses pembuatan kami, tetapi tampaknya tidak menyelesaikan masalah ini, meskipun ini membantu kami dengan baik di masa lalu.

Jadi, untuk men-debug masalah ini, saya mengaktifkan peta sumber di tugas grunt uglify kami. Mereka dihasilkan baik-baik saja dan Chrome tidak memuat peta dari server. Namun, saya masih mendapatkan pesan kesalahan tidak membantu yang sama, meskipun saya mendapat kesan bahwa sekarang saya harus melihat nama asli penyedia.

Bagaimana cara membuat Chrome menggunakan peta sumber untuk memberi tahu saya penyedia mana yang bermasalah di sini, atau, sebagai alternatif, bagaimana saya bisa mengetahui penyedia dengan cara lain?

Der Hochstapler
sumber
Anda dapat mencoba menambahkan komentar yang berbeda ke setiap file sumber JS (jika belum demikian), dan menggunakan opsi keepComments dari UglifyJS: yang akan memberi Anda gambaran tentang file mana yang berisi kode yang salah.
JB Nizet
Apakah Anda kebetulan menggunakan dekorator? Saya telah menemukan bahwa ngmin tampaknya tidak menulis ulang dekorator dengan benar ketika saya menggunakannya di masa lalu yang menghasilkan kesalahan seperti milik Anda.
dherman
@JBNizet: Saya suka idenya, tetapi menambahkan arahan itu ke opsi sepertinya tidak berpengaruh apa pun.
Der Hochstapler
@dherman: Bisakah Anda memberi saya contoh dekorator? Saya tidak yakin akan seperti apa mereka dalam konteks ini.
Der Hochstapler
Lihat github.com/gruntjs/grunt-contrib-uglify (jika Anda menggunakan grunt). Nilai opsi harus "semua".
JB Nizet

Jawaban:

193

Saya masih ingin tahu bagaimana saya dapat menemukan tempat di kode sumber kami yang menyebabkan masalah ini, tetapi sejak itu saya dapat menemukan masalahnya secara manual.

Ada fungsi pengontrol yang dideklarasikan pada cakupan global, alih-alih menggunakan .controller()panggilan pada modul aplikasi.

Jadi ada yang seperti ini:

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

Ini berfungsi dengan baik untuk AngularJS, tetapi untuk membuatnya berfungsi dengan benar dengan mangling, saya harus mengubahnya menjadi:

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

Setelah tes lebih lanjut, saya benar-benar menemukan contoh lebih banyak pengontrol yang juga menyebabkan masalah. Beginilah cara saya menemukan sumber semuanya secara manual :

Pertama-tama, saya menganggap cukup penting untuk mengaktifkan kecantikan keluaran dalam opsi uglify. Untuk tugas kasar kami yang berarti:

options : {
    beautify : true,
    mangle   : true
}

Saya kemudian membuka situs web proyek di Chrome, dengan DevTools terbuka. Yang menghasilkan kesalahan seperti yang di bawah ini sedang dicatat:

masukkan deskripsi gambar di sini

Metode dalam pelacakan panggilan yang kami minati, adalah yang saya tandai dengan panah. Ini providerInjectormasukinjector.js . Anda akan ingin menempatkan breakpoint di mana ia mengeluarkan pengecualian:

masukkan deskripsi gambar di sini

Saat Anda sekarang menjalankan ulang aplikasi, breakpoint akan terkena dan Anda dapat melompat ke tumpukan panggilan. Akan ada panggilan dari invokedalaminjector.js , dikenali dari string "Token injeksi salah":

masukkan deskripsi gambar di sini

The localsparameter (hancur ke ddalam kode saya) memberikan ide yang cukup baik tentang yang objek dalam sumber Anda adalah masalah:

masukkan deskripsi gambar di sini

Sekilas grepsumber kami menemukan banyak contoh modalInstance, tetapi pergi dari sana, mudah untuk menemukan tempat ini di sumber:

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

Yang harus diubah menjadi:

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

Jika variabel tidak menyimpan informasi yang berguna, Anda juga dapat melompat lebih jauh ke atas tumpukan dan Anda harus menekan panggilan invokeyang seharusnya memiliki petunjuk tambahan:

masukkan deskripsi gambar di sini

Cegah hal ini terjadi lagi

Sekarang setelah Anda menemukan masalahnya, saya merasa bahwa saya harus menyebutkan cara terbaik untuk menghindari hal ini terjadi lagi di masa mendatang.

Jelas, Anda bisa saja menggunakan anotasi larik sebaris di mana-mana, atau $injectanotasi properti (bergantung pada preferensi Anda) dan mencoba untuk tidak melupakannya di masa mendatang. Jika Anda melakukannya, pastikan untuk mengaktifkan mode injeksi ketergantungan ketat , untuk menangkap kesalahan seperti ini sedini mungkin.

Awas! Jika Anda menggunakan Angular Batarang, StrictDI mungkin tidak berfungsi untuk Anda, karena Angular Batarang menyuntikkan kode tanpa pemberitahuan ke akun Anda (Batarang buruk!).

Atau Anda bisa membiarkan ng-anotasi mengurusnya. Saya sangat merekomendasikan melakukannya, karena ini menghilangkan banyak potensi kesalahan di area ini, seperti:

  • Anotasi DI hilang
  • Anotasi DI tidak lengkap
  • Anotasi DI dalam urutan yang salah

Menjaga anotasi tetap mutakhir hanya merepotkan dan Anda tidak perlu melakukannya jika dapat dilakukan secara otomatis. ng-annotate melakukan hal itu.

Ini harus terintegrasi dengan baik ke dalam proses build Anda dengan grunt-ng-annotate dan gulp-ng-annotate .

Der Hochstapler
sumber
12
Ini adalah artikel yang luar biasa, ditulis dengan hati-hati. Saya baru saja mengalami masalah ini, sepertinya ada masalah jauh di ngmin di suatu tempat. Kiat Anda membantu saya mengetahui ke mana harus mencari. Pada akhirnya saya hanya melakukan "array-ified" semua parameter sudut saya, dan masalahnya hilang. Semua build sebelumnya ng-minified dengan baik, dan tidak ada yang berubah. Saya tidak menambahkan fungsi global - itu hanya berhenti bekerja, secara misterius, dengan merusak beberapa controller / directive / service / filter?
zenocon
Ini adalah sumber bantuan yang luar biasa. Saya tidak tahu Anda harus menggunakan sintaks array (inline) juga untuk fungsi lain, seperti penyelesaian router, .run, .config, dll.
VDest
4
Dalam kasus saya itu adalah pengontrol dalam direktif. Jika dalam variabel 'd' Anda akan melihat $ attr, itu mungkin masalah yang sama. Anda harus membungkus params dalam tanda kurung larik untuk pengontrol direktif dalam. controller: ["$ scope", function ($ scope) {...}] alih-alih controller: function ($ scope) {...}
alex naumov
Terima kasih banyak atas penulisan dan solusi Anda menggunakan notasi larik / injeksi dependensi yang aman untuk referensi fungsi var. Saya juga mengalami kesalahan ini dan karena solusi Anda, saya dapat terus bergerak maju. kamu keren!
Frankie Loscavio
1
Setiap kali saya mengalami masalah ini, saya membaca ini lagi dan ingin memilih ini lagi. Btw, berikut adalah cara mengatur versi uglify({ output : { beautify : true }})
gulp
30

Tulisan Oliver Salzburg sangat fantastis. Suara positif.

Tip untuk siapa saja yang mungkin mengalami kesalahan ini. Milik saya hanya disebabkan oleh lupa untuk mengirimkan array untuk pengontrol direktif:

BURUK

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

BAIK

return {
    restrict: "E",
    scope: {                
    },
    controller: ["$scope", ExampleDirectiveController],
    templateUrl: "template/url/here.html"
};
Ash Clarke
sumber
2
Ini sangat nakal ... Uglify tidak menyebabkan ini untuk saya sampai pembaruan terkini!
SamMorrowDrums
Masalah saya sama, tetapi ternyata yang perlu saya tambahkan adalah /* @ngInject */sebelum fungsinya. Tampaknya melakukan bagian injeksi yang rumit tanpa perlu mengetikkan setiap modul yang disertakan (Saya menggunakan Yeoman)
Nicholas Blasgen
25

gunakan ng-strict-di dengan ng-app

Jika Anda menggunakan sudut 1,3 Anda dapat menyimpan sendiri dunia yang terluka dengan menggunakan ngStrictDi direktif dengan ngApp:

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

Sekarang - pra-minifikasi - apa pun yang tidak menggunakan anotasi akan meledakkan konsol Anda dan Anda dapat melihat nama sialan itu tanpa memburu melalui jejak tumpukan yang rusak.

Per dokumen:

aplikasi akan gagal menjalankan fungsi yang tidak menggunakan anotasi fungsi eksplisit (dan karenanya tidak cocok untuk minifikasi)

Satu peringatan , hanya mendeteksi bahwa ada yang penjelasan, tidak bahwa penjelasan lengkap.

Berarti:

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

Tidak akan menangkap bahwa ThingB bukan bagian dari anotasi.

Penghargaan untuk tip ini diberikan kepada orang - orang ng-anotasi , yang direkomendasikan daripada ngMin yang sekarang sudah usang.

Mark Fox
sumber
Ini membutuhkan lebih banyak suara positif. Ini bagus untuk men-debug aplikasi yang tidak pernah menggunakan ngInject atau sintaks array string.
Michael Pearson
11

Untuk memperkecil sudut, yang perlu Anda lakukan adalah mengubah deklarasi Anda ke mode "deklarasi" "array", misalnya:

Dari:

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

Untuk

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

Bagaimana cara mendeklarasikan layanan pabrik?

demoApp.factory('demoFactory', ['$q', '$http', function ($q, $http) {
    return {
          //some object
    };
}]);
Dalorzo
sumber
Aku tahu. Karena itulah kami menggunakan ngmin. Saya curiga ada masalah dengan beberapa bagian dari sumber kami atau ketergantungannya. Itulah mengapa saya mencoba mencari akar dari masalah ini.
Der Hochstapler
1
Rekomendasi saya adalah Anda membuat kode dengan cara ini. Jadi, Anda dapat menggunakan penambang apa pun
Dalorzo
3
Aku sedang menciptakan kode kita dengan cara ini. Tetapi kami memiliki ketergantungan eksternal yang tidak. ngmin telah menyelesaikan masalah ini dengan baik untuk kami di masa lalu. Saya berasumsi bahwa perubahan baru-baru ini menciptakan masalah ini. Sekarang saya ingin menemukan sumber masalah ini sehingga saya dapat memperbaikinya dengan benar di kode kita, ketergantungan kita atau mungkin di ngmin itu sendiri.
Der Hochstapler
Karena masalah terdengar seperti sangat spesifik untuk komponen atau kode tertentu sulit untuk memberikan panduan, setidaknya dari akhir saya
Dalorzo
ngmin tidak mengharuskan Anda untuk menggunakan mode deklarasi array, ia menambahkan banyak deklarasi yang tidak berguna.
Nanocom
8

Saya baru saja mengalami masalah yang sama dan menyelesaikannya hanya dengan mengganti ngmin (sekarang sudah tidak digunakan lagi) dengan ng-annotate untuk tugas build grunt saya.

Tampaknya sudut yeoman juga telah diperbarui untuk menggunakan ng-annotate pada komit ini: https://github.com/yeoman/generator-angular/commit/3eea4cbeb010eeaaf797c17604b4a3ab5371eccb

Namun jika Anda menggunakan yeoman angular versi lama seperti saya, ganti saja ng-min dengan ng-annotate di package.json Anda:

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

jalankan npm install(lalu opsional npm prune), dan ikuti perubahan dalam komit untuk mengedit Gruntfile.js.

Xuwen
sumber
7

untuk mengetahui apa nama variabel aslinya, Anda dapat mengubah cara uglify menghapus variabel:

../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;
    [...]
  }
};

dan sekarang kesalahannya jauh lebih jelas

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

Sangat jelas sekarang ...

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++;
    [...]
  }
};

sekarang setiap variabel dihancurkan menjadi nilai unik yang juga berisi yang asli ... cukup buka javascript yang diperkecil dan cari "a_orig_ $ stateProvider_91212" atau apa pun ... Anda akan melihatnya dalam konteks aslinya ...

tidak bisa lebih mudah ...

pengguna3338098
sumber
4

Juga jangan lupakan resolveproperti rutenya. Itu juga harus didefinisikan sebagai array:

$routeProvider.when('/foo', {
    resolve: {
        bar: ['myService1', function(myService1) {
            return myService1.getThis();
        }],
        baz: ['myService2', function(myService2) {
            return myService2.getThat();
        }]
    }
});
Petr Felzmann
sumber
Ini terjadi pada saya ketika saya menambahkan banyak resolusi ke rute saya. Anda berpotensi menghemat berjam-jam debugging yang menyakitkan, terima kasih.
Paul McClean
3

Dengan generator-gulp-angular:

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

}

Tulis / ** @ngInject * / sebelum setiap pengontrol, layanan, direktif.

Maxim Danilov
sumber
2

Perbaikan cepat dan kotor untuk ini jika Anda tidak memerlukan Uglify untuk mengacaukan / mempersingkat nama variabel Anda adalah dengan menyetel mangle = false di Gruntfile Anda

    uglify: {
        compile: {
            options: {
                mangle   : false,
                ...
            },
        }
    }
Parris Varney
sumber
Ini dapat menyelesaikan masalah, tetapi ukuran build yang dihasilkan akan lebih besar karena mangle dinonaktifkan.
NotABot
masih lebih kecil daripada tidak jelek sama sekali
mjwrazor