@domain-first/project-structure 1.1.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -94,7 +94,8 @@ const configSchema = zod.object({
94
94
  '@domain-first/wire',
95
95
  '@domain-first/handlers-rest'
96
96
  ])),
97
- defaultPersistenceLayerImplementation: zod.string()
97
+ defaultPersistenceLayerImplementation: zod.string(),
98
+ useReact: zod.boolean()
98
99
  });
99
100
  class ConfigFile {
100
101
  data;
@@ -136,10 +137,14 @@ const initializeUseCase = async ()=>{
136
137
  const defaultPersistenceLayerImplementation = await input({
137
138
  message: 'Default persistence layer implementation:'
138
139
  });
140
+ const useReact = await prompts_confirm({
141
+ message: 'Use React'
142
+ });
139
143
  ConfigFile.write({
140
144
  rootFolder: rootFolderName,
141
145
  domainFirstPackages,
142
- defaultPersistenceLayerImplementation
146
+ defaultPersistenceLayerImplementation,
147
+ useReact
143
148
  });
144
149
  const { path } = new ProjectRoot();
145
150
  const rootFolder = new Folder(path);
@@ -247,41 +252,26 @@ export class ${naming.ClassName}${aggregateNaming.ClassName}Repository implement
247
252
  }
248
253
 
249
254
  `.trim());
250
- if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) {
251
- const wiringFolder = boundedContextFolder.subitem([
252
- 'wiring',
253
- 'infrastructure',
254
- 'repositories',
255
- naming.fileName
256
- ]);
257
- wiringFolder.createFile(`wire-${naming.fileName}-${aggregateNaming.fileName}-repository.ts`, `
258
- import { wireClass } from '@domain-first/wire'
259
- import { ${naming.ClassName}${aggregateNaming.ClassName}Repository } from '../../../../infrastructure/repositories/${naming.fileName}/${naming.fileName}-${aggregateNaming.fileName}-repository'
260
-
261
- export const wire${naming.ClassName}${aggregateNaming.ClassName}Repository = wireClass(
262
- ${naming.ClassName}${aggregateNaming.ClassName}Repository,
263
- []
264
- )
265
- `.trim());
266
- }
267
255
  }
268
256
  if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) {
269
257
  const implementationNaming = new UnknownFormatNaming(infrastructureImplementationType);
270
258
  const testImplementationNaming = new UnknownFormatNaming(testImplementationType);
271
259
  boundedContextFolder.subitem([
272
260
  'wiring',
273
- 'domain',
274
261
  'repositories'
275
262
  ]).createFile(`wire-${aggregateNaming.fileName}-repository.ts`, `
276
- import { envBranchedWire } from '../../../../../shared/wiring/env-branched-wire'
277
- ${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')}
263
+ import { wireClass } from '@domain-first/wire'
264
+ import { envBranchedWire } from '../../../../shared/wiring/env-branched-wire'
265
+ ${implementationTypes.map((x)=>new UnknownFormatNaming(x)).map(({ ClassName, fileName })=>`import { ${ClassName}${aggregateNaming.ClassName}Repository } from '../../infrastructure/repositories/${fileName}/${fileName}-${aggregateNaming.fileName}-repository'`).join('\n')}
266
+
267
+ ${implementationTypes.map((x)=>new UnknownFormatNaming(x)).map(({ ClassName })=>`const wire${ClassName}Implementation = wireClass(${ClassName}${aggregateNaming.ClassName}Repository, [])`).join('\n\n')}
278
268
 
279
269
  export const wire${aggregateNaming.ClassName}Repository = envBranchedWire({
280
- test: wire${addTestImplementation ? testImplementationNaming.ClassName : implementationNaming.ClassName}${aggregateNaming.ClassName}Repository,
281
- development: wire${implementationNaming.ClassName}${aggregateNaming.ClassName}Repository,
282
- production: wire${implementationNaming.ClassName}${aggregateNaming.ClassName}Repository
270
+ test: wire${addTestImplementation ? testImplementationNaming.ClassName : implementationNaming.ClassName}Implementation,
271
+ development: wire${implementationNaming.ClassName}Implementation,
272
+ production: wire${implementationNaming.ClassName}Implementation
283
273
  })
284
- `.trim());
274
+ `.trim());
285
275
  }
286
276
  }
287
277
  };
