@azure-net/cli 2.0.4 → 2.0.6

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/bin/azure-net.js CHANGED
@@ -18,7 +18,10 @@ import {
18
18
  generateSchema,
19
19
  generatePresenter,
20
20
  generateModule,
21
- generateCrud
21
+ generateCrudBase,
22
+ generateCrudPresenter,
23
+ generatePresenterByService,
24
+ generateServiceByRepo
22
25
  } from '../src/commands/generate/index.js';
23
26
 
24
27
  const program = new Command();
@@ -32,7 +35,7 @@ program
32
35
  program
33
36
  .command('init:lint')
34
37
  .description('Setup linting and commit tools')
35
- .action(initLint)
38
+ .action(initLint);
36
39
 
37
40
  program
38
41
  .command('init:structure')
@@ -113,8 +116,23 @@ program
113
116
  .action(generateModule);
114
117
 
115
118
  program
116
- .command('make:crud')
117
- .description('Generate CRUD module')
118
- .action(generateCrud);
119
+ .command('make:crud-base')
120
+ .description('Generate CRUD repository and service')
121
+ .action(generateCrudBase);
122
+
123
+ program
124
+ .command('make:crud-presenter')
125
+ .description('Generate CRUD presenter for existing service')
126
+ .action(generateCrudPresenter);
127
+
128
+ program
129
+ .command('make:presenter-by-service')
130
+ .description('Generate presenter from existing service')
131
+ .action(generatePresenterByService);
132
+
133
+ program
134
+ .command('make:service-by-repo')
135
+ .description('Generate service from existing repository')
136
+ .action(generateServiceByRepo);
119
137
 
