Featured image of post FSS 全栈脚手架:pnpm + turbo + husky + lint-staged 实战

FSS 全栈脚手架:pnpm + turbo + husky + lint-staged 实战

全栈 monorepo 脚手架实战:pnpm workspace、turbo.json、husky + lint-staged、ESLint + Prettier 统一、Ant Design 集成、桌面端 Tauri 联动。

什么是 FSS

FSS(Full-Stack Scaffold)= 一套多端共享代码的全栈项目脚手架,结构:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
fss/
├── apps/
│   ├── app/        # PC 端
│   ├── desktop/    # 桌面端(Tauri)
│   ├── mini-program/  # 小程序
│   └── web/        # 移动端 Web
├── packages/
│   ├── ui/         # 公共 UI 组件
│   └── utils/      # 公共工具
└── global/
    ├── config/
    ├── proto/
    └── scripts/

核心技术栈:

  • 包管理:pnpm(快、节省磁盘)
  • 构建编排:turbo(增量构建、任务编排)
  • 代码规范:ESLint + Prettier + Stylelint
  • Git 钩子:husky + lint-staged + commitlint
  • UI 框架:Ant Design + Pro Components

初始化

1. 创建根 package.json

1
2
cd fss
pnpm init

2. 创建 pnpm-workspace.yaml

1
2
3
4
5
# pnpm-workspace.yaml
packages:
  - 'apps/*'
  - 'packages/*'
  - 'global/*'

3. 创建 turbo.json

 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
{
  "$schema": "https://turbo.build/schema.json",
  "globalDependencies": ["**/.env.*local", ".env"],
  "globalEnv": ["NODE_ENV"],
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**", "!.next/cache/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "lint": {
      "outputs": []
    },
    "test": {
      "dependsOn": ["^build"],
      "outputs": ["coverage/**"]
    },
    "clean": {
      "cache": false
    }
  }
}

4. 安装依赖

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
pnpm add -D -w \
  @eslint/js \
  eslint \
  eslint-plugin-react \
  eslint-plugin-react-hooks \
  eslint-plugin-react-refresh \
  globals \
  typescript-eslint \
  @typescript-eslint/eslint-plugin \
  @typescript-eslint/parser \
  stylelint \
  stylelint-config-standard \
  stylelint-config-recess-order \
  stylelint-scss \
  stylelint-less \
  postcss-less \
  husky \
  lint-staged \
  prettier

# commitlint
pnpm add -D -w @commitlint/cli @commitlint/config-conventional

5. 创建各端

1
2
3
4
5
6
7
8
# 创建一个 PC 端
cd apps
mkdir app && cd app
pnpm create vite admin-pc --template react-ts

# 移除默认 ESLint
pnpm remove -D @eslint/js eslint eslint-plugin-react-hooks \
  eslint-plugin-react-refresh globals typescript-eslint

ESLint + Prettier 统一

.eslintrc.cjs(根目录)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
module.exports = {
  root: true,
  env: { browser: true, es2022: true, node: true },
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:react/recommended',
    'plugin:react-hooks/recommended',
    'prettier',
  ],
  parser: '@typescript-eslint/parser',
  plugins: ['@typescript-eslint', 'react', 'react-hooks'],
  rules: {
    'react/react-in-jsx-scope': 'off',
    '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
  },
};

.prettierrc

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "printWidth": 100,
  "tabWidth": 2,
  "useTabs": false,
  "semi": true,
  "singleQuote": true,
  "trailingComma": "all",
  "bracketSpacing": true,
  "arrowParens": "always"
}

.stylelintrc.cjs

1
2
3
4
5
6
7
module.exports = {
  extends: ['stylelint-config-standard', 'stylelint-config-recess-order'],
  rules: {
    'no-descending-specificity': null,
    'selector-class-pattern': null,
  },
};

husky + lint-staged

启用 husky

1
2
pnpm run prepare
npx husky add .husky/pre-commit "npx lint-staged"

.lintstagedrc.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
  "*.{ts,tsx,js,jsx}": [
    "eslint --fix",
    "prettier --write"
  ],
  "*.{css,scss,less}": [
    "stylelint --fix",
    "prettier --write"
  ],
  "*.{json,md}": [
    "prettier --write"
  ]
}

commitlint

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [
      2,
      'always',
      ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'chore', 'revert'],
    ],
    'subject-max-length': [2, 'always', 72],
  },
};
1
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'

提交示例:

1
2
git commit -m "feat: 添加用户管理模块"
git commit -m "fix(auth): 修复 token 过期跳转"

turbo 命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
turbo --version

# 执行所有子项目的 build
pnpm turbo run build

# 执行指定子项目
pnpm turbo run dev --filter=admin-pc

# 所有子项目构建
pnpm turbo run build

# 代码校验
pnpm turbo run lint

# 单元测试
pnpm turbo run test

子项目依赖管理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 给指定子项目加 antd
pnpm add antd --filter=admin-pc

# 路由
pnpm add react-router --filter=admin-pc

# 状态管理
pnpm add @reduxjs/toolkit react-redux --filter=admin-pc

# CSS
pnpm add -D unocss --filter=admin-pc
pnpm add @unocss/reset --filter=admin-pc
pnpm add @unocss/preset-wind3 --filter=admin-pc

UI 共享包(packages/ui)

1
2
3
4
5
6
7
cd packages
mkdir ui && cd ui
pnpm init

# 装 antd
pnpm add antd
pnpm add -D @types/react @types/react-dom react react-dom

packages/ui/src/index.ts

1
2
export { default as Button } from './Button';
export { default as Table } from './Table';

在 admin-pc 引用:

1
2
// apps/admin-pc/src/App.tsx
import { Button, Table } from '@fss/ui';

desktop 子项目(Tauri + React)

1
2
3
4
5
cd apps
mkdir desktop && cd desktop

# 初始化 pnpm workspace
pnpm init
1
2
3
4
# pnpm-workspace.yaml
packages:
  - '../app'
  - '.'
1
2
3
4
5
# 加 Tauri CLI
pnpm add -D @tauri-apps/cli@latest -w

# 初始化 Tauri
pnpm tauri init

开发:

1
pnpm tauri dev

打包:

1
pnpm tauri build

全局目录(global/)

1
2
3
4
5
6
7
global/
├── config/         # 通用配置
│   ├── eslint/
│   ├── stylelint/
│   └── prettier/
├── proto/          # Protobuf 定义
└── scripts/        # 构建脚本

性能优化

  • turbo 远程缓存turbo.jsonremoteCache: { teamSlug: "...", token: "..." }
  • pnpm 设置~/.npmrcauto-install-peers=true / strict-peer-dependencies=false
  • 构建缓存outputs: ["dist/**", ".next/**"] 命中增量

下一步

  • 想把 PC 端 + 移动端用一套代码覆盖,看 2021-07-15《PC 前端技术选型》
  • 想用 Tauri 2.x 替代 Electron,看 2020-05-15《Tauri 2.x 跨平台桌面应用》

参考资料

  • pnpm workspace:https://pnpm.io/workspaces
  • Turborepo 文档:https://turbo.build/repo/docs
  • husky:https://typicode.github.io/husky/
  • lint-staged:https://github.com/lint-staged/lint-staged
  • Ant Design:https://ant.design/
使用 Hugo 构建
主题 StackJimmy 设计