@@ -292,14 +282,8 @@ const scaffoldNewApplicationPort = async (boundedContextFolder)=>{
292
282
  const naming = new UnknownFormatNaming(name);
293
283
  const wiringFolder = boundedContextFolder.subitem([
294
284
  'wiring',
295
- 'application',
296
285
  'ports'
297
286
  ]);
298
- const infraFolder = boundedContextFolder.subitem([
299
- 'wiring',
300
- 'infrastructure',
301
- 'application-ports'
302
- ]);
303
287
  const applicationPortsFolder = boundedContextFolder.subitem([
304
288
  'application',
305
289
  'ports'
@@ -339,29 +323,24 @@ export class ${currentNaming.ClassName} extends ${naming.ClassName} {
339
323
 
340
324
  }
341
325
  `);
342
- if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) infraFolder.subitem([
343
- implementation
344
- ]).createFile(`wire-${currentNaming.fileName}.ts`, `
345
- import { wireClass } from '@domain-first/wire'
346
- import { ${currentNaming.ClassName} } from '../../../../infrastructure/application-adapters/${implementation}/${currentNaming.fileName}'
347
-
348
- export const wire${currentNaming.ClassName} = wireClass(
349
- ${currentNaming.ClassName},
350
- []
351
- )
352
- `.trim());
353
326
  }
354
327
  if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) wiringFolder.createFile(`wire-${naming.fileName}.ts`, `
355
- import { envBranchedWire } from '../../../../${'shared' === boundedContextFolder.name ? '' : '../'}shared/wiring/env-branched-wire'
328
+ import { wireClass } from '@domain-first/wire'
329
+ import { envBranchedWire } from '../../../${'shared' === boundedContextFolder.name ? '' : '../'}shared/wiring/env-branched-wire'
330
+ ${implementationTypes.map((x)=>({
331
+ naming: new UnknownFormatNaming(`${x}-${naming.fileName}`),
332
+ implementation: x
333
+ })).map(({ naming: { ClassName, fileName }, implementation })=>`import { ${ClassName} } from '../../infrastructure/application-adapters/${implementation}/${fileName}'`).join('\n')}
334
+
356
335
  ${implementationTypes.map((x)=>({
357
336
  naming: new UnknownFormatNaming(`${x}-${naming.fileName}`),
358
337
  implementation: x
359
- })).map(({ naming: { ClassName, fileName }, implementation })=>`import { wire${ClassName} } from '../../infrastructure/application-ports/${implementation}/wire-${fileName}'`).join('\n')}
338
+ })).map(({ naming: { ClassName }, implementation })=>`const wire${new UnknownFormatNaming(implementation).ClassName}Implementation = wireClass(${ClassName}, [])`).join('\n\n')}
360
339
 
361
340
  export const wire${naming.ClassName} = envBranchedWire({
362
- test: wire${new UnknownFormatNaming(`${implementationTypes.at(-1)}-${naming.fileName}`).ClassName},
363
- development: wire${new UnknownFormatNaming(`${implementationTypes.at(0)}-${naming.fileName}`).ClassName},
364
- production: wire${new UnknownFormatNaming(`${implementationTypes.at(0)}-${naming.fileName}`).ClassName}
341
+ test: wire${new UnknownFormatNaming(`${implementationTypes.at(-1)}-implementation`).ClassName},
342
+ development: wire${new UnknownFormatNaming(`${implementationTypes.at(0)}-implementation`).ClassName},
343
+ production: wire${new UnknownFormatNaming(`${implementationTypes.at(0)}-implementation`).ClassName}
365
344
  })
366
345
 
367
346
  `.trim());
@@ -401,9 +380,14 @@ const scaffoldNewCommand = async (boundedContextFolder)=>{
401
380
  message: 'Name: '
402
381
  });
403
382
  const naming = new UnknownFormatNaming(commandName);
404
- const isOrchestrator = await prompts_confirm({
405
- message: 'Is an orchestrator command (no infrastructure implementations)?'
383
+ const commandType = await prompts_select({
384
+ choices: [
385
+ 'Execution (with infrastructure implementation)',
386
+ 'Orchestrator (no infrastructure implementation)'
387
+ ],
388
+ message: 'Command type'
406
389
  });
390
+ const isOrchestrator = 'Orchestrator (no infrastructure implementation)' === commandType;
407
391
  if (!isOrchestrator) {
408
392
  const implementationType = await input({
409
393
  message: 'Command infrastructure implementation type:',
@@ -427,81 +411,73 @@ const scaffoldNewCommand = async (boundedContextFolder)=>{
427
411
  infrastructureFolder.subitem([
428
412
  formatNaming.fileName
429
413
  ]).createFile(`${formatNaming.fileName}-${naming.fileName}-command.ts`, ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/handlers') ? `
