smash-package-installer
Version:
A tiny npm package installer used by smash-cli.
128 lines (117 loc) • 5.34 kB
JavaScript
/**
* npm 包下载器
*
* 变量命名提示:
* (1)packageName:含有包名、版本号,比如:smash-shell@0.0.1
* (2)purePackageName:只含有包名
*/
const path = require('path');
const Copier = require('smash-copy');
const Shell = require('smash-shell');
// 安装包时的默认选项
// https://www.npmjs.cn/cli/install/
const DEFAULT_OPTIONS = {
saveProd: true, // saved in the dependencies.
saveDev: false, // saved in the devDependencies.
saveOptional: false, // saved in the optionalDependencies.
noSave: false,
// 默认安装目录为工作目录。
// 这个目录需要有package.json文件,不然会安装到项目的顶层的node_modules里。
installationDir: process.cwd(),
};
class PackageInstaller {
/**
* 拆分名称与版本的方法
* @param {String} packageName 包名的类型总共有4种。
*/
static splitPackageName(packageName) {
// 这里支持的包的类型总有4种,
// (1)'smash-cli' => arr = [ 'smash-cli' ]
// (2.1)'smash-cli@^1.0.0' => arr = [ 'smash-cli', '^1.0.0' ]
// (2.2)'smash-cli@*' => arr = [ 'smash-cli', '*' ]
// (3)'@erye/smash-cli' => arr = [ '', 'erye/smash-cli' ]
// (4)'@erye/smash-cli@1.0.0' => arr = [ '', 'erye/smash-cli', '1.0.0' ]
const arr = packageName.split('@');
if (arr[0] == '') { // 数组首位是空,符合类型 3、4
arr[1] = `@${arr[1]}`; // 给第二位加上 @ 字符
arr.shift(); // 移除首位
}
return { purePackageName: arr[0], version: arr[1] || '' };
}
/**
* 判断已经安装号是否可以使用。参数位置不能调换。
*
* npm包的规则比较复杂:
* * 这意味着安装最新版本的依赖包
* ~ 会匹配最近的小版本依赖包,比如~1.2.3会匹配所有1.2.x版本,但是不包括1.3.0
* ^ 会匹配最新的大版本依赖包,比如^1.2.3会匹配所有1.x.x的包,包括1.3.0,但是不包括2.0.0
* @param {String} wantedVersion 目标版本号
* @param {String} installedVersion 当前版本号
*/
static isInstalledVersionUsable(wantedVersion, installedVersion) {
let isUsable = false;
// wantedVersion = installedVersion
// wantedVersion = ''
if (wantedVersion == installedVersion || wantedVersion == '') {
isUsable = true;
}
// wantedVersion = '*'
// wantedVersion = 'latest'
else if (wantedVersion == '*' || wantedVersion == 'latest') { // 安装最新版本
isUsable = false; // 直接安装最新版本,不再做检查操作
}
// wantedVersion = '~1.0.0'
else if (wantedVersion.indexOf('~') == 0) { // 会匹配最近的小版本依赖包
let arr1 = wantedVersion.replace(/[\~\^]/, '').split('.'); // => ['1', '2', 'x']
let arr2 = installedVersion.replace(/[\~\^]/, '').split('.'); // => ['1', '2', 'x']
isUsable = arr1[0] == arr2[0] && arr1[1] == arr2[1]; // 1位、2位必须相等,3位人一直
}
// wantedVersion = '^1.0.0'
else if (wantedVersion.indexOf('^') == 0) { // 会匹配最新的大版本依赖包
let arr1 = wantedVersion.replace(/[\~\^]/, '').split('.'); // => ['1', 'x', 'x']
let arr2 = installedVersion.replace(/[\~\^]/, '').split('.'); // => ['1', 'x', 'x']
isUsable = arr1[0] == arr2[0]; // 1位必须相等,2位、3位任意值
}
// wantedVersion = '1.0.0' ...
else {
isUsable = wantedVersion === installedVersion.replace(/^[\~\^]/, '');
}
return isUsable;
}
/**
* 在指定的目录创建一个空的package.json文件
* @param {String} dir
*/
static createEmptyPackageJson(dir) {
const fileSrc = path.resolve(__dirname, './emptyPackage.json');
const fileDst = path.resolve(dir, './package.json');
return Copier.copySync(fileSrc, fileDst);
}
/**
* 安装一个或者多个npm包到指定的目录下
* @param {String|Array} packageName 'store5',或者 ['store5', 'smash-copy']
*/
static install(packageName, options) {
if (packageName instanceof Array) {
packageName = packageName.join(' ');
}
options = { ...DEFAULT_OPTIONS, ...options };
let command = `npm i ${packageName}`;
if (options.saveProd) {
command = `npm i ${packageName} --save`;
}
if (options.saveDev) {
command = `npm i ${packageName} --save-dev`;
}
if (options.saveDev) {
command = `npm i ${packageName} --save-optional`;
}
if (options.noSave) {
command = `npm i ${packageName} --no-save`;
}
return Shell.execSync(command, {
cwd: options.installationDir,
});
}
}
module.exports = PackageInstaller;