@azure-net/cli 1.4.1 → 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.
Files changed (32) hide show
  1. package/bin/azure-net.js +83 -32
  2. package/package.json +1 -1
  3. package/src/commands/generate/generateCrud.js +323 -0
  4. package/src/commands/generate/generateDatasource.js +36 -0
  5. package/src/commands/generate/generateModule.js +23 -0
  6. package/src/commands/generate/generatePresenter.js +113 -0
  7. package/src/commands/generate/generateRepository.js +97 -0
  8. package/src/commands/generate/generateResponse.js +42 -0
  9. package/src/commands/generate/generateSchema.js +70 -0
  10. package/src/commands/generate/generateService.js +93 -0
  11. package/src/commands/generate/index.js +8 -0
  12. package/src/commands/init/index.js +6 -0
  13. package/src/commands/init/initAliases.js +50 -0
  14. package/src/commands/init/initEdges.js +42 -0
  15. package/src/commands/init/initMiddleware.js +58 -0
  16. package/src/commands/init/initSchema.js +19 -0
  17. package/src/commands/init/initStructure.js +68 -0
  18. package/src/commands/init/initTranslations.js +97 -0
  19. package/src/templates/domain/request/ClassRequest.ejs +8 -0
  20. package/src/utils/contextUtils.js +53 -0
  21. package/src/utils/fileUtils.js +60 -0
  22. package/src/utils/loadConfig.js +2 -2
  23. package/src/utils/providerUtils.js +78 -0
  24. package/.idea/azure-net-ecosystem-cli.iml +0 -9
  25. package/.idea/modules.xml +0 -8
  26. package/src/commands/generateCrud.js +0 -135
  27. package/src/commands/generateDatasource.js +0 -44
  28. package/src/commands/generateRepo.js +0 -69
  29. package/src/commands/generateService.js +0 -77
  30. package/src/commands/generateStructure.js +0 -65
  31. package/src/commands/initAliases.js +0 -101
  32. package/src/commands/initLint.js +0 -138
package/bin/azure-net.js CHANGED
@@ -1,30 +1,90 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { Command } from 'commander';
4
- import generateDatasource from '../src/commands/generateDatasource.js';
5
- import generateRepo from '../src/commands/generateRepo.js';
6
- import generateService from '../src/commands/generateService.js';
7
- import generateCrud from '../src/commands/generateCrud.js';
8
- import initLint from '../src/commands/initLint.js';
9
- import initStructure from '../src/commands/generateStructure.js';
10
- import initAliases from '../src/commands/initAliases.js';
4
+ import {
5
+ initStructure,
6
+ initAliases,
7
+ initMiddleware,
8
+ initEdges,
9
+ initTranslations,
10
+ initSchema
11
+ } from '../src/commands/init/index.js';
12
+ import {
13
+ generateDatasource,
14
+ generateResponse,
15
+ generateRepository,
16
+ generateService,
17
+ generateSchema,
18
+ generatePresenter,
19
+ generateModule,
20
+ generateCrud
21
+ } from '../src/commands/generate/index.js';
11
22
 
12
23
  const program = new Command();
13
24
 
14
25
  program
15
26
  .name('azure-net')
16
- .description('Universal code generator')
17
- .version('1.0.0');
27
+ .description('Azure-Net CLI generator')
28
+ .version('2.0.0');
18
29
 
30
+ // Init commands
31
+ program
32
+ .command('init:structure')
33
+ .description('Generate base folder structure')
34
+ .action(initStructure);
35
+
36
+ program
37
+ .command('init:aliases')
38
+ .description('Generate alias mappings')
39
+ .action(initAliases);
40
+
41
+ program
42
+ .command('init:middleware')
43
+ .description('Initialize middleware')
44
+ .action(initMiddleware);
45
+
46
+ program
47
+ .command('init:edges')
48
+ .description('Initialize edges-svelte')
49
+ .action(initEdges);
50
+
51
+ program
52
+ .command('init:translations')
53
+ .description('Initialize translations')
54
+ .action(initTranslations);
55
+
56
+ program
57
+ .command('init:schema')
58
+ .description('Initialize schema factory')
59
+ .action(initSchema);
60
+
61
+ program
62
+ .command('init')
63
+ .description('Run all initialization steps')
64
+ .action(async () => {
65
+ await initStructure();
66
+ await initAliases();
67
+ await initEdges();
68
+ await initMiddleware();
69
+ await initTranslations();
70
+ await initSchema();
71
+ });
72
+
73
+ // Generate commands
19
74
  program
