Break out task running functionality into a lib. Start on nospawn option.
This commit is contained in:
parent
09586abc0c
commit
8572348cea
@ -43,3 +43,6 @@ Yes. Although `grunt-contrib-watch` is a replacement watch task for Grunt v0.4,
|
||||
|
||||
## Why is the watch devouring all my memory?
|
||||
Likely because of an enthusiastic pattern trying to watch thousands of files. Such as `'**/*.js'` but forgetting to exclude the `node_modules` folder with `'!node_modules/**/*.js'`. Try grouping your files within a subfolder or be more explicit with your file matching pattern.
|
||||
|
||||
## Why spawn as child processes as a default?
|
||||
TODO: Write this.
|
||||
|
@ -12,6 +12,12 @@ Type: `String|Array`
|
||||
|
||||
This defines which tasks to run when a watched file event occurs.
|
||||
|
||||
## options.nospawn
|
||||
Type: `boolean`
|
||||
Default: false
|
||||
|
||||
TODO: Write this.
|
||||
|
||||
## options.interrupt
|
||||
Type: `boolean`
|
||||
Default: false
|
||||
|
77
tasks/lib/taskrun.js
Normal file
77
tasks/lib/taskrun.js
Normal file
@ -0,0 +1,77 @@
|
||||
var grunt = require('grunt');
|
||||
|
||||
var taskrun = module.exports = {
|
||||
waiting: 'Waiting...',
|
||||
cliArgs: null,
|
||||
nameArgs: null,
|
||||
changedFiles: Object.create(null)
|
||||
};
|
||||
|
||||
// Do this when watch has completed
|
||||
function completed(start) {
|
||||
grunt.log.writeln('').write(String(
|
||||
'Completed in ' +
|
||||
Number((Date.now() - start) / 1000).toFixed(2) +
|
||||
's at ' +
|
||||
(new Date()).toString()
|
||||
).cyan + ' - ' + taskrun.waiting);
|
||||
}
|
||||
|
||||
// Do this when watch has been triggered
|
||||
function triggered() {
|
||||
grunt.log.ok();
|
||||
Object.keys(taskrun.changedFiles).forEach(function(filepath) {
|
||||
// Log which file has changed, and how.
|
||||
grunt.log.ok('File "' + filepath + '" ' + taskrun.changedFiles[filepath] + '.');
|
||||
});
|
||||
// Reset changedFiles
|
||||
taskrun.changedFiles = Object.create(null);
|
||||
}
|
||||
|
||||
// Keep track of spawned processes
|
||||
var spawned = Object.create(null);
|
||||
|
||||
taskrun.spawn = grunt.util._.debounce(function(id, tasks, options, done) {
|
||||
// If interrupted, reset the spawned for a target
|
||||
if (options.interrupt && typeof spawned[id] === 'object') {
|
||||
grunt.log.writeln('').write('Previously spawned task has been interrupted...'.yellow);
|
||||
spawned[id].kill('SIGINT');
|
||||
delete spawned[id];
|
||||
}
|
||||
|
||||
// Only spawn one at a time unless interrupt is specified
|
||||
if (!spawned[id]) {
|
||||
triggered();
|
||||
|
||||
// Spawn the tasks as a child process
|
||||
var start = Date.now();
|
||||
spawned[id] = grunt.util.spawn({
|
||||
// Spawn with the grunt bin
|
||||
grunt: true,
|
||||
// Run from current working dir and inherit stdio from process
|
||||
opts: {cwd: process.cwd(), stdio: 'inherit'},
|
||||
// Run grunt this process uses, append the task to be run and any cli options
|
||||
args: grunt.util._.union(tasks, taskrun.cliArgs)
|
||||
}, function(err, res, code) {
|
||||
// Spawn is done
|
||||
delete spawned[id];
|
||||
completed(start);
|
||||
});
|
||||
}
|
||||
}, 250);
|
||||
|
||||
taskrun.nospawn = grunt.util._.debounce(function(id, tasks, options, done) {
|
||||
// todo: add interrupt
|
||||
|
||||
triggered();
|
||||
|
||||
// Mark tasks to run and enqueue this task afterward
|
||||
var start = Date.now();
|
||||
grunt.task.run(tasks).mark().run(taskrun.nameArgs);
|
||||
|
||||
// todo: check if subsequent runs and run completed with start at watch beginning
|
||||
completed(start);
|
||||
|
||||
// Finish the task
|
||||
done();
|
||||
}, 250);
|
@ -10,12 +10,13 @@ module.exports = function(grunt) {
|
||||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var Gaze = require('gaze').Gaze;
|
||||
var taskrun = require('./lib/taskrun');
|
||||
|
||||
// Default options for the watch task
|
||||
var defaults = {
|
||||
interrupt: false
|
||||
interrupt: false,
|
||||
nospawn: false
|
||||
};
|
||||
|
||||
grunt.registerTask('watch', 'Run predefined tasks whenever watched files change.', function(target) {
|
||||
@ -40,69 +41,15 @@ module.exports = function(grunt) {
|
||||
targets.push({files: watch.files, tasks: watch.tasks});
|
||||
}
|
||||
|
||||
// Message to display when waiting for changes
|
||||
var waiting = 'Waiting...';
|
||||
|
||||
// File changes to be logged.
|
||||
var changedFiles = Object.create(null);
|
||||
|
||||
// Keep track of spawns per tasks
|
||||
var spawned = Object.create(null);
|
||||
|
||||
// List of changed / deleted file paths.
|
||||
grunt.file.watchFiles = {changed: [], deleted: [], added: []};
|
||||
// This task's name + optional args, in string format.
|
||||
taskrun.nameArgs = this.nameArgs;
|
||||
|
||||
// Get process.argv options without grunt.cli.tasks to pass to child processes
|
||||
var cliArgs = grunt.util._.without.apply(null, [[].slice.call(process.argv, 2)].concat(grunt.cli.tasks));
|
||||
taskrun.cliArgs = grunt.util._.without.apply(null, [[].slice.call(process.argv, 2)].concat(grunt.cli.tasks));
|
||||
|
||||
// Call to close this task
|
||||
var done = this.async();
|
||||
grunt.log.write(waiting);
|
||||
|
||||
// Run the tasks for the changed files
|
||||
var runTasks = grunt.util._.debounce(function runTasks(i, tasks, options) {
|
||||
// If interrupted, reset the spawned for a target
|
||||
if (options.interrupt && typeof spawned[i] === 'object') {
|
||||
grunt.log.writeln('').write('Previously spawned task has been interrupted...'.yellow);
|
||||
spawned[i].kill('SIGINT');
|
||||
delete spawned[i];
|
||||
}
|
||||
|
||||
// Only spawn one at a time unless interrupt is specified
|
||||
if (!spawned[i]) {
|
||||
grunt.log.ok();
|
||||
|
||||
var fileArray = Object.keys(changedFiles);
|
||||
fileArray.forEach(function(filepath) {
|
||||
// Log which file has changed, and how.
|
||||
grunt.log.ok('File "' + filepath + '" ' + changedFiles[filepath] + '.');
|
||||
});
|
||||
|
||||
// Reset changedFiles
|
||||
changedFiles = Object.create(null);
|
||||
|
||||
// Spawn the tasks as a child process
|
||||
var start = Date.now();
|
||||
spawned[i] = grunt.util.spawn({
|
||||
// Spawn with the grunt bin
|
||||
grunt: true,
|
||||
// Run from current working dir and inherit stdio from process
|
||||
opts: {cwd: process.cwd(), stdio: 'inherit'},
|
||||
// Run grunt this process uses, append the task to be run and any cli options
|
||||
args: grunt.util._.union(tasks, cliArgs)
|
||||
}, function(err, res, code) {
|
||||
// Spawn is done
|
||||
delete spawned[i];
|
||||
var msg = String(
|
||||
'Completed in ' +
|
||||
Number((Date.now() - start) / 1000).toFixed(2) +
|
||||
's at ' +
|
||||
(new Date()).toString()
|
||||
).cyan;
|
||||
grunt.log.writeln('').write(msg + ' - ' + waiting);
|
||||
});
|
||||
}
|
||||
}, 250);
|
||||
grunt.log.write(taskrun.waiting);
|
||||
|
||||
targets.forEach(function(target, i) {
|
||||
if (typeof target.files === 'string') {
|
||||
@ -118,7 +65,7 @@ module.exports = function(grunt) {
|
||||
var options = grunt.util._.defaults(target.options || {}, defaults);
|
||||
|
||||
// Create watcher per target
|
||||
var gaze = new Gaze(patterns, options, function(err) {
|
||||
new Gaze(patterns, options, function(err) {
|
||||
if (err) {
|
||||
grunt.log.error(err.message);
|
||||
return done();
|
||||
@ -127,8 +74,8 @@ module.exports = function(grunt) {
|
||||
// On changed/added/deleted
|
||||
this.on('all', function(status, filepath) {
|
||||
filepath = path.relative(process.cwd(), filepath);
|
||||
changedFiles[filepath] = status;
|
||||
runTasks(i, target.tasks, options);
|
||||
taskrun.changedFiles[filepath] = status;
|
||||
taskrun[options.nospawn ? 'nospawn' : 'spawn'](i, target.tasks, options, done);
|
||||
});
|
||||
|
||||
// On watcher error
|
||||
|
45
test/fixtures/nospawn/Gruntfile.js
vendored
Normal file
45
test/fixtures/nospawn/Gruntfile.js
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
module.exports = function(grunt) {
|
||||
'use strict';
|
||||
|
||||
var http = require('http');
|
||||
var port = 8080;
|
||||
|
||||
grunt.initConfig({
|
||||
watch: {
|
||||
nospawn: {
|
||||
files: ['lib/nospawn.js'],
|
||||
tasks: ['server'],
|
||||
options: {
|
||||
nospawn: true
|
||||
}
|
||||
},
|
||||
spawn: {
|
||||
files: ['lib/spawn.js'],
|
||||
tasks: ['server']
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Load this watch task
|
||||
grunt.loadTasks('../../../tasks');
|
||||
|
||||
// Our test server task
|
||||
var server;
|
||||
grunt.registerTask('server', function() {
|
||||
if (!server) {
|
||||
server = http.createServer(function(req, res) {
|
||||
res.writeHead(200, {'Content-Type': 'text/plain'});
|
||||
res.end('It works!');
|
||||
}).listen(port);
|
||||
grunt.log.writeln('Server is listening...');
|
||||
} else {
|
||||
http.request({port: port}, function(res) {
|
||||
res.on('data', function(buf) {
|
||||
grunt.log.writeln(buf);
|
||||
});
|
||||
}).end();
|
||||
}
|
||||
});
|
||||
|
||||
grunt.registerTask('default', ['server', 'watch']);
|
||||
};
|
1
test/fixtures/nospawn/lib/nospawn.js
vendored
Normal file
1
test/fixtures/nospawn/lib/nospawn.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
var nospawn = true;
|
1
test/fixtures/nospawn/lib/spawn.js
vendored
Normal file
1
test/fixtures/nospawn/lib/spawn.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
var spawn = true;
|
40
test/tasks/nospawn_test.js
Normal file
40
test/tasks/nospawn_test.js
Normal file
@ -0,0 +1,40 @@
|
||||
'use strict';
|
||||
|
||||
var grunt = require('grunt');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var helper = require('./helper');
|
||||
|
||||
var fixtures = helper.fixtures;
|
||||
|
||||
function cleanUp() {
|
||||
helper.cleanUp([
|
||||
'nospawn/node_modules'
|
||||
]);
|
||||
}
|
||||
|
||||
exports.nospawn = {
|
||||
setUp: function(done) {
|
||||
cleanUp();
|
||||
fs.symlinkSync(path.join(__dirname, '../../node_modules'), path.join(fixtures, 'nospawn', 'node_modules'));
|
||||
done();
|
||||
},
|
||||
tearDown: function(done) {
|
||||
cleanUp();
|
||||
done();
|
||||
},
|
||||
nospawn: function(test) {
|
||||
//test.expect(2);
|
||||
var cwd = path.resolve(fixtures, 'nospawn');
|
||||
var assertWatch = helper.assertTask('watch', {cwd:cwd});
|
||||
assertWatch(function() {
|
||||
var write = 'var one = true;';
|
||||
grunt.file.write(path.join(cwd, 'lib', 'one.js'), write);
|
||||
}, function(result) {
|
||||
helper.verboseLog(result);
|
||||
//test.ok(result.indexOf('File "lib' + path.sep + 'one.js" changed') !== -1, 'Watch should have fired when oneTarget/lib/one.js has changed.');
|
||||
//test.ok(result.indexOf('I do absolutely nothing.') !== -1, 'echo task should have fired.');
|
||||
test.done();
|
||||
});
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user