根据模块划分调整工程结构
- 核心模块: Core
- 命令模块: Command
- 模型模块: Model
- 工具模块: Utils
hzw-cli-dev
├── command // 命令模块
├── core // 核心模块
├── utils // 工具模块
├── models // 模型模块
├── node_modules // 依赖
├── package-lock.json
├── package.json
└── lerna.json // lerna 配置文件
准备阶段流程图
import-local
代码语言:javascript复制// coreclibinindex.js
#! /usr/bin/env node
const importLocal = require('import-local')
// 如果当前项目中的 node_modules 中存在一个脚手架命令,全局的 node 环境中也存在一个脚手架命令的时候
// import-local 会优先选用项目中 node_modules 的版本,然后输出 log
if (importLocal(__filename)) {
require('npmlog').info('提示', '正在使用当前项目中 hzw-cli-dev 的版本')
} else {
// 使用全局下的脚手架命令
require('../lib')(process.argv.slice(2))
}
require
支持加载的模块类型.js/.json/.node
。 当加载.js
模块时,需要使用module.exports/exports
进行导出。 当加载.json
模块时,会调用JSON.parse
对模块进行解析,并返回一个对象。 当加载.node
模块时,会使用一个c
插件,基本不用。 当加载 任意类型的文件 模块时,会当作.js
去执行,如果内容不是js
代码,那么会报错。
log 工具
创建 log
包
// 使用 lerna 创建包
lerna create @hzw-cli-dev/log
// 使用 lerna 给 log 包 安装依赖
lerna add npmlog utils/log
// 修改入口文件名为 index.js
// 修改 package.json 中的 main 字段为 lib/index.js
// 给 coreclipackage.json 添加依赖
"dependencies": {
"@hzw-cli-dev/utils": "^1.0.1",
"@hzw-cli-dev/log": "^1.0.1"
}
// 使用 lerna link 安装本地依赖 (不知道为啥 file:xx 这种形式不好用)
lerna link
定制 log
'use strict';
// 引入 npmlog 模块
const log = require('npmlog');
// 从环境变量中读取 log.level
// log.level 的作用是: 只有超过 level 设置的权重,log 才会生效
log.level = process.env.LOG_LEVEL || 'info';
// 定制 log 的 level 参数: (名称,权重,配置)
log.addLevel('success', 2000, { fg: 'red', bg: 'yellow', bold: true });
// 定制 log 的前缀
log.heading = 'hzw';
// 定制 log 前缀的样式
log.headingStyle = { fg: 'blue', bg: 'green', bold: true };
module.exports = log;
调用 log
// coreclilibindex.js
// 引入我们封装的 npmlog 工具
const log = require('@hzw-cli-dev/log');
log.success('test', 'success...');
效果如下,还是挺好玩的,这样就可以根据自己的喜好高度定制 log
了
检查版本号
代码语言:javascript复制'use strict';
// 引入当前脚手架的 package.json 文件
const pkg = require('../package.json');
// 引入我们封装的 npmlog 工具
const log = require('@hzw-cli-dev/log');
/**
* @description: 核心方法
* @param {*}
* @return {*}
*/
function core() {
// 检查版本号
checkPkgVersion();
}
/**
* @description: 检查 package.json 中的 version 版本号
* @param {*}
* @return {*}
*/
function checkPkgVersion() {
log.success('友情提示,当前的版本是:', pkg.version);
}
module.exports = core;
检查 node 版本
- 安装版本对比的第三方库
semver
- 安装定义脚手架输出颜色的库
colors
lerna add semver core/cli
lerna add colors core/cli
定义最低 node
版本号
// coreclilibconst.js
const LOWEST_NODE_VERSION = '17.0.0';
module.exports = {
LOWEST_NODE_VERSION,
};
检查 node
版本号是否符合要求
// coreclilibindex.js
'use strict';
// 引入版本比对第三方库 semver
const semver = require('semver');
// 引入颜色库 colors
const colors = require('colors/safe');
// 引入当前脚手架的 package.json 文件
const pkg = require('../package.json');
// 引入我们封装的 npmlog 工具
const log = require('@hzw-cli-dev/log');
// 引入配置文件
const constant = require('./const');
/**
* @description: 核心方法
* @param {*}
* @return {*}
*/
function core() {
try {
// 检查版本号
checkPkgVersion();
// 检查 node 版本
checkNodeVersion();
} catch (error) {
log.error(error.message);
}
}
/**
* @description: 检查当前的 node 版本,防止 node 版本过低调用最新 api 出错
* @param {*}
* @return {*}
*/
function checkNodeVersion() {
// 获取当前 node 版本号
const currentVersion = process.version;
log.info('友情提示,当前的node版本是:', process.version);
// 获取最低 node 版本号
const lowestVersion = constant.LOWEST_NODE_VERSION;
// 对比最低 node 版本号
if (!semver.gte(currentVersion, lowestVersion)) {
throw new Error(colors.red('错误:node版本过低'));
}
}
module.exports = core;
效果如下
检查 root 账号启动
安装第三方库 root-check
, 要指定版本,不然 2.0
是用 es module
写的,会报错。
lerna add root-check@1.0.0 core/cli/
代码语言:javascript复制// coreclilibindex.js
/**
* @description: 检查root账户并自动降级
* @param {*}
* @return {*}
*/
function checkRoot() {
// 检查 root 等级并自动降级
const rootCheck = require('root-check');
rootCheck();
}
检查用户主目录
安装第三方库 user-home
, 跨操作系统获取用户主目录。
安装第三方库 path-exists
, 检查文件是否存在。
lerna add user-home core/cli/
lerna add path-exists@4.0.0 core/cli/
代码语言:javascript复制// coreclilibindex.js
/**
* @description:检查用户主目录
* @param {*}
* @return {*}
*/
function checkUserHome() {
// 引入user-home 跨操作系统获取用户主目录
const userHome = require('user-home');
// 检查文件是否存在
const pathExists = require('path-exists').sync;
// 如果主目录不存在,抛出异常
if (!userHome || !pathExists(userHome)) {
throw new Error(colors.red('当前登录用户主目录不存在'));
}
}
检查入参
安装第三方库 minimist
,解析参数。
lerna add minimist core/cli/
代码语言:javascript复制/**
* @description: 解析参数,判断是否开启 debug 模式,并在全局变量中设置 log 等级
* @param {*}
* @return {*}
*/
let args;
function checkInputArgs() {
const minimist = require('minimist');
args = minimist(process.argv.slice(2));
// 判断是否开启 debug 模式,并在全局变量中设置 log 等级
checkArgs();
}
/**
* @description: 判断是否开启 debug 模式,并在全局变量中设置 log 等级
* @param {*}
* @return {*}
*/
function checkArgs() {
if (args.debug) {
process.env.LOG_LEVEL = 'verbose';
} else {
process.env.LOG_LEVEL = 'info';
}
// 设置 log 的等级
log.level = process.env.LOG_LEVEL;
}
检查环境变量
安装第三方库 dotenv
。
lerna add dotenv core/cli/
代码语言:javascript复制/**
* @description: 检查环境变量
* @param {*}
* @return {*}
*/
function checkEnv() {
// 引入解析环境变量的库 dotenv
const dotenv = require('dotenv');
// 环境变量的路径
const dotenvPath = path.resolve(userHome, '.env');
// 如果路径存在
if (pathExists(dotenvPath)) {
// 把.env的环境变量放在process.env里
dotenv.config({
path: dotenvPath,
});
}
// 创建默认的环境变量配置
createDefaultConfig();
log.verbose('环境变量', process.env.CLI_HOME_PATH);
}
/**
* @description: 创建默认的环境变量配置
* @param {*}
* @return {*}
*/
function createDefaultConfig() {
const cliConfig = {
home: userHome,
};
// 如果 CLI_HOME 存在 使用CLI_HOME
if (process.env.CLI_HOME) {
cliConfig['cliHome'] = path.join(userHome, process.env.CLI_HOME);
} else {
// 如果 CLI_HOME 不存在 使用默认配置
cliConfig['cliHome'] = path.join(userHome, constant.DEFAULT_CLI_HOME);
}
// 设置 process.env.CLI_HOME_PATH
process.env.CLI_HOME_PATH = cliConfig.cliHome;
}
通用 npm API 模块封装
通过 npm API: https://registry.npmjs.org/模块名
可以获取到某个模块的信息.
也可以换成其他的镜像 比如淘宝源 https://registry.npmmirror.com/模块名
// 通过 lerna 新建一个包 放在 utils 下面
lerna create @hzw-cli-dev/get-npm-info ./utils/get-npm-info
// 修改文件名和 main 属性为 index.js
// core模块引入
// lerna link 安装本地依赖
// 安装 axios 用来发起网络请求
lerna add axios utils/get-npm-info
// 安装 url-join 帮助拼接url
lerna add url-join utils/get-npm-info
// 安装 semver 用来做版本比对
lerna add semver utils/get-npm-info
封装工具包 get-npm-info
// utilsget-npm-infolibindex.js
'use strict';
const axios = require('axios');
const urlJoin = require('url-join');
const semver = require('semver');
/**
* @description: 获取 npm 模块的信息
* @param {*} npmName npm 模块名称
* @param {*} register npm 镜像地址
* @return {*}
*/
async function getNpmInfo(npmName, register) {
// 如果 npmName 不存在直接返回
if (!npmName) {
return null;
}
// 获取镜像地址 ,如果没有传递参数则默认使用 npm 源
const registerUrl = register || getRegister('taobao');
// 拼接url
const npmInfoUrl = urlJoin(registerUrl, npmName);
// 调用 npm API 获取数据
return axios
.get(npmInfoUrl)
.then((res) => {
if (res.status === 200) {
return res.data;
}
return null;
})
.catch((e) => {
return Promise.reject(e);
});
}
/**
* @description: 获取 npm 镜像地址
* @param {*} origin 源
* @return {*} 镜像地址
*/
function getRegister(origin) {
const originList = {
npm: 'https://registry.npmjs.org/',
taobao: 'https://registry.npmmirror.com/',
};
return originList[origin];
}
/**
* @description: 获取模块版本号数组
* @param {*} npmName npm 模块名称
* @param {*} register npm 镜像地址
* @return {*} 模块的版本号
*/
async function getNpmVersions(npmName, register) {
const data = await getNpmInfo(npmName, register);
if (data) {
return Object.keys(data.versions);
}
return [];
}
/**
* @description: 获取符合条件的版本号(大于当前版本的版本号)
* @param {*} baseVersion 当前版本
* @param {*} versions 版本号数组
* @return {*} 大于当前版本的版本号数组
*/
function getNpmSemverVersions(baseVersion, versions) {
if (!versions || versions.length === 0) {
return [];
}
return versions
.filter((version) => semver.satisfies(version, `>=1.0.0`))
.sort((a, b) => semver.gt(b, a));
}
/**
* @description:从 npm 获取符合条件的版本号(大于当前版本的最新版本号)
* @param {*} npmName npm 模块名称
* @param {*} register npm 镜像地址
* @param {*} baseVersion 当前版本
* @return {*} 最新版本号
*/
async function getNpmSemverVersion(baseVersion, npmName, register) {
const versions = await getNpmVersions(npmName, register);
const newVersions = getNpmSemverVersions(baseVersion, versions);
return newVersions[0] || null;
}
module.exports = {
getNpmInfo,
getNpmVersions,
getNpmSemverVersion,
};
获取 npm
包的信息
// coreclilibindex.js
/**
* @description: 检查是否需要全局更新
* 1.获取当前版本号和模块名
2.调用npm API ,获取所有的版本号
3.提取所有的版本号,比对哪些版本号是大于当前版本号的
4.获取最新的版本号,提示用户更新到该版本
* @param {*}
* @return {*}
*/
async function checkGlobalUpdate() {
const currentVersion = pkg.version;
const npmName = pkg.name;
const { getNpmSemverVersion } = require('@hzw-cli-dev/get-npm-info');
// getNpmInfo(npmName);
const lastVersion = await getNpmSemverVersion(currentVersion, 'vue');
// 如果最新版本存在并且大于当前版本
if (lastVersion && semver.gt(lastVersion, currentVersion)) {
log.warn(
'友情提示',
colors.yellow('请更新版本:当前的版本是:', lastVersion),
);
log.warn(
'友情提示',
colors.yellow('更新命令:', `npm install -g ${npmName}`),
);
}
}