@serpent/common-cli
Version:
通用的 cli 相关的函数
481 lines (473 loc) • 21.7 kB
JavaScript
import path from 'path';
import { b as findupPackage, o as ospath, k as existsDir } from './context-96dcd180.mjs';
import { D as Dir, J as JsonFile } from './Dir-b98fddc7.mjs';
import 'module';
import 'assert';
import { r as readJsonFile } from './readJsonFile-255f65c0.mjs';
import fs from 'fs';
import { silentRunOutput, run } from './run.mjs';
export { c as createPackage } from './createPackage-cfe91bd7.mjs';
import { _ as __awaiter, a as __generator, b as __spreadArray, e as __rest, c as __assign } from './tslib.es6-636c4f06.mjs';
import { t as tryGetProjectRootDir, i as isProjectRootDir } from './project-0299066e.mjs';
import { i as isCmdExists } from './isCommandExists-0f8fbb8f.mjs';
import { compareVersion, satisfiesVersion, sortVersions, filterVersions } from './version.mjs';
import { logger } from './logger.mjs';
import http from 'http';
import https from 'https';
import './_commonjsHelpers-7d1333e8.mjs';
import './exists-d3d14d46.mjs';
import 'os';
import 'node:buffer';
import 'node:path';
import 'node:child_process';
import 'node:process';
import 'child_process';
import 'node:url';
import 'node:os';
import './index-3b2c9171.mjs';
import 'events';
import 'buffer';
import 'stream';
import 'util';
import 'url';
import './info-fd7bb244.mjs';
import './warn-1994a28d.mjs';
import './supportColor-2e661db8.mjs';
import './helper-1331d234.mjs';
import 'crypto';
import './satisfies-144b0f35.mjs';
import './format-e8a6577a.mjs';
function getDurkaRegistry() {
return process.env.DURKA_REGISTRY || 'https://registry.npmmirror.com/';
}
/** 获取当前环境下的 registry */
function getNpmClientRegistry(npmClient) {
if (npmClient === void 0) { npmClient = 'npm'; }
return silentRunOutput("".concat(npmClient, " config get registry"));
}
function getDurkaConfig() {
var res = {};
try {
res = readJsonFile(findupPackage()).durka || {};
}
catch (e) { }
return res;
}
function getDurkaHome() {
return process.env.DURKA_HOME || path.join(ospath.home(), '.durka');
}
function getDurkaInstallPrefix(mod, version) {
if (mod === void 0) { mod = 'node'; }
if (version === void 0) { version = process.version; }
return path.join(getDurkaHome(), 'packages', mod, version.split('.')[0]);
}
/**
* 安装脚本到 durka/scripts 目录下
*/
function installDurkaScript(options) {
var name = options.name, subname = options.subname, content = options.content, devOnly = options.devOnly, installToPackage = options.installToPackage, installMode = options.installMode;
var root = tryGetProjectRootDir();
if (!root)
throw new Error("Not a valid node project, can not install durka script.");
var dist = new Dir(path.join(root, 'durka', 'scripts'));
var config = dist.tryReadJson('config.json', {});
if (!config[name])
config[name] = [];
// 安装过,就不需要安装了
if (!config[name].includes(subname)) {
config[name].push(subname);
dist.writeContent('config.json', JSON.stringify(config, null, 2));
}
dist.writeContent("".concat(name, "-").concat(subname), content, { mode: 511 });
if (installToPackage) {
var pkgFile = path.join(root, 'package.json');
var jf = new JsonFile(pkgFile);
var scriptsPath = "scripts.".concat(name);
var prevValue = jf.get(scriptsPath);
var appendCmd1 = "durka --devOnly run ".concat(name);
var appendCmd2 = "durka run ".concat(name);
var needAppendedValue = devOnly ? appendCmd1 : appendCmd2;
var notNeedAppendValue = devOnly ? appendCmd2 : appendCmd1;
if (!prevValue) {
jf.set(scriptsPath, needAppendedValue);
jf.save();
}
else if (!prevValue.includes(needAppendedValue) && prevValue.includes(notNeedAppendValue)) {
throw new Error("\u5DF2\u7ECF\u5B58\u5728\u547D\u4EE4 ".concat(notNeedAppendValue, "\uFF0C\u65E0\u6CD5\u518D\u6DFB\u52A0\u547D\u4EE4 ").concat(needAppendedValue));
}
else if (!prevValue.includes(needAppendedValue)) {
if (installMode === 'after') {
jf.set(scriptsPath, "".concat(prevValue, " && ").concat(needAppendedValue));
}
else {
jf.set(scriptsPath, "".concat(needAppendedValue, " && ").concat(prevValue));
}
jf.save();
}
}
}
/**
* 运行 durka/scripts 中的脚本
*/
function runDurkaScript(options) {
return __awaiter(this, void 0, void 0, function () {
var name, _a, args, onlyRunSubname, root, dist, config, subnameList, _i, subnameList_1, subname, file;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
name = options.name, _a = options.args, args = _a === void 0 ? [] : _a, onlyRunSubname = options.subname;
root = tryGetProjectRootDir();
if (!root)
throw new Error("".concat(process.cwd(), " is not a valid node project, can not run durka script."));
dist = new Dir(path.join(root, 'durka', 'scripts'));
config = dist.tryReadJson('config.json', {});
subnameList = config[name] || [];
if (!onlyRunSubname) return [3 /*break*/, 4];
if (!subnameList.includes(onlyRunSubname)) return [3 /*break*/, 2];
return [4 /*yield*/, run([dist.abs("".concat(name, "-").concat(onlyRunSubname))])];
case 1:
_b.sent();
return [3 /*break*/, 3];
case 2: throw new Error("Not found ".concat(name, ":").concat(onlyRunSubname, " script in durka/scripts/config.json"));
case 3: return [3 /*break*/, 8];
case 4:
_i = 0, subnameList_1 = subnameList;
_b.label = 5;
case 5:
if (!(_i < subnameList_1.length)) return [3 /*break*/, 8];
subname = subnameList_1[_i];
file = dist.abs("".concat(name, "-").concat(subname));
return [4 /*yield*/, run(__spreadArray([file], args, true))];
case 6:
_b.sent();
_b.label = 7;
case 7:
_i++;
return [3 /*break*/, 5];
case 8: return [2 /*return*/];
}
});
});
}
/**
* 请求指定链接上的内容,默认会解析成 json,超过 5s 未返回会报错
* @param url
* @param options
*/
function fetch(url, options) {
var _a;
if (options === void 0) { options = {}; }
return __awaiter(this, void 0, void 0, function () {
var signal, _b, timeout, data;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
signal = { timeout: false };
return [4 /*yield*/, wait(simpleFetch(url, signal, options.json), signal, (_a = options.timeout) !== null && _a !== void 0 ? _a : 5000)];
case 1:
_b = _c.sent(), timeout = _b.timeout, data = _b.data;
if (timeout) {
throw new Error("request ".concat(url, " timeout"));
}
return [2 /*return*/, data];
}
});
});
}
/**
* 一个简单的获取指定链接上的 json 数据的函数
*/
function simpleFetch(url, signal, json) {
if (json === void 0) { json = true; }
return new Promise(function (resolve, reject) {
var isHTTPS = url.startsWith('https:');
var client;
if (isHTTPS) {
client = https.get(url, { timeout: 5000 }, fetchCallback);
}
else {
client = http.get(url, { timeout: 5000 }, fetchCallback);
}
client.on('error', function (e) {
reject(e);
});
function fetchCallback(res) {
var _data = '';
var status = res.statusCode;
var message = res.statusMessage;
res.on('close', function () {
res.removeAllListeners();
var data;
if (_data) {
if (json) {
try {
data = JSON.parse(_data);
}
catch (e) {
reject(new Error("".concat(url, " response is not a valid json: ").concat(_data)));
}
}
else {
data = _data;
}
}
if (!status || (status >= 200 && status < 300)) {
resolve(data);
}
else if (status === 404) {
// 特殊错误码,方便后续业务处理
reject(new Error("NOT FOUND ".concat(url)));
}
else {
reject(new Error("fetch ".concat(url, " error: ").concat(status, " ").concat(message)));
}
});
res.on('data', function (chunk) {
if (signal.timeout) {
res.removeAllListeners();
res.destroy();
}
else {
if (typeof chunk === 'string')
_data += chunk;
if (Buffer.isBuffer(chunk))
_data += chunk.toString();
}
});
}
});
}
/**
* 给异步函数设置一个超时时间,如果超过指定的时间还没返回,则直接 resolve 掉
*/
function wait(promise, signal, maxWaitTime) {
return Promise.race([
promise.then(function (res) { return ({ timeout: false, data: res }); }),
sleep(maxWaitTime).then(function () {
signal.timeout = true;
return { timeout: true };
}),
]);
}
function sleep(ms) {
return new Promise(function (resolve) {
setTimeout(function () { return resolve(); }, ms);
});
}
/**
* 获取 npm 模块的详细信息(如果远程获取失败则自动切换到本地
* @param name npm 模块名称
* @param registry npm registry
*/
function getPackageDetail(name, registry) {
return __awaiter(this, void 0, void 0, function () {
var e_1, detail;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 4]);
return [4 /*yield*/, getRemotePackageDetail(name, registry)];
case 1: return [2 /*return*/, _a.sent()];
case 2:
e_1 = _a.sent();
return [4 /*yield*/, getLocalPackageDetail(name)];
case 3:
detail = _a.sent();
if (!Object.keys(detail.versions).length)
throw e_1; // 本地也没有对应的模块
logger.warn(" fetch \"".concat(name, "\" error: ").concat(e_1.message));
logger.warn(" fallback to offline mode");
return [2 /*return*/, detail];
case 4: return [2 /*return*/];
}
});
});
}
/** 获取在线模块信息 */
function getRemotePackageDetail(name, registry) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, _getRemotePackageDetail(name, registry)];
});
});
}
function _getRemotePackageDetail(name, registry, retryCount) {
if (retryCount === void 0) { retryCount = 0; }
return __awaiter(this, void 0, void 0, function () {
var pkgUrl, fields, jq, raw, _a, _b, versions, _c, deps_1, rest, e_3;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
pkgUrl = new URL(name, registry || getDurkaRegistry()).href;
if (!(isCmdExists('curl') && isCmdExists('jq'))) return [3 /*break*/, 4];
_d.label = 1;
case 1:
_d.trys.push([1, 3, , 4]);
fields = ['description', 'time', 'dist-tags', 'homepage', 'license', 'repository', 'readme', 'author', 'maintainers'];
jq = "jq '{ versions: .versions | keys_unsorted, deps: [.versions[].dependencies], ".concat(fields.map(function (f) { return "\"".concat(f, "\": .\"").concat(f, "\""); }), " }'");
return [4 /*yield*/, silentRunOutput("curl --connect-timeout 5 --max-time 16 -s '".concat(pkgUrl, "' | ").concat(jq), { shell: true })];
case 2:
raw = _d.sent();
_a = JSON.parse(raw), _b = _a.versions, versions = _b === void 0 ? [] : _b, _c = _a.deps, deps_1 = _c === void 0 ? [] : _c, rest = __rest(_a, ["versions", "deps"]);
return [2 /*return*/, __assign(__assign({ name: name }, rest), { versions: versions.reduce(function (res, version, i) {
res[version] = { name: name, version: version, dependencies: deps_1[i] };
return res;
}, {}) })];
case 3:
_d.sent();
// TODO: 有可能是网络问题,暂时不处理了,后面识别到了再一个个添加
throw new Error("Not found package \"".concat(name, "\" in ").concat(pkgUrl)); // 下游有消费,保持一致
case 4:
_d.trys.push([4, 6, , 9]);
return [4 /*yield*/, fetch(pkgUrl)];
case 5: return [2 /*return*/, _d.sent()];
case 6:
e_3 = _d.sent();
if (!(e_3.error === 'ECONNRESET' && retryCount <= 2)) return [3 /*break*/, 8];
return [4 /*yield*/, _getRemotePackageDetail(name, registry, retryCount + 1)];
case 7: return [2 /*return*/, _d.sent()];
case 8:
// TODO: 有可能是网络问题,暂时不处理了,后面识别到了再一个个添加
// if (e.message === `NOT FOUND ${pkgUrl}`)
throw new Error("Not found package \"".concat(name, "\" in ").concat(pkgUrl)); // 下游有消费,保持一致
case 9: return [2 /*return*/];
}
});
});
}
/** 获取本地模块信息 */
function getLocalPackageDetail(name) {
return __awaiter(this, void 0, void 0, function () {
var baseDir, detail, localVersions, maxVersion, versions;
return __generator(this, function (_a) {
baseDir = path.join.apply(path, __spreadArray([getDurkaInstallPrefix()], name.split('/'), false));
detail = { name: name, versions: {} };
if (existsDir(baseDir)) {
localVersions = fs.readdirSync(baseDir);
localVersions.forEach(function (v) {
var rootDir = path.join(baseDir, v, 'lib', 'node_modules', name);
if (isProjectRootDir(rootDir)) {
var pkg_1 = readJsonFile(path.join(rootDir, 'package.json'));
detail.versions[v] = Object.keys(pkg_1).reduce(function (res, k) {
// _ 开头的字段是 npm 在发布或安装的时候自动生成的字段
if (!k.startsWith('_'))
res[k] = pkg_1[k];
return res;
}, {});
// 记录当前版本所在的文件目录
detail.versions[v].durkaLocalFolder = rootDir;
}
});
localVersions = Object.keys(detail.versions);
if (localVersions.length) {
maxVersion = localVersions.sort(compareVersion)[localVersions.length - 1];
versions = detail.versions;
Object.assign(detail, detail.versions[maxVersion], { name: name, versions: versions });
}
}
return [2 /*return*/, detail];
});
});
}
/** 获取远程模块的版本号 */
function getRemoteVersions(name, options) {
if (options === void 0) { options = {}; }
return getVersions(getPackageDetail(name, options.registry), options);
}
/** 获取本地模块的版本号 */
function getLocalVersions(name, options) {
return getVersions(getLocalPackageDetail(name), options);
}
/** 获取远程所有 tag 及其关联的版本号 */
function getRemoteTags(name, registry) {
return getPackageDetail(name, registry).then(function (d) { return d['dist-tags'] || {}; });
}
/** 获取远程中指定 tag 关联的版本号,有可能不存在 */
function getRemoteTagVersion(name, tag, registry) {
return getRemoteTags(name, registry).then(function (d) { return d[tag]; });
}
/** 获取远程模块的最新版本号 */
function getRemoteLatestVersion(name, options) {
if (options === void 0) { options = {}; }
return getLatest(getRemoteVersions, name, options);
}
/** 获取本地模块的最新版本号 */
function getLocalLatestVersion(name, options) {
if (options === void 0) { options = {}; }
return getLatest(getLocalVersions, name, options);
}
function getLatest(fetch, name, options) {
if (options === void 0) { options = {}; }
var includePrerelease = options.includePrerelease, range = options.range, tag = options.tag, registry = options.registry;
return fetch(name, {
mode: includePrerelease ? 'pre&major' : 'major',
range: range,
tag: tag,
order: 'desc',
registry: registry,
}).then(function (versions) { return versions.shift(); });
}
function getVersions(prom, options) {
if (options === void 0) { options = {}; }
var _a = options.order, order = _a === void 0 ? 'desc' : _a, _b = options.mode, mode = _b === void 0 ? 'all' : _b, range = options.range, tag = options.tag;
return prom.then(function (detail) {
// 服务器报错,可能会返回一个 html 内容
if (!detail || typeof detail !== 'object') {
throw new Error("Fetch package versions failed: ".concat(detail));
}
var versions = [];
if (tag) {
var tagVersion = (detail['dist-tags'] || {})[tag];
if (tagVersion)
versions.push(tagVersion);
}
else {
versions = Object.keys(detail.versions);
}
if (range)
versions = versions.filter(function (v) { return satisfiesVersion(v, range); });
if (mode === 'all')
return sortVersions(versions, order);
return sortVersions(filterVersions(versions, mode), order);
});
}
/**
* 安装模块
* @param name 要安装的模块名
* @param version 要安装的模块版本,必须是一个具体是版本,不能是 tag 或 "^"、"~" 开头的版本号
* @param options
* @returns 返回安装后的模块根目录
*/
function npmInstall(name, version, options) {
if (options === void 0) { options = {}; }
return __awaiter(this, void 0, void 0, function () {
var _a, prefix, _b, registry, _c, dependencies, _d, flags, nameParts, realPrefix, expectResultDir, installs;
return __generator(this, function (_e) {
switch (_e.label) {
case 0:
_a = options.prefix, prefix = _a === void 0 ? getDurkaInstallPrefix() : _a, _b = options.registry, registry = _b === void 0 ? getDurkaRegistry() : _b, _c = options.dependencies, dependencies = _c === void 0 ? [] : _c, _d = options.flags, flags = _d === void 0 ? [] : _d;
nameParts = name.split('/');
realPrefix = path.join.apply(path, __spreadArray(__spreadArray([prefix], nameParts, false), [version], false));
expectResultDir = path.join.apply(path, __spreadArray([realPrefix, 'lib', 'node_modules'], nameParts, false));
if (!(!isProjectRootDir(expectResultDir) || options.force)) return [3 /*break*/, 2];
console.log();
installs = __spreadArray(["".concat(name, "@").concat(version)], dependencies, true);
return [4 /*yield*/, run(__spreadArray(__spreadArray(__spreadArray(['npm', 'install', '--global'], flags, true), ['--prefix', realPrefix, '--registry', registry], false), installs, true))];
case 1:
_e.sent();
console.log();
_e.label = 2;
case 2: return [2 /*return*/, expectResultDir];
}
});
});
}
/** 获取 npmInstall 命令安装项目之后的根目录 */
function getNpmInstallRootPath(name, version, options) {
if (options === void 0) { options = {}; }
var _a = options.prefix, prefix = _a === void 0 ? getDurkaInstallPrefix() : _a;
var ns = name.split('/');
var p = path.join.apply(path, __spreadArray(__spreadArray([prefix], ns, false), [version], false));
return path.join.apply(path, __spreadArray([p, 'lib', 'node_modules'], ns, false));
}
export { fetch, getDurkaConfig, getDurkaHome, getDurkaInstallPrefix, getDurkaRegistry, getLocalLatestVersion, getLocalPackageDetail, getLocalVersions, getNpmClientRegistry, getNpmInstallRootPath, getPackageDetail, getRemoteLatestVersion, getRemotePackageDetail, getRemoteTagVersion, getRemoteTags, getRemoteVersions, installDurkaScript, npmInstall, runDurkaScript };