calvinl / ng-phonegap

Grunt workflow for building AngularJS applications on PhoneGap.
MIT License
138 stars 28 forks source link

SASS compile problem to tmp dir #6

Closed stinoga closed 10 years ago

stinoga commented 10 years ago

First off, love this workflow Calvin. Working great for setting up my phonegap app. I've updated my gruntfile to use SASS instead of LESS, and the CSS file doesn't seem to be compiled anywhere. The watch task picks up on the .scss file changing, and I get no errors, but the css file is not in the tmp directory. All I see is the vendor.css file. Any ideas? Below is my grunt file:

module.exports = function(grunt) {

  var _APP_NAME_ = "change";

  // initial grunt configuration
  grunt.initConfig({

    pkg: grunt.file.readJSON('package.json'),
    appDir: 'www',
    tmpDir: 'tmp',
    srcDir: 'src',
    bowerDir: 'vendor',
    vendorDir: 'src/js/vendor',
    releaseDir: '<%= appDir %>',
    connect: {
      server: {
        options: {
          port: 9001,
          base: '<%= appDir %>'
        }
      }
    },
    assets: {
      css: {
        vendor: [

          // Add additional Bower components here

        ],
        // shouldn't need to touch this
        src: [
          '<%= appDir %>/css/vendor.css',
          '<%= appDir %>/css/app.css'
        ]
      },
      js: {
        vendor: [
          // add any Bower components here.
          '<%= bowerDir %>/angular/angular.js',
          '<%= bowerDir %>/angular-route/angular-route.js',
          '<%= bowerDir %>/angular-touch/angular-touch.js'
        ],
        // shouldn't need to touch this.
        src: [
          '<%= appDir %>/js/vendor.js',
          '<%= appDir %>/js/pg.js',
          '<%= appDir %>/js/app.js',
          '<%= appDir %>/js/config.js',
          '<%= appDir %>/js/modules.js',
          '<%= appDir %>/js/directives.js',
          '<%= appDir %>/js/filters.js',
          '<%= appDir %>/js/services.js',
          '<%= appDir %>/js/controllers.js'
        ]
      }
    },

    // concatenate files for angularjs
    concat: {
      vendor: {
        files: {
          '<%= tmpDir %>/css/vendor.css': '<%= assets.css.vendor %>',
          '<%= tmpDir %>/js/vendor.js'  : '<%= assets.js.vendor %>'
        }
      },
      angular: {
        files: {
          '<%= tmpDir %>/js/config.js'      : ['<%= srcDir %>/js/config/*.js'],
          '<%= tmpDir %>/js/modules.js'     : ['<%= srcDir %>/js/modules/*.js', '<%= srcDir %>/js/modules/**/*.js'],
          '<%= tmpDir %>/js/controllers.js' : ['<%= srcDir %>/js/controllers/*.js', '<%= srcDir %>/js/controllers/**/*.js'],
          '<%= tmpDir %>/js/directives.js'  : ['<%= srcDir %>/js/directives/*.js', '<%= srcDir %>/js/directives/**/*.js'],
          '<%= tmpDir %>/js/filters.js'     : ['<%= srcDir %>/js/filters/*.js', '<%= srcDir %>/js/filters/**/*.js'],
          '<%= tmpDir %>/js/modules.js'     : ['<%= srcDir %>/js/modules/*.js', '<%= srcDir %>/js/modules/**/*.js'],
          '<%= tmpDir %>/js/services.js'    : ['<%= srcDir %>/js/services/*.js', '<%= srcDir %>/js/services/**/*.js']
        }
      },
      production: {
        files: {
          '<%= appDir %>/js/application.js': [
            '<%= tmpDir %>/js/production.js',
            '<%= tmpDir %>/js/vendor.js',
            '<%= tmpDir %>/js/pg.js',
            '<%= tmpDir %>/js/app.js',
            '<%= tmpDir %>/js/config.js',
            '<%= tmpDir %>/js/modules.js',
            '<%= tmpDir %>/js/directives.js',
            '<%= tmpDir %>/js/filters.js',
            '<%= tmpDir %>/js/services.js',
            '<%= tmpDir %>/js/controllers.js'
          ]
        }
      }
    },

    // for cleaning builds before re-building
    clean: {
      options: {
        force: true
      },
      tmp: {
        src: ['<%= tmpDir %>'],
      },
      development: {
        src: ['<%= tmpDir %>', '<%= appDir %>']
      },
      production: {
        src: ['<%= tmpDir %>', '<%= releaseDir %>']
      },
      css: {
        src: ['<%= appDir %>/css']
      },
      js: {
        src: ['<%= appDir %>/js']
      },
      img: {
        src: ['<%= appDir %>/img']
      },
      img: {
        src: ['<%= appDir %>/font']
      },
      partials: {
        src: ['<%= appDir %>/html/partials']
      }
    },

    copy: {
      config: {
        files: [
          { src: '<%= srcDir %>/js/config/application.js', dest: '<%= tmpDir %>/js/config/application.js' },
          { src: '<%= srcDir %>/js/app.js', dest: '<%= tmpDir %>/js/app.js' },
          { src: '<%= srcDir %>/js/pg.js', dest: '<%= tmpDir %>/js/pg.js' },
          { src: '<%= srcDir %>/config.xml', dest: '<%= appDir %>/config.xml' }
        ]
      },
      vendor: {
        files: [
          { src: '<%= tmpDir %>/css/vendor.css', dest: '<%= appDir %>/css/vendor.css' },
          { src: '<%= tmpDir %>/js/vendor.js', dest: '<%= appDir %>/js/vendor.js' }
        ]
      },
      img: {
        files: [
          { expand: true, cwd: '<%= srcDir %>/img/', src: ['**'], dest: '<%= appDir %>/img/' }
        ]
      },
      partials: {
        files: [
          { expand: true, cwd: '<%= srcDir %>/html/partials/', src: ['**'], dest: '<%= appDir %>/html/partials/' }
        ]
      },
      fonts: {
        files: [
          { expand: true, cwd: '<%= bowerDir %>/font-awesome/font/', src: ['**'], dest: '<%= appDir %>/font/' }
        ]
      },
      tmp_to_build: {
        files: [
          { expand: true, cwd: '<%= tmpDir %>/js/', src: ['*'], dest: '<%= appDir %>/js/' }
        ]
      },
      development: {
        files: [
          { src: '<%= srcDir %>/js/config/environments/development.js', dest: '<%= appDir %>/js/config/development.js' }
        ]
      },
      production: {
        files: [
          { src: '<%= srcDir %>/js/config/environments/production.js', dest: '<%= tmpDir %>/js/production.js' }
        ]
      }
    },

    // compile SASS files into CSS and store them in temp directories
    sass: {
      production: {
        files: [
          {
            src: ['<%= srcDir %>/**/*.scss', '<%= srcDir %>/!**/_*.scss'],
            cwd: 'scss',
            dest: '<%= tmpDir %>/css/app.css',
            ext: '.css',
            expand: true
          }
        ],
        options: {
          style: 'expanded',
          compass: true
        }
      },
      development: {
        files: [
          {
            src: ['<%= srcDir %>/**/*.scss', '<%= srcDir %>/!**/_*.scss'],
            cwd: 'scss',
            dest: '<%= tmpDir %>/css/app.css',
            ext: '.css',
            expand: true
          }
        ],
        options: {
          style: 'expanded',
          compass: true
        }
      }
    },

    // ngmin for pre-minifying AngularJS apps
    ngmin: {
      routers: {
        src: '<%= tmpDir %>/js/config/router.js',
        dest: '<%= tmpDir %>/js/config/router.js'
      },
      controllers: {
        src: '<%= tmpDir %>/js/controllers.js',
        dest: '<%= tmpDir %>/js/controllers.js'
      },
      directives: {
        src: '<%= tmpDir %>/js/directives.js',
        dest: '<%= tmpDir %>/js/directives.js'
      },
      modules: {
        src: '<%= tmpDir %>/js/modules.js',
        dest: '<%= tmpDir %>/js/modules.js'
      },
      filters: {
        src: '<%= tmpDir %>/js/filters.js',
        dest: '<%= tmpDir %>/js/filters.js'
      },
      services: {
        src: '<%= tmpDir %>/js/services.js',
        dest: '<%= tmpDir %>/js/services.js'
      },
      vendor: {
        src: '<%= tmpDir %>/js/vendor.js',
        dest: '<%= tmpDir %>/js/vendor.js'
      }
    },

    // uglify js for production
    uglify: {
      production: {
        files: {
          '<%= appDir %>/js/application.js': [
            '<%= appDir %>/js/application.js'
          ]
        }
      },
    },

    // minify css for production
    cssmin: {
      production: {
        files: {
          '<%= appDir %>/css/app.css': [
            '<%= tmpDir %>/css/vendor.css',
            '<%= tmpDir %>/css/app.css'
          ],
        }
      }
    },

    // watch files, build on the fly for development
    watch: {
      root: {
        files: ['<%= srcDir %>/*'],
        tasks: ['copy:config']
      },
      scripts: {
        files: ['<%= srcDir %>/js/**','<%= srcDir %>/js/*'],
        tasks: [
          'clean:js', 'concat:angular', 'concat:vendor',
          'copy:development', 'copy:config', 'copy:vendor',
          'copy:tmp_to_build'
        ]
      },
      sass: {
        files: ['<%= srcDir %>/scss/**/*.scss'],
        tasks: ['clean:css', 'concat:vendor', 'copy:vendor', 'sass:development']
      },
      img: {
        files: ['<%= srcDir %>/img/**'],
        tasks: ['clean:img', 'copy:img']
      },
      fonts: {
        files: ['<%= srcDir %>/font/**'],
        tasks: ['clean:fonts', 'copy:fonts']
      },
      partials: {
        files: ['<%= srcDir %>/html/partials/**'],
        tasks: ['clean:partials', 'copy:partials']
      },
      layouts: {
        files: ['<%= srcDir %>/html/layouts/**'],
        tasks: ['layouts:development']
      }
    },

    layouts: {
      options: {
        layout: '<%= srcDir %>/html/layouts/application.tmpl',
      },
      development: {
        options: {
          dest: '<%= appDir %>/index.html'
        },
        files: {
          css: '<%= assets.css.src %>',
          js: '<%= assets.js.src %>'
        }
      },
      production: {
        options: {
          dest: '<%= releaseDir %>/index.html'
        },
        files: {
          css: '<%= assets.css.src %>',
          js: '<%= assets.js.src %>'
        }
      }
    },

    config: {
      options: {
        template: '<%= srcDir %>/config.xml.tmpl',
        dest: '<%= appDir %>/config.xml'
      },
      enterprise: {},
      production: {},
      development: {}
    },

    // main build task (custom) with options
    // this task also builds out the main index.html
    // file based on templates, which are environment-aware
    build: {
      development: {},
      production: {},
      enterprise: {}
    }

  });

  // load grunt npm modules
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-copy');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-sass');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-cssmin');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-connect');
  grunt.loadNpmTasks('grunt-ngmin');

  grunt.registerTask('dev', ['build:development', 'connect', 'watch']);

  // build HTML files based on target
  grunt.registerMultiTask('layouts', 'Builds an HTML file for angular.', function() {

    var opts = this.options()
      , target = this.target
      , css = this.files[0].src
      , js = this.files[1].src
      , layout = grunt.template.process(grunt.file.read(opts.layout), {
          data: { env: target, js: js, css: css, appName: _APP_NAME_ }
        });

    // generate main index.html file
    grunt.file.write(opts.dest, layout);
    grunt.log.write('Generating ' + opts.dest + '...').ok();

  });

  grunt.registerMultiTask('config', 'Builds the Cordova configuration file from template.', function() {

    var opts = this.options()
      , target = this.target
      , version = this.args[0] || '';

    var template = grunt.template.process(grunt.file.read(opts.template), {
          data: { target: target, appName: _APP_NAME_ }
        });

    // generate main index.html file
    grunt.file.write(opts.dest, template);
    grunt.log.write('Generating ' + opts.dest + '...').ok();

  });

  // task for building main index page based on environment
  grunt.registerMultiTask('build', 'Build the app based on environment.', function() {

    var opts = this.options()
      , target = this.target
      , env = target
      , args = this.args
      , version = args[0];

    if (target == 'enterprise') {
      env = 'production';
    }

    // clean up directories
    grunt.task.run('clean:' + env);

    // build all SASS files based on environment
    grunt.task.run('sass:' + env);

    // concat angular files
    grunt.task.run('concat:angular');

    // use ngmin to pre-minify angular files
    grunt.task.run('ngmin');

    // concat vendor libs
    grunt.task.run('concat:vendor');

    // copy all files
    grunt.task.run('copy:' + env);
    grunt.task.run('copy:config');
    grunt.task.run('copy:partials');
    grunt.task.run('copy:img');
    grunt.task.run('copy:fonts');

    // copy tmp files to development
    if (target == 'development') {
      grunt.task.run('copy:vendor');
      grunt.task.run('copy:tmp_to_build');
    }

    if (target == 'production' || target == 'enterprise') {
      // concat all angular files into a single file
      grunt.task.run('concat:production');
      grunt.task.run('uglify:production');
      grunt.task.run('cssmin:production');
    }

    // build cordova config.xml file, uses target so we
    // can switch from production to enterprise for bundle ID
    grunt.task.run('config:' + target);

    // build main index.html file last
    grunt.task.run('layouts:' + env);

  });

};
calvinl commented 10 years ago

