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
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # domain-driver 🚀
2
2
 
3
- A CLI scaffolding tool for domain-driven development in Next.js. Generate feature folder structures instantly — like Laravel's `php artisan make` but for Next.js.
3
+ A CLI scaffolding tool for domain-driven feature folders. Like Laravel's `php artisan make`, but for Next.js, React, Node, and NestJS projects. It detects your stack and generates only the layers that stack needs, one file per action.
4
4
 
5
5
  ---
6
6
 
@@ -13,249 +13,241 @@ npm install -g domain-driver
13
13
  Or use without installing:
14
14
 
15
15
  ```bash
16
- npx domain-driver make:feature <n>
16
+ npx domain-driver make:feature <feature>[/<Entity>]
17
17
  ```
18
18
 
19
19
  ---
20
20
 
21
- ## Commands
22
-
23
- ### `make:feature`
21
+ ## Stack detection
24
22
 
25
- Scaffold a full feature folder structure.
23
+ Every command reads your `package.json` once and prints the stack it found before writing anything:
26
24
 
27
- ```bash
28
- domain-driver make:feature <n>
29
- domain-driver make:feature <n> -a
30
25
  ```
26
+ Stack: next-fullstack (detected)
27
+ ```
28
+
29
+ | Stack | Detected when | Features live in |
30
+ |---|---|---|
31
+ | `nest` | `@nestjs/core` is a dependency | `src/<feature>` |
32
+ | `next-fullstack` | `next` is a dependency and `app/api`, `src/app/api`, `pages/api`, or `src/pages/api` exists | `app/<feature>` or `src/app/<feature>` |
33
+ | `next-frontend` | `next` is a dependency, no api directory | `app/<feature>` or `src/app/<feature>` |
34
+ | `react` | `react` is a dependency, `next` is not | `src/features/<feature>` or `features/<feature>` |
35
+ | `node` | none of the above | `src/features/<feature>` or `features/<feature>` |
36
+
37
+ For `node`, the tool also picks up Express, Fastify, or Hono and shapes the controllers accordingly. With none of them present it generates framework-agnostic controller classes.
31
38
 
32
- The `-a` flag scaffolds all files inside each folder automatically.
39
+ Override detection with `--stack`:
33
40
 
34
41
  ```bash
35
- domain-driver make:feature coffee-type # folders + .gitkeep only
36
- domain-driver make:feature coffee-type -a # folders + all files
42
+ domain-driver --stack nest make:feature cat -a
37
43
  ```
38
44
 
39
45
  ---
40
46
 
41
- ### `make:component`
47
+ ## What each stack generates
42
48
 
43
- Scaffold a component inside an existing feature. Defaults to `client` if no type is specified.
49
+ | Layer | next-fullstack | next-frontend | react | node | nest |
50
+ |---|---|---|---|---|---|
51
+ | `page.tsx` | yes | yes | | | |
52
+ | components | client + server | client + server | flat | | |
53
+ | containers, hooks | yes | yes | yes | | |
54
+ | client services + repositories (fetch) | `client/` | top level | top level | | |
55
+ | server services + repositories (database stubs) | `server/` | | | yes | yes |
56
+ | controllers | `app/api/<feature>/` route handlers | | | five files + routes file | five files |
57
+ | module | | | | | yes |
58
+ | DTOs (`nestjs-zod`) | | | | | yes |
59
+ | schemas (Zod), types | yes | yes | yes | yes | yes |
44
60
 
45
- ```bash
46
- domain-driver make:component <feature> <n>
47
- domain-driver make:component <feature> <n> client
48
- domain-driver make:component <feature> <n> server
49
- ```
50
-
51
- ```bash
52
- domain-driver make:component coffee-type CoffeeTypeList
53
- domain-driver make:component coffee-type CoffeeTypeForm client
54
- domain-driver make:component coffee-type CoffeeTypeCard server
55
- ```
61
+ Every layer that has actions gets one file per action: `List`, `Show`, `Create`, `Update`, `Delete`. Saving a cat means `CreateCat.controller.ts`, `CreateCat.service.ts`, `CreateCat.repository.ts`, and `CreateCat.schema.ts`.
56
62
 
57
63
  ---
58
64
 
59
- ### `make:container`
65
+ ## Commands
60
66
 
61
- Scaffold a smart container component inside an existing feature.
67
+ Every layer command takes one target, `<feature>/<Name>`: the feature folder on the left, the name used inside the files on the right. `users/User` reads as "User inside users".
62
68
 
63
- ```bash
64
- domain-driver make:container <feature> <n>
65
- ```
69
+ ### `make:feature`
66
70
 
67
71
  ```bash
