Grunt usemin: concatenated JavaScript file not replaced in index.html file

vmontane picture vmontane · Mar 19, 2014 · Viewed 7.3k times · Source

I started with an empty project generated by yeoman, and tried to edit the Gruntfile.js to fit my needs.

The grunt build task reads my index.html file, and concatenates my bower dependencies and generates a .vendor.js file.

I broke something in the workflow, and now usemin does not replace markups in my index.html file, even if the .vendor.js file is generated.

Here is my Gruntfile.js

    'use strict';

    module.exports = function (grunt) {

      // Load grunt tasks automatically
      require('load-grunt-tasks')(grunt);

      // Time how long tasks take. Can help when optimizing build times
      require('time-grunt')(grunt);

      // Define the configuration for all the tasks
      grunt.initConfig({

        // Project settings
        yeoman: {
          // configurable paths
          app: require('./bower.json').appPath || 'app',
          dist: 'dist',
        },
        express: {
          options: {
            port: process.env.PORT || 9000
          },
          dev: {
            options: {
              script: 'server.js',
              debug: true
            }
          },
          prod: {
            options: {
              script: 'dist/server.js',
              node_env: 'production'
            }
          }
        },
        open: {
          server: {
            url: 'http://localhost:<%= express.options.port %>'
          }
        },

        // Watches files for changes and runs tasks based on the changed files
        watch: {
          js: {
            files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
            tasks: ['newer:jshint:all'],
            options: {
              livereload: true
            }
          },
          jsTest: {
            files: ['test/spec/{,*/}*.js'],
            tasks: ['newer:jshint:test', 'karma']
          },
          compass: {
            files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
            tasks: ['compass:server', 'autoprefixer']
          },
          gruntfile: {
            files: ['Gruntfile.js']
          },
          livereload: {
            files: [
              '<%= yeoman.app %>/views/{,*//*}*.{html,handlebars}',
              '{.tmp,<%= yeoman.app %>}/styles/{,*//*}*.css',
              '{.tmp,<%= yeoman.app %>}/scripts/{,*//*}*.js',
              '<%= yeoman.app %>/images/{,*//*}*.{png,jpg,jpeg,gif,webp,svg}',
              '<%= yeoman.app %>/data/{,*/}*.{png,jpg,jpeg,gif,webp,svg,json,pdf}'
            ],

            options: {
              livereload: true
            }
          },
          express: {
            files: [
              'server.js',
              'lib/**/*.{js,json}'
            ],
            tasks: ['newer:jshint:server', 'express:dev'],
            options: {
              livereload: true,
              nospawn: true //Without this option specified express won't be reloaded
            }
          }
        },

        // Make sure code styles are up to par and there are no obvious mistakes
        jshint: {
          options: {
            jshintrc: '.jshintrc',
            reporter: require('jshint-stylish')
          },
          server: {
            options: {
              jshintrc: 'lib/.jshintrc'
            },
            src: [ 'lib/{,*/}*.js']
          },
          all: [
            '<%= yeoman.app %>/scripts/{,*/}*.js'
          ],
          test: {
            options: {
              jshintrc: 'test/.jshintrc'
            },
            src: ['test/spec/{,*/}*.js']
          }
        },

        // Empties folders to start fresh
        clean: {
          dist: {
            files: [{
              dot: true,
              src: [
                '.tmp',
                '<%= yeoman.dist %>/views/*',
                '<%= yeoman.dist %>/public/*',
                '!<%= yeoman.dist %>/public/.git*',
              ]
            }]
          },
          heroku: {
            files: [{
              dot: true,
              src: [
                'heroku/*',
                '!heroku/.git*',
                '!heroku/Procfile'
              ]
            }]
          },
          server: '.tmp'
        },

        // Add vendor prefixed styles
        autoprefixer: {
          options: {
            browsers: ['last 1 version']
          },
          dist: {
            files: [{
              expand: true,
              cwd: '.tmp/styles/',
              src: '{,*/}*.css',
              dest: '.tmp/styles/'
            }]
          }
        },

        // Automatically inject Bower components into the app
        'bower-install': {
          app: {
            html: '<%= yeoman.app %>/views/edit/index.html',
            ignorePath: '<%= yeoman.app %>/'
          }
        },

        // Compiles Sass to CSS and generates necessary files if requested
        compass: {
          options: {
            sassDir: '<%= yeoman.app %>/styles',
            cssDir: '.tmp/styles',
            generatedImagesDir: '.tmp/images/generated',
            imagesDir: '<%= yeoman.app %>/images',
            javascriptsDir: '<%= yeoman.app %>/scripts',
            fontsDir: '<%= yeoman.app %>/styles/fonts',
            importPath: '<%= yeoman.app %>/bower_components',
            httpImagesPath: '/images',
            httpGeneratedImagesPath: '/images/generated',
            httpFontsPath: '/styles/fonts',
            relativeAssets: false,
            assetCacheBuster: false,
            raw: 'Sass::Script::Number.precision = 10\n'
          },
          dist: {
            options: {
              generatedImagesDir: '<%= yeoman.dist %>/public/images/generated'
            }
          },
          server: {
            options: {
              debugInfo: true
            }
          }
        },

        // Renames files for browser caching purposes
        rev: {
          dist: {
            files: {
              src: [
                '<%= yeoman.dist %>/public/scripts/{,*/}*.js',
                '<%= yeoman.dist %>/public/styles/{,*/}*.css',
                //'<%= yeoman.dist %>/public/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
                '<%= yeoman.dist %>/public/styles/fonts/*'
              ]
            }
          }
        },

        // The following *-min tasks produce minified files in the dist folder
        imagemin: {
          dist: {
            files: [{
              expand: true,
              cwd: '<%= yeoman.app %>/images',
              src: '{,*/}*.{png,jpg,jpeg,gif}',
              dest: '<%= yeoman.dist %>/public/images'
            }]
          }
        },


        // Reads HTML for usemin blocks to enable smart builds that automatically
        // concat, minify and revision files. Creates configurations in memory so
        // additional tasks can operate on them
        useminPrepare: {
          html: ['<%= yeoman.app %>/views/edit/index.html'
                 //'<%= yeoman.app %>/../views/index.handlebars'
            ],
          options: {
            dest: '<%= yeoman.dist %>/public'
          }
        },

        // Performs rewrites based on rev and the useminPrepare configuration
        usemin: {
          html: ['<%= yeoman.dist %>/views/edit/index.html',
                 '<%= yeoman.dist %>/views/{,*/}*.handlebars'],
          css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
          options: {
            assetsDirs: ['<%= yeoman.dist %>/public', '<%= yeoman.dist %>/public/images']
          }
        },
        svgmin: {
          dist: {
            files: [{
              expand: true,
              cwd: '<%= yeoman.app %>/images',
              src: '{,*/}*.svg',
              dest: '<%= yeoman.dist %>/public/images'
            }]
          }
        },
        htmlmin: {
          dist: {
            options: {
              //collapseWhitespace: true,
              //collapseBooleanAttributes: true,
              //removeCommentsFromCDATA: true,
              //removeOptionalTags: true
            },
            files: {
              '<%= yeoman.dist %>/public/views/edit/index.html': '<%= yeoman.app %>/views/edit/index.html'
            }
            //files: [{
            //  expand: true,
            //  cwd: '<%= yeoman.app %>/views/edit',
            //  src: ['*.html', 'partials/*.html'],
            //  dest: '<%= yeoman.dist %>/public/views/edit'
            //}]
          }
        },

        // Allow the use of non-minsafe AngularJS files. Automatically makes it
        // minsafe compatible so Uglify does not destroy the ng references
        ngmin: {
          dist: {
            files: [{
              expand: true,
              cwd: '.tmp/concat/scripts',
              src: '*.js',
              dest: '.tmp/concat/scripts'
            }]
          }
        },

        // Replace Google CDN references
        cdnify: {
          dist: {
            html: ['<%= yeoman.dist %>/public/views/edit/*.html']
          }
        },

        // Copies remaining files to places other tasks can use
        copy: {
          dist: {
            files: [{
              expand: true,
              dot: true,
              cwd: '<%= yeoman.app %>',
              dest: '<%= yeoman.dist %>/public',
              src: [
                '*.{ico,png,txt}',
                '.htaccess',
                '*.html',
                //'views/edit/{,*/}*.html',
                //'templates/{,*/}*.html',
                'bower_components/**/*',
                'images/{,*/}*.{webp}',
                'data/{,*/}*.*',
                'fonts/**/*'
              ]
            },
            //{
            //  expand: true,
            //  dot: true,
            //  cwd: '<%= yeoman.app %>/views',
            //  dest: '<%= yeoman.dist %>/views',
            //  src: '**/*.handlebars'
            //},
            {
              expand: true,
              cwd: '.tmp/images',
              dest: '<%= yeoman.dist %>/public/images',
              src: ['generated/*']
      }, {
              expand: true,
              dest: '<%= yeoman.dist %>',
              src: [
                'package.json',
                'server.js',
                'lib/**/*'
              ]
            }]
          },
          styles: {
            expand: true,
            cwd: '<%= yeoman.app %>/styles',
            dest: '.tmp/styles/',
            src: '{,*/}*.css'
          }
        },

        // Run some tasks in parallel to speed up the build process
        concurrent: {
          server: [
            'compass:server'
          ],
          test: [
            'compass'
          ],
          dist: [
            'compass:dist',
            'imagemin',
            'svgmin',
            'htmlmin'
          ]
        },

        // By default, your `index.html`'s <!-- Usemin block --> will take care of
        // minification. These next options are pre-configured if you do not wish
        // to use the Usemin blocks.
        // cssmin: {
        //   dist: {
        //     files: {
        //       '<%= yeoman.dist %>/styles/main.css': [
        //         '.tmp/styles/{,*/}*.css',
        //         '<%= yeoman.app %>/styles/{,*/}*.css'
        //       ]
        //     }
        //   }
        // },
        // uglify: {
        //   dist: {
        //     files: {
        //       '<%= yeoman.dist %>/scripts/scripts.js': [
        //         '<%= yeoman.dist %>/scripts/scripts.js'
        //       ]
        //     }
        //   }
        // },
        //concat: {
        //   dist: {}
        //},

        // Test settings
        karma: {
          unit: {
            configFile: 'karma.conf.js',
            singleRun: true
          }
        }
      });

      grunt.registerTask('express-keepalive', 'Keep grunt running', function() {
        this.async();
      });

      grunt.registerTask('serve', function (target) {
        if (target === 'dist') {
          return grunt.task.run(['build', 'express:prod', 'open', 'express-keepalive']);
        }

        grunt.task.run([
          'clean:server',
          'bower-install',
          'concurrent:server',
          'autoprefixer',
          'express:dev',
          'open',
          'watch'
        ]);
      });

      grunt.registerTask('server', function () {
        grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
        grunt.task.run(['serve']);
      });

      grunt.registerTask('test', [
        'clean:server',
        'concurrent:test',
        'autoprefixer',
        'karma'
      ]);

      grunt.registerTask('build', [
        'clean:dist',
        'bower-install',
        'useminPrepare',
        'concurrent:dist',
        'autoprefixer',
        'concat',
        'ngmin',
        'copy:dist',
        'cdnify',
        'cssmin',
        'uglify',
        'rev',
        'usemin'
      ]);


      grunt.registerTask('heroku', function () {
        grunt.log.warn('The `heroku` task has been deprecated. Use `grunt build` to build for deployment.');
        grunt.task.run(['build']);
      });

      grunt.registerTask('default', [
        'newer:jshint',
        'test',
        'build'
      ]);
    };