It looks like you just have to change the dest option in your development targetment to point at appDir instead of tmpDir.

tmpDir is used for production only, because the concatenated files are moved to a temporary directory first, before minification/uglification. However, in the development target, we leave them un-minified so it's more readable.

You can see on https://github.com/calvinl/ng-phonegap/blob/development/Gruntfile.js#190 line 190 that the development target is pointed directly at appDir.

Let me know if that works for you!

calvinl commented 10 years ago

p.s. Not necessary, but i would recommend adding your sass target separately from the LESS target, rather than replacing it -- that way you can easily switch back and forth when you want/need to.

stinoga commented 10 years ago

Yeah, I tried that actually. When I make a change to an scss file after pointing the dest option to appDir, I still get no compiled CSS file. Wondering if the clean is wiping it out?

>> File "src/scss/main.scss" changed.
Running "clean:css" (clean) task
Cleaning "www/css"...OK
calvinl commented 10 years ago

Your sass task is misconfigured then. Try something like this:

// compile SASS files into CSS and store them in temp directories
sass: {
  production: {
    files: [
      {
        src: ['<%= srcDir %>/**/*.scss', '<%= srcDir %>/!**/_*.scss'],
        cwd: 'scss',
        dest: '<%= tmpDir %>/css/app.css',
        ext: '.css',
        expand: true
      }
    ],
    options: {
      style: 'expanded',
      compass: true
    }
  },
  development: {
    files: {
      // put app.css directly into the build directory for development
      "<%= appDir %>/css/app.css": [
        "<%= srcDir %>/css/**/*.scss"
      ]
    },
    options: {
      style: 'expanded',
      compass: true
    }
  }
},

Notice the the different style -- you can get more specific using the style you were using, but at least now you know it's just a misconfiguration. I'd suggest reading the docs for grunt-contrib-sass to get a handle on how it works.

You'll also need to install grunt-contrib-compass since you have compass: true in your options.

stinoga commented 10 years ago

Ah, the problem was the cwd property. It was causing the path to be set relative to a path that didn't exist. This fixed the problem:

development: {
  options: {
    style: 'expanded',
    compass: true
  },
  files: [
    {
      expand: true,
      src: ['**/*.scss', '!**/_*.scss'],
      cwd: '<%= srcDir %>/scss',
      dest: '<%= appDir %>/css',
      ext: '.css'
    }
  ]
}

Thanks for the help Calvin!