git规范

master仓库、dev开发、release上线

  • 开始开发创建分支dev/0.0.1
  • 上线pushmarge分支dev/0.0.1master分支
  • 打上标签release/0.0.1
  • 删掉dev/0.0.1分支
  • 开发的时候自动创建dev/0.0.2分支

git repo—fork—>own git repo—clone—>loal repo(本地创建分支开发,一般由脚手架统一管理比较好)
开发完成后loal repo—push—>own git repo—pull requests—code review(代码检查/评审)—合并—>git repo—>打个tag,删掉开发分支


脚手架开发流程

脚手架项目初始化

  • npm init -y # 初始化 npm 项目
  • cnpm install --global lerna # 安装 lerna(如果 cnpm 不好使可以用 yarn)
  • cnpm i -D lerna # 安装 lerna
  • lerna init # 初始化项目
    1
    2
    3
    4
    5
    6
    7
    8
    # .gitignore
    .vscode
    .idea
    node_modules
    packages/**/node_modules
    lerna-debug.log
    # 创建一个 Organizations
    [Organizations](https://www.npmjs.com/settings/cxvhweb/packages)

创建 package

  • lerna create core
  • lerna create utils
  • lerna add axios package/core安装axios时需要指定路径
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    info cli using local version of lerna
    lerna notice cli v3.22.1
    lerna WARN ENOREMOTE No git remote found, skipping repository property
    package name: (core) @cxvh-cli/core # @cxvh-cli 是组织,core是组织下面的包名,这样防止core名称冲突
    version: (0.0.0)
    description: cxvh-cli core
    keywords:
    homepage:
    license: (ISC)
    entry point: (lib/core.js)
    git repository:
    About to write to E:\learn\cxvh-cli\packages\core\package.json:

发布包

  • docs
  • 发布流程说明:
    • 根目录创建LICENSE.md
    • package.json添加"publishConfig":{"access":"public"}(所有的package.json都要加)
    • git commit后不要push
    • lerna publish
    • 完成发布

lerna 命令

  • lerna init初始化项目
  • lerna create core创建package
  • lerna add @cxvh-cli安装依赖(到所有的包)
  • lerna add @cxvh-cli ./commands/init添加依赖到指定包
  • lerna link链接依赖(在项目根目录执行)——>相当于npm link @cxvh-cli/utils(先手动配置要连接的依赖/packages/core/package.json,会在node_modules下面生成软连接)
  • lerna clean清空依赖(需要手动删除 package 里面的依赖名称)
  • lerna bootstrap重新安装依赖(相当于npm i
  • 执行shell脚本
    • 在所有package里面执行shell脚本:lerna exec -- rm -rf node_modules
    • 在指定package里面执行shell脚本(这里注意是包名不是路径):lerna exec --scope @cxvh-cli/core -- rm -rf node_modules
  • 执行package.jsonscripts
    • 在所有scripts里面执行脚本:lerna run test
    • 在指定package里面执行scriptslerna run --scope @cxvh-cli/core build
  • lerna version更新版本号(代码需要先git commit
  • lerna changed查看上版本以来的所有变更
  • lerna diff查看diff
  • lerna publish项目发布

core模块技术方案—>命令执行流程

准备阶段

检查版本号—>检查node版本(高版本api低版本不支持)—>检查root启动(root启动创建的文件后期无法维护删除,要降级)—>检查用户主目录(拿到主目录,写入缓存要用)—>检查入参—>检查环境变量—>检查是否为最新版本—否—>提示更新

  1. 自定义日志样式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    'use strict';
    const log=require('npmlog')
    log.level=process.env.LOG_LEVEL?process.env.LOG_LEVEL:'info'; // 判断 debug 模式
    log.heading="cxvh"; // 修改前缀
    log.headingStyle={fg:"red",bg:"black"}; // 修改前缀样式
    log.addLevel('success',2000,{fg:'green',bold:true}); // 添加自定义命令
    module.exports = log;
    /*
    log.addLevel('silly', -Infinity, { inverse: true }, 'sill')
    log.addLevel('verbose', 1000, { fg: 'blue', bg: 'black' }, 'verb')
    log.addLevel('info', 2000, { fg: 'green' })
    log.addLevel('timing', 2500, { fg: 'green', bg: 'black' })
    log.addLevel('http', 3000, { fg: 'green', bg: 'black' })
    log.addLevel('notice', 3500, { fg: 'blue', bg: 'black' })
    log.addLevel('warn', 4000, { fg: 'black', bg: 'yellow' }, 'WARN')
    log.addLevel('error', 5000, { fg: 'red', bg: 'black' }, 'ERR!')
    log.addLevel('silent', Infinity)
    使用方法==>log.notice("提示:","提示内容")
    */
  2. 检查版本号

    1
    2
    3
    4
    5
    const pkg=require('../package.json')
    const log=require('@cxvh-cli/log')
    function checkPkgVersion(){
    log.notice("版本号",pkg.version);
    }
  3. 检查版本号

    1
    2
    3
    4
    5
    const currentVersion=process.version;
    const lowestVersion="13.0.0";
    if(!semver.gte(currentVersion,lowestVersion)){
    throw new Error(colors.red(`cxvh-cli 需要安装 v${lowestVersion} 以上版本的 Node.js`));
    }
  4. root账号启动检查和自动降级功能

    1
    使用依赖包--->`root-check`
  5. 用户主目录检查功能

    1
    2
    3
    4
    5
    const userHome = require('user-home');
    const pathExists = require('path-exists').sync;
    if(!userHome || !pathExists(userHome)){
    throw new Error(colors.red('当前主目录不存在!'));
    }
  6. 入参检查和debug模式开发

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function checkInputArgs(){
    args = require('minimist')(process.argv.slice(2));
    checkArgs();
    }
    function checkArgs(){
    if(args.debug){
    process.env.LOG_LEVEL="verbpse"
    }else{
    process.env.LOG_LEVEL="info"
    }
    log.level=process.env.LOG_LEVEL
    }
  7. 环境变量检查功能

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    function checkEnv(){
    const dotenvPath=path.resolve(userHome,'.env')
    if(pathExists(dotenvPath)){
    let config=require('dotenv').config({
    path:dotenvPath
    })
    }
    createDefaultConfig()
    log.verbose("环境变量",process.env.CLI_HOME_PATH)
    }
    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,constant.DEFAULT_CLI_HOME)
    }
    process.env.CLI_HOME_PATH=cliConfig.cliHome;
    }
  8. 通用npm API模块封装


