@gadmin2n/cli 0.0.158 → 0.0.160
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/actions/prisma.action.js
CHANGED
|
@@ -51,39 +51,40 @@ function replaceFileContent(filePath, match, src, dest) {
|
|
|
51
51
|
}
|
|
52
52
|
class PrismaAction extends abstract_action_1.AbstractAction {
|
|
53
53
|
buildMergedSchema() {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
// 读 generator 头
|
|
55
|
+
const generatorHead = fs.readFileSync('server/prisma/.generator.prisma', 'utf8');
|
|
56
|
+
// 读用户 schema 源
|
|
57
57
|
let schemaStr = '';
|
|
58
58
|
if (shell.test('-d', 'config/prisma')) {
|
|
59
|
-
schemaStr = shell.cat('config/prisma/*.prisma');
|
|
59
|
+
schemaStr = shell.cat('config/prisma/*.prisma').toString();
|
|
60
60
|
}
|
|
61
61
|
else {
|
|
62
|
-
schemaStr = shell.cat('config/schema.prisma');
|
|
62
|
+
schemaStr = shell.cat('config/schema.prisma').toString();
|
|
63
63
|
}
|
|
64
64
|
const schemaRegex = /([\s\S]*?model\s\w*\s*\{)([^\}]*)(\})/gim; // https://regex101.com/
|
|
65
|
-
let m,
|
|
65
|
+
let m, merged = '';
|
|
66
66
|
while ((m = schemaRegex.exec(schemaStr)) !== null) {
|
|
67
67
|
// This is necessary to avoid infinite loops with zero-width matches
|
|
68
68
|
if (m.index === schemaRegex.lastIndex) {
|
|
69
69
|
schemaRegex.lastIndex++;
|
|
70
70
|
}
|
|
71
71
|
let ignore = !!m[1].match(/\/\/\/\s*@IgnoreAutoField/);
|
|
72
|
-
|
|
72
|
+
merged += m[1];
|
|
73
73
|
if (!ignore) {
|
|
74
|
-
|
|
74
|
+
merged += '\n id BigInt @id @default(autoincrement())';
|
|
75
75
|
}
|
|
76
|
-
|
|
76
|
+
merged += m[2];
|
|
77
77
|
if (!ignore) {
|
|
78
|
-
|
|
78
|
+
merged +=
|
|
79
79
|
' creator String @db.VarChar(128) @map("creator")\n createdAt DateTime @default(now()) @map("created_at")\n updatedAt DateTime @updatedAt @default(now()) @map("updated_at")\n';
|
|
80
80
|
}
|
|
81
|
-
|
|
81
|
+
merged += m[3];
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
// 一次原子写 + diff:内容未变则完全不 touch 文件,避免任何 watcher 感知抖动。
|
|
84
|
+
return (0, sync_fs_1.writeFileIfChanged)('server/prisma/schema.prisma', generatorHead + merged);
|
|
84
85
|
}
|
|
85
86
|
handle(inputs, options) {
|
|
86
|
-
var _a;
|
|
87
|
+
var _a, _b;
|
|
87
88
|
return __awaiter(this, void 0, void 0, function* () {
|
|
88
89
|
// 检查 prisma schema 配置是否存在
|
|
89
90
|
const hasPrismaConfig = shell.test('-e', 'config/schema.prisma') ||
|
|
@@ -94,6 +95,18 @@ class PrismaAction extends abstract_action_1.AbstractAction {
|
|
|
94
95
|
process.exit(-1);
|
|
95
96
|
}
|
|
96
97
|
const devMode = !!((_a = options.find((option) => option.name === 'dev')) === null || _a === void 0 ? void 0 : _a.value);
|
|
98
|
+
const verbose = !!((_b = options.find((option) => option.name === 'verbose')) === null || _b === void 0 ? void 0 : _b.value);
|
|
99
|
+
// 分发过程按类别记账,收尾统一打摘要 / --verbose 打完整清单
|
|
100
|
+
const stats = {
|
|
101
|
+
serverModulesIndex: [],
|
|
102
|
+
serverModulesNew: [],
|
|
103
|
+
serverModulesSkipped: [],
|
|
104
|
+
routesNew: [],
|
|
105
|
+
routesSkipped: [],
|
|
106
|
+
generated: [],
|
|
107
|
+
generatedUnchanged: [],
|
|
108
|
+
orphansRemoved: [], // web/src/generated/** 孤儿清理
|
|
109
|
+
};
|
|
97
110
|
// 构建合并的 schema(两种模式都需要重建 schema.prisma)
|
|
98
111
|
this.buildMergedSchema();
|
|
99
112
|
// Staging 目录:generator 全量写入到 node_modules/.cache/*,CLI 再增量分发。
|
|
@@ -145,7 +158,10 @@ class PrismaAction extends abstract_action_1.AbstractAction {
|
|
|
145
158
|
// 1.1 modules.index.ts —— 顶层入口
|
|
146
159
|
const modulesIndexSrc = (0, path_1.join)(SERVER_STAGING, 'modules.index.ts');
|
|
147
160
|
if (fs.existsSync(modulesIndexSrc)) {
|
|
148
|
-
|
|
161
|
+
const rel = 'server/src/generated/modules.index.ts';
|
|
162
|
+
if ((0, sync_fs_1.copyFileIfChanged)(modulesIndexSrc, rel)) {
|
|
163
|
+
stats.serverModulesIndex.push(rel);
|
|
164
|
+
}
|
|
149
165
|
}
|
|
150
166
|
// 1.2 逐 model 首次落地到 server/src/modules/{model}(跳过 form.validator,它归 web 阶段)
|
|
151
167
|
for (const modelName of stagingModels) {
|
|
@@ -155,10 +171,13 @@ class PrismaAction extends abstract_action_1.AbstractAction {
|
|
|
155
171
|
if (rel === validatorInStaging)
|
|
156
172
|
continue;
|
|
157
173
|
const dest = (0, path_1.join)('server/src/modules', modelName, rel);
|
|
158
|
-
if (fs.existsSync(dest))
|
|
174
|
+
if (fs.existsSync(dest)) {
|
|
175
|
+
stats.serverModulesSkipped.push(dest);
|
|
159
176
|
continue;
|
|
177
|
+
}
|
|
160
178
|
fs.mkdirSync((0, path_1.dirname)(dest), { recursive: true });
|
|
161
179
|
fs.copyFileSync((0, path_1.join)(modelStaging, rel), dest);
|
|
180
|
+
stats.serverModulesNew.push(dest);
|
|
162
181
|
}
|
|
163
182
|
}
|
|
164
183
|
}
|
|
@@ -174,10 +193,20 @@ class PrismaAction extends abstract_action_1.AbstractAction {
|
|
|
174
193
|
if (!fs.existsSync(destRoutes)) {
|
|
175
194
|
fs.mkdirSync((0, path_1.dirname)(destRoutes), { recursive: true });
|
|
176
195
|
fs.copyFileSync(src, destRoutes);
|
|
196
|
+
stats.routesNew.push(destRoutes);
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
stats.routesSkipped.push(destRoutes);
|
|
177
200
|
}
|
|
178
201
|
}
|
|
179
202
|
else {
|
|
180
|
-
|
|
203
|
+
const dest = (0, path_1.join)('web/src/generated', rel);
|
|
204
|
+
if ((0, sync_fs_1.copyFileIfChanged)(src, dest)) {
|
|
205
|
+
stats.generated.push(dest);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
stats.generatedUnchanged.push(dest);
|
|
209
|
+
}
|
|
181
210
|
webKeep.add(rel);
|
|
182
211
|
}
|
|
183
212
|
}
|
|
@@ -185,7 +214,13 @@ class PrismaAction extends abstract_action_1.AbstractAction {
|
|
|
185
214
|
// 2.2 form.validator.ts(server generator 产出,需搬到 web/src/generated/props/{model}/)
|
|
186
215
|
for (const modelName of stagingModels) {
|
|
187
216
|
const validatorRel = `props/${modelName}/form.validator.ts`;
|
|
188
|
-
|
|
217
|
+
const dest = (0, path_1.join)('web/src/generated', validatorRel);
|
|
218
|
+
if ((0, sync_fs_1.copyFileIfChanged)((0, path_1.join)(SERVER_STAGING, modelName, 'dto', `${modelName}.form.validator.ts`), dest)) {
|
|
219
|
+
stats.generated.push(dest);
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
stats.generatedUnchanged.push(dest);
|
|
223
|
+
}
|
|
189
224
|
webKeep.add(validatorRel);
|
|
190
225
|
}
|
|
191
226
|
// 2.3 Prisma / config 类型
|
|
@@ -195,19 +230,67 @@ class PrismaAction extends abstract_action_1.AbstractAction {
|
|
|
195
230
|
let prismaContent = fs.readFileSync(prismaSrc, 'utf8');
|
|
196
231
|
prismaContent = prismaContent.replace("import * as runtime from '@prisma/client/runtime/index';", 'declare const runtime : any');
|
|
197
232
|
prismaContent = prismaContent.replace(/bigint/g, 'number');
|
|
198
|
-
|
|
233
|
+
const prismaDest = (0, path_1.join)('web/src/generated', prismaRel);
|
|
234
|
+
if ((0, sync_fs_1.writeFileIfChanged)(prismaDest, prismaContent)) {
|
|
235
|
+
stats.generated.push(prismaDest);
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
stats.generatedUnchanged.push(prismaDest);
|
|
239
|
+
}
|
|
199
240
|
webKeep.add(prismaRel);
|
|
200
241
|
const configRel = 'types/config.types.d.ts';
|
|
201
|
-
|
|
242
|
+
const configDest = (0, path_1.join)('web/src/generated', configRel);
|
|
243
|
+
if ((0, sync_fs_1.copyFileIfChanged)('config/.types.d.ts', configDest)) {
|
|
244
|
+
stats.generated.push(configDest);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
stats.generatedUnchanged.push(configDest);
|
|
248
|
+
}
|
|
202
249
|
webKeep.add(configRel);
|
|
203
250
|
// 2.4 孤儿清理:删除 web/src/generated 下所有本轮未产出的文件(例如被删除的 model)
|
|
204
|
-
(0, sync_fs_1.pruneOrphans)('web/src/generated', webKeep);
|
|
251
|
+
stats.orphansRemoved = (0, sync_fs_1.pruneOrphans)('web/src/generated', webKeep).map((r) => (0, path_1.join)('web/src/generated', r));
|
|
252
|
+
// 打印摘要 / 完整清单
|
|
253
|
+
this.printReport(stats, verbose);
|
|
205
254
|
// 分发全部完成后,若 server/package.json 定义了 postgadminGenerate,
|
|
206
255
|
// 触发它。项目侧可用来做 dev server 通知、缓存清理等自定义收尾。
|
|
207
256
|
this.runProjectPostHook();
|
|
208
257
|
process.exit(0);
|
|
209
258
|
});
|
|
210
259
|
}
|
|
260
|
+
printReport(stats, verbose) {
|
|
261
|
+
const serverWritten = stats.serverModulesIndex.length + stats.serverModulesNew.length;
|
|
262
|
+
const webWritten = stats.generated.length + stats.routesNew.length;
|
|
263
|
+
console.log('');
|
|
264
|
+
console.log(chalk.bold('gadmin2 g prisma:'));
|
|
265
|
+
console.log(` ${chalk.cyan('server')}: ${serverWritten} written` +
|
|
266
|
+
` (index=${stats.serverModulesIndex.length},` +
|
|
267
|
+
` modules-new=${stats.serverModulesNew.length}),` +
|
|
268
|
+
` ${stats.serverModulesSkipped.length} skipped`);
|
|
269
|
+
console.log(` ${chalk.cyan('web')}: ${webWritten} written` +
|
|
270
|
+
` (routes-new=${stats.routesNew.length},` +
|
|
271
|
+
` generated=${stats.generated.length}),` +
|
|
272
|
+
` ${stats.routesSkipped.length + stats.generatedUnchanged.length} unchanged,` +
|
|
273
|
+
` ${stats.orphansRemoved.length} orphan removed`);
|
|
274
|
+
if (!verbose) {
|
|
275
|
+
console.log(chalk.gray(' (use -v / --verbose to list files)'));
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const printGroup = (title, list, color) => {
|
|
279
|
+
if (list.length === 0)
|
|
280
|
+
return;
|
|
281
|
+
console.log(chalk.bold(`\n ${title} (${list.length}):`));
|
|
282
|
+
for (const p of list)
|
|
283
|
+
console.log(' ' + color(p));
|
|
284
|
+
};
|
|
285
|
+
printGroup('server modules.index (diff-write)', stats.serverModulesIndex, chalk.green);
|
|
286
|
+
printGroup('server modules (new)', stats.serverModulesNew, chalk.green);
|
|
287
|
+
printGroup('server modules (skipped, exists)', stats.serverModulesSkipped, chalk.gray);
|
|
288
|
+
printGroup('web routes (new)', stats.routesNew, chalk.green);
|
|
289
|
+
printGroup('web routes (skipped, exists)', stats.routesSkipped, chalk.gray);
|
|
290
|
+
printGroup('web generated (written)', stats.generated, chalk.green);
|
|
291
|
+
printGroup('web generated (unchanged)', stats.generatedUnchanged, chalk.gray);
|
|
292
|
+
printGroup('web orphans removed', stats.orphansRemoved, chalk.red);
|
|
293
|
+
}
|
|
211
294
|
runProjectPostHook() {
|
|
212
295
|
var _a;
|
|
213
296
|
const pkgPath = 'server/package.json';
|
|
@@ -32,6 +32,7 @@ class GenerateCommand extends abstract_command_1.AbstractCommand {
|
|
|
32
32
|
return { value: false, passedAsInput: true };
|
|
33
33
|
})
|
|
34
34
|
.option('-c, --collection [collectionName]', 'Schematics collection to use.')
|
|
35
|
+
.option('-v, --verbose', 'Print each affected src/ file (prisma).')
|
|
35
36
|
.action((schematic, name, path, command) => __awaiter(this, void 0, void 0, function* () {
|
|
36
37
|
const options = [];
|
|
37
38
|
options.push({ name: 'dry-run', value: !!command.dryRun });
|
|
@@ -64,6 +65,7 @@ class GenerateCommand extends abstract_command_1.AbstractCommand {
|
|
|
64
65
|
keepInputNameFormat: true,
|
|
65
66
|
},
|
|
66
67
|
});
|
|
68
|
+
options.push({ name: 'verbose', value: !!command.verbose });
|
|
67
69
|
const inputs = [];
|
|
68
70
|
inputs.push({ name: 'schematic', value: schematic });
|
|
69
71
|
inputs.push({ name: 'name', value: name });
|
|
@@ -34,6 +34,10 @@ class GadminCollection extends abstract_collection_1.AbstractCollection {
|
|
|
34
34
|
const opts = [];
|
|
35
35
|
if (name.includes(':dev'))
|
|
36
36
|
opts.push({ name: 'dev', value: true });
|
|
37
|
+
const verbose = options.find((o) => o.name === 'verbose');
|
|
38
|
+
if (verbose && verbose.value) {
|
|
39
|
+
opts.push({ name: 'verbose', value: true });
|
|
40
|
+
}
|
|
37
41
|
return yield prisma.handle([], opts);
|
|
38
42
|
}
|
|
39
43
|
const schematic = this.validate(name);
|
package/lib/utils/sync-fs.d.ts
CHANGED
|
@@ -37,5 +37,6 @@ export declare function syncDir(srcDir: string, destDir: string): {
|
|
|
37
37
|
/**
|
|
38
38
|
* 删除 destDir 下所有不在 keepFiles 里的文件(以及随之空掉的父目录)。
|
|
39
39
|
* 用于「本轮 model 目录被删」的孤儿清理场景。
|
|
40
|
+
* @returns 被删除的文件相对路径列表(相对 destDir)
|
|
40
41
|
*/
|
|
41
|
-
export declare function pruneOrphans(destDir: string, keepFiles: Set<string>):
|
|
42
|
+
export declare function pruneOrphans(destDir: string, keepFiles: Set<string>): string[];
|
package/lib/utils/sync-fs.js
CHANGED
|
@@ -115,15 +115,16 @@ exports.syncDir = syncDir;
|
|
|
115
115
|
/**
|
|
116
116
|
* 删除 destDir 下所有不在 keepFiles 里的文件(以及随之空掉的父目录)。
|
|
117
117
|
* 用于「本轮 model 目录被删」的孤儿清理场景。
|
|
118
|
+
* @returns 被删除的文件相对路径列表(相对 destDir)
|
|
118
119
|
*/
|
|
119
120
|
function pruneOrphans(destDir, keepFiles) {
|
|
120
|
-
|
|
121
|
+
const removed = [];
|
|
121
122
|
for (const rel of listFilesRecursive(destDir)) {
|
|
122
123
|
if (!keepFiles.has(rel)) {
|
|
123
124
|
removeFileAndPruneEmptyDirs(path.join(destDir, rel), destDir);
|
|
124
|
-
|
|
125
|
+
removed.push(rel);
|
|
125
126
|
}
|
|
126
127
|
}
|
|
127
|
-
return
|
|
128
|
+
return removed;
|
|
128
129
|
}
|
|
129
130
|
exports.pruneOrphans = pruneOrphans;
|