opticore-feature-component 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -176,7 +176,9 @@ async function choiceFeatureCleanModule() {
176
176
  ["opticore_principle"],
177
177
  [
178
178
  { label: "OptiCoreJs CLEAN Module", value: ["opticore_clean_module"], hint: "recommended" },
179
- { label: "Custom feature", value: ["custom_feature"] }
179
+ { label: "MVC Architecture", value: ["mvc_architecture"] },
180
+ { label: "Layered Architecture", value: ["layered_architecture"] },
181
+ { label: "Build your own architecture", value: ["custom_feature"] }
180
182
  ]
181
183
  );
182
184
  if ((0, import_prompts2.isCancel)(featureSelected)) {
@@ -1015,19 +1017,19 @@ var init_router_template = __esm({
1015
1017
  const handlerName = `${capitalized}HandlerRouter`;
1016
1018
  const controllerName = `${capitalized}Controller`;
1017
1019
  const routesCode = (controllerMethods || []).map((method) => {
1018
- const { httpMethod, path: path8 } = resolveRoute(method, featureName);
1020
+ const { httpMethod, path: path10 } = resolveRoute(method, featureName);
1019
1021
  return `{
1020
- path: \`${path8}\`,
1022
+ path: \`${path10}\`,
1021
1023
  method: "${httpMethod}",
1022
1024
  middlewares: [],
1023
1025
  handler: async (ctx: ICustomContext) => await ${controllerName}.${method}(ctx.req, ctx.res)
1024
1026
  }`;
1025
1027
  }).join(",\n");
1026
- return `import { OpticoreRouting, ICustomContext, IMultipleRouteDefinition } from "opticore-router";
1028
+ return `import { OpticoreRoutingFactory, ICustomContext, IMultipleRouteDefinition } from "opticore-router";
1027
1029
  import { ${controllerName} } from "../adapters/controllers/${featureName}.controller";
1028
1030
 
1029
1031
  export const ${handlerName}: () => IMultipleRouteDefinition = () => {
1030
- return OpticoreRouting.routes(
1032
+ return OpticoreRoutingFactory.routes(
1031
1033
  ${controllerName},
1032
1034
  [
1033
1035
  ${routesCode.replace(/\n/g, "\n ")}
@@ -1591,19 +1593,19 @@ var init_simpleRouter_template = __esm({
1591
1593
  const handlerName = `${capitalized}HandlerRouter`;
1592
1594
  const controllerName = `${capitalized}Controller`;
1593
1595
  const routesCode = (controllerMethods || []).map((method) => {
1594
- const { httpMethod, path: path8 } = resolveRoute2(method, featureName);
1596
+ const { httpMethod, path: path10 } = resolveRoute2(method, featureName);
1595
1597
  return `{
1596
- path: \`${path8}\`,
1598
+ path: \`${path10}\`,
1597
1599
  method: "${httpMethod}",
1598
1600
  middlewares: [],
1599
1601
  handler: async (ctx: ICustomContext) => await ${controllerName}.${method}(ctx.req, ctx.res)
1600
1602
  }`;
1601
1603
  }).join(",\n");
1602
- return `import { OpticoreRouting, ICustomContext, IMultipleRouteDefinition } from "opticore-router";
1604
+ return `import { OpticoreRoutingFactory, ICustomContext, IMultipleRouteDefinition } from "opticore-router";
1603
1605
  import { ${controllerName} } from "../controllers/${featureName}.controller";
1604
1606
 
1605
1607
  export const ${handlerName}: () => IMultipleRouteDefinition = () => {
1606
- return OpticoreRouting.routes(
1608
+ return OpticoreRoutingFactory.routes(
1607
1609
  ${controllerName},
1608
1610
  [
1609
1611
  ${routesCode.replace(/\n/g, "\n ")}
@@ -1870,7 +1872,7 @@ export class ${controllerName} {}
1870
1872
  */
1871
1873
  static async updateRegisterRouter(featureName) {
1872
1874
  const routerName = `${this.capitalize(featureName)}Router`;
1873
- const routerPath = this.structure === "simple" ? `../../features/${featureName}/routes/${featureName}.router` : this.structure === "custom" ? `../../features/${featureName}/${featureName}.router` : `../../features/${featureName}/infrastructure/routes/${featureName}.router`;
1875
+ const routerPath = this.structure === "simple" || this.structure === "mvc" || this.structure === "layered" ? `../../features/${featureName}/routes/${featureName}.router` : this.structure === "custom" ? `../../features/${featureName}/${featureName}.router` : `../../features/${featureName}/infrastructure/routes/${featureName}.router`;
1874
1876
  const importStatement = `import { ${routerName} } from "${routerPath}"`;
1875
1877
  try {
1876
1878
  if (!import_fs2.default.existsSync(this.REGISTER_ROUTER_PATH)) {
@@ -2418,19 +2420,19 @@ export const ${moduleName.toUpperCase()}_PREFIX = "/${moduleName}";
2418
2420
  const handlerName = `${cap}HandlerRouter`;
2419
2421
  const controllerName = `${cap}Controller`;
2420
2422
  const routesCode = (controllerMethods || []).map((method) => {
2421
- const { httpMethod, path: path8 } = CResolveRoute(method, moduleName);
2423
+ const { httpMethod, path: path10 } = CResolveRoute(method, moduleName);
2422
2424
  return `{
2423
- path: \`${path8}\`,
2425
+ path: \`${path10}\`,
2424
2426
  method: "${httpMethod}",
2425
2427
  middlewares: [],
2426
2428
  handler: async (ctx: ICustomContext) => await ${controllerName}.${method}(ctx.req, ctx.res)
2427
2429
  }`;
2428
2430
  }).join(",\n");
2429
- return `import { OpticoreRouting, ICustomContext, IMultipleRouteDefinition } from "opticore-router";
2431
+ return `import { OpticoreRoutingFactory, ICustomContext, IMultipleRouteDefinition } from "opticore-router";
2430
2432
  import { ${controllerName} } from "../../app/${moduleName}.controller";
2431
2433
 
2432
2434
  export const ${handlerName}: () => IMultipleRouteDefinition = () => {
2433
- return OpticoreRouting.routes(
2435
+ return OpticoreRoutingFactory.routes(
2434
2436
  ${controllerName},
2435
2437
  [
2436
2438
  ${routesCode ? routesCode.replace(/\n/g, "\n ") : ""}
@@ -2584,17 +2586,711 @@ ${import_ansi_colors9.default.bgGreen(import_ansi_colors9.default.white(`
2584
2586
  }
2585
2587
  });
2586
2588
 
2589
+ // src/templates/mvcModule/mvcModule.template.ts
2590
+ var capitalize19, generateMvcModelTemplate, generateMvcViewTemplate, generateMvcControllerTemplate, generateMethod4, generateFindAllMethod3, generateFindByIdMethod3, generateCreateMethod3, generateUpdateMethod3, generateDeleteMethod3, generateMvcRouterHandlerTemplate, generateMvcRouterTemplate, resolveRoute3;
2591
+ var init_mvcModule_template = __esm({
2592
+ "src/templates/mvcModule/mvcModule.template.ts"() {
2593
+ "use strict";
2594
+ init_cjs_shims();
2595
+ capitalize19 = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2596
+ generateMvcModelTemplate = (featureName) => {
2597
+ const capitalized = capitalize19(featureName);
2598
+ const modelName = `${capitalized}Model`;
2599
+ return `/**
2600
+ * ${modelName} \u2014 Model
2601
+ * Owns both the data shape and the data-access logic for "${featureName}".
2602
+ */
2603
+ export class ${modelName} {
2604
+ id: string;
2605
+ createdAt: Date;
2606
+ updatedAt: Date;
2607
+
2608
+ constructor(
2609
+ id: string,
2610
+ // TODO: add your model properties here
2611
+ createdAt?: Date,
2612
+ updatedAt?: Date,
2613
+ ) {
2614
+ this.id = id;
2615
+ this.createdAt = createdAt ?? new Date();
2616
+ this.updatedAt = updatedAt ?? new Date();
2617
+ }
2618
+
2619
+ private static store: Map<string, ${modelName}> = new Map();
2620
+
2621
+ static async findAll(): Promise<${modelName}[]> {
2622
+ // TODO: Replace with your ORM query
2623
+ return Array.from(this.store.values());
2624
+ }
2625
+
2626
+ static async findById(id: string): Promise<${modelName} | null> {
2627
+ // TODO: Replace with your ORM query
2628
+ return this.store.get(id) ?? null;
2629
+ }
2630
+
2631
+ static async create(data: Record<string, unknown>): Promise<${modelName}> {
2632
+ const model = new ${modelName}(String(Date.now()));
2633
+ // TODO: Map data fields onto model
2634
+ this.store.set(model.id, model);
2635
+ return model;
2636
+ }
2637
+
2638
+ static async update(id: string, data: Record<string, unknown>): Promise<${modelName} | null> {
2639
+ const existing = this.store.get(id);
2640
+ if (!existing) return null;
2641
+ // TODO: Apply data fields onto existing model
2642
+ existing.updatedAt = new Date();
2643
+ this.store.set(id, existing);
2644
+ return existing;
2645
+ }
2646
+
2647
+ static async delete(id: string): Promise<boolean> {
2648
+ // TODO: Replace with your ORM delete
2649
+ return this.store.delete(id);
2650
+ }
2651
+ }
2652
+ `;
2653
+ };
2654
+ generateMvcViewTemplate = (featureName) => {
2655
+ const capitalized = capitalize19(featureName);
2656
+ const modelName = `${capitalized}Model`;
2657
+ const viewName = `${capitalized}View`;
2658
+ return `import { ${modelName} } from "../models/${featureName}.model";
2659
+
2660
+ /**
2661
+ * ${viewName} \u2014 View
2662
+ * Shapes "${featureName}" data before it is sent back as JSON.
2663
+ */
2664
+ export class ${viewName} {
2665
+ static toJSON(model: ${modelName}): Record<string, unknown> {
2666
+ return {
2667
+ id: model.id,
2668
+ createdAt: model.createdAt,
2669
+ updatedAt: model.updatedAt,
2670
+ // TODO: expose the fields API consumers should see (and only those)
2671
+ };
2672
+ }
2673
+
2674
+ static toJSONList(models: ${modelName}[]): Record<string, unknown>[] {
2675
+ return models.map((model: ${modelName}): Record<string, unknown> => this.toJSON(model));
2676
+ }
2677
+ }
2678
+ `;
2679
+ };
2680
+ generateMvcControllerTemplate = (controllerName, featureName, methods) => {
2681
+ const capitalized = capitalize19(featureName);
2682
+ const modelName = `${capitalized}Model`;
2683
+ const viewName = `${capitalized}View`;
2684
+ const methodsCode = methods.map((method) => generateMethod4(method, capitalized, modelName, viewName)).join("\n");
2685
+ return `import { Request, Response } from "opticore-express";
2686
+ import { ResponseHandler, HttpStatusCode, IResponseHandlerSuccessData } from "opticore-http-response";
2687
+ import { ${modelName} } from "../models/${featureName}.model";
2688
+ import { ${viewName} } from "../views/${featureName}.view";
2689
+
2690
+ export class ${controllerName} {
2691
+ ${methodsCode}
2692
+ private static handleError(error: unknown) {
2693
+ const message = error instanceof Error ? error.message : "Internal server error";
2694
+ return ResponseHandler.error(message, HttpStatusCode.INTERNAL_SERVER_ERROR);
2695
+ }
2696
+ }
2697
+ `;
2698
+ };
2699
+ generateMethod4 = (method, capitalized, modelName, viewName) => {
2700
+ const lower = method.toLowerCase();
2701
+ if (lower.includes("findall") || lower.includes("getall")) return generateFindAllMethod3(method, capitalized, modelName, viewName);
2702
+ if (lower.includes("findbyid") || lower.includes("getbyid") || lower.includes("getone")) return generateFindByIdMethod3(method, capitalized, modelName, viewName);
2703
+ if (lower.includes("create") || lower.includes("add")) return generateCreateMethod3(method, capitalized, modelName, viewName);
2704
+ if (lower.includes("update") || lower.includes("edit")) return generateUpdateMethod3(method, capitalized, modelName, viewName);
2705
+ if (lower.includes("delete") || lower.includes("remove")) return generateDeleteMethod3(method, capitalized, modelName);
2706
+ return `
2707
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
2708
+ try {
2709
+ // TODO: Implement ${method} logic
2710
+ return ResponseHandler.success({}, "success", HttpStatusCode.OK);
2711
+ } catch (error) {
2712
+ return ${capitalized}Controller.handleError(error);
2713
+ }
2714
+ }
2715
+ `;
2716
+ };
2717
+ generateFindAllMethod3 = (method, capitalized, modelName, viewName) => `
2718
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
2719
+ try {
2720
+ const models = await ${modelName}.findAll();
2721
+ return ResponseHandler.success(${viewName}.toJSONList(models), "success", HttpStatusCode.OK);
2722
+ } catch (error) {
2723
+ return ${capitalized}Controller.handleError(error);
2724
+ }
2725
+ }
2726
+ `;
2727
+ generateFindByIdMethod3 = (method, capitalized, modelName, viewName) => `
2728
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
2729
+ try {
2730
+ const { id } = req.params;
2731
+ const model = await ${modelName}.findById(id);
2732
+ if (!model) {
2733
+ return ResponseHandler.error(\`Not found: \${id}\`, HttpStatusCode.NOT_FOUND);
2734
+ }
2735
+ return ResponseHandler.success(${viewName}.toJSON(model), "success", HttpStatusCode.OK);
2736
+ } catch (error) {
2737
+ return ${capitalized}Controller.handleError(error);
2738
+ }
2739
+ }
2740
+ `;
2741
+ generateCreateMethod3 = (method, capitalized, modelName, viewName) => `
2742
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
2743
+ try {
2744
+ const model = await ${modelName}.create(req.body);
2745
+ return ResponseHandler.success(${viewName}.toJSON(model), "created", HttpStatusCode.CREATED);
2746
+ } catch (error) {
2747
+ return ${capitalized}Controller.handleError(error);
2748
+ }
2749
+ }
2750
+ `;
2751
+ generateUpdateMethod3 = (method, capitalized, modelName, viewName) => `
2752
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
2753
+ try {
2754
+ const { id } = req.params;
2755
+ const model = await ${modelName}.update(id, req.body);
2756
+ if (!model) {
2757
+ return ResponseHandler.error(\`Not found: \${id}\`, HttpStatusCode.NOT_FOUND);
2758
+ }
2759
+ return ResponseHandler.success(${viewName}.toJSON(model), "success", HttpStatusCode.OK);
2760
+ } catch (error) {
2761
+ return ${capitalized}Controller.handleError(error);
2762
+ }
2763
+ }
2764
+ `;
2765
+ generateDeleteMethod3 = (method, capitalized, modelName) => `
2766
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
2767
+ try {
2768
+ const { id } = req.params;
2769
+ const deleted = await ${modelName}.delete(id);
2770
+ if (!deleted) {
2771
+ return ResponseHandler.error(\`Not found: \${id}\`, HttpStatusCode.NOT_FOUND);
2772
+ }
2773
+ return ResponseHandler.success(null, "deleted", HttpStatusCode.NO_CONTENT);
2774
+ } catch (error) {
2775
+ return ${capitalized}Controller.handleError(error);
2776
+ }
2777
+ }
2778
+ `;
2779
+ generateMvcRouterHandlerTemplate = (featureName, controllerMethods) => {
2780
+ const capitalized = capitalize19(featureName);
2781
+ const handlerName = `${capitalized}HandlerRouter`;
2782
+ const controllerName = `${capitalized}Controller`;
2783
+ const routesCode = (controllerMethods || []).map((method) => {
2784
+ const { httpMethod, path: path10 } = resolveRoute3(method, featureName);
2785
+ return `{
2786
+ path: \`${path10}\`,
2787
+ method: "${httpMethod}",
2788
+ middlewares: [],
2789
+ handler: async (ctx: ICustomContext) => await ${controllerName}.${method}(ctx.req, ctx.res)
2790
+ }`;
2791
+ }).join(",\n");
2792
+ return `import { OpticoreRoutingFactory, ICustomContext, IMultipleRouteDefinition } from "opticore-router";
2793
+ import { ${controllerName} } from "../controllers/${featureName}.controller";
2794
+
2795
+ export const ${handlerName}: () => IMultipleRouteDefinition = () => {
2796
+ return OpticoreRoutingFactory.routes(
2797
+ ${controllerName},
2798
+ [
2799
+ ${routesCode.replace(/\n/g, "\n ")}
2800
+ ]
2801
+ );
2802
+ };
2803
+ `;
2804
+ };
2805
+ generateMvcRouterTemplate = (featureName) => {
2806
+ const capitalized = capitalize19(featureName);
2807
+ const handlerName = `${capitalized}HandlerRouter`;
2808
+ const routerName = `${capitalized}Router`;
2809
+ return `import { ${handlerName} } from "./${featureName}.router.handler";
2810
+ import { TFeatureRoutes } from "opticore-router";
2811
+
2812
+ export const ${routerName}: TFeatureRoutes = {
2813
+ routes: [
2814
+ {
2815
+ path: ${handlerName}().path,
2816
+ handler: ${handlerName}().handler
2817
+ }
2818
+ ]
2819
+ };
2820
+ `;
2821
+ };
2822
+ resolveRoute3 = (methodName, featureName) => {
2823
+ const lower = methodName.toLowerCase();
2824
+ if (lower.includes("findall") || lower.includes("getall")) return { httpMethod: "get", path: `/${featureName}` };
2825
+ if (lower.includes("findbyid") || lower.includes("getbyid") || lower.includes("getone")) return { httpMethod: "get", path: `/${featureName}/:id` };
2826
+ if (lower.includes("create") || lower.includes("add")) return { httpMethod: "post", path: `/${featureName}` };
2827
+ if (lower.includes("update") || lower.includes("edit")) return { httpMethod: "put", path: `/${featureName}/:id` };
2828
+ if (lower.includes("delete") || lower.includes("remove")) return { httpMethod: "delete", path: `/${featureName}/:id` };
2829
+ return { httpMethod: "get", path: `/${featureName}/${lower}` };
2830
+ };
2831
+ }
2832
+ });
2833
+
2834
+ // src/templates/mvcModule/mvcModuleScaffold.template.ts
2835
+ var import_path7, import_ansi_colors10, import_prompts6, capitalize20, mvcModuleTemplate;
2836
+ var init_mvcModuleScaffold_template = __esm({
2837
+ "src/templates/mvcModule/mvcModuleScaffold.template.ts"() {
2838
+ "use strict";
2839
+ init_cjs_shims();
2840
+ import_path7 = __toESM(require("path"), 1);
2841
+ import_ansi_colors10 = __toESM(require("ansi-colors"), 1);
2842
+ import_prompts6 = require("@clack/prompts");
2843
+ init_prompt_utils();
2844
+ init_fsModule_utils();
2845
+ init_featureComponentGenerator_utils();
2846
+ init_mvcModule_template();
2847
+ capitalize20 = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2848
+ mvcModuleTemplate = async (featureName) => {
2849
+ FeatureComponentGeneratorUtils.setStructure("mvc");
2850
+ const basePath = import_path7.default.join("src", "features", featureName);
2851
+ const paths = {
2852
+ modelsDir: import_path7.default.join(basePath, "models"),
2853
+ viewsDir: import_path7.default.join(basePath, "views"),
2854
+ controllersDir: import_path7.default.join(basePath, "controllers"),
2855
+ routesDir: import_path7.default.join(basePath, "routes")
2856
+ };
2857
+ Object.values(paths).forEach((dir) => UFsModule.createDirectoryRecursively(dir));
2858
+ const controllerName = `${capitalize20(featureName)}Controller`;
2859
+ UFsModule.createFile(import_path7.default.join(paths.modelsDir, `${featureName}.model.ts`), generateMvcModelTemplate(featureName));
2860
+ console.log(import_ansi_colors10.default.green(`\u2705 Model created: ${import_path7.default.join(paths.modelsDir, `${featureName}.model.ts`)}`));
2861
+ UFsModule.createFile(import_path7.default.join(paths.viewsDir, `${featureName}.view.ts`), generateMvcViewTemplate(featureName));
2862
+ console.log(import_ansi_colors10.default.green(`\u2705 View created: ${import_path7.default.join(paths.viewsDir, `${featureName}.view.ts`)}`));
2863
+ const wantsMethods = await UPrompt.confirm("Do you want to add methods to the controller?");
2864
+ let methods = [];
2865
+ let controllerContent;
2866
+ if (wantsMethods && !(0, import_prompts6.isCancel)(wantsMethods)) {
2867
+ const methodsText = await UPrompt.text(
2868
+ "Enter the method names (comma separated):",
2869
+ "create, findAll, findById, update, delete",
2870
+ (v) => v ? void 0 : "You must enter at least one method."
2871
+ );
2872
+ methods = methodsText.split(",").map((m) => m.trim()).filter(Boolean);
2873
+ controllerContent = generateMvcControllerTemplate(controllerName, featureName, methods);
2874
+ } else {
2875
+ controllerContent = `import { Request, Response } from "opticore-express";
2876
+
2877
+ export class ${controllerName} {}
2878
+ `;
2879
+ }
2880
+ const controllerPath = import_path7.default.join(paths.controllersDir, `${featureName}.controller.ts`);
2881
+ UFsModule.createFile(controllerPath, controllerContent);
2882
+ console.log(import_ansi_colors10.default.green(`\u2705 Controller created: ${controllerPath}`));
2883
+ UFsModule.createFile(
2884
+ import_path7.default.join(paths.routesDir, `${featureName}.router.handler.ts`),
2885
+ generateMvcRouterHandlerTemplate(featureName, methods)
2886
+ );
2887
+ console.log(import_ansi_colors10.default.green(`\u2705 Router Handler created: ${import_path7.default.join(paths.routesDir, `${featureName}.router.handler.ts`)}`));
2888
+ UFsModule.createFile(
2889
+ import_path7.default.join(paths.routesDir, `${featureName}.router.ts`),
2890
+ generateMvcRouterTemplate(featureName)
2891
+ );
2892
+ await FeatureComponentGeneratorUtils.registerModuleRouter(featureName);
2893
+ console.log(import_ansi_colors10.default.green(`\u2705 Router created: ${import_path7.default.join(paths.routesDir, `${featureName}.router.ts`)}`));
2894
+ console.log(
2895
+ import_ansi_colors10.default.bgGreen(
2896
+ import_ansi_colors10.default.white(`
2897
+ \u{1F389} Feature "${featureName}" scaffolded with MVC architecture!
2898
+ `)
2899
+ )
2900
+ );
2901
+ };
2902
+ }
2903
+ });
2904
+
2905
+ // src/templates/layeredModule/layeredModule.template.ts
2906
+ var capitalize21, generateLayeredEntityTemplate, generateLayeredDtoTemplate, generateLayeredRepositoryTemplate, generateLayeredServiceTemplate, generateLayeredControllerTemplate, generateMethod5, generateFindAllMethod4, generateFindByIdMethod4, generateCreateMethod4, generateUpdateMethod4, generateDeleteMethod4, generateLayeredRouterHandlerTemplate, generateLayeredRouterTemplate, resolveRoute4;
2907
+ var init_layeredModule_template = __esm({
2908
+ "src/templates/layeredModule/layeredModule.template.ts"() {
2909
+ "use strict";
2910
+ init_cjs_shims();
2911
+ capitalize21 = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2912
+ generateLayeredEntityTemplate = (featureName) => {
2913
+ const capitalized = capitalize21(featureName);
2914
+ const entityName = `${capitalized}Entity`;
2915
+ return `/**
2916
+ * ${entityName} \u2014 Entity
2917
+ * Plain data structure for the "${featureName}" domain. No data-access logic here.
2918
+ */
2919
+ export class ${entityName} {
2920
+ constructor(
2921
+ public readonly id: string,
2922
+ // TODO: add your entity fields here
2923
+ public readonly createdAt: Date = new Date(),
2924
+ public readonly updatedAt: Date = new Date(),
2925
+ ) {}
2926
+ }
2927
+ `;
2928
+ };
2929
+ generateLayeredDtoTemplate = (featureName) => {
2930
+ const capitalized = capitalize21(featureName);
2931
+ const entityName = `${capitalized}Entity`;
2932
+ return `import { ${entityName} } from "../entities/${featureName}.entity";
2933
+
2934
+ export interface Create${capitalized}Dto {
2935
+ // TODO: define the fields required to create a ${featureName}
2936
+ }
2937
+
2938
+ export interface Update${capitalized}Dto {
2939
+ // TODO: define the fields that can be updated
2940
+ }
2941
+
2942
+ export interface ${capitalized}ResponseDto {
2943
+ id: string;
2944
+ createdAt: Date;
2945
+ updatedAt: Date;
2946
+ // TODO: define the fields exposed to API consumers
2947
+ }
2948
+
2949
+ export const to${capitalized}ResponseDto = (entity: ${entityName}): ${capitalized}ResponseDto => ({
2950
+ id: entity.id,
2951
+ createdAt: entity.createdAt,
2952
+ updatedAt: entity.updatedAt,
2953
+ });
2954
+ `;
2955
+ };
2956
+ generateLayeredRepositoryTemplate = (featureName) => {
2957
+ const capitalized = capitalize21(featureName);
2958
+ const entityName = `${capitalized}Entity`;
2959
+ const repoName = `${capitalized}Repository`;
2960
+ return `import { ${entityName} } from "../entities/${featureName}.entity";
2961
+
2962
+ export class ${repoName} {
2963
+ private store: Map<string, ${entityName}> = new Map();
2964
+
2965
+ async findAll(): Promise<${entityName}[]> {
2966
+ // TODO: Replace with your ORM query
2967
+ return Array.from(this.store.values());
2968
+ }
2969
+
2970
+ async findById(id: string): Promise<${entityName} | null> {
2971
+ // TODO: Replace with your ORM query
2972
+ return this.store.get(id) ?? null;
2973
+ }
2974
+
2975
+ async create(entity: ${entityName}): Promise<${entityName}> {
2976
+ // TODO: Replace with your ORM save
2977
+ this.store.set(entity.id, entity);
2978
+ return entity;
2979
+ }
2980
+
2981
+ async update(entity: ${entityName}): Promise<${entityName} | null> {
2982
+ // TODO: Replace with your ORM update
2983
+ if (!this.store.has(entity.id)) return null;
2984
+ this.store.set(entity.id, entity);
2985
+ return entity;
2986
+ }
2987
+
2988
+ async delete(id: string): Promise<boolean> {
2989
+ // TODO: Replace with your ORM delete
2990
+ return this.store.delete(id);
2991
+ }
2992
+ }
2993
+ `;
2994
+ };
2995
+ generateLayeredServiceTemplate = (featureName) => {
2996
+ const capitalized = capitalize21(featureName);
2997
+ const entityName = `${capitalized}Entity`;
2998
+ const repoName = `${capitalized}Repository`;
2999
+ const serviceName = `${capitalized}Service`;
3000
+ return `import { ${repoName} } from "../repositories/${featureName}.repository";
3001
+ import { ${entityName} } from "../entities/${featureName}.entity";
3002
+ import { Create${capitalized}Dto, Update${capitalized}Dto, ${capitalized}ResponseDto, to${capitalized}ResponseDto } from "../dto/${featureName}.dto";
3003
+
3004
+ export class ${serviceName} {
3005
+ private readonly repository: ${repoName};
3006
+
3007
+ constructor() {
3008
+ this.repository = new ${repoName}();
3009
+ }
3010
+
3011
+ async findAll(): Promise<${capitalized}ResponseDto[]> {
3012
+ const entities = await this.repository.findAll();
3013
+ return entities.map(to${capitalized}ResponseDto);
3014
+ }
3015
+
3016
+ async findById(id: string): Promise<${capitalized}ResponseDto | null> {
3017
+ const entity = await this.repository.findById(id);
3018
+ return entity ? to${capitalized}ResponseDto(entity) : null;
3019
+ }
3020
+
3021
+ async create(data: Create${capitalized}Dto): Promise<${capitalized}ResponseDto> {
3022
+ const entity = new ${entityName}(String(Date.now()));
3023
+ // TODO: Map data fields onto entity
3024
+ const created = await this.repository.create(entity);
3025
+ return to${capitalized}ResponseDto(created);
3026
+ }
3027
+
3028
+ async update(id: string, data: Update${capitalized}Dto): Promise<${capitalized}ResponseDto | null> {
3029
+ const existing = await this.repository.findById(id);
3030
+ if (!existing) {
3031
+ return null;
3032
+ }
3033
+ // TODO: Apply data fields onto existing entity
3034
+ const updated = await this.repository.update(existing);
3035
+ return updated ? to${capitalized}ResponseDto(updated) : null;
3036
+ }
3037
+
3038
+ async delete(id: string): Promise<boolean> {
3039
+ return this.repository.delete(id);
3040
+ }
3041
+ }
3042
+ `;
3043
+ };
3044
+ generateLayeredControllerTemplate = (controllerName, featureName, methods) => {
3045
+ const capitalized = capitalize21(featureName);
3046
+ const serviceName = `${capitalized}Service`;
3047
+ const methodsCode = methods.map((method) => generateMethod5(method, capitalized, serviceName)).join("\n");
3048
+ return `import { Request, Response } from "opticore-express";
3049
+ import { ResponseHandler, HttpStatusCode, IResponseHandlerSuccessData } from "opticore-http-response";
3050
+ import { ${serviceName} } from "../services/${featureName}.service";
3051
+
3052
+ export class ${controllerName} {
3053
+ private static buildService(): ${serviceName} {
3054
+ return new ${serviceName}();
3055
+ }
3056
+
3057
+ ${methodsCode}
3058
+ private static handleError(error: unknown) {
3059
+ const message = error instanceof Error ? error.message : "Internal server error";
3060
+ return ResponseHandler.error(message, HttpStatusCode.INTERNAL_SERVER_ERROR);
3061
+ }
3062
+ }
3063
+ `;
3064
+ };
3065
+ generateMethod5 = (method, capitalized, serviceName) => {
3066
+ const lower = method.toLowerCase();
3067
+ if (lower.includes("findall") || lower.includes("getall")) return generateFindAllMethod4(method, capitalized);
3068
+ if (lower.includes("findbyid") || lower.includes("getbyid") || lower.includes("getone")) return generateFindByIdMethod4(method, capitalized);
3069
+ if (lower.includes("create") || lower.includes("add")) return generateCreateMethod4(method, capitalized);
3070
+ if (lower.includes("update") || lower.includes("edit")) return generateUpdateMethod4(method, capitalized);
3071
+ if (lower.includes("delete") || lower.includes("remove")) return generateDeleteMethod4(method, capitalized);
3072
+ return `
3073
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
3074
+ try {
3075
+ const service = ${capitalized}Controller.buildService();
3076
+ // TODO: Implement ${method} logic
3077
+ return ResponseHandler.success({}, "success", HttpStatusCode.OK);
3078
+ } catch (error) {
3079
+ return ${capitalized}Controller.handleError(error);
3080
+ }
3081
+ }
3082
+ `;
3083
+ };
3084
+ generateFindAllMethod4 = (method, capitalized) => `
3085
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
3086
+ try {
3087
+ const service = ${capitalized}Controller.buildService();
3088
+ const results = await service.findAll();
3089
+ return ResponseHandler.success(results, "success", HttpStatusCode.OK);
3090
+ } catch (error) {
3091
+ return ${capitalized}Controller.handleError(error);
3092
+ }
3093
+ }
3094
+ `;
3095
+ generateFindByIdMethod4 = (method, capitalized) => `
3096
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
3097
+ try {
3098
+ const { id } = req.params;
3099
+ const service = ${capitalized}Controller.buildService();
3100
+ const result = await service.findById(id);
3101
+ if (!result) {
3102
+ return ResponseHandler.error(\`Not found: \${id}\`, HttpStatusCode.NOT_FOUND);
3103
+ }
3104
+ return ResponseHandler.success(result, "success", HttpStatusCode.OK);
3105
+ } catch (error) {
3106
+ return ${capitalized}Controller.handleError(error);
3107
+ }
3108
+ }
3109
+ `;
3110
+ generateCreateMethod4 = (method, capitalized) => `
3111
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
3112
+ try {
3113
+ const service = ${capitalized}Controller.buildService();
3114
+ const result = await service.create(req.body);
3115
+ return ResponseHandler.success(result, "created", HttpStatusCode.CREATED);
3116
+ } catch (error) {
3117
+ return ${capitalized}Controller.handleError(error);
3118
+ }
3119
+ }
3120
+ `;
3121
+ generateUpdateMethod4 = (method, capitalized) => `
3122
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
3123
+ try {
3124
+ const { id } = req.params;
3125
+ const service = ${capitalized}Controller.buildService();
3126
+ const result = await service.update(id, req.body);
3127
+ if (!result) {
3128
+ return ResponseHandler.error(\`Not found: \${id}\`, HttpStatusCode.NOT_FOUND);
3129
+ }
3130
+ return ResponseHandler.success(result, "success", HttpStatusCode.OK);
3131
+ } catch (error) {
3132
+ return ${capitalized}Controller.handleError(error);
3133
+ }
3134
+ }
3135
+ `;
3136
+ generateDeleteMethod4 = (method, capitalized) => `
3137
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
3138
+ try {
3139
+ const { id } = req.params;
3140
+ const service = ${capitalized}Controller.buildService();
3141
+ const deleted = await service.delete(id);
3142
+ if (!deleted) {
3143
+ return ResponseHandler.error(\`Not found: \${id}\`, HttpStatusCode.NOT_FOUND);
3144
+ }
3145
+ return ResponseHandler.success(null, "deleted", HttpStatusCode.NO_CONTENT);
3146
+ } catch (error) {
3147
+ return ${capitalized}Controller.handleError(error);
3148
+ }
3149
+ }
3150
+ `;
3151
+ generateLayeredRouterHandlerTemplate = (featureName, controllerMethods) => {
3152
+ const capitalized = capitalize21(featureName);
3153
+ const handlerName = `${capitalized}HandlerRouter`;
3154
+ const controllerName = `${capitalized}Controller`;
3155
+ const routesCode = (controllerMethods || []).map((method) => {
3156
+ const { httpMethod, path: path10 } = resolveRoute4(method, featureName);
3157
+ return `{
3158
+ path: \`${path10}\`,
3159
+ method: "${httpMethod}",
3160
+ middlewares: [],
3161
+ handler: async (ctx: ICustomContext) => await ${controllerName}.${method}(ctx.req, ctx.res)
3162
+ }`;
3163
+ }).join(",\n");
3164
+ return `import { OpticoreRoutingFactory, ICustomContext, IMultipleRouteDefinition } from "opticore-router";
3165
+ import { ${controllerName} } from "../controllers/${featureName}.controller";
3166
+
3167
+ export const ${handlerName}: () => IMultipleRouteDefinition = () => {
3168
+ return OpticoreRoutingFactory.routes(
3169
+ ${controllerName},
3170
+ [
3171
+ ${routesCode.replace(/\n/g, "\n ")}
3172
+ ]
3173
+ );
3174
+ };
3175
+ `;
3176
+ };
3177
+ generateLayeredRouterTemplate = (featureName) => {
3178
+ const capitalized = capitalize21(featureName);
3179
+ const handlerName = `${capitalized}HandlerRouter`;
3180
+ const routerName = `${capitalized}Router`;
3181
+ return `import { ${handlerName} } from "./${featureName}.router.handler";
3182
+ import { TFeatureRoutes } from "opticore-router";
3183
+
3184
+ export const ${routerName}: TFeatureRoutes = {
3185
+ routes: [
3186
+ {
3187
+ path: ${handlerName}().path,
3188
+ handler: ${handlerName}().handler
3189
+ }
3190
+ ]
3191
+ };
3192
+ `;
3193
+ };
3194
+ resolveRoute4 = (methodName, featureName) => {
3195
+ const lower = methodName.toLowerCase();
3196
+ if (lower.includes("findall") || lower.includes("getall")) return { httpMethod: "get", path: `/${featureName}` };
3197
+ if (lower.includes("findbyid") || lower.includes("getbyid") || lower.includes("getone")) return { httpMethod: "get", path: `/${featureName}/:id` };
3198
+ if (lower.includes("create") || lower.includes("add")) return { httpMethod: "post", path: `/${featureName}` };
3199
+ if (lower.includes("update") || lower.includes("edit")) return { httpMethod: "put", path: `/${featureName}/:id` };
3200
+ if (lower.includes("delete") || lower.includes("remove")) return { httpMethod: "delete", path: `/${featureName}/:id` };
3201
+ return { httpMethod: "get", path: `/${featureName}/${lower}` };
3202
+ };
3203
+ }
3204
+ });
3205
+
3206
+ // src/templates/layeredModule/layeredModuleScaffold.template.ts
3207
+ var import_path8, import_ansi_colors11, import_prompts7, capitalize22, layeredModuleTemplate;
3208
+ var init_layeredModuleScaffold_template = __esm({
3209
+ "src/templates/layeredModule/layeredModuleScaffold.template.ts"() {
3210
+ "use strict";
3211
+ init_cjs_shims();
3212
+ import_path8 = __toESM(require("path"), 1);
3213
+ import_ansi_colors11 = __toESM(require("ansi-colors"), 1);
3214
+ import_prompts7 = require("@clack/prompts");
3215
+ init_prompt_utils();
3216
+ init_fsModule_utils();
3217
+ init_featureComponentGenerator_utils();
3218
+ init_layeredModule_template();
3219
+ capitalize22 = (str) => str.charAt(0).toUpperCase() + str.slice(1);
3220
+ layeredModuleTemplate = async (featureName) => {
3221
+ FeatureComponentGeneratorUtils.setStructure("layered");
3222
+ const basePath = import_path8.default.join("src", "features", featureName);
3223
+ const paths = {
3224
+ entitiesDir: import_path8.default.join(basePath, "entities"),
3225
+ dtoDir: import_path8.default.join(basePath, "dto"),
3226
+ repositoriesDir: import_path8.default.join(basePath, "repositories"),
3227
+ servicesDir: import_path8.default.join(basePath, "services"),
3228
+ controllersDir: import_path8.default.join(basePath, "controllers"),
3229
+ routesDir: import_path8.default.join(basePath, "routes")
3230
+ };
3231
+ Object.values(paths).forEach((dir) => UFsModule.createDirectoryRecursively(dir));
3232
+ const controllerName = `${capitalize22(featureName)}Controller`;
3233
+ UFsModule.createFile(import_path8.default.join(paths.entitiesDir, `${featureName}.entity.ts`), generateLayeredEntityTemplate(featureName));
3234
+ console.log(import_ansi_colors11.default.green(`\u2705 Entity created: ${import_path8.default.join(paths.entitiesDir, `${featureName}.entity.ts`)}`));
3235
+ UFsModule.createFile(import_path8.default.join(paths.dtoDir, `${featureName}.dto.ts`), generateLayeredDtoTemplate(featureName));
3236
+ console.log(import_ansi_colors11.default.green(`\u2705 DTO created: ${import_path8.default.join(paths.dtoDir, `${featureName}.dto.ts`)}`));
3237
+ UFsModule.createFile(import_path8.default.join(paths.repositoriesDir, `${featureName}.repository.ts`), generateLayeredRepositoryTemplate(featureName));
3238
+ console.log(import_ansi_colors11.default.green(`\u2705 Repository created: ${import_path8.default.join(paths.repositoriesDir, `${featureName}.repository.ts`)}`));
3239
+ UFsModule.createFile(import_path8.default.join(paths.servicesDir, `${featureName}.service.ts`), generateLayeredServiceTemplate(featureName));
3240
+ console.log(import_ansi_colors11.default.green(`\u2705 Service created: ${import_path8.default.join(paths.servicesDir, `${featureName}.service.ts`)}`));
3241
+ const wantsMethods = await UPrompt.confirm("Do you want to add methods to the controller?");
3242
+ let methods = [];
3243
+ let controllerContent;
3244
+ if (wantsMethods && !(0, import_prompts7.isCancel)(wantsMethods)) {
3245
+ const methodsText = await UPrompt.text(
3246
+ "Enter the method names (comma separated):",
3247
+ "create, findAll, findById, update, delete",
3248
+ (v) => v ? void 0 : "You must enter at least one method."
3249
+ );
3250
+ methods = methodsText.split(",").map((m) => m.trim()).filter(Boolean);
3251
+ controllerContent = generateLayeredControllerTemplate(controllerName, featureName, methods);
3252
+ } else {
3253
+ controllerContent = `import { Request, Response } from "opticore-express";
3254
+
3255
+ export class ${controllerName} {}
3256
+ `;
3257
+ }
3258
+ const controllerPath = import_path8.default.join(paths.controllersDir, `${featureName}.controller.ts`);
3259
+ UFsModule.createFile(controllerPath, controllerContent);
3260
+ console.log(import_ansi_colors11.default.green(`\u2705 Controller created: ${controllerPath}`));
3261
+ UFsModule.createFile(
3262
+ import_path8.default.join(paths.routesDir, `${featureName}.router.handler.ts`),
3263
+ generateLayeredRouterHandlerTemplate(featureName, methods)
3264
+ );
3265
+ console.log(import_ansi_colors11.default.green(`\u2705 Router Handler created: ${import_path8.default.join(paths.routesDir, `${featureName}.router.handler.ts`)}`));
3266
+ UFsModule.createFile(
3267
+ import_path8.default.join(paths.routesDir, `${featureName}.router.ts`),
3268
+ generateLayeredRouterTemplate(featureName)
3269
+ );
3270
+ await FeatureComponentGeneratorUtils.registerModuleRouter(featureName);
3271
+ console.log(import_ansi_colors11.default.green(`\u2705 Router created: ${import_path8.default.join(paths.routesDir, `${featureName}.router.ts`)}`));
3272
+ console.log(
3273
+ import_ansi_colors11.default.bgGreen(
3274
+ import_ansi_colors11.default.white(`
3275
+ \u{1F389} Feature "${featureName}" scaffolded with Layered architecture!
3276
+ `)
3277
+ )
3278
+ );
3279
+ };
3280
+ }
3281
+ });
3282
+
2587
3283
  // src/core/core.ts
2588
3284
  var core_exports = {};
2589
- var import_process2, import_node_fs2, import_path7, import_prompts6;
3285
+ var import_process2, import_node_fs2, import_path9, import_prompts8;
2590
3286
  var init_core = __esm({
2591
3287
  "src/core/core.ts"() {
2592
3288
  "use strict";
2593
3289
  init_cjs_shims();
2594
3290
  import_process2 = __toESM(require("process"), 1);
2595
3291
  import_node_fs2 = __toESM(require("fs"), 1);
2596
- import_path7 = __toESM(require("path"), 1);
2597
- import_prompts6 = require("@clack/prompts");
3292
+ import_path9 = __toESM(require("path"), 1);
3293
+ import_prompts8 = require("@clack/prompts");
2598
3294
  init_welcomeMessage_utils();
2599
3295
  init_choiceFeatureCleanModule();
2600
3296
  init_customFeatureInfo_utils();
@@ -2602,6 +3298,8 @@ var init_core = __esm({
2602
3298
  init_base_service();
2603
3299
  init_createController_application();
2604
3300
  init_customModuleScaffold_template();
3301
+ init_mvcModuleScaffold_template();
3302
+ init_layeredModuleScaffold_template();
2605
3303
  (async () => {
2606
3304
  UWelcomeMessage();
2607
3305
  const featureChoice = await choiceFeatureCleanModule();
@@ -2609,18 +3307,40 @@ var init_core = __esm({
2609
3307
  await customModuleScaffoldTemplate();
2610
3308
  return;
2611
3309
  }
3310
+ if (featureChoice === "mvc_architecture" || featureChoice === "layered_architecture") {
3311
+ const featureName2 = await _featureName();
3312
+ const directory2 = import_path9.default.join(import_process2.default.cwd(), "src", "features");
3313
+ const featureDirectory2 = import_path9.default.join(directory2, featureName2.toString());
3314
+ if (BaseService._isSubdirectoryExists(featureName2)) {
3315
+ BaseService._featureFounded(featureName2);
3316
+ }
3317
+ if ((0, import_prompts8.isCancel)(featureName2)) {
3318
+ BaseService._cancelOperation(featureDirectory2);
3319
+ return;
3320
+ }
3321
+ if (!import_node_fs2.default.existsSync(directory2)) {
3322
+ BaseService._checkFeatureDir();
3323
+ return;
3324
+ }
3325
+ if (featureChoice === "mvc_architecture") {
3326
+ await mvcModuleTemplate(featureName2.toString());
3327
+ } else {
3328
+ await layeredModuleTemplate(featureName2.toString());
3329
+ }
3330
+ return;
3331
+ }
2612
3332
  if (!featureChoice || featureChoice !== "opticore_clean_module") {
2613
3333
  import_process2.default.exit(0);
2614
3334
  }
2615
3335
  UCustomFeatureInfo(featureChoice);
2616
3336
  const featureName = await _featureName();
2617
- const directory = import_path7.default.join(import_process2.default.cwd(), "src", "features");
2618
- const featureDirectory = import_path7.default.join(directory, featureName.toString());
3337
+ const directory = import_path9.default.join(import_process2.default.cwd(), "src", "features");
3338
+ const featureDirectory = import_path9.default.join(directory, featureName.toString());
2619
3339
  const featureCleanModule = BaseService._isSubdirectoryExists(featureName);
2620
3340
  if (featureCleanModule) {
2621
3341
  BaseService._featureFounded(featureName);
2622
3342
  }
2623
- if ((0, import_prompts6.isCancel)(featureName)) {
3343
+ if ((0, import_prompts8.isCancel)(featureName)) {
2624
3344
  BaseService._cancelOperation(featureDirectory);
2625
3345
  } else {
2626
3346
  try {