120
138
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azure-net/cli",
3
- "version": "2.0.4",
3
+ "version": "2.0.6",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "azure-net": "bin/azure-net.js"
@@ -0,0 +1,188 @@
1
+ import prompts from 'prompts';
2
+ import path from 'path';
3
+ import { selectContext, getContextPath, toPascalCase, toCamelCase, getAvailableFiles } from '../../utils/contextUtils.js';
4
+ import { writeIfNotExists, updateIndexTs } from '../../utils/fileUtils.js';
5
+
6
+ // Templates for CRUD base generation
7
+ const entityTemplate = `export interface I{{name}} {
8
+ \tid: number;
9
+ \t// Add entity fields here
10
+ \tcreated_at: string;
11
+ \tupdated_at: string;
12
+ }`;
13
+
14
+ const portsIndexTemplate = `import type { I{{name}} } from '\${{context}}/Domain/Entities/{{name}}';
15
+
16
+ export interface I{{name}}Collection {
17
+ \tdata: I{{name}}[];
18
+ \ttotal: number;
19
+ }
20
+
21
+ export interface I{{name}}CollectionQuery {
22
+ \tpage?: number;
23
+ \tperPage?: number;
24
+ \t[key: string]: unknown;
25
+ }
26
+
27
+ export interface I{{name}}CreateRequest {
28
+ \t// Add request fields here
29
+ \t[key: string]: unknown;
30
+ }
31
+
32
+ export interface I{{name}}UpdateRequest {
33
+ \t// Add request fields here
34
+ \t[key: string]: unknown;
35
+ }`;
36
+
37
+ const repositoryTemplate = `import { {{datasource}} } from '{{datasourceImport}}';
38
+ import type { I{{name}} } from '\${{context}}/Domain/Entities/{{name}}';
39
+ import type {
40
+ \tI{{name}}Collection,
41
+ \tI{{name}}CollectionQuery,
42
+ \tI{{name}}CreateRequest,
43
+ \tI{{name}}UpdateRequest
44
+ } from '\${{context}}/Domain/Ports/{{name}}';
45
+
46
+ export class {{name}}Repository {
47
+ \tprivate endpoint = '{{endpoint}}';
48
+ \t
49
+ \tconstructor(private {{datasourceVar}}: {{datasource}}) {}
50
+ \t
51
+ \tpublic async collection(query?: I{{name}}CollectionQuery) {
52
+ \t\treturn this.{{datasourceVar}}.createRequest<I{{name}}Collection>(({ http, query: q }) =>
53
+ \t\t\thttp.get(\`\${this.endpoint}\${q.build(query || {})}\`)
54
+ \t\t).then(res => res.getData());
55
+ \t}
56
+ \t
57
+ \tpublic async resource(id: number) {
58
+ \t\treturn this.{{datasourceVar}}.createRequest<I{{name}}>(({ http }) =>
59
+ \t\t\thttp.get(\`\${this.endpoint}/\${id}\`)
60
+ \t\t).then(res => res.getData());
61
+ \t}
62
+ \t
63
+ \tpublic async create(data: I{{name}}CreateRequest) {
64
+ \t\treturn this.{{datasourceVar}}.createRequest<I{{name}}>(({ http }) =>
65
+ \t\t\thttp.post(this.endpoint, { json: data })
66
+ \t\t).then(res => res.getData());
67
+ \t}
68
+ \t
69
+ \tpublic async update(id: number, data: I{{name}}UpdateRequest) {
70
+ \t\treturn this.{{datasourceVar}}.createRequest<I{{name}}>(({ http }) =>
71
+ \t\t\thttp.put(\`\${this.endpoint}/\${id}\`, { json: data })
72
+ \t\t).then(res => res.getData());
73
+ \t}
74
+ \t
75
+ \tpublic async remove(id: number) {
76
+ \t\treturn this.{{datasourceVar}}.createRequest<void>(({ http }) =>
77
+ \t\t\thttp.delete(\`\${this.endpoint}/\${id}\`)
78
+ \t\t).then(() => undefined);
79
+ \t}
80
+ }`;
81
+
82
+ const serviceTemplate = `import { ClassMirror } from '@azure-net/kit';
83
+ import { {{name}}Repository } from '\${{context}}/Infrastructure/Http/Repositories';
84
+
85
+ export class {{name}}Service extends ClassMirror<{{name}}Repository> {
86
+ \tconstructor(private {{camelName}}Repository: {{name}}Repository) {
87
+ \t\tsuper({{camelName}}Repository);
88
+ \t}
89
+ \t
90
+ \tdeclare collection: {{name}}Repository['collection'];
91
+ \tdeclare resource: {{name}}Repository['resource'];
92
+ \tdeclare create: {{name}}Repository['create'];
93
+ \tdeclare update: {{name}}Repository['update'];
94
+ \tdeclare remove: {{name}}Repository['remove'];
95
+ }`;
96
+
97
+ export default async function generateCrudBase() {
98
+ const context = await selectContext('Select context for CRUD base:');
99
+
100
+ const { name } = await prompts({
101
+ type: 'text',
102
+ name: 'name',
103
+ message: 'Entity name (PascalCase):'
104
+ });
105
+
106
+ const { endpoint } = await prompts({
107
+ type: 'text',
108
+ name: 'endpoint',
109
+ message: 'API endpoint (e.g. /api/users):'
110
+ });
111
+
112
+ // Get available datasources
113
+ const contextPath = getContextPath(context);
114
+ const sharedDatasources = await getAvailableFiles(
115
+ path.join(process.cwd(), 'src/app/shared/Datasource')
116
+ );
117
+ const contextDatasources = await getAvailableFiles(
118
+ path.join(contextPath, 'Infrastructure/Http/Datasource')
119
+ );
120
+
121
+ const allDatasources = [
122
+ ...sharedDatasources.map(d => ({ title: `${d} (shared)`, value: { name: d, from: 'shared' } })),
123
+ ...contextDatasources.map(d => ({ title: `${d} (${context})`, value: { name: d, from: context } }))
124
+ ];
125
+
126
+ const { datasource } = await prompts({
127
+ type: 'select',
128
+ name: 'datasource',
129
+ message: 'Select datasource:',
130
+ choices: allDatasources
131
+ });
132
+
133
+ const pascalName = toPascalCase(name);
134
+ const camelName = toCamelCase(name);
135
+
136
+ console.log('\nšŸš€ Generating CRUD base (repository & service)...\n');
137
+
138
+ // 1. Create Entity
139
+ const entityPath = path.join(contextPath, 'Domain/Entities', pascalName);
140
+ await writeIfNotExists(
141
+ path.join(entityPath, 'index.ts'),
142
+ entityTemplate.replace(/{{name}}/g, pascalName)
143
+ );
144
+
145
+ // 2. Create Ports
146
+ const portsPath = path.join(contextPath, 'Domain/Ports', pascalName);
147
+ await writeIfNotExists(
148
+ path.join(portsPath, 'index.ts'),
149
+ portsIndexTemplate
150
+ .replace(/{{name}}/g, pascalName)
151
+ .replace(/{{context}}/g, context)
152
+ );
153
+
154
+ // 3. Create Repository
155
+ const repoPath = path.join(contextPath, 'Infrastructure/Http/Repositories');
156
+ const datasourceImport = datasource.from === 'shared'
157
+ ? '$shared/Datasource'
158
+ : `\$${context}/Infrastructure/Http/Datasource`;
159
+
160
+ await writeIfNotExists(
161
+ path.join(repoPath, `${pascalName}Repository.ts`),
162
+ repositoryTemplate
163
+ .replace(/{{name}}/g, pascalName)
164
+ .replace(/{{context}}/g, context)
165
+ .replace(/{{endpoint}}/g, endpoint)
166
+ .replace(/{{datasource}}/g, datasource.name)
167
+ .replace(/{{datasourceImport}}/g, datasourceImport)
168
+ .replace(/{{datasourceVar}}/g, toCamelCase(datasource.name))
169
+ );
170
+ await updateIndexTs(repoPath);
171
+
172
+ // 4. Create Service
173
+ const servicePath = path.join(contextPath, 'Application/Services');
174
+ await writeIfNotExists(
175
+ path.join(servicePath, `${pascalName}Service.ts`),
176
+ serviceTemplate
177
+ .replace(/{{name}}/g, pascalName)
178
+ .replace(/{{camelName}}/g, camelName)
179
+ .replace(/{{context}}/g, context)
180
+ );
181
+ await updateIndexTs(servicePath);
182
+
183
+ console.log(`āœ… CRUD base for ${pascalName} created successfully!`);
184
+ console.log(`\nšŸ’” Remember to:`);
185
+ console.log(` 1. Add ${pascalName}Repository to InfrastructureProvider`);
186
+ console.log(` 2. Add ${pascalName}Service to ApplicationProvider`);
187
+ console.log(` 3. Use 'make:crud-presenter' to create the CRUD presenter`);
188
+ }
@@ -0,0 +1,220 @@
1
+ import prompts from 'prompts';
2
+ import path from 'path';
3
+ import fs from 'fs/promises';
4
+ import { selectContext, getContextPath, toPascalCase, getAvailableFiles, getApplicationProviderServices } from '../../utils/contextUtils.js';
5
+ import { writeIfNotExists } from '../../utils/fileUtils.js';
6
+
7
+ const createSchemaTemplate = `import { {{schemaFactory}} } from '{{schemaImport}}';
8
+ import type { I{{name}}CreateRequest } from '\${{context}}/Domain/Ports/{{entity}}';
9
+
10
+ export const Create{{name}}Schema = {{schemaFactory}}<I{{name}}CreateRequest>()
11
+ \t.rules((rules) => ({
12
+ \t\t// Add validation rules here
13
+ \t}))
14
+ \t.create();`;
15
+
16
+ const updateSchemaTemplate = `import { {{schemaFactory}} } from '{{schemaImport}}';
17
+ import type { I{{name}}UpdateRequest } from '\${{context}}/Domain/Ports/{{entity}}';
18
+
19
+ export const Update{{name}}Schema = {{schemaFactory}}<I{{name}}UpdateRequest>()
20
+ \t.rules((rules) => ({
21
+ \t\t// Add validation rules here
22
+ \t}))
23
+ \t.create();`;
24
+
25
+ const presenterWithSharedTemplate = `import { {{presenterFactory}} } from '$shared/Presenter';
26
+ import { ApplicationProvider } from '\${{context}}/Application';
27
+ import { Create{{name}}Schema, Update{{name}}Schema } from './Schema';
28
+ import type {
29
+ \tI{{name}}CollectionQuery,
30
+ \tI{{name}}CreateRequest,
31
+ \tI{{name}}UpdateRequest
32
+ } from '\${{context}}/Domain/Ports/{{name}}';
33
+
34
+ export const {{name}}Presenter = {{presenterFactory}}('{{name}}Presenter', ({ createAsyncResource, createAsyncAction }) => {
35
+ \tconst { {{serviceName}} } = ApplicationProvider();
36
+ \t
37
+ \tconst collection = async (query?: I{{name}}CollectionQuery) =>
38
+ \t\tawait createAsyncResource({{serviceName}}.collection(query));
39
+ \t
40
+ \tconst resource = async (id: number) =>
41
+ \t\tawait createAsyncResource({{serviceName}}.resource(id));
42
+ \t
43
+ \tconst create = async (request: I{{name}}CreateRequest) =>
44
+ \t\tawait createAsyncAction({{serviceName}}.create(Create{{name}}Schema.from(request).json()));
45
+ \t
46
+ \tconst update = async (id: number, request: I{{name}}UpdateRequest) =>
47
+ \t\tawait createAsyncAction({{serviceName}}.update(id, Update{{name}}Schema.from(request).json()));
48
+ \t
49
+ \tconst remove = async (id: number) =>
50
+ \t\tawait createAsyncAction({{serviceName}}.remove(id));
51
+ \t
52
+ \treturn { collection, resource, create, update, remove };
53
+ });`;
54
+
55
+ const presenterWithoutSharedTemplate = `import { createPresenter } from '@azure-net/kit';
56
+ import { ApplicationProvider } from '\${{context}}/Application';
57
+ import { Create{{name}}Schema, Update{{name}}Schema } from './Schema';
58
+ import type {
59
+ \tI{{name}}CollectionQuery,
60
+ \tI{{name}}CreateRequest,
61
+ \tI{{name}}UpdateRequest
62
+ } from '\${{context}}/Domain/Ports/{{name}}';
63
+
64
+ export const {{name}}Presenter = createPresenter('{{name}}Presenter', () => {
65
+ \tconst { {{serviceName}} } = ApplicationProvider();
66
+ \t
67
+ \tconst collection = async (query?: I{{name}}CollectionQuery) =>
68
+ \t\tawait {{serviceName}}.collection(query);
69
+ \t
70
+ \tconst resource = async (id: number) =>
71
+ \t\tawait {{serviceName}}.resource(id);
72
+ \t
73
+ \tconst create = async (request: I{{name}}CreateRequest) =>
74
+ \t\tawait {{serviceName}}.create(Create{{name}}Schema.from(request).json());
75
+ \t
76
+ \tconst update = async (id: number, request: I{{name}}UpdateRequest) =>
77
+ \t\tawait {{serviceName}}.update(id, Update{{name}}Schema.from(request).json());
78
+ \t
79
+ \tconst remove = async (id: number) =>
80
+ \t\tawait {{serviceName}}.remove(id);
81
+ \t
82
+ \treturn { collection, resource, create, update, remove };
83
+ });`;
84
+
85
+ export default async function generateCrudPresenter() {
86
+ const context = await selectContext('Select context for CRUD presenter:');
87
+
88
+ // Get available services from ApplicationProvider
89
+ const services = await getApplicationProviderServices(context);
90
+
91
+ if (services.length === 0) {
92
+ console.error('āŒ No services found in ApplicationProvider. Add services manually first.');
93
+ return;
94
+ }
95
+
96
+ const { service } = await prompts({
97
+ type: 'select',
98
+ name: 'service',
99
+ message: 'Select service for CRUD:',
100
+ choices: services.map(s => ({ title: s, value: s }))
101
+ });
102
+
103
+ // Extract entity name from service name
104
+ const entityName = service.replace('Service', '');
105
+ const pascalName = toPascalCase(entityName);
106
+
107
+ // Get schema factory
108
+ const sharedSchemas = await getAvailableFiles(
109
+ path.join(process.cwd(), 'src/app/shared/Schema')
110
+ );
111
+
112
+ const { schemaType } = await prompts({
113
+ type: 'select',
114
+ name: 'schemaType',
115
+ message: 'Select schema factory:',
116
+ choices: [
117
+ { title: 'Use createSchemaFactory from package', value: 'package' },
118
+ ...sharedSchemas.map(s => ({ title: `Use ${s} from shared`, value: s }))
119
+ ]
120
+ });
121
+
122
+ // Get presenter factory
123
+ const sharedPresenters = await getAvailableFiles(
124
+ path.join(process.cwd(), 'src/app/shared/Presenter')
125
+ );
126
+
127
+ const { presenterType } = await prompts({
128
+ type: 'select',
129
+ name: 'presenterType',
130
+ message: 'Select presenter factory:',
131
+ choices: [
132
+ { title: 'Use createPresenter from package', value: 'package' },
133
+ ...sharedPresenters.map(p => ({ title: `Use ${p} from shared`, value: p }))
134
+ ]
135
+ });
136
+
137
+ const contextPath = getContextPath(context);
138
+ const deliveryModulePath = path.join(contextPath, 'Delivery', pascalName);
139
+ const schemaPath = path.join(deliveryModulePath, 'Schema');
140
+
141
+ console.log('\nšŸš€ Generating CRUD presenter...\n');
142
+
143
+ // Create schemas
144
+ const schemaFactory = schemaType === 'package' ? 'Schema' : schemaType;
145
+ const schemaImport = schemaType === 'package'
146
+ ? '@azure-net/kit'
147
+ : '$shared/Schema';
148
+
149
+ if (schemaType === 'package') {
150
+ const schemaImportFull = `import { createSchemaFactory } from '@azure-net/kit';
151
+ import { createRules, validationMessagesI18n } from '@azure-net/kit/schema';
152
+
153
+ const Schema = createSchemaFactory(createRules(validationMessagesI18n));`;
154
+
155
+ await writeIfNotExists(
156
+ path.join(schemaPath, `Create${pascalName}Schema.ts`),
157
+ schemaImportFull + '\n' + createSchemaTemplate
158
+ .replace('import { {{schemaFactory}} } from \'{{schemaImport}}\';\n', '')
159
+ .replace(/{{name}}/g, pascalName)
160
+ .replace(/{{context}}/g, context)
161
+ .replace(/{{entity}}/g, pascalName)
162
+ .replace(/{{schemaFactory}}/g, 'Schema')
163
+ );
164
+
165
+ await writeIfNotExists(
166
+ path.join(schemaPath, `Update${pascalName}Schema.ts`),
167
+ schemaImportFull + '\n' + updateSchemaTemplate
168
+ .replace('import { {{schemaFactory}} } from \'{{schemaImport}}\';\n', '')
169
+ .replace(/{{name}}/g, pascalName)
170
+ .replace(/{{context}}/g, context)
171
+ .replace(/{{entity}}/g, pascalName)
172
+ .replace(/{{schemaFactory}}/g, 'Schema')
173
+ );
174
+ } else {
175
+ await writeIfNotExists(
176
+ path.join(schemaPath, `Create${pascalName}Schema.ts`),
177
+ createSchemaTemplate
178
+ .replace(/{{name}}/g, pascalName)
179
+ .replace(/{{context}}/g, context)
180
+ .replace(/{{entity}}/g, pascalName)
181
+ .replace(/{{schemaFactory}}/g, schemaFactory)
182
+ .replace(/{{schemaImport}}/g, schemaImport)
183
+ );
184
+
185
+ await writeIfNotExists(
186
+ path.join(schemaPath, `Update${pascalName}Schema.ts`),
187
+ updateSchemaTemplate
188
+ .replace(/{{name}}/g, pascalName)
189
+ .replace(/{{context}}/g, context)
190
+ .replace(/{{entity}}/g, pascalName)
191
+ .replace(/{{schemaFactory}}/g, schemaFactory)
192
+ .replace(/{{schemaImport}}/g, schemaImport)
193
+ );
194
+ }
195
+ await updateIndexTs(schemaPath);
196
+
197
+ // Create Presenter
198
+ const presenterTemplate = presenterType === 'package'
199
+ ? presenterWithoutSharedTemplate
200
+ : presenterWithSharedTemplate;
201
+
202
+ const presenterFactory = presenterType === 'package' ? 'createPresenter' : presenterType;
203
+
204
+ await writeIfNotExists(
205
+ path.join(deliveryModulePath, `${pascalName}Presenter.ts`),
206
+ presenterTemplate
207
+ .replace(/{{name}}/g, pascalName)
208
+ .replace(/{{context}}/g, context)
209
+ .replace(/{{serviceName}}/g, service)
210
+ .replace(/{{presenterFactory}}/g, presenterFactory)
211
+ );
212
+
213
+ // Create module index
214
+ await writeIfNotExists(
215
+ path.join(deliveryModulePath, 'index.ts'),
216
+ `export * from './${pascalName}Presenter';\nexport * from './Schema';`
217
+ );
218
+
219
+ console.log(`āœ… CRUD presenter for ${pascalName} created successfully!`);
220
+ }
@@ -1,32 +1,20 @@
1
1
  import prompts from 'prompts';
