├── .gitignore ├── CHANGELOG ├── package.json ├── LICENSE-MIT ├── Gruntfile.js ├── tasks └── parallel.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | .idea/ 4 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | v0.5.1 2 | date: 2016-06-02 3 | changes: 4 | - exclude .idea/ from the package 5 | 6 | v0.5.0 7 | date: 2016-06-02 8 | changes: 9 | - compatibility with grunt v1.0 10 | - remove unused grunt dependency from `dependencies` (only keep in `devDependencies`) 11 | 12 | v0.4.1 13 | date: 2014-01-29 14 | changes: 15 | - unbreak grunt tasks 16 | 17 | v0.4 18 | date: 2014-01-29 19 | changes: 20 | - flags passed to grunt tasks 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grunt-parallel", 3 | "description": "Run tasks or commands in child processes.", 4 | "version": "0.5.1", 5 | "homepage": "https://github.com/iammerrick/grunt-parallel", 6 | "author": { 7 | "name": "Merrick Christensen", 8 | "email": "merrick.christensen@gmail.com", 9 | "url": "http://merrickchristensen.com" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/iammerrick/grunt-parallel.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/iammerrick/grunt-parallel/issues" 17 | }, 18 | "license": "MIT", 19 | "main": "Gruntfile.js", 20 | "engines": { 21 | "node": "*" 22 | }, 23 | "peerDependencies": { 24 | "grunt": ">=0.4.0" 25 | }, 26 | "devDependencies": { 27 | "grunt": ">=0.4.0" 28 | }, 29 | "keywords": [ 30 | "gruntplugin", 31 | "parallel" 32 | ], 33 | "dependencies": { 34 | "q": "~0.8.12", 35 | "lpad": "~0.1.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Merrick Christensen 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | grunt.registerTask('fast', function() { 4 | grunt.log.write('Fast task finished.'); 5 | }); 6 | 7 | grunt.registerTask('block', function() { 8 | var ms = 1000; 9 | var start = +(new Date()); 10 | while (new Date() - start < ms); 11 | grunt.log.write('Blocking finished.'); 12 | }); 13 | 14 | grunt.registerTask('fail', function() { 15 | var ms = 500; 16 | var start = +(new Date()); 17 | while (new Date() - start < ms); 18 | grunt.log.error('Failure to be awesome!'); 19 | throw new Error('Broken!'); 20 | }); 21 | 22 | // Project configuration. 23 | grunt.initConfig({ 24 | parallel: { 25 | mix: { 26 | tasks: [{ 27 | grunt: true, 28 | args: ['fast'] 29 | }, { 30 | grunt: true, 31 | args: ['block'] 32 | }, { 33 | cmd: 'pwd' 34 | },{ 35 | args: ['fast'] 36 | }] 37 | }, 38 | shell: { 39 | tasks: [{ 40 | cmd: 'whoami' 41 | }] 42 | }, 43 | grunt: { 44 | options: { 45 | grunt: true 46 | }, 47 | tasks: ['fast', 'block', 'fast'] 48 | }, 49 | stream: { 50 | options: { 51 | stream: true 52 | }, 53 | tasks: [{ cmd: 'tail', args: ['-f', '/var/log/system.log']}] 54 | } 55 | } 56 | }); 57 | 58 | // Load local tasks. 59 | grunt.loadTasks('tasks'); 60 | 61 | grunt.registerTask('default', ['parallel']); 62 | 63 | }; 64 | -------------------------------------------------------------------------------- /tasks/parallel.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-parallel 3 | * https://github.com/iammerrick/grunt-parallel 4 | * 5 | * Copyright (c) 2013 Merrick Christensen 6 | * Licensed under the MIT license. 7 | */ 8 | /*jshint es5:true*/ 9 | module.exports = function(grunt) { 10 | var Q = require('q'); 11 | var lpad = require('lpad'); 12 | 13 | function spawn(task) { 14 | var deferred = Q.defer(); 15 | 16 | grunt.util.spawn(task, function(error, result, code) { 17 | grunt.log.writeln(); 18 | lpad.stdout(' '); 19 | 20 | if (error || code !== 0) { 21 | var message = result.stderr || result.stdout; 22 | 23 | grunt.log.error(message); 24 | lpad.stdout(); 25 | 26 | return deferred.reject(); 27 | } 28 | 29 | grunt.log.writeln(result); 30 | lpad.stdout(); 31 | 32 | deferred.resolve(); 33 | }); 34 | 35 | return deferred.promise; 36 | } 37 | 38 | grunt.registerMultiTask('parallel', 'Run sub-tasks in parallel.', function() { 39 | var done = this.async(); 40 | var options = this.options({ 41 | grunt: false, 42 | stream: false 43 | }); 44 | var flags = grunt.option.flags(); 45 | 46 | // If the configuration specifies that the task is a grunt task. Make it so. 47 | if (options.grunt === true) { 48 | this.data.tasks = this.data.tasks.map(function(task) { 49 | return { 50 | args: [task], 51 | grunt: true 52 | } 53 | }); 54 | } 55 | 56 | // Normalize tasks config. 57 | this.data.tasks = this.data.tasks.map(function(task) { 58 | 59 | // Default to grunt it a command isn't specified 60 | if ( ! task.cmd ) { 61 | task.grunt = true; 62 | } 63 | 64 | // Pipe to the parent stdout when streaming. 65 | if ( task.stream || ( task.stream === undefined && options.stream ) ) { 66 | task.opts = task.opts || {}; 67 | task.opts.stdio = 'inherit'; 68 | } 69 | 70 | return task; 71 | }); 72 | 73 | // Allow any flags to be passed to spawned tasks 74 | // This includes the verbose flag as well as any custom task flags 75 | this.data.tasks.forEach(function ( task ) { 76 | if ( task.grunt ) { 77 | flags.forEach(function ( flag ) { 78 | task.args.push( flag ); 79 | }); 80 | } 81 | }); 82 | 83 | Q.all(this.data.tasks.map(spawn)).then(done, done.bind(this, false)); 84 | }); 85 | }; 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # grunt-parallel 2 | 3 | Run commands and tasks in parallel to speed up your build. 4 | 5 | ## Getting Started 6 | Install this grunt plugin next to your project's [Gruntfile.js gruntfile][getting_started] with: `npm install grunt-parallel --save-dev` 7 | 8 | Then add this line to your project's `Gruntfile.js` gruntfile: 9 | 10 | ```javascript 11 | grunt.loadNpmTasks('grunt-parallel'); 12 | ``` 13 | 14 | [grunt]: http://gruntjs.com/ 15 | [getting_started]: https://github.com/gruntjs/grunt/blob/master/docs/getting_started.md 16 | 17 | ## Documentation 18 | 19 | ## The Configuration 20 | 21 | ```javascript 22 | grunt.initConfig({ 23 | parallel: { 24 | mix: { 25 | tasks: [{ 26 | grunt: true, 27 | args: ['fast'] 28 | }, { 29 | grunt: true, 30 | args: ['block'] 31 | }, { 32 | cmd: 'pwd' 33 | },{ 34 | grunt: true, 35 | args: ['fast'] 36 | }] 37 | }, 38 | shell: { 39 | tasks: [{ 40 | cmd: 'whoami' 41 | }] 42 | }, 43 | grunt: { 44 | options: { 45 | grunt: true 46 | }, 47 | tasks: ['fast', 'block', 'fast'] 48 | }, 49 | stream: { 50 | options: { 51 | stream: true 52 | }, 53 | tasks: [ { cmd: 'tail', args: ['-f', '/var/log/system.log'] }] 54 | } 55 | } 56 | }); 57 | ``` 58 | 59 | ## Example 60 | 61 | ![Example](http://f.cl.ly/items/3e281L3X3h01293q3Z11/grunt-parallel.png) 62 | 63 | 64 | ### Settings 65 | 66 | * tasks - An array of commands to run, each deferred to: http://gruntjs.com/api/grunt.util#grunt.util.spawn 67 | 68 | ```javascript 69 | grunt.initConfig({ 70 | parallel: { 71 | assets: { 72 | tasks: [{ 73 | grunt: true, 74 | args: ['requirejs'] 75 | }, { 76 | grunt: true, 77 | args: ['compass'] 78 | },{ 79 | cmd: 'some-custom-shell-script.sh' 80 | }] 81 | } 82 | } 83 | }); 84 | ``` 85 | 86 | #### Streaming Log Output For Never Ending Tasks 87 | 88 | Sometimes tasks don't end and consequently you don't want to wait to receive their output till they are done, because you would never see their output. Think of watching files or tailing logs. For this case you can set the stream option to true, and all of the tasks output will be logged to your console, this is letting the sub process inherit your stdio. 89 | 90 | ```javascript 91 | grunt.initConfig({ 92 | stream: { 93 | options: { 94 | stream: true 95 | }, 96 | tasks: [{ cmd: 'tail', args: ['-f', '/var/log/system.log']}] 97 | } 98 | }); 99 | ``` 100 | 101 | Since tail runs till you send it a shutdown signal, you would like to stream the output to your stdio. 102 | 103 | #### Only Using Grunt 104 | 105 | If you are only going to delegate to other grunt tasks you can simply put `grunt: true` in your tasks configuration and grunt-parallel will run them all using grunt. 106 | 107 | ```javascript 108 | grunt.initConfig({ 109 | parallel: { 110 | assets: { 111 | options: { 112 | grunt: true 113 | }, 114 | tasks: ['fast', 'block', 'fast'] 115 | } 116 | } 117 | }); 118 | ``` 119 | 120 | One might target the task using `grunt parallel:assets`. This would run compass, requirejs, and a custom shell script at the same time, each logging to your console when they are done. 121 | 122 | ## License 123 | Copyright (c) 2013 Merrick Christensen 124 | Licensed under the MIT license. 125 | --------------------------------------------------------------------------------