├── .gitignore ├── .travis.yml ├── Gulpfile.js ├── LICENSE ├── README.md ├── index.js ├── package.json └── test ├── fixtures └── Gruntfile.js └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | # node 2 | 3 | lib-cov 4 | *.seed 5 | *.log 6 | *.csv 7 | *.dat 8 | *.out 9 | *.pid 10 | *.gz 11 | *.node 12 | 13 | pids 14 | logs 15 | results 16 | 17 | npm-debug.log 18 | 19 | .DS_Store 20 | node_modules 21 | build 22 | 23 | # idea 24 | .idea/ 25 | *.iml -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.10 4 | threadsafe: yes 5 | before_install: npm install -g grunt-cli -------------------------------------------------------------------------------- /Gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var mocha = require('gulp-mocha'); 3 | 4 | gulp.task('test', function (cb) { 5 | gulp.src('test/*.js') 6 | .pipe(mocha({ 7 | reporter: 'spec' 8 | })); 9 | }); 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 gratimax 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [gulp](https://github.com/gulpjs/gulp)-grunt 2 | 3 | 4 | #### Run grunt tasks from gulp 5 | [![NPM version](https://badge.fury.io/js/gulp-grunt.png)](https://npmjs.org/package/gulp-grunt) 6 | [![travis build](https://api.travis-ci.org/gratimax/gulp-grunt.png)](https://travis-ci.org/gratimax/gulp-grunt) 7 | [![dependencies](https://david-dm.org/gratimax/gulp-grunt.png)](https://david-dm.org/gratimax/gulp-grunt) 8 | 9 | What if your favorite grunt plugin isn't available for gulp yet? 10 | Don't fret, there is nothing to worry about! 11 | Why don't you just hook in your grunt configuration? 12 | 13 | This plugin is a bit different from most other gulp plugins. 14 | You cannot use it inline, because it does not create a stream. 15 | Rather, use it at the top of your gulpfile, calling it with your gulp as an argument. 16 | This classifies gulp-grunt as _gulpfriendly_, not a gulpplugin. 17 | 18 | ## Example usage 19 | ```js 20 | var gulp = require('gulp'); 21 | require('gulp-grunt')(gulp); // add all the gruntfile tasks to gulp 22 | 23 | // continue defining tasks... 24 | gulp.task('do-this', function() { 25 | ... 26 | }); 27 | 28 | // run them like any other task 29 | gulp.task('default', [ 30 | // run complete grunt tasks 31 | 'grunt-minify', 32 | 'grunt-test', 33 | // or run specific targets 34 | 'grunt-sass:dist', 35 | 'grunt-browserify:dev' 36 | ]); 37 | ``` 38 | Note that all the grunt tasks that were added begin with the prefix 'grunt-'. 39 | This is for usability, so that your grunt tasks do not clash with your gulp tasks. 40 | Also note that `require('gulp-grunt')(gulp)` does not have to be at the top of your file. 41 | It could very well be at the bottom, except that then it could possibly overwrite some of your 42 | gulp tasks. 43 | 44 | To run specific targets, use the regular grunt syntax `[task]:[target]`, as in the example above. (To learn more about Grunt targets, check out [the Grunt documentation](http://gruntjs.com/configuring-tasks#task-configuration-and-targets).) 45 | 46 | ## Functions 47 | 48 | ### gulp-grunt() 49 | __Takes__ `(gulp, options)` 50 | 51 | Configuration is done with the function call: 52 | ```js 53 | require('gulp-grunt')(gulp, { 54 | base: ..., 55 | prefix: ... 56 | }); 57 | ``` 58 | This function appends all the grunt tasks it has found to your gulp object as normal gulp tasks. 59 | 60 | #### gulp 61 | Your gulp object that you imported with the code: 62 | ```js 63 | var gulp = require('gulp'); 64 | ``` 65 | Pass it in and gulp-grunt will add all the tasks. 66 | 67 | #### options 68 | `options` is the configuration object you pass in. 69 | 70 | #### options.base 71 | This tells grunt where to look for your gruntfile. 72 | Set it to some absolute path. 73 | This may require you to use `path.join` for relative paths: 74 | ```js 75 | require('gulp-grunt')(gulp, { 76 | base: require('path').join(__dirname, 'yourrelativepathhere') 77 | }); 78 | ``` 79 | 80 | #### options.prefix 81 | This tells gulp-grunt how to prefix your tasks. 82 | For instance, if in the gruntfile you define the tasks 'minify' and 'compile', 83 | and if you pass gulp-grunt this configuration: 84 | ```js 85 | require('gulp-grunt')(gulp, { 86 | prefix: 'theknightswhosay-' 87 | }) 88 | ``` 89 | The grunt tasks can be called from gulp, except they would have the prefix, so 90 | 'theknightswhosay-minify' and 'theknightswhosay-compile'. 91 | 92 | You can simply pass in an empty string(`''`) if you wish to have no prefix. 93 | 94 | #### options.verbose 95 | If this option is enabled(true), then gulp-grunt will tell you when it starts running a Grunt task or stops it. 96 | This option is mainly for debugging. 97 | 98 | #### options.force 99 | If this option set to `true`, grunt task will never fail, but just give you a warning instead. 100 | 101 | #### default options 102 | 103 | ```js 104 | { 105 | base: null, // this is just the directory that your Gulpfile is in 106 | prefix: 'grunt-', 107 | verbose: false, 108 | force: true 109 | } 110 | ``` 111 | 112 | ### gulp-grunt.tasks() 113 | __Takes__ `(options)` 114 | 115 | This just returns all the grunt tasks found, along with their associated functions. 116 | Calling is essentially the same as with the main function: 117 | ```js 118 | var gulp_grunt = require('gulp-grunt') 119 | var tasks = gulp_grunt.tasks({ 120 | base: ..., 121 | prefix: ... 122 | }); 123 | ``` 124 | Output is something like: 125 | ```js 126 | { 127 | 'grunt-test': [Function], 128 | 'grunt-minify': [Function] 129 | // etc... 130 | } 131 | ``` 132 | 133 | #### options 134 | This object is the exact same as for [gulp-grunt()](#gulp-grunt-1) above. 135 | This tells gulp-grunt what prefix to use and what base to search for, among other things. 136 | 137 | *** 138 | 139 | Have fun grunting and gulping! :D 140 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var grunt = require('grunt'); 2 | var spawn = require('child_process').spawn; 3 | 4 | var gruntCmd = (process.platform === 'win32') ? 'grunt.cmd' : 'grunt'; 5 | 6 | var makeOptions = function (options) { 7 | 8 | var baseOptions = { 9 | base: null, 10 | prefix: 'grunt-', 11 | verbose: false, 12 | force: true 13 | }; 14 | 15 | if (options) { 16 | for (var key in options) { 17 | baseOptions[key] = options[key]; 18 | if(key != 'base' && key != 'prefix'){ 19 | grunt.option(key, options[key]); 20 | } 21 | } 22 | } 23 | 24 | return baseOptions; 25 | }; 26 | 27 | module.exports = function (gulp, options) { 28 | var tasks = getTasks(options); 29 | 30 | for (var name in tasks) { 31 | if (tasks.hasOwnProperty(name)) { 32 | var fn = tasks[name]; 33 | gulp.task(name, fn); 34 | } 35 | } 36 | 37 | }; 38 | 39 | var getTasks = module.exports.tasks = function (options) { 40 | var opt = makeOptions(options); 41 | 42 | var oldCwd = process.cwd(); 43 | var cwd = opt.base != null ? opt.base : oldCwd; 44 | 45 | grunt.file.setBase(cwd); 46 | 47 | var gruntCliDir = opt.base ? (opt.base + "/") : ""; 48 | 49 | grunt.task.init([]); 50 | 51 | process.chdir(oldCwd); 52 | 53 | var gruntTasks = grunt.task._tasks, 54 | finalTasks = {}; 55 | 56 | var registerGruntTask = function (name) { 57 | finalTasks[opt.prefix + name] = function (cb) { 58 | if (opt.verbose) { 59 | console.log('[grunt-gulp] Running Grunt "' + name + '" task...'); 60 | } 61 | var args = opt.force ? [name, '--force', '--verbose=' + opt.verbose] : [name, '--verbose=' + opt.verbose]; 62 | for (var key in opt) { 63 | if (key != 'base' && key != 'prefix') { 64 | args = args.concat('--' + key + '=' + opt[key]); 65 | } 66 | } 67 | var child = spawn( 68 | gruntCliDir + gruntCmd, 69 | args, 70 | {cwd: cwd} 71 | ); 72 | child.stdout.on('data', function (d) { 73 | grunt.log.write(d); 74 | }); 75 | child.stderr.on('data', function (d) { 76 | grunt.log.error(d); 77 | }); 78 | child.on('close', function (code) { 79 | if (opt.verbose) { 80 | grunt.log.ok('[grunt-gulp] Done running Grunt "' + name + '" task.'); 81 | } 82 | if (code != 0) { 83 | grunt.fail.warn('[grunt-gulp] Failed running Grunt "' + name + '" task.') 84 | } 85 | cb(); 86 | }); 87 | }; 88 | } 89 | 90 | for (var name in gruntTasks) { 91 | if (gruntTasks.hasOwnProperty(name)) { 92 | // add tasks 93 | registerGruntTask(name); 94 | // also add target-specific tasks 95 | for (var target in grunt.config.get(name)) { 96 | registerGruntTask(name + ':' + target); 97 | } 98 | } 99 | } 100 | 101 | return finalTasks; 102 | }; 103 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-grunt", 3 | "description": "Run grunt tasks from gulp", 4 | "version": "0.5.5", 5 | "homepage": "http://github.com/gratimax/gulp-grunt", 6 | "repository": "git://github.com/gratimax/gulp-grunt.git", 7 | "author": "gratimax ", 8 | "main": "./index.js", 9 | "keywords": [ 10 | "gulpfriendly", 11 | "grunt" 12 | ], 13 | "dependencies": { 14 | "grunt": "^1.0.1" 15 | }, 16 | "devDependencies": { 17 | "gulp-mocha": "^2.1.3", 18 | "mocha": "^2.3.3", 19 | "chai": "^3.3.0", 20 | "gulp": "^3.9.0" 21 | }, 22 | "scripts": { 23 | "test": "mocha -R spec" 24 | }, 25 | "engines": { 26 | "node": ">= 0.9.0" 27 | }, 28 | "license": "MIT" 29 | } 30 | -------------------------------------------------------------------------------- /test/fixtures/Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | 3 | grunt.registerTask('test', 'does blah', function () { 4 | console.log('[test] you should probably see that it has tested'); 5 | }); 6 | 7 | grunt.registerTask('error', 'makes an error', function () { 8 | grunt.log.error('[test] you should see this error'); 9 | return false; 10 | }); 11 | 12 | grunt.registerTask('epic-error', 'should interupt build', function() { 13 | grunt.fail.warn('[test] I am totally failed'); 14 | return false; 15 | }); 16 | 17 | }; 18 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | var path = require('path'); 3 | var Gulp = require('gulp').Gulp; 4 | var addGrunt = require('../'); 5 | require('mocha'); 6 | 7 | describe('gulp-grunt', function () { 8 | 9 | var gulp; 10 | 11 | /* 12 | This function essentially takes a function and 'silences' it. 13 | This means that all the output will be logged to an array('out') instead of stdout. 14 | If the function only takes one parameter, 15 | that array will be passed in and the function will be synchronous. 16 | If the function takes two parameters(the array and a callback), 17 | everything is done asynchronously. 18 | This also has the advantage of making the output look very clean. 19 | */ 20 | var silence = function (silenced) { 21 | return function (cb) { 22 | var out = [], 23 | // everything back to normal 24 | revert = (function (write) { 25 | return function () { 26 | process.stdout.write = write; 27 | } 28 | })(process.stdout.write); 29 | 30 | // modify process.stdout 31 | process.stdout.write = function (string) { 32 | out.push(string); 33 | }; 34 | 35 | if (silenced.length == 1) { 36 | // If the function is synchronous 37 | silenced(out); 38 | revert(); 39 | cb(); 40 | } else if (silenced.length == 2) { 41 | // If the function is asynchronous 42 | silenced(out, function (err) { 43 | revert(); 44 | if (err) { 45 | cb(err); 46 | } else { 47 | cb(); 48 | } 49 | }); 50 | } 51 | 52 | } 53 | }; 54 | 55 | // Encapsulate the expect clauses on asynchronous silenced functions. 56 | var clause = function (fn, done) { 57 | try { 58 | fn(); 59 | done(); 60 | } catch (e) { 61 | done(e); 62 | } 63 | }; 64 | 65 | beforeEach(function () { 66 | gulp = new Gulp; 67 | }); 68 | 69 | it('should load grunt tasks', function () { 70 | addGrunt(gulp, { base: path.join(__dirname, 'fixtures')}); 71 | expect(gulp.tasks).to.have.keys(['grunt-test', 'grunt-error', 'grunt-epic-error']); 72 | }); 73 | 74 | it('should still run gulp tasks', function () { 75 | var ran = false; 76 | 77 | gulp.task('x', function () { 78 | ran = true; 79 | }); 80 | gulp.run('x'); 81 | expect(ran).to.be.true; 82 | }); 83 | 84 | it('should work with another prefix', function () { 85 | addGrunt(gulp, { base: path.join(__dirname, 'fixtures'), prefix: 'gr-' }); 86 | expect(gulp.tasks).to.have.keys(['gr-test', 'gr-error', 'gr-epic-error']); 87 | }); 88 | 89 | it('should work with no prefix', function () { 90 | addGrunt(gulp, { base: path.join(__dirname, 'fixtures'), prefix: '' }); 91 | expect(gulp.tasks).to.have.keys(['test', 'error', 'epic-error']); 92 | }); 93 | 94 | it('should run grunt tasks, which fails', silence(function (out, done) { 95 | addGrunt(gulp, { base: path.join(__dirname, 'fixtures'), force: false}); 96 | gulp.run('grunt-epic-error', function () { 97 | clause(function () { 98 | expect(out).to.include('[test] I am totally failed\n'); 99 | }, done); 100 | }); 101 | 102 | })); 103 | 104 | it('should run grunt tasks', silence(function (out, done) { 105 | addGrunt(gulp, { base: path.join(__dirname, 'fixtures')}); 106 | gulp.run('grunt-test', function () { 107 | clause(function () { 108 | expect(out).to.include('[test] you should probably see that it has tested\n'); 109 | }, done); 110 | }); 111 | 112 | })); 113 | 114 | it('should handle errors gracefully', silence(function (out, done) { 115 | addGrunt(gulp, { base: path.join(__dirname, 'fixtures') }); 116 | 117 | gulp.run('grunt-error', function () { 118 | clause(function () { 119 | var errored = false; 120 | for (var i = 0; i < out.length; i++) { 121 | if (out[i].indexOf("[test] you should see this error") != -1) { 122 | errored = true; 123 | } 124 | } 125 | expect(errored).to.be.true; 126 | }, done); 127 | }); 128 | 129 | })); 130 | 131 | }); 132 | --------------------------------------------------------------------------------