UNPKG

@serpent/common-cli

Version:

通用的 cli 相关的函数

678 lines (634 loc) 20.3 kB
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 };