@azure-net/cli 2.0.0 → 2.0.2
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 +7 -1
- package/package.json +1 -1
- package/src/commands/generate/generateCrud.js +146 -91
- package/src/commands/generate/generateDatasource.js +10 -5
- package/src/commands/generate/generatePresenter.js +14 -14
- package/src/commands/generate/generateRepository.js +25 -20
- package/src/commands/generate/generateSchema.js +35 -9
- package/src/commands/generate/generateService.js +15 -14
- package/src/commands/init/index.js +2 -1
- package/src/commands/init/initLint.js +138 -0
- package/src/commands/init/initMiddleware.js +9 -24
- package/src/commands/init/initSchema.js +2 -2
- package/src/commands/init/initStructure.js +40 -29
- package/src/commands/init/initTranslations.js +14 -9
- package/src/utils/fileUtils.js +20 -1
- package/src/utils/providerUtils.js +49 -20
package/bin/azure-net.js
CHANGED
|
@@ -7,7 +7,8 @@ import {
|
|
|
7
7
|
initMiddleware,
|
|
8
8
|
initEdges,
|
|
9
9
|
initTranslations,
|
|
10
|
-
initSchema
|
|
10
|
+
initSchema,
|
|
11
|
+
initLint
|
|
11
12
|
} from '../src/commands/init/index.js';
|
|
12
13
|
import {
|
|
13
14
|
generateDatasource,
|
|
@@ -28,6 +29,11 @@ program
|
|
|
28
29
|
.version('2.0.0');
|
|
29
30
|
|
|
30
31
|
// Init commands
|
|
32
|
+
program
|
|
33
|
+
.command('init:lint')
|
|
34
|
+
.description('Setup linting and commit tools')
|
|
35
|
+
.action(initLint)
|
|
36
|
+
|
|
31
37
|
program
|
|
32
38
|
.command('init:structure')
|
|
33
39
|
.description('Generate base folder structure')
|
package/package.json
CHANGED
|
@@ -2,74 +2,85 @@ import prompts from 'prompts';
|
|
|
2
2
|
import fs from 'fs/promises';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { selectContext, getContextPath, toPascalCase, toCamelCase, getAvailableFiles } from '../../utils/contextUtils.js';
|
|
5
|
-
import { writeIfNotExists, updateIndexTs } from '../../utils/fileUtils.js';
|
|
5
|
+
import { writeIfNotExists, updateIndexTs, ensureIndexExports } from '../../utils/fileUtils.js';
|
|
6
6
|
import { ensureProvider, addToProvider } from '../../utils/providerUtils.js';
|
|
7
7
|
|
|
8
8
|
// Templates for CRUD generation
|
|
9
9
|
const entityTemplate = `export interface I{{name}} {
|
|
10
10
|
\tid: number;
|
|
11
11
|
\t// Add entity fields here
|
|
12
|
-
\
|
|
13
|
-
|
|
12
|
+
\tcreated_at: string;
|
|
13
|
+
\tupdated_at: string;
|
|
14
|
+
}`;
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
const portsIndexTemplate = `import type { I{{name}} } from '../../Entities/{{name}}';
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
export interface I{{name}}Collection {
|
|
19
|
+
\tdata: I{{name}}[];
|
|
20
|
+
\ttotal: number;
|
|
21
|
+
}
|
|
18
22
|
|
|
19
|
-
export type I{{name}}CreateRequest = Omit<I{{name}}, 'id'>;
|
|
20
|
-
export type I{{name}}UpdateRequest = Partial<I{{name}}CreateRequest>;
|
|
21
23
|
export interface I{{name}}CollectionQuery {
|
|
24
|
+
\tpage?: number;
|
|
25
|
+
\tperPage?: number;
|
|
22
26
|
\t[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface I{{name}}CreateRequest {
|
|
30
|
+
\t// Add request fields here
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface I{{name}}UpdateRequest {
|
|
34
|
+
\t// Add request fields here
|
|
23
35
|
}`;
|
|
24
36
|
|
|
25
37
|
const repositoryTemplate = `import { {{datasource}} } from '{{datasourceImport}}';
|
|
26
|
-
import type {
|
|
38
|
+
import type { I{{name}} } from '\${{context}}/Domain/Entities/{{name}}';
|
|
39
|
+
import type {
|
|
27
40
|
\tI{{name}}Collection,
|
|
28
|
-
\tI{{name}},
|
|
41
|
+
\tI{{name}}CollectionQuery,
|
|
29
42
|
\tI{{name}}CreateRequest,
|
|
30
|
-
\tI{{name}}UpdateRequest
|
|
31
|
-
|
|
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';
|
|
43
|
+
\tI{{name}}UpdateRequest
|
|
44
|
+
} from '\${{context}}/Domain/Ports/{{name}}';
|
|
34
45
|
|
|
35
46
|
export class {{name}}Repository {
|
|
36
47
|
\tprivate endpoint = '{{endpoint}}';
|
|
37
48
|
\t
|
|
38
49
|
\tconstructor(private {{datasourceVar}}: {{datasource}}) {}
|
|
39
50
|
\t
|
|
40
|
-
\tpublic async collection(
|
|
41
|
-
\t\treturn
|
|
42
|
-
\t\t\thttp.get(\`\${this.endpoint}\${
|
|
43
|
-
\t\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());
|
|
44
55
|
\t}
|
|
45
56
|
\t
|
|
46
57
|
\tpublic async resource(id: number) {
|
|
47
|
-
\t\treturn
|
|
58
|
+
\t\treturn this.{{datasourceVar}}.createRequest<I{{name}}>(({ http }) =>
|
|
48
59
|
\t\t\thttp.get(\`\${this.endpoint}/\${id}\`)
|
|
49
|
-
\t\t);
|
|
60
|
+
\t\t).then(res => res.getData());
|
|
50
61
|
\t}
|
|
51
62
|
\t
|
|
52
|
-
\tpublic async create(data: CreateRequest) {
|
|
53
|
-
\t\treturn
|
|
63
|
+
\tpublic async create(data: I{{name}}CreateRequest) {
|
|
64
|
+
\t\treturn this.{{datasourceVar}}.createRequest<I{{name}}>(({ http }) =>
|
|
54
65
|
\t\t\thttp.post(this.endpoint, { json: data })
|
|
55
|
-
\t\t);
|
|
66
|
+
\t\t).then(res => res.getData());
|
|
56
67
|
\t}
|
|
57
68
|
\t
|
|
58
|
-
\tpublic async update(id: number, data: UpdateRequest) {
|
|
59
|
-
\t\treturn
|
|
69
|
+
\tpublic async update(id: number, data: I{{name}}UpdateRequest) {
|
|
70
|
+
\t\treturn this.{{datasourceVar}}.createRequest<I{{name}}>(({ http }) =>
|
|
60
71
|
\t\t\thttp.put(\`\${this.endpoint}/\${id}\`, { json: data })
|
|
61
|
-
\t\t);
|
|
72
|
+
\t\t).then(res => res.getData());
|
|
62
73
|
\t}
|
|
63
74
|
\t
|
|
64
75
|
\tpublic async remove(id: number) {
|
|
65
|
-
\t\treturn
|
|
76
|
+
\t\treturn this.{{datasourceVar}}.createRequest<void>(({ http }) =>
|
|
66
77
|
\t\t\thttp.delete(\`\${this.endpoint}/\${id}\`)
|
|
67
|
-
\t\t);
|
|
78
|
+
\t\t).then(() => undefined);
|
|
68
79
|
\t}
|
|
69
80
|
}`;
|
|
70
81
|
|
|
71
82
|
const serviceTemplate = `import { ClassMirror } from '@azure-net/kit';
|
|
72
|
-
import { {{name}}Repository } from '../../Infrastructure/Repositories
|
|
83
|
+
import { {{name}}Repository } from '../../Infrastructure/Http/Repositories';
|
|
73
84
|
|
|
74
85
|
export class {{name}}Service extends ClassMirror<{{name}}Repository> {
|
|
75
86
|
\tconstructor(private {{camelName}}Repository: {{name}}Repository) {
|
|
@@ -84,7 +95,7 @@ export class {{name}}Service extends ClassMirror<{{name}}Repository> {
|
|
|
84
95
|
}`;
|
|
85
96
|
|
|
86
97
|
const createSchemaTemplate = `import { {{schemaFactory}} } from '{{schemaImport}}';
|
|
87
|
-
import type { I{{name}}CreateRequest } from '
|
|
98
|
+
import type { I{{name}}CreateRequest } from '\${{context}}/Domain/Ports/{{name}}';
|
|
88
99
|
|
|
89
100
|
export const Create{{name}}Schema = {{schemaFactory}}<I{{name}}CreateRequest>()
|
|
90
101
|
\t.rules((rules) => ({
|
|
@@ -93,7 +104,7 @@ export const Create{{name}}Schema = {{schemaFactory}}<I{{name}}CreateRequest>()
|
|
|
93
104
|
\t.create();`;
|
|
94
105
|
|
|
95
106
|
const updateSchemaTemplate = `import { {{schemaFactory}} } from '{{schemaImport}}';
|
|
96
|
-
import type { I{{name}}UpdateRequest } from '
|
|
107
|
+
import type { I{{name}}UpdateRequest } from '\${{context}}/Domain/Ports/{{name}}';
|
|
97
108
|
|
|
98
109
|
export const Update{{name}}Schema = {{schemaFactory}}<I{{name}}UpdateRequest>()
|
|
99
110
|
\t.rules((rules) => ({
|
|
@@ -102,15 +113,19 @@ export const Update{{name}}Schema = {{schemaFactory}}<I{{name}}UpdateRequest>()
|
|
|
102
113
|
\t.create();`;
|
|
103
114
|
|
|
104
115
|
const presenterTemplate = `import { {{presenterFactory}} } from '{{presenterImport}}';
|
|
105
|
-
import { ApplicationProvider } from '
|
|
106
|
-
import { Create{{name}}Schema, Update{{name}}Schema } from '
|
|
107
|
-
import type {
|
|
116
|
+
import { ApplicationProvider } from '\${{context}}/Application';
|
|
117
|
+
import { Create{{name}}Schema, Update{{name}}Schema } from './Schema';
|
|
118
|
+
import type {
|
|
119
|
+
\tI{{name}}CollectionQuery,
|
|
120
|
+
\tI{{name}}CreateRequest,
|
|
121
|
+
\tI{{name}}UpdateRequest
|
|
122
|
+
} from '\${{context}}/Domain/Ports/{{name}}';
|
|
108
123
|
|
|
109
|
-
export const {{
|
|
124
|
+
export const {{name}}Presenter = {{presenterFactory}}('{{name}}Presenter', ({ createAsyncResource, createAsyncAction }) => {
|
|
110
125
|
\tconst { {{name}}Service } = ApplicationProvider();
|
|
111
126
|
\t
|
|
112
|
-
\tconst collection = async (
|
|
113
|
-
\t\tawait createAsyncResource({{name}}Service.collection(
|
|
127
|
+
\tconst collection = async (query?: I{{name}}CollectionQuery) =>
|
|
128
|
+
\t\tawait createAsyncResource({{name}}Service.collection(query));
|
|
114
129
|
\t
|
|
115
130
|
\tconst resource = async (id: number) =>
|
|
116
131
|
\t\tawait createAsyncResource({{name}}Service.resource(id));
|
|
@@ -150,15 +165,15 @@ export default async function generateCrud() {
|
|
|
150
165
|
// Get available datasources
|
|
151
166
|
const contextPath = getContextPath(context);
|
|
152
167
|
const coreDatasources = await getAvailableFiles(
|
|
153
|
-
path.join(process.cwd(), 'src/app/core/
|
|
168
|
+
path.join(process.cwd(), 'src/app/core/Datasource')
|
|
154
169
|
);
|
|
155
170
|
const contextDatasources = await getAvailableFiles(
|
|
156
|
-
path.join(contextPath, 'Infrastructure'
|
|
171
|
+
path.join(contextPath, 'Infrastructure/Http/Datasource')
|
|
157
172
|
);
|
|
158
173
|
|
|
159
174
|
const allDatasources = [
|
|
160
|
-
...coreDatasources.map(d => ({ title: `${d}
|
|
161
|
-
...contextDatasources.map(d => ({ title: `${d}
|
|
175
|
+
...coreDatasources.map(d => ({ title: `${d} (core)`, value: { name: d, from: 'core' } })),
|
|
176
|
+
...contextDatasources.map(d => ({ title: `${d} (${context})`, value: { name: d, from: context } }))
|
|
162
177
|
];
|
|
163
178
|
|
|
164
179
|
const { datasource } = await prompts({
|
|
@@ -170,7 +185,7 @@ export default async function generateCrud() {
|
|
|
170
185
|
|
|
171
186
|
// Get schema and presenter factories
|
|
172
187
|
const coreSchemas = await getAvailableFiles(
|
|
173
|
-
path.join(process.cwd(), 'src/app/core/
|
|
188
|
+
path.join(process.cwd(), 'src/app/core/Schema')
|
|
174
189
|
);
|
|
175
190
|
|
|
176
191
|
const { schemaType } = await prompts({
|
|
@@ -178,13 +193,13 @@ export default async function generateCrud() {
|
|
|
178
193
|
name: 'schemaType',
|
|
179
194
|
message: 'Select schema factory:',
|
|
180
195
|
choices: [
|
|
181
|
-
{ title: 'Use
|
|
196
|
+
{ title: 'Use createSchemaFactory from package', value: 'package' },
|
|
182
197
|
...coreSchemas.map(s => ({ title: `Use ${s} from core`, value: s }))
|
|
183
198
|
]
|
|
184
199
|
});
|
|
185
200
|
|
|
186
201
|
const corePresenters = await getAvailableFiles(
|
|
187
|
-
path.join(process.cwd(), 'src/app/core/
|
|
202
|
+
path.join(process.cwd(), 'src/app/core/Presenter')
|
|
188
203
|
);
|
|
189
204
|
|
|
190
205
|
const { presenterType } = await prompts({
|
|
@@ -210,23 +225,26 @@ export default async function generateCrud() {
|
|
|
210
225
|
entityTemplate.replace(/{{name}}/g, pascalName)
|
|
211
226
|
);
|
|
212
227
|
|
|
213
|
-
// 2. Create Ports
|
|
228
|
+
// 2. Create Ports
|
|
214
229
|
const portsPath = path.join(contextPath, 'Domain', 'Ports', pascalName);
|
|
215
230
|
await writeIfNotExists(
|
|
216
231
|
path.join(portsPath, 'index.ts'),
|
|
217
|
-
|
|
232
|
+
portsIndexTemplate
|
|
233
|
+
.replace(/{{name}}/g, pascalName)
|
|
234
|
+
.replace(/{{context}}/g, context)
|
|
218
235
|
);
|
|
219
236
|
|
|
220
237
|
// 3. Create Repository
|
|
221
|
-
const repoPath = path.join(contextPath, 'Infrastructure', 'Repositories');
|
|
238
|
+
const repoPath = path.join(contextPath, 'Infrastructure', 'Http', 'Repositories');
|
|
222
239
|
const datasourceImport = datasource.from === 'core'
|
|
223
|
-
? '$core/
|
|
224
|
-
:
|
|
240
|
+
? '$core/Datasource'
|
|
241
|
+
: `\$${context}/Infrastructure/Http/Datasource`;
|
|
225
242
|
|
|
226
243
|
await writeIfNotExists(
|
|
227
244
|
path.join(repoPath, `${pascalName}Repository.ts`),
|
|
228
245
|
repositoryTemplate
|
|
229
246
|
.replace(/{{name}}/g, pascalName)
|
|
247
|
+
.replace(/{{context}}/g, context)
|
|
230
248
|
.replace(/{{endpoint}}/g, endpoint)
|
|
231
249
|
.replace(/{{datasource}}/g, datasource.name)
|
|
232
250
|
.replace(/{{datasourceImport}}/g, datasourceImport)
|
|
@@ -244,80 +262,117 @@ export default async function generateCrud() {
|
|
|
244
262
|
);
|
|
245
263
|
await updateIndexTs(servicePath);
|
|
246
264
|
|
|
247
|
-
// 5. Create
|
|
248
|
-
const
|
|
249
|
-
const
|
|
265
|
+
// 5. Create Delivery module structure
|
|
266
|
+
const deliveryModulePath = path.join(contextPath, 'Delivery', pascalName);
|
|
267
|
+
const schemaPath = path.join(deliveryModulePath, 'Schema');
|
|
268
|
+
|
|
269
|
+
// Create schemas
|
|
270
|
+
const schemaFactory = schemaType === 'package' ? 'Schema' : schemaType;
|
|
250
271
|
const schemaImport = schemaType === 'package'
|
|
251
272
|
? '@azure-net/kit'
|
|
252
|
-
: '$core/
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
273
|
+
: '$core/Schema';
|
|
274
|
+
|
|
275
|
+
if (schemaType === 'package') {
|
|
276
|
+
// Add the import for createSchemaFactory and rules
|
|
277
|
+
const schemaImportFull = `import { createSchemaFactory } from '@azure-net/kit';
|
|
278
|
+
import { createRules, validationMessagesI18n } from '@azure-net/kit/schema';
|
|
279
|
+
|
|
280
|
+
const Schema = createSchemaFactory(createRules(validationMessagesI18n));`;
|
|
281
|
+
|
|
282
|
+
await writeIfNotExists(
|
|
283
|
+
path.join(schemaPath, `Create${pascalName}Schema.ts`),
|
|
284
|
+
schemaImportFull + '\n' + createSchemaTemplate
|
|
285
|
+
.replace('import { {{schemaFactory}} } from \'{{schemaImport}}\';\n', '')
|
|
286
|
+
.replace(/{{name}}/g, pascalName)
|
|
287
|
+
.replace(/{{context}}/g, context)
|
|
288
|
+
.replace(/{{schemaFactory}}/g, 'Schema')
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
await writeIfNotExists(
|
|
292
|
+
path.join(schemaPath, `Update${pascalName}Schema.ts`),
|
|
293
|
+
schemaImportFull + '\n' + updateSchemaTemplate
|
|
294
|
+
.replace('import { {{schemaFactory}} } from \'{{schemaImport}}\';\n', '')
|
|
295
|
+
.replace(/{{name}}/g, pascalName)
|
|
296
|
+
.replace(/{{context}}/g, context)
|
|
297
|
+
.replace(/{{schemaFactory}}/g, 'Schema')
|
|
298
|
+
);
|
|
299
|
+
} else {
|
|
300
|
+
await writeIfNotExists(
|
|
301
|
+
path.join(schemaPath, `Create${pascalName}Schema.ts`),
|
|
302
|
+
createSchemaTemplate
|
|
303
|
+
.replace(/{{name}}/g, pascalName)
|
|
304
|
+
.replace(/{{context}}/g, context)
|
|
305
|
+
.replace(/{{schemaFactory}}/g, schemaFactory)
|
|
306
|
+
.replace(/{{schemaImport}}/g, schemaImport)
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
await writeIfNotExists(
|
|
310
|
+
path.join(schemaPath, `Update${pascalName}Schema.ts`),
|
|
311
|
+
updateSchemaTemplate
|
|
312
|
+
.replace(/{{name}}/g, pascalName)
|
|
313
|
+
.replace(/{{context}}/g, context)
|
|
314
|
+
.replace(/{{schemaFactory}}/g, schemaFactory)
|
|
315
|
+
.replace(/{{schemaImport}}/g, schemaImport)
|
|
316
|
+
);
|
|
317
|
+
}
|
|
269
318
|
await updateIndexTs(schemaPath);
|
|
270
319
|
|
|
271
320
|
// 6. Create Presenter
|
|
272
|
-
const presenterPath = path.join(contextPath, 'Delivery', 'Presenters');
|
|
273
321
|
const presenterFactory = presenterType === 'package' ? 'createPresenter' : presenterType;
|
|
274
322
|
const presenterImport = presenterType === 'package'
|
|
275
323
|
? '@azure-net/kit'
|
|
276
|
-
: '$core/
|
|
324
|
+
: '$core/Presenter';
|
|
277
325
|
|
|
278
326
|
await writeIfNotExists(
|
|
279
|
-
path.join(
|
|
327
|
+
path.join(deliveryModulePath, `${pascalName}Presenter.ts`),
|
|
280
328
|
presenterTemplate
|
|
281
329
|
.replace(/{{name}}/g, pascalName)
|
|
282
|
-
.replace(/{{
|
|
330
|
+
.replace(/{{context}}/g, context)
|
|
283
331
|
.replace(/{{presenterFactory}}/g, presenterFactory)
|
|
284
332
|
.replace(/{{presenterImport}}/g, presenterImport)
|
|
285
333
|
);
|
|
286
|
-
|
|
334
|
+
|
|
335
|
+
// Create module index
|
|
336
|
+
await writeIfNotExists(
|
|
337
|
+
path.join(deliveryModulePath, 'index.ts'),
|
|
338
|
+
`export * from './${pascalName}Presenter';\nexport * from './Schema';`
|
|
339
|
+
);
|
|
287
340
|
|
|
288
341
|
// 7. Setup Providers
|
|
289
342
|
// Ensure DatasourceProvider
|
|
290
|
-
|
|
343
|
+
const datasourceProviderPath = path.join(contextPath, 'Infrastructure', 'Providers', 'DatasourceProvider.ts');
|
|
344
|
+
if (!(await fs.access(datasourceProviderPath).then(() => true).catch(() => false))) {
|
|
345
|
+
const datasourceProviderContent = `import { createBoundaryProvider } from '@azure-net/kit';
|
|
346
|
+
import { ${datasource.name} } from '${datasource.from === 'core' ? '$core/Datasource' : '../Http/Datasource'}';
|
|
347
|
+
|
|
348
|
+
export const DatasourceProvider = createBoundaryProvider('${contextName}DatasourceProvider', () => ({
|
|
349
|
+
\t${datasource.name}: () => new ${datasource.name}()
|
|
350
|
+
}));`;
|
|
351
|
+
await writeIfNotExists(datasourceProviderPath, datasourceProviderContent);
|
|
352
|
+
await updateIndexTs(path.dirname(datasourceProviderPath));
|
|
353
|
+
}
|
|
291
354
|
|
|
292
|
-
// Ensure InfrastructureProvider
|
|
355
|
+
// Ensure InfrastructureProvider
|
|
293
356
|
const infraProvider = await ensureProvider(context, 'Infrastructure', {});
|
|
294
357
|
await addToProvider(
|
|
295
358
|
infraProvider.path,
|
|
296
359
|
`${pascalName}Repository`,
|
|
297
|
-
'../Repositories
|
|
360
|
+
'../Http/Repositories',
|
|
361
|
+
`DatasourceProvider.${datasource.name}`
|
|
298
362
|
);
|
|
299
363
|
|
|
300
|
-
//
|
|
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
|
|
364
|
+
// Ensure ApplicationProvider
|
|
308
365
|
const appProvider = await ensureProvider(context, 'Application', { infrastructure: true });
|
|
309
366
|
await addToProvider(
|
|
310
367
|
appProvider.path,
|
|
311
368
|
`${pascalName}Service`,
|
|
312
|
-
'../Services
|
|
369
|
+
'../Services',
|
|
370
|
+
`InfrastructureProvider.${pascalName}Repository`
|
|
313
371
|
);
|
|
314
372
|
|
|
315
|
-
// Update
|
|
316
|
-
|
|
317
|
-
|
|
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');
|
|
373
|
+
// Update all layer indexes
|
|
374
|
+
await ensureIndexExports(path.join(contextPath, 'Infrastructure'));
|
|
375
|
+
await ensureIndexExports(path.join(contextPath, 'Application'));
|
|
321
376
|
|
|
322
377
|
console.log(`✅ CRUD module for ${pascalName} created successfully!`);
|
|
323
378
|
}
|
|
@@ -3,11 +3,16 @@ import { selectContext, getContextPath, toPascalCase } from '../../utils/context
|
|
|
3
3
|
import { writeIfNotExists, updateIndexTs } from '../../utils/fileUtils.js';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
|
|
6
|
-
const datasourceTemplate = `import { BaseHttpDatasource
|
|
6
|
+
const datasourceTemplate = `import { BaseHttpDatasource } from '@azure-net/kit/infra';
|
|
7
|
+
import { HttpService } from '@azure-net/kit/infra';
|
|
7
8
|
|
|
8
9
|
export class {{name}}Datasource extends BaseHttpDatasource {
|
|
9
10
|
\tconstructor() {
|
|
10
|
-
\t\tsuper({
|
|
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});
|
|
11
16
|
\t}
|
|
12
17
|
}`;
|
|
13
18
|
|
|
@@ -17,14 +22,14 @@ export default async function generateDatasource() {
|
|
|
17
22
|
const { name } = await prompts({
|
|
18
23
|
type: 'text',
|
|
19
24
|
name: 'name',
|
|
20
|
-
message: 'Datasource name (
|
|
25
|
+
message: 'Datasource name (without "Datasource" suffix):'
|
|
21
26
|
});
|
|
22
27
|
|
|
23
28
|
const pascalName = toPascalCase(name);
|
|
24
29
|
const contextPath = getContextPath(context);
|
|
25
30
|
const datasourcePath = context === 'core'
|
|
26
|
-
? path.join(contextPath, '
|
|
27
|
-
: path.join(contextPath, 'Infrastructure', '
|
|
31
|
+
? path.join(contextPath, 'Datasource')
|
|
32
|
+
: path.join(contextPath, 'Infrastructure', 'Http', 'Datasource');
|
|
28
33
|
|
|
29
34
|
const content = datasourceTemplate.replace(/{{name}}/g, pascalName);
|
|
30
35
|
const filePath = path.join(datasourcePath, `${pascalName}Datasource.ts`);
|
|
@@ -3,15 +3,15 @@ import path from 'path';
|
|
|
3
3
|
import { selectContext, getContextPath, toPascalCase, getAvailableFiles } from '../../utils/contextUtils.js';
|
|
4
4
|
import { writeIfNotExists, updateIndexTs } from '../../utils/fileUtils.js';
|
|
5
5
|
|
|
6
|
-
const presenterWithCoreTemplate = `import { {{corePresenter}} } from '
|
|
6
|
+
const presenterWithCoreTemplate = `import { {{corePresenter}} } from '\$core/Presenter';
|
|
7
7
|
{{serviceImport}}
|
|
8
8
|
|
|
9
|
-
export const {{
|
|
9
|
+
export const {{name}}Presenter = {{corePresenter}}('{{name}}Presenter', ({ createAsyncResource, createAsyncAction }) => {
|
|
10
10
|
\t{{serviceInit}}
|
|
11
11
|
\t
|
|
12
12
|
\tconst example = async () => {
|
|
13
13
|
\t\t// Implement presenter logic
|
|
14
|
-
\t\treturn {};
|
|
14
|
+
\t\treturn await createAsyncResource(Promise.resolve({}));
|
|
15
15
|
\t};
|
|
16
16
|
\t
|
|
17
17
|
\treturn { example };
|
|
@@ -20,7 +20,7 @@ export const {{contextName}}{{name}}Presenter = {{corePresenter}}('{{contextName
|
|
|
20
20
|
const presenterWithPackageTemplate = `import { createPresenter } from '@azure-net/kit';
|
|
21
21
|
{{serviceImport}}
|
|
22
22
|
|
|
23
|
-
export const {{
|
|
23
|
+
export const {{name}}Presenter = createPresenter('{{name}}Presenter', () => {
|
|
24
24
|
\t{{serviceInit}}
|
|
25
25
|
\t
|
|
26
26
|
\tconst example = async () => {
|
|
@@ -42,12 +42,12 @@ export default async function generatePresenter() {
|
|
|
42
42
|
const { name } = await prompts({
|
|
43
43
|
type: 'text',
|
|
44
44
|
name: 'name',
|
|
45
|
-
message: '
|
|
45
|
+
message: 'Module name (will create folder in Delivery):'
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
// Check for core presenters
|
|
49
49
|
const corePresenters = await getAvailableFiles(
|
|
50
|
-
path.join(process.cwd(), 'src/app/core/
|
|
50
|
+
path.join(process.cwd(), 'src/app/core/Presenter')
|
|
51
51
|
);
|
|
52
52
|
|
|
53
53
|
const presenterChoices = [
|
|
@@ -70,7 +70,7 @@ export default async function generatePresenter() {
|
|
|
70
70
|
|
|
71
71
|
const serviceChoices = [
|
|
72
72
|
{ title: 'Without service', value: null },
|
|
73
|
-
...services.map(s => ({ title: `${s}
|
|
73
|
+
...services.map(s => ({ title: `${s}`, value: s }))
|
|
74
74
|
];
|
|
75
75
|
|
|
76
76
|
const { service } = await prompts({
|
|
@@ -81,12 +81,11 @@ export default async function generatePresenter() {
|
|
|
81
81
|
});
|
|
82
82
|
|
|
83
83
|
const pascalName = toPascalCase(name);
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
const filePath = path.join(presenterPath, `${pascalName}Presenter.ts`);
|
|
84
|
+
const modulePath = path.join(contextPath, 'Delivery', pascalName);
|
|
85
|
+
const filePath = path.join(modulePath, `${pascalName}Presenter.ts`);
|
|
87
86
|
|
|
88
87
|
const serviceImport = service
|
|
89
|
-
? `import { ApplicationProvider } from '
|
|
88
|
+
? `import { ApplicationProvider } from '\$${context}/Application';`
|
|
90
89
|
: '';
|
|
91
90
|
|
|
92
91
|
const serviceInit = service
|
|
@@ -95,19 +94,20 @@ export default async function generatePresenter() {
|
|
|
95
94
|
|
|
96
95
|
const content = presenterType === 'package'
|
|
97
96
|
? presenterWithPackageTemplate
|
|
98
|
-
.replace(/{{contextName}}/g, contextName)
|
|
99
97
|
.replace(/{{name}}/g, pascalName)
|
|
100
98
|
.replace(/{{serviceImport}}/g, serviceImport)
|
|
101
99
|
.replace(/{{serviceInit}}/g, serviceInit)
|
|
102
100
|
: presenterWithCoreTemplate
|
|
103
|
-
.replace(/{{contextName}}/g, contextName)
|
|
104
101
|
.replace(/{{name}}/g, pascalName)
|
|
105
102
|
.replace(/{{corePresenter}}/g, presenterType)
|
|
106
103
|
.replace(/{{serviceImport}}/g, serviceImport)
|
|
107
104
|
.replace(/{{serviceInit}}/g, serviceInit);
|
|
108
105
|
|
|
109
106
|
await writeIfNotExists(filePath, content);
|
|
110
|
-
await
|
|
107
|
+
await writeIfNotExists(
|
|
108
|
+
path.join(modulePath, 'index.ts'),
|
|
109
|
+
`export * from './${pascalName}Presenter';`
|
|
110
|
+
);
|
|
111
111
|
|
|
112
112
|
console.log(`✅ Presenter created at ${filePath}`);
|
|
113
113
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import prompts from 'prompts';
|
|
2
2
|
import fs from 'fs/promises';
|
|
3
3
|
import path from 'path';
|
|
4
|
-
import { selectContext, getContextPath, toPascalCase, getAvailableFiles } from '../../utils/contextUtils.js';
|
|
4
|
+
import { selectContext, getContextPath, toPascalCase, getAvailableFiles, toCamelCase } from '../../utils/contextUtils.js';
|
|
5
5
|
import { writeIfNotExists, updateIndexTs } from '../../utils/fileUtils.js';
|
|
6
6
|
import { ensureProvider, addToProvider } from '../../utils/providerUtils.js';
|
|
7
7
|
|
|
@@ -34,15 +34,15 @@ export default async function generateRepository() {
|
|
|
34
34
|
// Get available datasources
|
|
35
35
|
const contextPath = getContextPath(context);
|
|
36
36
|
const coreDatasources = await getAvailableFiles(
|
|
37
|
-
path.join(process.cwd(), 'src/app/core/
|
|
37
|
+
path.join(process.cwd(), 'src/app/core/Datasource')
|
|
38
38
|
);
|
|
39
39
|
const contextDatasources = await getAvailableFiles(
|
|
40
|
-
path.join(contextPath, 'Infrastructure', '
|
|
40
|
+
path.join(contextPath, 'Infrastructure', 'Http', 'Datasource')
|
|
41
41
|
);
|
|
42
42
|
|
|
43
43
|
const allDatasources = [
|
|
44
|
-
...coreDatasources.map(d => ({ title: `${d}
|
|
45
|
-
...contextDatasources.map(d => ({ title: `${d}
|
|
44
|
+
...coreDatasources.map(d => ({ title: `${d} (core)`, value: { name: d, from: 'core' } })),
|
|
45
|
+
...contextDatasources.map(d => ({ title: `${d} (${context})`, value: { name: d, from: context } }))
|
|
46
46
|
];
|
|
47
47
|
|
|
48
48
|
if (allDatasources.length === 0) {
|
|
@@ -58,40 +58,45 @@ export default async function generateRepository() {
|
|
|
58
58
|
});
|
|
59
59
|
|
|
60
60
|
const pascalName = toPascalCase(name);
|
|
61
|
-
const
|
|
61
|
+
const contextName = toPascalCase(context);
|
|
62
|
+
const repoPath = path.join(contextPath, 'Infrastructure', 'Http', 'Repositories');
|
|
62
63
|
const filePath = path.join(repoPath, `${pascalName}Repository.ts`);
|
|
63
64
|
|
|
64
65
|
// Generate repository
|
|
65
66
|
const datasourceImport = datasource.from === 'core'
|
|
66
|
-
? '$core/
|
|
67
|
-
: '../
|
|
67
|
+
? '$core/Datasource'
|
|
68
|
+
: '../Datasource';
|
|
68
69
|
|
|
69
70
|
const content = repositoryTemplate
|
|
70
71
|
.replace(/{{name}}/g, pascalName)
|
|
71
72
|
.replace(/{{datasource}}/g, datasource.name)
|
|
72
73
|
.replace(/{{datasourceImport}}/g, datasourceImport)
|
|
73
|
-
.replace(/{{datasourceVar}}/g,
|
|
74
|
+
.replace(/{{datasourceVar}}/g, toCamelCase(datasource.name));
|
|
74
75
|
|
|
75
76
|
await writeIfNotExists(filePath, content);
|
|
76
77
|
await updateIndexTs(repoPath);
|
|
77
78
|
|
|
78
79
|
// Ensure DatasourceProvider exists
|
|
79
|
-
const
|
|
80
|
+
const datasourceProviderPath = path.join(contextPath, 'Infrastructure', 'Providers', 'DatasourceProvider.ts');
|
|
81
|
+
if (!(await fs.access(datasourceProviderPath).then(() => true).catch(() => false))) {
|
|
82
|
+
const datasourceProviderContent = `import { createBoundaryProvider } from '@azure-net/kit';
|
|
83
|
+
import { ${datasource.name} } from '${datasource.from === 'core' ? '$core/Datasource' : '../Http/Datasource'}';
|
|
84
|
+
|
|
85
|
+
export const DatasourceProvider = createBoundaryProvider('${contextName}DatasourceProvider', () => ({
|
|
86
|
+
\t${datasource.name}: () => new ${datasource.name}()
|
|
87
|
+
}));`;
|
|
88
|
+
await writeIfNotExists(datasourceProviderPath, datasourceProviderContent);
|
|
89
|
+
await updateIndexTs(path.dirname(datasourceProviderPath));
|
|
90
|
+
}
|
|
80
91
|
|
|
81
|
-
// Ensure InfrastructureProvider
|
|
82
|
-
const infraProvider = await ensureProvider(context, 'Infrastructure', {
|
|
92
|
+
// Ensure InfrastructureProvider and add repository
|
|
93
|
+
const infraProvider = await ensureProvider(context, 'Infrastructure', { hasDatasource: true });
|
|
83
94
|
await addToProvider(
|
|
84
95
|
infraProvider.path,
|
|
85
96
|
`${pascalName}Repository`,
|
|
86
|
-
'../Repositories
|
|
97
|
+
'../Http/Repositories',
|
|
98
|
+
`DatasourceProvider.${datasource.name}`
|
|
87
99
|
);
|
|
88
100
|
|
|
89
|
-
// Add datasource dependency to repository in provider
|
|
90
|
-
let providerContent = await fs.readFile(infraProvider.path, 'utf-8');
|
|
91
|
-
const repoFactory = `${pascalName}Repository: () => new ${pascalName}Repository()`;
|
|
92
|
-
const repoWithDatasource = `${pascalName}Repository: () => new ${pascalName}Repository(DatasourceProvider.${datasource.name})`;
|
|
93
|
-
providerContent = providerContent.replace(repoFactory, repoWithDatasource);
|
|
94
|
-
await fs.writeFile(infraProvider.path, providerContent, 'utf-8');
|
|
95
|
-
|
|
96
101
|
console.log(`✅ Repository created at ${filePath}`);
|
|
97
102
|
}
|