68
- domain-driver make:container coffee-type CoffeeTypeContainer
72
+ domain-driver make:feature users # folders + .gitkeep, plus page.tsx (Next) or the module (Nest)
73
+ domain-driver make:feature users/User -a # every layer for the detected stack, entity User
69
74
  ```
70
75
 
71
- ---
76
+ Feature names must be kebab-case. Without `/Entity`, the entity is the PascalCase feature name (`users` becomes `Users`).
72
77
 
73
- ### `make:hook`
74
-
75
- Scaffold a custom hook inside an existing feature.
78
+ ### `make:action`
76
79
 
77
80
  ```bash
78
- domain-driver make:hook <feature> <n>
81
+ domain-driver make:action users/User findActiveUsers
82
+ domain-driver make:action users/User archiveUser --with-input --returns one
79
83
  ```
80
84
 
81
- ```bash
82
- domain-driver make:hook coffee-type useCoffeeType
83
- ```
85
+ Scaffolds a bespoke operation as its own files, so it never lands inside `ShowUser` or `ListUser`. The action name is used as-is for file and class names: `FindActiveUsers.service.ts`, `FindActiveUsersService`, handler `findActiveUsersController`, route `/find-active-users`. Include the noun in the name (`archiveUser`, not `archive`).
84
86
 
85
- ---
87
+ | Option | Effect |
88
+ |---|---|
89
+ | `--with-input` | writes `ArchiveUser.schema.ts` (and the Nest DTO), the service takes `data`, the controller is a `POST` with body validation |
90
+ | `--returns list` | `Promise<User[]>` (default) |
91
+ | `--returns one` | `Promise<User>` |
92
+ | `--returns void` | `Promise<void>`, controller responds 204 |
86
93
 
87
- ### `make:service`
94
+ | Stack | Files written |
95
+ |---|---|
96
+ | `node` | service, repository, controller for the detected framework, plus the line to add above the `/:id` routes in `<feature>.routes.ts` printed |
97
+ | `nest` | injectable service and repository, `@Controller` class, DTO with input, plus the classes to register printed, with the controller listed before `Show<Entity>Controller` |
98
+ | `next-fullstack` | server service and repository, client service and repository, `app/api/<feature>/<slug>/route.ts` |
99
+ | `next-frontend`, `react` | client service and repository calling `/api/<feature>/<slug>` |
88
100
 
89
- Scaffold a set of single-responsibility service files inside an existing feature.
101
+ ### `make:controller`
90
102
 
91
103
  ```bash
92
- domain-driver make:service <feature> <n>
104
+ domain-driver make:controller users/User
93
105
  ```
94
106
 
95
- ```bash
96
- domain-driver make:service coffee-type CoffeeType
97
- ```
107
+ Node: five controllers plus `<feature>.routes.ts` for Express, Fastify, or Hono. Nest: five single-action controllers. Next.js fullstack: `app/api/<feature>/route.ts` and `app/api/<feature>/[id]/route.ts`. Not available on frontend-only stacks.
98
108
 
99
- Generates:
109
+ ### `make:service` and `make:repository`
100
110
 
111
+ ```bash
112
+ domain-driver make:service users/User [--side client|server|both]
113
+ domain-driver make:repository users/User [--side client|server|both]
101
114
  ```
102
- app/coffee-type/services/
103
- ├── ListCoffeeType.service.ts
104
- ├── ShowCoffeeType.service.ts
105
- ├── CreateCoffeeType.service.ts
106
- ├── UpdateCoffeeType.service.ts
107
- └── DeleteCoffeeType.service.ts
108
- ```
109
-
110
- ---
111
115
 
112
- ### `make:repository`
116
+ `--side` matters on `next-fullstack`, where both sides exist. Default is `both`.
113
117
 
114
- Scaffold a set of single-responsibility repository files inside an existing feature.
118
+ ### `make:schema`
115
119
 
