nsbp-cli 0.2.26 → 0.2.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +1 -1
  2. package/bin/nsbp.js +94 -74
  3. package/package.json +1 -1
  4. package/templates/basic/README.md +43 -0
  5. package/templates/basic/docs/DEVELOPMENT_GUIDE.md +290 -0
  6. package/templates/basic/docs/ESLINT_AND_PRETTIER.md +184 -0
  7. package/templates/basic/docs/HUSKY_9_UPGRADE.md +76 -0
  8. package/templates/basic/docs/HUSKY_ESLINT_SETUP.md +293 -0
  9. package/templates/basic/docs/SETUP_GIT_HOOKS.md +106 -0
  10. package/templates/basic/gitignore +3 -0
  11. package/templates/basic/package.json +27 -3
  12. package/templates/basic/scripts/setup-husky.js +24 -0
  13. package/templates/basic/src/Routers.tsx +4 -5
  14. package/templates/basic/src/client/index.tsx +6 -2
  15. package/templates/basic/src/component/Header.tsx +10 -10
  16. package/templates/basic/src/component/Layout.tsx +10 -4
  17. package/templates/basic/src/component/Theme.tsx +6 -2
  18. package/templates/basic/src/containers/Home.tsx +142 -77
  19. package/templates/basic/src/containers/Login.tsx +3 -3
  20. package/templates/basic/src/containers/Photo.tsx +37 -25
  21. package/templates/basic/src/externals/window.d.ts +3 -1
  22. package/templates/basic/src/reducers/home.ts +1 -1
  23. package/templates/basic/src/reducers/index.ts +1 -1
  24. package/templates/basic/src/reducers/photo.ts +8 -3
  25. package/templates/basic/src/server/index.ts +35 -26
  26. package/templates/basic/src/server/photo.ts +14 -7
  27. package/templates/basic/src/server/utils.tsx +13 -15
  28. package/templates/basic/src/services/home.ts +3 -28
  29. package/templates/basic/src/services/photo.ts +30 -32
  30. package/templates/basic/src/store/constants.ts +1 -1
  31. package/templates/basic/src/store/index.ts +3 -2
  32. package/templates/basic/src/styled/component/header.ts +5 -1
  33. package/templates/basic/src/styled/home.ts +100 -24
  34. package/templates/basic/src/styled/photo.ts +2 -2
  35. package/templates/basic/src/utils/config.ts +1 -1
  36. package/templates/basic/src/utils/fetch.ts +4 -8
  37. package/templates/basic/src/utils/index.ts +1 -1
  38. package/templates/basic/tsconfig.json +6 -1
package/README.md CHANGED
@@ -147,7 +147,7 @@ node ./bin/nsbp.js --help # Test CLI locally
147
147
 
148
148
  - **Package Name**: `nsbp-cli`
149
149
  - **Bin Command**: `nsbp` (install globally and run `nsbp --help`)
150
- - **Version**: `0.2.26`
150
+ - **Version**: `0.2.29`
151
151
  - **Dependencies**: chalk, commander, fs-extra, inquirer
152
152
  - **Package Manager**: Uses pnpm (also compatible with npm)
153
153
  - **Node Version**: >=16.0.0
package/bin/nsbp.js CHANGED
@@ -1,55 +1,59 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const { Command } = require('commander');
4
- const chalk = require('chalk');
5
- const fs = require('fs-extra');
6
- const path = require('path');
7
- const { execSync } = require('child_process');
8
- const inquirer = require('inquirer');
3
+ const { Command } = require('commander')
4
+ const chalk = require('chalk')
5
+ const fs = require('fs-extra')
6
+ const path = require('path')
7
+ const { execSync } = require('child_process')
8
+ const inquirer = require('inquirer')
9
9
 
10
10
  // Read version from package.json dynamically
11
- const packageJson = require('../package.json');
11
+ const packageJson = require('../package.json')
12
12
 
13
- const program = new Command();
13
+ const program = new Command()
14
14
 
15
15
  program
16
16
  .name('nsbp')
17
17
  .description('CLI tool to create NSBP (Node React SSR by Webpack) projects')
18
- .version(packageJson.version);
18
+ .version(packageJson.version)
19
19
 
20
20
  program
21
21
  .command('create <project-name>')
22
22
  .description('Create a new NSBP project')
23
- .option('-t, --template <template>', 'Specify template (basic, blog, ecommerce)', 'basic')
23
+ .option(
24
+ '-t, --template <template>',
25
+ 'Specify template (basic, blog, ecommerce)',
26
+ 'basic'
27
+ )
24
28
  .option('--skip-install', 'Skip pnpm install')
