核心模块开发
准备阶段
核心库:
- import-local:当前项目中的 node_modules 中存在一个脚手架命令,和全局的 node 环境中也存在一个脚手架命令的时候,它会优先选用 node_modules 中的本地版本
- commander:命令注册
工具库:
- npmlog:打印日志
- fs-extra:文件操作
- semver:版本比对
- colors:在终端打印不同的颜色文本
- user-home:快速拿到用户的主目录
- dotenv:获取环境变量
- root-check:root 账号的检查和自动降级
流程简介
const importLocal = require(‘import-local’);
// 判断 importLocal(filename) 为 true 的时候 会输出一行 log
// 判断本地 node_modules
中是否存在脚手架
// filename: 返回当前模块文件被解析过后的绝对路径
if (importLocal(__filename)) {
require(‘npmlog’).info(‘cli’, ‘正在使用 temp-cli-dev 本地版本’);
} else {
require(‘../lib’)(process.argv.slice(2)); //从进程中获取参数
}
2. 检查版本号
```typescript
const log = require('npmlog');
// 加载 .json 时会使用 JSON.parse 进行转换编译从而得到一个 json 对象
const pkg = require('../package.json');
// 检查版本
function checkPkgVersion () {
log.info('cli', pkg.version);
}
- 检查 node 版本
使用 semver 实现版本号的解析和比较
const semver = require('semver');
const LOWEST_NODE_VERSION = '12.0.0'; // 当前可用的最低 node 版本
// 检查 node 版本
function checkNodeVersion () {
const currentVersion = process.version; // 当前 Node 版本
const lastVersion = LOWEST_NODE_VERSION;
// gte(v1, v2): v1 >= v2
if (!semver.gte(currentVersion, lastVersion)) {
throw new Error(colors.red(`当前脚手架需要安装v${lastVersion}以上版本的Node.js`));
}
}
- 检查是否 root 账号
如果进行相关操作的时候使用的是 root 账号,那么会带来很多权限问题。root-check 会判断出当前系统登录账号是否是 root 账号,如果是,会自动帮我们降级,避免后续的权限问题。
// 检查登录账号的级别,是否需要降级
function checkRoot () {
//使用后,检查到root账户启动,会进行降级为用户账户
const rootCheck = require('root-check');
rootCheck();
}
- 检查用户主目录是否存在
path-exists 该库的最新版不再支持 commonJS 模式
const userHome = require('user-home'); // 获取当前用户主目录
const pathExists = require('path-exists').sync; //判断目录是否存在
const colors = require('colors');
// 检查用户主目录
function checkUserHome () {
if (!userHome || !pathExists(userHome)) {
throw new Error(colors.red('当前登录用户主目录不存在!!!'));
}
}
- 检查入参
minimist 是一个轻量级的命令行参数解析引擎,利用它来解析命令行的参数,进行相应的配置
// 检查入参
function checkInputArgs() {
const minimist = require('minimist');
let args = minimist(process.argv.slice(2));
checkArgs(args);
}
function checkArgs(args) {
if (args.debug) {
process.env.LOG_LEVEL = 'verbose';
} else {
process.env.LOG_LEVEL = 'info';
}
log.level = process.env.LOG_LEVEL;
}
- 检查环境变量
.env 文件中存储的数据格式是 key=value 这种格式的,获取到 .env 文件后使用 dotenv 会将里面的数据挂载到 process.env 上
const path = require('path');
// 检查环境变量
function checkEnv () {
const dotenv = require('dotenv');
const dotenvPath = path.resolve(__dirname, '../../../.env');
if (pathExists(dotenvPath)) {
// config will read your .env file, parse the contents, assign it to process.env,
// and return an Object with a parsed key containing the loaded content or an error key if it failed.
dotenv.config({
path: dotenvPath
});
}
log.info('环境变量', process.env.CLI_HOME_PORT);
}
function createDefaultConfig() {
const cliConfig = {
home: userHome
}
if (process.env.CLI_HOME) {
cliConfig['cliHome'] = path.join(userHome, process.env.CLI_HOME);
} else {
cliConfig['cliHome'] = path.join(userHome, constants.DEFAULT_CLI_HOME);
}
process.env.CLI_HOME_PATH = cliConfig.cliHome;
}
- 检查是否是最新版本,是否需要更新
```typescript
async function checkGlobalUpdate() {
//1.获取当前版本号和模块名
const currentVersion = pkg.version;
const npmName = pkg.name;
//2.调用npm API,获取所有版本号
const { getNpmSemverVersion } = require(‘@temp-cli-dev/get-npm-info’);
//3.提取所有版本号,比对哪些版本号是大于当前版本号
const lastVersion = await getNpmSemverVersion(currentVersion, npmName);
if (lastVersion && semver.gt(lastVersion, currentVersion)) {
} }//4.获取最新的版本号,提示用户更新到该版本
log.warn(colors.yellow(`请手动更新${npmName},当前版本:${currentVersion},最新版本:${lastVersion}
更新命令:npm install -g ${npmName}`))
const axios = require(‘axios’); const urlJoin = require(‘url-join’); const semver = require(‘semver’);
function getNpmInfo(npmName,registry) { if (!npmName) { return null; } const registryUrl = registry || getDefaultRegistry(); const npmInfoUrl = urlJoin(registryUrl,npmName); return axios.get(npmInfoUrl).then(response => { if (response.status === 200) { return response.data; } return null; }).catch(err => { return Promise.reject(err); }) }
function getDefaultRegistry(isOriginal = false) { return isOriginal ? “https://registry.npmjs.org/“ : “https://registry.npm.taobao.org/“; }
async function getNpmVersions(npmName,registry) { const data = await getNpmInfo(npmName,registry); if (data) { return Object.keys(data.versions); } else { return []; } }
function getSemverVersions(baseVersion,versions) {
return versions.filter(version =>
semver.satisfies(version,^${baseVersion}
)
).sort((a,b)=> semver.gt(b,a));
}
async function getNpmSemverVersion(baseVersion,npmName,registry) { const versions = await getNpmVersions(npmName, registry); const newVersions = getSemverVersions(baseVersion, versions); if (newVersions && newVersions.length > 0) { return newVersions[0]; } }
async function getNpmLatestVersion(npmName,registry) { let versions = await getNpmVersions(npmName,registry); if (versions) { return versions.sort((a,b)=> semver.gt(b,a))[0]; } return null; } ```