Angular grunt build (from yeoman) breaks my app

alonisser picture alonisser · Nov 20, 2013 · Viewed 10.9k times · Source

after running the build from the /dist folder I get:

Uncaught Error: [$injector:modulerr] http://errors.angularjs.org/1.2.1/$injector/modulerr?p0=ourname&p1=Error%3A…(http%3A%2F%2Flocalhost%3A8085%2Flib%2Fangular%2Fangular.min.js%3A32%3A462) 

nothing I do seems to solve this problem

this is the grunt task flow - adapted 1 to 1 from yeoman angular-generator:

module.exports = function(grunt){
    require('load-grunt-tasks')(grunt);
    require('time-grunt')(grunt);

    grunt.initConfig({
        //pkg: grunt.file.readJSON('package.json'),
        yeoman: {
            // configurable paths
            app: require('./bower.json').appPath || 'app',
            dist: 'dist'
        },
        coveralls:{
        options:{
          coverage_dir:'coverage'
        }
         },
        jshint:{
            files:['app/js/**/*.js', 'Gruntfile.js'],
            options:grunt.file.readJSON('.jshintrc')


        },
        watch:{
            styles: {
                files: ['<%= yeoman.app %>/css/{,*/}*.css'],
                tasks: ['copy:css', 'autoprefixer']
            },
            livereload: {
                options: {
                    livereload: '<%= connect.options.livereload %>'
                },
                files: [
                    '<%= yeoman.app %>/{,*/}*.html',
                    '.tmp/css/{,*/}*.css',
                    '{.tmp,<%= yeoman.app %>}/js/{,*/}*.js',
                    '<%= yeoman.app %>/img/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
                ]
            }
        },
        autoprefixer: {
            options: ['last 1 version'],
            dist: {
                files: [{
                    expand: true,
                    cwd: '.tmp/styles/',
                    src: '{,*/}*.css',
                    dest: '.tmp/styles/'
                }]
            }
        },
        connect: {
            options: {
                port: 9000,
                // Change this to '0.0.0.0' to access the server from outside.
                hostname: 'localhost',
                livereload: 35729
            },
            livereload: {
                options: {
                    open: true,
                    base: [
                        '.tmp',
                        '<%= yeoman.app %>'
                    ]
                }
            },
            test: {
                options: {
                    port: 9001,
                    base: [
                        '.tmp',
                        'test',
                        '<%= yeoman.app %>'
                    ]
                }
            },
            dist: {
                options: {
                    base: '<%= yeoman.dist %>'
                }
            }
        },
        clean: {
            dist: {
                files: [{
                    dot: true,
                    src: [
                        '.tmp',
                        '<%= yeoman.dist %>/*',
                        '!<%= yeoman.dist %>/.git*'
                    ]
                }]
            },
//            server: '.tmp'
        },
            rev: {
                dist: {
                    files: {
                        src: [
                            '<%= yeoman.dist %>/js/{,*/}*.js',
                            '<%= yeoman.dist %>/css/{,*/}*.css',
                            '<%= yeoman.dist %>/img/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
                            '<%= yeoman.dist %>/css/fonts/*'
                        ]
                    }
                }
            },
            useminPrepare: {
                html: '<%= yeoman.app %>/index.html',
                options: {
                    dest: '<%= yeoman.dist %>',
                    html: {
                        steps: {'js': ['concat','ngmin']},
                        post: {}
                    }
                }
            },
            usemin: {
                html: ['<%= yeoman.dist %>/{,*/}*.html'],
                css: ['<%= yeoman.dist %>/css/{,*/}*.css'],
                options: {
                    assetsDirs: ['<%= yeoman.dist %>']
                }
            },
            imagemin: {
                dist: {
                    files: [{
                        expand: true,
                        cwd: '<%= yeoman.app %>/img',
                        src: '{,*/}*.{png,jpg,jpeg}',
                        dest: '<%= yeoman.dist %>/img'
                    }]
                }
            },
            svgmin: {
                dist: {
                    files: [{
                        expand: true,
                        cwd: '<%= yeoman.app %>/img',
                        src: '{,*/}*.svg',
                        dest: '<%= yeoman.dist %>/img'
                    }]
                }
            },
            cssmin: {
                // By default, your `index.html` <!-- Usemin Block --> will take care of
                // minification. This option is pre-configured if you do not wish to use
                // Usemin blocks.
                // dist: {
                //   files: {
                //     '<%= yeoman.dist %>/styles/main.css': [
                //       '.tmp/styles/{,*/}*.css',
                //       '<%= yeoman.app %>/styles/{,*/}*.css'
                //     ]
                //   }
                // }
            },
            htmlmin: {
                dist: {
                    options: {
                        /*removeCommentsFromCDATA: true,
                         // https://github.com/yeoman/grunt-usemin/issues/44
                         //collapseWhituseminPrepareespace: true,
                         collapseBooleanAttributes: true,
                         removeAttributeQuotes: true,
                         removeRedundantAttributes: true,
                         useShortDoctype: true,
                         removeEmptyAttributes: true,
                         removeOptionalTags: true*/
                    },
                    files: [{
                        expand: true,
                        cwd: '<%= yeoman.app %>',
                        src: ['*.html', 'partials/*.html'],
                        dest: '<%= yeoman.dist %>'
                    }]
                }
            },
        copy: {
            dist: {
                files: [{
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>',
                    dest: '<%= yeoman.dist %>',
                    src: [
                        '*.{ico,png,txt}',
                        'lib/**/*',
                        'img/{,*/}*.{gif,webp}',
                        'fonts/*',
                        'languages/*'
                    ]
                }, {
                    expand: true,
                    cwd: '.tmp/img',
                    dest: '<%= yeoman.dist %>/img',
                    src: [
                        'generated/*'
                    ]
                }]
            },
            styles: {
                expand: true,
                cwd: '<%= yeoman.app %>/css',
                dest: '.tmp/css/',
                src: '{,*/}*.css'
            }
        },
        concurrent: {
            server: [
                'copy:styles'
            ],
            test: [
                'copy:styles'
            ],
            dist: [
                'copy:styles',
                'imagemin',
                'svgmin',
                'htmlmin'
            ]
        },
//        cdnify: {
//            dist: {
//                html: ['<%= yeoman.dist %>/*.html']
//            }
//        },
        ngmin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '.tmp/concat/js',
                    src: '*.js',
                    dest: '.tmp/concat/js'
                }]
            }
        },
        uglify: {
            dist: {
                options:{
                    compress:false,
                    mangle:false
                },
                files: {
                    '<%= yeoman.dist %>/js/scripts.js': [
                        '<%= yeoman.dist %>/js/scripts.js'
                    ]
                }
            }
        },