25
29
  .action(async (projectName, options) => {
26
- console.log(chalk.cyan(`🚀 Creating NSBP project: ${projectName}`));
27
-
30
+ console.log(chalk.cyan(`🚀 Creating NSBP project: ${projectName}`))
31
+
28
32
  try {
29
33
  // Check if directory already exists
30
- const targetDir = path.join(process.cwd(), projectName);
34
+ const targetDir = path.join(process.cwd(), projectName)
31
35
  if (fs.existsSync(targetDir)) {
32
36
  const { overwrite } = await inquirer.prompt([
33
37
  {
34
38
  type: 'confirm',
35
39
  name: 'overwrite',
36
40
  message: `Directory "${projectName}" already exists. Overwrite?`,
37
- default: false,
38
- },
39
- ]);
41
+ default: false
42
+ }
43
+ ])
40
44
  if (!overwrite) {
41
- console.log(chalk.yellow('Operation cancelled.'));
42
- process.exit(0);
45
+ console.log(chalk.yellow('Operation cancelled.'))
46
+ process.exit(0)
43
47
  }
44
- fs.removeSync(targetDir);
48
+ fs.removeSync(targetDir)
45
49
  }
46
50
 
47
51
  // Create project directory
48
- fs.ensureDirSync(targetDir);
52
+ fs.ensureDirSync(targetDir)
49
53
 
50
54
  // Get template source path from built-in templates
51
- const templateSource = path.join(__dirname, '../templates/basic');
52
-
55
+ const templateSource = path.join(__dirname, '../templates/basic')
56
+
53
57
  // List of files/directories to copy
54
58
  const copyItems = [
55
59
  'src',
@@ -67,112 +71,128 @@ program
67
71
  'gitignore',
68
72
  'Makefile',
69
73
  'README.md'
70
- ];
74
+ ]
71
75
 
72
76
  // Copy each item
73
- console.log(chalk.cyan('📦 Copying project files...'));
77
+ console.log(chalk.cyan('📦 Copying project files...'))
74
78
  for (const item of copyItems) {
75
- const source = path.join(templateSource, item);
76
- const target = path.join(targetDir, item);
79
+ const source = path.join(templateSource, item)
80
+ const target = path.join(targetDir, item)
77
81
  if (fs.existsSync(source)) {
78
82
  if (fs.statSync(source).isDirectory()) {
79
83
  fs.copySync(source, target, {
80
84
  filter: (src) => {
81
85
  // Exclude node_modules, build, .temp_cache, etc.
82
86
  // Use relative path to avoid matching parent directory names
83
- const relativePath = path.relative(source, src);
84
- const segments = relativePath.split(path.sep);
85
- const excluded = ['node_modules', '.temp_cache', 'build', '.git', '.DS_Store', '.serena'];
86
- return !segments.some(seg => excluded.includes(seg));
87
+ const relativePath = path.relative(source, src)
88
+ const segments = relativePath.split(path.sep)
89
+ const excluded = [
90
+ 'node_modules',
91
+ '.temp_cache',
92
+ 'build',
93
+ '.git',
94
+ '.DS_Store',
95
+ '.serena'
96
+ ]
97
+ return !segments.some((seg) => excluded.includes(seg))
87
98
  }
88
- });
99
+ })
89
100
  } else {
90
- fs.copySync(source, target);
101
+ fs.copySync(source, target)
91
102
  }
92
103
  }
93
104
  }
94
105
 
95
106
  // Rename gitignore to .gitignore
96
- const gitignorePath = path.join(targetDir, 'gitignore');
107
+ const gitignorePath = path.join(targetDir, 'gitignore')
97
108
  if (fs.existsSync(gitignorePath)) {
98
- fs.renameSync(gitignorePath, path.join(targetDir, '.gitignore'));
109
+ fs.renameSync(gitignorePath, path.join(targetDir, '.gitignore'))
99
110
  }
100
111
 
101
112
  // Create package.json for new project
102
- const originalPackage = require(path.join(templateSource, 'package.json'));
113
+ const originalPackage = require(path.join(templateSource, 'package.json'))
103
114
  const newPackage = {
104
115
  ...originalPackage,
105
116
  name: projectName,
106
117
  version: '1.0.0',
107
118
  description: `NSBP project: ${projectName}`
108
- };
119
+ }
109
120
 