20
75
  .command('make:datasource')
21
76
  .description('Generate datasource')
22
77
  .action(generateDatasource);
23
78
 
79
+ program
80
+ .command('make:response')
81
+ .description('Generate response builder')
82
+ .action(generateResponse);
83
+
24
84
  program
25
85
  .command('make:repo')
26
- .description('Generate repo')
27
- .action(generateRepo);
86
+ .description('Generate repository')
87
+ .action(generateRepository);
28
88
 
29
89
  program
30
90
  .command('make:service')
@@ -32,32 +92,23 @@ program
32
92
  .action(generateService);
33
93
 
34
94
  program
35
- .command('make:crud')
36
- .description('Generate CRUD Domain/Repo/Service')
37
- .action(generateCrud);
95
+ .command('make:schema')
96
+ .description('Generate schema')
97
+ .action(generateSchema);
38
98
 
39
99
  program
40
- .command('init:lint')
41
- .description('Setup linting and commit tools')
42
- .action(initLint)
43
-
44
- program
45
- .command('init:structure')
46
- .description('Generate base folder structure based on config')
47
- .action(initStructure);
100
+ .command('make:presenter')
101
+ .description('Generate presenter')
102
+ .action(generatePresenter);
48
103
 
49
104
  program
50
- .command('init:aliases')
51
- .description('Generate alias mappings for modules or default architecture')
52
- .action(initAliases);
105
+ .command('make:module')
106
+ .description('Generate complete module')
107
+ .action(generateModule);
53
108
 
54
109
  program
55
- .command('init')
56
- .description('Run all initialization steps: structure, aliases, lint')
57
- .action(async () => {
58
- await initStructure();
59
- await initAliases();
60
- await initLint();
61
- });
110
+ .command('make:crud')
111
+ .description('Generate CRUD module')
112
+ .action(generateCrud);
62
113
 