116
120
  ```bash
117
- domain-driver make:repository <feature> <n>
121
+ domain-driver make:schema users/User
118
122
  ```
119
123
 
124
+ Writes `CreateUser.schema.ts` and `UpdateUser.schema.ts`. On Nest it also writes the matching DTO classes derived with `createZodDto` from `nestjs-zod`.
125
+
126
+ ### `make:types`, `make:component`, `make:container`, `make:hook`
127
+
120
128
  ```bash
121
- domain-driver make:repository coffee-type CoffeeType
129
+ domain-driver make:types users/User
130
+ domain-driver make:component users/UserCard [client|server]
131
+ domain-driver make:container users/UserContainer
132
+ domain-driver make:hook users/useUser
122
133
  ```
123
134
 
124
- Generates:
135
+ Component, container, and hook commands fail with a clear message on backend stacks, and `server` components are rejected on React.
125
136
 
126
- ```
127
- app/coffee-type/repositories/
128
- ├── ListCoffeeType.repository.ts
129
- ├── ShowCoffeeType.repository.ts
130
- ├── CreateCoffeeType.repository.ts
131
- ├── UpdateCoffeeType.repository.ts
132
- └── DeleteCoffeeType.repository.ts
137
+ ### `init`
138
+
139
+ ```bash
140
+ domain-driver init
133
141
  ```
134
142
 
135
- ---
143
+ Writes agent guidance into the current project so coding agents scaffold with domain-driver instead of hand-writing layers:
136
144
 
137
- ### `make:schema`
145
+ - `AGENTS.md` and `CLAUDE.md` get a section between `<!-- domain-driver:start -->` and `<!-- domain-driver:end -->`. Existing content outside the markers is never touched; the section is created, refreshed in place, or left alone.
146
+ - `.claude/skills/domain-driver/SKILL.md` is a Claude Code skill owned by the tool.
138
147
 
139
- Scaffold Zod schemas for create and update operations inside an existing feature.
148
+ Running `init` twice reports `unchanged`. Re-run it after upgrading domain-driver.
140
149
 
141
- ```bash
142
- domain-driver make:schema <feature> <n>
143
- ```
150
+ **On install.** A local `npm install domain-driver` in a project runs `init` automatically. It does nothing when `CI` is set, for global installs, when there is no `package.json` in the installing project, or when domain-driver installs itself. Files are written to the directory you ran npm install from, which in a workspace is the repository root. Opt out with `npm install --ignore-scripts`, or delete the marked section afterwards.
144
151
 
145
- ```bash
146
- domain-driver make:schema coffee-type CoffeeType
147
- ```
152
+ ---
148
153
 
149
- Generates:
154
+ ## Example: `make:feature coffee-type -a` on Node with Express
150
155
 
151
156
  ```
152
- app/coffee-type/schemas/
153
- ├── CreateCoffeeType.schema.ts
154
- └── UpdateCoffeeType.schema.ts
157
+ src/features/coffee-type/
158
+ ├── coffee-type.routes.ts
159
+ ├── controllers/
160
+ │ ├── ListCoffeeType.controller.ts
161
+ │ ├── ShowCoffeeType.controller.ts
162
+ │ ├── CreateCoffeeType.controller.ts
163
+ │ ├── UpdateCoffeeType.controller.ts
164
+ │ └── DeleteCoffeeType.controller.ts
165
+ ├── services/ (five files)
166
+ ├── repositories/ (five files, database-agnostic stubs)
167
+ ├── schemas/
168
+ │ ├── CreateCoffeeType.schema.ts
169
+ │ └── UpdateCoffeeType.schema.ts
170
+ └── types/
171
+ └── CoffeeType.types.ts
155
172
  ```
156
173
 
157
- ---
158
-
159
- ## What `make:feature -a` generates
174
+ Mount the routes with `app.use('/coffee-type', coffeeTypeRoutes)`. Then `make:action coffee-type/CoffeeType findActive` adds `FindActive.controller.ts`, `FindActive.service.ts`, `FindActive.repository.ts`, and prints `router.get('/find-active', findActiveController);` for the routes file.
160
175
 
161
- Running `domain-driver make:feature coffee-type -a` creates the full structure:
176
+ ## Example: `make:feature coffee-type -a` on NestJS
162
177
 
163
178
  ```
164
- app/
165
- └── coffee-type/
166
- ├── components/
167
- │ ├── server/
168
- │ └── client/
169
- │ └── CoffeeType.tsx
170
- ├── containers/
171
- │ └── CoffeeTypeContainer.tsx
172
- ├── hooks/
173
- │ └── useCoffeeType.ts
174
- ├── services/
175
- │ ├── ListCoffeeType.service.ts
176
- │ ├── ShowCoffeeType.service.ts
177
- │ ├── CreateCoffeeType.service.ts
178
- │ ├── UpdateCoffeeType.service.ts
179
- │ └── DeleteCoffeeType.service.ts
180
- ├── repositories/
181
- │ ├── ListCoffeeType.repository.ts
182
- │ ├── ShowCoffeeType.repository.ts
183
- │ ├── CreateCoffeeType.repository.ts
184
- │ ├── UpdateCoffeeType.repository.ts
185
- │ └── DeleteCoffeeType.repository.ts
186
- ├── schemas/
187
- │ ├── CreateCoffeeType.schema.ts
188
- │ └── UpdateCoffeeType.schema.ts
189
- └── page.tsx
179
+ src/coffee-type/
180
+ ├── coffee-type.module.ts registers 5 controllers and 10 providers
181
+ ├── controllers/ five @Controller('coffee-type') classes
182
+ ├── services/ five @Injectable() services
183
+ ├── repositories/ five @Injectable() repositories
184
+ ├── dto/
185
+ │ ├── CreateCoffeeType.dto.ts
186
+ │ └── UpdateCoffeeType.dto.ts
187
+ ├── schemas/
188
+ └── types/
190
189
  ```
191
190
 
191
+ Install `nestjs-zod` and register `ZodValidationPipe` as `APP_PIPE` once in your `AppModule`. The tool prints this hint when the package is missing.
192
+
192
193
  ---
193
194
 
194
195
  ## Philosophy
195
196
 
196
- This tool follows a strict **domain-driven** folder structure where everything related to a feature lives together. No more hunting across `/components`, `/hooks`, and `/services` top-level folders.
197
+ Everything for a feature lives in one folder, and every file does one thing.
197
198
 
198
- The layer responsibilities are:
199
+ - **repositories** — data access only. Client-side repositories call your API; server-side repositories call your database.
200
+ - **services** — business logic, one class per action.
201
+ - **controllers** — HTTP in, service call, HTTP out, one file per action.
202
+ - **hooks** — React state and side effects, calls services.
203
+ - **containers** — wire hooks into UI.
204
+ - **components** — presentational UI.
205
+ - **schemas** — Zod validation for create and update; the update body never carries the id, it comes from the path.
206
+ - **types** — the entity interface.
199
207
 
200
- - **repositories** — data fetching only, no business logic
201
- - **services** — business logic, calls repositories
202
- - **hooks** — React state and side effects, calls services
203
- - **containers** — wire hooks into UI, no direct data fetching
204
- - **components** — pure presentational UI, no data dependencies
205
- - **schemas** — Zod validation for create and update operations
208
+ Server-side repositories throw a clear not-implemented error until you wire your ORM. Compiling code that fails loudly beats a fake store that looks like it works.
206
209
 
207
210
  ---
208
211
 
209
- ## Requirements
212
+ ## Upgrading from 0.1.0
210
213
 
211
- - Node.js 18+
214
+ Every layer command now takes a single `<feature>/<Name>` target instead of separate feature and name arguments, for example `make:schema users User` becomes `make:schema users/User`. `make:feature users -a` still works and names the entity `Users`; write `users/User` if you want a different entity name.
212
215
 
213
216
  ---
214
217
 
215
- ## Framework Support
218
+ ## Requirements
216
219
 
217
- This tool is optimised for **Next.js App Router** projects. All files are scaffolded into the `app/` directory following Next.js conventions (`page.tsx`, server/client component separation, etc.).
220
+ - Node.js 18+
218
221
 
219
- If no `app/` directory exists, it will be created automatically. This means the tool can also be used in any project where an `app/<feature>` folder structure makes sense.
222
+ ---
220
223
 
221
224
  ## Local Development
222
225
 
223
226
  ```bash
224
- # Clone the repo
225
227
  git clone https://github.com/IsaacHatilima/domain-driver