And here is what is generated when I run grunt:

    tree dist/public/scripts/
    dist/public/scripts/
    ├── 434e7b5e.scripts-edit.js
    └── ad5d9b7c.vendor-edit.js

index.html:

    <!-- build:js(app) scripts/vendor-edit.js -->
    <!-- bower:js-->
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script src="bower_components/angular/angular.js"></script>
    <script src="bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap.js"></script>
    <script src="bower_components/angular-resource/angular-resource.js"></script>
    <script src="bower_components/angular-cookies/angular-cookies.js"></script>
    <script src="bower_components/angular-sanitize/angular-sanitize.js"></script>
    <script src="bower_components/angular-route/angular-route.js"></script>
    <script src="bower_components/angular-animate/angular-animate.js"></script>
    <script src="bower_components/angular-http-auth/src/http-auth-interceptor.js"></script>
    <script src="bower_components/angular-translate/angular-translate.js"></script>
    <script src="bower_components/angular-translate-storage-cookie/angular-translate-storage-cookie.js"></script>
    <script src="bower_components/angular-translate-storage-local/angular-translate-storage-local.js"></script>
    <script src="bower_components/angular-ui-router/release/angular-ui-router.js"></script>
    <script src="bower_components/moment/moment.js"></script>
    <script src="bower_components/angular-moment/angular-moment.js"></script>
    <script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
    <script src="bower_components/AngularJS-Toaster/toaster.js"></script>
    <!-- endbower -->
    <!-- endbuild -->