2
2
  import path from 'path';
3
- import { selectContext, getContextPath, toPascalCase, getAvailableFiles, toCamelCase } from '../../utils/contextUtils.js';
3
+ import { selectContext, getContextPath, toPascalCase, getAvailableFiles } from '../../utils/contextUtils.js';
4
4
  import { writeIfNotExists, updateIndexTs } from '../../utils/fileUtils.js';
5
- import { ensureProvider, addToProvider } from '../../utils/providerUtils.js';
6
5
 
7
6
  const datasourceWithResponseTemplate = `import { BaseHttpDatasource, type CreateRequestCallbackType } from '@azure-net/kit/infra';
8
7
  import { {{responseClass}}, type {{responseInterface}} } from '{{responseImport}}';
8
+ import { HttpService } from '@azure-net/kit/infra';
9
9
 
10
10
  export class {{name}}Datasource extends BaseHttpDatasource {
11
- \tconstructor(private http: HttpService) {
12
- \t\tsuper({ http });
13
- \t}
14
-
15
11
  \tasync createRequest<T>(callback: CreateRequestCallbackType<{{responseInterface}}<T>>) {
16
12
  \t\treturn new {{responseClass}}<T>(await this.createRawRequest<{{responseInterface}}<T>>(callback));
17
13
  \t}
18
14
  }`;
