13
votes

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?

5
One or more of your angular scripts are not minification friendly.Stewie

5 Answers

16
votes

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

8
votes

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

2
votes

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.

0
votes

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.

0
votes

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