Note that there are some explanatory texts on larger screens.

plurals
  1. POMultiple Build Folders (multiple clients, mulitask, multiple targets) using Grunt (Yeoman)
    text
    copied!<p>I am building a project right now that will be a webapp (browser runnable) and a Phonegap app (iOS &amp; Android). Although my project, theoretically, could use the same <code>dist</code> folder that my <strong><a href="http://yeoman.io/">Yeoman</a></strong> generated, <strong><a href="http://gruntjs.com/">Grunt</a></strong> tasks <code>build</code> production ready code by running <code>grunt build</code>. I would like to run something like <code>grunt build_web</code>, <code>grunt build_ios</code>, and <code>grunt build_android</code>, to build out production code for each platform individually. Or <code>grunt build:web</code>, <code>grunt build:ios</code>, <code>grunt build:android</code>. This way, I could customize some of the loaded scripts, images, etc each with their own build directives.</p> <p><strike>So, should I go through my Gruntfile copying and pasting all of by <code>dist</code> and <code>build</code> directives responsibly? Or, is there a best practice for this?</strike> (tried this, didn't work)</p> <p>Yeoman folks, is this possible?</p> <p>Here is my current <strong>Gruntfile.js</strong> in case it would be useful to see.</p> <pre><code>'use strict'; var LIVERELOAD_PORT = 35729; var lrSnippet = require('connect-livereload')({port: LIVERELOAD_PORT}); var mountFolder = function (connect, dir) { return connect.static(require('path').resolve(dir)); }; // # Globbing // for performance reasons we're only matching one level down: // 'test/spec/{,*/}*.js' // use this if you want to recursively match all subfolders: // 'test/spec/**/*.js' module.exports = function (grunt) { // show elapsed time at the end require('time-grunt')(grunt); // load all grunt tasks require('load-grunt-tasks')(grunt); // configurable paths var yeomanConfig = { app: 'app', dist: '../www' }; grunt.initConfig({ yeoman: yeomanConfig, watch: { coffee: { files: ['&lt;%= yeoman.app %&gt;/scripts/{,*/}*.coffee'], tasks: ['coffee:dist'] }, coffeeTest: { files: ['test/spec/{,*/}*.coffee'], tasks: ['coffee:test'] }, compass: { files: ['&lt;%= yeoman.app %&gt;/styles/{,*/}*.{scss,sass}'], tasks: ['compass:server', 'autoprefixer'] }, styles: { files: ['&lt;%= yeoman.app %&gt;/styles/{,*/}*.css'], tasks: ['copy:styles', 'autoprefixer'] }, livereload: { options: { livereload: LIVERELOAD_PORT }, files: [ '&lt;%= yeoman.app %&gt;/*.html', '.tmp/styles/{,*/}*.css', '{.tmp,&lt;%= yeoman.app %&gt;}/scripts/{,*/}*.js', '&lt;%= yeoman.app %&gt;/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}' ] } }, connect: { options: { port: 9000, // change this to '0.0.0.0' to access the server from outside hostname: 'localhost' }, livereload: { options: { middleware: function (connect) { return [ lrSnippet, mountFolder(connect, '.tmp'), mountFolder(connect, yeomanConfig.app) ]; } } }, test: { options: { middleware: function (connect) { return [ mountFolder(connect, '.tmp'), mountFolder(connect, 'test'), mountFolder(connect, yeomanConfig.app) ]; } } }, dist: { options: { middleware: function (connect) { return [ mountFolder(connect, yeomanConfig.dist) ]; } } } }, open: { server: { path: 'http://localhost:&lt;%= connect.options.port %&gt;' } }, clean: { options: { force: true }, dist: { files: [{ dot: true, src: [ '.tmp', '&lt;%= yeoman.dist %&gt;/*', '!&lt;%= yeoman.dist %&gt;/.git*' ] }] }, server: '.tmp' }, jshint: { options: { jshintrc: '.jshintrc' }, all: [ 'Gruntfile.js', '&lt;%= yeoman.app %&gt;/scripts/{,*/}*.js', '!&lt;%= yeoman.app %&gt;/scripts/vendor/*', 'test/spec/{,*/}*.js' ] }, mocha: { all: { options: { run: true, urls: ['http://localhost:&lt;%= connect.options.port %&gt;/index.html'] } } }, coffee: { dist: { files: [{ expand: true, cwd: '&lt;%= yeoman.app %&gt;/scripts', src: '{,*/}*.coffee', dest: '.tmp/scripts', ext: '.js' }] }, test: { files: [{ expand: true, cwd: 'test/spec', src: '{,*/}*.coffee', dest: '.tmp/spec', ext: '.js' }] } }, compass: { options: { sassDir: '&lt;%= yeoman.app %&gt;/styles', cssDir: '.tmp/styles', generatedImagesDir: '.tmp/images/generated', imagesDir: '&lt;%= yeoman.app %&gt;/images', javascriptsDir: '&lt;%= yeoman.app %&gt;/scripts', fontsDir: '&lt;%= yeoman.app %&gt;/styles/fonts', importPath: '&lt;%= yeoman.app %&gt;/bower_components', httpImagesPath: '/images', httpGeneratedImagesPath: '/images/generated', httpFontsPath: '/styles/fonts', relativeAssets: false }, dist: { options: { generatedImagesDir: '&lt;%= yeoman.dist %&gt;/images/generated' } }, server: { options: { debugInfo: true } } }, autoprefixer: { options: { browsers: ['last 1 version'] }, dist: { files: [{ expand: true, cwd: '.tmp/styles/', src: '{,*/}*.css', dest: '.tmp/styles/' }] } }, // not used since Uglify task does concat, // but still available if needed /*concat: { dist: {} },*/ requirejs: { dist: { // Options: https://github.com/jrburke/r.js/blob/master/build/example.build.js options: { // `name` and `out` is set by grunt-usemin baseUrl: yeomanConfig.app + '/scripts', optimize: 'none', // TODO: Figure out how to make sourcemaps work with grunt-usemin // https://github.com/yeoman/grunt-usemin/issues/30 //generateSourceMaps: true, // required to support SourceMaps // http://requirejs.org/docs/errors.html#sourcemapcomments preserveLicenseComments: false, useStrict: true, wrap: true //uglify2: {} // https://github.com/mishoo/UglifyJS2 } } }, rev: { dist: { files: { src: [ '&lt;%= yeoman.dist %&gt;/scripts/{,*/}*.js', '&lt;%= yeoman.dist %&gt;/styles/{,*/}*.css', '&lt;%= yeoman.dist %&gt;/images/{,*/}*.{png,jpg,jpeg,gif,webp}', '&lt;%= yeoman.dist %&gt;/styles/fonts/{,*/}*.*' ] } } }, useminPrepare: { options: { dest: '&lt;%= yeoman.dist %&gt;' }, html: '&lt;%= yeoman.app %&gt;/index.html' }, usemin: { options: { dirs: ['&lt;%= yeoman.dist %&gt;'] }, html: ['&lt;%= yeoman.dist %&gt;/{,*/}*.html'], css: ['&lt;%= yeoman.dist %&gt;/styles/{,*/}*.css'] }, imagemin: { dist: { files: [{ expand: true, cwd: '&lt;%= yeoman.app %&gt;/images', src: '{,*/}*.{png,jpg,jpeg}', dest: '&lt;%= yeoman.dist %&gt;/images' }] } }, svgmin: { dist: { files: [{ expand: true, cwd: '&lt;%= yeoman.app %&gt;/images', src: '{,*/}*.svg', dest: '&lt;%= yeoman.dist %&gt;/images' }] } }, cssmin: { // This task is pre-configured if you do not wish to use Usemin // blocks for your CSS. By default, the Usemin block from your // `index.html` will take care of minification, e.g. // // &lt;!-- build:css({.tmp,app}) styles/main.css --&gt; // // dist: { // files: { // '&lt;%= yeoman.dist %&gt;/styles/main.css': [ // '.tmp/styles/{,*/}*.css', // '&lt;%= yeoman.app %&gt;/styles/{,*/}*.css' // ] // } // } }, htmlmin: { dist: { options: { /*removeCommentsFromCDATA: true, // https://github.com/yeoman/grunt-usemin/issues/44 //collapseWhitespace: true, collapseBooleanAttributes: true, removeAttributeQuotes: true, removeRedundantAttributes: true, useShortDoctype: true, removeEmptyAttributes: true, removeOptionalTags: true*/ }, files: [{ expand: true, cwd: '&lt;%= yeoman.app %&gt;', src: '*.html', dest: '&lt;%= yeoman.dist %&gt;' }] } }, // Put files not handled in other tasks here copy: { dist: { files: [{ expand: true, dot: true, cwd: '&lt;%= yeoman.app %&gt;', dest: '&lt;%= yeoman.dist %&gt;', src: [ '*.{ico,png,txt}', '.htaccess', 'images/{,*/}*.{webp,gif}', 'styles/fonts/{,*/}*.*' ] }] }, styles: { expand: true, dot: true, cwd: '&lt;%= yeoman.app %&gt;/styles', dest: '.tmp/styles/', src: '{,*/}*.css' } }, modernizr: { devFile: '&lt;%= yeoman.app %&gt;/bower_components/modernizr/modernizr.js', outputFile: '&lt;%= yeoman.dist %&gt;/bower_components/modernizr/modernizr.js', files: [ '&lt;%= yeoman.dist %&gt;/scripts/{,*/}*.js', '&lt;%= yeoman.dist %&gt;/styles/{,*/}*.css', '!&lt;%= yeoman.dist %&gt;/scripts/vendor/*' ], uglify: true }, concurrent: { server: [ 'compass', 'coffee:dist', 'copy:styles' ], test: [ 'coffee', 'copy:styles' ], dist: [ 'coffee', 'compass', 'copy:styles', 'imagemin', 'svgmin', 'htmlmin' ] }, bower: { options: { exclude: ['modernizr'] }, all: { rjsConfig: '&lt;%= yeoman.app %&gt;/scripts/main.js' } } }); grunt.registerTask('server', function (target) { if (target === 'dist') { return grunt.task.run(['build', 'open', 'connect:dist:keepalive']); } grunt.task.run([ 'clean:server', 'concurrent:server', 'autoprefixer', 'connect:livereload', 'open', 'watch' ]); }); grunt.registerTask('test', [ 'clean:server', 'concurrent:test', 'autoprefixer', 'connect:test', 'mocha' ]); grunt.registerTask('build', [ 'clean:dist', 'useminPrepare', 'concurrent:dist', 'autoprefixer', 'requirejs', 'concat', 'cssmin', 'uglify', 'modernizr', 'copy:dist', 'rev', 'usemin' ]); grunt.registerTask('default', [ 'jshint', 'test', 'build' ]); }; </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload