nsbp-cli 0.2.27 → 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.
- package/README.md +1 -1
- package/bin/nsbp.js +94 -74
- package/package.json +1 -1
- package/templates/basic/README.md +43 -0
- package/templates/basic/docs/DEVELOPMENT_GUIDE.md +290 -0
- package/templates/basic/docs/ESLINT_AND_PRETTIER.md +184 -0
- package/templates/basic/docs/HUSKY_9_UPGRADE.md +76 -0
- package/templates/basic/docs/HUSKY_ESLINT_SETUP.md +293 -0
- package/templates/basic/docs/SETUP_GIT_HOOKS.md +106 -0
- package/templates/basic/gitignore +3 -0
- package/templates/basic/package.json +27 -3
- package/templates/basic/scripts/setup-husky.js +24 -0
- package/templates/basic/src/Routers.tsx +4 -5
- package/templates/basic/src/client/index.tsx +5 -1
- package/templates/basic/src/component/Header.tsx +10 -10
- package/templates/basic/src/component/Layout.tsx +9 -3
- package/templates/basic/src/component/Theme.tsx +5 -1
- package/templates/basic/src/containers/Home.tsx +141 -76
- package/templates/basic/src/containers/Photo.tsx +30 -18
- package/templates/basic/src/externals/window.d.ts +3 -1
- package/templates/basic/src/reducers/photo.ts +7 -2
- package/templates/basic/src/server/index.ts +35 -26
- package/templates/basic/src/server/photo.ts +14 -7
- package/templates/basic/src/server/utils.tsx +9 -7
- package/templates/basic/src/services/home.ts +1 -1
- package/templates/basic/src/services/photo.ts +28 -30
- package/templates/basic/src/store/constants.ts +1 -1
- package/templates/basic/src/store/index.ts +2 -1
- package/templates/basic/src/styled/component/header.ts +5 -1
- package/templates/basic/src/styled/home.ts +100 -24
- package/templates/basic/src/styled/photo.ts +2 -2
- package/templates/basic/src/utils/config.ts +1 -1
- package/templates/basic/src/utils/fetch.ts +4 -8
- package/templates/basic/src/utils/index.ts +1 -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.
|
|
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(
|
|
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 = [
|
|
86
|
-
|
|
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(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
console.error(chalk.yellow('
|
|
137
|
-
|
|
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(
|
|
146
|
-
|
|
147
|
-
|
|
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(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
console.log('
|
|
169
|
-
console.log('
|
|
170
|
-
console.log(' •
|
|
171
|
-
console.log(' •
|
|
172
|
-
console.log('')
|
|
173
|
-
console.log(
|
|
174
|
-
console.log(
|
|
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
|
@@ -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
|
+
- [ ] 设置自动化部署
|