/**
* @fileOverview project-dir helper module
* @name path.js
* @author Dongsoo Lee <mrlee_23@naver.com>
* @copyright 2018 Dongsoo Lee <mrlee_23@naver.com>
* @module lib/path
* @version 0.1.4
* @since 0.0.1
* @created 2017-12-26
*
* @requires module:path
* @requires module:fs
* @requires module:lib/typeCheck.js
*
*/
const path = require('path'),
fs = require('fs'),
type = require('./typeCheck.js');
/**
* @class
* @name Path
* @classdesc path main class
* @version 0.1.4
* @since 0.0.1
* @created 2017-12-26
*/
let Path = {
__proto__: path.__proto__
};
Object.getOwnPropertyNames(path).forEach(function (key) {
Object.defineProperty(Path, key, Object.getOwnPropertyDescriptor(path, key));
});
Object.assign(Path, {
/**
* @public
* @instance
* @function locateBase:
* @version 0.1.4
* @since 0.0.1
* @created 2017-12-26
* @memberof module:lib/path~Path
* @description Search base locate
*
* @param {string} _path - Starting point
* @param {query} regexp - If matched, will be break.
* @throws {Error} path does not exists.
* @returns {string} base path or "/"
*/
locateBase: function (_path, regexp) {
if (!fs.existsSync(_path)) throw new Error(`_path(${_path}) does not exist.`);
return this.toRoot(_path, (basedir, nodes) => {
return nodes.some(node => node.match(regexp));
});
},
/**
* @public
* @instance
* @function toRoot
* @version 0.1.4
* @since 0.0.1
* @created 2017-12-26
* @memberof module:lib/path~Path
* @description goto root if no breaks.
*
* @param {string} _path - Starting path
* @param {Object} options
* @param {boolean} options.resolve=true - if true, giving path will be resolved,
* @param {boolean} options.reliable=false - if true, check all directory is exists.
* @param {Function} options.checker=type.isRoot - Check break loop or not, break when it returns true.
* @param {Function} callback - If this callback function returns true, loop will be break.
* @throws {Error} Callback or path(options.reliable == true) does not exist.
* @returns {string} breaked path. If no breaks returns "/"
*
*/
toRoot: function (_path, ...args) {
let callback = args.pop(),
options = args[0] || {};
options = Object.assign({
resolve: true,
reliable: false,
checker: type.isRoot
}, options);
if (options.reliable && !fs.existsSync(_path)) throw new Error(`_path(${_path}) does not exist.`);
if (typeof callback !== 'function') throw new Error(`The callback function does not set.`);
options.resolve && (_path = path.resolve(_path));
while (!options.checker(_path)) {
if (options.reliable)
!fs.lstatSync(_path).isDirectory() && (_path = path.dirname(_path));
let nodes = fs.existsSync(_path) ? fs.readdirSync(_path) : [];
if (callback(_path, nodes) == true) break; // to exit when callback returns true.
_path = path.join(_path, '../');
}
return _path;
},
/**
* @public
* @instance
* @function fromRoot
* @version 0.1.4
* @since 0.1.4
* @created 2018-02-01
* @memberof module:lib/path~Path
* @description goto _path from root if no breaks.
*
* @param {string} _path - End point of path.
* @param {Object} options
* @param {boolean} options.reliable=false - if true, check all directory is exists.
* @param {Function} options.start='/' - Set start directory. It treats as root path.
* @param {Function} options.checker=type.isRoot - Check break loop or not, break when it returns true.
* @param {Function} callback - If this callback function returns true, loop will be break.
* @throws {Error} Callback or path(options.reliable == true) does not exist.
* @returns {string} breaked path. If no breaks returns _path
*
*/
fromRoot: function (_path, ...args) {
let callback = args.pop(),
options = args[0] || {},
orgPath = _path;
options = Object.assign({
reliable: false,
start: '/',
checker: (_p) => this.equal(_p, orgPath)
}, options);
if (typeof callback !== 'function') throw new Error(`The callback function does not set.`);
_path = path.resolve(_path);
let pathArr = path.relative(options.start, _path).split(path.sep);
pathArr.reverse();
let curPath = options.start;
while (!options.checker(curPath)) {
if (options.reliable)
!fs.lstatSync(curPath).isDirectory() && (curPath = path.dirname(curPath));
let nodes = fs.existsSync(curPath) ? fs.readdirSync(curPath) : [];
if (callback(curPath, nodes) == true) break; // to exit when callback returns true.
let popPath = pathArr.pop();
if (popPath == null) break;
curPath = path.join(curPath, popPath);
}
if (options.reliable && !fs.existsSync(curPath)) throw new Error(`curPath(${curPath}) does not exist.`);
return curPath;
},
/**
* @public
* @instance
* @function equal
* @version 0.1.4
* @since 0.0.1
* @created 2017-12-26
* @memberof module:lib/path~Path
* @description Check two paths are equal.
*
* @param {string} path1 - to check path
* @param {string} path2 - to check path
* @returns {boolean} If paths are equal, returns true.
*/
equal: function (path1, path2) {
return path.relative(path1, path2).length == 0;
},
/**
* @public
* @instance
* @function isParent
* @version 0.1.4
* @since 0.0.1
* @created 2017-12-26
* @memberof module:lib/path~Path
* @description Check path1 is parent of path2
*
* @param {string} path1 - maybe parent
* @param {string} path2 - maybe child
* @returns {boolean} If path1 is parent of path2, returns true.
*/
isParent: function (path1, path2) {
let self = this;
return type.isRoot(this.toRoot(path.dirname(path2), p => {
return self.equal(path1, p);
})) != true;
}
});
module.exports = Path;