@domain-first/project-structure 1.1.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,6 +9,7 @@ declare const configSchema: z.ZodObject<{
9
9
  "@domain-first/handlers-rest": "@domain-first/handlers-rest";
10
10
  }>>;
11
11
  defaultPersistenceLayerImplementation: z.ZodString;
12
+ useReact: z.ZodBoolean;
12
13
  }, z.core.$strip>;
13
14
  export declare class ConfigFile {
14
15
  readonly data: z.infer<typeof configSchema>;
package/dist/index.cjs CHANGED
@@ -124,7 +124,8 @@ const configSchema = external_zod_default().object({
124
124
  '@domain-first/wire',
125
125
  '@domain-first/handlers-rest'
126
126
  ])),
127
- defaultPersistenceLayerImplementation: external_zod_default().string()
127
+ defaultPersistenceLayerImplementation: external_zod_default().string(),
128
+ useReact: external_zod_default().boolean()
128
129
  });
129
130
  class ConfigFile {
130
131
  data;
@@ -166,10 +167,14 @@ const initializeUseCase = async ()=>{
166
167
  const defaultPersistenceLayerImplementation = await (0, prompts_namespaceObject.input)({
167
168
  message: 'Default persistence layer implementation:'
168
169
  });
170
+ const useReact = await (0, prompts_namespaceObject.confirm)({
171
+ message: 'Use React'
172
+ });
169
173
  ConfigFile.write({
170
174
  rootFolder: rootFolderName,
171
175
  domainFirstPackages,
172
- defaultPersistenceLayerImplementation
176
+ defaultPersistenceLayerImplementation,
177
+ useReact
173
178
  });
174
179
  const { path } = new ProjectRoot();
175
180
  const rootFolder = new Folder(path);
@@ -532,7 +537,7 @@ const scaffoldNewCommand = async (boundedContextFolder)=>{
532
537
  return;
533
538
  }
534
539
  if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/handlers')) commandsFolder.createFile(`${naming.fileName}-command.ts`, `
535
- import { defineHandler, type Handler } from '@domain-first/handlers'
540
+ import { defineHandler } from '@domain-first/handlers'
536
541
 
537
542
  export class ${naming.ClassName}Command {
538
543
  static inputSchema = {}
@@ -826,7 +831,7 @@ const scaffoldNewQuery = async (boundedContextFolder)=>{
826
831
  return;
827
832
  }
828
833
  if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/handlers')) queriesFolder.createFile(`${naming.fileName}-query.ts`, `
829
- import { defineHandler, type Handler } from '@domain-first/handlers'
834
+ import { defineHandler } from '@domain-first/handlers'
830
835
 
831
836
  export class ${naming.ClassName}Query {
832
837
  static inputSchema = {}
@@ -902,6 +907,109 @@ export const wire${naming.ClassName}UseCase = wireClass(
902
907
 
903
908
  `.trim());
904
909
  };
910
+ const scaffoldReactWidget = async (presentationReactFolder)=>{
911
+ const widgetName = await (0, prompts_namespaceObject.input)({
912
+ message: 'Name: '
913
+ });
914
+ const widgetType = await (0, prompts_namespaceObject.select)({
915
+ choices: [
916
+ 'monolithic',
917
+ 'with separated ui',
918
+ 'headless'
919
+ ],
920
+ message: 'Type: '
921
+ });
922
+ const { ClassName, fileName } = new UnknownFormatNaming(widgetName);
923
+ const folder = presentationReactFolder.subitem([
924
+ 'widgets',
925
+ fileName
926
+ ]);
927
+ switch(widgetType){
928
+ case 'monolithic':
929
+ folder.createFile('index.tsx', `
930
+ import { type FC } from 'react'
931
+
932
+ export type ${ClassName}WidgetProps = {}
933
+
934
+ export const ${ClassName}Widget: FC<${ClassName}WidgetProps> = (props) => {
935
+ return <></>
936
+ }
937
+ `.trim());
938
+ break;
939
+ case 'with separated ui':
940
+ folder.createFile('types.ts', `
941
+ export type ${ClassName}WidgetProps = { }
942
+
943
+ export type ${ClassName}WidgetUIProps = { }
944
+ `.trim());
945
+ folder.createFile('index.tsx', `
946
+ import { type FC } from 'react'
947
+ import type { ${ClassName}WidgetProps } from './types'
948
+ import { ${ClassName}WidgetUI } from './ui'
949
+ import { useUIModel } from './hooks/use-ui-model'
950
+
951
+ export const ${ClassName}Widget: FC<${ClassName}WidgetProps> = (props) => {
952
+ const uiModel = useUIModel(props)
953
+ return <${ClassName}WidgetUI {...uiModel} />
954
+ }
955
+
956
+ export type { ${ClassName}WidgetProps }
957
+ `.trim());
958
+ folder.subitem([
959
+ 'ui'
960
+ ]).createFile('index.tsx', `
961
+ import { FC } from 'react'
962
+ import type { ${ClassName}WidgetUIProps } from '../types'
963
+
964
+ export const ${ClassName}WidgetUI: FC<${ClassName}WidgetUIProps> = (props) => {
965
+ return <></>
966
+ }
967
+ `.trim());
968
+ folder.subitem([
969
+ 'hooks'
970
+ ]).createFile('use-ui-model.ts', `
971
+ import type { ${ClassName}WidgetProps, ${ClassName}WidgetUIProps } from '../types'
972
+
973
+ export const useUIModel = (widgetProps: ${ClassName}WidgetProps): ${ClassName}WidgetUIProps => {
974
+ return {}
975
+ }
976
+ `.trim());
977
+ break;
978
+ case 'headless':
979
+ folder.createFile('types.ts', `
980
+ import { type FC } from 'react'
981
+
982
+ export type ${ClassName}WidgetUIProps = { }
983
+
984
+ export type ${ClassName}WidgetProps = {
985
+ UI: FC<${ClassName}WidgetUIProps>
986
+ }
987
+
988
+ `.trim());
989
+ folder.createFile('index.tsx', `
990
+ import { type FC } from 'react'
991
+ import type { ${ClassName}WidgetProps } from './types'
992
+ import { useUIModel } from './hooks/use-ui-model'
993
+
994
+ export const ${ClassName}Widget: FC<${ClassName}WidgetProps> = (props) => {
995
+ const uiModel = useUIModel(props)
996
+ return <props.UI {...uiModel} />
997
+ }
998
+
999
+ export type { ${ClassName}WidgetProps }
1000
+ `.trim());
1001
+ folder.subitem([
1002
+ 'hooks'
1003
+ ]).createFile('use-ui-model.ts', `
1004
+ import type { ${ClassName}WidgetProps, ${ClassName}WidgetUIProps } from '../types'
1005
+
1006
+ export const useUIModel = (widgetProps: Omit<${ClassName}WidgetProps, 'UI'>): ${ClassName}WidgetUIProps => {
1007
+ return {}
1008
+ }
1009
+ `.trim());
1010
+ break;
1011
+ }
1012
+ };
905
1013
  const main = async ()=>{
906
1014
  if (!ConfigFile.exists) return void await initializeUseCase();
907
1015
  const rootPath = new ProjectRoot().path;
@@ -918,18 +1026,41 @@ const main = async ()=>{
918
1026
  };
919
1027
  const options = [
920
1028
  ...currentBoundedContextOptions,
1029
+ ConfigFile.Instance.data.useReact ? {
1030
+ name: 'Scaffold React module',
1031
+ action: 'Scaffold React module'
1032
+ } : void 0,
921
1033
  {
922
1034
  name: 'Shared Layer',
923
1035
  action: 'Scaffold in bounded context'
924
1036
  },
925
1037
  scaffoldNewBoundedContext
926
- ];
1038
+ ].filter((x)=>!!x);
927
1039
  const result = await (0, prompts_namespaceObject.select)({
928
1040
  message: '@domain-first/project-structure',
929
1041
  choices: options.map((x)=>x.name)
930
1042
  });
931
1043
  const { action, name } = options.find((x)=>x.name === result);
932
1044
  switch(action){
1045
+ case 'Scaffold React module':
1046
+ {
1047
+ const action = await (0, prompts_namespaceObject.select)({
1048
+ message: 'Module: ',
1049
+ choices: [
1050
+ 'New widget'
1051
+ ]
1052
+ });
1053
+ switch(action){
1054
+ case 'New widget':
1055
+ return void await scaffoldReactWidget(new Folder(rootPath).subitem([
1056
+ ConfigFile.Instance.data.rootFolder,
1057
+ 'presentation',
1058
+ 'react'
1059
+ ]));
1060
+ default:
1061
+ return;
1062
+ }
1063
+ }
933
1064
  case 'Scaffold new bounded context':
934
1065
  return void await scaffoldNewBoundedContextUseCase();
935
1066
  case 'Scaffold in bounded context':
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);
@@ -502,7 +507,7 @@ const scaffoldNewCommand = async (boundedContextFolder)=>{
502
507
  return;
503
508
  }
504
509
  if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/handlers')) commandsFolder.createFile(`${naming.fileName}-command.ts`, `
505
- import { defineHandler, type Handler } from '@domain-first/handlers'
510
+ import { defineHandler } from '@domain-first/handlers'
506
511
 
507
512
  export class ${naming.ClassName}Command {
508
513
  static inputSchema = {}
@@ -796,7 +801,7 @@ const scaffoldNewQuery = async (boundedContextFolder)=>{
796
801
  return;
797
802
  }
798
803
  if (ConfigFile.Instance.data.domainFirstPackages.includes('@domain-first/handlers')) queriesFolder.createFile(`${naming.fileName}-query.ts`, `
799
- import { defineHandler, type Handler } from '@domain-first/handlers'
804
+ import { defineHandler } from '@domain-first/handlers'
800
805
 
801
806
  export class ${naming.ClassName}Query {
802
807
  static inputSchema = {}
@@ -872,6 +877,109 @@ export const wire${naming.ClassName}UseCase = wireClass(
872
877
 
873
878
  `.trim());
874
879
  };
880
+ const scaffoldReactWidget = async (presentationReactFolder)=>{
881
+ const widgetName = await input({
882
+ message: 'Name: '
883
+ });
884
+ const widgetType = await prompts_select({
885
+ choices: [
886
+ 'monolithic',
887
+ 'with separated ui',
888
+ 'headless'
889
+ ],
890
+ message: 'Type: '
891
+ });
892
+ const { ClassName, fileName } = new UnknownFormatNaming(widgetName);
893
+ const folder = presentationReactFolder.subitem([
894
+ 'widgets',
895
+ fileName
896
+ ]);
897
+ switch(widgetType){
898
+ case 'monolithic':
899
+ folder.createFile('index.tsx', `
900
+ import { type FC } from 'react'
901
+
902
+ export type ${ClassName}WidgetProps = {}
903
+
904
+ export const ${ClassName}Widget: FC<${ClassName}WidgetProps> = (props) => {
905
+ return <></>
906
+ }
907
+ `.trim());
908
+ break;
909
+ case 'with separated ui':
910
+ folder.createFile('types.ts', `
911
+ export type ${ClassName}WidgetProps = { }
912
+
913
+ export type ${ClassName}WidgetUIProps = { }
914
+ `.trim());
915
+ folder.createFile('index.tsx', `
916
+ import { type FC } from 'react'
917
+ import type { ${ClassName}WidgetProps } from './types'
918
+ import { ${ClassName}WidgetUI } from './ui'
919
+ import { useUIModel } from './hooks/use-ui-model'
920
+
921
+ export const ${ClassName}Widget: FC<${ClassName}WidgetProps> = (props) => {
922
+ const uiModel = useUIModel(props)
923
+ return <${ClassName}WidgetUI {...uiModel} />
924
+ }
925
+
926
+ export type { ${ClassName}WidgetProps }
927
+ `.trim());
928
+ folder.subitem([
929
+ 'ui'
930
+ ]).createFile('index.tsx', `
931
+ import { FC } from 'react'
932
+ import type { ${ClassName}WidgetUIProps } from '../types'
933
+
934
+ export const ${ClassName}WidgetUI: FC<${ClassName}WidgetUIProps> = (props) => {
935
+ return <></>
936
+ }
937
+ `.trim());
938
+ folder.subitem([
939
+ 'hooks'
940
+ ]).createFile('use-ui-model.ts', `
941
+ import type { ${ClassName}WidgetProps, ${ClassName}WidgetUIProps } from '../types'
942
+
943
+ export const useUIModel = (widgetProps: ${ClassName}WidgetProps): ${ClassName}WidgetUIProps => {
944
+ return {}
945
+ }
946
+ `.trim());
947
+ break;
948
+ case 'headless':
949
+ folder.createFile('types.ts', `
950
+ import { type FC } from 'react'
951
+
952
+ export type ${ClassName}WidgetUIProps = { }
953
+
954
+ export type ${ClassName}WidgetProps = {
955
+ UI: FC<${ClassName}WidgetUIProps>
956
+ }
957
+
958
+ `.trim());
959
+ folder.createFile('index.tsx', `
960
+ import { type FC } from 'react'
961
+ import type { ${ClassName}WidgetProps } from './types'
962
+ import { useUIModel } from './hooks/use-ui-model'
963
+
964
+ export const ${ClassName}Widget: FC<${ClassName}WidgetProps> = (props) => {
965
+ const uiModel = useUIModel(props)
966
+ return <props.UI {...uiModel} />
967
+ }
968
+
969
+ export type { ${ClassName}WidgetProps }
970
+ `.trim());
971
+ folder.subitem([
972
+ 'hooks'
973
+ ]).createFile('use-ui-model.ts', `
974
+ import type { ${ClassName}WidgetProps, ${ClassName}WidgetUIProps } from '../types'
975
+
976
+ export const useUIModel = (widgetProps: Omit<${ClassName}WidgetProps, 'UI'>): ${ClassName}WidgetUIProps => {
977
+ return {}
978
+ }
979
+ `.trim());
980
+ break;
981
+ }
982
+ };
875
983
  const main = async ()=>{
876
984
  if (!ConfigFile.exists) return void await initializeUseCase();
877
985
  const rootPath = new ProjectRoot().path;
@@ -888,18 +996,41 @@ const main = async ()=>{
888
996
  };
889
997
  const options = [
890
998
  ...currentBoundedContextOptions,
999
+ ConfigFile.Instance.data.useReact ? {
1000
+ name: 'Scaffold React module',
1001
+ action: 'Scaffold React module'
1002
+ } : void 0,
891
1003
  {
892
1004
  name: 'Shared Layer',
893
1005
  action: 'Scaffold in bounded context'
894
1006
  },
895
1007
  scaffoldNewBoundedContext
896
- ];
1008
+ ].filter((x)=>!!x);
897
1009
  const result = await prompts_select({
898
1010
  message: '@domain-first/project-structure',
899
1011
  choices: options.map((x)=>x.name)
900
1012
  });
901
1013
  const { action, name } = options.find((x)=>x.name === result);
902
1014
  switch(action){
1015
+ case 'Scaffold React module':
1016
+ {
1017
+ const action = await prompts_select({
1018
+ message: 'Module: ',
1019
+ choices: [
1020
+ 'New widget'
1021
+ ]
1022
+ });
1023
+ switch(action){
1024
+ case 'New widget':
1025
+ return void await scaffoldReactWidget(new Folder(rootPath).subitem([
1026
+ ConfigFile.Instance.data.rootFolder,
1027
+ 'presentation',
1028
+ 'react'
1029
+ ]));
1030
+ default:
1031
+ return;
1032
+ }
1033
+ }
903
1034
  case 'Scaffold new bounded context':
904
1035
  return void await scaffoldNewBoundedContextUseCase();
905
1036
  case 'Scaffold in bounded context':
@@ -8,3 +8,4 @@ export * from './scaffold-new-error';
8
8
  export * from './scaffold-new-handlers-rest-endpoint';
9
9
  export * from './scaffold-new-query';
10
10
  export * from './scaffold-new-use-case';
11
+ export * from './scaffold-react-widget';
@@ -0,0 +1,2 @@
1
+ import type { Folder } from '../file-system';
2
+ export declare const scaffoldReactWidget: (presentationReactFolder: Folder) => Promise<void>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@domain-first/project-structure",
3
3
  "description": "Build and manage your Domain-First project structure with CLI",
4
- "version": "1.1.2",
4
+ "version": "1.2.0",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",