@lark-apaas/fullstack-cli 1.1.16-alpha.2 → 1.1.16-alpha.4
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/config/drizzle.config.js +1 -24
- package/dist/index.js +605 -982
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import
|
|
2
|
+
import fs20 from "fs";
|
|
3
3
|
import path17 from "path";
|
|
4
4
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
5
5
|
import { config as dotenvConfig } from "dotenv";
|
|
@@ -117,94 +117,104 @@ Command "${ctx.commandName}" completed in ${elapsed}ms`);
|
|
|
117
117
|
|
|
118
118
|
// src/commands/db/schema.handler.ts
|
|
119
119
|
import path2 from "path";
|
|
120
|
-
import
|
|
120
|
+
import fs3 from "fs";
|
|
121
121
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
122
122
|
import { spawnSync } from "child_process";
|
|
123
123
|
import { createRequire } from "module";
|
|
124
124
|
import { config as loadEnv } from "dotenv";
|
|
125
125
|
|
|
126
|
-
// src/commands/db/gen-dbschema/index.ts
|
|
127
|
-
import fs3 from "fs";
|
|
128
|
-
import path from "path";
|
|
129
|
-
|
|
130
126
|
// src/commands/db/gen-dbschema/postprocess.ts
|
|
131
127
|
import fs2 from "fs";
|
|
128
|
+
import path from "path";
|
|
132
129
|
|
|
133
|
-
// src/commands/db/gen-dbschema/
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
unmatchedUnknown: [],
|
|
146
|
-
replacedTimestamp: 0,
|
|
147
|
-
replacedDefaultNow: 0,
|
|
148
|
-
removedSystemFields: 0,
|
|
149
|
-
addedImports: [],
|
|
150
|
-
removedImports: []
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// src/commands/db/gen-dbschema/transforms/core.ts
|
|
155
|
-
var PROJECT_OPTIONS = {
|
|
156
|
-
skipAddingFilesFromTsConfig: true,
|
|
157
|
-
skipFileDependencyResolution: true,
|
|
158
|
-
manipulationSettings: {
|
|
159
|
-
indentationText: IndentationText.TwoSpaces,
|
|
160
|
-
quoteKind: QuoteKind.Single
|
|
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
|
+
`;
|
|
161
142
|
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const project = new Project({
|
|
165
|
-
...PROJECT_OPTIONS,
|
|
166
|
-
useInMemoryFileSystem: true
|
|
167
|
-
});
|
|
168
|
-
const sourceFile = project.createSourceFile(fileName, source);
|
|
169
|
-
return { project, sourceFile };
|
|
143
|
+
return `${HEADER_COMMENT}
|
|
144
|
+
${trimmed}`;
|
|
170
145
|
}
|
|
171
|
-
function
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
transform.transform({ sourceFile, stats });
|
|
176
|
-
} catch (error) {
|
|
177
|
-
console.error(`[ast] Transform "${transform.name}" failed:`, error);
|
|
178
|
-
throw error;
|
|
179
|
-
}
|
|
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);
|
|
180
150
|
}
|
|
181
|
-
return
|
|
182
|
-
}
|
|
183
|
-
function formatSourceFile(sourceFile) {
|
|
184
|
-
sourceFile.formatText({
|
|
185
|
-
indentSize: 2,
|
|
186
|
-
convertTabsToSpaces: true
|
|
187
|
-
});
|
|
151
|
+
return current;
|
|
188
152
|
}
|
|
189
|
-
function
|
|
190
|
-
return
|
|
153
|
+
function collapseExtraBlankLines(text) {
|
|
154
|
+
return text.replace(/\n{3,}/g, "\n\n");
|
|
191
155
|
}
|
|
192
156
|
|
|
193
|
-
// src/commands/db/gen-dbschema/
|
|
194
|
-
|
|
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
|
+
}
|
|
195
185
|
|
|
196
|
-
// src/commands/db/gen-dbschema/
|
|
186
|
+
// src/commands/db/gen-dbschema/helper/table-rename.ts
|
|
197
187
|
import { pinyin } from "pinyin-pro";
|
|
198
|
-
function
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
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, "\\/");
|
|
208
218
|
}
|
|
209
219
|
function toCamelCase(str) {
|
|
210
220
|
const words = str.split(/[_\-\s]+/).filter(Boolean);
|
|
@@ -232,640 +242,120 @@ function sanitizeIdentifier(name) {
|
|
|
232
242
|
}
|
|
233
243
|
return sanitized;
|
|
234
244
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
"pgTable",
|
|
239
|
-
"pgView",
|
|
240
|
-
"pgMaterializedView",
|
|
241
|
-
"pgEnum",
|
|
242
|
-
"pgSequence",
|
|
243
|
-
// Also match schema.xxx() format (before conversion)
|
|
244
|
-
"table",
|
|
245
|
-
"view",
|
|
246
|
-
"materializedView",
|
|
247
|
-
"enum",
|
|
248
|
-
"sequence"
|
|
249
|
-
];
|
|
250
|
-
function extractFactoryName(initializer) {
|
|
251
|
-
if (!Node.isCallExpression(initializer)) {
|
|
252
|
-
return void 0;
|
|
253
|
-
}
|
|
254
|
-
const args = initializer.getArguments();
|
|
255
|
-
if (args.length === 0) {
|
|
256
|
-
return void 0;
|
|
257
|
-
}
|
|
258
|
-
const firstArg = args[0];
|
|
259
|
-
if (Node.isStringLiteral(firstArg)) {
|
|
260
|
-
return firstArg.getLiteralText();
|
|
261
|
-
}
|
|
262
|
-
return void 0;
|
|
263
|
-
}
|
|
264
|
-
function isPgFactoryCall(initializer) {
|
|
265
|
-
if (!Node.isCallExpression(initializer)) {
|
|
266
|
-
return false;
|
|
267
|
-
}
|
|
268
|
-
const expression = initializer.getExpression();
|
|
269
|
-
const exprText = expression.getText();
|
|
270
|
-
if (PG_FACTORIES.includes(exprText)) {
|
|
271
|
-
return true;
|
|
245
|
+
function toAsciiName(name) {
|
|
246
|
+
if (!/[^\x00-\x7F]/.test(name)) {
|
|
247
|
+
return name;
|
|
272
248
|
}
|
|
273
|
-
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
249
|
+
try {
|
|
250
|
+
const transliterated = pinyin(name, { toneType: "none", type: "array" }).join("_");
|
|
251
|
+
return transliterated || name;
|
|
252
|
+
} catch (error) {
|
|
253
|
+
return name;
|
|
278
254
|
}
|
|
279
|
-
return false;
|
|
280
255
|
}
|
|
281
|
-
function getCurrentName(decl) {
|
|
282
|
-
const name = decl.getName();
|
|
283
|
-
return name.replace(/^"|"$/g, "");
|
|
284
|
-
}
|
|
285
|
-
var renameIdentifiersTransform = {
|
|
286
|
-
name: "rename-identifiers",
|
|
287
|
-
transform(ctx) {
|
|
288
|
-
const { sourceFile, stats } = ctx;
|
|
289
|
-
const renames = [];
|
|
290
|
-
for (const statement of sourceFile.getStatements()) {
|
|
291
|
-
if (!Node.isVariableStatement(statement)) {
|
|
292
|
-
continue;
|
|
293
|
-
}
|
|
294
|
-
if (!statement.hasExportKeyword()) {
|
|
295
|
-
continue;
|
|
296
|
-
}
|
|
297
|
-
for (const decl of statement.getDeclarations()) {
|
|
298
|
-
const initializer = decl.getInitializer();
|
|
299
|
-
if (!initializer) {
|
|
300
|
-
continue;
|
|
301
|
-
}
|
|
302
|
-
if (!isPgFactoryCall(initializer)) {
|
|
303
|
-
continue;
|
|
304
|
-
}
|
|
305
|
-
const tableName = extractFactoryName(initializer);
|
|
306
|
-
if (!tableName) {
|
|
307
|
-
continue;
|
|
308
|
-
}
|
|
309
|
-
const currentName = getCurrentName(decl);
|
|
310
|
-
const sanitized = sanitizeIdentifier(tableName);
|
|
311
|
-
if (sanitized !== currentName) {
|
|
312
|
-
renames.push({ decl, from: currentName, to: sanitized });
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
for (const { decl, from, to } of renames.reverse()) {
|
|
317
|
-
try {
|
|
318
|
-
decl.rename(to);
|
|
319
|
-
stats.renamedIdentifiers.push({ from, to });
|
|
320
|
-
} catch (error) {
|
|
321
|
-
console.warn(`[rename-identifiers] Failed to rename "${from}" to "${to}":`, error);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
stats.renamedIdentifiers.reverse();
|
|
325
|
-
}
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
// src/commands/db/gen-dbschema/transforms/ast/remove-pg-schema.ts
|
|
329
|
-
import { Node as Node2 } from "ts-morph";
|
|
330
|
-
var removePgSchemaTransform = {
|
|
331
|
-
name: "remove-pg-schema",
|
|
332
|
-
transform(ctx) {
|
|
333
|
-
const { sourceFile, stats } = ctx;
|
|
334
|
-
const statementsToRemove = [];
|
|
335
|
-
for (const statement of sourceFile.getStatements()) {
|
|
336
|
-
if (!Node2.isVariableStatement(statement)) {
|
|
337
|
-
continue;
|
|
338
|
-
}
|
|
339
|
-
for (const decl of statement.getDeclarations()) {
|
|
340
|
-
const initializer = decl.getInitializer();
|
|
341
|
-
if (!initializer || !Node2.isCallExpression(initializer)) {
|
|
342
|
-
continue;
|
|
343
|
-
}
|
|
344
|
-
const calleeText = initializer.getExpression().getText();
|
|
345
|
-
if (calleeText === "pgSchema") {
|
|
346
|
-
statementsToRemove.push(statement);
|
|
347
|
-
stats.removedPgSchemas++;
|
|
348
|
-
break;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
for (const statement of statementsToRemove.reverse()) {
|
|
353
|
-
statement.remove();
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
};
|
|
357
256
|
|
|
358
|
-
// src/commands/db/gen-dbschema/
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
if (
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
if (!Node3.isPropertyAccessExpression(expression)) {
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
const objectExpr = expression.getExpression();
|
|
380
|
-
const methodName = expression.getName();
|
|
381
|
-
const pgFactoryName = SCHEMA_METHOD_TO_PG[methodName];
|
|
382
|
-
if (!pgFactoryName) {
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
if (!Node3.isIdentifier(objectExpr)) {
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
expression.replaceWithText(pgFactoryName);
|
|
389
|
-
stats.convertedSchemaCalls++;
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
};
|
|
393
|
-
|
|
394
|
-
// src/commands/db/gen-dbschema/transforms/ast/patch-defects.ts
|
|
395
|
-
import { Node as Node4 } from "ts-morph";
|
|
396
|
-
var patchDefectsTransform = {
|
|
397
|
-
name: "patch-defects",
|
|
398
|
-
transform(ctx) {
|
|
399
|
-
const { sourceFile, stats } = ctx;
|
|
400
|
-
sourceFile.forEachDescendant((node) => {
|
|
401
|
-
if (!Node4.isCallExpression(node)) {
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
const expr = node.getExpression();
|
|
405
|
-
if (!Node4.isPropertyAccessExpression(expr)) {
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
if (expr.getName() !== "default") {
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
const args = node.getArguments();
|
|
412
|
-
if (args.length !== 1) {
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
const arg = args[0];
|
|
416
|
-
if (Node4.isStringLiteral(arg)) {
|
|
417
|
-
const text = arg.getLiteralText();
|
|
418
|
-
if (text === "") {
|
|
419
|
-
stats.patchedDefects++;
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
};
|
|
425
|
-
|
|
426
|
-
// src/commands/db/gen-dbschema/transforms/ast/replace-unknown.ts
|
|
427
|
-
import { Node as Node5 } from "ts-morph";
|
|
428
|
-
var KNOWN_TYPE_FACTORIES = {
|
|
429
|
-
user_profile: "userProfile",
|
|
430
|
-
file_attachment: "fileAttachment"
|
|
431
|
-
};
|
|
432
|
-
var replaceUnknownTransform = {
|
|
433
|
-
name: "replace-unknown",
|
|
434
|
-
transform(ctx) {
|
|
435
|
-
const { sourceFile, stats } = ctx;
|
|
436
|
-
const fullText = sourceFile.getFullText();
|
|
437
|
-
sourceFile.forEachDescendant((node) => {
|
|
438
|
-
if (!Node5.isCallExpression(node)) {
|
|
439
|
-
return;
|
|
440
|
-
}
|
|
441
|
-
const expression = node.getExpression();
|
|
442
|
-
if (!Node5.isIdentifier(expression) || expression.getText() !== "unknown") {
|
|
443
|
-
return;
|
|
444
|
-
}
|
|
445
|
-
const nodeStart = node.getStart();
|
|
446
|
-
const textBefore = fullText.slice(Math.max(0, nodeStart - 500), nodeStart);
|
|
447
|
-
const lines = textBefore.split("\n");
|
|
448
|
-
let factoryName = "text";
|
|
449
|
-
let foundKnownType = false;
|
|
450
|
-
for (let i = lines.length - 1; i >= Math.max(0, lines.length - 5); i--) {
|
|
451
|
-
const line = lines[i];
|
|
452
|
-
const todoMatch = line.match(/\/\/ TODO: failed to parse database type '(?:\w+\.)?([\w_]+)(\[\])?'/);
|
|
453
|
-
if (todoMatch) {
|
|
454
|
-
const typeName = todoMatch[1];
|
|
455
|
-
if (KNOWN_TYPE_FACTORIES[typeName]) {
|
|
456
|
-
factoryName = KNOWN_TYPE_FACTORIES[typeName];
|
|
457
|
-
foundKnownType = true;
|
|
458
|
-
}
|
|
459
|
-
break;
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
expression.replaceWithText(factoryName);
|
|
463
|
-
if (foundKnownType) {
|
|
464
|
-
stats.replacedUnknown++;
|
|
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;
|
|
465
275
|
} else {
|
|
466
|
-
|
|
276
|
+
unmatched.push(line.trim());
|
|
277
|
+
result.push(line);
|
|
467
278
|
}
|
|
468
|
-
});
|
|
469
|
-
const todoCommentRegex = /\/\/ TODO: failed to parse database type '[^']+'\s*\n/g;
|
|
470
|
-
const currentText = sourceFile.getFullText();
|
|
471
|
-
const cleanedText = currentText.replace(todoCommentRegex, "");
|
|
472
|
-
if (cleanedText !== currentText) {
|
|
473
|
-
sourceFile.replaceWithText(cleanedText);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
};
|
|
477
|
-
|
|
478
|
-
// src/commands/db/gen-dbschema/transforms/ast/replace-timestamp.ts
|
|
479
|
-
import { Node as Node6 } from "ts-morph";
|
|
480
|
-
function checkTimestampOptions(optionsArg) {
|
|
481
|
-
let hasWithTimezone = false;
|
|
482
|
-
let hasModeString = false;
|
|
483
|
-
for (const prop of optionsArg.getProperties()) {
|
|
484
|
-
if (!Node6.isPropertyAssignment(prop)) {
|
|
485
279
|
continue;
|
|
486
280
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
if (propName === "withTimezone") {
|
|
490
|
-
if (Node6.isTrueLiteral(initializer)) {
|
|
491
|
-
hasWithTimezone = true;
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
if (propName === "mode") {
|
|
495
|
-
if (Node6.isStringLiteral(initializer) && initializer.getLiteralText() === "string") {
|
|
496
|
-
hasModeString = true;
|
|
497
|
-
}
|
|
281
|
+
if (line.includes("unknown(")) {
|
|
282
|
+
unmatched.push(line.trim());
|
|
498
283
|
}
|
|
284
|
+
result.push(line);
|
|
499
285
|
}
|
|
500
|
-
return
|
|
286
|
+
return {
|
|
287
|
+
text: result.join("\n"),
|
|
288
|
+
replaced,
|
|
289
|
+
unmatched
|
|
290
|
+
};
|
|
501
291
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
const { sourceFile, stats } = ctx;
|
|
506
|
-
const replacements = [];
|
|
507
|
-
sourceFile.forEachDescendant((node) => {
|
|
508
|
-
if (!Node6.isCallExpression(node)) {
|
|
509
|
-
return;
|
|
510
|
-
}
|
|
511
|
-
const expression = node.getExpression();
|
|
512
|
-
if (!Node6.isIdentifier(expression) || expression.getText() !== "timestamp") {
|
|
513
|
-
return;
|
|
514
|
-
}
|
|
515
|
-
const args = node.getArguments();
|
|
516
|
-
if (args.length === 2) {
|
|
517
|
-
const [fieldArg, optionsArg] = args;
|
|
518
|
-
if (!Node6.isStringLiteral(fieldArg)) {
|
|
519
|
-
return;
|
|
520
|
-
}
|
|
521
|
-
if (!Node6.isObjectLiteralExpression(optionsArg)) {
|
|
522
|
-
return;
|
|
523
|
-
}
|
|
524
|
-
if (checkTimestampOptions(optionsArg)) {
|
|
525
|
-
const quote = fieldArg.getQuoteKind() === 1 ? '"' : "'";
|
|
526
|
-
const fieldName = fieldArg.getLiteralText();
|
|
527
|
-
replacements.push({
|
|
528
|
-
node,
|
|
529
|
-
replacement: `customTimestamptz(${quote}${fieldName}${quote})`
|
|
530
|
-
});
|
|
531
|
-
}
|
|
532
|
-
return;
|
|
533
|
-
}
|
|
534
|
-
if (args.length === 1) {
|
|
535
|
-
const [optionsArg] = args;
|
|
536
|
-
if (!Node6.isObjectLiteralExpression(optionsArg)) {
|
|
537
|
-
return;
|
|
538
|
-
}
|
|
539
|
-
if (checkTimestampOptions(optionsArg)) {
|
|
540
|
-
replacements.push({
|
|
541
|
-
node,
|
|
542
|
-
replacement: "customTimestamptz()"
|
|
543
|
-
});
|
|
544
|
-
}
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
});
|
|
548
|
-
for (const { node, replacement } of replacements.reverse()) {
|
|
549
|
-
node.replaceWithText(replacement);
|
|
550
|
-
stats.replacedTimestamp++;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
};
|
|
554
|
-
|
|
555
|
-
// src/commands/db/gen-dbschema/transforms/ast/replace-default-now.ts
|
|
556
|
-
import { Node as Node7 } from "ts-morph";
|
|
557
|
-
var replaceDefaultNowTransform = {
|
|
558
|
-
name: "replace-default-now",
|
|
559
|
-
transform(ctx) {
|
|
560
|
-
const { sourceFile, stats } = ctx;
|
|
561
|
-
sourceFile.forEachDescendant((node) => {
|
|
562
|
-
if (!Node7.isCallExpression(node)) {
|
|
563
|
-
return;
|
|
564
|
-
}
|
|
565
|
-
const expression = node.getExpression();
|
|
566
|
-
if (!Node7.isPropertyAccessExpression(expression)) {
|
|
567
|
-
return;
|
|
568
|
-
}
|
|
569
|
-
if (expression.getName() !== "defaultNow") {
|
|
570
|
-
return;
|
|
571
|
-
}
|
|
572
|
-
if (node.getArguments().length !== 0) {
|
|
573
|
-
return;
|
|
574
|
-
}
|
|
575
|
-
const objectExpr = expression.getExpression();
|
|
576
|
-
const objectText = objectExpr.getText();
|
|
577
|
-
node.replaceWithText(`${objectText}.default(sql\`CURRENT_TIMESTAMP\`)`);
|
|
578
|
-
stats.replacedDefaultNow++;
|
|
579
|
-
});
|
|
292
|
+
function replaceFollowingUnknown(nextLine, factory) {
|
|
293
|
+
if (!nextLine || !nextLine.includes("unknown(")) {
|
|
294
|
+
return void 0;
|
|
580
295
|
}
|
|
581
|
-
};
|
|
296
|
+
return nextLine.replace("unknown(", `${factory}(`);
|
|
297
|
+
}
|
|
582
298
|
|
|
583
|
-
// src/commands/db/gen-dbschema/
|
|
584
|
-
import
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
var TABLE_FACTORIES = ["pgTable", "pgView", "pgMaterializedView", "table", "view", "materializedView"];
|
|
592
|
-
var removeSystemFieldsTransform = {
|
|
593
|
-
name: "remove-system-fields",
|
|
594
|
-
transform(ctx) {
|
|
595
|
-
const { sourceFile, stats } = ctx;
|
|
596
|
-
sourceFile.forEachDescendant((node) => {
|
|
597
|
-
if (!Node8.isCallExpression(node)) {
|
|
598
|
-
return;
|
|
599
|
-
}
|
|
600
|
-
const expression = node.getExpression();
|
|
601
|
-
let factoryName = "";
|
|
602
|
-
if (Node8.isIdentifier(expression)) {
|
|
603
|
-
factoryName = expression.getText();
|
|
604
|
-
} else if (Node8.isPropertyAccessExpression(expression)) {
|
|
605
|
-
factoryName = expression.getName();
|
|
606
|
-
}
|
|
607
|
-
if (!TABLE_FACTORIES.includes(factoryName)) {
|
|
608
|
-
return;
|
|
609
|
-
}
|
|
610
|
-
const args = node.getArguments();
|
|
611
|
-
if (args.length < 2) {
|
|
612
|
-
return;
|
|
613
|
-
}
|
|
614
|
-
const columnsArg = args[1];
|
|
615
|
-
if (!Node8.isObjectLiteralExpression(columnsArg)) {
|
|
616
|
-
return;
|
|
617
|
-
}
|
|
618
|
-
const fieldNames = /* @__PURE__ */ new Set();
|
|
619
|
-
const properties = columnsArg.getProperties();
|
|
620
|
-
for (const prop of properties) {
|
|
621
|
-
if (!Node8.isPropertyAssignment(prop)) {
|
|
622
|
-
continue;
|
|
623
|
-
}
|
|
624
|
-
const nameNode = prop.getNameNode();
|
|
625
|
-
let fieldName = "";
|
|
626
|
-
if (Node8.isStringLiteral(nameNode)) {
|
|
627
|
-
fieldName = nameNode.getLiteralText();
|
|
628
|
-
} else if (Node8.isIdentifier(nameNode)) {
|
|
629
|
-
fieldName = nameNode.getText();
|
|
630
|
-
}
|
|
631
|
-
if (fieldName) {
|
|
632
|
-
fieldNames.add(fieldName);
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
const propsToRemove = [];
|
|
636
|
-
for (const prop of properties) {
|
|
637
|
-
if (!Node8.isPropertyAssignment(prop)) {
|
|
638
|
-
continue;
|
|
639
|
-
}
|
|
640
|
-
const nameNode = prop.getNameNode();
|
|
641
|
-
let fieldName = "";
|
|
642
|
-
if (Node8.isStringLiteral(nameNode)) {
|
|
643
|
-
fieldName = nameNode.getLiteralText();
|
|
644
|
-
} else if (Node8.isIdentifier(nameNode)) {
|
|
645
|
-
fieldName = nameNode.getText();
|
|
646
|
-
}
|
|
647
|
-
const businessField = SYSTEM_TO_BUSINESS[fieldName];
|
|
648
|
-
if (businessField && fieldNames.has(businessField)) {
|
|
649
|
-
propsToRemove.push(prop);
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
for (const prop of propsToRemove) {
|
|
653
|
-
const leadingCommentRanges = prop.getLeadingCommentRanges();
|
|
654
|
-
for (const comment of leadingCommentRanges) {
|
|
655
|
-
const commentText = comment.getText();
|
|
656
|
-
if (commentText.includes("System field:")) {
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
prop.remove();
|
|
660
|
-
stats.removedSystemFields++;
|
|
661
|
-
}
|
|
662
|
-
});
|
|
299
|
+
// src/commands/db/gen-dbschema/helper/imports.ts
|
|
300
|
+
import fs from "fs";
|
|
301
|
+
import { fileURLToPath } from "url";
|
|
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;
|
|
663
307
|
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
var tweakImportsTransform = {
|
|
670
|
-
name: "tweak-imports",
|
|
671
|
-
transform(ctx) {
|
|
672
|
-
const { sourceFile, stats } = ctx;
|
|
673
|
-
const fullText = sourceFile.getFullText();
|
|
674
|
-
const imports = sourceFile.getImportDeclarations();
|
|
675
|
-
const pgCoreImport = imports.find((imp) => {
|
|
676
|
-
const moduleSpec = imp.getModuleSpecifierValue();
|
|
677
|
-
return moduleSpec === "drizzle-orm/pg-core";
|
|
678
|
-
});
|
|
679
|
-
if (!pgCoreImport) {
|
|
680
|
-
return;
|
|
681
|
-
}
|
|
682
|
-
const namedImports = pgCoreImport.getNamedImports();
|
|
683
|
-
const currentImports = namedImports.map((ni) => ni.getName());
|
|
684
|
-
const toRemove = [];
|
|
685
|
-
const toAdd = [];
|
|
686
|
-
for (const identifier of REMOVE_IMPORTS) {
|
|
687
|
-
if (currentImports.includes(identifier)) {
|
|
688
|
-
toRemove.push(identifier);
|
|
689
|
-
stats.removedImports.push(identifier);
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
if (currentImports.includes("timestamp")) {
|
|
693
|
-
const timestampUsed = /timestamp\s*\(/.test(fullText);
|
|
694
|
-
if (!timestampUsed) {
|
|
695
|
-
toRemove.push("timestamp");
|
|
696
|
-
stats.removedImports.push("timestamp");
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
for (const factory of PG_FACTORIES2) {
|
|
700
|
-
if (!currentImports.includes(factory)) {
|
|
701
|
-
const pattern = new RegExp(`${factory}\\s*\\(`);
|
|
702
|
-
if (pattern.test(fullText)) {
|
|
703
|
-
toAdd.push(factory);
|
|
704
|
-
stats.addedImports.push(factory);
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
for (const identifier of toRemove) {
|
|
709
|
-
const freshNamedImports = pgCoreImport.getNamedImports();
|
|
710
|
-
const namedImport = freshNamedImports.find((ni) => ni.getName() === identifier);
|
|
711
|
-
if (namedImport) {
|
|
712
|
-
namedImport.remove();
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
for (const identifier of toAdd) {
|
|
716
|
-
pgCoreImport.addNamedImport(identifier);
|
|
717
|
-
}
|
|
718
|
-
if (fullText.includes("sql`CURRENT_TIMESTAMP`")) {
|
|
719
|
-
const drizzleOrmImport = imports.find((imp) => {
|
|
720
|
-
const moduleSpec = imp.getModuleSpecifierValue();
|
|
721
|
-
return moduleSpec === "drizzle-orm";
|
|
722
|
-
});
|
|
723
|
-
if (!drizzleOrmImport) {
|
|
724
|
-
sourceFile.addImportDeclaration({
|
|
725
|
-
moduleSpecifier: "drizzle-orm",
|
|
726
|
-
namedImports: ["sql"]
|
|
727
|
-
});
|
|
728
|
-
stats.addedImports.push("sql");
|
|
729
|
-
} else {
|
|
730
|
-
const hasSql = drizzleOrmImport.getNamedImports().some((ni) => ni.getName() === "sql");
|
|
731
|
-
if (!hasSql) {
|
|
732
|
-
drizzleOrmImport.addNamedImport("sql");
|
|
733
|
-
stats.addedImports.push("sql");
|
|
734
|
-
}
|
|
735
|
-
}
|
|
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);
|
|
736
313
|
}
|
|
737
|
-
|
|
738
|
-
};
|
|
739
|
-
|
|
740
|
-
// src/commands/db/gen-dbschema/transforms/ast/index.ts
|
|
741
|
-
var defaultTransforms = [
|
|
742
|
-
patchDefectsTransform,
|
|
743
|
-
// #2 Fix syntax errors first
|
|
744
|
-
removePgSchemaTransform,
|
|
745
|
-
// #3 Remove pgSchema declarations
|
|
746
|
-
convertSchemaCallsTransform,
|
|
747
|
-
// #4 Convert schema.xxx() to pgXxx()
|
|
748
|
-
renameIdentifiersTransform,
|
|
749
|
-
// #5+#6 Rename identifiers (auto-updates refs)
|
|
750
|
-
replaceUnknownTransform,
|
|
751
|
-
// #7 Replace unknown types
|
|
752
|
-
replaceTimestampTransform,
|
|
753
|
-
// #8 Replace timestamp
|
|
754
|
-
replaceDefaultNowTransform,
|
|
755
|
-
// #9 Replace .defaultNow()
|
|
756
|
-
removeSystemFieldsTransform,
|
|
757
|
-
// #10 Remove conflicting system fields
|
|
758
|
-
tweakImportsTransform
|
|
759
|
-
// #12 Adjust imports
|
|
760
|
-
];
|
|
761
|
-
|
|
762
|
-
// src/commands/db/gen-dbschema/transforms/text/patch-defects.ts
|
|
763
|
-
function patchDefects(source) {
|
|
764
|
-
let fixed = 0;
|
|
765
|
-
const renamedQuotedExports = [];
|
|
766
|
-
let text = source;
|
|
767
|
-
text = text.replace(/\.default\('\)/g, () => {
|
|
768
|
-
fixed += 1;
|
|
769
|
-
return `.default('')`;
|
|
770
|
-
});
|
|
771
|
-
const quotedExportPattern = /export const\s+"([^"]+)"\s*=/g;
|
|
772
|
-
text = text.replace(quotedExportPattern, (match, quotedName) => {
|
|
773
|
-
const sanitized = sanitizeIdentifier(quotedName);
|
|
774
|
-
renamedQuotedExports.push({ from: quotedName, to: sanitized });
|
|
775
|
-
fixed += 1;
|
|
776
|
-
return `export const ${sanitized} =`;
|
|
314
|
+
return true;
|
|
777
315
|
});
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
const callPattern = new RegExp(`"${escaped}"\\s*\\(`, "g");
|
|
781
|
-
text = text.replace(callPattern, `${to}(`);
|
|
782
|
-
const dotPattern = new RegExp(`"${escaped}"\\s*\\.`, "g");
|
|
783
|
-
text = text.replace(dotPattern, `${to}.`);
|
|
316
|
+
if (source.includes("pgTable(") && !filteredIdentifiers.includes("pgTable")) {
|
|
317
|
+
filteredIdentifiers.push("pgTable");
|
|
784
318
|
}
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
// src/commands/db/gen-dbschema/transforms/text/header.ts
|
|
789
|
-
var ESLINT_DISABLE = "/* eslint-disable */";
|
|
790
|
-
var HEADER_COMMENT = "/** auto generated, do not edit */";
|
|
791
|
-
var FULL_HEADER = `${ESLINT_DISABLE}
|
|
792
|
-
${HEADER_COMMENT}`;
|
|
793
|
-
function ensureHeader(source) {
|
|
794
|
-
let trimmed = source;
|
|
795
|
-
const headerPatterns = [
|
|
796
|
-
/^\/\*\s*eslint-disable\s*\*\/\s*\n?/,
|
|
797
|
-
/^\/\*\*\s*auto generated[^*]*\*\/\s*\n?/
|
|
798
|
-
];
|
|
799
|
-
for (const pattern of headerPatterns) {
|
|
800
|
-
while (pattern.test(trimmed)) {
|
|
801
|
-
trimmed = trimmed.replace(pattern, "");
|
|
802
|
-
}
|
|
319
|
+
if (source.includes("pgView(") && !filteredIdentifiers.includes("pgView")) {
|
|
320
|
+
filteredIdentifiers.push("pgView");
|
|
803
321
|
}
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
${trimmed}`;
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
// src/commands/db/gen-dbschema/transforms/text/system-comments.ts
|
|
810
|
-
var SYSTEM_FIELD_COMMENTS = {
|
|
811
|
-
_created_at: "Creation time",
|
|
812
|
-
_created_by: "Creator",
|
|
813
|
-
_updated_at: "Update time",
|
|
814
|
-
_updated_by: "Updater"
|
|
815
|
-
};
|
|
816
|
-
function addSystemFieldComments(source) {
|
|
817
|
-
const lines = source.split("\n");
|
|
818
|
-
for (let i = 0; i < lines.length; i += 1) {
|
|
819
|
-
const line = lines[i];
|
|
820
|
-
const entry = Object.entries(SYSTEM_FIELD_COMMENTS).find(
|
|
821
|
-
([key]) => line.includes(`"${key}"`) || line.includes(`'${key}'`)
|
|
822
|
-
);
|
|
823
|
-
if (!entry) {
|
|
824
|
-
continue;
|
|
825
|
-
}
|
|
826
|
-
const [, description] = entry;
|
|
827
|
-
const previousLine = lines[i - 1]?.trim() ?? "";
|
|
828
|
-
if (previousLine.startsWith("//") && previousLine.includes("System field")) {
|
|
829
|
-
continue;
|
|
830
|
-
}
|
|
831
|
-
const indentMatch = line.match(/^\s*/);
|
|
832
|
-
const indent = indentMatch ? indentMatch[0] : "";
|
|
833
|
-
const comment = `${indent}// System field: ${description} (auto-filled, do not modify)`;
|
|
834
|
-
lines.splice(i, 0, comment);
|
|
835
|
-
i += 1;
|
|
322
|
+
if (source.includes("pgMaterializedView(") && !filteredIdentifiers.includes("pgMaterializedView")) {
|
|
323
|
+
filteredIdentifiers.push("pgMaterializedView");
|
|
836
324
|
}
|
|
837
|
-
|
|
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);
|
|
838
334
|
}
|
|
839
|
-
|
|
840
|
-
// src/commands/db/gen-dbschema/transforms/text/inline-types.ts
|
|
841
|
-
import fs from "fs";
|
|
842
|
-
import { fileURLToPath } from "url";
|
|
843
335
|
function inlineCustomTypes(source) {
|
|
844
|
-
|
|
336
|
+
const text = source.replace(/import \{[^}]*\} from ["']\.\/types["'];?\n*/g, "");
|
|
845
337
|
const templatePath = resolveTemplateTypesPath();
|
|
846
338
|
if (!templatePath) {
|
|
847
|
-
console.warn("[
|
|
339
|
+
console.warn("[postprocess-drizzle-schema] Template types file not found.");
|
|
848
340
|
return text;
|
|
849
341
|
}
|
|
850
|
-
|
|
851
|
-
return inlineFromTemplate(text, templateContent);
|
|
342
|
+
return inlineFromTemplateContent(text, fs.readFileSync(templatePath, "utf8"));
|
|
852
343
|
}
|
|
853
|
-
function
|
|
344
|
+
function inlineFromTemplateContent(text, templateContent) {
|
|
854
345
|
const typeDefinitions = templateContent.replace(/^import\s+.*;\r?\n*/gm, "").trim();
|
|
855
|
-
let text = source;
|
|
856
346
|
const needsSql = typeDefinitions.includes("sql`") && !text.includes("from 'drizzle-orm'") && !text.includes('from "drizzle-orm"');
|
|
857
347
|
const needsCustomType = typeDefinitions.includes("customType<") && !text.includes("customType");
|
|
858
348
|
if (needsCustomType) {
|
|
859
349
|
text = ensureImportIdentifier(text, "drizzle-orm/pg-core", "customType");
|
|
860
350
|
}
|
|
861
|
-
if (needsSql) {
|
|
351
|
+
if (needsSql && !text.includes("from 'drizzle-orm'") && !text.includes('from "drizzle-orm"')) {
|
|
862
352
|
const importMatch = text.match(/^import [\s\S]*?from ["']drizzle-orm\/pg-core["'];?\n/m);
|
|
863
353
|
if (importMatch) {
|
|
864
354
|
const insertPoint = text.indexOf(importMatch[0]) + importMatch[0].length;
|
|
865
355
|
text = text.slice(0, insertPoint) + "import { sql } from 'drizzle-orm';\n" + text.slice(insertPoint);
|
|
866
356
|
}
|
|
867
357
|
}
|
|
868
|
-
const headerPrefix = `${
|
|
358
|
+
const headerPrefix = `${HEADER_COMMENT}
|
|
869
359
|
`;
|
|
870
360
|
let insertionPoint = 0;
|
|
871
361
|
if (text.startsWith(headerPrefix)) {
|
|
@@ -899,8 +389,8 @@ function ensureImportIdentifier(source, packageName, identifier) {
|
|
|
899
389
|
}
|
|
900
390
|
function resolveTemplateTypesPath() {
|
|
901
391
|
const candidates = [
|
|
902
|
-
new URL("
|
|
903
|
-
new URL("
|
|
392
|
+
new URL("../template/types.ts", import.meta.url),
|
|
393
|
+
new URL("./gen-dbschema-template/types.ts", import.meta.url)
|
|
904
394
|
];
|
|
905
395
|
for (const url of candidates) {
|
|
906
396
|
const p = fileURLToPath(url);
|
|
@@ -911,9 +401,131 @@ function resolveTemplateTypesPath() {
|
|
|
911
401
|
return void 0;
|
|
912
402
|
}
|
|
913
403
|
|
|
914
|
-
// src/commands/db/gen-dbschema/
|
|
404
|
+
// src/commands/db/gen-dbschema/helper/system-fields.ts
|
|
405
|
+
function addSystemFieldComments(source) {
|
|
406
|
+
const commentMap = {
|
|
407
|
+
"_created_at": "Creation time",
|
|
408
|
+
"_created_by": "Creator",
|
|
409
|
+
"_updated_at": "Update time",
|
|
410
|
+
"_updated_by": "Updater"
|
|
411
|
+
};
|
|
412
|
+
const lines = source.split("\n");
|
|
413
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
414
|
+
const line = lines[i];
|
|
415
|
+
const entry = Object.entries(commentMap).find(([key]) => line.includes(`"${key}"`));
|
|
416
|
+
if (!entry) {
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
const [, description] = entry;
|
|
420
|
+
const previousLine = lines[i - 1]?.trim() ?? "";
|
|
421
|
+
if (previousLine.startsWith("//") && previousLine.includes("System field")) {
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
const indentMatch = line.match(/^\s*/);
|
|
425
|
+
const indent = indentMatch ? indentMatch[0] : "";
|
|
426
|
+
const comment = `${indent}// System field: ${description} (auto-filled, do not modify)`;
|
|
427
|
+
lines.splice(i, 0, comment);
|
|
428
|
+
i += 1;
|
|
429
|
+
}
|
|
430
|
+
return lines.join("\n");
|
|
431
|
+
}
|
|
432
|
+
function removeConflictingSystemFields(source) {
|
|
433
|
+
const systemFieldMap = {
|
|
434
|
+
"_created_at": "created_at",
|
|
435
|
+
"_created_by": "created_by",
|
|
436
|
+
"_updated_at": "updated_at",
|
|
437
|
+
"_updated_by": "updated_by"
|
|
438
|
+
};
|
|
439
|
+
const lines = source.split("\n");
|
|
440
|
+
const result = [];
|
|
441
|
+
let inTable = false;
|
|
442
|
+
let tableStartLine = -1;
|
|
443
|
+
const tableBusinessFields = /* @__PURE__ */ new Set();
|
|
444
|
+
let bracketDepth = 0;
|
|
445
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
446
|
+
const line = lines[i];
|
|
447
|
+
if (!inTable && /=\s*(pgTable|pgView|pgMaterializedView)\s*\(/.test(line)) {
|
|
448
|
+
inTable = true;
|
|
449
|
+
tableStartLine = result.length;
|
|
450
|
+
tableBusinessFields.clear();
|
|
451
|
+
bracketDepth = 0;
|
|
452
|
+
}
|
|
453
|
+
if (inTable) {
|
|
454
|
+
for (const char of line) {
|
|
455
|
+
if (char === "{") bracketDepth++;
|
|
456
|
+
if (char === "}") bracketDepth--;
|
|
457
|
+
}
|
|
458
|
+
for (const businessField of Object.values(systemFieldMap)) {
|
|
459
|
+
if (line.includes(`"${businessField}"`) || line.includes(`'${businessField}'`)) {
|
|
460
|
+
tableBusinessFields.add(businessField);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if (bracketDepth === 0 && line.includes(");")) {
|
|
464
|
+
inTable = false;
|
|
465
|
+
const tableEndLine = result.length;
|
|
466
|
+
for (let j = tableStartLine; j <= tableEndLine; j++) {
|
|
467
|
+
const tableLine = result[j] || "";
|
|
468
|
+
let shouldRemove = false;
|
|
469
|
+
for (const [systemField, businessField] of Object.entries(systemFieldMap)) {
|
|
470
|
+
if (tableBusinessFields.has(businessField)) {
|
|
471
|
+
if (tableLine.includes(`"${systemField}"`) || tableLine.includes(`'${systemField}'`)) {
|
|
472
|
+
shouldRemove = true;
|
|
473
|
+
if (j > 0 && result[j - 1]?.includes("// System field:")) {
|
|
474
|
+
result[j - 1] = null;
|
|
475
|
+
}
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
if (shouldRemove) {
|
|
481
|
+
result[j] = null;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
result.push(line);
|
|
487
|
+
}
|
|
488
|
+
return result.filter((line) => line !== null).join("\n");
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// src/commands/db/gen-dbschema/helper/patch-helper.ts
|
|
492
|
+
function patchDrizzleKitDefects(source) {
|
|
493
|
+
let fixed = 0;
|
|
494
|
+
const text = source.replace(/\.default\('\)/g, () => {
|
|
495
|
+
fixed += 1;
|
|
496
|
+
return `.default('')`;
|
|
497
|
+
});
|
|
498
|
+
return { text, fixed };
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// src/commands/db/gen-dbschema/helper/timestamp-replacement.ts
|
|
502
|
+
function replaceTimestampWithCustomTypes(source) {
|
|
503
|
+
let replaced = 0;
|
|
504
|
+
const pattern = /timestamp\((['"])(.*?)\1,\s*(\{[^}]*\})\)/g;
|
|
505
|
+
const text = source.replace(pattern, (match, quote, fieldName, options) => {
|
|
506
|
+
const hasWithTimezone = /withTimezone:\s*true/.test(options);
|
|
507
|
+
const hasModeString = /mode:\s*['"]string['"]/.test(options);
|
|
508
|
+
if (hasWithTimezone && hasModeString) {
|
|
509
|
+
replaced += 1;
|
|
510
|
+
return `customTimestamptz(${quote}${fieldName}${quote})`;
|
|
511
|
+
}
|
|
512
|
+
return match;
|
|
513
|
+
});
|
|
514
|
+
return { text, replaced };
|
|
515
|
+
}
|
|
516
|
+
function replaceDefaultNowWithSql(source) {
|
|
517
|
+
let replaced = 0;
|
|
518
|
+
const pattern = /\.defaultNow\(\)/g;
|
|
519
|
+
const text = source.replace(pattern, () => {
|
|
520
|
+
replaced += 1;
|
|
521
|
+
return ".default(sql`CURRENT_TIMESTAMP`)";
|
|
522
|
+
});
|
|
523
|
+
return { text, replaced };
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// src/commands/db/gen-dbschema/helper/appendTableAliases.ts
|
|
915
527
|
var TABLE_ALIAS_MARKER = "// table aliases";
|
|
916
|
-
function
|
|
528
|
+
function appendTableAliases(source) {
|
|
917
529
|
const markerIndex = source.indexOf(`
|
|
918
530
|
${TABLE_ALIAS_MARKER}`);
|
|
919
531
|
const base = markerIndex === -1 ? source : source.slice(0, markerIndex);
|
|
@@ -935,100 +547,62 @@ ${aliasLines}
|
|
|
935
547
|
`;
|
|
936
548
|
}
|
|
937
549
|
|
|
938
|
-
// src/commands/db/gen-dbschema/transforms/text/format.ts
|
|
939
|
-
function formatSource(source) {
|
|
940
|
-
let text = source;
|
|
941
|
-
text = text.replace(/\r\n/g, "\n");
|
|
942
|
-
text = text.replace(/\n{3,}/g, "\n\n");
|
|
943
|
-
if (!text.endsWith("\n")) {
|
|
944
|
-
text += "\n";
|
|
945
|
-
}
|
|
946
|
-
return text;
|
|
947
|
-
}
|
|
948
|
-
|
|
949
550
|
// src/commands/db/gen-dbschema/postprocess.ts
|
|
950
|
-
function postprocessSchema(rawSource) {
|
|
951
|
-
const patchResult = patchDefects(rawSource);
|
|
952
|
-
let source = patchResult.text;
|
|
953
|
-
const { sourceFile } = parseSource(source);
|
|
954
|
-
const astStats = applyTransforms(sourceFile, defaultTransforms);
|
|
955
|
-
formatSourceFile(sourceFile);
|
|
956
|
-
source = printSourceFile(sourceFile);
|
|
957
|
-
source = ensureHeader(source);
|
|
958
|
-
source = addSystemFieldComments(source);
|
|
959
|
-
source = inlineCustomTypes(source);
|
|
960
|
-
source = generateTableAliases(source);
|
|
961
|
-
source = formatSource(source);
|
|
962
|
-
return {
|
|
963
|
-
source,
|
|
964
|
-
astStats,
|
|
965
|
-
patchedDefects: patchResult.fixed
|
|
966
|
-
};
|
|
967
|
-
}
|
|
968
|
-
function logStats(result, prefix = "[postprocess]") {
|
|
969
|
-
const { astStats, patchedDefects } = result;
|
|
970
|
-
if (patchedDefects > 0) {
|
|
971
|
-
console.info(`${prefix} Patched ${patchedDefects} syntax defects`);
|
|
972
|
-
}
|
|
973
|
-
if (astStats.removedPgSchemas > 0) {
|
|
974
|
-
console.info(`${prefix} Removed ${astStats.removedPgSchemas} pgSchema declarations`);
|
|
975
|
-
}
|
|
976
|
-
if (astStats.convertedSchemaCalls > 0) {
|
|
977
|
-
console.info(`${prefix} Converted ${astStats.convertedSchemaCalls} schema.xxx() calls`);
|
|
978
|
-
}
|
|
979
|
-
if (astStats.renamedIdentifiers.length > 0) {
|
|
980
|
-
console.info(`${prefix} Renamed ${astStats.renamedIdentifiers.length} identifiers:`);
|
|
981
|
-
for (const { from, to } of astStats.renamedIdentifiers) {
|
|
982
|
-
console.info(`${prefix} ${from} -> ${to}`);
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
if (astStats.replacedUnknown > 0) {
|
|
986
|
-
console.info(`${prefix} Replaced ${astStats.replacedUnknown} unknown types with custom types`);
|
|
987
|
-
}
|
|
988
|
-
if (astStats.fallbackToText > 0) {
|
|
989
|
-
console.info(`${prefix} Replaced ${astStats.fallbackToText} unknown types with text (fallback)`);
|
|
990
|
-
}
|
|
991
|
-
if (astStats.unmatchedUnknown.length > 0) {
|
|
992
|
-
console.warn(`${prefix} Unmatched unknown types:`);
|
|
993
|
-
for (const line of astStats.unmatchedUnknown) {
|
|
994
|
-
console.warn(`${prefix} ${line}`);
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
if (astStats.replacedTimestamp > 0) {
|
|
998
|
-
console.info(`${prefix} Replaced ${astStats.replacedTimestamp} timestamp with customTimestamptz`);
|
|
999
|
-
}
|
|
1000
|
-
if (astStats.replacedDefaultNow > 0) {
|
|
1001
|
-
console.info(`${prefix} Replaced ${astStats.replacedDefaultNow} .defaultNow() calls`);
|
|
1002
|
-
}
|
|
1003
|
-
if (astStats.removedSystemFields > 0) {
|
|
1004
|
-
console.info(`${prefix} Removed ${astStats.removedSystemFields} conflicting system fields`);
|
|
1005
|
-
}
|
|
1006
|
-
if (astStats.addedImports.length > 0) {
|
|
1007
|
-
console.info(`${prefix} Added imports: ${astStats.addedImports.join(", ")}`);
|
|
1008
|
-
}
|
|
1009
|
-
if (astStats.removedImports.length > 0) {
|
|
1010
|
-
console.info(`${prefix} Removed imports: ${astStats.removedImports.join(", ")}`);
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
// src/commands/db/gen-dbschema/index.ts
|
|
1015
551
|
function postprocessDrizzleSchema(targetPath) {
|
|
1016
552
|
const resolvedPath = path.resolve(targetPath);
|
|
1017
|
-
if (!
|
|
553
|
+
if (!fs2.existsSync(resolvedPath)) {
|
|
1018
554
|
console.warn(`[postprocess-drizzle-schema] File not found: ${resolvedPath}`);
|
|
1019
555
|
return void 0;
|
|
1020
556
|
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
557
|
+
let text = fs2.readFileSync(resolvedPath, "utf8");
|
|
558
|
+
text = ensureHeaderComment(text);
|
|
559
|
+
const patchResult = patchDrizzleKitDefects(text);
|
|
560
|
+
text = patchResult.text;
|
|
561
|
+
text = removePgSchemaDeclarations(text);
|
|
562
|
+
const tableConversion = convertSchemaTableInvocations(text);
|
|
563
|
+
text = tableConversion.text;
|
|
564
|
+
const renameResult = renamePgTableConstants(text);
|
|
565
|
+
text = renameResult.text;
|
|
566
|
+
text = updateTableReferenceIdentifiers(text, renameResult.renames);
|
|
567
|
+
const replacement = replaceUnknownColumns(text);
|
|
568
|
+
text = replacement.text;
|
|
569
|
+
const timestampReplacement = replaceTimestampWithCustomTypes(text);
|
|
570
|
+
text = timestampReplacement.text;
|
|
571
|
+
const defaultNowReplacement = replaceDefaultNowWithSql(text);
|
|
572
|
+
text = defaultNowReplacement.text;
|
|
573
|
+
text = removeConflictingSystemFields(text);
|
|
574
|
+
text = addSystemFieldComments(text);
|
|
575
|
+
text = tweakImports(text);
|
|
576
|
+
text = inlineCustomTypes(text);
|
|
577
|
+
text = appendTableAliases(text);
|
|
578
|
+
text = text.replace(/\r?\n/g, "\n");
|
|
579
|
+
text = collapseExtraBlankLines(text);
|
|
580
|
+
fs2.writeFileSync(resolvedPath, text, "utf8");
|
|
581
|
+
if (patchResult.fixed > 0) {
|
|
582
|
+
console.info(`[postprocess-drizzle-schema] Patched ${patchResult.fixed} drizzle-kit defects (.default(') -> .default(''))`);
|
|
583
|
+
}
|
|
584
|
+
if (replacement.replaced > 0) {
|
|
585
|
+
console.info(`[postprocess-drizzle-schema] Replaced ${replacement.replaced} unknown columns`);
|
|
586
|
+
}
|
|
587
|
+
if (replacement.unmatched.length > 0) {
|
|
588
|
+
console.warn("[postprocess-drizzle-schema] Unmatched custom types:", replacement.unmatched.length);
|
|
589
|
+
replacement.unmatched.forEach((line) => console.warn(` ${line}`));
|
|
590
|
+
}
|
|
591
|
+
if (tableConversion.converted > 0) {
|
|
592
|
+
console.info(`[postprocess-drizzle-schema] Converted ${tableConversion.converted} schema.table invocations to pgTable`);
|
|
593
|
+
}
|
|
594
|
+
if (timestampReplacement.replaced > 0) {
|
|
595
|
+
console.info(`[postprocess-drizzle-schema] Replaced ${timestampReplacement.replaced} timestamp fields with customTimestamptz`);
|
|
596
|
+
}
|
|
597
|
+
if (defaultNowReplacement.replaced > 0) {
|
|
598
|
+
console.info(`[postprocess-drizzle-schema] Replaced ${defaultNowReplacement.replaced} .defaultNow() with .default(sql\`CURRENT_TIMESTAMP\`)`);
|
|
599
|
+
}
|
|
1025
600
|
return {
|
|
1026
|
-
replacedUnknown:
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
replacedDefaultNow: result.astStats.replacedDefaultNow
|
|
601
|
+
replacedUnknown: replacement.replaced,
|
|
602
|
+
unmatchedUnknown: replacement.unmatched,
|
|
603
|
+
patchedDefects: patchResult.fixed,
|
|
604
|
+
replacedTimestamps: timestampReplacement.replaced,
|
|
605
|
+
replacedDefaultNow: defaultNowReplacement.replaced
|
|
1032
606
|
};
|
|
1033
607
|
}
|
|
1034
608
|
|
|
@@ -1450,10 +1024,10 @@ export class ${className}Module {}
|
|
|
1450
1024
|
}
|
|
1451
1025
|
|
|
1452
1026
|
// src/commands/db/gen-nest-resource/schema-parser.ts
|
|
1453
|
-
import { Project
|
|
1027
|
+
import { Project, Node } from "ts-morph";
|
|
1454
1028
|
var DrizzleSchemaParser = class {
|
|
1455
1029
|
constructor(projectOptions) {
|
|
1456
|
-
this.project = new
|
|
1030
|
+
this.project = new Project(projectOptions);
|
|
1457
1031
|
}
|
|
1458
1032
|
parseSchemaFile(filePath) {
|
|
1459
1033
|
const sourceFile = this.project.addSourceFileAtPath(filePath);
|
|
@@ -1463,7 +1037,7 @@ var DrizzleSchemaParser = class {
|
|
|
1463
1037
|
const declarations = statement.getDeclarations();
|
|
1464
1038
|
for (const declaration of declarations) {
|
|
1465
1039
|
const initializer = declaration.getInitializer();
|
|
1466
|
-
if (initializer &&
|
|
1040
|
+
if (initializer && Node.isCallExpression(initializer)) {
|
|
1467
1041
|
const expression = initializer.getExpression();
|
|
1468
1042
|
if (expression.getText() === "pgTable") {
|
|
1469
1043
|
const tableInfo = this.parsePgTable(
|
|
@@ -1486,13 +1060,13 @@ var DrizzleSchemaParser = class {
|
|
|
1486
1060
|
}
|
|
1487
1061
|
const tableName = args[0].getText().replace(/['"]/g, "");
|
|
1488
1062
|
const fieldsArg = args[1];
|
|
1489
|
-
if (!
|
|
1063
|
+
if (!Node.isObjectLiteralExpression(fieldsArg)) {
|
|
1490
1064
|
return null;
|
|
1491
1065
|
}
|
|
1492
1066
|
const fields = [];
|
|
1493
1067
|
const properties = fieldsArg.getProperties();
|
|
1494
1068
|
for (const prop of properties) {
|
|
1495
|
-
if (
|
|
1069
|
+
if (Node.isPropertyAssignment(prop)) {
|
|
1496
1070
|
const fieldName = prop.getName();
|
|
1497
1071
|
const initializer = prop.getInitializer();
|
|
1498
1072
|
const leadingComments = prop.getLeadingCommentRanges();
|
|
@@ -1500,7 +1074,7 @@ var DrizzleSchemaParser = class {
|
|
|
1500
1074
|
if (leadingComments.length > 0) {
|
|
1501
1075
|
comment = leadingComments.map((c) => c.getText()).join("\n").replace(/\/\//g, "").trim();
|
|
1502
1076
|
}
|
|
1503
|
-
if (initializer &&
|
|
1077
|
+
if (initializer && Node.isCallExpression(initializer)) {
|
|
1504
1078
|
const fieldInfo = this.parseField(fieldName, initializer, comment);
|
|
1505
1079
|
fields.push(fieldInfo);
|
|
1506
1080
|
}
|
|
@@ -1532,10 +1106,10 @@ var DrizzleSchemaParser = class {
|
|
|
1532
1106
|
parseBaseType(callExpr, fieldInfo) {
|
|
1533
1107
|
let current = callExpr;
|
|
1534
1108
|
let baseCall = null;
|
|
1535
|
-
while (
|
|
1109
|
+
while (Node.isCallExpression(current)) {
|
|
1536
1110
|
baseCall = current;
|
|
1537
1111
|
const expression2 = current.getExpression();
|
|
1538
|
-
if (
|
|
1112
|
+
if (Node.isPropertyAccessExpression(expression2)) {
|
|
1539
1113
|
current = expression2.getExpression();
|
|
1540
1114
|
} else {
|
|
1541
1115
|
break;
|
|
@@ -1546,7 +1120,7 @@ var DrizzleSchemaParser = class {
|
|
|
1546
1120
|
}
|
|
1547
1121
|
const expression = baseCall.getExpression();
|
|
1548
1122
|
let typeName = "";
|
|
1549
|
-
if (
|
|
1123
|
+
if (Node.isPropertyAccessExpression(expression)) {
|
|
1550
1124
|
typeName = expression.getName();
|
|
1551
1125
|
} else {
|
|
1552
1126
|
typeName = expression.getText();
|
|
@@ -1555,25 +1129,25 @@ var DrizzleSchemaParser = class {
|
|
|
1555
1129
|
const args = baseCall.getArguments();
|
|
1556
1130
|
if (args.length > 0) {
|
|
1557
1131
|
const firstArg = args[0];
|
|
1558
|
-
if (
|
|
1132
|
+
if (Node.isStringLiteral(firstArg)) {
|
|
1559
1133
|
fieldInfo.columnName = firstArg.getLiteralText();
|
|
1560
|
-
} else if (
|
|
1134
|
+
} else if (Node.isObjectLiteralExpression(firstArg)) {
|
|
1561
1135
|
this.parseTypeConfig(firstArg, fieldInfo);
|
|
1562
|
-
} else if (
|
|
1136
|
+
} else if (Node.isArrayLiteralExpression(firstArg)) {
|
|
1563
1137
|
fieldInfo.enumValues = firstArg.getElements().map((el) => el.getText().replace(/['"]/g, ""));
|
|
1564
1138
|
}
|
|
1565
1139
|
}
|
|
1566
|
-
if (args.length > 1 &&
|
|
1140
|
+
if (args.length > 1 && Node.isObjectLiteralExpression(args[1])) {
|
|
1567
1141
|
this.parseTypeConfig(args[1], fieldInfo);
|
|
1568
1142
|
}
|
|
1569
1143
|
}
|
|
1570
1144
|
parseTypeConfig(objLiteral, fieldInfo) {
|
|
1571
|
-
if (!
|
|
1145
|
+
if (!Node.isObjectLiteralExpression(objLiteral)) {
|
|
1572
1146
|
return;
|
|
1573
1147
|
}
|
|
1574
1148
|
const properties = objLiteral.getProperties();
|
|
1575
1149
|
for (const prop of properties) {
|
|
1576
|
-
if (
|
|
1150
|
+
if (Node.isPropertyAssignment(prop)) {
|
|
1577
1151
|
const propName = prop.getName();
|
|
1578
1152
|
const value = prop.getInitializer()?.getText();
|
|
1579
1153
|
switch (propName) {
|
|
@@ -1605,9 +1179,9 @@ var DrizzleSchemaParser = class {
|
|
|
1605
1179
|
}
|
|
1606
1180
|
parseCallChain(callExpr, fieldInfo) {
|
|
1607
1181
|
let current = callExpr;
|
|
1608
|
-
while (
|
|
1182
|
+
while (Node.isCallExpression(current)) {
|
|
1609
1183
|
const expression = current.getExpression();
|
|
1610
|
-
if (
|
|
1184
|
+
if (Node.isPropertyAccessExpression(expression)) {
|
|
1611
1185
|
const methodName = expression.getName();
|
|
1612
1186
|
const args = current.getArguments();
|
|
1613
1187
|
switch (methodName) {
|
|
@@ -1704,7 +1278,7 @@ var require2 = createRequire(import.meta.url);
|
|
|
1704
1278
|
async function run(options = {}) {
|
|
1705
1279
|
let exitCode = 0;
|
|
1706
1280
|
const envPath2 = path2.resolve(process.cwd(), ".env");
|
|
1707
|
-
if (
|
|
1281
|
+
if (fs3.existsSync(envPath2)) {
|
|
1708
1282
|
loadEnv({ path: envPath2 });
|
|
1709
1283
|
console.log("[gen-db-schema] \u2713 Loaded .env file");
|
|
1710
1284
|
}
|
|
@@ -1724,7 +1298,7 @@ async function run(options = {}) {
|
|
|
1724
1298
|
path2.resolve(__dirname2, "../../config/drizzle.config.js"),
|
|
1725
1299
|
path2.resolve(__dirname2, "../../../dist/config/drizzle.config.js")
|
|
1726
1300
|
];
|
|
1727
|
-
const configPath = configPathCandidates.find((p) =>
|
|
1301
|
+
const configPath = configPathCandidates.find((p) => fs3.existsSync(p));
|
|
1728
1302
|
console.log("[gen-db-schema] Using drizzle config from:", configPath ?? "(not found)");
|
|
1729
1303
|
if (!configPath) {
|
|
1730
1304
|
console.error("[gen-db-schema] Error: drizzle config not found in CLI package");
|
|
@@ -1736,8 +1310,8 @@ async function run(options = {}) {
|
|
|
1736
1310
|
let lastDir = null;
|
|
1737
1311
|
while (currentDir !== lastDir) {
|
|
1738
1312
|
const pkgJsonPath = path2.join(currentDir, "package.json");
|
|
1739
|
-
if (
|
|
1740
|
-
const pkgJsonRaw =
|
|
1313
|
+
if (fs3.existsSync(pkgJsonPath)) {
|
|
1314
|
+
const pkgJsonRaw = fs3.readFileSync(pkgJsonPath, "utf8");
|
|
1741
1315
|
const pkgJson = JSON.parse(pkgJsonRaw);
|
|
1742
1316
|
if (pkgJson.name === "drizzle-kit") {
|
|
1743
1317
|
const binField = pkgJson.bin;
|
|
@@ -1772,7 +1346,7 @@ async function run(options = {}) {
|
|
|
1772
1346
|
throw new Error(`drizzle-kit introspect failed with status ${result.status}`);
|
|
1773
1347
|
}
|
|
1774
1348
|
const generatedSchema = path2.join(OUT_DIR, "schema.ts");
|
|
1775
|
-
if (!
|
|
1349
|
+
if (!fs3.existsSync(generatedSchema)) {
|
|
1776
1350
|
console.error("[gen-db-schema] schema.ts not generated");
|
|
1777
1351
|
throw new Error("drizzle-kit introspect failed to generate schema.ts");
|
|
1778
1352
|
}
|
|
@@ -1781,8 +1355,8 @@ async function run(options = {}) {
|
|
|
1781
1355
|
console.warn("[gen-db-schema] Unmatched custom types detected:", stats.unmatchedUnknown);
|
|
1782
1356
|
}
|
|
1783
1357
|
console.log("[gen-db-schema] \u2713 Postprocessed schema");
|
|
1784
|
-
|
|
1785
|
-
|
|
1358
|
+
fs3.mkdirSync(path2.dirname(SCHEMA_FILE), { recursive: true });
|
|
1359
|
+
fs3.copyFileSync(generatedSchema, SCHEMA_FILE);
|
|
1786
1360
|
console.log(`[gen-db-schema] \u2713 Copied to ${outputPath}`);
|
|
1787
1361
|
try {
|
|
1788
1362
|
if (options.enableNestModuleGenerate) {
|
|
@@ -1803,8 +1377,8 @@ async function run(options = {}) {
|
|
|
1803
1377
|
console.error("[gen-db-schema] Failed:", err instanceof Error ? err.message : String(err));
|
|
1804
1378
|
exitCode = 1;
|
|
1805
1379
|
} finally {
|
|
1806
|
-
if (
|
|
1807
|
-
|
|
1380
|
+
if (fs3.existsSync(OUT_DIR)) {
|
|
1381
|
+
fs3.rmSync(OUT_DIR, { recursive: true, force: true });
|
|
1808
1382
|
}
|
|
1809
1383
|
process.exit(exitCode);
|
|
1810
1384
|
}
|
|
@@ -1823,7 +1397,7 @@ var genDbSchemaCommand = {
|
|
|
1823
1397
|
|
|
1824
1398
|
// src/commands/sync/run.handler.ts
|
|
1825
1399
|
import path4 from "path";
|
|
1826
|
-
import
|
|
1400
|
+
import fs5 from "fs";
|
|
1827
1401
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1828
1402
|
|
|
1829
1403
|
// src/config/sync.ts
|
|
@@ -1887,14 +1461,14 @@ function genSyncConfig(perms = {}) {
|
|
|
1887
1461
|
}
|
|
1888
1462
|
|
|
1889
1463
|
// src/utils/file-ops.ts
|
|
1890
|
-
import
|
|
1464
|
+
import fs4 from "fs";
|
|
1891
1465
|
import path3 from "path";
|
|
1892
1466
|
function removeLineFromFile(filePath, pattern) {
|
|
1893
|
-
if (!
|
|
1467
|
+
if (!fs4.existsSync(filePath)) {
|
|
1894
1468
|
console.log(`[fullstack-cli] \u25CB ${path3.basename(filePath)} (not found)`);
|
|
1895
1469
|
return false;
|
|
1896
1470
|
}
|
|
1897
|
-
const content =
|
|
1471
|
+
const content = fs4.readFileSync(filePath, "utf-8");
|
|
1898
1472
|
const lines = content.split("\n");
|
|
1899
1473
|
const trimmedPattern = pattern.trim();
|
|
1900
1474
|
const filteredLines = lines.filter((line) => line.trim() !== trimmedPattern);
|
|
@@ -1902,7 +1476,7 @@ function removeLineFromFile(filePath, pattern) {
|
|
|
1902
1476
|
console.log(`[fullstack-cli] \u25CB ${path3.basename(filePath)} (pattern not found: ${pattern})`);
|
|
1903
1477
|
return false;
|
|
1904
1478
|
}
|
|
1905
|
-
|
|
1479
|
+
fs4.writeFileSync(filePath, filteredLines.join("\n"));
|
|
1906
1480
|
console.log(`[fullstack-cli] \u2713 ${path3.basename(filePath)} (removed: ${pattern})`);
|
|
1907
1481
|
return true;
|
|
1908
1482
|
}
|
|
@@ -1918,7 +1492,7 @@ async function run2(options) {
|
|
|
1918
1492
|
process.exit(0);
|
|
1919
1493
|
}
|
|
1920
1494
|
const userPackageJson = path4.join(userProjectRoot, "package.json");
|
|
1921
|
-
if (!
|
|
1495
|
+
if (!fs5.existsSync(userPackageJson)) {
|
|
1922
1496
|
console.log("[fullstack-cli] Skip syncing (not a valid npm project)");
|
|
1923
1497
|
process.exit(0);
|
|
1924
1498
|
}
|
|
@@ -1964,7 +1538,7 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
|
1964
1538
|
}
|
|
1965
1539
|
const srcPath = path4.join(pluginRoot, rule.from);
|
|
1966
1540
|
const destPath = path4.join(userProjectRoot, rule.to);
|
|
1967
|
-
if (!
|
|
1541
|
+
if (!fs5.existsSync(srcPath)) {
|
|
1968
1542
|
console.warn(`[fullstack-cli] Source not found: ${rule.from}`);
|
|
1969
1543
|
return;
|
|
1970
1544
|
}
|
|
@@ -1982,31 +1556,31 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
|
1982
1556
|
}
|
|
1983
1557
|
function syncFile(src, dest, overwrite = true) {
|
|
1984
1558
|
const destDir = path4.dirname(dest);
|
|
1985
|
-
if (!
|
|
1986
|
-
|
|
1559
|
+
if (!fs5.existsSync(destDir)) {
|
|
1560
|
+
fs5.mkdirSync(destDir, { recursive: true });
|
|
1987
1561
|
}
|
|
1988
|
-
if (
|
|
1562
|
+
if (fs5.existsSync(dest) && !overwrite) {
|
|
1989
1563
|
console.log(`[fullstack-cli] \u25CB ${path4.basename(dest)} (skipped, already exists)`);
|
|
1990
1564
|
return;
|
|
1991
1565
|
}
|
|
1992
|
-
|
|
1566
|
+
fs5.copyFileSync(src, dest);
|
|
1993
1567
|
console.log(`[fullstack-cli] \u2713 ${path4.basename(dest)}`);
|
|
1994
1568
|
}
|
|
1995
1569
|
function syncDirectory(src, dest, overwrite = true) {
|
|
1996
|
-
if (!
|
|
1997
|
-
|
|
1570
|
+
if (!fs5.existsSync(dest)) {
|
|
1571
|
+
fs5.mkdirSync(dest, { recursive: true });
|
|
1998
1572
|
}
|
|
1999
|
-
const files =
|
|
1573
|
+
const files = fs5.readdirSync(src);
|
|
2000
1574
|
let count = 0;
|
|
2001
1575
|
files.forEach((file) => {
|
|
2002
1576
|
const srcFile = path4.join(src, file);
|
|
2003
1577
|
const destFile = path4.join(dest, file);
|
|
2004
|
-
const stats =
|
|
1578
|
+
const stats = fs5.statSync(srcFile);
|
|
2005
1579
|
if (stats.isDirectory()) {
|
|
2006
1580
|
syncDirectory(srcFile, destFile, overwrite);
|
|
2007
1581
|
} else {
|
|
2008
|
-
if (overwrite || !
|
|
2009
|
-
|
|
1582
|
+
if (overwrite || !fs5.existsSync(destFile)) {
|
|
1583
|
+
fs5.copyFileSync(srcFile, destFile);
|
|
2010
1584
|
console.log(`[fullstack-cli] \u2713 ${path4.relative(dest, destFile)}`);
|
|
2011
1585
|
count++;
|
|
2012
1586
|
}
|
|
@@ -2017,28 +1591,28 @@ function syncDirectory(src, dest, overwrite = true) {
|
|
|
2017
1591
|
}
|
|
2018
1592
|
}
|
|
2019
1593
|
function appendToFile(src, dest) {
|
|
2020
|
-
const content =
|
|
1594
|
+
const content = fs5.readFileSync(src, "utf-8");
|
|
2021
1595
|
let existingContent = "";
|
|
2022
|
-
if (
|
|
2023
|
-
existingContent =
|
|
1596
|
+
if (fs5.existsSync(dest)) {
|
|
1597
|
+
existingContent = fs5.readFileSync(dest, "utf-8");
|
|
2024
1598
|
}
|
|
2025
1599
|
if (existingContent.includes(content.trim())) {
|
|
2026
1600
|
console.log(`[fullstack-cli] \u25CB ${path4.basename(dest)} (already contains content)`);
|
|
2027
1601
|
return;
|
|
2028
1602
|
}
|
|
2029
|
-
|
|
1603
|
+
fs5.appendFileSync(dest, content);
|
|
2030
1604
|
console.log(`[fullstack-cli] \u2713 ${path4.basename(dest)} (appended)`);
|
|
2031
1605
|
}
|
|
2032
1606
|
function setPermissions(permissions, projectRoot) {
|
|
2033
1607
|
for (const [pattern, mode] of Object.entries(permissions)) {
|
|
2034
1608
|
if (pattern === "**/*.sh") {
|
|
2035
1609
|
const scriptsDir = path4.join(projectRoot, "scripts");
|
|
2036
|
-
if (
|
|
2037
|
-
const files =
|
|
1610
|
+
if (fs5.existsSync(scriptsDir)) {
|
|
1611
|
+
const files = fs5.readdirSync(scriptsDir);
|
|
2038
1612
|
files.forEach((file) => {
|
|
2039
1613
|
if (file.endsWith(".sh")) {
|
|
2040
1614
|
const filePath = path4.join(scriptsDir, file);
|
|
2041
|
-
|
|
1615
|
+
fs5.chmodSync(filePath, mode);
|
|
2042
1616
|
}
|
|
2043
1617
|
});
|
|
2044
1618
|
}
|
|
@@ -2046,16 +1620,16 @@ function setPermissions(permissions, projectRoot) {
|
|
|
2046
1620
|
}
|
|
2047
1621
|
}
|
|
2048
1622
|
function deleteFile(filePath) {
|
|
2049
|
-
if (
|
|
2050
|
-
|
|
1623
|
+
if (fs5.existsSync(filePath)) {
|
|
1624
|
+
fs5.unlinkSync(filePath);
|
|
2051
1625
|
console.log(`[fullstack-cli] \u2713 ${path4.basename(filePath)} (deleted)`);
|
|
2052
1626
|
} else {
|
|
2053
1627
|
console.log(`[fullstack-cli] \u25CB ${path4.basename(filePath)} (not found)`);
|
|
2054
1628
|
}
|
|
2055
1629
|
}
|
|
2056
1630
|
function deleteDirectory(dirPath) {
|
|
2057
|
-
if (
|
|
2058
|
-
|
|
1631
|
+
if (fs5.existsSync(dirPath)) {
|
|
1632
|
+
fs5.rmSync(dirPath, { recursive: true });
|
|
2059
1633
|
console.log(`[fullstack-cli] \u2713 ${path4.basename(dirPath)} (deleted)`);
|
|
2060
1634
|
} else {
|
|
2061
1635
|
console.log(`[fullstack-cli] \u25CB ${path4.basename(dirPath)} (not found)`);
|
|
@@ -2074,7 +1648,7 @@ var syncCommand = {
|
|
|
2074
1648
|
};
|
|
2075
1649
|
|
|
2076
1650
|
// src/commands/action-plugin/utils.ts
|
|
2077
|
-
import
|
|
1651
|
+
import fs6 from "fs";
|
|
2078
1652
|
import path5 from "path";
|
|
2079
1653
|
import { spawnSync as spawnSync2, execSync } from "child_process";
|
|
2080
1654
|
function parsePluginName(input) {
|
|
@@ -2100,11 +1674,11 @@ function getPluginPath(pluginName) {
|
|
|
2100
1674
|
}
|
|
2101
1675
|
function readPackageJson() {
|
|
2102
1676
|
const pkgPath = getPackageJsonPath();
|
|
2103
|
-
if (!
|
|
1677
|
+
if (!fs6.existsSync(pkgPath)) {
|
|
2104
1678
|
throw new Error("package.json not found in current directory");
|
|
2105
1679
|
}
|
|
2106
1680
|
try {
|
|
2107
|
-
const content =
|
|
1681
|
+
const content = fs6.readFileSync(pkgPath, "utf-8");
|
|
2108
1682
|
return JSON.parse(content);
|
|
2109
1683
|
} catch {
|
|
2110
1684
|
throw new Error("Failed to parse package.json");
|
|
@@ -2112,7 +1686,7 @@ function readPackageJson() {
|
|
|
2112
1686
|
}
|
|
2113
1687
|
function writePackageJson(pkg2) {
|
|
2114
1688
|
const pkgPath = getPackageJsonPath();
|
|
2115
|
-
|
|
1689
|
+
fs6.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
|
|
2116
1690
|
}
|
|
2117
1691
|
function readActionPlugins() {
|
|
2118
1692
|
const pkg2 = readPackageJson();
|
|
@@ -2146,11 +1720,11 @@ function npmInstall(tgzPath) {
|
|
|
2146
1720
|
}
|
|
2147
1721
|
function getPackageVersion(pluginName) {
|
|
2148
1722
|
const pkgJsonPath = path5.join(getPluginPath(pluginName), "package.json");
|
|
2149
|
-
if (!
|
|
1723
|
+
if (!fs6.existsSync(pkgJsonPath)) {
|
|
2150
1724
|
return null;
|
|
2151
1725
|
}
|
|
2152
1726
|
try {
|
|
2153
|
-
const content =
|
|
1727
|
+
const content = fs6.readFileSync(pkgJsonPath, "utf-8");
|
|
2154
1728
|
const pkg2 = JSON.parse(content);
|
|
2155
1729
|
return pkg2.version || null;
|
|
2156
1730
|
} catch {
|
|
@@ -2159,11 +1733,11 @@ function getPackageVersion(pluginName) {
|
|
|
2159
1733
|
}
|
|
2160
1734
|
function readPluginPackageJson(pluginPath) {
|
|
2161
1735
|
const pkgJsonPath = path5.join(pluginPath, "package.json");
|
|
2162
|
-
if (!
|
|
1736
|
+
if (!fs6.existsSync(pkgJsonPath)) {
|
|
2163
1737
|
return null;
|
|
2164
1738
|
}
|
|
2165
1739
|
try {
|
|
2166
|
-
const content =
|
|
1740
|
+
const content = fs6.readFileSync(pkgJsonPath, "utf-8");
|
|
2167
1741
|
return JSON.parse(content);
|
|
2168
1742
|
} catch {
|
|
2169
1743
|
return null;
|
|
@@ -2173,34 +1747,34 @@ function extractTgzToNodeModules(tgzPath, pluginName) {
|
|
|
2173
1747
|
const nodeModulesPath = path5.join(getProjectRoot(), "node_modules");
|
|
2174
1748
|
const targetDir = path5.join(nodeModulesPath, pluginName);
|
|
2175
1749
|
const scopeDir = path5.dirname(targetDir);
|
|
2176
|
-
if (!
|
|
2177
|
-
|
|
1750
|
+
if (!fs6.existsSync(scopeDir)) {
|
|
1751
|
+
fs6.mkdirSync(scopeDir, { recursive: true });
|
|
2178
1752
|
}
|
|
2179
|
-
if (
|
|
2180
|
-
|
|
1753
|
+
if (fs6.existsSync(targetDir)) {
|
|
1754
|
+
fs6.rmSync(targetDir, { recursive: true });
|
|
2181
1755
|
}
|
|
2182
1756
|
const tempDir = path5.join(nodeModulesPath, ".cache", "fullstack-cli", "extract-temp");
|
|
2183
|
-
if (
|
|
2184
|
-
|
|
1757
|
+
if (fs6.existsSync(tempDir)) {
|
|
1758
|
+
fs6.rmSync(tempDir, { recursive: true });
|
|
2185
1759
|
}
|
|
2186
|
-
|
|
1760
|
+
fs6.mkdirSync(tempDir, { recursive: true });
|
|
2187
1761
|
try {
|
|
2188
1762
|
execSync(`tar -xzf "${tgzPath}" -C "${tempDir}"`, { stdio: "pipe" });
|
|
2189
1763
|
const extractedDir = path5.join(tempDir, "package");
|
|
2190
|
-
if (
|
|
2191
|
-
|
|
1764
|
+
if (fs6.existsSync(extractedDir)) {
|
|
1765
|
+
fs6.renameSync(extractedDir, targetDir);
|
|
2192
1766
|
} else {
|
|
2193
|
-
const files =
|
|
1767
|
+
const files = fs6.readdirSync(tempDir);
|
|
2194
1768
|
if (files.length === 1) {
|
|
2195
|
-
|
|
1769
|
+
fs6.renameSync(path5.join(tempDir, files[0]), targetDir);
|
|
2196
1770
|
} else {
|
|
2197
1771
|
throw new Error("Unexpected tgz structure");
|
|
2198
1772
|
}
|
|
2199
1773
|
}
|
|
2200
1774
|
return targetDir;
|
|
2201
1775
|
} finally {
|
|
2202
|
-
if (
|
|
2203
|
-
|
|
1776
|
+
if (fs6.existsSync(tempDir)) {
|
|
1777
|
+
fs6.rmSync(tempDir, { recursive: true });
|
|
2204
1778
|
}
|
|
2205
1779
|
}
|
|
2206
1780
|
}
|
|
@@ -2212,7 +1786,7 @@ function checkMissingPeerDeps(peerDeps) {
|
|
|
2212
1786
|
const nodeModulesPath = path5.join(getProjectRoot(), "node_modules");
|
|
2213
1787
|
for (const [depName, _version] of Object.entries(peerDeps)) {
|
|
2214
1788
|
const depPath = path5.join(nodeModulesPath, depName);
|
|
2215
|
-
if (!
|
|
1789
|
+
if (!fs6.existsSync(depPath)) {
|
|
2216
1790
|
missing.push(depName);
|
|
2217
1791
|
}
|
|
2218
1792
|
}
|
|
@@ -2236,15 +1810,15 @@ function installMissingDeps(deps) {
|
|
|
2236
1810
|
}
|
|
2237
1811
|
function removePluginDirectory(pluginName) {
|
|
2238
1812
|
const pluginPath = getPluginPath(pluginName);
|
|
2239
|
-
if (
|
|
2240
|
-
|
|
1813
|
+
if (fs6.existsSync(pluginPath)) {
|
|
1814
|
+
fs6.rmSync(pluginPath, { recursive: true });
|
|
2241
1815
|
console.log(`[action-plugin] Removed ${pluginName}`);
|
|
2242
1816
|
}
|
|
2243
1817
|
}
|
|
2244
1818
|
|
|
2245
1819
|
// src/commands/action-plugin/api-client.ts
|
|
2246
1820
|
import { HttpClient as HttpClient2 } from "@lark-apaas/http-client";
|
|
2247
|
-
import
|
|
1821
|
+
import fs7 from "fs";
|
|
2248
1822
|
import path6 from "path";
|
|
2249
1823
|
|
|
2250
1824
|
// src/utils/http-client.ts
|
|
@@ -2258,13 +1832,10 @@ function getHttpClient() {
|
|
|
2258
1832
|
enabled: true
|
|
2259
1833
|
}
|
|
2260
1834
|
});
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
return req;
|
|
2266
|
-
});
|
|
2267
|
-
}
|
|
1835
|
+
clientInstance.interceptors.request.use((req) => {
|
|
1836
|
+
req.headers["x-tt-env"] = "boe_miaoda_plugin";
|
|
1837
|
+
return req;
|
|
1838
|
+
});
|
|
2268
1839
|
}
|
|
2269
1840
|
return clientInstance;
|
|
2270
1841
|
}
|
|
@@ -2337,8 +1908,8 @@ function getPluginCacheDir() {
|
|
|
2337
1908
|
}
|
|
2338
1909
|
function ensureCacheDir() {
|
|
2339
1910
|
const cacheDir = getPluginCacheDir();
|
|
2340
|
-
if (!
|
|
2341
|
-
|
|
1911
|
+
if (!fs7.existsSync(cacheDir)) {
|
|
1912
|
+
fs7.mkdirSync(cacheDir, { recursive: true });
|
|
2342
1913
|
}
|
|
2343
1914
|
}
|
|
2344
1915
|
function getTempFilePath(pluginKey, version) {
|
|
@@ -2361,7 +1932,7 @@ async function downloadPlugin(pluginKey, requestedVersion) {
|
|
|
2361
1932
|
tgzBuffer = await downloadFromPublic(pluginInfo.downloadURL);
|
|
2362
1933
|
}
|
|
2363
1934
|
const tgzPath = getTempFilePath(pluginKey, pluginInfo.version);
|
|
2364
|
-
|
|
1935
|
+
fs7.writeFileSync(tgzPath, tgzBuffer);
|
|
2365
1936
|
console.log(`[action-plugin] Downloaded to ${tgzPath} (${(tgzBuffer.length / 1024).toFixed(2)} KB)`);
|
|
2366
1937
|
return {
|
|
2367
1938
|
tgzPath,
|
|
@@ -2371,8 +1942,8 @@ async function downloadPlugin(pluginKey, requestedVersion) {
|
|
|
2371
1942
|
}
|
|
2372
1943
|
function cleanupTempFile(tgzPath) {
|
|
2373
1944
|
try {
|
|
2374
|
-
if (
|
|
2375
|
-
|
|
1945
|
+
if (fs7.existsSync(tgzPath)) {
|
|
1946
|
+
fs7.unlinkSync(tgzPath);
|
|
2376
1947
|
}
|
|
2377
1948
|
} catch {
|
|
2378
1949
|
}
|
|
@@ -2708,7 +2279,7 @@ var actionPluginCommandGroup = {
|
|
|
2708
2279
|
};
|
|
2709
2280
|
|
|
2710
2281
|
// src/commands/capability/utils.ts
|
|
2711
|
-
import
|
|
2282
|
+
import fs8 from "fs";
|
|
2712
2283
|
import path7 from "path";
|
|
2713
2284
|
var CAPABILITIES_DIR = "server/capabilities";
|
|
2714
2285
|
function getProjectRoot2() {
|
|
@@ -2724,23 +2295,23 @@ function getPluginManifestPath(pluginKey) {
|
|
|
2724
2295
|
return path7.join(getProjectRoot2(), "node_modules", pluginKey, "manifest.json");
|
|
2725
2296
|
}
|
|
2726
2297
|
function capabilitiesDirExists() {
|
|
2727
|
-
return
|
|
2298
|
+
return fs8.existsSync(getCapabilitiesDir());
|
|
2728
2299
|
}
|
|
2729
2300
|
function listCapabilityIds() {
|
|
2730
2301
|
const dir = getCapabilitiesDir();
|
|
2731
|
-
if (!
|
|
2302
|
+
if (!fs8.existsSync(dir)) {
|
|
2732
2303
|
return [];
|
|
2733
2304
|
}
|
|
2734
|
-
const files =
|
|
2735
|
-
return files.filter((f) => f.endsWith(".json")
|
|
2305
|
+
const files = fs8.readdirSync(dir);
|
|
2306
|
+
return files.filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, ""));
|
|
2736
2307
|
}
|
|
2737
2308
|
function readCapability(id) {
|
|
2738
2309
|
const filePath = getCapabilityPath(id);
|
|
2739
|
-
if (!
|
|
2310
|
+
if (!fs8.existsSync(filePath)) {
|
|
2740
2311
|
throw new Error(`Capability not found: ${id}`);
|
|
2741
2312
|
}
|
|
2742
2313
|
try {
|
|
2743
|
-
const content =
|
|
2314
|
+
const content = fs8.readFileSync(filePath, "utf-8");
|
|
2744
2315
|
return JSON.parse(content);
|
|
2745
2316
|
} catch (error) {
|
|
2746
2317
|
if (error instanceof SyntaxError) {
|
|
@@ -2751,27 +2322,15 @@ function readCapability(id) {
|
|
|
2751
2322
|
}
|
|
2752
2323
|
function readAllCapabilities() {
|
|
2753
2324
|
const ids = listCapabilityIds();
|
|
2754
|
-
|
|
2755
|
-
for (const id of ids) {
|
|
2756
|
-
try {
|
|
2757
|
-
const capability = readCapability(id);
|
|
2758
|
-
if (!capability.pluginKey) {
|
|
2759
|
-
continue;
|
|
2760
|
-
}
|
|
2761
|
-
capabilities.push(capability);
|
|
2762
|
-
} catch {
|
|
2763
|
-
continue;
|
|
2764
|
-
}
|
|
2765
|
-
}
|
|
2766
|
-
return capabilities;
|
|
2325
|
+
return ids.map((id) => readCapability(id));
|
|
2767
2326
|
}
|
|
2768
2327
|
function readPluginManifest(pluginKey) {
|
|
2769
2328
|
const manifestPath = getPluginManifestPath(pluginKey);
|
|
2770
|
-
if (!
|
|
2329
|
+
if (!fs8.existsSync(manifestPath)) {
|
|
2771
2330
|
throw new Error(`Plugin not installed: ${pluginKey} (manifest.json not found)`);
|
|
2772
2331
|
}
|
|
2773
2332
|
try {
|
|
2774
|
-
const content =
|
|
2333
|
+
const content = fs8.readFileSync(manifestPath, "utf-8");
|
|
2775
2334
|
return JSON.parse(content);
|
|
2776
2335
|
} catch (error) {
|
|
2777
2336
|
if (error instanceof SyntaxError) {
|
|
@@ -2945,7 +2504,7 @@ var capabilityCommandGroup = {
|
|
|
2945
2504
|
};
|
|
2946
2505
|
|
|
2947
2506
|
// src/commands/migration/version-manager.ts
|
|
2948
|
-
import
|
|
2507
|
+
import fs9 from "fs";
|
|
2949
2508
|
import path8 from "path";
|
|
2950
2509
|
var PACKAGE_JSON = "package.json";
|
|
2951
2510
|
var VERSION_FIELD = "migrationVersion";
|
|
@@ -2954,25 +2513,25 @@ function getPackageJsonPath2() {
|
|
|
2954
2513
|
}
|
|
2955
2514
|
function getCurrentVersion() {
|
|
2956
2515
|
const pkgPath = getPackageJsonPath2();
|
|
2957
|
-
if (!
|
|
2516
|
+
if (!fs9.existsSync(pkgPath)) {
|
|
2958
2517
|
throw new Error("package.json not found");
|
|
2959
2518
|
}
|
|
2960
|
-
const pkg2 = JSON.parse(
|
|
2519
|
+
const pkg2 = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
|
|
2961
2520
|
return pkg2[VERSION_FIELD] ?? 0;
|
|
2962
2521
|
}
|
|
2963
2522
|
function setCurrentVersion(version) {
|
|
2964
2523
|
const pkgPath = getPackageJsonPath2();
|
|
2965
|
-
const pkg2 = JSON.parse(
|
|
2524
|
+
const pkg2 = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
|
|
2966
2525
|
pkg2[VERSION_FIELD] = version;
|
|
2967
|
-
|
|
2526
|
+
fs9.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
|
|
2968
2527
|
}
|
|
2969
2528
|
|
|
2970
2529
|
// src/commands/migration/versions/v001_capability/json-migrator/detector.ts
|
|
2971
|
-
import
|
|
2530
|
+
import fs11 from "fs";
|
|
2972
2531
|
import path10 from "path";
|
|
2973
2532
|
|
|
2974
2533
|
// src/commands/migration/versions/v001_capability/utils.ts
|
|
2975
|
-
import
|
|
2534
|
+
import fs10 from "fs";
|
|
2976
2535
|
import path9 from "path";
|
|
2977
2536
|
var CAPABILITIES_DIR2 = "server/capabilities";
|
|
2978
2537
|
function getProjectRoot3() {
|
|
@@ -2989,30 +2548,19 @@ function getPluginManifestPath2(pluginKey) {
|
|
|
2989
2548
|
function detectJsonMigration() {
|
|
2990
2549
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
2991
2550
|
const oldFilePath = path10.join(capabilitiesDir, "capabilities.json");
|
|
2992
|
-
if (!
|
|
2551
|
+
if (!fs11.existsSync(oldFilePath)) {
|
|
2993
2552
|
return {
|
|
2994
2553
|
needsMigration: false,
|
|
2995
2554
|
reason: "capabilities.json not found"
|
|
2996
2555
|
};
|
|
2997
2556
|
}
|
|
2998
2557
|
try {
|
|
2999
|
-
const content =
|
|
2558
|
+
const content = fs11.readFileSync(oldFilePath, "utf-8");
|
|
3000
2559
|
const parsed = JSON.parse(content);
|
|
3001
|
-
|
|
3002
|
-
return {
|
|
3003
|
-
needsMigration: false,
|
|
3004
|
-
reason: "capabilities.json is not a valid array"
|
|
3005
|
-
};
|
|
3006
|
-
}
|
|
3007
|
-
if (parsed.length === 0) {
|
|
3008
|
-
return {
|
|
3009
|
-
needsMigration: false,
|
|
3010
|
-
reason: "capabilities.json is an empty array"
|
|
3011
|
-
};
|
|
3012
|
-
}
|
|
2560
|
+
const capabilities = Array.isArray(parsed) ? parsed : [];
|
|
3013
2561
|
return {
|
|
3014
2562
|
needsMigration: true,
|
|
3015
|
-
oldCapabilities:
|
|
2563
|
+
oldCapabilities: capabilities,
|
|
3016
2564
|
oldFilePath
|
|
3017
2565
|
};
|
|
3018
2566
|
} catch (error) {
|
|
@@ -3047,7 +2595,7 @@ async function check(options) {
|
|
|
3047
2595
|
}
|
|
3048
2596
|
|
|
3049
2597
|
// src/commands/migration/versions/v001_capability/json-migrator/index.ts
|
|
3050
|
-
import
|
|
2598
|
+
import fs12 from "fs";
|
|
3051
2599
|
import path11 from "path";
|
|
3052
2600
|
|
|
3053
2601
|
// src/commands/migration/versions/v001_capability/mapping.ts
|
|
@@ -3278,10 +2826,10 @@ function transformCapabilities(oldCapabilities) {
|
|
|
3278
2826
|
// src/commands/migration/versions/v001_capability/json-migrator/index.ts
|
|
3279
2827
|
function loadExistingCapabilities() {
|
|
3280
2828
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
3281
|
-
if (!
|
|
2829
|
+
if (!fs12.existsSync(capabilitiesDir)) {
|
|
3282
2830
|
return [];
|
|
3283
2831
|
}
|
|
3284
|
-
const files =
|
|
2832
|
+
const files = fs12.readdirSync(capabilitiesDir);
|
|
3285
2833
|
const capabilities = [];
|
|
3286
2834
|
for (const file of files) {
|
|
3287
2835
|
if (file === "capabilities.json" || !file.endsWith(".json")) {
|
|
@@ -3289,7 +2837,7 @@ function loadExistingCapabilities() {
|
|
|
3289
2837
|
}
|
|
3290
2838
|
try {
|
|
3291
2839
|
const filePath = path11.join(capabilitiesDir, file);
|
|
3292
|
-
const content =
|
|
2840
|
+
const content = fs12.readFileSync(filePath, "utf-8");
|
|
3293
2841
|
const capability = JSON.parse(content);
|
|
3294
2842
|
if (capability.id && capability.pluginKey) {
|
|
3295
2843
|
capabilities.push(capability);
|
|
@@ -3349,7 +2897,7 @@ async function migrateJsonFiles(options) {
|
|
|
3349
2897
|
for (const cap of newCapabilities) {
|
|
3350
2898
|
const filePath = path11.join(capabilitiesDir, `${cap.id}.json`);
|
|
3351
2899
|
const content = JSON.stringify(cap, null, 2);
|
|
3352
|
-
|
|
2900
|
+
fs12.writeFileSync(filePath, content, "utf-8");
|
|
3353
2901
|
console.log(` \u2713 Created: ${cap.id}.json`);
|
|
3354
2902
|
}
|
|
3355
2903
|
return {
|
|
@@ -3361,11 +2909,11 @@ async function migrateJsonFiles(options) {
|
|
|
3361
2909
|
}
|
|
3362
2910
|
|
|
3363
2911
|
// src/commands/migration/versions/v001_capability/plugin-installer/detector.ts
|
|
3364
|
-
import
|
|
2912
|
+
import fs13 from "fs";
|
|
3365
2913
|
function isPluginInstalled2(pluginKey) {
|
|
3366
2914
|
const actionPlugins = readActionPlugins();
|
|
3367
2915
|
const manifestPath = getPluginManifestPath2(pluginKey);
|
|
3368
|
-
return
|
|
2916
|
+
return fs13.existsSync(manifestPath) && !!actionPlugins[pluginKey];
|
|
3369
2917
|
}
|
|
3370
2918
|
function detectPluginsToInstall(capabilities) {
|
|
3371
2919
|
const pluginKeys = /* @__PURE__ */ new Set();
|
|
@@ -3442,10 +2990,10 @@ async function installPlugins(capabilities, options) {
|
|
|
3442
2990
|
|
|
3443
2991
|
// src/commands/migration/versions/v001_capability/code-migrator/index.ts
|
|
3444
2992
|
import path13 from "path";
|
|
3445
|
-
import { Project as
|
|
2993
|
+
import { Project as Project2 } from "ts-morph";
|
|
3446
2994
|
|
|
3447
2995
|
// src/commands/migration/versions/v001_capability/code-migrator/scanner.ts
|
|
3448
|
-
import
|
|
2996
|
+
import fs14 from "fs";
|
|
3449
2997
|
import path12 from "path";
|
|
3450
2998
|
var EXCLUDED_DIRS = [
|
|
3451
2999
|
"node_modules",
|
|
@@ -3461,7 +3009,7 @@ var EXCLUDED_PATTERNS = [
|
|
|
3461
3009
|
/\.d\.ts$/
|
|
3462
3010
|
];
|
|
3463
3011
|
function scanDirectory(dir, files = []) {
|
|
3464
|
-
const entries =
|
|
3012
|
+
const entries = fs14.readdirSync(dir, { withFileTypes: true });
|
|
3465
3013
|
for (const entry of entries) {
|
|
3466
3014
|
const fullPath = path12.join(dir, entry.name);
|
|
3467
3015
|
if (entry.isDirectory()) {
|
|
@@ -3480,13 +3028,13 @@ function scanDirectory(dir, files = []) {
|
|
|
3480
3028
|
}
|
|
3481
3029
|
function scanServerFiles() {
|
|
3482
3030
|
const serverDir = path12.join(getProjectRoot3(), "server");
|
|
3483
|
-
if (!
|
|
3031
|
+
if (!fs14.existsSync(serverDir)) {
|
|
3484
3032
|
return [];
|
|
3485
3033
|
}
|
|
3486
3034
|
return scanDirectory(serverDir);
|
|
3487
3035
|
}
|
|
3488
3036
|
function hasCapabilityImport(filePath) {
|
|
3489
|
-
const content =
|
|
3037
|
+
const content = fs14.readFileSync(filePath, "utf-8");
|
|
3490
3038
|
return /import\s+.*from\s+['"][^'"]*capabilities[^'"]*['"]/.test(content);
|
|
3491
3039
|
}
|
|
3492
3040
|
function scanFilesToMigrate() {
|
|
@@ -3550,17 +3098,17 @@ function analyzeImports(sourceFile) {
|
|
|
3550
3098
|
}
|
|
3551
3099
|
|
|
3552
3100
|
// src/commands/migration/versions/v001_capability/code-migrator/analyzers/call-site-analyzer.ts
|
|
3553
|
-
import { SyntaxKind
|
|
3101
|
+
import { SyntaxKind } from "ts-morph";
|
|
3554
3102
|
function analyzeCallSites(sourceFile, imports) {
|
|
3555
3103
|
const callSites = [];
|
|
3556
3104
|
const importMap = /* @__PURE__ */ new Map();
|
|
3557
3105
|
for (const imp of imports) {
|
|
3558
3106
|
importMap.set(imp.importName, imp.capabilityId);
|
|
3559
3107
|
}
|
|
3560
|
-
const callExpressions = sourceFile.getDescendantsOfKind(
|
|
3108
|
+
const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
3561
3109
|
for (const callExpr of callExpressions) {
|
|
3562
3110
|
const expression = callExpr.getExpression();
|
|
3563
|
-
if (expression.getKind() ===
|
|
3111
|
+
if (expression.getKind() === SyntaxKind.Identifier) {
|
|
3564
3112
|
const functionName = expression.getText();
|
|
3565
3113
|
const capabilityId = importMap.get(functionName);
|
|
3566
3114
|
if (capabilityId) {
|
|
@@ -3573,11 +3121,11 @@ function analyzeCallSites(sourceFile, imports) {
|
|
|
3573
3121
|
text: callExpr.getText()
|
|
3574
3122
|
});
|
|
3575
3123
|
}
|
|
3576
|
-
} else if (expression.getKind() ===
|
|
3577
|
-
const propAccess = expression.asKind(
|
|
3124
|
+
} else if (expression.getKind() === SyntaxKind.PropertyAccessExpression) {
|
|
3125
|
+
const propAccess = expression.asKind(SyntaxKind.PropertyAccessExpression);
|
|
3578
3126
|
if (propAccess) {
|
|
3579
3127
|
const objectExpr = propAccess.getExpression();
|
|
3580
|
-
if (objectExpr.getKind() ===
|
|
3128
|
+
if (objectExpr.getKind() === SyntaxKind.Identifier) {
|
|
3581
3129
|
const objectName = objectExpr.getText();
|
|
3582
3130
|
const capabilityId = importMap.get(objectName);
|
|
3583
3131
|
if (capabilityId) {
|
|
@@ -3613,15 +3161,13 @@ function analyzeClass(sourceFile) {
|
|
|
3613
3161
|
if (!name) continue;
|
|
3614
3162
|
const isInjectable = hasDecorator(classDecl, "Injectable");
|
|
3615
3163
|
const isController = hasDecorator(classDecl, "Controller");
|
|
3616
|
-
|
|
3617
|
-
if (classInfo && classInfo.isInjectable && !isInjectable && !isController && !isAutomation) {
|
|
3164
|
+
if (classInfo && classInfo.isInjectable && !isInjectable && !isController) {
|
|
3618
3165
|
continue;
|
|
3619
3166
|
}
|
|
3620
3167
|
const info = {
|
|
3621
3168
|
name,
|
|
3622
3169
|
isInjectable,
|
|
3623
3170
|
isController,
|
|
3624
|
-
isAutomation,
|
|
3625
3171
|
constructorParamCount: 0
|
|
3626
3172
|
};
|
|
3627
3173
|
const classBody = classDecl.getChildSyntaxListOrThrow();
|
|
@@ -3640,7 +3186,7 @@ function analyzeClass(sourceFile) {
|
|
|
3640
3186
|
info.constructorParamsEnd = ctor.getStart();
|
|
3641
3187
|
}
|
|
3642
3188
|
}
|
|
3643
|
-
if (isInjectable || isController ||
|
|
3189
|
+
if (isInjectable || isController || !classInfo) {
|
|
3644
3190
|
classInfo = info;
|
|
3645
3191
|
}
|
|
3646
3192
|
}
|
|
@@ -3653,10 +3199,10 @@ function canAutoMigrate(classInfo) {
|
|
|
3653
3199
|
reason: "No class found in file"
|
|
3654
3200
|
};
|
|
3655
3201
|
}
|
|
3656
|
-
if (!classInfo.isInjectable && !classInfo.isController
|
|
3202
|
+
if (!classInfo.isInjectable && !classInfo.isController) {
|
|
3657
3203
|
return {
|
|
3658
3204
|
canMigrate: false,
|
|
3659
|
-
reason: `Class "${classInfo.name}" is not @Injectable
|
|
3205
|
+
reason: `Class "${classInfo.name}" is not @Injectable or @Controller`
|
|
3660
3206
|
};
|
|
3661
3207
|
}
|
|
3662
3208
|
return { canMigrate: true };
|
|
@@ -3806,7 +3352,7 @@ function addInjection(sourceFile) {
|
|
|
3806
3352
|
}
|
|
3807
3353
|
|
|
3808
3354
|
// src/commands/migration/versions/v001_capability/code-migrator/transformers/call-site-transformer.ts
|
|
3809
|
-
import { SyntaxKind as
|
|
3355
|
+
import { SyntaxKind as SyntaxKind2 } from "ts-morph";
|
|
3810
3356
|
var DEFAULT_ACTION_NAME = "run";
|
|
3811
3357
|
function generateNewCallText(capabilityId, actionName, args) {
|
|
3812
3358
|
const argsText = args.trim() || "{}";
|
|
@@ -3821,19 +3367,19 @@ function transformCallSites(sourceFile, imports) {
|
|
|
3821
3367
|
});
|
|
3822
3368
|
}
|
|
3823
3369
|
let replacedCount = 0;
|
|
3824
|
-
const callExpressions = sourceFile.getDescendantsOfKind(
|
|
3370
|
+
const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind2.CallExpression);
|
|
3825
3371
|
const sortedCalls = [...callExpressions].sort((a, b) => b.getStart() - a.getStart());
|
|
3826
3372
|
for (const callExpr of sortedCalls) {
|
|
3827
3373
|
const expression = callExpr.getExpression();
|
|
3828
3374
|
let importInfo;
|
|
3829
|
-
if (expression.getKind() ===
|
|
3375
|
+
if (expression.getKind() === SyntaxKind2.Identifier) {
|
|
3830
3376
|
const functionName = expression.getText();
|
|
3831
3377
|
importInfo = importMap.get(functionName);
|
|
3832
|
-
} else if (expression.getKind() ===
|
|
3833
|
-
const propAccess = expression.asKind(
|
|
3378
|
+
} else if (expression.getKind() === SyntaxKind2.PropertyAccessExpression) {
|
|
3379
|
+
const propAccess = expression.asKind(SyntaxKind2.PropertyAccessExpression);
|
|
3834
3380
|
if (propAccess) {
|
|
3835
3381
|
const objectExpr = propAccess.getExpression();
|
|
3836
|
-
if (objectExpr.getKind() ===
|
|
3382
|
+
if (objectExpr.getKind() === SyntaxKind2.Identifier) {
|
|
3837
3383
|
const objectName = objectExpr.getText();
|
|
3838
3384
|
importInfo = importMap.get(objectName);
|
|
3839
3385
|
}
|
|
@@ -3934,7 +3480,7 @@ async function migrateCode(options, capabilities) {
|
|
|
3934
3480
|
console.log(" No files need code migration.\n");
|
|
3935
3481
|
return result;
|
|
3936
3482
|
}
|
|
3937
|
-
const project = new
|
|
3483
|
+
const project = new Project2({
|
|
3938
3484
|
skipAddingFilesFromTsConfig: true,
|
|
3939
3485
|
compilerOptions: {
|
|
3940
3486
|
allowJs: true
|
|
@@ -3977,17 +3523,17 @@ function getSuggestion(analysis) {
|
|
|
3977
3523
|
}
|
|
3978
3524
|
|
|
3979
3525
|
// src/commands/migration/versions/v001_capability/cleanup.ts
|
|
3980
|
-
import
|
|
3526
|
+
import fs15 from "fs";
|
|
3981
3527
|
import path14 from "path";
|
|
3982
3528
|
function cleanupOldFiles(capabilities, dryRun) {
|
|
3983
3529
|
const deletedFiles = [];
|
|
3984
3530
|
const errors = [];
|
|
3985
3531
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
3986
3532
|
const oldJsonPath = path14.join(capabilitiesDir, "capabilities.json");
|
|
3987
|
-
if (
|
|
3533
|
+
if (fs15.existsSync(oldJsonPath)) {
|
|
3988
3534
|
try {
|
|
3989
3535
|
if (!dryRun) {
|
|
3990
|
-
|
|
3536
|
+
fs15.unlinkSync(oldJsonPath);
|
|
3991
3537
|
}
|
|
3992
3538
|
deletedFiles.push("capabilities.json");
|
|
3993
3539
|
} catch (error) {
|
|
@@ -3996,10 +3542,10 @@ function cleanupOldFiles(capabilities, dryRun) {
|
|
|
3996
3542
|
}
|
|
3997
3543
|
for (const cap of capabilities) {
|
|
3998
3544
|
const tsFilePath = path14.join(capabilitiesDir, `${cap.id}.ts`);
|
|
3999
|
-
if (
|
|
3545
|
+
if (fs15.existsSync(tsFilePath)) {
|
|
4000
3546
|
try {
|
|
4001
3547
|
if (!dryRun) {
|
|
4002
|
-
|
|
3548
|
+
fs15.unlinkSync(tsFilePath);
|
|
4003
3549
|
}
|
|
4004
3550
|
deletedFiles.push(`${cap.id}.ts`);
|
|
4005
3551
|
} catch (error) {
|
|
@@ -4015,7 +3561,7 @@ function cleanupOldFiles(capabilities, dryRun) {
|
|
|
4015
3561
|
}
|
|
4016
3562
|
|
|
4017
3563
|
// src/commands/migration/versions/v001_capability/report-generator.ts
|
|
4018
|
-
import
|
|
3564
|
+
import fs16 from "fs";
|
|
4019
3565
|
import path15 from "path";
|
|
4020
3566
|
var REPORT_FILE = "capability-migration-report.md";
|
|
4021
3567
|
function printSummary(result) {
|
|
@@ -4179,15 +3725,15 @@ async function generateReport(result) {
|
|
|
4179
3725
|
}
|
|
4180
3726
|
lines.push("");
|
|
4181
3727
|
const logDir = process.env.LOG_DIR || "logs";
|
|
4182
|
-
if (!
|
|
3728
|
+
if (!fs16.existsSync(logDir)) {
|
|
4183
3729
|
return;
|
|
4184
3730
|
}
|
|
4185
3731
|
const reportDir = path15.join(logDir, "migration");
|
|
4186
|
-
if (!
|
|
4187
|
-
|
|
3732
|
+
if (!fs16.existsSync(reportDir)) {
|
|
3733
|
+
fs16.mkdirSync(reportDir, { recursive: true });
|
|
4188
3734
|
}
|
|
4189
3735
|
const reportPath = path15.join(reportDir, REPORT_FILE);
|
|
4190
|
-
|
|
3736
|
+
fs16.writeFileSync(reportPath, lines.join("\n"), "utf-8");
|
|
4191
3737
|
console.log(`\u{1F4C4} Report generated: ${reportPath}`);
|
|
4192
3738
|
}
|
|
4193
3739
|
|
|
@@ -4722,7 +4268,7 @@ var migrationCommand = {
|
|
|
4722
4268
|
import path16 from "path";
|
|
4723
4269
|
|
|
4724
4270
|
// src/commands/read-logs/std-utils.ts
|
|
4725
|
-
import
|
|
4271
|
+
import fs17 from "fs";
|
|
4726
4272
|
function formatStdPrefixTime(localTime) {
|
|
4727
4273
|
const match = localTime.match(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/);
|
|
4728
4274
|
if (!match) return localTime;
|
|
@@ -4752,11 +4298,11 @@ function stripPrefixFromStdLine(line) {
|
|
|
4752
4298
|
return `[${time}] ${content}`;
|
|
4753
4299
|
}
|
|
4754
4300
|
function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarker) {
|
|
4755
|
-
const stat =
|
|
4301
|
+
const stat = fs17.statSync(filePath);
|
|
4756
4302
|
if (stat.size === 0) {
|
|
4757
4303
|
return { lines: [], markerFound: false, totalLinesCount: 0 };
|
|
4758
4304
|
}
|
|
4759
|
-
const fd =
|
|
4305
|
+
const fd = fs17.openSync(filePath, "r");
|
|
4760
4306
|
const chunkSize = 64 * 1024;
|
|
4761
4307
|
let position = stat.size;
|
|
4762
4308
|
let remainder = "";
|
|
@@ -4770,7 +4316,7 @@ function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarke
|
|
|
4770
4316
|
const length = Math.min(chunkSize, position);
|
|
4771
4317
|
position -= length;
|
|
4772
4318
|
const buffer = Buffer.alloc(length);
|
|
4773
|
-
|
|
4319
|
+
fs17.readSync(fd, buffer, 0, length, position);
|
|
4774
4320
|
let chunk = buffer.toString("utf8");
|
|
4775
4321
|
if (remainder) {
|
|
4776
4322
|
chunk += remainder;
|
|
@@ -4812,7 +4358,7 @@ function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarke
|
|
|
4812
4358
|
}
|
|
4813
4359
|
}
|
|
4814
4360
|
} finally {
|
|
4815
|
-
|
|
4361
|
+
fs17.closeSync(fd);
|
|
4816
4362
|
}
|
|
4817
4363
|
return { lines: collected.reverse(), markerFound, totalLinesCount };
|
|
4818
4364
|
}
|
|
@@ -4833,21 +4379,21 @@ function readServerStdSegment(filePath, maxLines, offset) {
|
|
|
4833
4379
|
}
|
|
4834
4380
|
|
|
4835
4381
|
// src/commands/read-logs/tail.ts
|
|
4836
|
-
import
|
|
4382
|
+
import fs18 from "fs";
|
|
4837
4383
|
function fileExists(filePath) {
|
|
4838
4384
|
try {
|
|
4839
|
-
|
|
4385
|
+
fs18.accessSync(filePath, fs18.constants.F_OK | fs18.constants.R_OK);
|
|
4840
4386
|
return true;
|
|
4841
4387
|
} catch {
|
|
4842
4388
|
return false;
|
|
4843
4389
|
}
|
|
4844
4390
|
}
|
|
4845
4391
|
function readFileTailLines(filePath, maxLines) {
|
|
4846
|
-
const stat =
|
|
4392
|
+
const stat = fs18.statSync(filePath);
|
|
4847
4393
|
if (stat.size === 0) {
|
|
4848
4394
|
return [];
|
|
4849
4395
|
}
|
|
4850
|
-
const fd =
|
|
4396
|
+
const fd = fs18.openSync(filePath, "r");
|
|
4851
4397
|
const chunkSize = 64 * 1024;
|
|
4852
4398
|
const chunks = [];
|
|
4853
4399
|
let position = stat.size;
|
|
@@ -4857,13 +4403,13 @@ function readFileTailLines(filePath, maxLines) {
|
|
|
4857
4403
|
const length = Math.min(chunkSize, position);
|
|
4858
4404
|
position -= length;
|
|
4859
4405
|
const buffer = Buffer.alloc(length);
|
|
4860
|
-
|
|
4406
|
+
fs18.readSync(fd, buffer, 0, length, position);
|
|
4861
4407
|
chunks.unshift(buffer.toString("utf8"));
|
|
4862
4408
|
const chunkLines = buffer.toString("utf8").split("\n").length - 1;
|
|
4863
4409
|
collectedLines += chunkLines;
|
|
4864
4410
|
}
|
|
4865
4411
|
} finally {
|
|
4866
|
-
|
|
4412
|
+
fs18.closeSync(fd);
|
|
4867
4413
|
}
|
|
4868
4414
|
const content = chunks.join("");
|
|
4869
4415
|
const allLines = content.split("\n");
|
|
@@ -4879,11 +4425,11 @@ function readFileTailLines(filePath, maxLines) {
|
|
|
4879
4425
|
return allLines.slice(allLines.length - maxLines);
|
|
4880
4426
|
}
|
|
4881
4427
|
function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
|
|
4882
|
-
const stat =
|
|
4428
|
+
const stat = fs18.statSync(filePath);
|
|
4883
4429
|
if (stat.size === 0) {
|
|
4884
4430
|
return { lines: [], totalLinesCount: 0 };
|
|
4885
4431
|
}
|
|
4886
|
-
const fd =
|
|
4432
|
+
const fd = fs18.openSync(filePath, "r");
|
|
4887
4433
|
const chunkSize = 64 * 1024;
|
|
4888
4434
|
let position = stat.size;
|
|
4889
4435
|
let remainder = "";
|
|
@@ -4895,7 +4441,7 @@ function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
|
|
|
4895
4441
|
const length = Math.min(chunkSize, position);
|
|
4896
4442
|
position -= length;
|
|
4897
4443
|
const buffer = Buffer.alloc(length);
|
|
4898
|
-
|
|
4444
|
+
fs18.readSync(fd, buffer, 0, length, position);
|
|
4899
4445
|
let chunk = buffer.toString("utf8");
|
|
4900
4446
|
if (remainder) {
|
|
4901
4447
|
chunk += remainder;
|
|
@@ -4926,7 +4472,7 @@ function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
|
|
|
4926
4472
|
}
|
|
4927
4473
|
}
|
|
4928
4474
|
} finally {
|
|
4929
|
-
|
|
4475
|
+
fs18.closeSync(fd);
|
|
4930
4476
|
}
|
|
4931
4477
|
return { lines: collected.reverse(), totalLinesCount };
|
|
4932
4478
|
}
|
|
@@ -5028,7 +4574,7 @@ function extractClientStdSegment(lines, maxLines, offset) {
|
|
|
5028
4574
|
}
|
|
5029
4575
|
|
|
5030
4576
|
// src/commands/read-logs/json-lines.ts
|
|
5031
|
-
import
|
|
4577
|
+
import fs19 from "fs";
|
|
5032
4578
|
function normalizePid(value) {
|
|
5033
4579
|
if (typeof value === "number") {
|
|
5034
4580
|
return String(value);
|
|
@@ -5079,11 +4625,11 @@ function buildWantedLevelSet(levels) {
|
|
|
5079
4625
|
return set.size > 0 ? set : null;
|
|
5080
4626
|
}
|
|
5081
4627
|
function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
|
|
5082
|
-
const stat =
|
|
4628
|
+
const stat = fs19.statSync(filePath);
|
|
5083
4629
|
if (stat.size === 0) {
|
|
5084
4630
|
return { lines: [], totalLinesCount: 0 };
|
|
5085
4631
|
}
|
|
5086
|
-
const fd =
|
|
4632
|
+
const fd = fs19.openSync(filePath, "r");
|
|
5087
4633
|
const chunkSize = 64 * 1024;
|
|
5088
4634
|
let position = stat.size;
|
|
5089
4635
|
let remainder = "";
|
|
@@ -5098,7 +4644,7 @@ function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
|
|
|
5098
4644
|
const length = Math.min(chunkSize, position);
|
|
5099
4645
|
position -= length;
|
|
5100
4646
|
const buffer = Buffer.alloc(length);
|
|
5101
|
-
|
|
4647
|
+
fs19.readSync(fd, buffer, 0, length, position);
|
|
5102
4648
|
let chunk = buffer.toString("utf8");
|
|
5103
4649
|
if (remainder) {
|
|
5104
4650
|
chunk += remainder;
|
|
@@ -5160,7 +4706,7 @@ function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
|
|
|
5160
4706
|
}
|
|
5161
4707
|
}
|
|
5162
4708
|
} finally {
|
|
5163
|
-
|
|
4709
|
+
fs19.closeSync(fd);
|
|
5164
4710
|
}
|
|
5165
4711
|
return { lines: collected.reverse(), totalLinesCount };
|
|
5166
4712
|
}
|
|
@@ -5203,11 +4749,11 @@ function extractTraceId(obj) {
|
|
|
5203
4749
|
function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
|
|
5204
4750
|
const wanted = traceId.trim();
|
|
5205
4751
|
if (!wanted) return { lines: [], totalLinesCount: 0 };
|
|
5206
|
-
const stat =
|
|
4752
|
+
const stat = fs19.statSync(filePath);
|
|
5207
4753
|
if (stat.size === 0) {
|
|
5208
4754
|
return { lines: [], totalLinesCount: 0 };
|
|
5209
4755
|
}
|
|
5210
|
-
const fd =
|
|
4756
|
+
const fd = fs19.openSync(filePath, "r");
|
|
5211
4757
|
const chunkSize = 64 * 1024;
|
|
5212
4758
|
let position = stat.size;
|
|
5213
4759
|
let remainder = "";
|
|
@@ -5220,7 +4766,7 @@ function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
|
|
|
5220
4766
|
const length = Math.min(chunkSize, position);
|
|
5221
4767
|
position -= length;
|
|
5222
4768
|
const buffer = Buffer.alloc(length);
|
|
5223
|
-
|
|
4769
|
+
fs19.readSync(fd, buffer, 0, length, position);
|
|
5224
4770
|
let chunk = buffer.toString("utf8");
|
|
5225
4771
|
if (remainder) {
|
|
5226
4772
|
chunk += remainder;
|
|
@@ -5273,7 +4819,7 @@ function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
|
|
|
5273
4819
|
}
|
|
5274
4820
|
}
|
|
5275
4821
|
} finally {
|
|
5276
|
-
|
|
4822
|
+
fs19.closeSync(fd);
|
|
5277
4823
|
}
|
|
5278
4824
|
return { lines: collected.reverse(), totalLinesCount };
|
|
5279
4825
|
}
|
|
@@ -5282,11 +4828,11 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
|
|
|
5282
4828
|
if (!wantedLevelSet) {
|
|
5283
4829
|
return { lines: [], totalLinesCount: 0 };
|
|
5284
4830
|
}
|
|
5285
|
-
const stat =
|
|
4831
|
+
const stat = fs19.statSync(filePath);
|
|
5286
4832
|
if (stat.size === 0) {
|
|
5287
4833
|
return { lines: [], totalLinesCount: 0 };
|
|
5288
4834
|
}
|
|
5289
|
-
const fd =
|
|
4835
|
+
const fd = fs19.openSync(filePath, "r");
|
|
5290
4836
|
const chunkSize = 64 * 1024;
|
|
5291
4837
|
let position = stat.size;
|
|
5292
4838
|
let remainder = "";
|
|
@@ -5298,7 +4844,7 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
|
|
|
5298
4844
|
const length = Math.min(chunkSize, position);
|
|
5299
4845
|
position -= length;
|
|
5300
4846
|
const buffer = Buffer.alloc(length);
|
|
5301
|
-
|
|
4847
|
+
fs19.readSync(fd, buffer, 0, length, position);
|
|
5302
4848
|
let chunk = buffer.toString("utf8");
|
|
5303
4849
|
if (remainder) {
|
|
5304
4850
|
chunk += remainder;
|
|
@@ -5345,13 +4891,97 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
|
|
|
5345
4891
|
}
|
|
5346
4892
|
}
|
|
5347
4893
|
} finally {
|
|
5348
|
-
|
|
4894
|
+
fs19.closeSync(fd);
|
|
5349
4895
|
}
|
|
5350
4896
|
return { lines: collected.reverse(), totalLinesCount };
|
|
5351
4897
|
}
|
|
5352
4898
|
|
|
5353
4899
|
// src/commands/read-logs/index.ts
|
|
5354
4900
|
var LOG_TYPES = ["server", "trace", "server-std", "client-std", "browser"];
|
|
4901
|
+
function normalizeObjectKey(key) {
|
|
4902
|
+
return key.toLowerCase().replace(/_/g, "");
|
|
4903
|
+
}
|
|
4904
|
+
function formatIsoWithLocalOffset(date, includeMilliseconds) {
|
|
4905
|
+
const pad2 = (n) => String(n).padStart(2, "0");
|
|
4906
|
+
const pad3 = (n) => String(n).padStart(3, "0");
|
|
4907
|
+
const year = date.getFullYear();
|
|
4908
|
+
const month = pad2(date.getMonth() + 1);
|
|
4909
|
+
const day = pad2(date.getDate());
|
|
4910
|
+
const hour = pad2(date.getHours());
|
|
4911
|
+
const minute = pad2(date.getMinutes());
|
|
4912
|
+
const second = pad2(date.getSeconds());
|
|
4913
|
+
const ms = pad3(date.getMilliseconds());
|
|
4914
|
+
const tzOffsetMinutes = -date.getTimezoneOffset();
|
|
4915
|
+
const sign = tzOffsetMinutes >= 0 ? "+" : "-";
|
|
4916
|
+
const abs = Math.abs(tzOffsetMinutes);
|
|
4917
|
+
const offsetHH = pad2(Math.floor(abs / 60));
|
|
4918
|
+
const offsetMM = pad2(abs % 60);
|
|
4919
|
+
const msPart = includeMilliseconds ? `.${ms}` : "";
|
|
4920
|
+
return `${year}-${month}-${day}T${hour}:${minute}:${second}${msPart}${sign}${offsetHH}:${offsetMM}`;
|
|
4921
|
+
}
|
|
4922
|
+
function formatLocalDateTimeStringWithOffset(input) {
|
|
4923
|
+
const match = input.match(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/);
|
|
4924
|
+
if (!match) return input;
|
|
4925
|
+
const year = Number(match[1]);
|
|
4926
|
+
const month = Number(match[2]);
|
|
4927
|
+
const day = Number(match[3]);
|
|
4928
|
+
const hour = Number(match[4]);
|
|
4929
|
+
const minute = Number(match[5]);
|
|
4930
|
+
const second = Number(match[6]);
|
|
4931
|
+
const date = new Date(year, month - 1, day, hour, minute, second, 0);
|
|
4932
|
+
if (Number.isNaN(date.getTime())) return input;
|
|
4933
|
+
return formatIsoWithLocalOffset(date, false);
|
|
4934
|
+
}
|
|
4935
|
+
function formatIsoNoTzStringWithOffset(input) {
|
|
4936
|
+
const match = input.match(
|
|
4937
|
+
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,3}))?$/
|
|
4938
|
+
);
|
|
4939
|
+
if (!match) return input;
|
|
4940
|
+
const year = Number(match[1]);
|
|
4941
|
+
const month = Number(match[2]);
|
|
4942
|
+
const day = Number(match[3]);
|
|
4943
|
+
const hour = Number(match[4]);
|
|
4944
|
+
const minute = Number(match[5]);
|
|
4945
|
+
const second = Number(match[6]);
|
|
4946
|
+
const msRaw = match[7];
|
|
4947
|
+
const ms = typeof msRaw === "string" ? Number(msRaw.padEnd(3, "0")) : 0;
|
|
4948
|
+
const date = new Date(year, month - 1, day, hour, minute, second, ms);
|
|
4949
|
+
if (Number.isNaN(date.getTime())) return input;
|
|
4950
|
+
return formatIsoWithLocalOffset(date, typeof msRaw === "string");
|
|
4951
|
+
}
|
|
4952
|
+
function normalizeTimeString(input) {
|
|
4953
|
+
const localDateTime = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
|
|
4954
|
+
if (localDateTime.test(input)) {
|
|
4955
|
+
return formatLocalDateTimeStringWithOffset(input);
|
|
4956
|
+
}
|
|
4957
|
+
const isoWithTz = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/i;
|
|
4958
|
+
if (isoWithTz.test(input)) {
|
|
4959
|
+
const date = new Date(input);
|
|
4960
|
+
if (Number.isNaN(date.getTime())) return input;
|
|
4961
|
+
const includeMs = /\.\d{1,3}/.test(input);
|
|
4962
|
+
return formatIsoWithLocalOffset(date, includeMs);
|
|
4963
|
+
}
|
|
4964
|
+
const isoNoTz = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?$/;
|
|
4965
|
+
if (isoNoTz.test(input)) {
|
|
4966
|
+
return formatIsoNoTzStringWithOffset(input);
|
|
4967
|
+
}
|
|
4968
|
+
return input;
|
|
4969
|
+
}
|
|
4970
|
+
function normalizeTimezonesInValue(value, keyNormalized) {
|
|
4971
|
+
if (typeof value === "string" && (keyNormalized === "time" || keyNormalized === "servertime")) {
|
|
4972
|
+
return normalizeTimeString(value);
|
|
4973
|
+
}
|
|
4974
|
+
if (!value || typeof value !== "object") return value;
|
|
4975
|
+
if (Array.isArray(value)) {
|
|
4976
|
+
return value.map((item) => normalizeTimezonesInValue(item));
|
|
4977
|
+
}
|
|
4978
|
+
const obj = value;
|
|
4979
|
+
const result = {};
|
|
4980
|
+
for (const [key, child] of Object.entries(obj)) {
|
|
4981
|
+
result[key] = normalizeTimezonesInValue(child, normalizeObjectKey(key));
|
|
4982
|
+
}
|
|
4983
|
+
return result;
|
|
4984
|
+
}
|
|
5355
4985
|
function sanitizeStructuredLog(value) {
|
|
5356
4986
|
if (!value || typeof value !== "object") return value;
|
|
5357
4987
|
if (Array.isArray(value)) return value;
|
|
@@ -5456,16 +5086,17 @@ async function readLogsJsonResult(options) {
|
|
|
5456
5086
|
const logs = [];
|
|
5457
5087
|
let hasError = false;
|
|
5458
5088
|
for (const line of lines) {
|
|
5459
|
-
if (!hasError && hasErrorInStdLines([line])) {
|
|
5460
|
-
hasError = true;
|
|
5461
|
-
}
|
|
5462
5089
|
try {
|
|
5463
5090
|
const parsed = JSON.parse(line);
|
|
5464
|
-
|
|
5091
|
+
const normalized = normalizeTimezonesInValue(parsed);
|
|
5092
|
+
logs.push(sanitizeStructuredLog(normalized));
|
|
5465
5093
|
if (!hasError && hasErrorInLogObject(parsed)) {
|
|
5466
5094
|
hasError = true;
|
|
5467
5095
|
}
|
|
5468
5096
|
} catch {
|
|
5097
|
+
if (!hasError && hasErrorInStdLines([line])) {
|
|
5098
|
+
hasError = true;
|
|
5099
|
+
}
|
|
5469
5100
|
continue;
|
|
5470
5101
|
}
|
|
5471
5102
|
}
|
|
@@ -5495,15 +5126,8 @@ function resolveLogFilePath(logDir, type) {
|
|
|
5495
5126
|
throw new Error(`Unsupported log type: ${type}`);
|
|
5496
5127
|
}
|
|
5497
5128
|
async function run4(options) {
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
process.stdout.write(JSON.stringify(result) + "\n");
|
|
5501
|
-
} catch (error) {
|
|
5502
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5503
|
-
const result = { hasError: true, totalLinesCount: 0, logs: [message] };
|
|
5504
|
-
process.stdout.write(JSON.stringify(result) + "\n");
|
|
5505
|
-
process.exitCode = 1;
|
|
5506
|
-
}
|
|
5129
|
+
const result = await readLogsJsonResult(options);
|
|
5130
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
5507
5131
|
}
|
|
5508
5132
|
function parseLogType(input) {
|
|
5509
5133
|
const value = typeof input === "string" ? input.trim() : "";
|
|
@@ -5535,9 +5159,9 @@ var readLogsCommand = {
|
|
|
5535
5159
|
name: "read-logs",
|
|
5536
5160
|
description: "Read latest logs from log files",
|
|
5537
5161
|
register(program) {
|
|
5538
|
-
program.command(this.name).description(this.description).option("--dir <path>", "Logs directory", "
|
|
5162
|
+
program.command(this.name).description(this.description).option("--dir <path>", "Logs directory", "/tmp").option("--type <type>", `Log type: ${LOG_TYPES.join("|")}`, "server-std").option("--max-lines <lines>", "Max lines to return", "30").option("--offset <lines>", "Skip latest N lines for pagination", "0").option("--trace-id <id>", "Filter structured logs by trace id").option("--level <levels>", "Filter structured logs by level (comma-separated)").action(async (rawOptions) => {
|
|
5539
5163
|
try {
|
|
5540
|
-
const logDir = typeof rawOptions.dir === "string" ? rawOptions.dir : "
|
|
5164
|
+
const logDir = typeof rawOptions.dir === "string" ? rawOptions.dir : "/tmp";
|
|
5541
5165
|
const type = parseLogType(rawOptions.type);
|
|
5542
5166
|
const maxLines = parsePositiveInt(rawOptions.maxLines, "--max-lines");
|
|
5543
5167
|
const offset = parseNonNegativeInt(rawOptions.offset, "--offset");
|
|
@@ -5546,8 +5170,7 @@ var readLogsCommand = {
|
|
|
5546
5170
|
await run4({ logDir, type, maxLines, offset, traceId, levels });
|
|
5547
5171
|
} catch (error) {
|
|
5548
5172
|
const message = error instanceof Error ? error.message : String(error);
|
|
5549
|
-
|
|
5550
|
-
process.stdout.write(JSON.stringify(result) + "\n");
|
|
5173
|
+
process.stderr.write(message + "\n");
|
|
5551
5174
|
process.exitCode = 1;
|
|
5552
5175
|
}
|
|
5553
5176
|
});
|
|
@@ -5566,11 +5189,11 @@ var commands = [
|
|
|
5566
5189
|
|
|
5567
5190
|
// src/index.ts
|
|
5568
5191
|
var envPath = path17.join(process.cwd(), ".env");
|
|
5569
|
-
if (
|
|
5192
|
+
if (fs20.existsSync(envPath)) {
|
|
5570
5193
|
dotenvConfig({ path: envPath });
|
|
5571
5194
|
}
|
|
5572
5195
|
var __dirname = path17.dirname(fileURLToPath4(import.meta.url));
|
|
5573
|
-
var pkg = JSON.parse(
|
|
5196
|
+
var pkg = JSON.parse(fs20.readFileSync(path17.join(__dirname, "../package.json"), "utf-8"));
|
|
5574
5197
|
var cli = new FullstackCLI(pkg.version);
|
|
5575
5198
|
cli.useAll(commands);
|
|
5576
5199
|
cli.run();
|