domain-driver 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +173 -28
- package/dist/commands/component.js +63 -0
- package/dist/commands/container.js +66 -0
- package/dist/commands/feature.js +30 -6
- package/dist/commands/hook.js +62 -0
- package/dist/commands/repository.js +107 -0
- package/dist/commands/schema.js +78 -0
- package/dist/commands/service.js +94 -0
- package/dist/index.js +60 -9
- package/package.json +1 -1
- package/src/commands/component.ts +36 -0
- package/src/commands/container.ts +36 -0
- package/src/commands/feature.ts +33 -7
- package/src/commands/hook.ts +32 -0
- package/src/commands/repository.ts +81 -0
- package/src/commands/schema.ts +52 -0
- package/src/commands/service.ts +68 -0
- package/src/index.ts +67 -8
package/README.md
CHANGED
|
@@ -13,47 +13,182 @@ npm install -g domain-driver
|
|
|
13
13
|
Or use without installing:
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
npx domain-driver make:feature <
|
|
16
|
+
npx domain-driver make:feature <n>
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
21
|
-
##
|
|
21
|
+
## Commands
|
|
22
|
+
|
|
23
|
+
### `make:feature`
|
|
24
|
+
|
|
25
|
+
Scaffold a full feature folder structure.
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
domain-driver make:feature <n>
|
|
29
|
+
domain-driver make:feature <n> -a
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The `-a` flag scaffolds all files inside each folder automatically.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
domain-driver make:feature coffee-type # folders + .gitkeep only
|
|
36
|
+
domain-driver make:feature coffee-type -a # folders + all files
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
### `make:component`
|
|
42
|
+
|
|
43
|
+
Scaffold a component inside an existing feature. Defaults to `client` if no type is specified.
|
|
44
|
+
|
|
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
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
### `make:container`
|
|
60
|
+
|
|
61
|
+
Scaffold a smart container component inside an existing feature.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
domain-driver make:container <feature> <n>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
domain-driver make:container coffee-type CoffeeTypeContainer
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
### `make:hook`
|
|
74
|
+
|
|
75
|
+
Scaffold a custom hook inside an existing feature.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
domain-driver make:hook <feature> <n>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
domain-driver make:hook coffee-type useCoffeeType
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
### `make:service`
|
|
88
|
+
|
|
89
|
+
Scaffold a set of single-responsibility service files inside an existing feature.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
domain-driver make:service <feature> <n>
|
|
93
|
+
```
|
|
22
94
|
|
|
23
95
|
```bash
|
|
24
|
-
domain-driver make:
|
|
96
|
+
domain-driver make:service coffee-type CoffeeType
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Generates:
|
|
100
|
+
|
|
25
101
|
```
|
|
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
|
+
|
|
112
|
+
### `make:repository`
|
|
113
|
+
|
|
114
|
+
Scaffold a set of single-responsibility repository files inside an existing feature.
|
|
26
115
|
|
|
27
|
-
|
|
116
|
+
```bash
|
|
117
|
+
domain-driver make:repository <feature> <n>
|
|
118
|
+
```
|
|
28
119
|
|
|
29
120
|
```bash
|
|
30
|
-
domain-driver make:
|
|
31
|
-
|
|
32
|
-
|
|
121
|
+
domain-driver make:repository coffee-type CoffeeType
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Generates:
|
|
125
|
+
|
|
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
|
|
33
133
|
```
|
|
34
134
|
|
|
35
135
|
---
|
|
36
136
|
|
|
37
|
-
|
|
137
|
+
### `make:schema`
|
|
138
|
+
|
|
139
|
+
Scaffold Zod schemas for create and update operations inside an existing feature.
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
domain-driver make:schema <feature> <n>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
domain-driver make:schema coffee-type CoffeeType
|
|
147
|
+
```
|
|
38
148
|
|
|
39
|
-
|
|
149
|
+
Generates:
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
app/coffee-type/schemas/
|
|
153
|
+
├── CreateCoffeeType.schema.ts
|
|
154
|
+
└── UpdateCoffeeType.schema.ts
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## What `make:feature -a` generates
|
|
160
|
+
|
|
161
|
+
Running `domain-driver make:feature coffee-type -a` creates the full structure:
|
|
40
162
|
|
|
41
163
|
```
|
|
42
164
|
app/
|
|
43
|
-
└──
|
|
165
|
+
└── coffee-type/
|
|
44
166
|
├── components/
|
|
45
|
-
│ ├── server/
|
|
46
|
-
│ └── client/
|
|
47
|
-
|
|
48
|
-
├──
|
|
49
|
-
|
|
50
|
-
├──
|
|
51
|
-
|
|
52
|
-
|
|
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
|
|
53
190
|
```
|
|
54
191
|
|
|
55
|
-
Each folder includes a `.gitkeep` file so empty directories are tracked in Git.
|
|
56
|
-
|
|
57
192
|
---
|
|
58
193
|
|
|
59
194
|
## Philosophy
|
|
@@ -67,22 +202,27 @@ The layer responsibilities are:
|
|
|
67
202
|
- **hooks** — React state and side effects, calls services
|
|
68
203
|
- **containers** — wire hooks into UI, no direct data fetching
|
|
69
204
|
- **components** — pure presentational UI, no data dependencies
|
|
70
|
-
- **schemas** — Zod validation for
|
|
205
|
+
- **schemas** — Zod validation for create and update operations
|
|
71
206
|
|
|
72
207
|
---
|
|
73
208
|
|
|
74
209
|
## Requirements
|
|
75
210
|
|
|
76
211
|
- Node.js 18+
|
|
77
|
-
- A Next.js project with an `app/` directory (App Router)
|
|
78
212
|
|
|
79
213
|
---
|
|
80
214
|
|
|
215
|
+
## Framework Support
|
|
216
|
+
|
|
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.).
|
|
218
|
+
|
|
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.
|
|
220
|
+
|
|
81
221
|
## Local Development
|
|
82
222
|
|
|
83
223
|
```bash
|
|
84
224
|
# Clone the repo
|
|
85
|
-
git clone https://github.com/
|
|
225
|
+
git clone https://github.com/IsaacHatilima/domain-driver
|
|
86
226
|
cd domain-driver
|
|
87
227
|
|
|
88
228
|
# Install dependencies
|
|
@@ -96,17 +236,22 @@ npm link
|
|
|
96
236
|
|
|
97
237
|
# Test it
|
|
98
238
|
domain-driver make:feature test-feature
|
|
239
|
+
domain-driver make:feature test-feature -a
|
|
99
240
|
```
|
|
100
241
|
|
|
101
242
|
---
|
|
102
243
|
|
|
103
244
|
## Roadmap
|
|
104
245
|
|
|
105
|
-
- [
|
|
106
|
-
- [
|
|
107
|
-
- [
|
|
108
|
-
- [
|
|
109
|
-
- [
|
|
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
|
|
110
255
|
- [ ] Config file — customize folder structure per project
|
|
111
256
|
|
|
112
257
|
---
|
|
@@ -0,0 +1,63 @@
|
|
|
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.makeComponent = makeComponent;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
function renderComponent(name, type) {
|
|
40
|
+
const directive = type === 'client' ? "'use client';\n\n" : '';
|
|
41
|
+
return `${directive}export default function ${name}() {
|
|
42
|
+
return (
|
|
43
|
+
<div>
|
|
44
|
+
<h1>${name}</h1>
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
`;
|
|
49
|
+
}
|
|
50
|
+
function makeComponent(feature, name, type = 'client') {
|
|
51
|
+
const base = path.join(process.cwd(), 'app', feature, 'components', type);
|
|
52
|
+
if (!fs.existsSync(base)) {
|
|
53
|
+
console.error(`❌ Feature "${feature}" does not exist. Run make:feature ${feature} first.`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
const filePath = path.join(base, `${name}.tsx`);
|
|
57
|
+
if (fs.existsSync(filePath)) {
|
|
58
|
+
console.error(`❌ Component "${name}" already exists at ${filePath}`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
fs.writeFileSync(filePath, renderComponent(name, type));
|
|
62
|
+
console.log(`✅ Component "${name}" created at ${filePath}`);
|
|
63
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
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.makeContainer = makeContainer;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
function renderContainer(name) {
|
|
40
|
+
return `'use client';
|
|
41
|
+
|
|
42
|
+
interface Props {}
|
|
43
|
+
|
|
44
|
+
export default function ${name}({ }: Props) {
|
|
45
|
+
return (
|
|
46
|
+
<div>
|
|
47
|
+
<h1>${name}</h1>
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
`;
|
|
52
|
+
}
|
|
53
|
+
function makeContainer(feature, name) {
|
|
54
|
+
const base = path.join(process.cwd(), 'app', feature, 'containers');
|
|
55
|
+
if (!fs.existsSync(base)) {
|
|
56
|
+
console.error(`❌ Feature "${feature}" does not exist. Run make:feature ${feature} first.`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
const filePath = path.join(base, `${name}.tsx`);
|
|
60
|
+
if (fs.existsSync(filePath)) {
|
|
61
|
+
console.error(`❌ Container "${name}" already exists at ${filePath}`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
fs.writeFileSync(filePath, renderContainer(name));
|
|
65
|
+
console.log(`✅ Container "${name}" created at ${filePath}`);
|
|
66
|
+
}
|
package/dist/commands/feature.js
CHANGED
|
@@ -36,6 +36,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.makeFeature = makeFeature;
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
|
+
const component_1 = require("./component");
|
|
40
|
+
const hook_1 = require("./hook");
|
|
41
|
+
const service_1 = require("./service");
|
|
42
|
+
const repository_1 = require("./repository");
|
|
43
|
+
const schema_1 = require("./schema");
|
|
44
|
+
const container_1 = require("./container");
|
|
39
45
|
const FEATURE_DIRS = [
|
|
40
46
|
'components/server',
|
|
41
47
|
'components/client',
|
|
@@ -45,11 +51,14 @@ const FEATURE_DIRS = [
|
|
|
45
51
|
'repositories',
|
|
46
52
|
'schemas',
|
|
47
53
|
];
|
|
48
|
-
function
|
|
49
|
-
|
|
54
|
+
function toPascalCase(name) {
|
|
55
|
+
return name
|
|
50
56
|
.split('-')
|
|
51
57
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
52
58
|
.join('');
|
|
59
|
+
}
|
|
60
|
+
function renderPage(name) {
|
|
61
|
+
const componentName = toPascalCase(name);
|
|
53
62
|
return `export default function ${componentName}Page() {
|
|
54
63
|
return (
|
|
55
64
|
<div>
|
|
@@ -59,13 +68,28 @@ function renderTemplate(name) {
|
|
|
59
68
|
}
|
|
60
69
|
`;
|
|
61
70
|
}
|
|
62
|
-
async function makeFeature(name) {
|
|
71
|
+
async function makeFeature(name, all = false) {
|
|
63
72
|
const base = path.join(process.cwd(), 'app', name);
|
|
73
|
+
const pascalName = toPascalCase(name);
|
|
74
|
+
if (fs.existsSync(base)) {
|
|
75
|
+
console.error(`❌ Feature "${name}" already exists at ${base}`);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
64
78
|
for (const dir of FEATURE_DIRS) {
|
|
65
79
|
fs.mkdirSync(path.join(base, dir), { recursive: true });
|
|
66
|
-
|
|
80
|
+
if (!all) {
|
|
81
|
+
fs.writeFileSync(path.join(base, dir, '.gitkeep'), '');
|
|
82
|
+
}
|
|
67
83
|
}
|
|
68
|
-
|
|
69
|
-
fs.writeFileSync(path.join(base, 'page.tsx'), page);
|
|
84
|
+
fs.writeFileSync(path.join(base, 'page.tsx'), renderPage(name));
|
|
70
85
|
console.log(`✅ Feature "${name}" scaffolded at ${base}`);
|
|
86
|
+
if (all) {
|
|
87
|
+
(0, component_1.makeComponent)(name, pascalName, 'client');
|
|
88
|
+
(0, container_1.makeContainer)(name, `${pascalName}Container`);
|
|
89
|
+
(0, hook_1.makeHook)(name, `use${pascalName}`);
|
|
90
|
+
(0, service_1.makeService)(name, pascalName);
|
|
91
|
+
(0, repository_1.makeRepository)(name, `${pascalName}Repository`);
|
|
92
|
+
(0, schema_1.makeSchema)(name, `${pascalName}Schema`);
|
|
93
|
+
console.log(`✅ All files scaffolded for "${name}"`);
|
|
94
|
+
}
|
|
71
95
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
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.makeHook = makeHook;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
function renderHook(name) {
|
|
40
|
+
return `import { useState } from 'react';
|
|
41
|
+
|
|
42
|
+
export function ${name}() {
|
|
43
|
+
const [data, setData] = useState(null);
|
|
44
|
+
|
|
45
|
+
return { data };
|
|
46
|
+
}
|
|
47
|
+
`;
|
|
48
|
+
}
|
|
49
|
+
function makeHook(feature, name) {
|
|
50
|
+
const base = path.join(process.cwd(), 'app', feature, 'hooks');
|
|
51
|
+
if (!fs.existsSync(base)) {
|
|
52
|
+
console.error(`❌ Feature "${feature}" does not exist. Run make:feature ${feature} first.`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
const filePath = path.join(base, `${name}.ts`);
|
|
56
|
+
if (fs.existsSync(filePath)) {
|
|
57
|
+
console.error(`❌ Hook "${name}" already exists at ${filePath}`);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
fs.writeFileSync(filePath, renderHook(name));
|
|
61
|
+
console.log(`✅ Hook "${name}" created at ${filePath}`);
|
|
62
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
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.makeRepository = makeRepository;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const REPOSITORY_ACTIONS = ['List', 'Create', 'Update', 'Delete', 'Show'];
|
|
40
|
+
function renderRepository(action, name, feature) {
|
|
41
|
+
switch (action) {
|
|
42
|
+
case 'List':
|
|
43
|
+
return `export class List${name}Repository {
|
|
44
|
+
async handle() {
|
|
45
|
+
const response = await fetch('/api/${feature}');
|
|
46
|
+
return response.json();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
`;
|
|
50
|
+
case 'Show':
|
|
51
|
+
return `export class Show${name}Repository {
|
|
52
|
+
async handle(id: string) {
|
|
53
|
+
const response = await fetch(\`/api/${feature}/\${id}\`);
|
|
54
|
+
return response.json();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
`;
|
|
58
|
+
case 'Create':
|
|
59
|
+
return `export class Create${name}Repository {
|
|
60
|
+
async handle(data: unknown) {
|
|
61
|
+
const response = await fetch('/api/${feature}', {
|
|
62
|
+
method: 'POST',
|
|
63
|
+
body: JSON.stringify(data),
|
|
64
|
+
});
|
|
65
|
+
return response.json();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
`;
|
|
69
|
+
case 'Update':
|
|
70
|
+
return `export class Update${name}Repository {
|
|
71
|
+
async handle(id: string, data: unknown) {
|
|
72
|
+
const response = await fetch(\`/api/${feature}/\${id}\`, {
|
|
73
|
+
method: 'PUT',
|
|
74
|
+
body: JSON.stringify(data),
|
|
75
|
+
});
|
|
76
|
+
return response.json();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
`;
|
|
80
|
+
case 'Delete':
|
|
81
|
+
return `export class Delete${name}Repository {
|
|
82
|
+
async handle(id: string) {
|
|
83
|
+
const response = await fetch(\`/api/${feature}/\${id}\`, {
|
|
84
|
+
method: 'DELETE',
|
|
85
|
+
});
|
|
86
|
+
return response.json();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function makeRepository(feature, name) {
|
|
93
|
+
const base = path.join(process.cwd(), 'app', feature, 'repositories');
|
|
94
|
+
if (!fs.existsSync(base)) {
|
|
95
|
+
console.error(`❌ Feature "${feature}" does not exist. Run make:feature ${feature} first.`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
for (const action of REPOSITORY_ACTIONS) {
|
|
99
|
+
const filePath = path.join(base, `${action}${name}.repository.ts`);
|
|
100
|
+
if (fs.existsSync(filePath)) {
|
|
101
|
+
console.warn(`⚠️ Skipping "${action}${name}.repository.ts" — already exists`);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
fs.writeFileSync(filePath, renderRepository(action, name, feature));
|
|
105
|
+
}
|
|
106
|
+
console.log(`✅ Repositories for "${name}" created at ${base}`);
|
|
107
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
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.makeSchema = makeSchema;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const SCHEMA_ACTIONS = ['Create', 'Update'];
|
|
40
|
+
function renderSchema(action, name) {
|
|
41
|
+
switch (action) {
|
|
42
|
+
case 'Create':
|
|
43
|
+
return `import { z } from 'zod';
|
|
44
|
+
|
|
45
|
+
export const Create${name}Schema = z.object({
|
|
46
|
+
// add create fields here
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
export type Create${name} = z.infer<typeof Create${name}Schema>;
|
|
50
|
+
`;
|
|
51
|
+
case 'Update':
|
|
52
|
+
return `import { z } from 'zod';
|
|
53
|
+
|
|
54
|
+
export const Update${name}Schema = z.object({
|
|
55
|
+
id: z.string(),
|
|
56
|
+
// add update fields here
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
export type Update${name} = z.infer<typeof Update${name}Schema>;
|
|
60
|
+
`;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function makeSchema(feature, name) {
|
|
64
|
+
const base = path.join(process.cwd(), 'app', feature, 'schemas');
|
|
65
|
+
if (!fs.existsSync(base)) {
|
|
66
|
+
console.error(`❌ Feature "${feature}" does not exist. Run make:feature ${feature} first.`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
for (const action of SCHEMA_ACTIONS) {
|
|
70
|
+
const filePath = path.join(base, `${action}${name}.schema.ts`);
|
|
71
|
+
if (fs.existsSync(filePath)) {
|
|
72
|
+
console.warn(`⚠️ Skipping "${action}${name}.schema.ts" — already exists`);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
fs.writeFileSync(filePath, renderSchema(action, name));
|
|
76
|
+
}
|
|
77
|
+
console.log(`✅ Schemas for "${name}" created at ${base}`);
|
|
78
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
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.makeService = makeService;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const SERVICE_ACTIONS = ['List', 'Create', 'Update', 'Delete', 'Show'];
|
|
40
|
+
function renderService(action, name) {
|
|
41
|
+
switch (action) {
|
|
42
|
+
case 'List':
|
|
43
|
+
return `export class List${name}Service {
|
|
44
|
+
async handle() {
|
|
45
|
+
// fetch all ${name}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
`;
|
|
49
|
+
case 'Show':
|
|
50
|
+
return `export class Show${name}Service {
|
|
51
|
+
async handle(id: string) {
|
|
52
|
+
// fetch single ${name}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
`;
|
|
56
|
+
case 'Create':
|
|
57
|
+
return `export class Create${name}Service {
|
|
58
|
+
async handle(data: unknown) {
|
|
59
|
+
// create ${name}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
`;
|
|
63
|
+
case 'Update':
|
|
64
|
+
return `export class Update${name}Service {
|
|
65
|
+
async handle(id: string, data: unknown) {
|
|
66
|
+
// update ${name}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
`;
|
|
70
|
+
case 'Delete':
|
|
71
|
+
return `export class Delete${name}Service {
|
|
72
|
+
async handle(id: string) {
|
|
73
|
+
// delete ${name}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
`;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function makeService(feature, name) {
|
|
80
|
+
const base = path.join(process.cwd(), 'app', feature, 'services');
|
|
81
|
+
if (!fs.existsSync(base)) {
|
|
82
|
+
console.error(`❌ Feature "${feature}" does not exist. Run make:feature ${feature} first.`);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
for (const action of SERVICE_ACTIONS) {
|
|
86
|
+
const filePath = path.join(base, `${action}${name}.service.ts`);
|
|
87
|
+
if (fs.existsSync(filePath)) {
|
|
88
|
+
console.warn(`⚠️ Skipping "${action}${name}.service.ts" — already exists`);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
fs.writeFileSync(filePath, renderService(action, name));
|
|
92
|
+
}
|
|
93
|
+
console.log(`✅ Services for "${name}" created at ${base}`);
|
|
94
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -2,15 +2,66 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
const feature_1 = require("./commands/feature");
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
const component_1 = require("./commands/component");
|
|
6
|
+
const hook_1 = require("./commands/hook");
|
|
7
|
+
const service_1 = require("./commands/service");
|
|
8
|
+
const schema_1 = require("./commands/schema");
|
|
9
|
+
const repository_1 = require("./commands/repository");
|
|
10
|
+
const container_1 = require("./commands/container");
|
|
11
|
+
const [, , command, feature, name] = process.argv;
|
|
12
|
+
if (!command || !feature) {
|
|
13
|
+
console.error('Usage: domain-driver <command> [options]');
|
|
8
14
|
process.exit(1);
|
|
9
15
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
switch (command) {
|
|
17
|
+
case 'make:feature':
|
|
18
|
+
const all = process.argv.includes('-a') || process.argv.includes('-A');
|
|
19
|
+
(0, feature_1.makeFeature)(feature, all).then(() => { });
|
|
20
|
+
break;
|
|
21
|
+
case 'make:component':
|
|
22
|
+
if (!name) {
|
|
23
|
+
console.error('Usage: domain-driver make:component <feature> <name> [client|server]');
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
const type = process.argv[5] || 'client';
|
|
27
|
+
(0, component_1.makeComponent)(feature, name, type);
|
|
28
|
+
break;
|
|
29
|
+
case 'make:hook':
|
|
30
|
+
if (!name) {
|
|
31
|
+
console.error('Usage: domain-driver make:hook <feature> <name>');
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
(0, hook_1.makeHook)(feature, name);
|
|
35
|
+
break;
|
|
36
|
+
case 'make:service':
|
|
37
|
+
if (!name) {
|
|
38
|
+
console.error('Usage: domain-driver make:service <feature> <name>');
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
(0, service_1.makeService)(feature, name);
|
|
42
|
+
break;
|
|
43
|
+
case 'make:schema':
|
|
44
|
+
if (!name) {
|
|
45
|
+
console.error('Usage: domain-driver make:schema <feature> <name>');
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
(0, schema_1.makeSchema)(feature, name);
|
|
49
|
+
break;
|
|
50
|
+
case 'make:repository':
|
|
51
|
+
if (!name) {
|
|
52
|
+
console.error('Usage: domain-driver make:repository <feature> <name>');
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
(0, repository_1.makeRepository)(feature, name);
|
|
56
|
+
break;
|
|
57
|
+
case 'make:container':
|
|
58
|
+
if (!name) {
|
|
59
|
+
console.error('Usage: domain-driver make:container <feature> <name>');
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
(0, container_1.makeContainer)(feature, name);
|
|
63
|
+
break;
|
|
64
|
+
default:
|
|
65
|
+
console.error(`Unknown command: ${command}`);
|
|
66
|
+
process.exit(1);
|
|
16
67
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
type ComponentType = 'client' | 'server';
|
|
5
|
+
|
|
6
|
+
function renderComponent(name: string, type: ComponentType): string {
|
|
7
|
+
const directive = type === 'client' ? "'use client';\n\n" : '';
|
|
8
|
+
|
|
9
|
+
return `${directive}export default function ${name}() {
|
|
10
|
+
return (
|
|
11
|
+
<div>
|
|
12
|
+
<h1>${name}</h1>
|
|
13
|
+
</div>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function makeComponent(feature: string, name: string, type: ComponentType = 'client') {
|
|
20
|
+
const base = path.join(process.cwd(), 'app', feature, 'components', type);
|
|
21
|
+
|
|
22
|
+
if (!fs.existsSync(base)) {
|
|
23
|
+
console.error(`❌ Feature "${feature}" does not exist. Run make:feature ${feature} first.`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const filePath = path.join(base, `${name}.tsx`);
|
|
28
|
+
|
|
29
|
+
if (fs.existsSync(filePath)) {
|
|
30
|
+
console.error(`❌ Component "${name}" already exists at ${filePath}`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
fs.writeFileSync(filePath, renderComponent(name, type));
|
|
35
|
+
console.log(`✅ Component "${name}" created at ${filePath}`);
|
|
36
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
function renderContainer(name: string): string {
|
|
5
|
+
return `'use client';
|
|
6
|
+
|
|
7
|
+
interface Props {}
|
|
8
|
+
|
|
9
|
+
export default function ${name}({ }: Props) {
|
|
10
|
+
return (
|
|
11
|
+
<div>
|
|
12
|
+
<h1>${name}</h1>
|
|
13
|
+
</div>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function makeContainer(feature: string, name: string) {
|
|
20
|
+
const base = path.join(process.cwd(), 'app', feature, 'containers');
|
|
21
|
+
|
|
22
|
+
if (!fs.existsSync(base)) {
|
|
23
|
+
console.error(`❌ Feature "${feature}" does not exist. Run make:feature ${feature} first.`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const filePath = path.join(base, `${name}.tsx`);
|
|
28
|
+
|
|
29
|
+
if (fs.existsSync(filePath)) {
|
|
30
|
+
console.error(`❌ Container "${name}" already exists at ${filePath}`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
fs.writeFileSync(filePath, renderContainer(name));
|
|
35
|
+
console.log(`✅ Container "${name}" created at ${filePath}`);
|
|
36
|
+
}
|
package/src/commands/feature.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
+
import { makeComponent } from './component';
|
|
4
|
+
import { makeHook } from './hook';
|
|
5
|
+
import { makeService } from './service';
|
|
6
|
+
import { makeRepository } from './repository';
|
|
7
|
+
import { makeSchema } from './schema';
|
|
8
|
+
import { makeContainer } from './container';
|
|
3
9
|
|
|
4
10
|
const FEATURE_DIRS = [
|
|
5
11
|
'components/server',
|
|
@@ -11,12 +17,15 @@ const FEATURE_DIRS = [
|
|
|
11
17
|
'schemas',
|
|
12
18
|
];
|
|
13
19
|
|
|
14
|
-
function
|
|
15
|
-
|
|
20
|
+
function toPascalCase(name: string): string {
|
|
21
|
+
return name
|
|
16
22
|
.split('-')
|
|
17
23
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
18
24
|
.join('');
|
|
25
|
+
}
|
|
19
26
|
|
|
27
|
+
function renderPage(name: string): string {
|
|
28
|
+
const componentName = toPascalCase(name);
|
|
20
29
|
return `export default function ${componentName}Page() {
|
|
21
30
|
return (
|
|
22
31
|
<div>
|
|
@@ -27,16 +36,33 @@ function renderTemplate(name: string): string {
|
|
|
27
36
|
`;
|
|
28
37
|
}
|
|
29
38
|
|
|
30
|
-
export async function makeFeature(name: string) {
|
|
39
|
+
export async function makeFeature(name: string, all: boolean = false) {
|
|
31
40
|
const base = path.join(process.cwd(), 'app', name);
|
|
41
|
+
const pascalName = toPascalCase(name);
|
|
42
|
+
|
|
43
|
+
if (fs.existsSync(base)) {
|
|
44
|
+
console.error(`❌ Feature "${name}" already exists at ${base}`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
32
47
|
|
|
33
48
|
for (const dir of FEATURE_DIRS) {
|
|
34
49
|
fs.mkdirSync(path.join(base, dir), { recursive: true });
|
|
35
|
-
|
|
50
|
+
if (!all) {
|
|
51
|
+
fs.writeFileSync(path.join(base, dir, '.gitkeep'), '');
|
|
52
|
+
}
|
|
36
53
|
}
|
|
37
54
|
|
|
38
|
-
|
|
39
|
-
fs.writeFileSync(path.join(base, 'page.tsx'), page);
|
|
40
|
-
|
|
55
|
+
fs.writeFileSync(path.join(base, 'page.tsx'), renderPage(name));
|
|
41
56
|
console.log(`✅ Feature "${name}" scaffolded at ${base}`);
|
|
57
|
+
|
|
58
|
+
if (all) {
|
|
59
|
+
makeComponent(name, pascalName, 'client');
|
|
60
|
+
makeContainer(name, `${pascalName}Container`);
|
|
61
|
+
makeHook(name, `use${pascalName}`);
|
|
62
|
+
makeService(name, pascalName);
|
|
63
|
+
makeRepository(name, `${pascalName}Repository`);
|
|
64
|
+
makeSchema(name, `${pascalName}Schema`);
|
|
65
|
+
|
|
66
|
+
console.log(`✅ All files scaffolded for "${name}"`);
|
|
67
|
+
}
|
|
42
68
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
function renderHook(name: string): string {
|
|
5
|
+
return `import { useState } from 'react';
|
|
6
|
+
|
|
7
|
+
export function ${name}() {
|
|
8
|
+
const [data, setData] = useState(null);
|
|
9
|
+
|
|
10
|
+
return { data };
|
|
11
|
+
}
|
|
12
|
+
`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function makeHook(feature: string, name: string) {
|
|
16
|
+
const base = path.join(process.cwd(), 'app', feature, 'hooks');
|
|
17
|
+
|
|
18
|
+
if (!fs.existsSync(base)) {
|
|
19
|
+
console.error(`❌ Feature "${feature}" does not exist. Run make:feature ${feature} first.`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const filePath = path.join(base, `${name}.ts`);
|
|
24
|
+
|
|
25
|
+
if (fs.existsSync(filePath)) {
|
|
26
|
+
console.error(`❌ Hook "${name}" already exists at ${filePath}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
fs.writeFileSync(filePath, renderHook(name));
|
|
31
|
+
console.log(`✅ Hook "${name}" created at ${filePath}`);
|
|
32
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
type RepositoryAction = 'List' | 'Create' | 'Update' | 'Delete' | 'Show';
|
|
5
|
+
|
|
6
|
+
const REPOSITORY_ACTIONS: RepositoryAction[] = ['List', 'Create', 'Update', 'Delete', 'Show'];
|
|
7
|
+
|
|
8
|
+
function renderRepository(action: RepositoryAction, name: string, feature: string): string {
|
|
9
|
+
switch (action) {
|
|
10
|
+
case 'List':
|
|
11
|
+
return `export class List${name}Repository {
|
|
12
|
+
async handle() {
|
|
13
|
+
const response = await fetch('/api/${feature}');
|
|
14
|
+
return response.json();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
`;
|
|
18
|
+
case 'Show':
|
|
19
|
+
return `export class Show${name}Repository {
|
|
20
|
+
async handle(id: string) {
|
|
21
|
+
const response = await fetch(\`/api/${feature}/\${id}\`);
|
|
22
|
+
return response.json();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
`;
|
|
26
|
+
case 'Create':
|
|
27
|
+
return `export class Create${name}Repository {
|
|
28
|
+
async handle(data: unknown) {
|
|
29
|
+
const response = await fetch('/api/${feature}', {
|
|
30
|
+
method: 'POST',
|
|
31
|
+
body: JSON.stringify(data),
|
|
32
|
+
});
|
|
33
|
+
return response.json();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
37
|
+
case 'Update':
|
|
38
|
+
return `export class Update${name}Repository {
|
|
39
|
+
async handle(id: string, data: unknown) {
|
|
40
|
+
const response = await fetch(\`/api/${feature}/\${id}\`, {
|
|
41
|
+
method: 'PUT',
|
|
42
|
+
body: JSON.stringify(data),
|
|
43
|
+
});
|
|
44
|
+
return response.json();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
`;
|
|
48
|
+
case 'Delete':
|
|
49
|
+
return `export class Delete${name}Repository {
|
|
50
|
+
async handle(id: string) {
|
|
51
|
+
const response = await fetch(\`/api/${feature}/\${id}\`, {
|
|
52
|
+
method: 'DELETE',
|
|
53
|
+
});
|
|
54
|
+
return response.json();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
`;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function makeRepository(feature: string, name: string) {
|
|
62
|
+
const base = path.join(process.cwd(), 'app', feature, 'repositories');
|
|
63
|
+
|
|
64
|
+
if (!fs.existsSync(base)) {
|
|
65
|
+
console.error(`❌ Feature "${feature}" does not exist. Run make:feature ${feature} first.`);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
for (const action of REPOSITORY_ACTIONS) {
|
|
70
|
+
const filePath = path.join(base, `${action}${name}.repository.ts`);
|
|
71
|
+
|
|
72
|
+
if (fs.existsSync(filePath)) {
|
|
73
|
+
console.warn(`⚠️ Skipping "${action}${name}.repository.ts" — already exists`);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
fs.writeFileSync(filePath, renderRepository(action, name, feature));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.log(`✅ Repositories for "${name}" created at ${base}`);
|
|
81
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
type SchemaAction = 'Create' | 'Update';
|
|
5
|
+
|
|
6
|
+
const SCHEMA_ACTIONS: SchemaAction[] = ['Create', 'Update'];
|
|
7
|
+
|
|
8
|
+
function renderSchema(action: SchemaAction, name: string): string {
|
|
9
|
+
switch (action) {
|
|
10
|
+
case 'Create':
|
|
11
|
+
return `import { z } from 'zod';
|
|
12
|
+
|
|
13
|
+
export const Create${name}Schema = z.object({
|
|
14
|
+
// add create fields here
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export type Create${name} = z.infer<typeof Create${name}Schema>;
|
|
18
|
+
`;
|
|
19
|
+
case 'Update':
|
|
20
|
+
return `import { z } from 'zod';
|
|
21
|
+
|
|
22
|
+
export const Update${name}Schema = z.object({
|
|
23
|
+
id: z.string(),
|
|
24
|
+
// add update fields here
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export type Update${name} = z.infer<typeof Update${name}Schema>;
|
|
28
|
+
`;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function makeSchema(feature: string, name: string) {
|
|
33
|
+
const base = path.join(process.cwd(), 'app', feature, 'schemas');
|
|
34
|
+
|
|
35
|
+
if (!fs.existsSync(base)) {
|
|
36
|
+
console.error(`❌ Feature "${feature}" does not exist. Run make:feature ${feature} first.`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
for (const action of SCHEMA_ACTIONS) {
|
|
41
|
+
const filePath = path.join(base, `${action}${name}.schema.ts`);
|
|
42
|
+
|
|
43
|
+
if (fs.existsSync(filePath)) {
|
|
44
|
+
console.warn(`⚠️ Skipping "${action}${name}.schema.ts" — already exists`);
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
fs.writeFileSync(filePath, renderSchema(action, name));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
console.log(`✅ Schemas for "${name}" created at ${base}`);
|
|
52
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
type ServiceAction = 'List' | 'Create' | 'Update' | 'Delete' | 'Show';
|
|
5
|
+
|
|
6
|
+
const SERVICE_ACTIONS: ServiceAction[] = ['List', 'Create', 'Update', 'Delete', 'Show'];
|
|
7
|
+
|
|
8
|
+
function renderService(action: ServiceAction, name: string): string {
|
|
9
|
+
switch (action) {
|
|
10
|
+
case 'List':
|
|
11
|
+
return `export class List${name}Service {
|
|
12
|
+
async handle() {
|
|
13
|
+
// fetch all ${name}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
17
|
+
case 'Show':
|
|
18
|
+
return `export class Show${name}Service {
|
|
19
|
+
async handle(id: string) {
|
|
20
|
+
// fetch single ${name}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
24
|
+
case 'Create':
|
|
25
|
+
return `export class Create${name}Service {
|
|
26
|
+
async handle(data: unknown) {
|
|
27
|
+
// create ${name}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
`;
|
|
31
|
+
case 'Update':
|
|
32
|
+
return `export class Update${name}Service {
|
|
33
|
+
async handle(id: string, data: unknown) {
|
|
34
|
+
// update ${name}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
`;
|
|
38
|
+
case 'Delete':
|
|
39
|
+
return `export class Delete${name}Service {
|
|
40
|
+
async handle(id: string) {
|
|
41
|
+
// delete ${name}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
`;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function makeService(feature: string, name: string) {
|
|
49
|
+
const base = path.join(process.cwd(), 'app', feature, 'services');
|
|
50
|
+
|
|
51
|
+
if (!fs.existsSync(base)) {
|
|
52
|
+
console.error(`❌ Feature "${feature}" does not exist. Run make:feature ${feature} first.`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
for (const action of SERVICE_ACTIONS) {
|
|
57
|
+
const filePath = path.join(base, `${action}${name}.service.ts`);
|
|
58
|
+
|
|
59
|
+
if (fs.existsSync(filePath)) {
|
|
60
|
+
console.warn(`⚠️ Skipping "${action}${name}.service.ts" — already exists`);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
fs.writeFileSync(filePath, renderService(action, name));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
console.log(`✅ Services for "${name}" created at ${base}`);
|
|
68
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,16 +1,75 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { makeFeature } from './commands/feature';
|
|
3
|
+
import { makeComponent } from './commands/component';
|
|
4
|
+
import { makeHook } from './commands/hook';
|
|
5
|
+
import { makeService } from './commands/service';
|
|
6
|
+
import { makeSchema } from './commands/schema';
|
|
7
|
+
import { makeRepository } from './commands/repository';
|
|
8
|
+
import { makeContainer } from './commands/container';
|
|
3
9
|
|
|
4
|
-
const [,, command, name] = process.argv;
|
|
10
|
+
const [,, command, feature, name] = process.argv;
|
|
5
11
|
|
|
6
|
-
if (!command || !
|
|
7
|
-
console.error('Usage: domain-driver
|
|
12
|
+
if (!command || !feature) {
|
|
13
|
+
console.error('Usage: domain-driver <command> [options]');
|
|
8
14
|
process.exit(1);
|
|
9
15
|
}
|
|
10
16
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
switch (command) {
|
|
18
|
+
case 'make:feature':
|
|
19
|
+
const all = process.argv.includes('-a') || process.argv.includes('-A');
|
|
20
|
+
makeFeature(feature, all).then(() => {});
|
|
21
|
+
break;
|
|
22
|
+
|
|
23
|
+
case 'make:component':
|
|
24
|
+
if (!name) {
|
|
25
|
+
console.error('Usage: domain-driver make:component <feature> <name> [client|server]');
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
const type = (process.argv[5] as 'client' | 'server') || 'client';
|
|
29
|
+
makeComponent(feature, name, type);
|
|
30
|
+
break;
|
|
31
|
+
|
|
32
|
+
case 'make:hook':
|
|
33
|
+
if (!name) {
|
|
34
|
+
console.error('Usage: domain-driver make:hook <feature> <name>');
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
makeHook(feature, name);
|
|
38
|
+
break;
|
|
39
|
+
|
|
40
|
+
case 'make:service':
|
|
41
|
+
if (!name) {
|
|
42
|
+
console.error('Usage: domain-driver make:service <feature> <name>');
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
makeService(feature, name);
|
|
46
|
+
break;
|
|
47
|
+
|
|
48
|
+
case 'make:schema':
|
|
49
|
+
if (!name) {
|
|
50
|
+
console.error('Usage: domain-driver make:schema <feature> <name>');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
makeSchema(feature, name);
|
|
54
|
+
break;
|
|
55
|
+
|
|
56
|
+
case 'make:repository':
|
|
57
|
+
if (!name) {
|
|
58
|
+
console.error('Usage: domain-driver make:repository <feature> <name>');
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
makeRepository(feature, name);
|
|
62
|
+
break;
|
|
63
|
+
|
|
64
|
+
case 'make:container':
|
|
65
|
+
if (!name) {
|
|
66
|
+
console.error('Usage: domain-driver make:container <feature> <name>');
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
makeContainer(feature, name);
|
|
70
|
+
break;
|
|
71
|
+
|
|
72
|
+
default:
|
|
73
|
+
console.error(`Unknown command: ${command}`);
|
|
74
|
+
process.exit(1);
|
|
16
75
|
}
|