430
- import { defineHandler } from '@domain-first/handlers'
431
- import { ${naming.ClassName}Command } from '../../../application/commands/${naming.fileName}-command'
432
-
433
- export class ${formatNaming.ClassName}${naming.ClassName}Command implements ${naming.ClassName}Command {
434
- constructor() {}
414
+ import { defineHandler } from '@domain-first/handlers'
415
+ import { ${naming.ClassName}Command } from '../../../application/commands/execution/${naming.fileName}-command'
435
416
 
436
- handle = defineHandler({
437
- inputSchema: ${naming.ClassName}Command.inputSchema,
438
- outputSchema: ${naming.ClassName}Command.outputSchema,
439
- handler: async (input) => {
440
- return {}
441
- }
442
- })
417
+ export class ${formatNaming.ClassName}${naming.ClassName}Command extends ${naming.ClassName}Command {
418
+ constructor() {
419
+ super()
443
420
  }
444
- `.trim() : `
445
- import { ${naming.ClassName}Command } from '../../../domain/commands/${naming.fileName}-command'
446
421
 
447
- export class ${formatNaming.ClassName}${naming.ClassName}Command implements ${naming.ClassName}Command {
422
+ handle = defineHandler({
423
+ inputSchema: ${naming.ClassName}Command.inputSchema,
424
+ outputSchema: ${naming.ClassName}Command.outputSchema,
425
+ handler: async (input) => {
426
+ return {}
427
+ }
428
+ })
429
+ }
430
+ `.trim() : `
431
+ import { ${naming.ClassName}Command } from '../../../domain/commands/${naming.fileName}-command'
448
432
 
433
+ export class ${formatNaming.ClassName}${naming.ClassName}Command extends ${naming.ClassName}Command {
434
+ constructor() {
435
+ super()
449
436
  }
437
+ }
450
438
  `.trim());
451
- if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) {
452
- const wiringFolder = boundedContextFolder.subitem([
453
- 'wiring',
454
- 'infrastructure',
455
- 'commands',
456
- formatNaming.fileName
457
- ]);
458
- wiringFolder.createFile(`wire-${formatNaming.fileName}-${naming.fileName}-command.ts`, `
459
- import { wireClass } from '@domain-first/wire'
460
- import { ${formatNaming.ClassName}${naming.ClassName}Command } from '../../../../infrastructure/commands/${formatNaming.fileName}/${formatNaming.fileName}-${naming.fileName}-command'
461
-
462
- export const wire${formatNaming.ClassName}${naming.ClassName}Command = wireClass(
463
- ${formatNaming.ClassName}${naming.ClassName}Command,
464
- []
465
- )
466
-
467
- `.trim());
468
- }
469
439
  }
470
- if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/handlers')) commandsFolder.createFile(`${naming.fileName}-command.ts`, `
471
- import type { Handler } from '@domain-first/handlers'
440
+ if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/handlers')) commandsFolder.subitem([
441
+ 'execution'
442
+ ]).createFile(`${naming.fileName}-command.ts`, `
443
+ import type { Handler } from '@domain-first/handlers'
472
444
 
473
- export abstract class ${naming.ClassName}Command {
474
- static inputSchema = {}
475
- static outputSchema = {}
445
+ export abstract class ${naming.ClassName}Command {
446
+ static inputSchema = {}
447
+ static outputSchema = {}
476
448
 
477
- abstract handle: Handler<typeof ${naming.ClassName}Command.inputSchema, typeof ${naming.ClassName}Command.outputSchema>
478
- }
449
+ abstract handle: Handler<typeof ${naming.ClassName}Command.inputSchema, typeof ${naming.ClassName}Command.outputSchema>
450
+ }
479
451
  `.trim());
480
452
  else commandsFolder.createFile(`${naming.fileName}-command.ts`, `
481
- export abstract class ${naming.ClassName}Command {
453
+ export abstract class ${naming.ClassName}Command {
482
454
 
483
- }`.trim());
455
+ }`.trim());
484
456
  if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) {
485
457
  const implementationNaming = new UnknownFormatNaming(implementationType);
486
458
  const testImplementationNaming = new UnknownFormatNaming(testImplementationType);
487
459
  boundedContextFolder.subitem([
488
460
  'wiring',
489
- 'application',
490
461
  'commands'
491
462
  ]).createFile(`wire-${naming.fileName}-command.ts`, `