63
114
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azure-net/cli",
3
- "version": "1.4.1",
3
+ "version": "2.0.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "azure-net": "bin/azure-net.js"
@@ -0,0 +1,323 @@
1
+ import prompts from 'prompts';
2
+ import fs from 'fs/promises';
3
+ import path from 'path';
4
+ import { selectContext, getContextPath, toPascalCase, toCamelCase, getAvailableFiles } from '../../utils/contextUtils.js';
5
+ import { writeIfNotExists, updateIndexTs } from '../../utils/fileUtils.js';
6
+ import { ensureProvider, addToProvider } from '../../utils/providerUtils.js';
7
+
8
+ // Templates for CRUD generation
9
+ const entityTemplate = `export interface I{{name}} {
10
+ \tid: number;
11
+ \t// Add entity fields here
12
+ \t[key: string]: unknown;
13
+ }
14
+
15
+ export type I{{name}}Collection = I{{name}}[];`;
16
+
17
+ const requestTemplate = `import type { I{{name}} } from '../../Entities/{{name}}/index.js';
18
+
19
+ export type I{{name}}CreateRequest = Omit<I{{name}}, 'id'>;
20
+ export type I{{name}}UpdateRequest = Partial<I{{name}}CreateRequest>;
21
+ export interface I{{name}}CollectionQuery {
22
+ \t[key: string]: unknown;
23
+ }`;
24
+
25
+ const repositoryTemplate = `import { {{datasource}} } from '{{datasourceImport}}';
26
+ import type {
27
+ \tI{{name}}Collection,
28
+ \tI{{name}},
29
+ \tI{{name}}CreateRequest,
30
+ \tI{{name}}UpdateRequest,
31
+ \tI{{name}}CollectionQuery
32
+ } from '../../Domain/Entities/{{name}}/index.js';
33
+ import type { I{{name}}CreateRequest as CreateRequest, I{{name}}UpdateRequest as UpdateRequest } from '../../Domain/Ports/{{name}}/index.js';
34
+
35
+ export class {{name}}Repository {
36
+ \tprivate endpoint = '{{endpoint}}';
37
+ \t
38
+ \tconstructor(private {{datasourceVar}}: {{datasource}}) {}
39
+ \t
40
+ \tpublic async collection(queryParams?: I{{name}}CollectionQuery) {
41
+ \t\treturn await this.{{datasourceVar}}.createRequest<I{{name}}Collection>(({ http, query }) =>
42
+ \t\t\thttp.get(\`\${this.endpoint}\${query.build(queryParams)}\`)
43
+ \t\t);
44
+ \t}
45
+ \t
46
+ \tpublic async resource(id: number) {
47
+ \t\treturn await this.{{datasourceVar}}.createRequest<I{{name}}>(({ http }) =>
48
+ \t\t\thttp.get(\`\${this.endpoint}/\${id}\`)
49
+ \t\t);
50
+ \t}
51
+ \t
52
+ \tpublic async create(data: CreateRequest) {
53
+ \t\treturn await this.{{datasourceVar}}.createRequest<I{{name}}>(({ http }) =>
54
+ \t\t\thttp.post(this.endpoint, { json: data })
55
+ \t\t);
56
+ \t}
57
+ \t
58
+ \tpublic async update(id: number, data: UpdateRequest) {
59
+ \t\treturn await this.{{datasourceVar}}.createRequest<I{{name}}>(({ http }) =>
60
+ \t\t\thttp.put(\`\${this.endpoint}/\${id}\`, { json: data })
61
+ \t\t);
62
+ \t}
63
+ \t
64
+ \tpublic async remove(id: number) {
65
+ \t\treturn await this.{{datasourceVar}}.createRequest<never>(({ http }) =>
66
+ \t\t\thttp.delete(\`\${this.endpoint}/\${id}\`)
67
+ \t\t);
68
+ \t}
69
+ }`;
70
+
71
+ const serviceTemplate = `import { ClassMirror } from '@azure-net/kit';
72
+ import { {{name}}Repository } from '../../Infrastructure/Repositories/index.js';
73
+
74
+ export class {{name}}Service extends ClassMirror<{{name}}Repository> {
75
+ \tconstructor(private {{camelName}}Repository: {{name}}Repository) {
76
+ \t\tsuper({{camelName}}Repository);
77
+ \t}
78
+ \t
79
+ \tdeclare collection: {{name}}Repository['collection'];
80
+ \tdeclare resource: {{name}}Repository['resource'];
81
+ \tdeclare create: {{name}}Repository['create'];
82
+ \tdeclare update: {{name}}Repository['update'];
83
+ \tdeclare remove: {{name}}Repository['remove'];
84
+ }`;
85
+
86
+ const createSchemaTemplate = `import { {{schemaFactory}} } from '{{schemaImport}}';
87
+ import type { I{{name}}CreateRequest } from '../../Domain/Ports/{{name}}/index.js';
88
+
89
+ export const Create{{name}}Schema = {{schemaFactory}}<I{{name}}CreateRequest>()
90
+ \t.rules((rules) => ({
91
+ \t\t// Add validation rules here
92
+ \t}))
93
+ \t.create();`;
94
+
95
+ const updateSchemaTemplate = `import { {{schemaFactory}} } from '{{schemaImport}}';
96
+ import type { I{{name}}UpdateRequest } from '../../Domain/Ports/{{name}}/index.js';
97
+
98
+ export const Update{{name}}Schema = {{schemaFactory}}<I{{name}}UpdateRequest>()
99
+ \t.rules((rules) => ({
100
+ \t\t// Add validation rules here
101
+ \t}))
102
+ \t.create();`;
103
+
104
+ const presenterTemplate = `import { {{presenterFactory}} } from '{{presenterImport}}';
105
+ import { ApplicationProvider } from '../../Application/index.js';
106
+ import { Create{{name}}Schema, Update{{name}}Schema } from '../Schemas/index.js';
107
+ import type { I{{name}}CreateRequest, I{{name}}UpdateRequest, I{{name}}CollectionQuery } from '../../Domain/Ports/{{name}}/index.js';
108
+
109
+ export const {{contextName}}{{name}}Presenter = {{presenterFactory}}('{{contextName}}{{name}}Presenter', ({ createAsyncResource, createAsyncAction }) => {
110
+ \tconst { {{name}}Service } = ApplicationProvider();
111
+ \t
112
+ \tconst collection = async (queryParams?: I{{name}}CollectionQuery) =>
113
+ \t\tawait createAsyncResource({{name}}Service.collection(queryParams));
114
+ \t
115
+ \tconst resource = async (id: number) =>
116
+ \t\tawait createAsyncResource({{name}}Service.resource(id));
117
+ \t
118
+ \tconst create = async (request: I{{name}}CreateRequest) =>
119
+ \t\tawait createAsyncAction({{name}}Service.create(Create{{name}}Schema.from(request).json()));
120
+ \t
121
+ \tconst update = async (id: number, request: I{{name}}UpdateRequest) =>
122
+ \t\tawait createAsyncAction({{name}}Service.update(id, Update{{name}}Schema.from(request).json()));
123
+ \t
124
+ \tconst remove = async (id: number) =>
125
+ \t\tawait createAsyncAction({{name}}Service.remove(id));
126
+ \t
127
+ \treturn { collection, resource, create, update, remove };
128
+ });`;
129
+
130
+ export default async function generateCrud() {
131
+ const context = await selectContext('Select context for CRUD module:');
132
+
133
+ if (context === 'core') {
134
+ console.error('❌ Cannot create CRUD in core. Choose a specific context.');
135
+ return;
136
+ }
137
+
138
+ const { name } = await prompts({
139
+ type: 'text',
140
+ name: 'name',
141
+ message: 'Entity name (PascalCase):'
142
+ });
143
+
144
+ const { endpoint } = await prompts({
145
+ type: 'text',
146
+ name: 'endpoint',
147
+ message: 'API endpoint (e.g. /api/users):'
148
+ });
149
+
150
+ // Get available datasources
151
+ const contextPath = getContextPath(context);
152
+ const coreDatasources = await getAvailableFiles(
153
+ path.join(process.cwd(), 'src/app/core/Datasources')
154
+ );
155
+ const contextDatasources = await getAvailableFiles(
156
+ path.join(contextPath, 'Infrastructure', 'Datasources')
157
+ );
158
+
159
+ const allDatasources = [
160
+ ...coreDatasources.map(d => ({ title: `${d}Datasource (core)`, value: { name: `${d}Datasource`, from: 'core' } })),
161
+ ...contextDatasources.map(d => ({ title: `${d}Datasource (${context})`, value: { name: `${d}Datasource`, from: context } }))
162
+ ];
163
+
164
+ const { datasource } = await prompts({
165
+ type: 'select',
166
+ name: 'datasource',
167
+ message: 'Select datasource:',
168
+ choices: allDatasources
169
+ });
170
+
171
+ // Get schema and presenter factories
172
+ const coreSchemas = await getAvailableFiles(
173
+ path.join(process.cwd(), 'src/app/core/Schemas')
174
+ );
175
+
176
+ const { schemaType } = await prompts({
177
+ type: 'select',
178
+ name: 'schemaType',
179
+ message: 'Select schema factory:',
180
+ choices: [
181
+ { title: 'Use schema from package', value: 'package' },
182
+ ...coreSchemas.map(s => ({ title: `Use ${s} from core`, value: s }))
183
+ ]
184
+ });
185
+
186
+ const corePresenters = await getAvailableFiles(
187
+ path.join(process.cwd(), 'src/app/core/Presenters')
188
+ );
189
+
190
+ const { presenterType } = await prompts({
191
+ type: 'select',
192
+ name: 'presenterType',
193
+ message: 'Select presenter factory:',
194
+ choices: [
195
+ { title: 'Use createPresenter from package', value: 'package' },
196
+ ...corePresenters.map(p => ({ title: `Use ${p} from core`, value: p }))
197
+ ]
198
+ });
199
+
200
+ const pascalName = toPascalCase(name);
201
+ const camelName = toCamelCase(name);
202
+ const contextName = toPascalCase(context);
203
+
204
+ console.log('\n🚀 Generating CRUD module...\n');
205
+
206
+ // 1. Create Entity
207
+ const entityPath = path.join(contextPath, 'Domain', 'Entities', pascalName);
208
+ await writeIfNotExists(
209
+ path.join(entityPath, 'index.ts'),
210
+ entityTemplate.replace(/{{name}}/g, pascalName)
211
+ );
212
+
213
+ // 2. Create Ports (Requests/Responses)
214
+ const portsPath = path.join(contextPath, 'Domain', 'Ports', pascalName);
215
+ await writeIfNotExists(
216
+ path.join(portsPath, 'index.ts'),
217
+ requestTemplate.replace(/{{name}}/g, pascalName)
218
+ );
219
+
220
+ // 3. Create Repository
221
+ const repoPath = path.join(contextPath, 'Infrastructure', 'Repositories');
222
+ const datasourceImport = datasource.from === 'core'
223
+ ? '$core/Datasources/index.js'
224
+ : '../Datasources/index.js';
225
+
226
+ await writeIfNotExists(
227
+ path.join(repoPath, `${pascalName}Repository.ts`),
228
+ repositoryTemplate
229
+ .replace(/{{name}}/g, pascalName)
230
+ .replace(/{{endpoint}}/g, endpoint)
231
+ .replace(/{{datasource}}/g, datasource.name)
232
+ .replace(/{{datasourceImport}}/g, datasourceImport)
233
+ .replace(/{{datasourceVar}}/g, toCamelCase(datasource.name))
234
+ );
235
+ await updateIndexTs(repoPath);
236
+
237
+ // 4. Create Service
238
+ const servicePath = path.join(contextPath, 'Application', 'Services');
239
+ await writeIfNotExists(
240
+ path.join(servicePath, `${pascalName}Service.ts`),
241
+ serviceTemplate
242
+ .replace(/{{name}}/g, pascalName)
243
+ .replace(/{{camelName}}/g, camelName)
244
+ );
245
+ await updateIndexTs(servicePath);
246
+
247
+ // 5. Create Schemas
248
+ const schemaPath = path.join(contextPath, 'Delivery', 'Schemas');
249
+ const schemaFactory = schemaType === 'package' ? 'schema' : schemaType;
250
+ const schemaImport = schemaType === 'package'
251
+ ? '@azure-net/kit'
252
+ : '$core/Schemas/index.js';
253
+
254
+ await writeIfNotExists(
255
+ path.join(schemaPath, `Create${pascalName}Schema.ts`),
256
+ createSchemaTemplate
257
+ .replace(/{{name}}/g, pascalName)
258
+ .replace(/{{schemaFactory}}/g, schemaFactory)
259
+ .replace(/{{schemaImport}}/g, schemaImport)
260
+ );
261
+
262
+ await writeIfNotExists(
263
+ path.join(schemaPath, `Update${pascalName}Schema.ts`),
264
+ updateSchemaTemplate
265
+ .replace(/{{name}}/g, pascalName)
266
+ .replace(/{{schemaFactory}}/g, schemaFactory)
267
+ .replace(/{{schemaImport}}/g, schemaImport)
268
+ );
269
+ await updateIndexTs(schemaPath);
270
+
271
+ // 6. Create Presenter
272
+ const presenterPath = path.join(contextPath, 'Delivery', 'Presenters');
273
+ const presenterFactory = presenterType === 'package' ? 'createPresenter' : presenterType;
274
+ const presenterImport = presenterType === 'package'
275
+ ? '@azure-net/kit'
276
+ : '$core/Presenters/index.js';
277
+
278
+ await writeIfNotExists(
279
+ path.join(presenterPath, `${pascalName}Presenter.ts`),
280
+ presenterTemplate
281
+ .replace(/{{name}}/g, pascalName)
282
+ .replace(/{{contextName}}/g, contextName)
283
+ .replace(/{{presenterFactory}}/g, presenterFactory)
284
+ .replace(/{{presenterImport}}/g, presenterImport)
285
+ );
286
+ await updateIndexTs(presenterPath);
287
+
288
+ // 7. Setup Providers
289
+ // Ensure DatasourceProvider
290
+ await ensureProvider(context, 'Infrastructure', { datasource: datasource.name });
291
+
292
+ // Ensure InfrastructureProvider and add repository
293
+ const infraProvider = await ensureProvider(context, 'Infrastructure', {});
294
+ await addToProvider(
295
+ infraProvider.path,
296
+ `${pascalName}Repository`,
297
+ '../Repositories/index.js'
298
+ );
299
+
300
+ // Update provider with datasource dependency
301
+ let infraContent = await fs.readFile(infraProvider.path, 'utf-8');
302
+ const repoFactory = `${pascalName}Repository: () => new ${pascalName}Repository()`;
303
+ const repoWithDatasource = `${pascalName}Repository: () => new ${pascalName}Repository(DatasourceProvider.${datasource.name})`;
304
+ infraContent = infraContent.replace(repoFactory, repoWithDatasource);
305
+ await fs.writeFile(infraProvider.path, infraContent, 'utf-8');
306
+
307
+ // Ensure ApplicationProvider and add service
308
+ const appProvider = await ensureProvider(context, 'Application', { infrastructure: true });
309
+ await addToProvider(
310
+ appProvider.path,
311
+ `${pascalName}Service`,
312
+ '../Services/index.js'
313
+ );
314
+
315
+ // Update provider with repository dependency
316
+ let appContent = await fs.readFile(appProvider.path, 'utf-8');
317
+ const serviceFactory = `${pascalName}Service: () => new ${pascalName}Service()`;
318
+ const serviceWithRepo = `${pascalName}Service: () => new ${pascalName}Service(InfrastructureProvider.${pascalName}Repository)`;
319
+ appContent = appContent.replace(serviceFactory, serviceWithRepo);
320
+ await fs.writeFile(appProvider.path, appContent, 'utf-8');
321
+
322
+ console.log(`✅ CRUD module for ${pascalName} created successfully!`);
323
+ }
@@ -0,0 +1,36 @@
1
+ import prompts from 'prompts';
2
+ import { selectContext, getContextPath, toPascalCase } from '../../utils/contextUtils.js';
3
+ import { writeIfNotExists, updateIndexTs } from '../../utils/fileUtils.js';
4
+ import path from 'path';
5
+
6
+ const datasourceTemplate = `import { BaseHttpDatasource, HttpService } from '@azure-net/kit';
7
+
8
+ export class {{name}}Datasource extends BaseHttpDatasource {
9
+ \tconstructor() {
10
+ \t\tsuper({ http: new HttpService({ baseUrl: '' }) });
11
+ \t}
12
+ }`;
13
+
14
+ export default async function generateDatasource() {
15
+ const context = await selectContext();
16
+
17
+ const { name } = await prompts({
18
+ type: 'text',
19
+ name: 'name',
20
+ message: 'Datasource name (PascalCase):'
21
+ });
22
+
23
+ const pascalName = toPascalCase(name);
24
+ const contextPath = getContextPath(context);
25
+ const datasourcePath = context === 'core'
26
+ ? path.join(contextPath, 'Datasources')
27
+ : path.join(contextPath, 'Infrastructure', 'Datasources');
28
+
29
+ const content = datasourceTemplate.replace(/{{name}}/g, pascalName);
30
+ const filePath = path.join(datasourcePath, `${pascalName}Datasource.ts`);
31
+
32
+ await writeIfNotExists(filePath, content);
33
+ await updateIndexTs(datasourcePath);
34
+
35
+ console.log(`✅ Datasource created at ${filePath}`);
36
+ }
@@ -0,0 +1,23 @@
1
+ import prompts from 'prompts';
2
+ import generateRepository from './generateRepository.js';
3
+ import generateService from './generateService.js';
4
+ import generatePresenter from './generatePresenter.js';
5
+ import generateSchema from './generateSchema.js';
6
+
7
+ export default async function generateModule() {
8
+ console.log('🚀 Generating complete module...\n');
9
+
10
+ const { confirm } = await prompts({
11
+ type: 'confirm',
12
+ name: 'confirm',
13
+ message: 'This will create repository, service, presenter and schemas. Continue?'
14
+ });
15
+
16
+ if (confirm) {
17
+ await generateRepository();
18
+ await generateService();
19
+ await generatePresenter();
20
+ await generateSchema(); // Create schema
21
+ await generateSchema(); // Update schema
22
+ }
23
+ }
@@ -0,0 +1,113 @@
1
+ import prompts from 'prompts';
2
+ import path from 'path';
3
+ import { selectContext, getContextPath, toPascalCase, getAvailableFiles } from '../../utils/contextUtils.js';
4
+ import { writeIfNotExists, updateIndexTs } from '../../utils/fileUtils.js';
5
+
6
+ const presenterWithCoreTemplate = `import { {{corePresenter}} } from '$core/Presenters/index.js';
7
+ {{serviceImport}}
8
+
9
+ export const {{contextName}}{{name}}Presenter = {{corePresenter}}('{{contextName}}{{name}}Presenter', ({ createAsyncResource, createAsyncAction }) => {
10
+ \t{{serviceInit}}
11
+ \t
12
+ \tconst example = async () => {
13
+ \t\t// Implement presenter logic
14
+ \t\treturn {};
15
+ \t};
16
+ \t
17
+ \treturn { example };
18
+ });`;
19
+
20
+ const presenterWithPackageTemplate = `import { createPresenter } from '@azure-net/kit';
21
+ {{serviceImport}}
22
+
23
+ export const {{contextName}}{{name}}Presenter = createPresenter('{{contextName}}{{name}}Presenter', () => {
24
+ \t{{serviceInit}}
25
+ \t
26
+ \tconst example = async () => {
27
+ \t\t// Implement presenter logic
28
+ \t\treturn {};
29
+ \t};
30
+ \t
31
+ \treturn { example };
32
+ });`;
33
+
34
+ export default async function generatePresenter() {
35
+ const context = await selectContext('Select context for presenter:');
36
+
37
+ if (context === 'core') {
38
+ console.error('❌ Cannot create presenter in core. Choose a specific context.');
39
+ return;
40
+ }
41
+
42
+ const { name } = await prompts({
43
+ type: 'text',
44
+ name: 'name',
45
+ message: 'Presenter name (without "Presenter" suffix):'
46
+ });
47
+
48
+ // Check for core presenters
49
+ const corePresenters = await getAvailableFiles(
50
+ path.join(process.cwd(), 'src/app/core/Presenters')
51
+ );
52
+
53
+ const presenterChoices = [
54
+ { title: 'Use createPresenter from package', value: 'package' },
55
+ ...corePresenters.map(p => ({ title: `Use ${p} from core`, value: p }))
56
+ ];
57
+
58
+ const { presenterType } = await prompts({
59
+ type: 'select',
60
+ name: 'presenterType',
61
+ message: 'Select presenter factory:',
62
+ choices: presenterChoices
63
+ });
64
+
65
+ // Get available services
66
+ const contextPath = getContextPath(context);
67
+ const services = await getAvailableFiles(
68
+ path.join(contextPath, 'Application', 'Services')
69
+ );
70
+
71
+ const serviceChoices = [
72
+ { title: 'Without service', value: null },
73
+ ...services.map(s => ({ title: `${s}Service`, value: `${s}Service` }))
74
+ ];
75
+
76
+ const { service } = await prompts({
77
+ type: 'select',
78
+ name: 'service',
79
+ message: 'Select service (optional):',
80
+ choices: serviceChoices
81
+ });
82
+
83
+ const pascalName = toPascalCase(name);
84
+ const contextName = toPascalCase(context);
85
+ const presenterPath = path.join(contextPath, 'Delivery', 'Presenters');
86
+ const filePath = path.join(presenterPath, `${pascalName}Presenter.ts`);
87
+
88
+ const serviceImport = service
89
+ ? `import { ApplicationProvider } from '../../Application/index.js';`
90
+ : '';
91
+
92
+ const serviceInit = service
93
+ ? `const { ${service} } = ApplicationProvider();`
94
+ : '';
95
+
96
+ const content = presenterType === 'package'
97
+ ? presenterWithPackageTemplate
98
+ .replace(/{{contextName}}/g, contextName)
99
+ .replace(/{{name}}/g, pascalName)
100
+ .replace(/{{serviceImport}}/g, serviceImport)
101
+ .replace(/{{serviceInit}}/g, serviceInit)
102
+ : presenterWithCoreTemplate
103
+ .replace(/{{contextName}}/g, contextName)
104
+ .replace(/{{name}}/g, pascalName)
105
+ .replace(/{{corePresenter}}/g, presenterType)
106
+ .replace(/{{serviceImport}}/g, serviceImport)
107
+ .replace(/{{serviceInit}}/g, serviceInit);
108
+
109
+ await writeIfNotExists(filePath, content);
110
+ await updateIndexTs(presenterPath);
111
+
112
+ console.log(`✅ Presenter created at ${filePath}`);
113
+ }