13

I'm a Grunt newbie and I'm trying to convert an Angular app (based on angular-seed) to use it for CSS/JS concat/minification. I have an index.html file that defines the CSS and JS files:

<!-- build:css css/myjmh.css -->
<link rel="stylesheet" href="lib/bootstrap/bootstrap.min.css"/>
<link rel="stylesheet" href="lib/font-awesome/font-awesome.min.css"/>
<link rel="stylesheet" href="lib/toaster/toaster.css"/>
<link rel="stylesheet" href="css/app.css"/>
<link rel="stylesheet" href="css/custom.css"/>
<link rel="stylesheet" href="css/responsive.css"/>
<!-- endbuild -->

...

<!-- build:js js/myjmh.min.js -->
<script src="lib/jquery/jquery-1.10.2.min.js"></script>
<script src="lib/bootstrap/bootstrap.min.js"></script>
<script src="lib/angular/angular.min.js"></script>
<script src="lib/angular/angular-animate.min.js"></script>
<script src="lib/angular/angular-cookies.min.js"></script>
<script src="lib/angular/angular-resource.min.js"></script>
<script src="lib/angular/angular-route.min.js"></script>
<script src="lib/fastclick.min.js"></script>
<script src="lib/toaster/toaster.js"></script>
<script src="lib/webshim/modernizr.min.js"></script>
<script src="lib/webshim/polyfiller.min.js"></script>
<script src="js/app.js"></script>
<script src="js/services.js"></script>
<script src="js/controllers.js"></script>
<script src="js/filters.js"></script>
<script src="js/directives.js"></script>
<!-- endbuild -->

I'm trying to use grunt-usemin and its useminPrepare task to grab these values from my HTML.

Here's my Gruntfile.js:

module.exports = function (grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

        clean: ["dist"],

        copy: {
            main: {
                src: 'app/index.html',
                dest: 'dist/index.html'
            }
        },

        useminPrepare: {
            html: 'app/index.html'
        },

        usemin: {
            html: ['dist/index.html']
        },

        ngmin: {
            dist: {
                files: [
                    {
                        expand: true,
                        cwd: '.tmp/concat/js',
                        src: '*.js',
                        dest: '.tmp/concat/js'
                    }
                ]
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-cssmin');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-ngmin');
    grunt.loadNpmTasks('grunt-usemin');
    grunt.loadNpmTasks('grunt-contrib-watch');

    grunt.registerTask('default', [
        'copy', 'useminPrepare', 'concat', 'ngmin', 'uglify', 'cssmin', 'usemin'
    ]);
};

With all these settings, a "dist" directory is created with all the artifacts and everything seems to be generated/concatenated and minified correctly. However, when I load the new page up in my browser, I get the following error:

Uncaught Error: [$injector:unpr] http://errors.angularjs.org/1.2.3/$injector/unpr?p0=aProvider%20%3C-%20a 

It seems like Grunt is doing something that doesn't play well with Angular. Any ideas on what this might be?

Matt Raible
  • 8,187
  • 9
  • 61
  • 120

5 Answers5

16

I was able to solve this by turning off mangling in uglify:

    uglify: {
        options: {
            report: 'min',
            mangle: false
        }
    }

As you might notice from my Gruntfile.js, ngmin is already used and this doesn't seem to help.

Found answer here: https://stackoverflow.com/a/17239358/65681

Community
  • 1
  • 1
Matt Raible
  • 8,187
  • 9
  • 61
  • 120
  • It looks like ngmin wasn't able to fix the missing DI. (I have no idea why.) But if you search manually for unpr probably you find a place where DI is missing. – Lajos Veres Mar 17 '14 at 17:04
  • Angular don't let you minify the fils because of the way he treats dependency injection, you have to declare it as an Array with the function as the last parameter or use `ng-annotate`. This is the answer: http://stackoverflow.com/questions/18782324/angularjs-minify-best-practice – Julio Marins Apr 01 '16 at 21:18
  • Thanks :) you made my night! – pmandell May 26 '16 at 04:43
8

You need to do one of two things:

  1. Make all of your angular code minification-friendly. See: http://docs.angularjs.org/guide/di#dependency-injection_dependency-annotation

  2. Use a tool like grunt-ngannotate

Julio Marins
  • 10,039
  • 8
  • 48
  • 54
Jeff Hubbard
  • 9,822
  • 3
  • 30
  • 28
2

If you are having same issues with declarations as I had, you could also use ng-annotate in order to achieve this. Here is a link to github: https://github.com/mzgol/grunt-ng-annotate

My working configuration is:

ngAnnotate: {
    options: {
        singleQuotes: true,
        regexp: '^(ng\n?[\\ ]+(.*)|(module.*))$'
    },
    prod: {
        files: [{
            expand: true,
            src: 'dist/**/*.js'
        }]
    }
}

Use it carefully as it will modify existing files. My assumption is that files in 'dist' folder should be generated by Grunt and can be deleted at any time.

Valera Tumash
  • 628
  • 7
  • 16
0

As others have stated, it seems that you have a problem with minification. I see that you're using ngmin. But ngmin may not work when a file with mixed patterns is thrown at it. I mean, make sure that all of the files that you pass to ngmin don't already have minification-safe pattern.

M.K. Safi
  • 6,560
  • 10
  • 40
  • 58
0

Mangle false shouldn't be the solution, actually the code is not minification friendly, but the issues that I had were not explicitly described here, so they were:

1) "Make sure you only define each module with the angular.module(name, [requires]) syntax once across your entire project. Retrieve it for subsequent use with angular.module(name)" - I used it wrong in many places and it worked with mangle:false, breaking when setting mangle:true

2) I used bootstrap modal windows specifying instance controller not as a string, I've found the solution here

Community
  • 1
  • 1
dzezzz
  • 985
  • 7
  • 17