@azure-net/cli 2.0.5 → 2.0.7
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 +23 -5
- package/package.json +1 -1
- package/src/commands/generate/generateCrudBase.js +188 -0
- package/src/commands/generate/generateCrudPresenter.js +220 -0
- package/src/commands/generate/generateDatasource.js +14 -25
- package/src/commands/generate/generateModule.js +9 -1
- package/src/commands/generate/generatePresenter.js +19 -49
- package/src/commands/generate/generatePresenterByService.js +102 -0
- package/src/commands/generate/generateRepository.js +7 -26
- package/src/commands/generate/generateResponse.js +6 -3
- package/src/commands/generate/generateService.js +3 -26
- package/src/commands/generate/generateServiceByRepo.js +86 -0
- package/src/commands/generate/index.js +4 -1
- package/src/commands/init/initAliases.js +1 -1
- package/src/commands/init/initMiddleware.js +4 -4
- package/src/commands/init/initSchema.js +2 -2
- package/src/commands/init/initStructure.js +76 -15
- package/src/commands/init/initTranslations.js +4 -4
- package/src/utils/contextUtils.js +25 -4
- package/src/commands/generate/generateCrud.js +0 -400
- package/src/utils/providerUtils.js +0 -254
package/bin/azure-net.js
CHANGED
|
@@ -18,7 +18,10 @@ import {
|
|
|
18
18
|
generateSchema,
|
|
19
19
|
generatePresenter,
|
|
20
20
|
generateModule,
|
|
21
|
-
|
|
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
|
|
118
|
-
.action(
|
|
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
|
@@ -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 {updateIndexTs, 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,6 +1,5 @@
|
|
|
1
1
|
import prompts from 'prompts';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import fs from 'fs/promises';
|
|
4
3
|
import { selectContext, getContextPath, toPascalCase, getAvailableFiles } from '../../utils/contextUtils.js';
|
|
5
4
|
import { writeIfNotExists, updateIndexTs } from '../../utils/fileUtils.js';
|
|
6
5
|
|
|
@@ -9,22 +8,13 @@ import { {{responseClass}}, type {{responseInterface}} } from '{{responseImport}
|
|
|
9
8
|
import { HttpService } from '@azure-net/kit/infra';
|
|
10
9
|
|
|
11
10
|
export class {{name}}Datasource extends BaseHttpDatasource {
|
|
12
|
-
\tconstructor(config?: { baseUrl?: string; [key: string]: unknown }) {
|
|
13
|
-
\t\tsuper({
|
|
14
|
-
\t\t\thttp: new HttpService({
|
|
15
|
-
\t\t\t\tbaseUrl: config?.baseUrl || 'https://api.example.com',
|
|
16
|
-
\t\t\t\t...config
|
|
17
|
-
\t\t\t})
|
|
18
|
-
\t\t});
|
|
19
|
-
\t}
|
|
20
|
-
|
|
21
11
|
\tasync createRequest<T>(callback: CreateRequestCallbackType<{{responseInterface}}<T>>) {
|
|
22
12
|
\t\treturn new {{responseClass}}<T>(await this.createRawRequest<{{responseInterface}}<T>>(callback));
|
|
23
13
|
\t}
|
|
24
14
|
}`;
|
|
25
15
|
|
|
26
16
|
export default async function generateDatasource() {
|
|
27
|
-
const context = await selectContext();
|
|
17
|
+
const context = await selectContext('Select context for datasource (or shared):');
|
|
28
18
|
|
|
29
19
|
const { name } = await prompts({
|
|
30
20
|
type: 'text',
|
|
@@ -33,21 +23,23 @@ export default async function generateDatasource() {
|
|
|
33
23
|
});
|
|
34
24
|
|
|
35
25
|
const pascalName = toPascalCase(name);
|
|
36
|
-
const contextPath =
|
|
26
|
+
const contextPath = context === 'shared'
|
|
27
|
+
? path.join(process.cwd(), 'src/app/shared')
|
|
28
|
+
: getContextPath(context);
|
|
37
29
|
|
|
38
30
|
// Get available responses
|
|
39
|
-
const
|
|
40
|
-
path.join(process.cwd(), 'src/app/
|
|
31
|
+
const sharedResponses = await getAvailableFiles(
|
|
32
|
+
path.join(process.cwd(), 'src/app/shared/Response')
|
|
41
33
|
);
|
|
42
34
|
|
|
43
|
-
const contextResponses = context !== '
|
|
35
|
+
const contextResponses = context !== 'shared'
|
|
44
36
|
? await getAvailableFiles(path.join(contextPath, 'Infrastructure/Response'))
|
|
45
37
|
: [];
|
|
46
38
|
|
|
47
39
|
const allResponses = [
|
|
48
|
-
...
|
|
49
|
-
title: `${r} (
|
|
50
|
-
value: { name: r, from: '
|
|
40
|
+
...sharedResponses.map(r => ({
|
|
41
|
+
title: `${r} (shared)`,
|
|
42
|
+
value: { name: r, from: 'shared' }
|
|
51
43
|
})),
|
|
52
44
|
...contextResponses.map(r => ({
|
|
53
45
|
title: `${r} (${context})`,
|
|
@@ -70,15 +62,13 @@ export default async function generateDatasource() {
|
|
|
70
62
|
// Determine interface name
|
|
71
63
|
const responseInterface = `I${response.name.replace('Response', '')}Response`;
|
|
72
64
|
|
|
73
|
-
const datasourcePath = context === '
|
|
65
|
+
const datasourcePath = context === 'shared'
|
|
74
66
|
? path.join(contextPath, 'Datasource')
|
|
75
67
|
: path.join(contextPath, 'Infrastructure/Http/Datasource');
|
|
76
68
|
|
|
77
|
-
const responseImport = response.from === '
|
|
78
|
-
? context === '
|
|
79
|
-
: context
|
|
80
|
-
? `../contexts/${context}/Infrastructure/Response`
|
|
81
|
-
: '../../Response';
|
|
69
|
+
const responseImport = response.from === 'shared'
|
|
70
|
+
? context === 'shared' ? '../Response' : '$shared/Response'
|
|
71
|
+
: `\$${context}/Infrastructure/Response`;
|
|
82
72
|
|
|
83
73
|
const content = datasourceWithResponseTemplate
|
|
84
74
|
.replace(/{{name}}/g, pascalName)
|
|
@@ -91,5 +81,4 @@ export default async function generateDatasource() {
|
|
|
91
81
|
await updateIndexTs(datasourcePath);
|
|
92
82
|
|
|
93
83
|
console.log(`✅ Datasource created at ${filePath}`);
|
|
94
|
-
console.log(`\n💡 Remember to configure the datasource in DatasourceProvider when using it in repositories.`);
|
|
95
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: '
|
|
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
|
}
|