1. 初始化项目
-
npm init -
修改package.json
"bin": {
"winter_cli": "bin/index.js"
}
- 新增 bin/index.js
#!/usr/bin/env node
//一定要写,表明了index.js是node可执行文件
console.log('test')
- 执行
winter_cli命令
说明还没有建立全局的winter_cli命令,我们可以用npm link链接到全局
此时,全局命令已经链接成功
- PS: 关于
npm link
juejin.cn/post/735555…
2. 定义选项(类似npm -v、node -v这样的命令)
-
安装commander库
npm install commander
Commander 负责将参数解析为选项和命令参数。
PS: 关于Commanderjuejin.cn/post/713192… -
修改bin/index.js文件
#!/usr/bin/env node
//一定要写,表明了index.js是node可执行文件
const { program } = require('commander')
function strToArr (value) {
return value?.split(',')
}
program.version(`version is ${require('../package.json').version}`)
.option('-d, --debug', '调试')
.option('-l, --list <value>', '把字符串分割为数组', strToArr)
.action((options, command) => {
if(options.debug) {
console.log('调试成功')
}
if(options.list !== undefined){
console.log(options.list)
}
})
.parse(process.argv)
- 终端验证
3.设置子命令(类似npm run xxx)
- 修改bin/index.js文件
program
.command('create <filename>')
.description('创建一个项目')
.action((filename) => {
console.log('项目名为:', filename)
})
- 终端验证
4. github拉取代码、设置用户询问和提示
- 安装过程中需要用到的插件:
npm install commander //负责将参数解析为选项和命令参数
npm install download-git-repo //github api 拉取代码
npm install inquirer // 生成用户询问和提示 项目里面用的8.2.2版本,用更高版本会有如下报错,还未解决
npm install ora // 用了做拉取的loading 项目里面用2版本,更高版本也会报错
高版本报错如下:
- 由于接下来的配置文件较多,不能都写在bin/index.js文件中,所以我们调整下目录结构
新增actions.js(写所有的action配置)、constants.js(常量配置,比如version、name)、create.js(拉取代码、生成用户提示、项目配置、选择项目等)、main.js(配置入口、遍历actions里面导出的配置)
//bin/index.js
#!/usr/bin/env node
//一定要写,表明了index.js是node可执行文件
require('../src/main')
//actions.js
const actions = {
create: {
alias: 'crt',
description: 'create a project',
examles: [
'make-cli create <project>'
]
},
config: {
alias: 'conf',
description: 'config project variable',
examles: [
'make-cli config set <key> <value>',
'make-cli config get <key>'
]
},
'*': {
alias: '',
description: 'command not found',
examles: []
}
}
module.exports = {
actions
}
//constants.js
const { name, version } = require('../package.json')
module.exports = {
name,
version
}
//create.js
const download = require('download-git-repo') //github api 拉取代码
const inquirer = require('inquirer') //用户询问
const ora = require('ora') //实现loading效果
// key是给用户选择的名字,value对应github里面的项目名
const templateMap = {
vite_react: 'vite_react_project_template',
ui_library: 'ui-test',
monorepo: 'monorepo-test'
}
const fetchTemplate = async (options, filename) => {
const { template } = options
const templateUrl = `drxiong/${templateMap[template]}`
//根据template模板
const loading = ora('fetching')
loading.start()
// ${process.cwd()} 表示当前目录
console.log(templateUrl,`${process.cwd()}/${filename}`)
download(templateUrl, `${process.cwd()}/${filename}`, err => {
if(err){
console.log('err:', err)
return
}
loading.succeed('success!!!')
})
}
const templateOptions = ['vite_react', 'ui_library', 'monorepo']
const styleOptions = ['无', 'less', 'sass']
// 拉取代码之前询问配置,之后再根据用户选择
const askOptions = async () => {
const { template } = await inquirer.prompt([
{
type: 'list',
name: 'template',
message: '请选择一个你要创建的项目',
choices: templateOptions
}
])
return {
template
}
}
module.exports = async (projectName) => {
const result = await askOptions()
fetchTemplate(result, projectName)
}
//main.js
const { program } = require('commander')
const { version } = require('./constants')
const { actions } = require('./actions')
const path = require('path')
// 遍历配置
Reflect.ownKeys(actions).forEach(action => {
program.command(action)
.alias(actions[action].alias)
.description(actions[action].description)
.action(()=>{
if(action === '*') {
console.warn(actions[action].description)
} else {
// 寻找对应操作的文件
const actionPath = path.join(__dirname, action)
// 导出文件函数
const func = require(actionPath)
// 执行函数,并传参
typeof func === 'function' && func(...process.argv.slice(3))
}
})
})
program.version(version)
.parse(process.argv) // process.argv 命令行参数
- 终端测试
- 现在
winter-cli项目的根目录执行npm link,将winter-cli命令链接到全局 - 在需要生成新项目模板的目录执行:
winter_cli create first-project
- 此时到xy目录会发现,一个名为
first-project的项目已经生成,且运用了模板vite_react, 代码与github上的vite_react_project_template一致,是一个用vite+pnpm搭建的具有单元测试、eslint等基础功能的react项目模板。
至此这个工具在本地的功能已经完善,下一步就是发到npm上。
5. npm发包
- 手动发包:
npm login
npm publish
- 自动发包
-
配置SSH(自动发包的时候需要自动修改版本号,修改了package.json文件,同时需要把修改后的package.json提交到github上,这时候需要使用ssh鉴权,所以需要配置SSH)
-
生成一对名叫runner的公钥私钥对(为了避免与本机的id_rsa冲突,所以重新起了一个名字叫runner)
-
查看公钥、私钥
-
将公钥拷贝出来配置到github的公钥里面
-
将私钥拷出来放到项目的secrets里面
-
-
- 配置npm的Access Tokens
复制token
到github项目里面配置token
- 新增publish.yml文件
name: Publish Smarty-ui-vite To Npm
# push main分支的时候执行
on:
push:
branches: [main]
jobs:
publish:
runs-on: ubuntu-latest # 一个环境,相当于一台机器
steps:
- uses: actions/checkout@v4 # 拉取代码
- name: Setup Node.js
uses: actions/setup-node@v4 # 设置node版本
with:
node-version: '18'
registry-url: 'https://registry.npmjs.org/'
always-auth: true
- name: Debug .npmrc
run: cat .npmrc
- name: Debug npm version
run: npm --version
- name: Install semver # 安装设置版本的依赖
run: npm install semver
- name: set ssh key # 临时设置 ssh key 【RUNNER_TOKEN需要和github上的私钥名对应】 【1.家目录下新建.ssh文件,2.将github上的私钥写入.ssh/id_rsa,3.修改权限,4.收集 github.com 的公钥信息,并将这些信息追加到 /home/runner/.ssh/known_hosts 文件中】
run: |
mkdir -p /home/runner/.ssh/
echo "${{secrets.RUNNER_TOKEN}}" > /home/runner/.ssh/id_rsa
chmod 600 /home/runner/.ssh/id_rsa
ssh-keyscan "github.com" >> /home/runner/.ssh/known_hosts
- name: Set up Git
run: |
git config --global user.email "1763303455@qq.com"
git config --global user.name "drxiong"
- name: Auto version increment # 获取当前的version, 并修改版本的修订号,然后将修改后的package.json提交到github上
env: # 这里注意commit的描述里面有 [skip ci] 这个不能少,是为了避免在这一次提交过程继续触发这个脚本,导致陷入循环的
NODE_AUTH_TOKEN: ${{ secrets.RUNNER_TOKEN }}
run: |
current_version=$(node -p "require('/home/runner/work/winter_cli/winter_cli/package.json').version")
new_version=$(node -p "const semver = require('semver'); semver.inc('$current_version', 'patch')")
npm version --no-git-tag-version patch
git add .
git commit -m "Auto version increment [skip ci]"
git push git@github.com:drxiong/winter_cli.git
- name: Check environment variable
run: |
if [ -z "${{ secrets.WINTER_CLI_NPM_AUTH_TOKEN }}" ]; then
echo "WINTER_CLI_NPM_AUTH_TOKEN is not set."
else
echo "WINTER_CLI_NPM_AUTH_TOKEN is set."
fi
- name: Publish package # 发包,这里的WINTER_CLI_NPM_AUTH_TOKEN需要分别在npm和github上设置
run: npm set registry https://registry.npmjs.org/ && npm publish --access public --no-git-checks
env:
NODE_AUTH_TOKEN: ${{secrets.WINTER_CLI_NPM_AUTH_TOKEN}}
这样在push main分支的时候,就会自动发布npm包了,并且每次会将version的修订号自动加1