首页
学习
活动
专区
圈层
工具
发布
50 篇文章
1
【架构师(第一篇)】整体需求分析和架构设计
2
【架构师(第二篇)】脚手架架构设计和框架搭建
3
【架构师(第三篇)】脚手架开发之掌握Lerna操作流程
4
【架构师(第四篇)】脚手架开发之Lerna源码分析
5
【架构师(第五篇)】脚手架之import-local执行流程及简历设计
6
【架构师(第六篇)】脚手架之需求分析和架构设计
7
【架构师(第七篇)】脚手架之准备阶段编写
8
【架构师(第八篇)】脚手架之 commander 框架使用方法
9
【架构师(第九篇)】如何让 Node 环境支持 ES Module
10
【架构师(第十篇)】脚手架之注册命令及架构优化
11
【架构师(第十一篇)】脚手架之命令注册和执行过程开发
12
【架构师(第十二篇)】脚手架之命令行交互工具 inquirer.js 使用方法
13
【架构师(第十三篇)】脚手架之创建项目准备阶段开发
14
【架构师(第十四篇)】脚手架之 egg.js 和 mongodb 的使用
15
【架构师(第十五篇)】脚手架之创建项目模板开发
16
【架构师(第十六篇)】脚手架之创建项目模板的下载与更新
17
【架构师(第十七篇)】脚手架之 ejs 和 glob 的使用
18
【架构师(第十八篇)】脚手架之项目模板的安装
19
【架构师(第十九篇)】脚手架之组件库模板开发
20
【架构师(第二十篇)】脚手架之自定义模板及第一阶段总结
21
【架构师(第二十一篇)】编辑器开发之需求分析和架构设计
22
【架构师(第二十二篇)】编辑器开发之项目整体搭建
23
【架构师(第二十三篇)】编辑器开发之画布区域组件的渲染
24
【架构师(第二十四篇)】编辑器开发之添加模版到画布
25
【架构师(第二十五篇)】编辑器开发之属性编辑区域表单渲染
26
【架构师(第二十六篇)】编辑器开发之属性编辑同步渲染
27
【架构师(第二十七篇)】前端单元测试框架 Jest 基础知识入门
28
【架构师(第二十八篇)】 测试工具 Vue-Test-Utils 基础语法
29
【架构师(第二十九篇)】Vue-Test-Utils 触发事件和异步请求
30
【架构师(第三十篇)】Vue-Test-Utils 全局组件和第三方库 vuex | vue-router
31
【架构师(第三十一篇)】前端测试之 TDD 的开发方式
32
【架构师(第三十二篇)】 通用上传组件开发及测试用例
33
【架构师(第三十三篇)】 Vue 中的实例及本地图片预览
34
【架构师(第三十四篇)】 业务组件库开发之 vue3 的插件系统
35
【架构师(第三十五篇)】 业务组件库开发之使用 Rollup 进行打包
36
【架构师(第三十六篇)】 业务组件库开发之发布到 NPM
37
【架构师(第三十七篇)】 服务端开发之后端框架与数据库技术选型
38
【架构师(第三十八篇)】 服务端开发之本地安装最新版 MySQL 数据库
39
【架构师(第三十九篇)】 服务端开发之连接 MySQL 数据库
40
【架构师(第四十篇)】 服务端开发之连接 Mongodb 数据库
41
【架构师(第四十一篇)】 服务端开发之安装并连接 Redis数据库
42
【架构师(第四十二篇)】 服务端开发之常用的登录鉴权方式
43
【架构师(第四十三篇)】 服务端开发之单元测试和接口测试
44
【架构师(第四十四篇)】 服务端开发之 pm2 和 nginx 介绍
45
【架构师(第四十五篇)】 服务端开发之认识 Github actions
46
【架构师(第四十六篇)】 服务端开发之安装 Docker
47
【架构师(第四十七篇)】 服务端开发之认识 Docker
48
【架构师(第四十八篇)】 服务端开发之 Dockerfile
49
【架构师(第四十九篇)】 服务端开发之认识 Docker-compose
50
【架构师(第五十篇)】 服务端开发之自动发布到测试机
清单首页架构文章详情

【架构师(第七篇)】脚手架之准备阶段编写


根据模块划分调整工程结构

  • 核心模块: Core
  • 命令模块: Command
  • 模型模块: Model
  • 工具模块: Utils
代码语言:javascript
复制
hzw-cli-dev
├── command // 命令模块
├── core // 核心模块
├── utils // 工具模块
├── models // 模型模块
├── node_modules // 依赖
├── package-lock.json 
├── package.json 
└── lerna.json // lerna 配置文件

准备阶段流程图

import-local

代码语言:javascript
复制
// core\cli\bin\index.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

代码语言:javascript
复制
// 使用 lerna 创建包
lerna create @hzw-cli-dev/log
// 使用 lerna  给 log 包 安装依赖
lerna add npmlog  utils/log
// 修改入口文件名为 index.js
// 修改 package.json 中的 main 字段为 lib/index.js
// 给 core\cli\package.json 添加依赖
"dependencies": {
    "@hzw-cli-dev/utils": "^1.0.1",
    "@hzw-cli-dev/log": "^1.0.1"
}
// 使用 lerna link 安装本地依赖 (不知道为啥 file:xx 这种形式不好用)
lerna link

定制 log

代码语言:javascript
复制
'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

代码语言:javascript
复制
// core\cli\lib\index.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
代码语言:javascript
复制
lerna add semver core/cli
lerna add colors core/cli

定义最低 node 版本号

代码语言:javascript
复制
// core\cli\lib\const.js
const LOWEST_NODE_VERSION = '17.0.0';

module.exports = {
  LOWEST_NODE_VERSION,
};

检查 node 版本号是否符合要求

代码语言:javascript
复制
// core\cli\lib\index.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 写的,会报错。

代码语言:javascript
复制
lerna add root-check@1.0.0 core/cli/
代码语言:javascript
复制
// core\cli\lib\index.js
/**
 * @description: 检查root账户并自动降级
 * @param {*}
 * @return {*}
 */
function checkRoot() {
  // 检查 root 等级并自动降级
  const rootCheck = require('root-check');
  rootCheck();
}

检查用户主目录

安装第三方库 user-home , 跨操作系统获取用户主目录。

安装第三方库 path-exists , 检查文件是否存在。

代码语言:javascript
复制
lerna add user-home core/cli/
lerna add path-exists@4.0.0 core/cli/
代码语言:javascript
复制
// core\cli\lib\index.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 ,解析参数。

代码语言:javascript
复制
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

代码语言:javascript
复制
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/模块名

代码语言:javascript
复制
// 通过 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

代码语言:javascript
复制
// utils\get-npm-info\lib\index.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 包的信息

代码语言:javascript
复制
// core\cli\lib\index.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}`),
    );
  }
}
下一篇
举报
领券