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.js
CHANGED
|
@@ -7,7 +7,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
7
7
|
});
|
|
8
8
|
|
|
9
9
|
// src/cli/index.ts
|
|
10
|
-
import { Command as
|
|
10
|
+
import { Command as Command13 } from "commander";
|
|
11
11
|
|
|
12
12
|
// src/cli/commands/init.ts
|
|
13
13
|
import { Command } from "commander";
|
|
@@ -1205,7 +1205,7 @@ declare module 'fastify' {
|
|
|
1205
1205
|
|
|
1206
1206
|
// src/cli/commands/generate.ts
|
|
1207
1207
|
import { Command as Command2 } from "commander";
|
|
1208
|
-
import
|
|
1208
|
+
import path5 from "path";
|
|
1209
1209
|
import ora2 from "ora";
|
|
1210
1210
|
import inquirer2 from "inquirer";
|
|
1211
1211
|
import chalk5 from "chalk";
|
|
@@ -1434,11 +1434,11 @@ function displayError(error2) {
|
|
|
1434
1434
|
}
|
|
1435
1435
|
function validateProject() {
|
|
1436
1436
|
try {
|
|
1437
|
-
const
|
|
1438
|
-
if (!
|
|
1437
|
+
const fs14 = __require("fs");
|
|
1438
|
+
if (!fs14.existsSync("package.json")) {
|
|
1439
1439
|
return ErrorTypes.NOT_IN_PROJECT();
|
|
1440
1440
|
}
|
|
1441
|
-
const packageJson = JSON.parse(
|
|
1441
|
+
const packageJson = JSON.parse(fs14.readFileSync("package.json", "utf-8"));
|
|
1442
1442
|
if (!packageJson.dependencies?.fastify) {
|
|
1443
1443
|
return new ServCraftError("This does not appear to be a ServCraft project", [
|
|
1444
1444
|
`ServCraft projects require Fastify`,
|
|
@@ -2489,6 +2489,39 @@ describe('${pascalName} Integration Tests', () => {
|
|
|
2489
2489
|
`;
|
|
2490
2490
|
}
|
|
2491
2491
|
|
|
2492
|
+
// src/cli/utils/template-loader.ts
|
|
2493
|
+
import fs3 from "fs/promises";
|
|
2494
|
+
import path4 from "path";
|
|
2495
|
+
async function loadCustomTemplate(templateType) {
|
|
2496
|
+
const projectRoot = getProjectRoot();
|
|
2497
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
2498
|
+
const locations = [
|
|
2499
|
+
path4.join(projectRoot, ".servcraft", "templates", `${templateType}.ts`),
|
|
2500
|
+
path4.join(homeDir, ".servcraft", "templates", `${templateType}.ts`)
|
|
2501
|
+
];
|
|
2502
|
+
for (const location of locations) {
|
|
2503
|
+
try {
|
|
2504
|
+
await fs3.access(location);
|
|
2505
|
+
const templateModule = await import(`file://${location}`);
|
|
2506
|
+
const functionName = `${templateType.replace(/-/g, "")}Template`;
|
|
2507
|
+
if (templateModule[functionName]) {
|
|
2508
|
+
return templateModule;
|
|
2509
|
+
}
|
|
2510
|
+
} catch {
|
|
2511
|
+
continue;
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
return null;
|
|
2515
|
+
}
|
|
2516
|
+
async function getTemplate(templateType, builtInTemplate) {
|
|
2517
|
+
const customTemplate = await loadCustomTemplate(templateType);
|
|
2518
|
+
if (customTemplate) {
|
|
2519
|
+
const functionName = `${templateType.replace(/-/g, "")}Template`;
|
|
2520
|
+
return customTemplate[functionName];
|
|
2521
|
+
}
|
|
2522
|
+
return builtInTemplate;
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2492
2525
|
// src/cli/commands/generate.ts
|
|
2493
2526
|
function enableDryRunIfNeeded(options) {
|
|
2494
2527
|
const dryRun = DryRunManager.getInstance();
|
|
@@ -2521,60 +2554,70 @@ generateCommand.command("module <name> [fields...]").alias("m").description(
|
|
|
2521
2554
|
const pluralName = pluralize(kebabName);
|
|
2522
2555
|
const tableName = pluralize(kebabName.replace(/-/g, "_"));
|
|
2523
2556
|
const validatorType = options.validator || "zod";
|
|
2524
|
-
const moduleDir =
|
|
2557
|
+
const moduleDir = path5.join(getModulesDir(), kebabName);
|
|
2525
2558
|
if (await fileExists(moduleDir)) {
|
|
2526
2559
|
spinner.stop();
|
|
2527
2560
|
error(`Module "${kebabName}" already exists`);
|
|
2528
2561
|
return;
|
|
2529
2562
|
}
|
|
2530
2563
|
const hasFields = fields.length > 0;
|
|
2564
|
+
const controllerTpl = await getTemplate("controller", controllerTemplate);
|
|
2565
|
+
const serviceTpl = await getTemplate("service", serviceTemplate);
|
|
2566
|
+
const repositoryTpl = await getTemplate("repository", repositoryTemplate);
|
|
2567
|
+
const typesTpl = await getTemplate("types", typesTemplate);
|
|
2568
|
+
const schemasTpl = await getTemplate("schemas", schemasTemplate);
|
|
2569
|
+
const routesTpl = await getTemplate("routes", routesTemplate);
|
|
2570
|
+
const moduleIndexTpl = await getTemplate("module-index", moduleIndexTemplate);
|
|
2531
2571
|
const files = [
|
|
2532
2572
|
{
|
|
2533
2573
|
name: `${kebabName}.types.ts`,
|
|
2534
|
-
content: hasFields ? dynamicTypesTemplate(kebabName, pascalName, fields) :
|
|
2574
|
+
content: hasFields ? dynamicTypesTemplate(kebabName, pascalName, fields) : typesTpl(kebabName, pascalName)
|
|
2535
2575
|
},
|
|
2536
2576
|
{
|
|
2537
2577
|
name: `${kebabName}.schemas.ts`,
|
|
2538
|
-
content: hasFields ? dynamicSchemasTemplate(kebabName, pascalName, camelName, fields, validatorType) :
|
|
2578
|
+
content: hasFields ? dynamicSchemasTemplate(kebabName, pascalName, camelName, fields, validatorType) : schemasTpl(kebabName, pascalName, camelName)
|
|
2539
2579
|
},
|
|
2540
2580
|
{
|
|
2541
2581
|
name: `${kebabName}.service.ts`,
|
|
2542
|
-
content:
|
|
2582
|
+
content: serviceTpl(kebabName, pascalName, camelName)
|
|
2543
2583
|
},
|
|
2544
2584
|
{
|
|
2545
2585
|
name: `${kebabName}.controller.ts`,
|
|
2546
|
-
content:
|
|
2586
|
+
content: controllerTpl(kebabName, pascalName, camelName)
|
|
2547
2587
|
},
|
|
2548
|
-
{ name: "index.ts", content:
|
|
2588
|
+
{ name: "index.ts", content: moduleIndexTpl(kebabName, pascalName, camelName) }
|
|
2549
2589
|
];
|
|
2550
2590
|
if (options.repository !== false) {
|
|
2551
2591
|
files.push({
|
|
2552
2592
|
name: `${kebabName}.repository.ts`,
|
|
2553
|
-
content:
|
|
2593
|
+
content: repositoryTpl(kebabName, pascalName, camelName, pluralName)
|
|
2554
2594
|
});
|
|
2555
2595
|
}
|
|
2556
2596
|
if (options.routes !== false) {
|
|
2557
2597
|
files.push({
|
|
2558
2598
|
name: `${kebabName}.routes.ts`,
|
|
2559
|
-
content:
|
|
2599
|
+
content: routesTpl(kebabName, pascalName, camelName, pluralName)
|
|
2560
2600
|
});
|
|
2561
2601
|
}
|
|
2562
2602
|
for (const file of files) {
|
|
2563
|
-
await writeFile(
|
|
2603
|
+
await writeFile(path5.join(moduleDir, file.name), file.content);
|
|
2564
2604
|
}
|
|
2565
2605
|
if (options.withTests) {
|
|
2566
|
-
const testDir =
|
|
2606
|
+
const testDir = path5.join(moduleDir, "__tests__");
|
|
2607
|
+
const controllerTestTpl = await getTemplate("controller-test", controllerTestTemplate);
|
|
2608
|
+
const serviceTestTpl = await getTemplate("service-test", serviceTestTemplate);
|
|
2609
|
+
const integrationTestTpl = await getTemplate("integration-test", integrationTestTemplate);
|
|
2567
2610
|
await writeFile(
|
|
2568
|
-
|
|
2569
|
-
|
|
2611
|
+
path5.join(testDir, `${kebabName}.controller.test.ts`),
|
|
2612
|
+
controllerTestTpl(kebabName, pascalName, camelName)
|
|
2570
2613
|
);
|
|
2571
2614
|
await writeFile(
|
|
2572
|
-
|
|
2573
|
-
|
|
2615
|
+
path5.join(testDir, `${kebabName}.service.test.ts`),
|
|
2616
|
+
serviceTestTpl(kebabName, pascalName, camelName)
|
|
2574
2617
|
);
|
|
2575
2618
|
await writeFile(
|
|
2576
|
-
|
|
2577
|
-
|
|
2619
|
+
path5.join(testDir, `${kebabName}.integration.test.ts`),
|
|
2620
|
+
integrationTestTpl(kebabName, pascalName, camelName)
|
|
2578
2621
|
);
|
|
2579
2622
|
}
|
|
2580
2623
|
spinner.succeed(`Module "${pascalName}" generated successfully!`);
|
|
@@ -2632,8 +2675,8 @@ generateCommand.command("controller <name>").alias("c").description("Generate a
|
|
|
2632
2675
|
const pascalName = toPascalCase(name);
|
|
2633
2676
|
const camelName = toCamelCase(name);
|
|
2634
2677
|
const moduleName = options.module ? toKebabCase(options.module) : kebabName;
|
|
2635
|
-
const moduleDir =
|
|
2636
|
-
const filePath =
|
|
2678
|
+
const moduleDir = path5.join(getModulesDir(), moduleName);
|
|
2679
|
+
const filePath = path5.join(moduleDir, `${kebabName}.controller.ts`);
|
|
2637
2680
|
if (await fileExists(filePath)) {
|
|
2638
2681
|
spinner.stop();
|
|
2639
2682
|
displayError(ErrorTypes.FILE_ALREADY_EXISTS(`${kebabName}.controller.ts`));
|
|
@@ -2656,8 +2699,8 @@ generateCommand.command("service <name>").alias("s").description("Generate a ser
|
|
|
2656
2699
|
const pascalName = toPascalCase(name);
|
|
2657
2700
|
const camelName = toCamelCase(name);
|
|
2658
2701
|
const moduleName = options.module ? toKebabCase(options.module) : kebabName;
|
|
2659
|
-
const moduleDir =
|
|
2660
|
-
const filePath =
|
|
2702
|
+
const moduleDir = path5.join(getModulesDir(), moduleName);
|
|
2703
|
+
const filePath = path5.join(moduleDir, `${kebabName}.service.ts`);
|
|
2661
2704
|
if (await fileExists(filePath)) {
|
|
2662
2705
|
spinner.stop();
|
|
2663
2706
|
error(`Service "${kebabName}" already exists`);
|
|
@@ -2681,8 +2724,8 @@ generateCommand.command("repository <name>").alias("r").description("Generate a
|
|
|
2681
2724
|
const camelName = toCamelCase(name);
|
|
2682
2725
|
const pluralName = pluralize(kebabName);
|
|
2683
2726
|
const moduleName = options.module ? toKebabCase(options.module) : kebabName;
|
|
2684
|
-
const moduleDir =
|
|
2685
|
-
const filePath =
|
|
2727
|
+
const moduleDir = path5.join(getModulesDir(), moduleName);
|
|
2728
|
+
const filePath = path5.join(moduleDir, `${kebabName}.repository.ts`);
|
|
2686
2729
|
if (await fileExists(filePath)) {
|
|
2687
2730
|
spinner.stop();
|
|
2688
2731
|
error(`Repository "${kebabName}" already exists`);
|
|
@@ -2704,8 +2747,8 @@ generateCommand.command("types <name>").alias("t").description("Generate types/i
|
|
|
2704
2747
|
const kebabName = toKebabCase(name);
|
|
2705
2748
|
const pascalName = toPascalCase(name);
|
|
2706
2749
|
const moduleName = options.module ? toKebabCase(options.module) : kebabName;
|
|
2707
|
-
const moduleDir =
|
|
2708
|
-
const filePath =
|
|
2750
|
+
const moduleDir = path5.join(getModulesDir(), moduleName);
|
|
2751
|
+
const filePath = path5.join(moduleDir, `${kebabName}.types.ts`);
|
|
2709
2752
|
if (await fileExists(filePath)) {
|
|
2710
2753
|
spinner.stop();
|
|
2711
2754
|
error(`Types file "${kebabName}.types.ts" already exists`);
|
|
@@ -2728,8 +2771,8 @@ generateCommand.command("schema <name>").alias("v").description("Generate valida
|
|
|
2728
2771
|
const pascalName = toPascalCase(name);
|
|
2729
2772
|
const camelName = toCamelCase(name);
|
|
2730
2773
|
const moduleName = options.module ? toKebabCase(options.module) : kebabName;
|
|
2731
|
-
const moduleDir =
|
|
2732
|
-
const filePath =
|
|
2774
|
+
const moduleDir = path5.join(getModulesDir(), moduleName);
|
|
2775
|
+
const filePath = path5.join(moduleDir, `${kebabName}.schemas.ts`);
|
|
2733
2776
|
if (await fileExists(filePath)) {
|
|
2734
2777
|
spinner.stop();
|
|
2735
2778
|
error(`Schemas file "${kebabName}.schemas.ts" already exists`);
|
|
@@ -2753,8 +2796,8 @@ generateCommand.command("routes <name>").description("Generate routes").option("
|
|
|
2753
2796
|
const camelName = toCamelCase(name);
|
|
2754
2797
|
const pluralName = pluralize(kebabName);
|
|
2755
2798
|
const moduleName = options.module ? toKebabCase(options.module) : kebabName;
|
|
2756
|
-
const moduleDir =
|
|
2757
|
-
const filePath =
|
|
2799
|
+
const moduleDir = path5.join(getModulesDir(), moduleName);
|
|
2800
|
+
const filePath = path5.join(moduleDir, `${kebabName}.routes.ts`);
|
|
2758
2801
|
if (await fileExists(filePath)) {
|
|
2759
2802
|
spinner.stop();
|
|
2760
2803
|
error(`Routes file "${kebabName}.routes.ts" already exists`);
|
|
@@ -2842,21 +2885,21 @@ async function promptForFields() {
|
|
|
2842
2885
|
|
|
2843
2886
|
// src/cli/commands/add-module.ts
|
|
2844
2887
|
import { Command as Command3 } from "commander";
|
|
2845
|
-
import
|
|
2888
|
+
import path8 from "path";
|
|
2846
2889
|
import ora3 from "ora";
|
|
2847
2890
|
import chalk7 from "chalk";
|
|
2848
|
-
import * as
|
|
2891
|
+
import * as fs6 from "fs/promises";
|
|
2849
2892
|
|
|
2850
2893
|
// src/cli/utils/env-manager.ts
|
|
2851
|
-
import * as
|
|
2852
|
-
import * as
|
|
2894
|
+
import * as fs4 from "fs/promises";
|
|
2895
|
+
import * as path6 from "path";
|
|
2853
2896
|
import { existsSync } from "fs";
|
|
2854
2897
|
var EnvManager = class {
|
|
2855
2898
|
envPath;
|
|
2856
2899
|
envExamplePath;
|
|
2857
2900
|
constructor(projectRoot) {
|
|
2858
|
-
this.envPath =
|
|
2859
|
-
this.envExamplePath =
|
|
2901
|
+
this.envPath = path6.join(projectRoot, ".env");
|
|
2902
|
+
this.envExamplePath = path6.join(projectRoot, ".env.example");
|
|
2860
2903
|
}
|
|
2861
2904
|
/**
|
|
2862
2905
|
* Add environment variables to .env file
|
|
@@ -2867,7 +2910,7 @@ var EnvManager = class {
|
|
|
2867
2910
|
let created2 = false;
|
|
2868
2911
|
let envContent = "";
|
|
2869
2912
|
if (existsSync(this.envPath)) {
|
|
2870
|
-
envContent = await
|
|
2913
|
+
envContent = await fs4.readFile(this.envPath, "utf-8");
|
|
2871
2914
|
} else {
|
|
2872
2915
|
created2 = true;
|
|
2873
2916
|
}
|
|
@@ -2896,7 +2939,7 @@ var EnvManager = class {
|
|
|
2896
2939
|
}
|
|
2897
2940
|
newContent += "\n";
|
|
2898
2941
|
}
|
|
2899
|
-
await
|
|
2942
|
+
await fs4.writeFile(this.envPath, newContent, "utf-8");
|
|
2900
2943
|
if (existsSync(this.envExamplePath)) {
|
|
2901
2944
|
await this.updateEnvExample(sections);
|
|
2902
2945
|
}
|
|
@@ -2908,7 +2951,7 @@ var EnvManager = class {
|
|
|
2908
2951
|
async updateEnvExample(sections) {
|
|
2909
2952
|
let exampleContent = "";
|
|
2910
2953
|
if (existsSync(this.envExamplePath)) {
|
|
2911
|
-
exampleContent = await
|
|
2954
|
+
exampleContent = await fs4.readFile(this.envExamplePath, "utf-8");
|
|
2912
2955
|
}
|
|
2913
2956
|
const existingKeys = this.parseExistingKeys(exampleContent);
|
|
2914
2957
|
let newContent = exampleContent;
|
|
@@ -2932,7 +2975,7 @@ var EnvManager = class {
|
|
|
2932
2975
|
}
|
|
2933
2976
|
newContent += "\n";
|
|
2934
2977
|
}
|
|
2935
|
-
await
|
|
2978
|
+
await fs4.writeFile(this.envExamplePath, newContent, "utf-8");
|
|
2936
2979
|
}
|
|
2937
2980
|
/**
|
|
2938
2981
|
* Parse existing environment variable keys
|
|
@@ -3506,34 +3549,34 @@ var EnvManager = class {
|
|
|
3506
3549
|
};
|
|
3507
3550
|
|
|
3508
3551
|
// src/cli/utils/template-manager.ts
|
|
3509
|
-
import * as
|
|
3510
|
-
import * as
|
|
3552
|
+
import * as fs5 from "fs/promises";
|
|
3553
|
+
import * as path7 from "path";
|
|
3511
3554
|
import { createHash } from "crypto";
|
|
3512
3555
|
import { existsSync as existsSync2 } from "fs";
|
|
3513
3556
|
var TemplateManager = class {
|
|
3514
3557
|
templatesDir;
|
|
3515
3558
|
manifestsDir;
|
|
3516
3559
|
constructor(projectRoot) {
|
|
3517
|
-
this.templatesDir =
|
|
3518
|
-
this.manifestsDir =
|
|
3560
|
+
this.templatesDir = path7.join(projectRoot, ".servcraft", "templates");
|
|
3561
|
+
this.manifestsDir = path7.join(projectRoot, ".servcraft", "manifests");
|
|
3519
3562
|
}
|
|
3520
3563
|
/**
|
|
3521
3564
|
* Initialize template system
|
|
3522
3565
|
*/
|
|
3523
3566
|
async initialize() {
|
|
3524
|
-
await
|
|
3525
|
-
await
|
|
3567
|
+
await fs5.mkdir(this.templatesDir, { recursive: true });
|
|
3568
|
+
await fs5.mkdir(this.manifestsDir, { recursive: true });
|
|
3526
3569
|
}
|
|
3527
3570
|
/**
|
|
3528
3571
|
* Save module template
|
|
3529
3572
|
*/
|
|
3530
3573
|
async saveTemplate(moduleName, files) {
|
|
3531
3574
|
await this.initialize();
|
|
3532
|
-
const moduleTemplateDir =
|
|
3533
|
-
await
|
|
3575
|
+
const moduleTemplateDir = path7.join(this.templatesDir, moduleName);
|
|
3576
|
+
await fs5.mkdir(moduleTemplateDir, { recursive: true });
|
|
3534
3577
|
for (const [fileName, content] of Object.entries(files)) {
|
|
3535
|
-
const filePath =
|
|
3536
|
-
await
|
|
3578
|
+
const filePath = path7.join(moduleTemplateDir, fileName);
|
|
3579
|
+
await fs5.writeFile(filePath, content, "utf-8");
|
|
3537
3580
|
}
|
|
3538
3581
|
}
|
|
3539
3582
|
/**
|
|
@@ -3541,8 +3584,8 @@ var TemplateManager = class {
|
|
|
3541
3584
|
*/
|
|
3542
3585
|
async getTemplate(moduleName, fileName) {
|
|
3543
3586
|
try {
|
|
3544
|
-
const filePath =
|
|
3545
|
-
return await
|
|
3587
|
+
const filePath = path7.join(this.templatesDir, moduleName, fileName);
|
|
3588
|
+
return await fs5.readFile(filePath, "utf-8");
|
|
3546
3589
|
} catch {
|
|
3547
3590
|
return null;
|
|
3548
3591
|
}
|
|
@@ -3566,16 +3609,16 @@ var TemplateManager = class {
|
|
|
3566
3609
|
installedAt: /* @__PURE__ */ new Date(),
|
|
3567
3610
|
updatedAt: /* @__PURE__ */ new Date()
|
|
3568
3611
|
};
|
|
3569
|
-
const manifestPath =
|
|
3570
|
-
await
|
|
3612
|
+
const manifestPath = path7.join(this.manifestsDir, `${moduleName}.json`);
|
|
3613
|
+
await fs5.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
3571
3614
|
}
|
|
3572
3615
|
/**
|
|
3573
3616
|
* Get module manifest
|
|
3574
3617
|
*/
|
|
3575
3618
|
async getManifest(moduleName) {
|
|
3576
3619
|
try {
|
|
3577
|
-
const manifestPath =
|
|
3578
|
-
const content = await
|
|
3620
|
+
const manifestPath = path7.join(this.manifestsDir, `${moduleName}.json`);
|
|
3621
|
+
const content = await fs5.readFile(manifestPath, "utf-8");
|
|
3579
3622
|
return JSON.parse(content);
|
|
3580
3623
|
} catch {
|
|
3581
3624
|
return null;
|
|
@@ -3603,7 +3646,7 @@ var TemplateManager = class {
|
|
|
3603
3646
|
}
|
|
3604
3647
|
const results = [];
|
|
3605
3648
|
for (const [fileName, fileInfo] of Object.entries(manifest.files)) {
|
|
3606
|
-
const filePath =
|
|
3649
|
+
const filePath = path7.join(moduleDir, fileName);
|
|
3607
3650
|
if (!existsSync2(filePath)) {
|
|
3608
3651
|
results.push({
|
|
3609
3652
|
fileName,
|
|
@@ -3613,7 +3656,7 @@ var TemplateManager = class {
|
|
|
3613
3656
|
});
|
|
3614
3657
|
continue;
|
|
3615
3658
|
}
|
|
3616
|
-
const currentContent = await
|
|
3659
|
+
const currentContent = await fs5.readFile(filePath, "utf-8");
|
|
3617
3660
|
const currentHash = this.hashContent(currentContent);
|
|
3618
3661
|
results.push({
|
|
3619
3662
|
fileName,
|
|
@@ -3629,7 +3672,7 @@ var TemplateManager = class {
|
|
|
3629
3672
|
*/
|
|
3630
3673
|
async createBackup(moduleName, moduleDir) {
|
|
3631
3674
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").substring(0, 19);
|
|
3632
|
-
const backupDir =
|
|
3675
|
+
const backupDir = path7.join(path7.dirname(moduleDir), `${moduleName}.backup-${timestamp}`);
|
|
3633
3676
|
await this.copyDirectory(moduleDir, backupDir);
|
|
3634
3677
|
return backupDir;
|
|
3635
3678
|
}
|
|
@@ -3637,15 +3680,15 @@ var TemplateManager = class {
|
|
|
3637
3680
|
* Copy directory recursively
|
|
3638
3681
|
*/
|
|
3639
3682
|
async copyDirectory(src, dest) {
|
|
3640
|
-
await
|
|
3641
|
-
const entries = await
|
|
3683
|
+
await fs5.mkdir(dest, { recursive: true });
|
|
3684
|
+
const entries = await fs5.readdir(src, { withFileTypes: true });
|
|
3642
3685
|
for (const entry of entries) {
|
|
3643
|
-
const srcPath =
|
|
3644
|
-
const destPath =
|
|
3686
|
+
const srcPath = path7.join(src, entry.name);
|
|
3687
|
+
const destPath = path7.join(dest, entry.name);
|
|
3645
3688
|
if (entry.isDirectory()) {
|
|
3646
3689
|
await this.copyDirectory(srcPath, destPath);
|
|
3647
3690
|
} else {
|
|
3648
|
-
await
|
|
3691
|
+
await fs5.copyFile(srcPath, destPath);
|
|
3649
3692
|
}
|
|
3650
3693
|
}
|
|
3651
3694
|
}
|
|
@@ -3742,8 +3785,8 @@ var TemplateManager = class {
|
|
|
3742
3785
|
}
|
|
3743
3786
|
manifest.files = fileHashes;
|
|
3744
3787
|
manifest.updatedAt = /* @__PURE__ */ new Date();
|
|
3745
|
-
const manifestPath =
|
|
3746
|
-
await
|
|
3788
|
+
const manifestPath = path7.join(this.manifestsDir, `${moduleName}.json`);
|
|
3789
|
+
await fs5.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
3747
3790
|
}
|
|
3748
3791
|
};
|
|
3749
3792
|
|
|
@@ -4100,7 +4143,7 @@ var addModuleCommand = new Command3("add").description("Add a pre-built module t
|
|
|
4100
4143
|
}
|
|
4101
4144
|
const spinner = ora3(`Adding ${module.name} module...`).start();
|
|
4102
4145
|
try {
|
|
4103
|
-
const moduleDir =
|
|
4146
|
+
const moduleDir = path8.join(getModulesDir(), moduleName);
|
|
4104
4147
|
const templateManager = new TemplateManager(process.cwd());
|
|
4105
4148
|
const moduleExists = await fileExists(moduleDir);
|
|
4106
4149
|
if (moduleExists) {
|
|
@@ -4133,7 +4176,7 @@ var addModuleCommand = new Command3("add").description("Add a pre-built module t
|
|
|
4133
4176
|
const backupPath = await templateManager.createBackup(moduleName, moduleDir);
|
|
4134
4177
|
InteractivePrompt.showBackupCreated(backupPath);
|
|
4135
4178
|
}
|
|
4136
|
-
await
|
|
4179
|
+
await fs6.rm(moduleDir, { recursive: true, force: true });
|
|
4137
4180
|
await ensureDir(moduleDir);
|
|
4138
4181
|
await generateModuleFiles(moduleName, moduleDir);
|
|
4139
4182
|
const files = await getModuleFiles(moduleName, moduleDir);
|
|
@@ -4244,7 +4287,7 @@ export * from './auth.schemas.js';
|
|
|
4244
4287
|
`
|
|
4245
4288
|
};
|
|
4246
4289
|
for (const [name, content] of Object.entries(files)) {
|
|
4247
|
-
await writeFile(
|
|
4290
|
+
await writeFile(path8.join(dir, name), content);
|
|
4248
4291
|
}
|
|
4249
4292
|
}
|
|
4250
4293
|
async function generateUsersModule(dir) {
|
|
@@ -4284,7 +4327,7 @@ export * from './user.schemas.js';
|
|
|
4284
4327
|
`
|
|
4285
4328
|
};
|
|
4286
4329
|
for (const [name, content] of Object.entries(files)) {
|
|
4287
|
-
await writeFile(
|
|
4330
|
+
await writeFile(path8.join(dir, name), content);
|
|
4288
4331
|
}
|
|
4289
4332
|
}
|
|
4290
4333
|
async function generateEmailModule(dir) {
|
|
@@ -4341,7 +4384,7 @@ export { EmailService, emailService } from './email.service.js';
|
|
|
4341
4384
|
`
|
|
4342
4385
|
};
|
|
4343
4386
|
for (const [name, content] of Object.entries(files)) {
|
|
4344
|
-
await writeFile(
|
|
4387
|
+
await writeFile(path8.join(dir, name), content);
|
|
4345
4388
|
}
|
|
4346
4389
|
}
|
|
4347
4390
|
async function generateAuditModule(dir) {
|
|
@@ -4385,7 +4428,7 @@ export { AuditService, auditService } from './audit.service.js';
|
|
|
4385
4428
|
`
|
|
4386
4429
|
};
|
|
4387
4430
|
for (const [name, content] of Object.entries(files)) {
|
|
4388
|
-
await writeFile(
|
|
4431
|
+
await writeFile(path8.join(dir, name), content);
|
|
4389
4432
|
}
|
|
4390
4433
|
}
|
|
4391
4434
|
async function generateUploadModule(dir) {
|
|
@@ -4411,7 +4454,7 @@ export interface UploadOptions {
|
|
|
4411
4454
|
`
|
|
4412
4455
|
};
|
|
4413
4456
|
for (const [name, content] of Object.entries(files)) {
|
|
4414
|
-
await writeFile(
|
|
4457
|
+
await writeFile(path8.join(dir, name), content);
|
|
4415
4458
|
}
|
|
4416
4459
|
}
|
|
4417
4460
|
async function generateCacheModule(dir) {
|
|
@@ -4457,7 +4500,7 @@ export { CacheService, cacheService } from './cache.service.js';
|
|
|
4457
4500
|
`
|
|
4458
4501
|
};
|
|
4459
4502
|
for (const [name, content] of Object.entries(files)) {
|
|
4460
|
-
await writeFile(
|
|
4503
|
+
await writeFile(path8.join(dir, name), content);
|
|
4461
4504
|
}
|
|
4462
4505
|
}
|
|
4463
4506
|
async function generateGenericModule(dir, name) {
|
|
@@ -4471,22 +4514,22 @@ export interface ${name.charAt(0).toUpperCase() + name.slice(1)}Data {
|
|
|
4471
4514
|
`
|
|
4472
4515
|
};
|
|
4473
4516
|
for (const [fileName, content] of Object.entries(files)) {
|
|
4474
|
-
await writeFile(
|
|
4517
|
+
await writeFile(path8.join(dir, fileName), content);
|
|
4475
4518
|
}
|
|
4476
4519
|
}
|
|
4477
4520
|
async function findServercraftModules() {
|
|
4478
|
-
const scriptDir =
|
|
4521
|
+
const scriptDir = path8.dirname(new URL(import.meta.url).pathname);
|
|
4479
4522
|
const possiblePaths = [
|
|
4480
4523
|
// Local node_modules (when servcraft is a dependency)
|
|
4481
|
-
|
|
4524
|
+
path8.join(process.cwd(), "node_modules", "servcraft", "src", "modules"),
|
|
4482
4525
|
// From dist/cli/index.js -> src/modules (npx or global install)
|
|
4483
|
-
|
|
4526
|
+
path8.resolve(scriptDir, "..", "..", "src", "modules"),
|
|
4484
4527
|
// From src/cli/commands/add-module.ts -> src/modules (development)
|
|
4485
|
-
|
|
4528
|
+
path8.resolve(scriptDir, "..", "..", "modules")
|
|
4486
4529
|
];
|
|
4487
4530
|
for (const p of possiblePaths) {
|
|
4488
4531
|
try {
|
|
4489
|
-
const stats = await
|
|
4532
|
+
const stats = await fs6.stat(p);
|
|
4490
4533
|
if (stats.isDirectory()) {
|
|
4491
4534
|
return p;
|
|
4492
4535
|
}
|
|
@@ -4506,7 +4549,7 @@ async function generateModuleFiles(moduleName, moduleDir) {
|
|
|
4506
4549
|
const sourceDirName = moduleNameMap[moduleName] || moduleName;
|
|
4507
4550
|
const servercraftModulesDir = await findServercraftModules();
|
|
4508
4551
|
if (servercraftModulesDir) {
|
|
4509
|
-
const sourceModuleDir =
|
|
4552
|
+
const sourceModuleDir = path8.join(servercraftModulesDir, sourceDirName);
|
|
4510
4553
|
if (await fileExists(sourceModuleDir)) {
|
|
4511
4554
|
await copyModuleFromSource(sourceModuleDir, moduleDir);
|
|
4512
4555
|
return;
|
|
@@ -4536,26 +4579,26 @@ async function generateModuleFiles(moduleName, moduleDir) {
|
|
|
4536
4579
|
}
|
|
4537
4580
|
}
|
|
4538
4581
|
async function copyModuleFromSource(sourceDir, targetDir) {
|
|
4539
|
-
const entries = await
|
|
4582
|
+
const entries = await fs6.readdir(sourceDir, { withFileTypes: true });
|
|
4540
4583
|
for (const entry of entries) {
|
|
4541
|
-
const sourcePath =
|
|
4542
|
-
const targetPath =
|
|
4584
|
+
const sourcePath = path8.join(sourceDir, entry.name);
|
|
4585
|
+
const targetPath = path8.join(targetDir, entry.name);
|
|
4543
4586
|
if (entry.isDirectory()) {
|
|
4544
|
-
await
|
|
4587
|
+
await fs6.mkdir(targetPath, { recursive: true });
|
|
4545
4588
|
await copyModuleFromSource(sourcePath, targetPath);
|
|
4546
4589
|
} else {
|
|
4547
|
-
await
|
|
4590
|
+
await fs6.copyFile(sourcePath, targetPath);
|
|
4548
4591
|
}
|
|
4549
4592
|
}
|
|
4550
4593
|
}
|
|
4551
4594
|
async function getModuleFiles(moduleName, moduleDir) {
|
|
4552
4595
|
const files = {};
|
|
4553
|
-
const entries = await
|
|
4596
|
+
const entries = await fs6.readdir(moduleDir);
|
|
4554
4597
|
for (const entry of entries) {
|
|
4555
|
-
const filePath =
|
|
4556
|
-
const stat2 = await
|
|
4598
|
+
const filePath = path8.join(moduleDir, entry);
|
|
4599
|
+
const stat2 = await fs6.stat(filePath);
|
|
4557
4600
|
if (stat2.isFile() && entry.endsWith(".ts")) {
|
|
4558
|
-
const content = await
|
|
4601
|
+
const content = await fs6.readFile(filePath, "utf-8");
|
|
4559
4602
|
files[entry] = content;
|
|
4560
4603
|
}
|
|
4561
4604
|
}
|
|
@@ -4570,8 +4613,8 @@ async function showDiffForModule(templateManager, moduleName, moduleDir) {
|
|
|
4570
4613
|
if (file.isModified) {
|
|
4571
4614
|
console.log(chalk7.yellow(`
|
|
4572
4615
|
\u{1F4C4} ${file.fileName}:`));
|
|
4573
|
-
const currentPath =
|
|
4574
|
-
const currentContent = await
|
|
4616
|
+
const currentPath = path8.join(moduleDir, file.fileName);
|
|
4617
|
+
const currentContent = await fs6.readFile(currentPath, "utf-8");
|
|
4575
4618
|
const originalContent = await templateManager.getTemplate(moduleName, file.fileName);
|
|
4576
4619
|
if (originalContent) {
|
|
4577
4620
|
const diff = templateManager.generateDiff(originalContent, currentContent);
|
|
@@ -4583,11 +4626,11 @@ async function showDiffForModule(templateManager, moduleName, moduleDir) {
|
|
|
4583
4626
|
async function performSmartMerge(templateManager, moduleName, moduleDir, _displayName) {
|
|
4584
4627
|
const spinner = ora3("Analyzing files for merge...").start();
|
|
4585
4628
|
const newFiles = {};
|
|
4586
|
-
const templateDir =
|
|
4629
|
+
const templateDir = path8.join(templateManager["templatesDir"], moduleName);
|
|
4587
4630
|
try {
|
|
4588
|
-
const entries = await
|
|
4631
|
+
const entries = await fs6.readdir(templateDir);
|
|
4589
4632
|
for (const entry of entries) {
|
|
4590
|
-
const content = await
|
|
4633
|
+
const content = await fs6.readFile(path8.join(templateDir, entry), "utf-8");
|
|
4591
4634
|
newFiles[entry] = content;
|
|
4592
4635
|
}
|
|
4593
4636
|
} catch {
|
|
@@ -4605,7 +4648,7 @@ async function performSmartMerge(templateManager, moduleName, moduleDir, _displa
|
|
|
4605
4648
|
};
|
|
4606
4649
|
for (const fileInfo of modifiedFiles) {
|
|
4607
4650
|
const fileName = fileInfo.fileName;
|
|
4608
|
-
const filePath =
|
|
4651
|
+
const filePath = path8.join(moduleDir, fileName);
|
|
4609
4652
|
const newContent = newFiles[fileName];
|
|
4610
4653
|
if (!newContent) {
|
|
4611
4654
|
continue;
|
|
@@ -4618,7 +4661,7 @@ async function performSmartMerge(templateManager, moduleName, moduleDir, _displa
|
|
|
4618
4661
|
} else if (batchAction === "overwrite-all") {
|
|
4619
4662
|
fileAction = "overwrite";
|
|
4620
4663
|
} else {
|
|
4621
|
-
const currentContent = await
|
|
4664
|
+
const currentContent = await fs6.readFile(filePath, "utf-8");
|
|
4622
4665
|
const yourLines = currentContent.split("\n").length;
|
|
4623
4666
|
const newLines = newContent.split("\n").length;
|
|
4624
4667
|
const choice = await InteractivePrompt.askFileAction(
|
|
@@ -4642,20 +4685,20 @@ async function performSmartMerge(templateManager, moduleName, moduleDir, _displa
|
|
|
4642
4685
|
continue;
|
|
4643
4686
|
}
|
|
4644
4687
|
if (fileAction === "overwrite") {
|
|
4645
|
-
await
|
|
4688
|
+
await fs6.writeFile(filePath, newContent, "utf-8");
|
|
4646
4689
|
stats.overwritten++;
|
|
4647
4690
|
continue;
|
|
4648
4691
|
}
|
|
4649
4692
|
if (fileAction === "merge") {
|
|
4650
4693
|
const originalContent = await templateManager.getTemplate(moduleName, fileName);
|
|
4651
|
-
const currentContent = await
|
|
4694
|
+
const currentContent = await fs6.readFile(filePath, "utf-8");
|
|
4652
4695
|
if (originalContent) {
|
|
4653
4696
|
const mergeResult = await templateManager.mergeFiles(
|
|
4654
4697
|
originalContent,
|
|
4655
4698
|
currentContent,
|
|
4656
4699
|
newContent
|
|
4657
4700
|
);
|
|
4658
|
-
await
|
|
4701
|
+
await fs6.writeFile(filePath, mergeResult.merged, "utf-8");
|
|
4659
4702
|
if (mergeResult.hasConflicts) {
|
|
4660
4703
|
stats.conflicts++;
|
|
4661
4704
|
InteractivePrompt.displayConflicts(mergeResult.conflicts);
|
|
@@ -4663,7 +4706,7 @@ async function performSmartMerge(templateManager, moduleName, moduleDir, _displa
|
|
|
4663
4706
|
stats.merged++;
|
|
4664
4707
|
}
|
|
4665
4708
|
} else {
|
|
4666
|
-
await
|
|
4709
|
+
await fs6.writeFile(filePath, newContent, "utf-8");
|
|
4667
4710
|
stats.overwritten++;
|
|
4668
4711
|
}
|
|
4669
4712
|
}
|
|
@@ -4768,14 +4811,14 @@ dbCommand.command("status").description("Show migration status").action(async ()
|
|
|
4768
4811
|
|
|
4769
4812
|
// src/cli/commands/docs.ts
|
|
4770
4813
|
import { Command as Command5 } from "commander";
|
|
4771
|
-
import
|
|
4772
|
-
import
|
|
4814
|
+
import path10 from "path";
|
|
4815
|
+
import fs8 from "fs/promises";
|
|
4773
4816
|
import ora6 from "ora";
|
|
4774
4817
|
import chalk9 from "chalk";
|
|
4775
4818
|
|
|
4776
4819
|
// src/cli/utils/docs-generator.ts
|
|
4777
|
-
import
|
|
4778
|
-
import
|
|
4820
|
+
import fs7 from "fs/promises";
|
|
4821
|
+
import path9 from "path";
|
|
4779
4822
|
import ora5 from "ora";
|
|
4780
4823
|
|
|
4781
4824
|
// src/core/server.ts
|
|
@@ -5496,11 +5539,11 @@ function validateQuery(schema, data) {
|
|
|
5496
5539
|
function formatZodErrors(error2) {
|
|
5497
5540
|
const errors = {};
|
|
5498
5541
|
for (const issue of error2.issues) {
|
|
5499
|
-
const
|
|
5500
|
-
if (!errors[
|
|
5501
|
-
errors[
|
|
5542
|
+
const path15 = issue.path.join(".") || "root";
|
|
5543
|
+
if (!errors[path15]) {
|
|
5544
|
+
errors[path15] = [];
|
|
5502
5545
|
}
|
|
5503
|
-
errors[
|
|
5546
|
+
errors[path15].push(issue.message);
|
|
5504
5547
|
}
|
|
5505
5548
|
return errors;
|
|
5506
5549
|
}
|
|
@@ -5737,7 +5780,18 @@ function getSkip(params) {
|
|
|
5737
5780
|
}
|
|
5738
5781
|
|
|
5739
5782
|
// src/modules/user/user.repository.ts
|
|
5740
|
-
|
|
5783
|
+
var UserRole = {
|
|
5784
|
+
USER: "USER",
|
|
5785
|
+
MODERATOR: "MODERATOR",
|
|
5786
|
+
ADMIN: "ADMIN",
|
|
5787
|
+
SUPER_ADMIN: "SUPER_ADMIN"
|
|
5788
|
+
};
|
|
5789
|
+
var UserStatus = {
|
|
5790
|
+
ACTIVE: "ACTIVE",
|
|
5791
|
+
INACTIVE: "INACTIVE",
|
|
5792
|
+
SUSPENDED: "SUSPENDED",
|
|
5793
|
+
BANNED: "BANNED"
|
|
5794
|
+
};
|
|
5741
5795
|
var UserRepository = class {
|
|
5742
5796
|
/**
|
|
5743
5797
|
* Find user by ID
|
|
@@ -6333,9 +6387,9 @@ async function generateDocs(outputPath = "openapi.json", silent = false) {
|
|
|
6333
6387
|
await registerUserModule(app, authService);
|
|
6334
6388
|
await app.ready();
|
|
6335
6389
|
const spec = app.swagger();
|
|
6336
|
-
const absoluteOutput =
|
|
6337
|
-
await
|
|
6338
|
-
await
|
|
6390
|
+
const absoluteOutput = path9.resolve(outputPath);
|
|
6391
|
+
await fs7.mkdir(path9.dirname(absoluteOutput), { recursive: true });
|
|
6392
|
+
await fs7.writeFile(absoluteOutput, JSON.stringify(spec, null, 2), "utf8");
|
|
6339
6393
|
spinner?.succeed(`OpenAPI spec generated at ${absoluteOutput}`);
|
|
6340
6394
|
await app.close();
|
|
6341
6395
|
return absoluteOutput;
|
|
@@ -6351,10 +6405,10 @@ docsCommand.command("generate").alias("gen").description("Generate OpenAPI/Swagg
|
|
|
6351
6405
|
try {
|
|
6352
6406
|
const outputPath = await generateDocs(options.output, false);
|
|
6353
6407
|
if (options.format === "yaml") {
|
|
6354
|
-
const jsonContent = await
|
|
6408
|
+
const jsonContent = await fs8.readFile(outputPath, "utf-8");
|
|
6355
6409
|
const spec = JSON.parse(jsonContent);
|
|
6356
6410
|
const yamlPath = outputPath.replace(".json", ".yaml");
|
|
6357
|
-
await
|
|
6411
|
+
await fs8.writeFile(yamlPath, jsonToYaml(spec));
|
|
6358
6412
|
success(`YAML documentation generated: ${yamlPath}`);
|
|
6359
6413
|
}
|
|
6360
6414
|
console.log("\n\u{1F4DA} Documentation URLs:");
|
|
@@ -6379,14 +6433,14 @@ docsCommand.command("export").description("Export documentation to Postman, Inso
|
|
|
6379
6433
|
const spinner = ora6("Exporting documentation...").start();
|
|
6380
6434
|
try {
|
|
6381
6435
|
const projectRoot = getProjectRoot();
|
|
6382
|
-
const specPath =
|
|
6436
|
+
const specPath = path10.join(projectRoot, "openapi.json");
|
|
6383
6437
|
try {
|
|
6384
|
-
await
|
|
6438
|
+
await fs8.access(specPath);
|
|
6385
6439
|
} catch {
|
|
6386
6440
|
spinner.text = "Generating OpenAPI spec first...";
|
|
6387
6441
|
await generateDocs("openapi.json", true);
|
|
6388
6442
|
}
|
|
6389
|
-
const specContent = await
|
|
6443
|
+
const specContent = await fs8.readFile(specPath, "utf-8");
|
|
6390
6444
|
const spec = JSON.parse(specContent);
|
|
6391
6445
|
let output;
|
|
6392
6446
|
let defaultName;
|
|
@@ -6406,8 +6460,8 @@ docsCommand.command("export").description("Export documentation to Postman, Inso
|
|
|
6406
6460
|
default:
|
|
6407
6461
|
throw new Error(`Unknown format: ${options.format}`);
|
|
6408
6462
|
}
|
|
6409
|
-
const outPath =
|
|
6410
|
-
await
|
|
6463
|
+
const outPath = path10.join(projectRoot, options.output || defaultName);
|
|
6464
|
+
await fs8.writeFile(outPath, output);
|
|
6411
6465
|
spinner.succeed(`Exported to: ${options.output || defaultName}`);
|
|
6412
6466
|
if (options.format === "postman") {
|
|
6413
6467
|
info("\n Import in Postman: File > Import > Select file");
|
|
@@ -6420,13 +6474,13 @@ docsCommand.command("export").description("Export documentation to Postman, Inso
|
|
|
6420
6474
|
docsCommand.command("status").description("Show documentation status").action(async () => {
|
|
6421
6475
|
const projectRoot = getProjectRoot();
|
|
6422
6476
|
console.log(chalk9.bold("\n\u{1F4CA} Documentation Status\n"));
|
|
6423
|
-
const specPath =
|
|
6477
|
+
const specPath = path10.join(projectRoot, "openapi.json");
|
|
6424
6478
|
try {
|
|
6425
|
-
const stat2 = await
|
|
6479
|
+
const stat2 = await fs8.stat(specPath);
|
|
6426
6480
|
success(
|
|
6427
6481
|
`openapi.json exists (${formatBytes(stat2.size)}, modified ${formatDate(stat2.mtime)})`
|
|
6428
6482
|
);
|
|
6429
|
-
const content = await
|
|
6483
|
+
const content = await fs8.readFile(specPath, "utf-8");
|
|
6430
6484
|
const spec = JSON.parse(content);
|
|
6431
6485
|
const pathCount = Object.keys(spec.paths || {}).length;
|
|
6432
6486
|
info(` ${pathCount} endpoints documented`);
|
|
@@ -6538,7 +6592,7 @@ function formatDate(date) {
|
|
|
6538
6592
|
// src/cli/commands/list.ts
|
|
6539
6593
|
import { Command as Command6 } from "commander";
|
|
6540
6594
|
import chalk10 from "chalk";
|
|
6541
|
-
import
|
|
6595
|
+
import fs9 from "fs/promises";
|
|
6542
6596
|
var AVAILABLE_MODULES2 = {
|
|
6543
6597
|
// Core
|
|
6544
6598
|
auth: {
|
|
@@ -6653,7 +6707,7 @@ var AVAILABLE_MODULES2 = {
|
|
|
6653
6707
|
async function getInstalledModules() {
|
|
6654
6708
|
try {
|
|
6655
6709
|
const modulesDir = getModulesDir();
|
|
6656
|
-
const entries = await
|
|
6710
|
+
const entries = await fs9.readdir(modulesDir, { withFileTypes: true });
|
|
6657
6711
|
return entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
6658
6712
|
} catch {
|
|
6659
6713
|
return [];
|
|
@@ -6762,10 +6816,10 @@ var listCommand = new Command6("list").alias("ls").description("List available a
|
|
|
6762
6816
|
|
|
6763
6817
|
// src/cli/commands/remove.ts
|
|
6764
6818
|
import { Command as Command7 } from "commander";
|
|
6765
|
-
import
|
|
6819
|
+
import path11 from "path";
|
|
6766
6820
|
import ora7 from "ora";
|
|
6767
6821
|
import chalk11 from "chalk";
|
|
6768
|
-
import
|
|
6822
|
+
import fs10 from "fs/promises";
|
|
6769
6823
|
import inquirer4 from "inquirer";
|
|
6770
6824
|
var removeCommand = new Command7("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) => {
|
|
6771
6825
|
const projectError = validateProject();
|
|
@@ -6774,9 +6828,9 @@ var removeCommand = new Command7("remove").alias("rm").description("Remove an in
|
|
|
6774
6828
|
return;
|
|
6775
6829
|
}
|
|
6776
6830
|
console.log(chalk11.bold.cyan("\n\u{1F5D1}\uFE0F ServCraft Module Removal\n"));
|
|
6777
|
-
const moduleDir =
|
|
6831
|
+
const moduleDir = path11.join(getModulesDir(), moduleName);
|
|
6778
6832
|
try {
|
|
6779
|
-
const exists = await
|
|
6833
|
+
const exists = await fs10.access(moduleDir).then(() => true).catch(() => false);
|
|
6780
6834
|
if (!exists) {
|
|
6781
6835
|
displayError(
|
|
6782
6836
|
new ServCraftError(`Module "${moduleName}" is not installed`, [
|
|
@@ -6786,7 +6840,7 @@ var removeCommand = new Command7("remove").alias("rm").description("Remove an in
|
|
|
6786
6840
|
);
|
|
6787
6841
|
return;
|
|
6788
6842
|
}
|
|
6789
|
-
const files = await
|
|
6843
|
+
const files = await fs10.readdir(moduleDir);
|
|
6790
6844
|
const fileCount = files.length;
|
|
6791
6845
|
if (!options?.yes) {
|
|
6792
6846
|
console.log(chalk11.yellow(`\u26A0 This will remove the "${moduleName}" module:`));
|
|
@@ -6807,7 +6861,7 @@ var removeCommand = new Command7("remove").alias("rm").description("Remove an in
|
|
|
6807
6861
|
}
|
|
6808
6862
|
}
|
|
6809
6863
|
const spinner = ora7("Removing module...").start();
|
|
6810
|
-
await
|
|
6864
|
+
await fs10.rm(moduleDir, { recursive: true, force: true });
|
|
6811
6865
|
spinner.succeed(`Module "${moduleName}" removed successfully!`);
|
|
6812
6866
|
console.log("\n" + chalk11.bold("\u2713 Removed:"));
|
|
6813
6867
|
success(` src/modules/${moduleName}/ (${fileCount} files)`);
|
|
@@ -6833,7 +6887,7 @@ var removeCommand = new Command7("remove").alias("rm").description("Remove an in
|
|
|
6833
6887
|
// src/cli/commands/doctor.ts
|
|
6834
6888
|
import { Command as Command8 } from "commander";
|
|
6835
6889
|
import chalk12 from "chalk";
|
|
6836
|
-
import
|
|
6890
|
+
import fs11 from "fs/promises";
|
|
6837
6891
|
async function checkNodeVersion() {
|
|
6838
6892
|
const version = process.version;
|
|
6839
6893
|
const major = parseInt(version.slice(1).split(".")[0] || "0", 10);
|
|
@@ -6850,7 +6904,7 @@ async function checkNodeVersion() {
|
|
|
6850
6904
|
async function checkPackageJson() {
|
|
6851
6905
|
const checks = [];
|
|
6852
6906
|
try {
|
|
6853
|
-
const content = await
|
|
6907
|
+
const content = await fs11.readFile("package.json", "utf-8");
|
|
6854
6908
|
const pkg = JSON.parse(content);
|
|
6855
6909
|
checks.push({ name: "package.json", status: "pass", message: "Found" });
|
|
6856
6910
|
if (pkg.dependencies?.fastify) {
|
|
@@ -6878,7 +6932,7 @@ async function checkDirectories() {
|
|
|
6878
6932
|
const dirs = ["src", "node_modules", ".git", ".env"];
|
|
6879
6933
|
for (const dir of dirs) {
|
|
6880
6934
|
try {
|
|
6881
|
-
await
|
|
6935
|
+
await fs11.access(dir);
|
|
6882
6936
|
checks.push({ name: dir, status: "pass", message: "Exists" });
|
|
6883
6937
|
} catch {
|
|
6884
6938
|
const isCritical = dir === "src" || dir === "node_modules";
|
|
@@ -6927,12 +6981,12 @@ ${chalk12.green(pass + " passed")} | ${chalk12.yellow(warn2 + " warnings")} | ${
|
|
|
6927
6981
|
// src/cli/commands/update.ts
|
|
6928
6982
|
import { Command as Command9 } from "commander";
|
|
6929
6983
|
import chalk13 from "chalk";
|
|
6930
|
-
import
|
|
6931
|
-
import
|
|
6984
|
+
import fs12 from "fs/promises";
|
|
6985
|
+
import path12 from "path";
|
|
6932
6986
|
import { fileURLToPath } from "url";
|
|
6933
6987
|
import inquirer5 from "inquirer";
|
|
6934
6988
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
6935
|
-
var __dirname2 =
|
|
6989
|
+
var __dirname2 = path12.dirname(__filename2);
|
|
6936
6990
|
var AVAILABLE_MODULES3 = [
|
|
6937
6991
|
"auth",
|
|
6938
6992
|
"users",
|
|
@@ -6960,7 +7014,7 @@ var AVAILABLE_MODULES3 = [
|
|
|
6960
7014
|
async function getInstalledModules2() {
|
|
6961
7015
|
try {
|
|
6962
7016
|
const modulesDir = getModulesDir();
|
|
6963
|
-
const entries = await
|
|
7017
|
+
const entries = await fs12.readdir(modulesDir, { withFileTypes: true });
|
|
6964
7018
|
const installedModules = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => AVAILABLE_MODULES3.includes(name));
|
|
6965
7019
|
return installedModules;
|
|
6966
7020
|
} catch {
|
|
@@ -6968,16 +7022,16 @@ async function getInstalledModules2() {
|
|
|
6968
7022
|
}
|
|
6969
7023
|
}
|
|
6970
7024
|
async function copyModuleFiles(moduleName, _projectRoot) {
|
|
6971
|
-
const cliRoot =
|
|
6972
|
-
const sourceModulePath =
|
|
7025
|
+
const cliRoot = path12.resolve(__dirname2, "../../../");
|
|
7026
|
+
const sourceModulePath = path12.join(cliRoot, "src", "modules", moduleName);
|
|
6973
7027
|
const targetModulesDir = getModulesDir();
|
|
6974
|
-
const targetModulePath =
|
|
7028
|
+
const targetModulePath = path12.join(targetModulesDir, moduleName);
|
|
6975
7029
|
try {
|
|
6976
|
-
await
|
|
7030
|
+
await fs12.access(sourceModulePath);
|
|
6977
7031
|
} catch {
|
|
6978
7032
|
throw new Error(`Module source not found: ${moduleName}`);
|
|
6979
7033
|
}
|
|
6980
|
-
await
|
|
7034
|
+
await fs12.cp(sourceModulePath, targetModulePath, { recursive: true });
|
|
6981
7035
|
}
|
|
6982
7036
|
async function updateModule(moduleName, options) {
|
|
6983
7037
|
const projectError = validateProject();
|
|
@@ -7253,8 +7307,275 @@ var completionCommand = new Command10("completion").description("Generate shell
|
|
|
7253
7307
|
}
|
|
7254
7308
|
});
|
|
7255
7309
|
|
|
7310
|
+
// src/cli/commands/scaffold.ts
|
|
7311
|
+
import { Command as Command11 } from "commander";
|
|
7312
|
+
import path13 from "path";
|
|
7313
|
+
import ora8 from "ora";
|
|
7314
|
+
import chalk14 from "chalk";
|
|
7315
|
+
var scaffoldCommand = new Command11("scaffold").description("Generate complete CRUD with Prisma model").argument("<name>", "Resource name (e.g., product, user)").option(
|
|
7316
|
+
"--fields <fields>",
|
|
7317
|
+
'Field definitions: "name:string email:string? age:number category:relation"'
|
|
7318
|
+
).option("--validator <type>", "Validator type: zod, joi, yup", "zod").option("--dry-run", "Preview changes without writing files").action(
|
|
7319
|
+
async (name, options) => {
|
|
7320
|
+
const dryRun = DryRunManager.getInstance();
|
|
7321
|
+
if (options.dryRun) {
|
|
7322
|
+
dryRun.enable();
|
|
7323
|
+
console.log(chalk14.yellow("\n\u26A0 DRY RUN MODE - No files will be written\n"));
|
|
7324
|
+
}
|
|
7325
|
+
if (!options.fields) {
|
|
7326
|
+
console.log(chalk14.red("\n\u2717 Error: --fields option is required\n"));
|
|
7327
|
+
console.log(chalk14.gray("Example:"));
|
|
7328
|
+
console.log(
|
|
7329
|
+
chalk14.cyan(
|
|
7330
|
+
' servcraft scaffold product --fields "name:string price:number category:relation"'
|
|
7331
|
+
)
|
|
7332
|
+
);
|
|
7333
|
+
process.exit(1);
|
|
7334
|
+
}
|
|
7335
|
+
const spinner = ora8("Scaffolding resource...").start();
|
|
7336
|
+
try {
|
|
7337
|
+
const fields = parseFields(options.fields || "");
|
|
7338
|
+
if (!fields || fields.length === 0) {
|
|
7339
|
+
spinner.fail("No valid fields provided");
|
|
7340
|
+
console.log(chalk14.gray(`
|
|
7341
|
+
Received: ${options.fields}`));
|
|
7342
|
+
console.log(chalk14.gray(`Parsed: ${JSON.stringify(fields)}`));
|
|
7343
|
+
process.exit(1);
|
|
7344
|
+
}
|
|
7345
|
+
const kebabName = toKebabCase(name);
|
|
7346
|
+
const pascalName = toPascalCase(name);
|
|
7347
|
+
const camelName = toCamelCase(name);
|
|
7348
|
+
const pluralName = pluralize(camelName);
|
|
7349
|
+
const tableName = pluralize(kebabName);
|
|
7350
|
+
const modulesDir = getModulesDir();
|
|
7351
|
+
const moduleDir = path13.join(modulesDir, kebabName);
|
|
7352
|
+
const controllerTpl = await getTemplate("controller", controllerTemplate);
|
|
7353
|
+
const serviceTpl = await getTemplate("service", serviceTemplate);
|
|
7354
|
+
const repositoryTpl = await getTemplate("repository", repositoryTemplate);
|
|
7355
|
+
const routesTpl = await getTemplate("routes", routesTemplate);
|
|
7356
|
+
const moduleIndexTpl = await getTemplate("module-index", moduleIndexTemplate);
|
|
7357
|
+
const files = [
|
|
7358
|
+
{
|
|
7359
|
+
name: `${kebabName}.types.ts`,
|
|
7360
|
+
content: dynamicTypesTemplate(kebabName, pascalName, fields)
|
|
7361
|
+
},
|
|
7362
|
+
{
|
|
7363
|
+
name: `${kebabName}.schemas.ts`,
|
|
7364
|
+
content: dynamicSchemasTemplate(
|
|
7365
|
+
kebabName,
|
|
7366
|
+
pascalName,
|
|
7367
|
+
camelName,
|
|
7368
|
+
fields,
|
|
7369
|
+
options.validator || "zod"
|
|
7370
|
+
)
|
|
7371
|
+
},
|
|
7372
|
+
{
|
|
7373
|
+
name: `${kebabName}.service.ts`,
|
|
7374
|
+
content: serviceTpl(kebabName, pascalName, camelName)
|
|
7375
|
+
},
|
|
7376
|
+
{
|
|
7377
|
+
name: `${kebabName}.controller.ts`,
|
|
7378
|
+
content: controllerTpl(kebabName, pascalName, camelName)
|
|
7379
|
+
},
|
|
7380
|
+
{
|
|
7381
|
+
name: "index.ts",
|
|
7382
|
+
content: moduleIndexTpl(kebabName, pascalName, camelName)
|
|
7383
|
+
},
|
|
7384
|
+
{
|
|
7385
|
+
name: `${kebabName}.repository.ts`,
|
|
7386
|
+
content: repositoryTpl(kebabName, pascalName, camelName, pluralName)
|
|
7387
|
+
},
|
|
7388
|
+
{
|
|
7389
|
+
name: `${kebabName}.routes.ts`,
|
|
7390
|
+
content: routesTpl(kebabName, pascalName, camelName, pluralName)
|
|
7391
|
+
}
|
|
7392
|
+
];
|
|
7393
|
+
for (const file of files) {
|
|
7394
|
+
await writeFile(path13.join(moduleDir, file.name), file.content);
|
|
7395
|
+
}
|
|
7396
|
+
const testDir = path13.join(moduleDir, "__tests__");
|
|
7397
|
+
const controllerTestTpl = await getTemplate("controller-test", controllerTestTemplate);
|
|
7398
|
+
const serviceTestTpl = await getTemplate("service-test", serviceTestTemplate);
|
|
7399
|
+
const integrationTestTpl = await getTemplate("integration-test", integrationTestTemplate);
|
|
7400
|
+
await writeFile(
|
|
7401
|
+
path13.join(testDir, `${kebabName}.controller.test.ts`),
|
|
7402
|
+
controllerTestTpl(kebabName, pascalName, camelName)
|
|
7403
|
+
);
|
|
7404
|
+
await writeFile(
|
|
7405
|
+
path13.join(testDir, `${kebabName}.service.test.ts`),
|
|
7406
|
+
serviceTestTpl(kebabName, pascalName, camelName)
|
|
7407
|
+
);
|
|
7408
|
+
await writeFile(
|
|
7409
|
+
path13.join(testDir, `${kebabName}.integration.test.ts`),
|
|
7410
|
+
integrationTestTpl(kebabName, pascalName, camelName)
|
|
7411
|
+
);
|
|
7412
|
+
spinner.succeed(`Resource "${pascalName}" scaffolded successfully!`);
|
|
7413
|
+
console.log("\n" + "\u2500".repeat(70));
|
|
7414
|
+
info("\u{1F4CB} Prisma model to add to schema.prisma:");
|
|
7415
|
+
console.log(chalk14.gray("\n// Copy this to your schema.prisma file:\n"));
|
|
7416
|
+
console.log(dynamicPrismaTemplate(pascalName, tableName, fields));
|
|
7417
|
+
console.log("\u2500".repeat(70));
|
|
7418
|
+
console.log("\n\u{1F4CB} Fields scaffolded:");
|
|
7419
|
+
fields.forEach((f) => {
|
|
7420
|
+
const opts = [];
|
|
7421
|
+
if (f.isOptional) opts.push("optional");
|
|
7422
|
+
if (f.isArray) opts.push("array");
|
|
7423
|
+
if (f.isUnique) opts.push("unique");
|
|
7424
|
+
if (f.relation) opts.push(`relation: ${f.relation.model}`);
|
|
7425
|
+
const optsStr = opts.length > 0 ? ` (${opts.join(", ")})` : "";
|
|
7426
|
+
success(` ${f.name}: ${f.type}${optsStr}`);
|
|
7427
|
+
});
|
|
7428
|
+
console.log("\n\u{1F4C1} Files created:");
|
|
7429
|
+
files.forEach((f) => success(` src/modules/${kebabName}/${f.name}`));
|
|
7430
|
+
success(` src/modules/${kebabName}/__tests__/${kebabName}.controller.test.ts`);
|
|
7431
|
+
success(` src/modules/${kebabName}/__tests__/${kebabName}.service.test.ts`);
|
|
7432
|
+
success(` src/modules/${kebabName}/__tests__/${kebabName}.integration.test.ts`);
|
|
7433
|
+
console.log("\n\u{1F4CC} Next steps:");
|
|
7434
|
+
info(" 1. Add the Prisma model to your schema.prisma file");
|
|
7435
|
+
info(" 2. Run: npx prisma db push (or prisma migrate dev)");
|
|
7436
|
+
info(" 3. Run: npx prisma generate");
|
|
7437
|
+
info(" 4. Register the module routes in your app");
|
|
7438
|
+
info(" 5. Update the test files with actual test data");
|
|
7439
|
+
console.log(
|
|
7440
|
+
chalk14.gray("\n\u{1F4A1} Tip: Use --dry-run to preview changes before applying them\n")
|
|
7441
|
+
);
|
|
7442
|
+
if (options.dryRun) {
|
|
7443
|
+
dryRun.printSummary();
|
|
7444
|
+
}
|
|
7445
|
+
} catch (error2) {
|
|
7446
|
+
spinner.fail("Failed to scaffold resource");
|
|
7447
|
+
if (error2 instanceof Error) {
|
|
7448
|
+
console.error(chalk14.red(`
|
|
7449
|
+
\u2717 ${error2.message}
|
|
7450
|
+
`));
|
|
7451
|
+
console.error(chalk14.gray(error2.stack));
|
|
7452
|
+
}
|
|
7453
|
+
process.exit(1);
|
|
7454
|
+
}
|
|
7455
|
+
}
|
|
7456
|
+
);
|
|
7457
|
+
|
|
7458
|
+
// src/cli/commands/templates.ts
|
|
7459
|
+
import { Command as Command12 } from "commander";
|
|
7460
|
+
import chalk15 from "chalk";
|
|
7461
|
+
import fs13 from "fs/promises";
|
|
7462
|
+
import path14 from "path";
|
|
7463
|
+
var TEMPLATE_TYPES = [
|
|
7464
|
+
"controller",
|
|
7465
|
+
"service",
|
|
7466
|
+
"repository",
|
|
7467
|
+
"types",
|
|
7468
|
+
"schemas",
|
|
7469
|
+
"routes",
|
|
7470
|
+
"module-index",
|
|
7471
|
+
"controller-test",
|
|
7472
|
+
"service-test",
|
|
7473
|
+
"integration-test"
|
|
7474
|
+
];
|
|
7475
|
+
async function initTemplates() {
|
|
7476
|
+
const projectError = validateProject();
|
|
7477
|
+
if (projectError) {
|
|
7478
|
+
displayError(projectError);
|
|
7479
|
+
return;
|
|
7480
|
+
}
|
|
7481
|
+
const projectRoot = getProjectRoot();
|
|
7482
|
+
const templatesDir = path14.join(projectRoot, ".servcraft", "templates");
|
|
7483
|
+
try {
|
|
7484
|
+
await fs13.mkdir(templatesDir, { recursive: true });
|
|
7485
|
+
console.log(chalk15.cyan("\n\u{1F4C1} Creating custom template directory...\n"));
|
|
7486
|
+
const exampleController = `// Custom controller template
|
|
7487
|
+
// Available variables: name, pascalName, camelName, pluralName
|
|
7488
|
+
export function controllerTemplate(name: string, pascalName: string, camelName: string): string {
|
|
7489
|
+
return \`import type { FastifyRequest, FastifyReply } from 'fastify';
|
|
7490
|
+
import type { \${pascalName}Service } from './\${name}.service.js';
|
|
7491
|
+
|
|
7492
|
+
export class \${pascalName}Controller {
|
|
7493
|
+
constructor(private \${camelName}Service: \${pascalName}Service) {}
|
|
7494
|
+
|
|
7495
|
+
// Add your custom controller methods here
|
|
7496
|
+
async getAll(request: FastifyRequest, reply: FastifyReply) {
|
|
7497
|
+
const data = await this.\${camelName}Service.getAll();
|
|
7498
|
+
return reply.send({ data });
|
|
7499
|
+
}
|
|
7500
|
+
}
|
|
7501
|
+
\`;
|
|
7502
|
+
}
|
|
7503
|
+
`;
|
|
7504
|
+
await fs13.writeFile(
|
|
7505
|
+
path14.join(templatesDir, "controller.example.ts"),
|
|
7506
|
+
exampleController,
|
|
7507
|
+
"utf-8"
|
|
7508
|
+
);
|
|
7509
|
+
console.log(chalk15.green("\u2714 Created template directory: .servcraft/templates/"));
|
|
7510
|
+
console.log(chalk15.green("\u2714 Created example template: controller.example.ts\n"));
|
|
7511
|
+
console.log(chalk15.bold("\u{1F4CB} Available template types:\n"));
|
|
7512
|
+
TEMPLATE_TYPES.forEach((type) => {
|
|
7513
|
+
console.log(chalk15.gray(` \u2022 ${type}.ts`));
|
|
7514
|
+
});
|
|
7515
|
+
console.log(chalk15.yellow("\n\u{1F4A1} To customize a template:"));
|
|
7516
|
+
console.log(chalk15.gray(" 1. Copy the example template"));
|
|
7517
|
+
console.log(chalk15.gray(" 2. Rename it (remove .example)"));
|
|
7518
|
+
console.log(chalk15.gray(" 3. Modify the template code"));
|
|
7519
|
+
console.log(chalk15.gray(" 4. Use --template flag when generating\n"));
|
|
7520
|
+
} catch (error2) {
|
|
7521
|
+
if (error2 instanceof Error) {
|
|
7522
|
+
console.error(chalk15.red(`
|
|
7523
|
+
\u2717 Failed to initialize templates: ${error2.message}
|
|
7524
|
+
`));
|
|
7525
|
+
}
|
|
7526
|
+
}
|
|
7527
|
+
}
|
|
7528
|
+
async function listTemplates() {
|
|
7529
|
+
const projectError = validateProject();
|
|
7530
|
+
if (projectError) {
|
|
7531
|
+
displayError(projectError);
|
|
7532
|
+
return;
|
|
7533
|
+
}
|
|
7534
|
+
const projectRoot = getProjectRoot();
|
|
7535
|
+
const projectTemplatesDir = path14.join(projectRoot, ".servcraft", "templates");
|
|
7536
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
7537
|
+
const userTemplatesDir = path14.join(homeDir, ".servcraft", "templates");
|
|
7538
|
+
console.log(chalk15.bold.cyan("\n\u{1F4CB} Available Templates\n"));
|
|
7539
|
+
console.log(chalk15.bold("Project templates (.servcraft/templates/):"));
|
|
7540
|
+
try {
|
|
7541
|
+
const files = await fs13.readdir(projectTemplatesDir);
|
|
7542
|
+
const templates = files.filter((f) => f.endsWith(".ts") && !f.endsWith(".example.ts"));
|
|
7543
|
+
if (templates.length > 0) {
|
|
7544
|
+
templates.forEach((t) => {
|
|
7545
|
+
console.log(chalk15.green(` \u2713 ${t}`));
|
|
7546
|
+
});
|
|
7547
|
+
} else {
|
|
7548
|
+
console.log(chalk15.gray(" (none)"));
|
|
7549
|
+
}
|
|
7550
|
+
} catch {
|
|
7551
|
+
console.log(chalk15.gray(" (directory not found)"));
|
|
7552
|
+
}
|
|
7553
|
+
console.log(chalk15.bold("\nUser templates (~/.servcraft/templates/):"));
|
|
7554
|
+
try {
|
|
7555
|
+
const files = await fs13.readdir(userTemplatesDir);
|
|
7556
|
+
const templates = files.filter((f) => f.endsWith(".ts") && !f.endsWith(".example.ts"));
|
|
7557
|
+
if (templates.length > 0) {
|
|
7558
|
+
templates.forEach((t) => {
|
|
7559
|
+
console.log(chalk15.green(` \u2713 ${t}`));
|
|
7560
|
+
});
|
|
7561
|
+
} else {
|
|
7562
|
+
console.log(chalk15.gray(" (none)"));
|
|
7563
|
+
}
|
|
7564
|
+
} catch {
|
|
7565
|
+
console.log(chalk15.gray(" (directory not found)"));
|
|
7566
|
+
}
|
|
7567
|
+
console.log(chalk15.bold("\nBuilt-in templates:"));
|
|
7568
|
+
TEMPLATE_TYPES.forEach((t) => {
|
|
7569
|
+
console.log(chalk15.cyan(` \u2022 ${t}.ts`));
|
|
7570
|
+
});
|
|
7571
|
+
console.log(chalk15.gray('\n\u{1F4A1} Run "servcraft templates init" to create custom templates\n'));
|
|
7572
|
+
}
|
|
7573
|
+
var templatesCommand = new Command12("templates").description("Manage code generation templates").addCommand(
|
|
7574
|
+
new Command12("init").description("Initialize custom templates directory").action(initTemplates)
|
|
7575
|
+
).addCommand(new Command12("list").description("List available templates").action(listTemplates));
|
|
7576
|
+
|
|
7256
7577
|
// src/cli/index.ts
|
|
7257
|
-
var program = new
|
|
7578
|
+
var program = new Command13();
|
|
7258
7579
|
program.name("servcraft").description("Servcraft - A modular Node.js backend framework CLI").version("0.1.0");
|
|
7259
7580
|
program.addCommand(initCommand);
|
|
7260
7581
|
program.addCommand(generateCommand);
|
|
@@ -7266,5 +7587,7 @@ program.addCommand(removeCommand);
|
|
|
7266
7587
|
program.addCommand(doctorCommand);
|
|
7267
7588
|
program.addCommand(updateCommand);
|
|
7268
7589
|
program.addCommand(completionCommand);
|
|
7590
|
+
program.addCommand(scaffoldCommand);
|
|
7591
|
+
program.addCommand(templatesCommand);
|
|
7269
7592
|
program.parse();
|
|
7270
7593
|
//# sourceMappingURL=index.js.map
|