19
15
 
20
- const datasourceWithoutResponseTemplate = `import { BaseHttpDatasource } from '@azure-net/kit/infra';
21
-
22
- export class {{name}}Datasource extends BaseHttpDatasource {
23
- \tconstructor(private http: HttpService) {
24
- \t\tsuper({ http });
25
- \t}
26
- }`;
27
-
28
16
  export default async function generateDatasource() {
29
- const context = await selectContext();
17
+ const context = await selectContext('Select context for datasource (or shared):');
30
18
 
31
19
  const { name } = await prompts({
32
20
  type: 'text',
@@ -35,22 +23,23 @@ export default async function generateDatasource() {
35
23
  });
36
24
 
37
25
  const pascalName = toPascalCase(name);
38
- const camelName = toCamelCase(name);
39
- const contextPath = getContextPath(context);
26
+ const contextPath = context === 'shared'
27
+ ? path.join(process.cwd(), 'src/app/shared')
28
+ : getContextPath(context);
40
29
 
41
30
  // Get available responses
42
- const coreResponses = await getAvailableFiles(
43
- path.join(process.cwd(), 'src/app/core/Response')
31
+ const sharedResponses = await getAvailableFiles(
32
+ path.join(process.cwd(), 'src/app/shared/Response')
44
33
  );
45
34
 
46
- const contextResponses = context !== 'core'
35
+ const contextResponses = context !== 'shared'
47
36
  ? await getAvailableFiles(path.join(contextPath, 'Infrastructure/Response'))
48
37
  : [];
49
38
 
50
39
  const allResponses = [
51
- ...coreResponses.map(r => ({
52
- title: `${r} (core)`,
53
- value: { name: r, from: 'core' }
40
+ ...sharedResponses.map(r => ({
41
+ title: `${r} (shared)`,
42
+ value: { name: r, from: 'shared' }
54
43
  })),
55
44
  ...contextResponses.map(r => ({
56
45
  title: `${r} (${context})`,
@@ -70,18 +59,16 @@ export default async function generateDatasource() {
70
59
  choices: allResponses
71
60
  });
72
61
 
73
- // Determine interface name - typically IBackendApiDataSourceResponse or similar
62
+ // Determine interface name
74
63
  const responseInterface = `I${response.name.replace('Response', '')}Response`;
75
64
 
76
- const datasourcePath = context === 'core'
65
+ const datasourcePath = context === 'shared'
77
66
  ? path.join(contextPath, 'Datasource')
78
- : path.join(contextPath, 'Infrastructure', 'Http', 'Datasource');
67
+ : path.join(contextPath, 'Infrastructure/Http/Datasource');
79
68
 
80
- const responseImport = response.from === 'core'
81
- ? '$core/Response'
82
- : context === 'core'
83
- ? '../Response'
84
- : '../../Response';
69
+ const responseImport = response.from === 'shared'
70
+ ? context === 'shared' ? '../Response' : '$shared/Response'
71
+ : `\$${context}/Infrastructure/Response`;
85
72
 
86
73
  const content = datasourceWithResponseTemplate
87
74
  .replace(/{{name}}/g, pascalName)
@@ -93,63 +80,5 @@ export default async function generateDatasource() {
93
80
  await writeIfNotExists(filePath, content);
94
81
  await updateIndexTs(datasourcePath);
95
82
 
96
- // If not in core, ensure DatasourceProvider exists
97
- if (context !== 'core') {
98
- const contextName = toPascalCase(context);
99
- const providerPath = path.join(contextPath, 'Infrastructure/Providers/DatasourceProvider.ts');
100
-
101
- if (!(await fs.access(providerPath).then(() => true).catch(() => false))) {
102
- const providerContent = `import { createBoundaryProvider } from '@azure-net/kit';
103
- import { HttpService } from '@azure-net/kit/infra';
104
- import { ${pascalName}Datasource } from '../Http/Datasource';
105
-
106
- export const DatasourceProvider = createBoundaryProvider('${contextName}DatasourceProvider', () => ({
107
- \t${pascalName}Datasource: () => new ${pascalName}Datasource(
108
- \t\tnew HttpService({ baseUrl: 'https://api.example.com' })
109
- \t)
110
- }));`;
111
- await writeIfNotExists(providerPath, providerContent);
112
- await updateIndexTs(path.dirname(providerPath));
113
- } else {
114
- // Add datasource to existing provider
115
- await addDatasourceToProvider(providerPath, pascalName);
116
- }
117
- }
118
-
119
83
  console.log(`āœ… Datasource created at ${filePath}`);
120
- }
121
-
122
- async function addDatasourceToProvider(providerPath, datasourceName) {
123
- let content = await fs.readFile(providerPath, 'utf-8');
124
-
125
- // Add import if not exists
126
- if (!content.includes(`${datasourceName}Datasource`)) {
127
- const importStatement = `import { ${datasourceName}Datasource } from '../Http/Datasource';`;
128
- const lastImportIndex = content.lastIndexOf('import ');
129
- const endOfImportLine = content.indexOf('\n', lastImportIndex);
130
- content = content.slice(0, endOfImportLine + 1) + importStatement + '\n' + content.slice(endOfImportLine + 1);
131
- }
132
-
133
- // Add factory method
134
- const factoryLine = `\t${datasourceName}Datasource: () => new ${datasourceName}Datasource(
135
- \t\tnew HttpService({ baseUrl: 'https://api.example.com' })
136
- \t)`;
137
-
138
- if (!content.includes(`${datasourceName}Datasource:`)) {
139
- const returnIndex = content.indexOf('=> ({');
140
- const endOfReturn = content.indexOf('})', returnIndex);
141
- const beforeEnd = content.lastIndexOf('\n', endOfReturn);
142
-
143
- // Check if there are existing entries
144
- const servicesSection = content.substring(returnIndex, endOfReturn);
145
- const hasServices = servicesSection.includes(':');
146
-
147
- if (hasServices) {
148
- content = content.slice(0, beforeEnd) + ',\n' + factoryLine + content.slice(beforeEnd);
149
- } else {
150
- content = content.slice(0, beforeEnd + 1) + factoryLine + content.slice(beforeEnd);
151
- }
152
- }
153
-
154
- await fs.writeFile(providerPath, content, 'utf-8');
155
84
  }
@@ -6,11 +6,13 @@ import generateSchema from './generateSchema.js';
6
6
 
7
7
  export default async function generateModule() {
8
8
  console.log('šŸš€ Generating complete module...\n');
9
+ console.log('āš ļø Note: This will create repository, service, presenter and schemas.');
10
+ console.log(' You will need to manually wire them in the providers.\n');
9
11
 
10
12
  const { confirm } = await prompts({
11
13
  type: 'confirm',
12
14
  name: 'confirm',
13
- message: 'This will create repository, service, presenter and schemas. Continue?'
15
+ message: 'Continue?'
14
16
  });
15
17
 
16
18
  if (confirm) {
@@ -19,5 +21,11 @@ export default async function generateModule() {
19
21
  await generatePresenter();
20
22
  await generateSchema(); // Create schema
21
23
  await generateSchema(); // Update schema
24
+
25
+ console.log('\nāœ… Module generation complete!');
26
+ console.log('\nšŸ’” Remember to:');
27
+ console.log(' 1. Add repository to InfrastructureProvider');
28
+ console.log(' 2. Add service to ApplicationProvider');
29
+ console.log(' 3. Update presenter to use the service from ApplicationProvider');
22
30
  }
23
31
  }