110
121
  fs.writeFileSync(
111
122
  path.join(targetDir, 'package.json'),
112
123
  JSON.stringify(newPackage, null, 2)
113
- );
124
+ )
114
125
 
115
126
  // Remove package-lock.json if exists (use pnpm instead)
116
- const packageLockPath = path.join(targetDir, 'package-lock.json');
127
+ const packageLockPath = path.join(targetDir, 'package-lock.json')
117
128
  if (fs.existsSync(packageLockPath)) {
118
- fs.removeSync(packageLockPath);
129
+ fs.removeSync(packageLockPath)
119
130
  }
120
131
 
121
132
  // Create .npmignore to prevent package-lock.json from being committed
122
- const npmignorePath = path.join(targetDir, '.npmignore');
133
+ const npmignorePath = path.join(targetDir, '.npmignore')
123
134
  if (!fs.existsSync(npmignorePath)) {
124
- fs.writeFileSync(npmignorePath, 'package-lock.json\n');
135
+ fs.writeFileSync(npmignorePath, 'package-lock.json\n')
125
136
  }
126
137
 
127
138
  // Optionally install dependencies
128
139
  if (!options.skipInstall) {
129
140
  // Check if pnpm is available
130
141
  try {
131
- execSync('which pnpm', { stdio: 'ignore' });
142
+ execSync('which pnpm', { stdio: 'ignore' })
132
143
  } catch {
133
- console.error(chalk.red('❌ pnpm is not installed or not available in PATH.'));
134
- console.error(chalk.yellow('Please install pnpm before continuing:'));
135
- console.error(chalk.cyan(' npm install -g pnpm'));
136
- console.error(chalk.yellow('Or use --skip-install flag to skip dependency installation.'));
137
- process.exit(1);
144
+ console.error(
145
+ chalk.red(' pnpm is not installed or not available in PATH.')
146
+ )
147
+ console.error(chalk.yellow('Please install pnpm before continuing:'))
148
+ console.error(chalk.cyan(' npm install -g pnpm'))
149
+ console.error(
150
+ chalk.yellow(
151
+ 'Or use --skip-install flag to skip dependency installation.'
152
+ )
153
+ )
154
+ process.exit(1)
138
155
  }
139
-
140
- console.log(chalk.cyan('📦 Installing dependencies...'));
141
- process.chdir(targetDir);
142
- execSync('pnpm install', { stdio: 'inherit' });
156
+
157
+ console.log(chalk.cyan('📦 Installing dependencies...'))
158
+ process.chdir(targetDir)
159
+ execSync('pnpm install', { stdio: 'inherit' })
143
160
  }
144
161
 
145
- console.log(chalk.green(`✅ NSBP project "${projectName}" created successfully!`));
146
- console.log(chalk.yellow('\nNext steps:'));
147
- console.log(` cd ${projectName}`);
162
+ console.log(
163
+ chalk.green(`✅ NSBP project "${projectName}" created successfully!`)
164
+ )
165
+ console.log(chalk.yellow('\nNext steps:'))
166
+ console.log(` cd ${projectName}`)
148
167
  if (options.skipInstall) {
149
- console.log(' pnpm install');
168
+ console.log(' pnpm install')
150
169
  }
151
- console.log(' pnpm run dev');
152
- console.log(chalk.cyan('\nHappy coding! 🎉'));
153
-
170
+ console.log(' pnpm run dev')
171
+ console.log(chalk.cyan('\nHappy coding! 🎉'))
154
172
  } catch (error) {
155
- console.error(chalk.red(`❌ Error creating project: ${error.message}`));
156
- process.exit(1);
173
+ console.error(chalk.red(`❌ Error creating project: ${error.message}`))
174
+ process.exit(1)
157
175
  }
158
- });
176
+ })
159
177
 
160
178
  program
161
179
  .command('info')
162
180
  .description('Display information about NSBP')