492
- import { envBranchedWire } from '../../../../../shared/wiring/env-branched-wire'
493
- ${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')}
463
+ import { wireClass } from '@domain-first/wire'
464
+ import { envBranchedWire } from '../../../../shared/wiring/env-branched-wire'
465
+ ${implementationTypes.map((x)=>new UnknownFormatNaming(x)).map(({ ClassName, fileName })=>`import { ${ClassName}${naming.ClassName}Command } from '../../infrastructure/commands/${fileName}/${fileName}-${naming.fileName}-command'`).join('\n')}
494
466
 
495
- export const wire${naming.ClassName}Command = envBranchedWire({
496
- test: wire${addTestImplementation ? testImplementationNaming.ClassName : implementationNaming.ClassName}${naming.ClassName}Command,
497
- development: wire${implementationNaming.ClassName}${naming.ClassName}Command,
498
- production: wire${implementationNaming.ClassName}${naming.ClassName}Command
499
- })
467
+ ${implementationTypes.map((x)=>new UnknownFormatNaming(x)).map(({ ClassName })=>`const wire${ClassName}Implementation = wireClass(${ClassName}${naming.ClassName}Command, [])`).join('\n\n')}
468
+
469
+ export const wire${naming.ClassName}Command = envBranchedWire({
470
+ test: wire${addTestImplementation ? testImplementationNaming.ClassName : implementationNaming.ClassName}Implementation,
471
+ development: wire${implementationNaming.ClassName}Implementation,
472
+ production: wire${implementationNaming.ClassName}Implementation
473
+ })
500
474
  `.trim());
501
475
  }
502
476
  return;
503
477
  }
504
- if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/handlers')) commandsFolder.createFile(`${naming.fileName}-command.ts`, `
478
+ if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/handlers')) commandsFolder.subitem([
479
+ 'orchestration'
480
+ ]).createFile(`${naming.fileName}-command.ts`, `
505
481
  import { defineHandler } from '@domain-first/handlers'
506
482
 
507
483
  export class ${naming.ClassName}Command {
@@ -517,7 +493,9 @@ export class ${naming.ClassName}Command {
517
493
  })
518
494
  }
519
495
  `.trim());