核心库

  • import-local优先执行本地命令
  • commander命令注册

工具库

  • npmlog打印日志,在它基础上封装
  • fs-extra文件操作,基于fs封装了许多操作文件的方法
  • semver脚手架版本是否最新,用这个来实现判断
  • colors打印的日志等信息加上颜色
  • user-home拿到用户主目录
  • dotenv获取环境变量
  • root-check判断当前是否为管理员用户
  • ``

拆包策略

根据模块的功能拆分

  • 核心流程模块:core
  • 命令模块:commands
    • 初始化
    • 发布
    • 清除缓存
  • 模型层模块:models
    • command命令
    • project项目
    • component组件
    • npm模块
    • git仓库
  • 工具模块(支撑模块):utils
    • git操作
    • 云构建
    • 工具方法
    • api请求
    • git api

改造多进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
'use strict';

const semver=require('semver')
const colors=require('colors/safe')
const log=require('@cxvh-cli/log');
const {isObject}=require('@cxvh-cli/utils');

const LOWEST_NODE_VERSION="13.0.0"||"16.0.0" // 定义最低版本号

class command{
// TODO
constructor(argv) {
// log.verbose('Command constructor!',argv)
if(!argv){
throw new Error('参数不能为空!')
}
if(!Array.isArray(argv)){
throw new Error('参数必须为数组!')
}
if(!argv.length){
throw new Error('参数列表为空!')
}
this._argv=argv;
let runner=new Promise((resolve,reject) => {
let chain=Promise.resolve();
chain=chain.then(()=>this.checkNodeVersion())
chain=chain.then(()=>this.initArgs())
chain=chain.then(()=>this.init())
chain=chain.then(()=>this.exec())
chain.catch(err=>{
log.error(err.message)
})
})
}
initArgs(){
this._cmd=this._argv[this._argv.length-1];
this._argv=this._argv.slice(0,this._argv.length-1);
// console.log('this._cmd,this._argv',this._cmd,this._argv)
}
checkNodeVersion(){
// 获取当前 node 版本号
const currentVersion=process.version;
const lowestVersion=LOWEST_NODE_VERSION;
if(!semver.gte(currentVersion,lowestVersion)){
throw new Error(colors.red(`cxvh-cli 需要安装 v${lowestVersion} 以上版本的 Node.js`));
}
// 比对最低版本号
}
init(){
throw new Error('init 必须实现')
}
exec(){
throw new Error('exec 必须实现')
}
}
module.exports = command;


相关依赖

  • yargs通过解析参数并生成优雅的用户界面来帮助您构建交互式命令行工具

小技巧

  • npm link时删掉node_modulespackage-lock.json,会自动生成package-lock.json并安装依赖
  • lerna add不指定路径就是安装到所有模块,一般会指定如:lerna add axios packages/core
  • 操作没问题的情况下报错==>coannot find module '@cxvh-cli/log'如何解决?重新lerna link
  • lerna add colors core/cli相当于在core/cli文件夹下执行npm i -S colors
  • npm unlink test其它路径执行需要指定包名
  • noderequire支持加载:1.绝对路径;2.相对路径;3.内置模块;4.加载node_modules的模块
  • node支持字符串代码,如:node -e "require('./core/cli/bin/index.js')"

相关知识点、拓展手册