web-dev-qa-db-fra.com

Bug de minification angular.module

Avoir le temps sacré d'essayer de comprendre pourquoi la minification ne fonctionne pas.

J'ai injecté via un objet array mes fournisseurs avant la fonction, en fonction de nombreuses suggestions sur le Web, et pourtant, "fournisseur inconnu: aProvider <- a"

Ordinaire:

var app = angular.module('bpwApp', ['ui.bootstrap', 'ui', 'myTabs'])
    .config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider){
    $routeProvider.
        when('/', {templateUrl: 'partials/home.jade', controller: HomeCtrl});

    $locationProvider.html5Mode(true);
    }])

Minified:

var app = angular.module('bpwApp', ['ui.bootstrap', 'ui', 'myTabs'])
    .config(['$routeProvider', '$locationProvider', function(a, b){
    a.
        when('/', {templateUrl: 'partials/home.jade', controller: HomeCtrl});

    b.html5Mode(true);
    }])

Toute suggestion serait très obligée!

81
BPWDevelopment

AndrewM96 suggestion de ng-min est juste.

L’alignement et l’espace blanc importent autant pour Uglify que pour Angular.

5
BPWDevelopment

J'ai rencontré ce problème auparavant avec Grunt.js Uglify plugin.

Une des options est mangle

uglify: {
  options: {
    mangle: false
  },

Ce qui, je crois, exécute les fonctions regex sur "comme des chaînes" et les minifys.

Par exemple:

angular.module("imgur", ["imgur.global","imgur.album"]);

Deviendrait:

angular.module("a", ["a.global","a.album"]);

Désactivez-le: cette fonctionnalité ne joue pas avec Nice avec Angular.

Modifier:

Pour être plus précis comme l'explique @JoshDavidMiller:

Uglify mangle ne modifie que les variables, ce qui cause le problème AngularJS. C'est-à-dire que le problème réside dans l'injection et non dans la définition.

function MyCtrl($scope, myService) serait mutilé en function MyCtrl(a, b), mais la définition du service à l'intérieur d'une chaîne ne devrait jamais être altérée.

  • Exécuter ng-min Avant d’exécuter uglify résout ce problème.
139
Dan Kanze

Problème

De AngularJS: The Bad Parts :

Angular a un injecteur de dépendance intégré qui transmettra les objets appropriés à votre fonction en fonction du nom de ses paramètres:

function MyController($scope, $window) {
    // ...
}

Ici, les noms des paramètres $scope Et $window Seront comparés à une liste de noms connus, et les objets correspondants seront instanciés et transmis à la fonction. Angular) obtient les noms de paramètres en appelant toString() sur la fonction, puis en analysant la définition de la fonction.

Le problème avec ceci, bien sûr, est que cela cesse de fonctionner dès que vous réduisez votre code . Étant donné que vous vous souciez de l'expérience utilisateur, vous réduirez votre code. Par conséquent, l'utilisation de ce mécanisme d'identification interne endommagera votre application. En fait, une méthodologie de développement courante consiste à utiliser du code non minié dans le développement pour faciliter le débogage, puis à réduire au minimum le code lors du passage en production ou en transfert. Dans ce cas, le problème ne se posera pas tant que vous n’aurez pas atteint le point le plus douloureux.

(...)

Puisque ce mécanisme d’injection de dépendance ne fonctionne pas dans le cas général, Angular fournit également un mécanisme. Il est bien sûr qu’il en fournit deux. Vous pouvez soit faire passer un tableau de la manière suivante:

module.controller('MyController', ['$scope', '$window', MyController]);

Ou vous pouvez définir la propriété $inject Sur votre constructeur:

MyController.$inject = ['$scope', '$window'];

Solution

Vous pouvez utiliser ng-annotate pour ajouter automatiquement des annotations requises pour réduire:

ng-annotate Ajoute et supprime les annotations d'injection de dépendance AngularJS. Il est non intrusif, votre code source reste donc exactement le même. Pas de commentaires perdus ni de lignes déplacées.

ng-annotate Est plus rapide et plus stable que ngmin (qui est maintenant obsolète) et contient des plugins pour de nombreux outils:


Depuis AngularJS 1.3, il y a aussi un nouveau paramètre dans ngApp appelé ngStrictDi:

