@azure-net/cli 2.0.0 → 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 +7 -1
- package/package.json +1 -1
- package/src/commands/generate/generateCrud.js +103 -76
- 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 +34 -9
- 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,83 @@ 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
|
-
|
|
14
|
-
|
|
15
|
-
export type I{{name}}Collection = I{{name}}[];`;
|
|
12
|
+
\tcreated_at: string;
|
|
13
|
+
\tupdated_at: string;
|
|
14
|
+
}`;
|
|
16
15
|
|
|
17
|
-
const
|
|
16
|
+
const portsIndexTemplate = `export interface I{{name}}Collection {
|
|
17
|
+
\tdata: I{{name}}[];
|
|
18
|
+
\ttotal: number;
|
|
19
|
+
}
|
|
18
20
|
|
|
19
|
-
export type I{{name}}CreateRequest = Omit<I{{name}}, 'id'>;
|
|
20
|
-
export type I{{name}}UpdateRequest = Partial<I{{name}}CreateRequest>;
|
|
21
21
|
export interface I{{name}}CollectionQuery {
|
|
22
|
+
\tpage?: number;
|
|
23
|
+
\tperPage?: number;
|
|
22
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
|
|
23
33
|
}`;
|
|
24
34
|
|
|
25
35
|
const repositoryTemplate = `import { {{datasource}} } from '{{datasourceImport}}';
|
|
26
|
-
import type {
|
|
36
|
+
import type { I{{name}} } from '${{context}}/Domain/Entities/{{name}}';
|
|
37
|
+
import type {
|
|
27
38
|
\tI{{name}}Collection,
|
|
28
|
-
\tI{{name}},
|
|
39
|
+
\tI{{name}}CollectionQuery,
|
|
29
40
|
\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';
|
|
41
|
+
\tI{{name}}UpdateRequest
|
|
42
|
+
} from '${{context}}/Domain/Ports/{{name}}';
|
|
34
43
|
|
|
35
44
|
export class {{name}}Repository {
|
|
36
45
|
\tprivate endpoint = '{{endpoint}}';
|
|
37
46
|
\t
|
|
38
47
|
\tconstructor(private {{datasourceVar}}: {{datasource}}) {}
|
|
39
48
|
\t
|
|
40
|
-
\tpublic async collection(
|
|
41
|
-
\t\treturn
|
|
42
|
-
\t\t\thttp.get(\`\${this.endpoint}\${
|
|
43
|
-
\t\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());
|
|
44
53
|
\t}
|
|
45
54
|
\t
|
|
46
55
|
\tpublic async resource(id: number) {
|
|
47
|
-
\t\treturn
|
|
56
|
+
\t\treturn this.{{datasourceVar}}.createRequest<I{{name}}>(({ http }) =>
|
|
48
57
|
\t\t\thttp.get(\`\${this.endpoint}/\${id}\`)
|
|
49
|
-
\t\t);
|
|
58
|
+
\t\t).then(res => res.getData());
|
|
50
59
|
\t}
|
|
51
60
|
\t
|
|
52
|
-
\tpublic async create(data: CreateRequest) {
|
|
53
|
-
\t\treturn
|
|
61
|
+
\tpublic async create(data: I{{name}}CreateRequest) {
|
|
62
|
+
\t\treturn this.{{datasourceVar}}.createRequest<I{{name}}>(({ http }) =>
|
|
54
63
|
\t\t\thttp.post(this.endpoint, { json: data })
|
|
55
|
-
\t\t);
|
|
64
|
+
\t\t).then(res => res.getData());
|
|
56
65
|
\t}
|
|
57
66
|
\t
|
|
58
|
-
\tpublic async update(id: number, data: UpdateRequest) {
|
|
59
|
-
\t\treturn
|
|
67
|
+
\tpublic async update(id: number, data: I{{name}}UpdateRequest) {
|
|
68
|
+
\t\treturn this.{{datasourceVar}}.createRequest<I{{name}}>(({ http }) =>
|
|
60
69
|
\t\t\thttp.put(\`\${this.endpoint}/\${id}\`, { json: data })
|
|
61
|
-
\t\t);
|
|
70
|
+
\t\t).then(res => res.getData());
|
|
62
71
|
\t}
|
|
63
72
|
\t
|
|
64
73
|
\tpublic async remove(id: number) {
|
|
65
|
-
\t\treturn
|
|
74
|
+
\t\treturn this.{{datasourceVar}}.createRequest<void>(({ http }) =>
|
|
66
75
|
\t\t\thttp.delete(\`\${this.endpoint}/\${id}\`)
|
|
67
|
-
\t\t);
|
|
76
|
+
\t\t).then(() => undefined);
|
|
68
77
|
\t}
|
|
69
78
|
}`;
|
|
70
79
|
|
|
71
80
|
const serviceTemplate = `import { ClassMirror } from '@azure-net/kit';
|
|
72
|
-
import { {{name}}Repository } from '../../Infrastructure/Repositories
|
|
81
|
+
import { {{name}}Repository } from '../../Infrastructure/Http/Repositories';
|
|
73
82
|
|
|
74
83
|
export class {{name}}Service extends ClassMirror<{{name}}Repository> {
|
|
75
84
|
\tconstructor(private {{camelName}}Repository: {{name}}Repository) {
|
|
@@ -84,7 +93,7 @@ export class {{name}}Service extends ClassMirror<{{name}}Repository> {
|
|
|
84
93
|
}`;
|
|
85
94
|
|
|
86
95
|
const createSchemaTemplate = `import { {{schemaFactory}} } from '{{schemaImport}}';
|
|
87
|
-
import type { I{{name}}CreateRequest } from '
|
|
96
|
+
import type { I{{name}}CreateRequest } from '${{context}}/Domain/Ports/{{name}}';
|
|
88
97
|
|
|
89
98
|
export const Create{{name}}Schema = {{schemaFactory}}<I{{name}}CreateRequest>()
|
|
90
99
|
\t.rules((rules) => ({
|
|
@@ -93,7 +102,7 @@ export const Create{{name}}Schema = {{schemaFactory}}<I{{name}}CreateRequest>()
|
|
|
93
102
|
\t.create();`;
|
|
94
103
|
|
|
95
104
|
const updateSchemaTemplate = `import { {{schemaFactory}} } from '{{schemaImport}}';
|
|
96
|
-
import type { I{{name}}UpdateRequest } from '
|
|
105
|
+
import type { I{{name}}UpdateRequest } from '${{context}}/Domain/Ports/{{name}}';
|
|
97
106
|
|
|
98
107
|
export const Update{{name}}Schema = {{schemaFactory}}<I{{name}}UpdateRequest>()
|
|
99
108
|
\t.rules((rules) => ({
|
|
@@ -102,15 +111,19 @@ export const Update{{name}}Schema = {{schemaFactory}}<I{{name}}UpdateRequest>()
|
|
|
102
111
|
\t.create();`;
|
|
103
112
|
|
|
104
113
|
const presenterTemplate = `import { {{presenterFactory}} } from '{{presenterImport}}';
|
|
105
|
-
import { ApplicationProvider } from '
|
|
106
|
-
import { Create{{name}}Schema, Update{{name}}Schema } from '
|
|
107
|
-
import type {
|
|
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}}';
|
|
108
121
|
|
|
109
|
-
export const {{
|
|
122
|
+
export const {{name}}Presenter = {{presenterFactory}}('{{name}}Presenter', ({ createAsyncResource, createAsyncAction }) => {
|
|
110
123
|
\tconst { {{name}}Service } = ApplicationProvider();
|
|
111
124
|
\t
|
|
112
|
-
\tconst collection = async (
|
|
113
|
-
\t\tawait createAsyncResource({{name}}Service.collection(
|
|
125
|
+
\tconst collection = async (query?: I{{name}}CollectionQuery) =>
|
|
126
|
+
\t\tawait createAsyncResource({{name}}Service.collection(query));
|
|
114
127
|
\t
|
|
115
128
|
\tconst resource = async (id: number) =>
|
|
116
129
|
\t\tawait createAsyncResource({{name}}Service.resource(id));
|
|
@@ -150,15 +163,15 @@ export default async function generateCrud() {
|
|
|
150
163
|
// Get available datasources
|
|
151
164
|
const contextPath = getContextPath(context);
|
|
152
165
|
const coreDatasources = await getAvailableFiles(
|
|
153
|
-
path.join(process.cwd(), 'src/app/core/
|
|
166
|
+
path.join(process.cwd(), 'src/app/core/Datasource')
|
|
154
167
|
);
|
|
155
168
|
const contextDatasources = await getAvailableFiles(
|
|
156
|
-
path.join(contextPath, 'Infrastructure'
|
|
169
|
+
path.join(contextPath, 'Infrastructure/Http/Datasource')
|
|
157
170
|
);
|
|
158
171
|
|
|
159
172
|
const allDatasources = [
|
|
160
|
-
...coreDatasources.map(d => ({ title: `${d}
|
|
161
|
-
...contextDatasources.map(d => ({ title: `${d}
|
|
173
|
+
...coreDatasources.map(d => ({ title: `${d} (core)`, value: { name: d, from: 'core' } })),
|
|
174
|
+
...contextDatasources.map(d => ({ title: `${d} (${context})`, value: { name: d, from: context } }))
|
|
162
175
|
];
|
|
163
176
|
|
|
164
177
|
const { datasource } = await prompts({
|
|
@@ -170,7 +183,7 @@ export default async function generateCrud() {
|
|
|
170
183
|
|
|
171
184
|
// Get schema and presenter factories
|
|
172
185
|
const coreSchemas = await getAvailableFiles(
|
|
173
|
-
path.join(process.cwd(), 'src/app/core/
|
|
186
|
+
path.join(process.cwd(), 'src/app/core/Schema')
|
|
174
187
|
);
|
|
175
188
|
|
|
176
189
|
const { schemaType } = await prompts({
|
|
@@ -178,13 +191,13 @@ export default async function generateCrud() {
|
|
|
178
191
|
name: 'schemaType',
|
|
179
192
|
message: 'Select schema factory:',
|
|
180
193
|
choices: [
|
|
181
|
-
{ title: 'Use
|
|
194
|
+
{ title: 'Use createSchemaFactory from package', value: 'package' },
|
|
182
195
|
...coreSchemas.map(s => ({ title: `Use ${s} from core`, value: s }))
|
|
183
196
|
]
|
|
184
197
|
});
|
|
185
198
|
|
|
186
199
|
const corePresenters = await getAvailableFiles(
|
|
187
|
-
path.join(process.cwd(), 'src/app/core/
|
|
200
|
+
path.join(process.cwd(), 'src/app/core/Presenter')
|
|
188
201
|
);
|
|
189
202
|
|
|
190
203
|
const { presenterType } = await prompts({
|
|
@@ -210,23 +223,26 @@ export default async function generateCrud() {
|
|
|
210
223
|
entityTemplate.replace(/{{name}}/g, pascalName)
|
|
211
224
|
);
|
|
212
225
|
|
|
213
|
-
// 2. Create Ports
|
|
226
|
+
// 2. Create Ports
|
|
214
227
|
const portsPath = path.join(contextPath, 'Domain', 'Ports', pascalName);
|
|
215
228
|
await writeIfNotExists(
|
|
216
229
|
path.join(portsPath, 'index.ts'),
|
|
217
|
-
|
|
230
|
+
portsIndexTemplate
|
|
231
|
+
.replace(/{{name}}/g, pascalName)
|
|
232
|
+
.replace(/{{context}}/g, context)
|
|
218
233
|
);
|
|
219
234
|
|
|
220
235
|
// 3. Create Repository
|
|
221
|
-
const repoPath = path.join(contextPath, 'Infrastructure', 'Repositories');
|
|
236
|
+
const repoPath = path.join(contextPath, 'Infrastructure', 'Http', 'Repositories');
|
|
222
237
|
const datasourceImport = datasource.from === 'core'
|
|
223
|
-
? '$core/
|
|
224
|
-
:
|
|
238
|
+
? '$core/Datasource'
|
|
239
|
+
: `$${context}/Infrastructure/Http/Datasource`;
|
|
225
240
|
|
|
226
241
|
await writeIfNotExists(
|
|
227
242
|
path.join(repoPath, `${pascalName}Repository.ts`),
|
|
228
243
|
repositoryTemplate
|
|
229
244
|
.replace(/{{name}}/g, pascalName)
|
|
245
|
+
.replace(/{{context}}/g, context)
|
|
230
246
|
.replace(/{{endpoint}}/g, endpoint)
|
|
231
247
|
.replace(/{{datasource}}/g, datasource.name)
|
|
232
248
|
.replace(/{{datasourceImport}}/g, datasourceImport)
|
|
@@ -244,17 +260,21 @@ export default async function generateCrud() {
|
|
|
244
260
|
);
|
|
245
261
|
await updateIndexTs(servicePath);
|
|
246
262
|
|
|
247
|
-
// 5. Create
|
|
248
|
-
const
|
|
249
|
-
const
|
|
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;
|
|
250
269
|
const schemaImport = schemaType === 'package'
|
|
251
270
|
? '@azure-net/kit'
|
|
252
|
-
: '$core/
|
|
271
|
+
: '$core/Schema';
|
|
253
272
|
|
|
254
273
|
await writeIfNotExists(
|
|
255
274
|
path.join(schemaPath, `Create${pascalName}Schema.ts`),
|
|
256
275
|
createSchemaTemplate
|
|
257
276
|
.replace(/{{name}}/g, pascalName)
|
|
277
|
+
.replace(/{{context}}/g, context)
|
|
258
278
|
.replace(/{{schemaFactory}}/g, schemaFactory)
|
|
259
279
|
.replace(/{{schemaImport}}/g, schemaImport)
|
|
260
280
|
);
|
|
@@ -263,61 +283,68 @@ export default async function generateCrud() {
|
|
|
263
283
|
path.join(schemaPath, `Update${pascalName}Schema.ts`),
|
|
264
284
|
updateSchemaTemplate
|
|
265
285
|
.replace(/{{name}}/g, pascalName)
|
|
286
|
+
.replace(/{{context}}/g, context)
|
|
266
287
|
.replace(/{{schemaFactory}}/g, schemaFactory)
|
|
267
288
|
.replace(/{{schemaImport}}/g, schemaImport)
|
|
268
289
|
);
|
|
269
290
|
await updateIndexTs(schemaPath);
|
|
270
291
|
|
|
271
292
|
// 6. Create Presenter
|
|
272
|
-
const presenterPath = path.join(contextPath, 'Delivery', 'Presenters');
|
|
273
293
|
const presenterFactory = presenterType === 'package' ? 'createPresenter' : presenterType;
|
|
274
294
|
const presenterImport = presenterType === 'package'
|
|
275
295
|
? '@azure-net/kit'
|
|
276
|
-
: '$core/
|
|
296
|
+
: '$core/Presenter';
|
|
277
297
|
|
|
278
298
|
await writeIfNotExists(
|
|
279
|
-
path.join(
|
|
299
|
+
path.join(deliveryModulePath, `${pascalName}Presenter.ts`),
|
|
280
300
|
presenterTemplate
|
|
281
301
|
.replace(/{{name}}/g, pascalName)
|
|
282
|
-
.replace(/{{
|
|
302
|
+
.replace(/{{context}}/g, context)
|
|
283
303
|
.replace(/{{presenterFactory}}/g, presenterFactory)
|
|
284
304
|
.replace(/{{presenterImport}}/g, presenterImport)
|
|
285
305
|
);
|
|
286
|
-
|
|
306
|
+
|
|
307
|
+
// Create module index
|
|
308
|
+
await writeIfNotExists(
|
|
309
|
+
path.join(deliveryModulePath, 'index.ts'),
|
|
310
|
+
`export * from './${pascalName}Presenter';\nexport * from './Schema';`
|
|
311
|
+
);
|
|
287
312
|
|
|
288
313
|
// 7. Setup Providers
|
|
289
314
|
// Ensure DatasourceProvider
|
|
290
|
-
|
|
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
|
+
}
|
|
291
326
|
|
|
292
|
-
// Ensure InfrastructureProvider
|
|
327
|
+
// Ensure InfrastructureProvider
|
|
293
328
|
const infraProvider = await ensureProvider(context, 'Infrastructure', {});
|
|
294
329
|
await addToProvider(
|
|
295
330
|
infraProvider.path,
|
|
296
331
|
`${pascalName}Repository`,
|
|
297
|
-
'../Repositories
|
|
332
|
+
'../Http/Repositories',
|
|
333
|
+
`DatasourceProvider.${datasource.name}`
|
|
298
334
|
);
|
|
299
335
|
|
|
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
|
|
336
|
+
// Ensure ApplicationProvider
|
|
308
337
|
const appProvider = await ensureProvider(context, 'Application', { infrastructure: true });
|
|
309
338
|
await addToProvider(
|
|
310
339
|
appProvider.path,
|
|
311
340
|
`${pascalName}Service`,
|
|
312
|
-
'../Services
|
|
341
|
+
'../Services',
|
|
342
|
+
`InfrastructureProvider.${pascalName}Repository`
|
|
313
343
|
);
|
|
314
344
|
|
|
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');
|
|
345
|
+
// Update all layer indexes
|
|
346
|
+
await ensureIndexExports(path.join(contextPath, 'Infrastructure'));
|
|
347
|
+
await ensureIndexExports(path.join(contextPath, 'Application'));
|
|
321
348
|
|
|
322
349
|
console.log(`✅ CRUD module for ${pascalName} created successfully!`);
|
|
323
350
|
}
|
|
@@ -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 '$core/
|
|
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
|
}
|
|
@@ -3,8 +3,8 @@ 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 schemaWithCoreTemplate = `import { {{coreSchema}} } from '$core/
|
|
7
|
-
import type { I{{name}}Request } from '
|
|
6
|
+
const schemaWithCoreTemplate = `import { {{coreSchema}} } from '$core/Schema';
|
|
7
|
+
import type { I{{name}}Request } from '${{context}}/Domain/Ports/{{entity}}';
|
|
8
8
|
|
|
9
9
|
export const {{name}}Schema = {{coreSchema}}<I{{name}}Request>()
|
|
10
10
|
\t.rules((rules) => ({
|
|
@@ -12,10 +12,13 @@ export const {{name}}Schema = {{coreSchema}}<I{{name}}Request>()
|
|
|
12
12
|
\t}))
|
|
13
13
|
\t.create();`;
|
|
14
14
|
|
|
15
|
-
const schemaWithPackageTemplate = `import {
|
|
16
|
-
import
|
|
15
|
+
const schemaWithPackageTemplate = `import { createSchemaFactory } from '@azure-net/kit';
|
|
16
|
+
import { createRules, validationMessagesI18n } from '@azure-net/kit/schema';
|
|
17
|
+
import type { I{{name}}Request } from '${{context}}/Domain/Ports/{{entity}}';
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
const Schema = createSchemaFactory(createRules(validationMessagesI18n));
|
|
20
|
+
|
|
21
|
+
export const {{name}}Schema = Schema<I{{name}}Request>()
|
|
19
22
|
\t.rules((rules) => ({
|
|
20
23
|
\t\t// Add validation rules here
|
|
21
24
|
\t}))
|
|
@@ -29,6 +32,12 @@ export default async function generateSchema() {
|
|
|
29
32
|
return;
|
|
30
33
|
}
|
|
31
34
|
|
|
35
|
+
const { module } = await prompts({
|
|
36
|
+
type: 'text',
|
|
37
|
+
name: 'module',
|
|
38
|
+
message: 'Module name (folder in Delivery):'
|
|
39
|
+
});
|
|
40
|
+
|
|
32
41
|
const { name } = await prompts({
|
|
33
42
|
type: 'text',
|
|
34
43
|
name: 'name',
|
|
@@ -37,11 +46,11 @@ export default async function generateSchema() {
|
|
|
37
46
|
|
|
38
47
|
// Check for core schemas
|
|
39
48
|
const coreSchemas = await getAvailableFiles(
|
|
40
|
-
path.join(process.cwd(), 'src/app/core/
|
|
49
|
+
path.join(process.cwd(), 'src/app/core/Schema')
|
|
41
50
|
);
|
|
42
51
|
|
|
43
52
|
const choices = [
|
|
44
|
-
{ title: 'Use
|
|
53
|
+
{ title: 'Use createSchemaFactory from package', value: 'package' },
|
|
45
54
|
...coreSchemas.map(s => ({ title: `Use ${s} from core`, value: s }))
|
|
46
55
|
];
|
|
47
56
|
|
|
@@ -53,18 +62,34 @@ export default async function generateSchema() {
|
|
|
53
62
|
});
|
|
54
63
|
|
|
55
64
|
const pascalName = toPascalCase(name);
|
|
65
|
+
const moduleName = toPascalCase(module);
|
|
56
66
|
const contextPath = getContextPath(context);
|
|
57
|
-
const schemaPath = path.join(contextPath, 'Delivery', '
|
|
67
|
+
const schemaPath = path.join(contextPath, 'Delivery', moduleName, 'Schema');
|
|
58
68
|
const filePath = path.join(schemaPath, `${pascalName}Schema.ts`);
|
|
59
69
|
|
|
60
70
|
const content = schemaType === 'package'
|
|
61
|
-
? schemaWithPackageTemplate
|
|
71
|
+
? schemaWithPackageTemplate
|
|
72
|
+
.replace(/{{name}}/g, pascalName)
|
|
73
|
+
.replace(/{{context}}/g, context)
|
|
74
|
+
.replace(/{{entity}}/g, moduleName)
|
|
62
75
|
: schemaWithCoreTemplate
|
|
63
76
|
.replace(/{{name}}/g, pascalName)
|
|
77
|
+
.replace(/{{context}}/g, context)
|
|
78
|
+
.replace(/{{entity}}/g, moduleName)
|
|
64
79
|
.replace(/{{coreSchema}}/g, schemaType);
|
|
65
80
|
|
|
66
81
|
await writeIfNotExists(filePath, content);
|
|
67
82
|
await updateIndexTs(schemaPath);
|
|
68
83
|
|
|
84
|
+
// Update module index
|
|
85
|
+
const moduleIndexPath = path.join(contextPath, 'Delivery', moduleName, 'index.ts');
|
|
86
|
+
if (await fs.access(moduleIndexPath).then(() => true).catch(() => false)) {
|
|
87
|
+
let indexContent = await fs.readFile(moduleIndexPath, 'utf-8');
|
|
88
|
+
if (!indexContent.includes(`export * from './Schema'`)) {
|
|
89
|
+
indexContent += `\nexport * from './Schema';`;
|
|
90
|
+
await fs.writeFile(moduleIndexPath, indexContent, 'utf-8');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
69
94
|
console.log(`✅ Schema created at ${filePath}`);
|
|
70
95
|
}
|
|
@@ -3,4 +3,5 @@ export { default as initAliases } from './initAliases.js';
|
|
|
3
3
|
export { default as initMiddleware } from './initMiddleware.js';
|
|
4
4
|
export { default as initEdges } from './initEdges.js';
|
|
5
5
|
export { default as initTranslations } from './initTranslations.js';
|
|
6
|
-
export { default as initSchema } from './initSchema.js';
|
|
6
|
+
export { default as initSchema } from './initSchema.js';
|
|
7
|
+
export { default as initLint } from './initLint.js';
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import prompts from 'prompts';
|
|
5
|
+
|
|
6
|
+
const dependencies = [
|
|
7
|
+
'@commitlint/cli',
|
|
8
|
+
'semantic-release',
|
|
9
|
+
'@semantic-release/git',
|
|
10
|
+
'git-cz',
|
|
11
|
+
'husky',
|
|
12
|
+
'commitizen',
|
|
13
|
+
'env-cmd'
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
const scriptsToAdd = {
|
|
17
|
+
precommit: 'pnpm lint && pnpm check',
|
|
18
|
+
'semantic-release': 'env-cmd semantic-release',
|
|
19
|
+
commit: 'pnpm format && git add . && git-cz && git push',
|
|
20
|
+
release: 'pnpm commit && pnpm semantic-release'
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const commitlintConfig = `export default {
|
|
24
|
+
rules: {
|
|
25
|
+
'body-leading-blank': [2, 'always'],
|
|
26
|
+
'footer-leading-blank': [2, 'always'],
|
|
27
|
+
'header-max-length': [2, 'always', 72],
|
|
28
|
+
'subject-empty': [2, 'never'],
|
|
29
|
+
'subject-full-stop': [2, 'never', '.'],
|
|
30
|
+
'type-case': [2, 'always', 'lower-case'],
|
|
31
|
+
'type-empty': [2, 'never'],
|
|
32
|
+
'type-enum': [2, 'always', ['feat', 'fix', 'refactor', 'perf', 'chore', 'docs', 'style', 'test', 'ci']]
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
const releaseConfig = `export default {
|
|
38
|
+
branches: ['main', 'dev'],
|
|
39
|
+
dryRun: false,
|
|
40
|
+
ci: false,
|
|
41
|
+
plugins: [
|
|
42
|
+
[
|
|
43
|
+
'@semantic-release/commit-analyzer',
|
|
44
|
+
{
|
|
45
|
+
releaseRules: [
|
|
46
|
+
{ type: 'release', release: 'major' },
|
|
47
|
+
{ type: 'feature', release: 'minor' },
|
|
48
|
+
{ type: 'perf', release: false },
|
|
49
|
+
{ type: 'refactor', release: false },
|
|
50
|
+
{ type: 'fix', release: 'patch' },
|
|
51
|
+
{ type: 'chore', release: false },
|
|
52
|
+
{ type: 'ci', release: false },
|
|
53
|
+
{ type: 'docs', scope: 'README', release: false },
|
|
54
|
+
{ type: 'test', release: false },
|
|
55
|
+
{ type: 'style', release: false }
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
'@semantic-release/release-notes-generator'
|
|
60
|
+
]
|
|
61
|
+
};
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
const prettierConfig = `{
|
|
65
|
+
"useTabs": true,
|
|
66
|
+
"singleQuote": true,
|
|
67
|
+
"trailingComma": "none",
|
|
68
|
+
"tabWidth": 2,
|
|
69
|
+
"semi": true,
|
|
70
|
+
"printWidth": 150,
|
|
71
|
+
"plugins": ["prettier-plugin-svelte"],
|
|
72
|
+
"overrides": [
|
|
73
|
+
{
|
|
74
|
+
"files": "*.svelte",
|
|
75
|
+
"options": {
|
|
76
|
+
"parser": "svelte"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
`;
|
|
82
|
+
|
|
83
|
+
export default async function initLint() {
|
|
84
|
+
const cwd = process.cwd();
|
|
85
|
+
const pkgPath = path.join(cwd, 'package.json');
|
|
86
|
+
|
|
87
|
+
if (!fs.existsSync(pkgPath)) {
|
|
88
|
+
console.error('❌ package.json not found in this directory');
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const { manager } = await prompts({
|
|
93
|
+
type: 'select',
|
|
94
|
+
name: 'manager',
|
|
95
|
+
message: 'Choose your package manager:',
|
|
96
|
+
choices: [
|
|
97
|
+
{ title: 'pnpm', value: 'pnpm' },
|
|
98
|
+
{ title: 'npm', value: 'npm' },
|
|
99
|
+
{ title: 'yarn', value: 'yarn' }
|
|
100
|
+
],
|
|
101
|
+
initial: 0
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const installCommand =
|
|
105
|
+
manager === 'pnpm'
|
|
106
|
+
? `pnpm add -D ${dependencies.join(' ')}`
|
|
107
|
+
: manager === 'npm'
|
|
108
|
+
? `npm install -D ${dependencies.join(' ')}`
|
|
109
|
+
: `yarn add -D ${dependencies.join(' ')}`;
|
|
110
|
+
|
|
111
|
+
console.log(`📦 Installing packages with ${manager}...`);
|
|
112
|
+
execSync(installCommand, { stdio: 'inherit' });
|
|
113
|
+
|
|
114
|
+
console.log('📁 Updating package.json...');
|
|
115
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
116
|
+
|
|
117
|
+
pkg.scripts = {
|
|
118
|
+
...pkg.scripts,
|
|
119
|
+
...scriptsToAdd
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
pkg.config = pkg.config || {};
|
|
123
|
+
pkg.config.commitizen = {
|
|
124
|
+
path: 'git-cz'
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 4));
|
|
128
|
+
|
|
129
|
+
// Write config files
|
|
130
|
+
fs.writeFileSync(path.join(cwd, 'commitlint.config.ts'), commitlintConfig);
|
|
131
|
+
fs.writeFileSync(path.join(cwd, 'release.config.ts'), releaseConfig);
|
|
132
|
+
fs.writeFileSync(path.join(cwd, '.prettierrc'), prettierConfig);
|
|
133
|
+
|
|
134
|
+
console.log('🔧 Initializing Husky...');
|
|
135
|
+
execSync(`${manager === 'npm' ? 'npx' : manager} husky`, { stdio: 'inherit' });
|
|
136
|
+
execSync('npm run format', { stdio: 'inherit' });
|
|
137
|
+
console.log('✅ Linting and release setup completed! Change your prepare script in package.json: node -e \"if (process.env.NODE_ENV !== \'production\'){process.exit(1)} \" || (husky && svelte-kit sync)');
|
|
138
|
+
}
|
|
@@ -5,37 +5,22 @@ const middlewarePath = path.join(process.cwd(), 'src/app/core/Middleware/Middlew
|
|
|
5
5
|
const hooksServerPath = path.join(process.cwd(), 'src/hooks.server.ts');
|
|
6
6
|
const layoutPath = path.join(process.cwd(), 'src/routes/+layout.svelte');
|
|
7
7
|
|
|
8
|
-
const middlewareTemplate = `import { createMiddlewareManager
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
\tconst user = isServer ? event?.locals?.user : page?.data?.user;
|
|
12
|
-
\tif (isServer && !user && cookies.get('token') && event) {
|
|
13
|
-
\t\t// Add auth logic here
|
|
14
|
-
\t}
|
|
15
|
-
\tnext();
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const GuardMiddleware: IMiddleware = async ({ next, page, event, isServer, to }) => {
|
|
19
|
-
\tconst user = isServer ? event?.locals?.user : page?.data?.user;
|
|
20
|
-
\tif (!user && !to.pathname.startsWith('/login')) {
|
|
21
|
-
\t\tnext('/login');
|
|
22
|
-
\t}
|
|
23
|
-
\tif (user && to.pathname.startsWith('/login')) {
|
|
24
|
-
\t\tnext('/');
|
|
25
|
-
\t}
|
|
26
|
-
\tnext();
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export const { clientMiddleware, serverMiddleware } = createMiddlewareManager([AuthMiddleware, GuardMiddleware]);`;
|
|
8
|
+
const middlewareTemplate = `import { createMiddlewareManager } from '@azure-net/kit';
|
|
9
|
+
|
|
10
|
+
export const { clientMiddleware, serverMiddleware } = createMiddlewareManager([]);`;
|
|
30
11
|
|
|
31
12
|
export default async function initMiddleware() {
|
|
32
13
|
await writeIfNotExists(middlewarePath, middlewareTemplate);
|
|
14
|
+
await writeIfNotExists(
|
|
15
|
+
path.join(process.cwd(), 'src/app/core/Middleware/index.ts'),
|
|
16
|
+
`export * from './MiddlewareManager';`
|
|
17
|
+
);
|
|
33
18
|
|
|
34
19
|
// Add to hooks.server.ts
|
|
35
20
|
await injectIntoFile(
|
|
36
21
|
hooksServerPath,
|
|
37
|
-
`import { serverMiddleware } from '$core/Middleware/index.js';`,
|
|
38
22
|
'import',
|
|
23
|
+
`import { serverMiddleware } from '$core';`,
|
|
39
24
|
'before'
|
|
40
25
|
);
|
|
41
26
|
|
|
@@ -50,7 +35,7 @@ export default async function initMiddleware() {
|
|
|
50
35
|
await injectIntoFile(
|
|
51
36
|
layoutPath,
|
|
52
37
|
'<script',
|
|
53
|
-
`\timport { clientMiddleware } from '$core
|
|
38
|
+
`\timport { clientMiddleware } from '$core';\n\tclientMiddleware();`,
|
|
54
39
|
'after'
|
|
55
40
|
);
|
|
56
41
|
|
|
@@ -3,7 +3,7 @@ import path from 'path';
|
|
|
3
3
|
|
|
4
4
|
const schemaPath = path.join(process.cwd(), 'src/app/core/Schemas/Schema.ts');
|
|
5
5
|
|
|
6
|
-
const schemaTemplate = `import { createSchemaFactory } from '@azure-net/kit
|
|
6
|
+
const schemaTemplate = `import { createSchemaFactory } from '@azure-net/kit';
|
|
7
7
|
import { createRules, validationMessagesI18n } from '@azure-net/kit/schema';
|
|
8
8
|
|
|
9
9
|
export const Schema = createSchemaFactory(createRules(validationMessagesI18n));`;
|
|
@@ -12,7 +12,7 @@ export default async function initSchema() {
|
|
|
12
12
|
await writeIfNotExists(schemaPath, schemaTemplate);
|
|
13
13
|
await writeIfNotExists(
|
|
14
14
|
path.join(process.cwd(), 'src/app/core/Schemas/index.ts'),
|
|
15
|
-
`export * from './Schema
|
|
15
|
+
`export * from './Schema';`
|
|
16
16
|
);
|
|
17
17
|
|
|
18
18
|
console.log('✅ Schema factory initialized');
|
|
@@ -2,7 +2,6 @@ import fs from 'fs/promises';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { loadUserConfig } from '../../utils/loadConfig.js';
|
|
4
4
|
|
|
5
|
-
const BASE_STRUCTURE = ['Domain', 'Infrastructure', 'Application', 'Delivery'];
|
|
6
5
|
const APP_ROOT = path.join(process.cwd(), 'src', 'app');
|
|
7
6
|
const CONTEXTS_PATH = path.join(APP_ROOT, 'contexts');
|
|
8
7
|
const CORE_PATH = path.join(APP_ROOT, 'core');
|
|
@@ -16,31 +15,34 @@ async function createDirIfNotExists(dirPath) {
|
|
|
16
15
|
}
|
|
17
16
|
}
|
|
18
17
|
|
|
18
|
+
async function createIndexFile(dirPath, exportPatterns = []) {
|
|
19
|
+
const indexPath = path.join(dirPath, 'index.ts');
|
|
20
|
+
if (exportPatterns.length > 0) {
|
|
21
|
+
const content = exportPatterns.map(pattern => `export * from '${pattern}';`).join('\n') + '\n';
|
|
22
|
+
await fs.writeFile(indexPath, content, 'utf-8');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
19
26
|
async function createBaseStructure(root) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
27
|
+
// Domain
|
|
28
|
+
const domainPath = path.join(root, 'Domain');
|
|
29
|
+
await createDirIfNotExists(path.join(domainPath, 'Entities'));
|
|
30
|
+
await createDirIfNotExists(path.join(domainPath, 'Ports'));
|
|
23
31
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
await createDirIfNotExists(path.join(sectionPath, 'Presenters'));
|
|
39
|
-
await createDirIfNotExists(path.join(sectionPath, 'Schemas'));
|
|
40
|
-
await createDirIfNotExists(path.join(sectionPath, 'Actions'));
|
|
41
|
-
await createDirIfNotExists(path.join(sectionPath, 'Stores'));
|
|
42
|
-
}
|
|
43
|
-
}
|
|
32
|
+
// Infrastructure
|
|
33
|
+
const infraPath = path.join(root, 'Infrastructure');
|
|
34
|
+
await createDirIfNotExists(path.join(infraPath, 'Http', 'Repositories'));
|
|
35
|
+
await createDirIfNotExists(path.join(infraPath, 'Http', 'Datasource'));
|
|
36
|
+
await createDirIfNotExists(path.join(infraPath, 'Providers'));
|
|
37
|
+
|
|
38
|
+
// Application
|
|
39
|
+
const appPath = path.join(root, 'Application');
|
|
40
|
+
await createDirIfNotExists(path.join(appPath, 'Services'));
|
|
41
|
+
await createDirIfNotExists(path.join(appPath, 'Providers'));
|
|
42
|
+
|
|
43
|
+
// Delivery - organized by modules, not by type
|
|
44
|
+
const deliveryPath = path.join(root, 'Delivery');
|
|
45
|
+
await createDirIfNotExists(deliveryPath);
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
export default async function initStructure() {
|
|
@@ -57,12 +59,21 @@ export default async function initStructure() {
|
|
|
57
59
|
|
|
58
60
|
// Create core structure
|
|
59
61
|
await createDirIfNotExists(CORE_PATH);
|
|
60
|
-
await createDirIfNotExists(path.join(CORE_PATH, '
|
|
61
|
-
await createDirIfNotExists(path.join(CORE_PATH, '
|
|
62
|
-
await createDirIfNotExists(path.join(CORE_PATH, '
|
|
62
|
+
await createDirIfNotExists(path.join(CORE_PATH, 'Datasource'));
|
|
63
|
+
await createDirIfNotExists(path.join(CORE_PATH, 'Response'));
|
|
64
|
+
await createDirIfNotExists(path.join(CORE_PATH, 'Schema'));
|
|
63
65
|
await createDirIfNotExists(path.join(CORE_PATH, 'Middleware'));
|
|
64
|
-
await createDirIfNotExists(path.join(CORE_PATH, '
|
|
65
|
-
await createDirIfNotExists(path.join(CORE_PATH, '
|
|
66
|
+
await createDirIfNotExists(path.join(CORE_PATH, 'Presenter'));
|
|
67
|
+
await createDirIfNotExists(path.join(CORE_PATH, 'Translation'));
|
|
68
|
+
|
|
69
|
+
// Create core index.ts that exports all subdirectories
|
|
70
|
+
await createIndexFile(CORE_PATH, [
|
|
71
|
+
'./Schema',
|
|
72
|
+
'./Datasource',
|
|
73
|
+
'./Response',
|
|
74
|
+
'./Middleware',
|
|
75
|
+
'./Translation'
|
|
76
|
+
]);
|
|
66
77
|
|
|
67
78
|
console.log('✅ Structure initialization complete!');
|
|
68
79
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { writeIfNotExists, updateIndexTs, injectIntoFile } from '../../utils/fileUtils.js';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
|
|
4
|
-
const translationsPath = path.join(process.cwd(), 'src/app/core/
|
|
4
|
+
const translationsPath = path.join(process.cwd(), 'src/app/core/Translation');
|
|
5
5
|
const layoutServerPath = path.join(process.cwd(), 'src/routes/+layout.server.ts');
|
|
6
6
|
const layoutTsPath = path.join(process.cwd(), 'src/routes/+layout.ts');
|
|
7
7
|
|
|
8
|
-
const translationProviderTemplate = `import { messages } from './Locales
|
|
8
|
+
const translationProviderTemplate = `import { messages } from './Locales';
|
|
9
9
|
import { createTranslations } from '@azure-net/kit/i18n';
|
|
10
10
|
|
|
11
11
|
export const Translation = createTranslations({
|
|
@@ -28,8 +28,8 @@ const ruLocaleTemplate = `export default {
|
|
|
28
28
|
};`;
|
|
29
29
|
|
|
30
30
|
const localesIndexTemplate = `export const messages = {
|
|
31
|
-
\ten: () => import('./en
|
|
32
|
-
\tru: () => import('./ru
|
|
31
|
+
\ten: () => import('./en').then((res) => res.default),
|
|
32
|
+
\tru: () => import('./ru').then((res) => res.default)
|
|
33
33
|
};`;
|
|
34
34
|
|
|
35
35
|
export default async function initTranslations() {
|
|
@@ -54,13 +54,11 @@ export default async function initTranslations() {
|
|
|
54
54
|
localesIndexTemplate
|
|
55
55
|
);
|
|
56
56
|
|
|
57
|
-
await updateIndexTs(translationsPath);
|
|
58
|
-
|
|
59
57
|
// Add to hooks.server.ts
|
|
60
58
|
await injectIntoFile(
|
|
61
59
|
path.join(process.cwd(), 'src/hooks.server.ts'),
|
|
62
60
|
'import',
|
|
63
|
-
`import { Translation } from '$core
|
|
61
|
+
`import { Translation } from '$core';`,
|
|
64
62
|
'before'
|
|
65
63
|
);
|
|
66
64
|
|
|
@@ -71,18 +69,25 @@ export default async function initTranslations() {
|
|
|
71
69
|
'after'
|
|
72
70
|
);
|
|
73
71
|
|
|
72
|
+
await injectIntoFile(
|
|
73
|
+
path.join(process.cwd(), 'src/hooks.server.ts'),
|
|
74
|
+
'transformPageChunk: ({ html })',
|
|
75
|
+
' => {\n\t\t\t\tconst serialized = serialize(html);\n\t\t\t\treturn applyHtmlLocaleAttr(serialized);\n\t\t\t}',
|
|
76
|
+
'after'
|
|
77
|
+
);
|
|
78
|
+
|
|
74
79
|
// Add to layout files
|
|
75
80
|
await injectIntoFile(
|
|
76
81
|
layoutServerPath,
|
|
77
82
|
'export const load',
|
|
78
|
-
`\treturn { lang: locals.lang
|
|
83
|
+
`\treturn { lang: locals.lang };`,
|
|
79
84
|
'after'
|
|
80
85
|
);
|
|
81
86
|
|
|
82
87
|
await injectIntoFile(
|
|
83
88
|
layoutTsPath,
|
|
84
89
|
'import',
|
|
85
|
-
`import { Translation } from '$core/
|
|
90
|
+
`import { Translation } from '$core';\nimport { browser } from '$app/environment';`,
|
|
86
91
|
'before'
|
|
87
92
|
);
|
|
88
93
|
|
package/src/utils/fileUtils.js
CHANGED
|
@@ -20,7 +20,7 @@ export async function updateIndexTs(dir, filePattern = '.ts', ignore = ['index.t
|
|
|
20
20
|
f.endsWith(filePattern) && !ignore.includes(f)
|
|
21
21
|
);
|
|
22
22
|
const content = files
|
|
23
|
-
.map(f => `export * from './${f.replace('.ts', '')}
|
|
23
|
+
.map(f => `export * from './${f.replace('.ts', '')}';`)
|
|
24
24
|
.join('\n') + '\n';
|
|
25
25
|
await fs.writeFile(path.join(dir, 'index.ts'), content, 'utf-8');
|
|
26
26
|
} catch (error) {
|
|
@@ -28,6 +28,25 @@ export async function updateIndexTs(dir, filePattern = '.ts', ignore = ['index.t
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
export async function ensureIndexExports(layerPath) {
|
|
32
|
+
try {
|
|
33
|
+
const subdirs = await fs.readdir(layerPath, { withFileTypes: true });
|
|
34
|
+
const exportLines = subdirs
|
|
35
|
+
.filter(d => d.isDirectory())
|
|
36
|
+
.map(d => `export * from './${d.name}';`);
|
|
37
|
+
|
|
38
|
+
if (exportLines.length > 0) {
|
|
39
|
+
await fs.writeFile(
|
|
40
|
+
path.join(layerPath, 'index.ts'),
|
|
41
|
+
exportLines.join('\n') + '\n',
|
|
42
|
+
'utf-8'
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
} catch (error) {
|
|
46
|
+
// Directory doesn't exist
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
31
50
|
export async function injectIntoFile(filepath, searchPattern, newContent, position = 'after') {
|
|
32
51
|
try {
|
|
33
52
|
let content = await fs.readFile(filepath, 'utf-8');
|
|
@@ -4,7 +4,10 @@ import { toPascalCase, toCamelCase } from './contextUtils.js';
|
|
|
4
4
|
import { writeIfNotExists, updateIndexTs } from './fileUtils.js';
|
|
5
5
|
|
|
6
6
|
export async function ensureProvider(context, layer, dependencies = {}) {
|
|
7
|
-
const
|
|
7
|
+
const contextName = toPascalCase(context);
|
|
8
|
+
const providerName = `${layer}Provider`;
|
|
9
|
+
const boundaryName = `${contextName}${layer}BoundaryProvider`;
|
|
10
|
+
|
|
8
11
|
const contextPath = path.join(process.cwd(), 'src/app/contexts', context);
|
|
9
12
|
const providerPath = path.join(contextPath, layer, 'Providers', `${layer}Provider.ts`);
|
|
10
13
|
|
|
@@ -16,29 +19,38 @@ export async function ensureProvider(context, layer, dependencies = {}) {
|
|
|
16
19
|
const content = generateProviderContent(context, layer, dependencies);
|
|
17
20
|
await writeIfNotExists(providerPath, content);
|
|
18
21
|
await updateIndexTs(path.dirname(providerPath));
|
|
19
|
-
|
|
22
|
+
|
|
23
|
+
// Update layer index to export provider
|
|
24
|
+
const layerIndexPath = path.join(contextPath, layer, 'index.ts');
|
|
25
|
+
await writeIfNotExists(layerIndexPath, `export { ${providerName} } from './Providers';`);
|
|
26
|
+
|
|
20
27
|
return { exists: false, name: providerName, path: providerPath };
|
|
21
28
|
}
|
|
22
29
|
}
|
|
23
30
|
|
|
24
31
|
function generateProviderContent(context, layer, dependencies) {
|
|
25
|
-
const
|
|
32
|
+
const contextName = toPascalCase(context);
|
|
33
|
+
const boundaryName = `${contextName}${layer}BoundaryProvider`;
|
|
26
34
|
const varName = `${layer}Provider`;
|
|
27
35
|
|
|
28
36
|
let imports = [`import { createBoundaryProvider } from '@azure-net/kit';`];
|
|
29
37
|
let deps = [];
|
|
30
|
-
let
|
|
38
|
+
let depParams = '';
|
|
31
39
|
|
|
32
|
-
if (dependencies.
|
|
33
|
-
imports.push(`import {
|
|
34
|
-
|
|
40
|
+
if (dependencies.hasDatasource && layer === 'Infrastructure') {
|
|
41
|
+
imports.push(`import { DatasourceProvider } from './DatasourceProvider';`);
|
|
42
|
+
deps.push('DatasourceProvider');
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
if (dependencies.infrastructure && layer === 'Application') {
|
|
38
|
-
imports.push(`import { InfrastructureProvider } from '
|
|
46
|
+
imports.push(`import { InfrastructureProvider } from '$${context}/Infrastructure';`);
|
|
39
47
|
deps.push('InfrastructureProvider');
|
|
40
48
|
}
|
|
41
49
|
|
|
50
|
+
if (deps.length > 0) {
|
|
51
|
+
depParams = `{ ${deps.join(', ')} }`;
|
|
52
|
+
}
|
|
53
|
+
|
|
42
54
|
const dependsOnStr = deps.length > 0
|
|
43
55
|
? `,\n\t{ dependsOn: { ${deps.join(', ')} } }`
|
|
44
56
|
: '';
|
|
@@ -46,32 +58,49 @@ function generateProviderContent(context, layer, dependencies) {
|
|
|
46
58
|
return `${imports.join('\n')}
|
|
47
59
|
|
|
48
60
|
export const ${varName} = createBoundaryProvider(
|
|
49
|
-
\t'${
|
|
50
|
-
\t(${
|
|
51
|
-
|
|
61
|
+
\t'${boundaryName}',
|
|
62
|
+
\t(${depParams ? depParams : ''}) => ({
|
|
63
|
+
\t\t// Services will be added here
|
|
52
64
|
\t})${dependsOnStr}
|
|
53
65
|
);`;
|
|
54
66
|
}
|
|
55
67
|
|
|
56
|
-
export async function addToProvider(providerPath, serviceName, importPath) {
|
|
68
|
+
export async function addToProvider(providerPath, serviceName, importPath, dependency = null) {
|
|
57
69
|
let content = await fs.readFile(providerPath, 'utf-8');
|
|
58
70
|
|
|
59
71
|
// Add import if not exists
|
|
60
72
|
const importStatement = `import { ${serviceName} } from '${importPath}';`;
|
|
61
|
-
if (!content.includes(
|
|
73
|
+
if (!content.includes(serviceName)) {
|
|
62
74
|
const lastImportIndex = content.lastIndexOf('import ');
|
|
63
75
|
const endOfImportLine = content.indexOf('\n', lastImportIndex);
|
|
64
76
|
content = content.slice(0, endOfImportLine + 1) + importStatement + '\n' + content.slice(endOfImportLine + 1);
|
|
65
77
|
}
|
|
66
78
|
|
|
67
|
-
// Add factory method
|
|
68
|
-
const factoryLine =
|
|
69
|
-
|
|
70
|
-
|
|
79
|
+
// Add factory method with dependency
|
|
80
|
+
const factoryLine = dependency
|
|
81
|
+
? `\t\t${serviceName}: () => new ${serviceName}(${dependency})`
|
|
82
|
+
: `\t\t${serviceName}: () => new ${serviceName}()`;
|
|
83
|
+
|
|
84
|
+
if (!content.includes(`${serviceName}:`)) {
|
|
85
|
+
// Find the position to insert
|
|
86
|
+
const returnIndex = content.indexOf('=> ({');
|
|
87
|
+
const endOfReturn = content.indexOf('\t})', returnIndex);
|
|
88
|
+
|
|
89
|
+
// Check if there are existing services
|
|
90
|
+
const servicesSection = content.substring(returnIndex, endOfReturn);
|
|
91
|
+
const hasServices = servicesSection.includes(':');
|
|
71
92
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
93
|
+
if (hasServices) {
|
|
94
|
+
// Add comma before new service
|
|
95
|
+
const beforeEnd = content.lastIndexOf('\n', endOfReturn);
|
|
96
|
+
content = content.slice(0, beforeEnd) + ',\n' + factoryLine + content.slice(beforeEnd);
|
|
97
|
+
} else {
|
|
98
|
+
// First service, replace comment
|
|
99
|
+
content = content.replace(
|
|
100
|
+
'// Services will be added here',
|
|
101
|
+
factoryLine
|
|
102
|
+
);
|
|
103
|
+
}
|
|
75
104
|
}
|
|
76
105
|
|
|
77
106
|
await fs.writeFile(providerPath, content, 'utf-8');
|