//        concat:{
//            options:{
//                seperator:';'
//            },
//            dist:{
//                src :['app/js/**/*.js', 'app/lib/**/*.js'],
//                dest :'dist/app/js/<%pkg.name%>.js'
//
//            }
//        },
        exec:{
            instrument:{
                cmd: function(){
                    return 'istanbul instrument app/js -o app/instrumentjs';
                }

            },
            djangoUp:{
              cmd: function(){
                  var command = 'workon stokeet-api  && cd ../stokeet-api/ && python manage.py runserver> /dev/null 2>&1 && cd ../angular/ & ';
                  return command;
              }
            },

            webserverUp:{
              cmd: function(){
                  var command = 'cd ../angular/ && node ./scripts/web-server.js > /dev/null 2>&1 &';
                  return command;
              }

            }
        },
        karma:{
            unit:{
                configFile:'config/karma.conf.js',
                autoWatch:true,
                browsers:['PhantomJS']
            },
            ci:{
                configFile:'config/karma.conf.js',
                singleRun:true,
                autoWatch:false,
                browsers:['Firefox','PhantomJS']
            },
            buildTest:{
                configFile:'config/karma.conf.js',
                singleRun:true,
                autoWatch:false,
                browsers:['PhantomJS']
            }

        }



    });
    grunt.registerTask('coverage',['coveralls']);
    grunt.registerTask('default',['jshint']);
    grunt.registerTask('instrument',['exec: instrument']);
//    grunt.registerTask('concat',['concat']);
    grunt.registerTask('dev_up',['exec:djangoUp', 'exec:webserverUp']);
    grunt.registerTask('test',[
                            'clean:server',
                            'concurrent:test',
                            'autoprefixer',
                            'connect:test',
                            'karma:buildTest']),
    grunt.registerTask('build', [
                            'clean:dist',
                            'useminPrepare',
                            'concurrent:dist',
                            'autoprefixer',
                            'concat',

                            'copy:dist',
                            'ngmin',
                            'cssmin',
                            'uglify',
                            'rev',
                            'usemin'
    ]);
    grunt.registerTask('server', function (target) {
        if (target === 'dist') {
            return grunt.task.run(['build', 'connect:dist:keepalive']);
        }

        grunt.task.run([
            'clean:server',
            'concurrent:server',
            'autoprefixer',
            'connect:livereload',
            'watch'
        ]);
    });

};

You can see I tried overriding the usemin default flow, but that didn't help

I suspect this has to do with the known angular minification problem, but since ngmin is running here, and all my code (Not sure about the plugins) does respect the angular minification safe array notation I'm not sure .

any ideas? I'll be glad for any help with this

Answer

QuarK picture QuarK · Dec 17, 2013

I will try to be brief explaining the problem when building with usemin 2.0.x and ngmin in Angularjs (according my experience, I may be wrong in something, if yes please correct me):

The normal flow in the Build grunt task is that ngmin is executed before usemin and others, to let the code with injections like this:

...
angular.module("Config",[])
    .config(["$httpProvider","CONSTANTS","ERRORS","HEADERS",function(a,b,c,d){var
...

usemin 0.1.x was using only 'concat', but the version 2.0.x is using 'concat' and 'uglifyjs' so, after concatenate the code, it changes the javascript code again, this corrupts the ngmin, as you can see in the following example:

...
angular.module("Config",[])
    .config(function(a,b,c,d){var
...

So you have to stop it defining the flow in useminPrepare, like the following:

useminPrepare: {
        html: '<%= yeoman.app %>/index.html',
        options: {
            dest: '<%= yeoman.dist %>',
            flow: {
                steps: {'js': ['concat']},
                post: {}
            }
        }
    },

Edit You can add the task in the grunt tasks:

grunt.registerTask('build', [ 
    'clean:dist', 
    'jshint', 
    'useminPrepare', 
    'imagemin', 
    'cssmin', 
    'ngmin', 
    'uglify', 
    'rev', 
    'usemin'
])

Hope it helps!