├── 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 |
--------------------------------------------------------------------------------