noda
Version:
NOde Developing Assistant
384 lines (339 loc) • 11.1 kB
JavaScript
/**
* Node Developing Assistant.
* @author youngoat@163.com
*/
;
const MODULE_REQUIRE = 1
/* built-in */
, fs = require('fs')
, os = require('os')
, path = require('path')
, util = require('util')
/* NPM */
/* in-package */
, findInDirectory = require('./lib/findInDirectory')
, getCaller = require('./lib/getCaller')
, getCallerPackageDir = require('./lib/getCallerPackageDir')
, getCallerDir = () => path.dirname(getCaller(1).filename)
;
/**
* Return the package.json object of the package in which the caller is located.
* @return {object}
*/
let currentPackage = function() {
// Find home directory of the package in which the caller is located.
let dirname = getCallerPackageDir();
// Return the meta json of the package.
return require(dirname + '/package.json');
};
/**
* Whether file/directory exists in the package in which the caller is located.
* @param {string} subpath path relative to the homedir of current package
* @param {boolean} resolveAsModule try to resolve the subpath as it is a module
* @return {boolean}
*/
let inExists = function(subpath, resolveAsModule) {
if (arguments.length == 1) {
resolveAsModule = false;
}
// Find home directory of the package in which the caller is located.
let dirname = getCallerPackageDir();
let pathname = path.join(dirname, subpath);
let ret;
if (resolveAsModule) {
ret = fs.existsSync(pathname)
|| fs.existsSync(`${pathname}.js`)
|| fs.existsSync(`${pathname}.json`)
|| fs.existsSync(path.join(pathname, 'index.js'))
|| fs.existsSync(path.join(pathname, 'index.json'))
;
}
else {
ret = fs.existsSync(pathname);
}
return ret;
};
/**
* Read file in the package in which the caller is located.
* @param {string} subpath path relative to the homedir of current package
* @param {string} [encoding]
* @param {boolean} [nullIfNotFound]
* @return {string|Buffer}
*/
let inRead = function(subpath, encoding, nullIfNotFound) {
if (arguments.length == 3) {
// DO NOTHING.
}
else if (arguments.length == 2) {
if (typeof arguments[1] == 'boolean') {
encoding = null;
nullIfNotFound = arguments[1];
}
else if (typeof arguments[1] == 'string') {
encoding = arguments[1];
nullIfNotFound = false;
}
else {
throw new Error('The second argument should be a string (encoding) or boolean (nullIfNotFound) value.');
}
}
// Find home directory of the package in which the caller is located.
let dirname = getCallerPackageDir();
let pathname = path.join(dirname, subpath);
let ret = null;
if (!fs.existsSync(pathname)) {
if (!nullIfNotFound) {
throw new Error(`File not found: ${pathname}`);
}
}
else {
ret = fs.readFileSync(pathname, encoding);
}
return ret;
}
/**
* Read the contents of a directory.
* @param {string} subpath path relative to the homedir of current package
* @return {string[]}
*/
let inReaddir = function(subpath) {
// Find home directory of the package in which the caller is located.
let dirname = getCallerPackageDir();
let pathname = path.join(dirname, subpath);
return fs.readdirSync(pathname);
};
/**
* To require some sub module in same package with fixed subpath wherever the caller is located.
*
* @example
* // PACKAGE_HOMEDIR/lib/index.js
*
* // PACKAGE_HOMEDIR/foo.js
* noda.inRequire('lib');
*
* // PACKAGE_HOMEDIR/foo/bar.js
* noda.inRequire('lib');
*
* @param {string} subpath sub module's path relative to the home directory of the package in which the caller is located.
*/
let inRequire = function(subpath, nullIfNotFound) {
// Find home directory of the package in which the caller is located.
let dirname = getCallerPackageDir();
let pathname = path.join(dirname, subpath);
let mod = null;
try {
mod = require(pathname);
} catch (ex) {
if (nullIfNotFound && ex.code === 'MODULE_NOT_FOUND') {
mod = null;
}
else {
throw ex;
}
}
return mod;
};
/**
* Resolve the subpath into an absolute path.
* The subpath is relative to the home directory of the package in which the caller is located.
* @param {string} subpath subpath relative to the home directory of the package in which the caller is located.
*/
let inResolve = function(...subpath) {
// Find home directory of the package in which the caller is located.
let dirname = getCallerPackageDir();
return path.join.apply(path, [ dirname ].concat(subpath));
};
/**
* Require module whose name is same with the name of current platform.
* @param {string} dirname
*/
let osRequire = (dirname) => {
if (!path.isAbsolute(dirname)) {
dirname = path.resolve(getCallerDir(), dirname);
}
try {
return require(path.join(dirname, os.platform()));
}
catch (ex) {
if (ex.code === 'MODULE_NOT_FOUJND') {
throw new Error(`current platform not supported: ${os.platform()}`);
}
else {
throw ex;
}
}
};
/**
* Read the directory and require all javascript modules except those excluded.
* ATTENTION: Directory 'node_modules' is always excluded.
* @param {string} dirname
* @param {Array} [excludes = ['index']] names to be excluded(ignored)
* @param {string} [exlucdes] the only name to be excluded(ingored)
* @return {Object}
*/
let requireDir = (dirname, excludes) => {
if (!path.isAbsolute(dirname)) {
dirname = path.resolve(getCallerDir(), dirname);
}
// Uniform the argument "excludes".
if (util.isUndefined(excludes)) {
excludes = [ 'index' ];
}
else if (util.isString(excludes)) {
excludes = [ excludes ];
}
excludes = excludes.map(exclude => exclude.replace(/\.js$/, ''));
// Iterate the directory and require sub modules.
let mod = {};
fs.readdirSync(dirname).forEach((name) => {
let pathname = path.join(dirname, name);
let modname = null;
if (!excludes.includes['*'] && path.extname(name) === '.js') {
modname = name.replace(/\.js$/, '');
}
else if (fs.statSync(pathname).isDirectory()
&& !excludes.includes('*/')
&& fs.existsSync(path.join(pathname, 'index.js'))) {
modname = name;
}
if (modname && !excludes.includes(modname)) {
mod[modname] = require(pathname);
}
});
return mod;
};
/**
* Based on requireDir(), but the dirname is regarded as relative path to home directory of the package in which the caller is located.
*/
let inRequireDir = (dirname, excludes) => {
dirname = path.join(getCallerPackageDir(), dirname);
return requireDir(dirname, excludes);
};
/**
* Read file next to the file in which the caller is located.
* @param {string} subpath path relative to the directory of the caller file
* @param {string} [encoding]
* @param {boolean} [nullIfNotFound]
* @return {string|Buffer}
*/
let nextRead = function(subpath, encoding, nullIfNotFound) {
if (arguments.length == 3) {
// DO NOTHING.
}
else if (arguments.length == 2) {
if (typeof arguments[1] == 'boolean') {
encoding = null;
nullIfNotFound = arguments[1];
}
else if (typeof arguments[1] == 'string') {
encoding = arguments[1];
nullIfNotFound = false;
}
else {
throw new Error('The second argument should be a string (encoding) or boolean (nullIfNotFound) value.');
}
}
// Find the directory of the file in which the caller is located.
let dirname = getCallerDir();
let pathname = path.join(dirname, subpath);
let ret = null;
if (!fs.existsSync(pathname)) {
if (!nullIfNotFound) {
throw new Error(`File not found: ${pathname}`);
}
}
else {
ret = fs.readFileSync(pathname, encoding);
}
return ret;
}
/**
* Find sub-directory or file in ascent directory and return the full path.
* @param {string} pathname relative pathname of sub-directory or file
* @return {string}
*/
let upResolve = (pathname) => {
let cwd = getCallerDir();
let realPathname = null;
for (let d = null; d != cwd && !realPathname; cwd = path.dirname(cwd)) {
let p = path.join(cwd, pathname);
if (fs.existsSync(p)) realPathname = p;
else d = cwd;
}
return realPathname;
};
/**
* Find sub-directory or file in descent directory and return the full path.
* @param {string} pathname - relative pathname of sub-directory or file
* @param {number} [depth=9] - max depth to search
* @param {string} [order=bfs] - DFS (Depth-First Search) or BFS (Breadth-First Search)
* @return {string}
*/
let downResolve = function(pathname) {
let depth = null, order = null;
for (let i = 1, arg; i < arguments.length; i++) {
switch (typeof arguments[i]) {
case 'number':
if (depth === null) depth = arguments[i];
else throw new Error(`duplicated arguments: {number} depth`);
break;
case 'string':
if (order === null) order = arguments[i];
else throw new Error(`duplicated arguments: {string} order`);
break;
default:
if (!util.isUndefined(arguments[i]) && arguments[i] !== null) {
throw new Error(`unrecognized argument: ${arguments[i]}`);
}
}
}
if (depth === null) depth = 9;
if (order === null) order = 'bfs';
order = order.toLowerCase();
let cwd = getCallerDir();
if (order == 'dfs') {
return findInDirectory.depth_first(cwd, pathname, depth);
}
if (order == 'bfs') {
return findInDirectory.breadth_first(cwd, pathname, depth);
}
throw new Error(`invalid order: ${order}`);
};
/**
* Return the root path of current package.
*/
let packageRoot = function() {
return inResolve('.');
};
/**
* Return dirname, filename and lineno of the caller.
* @param {int} [depth=0]
*/
let whereami = function(depth) {
if (arguments.length === 0) depth = 0;
return getCallerPosition(depth);
};
module.exports = {
bindings: require('./bindings'),
packageOf: require('./packageOf'),
currentPackage,
inExists,
inRead,
inReaddir,
inRequire,
inRequireDir,
inResolve,
nextRead,
upResolve,
downResolve,
// Hold, not released.
// whereami,
osRequire,
requireDir,
count: require('./count'),
'existsInPackage': inExists,
'readInPackage': inRead,
'requireInPackage': inRequire,
'requireDirInPackage': inRequireDir,
'resolveInPackage': inResolve,
};