@domain-first/project-structure 0.0.1
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/LICENSE +21 -0
- package/README.md +9 -0
- package/dist/config-file.d.ts +21 -0
- package/dist/file-system.d.ts +22 -0
- package/dist/index.cjs +950 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +916 -0
- package/dist/project-root.d.ts +4 -0
- package/dist/project-root.spec.d.ts +1 -0
- package/dist/unknown-format-naming.d.ts +9 -0
- package/dist/use-cases/index.d.ts +10 -0
- package/dist/use-cases/initialize.d.ts +1 -0
- package/dist/use-cases/scaffold-new-aggregate.d.ts +2 -0
- package/dist/use-cases/scaffold-new-application-port.d.ts +2 -0
- package/dist/use-cases/scaffold-new-bounded-context.d.ts +1 -0
- package/dist/use-cases/scaffold-new-command.d.ts +2 -0
- package/dist/use-cases/scaffold-new-domain-service.d.ts +2 -0
- package/dist/use-cases/scaffold-new-error.d.ts +2 -0
- package/dist/use-cases/scaffold-new-handlers-rest-endpoint.d.ts +2 -0
- package/dist/use-cases/scaffold-new-query.d.ts +2 -0
- package/dist/use-cases/scaffold-new-use-case.d.ts +2 -0
- package/package.json +49 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,950 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __webpack_require__ = {};
|
|
4
|
+
(()=>{
|
|
5
|
+
__webpack_require__.n = (module)=>{
|
|
6
|
+
var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
|
|
7
|
+
__webpack_require__.d(getter, {
|
|
8
|
+
a: getter
|
|
9
|
+
});
|
|
10
|
+
return getter;
|
|
11
|
+
};
|
|
12
|
+
})();
|
|
13
|
+
(()=>{
|
|
14
|
+
__webpack_require__.d = (exports1, getters, values)=>{
|
|
15
|
+
var define = (defs, kind)=>{
|
|
16
|
+
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
[kind]: defs[key]
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
define(getters, "get");
|
|
22
|
+
define(values, "value");
|
|
23
|
+
};
|
|
24
|
+
})();
|
|
25
|
+
(()=>{
|
|
26
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
27
|
+
})();
|
|
28
|
+
var __webpack_exports__ = {};
|
|
29
|
+
const prompts_namespaceObject = require("@inquirer/prompts");
|
|
30
|
+
const external_node_path_namespaceObject = require("node:path");
|
|
31
|
+
var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
|
|
32
|
+
const external_zod_namespaceObject = require("zod");
|
|
33
|
+
var external_zod_default = /*#__PURE__*/ __webpack_require__.n(external_zod_namespaceObject);
|
|
34
|
+
const external_node_fs_namespaceObject = require("node:fs");
|
|
35
|
+
var external_node_fs_default = /*#__PURE__*/ __webpack_require__.n(external_node_fs_namespaceObject);
|
|
36
|
+
class FileSystemItem {
|
|
37
|
+
path;
|
|
38
|
+
constructor(path){
|
|
39
|
+
this.path = path;
|
|
40
|
+
}
|
|
41
|
+
subitem = (relativePath)=>new this.constructor(external_node_path_default().resolve(this.path, ...relativePath));
|
|
42
|
+
get exists() {
|
|
43
|
+
return external_node_fs_default().existsSync(this.path);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
class Folder extends FileSystemItem {
|
|
47
|
+
createIfNotExisted = ()=>{
|
|
48
|
+
external_node_fs_default().mkdirSync(this.path, {
|
|
49
|
+
recursive: true
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
get content() {
|
|
53
|
+
if (!this.exists) return {
|
|
54
|
+
subfolderNames: [],
|
|
55
|
+
fileNames: []
|
|
56
|
+
};
|
|
57
|
+
const content = external_node_fs_default().readdirSync(this.path, {
|
|
58
|
+
withFileTypes: true
|
|
59
|
+
});
|
|
60
|
+
return {
|
|
61
|
+
subfolderNames: content.filter((x)=>x.isDirectory()).map((x)=>x.name),
|
|
62
|
+
fileNames: content.filter((x)=>!x.isDirectory()).map((x)=>external_node_path_default().parse(x.name).name)
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
get name() {
|
|
66
|
+
return external_node_path_default().basename(this.path);
|
|
67
|
+
}
|
|
68
|
+
createFile = (name, content)=>{
|
|
69
|
+
this.createIfNotExisted();
|
|
70
|
+
const file = new File(this.path).subitem([
|
|
71
|
+
name
|
|
72
|
+
]);
|
|
73
|
+
file.data = content;
|
|
74
|
+
return file;
|
|
75
|
+
};
|
|
76
|
+
file = (name)=>{
|
|
77
|
+
this.createIfNotExisted();
|
|
78
|
+
const file = new File(this.path).subitem([
|
|
79
|
+
name
|
|
80
|
+
]);
|
|
81
|
+
return file;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
class File extends FileSystemItem {
|
|
85
|
+
get data() {
|
|
86
|
+
if (!this.exists) throw new Error('File does not exist');
|
|
87
|
+
return external_node_fs_default().readFileSync(this.path, 'utf-8');
|
|
88
|
+
}
|
|
89
|
+
set data(content) {
|
|
90
|
+
external_node_fs_default().writeFileSync(this.path, content);
|
|
91
|
+
}
|
|
92
|
+
addLine = (newLine, separator = '\n')=>{
|
|
93
|
+
let content = '';
|
|
94
|
+
try {
|
|
95
|
+
content = external_node_fs_default().readFileSync(this.path, 'utf8');
|
|
96
|
+
} catch {}
|
|
97
|
+
external_node_fs_default().writeFileSync(this.path, [
|
|
98
|
+
content.trim(),
|
|
99
|
+
newLine.trim()
|
|
100
|
+
].filter((x)=>!!x).join(separator), 'utf8');
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
class ProjectRoot {
|
|
104
|
+
path;
|
|
105
|
+
constructor(){
|
|
106
|
+
let dir = process.cwd();
|
|
107
|
+
while(dir !== external_node_path_default().parse(dir).root){
|
|
108
|
+
if (external_node_fs_default().existsSync(external_node_path_default().join(dir, 'package.json'))) {
|
|
109
|
+
this.path = dir;
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
dir = external_node_path_default().dirname(dir);
|
|
113
|
+
}
|
|
114
|
+
throw new Error('No package.json was found');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const configFileName = 'domain-first.project-structure.config.json';
|
|
118
|
+
const configSchema = external_zod_default().object({
|
|
119
|
+
rootFolder: external_zod_default().string(),
|
|
120
|
+
domainFirstPackages: external_zod_default().array(external_zod_default()["enum"]([
|
|
121
|
+
'@domain-first/types',
|
|
122
|
+
'@domain-first/errors',
|
|
123
|
+
'@domain-first/handlers',
|
|
124
|
+
'@domain-first/wire',
|
|
125
|
+
'@domain-first/handlers-rest'
|
|
126
|
+
])),
|
|
127
|
+
defaultPersistenceLayerImplementation: external_zod_default().string()
|
|
128
|
+
});
|
|
129
|
+
class ConfigFile {
|
|
130
|
+
data;
|
|
131
|
+
constructor(){
|
|
132
|
+
const filePath = ConfigFile.path;
|
|
133
|
+
const file = new File(filePath);
|
|
134
|
+
const configObject = JSON.parse(file.data);
|
|
135
|
+
this.data = configSchema.parse(configObject);
|
|
136
|
+
}
|
|
137
|
+
static get Instance() {
|
|
138
|
+
return new ConfigFile();
|
|
139
|
+
}
|
|
140
|
+
static get path() {
|
|
141
|
+
const { path: root } = new ProjectRoot();
|
|
142
|
+
return external_node_path_default().resolve(root, configFileName);
|
|
143
|
+
}
|
|
144
|
+
static get exists() {
|
|
145
|
+
return new File(ConfigFile.path).exists;
|
|
146
|
+
}
|
|
147
|
+
static write = (config)=>{
|
|
148
|
+
new File(ConfigFile.path).data = JSON.stringify(config, null, '\t');
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const initializeUseCase = async ()=>{
|
|
152
|
+
const rootFolderName = await (0, prompts_namespaceObject.input)({
|
|
153
|
+
message: 'Root folder (use "/" for inner folders):',
|
|
154
|
+
default: 'src'
|
|
155
|
+
});
|
|
156
|
+
const domainFirstPackages = await (0, prompts_namespaceObject.checkbox)({
|
|
157
|
+
message: '@domain-first packages',
|
|
158
|
+
choices: [
|
|
159
|
+
'@domain-first/types',
|
|
160
|
+
'@domain-first/errors',
|
|
161
|
+
'@domain-first/handlers',
|
|
162
|
+
'@domain-first/wire',
|
|
163
|
+
'@domain-first/handlers-rest'
|
|
164
|
+
]
|
|
165
|
+
});
|
|
166
|
+
const defaultPersistenceLayerImplementation = await (0, prompts_namespaceObject.input)({
|
|
167
|
+
message: 'Default persistence layer implementation:'
|
|
168
|
+
});
|
|
169
|
+
ConfigFile.write({
|
|
170
|
+
rootFolder: rootFolderName,
|
|
171
|
+
domainFirstPackages,
|
|
172
|
+
defaultPersistenceLayerImplementation
|
|
173
|
+
});
|
|
174
|
+
const { path } = new ProjectRoot();
|
|
175
|
+
const rootFolder = new Folder(path);
|
|
176
|
+
if (domainFirstPackages.includes('@domain-first/wire')) rootFolder.subitem([
|
|
177
|
+
rootFolderName,
|
|
178
|
+
'shared',
|
|
179
|
+
'wiring'
|
|
180
|
+
]).createFile('env-branched-wire.ts', `
|
|
181
|
+
import { branchedWire } from '@domain-first/wire'
|
|
182
|
+
|
|
183
|
+
export const envBranchedWire = branchedWire(() => {
|
|
184
|
+
return process.env.NODE_ENV as 'test' | 'development' | 'production'
|
|
185
|
+
});
|
|
186
|
+
`);
|
|
187
|
+
if (domainFirstPackages.includes('@domain-first/errors')) rootFolder.subitem([
|
|
188
|
+
rootFolderName,
|
|
189
|
+
'shared'
|
|
190
|
+
]).subitem([
|
|
191
|
+
'domain',
|
|
192
|
+
'errors'
|
|
193
|
+
]).createFile('index.ts', `
|
|
194
|
+
import { errorNamespace } from '@domain-first/errors'
|
|
195
|
+
|
|
196
|
+
export const SharedErrors = errorNamespace('shared')
|
|
197
|
+
`.trim());
|
|
198
|
+
};
|
|
199
|
+
class UnknownFormatNaming {
|
|
200
|
+
rawSource;
|
|
201
|
+
camelCase;
|
|
202
|
+
PascalCase;
|
|
203
|
+
constructor(rawSource){
|
|
204
|
+
this.rawSource = rawSource;
|
|
205
|
+
const source = rawSource.trim();
|
|
206
|
+
if (!source.includes('-')) {
|
|
207
|
+
this.camelCase = source.substring(0, 1).toLowerCase() + source.substring(1);
|
|
208
|
+
this.PascalCase = source.substring(0, 1).toUpperCase() + source.substring(1);
|
|
209
|
+
}
|
|
210
|
+
const words = source.split('-');
|
|
211
|
+
this.camelCase = words.map((word, index)=>0 === index ? word.toLowerCase() : word[0].toUpperCase() + word.slice(1).toLowerCase()).join('');
|
|
212
|
+
this.PascalCase = words.map((word)=>word[0].toUpperCase() + word.slice(1).toLowerCase()).join('');
|
|
213
|
+
}
|
|
214
|
+
get fileName() {
|
|
215
|
+
return this.rawSource;
|
|
216
|
+
}
|
|
217
|
+
get variableName() {
|
|
218
|
+
return this.camelCase;
|
|
219
|
+
}
|
|
220
|
+
get ClassName() {
|
|
221
|
+
return this.PascalCase;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const scaffoldNewAggregateUseCase = async (boundedContextFolder)=>{
|
|
225
|
+
const aggregateName = await (0, prompts_namespaceObject.input)({
|
|
226
|
+
message: 'Aggregate name:'
|
|
227
|
+
});
|
|
228
|
+
const aggregateNaming = new UnknownFormatNaming(aggregateName);
|
|
229
|
+
const withRepository = await (0, prompts_namespaceObject.confirm)({
|
|
230
|
+
message: 'With Repository'
|
|
231
|
+
});
|
|
232
|
+
const aggregatesFolder = boundedContextFolder.subitem([
|
|
233
|
+
'domain',
|
|
234
|
+
'aggregates',
|
|
235
|
+
aggregateName
|
|
236
|
+
]);
|
|
237
|
+
aggregatesFolder.createFile(`${aggregateNaming.fileName}.aggregate-root.ts`, ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/types') ? `
|
|
238
|
+
import { domainType } from '@domain-first/types'
|
|
239
|
+
|
|
240
|
+
export class ${aggregateNaming.ClassName} extends domainType() {
|
|
241
|
+
|
|
242
|
+
}`.trim() : `export class ${aggregateNaming.ClassName} {}`);
|
|
243
|
+
const barrelFile = aggregatesFolder.createFile("index.ts", `export * from './${aggregateNaming.fileName}.aggregate-root'`);
|
|
244
|
+
if (withRepository) {
|
|
245
|
+
aggregatesFolder.createFile(`${aggregateNaming.fileName}-repository.ts`, `export abstract class ${aggregateNaming.ClassName}Repository { }`);
|
|
246
|
+
barrelFile.addLine(`export * from './${aggregateNaming.fileName}-repository'`);
|
|
247
|
+
const infrastructureImplementationType = await (0, prompts_namespaceObject.input)({
|
|
248
|
+
message: 'Repository infrastructure implementation type:',
|
|
249
|
+
default: 'prisma'
|
|
250
|
+
});
|
|
251
|
+
let addTestImplementation = false;
|
|
252
|
+
let testImplementationType = '';
|
|
253
|
+
addTestImplementation = await (0, prompts_namespaceObject.confirm)({
|
|
254
|
+
message: 'Add a test implementation for Repository'
|
|
255
|
+
});
|
|
256
|
+
if (addTestImplementation) testImplementationType = await (0, prompts_namespaceObject.input)({
|
|
257
|
+
message: 'Test Repository infrastructure implementation type:',
|
|
258
|
+
default: 'in-memory'
|
|
259
|
+
});
|
|
260
|
+
const infrastructureRepositoriesFolder = boundedContextFolder.subitem([
|
|
261
|
+
'infrastructure',
|
|
262
|
+
'repositories'
|
|
263
|
+
]);
|
|
264
|
+
const implementationTypes = [
|
|
265
|
+
infrastructureImplementationType,
|
|
266
|
+
testImplementationType
|
|
267
|
+
].filter((x)=>!!x);
|
|
268
|
+
for (const implementation of implementationTypes){
|
|
269
|
+
const naming = new UnknownFormatNaming(implementation);
|
|
270
|
+
infrastructureRepositoriesFolder.subitem([
|
|
271
|
+
naming.fileName
|
|
272
|
+
]).createFile(`${naming.fileName}-${aggregateName}-repository.ts`, `
|
|
273
|
+
import { ${aggregateNaming.ClassName}Repository } from '../../../domain/aggregates/${aggregateName}'
|
|
274
|
+
|
|
275
|
+
export class ${naming.ClassName}${aggregateNaming.ClassName}Repository implements ${aggregateNaming.ClassName}Repository {
|
|
276
|
+
constructor() {}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
`.trim());
|
|
280
|
+
if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) {
|
|
281
|
+
const wiringFolder = boundedContextFolder.subitem([
|
|
282
|
+
'wiring',
|
|
283
|
+
'infrastructure',
|
|
284
|
+
'repositories',
|
|
285
|
+
naming.fileName
|
|
286
|
+
]);
|
|
287
|
+
wiringFolder.createFile(`wire-${naming.fileName}-${aggregateNaming.fileName}-repository.ts`, `
|
|
288
|
+
import { wireClass } from '@domain-first/wire'
|
|
289
|
+
import { ${naming.ClassName}${aggregateNaming.ClassName}Repository } from '../../../../infrastructure/repositories/${naming.fileName}/${naming.fileName}-${aggregateNaming.fileName}-repository'
|
|
290
|
+
|
|
291
|
+
export const wire${naming.ClassName}${aggregateNaming.ClassName}Repository = wireClass(
|
|
292
|
+
${naming.ClassName}${aggregateNaming.ClassName}Repository,
|
|
293
|
+
[]
|
|
294
|
+
)
|
|
295
|
+
`.trim());
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) {
|
|
299
|
+
const implementationNaming = new UnknownFormatNaming(infrastructureImplementationType);
|
|
300
|
+
const testImplementationNaming = new UnknownFormatNaming(testImplementationType);
|
|
301
|
+
boundedContextFolder.subitem([
|
|
302
|
+
'wiring',
|
|
303
|
+
'domain',
|
|
304
|
+
'repositories'
|
|
305
|
+
]).createFile(`wire-${aggregateNaming.fileName}-repository.ts`, `
|
|
306
|
+
import { envBranchedWire } from '../../../../../shared/wiring/env-branched-wire'
|
|
307
|
+
${implementationTypes.map((x)=>new UnknownFormatNaming(x)).map(({ ClassName, fileName })=>`import { wire${ClassName}${aggregateNaming.ClassName}Repository } from '../../infrastructure/repositories/${fileName}/wire-${fileName}-${aggregateNaming.fileName}-repository'`).join('\n')}
|
|
308
|
+
|
|
309
|
+
export const wire${aggregateNaming.ClassName}Repository = envBranchedWire({
|
|
310
|
+
test: wire${addTestImplementation ? testImplementationNaming.ClassName : implementationNaming.ClassName}${aggregateNaming.ClassName}Repository,
|
|
311
|
+
development: wire${implementationNaming.ClassName}${aggregateNaming.ClassName}Repository,
|
|
312
|
+
production: wire${implementationNaming.ClassName}${aggregateNaming.ClassName}Repository
|
|
313
|
+
})
|
|
314
|
+
`.trim());
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
const scaffoldNewApplicationPort = async (boundedContextFolder)=>{
|
|
319
|
+
const name = await (0, prompts_namespaceObject.input)({
|
|
320
|
+
message: 'Name: '
|
|
321
|
+
});
|
|
322
|
+
const naming = new UnknownFormatNaming(name);
|
|
323
|
+
const wiringFolder = boundedContextFolder.subitem([
|
|
324
|
+
'wiring',
|
|
325
|
+
'application',
|
|
326
|
+
'ports',
|
|
327
|
+
naming.fileName
|
|
328
|
+
]);
|
|
329
|
+
const infraFolder = boundedContextFolder.subitem([
|
|
330
|
+
'wiring',
|
|
331
|
+
'infrastructure',
|
|
332
|
+
'application-ports'
|
|
333
|
+
]);
|
|
334
|
+
const applicationPortsFolder = boundedContextFolder.subitem([
|
|
335
|
+
'application',
|
|
336
|
+
'ports'
|
|
337
|
+
]);
|
|
338
|
+
applicationPortsFolder.createFile(`${naming.fileName}.ts`, `
|
|
339
|
+
export abstract class ${naming.ClassName} {
|
|
340
|
+
|
|
341
|
+
}
|
|
342
|
+
`.trim());
|
|
343
|
+
const implementationType = await (0, prompts_namespaceObject.input)({
|
|
344
|
+
message: 'Adapter implementation type:'
|
|
345
|
+
});
|
|
346
|
+
const addTestImplementation = await (0, prompts_namespaceObject.confirm)({
|
|
347
|
+
message: 'With test implementation'
|
|
348
|
+
});
|
|
349
|
+
let testImplementationType = '';
|
|
350
|
+
if (addTestImplementation) testImplementationType = await (0, prompts_namespaceObject.input)({
|
|
351
|
+
message: 'Test adapter implementation type:',
|
|
352
|
+
default: 'mock'
|
|
353
|
+
});
|
|
354
|
+
const infrastructureFolder = boundedContextFolder.subitem([
|
|
355
|
+
'infrastructure',
|
|
356
|
+
'application-adapters'
|
|
357
|
+
]);
|
|
358
|
+
const implementationTypes = [
|
|
359
|
+
implementationType,
|
|
360
|
+
testImplementationType
|
|
361
|
+
].filter((x)=>!!x);
|
|
362
|
+
for (const implementation of implementationTypes){
|
|
363
|
+
const currentNaming = new UnknownFormatNaming(`${implementation}-${naming.fileName}`);
|
|
364
|
+
infrastructureFolder.subitem([
|
|
365
|
+
implementation
|
|
366
|
+
]).createFile(`${currentNaming.fileName}.ts`, `
|
|
367
|
+
import { ${naming.ClassName} } from '../../../application/ports/${naming.fileName}'
|
|
368
|
+
|
|
369
|
+
export class ${currentNaming.ClassName} extends ${naming.ClassName} {
|
|
370
|
+
|
|
371
|
+
}
|
|
372
|
+
`);
|
|
373
|
+
if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) infraFolder.subitem([
|
|
374
|
+
implementation
|
|
375
|
+
]).createFile(`wire-${currentNaming.fileName}.ts`, `
|
|
376
|
+
import { wireClass } from '@domain-first/wire'
|
|
377
|
+
import { ${currentNaming.ClassName} } from '../../../../infrastructure/application-adapters/${implementation}/${currentNaming.fileName}'
|
|
378
|
+
|
|
379
|
+
export const wire${currentNaming.ClassName} = wireClass(
|
|
380
|
+
${currentNaming.ClassName},
|
|
381
|
+
[]
|
|
382
|
+
)
|
|
383
|
+
`.trim());
|
|
384
|
+
}
|
|
385
|
+
if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) wiringFolder.createFile(`wire-${naming.fileName}.ts`, `
|
|
386
|
+
import { envBranchedWire } from '../../../../../${'shared' === boundedContextFolder.name ? '' : '../'}shared/wiring/env-branched-wire'
|
|
387
|
+
${implementationTypes.map((x)=>({
|
|
388
|
+
naming: new UnknownFormatNaming(`${x}-${naming.fileName}`),
|
|
389
|
+
implementation: x
|
|
390
|
+
})).map(({ naming: { ClassName, fileName }, implementation })=>`import { wire${ClassName} } from '../../../infrastructure/application-ports/${implementation}/wire-${fileName}'`).join('\n')}
|
|
391
|
+
|
|
392
|
+
export const wire${naming.ClassName} = envBranchedWire({
|
|
393
|
+
test: wire${new UnknownFormatNaming(`${implementationTypes.at(-1)}-${naming.fileName}`).ClassName},
|
|
394
|
+
development: wire${new UnknownFormatNaming(`${implementationTypes.at(0)}-${naming.fileName}`).ClassName},
|
|
395
|
+
production: wire${new UnknownFormatNaming(`${implementationTypes.at(0)}-${naming.fileName}`).ClassName}
|
|
396
|
+
})
|
|
397
|
+
|
|
398
|
+
`.trim());
|
|
399
|
+
};
|
|
400
|
+
const scaffoldNewBoundedContextUseCase = async ()=>{
|
|
401
|
+
const name = await (0, prompts_namespaceObject.input)({
|
|
402
|
+
message: 'Name:',
|
|
403
|
+
validate: (x)=>!!x.length
|
|
404
|
+
});
|
|
405
|
+
const { path } = new ProjectRoot();
|
|
406
|
+
const rootFolder = new Folder(path);
|
|
407
|
+
const folder = rootFolder.subitem([
|
|
408
|
+
ConfigFile.Instance.data.rootFolder,
|
|
409
|
+
'bounded-contexts',
|
|
410
|
+
name
|
|
411
|
+
]);
|
|
412
|
+
if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/errors')) folder.subitem([
|
|
413
|
+
'domain',
|
|
414
|
+
'errors'
|
|
415
|
+
]).createFile('index.ts', `
|
|
416
|
+
import { errorNamespace } from '@domain-first/errors'
|
|
417
|
+
|
|
418
|
+
export const ${new UnknownFormatNaming(name).ClassName}Errors = errorNamespace('${name}')
|
|
419
|
+
`.trim());
|
|
420
|
+
folder.createIfNotExisted();
|
|
421
|
+
};
|
|
422
|
+
const scaffoldNewCommand = async (boundedContextFolder)=>{
|
|
423
|
+
const commandsFolder = boundedContextFolder.subitem([
|
|
424
|
+
'application',
|
|
425
|
+
'commands'
|
|
426
|
+
]);
|
|
427
|
+
const infrastructureFolder = boundedContextFolder.subitem([
|
|
428
|
+
'infrastructure',
|
|
429
|
+
'commands'
|
|
430
|
+
]);
|
|
431
|
+
const commandName = await (0, prompts_namespaceObject.input)({
|
|
432
|
+
message: 'Name: '
|
|
433
|
+
});
|
|
434
|
+
const naming = new UnknownFormatNaming(commandName);
|
|
435
|
+
const implementationType = await (0, prompts_namespaceObject.input)({
|
|
436
|
+
message: 'Command infrastructure implementation type:',
|
|
437
|
+
default: ConfigFile.Instance.data.defaultPersistenceLayerImplementation
|
|
438
|
+
});
|
|
439
|
+
let addTestImplementation = false;
|
|
440
|
+
let testImplementationType = '';
|
|
441
|
+
addTestImplementation = await (0, prompts_namespaceObject.confirm)({
|
|
442
|
+
message: 'Add a test implementation for Command'
|
|
443
|
+
});
|
|
444
|
+
if (addTestImplementation) testImplementationType = await (0, prompts_namespaceObject.input)({
|
|
445
|
+
message: 'Test Command infrastructure implementation type:',
|
|
446
|
+
default: 'in-memory'
|
|
447
|
+
});
|
|
448
|
+
const implementationTypes = [
|
|
449
|
+
implementationType,
|
|
450
|
+
testImplementationType
|
|
451
|
+
].filter((x)=>!!x);
|
|
452
|
+
for (const implementation of implementationTypes){
|
|
453
|
+
const formatNaming = new UnknownFormatNaming(implementation);
|
|
454
|
+
infrastructureFolder.subitem([
|
|
455
|
+
formatNaming.fileName
|
|
456
|
+
]).createFile(`${formatNaming.fileName}-${naming.fileName}-command.ts`, ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/handlers') ? `
|
|
457
|
+
import { defineHandler } from '@domain-first/handlers'
|
|
458
|
+
import { ${naming.ClassName}Command } from '../../../application/commands/${naming.fileName}-command'
|
|
459
|
+
|
|
460
|
+
export class ${formatNaming.ClassName}${naming.ClassName}Command implements ${naming.ClassName}Command {
|
|
461
|
+
constructor() {}
|
|
462
|
+
|
|
463
|
+
handle = defineHandler({
|
|
464
|
+
inputSchema: ${naming.ClassName}Command.inputSchema,
|
|
465
|
+
outputSchema: ${naming.ClassName}Command.outputSchema,
|
|
466
|
+
handler: async (input) => {
|
|
467
|
+
return {}
|
|
468
|
+
}
|
|
469
|
+
})
|
|
470
|
+
}
|
|
471
|
+
`.trim() : `
|
|
472
|
+
import { ${naming.ClassName}Command } from '../../../domain/commands/${naming.fileName}-command'
|
|
473
|
+
|
|
474
|
+
export class ${formatNaming.ClassName}${naming.ClassName}Command implements ${naming.ClassName}Command {
|
|
475
|
+
|
|
476
|
+
}
|
|
477
|
+
`.trim());
|
|
478
|
+
if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) {
|
|
479
|
+
const wiringFolder = boundedContextFolder.subitem([
|
|
480
|
+
'wiring',
|
|
481
|
+
'infrastructure',
|
|
482
|
+
'commands',
|
|
483
|
+
formatNaming.fileName
|
|
484
|
+
]);
|
|
485
|
+
wiringFolder.createFile(`wire-${formatNaming.fileName}-${naming.fileName}-command.ts`, `
|
|
486
|
+
import { wireClass } from '@domain-first/wire'
|
|
487
|
+
import { ${formatNaming.ClassName}${naming.ClassName}Command } from '../../../../infrastructure/commands/${formatNaming.fileName}/${formatNaming.fileName}-${naming.fileName}-command'
|
|
488
|
+
|
|
489
|
+
export const wire${formatNaming.ClassName}${naming.ClassName}Command = wireClass(
|
|
490
|
+
${formatNaming.ClassName}${naming.ClassName}Command,
|
|
491
|
+
[]
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
`.trim());
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/handlers')) commandsFolder.createFile(`${naming.fileName}-command.ts`, `
|
|
498
|
+
import type { Handler } from '@domain-first/handlers'
|
|
499
|
+
|
|
500
|
+
export abstract class ${naming.ClassName}Command {
|
|
501
|
+
static inputSchema = {}
|
|
502
|
+
static outputSchema = {}
|
|
503
|
+
|
|
504
|
+
abstract handle: Handler<typeof ${naming.ClassName}Command.inputSchema, typeof ${naming.ClassName}Command.outputSchema>
|
|
505
|
+
}
|
|
506
|
+
`.trim());
|
|
507
|
+
else commandsFolder.createFile(`${naming.fileName}-command.ts`, `
|
|
508
|
+
export abstract class ${naming.ClassName}Command {
|
|
509
|
+
|
|
510
|
+
}`.trim());
|
|
511
|
+
if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) {
|
|
512
|
+
const implementationNaming = new UnknownFormatNaming(implementationType);
|
|
513
|
+
const testImplementationNaming = new UnknownFormatNaming(testImplementationType);
|
|
514
|
+
boundedContextFolder.subitem([
|
|
515
|
+
'wiring',
|
|
516
|
+
'application',
|
|
517
|
+
'commands'
|
|
518
|
+
]).createFile(`wire-${naming.fileName}-command.ts`, `
|
|
519
|
+
import { envBranchedWire } from '../../../../../shared/wiring/env-branched-wire'
|
|
520
|
+
${implementationTypes.map((x)=>new UnknownFormatNaming(x)).map(({ ClassName, fileName })=>`import { wire${ClassName}${naming.ClassName}Command } from '../../infrastructure/commands/${fileName}/wire-${fileName}-${naming.fileName}-command'`).join('\n')}
|
|
521
|
+
|
|
522
|
+
export const wire${naming.ClassName}Command = envBranchedWire({
|
|
523
|
+
test: wire${addTestImplementation ? testImplementationNaming.ClassName : implementationNaming.ClassName}${naming.ClassName}Command,
|
|
524
|
+
development: wire${implementationNaming.ClassName}${naming.ClassName}Command,
|
|
525
|
+
production: wire${implementationNaming.ClassName}${naming.ClassName}Command
|
|
526
|
+
})
|
|
527
|
+
`.trim());
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
const scaffoldNewDomainService = async (boundedContextFolder)=>{
|
|
531
|
+
const portName = await (0, prompts_namespaceObject.input)({
|
|
532
|
+
message: 'Name:'
|
|
533
|
+
});
|
|
534
|
+
const naming = new UnknownFormatNaming(portName);
|
|
535
|
+
boundedContextFolder.subitem([
|
|
536
|
+
'domain',
|
|
537
|
+
'services'
|
|
538
|
+
]).createFile(`${naming.fileName}-service.ts`, `
|
|
539
|
+
export class ${naming.ClassName}Service {
|
|
540
|
+
|
|
541
|
+
}
|
|
542
|
+
`.trim());
|
|
543
|
+
if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) boundedContextFolder.subitem([
|
|
544
|
+
'wiring',
|
|
545
|
+
'domain',
|
|
546
|
+
'services'
|
|
547
|
+
]).createFile(`wire-${naming.fileName}-service.ts`, `
|
|
548
|
+
import { wireClass } from '@domain-first/wire'
|
|
549
|
+
import { ${naming.ClassName}Service } from '../../../domain/services/${naming.fileName}-service'
|
|
550
|
+
|
|
551
|
+
export const wire${naming.ClassName}Service = wireClass(
|
|
552
|
+
${naming.ClassName}Service,
|
|
553
|
+
[]
|
|
554
|
+
)
|
|
555
|
+
`.trim());
|
|
556
|
+
};
|
|
557
|
+
const scaffoldNewErrorUseCase = async (boundedContextFolder)=>{
|
|
558
|
+
const currentErrorFolder = boundedContextFolder.subitem([
|
|
559
|
+
'domain',
|
|
560
|
+
'errors'
|
|
561
|
+
]);
|
|
562
|
+
const currentErrorNamespaces = currentErrorFolder.content.fileNames.filter((x)=>'index' !== x);
|
|
563
|
+
const variants = [
|
|
564
|
+
'New error without namespace',
|
|
565
|
+
...currentErrorNamespaces.map((x)=>`New error in namespace ${x}`),
|
|
566
|
+
'New namespace'
|
|
567
|
+
];
|
|
568
|
+
const selectedVariant = await (0, prompts_namespaceObject.select)({
|
|
569
|
+
choices: variants,
|
|
570
|
+
message: 'Action:'
|
|
571
|
+
});
|
|
572
|
+
if ('New error without namespace' === selectedVariant) {
|
|
573
|
+
const errorName = await (0, prompts_namespaceObject.input)({
|
|
574
|
+
message: 'Error name:'
|
|
575
|
+
});
|
|
576
|
+
const naming = new UnknownFormatNaming(errorName);
|
|
577
|
+
currentErrorFolder.file('index.ts').addLine(`export const ${naming.ClassName}Error = ${new UnknownFormatNaming(boundedContextFolder.name).ClassName}Errors.define('${naming.fileName}')`, '\n\n');
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
if ('New namespace' === selectedVariant) {
|
|
581
|
+
const namespaceName = await (0, prompts_namespaceObject.input)({
|
|
582
|
+
message: 'Namespace name:'
|
|
583
|
+
});
|
|
584
|
+
const { ClassName, fileName } = new UnknownFormatNaming(namespaceName);
|
|
585
|
+
currentErrorFolder.createFile(`${fileName}.ts`, `
|
|
586
|
+
import { errorNamespace } from '@domain-first/errors'
|
|
587
|
+
|
|
588
|
+
const ${ClassName}Errors = errorNamespace('${boundedContextFolder.name}').subnamespace('${fileName}')
|
|
589
|
+
`);
|
|
590
|
+
currentErrorFolder.file('index.ts').addLine(`export * from './${fileName}'`);
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
const namespace = selectedVariant.substring(23);
|
|
594
|
+
const errorName = await (0, prompts_namespaceObject.input)({
|
|
595
|
+
message: 'Error name:'
|
|
596
|
+
});
|
|
597
|
+
const { ClassName, fileName } = new UnknownFormatNaming(errorName);
|
|
598
|
+
currentErrorFolder.file(`${namespace}.ts`).addLine(`export const ${ClassName}Error = ${new UnknownFormatNaming(namespace).ClassName}Errors.define('${fileName}')`, '\n\n');
|
|
599
|
+
};
|
|
600
|
+
const scaffoldNewHandlersRestEndpoint = async (boundedContextFolder)=>{
|
|
601
|
+
const restPresentationFolder = boundedContextFolder.subitem([
|
|
602
|
+
'presentation',
|
|
603
|
+
'rest'
|
|
604
|
+
]);
|
|
605
|
+
const controllers = restPresentationFolder.content.subfolderNames;
|
|
606
|
+
let controller = await (0, prompts_namespaceObject.select)({
|
|
607
|
+
message: 'Controller: ',
|
|
608
|
+
choices: [
|
|
609
|
+
...controllers,
|
|
610
|
+
'New controller'
|
|
611
|
+
]
|
|
612
|
+
});
|
|
613
|
+
if ('New controller' === controller) {
|
|
614
|
+
controller = await (0, prompts_namespaceObject.input)({
|
|
615
|
+
message: 'Name: '
|
|
616
|
+
});
|
|
617
|
+
if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) boundedContextFolder.subitem([
|
|
618
|
+
'wiring',
|
|
619
|
+
'presentation',
|
|
620
|
+
'rest'
|
|
621
|
+
]).file('index.ts').addLine(`export * from './${controller}'`);
|
|
622
|
+
}
|
|
623
|
+
const method = await (0, prompts_namespaceObject.select)({
|
|
624
|
+
message: 'Method:',
|
|
625
|
+
choices: [
|
|
626
|
+
'get',
|
|
627
|
+
'post',
|
|
628
|
+
'patch',
|
|
629
|
+
'delete',
|
|
630
|
+
'put'
|
|
631
|
+
]
|
|
632
|
+
});
|
|
633
|
+
const path = (await (0, prompts_namespaceObject.input)({
|
|
634
|
+
message: 'Relative path (split by "/"):'
|
|
635
|
+
})).split('/').filter((x)=>!!x);
|
|
636
|
+
const controllerFolder = restPresentationFolder.subitem([
|
|
637
|
+
controller
|
|
638
|
+
]);
|
|
639
|
+
const nameBase = `${controller}-${path.length ? `${path.join('-')}-` : ''}${method}-endpoint`;
|
|
640
|
+
const endpointNaming = new UnknownFormatNaming(nameBase);
|
|
641
|
+
controllerFolder.createFile(`${endpointNaming.fileName}.ts`, `
|
|
642
|
+
import { EndpointGenerator } from '@domain-first/handlers-rest'
|
|
643
|
+
|
|
644
|
+
export class ${endpointNaming.ClassName} {
|
|
645
|
+
constructor(private readonly endpointGenerator: EndpointGenerator) {}
|
|
646
|
+
|
|
647
|
+
get logic() {
|
|
648
|
+
return () => {}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
get handle() {
|
|
652
|
+
return this.endpointGenerator(this.logic, {
|
|
653
|
+
route: { method: '${method}', path: ['${controller}'${path.length ? `, ${path.map((x)=>`'${x}'`).join(', ')}` : ''}] },
|
|
654
|
+
tags: ['${boundedContextFolder.name}: ${controller}'],
|
|
655
|
+
})
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
}
|
|
659
|
+
`.trim());
|
|
660
|
+
if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) {
|
|
661
|
+
const wiringFolder = boundedContextFolder.subitem([
|
|
662
|
+
'wiring',
|
|
663
|
+
'presentation',
|
|
664
|
+
'rest',
|
|
665
|
+
controller
|
|
666
|
+
]);
|
|
667
|
+
const wireNameBase = `wire-${nameBase}`;
|
|
668
|
+
const wireNaming = new UnknownFormatNaming(wireNameBase);
|
|
669
|
+
wiringFolder.createFile(`${wireNaming.fileName}.ts`, `
|
|
670
|
+
import { wireClass } from '@domain-first/wire'
|
|
671
|
+
import { ${endpointNaming.ClassName} } from '../../../../presentation/rest/${controller}/${endpointNaming.fileName}'
|
|
672
|
+
|
|
673
|
+
export const wire${endpointNaming.ClassName} = wireClass(
|
|
674
|
+
${endpointNaming.ClassName},
|
|
675
|
+
[]
|
|
676
|
+
)
|
|
677
|
+
`);
|
|
678
|
+
wiringFolder.file('index.ts').addLine(`export * from './${wireNaming.fileName}'`);
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
const scaffoldNewQuery = async (boundedContextFolder)=>{
|
|
682
|
+
const queriesFolder = boundedContextFolder.subitem([
|
|
683
|
+
'application',
|
|
684
|
+
'queries'
|
|
685
|
+
]);
|
|
686
|
+
const infrastructureFolder = boundedContextFolder.subitem([
|
|
687
|
+
'infrastructure',
|
|
688
|
+
'queries'
|
|
689
|
+
]);
|
|
690
|
+
const queryName = await (0, prompts_namespaceObject.input)({
|
|
691
|
+
message: 'Name: '
|
|
692
|
+
});
|
|
693
|
+
const naming = new UnknownFormatNaming(queryName);
|
|
694
|
+
const implementationType = await (0, prompts_namespaceObject.input)({
|
|
695
|
+
message: 'Query infrastructure implementation type:',
|
|
696
|
+
default: ConfigFile.Instance.data.defaultPersistenceLayerImplementation
|
|
697
|
+
});
|
|
698
|
+
let addTestImplementation = false;
|
|
699
|
+
let testImplementationType = '';
|
|
700
|
+
addTestImplementation = await (0, prompts_namespaceObject.confirm)({
|
|
701
|
+
message: 'Add a test implementation for Query'
|
|
702
|
+
});
|
|
703
|
+
if (addTestImplementation) testImplementationType = await (0, prompts_namespaceObject.input)({
|
|
704
|
+
message: 'Test Query infrastructure implementation type:',
|
|
705
|
+
default: 'in-memory'
|
|
706
|
+
});
|
|
707
|
+
const implementationTypes = [
|
|
708
|
+
implementationType,
|
|
709
|
+
testImplementationType
|
|
710
|
+
].filter((x)=>!!x);
|
|
711
|
+
for (const implementation of implementationTypes){
|
|
712
|
+
const formatNaming = new UnknownFormatNaming(implementation);
|
|
713
|
+
infrastructureFolder.subitem([
|
|
714
|
+
formatNaming.fileName
|
|
715
|
+
]).createFile(`${formatNaming.fileName}-${naming.fileName}-query.ts`, ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/handlers') ? `
|
|
716
|
+
import { defineHandler } from '@domain-first/handlers'
|
|
717
|
+
import { ${naming.ClassName}Query } from '../../../application/queries/${naming.fileName}-query'
|
|
718
|
+
|
|
719
|
+
export class ${formatNaming.ClassName}${naming.ClassName}Query implements ${naming.ClassName}Query {
|
|
720
|
+
constructor() {}
|
|
721
|
+
|
|
722
|
+
handle = defineHandler({
|
|
723
|
+
inputSchema: ${naming.ClassName}Query.inputSchema,
|
|
724
|
+
outputSchema: ${naming.ClassName}Query.outputSchema,
|
|
725
|
+
handler: async (input) => {
|
|
726
|
+
return {}
|
|
727
|
+
}
|
|
728
|
+
})
|
|
729
|
+
}
|
|
730
|
+
`.trim() : `
|
|
731
|
+
import { ${naming.ClassName}Query } from '../../../domain/queries/${naming.fileName}-query'
|
|
732
|
+
|
|
733
|
+
export class ${formatNaming.ClassName}${naming.ClassName}Query implements ${naming.ClassName}Query {
|
|
734
|
+
|
|
735
|
+
}
|
|
736
|
+
`.trim());
|
|
737
|
+
if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) {
|
|
738
|
+
const wiringFolder = boundedContextFolder.subitem([
|
|
739
|
+
'wiring',
|
|
740
|
+
'infrastructure',
|
|
741
|
+
'queries',
|
|
742
|
+
formatNaming.fileName
|
|
743
|
+
]);
|
|
744
|
+
wiringFolder.createFile(`wire-${formatNaming.fileName}-${naming.fileName}-query.ts`, `
|
|
745
|
+
import { wireClass } from '@domain-first/wire'
|
|
746
|
+
import { ${formatNaming.ClassName}${naming.ClassName}Query } from '../../../../infrastructure/queries/${formatNaming.fileName}/${formatNaming.fileName}-${naming.fileName}-query'
|
|
747
|
+
|
|
748
|
+
export const wire${formatNaming.ClassName}${naming.ClassName}Query = wireClass(
|
|
749
|
+
${formatNaming.ClassName}${naming.ClassName}Query,
|
|
750
|
+
[]
|
|
751
|
+
)
|
|
752
|
+
`.trim());
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/handlers')) queriesFolder.createFile(`${naming.fileName}-query.ts`, `
|
|
756
|
+
import type { Handler } from '@domain-first/handlers'
|
|
757
|
+
|
|
758
|
+
export abstract class ${naming.ClassName}Query {
|
|
759
|
+
static inputSchema = {}
|
|
760
|
+
static outputSchema = {}
|
|
761
|
+
|
|
762
|
+
abstract handle: Handler<typeof ${naming.ClassName}Query.inputSchema, typeof ${naming.ClassName}Query.outputSchema>
|
|
763
|
+
}
|
|
764
|
+
`.trim());
|
|
765
|
+
else queriesFolder.createFile(`${naming.fileName}-query.ts`, `
|
|
766
|
+
export abstract class ${naming.ClassName}Query {
|
|
767
|
+
|
|
768
|
+
}`.trim());
|
|
769
|
+
if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) {
|
|
770
|
+
const implementationNaming = new UnknownFormatNaming(implementationType);
|
|
771
|
+
const testImplementationNaming = new UnknownFormatNaming(testImplementationType);
|
|
772
|
+
boundedContextFolder.subitem([
|
|
773
|
+
'wiring',
|
|
774
|
+
'application',
|
|
775
|
+
'queries'
|
|
776
|
+
]).createFile(`wire-${naming.fileName}-query.ts`, `
|
|
777
|
+
import { envBranchedWire } from '../../../../../shared/wiring/env-branched-wire'
|
|
778
|
+
${implementationTypes.map((x)=>new UnknownFormatNaming(x)).map(({ ClassName, fileName })=>`import { wire${ClassName}${naming.ClassName}Query } from '../../infrastructure/queries/${fileName}/wire-${fileName}-${naming.fileName}-query'`).join('\n')}
|
|
779
|
+
|
|
780
|
+
export const wire${naming.ClassName}Query = envBranchedWire({
|
|
781
|
+
test: wire${addTestImplementation ? testImplementationNaming.ClassName : implementationNaming.ClassName}${naming.ClassName}Query,
|
|
782
|
+
development: wire${implementationNaming.ClassName}${naming.ClassName}Query,
|
|
783
|
+
production: wire${implementationNaming.ClassName}${naming.ClassName}Query
|
|
784
|
+
})
|
|
785
|
+
`.trim());
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
const scaffoldNewUseCase = async (boundedContextFolder)=>{
|
|
789
|
+
const useCasesFolder = boundedContextFolder.subitem([
|
|
790
|
+
'application',
|
|
791
|
+
'use-cases'
|
|
792
|
+
]);
|
|
793
|
+
const useCaseName = await (0, prompts_namespaceObject.input)({
|
|
794
|
+
message: 'Name: '
|
|
795
|
+
});
|
|
796
|
+
const naming = new UnknownFormatNaming(useCaseName);
|
|
797
|
+
useCasesFolder.createFile(`${naming.fileName}-use-case.ts`, ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/handlers') ? `
|
|
798
|
+
import { defineHandler } from '@domain-first/handlers'
|
|
799
|
+
|
|
800
|
+
export class ${naming.ClassName}UseCase {
|
|
801
|
+
constructor() {}
|
|
802
|
+
|
|
803
|
+
static inputSchema = {}
|
|
804
|
+
static outputSchema = {}
|
|
805
|
+
|
|
806
|
+
handle = defineHandler({
|
|
807
|
+
inputSchema: ${naming.ClassName}UseCase.inputSchema,
|
|
808
|
+
outputSchema: ${naming.ClassName}UseCase.outputSchema,
|
|
809
|
+
handler: async (input) => {
|
|
810
|
+
return {}
|
|
811
|
+
}
|
|
812
|
+
})
|
|
813
|
+
}
|
|
814
|
+
`.trim() : `
|
|
815
|
+
export class ${naming.ClassName}UseCase {
|
|
816
|
+
|
|
817
|
+
}
|
|
818
|
+
`.trim());
|
|
819
|
+
if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) boundedContextFolder.subitem([
|
|
820
|
+
'wiring',
|
|
821
|
+
'application',
|
|
822
|
+
'use-cases'
|
|
823
|
+
]).createFile(`wire-${naming.fileName}-use-case.ts`, `
|
|
824
|
+
import { wireClass } from '@domain-first/wire'
|
|
825
|
+
import { ${naming.ClassName}UseCase } from '../../../application/use-cases/${naming.fileName}-use-case'
|
|
826
|
+
|
|
827
|
+
export const wire${naming.ClassName}UseCase = wireClass(
|
|
828
|
+
${naming.ClassName}UseCase,
|
|
829
|
+
[]
|
|
830
|
+
)
|
|
831
|
+
|
|
832
|
+
`.trim());
|
|
833
|
+
};
|
|
834
|
+
const main = async ()=>{
|
|
835
|
+
if (!ConfigFile.exists) return void await initializeUseCase();
|
|
836
|
+
const rootPath = new ProjectRoot().path;
|
|
837
|
+
const currentBoundedContextOptions = new Folder(rootPath).subitem([
|
|
838
|
+
ConfigFile.Instance.data.rootFolder,
|
|
839
|
+
'bounded-contexts'
|
|
840
|
+
]).content.subfolderNames.map((name)=>({
|
|
841
|
+
name: `Bounded Context: ${name}`,
|
|
842
|
+
action: 'Scaffold in bounded context'
|
|
843
|
+
}));
|
|
844
|
+
const scaffoldNewBoundedContext = {
|
|
845
|
+
name: 'Scaffold new bounded context',
|
|
846
|
+
action: 'Scaffold new bounded context'
|
|
847
|
+
};
|
|
848
|
+
const options = [
|
|
849
|
+
...currentBoundedContextOptions,
|
|
850
|
+
{
|
|
851
|
+
name: 'Shared Layer',
|
|
852
|
+
action: 'Scaffold in bounded context'
|
|
853
|
+
},
|
|
854
|
+
scaffoldNewBoundedContext
|
|
855
|
+
];
|
|
856
|
+
const result = await (0, prompts_namespaceObject.select)({
|
|
857
|
+
message: '@domain-first/project-structure',
|
|
858
|
+
choices: options.map((x)=>x.name)
|
|
859
|
+
});
|
|
860
|
+
const { action, name } = options.find((x)=>x.name === result);
|
|
861
|
+
switch(action){
|
|
862
|
+
case 'Scaffold new bounded context':
|
|
863
|
+
return void await scaffoldNewBoundedContextUseCase();
|
|
864
|
+
case 'Scaffold in bounded context':
|
|
865
|
+
{
|
|
866
|
+
const specificContext = name.startsWith('Bounded');
|
|
867
|
+
const boundedContextFolder = new Folder(new ProjectRoot().path).subitem(specificContext ? [
|
|
868
|
+
ConfigFile.Instance.data.rootFolder,
|
|
869
|
+
'bounded-contexts',
|
|
870
|
+
name.substring(17)
|
|
871
|
+
] : [
|
|
872
|
+
ConfigFile.Instance.data.rootFolder,
|
|
873
|
+
'shared'
|
|
874
|
+
]);
|
|
875
|
+
const layer = await (0, prompts_namespaceObject.select)({
|
|
876
|
+
message: 'Layer:',
|
|
877
|
+
choices: [
|
|
878
|
+
'Domain',
|
|
879
|
+
'Application',
|
|
880
|
+
'Presentation'
|
|
881
|
+
]
|
|
882
|
+
});
|
|
883
|
+
if ('Domain' === layer) {
|
|
884
|
+
const operation = await (0, prompts_namespaceObject.select)({
|
|
885
|
+
message: 'Domain layer operation:',
|
|
886
|
+
choices: specificContext ? [
|
|
887
|
+
'New Aggregate',
|
|
888
|
+
'Errors',
|
|
889
|
+
'New Service'
|
|
890
|
+
] : [
|
|
891
|
+
'Errors'
|
|
892
|
+
]
|
|
893
|
+
});
|
|
894
|
+
switch(operation){
|
|
895
|
+
case 'New Aggregate':
|
|
896
|
+
return void await scaffoldNewAggregateUseCase(boundedContextFolder);
|
|
897
|
+
case 'Errors':
|
|
898
|
+
return void await scaffoldNewErrorUseCase(boundedContextFolder);
|
|
899
|
+
case 'New Service':
|
|
900
|
+
return void await scaffoldNewDomainService(boundedContextFolder);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
if ('Application' === layer) {
|
|
904
|
+
const operation = await (0, prompts_namespaceObject.select)({
|
|
905
|
+
message: 'Application layer operation:',
|
|
906
|
+
choices: specificContext ? [
|
|
907
|
+
'New Query',
|
|
908
|
+
'New Command',
|
|
909
|
+
'New Use Case',
|
|
910
|
+
'New Port'
|
|
911
|
+
] : [
|
|
912
|
+
'New Port'
|
|
913
|
+
]
|
|
914
|
+
});
|
|
915
|
+
switch(operation){
|
|
916
|
+
case 'New Query':
|
|
917
|
+
return void await scaffoldNewQuery(boundedContextFolder);
|
|
918
|
+
case 'New Command':
|
|
919
|
+
return void await scaffoldNewCommand(boundedContextFolder);
|
|
920
|
+
case 'New Use Case':
|
|
921
|
+
return void await scaffoldNewUseCase(boundedContextFolder);
|
|
922
|
+
case 'New Port':
|
|
923
|
+
return void await scaffoldNewApplicationPort(boundedContextFolder);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
if ('Presentation' === layer) {
|
|
927
|
+
const action = await (0, prompts_namespaceObject.select)({
|
|
928
|
+
message: 'Presentation layer operation:',
|
|
929
|
+
choices: specificContext ? [
|
|
930
|
+
ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/handlers-rest') ? 'New Handlers-REST Endpoint' : void 0
|
|
931
|
+
].filter((x)=>x) : [
|
|
932
|
+
''
|
|
933
|
+
]
|
|
934
|
+
});
|
|
935
|
+
switch(action){
|
|
936
|
+
case 'New Handlers-REST Endpoint':
|
|
937
|
+
return void await scaffoldNewHandlersRestEndpoint(boundedContextFolder);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
default:
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
};
|
|
946
|
+
main();
|
|
947
|
+
for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
948
|
+
Object.defineProperty(exports, '__esModule', {
|
|
949
|
+
value: true
|
|
950
|
+
});
|