t-comm
Version:
专业、稳定、纯粹的工具库
257 lines (254 loc) • 11.1 kB
JavaScript
import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
import _regeneratorRuntime from '@babel/runtime/regenerator';
import { _ as __awaiter } from '../tslib.es6-848120af.js';
import axios from 'axios';
import { getChildProcess } from '../nodejs/child_process.mjs';
/**
* 判断包是否是腾讯内网包(@tencent 开头)
* @param name 包名
* @returns 是否是 @tencent/xxx 风格的内网包
*/
function isTencentInternalPackage(name) {
return typeof name === 'string' && name.startsWith('@tencent/');
}
/**
* 解析 npm 包详情页地址。
* - `@tencent/xxx`:腾讯内网镜像 detail 页(mirrors.tencent.com 私有 npm,repo_id=537)
* - 其他:npmjs.com 公网详情页
*
* @param name 包名
* @returns 完整 URL
* @example
*
* resolveNpmLink('t-comm')
* // 'https://mirrors.tencent.com/#/private/npm/detail?repo_id=537&project_name=%40tencent%2Ft-comm'
*
* resolveNpmLink('lodash')
* // 'https://npmjs.com/package/lodash'
*/
function resolveNpmLink(name) {
if (!name) return '';
if (isTencentInternalPackage(name)) {
return "https://mirrors.tencent.com/#/private/npm/detail?repo_id=537&project_name=".concat(encodeURIComponent(name));
}
return "https://npmjs.com/package/".concat(name);
}
/**
* 通过 `npm view <name> time --json` 拿到所有版本的发布时间,
* 按时间倒序返回最近的 N 个版本号(跳过 created/modified 两个非版本号 key)。
*
* 这样做的原因:
* - version-tip 是在「发布完成后」调用的,registry 上已经存在新版本;
* - `time` 字段记录每个版本的 ISO 发布时间,倒序就能依次拿到「新版本」「上一个版本」。
*
* @param {string} name 包名
* @param {number} limit 取最近的几个版本,默认 2(新、旧各一个)
* @returns {string[]} 版本号数组,按时间倒序;失败返回空数组
*/
function getRecentVersions(name) {
var limit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 2;
try {
var _getChildProcess = getChildProcess(),
execSync = _getChildProcess.execSync;
var out = execSync("npm view ".concat(name, " time --json"), {
encoding: 'utf-8',
stdio: ['ignore', 'pipe', 'pipe'],
maxBuffer: 16 * 1024 * 1024
});
var time = JSON.parse(String(out).trim() || '{}');
return Object.keys(time).filter(function (v) {
return v !== 'created' && v !== 'modified';
}).sort(function (a, b) {
return new Date(time[b]).getTime() - new Date(time[a]).getTime();
}).slice(0, limit);
} catch (e) {
console.warn("[version-tip] \u83B7\u53D6\u5386\u53F2\u7248\u672C\u5931\u8D25\uFF1A".concat((e === null || e === void 0 ? void 0 : e.message) || e));
return [];
}
}
/**
* 通过 `npm pack --dry-run --json` 获取「即将发布」的新包体积。
*
* 注意事项:
* - 必须在项目根目录或正确的 cwd 下执行,否则会发错包。
* - 文件多时输出会撑爆默认 `maxBuffer`(1MB),这里设为 64MB。
* - `--loglevel=error` 用于压住 npm 警告,进一步减少缓冲区占用。
*
* @param options.cwd 工作目录,默认 `process.cwd()`
* @returns 新包体积。失败时返回全 0。
*/
function getNewPackageSize() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var _a, _b, _c, _d, _e;
var empty = {
tarballSize: 0,
unpackedSize: 0,
fileCount: 0
};
try {
var _getChildProcess2 = getChildProcess(),
execSync = _getChildProcess2.execSync;
var out = execSync('npm pack --dry-run --json --loglevel=error', {
cwd: options.cwd || process.cwd(),
encoding: 'utf-8',
stdio: ['ignore', 'pipe', 'pipe'],
maxBuffer: 64 * 1024 * 1024
});
var arr = JSON.parse(String(out).trim());
var info = Array.isArray(arr) ? arr[0] : arr;
return {
tarballSize: (_a = info === null || info === void 0 ? void 0 : info.size) !== null && _a !== void 0 ? _a : 0,
unpackedSize: (_b = info === null || info === void 0 ? void 0 : info.unpackedSize) !== null && _b !== void 0 ? _b : 0,
fileCount: (_e = (_c = info === null || info === void 0 ? void 0 : info.entryCount) !== null && _c !== void 0 ? _c : (_d = info === null || info === void 0 ? void 0 : info.files) === null || _d === void 0 ? void 0 : _d.length) !== null && _e !== void 0 ? _e : 0
};
} catch (e) {
console.warn("[GEN VERSION TIP] \u83B7\u53D6\u65B0\u5305\u4F53\u79EF\u5931\u8D25\uFF1A".concat((e === null || e === void 0 ? void 0 : e.message) || e));
return empty;
}
}
/**
* 选择 registry:内网包走腾讯源,公网包走 npmjs.org
*/
function pickRegistry(name) {
var registry = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
if (registry) return registry.replace(/\/$/, '');
return isTencentInternalPackage(name) ? 'https://mirrors.tencent.com/npm' : 'https://registry.npmjs.org';
}
/**
* 把 scoped 包名编码为 registry 路径片段
* `@tencent/foo` -> `@tencent/foo`(斜杠不编码,scope/name 各自 encode)
*/
function encodePackageName() {
var name = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
if (name.startsWith('@')) {
var _name$slice$split = name.slice(1).split('/'),
_name$slice$split2 = _slicedToArray(_name$slice$split, 2),
scope = _name$slice$split2[0],
_name$slice$split2$ = _name$slice$split2[1],
sub = _name$slice$split2$ === void 0 ? '' : _name$slice$split2$;
return "@".concat(encodeURIComponent(scope), "/").concat(encodeURIComponent(sub));
}
return encodeURIComponent(name);
}
/**
* 通过 registry 接口拿指定版本的包体积。
* 兼容字段:
* - 腾讯源 mirrors.tencent.com:`dist.packageSize`(tarball 字节,可能无 unpackedSize)
* - 公网 npm:`dist.size`(tarball) + `dist.unpackedSize`(解包大小) + `dist.fileCount`
*/
function getPackageSizeByVersion(name, version) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var _a, _b, _c, _d, _e, _f, _g;
return __awaiter(this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
var empty, registry, url, _yield$axios$get, data, dist, _t;
return _regeneratorRuntime.wrap(function (_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
empty = {
tarballSize: 0,
unpackedSize: 0,
fileCount: 0
};
if (!(!name || !version)) {
_context.next = 1;
break;
}
return _context.abrupt("return", empty);
case 1:
registry = pickRegistry(name, options.registry);
url = "".concat(registry, "/").concat(encodePackageName(name), "/").concat(encodeURIComponent(version));
_context.prev = 2;
_context.next = 3;
return axios.get(url, {
responseType: 'json',
timeout: (_a = options.timeout) !== null && _a !== void 0 ? _a : 15000
});
case 3:
_yield$axios$get = _context.sent;
data = _yield$axios$get.data;
dist = (_b = data === null || data === void 0 ? void 0 : data.dist) !== null && _b !== void 0 ? _b : {};
return _context.abrupt("return", {
tarballSize: (_e = (_d = (_c = dist.packageSize) !== null && _c !== void 0 ? _c : dist.size) !== null && _d !== void 0 ? _d : dist.contentLength) !== null && _e !== void 0 ? _e : 0,
unpackedSize: (_f = dist.unpackedSize) !== null && _f !== void 0 ? _f : 0,
fileCount: (_g = dist.fileCount) !== null && _g !== void 0 ? _g : 0
});
case 4:
_context.prev = 4;
_t = _context["catch"](2);
console.warn("[version-tip] \u83B7\u53D6 ".concat(name, "@").concat(version, " \u4F53\u79EF\u5931\u8D25\uFF1A").concat((_t === null || _t === void 0 ? void 0 : _t.message) || _t));
return _context.abrupt("return", empty);
case 5:
case "end":
return _context.stop();
}
}, _callee, null, [[2, 4]]);
}));
}
/**
* 收集 `PackageSizeInfo`:
* - 从 `npm view <name> time` 拿到最近两个版本(新、旧)
* - 分别请求 registry 拿两个版本的体积,对比生成 PackageSizeInfo
*
* 注意:本方法依赖「发布完成后」调用,新版本必须已经在 registry 上。
*
* @param {object} appInfo package.json 内容,至少包含 name
* @param {object} [options]
* @param {string} [options.registry] 自定义 registry
* @returns {Promise<import('t-comm').PackageSizeInfo>}
*/
function collectPackageSize(appInfo) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
return __awaiter(this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
var name, _getRecentVersions, _getRecentVersions2, newVersion, oldVersion, _yield$Promise$all, _yield$Promise$all2, newPkg, oldPkg;
return _regeneratorRuntime.wrap(function (_context2) {
while (1) switch (_context2.prev = _context2.next) {
case 0:
name = appInfo === null || appInfo === void 0 ? void 0 : appInfo.name;
if (name) {
_context2.next = 1;
break;
}
return _context2.abrupt("return", {
newSize: 0
});
case 1:
_getRecentVersions = getRecentVersions(name, 2), _getRecentVersions2 = _slicedToArray(_getRecentVersions, 2), newVersion = _getRecentVersions2[0], oldVersion = _getRecentVersions2[1];
console.log("[version-tip] ".concat(name, " new=").concat(newVersion || '(none)', " old=").concat(oldVersion || '(none)'));
if (newVersion) {
_context2.next = 2;
break;
}
return _context2.abrupt("return", {
newSize: 0
});
case 2:
_context2.next = 3;
return Promise.all([getPackageSizeByVersion(name, newVersion, {
registry: options.registry
}), oldVersion ? getPackageSizeByVersion(name, oldVersion, {
registry: options.registry
}) : Promise.resolve({
tarballSize: 0,
unpackedSize: 0,
fileCount: 0
})]);
case 3:
_yield$Promise$all = _context2.sent;
_yield$Promise$all2 = _slicedToArray(_yield$Promise$all, 2);
newPkg = _yield$Promise$all2[0];
oldPkg = _yield$Promise$all2[1];
return _context2.abrupt("return", {
oldSize: oldPkg.tarballSize,
newSize: newPkg.tarballSize,
oldUnpacked: oldPkg.unpackedSize,
newUnpacked: newPkg.unpackedSize,
fileCount: newPkg.fileCount
});
case 4:
case "end":
return _context2.stop();
}
}, _callee2);
}));
}
export { collectPackageSize, getNewPackageSize, getPackageSizeByVersion, getRecentVersions, isTencentInternalPackage, resolveNpmLink };