And finaly, here is the grunt output

        Running "compass:dist" (compass) task
        directory .tmp/styles/
           create .tmp/styles/edit.css (16.644s)
           create .tmp/styles/webicons.css (0.156s)
           create .tmp/styles/main.css (10.947s)
        Compilation took 27.77s

        Done, without errors.


        Execution Time (2014-03-19 13:47:21 UTC)
        compass:dist  29.1s  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 100%
        Total 29.2s

        Running "htmlmin:dist" (htmlmin) task
        File dist/public/views/edit/index.html created.

        Done, without errors.


        Execution Time (2014-03-19 13:47:51 UTC)
        loading tasks  13ms  ▇▇▇▇▇▇▇▇▇▇▇▇ 25%
        htmlmin         2ms  ▇▇ 4%
        htmlmin:dist   35ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 67%
        Total 52ms

    Running "autoprefixer:dist" (autoprefixer) task
    Prefixed file ".tmp/styles/edit.css" created.
    Prefixed file ".tmp/styles/main.css" created.
    Prefixed file ".tmp/styles/webicons.css" created.

    Running "concat:generated" (concat) task
    File ".tmp/concat/styles/edit.css" created.
    File ".tmp/concat/scripts/vendor-edit.js" created.
    File ".tmp/concat/scripts/scripts-edit.js" created.

    Running "ngmin:dist" (ngmin) task
    ngminifying .tmp/concat/scripts/scripts-edit.js, .tmp/concat/scripts/vendor-edit.js

    Running "copy:dist" (copy) task
    Created 125 directories, copied 907 files

    Running "cdnify:dist" (cdnify) task
    Going through dist/public/views/edit/index.html to update script refs

    Running "cssmin:generated" (cssmin) task
    File dist/public/styles/edit.css created.

    Running "uglify:generated" (uglify) task
    File "dist/public/scripts/vendor-edit.js" created.
    File "dist/public/scripts/scripts-edit.js" created.

    Running "rev:dist" (rev) task
    dist/public/scripts/scripts-edit.js >> 434e7b5e.scripts-edit.js
    dist/public/scripts/vendor-edit.js >> ad5d9b7c.vendor-edit.js
    dist/public/styles/edit.css >> 9d137efe.edit.css

    Running "usemin:html" (usemin) task

    Running "usemin:css" (usemin) task


    Execution Time (2014-03-19 13:46:43 UTC)
    concurrent:test   20.7s  ▇▇▇▇▇▇▇ 15%
    karma:unit         3.8s  ▇▇ 3%
    concurrent:dist   41.9s  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 31%
    ngmin:dist        44.5s  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 33%
    copy:dist          2.2s  ▇ 2%
    uglify:generated  18.1s  ▇▇▇▇▇▇ 14%
    Total 133.9s

Any help understanding this would be appreciated.

Answer

vmontane picture vmontane · Mar 26, 2014

I'll answer my own question:

I had a difference between my useminprepare configuration and my usemin configuration.

useminprepare was writing to <%= yeoman.dist %>/public/views/edit/index.html

and usemin was reading from <%= yeoman.dist %>/views/edit/index.html