book-cliiii
Version:
Command line interface for front end project
265 lines (243 loc) • 7.84 kB
JavaScript
/* eslint-disable no-underscore-dangle */
/** @module env/install */
const fs = require('fs');
const path = require('path');
// const semver = require("semver");
const execa = require('execa');
// const { createRequire } = require('module');
// const logger = require("./logger");
const REPOSITORY_FOLDER = 'book-repository';
/**
* @private
*/
class Install {
constructor(repositoryPath = REPOSITORY_FOLDER) {
// this.log = logger();
this._repositoryPath = path.join(
`${process.env.HOME}/book-cli/`,
repositoryPath,
);
}
/**
* @private
* @property
* 仓库绝对路径( npm--prefix)
*/
get repositoryPath() {
return this._repositoryPath;
}
set repositoryPath(repositoryPath) {
this._repositoryPath = path.resolve(repositoryPath);
delete this._nodeModulesPath;
}
/**
* @private
* @property nodeModulesPath
* 存储库的node_modules的路径
*/
get nodeModulesPath() {
if (!this._nodeModulesPath) {
this._nodeModulesPath = this.runPackageManager('root').stdout;
}
return this._nodeModulesPath;
}
/**
* @private
* @method
* Create the repositoryPath if it doesn't exists.
*/
createRepositoryFolder() {
if (!fs.existsSync(this.repositoryPath)) {
fs.mkdirSync(this.repositoryPath, { recursive: true });
}
}
/**
* @private
* @method
* 解析包名称模块路径 [repository path]{@link repository.repositoryPath}
* @param {String} packageName - 包名。 如果packageName是绝对路径,则将modulePath加入其中。
* @param {String} [modulePath] - 包路径中模块的路径
* @returns {String} 模块绝对路径
*/
resolvePackagePath(packageName = '', modulePath = '') {
if (path.isAbsolute(packageName)) {
return path.join(packageName, modulePath);
}
return path.join(this.nodeModulesPath, packageName, modulePath);
}
/**
* @private
* @method
* 删除 node 包缓存, 对重新安装是很有必要的。
* 默认情况下仅删除 package.json, 用于版本验证。
* 仅删除仓库软件包缓存。
* @param {String} packageName - Package name.
* @param {Boolean} [force=false] - 如果为true,则删除每个缓存包。
* @throw 如果force === false且加载了package.json以外的任何其他文件, 则会发生错误。
*/
cleanupPackageCache(packageName, force = false) {
if (!packageName) {
throw new Error('You must provide a packageName');
}
console.log('Cleaning cache of %s', packageName);
// debug("Cleaning cache of %s", packageName);
const packagePath = this.resolvePackagePath(packageName);
const toCleanup = Object.keys(require.cache).filter((cache) => cache.startsWith(packagePath));
if (!force && toCleanup.find((cache) => !cache.endsWith('package.json'))) {
throw new Error(`Package ${packageName} already loaded`);
}
toCleanup.forEach((cache) => {
delete require.cache[cache];
});
}
/**
* @private
* @method
* 运行包管理 packageManager
* @param {String} cmd - 指令
* @param {String[]} [args] - 附加参数
* @param {Object} [options] - 传递配置给 execa。
*/
runPackageManager(cmd, args, options) {
const allArgs = [
cmd,
// '-g',
'--prefix',
this.repositoryPath,
// '--loglevel',
// 'error',
];
if (cmd === 'install') {
allArgs.push('--no-optional');
}
if (args) {
allArgs.push(...args);
}
// console.log('Running npm with args %o', allArgs);
// debug('Running npm with args %o', allArgs);
execa.sync('npm', [
'config',
'set',
'@jdbk:registry=http://registry.m.jd.com',
]);
return execa.sync('npm', allArgs, options);
}
/**
* @private
* @method
* 验证软件包是否已安装并且与版本范围匹配。
* @param {String} packageName - Package name.
* @param {String} versionRange - 包版本范围。
* @returns {Boolean} 如果包已安装且与版本匹配,则为True。
* @throws Error.
*/
// verifyInstalledVersion(packageName, versionRange) {
// const packagePath = this.resolvePackagePath(packageName);
// const installedVersion = this.getPackageVersion(packagePath);
// let installed;
// if (installedVersion && !versionRange) {
// installed = true;
// } else if (installedVersion) {
// installed =
// semver.validRange(versionRange) &&
// semver.satisfies(installedVersion, versionRange);
// }
// return installed ? installedVersion : undefined;
// }
/**
* @private
* @method
* 安装包到文件中。
* @param {String} packageName - Package name.
* @param {String} versionRange - 包版本范围。
* @returns {String} 包路径
* @throws Error.
*/
installPackage(packageName, versionRange) {
const pkgs = {};
pkgs[packageName] = versionRange;
// console.log('pkgs==', pkgs);
const success = this.installPackages(pkgs) === true;
if (!success) {
throw new Error(
`Error installing package ${packageName}, version ${versionRange}.`,
);
}
console.log(`Package ${packageName} sucessfully installed`);
// debug(`Package ${packageName} sucessfully installed`);
return this.resolvePackagePath(packageName);
}
/**
* @private
* @method
* 安装包
* @param {Object} packages - 要安装的包
* @returns {Boolean}
* @example
* repository.installPackages({ 'book-cli-project-vue': '2.3.0' });
*/
installPackages(packages) {
this.createRepositoryFolder();
// debug('Installing packages %o', packages);
const packagesArgs = Object.entries(packages)
.filter(([packageName, _]) => true)
.map(
([packageName, version]) => (version ? `${packageName}@${version}` : packageName),
// ? semver.validRange(version)
// ? `${packageName}@${version}`
// : version
// : packageName
);
// console.log('packagesArgs===', packagesArgs);
const result = this.runPackageManager('install', [...packagesArgs], {
stdio: 'inherit',
});
// console.log('result===', result);
const success = result.exitCode === 0;
Object.keys(packages).forEach((packageName) => {
if (success) {
console.log(`${packageName} installed.`);
// this.log.ok(`${packageName} installed.`);
} else {
console.log(`${packageName} cannot be installed.`);
// this.log.error(`${packageName} cannot be installed.`);
}
});
return success;
}
/**
* @private
* @method
* 获取安装的软件包版本。
* @param {String} packageName - Package name.
* @returns {String|undefined} 包版本/undefined
*/
// getPackageVersion(packageName) {
// try {
// const packageJson = this.resolvePackagePath(packageName, "package.json");
// return require(packageJson).version;
// } catch (_) {
// return undefined;
// }
// }
/**
* @private
* @method
* 需要存储库中的模块。
* @param {String} packageName - Package name.
* @param {String} versionRange - 包版本范围
* @param {String} modulePath - Package name.
* @returns {Object} Module.
* @throws Error.
*/
requireModule(packageName, versionRange, modulePath = '') {
// console.log('packageName:', packageName);
const absolutePath = this.installPackage(`@jdbk/${packageName}`, versionRange);
// console.log('absolutePath======', absolutePath);
// const absolutePath = this.resolvePackagePath(packageName, modulePath);
// console.log("absolutePath==", absolutePath);
// debug('Loading module at %s', absolutePath);
return absolutePath;
}
}
module.exports = Install;