1 | #!/usr/bin/env node
|
2 |
|
3 |
|
4 | // Remove any command line information before bower/rc/optimist are loaded and attempt to hijack
|
5 | // our CLI args. This is a total hack but works around the issue described here:
|
6 | // https://github.com/bower/bower/issues/543
|
7 | //
|
8 | // This can be removed once bower is updated to 1.9.3 (which updates rc to 0.3.0)
|
9 | //
|
10 | var args = process.argv.slice(2);
|
11 | process.argv = [process.argv[0], process.argv[1]];
|
12 |
|
13 | process.on('uncaughtException', function(err) {
|
14 | verbose = false;
|
15 |
|
16 | if (err.code === 'EMFILE') {
|
17 | console.error('\n\n\tEMFILE error: Too many files open at one time. Fix this by running\n\n\t\tulimit -n 9999\n\n');
|
18 | } else {
|
19 | console.error('Unhandled Exception:', err.stack || err);
|
20 | }
|
21 | process.exit(128);
|
22 | });
|
23 |
|
24 | var _ = require('underscore'),
|
25 | fs = require('fs'),
|
26 | lumbar = require('../lib/lumbar'),
|
27 | dirname = require('path').dirname,
|
28 | growl = require('growl');
|
29 |
|
30 | var LONG_USAGE="lumbar help\n"+
|
31 | " Print this long help message.\n"+
|
32 | "lumbar build [--package name] [--config file] [--minimize] [--use path [--with options]] lumbarFile [outputDir]\n"+
|
33 | " Build out the package(s).\n"+
|
34 | "lumbar watch [--package name] [--config file] [--minimize] [--use path [--with options]] lumbarFile [outputDir]\n"+
|
35 | " Start watching the files for changes and rebuild as necessary.\n"+
|
36 | "\n"+
|
37 | "--package: represents the name of a corresponding package listed\n"+
|
38 | " under 'packages' in lumbarFile.\n"+
|
39 | "\n"+
|
40 | "--module: build only a specific module. May be defined multiple times to build multiple.\n"+
|
41 | "\n"+
|
42 | "--minimize: to shrink the resultant file.\n"+
|
43 | "\n"+
|
44 | "--sourceMap: generate a source map for the output modules.\n"+
|
45 | " WARN: This copies the source files into the module output unobfuscated.\n"
|
46 | " care should be exercised when used with production releases\n"
|
47 | "\n"+
|
48 | "--config: is the name and path to the lumbar config file, if\n"+
|
49 | " not given then lumbar.json is assumed.\n"+
|
50 | "\n"+
|
51 | "--use: path to your plugin to load\n"+
|
52 | "\n"+
|
53 | " --with: an optional json config object to pass to your plugin.\n"+
|
54 | "\n"+
|
55 | " --name: an optional name to assign to your plugin.\n"+
|
56 | "\n"+
|
57 | "lumbarFile: is the name and path to the lumbar config file, if\n"+
|
58 | " not given then lumbar.json is assumed.\n"+
|
59 | "\n"+
|
60 | "outputDir: Required. Designates where the files will be placed.\n"+
|
61 | "\n";
|
62 |
|
63 | var configFile,
|
64 | packageName,
|
65 | modules = [],
|
66 | libraries = [],
|
67 | minimize = false,
|
68 | sourceMap = false,
|
69 | build = true, // build by default.
|
70 | watch = false,
|
71 | verbose = false,
|
72 | modeSeen;
|
73 |
|
74 | var lumbarFile = 'lumbar.json';
|
75 | var outputDir;
|
76 | /**
|
77 | * Import paths.
|
78 | */
|
79 | var plugins = [],
|
80 | files = [];
|
81 |
|
82 | var arg;
|
83 | while (args.length) {
|
84 | arg = args.shift();
|
85 |
|
86 | var value = arg.split('=');
|
87 | arg = value[0];
|
88 | value = value[1];
|
89 |
|
90 | switch (arg) {
|
91 | case '-h':
|
92 | case '--help':
|
93 | console.error(LONG_USAGE);
|
94 | process.exit(0);
|
95 | break;
|
96 | case '-c':
|
97 | case '--config':
|
98 | configFile = args.shift();
|
99 | if (!configFile) throw new Error('--config <file> required');
|
100 | break;
|
101 | case '-p':
|
102 | case '--package':
|
103 | packageName = args.shift();
|
104 | if (!packageName) throw new Error('--package <name> required');
|
105 | break;
|
106 | case '--module':
|
107 | var moduleName = args.shift();
|
108 | if (!moduleName) throw new Error('--module <name> required');
|
109 | modules.push(moduleName);
|
110 | break;
|
111 | case '--library':
|
112 | if (!value) throw new Error('--library=<name> required');
|
113 | libraries.push(value);
|
114 | break;
|
115 | case '-m':
|
116 | case '--minimize':
|
117 | minimize = true;
|
118 | break;
|
119 | case '-s':
|
120 | case '--sourceMap':
|
121 | sourceMap = value || true;
|
122 | break;
|
123 | case '-b':
|
124 | case '--build':
|
125 | case 'build':
|
126 | if (modeSeen) {
|
127 | files.push(arg);
|
128 | } else {
|
129 | modeSeen = true;
|
130 | build = true;
|
131 | watch = false;
|
132 | }
|
133 | break;
|
134 | case '-w':
|
135 | case '--watch':
|
136 | case 'watch':
|
137 | if (modeSeen) {
|
138 | files.push(arg);
|
139 | } else {
|
140 | modeSeen = true;
|
141 | watch = true;
|
142 | build = false;
|
143 | }
|
144 | break;
|
145 | case '-v':
|
146 | case '--verbose':
|
147 | verbose = true;
|
148 | break;
|
149 | case '-u':
|
150 | case '--use':
|
151 | var options;
|
152 | var path = args.shift();
|
153 | if (!path) throw new Error('--use <path> required');
|
154 |
|
155 | if ('--with' == args[0]) {
|
156 | args.shift();
|
157 | options = args.shift();
|
158 | if (!options) throw new Error('--with <options> required');
|
159 | try {
|
160 | options = eval('(' + options + ')');
|
161 | } catch (err) {
|
162 | console.error('Failed to parse plugin options:', options);
|
163 | throw err;
|
164 | }
|
165 | }
|
166 |
|
167 | plugins.push({ path: path, options: options, name: 'PlugIn_' + plugins.length });
|
168 |
|
169 | break;
|
170 | default:
|
171 | files.push(arg);
|
172 | }
|
173 | }
|
174 |
|
175 | if (build || watch) {
|
176 | var file = files.shift();
|
177 | try {
|
178 | var stat = fs.statSync(file);
|
179 |
|
180 | if (stat.isFile()) {
|
181 | lumbarFile = file;
|
182 |
|
183 | file = files.shift();
|
184 | if (file) {
|
185 | outputDir = file;
|
186 | }
|
187 | } else {
|
188 | outputDir = file;
|
189 | }
|
190 | } catch (err) {
|
191 | outputDir = file;
|
192 | }
|
193 |
|
194 | if (files.length) {
|
195 | console.error(LONG_USAGE);
|
196 | process.exit(3);
|
197 | return true;
|
198 | }
|
199 | } else {
|
200 | console.error(LONG_USAGE);
|
201 | process.exit(2);
|
202 | }
|
203 |
|
204 | plugins = plugins.map(function(plugin) {
|
205 | var path = plugin.path;
|
206 | var name = plugin.name;
|
207 | var options = plugin.options;
|
208 | try {
|
209 | var fn = require(path);
|
210 | } catch (err) {
|
211 | try {
|
212 | // Load from the current directory if not found in the normal lookup paths
|
213 | fn = require(process.cwd() + '/node_modules/' + path);
|
214 | } catch (err) {
|
215 | // Last case, attempt to load from the plugins dir
|
216 | fn = require('../lib/plugins/' + path);
|
217 | }
|
218 | }
|
219 |
|
220 | lumbar.plugin(name, 'function' != typeof fn ? fn : fn(options));
|
221 | return name;
|
222 | });
|
223 |
|
224 | var options = {
|
225 | verbose: verbose,
|
226 |
|
227 | libraries: libraries,
|
228 | packageConfigFile: configFile,
|
229 | minimize: minimize,
|
230 | sourceMap: sourceMap,
|
231 | outdir: outputDir,
|
232 | plugins: plugins
|
233 | };
|
234 |
|
235 | // invoke init() from ../lib/lumbar.js.
|
236 | var arise = lumbar.init(lumbarFile, options),
|
237 | // is worker going to point to watch or build func?
|
238 | worker = watch ? arise.watch : arise.build,
|
239 | lastMessage,
|
240 | watchOutput = {},
|
241 | growlList = [],
|
242 | osType = require('os').type();
|
243 |
|
244 | function safeGrowl(msg, options) {
|
245 | if (require('os').type().toLowerCase().match(/^windows/)) {
|
246 | return;
|
247 | }
|
248 | try {
|
249 | growl(msg, options);
|
250 | } catch (err) {
|
251 | if (err.errno === 'EMFILE') {
|
252 | _.defer(safeGrowl, msg, options);
|
253 | } else {
|
254 | throw err;
|
255 | }
|
256 | }
|
257 | }
|
258 |
|
259 | var pingGrowl = _.debounce(function() {
|
260 | safeGrowl('compiled: ' + growlList.map(function(file) { return file.substring(options.outdir.length); }).join('\n\t'), {title: 'Lumbar'});
|
261 | growlList = [];
|
262 | }, 1000);
|
263 |
|
264 | arise.on('watch-change', function(status) {
|
265 | if (!watchOutput[status.fileName]) {
|
266 | console.log('\t\033[90mchanged\033[0m ' + status.fileName);
|
267 | watchOutput[status.fileName] = true;
|
268 | }
|
269 | });
|
270 | arise.on('log', function(log) {
|
271 | if (verbose) {
|
272 | console.log(log);
|
273 | }
|
274 | });
|
275 | arise.on('output', function(status) {
|
276 | if (status.error) {
|
277 | return;
|
278 | }
|
279 |
|
280 | watchOutput = {};
|
281 | if (status.watch) {
|
282 | console.log('\033[90mwatching\033[0m ' + status.fileName);
|
283 | } else {
|
284 | console.log('\033[90mcompiled\033[0m ' + status.fileName);
|
285 | }
|
286 | if (status.warnings && status.warnings.length) {
|
287 | if (watch) {
|
288 | safeGrowl(status.warnings.length + ' warnings in ' + status.fileName, { title: 'Lumbar warnings', sticky: true });
|
289 | }
|
290 | console.log('\t\033[91m' + status.warnings.length + ' warnings\033[0m');
|
291 | _.each(status.warnings, function(warning) {
|
292 | console.log('\t\033[91mwarning\033[0m ' + warning.msg);
|
293 | console.log('\t\tat ' + warning.file + ':' + warning.line + (warning.column ? ',' + warning.column : ''));
|
294 | _.each(warning.context, function(line) {
|
295 | console.log('\t' + line);
|
296 | });
|
297 | console.log();
|
298 | });
|
299 | }
|
300 |
|
301 | if (watch) {
|
302 | growlList.push(status.fileName);
|
303 | pingGrowl();
|
304 | }
|
305 | });
|
306 | arise.on('error', function(err) {
|
307 | if ((err.source || err).code === 'EMFILE') {
|
308 | throw err.source || err;
|
309 | }
|
310 |
|
311 | var message = err.stack || err.message;
|
312 | if (lastMessage !== message) {
|
313 | lastMessage = message;
|
314 |
|
315 | console.error(message);
|
316 | try {
|
317 | safeGrowl(err.message, { title: 'Lumbar error', sticky: true });
|
318 | } catch (err) {
|
319 | console.log(err);
|
320 | throw err;
|
321 | }
|
322 | }
|
323 | });
|
324 |
|
325 | // execute either watch() or build() and pass lumbar as the context.
|
326 | worker.call(arise, packageName, modules, function(err, status) {
|
327 | if (err) {
|
328 | throw err;
|
329 | }
|
330 | });
|