163
181
  .action(() => {
164
- console.log(chalk.cyan('NSBP - Node React SSR by Webpack'));
165
- console.log(chalk.gray('A lightweight React SSR framework with full Webpack control.'));
166
- console.log('');
167
- console.log(chalk.yellow('Key Features:'));
168
- console.log(' • ~60% less resource usage than Next.js');
169
- console.log(' Full Webpack configuration control');
170
- console.log(' • TypeScript support out of the box');
171
- console.log(' • Built-in image service');
172
- console.log('');
173
- console.log(chalk.cyan('Website: ') + 'https://github.com/nsbp/nsbp');
174
- console.log(chalk.cyan('Usage: ') + 'nsbp create my-app');
175
- });
182
+ console.log(chalk.cyan('NSBP - Node React SSR by Webpack'))
183
+ console.log(
184
+ chalk.gray('A lightweight React SSR framework with full Webpack control.')
185
+ )
186
+ console.log('')
187
+ console.log(chalk.yellow('Key Features:'))
188
+ console.log(' • Lightweight and flexible')
189
+ console.log(' • Full Webpack configuration control')
190
+ console.log(' • TypeScript support out of the box')
191
+ console.log(' Built-in image service')
192
+ console.log('')
193
+ console.log(chalk.cyan('Website: ') + 'https://github.com/nsbp/nsbp')
194
+ console.log(chalk.cyan('Usage: ') + 'nsbp create my-app')
195
+ })
176
196
 
177
197
  // Parse command line arguments
178
- program.parse();
198
+ program.parse()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nsbp-cli",
3
- "version": "0.2.26",
3
+ "version": "0.2.29",
4
4
  "description": "CLI tool for creating NSBP (Node React SSR by Webpack) projects",
5
5
  "main": "index.js",
6
6
  "homepage": "https://nsbp.erishen.cn/",
@@ -2,6 +2,49 @@
2
2
 
