@domain-first/project-structure 2.0.1 → 2.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.
@@ -10,6 +10,7 @@ declare const configSchema: z.ZodObject<{
10
10
  }>>;
11
11
  defaultPersistenceLayerImplementation: z.ZodString;
12
12
  useReact: z.ZodBoolean;
13
+ testingLibrary: z.ZodOptional<z.ZodString>;
13
14
  }, z.core.$strip>;
14
15
  export declare class ConfigFile {
15
16
  readonly data: z.infer<typeof configSchema>;
package/dist/index.cjs CHANGED
@@ -125,7 +125,8 @@ const configSchema = external_zod_default().object({
125
125
  '@domain-first/handlers-rest'
126
126
  ])),
127
127
  defaultPersistenceLayerImplementation: external_zod_default().string(),
128
- useReact: external_zod_default().boolean()
128
+ useReact: external_zod_default().boolean(),
129
+ testingLibrary: external_zod_default().string().optional()
129
130
  });
130
131
  class ConfigFile {
131
132
  data;
@@ -170,11 +171,20 @@ const initializeUseCase = async ()=>{
170
171
  const useReact = await (0, prompts_namespaceObject.confirm)({
171
172
  message: 'Use React'
172
173
  });
174
+ let testingLibrary;
175
+ const withTests = await (0, prompts_namespaceObject.confirm)({
176
+ message: 'Scaffold unit tests'
177
+ });
178
+ if (withTests) testingLibrary = await (0, prompts_namespaceObject.input)({
179
+ message: 'Unit testing library:',
180
+ default: 'vitest'
181
+ });
173
182
  ConfigFile.write({
174
183
  rootFolder: rootFolderName,
175
184
  domainFirstPackages,
176
185
  defaultPersistenceLayerImplementation,
177
- useReact
186
+ useReact,
187
+ testingLibrary
178
188
  });
179
189
  const { path } = new ProjectRoot();
180
190
  const rootFolder = new Folder(path);
@@ -225,12 +235,19 @@ class UnknownFormatNaming {
225
235
  get ClassName() {
226
236
  return this.PascalCase;
227
237
  }
238
+ get withSpaces() {
239
+ return this.fileName.replaceAll('-', ' ');
240
+ }
228
241
  }
229
242
  const scaffoldNewAggregateUseCase = async (boundedContextFolder)=>{
230
243
  const aggregateName = await (0, prompts_namespaceObject.input)({
231
244
  message: 'Aggregate name:'
232
245
  });
233
246
  const aggregateNaming = new UnknownFormatNaming(aggregateName);
247
+ let scaffoldUnitTests;
248
+ if (ConfigFile.Instance.data.testingLibrary) scaffoldUnitTests = await (0, prompts_namespaceObject.confirm)({
249
+ message: 'Scaffold unit-tests'
250
+ });
234
251
  const withRepository = await (0, prompts_namespaceObject.confirm)({
235
252
  message: 'With Repository'
236
253
  });
@@ -246,9 +263,42 @@ export class ${aggregateNaming.ClassName} extends domainType() {
246
263
 
247
264
  }`.trim() : `export class ${aggregateNaming.ClassName} {}`);
248
265
  const barrelFile = aggregatesFolder.createFile("index.ts", `export * from './${aggregateNaming.fileName}.aggregate-root'`);
266
+ if (scaffoldUnitTests) aggregatesFolder.createFile(`${aggregateNaming.fileName}.aggregate-root.spec.ts`, `
267
+ import { describe, test, expect, beforeEach } from '${ConfigFile.Instance.data.testingLibrary}'
268
+ import { ${aggregateNaming.ClassName} } from './${aggregateNaming.fileName}.aggregate-root'
269
+
270
+ let ${aggregateNaming.variableName}: ${aggregateNaming.ClassName}
271
+
272
+ beforeEach(() => {
273
+ ${aggregateNaming.variableName} = new ${aggregateNaming.ClassName}()
274
+ })
275
+
276
+ describe('${aggregateNaming.withSpaces}', () => {
277
+ test('can be instantiated via constructor', () => {
278
+ expect(${aggregateNaming.variableName}).toBeDefined()
279
+ })
280
+ })
281
+ `);
249
282
  if (withRepository) {
250
283
  aggregatesFolder.createFile(`${aggregateNaming.fileName}-repository.ts`, `export abstract class ${aggregateNaming.ClassName}Repository { }`);
251
284
  barrelFile.addLine(`export * from './${aggregateNaming.fileName}-repository'`);
285
+ if (scaffoldUnitTests) aggregatesFolder.createFile(`${aggregateNaming.fileName}-repository.spec.ts`, `
286
+ import { describe, test, expect, beforeEach } from '${ConfigFile.Instance.data.testingLibrary}'
287
+ import { wire${aggregateNaming.ClassName}Repository } from '../../../wiring/repositories/wire-${aggregateNaming.fileName}-repository'
288
+ import type { ${aggregateNaming.ClassName}Repository } from './${aggregateNaming.fileName}-repository'
289
+
290
+ let ${aggregateNaming.variableName}Repository: ${aggregateNaming.ClassName}Repository
291
+
292
+ beforeEach(() => {
293
+ ${aggregateNaming.variableName}Repository = wire${aggregateNaming.ClassName}Repository()
294
+ })
295
+
296
+ describe('${aggregateNaming.withSpaces} repository', () => {
297
+ test('can be wired', () => {
298
+ expect(${aggregateNaming.variableName}Repository).toBeDefined()
299
+ })
300
+ })
301
+ `.trim());
252
302
  const infrastructureImplementationType = await (0, prompts_namespaceObject.input)({
253
303
  message: 'Repository infrastructure implementation type:',
254
304
  default: 'prisma'
@@ -895,6 +945,23 @@ export const wire${naming.ClassName}UseCase = wireClass(
895
945
 
896
946
  `.trim());
897
947
  };
948
+ const scaffoldReactPage = async (presentationReactFolder)=>{
949
+ const name = await (0, prompts_namespaceObject.input)({
950
+ message: 'Provide name:'
951
+ });
952
+ const { ClassName, fileName } = new UnknownFormatNaming(name);
953
+ presentationReactFolder.subitem([
954
+ 'pages'
955
+ ]).createFile(`${fileName}-page.tsx`, `
956
+ import { type FC } from 'react'
957
+
958
+ export type ${ClassName}PageProps = {}
959
+
960
+ export const ${ClassName}Page: FC<${ClassName}PageProps> = (props) => {
961
+ return <></>
962
+ }
963
+ `.trim());
964
+ };
898
965
  const scaffoldReactWidget = async (presentationReactFolder)=>{
899
966
  const widgetName = await (0, prompts_namespaceObject.input)({
900
967
  message: 'Name: '
@@ -1032,19 +1099,23 @@ const main = async ()=>{
1032
1099
  switch(action){
1033
1100
  case 'Scaffold React module':
1034
1101
  {
1102
+ const reactFolder = new Folder(rootPath).subitem([
1103
+ ConfigFile.Instance.data.rootFolder,
1104
+ 'presentation',
1105
+ 'react'
1106
+ ]);
1035
1107
  const action = await (0, prompts_namespaceObject.select)({
1036
1108
  message: 'Module: ',
1037
1109
  choices: [
1038
- 'New widget'
1110
+ 'New widget',
1111
+ 'New page'
1039
1112
  ]
1040
1113
  });
1041
1114
  switch(action){
1042
1115
  case 'New widget':
1043
- return void await scaffoldReactWidget(new Folder(rootPath).subitem([
1044
- ConfigFile.Instance.data.rootFolder,
1045
- 'presentation',
1046
- 'react'
1047
- ]));
1116
+ return void await scaffoldReactWidget(reactFolder);
1117
+ case 'New page':
1118
+ return void await scaffoldReactPage(reactFolder);
1048
1119
  default:
1049
1120
  return;
1050
1121
  }
package/dist/index.js CHANGED
@@ -95,7 +95,8 @@ const configSchema = zod.object({
95
95
  '@domain-first/handlers-rest'
96
96
  ])),
97
97
  defaultPersistenceLayerImplementation: zod.string(),
98
- useReact: zod.boolean()
98
+ useReact: zod.boolean(),
99
+ testingLibrary: zod.string().optional()
99
100
  });
100
101
  class ConfigFile {
101
102
  data;
@@ -140,11 +141,20 @@ const initializeUseCase = async ()=>{
140
141
  const useReact = await prompts_confirm({
141
142
  message: 'Use React'
142
143
  });
144
+ let testingLibrary;
145
+ const withTests = await prompts_confirm({
146
+ message: 'Scaffold unit tests'
147
+ });
148
+ if (withTests) testingLibrary = await input({
149
+ message: 'Unit testing library:',
150
+ default: 'vitest'
151
+ });
143
152
  ConfigFile.write({
144
153
  rootFolder: rootFolderName,
145
154
  domainFirstPackages,
146
155
  defaultPersistenceLayerImplementation,
147
- useReact
156
+ useReact,
157
+ testingLibrary
148
158
  });
149
159
  const { path } = new ProjectRoot();
150
160
  const rootFolder = new Folder(path);
@@ -195,12 +205,19 @@ class UnknownFormatNaming {
195
205
  get ClassName() {
196
206
  return this.PascalCase;
197
207
  }
208
+ get withSpaces() {
209
+ return this.fileName.replaceAll('-', ' ');
210
+ }
198
211
  }
199
212
  const scaffoldNewAggregateUseCase = async (boundedContextFolder)=>{
200
213
  const aggregateName = await input({
201
214
  message: 'Aggregate name:'
202
215
  });
203
216
  const aggregateNaming = new UnknownFormatNaming(aggregateName);
217
+ let scaffoldUnitTests;
218
+ if (ConfigFile.Instance.data.testingLibrary) scaffoldUnitTests = await prompts_confirm({
219
+ message: 'Scaffold unit-tests'
220
+ });
204
221
  const withRepository = await prompts_confirm({
205
222
  message: 'With Repository'
206
223
  });
@@ -216,9 +233,42 @@ export class ${aggregateNaming.ClassName} extends domainType() {
216
233
 
217
234
  }`.trim() : `export class ${aggregateNaming.ClassName} {}`);
218
235
  const barrelFile = aggregatesFolder.createFile("index.ts", `export * from './${aggregateNaming.fileName}.aggregate-root'`);
236
+ if (scaffoldUnitTests) aggregatesFolder.createFile(`${aggregateNaming.fileName}.aggregate-root.spec.ts`, `
237
+ import { describe, test, expect, beforeEach } from '${ConfigFile.Instance.data.testingLibrary}'
238
+ import { ${aggregateNaming.ClassName} } from './${aggregateNaming.fileName}.aggregate-root'
239
+
240
+ let ${aggregateNaming.variableName}: ${aggregateNaming.ClassName}
241
+
242
+ beforeEach(() => {
243
+ ${aggregateNaming.variableName} = new ${aggregateNaming.ClassName}()
244
+ })
245
+
246
+ describe('${aggregateNaming.withSpaces}', () => {
247
+ test('can be instantiated via constructor', () => {
248
+ expect(${aggregateNaming.variableName}).toBeDefined()
249
+ })
250
+ })
251
+ `);
219
252
  if (withRepository) {
220
253
  aggregatesFolder.createFile(`${aggregateNaming.fileName}-repository.ts`, `export abstract class ${aggregateNaming.ClassName}Repository { }`);
221
254
  barrelFile.addLine(`export * from './${aggregateNaming.fileName}-repository'`);
255
+ if (scaffoldUnitTests) aggregatesFolder.createFile(`${aggregateNaming.fileName}-repository.spec.ts`, `
256
+ import { describe, test, expect, beforeEach } from '${ConfigFile.Instance.data.testingLibrary}'
257
+ import { wire${aggregateNaming.ClassName}Repository } from '../../../wiring/repositories/wire-${aggregateNaming.fileName}-repository'
258
+ import type { ${aggregateNaming.ClassName}Repository } from './${aggregateNaming.fileName}-repository'
259
+
260
+ let ${aggregateNaming.variableName}Repository: ${aggregateNaming.ClassName}Repository
261
+
262
+ beforeEach(() => {
263
+ ${aggregateNaming.variableName}Repository = wire${aggregateNaming.ClassName}Repository()
264
+ })
265
+
266
+ describe('${aggregateNaming.withSpaces} repository', () => {
267
+ test('can be wired', () => {
268
+ expect(${aggregateNaming.variableName}Repository).toBeDefined()
269
+ })
270
+ })
271
+ `.trim());
222
272
  const infrastructureImplementationType = await input({
223
273
  message: 'Repository infrastructure implementation type:',
224
274
  default: 'prisma'
@@ -865,6 +915,23 @@ export const wire${naming.ClassName}UseCase = wireClass(
865
915
 
866
916
  `.trim());
867
917
  };
918
+ const scaffoldReactPage = async (presentationReactFolder)=>{
919
+ const name = await input({
920
+ message: 'Provide name:'
921
+ });
922
+ const { ClassName, fileName } = new UnknownFormatNaming(name);
923
+ presentationReactFolder.subitem([
924
+ 'pages'
925
+ ]).createFile(`${fileName}-page.tsx`, `
926
+ import { type FC } from 'react'
927
+
928
+ export type ${ClassName}PageProps = {}
929
+
930
+ export const ${ClassName}Page: FC<${ClassName}PageProps> = (props) => {
931
+ return <></>
932
+ }
933
+ `.trim());
934
+ };
868
935
  const scaffoldReactWidget = async (presentationReactFolder)=>{
869
936
  const widgetName = await input({
870
937
  message: 'Name: '
@@ -1002,19 +1069,23 @@ const main = async ()=>{
1002
1069
  switch(action){
1003
1070
  case 'Scaffold React module':
1004
1071
  {
1072
+ const reactFolder = new Folder(rootPath).subitem([
1073
+ ConfigFile.Instance.data.rootFolder,
1074
+ 'presentation',
1075
+ 'react'
1076
+ ]);
1005
1077
  const action = await prompts_select({
1006
1078
  message: 'Module: ',
1007
1079
  choices: [
1008
- 'New widget'
1080
+ 'New widget',
1081
+ 'New page'
1009
1082
  ]
1010
1083
  });
1011
1084
  switch(action){
1012
1085
  case 'New widget':
1013
- return void await scaffoldReactWidget(new Folder(rootPath).subitem([
1014
- ConfigFile.Instance.data.rootFolder,
1015
- 'presentation',
1016
- 'react'
1017
- ]));
1086
+ return void await scaffoldReactWidget(reactFolder);
1087
+ case 'New page':
1088
+ return void await scaffoldReactPage(reactFolder);
1018
1089
  default:
1019
1090
  return;
1020
1091
  }
@@ -6,4 +6,5 @@ export declare class UnknownFormatNaming {
6
6
  get fileName(): string;
7
7
  get variableName(): string;
8
8
  get ClassName(): string;
9
+ get withSpaces(): string;
9
10
  }
@@ -8,4 +8,5 @@ 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-page';
11
12
  export * from './scaffold-react-widget';
@@ -0,0 +1,2 @@
1
+ import type { Folder } from '../file-system';
2
+ export declare const scaffoldReactPage: (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": "2.0.1",
4
+ "version": "2.2.0",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",