226
228
  cd domain-driver
227
-
228
- # Install dependencies
229
229
  npm install
230
-
231
- # Build
232
230
  npm run build
233
-
234
- # Link globally for local testing
235
231
  npm link
236
-
237
- # Test it
238
- domain-driver make:feature test-feature
239
- domain-driver make:feature test-feature -a
232
+ npm test
233
+ npm run test:coverage
240
234
  ```
241
235
 
242
236
  ---
243
237
 
244
238
  ## Roadmap
245
239
 
246
- - [x] `make:feature` — scaffold feature folder structure
247
- - [x] `make:feature -a` — scaffold feature with all files
248
- - [x] `make:component` — scaffold a component
249
- - [x] `make:container` — scaffold a container
250
- - [x] `make:hook` — scaffold a custom hook
251
- - [x] `make:service` — scaffold single-responsibility services
252
- - [x] `make:repository` — scaffold single-responsibility repositories
253
- - [x] `make:schema` — scaffold Zod schemas
254
- - [ ] Interactive mode — prompt for name if not provided
255
- - [ ] Config file — customize folder structure per project
240
+ - [x] Stack detection for Next.js, React, Node, and NestJS
241
+ - [x] `make:controller` with Express, Fastify, Hono, Nest, and Next route handlers
242
+ - [x] Per-action files in every layer
243
+ - [x] Bespoke actions with make:action
244
+ - [x] Agent guidance with init and a guarded postinstall
245
+ - [ ] Config file — override stack and feature root per project
246
+ - [ ] Configurable API base URL for client repositories
247
+ - [ ] ORM-aware server repositories
256
248
 
257
249
  ---
258
250
 
259
251
  ## License
260
252
 
261
- MIT
253
+ MIT
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.parseReturns = parseReturns;
37
+ exports.makeAction = makeAction;
38
+ const path = __importStar(require("path"));
39
+ const registry_1 = require("../stack/registry");
40
+ const actions_1 = require("../templates/actions");
41
+ const server_repository_1 = require("../templates/backend/server-repository");
42
+ const nest_1 = require("../templates/controllers/nest");
43
+ const next_action_route_1 = require("../templates/controllers/next-action-route");
44
+ const node_1 = require("../templates/controllers/node");
45
+ const client_repository_1 = require("../templates/frontend/client-repository");
46
+ const dto_1 = require("../templates/nest/dto");
47
+ const service_1 = require("../templates/service");
48
+ const schema_1 = require("../templates/shared/schema");
49
+ const fs_1 = require("../utils/fs");
50
+ const naming_1 = require("../utils/naming");
51
+ const paths_1 = require("../utils/paths");
52
+ const hints_1 = require("./hints");
53
+ const resolve_1 = require("./resolve");
54
+ const write_1 = require("./write");
55
+ function parseReturns(value) {
56
+ if (actions_1.RETURN_KINDS.includes(value))
57
+ return value;
58
+ throw new Error(`Invalid --returns "${value}". Use list, one, or void.`);
59
+ }
60
+ function makeAction(feature, entity, actionName, options) {
61
+ const ctx = (0, resolve_1.requireFeature)(feature);
62
+ const spec = (0, actions_1.customAction)(entity, actionName, options);
63
+ hintMissingTypes(ctx, entity);
64
+ let wroteAny = false;
65
+ if (spec.schema !== null)
66
+ wroteAny = writeInput(ctx, spec) || wroteAny;
67
+ if ((0, registry_1.hasLayer)(ctx.profile, 'serverRepository'))
68
+ wroteAny = writeSide(ctx, spec, entity, 'server') || wroteAny;
69
+ if ((0, registry_1.hasLayer)(ctx.profile, 'controller'))
70
+ wroteAny = writeController(ctx, spec, entity) || wroteAny;
71
+ if ((0, registry_1.hasLayer)(ctx.profile, 'clientRepository'))
72
+ wroteAny = writeSide(ctx, spec, entity, 'client') || wroteAny;
73
+ if (wroteAny)
74
+ console.log(`✅ Action "${spec.name}" scaffolded in "${feature}"`);
75
+ return wroteAny;
76
+ }
77
+ function hintMissingTypes(ctx, entity) {
78
+ const typesFile = path.join(ctx.featureDir, (0, registry_1.layerDir)(ctx.profile, 'types'), `${entity}.types.ts`);
79
+ if ((0, fs_1.fileExists)(typesFile))
80
+ return;
81
+ console.log(`â„šī¸ types/${entity}.types.ts not found. Run: domain-driver make:types ${ctx.feature}/${entity}`);
82
+ }
83
+ function writeInput(ctx, spec) {
84
+ const schemaFile = path.join((0, resolve_1.ensureLayerDir)(ctx, 'schema'), `${spec.name}.schema.ts`);
85
+ const wroteSchema = (0, write_1.writeIfAbsent)(schemaFile, () => (0, schema_1.renderSchema)(spec.name, (0, naming_1.lowerFirst)(spec.name)));
86
+ if (!(0, registry_1.hasLayer)(ctx.profile, 'dto'))
87
+ return wroteSchema;
88
+ const dtoFile = path.join((0, resolve_1.ensureLayerDir)(ctx, 'dto'), `${spec.name}.dto.ts`);
89
+ const wroteDto = (0, write_1.writeIfAbsent)(dtoFile, () => (0, dto_1.renderDto)(ctx, spec.name, dtoFile));
90
+ (0, hints_1.hintNestjsZod)(ctx.stack);
91
+ return wroteSchema || wroteDto;
92
+ }
93
+ function writeSide(ctx, spec, entity, side) {
94
+ const repositoryLayer = side === 'client' ? 'clientRepository' : 'serverRepository';
95
+ const serviceLayer = side === 'client' ? 'clientService' : 'serverService';
96
+ const renderRepository = side === 'client' ? client_repository_1.renderClientRepository : server_repository_1.renderServerRepository;
97
+ const repositoryFile = path.join((0, resolve_1.ensureLayerDir)(ctx, repositoryLayer), `${spec.name}.repository.ts`);
98
+ const wroteRepository = (0, write_1.writeIfAbsent)(repositoryFile, () => renderRepository(ctx, spec, entity, repositoryFile));
99
+ const serviceFile = path.join((0, resolve_1.ensureLayerDir)(ctx, serviceLayer), `${spec.name}.service.ts`);
100
+ const wroteService = (0, write_1.writeIfAbsent)(serviceFile, () => (0, service_1.renderService)(ctx, spec, entity, serviceFile, side));
101
+ return wroteRepository || wroteService;
102
+ }
103
+ function writeController(ctx, spec, entity) {
104
+ if (ctx.profile.name === 'next-fullstack') {
105
+ const routeDir = path.join((0, paths_1.apiRouteDir)(ctx.stack, ctx.feature), spec.path.slice(1));
106
+ (0, fs_1.mkdirSafe)(routeDir);
107
+ const routeFile = path.join(routeDir, 'route.ts');
108
+ return (0, write_1.writeIfAbsent)(routeFile, () => (0, next_action_route_1.renderActionRoute)(ctx, spec, routeFile));
109
+ }
110
+ const controllerFile = path.join((0, resolve_1.ensureLayerDir)(ctx, 'controller'), `${spec.name}.controller.ts`);
111
+ const render = ctx.profile.name === 'nest' ? nest_1.renderNestController : node_1.renderNodeController;
112
+ const wroteController = (0, write_1.writeIfAbsent)(controllerFile, () => render(ctx, spec, entity, controllerFile));
113
+ const line = ctx.profile.name === 'node' ? (0, node_1.renderNodeRouteLine)(ctx, spec, entity) : null;
114
+ if (wroteController && line !== null) {
115
+ console.log(`â„šī¸ Add to ${ctx.feature}.routes.ts above the '/:id' routes: ${line}`);
116
+ }
117
+ return wroteController;
118
+ }
@@ -33,30 +33,29 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.parseComponentType = parseComponentType;
36
37
  exports.makeComponent = makeComponent;
37
38
  const path = __importStar(require("path"));
38
- const utils_1 = require("../utils");
39
- function renderComponent(name, type) {
40
- const directive = type === 'client' ? "'use client';\n\n" : '';
41
- return `${directive}interface ${name}Props {
42
- id: string;
43
- }
44
-
45
- export default function ${name}({ id }: ${name}Props) {
46
- return (
47
- <div>
48
- <h1>${name}</h1>
49
- </div>
50
- );
51
- }
52
- `;
39
+ const registry_1 = require("../stack/registry");
40
+ const component_1 = require("../templates/frontend/component");
41
+ const fs_1 = require("../utils/fs");
42
+ const resolve_1 = require("./resolve");
43
+ function parseComponentType(value) {
44
+ return value === 'server' ? 'server' : 'client';
53
45
  }
54
46
  function makeComponent(feature, name, type = 'client') {
55
- const base = (0, utils_1.ensureFeatureExists)(feature, `components/${type}`);
56
- const filePath = path.join(base, `${name}.tsx`);
57
- if ((0, utils_1.fileExists)(filePath)) {
47
+ const ctx = (0, resolve_1.requireFeature)(feature);
48
+ (0, registry_1.assertLayer)(ctx.profile, 'component', 'make:component');
49
+ if (type === 'server' && !ctx.profile.serverComponents) {
50
+ throw new Error(`The ${ctx.profile.name} stack has no server components.`);
51
+ }
52
+ const dir = path.join(ctx.featureDir, (0, registry_1.componentDir)(ctx.profile, type));
53
+ (0, fs_1.mkdirSafe)(dir);
54
+ const filePath = path.join(dir, `${name}.tsx`);
55
+ if ((0, fs_1.fileExists)(filePath)) {
58
56
  throw new Error(`Component "${name}" already exists at ${filePath}`);
59
57
  }
60
- (0, utils_1.writeFileSafe)(filePath, renderComponent(name, type));
58
+ const directive = type === 'client' && ctx.profile.clientDirective;
59
+ (0, fs_1.writeFileSafe)(filePath, (0, component_1.renderComponent)(name, directive));
61
60
  console.log(`✅ Component "${name}" created at ${filePath}`);
62
61
  }
@@ -35,39 +35,18 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.makeContainer = makeContainer;
37
37
  const path = __importStar(require("path"));
38
- const utils_1 = require("../utils");
39
- function renderContainer(containerName, name, feature) {
40
- const hookName = `use${name}`;
41
- const hookPath = (0, utils_1.resolveImport)(feature, `hooks/${hookName}`);
42
- const componentPath = (0, utils_1.resolveImport)(feature, `components/client/${name}`);
43
- return `'use client';
44
-
45
- import { ${hookName} } from '${hookPath}';
46
- import ${name} from '${componentPath}';
47
-
48
- export default function ${containerName}() {
49
- const { items, loading, error } = ${hookName}();
50
-
51
- if (loading) return <div>Loading...</div>;
52
- if (error) return <div>Error: {error}</div>;
53
-
54
- return (
55
- <div>
56
- {items.map((item) => (
57
- <${name} key={item.id} {...item} />
58
- ))}
59
- </div>
60
- );
61
- }
62
- `;
63
- }
38
+ const registry_1 = require("../stack/registry");
39
+ const container_1 = require("../templates/frontend/container");
40
+ const fs_1 = require("../utils/fs");
41
+ const resolve_1 = require("./resolve");
64
42
  function makeContainer(feature, name, pascalName) {
65
- const base = (0, utils_1.ensureFeatureExists)(feature, 'containers');
66
- const filePath = path.join(base, `${name}.tsx`);
67
- if ((0, utils_1.fileExists)(filePath)) {
43
+ const ctx = (0, resolve_1.requireFeature)(feature);
44
+ (0, registry_1.assertLayer)(ctx.profile, 'container', 'make:container');
45
+ const filePath = path.join((0, resolve_1.ensureLayerDir)(ctx, 'container'), `${name}.tsx`);
46
+ if ((0, fs_1.fileExists)(filePath)) {
68
47
  throw new Error(`Container "${name}" already exists at ${filePath}`);
69
48
  }
70
- const entityName = pascalName ?? name.replace(/Container$/, '');
71
- (0, utils_1.writeFileSafe)(filePath, renderContainer(name, entityName, feature));
49
+ const entity = pascalName ?? name.replace(/Container$/, '');
50
+ (0, fs_1.writeFileSafe)(filePath, (0, container_1.renderContainer)(ctx, name, entity, filePath));
72
51
  console.log(`✅ Container "${name}" created at ${filePath}`);
73
52
  }