@lark-apaas/fullstack-cli 0.1.0-alpha.1 → 0.1.0-alpha.11
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 +129 -34
- package/bin/cli.js +11 -0
- package/dist/commands/gen-db-schema.d.ts +19 -0
- package/dist/commands/gen-db-schema.js +96 -0
- package/dist/commands/sync.d.ts +4 -0
- package/dist/commands/sync.js +162 -0
- package/dist/config/drizzle.config.d.ts +2 -0
- package/dist/config/drizzle.config.js +19 -0
- package/dist/config/{postinstall.config.d.ts → sync.d.ts} +2 -3
- package/dist/config/sync.js +33 -0
- package/dist/index.d.ts +1 -10
- package/dist/index.js +52 -9
- package/package.json +10 -3
- package/templates/.gitignore.append +8 -1
- package/templates/scripts/gen-openapi.ts +12 -3
- package/bin/sync-scripts.js +0 -185
- package/dist/config/postinstall.config.js +0 -28
- package/dist/postinstall.config.d.ts +0 -22
- package/dist/postinstall.config.js +0 -34
- package/templates/scripts/gen-db-schema.ts +0 -48
- /package/templates/{type.ts → server/type.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# @lark-apaas/fullstack-cli
|
|
2
2
|
|
|
3
|
-
> Fullstack
|
|
3
|
+
> Fullstack 开发工具集 - 文件派生、数据库 Schema 生成、OpenAPI 生成
|
|
4
4
|
|
|
5
5
|
## 功能
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
1. **自动派生** - 自动管理项目脚本和配置文件
|
|
8
|
+
2. **数据库 Schema 生成** - 从现有数据库生成 Drizzle ORM schema
|
|
9
|
+
3. **OpenAPI 生成** - 自动生成 API 文档和客户端 SDK
|
|
8
10
|
|
|
9
11
|
## 安装
|
|
10
12
|
|
|
@@ -14,75 +16,168 @@ npm install --save-dev @lark-apaas/fullstack-cli
|
|
|
14
16
|
|
|
15
17
|
## 使用
|
|
16
18
|
|
|
17
|
-
### 自动派生
|
|
19
|
+
### 1. 自动派生
|
|
18
20
|
|
|
19
|
-
每次运行 `npm install`
|
|
21
|
+
每次运行 `npm install` 时,会自动派生文件到项目:
|
|
20
22
|
|
|
21
23
|
**派生内容:**
|
|
22
|
-
- `scripts/` - 脚本目录(总是覆盖)
|
|
24
|
+
- `scripts/` - Shell 脚本目录(总是覆盖)
|
|
23
25
|
- `.gitignore` - 追加 fullstack-cli 相关忽略规则
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
⚠️ **`scripts/` 目录由 CLI 自动管理,请勿手动修改!**
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
### 2. 生成数据库 Schema
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
从现有数据库生成 Drizzle ORM schema:
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
```bash
|
|
34
|
+
npm run gen:db-schema
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**环境变量配置:**
|
|
38
|
+
```bash
|
|
39
|
+
# 必需
|
|
40
|
+
SUDA_DATABASE_URL=postgresql://user:pass@host:port/dbname?schema=public
|
|
41
|
+
|
|
42
|
+
# 可选
|
|
43
|
+
DB_SCHEMA_OUTPUT=server/database/schema.ts # 输出路径
|
|
44
|
+
DRIZZLE_SCHEMA_FILTER=public,custom # Schema 过滤器
|
|
45
|
+
DRIZZLE_TABLES_FILTER=users,orders # 表过滤器
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**工作流程:**
|
|
49
|
+
1. 从 `SUDA_DATABASE_URL` 读取数据库连接
|
|
50
|
+
2. 使用 CLI 内置的 `drizzle.config.ts`(用户无需维护)
|
|
51
|
+
3. 执行 `drizzle-kit introspect`
|
|
52
|
+
4. 后处理 schema(通过 devtool-kits)
|
|
53
|
+
5. 输出到 `server/database/schema.ts`
|
|
54
|
+
|
|
55
|
+
### 3. 生成 OpenAPI 文档
|
|
56
|
+
|
|
57
|
+
自动生成 OpenAPI 文档和客户端 SDK:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm run gen:openapi
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**环境变量配置:**
|
|
64
|
+
```bash
|
|
65
|
+
# 可选
|
|
66
|
+
APP_MODULE_PATH=server/app.module # AppModule 路径(不含扩展名)
|
|
67
|
+
CLIENT_BASE_PATH=/ # 客户端基础路径
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**工作流程:**
|
|
71
|
+
1. 动态导入用户项目的 `AppModule`
|
|
72
|
+
2. 挂载 DevToolsModule
|
|
73
|
+
3. 生成 OpenAPI 文档和客户端 SDK
|
|
74
|
+
|
|
75
|
+
### 4. CLI 命令
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# 查看帮助
|
|
79
|
+
fullstack-cli --help
|
|
80
|
+
|
|
81
|
+
# 生成数据库 schema
|
|
82
|
+
fullstack-cli gen-db-schema
|
|
83
|
+
|
|
84
|
+
# 生成 OpenAPI
|
|
85
|
+
fullstack-cli gen-openapi
|
|
86
|
+
|
|
87
|
+
# 查看版本
|
|
88
|
+
fullstack-cli --version
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## package.json 配置
|
|
92
|
+
|
|
93
|
+
在用户项目的 `package.json` 中添加:
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"scripts": {
|
|
98
|
+
"gen:db-schema": "fullstack-cli gen-db-schema",
|
|
99
|
+
"gen:openapi": "NODE_ENV=development DEPRECATED_SKIP_INIT_DB_CONNECTION=true fullstack-cli gen-openapi"
|
|
100
|
+
},
|
|
101
|
+
"devDependencies": {
|
|
102
|
+
"@lark-apaas/fullstack-cli": "^0.1.0",
|
|
103
|
+
"drizzle-kit": "^0.20.0" // 如果使用数据库功能
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## 技术细节
|
|
109
|
+
|
|
110
|
+
### 派生配置
|
|
32
111
|
|
|
33
|
-
|
|
112
|
+
派生规则在 `src/postinstall.config.ts` 中定义:
|
|
34
113
|
|
|
35
114
|
```typescript
|
|
36
115
|
export default {
|
|
37
|
-
// 派生规则
|
|
38
116
|
sync: [
|
|
39
|
-
// 派生整个目录
|
|
40
117
|
{
|
|
41
118
|
from: 'templates/scripts',
|
|
42
119
|
to: 'scripts',
|
|
43
120
|
type: 'directory',
|
|
44
121
|
overwrite: true,
|
|
45
122
|
},
|
|
46
|
-
|
|
47
|
-
// 追加内容到文件
|
|
48
123
|
{
|
|
49
124
|
from: 'templates/.gitignore.append',
|
|
50
125
|
to: '.gitignore',
|
|
51
126
|
type: 'append',
|
|
52
127
|
},
|
|
53
128
|
],
|
|
54
|
-
|
|
55
|
-
// 文件权限设置
|
|
56
129
|
permissions: {
|
|
57
130
|
'**/*.sh': 0o755,
|
|
58
131
|
},
|
|
59
132
|
};
|
|
60
133
|
```
|
|
61
134
|
|
|
62
|
-
###
|
|
135
|
+
### Drizzle 配置
|
|
63
136
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
137
|
+
CLI 内置 `templates/drizzle.config.ts`,通过环境变量动态配置:
|
|
138
|
+
- 用户**无需**维护 `drizzle.config.ts`
|
|
139
|
+
- 所有配置通过环境变量传递
|
|
140
|
+
- 使用绝对路径指向用户项目
|
|
67
141
|
|
|
68
|
-
|
|
69
|
-
- `overwrite: true` - 总是覆盖
|
|
70
|
-
- `overwrite: false` - 仅当文件不存在时复制
|
|
142
|
+
### OpenAPI 生成
|
|
71
143
|
|
|
72
|
-
-
|
|
73
|
-
|
|
144
|
+
- 动态导入用户的 `AppModule`
|
|
145
|
+
- 使用 `@lark-apaas/fullstack-nestjs-core` 的 DevToolsModule
|
|
146
|
+
- 自动生成客户端 SDK
|
|
74
147
|
|
|
75
|
-
##
|
|
148
|
+
## 依赖关系
|
|
76
149
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
150
|
+
```
|
|
151
|
+
fullstack-cli
|
|
152
|
+
↓ 依赖
|
|
153
|
+
@lark-apaas/devtool-kits (核心逻辑)
|
|
154
|
+
|
|
155
|
+
用户项目 peerDependencies:
|
|
156
|
+
- @nestjs/core (用于 gen-openapi)
|
|
157
|
+
- @lark-apaas/fullstack-nestjs-core (用于 gen-openapi)
|
|
158
|
+
- drizzle-kit (用于 gen-db-schema,可选)
|
|
159
|
+
```
|
|
84
160
|
|
|
85
|
-
|
|
161
|
+
## 升级说明
|
|
162
|
+
|
|
163
|
+
### 从旧版本迁移
|
|
164
|
+
|
|
165
|
+
**可以删除的文件:**
|
|
166
|
+
- ❌ `scripts/gen-db-schema.ts` - 已内置到 CLI
|
|
167
|
+
- ❌ `scripts/gen-openapi.ts` - 已内置到 CLI
|
|
168
|
+
- ❌ `drizzle.config.ts` - 已由 CLI 管理
|
|
169
|
+
|
|
170
|
+
**更新 package.json:**
|
|
171
|
+
```diff
|
|
172
|
+
{
|
|
173
|
+
"scripts": {
|
|
174
|
+
- "gen:db-schema": "ts-node scripts/gen-db-schema.ts",
|
|
175
|
+
+ "gen:db-schema": "fullstack-cli gen-db-schema",
|
|
176
|
+
- "gen:openapi": "nest start --entryFile ../scripts/gen-openapi",
|
|
177
|
+
+ "gen:openapi": "fullstack-cli gen-openapi"
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
```
|
|
86
181
|
|
|
87
182
|
## License
|
|
88
183
|
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
|
|
9
|
+
// 直接执行编译后的 index.js
|
|
10
|
+
const indexPath = path.join(__dirname, '../dist/index.js');
|
|
11
|
+
await import(indexPath);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 生成数据库 schema
|
|
3
|
+
*
|
|
4
|
+
* 命令行选项:
|
|
5
|
+
* - --output <path>: schema 输出路径,默认 'server/database/schema.ts'
|
|
6
|
+
* - --schema-filter <schemas>: schema 过滤器,逗号分隔
|
|
7
|
+
* - --tables-filter <tables>: 表过滤器,逗号分隔,默认 '*'
|
|
8
|
+
*
|
|
9
|
+
* 环境变量配置:
|
|
10
|
+
* - SUDA_DATABASE_URL: 数据库连接 URL(必需)
|
|
11
|
+
* - DB_SCHEMA_OUTPUT: schema 输出路径(命令行选项优先)
|
|
12
|
+
* - DRIZZLE_SCHEMA_FILTER: schema 过滤器(命令行选项优先)
|
|
13
|
+
* - DRIZZLE_TABLES_FILTER: 表过滤器(命令行选项优先)
|
|
14
|
+
*/
|
|
15
|
+
export declare function run(options?: {
|
|
16
|
+
output?: string;
|
|
17
|
+
schemaFilter?: string;
|
|
18
|
+
tablesFilter?: string;
|
|
19
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { spawnSync } from 'node:child_process';
|
|
5
|
+
import { createRequire } from 'node:module';
|
|
6
|
+
// 加载 .env 配置
|
|
7
|
+
import { config as loadEnv } from 'dotenv';
|
|
8
|
+
// 创建 require 函数来加载 CommonJS 模块
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
/**
|
|
11
|
+
* 生成数据库 schema
|
|
12
|
+
*
|
|
13
|
+
* 命令行选项:
|
|
14
|
+
* - --output <path>: schema 输出路径,默认 'server/database/schema.ts'
|
|
15
|
+
* - --schema-filter <schemas>: schema 过滤器,逗号分隔
|
|
16
|
+
* - --tables-filter <tables>: 表过滤器,逗号分隔,默认 '*'
|
|
17
|
+
*
|
|
18
|
+
* 环境变量配置:
|
|
19
|
+
* - SUDA_DATABASE_URL: 数据库连接 URL(必需)
|
|
20
|
+
* - DB_SCHEMA_OUTPUT: schema 输出路径(命令行选项优先)
|
|
21
|
+
* - DRIZZLE_SCHEMA_FILTER: schema 过滤器(命令行选项优先)
|
|
22
|
+
* - DRIZZLE_TABLES_FILTER: 表过滤器(命令行选项优先)
|
|
23
|
+
*/
|
|
24
|
+
export async function run(options = {}) {
|
|
25
|
+
// 加载用户项目的 .env 文件
|
|
26
|
+
const envPath = path.resolve(process.cwd(), '.env');
|
|
27
|
+
if (fs.existsSync(envPath)) {
|
|
28
|
+
loadEnv({ path: envPath });
|
|
29
|
+
console.log('[gen-db-schema] ✓ Loaded .env file');
|
|
30
|
+
}
|
|
31
|
+
const databaseUrl = process.env.SUDA_DATABASE_URL;
|
|
32
|
+
if (!databaseUrl) {
|
|
33
|
+
console.error('[gen-db-schema] Error: SUDA_DATABASE_URL environment variable is required');
|
|
34
|
+
console.error('[gen-db-schema] Please set it in .env file or pass it as environment variable');
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
// 命令行选项优先于环境变量
|
|
38
|
+
const outputPath = options.output || process.env.DB_SCHEMA_OUTPUT || 'server/database/schema.ts';
|
|
39
|
+
const OUT_DIR = path.resolve(process.cwd(), 'server/database/.introspect');
|
|
40
|
+
const SCHEMA_FILE = path.resolve(process.cwd(), outputPath);
|
|
41
|
+
console.log('[gen-db-schema] Starting...');
|
|
42
|
+
console.log(`[gen-db-schema] Output: ${outputPath}`);
|
|
43
|
+
// 获取当前文件所在目录(ESM 方式)
|
|
44
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
45
|
+
const __dirname = path.dirname(__filename);
|
|
46
|
+
// 使用 CLI 内部的 drizzle 配置
|
|
47
|
+
const cliRoot = path.resolve(__dirname, '../');
|
|
48
|
+
const configPath = path.join(cliRoot, 'config', 'drizzle.config.js');
|
|
49
|
+
if (!fs.existsSync(configPath)) {
|
|
50
|
+
console.error('[gen-db-schema] Error: drizzle config not found in CLI package');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
// 通过环境变量传递绝对路径给配置文件
|
|
54
|
+
const env = {
|
|
55
|
+
...process.env,
|
|
56
|
+
__DRIZZLE_OUT_DIR__: OUT_DIR,
|
|
57
|
+
__DRIZZLE_SCHEMA_PATH__: SCHEMA_FILE,
|
|
58
|
+
};
|
|
59
|
+
// 执行 drizzle-kit introspect
|
|
60
|
+
const args = process.argv.slice(3);
|
|
61
|
+
const spawnArgs = ['--yes', 'drizzle-kit', 'introspect', '--config', configPath, ...args];
|
|
62
|
+
const result = spawnSync('npx', spawnArgs, { stdio: 'inherit', env });
|
|
63
|
+
if (result.error) {
|
|
64
|
+
console.error('[gen-db-schema] Execution failed:', result.error);
|
|
65
|
+
process.exit(result.status ?? 1);
|
|
66
|
+
}
|
|
67
|
+
if ((result.status ?? 0) !== 0) {
|
|
68
|
+
process.exit(result.status ?? 1);
|
|
69
|
+
}
|
|
70
|
+
// 复制生成的 schema
|
|
71
|
+
const generatedSchema = path.join(OUT_DIR, 'schema.ts');
|
|
72
|
+
if (!fs.existsSync(generatedSchema)) {
|
|
73
|
+
console.error('[gen-db-schema] schema.ts not generated');
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
fs.mkdirSync(path.dirname(SCHEMA_FILE), { recursive: true });
|
|
77
|
+
fs.copyFileSync(generatedSchema, SCHEMA_FILE);
|
|
78
|
+
console.log(`[gen-db-schema] ✓ Copied to ${outputPath}`);
|
|
79
|
+
// 后处理 schema(使用 CommonJS require 方式加载)
|
|
80
|
+
try {
|
|
81
|
+
const { postprocessDrizzleSchema } = require('@lark-apaas/devtool-kits');
|
|
82
|
+
const stats = postprocessDrizzleSchema(SCHEMA_FILE);
|
|
83
|
+
if (stats?.unmatchedUnknown?.length) {
|
|
84
|
+
console.warn('[gen-db-schema] Unmatched custom types detected:', stats.unmatchedUnknown);
|
|
85
|
+
}
|
|
86
|
+
console.log('[gen-db-schema] ✓ Postprocessed schema');
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
console.warn('[gen-db-schema] Postprocess failed:', error instanceof Error ? error.message : String(error));
|
|
90
|
+
}
|
|
91
|
+
// 清理临时文件
|
|
92
|
+
if (fs.existsSync(OUT_DIR)) {
|
|
93
|
+
fs.rmSync(OUT_DIR, { recursive: true, force: true });
|
|
94
|
+
}
|
|
95
|
+
console.log('[gen-db-schema] ✓ Complete');
|
|
96
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { syncConfig } from '../config/sync.js';
|
|
5
|
+
/**
|
|
6
|
+
* 同步模板文件到用户项目
|
|
7
|
+
*/
|
|
8
|
+
export async function run() {
|
|
9
|
+
// 检测是否在用户项目中(通过 INIT_CWD 判断)
|
|
10
|
+
const userProjectRoot = process.env.INIT_CWD || process.cwd();
|
|
11
|
+
// 获取插件根目录
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = path.dirname(__filename);
|
|
14
|
+
const pluginRoot = path.resolve(__dirname, '../..');
|
|
15
|
+
// 只有在插件自己的开发目录中才跳过(避免在开发插件时触发 sync)
|
|
16
|
+
if (userProjectRoot === pluginRoot) {
|
|
17
|
+
console.log('[fullstack-cli] Skip syncing (installing plugin itself)');
|
|
18
|
+
process.exit(0);
|
|
19
|
+
}
|
|
20
|
+
// 检查是否存在 package.json,确保是有效的项目
|
|
21
|
+
const userPackageJson = path.join(userProjectRoot, 'package.json');
|
|
22
|
+
if (!fs.existsSync(userPackageJson)) {
|
|
23
|
+
console.log('[fullstack-cli] Skip syncing (not a valid npm project)');
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
console.log('[fullstack-cli] Starting sync...');
|
|
28
|
+
// 使用配置
|
|
29
|
+
const config = syncConfig;
|
|
30
|
+
if (!config || !config.sync) {
|
|
31
|
+
console.warn('[fullstack-cli] No sync configuration found');
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
// 执行同步规则
|
|
35
|
+
for (const rule of config.sync) {
|
|
36
|
+
await syncRule(rule, pluginRoot, userProjectRoot);
|
|
37
|
+
}
|
|
38
|
+
// 设置权限
|
|
39
|
+
if (config.permissions) {
|
|
40
|
+
setPermissions(config.permissions, userProjectRoot);
|
|
41
|
+
}
|
|
42
|
+
console.log('[fullstack-cli] Sync completed successfully ✅');
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
46
|
+
console.error('[fullstack-cli] Failed to sync:', message);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 执行单个同步规则
|
|
52
|
+
*/
|
|
53
|
+
async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
54
|
+
const srcPath = path.join(pluginRoot, rule.from);
|
|
55
|
+
const destPath = path.join(userProjectRoot, rule.to);
|
|
56
|
+
if (!fs.existsSync(srcPath)) {
|
|
57
|
+
console.warn(`[fullstack-cli] Source not found: ${rule.from}`);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
switch (rule.type) {
|
|
61
|
+
case 'directory':
|
|
62
|
+
syncDirectory(srcPath, destPath, rule.overwrite ?? true);
|
|
63
|
+
break;
|
|
64
|
+
case 'file':
|
|
65
|
+
syncFile(srcPath, destPath, rule.overwrite ?? true);
|
|
66
|
+
break;
|
|
67
|
+
case 'append':
|
|
68
|
+
appendToFile(srcPath, destPath);
|
|
69
|
+
break;
|
|
70
|
+
default:
|
|
71
|
+
console.warn(`[fullstack-cli] Unknown sync type: ${rule.type}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 同步单个文件
|
|
76
|
+
*/
|
|
77
|
+
function syncFile(src, dest, overwrite = true) {
|
|
78
|
+
// 确保目标目录存在
|
|
79
|
+
const destDir = path.dirname(dest);
|
|
80
|
+
if (!fs.existsSync(destDir)) {
|
|
81
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
82
|
+
}
|
|
83
|
+
// 检查目标文件是否存在
|
|
84
|
+
if (fs.existsSync(dest) && !overwrite) {
|
|
85
|
+
console.log(`[fullstack-cli] ○ ${path.basename(dest)} (skipped, already exists)`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
// 复制文件
|
|
89
|
+
fs.copyFileSync(src, dest);
|
|
90
|
+
console.log(`[fullstack-cli] ✓ ${path.basename(dest)}`);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* 同步整个目录
|
|
94
|
+
*/
|
|
95
|
+
function syncDirectory(src, dest, overwrite = true) {
|
|
96
|
+
// 确保目标目录存在
|
|
97
|
+
if (!fs.existsSync(dest)) {
|
|
98
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
99
|
+
}
|
|
100
|
+
// 读取源目录所有文件
|
|
101
|
+
const files = fs.readdirSync(src);
|
|
102
|
+
let count = 0;
|
|
103
|
+
files.forEach(file => {
|
|
104
|
+
const srcFile = path.join(src, file);
|
|
105
|
+
const destFile = path.join(dest, file);
|
|
106
|
+
const stats = fs.statSync(srcFile);
|
|
107
|
+
if (stats.isDirectory()) {
|
|
108
|
+
// 递归处理子目录
|
|
109
|
+
syncDirectory(srcFile, destFile, overwrite);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
// 复制文件
|
|
113
|
+
if (overwrite || !fs.existsSync(destFile)) {
|
|
114
|
+
fs.copyFileSync(srcFile, destFile);
|
|
115
|
+
console.log(`[fullstack-cli] ✓ ${path.relative(dest, destFile)}`);
|
|
116
|
+
count++;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
if (count > 0) {
|
|
121
|
+
console.log(`[fullstack-cli] Synced ${count} files to ${path.basename(dest)}/`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* 追加内容到文件
|
|
126
|
+
*/
|
|
127
|
+
function appendToFile(src, dest) {
|
|
128
|
+
const content = fs.readFileSync(src, 'utf-8');
|
|
129
|
+
// 读取目标文件内容(如果存在)
|
|
130
|
+
let existingContent = '';
|
|
131
|
+
if (fs.existsSync(dest)) {
|
|
132
|
+
existingContent = fs.readFileSync(dest, 'utf-8');
|
|
133
|
+
}
|
|
134
|
+
// 检查是否已包含相同内容(避免重复追加)
|
|
135
|
+
if (existingContent.includes(content.trim())) {
|
|
136
|
+
console.log(`[fullstack-cli] ○ ${path.basename(dest)} (already contains content)`);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
// 追加内容
|
|
140
|
+
fs.appendFileSync(dest, content);
|
|
141
|
+
console.log(`[fullstack-cli] ✓ ${path.basename(dest)} (appended)`);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* 设置文件权限
|
|
145
|
+
*/
|
|
146
|
+
function setPermissions(permissions, projectRoot) {
|
|
147
|
+
for (const [pattern, mode] of Object.entries(permissions)) {
|
|
148
|
+
// 简单实现:只支持 **/*.sh 这种模式
|
|
149
|
+
if (pattern === '**/*.sh') {
|
|
150
|
+
const scriptsDir = path.join(projectRoot, 'scripts');
|
|
151
|
+
if (fs.existsSync(scriptsDir)) {
|
|
152
|
+
const files = fs.readdirSync(scriptsDir);
|
|
153
|
+
files.forEach(file => {
|
|
154
|
+
if (file.endsWith('.sh')) {
|
|
155
|
+
const filePath = path.join(scriptsDir, file);
|
|
156
|
+
fs.chmodSync(filePath, mode);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { defineConfig } from 'drizzle-kit';
|
|
2
|
+
const outputDir = process.env.__DRIZZLE_OUT_DIR__ || './server/database/.introspect';
|
|
3
|
+
const schemaPath = process.env.__DRIZZLE_SCHEMA_PATH__ || './server/database/schema.ts';
|
|
4
|
+
const parsedUrl = new URL(process.env.SUDA_DATABASE_URL || '');
|
|
5
|
+
const schemaFilter = parsedUrl.searchParams.get('schema')?.split(',') ?? [];
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
schema: schemaPath,
|
|
8
|
+
out: outputDir,
|
|
9
|
+
tablesFilter: ['*'],
|
|
10
|
+
schemaFilter,
|
|
11
|
+
dialect: 'postgresql',
|
|
12
|
+
dbCredentials: {
|
|
13
|
+
host: parsedUrl.hostname,
|
|
14
|
+
port: Number(parsedUrl.port) || 5432,
|
|
15
|
+
user: decodeURIComponent(parsedUrl.username),
|
|
16
|
+
password: decodeURIComponent(parsedUrl.password),
|
|
17
|
+
database: parsedUrl.pathname.split('/')[1],
|
|
18
|
+
},
|
|
19
|
+
});
|
|
@@ -12,11 +12,10 @@ export interface SyncRule {
|
|
|
12
12
|
/** 是否覆盖已存在的文件(仅 directory 和 file 类型有效) */
|
|
13
13
|
overwrite?: boolean;
|
|
14
14
|
}
|
|
15
|
-
export interface
|
|
15
|
+
export interface SyncConfig {
|
|
16
16
|
/** 派生规则 */
|
|
17
17
|
sync: SyncRule[];
|
|
18
18
|
/** 文件权限设置 */
|
|
19
19
|
permissions?: Record<string, number>;
|
|
20
20
|
}
|
|
21
|
-
declare const
|
|
22
|
-
export default config;
|
|
21
|
+
export declare const syncConfig: SyncConfig;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fullstack-cli 派生配置
|
|
3
|
+
* 定义哪些文件/目录需要同步到用户项目
|
|
4
|
+
*/
|
|
5
|
+
export const syncConfig = {
|
|
6
|
+
// 派生规则
|
|
7
|
+
sync: [
|
|
8
|
+
// 1. 派生 scripts 目录(总是覆盖)
|
|
9
|
+
{
|
|
10
|
+
from: 'templates/scripts',
|
|
11
|
+
to: 'scripts',
|
|
12
|
+
type: 'directory',
|
|
13
|
+
overwrite: true,
|
|
14
|
+
},
|
|
15
|
+
// // 2. 追加内容到 .gitignore
|
|
16
|
+
// {
|
|
17
|
+
// from: 'templates/.gitignore.append',
|
|
18
|
+
// to: '.gitignore',
|
|
19
|
+
// type: 'append',
|
|
20
|
+
// },
|
|
21
|
+
// // 3. 派生 server/type.ts 文件(总是覆盖)
|
|
22
|
+
// {
|
|
23
|
+
// from: 'templates/server/type.ts',
|
|
24
|
+
// to: 'server/type.ts',
|
|
25
|
+
// type: 'file',
|
|
26
|
+
// },
|
|
27
|
+
],
|
|
28
|
+
// 文件权限设置
|
|
29
|
+
permissions: {
|
|
30
|
+
// 所有 .sh 文件设置为可执行
|
|
31
|
+
// '**/*.sh': 0o755,
|
|
32
|
+
},
|
|
33
|
+
};
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,9 +1,52 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import cac from 'cac';
|
|
5
|
+
import { run as runGenDbSchema } from './commands/gen-db-schema.js';
|
|
6
|
+
import { run as runSync } from './commands/sync.js';
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
// 读取 package.json
|
|
10
|
+
const pkgPath = path.join(__dirname, '../package.json');
|
|
11
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
12
|
+
const cli = cac('fullstack-cli');
|
|
13
|
+
// 设置全局描述和版本
|
|
14
|
+
cli
|
|
15
|
+
.version(pkg.version)
|
|
16
|
+
.help();
|
|
17
|
+
// gen-db-schema 命令
|
|
18
|
+
cli
|
|
19
|
+
.command('gen-db-schema', 'Generate database schema from existing database')
|
|
20
|
+
.option('--output <path>', 'Output path for schema', {
|
|
21
|
+
default: 'server/database/schema.ts',
|
|
22
|
+
})
|
|
23
|
+
.option('--schema-filter <schemas>', 'Schema filter, comma-separated')
|
|
24
|
+
.option('--tables-filter <tables>', 'Tables filter, comma-separated')
|
|
25
|
+
.example('fullstack-cli gen-db-schema')
|
|
26
|
+
.example('fullstack-cli gen-db-schema --output custom/schema.ts')
|
|
27
|
+
.action(async (options) => {
|
|
28
|
+
try {
|
|
29
|
+
await runGenDbSchema(options);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
console.error(`Failed to execute command:`, error.message);
|
|
33
|
+
console.error(error.stack);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
// sync 命令
|
|
38
|
+
cli
|
|
39
|
+
.command('sync', 'Sync template files (scripts, configs) to user project')
|
|
40
|
+
.example('fullstack-cli sync')
|
|
41
|
+
.action(async () => {
|
|
42
|
+
try {
|
|
43
|
+
await runSync();
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error(`Failed to execute command:`, error.message);
|
|
47
|
+
console.error(error.stack);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
// 解析命令行参数
|
|
52
|
+
cli.parse();
|
package/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/fullstack-cli",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.11",
|
|
4
4
|
"description": "CLI tool for fullstack template management",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"main": "dist/index.js",
|
|
6
7
|
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": "./bin/cli.js",
|
|
7
9
|
"files": [
|
|
8
10
|
"dist",
|
|
9
11
|
"templates",
|
|
@@ -11,8 +13,7 @@
|
|
|
11
13
|
],
|
|
12
14
|
"scripts": {
|
|
13
15
|
"build": "tsc",
|
|
14
|
-
"prepublishOnly": "npm run build"
|
|
15
|
-
"postinstall": "node bin/sync-scripts.js"
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
16
17
|
},
|
|
17
18
|
"keywords": [
|
|
18
19
|
"fullstack",
|
|
@@ -27,6 +28,12 @@
|
|
|
27
28
|
"publishConfig": {
|
|
28
29
|
"access": "public"
|
|
29
30
|
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@lark-apaas/devtool-kits": "^1.0.0",
|
|
33
|
+
"cac": "^6.7.14",
|
|
34
|
+
"dotenv": "^16.0.0",
|
|
35
|
+
"drizzle-kit": "0.31.5"
|
|
36
|
+
},
|
|
30
37
|
"devDependencies": {
|
|
31
38
|
"@types/node": "^22.0.0",
|
|
32
39
|
"typescript": "^5.9.2"
|
|
@@ -3,11 +3,20 @@ import { DevToolsModule } from '@lark-apaas/fullstack-nestjs-core';
|
|
|
3
3
|
|
|
4
4
|
import { AppModule } from '../server/app.module';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* 生成 OpenAPI 文档和客户端 SDK
|
|
8
|
+
*
|
|
9
|
+
* 此文件由 @lark-apaas/fullstack-cli 自动派生,请勿手动修改
|
|
10
|
+
* 每次运行 npm install 时会自动更新
|
|
11
|
+
*/
|
|
6
12
|
async function generateOpenApi() {
|
|
7
|
-
const app = await NestFactory.create(AppModule, {logger: false});
|
|
13
|
+
const app = await NestFactory.create(AppModule, { logger: false });
|
|
8
14
|
const basePath = process.env.CLIENT_BASE_PATH;
|
|
9
|
-
|
|
10
|
-
|
|
15
|
+
|
|
16
|
+
if (basePath) {
|
|
17
|
+
app.setGlobalPrefix(basePath);
|
|
18
|
+
}
|
|
19
|
+
|
|
11
20
|
await DevToolsModule.mount(app, {
|
|
12
21
|
basePath,
|
|
13
22
|
docsPath: '/api_docs',
|
package/bin/sync-scripts.js
DELETED
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const path = require('path');
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
|
|
6
|
-
// 主函数
|
|
7
|
-
async function main() {
|
|
8
|
-
// 检测是否在用户项目中(通过 INIT_CWD 判断)
|
|
9
|
-
const userProjectRoot = process.env.INIT_CWD || process.cwd();
|
|
10
|
-
const pluginRoot = path.resolve(__dirname, '..');
|
|
11
|
-
|
|
12
|
-
// 如果在插件自己的目录,跳过
|
|
13
|
-
const isInstallingPlugin = __dirname.includes('/node_modules/@lark-apaas/fullstack-cli/');
|
|
14
|
-
if (isInstallingPlugin || userProjectRoot === pluginRoot) {
|
|
15
|
-
console.log('[fullstack-cli] Skip syncing (installing plugin itself)');
|
|
16
|
-
process.exit(0);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// 检查是否存在 package.json,确保是有效的项目
|
|
20
|
-
const userPackageJson = path.join(userProjectRoot, 'package.json');
|
|
21
|
-
if (!fs.existsSync(userPackageJson)) {
|
|
22
|
-
console.log('[fullstack-cli] Skip syncing (not a valid npm project)');
|
|
23
|
-
process.exit(0);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
try {
|
|
27
|
-
console.log('[fullstack-cli] Starting sync...');
|
|
28
|
-
|
|
29
|
-
// 加载配置文件(从 dist 目录加载编译后的配置)
|
|
30
|
-
const configPath = path.join(pluginRoot, 'dist', 'postinstall.config.js');
|
|
31
|
-
const { default: config } = await import(`file://${configPath}`);
|
|
32
|
-
|
|
33
|
-
if (!config || !config.sync) {
|
|
34
|
-
console.warn('[fullstack-cli] No sync configuration found');
|
|
35
|
-
process.exit(0);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// 执行同步规则
|
|
39
|
-
for (const rule of config.sync) {
|
|
40
|
-
await syncRule(rule, pluginRoot, userProjectRoot);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// 设置权限
|
|
44
|
-
if (config.permissions) {
|
|
45
|
-
setPermissions(config.permissions, userProjectRoot);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
console.log('[fullstack-cli] Sync completed successfully ✅');
|
|
49
|
-
} catch (error) {
|
|
50
|
-
console.error('[fullstack-cli] Failed to sync:', error.message);
|
|
51
|
-
// 不阻塞安装过程
|
|
52
|
-
process.exit(0);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* 执行单个同步规则
|
|
58
|
-
*/
|
|
59
|
-
async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
60
|
-
const srcPath = path.join(pluginRoot, rule.from);
|
|
61
|
-
const destPath = path.join(userProjectRoot, rule.to);
|
|
62
|
-
|
|
63
|
-
if (!fs.existsSync(srcPath)) {
|
|
64
|
-
console.warn(`[fullstack-cli] Source not found: ${rule.from}`);
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
switch (rule.type) {
|
|
69
|
-
case 'directory':
|
|
70
|
-
syncDirectory(srcPath, destPath, rule.overwrite);
|
|
71
|
-
break;
|
|
72
|
-
case 'file':
|
|
73
|
-
syncFile(srcPath, destPath, rule.overwrite);
|
|
74
|
-
break;
|
|
75
|
-
case 'append':
|
|
76
|
-
appendToFile(srcPath, destPath);
|
|
77
|
-
break;
|
|
78
|
-
default:
|
|
79
|
-
console.warn(`[fullstack-cli] Unknown sync type: ${rule.type}`);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* 同步单个文件
|
|
85
|
-
*/
|
|
86
|
-
function syncFile(src, dest, overwrite = true) {
|
|
87
|
-
// 确保目标目录存在
|
|
88
|
-
const destDir = path.dirname(dest);
|
|
89
|
-
if (!fs.existsSync(destDir)) {
|
|
90
|
-
fs.mkdirSync(destDir, { recursive: true });
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// 检查目标文件是否存在
|
|
94
|
-
if (fs.existsSync(dest) && !overwrite) {
|
|
95
|
-
console.log(`[fullstack-cli] ○ ${path.basename(dest)} (skipped, already exists)`);
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// 复制文件
|
|
100
|
-
fs.copyFileSync(src, dest);
|
|
101
|
-
console.log(`[fullstack-cli] ✓ ${path.basename(dest)}`);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* 同步整个目录
|
|
106
|
-
*/
|
|
107
|
-
function syncDirectory(src, dest, overwrite = true) {
|
|
108
|
-
// 确保目标目录存在
|
|
109
|
-
if (!fs.existsSync(dest)) {
|
|
110
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// 读取源目录所有文件
|
|
114
|
-
const files = fs.readdirSync(src);
|
|
115
|
-
let count = 0;
|
|
116
|
-
|
|
117
|
-
files.forEach(file => {
|
|
118
|
-
const srcFile = path.join(src, file);
|
|
119
|
-
const destFile = path.join(dest, file);
|
|
120
|
-
const stats = fs.statSync(srcFile);
|
|
121
|
-
|
|
122
|
-
if (stats.isDirectory()) {
|
|
123
|
-
// 递归处理子目录
|
|
124
|
-
syncDirectory(srcFile, destFile, overwrite);
|
|
125
|
-
} else {
|
|
126
|
-
// 复制文件
|
|
127
|
-
if (overwrite || !fs.existsSync(destFile)) {
|
|
128
|
-
fs.copyFileSync(srcFile, destFile);
|
|
129
|
-
console.log(`[fullstack-cli] ✓ ${path.relative(dest, destFile)}`);
|
|
130
|
-
count++;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
if (count > 0) {
|
|
136
|
-
console.log(`[fullstack-cli] Synced ${count} files to ${path.basename(dest)}/`);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* 追加内容到文件
|
|
142
|
-
*/
|
|
143
|
-
function appendToFile(src, dest) {
|
|
144
|
-
const content = fs.readFileSync(src, 'utf-8');
|
|
145
|
-
|
|
146
|
-
// 读取目标文件内容(如果存在)
|
|
147
|
-
let existingContent = '';
|
|
148
|
-
if (fs.existsSync(dest)) {
|
|
149
|
-
existingContent = fs.readFileSync(dest, 'utf-8');
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// 检查是否已包含相同内容(避免重复追加)
|
|
153
|
-
if (existingContent.includes(content.trim())) {
|
|
154
|
-
console.log(`[fullstack-cli] ○ ${path.basename(dest)} (already contains content)`);
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// 追加内容
|
|
159
|
-
fs.appendFileSync(dest, content);
|
|
160
|
-
console.log(`[fullstack-cli] ✓ ${path.basename(dest)} (appended)`);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* 设置文件权限
|
|
165
|
-
*/
|
|
166
|
-
function setPermissions(permissions, projectRoot) {
|
|
167
|
-
for (const [pattern, mode] of Object.entries(permissions)) {
|
|
168
|
-
// 简单实现:只支持 **/*.sh 这种模式
|
|
169
|
-
if (pattern === '**/*.sh') {
|
|
170
|
-
const scriptsDir = path.join(projectRoot, 'scripts');
|
|
171
|
-
if (fs.existsSync(scriptsDir)) {
|
|
172
|
-
const files = fs.readdirSync(scriptsDir);
|
|
173
|
-
files.forEach(file => {
|
|
174
|
-
if (file.endsWith('.sh')) {
|
|
175
|
-
const filePath = path.join(scriptsDir, file);
|
|
176
|
-
fs.chmodSync(filePath, mode);
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// 运行主函数
|
|
185
|
-
main();
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* fullstack-cli 派生配置
|
|
3
|
-
* 定义哪些文件/目录需要同步到用户项目
|
|
4
|
-
*/
|
|
5
|
-
const config = {
|
|
6
|
-
// 派生规则
|
|
7
|
-
sync: [
|
|
8
|
-
// 1. 派生 scripts 目录(总是覆盖)
|
|
9
|
-
{
|
|
10
|
-
from: 'templates/scripts',
|
|
11
|
-
to: 'scripts',
|
|
12
|
-
type: 'directory',
|
|
13
|
-
overwrite: true,
|
|
14
|
-
},
|
|
15
|
-
// 2. 追加内容到 .gitignore
|
|
16
|
-
{
|
|
17
|
-
from: 'templates/.gitignore.append',
|
|
18
|
-
to: '.gitignore',
|
|
19
|
-
type: 'append',
|
|
20
|
-
},
|
|
21
|
-
],
|
|
22
|
-
// 文件权限设置
|
|
23
|
-
permissions: {
|
|
24
|
-
// 所有 .sh 文件设置为可执行
|
|
25
|
-
'**/*.sh': 0o755,
|
|
26
|
-
},
|
|
27
|
-
};
|
|
28
|
-
export default config;
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* fullstack-cli 派生配置
|
|
3
|
-
* 定义哪些文件/目录需要同步到用户项目
|
|
4
|
-
*/
|
|
5
|
-
export interface SyncRule {
|
|
6
|
-
/** 源文件/目录路径(相对于插件根目录) */
|
|
7
|
-
from: string;
|
|
8
|
-
/** 目标文件/目录路径(相对于用户项目根目录) */
|
|
9
|
-
to: string;
|
|
10
|
-
/** 同步类型 */
|
|
11
|
-
type: 'directory' | 'file' | 'append';
|
|
12
|
-
/** 是否覆盖已存在的文件(仅 directory 和 file 类型有效) */
|
|
13
|
-
overwrite?: boolean;
|
|
14
|
-
}
|
|
15
|
-
export interface PostinstallConfig {
|
|
16
|
-
/** 派生规则 */
|
|
17
|
-
sync: SyncRule[];
|
|
18
|
-
/** 文件权限设置 */
|
|
19
|
-
permissions?: Record<string, number>;
|
|
20
|
-
}
|
|
21
|
-
declare const config: PostinstallConfig;
|
|
22
|
-
export default config;
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* fullstack-cli 派生配置
|
|
3
|
-
* 定义哪些文件/目录需要同步到用户项目
|
|
4
|
-
*/
|
|
5
|
-
const config = {
|
|
6
|
-
// 派生规则
|
|
7
|
-
sync: [
|
|
8
|
-
// 1. 派生 scripts 目录(总是覆盖)
|
|
9
|
-
{
|
|
10
|
-
from: 'templates/scripts',
|
|
11
|
-
to: 'scripts',
|
|
12
|
-
type: 'directory',
|
|
13
|
-
overwrite: true,
|
|
14
|
-
},
|
|
15
|
-
// 2. 追加内容到 .gitignore
|
|
16
|
-
{
|
|
17
|
-
from: 'templates/.gitignore.append',
|
|
18
|
-
to: '.gitignore',
|
|
19
|
-
type: 'append',
|
|
20
|
-
},
|
|
21
|
-
// 3. 派生 server/type.ts 文件(总是覆盖)
|
|
22
|
-
{
|
|
23
|
-
from: 'templates/server/type.ts',
|
|
24
|
-
to: 'server/type.ts',
|
|
25
|
-
type: 'file',
|
|
26
|
-
},
|
|
27
|
-
],
|
|
28
|
-
// 文件权限设置
|
|
29
|
-
permissions: {
|
|
30
|
-
// 所有 .sh 文件设置为可执行
|
|
31
|
-
'**/*.sh': 0o755,
|
|
32
|
-
},
|
|
33
|
-
};
|
|
34
|
-
export default config;
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { spawnSync } from 'node:child_process';
|
|
4
|
-
import { postprocessDrizzleSchema } from '@lark-apaas/devtool-kits';
|
|
5
|
-
|
|
6
|
-
const OUT_DIR = path.resolve(process.cwd(), 'server/database/.introspect');
|
|
7
|
-
const SCHEMA_FILE = path.resolve(process.cwd(), 'server/database/schema.ts');
|
|
8
|
-
|
|
9
|
-
function run(): void {
|
|
10
|
-
const args = process.argv.slice(2);
|
|
11
|
-
const spawnArgs = ['--yes', 'drizzle-kit', 'introspect', '--config', 'drizzle.config.ts', ...args];
|
|
12
|
-
|
|
13
|
-
const result = spawnSync('npx', spawnArgs, { stdio: 'inherit' });
|
|
14
|
-
if (result.error) {
|
|
15
|
-
console.error('[gen-db-schema] Execution failed:', result.error);
|
|
16
|
-
process.exit(result.status ?? 1);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if ((result.status ?? 0) !== 0) {
|
|
20
|
-
process.exit(result.status ?? 1);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const generatedSchema = path.join(OUT_DIR, 'schema.ts');
|
|
24
|
-
if (!fs.existsSync(generatedSchema)) {
|
|
25
|
-
console.warn('[gen-db-schema] schema.ts not generated');
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
fs.mkdirSync(path.dirname(SCHEMA_FILE), { recursive: true });
|
|
30
|
-
fs.copyFileSync(generatedSchema, SCHEMA_FILE);
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
const stats = postprocessDrizzleSchema(SCHEMA_FILE);
|
|
34
|
-
if (stats?.unmatchedUnknown?.length) {
|
|
35
|
-
console.warn('[gen-db-schema] Unmatched custom types detected');
|
|
36
|
-
}
|
|
37
|
-
} catch (error) {
|
|
38
|
-
console.warn('[gen-db-schema] postprocess failed:', error);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (fs.existsSync(OUT_DIR)) {
|
|
42
|
-
fs.rmSync(OUT_DIR, { recursive: true, force: true });
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (require.main === module) {
|
|
47
|
-
run();
|
|
48
|
-
}
|
|
File without changes
|