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