si cet attribut est présent sur l'élément de l'application, l'injecteur sera créé en mode "strict-di". Cela signifie que l'application n'appellera pas les fonctions qui n'utilisent pas d'annotation de fonction explicite (et ne conviennent donc pas à la minification), comme décrit dans le guide d'injection de dépendance , et des informations de débogage utiles vous aideront à localiser la racine de ces insectes.

50
Paolo Moretti

J'ai la même erreur. Cependant, pour moi, le problème est la déclaration du contrôleur des directives. Vous devriez le faire à la place.

myModule.directive('directiveName', function factory(injectables) {
    var directiveDefinitionObject = {
      templateUrl: 'directive.html',
      replace: false,
      restrict: 'A',
      controller: ["$scope", "$element", "$attrs", "$transclude", "otherInjectables",
        function($scope, $element, $attrs, $transclude, otherInjectables) { ... }]
    };
    return directiveDefinitionObject;
  });

https://github.com/angular/angular.js/pull/3125

22
angelokh

J'ai eu un problème similaire en utilisant grunt, ngmin et uglify.

J'ai couru le processus dans cet ordre: concat, ngmin, uglify

Je continuais à obtenir l'erreur $ injector de angular jusqu'à ce que j'ajoute l'option uglify mangle: false - tout était alors corrigé.

J'ai aussi essayé d'ajouter les exceptions à uglify comme ceci:

 options: {
  mangle: {
     except: ['jQuery', 'angular']
  }
}

Mais en vain...

Voici mon gruntFile.js pour plus de précisions:

module.exports = function(grunt) {
'use strict';
// Configuration goes here
grunt.initConfig({
    pkg: require('./package.json'),

    watch: {
        files: ['scripts/**/*.js', 'test/**/*spec.js', 'GruntFile.js'],
        tasks: ['test', 'ngmin']
    },

    jasmine : {
        // Your project's source files
        src : ['bower_components/angular/angular.js', 'bower_components/angular-mocks/angular-mocks.js', 'scripts/app.js', 'scripts/**/*.js' ],
        // Your Jasmine spec files

        options : {
            specs : 'test/**/*spec.js',
            helpers: 'test/lib/*.js'
        }
    },

    concat: {
      dist : {
          src: ['scripts/app.js', 'scripts/**/*.js'],
          dest: 'production/js/concat.js'
      }
    },

    ngmin: {
        angular: {
            src : ['production/js/concat.js'],
            dest : 'production/js/ngmin.js'
        }

    },

    uglify : {
        options: {
            report: 'min',
            mangle: false
        },
        my_target : {
            files : {
                'production/app/app.min.js' : ['production/js/ngmin.js']
            }
        }
    },

  docular : {
      groups: [],
      showDocularDocs: false,
      showAngularDocs: false
  }

});

// Load plugins here
grunt.loadNpmTasks('grunt-ngmin');
grunt.loadNpmTasks('grunt-docular');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jasmine');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-connect');

// Define your tasks here
grunt.registerTask('test', ['jasmine']);
grunt.registerTask('build', ['concat', 'ngmin', 'uglify']);
grunt.registerTask('default', ['test', 'build', 'watch']);

};

9
Sten Muchow

J'avais un problème similaire. Et l'a résolu de la manière suivante. Nous devons exécuter un module Gulp appelé gulp-ng-annotate avant d'exécuter uglify. Nous installons donc ce module

npm install gulp-ng-annotate --save-dev

Alors faites le require dans Gulpfile.js

ngannotate = require(‘gulp-ng-annotate’)

Et dans votre tâche usemin faire quelque chose comme ça

js: [ngannotate(), uglify(),rev()] 

Cela l'a résolu pour moi.

[EDIT: fautes de frappe fixes]

3

Uglify a une option pour désactiver le brassage sur des fichiers spécifiques:

options: {
  mangle: {
     except: ['jQuery', 'angular']
  }
}

https://github.com/gruntjs/grunt-contrib-uglify#reserved-identifiers

2
Courtenay

Ceci est très difficile à déboguer car beaucoup de services sont nommés de la même manière (principalement e ou a). Cela ne résoudra pas l'erreur, mais vous fournira le nom du service non résol, ce qui vous permettra de localiser, dans la sortie altérée, l'emplacement dans le code et enfin de résoudre le problème. :

Allez dans lib/scope.jsde Uglify2 (node_modules/grunt-contrib-uglify/node_modules/uglify-js/lib/scope.js) et remplacez la ligne

this.mangled_name = this.scope.next_mangled(options);

avec

this.mangled_name = this.name + "__debugging_" + counter++
2
Bas