3
3
  🌐 **Online Demo**: [https://nsbp.erishen.cn/](https://nsbp.erishen.cn/)
4
4
 
5
+ ## 🚀 快速开始
6
+
7
+ ```bash
8
+ # 1. 安装依赖
9
+ pnpm install
10
+
11
+ # 2. 初始化 Git hooks(代码质量检查)
12
+ pnpm run prepare
13
+
14
+ # 3. 配置环境变量
15
+ cp .env.example .env
16
+
17
+ # 4. 启动开发环境
18
+ pnpm run dev
19
+ ```
20
+
21
+ ## 📝 开发工具
22
+
23
+ 本项目配置了完整的代码质量检查工具:
24
+
25
+ - **ESLint**: TypeScript + React 代码质量检查
26
+ - **Prettier**: 自动代码格式化
27
+ - **Husky**: Git hooks 自动化
28
+
29
+ ### 代码检查命令
30
+
31
+ ```bash
32
+ pnpm run lint # Lint 检查
33
+ pnpm run lint:fix # Lint 自动修复
34
+ pnpm run format # 格式化代码
35
+ ```
36
+
37
+ ### Git Hooks
38
+
39
+ - `pre-commit`: 提交前自动 lint 和格式化
40
+ - `pre-push`: 推送前运行完整 lint 检查
41
+ - `commit-msg`: 验证提交信息格式(Conventional Commits)
42
+
43
+ 详细配置请查看:
44
+ - [docs/ESLINT_AND_PRETTIER.md](./docs/ESLINT_AND_PRETTIER.md) - ESLint 和 Prettier 配置
45
+ - [docs/SETUP_GIT_HOOKS.md](./docs/SETUP_GIT_HOOKS.md) - Git hooks 配置
46
+ - [docs/DEVELOPMENT_GUIDE.md](./docs/DEVELOPMENT_GUIDE.md) - 完整开发指南
47
+
5
48
  ## 环境变量配置
6
49
 
7
50
  ### 快速开始
@@ -0,0 +1,290 @@
1
+ # NSBP 开发指南
2
+
3
+ ## 🚀 快速开始
4
+
5
+ ### 1. 安装依赖
6
+
7
+ ```bash
8
+ # 使用 pnpm(Husky hooks 自动创建)
9
+ pnpm install
10
+ ```
11
+
12
+ ⚠️ **注意**:Husky 9.x 会自动创建 Git hooks,无需运行 `pnpm run prepare`。
13
+
14
+ ### 2. 启动开发环境
15
+
16
+ ```bash
17
+ # 开发模式(带热重载)
18
+ pnpm run dev
19
+
20
+ # 或分步启动
21
+ pnpm run dev:init # 初始化构建
22
+ pnpm run dev:build:* # 监听文件变化
23
+ pnpm run dev:build:start # 启动服务器
24
+ ```
25
+
26
+ ### 3. 访问应用
27
+
28
+ - **客户端渲染**: http://localhost:3001/
29
+ - **服务端渲染**: http://localhost:3001/?seo=1
30
+ - **BrowserSync**: http://localhost:3000/
31
+
32
+ ## 📝 开发工作流
33
+
34
+ ### 提交代码
35
+
36
+ ```bash
37
+ # 1. 创建功能分支
38
+ git checkout -b feat/your-feature
39
+
40
+ # 2. 开发并测试
41
+ # ...
42
+
43
+ # 3. 格式化代码
44
+ pnpm run format
45
+
46
+ # 4. Lint 检查
47
+ pnpm run lint
48
+
49
+ # 5. 提交(Git hooks 自动运行 lint-staged)
50
+ git add .
51
+ git commit -m "feat: add new feature"
52
+
53
+ # 6. 推送(Git hooks 自动运行 lint)
54
+ git push origin feat/your-feature
55
+ ```
56
+
57
+ ### 提交信息格式
58
+
59
+ 遵循 Conventional Commits 规范:
60
+
61
+ ```
62
+ <type>(<scope>): <subject>
63
+
64
+ <body>
65
+
66
+ <footer>
67
+ ```
68
+
69
+ **类型 (type)**:
70
+ - `feat`: 新功能
71
+ - `fix`: Bug 修复
72
+ - `docs`: 文档更新
73
+ - `style`: 代码格式化
74
+ - `refactor`: 重构(非功能或 Bug)
75
+ - `test`: 测试相关
76
+ - `chore`: 构建/工具链相关
77
+ - `perf`: 性能优化
78
+ - `ci`: CI 配置变更
79
+
80
+ **示例**:
81
+ ```bash
82
+ git commit -m "feat(core): add SSR data preloading support"
83
+ git commit -m "fix(client): resolve hydration mismatch error"
84
+ git commit -m "docs(readme): update installation guide"
85
+ ```
86
+
87
+ ## 🛠️ 常用命令
88
+
89
+ ### 开发命令
90
+
91
+ ```bash
92
+ pnpm run dev # 完整开发环境
93
+ pnpm run dev:init # 初始化构建
94
+ pnpm run dev:build:* # 监听文件变化
95
+ pnpm run dev:build:start # 启动开发服务器
96
+ ```
97
+
98
+ ### 构建命令
99
+
100
+ ```bash
101
+ pnpm run build # 生产构建
102
+ pnpm run build:server # 构建服务端
103
+ pnpm run build:client # 构建客户端
104
+ pnpm run start # 启动生产服务器
105
+ ```
106
+
107
+ ### 代码质量
108
+
109
+ ```bash
110
+ pnpm run lint # ESLint 检查
111
+ pnpm run lint:fix # ESLint 自动修复
112
+ pnpm run format # Prettier 格式化
113
+ pnpm run lint-staged # 对暂存文件运行 lint(Git 钩子自动执行)
114
+ ```
115
+
116
+ ### 清理命令
117
+
118
+ ```bash
119
+ pnpm run clean # 清理构建产物和缓存
120
+ rm -rf .temp_cache # 清理 Webpack 缓存
121
+ rm -rf node_modules # 清理依赖
122
+ pnpm install # 重新安装依赖
123
+ ```
124
+
125
+ ### Docker 命令
126
+
127
+ ```bash
128
+ # 生产环境
129
+ make build # 构建镜像
130
+ make prod # 启动生产环境
131
+ make logs # 查看日志
132
+ make restart # 重启容器
133
+ make shell # 进入容器
134
+ make down # 停止容器
135
+ make clean # 完全清理
136
+
137
+ # 开发环境
138
+ make dev # 启动开发环境
139
+ make logs-dev # 查看开发日志
140
+ make rebuild-dev # 重新构建并启动
141
+ ```
142
+
143
+ ## 📂 项目结构
144
+
145
+ ```
146
+ nsbp/
147
+ ├── cli/ # CLI 工具和模板
148
+ │ ├── bin/ # CLI 二进制文件
149
+ │ ├── templates/ # 项目模板
150
+ │ └── scripts/ # 构建脚本
151
+ ├── config/ # Webpack 配置
152
+ │ ├── webpack.base.js # 基础配置
153
+ │ ├── webpack.client.js # 客户端配置
154
+ │ └── webpack.server.js # 服务端配置
155
+ ├── public/ # 静态资源输出目录
156
+ ├── src/ # 源代码目录
157
+ │ ├── client/ # 客户端入口
158
+ │ ├── server/ # 服务端代码
159
+ │ ├── containers/ # 页面组件
160
+ │ ├── component/ # 公共组件
161
+ │ ├── styled/ # 样式组件
162
+ │ ├── services/ # API 服务
163
+ │ ├── reducers/ # Redux reducers
164
+ │ ├── store/ # Redux store
165
+ │ └── utils/ # 工具函数
166
+ ├── scripts/ # Node.js 脚本
167
+ ├── docker/ # Docker 配置
168
+ ├── docs/ # 文档
169
+ └── .husky/ # Git hooks
170
+ ```
171
+
172
+ ## 🔍 代码检查工具
173
+
174
+ ### ESLint
175
+
176
+ - **配置文件**: `.eslintrc.js`
177
+ - **忽略文件**: `.eslintignore`
178
+ - **用途**: TypeScript + React 代码质量检查
179
+
180
+ **规则**:
181
+ - TypeScript: 类型检查、未使用变量警告
182
+ - React: Hooks 规则、组件最佳实践
183
+ - Prettier: 代码风格一致性
184
+
185
+ ### Prettier
186
+
187
+ - **配置文件**: `.prettierrc.js`
188
+ - **用途**: 自动格式化代码
189
+
190
+ **配置**:
191
+ - 2 空格缩进
192
+ - 单引号
193
+ - 无分号
194
+ - 100 字符换行
195
+
196
+ ### Husky
197
+
198
+ - **配置目录**: `.husky/`
199
+ - **用途**: Git 钩子自动化
200
+
201
+ **钩子**:
202
+ - `pre-commit`: 提交前 lint 暂存文件
203
+ - `pre-push`: 推送前全量 lint
204
+ - `commit-msg`: 验证提交信息格式
205
+
206
+ ## 🐛 常见问题
207
+
208
+ ### Webpack 缓存错误
209
+
210
+ **问题**:
211
+ ```
212
+ Cannot find module 'xxx'
213
+ Restoring failed for ResolverCachePlugin
214
+ ```
215
+
216
+ **解决方案**:
217
+ ```bash
218
+ # 清理缓存
219
+ pnpm run clean
220
+ rm -rf .temp_cache
221
+
222
+ # 重新构建
223
+ pnpm run dev
224
+ ```
225
+
226
+ ### Git 钩子失败
227
+
228
+ **问题**:
229
+ ```
230
+ husky - pre-commit hook failed
231
+ ```
232
+
233
+ **解决方案**:
234
+ ```bash
235
+ # 查看错误
236
+ pnpm run lint
237
+
238
+ # 自动修复
239
+ pnpm run lint:fix
240
+
241
+ # 重新提交
242
+ git add .
243
+ git commit -m "style: resolve linting issues"
244
+ ```
245
+
246
+ ### TypeScript 类型错误
247
+
248
+ **问题**: 编辑器显示类型错误,但项目能编译
249
+
250
+ **解决方案**:
251
+ ```bash
252
+ # 重启 TypeScript 服务器
253
+ # VSCode: Ctrl+Shift+P -> "TypeScript: Restart TS Server"
254
+
255
+ # 确保项目已编译
256
+ pnpm run build:server
257
+ ```
258
+
259
+ ### Docker 权限错误
260
+
261
+ **问题**:
262
+ ```
263
+ EACCES: permission denied
264
+ ```
265
+
266
+ **解决方案**:
267
+ Docker 已在 `entrypoint.sh` 中自动修复权限,无需手动处理。
268
+
269
+ ## 📚 相关文档
270
+
271
+ - [ESLINT_AND_PRETTIER.md](./ESLINT_AND_PRETTIER.md) - 代码风格配置
272
+ - [SETUP_GIT_HOOKS.md](./SETUP_GIT_HOOKS.md) - Git hooks 配置
273
+ - [README.md](../README.md) - 项目总览
274
+
275
+ ## 💡 最佳实践
276
+
277
+ 1. **提交前**: 总是运行 `pnpm run format` 和 `pnpm run lint:fix`
278
+ 2. **分支管理**: 使用功能分支,不直接在 main/master 分支开发
279
+ 3. **提交信息**: 遵循 Conventional Commits 规范
280
+ 4. **代码审查**: 提交 PR 前自查代码质量和格式
281
+ 5. **定期清理**: 定期运行 `pnpm run clean` 清理缓存
282
+ 6. **依赖更新**: 使用 `pnpm update` 而不是手动修改版本号
283
+
284
+ ## 🎯 下一步
285
+
286
+ - [ ] 配置 CI/CD 流程
287
+ - [ ] 添加单元测试
288
+ - [ ] 添加 E2E 测试
289
+ - [ ] 配置代码覆盖率
290
+ - [ ] 设置自动化部署