even

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

lerna使用

1、原生脚手架开发痛点分析

1. 痛点一: 重复操作
 - 多Package本地link
 - 多Package本地安装
 - 多Package单元测试
 - 多Package代码提交
 - 多Package代码发布
2. 痛点二:版本一致性
- 发布时版本一致性
- 发布后相互依赖版本升级

package越多, 管理复杂度越高

2、lerna简价

lerna是一个优化基于 git + npm的多package项目的管理工具

优势:

  • 大幅减少重复操作
  • 提升操作的标准化

lerna 是架构优化的产物,它揭示了一个架构真理: 项目复杂度提升后,就需要对项目进行架构优化。 架构优化的主要目标往往都是以效能为核心。

3、lerna框架的搭建

// 第一步 进行全局安装lerna
yarn global add lerna

// 第二步 进行项目初始化
npm init -y
lerna init  // lerna初始化  --会自动生成lerna.json的配置文件,packages文件夹,并且自动实现git的初始化

// 第三步 创建.gitignore对一些文件进行git忽略
touch .gitignore  // 添加 .vscode .idea  node_modules  packages/**/node_modules 等路径

// 第四步 在npm官网上创建组织,这点关乎后期发布成功问题,点击 add organization进行添加,注意组织的名称尽量与项目的name保持一致

// 第五步 在项目中利用 lerna create [package name] 进行创建分包,名称需要是 @[group name]/[package name]进行创建

4、lerna的简单使用

lerna init  #项目初始化
lerna add <package name> #在所有的package中添加指定的依赖
lerna add <package name> packages/<local package> # 在本地的local项目中添加指定的依赖, packages表示目录文件夹

lerna link  # 在根目录下执行lerna link,那么在本地之间会自动建立包的引用, 如在core中: @even-cli/utils: '^1.0.0',那么会自动进行软链,但是建议使用本地引用: @even-cli/utils: 'file:../utils'进行使用,注意更改后需要npm install一下,引入的时候是const utils = require('@even-cli/utils')

lerna clean # 把所有项目下的node_modules进行删除
lerna bootstrap # 对所有的依赖进行重新安装
lerna run <script name> #执行所以package下的script name的指令,如果指令存在的话
lerna run --scope <package path> <script> # 如: lerna run --scope @even-cli/utils test 表示执行utils这个包下的test脚本
lerna changed #用来查看哪些包发生了变更, 需要被发布
lerna diff #查看当前文件相对到上次提交发生的变化
lerna version #进行版本号的修改管理
lerna publish #进行包的发布, 注意不要使用npm publish, 而是使用lerna publish

常见报错注意:

  • 发布时组织与包名需要一一对应

  • 如果发布的过程中,报nolicense的错误,那么需要在项目的根目录下添加 LICENSE.md文件

  • 需要在每个包的package.json中添加下面代码,在发布的过程中有报E402 You must sign up for private packages的情况下

  "publishConfig": {
    "access": "public"
  }

注意: 在lerna中,如果不需要用package文件夹,那么就需要在lerna.json中改定义,如下:

{
  //"packages": [ "packages/*"],
  // 改成你需要定义的文件夹
  "packages": ["command/*", "core/*", "models/*", "tools/*"],
  "version": "0.0.1"
}

5、yargs入门

yargs的常规用法如下:

#! /usr/bin/env node
import yargs from 'yargs/yargs'
import dedent from 'dedent'

const pkg = require('../package.json') // 导入package.json的配置

// 获取到指令的参数
const argv: any = process.argv.slice(2)

const context: Record<string, string> = {
  evenVersion: pkg.version,  // 注意这里不要用version
}

const cli = yargs()

cli
  .usage('Usage: even [command] <option>') // 表示提示命令的用法
  //声明最少参数,如果少于指定参数,则进行提示
  .demandCommand(1, 'A command is required. Pass --help to see all available commands and options.')
  .strict() // 采用严格模式
  .alias('h', 'help') // 取别名
  .alias('v', 'version')
  .wrap(cli.terminalWidth()) // wrap里面可以是一个数值,表示命令与解析的距离,同时可以使用 cli提供的方法来定义宽度
  .epilogue(dedent`this is end of command`) // 这个指令表示加在末尾的文本,dedent表示即使换行也不进行文本缩进
  //为全局command添加选项: options
  .options({
    // 可以在options里面进行批量配置指令
    debug: {
      type: 'boolean',
      describe: 'start debug mode',
      alias: 'd',
    },
  })
  .option('log', {
    // 这个命令是进行单个指令配置
    type: 'boolean',
    alias: 'l',
    describe: 'this is test log',
  })
  .group(['debug', 'log'], 'spec options:') //对指令进行分组,把debug与log放能spec options下面
  .recommendCommands() // 命令纠错提示, 会根据当前输入的command去找最相似的进行提示
  .fail((msg: string, err: Error) => {
    // 定制错误信息
    console.log(msg)
  })
  .command(
    // 命令的定义方式一
    'serve [port]',
    'start the serve',
    (yargs) => {},
    (argv) => {}
  )
  // .command({  // 命令的定义方式二
  //   command: 'ls',
  //   aliases: ['la'],
  //   describe: 'ls all argv',
  //   builder: (yargs) => {
  //     return yargs.option('all', {
  //       type: 'boolean',
  //       alias: 'a',
  //       describe: 'show all argv',
  //     })
  //   },
  //   handler: (argv) => {
  //     console.log(argv)
  //   },
  // })
  .command('ls', false, {
    // 命令的定义方式三,注意第二个参数表示不展示命令在帮助信息里面
    // 表示不在帮助信息中展示,即为内部使用
    aliases: ['la'],
    describe: 'ls all argv',
    builder: (yargs) => {
      return yargs.option('all', {
        type: 'boolean',
        alias: 'a',
        describe: 'show all argv',
      })
    },
    handler: (argv) => {
      console.log(argv)
    },
  })
  .parse(argv, context)

