postgresdk 0.18.27 → 0.18.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/cli-utils.d.ts +14 -0
- package/dist/cli.js +73 -22
- package/dist/index.d.ts +3 -1
- package/dist/index.js +56 -11
- package/dist/utils.d.ts +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -547,6 +547,8 @@ pnpm dlx postgresdk@latest pull
|
|
|
547
547
|
|
|
548
548
|
The SDK files are written directly to your client project, giving you full TypeScript autocomplete and type safety.
|
|
549
549
|
|
|
550
|
+
**Stale file cleanup:** Both `generate` and `pull` automatically remove files that are no longer part of the SDK. In an interactive terminal you'll be prompted to confirm each deletion; use `--force` (or `-y`) to skip prompts. In non-interactive environments (CI), stale files are skipped with a warning unless `--force` is passed.
|
|
551
|
+
|
|
550
552
|
### Using the SDK
|
|
551
553
|
|
|
552
554
|
#### CRUD Operations
|
|
@@ -1014,6 +1016,7 @@ Commands:
|
|
|
1014
1016
|
|
|
1015
1017
|
Options:
|
|
1016
1018
|
-c, --config <path> Path to config file (default: postgresdk.config.ts)
|
|
1019
|
+
--force, -y Delete stale files without prompting (generate & pull)
|
|
1017
1020
|
|
|
1018
1021
|
Init subcommands/flags:
|
|
1019
1022
|
init pull Generate pull-only config (alias for --sdk)
|
|
@@ -1026,7 +1029,9 @@ Examples:
|
|
|
1026
1029
|
npx postgresdk@latest init --api # API-side config
|
|
1027
1030
|
npx postgresdk@latest generate
|
|
1028
1031
|
npx postgresdk@latest generate -c custom.config.ts
|
|
1032
|
+
npx postgresdk@latest generate --force # Skip stale file prompts
|
|
1029
1033
|
npx postgresdk@latest pull --from=https://api.com --output=./src/sdk
|
|
1034
|
+
npx postgresdk@latest pull --from=https://api.com --output=./src/sdk --force
|
|
1030
1035
|
```
|
|
1031
1036
|
|
|
1032
1037
|
### Generated Tests
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Parse --force / -y / --yes from CLI args */
|
|
2
|
+
export declare function parseForceFlag(args: string[]): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* For each stale file, prompt the user to confirm deletion (shadcn-style per-file).
|
|
5
|
+
* If `force` is true, all files are deleted without prompting.
|
|
6
|
+
* If stdout is not a TTY (e.g. CI), files are skipped with a warning unless `force` is true.
|
|
7
|
+
*
|
|
8
|
+
* Returns counts of deleted and skipped files.
|
|
9
|
+
*/
|
|
10
|
+
export declare function confirmAndDeleteStaleFiles(staleFiles: string[], force: boolean): Promise<{
|
|
11
|
+
deleted: number;
|
|
12
|
+
skipped: number;
|
|
13
|
+
filesDeleted: string[];
|
|
14
|
+
}>;
|
package/dist/cli.js
CHANGED
|
@@ -528,9 +528,8 @@ async function collectDirsRecursively(root) {
|
|
|
528
528
|
}
|
|
529
529
|
return dirs;
|
|
530
530
|
}
|
|
531
|
-
async function
|
|
532
|
-
|
|
533
|
-
const filesDeleted = [];
|
|
531
|
+
async function findStaleFiles(generatedPaths, dirsToScan) {
|
|
532
|
+
const stale = [];
|
|
534
533
|
for (const dir of dirsToScan) {
|
|
535
534
|
if (!existsSync(dir))
|
|
536
535
|
continue;
|
|
@@ -542,13 +541,11 @@ async function deleteStaleFiles(generatedPaths, dirsToScan) {
|
|
|
542
541
|
continue;
|
|
543
542
|
const fullPath = join(dir, entry.name);
|
|
544
543
|
if (!generatedPaths.has(fullPath)) {
|
|
545
|
-
|
|
546
|
-
deleted++;
|
|
547
|
-
filesDeleted.push(fullPath);
|
|
544
|
+
stale.push(fullPath);
|
|
548
545
|
}
|
|
549
546
|
}
|
|
550
547
|
}
|
|
551
|
-
return
|
|
548
|
+
return stale;
|
|
552
549
|
}
|
|
553
550
|
var pascal = (s) => s.split(/[_\s-]+/).map((w) => w?.[0] ? w[0].toUpperCase() + w.slice(1) : "").join("");
|
|
554
551
|
var init_utils = () => {};
|
|
@@ -1427,6 +1424,50 @@ var init_emit_sdk_contract = __esm(() => {
|
|
|
1427
1424
|
init_emit_include_methods();
|
|
1428
1425
|
});
|
|
1429
1426
|
|
|
1427
|
+
// src/cli-utils.ts
|
|
1428
|
+
import { unlink as unlink2 } from "fs/promises";
|
|
1429
|
+
import prompts from "prompts";
|
|
1430
|
+
function parseForceFlag(args) {
|
|
1431
|
+
return args.includes("--force") || args.includes("-y") || args.includes("--yes");
|
|
1432
|
+
}
|
|
1433
|
+
async function confirmAndDeleteStaleFiles(staleFiles, force) {
|
|
1434
|
+
if (staleFiles.length === 0)
|
|
1435
|
+
return { deleted: 0, skipped: 0, filesDeleted: [] };
|
|
1436
|
+
if (!force && !process.stdin.isTTY) {
|
|
1437
|
+
console.log(`⚠️ ${staleFiles.length} stale file(s) not deleted (non-interactive shell). Re-run with --force to delete.`);
|
|
1438
|
+
return { deleted: 0, skipped: staleFiles.length, filesDeleted: [] };
|
|
1439
|
+
}
|
|
1440
|
+
let deleted = 0;
|
|
1441
|
+
let skipped = 0;
|
|
1442
|
+
const filesDeleted = [];
|
|
1443
|
+
for (const filePath of staleFiles) {
|
|
1444
|
+
let shouldDelete = force;
|
|
1445
|
+
if (!force) {
|
|
1446
|
+
const { confirmed } = await prompts({
|
|
1447
|
+
type: "confirm",
|
|
1448
|
+
name: "confirmed",
|
|
1449
|
+
message: `Delete stale file: ${filePath}?`,
|
|
1450
|
+
initial: false
|
|
1451
|
+
});
|
|
1452
|
+
if (confirmed === undefined) {
|
|
1453
|
+
console.log("Stale file deletion aborted.");
|
|
1454
|
+
break;
|
|
1455
|
+
}
|
|
1456
|
+
shouldDelete = confirmed;
|
|
1457
|
+
}
|
|
1458
|
+
if (shouldDelete) {
|
|
1459
|
+
await unlink2(filePath);
|
|
1460
|
+
console.log(` ✗ ${filePath}`);
|
|
1461
|
+
filesDeleted.push(filePath);
|
|
1462
|
+
deleted++;
|
|
1463
|
+
} else {
|
|
1464
|
+
skipped++;
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
return { deleted, skipped, filesDeleted };
|
|
1468
|
+
}
|
|
1469
|
+
var init_cli_utils = () => {};
|
|
1470
|
+
|
|
1430
1471
|
// src/cli-config-utils.ts
|
|
1431
1472
|
function extractConfigFields(configContent) {
|
|
1432
1473
|
const fields = [];
|
|
@@ -1848,7 +1889,7 @@ __export(exports_cli_init, {
|
|
|
1848
1889
|
});
|
|
1849
1890
|
import { existsSync as existsSync3, writeFileSync, readFileSync as readFileSync2, copyFileSync } from "fs";
|
|
1850
1891
|
import { resolve } from "path";
|
|
1851
|
-
import
|
|
1892
|
+
import prompts2 from "prompts";
|
|
1852
1893
|
async function initCommand(args) {
|
|
1853
1894
|
console.log(`\uD83D\uDE80 Initializing postgresdk configuration...
|
|
1854
1895
|
`);
|
|
@@ -1873,7 +1914,7 @@ async function initCommand(args) {
|
|
|
1873
1914
|
}
|
|
1874
1915
|
});
|
|
1875
1916
|
console.log();
|
|
1876
|
-
const { mergeStrategy } = await
|
|
1917
|
+
const { mergeStrategy } = await prompts2({
|
|
1877
1918
|
type: "select",
|
|
1878
1919
|
name: "mergeStrategy",
|
|
1879
1920
|
message: "How would you like to proceed?",
|
|
@@ -1913,7 +1954,7 @@ async function initCommand(args) {
|
|
|
1913
1954
|
\uD83D\uDD04 Let's review your configuration:
|
|
1914
1955
|
`);
|
|
1915
1956
|
for (const field of existingFields.filter((f) => !f.isCommented)) {
|
|
1916
|
-
const { choice } = await
|
|
1957
|
+
const { choice } = await prompts2({
|
|
1917
1958
|
type: "select",
|
|
1918
1959
|
name: "choice",
|
|
1919
1960
|
message: `${field.description}:
|
|
@@ -1945,7 +1986,7 @@ async function initCommand(args) {
|
|
|
1945
1986
|
\uD83D\uDCE6 New configuration options available:
|
|
1946
1987
|
`);
|
|
1947
1988
|
for (const option of missingOptions) {
|
|
1948
|
-
const { addOption } = await
|
|
1989
|
+
const { addOption } = await prompts2({
|
|
1949
1990
|
type: "confirm",
|
|
1950
1991
|
name: "addOption",
|
|
1951
1992
|
message: `Add ${option.description} configuration? (commented out by default)`,
|
|
@@ -1984,7 +2025,7 @@ async function initCommand(args) {
|
|
|
1984
2025
|
} else if (isSdkSide) {
|
|
1985
2026
|
projectType = "sdk";
|
|
1986
2027
|
} else {
|
|
1987
|
-
const response = await
|
|
2028
|
+
const response = await prompts2({
|
|
1988
2029
|
type: "select",
|
|
1989
2030
|
name: "projectType",
|
|
1990
2031
|
message: "What type of project is this?",
|
|
@@ -2280,6 +2321,7 @@ async function pullCommand(args) {
|
|
|
2280
2321
|
console.error("⚠️ Failed to load config file:", err);
|
|
2281
2322
|
}
|
|
2282
2323
|
}
|
|
2324
|
+
const force = parseForceFlag(args);
|
|
2283
2325
|
const cliConfig = {
|
|
2284
2326
|
from: args.find((a) => a.startsWith("--from="))?.split("=")[1],
|
|
2285
2327
|
output: args.find((a) => a.startsWith("--output="))?.split("=")[1],
|
|
@@ -2362,10 +2404,8 @@ Options:`);
|
|
|
2362
2404
|
}
|
|
2363
2405
|
const generatedPaths = new Set(Object.keys(sdk.files).map((p) => join3(resolvedOutput, p)));
|
|
2364
2406
|
const dirsToScan = await collectDirsRecursively(resolvedOutput);
|
|
2365
|
-
const
|
|
2366
|
-
|
|
2367
|
-
console.log(` ✗ ${f}`);
|
|
2368
|
-
}
|
|
2407
|
+
const staleFiles = await findStaleFiles(generatedPaths, dirsToScan);
|
|
2408
|
+
const deleteResult = await confirmAndDeleteStaleFiles(staleFiles, force);
|
|
2369
2409
|
const metadataPath = join3(resolvedOutput, ".postgresdk.json");
|
|
2370
2410
|
const metadata = {
|
|
2371
2411
|
version: sdk.version,
|
|
@@ -2381,7 +2421,7 @@ Options:`);
|
|
|
2381
2421
|
if (metadataChanged) {
|
|
2382
2422
|
await writeFile2(metadataPath, JSON.stringify(metadata, null, 2));
|
|
2383
2423
|
}
|
|
2384
|
-
if (filesWritten === 0 && !metadataChanged && deleteResult.deleted === 0) {
|
|
2424
|
+
if (filesWritten === 0 && !metadataChanged && deleteResult.deleted === 0 && deleteResult.skipped === 0) {
|
|
2385
2425
|
console.log(`✅ SDK up-to-date (${filesUnchanged} files unchanged)`);
|
|
2386
2426
|
} else {
|
|
2387
2427
|
const parts = [];
|
|
@@ -2389,6 +2429,8 @@ Options:`);
|
|
|
2389
2429
|
parts.push(`updated ${filesWritten} files`);
|
|
2390
2430
|
if (deleteResult.deleted > 0)
|
|
2391
2431
|
parts.push(`deleted ${deleteResult.deleted} stale files`);
|
|
2432
|
+
if (deleteResult.skipped > 0)
|
|
2433
|
+
parts.push(`skipped ${deleteResult.skipped} stale files`);
|
|
2392
2434
|
if (filesUnchanged > 0)
|
|
2393
2435
|
parts.push(`${filesUnchanged} unchanged`);
|
|
2394
2436
|
console.log(`✅ SDK pulled successfully to ${config.output}`);
|
|
@@ -2401,6 +2443,7 @@ Options:`);
|
|
|
2401
2443
|
}
|
|
2402
2444
|
var init_cli_pull = __esm(() => {
|
|
2403
2445
|
init_utils();
|
|
2446
|
+
init_cli_utils();
|
|
2404
2447
|
});
|
|
2405
2448
|
|
|
2406
2449
|
// src/index.ts
|
|
@@ -7111,6 +7154,7 @@ function generateTestCases(table, sampleData, updateData, hasForeignKeys = false
|
|
|
7111
7154
|
// src/index.ts
|
|
7112
7155
|
init_emit_sdk_contract();
|
|
7113
7156
|
init_utils();
|
|
7157
|
+
init_cli_utils();
|
|
7114
7158
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
7115
7159
|
var __dirname2 = dirname2(__filename2);
|
|
7116
7160
|
var { version: CLI_VERSION } = JSON.parse(readFileSync(join2(__dirname2, "../package.json"), "utf-8"));
|
|
@@ -7120,7 +7164,7 @@ function resolveSoftDeleteColumn(cfg, tableName) {
|
|
|
7120
7164
|
return overrides[tableName] ?? null;
|
|
7121
7165
|
return cfg.softDeleteColumn ?? null;
|
|
7122
7166
|
}
|
|
7123
|
-
async function generate(configPath) {
|
|
7167
|
+
async function generate(configPath, options) {
|
|
7124
7168
|
if (!existsSync2(configPath)) {
|
|
7125
7169
|
throw new Error(`Config file not found: ${configPath}
|
|
7126
7170
|
|
|
@@ -7315,7 +7359,7 @@ async function generate(configPath) {
|
|
|
7315
7359
|
}
|
|
7316
7360
|
console.log("✍️ Writing files...");
|
|
7317
7361
|
const writeResult = await writeFilesIfChanged(files);
|
|
7318
|
-
let deleteResult = { deleted: 0, filesDeleted: [] };
|
|
7362
|
+
let deleteResult = { deleted: 0, skipped: 0, filesDeleted: [] };
|
|
7319
7363
|
if (cfg.clean !== false) {
|
|
7320
7364
|
const dirsToScan = [
|
|
7321
7365
|
serverDir,
|
|
@@ -7331,9 +7375,10 @@ async function generate(configPath) {
|
|
|
7331
7375
|
if (generateTests)
|
|
7332
7376
|
dirsToScan.push(testDir);
|
|
7333
7377
|
const generatedPaths = new Set(files.map((f) => f.path));
|
|
7334
|
-
|
|
7378
|
+
const staleFiles = await findStaleFiles(generatedPaths, dirsToScan);
|
|
7379
|
+
deleteResult = await confirmAndDeleteStaleFiles(staleFiles, options?.force ?? false);
|
|
7335
7380
|
}
|
|
7336
|
-
if (writeResult.written === 0 && deleteResult.deleted === 0) {
|
|
7381
|
+
if (writeResult.written === 0 && deleteResult.deleted === 0 && deleteResult.skipped === 0) {
|
|
7337
7382
|
console.log(`✅ All ${writeResult.unchanged} files up-to-date (no changes)`);
|
|
7338
7383
|
} else {
|
|
7339
7384
|
const parts = [];
|
|
@@ -7341,6 +7386,8 @@ async function generate(configPath) {
|
|
|
7341
7386
|
parts.push(`updated ${writeResult.written} files`);
|
|
7342
7387
|
if (deleteResult.deleted > 0)
|
|
7343
7388
|
parts.push(`deleted ${deleteResult.deleted} stale files`);
|
|
7389
|
+
if (deleteResult.skipped > 0)
|
|
7390
|
+
parts.push(`skipped ${deleteResult.skipped} stale files`);
|
|
7344
7391
|
if (writeResult.unchanged > 0)
|
|
7345
7392
|
parts.push(`${writeResult.unchanged} unchanged`);
|
|
7346
7393
|
console.log(`✅ ${parts.join(", ")}`);
|
|
@@ -7383,6 +7430,7 @@ import { resolve as resolve3 } from "node:path";
|
|
|
7383
7430
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
7384
7431
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
7385
7432
|
import { dirname as dirname4, join as join4 } from "node:path";
|
|
7433
|
+
init_cli_utils();
|
|
7386
7434
|
var __filename3 = fileURLToPath2(import.meta.url);
|
|
7387
7435
|
var __dirname3 = dirname4(__filename3);
|
|
7388
7436
|
var packageJson = JSON.parse(readFileSync3(join4(__dirname3, "../package.json"), "utf-8"));
|
|
@@ -7412,11 +7460,13 @@ Init Options:
|
|
|
7412
7460
|
|
|
7413
7461
|
Generate Options:
|
|
7414
7462
|
-c, --config <path> Path to config file (default: postgresdk.config.ts)
|
|
7463
|
+
--force, -y Delete stale files without prompting
|
|
7415
7464
|
|
|
7416
7465
|
Pull Options:
|
|
7417
7466
|
--from <url> API URL to pull SDK from
|
|
7418
7467
|
--output <path> Output directory (default: ./src/sdk)
|
|
7419
7468
|
--token <token> Authentication token
|
|
7469
|
+
--force, -y Delete stale files without prompting
|
|
7420
7470
|
-c, --config <path> Path to config file with pull settings
|
|
7421
7471
|
|
|
7422
7472
|
Examples:
|
|
@@ -7438,8 +7488,9 @@ if (command === "init") {
|
|
|
7438
7488
|
if (configIndex !== -1 && args[configIndex + 1]) {
|
|
7439
7489
|
configPath = args[configIndex + 1];
|
|
7440
7490
|
}
|
|
7491
|
+
const force = parseForceFlag(args);
|
|
7441
7492
|
try {
|
|
7442
|
-
await generate(resolve3(process.cwd(), configPath));
|
|
7493
|
+
await generate(resolve3(process.cwd(), configPath), { force });
|
|
7443
7494
|
} catch (err) {
|
|
7444
7495
|
console.error("❌ Generation failed:", err);
|
|
7445
7496
|
process.exit(1);
|
package/dist/index.d.ts
CHANGED
|
@@ -2,4 +2,6 @@ import "dotenv/config";
|
|
|
2
2
|
import type { Config } from "./types";
|
|
3
3
|
/** Resolves the effective soft delete column for a given table, respecting per-table overrides. */
|
|
4
4
|
export declare function resolveSoftDeleteColumn(cfg: Pick<Config, "softDeleteColumn" | "softDeleteColumnOverrides">, tableName: string): string | null;
|
|
5
|
-
export declare function generate(configPath: string
|
|
5
|
+
export declare function generate(configPath: string, options?: {
|
|
6
|
+
force?: boolean;
|
|
7
|
+
}): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -527,9 +527,8 @@ async function collectDirsRecursively(root) {
|
|
|
527
527
|
}
|
|
528
528
|
return dirs;
|
|
529
529
|
}
|
|
530
|
-
async function
|
|
531
|
-
|
|
532
|
-
const filesDeleted = [];
|
|
530
|
+
async function findStaleFiles(generatedPaths, dirsToScan) {
|
|
531
|
+
const stale = [];
|
|
533
532
|
for (const dir of dirsToScan) {
|
|
534
533
|
if (!existsSync(dir))
|
|
535
534
|
continue;
|
|
@@ -541,13 +540,11 @@ async function deleteStaleFiles(generatedPaths, dirsToScan) {
|
|
|
541
540
|
continue;
|
|
542
541
|
const fullPath = join(dir, entry.name);
|
|
543
542
|
if (!generatedPaths.has(fullPath)) {
|
|
544
|
-
|
|
545
|
-
deleted++;
|
|
546
|
-
filesDeleted.push(fullPath);
|
|
543
|
+
stale.push(fullPath);
|
|
547
544
|
}
|
|
548
545
|
}
|
|
549
546
|
}
|
|
550
|
-
return
|
|
547
|
+
return stale;
|
|
551
548
|
}
|
|
552
549
|
var pascal = (s) => s.split(/[_\s-]+/).map((w) => w?.[0] ? w[0].toUpperCase() + w.slice(1) : "").join("");
|
|
553
550
|
var init_utils = () => {};
|
|
@@ -1426,6 +1423,50 @@ var init_emit_sdk_contract = __esm(() => {
|
|
|
1426
1423
|
init_emit_include_methods();
|
|
1427
1424
|
});
|
|
1428
1425
|
|
|
1426
|
+
// src/cli-utils.ts
|
|
1427
|
+
import { unlink as unlink2 } from "fs/promises";
|
|
1428
|
+
import prompts from "prompts";
|
|
1429
|
+
function parseForceFlag(args) {
|
|
1430
|
+
return args.includes("--force") || args.includes("-y") || args.includes("--yes");
|
|
1431
|
+
}
|
|
1432
|
+
async function confirmAndDeleteStaleFiles(staleFiles, force) {
|
|
1433
|
+
if (staleFiles.length === 0)
|
|
1434
|
+
return { deleted: 0, skipped: 0, filesDeleted: [] };
|
|
1435
|
+
if (!force && !process.stdin.isTTY) {
|
|
1436
|
+
console.log(`⚠️ ${staleFiles.length} stale file(s) not deleted (non-interactive shell). Re-run with --force to delete.`);
|
|
1437
|
+
return { deleted: 0, skipped: staleFiles.length, filesDeleted: [] };
|
|
1438
|
+
}
|
|
1439
|
+
let deleted = 0;
|
|
1440
|
+
let skipped = 0;
|
|
1441
|
+
const filesDeleted = [];
|
|
1442
|
+
for (const filePath of staleFiles) {
|
|
1443
|
+
let shouldDelete = force;
|
|
1444
|
+
if (!force) {
|
|
1445
|
+
const { confirmed } = await prompts({
|
|
1446
|
+
type: "confirm",
|
|
1447
|
+
name: "confirmed",
|
|
1448
|
+
message: `Delete stale file: ${filePath}?`,
|
|
1449
|
+
initial: false
|
|
1450
|
+
});
|
|
1451
|
+
if (confirmed === undefined) {
|
|
1452
|
+
console.log("Stale file deletion aborted.");
|
|
1453
|
+
break;
|
|
1454
|
+
}
|
|
1455
|
+
shouldDelete = confirmed;
|
|
1456
|
+
}
|
|
1457
|
+
if (shouldDelete) {
|
|
1458
|
+
await unlink2(filePath);
|
|
1459
|
+
console.log(` ✗ ${filePath}`);
|
|
1460
|
+
filesDeleted.push(filePath);
|
|
1461
|
+
deleted++;
|
|
1462
|
+
} else {
|
|
1463
|
+
skipped++;
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
return { deleted, skipped, filesDeleted };
|
|
1467
|
+
}
|
|
1468
|
+
var init_cli_utils = () => {};
|
|
1469
|
+
|
|
1429
1470
|
// src/index.ts
|
|
1430
1471
|
var import_config = __toESM(require_config(), 1);
|
|
1431
1472
|
import { join as join2, relative, dirname as dirname2 } from "node:path";
|
|
@@ -6134,6 +6175,7 @@ function generateTestCases(table, sampleData, updateData, hasForeignKeys = false
|
|
|
6134
6175
|
// src/index.ts
|
|
6135
6176
|
init_emit_sdk_contract();
|
|
6136
6177
|
init_utils();
|
|
6178
|
+
init_cli_utils();
|
|
6137
6179
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
6138
6180
|
var __dirname2 = dirname2(__filename2);
|
|
6139
6181
|
var { version: CLI_VERSION } = JSON.parse(readFileSync(join2(__dirname2, "../package.json"), "utf-8"));
|
|
@@ -6143,7 +6185,7 @@ function resolveSoftDeleteColumn(cfg, tableName) {
|
|
|
6143
6185
|
return overrides[tableName] ?? null;
|
|
6144
6186
|
return cfg.softDeleteColumn ?? null;
|
|
6145
6187
|
}
|
|
6146
|
-
async function generate(configPath) {
|
|
6188
|
+
async function generate(configPath, options) {
|
|
6147
6189
|
if (!existsSync2(configPath)) {
|
|
6148
6190
|
throw new Error(`Config file not found: ${configPath}
|
|
6149
6191
|
|
|
@@ -6338,7 +6380,7 @@ async function generate(configPath) {
|
|
|
6338
6380
|
}
|
|
6339
6381
|
console.log("✍️ Writing files...");
|
|
6340
6382
|
const writeResult = await writeFilesIfChanged(files);
|
|
6341
|
-
let deleteResult = { deleted: 0, filesDeleted: [] };
|
|
6383
|
+
let deleteResult = { deleted: 0, skipped: 0, filesDeleted: [] };
|
|
6342
6384
|
if (cfg.clean !== false) {
|
|
6343
6385
|
const dirsToScan = [
|
|
6344
6386
|
serverDir,
|
|
@@ -6354,9 +6396,10 @@ async function generate(configPath) {
|
|
|
6354
6396
|
if (generateTests)
|
|
6355
6397
|
dirsToScan.push(testDir);
|
|
6356
6398
|
const generatedPaths = new Set(files.map((f) => f.path));
|
|
6357
|
-
|
|
6399
|
+
const staleFiles = await findStaleFiles(generatedPaths, dirsToScan);
|
|
6400
|
+
deleteResult = await confirmAndDeleteStaleFiles(staleFiles, options?.force ?? false);
|
|
6358
6401
|
}
|
|
6359
|
-
if (writeResult.written === 0 && deleteResult.deleted === 0) {
|
|
6402
|
+
if (writeResult.written === 0 && deleteResult.deleted === 0 && deleteResult.skipped === 0) {
|
|
6360
6403
|
console.log(`✅ All ${writeResult.unchanged} files up-to-date (no changes)`);
|
|
6361
6404
|
} else {
|
|
6362
6405
|
const parts = [];
|
|
@@ -6364,6 +6407,8 @@ async function generate(configPath) {
|
|
|
6364
6407
|
parts.push(`updated ${writeResult.written} files`);
|
|
6365
6408
|
if (deleteResult.deleted > 0)
|
|
6366
6409
|
parts.push(`deleted ${deleteResult.deleted} stale files`);
|
|
6410
|
+
if (deleteResult.skipped > 0)
|
|
6411
|
+
parts.push(`skipped ${deleteResult.skipped} stale files`);
|
|
6367
6412
|
if (writeResult.unchanged > 0)
|
|
6368
6413
|
parts.push(`${writeResult.unchanged} unchanged`);
|
|
6369
6414
|
console.log(`✅ ${parts.join(", ")}`);
|
package/dist/utils.d.ts
CHANGED
|
@@ -25,6 +25,11 @@ export declare function ensureDirs(dirs: string[]): Promise<void>;
|
|
|
25
25
|
* Returns an empty array if `root` does not exist.
|
|
26
26
|
*/
|
|
27
27
|
export declare function collectDirsRecursively(root: string): Promise<string[]>;
|
|
28
|
+
/**
|
|
29
|
+
* Find files in the given directories that are not in the set of generated paths,
|
|
30
|
+
* without deleting them. Used to identify stale files before prompting for confirmation.
|
|
31
|
+
*/
|
|
32
|
+
export declare function findStaleFiles(generatedPaths: Set<string>, dirsToScan: string[]): Promise<string[]>;
|
|
28
33
|
/**
|
|
29
34
|
* Delete files in the given directories that are not in the set of generated paths.
|
|
30
35
|
* Used to remove stale files for tables that no longer exist in the schema.
|