Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

jake

Package Overview
Dependencies
Maintainers
0
Versions
167
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

jake - npm Package Compare versions

Comparing version 0.1.12 to 0.1.13

bin/cli.js

743

lib/jake.js

@@ -1,4 +0,3 @@

#!/usr/bin/env node
/*
* Node-Jake JavaScript build tool
* Jake JavaScript build tool
* Copyright 2112 Matthew Eernisse (mde@fleegix.org)

@@ -20,138 +19,41 @@ *

var JAKE_VERSION = '0.1.12'
, jake
, args = process.argv.slice(2)
var jake
, fs = require('fs')
, path = require('path')
, sys = require('sys')
, usage
, parseopts = {}
, optsReg
, Parser
, parsed
, opts
, cmds
, taskName
, jakefile
, dirname
, isCoffee
, exists
, tasks;
, path = require('path');
var Namespace = function (name, parentNamespace) {
this.name = name;
this.parentNamespace = parentNamespace;
this.childNamespaces = {};
this.tasks = {};
};
exists = function () {
var cwd = process.cwd();
if (path.existsSync(jakefile) || path.existsSync(jakefile + '.js') ||
path.existsSync(jakefile + '.coffee')) {
return true;
}
process.chdir("..");
if (cwd === process.cwd()) {
return false;
}
return exists();
};
usage = ''
+ 'Node-Jake JavaScript build tool\n'
+ '********************************************************************************\n'
+ 'If no flags are given, Node-Jake looks for a Jakefile or Jakefile.js in the current directory.\n'
+ '********************************************************************************\n'
+ '{Usage}: jake [options] target (commands/options ...)\n'
+ '\n'
+ '{Options}:\n'
+ ' -f, --jakefile FILE Use FILE as the Jakefile\n'
+ ' -C, --directory DIRECTORY Change to DIRECTORY before running tasks.\n'
+ ' -T, --tasks Display the tasks, with descriptions, then exit.\n'
+ ' -h, --help Outputs help information\n'
+ ' -V, --version Outputs Node-Jake version\n'
+ '';
/**
* @constructor
* Parses a list of command-line args into a key/value object of
* options and an array of positional commands.
* @ param {Array} opts A list of options in the following format:
* [{full: 'foo', abbr: 'f'}, {full: 'bar', abbr: 'b'}]]
* A Jake task
*/
parseopts.Parser = function (opts) {
// Positional commands parse out of the args
this.cmds = [];
// A key/value object of matching options parsed out of the args
this.opts = {};
// Data structures used for parsing
this.reg = [];
this.shortOpts = {};
this.longOpts = {};
var item;
for (var i = 0, ii = opts.length; i < ii; i++) {
item = opts[i];
this.shortOpts[item.abbr] = item.full;
this.longOpts[item.full] = item.full;
}
this.reg = opts;
var Task = function (name, prereqs, action, async, type) {
this.name = name;
this.fullName = null;
this.prereqs = prereqs;
this.action = action;
this.desription = null;
this.async = async === true;
this.type = type;
this.done = false;
};
parseopts.Parser.prototype = new function () {
Task.prototype = new (function () {
this.invoke = function () {
jake.runTask(this.fullName, arguments, true);
};
/**
* Parses an array of arguments into options and positional commands
* Any matcthing opts end up in a key/value object keyed by the 'full'
* name of the option. Any args that aren't passed as options end up in
* an array of positional commands.
* Any options passed without a value end up with a value of null
* in the key/value object of options
* If the user passes options that are not defined in the list passed
* to the constructor, the parser throws an error 'Unknown option.'
* @param {Array} args The command-line args to parse
*/
this.parse = function (args) {
var cmds = []
, opts = {}
, arg
, argName
, argItems;
this.execute = function () {
jake.reenableTask(this.fullName, false);
jake.runTask(this.fullName, arguments, false);
};
while (args.length) {
arg = args.shift();
if (arg.indexOf('--') == 0) {
argItems = arg.split('=');
argName = this.longOpts[argItems[0].substr(2)];
if (argName) {
// If there's no attached value, value is null
opts[argName] = argItems[1] || true;
}
else {
throw new Error('Unknown option "' + argItems[0] + '"');
}
}
else if (arg.indexOf('-') == 0) {
argName = this.shortOpts[arg.substr(1)];
if (argName) {
// If there is no following item, or the next item is
// another opt, value is null
opts[argName] = (!args[0] || (args[0].indexOf('-') == 0)) ?
true : args.shift();
}
else {
throw new Error('Unknown option "' + arg + '"');
}
}
else {
cmds.push(arg);
}
}
this.cmds = cmds;
this.opts = opts;
this.reenable = function (deep) {
jake.reenableTask(this.fullName, deep);
};
})();
var Namespace = function (name, parentNamespace) {
this.name = name;
this.parentNamespace = parentNamespace;
this.childNamespaces = {};
this.tasks = {};
};

@@ -169,4 +71,8 @@

var _this = this
// The list of tasks/dependencies to run, parsed recursively
// and run bottom-up, so dependencies run first
, _taskIndex = 0
, _modTimes = {}
, _workingTaskList = []
// The list of tasks/prerequisites to run, parsed recursively
// and run bottom-up, so prerequisites run first
, _taskList = []

@@ -201,140 +107,34 @@ // A dictionary of loaded tasks, to ensure that all tasks

/**
* Tells us if the task has any dependencies
* @param {Array.<String>} deps An array of dependencies
* @return {Boolean} true if deps is a non-empty Array
* Tells us if the task has any prerequisites
* @param {Array.<String>} prereqs An array of prerequisites
* @return {Boolean} true if prereqs is a non-empty Array
*/
, _taskHasDeps = function (deps) {
return !!(deps && _isArray(deps) && deps.length);
, _taskHasPrereqs = function (prereqs) {
return !!(prereqs && _isArray(prereqs) && prereqs.length);
}
/**
* Handles a file task
* @param {Error} err Error, if any, returned from fs.lstat
* @param {fs.Stats} stats Stats obj, if any, returned from fs.lstat
* @param {String} name The task name
* @param {Array} deps The array of dependencies, if any
* @callback {Function} Callback for running the task
*/
, _handleFileTask = function (name, opts, callback) {
var err = opts.err
, stats = opts.stats
, deps = opts.deps
, includeDeps = opts.includeDeps
, subOpts = {};
// If the task has dependencies these are invoked in order.
// If any of them were changed after the current file, or
// if the current file does not exist, then push the current
// task to the list of tasks and update the time of last change.
if (includeDeps && _taskHasDeps(deps)) {
stats = stats || {ctime: 0};
// Clone original opts, set root to false
_mixin(subOpts, opts);
subOpts.root = false;
for (var i = 0, ii = deps.length, depsLeft = deps.length, maxTime = stats.ctime;
i < ii; i++) {
_parseDeps(deps[i], subOpts, function (ctime) {
depsLeft -= 1;
maxTime = (maxTime == null || maxTime < ctime) ? ctime : maxTime;
if (depsLeft == 0) {
if (maxTime > stats.ctime) {
_taskList.push(name);
}
callback(maxTime);
}
});
}
}
// If it does not have dependencies and could not
// be found, simply execute the task and use the
// current time as the last time it changed.
else if (err) {
// File not found
if (err.errno == 2) {
_taskList.push(name);
callback(new Date());
}
// Errors are rethrown.
else {
throw new Error(err.message);
}
}
// No dependencies and the file already existed, then don't
// do anything and just return the time of last changed.
else {
callback(stats.ctime);
}
}
/**
* Parses all dependencies of a task (and their dependencies, etc.)
* recursively -- depth-first, so deps run first
* Parses all prerequisites of a task (and their prerequisites, etc.)
* recursively -- depth-first, so prereqs run first
* @param {String} name The name of the current task whose
* dependencies are being parsed.
* @param {Object} opts -- has following options:
* root {Boolean}: Is this the root task of a dependency tree or not
* @param {Function} [callback] Callbacks for async tasks
* prerequisites are being parsed.
* @param {Boolean} [isRoot] Is this the root task of a prerequisite tree or not
* @param {Boolean} [includePrereqs] Whether or not to descend into prerequs
*/
, _parseDeps = function (name, opts, callback) {
, _parsePrereqs = function (name, isRoot, includePrereqs) {
var task = _this.getTask(name)
, deps = task ? task.deps : []
, root = opts.root
, includeDeps = opts.includeDeps
, subOpts = {};
, prereqs = task ? task.prereqs : [];
// No task found
if (!task) {
// If this is the root task, it's a failure if the task cannot be found.
if (root) {
throw new Error('Task "' + name + '" is not defined in the Jakefile.');
}
// If this is not the root task, we'll just assume the name is a file.
// Search for a file instead and provide the callback with the
// last time it changed
fs.lstat(name, function(err, stats) {
if (err) {
throw new Error(err.message);
}
callback(stats.ctime);
});
// No task found -- if it's the root, throw, because we know that
// *should* be an existing task. Otherwise it could be a file prereq
if (isRoot && !task) {
throw new Error('task not found');
}
// The task was found
else {
// File task
if (task.isFile) {
fs.lstat(name, function(err, stats) {
var taskOpts = {
err: err
, stats: stats
, deps: deps
, includeDeps: includeDeps
};
_handleFileTask(name, taskOpts, callback);
});
}
// Normal task
else {
// If the task has dependencies, execute those first and then
// push the task to the task list. In case it will be used as a
// dependancy for a file task, the last time of change is set to
// the current time in order to force files to update as well.
if (includeDeps && _taskHasDeps(deps)) {
// Clone original opts, set root to false
_mixin(subOpts, opts);
subOpts.root = false;
for (var i = 0, ii = deps.length, ctr = deps.length; i < ii; i++) {
_parseDeps(deps[i], subOpts, function () {
ctr -= 1;
if (ctr == 0) {
_taskList.push(name);
callback(new Date());
}
});
}
if (includePrereqs && _taskHasPrereqs(prereqs)) {
for (var i = 0, ii = prereqs.length; i < ii; i++) {
_parsePrereqs(prereqs[i], false, includePrereqs);
}
// If the task does not have dependencies, just push it.
else {
_taskList.push(name);
callback(new Date());
}
}
_workingTaskList.push(name);
}

@@ -345,2 +145,3 @@ };

// =================
this.errorCode = undefined;
// Name/value map of all the various tasks defined in a Jakefile.

@@ -357,16 +158,20 @@ // Non-namespaced tasks are placed into 'default.'

this.populateAndProcessTaskList = function (name, includeDeps, callback) {
this.populateAndProcessTaskList = function (name, includePrereqs, callback) {
var opts = {
root: true
, includeDeps: includeDeps
, includePrereqs: includePrereqs
};
// Parse all the dependencies up front. This allows use of a simple
// Parse all the prerequisites up front. This allows use of a simple
// queue to run all the tasks in order, and treat sync/async essentially
// the same.
_parseDeps(name, opts, callback);
_parsePrereqs(name, opts, callback);
};
this.createTree = function (name, isRoot, includePrereqs) {
_parsePrereqs(name, isRoot, includePrereqs);
};
/**
* Initial function called to run the specified task. Parses all the
* dependencies and then kick off the queue-processing
* prerequisites and then kicks off the queue-processing
* @param {String} name The name of the task to run

@@ -378,27 +183,22 @@ * @param {Array} args The list of command-line args passed after

*/
this.runTask = function (name, includeDeps) {
this.populateAndProcessTaskList(name, includeDeps, function () {
if (!_taskList.length) {
_this.die('No tasks to run.');
}
// Kick off running the list of tasks
_this.runNextTask();
});
this.runTask = function (name, args, includePrereqs) {
this.createTree(name, true, includePrereqs);
_taskList.splice.apply(_taskList, [_taskIndex, 0].concat(_workingTaskList));
_workingTaskList = [];
this.runNextTask(args);
};
this.reenableTask = function (name, includeDeps) {
var self = this;
this.populateAndProcessTaskList(name, includeDeps, function () {
var name
, task;
if (!_taskList.length) {
_this.die('No tasks to reenable.');
this.reenableTask = function (name, includePrereqs) {
_parsePrereqs(name, true, includePrereqs);
if (!_workingTaskList.length) {
fail('No tasks to reenable.');
}
else {
for (var i = 0, ii = _workingTaskList.length; i < ii; i++) {
name = _workingTaskList[i];
task = this.getTask(name);
task.done = false;
}
else {
while (name = (_taskList.shift())) {
task = self.getTask(name);
task.done = false;
}
}
});
}
_workingTaskList = [];
};

@@ -446,76 +246,105 @@

*/
this.runNextTask = function () {
var name = _taskList.shift()
this.runNextTask = function (args) {
var name = _taskList[_taskIndex]
, task
, parsed
, passArgs;
, prereqs
, prereqName
, prereqTask
, stats
, modTime;
// If there are still tasks to run, do it
if (name) {
_taskIndex++;
task = this.getTask(name);
// Run tasks only once, even if it ends up in the task queue
// multiple times
if (task.done) {
complete();
}
// Okie, we haven't done this one
else {
// Flag this one as done, no repeatsies
task.done = true;
// TODO Do this once instead of on each iteration
parsed = this.parseArgs(this.args);
passArgs = parsed.cmds;
if (parsed.opts) {
passArgs = parsed.cmds.concat(parsed.opts);
}
// Task or file-task
if (task) {
prereqs = task.prereqs;
// Run this mofo
task.handler.apply(task, passArgs);
// Async tasks call this themselves
if (!task.async) {
// Run tasks only once, even if it ends up in the task queue multiple times
if (task.done) {
complete();
}
}
}
};
// Okie, we haven't done this one
else {
// Flag this one as done, no repeatsies
task.done = true;
/**
* Parse the list of args into positional args and a final keyword/value
* object to pass to the task invocations.
* @param {Array} args A list of arguments to parse.
*/
this.parseArgs = function (args) {
var cmds = []
, opts = {}
, pat = /:|=/
, argItems
, hasOpts = false;
if (task.type == 'file') {
try {
stats = fs.statSync(name);
modTime = stats.ctime;
}
catch (e) {
// Assume there's a task to fall back to to generate the file
}
for (var i = 0; i < args.length; i++) {
argItems = args[i].split(pat);
if (argItems.length > 1) {
hasOpts = true;
opts[argItems[0]] = argItems[1];
if (prereqs.length) {
for (var i = 0, ii = prereqs.length; i < ii; i++) {
prereqName = prereqs[i];
prereqTask = this.getTask(prereqName);
// Run the action if the prereq is a normal task, or a file/directory
// task with a mod-date more recent than the one for this file
if ((prereqTask && prereqTask.type == 'task') || !modTime ||
_modTimes[prereqName] > modTime) {
if (typeof task.action == 'function') {
task.action.apply(task, args || []);
// The action may have created/modified the file
// ---------
// If there's a valid file at the end of running the task,
// use its mod-time as last modified
try {
stats = fs.statSync(name);
modTime = stats.ctime;
}
// If there's still no actual file after running the file-task,
// treat this simply as a plain task -- the current time will be
// the mod-time for anything that depends on this file-task
catch (e) {
modTime = new Date();
}
}
break;
}
}
}
else {
if (typeof task.action == 'function') {
task.action.apply(task, args || []);
modTime = new Date();
}
}
_modTimes[name] = modTime;
// Async tasks call this themselves
if (!task.async) {
complete();
}
}
else {
// Run this mofo
if (typeof task.action == 'function') {
task.action.apply(task, args || []);
}
// Async tasks call this themselves
if (!task.async) {
complete();
}
}
}
}
// Task doesn't exist; assume file. Just get the mod-time if the file
// actually exists
else {
cmds.push(args[i]);
stats = fs.statSync(name);
_modTimes[name] = stats.ctime;
complete();
}
}
if (!hasOpts) { opts = null; }
return {cmds: cmds, opts: opts};
};
/**
* Prints out a message and ends the jake program.
* @param {String} str The message to print out before dying.
*/
this.die = function (str) {
var len = str.length, i;
for (i = 0; i < len; i+=25) {
sys.print(str.slice(i,i+25));
}
process.exit();
};
this.parseAllTasks = function () {

@@ -548,3 +377,3 @@ var _parseNs = function (name, ns) {

*/
this.showAllTaskDescriptions = function () {
this.showAllTaskDescriptions = function (f) {
var maxTaskNameLength = 0

@@ -554,3 +383,5 @@ , task

, padding
, descr;
, name
, descr
, filter = typeof f == 'string' ? f : null;

@@ -566,3 +397,9 @@ for (var p in jake.Task) {

for (var p in jake.Task) {
if (filter && p.indexOf(filter) == -1) {
continue;
}
task = jake.Task[p];
name = '\033[32m' + p + '\033[39m ';
// Create padding-string with calculated length

@@ -572,185 +409,65 @@ padding = (new Array(maxTaskNameLength - p.length + 2)).join(' ');

descr = task.description || '(No description)';
// Comment-colors FTW
descr = "\033[90m # "+ descr +"\033[39m";
descr = '\033[90m # ' + descr + '\033[39m \033[37m \033[39m';
console.log('jake ' + p + padding + descr);
console.log('jake ' + name + padding + descr);
}
process.exit();
};
}();
this.createTask = function () {
var args = Array.prototype.slice.call(arguments)
, task
, type
, name
, action
, async
, prereqs = [];
/**
* @constructor
* A Jake task
*/
jake.Task = function (name, deps, handler, async, isFile) {
this.name = name;
this.fullName = null;
this.deps = deps;
this.handler = handler;
this.desription = null;
this.async = async === true;
this.isFile = isFile;
this.done = false;
};
type = args.shift()
jake.Task.prototype = new (function () {
this.invoke = function () {
jake.runTask(this.fullName, true);
};
// name, [deps], [action]
// Older name (string) + deps (array) format
if (typeof args[0] == 'string') {
name = args.shift();
if (_isArray(args[0])) {
prereqs = args.shift();
}
if (typeof args[0] == 'function') {
action = args.shift();
async = args.shift();
}
}
// name:deps, [action]
// Newer object-literal syntax, e.g.: {'name': ['depA', 'depB']}
else {
obj = args.shift()
for (var p in obj) {
prereqs = prereqs.concat(obj[p]);
name = p;
}
action = args.shift();
async = args.shift();
}
this.execute = function () {
jake.reenableTask(this.fullName, true);
jake.runTask(this.fullName, false);
};
if (type == 'directory') {
action = function () {
if (!path.existsSync(name)) {
fs.mkdirSync(name, 0755);
}
};
}
this.reenable = function (deep) {
jake.reenableTask(this.fullName, deep);
};
})();
task = new jake.Task(name, prereqs, action, async, type);
jake.taskOrFile = function () {
var args = Array.prototype.slice.call(arguments)
, type = args.shift()
, name = args.shift()
// Dependencies are an optional arg
, deps = typeof args[0] == 'function' ?
[] : args.shift()
, handler = args.shift()
, async = args.shift()
, isFile = (type == 'file');
var task = new jake.Task(name, deps, handler, async, isFile);
if (jake.currentTaskDescription) {
task.description = jake.currentTaskDescription;
jake.currentTaskDescription = null;
}
jake.currentNamespace.tasks[name] = task;
};
// Global functions for being called inside the Jakefile
// Yes, globals are ugly, but adding a few globals keeps
// the API nice and simple
global.jake = jake;
global.task = function (name, deps, handler, async) {
var args = Array.prototype.slice.call(arguments)
, type;
args.unshift('task');
jake.taskOrFile.apply(global, args);
};
global.file = function (name, deps, handler, async) {
var args = Array.prototype.slice.call(arguments);
args.unshift('file');
jake.taskOrFile.apply(global, args);
};
global.desc = function (str) {
jake.currentTaskDescription = str;
};
global.namespace = function (name, nextLevelDown) {
var curr = jake.currentNamespace
, ns = new Namespace(name, curr);
curr.childNamespaces[name] = ns;
jake.currentNamespace = ns;
nextLevelDown();
jake.currentNamespace = curr;
};
global.complete = function () {
jake.runNextTask();
};
global.fail = function (msg) {
var message = (msg && msg.toString()) || '(No error message specified.)';
throw new Error(message);
};
// ========================
// Run Jake
// ========================
optsReg = [
{ full: 'directory'
, abbr: 'C'
}
, { full: 'jakefile'
, abbr: 'f'
}
, { full: 'tasks'
, abbr: 'T'
}
, { full: 'help'
, abbr: 'h'
}
, { full: 'version'
, abbr: 'V'
}
];
Parser = new parseopts.Parser(optsReg);
parsed = Parser.parse(args);
opts = Parser.opts;
cmds = Parser.cmds;
taskName = cmds.shift();
dirname = opts.directory || process.cwd();
process.chdir(dirname);
taskName = taskName || 'default';
jakefile = opts.jakefile ?
opts.jakefile.replace(/\.js$/, '').replace(/\.coffee$/, '') : 'Jakefile';
if (opts.help) {
jake.die(usage);
}
if (opts.version) {
jake.die(JAKE_VERSION);
}
if (!exists()) {
jake.die('Could not load Jakefile.\nIf no Jakefile specified with -f or --jakefile, ' +
'jake looks for Jakefile or Jakefile.js in the current directory ' +
'or one of the parent directories.');
}
isCoffee = path.existsSync(jakefile + '.coffee');
try {
if (isCoffee) {
try {
CoffeeScript = require('coffee-script');
if (jake.currentTaskDescription) {
task.description = jake.currentTaskDescription;
jake.currentTaskDescription = null;
}
catch (e) {
jake.die('CoffeeScript is missing! Try `npm install coffee-script`');
}
}
tasks = require(path.join(process.cwd(), jakefile));
}
catch (e) {
if (e.stack) {
console.log(e.stack);
}
jake.die('Could not load Jakefile: ' + e);
}
jake.currentNamespace.tasks[name] = task;
};
}();
process.addListener('uncaughtException', function (err) {
console.log('jake aborted.');
if (err.stack) {
console.log(err.stack);
}
});
jake.Task = Task;
jake.Namespace = Namespace;
jake.parseAllTasks();
if (opts.tasks) {
jake.showAllTaskDescriptions();
}
else {
jake.args = cmds;
jake.runTask(taskName, true);
}
module.exports = jake;

@@ -1,6 +0,11 @@

{ "name" : "jake"
, "version" : "0.1.12"
, "author" : "Matthew Eernisse <mde@fleegix.org> (http://fleegix.org)"
, "main" : "./lib/jake"
, "bin" : { "jake" : "./lib/jake.js" }
{ "name": "jake"
, "version": "0.1.13"
, "author": "Matthew Eernisse <mde@fleegix.org> (http://fleegix.org)"
, "bin": { "jake": "./bin/cli.js" }
, "main": "./lib"
, "repository": {
"type": "git"
, "url": "https://github.com/mde/jake.git"
}
, "preferGlobal": true
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc