index.js
6.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/*
* grunt
* http://gruntjs.com/
*
* Copyright (c) 2016 "Cowboy" Ben Alman
* Licensed under the MIT license.
* https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
*/
'use strict';
// Nodejs libs.
var spawn = require('child_process').spawn;
var nodeUtil = require('util');
var path = require('path');
// The module to be exported.
var util = module.exports = {};
util.namespace = require('getobject');
// External libs.
util.hooker = require('hooker');
util.async = require('async');
var _ = util._ = require('lodash');
var which = require('which').sync;
// Instead of process.exit. See https://github.com/cowboy/node-exit
util.exit = require('exit');
// Mixin Underscore.string methods.
_.str = require('underscore.string');
_.mixin(_.str.exports());
// Return a function that normalizes the given function either returning a
// value or accepting a "done" callback that accepts a single value.
util.callbackify = function(fn) {
return function callbackable() {
// Invoke original function, getting its result.
var result = fn.apply(this, arguments);
// If the same number or less arguments were specified than fn accepts,
// assume the "done" callback was already handled.
var length = arguments.length;
if (length === fn.length) { return; }
// Otherwise, if the last argument is a function, assume it is a "done"
// callback and call it.
var done = arguments[length - 1];
if (typeof done === 'function') { done(result); }
};
};
// Create a new Error object, with an origError property that will be dumped
// if grunt was run with the --debug=9 option.
util.error = function(err, origError) {
if (!nodeUtil.isError(err)) { err = new Error(err); }
if (origError) { err.origError = origError; }
return err;
};
// The line feed char for the current system.
util.linefeed = process.platform === 'win32' ? '\r\n' : '\n';
// Normalize linefeeds in a string.
util.normalizelf = function(str) {
return str.replace(/\r\n|\n/g, util.linefeed);
};
// What "kind" is a value?
// I really need to rework https://github.com/cowboy/javascript-getclass
var kindsOf = {};
'Number String Boolean Function RegExp Array Date Error'.split(' ').forEach(function(k) {
kindsOf['[object ' + k + ']'] = k.toLowerCase();
});
util.kindOf = function(value) {
// Null or undefined.
if (value == null) { return String(value); }
// Everything else.
return kindsOf[kindsOf.toString.call(value)] || 'object';
};
// Coerce something to an Array.
util.toArray = _.toArray;
// Return the string `str` repeated `n` times.
util.repeat = function(n, str) {
return new Array(n + 1).join(str || ' ');
};
// Given str of "a/b", If n is 1, return "a" otherwise "b".
util.pluralize = function(n, str, separator) {
var parts = str.split(separator || '/');
return n === 1 ? (parts[0] || '') : (parts[1] || '');
};
// Recurse through objects and arrays, executing fn for each non-object.
util.recurse = function(value, fn, fnContinue) {
function recurse(value, fn, fnContinue, state) {
var error;
if (state.objs.indexOf(value) !== -1) {
error = new Error('Circular reference detected (' + state.path + ')');
error.path = state.path;
throw error;
}
var obj, key;
if (fnContinue && fnContinue(value) === false) {
// Skip value if necessary.
return value;
} else if (util.kindOf(value) === 'array') {
// If value is an array, recurse.
return value.map(function(item, index) {
return recurse(item, fn, fnContinue, {
objs: state.objs.concat([value]),
path: state.path + '[' + index + ']',
});
});
} else if (util.kindOf(value) === 'object' && !Buffer.isBuffer(value)) {
// If value is an object, recurse.
obj = {};
for (key in value) {
obj[key] = recurse(value[key], fn, fnContinue, {
objs: state.objs.concat([value]),
path: state.path + (/\W/.test(key) ? '["' + key + '"]' : '.' + key),
});
}
return obj;
} else {
// Otherwise pass value into fn and return.
return fn(value);
}
}
return recurse(value, fn, fnContinue, {objs: [], path: ''});
};
// Spawn a child process, capturing its stdout and stderr.
util.spawn = function(opts, done) {
// Build a result object and pass it (among other things) into the
// done function.
var callDone = function(code, stdout, stderr) {
// Remove trailing whitespace (newline)
stdout = _.rtrim(stdout);
stderr = _.rtrim(stderr);
// Create the result object.
var result = {
stdout: stdout,
stderr: stderr,
code: code,
toString: function() {
if (code === 0) {
return stdout;
} else if ('fallback' in opts) {
return opts.fallback;
} else if (opts.grunt) {
// grunt.log.error uses standard out, to be fixed in 0.5.
return stderr || stdout;
}
return stderr;
}
};
// On error (and no fallback) pass an error object, otherwise pass null.
done(code === 0 || 'fallback' in opts ? null : new Error(stderr), result, code);
};
var cmd, args;
var pathSeparatorRe = /[\\\/]/g;
if (opts.grunt) {
cmd = process.execPath;
args = process.execArgv.concat(process.argv[1], opts.args);
} else {
// On Windows, child_process.spawn will only file .exe files in the PATH,
// not other executable types (grunt issue #155).
try {
if (!pathSeparatorRe.test(opts.cmd)) {
// Only use which if cmd has no path component.
cmd = which(opts.cmd);
} else {
cmd = opts.cmd.replace(pathSeparatorRe, path.sep);
}
} catch (err) {
callDone(127, '', String(err));
return;
}
args = opts.args || [];
}
var child = spawn(cmd, args, opts.opts);
var stdout = new Buffer('');
var stderr = new Buffer('');
if (child.stdout) {
child.stdout.on('data', function(buf) {
stdout = Buffer.concat([stdout, new Buffer(buf)]);
});
}
if (child.stderr) {
child.stderr.on('data', function(buf) {
stderr = Buffer.concat([stderr, new Buffer(buf)]);
});
}
child.on('close', function(code) {
callDone(code, stdout.toString(), stderr.toString());
});
return child;
};