UNPKG

book-cliiii

Version:

Command line interface for front end project

265 lines (243 loc) 7.84 kB
/* 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;