servcraft 0.3.1 → 0.4.3
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 +26 -0
- package/ROADMAP.md +54 -14
- package/dist/cli/index.cjs +489 -166
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +470 -147
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +31 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/generate.ts +25 -10
- package/src/cli/commands/scaffold.ts +211 -0
- package/src/cli/commands/templates.ts +147 -0
- package/src/cli/index.ts +8 -0
- package/src/cli/utils/template-loader.ts +80 -0
- package/src/modules/user/user.repository.ts +18 -1
package/dist/cli/index.cjs
CHANGED
|
@@ -28,7 +28,7 @@ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${_
|
|
|
28
28
|
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
29
29
|
|
|
30
30
|
// src/cli/index.ts
|
|
31
|
-
var
|
|
31
|
+
var import_commander13 = require("commander");
|
|
32
32
|
|
|
33
33
|
// src/cli/commands/init.ts
|
|
34
34
|
var import_commander = require("commander");
|
|
@@ -1226,7 +1226,7 @@ declare module 'fastify' {
|
|
|
1226
1226
|
|
|
1227
1227
|
// src/cli/commands/generate.ts
|
|
1228
1228
|
var import_commander2 = require("commander");
|
|
1229
|
-
var
|
|
1229
|
+
var import_path5 = __toESM(require("path"), 1);
|
|
1230
1230
|
var import_ora2 = __toESM(require("ora"), 1);
|
|
1231
1231
|
var import_inquirer2 = __toESM(require("inquirer"), 1);
|
|
1232
1232
|
var import_chalk5 = __toESM(require("chalk"), 1);
|
|
@@ -1455,11 +1455,11 @@ function displayError(error2) {
|
|
|
1455
1455
|
}
|
|
1456
1456
|
function validateProject() {
|
|
1457
1457
|
try {
|
|
1458
|
-
const
|
|
1459
|
-
if (!
|
|
1458
|
+
const fs14 = require("fs");
|
|
1459
|
+
if (!fs14.existsSync("package.json")) {
|
|
1460
1460
|
return ErrorTypes.NOT_IN_PROJECT();
|
|
1461
1461
|
}
|
|
1462
|
-
const packageJson = JSON.parse(
|
|
1462
|
+
const packageJson = JSON.parse(fs14.readFileSync("package.json", "utf-8"));
|
|
1463
1463
|
if (!packageJson.dependencies?.fastify) {
|
|
1464
1464
|
return new ServCraftError("This does not appear to be a ServCraft project", [
|
|
1465
1465
|
`ServCraft projects require Fastify`,
|
|
@@ -2510,6 +2510,39 @@ describe('${pascalName} Integration Tests', () => {
|
|
|
2510
2510
|
`;
|
|
2511
2511
|
}
|
|
2512
2512
|
|
|
2513
|
+
// src/cli/utils/template-loader.ts
|
|
2514
|
+
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
2515
|
+
var import_path4 = __toESM(require("path"), 1);
|
|
2516
|
+
async function loadCustomTemplate(templateType) {
|
|
2517
|
+
const projectRoot = getProjectRoot();
|
|
2518
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
2519
|
+
const locations = [
|
|
2520
|
+
import_path4.default.join(projectRoot, ".servcraft", "templates", `${templateType}.ts`),
|
|
2521
|
+
import_path4.default.join(homeDir, ".servcraft", "templates", `${templateType}.ts`)
|
|
2522
|
+
];
|
|
2523
|
+
for (const location of locations) {
|
|
2524
|
+
try {
|
|
2525
|
+
await import_promises3.default.access(location);
|
|
2526
|
+
const templateModule = await import(`file://${location}`);
|
|
2527
|
+
const functionName = `${templateType.replace(/-/g, "")}Template`;
|
|
2528
|
+
if (templateModule[functionName]) {
|
|
2529
|
+
return templateModule;
|
|
2530
|
+
}
|
|
2531
|
+
} catch {
|
|
2532
|
+
continue;
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
return null;
|
|
2536
|
+
}
|
|
2537
|
+
async function getTemplate(templateType, builtInTemplate) {
|
|
2538
|
+
const customTemplate = await loadCustomTemplate(templateType);
|
|
2539
|
+
if (customTemplate) {
|
|
2540
|
+
const functionName = `${templateType.replace(/-/g, "")}Template`;
|
|
2541
|
+
return customTemplate[functionName];
|
|
2542
|
+
}
|
|
2543
|
+
return builtInTemplate;
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2513
2546
|
// src/cli/commands/generate.ts
|
|
2514
2547
|
function enableDryRunIfNeeded(options) {
|
|
2515
2548
|
const dryRun = DryRunManager.getInstance();
|
|
@@ -2542,60 +2575,70 @@ generateCommand.command("module <name> [fields...]").alias("m").description(
|
|
|
2542
2575
|
const pluralName = pluralize(kebabName);
|
|
2543
2576
|
const tableName = pluralize(kebabName.replace(/-/g, "_"));
|
|
2544
2577
|
const validatorType = options.validator || "zod";
|
|
2545
|
-
const moduleDir =
|
|
2578
|
+
const moduleDir = import_path5.default.join(getModulesDir(), kebabName);
|
|
2546
2579
|
if (await fileExists(moduleDir)) {
|
|
2547
2580
|
spinner.stop();
|
|
2548
2581
|
error(`Module "${kebabName}" already exists`);
|
|
2549
2582
|
return;
|
|
2550
2583
|
}
|
|
2551
2584
|
const hasFields = fields.length > 0;
|
|
2585
|
+
const controllerTpl = await getTemplate("controller", controllerTemplate);
|
|
2586
|
+
const serviceTpl = await getTemplate("service", serviceTemplate);
|
|
2587
|
+
const repositoryTpl = await getTemplate("repository", repositoryTemplate);
|
|
2588
|
+
const typesTpl = await getTemplate("types", typesTemplate);
|
|
2589
|
+
const schemasTpl = await getTemplate("schemas", schemasTemplate);
|
|
2590
|
+
const routesTpl = await getTemplate("routes", routesTemplate);
|
|
2591
|
+
const moduleIndexTpl = await getTemplate("module-index", moduleIndexTemplate);
|
|
2552
2592
|
const files = [
|
|
2553
2593
|
{
|
|
2554
2594
|
name: `${kebabName}.types.ts`,
|
|
2555
|
-
content: hasFields ? dynamicTypesTemplate(kebabName, pascalName, fields) :
|
|
2595
|
+
content: hasFields ? dynamicTypesTemplate(kebabName, pascalName, fields) : typesTpl(kebabName, pascalName)
|
|
2556
2596
|
},
|
|
2557
2597
|
{
|
|
2558
2598
|
name: `${kebabName}.schemas.ts`,
|
|
2559
|
-
content: hasFields ? dynamicSchemasTemplate(kebabName, pascalName, camelName, fields, validatorType) :
|
|
2599
|
+
content: hasFields ? dynamicSchemasTemplate(kebabName, pascalName, camelName, fields, validatorType) : schemasTpl(kebabName, pascalName, camelName)
|
|
2560
2600
|
},
|
|
2561
2601
|
{
|
|
2562
2602
|
name: `${kebabName}.service.ts`,
|
|
2563
|
-
content:
|
|
2603
|
+
content: serviceTpl(kebabName, pascalName, camelName)
|
|
2564
2604
|
},
|
|
2565
2605
|
{
|
|
2566
2606
|
name: `${kebabName}.controller.ts`,
|
|
2567
|
-
content:
|
|
2607
|
+
content: controllerTpl(kebabName, pascalName, camelName)
|
|
2568
2608
|
},
|
|
2569
|
-
{ name: "index.ts", content:
|
|
2609
|
+
{ name: "index.ts", content: moduleIndexTpl(kebabName, pascalName, camelName) }
|
|
2570
2610
|
];
|
|
2571
2611
|
if (options.repository !== false) {
|
|
2572
2612
|
files.push({
|
|
2573
2613
|
name: `${kebabName}.repository.ts`,
|
|
2574
|
-
content:
|
|
2614
|
+
content: repositoryTpl(kebabName, pascalName, camelName, pluralName)
|
|
2575
2615
|
});
|
|
2576
2616
|
}
|
|
2577
2617
|
if (options.routes !== false) {
|
|
2578
2618
|
files.push({
|
|
2579
2619
|
name: `${kebabName}.routes.ts`,
|
|
2580
|
-
content:
|
|
2620
|
+
content: routesTpl(kebabName, pascalName, camelName, pluralName)
|
|
2581
2621
|
});
|
|
2582
2622
|
}
|
|
2583
2623
|
for (const file of files) {
|
|
2584
|
-
await writeFile(
|
|
2624
|
+
await writeFile(import_path5.default.join(moduleDir, file.name), file.content);
|
|
2585
2625
|
}
|
|
2586
2626
|
if (options.withTests) {
|
|
2587
|
-
const testDir =
|
|
2627
|
+
const testDir = import_path5.default.join(moduleDir, "__tests__");
|
|
2628
|
+
const controllerTestTpl = await getTemplate("controller-test", controllerTestTemplate);
|
|
2629
|
+
const serviceTestTpl = await getTemplate("service-test", serviceTestTemplate);
|
|
2630
|
+
const integrationTestTpl = await getTemplate("integration-test", integrationTestTemplate);
|
|
2588
2631
|
await writeFile(
|
|
2589
|
-
|
|
2590
|
-
|
|
2632
|
+
import_path5.default.join(testDir, `${kebabName}.controller.test.ts`),
|
|
2633
|
+
controllerTestTpl(kebabName, pascalName, camelName)
|
|
2591
2634
|
);
|
|
2592
2635
|
await writeFile(
|
|
2593
|
-
|
|
2594
|
-
|
|
2636
|
+
import_path5.default.join(testDir, `${kebabName}.service.test.ts`),
|
|
2637
|
+
serviceTestTpl(kebabName, pascalName, camelName)
|
|
2595
2638
|
);
|
|
2596
2639
|
await writeFile(
|
|
2597
|
-
|
|
2598
|
-
|
|
2640
|
+
import_path5.default.join(testDir, `${kebabName}.integration.test.ts`),
|
|
2641
|
+
integrationTestTpl(kebabName, pascalName, camelName)
|
|
2599
2642
|
);
|
|
2600
2643
|
}
|
|
2601
2644
|
spinner.succeed(`Module "${pascalName}" generated successfully!`);
|
|
@@ -2653,8 +2696,8 @@ generateCommand.command("controller <name>").alias("c").description("Generate a
|
|
|
2653
2696
|
const pascalName = toPascalCase(name);
|
|
2654
2697
|
const camelName = toCamelCase(name);
|
|
2655
2698
|
const moduleName = options.module ? toKebabCase(options.module) : kebabName;
|
|
2656
|
-
const moduleDir =
|
|
2657
|
-
const filePath =
|
|
2699
|
+
const moduleDir = import_path5.default.join(getModulesDir(), moduleName);
|
|
2700
|
+
const filePath = import_path5.default.join(moduleDir, `${kebabName}.controller.ts`);
|
|
2658
2701
|
if (await fileExists(filePath)) {
|
|
2659
2702
|
spinner.stop();
|
|
2660
2703
|
displayError(ErrorTypes.FILE_ALREADY_EXISTS(`${kebabName}.controller.ts`));
|
|
@@ -2677,8 +2720,8 @@ generateCommand.command("service <name>").alias("s").description("Generate a ser
|
|
|
2677
2720
|
const pascalName = toPascalCase(name);
|
|
2678
2721
|
const camelName = toCamelCase(name);
|
|
2679
2722
|
const moduleName = options.module ? toKebabCase(options.module) : kebabName;
|
|
2680
|
-
const moduleDir =
|
|
2681
|
-
const filePath =
|
|
2723
|
+
const moduleDir = import_path5.default.join(getModulesDir(), moduleName);
|
|
2724
|
+
const filePath = import_path5.default.join(moduleDir, `${kebabName}.service.ts`);
|
|
2682
2725
|
if (await fileExists(filePath)) {
|
|
2683
2726
|
spinner.stop();
|
|
2684
2727
|
error(`Service "${kebabName}" already exists`);
|
|
@@ -2702,8 +2745,8 @@ generateCommand.command("repository <name>").alias("r").description("Generate a
|
|
|
2702
2745
|
const camelName = toCamelCase(name);
|
|
2703
2746
|
const pluralName = pluralize(kebabName);
|
|
2704
2747
|
const moduleName = options.module ? toKebabCase(options.module) : kebabName;
|
|
2705
|
-
const moduleDir =
|
|
2706
|
-
const filePath =
|
|
2748
|
+
const moduleDir = import_path5.default.join(getModulesDir(), moduleName);
|
|
2749
|
+
const filePath = import_path5.default.join(moduleDir, `${kebabName}.repository.ts`);
|
|
2707
2750
|
if (await fileExists(filePath)) {
|
|
2708
2751
|
spinner.stop();
|
|
2709
2752
|
error(`Repository "${kebabName}" already exists`);
|
|
@@ -2725,8 +2768,8 @@ generateCommand.command("types <name>").alias("t").description("Generate types/i
|
|
|
2725
2768
|
const kebabName = toKebabCase(name);
|
|
2726
2769
|
const pascalName = toPascalCase(name);
|
|
2727
2770
|
const moduleName = options.module ? toKebabCase(options.module) : kebabName;
|
|
2728
|
-
const moduleDir =
|
|
2729
|
-
const filePath =
|
|
2771
|
+
const moduleDir = import_path5.default.join(getModulesDir(), moduleName);
|
|
2772
|
+
const filePath = import_path5.default.join(moduleDir, `${kebabName}.types.ts`);
|
|
2730
2773
|
if (await fileExists(filePath)) {
|
|
2731
2774
|
spinner.stop();
|
|
2732
2775
|
error(`Types file "${kebabName}.types.ts" already exists`);
|
|
@@ -2749,8 +2792,8 @@ generateCommand.command("schema <name>").alias("v").description("Generate valida
|
|
|
2749
2792
|
const pascalName = toPascalCase(name);
|
|
2750
2793
|
const camelName = toCamelCase(name);
|
|
2751
2794
|
const moduleName = options.module ? toKebabCase(options.module) : kebabName;
|
|
2752
|
-
const moduleDir =
|
|
2753
|
-
const filePath =
|
|
2795
|
+
const moduleDir = import_path5.default.join(getModulesDir(), moduleName);
|
|
2796
|
+
const filePath = import_path5.default.join(moduleDir, `${kebabName}.schemas.ts`);
|
|
2754
2797
|
if (await fileExists(filePath)) {
|
|
2755
2798
|
spinner.stop();
|
|
2756
2799
|
error(`Schemas file "${kebabName}.schemas.ts" already exists`);
|
|
@@ -2774,8 +2817,8 @@ generateCommand.command("routes <name>").description("Generate routes").option("
|
|
|
2774
2817
|
const camelName = toCamelCase(name);
|
|
2775
2818
|
const pluralName = pluralize(kebabName);
|
|
2776
2819
|
const moduleName = options.module ? toKebabCase(options.module) : kebabName;
|
|
2777
|
-
const moduleDir =
|
|
2778
|
-
const filePath =
|
|
2820
|
+
const moduleDir = import_path5.default.join(getModulesDir(), moduleName);
|
|
2821
|
+
const filePath = import_path5.default.join(moduleDir, `${kebabName}.routes.ts`);
|
|
2779
2822
|
if (await fileExists(filePath)) {
|
|
2780
2823
|
spinner.stop();
|
|
2781
2824
|
error(`Routes file "${kebabName}.routes.ts" already exists`);
|
|
@@ -2863,21 +2906,21 @@ async function promptForFields() {
|
|
|
2863
2906
|
|
|
2864
2907
|
// src/cli/commands/add-module.ts
|
|
2865
2908
|
var import_commander3 = require("commander");
|
|
2866
|
-
var
|
|
2909
|
+
var import_path6 = __toESM(require("path"), 1);
|
|
2867
2910
|
var import_ora3 = __toESM(require("ora"), 1);
|
|
2868
2911
|
var import_chalk7 = __toESM(require("chalk"), 1);
|
|
2869
|
-
var
|
|
2912
|
+
var fs6 = __toESM(require("fs/promises"), 1);
|
|
2870
2913
|
|
|
2871
2914
|
// src/cli/utils/env-manager.ts
|
|
2872
|
-
var
|
|
2873
|
-
var
|
|
2915
|
+
var fs4 = __toESM(require("fs/promises"), 1);
|
|
2916
|
+
var path6 = __toESM(require("path"), 1);
|
|
2874
2917
|
var import_fs = require("fs");
|
|
2875
2918
|
var EnvManager = class {
|
|
2876
2919
|
envPath;
|
|
2877
2920
|
envExamplePath;
|
|
2878
2921
|
constructor(projectRoot) {
|
|
2879
|
-
this.envPath =
|
|
2880
|
-
this.envExamplePath =
|
|
2922
|
+
this.envPath = path6.join(projectRoot, ".env");
|
|
2923
|
+
this.envExamplePath = path6.join(projectRoot, ".env.example");
|
|
2881
2924
|
}
|
|
2882
2925
|
/**
|
|
2883
2926
|
* Add environment variables to .env file
|
|
@@ -2888,7 +2931,7 @@ var EnvManager = class {
|
|
|
2888
2931
|
let created2 = false;
|
|
2889
2932
|
let envContent = "";
|
|
2890
2933
|
if ((0, import_fs.existsSync)(this.envPath)) {
|
|
2891
|
-
envContent = await
|
|
2934
|
+
envContent = await fs4.readFile(this.envPath, "utf-8");
|
|
2892
2935
|
} else {
|
|
2893
2936
|
created2 = true;
|
|
2894
2937
|
}
|
|
@@ -2917,7 +2960,7 @@ var EnvManager = class {
|
|
|
2917
2960
|
}
|
|
2918
2961
|
newContent += "\n";
|
|
2919
2962
|
}
|
|
2920
|
-
await
|
|
2963
|
+
await fs4.writeFile(this.envPath, newContent, "utf-8");
|
|
2921
2964
|
if ((0, import_fs.existsSync)(this.envExamplePath)) {
|
|
2922
2965
|
await this.updateEnvExample(sections);
|
|
2923
2966
|
}
|
|
@@ -2929,7 +2972,7 @@ var EnvManager = class {
|
|
|
2929
2972
|
async updateEnvExample(sections) {
|
|
2930
2973
|
let exampleContent = "";
|
|
2931
2974
|
if ((0, import_fs.existsSync)(this.envExamplePath)) {
|
|
2932
|
-
exampleContent = await
|
|
2975
|
+
exampleContent = await fs4.readFile(this.envExamplePath, "utf-8");
|
|
2933
2976
|
}
|
|
2934
2977
|
const existingKeys = this.parseExistingKeys(exampleContent);
|
|
2935
2978
|
let newContent = exampleContent;
|
|
@@ -2953,7 +2996,7 @@ var EnvManager = class {
|
|
|
2953
2996
|
}
|
|
2954
2997
|
newContent += "\n";
|
|
2955
2998
|
}
|
|
2956
|
-
await
|
|
2999
|
+
await fs4.writeFile(this.envExamplePath, newContent, "utf-8");
|
|
2957
3000
|
}
|
|
2958
3001
|
/**
|
|
2959
3002
|
* Parse existing environment variable keys
|
|
@@ -3527,34 +3570,34 @@ var EnvManager = class {
|
|
|
3527
3570
|
};
|
|
3528
3571
|
|
|
3529
3572
|
// src/cli/utils/template-manager.ts
|
|
3530
|
-
var
|
|
3531
|
-
var
|
|
3573
|
+
var fs5 = __toESM(require("fs/promises"), 1);
|
|
3574
|
+
var path7 = __toESM(require("path"), 1);
|
|
3532
3575
|
var import_crypto = require("crypto");
|
|
3533
3576
|
var import_fs2 = require("fs");
|
|
3534
3577
|
var TemplateManager = class {
|
|
3535
3578
|
templatesDir;
|
|
3536
3579
|
manifestsDir;
|
|
3537
3580
|
constructor(projectRoot) {
|
|
3538
|
-
this.templatesDir =
|
|
3539
|
-
this.manifestsDir =
|
|
3581
|
+
this.templatesDir = path7.join(projectRoot, ".servcraft", "templates");
|
|
3582
|
+
this.manifestsDir = path7.join(projectRoot, ".servcraft", "manifests");
|
|
3540
3583
|
}
|
|
3541
3584
|
/**
|
|
3542
3585
|
* Initialize template system
|
|
3543
3586
|
*/
|
|
3544
3587
|
async initialize() {
|
|
3545
|
-
await
|
|
3546
|
-
await
|
|
3588
|
+
await fs5.mkdir(this.templatesDir, { recursive: true });
|
|
3589
|
+
await fs5.mkdir(this.manifestsDir, { recursive: true });
|
|
3547
3590
|
}
|
|
3548
3591
|
/**
|
|
3549
3592
|
* Save module template
|
|
3550
3593
|
*/
|
|
3551
3594
|
async saveTemplate(moduleName, files) {
|
|
3552
3595
|
await this.initialize();
|
|
3553
|
-
const moduleTemplateDir =
|
|
3554
|
-
await
|
|
3596
|
+
const moduleTemplateDir = path7.join(this.templatesDir, moduleName);
|
|
3597
|
+
await fs5.mkdir(moduleTemplateDir, { recursive: true });
|
|
3555
3598
|
for (const [fileName, content] of Object.entries(files)) {
|
|
3556
|
-
const filePath =
|
|
3557
|
-
await
|
|
3599
|
+
const filePath = path7.join(moduleTemplateDir, fileName);
|
|
3600
|
+
await fs5.writeFile(filePath, content, "utf-8");
|
|
3558
3601
|
}
|
|
3559
3602
|
}
|
|
3560
3603
|
/**
|
|
@@ -3562,8 +3605,8 @@ var TemplateManager = class {
|
|
|
3562
3605
|
*/
|
|
3563
3606
|
async getTemplate(moduleName, fileName) {
|
|
3564
3607
|
try {
|
|
3565
|
-
const filePath =
|
|
3566
|
-
return await
|
|
3608
|
+
const filePath = path7.join(this.templatesDir, moduleName, fileName);
|
|
3609
|
+
return await fs5.readFile(filePath, "utf-8");
|
|
3567
3610
|
} catch {
|
|
3568
3611
|
return null;
|
|
3569
3612
|
}
|
|
@@ -3587,16 +3630,16 @@ var TemplateManager = class {
|
|
|
3587
3630
|
installedAt: /* @__PURE__ */ new Date(),
|
|
3588
3631
|
updatedAt: /* @__PURE__ */ new Date()
|
|
3589
3632
|
};
|
|
3590
|
-
const manifestPath =
|
|
3591
|
-
await
|
|
3633
|
+
const manifestPath = path7.join(this.manifestsDir, `${moduleName}.json`);
|
|
3634
|
+
await fs5.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
3592
3635
|
}
|
|
3593
3636
|
/**
|
|
3594
3637
|
* Get module manifest
|
|
3595
3638
|
*/
|
|
3596
3639
|
async getManifest(moduleName) {
|
|
3597
3640
|
try {
|
|
3598
|
-
const manifestPath =
|
|
3599
|
-
const content = await
|
|
3641
|
+
const manifestPath = path7.join(this.manifestsDir, `${moduleName}.json`);
|
|
3642
|
+
const content = await fs5.readFile(manifestPath, "utf-8");
|
|
3600
3643
|
return JSON.parse(content);
|
|
3601
3644
|
} catch {
|
|
3602
3645
|
return null;
|
|
@@ -3624,7 +3667,7 @@ var TemplateManager = class {
|
|
|
3624
3667
|
}
|
|
3625
3668
|
const results = [];
|
|
3626
3669
|
for (const [fileName, fileInfo] of Object.entries(manifest.files)) {
|
|
3627
|
-
const filePath =
|
|
3670
|
+
const filePath = path7.join(moduleDir, fileName);
|
|
3628
3671
|
if (!(0, import_fs2.existsSync)(filePath)) {
|
|
3629
3672
|
results.push({
|
|
3630
3673
|
fileName,
|
|
@@ -3634,7 +3677,7 @@ var TemplateManager = class {
|
|
|
3634
3677
|
});
|
|
3635
3678
|
continue;
|
|
3636
3679
|
}
|
|
3637
|
-
const currentContent = await
|
|
3680
|
+
const currentContent = await fs5.readFile(filePath, "utf-8");
|
|
3638
3681
|
const currentHash = this.hashContent(currentContent);
|
|
3639
3682
|
results.push({
|
|
3640
3683
|
fileName,
|
|
@@ -3650,7 +3693,7 @@ var TemplateManager = class {
|
|
|
3650
3693
|
*/
|
|
3651
3694
|
async createBackup(moduleName, moduleDir) {
|
|
3652
3695
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").substring(0, 19);
|
|
3653
|
-
const backupDir =
|
|
3696
|
+
const backupDir = path7.join(path7.dirname(moduleDir), `${moduleName}.backup-${timestamp}`);
|
|
3654
3697
|
await this.copyDirectory(moduleDir, backupDir);
|
|
3655
3698
|
return backupDir;
|
|
3656
3699
|
}
|
|
@@ -3658,15 +3701,15 @@ var TemplateManager = class {
|
|
|
3658
3701
|
* Copy directory recursively
|
|
3659
3702
|
*/
|
|
3660
3703
|
async copyDirectory(src, dest) {
|
|
3661
|
-
await
|
|
3662
|
-
const entries = await
|
|
3704
|
+
await fs5.mkdir(dest, { recursive: true });
|
|
3705
|
+
const entries = await fs5.readdir(src, { withFileTypes: true });
|
|
3663
3706
|
for (const entry of entries) {
|
|
3664
|
-
const srcPath =
|
|
3665
|
-
const destPath =
|
|
3707
|
+
const srcPath = path7.join(src, entry.name);
|
|
3708
|
+
const destPath = path7.join(dest, entry.name);
|
|
3666
3709
|
if (entry.isDirectory()) {
|
|
3667
3710
|
await this.copyDirectory(srcPath, destPath);
|
|
3668
3711
|
} else {
|
|
3669
|
-
await
|
|
3712
|
+
await fs5.copyFile(srcPath, destPath);
|
|
3670
3713
|
}
|
|
3671
3714
|
}
|
|
3672
3715
|
}
|
|
@@ -3763,8 +3806,8 @@ var TemplateManager = class {
|
|
|
3763
3806
|
}
|
|
3764
3807
|
manifest.files = fileHashes;
|
|
3765
3808
|
manifest.updatedAt = /* @__PURE__ */ new Date();
|
|
3766
|
-
const manifestPath =
|
|
3767
|
-
await
|
|
3809
|
+
const manifestPath = path7.join(this.manifestsDir, `${moduleName}.json`);
|
|
3810
|
+
await fs5.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
3768
3811
|
}
|
|
3769
3812
|
};
|
|
3770
3813
|
|
|
@@ -4121,7 +4164,7 @@ var addModuleCommand = new import_commander3.Command("add").description("Add a p
|
|
|
4121
4164
|
}
|
|
4122
4165
|
const spinner = (0, import_ora3.default)(`Adding ${module2.name} module...`).start();
|
|
4123
4166
|
try {
|
|
4124
|
-
const moduleDir =
|
|
4167
|
+
const moduleDir = import_path6.default.join(getModulesDir(), moduleName);
|
|
4125
4168
|
const templateManager = new TemplateManager(process.cwd());
|
|
4126
4169
|
const moduleExists = await fileExists(moduleDir);
|
|
4127
4170
|
if (moduleExists) {
|
|
@@ -4154,7 +4197,7 @@ var addModuleCommand = new import_commander3.Command("add").description("Add a p
|
|
|
4154
4197
|
const backupPath = await templateManager.createBackup(moduleName, moduleDir);
|
|
4155
4198
|
InteractivePrompt.showBackupCreated(backupPath);
|
|
4156
4199
|
}
|
|
4157
|
-
await
|
|
4200
|
+
await fs6.rm(moduleDir, { recursive: true, force: true });
|
|
4158
4201
|
await ensureDir(moduleDir);
|
|
4159
4202
|
await generateModuleFiles(moduleName, moduleDir);
|
|
4160
4203
|
const files = await getModuleFiles(moduleName, moduleDir);
|
|
@@ -4265,7 +4308,7 @@ export * from './auth.schemas.js';
|
|
|
4265
4308
|
`
|
|
4266
4309
|
};
|
|
4267
4310
|
for (const [name, content] of Object.entries(files)) {
|
|
4268
|
-
await writeFile(
|
|
4311
|
+
await writeFile(import_path6.default.join(dir, name), content);
|
|
4269
4312
|
}
|
|
4270
4313
|
}
|
|
4271
4314
|
async function generateUsersModule(dir) {
|
|
@@ -4305,7 +4348,7 @@ export * from './user.schemas.js';
|
|
|
4305
4348
|
`
|
|
4306
4349
|
};
|
|
4307
4350
|
for (const [name, content] of Object.entries(files)) {
|
|
4308
|
-
await writeFile(
|
|
4351
|
+
await writeFile(import_path6.default.join(dir, name), content);
|
|
4309
4352
|
}
|
|
4310
4353
|
}
|
|
4311
4354
|
async function generateEmailModule(dir) {
|
|
@@ -4362,7 +4405,7 @@ export { EmailService, emailService } from './email.service.js';
|
|
|
4362
4405
|
`
|
|
4363
4406
|
};
|
|
4364
4407
|
for (const [name, content] of Object.entries(files)) {
|
|
4365
|
-
await writeFile(
|
|
4408
|
+
await writeFile(import_path6.default.join(dir, name), content);
|
|
4366
4409
|
}
|
|
4367
4410
|
}
|
|
4368
4411
|
async function generateAuditModule(dir) {
|
|
@@ -4406,7 +4449,7 @@ export { AuditService, auditService } from './audit.service.js';
|
|
|
4406
4449
|
`
|
|
4407
4450
|
};
|
|
4408
4451
|
for (const [name, content] of Object.entries(files)) {
|
|
4409
|
-
await writeFile(
|
|
4452
|
+
await writeFile(import_path6.default.join(dir, name), content);
|
|
4410
4453
|
}
|
|
4411
4454
|
}
|
|
4412
4455
|
async function generateUploadModule(dir) {
|
|
@@ -4432,7 +4475,7 @@ export interface UploadOptions {
|
|
|
4432
4475
|
`
|
|
4433
4476
|
};
|
|
4434
4477
|
for (const [name, content] of Object.entries(files)) {
|
|
4435
|
-
await writeFile(
|
|
4478
|
+
await writeFile(import_path6.default.join(dir, name), content);
|
|
4436
4479
|
}
|
|
4437
4480
|
}
|
|
4438
4481
|
async function generateCacheModule(dir) {
|
|
@@ -4478,7 +4521,7 @@ export { CacheService, cacheService } from './cache.service.js';
|
|
|
4478
4521
|
`
|
|
4479
4522
|
};
|
|
4480
4523
|
for (const [name, content] of Object.entries(files)) {
|
|
4481
|
-
await writeFile(
|
|
4524
|
+
await writeFile(import_path6.default.join(dir, name), content);
|
|
4482
4525
|
}
|
|
4483
4526
|
}
|
|
4484
4527
|
async function generateGenericModule(dir, name) {
|
|
@@ -4492,22 +4535,22 @@ export interface ${name.charAt(0).toUpperCase() + name.slice(1)}Data {
|
|
|
4492
4535
|
`
|
|
4493
4536
|
};
|
|
4494
4537
|
for (const [fileName, content] of Object.entries(files)) {
|
|
4495
|
-
await writeFile(
|
|
4538
|
+
await writeFile(import_path6.default.join(dir, fileName), content);
|
|
4496
4539
|
}
|
|
4497
4540
|
}
|
|
4498
4541
|
async function findServercraftModules() {
|
|
4499
|
-
const scriptDir =
|
|
4542
|
+
const scriptDir = import_path6.default.dirname(new URL(importMetaUrl).pathname);
|
|
4500
4543
|
const possiblePaths = [
|
|
4501
4544
|
// Local node_modules (when servcraft is a dependency)
|
|
4502
|
-
|
|
4545
|
+
import_path6.default.join(process.cwd(), "node_modules", "servcraft", "src", "modules"),
|
|
4503
4546
|
// From dist/cli/index.js -> src/modules (npx or global install)
|
|
4504
|
-
|
|
4547
|
+
import_path6.default.resolve(scriptDir, "..", "..", "src", "modules"),
|
|
4505
4548
|
// From src/cli/commands/add-module.ts -> src/modules (development)
|
|
4506
|
-
|
|
4549
|
+
import_path6.default.resolve(scriptDir, "..", "..", "modules")
|
|
4507
4550
|
];
|
|
4508
4551
|
for (const p of possiblePaths) {
|
|
4509
4552
|
try {
|
|
4510
|
-
const stats = await
|
|
4553
|
+
const stats = await fs6.stat(p);
|
|
4511
4554
|
if (stats.isDirectory()) {
|
|
4512
4555
|
return p;
|
|
4513
4556
|
}
|
|
@@ -4527,7 +4570,7 @@ async function generateModuleFiles(moduleName, moduleDir) {
|
|
|
4527
4570
|
const sourceDirName = moduleNameMap[moduleName] || moduleName;
|
|
4528
4571
|
const servercraftModulesDir = await findServercraftModules();
|
|
4529
4572
|
if (servercraftModulesDir) {
|
|
4530
|
-
const sourceModuleDir =
|
|
4573
|
+
const sourceModuleDir = import_path6.default.join(servercraftModulesDir, sourceDirName);
|
|
4531
4574
|
if (await fileExists(sourceModuleDir)) {
|
|
4532
4575
|
await copyModuleFromSource(sourceModuleDir, moduleDir);
|
|
4533
4576
|
return;
|
|
@@ -4557,26 +4600,26 @@ async function generateModuleFiles(moduleName, moduleDir) {
|
|
|
4557
4600
|
}
|
|
4558
4601
|
}
|
|
4559
4602
|
async function copyModuleFromSource(sourceDir, targetDir) {
|
|
4560
|
-
const entries = await
|
|
4603
|
+
const entries = await fs6.readdir(sourceDir, { withFileTypes: true });
|
|
4561
4604
|
for (const entry of entries) {
|
|
4562
|
-
const sourcePath =
|
|
4563
|
-
const targetPath =
|
|
4605
|
+
const sourcePath = import_path6.default.join(sourceDir, entry.name);
|
|
4606
|
+
const targetPath = import_path6.default.join(targetDir, entry.name);
|
|
4564
4607
|
if (entry.isDirectory()) {
|
|
4565
|
-
await
|
|
4608
|
+
await fs6.mkdir(targetPath, { recursive: true });
|
|
4566
4609
|
await copyModuleFromSource(sourcePath, targetPath);
|
|
4567
4610
|
} else {
|
|
4568
|
-
await
|
|
4611
|
+
await fs6.copyFile(sourcePath, targetPath);
|
|
4569
4612
|
}
|
|
4570
4613
|
}
|
|
4571
4614
|
}
|
|
4572
4615
|
async function getModuleFiles(moduleName, moduleDir) {
|
|
4573
4616
|
const files = {};
|
|
4574
|
-
const entries = await
|
|
4617
|
+
const entries = await fs6.readdir(moduleDir);
|
|
4575
4618
|
for (const entry of entries) {
|
|
4576
|
-
const filePath =
|
|
4577
|
-
const stat2 = await
|
|
4619
|
+
const filePath = import_path6.default.join(moduleDir, entry);
|
|
4620
|
+
const stat2 = await fs6.stat(filePath);
|
|
4578
4621
|
if (stat2.isFile() && entry.endsWith(".ts")) {
|
|
4579
|
-
const content = await
|
|
4622
|
+
const content = await fs6.readFile(filePath, "utf-8");
|
|
4580
4623
|
files[entry] = content;
|
|
4581
4624
|
}
|
|
4582
4625
|
}
|
|
@@ -4591,8 +4634,8 @@ async function showDiffForModule(templateManager, moduleName, moduleDir) {
|
|
|
4591
4634
|
if (file.isModified) {
|
|
4592
4635
|
console.log(import_chalk7.default.yellow(`
|
|
4593
4636
|
\u{1F4C4} ${file.fileName}:`));
|
|
4594
|
-
const currentPath =
|
|
4595
|
-
const currentContent = await
|
|
4637
|
+
const currentPath = import_path6.default.join(moduleDir, file.fileName);
|
|
4638
|
+
const currentContent = await fs6.readFile(currentPath, "utf-8");
|
|
4596
4639
|
const originalContent = await templateManager.getTemplate(moduleName, file.fileName);
|
|
4597
4640
|
if (originalContent) {
|
|
4598
4641
|
const diff = templateManager.generateDiff(originalContent, currentContent);
|
|
@@ -4604,11 +4647,11 @@ async function showDiffForModule(templateManager, moduleName, moduleDir) {
|
|
|
4604
4647
|
async function performSmartMerge(templateManager, moduleName, moduleDir, _displayName) {
|
|
4605
4648
|
const spinner = (0, import_ora3.default)("Analyzing files for merge...").start();
|
|
4606
4649
|
const newFiles = {};
|
|
4607
|
-
const templateDir =
|
|
4650
|
+
const templateDir = import_path6.default.join(templateManager["templatesDir"], moduleName);
|
|
4608
4651
|
try {
|
|
4609
|
-
const entries = await
|
|
4652
|
+
const entries = await fs6.readdir(templateDir);
|
|
4610
4653
|
for (const entry of entries) {
|
|
4611
|
-
const content = await
|
|
4654
|
+
const content = await fs6.readFile(import_path6.default.join(templateDir, entry), "utf-8");
|
|
4612
4655
|
newFiles[entry] = content;
|
|
4613
4656
|
}
|
|
4614
4657
|
} catch {
|
|
@@ -4626,7 +4669,7 @@ async function performSmartMerge(templateManager, moduleName, moduleDir, _displa
|
|
|
4626
4669
|
};
|
|
4627
4670
|
for (const fileInfo of modifiedFiles) {
|
|
4628
4671
|
const fileName = fileInfo.fileName;
|
|
4629
|
-
const filePath =
|
|
4672
|
+
const filePath = import_path6.default.join(moduleDir, fileName);
|
|
4630
4673
|
const newContent = newFiles[fileName];
|
|
4631
4674
|
if (!newContent) {
|
|
4632
4675
|
continue;
|
|
@@ -4639,7 +4682,7 @@ async function performSmartMerge(templateManager, moduleName, moduleDir, _displa
|
|
|
4639
4682
|
} else if (batchAction === "overwrite-all") {
|
|
4640
4683
|
fileAction = "overwrite";
|
|
4641
4684
|
} else {
|
|
4642
|
-
const currentContent = await
|
|
4685
|
+
const currentContent = await fs6.readFile(filePath, "utf-8");
|
|
4643
4686
|
const yourLines = currentContent.split("\n").length;
|
|
4644
4687
|
const newLines = newContent.split("\n").length;
|
|
4645
4688
|
const choice = await InteractivePrompt.askFileAction(
|
|
@@ -4663,20 +4706,20 @@ async function performSmartMerge(templateManager, moduleName, moduleDir, _displa
|
|
|
4663
4706
|
continue;
|
|
4664
4707
|
}
|
|
4665
4708
|
if (fileAction === "overwrite") {
|
|
4666
|
-
await
|
|
4709
|
+
await fs6.writeFile(filePath, newContent, "utf-8");
|
|
4667
4710
|
stats.overwritten++;
|
|
4668
4711
|
continue;
|
|
4669
4712
|
}
|
|
4670
4713
|
if (fileAction === "merge") {
|
|
4671
4714
|
const originalContent = await templateManager.getTemplate(moduleName, fileName);
|
|
4672
|
-
const currentContent = await
|
|
4715
|
+
const currentContent = await fs6.readFile(filePath, "utf-8");
|
|
4673
4716
|
if (originalContent) {
|
|
4674
4717
|
const mergeResult = await templateManager.mergeFiles(
|
|
4675
4718
|
originalContent,
|
|
4676
4719
|
currentContent,
|
|
4677
4720
|
newContent
|
|
4678
4721
|
);
|
|
4679
|
-
await
|
|
4722
|
+
await fs6.writeFile(filePath, mergeResult.merged, "utf-8");
|
|
4680
4723
|
if (mergeResult.hasConflicts) {
|
|
4681
4724
|
stats.conflicts++;
|
|
4682
4725
|
InteractivePrompt.displayConflicts(mergeResult.conflicts);
|
|
@@ -4684,7 +4727,7 @@ async function performSmartMerge(templateManager, moduleName, moduleDir, _displa
|
|
|
4684
4727
|
stats.merged++;
|
|
4685
4728
|
}
|
|
4686
4729
|
} else {
|
|
4687
|
-
await
|
|
4730
|
+
await fs6.writeFile(filePath, newContent, "utf-8");
|
|
4688
4731
|
stats.overwritten++;
|
|
4689
4732
|
}
|
|
4690
4733
|
}
|
|
@@ -4789,14 +4832,14 @@ dbCommand.command("status").description("Show migration status").action(async ()
|
|
|
4789
4832
|
|
|
4790
4833
|
// src/cli/commands/docs.ts
|
|
4791
4834
|
var import_commander5 = require("commander");
|
|
4792
|
-
var
|
|
4793
|
-
var
|
|
4835
|
+
var import_path8 = __toESM(require("path"), 1);
|
|
4836
|
+
var import_promises5 = __toESM(require("fs/promises"), 1);
|
|
4794
4837
|
var import_ora6 = __toESM(require("ora"), 1);
|
|
4795
4838
|
var import_chalk9 = __toESM(require("chalk"), 1);
|
|
4796
4839
|
|
|
4797
4840
|
// src/cli/utils/docs-generator.ts
|
|
4798
|
-
var
|
|
4799
|
-
var
|
|
4841
|
+
var import_promises4 = __toESM(require("fs/promises"), 1);
|
|
4842
|
+
var import_path7 = __toESM(require("path"), 1);
|
|
4800
4843
|
var import_ora5 = __toESM(require("ora"), 1);
|
|
4801
4844
|
|
|
4802
4845
|
// src/core/server.ts
|
|
@@ -5517,11 +5560,11 @@ function validateQuery(schema, data) {
|
|
|
5517
5560
|
function formatZodErrors(error2) {
|
|
5518
5561
|
const errors = {};
|
|
5519
5562
|
for (const issue of error2.issues) {
|
|
5520
|
-
const
|
|
5521
|
-
if (!errors[
|
|
5522
|
-
errors[
|
|
5563
|
+
const path15 = issue.path.join(".") || "root";
|
|
5564
|
+
if (!errors[path15]) {
|
|
5565
|
+
errors[path15] = [];
|
|
5523
5566
|
}
|
|
5524
|
-
errors[
|
|
5567
|
+
errors[path15].push(issue.message);
|
|
5525
5568
|
}
|
|
5526
5569
|
return errors;
|
|
5527
5570
|
}
|
|
@@ -5758,7 +5801,18 @@ function getSkip(params) {
|
|
|
5758
5801
|
}
|
|
5759
5802
|
|
|
5760
5803
|
// src/modules/user/user.repository.ts
|
|
5761
|
-
var
|
|
5804
|
+
var UserRole = {
|
|
5805
|
+
USER: "USER",
|
|
5806
|
+
MODERATOR: "MODERATOR",
|
|
5807
|
+
ADMIN: "ADMIN",
|
|
5808
|
+
SUPER_ADMIN: "SUPER_ADMIN"
|
|
5809
|
+
};
|
|
5810
|
+
var UserStatus = {
|
|
5811
|
+
ACTIVE: "ACTIVE",
|
|
5812
|
+
INACTIVE: "INACTIVE",
|
|
5813
|
+
SUSPENDED: "SUSPENDED",
|
|
5814
|
+
BANNED: "BANNED"
|
|
5815
|
+
};
|
|
5762
5816
|
var UserRepository = class {
|
|
5763
5817
|
/**
|
|
5764
5818
|
* Find user by ID
|
|
@@ -5808,7 +5862,7 @@ var UserRepository = class {
|
|
|
5808
5862
|
password: data.password,
|
|
5809
5863
|
name: data.name,
|
|
5810
5864
|
role: this.mapRoleToEnum(data.role || "user"),
|
|
5811
|
-
status:
|
|
5865
|
+
status: UserStatus.ACTIVE,
|
|
5812
5866
|
emailVerified: false
|
|
5813
5867
|
}
|
|
5814
5868
|
});
|
|
@@ -5941,22 +5995,22 @@ var UserRepository = class {
|
|
|
5941
5995
|
*/
|
|
5942
5996
|
mapRoleToEnum(role) {
|
|
5943
5997
|
const roleMap = {
|
|
5944
|
-
user:
|
|
5945
|
-
moderator:
|
|
5946
|
-
admin:
|
|
5947
|
-
super_admin:
|
|
5998
|
+
user: UserRole.USER,
|
|
5999
|
+
moderator: UserRole.MODERATOR,
|
|
6000
|
+
admin: UserRole.ADMIN,
|
|
6001
|
+
super_admin: UserRole.SUPER_ADMIN
|
|
5948
6002
|
};
|
|
5949
|
-
return roleMap[role] ||
|
|
6003
|
+
return roleMap[role] || UserRole.USER;
|
|
5950
6004
|
}
|
|
5951
6005
|
/**
|
|
5952
6006
|
* Map Prisma enum to application role
|
|
5953
6007
|
*/
|
|
5954
6008
|
mapEnumToRole(role) {
|
|
5955
6009
|
const roleMap = {
|
|
5956
|
-
[
|
|
5957
|
-
[
|
|
5958
|
-
[
|
|
5959
|
-
[
|
|
6010
|
+
[UserRole.USER]: "user",
|
|
6011
|
+
[UserRole.MODERATOR]: "moderator",
|
|
6012
|
+
[UserRole.ADMIN]: "admin",
|
|
6013
|
+
[UserRole.SUPER_ADMIN]: "super_admin"
|
|
5960
6014
|
};
|
|
5961
6015
|
return roleMap[role];
|
|
5962
6016
|
}
|
|
@@ -5965,22 +6019,22 @@ var UserRepository = class {
|
|
|
5965
6019
|
*/
|
|
5966
6020
|
mapStatusToEnum(status) {
|
|
5967
6021
|
const statusMap = {
|
|
5968
|
-
active:
|
|
5969
|
-
inactive:
|
|
5970
|
-
suspended:
|
|
5971
|
-
banned:
|
|
6022
|
+
active: UserStatus.ACTIVE,
|
|
6023
|
+
inactive: UserStatus.INACTIVE,
|
|
6024
|
+
suspended: UserStatus.SUSPENDED,
|
|
6025
|
+
banned: UserStatus.BANNED
|
|
5972
6026
|
};
|
|
5973
|
-
return statusMap[status] ||
|
|
6027
|
+
return statusMap[status] || UserStatus.ACTIVE;
|
|
5974
6028
|
}
|
|
5975
6029
|
/**
|
|
5976
6030
|
* Map Prisma enum to application status
|
|
5977
6031
|
*/
|
|
5978
6032
|
mapEnumToStatus(status) {
|
|
5979
6033
|
const statusMap = {
|
|
5980
|
-
[
|
|
5981
|
-
[
|
|
5982
|
-
[
|
|
5983
|
-
[
|
|
6034
|
+
[UserStatus.ACTIVE]: "active",
|
|
6035
|
+
[UserStatus.INACTIVE]: "inactive",
|
|
6036
|
+
[UserStatus.SUSPENDED]: "suspended",
|
|
6037
|
+
[UserStatus.BANNED]: "banned"
|
|
5984
6038
|
};
|
|
5985
6039
|
return statusMap[status];
|
|
5986
6040
|
}
|
|
@@ -6354,9 +6408,9 @@ async function generateDocs(outputPath = "openapi.json", silent = false) {
|
|
|
6354
6408
|
await registerUserModule(app, authService);
|
|
6355
6409
|
await app.ready();
|
|
6356
6410
|
const spec = app.swagger();
|
|
6357
|
-
const absoluteOutput =
|
|
6358
|
-
await
|
|
6359
|
-
await
|
|
6411
|
+
const absoluteOutput = import_path7.default.resolve(outputPath);
|
|
6412
|
+
await import_promises4.default.mkdir(import_path7.default.dirname(absoluteOutput), { recursive: true });
|
|
6413
|
+
await import_promises4.default.writeFile(absoluteOutput, JSON.stringify(spec, null, 2), "utf8");
|
|
6360
6414
|
spinner?.succeed(`OpenAPI spec generated at ${absoluteOutput}`);
|
|
6361
6415
|
await app.close();
|
|
6362
6416
|
return absoluteOutput;
|
|
@@ -6372,10 +6426,10 @@ docsCommand.command("generate").alias("gen").description("Generate OpenAPI/Swagg
|
|
|
6372
6426
|
try {
|
|
6373
6427
|
const outputPath = await generateDocs(options.output, false);
|
|
6374
6428
|
if (options.format === "yaml") {
|
|
6375
|
-
const jsonContent = await
|
|
6429
|
+
const jsonContent = await import_promises5.default.readFile(outputPath, "utf-8");
|
|
6376
6430
|
const spec = JSON.parse(jsonContent);
|
|
6377
6431
|
const yamlPath = outputPath.replace(".json", ".yaml");
|
|
6378
|
-
await
|
|
6432
|
+
await import_promises5.default.writeFile(yamlPath, jsonToYaml(spec));
|
|
6379
6433
|
success(`YAML documentation generated: ${yamlPath}`);
|
|
6380
6434
|
}
|
|
6381
6435
|
console.log("\n\u{1F4DA} Documentation URLs:");
|
|
@@ -6400,14 +6454,14 @@ docsCommand.command("export").description("Export documentation to Postman, Inso
|
|
|
6400
6454
|
const spinner = (0, import_ora6.default)("Exporting documentation...").start();
|
|
6401
6455
|
try {
|
|
6402
6456
|
const projectRoot = getProjectRoot();
|
|
6403
|
-
const specPath =
|
|
6457
|
+
const specPath = import_path8.default.join(projectRoot, "openapi.json");
|
|
6404
6458
|
try {
|
|
6405
|
-
await
|
|
6459
|
+
await import_promises5.default.access(specPath);
|
|
6406
6460
|
} catch {
|
|
6407
6461
|
spinner.text = "Generating OpenAPI spec first...";
|
|
6408
6462
|
await generateDocs("openapi.json", true);
|
|
6409
6463
|
}
|
|
6410
|
-
const specContent = await
|
|
6464
|
+
const specContent = await import_promises5.default.readFile(specPath, "utf-8");
|
|
6411
6465
|
const spec = JSON.parse(specContent);
|
|
6412
6466
|
let output;
|
|
6413
6467
|
let defaultName;
|
|
@@ -6427,8 +6481,8 @@ docsCommand.command("export").description("Export documentation to Postman, Inso
|
|
|
6427
6481
|
default:
|
|
6428
6482
|
throw new Error(`Unknown format: ${options.format}`);
|
|
6429
6483
|
}
|
|
6430
|
-
const outPath =
|
|
6431
|
-
await
|
|
6484
|
+
const outPath = import_path8.default.join(projectRoot, options.output || defaultName);
|
|
6485
|
+
await import_promises5.default.writeFile(outPath, output);
|
|
6432
6486
|
spinner.succeed(`Exported to: ${options.output || defaultName}`);
|
|
6433
6487
|
if (options.format === "postman") {
|
|
6434
6488
|
info("\n Import in Postman: File > Import > Select file");
|
|
@@ -6441,13 +6495,13 @@ docsCommand.command("export").description("Export documentation to Postman, Inso
|
|
|
6441
6495
|
docsCommand.command("status").description("Show documentation status").action(async () => {
|
|
6442
6496
|
const projectRoot = getProjectRoot();
|
|
6443
6497
|
console.log(import_chalk9.default.bold("\n\u{1F4CA} Documentation Status\n"));
|
|
6444
|
-
const specPath =
|
|
6498
|
+
const specPath = import_path8.default.join(projectRoot, "openapi.json");
|
|
6445
6499
|
try {
|
|
6446
|
-
const stat2 = await
|
|
6500
|
+
const stat2 = await import_promises5.default.stat(specPath);
|
|
6447
6501
|
success(
|
|
6448
6502
|
`openapi.json exists (${formatBytes(stat2.size)}, modified ${formatDate(stat2.mtime)})`
|
|
6449
6503
|
);
|
|
6450
|
-
const content = await
|
|
6504
|
+
const content = await import_promises5.default.readFile(specPath, "utf-8");
|
|
6451
6505
|
const spec = JSON.parse(content);
|
|
6452
6506
|
const pathCount = Object.keys(spec.paths || {}).length;
|
|
6453
6507
|
info(` ${pathCount} endpoints documented`);
|
|
@@ -6559,7 +6613,7 @@ function formatDate(date) {
|
|
|
6559
6613
|
// src/cli/commands/list.ts
|
|
6560
6614
|
var import_commander6 = require("commander");
|
|
6561
6615
|
var import_chalk10 = __toESM(require("chalk"), 1);
|
|
6562
|
-
var
|
|
6616
|
+
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
6563
6617
|
var AVAILABLE_MODULES2 = {
|
|
6564
6618
|
// Core
|
|
6565
6619
|
auth: {
|
|
@@ -6674,7 +6728,7 @@ var AVAILABLE_MODULES2 = {
|
|
|
6674
6728
|
async function getInstalledModules() {
|
|
6675
6729
|
try {
|
|
6676
6730
|
const modulesDir = getModulesDir();
|
|
6677
|
-
const entries = await
|
|
6731
|
+
const entries = await import_promises6.default.readdir(modulesDir, { withFileTypes: true });
|
|
6678
6732
|
return entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
6679
6733
|
} catch {
|
|
6680
6734
|
return [];
|
|
@@ -6783,10 +6837,10 @@ var listCommand = new import_commander6.Command("list").alias("ls").description(
|
|
|
6783
6837
|
|
|
6784
6838
|
// src/cli/commands/remove.ts
|
|
6785
6839
|
var import_commander7 = require("commander");
|
|
6786
|
-
var
|
|
6840
|
+
var import_path9 = __toESM(require("path"), 1);
|
|
6787
6841
|
var import_ora7 = __toESM(require("ora"), 1);
|
|
6788
6842
|
var import_chalk11 = __toESM(require("chalk"), 1);
|
|
6789
|
-
var
|
|
6843
|
+
var import_promises7 = __toESM(require("fs/promises"), 1);
|
|
6790
6844
|
var import_inquirer4 = __toESM(require("inquirer"), 1);
|
|
6791
6845
|
var removeCommand = new import_commander7.Command("remove").alias("rm").description("Remove an installed module from your project").argument("<module>", "Module to remove").option("-y, --yes", "Skip confirmation prompt").option("--keep-env", "Keep environment variables").action(async (moduleName, options) => {
|
|
6792
6846
|
const projectError = validateProject();
|
|
@@ -6795,9 +6849,9 @@ var removeCommand = new import_commander7.Command("remove").alias("rm").descript
|
|
|
6795
6849
|
return;
|
|
6796
6850
|
}
|
|
6797
6851
|
console.log(import_chalk11.default.bold.cyan("\n\u{1F5D1}\uFE0F ServCraft Module Removal\n"));
|
|
6798
|
-
const moduleDir =
|
|
6852
|
+
const moduleDir = import_path9.default.join(getModulesDir(), moduleName);
|
|
6799
6853
|
try {
|
|
6800
|
-
const exists = await
|
|
6854
|
+
const exists = await import_promises7.default.access(moduleDir).then(() => true).catch(() => false);
|
|
6801
6855
|
if (!exists) {
|
|
6802
6856
|
displayError(
|
|
6803
6857
|
new ServCraftError(`Module "${moduleName}" is not installed`, [
|
|
@@ -6807,7 +6861,7 @@ var removeCommand = new import_commander7.Command("remove").alias("rm").descript
|
|
|
6807
6861
|
);
|
|
6808
6862
|
return;
|
|
6809
6863
|
}
|
|
6810
|
-
const files = await
|
|
6864
|
+
const files = await import_promises7.default.readdir(moduleDir);
|
|
6811
6865
|
const fileCount = files.length;
|
|
6812
6866
|
if (!options?.yes) {
|
|
6813
6867
|
console.log(import_chalk11.default.yellow(`\u26A0 This will remove the "${moduleName}" module:`));
|
|
@@ -6828,7 +6882,7 @@ var removeCommand = new import_commander7.Command("remove").alias("rm").descript
|
|
|
6828
6882
|
}
|
|
6829
6883
|
}
|
|
6830
6884
|
const spinner = (0, import_ora7.default)("Removing module...").start();
|
|
6831
|
-
await
|
|
6885
|
+
await import_promises7.default.rm(moduleDir, { recursive: true, force: true });
|
|
6832
6886
|
spinner.succeed(`Module "${moduleName}" removed successfully!`);
|
|
6833
6887
|
console.log("\n" + import_chalk11.default.bold("\u2713 Removed:"));
|
|
6834
6888
|
success(` src/modules/${moduleName}/ (${fileCount} files)`);
|
|
@@ -6854,7 +6908,7 @@ var removeCommand = new import_commander7.Command("remove").alias("rm").descript
|
|
|
6854
6908
|
// src/cli/commands/doctor.ts
|
|
6855
6909
|
var import_commander8 = require("commander");
|
|
6856
6910
|
var import_chalk12 = __toESM(require("chalk"), 1);
|
|
6857
|
-
var
|
|
6911
|
+
var import_promises8 = __toESM(require("fs/promises"), 1);
|
|
6858
6912
|
async function checkNodeVersion() {
|
|
6859
6913
|
const version = process.version;
|
|
6860
6914
|
const major = parseInt(version.slice(1).split(".")[0] || "0", 10);
|
|
@@ -6871,7 +6925,7 @@ async function checkNodeVersion() {
|
|
|
6871
6925
|
async function checkPackageJson() {
|
|
6872
6926
|
const checks = [];
|
|
6873
6927
|
try {
|
|
6874
|
-
const content = await
|
|
6928
|
+
const content = await import_promises8.default.readFile("package.json", "utf-8");
|
|
6875
6929
|
const pkg = JSON.parse(content);
|
|
6876
6930
|
checks.push({ name: "package.json", status: "pass", message: "Found" });
|
|
6877
6931
|
if (pkg.dependencies?.fastify) {
|
|
@@ -6899,7 +6953,7 @@ async function checkDirectories() {
|
|
|
6899
6953
|
const dirs = ["src", "node_modules", ".git", ".env"];
|
|
6900
6954
|
for (const dir of dirs) {
|
|
6901
6955
|
try {
|
|
6902
|
-
await
|
|
6956
|
+
await import_promises8.default.access(dir);
|
|
6903
6957
|
checks.push({ name: dir, status: "pass", message: "Exists" });
|
|
6904
6958
|
} catch {
|
|
6905
6959
|
const isCritical = dir === "src" || dir === "node_modules";
|
|
@@ -6948,12 +7002,12 @@ ${import_chalk12.default.green(pass + " passed")} | ${import_chalk12.default.yel
|
|
|
6948
7002
|
// src/cli/commands/update.ts
|
|
6949
7003
|
var import_commander9 = require("commander");
|
|
6950
7004
|
var import_chalk13 = __toESM(require("chalk"), 1);
|
|
6951
|
-
var
|
|
6952
|
-
var
|
|
7005
|
+
var import_promises9 = __toESM(require("fs/promises"), 1);
|
|
7006
|
+
var import_path10 = __toESM(require("path"), 1);
|
|
6953
7007
|
var import_url = require("url");
|
|
6954
7008
|
var import_inquirer5 = __toESM(require("inquirer"), 1);
|
|
6955
7009
|
var __filename2 = (0, import_url.fileURLToPath)(importMetaUrl);
|
|
6956
|
-
var __dirname =
|
|
7010
|
+
var __dirname = import_path10.default.dirname(__filename2);
|
|
6957
7011
|
var AVAILABLE_MODULES3 = [
|
|
6958
7012
|
"auth",
|
|
6959
7013
|
"users",
|
|
@@ -6981,7 +7035,7 @@ var AVAILABLE_MODULES3 = [
|
|
|
6981
7035
|
async function getInstalledModules2() {
|
|
6982
7036
|
try {
|
|
6983
7037
|
const modulesDir = getModulesDir();
|
|
6984
|
-
const entries = await
|
|
7038
|
+
const entries = await import_promises9.default.readdir(modulesDir, { withFileTypes: true });
|
|
6985
7039
|
const installedModules = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => AVAILABLE_MODULES3.includes(name));
|
|
6986
7040
|
return installedModules;
|
|
6987
7041
|
} catch {
|
|
@@ -6989,16 +7043,16 @@ async function getInstalledModules2() {
|
|
|
6989
7043
|
}
|
|
6990
7044
|
}
|
|
6991
7045
|
async function copyModuleFiles(moduleName, _projectRoot) {
|
|
6992
|
-
const cliRoot =
|
|
6993
|
-
const sourceModulePath =
|
|
7046
|
+
const cliRoot = import_path10.default.resolve(__dirname, "../../../");
|
|
7047
|
+
const sourceModulePath = import_path10.default.join(cliRoot, "src", "modules", moduleName);
|
|
6994
7048
|
const targetModulesDir = getModulesDir();
|
|
6995
|
-
const targetModulePath =
|
|
7049
|
+
const targetModulePath = import_path10.default.join(targetModulesDir, moduleName);
|
|
6996
7050
|
try {
|
|
6997
|
-
await
|
|
7051
|
+
await import_promises9.default.access(sourceModulePath);
|
|
6998
7052
|
} catch {
|
|
6999
7053
|
throw new Error(`Module source not found: ${moduleName}`);
|
|
7000
7054
|
}
|
|
7001
|
-
await
|
|
7055
|
+
await import_promises9.default.cp(sourceModulePath, targetModulePath, { recursive: true });
|
|
7002
7056
|
}
|
|
7003
7057
|
async function updateModule(moduleName, options) {
|
|
7004
7058
|
const projectError = validateProject();
|
|
@@ -7274,8 +7328,275 @@ var completionCommand = new import_commander10.Command("completion").description
|
|
|
7274
7328
|
}
|
|
7275
7329
|
});
|
|
7276
7330
|
|
|
7331
|
+
// src/cli/commands/scaffold.ts
|
|
7332
|
+
var import_commander11 = require("commander");
|
|
7333
|
+
var import_path11 = __toESM(require("path"), 1);
|
|
7334
|
+
var import_ora8 = __toESM(require("ora"), 1);
|
|
7335
|
+
var import_chalk14 = __toESM(require("chalk"), 1);
|
|
7336
|
+
var scaffoldCommand = new import_commander11.Command("scaffold").description("Generate complete CRUD with Prisma model").argument("<name>", "Resource name (e.g., product, user)").option(
|
|
7337
|
+
"--fields <fields>",
|
|
7338
|
+
'Field definitions: "name:string email:string? age:number category:relation"'
|
|
7339
|
+
).option("--validator <type>", "Validator type: zod, joi, yup", "zod").option("--dry-run", "Preview changes without writing files").action(
|
|
7340
|
+
async (name, options) => {
|
|
7341
|
+
const dryRun = DryRunManager.getInstance();
|
|
7342
|
+
if (options.dryRun) {
|
|
7343
|
+
dryRun.enable();
|
|
7344
|
+
console.log(import_chalk14.default.yellow("\n\u26A0 DRY RUN MODE - No files will be written\n"));
|
|
7345
|
+
}
|
|
7346
|
+
if (!options.fields) {
|
|
7347
|
+
console.log(import_chalk14.default.red("\n\u2717 Error: --fields option is required\n"));
|
|
7348
|
+
console.log(import_chalk14.default.gray("Example:"));
|
|
7349
|
+
console.log(
|
|
7350
|
+
import_chalk14.default.cyan(
|
|
7351
|
+
' servcraft scaffold product --fields "name:string price:number category:relation"'
|
|
7352
|
+
)
|
|
7353
|
+
);
|
|
7354
|
+
process.exit(1);
|
|
7355
|
+
}
|
|
7356
|
+
const spinner = (0, import_ora8.default)("Scaffolding resource...").start();
|
|
7357
|
+
try {
|
|
7358
|
+
const fields = parseFields(options.fields || "");
|
|
7359
|
+
if (!fields || fields.length === 0) {
|
|
7360
|
+
spinner.fail("No valid fields provided");
|
|
7361
|
+
console.log(import_chalk14.default.gray(`
|
|
7362
|
+
Received: ${options.fields}`));
|
|
7363
|
+
console.log(import_chalk14.default.gray(`Parsed: ${JSON.stringify(fields)}`));
|
|
7364
|
+
process.exit(1);
|
|
7365
|
+
}
|
|
7366
|
+
const kebabName = toKebabCase(name);
|
|
7367
|
+
const pascalName = toPascalCase(name);
|
|
7368
|
+
const camelName = toCamelCase(name);
|
|
7369
|
+
const pluralName = pluralize(camelName);
|
|
7370
|
+
const tableName = pluralize(kebabName);
|
|
7371
|
+
const modulesDir = getModulesDir();
|
|
7372
|
+
const moduleDir = import_path11.default.join(modulesDir, kebabName);
|
|
7373
|
+
const controllerTpl = await getTemplate("controller", controllerTemplate);
|
|
7374
|
+
const serviceTpl = await getTemplate("service", serviceTemplate);
|
|
7375
|
+
const repositoryTpl = await getTemplate("repository", repositoryTemplate);
|
|
7376
|
+
const routesTpl = await getTemplate("routes", routesTemplate);
|
|
7377
|
+
const moduleIndexTpl = await getTemplate("module-index", moduleIndexTemplate);
|
|
7378
|
+
const files = [
|
|
7379
|
+
{
|
|
7380
|
+
name: `${kebabName}.types.ts`,
|
|
7381
|
+
content: dynamicTypesTemplate(kebabName, pascalName, fields)
|
|
7382
|
+
},
|
|
7383
|
+
{
|
|
7384
|
+
name: `${kebabName}.schemas.ts`,
|
|
7385
|
+
content: dynamicSchemasTemplate(
|
|
7386
|
+
kebabName,
|
|
7387
|
+
pascalName,
|
|
7388
|
+
camelName,
|
|
7389
|
+
fields,
|
|
7390
|
+
options.validator || "zod"
|
|
7391
|
+
)
|
|
7392
|
+
},
|
|
7393
|
+
{
|
|
7394
|
+
name: `${kebabName}.service.ts`,
|
|
7395
|
+
content: serviceTpl(kebabName, pascalName, camelName)
|
|
7396
|
+
},
|
|
7397
|
+
{
|
|
7398
|
+
name: `${kebabName}.controller.ts`,
|
|
7399
|
+
content: controllerTpl(kebabName, pascalName, camelName)
|
|
7400
|
+
},
|
|
7401
|
+
{
|
|
7402
|
+
name: "index.ts",
|
|
7403
|
+
content: moduleIndexTpl(kebabName, pascalName, camelName)
|
|
7404
|
+
},
|
|
7405
|
+
{
|
|
7406
|
+
name: `${kebabName}.repository.ts`,
|
|
7407
|
+
content: repositoryTpl(kebabName, pascalName, camelName, pluralName)
|
|
7408
|
+
},
|
|
7409
|
+
{
|
|
7410
|
+
name: `${kebabName}.routes.ts`,
|
|
7411
|
+
content: routesTpl(kebabName, pascalName, camelName, pluralName)
|
|
7412
|
+
}
|
|
7413
|
+
];
|
|
7414
|
+
for (const file of files) {
|
|
7415
|
+
await writeFile(import_path11.default.join(moduleDir, file.name), file.content);
|
|
7416
|
+
}
|
|
7417
|
+
const testDir = import_path11.default.join(moduleDir, "__tests__");
|
|
7418
|
+
const controllerTestTpl = await getTemplate("controller-test", controllerTestTemplate);
|
|
7419
|
+
const serviceTestTpl = await getTemplate("service-test", serviceTestTemplate);
|
|
7420
|
+
const integrationTestTpl = await getTemplate("integration-test", integrationTestTemplate);
|
|
7421
|
+
await writeFile(
|
|
7422
|
+
import_path11.default.join(testDir, `${kebabName}.controller.test.ts`),
|
|
7423
|
+
controllerTestTpl(kebabName, pascalName, camelName)
|
|
7424
|
+
);
|
|
7425
|
+
await writeFile(
|
|
7426
|
+
import_path11.default.join(testDir, `${kebabName}.service.test.ts`),
|
|
7427
|
+
serviceTestTpl(kebabName, pascalName, camelName)
|
|
7428
|
+
);
|
|
7429
|
+
await writeFile(
|
|
7430
|
+
import_path11.default.join(testDir, `${kebabName}.integration.test.ts`),
|
|
7431
|
+
integrationTestTpl(kebabName, pascalName, camelName)
|
|
7432
|
+
);
|
|
7433
|
+
spinner.succeed(`Resource "${pascalName}" scaffolded successfully!`);
|
|
7434
|
+
console.log("\n" + "\u2500".repeat(70));
|
|
7435
|
+
info("\u{1F4CB} Prisma model to add to schema.prisma:");
|
|
7436
|
+
console.log(import_chalk14.default.gray("\n// Copy this to your schema.prisma file:\n"));
|
|
7437
|
+
console.log(dynamicPrismaTemplate(pascalName, tableName, fields));
|
|
7438
|
+
console.log("\u2500".repeat(70));
|
|
7439
|
+
console.log("\n\u{1F4CB} Fields scaffolded:");
|
|
7440
|
+
fields.forEach((f) => {
|
|
7441
|
+
const opts = [];
|
|
7442
|
+
if (f.isOptional) opts.push("optional");
|
|
7443
|
+
if (f.isArray) opts.push("array");
|
|
7444
|
+
if (f.isUnique) opts.push("unique");
|
|
7445
|
+
if (f.relation) opts.push(`relation: ${f.relation.model}`);
|
|
7446
|
+
const optsStr = opts.length > 0 ? ` (${opts.join(", ")})` : "";
|
|
7447
|
+
success(` ${f.name}: ${f.type}${optsStr}`);
|
|
7448
|
+
});
|
|
7449
|
+
console.log("\n\u{1F4C1} Files created:");
|
|
7450
|
+
files.forEach((f) => success(` src/modules/${kebabName}/${f.name}`));
|
|
7451
|
+
success(` src/modules/${kebabName}/__tests__/${kebabName}.controller.test.ts`);
|
|
7452
|
+
success(` src/modules/${kebabName}/__tests__/${kebabName}.service.test.ts`);
|
|
7453
|
+
success(` src/modules/${kebabName}/__tests__/${kebabName}.integration.test.ts`);
|
|
7454
|
+
console.log("\n\u{1F4CC} Next steps:");
|
|
7455
|
+
info(" 1. Add the Prisma model to your schema.prisma file");
|
|
7456
|
+
info(" 2. Run: npx prisma db push (or prisma migrate dev)");
|
|
7457
|
+
info(" 3. Run: npx prisma generate");
|
|
7458
|
+
info(" 4. Register the module routes in your app");
|
|
7459
|
+
info(" 5. Update the test files with actual test data");
|
|
7460
|
+
console.log(
|
|
7461
|
+
import_chalk14.default.gray("\n\u{1F4A1} Tip: Use --dry-run to preview changes before applying them\n")
|
|
7462
|
+
);
|
|
7463
|
+
if (options.dryRun) {
|
|
7464
|
+
dryRun.printSummary();
|
|
7465
|
+
}
|
|
7466
|
+
} catch (error2) {
|
|
7467
|
+
spinner.fail("Failed to scaffold resource");
|
|
7468
|
+
if (error2 instanceof Error) {
|
|
7469
|
+
console.error(import_chalk14.default.red(`
|
|
7470
|
+
\u2717 ${error2.message}
|
|
7471
|
+
`));
|
|
7472
|
+
console.error(import_chalk14.default.gray(error2.stack));
|
|
7473
|
+
}
|
|
7474
|
+
process.exit(1);
|
|
7475
|
+
}
|
|
7476
|
+
}
|
|
7477
|
+
);
|
|
7478
|
+
|
|
7479
|
+
// src/cli/commands/templates.ts
|
|
7480
|
+
var import_commander12 = require("commander");
|
|
7481
|
+
var import_chalk15 = __toESM(require("chalk"), 1);
|
|
7482
|
+
var import_promises10 = __toESM(require("fs/promises"), 1);
|
|
7483
|
+
var import_path12 = __toESM(require("path"), 1);
|
|
7484
|
+
var TEMPLATE_TYPES = [
|
|
7485
|
+
"controller",
|
|
7486
|
+
"service",
|
|
7487
|
+
"repository",
|
|
7488
|
+
"types",
|
|
7489
|
+
"schemas",
|
|
7490
|
+
"routes",
|
|
7491
|
+
"module-index",
|
|
7492
|
+
"controller-test",
|
|
7493
|
+
"service-test",
|
|
7494
|
+
"integration-test"
|
|
7495
|
+
];
|
|
7496
|
+
async function initTemplates() {
|
|
7497
|
+
const projectError = validateProject();
|
|
7498
|
+
if (projectError) {
|
|
7499
|
+
displayError(projectError);
|
|
7500
|
+
return;
|
|
7501
|
+
}
|
|
7502
|
+
const projectRoot = getProjectRoot();
|
|
7503
|
+
const templatesDir = import_path12.default.join(projectRoot, ".servcraft", "templates");
|
|
7504
|
+
try {
|
|
7505
|
+
await import_promises10.default.mkdir(templatesDir, { recursive: true });
|
|
7506
|
+
console.log(import_chalk15.default.cyan("\n\u{1F4C1} Creating custom template directory...\n"));
|
|
7507
|
+
const exampleController = `// Custom controller template
|
|
7508
|
+
// Available variables: name, pascalName, camelName, pluralName
|
|
7509
|
+
export function controllerTemplate(name: string, pascalName: string, camelName: string): string {
|
|
7510
|
+
return \`import type { FastifyRequest, FastifyReply } from 'fastify';
|
|
7511
|
+
import type { \${pascalName}Service } from './\${name}.service.js';
|
|
7512
|
+
|
|
7513
|
+
export class \${pascalName}Controller {
|
|
7514
|
+
constructor(private \${camelName}Service: \${pascalName}Service) {}
|
|
7515
|
+
|
|
7516
|
+
// Add your custom controller methods here
|
|
7517
|
+
async getAll(request: FastifyRequest, reply: FastifyReply) {
|
|
7518
|
+
const data = await this.\${camelName}Service.getAll();
|
|
7519
|
+
return reply.send({ data });
|
|
7520
|
+
}
|
|
7521
|
+
}
|
|
7522
|
+
\`;
|
|
7523
|
+
}
|
|
7524
|
+
`;
|
|
7525
|
+
await import_promises10.default.writeFile(
|
|
7526
|
+
import_path12.default.join(templatesDir, "controller.example.ts"),
|
|
7527
|
+
exampleController,
|
|
7528
|
+
"utf-8"
|
|
7529
|
+
);
|
|
7530
|
+
console.log(import_chalk15.default.green("\u2714 Created template directory: .servcraft/templates/"));
|
|
7531
|
+
console.log(import_chalk15.default.green("\u2714 Created example template: controller.example.ts\n"));
|
|
7532
|
+
console.log(import_chalk15.default.bold("\u{1F4CB} Available template types:\n"));
|
|
7533
|
+
TEMPLATE_TYPES.forEach((type) => {
|
|
7534
|
+
console.log(import_chalk15.default.gray(` \u2022 ${type}.ts`));
|
|
7535
|
+
});
|
|
7536
|
+
console.log(import_chalk15.default.yellow("\n\u{1F4A1} To customize a template:"));
|
|
7537
|
+
console.log(import_chalk15.default.gray(" 1. Copy the example template"));
|
|
7538
|
+
console.log(import_chalk15.default.gray(" 2. Rename it (remove .example)"));
|
|
7539
|
+
console.log(import_chalk15.default.gray(" 3. Modify the template code"));
|
|
7540
|
+
console.log(import_chalk15.default.gray(" 4. Use --template flag when generating\n"));
|
|
7541
|
+
} catch (error2) {
|
|
7542
|
+
if (error2 instanceof Error) {
|
|
7543
|
+
console.error(import_chalk15.default.red(`
|
|
7544
|
+
\u2717 Failed to initialize templates: ${error2.message}
|
|
7545
|
+
`));
|
|
7546
|
+
}
|
|
7547
|
+
}
|
|
7548
|
+
}
|
|
7549
|
+
async function listTemplates() {
|
|
7550
|
+
const projectError = validateProject();
|
|
7551
|
+
if (projectError) {
|
|
7552
|
+
displayError(projectError);
|
|
7553
|
+
return;
|
|
7554
|
+
}
|
|
7555
|
+
const projectRoot = getProjectRoot();
|
|
7556
|
+
const projectTemplatesDir = import_path12.default.join(projectRoot, ".servcraft", "templates");
|
|
7557
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
7558
|
+
const userTemplatesDir = import_path12.default.join(homeDir, ".servcraft", "templates");
|
|
7559
|
+
console.log(import_chalk15.default.bold.cyan("\n\u{1F4CB} Available Templates\n"));
|
|
7560
|
+
console.log(import_chalk15.default.bold("Project templates (.servcraft/templates/):"));
|
|
7561
|
+
try {
|
|
7562
|
+
const files = await import_promises10.default.readdir(projectTemplatesDir);
|
|
7563
|
+
const templates = files.filter((f) => f.endsWith(".ts") && !f.endsWith(".example.ts"));
|
|
7564
|
+
if (templates.length > 0) {
|
|
7565
|
+
templates.forEach((t) => {
|
|
7566
|
+
console.log(import_chalk15.default.green(` \u2713 ${t}`));
|
|
7567
|
+
});
|
|
7568
|
+
} else {
|
|
7569
|
+
console.log(import_chalk15.default.gray(" (none)"));
|
|
7570
|
+
}
|
|
7571
|
+
} catch {
|
|
7572
|
+
console.log(import_chalk15.default.gray(" (directory not found)"));
|
|
7573
|
+
}
|
|
7574
|
+
console.log(import_chalk15.default.bold("\nUser templates (~/.servcraft/templates/):"));
|
|
7575
|
+
try {
|
|
7576
|
+
const files = await import_promises10.default.readdir(userTemplatesDir);
|
|
7577
|
+
const templates = files.filter((f) => f.endsWith(".ts") && !f.endsWith(".example.ts"));
|
|
7578
|
+
if (templates.length > 0) {
|
|
7579
|
+
templates.forEach((t) => {
|
|
7580
|
+
console.log(import_chalk15.default.green(` \u2713 ${t}`));
|
|
7581
|
+
});
|
|
7582
|
+
} else {
|
|
7583
|
+
console.log(import_chalk15.default.gray(" (none)"));
|
|
7584
|
+
}
|
|
7585
|
+
} catch {
|
|
7586
|
+
console.log(import_chalk15.default.gray(" (directory not found)"));
|
|
7587
|
+
}
|
|
7588
|
+
console.log(import_chalk15.default.bold("\nBuilt-in templates:"));
|
|
7589
|
+
TEMPLATE_TYPES.forEach((t) => {
|
|
7590
|
+
console.log(import_chalk15.default.cyan(` \u2022 ${t}.ts`));
|
|
7591
|
+
});
|
|
7592
|
+
console.log(import_chalk15.default.gray('\n\u{1F4A1} Run "servcraft templates init" to create custom templates\n'));
|
|
7593
|
+
}
|
|
7594
|
+
var templatesCommand = new import_commander12.Command("templates").description("Manage code generation templates").addCommand(
|
|
7595
|
+
new import_commander12.Command("init").description("Initialize custom templates directory").action(initTemplates)
|
|
7596
|
+
).addCommand(new import_commander12.Command("list").description("List available templates").action(listTemplates));
|
|
7597
|
+
|
|
7277
7598
|
// src/cli/index.ts
|
|
7278
|
-
var program = new
|
|
7599
|
+
var program = new import_commander13.Command();
|
|
7279
7600
|
program.name("servcraft").description("Servcraft - A modular Node.js backend framework CLI").version("0.1.0");
|
|
7280
7601
|
program.addCommand(initCommand);
|
|
7281
7602
|
program.addCommand(generateCommand);
|
|
@@ -7287,5 +7608,7 @@ program.addCommand(removeCommand);
|
|
|
7287
7608
|
program.addCommand(doctorCommand);
|
|
7288
7609
|
program.addCommand(updateCommand);
|
|
7289
7610
|
program.addCommand(completionCommand);
|
|
7611
|
+
program.addCommand(scaffoldCommand);
|
|
7612
|
+
program.addCommand(templatesCommand);
|
|
7290
7613
|
program.parse();
|
|
7291
7614
|
//# sourceMappingURL=index.cjs.map
|