@azure-net/cli 5.0.0-1 → 5.0.0
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 +12 -45
- package/bin/azure-net.ts +13 -5
- package/package.json +1 -1
- package/src/checks/checkDomain.js +8 -38
- package/src/checks/checkFoldersStructure.js +0 -14
- package/src/checks/checkHooks.js +0 -5
- package/src/checks/checkImports.js +0 -1
- package/src/checks/checkInternal.js +2 -1
- package/src/checks/checkInternal.ts +2 -0
- package/src/checks/checkLayerBoundaries.js +50 -54
- package/src/checks/checkLayerBoundaries.ts +55 -58
- package/src/checks/checkLayerImports.js +136 -0
- package/src/checks/checkLayerImports.ts +185 -0
- package/src/checks/checkPresenterNames.js +1 -37
- package/src/checks/checkPresenterNames.ts +1 -6
- package/src/checks/checkProject.js +0 -6
- package/src/checks/checkProviderGraph.js +1 -38
- package/src/checks/checkProviderNames.js +0 -28
- package/src/checks/index.js +1 -1
- package/src/checks/index.ts +1 -0
- package/src/generate/layers/generateRepository.js +4 -19
- package/src/generate/layers/generateRepositoryMethod.js +0 -12
- package/src/generate/layers/generateUseCases.js +0 -6
- package/src/generate/layers/repositoryShared.js +41 -104
- package/src/generate/layers/repositoryShared.ts +17 -12
- package/src/generate/module/generateDomain.js +0 -25
- package/src/generate/module/generateModule.js +0 -10
- package/src/generate/module/generateModulePreset.js +10 -51
- package/src/generate/module/generateModulePreset.ts +9 -8
- package/src/generate/module/generatePresenter.js +29 -76
- package/src/generate/module/generatePresenter.ts +15 -10
- package/src/generate/module/generateRepository.js +0 -17
- package/src/generate/module/generateSchemaFromType.js +9 -58
- package/src/generate/module/generateSchemaFromType.ts +1 -2
- package/src/generate/module/generateTypesJson.js +4 -45
- package/src/generate/module/generateUseCases.js +0 -9
- package/src/generate/module/repositoryModuleShared.js +21 -98
- package/src/generate/module/repositoryModuleShared.ts +14 -10
- package/src/generate/ui/generateComponent.js +0 -16
- package/src/generate/ui/generateDesignComponent.js +0 -12
- package/src/generate/ui/generateWidget.js +0 -12
- package/src/init/initAliases.js +42 -64
- package/src/init/initAliases.ts +47 -42
- package/src/init/initEdges.js +0 -15
- package/src/init/initStructure.js +48 -21
- package/src/init/initStructure.ts +51 -3
- package/src/install/installFresh.js +0 -16
- package/src/plugins/addLint.js +11 -46
- package/src/scaffolds/createCoreSchema.js +6 -40
- package/src/scaffolds/createCoreSchema.ts +6 -18
- package/src/scaffolds/createDatasource.js +26 -61
- package/src/scaffolds/createDatasource.ts +26 -40
- package/src/scaffolds/createDatasourceProvider.js +19 -56
- package/src/scaffolds/createDatasourceProvider.ts +20 -34
- package/src/scaffolds/createPresenterFactory.js +5 -34
- package/src/scaffolds/createPresenterFactory.ts +5 -17
- package/src/scaffolds/createProgram.js +1 -5
- package/src/scaffolds/createResponse.js +3 -11
- package/src/scaffolds/createResponse.ts +3 -1
- package/src/scaffolds/createSchemaRule.js +3 -8
- package/src/scaffolds/createSchemaRule.ts +3 -1
- package/src/scaffolds/createTranslationManager.js +3 -14
- package/src/scaffolds/createTranslationManager.ts +3 -1
- package/src/utils/contextUtils.js +5 -20
- package/src/utils/contextUtils.ts +2 -3
- package/src/utils/fileUtils.js +3 -38
- package/src/utils/fileUtils.ts +0 -26
- package/src/utils/loadConfig.js +1 -14
- package/src/utils/loadConfig.ts +1 -3
- package/src/utils/sharedFoundation.js +48 -0
- package/src/utils/sharedFoundation.ts +70 -0
|
@@ -2,7 +2,6 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import prompts from 'prompts';
|
|
4
4
|
import { getConfigState, resolveContextAlias, selectContext } from './repositoryModuleShared.js';
|
|
5
|
-
|
|
6
5
|
const listDirectories = async (targetPath) => {
|
|
7
6
|
try {
|
|
8
7
|
const items = await fs.readdir(targetPath, { withFileTypes: true });
|
|
@@ -11,7 +10,6 @@ const listDirectories = async (targetPath) => {
|
|
|
11
10
|
return [];
|
|
12
11
|
}
|
|
13
12
|
};
|
|
14
|
-
|
|
15
13
|
const listSchemaFiles = async (targetPath) => {
|
|
16
14
|
try {
|
|
17
15
|
const files = await fs.readdir(targetPath);
|
|
@@ -20,24 +18,18 @@ const listSchemaFiles = async (targetPath) => {
|
|
|
20
18
|
return [];
|
|
21
19
|
}
|
|
22
20
|
};
|
|
23
|
-
|
|
24
21
|
const resolveAliasMap = async () => {
|
|
25
|
-
const { contexts
|
|
22
|
+
const { contexts } = await getConfigState();
|
|
26
23
|
const map = new Map();
|
|
27
|
-
map.set(coreAlias, path.join(process.cwd(), 'src', 'core'));
|
|
28
|
-
|
|
29
24
|
for (const context of contexts) {
|
|
30
25
|
const alias = resolveContextAlias(contexts, context.name);
|
|
31
26
|
map.set(alias, path.join(process.cwd(), 'src', 'app', context.name));
|
|
32
27
|
}
|
|
33
|
-
|
|
34
28
|
return map;
|
|
35
29
|
};
|
|
36
|
-
|
|
37
30
|
const resolveImportToFile = async (fromFile, importPath, aliasMap) => {
|
|
38
31
|
const rawImport = String(importPath ?? '').trim();
|
|
39
32
|
if (!rawImport.length) return null;
|
|
40
|
-
|
|
41
33
|
let absolute = '';
|
|
42
34
|
if (rawImport.startsWith('.')) {
|
|
43
35
|
absolute = path.resolve(path.dirname(fromFile), rawImport);
|
|
@@ -47,7 +39,6 @@ const resolveImportToFile = async (fromFile, importPath, aliasMap) => {
|
|
|
47
39
|
const suffix = rawImport === alias ? '' : rawImport.slice(alias.length + 1);
|
|
48
40
|
absolute = path.join(aliasMap.get(alias), suffix);
|
|
49
41
|
}
|
|
50
|
-
|
|
51
42
|
const tsCandidate = absolute.endsWith('.ts') ? absolute : `${absolute}.ts`;
|
|
52
43
|
try {
|
|
53
44
|
await fs.access(tsCandidate);
|
|
@@ -62,36 +53,34 @@ const resolveImportToFile = async (fromFile, importPath, aliasMap) => {
|
|
|
62
53
|
}
|
|
63
54
|
}
|
|
64
55
|
};
|
|
65
|
-
|
|
66
56
|
const extractSchemaTypeName = (content) => {
|
|
67
57
|
const typeMatch = content.match(/=\s*[A-Za-z0-9_]+<\s*([^>]+?)\s*>\s*\(\)/m);
|
|
68
58
|
if (!typeMatch) return null;
|
|
69
59
|
return String(typeMatch[1]).trim();
|
|
70
60
|
};
|
|
71
|
-
|
|
72
61
|
const findTypeImportPath = (content, typeName) => {
|
|
73
62
|
const importRegex = /import\s+type\s+\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/g;
|
|
74
63
|
let match;
|
|
75
64
|
while ((match = importRegex.exec(content)) !== null) {
|
|
76
|
-
const
|
|
65
|
+
const importedList = match[1];
|
|
66
|
+
const sourcePath = match[2];
|
|
67
|
+
if (!importedList || !sourcePath) continue;
|
|
68
|
+
const imported = importedList
|
|
77
69
|
.split(',')
|
|
78
70
|
.map((item) => item.trim())
|
|
79
71
|
.filter(Boolean);
|
|
80
72
|
if (imported.includes(typeName)) {
|
|
81
|
-
return
|
|
73
|
+
return sourcePath;
|
|
82
74
|
}
|
|
83
75
|
}
|
|
84
76
|
return null;
|
|
85
77
|
};
|
|
86
|
-
|
|
87
78
|
const extractInterfaceBody = (content, interfaceName) => {
|
|
88
79
|
const declarationRegex = new RegExp(`export\\s+interface\\s+${interfaceName}\\s*\\{`);
|
|
89
80
|
const declarationMatch = content.match(declarationRegex);
|
|
90
81
|
if (!declarationMatch || declarationMatch.index === undefined) return null;
|
|
91
|
-
|
|
92
82
|
const openBraceIndex = content.indexOf('{', declarationMatch.index);
|
|
93
83
|
if (openBraceIndex === -1) return null;
|
|
94
|
-
|
|
95
84
|
let depth = 0;
|
|
96
85
|
let closeBraceIndex = -1;
|
|
97
86
|
for (let index = openBraceIndex; index < content.length; index += 1) {
|
|
@@ -105,11 +94,9 @@ const extractInterfaceBody = (content, interfaceName) => {
|
|
|
105
94
|
}
|
|
106
95
|
}
|
|
107
96
|
}
|
|
108
|
-
|
|
109
97
|
if (closeBraceIndex === -1) return null;
|
|
110
98
|
return content.slice(openBraceIndex + 1, closeBraceIndex);
|
|
111
99
|
};
|
|
112
|
-
|
|
113
100
|
const resolveRelativeExportPath = async (fromFile, exportPath) => {
|
|
114
101
|
const absolute = path.resolve(path.dirname(fromFile), exportPath);
|
|
115
102
|
const tsCandidate = absolute.endsWith('.ts') ? absolute : `${absolute}.ts`;
|
|
@@ -126,38 +113,30 @@ const resolveRelativeExportPath = async (fromFile, exportPath) => {
|
|
|
126
113
|
}
|
|
127
114
|
}
|
|
128
115
|
};
|
|
129
|
-
|
|
130
116
|
const findInterfaceBodyByExportGraph = async (entryFilePath, interfaceName, visited = new Set()) => {
|
|
131
117
|
const resolvedPath = path.resolve(entryFilePath);
|
|
132
118
|
if (visited.has(resolvedPath)) return null;
|
|
133
119
|
visited.add(resolvedPath);
|
|
134
|
-
|
|
135
120
|
let content = '';
|
|
136
121
|
try {
|
|
137
122
|
content = await fs.readFile(resolvedPath, 'utf-8');
|
|
138
123
|
} catch {
|
|
139
124
|
return null;
|
|
140
125
|
}
|
|
141
|
-
|
|
142
126
|
const direct = extractInterfaceBody(content, interfaceName);
|
|
143
127
|
if (direct) return direct;
|
|
144
|
-
|
|
145
|
-
const exportRegex = /export\\s+(?:\\*|\\{[^}]+\\})\\s+from\\s+['\"]([^'\"]+)['\"]/g;
|
|
128
|
+
const exportRegex = /export\s+(?:\*|\{[^}]+\})\s+from\s+['"]([^'"]+)['"]/g;
|
|
146
129
|
let match;
|
|
147
130
|
while ((match = exportRegex.exec(content)) !== null) {
|
|
148
131
|
const exportPath = match[1];
|
|
149
132
|
if (!exportPath || !exportPath.startsWith('.')) continue;
|
|
150
|
-
// eslint-disable-next-line no-await-in-loop
|
|
151
133
|
const nextPath = await resolveRelativeExportPath(resolvedPath, exportPath);
|
|
152
134
|
if (!nextPath) continue;
|
|
153
|
-
// eslint-disable-next-line no-await-in-loop
|
|
154
135
|
const nested = await findInterfaceBodyByExportGraph(nextPath, interfaceName, visited);
|
|
155
136
|
if (nested) return nested;
|
|
156
137
|
}
|
|
157
|
-
|
|
158
138
|
return null;
|
|
159
139
|
};
|
|
160
|
-
|
|
161
140
|
const findInterfaceBodyByScan = async (rootDir, interfaceName) => {
|
|
162
141
|
const queue = [rootDir];
|
|
163
142
|
while (queue.length) {
|
|
@@ -169,7 +148,6 @@ const findInterfaceBodyByScan = async (rootDir, interfaceName) => {
|
|
|
169
148
|
} catch {
|
|
170
149
|
continue;
|
|
171
150
|
}
|
|
172
|
-
|
|
173
151
|
for (const entry of entries) {
|
|
174
152
|
const absolute = path.join(current, entry.name);
|
|
175
153
|
if (entry.isDirectory()) {
|
|
@@ -177,7 +155,6 @@ const findInterfaceBodyByScan = async (rootDir, interfaceName) => {
|
|
|
177
155
|
continue;
|
|
178
156
|
}
|
|
179
157
|
if (!entry.isFile() || !entry.name.endsWith('.ts')) continue;
|
|
180
|
-
// eslint-disable-next-line no-await-in-loop
|
|
181
158
|
const content = await fs.readFile(absolute, 'utf-8');
|
|
182
159
|
const body = extractInterfaceBody(content, interfaceName);
|
|
183
160
|
if (body) return body;
|
|
@@ -185,17 +162,16 @@ const findInterfaceBodyByScan = async (rootDir, interfaceName) => {
|
|
|
185
162
|
}
|
|
186
163
|
return null;
|
|
187
164
|
};
|
|
188
|
-
|
|
189
165
|
const extractRuleKeys = (body) => {
|
|
190
166
|
const keys = [];
|
|
191
167
|
const keyRegex = /^\s*([A-Za-z_$][A-Za-z0-9_$]*)\s*:/gm;
|
|
192
168
|
let match;
|
|
193
169
|
while ((match = keyRegex.exec(body)) !== null) {
|
|
194
|
-
|
|
170
|
+
const key = match[1];
|
|
171
|
+
if (key) keys.push(key);
|
|
195
172
|
}
|
|
196
173
|
return Array.from(new Set(keys));
|
|
197
174
|
};
|
|
198
|
-
|
|
199
175
|
const extractInterfaceFields = (body) => {
|
|
200
176
|
const fields = [];
|
|
201
177
|
const fieldRegex = /^\s*(?:readonly\s+)?([A-Za-z_$][A-Za-z0-9_$]*)(\?)?\s*:\s*([^;]+);/gm;
|
|
@@ -211,53 +187,42 @@ const extractInterfaceFields = (body) => {
|
|
|
211
187
|
type
|
|
212
188
|
});
|
|
213
189
|
}
|
|
214
|
-
|
|
215
190
|
const unique = new Map();
|
|
216
191
|
for (const field of fields) {
|
|
217
192
|
if (!unique.has(field.name)) unique.set(field.name, field);
|
|
218
193
|
}
|
|
219
194
|
return Array.from(unique.values());
|
|
220
195
|
};
|
|
221
|
-
|
|
222
196
|
const buildRulesArray = (field) => {
|
|
223
197
|
const ruleCalls = [];
|
|
224
198
|
if (!field.optional) {
|
|
225
199
|
ruleCalls.push('rules.required()');
|
|
226
200
|
}
|
|
227
|
-
|
|
228
201
|
if (field.type === 'string') {
|
|
229
202
|
ruleCalls.push('rules.string()');
|
|
230
203
|
} else if (field.type === 'number') {
|
|
231
204
|
ruleCalls.push('rules.number()');
|
|
232
205
|
}
|
|
233
|
-
|
|
234
206
|
return `[${ruleCalls.join(', ')}]`;
|
|
235
207
|
};
|
|
236
|
-
|
|
237
208
|
const updateRulesObject = (schemaContent, fields) => {
|
|
238
209
|
const rulesRegex = /\.rules\(\(rules\)\s*=>\s*\(\{([\s\S]*?)\}\)\)/m;
|
|
239
210
|
const match = schemaContent.match(rulesRegex);
|
|
240
211
|
if (!match) return null;
|
|
241
|
-
|
|
242
212
|
const currentBody = match[1] ?? '';
|
|
243
213
|
const existingKeys = new Set(extractRuleKeys(currentBody));
|
|
244
214
|
const newLines = fields.filter((field) => !existingKeys.has(field.name)).map((field) => `\t\t${field.name}: ${buildRulesArray(field)},`);
|
|
245
|
-
|
|
246
215
|
if (!newLines.length) return schemaContent;
|
|
247
|
-
|
|
248
216
|
const trimmedBody = currentBody.trimEnd();
|
|
249
217
|
const nextBody = trimmedBody.length ? `${trimmedBody}\n${newLines.join('\n')}\n\t` : `\n${newLines.join('\n')}\n\t`;
|
|
250
|
-
|
|
251
218
|
return schemaContent.replace(rulesRegex, `.rules((rules) => ({${nextBody}}))`);
|
|
252
219
|
};
|
|
253
|
-
|
|
254
220
|
export default async function generateSchemaFromType() {
|
|
255
221
|
const contextName = await selectContext('Select context for schema update:');
|
|
256
222
|
if (!contextName) {
|
|
257
223
|
process.exitCode = 1;
|
|
258
224
|
return;
|
|
259
225
|
}
|
|
260
|
-
|
|
261
226
|
const presentationPath = path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'presentation');
|
|
262
227
|
const presenterFolders = await listDirectories(presentationPath);
|
|
263
228
|
if (!presenterFolders.length) {
|
|
@@ -265,7 +230,6 @@ export default async function generateSchemaFromType() {
|
|
|
265
230
|
process.exitCode = 1;
|
|
266
231
|
return;
|
|
267
232
|
}
|
|
268
|
-
|
|
269
233
|
const { presenterFolder } = await prompts({
|
|
270
234
|
type: 'select',
|
|
271
235
|
name: 'presenterFolder',
|
|
@@ -273,12 +237,10 @@ export default async function generateSchemaFromType() {
|
|
|
273
237
|
choices: presenterFolders.map((item) => ({ title: item, value: item })),
|
|
274
238
|
initial: 0
|
|
275
239
|
});
|
|
276
|
-
|
|
277
240
|
if (!presenterFolder) {
|
|
278
241
|
process.exitCode = 1;
|
|
279
242
|
return;
|
|
280
243
|
}
|
|
281
|
-
|
|
282
244
|
const schemaFolderPath = path.join(presentationPath, String(presenterFolder), 'schema');
|
|
283
245
|
const schemas = await listSchemaFiles(schemaFolderPath);
|
|
284
246
|
if (!schemas.length) {
|
|
@@ -286,7 +248,6 @@ export default async function generateSchemaFromType() {
|
|
|
286
248
|
process.exitCode = 1;
|
|
287
249
|
return;
|
|
288
250
|
}
|
|
289
|
-
|
|
290
251
|
const { schemaFile } = await prompts({
|
|
291
252
|
type: 'select',
|
|
292
253
|
name: 'schemaFile',
|
|
@@ -294,29 +255,24 @@ export default async function generateSchemaFromType() {
|
|
|
294
255
|
choices: schemas.map((item) => ({ title: item, value: item })),
|
|
295
256
|
initial: 0
|
|
296
257
|
});
|
|
297
|
-
|
|
298
258
|
if (!schemaFile) {
|
|
299
259
|
process.exitCode = 1;
|
|
300
260
|
return;
|
|
301
261
|
}
|
|
302
|
-
|
|
303
262
|
const schemaPath = path.join(schemaFolderPath, String(schemaFile));
|
|
304
263
|
const schemaContent = await fs.readFile(schemaPath, 'utf-8');
|
|
305
|
-
|
|
306
264
|
const typeName = extractSchemaTypeName(schemaContent);
|
|
307
265
|
if (!typeName) {
|
|
308
266
|
console.error('❌ Unable to resolve schema generic type.');
|
|
309
267
|
process.exitCode = 1;
|
|
310
268
|
return;
|
|
311
269
|
}
|
|
312
|
-
|
|
313
270
|
const importPath = findTypeImportPath(schemaContent, typeName);
|
|
314
271
|
if (!importPath) {
|
|
315
272
|
console.error(`❌ Unable to find import for type "${typeName}".`);
|
|
316
273
|
process.exitCode = 1;
|
|
317
274
|
return;
|
|
318
275
|
}
|
|
319
|
-
|
|
320
276
|
const aliasMap = await resolveAliasMap();
|
|
321
277
|
const typeFilePath = await resolveImportToFile(schemaPath, importPath, aliasMap);
|
|
322
278
|
if (!typeFilePath) {
|
|
@@ -324,7 +280,6 @@ export default async function generateSchemaFromType() {
|
|
|
324
280
|
process.exitCode = 1;
|
|
325
281
|
return;
|
|
326
282
|
}
|
|
327
|
-
|
|
328
283
|
let interfaceBody = await findInterfaceBodyByExportGraph(typeFilePath, typeName);
|
|
329
284
|
if (!interfaceBody) {
|
|
330
285
|
interfaceBody = await findInterfaceBodyByScan(path.dirname(typeFilePath), typeName);
|
|
@@ -334,25 +289,21 @@ export default async function generateSchemaFromType() {
|
|
|
334
289
|
process.exitCode = 1;
|
|
335
290
|
return;
|
|
336
291
|
}
|
|
337
|
-
|
|
338
292
|
const interfaceFields = extractInterfaceFields(interfaceBody);
|
|
339
293
|
if (!interfaceFields.length) {
|
|
340
294
|
console.log('ℹ️ Interface has no explicit keys to transfer into rules.');
|
|
341
295
|
return;
|
|
342
296
|
}
|
|
343
|
-
|
|
344
297
|
const updatedSchemaContent = updateRulesObject(schemaContent, interfaceFields);
|
|
345
298
|
if (!updatedSchemaContent) {
|
|
346
299
|
console.error('❌ Could not locate `.rules((rules) => ({}))` block in schema file.');
|
|
347
300
|
process.exitCode = 1;
|
|
348
301
|
return;
|
|
349
302
|
}
|
|
350
|
-
|
|
351
303
|
if (updatedSchemaContent === schemaContent) {
|
|
352
304
|
console.log('ℹ️ Rules already include all keys from type.');
|
|
353
305
|
return;
|
|
354
306
|
}
|
|
355
|
-
|
|
356
307
|
await fs.writeFile(schemaPath, updatedSchemaContent, 'utf-8');
|
|
357
308
|
console.log(`✅ Schema updated from type: ${schemaPath}`);
|
|
358
309
|
}
|
|
@@ -22,9 +22,8 @@ const listSchemaFiles = async (targetPath: string): Promise<string[]> => {
|
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
const resolveAliasMap = async (): Promise<Map<string, string>> => {
|
|
25
|
-
const { contexts
|
|
25
|
+
const { contexts } = await getConfigState();
|
|
26
26
|
const map = new Map<string, string>();
|
|
27
|
-
map.set(coreAlias, path.join(process.cwd(), 'src', 'core'));
|
|
28
27
|
|
|
29
28
|
for (const context of contexts) {
|
|
30
29
|
const alias = resolveContextAlias(contexts, context.name);
|
|
@@ -2,34 +2,26 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import prompts from 'prompts';
|
|
4
4
|
import { selectContext } from './repositoryModuleShared.js';
|
|
5
|
-
|
|
6
5
|
const isValidIdentifier = (value) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(value);
|
|
7
|
-
|
|
8
6
|
const toPropertyName = (key) => (isValidIdentifier(key) ? key : `'${String(key).replace(/'/g, "\\'")}'`);
|
|
9
|
-
|
|
10
7
|
const getIndent = (level) => '\t'.repeat(level);
|
|
11
|
-
|
|
12
8
|
const normalizeObjectInput = (rawValue) => {
|
|
13
9
|
let input = String(rawValue ?? '').trim();
|
|
14
10
|
if (input.endsWith(';')) {
|
|
15
11
|
input = input.slice(0, -1).trim();
|
|
16
12
|
}
|
|
17
|
-
|
|
18
13
|
const openCurly = (input.match(/\{/g) ?? []).length;
|
|
19
14
|
const closeCurly = (input.match(/\}/g) ?? []).length;
|
|
20
15
|
if (openCurly > closeCurly) {
|
|
21
16
|
input += '}'.repeat(openCurly - closeCurly);
|
|
22
17
|
}
|
|
23
|
-
|
|
24
18
|
return input;
|
|
25
19
|
};
|
|
26
|
-
|
|
27
20
|
const parseObjectInput = (rawValue) => {
|
|
28
21
|
const input = normalizeObjectInput(rawValue);
|
|
29
22
|
if (!input.length) {
|
|
30
23
|
throw new Error('Input is empty');
|
|
31
24
|
}
|
|
32
|
-
|
|
33
25
|
try {
|
|
34
26
|
const parsed = JSON.parse(input);
|
|
35
27
|
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
@@ -39,63 +31,49 @@ const parseObjectInput = (rawValue) => {
|
|
|
39
31
|
} catch {
|
|
40
32
|
// fallback to JS object literal
|
|
41
33
|
}
|
|
42
|
-
|
|
43
|
-
// eslint-disable-next-line no-new-func
|
|
44
34
|
const parsed = new Function(`"use strict"; return (${input});`)();
|
|
45
35
|
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
46
36
|
throw new Error('Input root must be an object');
|
|
47
37
|
}
|
|
48
38
|
return parsed;
|
|
49
39
|
};
|
|
50
|
-
|
|
51
40
|
const stringifyType = (value, level = 1) => {
|
|
52
41
|
if (value === null) return 'null';
|
|
53
|
-
|
|
54
42
|
const valueType = typeof value;
|
|
55
43
|
if (valueType === 'string') return 'string';
|
|
56
44
|
if (valueType === 'number') return Number.isFinite(value) ? 'number' : 'unknown';
|
|
57
45
|
if (valueType === 'boolean') return 'boolean';
|
|
58
46
|
if (valueType === 'bigint') return 'bigint';
|
|
59
47
|
if (valueType === 'undefined') return 'undefined';
|
|
60
|
-
|
|
61
48
|
if (Array.isArray(value)) {
|
|
62
49
|
if (!value.length) return 'unknown[]';
|
|
63
|
-
|
|
64
50
|
const itemTypes = Array.from(new Set(value.map((item) => stringifyType(item, level + 1))));
|
|
65
51
|
if (itemTypes.length === 1) {
|
|
66
|
-
const single = itemTypes[0];
|
|
52
|
+
const single = itemTypes[0] ?? 'unknown';
|
|
67
53
|
if (single.includes('\n')) return `Array<${single}>`;
|
|
68
54
|
return `${single}[]`;
|
|
69
55
|
}
|
|
70
|
-
|
|
71
56
|
const union = itemTypes.join(' | ');
|
|
72
57
|
if (union.includes('\n')) return `Array<${union}>`;
|
|
73
58
|
return `(${union})[]`;
|
|
74
59
|
}
|
|
75
|
-
|
|
76
60
|
if (valueType === 'object') {
|
|
77
61
|
const entries = Object.entries(value);
|
|
78
62
|
if (!entries.length) return 'Record<string, unknown>';
|
|
79
|
-
|
|
80
63
|
const lines = entries.map(([entryKey, entryValue]) => {
|
|
81
64
|
const propertyType = stringifyType(entryValue, level + 1);
|
|
82
65
|
return `${getIndent(level + 1)}${toPropertyName(entryKey)}: ${propertyType};`;
|
|
83
66
|
});
|
|
84
|
-
|
|
85
67
|
return `{\n${lines.join('\n')}\n${getIndent(level)}}`;
|
|
86
68
|
}
|
|
87
|
-
|
|
88
69
|
return 'unknown';
|
|
89
70
|
};
|
|
90
|
-
|
|
91
71
|
const findInterfaceDeclaration = (content) => {
|
|
92
72
|
const declarationMatch = content.match(/export\s+interface\s+([A-Za-z0-9_]+)\s*\{/);
|
|
93
73
|
if (!declarationMatch || declarationMatch.index === undefined) return null;
|
|
94
|
-
|
|
95
|
-
const name = declarationMatch[1];
|
|
74
|
+
const name = declarationMatch[1] ?? 'Interface';
|
|
96
75
|
const openBraceIndex = content.indexOf('{', declarationMatch.index);
|
|
97
76
|
if (openBraceIndex === -1) return null;
|
|
98
|
-
|
|
99
77
|
let depth = 0;
|
|
100
78
|
let closeBraceIndex = -1;
|
|
101
79
|
for (let index = openBraceIndex; index < content.length; index += 1) {
|
|
@@ -109,9 +87,7 @@ const findInterfaceDeclaration = (content) => {
|
|
|
109
87
|
}
|
|
110
88
|
}
|
|
111
89
|
}
|
|
112
|
-
|
|
113
90
|
if (closeBraceIndex === -1) return null;
|
|
114
|
-
|
|
115
91
|
const body = content.slice(openBraceIndex + 1, closeBraceIndex);
|
|
116
92
|
return {
|
|
117
93
|
name,
|
|
@@ -120,17 +96,16 @@ const findInterfaceDeclaration = (content) => {
|
|
|
120
96
|
body
|
|
121
97
|
};
|
|
122
98
|
};
|
|
123
|
-
|
|
124
99
|
const extractExistingRootKeys = (body) => {
|
|
125
100
|
const keys = new Set();
|
|
126
101
|
const keyRegex = /^\s*([A-Za-z_$][A-Za-z0-9_$]*)\??\s*:/gm;
|
|
127
102
|
let match;
|
|
128
103
|
while ((match = keyRegex.exec(body)) !== null) {
|
|
129
|
-
|
|
104
|
+
const key = match[1];
|
|
105
|
+
if (key) keys.add(key);
|
|
130
106
|
}
|
|
131
107
|
return keys;
|
|
132
108
|
};
|
|
133
|
-
|
|
134
109
|
const listDomains = async (contextName) => {
|
|
135
110
|
const domainRoot = path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'domain');
|
|
136
111
|
try {
|
|
@@ -140,7 +115,6 @@ const listDomains = async (contextName) => {
|
|
|
140
115
|
return [];
|
|
141
116
|
}
|
|
142
117
|
};
|
|
143
|
-
|
|
144
118
|
const listModelFiles = async (contextName, domainName) => {
|
|
145
119
|
const modelRoot = path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'domain', domainName, 'model');
|
|
146
120
|
try {
|
|
@@ -155,21 +129,18 @@ const listModelFiles = async (contextName, domainName) => {
|
|
|
155
129
|
return [];
|
|
156
130
|
}
|
|
157
131
|
};
|
|
158
|
-
|
|
159
132
|
export default async function generateTypesJson() {
|
|
160
133
|
const contextName = await selectContext('Select context for model update:');
|
|
161
134
|
if (!contextName) {
|
|
162
135
|
process.exitCode = 1;
|
|
163
136
|
return;
|
|
164
137
|
}
|
|
165
|
-
|
|
166
138
|
const domains = await listDomains(contextName);
|
|
167
139
|
if (!domains.length) {
|
|
168
140
|
console.error(`❌ No domains found in context "${contextName}".`);
|
|
169
141
|
process.exitCode = 1;
|
|
170
142
|
return;
|
|
171
143
|
}
|
|
172
|
-
|
|
173
144
|
const { domainName } = await prompts({
|
|
174
145
|
type: 'select',
|
|
175
146
|
name: 'domainName',
|
|
@@ -177,19 +148,16 @@ export default async function generateTypesJson() {
|
|
|
177
148
|
choices: domains.map((item) => ({ title: item, value: item })),
|
|
178
149
|
initial: 0
|
|
179
150
|
});
|
|
180
|
-
|
|
181
151
|
if (!domainName) {
|
|
182
152
|
process.exitCode = 1;
|
|
183
153
|
return;
|
|
184
154
|
}
|
|
185
|
-
|
|
186
155
|
const models = await listModelFiles(contextName, String(domainName));
|
|
187
156
|
if (!models.length) {
|
|
188
157
|
console.error(`❌ No model files found in domain "${domainName}".`);
|
|
189
158
|
process.exitCode = 1;
|
|
190
159
|
return;
|
|
191
160
|
}
|
|
192
|
-
|
|
193
161
|
const { modelPath } = await prompts({
|
|
194
162
|
type: 'select',
|
|
195
163
|
name: 'modelPath',
|
|
@@ -197,12 +165,10 @@ export default async function generateTypesJson() {
|
|
|
197
165
|
choices: models.map((item) => ({ title: item.name, value: item.path })),
|
|
198
166
|
initial: 0
|
|
199
167
|
});
|
|
200
|
-
|
|
201
168
|
if (!modelPath) {
|
|
202
169
|
process.exitCode = 1;
|
|
203
170
|
return;
|
|
204
171
|
}
|
|
205
|
-
|
|
206
172
|
const { rawJson } = await prompts({
|
|
207
173
|
type: 'text',
|
|
208
174
|
name: 'rawJson',
|
|
@@ -216,12 +182,10 @@ export default async function generateTypesJson() {
|
|
|
216
182
|
}
|
|
217
183
|
}
|
|
218
184
|
});
|
|
219
|
-
|
|
220
185
|
if (!rawJson) {
|
|
221
186
|
process.exitCode = 1;
|
|
222
187
|
return;
|
|
223
188
|
}
|
|
224
|
-
|
|
225
189
|
const parsedJson = parseObjectInput(rawJson);
|
|
226
190
|
const content = await fs.readFile(String(modelPath), 'utf-8');
|
|
227
191
|
const interfaceDeclaration = findInterfaceDeclaration(content);
|
|
@@ -230,23 +194,18 @@ export default async function generateTypesJson() {
|
|
|
230
194
|
process.exitCode = 1;
|
|
231
195
|
return;
|
|
232
196
|
}
|
|
233
|
-
|
|
234
197
|
const existingKeys = extractExistingRootKeys(interfaceDeclaration.body);
|
|
235
198
|
const generatedLines = Object.entries(parsedJson)
|
|
236
199
|
.filter(([key]) => !existingKeys.has(key))
|
|
237
200
|
.map(([key, value]) => `${getIndent(1)}${toPropertyName(key)}: ${stringifyType(value, 1)};`);
|
|
238
|
-
|
|
239
201
|
if (!generatedLines.length) {
|
|
240
202
|
console.log('ℹ️ No new keys to add. Model already contains all JSON keys.');
|
|
241
203
|
return;
|
|
242
204
|
}
|
|
243
|
-
|
|
244
205
|
const trimmedBody = interfaceDeclaration.body.trimEnd();
|
|
245
206
|
const bodyPrefix = trimmedBody.length ? `${trimmedBody}\n` : '';
|
|
246
207
|
const newBody = `${bodyPrefix}${generatedLines.join('\n')}\n`;
|
|
247
|
-
|
|
248
208
|
const updatedContent = `${content.slice(0, interfaceDeclaration.openBraceIndex + 1)}${newBody}${content.slice(interfaceDeclaration.closeBraceIndex)}`;
|
|
249
209
|
await fs.writeFile(String(modelPath), updatedContent, 'utf-8');
|
|
250
|
-
|
|
251
210
|
console.log(`✅ Model updated: ${modelPath}`);
|
|
252
211
|
}
|
|
@@ -8,14 +8,12 @@ import {
|
|
|
8
8
|
resolveRepositoriesPath,
|
|
9
9
|
selectContext
|
|
10
10
|
} from './repositoryModuleShared.js';
|
|
11
|
-
|
|
12
11
|
export default async function generateUseCases() {
|
|
13
12
|
const contextName = await selectContext('Select context for UseCases:');
|
|
14
13
|
if (!contextName) {
|
|
15
14
|
process.exitCode = 1;
|
|
16
15
|
return;
|
|
17
16
|
}
|
|
18
|
-
|
|
19
17
|
const repositoriesPath = resolveRepositoriesPath(contextName);
|
|
20
18
|
const repositories = await getAvailableTsNames(repositoriesPath);
|
|
21
19
|
if (!repositories.length) {
|
|
@@ -23,7 +21,6 @@ export default async function generateUseCases() {
|
|
|
23
21
|
process.exitCode = 1;
|
|
24
22
|
return;
|
|
25
23
|
}
|
|
26
|
-
|
|
27
24
|
const { repositoryName } = await prompts({
|
|
28
25
|
type: 'select',
|
|
29
26
|
name: 'repositoryName',
|
|
@@ -31,12 +28,10 @@ export default async function generateUseCases() {
|
|
|
31
28
|
choices: repositories.map((item) => ({ title: item, value: item })),
|
|
32
29
|
initial: 0
|
|
33
30
|
});
|
|
34
|
-
|
|
35
31
|
if (!repositoryName) {
|
|
36
32
|
process.exitCode = 1;
|
|
37
33
|
return;
|
|
38
34
|
}
|
|
39
|
-
|
|
40
35
|
const { contexts } = await getConfigState();
|
|
41
36
|
const contextAlias = resolveContextAlias(contexts, contextName);
|
|
42
37
|
const domainName = await resolveDomainForRepository({
|
|
@@ -44,24 +39,20 @@ export default async function generateUseCases() {
|
|
|
44
39
|
contextAlias,
|
|
45
40
|
repositoryName: String(repositoryName)
|
|
46
41
|
});
|
|
47
|
-
|
|
48
42
|
if (!domainName) {
|
|
49
43
|
console.error('❌ Unable to resolve domain from repository imports.');
|
|
50
44
|
process.exitCode = 1;
|
|
51
45
|
return;
|
|
52
46
|
}
|
|
53
|
-
|
|
54
47
|
const created = await createUseCasesForRepository({
|
|
55
48
|
contextName,
|
|
56
49
|
contextAlias,
|
|
57
50
|
domainName,
|
|
58
51
|
repositoryName: String(repositoryName)
|
|
59
52
|
});
|
|
60
|
-
|
|
61
53
|
if (!created) {
|
|
62
54
|
process.exitCode = 1;
|
|
63
55
|
return;
|
|
64
56
|
}
|
|
65
|
-
|
|
66
57
|
console.log(`✅ UseCases generated for ${repositoryName}`);
|
|
67
58
|
}
|