UNPKG

glov-build

Version:
220 lines (204 loc) 5.71 kB
exports.asyncLoader = asyncLoader; exports.asyncSaver = asyncSaver; exports.callbackify = callbackify; exports.unpromisify = unpromisify; exports.cmpTaskPhase = cmpTaskPhase; exports.merge = merge; exports.empty = empty; exports.writeFileWithMkdir = writeFileWithMkdir; exports.deleteFileWithRmdir = deleteFileWithRmdir; exports.forwardSlashes = forwardSlashes; exports.ridx = ridx; const assert = require('assert'); const fs = require('fs'); const path = require('path'); function merge(dest, src) { for (let key in src) { let value = src[key]; if (typeof value === 'object') { value = merge(dest[key] || {}, value); } dest[key] = value; } return dest; } function empty(obj) { for (let key in obj) { return false; } return true; } function callbackify(f) { return function () { let cb = arguments[arguments.length - 1]; // eslint-disable-line prefer-rest-params assert.equal(typeof cb, 'function'); let args = Array.prototype.slice.call(arguments, 0, -1); // eslint-disable-line prefer-rest-params let p = f.apply(this, args); // eslint-disable-line no-invalid-this p.then((result) => { if (cb) { // escape promise so it doesn't catch and re-throw the error! process.nextTick(cb.bind(this, null, result)); // eslint-disable-line no-invalid-this cb = null; } }).catch((err) => { if (cb) { process.nextTick(cb.bind(this, err)); // eslint-disable-line no-invalid-this cb = null; } }); }; } // Escape implicit try/catches from callbacks fired within Promises function unpromisify(f) { return function () { // eslint-disable-next-line no-invalid-this, prefer-rest-params process.nextTick(f.apply.bind(f, this, arguments)); }; } function cmpTaskPhase(a, b) { return (b.phase - a.phase) || (a.uid - b.uid); } let mkdir_in_progress = {}; function writeFileWithMkdir(gb, disk_path, contents, next) { fs.writeFile(disk_path, contents, function (err) { function finishWritefile() { // ignore error fs.writeFile(disk_path, contents, next); } if (err && (err.code === 'ENOENT' || err.code === 'EPERM')) { // Containing directory probably doesn't exist let dirname = path.dirname(disk_path); let cbs = mkdir_in_progress[dirname]; if (cbs) { cbs.push(finishWritefile); } else { cbs = mkdir_in_progress[dirname] = [finishWritefile]; gb.debug(`Directory "${dirname}" does not exist, creating...`); fs.mkdir(dirname, { recursive: true }, function () { assert.equal(cbs, mkdir_in_progress[dirname]); delete mkdir_in_progress[dirname]; for (let ii = 0; ii < cbs.length; ++ii) { cbs[ii](); } }); } } else { next(err); } }); } function rmdirtree(dir, next) { // just try to remove the directory - should maybe stat it first and check contents // if it's a symlink? fs.rmdir(dir, function (err) { if (err) { // probably not empty next(); } else { let parent = path.dirname(dir); if (parent) { rmdirtree(parent, next); } else { next(); } } }); } function deleteFileWithRmdir(disk_path, next) { fs.unlink(disk_path, function (err1) { if (err1 && err1.code === 'ENOENT') { // already gone err1 = null; } rmdirtree(path.dirname(disk_path), function () { next(err1); }); }); } // function AsyncLoader(load_fn) { // this.load_cbs = []; // this.load_fn = load_fn; // this.err = null; // this.value = null; // this.load_fn(this.onLoad.bind(this)); // } // AsyncLoader.prototype.onLoad = function (err, value) { // let cbs = this.load_cbs; // this.load_cbs = null; // this.err = err; // this.value = value; // for (let ii = 0; ii < cbs.length; ++ii) { // cbs[ii](err, value); // } // }; // AsyncLoader.prototype.load = function (cb) { // if (!this.load_cbs) { // return cb(this.err, this.value); // } // this.load_cbs.push(cb); // }; // function asyncLoader(load_fn) { // let loader = new AsyncLoader(load_fn); // return loader.load.bind(loader); // } function asyncLoader(load_fn) { const sym_load_cbs = Symbol('load_cbs'); const sym_loaded = Symbol('loaded'); const sym_err = Symbol('err'); function load(obj) { load_fn(obj, function (err) { obj[sym_err] = err; let cbs = obj[sym_load_cbs]; obj[sym_loaded] = true; obj[sym_load_cbs] = null; for (let ii = 0; ii < cbs.length; ++ii) { cbs[ii](obj[sym_err], obj); } }); } function loader(obj, cb) { if (obj[sym_loaded]) { return cb(obj[sym_err], obj); } if (obj[sym_load_cbs]) { return obj[sym_load_cbs].push(cb); } obj[sym_load_cbs] = [cb]; load(obj); } loader.reset = function (obj, is_loaded, err) { assert(!obj[sym_load_cbs]); // load currently in progress obj[sym_err] = err; obj[sym_loaded] = Boolean(is_loaded); }; return loader; } function asyncSaver(save_fn) { const sym_saving = Symbol('saving'); function save(obj, cbs) { obj[sym_saving] = []; save_fn(obj, function (err) { for (let ii = 0; ii < cbs.length; ++ii) { cbs[ii](err); } if (obj[sym_saving].length) { save(obj, obj[sym_saving]); } else { delete obj[sym_saving]; } }); } return function (obj, cb) { if (obj[sym_saving]) { return obj[sym_saving].push(cb); } save(obj, [cb]); }; } function forwardSlashes(str) { return str.replace(/\\/g, '/'); } function ridx(arr, idx) { arr[idx] = arr[arr.length - 1]; arr.pop(); }