Yongzhi
monorepo工程化实践 - 脚手架开发
背景
因当下场景需要快速创建项目,目前参考了vite
、astro
等脚手架的工程化实践,记录下本次monorepo工程化实践。废话不多说下面开始👇
monorepo项目搭建
有关于什么是monorepo
这里不过多赘述,有兴趣可以点击博客查看,我个人推荐使用pnpm
作为包管理工具,因为pnpm
的特性可以很好的解决monorepo
的依赖管理问题,这里不过多赘述,有兴趣可以点击博客查看。
我个人推荐使用pnpm
创建项目,相关细节请移步pnpm monorepo查看。
初始化项目
mkdir terky
cd terky
pnpm init
新建
pnpm-workspace.yaml
文件,配置packages
目录下的所有包
packages:
- 'packages/*'
因为这个项目是一个脚手架,所以不仅要有脚手架包,还要有模板包,这是我的项目目录结构
├─packages
│ ├─create-terky
│ ├─template-vue3-ts
│ ├─template-vue3
│ ├─src
eslint
此配置作为基础配置,会被
packages
下的包继承
- eslint
eslint
核心包 - @typescript-eslint/parser
eslint
解析器,用于解析typescript
代码 - @typescript-eslint/eslint-plugin
eslint
插件,用于检查typescript
代码
pnpm add eslint -D -w
pnpm add @typescript-eslint/parser -D -w
pnpm add @typescript-eslint/eslint-plugin -D -w
新建
.eslintrc.json
文件,配置eslint
{
"parser": "@typescript-eslint/parser",
"env": {
"es6": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2021
},
"rules": {
}
}
commit规范
首先全局安装 commitizen
,这样可以在本地使用cz
或git cz
替代git commit
pnpm add -g commitizen
目前有很多commit
工具帮助我们进行规范化commit
格式,关于commit
规范你可以点击这里查看
推荐使用cz-git
,官方提供了commit
规范模版,以及支持emoji
、中文英文双语,而且官网也是中文描述
pnpm add cz-git -D -w
具体如何配置,请移步官网文档查看,写的很清楚,可以直接选择喜欢的配置模版直接使用,建议搭配commitlint
使用。安装完成后向.commitlintrc.js
添加检测规则
{
...
extends: ['@commitlint/config-conventional']
...
}
添加scripts
脚本命令
{
"scripts": {
"commitlint": "commitlint --config .commitlintrc.js -e -V"
}
}
commitlint
需要搭配husky
,需要添加commit-msg hooks
pnpm add husky @commitlint/{config-conventional,cli} -D -w
husky install
npx husky add .husky/commit-msg 'npm run commitlint'
create-terky
terky
是一个现代化的脚手架,通过使用npm create terky
可以快速从github
的模版列表中拉取我们所需要的项目基础模版,省去重复的webpack
配置
和一起其他的项目配置
npm create <package>
是什么?
一般在初始化项目的时候,都会去执行npm init
,会在当前目录生成一个package.json
文件,在init
后面还可以跟一个参数<initializer>
,
当增加上这个参数时,npm
会去查询名为create-<initializer>
这个包,如果本地存在那么就执行本地缓存,反之则同步远程包到本地执行。使用
npm exec
执行create-<initializer>
包的bin
下定义的命令create-<initializer>
,bin
是在package.json
中定义的。
npm v6
版本给init
命令增加了一个别名create
,所以 init
等于create
,详细信息可以点击这里查看
实战
首先需要在本地初始化一个package.json
,并安装所需依赖
pnpm init
pnpm add -D typescript
pnpm add -D unbuild
pnpm add prompts
pnpm add ora
pnpm add kolorist
pnpm add fs-extra
pnpm add execa
pnpm add minimist
- typescript 我使用
typescript
编写项目 - unbuild 打包
typescript
代码 - prompts 给用户提供交互式选择
- ora 可以在控制台使用loading动画
- kolorist 修改控制台中输出的文字的颜色
- fs-extra 基于
fs
模块更好的文件操作API - execa 在
nodejs
中执行命令 - minimist 解析传入的参数
使用tsc --init
初始化tsconfig.json
{
"include": ["build.config.ts","src"],
"compilerOptions": {
"outDir": "dist",
"target": "ES2020",
"module": "ES2020",
"moduleResolution": "bundler",
"strict": true,
"skipLibCheck": true,
"declaration": false,
"sourceMap": false,
"noUnusedLocals": true,
"esModuleInterop": true,
"noEmit": true,
"allowImportingTsExtensions": true
}
}
创建build.config.ts
提供unbuild
打包配置,并在package.json
中增加命令
import { defineBuildConfig } from 'unbuild'
export default defineBuildConfig({
entries: ['src/index.ts'],
clean: true,
rollup: {
inlineDependencies: true,
esbuild: {
minify: true,
},
},
})
{
...
"script": {
"build": "unbuild"
},
...
}
新建index.js
提供程序主入口
#!/usr/bin/env node
import "./dist/index.mjs"
src目录结构如下
├─src
| ├─index.ts
| ├─utils.ts
| ├─actions
| ├─utils.ts
- utils.ts
在这个文件中需要提供一些工具函数及问答交互,有关
prompts
的用法请移步文档查看
import { blue, yellow } from 'kolorist'
import fs from 'fs'
// 默认项目名称
export const defaultDir = 'terky-app'
// 问答交互
export const FRAMEWORKS = [
{
name: 'vue3',
display: 'JavaScript',
color: yellow,
value: 'template-vue3'
},
{
name: 'vue3-ts',
display: 'TypeScript',
color: blue,
value: 'template-vue3-ts'
},
]
// 判断目录是否存在
export const isEmpty = (dir: string) => fs.existsSync(dir)
// 创建项目目录
export const mkdirSync = (dir: string) => fs.mkdirSync(dir)
- git.ts
在这个函数中其实只做了一件事,那就是将用户所选择的模版下载到本地
import path, { dirname } from "path";
import { fileURLToPath } from 'url';
import { execa } from 'execa'
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
export const createRepo = async (targetDir: string, template: string) => {
const filePath = path.join(__dirname, `../${template}`)
await execa('cp', ['-r',filePath, targetDir])
}
- index.ts
这个文件是一个主入口,也是我们打包的入口,执行命令其实就是相当于执行这个文件中的
main
函数
1.首先根据utils
中定义的问答列表,向用户提问🙋。
2.根据用户输入和选择的内容判断目录是否存在
a.如果存在那么退出程序,提示用户目录已经存在
b.不存在目录那么新建目录
3.执行git
中所提供createRepo
函数,初始化目录并拉取模版代码
import { defaultDir, FRAMEWORKS, isEmpty, mkdirSync} from './utils.ts'
import { createRepo } from './actions/git.ts'
import { red, reset, } from 'kolorist'
import prompts from 'prompts'
import path from "path"
import ora from 'ora'
async function main () {
const result = await prompts([
{
type: 'text',
name: 'projectName',
message: reset('👉 项目名称 | Project name:'),
initial: defaultDir
},
{
type: 'select',
name: 'template',
message: reset('👉 选择模板 | Select template:'),
choices: FRAMEWORKS.map((framework) => ({
title: framework.color(framework.name),
value: framework.value,
}))
}
], {
onCancel: () => {
console.log(red('✖') + '操作被取消 | Operation canceled')
}
})
const spinner = ora('正在创建项目 | Creating project...').start()
const { projectName, template } = result
const root = path.join(process.cwd(), projectName)
if (isEmpty(root)) {
console.log(red('✖') + `目录已存在 | Directory already exists: /${projectName}`)
ora().fail('创建失败 | Create failed')
spinner.stop()
return
}
spinner.text = '正在创建目录 | Creating directory...'
await mkdirSync(projectName)
await createRepo(root, template, spinner)
spinner.succeed('创建成功 | Create success')
}
main().catch((err) => {
console.error(err)
process.exit(1)
})
本地测试
还记得前面所提到npm create
的原理吗,现在需要向package.json
中添加bin
字段
{
...
"bin": {
"create-terky": "index.js"
},
"scripts": {
"build": "unbuild"
}
}
现在我们可以执行pnpm build
,生成dist/index.mjs
,因为我们主入口文件需要它
#!/usr/bin/env node
import "./dist/index.mjs"
完成以上步骤后,我们需要在当前目录执行npm link
,将此包链接到全局就可以直接使用bin
下定义的命令create-terky
发布
本地测试完成之后就可以发布到npm
,在发布之前您需要先拥有一个npm
账号,您可以在这里注册。如果您已经拥有账号,
您可以忽略上一步。现在还需要在本地新建一个名为.npmrc
的文件,并写入内容
registry=https://registry.npmjs.org/
这个文件可以让我们在当前项目的目录下执行npm login
时,总是登录到公共的npm
。这样可以避免您登录到可能正在使用您自己的组织内部的私有npm
服务。
现在准备工作已经完毕,开始吧
npm login
在发布之前请您确保您的
package.json
中的name
字段在npm
中是独一无二的,避免与其他模块冲突
npm public
如果没有出现error
提示,那么您就可以登录npm
,搜索自己的包名了。当然您也可以直接尝试npm create terky
来使用我所发布的工具。
完成代码可以点击查看,您也会有好的想法或者模版,
您可以提交PR或发送邮件至<[email protected]>
,我会尽快回复您🦀️🦀️。
在下面您可以看到一些对您有用的链接👇