domain-driver 0.1.0 → 0.2.0

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.
Files changed (77) hide show
  1. package/README.md +138 -146
  2. package/dist/commands/action.js +118 -0
  3. package/dist/commands/component.js +18 -19
  4. package/dist/commands/container.js +10 -31
  5. package/dist/commands/controller.js +85 -0
  6. package/dist/commands/feature.js +59 -51
  7. package/dist/commands/hints.js +28 -0
  8. package/dist/commands/hook.js +10 -111
  9. package/dist/commands/repository.js +17 -115
  10. package/dist/commands/resolve.js +61 -0
  11. package/dist/commands/schema.js +22 -69
  12. package/dist/commands/service.js +15 -113
  13. package/dist/commands/sides.js +34 -0
  14. package/dist/commands/target.js +32 -0
  15. package/dist/commands/types.js +9 -17
  16. package/dist/commands/write.js +66 -0
  17. package/dist/index.js +109 -94
  18. package/dist/init/content.js +73 -0
  19. package/dist/init/init.js +60 -0
  20. package/dist/init/markers.js +21 -0
  21. package/dist/postinstall.js +80 -0
  22. package/dist/stack/detect.js +125 -0
  23. package/dist/stack/profiles/nest.js +18 -0
  24. package/dist/stack/profiles/next-frontend.js +28 -0
  25. package/dist/stack/profiles/next-fullstack.js +44 -0
  26. package/dist/stack/profiles/node.js +17 -0
  27. package/dist/stack/profiles/react.js +19 -0
  28. package/dist/stack/registry.js +63 -0
  29. package/dist/stack/types.js +24 -0
  30. package/dist/templates/actions.js +68 -0
  31. package/dist/templates/backend/server-repository.js +22 -0
  32. package/dist/templates/context.js +52 -0
  33. package/dist/templates/controllers/express.js +60 -0
  34. package/dist/templates/controllers/fastify.js +54 -0
  35. package/dist/templates/controllers/generic.js +30 -0
  36. package/dist/templates/controllers/hono.js +52 -0
  37. package/dist/templates/controllers/nest.js +63 -0
  38. package/dist/templates/controllers/next-action-route.js +40 -0
  39. package/dist/templates/controllers/next-route.js +62 -0
  40. package/dist/templates/controllers/node.js +46 -0
  41. package/dist/templates/controllers/shape.js +28 -0
  42. package/dist/templates/frontend/client-repository.js +38 -0
  43. package/dist/templates/frontend/component.js +18 -0
  44. package/dist/templates/frontend/container.js +28 -0
  45. package/{src/commands/hook.ts → dist/templates/frontend/hook.js} +28 -41
  46. package/dist/templates/frontend/page.js +26 -0
  47. package/dist/templates/nest/dto.js +12 -0
  48. package/dist/templates/nest/injectable.js +4 -0
  49. package/dist/templates/nest/module.js +41 -0
  50. package/dist/templates/service.js +40 -0
  51. package/dist/templates/shared/schema.js +13 -0
  52. package/dist/templates/shared/types.js +12 -0
  53. package/dist/templates/signatures.js +15 -0
  54. package/dist/utils/alias.js +80 -0
  55. package/dist/utils/fs.js +70 -0
  56. package/dist/utils/imports.js +61 -0
  57. package/dist/utils/naming.js +31 -0
  58. package/dist/utils/paths.js +44 -0
  59. package/package.json +27 -6
  60. package/scripts/postinstall.js +7 -0
  61. package/.github/workflows/publish.yml +0 -28
  62. package/dist/utils.js +0 -132
  63. package/src/__tests__/utils.test.ts +0 -128
  64. package/src/commands/__tests__/feature.test.ts +0 -85
  65. package/src/commands/__tests__/imports.test.ts +0 -115
  66. package/src/commands/__tests__/individual.test.ts +0 -137
  67. package/src/commands/component.ts +0 -33
  68. package/src/commands/container.ts +0 -42
  69. package/src/commands/feature.ts +0 -74
  70. package/src/commands/repository.ts +0 -95
  71. package/src/commands/schema.ts +0 -47
  72. package/src/commands/service.ts +0 -93
  73. package/src/commands/types.ts +0 -25
  74. package/src/index.ts +0 -126
  75. package/src/utils.ts +0 -115
  76. package/tsconfig.json +0 -13
  77. package/vitest.config.ts +0 -7
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderExpressController = renderExpressController;
4
+ exports.renderExpressRoutes = renderExpressRoutes;
5
+ const naming_1 = require("../../utils/naming");
6
+ const actions_1 = require("../actions");
7
+ const shape_1 = require("./shape");
8
+ function renderExpressController(ctx, spec, fromFile) {
9
+ const imports = [
10
+ "import { NextFunction, Request, Response } from 'express';",
11
+ ...(spec.schema !== null ? [(0, shape_1.schemaImport)(ctx, fromFile, spec.schema)] : []),
12
+ (0, shape_1.serviceImport)(ctx, fromFile, spec),
13
+ ].join('\n');
14
+ const reqName = spec.schema !== null || spec.usesId ? 'req' : '_req';
15
+ const requestType = spec.usesId ? 'Request<{ id: string }>' : 'Request';
16
+ const validate = spec.schema !== null
17
+ ? ` const parsed = ${spec.schema}Schema.safeParse(req.body);
18
+ if (!parsed.success) {
19
+ res.status(400).json({ errors: parsed.error.flatten() });
20
+ return;
21
+ }
22
+ `
23
+ : '';
24
+ const args = (0, shape_1.callArgs)(spec, 'req.params.id', 'parsed.data');
25
+ const respond = spec.status === 204
26
+ ? ` await service.handle(${args});
27
+ res.status(204).send();`
28
+ : ` res.status(${spec.status}).json(await service.handle(${args}));`;
29
+ return `${imports}
30
+
31
+ const service = new ${spec.name}Service();
32
+
33
+ export async function ${spec.handler}(${reqName}: ${requestType}, res: Response, next: NextFunction): Promise<void> {
34
+ ${validate} try {
35
+ ${respond}
36
+ } catch (error) {
37
+ next(error);
38
+ }
39
+ }
40
+ `;
41
+ }
42
+ function renderExpressRoutes(ctx, entity, fromFile) {
43
+ const name = (0, naming_1.lowerFirst)(entity);
44
+ const handler = (action) => (0, actions_1.standardAction)(action, entity).handler;
45
+ return `import { Router } from 'express';
46
+ ${(0, shape_1.controllerImports)(ctx, fromFile, entity)}
47
+
48
+ const router = Router();
49
+
50
+ router.get('/', ${handler('List')});
51
+ router.get('/:id', ${handler('Show')});
52
+ router.post('/', ${handler('Create')});
53
+ router.put('/:id', ${handler('Update')});
54
+ router.delete('/:id', ${handler('Delete')});
55
+
56
+ export default router;
57
+
58
+ // app.use('/${ctx.feature}', ${name}Routes);
59
+ `;
60
+ }
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderFastifyController = renderFastifyController;
4
+ exports.renderFastifyRoutes = renderFastifyRoutes;
5
+ const naming_1 = require("../../utils/naming");
6
+ const actions_1 = require("../actions");
7
+ const shape_1 = require("./shape");
8
+ function renderFastifyController(ctx, spec, fromFile) {
9
+ const imports = [
10
+ "import { FastifyReply, FastifyRequest } from 'fastify';",
11
+ ...(spec.schema !== null ? [(0, shape_1.schemaImport)(ctx, fromFile, spec.schema)] : []),
12
+ (0, shape_1.serviceImport)(ctx, fromFile, spec),
13
+ ].join('\n');
14
+ const requestType = spec.usesId ? 'FastifyRequest<{ Params: { id: string } }>' : 'FastifyRequest';
15
+ const requestName = spec.schema !== null || spec.usesId ? 'request' : '_request';
16
+ const validate = spec.schema !== null
17
+ ? ` const parsed = ${spec.schema}Schema.safeParse(request.body);
18
+ if (!parsed.success) {
19
+ reply.status(400).send({ errors: parsed.error.flatten() });
20
+ return;
21
+ }
22
+ `
23
+ : '';
24
+ const args = (0, shape_1.callArgs)(spec, 'request.params.id', 'parsed.data');
25
+ const respond = spec.status === 204
26
+ ? ` await service.handle(${args});
27
+ reply.status(204).send();`
28
+ : ` reply.status(${spec.status}).send(await service.handle(${args}));`;
29
+ return `${imports}
30
+
31
+ const service = new ${spec.name}Service();
32
+
33
+ export async function ${spec.handler}(${requestName}: ${requestType}, reply: FastifyReply): Promise<void> {
34
+ ${validate}${respond}
35
+ }
36
+ `;
37
+ }
38
+ function renderFastifyRoutes(ctx, entity, fromFile) {
39
+ const name = (0, naming_1.lowerFirst)(entity);
40
+ const handler = (action) => (0, actions_1.standardAction)(action, entity).handler;
41
+ return `import { FastifyInstance } from 'fastify';
42
+ ${(0, shape_1.controllerImports)(ctx, fromFile, entity)}
43
+
44
+ export async function ${name}Routes(app: FastifyInstance): Promise<void> {
45
+ app.get('/', ${handler('List')});
46
+ app.get('/:id', ${handler('Show')});
47
+ app.post('/', ${handler('Create')});
48
+ app.put('/:id', ${handler('Update')});
49
+ app.delete('/:id', ${handler('Delete')});
50
+ }
51
+
52
+ // app.register(${name}Routes, { prefix: '/${ctx.feature}' });
53
+ `;
54
+ }
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderGenericController = renderGenericController;
4
+ const shape_1 = require("./shape");
5
+ function renderGenericController(ctx, spec, entity, fromFile) {
6
+ const typeImport = spec.usesEntityType
7
+ ? [`import { ${entity} } from '${ctx.importLayer(fromFile, 'types', `${entity}.types`)}';`]
8
+ : [];
9
+ const imports = [
10
+ ...typeImport,
11
+ ...(spec.schema !== null ? [(0, shape_1.schemaImport)(ctx, fromFile, spec.schema)] : []),
12
+ (0, shape_1.serviceImport)(ctx, fromFile, spec),
13
+ ].join('\n');
14
+ const params = [spec.usesId ? 'id: string' : null, spec.schema !== null ? 'input: unknown' : null]
15
+ .filter((param) => param !== null)
16
+ .join(', ');
17
+ const args = [spec.usesId ? 'id' : null, spec.schema !== null ? `${spec.schema}Schema.parse(input)` : null]
18
+ .filter((arg) => arg !== null)
19
+ .join(', ');
20
+ return `${imports}
21
+
22
+ const service = new ${spec.name}Service();
23
+
24
+ export class ${spec.name}Controller {
25
+ async handle(${params}): ${spec.returns} {
26
+ return service.handle(${args});
27
+ }
28
+ }
29
+ `;
30
+ }
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderHonoController = renderHonoController;
4
+ exports.renderHonoRoutes = renderHonoRoutes;
5
+ const naming_1 = require("../../utils/naming");
6
+ const actions_1 = require("../actions");
7
+ const shape_1 = require("./shape");
8
+ function renderHonoController(ctx, spec, fromFile) {
9
+ const imports = [
10
+ "import { Context } from 'hono';",
11
+ ...(spec.schema !== null ? [(0, shape_1.schemaImport)(ctx, fromFile, spec.schema)] : []),
12
+ (0, shape_1.serviceImport)(ctx, fromFile, spec),
13
+ ].join('\n');
14
+ const contextType = spec.usesId ? "Context<{}, '/:id'>" : 'Context';
15
+ const validate = spec.schema !== null
16
+ ? ` const parsed = ${spec.schema}Schema.safeParse(await c.req.json());
17
+ if (!parsed.success) return c.json({ errors: parsed.error.flatten() }, 400);
18
+ `
19
+ : '';
20
+ const args = (0, shape_1.callArgs)(spec, "c.req.param('id')", 'parsed.data');
21
+ const respond = spec.status === 204
22
+ ? ` await service.handle(${args});
23
+ return c.body(null, 204);`
24
+ : ` return c.json(await service.handle(${args}), ${spec.status});`;
25
+ return `${imports}
26
+
27
+ const service = new ${spec.name}Service();
28
+
29
+ export async function ${spec.handler}(c: ${contextType}): Promise<Response> {
30
+ ${validate}${respond}
31
+ }
32
+ `;
33
+ }
34
+ function renderHonoRoutes(ctx, entity, fromFile) {
35
+ const name = (0, naming_1.lowerFirst)(entity);
36
+ const handler = (action) => (0, actions_1.standardAction)(action, entity).handler;
37
+ return `import { Hono } from 'hono';
38
+ ${(0, shape_1.controllerImports)(ctx, fromFile, entity)}
39
+
40
+ const ${name} = new Hono();
41
+
42
+ ${name}.get('/', ${handler('List')});
43
+ ${name}.get('/:id', ${handler('Show')});
44
+ ${name}.post('/', ${handler('Create')});
45
+ ${name}.put('/:id', ${handler('Update')});
46
+ ${name}.delete('/:id', ${handler('Delete')});
47
+
48
+ export default ${name};
49
+
50
+ // app.route('/${ctx.feature}', ${name});
51
+ `;
52
+ }
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderNestController = renderNestController;
4
+ const shape_1 = require("./shape");
5
+ const METHOD_DECORATORS = Object.freeze({
6
+ get: 'Get',
7
+ post: 'Post',
8
+ put: 'Put',
9
+ delete: 'Delete',
10
+ });
11
+ const NEST_DEFAULT_STATUS = Object.freeze({
12
+ get: 200,
13
+ post: 201,
14
+ put: 200,
15
+ delete: 200,
16
+ });
17
+ function nestRoute(spec) {
18
+ const decoratorName = METHOD_DECORATORS[spec.method];
19
+ const routePath = spec.path === '/' ? '' : spec.path.slice(1);
20
+ const route = `@${decoratorName}(${routePath ? `'${routePath}'` : ''})`;
21
+ const needsHttpCode = spec.status !== NEST_DEFAULT_STATUS[spec.method];
22
+ const decorator = needsHttpCode ? `${route}\n @HttpCode(${spec.status})` : route;
23
+ const commonImports = [
24
+ 'Controller',
25
+ decoratorName,
26
+ ...(spec.schema !== null ? ['Body'] : []),
27
+ ...(spec.usesId ? ['Param'] : []),
28
+ ...(needsHttpCode ? ['HttpCode'] : []),
29
+ ].sort();
30
+ return { decorator, commonImports };
31
+ }
32
+ function renderNestController(ctx, spec, entity, fromFile) {
33
+ const route = nestRoute(spec);
34
+ const dto = spec.schema !== null ? `${spec.schema}Dto` : null;
35
+ const imports = [
36
+ `import { ${route.commonImports.join(', ')} } from '@nestjs/common';`,
37
+ ...(spec.usesEntityType
38
+ ? [`import { ${entity} } from '${ctx.importLayer(fromFile, 'types', `${entity}.types`)}';`]
39
+ : []),
40
+ ...(dto !== null && spec.schema !== null
41
+ ? [`import { ${dto} } from '${ctx.importLayer(fromFile, 'dto', `${spec.schema}.dto`)}';`]
42
+ : []),
43
+ (0, shape_1.serviceImport)(ctx, fromFile, spec),
44
+ ].join('\n');
45
+ const params = [spec.usesId ? "@Param('id') id: string" : null, dto !== null ? `@Body() body: ${dto}` : null]
46
+ .filter((param) => param !== null)
47
+ .join(', ');
48
+ const args = [spec.usesId ? 'id' : null, dto !== null ? 'body' : null]
49
+ .filter((arg) => arg !== null)
50
+ .join(', ');
51
+ return `${imports}
52
+
53
+ @Controller('${ctx.feature}')
54
+ export class ${spec.name}Controller {
55
+ constructor(private readonly service: ${spec.name}Service) {}
56
+
57
+ ${route.decorator}
58
+ handle(${params}): ${spec.returns} {
59
+ return this.service.handle(${args});
60
+ }
61
+ }
62
+ `;
63
+ }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderActionRoute = renderActionRoute;
4
+ const shape_1 = require("./shape");
5
+ function renderActionRoute(ctx, spec, fromFile) {
6
+ const imports = [
7
+ "import { NextResponse } from 'next/server';",
8
+ ...(spec.schema !== null ? [(0, shape_1.schemaImport)(ctx, fromFile, spec.schema)] : []),
9
+ (0, shape_1.serviceImport)(ctx, fromFile, spec),
10
+ ].join('\n');
11
+ const method = spec.method.toUpperCase();
12
+ const requestParam = spec.schema !== null ? 'request: Request' : '';
13
+ const validate = spec.schema !== null
14
+ ? ` const parsed = ${spec.schema}Schema.safeParse(await request.json());
15
+ if (!parsed.success) {
16
+ return NextResponse.json({ errors: parsed.error.flatten() }, { status: 400 });
17
+ }
18
+ `
19
+ : '';
20
+ const args = spec.schema !== null ? 'parsed.data' : '';
21
+ if (spec.status === 204) {
22
+ return `${imports}
23
+
24
+ const service = new ${spec.name}Service();
25
+
26
+ export async function ${method}(${requestParam}): Promise<Response> {
27
+ ${validate} await service.handle(${args});
28
+ return new Response(null, { status: 204 });
29
+ }
30
+ `;
31
+ }
32
+ return `${imports}
33
+
34
+ const service = new ${spec.name}Service();
35
+
36
+ export async function ${method}(${requestParam}): Promise<NextResponse> {
37
+ ${validate} return NextResponse.json(await service.handle(${args}));
38
+ }
39
+ `;
40
+ }
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderCollectionRoute = renderCollectionRoute;
4
+ exports.renderItemRoute = renderItemRoute;
5
+ const actions_1 = require("../actions");
6
+ const shape_1 = require("./shape");
7
+ function renderCollectionRoute(ctx, entity, fromFile) {
8
+ return `import { NextResponse } from 'next/server';
9
+ ${(0, shape_1.schemaImport)(ctx, fromFile, `Create${entity}`)}
10
+ ${(0, shape_1.serviceImport)(ctx, fromFile, (0, actions_1.standardAction)('List', entity))}
11
+ ${(0, shape_1.serviceImport)(ctx, fromFile, (0, actions_1.standardAction)('Create', entity))}
12
+
13
+ const listService = new List${entity}Service();
14
+ const createService = new Create${entity}Service();
15
+
16
+ export async function GET(): Promise<NextResponse> {
17
+ return NextResponse.json(await listService.handle());
18
+ }
19
+
20
+ export async function POST(request: Request): Promise<NextResponse> {
21
+ const parsed = Create${entity}Schema.safeParse(await request.json());
22
+ if (!parsed.success) {
23
+ return NextResponse.json({ errors: parsed.error.flatten() }, { status: 400 });
24
+ }
25
+ return NextResponse.json(await createService.handle(parsed.data), { status: 201 });
26
+ }
27
+ `;
28
+ }
29
+ function renderItemRoute(ctx, entity, fromFile) {
30
+ return `import { NextResponse } from 'next/server';
31
+ ${(0, shape_1.schemaImport)(ctx, fromFile, `Update${entity}`)}
32
+ ${(0, shape_1.serviceImport)(ctx, fromFile, (0, actions_1.standardAction)('Show', entity))}
33
+ ${(0, shape_1.serviceImport)(ctx, fromFile, (0, actions_1.standardAction)('Update', entity))}
34
+ ${(0, shape_1.serviceImport)(ctx, fromFile, (0, actions_1.standardAction)('Delete', entity))}
35
+
36
+ const showService = new Show${entity}Service();
37
+ const updateService = new Update${entity}Service();
38
+ const deleteService = new Delete${entity}Service();
39
+
40
+ type RouteContext = { params: Promise<{ id: string }> };
41
+
42
+ export async function GET(_request: Request, { params }: RouteContext): Promise<NextResponse> {
43
+ const { id } = await params;
44
+ return NextResponse.json(await showService.handle(id));
45
+ }
46
+
47
+ export async function PUT(request: Request, { params }: RouteContext): Promise<NextResponse> {
48
+ const { id } = await params;
49
+ const parsed = Update${entity}Schema.safeParse(await request.json());
50
+ if (!parsed.success) {
51
+ return NextResponse.json({ errors: parsed.error.flatten() }, { status: 400 });
52
+ }
53
+ return NextResponse.json(await updateService.handle(id, parsed.data));
54
+ }
55
+
56
+ export async function DELETE(_request: Request, { params }: RouteContext): Promise<Response> {
57
+ const { id } = await params;
58
+ await deleteService.handle(id);
59
+ return new Response(null, { status: 204 });
60
+ }
61
+ `;
62
+ }
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderNodeController = renderNodeController;
4
+ exports.renderNodeRoutes = renderNodeRoutes;
5
+ exports.renderNodeRouteLine = renderNodeRouteLine;
6
+ const naming_1 = require("../../utils/naming");
7
+ const express_1 = require("./express");
8
+ const fastify_1 = require("./fastify");
9
+ const hono_1 = require("./hono");
10
+ const generic_1 = require("./generic");
11
+ function renderNodeController(ctx, spec, entity, fromFile) {
12
+ switch (ctx.stack.httpFramework) {
13
+ case 'express':
14
+ return (0, express_1.renderExpressController)(ctx, spec, fromFile);
15
+ case 'fastify':
16
+ return (0, fastify_1.renderFastifyController)(ctx, spec, fromFile);
17
+ case 'hono':
18
+ return (0, hono_1.renderHonoController)(ctx, spec, fromFile);
19
+ case null:
20
+ return (0, generic_1.renderGenericController)(ctx, spec, entity, fromFile);
21
+ }
22
+ }
23
+ function renderNodeRoutes(ctx, entity, fromFile) {
24
+ switch (ctx.stack.httpFramework) {
25
+ case 'express':
26
+ return (0, express_1.renderExpressRoutes)(ctx, entity, fromFile);
27
+ case 'fastify':
28
+ return (0, fastify_1.renderFastifyRoutes)(ctx, entity, fromFile);
29
+ case 'hono':
30
+ return (0, hono_1.renderHonoRoutes)(ctx, entity, fromFile);
31
+ case null:
32
+ return null;
33
+ }
34
+ }
35
+ function renderNodeRouteLine(ctx, spec, entity) {
36
+ switch (ctx.stack.httpFramework) {
37
+ case 'express':
38
+ return `router.${spec.method}('${spec.path}', ${spec.handler});`;
39
+ case 'fastify':
40
+ return `app.${spec.method}('${spec.path}', ${spec.handler});`;
41
+ case 'hono':
42
+ return `${(0, naming_1.lowerFirst)(entity)}.${spec.method}('${spec.path}', ${spec.handler});`;
43
+ case null:
44
+ return null;
45
+ }
46
+ }
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.schemaImport = schemaImport;
4
+ exports.serviceImport = serviceImport;
5
+ exports.controllerImports = controllerImports;
6
+ exports.callArgs = callArgs;
7
+ const actions_1 = require("../actions");
8
+ function schemaImport(ctx, fromFile, schemaName) {
9
+ const schemaPath = ctx.importLayer(fromFile, 'schema', `${schemaName}.schema`);
10
+ return `import { ${schemaName}Schema } from '${schemaPath}';`;
11
+ }
12
+ function serviceImport(ctx, fromFile, spec) {
13
+ const servicePath = ctx.importLayer(fromFile, 'serverService', `${spec.name}.service`);
14
+ return `import { ${spec.name}Service } from '${servicePath}';`;
15
+ }
16
+ function controllerImports(ctx, fromFile, entity) {
17
+ return (0, actions_1.standardActions)(entity)
18
+ .map((spec) => {
19
+ const controllerPath = ctx.importLayer(fromFile, 'controller', `${spec.name}.controller`);
20
+ return `import { ${spec.handler} } from '${controllerPath}';`;
21
+ })
22
+ .join('\n');
23
+ }
24
+ function callArgs(spec, idExpression, bodyExpression) {
25
+ return [spec.usesId ? idExpression : null, spec.schema !== null ? bodyExpression : null]
26
+ .filter((arg) => arg !== null)
27
+ .join(', ');
28
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderClientRepository = renderClientRepository;
4
+ const signatures_1 = require("../signatures");
5
+ const JSON_HEADERS = "headers: { 'Content-Type': 'application/json' },";
6
+ function fetchUrl(spec, base) {
7
+ if (!spec.path.includes(':id'))
8
+ return `'${base}${spec.path === '/' ? '' : spec.path}'`;
9
+ return `\`${base}${spec.path.replace(':id', '${id}')}\``;
10
+ }
11
+ function fetchCall(spec, base) {
12
+ const url = fetchUrl(spec, base);
13
+ if (spec.method === 'get')
14
+ return url;
15
+ const method = spec.method.toUpperCase();
16
+ if (spec.schema === null) {
17
+ return `${url}, {
18
+ method: '${method}',
19
+ }`;
20
+ }
21
+ return `${url}, {
22
+ method: '${method}',
23
+ ${JSON_HEADERS}
24
+ body: JSON.stringify(data),
25
+ }`;
26
+ }
27
+ function renderClientRepository(ctx, spec, entity, fromFile) {
28
+ const imports = (0, signatures_1.domainImports)(ctx, fromFile, spec, entity).join('\n');
29
+ const header = imports ? `${imports}\n\n` : '';
30
+ const returnLine = spec.returns === 'Promise<void>' ? '' : ' return response.json();\n';
31
+ return `${header}export class ${spec.name}Repository {
32
+ async handle(${spec.params}): ${spec.returns} {
33
+ const response = await fetch(${fetchCall(spec, `/api/${ctx.feature}`)});
34
+ if (!response.ok) throw new Error('${spec.failure}');
35
+ ${returnLine} }
36
+ }
37
+ `;
38
+ }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderComponent = renderComponent;
4
+ function renderComponent(name, directive) {
5
+ const header = directive ? "'use client';\n\n" : '';
6
+ return `${header}interface ${name}Props {
7
+ id: string;
8
+ }
9
+
10
+ export default function ${name}({ id }: ${name}Props) {
11
+ return (
12
+ <div>
13
+ <h1>${name} {id}</h1>
14
+ </div>
15
+ );
16
+ }
17
+ `;
18
+ }
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderContainer = renderContainer;
4
+ const registry_1 = require("../../stack/registry");
5
+ function renderContainer(ctx, containerName, entity, fromFile) {
6
+ const header = ctx.profile.clientDirective ? "'use client';\n\n" : '';
7
+ const hookName = `use${entity}`;
8
+ const hookPath = ctx.importLayer(fromFile, 'hook', hookName);
9
+ const componentPath = ctx.importFrom(fromFile, `${(0, registry_1.componentDir)(ctx.profile, 'client')}/${entity}`);
10
+ return `${header}import { ${hookName} } from '${hookPath}';
11
+ import ${entity} from '${componentPath}';
12
+
13
+ export default function ${containerName}() {
14
+ const { items, loading, error } = ${hookName}();
15
+
16
+ if (loading) return <div>Loading...</div>;
17
+ if (error) return <div>Error: {error}</div>;
18
+
19
+ return (
20
+ <div>
21
+ {items.map((item) => (
22
+ <${entity} key={item.id} {...item} />
23
+ ))}
24
+ </div>
25
+ );
26
+ }
27
+ `;
28
+ }
@@ -1,32 +1,32 @@
1
- import * as path from 'path';
2
- import { ensureFeatureExists, writeFileSafe, fileExists, resolveImport } from '../utils';
3
-
4
- function renderHook(hookName: string, name: string, feature: string): string {
5
- const typePath = resolveImport(feature, `types/${name}.types`);
6
- const svcPath = (op: string) => resolveImport(feature, `services/${op}${name}.service`);
7
- const schemaPath = (op: string) => resolveImport(feature, `schemas/${op}${name}.schema`);
8
-
9
- return `'use client';
10
-
11
- import { useState, useEffect, useCallback } from 'react';
12
- import { ${name} } from '${typePath}';
13
- import { List${name}Service } from '${svcPath('List')}';
14
- import { Show${name}Service } from '${svcPath('Show')}';
15
- import { Create${name}Service } from '${svcPath('Create')}';
16
- import { Update${name}Service } from '${svcPath('Update')}';
17
- import { Delete${name}Service } from '${svcPath('Delete')}';
18
- import { Create${name} } from '${schemaPath('Create')}';
19
- import { Update${name} } from '${schemaPath('Update')}';
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderHook = renderHook;
4
+ const actions_1 = require("../actions");
5
+ function serviceImports(ctx, fromFile, entity) {
6
+ return actions_1.ACTIONS.map((action) => {
7
+ const servicePath = ctx.importLayer(fromFile, 'clientService', `${action}${entity}.service`);
8
+ return `import { ${action}${entity}Service } from '${servicePath}';`;
9
+ }).join('\n');
10
+ }
11
+ function serviceInstances(entity) {
12
+ return actions_1.ACTIONS.map((action) => `const ${action.toLowerCase()}Service = new ${action}${entity}Service();`).join('\n');
13
+ }
14
+ function renderHook(ctx, hookName, entity, fromFile) {
15
+ const header = ctx.profile.clientDirective ? "'use client';\n\n" : '';
16
+ const typePath = ctx.importLayer(fromFile, 'types', `${entity}.types`);
17
+ const createPath = ctx.importLayer(fromFile, 'schema', `Create${entity}.schema`);
18
+ const updatePath = ctx.importLayer(fromFile, 'schema', `Update${entity}.schema`);
19
+ return `${header}import { useState, useEffect, useCallback } from 'react';
20
+ import { ${entity} } from '${typePath}';
21
+ ${serviceImports(ctx, fromFile, entity)}
22
+ import { Create${entity} } from '${createPath}';
23
+ import { Update${entity} } from '${updatePath}';
20
24
 
21
- const listService = new List${name}Service();
22
- const showService = new Show${name}Service();
23
- const createService = new Create${name}Service();
24
- const updateService = new Update${name}Service();
25
- const deleteService = new Delete${name}Service();
25
+ ${serviceInstances(entity)}
26
26
 
27
27
  export function ${hookName}() {
28
- const [items, setItems] = useState<${name}[]>([]);
29
- const [selected, setSelected] = useState<${name} | null>(null);
28
+ const [items, setItems] = useState<${entity}[]>([]);
29
+ const [selected, setSelected] = useState<${entity} | null>(null);
30
30
  const [loading, setLoading] = useState(false);
31
31
  const [error, setError] = useState<string | null>(null);
32
32
 
@@ -56,7 +56,7 @@ export function ${hookName}() {
56
56
  }
57
57
  }, []);
58
58
 
59
- const create = useCallback(async (data: Create${name}) => {
59
+ const create = useCallback(async (data: Create${entity}) => {
60
60
  setLoading(true);
61
61
  setError(null);
62
62
  try {
@@ -71,7 +71,7 @@ export function ${hookName}() {
71
71
  }
72
72
  }, []);
73
73
 
74
- const update = useCallback(async (id: string, data: Update${name}) => {
74
+ const update = useCallback(async (id: string, data: Update${entity}) => {
75
75
  setLoading(true);
76
76
  setError(null);
77
77
  try {
@@ -107,16 +107,3 @@ export function ${hookName}() {
107
107
  }
108
108
  `;
109
109
  }
110
-
111
- export function makeHook(feature: string, name: string, pascalName?: string): void {
112
- const base = ensureFeatureExists(feature, 'hooks');
113
- const filePath = path.join(base, `${name}.ts`);
114
-
115
- if (fileExists(filePath)) {
116
- throw new Error(`Hook "${name}" already exists at ${filePath}`);
117
- }
118
-
119
- const entityName = pascalName ?? name.replace(/^use/, '');
120
- writeFileSafe(filePath, renderHook(name, entityName, feature));
121
- console.log(`✅ Hook "${name}" created at ${filePath}`);
122
- }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderPage = renderPage;
4
+ function renderPage(ctx, entity, fromFile, withContainer) {
5
+ if (!withContainer) {
6
+ return `export default function ${entity}Page() {
7
+ return (
8
+ <div>
9
+ <h1>${entity}</h1>
10
+ </div>
11
+ );
12
+ }
13
+ `;
14
+ }
15
+ const containerPath = ctx.importLayer(fromFile, 'container', `${entity}Container`);
16
+ return `import ${entity}Container from '${containerPath}';
17
+
18
+ export default function ${entity}Page() {
19
+ return (
20
+ <div>
21
+ <${entity}Container />
22
+ </div>
23
+ );
24
+ }
25
+ `;
26
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderDto = renderDto;
4
+ function renderDto(ctx, name, fromFile) {
5
+ const schemaName = `${name}Schema`;
6
+ const schemaPath = ctx.importLayer(fromFile, 'schema', `${name}.schema`);
7
+ return `import { createZodDto } from 'nestjs-zod';
8
+ import { ${schemaName} } from '${schemaPath}';
9
+
10
+ export class ${name}Dto extends createZodDto(${schemaName}) {}
11
+ `;
12
+ }