@serpent/common-cli
Version:
通用的 cli 相关的函数
678 lines (634 loc) • 20.3 kB
JavaScript
import fs from 'fs';
import path from 'path';
import { v as mkdirp, h as existsFile, p as toOSPath, q as toPosixPath, g as exists, k as existsDir, r as rm, m as mkdirp$1 } from './context-96dcd180.mjs';
import { g as getDefaultExportFromCjs } from './_commonjsHelpers-7d1333e8.mjs';
/**
* 将内容写入到指定的路径中
* @return 文件是否写入成功(不成功可能是因为文件没变化)
*/
function writeFile(absPath, content, options) {
if (options === void 0) { options = { writeWhenChanged: true }; }
mkdirp(path.dirname(absPath));
if (!options.writeWhenChanged || !equals(absPath, content)) {
fs.writeFileSync(absPath, content, options);
return true;
}
return false;
}
function equals(absPath, content) {
if (!existsFile(absPath))
return false;
var buffer = fs.readFileSync(absPath);
if (typeof content === 'string')
return buffer.toString() === content;
return buffer.equals(content);
}
/**
* @module libs/lang/isObject
* @createdAt 2016-07-01
*
* @copyright Copyright (c) 2016 Zhonglei Qiu
* @license Licensed under the MIT license.
*/
/**
* 判断 any 是不是一个 JS Object
*
* 除了 null, 及字面量,其它一般都是 Object,包括 函数
*
* @param {*} any 任何的 JS 类型
* @return Boolean
*
* @see [is-obj@1.0.1]{@link https://github.com/sindresorhus/is-obj/tree/v1.0.1}
* @author Zhonglei Qiu
* @since 2.0.0
*/
var isObject$2 = function(any) {
var type = typeof any;
return any !== null && (type === 'object' || type === 'function')
};
/**
* @module libs/lang/hasOwnEnumProp
* @createdAt 2016-07-01
*
* @copyright Copyright (c) 2016 Zhonglei Qiu
* @license Licensed under the MIT license.
*/
var isObject$1 = isObject$2;
/**
* 判断 obj 对象是否含有某个 key,且 key 需要 enumerable ( 利用 getOwnPropertyDescriptor )
*
* 另外也可以使用 Object.prototype.propertyIsEnumerable.call(obj, key)
*
* @param {Object} obj
* @param {String} key
* @return {Boolean}
*
* @author Zhonglei Qiu
* @since 2.0.0
*/
var hasOwnEnumProp$1 = function(obj, key) {
// 如果不做判断 node v0.12 会报 TypeError: Object.getOwnPropertyDescriptor called on non-object
// 但高版本的都不会报错
if (!isObject$1(obj)) return false
// https://github.com/sindresorhus/dot-prop/issues/23
// 可以过滤掉原生的像 hasOwnProperty 这样的 key
var descriptor = Object.getOwnPropertyDescriptor(obj, key);
return !!(descriptor && descriptor.enumerable)
};
/**
* @module libs/lang/DotProp
* @createdAt 2016-07-01
*
* @copyright Copyright (c) 2016 Zhonglei Qiu
* @license Licensed under the MIT license.
*/
var hasOwnEnumProp = hasOwnEnumProp$1;
var isObject = isObject$2;
/**
* 创建一个对象,使你可以通过 get/set/has/del 的方法来对其值
* 做增删改查的操作,并且路径名支持 . 链接
*
* @class
* @param {Objet} data 要操作的数据池
*
* @example
* var dp = new DotProp({foo: {bar: 1}})
* dp.has('foo.bar') // true
* dp.get('foo') // {bar: 1}
* dp.set('x', 'x') // true
* dp.del('foo') // true
* dp.data // {x: 'x'}
*
*
* @see [dot-prop@3.0.0]{@link https://github.com/sindresorhus/dot-prop/tree/v3.0.0}
* @author Zhonglei Qiu
* @since 2.0.0
*/
function DotProp(data) {
if (!(this instanceof DotProp)) return new DotProp(data)
this.data = Object(data);
}
/**
* 获取数据池中路径为 path 的值
* @param {String} path 路径
* @return {*}
*
* @author Zhonglei Qiu
* @since 2.0.0
*/
DotProp.prototype.get = function(path) {
return DotProp.get(this.data, path)
};
/**
* 设置数据池中路径为 path 的值,如果中间的路径不存在,
* 或者不为 Object,则自动添加或修改成 Object
*
* @param {String} path 路径
* @param {String} value 要设置的值
* @return {Boolean} 是否设置成功(当 p 不为字符串时设置不成功)
*
* @author Zhonglei Qiu
* @since 2.0.0
*/
DotProp.prototype.set = function(path, value) {
return DotProp.set(this.data, path, value)
};
/**
* 判断数据池中是否有路径 path
*
* @param {String} path 路径
* @return {Boolean}
*
* @author Zhonglei Qiu
* @since 2.0.0
*/
DotProp.prototype.has = function(path) {
return DotProp.has(this.data, path)
};
/**
* 删除数据池中的路径上的值
* @param {String} path 路径
* @return {Boolean} 是否删除成功
*
* @author Zhonglei Qiu
* @since 2.0.0
*/
DotProp.prototype.del = function(path) {
return DotProp.del(this.data, path)
};
/**
* 判断数据池 obj 中是否有路径 path
*
* @static
* @param {Object} obj 数据池
* @param {String} path 路径
* @return {Boolean}
*
* @author Zhonglei Qiu
* @since 2.0.0
*/
DotProp.has = function(obj, path) {
if (!isObject(obj) || typeof path !== 'string') {
return false
}
return find(obj, getPathSegments(path), function(data) {
if (data.isDrained) {
return {next: false, value: false}
} else if (data.isLast) {
return {next: false, value: true}
} else {
return {next: true}
}
})
};
/**
* 获取数据池 obj 中的路径 path 中的值
*
* @static
* @param {Object} obj 数据池
* @param {String} path 路径
* @return {*} 获取到的值或者 `undefined`
*
* @author Zhonglei Qiu
* @since 2.0.0
*/
DotProp.get = function(obj, path) {
if (!isObject(obj) || typeof path !== 'string') {
return obj
}
return find(obj, getPathSegments(path), function(data) {
if (data.isDrained) {
return {next: false, value: undefined}
} else if (data.isLast) {
return {next: false, value: data.current}
} else {
return {next: true}
}
})
};
/**
* 删除数据池 obj 中的路径 path
*
* @static
* @param {Object} obj 数据池
* @param {String} path 路径
* @return {Boolean} 是否删除成功
*
* @author Zhonglei Qiu
* @since 2.0.0
*/
DotProp.del = function(obj, path) {
if (!isObject(obj) || typeof path !== 'string') {
return false
}
return find(obj, getPathSegments(path), function(data) {
if (data.isDrained) {
return {next: false, value: false}
} else if (data.isLast) {
delete data.parent[data.segment];
return {next: false, value: true}
} else {
return {next: true}
}
})
};
/**
* 设置数据池 obj 中路径 path 的值为 value
*
* @static
* @param {Object} obj 数据池
* @param {String} path 路径
* @param {*} value 要设置的值
* @return {Boolean} 是否设置成功
*
* @author Zhonglei Qiu
* @since 2.0.0
*/
DotProp.set = function(obj, path, value) {
if (!isObject(obj) || typeof path !== 'string') {
return false
}
return find(obj, getPathSegments(path), function(data) {
var current = data.current;
var segment = data.segment;
if (data.isLast) {
data.parent[segment] = value;
return {next: false, value: true}
}
if (!isObject(current)) {
current = data.parent[segment] = {};
}
return {next: true, current: current}
})
};
function find(obj, segments, fn) {
var len = segments.length;
var parent = null;
var current = obj;
var isDrained = false;
var i, segment, fnRtn, result;
for (i = 0; i < len; i++) {
segment = segments[i];
parent = current;
if (hasOwnEnumProp(current, segment)) {
current = current[segment];
} else {
current = null;
isDrained = true;
}
fnRtn = fn({
isLast: i === len - 1,
isDrained: isDrained,
segment: segment,
parent: parent,
current: current
});
result = fnRtn.value;
if (!fnRtn.next) break
else if ('current' in fnRtn) current = fnRtn.current; // 回调更新了 current 对象
}
return result
}
function getPathSegments(path) {
var p;
var parts = [];
var pathArr = path.split('.');
for (var i = 0; i < pathArr.length; i++) {
p = pathArr[i];
while (p[p.length - 1] === '\\' && pathArr[i + 1] !== undefined) {
p = p.slice(0, -1) + '.';
p += pathArr[++i];
}
parts.push(p);
}
return parts
}
var DotProp_1 = DotProp;
var DotProp$1 = /*@__PURE__*/getDefaultExportFromCjs(DotProp_1);
var JsonFile = /** @class */ (function () {
function JsonFile(filepath, fileNotExistsValue) {
this.filepath = filepath;
this.formatEOF = '\n';
this.formatTAB = ' ';
/** 标识是否有更新过任意属性,仅限于使用了 get/set/add/del/data 操作,其它操作无法监控到 */
this.touched = false;
var data = fileNotExistsValue;
if (existsFile(filepath)) {
var content = fs.readFileSync(filepath).toString();
var _a = parseJsonContentFormat(content), eof = _a.eof, tab = _a.tab;
this.formatEOF = eof;
this.formatTAB = tab;
data = JSON.parse(content);
}
else if (data == null) {
throw new Error("File ".concat(filepath, " not exists. if this is expected, please specify \"fileNotExistsValue\" value"));
}
this.dp = new DotProp$1(data);
}
Object.defineProperty(JsonFile.prototype, "data", {
get: function () {
return this.dp.data;
},
set: function (value) {
this.touched = true;
this.dp.data = value;
},
enumerable: false,
configurable: true
});
Object.defineProperty(JsonFile.prototype, "content", {
get: function () {
return JSON.stringify(this.dp.data, null, this.formatTAB) + this.formatEOF;
},
enumerable: false,
configurable: true
});
/**
* 获取指定路径上的值,如果不存在,返回 undefined
* @param path 属性的路径,用 "." 分隔
*/
JsonFile.prototype.get = function (path) {
return this.dp.get(path);
};
/**
* 删除指定路径上的值
* @param path 属性的路径,用 "." 分隔
*/
JsonFile.prototype.del = function (path) {
this.touched = true;
return this.dp.del(path);
};
/**
* 设置指定路径上的值
* @param path 属性的路径,用 "." 分隔
* @param value 自定义的值
*/
JsonFile.prototype.set = function (path, value) {
this.touched = true;
var data = typeof value === 'function' ? value(this.get(path)) : value;
return this.dp.set(path, data);
};
/**
* 添加值到指定路径上,如果路径上已经存在值,则不会添加
* @param path 属性的路径,用 "." 分隔
* @param value 自定义的值
*/
JsonFile.prototype.add = function (path, value) {
if (this.dp.has(path))
return false;
this.dp.set(path, value);
return true;
};
/**
* 指定路径上是否有值
* @param path 属性的路径,用 "." 分隔
*/
JsonFile.prototype.has = function (path) {
return this.dp.has(path);
};
/**
* 保存到原文件或指定的文件中
*/
JsonFile.prototype.save = function (filepath) {
writeFile(filepath || this.filepath, this.content);
};
return JsonFile;
}());
/** 解析 json 文件内容的格式,包含:结尾的空白字符 和 tab */
function parseJsonContentFormat(jsonContent) {
var tab = '';
var eof = jsonContent.substring(jsonContent.trimEnd().length);
var rows = jsonContent.split(/\r?\n/);
var firstNotEmptyRow = rows.slice(1).find(function (row) { return /^\s/.test(row) && !!row.trim(); });
if (firstNotEmptyRow) {
tab = firstNotEmptyRow.replace(firstNotEmptyRow.trimStart(), '');
}
return { tab: tab, eof: eof };
}
/**
* 获取文件的内容,如果没有对应的文件则返回 undefined
*/
function tryReadFile(absPath) {
if (existsFile(absPath)) {
try {
return readFile(absPath);
}
catch (e) { }
}
return;
}
function readFile(absPath) {
return fs.readFileSync(absPath);
}
/**
* 将 json 数据写入到指定的文件路径中
* @return 文件是否写入成功(不成功可能是因为文件没变化)
*/
function writeJsonFile(absPath, json, options) {
var _a;
var eof = '\n';
var tab = ' ';
if (existsFile(absPath)) {
(_a = parseJsonContentFormat(readFile(absPath).toString()), eof = _a.eof, tab = _a.tab);
}
return writeFile(absPath, JSON.stringify(json, null, tab) + eof, options);
}
var Dir = /** @class */ (function () {
function Dir(rootDir) {
this.rootDir = toOSPath(path.resolve(rootDir));
}
/** relative: 获取相对根目录的相对路径 (路径分隔符为:posix格式) */
Dir.prototype.rel = function (absPath) {
var p = path.relative(this.rootDir, toOSPath(absPath));
return path.sep !== path.posix.sep ? toPosixPath(p) : p;
};
/** absolute: 获取相对于根目录的绝对路径(路径分隔符为:操作系统相关的格式) */
Dir.prototype.abs = function (relPath) {
return path.resolve(this.rootDir, toOSPath(relPath));
};
/**
* 返回指定文件的绝对路径,如果没有指定文件路径,则返回默认文件的绝对路径(用在命令行中,如果用户没有提供路径就使用根目录下的一个路径)
* @param filePathRelativeToWorkDir 指定的文件路径(相对于当前工作目录)
* @param defaultFilePath 默认文件路径(相对于当前根目录)
*/
Dir.prototype.path = function (filePathRelativeToWorkDir, defaultFilePath) {
if (filePathRelativeToWorkDir)
return path.resolve(filePathRelativeToWorkDir);
return this.abs(defaultFilePath);
};
/**
* 指定路径上是否存在文件或目录或其它类型的文件
* @param filePath 相对于根目录的路径,也可以是绝对路径
*/
Dir.prototype.exists = function (filePath) {
return exists(this.abs(filePath));
};
/**
* 指定路径上是否存在文件
* @param filePath 相对于根目录的路径,也可以是绝对路径
*/
Dir.prototype.existsFile = function (filePath) {
return existsFile(this.abs(filePath));
};
/**
* 指定路径上是否存在文件夹
* @param filePath 相对于根目录的路径,也可以是绝对路径
*/
Dir.prototype.existsDir = function (filePath) {
return existsDir(this.abs(filePath));
};
/**
* 获取指定路径的文件 Buffer (如果文件不存在会报错)
* @param filePath 相对于根目录的路径,也可以是绝对路径
*/
Dir.prototype.readBuffer = function (filePath) {
return fs.readFileSync(this.abs(filePath));
};
Dir.prototype.tryReadBuffer = function (filePath, defaultValue) {
if (!this.exists(filePath))
return defaultValue;
return this.readBuffer(filePath);
};
/**
* 获取指定路径的文件内容
* @param filePath 相对于根目录的路径,也可以是绝对路径
*/
Dir.prototype.readContent = function (filePath) {
return this.readBuffer(filePath).toString();
};
Dir.prototype.tryReadContent = function (filePath, defaultValue) {
if (!this.exists(filePath))
return defaultValue;
return this.readContent(filePath);
};
/**
* 获取指定路径的文件内容 (如果文件不存在或JSON解析失败会报错)
* @param filePath 相对于根目录的路径,也可以是绝对路径
*/
Dir.prototype.readJson = function (filePath) {
return JSON.parse(this.readContent(filePath));
};
Dir.prototype.tryReadJson = function (filePath, defaultValue) {
if (!this.exists(filePath))
return defaultValue;
var content = this.readContent(filePath);
try {
return JSON.parse(content);
}
catch (e) {
return defaultValue;
}
};
/**
* 设置指定路径的文件 Buffer
* @param filePath 相对于根目录的路径,也可以是绝对路径
*/
Dir.prototype.writeBuffer = function (filePath, buffer, options) {
var data = typeof buffer === 'function' ? buffer(this.tryReadBuffer(filePath)) : buffer;
if (data != null) {
this.writeContent(filePath, data, options);
}
return this;
};
/**
* 设置指定路径的文件内容
* @param filePath 相对于根目录的路径,也可以是绝对路径
*/
Dir.prototype.writeContent = function (filePath, content, options) {
var data = typeof content === 'function' ? content(this.tryReadContent(filePath)) : content;
if (data != null) {
writeFile(this.abs(filePath), data, options);
}
return this;
};
/**
* 设置指定路径的文件 JSON 内容
* @param filePath 相对于根目录的路径,也可以是绝对路径
*/
Dir.prototype.writeJson = function (filePath, json, options) {
var data = typeof json === 'function' ? json(this.tryReadJson(filePath)) : json;
if (data != null) {
writeJsonFile(this.abs(filePath), data, options);
}
return this;
};
/**
* 删除指定文件或文件夹(如果指定了文件夹,会同时删除其中的所有文件)
* @param filePath 相对于根目录的路径,也可以是绝对路径
*/
Dir.prototype.rm = function (filePath) {
rm(this.abs(filePath));
return this;
};
/**
* 列出指定文件夹路径下的所有文件,如果文件夹不存在,返回空数组
* @param dirPath 文件夹路径,可以相对于根目录的路径,也可以是绝对路径
*/
Dir.prototype.ls = function (dirPath) {
var dir = dirPath || this.rootDir;
if (this.existsDir(dir)) {
return fs.readdirSync(this.abs(dir));
}
return [];
};
/**
* 清空指定文件中的文件,但不会删除指定的文件夹;
*
* - 如果指定的文件夹不存在,不会做任何事
* - 如果没有提供参数,表示清空当前根目录
* @param dirPath 文件夹路径,可以相对于根目录的路径,也可以是绝对路径
*/
Dir.prototype.empty = function (dirPath) {
var _this = this;
var dir = dirPath || this.rootDir;
this.ls(dir).forEach(function (name) {
_this.rm(path.join(dir, name));
});
return this;
};
/**
* 确保指定的目录一定存在
* @param dirPath 文件夹路径,可以相对于根目录的路径,也可以是绝对路径
*/
Dir.prototype.ensureDir = function (dirPath) {
if (!this.existsDir(dirPath)) {
mkdirp$1(dirPath);
}
return this;
};
/**
* 确保指定的文件一定存在
* @param filePath 文件路径,可以相对于根目录的路径,也可以是绝对路径
*/
Dir.prototype.ensureFile = function (filePath) {
if (!this.existsFile(filePath)) {
this.writeContent(filePath, '');
}
return this;
};
/**
* 创建流
* @param filePath 相对于根目录的路径,也可以是绝对路径
*/
Dir.prototype.stream = function (filePath) {
var _this = this;
var absPath = this.abs(filePath);
return {
/** 生成可读流 */
reader: function (options) {
return fs.createReadStream(absPath, options);
},
/** 生成可写流 */
writer: function (options) {
_this.ensureDir(path.dirname(absPath));
return fs.createWriteStream(absPath, options);
},
};
};
/** 根目录路径 */
Dir.prototype.toString = function () {
return this.rootDir;
};
return Dir;
}());
export { Dir as D, JsonFile as J, writeJsonFile as a, parseJsonContentFormat as p, readFile as r, tryReadFile as t, writeFile as w };