注意: import-local这个库是用于优先引入项目的本地库,示例如下

#!/usr/bin/env node

"use strict";

/* eslint-disable import/no-dynamic-require, global-require */
const importLocal = require("import-local");

if (importLocal(__filename)) {  // 先引入本地的库,如果本地库对应的为null,那么就会引入全局的库
  require("npmlog").info("cli", "using local version of lerna");
} else {
  require(".")(process.argv.slice(2));
}

pkg-dir是查找package.json的上层文件夹目录

6、commander的使用

commander的基础用法

#! /usr/bin/env node
import { program, Command } from 'commander'

const pkg = require('../package.json')

// new Command().name('even-test').usage('<command> [options]').parse(process.argv)

program
  .name('even-test')
  .usage('<command> [options]')
  .version(pkg.version)
  .option('-d, --debug', '启动debug的模式', true)
  .option('-m --mode <name>', '启动指定的mode模式')
  .parse(process.argv)

console.log(program.opts()) // 获取到所有的参数

//这里的name相当于重新定义usage前的name,如: Usage: even-test <command> [options], 通常使用package.json里的bin的key
//以上两种写法都一样,但是更推荐使用第二种用法
//option 第一个参数表示option的名称和简称,第二个参数是表示describe, 第三个参数表示默认值

注意:在进行配置选项或者命令注册的时候表示必填项, [options]中的中括号表示的是可选项

commander的命令注册

#! /usr/bin/env node
import { program, Command } from 'commander'

const pkg = require('../package.json')

program.name('even-test').usage('<command> [options]').version(pkg.version)

// 创建命令
program
  .command('init <name>', { hidden: false, isDefault: false })  // 如果把isDefault设置为true那么even时默认就会执行该命令
  .description('初始化项目')
  .option('-f --force', '强制覆盖本地的项目')
  .action((...rest) => {
    console.log(rest)
  })
//注意:在使用program.command的时候注意program.command返回的结果并不是program对象
//所以进行command后再进行配置全局option或者其他的命令时就需要用分号隔开

//注册子命令通过addCommand进行注册,相当于是命令中套用命令

const serve = new Command() // 创建子目录的根路径调用程序
  .command('serve')
  .description('服务')
  .action(() => {
    console.log('test')
  })

serve // 声明和调用子命令的调用程序
  .command('start [port]')
  .description('启动服务')
  .action((port) => {
    console.log(port)
  })

serve // 声明停止服务的子命令
  .command('stop')
  .description('停止服务')
  .action(() => {
    console.log('stop serve')
  })

program.addCommand(serve)

program.parse(process.argv)

commander的高级用法

#! /usr/bin/env node
import { program, Command } from 'commander'

const pkg = require('../package.json')

program.name('even-test').usage('<command> [options]').version(pkg.version).option('-d, --debug', '打开debug模式')

program
  .command('init <name>')
  .description('初始化项目')
  .option('-f --force', '强制覆盖本地的项目')
  .action((...rest) => {
    // console.log(rest)
  })

// 高级定制1: 如何自定义help信息
// program.outputHelp() // 打印帮助信息
// console.log(program.helpInformation()) // 获取帮助信息,并且打印
// 修改方式一
// program.helpInformation = (...rest) => {
//   return 'this is help info'
// }
//修改方式二
// program.on('--help', () => {
//   console.log('yes help')
// })

// 高级定制2: 监听指定的选项
program.on('option:debug', () => {
  console.log('this is debug')
})
// 注意:如果是监听help那么使用的是 program.on('--help',()=> {}) 进行监听

// 高级定制3:对未知命令的监听
program.on('command:*', (opt) => {
  // 能进入到这里说明,没有匹配到上面所有的命令
  const arr = program.commands.map((cmd) => cmd.name())
  console.log(arr)
})

program.parse(process.argv)

posted on 2022-08-25 12:55  even_blogs  阅读(941)  评论(0编辑  收藏  举报