520
- else commandsFolder.createFile(`${naming.fileName}-command.ts`, `
496
+ else commandsFolder.subitem([
497
+ 'orchestration'
498
+ ]).createFile(`${naming.fileName}-command.ts`, `
521
499
  export class ${naming.ClassName}Command {
522
500
 
523
501
  }`.trim());
@@ -527,7 +505,7 @@ export class ${naming.ClassName}Command {
527
505
  'commands'
528
506
  ]).createFile(`wire-${naming.fileName}-command.ts`, `
529
507
  import { wireClass } from '@domain-first/wire'
530
- import { ${naming.ClassName}Command } from '../../../application/commands/${naming.fileName}-command'
508
+ import { ${naming.ClassName}Command } from '../../../application/commands/orchestration/${naming.fileName}-command'
531
509
 
532
510
  export const wire${naming.ClassName}Command = wireClass(${naming.ClassName}Command, [])
533
511
  `.trim());
@@ -579,7 +557,7 @@ const scaffoldNewErrorUseCase = async (boundedContextFolder)=>{
579
557
  message: 'Error name:'
580
558
  });
581
559
  const naming = new UnknownFormatNaming(errorName);
582
- currentErrorFolder.file('index.ts').addLine(`export const ${naming.ClassName}Error = ${new UnknownFormatNaming(boundedContextFolder.name).ClassName}Errors.define('${naming.fileName}')`, '\n\n');
560
+ currentErrorFolder.file('index.ts').addLine(`export const ${naming.ClassName}${new UnknownFormatNaming(boundedContextFolder.name).ClassName}Error = ${new UnknownFormatNaming(boundedContextFolder.name).ClassName}Errors.define('${naming.fileName}')`, '\n\n');
583
561
  return;
584
562
  }
585
563
  if ('New namespace' === selectedVariant) {
@@ -590,17 +568,18 @@ const scaffoldNewErrorUseCase = async (boundedContextFolder)=>{
590
568
  currentErrorFolder.createFile(`${fileName}.ts`, `
591
569
  import { errorNamespace } from '@domain-first/errors'
592
570
 
593
- const ${ClassName}Errors = errorNamespace('${boundedContextFolder.name}').subnamespace('${fileName}')
571
+ export const ${ClassName}${new UnknownFormatNaming(boundedContextFolder.name).ClassName}Errors = errorNamespace('${boundedContextFolder.name}').subnamespace('${fileName}')
594
572
  `);
595
- currentErrorFolder.file('index.ts').addLine(`export * from './${fileName}'`);
573
+ currentErrorFolder.file('index.ts').addLine(`export * from './${fileName}'`, '\n\n');
596
574
  return;
597
575
  }
598
576
  const namespace = selectedVariant.substring(23);
577
+ const namespaceNaming = new UnknownFormatNaming(namespace);
599
578
  const errorName = await input({
600
579
  message: 'Error name:'
601
580
  });
602
581
  const { ClassName, fileName } = new UnknownFormatNaming(errorName);
603
- currentErrorFolder.file(`${namespace}.ts`).addLine(`export const ${ClassName}Error = ${new UnknownFormatNaming(namespace).ClassName}Errors.define('${fileName}')`, '\n\n');
582
+ currentErrorFolder.file(`${namespace}.ts`).addLine(`export const ${ClassName}${namespaceNaming.ClassName}${new UnknownFormatNaming(boundedContextFolder.name).ClassName}Error = ${namespaceNaming.ClassName}${new UnknownFormatNaming(boundedContextFolder.name).ClassName}Errors.define('${fileName}')`, '\n\n');
604
583
  };
605
584
  const scaffoldNewHandlersRestEndpoint = async (boundedContextFolder)=>{
606
585
  const restPresentationFolder = boundedContextFolder.subitem([
@@ -619,11 +598,27 @@ const scaffoldNewHandlersRestEndpoint = async (boundedContextFolder)=>{
619
598
  controller = await input({
620
599
  message: 'Name: '
621
600
  });
622
- if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) boundedContextFolder.subitem([
623
- 'wiring',
624
- 'presentation',
625
- 'rest'
626
- ]).file('index.ts').addLine(`export * from './${controller}'`);
601
+ if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) {
602
+ boundedContextFolder.subitem([
603
+ 'wiring',
604
+ 'presentation',
605
+ 'rest'
606
+ ]).file('index.ts').addLine(`export * from './${controller}'`);
607
+ const sharedRestFolder = boundedContextFolder.subitem([
608
+ '..',
609
+ '..',
610
+ 'presentation',
611
+ 'rest'
612
+ ]);
613
+ sharedRestFolder.file('wires.ts').addLine(`export * from '../../bounded-contexts/${boundedContextFolder.name}/wiring/presentation/rest/${controller}'`);
614
+ if (!sharedRestFolder.file('index.ts').exists) sharedRestFolder.createFile('index.ts', `
615
+ import * as wires from './wires'
616
+
617
+ const restWires = Object.values(wires)
618
+
619
+ export const restHandlers = restWires.map(x => x().handle)
620
+ `.trim());
621
+ }
627
622
  }
628
623
  const method = await prompts_select({
629
624
  message: 'Method:',
@@ -641,9 +636,10 @@ const scaffoldNewHandlersRestEndpoint = async (boundedContextFolder)=>{
641
636
  const controllerFolder = restPresentationFolder.subitem([
642
637
  controller
643
638
  ]);
639
+ const endpointFolder = controllerFolder.subitem(path);
644
640
  const nameBase = `${controller}-${path.length ? `${path.join('-')}-` : ''}${method}-endpoint`;
645
641
  const endpointNaming = new UnknownFormatNaming(nameBase);
646
- controllerFolder.createFile(`${endpointNaming.fileName}.ts`, `
642
+ endpointFolder.createFile(`${method.toUpperCase()}.ts`, `
647
643
  import { EndpointGenerator } from '@domain-first/handlers-rest'
648
644
 
649
645
  export class ${endpointNaming.ClassName} {
@@ -659,7 +655,6 @@ export class ${endpointNaming.ClassName} {
659
655
  tags: ['${boundedContextFolder.name}: ${controller}'],
660
656
  })
661
657
  }
662
-
663
658
  }
664
659
  `.trim());
665
660
  if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) {
@@ -673,7 +668,7 @@ export class ${endpointNaming.ClassName} {
673
668
  const wireNaming = new UnknownFormatNaming(wireNameBase);
674
669
  wiringFolder.createFile(`${wireNaming.fileName}.ts`, `
675
670
  import { wireClass } from '@domain-first/wire'
676
- import { ${endpointNaming.ClassName} } from '../../../../presentation/rest/${controller}/${endpointNaming.fileName}'
671
+ import { ${endpointNaming.ClassName} } from '../../../../presentation/rest/${controller}/${path.length ? `${path.join('/')}/` : ''}${method.toUpperCase()}'
677
672
 
678
673
  export const wire${endpointNaming.ClassName} = wireClass(
679
674
  ${endpointNaming.ClassName},
@@ -696,9 +691,14 @@ const scaffoldNewQuery = async (boundedContextFolder)=>{
696
691
  message: 'Name: '
697
692
  });
698
693
  const naming = new UnknownFormatNaming(queryName);
699
- const isOrchestrator = await prompts_confirm({
700
- message: 'Is an orchestrator query (no infrastructure implementations)?'
694
+ const queryType = await prompts_select({
695
+ choices: [
696
+ 'Execution (with infrastructure implementation)',
697
+ 'Orchestrator (no infrastructure implementation)'
698
+ ],
699
+ message: 'Query type'
701
700
  });
701
+ const isOrchestrator = 'Orchestrator (no infrastructure implementation)' === queryType;
702
702
  if (!isOrchestrator) {
703
703
  const implementationType = await input({
704
704
  message: 'Query infrastructure implementation type:',
@@ -722,80 +722,73 @@ const scaffoldNewQuery = async (boundedContextFolder)=>{
722
722
  infrastructureFolder.subitem([
723
723
  formatNaming.fileName
724
724
  ]).createFile(`${formatNaming.fileName}-${naming.fileName}-query.ts`, ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/handlers') ? `
725
- import { defineHandler } from '@domain-first/handlers'
726
- import { ${naming.ClassName}Query } from '../../../application/queries/${naming.fileName}-query'
727
-
728
- export class ${formatNaming.ClassName}${naming.ClassName}Query implements ${naming.ClassName}Query {
729
- constructor() {}
725
+ import { defineHandler } from '@domain-first/handlers'
726
+ import { ${naming.ClassName}Query } from '../../../application/queries/execution/${naming.fileName}-query'
730
727
 
731
- handle = defineHandler({
732
- inputSchema: ${naming.ClassName}Query.inputSchema,
733
- outputSchema: ${naming.ClassName}Query.outputSchema,
734
- handler: async (input) => {
735
- return {}
736
- }
737
- })
728
+ export class ${formatNaming.ClassName}${naming.ClassName}Query extends ${naming.ClassName}Query {
729
+ constructor() {
730
+ super()
738
731
  }
739
- `.trim() : `
740
- import { ${naming.ClassName}Query } from '../../../domain/queries/${naming.fileName}-query'
741
732
 
742
- export class ${formatNaming.ClassName}${naming.ClassName}Query implements ${naming.ClassName}Query {
733
+ handle = defineHandler({
734
+ inputSchema: ${naming.ClassName}Query.inputSchema,
735
+ outputSchema: ${naming.ClassName}Query.outputSchema,
736
+ handler: async (input) => {
737
+ return {}
738
+ }
739
+ })
740
+ }
741
+ `.trim() : `
742
+ import { ${naming.ClassName}Query } from '../../../domain/queries/${naming.fileName}-query'
743
743
 
744
+ export class ${formatNaming.ClassName}${naming.ClassName}Query extends ${naming.ClassName}Query {
745
+ constructor() {
746
+ super()
744
747
  }
748
+ }
745
749
  `.trim());
746
- if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) {
747
- const wiringFolder = boundedContextFolder.subitem([
748
- 'wiring',
749
- 'infrastructure',
750
- 'queries',
751
- formatNaming.fileName
752
- ]);
753
- wiringFolder.createFile(`wire-${formatNaming.fileName}-${naming.fileName}-query.ts`, `
754
- import { wireClass } from '@domain-first/wire'
755
- import { ${formatNaming.ClassName}${naming.ClassName}Query } from '../../../../infrastructure/queries/${formatNaming.fileName}/${formatNaming.fileName}-${naming.fileName}-query'
756
-
757
- export const wire${formatNaming.ClassName}${naming.ClassName}Query = wireClass(
758
- ${formatNaming.ClassName}${naming.ClassName}Query,
759
- []
760
- )
761
- `.trim());
762
- }
763
750
  }
764
- if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/handlers')) queriesFolder.createFile(`${naming.fileName}-query.ts`, `
765
- import type { Handler } from '@domain-first/handlers'
751
+ if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/handlers')) queriesFolder.subitem([
752
+ 'execution'
753
+ ]).createFile(`${naming.fileName}-query.ts`, `
754
+ import type { Handler } from '@domain-first/handlers'
766
755
 
767
- export abstract class ${naming.ClassName}Query {
768
- static inputSchema = {}
769
- static outputSchema = {}
756
+ export abstract class ${naming.ClassName}Query {
757
+ static inputSchema = {}
758
+ static outputSchema = {}
770
759
 
771
- abstract handle: Handler<typeof ${naming.ClassName}Query.inputSchema, typeof ${naming.ClassName}Query.outputSchema>
772
- }
760
+ abstract handle: Handler<typeof ${naming.ClassName}Query.inputSchema, typeof ${naming.ClassName}Query.outputSchema>
761
+ }
773
762
  `.trim());
774
763
  else queriesFolder.createFile(`${naming.fileName}-query.ts`, `
775
- export abstract class ${naming.ClassName}Query {
764
+ export abstract class ${naming.ClassName}Query {
776
765
 
777
- }`.trim());
766
+ }`.trim());
778
767
  if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) {
779
768
  const implementationNaming = new UnknownFormatNaming(implementationType);
780
769
  const testImplementationNaming = new UnknownFormatNaming(testImplementationType);
781
770
  boundedContextFolder.subitem([
782
771
  'wiring',
783
- 'application',
784
772
  'queries'
785
773
  ]).createFile(`wire-${naming.fileName}-query.ts`, `
786
- import { envBranchedWire } from '../../../../../shared/wiring/env-branched-wire'
787
- ${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')}
774
+ import { wireClass } from '@domain-first/wire'
775
+ import { envBranchedWire } from '../../../../shared/wiring/env-branched-wire'
776
+ ${implementationTypes.map((x)=>new UnknownFormatNaming(x)).map(({ ClassName, fileName })=>`import { ${ClassName}${naming.ClassName}Query } from '../../infrastructure/queries/${fileName}/${fileName}-${naming.fileName}-query'`).join('\n')}
788
777
 
789
- export const wire${naming.ClassName}Query = envBranchedWire({
790
- test: wire${addTestImplementation ? testImplementationNaming.ClassName : implementationNaming.ClassName}${naming.ClassName}Query,
791
- development: wire${implementationNaming.ClassName}${naming.ClassName}Query,
792
- production: wire${implementationNaming.ClassName}${naming.ClassName}Query
793
- })
778
+ ${implementationTypes.map((x)=>new UnknownFormatNaming(x)).map(({ ClassName })=>`const wire${ClassName}Implementation = wireClass(${ClassName}${naming.ClassName}Query, [])`).join('\n\n')}
779
+
780
+ export const wire${naming.ClassName}Query = envBranchedWire({
781
+ test: wire${addTestImplementation ? testImplementationNaming.ClassName : implementationNaming.ClassName}Implementation,
782
+ development: wire${implementationNaming.ClassName}Implementation,
783
+ production: wire${implementationNaming.ClassName}Implementation
784
+ })
794
785
  `.trim());
795
786
  }
796
787
  return;
797
788
  }
798
- if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/handlers')) queriesFolder.createFile(`${naming.fileName}-query.ts`, `
789
+ if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/handlers')) queriesFolder.subitem([
790
+ 'orchestration'
791
+ ]).createFile(`${naming.fileName}-query.ts`, `
799
792
  import { defineHandler } from '@domain-first/handlers'
800
793
 
801
794
  export class ${naming.ClassName}Query {
@@ -811,7 +804,9 @@ export class ${naming.ClassName}Query {
811
804
  })
812
805
  }
813
806
  `.trim());
814
- else queriesFolder.createFile(`${naming.fileName}-query.ts`, `
807
+ else queriesFolder.subitem([
808
+ 'orchestration'
809
+ ]).createFile(`${naming.fileName}-query.ts`, `
815
810
  export class ${naming.ClassName}Query {
816
811
 
817
812
  }`.trim());
@@ -821,7 +816,7 @@ export class ${naming.ClassName}Query {
821
816
  'queries'
822
817
  ]).createFile(`wire-${naming.fileName}-query.ts`, `
823
818
  import { wireClass } from '@domain-first/wire'
824
- import { ${naming.ClassName}Query } from '../../../application/queries/${naming.fileName}-query'
819
+ import { ${naming.ClassName}Query } from '../../../application/queries/orchestration/${naming.fileName}-query'
825
820
 
826
821
  export const wire${naming.ClassName}Query = wireClass(${naming.ClassName}Query, [])
827
822
  `.trim());
@@ -859,11 +854,10 @@ export class ${naming.ClassName}UseCase {
859
854
  `.trim());
860
855
  if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/wire')) boundedContextFolder.subitem([
861
856
  'wiring',
862
- 'application',
863
857
  'use-cases'
864
858
  ]).createFile(`wire-${naming.fileName}-use-case.ts`, `
865
859
  import { wireClass } from '@domain-first/wire'
866
- import { ${naming.ClassName}UseCase } from '../../../application/use-cases/${naming.fileName}-use-case'
860
+ import { ${naming.ClassName}UseCase } from '../../application/use-cases/${naming.fileName}-use-case'
867
861
 
868
862
  export const wire${naming.ClassName}UseCase = wireClass(
869
863
  ${naming.ClassName}UseCase,
@@ -872,6 +866,109 @@ export const wire${naming.ClassName}UseCase = wireClass(
872
866
 
873
867
  `.trim());
