@lark-apaas/fullstack-cli 1.1.12-alpha.12 → 1.1.12-alpha.14
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/dist/index.js +1387 -234
- package/package.json +1 -2
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import fs20 from "fs";
|
|
3
|
+
import path18 from "path";
|
|
4
4
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
5
5
|
import { config as dotenvConfig } from "dotenv";
|
|
6
6
|
|
|
@@ -116,17 +116,1172 @@ Command "${ctx.commandName}" completed in ${elapsed}ms`);
|
|
|
116
116
|
};
|
|
117
117
|
|
|
118
118
|
// src/commands/db/schema.handler.ts
|
|
119
|
-
import
|
|
120
|
-
import
|
|
119
|
+
import path3 from "path";
|
|
120
|
+
import fs3 from "fs";
|
|
121
121
|
import { fileURLToPath } from "url";
|
|
122
122
|
import { spawnSync } from "child_process";
|
|
123
123
|
import { createRequire } from "module";
|
|
124
124
|
import { config as loadEnv } from "dotenv";
|
|
125
|
+
|
|
126
|
+
// src/commands/db/gen-dbschema/postprocess.ts
|
|
127
|
+
import fs2 from "fs";
|
|
128
|
+
import path2 from "path";
|
|
129
|
+
|
|
130
|
+
// src/commands/db/gen-dbschema/helper/header-format.ts
|
|
131
|
+
var HEADER_COMMENT = "/** auto generated, do not edit */";
|
|
132
|
+
function ensureHeaderComment(source) {
|
|
133
|
+
let text = source.startsWith("\uFEFF") ? source.slice(1) : source;
|
|
134
|
+
while (text.startsWith(HEADER_COMMENT)) {
|
|
135
|
+
text = text.slice(HEADER_COMMENT.length);
|
|
136
|
+
text = stripLeadingNewlines(text);
|
|
137
|
+
}
|
|
138
|
+
const trimmed = stripLeadingNewlines(text);
|
|
139
|
+
if (trimmed.length === 0) {
|
|
140
|
+
return `${HEADER_COMMENT}
|
|
141
|
+
`;
|
|
142
|
+
}
|
|
143
|
+
return `${HEADER_COMMENT}
|
|
144
|
+
${trimmed}`;
|
|
145
|
+
}
|
|
146
|
+
function stripLeadingNewlines(value) {
|
|
147
|
+
let current = value;
|
|
148
|
+
while (current.startsWith("\r\n") || current.startsWith("\n")) {
|
|
149
|
+
current = current.startsWith("\r\n") ? current.slice(2) : current.slice(1);
|
|
150
|
+
}
|
|
151
|
+
return current;
|
|
152
|
+
}
|
|
153
|
+
function collapseExtraBlankLines(text) {
|
|
154
|
+
return text.replace(/\n{3,}/g, "\n\n");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// src/commands/db/gen-dbschema/helper/schema-conversion.ts
|
|
158
|
+
function removePgSchemaDeclarations(source) {
|
|
159
|
+
return source.replace(/export const \w+ = pgSchema\([\s\S]*?\);\n*/g, "");
|
|
160
|
+
}
|
|
161
|
+
function convertSchemaTableInvocations(source) {
|
|
162
|
+
let converted = 0;
|
|
163
|
+
let text = source.replace(/([A-Za-z0-9_]+)\.table\(/g, () => {
|
|
164
|
+
converted += 1;
|
|
165
|
+
return "pgTable(";
|
|
166
|
+
});
|
|
167
|
+
text = text.replace(/([A-Za-z0-9_]+)\.view\(/g, () => {
|
|
168
|
+
converted += 1;
|
|
169
|
+
return "pgView(";
|
|
170
|
+
});
|
|
171
|
+
text = text.replace(/([A-Za-z0-9_]+)\.materializedView\(/g, () => {
|
|
172
|
+
converted += 1;
|
|
173
|
+
return "pgMaterializedView(";
|
|
174
|
+
});
|
|
175
|
+
text = text.replace(/([A-Za-z0-9_]+)\.enum\(/g, () => {
|
|
176
|
+
converted += 1;
|
|
177
|
+
return "pgEnum(";
|
|
178
|
+
});
|
|
179
|
+
text = text.replace(/([A-Za-z0-9_]+)\.sequence\(/g, () => {
|
|
180
|
+
converted += 1;
|
|
181
|
+
return "pgSequence(";
|
|
182
|
+
});
|
|
183
|
+
return { text, converted };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/commands/db/gen-dbschema/helper/table-rename.ts
|
|
187
|
+
import { pinyin } from "pinyin-pro";
|
|
188
|
+
function renamePgTableConstants(source) {
|
|
189
|
+
const pgTableRegex = /export const\s+([^\s=]+)\s*=\s*(pgTable|pgView|pgMaterializedView)\(\s*["'`]([^"'`]+)["'`]/gu;
|
|
190
|
+
const renames = [];
|
|
191
|
+
const updated = source.replace(pgTableRegex, (match, currentName, factory, tableName) => {
|
|
192
|
+
const sanitized = sanitizeIdentifier(tableName);
|
|
193
|
+
if (sanitized === currentName) {
|
|
194
|
+
return match;
|
|
195
|
+
}
|
|
196
|
+
renames.push({ from: currentName, to: sanitized });
|
|
197
|
+
const equalsIndex = match.indexOf("=");
|
|
198
|
+
const suffix = equalsIndex >= 0 ? match.slice(equalsIndex) : ` = ${factory}("${tableName}"`;
|
|
199
|
+
const normalizedSuffix = suffix.trimStart();
|
|
200
|
+
return `export const ${sanitized} ${normalizedSuffix}`;
|
|
201
|
+
});
|
|
202
|
+
return { text: updated, renames };
|
|
203
|
+
}
|
|
204
|
+
function updateTableReferenceIdentifiers(source, renames) {
|
|
205
|
+
if (renames.length === 0) {
|
|
206
|
+
return source;
|
|
207
|
+
}
|
|
208
|
+
return renames.reduce((acc, rename) => {
|
|
209
|
+
if (!rename.from || rename.from === rename.to) {
|
|
210
|
+
return acc;
|
|
211
|
+
}
|
|
212
|
+
const pattern = new RegExp(`\\b${escapeRegExp(rename.from)}(\\s*\\.)`, "g");
|
|
213
|
+
return acc.replace(pattern, `${rename.to}$1`);
|
|
214
|
+
}, source);
|
|
215
|
+
}
|
|
216
|
+
function escapeRegExp(value) {
|
|
217
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\//g, "\\/");
|
|
218
|
+
}
|
|
219
|
+
function toCamelCase(str) {
|
|
220
|
+
const words = str.split(/[_\-\s]+/).filter(Boolean);
|
|
221
|
+
if (words.length === 0) {
|
|
222
|
+
return "";
|
|
223
|
+
}
|
|
224
|
+
return words.map((word, index) => {
|
|
225
|
+
if (index === 0) {
|
|
226
|
+
return word.toLowerCase();
|
|
227
|
+
}
|
|
228
|
+
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
229
|
+
}).join("");
|
|
230
|
+
}
|
|
231
|
+
function sanitizeIdentifier(name) {
|
|
232
|
+
const asciiName = toAsciiName(name);
|
|
233
|
+
let sanitized = asciiName.replace(/[^A-Za-z0-9_]/g, "_");
|
|
234
|
+
sanitized = sanitized.replace(/_+/g, "_");
|
|
235
|
+
sanitized = sanitized.replace(/^_|_$/g, "");
|
|
236
|
+
sanitized = toCamelCase(sanitized);
|
|
237
|
+
if (!sanitized) {
|
|
238
|
+
sanitized = "table";
|
|
239
|
+
}
|
|
240
|
+
if (!/^[A-Za-z_]/.test(sanitized)) {
|
|
241
|
+
sanitized = `_${sanitized}`;
|
|
242
|
+
}
|
|
243
|
+
return sanitized;
|
|
244
|
+
}
|
|
245
|
+
function toAsciiName(name) {
|
|
246
|
+
if (!/[^\x00-\x7F]/.test(name)) {
|
|
247
|
+
return name;
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
const transliterated = pinyin(name, { toneType: "none", type: "array" }).join("_");
|
|
251
|
+
return transliterated || name;
|
|
252
|
+
} catch (error) {
|
|
253
|
+
return name;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// src/commands/db/gen-dbschema/helper/custom-types.ts
|
|
258
|
+
var CUSTOM_TYPE_PATTERN = /\/\/ TODO: failed to parse database type '(?:\w+\.)?(user_profile|file_attachment)(\[\])?'/;
|
|
259
|
+
function replaceUnknownColumns(source) {
|
|
260
|
+
const lines = source.split("\n");
|
|
261
|
+
const result = [];
|
|
262
|
+
let replaced = 0;
|
|
263
|
+
const unmatched = [];
|
|
264
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
265
|
+
const line = lines[i];
|
|
266
|
+
const match = line.match(CUSTOM_TYPE_PATTERN);
|
|
267
|
+
if (match) {
|
|
268
|
+
const typeName = match[1];
|
|
269
|
+
const factory = typeName === "user_profile" ? "userProfile" : "fileAttachment";
|
|
270
|
+
const replacedLine = replaceFollowingUnknown(lines[i + 1], factory);
|
|
271
|
+
if (replacedLine) {
|
|
272
|
+
result.push(replacedLine);
|
|
273
|
+
replaced += 1;
|
|
274
|
+
i += 1;
|
|
275
|
+
} else {
|
|
276
|
+
unmatched.push(line.trim());
|
|
277
|
+
result.push(line);
|
|
278
|
+
}
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
if (line.includes("unknown(")) {
|
|
282
|
+
unmatched.push(line.trim());
|
|
283
|
+
}
|
|
284
|
+
result.push(line);
|
|
285
|
+
}
|
|
286
|
+
return {
|
|
287
|
+
text: result.join("\n"),
|
|
288
|
+
replaced,
|
|
289
|
+
unmatched
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
function replaceFollowingUnknown(nextLine, factory) {
|
|
293
|
+
if (!nextLine || !nextLine.includes("unknown(")) {
|
|
294
|
+
return void 0;
|
|
295
|
+
}
|
|
296
|
+
return nextLine.replace("unknown(", `${factory}(`);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// src/commands/db/gen-dbschema/helper/imports.ts
|
|
300
|
+
import fs from "fs";
|
|
301
|
+
import path from "path";
|
|
302
|
+
function tweakImports(source) {
|
|
303
|
+
const importRegex = /import \{([^}]*)\} from "drizzle-orm\/pg-core";?/;
|
|
304
|
+
const match = source.match(importRegex);
|
|
305
|
+
if (!match) {
|
|
306
|
+
return source;
|
|
307
|
+
}
|
|
308
|
+
const identifiers = match[1].split(",").map((id) => id.trim()).filter(Boolean).filter((id) => id !== "pgSchema" && id !== "customType");
|
|
309
|
+
const filteredIdentifiers = identifiers.filter((id) => {
|
|
310
|
+
if (id === "timestamp") {
|
|
311
|
+
const timestampUsageRegex = /timestamp\s*\(/;
|
|
312
|
+
return timestampUsageRegex.test(source);
|
|
313
|
+
}
|
|
314
|
+
return true;
|
|
315
|
+
});
|
|
316
|
+
if (source.includes("pgTable(") && !filteredIdentifiers.includes("pgTable")) {
|
|
317
|
+
filteredIdentifiers.push("pgTable");
|
|
318
|
+
}
|
|
319
|
+
if (source.includes("pgView(") && !filteredIdentifiers.includes("pgView")) {
|
|
320
|
+
filteredIdentifiers.push("pgView");
|
|
321
|
+
}
|
|
322
|
+
if (source.includes("pgMaterializedView(") && !filteredIdentifiers.includes("pgMaterializedView")) {
|
|
323
|
+
filteredIdentifiers.push("pgMaterializedView");
|
|
324
|
+
}
|
|
325
|
+
if (source.includes("pgEnum(") && !filteredIdentifiers.includes("pgEnum")) {
|
|
326
|
+
filteredIdentifiers.push("pgEnum");
|
|
327
|
+
}
|
|
328
|
+
if (source.includes("pgSequence(") && !filteredIdentifiers.includes("pgSequence")) {
|
|
329
|
+
filteredIdentifiers.push("pgSequence");
|
|
330
|
+
}
|
|
331
|
+
const unique = Array.from(new Set(filteredIdentifiers));
|
|
332
|
+
const replacement = `import { ${unique.join(", ")} } from "drizzle-orm/pg-core"`;
|
|
333
|
+
return source.replace(importRegex, replacement);
|
|
334
|
+
}
|
|
335
|
+
function inlineCustomTypes(source) {
|
|
336
|
+
let text = source.replace(/import \{[^}]*\} from ["']\.\/types["'];?\n*/g, "");
|
|
337
|
+
const possiblePaths = [
|
|
338
|
+
// When bundled by tsup - __dirname points to dist/
|
|
339
|
+
path.resolve(__dirname, "template", "types.ts"),
|
|
340
|
+
// When running from source (development) - relative to helper/imports.ts
|
|
341
|
+
path.resolve(__dirname, "../template", "types.ts"),
|
|
342
|
+
// Alternative paths
|
|
343
|
+
path.resolve(__dirname, "../../template", "types.ts"),
|
|
344
|
+
path.resolve(__dirname, "../../../template", "types.ts")
|
|
345
|
+
];
|
|
346
|
+
let templatePath;
|
|
347
|
+
for (const possiblePath of possiblePaths) {
|
|
348
|
+
if (fs.existsSync(possiblePath)) {
|
|
349
|
+
templatePath = possiblePath;
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (!templatePath) {
|
|
354
|
+
console.warn("[postprocess-drizzle-schema] Template types file not found. Tried paths:", possiblePaths);
|
|
355
|
+
return text;
|
|
356
|
+
}
|
|
357
|
+
return inlineFromTemplate(text, templatePath);
|
|
358
|
+
}
|
|
359
|
+
function inlineFromTemplate(text, templatePath) {
|
|
360
|
+
const templateContent = fs.readFileSync(templatePath, "utf8");
|
|
361
|
+
const typeDefinitions = templateContent.replace(/^import\s+.*;\r?\n*/gm, "").trim();
|
|
362
|
+
const needsSql = typeDefinitions.includes("sql`") && !text.includes("from 'drizzle-orm'") && !text.includes('from "drizzle-orm"');
|
|
363
|
+
const needsCustomType = typeDefinitions.includes("customType<") && !text.includes("customType");
|
|
364
|
+
if (needsCustomType) {
|
|
365
|
+
text = ensureImportIdentifier(text, "drizzle-orm/pg-core", "customType");
|
|
366
|
+
}
|
|
367
|
+
if (needsSql && !text.includes("from 'drizzle-orm'") && !text.includes('from "drizzle-orm"')) {
|
|
368
|
+
const importMatch = text.match(/^import [\s\S]*?from ["']drizzle-orm\/pg-core["'];?\n/m);
|
|
369
|
+
if (importMatch) {
|
|
370
|
+
const insertPoint = text.indexOf(importMatch[0]) + importMatch[0].length;
|
|
371
|
+
text = text.slice(0, insertPoint) + "import { sql } from 'drizzle-orm';\n" + text.slice(insertPoint);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
const headerPrefix = `${HEADER_COMMENT}
|
|
375
|
+
`;
|
|
376
|
+
let insertionPoint = 0;
|
|
377
|
+
if (text.startsWith(headerPrefix)) {
|
|
378
|
+
insertionPoint = headerPrefix.length;
|
|
379
|
+
}
|
|
380
|
+
const importSectionMatch = text.slice(insertionPoint).match(/^(?:import [^\n]+\n)+/);
|
|
381
|
+
if (importSectionMatch) {
|
|
382
|
+
insertionPoint += importSectionMatch[0].length;
|
|
383
|
+
}
|
|
384
|
+
const typeBlock = `
|
|
385
|
+
${typeDefinitions}
|
|
386
|
+
|
|
387
|
+
`;
|
|
388
|
+
return text.slice(0, insertionPoint) + typeBlock + text.slice(insertionPoint);
|
|
389
|
+
}
|
|
390
|
+
function ensureImportIdentifier(source, packageName, identifier) {
|
|
391
|
+
const escapedPackage = packageName.replace(/\//g, "\\/");
|
|
392
|
+
const importRegex = new RegExp(`import \\{([^}]*)\\} from ["']${escapedPackage}["'];?`);
|
|
393
|
+
const match = source.match(importRegex);
|
|
394
|
+
if (!match) {
|
|
395
|
+
return source;
|
|
396
|
+
}
|
|
397
|
+
const identifiers = match[1].split(",").map((id) => id.trim()).filter(Boolean);
|
|
398
|
+
if (identifiers.includes(identifier)) {
|
|
399
|
+
return source;
|
|
400
|
+
}
|
|
401
|
+
identifiers.push(identifier);
|
|
402
|
+
const unique = Array.from(new Set(identifiers));
|
|
403
|
+
const replacement = `import { ${unique.join(", ")} } from "${packageName}"`;
|
|
404
|
+
return source.replace(importRegex, replacement);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// src/commands/db/gen-dbschema/helper/system-fields.ts
|
|
408
|
+
function addSystemFieldComments(source) {
|
|
409
|
+
const commentMap = {
|
|
410
|
+
"_created_at": "Creation time",
|
|
411
|
+
"_created_by": "Creator",
|
|
412
|
+
"_updated_at": "Update time",
|
|
413
|
+
"_updated_by": "Updater"
|
|
414
|
+
};
|
|
415
|
+
const lines = source.split("\n");
|
|
416
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
417
|
+
const line = lines[i];
|
|
418
|
+
const entry = Object.entries(commentMap).find(([key]) => line.includes(`"${key}"`));
|
|
419
|
+
if (!entry) {
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
const [, description] = entry;
|
|
423
|
+
const previousLine = lines[i - 1]?.trim() ?? "";
|
|
424
|
+
if (previousLine.startsWith("//") && previousLine.includes("System field")) {
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
const indentMatch = line.match(/^\s*/);
|
|
428
|
+
const indent = indentMatch ? indentMatch[0] : "";
|
|
429
|
+
const comment = `${indent}// System field: ${description} (auto-filled, do not modify)`;
|
|
430
|
+
lines.splice(i, 0, comment);
|
|
431
|
+
i += 1;
|
|
432
|
+
}
|
|
433
|
+
return lines.join("\n");
|
|
434
|
+
}
|
|
435
|
+
function removeConflictingSystemFields(source) {
|
|
436
|
+
const systemFieldMap = {
|
|
437
|
+
"_created_at": "created_at",
|
|
438
|
+
"_created_by": "created_by",
|
|
439
|
+
"_updated_at": "updated_at",
|
|
440
|
+
"_updated_by": "updated_by"
|
|
441
|
+
};
|
|
442
|
+
const lines = source.split("\n");
|
|
443
|
+
const result = [];
|
|
444
|
+
let inTable = false;
|
|
445
|
+
let tableStartLine = -1;
|
|
446
|
+
const tableBusinessFields = /* @__PURE__ */ new Set();
|
|
447
|
+
let bracketDepth = 0;
|
|
448
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
449
|
+
const line = lines[i];
|
|
450
|
+
if (!inTable && /=\s*(pgTable|pgView|pgMaterializedView)\s*\(/.test(line)) {
|
|
451
|
+
inTable = true;
|
|
452
|
+
tableStartLine = result.length;
|
|
453
|
+
tableBusinessFields.clear();
|
|
454
|
+
bracketDepth = 0;
|
|
455
|
+
}
|
|
456
|
+
if (inTable) {
|
|
457
|
+
for (const char of line) {
|
|
458
|
+
if (char === "{") bracketDepth++;
|
|
459
|
+
if (char === "}") bracketDepth--;
|
|
460
|
+
}
|
|
461
|
+
for (const businessField of Object.values(systemFieldMap)) {
|
|
462
|
+
if (line.includes(`"${businessField}"`) || line.includes(`'${businessField}'`)) {
|
|
463
|
+
tableBusinessFields.add(businessField);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
if (bracketDepth === 0 && line.includes(");")) {
|
|
467
|
+
inTable = false;
|
|
468
|
+
const tableEndLine = result.length;
|
|
469
|
+
for (let j = tableStartLine; j <= tableEndLine; j++) {
|
|
470
|
+
const tableLine = result[j] || "";
|
|
471
|
+
let shouldRemove = false;
|
|
472
|
+
for (const [systemField, businessField] of Object.entries(systemFieldMap)) {
|
|
473
|
+
if (tableBusinessFields.has(businessField)) {
|
|
474
|
+
if (tableLine.includes(`"${systemField}"`) || tableLine.includes(`'${systemField}'`)) {
|
|
475
|
+
shouldRemove = true;
|
|
476
|
+
if (j > 0 && result[j - 1]?.includes("// System field:")) {
|
|
477
|
+
result[j - 1] = null;
|
|
478
|
+
}
|
|
479
|
+
break;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
if (shouldRemove) {
|
|
484
|
+
result[j] = null;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
result.push(line);
|
|
490
|
+
}
|
|
491
|
+
return result.filter((line) => line !== null).join("\n");
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// src/commands/db/gen-dbschema/helper/patch-helper.ts
|
|
495
|
+
function patchDrizzleKitDefects(source) {
|
|
496
|
+
let fixed = 0;
|
|
497
|
+
const text = source.replace(/\.default\('\)/g, () => {
|
|
498
|
+
fixed += 1;
|
|
499
|
+
return `.default('')`;
|
|
500
|
+
});
|
|
501
|
+
return { text, fixed };
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// src/commands/db/gen-dbschema/helper/timestamp-replacement.ts
|
|
505
|
+
function replaceTimestampWithCustomTypes(source) {
|
|
506
|
+
let replaced = 0;
|
|
507
|
+
const pattern = /timestamp\((['"])(.*?)\1,\s*(\{[^}]*\})\)/g;
|
|
508
|
+
const text = source.replace(pattern, (match, quote, fieldName, options) => {
|
|
509
|
+
const hasWithTimezone = /withTimezone:\s*true/.test(options);
|
|
510
|
+
const hasModeString = /mode:\s*['"]string['"]/.test(options);
|
|
511
|
+
if (hasWithTimezone && hasModeString) {
|
|
512
|
+
replaced += 1;
|
|
513
|
+
return `customTimestamptz(${quote}${fieldName}${quote})`;
|
|
514
|
+
}
|
|
515
|
+
return match;
|
|
516
|
+
});
|
|
517
|
+
return { text, replaced };
|
|
518
|
+
}
|
|
519
|
+
function replaceDefaultNowWithSql(source) {
|
|
520
|
+
let replaced = 0;
|
|
521
|
+
const pattern = /\.defaultNow\(\)/g;
|
|
522
|
+
const text = source.replace(pattern, () => {
|
|
523
|
+
replaced += 1;
|
|
524
|
+
return ".default(sql`CURRENT_TIMESTAMP`)";
|
|
525
|
+
});
|
|
526
|
+
return { text, replaced };
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// src/commands/db/gen-dbschema/helper/appendTableAliases.ts
|
|
530
|
+
var TABLE_ALIAS_MARKER = "// table aliases";
|
|
531
|
+
function appendTableAliases(source) {
|
|
532
|
+
const markerIndex = source.indexOf(`
|
|
533
|
+
${TABLE_ALIAS_MARKER}`);
|
|
534
|
+
const base = markerIndex === -1 ? source : source.slice(0, markerIndex);
|
|
535
|
+
const exportRegex = /export const\s+([A-Za-z_$][\w$]*)\s*=\s*pgTable\s*\(/g;
|
|
536
|
+
const tableExports = /* @__PURE__ */ new Set();
|
|
537
|
+
for (const match of base.matchAll(exportRegex)) {
|
|
538
|
+
const name = match[1];
|
|
539
|
+
tableExports.add(name);
|
|
540
|
+
}
|
|
541
|
+
if (tableExports.size === 0) {
|
|
542
|
+
return base;
|
|
543
|
+
}
|
|
544
|
+
const aliasLines = Array.from(tableExports).sort().map((name) => `export const ${name}Table = ${name};`).join("\n");
|
|
545
|
+
const prefix = base.trimEnd();
|
|
546
|
+
return `${prefix}
|
|
547
|
+
|
|
548
|
+
${TABLE_ALIAS_MARKER}
|
|
549
|
+
${aliasLines}
|
|
550
|
+
`;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// src/commands/db/gen-dbschema/postprocess.ts
|
|
554
|
+
function postprocessDrizzleSchema(targetPath) {
|
|
555
|
+
const resolvedPath = path2.resolve(targetPath);
|
|
556
|
+
if (!fs2.existsSync(resolvedPath)) {
|
|
557
|
+
console.warn(`[postprocess-drizzle-schema] File not found: ${resolvedPath}`);
|
|
558
|
+
return void 0;
|
|
559
|
+
}
|
|
560
|
+
let text = fs2.readFileSync(resolvedPath, "utf8");
|
|
561
|
+
text = ensureHeaderComment(text);
|
|
562
|
+
const patchResult = patchDrizzleKitDefects(text);
|
|
563
|
+
text = patchResult.text;
|
|
564
|
+
text = removePgSchemaDeclarations(text);
|
|
565
|
+
const tableConversion = convertSchemaTableInvocations(text);
|
|
566
|
+
text = tableConversion.text;
|
|
567
|
+
const renameResult = renamePgTableConstants(text);
|
|
568
|
+
text = renameResult.text;
|
|
569
|
+
text = updateTableReferenceIdentifiers(text, renameResult.renames);
|
|
570
|
+
const replacement = replaceUnknownColumns(text);
|
|
571
|
+
text = replacement.text;
|
|
572
|
+
const timestampReplacement = replaceTimestampWithCustomTypes(text);
|
|
573
|
+
text = timestampReplacement.text;
|
|
574
|
+
const defaultNowReplacement = replaceDefaultNowWithSql(text);
|
|
575
|
+
text = defaultNowReplacement.text;
|
|
576
|
+
text = removeConflictingSystemFields(text);
|
|
577
|
+
text = addSystemFieldComments(text);
|
|
578
|
+
text = tweakImports(text);
|
|
579
|
+
text = inlineCustomTypes(text);
|
|
580
|
+
text = appendTableAliases(text);
|
|
581
|
+
text = text.replace(/\r?\n/g, "\n");
|
|
582
|
+
text = collapseExtraBlankLines(text);
|
|
583
|
+
fs2.writeFileSync(resolvedPath, text, "utf8");
|
|
584
|
+
if (patchResult.fixed > 0) {
|
|
585
|
+
console.info(`[postprocess-drizzle-schema] Patched ${patchResult.fixed} drizzle-kit defects (.default(') -> .default(''))`);
|
|
586
|
+
}
|
|
587
|
+
if (replacement.replaced > 0) {
|
|
588
|
+
console.info(`[postprocess-drizzle-schema] Replaced ${replacement.replaced} unknown columns`);
|
|
589
|
+
}
|
|
590
|
+
if (replacement.unmatched.length > 0) {
|
|
591
|
+
console.warn("[postprocess-drizzle-schema] Unmatched custom types:", replacement.unmatched.length);
|
|
592
|
+
replacement.unmatched.forEach((line) => console.warn(` ${line}`));
|
|
593
|
+
}
|
|
594
|
+
if (tableConversion.converted > 0) {
|
|
595
|
+
console.info(`[postprocess-drizzle-schema] Converted ${tableConversion.converted} schema.table invocations to pgTable`);
|
|
596
|
+
}
|
|
597
|
+
if (timestampReplacement.replaced > 0) {
|
|
598
|
+
console.info(`[postprocess-drizzle-schema] Replaced ${timestampReplacement.replaced} timestamp fields with customTimestamptz`);
|
|
599
|
+
}
|
|
600
|
+
if (defaultNowReplacement.replaced > 0) {
|
|
601
|
+
console.info(`[postprocess-drizzle-schema] Replaced ${defaultNowReplacement.replaced} .defaultNow() with .default(sql\`CURRENT_TIMESTAMP\`)`);
|
|
602
|
+
}
|
|
603
|
+
return {
|
|
604
|
+
replacedUnknown: replacement.replaced,
|
|
605
|
+
unmatchedUnknown: replacement.unmatched,
|
|
606
|
+
patchedDefects: patchResult.fixed,
|
|
607
|
+
replacedTimestamps: timestampReplacement.replaced,
|
|
608
|
+
replacedDefaultNow: defaultNowReplacement.replaced
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// src/commands/db/gen-nest-resource/generator.ts
|
|
613
|
+
import { pluralize } from "inflection";
|
|
614
|
+
|
|
615
|
+
// src/commands/db/gen-nest-resource/utils.ts
|
|
616
|
+
function mapDrizzleTypeToTS(field) {
|
|
617
|
+
const typeMap = {
|
|
618
|
+
// String types
|
|
619
|
+
char: "string",
|
|
620
|
+
varchar: "string",
|
|
621
|
+
text: "string",
|
|
622
|
+
// Numeric types
|
|
623
|
+
smallint: "number",
|
|
624
|
+
integer: "number",
|
|
625
|
+
int: "number",
|
|
626
|
+
bigint: "string",
|
|
627
|
+
// bigint 在 JS 中通常作为 string 处理
|
|
628
|
+
serial: "number",
|
|
629
|
+
smallserial: "number",
|
|
630
|
+
bigserial: "string",
|
|
631
|
+
// Decimal types
|
|
632
|
+
decimal: "string",
|
|
633
|
+
// 精确数值通常用 string
|
|
634
|
+
numeric: "string",
|
|
635
|
+
real: "number",
|
|
636
|
+
doublePrecision: "number",
|
|
637
|
+
// Boolean
|
|
638
|
+
boolean: "boolean",
|
|
639
|
+
// Date/Time types
|
|
640
|
+
timestamp: "Date",
|
|
641
|
+
timestamptz: "Date",
|
|
642
|
+
date: "Date",
|
|
643
|
+
time: "string",
|
|
644
|
+
timetz: "string",
|
|
645
|
+
interval: "string",
|
|
646
|
+
// UUID
|
|
647
|
+
uuid: "string",
|
|
648
|
+
// JSON types
|
|
649
|
+
json: "any",
|
|
650
|
+
jsonb: "any",
|
|
651
|
+
// Binary
|
|
652
|
+
bytea: "Buffer",
|
|
653
|
+
// Network types
|
|
654
|
+
inet: "string",
|
|
655
|
+
cidr: "string",
|
|
656
|
+
macaddr: "string",
|
|
657
|
+
macaddr8: "string",
|
|
658
|
+
// Geometric types
|
|
659
|
+
point: "{ x: number; y: number }",
|
|
660
|
+
line: "string",
|
|
661
|
+
lseg: "string",
|
|
662
|
+
box: "string",
|
|
663
|
+
path: "string",
|
|
664
|
+
polygon: "string",
|
|
665
|
+
circle: "string",
|
|
666
|
+
// Array types (handled by isArray flag)
|
|
667
|
+
array: "any[]",
|
|
668
|
+
// Custom types
|
|
669
|
+
customType: "any",
|
|
670
|
+
customTimestamptz: "Date",
|
|
671
|
+
userProfile: "string",
|
|
672
|
+
fileAttachment: "FileAttachment",
|
|
673
|
+
// Enum (handled separately)
|
|
674
|
+
pgEnum: "string"
|
|
675
|
+
};
|
|
676
|
+
let baseType = typeMap[field.type] || "any";
|
|
677
|
+
if (field.isArray) {
|
|
678
|
+
baseType = baseType.endsWith("[]") ? baseType : `${baseType}[]`;
|
|
679
|
+
}
|
|
680
|
+
if (field.enumValues && field.enumValues.length > 0) {
|
|
681
|
+
baseType = field.enumValues.map((v) => `'${v}'`).join(" | ");
|
|
682
|
+
}
|
|
683
|
+
return baseType;
|
|
684
|
+
}
|
|
685
|
+
function toPascalCase(str) {
|
|
686
|
+
return str.replace(/([a-z])([A-Z])/g, "$1 $2").split(/[-_\s]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
687
|
+
}
|
|
688
|
+
function toKebabCase(str) {
|
|
689
|
+
return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase().replace(/[_\s]/g, "-");
|
|
690
|
+
}
|
|
691
|
+
function toSnakeCase(str) {
|
|
692
|
+
return str.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase().replace(/[-\s]/g, "_");
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// src/commands/db/gen-nest-resource/generator.ts
|
|
696
|
+
function generateDTO(table) {
|
|
697
|
+
const className = toPascalCase(table.variableName);
|
|
698
|
+
let dto = `// \u8BF7\u4FEE\u6539\u8BE5\u6587\u4EF6\u4EE3\u7801\u4EE5\u6EE1\u8DB3\u9700\u6C42
|
|
699
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
700
|
+
import { IsDefined, IsNumber, IsOptional, IsString, MaxLength, IsInt, IsBoolean, IsUUID, IsDate, IsObject, IsArray } from 'class-validator';
|
|
701
|
+
import { Type } from 'class-transformer';
|
|
702
|
+
import { FileAttachment } from '../../../database/schema';
|
|
703
|
+
|
|
704
|
+
`;
|
|
705
|
+
dto += `export class Create${className}Dto {
|
|
706
|
+
`;
|
|
707
|
+
for (const field of table.fields) {
|
|
708
|
+
if (field.isPrimaryKey || field.name === "id" || field.name.startsWith("_") || field.name.startsWith("created") || field.name.startsWith("updated")) {
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
const tsType = mapDrizzleTypeToTS(field);
|
|
712
|
+
const optional = field.nullable || field.hasDefault ? "?" : "";
|
|
713
|
+
const decorators = generateValidationDecorators(field);
|
|
714
|
+
if (decorators) {
|
|
715
|
+
dto += decorators;
|
|
716
|
+
}
|
|
717
|
+
dto += ` ${field.name}${optional}: ${tsType};
|
|
718
|
+
|
|
719
|
+
`;
|
|
720
|
+
}
|
|
721
|
+
dto += "}\n\n";
|
|
722
|
+
dto += `export class Update${className}Dto {
|
|
723
|
+
`;
|
|
724
|
+
for (const field of table.fields) {
|
|
725
|
+
if (field.name.startsWith("_") || field.name.startsWith("created") || field.name.startsWith("updated") || field.isPrimaryKey || field.name === "id") {
|
|
726
|
+
continue;
|
|
727
|
+
}
|
|
728
|
+
const tsType = mapDrizzleTypeToTS(field);
|
|
729
|
+
const decorators = generateValidationDecorators(field, {
|
|
730
|
+
isUpdate: true
|
|
731
|
+
});
|
|
732
|
+
if (decorators) {
|
|
733
|
+
dto += decorators;
|
|
734
|
+
}
|
|
735
|
+
dto += ` ${field.name}?: ${tsType};
|
|
736
|
+
|
|
737
|
+
`;
|
|
738
|
+
}
|
|
739
|
+
dto += "}\n\n";
|
|
740
|
+
dto += `export class ${className}ResponseDto {
|
|
741
|
+
`;
|
|
742
|
+
for (const field of table.fields) {
|
|
743
|
+
const tsType = mapDrizzleTypeToTS(field);
|
|
744
|
+
const optional = field.nullable ? "?" : "";
|
|
745
|
+
const decorators = generateValidationDecorators(field, {
|
|
746
|
+
isResponse: true
|
|
747
|
+
});
|
|
748
|
+
if (decorators) {
|
|
749
|
+
dto += decorators;
|
|
750
|
+
}
|
|
751
|
+
dto += ` ${field.name}${optional}: ${tsType};
|
|
752
|
+
|
|
753
|
+
`;
|
|
754
|
+
}
|
|
755
|
+
dto += "}\n";
|
|
756
|
+
return dto;
|
|
757
|
+
}
|
|
758
|
+
function generateValidationDecorators(field, {
|
|
759
|
+
isUpdate = false,
|
|
760
|
+
isResponse = false
|
|
761
|
+
} = {}) {
|
|
762
|
+
let decorators = " // \u8BF7\u6309\u7528\u6237\u9700\u6C42\u4FEE\u6539\u4EE5\u4E0B\u88C5\u9970\u5668\u6CE8\u91CA\n";
|
|
763
|
+
if (field.nullable || !isResponse && field.hasDefault || isUpdate) {
|
|
764
|
+
decorators += ` @ApiPropertyOptional({ description: '${field.comment || field.name}' })
|
|
765
|
+
`;
|
|
766
|
+
if (isResponse) {
|
|
767
|
+
return decorators;
|
|
768
|
+
}
|
|
769
|
+
decorators += " @IsOptional()\n";
|
|
770
|
+
} else {
|
|
771
|
+
decorators += ` @ApiProperty({ description: '${field.comment || field.name}' })
|
|
772
|
+
`;
|
|
773
|
+
if (isResponse) {
|
|
774
|
+
return decorators;
|
|
775
|
+
}
|
|
776
|
+
decorators += " @IsDefined()\n";
|
|
777
|
+
}
|
|
778
|
+
switch (field.type) {
|
|
779
|
+
case "varchar":
|
|
780
|
+
case "char":
|
|
781
|
+
case "text":
|
|
782
|
+
decorators += " @IsString()\n";
|
|
783
|
+
if (field.length) {
|
|
784
|
+
decorators += ` @MaxLength(${field.length})
|
|
785
|
+
`;
|
|
786
|
+
}
|
|
787
|
+
break;
|
|
788
|
+
case "integer":
|
|
789
|
+
case "smallint":
|
|
790
|
+
case "serial":
|
|
791
|
+
case "smallserial":
|
|
792
|
+
decorators += " @IsInt()\n";
|
|
793
|
+
break;
|
|
794
|
+
case "decimal":
|
|
795
|
+
case "numeric":
|
|
796
|
+
case "real":
|
|
797
|
+
case "doublePrecision":
|
|
798
|
+
decorators += " @IsNumber()\n";
|
|
799
|
+
break;
|
|
800
|
+
case "boolean":
|
|
801
|
+
decorators += " @IsBoolean()\n";
|
|
802
|
+
break;
|
|
803
|
+
case "uuid":
|
|
804
|
+
decorators += " @IsUUID()\n";
|
|
805
|
+
break;
|
|
806
|
+
case "timestamp":
|
|
807
|
+
case "timestamptz":
|
|
808
|
+
case "date":
|
|
809
|
+
case "customTimestamptz":
|
|
810
|
+
decorators += " @IsDate()\n";
|
|
811
|
+
break;
|
|
812
|
+
case "json":
|
|
813
|
+
case "jsonb":
|
|
814
|
+
decorators += " @IsObject()\n";
|
|
815
|
+
break;
|
|
816
|
+
}
|
|
817
|
+
if (field.isArray) {
|
|
818
|
+
decorators += " @IsArray()\n";
|
|
819
|
+
}
|
|
820
|
+
return decorators;
|
|
821
|
+
}
|
|
822
|
+
function generateController(table) {
|
|
823
|
+
const className = toPascalCase(table.variableName);
|
|
824
|
+
const routePath = toKebabCase(pluralize(table.variableName));
|
|
825
|
+
const filePath = toSnakeCase(table.variableName);
|
|
826
|
+
const pkField = table.fields.find((f) => f.isPrimaryKey);
|
|
827
|
+
const pkType = pkField ? mapDrizzleTypeToTS(pkField) : "string";
|
|
828
|
+
const pkName = pkField ? pkField.name : "id";
|
|
829
|
+
const controller = `
|
|
830
|
+
// \u8BF7\u4FEE\u6539\u8BE5\u6587\u4EF6\u4EE3\u7801\u4EE5\u6EE1\u8DB3\u9700\u6C42
|
|
831
|
+
import {
|
|
832
|
+
Controller,
|
|
833
|
+
Get,
|
|
834
|
+
Post,
|
|
835
|
+
Put,
|
|
836
|
+
Delete,
|
|
837
|
+
Body,
|
|
838
|
+
Param,
|
|
839
|
+
Query,
|
|
840
|
+
} from '@nestjs/common';
|
|
841
|
+
import {
|
|
842
|
+
ApiTags,
|
|
843
|
+
ApiOperation,
|
|
844
|
+
ApiOkResponse,
|
|
845
|
+
ApiCreatedResponse,
|
|
846
|
+
} from '@nestjs/swagger';
|
|
847
|
+
import {
|
|
848
|
+
Create${className}Dto,
|
|
849
|
+
Update${className}Dto,
|
|
850
|
+
${className}ResponseDto
|
|
851
|
+
} from './dtos/${filePath}.dto';
|
|
852
|
+
import { ${className}Service } from './${filePath}.service';
|
|
853
|
+
|
|
854
|
+
@ApiTags('${toPascalCase(table.variableName)}')
|
|
855
|
+
@Controller('api/${routePath}')
|
|
856
|
+
export class ${className}Controller {
|
|
857
|
+
constructor(private readonly ${table.variableName}Service: ${className}Service) {}
|
|
858
|
+
|
|
859
|
+
@Post()
|
|
860
|
+
@ApiOperation({
|
|
861
|
+
summary: '\u521B\u5EFA\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
862
|
+
description: '\u521B\u5EFA\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
863
|
+
})
|
|
864
|
+
@ApiCreatedResponse({
|
|
865
|
+
description: '\u6210\u529F\u521B\u5EFA\u4E00\u6761\u8BB0\u5F55',
|
|
866
|
+
type: ${className}ResponseDto,
|
|
867
|
+
})
|
|
868
|
+
async create(
|
|
869
|
+
@Body() createDto: Create${className}Dto
|
|
870
|
+
): Promise<${className}ResponseDto> {
|
|
871
|
+
return this.${table.variableName}Service.create(createDto);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
@ApiOperation({
|
|
875
|
+
summary: '\u6839\u636E\u4E3B\u952E\u67E5\u8BE2\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
876
|
+
description: '\u6839\u636E\u4E3B\u952E\u67E5\u8BE2\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
877
|
+
})
|
|
878
|
+
@ApiOkResponse({
|
|
879
|
+
description: '\u6210\u529F\u67E5\u8BE2\u4E00\u6761\u8BB0\u5F55',
|
|
880
|
+
type: ${className}ResponseDto,
|
|
881
|
+
})
|
|
882
|
+
@Get(':${pkName}')
|
|
883
|
+
async findOne(
|
|
884
|
+
@Param('${pkName}') ${pkName}: ${pkType}
|
|
885
|
+
): Promise<${className}ResponseDto> {
|
|
886
|
+
return this.${table.variableName}Service.findOne(${pkName});
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
@ApiOperation({
|
|
890
|
+
summary: '\u6839\u636E\u4E3B\u952E\u66F4\u65B0\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
891
|
+
description: '\u6839\u636E\u4E3B\u952E\u66F4\u65B0\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
892
|
+
})
|
|
893
|
+
@ApiOkResponse({
|
|
894
|
+
description: '\u6210\u529F\u66F4\u65B0\u4E00\u6761\u8BB0\u5F55',
|
|
895
|
+
type: ${className}ResponseDto,
|
|
896
|
+
})
|
|
897
|
+
@Put(':${pkName}')
|
|
898
|
+
async update(
|
|
899
|
+
@Param('${pkName}') ${pkName}: ${pkType},
|
|
900
|
+
@Body() updateDto: Update${className}Dto
|
|
901
|
+
): Promise<${className}ResponseDto> {
|
|
902
|
+
return this.${table.variableName}Service.update(${pkName}, updateDto);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
@ApiOperation({
|
|
906
|
+
summary: '\u6839\u636E\u4E3B\u952E\u5220\u9664\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
907
|
+
description: '\u6839\u636E\u4E3B\u952E\u5220\u9664\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
908
|
+
})
|
|
909
|
+
@ApiOkResponse({
|
|
910
|
+
description: '\u6210\u529F\u5220\u9664\u4E00\u6761\u8BB0\u5F55',
|
|
911
|
+
})
|
|
912
|
+
@Delete(':${pkName}')
|
|
913
|
+
async remove(
|
|
914
|
+
@Param('${pkName}') ${pkName}: ${pkType}
|
|
915
|
+
): Promise<void> {
|
|
916
|
+
return this.${table.variableName}Service.remove(${pkName});
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
`;
|
|
920
|
+
return controller;
|
|
921
|
+
}
|
|
922
|
+
function generateService(table) {
|
|
923
|
+
const className = toPascalCase(table.variableName);
|
|
924
|
+
const filePath = toSnakeCase(table.variableName);
|
|
925
|
+
const pkField = table.fields.find((f) => f.isPrimaryKey);
|
|
926
|
+
const pkType = pkField ? mapDrizzleTypeToTS(pkField) : "string";
|
|
927
|
+
const pkName = pkField ? pkField.name : "id";
|
|
928
|
+
const service = `
|
|
929
|
+
// \u8BF7\u4FEE\u6539\u8BE5\u6587\u4EF6\u4EE3\u7801\u4EE5\u6EE1\u8DB3\u9700\u6C42
|
|
930
|
+
import { Injectable, Inject, Logger, NotFoundException } from '@nestjs/common';
|
|
931
|
+
import { eq } from 'drizzle-orm';
|
|
932
|
+
import { DRIZZLE_DATABASE, type PostgresJsDatabase } from '@lark-apaas/fullstack-nestjs-core';
|
|
933
|
+
import { ${table.variableName} } from '../../database/schema';
|
|
934
|
+
import {
|
|
935
|
+
Create${className}Dto,
|
|
936
|
+
Update${className}Dto,
|
|
937
|
+
${className}ResponseDto
|
|
938
|
+
} from './dtos/${filePath}.dto';
|
|
939
|
+
|
|
940
|
+
@Injectable()
|
|
941
|
+
export class ${className}Service {
|
|
942
|
+
private readonly logger = new Logger(${className}Service.name);
|
|
943
|
+
|
|
944
|
+
constructor(@Inject(DRIZZLE_DATABASE) private readonly db: PostgresJsDatabase) {}
|
|
945
|
+
|
|
946
|
+
async create(createDto: Create${className}Dto): Promise<${className}ResponseDto> {
|
|
947
|
+
const [result] = await this.db
|
|
948
|
+
.insert(${table.variableName})
|
|
949
|
+
.values(createDto)
|
|
950
|
+
.returning();
|
|
951
|
+
|
|
952
|
+
this.logger.log(\`Created ${className} with ${pkName} \${result.${pkName}}\`);
|
|
953
|
+
|
|
954
|
+
return result;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
async findAll(options?: { page?: number; limit?: number }): Promise<${className}ResponseDto[]> {
|
|
958
|
+
const { page = 1, limit = 10 } = options || {};
|
|
959
|
+
const offset = (page - 1) * limit;
|
|
960
|
+
|
|
961
|
+
return this.db
|
|
962
|
+
.select()
|
|
963
|
+
.from(${table.variableName})
|
|
964
|
+
.limit(limit)
|
|
965
|
+
.offset(offset);
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
async findOne(${pkName}: ${pkType}): Promise<${className}ResponseDto> {
|
|
969
|
+
const [result] = await this.db
|
|
970
|
+
.select()
|
|
971
|
+
.from(${table.variableName})
|
|
972
|
+
.where(eq(${table.variableName}.${pkName}, ${pkName}))
|
|
973
|
+
.limit(1);
|
|
974
|
+
|
|
975
|
+
if (!result) {
|
|
976
|
+
throw new NotFoundException(\`${className} with ${pkName} \${${pkName}} not found\`);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
return result;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
async update(${pkName}: ${pkType}, updateDto: Update${className}Dto): Promise<${className}ResponseDto> {
|
|
983
|
+
const [result] = await this.db
|
|
984
|
+
.update(${table.variableName})
|
|
985
|
+
.set(updateDto)
|
|
986
|
+
.where(eq(${table.variableName}.${pkName}, ${pkName}))
|
|
987
|
+
.returning();
|
|
988
|
+
|
|
989
|
+
if (!result) {
|
|
990
|
+
throw new NotFoundException(\`${className} with ${pkName} \${${pkName}} not found\`);
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
return result;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
async remove(${pkName}: ${pkType}): Promise<void> {
|
|
997
|
+
const result = await this.db
|
|
998
|
+
.delete(${table.variableName})
|
|
999
|
+
.where(eq(${table.variableName}.${pkName}, ${pkName}))
|
|
1000
|
+
.returning();
|
|
1001
|
+
|
|
1002
|
+
if (result.length === 0) {
|
|
1003
|
+
throw new NotFoundException(\`${className} with ${pkName} \${${pkName}} not found\`);
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
this.logger.log(\`Deleted ${className} with ${pkName} \${${pkName}}\`);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
`;
|
|
1010
|
+
return service;
|
|
1011
|
+
}
|
|
1012
|
+
function generateModule(table) {
|
|
1013
|
+
const className = toPascalCase(table.variableName);
|
|
1014
|
+
const filePath = toSnakeCase(table.variableName);
|
|
1015
|
+
const module = `
|
|
1016
|
+
import { Module } from '@nestjs/common';
|
|
1017
|
+
import { ${className}Controller } from './${filePath}.controller';
|
|
1018
|
+
import { ${className}Service } from './${filePath}.service';
|
|
1019
|
+
|
|
1020
|
+
@Module({
|
|
1021
|
+
controllers: [${className}Controller],
|
|
1022
|
+
providers: [${className}Service],
|
|
1023
|
+
})
|
|
1024
|
+
export class ${className}Module {}
|
|
1025
|
+
`;
|
|
1026
|
+
return module;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
// src/commands/db/gen-nest-resource/schema-parser.ts
|
|
1030
|
+
import { Project, Node } from "ts-morph";
|
|
1031
|
+
var DrizzleSchemaParser = class {
|
|
1032
|
+
constructor(projectOptions) {
|
|
1033
|
+
this.project = new Project(projectOptions);
|
|
1034
|
+
}
|
|
1035
|
+
parseSchemaFile(filePath) {
|
|
1036
|
+
const sourceFile = this.project.addSourceFileAtPath(filePath);
|
|
1037
|
+
const tables = [];
|
|
1038
|
+
const variableStatements = sourceFile.getVariableStatements();
|
|
1039
|
+
for (const statement of variableStatements) {
|
|
1040
|
+
const declarations = statement.getDeclarations();
|
|
1041
|
+
for (const declaration of declarations) {
|
|
1042
|
+
const initializer = declaration.getInitializer();
|
|
1043
|
+
if (initializer && Node.isCallExpression(initializer)) {
|
|
1044
|
+
const expression = initializer.getExpression();
|
|
1045
|
+
if (expression.getText() === "pgTable") {
|
|
1046
|
+
const tableInfo = this.parsePgTable(
|
|
1047
|
+
declaration.getName(),
|
|
1048
|
+
initializer
|
|
1049
|
+
);
|
|
1050
|
+
if (tableInfo) {
|
|
1051
|
+
tables.push(tableInfo);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
return tables;
|
|
1058
|
+
}
|
|
1059
|
+
parsePgTable(variableName, callExpr) {
|
|
1060
|
+
const args = callExpr.getArguments();
|
|
1061
|
+
if (args.length < 2) {
|
|
1062
|
+
return null;
|
|
1063
|
+
}
|
|
1064
|
+
const tableName = args[0].getText().replace(/['"]/g, "");
|
|
1065
|
+
const fieldsArg = args[1];
|
|
1066
|
+
if (!Node.isObjectLiteralExpression(fieldsArg)) {
|
|
1067
|
+
return null;
|
|
1068
|
+
}
|
|
1069
|
+
const fields = [];
|
|
1070
|
+
const properties = fieldsArg.getProperties();
|
|
1071
|
+
for (const prop of properties) {
|
|
1072
|
+
if (Node.isPropertyAssignment(prop)) {
|
|
1073
|
+
const fieldName = prop.getName();
|
|
1074
|
+
const initializer = prop.getInitializer();
|
|
1075
|
+
const leadingComments = prop.getLeadingCommentRanges();
|
|
1076
|
+
let comment;
|
|
1077
|
+
if (leadingComments.length > 0) {
|
|
1078
|
+
comment = leadingComments.map((c) => c.getText()).join("\n").replace(/\/\//g, "").trim();
|
|
1079
|
+
}
|
|
1080
|
+
if (initializer && Node.isCallExpression(initializer)) {
|
|
1081
|
+
const fieldInfo = this.parseField(fieldName, initializer, comment);
|
|
1082
|
+
fields.push(fieldInfo);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
return {
|
|
1087
|
+
tableName,
|
|
1088
|
+
variableName,
|
|
1089
|
+
fields
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
parseField(fieldName, callExpr, comment) {
|
|
1093
|
+
const fieldInfo = {
|
|
1094
|
+
name: fieldName,
|
|
1095
|
+
columnName: fieldName,
|
|
1096
|
+
type: "",
|
|
1097
|
+
nullable: true,
|
|
1098
|
+
hasDefault: false,
|
|
1099
|
+
notNull: false,
|
|
1100
|
+
isPrimaryKey: false,
|
|
1101
|
+
isUnique: false,
|
|
1102
|
+
isArray: false,
|
|
1103
|
+
comment
|
|
1104
|
+
};
|
|
1105
|
+
this.parseBaseType(callExpr, fieldInfo);
|
|
1106
|
+
this.parseCallChain(callExpr, fieldInfo);
|
|
1107
|
+
return fieldInfo;
|
|
1108
|
+
}
|
|
1109
|
+
parseBaseType(callExpr, fieldInfo) {
|
|
1110
|
+
let current = callExpr;
|
|
1111
|
+
let baseCall = null;
|
|
1112
|
+
while (Node.isCallExpression(current)) {
|
|
1113
|
+
baseCall = current;
|
|
1114
|
+
const expression2 = current.getExpression();
|
|
1115
|
+
if (Node.isPropertyAccessExpression(expression2)) {
|
|
1116
|
+
current = expression2.getExpression();
|
|
1117
|
+
} else {
|
|
1118
|
+
break;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
if (!baseCall) {
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
const expression = baseCall.getExpression();
|
|
1125
|
+
let typeName = "";
|
|
1126
|
+
if (Node.isPropertyAccessExpression(expression)) {
|
|
1127
|
+
typeName = expression.getName();
|
|
1128
|
+
} else {
|
|
1129
|
+
typeName = expression.getText();
|
|
1130
|
+
}
|
|
1131
|
+
fieldInfo.type = typeName;
|
|
1132
|
+
const args = baseCall.getArguments();
|
|
1133
|
+
if (args.length > 0) {
|
|
1134
|
+
const firstArg = args[0];
|
|
1135
|
+
if (Node.isStringLiteral(firstArg)) {
|
|
1136
|
+
fieldInfo.columnName = firstArg.getLiteralText();
|
|
1137
|
+
} else if (Node.isObjectLiteralExpression(firstArg)) {
|
|
1138
|
+
this.parseTypeConfig(firstArg, fieldInfo);
|
|
1139
|
+
} else if (Node.isArrayLiteralExpression(firstArg)) {
|
|
1140
|
+
fieldInfo.enumValues = firstArg.getElements().map((el) => el.getText().replace(/['"]/g, ""));
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
if (args.length > 1 && Node.isObjectLiteralExpression(args[1])) {
|
|
1144
|
+
this.parseTypeConfig(args[1], fieldInfo);
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
parseTypeConfig(objLiteral, fieldInfo) {
|
|
1148
|
+
if (!Node.isObjectLiteralExpression(objLiteral)) {
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
const properties = objLiteral.getProperties();
|
|
1152
|
+
for (const prop of properties) {
|
|
1153
|
+
if (Node.isPropertyAssignment(prop)) {
|
|
1154
|
+
const propName = prop.getName();
|
|
1155
|
+
const value = prop.getInitializer()?.getText();
|
|
1156
|
+
switch (propName) {
|
|
1157
|
+
case "length":
|
|
1158
|
+
fieldInfo.length = value ? parseInt(value) : void 0;
|
|
1159
|
+
break;
|
|
1160
|
+
case "precision":
|
|
1161
|
+
fieldInfo.precision = value ? parseInt(value) : void 0;
|
|
1162
|
+
break;
|
|
1163
|
+
case "scale":
|
|
1164
|
+
fieldInfo.scale = value ? parseInt(value) : void 0;
|
|
1165
|
+
break;
|
|
1166
|
+
case "default":
|
|
1167
|
+
fieldInfo.hasDefault = true;
|
|
1168
|
+
fieldInfo.defaultValue = value;
|
|
1169
|
+
break;
|
|
1170
|
+
// 时间精度(用于 timestamp, time 等)
|
|
1171
|
+
case "withTimezone":
|
|
1172
|
+
fieldInfo.withTimezone = value === "true";
|
|
1173
|
+
break;
|
|
1174
|
+
case "mode":
|
|
1175
|
+
fieldInfo.mode = value?.replace(/['"]/g, "");
|
|
1176
|
+
break;
|
|
1177
|
+
default:
|
|
1178
|
+
throw new Error(`Unsupported property: ${propName}`);
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
parseCallChain(callExpr, fieldInfo) {
|
|
1184
|
+
let current = callExpr;
|
|
1185
|
+
while (Node.isCallExpression(current)) {
|
|
1186
|
+
const expression = current.getExpression();
|
|
1187
|
+
if (Node.isPropertyAccessExpression(expression)) {
|
|
1188
|
+
const methodName = expression.getName();
|
|
1189
|
+
const args = current.getArguments();
|
|
1190
|
+
switch (methodName) {
|
|
1191
|
+
case "notNull":
|
|
1192
|
+
fieldInfo.notNull = true;
|
|
1193
|
+
fieldInfo.nullable = false;
|
|
1194
|
+
break;
|
|
1195
|
+
case "default":
|
|
1196
|
+
fieldInfo.hasDefault = true;
|
|
1197
|
+
if (args.length > 0) {
|
|
1198
|
+
fieldInfo.defaultValue = args[0].getText();
|
|
1199
|
+
}
|
|
1200
|
+
break;
|
|
1201
|
+
case "defaultRandom":
|
|
1202
|
+
fieldInfo.hasDefault = true;
|
|
1203
|
+
fieldInfo.defaultValue = "random";
|
|
1204
|
+
break;
|
|
1205
|
+
case "primaryKey":
|
|
1206
|
+
fieldInfo.isPrimaryKey = true;
|
|
1207
|
+
fieldInfo.notNull = true;
|
|
1208
|
+
fieldInfo.nullable = false;
|
|
1209
|
+
break;
|
|
1210
|
+
case "unique":
|
|
1211
|
+
fieldInfo.isUnique = true;
|
|
1212
|
+
break;
|
|
1213
|
+
case "array":
|
|
1214
|
+
fieldInfo.isArray = true;
|
|
1215
|
+
break;
|
|
1216
|
+
case "references":
|
|
1217
|
+
if (args.length > 0) {
|
|
1218
|
+
const refArg = args[0].getText();
|
|
1219
|
+
const match = refArg.match(/=>\s*(\w+)\.(\w+)/);
|
|
1220
|
+
if (match) {
|
|
1221
|
+
fieldInfo.references = {
|
|
1222
|
+
table: match[1],
|
|
1223
|
+
column: match[2]
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
break;
|
|
1228
|
+
default:
|
|
1229
|
+
throw new Error(`Unsupported method: ${methodName}`);
|
|
1230
|
+
}
|
|
1231
|
+
current = expression.getExpression();
|
|
1232
|
+
} else {
|
|
1233
|
+
break;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
};
|
|
1238
|
+
|
|
1239
|
+
// src/commands/db/gen-nest-resource/index.ts
|
|
1240
|
+
import { join } from "path";
|
|
1241
|
+
import { mkdir, rm, writeFile } from "fs/promises";
|
|
1242
|
+
import { existsSync } from "fs";
|
|
1243
|
+
async function parseAndGenerateNestResourceTemplate(options) {
|
|
1244
|
+
const parser = new DrizzleSchemaParser({
|
|
1245
|
+
tsConfigFilePath: options.tsConfigFilePath
|
|
1246
|
+
});
|
|
1247
|
+
const tables = parser.parseSchemaFile(options.schemaFilePath);
|
|
1248
|
+
if (tables.length === 0) {
|
|
1249
|
+
console.warn("\u672A\u89E3\u6790\u5230\u4EFB\u4F55\u6570\u636E\u5E93\u8868\uFF0C\u65E0\u9700\u751F\u6210 Nest.js \u6A21\u5757\u6A21\u677F");
|
|
1250
|
+
return;
|
|
1251
|
+
}
|
|
1252
|
+
tables.sort((a, b) => b.variableName.length - a.variableName.length);
|
|
1253
|
+
const table = tables[0];
|
|
1254
|
+
console.info(`\u751F\u6210 Nest.js ${table.variableName} \u6A21\u5757`);
|
|
1255
|
+
const filePath = toSnakeCase(table.variableName);
|
|
1256
|
+
const moduleDir = join(options.moduleOutputDir, filePath);
|
|
1257
|
+
if (existsSync(moduleDir)) {
|
|
1258
|
+
console.info(`Nest.js \u6A21\u5757 ${filePath} \u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u751F\u6210\u4EE3\u7801`);
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
const dto = generateDTO(table);
|
|
1262
|
+
const controller = generateController(table);
|
|
1263
|
+
const service = generateService(table);
|
|
1264
|
+
const moduleFilePath = join(moduleDir, `${filePath}.module.ts`);
|
|
1265
|
+
const module = generateModule(table);
|
|
1266
|
+
try {
|
|
1267
|
+
await mkdir(moduleDir, { recursive: true });
|
|
1268
|
+
await mkdir(join(moduleDir, "dtos"), { recursive: true });
|
|
1269
|
+
await writeFile(join(moduleDir, "dtos", `${filePath}.dto.ts`), dto);
|
|
1270
|
+
await writeFile(join(moduleDir, `${filePath}.controller.ts`), controller);
|
|
1271
|
+
await writeFile(join(moduleDir, `${filePath}.service.ts`), service);
|
|
1272
|
+
await writeFile(moduleFilePath, module);
|
|
1273
|
+
} catch (err) {
|
|
1274
|
+
console.error(`\u751F\u6210 Nest.js ${filePath} \u6A21\u5757\u5931\u8D25: ${err.message}`);
|
|
1275
|
+
await rm(moduleDir, { recursive: true });
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
// src/commands/db/schema.handler.ts
|
|
125
1280
|
var require2 = createRequire(import.meta.url);
|
|
126
1281
|
async function run(options = {}) {
|
|
127
1282
|
let exitCode = 0;
|
|
128
|
-
const envPath2 =
|
|
129
|
-
if (
|
|
1283
|
+
const envPath2 = path3.resolve(process.cwd(), ".env");
|
|
1284
|
+
if (fs3.existsSync(envPath2)) {
|
|
130
1285
|
loadEnv({ path: envPath2 });
|
|
131
1286
|
console.log("[gen-db-schema] \u2713 Loaded .env file");
|
|
132
1287
|
}
|
|
@@ -136,17 +1291,17 @@ async function run(options = {}) {
|
|
|
136
1291
|
process.exit(1);
|
|
137
1292
|
}
|
|
138
1293
|
const outputPath = options.output || process.env.DB_SCHEMA_OUTPUT || "server/database/schema.ts";
|
|
139
|
-
const OUT_DIR =
|
|
140
|
-
const SCHEMA_FILE =
|
|
1294
|
+
const OUT_DIR = path3.resolve(process.cwd(), "server/database/.introspect");
|
|
1295
|
+
const SCHEMA_FILE = path3.resolve(process.cwd(), outputPath);
|
|
141
1296
|
console.log("[gen-db-schema] Starting...");
|
|
142
1297
|
const __filename = fileURLToPath(import.meta.url);
|
|
143
|
-
const
|
|
1298
|
+
const __dirname3 = path3.dirname(__filename);
|
|
144
1299
|
const configPathCandidates = [
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
1300
|
+
path3.resolve(__dirname3, "config/drizzle.config.js"),
|
|
1301
|
+
path3.resolve(__dirname3, "../../config/drizzle.config.js"),
|
|
1302
|
+
path3.resolve(__dirname3, "../../../dist/config/drizzle.config.js")
|
|
148
1303
|
];
|
|
149
|
-
const configPath = configPathCandidates.find((p) =>
|
|
1304
|
+
const configPath = configPathCandidates.find((p) => fs3.existsSync(p));
|
|
150
1305
|
console.log("[gen-db-schema] Using drizzle config from:", configPath ?? "(not found)");
|
|
151
1306
|
if (!configPath) {
|
|
152
1307
|
console.error("[gen-db-schema] Error: drizzle config not found in CLI package");
|
|
@@ -154,12 +1309,12 @@ async function run(options = {}) {
|
|
|
154
1309
|
}
|
|
155
1310
|
const resolveDrizzleKitBin = () => {
|
|
156
1311
|
const entryPath = require2.resolve("drizzle-kit");
|
|
157
|
-
let currentDir =
|
|
1312
|
+
let currentDir = path3.dirname(entryPath);
|
|
158
1313
|
let lastDir = null;
|
|
159
1314
|
while (currentDir !== lastDir) {
|
|
160
|
-
const pkgJsonPath =
|
|
161
|
-
if (
|
|
162
|
-
const pkgJsonRaw =
|
|
1315
|
+
const pkgJsonPath = path3.join(currentDir, "package.json");
|
|
1316
|
+
if (fs3.existsSync(pkgJsonPath)) {
|
|
1317
|
+
const pkgJsonRaw = fs3.readFileSync(pkgJsonPath, "utf8");
|
|
163
1318
|
const pkgJson = JSON.parse(pkgJsonRaw);
|
|
164
1319
|
if (pkgJson.name === "drizzle-kit") {
|
|
165
1320
|
const binField = pkgJson.bin;
|
|
@@ -167,11 +1322,11 @@ async function run(options = {}) {
|
|
|
167
1322
|
if (!binRelPath) {
|
|
168
1323
|
throw new Error("Unable to resolve drizzle-kit binary from package.json");
|
|
169
1324
|
}
|
|
170
|
-
return
|
|
1325
|
+
return path3.resolve(currentDir, binRelPath);
|
|
171
1326
|
}
|
|
172
1327
|
}
|
|
173
1328
|
lastDir = currentDir;
|
|
174
|
-
currentDir =
|
|
1329
|
+
currentDir = path3.dirname(currentDir);
|
|
175
1330
|
}
|
|
176
1331
|
throw new Error("Unable to locate drizzle-kit package root");
|
|
177
1332
|
};
|
|
@@ -193,29 +1348,27 @@ async function run(options = {}) {
|
|
|
193
1348
|
if ((result.status ?? 0) !== 0) {
|
|
194
1349
|
throw new Error(`drizzle-kit introspect failed with status ${result.status}`);
|
|
195
1350
|
}
|
|
196
|
-
const generatedSchema =
|
|
197
|
-
if (!
|
|
1351
|
+
const generatedSchema = path3.join(OUT_DIR, "schema.ts");
|
|
1352
|
+
if (!fs3.existsSync(generatedSchema)) {
|
|
198
1353
|
console.error("[gen-db-schema] schema.ts not generated");
|
|
199
1354
|
throw new Error("drizzle-kit introspect failed to generate schema.ts");
|
|
200
1355
|
}
|
|
201
|
-
const { postprocessDrizzleSchema } = require2("./gen-dbschema");
|
|
202
1356
|
const stats = postprocessDrizzleSchema(generatedSchema);
|
|
203
1357
|
if (stats?.unmatchedUnknown?.length) {
|
|
204
1358
|
console.warn("[gen-db-schema] Unmatched custom types detected:", stats.unmatchedUnknown);
|
|
205
1359
|
}
|
|
206
1360
|
console.log("[gen-db-schema] \u2713 Postprocessed schema");
|
|
207
|
-
|
|
208
|
-
|
|
1361
|
+
fs3.mkdirSync(path3.dirname(SCHEMA_FILE), { recursive: true });
|
|
1362
|
+
fs3.copyFileSync(generatedSchema, SCHEMA_FILE);
|
|
209
1363
|
console.log(`[gen-db-schema] \u2713 Copied to ${outputPath}`);
|
|
210
1364
|
try {
|
|
211
1365
|
if (options.enableNestModuleGenerate) {
|
|
212
|
-
const
|
|
213
|
-
const tsConfigFilePath = path.resolve(process.cwd(), "tsconfig.json");
|
|
1366
|
+
const tsConfigFilePath = path3.resolve(process.cwd(), "tsconfig.json");
|
|
214
1367
|
const schemaFilePath = SCHEMA_FILE;
|
|
215
1368
|
await parseAndGenerateNestResourceTemplate({
|
|
216
1369
|
tsConfigFilePath,
|
|
217
1370
|
schemaFilePath,
|
|
218
|
-
moduleOutputDir:
|
|
1371
|
+
moduleOutputDir: path3.resolve(process.cwd(), "server/modules")
|
|
219
1372
|
});
|
|
220
1373
|
console.log("[gen-db-schema] \u2713 Generate NestJS Module Boilerplate Successfully");
|
|
221
1374
|
}
|
|
@@ -227,8 +1380,8 @@ async function run(options = {}) {
|
|
|
227
1380
|
console.error("[gen-db-schema] Failed:", err instanceof Error ? err.message : String(err));
|
|
228
1381
|
exitCode = 1;
|
|
229
1382
|
} finally {
|
|
230
|
-
if (
|
|
231
|
-
|
|
1383
|
+
if (fs3.existsSync(OUT_DIR)) {
|
|
1384
|
+
fs3.rmSync(OUT_DIR, { recursive: true, force: true });
|
|
232
1385
|
}
|
|
233
1386
|
process.exit(exitCode);
|
|
234
1387
|
}
|
|
@@ -246,8 +1399,8 @@ var genDbSchemaCommand = {
|
|
|
246
1399
|
};
|
|
247
1400
|
|
|
248
1401
|
// src/commands/sync/run.handler.ts
|
|
249
|
-
import
|
|
250
|
-
import
|
|
1402
|
+
import path5 from "path";
|
|
1403
|
+
import fs5 from "fs";
|
|
251
1404
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
252
1405
|
|
|
253
1406
|
// src/config/sync.ts
|
|
@@ -311,23 +1464,23 @@ function genSyncConfig(perms = {}) {
|
|
|
311
1464
|
}
|
|
312
1465
|
|
|
313
1466
|
// src/utils/file-ops.ts
|
|
314
|
-
import
|
|
315
|
-
import
|
|
1467
|
+
import fs4 from "fs";
|
|
1468
|
+
import path4 from "path";
|
|
316
1469
|
function removeLineFromFile(filePath, pattern) {
|
|
317
|
-
if (!
|
|
318
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
1470
|
+
if (!fs4.existsSync(filePath)) {
|
|
1471
|
+
console.log(`[fullstack-cli] \u25CB ${path4.basename(filePath)} (not found)`);
|
|
319
1472
|
return false;
|
|
320
1473
|
}
|
|
321
|
-
const content =
|
|
1474
|
+
const content = fs4.readFileSync(filePath, "utf-8");
|
|
322
1475
|
const lines = content.split("\n");
|
|
323
1476
|
const trimmedPattern = pattern.trim();
|
|
324
1477
|
const filteredLines = lines.filter((line) => line.trim() !== trimmedPattern);
|
|
325
1478
|
if (filteredLines.length === lines.length) {
|
|
326
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
1479
|
+
console.log(`[fullstack-cli] \u25CB ${path4.basename(filePath)} (pattern not found: ${pattern})`);
|
|
327
1480
|
return false;
|
|
328
1481
|
}
|
|
329
|
-
|
|
330
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
1482
|
+
fs4.writeFileSync(filePath, filteredLines.join("\n"));
|
|
1483
|
+
console.log(`[fullstack-cli] \u2713 ${path4.basename(filePath)} (removed: ${pattern})`);
|
|
331
1484
|
return true;
|
|
332
1485
|
}
|
|
333
1486
|
|
|
@@ -335,14 +1488,14 @@ function removeLineFromFile(filePath, pattern) {
|
|
|
335
1488
|
async function run2(options) {
|
|
336
1489
|
const userProjectRoot = process.env.INIT_CWD || process.cwd();
|
|
337
1490
|
const __filename = fileURLToPath2(import.meta.url);
|
|
338
|
-
const
|
|
339
|
-
const pluginRoot =
|
|
1491
|
+
const __dirname3 = path5.dirname(__filename);
|
|
1492
|
+
const pluginRoot = path5.resolve(__dirname3, "..");
|
|
340
1493
|
if (userProjectRoot === pluginRoot) {
|
|
341
1494
|
console.log("[fullstack-cli] Skip syncing (installing plugin itself)");
|
|
342
1495
|
process.exit(0);
|
|
343
1496
|
}
|
|
344
|
-
const userPackageJson =
|
|
345
|
-
if (!
|
|
1497
|
+
const userPackageJson = path5.join(userProjectRoot, "package.json");
|
|
1498
|
+
if (!fs5.existsSync(userPackageJson)) {
|
|
346
1499
|
console.log("[fullstack-cli] Skip syncing (not a valid npm project)");
|
|
347
1500
|
process.exit(0);
|
|
348
1501
|
}
|
|
@@ -370,7 +1523,7 @@ async function run2(options) {
|
|
|
370
1523
|
}
|
|
371
1524
|
async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
372
1525
|
if (rule.type === "delete-file" || rule.type === "delete-directory") {
|
|
373
|
-
const destPath2 =
|
|
1526
|
+
const destPath2 = path5.join(userProjectRoot, rule.to);
|
|
374
1527
|
if (rule.type === "delete-file") {
|
|
375
1528
|
deleteFile(destPath2);
|
|
376
1529
|
} else {
|
|
@@ -379,16 +1532,16 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
|
379
1532
|
return;
|
|
380
1533
|
}
|
|
381
1534
|
if (rule.type === "remove-line") {
|
|
382
|
-
const destPath2 =
|
|
1535
|
+
const destPath2 = path5.join(userProjectRoot, rule.to);
|
|
383
1536
|
removeLineFromFile(destPath2, rule.pattern);
|
|
384
1537
|
return;
|
|
385
1538
|
}
|
|
386
1539
|
if (!("from" in rule)) {
|
|
387
1540
|
return;
|
|
388
1541
|
}
|
|
389
|
-
const srcPath =
|
|
390
|
-
const destPath =
|
|
391
|
-
if (!
|
|
1542
|
+
const srcPath = path5.join(pluginRoot, rule.from);
|
|
1543
|
+
const destPath = path5.join(userProjectRoot, rule.to);
|
|
1544
|
+
if (!fs5.existsSync(srcPath)) {
|
|
392
1545
|
console.warn(`[fullstack-cli] Source not found: ${rule.from}`);
|
|
393
1546
|
return;
|
|
394
1547
|
}
|
|
@@ -405,64 +1558,64 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
|
405
1558
|
}
|
|
406
1559
|
}
|
|
407
1560
|
function syncFile(src, dest, overwrite = true) {
|
|
408
|
-
const destDir =
|
|
409
|
-
if (!
|
|
410
|
-
|
|
1561
|
+
const destDir = path5.dirname(dest);
|
|
1562
|
+
if (!fs5.existsSync(destDir)) {
|
|
1563
|
+
fs5.mkdirSync(destDir, { recursive: true });
|
|
411
1564
|
}
|
|
412
|
-
if (
|
|
413
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
1565
|
+
if (fs5.existsSync(dest) && !overwrite) {
|
|
1566
|
+
console.log(`[fullstack-cli] \u25CB ${path5.basename(dest)} (skipped, already exists)`);
|
|
414
1567
|
return;
|
|
415
1568
|
}
|
|
416
|
-
|
|
417
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
1569
|
+
fs5.copyFileSync(src, dest);
|
|
1570
|
+
console.log(`[fullstack-cli] \u2713 ${path5.basename(dest)}`);
|
|
418
1571
|
}
|
|
419
1572
|
function syncDirectory(src, dest, overwrite = true) {
|
|
420
|
-
if (!
|
|
421
|
-
|
|
1573
|
+
if (!fs5.existsSync(dest)) {
|
|
1574
|
+
fs5.mkdirSync(dest, { recursive: true });
|
|
422
1575
|
}
|
|
423
|
-
const files =
|
|
1576
|
+
const files = fs5.readdirSync(src);
|
|
424
1577
|
let count = 0;
|
|
425
1578
|
files.forEach((file) => {
|
|
426
|
-
const srcFile =
|
|
427
|
-
const destFile =
|
|
428
|
-
const stats =
|
|
1579
|
+
const srcFile = path5.join(src, file);
|
|
1580
|
+
const destFile = path5.join(dest, file);
|
|
1581
|
+
const stats = fs5.statSync(srcFile);
|
|
429
1582
|
if (stats.isDirectory()) {
|
|
430
1583
|
syncDirectory(srcFile, destFile, overwrite);
|
|
431
1584
|
} else {
|
|
432
|
-
if (overwrite || !
|
|
433
|
-
|
|
434
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
1585
|
+
if (overwrite || !fs5.existsSync(destFile)) {
|
|
1586
|
+
fs5.copyFileSync(srcFile, destFile);
|
|
1587
|
+
console.log(`[fullstack-cli] \u2713 ${path5.relative(dest, destFile)}`);
|
|
435
1588
|
count++;
|
|
436
1589
|
}
|
|
437
1590
|
}
|
|
438
1591
|
});
|
|
439
1592
|
if (count > 0) {
|
|
440
|
-
console.log(`[fullstack-cli] Synced ${count} files to ${
|
|
1593
|
+
console.log(`[fullstack-cli] Synced ${count} files to ${path5.basename(dest)}/`);
|
|
441
1594
|
}
|
|
442
1595
|
}
|
|
443
1596
|
function appendToFile(src, dest) {
|
|
444
|
-
const content =
|
|
1597
|
+
const content = fs5.readFileSync(src, "utf-8");
|
|
445
1598
|
let existingContent = "";
|
|
446
|
-
if (
|
|
447
|
-
existingContent =
|
|
1599
|
+
if (fs5.existsSync(dest)) {
|
|
1600
|
+
existingContent = fs5.readFileSync(dest, "utf-8");
|
|
448
1601
|
}
|
|
449
1602
|
if (existingContent.includes(content.trim())) {
|
|
450
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
1603
|
+
console.log(`[fullstack-cli] \u25CB ${path5.basename(dest)} (already contains content)`);
|
|
451
1604
|
return;
|
|
452
1605
|
}
|
|
453
|
-
|
|
454
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
1606
|
+
fs5.appendFileSync(dest, content);
|
|
1607
|
+
console.log(`[fullstack-cli] \u2713 ${path5.basename(dest)} (appended)`);
|
|
455
1608
|
}
|
|
456
1609
|
function setPermissions(permissions, projectRoot) {
|
|
457
1610
|
for (const [pattern, mode] of Object.entries(permissions)) {
|
|
458
1611
|
if (pattern === "**/*.sh") {
|
|
459
|
-
const scriptsDir =
|
|
460
|
-
if (
|
|
461
|
-
const files =
|
|
1612
|
+
const scriptsDir = path5.join(projectRoot, "scripts");
|
|
1613
|
+
if (fs5.existsSync(scriptsDir)) {
|
|
1614
|
+
const files = fs5.readdirSync(scriptsDir);
|
|
462
1615
|
files.forEach((file) => {
|
|
463
1616
|
if (file.endsWith(".sh")) {
|
|
464
|
-
const filePath =
|
|
465
|
-
|
|
1617
|
+
const filePath = path5.join(scriptsDir, file);
|
|
1618
|
+
fs5.chmodSync(filePath, mode);
|
|
466
1619
|
}
|
|
467
1620
|
});
|
|
468
1621
|
}
|
|
@@ -470,19 +1623,19 @@ function setPermissions(permissions, projectRoot) {
|
|
|
470
1623
|
}
|
|
471
1624
|
}
|
|
472
1625
|
function deleteFile(filePath) {
|
|
473
|
-
if (
|
|
474
|
-
|
|
475
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
1626
|
+
if (fs5.existsSync(filePath)) {
|
|
1627
|
+
fs5.unlinkSync(filePath);
|
|
1628
|
+
console.log(`[fullstack-cli] \u2713 ${path5.basename(filePath)} (deleted)`);
|
|
476
1629
|
} else {
|
|
477
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
1630
|
+
console.log(`[fullstack-cli] \u25CB ${path5.basename(filePath)} (not found)`);
|
|
478
1631
|
}
|
|
479
1632
|
}
|
|
480
1633
|
function deleteDirectory(dirPath) {
|
|
481
|
-
if (
|
|
482
|
-
|
|
483
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
1634
|
+
if (fs5.existsSync(dirPath)) {
|
|
1635
|
+
fs5.rmSync(dirPath, { recursive: true });
|
|
1636
|
+
console.log(`[fullstack-cli] \u2713 ${path5.basename(dirPath)} (deleted)`);
|
|
484
1637
|
} else {
|
|
485
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
1638
|
+
console.log(`[fullstack-cli] \u25CB ${path5.basename(dirPath)} (not found)`);
|
|
486
1639
|
}
|
|
487
1640
|
}
|
|
488
1641
|
|
|
@@ -498,8 +1651,8 @@ var syncCommand = {
|
|
|
498
1651
|
};
|
|
499
1652
|
|
|
500
1653
|
// src/commands/action-plugin/utils.ts
|
|
501
|
-
import
|
|
502
|
-
import
|
|
1654
|
+
import fs6 from "fs";
|
|
1655
|
+
import path6 from "path";
|
|
503
1656
|
import { spawnSync as spawnSync2, execSync } from "child_process";
|
|
504
1657
|
function parsePluginName(input) {
|
|
505
1658
|
const match = input.match(/^(@[^/]+\/[^@]+)(?:@(.+))?$/);
|
|
@@ -517,18 +1670,18 @@ function getProjectRoot() {
|
|
|
517
1670
|
return process.cwd();
|
|
518
1671
|
}
|
|
519
1672
|
function getPackageJsonPath() {
|
|
520
|
-
return
|
|
1673
|
+
return path6.join(getProjectRoot(), "package.json");
|
|
521
1674
|
}
|
|
522
1675
|
function getPluginPath(pluginName) {
|
|
523
|
-
return
|
|
1676
|
+
return path6.join(getProjectRoot(), "node_modules", pluginName);
|
|
524
1677
|
}
|
|
525
1678
|
function readPackageJson() {
|
|
526
1679
|
const pkgPath = getPackageJsonPath();
|
|
527
|
-
if (!
|
|
1680
|
+
if (!fs6.existsSync(pkgPath)) {
|
|
528
1681
|
throw new Error("package.json not found in current directory");
|
|
529
1682
|
}
|
|
530
1683
|
try {
|
|
531
|
-
const content =
|
|
1684
|
+
const content = fs6.readFileSync(pkgPath, "utf-8");
|
|
532
1685
|
return JSON.parse(content);
|
|
533
1686
|
} catch {
|
|
534
1687
|
throw new Error("Failed to parse package.json");
|
|
@@ -536,7 +1689,7 @@ function readPackageJson() {
|
|
|
536
1689
|
}
|
|
537
1690
|
function writePackageJson(pkg2) {
|
|
538
1691
|
const pkgPath = getPackageJsonPath();
|
|
539
|
-
|
|
1692
|
+
fs6.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
|
|
540
1693
|
}
|
|
541
1694
|
function readActionPlugins() {
|
|
542
1695
|
const pkg2 = readPackageJson();
|
|
@@ -569,12 +1722,12 @@ function npmInstall(tgzPath) {
|
|
|
569
1722
|
}
|
|
570
1723
|
}
|
|
571
1724
|
function getPackageVersion(pluginName) {
|
|
572
|
-
const pkgJsonPath =
|
|
573
|
-
if (!
|
|
1725
|
+
const pkgJsonPath = path6.join(getPluginPath(pluginName), "package.json");
|
|
1726
|
+
if (!fs6.existsSync(pkgJsonPath)) {
|
|
574
1727
|
return null;
|
|
575
1728
|
}
|
|
576
1729
|
try {
|
|
577
|
-
const content =
|
|
1730
|
+
const content = fs6.readFileSync(pkgJsonPath, "utf-8");
|
|
578
1731
|
const pkg2 = JSON.parse(content);
|
|
579
1732
|
return pkg2.version || null;
|
|
580
1733
|
} catch {
|
|
@@ -582,49 +1735,49 @@ function getPackageVersion(pluginName) {
|
|
|
582
1735
|
}
|
|
583
1736
|
}
|
|
584
1737
|
function readPluginPackageJson(pluginPath) {
|
|
585
|
-
const pkgJsonPath =
|
|
586
|
-
if (!
|
|
1738
|
+
const pkgJsonPath = path6.join(pluginPath, "package.json");
|
|
1739
|
+
if (!fs6.existsSync(pkgJsonPath)) {
|
|
587
1740
|
return null;
|
|
588
1741
|
}
|
|
589
1742
|
try {
|
|
590
|
-
const content =
|
|
1743
|
+
const content = fs6.readFileSync(pkgJsonPath, "utf-8");
|
|
591
1744
|
return JSON.parse(content);
|
|
592
1745
|
} catch {
|
|
593
1746
|
return null;
|
|
594
1747
|
}
|
|
595
1748
|
}
|
|
596
1749
|
function extractTgzToNodeModules(tgzPath, pluginName) {
|
|
597
|
-
const nodeModulesPath =
|
|
598
|
-
const targetDir =
|
|
599
|
-
const scopeDir =
|
|
600
|
-
if (!
|
|
601
|
-
|
|
1750
|
+
const nodeModulesPath = path6.join(getProjectRoot(), "node_modules");
|
|
1751
|
+
const targetDir = path6.join(nodeModulesPath, pluginName);
|
|
1752
|
+
const scopeDir = path6.dirname(targetDir);
|
|
1753
|
+
if (!fs6.existsSync(scopeDir)) {
|
|
1754
|
+
fs6.mkdirSync(scopeDir, { recursive: true });
|
|
602
1755
|
}
|
|
603
|
-
if (
|
|
604
|
-
|
|
1756
|
+
if (fs6.existsSync(targetDir)) {
|
|
1757
|
+
fs6.rmSync(targetDir, { recursive: true });
|
|
605
1758
|
}
|
|
606
|
-
const tempDir =
|
|
607
|
-
if (
|
|
608
|
-
|
|
1759
|
+
const tempDir = path6.join(nodeModulesPath, ".cache", "fullstack-cli", "extract-temp");
|
|
1760
|
+
if (fs6.existsSync(tempDir)) {
|
|
1761
|
+
fs6.rmSync(tempDir, { recursive: true });
|
|
609
1762
|
}
|
|
610
|
-
|
|
1763
|
+
fs6.mkdirSync(tempDir, { recursive: true });
|
|
611
1764
|
try {
|
|
612
1765
|
execSync(`tar -xzf "${tgzPath}" -C "${tempDir}"`, { stdio: "pipe" });
|
|
613
|
-
const extractedDir =
|
|
614
|
-
if (
|
|
615
|
-
|
|
1766
|
+
const extractedDir = path6.join(tempDir, "package");
|
|
1767
|
+
if (fs6.existsSync(extractedDir)) {
|
|
1768
|
+
fs6.renameSync(extractedDir, targetDir);
|
|
616
1769
|
} else {
|
|
617
|
-
const files =
|
|
1770
|
+
const files = fs6.readdirSync(tempDir);
|
|
618
1771
|
if (files.length === 1) {
|
|
619
|
-
|
|
1772
|
+
fs6.renameSync(path6.join(tempDir, files[0]), targetDir);
|
|
620
1773
|
} else {
|
|
621
1774
|
throw new Error("Unexpected tgz structure");
|
|
622
1775
|
}
|
|
623
1776
|
}
|
|
624
1777
|
return targetDir;
|
|
625
1778
|
} finally {
|
|
626
|
-
if (
|
|
627
|
-
|
|
1779
|
+
if (fs6.existsSync(tempDir)) {
|
|
1780
|
+
fs6.rmSync(tempDir, { recursive: true });
|
|
628
1781
|
}
|
|
629
1782
|
}
|
|
630
1783
|
}
|
|
@@ -633,10 +1786,10 @@ function checkMissingPeerDeps(peerDeps) {
|
|
|
633
1786
|
return [];
|
|
634
1787
|
}
|
|
635
1788
|
const missing = [];
|
|
636
|
-
const nodeModulesPath =
|
|
1789
|
+
const nodeModulesPath = path6.join(getProjectRoot(), "node_modules");
|
|
637
1790
|
for (const [depName, _version] of Object.entries(peerDeps)) {
|
|
638
|
-
const depPath =
|
|
639
|
-
if (!
|
|
1791
|
+
const depPath = path6.join(nodeModulesPath, depName);
|
|
1792
|
+
if (!fs6.existsSync(depPath)) {
|
|
640
1793
|
missing.push(depName);
|
|
641
1794
|
}
|
|
642
1795
|
}
|
|
@@ -660,16 +1813,16 @@ function installMissingDeps(deps) {
|
|
|
660
1813
|
}
|
|
661
1814
|
function removePluginDirectory(pluginName) {
|
|
662
1815
|
const pluginPath = getPluginPath(pluginName);
|
|
663
|
-
if (
|
|
664
|
-
|
|
1816
|
+
if (fs6.existsSync(pluginPath)) {
|
|
1817
|
+
fs6.rmSync(pluginPath, { recursive: true });
|
|
665
1818
|
console.log(`[action-plugin] Removed ${pluginName}`);
|
|
666
1819
|
}
|
|
667
1820
|
}
|
|
668
1821
|
|
|
669
1822
|
// src/commands/action-plugin/api-client.ts
|
|
670
1823
|
import { HttpClient as HttpClient2 } from "@lark-apaas/http-client";
|
|
671
|
-
import
|
|
672
|
-
import
|
|
1824
|
+
import fs7 from "fs";
|
|
1825
|
+
import path7 from "path";
|
|
673
1826
|
|
|
674
1827
|
// src/utils/http-client.ts
|
|
675
1828
|
import { HttpClient } from "@lark-apaas/http-client";
|
|
@@ -754,19 +1907,19 @@ async function downloadFromPublic(downloadURL) {
|
|
|
754
1907
|
return Buffer.from(arrayBuffer);
|
|
755
1908
|
}
|
|
756
1909
|
function getPluginCacheDir() {
|
|
757
|
-
return
|
|
1910
|
+
return path7.join(process.cwd(), PLUGIN_CACHE_DIR);
|
|
758
1911
|
}
|
|
759
1912
|
function ensureCacheDir() {
|
|
760
1913
|
const cacheDir = getPluginCacheDir();
|
|
761
|
-
if (!
|
|
762
|
-
|
|
1914
|
+
if (!fs7.existsSync(cacheDir)) {
|
|
1915
|
+
fs7.mkdirSync(cacheDir, { recursive: true });
|
|
763
1916
|
}
|
|
764
1917
|
}
|
|
765
1918
|
function getTempFilePath(pluginKey, version) {
|
|
766
1919
|
ensureCacheDir();
|
|
767
1920
|
const safeKey = pluginKey.replace(/[/@]/g, "_");
|
|
768
1921
|
const filename = `${safeKey}-${version}.tgz`;
|
|
769
|
-
return
|
|
1922
|
+
return path7.join(getPluginCacheDir(), filename);
|
|
770
1923
|
}
|
|
771
1924
|
async function downloadPlugin(pluginKey, requestedVersion) {
|
|
772
1925
|
console.log(`[action-plugin] Fetching plugin info for ${pluginKey}@${requestedVersion}...`);
|
|
@@ -782,7 +1935,7 @@ async function downloadPlugin(pluginKey, requestedVersion) {
|
|
|
782
1935
|
tgzBuffer = await downloadFromPublic(pluginInfo.downloadURL);
|
|
783
1936
|
}
|
|
784
1937
|
const tgzPath = getTempFilePath(pluginKey, pluginInfo.version);
|
|
785
|
-
|
|
1938
|
+
fs7.writeFileSync(tgzPath, tgzBuffer);
|
|
786
1939
|
console.log(`[action-plugin] Downloaded to ${tgzPath} (${(tgzBuffer.length / 1024).toFixed(2)} KB)`);
|
|
787
1940
|
return {
|
|
788
1941
|
tgzPath,
|
|
@@ -792,8 +1945,8 @@ async function downloadPlugin(pluginKey, requestedVersion) {
|
|
|
792
1945
|
}
|
|
793
1946
|
function cleanupTempFile(tgzPath) {
|
|
794
1947
|
try {
|
|
795
|
-
if (
|
|
796
|
-
|
|
1948
|
+
if (fs7.existsSync(tgzPath)) {
|
|
1949
|
+
fs7.unlinkSync(tgzPath);
|
|
797
1950
|
}
|
|
798
1951
|
} catch {
|
|
799
1952
|
}
|
|
@@ -1118,39 +2271,39 @@ var actionPluginCommandGroup = {
|
|
|
1118
2271
|
};
|
|
1119
2272
|
|
|
1120
2273
|
// src/commands/capability/utils.ts
|
|
1121
|
-
import
|
|
1122
|
-
import
|
|
2274
|
+
import fs8 from "fs";
|
|
2275
|
+
import path8 from "path";
|
|
1123
2276
|
var CAPABILITIES_DIR = "server/capabilities";
|
|
1124
2277
|
function getProjectRoot2() {
|
|
1125
2278
|
return process.cwd();
|
|
1126
2279
|
}
|
|
1127
2280
|
function getCapabilitiesDir() {
|
|
1128
|
-
return
|
|
2281
|
+
return path8.join(getProjectRoot2(), CAPABILITIES_DIR);
|
|
1129
2282
|
}
|
|
1130
2283
|
function getCapabilityPath(id) {
|
|
1131
|
-
return
|
|
2284
|
+
return path8.join(getCapabilitiesDir(), `${id}.json`);
|
|
1132
2285
|
}
|
|
1133
2286
|
function getPluginManifestPath(pluginKey) {
|
|
1134
|
-
return
|
|
2287
|
+
return path8.join(getProjectRoot2(), "node_modules", pluginKey, "manifest.json");
|
|
1135
2288
|
}
|
|
1136
2289
|
function capabilitiesDirExists() {
|
|
1137
|
-
return
|
|
2290
|
+
return fs8.existsSync(getCapabilitiesDir());
|
|
1138
2291
|
}
|
|
1139
2292
|
function listCapabilityIds() {
|
|
1140
2293
|
const dir = getCapabilitiesDir();
|
|
1141
|
-
if (!
|
|
2294
|
+
if (!fs8.existsSync(dir)) {
|
|
1142
2295
|
return [];
|
|
1143
2296
|
}
|
|
1144
|
-
const files =
|
|
2297
|
+
const files = fs8.readdirSync(dir);
|
|
1145
2298
|
return files.filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, ""));
|
|
1146
2299
|
}
|
|
1147
2300
|
function readCapability(id) {
|
|
1148
2301
|
const filePath = getCapabilityPath(id);
|
|
1149
|
-
if (!
|
|
2302
|
+
if (!fs8.existsSync(filePath)) {
|
|
1150
2303
|
throw new Error(`Capability not found: ${id}`);
|
|
1151
2304
|
}
|
|
1152
2305
|
try {
|
|
1153
|
-
const content =
|
|
2306
|
+
const content = fs8.readFileSync(filePath, "utf-8");
|
|
1154
2307
|
return JSON.parse(content);
|
|
1155
2308
|
} catch (error) {
|
|
1156
2309
|
if (error instanceof SyntaxError) {
|
|
@@ -1165,11 +2318,11 @@ function readAllCapabilities() {
|
|
|
1165
2318
|
}
|
|
1166
2319
|
function readPluginManifest(pluginKey) {
|
|
1167
2320
|
const manifestPath = getPluginManifestPath(pluginKey);
|
|
1168
|
-
if (!
|
|
2321
|
+
if (!fs8.existsSync(manifestPath)) {
|
|
1169
2322
|
throw new Error(`Plugin not installed: ${pluginKey} (manifest.json not found)`);
|
|
1170
2323
|
}
|
|
1171
2324
|
try {
|
|
1172
|
-
const content =
|
|
2325
|
+
const content = fs8.readFileSync(manifestPath, "utf-8");
|
|
1173
2326
|
return JSON.parse(content);
|
|
1174
2327
|
} catch (error) {
|
|
1175
2328
|
if (error instanceof SyntaxError) {
|
|
@@ -1343,58 +2496,58 @@ var capabilityCommandGroup = {
|
|
|
1343
2496
|
};
|
|
1344
2497
|
|
|
1345
2498
|
// src/commands/migration/version-manager.ts
|
|
1346
|
-
import
|
|
1347
|
-
import
|
|
2499
|
+
import fs9 from "fs";
|
|
2500
|
+
import path9 from "path";
|
|
1348
2501
|
var PACKAGE_JSON = "package.json";
|
|
1349
2502
|
var VERSION_FIELD = "migrationVersion";
|
|
1350
2503
|
function getPackageJsonPath2() {
|
|
1351
|
-
return
|
|
2504
|
+
return path9.join(process.cwd(), PACKAGE_JSON);
|
|
1352
2505
|
}
|
|
1353
2506
|
function getCurrentVersion() {
|
|
1354
2507
|
const pkgPath = getPackageJsonPath2();
|
|
1355
|
-
if (!
|
|
2508
|
+
if (!fs9.existsSync(pkgPath)) {
|
|
1356
2509
|
throw new Error("package.json not found");
|
|
1357
2510
|
}
|
|
1358
|
-
const pkg2 = JSON.parse(
|
|
2511
|
+
const pkg2 = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
|
|
1359
2512
|
return pkg2[VERSION_FIELD] ?? 0;
|
|
1360
2513
|
}
|
|
1361
2514
|
function setCurrentVersion(version) {
|
|
1362
2515
|
const pkgPath = getPackageJsonPath2();
|
|
1363
|
-
const pkg2 = JSON.parse(
|
|
2516
|
+
const pkg2 = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
|
|
1364
2517
|
pkg2[VERSION_FIELD] = version;
|
|
1365
|
-
|
|
2518
|
+
fs9.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
|
|
1366
2519
|
}
|
|
1367
2520
|
|
|
1368
2521
|
// src/commands/migration/versions/v001_capability/json-migrator/detector.ts
|
|
1369
|
-
import
|
|
1370
|
-
import
|
|
2522
|
+
import fs11 from "fs";
|
|
2523
|
+
import path11 from "path";
|
|
1371
2524
|
|
|
1372
2525
|
// src/commands/migration/versions/v001_capability/utils.ts
|
|
1373
|
-
import
|
|
1374
|
-
import
|
|
2526
|
+
import fs10 from "fs";
|
|
2527
|
+
import path10 from "path";
|
|
1375
2528
|
var CAPABILITIES_DIR2 = "server/capabilities";
|
|
1376
2529
|
function getProjectRoot3() {
|
|
1377
2530
|
return process.cwd();
|
|
1378
2531
|
}
|
|
1379
2532
|
function getCapabilitiesDir2() {
|
|
1380
|
-
return
|
|
2533
|
+
return path10.join(getProjectRoot3(), CAPABILITIES_DIR2);
|
|
1381
2534
|
}
|
|
1382
2535
|
function getPluginManifestPath2(pluginKey) {
|
|
1383
|
-
return
|
|
2536
|
+
return path10.join(getProjectRoot3(), "node_modules", pluginKey, "manifest.json");
|
|
1384
2537
|
}
|
|
1385
2538
|
|
|
1386
2539
|
// src/commands/migration/versions/v001_capability/json-migrator/detector.ts
|
|
1387
2540
|
function detectJsonMigration() {
|
|
1388
2541
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
1389
|
-
const oldFilePath =
|
|
1390
|
-
if (!
|
|
2542
|
+
const oldFilePath = path11.join(capabilitiesDir, "capabilities.json");
|
|
2543
|
+
if (!fs11.existsSync(oldFilePath)) {
|
|
1391
2544
|
return {
|
|
1392
2545
|
needsMigration: false,
|
|
1393
2546
|
reason: "capabilities.json not found"
|
|
1394
2547
|
};
|
|
1395
2548
|
}
|
|
1396
2549
|
try {
|
|
1397
|
-
const content =
|
|
2550
|
+
const content = fs11.readFileSync(oldFilePath, "utf-8");
|
|
1398
2551
|
const parsed = JSON.parse(content);
|
|
1399
2552
|
const capabilities = Array.isArray(parsed) ? parsed : [];
|
|
1400
2553
|
return {
|
|
@@ -1434,8 +2587,8 @@ async function check(options) {
|
|
|
1434
2587
|
}
|
|
1435
2588
|
|
|
1436
2589
|
// src/commands/migration/versions/v001_capability/json-migrator/index.ts
|
|
1437
|
-
import
|
|
1438
|
-
import
|
|
2590
|
+
import fs12 from "fs";
|
|
2591
|
+
import path12 from "path";
|
|
1439
2592
|
|
|
1440
2593
|
// src/commands/migration/versions/v001_capability/mapping.ts
|
|
1441
2594
|
var DEFAULT_PLUGIN_VERSION = "1.0.0";
|
|
@@ -1665,18 +2818,18 @@ function transformCapabilities(oldCapabilities) {
|
|
|
1665
2818
|
// src/commands/migration/versions/v001_capability/json-migrator/index.ts
|
|
1666
2819
|
function loadExistingCapabilities() {
|
|
1667
2820
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
1668
|
-
if (!
|
|
2821
|
+
if (!fs12.existsSync(capabilitiesDir)) {
|
|
1669
2822
|
return [];
|
|
1670
2823
|
}
|
|
1671
|
-
const files =
|
|
2824
|
+
const files = fs12.readdirSync(capabilitiesDir);
|
|
1672
2825
|
const capabilities = [];
|
|
1673
2826
|
for (const file of files) {
|
|
1674
2827
|
if (file === "capabilities.json" || !file.endsWith(".json")) {
|
|
1675
2828
|
continue;
|
|
1676
2829
|
}
|
|
1677
2830
|
try {
|
|
1678
|
-
const filePath =
|
|
1679
|
-
const content =
|
|
2831
|
+
const filePath = path12.join(capabilitiesDir, file);
|
|
2832
|
+
const content = fs12.readFileSync(filePath, "utf-8");
|
|
1680
2833
|
const capability = JSON.parse(content);
|
|
1681
2834
|
if (capability.id && capability.pluginKey) {
|
|
1682
2835
|
capabilities.push(capability);
|
|
@@ -1734,9 +2887,9 @@ async function migrateJsonFiles(options) {
|
|
|
1734
2887
|
}
|
|
1735
2888
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
1736
2889
|
for (const cap of newCapabilities) {
|
|
1737
|
-
const filePath =
|
|
2890
|
+
const filePath = path12.join(capabilitiesDir, `${cap.id}.json`);
|
|
1738
2891
|
const content = JSON.stringify(cap, null, 2);
|
|
1739
|
-
|
|
2892
|
+
fs12.writeFileSync(filePath, content, "utf-8");
|
|
1740
2893
|
console.log(` \u2713 Created: ${cap.id}.json`);
|
|
1741
2894
|
}
|
|
1742
2895
|
return {
|
|
@@ -1748,10 +2901,10 @@ async function migrateJsonFiles(options) {
|
|
|
1748
2901
|
}
|
|
1749
2902
|
|
|
1750
2903
|
// src/commands/migration/versions/v001_capability/plugin-installer/detector.ts
|
|
1751
|
-
import
|
|
2904
|
+
import fs13 from "fs";
|
|
1752
2905
|
function isPluginInstalled2(pluginKey) {
|
|
1753
2906
|
const manifestPath = getPluginManifestPath2(pluginKey);
|
|
1754
|
-
return
|
|
2907
|
+
return fs13.existsSync(manifestPath);
|
|
1755
2908
|
}
|
|
1756
2909
|
function detectPluginsToInstall(capabilities) {
|
|
1757
2910
|
const pluginKeys = /* @__PURE__ */ new Set();
|
|
@@ -1827,12 +2980,12 @@ async function installPlugins(capabilities, options) {
|
|
|
1827
2980
|
}
|
|
1828
2981
|
|
|
1829
2982
|
// src/commands/migration/versions/v001_capability/code-migrator/index.ts
|
|
1830
|
-
import
|
|
1831
|
-
import { Project } from "ts-morph";
|
|
2983
|
+
import path14 from "path";
|
|
2984
|
+
import { Project as Project2 } from "ts-morph";
|
|
1832
2985
|
|
|
1833
2986
|
// src/commands/migration/versions/v001_capability/code-migrator/scanner.ts
|
|
1834
|
-
import
|
|
1835
|
-
import
|
|
2987
|
+
import fs14 from "fs";
|
|
2988
|
+
import path13 from "path";
|
|
1836
2989
|
var EXCLUDED_DIRS = [
|
|
1837
2990
|
"node_modules",
|
|
1838
2991
|
"dist",
|
|
@@ -1847,9 +3000,9 @@ var EXCLUDED_PATTERNS = [
|
|
|
1847
3000
|
/\.d\.ts$/
|
|
1848
3001
|
];
|
|
1849
3002
|
function scanDirectory(dir, files = []) {
|
|
1850
|
-
const entries =
|
|
3003
|
+
const entries = fs14.readdirSync(dir, { withFileTypes: true });
|
|
1851
3004
|
for (const entry of entries) {
|
|
1852
|
-
const fullPath =
|
|
3005
|
+
const fullPath = path13.join(dir, entry.name);
|
|
1853
3006
|
if (entry.isDirectory()) {
|
|
1854
3007
|
if (EXCLUDED_DIRS.includes(entry.name)) {
|
|
1855
3008
|
continue;
|
|
@@ -1865,14 +3018,14 @@ function scanDirectory(dir, files = []) {
|
|
|
1865
3018
|
return files;
|
|
1866
3019
|
}
|
|
1867
3020
|
function scanServerFiles() {
|
|
1868
|
-
const serverDir =
|
|
1869
|
-
if (!
|
|
3021
|
+
const serverDir = path13.join(getProjectRoot3(), "server");
|
|
3022
|
+
if (!fs14.existsSync(serverDir)) {
|
|
1870
3023
|
return [];
|
|
1871
3024
|
}
|
|
1872
3025
|
return scanDirectory(serverDir);
|
|
1873
3026
|
}
|
|
1874
3027
|
function hasCapabilityImport(filePath) {
|
|
1875
|
-
const content =
|
|
3028
|
+
const content = fs14.readFileSync(filePath, "utf-8");
|
|
1876
3029
|
return /import\s+.*from\s+['"][^'"]*capabilities[^'"]*['"]/.test(content);
|
|
1877
3030
|
}
|
|
1878
3031
|
function scanFilesToMigrate() {
|
|
@@ -2247,7 +3400,7 @@ function analyzeFile(project, filePath, actionNameMap) {
|
|
|
2247
3400
|
const callSites = analyzeCallSites(sourceFile, imports);
|
|
2248
3401
|
const classInfo = analyzeClass(sourceFile);
|
|
2249
3402
|
const { canMigrate, reason } = canAutoMigrate(classInfo);
|
|
2250
|
-
const relativePath =
|
|
3403
|
+
const relativePath = path14.relative(getProjectRoot3(), filePath);
|
|
2251
3404
|
return {
|
|
2252
3405
|
filePath: relativePath,
|
|
2253
3406
|
imports,
|
|
@@ -2258,7 +3411,7 @@ function analyzeFile(project, filePath, actionNameMap) {
|
|
|
2258
3411
|
};
|
|
2259
3412
|
}
|
|
2260
3413
|
function migrateFile(project, analysis, dryRun) {
|
|
2261
|
-
const absolutePath =
|
|
3414
|
+
const absolutePath = path14.join(getProjectRoot3(), analysis.filePath);
|
|
2262
3415
|
if (!analysis.canAutoMigrate) {
|
|
2263
3416
|
return {
|
|
2264
3417
|
filePath: analysis.filePath,
|
|
@@ -2318,7 +3471,7 @@ async function migrateCode(options, capabilities) {
|
|
|
2318
3471
|
console.log(" No files need code migration.\n");
|
|
2319
3472
|
return result;
|
|
2320
3473
|
}
|
|
2321
|
-
const project = new
|
|
3474
|
+
const project = new Project2({
|
|
2322
3475
|
skipAddingFilesFromTsConfig: true,
|
|
2323
3476
|
compilerOptions: {
|
|
2324
3477
|
allowJs: true
|
|
@@ -2361,17 +3514,17 @@ function getSuggestion(analysis) {
|
|
|
2361
3514
|
}
|
|
2362
3515
|
|
|
2363
3516
|
// src/commands/migration/versions/v001_capability/cleanup.ts
|
|
2364
|
-
import
|
|
2365
|
-
import
|
|
3517
|
+
import fs15 from "fs";
|
|
3518
|
+
import path15 from "path";
|
|
2366
3519
|
function cleanupOldFiles(capabilities, dryRun) {
|
|
2367
3520
|
const deletedFiles = [];
|
|
2368
3521
|
const errors = [];
|
|
2369
3522
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
2370
|
-
const oldJsonPath =
|
|
2371
|
-
if (
|
|
3523
|
+
const oldJsonPath = path15.join(capabilitiesDir, "capabilities.json");
|
|
3524
|
+
if (fs15.existsSync(oldJsonPath)) {
|
|
2372
3525
|
try {
|
|
2373
3526
|
if (!dryRun) {
|
|
2374
|
-
|
|
3527
|
+
fs15.unlinkSync(oldJsonPath);
|
|
2375
3528
|
}
|
|
2376
3529
|
deletedFiles.push("capabilities.json");
|
|
2377
3530
|
} catch (error) {
|
|
@@ -2379,11 +3532,11 @@ function cleanupOldFiles(capabilities, dryRun) {
|
|
|
2379
3532
|
}
|
|
2380
3533
|
}
|
|
2381
3534
|
for (const cap of capabilities) {
|
|
2382
|
-
const tsFilePath =
|
|
2383
|
-
if (
|
|
3535
|
+
const tsFilePath = path15.join(capabilitiesDir, `${cap.id}.ts`);
|
|
3536
|
+
if (fs15.existsSync(tsFilePath)) {
|
|
2384
3537
|
try {
|
|
2385
3538
|
if (!dryRun) {
|
|
2386
|
-
|
|
3539
|
+
fs15.unlinkSync(tsFilePath);
|
|
2387
3540
|
}
|
|
2388
3541
|
deletedFiles.push(`${cap.id}.ts`);
|
|
2389
3542
|
} catch (error) {
|
|
@@ -2399,8 +3552,8 @@ function cleanupOldFiles(capabilities, dryRun) {
|
|
|
2399
3552
|
}
|
|
2400
3553
|
|
|
2401
3554
|
// src/commands/migration/versions/v001_capability/report-generator.ts
|
|
2402
|
-
import
|
|
2403
|
-
import
|
|
3555
|
+
import fs16 from "fs";
|
|
3556
|
+
import path16 from "path";
|
|
2404
3557
|
var REPORT_FILE = "capability-migration-report.md";
|
|
2405
3558
|
function printSummary(result) {
|
|
2406
3559
|
const { jsonMigration, pluginInstallation, codeMigration, cleanup } = result;
|
|
@@ -2563,15 +3716,15 @@ async function generateReport(result) {
|
|
|
2563
3716
|
}
|
|
2564
3717
|
lines.push("");
|
|
2565
3718
|
const logDir = process.env.LOG_DIR || "logs";
|
|
2566
|
-
if (!
|
|
3719
|
+
if (!fs16.existsSync(logDir)) {
|
|
2567
3720
|
return;
|
|
2568
3721
|
}
|
|
2569
|
-
const reportDir =
|
|
2570
|
-
if (!
|
|
2571
|
-
|
|
3722
|
+
const reportDir = path16.join(logDir, "migration");
|
|
3723
|
+
if (!fs16.existsSync(reportDir)) {
|
|
3724
|
+
fs16.mkdirSync(reportDir, { recursive: true });
|
|
2572
3725
|
}
|
|
2573
|
-
const reportPath =
|
|
2574
|
-
|
|
3726
|
+
const reportPath = path16.join(reportDir, REPORT_FILE);
|
|
3727
|
+
fs16.writeFileSync(reportPath, lines.join("\n"), "utf-8");
|
|
2575
3728
|
console.log(`\u{1F4C4} Report generated: ${reportPath}`);
|
|
2576
3729
|
}
|
|
2577
3730
|
|
|
@@ -3103,10 +4256,10 @@ var migrationCommand = {
|
|
|
3103
4256
|
};
|
|
3104
4257
|
|
|
3105
4258
|
// src/commands/read-logs/index.ts
|
|
3106
|
-
import
|
|
4259
|
+
import path17 from "path";
|
|
3107
4260
|
|
|
3108
4261
|
// src/commands/read-logs/std-utils.ts
|
|
3109
|
-
import
|
|
4262
|
+
import fs17 from "fs";
|
|
3110
4263
|
function formatStdPrefixTime(localTime) {
|
|
3111
4264
|
const match = localTime.match(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/);
|
|
3112
4265
|
if (!match) return localTime;
|
|
@@ -3136,11 +4289,11 @@ function stripPrefixFromStdLine(line) {
|
|
|
3136
4289
|
return `[${time}] ${content}`;
|
|
3137
4290
|
}
|
|
3138
4291
|
function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarker) {
|
|
3139
|
-
const stat =
|
|
4292
|
+
const stat = fs17.statSync(filePath);
|
|
3140
4293
|
if (stat.size === 0) {
|
|
3141
4294
|
return { lines: [], markerFound: false, totalLinesCount: 0 };
|
|
3142
4295
|
}
|
|
3143
|
-
const fd =
|
|
4296
|
+
const fd = fs17.openSync(filePath, "r");
|
|
3144
4297
|
const chunkSize = 64 * 1024;
|
|
3145
4298
|
let position = stat.size;
|
|
3146
4299
|
let remainder = "";
|
|
@@ -3154,7 +4307,7 @@ function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarke
|
|
|
3154
4307
|
const length = Math.min(chunkSize, position);
|
|
3155
4308
|
position -= length;
|
|
3156
4309
|
const buffer = Buffer.alloc(length);
|
|
3157
|
-
|
|
4310
|
+
fs17.readSync(fd, buffer, 0, length, position);
|
|
3158
4311
|
let chunk = buffer.toString("utf8");
|
|
3159
4312
|
if (remainder) {
|
|
3160
4313
|
chunk += remainder;
|
|
@@ -3196,7 +4349,7 @@ function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarke
|
|
|
3196
4349
|
}
|
|
3197
4350
|
}
|
|
3198
4351
|
} finally {
|
|
3199
|
-
|
|
4352
|
+
fs17.closeSync(fd);
|
|
3200
4353
|
}
|
|
3201
4354
|
return { lines: collected.reverse(), markerFound, totalLinesCount };
|
|
3202
4355
|
}
|
|
@@ -3217,21 +4370,21 @@ function readServerStdSegment(filePath, maxLines, offset) {
|
|
|
3217
4370
|
}
|
|
3218
4371
|
|
|
3219
4372
|
// src/commands/read-logs/tail.ts
|
|
3220
|
-
import
|
|
4373
|
+
import fs18 from "fs";
|
|
3221
4374
|
function fileExists(filePath) {
|
|
3222
4375
|
try {
|
|
3223
|
-
|
|
4376
|
+
fs18.accessSync(filePath, fs18.constants.F_OK | fs18.constants.R_OK);
|
|
3224
4377
|
return true;
|
|
3225
4378
|
} catch {
|
|
3226
4379
|
return false;
|
|
3227
4380
|
}
|
|
3228
4381
|
}
|
|
3229
4382
|
function readFileTailLines(filePath, maxLines) {
|
|
3230
|
-
const stat =
|
|
4383
|
+
const stat = fs18.statSync(filePath);
|
|
3231
4384
|
if (stat.size === 0) {
|
|
3232
4385
|
return [];
|
|
3233
4386
|
}
|
|
3234
|
-
const fd =
|
|
4387
|
+
const fd = fs18.openSync(filePath, "r");
|
|
3235
4388
|
const chunkSize = 64 * 1024;
|
|
3236
4389
|
const chunks = [];
|
|
3237
4390
|
let position = stat.size;
|
|
@@ -3241,13 +4394,13 @@ function readFileTailLines(filePath, maxLines) {
|
|
|
3241
4394
|
const length = Math.min(chunkSize, position);
|
|
3242
4395
|
position -= length;
|
|
3243
4396
|
const buffer = Buffer.alloc(length);
|
|
3244
|
-
|
|
4397
|
+
fs18.readSync(fd, buffer, 0, length, position);
|
|
3245
4398
|
chunks.unshift(buffer.toString("utf8"));
|
|
3246
4399
|
const chunkLines = buffer.toString("utf8").split("\n").length - 1;
|
|
3247
4400
|
collectedLines += chunkLines;
|
|
3248
4401
|
}
|
|
3249
4402
|
} finally {
|
|
3250
|
-
|
|
4403
|
+
fs18.closeSync(fd);
|
|
3251
4404
|
}
|
|
3252
4405
|
const content = chunks.join("");
|
|
3253
4406
|
const allLines = content.split("\n");
|
|
@@ -3263,11 +4416,11 @@ function readFileTailLines(filePath, maxLines) {
|
|
|
3263
4416
|
return allLines.slice(allLines.length - maxLines);
|
|
3264
4417
|
}
|
|
3265
4418
|
function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
|
|
3266
|
-
const stat =
|
|
4419
|
+
const stat = fs18.statSync(filePath);
|
|
3267
4420
|
if (stat.size === 0) {
|
|
3268
4421
|
return { lines: [], totalLinesCount: 0 };
|
|
3269
4422
|
}
|
|
3270
|
-
const fd =
|
|
4423
|
+
const fd = fs18.openSync(filePath, "r");
|
|
3271
4424
|
const chunkSize = 64 * 1024;
|
|
3272
4425
|
let position = stat.size;
|
|
3273
4426
|
let remainder = "";
|
|
@@ -3279,7 +4432,7 @@ function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
|
|
|
3279
4432
|
const length = Math.min(chunkSize, position);
|
|
3280
4433
|
position -= length;
|
|
3281
4434
|
const buffer = Buffer.alloc(length);
|
|
3282
|
-
|
|
4435
|
+
fs18.readSync(fd, buffer, 0, length, position);
|
|
3283
4436
|
let chunk = buffer.toString("utf8");
|
|
3284
4437
|
if (remainder) {
|
|
3285
4438
|
chunk += remainder;
|
|
@@ -3310,7 +4463,7 @@ function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
|
|
|
3310
4463
|
}
|
|
3311
4464
|
}
|
|
3312
4465
|
} finally {
|
|
3313
|
-
|
|
4466
|
+
fs18.closeSync(fd);
|
|
3314
4467
|
}
|
|
3315
4468
|
return { lines: collected.reverse(), totalLinesCount };
|
|
3316
4469
|
}
|
|
@@ -3412,7 +4565,7 @@ function extractClientStdSegment(lines, maxLines, offset) {
|
|
|
3412
4565
|
}
|
|
3413
4566
|
|
|
3414
4567
|
// src/commands/read-logs/json-lines.ts
|
|
3415
|
-
import
|
|
4568
|
+
import fs19 from "fs";
|
|
3416
4569
|
function normalizePid(value) {
|
|
3417
4570
|
if (typeof value === "number") {
|
|
3418
4571
|
return String(value);
|
|
@@ -3463,11 +4616,11 @@ function buildWantedLevelSet(levels) {
|
|
|
3463
4616
|
return set.size > 0 ? set : null;
|
|
3464
4617
|
}
|
|
3465
4618
|
function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
|
|
3466
|
-
const stat =
|
|
4619
|
+
const stat = fs19.statSync(filePath);
|
|
3467
4620
|
if (stat.size === 0) {
|
|
3468
4621
|
return { lines: [], totalLinesCount: 0 };
|
|
3469
4622
|
}
|
|
3470
|
-
const fd =
|
|
4623
|
+
const fd = fs19.openSync(filePath, "r");
|
|
3471
4624
|
const chunkSize = 64 * 1024;
|
|
3472
4625
|
let position = stat.size;
|
|
3473
4626
|
let remainder = "";
|
|
@@ -3482,7 +4635,7 @@ function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
|
|
|
3482
4635
|
const length = Math.min(chunkSize, position);
|
|
3483
4636
|
position -= length;
|
|
3484
4637
|
const buffer = Buffer.alloc(length);
|
|
3485
|
-
|
|
4638
|
+
fs19.readSync(fd, buffer, 0, length, position);
|
|
3486
4639
|
let chunk = buffer.toString("utf8");
|
|
3487
4640
|
if (remainder) {
|
|
3488
4641
|
chunk += remainder;
|
|
@@ -3544,7 +4697,7 @@ function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
|
|
|
3544
4697
|
}
|
|
3545
4698
|
}
|
|
3546
4699
|
} finally {
|
|
3547
|
-
|
|
4700
|
+
fs19.closeSync(fd);
|
|
3548
4701
|
}
|
|
3549
4702
|
return { lines: collected.reverse(), totalLinesCount };
|
|
3550
4703
|
}
|
|
@@ -3587,11 +4740,11 @@ function extractTraceId(obj) {
|
|
|
3587
4740
|
function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
|
|
3588
4741
|
const wanted = traceId.trim();
|
|
3589
4742
|
if (!wanted) return { lines: [], totalLinesCount: 0 };
|
|
3590
|
-
const stat =
|
|
4743
|
+
const stat = fs19.statSync(filePath);
|
|
3591
4744
|
if (stat.size === 0) {
|
|
3592
4745
|
return { lines: [], totalLinesCount: 0 };
|
|
3593
4746
|
}
|
|
3594
|
-
const fd =
|
|
4747
|
+
const fd = fs19.openSync(filePath, "r");
|
|
3595
4748
|
const chunkSize = 64 * 1024;
|
|
3596
4749
|
let position = stat.size;
|
|
3597
4750
|
let remainder = "";
|
|
@@ -3604,7 +4757,7 @@ function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
|
|
|
3604
4757
|
const length = Math.min(chunkSize, position);
|
|
3605
4758
|
position -= length;
|
|
3606
4759
|
const buffer = Buffer.alloc(length);
|
|
3607
|
-
|
|
4760
|
+
fs19.readSync(fd, buffer, 0, length, position);
|
|
3608
4761
|
let chunk = buffer.toString("utf8");
|
|
3609
4762
|
if (remainder) {
|
|
3610
4763
|
chunk += remainder;
|
|
@@ -3657,7 +4810,7 @@ function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
|
|
|
3657
4810
|
}
|
|
3658
4811
|
}
|
|
3659
4812
|
} finally {
|
|
3660
|
-
|
|
4813
|
+
fs19.closeSync(fd);
|
|
3661
4814
|
}
|
|
3662
4815
|
return { lines: collected.reverse(), totalLinesCount };
|
|
3663
4816
|
}
|
|
@@ -3666,11 +4819,11 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
|
|
|
3666
4819
|
if (!wantedLevelSet) {
|
|
3667
4820
|
return { lines: [], totalLinesCount: 0 };
|
|
3668
4821
|
}
|
|
3669
|
-
const stat =
|
|
4822
|
+
const stat = fs19.statSync(filePath);
|
|
3670
4823
|
if (stat.size === 0) {
|
|
3671
4824
|
return { lines: [], totalLinesCount: 0 };
|
|
3672
4825
|
}
|
|
3673
|
-
const fd =
|
|
4826
|
+
const fd = fs19.openSync(filePath, "r");
|
|
3674
4827
|
const chunkSize = 64 * 1024;
|
|
3675
4828
|
let position = stat.size;
|
|
3676
4829
|
let remainder = "";
|
|
@@ -3682,7 +4835,7 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
|
|
|
3682
4835
|
const length = Math.min(chunkSize, position);
|
|
3683
4836
|
position -= length;
|
|
3684
4837
|
const buffer = Buffer.alloc(length);
|
|
3685
|
-
|
|
4838
|
+
fs19.readSync(fd, buffer, 0, length, position);
|
|
3686
4839
|
let chunk = buffer.toString("utf8");
|
|
3687
4840
|
if (remainder) {
|
|
3688
4841
|
chunk += remainder;
|
|
@@ -3729,7 +4882,7 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
|
|
|
3729
4882
|
}
|
|
3730
4883
|
}
|
|
3731
4884
|
} finally {
|
|
3732
|
-
|
|
4885
|
+
fs19.closeSync(fd);
|
|
3733
4886
|
}
|
|
3734
4887
|
return { lines: collected.reverse(), totalLinesCount };
|
|
3735
4888
|
}
|
|
@@ -3860,21 +5013,21 @@ async function readLogsJsonResult(options) {
|
|
|
3860
5013
|
};
|
|
3861
5014
|
}
|
|
3862
5015
|
function resolveLogFilePath(logDir, type) {
|
|
3863
|
-
const base =
|
|
5016
|
+
const base = path17.isAbsolute(logDir) ? logDir : path17.join(process.cwd(), logDir);
|
|
3864
5017
|
if (type === "server") {
|
|
3865
|
-
return
|
|
5018
|
+
return path17.join(base, "server.log");
|
|
3866
5019
|
}
|
|
3867
5020
|
if (type === "trace") {
|
|
3868
|
-
return
|
|
5021
|
+
return path17.join(base, "trace.log");
|
|
3869
5022
|
}
|
|
3870
5023
|
if (type === "server-std") {
|
|
3871
|
-
return
|
|
5024
|
+
return path17.join(base, "server.std.log");
|
|
3872
5025
|
}
|
|
3873
5026
|
if (type === "client-std") {
|
|
3874
|
-
return
|
|
5027
|
+
return path17.join(base, "client.std.log");
|
|
3875
5028
|
}
|
|
3876
5029
|
if (type === "browser") {
|
|
3877
|
-
return
|
|
5030
|
+
return path17.join(base, "browser.log");
|
|
3878
5031
|
}
|
|
3879
5032
|
throw new Error(`Unsupported log type: ${type}`);
|
|
3880
5033
|
}
|
|
@@ -3949,12 +5102,12 @@ var commands = [
|
|
|
3949
5102
|
];
|
|
3950
5103
|
|
|
3951
5104
|
// src/index.ts
|
|
3952
|
-
var envPath =
|
|
3953
|
-
if (
|
|
5105
|
+
var envPath = path18.join(process.cwd(), ".env");
|
|
5106
|
+
if (fs20.existsSync(envPath)) {
|
|
3954
5107
|
dotenvConfig({ path: envPath });
|
|
3955
5108
|
}
|
|
3956
|
-
var
|
|
3957
|
-
var pkg = JSON.parse(
|
|
5109
|
+
var __dirname2 = path18.dirname(fileURLToPath3(import.meta.url));
|
|
5110
|
+
var pkg = JSON.parse(fs20.readFileSync(path18.join(__dirname2, "../package.json"), "utf-8"));
|
|
3958
5111
|
var cli = new FullstackCLI(pkg.version);
|
|
3959
5112
|
cli.useAll(commands);
|
|
3960
5113
|
cli.run();
|