123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- var assert = require("assert");
- var path = require("path");
- var fs = require("graceful-fs");
- var Q = require("q");
- var createHash = require("crypto").createHash;
- var mkdirp = require("mkdirp");
- var iconv = require("iconv-lite");
- var Ap = Array.prototype;
- var slice = Ap.slice;
- var join = Ap.join;
- // The graceful-fs module attempts to limit the total number of open files
- // by queueing fs operations, but it doesn't know about all open files, so
- // we set the limit somewhat lower than the default to provide a healthy
- // buffer against EMFILE (too many open files) errors.
- fs.MAX_OPEN = 512;
- function makePromise(callback, context) {
- var deferred = Q.defer();
- function finish(err, result) {
- if (err) {
- deferred.reject(err);
- } else {
- deferred.resolve(result);
- }
- }
- process.nextTick(function() {
- try {
- callback.call(context || null, finish);
- } catch (err) {
- finish(err);
- }
- });
- return deferred.promise;
- }
- exports.makePromise = makePromise;
- exports.cachedMethod = function(fn, keyFn) {
- var p = require("private").makeAccessor();
- function wrapper() {
- var priv = p(this);
- var cache = priv.cache || (priv.cache = {});
- var args = arguments;
- var key = keyFn
- ? keyFn.apply(this, args)
- : join.call(args, "\0");
- return cache.hasOwnProperty(key)
- ? cache[key]
- : cache[key] = fn.apply(this, args);
- }
- wrapper.originalFn = fn;
- return wrapper;
- };
- function readFileP(file, charset) {
- return makePromise(charset ? function(callback) {
- return fs.readFile(file, function(err, data) {
- if (err) {
- callback(err);
- } else {
- callback(null, iconv.decode(data, charset));
- }
- });
- } : function(callback) {
- return fs.readFile(file, "utf8", callback);
- });
- }
- exports.readFileP = readFileP;
- exports.readJsonFileP = function(file) {
- return readFileP(file).then(function(json) {
- return JSON.parse(json);
- });
- };
- function mkdirP(dir) {
- return makePromise(function(callback) {
- mkdirp(dir, function(err) {
- callback(err, dir);
- });
- });
- }
- exports.mkdirP = mkdirP;
- function readFromStdinP(timeLimit, message, color) {
- var deferred = Q.defer();
- var ins = [];
- timeLimit = timeLimit || 1000;
- var timeout = setTimeout(function() {
- log.err(
- message || ("Warning: still waiting for STDIN after " +
- timeLimit + "ms"),
- color || "yellow"
- );
- }, timeLimit);
- try {
- // On Windows, just accessing process.stdin throws an exception
- // when no standard input has been provided. For consistency with
- // other platforms, log the error but continue waiting (until
- // killed) for the nonexistent input.
- var stdin = process.stdin;
- } catch (err) {
- log.err(err);
- }
- if (stdin) {
- stdin.resume();
- stdin.setEncoding("utf8");
- stdin.on("data", function(data) {
- ins.push(data);
- }).on("end", function() {
- clearTimeout(timeout);
- deferred.resolve(ins.join(""));
- });
- }
- return deferred.promise;
- }
- exports.readFromStdinP = readFromStdinP;
- exports.readJsonFromStdinP = function(timeLimit) {
- return readFromStdinP(timeLimit).then(function(input) {
- return JSON.parse(input);
- });
- };
- function deepHash(val) {
- var hash = createHash("sha1");
- var type = typeof val;
- if (val === null) {
- type = "null";
- }
- switch (type) {
- case "object":
- Object.keys(val).sort().forEach(function(key) {
- if (typeof val[key] === "function") {
- // Silently ignore nested methods, but nevertheless
- // complain below if the root value is a function.
- return;
- }
- hash.update(key + "\0")
- .update(deepHash(val[key]));
- });
- break;
- case "function":
- assert.ok(false, "cannot hash function objects");
- break;
- default:
- hash.update(val + "");
- break;
- }
- return hash.digest("hex");
- }
- exports.deepHash = deepHash;
- exports.existsP = function(fullPath) {
- return makePromise(function(callback) {
- fs.exists(fullPath, function(exists) {
- callback(null, exists);
- });
- });
- };
- function writeFdP(fd, content) {
- return makePromise(function(callback) {
- content += "";
- var buffer = new Buffer(content, "utf8");
- var length = fs.writeSync(fd, buffer, 0, buffer.length, null);
- assert.strictEqual(length, buffer.length);
- callback(null, content);
- }).finally(function() {
- fs.closeSync(fd);
- });
- }
- exports.writeFdP = writeFdP;
- function openFileP(file, mode) {
- return makePromise(function(callback) {
- fs.open(file, mode || "w+", callback);
- });
- }
- exports.openFileP = openFileP;
- function openExclusiveP(file) {
- // The 'x' in "wx+" means the file must be newly created.
- return openFileP(file, "wx+");
- }
- exports.openExclusiveP = openExclusiveP;
- exports.copyP = function(srcFile, dstFile) {
- return makePromise(function(callback) {
- var reader = fs.createReadStream(srcFile);
- function onError(err) {
- callback(err || new Error(
- "error in util.copyP(" +
- JSON.stringify(srcFile) + ", " +
- JSON.stringify(dstFile) + ")"
- ));
- }
- reader.on("error", onError).pipe(
- fs.createWriteStream(dstFile)
- ).on("finish", function() {
- callback(null, dstFile);
- }).on("error", onError);
- });
- };
- // Even though they use synchronous operations to avoid race conditions,
- // linkP and unlinkP have promise interfaces, for consistency. Note that
- // this means the operation will not happen until at least the next tick
- // of the event loop, but it will be atomic when it happens.
- exports.linkP = function(srcFile, dstFile) {
- return mkdirP(path.dirname(dstFile)).then(function() {
- if (fs.existsSync(dstFile))
- fs.unlinkSync(dstFile);
- fs.linkSync(srcFile, dstFile);
- return dstFile;
- });
- };
- exports.unlinkP = function(file) {
- return makePromise(function(callback) {
- try {
- if (fs.existsSync(file))
- fs.unlinkSync(file);
- callback(null, file);
- } catch (err) {
- callback(err);
- }
- });
- };
- var colors = {
- bold: "\033[1m",
- red: "\033[31m",
- green: "\033[32m",
- yellow: "\033[33m",
- cyan: "\033[36m",
- reset: "\033[0m"
- };
- Object.keys(colors).forEach(function(key) {
- if (key !== "reset") {
- exports[key] = function(text) {
- return colors[key] + text + colors.reset;
- };
- }
- });
- var log = exports.log = {
- out: function(text, color) {
- text = (text + "").trim();
- if (colors.hasOwnProperty(color))
- text = colors[color] + text + colors.reset;
- process.stdout.write(text + "\n");
- },
- err: function(text, color) {
- text = (text + "").trim();
- if (!colors.hasOwnProperty(color))
- color = "red";
- text = colors[color] + text + colors.reset;
- process.stderr.write(text + "\n");
- }
- };
- var slugExp = /[^a-z\-]/ig;
- exports.idToSlug = function(id) {
- return id.replace(slugExp, "_");
- };
- var moduleIdExp = /^[ a-z0-9\-_\/\.]+$/i;
- exports.isValidModuleId = function(id) {
- return id === "<stdin>" || moduleIdExp.test(id);
- };
- var objToStr = Object.prototype.toString;
- var arrStr = objToStr.call([]);
- function flatten(value, into) {
- if (objToStr.call(value) === arrStr) {
- into = into || [];
- for (var i = 0, len = value.length; i < len; ++i)
- if (i in value) // Skip holes.
- flatten(value[i], into);
- } else if (into) {
- into.push(value);
- } else {
- return value;
- }
- return into;
- };
- exports.flatten = flatten;
- exports.inherits = function(ctor, base) {
- return ctor.prototype = Object.create(base.prototype, {
- constructor: { value: ctor }
- });
- };
- function absolutize(moduleId, requiredId) {
- if (requiredId.charAt(0) === ".")
- requiredId = path.join(moduleId, "..", requiredId);
- return path.normalize(requiredId).replace(/\\/g, '/');
- }
- exports.absolutize = absolutize;
- function relativize(moduleId, requiredId) {
- requiredId = absolutize(moduleId, requiredId);
- if (requiredId.charAt(0) === ".") {
- // Keep the required ID relative.
- } else {
- // Relativize the required ID.
- requiredId = path.relative(
- path.join(moduleId, ".."),
- requiredId
- );
- }
- if (requiredId.charAt(0) !== ".")
- requiredId = "./" + requiredId;
- return requiredId.replace(/\\/g, '/');
- }
- exports.relativize = relativize;
- function waitForValuesP(obj, makeCopy) {
- if (typeof obj !== "object")
- return Q(obj);
- var result = makeCopy ? {} : obj;
- var keys = Object.keys(obj);
- if (keys.length === 0)
- return Q(result);
- return Q.all(keys.map(function(key) {
- return obj[key];
- })).then(function(values) {
- for (var i = values.length - 1; i >= 0; --i)
- result[keys[i]] = values[i];
- return result;
- });
- }
- exports.waitForValuesP = waitForValuesP;
- function camelize(hyphenated) {
- return hyphenated.replace(/-(.)/g, function(_, ch) {
- return ch.toUpperCase();
- });
- }
- exports.camelize = camelize;
|