Skip to content

Commit

Permalink
Tightening things up. Made banner comments more flexible, hopefully i…
Browse files Browse the repository at this point in the history
…ntuitive. Added config file directives that can go in place of files. Split banner helper into a separate misc tasks file. Added a preliminary grunt.js template.
  • Loading branch information
cowboy committed Oct 14, 2011
1 parent 831be27 commit e019c3c
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 51 deletions.
13 changes: 8 additions & 5 deletions grunt.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,22 @@ config.init({
author: '"Cowboy" Ben Alman',
license: ['MIT', 'GPL'],
copyright: 'Copyright (c) 2011 "Cowboy" Ben Alman',
repository: 'git://github.com/cowboy/node-grunt.git'
repository: 'git://github.com/cowboy/node-grunt.git',
banner: '/* {{meta.name}} - v{{meta.version}} - {{today "m/d/yyyy"}}\n' +
' * {{meta.homepage}}\n' +
' * {{{meta.copyright}}}; Licensed {{join meta.license}} */'
},
concat: {
'example/dist/main.js': ['grunt.js', 'lib/**']
'example/dist/main.js': ['<banner>', 'grunt.js', 'lib/**']
},
min: {
'example/dist/min.js': 'example/dist/main.js'
'example/dist/min.js': ['<banner>', 'example/dist/main.js']
},
test: {
files: ['test/**']
files: ['test/**/*.js']
},
lint: {
files: ['grunt.js', 'lib/**', 'test/**'],
files: ['grunt.js', 'lib/**/*.js', 'test/**/*.js', 'template/**/*.js'],
built: 'example/dist/main.js'
},
jshint: {
Expand Down
12 changes: 10 additions & 2 deletions lib/grunt/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ var namespace = urequire('namespace');
// The actual config data.
var data;

// Get config data.
// Get config data. If data hasn't been set, return null. If props string
// wasn't passed, return all data. Otherwise, return the proper prop.
exports = module.exports = function(props) {
return data ? namespace.get(data, props) : false;
return !data ? null : !props ? data : namespace.get(data, props);
};

// A not-at-all-short-cut for getting config data.
Expand Down Expand Up @@ -33,6 +34,13 @@ exports.escape = function(str) {
return str.replace(/\./g, '\\.');
};

// If a <SOMETHING> directive is passed, return SOMETHING, otherwise null.
var directiveRe = /^<(.*)>$/;
exports.getDirective = function(str) {
var matches = str.match(directiveRe);
return matches && matches[1];
};

// Test to see if required config params have been defined. If not, throw an
// exception (use this inside of a task).
exports.requires = function() {
Expand Down
15 changes: 13 additions & 2 deletions lib/grunt/file.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,23 @@ exports.expand = function() {
// Use the first argument if it's an Array, otherwise convert the arguments
// object to an array and use that.
var patterns = arguments[0] instanceof Array ? arguments[0] : [].slice.call(arguments);
// Generate a should-be-unique number.
var uid = +new Date();
// Return a uniqued array of matching file paths.
return _(patterns).chain().map(function(pattern) {
// Just return the pattern if it's an internal directive.
if (config.getDirective(pattern)) { return pattern; }
// Otherwise, expand paths.
return glob.globSync(pattern, glob.GLOB_DEFAULT | glob.GLOB_NOCHECK);
}).flatten().uniq().filter(function(path) {
}).flatten().uniq(false, function(filepath) {
// Only unique file paths, but don't unique <something> directives, in case
// they are repeated intentionally.
return config.getDirective(filepath) ? ++uid : filepath;
}).filter(function(filepath) {
// Just return the filepath if it's an internal directive.
if (config.getDirective(filepath)) { return filepath; }
try {
return fs.statSync(path).mode !== 16877;
return fs.statSync(filepath).mode !== 16877;
} catch(e) {
throw {name: 'TaskError', message: e.message};
}
Expand Down
2 changes: 1 addition & 1 deletion lib/grunt/help.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,6 @@ log.writeln();
log.writeln('Tasks run in the order specified. Arguments may be passed to tasks that accept them');
log.writeln('by using semicolons, like "lint:files".');
log.writeln();
log.writeln('* Passing no arguments runs a task for every sub-prop of this task\'s config prop.');
log.writeln('* Passing no arguments runs this task once for each of its config sub-properties.');

process.exit();
7 changes: 6 additions & 1 deletion lib/grunt/tasks/concat.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,10 @@ task.registerTask('concat', 'Concatenate files.*', function(name) {

// Concat source files.
task.registerHelper('concat', function(files) {
return files ? files.map(file.read).join('\n') : '';
return files ? files.map(function(filepath) {
// If a directive like <banner> is passed, return that helper's result.
var helper = config.getDirective(filepath);
// Otherwise, read and return the file.
return helper ? task.helper('directive', helper) : file.read(filepath);
}).join('\n') : '';
});
2 changes: 1 addition & 1 deletion lib/grunt/tasks/config.js → lib/grunt/tasks/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// TASKS
// ============================================================================

task.registerTask('config:gen', 'Generate a sample "grunt.js" config file in the current directory.', function() {
task.registerTask('init:config', 'Generate a sample "grunt.js" config file in the current directory.', function() {
console.log('TODO: WRITE THIS TASK');
});

Expand Down
55 changes: 16 additions & 39 deletions lib/grunt/tasks/min.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
var uglifyjs = require('uglify-js');
var zlib = require('zlib');
var handlebars = require('handlebars');
var dateformat = require('dateformat');

// ============================================================================
// TASKS
Expand All @@ -19,13 +17,21 @@ task.registerTask('min', 'Minify files with UglifyJS.*', function(name) {
// Fail if any required config properties have been omitted.
config.requires(propname);

// Concat specified files.
var errorcount = fail.errorcount;
var files = file.expand(config(propname));
// Get banner, if specified. It would be nice if UglifyJS supported ignoring
// all comments matching a certain pattern, like /*!...*/, but it doesn't.
var banner = '';
if (config.getDirective(files[0]) === 'banner') {
files.shift();
banner = task.helper('banner');
}
// Concat specified files. This should really be a single, pre-built (and
// linted) file, but it supports any number of files.
var max = task.helper('concat', files);

// Concat banner + minified source.
var min = task.helper('banner') + task.helper('uglify', max, config('uglify'));
var min = banner + task.helper('uglify', max, config('uglify'));
file.write(name, min);

// Fail task if there were errors.
Expand All @@ -47,12 +53,12 @@ task.registerTask('min', 'Minify files with UglifyJS.*', function(name) {
// Minify with UglifyJS.
// From https://github.com/mishoo/UglifyJS
task.registerHelper('uglify', function(src, options) {
var msg = 'Minifying with UglifyJS...';
verbose.write(msg);
if (!options) { options = {}; }
var jsp = uglifyjs.parser;
var pro = uglifyjs.uglify;
var ast;
options = options || {};
var ast, pos;
var msg = 'Minifying with UglifyJS...';
verbose.write(msg);
try {
ast = jsp.parse(src);
ast = pro.ast_mangle(ast, options.mangle || {});
Expand All @@ -64,7 +70,8 @@ task.registerHelper('uglify', function(src, options) {
} catch(e) {
// Something went wrong.
verbose.or.write(msg);
log.error().error('[L' + e.line + ':C' + e.col + '] ' + e.message + ' (position: ' + e.pos + ')');
pos = '['.red + ('L' + e.line).yellow + ':'.red + ('C' + e.col).yellow + ']'.red;
log.error().writeln(pos + ' ' + (e.message + ' (position: ' + e.pos + ')').yellow);
fail.warn('UglifyJS found errors.');
}
});
Expand All @@ -78,33 +85,3 @@ task.registerHelper('gzip', function(src) {
task.registerHelper('strip_comments', function(src) {
return src.replace(/\/\*[\s\S]*?\*\/\n*/g, '\n');
});

// Generate banner from template.
task.registerHelper('banner', function() {
var banner;
if (config('build.banner')) {
verbose.write('Generating banner...');
try {
banner = handlebars.compile(config('build.banner'))(config('meta')) + '\n';
verbose.ok();
} catch(e) {
banner = '';
verbose.error();
log.error(e.message);
fail.warn('Handlebars found errors.');
}
} else {
verbose.writeln('Note, no build "banner" template defined.');
banner = '';
}
return banner;
});

// Banner helpers.
handlebars.registerHelper('today', function(format) {
return dateformat(new Date(), format);
});

handlebars.registerHelper('join', function(items, separator) {
return items.join(typeof separator === 'string' ? separator : ', ');
});
49 changes: 49 additions & 0 deletions lib/grunt/tasks/misc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
var handlebars = require('handlebars');
var dateformat = require('dateformat');

// ============================================================================
// HELPERS
// ============================================================================

// Used to run a helper associated with a directive.
task.registerHelper('directive', function(dir) {
// If str is a directive, strip off the surrounding <>.
dir = config.getDirective(dir) || dir;
// Split directive into arguments.
var args = dir.split(':');
// If the helper exists, pass all args in and return its value, otherwise
// return null.
return task._helpers[args[0]] ? task.helper.apply(task, args) : null;
});

// Generate banner from template.
task.registerHelper('banner', function(prop) {
if (!prop) { prop = 'meta.banner'; }
var banner;
if (config(prop)) {
verbose.write('Generating banner...');
try {
// Compile and run template, passing in config object as the data source.
banner = handlebars.compile(config(prop))(config()) + '\n';
verbose.ok();
} catch(e) {
banner = '';
verbose.error();
log.error(e.message);
fail.warn('Handlebars found errors.');
}
} else {
fail.warn('No "' + prop + '" banner template defined.');
banner = '';
}
return banner;
});

// Banner helpers.
handlebars.registerHelper('today', function(format) {
return dateformat(new Date(), format);
});

handlebars.registerHelper('join', function(items, separator) {
return items.join(typeof separator === 'string' ? separator : ', ');
});
48 changes: 48 additions & 0 deletions template/grunt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Config.
config.init({
meta: {
name: 'project_name',
version: '0.1.0',
description: '',
homepage: 'http://github.com/your_name/project',
author: 'your_name',
license: ['MIT', 'GPL'],
copyright: 'Copyright (c) YYYY your_name',
repository: 'git://github.com/your_name/project.git',
banner: '/* {{meta.name}} - v{{meta.version}} - {{today "m/d/yyyy"}}\n' +
' * {{meta.homepage}}\n' +
' * {{{meta.copyright}}}; Licensed {{join meta.license}} */'
},
concat: {
'dist/project.js': ['<banner>', 'lib/project.js']
},
min: {
'dist/project.min.js': ['<banner>', 'dist/project.js']
},
test: {
files: ['test/**/*.js']
},
lint: {
files: ['grunt.js', 'lib/**/*.js', 'test/**/*.js'],
built: 'dist/project.js'
},
jshint: {
options: {
curly: true,
eqeqeq: true,
immed: true,
latedef: true,
newcap: true,
noarg: true,
sub: true,
undef: true,
eqnull: true
},
globals: {
}
},
uglify: {}
});

// Default task.
task.registerTask('default', 'lint:files test:files concat lint:built min');

0 comments on commit e019c3c

Please sign in to comment.