├── Makefile ├── README.markdown ├── lib └── jake.js └── package.json /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Node-Jake JavaScript build tool 3 | # Copyright 2112 Matthew Eernisse (mde@fleegix.org) 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | .PHONY: all build install clean uninstall 19 | 20 | all: build 21 | 22 | build: 23 | @echo 'Node-Jake built.' 24 | 25 | install: 26 | @cp ./lib/jake.js /usr/local/bin/jake && \ 27 | chmod 755 /usr/local/bin/jake && \ 28 | echo 'Node-Jake installed.' 29 | 30 | clean: 31 | @true 32 | 33 | uninstall: 34 | @rm -f /usr/local/bin/jake && \ 35 | echo 'Node-Jake uninstalled.' 36 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | ### Node-Jake -- JavaScript build tool for Node.js 2 | 3 | ### Installing 4 | 5 | Prerequisites: Node-Jake requires Node.js. () 6 | 7 | Get Node-Jake: 8 | 9 | git clone git://github.com/mde/node-jake.git 10 | 11 | Build Node-Jake: 12 | 13 | cd node-jake && make && sudo make install 14 | 15 | ### Installing with [npm](http://npmjs.org/) 16 | 17 | npm install jake 18 | 19 | Or, get the code, and `npm link` in the code root. 20 | 21 | ### Basic usage 22 | 23 | jake [-f Jakefile] [-C directory] [--version] target (commands/options ...) 24 | 25 | ### Description 26 | 27 | Jake is a simple JavaScript build program with capabilities similar to the regular make or rake command. 28 | 29 | Jake has the following features: 30 | * Jakefiles are in standard JavaScript syntax 31 | * Tasks with prerequisites 32 | * Namespaces for tasks 33 | 34 | ### Options 35 | 36 | --version Display the program version. 37 | 38 | -f *FILE* 39 | --jakefile *FILE* Use FILE as the Jakefile. 40 | 41 | -C *DIRECTORY* 42 | --directory *DIRECTORY* Change to DIRECTORY before running tasks. 43 | 44 | -T 45 | --tasks Display the tasks, with descriptions, then exit. 46 | 47 | ### Jakefile syntax 48 | 49 | 50 | Use `task` to define tasks. Call it with three arguments: 51 | 52 | task(name, dependencies, handler); 53 | 54 | Where `name` is the string name of the task, `dependencies` is an array of the dependencies, and `handler` is a function to run for the task. 55 | 56 | Use `desc` to add a string description of the task. 57 | 58 | Here's an example: 59 | 60 | var sys = require('sys'); 61 | 62 | desc('This is the default task.'); 63 | task('default', [], function (params) { 64 | sys.puts('This is the default task.'); 65 | sys.puts(sys.inspect(arguments)); 66 | }); 67 | 68 | Use `namespace` to create a namespace of tasks to perform. Call it with two arguments: 69 | 70 | namespace(name, namespaceTasks); 71 | 72 | Where is `name` is the name of the namespace, and `namespaceTasks` is a function with calls inside it to `task` or `desc` definining all the tasks for that namespace. 73 | 74 | Here's an example: 75 | 76 | var sys = require('sys'); 77 | 78 | desc('This is the default task.'); 79 | task('default', [], function () { 80 | sys.puts('This is the default task.'); 81 | sys.puts(sys.inspect(arguments)); 82 | }); 83 | 84 | namespace('foo', function () { 85 | desc('This the foo:bar task'); 86 | task('bar', [], function () { 87 | sys.puts('doing foo:bar task'); 88 | sys.puts(sys.inspect(arguments)); 89 | }); 90 | 91 | desc('This the foo:baz task'); 92 | task('baz', ['default', 'foo:bar'], function () { 93 | sys.puts('doing foo:baz task'); 94 | sys.puts(sys.inspect(arguments)); 95 | }); 96 | 97 | }); 98 | 99 | In this example, the foo:baz task depends on both the default and the foo:bar task. 100 | 101 | ### Passing parameters to jake 102 | 103 | Two kinds of parameters can be passed to Node-Jake: positional and named parameters. 104 | 105 | Any single parameters passed to the jake command after the task name are passed along to the task handler as positional arguments. For example, with the following Jakefile: 106 | 107 | var sys = require('sys'); 108 | 109 | desc('This is an awesome task.'); 110 | task('awesome', [], function () { 111 | sys.puts(sys.inspect(Array.prototype.slice.call(arguments))); 112 | }); 113 | 114 | You could run `jake` like this: 115 | 116 | jake awesome foo bar baz 117 | 118 | And you'd get the following output: 119 | 120 | [ 'foo', 'bar', 'baz' ] 121 | 122 | Any paramters passed to the jake command that contain a colon (:) or equals sign (=) will be added to a keyword/value object that is passed as a final argument to the task handler. 123 | 124 | With the above Jakefile, you could run `jake` like this: 125 | 126 | jake awesome foo bar baz qux:zoobie frang:asdf 127 | 128 | And you'd get the following output: 129 | 130 | [ 'foo' 131 | , 'bar' 132 | , 'baz' 133 | , { qux: 'zoobie', frang: 'asdf' } 134 | ] 135 | 136 | Running `jake` with no arguments runs the default task. 137 | 138 | ### Related projects 139 | 140 | James Coglan's "Jake": 141 | 142 | Confusingly, this is a Ruby tool for building JavaScript packages from source code. 143 | 144 | 280 North's Jake: 145 | 146 | This is also a JavaScript port of Rake, which runs on the Narwhal platform. 147 | 148 | ### Author 149 | 150 | Matthew Eernisse, mde@fleegix.org 151 | 152 | 153 | -------------------------------------------------------------------------------- /lib/jake.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * Node-Jake JavaScript build tool 4 | * Copyright 2112 Matthew Eernisse (mde@fleegix.org) 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | var JAKE_VERSION = '0.1.0'; 21 | var args = process.argv.slice(2); 22 | var sys = require('sys'); 23 | 24 | var parseopts = new function () { 25 | var optsReg = { 26 | directory: ['-C', '--directory'] 27 | , jakefile: ['-f', '--file'] 28 | , tasks: ['-T', '--tasks'] 29 | , version: [null, '--version'] 30 | }; 31 | 32 | this.parse = function (args) { 33 | var cmds = []; 34 | var opts = {}; 35 | var optsReverseMap = {}; 36 | var optsItem; 37 | var arg; 38 | var argName; 39 | var argItems; 40 | 41 | for (var p in optsReg) { 42 | optsItem = optsReg[p]; 43 | for (var i = 0; i < optsItem.length; i++) { 44 | optsReverseMap[optsItem[i]] = p; 45 | } 46 | } 47 | 48 | while (args.length) { 49 | arg = args.shift(); 50 | if (arg.indexOf('--') == 0) { 51 | argItems = arg.split('='); 52 | argName = optsReverseMap[argItems[0]]; 53 | if (argName) { 54 | // If there's no attached value, value is null 55 | opts[argName] = argItems[1] || null; 56 | } 57 | else { 58 | throw new Error('Unknown option "' + argItems[0] + '"'); 59 | } 60 | } 61 | else if (arg.indexOf('-') == 0) { 62 | argName = optsReverseMap[arg]; 63 | if (argName) { 64 | // If there is no following item, or the next item is 65 | // another opt, value is null 66 | opts[argName] = (!args[0] || (args[0].indexOf('-') == 0)) ? 67 | null : args.shift(); 68 | } 69 | else { 70 | throw new Error('Unknown option "' + arg + '"'); 71 | } 72 | } 73 | else { 74 | cmds.push(arg); 75 | } 76 | } 77 | 78 | return {cmds: cmds, opts: opts}; 79 | }; 80 | 81 | }; 82 | 83 | var parsed = parseopts.parse(args); 84 | var opts = parsed.opts; 85 | var cmds = parsed.cmds; 86 | var taskName = cmds.shift(); 87 | var jakefile; 88 | var dirname = opts.directory || process.cwd(); 89 | 90 | taskName = taskName || 'default'; 91 | jakefile = opts.jakefile ? 92 | opts.jakefile.replace(/\.js$/, '') : dirname + '/Jakefile'; 93 | 94 | var jake = new function () { 95 | this.currentNamespace = 'default'; 96 | this.currentTaskDescription = null; 97 | this.namespaceTasks = { 98 | 'default': {} 99 | }; 100 | this.runTask = function (name, args) { 101 | var nameArr = name.split(':'); 102 | var nsName, taskName; 103 | if (nameArr.length > 1) { 104 | nsName = nameArr[0]; 105 | taskName = nameArr[1]; 106 | } 107 | else { 108 | nsName = 'default'; 109 | taskName = name; 110 | } 111 | var task = this.namespaceTasks[nsName][taskName]; 112 | if (!task) { 113 | throw new Error('Task "' + name + '" is not defined in the Jakefile.'); 114 | } 115 | var deps = task.deps; 116 | if (deps && deps instanceof Array) { 117 | for (var i = 0; i < deps.length; i++) { 118 | this.runTask.call(this, deps[i], args); 119 | } 120 | } 121 | var parsed = this.parseArgs(args); 122 | var passArgs = parsed.cmds; 123 | if (parsed.opts) { 124 | passArgs = parsed.cmds.concat(parsed.opts); 125 | } 126 | task.handler.apply(task, passArgs); 127 | }; 128 | 129 | this.parseArgs = function (args) { 130 | var cmds = []; 131 | var opts = {}; 132 | var pat = /:|=/; 133 | var argItems; 134 | var hasOpts = false; 135 | for (var i = 0; i < args.length; i++) { 136 | argItems = args[i].split(pat); 137 | if (argItems.length > 1) { 138 | hasOpts = true; 139 | opts[argItems[0]] = argItems[1]; 140 | } 141 | else { 142 | cmds.push(args[i]); 143 | } 144 | } 145 | if (!hasOpts) { opts = null; } 146 | return {cmds: cmds, opts: opts}; 147 | }; 148 | 149 | this.die = function (str) { 150 | sys.puts(str); 151 | process.exit(); 152 | }; 153 | 154 | this.showAllTaskDescriptions = function () { 155 | var nsTasks = this.namespaceTasks; 156 | var str = ''; 157 | var task; 158 | for (var p in nsTasks) { 159 | for (var q in nsTasks[p]) { 160 | task = nsTasks[p][q]; 161 | if (p != 'default') { 162 | str += p + ':'; 163 | } 164 | str += q + ' -- '; 165 | if (task.description) { 166 | str += task.description 167 | } 168 | else { 169 | str += '(No description)'; 170 | } 171 | str += '\n'; 172 | } 173 | } 174 | this.die(str); 175 | }; 176 | 177 | }(); 178 | 179 | jake.Task = function (name, deps, handler) { 180 | this.name = name, 181 | this.deps = deps, 182 | this.handler = handler; 183 | this.desription = null; 184 | }; 185 | 186 | global.task = function (name, deps, handler) { 187 | var task = new jake.Task(name, deps, handler); 188 | if (jake.currentTaskDescription) { 189 | task.description = jake.currentTaskDescription; 190 | jake.currentTaskDescription = null; 191 | } 192 | jake.namespaceTasks[jake.currentNamespace][name] = task; 193 | }; 194 | 195 | global.desc = function (str) { 196 | jake.currentTaskDescription = str; 197 | }; 198 | 199 | global.namespace = function (name, tasks) { 200 | if (typeof jake.namespaceTasks[name] == 'undefined') { 201 | jake.namespaceTasks[name] = {}; 202 | } 203 | jake.currentNamespace = name; 204 | tasks(); 205 | jake.currentNamespace = 'default'; 206 | }; 207 | 208 | if (typeof opts.version != 'undefined') { 209 | jake.die(JAKE_VERSION); 210 | } 211 | 212 | var tasks = require(jakefile); 213 | 214 | if (typeof opts.tasks != 'undefined') { 215 | jake.showAllTaskDescriptions(); 216 | } 217 | else { 218 | process.chdir(dirname); 219 | jake.runTask(taskName, cmds); 220 | } 221 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { "name" : "jake" 2 | , "version" : "0.1.0" 3 | , "author" : "Matthew Eernisse (http://fleegix.org)" 4 | , "main" : "./lib/jake" 5 | , "bin" : { "jake" : "./lib/jake.js" } 6 | } 7 | --------------------------------------------------------------------------------