874
868
  };
869
+ const scaffoldReactWidget = async (presentationReactFolder)=>{
870
+ const widgetName = await input({
871
+ message: 'Name: '
872
+ });
873
+ const widgetType = await prompts_select({
874
+ choices: [
875
+ 'monolithic',
876
+ 'with separated ui',
877
+ 'headless'
878
+ ],
879
+ message: 'Type: '
880
+ });
881
+ const { ClassName, fileName } = new UnknownFormatNaming(widgetName);
882
+ const folder = presentationReactFolder.subitem([
883
+ 'widgets',
884
+ fileName
885
+ ]);
886
+ switch(widgetType){
887
+ case 'monolithic':
888
+ folder.createFile('index.tsx', `
889
+ import { type FC } from 'react'
890
+
891
+ export type ${ClassName}WidgetProps = {}
892
+
893
+ export const ${ClassName}Widget: FC<${ClassName}WidgetProps> = (props) => {
894
+ return <></>
895
+ }
896
+ `.trim());
897
+ break;
898
+ case 'with separated ui':
899
+ folder.createFile('types.ts', `
900
+ export type ${ClassName}WidgetProps = { }
901
+
902
+ export type ${ClassName}WidgetUIProps = { }
903
+ `.trim());
904
+ folder.createFile('index.tsx', `
905
+ import { type FC } from 'react'
906
+ import type { ${ClassName}WidgetProps } from './types'
907
+ import { ${ClassName}WidgetUI } from './ui'
908
+ import { useUIModel } from './hooks/use-ui-model'
909
+
910
+ export const ${ClassName}Widget: FC<${ClassName}WidgetProps> = (props) => {
911
+ const uiModel = useUIModel(props)
912
+ return <${ClassName}WidgetUI {...uiModel} />
913
+ }
914
+
915
+ export type { ${ClassName}WidgetProps }
916
+ `.trim());
917
+ folder.subitem([
918
+ 'ui'
919
+ ]).createFile('index.tsx', `
920
+ import { FC } from 'react'
921
+ import type { ${ClassName}WidgetUIProps } from '../types'
922
+
923
+ export const ${ClassName}WidgetUI: FC<${ClassName}WidgetUIProps> = (props) => {
924
+ return <></>
925
+ }
926
+ `.trim());
927
+ folder.subitem([
928
+ 'hooks'
929
+ ]).createFile('use-ui-model.ts', `
930
+ import type { ${ClassName}WidgetProps, ${ClassName}WidgetUIProps } from '../types'
931
+
932
+ export const useUIModel = (widgetProps: ${ClassName}WidgetProps): ${ClassName}WidgetUIProps => {
933
+ return {}
934
+ }
935
+ `.trim());
936
+ break;
937
+ case 'headless':
938
+ folder.createFile('types.ts', `
939
+ import { type FC } from 'react'
940
+
941
+ export type ${ClassName}WidgetUIProps = { }
942
+
943
+ export type ${ClassName}WidgetProps = {
944
+ UI: FC<${ClassName}WidgetUIProps>
945
+ }
946
+
947
+ `.trim());
948
+ folder.createFile('index.tsx', `
949
+ import { type FC } from 'react'
950
+ import type { ${ClassName}WidgetProps } from './types'
951
+ import { useUIModel } from './hooks/use-ui-model'
952
+
953
+ export const ${ClassName}Widget: FC<${ClassName}WidgetProps> = (props) => {
954
+ const uiModel = useUIModel(props)
955
+ return <props.UI {...uiModel} />
956
+ }
957
+
958
+ export type { ${ClassName}WidgetProps }
959
+ `.trim());
960
+ folder.subitem([
961
+ 'hooks'
962
+ ]).createFile('use-ui-model.ts', `
963
+ import type { ${ClassName}WidgetProps, ${ClassName}WidgetUIProps } from '../types'
964
+
965
+ export const useUIModel = (widgetProps: Omit<${ClassName}WidgetProps, 'UI'>): ${ClassName}WidgetUIProps => {
966
+ return {}
967
+ }
968
+ `.trim());
969
+ break;
970
+ }
971
+ };
875
972
  const main = async ()=>{
876
973
  if (!ConfigFile.exists) return void await initializeUseCase();
877
974
  const rootPath = new ProjectRoot().path;
@@ -888,18 +985,41 @@ const main = async ()=>{
888
985
  };
889
986
  const options = [
890
987
  ...currentBoundedContextOptions,
988
+ ConfigFile.Instance.data.useReact ? {
989
+ name: 'Scaffold React module',
990
+ action: 'Scaffold React module'
991
+ } : void 0,
891
992
  {
892
993
  name: 'Shared Layer',
893
994
  action: 'Scaffold in bounded context'
894
995
  },
895
996
  scaffoldNewBoundedContext
896
- ];
997
+ ].filter((x)=>!!x);
897
998
  const result = await prompts_select({
898
999
  message: '@domain-first/project-structure',
899
1000
  choices: options.map((x)=>x.name)
900
1001
  });
901
1002
  const { action, name } = options.find((x)=>x.name === result);
902
1003
  switch(action){
1004
+ case 'Scaffold React module':
1005
+ {
1006
+ const action = await prompts_select({
1007
+ message: 'Module: ',
1008
+ choices: [
1009
+ 'New widget'
1010
+ ]
1011
+ });
1012
+ switch(action){
1013
+ case 'New widget':
1014
+ return void await scaffoldReactWidget(new Folder(rootPath).subitem([
1015
+ ConfigFile.Instance.data.rootFolder,
1016
+ 'presentation',
1017
+ 'react'
1018
+ ]));
1019
+ default:
1020
+ return;
1021
+ }
1022
+ }
903
1023
  case 'Scaffold new bounded context':
904
1024
  return void await scaffoldNewBoundedContextUseCase();
905
1025
  case 'Scaffold in bounded context':