From 6beb43077eefc184c7bfbb1f9f33ba870fe90b88 Mon Sep 17 00:00:00 2001 From: Ben Alman Date: Mon, 12 Mar 2012 18:26:25 -0400 Subject: [PATCH] No globals? --- grunt.js | 97 +- lib/grunt.js | 61 +- lib/grunt/config.js | 76 +- lib/grunt/fail.js | 39 +- lib/grunt/file.js | 123 +-- lib/grunt/help.js | 28 +- lib/grunt/log.js | 83 +- lib/grunt/option.js | 6 +- lib/grunt/task.js | 134 +-- lib/grunt/template.js | 29 +- lib/grunt/utils.js | 42 +- tasks/concat.js | 66 +- tasks/init.js | 984 +++++++++--------- tasks/init/commonjs.js | 42 +- tasks/init/commonjs/root/README.md | 2 +- tasks/init/commonjs/root/grunt.js | 120 ++- tasks/init/commonjs/root/lib/name.js | 2 +- tasks/init/gruntplugin.js | 40 +- tasks/init/gruntplugin/root/README.md | 2 +- tasks/init/gruntplugin/root/grunt.js | 92 +- tasks/init/gruntplugin/root/tasks/name.js | 44 +- tasks/init/gruntplugin/root/test/name_test.js | 4 +- tasks/init/jquery.js | 38 +- tasks/init/jquery/root/README.md | 2 +- tasks/init/jquery/root/grunt.js | 119 ++- tasks/init/jquery/root/src/name.js | 2 +- tasks/init/jquery/root/test/name_test.js | 6 +- tasks/init/licenses/LICENSE-MIT | 2 +- tasks/init/node.js | 42 +- tasks/init/node/root/README.md | 2 +- tasks/init/node/root/grunt.js | 78 +- tasks/init/node/root/lib/name.js | 2 +- tasks/lint.js | 261 ++--- tasks/min.js | 155 +-- tasks/misc.js | 106 +- tasks/qunit.js | 444 ++++---- tasks/server.js | 78 +- tasks/test.js | 275 ++--- tasks/watch.js | 279 ++--- test/grunt/file_test.js | 5 + test/grunt/utils_test.js | 3 + test/tasks/concat_test.js | 7 +- test/tasks/init_test.js | 34 +- test/tasks/misc_test.js | 32 +- 44 files changed, 2204 insertions(+), 1884 deletions(-) diff --git a/grunt.js b/grunt.js index 0abbf476a..a129cb957 100644 --- a/grunt.js +++ b/grunt.js @@ -1,46 +1,57 @@ -/*global config:true, task:true*/ -config.init({ - pkg: '', - test: { - all: ['test/**/*.js'] - }, - lint: { - all: ['grunt.js', 'lib/**/*.js', 'tasks/*.js', 'tasks/*/*.js', 'test/**/*.js'] - }, - watch: { - files: '', - tasks: 'default' - }, - jshint: { - options: { - curly: true, - eqeqeq: true, - immed: true, - latedef: true, - newcap: true, - noarg: true, - sub: true, - undef: true, - boss: true, - eqnull: true, - node: true, - es5: true +/* + * grunt + * https://github.com/cowboy/grunt + * + * Copyright (c) 2012 "Cowboy" Ben Alman + * Licensed under the MIT license. + * http://benalman.com/about/license/ + */ + +module.exports = function(grunt) { + // Grunt utilities. + var task = grunt.task; + var file = grunt.file; + var utils = grunt.utils; + var log = grunt.log; + var verbose = grunt.verbose; + var fail = grunt.fail; + var option = grunt.option; + var config = grunt.config; + var template = grunt.template; + + // Initialize config. + grunt.initConfig({ + pkg: '', + test: { + all: ['test/**/*.js'] + }, + lint: { + all: ['grunt.js', 'lib/**/*.js', 'tasks/*.js', 'tasks/*/*.js', 'test/**/*.js'] }, - globals: { - exports: true, - grunt: true, - utils: true, - task: true, - file: true, - fail: true, - config: true, - option: true, - template: true, - log: true, - verbose: true + watch: { + files: '', + tasks: 'default' + }, + jshint: { + options: { + curly: true, + eqeqeq: true, + immed: true, + latedef: true, + newcap: true, + noarg: true, + sub: true, + undef: true, + boss: true, + eqnull: true, + node: true, + es5: true + }, + globals: {} } - } -}); + }); + + // Default task. + grunt.registerTask('default', 'lint test'); -// Default task. -task.registerTask('default', 'lint test'); +}; diff --git a/lib/grunt.js b/lib/grunt.js index 617ddb27c..23778ea4f 100644 --- a/lib/grunt.js +++ b/lib/grunt.js @@ -9,30 +9,35 @@ var path = require('path'); -// Perhaps, someday, this will be the only global. -global.grunt = {}; - -// Used to require grunt libs. Sweet, sugary goodness. Mmmmn. -grunt.require = function(libname) { return require(path.join(__dirname, 'grunt', libname)); }; - -// Internal grunt libs, exposed globally for convenience. -global.utils = grunt.require('utils'); -global.task = grunt.require('task'); -global.file = grunt.require('file'); -global.fail = grunt.require('fail'); -global.config = grunt.require('config'); -global.option = grunt.require('option'); -global.template = grunt.require('template'); -global.log = grunt.require('log'); -global.verbose = log.verbose; - -grunt.version = file.readJson(path.join(__dirname, '../package.json')).version; -exports.version = grunt.version; +var grunt = module.exports = {}; + +// Internal grunt libs, exposed for convenience. +var utils = grunt.utils = require('./grunt/utils'); +var template = grunt.template = require('./grunt/template'); +var log = grunt.log = require('./grunt/log'); +var verbose = grunt.verbose = log.verbose; +var fail = grunt.fail = require('./grunt/fail'); +var file = grunt.file = require('./grunt/file'); +var option = grunt.option = require('./grunt/option'); +var config = grunt.config = require('./grunt/config'); +var task = grunt.task = require('./grunt/task'); +var version = grunt.version = file.readJson(path.join(__dirname, '../package.json')).version; + +// More convenience stuff. +grunt.registerTask = task.registerTask; +grunt.registerMultiTask = task.registerMultiTask; +grunt.registerInitTask = task.registerInitTask; +grunt.registerHelper = task.registerHelper; +grunt.helper = task.helper.bind(task); +grunt.loadTasks = task.loadTasks; +grunt.loadNpmTasks = task.loadNpmTasks; + +grunt.initConfig = config.init; // Handle exceptions. -process.on('uncaughtException', function (e) { - fail.warn(e, 3); -}); +// process.on('uncaughtException', function (e) { +// fail.warn(e, 3); +// }); // Disable colors if --no-colors was passed. function initColors() { @@ -67,7 +72,7 @@ function initColors() { // Expose the task interface. I've never called this manually, and have no idea // how it will work. But it might. -exports.tasks = function(tasks, options, done) { +grunt.tasks = function(tasks, options, done) { // Update options with passed-in options. option.init(options); @@ -76,10 +81,10 @@ exports.tasks = function(tasks, options, done) { if (option('help')) { // Load and display help if the user did --help. - grunt.require('help'); + require('./grunt/help'); } else if (option('version')) { // Display the current grunt version if the user did --version. - log.writeln('grunt v' + grunt.version); + log.writeln('grunt v' + version); return; } @@ -125,9 +130,9 @@ exports.tasks = function(tasks, options, done) { }; // This is only executed when run via command line. -exports.cli = function(options, done) { +grunt.cli = function(options, done) { // Parse task list and options from the command line. - var cli = grunt.require('cli'); + var cli = require('./grunt/cli'); // CLI-parsed options override any passed-in "default" options. if (options) { @@ -145,5 +150,5 @@ exports.cli = function(options, done) { } // Run tasks. - exports.tasks(cli.tasks, cli.options, done); + grunt.tasks(cli.tasks, cli.options, done); }; diff --git a/lib/grunt/config.js b/lib/grunt/config.js index 3608a0e81..25d0c8c34 100644 --- a/lib/grunt/config.js +++ b/lib/grunt/config.js @@ -7,32 +7,34 @@ * http://benalman.com/about/license/ */ +var grunt = require('../grunt'); + // The actual config data. var data; -// If prop is an array, convert it to a props string. -function getPropString(prop) { - if (utils.kindOf(prop) === 'array') { - return prop.map(exports.escape).join('.'); - } - return prop; -} - // Get/set config data. If data hasn't been set, return null. If value was // passed, set value. If props string wasn't passed, return all data. Otherwise, // return the prop's value. -exports = module.exports = function(prop, value) { +var config = module.exports = function(prop, value) { if (arguments.length === 2) { // Two arguments were passed, set the property's value. - return exports.set(prop, value); + return config.set(prop, value); } else { // Get the property's value (or the entire data object). - return exports.get(prop); + return config.get(prop); } }; +// If prop is an array, convert it to a props string. +function getPropString(prop) { + if (grunt.utils.kindOf(prop) === 'array') { + return prop.map(config.escape).join('.'); + } + return prop; +} + // Get config data. -exports.get = function(prop) { +config.get = function(prop) { // Abort if no config data exists. if (!data) { return null; } // If prop is an array, convert it to a prop string. @@ -40,82 +42,82 @@ exports.get = function(prop) { if (prop) { // A property string/array was passed, get that property's value. - return utils.namespace.get(data, prop); + return grunt.utils.namespace.get(data, prop); } else { // Nothing was passed. Return a shalow clone of the actual config data. - return utils._.clone(data); + return grunt.utils._.clone(data); } }; // Set config data. -exports.set = function(prop, value) { +config.set = function(prop, value) { // Abort if no config data exists. if (!data) { return null; } // If prop is an array, convert it to a prop string. prop = getPropString(prop); // Set the property's value. - return utils.namespace.set(data, prop, value); + return grunt.utils.namespace.set(data, prop, value); }; // Get config data, processing it as a template if necessary. -exports.process = function(prop) { - return utils.recurse(exports.get(prop), function(value) { +config.process = function(prop) { + return grunt.utils.recurse(config.get(prop), function(value) { if (typeof value !== 'string') { return value; } - return template.process(value, data); + return grunt.template.process(value, data); }); }; // Initialize config data. -exports.init = function(obj) { - verbose.write('Initializing config...').ok(); +config.init = function(obj) { + grunt.verbose.write('Initializing config...').ok(); // Initialize data. data = obj || {}; // Expand data. - exports.expand(); + config.expand(); // Return data. return data; }; // Recursively expand config directives. -exports.expand = function() { +config.expand = function() { // Expand expandable directives. var expandable = ['config', 'json']; - data = utils.recurse(data, function(value) { + data = grunt.utils.recurse(data, function(value) { if (typeof value !== 'string') { return value; } // If value is an expandable directive, expand it. - var parts = task.getDirectiveParts(value) || []; - return expandable.indexOf(parts[0]) !== -1 ? task.directive(value) : value; + var parts = grunt.task.getDirectiveParts(value) || []; + return expandable.indexOf(parts[0]) !== -1 ? grunt.task.directive(value) : value; }); }; // Escape any . in name with \. so dot-based namespacing works properly. -exports.escape = function(str) { +config.escape = function(str) { return str.replace(/\./g, '\\.'); }; // Test to see if required config params have been defined. If not, throw an // exception (use this inside of a task). -exports.requires = function() { - var props = utils.toArray(arguments).map(getPropString); +config.requires = function() { + var props = grunt.utils.toArray(arguments).map(getPropString); var msg = 'Verifying option' + (props.length === 1 ? '' : 's') + - ' ' + log.wordlist(props) + ' exist' + (props.length === 1 ? 's' : '') + + ' ' + grunt.log.wordlist(props) + ' exist' + (props.length === 1 ? 's' : '') + ' in config...'; - verbose.write(msg); + grunt.verbose.write(msg); var failProps = data && props.filter(function(prop) { - return exports(prop) === undefined; + return config.get(prop) === undefined; }).map(function(prop) { return '"' + prop + '"'; }); if (data && failProps.length === 0) { - verbose.ok(); + grunt.verbose.ok(); return true; } else { - verbose.or.write(msg); - log.error().error('Unable to process task.'); + grunt.verbose.or.write(msg); + grunt.log.error().error('Unable to process task.'); if (!data) { - throw task.taskError('Unable to load config.'); + throw grunt.task.taskError('Unable to load config.'); } else { - throw task.taskError('Required config propert' + + throw grunt.task.taskError('Required config propert' + (failProps.length === 1 ? 'y' : 'ies') + ' ' + failProps.join(', ') + ' missing.'); } diff --git a/lib/grunt/fail.js b/lib/grunt/fail.js index 21e69a766..bcfea994e 100644 --- a/lib/grunt/fail.js +++ b/lib/grunt/fail.js @@ -7,6 +7,9 @@ * http://benalman.com/about/license/ */ +var grunt = require('../grunt'); +var fail = module.exports = {}; + // Error codes // 1. Generic error. // 2. Config file not found. @@ -19,7 +22,7 @@ // DRY it up! function writeln(e, mode) { - log.muted = false; + grunt.log.muted = false; // Pretty colors. var tags = { warn: ['<'.red + 'WARN'.yellow + '>'.red, ''.red], @@ -27,37 +30,37 @@ function writeln(e, mode) { }; var msg = String(e.message || e) + '\x07'; // Beep! if (mode === 'warn') { - msg += ' ' + (option('force') ? 'Used --force, continuing.'.underline : 'Use --force to continue.'); + msg += ' ' + (grunt.option('force') ? 'Used --force, continuing.'.underline : 'Use --force to continue.'); } - log.writeln([tags[mode][0], msg.yellow, tags[mode][1]].join(' ')); + grunt.log.writeln([tags[mode][0], msg.yellow, tags[mode][1]].join(' ')); } // A fatal error occured. Abort immediately. -exports.fatal = function(e, errcode) { +fail.fatal = function(e, errcode) { writeln(e, 'fatal'); process.reallyExit(typeof errcode === 'number' ? errcode : 1); }; // Keep track of error and warning counts. -exports.errorcount = 0; -exports.warncount = 0; +fail.errorcount = 0; +fail.warncount = 0; // Something (like the watch task) can override this to perform an alternate // action to exiting on warn. -exports.warnAlternate = null; +fail.warnAlternate = null; // A warning ocurred. Abort immediately unless -f or --force was used. -exports.warn = function(e, errcode) { +fail.warn = function(e, errcode) { var message = typeof e === 'string' ? e : e.message; - exports.warncount++; + fail.warncount++; writeln(message, 'warn'); // If -f or --force aren't used, stop script processing. - if (!option('force')) { - if (exports.warnAlternate) { - exports.warnAlternate(); + if (!grunt.option('force')) { + if (fail.warnAlternate) { + fail.warnAlternate(); } else { // If --debug is enabled, log the appropriate error stack (if it exists). - if (option('debug') >= 9) { + if (grunt.option('debug') >= 9) { if (e.origError && e.origError.stack) { console.log(e.origError.stack); } else if (e.stack) { @@ -65,17 +68,17 @@ exports.warn = function(e, errcode) { } } // Log and exit. - log.writeln().fail('Aborted due to warnings.'); + grunt.log.writeln().fail('Aborted due to warnings.'); process.reallyExit(typeof errcode === 'number' ? errcode : 2); } } }; // This gets called at the very end. -exports.report = function() { - if (exports.warncount > 0) { - log.writeln().fail('Done, but with warnings.'); +fail.report = function() { + if (fail.warncount > 0) { + grunt.log.writeln().fail('Done, but with warnings.'); } else { - log.writeln().success('Done, without errors.'); + grunt.log.writeln().success('Done, without errors.'); } }; diff --git a/lib/grunt/file.js b/lib/grunt/file.js index 9c68eb069..e1234bd51 100644 --- a/lib/grunt/file.js +++ b/lib/grunt/file.js @@ -7,46 +7,49 @@ * http://benalman.com/about/license/ */ +var grunt = require('../grunt'); +var file = module.exports = {}; + var fs = require('fs'); var path = require('path'); var glob = require('glob-whatev'); // Return an array of all file paths that match the given wildcard patterns. -exports.expand = function() { +file.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] : utils.toArray(arguments); + var patterns = arguments[0] instanceof Array ? arguments[0] : grunt.utils.toArray(arguments); // Generate a should-be-unique number. var uid = +new Date(); // Return a flattened, uniqued array of matching file paths. - return utils._(patterns).chain().flatten().map(function(pattern) { + return grunt.utils._(patterns).chain().flatten().map(function(pattern) { // If pattern is a template, process it accordingly. - pattern = template.process(pattern); + pattern = grunt.template.process(pattern); // Just return the pattern if it's an internal directive. - if (task.getDirectiveParts(pattern)) { return pattern; } + if (grunt.task.getDirectiveParts(pattern)) { return pattern; } // Otherwise, expand paths. return glob.glob(pattern); }).flatten().uniq(false, function(filepath) { // Only unique file paths, but don't unique directives, in case // they are repeated intentionally. - return task.getDirectiveParts(filepath) ? ++uid : filepath; + return grunt.task.getDirectiveParts(filepath) ? ++uid : filepath; }).filter(function(filepath) { // Just return the filepath if it's an internal directive. - if (task.getDirectiveParts(filepath)) { return filepath; } + if (grunt.task.getDirectiveParts(filepath)) { return filepath; } try { return fs.statSync(filepath).mode !== 16877; } catch(e) { - throw task.taskError(e.message, e); + throw grunt.task.taskError(e.message, e); } }).value(); }; // Return an array of all file paths that match the given wildcard patterns, // plus any URLs that were passed at the end. -exports.expandToUrls = function() { +file.expandToUrls = 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] : utils.toArray(arguments); + var patterns = arguments[0] instanceof Array ? arguments[0] : grunt.utils.toArray(arguments); var urls = []; // Filter all URLs out of patterns list and store them in a separate array. patterns = patterns.filter(function(pattern) { @@ -60,7 +63,7 @@ exports.expandToUrls = function() { return true; }); // Return expanded filepaths with urls at end. - return exports.expand(patterns).map(function(filepath) { + return file.expand(patterns).map(function(filepath) { var abspath = path.resolve(filepath); // Convert C:\foo\bar style paths to /C:/foo/bar. if (abspath.indexOf('/') !== 0) { @@ -71,7 +74,7 @@ exports.expandToUrls = function() { }; // Like mkdir -p. Create a directory and any intermediary directories. -exports.mkdir = function(dirpath) { +file.mkdir = function(dirpath) { var parts = []; dirpath.split('/').forEach(function(part) { parts.push(part); @@ -83,12 +86,12 @@ exports.mkdir = function(dirpath) { }; // Recurse into a directory, executing callback for each file. -exports.recurse = function(rootdir, callback, subdir) { +file.recurse = function recurse(rootdir, callback, subdir) { var abspath = subdir ? path.join(rootdir, subdir) : rootdir; fs.readdirSync(abspath).forEach(function(filename) { var filepath = path.join(abspath, filename); if (fs.statSync(filepath).mode === 16877) { - exports.recurse(rootdir, callback, path.join(subdir, filename)); + recurse(rootdir, callback, path.join(subdir, filename)); } else { callback(path.join(rootdir, subdir, filename), rootdir, subdir, filename); } @@ -98,104 +101,104 @@ exports.recurse = function(rootdir, callback, subdir) { var root = path.resolve('/'); // Is a given file path absolute? -exports.isPathAbsolute = function() { +file.isPathAbsolute = function() { var filepath = path.join.apply(path, arguments); return filepath.indexOf(root) === 0; }; // Search for a filename in the given directory or all parent directories. -exports.findup = function(rootdir, filename) { +file.findup = function findup(rootdir, filename) { var filepath = path.join(rootdir, filename); if (path.existsSync(filepath)) { return filepath; } else if (rootdir === root) { return false; } else { - return exports.findup(path.resolve(rootdir, '..'), filename); + return findup(path.resolve(rootdir, '..'), filename); } }; // Write a file. -exports.write = function(filepath, contents) { - var nowrite = option('no-write'); - verbose.write((nowrite ? 'Not actually writing ' : 'Writing ') + filepath + '...'); +file.write = function(filepath, contents) { + var nowrite = grunt.option('no-write'); + grunt.verbose.write((nowrite ? 'Not actually writing ' : 'Writing ') + filepath + '...'); try { if (!nowrite) { // Create path, if necessary. - exports.mkdir(path.dirname(filepath)); + file.mkdir(path.dirname(filepath)); // Actually write file. fs.writeFileSync(filepath, contents); } - verbose.ok(); + grunt.verbose.ok(); return true; } catch(e) { - verbose.error(); - throw task.taskError('Unable to write "' + filepath + '" file (Error code: ' + e.code + ').', e); + grunt.verbose.error(); + throw grunt.task.taskError('Unable to write "' + filepath + '" file (Error code: ' + e.code + ').', e); } }; // Read a file, return its contents. -exports.read = function(filepath, encoding) { +file.read = function(filepath, encoding) { var src; - verbose.write('Reading ' + filepath + '...'); + grunt.verbose.write('Reading ' + filepath + '...'); try { src = fs.readFileSync(String(filepath), encoding ? null : 'utf8'); - verbose.ok(); + grunt.verbose.ok(); return src; } catch(e) { - verbose.error(); - throw task.taskError('Unable to read "' + filepath + '" file (Error code: ' + e.code + ').', e); + grunt.verbose.error(); + throw grunt.task.taskError('Unable to read "' + filepath + '" file (Error code: ' + e.code + ').', e); } }; // Read a file, process its content (optionally), then write the output. -exports.copy = function(srcpath, destpath, callback) { - var src = exports.read(srcpath, true); +file.copy = function(srcpath, destpath, callback) { + var src = file.read(srcpath, true); if (callback) { - verbose.write('Processing source...'); + grunt.verbose.write('Processing source...'); try { src = callback(src.toString('utf8')); - verbose.ok(); + grunt.verbose.ok(); } catch(e) { - verbose.error(); - throw task.taskError('Error while processing "' + srcpath + '" file.', e); + grunt.verbose.error(); + throw grunt.task.taskError('Error while processing "' + srcpath + '" file.', e); } } - exports.write(destpath, src); + file.write(destpath, src); }; // Read a file, parse its contents, return an object. -exports.readJson = function(filepath) { +file.readJson = function(filepath) { var src = this.read(String(filepath)); var result; - verbose.write('Parsing ' + filepath + '...'); + grunt.verbose.write('Parsing ' + filepath + '...'); try { result = JSON.parse(src); - verbose.ok(); + grunt.verbose.ok(); return result; } catch(e) { - verbose.error(); - throw task.taskError('Unable to parse "' + filepath + '" file (' + e.message + ').', e); + grunt.verbose.error(); + throw grunt.task.taskError('Unable to parse "' + filepath + '" file (' + e.message + ').', e); } }; // Clear the require cache for all passed filepaths. -exports.clearRequireCache = function() { +file.clearRequireCache = function() { // If a non-string argument is passed, it's an array of filepaths, otherwise // each filepath is passed individually. - var filepaths = typeof arguments[0] !== 'string' ? arguments[0] : utils.toArray(arguments); + var filepaths = typeof arguments[0] !== 'string' ? arguments[0] : grunt.utils.toArray(arguments); // For each filepath, clear the require cache, if necessary. filepaths.forEach(function(filepath) { var abspath = path.resolve(filepath); if (require.cache[abspath]) { - verbose.write('Clearing require cache for "' + filepath + '" file...').ok(); + grunt.verbose.write('Clearing require cache for "' + filepath + '" file...').ok(); delete require.cache[abspath]; } }); }; // Access files in the user's ".grunt" folder. -exports.userpath = function() { +file.userpath = function() { var filepath = path.join.apply(path, arguments); var win32 = process.platform === 'win32'; var homepath = process.env[win32 ? 'USERPROFILE' : 'HOME']; @@ -203,7 +206,7 @@ exports.userpath = function() { }; // Get an Npm grunt plugin tasks path or null. -exports.npmTaskpath = function(name) { +file.npmTaskpath = function(name) { var filepath; try { filepath = require.resolve(name); @@ -216,15 +219,15 @@ exports.npmTaskpath = function(name) { }; // Get a list of task paths (or task-specific extraspaths). -exports.taskpaths = function() { +file.taskpaths = function() { var args; var taskpaths = []; // Path to user's "tasks" files. - taskpaths.push(exports.userpath('tasks')); + taskpaths.push(file.userpath('tasks')); // Paths to user-specified --tasks dirs. - var optiontasks = option('tasks') ? option('tasks').slice() : []; + var optiontasks = grunt.option('tasks') ? grunt.option('tasks').slice() : []; taskpaths = taskpaths.concat(optiontasks.map(function(relpath) { - var npmTaskpath = exports.npmTaskpath(relpath); + var npmTaskpath = file.npmTaskpath(relpath); if (npmTaskpath) { // Npm module found, use its path. return npmTaskpath; @@ -237,7 +240,7 @@ exports.taskpaths = function() { taskpaths.push(path.resolve(__dirname, '../../tasks')); // If arguments were specified, join them to pathnames. if (arguments.length > 0) { - args = utils.toArray(arguments); + args = grunt.utils.toArray(arguments); taskpaths = taskpaths.map(function(dirpath) { return path.join.apply(path, [dirpath].concat(args)); }); @@ -249,17 +252,17 @@ exports.taskpaths = function() { }; // Find all task files matching a given path. -exports.taskfiles = function() { +file.taskfiles = function() { var filepath = path.join.apply(path, arguments); var filepaths = {}; // When any returned array item is used in a string context, return the // absolute path. var toString = function() { return this.abs; }; // Iterate over all taskpaths in reverse. - exports.taskpaths().reverse().map(function(dirpath) { + file.taskpaths().reverse().map(function(dirpath) { var abspath = path.join(dirpath, filepath); // Expand the path in case a wildcard was passed. - exports.expand(abspath).forEach(function(abspath) { + file.expand(abspath).forEach(function(abspath) { var relpath = abspath.slice(dirpath.length + 1); // This object overrides any previously set object for this relpath. filepaths[relpath] = { @@ -277,28 +280,28 @@ exports.taskfiles = function() { }; // Find the first task file matching a given path. -exports.taskfile = function() { - return exports.taskfiles.apply(exports, arguments)[0]; +file.taskfile = function() { + return file.taskfiles.apply(file, arguments)[0]; }; // Read JSON defaults from task files (if they exist), merging them into one. // data object. var taskfileDefaults = {}; -exports.taskfileDefaults = function() { +file.taskfileDefaults = function() { var filepath = path.join.apply(path, arguments); var result = taskfileDefaults[filepath]; var filepaths; if (!result) { result = taskfileDefaults[filepath] = {}; // Find all matching taskfiles. - filepaths = exports.taskfiles(filepath); + filepaths = file.taskfiles(filepath); // Load defaults data. if (filepaths.length) { - verbose.subhead('Loading data from ' + filepath); + grunt.verbose.subhead('Loading data from ' + filepath); // Since extras path order goes from most-specific to least-specific, only // add-in properties that don't already exist. filepaths.forEach(function(filepath) { - utils._.defaults(result, exports.readJson(filepath)); + grunt.utils._.defaults(result, file.readJson(filepath)); }); } } diff --git a/lib/grunt/help.js b/lib/grunt/help.js index 9a9979e8c..b87a3ea88 100644 --- a/lib/grunt/help.js +++ b/lib/grunt/help.js @@ -7,10 +7,12 @@ * http://benalman.com/about/license/ */ +var grunt = require('../grunt'); + var path = require('path'); -var optlist = grunt.require('cli').optlist; +var optlist = require('./cli').optlist; -task.init([], true); +grunt.task.init([], true); // Build 2-column array for table view. var col1len = 0; @@ -21,30 +23,30 @@ var opts = Object.keys(optlist).map(function(long) { return [col1, o.info]; }); -var tasks = Object.keys(task._tasks).map(function(name) { +var tasks = Object.keys(grunt.task._tasks).map(function(name) { col1len = Math.max(col1len, name.length); - var info = task._tasks[name].info; - if (task._tasks[name].multi) { + var info = grunt.task._tasks[name].info; + if (grunt.task._tasks[name].multi) { info += ' *'; } return [name, info]; }); // Actually write out help screen. -log.writeln('grunt: a command line build tool for JavaScript projects. (v' + grunt.version + ')'); +grunt.log.writeln('grunt: a command line build tool for JavaScript projects. (v' + grunt.version + ')'); -log.header('Usage'); -log.writeln(' ' + path.basename(process.argv[1]) + ' [options] [task [task ...]]'); +grunt.log.header('Usage'); +grunt.log.writeln(' ' + path.basename(process.argv[1]) + ' [options] [task [task ...]]'); // Widths for options/tasks table output. var widths = [1, col1len, 2, 77 - col1len]; -log.header('Options'); -opts.forEach(function(a) { log.writetableln(widths, ['', utils._.pad(a[0], col1len), '', a[1]]); }); -log.header('Available tasks'); -tasks.forEach(function(a) { log.writetableln(widths, ['', utils._.pad(a[0], col1len), '', a[1]]); }); +grunt.log.header('Options'); +opts.forEach(function(a) { grunt.log.writetableln(widths, ['', grunt.utils._.pad(a[0], col1len), '', a[1]]); }); +grunt.log.header('Available tasks'); +tasks.forEach(function(a) { grunt.log.writetableln(widths, ['', grunt.utils._.pad(a[0], col1len), '', a[1]]); }); -log.writeln().writelns( +grunt.log.writeln().writelns( 'Tasks run in the order specified. Arguments may be passed to tasks that ' + 'accept them by using semicolons, like "lint:files". Tasks marked with * ' + 'are "multi tasks" and will iterate over all sub-targets if no argument is ' + diff --git a/lib/grunt/log.js b/lib/grunt/log.js index fa391f422..5459f53f4 100644 --- a/lib/grunt/log.js +++ b/lib/grunt/log.js @@ -7,13 +7,16 @@ * http://benalman.com/about/license/ */ +var grunt = require('../grunt'); +var log = module.exports = {}; + var util = require('util'); // Temporarily suppress output. var suppressOutput; // Allow external muting of output. -exports.muted = false; +log.muted = false; // True once anything has actually been logged. var hasLogged; @@ -23,9 +26,9 @@ var hasLogged; process.stdout = process.stderr; // Write output. -exports.write = function(msg) { +log.write = function(msg) { // Actually write output. - if (!exports.muted && !suppressOutput) { + if (!log.muted && !suppressOutput) { hasLogged = true; process.stdout.write(msg || ''); } @@ -34,7 +37,7 @@ exports.write = function(msg) { }; // Write a line of output. -exports.writeln = function(msg) { +log.writeln = function(msg) { // Actually write output. this.write((msg || '') + '\n'); // Chainable! @@ -43,58 +46,58 @@ exports.writeln = function(msg) { // Stuff. -exports.error = function(msg) { +log.error = function(msg) { if (msg) { - fail.errorcount++; - return this.writeln('>> '.red + utils._.trim(msg).replace(/\n/g, '\n>> '.red)); + grunt.fail.errorcount++; + return this.writeln('>> '.red + grunt.utils._.trim(msg).replace(/\n/g, '\n>> '.red)); } else { return this.writeln('ERROR'.red); } }; -exports.ok = function(msg) { +log.ok = function(msg) { if (msg) { - return this.writeln('>> '.green + utils._.trim(msg).replace(/\n/g, '\n>> '.green)); + return this.writeln('>> '.green + grunt.utils._.trim(msg).replace(/\n/g, '\n>> '.green)); } else { return this.writeln('OK'.green); } }; -exports.success = function(msg) { return this.writeln(msg.green); }; -exports.fail = function(msg) { return this.writeln(msg.red); }; -exports.header = function(msg) { +log.success = function(msg) { return this.writeln(msg.green); }; +log.fail = function(msg) { return this.writeln(msg.red); }; +log.header = function(msg) { // Skip line before header, but not if header is the very first line output. if (hasLogged) { this.writeln(); } return this.writeln(msg.underline); }; -exports.subhead = function(msg) { +log.subhead = function(msg) { // Skip line before subhead, but not if subhead is the very first line output. if (hasLogged) { this.writeln(); } return this.writeln(msg.bold); }; // For debugging. -exports.debug = function() { - if (option('debug')) { - this.writeln(('[D] ' + util.format.apply(this, arguments)).magenta); +log.debug = function() { + if (grunt.option('debug')) { + this.writeln(('[D] ' + grunt.util.format.apply(this, arguments)).magenta); } return this; }; // Write a line of a table. -exports.writetableln = function(widths, texts) { +log.writetableln = function(widths, texts) { return this.writeln(this.table(widths, texts)); }; // Wrap a long line of text to 80 columns. -exports.writelns = function(msg) { +log.writelns = function(msg) { return this.writeln(this.wraptext(80, msg)); }; // Display flags in verbose mode. -exports.writeflags = function(obj, prefix) { +log.writeflags = function(obj, prefix) { var wordlist; if (obj instanceof Array) { - wordlist = exports.wordlist(obj); + wordlist = log.wordlist(obj); } else if (typeof obj === 'object' && obj) { - wordlist = exports.wordlist(Object.keys(obj).map(function(key) { + wordlist = log.wordlist(Object.keys(obj).map(function(key) { var val = obj[key]; return key + (val === true ? '' : '=' + JSON.stringify(val)); })); @@ -105,26 +108,26 @@ exports.writeflags = function(obj, prefix) { // Create explicit "verbose" and "notverbose" functions, one for each already- // defined log function, that do the same thing but ONLY if -v or --verbose is // specified (or not specified). -exports.verbose = {}; -exports.notverbose = {}; +log.verbose = {}; +log.notverbose = {}; // Iterate over all exported functions. -Object.keys(exports).filter(function(key) { - return typeof exports[key] === 'function'; +Object.keys(log).filter(function(key) { + return typeof log[key] === 'function'; }).forEach(function(key) { // Like any other log function, but suppresses output if the "verbose" option // IS NOT set. - exports.verbose[key] = function() { - suppressOutput = !option('verbose'); - exports[key].apply(this, arguments); + log.verbose[key] = function() { + suppressOutput = !grunt.option('verbose'); + log[key].apply(this, arguments); suppressOutput = false; return this; }; // Like any other log function, but suppresses output if the "verbose" option // IS set. - exports.notverbose[key] = function() { - suppressOutput = option('verbose'); - exports[key].apply(this, arguments); + log.notverbose[key] = function() { + suppressOutput = grunt.option('verbose'); + log[key].apply(this, arguments); suppressOutput = false; return this; }; @@ -133,25 +136,25 @@ Object.keys(exports).filter(function(key) { // A way to switch between verbose and notverbose modes. For example, this will // write 'foo' if verbose logging is enabled, otherwise write 'bar': // verbose.write('foo').or.write('bar'); -exports.verbose.or = exports.notverbose; -exports.notverbose.or = exports.verbose; +log.verbose.or = log.notverbose; +log.notverbose.or = log.verbose; // Static methods. // Pretty-format a word list. -exports.wordlist = function(arr, separator) { +log.wordlist = function(arr, separator) { return arr.map(function(item) { return item.cyan; }).join(separator || ', '); }; // Return a string, uncolored (suitable for testing .length, etc). -exports.uncolor = function(str) { +log.uncolor = function(str) { return str.replace(/\x1B\[\d+m/g, ''); }; // Word-wrap text to a given width, permitting ANSI color codes. -exports.wraptext = function(width, text) { +log.wraptext = function(width, text) { // notes to self: // grab 1st character or ansi code from string // if ansi code, add to array and save for later, strip from front of string @@ -231,10 +234,10 @@ exports.wraptext = function(width, text) { // logs(text); // Format output into columns, wrapping words as-necessary. -exports.table = function(widths, texts) { +log.table = function(widths, texts) { var rows = []; widths.forEach(function(width, i) { - var lines = exports.wraptext(width, texts[i]).split('\n'); + var lines = log.wraptext(width, texts[i]).split('\n'); lines.forEach(function(line, j) { var row = rows[j]; if (!row) { row = rows[j] = []; } @@ -249,8 +252,8 @@ exports.table = function(widths, texts) { for (var i = 0; i < row.length; i++) { column = row[i] || ''; txt += column; - var diff = widths[i] - exports.uncolor(column).length; - if (diff > 0) { txt += utils.repeat(diff, ' '); } + var diff = widths[i] - log.uncolor(column).length; + if (diff > 0) { txt += grunt.utils.repeat(diff, ' '); } } lines.push(txt); }); diff --git a/lib/grunt/option.js b/lib/grunt/option.js index 50bb5ee89..d17d06ad9 100644 --- a/lib/grunt/option.js +++ b/lib/grunt/option.js @@ -11,7 +11,7 @@ var data = {}; // Get or set an option value. -exports = module.exports = function(key, value) { +var option = module.exports = function(key, value) { var no = key.match(/^no-(.+)$/); if (arguments.length === 2) { return (data[key] = value); @@ -23,12 +23,12 @@ exports = module.exports = function(key, value) { }; // Initialize option data. -exports.init = function(obj) { +option.init = function(obj) { return (data = obj || {}); }; // List of options as flags. -exports.flags = function() { +option.flags = function() { return Object.keys(data).map(function(key) { var val = data[key]; return '--' + (val === false ? 'no-' : '') + key + (typeof val === 'boolean' ? '' : '=' + val); diff --git a/lib/grunt/task.js b/lib/grunt/task.js index af9fd764f..b79ee6c40 100644 --- a/lib/grunt/task.js +++ b/lib/grunt/task.js @@ -7,66 +7,68 @@ * http://benalman.com/about/license/ */ +var grunt = require('../grunt'); + +// Extend generic "task" utils lib. +var parent = grunt.utils.task.create(); +var task = module.exports = Object.create(parent); + var path = require('path'); var fs = require('fs'); // An internal registry of tasks and handlers. var registry = {}; -// Extend generic "task" utils lib. -var parent = utils.task.create(); -exports = module.exports = Object.create(parent); - // If any log.error() calls occurred, then the task had errors. var errorcount; -exports.hadErrors = function() { - return fail.errorcount > errorcount; +task.hadErrors = function() { + return grunt.fail.errorcount > errorcount; }; // Override built-in registerTask. -exports.registerTask = function(name, info, fn) { +task.registerTask = function(name, info, fn) { // Add task to registry. registry.tasks.push(name); // Register task. - parent.registerTask.apply(this, arguments); + parent.registerTask.apply(task, arguments); // This task, now that it's been registered. - var task = this._tasks[name]; + var thisTask = task._tasks[name]; // Override task function. - var _fn = task.fn; - task.fn = function(arg) { + var _fn = thisTask.fn; + thisTask.fn = function(arg) { // Make a list of task-related extras paths available. - this.extraspaths = file.taskpaths.bind(file, this.name); + this.extraspaths = grunt.file.taskpaths.bind(grunt.file, this.name); // Initialize the errorcount for this task. - errorcount = fail.errorcount; + errorcount = grunt.fail.errorcount; // If this task was an alias or a multi task called without a target, // only log if in verbose mode. - var logger = _fn.alias || (task.multi && (!arg || arg === '*')) ? verbose : log; + var logger = _fn.alias || (thisTask.multi && (!arg || arg === '*')) ? 'verbose' : 'log'; // Actually log. - logger.header('Running "' + this.nameArgs + '"' + + grunt[logger].header('Running "' + this.nameArgs + '"' + (this.name !== this.nameArgs ? ' (' + this.name + ')' : '') + ' task'); // Actually run the task. return _fn.apply(this, arguments); }; - return this; + return task; }; // This is the most common "multi task" pattern. -exports.registerMultiTask = function(name, info, fn) { - exports.registerTask(name, info, function(target) { +task.registerMultiTask = function(name, info, fn) { + task.registerTask(name, info, function(target) { // If a target wasn't specified, run this task once for each target. if (!target || target === '*') { - return exports.runAllTargets(name, utils.toArray(arguments).slice(1)); + return task.runAllTargets(name, grunt.utils.toArray(arguments).slice(1)); } // Fail if any required config properties have been omitted. - config.requires([name, target]); + grunt.config.requires([name, target]); // Expose data on `this` (as well as task.current). - this.data = config([name, target]); + this.data = grunt.config([name, target]); // Expose file object on `this` (as well as task.current). this.file = {}; // Handle data structured like either: // 'prop': [srcfiles] // {prop: {src: [srcfiles], dest: 'destfile'}}. - if (utils.kindOf(this.data) === 'object') { + if (grunt.utils.kindOf(this.data) === 'object') { if ('src' in this.data) { this.file.src = this.data.src; } if ('dest' in this.data) { this.file.dest = this.data.dest; } } else { @@ -75,44 +77,46 @@ exports.registerMultiTask = function(name, info, fn) { } // Process src as a template (recursively, if necessary). if (this.file.src) { - this.file.src = utils.recurse(this.file.src, function(src) { - return template.process(src); + this.file.src = grunt.utils.recurse(this.file.src, function(src) { + return grunt.template.process(src); }); } // Process dest as a template. - if (this.file.dest) { this.file.dest = template.process(this.file.dest); } + if (this.file.dest) { + this.file.dest = grunt.template.process(this.file.dest); + } // Recreate flags object so that the target isn't set as a flag. - var args = utils.toArray(arguments).slice(1); + var args = grunt.utils.toArray(arguments).slice(1); this.flags = {}; args.forEach(function(arg) { this.flags[arg] = true; }, this); // Call original task function, passing in the target and any other args. return fn.apply(this, arguments); }); - this._tasks[name].multi = true; + task._tasks[name].multi = true; }; // Init tasks don't require properties in config, and as such will preempt // config loading errors. -exports.registerInitTask = function(name, info, fn) { - exports.registerTask(name, info, fn); - this._tasks[name].init = true; +task.registerInitTask = function(name, info, fn) { + task.registerTask(name, info, fn); + task._tasks[name].init = true; }; // Override built-in registerHelper to use the registry. -exports.registerHelper = function(name, fn) { +task.registerHelper = function(name, fn) { // Add task to registry. registry.helpers.push(name); // Actually register task. - return parent.registerHelper.apply(this, arguments); + return parent.registerHelper.apply(task, arguments); }; // If a property wasn't passed, run all task targets in turn. -exports.runAllTargets = function(taskname, args) { +task.runAllTargets = function(taskname, args) { // Get an array of sub-property keys under the given config object. - var targets = Object.keys(config(taskname) || {}); + var targets = Object.keys(grunt.config(taskname) || {}); // Fail if there are no actual properties to iterate over. if (targets.length === 0) { - log.error('No "' + taskname + '" targets found.'); + grunt.log.error('No "' + taskname + '" targets found.'); return false; } // Iterate over all properties not starting with _, running a task for each. @@ -120,7 +124,7 @@ exports.runAllTargets = function(taskname, args) { return !/^_/.test(target); }).forEach(function(target) { // Be sure to pass in any additionally specified args. - exports.run([taskname, target].concat(args || []).join(':')); + task.run([taskname, target].concat(args || []).join(':')); }); }; @@ -134,23 +138,29 @@ function loadTask(filepath, info) { registry.helpers = []; var file = path.basename(filepath); var msg = 'Loading ' + info + ' "' + file + '" tasks and helpers...'; + var fn; try { // Load taskfile. - require(path.resolve(filepath)); - verbose.write(msg).ok(); + fn = require(path.resolve(filepath)); + if (typeof fn === 'function') { + fn.call(grunt, grunt); + } else { + // Create globals? + } + grunt.verbose.write(msg).ok(); if (registry.tasks.length === 0 && registry.helpers.length === 0) { - verbose.error('No new tasks or helpers found.'); + grunt.verbose.error('No new tasks or helpers found.'); } else { if (registry.tasks.length > 0) { - verbose.writeln('Tasks: ' + log.wordlist(registry.tasks)); + grunt.verbose.writeln('Tasks: ' + grunt.log.wordlist(registry.tasks)); } if (registry.helpers.length > 0) { - verbose.writeln('Helpers: ' + log.wordlist(registry.helpers)); + grunt.verbose.writeln('Helpers: ' + grunt.log.wordlist(registry.helpers)); } } } catch(e) { // Something went wrong. - log.write(msg).error().error(e.message); + grunt.log.write(msg).error().error(e.message); } // Restore registry. var obj = loadTaskStack.pop() || {}; @@ -169,73 +179,73 @@ function loadTasks(tasksdir, info) { loadTask(path.join(tasksdir, filename), info); }); } catch(e) { - log.error(e.message); + grunt.log.error(e.message); } } // Load tasks and handlers from a given directory. -exports.loadTasks = function(tasksdir) { +task.loadTasks = function(tasksdir) { if (path.existsSync(tasksdir)) { loadTasks(tasksdir, '"' + tasksdir + '" directory'); } else { - log.error('Tasks directory "' + tasksdir + '" not found.'); + grunt.log.error('Tasks directory "' + tasksdir + '" not found.'); } }; // Load tasks and handlers from a given Npm module. Note that if grunt is // installed globally, this will load global Npm modules. If grunt is // installed locally, this will load local Npm modules. -exports.loadNpmTasks = function(name) { - var taskspath = file.npmTaskpath(name); +task.loadNpmTasks = function(name) { + var taskspath = grunt.file.npmTaskpath(name); if (taskspath) { loadTasks(taskspath, '"' + name + '" npm module'); } else { - log.error('Npm module "' + name + '" not found. Is it installed?'); + grunt.log.error('Npm module "' + name + '" not found. Is it installed?'); } }; // Initialize tasks. -exports.init = function(tasks, nocomplain) { - var taskpaths = file.taskpaths().reverse(); +task.init = function(tasks, nocomplain) { + var taskpaths = grunt.file.taskpaths().reverse(); var userTasks = taskpaths.slice(1); var builtinTasks = taskpaths[0]; - + // Load all built-in tasks. loadTasks(builtinTasks, 'built-in'); // Don't complain if all specified tasks are "init" tasks. nocomplain = nocomplain || tasks.every(function(name) { - var obj = exports._taskPlusArgs(name).task; + var obj = task._taskPlusArgs(name).task; return obj && obj.init; }); // Get any local config data or tasks that might exist. // Use --config override if specified. - var configfile = option('config'); + var configfile = grunt.option('config'); // If not specified, search the current directory or any parent directory. if (!configfile) { - configfile = file.findup(process.cwd(), 'grunt.js'); + configfile = grunt.file.findup(process.cwd(), 'grunt.js'); } var basename = path.basename(configfile); var msg = 'Reading "' + basename + '" config file...'; if (path.existsSync(configfile)) { - verbose.write(msg).ok(); + grunt.verbose.write(msg).ok(); // Load local tasks, if the file exists. loadTask(configfile, 'config file'); // Change working directory so that all paths are relative to the // configfile's location (or the --base option, if specified). - process.chdir(option('base') || path.dirname(configfile)); + process.chdir(grunt.option('base') || path.dirname(configfile)); } else if (nocomplain) { // Don't complain about missing config file. - } else if (option('config')) { + } else if (grunt.option('config')) { // If --config override was specified and it doesn't exist, complain. - log.write(msg).error(); - fail.fatal('Unable to find "' + configfile + '" config file.', 2); - } else if (!option('help')) { - verbose.write(msg).error(); - fail.fatal('Unable to find "grunt.js" config file. Do you need any --help?', 2); + grunt.log.write(msg).error(); + grunt.fail.fatal('Unable to find "' + configfile + '" config file.', 2); + } else if (!grunt.option('help')) { + grunt.verbose.write(msg).error(); + grunt.fail.fatal('Unable to find "grunt.js" config file. Do you need any --help?', 2); } // Load all user-specified tasks. diff --git a/lib/grunt/template.js b/lib/grunt/template.js index 8ea861a59..328a89f60 100644 --- a/lib/grunt/template.js +++ b/lib/grunt/template.js @@ -7,20 +7,23 @@ * http://benalman.com/about/license/ */ +var grunt = require('../grunt'); +var template = module.exports = {}; + var dateformat = require('dateformat'); // Miscellanous template helpers -exports.today = function(format) { +template.today = function(format) { return dateformat(new Date(), format); }; -exports.stripBanner = function(src) { +template.stripBanner = function(src) { return src.replace(/^\s*\/\*[^!][\s\S]*?\*\/\s*/, ''); }; // Set underscore template delimiters. -exports.delimiters = function(mode) { +template.delimiters = function(mode) { var modes = { // The underscore default template syntax should be a pretty sane default. default: { @@ -43,7 +46,7 @@ exports.delimiters = function(mode) { } }; var settings = modes[mode in modes ? mode : 'default']; - utils._.templateSettings = settings; + grunt.utils._.templateSettings = settings; // Get opener character for grunt to use. var opener = settings.opener; // Remove it from the underscore object and return it. @@ -52,16 +55,22 @@ exports.delimiters = function(mode) { }; // Process template + data with underscore. -exports.process = function(template, data, mode) { +template.process = function(template, data, mode) { // Set delimiters, and get a opening match character. - var opener = exports.delimiters(mode); + var opener = grunt.template.delimiters(mode); // Initialize data to config data if omitted. - if (!data) { data = config(); } + if (!data) { data = grunt.config() || {}; } + // Add a reference to grunt onto the data object. + data.grunt = grunt; // As long as template contains template tags, render it and get the result, // otherwise just use the template string. - while (template.indexOf(opener) >= 0) { - template = utils._.template(template)(data); + try { + while (template.indexOf(opener) >= 0) { + template = grunt.utils._.template(template)(data); + } + } catch (e) { + grunt.fail.warn('An error occurred while processing a template (' + e.message + ').'); } // Normalize linefeeds and return. - return utils.normalizelf(template); + return grunt.utils.normalizelf(template); }; diff --git a/lib/grunt/utils.js b/lib/grunt/utils.js index 2dacb071f..ab0abb8d8 100644 --- a/lib/grunt/utils.js +++ b/lib/grunt/utils.js @@ -7,25 +7,28 @@ * http://benalman.com/about/license/ */ +var grunt = require('../grunt'); +var utils = module.exports = {}; + var spawn = require('child_process').spawn; // A few internal utilites, exposed. -exports.task = require('../util/task'); -exports.namespace = require('../util/namespace'); +utils.task = require('../util/task'); +utils.namespace = require('../util/namespace'); // A few external utilites, exposed. -exports.hooker = require('hooker'); -exports.async = require('async'); -exports._ = require('underscore'); -exports._.str = require('underscore.string'); -exports._.mixin(exports._.str.exports()); +utils.hooker = require('hooker'); +utils.async = require('async'); +utils._ = require('underscore'); +utils._.str = require('underscore.string'); +utils._.mixin(utils._.str.exports()); // The line feed char for the current system. -exports.linefeed = process.platform === 'win32' ? '\r\n' : '\n'; +utils.linefeed = process.platform === 'win32' ? '\r\n' : '\n'; // Normalize linefeeds in a string. -exports.normalizelf = function(str) { - return str.replace(/\r\n|\n/g, exports.linefeed); +utils.normalizelf = function(str) { + return str.replace(/\r\n|\n/g, utils.linefeed); }; // What "kind" is a value? @@ -34,7 +37,7 @@ var kindsOf = {}; 'Number String Boolean Function RegExp Array Date Error'.split(' ').forEach(function(k) { kindsOf['[object ' + k + ']'] = k.toLowerCase(); }); -exports.kindOf = function(value) { +utils.kindOf = function(value) { if (value === null) { return 'null'; } else if (value == null) { @@ -44,26 +47,29 @@ exports.kindOf = function(value) { }; // Coerce something to an Array. -exports.toArray = Function.call.bind(Array.prototype.slice); +utils.toArray = Function.call.bind(Array.prototype.slice); // Return the string `str` repeated `n` times. -exports.repeat = function(n, str) { +utils.repeat = function(n, str) { return new Array(n + 1).join(str || ' '); }; // Recurse through objects and arrays, executing fn for each non-object. -exports.recurse = function recurse(value, fn) { +utils.recurse = function recurse(value, fn, fnContinue) { var obj; - if (utils.kindOf(value) === 'array') { + if (fnContinue && fnContinue(value) === false) { + // Skip value if necessary. + return value; + } else if (utils.kindOf(value) === 'array') { // If value is an array, recurse. return value.map(function(value) { - return recurse(value, fn); + return recurse(value, fn, fnContinue); }); } else if (utils.kindOf(value) === 'object') { // If value is an object, recurse. obj = {}; Object.keys(value).forEach(function(key) { - obj[key] = recurse(value[key], fn); + obj[key] = recurse(value[key], fn, fnContinue); }); return obj; } else { @@ -73,7 +79,7 @@ exports.recurse = function recurse(value, fn) { }; // Spawn a child process, capturing its stdout and stderr. -exports.spawn = function(opts, done) { +utils.spawn = function(opts, done) { var child = spawn(opts.cmd, opts.args, opts.opts); var stdout = ''; var stderr = ''; diff --git a/tasks/concat.js b/tasks/concat.js index c43d53670..3703fef70 100644 --- a/tasks/concat.js +++ b/tasks/concat.js @@ -7,29 +7,43 @@ * http://benalman.com/about/license/ */ -// ============================================================================ -// TASKS -// ============================================================================ - -task.registerMultiTask('concat', 'Concatenate files.', function(target) { - // Concat specified files. - var files = file.expand(this.file.src); - file.write(this.file.dest, task.helper('concat', files)); - - // Fail task if errors were logged. - if (task.hadErrors()) { return false; } - - // Otherwise, print a success message. - log.writeln('File "' + this.file.dest + '" created.'); -}); - -// ============================================================================ -// HELPERS -// ============================================================================ - -// Concat source files and/or directives. -task.registerHelper('concat', function(files) { - return files ? files.map(function(filepath) { - return task.directive(filepath, file.read); - }).join(utils.linefeed) : ''; -}); +module.exports = function(grunt) { + // Grunt utilities. + var task = grunt.task; + var file = grunt.file; + var utils = grunt.utils; + var log = grunt.log; + var verbose = grunt.verbose; + var fail = grunt.fail; + var option = grunt.option; + var config = grunt.config; + var template = grunt.template; + + // ========================================================================== + // TASKS + // ========================================================================== + + grunt.registerMultiTask('concat', 'Concatenate files.', function(target) { + // Concat specified files. + var files = file.expand(this.file.src); + file.write(this.file.dest, grunt.helper('concat', files)); + + // Fail task if errors were logged. + if (task.hadErrors()) { return false; } + + // Otherwise, print a success message. + log.writeln('File "' + this.file.dest + '" created.'); + }); + + // ========================================================================== + // HELPERS + // ========================================================================== + + // Concat source files and/or directives. + grunt.registerHelper('concat', function(files) { + return files ? files.map(function(filepath) { + return task.directive(filepath, file.read); + }).join(utils.linefeed) : ''; + }); + +}; diff --git a/tasks/init.js b/tasks/init.js index 837ecd35b..a9965da5e 100644 --- a/tasks/init.js +++ b/tasks/init.js @@ -7,535 +7,551 @@ * http://benalman.com/about/license/ */ -var fs = require('fs'); -var path = require('path'); +module.exports = function(grunt) { + // Grunt utilities. + var task = grunt.task; + var file = grunt.file; + var utils = grunt.utils; + var log = grunt.log; + var verbose = grunt.verbose; + var fail = grunt.fail; + var option = grunt.option; + var config = grunt.config; + var template = grunt.template; -var semver = require('semver'); + // Nodejs libs. + var fs = require('fs'); + var path = require('path'); -var prmpt = require('prompt'); -prmpt.message = '[' + '?'.green + ']'; -prmpt.delimiter = ' '; + // External libs. + var semver = require('semver'); -// ============================================================================ -// TASKS -// ============================================================================ + var prmpt = require('prompt'); + prmpt.message = '[' + '?'.green + ']'; + prmpt.delimiter = ' '; -// An array of all available license files. -function availableLicenses() { - return file.taskpaths('init/licenses').reduce(function(arr, filepath) { - return arr.concat(fs.readdirSync(filepath).map(function(filename) { - return filename.replace(/^LICENSE-/, ''); - })); - }, []); -} + // ========================================================================== + // TASKS + // ========================================================================== -task.registerInitTask('init', 'Generate project scaffolding from a predefined template.', function() { - // Extra arguments will be applied to the template file. - var args = utils.toArray(arguments); - // Template name. - var name = args.shift(); - // Valid init templates (.js files). - var templates = {}; - // Template-related search paths. - var searchpaths = []; - // Iterate over all available init-specific extras paths, building templates - // object and searchpaths index. - this.extraspaths().reverse().forEach(function(dirpath) { - var obj = {path: dirpath, subdirs: []}; - searchpaths.unshift(obj); - // Iterate over all files inside init-specific extras paths. - fs.readdirSync(dirpath).forEach(function(filename) { - var filepath = path.join(dirpath, filename); - if (fs.statSync(filepath).isDirectory()) { - // Push init subdirs into searchpaths subdirs array for later use. - obj.subdirs.push(filename); - } else if (fs.statSync(filepath).isFile() && path.extname(filepath) === '.js') { - // Add template (plus its path) to the templates object. - templates[path.basename(filename, '.js')] = path.join(dirpath, filename); - } + // An array of all available license files. + function availableLicenses() { + return file.taskpaths('init/licenses').reduce(function(arr, filepath) { + return arr.concat(fs.readdirSync(filepath).map(function(filename) { + return filename.replace(/^LICENSE-/, ''); + })); + }, []); + } + + grunt.registerInitTask('init', 'Generate project scaffolding from a predefined template.', function() { + // Extra arguments will be applied to the template file. + var args = utils.toArray(arguments); + // Template name. + var name = args.shift(); + // Valid init templates (.js files). + var templates = {}; + // Template-related search paths. + var searchpaths = []; + // Iterate over all available init-specific extras paths, building templates + // object and searchpaths index. + this.extraspaths().reverse().forEach(function(dirpath) { + var obj = {path: dirpath, subdirs: []}; + searchpaths.unshift(obj); + // Iterate over all files inside init-specific extras paths. + fs.readdirSync(dirpath).forEach(function(filename) { + var filepath = path.join(dirpath, filename); + if (fs.statSync(filepath).isDirectory()) { + // Push init subdirs into searchpaths subdirs array for later use. + obj.subdirs.push(filename); + } else if (fs.statSync(filepath).isFile() && path.extname(filepath) === '.js') { + // Add template (plus its path) to the templates object. + templates[path.basename(filename, '.js')] = path.join(dirpath, filename); + } + }); }); - }); - // Abort if a valid template was not specified. - if (!(name && name in templates)) { - log.error('A valid template name must be specified, eg. "grunt init:commonjs".' + - ' Valid templates are: ' + log.wordlist(Object.keys(templates)) + '.'); - return false; - } + // Abort if a valid template was not specified. + if (!(name && name in templates)) { + log.error('A valid template name must be specified, eg. "grunt init:commonjs".' + + ' Valid templates are: ' + log.wordlist(Object.keys(templates)) + '.'); + return false; + } - // Abort if a gruntfile was found (to avoid accidentally nuking it). - if (path.existsSync(path.join(process.cwd(), 'grunt.js'))) { - fail.warn('Beware, grunt.js file already exists.'); - } + // Abort if a gruntfile was found (to avoid accidentally nuking it). + if (path.existsSync(path.join(process.cwd(), 'grunt.js'))) { + fail.warn('Beware, grunt.js file already exists.'); + } - // This task is asynchronous. - var taskDone = this.async(); + // This task is asynchronous. + var taskDone = this.async(); - // Useful init sub-task-specific utilities. - var init = { - // Expose any user-specified default init values. - defaults: file.taskfileDefaults('init/defaults.json'), - // Expose rename rules for this template. - renames: file.taskfileDefaults('init', name, 'rename.json'), - // Return an object containing files to copy with their absolute source path - // and relative destination path, renamed (or omitted) according to rules in - // rename.json (if it exists). - filesToCopy: function(props) { - var files = {}; - var prefix = 'init/' + name + '/root/'; - // Iterate over all source files. - file.taskfiles('init', name, 'root', '**').forEach(function(obj) { - // Get the path relative to the template root. - var relpath = obj.rel.slice(prefix.length); - var rule = init.renames[relpath]; - // Omit files that have an empty / false rule value. - if (!rule && relpath in init.renames) { return; } - // Create an object for this file. - files[relpath] = { - src: obj.abs, - // Rename if a rule exists. - dest: rule ? template.process(rule, props, 'init') : relpath - }; - }); - return files; - }, - // Search init template paths for filename. - srcpath: function() { - var args = ['init', name, 'root'].concat(utils.toArray(arguments)); - var obj = file.taskfile.apply(file, args); - return obj ? String(obj) : null; - }, - // Determine absolute destination file path. - destpath: path.join.bind(path, process.cwd()), - // Given some number of licenses, add properly-named license files to the - // files object. - addLicenseFiles: function(files, licenses) { - var available = availableLicenses(); - licenses.forEach(function(license) { - var srcpath = file.taskfile('init/licenses/LICENSE-' + license); - files['LICENSE-' + license] = { - src: srcpath ? String(srcpath) : null, - dest: 'LICENSE-' + license - }; - }); - }, - // Given an absolute or relative source path, and an optional relative - // destination path, copy a file, optionally processing it through the - // passed callback. - copy: function(srcpath, destpath, callback) { - if (typeof destpath !== 'string') { - callback = destpath; - destpath = srcpath; - } - var abssrcpath = file.isPathAbsolute(srcpath) ? srcpath : init.srcpath(srcpath); - var absdestpath = init.destpath(destpath); - if (!path.existsSync(abssrcpath)) { - abssrcpath = file.taskfile('init/misc/placeholder'); - } - verbose.or.write('Writing ' + destpath + '...'); - try { - file.copy(abssrcpath, absdestpath, callback); - verbose.or.ok(); - } catch(e) { - verbose.or.error(); - throw e; - } - }, - // Iterate over all files in the passed object, copying the source file to - // the destination, processing the contents. - copyAndProcess: function(files, props) { - Object.keys(files).forEach(function(key) { - var obj = files[key]; - init.copy(obj.src, obj.dest, function(contents) { - return template.process(contents, props, 'init'); + // Useful init sub-task-specific utilities. + var init = { + // Expose any user-specified default init values. + defaults: file.taskfileDefaults('init/defaults.json'), + // Expose rename rules for this template. + renames: file.taskfileDefaults('init', name, 'rename.json'), + // Return an object containing files to copy with their absolute source path + // and relative destination path, renamed (or omitted) according to rules in + // rename.json (if it exists). + filesToCopy: function(props) { + var files = {}; + var prefix = 'init/' + name + '/root/'; + // Iterate over all source files. + file.taskfiles('init', name, 'root', '**').forEach(function(obj) { + // Get the path relative to the template root. + var relpath = obj.rel.slice(prefix.length); + var rule = init.renames[relpath]; + // Omit files that have an empty / false rule value. + if (!rule && relpath in init.renames) { return; } + // Create an object for this file. + files[relpath] = { + src: obj.abs, + // Rename if a rule exists. + dest: rule ? template.process(rule, props, 'init') : relpath + }; }); - }); - }, - // Save a package.json file in the destination directory. The callback - // can be used to post-process properties to add/remove/whatever. - writePackage: function(filename, props, callback) { - var pkg = {}; - // Basic values. - ['name', 'title', 'description', 'version', 'homepage'].forEach(function(prop) { - if (prop in props) { pkg[prop] = props[prop]; } - }); - // Author. - ['name', 'email', 'url'].forEach(function(prop) { - if (props['author_' + prop]) { - if (!pkg.author) { pkg.author = {}; } - pkg.author[prop] = props['author_' + prop]; + return files; + }, + // Search init template paths for filename. + srcpath: function() { + var args = ['init', name, 'root'].concat(utils.toArray(arguments)); + var obj = file.taskfile.apply(file, args); + return obj ? String(obj) : null; + }, + // Determine absolute destination file path. + destpath: path.join.bind(path, process.cwd()), + // Given some number of licenses, add properly-named license files to the + // files object. + addLicenseFiles: function(files, licenses) { + var available = availableLicenses(); + licenses.forEach(function(license) { + var srcpath = file.taskfile('init/licenses/LICENSE-' + license); + files['LICENSE-' + license] = { + src: srcpath ? String(srcpath) : null, + dest: 'LICENSE-' + license + }; + }); + }, + // Given an absolute or relative source path, and an optional relative + // destination path, copy a file, optionally processing it through the + // passed callback. + copy: function(srcpath, destpath, callback) { + if (typeof destpath !== 'string') { + callback = destpath; + destpath = srcpath; } - }); - // Other stuff. - if ('repository' in props) { pkg.repository = {type: 'git', url: props.repository}; } - if ('bugs' in props) { pkg.bugs = {url: props.bugs}; } - pkg.licenses = props.licenses.map(function(license) { - return {type: license, url: props.homepage + '/blob/master/LICENSE-' + license}; - }); - pkg.dependencies = {}; - pkg.devDependencies = {}; - pkg.keywords = []; - // Node/npm-specific (?) - if (props.node_version) { pkg.engines = {node: props.node_version}; } - if (props.node_main) { pkg.main = props.node_main; } - if (props.node_bin) { pkg.bin = props.node_bin; } - if (props.node_test) { - pkg.scripts = {test: props.node_test}; - if (props.node_test.split(' ')[0] === 'grunt') { - if (!props.node_devDependencies) { props.node_devDependencies = {}; } - props.node_devDependencies.grunt = '~' + grunt.version; + var abssrcpath = file.isPathAbsolute(srcpath) ? srcpath : init.srcpath(srcpath); + var absdestpath = init.destpath(destpath); + if (!path.existsSync(abssrcpath)) { + abssrcpath = file.taskfile('init/misc/placeholder'); } - } - if (props.node_dependencies) { pkg.dependencies = props.node_dependencies; } - if (props.node_devDependencies) { pkg.devDependencies = props.node_devDependencies; } - - // Allow final tweaks to the pkg object. - if (callback) { pkg = callback(pkg, props); } + verbose.or.write('Writing ' + destpath + '...'); + try { + file.copy(abssrcpath, absdestpath, callback); + verbose.or.ok(); + } catch(e) { + verbose.or.error(); + throw e; + } + }, + // Iterate over all files in the passed object, copying the source file to + // the destination, processing the contents. + copyAndProcess: function(files, props) { + Object.keys(files).forEach(function(key) { + var obj = files[key]; + init.copy(obj.src, obj.dest, function(contents) { + return template.process(contents, props, 'init'); + }); + }); + }, + // Save a package.json file in the destination directory. The callback + // can be used to post-process properties to add/remove/whatever. + writePackage: function(filename, props, callback) { + var pkg = {}; + // Basic values. + ['name', 'title', 'description', 'version', 'homepage'].forEach(function(prop) { + if (prop in props) { pkg[prop] = props[prop]; } + }); + // Author. + ['name', 'email', 'url'].forEach(function(prop) { + if (props['author_' + prop]) { + if (!pkg.author) { pkg.author = {}; } + pkg.author[prop] = props['author_' + prop]; + } + }); + // Other stuff. + if ('repository' in props) { pkg.repository = {type: 'git', url: props.repository}; } + if ('bugs' in props) { pkg.bugs = {url: props.bugs}; } + pkg.licenses = props.licenses.map(function(license) { + return {type: license, url: props.homepage + '/blob/master/LICENSE-' + license}; + }); + pkg.dependencies = {}; + pkg.devDependencies = {}; + pkg.keywords = []; + // Node/npm-specific (?) + if (props.node_version) { pkg.engines = {node: props.node_version}; } + if (props.node_main) { pkg.main = props.node_main; } + if (props.node_bin) { pkg.bin = props.node_bin; } + if (props.node_test) { + pkg.scripts = {test: props.node_test}; + if (props.node_test.split(' ')[0] === 'grunt') { + if (!props.node_devDependencies) { props.node_devDependencies = {}; } + props.node_devDependencies.grunt = '~' + grunt.version; + } + } + if (props.node_dependencies) { pkg.dependencies = props.node_dependencies; } + if (props.node_devDependencies) { pkg.devDependencies = props.node_devDependencies; } - // Write file. - file.write(init.destpath(filename), JSON.stringify(pkg, null, 2)); - } - }; + // Allow final tweaks to the pkg object. + if (callback) { pkg = callback(pkg, props); } - // Make args available as flags. - init.flags = {}; - args.forEach(function(flag) { init.flags[flag] = true; }); + // Write file. + file.write(init.destpath(filename), JSON.stringify(pkg, null, 2)); + } + }; - // Execute template code, passing in the init object, done function, and any - // other arguments specified after the init:name:???. - require(templates[name]).apply(null, [init, function() { - // Fail task if errors were logged. - if (task.hadErrors()) { taskDone(false); } - // Otherwise, print a success message. - log.writeln().writeln('Initialized from template "' + name + '".'); - // All done! - taskDone(); - }].concat(args)); -}); + // Make args available as flags. + init.flags = {}; + args.forEach(function(flag) { init.flags[flag] = true; }); -// ============================================================================ -// HELPERS -// ============================================================================ + // Execute template code, passing in the init object, done function, and any + // other arguments specified after the init:name:???. + require(templates[name]).apply(null, [grunt, init, function() { + // Fail task if errors were logged. + if (task.hadErrors()) { taskDone(false); } + // Otherwise, print a success message. + log.writeln().writeln('Initialized from template "' + name + '".'); + // All done! + taskDone(); + }].concat(args)); + }); -// Prompt user to override default values passed in obj. -task.registerHelper('prompt', function(defaults, options, done) { - // If defaults are omitted, shuffle arguments a bit. - if (utils.kindOf(defaults) === 'array') { - done = options; - options = defaults; - defaults = {}; - } + // ========================================================================== + // HELPERS + // ========================================================================== - // Keep track of any "sanitize" functions for later use. - var sanitize = {}; - options.forEach(function(option) { - if (option.sanitize) { - sanitize[option.name] = option.sanitize; + // Prompt user to override default values passed in obj. + grunt.registerHelper('prompt', function(defaults, options, done) { + // If defaults are omitted, shuffle arguments a bit. + if (utils.kindOf(defaults) === 'array') { + done = options; + options = defaults; + defaults = {}; } - }); - // Add one final "are you sure?" prompt. - options.push({ - message: 'Are these answers correct?'.green, - name: 'ANSWERS_VALID', - default: 'Y/n' - }); + // Keep track of any "sanitize" functions for later use. + var sanitize = {}; + options.forEach(function(option) { + if (option.sanitize) { + sanitize[option.name] = option.sanitize; + } + }); - // Ask user for input. This is in an IIFE because it has to execute at least - // once, and might be repeated. - (function ask() { - log.subhead('Please answer the following:'); - var result = utils._.clone(defaults); - // Loop over each prompt option. - utils.async.forEachSeries(options, function(option, done) { - var defaultValue; - utils.async.forEachSeries(['default', 'altDefault'], function(prop, next) { - if (typeof option[prop] === 'function') { - // If the value is a function, execute that function, using the - // value passed into the return callback as the new default value. - option[prop](defaultValue, result, function(err, value) { - defaultValue = value; - next(); - }); - } else { - // Otherwise, if the value actually exists, use it. - if (prop in option) { - defaultValue = option[prop]; - } - next(); - } - }, function() { - // Handle errors (there should never be errors). - option.default = defaultValue; - // Actually get user input. - prmpt.start(); - prmpt.getInput(option, function(err, line) { - if (err) { return done(err); } - result[option.name] = line; - done(); - }); - }); - }, function(err) { - // After all prompt questions have been answered... - if (/y/i.test(result.ANSWERS_VALID)) { - // User accepted all answers. Suspend prompt. - prmpt.pause(); - // Clean up. - delete result.ANSWERS_VALID; - // Iterate over all results. - utils.async.forEachSeries(Object.keys(result), function(name, next) { - // If this value needs to be sanitized, process it now. - if (sanitize[name]) { - sanitize[name](result[name], result, function(err, value) { - if (err) { - result[name] = err; - } else if (arguments.length === 2) { - result[name] = value === 'none' ? '' : value; - } + // Add one final "are you sure?" prompt. + options.push({ + message: 'Are these answers correct?'.green, + name: 'ANSWERS_VALID', + default: 'Y/n' + }); + + // Ask user for input. This is in an IIFE because it has to execute at least + // once, and might be repeated. + (function ask() { + log.subhead('Please answer the following:'); + var result = utils._.clone(defaults); + // Loop over each prompt option. + utils.async.forEachSeries(options, function(option, done) { + var defaultValue; + utils.async.forEachSeries(['default', 'altDefault'], function(prop, next) { + if (typeof option[prop] === 'function') { + // If the value is a function, execute that function, using the + // value passed into the return callback as the new default value. + option[prop](defaultValue, result, function(err, value) { + defaultValue = value; next(); }); } else { + // Otherwise, if the value actually exists, use it. + if (prop in option) { + defaultValue = option[prop]; + } next(); } - }, function(err) { - // Done! - log.writeln(); - done(err, result); + }, function() { + // Handle errors (there should never be errors). + option.default = defaultValue; + // Actually get user input. + prmpt.start(); + prmpt.getInput(option, function(err, line) { + if (err) { return done(err); } + result[option.name] = line; + done(); + }); }); - } else { - // Otherwise update the default value for each user prompt option... - options.slice(0, -1).forEach(function(option) { - option.default = result[option.name]; + }, function(err) { + // After all prompt questions have been answered... + if (/y/i.test(result.ANSWERS_VALID)) { + // User accepted all answers. Suspend prompt. + prmpt.pause(); + // Clean up. + delete result.ANSWERS_VALID; + // Iterate over all results. + utils.async.forEachSeries(Object.keys(result), function(name, next) { + // If this value needs to be sanitized, process it now. + if (sanitize[name]) { + sanitize[name](result[name], result, function(err, value) { + if (err) { + result[name] = err; + } else if (arguments.length === 2) { + result[name] = value === 'none' ? '' : value; + } + next(); + }); + } else { + next(); + } + }, function(err) { + // Done! + log.writeln(); + done(err, result); + }); + } else { + // Otherwise update the default value for each user prompt option... + options.slice(0, -1).forEach(function(option) { + option.default = result[option.name]; + }); + // ...and start over again. + ask(); + } + }); + }()); + }); + + // Built-in prompt options for the prompt_for helper. + // These generally follow the node "prompt" module convention, except: + // * The "default" value can be a function which is executed at run-time. + // * An optional "sanitize" function has been added to post-process data. + var prompts = { + name: { + message: 'Project name', + default: function(value, data, done) { + var types = ['javascript', 'js']; + if (data.type) { types.push(data.type); } + var type = '(?:' + types.join('|') + ')'; + // This regexp matches: + // leading type- type. type_ + // trailing -type .type _type and/or -js .js _js + var re = new RegExp('^' + type + '[\\-\\._]?|(?:[\\-\\._]?' + type + ')?(?:[\\-\\._]?js)?$', 'ig'); + // Strip the above stuff from the current dirname. + var name = path.basename(process.cwd()).replace(re, ''); + // Remove anything not a letter, number, dash, dot or underscore. + name = name.replace(/[^\w\-\.]/g, ''); + done(null, name); + }, + validator: /^[\w\-\.]+$/, + warning: 'Name must be only letters, numbers, dashes, dots or underscores.', + sanitize: function(value, data, done) { + // An additional value, safe to use as a JavaScript identifier. + data.js_safe_name = value.replace(/[\W_]+/g, '_').replace(/^(\d)/, '_$1'); + // If no value is passed to `done`, the original property isn't modified. + done(); + } + }, + title: { + message: 'Project title', + default: function(value, data, done) { + var title = data.name || ''; + title = title.replace(/[\W_]+/g, ' '); + title = title.replace(/\w+/g, function(word) { + return word[0].toUpperCase() + word.slice(1).toLowerCase(); }); - // ...and start over again. - ask(); + title = title.replace(/jquery/i, 'jQuery'); + done(null, title); } - }); - }()); -}); - -// Built-in prompt options for the prompt_for helper. -// These generally follow the node "prompt" module convention, except: -// * The "default" value can be a function which is executed at run-time. -// * An optional "sanitize" function has been added to post-process data. -var prompts = { - name: { - message: 'Project name', - default: function(value, data, done) { - var types = ['javascript', 'js']; - if (data.type) { types.push(data.type); } - var type = '(?:' + types.join('|') + ')'; - // This regexp matches: - // leading type- type. type_ - // trailing -type .type _type and/or -js .js _js - var re = new RegExp('^' + type + '[\\-\\._]?|(?:[\\-\\._]?' + type + ')?(?:[\\-\\._]?js)?$', 'ig'); - // Strip the above stuff from the current dirname. - var name = path.basename(process.cwd()).replace(re, ''); - // Remove anything not a letter, number, dash, dot or underscore. - name = name.replace(/[^\w\-\.]/g, ''); - done(null, name); }, - validator: /^[\w\-\.]+$/, - warning: 'Name must be only letters, numbers, dashes, dots or underscores.', - sanitize: function(value, data, done) { - // An additional value, safe to use as a JavaScript identifier. - data.js_safe_name = value.replace(/[\W_]+/g, '_').replace(/^(\d)/, '_$1'); - // If no value is passed to `done`, the original property isn't modified. - done(); - } - }, - title: { - message: 'Project title', - default: function(value, data, done) { - var title = data.name || ''; - title = title.replace(/[\W_]+/g, ' '); - title = title.replace(/\w+/g, function(word) { - return word[0].toUpperCase() + word.slice(1).toLowerCase(); - }); - title = title.replace(/jquery/i, 'jQuery'); - done(null, title); - } - }, - description: { - message: 'Description', - default: 'The best project ever.' - }, - version: { - message: 'Version', - default: function(value, data, done) { - // Get a valid semver tag from `git describe --tags` if possible. - utils.spawn({ - cmd: 'git', - args: ['describe', '--tags'], - fallback: '' - }, function(err, result, code) { - result = result.split('-')[0]; - done(null, semver.valid(result) || '0.1.0'); - }); + description: { + message: 'Description', + default: 'The best project ever.' + }, + version: { + message: 'Version', + default: function(value, data, done) { + // Get a valid semver tag from `git describe --tags` if possible. + utils.spawn({ + cmd: 'git', + args: ['describe', '--tags'], + fallback: '' + }, function(err, result, code) { + result = result.split('-')[0]; + done(null, semver.valid(result) || '0.1.0'); + }); + }, + validator: semver.valid, + warning: 'Must be a valid semantic version.' }, - validator: semver.valid, - warning: 'Must be a valid semantic version.' - }, - repository: { - message: 'Project git repository', - default: function(value, data, done) { - // Change any git@...:... uri to git://.../... format. - task.helper('git_origin', function(err, result) { - if (err) { - // Attempt to guess at the repo name. Maybe we'll get lucky! - result = 'git://github.com/' + (process.env.USER || '???') + '/' + - data.name + '.git'; + repository: { + message: 'Project git repository', + default: function(value, data, done) { + // Change any git@...:... uri to git://.../... format. + grunt.helper('git_origin', function(err, result) { + if (err) { + // Attempt to guess at the repo name. Maybe we'll get lucky! + result = 'git://github.com/' + (process.env.USER || '???') + '/' + + data.name + '.git'; + } else { + result = result.replace(/^git@([^:]+):/, 'git://$1/'); + } + done(null, result); + }); + }, + sanitize: function(value, data, done) { + // An additional computed "git_user" property. + var repo = grunt.helper('github_web_url', data.repository); + var parts; + if (repo != null) { + parts = repo.split('/'); + data.git_user = parts[parts.length - 2]; + done(); } else { - result = result.replace(/^git@([^:]+):/, 'git://$1/'); + // Attempt to pull the data from the user's git config. + utils.spawn({ + cmd: 'git', + args: ['config', '--get', 'github.user'], + fallback: '' + }, function(err, result, code) { + data.git_user = result || process.env.USER || '???'; + done(); + }); } - done(null, result); - }); + } }, - sanitize: function(value, data, done) { - // An additional computed "git_user" property. - var repo = task.helper('github_web_url', data.repository); - var parts; - if (repo != null) { - parts = repo.split('/'); - data.git_user = parts[parts.length - 2]; - done(); - } else { + homepage: { + message: 'Project homepage', + // If GitHub is the origin, the (potential) homepage is easy to figure out. + default: function(value, data, done) { + done(null, grunt.helper('github_web_url', data.repository) || 'none'); + } + }, + bugs: { + message: 'Project issues tracker', + // If GitHub is the origin, the issues tracker is easy to figure out. + default: function(value, data, done) { + done(null, grunt.helper('github_web_url', data.repository, 'issues') || 'none'); + } + }, + licenses: { + message: 'Licenses', + default: 'MIT', + validator: /^[\w\-]+(?:\s+[\w\-]+)*$/, + warning: 'Must be one or more space-separated licenses. (eg. ' + + availableLicenses().join(' ') + ')', + // Split the string on spaces. + sanitize: function(value, data, done) { done(value.split(/\s+/)); } + }, + author_name: { + message: 'Author name', + default: function(value, data, done) { // Attempt to pull the data from the user's git config. utils.spawn({ cmd: 'git', - args: ['config', '--get', 'github.user'], - fallback: '' - }, function(err, result, code) { - data.git_user = result || process.env.USER || '???'; - done(); - }); + args: ['config', '--get', 'user.name'], + fallback: 'none' + }, done); } + }, + author_email: { + message: 'Author email', + default: function(value, data, done) { + // Attempt to pull the data from the user's git config. + utils.spawn({ + cmd: 'git', + args: ['config', '--get', 'user.email'], + fallback: 'none' + }, done); + } + }, + author_url: { + message: 'Author url', + default: 'none' + }, + node_version: { + message: 'What versions of node does it run on?', + default: '>= ' + process.versions.node + }, + node_main: { + message: 'Main module/entry point', + default: function(value, data, done) { + done(null, 'lib/' + data.name); + } + }, + node_bin: { + message: 'CLI script', + default: function(value, data, done) { + done(null, 'bin/' + data.name); + } + }, + node_test: { + message: 'Test command', + default: 'grunt test' + }, + grunt_version: { + message: 'What versions of grunt does it require?', + default: '~' + grunt.version } - }, - homepage: { - message: 'Project homepage', - // If GitHub is the origin, the (potential) homepage is easy to figure out. - default: function(value, data, done) { - done(null, task.helper('github_web_url', data.repository) || 'none'); - } - }, - bugs: { - message: 'Project issues tracker', - // If GitHub is the origin, the issues tracker is easy to figure out. - default: function(value, data, done) { - done(null, task.helper('github_web_url', data.repository, 'issues') || 'none'); - } - }, - licenses: { - message: 'Licenses', - default: 'MIT', - validator: /^[\w\-]+(?:\s+[\w\-]+)*$/, - warning: 'Must be one or more space-separated licenses. (eg. ' + - availableLicenses().join(' ') + ')', - // Split the string on spaces. - sanitize: function(value, data, done) { done(value.split(/\s+/)); } - }, - author_name: { - message: 'Author name', - default: function(value, data, done) { - // Attempt to pull the data from the user's git config. - utils.spawn({ - cmd: 'git', - args: ['config', '--get', 'user.name'], - fallback: 'none' - }, done); - } - }, - author_email: { - message: 'Author email', - default: function(value, data, done) { - // Attempt to pull the data from the user's git config. - utils.spawn({ - cmd: 'git', - args: ['config', '--get', 'user.email'], - fallback: 'none' - }, done); - } - }, - author_url: { - message: 'Author url', - default: 'none' - }, - node_version: { - message: 'What versions of node does it run on?', - default: '>= ' + process.versions.node - }, - node_main: { - message: 'Main module/entry point', - default: function(value, data, done) { - done(null, 'lib/' + data.name); - } - }, - node_bin: { - message: 'CLI script', - default: function(value, data, done) { - done(null, 'bin/' + data.name); - } - }, - node_test: { - message: 'Test command', - default: 'grunt test' - }, - grunt_version: { - message: 'What versions of grunt does it require?', - default: '~' + grunt.version - } -}; + }; -// Expose prompts object so that prompt_for prompts can be added or modified. -task.registerHelper('prompt_for_obj', function() { - return prompts; -}); + // Expose prompts object so that prompt_for prompts can be added or modified. + grunt.registerHelper('prompt_for_obj', function() { + return prompts; + }); -// Commonly-used prompt options with meaningful default values. -task.registerHelper('prompt_for', function(name, altDefault) { - // Clone the option so the original options object doesn't get modified. - var option = utils._.clone(prompts[name]); - option.name = name; + // Commonly-used prompt options with meaningful default values. + grunt.registerHelper('prompt_for', function(name, altDefault) { + // Clone the option so the original options object doesn't get modified. + var option = utils._.clone(prompts[name]); + option.name = name; - var defaults = file.taskfileDefaults('init/defaults.json'); - if (name in defaults) { - // A user default was specified for this option, so use its value. - option.default = defaults[name]; - } else if (arguments.length === 2) { - // An alternate default was specified, so use it. - option.altDefault = altDefault; - } - return option; -}); + var defaults = file.taskfileDefaults('init/defaults.json'); + if (name in defaults) { + // A user default was specified for this option, so use its value. + option.default = defaults[name]; + } else if (arguments.length === 2) { + // An alternate default was specified, so use it. + option.altDefault = altDefault; + } + return option; + }); -// Get the git origin url from the current repo (if possible). -task.registerHelper('git_origin', function(done) { - utils.spawn({ - cmd: 'git', - args: ['remote', '-v'] - }, function(err, result, code) { - var re = /^origin\s/; - var lines; - if (!err) { - lines = result.split('\n').filter(re.test, re); - if (lines.length > 0) { - done(null, lines[0].split(/\s/)[1]); - return; + // Get the git origin url from the current repo (if possible). + grunt.registerHelper('git_origin', function(done) { + utils.spawn({ + cmd: 'git', + args: ['remote', '-v'] + }, function(err, result, code) { + var re = /^origin\s/; + var lines; + if (!err) { + lines = result.split('\n').filter(re.test, re); + if (lines.length > 0) { + done(null, lines[0].split(/\s/)[1]); + return; + } } + done(true, 'none'); + }); + }); + + // Generate a GitHub web URL from a GitHub repo URI. + var githubWebUrlRe = /^.+(?:@|:\/\/)(github.com)[:\/](.+?)(?:\.git|\/)?$/; + grunt.registerHelper('github_web_url', function(uri, suffix) { + var matches = githubWebUrlRe.exec(uri); + if (!matches) { return null; } + var url = 'https://' + matches[1] + '/' + matches[2]; + if (suffix) { + url += '/' + suffix.replace(/^\//, ''); } - done(true, 'none'); + return url; }); -}); -// Generate a GitHub web URL from a GitHub repo URI. -var githubWebUrlRe = /^.+(?:@|:\/\/)(github.com)[:\/](.+?)(?:\.git|\/)?$/; -task.registerHelper('github_web_url', function(uri, suffix) { - var matches = githubWebUrlRe.exec(uri); - if (!matches) { return null; } - var url = 'https://' + matches[1] + '/' + matches[2]; - if (suffix) { - url += '/' + suffix.replace(/^\//, ''); - } - return url; -}); +}; diff --git a/tasks/init/commonjs.js b/tasks/init/commonjs.js index 84a35d971..da9615e4e 100644 --- a/tasks/init/commonjs.js +++ b/tasks/init/commonjs.js @@ -7,22 +7,33 @@ * http://benalman.com/about/license/ */ -module.exports = function(init, done) { - task.helper('prompt', {}, [ +module.exports = function(grunt, init, done) { + // Grunt utilities. + var task = grunt.task; + var file = grunt.file; + var utils = grunt.utils; + var log = grunt.log; + var verbose = grunt.verbose; + var fail = grunt.fail; + var option = grunt.option; + var config = grunt.config; + var template = grunt.template; + + grunt.helper('prompt', {}, [ // Prompt for these values. - task.helper('prompt_for', 'name'), - task.helper('prompt_for', 'description'), - task.helper('prompt_for', 'version'), - task.helper('prompt_for', 'repository'), - task.helper('prompt_for', 'homepage'), - task.helper('prompt_for', 'bugs'), - task.helper('prompt_for', 'licenses'), - task.helper('prompt_for', 'author_name'), - task.helper('prompt_for', 'author_email'), - task.helper('prompt_for', 'author_url'), - task.helper('prompt_for', 'node_version'), - task.helper('prompt_for', 'node_main'), - task.helper('prompt_for', 'node_test') + grunt.helper('prompt_for', 'name'), + grunt.helper('prompt_for', 'description'), + grunt.helper('prompt_for', 'version'), + grunt.helper('prompt_for', 'repository'), + grunt.helper('prompt_for', 'homepage'), + grunt.helper('prompt_for', 'bugs'), + grunt.helper('prompt_for', 'licenses'), + grunt.helper('prompt_for', 'author_name'), + grunt.helper('prompt_for', 'author_email'), + grunt.helper('prompt_for', 'author_url'), + grunt.helper('prompt_for', 'node_version'), + grunt.helper('prompt_for', 'node_main'), + grunt.helper('prompt_for', 'node_test') ], function(err, props) { // Files to copy (and process). var files = init.filesToCopy(props); @@ -39,4 +50,5 @@ module.exports = function(init, done) { // All done! done(); }); + }; diff --git a/tasks/init/commonjs/root/README.md b/tasks/init/commonjs/root/README.md index d93504a42..893e35cea 100644 --- a/tasks/init/commonjs/root/README.md +++ b/tasks/init/commonjs/root/README.md @@ -53,5 +53,5 @@ _Also, please don't edit files in the "dist" subdirectory as they are generated _(Nothing yet)_ ## License -Copyright (c) {%= template.today('yyyy') %} {%= author_name %} +Copyright (c) {%= grunt.template.today('yyyy') %} {%= author_name %} Licensed under the {%= licenses.join(', ') %} license{%= licenses.length === 1 ? '' : 's' %}. diff --git a/tasks/init/commonjs/root/grunt.js b/tasks/init/commonjs/root/grunt.js index 2775693c3..c568e17d5 100644 --- a/tasks/init/commonjs/root/grunt.js +++ b/tasks/init/commonjs/root/grunt.js @@ -1,55 +1,69 @@ -/*global config:true, task:true*/ -config.init({ - pkg: '', - meta: { - banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' + - '<%= template.today("m/d/yyyy") %>\n' + - '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' + - '* Copyright (c) <%= template.today("yyyy") %> <%= pkg.author.name %>;' + - ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */' - }, - concat: { - dist: { - src: ['', '.js>'], - dest: 'dist/<%= pkg.name %>.js' - } - }, - min: { - dist: { - src: ['', ''], - dest: 'dist/<%= pkg.name %>.min.js' - } - }, - test: { - files: ['test/**/*.js'] - }, - lint: { - files: ['grunt.js', 'lib/**/*.js', 'test/**/*.js'] - }, - watch: { - files: '', - tasks: 'lint test' - }, - jshint: { - options: { - curly: true, - eqeqeq: true, - immed: true, - latedef: true, - newcap: true, - noarg: true, - sub: true, - undef: true, - boss: true, - eqnull: true +module.exports = function(grunt) { + // Grunt utilities. + var task = grunt.task; + var file = grunt.file; + var utils = grunt.utils; + var log = grunt.log; + var verbose = grunt.verbose; + var fail = grunt.fail; + var option = grunt.option; + var config = grunt.config; + var template = grunt.template; + + // Initialize config. + grunt.initConfig({ + pkg: '', + meta: { + banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' + + '<%= grunt.template.today("m/d/yyyy") %>\n' + + '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' + + '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' + + ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */' + }, + concat: { + dist: { + src: ['', '.js>'], + dest: 'dist/<%= pkg.name %>.js' + } + }, + min: { + dist: { + src: ['', ''], + dest: 'dist/<%= pkg.name %>.min.js' + } + }, + test: { + files: ['test/**/*.js'] }, - globals: { - exports: true, - module: false - } - }, - uglify: {} -}); + lint: { + files: ['grunt.js', 'lib/**/*.js', 'test/**/*.js'] + }, + watch: { + files: '', + tasks: 'lint test' + }, + jshint: { + options: { + curly: true, + eqeqeq: true, + immed: true, + latedef: true, + newcap: true, + noarg: true, + sub: true, + undef: true, + boss: true, + eqnull: true + }, + globals: { + exports: true, + module: false + } + }, + uglify: {} + }); + + // Default task. + grunt.registerTask('default', 'lint test concat min'); -// Default task. -task.registerTask('default', 'lint test concat min'); +}; diff --git a/tasks/init/commonjs/root/lib/name.js b/tasks/init/commonjs/root/lib/name.js index 434218a55..dadc70f66 100644 --- a/tasks/init/commonjs/root/lib/name.js +++ b/tasks/init/commonjs/root/lib/name.js @@ -2,7 +2,7 @@ * {%= name %} * {%= homepage %} * - * Copyright (c) {%= template.today('yyyy') %} {%= author_name %} + * Copyright (c) {%= grunt.template.today('yyyy') %} {%= author_name %} * Licensed under the {%= licenses.join(', ') %} license{%= licenses.length === 1 ? '' : 's' %}. */ diff --git a/tasks/init/gruntplugin.js b/tasks/init/gruntplugin.js index 7f37d0732..a25078b25 100644 --- a/tasks/init/gruntplugin.js +++ b/tasks/init/gruntplugin.js @@ -7,27 +7,38 @@ * http://benalman.com/about/license/ */ -module.exports = function(init, done) { - task.helper('prompt', {type: 'grunt'}, [ +module.exports = function(grunt, init, done) { + // Grunt utilities. + var task = grunt.task; + var file = grunt.file; + var utils = grunt.utils; + var log = grunt.log; + var verbose = grunt.verbose; + var fail = grunt.fail; + var option = grunt.option; + var config = grunt.config; + var template = grunt.template; + + grunt.helper('prompt', {type: 'grunt'}, [ // Prompt for these values. - task.helper('prompt_for', 'name', function(value, data, done) { + grunt.helper('prompt_for', 'name', function(value, data, done) { // Prepend "grunt-" to default name if not already there. data.short_name = value; value = data.full_name = 'grunt-' + value; // if (!/^grunt-/.test(value)) { value = 'grunt-' + value; } done(null, value); }), - task.helper('prompt_for', 'description', 'The best sample grunt tasks ever.'), - task.helper('prompt_for', 'version'), - task.helper('prompt_for', 'repository'), - task.helper('prompt_for', 'homepage'), - task.helper('prompt_for', 'bugs'), - task.helper('prompt_for', 'licenses'), - task.helper('prompt_for', 'author_name'), - task.helper('prompt_for', 'author_email'), - task.helper('prompt_for', 'author_url'), - task.helper('prompt_for', 'grunt_version'), - task.helper('prompt_for', 'node_version', '*') + grunt.helper('prompt_for', 'description', 'The best sample grunt tasks ever.'), + grunt.helper('prompt_for', 'version'), + grunt.helper('prompt_for', 'repository'), + grunt.helper('prompt_for', 'homepage'), + grunt.helper('prompt_for', 'bugs'), + grunt.helper('prompt_for', 'licenses'), + grunt.helper('prompt_for', 'author_name'), + grunt.helper('prompt_for', 'author_email'), + grunt.helper('prompt_for', 'author_url'), + grunt.helper('prompt_for', 'grunt_version'), + grunt.helper('prompt_for', 'node_version', '*') ], function(err, props) { // Set a few grunt-plugin-specific properties. props.node_main = 'grunt.js'; @@ -50,4 +61,5 @@ module.exports = function(init, done) { // All done! done(); }); + }; diff --git a/tasks/init/gruntplugin/root/README.md b/tasks/init/gruntplugin/root/README.md index b69531eab..9b6aa28be 100644 --- a/tasks/init/gruntplugin/root/README.md +++ b/tasks/init/gruntplugin/root/README.md @@ -21,5 +21,5 @@ In lieu of a formal styleguide, take care to maintain the existing coding style. _(Nothing yet)_ ## License -Copyright (c) {%= template.today('yyyy') %} {%= author_name %} +Copyright (c) {%= grunt.template.today('yyyy') %} {%= author_name %} Licensed under the {%= licenses.join(', ') %} license{%= licenses.length === 1 ? '' : 's' %}. diff --git a/tasks/init/gruntplugin/root/grunt.js b/tasks/init/gruntplugin/root/grunt.js index 3a81284f0..8f026cdbe 100644 --- a/tasks/init/gruntplugin/root/grunt.js +++ b/tasks/init/gruntplugin/root/grunt.js @@ -1,49 +1,51 @@ -/*global config:true, task:true*/ -config.init({ - pkg: '', - test: { - files: ['test/**/*.js'] - }, - lint: { - files: ['grunt.js', 'tasks/**/*.js', 'test/**/*.js'] - }, - watch: { - files: '', - tasks: 'default' - }, - jshint: { - options: { - curly: true, - eqeqeq: true, - immed: true, - latedef: true, - newcap: true, - noarg: true, - sub: true, - undef: true, - boss: true, - eqnull: true, - node: true, - es5: true +module.exports = function(grunt) { + // Grunt utilities. + var task = grunt.task; + var file = grunt.file; + var utils = grunt.utils; + var log = grunt.log; + var verbose = grunt.verbose; + var fail = grunt.fail; + var option = grunt.option; + var config = grunt.config; + var template = grunt.template; + + // Initialize config. + grunt.initConfig({ + pkg: '', + test: { + files: ['test/**/*.js'] + }, + lint: { + files: ['grunt.js', 'tasks/**/*.js', 'test/**/*.js'] }, - globals: { - exports: true, - grunt: true, - utils: true, - task: true, - file: true, - fail: true, - config: true, - option: true, - template: true, - log: true, - verbose: true + watch: { + files: '', + tasks: 'default' + }, + jshint: { + options: { + curly: true, + eqeqeq: true, + immed: true, + latedef: true, + newcap: true, + noarg: true, + sub: true, + undef: true, + boss: true, + eqnull: true, + node: true, + es5: true + }, + globals: {} } - } -}); + }); + + // Load local tasks. + grunt.loadTasks('tasks'); -// Load local tasks. -task.loadTasks('tasks'); + // Default task. + grunt.registerTask('default', 'lint test'); -// Default task. -task.registerTask('default', 'lint test'); +}; diff --git a/tasks/init/gruntplugin/root/tasks/name.js b/tasks/init/gruntplugin/root/tasks/name.js index f813d323b..c50056337 100644 --- a/tasks/init/gruntplugin/root/tasks/name.js +++ b/tasks/init/gruntplugin/root/tasks/name.js @@ -2,25 +2,39 @@ * {%= full_name %} * {%= homepage %} * - * Copyright (c) {%= template.today('yyyy') %} {%= author_name %} + * Copyright (c) {%= grunt.template.today('yyyy') %} {%= author_name %} * Licensed under the {%= licenses.join(', ') %} license{%= licenses.length === 1 ? '' : 's' %}. */ -// Please see the grunt documentation for more information regarding task and -// helper creation: https://github.com/cowboy/grunt/blob/master/docs/toc.md +module.exports = function(grunt) { + // Grunt utilities. + var task = grunt.task; + var file = grunt.file; + var utils = grunt.utils; + var log = grunt.log; + var verbose = grunt.verbose; + var fail = grunt.fail; + var option = grunt.option; + var config = grunt.config; + var template = grunt.template; -// ============================================================================ -// TASKS -// ============================================================================ + // Please see the grunt documentation for more information regarding task and + // helper creation: https://github.com/cowboy/grunt/blob/master/docs/toc.md -task.registerTask('{%= short_name %}', 'Your task description goes here.', function() { - log.write(task.helper('{%= short_name %}')); -}); + // ========================================================================== + // TASKS + // ========================================================================== -// ============================================================================ -// HELPERS -// ============================================================================ + grunt.registerTask('{%= short_name %}', 'Your task description goes here.', function() { + log.write(grunt.helper('{%= short_name %}')); + }); -task.registerHelper('{%= short_name %}', function() { - return '{%= short_name %}!!!'; -}); + // ========================================================================== + // HELPERS + // ========================================================================== + + grunt.registerHelper('{%= short_name %}', function() { + return '{%= short_name %}!!!'; + }); + +}; diff --git a/tasks/init/gruntplugin/root/test/name_test.js b/tasks/init/gruntplugin/root/test/name_test.js index 8584aec8e..8b9645f5f 100644 --- a/tasks/init/gruntplugin/root/test/name_test.js +++ b/tasks/init/gruntplugin/root/test/name_test.js @@ -1,3 +1,5 @@ +var grunt = require('grunt'); + exports['{%= short_name %}'] = { setUp: function(done) { // setup here @@ -6,7 +8,7 @@ exports['{%= short_name %}'] = { 'helper': function(test) { test.expect(1); // tests here - test.equal(task.helper('{%= short_name %}'), '{%= short_name %}!!!', 'should return the correct value.'); + test.equal(grunt.helper('{%= short_name %}'), '{%= short_name %}!!!', 'should return the correct value.'); test.done(); } }; diff --git a/tasks/init/jquery.js b/tasks/init/jquery.js index 0a2f8bff6..349e9f1bd 100644 --- a/tasks/init/jquery.js +++ b/tasks/init/jquery.js @@ -7,20 +7,31 @@ * http://benalman.com/about/license/ */ -module.exports = function(init, done) { - task.helper('prompt', {type: 'jquery'}, [ +module.exports = function(grunt, init, done) { + // Grunt utilities. + var task = grunt.task; + var file = grunt.file; + var utils = grunt.utils; + var log = grunt.log; + var verbose = grunt.verbose; + var fail = grunt.fail; + var option = grunt.option; + var config = grunt.config; + var template = grunt.template; + + grunt.helper('prompt', {type: 'jquery'}, [ // Prompt for these values. - task.helper('prompt_for', 'name'), - task.helper('prompt_for', 'title'), - task.helper('prompt_for', 'description', 'The best jQuery plugin ever.'), - task.helper('prompt_for', 'version'), - task.helper('prompt_for', 'repository'), - task.helper('prompt_for', 'homepage'), - task.helper('prompt_for', 'bugs'), - task.helper('prompt_for', 'licenses'), - task.helper('prompt_for', 'author_name'), - task.helper('prompt_for', 'author_email'), - task.helper('prompt_for', 'author_url') + grunt.helper('prompt_for', 'name'), + grunt.helper('prompt_for', 'title'), + grunt.helper('prompt_for', 'description', 'The best jQuery plugin ever.'), + grunt.helper('prompt_for', 'version'), + grunt.helper('prompt_for', 'repository'), + grunt.helper('prompt_for', 'homepage'), + grunt.helper('prompt_for', 'bugs'), + grunt.helper('prompt_for', 'licenses'), + grunt.helper('prompt_for', 'author_name'), + grunt.helper('prompt_for', 'author_email'), + grunt.helper('prompt_for', 'author_url') ], function(err, props) { // Files to copy (and process). var files = init.filesToCopy(props); @@ -37,4 +48,5 @@ module.exports = function(init, done) { // All done! done(); }); + }; diff --git a/tasks/init/jquery/root/README.md b/tasks/init/jquery/root/README.md index c78511da6..c13662093 100644 --- a/tasks/init/jquery/root/README.md +++ b/tasks/init/jquery/root/README.md @@ -35,5 +35,5 @@ _Also, please don't edit files in the "dist" subdirectory as they are generated _(Nothing yet)_ ## License -Copyright (c) {%= template.today('yyyy') %} {%= author_name %} +Copyright (c) {%= grunt.template.today('yyyy') %} {%= author_name %} Licensed under the {%= licenses.join(', ') %} license{%= licenses.length === 1 ? '' : 's' %}. diff --git a/tasks/init/jquery/root/grunt.js b/tasks/init/jquery/root/grunt.js index b66fa1e14..c4102f0f8 100644 --- a/tasks/init/jquery/root/grunt.js +++ b/tasks/init/jquery/root/grunt.js @@ -1,54 +1,69 @@ -/*global config:true, task:true*/ -config.init({ - pkg: '', - meta: { - banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' + - '<%= template.today("m/d/yyyy") %>\n' + - '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' + - '* Copyright (c) <%= template.today("yyyy") %> <%= pkg.author.name %>;' + - ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */' - }, - concat: { - dist: { - src: ['', '.js>'], - dest: 'dist/<%= pkg.name %>.js' - } - }, - min: { - dist: { - src: ['', ''], - dest: 'dist/<%= pkg.name %>.min.js' - } - }, - qunit: { - files: ['test/**/*.html'] - }, - lint: { - files: ['grunt.js', 'src/**/*.js', 'test/**/*.js'] - }, - watch: { - files: '', - tasks: 'lint qunit' - }, - jshint: { - options: { - curly: true, - eqeqeq: true, - immed: true, - latedef: true, - newcap: true, - noarg: true, - sub: true, - undef: true, - eqnull: true, - browser: true +/*global module:false*/ +module.exports = function(grunt) { + // Grunt utilities. + var task = grunt.task; + var file = grunt.file; + var utils = grunt.utils; + var log = grunt.log; + var verbose = grunt.verbose; + var fail = grunt.fail; + var option = grunt.option; + var config = grunt.config; + var template = grunt.template; + + // Initialize config. + grunt.initConfig({ + pkg: '', + meta: { + banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' + + '<%= grunt.template.today("m/d/yyyy") %>\n' + + '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' + + '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' + + ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */' + }, + concat: { + dist: { + src: ['', '.js>'], + dest: 'dist/<%= pkg.name %>.js' + } + }, + min: { + dist: { + src: ['', ''], + dest: 'dist/<%= pkg.name %>.min.js' + } + }, + qunit: { + files: ['test/**/*.html'] }, - globals: { - jQuery: true - } - }, - uglify: {} -}); + lint: { + files: ['grunt.js', 'src/**/*.js', 'test/**/*.js'] + }, + watch: { + files: '', + tasks: 'lint qunit' + }, + jshint: { + options: { + curly: true, + eqeqeq: true, + immed: true, + latedef: true, + newcap: true, + noarg: true, + sub: true, + undef: true, + eqnull: true, + browser: true + }, + globals: { + jQuery: true + } + }, + uglify: {} + }); + + // Default task. + grunt.registerTask('default', 'lint qunit concat min'); -// Default task. -task.registerTask('default', 'lint qunit concat min'); +}; diff --git a/tasks/init/jquery/root/src/name.js b/tasks/init/jquery/root/src/name.js index c31ca10ef..922c04146 100644 --- a/tasks/init/jquery/root/src/name.js +++ b/tasks/init/jquery/root/src/name.js @@ -2,7 +2,7 @@ * {%= name %} * {%= homepage %} * - * Copyright (c) {%= template.today('yyyy') %} {%= author_name %} + * Copyright (c) {%= grunt.template.today('yyyy') %} {%= author_name %} * Licensed under the {%= licenses.join(', ') %} license{%= licenses.length === 1 ? '' : 's' %}. */ diff --git a/tasks/init/jquery/root/test/name_test.js b/tasks/init/jquery/root/test/name_test.js index 4f5410547..a679d73a7 100644 --- a/tasks/init/jquery/root/test/name_test.js +++ b/tasks/init/jquery/root/test/name_test.js @@ -1,6 +1,6 @@ -/*global QUnit:true, module:true, test:true, asyncTest:true, expect:true*/ -/*global start:true, stop:true ok:true, equal:true, notEqual:true, deepEqual:true*/ -/*global notDeepEqual:true, strictEqual:true, notStrictEqual:true, raises:true*/ +/*global QUnit:false, module:false, test:false, asyncTest:false, expect:false*/ +/*global start:false, stop:false ok:false, equal:false, notEqual:false, deepEqual:false*/ +/*global notDeepEqual:false, strictEqual:false, notStrictEqual:false, raises:false*/ (function($) { module('jQuery#awesome', { diff --git a/tasks/init/licenses/LICENSE-MIT b/tasks/init/licenses/LICENSE-MIT index 4aabf4cbb..1922cc28f 100644 --- a/tasks/init/licenses/LICENSE-MIT +++ b/tasks/init/licenses/LICENSE-MIT @@ -1,4 +1,4 @@ -Copyright (c) {%= template.today('yyyy') %} {%= author_name %} +Copyright (c) {%= grunt.template.today('yyyy') %} {%= author_name %} Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/tasks/init/node.js b/tasks/init/node.js index 23a1e976e..a5193a8a8 100644 --- a/tasks/init/node.js +++ b/tasks/init/node.js @@ -7,22 +7,33 @@ * http://benalman.com/about/license/ */ -module.exports = function(init, done) { - task.helper('prompt', {type: 'node'}, [ +module.exports = function(grunt, init, done) { + // Grunt utilities. + var task = grunt.task; + var file = grunt.file; + var utils = grunt.utils; + var log = grunt.log; + var verbose = grunt.verbose; + var fail = grunt.fail; + var option = grunt.option; + var config = grunt.config; + var template = grunt.template; + + grunt.helper('prompt', {type: 'node'}, [ // Prompt for these values. - task.helper('prompt_for', 'name'), - task.helper('prompt_for', 'description'), - task.helper('prompt_for', 'version'), - task.helper('prompt_for', 'repository'), - task.helper('prompt_for', 'homepage'), - task.helper('prompt_for', 'bugs'), - task.helper('prompt_for', 'licenses'), - task.helper('prompt_for', 'author_name'), - task.helper('prompt_for', 'author_email'), - task.helper('prompt_for', 'author_url'), - task.helper('prompt_for', 'node_version'), - task.helper('prompt_for', 'node_main'), - task.helper('prompt_for', 'node_test') + grunt.helper('prompt_for', 'name'), + grunt.helper('prompt_for', 'description'), + grunt.helper('prompt_for', 'version'), + grunt.helper('prompt_for', 'repository'), + grunt.helper('prompt_for', 'homepage'), + grunt.helper('prompt_for', 'bugs'), + grunt.helper('prompt_for', 'licenses'), + grunt.helper('prompt_for', 'author_name'), + grunt.helper('prompt_for', 'author_email'), + grunt.helper('prompt_for', 'author_url'), + grunt.helper('prompt_for', 'node_version'), + grunt.helper('prompt_for', 'node_main'), + grunt.helper('prompt_for', 'node_test') ], function(err, props) { // Files to copy (and process). var files = init.filesToCopy(props); @@ -39,4 +50,5 @@ module.exports = function(init, done) { // All done! done(); }); + }; diff --git a/tasks/init/node/root/README.md b/tasks/init/node/root/README.md index f18dc5535..ba835913d 100644 --- a/tasks/init/node/root/README.md +++ b/tasks/init/node/root/README.md @@ -23,5 +23,5 @@ In lieu of a formal styleguide, take care to maintain the existing coding style. _(Nothing yet)_ ## License -Copyright (c) {%= template.today('yyyy') %} {%= author_name %} +Copyright (c) {%= grunt.template.today('yyyy') %} {%= author_name %} Licensed under the {%= licenses.join(', ') %} license{%= licenses.length === 1 ? '' : 's' %}. diff --git a/tasks/init/node/root/grunt.js b/tasks/init/node/root/grunt.js index 6a61c77af..787a50387 100644 --- a/tasks/init/node/root/grunt.js +++ b/tasks/init/node/root/grunt.js @@ -1,35 +1,49 @@ -/*global config:true, task:true*/ -config.init({ - pkg: '', - test: { - files: ['test/**/*.js'] - }, - lint: { - files: ['grunt.js', 'lib/**/*.js', 'test/**/*.js'] - }, - watch: { - files: '', - tasks: 'default' - }, - jshint: { - options: { - curly: true, - eqeqeq: true, - immed: true, - latedef: true, - newcap: true, - noarg: true, - sub: true, - undef: true, - boss: true, - eqnull: true, - node: true +module.exports = function(grunt) { + // Grunt utilities. + var task = grunt.task; + var file = grunt.file; + var utils = grunt.utils; + var log = grunt.log; + var verbose = grunt.verbose; + var fail = grunt.fail; + var option = grunt.option; + var config = grunt.config; + var template = grunt.template; + + // Initialize config. + grunt.initConfig({ + pkg: '', + test: { + files: ['test/**/*.js'] + }, + lint: { + files: ['grunt.js', 'lib/**/*.js', 'test/**/*.js'] }, - globals: { - exports: true + watch: { + files: '', + tasks: 'default' + }, + jshint: { + options: { + curly: true, + eqeqeq: true, + immed: true, + latedef: true, + newcap: true, + noarg: true, + sub: true, + undef: true, + boss: true, + eqnull: true, + node: true + }, + globals: { + exports: true + } } - } -}); + }); + + // Default task. + grunt.registerTask('default', 'lint test'); -// Default task. -task.registerTask('default', 'lint test'); +}; \ No newline at end of file diff --git a/tasks/init/node/root/lib/name.js b/tasks/init/node/root/lib/name.js index e1b66df90..021388353 100644 --- a/tasks/init/node/root/lib/name.js +++ b/tasks/init/node/root/lib/name.js @@ -2,7 +2,7 @@ * {%= name %} * {%= homepage %} * - * Copyright (c) {%= template.today('yyyy') %} {%= author_name %} + * Copyright (c) {%= grunt.template.today('yyyy') %} {%= author_name %} * Licensed under the {%= licenses.join(', ') %} license{%= licenses.length === 1 ? '' : 's' %}. */ diff --git a/tasks/lint.js b/tasks/lint.js index 2c37af8bb..0e5234493 100644 --- a/tasks/lint.js +++ b/tasks/lint.js @@ -7,140 +7,155 @@ * http://benalman.com/about/license/ */ -var jshint = require('jshint').JSHINT; +module.exports = function(grunt) { + // Grunt utilities. + var task = grunt.task; + var file = grunt.file; + var utils = grunt.utils; + var log = grunt.log; + var verbose = grunt.verbose; + var fail = grunt.fail; + var option = grunt.option; + var config = grunt.config; + var template = grunt.template; -// ============================================================================ -// TASKS -// ============================================================================ + // External libs. + var jshint = require('jshint').JSHINT; -task.registerMultiTask('lint', 'Validate files with JSHint.', function(target) { - // Get flags and globals, allowing target-specific options and globals to - // override the default options and globals. - var options, globals, tmp; + // ========================================================================== + // TASKS + // ========================================================================== - tmp = config(['jshint', target, 'options']); - if (typeof tmp === 'object') { - verbose.writeln('Using "' + target + '" JSHint options.'); - options = tmp; - } else { - verbose.writeln('Using master JSHint options.'); - options = config('jshint.options'); - } - verbose.writeflags(options, 'Options'); + grunt.registerMultiTask('lint', 'Validate files with JSHint.', function(target) { + // Get flags and globals, allowing target-specific options and globals to + // override the default options and globals. + var options, globals, tmp; - tmp = config(['jshint', target, 'globals']); - if (typeof tmp === 'object') { - verbose.writeln('Using "' + target + '" JSHint globals.'); - globals = tmp; - } else { - verbose.writeln('Using master JSHint globals.'); - globals = config('jshint.globals'); - } - verbose.writeflags(globals, 'Globals'); + tmp = config(['jshint', target, 'options']); + if (typeof tmp === 'object') { + verbose.writeln('Using "' + target + '" JSHint options.'); + options = tmp; + } else { + verbose.writeln('Using master JSHint options.'); + options = config('jshint.options'); + } + verbose.writeflags(options, 'Options'); - // Lint specified files. - file.expand(this.file.src).forEach(function(filepath) { - task.helper('lint', file.read(filepath), options, globals, filepath); - }); + tmp = config(['jshint', target, 'globals']); + if (typeof tmp === 'object') { + verbose.writeln('Using "' + target + '" JSHint globals.'); + globals = tmp; + } else { + verbose.writeln('Using master JSHint globals.'); + globals = config('jshint.globals'); + } + verbose.writeflags(globals, 'Globals'); - // Fail task if errors were logged. - if (task.hadErrors()) { return false; } + // Lint specified files. + file.expand(this.file.src).forEach(function(filepath) { + grunt.helper('lint', file.read(filepath), options, globals, filepath); + }); + + // Fail task if errors were logged. + if (task.hadErrors()) { return false; } - // Otherwise, print a success message. - log.writeln('Lint free.'); -}); + // Otherwise, print a success message. + log.writeln('Lint free.'); + }); -// ============================================================================ -// HELPERS -// ============================================================================ + // ========================================================================== + // HELPERS + // ========================================================================== -// No idea why JSHint treats tabs as options.indent # characters wide, but it -// does. See issue: https://github.com/jshint/jshint/issues/430 -function getTabStr(options) { - // Do something that's going to error. - jshint('\tx', options || {}); - // If an error occurred, figure out what character JSHint reported and - // subtract one. - var character = jshint.errors && jshint.errors[0] && jshint.errors[0].character - 1; - // If character is actually a number, use it. Otherwise use 1. - var tabsize = isNaN(character) ? 1 : character; - // If tabsize > 1, return something that should be safe to use as a - // placeholder. \uFFFF repeated 2+ times. - return tabsize > 1 && utils.repeat(tabsize, '\uFFFF'); -} + // No idea why JSHint treats tabs as options.indent # characters wide, but it + // does. See issue: https://github.com/jshint/jshint/issues/430 + function getTabStr(options) { + // Do something that's going to error. + jshint('\tx', options || {}); + // If an error occurred, figure out what character JSHint reported and + // subtract one. + var character = jshint.errors && jshint.errors[0] && jshint.errors[0].character - 1; + // If character is actually a number, use it. Otherwise use 1. + var tabsize = isNaN(character) ? 1 : character; + // If tabsize > 1, return something that should be safe to use as a + // placeholder. \uFFFF repeated 2+ times. + return tabsize > 1 && utils.repeat(tabsize, '\uFFFF'); + } -var tabregex = /\t/g; + var tabregex = /\t/g; -// Lint source code with JSHint. -task.registerHelper('lint', function(src, options, globals, extraMsg) { - // JSHint sometimes modifies objects you pass in, so clone them. - options = utils._.clone(options); - globals = utils._.clone(globals); - // Enable/disable debugging if option explicitly set. - if (option('debug') !== undefined) { - options.devel = options.debug = option('debug'); - // Tweak a few things. - if (option('debug')) { - options.maxerr = Infinity; + // Lint source code with JSHint. + grunt.registerHelper('lint', function(src, options, globals, extraMsg) { + // JSHint sometimes modifies objects you pass in, so clone them. + options = utils._.clone(options); + globals = utils._.clone(globals); + // Enable/disable debugging if option explicitly set. + if (option('debug') !== undefined) { + options.devel = options.debug = option('debug'); + // Tweak a few things. + if (option('debug')) { + options.maxerr = Infinity; + } } - } - var msg = 'Linting' + (extraMsg ? ' ' + extraMsg : '') + '...'; - verbose.write(msg); - // Tab size as reported by JSHint. - var tabstr = getTabStr(options); - var placeholderregex = new RegExp(tabstr, 'g'); - // Lint. - var result = jshint(src, options || {}, globals || {}); - // Attempt to work around JSHint erroneously reporting bugs. - // if (!result) { - // // Filter out errors that shouldn't be reported. - // jshint.errors = jshint.errors.filter(function(o) { - // return o && o.something === 'something'; - // }); - // // If no errors are left, JSHint actually succeeded. - // result = jshint.errors.length === 0; - // } - if (result) { - // Success! - verbose.ok(); - } else { - // Something went wrong. - verbose.or.write(msg); - log.error(); - // Iterate over all errors. - jshint.errors.forEach(function(e) { - // Sometimes there's no error object. - if (!e) { return; } - var pos; - var evidence = e.evidence; - var character = e.character; - if (evidence) { - // Manually increment errorcount since we're not using log.error(). - fail.errorcount++; - // Descriptive code error. - pos = '['.red + ('L' + e.line).yellow + ':'.red + ('C' + character).yellow + ']'.red; - log.writeln(pos + ' ' + e.reason.yellow); - // If necessary, eplace each tab char with something that can be - // swapped out later. - if (tabstr) { - evidence = evidence.replace(tabregex, tabstr); - } - if (character > evidence.length) { - // End of line. - evidence = evidence + ' '.inverse.red; + var msg = 'Linting' + (extraMsg ? ' ' + extraMsg : '') + '...'; + verbose.write(msg); + // Tab size as reported by JSHint. + var tabstr = getTabStr(options); + var placeholderregex = new RegExp(tabstr, 'g'); + // Lint. + var result = jshint(src, options || {}, globals || {}); + // Attempt to work around JSHint erroneously reporting bugs. + // if (!result) { + // // Filter out errors that shouldn't be reported. + // jshint.errors = jshint.errors.filter(function(o) { + // return o && o.something === 'something'; + // }); + // // If no errors are left, JSHint actually succeeded. + // result = jshint.errors.length === 0; + // } + if (result) { + // Success! + verbose.ok(); + } else { + // Something went wrong. + verbose.or.write(msg); + log.error(); + // Iterate over all errors. + jshint.errors.forEach(function(e) { + // Sometimes there's no error object. + if (!e) { return; } + var pos; + var evidence = e.evidence; + var character = e.character; + if (evidence) { + // Manually increment errorcount since we're not using log.error(). + fail.errorcount++; + // Descriptive code error. + pos = '['.red + ('L' + e.line).yellow + ':'.red + ('C' + character).yellow + ']'.red; + log.writeln(pos + ' ' + e.reason.yellow); + // If necessary, eplace each tab char with something that can be + // swapped out later. + if (tabstr) { + evidence = evidence.replace(tabregex, tabstr); + } + if (character > evidence.length) { + // End of line. + evidence = evidence + ' '.inverse.red; + } else { + // Middle of line. + evidence = evidence.slice(0, character - 1) + evidence[character - 1].inverse.red + + evidence.slice(character); + } + // Replace tab placeholder (or tabs) but with a 2-space soft tab. + evidence = evidence.replace(tabstr ? placeholderregex : tabregex, ' '); + log.writeln(evidence); } else { - // Middle of line. - evidence = evidence.slice(0, character - 1) + evidence[character - 1].inverse.red + - evidence.slice(character); + // Generic "Whoops, too many errors" error. + log.error(e.reason); } - // Replace tab placeholder (or tabs) but with a 2-space soft tab. - evidence = evidence.replace(tabstr ? placeholderregex : tabregex, ' '); - log.writeln(evidence); - } else { - // Generic "Whoops, too many errors" error. - log.error(e.reason); - } - }); - log.writeln(); - } -}); + }); + log.writeln(); + } + }); + +}; diff --git a/tasks/min.js b/tasks/min.js index d0ec74a03..bda5fb86c 100644 --- a/tasks/min.js +++ b/tasks/min.js @@ -7,83 +7,98 @@ * http://benalman.com/about/license/ */ -var uglifyjs = require('uglify-js'); -var gzip = require('gzip-js'); +module.exports = function(grunt) { + // Grunt utilities. + var task = grunt.task; + var file = grunt.file; + var utils = grunt.utils; + var log = grunt.log; + var verbose = grunt.verbose; + var fail = grunt.fail; + var option = grunt.option; + var config = grunt.config; + var template = grunt.template; -// ============================================================================ -// TASKS -// ============================================================================ + // External libs. + var uglifyjs = require('uglify-js'); + var gzip = require('gzip-js'); -task.registerMultiTask('min', 'Minify files with UglifyJS.', function(target) { - var files = file.expand(this.file.src); - // 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 = task.directive(files[0], function() { return null; }); - if (banner === null) { - banner = ''; - } else { - files.shift(); - } - // 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); + // ========================================================================== + // TASKS + // ========================================================================== - // Concat banner + minified source. - var min = banner + task.helper('uglify', max, config('uglify')); - file.write(this.file.dest, min); + grunt.registerMultiTask('min', 'Minify files with UglifyJS.', function(target) { + var files = file.expand(this.file.src); + // 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 = task.directive(files[0], function() { return null; }); + if (banner === null) { + banner = ''; + } else { + files.shift(); + } + // Concat specified files. This should really be a single, pre-built (and + // linted) file, but it supports any number of files. + var max = grunt.helper('concat', files); - // Fail task if errors were logged. - if (task.hadErrors()) { return false; } + // Concat banner + minified source. + var min = banner + grunt.helper('uglify', max, config('uglify')); + file.write(this.file.dest, min); - // Otherwise, print a success message.... - log.writeln('File "' + this.file.dest + '" created.'); - // ...and report some size information. - task.helper('min_max_info', min, max); -}); + // Fail task if errors were logged. + if (task.hadErrors()) { return false; } -// ============================================================================ -// HELPERS -// ============================================================================ + // Otherwise, print a success message.... + log.writeln('File "' + this.file.dest + '" created.'); + // ...and report some size information. + grunt.helper('min_max_info', min, max); + }); -// Minify with UglifyJS. -// From https://github.com/mishoo/UglifyJS -task.registerHelper('uglify', function(src, options) { - if (!options) { options = {}; } - var jsp = uglifyjs.parser; - var pro = uglifyjs.uglify; - var ast, pos; - var msg = 'Minifying with UglifyJS...'; - verbose.write(msg); - try { - ast = jsp.parse(src); - ast = pro.ast_mangle(ast, options.mangle || {}); - ast = pro.ast_squeeze(ast, options.squeeze || {}); - src = pro.gen_code(ast, options.codegen || {}); - // Success! - verbose.ok(); - return src; - } catch(e) { - // Something went wrong. - verbose.or.write(msg); - 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.', 10); - } -}); + // ========================================================================== + // HELPERS + // ========================================================================== -// Return gzipped source. -task.registerHelper('gzip', function(src) { - return src ? gzip.zip(src, {}) : ''; -}); + // Minify with UglifyJS. + // From https://github.com/mishoo/UglifyJS + grunt.registerHelper('uglify', function(src, options) { + if (!options) { options = {}; } + var jsp = uglifyjs.parser; + var pro = uglifyjs.uglify; + var ast, pos; + var msg = 'Minifying with UglifyJS...'; + verbose.write(msg); + try { + ast = jsp.parse(src); + ast = pro.ast_mangle(ast, options.mangle || {}); + ast = pro.ast_squeeze(ast, options.squeeze || {}); + src = pro.gen_code(ast, options.codegen || {}); + // Success! + verbose.ok(); + return src; + } catch(e) { + // Something went wrong. + verbose.or.write(msg); + 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.', 10); + } + }); -// Output some size info about a file. -task.registerHelper('min_max_info', function(min, max) { - var gzipSize = String(task.helper('gzip', min).length); - log.writeln('Uncompressed size: ' + String(max.length).green + ' bytes.'); - log.writeln('Compressed size: ' + gzipSize.green + ' bytes gzipped (' + String(min.length).green + ' bytes minified).'); -}); + // Return gzipped source. + grunt.registerHelper('gzip', function(src) { + return src ? gzip.zip(src, {}) : ''; + }); -// Strip /*...*/ comments from source. -task.registerHelper('strip_comments', function(src) { - return src.replace(/\/\*[\s\S]*?\*\/\n*/g, '\n'); -}); + // Output some size info about a file. + grunt.registerHelper('min_max_info', function(min, max) { + var gzipSize = String(grunt.helper('gzip', min).length); + log.writeln('Uncompressed size: ' + String(max.length).green + ' bytes.'); + log.writeln('Compressed size: ' + gzipSize.green + ' bytes gzipped (' + String(min.length).green + ' bytes minified).'); + }); + + // Strip /*...*/ comments from source. + grunt.registerHelper('strip_comments', function(src) { + return src.replace(/\/\*[\s\S]*?\*\/\n*/g, '\n'); + }); + +}; diff --git a/tasks/misc.js b/tasks/misc.js index ff936ee7b..eec1ea137 100644 --- a/tasks/misc.js +++ b/tasks/misc.js @@ -7,51 +7,65 @@ * http://benalman.com/about/license/ */ -// ============================================================================ -// HELPERS -// ============================================================================ - -// Get a config property. Most useful as a directive like . -task.registerHelper('config', config); - -// Read a JSON file. Most useful as a directive like . -var jsons = {}; -task.registerHelper('json', function(filepath) { - // Don't re-fetch if being called as a directive and JSON is already cached. - if (!this.directive || !(filepath in jsons)) { - jsons[filepath] = file.readJson(filepath); - } - return jsons[filepath]; -}); - -// Return the given source coude with any leading banner comment stripped. -task.registerHelper('strip_banner', template.stripBanner); - -// Get a source file's contents with any leading banner comment stripped. -task.registerHelper('file_strip_banner', function(filepath) { - return template.stripBanner(file.read(filepath)); -}); - -// Generate banner from template. -task.registerHelper('banner', function(prop) { - if (!prop) { prop = 'meta.banner'; } - var banner; - var tmpl = config(prop); - if (tmpl) { - // Now, log. - verbose.write('Generating banner...'); - try { - // Compile and run template, using config object as the data source. - banner = template.process(tmpl) + utils.linefeed; - verbose.ok(); - } catch(e) { +module.exports = function(grunt) { + // Grunt utilities. + var task = grunt.task; + var file = grunt.file; + var utils = grunt.utils; + var log = grunt.log; + var verbose = grunt.verbose; + var fail = grunt.fail; + var option = grunt.option; + var config = grunt.config; + var template = grunt.template; + + // ========================================================================== + // HELPERS + // ========================================================================== + + // Get a config property. Most useful as a directive like . + grunt.registerHelper('config', config); + + // Read a JSON file. Most useful as a directive like . + var jsons = {}; + grunt.registerHelper('json', function(filepath) { + // Don't re-fetch if being called as a directive and JSON is already cached. + if (!this.directive || !(filepath in jsons)) { + jsons[filepath] = file.readJson(filepath); + } + return jsons[filepath]; + }); + + // Return the given source coude with any leading banner comment stripped. + grunt.registerHelper('strip_banner', template.stripBanner); + + // Get a source file's contents with any leading banner comment stripped. + grunt.registerHelper('file_strip_banner', function(filepath) { + return template.stripBanner(file.read(filepath)); + }); + + // Generate banner from template. + grunt.registerHelper('banner', function(prop) { + if (!prop) { prop = 'meta.banner'; } + var banner; + var tmpl = config(prop); + if (tmpl) { + // Now, log. + verbose.write('Generating banner...'); + try { + // Compile and run template, using config object as the data source. + banner = template.process(tmpl) + utils.linefeed; + verbose.ok(); + } catch(e) { + banner = ''; + verbose.error(); + fail.warn(e, 11); + } + } else { + fail.warn('No "' + prop + '" banner template defined.', 11); banner = ''; - verbose.error(); - fail.warn(e, 11); } - } else { - fail.warn('No "' + prop + '" banner template defined.', 11); - banner = ''; - } - return banner; -}); + return banner; + }); + +}; diff --git a/tasks/qunit.js b/tasks/qunit.js index 4373233f6..bd507bb04 100644 --- a/tasks/qunit.js +++ b/tasks/qunit.js @@ -7,232 +7,248 @@ * http://benalman.com/about/license/ */ -var fs = require('fs'); -var path = require('path'); -var spawn = require('child_process').spawn; - -var Tempfile = require('temporary/lib/file'); - -// Keep track of the last-started module, test and status. -var currentModule, currentTest, status; -// Keep track of the last-started test(s). -var unfinished = {}; - -// Allow an error message to retain its color when split across multiple lines. -function formatMessage(str) { - return String(str).split('\n').map(function(s) { return s.magenta; }).join('\n'); -} - -// Keep track of failed assertions for pretty-printing. -var failedAssertions = []; -function logFailedAssertions() { - var assertion; - // Print each assertion error. - while (assertion = failedAssertions.shift()) { - verbose.or.error(assertion.testName); - log.error('Message: ' + formatMessage(assertion.message)); - if (assertion.actual !== assertion.expected) { - log.error('Actual: ' + formatMessage(assertion.actual)); - log.error('Expected: ' + formatMessage(assertion.expected)); - } - if (assertion.source) { - log.error(assertion.source.replace(/ {4}(at)/g, ' $1')); - } - log.writeln(); +module.exports = function(grunt) { + // Grunt utilities. + var task = grunt.task; + var file = grunt.file; + var utils = grunt.utils; + var log = grunt.log; + var verbose = grunt.verbose; + var fail = grunt.fail; + var option = grunt.option; + var config = grunt.config; + var template = grunt.template; + + // Nodejs libs. + var fs = require('fs'); + var path = require('path'); + var spawn = require('child_process').spawn; + + // External libs. + var Tempfile = require('temporary/lib/file'); + + // Keep track of the last-started module, test and status. + var currentModule, currentTest, status; + // Keep track of the last-started test(s). + var unfinished = {}; + + // Allow an error message to retain its color when split across multiple lines. + function formatMessage(str) { + return String(str).split('\n').map(function(s) { return s.magenta; }).join('\n'); } -} - -// Handle methods passed from PhantomJS, including QUnit hooks. -var phantomHandlers = { - // QUnit hooks. - moduleStart: function(name) { - unfinished[name] = true; - currentModule = name; - }, - moduleDone: function(name, failed, passed, total) { - delete unfinished[name]; - }, - log: function(result, actual, expected, message, source) { - if (!result) { - failedAssertions.push({ - actual: actual, expected: expected, message: message, source: source, - testName: currentTest - }); - } - }, - testStart: function(name) { - currentTest = (currentModule ? currentModule + ' - ' : '') + name; - verbose.write(currentTest + '...'); - }, - testDone: function(name, failed, passed, total) { - // Log errors if necessary, otherwise success. - if (failed > 0) { - // list assertions - if (option('verbose')) { - log.error(); - logFailedAssertions(); - } else { - log.write('F'.red); + + // Keep track of failed assertions for pretty-printing. + var failedAssertions = []; + function logFailedAssertions() { + var assertion; + // Print each assertion error. + while (assertion = failedAssertions.shift()) { + verbose.or.error(assertion.testName); + log.error('Message: ' + formatMessage(assertion.message)); + if (assertion.actual !== assertion.expected) { + log.error('Actual: ' + formatMessage(assertion.actual)); + log.error('Expected: ' + formatMessage(assertion.expected)); + } + if (assertion.source) { + log.error(assertion.source.replace(/ {4}(at)/g, ' $1')); } - } else { - verbose.ok().or.write('.'); + log.writeln(); } - }, - done: function(failed, passed, total, duration) { - status.failed += failed; - status.passed += passed; - status.total += total; - status.duration += duration; - // Print assertion errors here, if verbose mode is disabled. - if (!option('verbose')) { + } + + // Handle methods passed from PhantomJS, including QUnit hooks. + var phantomHandlers = { + // QUnit hooks. + moduleStart: function(name) { + unfinished[name] = true; + currentModule = name; + }, + moduleDone: function(name, failed, passed, total) { + delete unfinished[name]; + }, + log: function(result, actual, expected, message, source) { + if (!result) { + failedAssertions.push({ + actual: actual, expected: expected, message: message, source: source, + testName: currentTest + }); + } + }, + testStart: function(name) { + currentTest = (currentModule ? currentModule + ' - ' : '') + name; + verbose.write(currentTest + '...'); + }, + testDone: function(name, failed, passed, total) { + // Log errors if necessary, otherwise success. if (failed > 0) { - log.writeln(); - logFailedAssertions(); + // list assertions + if (option('verbose')) { + log.error(); + logFailedAssertions(); + } else { + log.write('F'.red); + } } else { - log.ok(); + verbose.ok().or.write('.'); + } + }, + done: function(failed, passed, total, duration) { + status.failed += failed; + status.passed += passed; + status.total += total; + status.duration += duration; + // Print assertion errors here, if verbose mode is disabled. + if (!option('verbose')) { + if (failed > 0) { + log.writeln(); + logFailedAssertions(); + } else { + log.ok(); + } + } + }, + // Error handlers. + done_fail: function(url) { + verbose.write('Running PhantomJS...').or.write('...'); + log.error(); + fail.warn('PhantomJS unable to load "' + url + '" URI.', 90); + }, + done_timeout: function() { + log.writeln(); + fail.warn('PhantomJS timed out, possibly due to a missing QUnit start() call.', 90); + }, + // console.log pass-through. + console: console.log.bind(console), + // Debugging messages. + debug: log.debug.bind(log, 'phantomjs') + }; + + // ========================================================================== + // TASKS + // ========================================================================== + + grunt.registerMultiTask('qunit', 'Run QUnit unit tests in a headless PhantomJS instance.', function(target) { + // Get files as URLs. + var urls = file.expandToUrls(this.file.src); + + // This task is asynchronous. + var done = this.async(); + + // Reset status. + status = {failed: 0, passed: 0, total: 0, duration: 0}; + + // Process each filepath in-order. + utils.async.forEachSeries(urls, function(url, next) { + var basename = path.basename(url); + verbose.subhead('Testing ' + basename).or.write('Testing ' + basename); + + // Create temporary file to be used for grunt-phantom communication. + var tempfile = new Tempfile(); + // Timeout ID. + var id; + // The number of tempfile lines already read. + var n = 0; + + // Reset current module. + currentModule = null; + + // Clean up. + function cleanup() { + clearTimeout(id); + tempfile.unlink(); } - } - }, - // Error handlers. - done_fail: function(url) { - verbose.write('Running PhantomJS...').or.write('...'); - log.error(); - fail.warn('PhantomJS unable to load "' + url + '" URI.', 90); - }, - done_timeout: function() { - log.writeln(); - fail.warn('PhantomJS timed out, possibly due to a missing QUnit start() call.', 90); - }, - // console.log pass-through. - console: console.log.bind(console), - // Debugging messages. - debug: log.debug.bind(log, 'phantomjs') -}; - -// ============================================================================ -// TASKS -// ============================================================================ - -task.registerMultiTask('qunit', 'Run QUnit unit tests in a headless PhantomJS instance.', function(target) { - // Get files as URLs. - var urls = file.expandToUrls(this.file.src); - - // This task is asynchronous. - var done = this.async(); - - // Reset status. - status = {failed: 0, passed: 0, total: 0, duration: 0}; - - // Process each filepath in-order. - utils.async.forEachSeries(urls, function(url, next) { - var basename = path.basename(url); - verbose.subhead('Testing ' + basename).or.write('Testing ' + basename); - - // Create temporary file to be used for grunt-phantom communication. - var tempfile = new Tempfile(); - // Timeout ID. - var id; - // The number of tempfile lines already read. - var n = 0; - - // Reset current module. - currentModule = null; - - // Clean up. - function cleanup() { - clearTimeout(id); - tempfile.unlink(); - } - // It's simple. As QUnit tests, assertions and modules begin and complete, - // the results are written as JSON to a temporary file. This polling loop - // checks that file for new lines, and for each one parses its JSON and - // executes the corresponding method with the specified arguments. - (function loopy() { - // Disable logging temporarily. - log.muted = true; - // Read the file, splitting lines on \n, and removing a trailing line. - var lines = file.read(tempfile.path).split('\n').slice(0, -1); - // Re-enable logging. - log.muted = false; - // Iterate over all lines that haven't already been processed. - var done = lines.slice(n).some(function(line) { - // Get args and method. - var args = JSON.parse(line); - var method = args.shift(); - // Execute method if it exists. - if (phantomHandlers[method]) { - phantomHandlers[method].apply(null, args); + // It's simple. As QUnit tests, assertions and modules begin and complete, + // the results are written as JSON to a temporary file. This polling loop + // checks that file for new lines, and for each one parses its JSON and + // executes the corresponding method with the specified arguments. + (function loopy() { + // Disable logging temporarily. + log.muted = true; + // Read the file, splitting lines on \n, and removing a trailing line. + var lines = file.read(tempfile.path).split('\n').slice(0, -1); + // Re-enable logging. + log.muted = false; + // Iterate over all lines that haven't already been processed. + var done = lines.slice(n).some(function(line) { + // Get args and method. + var args = JSON.parse(line); + var method = args.shift(); + // Execute method if it exists. + if (phantomHandlers[method]) { + phantomHandlers[method].apply(null, args); + } + // If the method name started with test, return true. Because the + // Array#some method was used, this not only sets "done" to true, + // but stops further iteration from occurring. + return (/^done/).test(method); + }); + + if (done) { + // All done. + cleanup(); + next(); + } else { + // Update n so previously processed lines are ignored. + n = lines.length; + // Check back in a little bit. + id = setTimeout(loopy, 100); + } + }()); + + // Launch PhantomJS. + var args = [ + // The main script file. + file.taskfile('qunit/phantom.js'), + // The temporary file used for communications. + tempfile.path, + // The QUnit helper file to be injected. + file.taskfile('qunit/qunit.js'), + // URL to the QUnit .html test file to run. + url, + // PhantomJS options. + '--config=' + file.taskfile('qunit/phantom.json') + ]; + + utils.spawn({ + cmd: 'phantomjs', + args: args + }, function(err, result, code) { + if (!err) { return; } + // Something went horribly wrong. + cleanup(); + verbose.or.writeln(); + log.write('Running PhantomJS...').error(); + if (code === 127) { + log.error( + 'In order for the qunit task to work properly, PhantomJS must be installed and\n' + + 'in the system PATH (if you can run "phantomjs" at the command line, this task\n' + + 'should work). Unfortunately, PhantomJS cannot be installed automatically via\n' + + 'npm or grunt. See the grunt qunit task documentation for more instructions at\n' + + 'https://github.com/cowboy/grunt/blob/master/docs/task_qunit.md' + ); + fail.warn('PhantomJS not found.', 90); + } else { + result.split('\n').forEach(log.error, log); + fail.warn('PhantomJS exited unexpectedly with exit code ' + code + '.', 90); } - // If the method name started with test, return true. Because the - // Array#some method was used, this not only sets "done" to true, - // but stops further iteration from occurring. - return (/^done/).test(method); + done(); }); + }, function(err) { + // All tests have been run. - if (done) { - // All done. - cleanup(); - next(); - } else { - // Update n so previously processed lines are ignored. - n = lines.length; - // Check back in a little bit. - id = setTimeout(loopy, 100); - } - }()); - - // Launch PhantomJS. - var args = [ - // The main script file. - file.taskfile('qunit/phantom.js'), - // The temporary file used for communications. - tempfile.path, - // The QUnit helper file to be injected. - file.taskfile('qunit/qunit.js'), - // URL to the QUnit .html test file to run. - url, - // PhantomJS options. - '--config=' + file.taskfile('qunit/phantom.json') - ]; - - utils.spawn({ - cmd: 'phantomjs', - args: args - }, function(err, result, code) { - if (!err) { return; } - // Something went horribly wrong. - cleanup(); - verbose.or.writeln(); - log.write('Running PhantomJS...').error(); - if (code === 127) { - log.error( - 'In order for the qunit task to work properly, PhantomJS must be installed and\n' + - 'in the system PATH (if you can run "phantomjs" at the command line, this task\n' + - 'should work). Unfortunately, PhantomJS cannot be installed automatically via\n' + - 'npm or grunt. See the grunt qunit task documentation for more instructions at\n' + - 'https://github.com/cowboy/grunt/blob/master/docs/task_qunit.md' - ); - fail.warn('PhantomJS not found.', 90); + // Log results. + if (status.failed > 0) { + fail.warn(status.failed + '/' + status.total + ' assertions failed (' + + status.duration + 'ms)', Math.min(99, 90 + status.failed)); } else { - result.split('\n').forEach(log.error, log); - fail.warn('PhantomJS exited unexpectedly with exit code ' + code + '.', 90); + verbose.writeln(); + log.ok(status.total + ' assertions passed (' + status.duration + 'ms)'); } + + // All done! done(); }); - }, function(err) { - // All tests have been run. - - // Log results. - if (status.failed > 0) { - fail.warn(status.failed + '/' + status.total + ' assertions failed (' + - status.duration + 'ms)', Math.min(99, 90 + status.failed)); - } else { - verbose.writeln(); - log.ok(status.total + ' assertions passed (' + status.duration + 'ms)'); - } - - // All done! - done(); }); -}); + +}; diff --git a/tasks/server.js b/tasks/server.js index 78be22bcc..f42787964 100644 --- a/tasks/server.js +++ b/tasks/server.js @@ -7,34 +7,50 @@ * http://benalman.com/about/license/ */ -var path = require('path'); - -var connect = require('connect'); - -// ============================================================================ -// TASKS -// ============================================================================ - -task.registerTask('server', 'Start a static web server.', function() { - // Get values from config, or use defaults. - var port = config('server.port') || 8000; - var base = path.resolve(config('server.base') || '.'); - - var middleware = [ - // Serve static files. - connect.static(base), - // Make empty directories browsable. (overkill?) - connect.directory(base) - ]; - - // If --debug was specified, enable logging. - if (option('debug')) { - connect.logger.format('grunt', ('[D] server :method :url :status ' + - ':res[content-length] - :response-time ms').magenta); - middleware.unshift(connect.logger('grunt')); - } - - // Start server. - log.writeln('Starting static web server on port ' + port + '.'); - connect.apply(null, middleware).listen(port); -}); + module.exports = function(grunt) { + // Grunt utilities. + var task = grunt.task; + var file = grunt.file; + var utils = grunt.utils; + var log = grunt.log; + var verbose = grunt.verbose; + var fail = grunt.fail; + var option = grunt.option; + var config = grunt.config; + var template = grunt.template; + + // Nodejs libs. + var path = require('path'); + + // External libs. + var connect = require('connect'); + + // ========================================================================== + // TASKS + // ========================================================================== + + grunt.registerTask('server', 'Start a static web server.', function() { + // Get values from config, or use defaults. + var port = config('server.port') || 8000; + var base = path.resolve(config('server.base') || '.'); + + var middleware = [ + // Serve static files. + connect.static(base), + // Make empty directories browsable. (overkill?) + connect.directory(base) + ]; + + // If --debug was specified, enable logging. + if (option('debug')) { + connect.logger.format('grunt', ('[D] server :method :url :status ' + + ':res[content-length] - :response-time ms').magenta); + middleware.unshift(connect.logger('grunt')); + } + + // Start server. + log.writeln('Starting static web server on port ' + port + '.'); + connect.apply(null, middleware).listen(port); + }); + + }; diff --git a/tasks/test.js b/tasks/test.js index 207f2b8c3..22f1b60d9 100644 --- a/tasks/test.js +++ b/tasks/test.js @@ -7,147 +7,164 @@ * http://benalman.com/about/license/ */ -var path = require('path'); -var nodeunit = require('nodeunit'); -var nodeunitUtils = require('nodeunit/lib/utils'); + module.exports = function(grunt) { + // Grunt utilities. + var task = grunt.task; + var file = grunt.file; + var utils = grunt.utils; + var log = grunt.log; + var verbose = grunt.verbose; + var fail = grunt.fail; + var option = grunt.option; + var config = grunt.config; + var template = grunt.template; -// ============================================================================ -// CUSTOM NODEUNIT REPORTER -// ============================================================================ + // Nodejs libs. + var path = require('path'); -// Keep track of the last-started module. -var currentModule; -// Keep track of the last-started test(s). -var unfinished = {}; + // External libs. + var nodeunit = require('nodeunit'); + var nodeunitUtils = require('nodeunit/lib/utils'); -// If Nodeunit explodes because a test was missing test.done(), handle it. -process.on('exit', function() { - var len = Object.keys(unfinished).length; - // If there are unfinished tests, tell the user why Nodeunit killed grunt. - if (len > 0) { - log.muted = false; - verbose.error().or.writeln('F'.red); - log.error('Incomplete tests/setups/teardowns:'); - Object.keys(unfinished).forEach(log.error, log); - fail.fatal('A test was missing test.done(), so nodeunit exploded. Sorry!', - Math.min(99, 90 + len)); - } -}); + // ========================================================================== + // CUSTOM NODEUNIT REPORTER + // ========================================================================== + + // Keep track of the last-started module. + var currentModule; + // Keep track of the last-started test(s). + var unfinished = {}; + + // If Nodeunit explodes because a test was missing test.done(), handle it. + process.on('exit', function() { + var len = Object.keys(unfinished).length; + // If there are unfinished tests, tell the user why Nodeunit killed grunt. + if (len > 0) { + log.muted = false; + verbose.error().or.writeln('F'.red); + log.error('Incomplete tests/setups/teardowns:'); + Object.keys(unfinished).forEach(log.error, log); + fail.fatal('A test was missing test.done(), so nodeunit exploded. Sorry!', + Math.min(99, 90 + len)); + } + }); -// Keep track of failed assertions for pretty-printing. -var failedAssertions = []; -function logFailedAssertions() { - var assertion, stack; - // Print each assertion error + stack. - while (assertion = failedAssertions.shift()) { - nodeunitUtils.betterErrors(assertion); - verbose.or.error(assertion.testName); - if (assertion.error.name === 'AssertionError' && assertion.message) { - log.error('AssertionMessage: ' + assertion.message.magenta); + // Keep track of failed assertions for pretty-printing. + var failedAssertions = []; + function logFailedAssertions() { + var assertion, stack; + // Print each assertion error + stack. + while (assertion = failedAssertions.shift()) { + nodeunitUtils.betterErrors(assertion); + verbose.or.error(assertion.testName); + if (assertion.error.name === 'AssertionError' && assertion.message) { + log.error('AssertionMessage: ' + assertion.message.magenta); + } + stack = assertion.error.stack.replace(/ {4}(at)/g, ' $1'); + stack = stack.replace(/:(.*?\n)/, '$1'.magenta); + log.error(stack + '\n').writeln(); } - stack = assertion.error.stack.replace(/ {4}(at)/g, ' $1'); - stack = stack.replace(/:(.*?\n)/, '$1'.magenta); - log.error(stack + '\n').writeln(); } -} -// Define our own Nodeunit reporter. -nodeunit.reporters.grunt = { - info: 'Grunt reporter', - run: function(files, options, callback) { - var opts = { - // No idea. - testspec: undefined, - // Executed when the first test in a file is run. If no tests exist in - // the file, this doesn't execute. - moduleStart: function(name) { - // Keep track of this so that moduleDone output can be suppressed in - // cases where a test file contains no tests. - currentModule = name; - verbose.subhead('Testing ' + name).or.write('Testing ' + name); - }, - // Executed after a file is done being processed. This executes whether - // tests exist in the file or not. - moduleDone: function(name) { - // Abort if no tests actually ran. - if (name !== currentModule) { return; } - // Print assertion errors here, if verbose mode is disabled. - if (!option('verbose')) { - if (failedAssertions.length > 0) { - log.writeln(); - logFailedAssertions(); - } else { - log.ok(); + // Define our own Nodeunit reporter. + nodeunit.reporters.grunt = { + info: 'Grunt reporter', + run: function(files, options, callback) { + var opts = { + // No idea. + testspec: undefined, + // Executed when the first test in a file is run. If no tests exist in + // the file, this doesn't execute. + moduleStart: function(name) { + // Keep track of this so that moduleDone output can be suppressed in + // cases where a test file contains no tests. + currentModule = name; + verbose.subhead('Testing ' + name).or.write('Testing ' + name); + }, + // Executed after a file is done being processed. This executes whether + // tests exist in the file or not. + moduleDone: function(name) { + // Abort if no tests actually ran. + if (name !== currentModule) { return; } + // Print assertion errors here, if verbose mode is disabled. + if (!option('verbose')) { + if (failedAssertions.length > 0) { + log.writeln(); + logFailedAssertions(); + } else { + log.ok(); + } } - } - }, - // Executed before each test is run. - testStart: function(name) { - // Keep track of the current test, in case test.done() was omitted - // and Nodeunit explodes. - unfinished[name] = name; - verbose.write(name + '...'); - // Mute output, in cases where a function being tested logs through - // grunt (for testing grunt internals). - log.muted = true; - }, - // Executed after each test and all its assertions are run. - testDone: function(name, assertions) { - delete unfinished[name]; - // Un-mute output. - log.muted = false; - // Log errors if necessary, otherwise success. - if (assertions.failures()) { - assertions.forEach(function(ass) { - if (ass.failed()) { - ass.testName = name; - failedAssertions.push(ass); + }, + // Executed before each test is run. + testStart: function(name) { + // Keep track of the current test, in case test.done() was omitted + // and Nodeunit explodes. + unfinished[name] = name; + verbose.write(name + '...'); + // Mute output, in cases where a function being tested logs through + // grunt (for testing grunt internals). + log.muted = true; + }, + // Executed after each test and all its assertions are run. + testDone: function(name, assertions) { + delete unfinished[name]; + // Un-mute output. + log.muted = false; + // Log errors if necessary, otherwise success. + if (assertions.failures()) { + assertions.forEach(function(ass) { + if (ass.failed()) { + ass.testName = name; + failedAssertions.push(ass); + } + }); + if (option('verbose')) { + log.error(); + logFailedAssertions(); + } else { + log.write('F'.red); } - }); - if (option('verbose')) { - log.error(); - logFailedAssertions(); } else { - log.write('F'.red); + verbose.ok().or.write('.'); } - } else { - verbose.ok().or.write('.'); - } - }, - // Executed when everything is all done. - done: function (assertions) { - if (assertions.failures()) { - fail.warn(assertions.failures() + '/' + assertions.length + - ' assertions failed (' + assertions.duration + 'ms)', - Math.min(99, 90 + assertions.failures())); - } else { - verbose.writeln(); - log.ok(assertions.length + ' assertions passed (' + - assertions.duration + 'ms)'); + }, + // Executed when everything is all done. + done: function (assertions) { + if (assertions.failures()) { + fail.warn(assertions.failures() + '/' + assertions.length + + ' assertions failed (' + assertions.duration + 'ms)', + Math.min(99, 90 + assertions.failures())); + } else { + verbose.writeln(); + log.ok(assertions.length + ' assertions passed (' + + assertions.duration + 'ms)'); + } + // Tell the task manager we're all done. + callback(); // callback(assertions.failures() === 0); } - // Tell the task manager we're all done. - callback(); // callback(assertions.failures() === 0); - } - }; + }; - // Nodeunit needs absolute paths. - var paths = files.map(function(filepath) { - return path.resolve(filepath); - }); - nodeunit.runFiles(paths, opts); - } -}; + // Nodeunit needs absolute paths. + var paths = files.map(function(filepath) { + return path.resolve(filepath); + }); + nodeunit.runFiles(paths, opts); + } + }; + + // ========================================================================== + // TASKS + // ========================================================================== -// ============================================================================ -// TASKS -// ============================================================================ + grunt.registerMultiTask('test', 'Run unit tests with nodeunit.', function(target) { + // File paths. + var filepaths = file.expand(this.file.src); + // Clear all tests' cached require data, in case this task is run inside a + // "watch" task loop. + file.clearRequireCache(filepaths); + // Run test(s)... asynchronously! + nodeunit.reporters.grunt.run(filepaths, {}, this.async()); + }); -task.registerMultiTask('test', 'Run unit tests with nodeunit.', function(target) { - // File paths. - var filepaths = file.expand(this.file.src); - // Clear all tests' cached require data, in case this task is run inside a - // "watch" task loop. - file.clearRequireCache(filepaths); - // Run test(s)... asynchronously! - nodeunit.reporters.grunt.run(filepaths, {}, this.async()); -}); + }; diff --git a/tasks/watch.js b/tasks/watch.js index cb06299d3..12174fede 100644 --- a/tasks/watch.js +++ b/tasks/watch.js @@ -7,138 +7,153 @@ * http://benalman.com/about/license/ */ -var fs = require('fs'); -var path = require('path'); - -// ============================================================================ -// TASKS -// ============================================================================ - -// Keep track of last modified times of files, in case files are reported to -// have changed incorrectly. -var mtimes = {}; - -task.registerTask('watch', 'Run predefined tasks whenever watched files change.', function(prop) { - - var props = ['watch']; - // If a prop was passed as the argument, use that sub-property of watch. - if (prop) { props.push(prop); } - // Get the files and tasks sub-properties. - var filesProp = props.concat('files'); - var tasksProp = props.concat('tasks'); - - // Fail if any required config properties have been omitted. - config.requires(filesProp, tasksProp); - - log.write('Waiting...'); - - // This task is asynchronous. - var taskDone = this.async(); - // Get a list of ffles to be watched. - var getFiles = file.expand.bind(file, config(filesProp)); - // The tasks to be run. - var tasks = config(tasksProp); - // This task's name + optional args, in string format. - var nameArgs = this.nameArgs; - // An ID by which the setInterval can be canceled. - var intervalId; - // Files that are being watched. - var watchedFiles = {}; - // File changes to be logged. - var changedFiles = {}; - - // Define an alternate fail "warn" behavior. - fail.warnAlternate = function() { - task.clearQueue().run(nameArgs); - }; - - // Cleanup when files have changed. This is debounced to handle situations - // where editors save multiple files "simultaneously" and should wait until - // all the files are saved. - var done = utils._.debounce(function() { - // Clear the files-added setInterval. - clearInterval(intervalId); - // Ok! - log.ok(); - Object.keys(changedFiles).forEach(function(filepath) { - // Log which file has changed, and how. - log.ok('File "' + filepath + '" ' + changedFiles[filepath] + '.'); - // Clear the modified file's cached require data. - file.clearRequireCache(filepath); - }); - // Unwatch all watched files. - Object.keys(watchedFiles).forEach(unWatchFile); - // Enqueue all specified tasks (if specified)... - if (tasks) { task.run(tasks); } - // ...followed by the watch task, so that it loops. - task.run(nameArgs); - // Continue task queue. - taskDone(); - }, 250); - - // Handle file changes. - function fileChanged(status, filepath) { - // If file was deleted and then re-added, consider it changed. - if (changedFiles[filepath] === 'deleted' && status === 'added') { - status = 'changed'; - } - // Keep track of changed status for later. - changedFiles[filepath] = status; - // Execute debounced done function. - done(); - } - - // Watch a file. - function watchFile(filepath) { - if (!watchedFiles[filepath]) { - // Watch this file for changes. This probably won't scale to hundreds of - // files.. but I bet someone will try it! - watchedFiles[filepath] = fs.watch(filepath, function(event) { - var mtime; - // Has the file been deleted? - var deleted = !path.existsSync(filepath); - if (deleted) { - // If file was deleted, stop watching file. - unWatchFile(filepath); - // Remove from mtimes. - delete mtimes[filepath]; - } else { - // Get last modified time of file. - mtime = +fs.statSync(filepath).mtime; - // If same as stored mtime, the file hasn't changed. - if (mtime === mtimes[filepath]) { return; } - // Otherwise it has, store mtime for later use. - mtimes[filepath] = mtime; - } - // Call "change" for this file, setting status appropriately (rename -> - // renamed, change -> changed). - fileChanged(deleted ? 'deleted' : event + 'd', filepath); + module.exports = function(grunt) { + // Grunt utilities. + var task = grunt.task; + var file = grunt.file; + var utils = grunt.utils; + var log = grunt.log; + var verbose = grunt.verbose; + var fail = grunt.fail; + var option = grunt.option; + var config = grunt.config; + var template = grunt.template; + + // Nodejs libs. + var fs = require('fs'); + var path = require('path'); + + // ========================================================================== + // TASKS + // ========================================================================== + + // Keep track of last modified times of files, in case files are reported to + // have changed incorrectly. + var mtimes = {}; + + grunt.registerTask('watch', 'Run predefined tasks whenever watched files change.', function(prop) { + + var props = ['watch']; + // If a prop was passed as the argument, use that sub-property of watch. + if (prop) { props.push(prop); } + // Get the files and tasks sub-properties. + var filesProp = props.concat('files'); + var tasksProp = props.concat('tasks'); + + // Fail if any required config properties have been omitted. + config.requires(filesProp, tasksProp); + + log.write('Waiting...'); + + // This task is asynchronous. + var taskDone = this.async(); + // Get a list of ffles to be watched. + var getFiles = file.expand.bind(file, config(filesProp)); + // The tasks to be run. + var tasks = config(tasksProp); + // This task's name + optional args, in string format. + var nameArgs = this.nameArgs; + // An ID by which the setInterval can be canceled. + var intervalId; + // Files that are being watched. + var watchedFiles = {}; + // File changes to be logged. + var changedFiles = {}; + + // Define an alternate fail "warn" behavior. + fail.warnAlternate = function() { + task.clearQueue().run(nameArgs); + }; + + // Cleanup when files have changed. This is debounced to handle situations + // where editors save multiple files "simultaneously" and should wait until + // all the files are saved. + var done = utils._.debounce(function() { + // Clear the files-added setInterval. + clearInterval(intervalId); + // Ok! + log.ok(); + Object.keys(changedFiles).forEach(function(filepath) { + // Log which file has changed, and how. + log.ok('File "' + filepath + '" ' + changedFiles[filepath] + '.'); + // Clear the modified file's cached require data. + file.clearRequireCache(filepath); }); + // Unwatch all watched files. + Object.keys(watchedFiles).forEach(unWatchFile); + // Enqueue all specified tasks (if specified)... + if (tasks) { task.run(tasks); } + // ...followed by the watch task, so that it loops. + task.run(nameArgs); + // Continue task queue. + taskDone(); + }, 250); + + // Handle file changes. + function fileChanged(status, filepath) { + // If file was deleted and then re-added, consider it changed. + if (changedFiles[filepath] === 'deleted' && status === 'added') { + status = 'changed'; + } + // Keep track of changed status for later. + changedFiles[filepath] = status; + // Execute debounced done function. + done(); } - } - - // Unwatch a file. - function unWatchFile(filepath) { - if (watchedFiles[filepath]) { - // Close watcher. - watchedFiles[filepath].close(); - // Remove from watched files. - delete watchedFiles[filepath]; + + // Watch a file. + function watchFile(filepath) { + if (!watchedFiles[filepath]) { + // Watch this file for changes. This probably won't scale to hundreds of + // files.. but I bet someone will try it! + watchedFiles[filepath] = fs.watch(filepath, function(event) { + var mtime; + // Has the file been deleted? + var deleted = !path.existsSync(filepath); + if (deleted) { + // If file was deleted, stop watching file. + unWatchFile(filepath); + // Remove from mtimes. + delete mtimes[filepath]; + } else { + // Get last modified time of file. + mtime = +fs.statSync(filepath).mtime; + // If same as stored mtime, the file hasn't changed. + if (mtime === mtimes[filepath]) { return; } + // Otherwise it has, store mtime for later use. + mtimes[filepath] = mtime; + } + // Call "change" for this file, setting status appropriately (rename -> + // renamed, change -> changed). + fileChanged(deleted ? 'deleted' : event + 'd', filepath); + }); + } + } + + // Unwatch a file. + function unWatchFile(filepath) { + if (watchedFiles[filepath]) { + // Close watcher. + watchedFiles[filepath].close(); + // Remove from watched files. + delete watchedFiles[filepath]; + } } - } - - // Watch all currently existing files for changes. - getFiles().forEach(watchFile); - - // Watch for files to be added. - intervalId = setInterval(function() { - // Files that have been added since last interval execution. - var added = utils._.difference(getFiles(), Object.keys(watchedFiles)); - added.forEach(function(filepath) { - // This file has been added. - fileChanged('added', filepath); - // Watch this file. - watchFile(filepath); - }); - }, 200); -}); + + // Watch all currently existing files for changes. + getFiles().forEach(watchFile); + + // Watch for files to be added. + intervalId = setInterval(function() { + // Files that have been added since last interval execution. + var added = utils._.difference(getFiles(), Object.keys(watchedFiles)); + added.forEach(function(filepath) { + // This file has been added. + fileChanged('added', filepath); + // Watch this file. + watchFile(filepath); + }); + }, 200); + }); + + }; diff --git a/test/grunt/file_test.js b/test/grunt/file_test.js index 7b113137e..a1e0a0186 100644 --- a/test/grunt/file_test.js +++ b/test/grunt/file_test.js @@ -1,3 +1,8 @@ +var grunt = require('../../lib/grunt'); +var file = grunt.file; +var template = grunt.template; +var utils = grunt.utils; + var fs = require('fs'); var path = require('path'); diff --git a/test/grunt/utils_test.js b/test/grunt/utils_test.js index 5def6a3ab..545f1821b 100644 --- a/test/grunt/utils_test.js +++ b/test/grunt/utils_test.js @@ -1,3 +1,6 @@ +var grunt = require('../../lib/grunt'); +var utils = grunt.utils; + exports['utils'] = { 'linefeed': function(test) { test.expect(1); diff --git a/test/tasks/concat_test.js b/test/tasks/concat_test.js index 5e9070a3e..7f997667a 100644 --- a/test/tasks/concat_test.js +++ b/test/tasks/concat_test.js @@ -1,12 +1,15 @@ +var grunt = require('../../lib/grunt'); +var utils = grunt.utils; + exports['concat'] = function(test) { test.expect(1); - task.registerHelper('test_helper', function(a, b) { return a + b; }); + grunt.registerHelper('test_helper', function(a, b) { return a + b; }); var files = [ 'test/fixtures/a.js', '', 'test/fixtures/b.js' ]; var lf = utils.linefeed; - test.equal(task.helper('concat', files), 'var a = 1;\n' + lf + 'xy' + lf + 'var b = 2;\n', 'It should concatenate files and directives.'); + test.equal(grunt.helper('concat', files), 'var a = 1;\n' + lf + 'xy' + lf + 'var b = 2;\n', 'It should concatenate files and directives.'); test.done(); }; diff --git a/test/tasks/init_test.js b/test/tasks/init_test.js index 783fc28f9..dfae3e853 100644 --- a/test/tasks/init_test.js +++ b/test/tasks/init_test.js @@ -1,34 +1,36 @@ +var grunt = require('../../lib/grunt'); + exports['github_web_url'] = { 'no args': function(test) { test.expect(1); - test.equal(task.helper('github_web_url'), null, 'It should return null.'); + test.equal(grunt.helper('github_web_url'), null, 'It should return null.'); test.done(); }, 'nonsensical args': function(test) { test.expect(3); - test.equal(task.helper('github_web_url', ''), null, 'It should return null.'); - test.equal(task.helper('github_web_url', 'omgfoo'), null, 'It should return null.'); - test.equal(task.helper('github_web_url', 'http://benalman.com/'), null, 'It should return null.'); + test.equal(grunt.helper('github_web_url', ''), null, 'It should return null.'); + test.equal(grunt.helper('github_web_url', 'omgfoo'), null, 'It should return null.'); + test.equal(grunt.helper('github_web_url', 'http://benalman.com/'), null, 'It should return null.'); test.done(); }, 'no suffix': function(test) { test.expect(5); - test.equal(task.helper('github_web_url', 'git@github.com:cowboy/grunt.git'), 'https://github.com/cowboy/grunt', 'It should convert the URI.'); - test.equal(task.helper('github_web_url', 'https://cowboy@github.com/cowboy/grunt.git'), 'https://github.com/cowboy/grunt', 'It should convert the URI.'); - test.equal(task.helper('github_web_url', 'git://github.com/cowboy/grunt.git'), 'https://github.com/cowboy/grunt', 'It should convert the URI.'); - test.equal(task.helper('github_web_url', 'http://github.com/paulirish/newsite'), 'https://github.com/paulirish/newsite', 'It should convert the URI.'); - test.equal(task.helper('github_web_url', 'http://github.com/paulirish/newsite/'), 'https://github.com/paulirish/newsite', 'It should convert the URI.'); + test.equal(grunt.helper('github_web_url', 'git@github.com:cowboy/grunt.git'), 'https://github.com/cowboy/grunt', 'It should convert the URI.'); + test.equal(grunt.helper('github_web_url', 'https://cowboy@github.com/cowboy/grunt.git'), 'https://github.com/cowboy/grunt', 'It should convert the URI.'); + test.equal(grunt.helper('github_web_url', 'git://github.com/cowboy/grunt.git'), 'https://github.com/cowboy/grunt', 'It should convert the URI.'); + test.equal(grunt.helper('github_web_url', 'http://github.com/paulirish/newsite'), 'https://github.com/paulirish/newsite', 'It should convert the URI.'); + test.equal(grunt.helper('github_web_url', 'http://github.com/paulirish/newsite/'), 'https://github.com/paulirish/newsite', 'It should convert the URI.'); test.done(); }, 'suffix': function(test) { test.expect(7); - test.equal(task.helper('github_web_url', 'git@github.com:cowboy/grunt.git', 'issues'), 'https://github.com/cowboy/grunt/issues', 'It should convert the URI.'); - test.equal(task.helper('github_web_url', 'https://cowboy@github.com/cowboy/grunt.git', 'issues'), 'https://github.com/cowboy/grunt/issues', 'It should convert the URI.'); - test.equal(task.helper('github_web_url', 'git://github.com/cowboy/grunt.git', 'issues'), 'https://github.com/cowboy/grunt/issues', 'It should convert the URI.'); - test.equal(task.helper('github_web_url', 'http://github.com/paulirish/newsite', 'issues'), 'https://github.com/paulirish/newsite/issues', 'It should convert the URI.'); - test.equal(task.helper('github_web_url', 'http://github.com/paulirish/newsite/', 'issues'), 'https://github.com/paulirish/newsite/issues', 'It should convert the URI.'); - test.equal(task.helper('github_web_url', 'http://github.com/paulirish/newsite', '/issues'), 'https://github.com/paulirish/newsite/issues', 'It should convert the URI.'); - test.equal(task.helper('github_web_url', 'http://github.com/paulirish/newsite/', '/issues'), 'https://github.com/paulirish/newsite/issues', 'It should convert the URI.'); + test.equal(grunt.helper('github_web_url', 'git@github.com:cowboy/grunt.git', 'issues'), 'https://github.com/cowboy/grunt/issues', 'It should convert the URI.'); + test.equal(grunt.helper('github_web_url', 'https://cowboy@github.com/cowboy/grunt.git', 'issues'), 'https://github.com/cowboy/grunt/issues', 'It should convert the URI.'); + test.equal(grunt.helper('github_web_url', 'git://github.com/cowboy/grunt.git', 'issues'), 'https://github.com/cowboy/grunt/issues', 'It should convert the URI.'); + test.equal(grunt.helper('github_web_url', 'http://github.com/paulirish/newsite', 'issues'), 'https://github.com/paulirish/newsite/issues', 'It should convert the URI.'); + test.equal(grunt.helper('github_web_url', 'http://github.com/paulirish/newsite/', 'issues'), 'https://github.com/paulirish/newsite/issues', 'It should convert the URI.'); + test.equal(grunt.helper('github_web_url', 'http://github.com/paulirish/newsite', '/issues'), 'https://github.com/paulirish/newsite/issues', 'It should convert the URI.'); + test.equal(grunt.helper('github_web_url', 'http://github.com/paulirish/newsite/', '/issues'), 'https://github.com/paulirish/newsite/issues', 'It should convert the URI.'); test.done(); } }; diff --git a/test/tasks/misc_test.js b/test/tasks/misc_test.js index 1b9ef5c49..0bc538d83 100644 --- a/test/tasks/misc_test.js +++ b/test/tasks/misc_test.js @@ -1,13 +1,19 @@ +var grunt = require('../../lib/grunt'); +var file = grunt.file; +var utils = grunt.utils; +var config = grunt.config; +var template = grunt.template; + exports['config'] = function(test) { test.expect(2); - test.deepEqual(task.helper('config'), config(), 'It should just pass through to config.'); - test.deepEqual(task.helper('config', 'meta'), config('meta'), 'It should just pass through to config.'); + test.deepEqual(grunt.helper('config'), config(), 'It should just pass through to config.'); + test.deepEqual(grunt.helper('config', 'meta'), config('meta'), 'It should just pass through to config.'); test.done(); }; exports['json'] = function(test) { test.expect(2); - var obj = task.helper('json', 'test/fixtures/test.json'); + var obj = grunt.helper('json', 'test/fixtures/test.json'); test.equal(obj.foo, 'bar', 'JSON properties should be available as-defined.'); test.deepEqual(obj.baz, [1, 2, 3], 'JSON properties should be available as-defined.'); test.done(); @@ -16,18 +22,18 @@ exports['json'] = function(test) { exports['strip_banner'] = function(test) { test.expect(2); var src = file.read('test/fixtures/banner.js'); - test.equal(task.helper('strip_banner', src), '// Comment\n\n/* Comment */\n', 'It should strip only the top banner.'); + test.equal(grunt.helper('strip_banner', src), '// Comment\n\n/* Comment */\n', 'It should strip only the top banner.'); src = file.read('test/fixtures/banner2.js'); - test.equal(task.helper('strip_banner', src), '\n/*! SAMPLE\n * BANNER */\n\n// Comment\n\n/* Comment */\n', 'It should strip only the top banner.'); + test.equal(grunt.helper('strip_banner', src), '\n/*! SAMPLE\n * BANNER */\n\n// Comment\n\n/* Comment */\n', 'It should strip only the top banner.'); test.done(); }; exports['file_strip_banner'] = function(test) { test.expect(2); var filepath = 'test/fixtures/banner.js'; - test.equal(task.helper('file_strip_banner', filepath), '// Comment\n\n/* Comment */\n', 'It should strip only the top banner.'); + test.equal(grunt.helper('file_strip_banner', filepath), '// Comment\n\n/* Comment */\n', 'It should strip only the top banner.'); filepath = 'test/fixtures/banner2.js'; - test.equal(task.helper('file_strip_banner', filepath), '\n/*! SAMPLE\n * BANNER */\n\n// Comment\n\n/* Comment */\n', 'It should not strip a top banner beginning with /*!.'); + test.equal(grunt.helper('file_strip_banner', filepath), '\n/*! SAMPLE\n * BANNER */\n\n// Comment\n\n/* Comment */\n', 'It should not strip a top banner beginning with /*!.'); test.done(); }; @@ -36,19 +42,19 @@ exports['banner'] = function(test) { config('test_config', {a: 'aaaaa', b: 'bbbbb', c: [1, 2, 3], d: [{a: 1}, {a: 2}, {a: 3}]}); config('meta.banner', 'foo\n<%= test_config.a %>\nbar'); - test.equal(task.helper('banner'), utils.normalizelf('foo\naaaaa\nbar\n'), 'It should use the default banner.'); + test.equal(grunt.helper('banner'), utils.normalizelf('foo\naaaaa\nbar\n'), 'It should use the default banner.'); config('test_config.banner', '<%= test_config.b %>'); - test.equal(task.helper('banner', 'test_config.banner'), utils.normalizelf('bbbbb\n'), 'It should use the requested banner.'); + test.equal(grunt.helper('banner', 'test_config.banner'), utils.normalizelf('bbbbb\n'), 'It should use the requested banner.'); config('test_config.banner', '<%= test_config.c.join(", ") %>'); - test.equal(task.helper('banner', 'test_config.banner'), utils.normalizelf('1, 2, 3\n'), 'It should join arrays.'); + test.equal(grunt.helper('banner', 'test_config.banner'), utils.normalizelf('1, 2, 3\n'), 'It should join arrays.'); config('test_config.banner', '<%= _.pluck(test_config.d, "a").join(", ") %>'); - test.equal(task.helper('banner', 'test_config.banner'), utils.normalizelf('1, 2, 3\n'), 'It should join arrays.'); + test.equal(grunt.helper('banner', 'test_config.banner'), utils.normalizelf('1, 2, 3\n'), 'It should join arrays.'); - config('test_config.banner', '<%= template.today("m/d/yyyy") %>'); - test.equal(task.helper('banner', 'test_config.banner'), utils.normalizelf(template.today('m/d/yyyy') + '\n'), 'It should parse the current date correctly.'); + config('test_config.banner', '<%= grunt.template.today("m/d/yyyy") %>'); + test.equal(grunt.helper('banner', 'test_config.banner'), utils.normalizelf(template.today('m/d/yyyy') + '\n'), 'It should parse the current date correctly.'); test.done(); };