@azure-net/cli 5.1.1 → 6.0.0-next.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/README.md +189 -0
- package/bin/azure-net.js +236 -221
- package/dist/bin/azure-net.d.ts +2 -0
- package/dist/src/checks/architecture.d.ts +20 -0
- package/dist/src/checks/checkDomain.d.ts +1 -0
- package/dist/src/checks/checkFoldersStructure.d.ts +1 -0
- package/dist/src/checks/checkHooks.d.ts +1 -0
- package/dist/src/checks/checkImports.d.ts +1 -0
- package/dist/src/checks/checkInternal.d.ts +1 -0
- package/dist/src/checks/checkLayerBoundaries.d.ts +1 -0
- package/dist/src/checks/checkLayerImports.d.ts +1 -0
- package/dist/src/checks/checkPresenterNames.d.ts +1 -0
- package/dist/src/checks/checkProject.d.ts +1 -0
- package/dist/src/checks/checkProviderGraph.d.ts +1 -0
- package/dist/src/checks/checkProviderNames.d.ts +1 -0
- package/dist/src/generate/layers/generateRepository.d.ts +1 -0
- package/dist/src/generate/layers/generateRepositoryMethod.d.ts +1 -0
- package/dist/src/generate/layers/generateUseCases.d.ts +1 -0
- package/dist/src/generate/module/generateDomain.d.ts +8 -0
- package/dist/src/generate/module/generateModule.d.ts +1 -0
- package/dist/src/generate/module/generateModulePreset.d.ts +1 -0
- package/dist/src/generate/module/generatePresenter.d.ts +29 -0
- package/dist/src/generate/module/generateRepository.d.ts +15 -0
- package/dist/src/generate/module/generateRepositoryMethod.d.ts +2 -0
- package/dist/src/generate/module/generateSchemaFromType.d.ts +9 -0
- package/dist/src/generate/module/generateStore.d.ts +1 -0
- package/dist/src/generate/module/generateTypesJson.d.ts +2 -0
- package/dist/src/generate/module/generateUseCases.d.ts +1 -0
- package/dist/src/generate/module/repositoryModuleShared.d.ts +89 -0
- package/dist/src/generate/routes/generateRoutes.d.ts +14 -0
- package/dist/src/generate/ui/generateComponent.d.ts +1 -0
- package/dist/src/generate/ui/generateDesignComponent.d.ts +1 -0
- package/dist/src/generate/ui/generateWidget.d.ts +1 -0
- package/dist/src/git/commit.d.ts +23 -0
- package/dist/src/git/push.d.ts +11 -0
- package/dist/src/git/runGit.d.ts +2 -0
- package/dist/src/init/initAliases.d.ts +12 -0
- package/dist/src/init/initEdges.d.ts +2 -0
- package/dist/src/init/initStructure.d.ts +1 -0
- package/dist/src/install/configureCommitTools.d.ts +1 -0
- package/dist/src/install/installFresh.d.ts +1 -0
- package/dist/src/migrate/migrate.d.ts +22 -0
- package/dist/src/migrate/presenters.d.ts +7 -0
- package/dist/src/plugins/addLint.d.ts +5 -0
- package/dist/src/scaffolds/createCoreSchema.d.ts +1 -0
- package/dist/src/scaffolds/createDatasource.d.ts +1 -0
- package/dist/src/scaffolds/createProgram.d.ts +1 -0
- package/dist/src/scaffolds/createResponse.d.ts +1 -0
- package/dist/src/scaffolds/createSchemaRule.d.ts +1 -0
- package/dist/src/scaffolds/createTranslationManager.d.ts +1 -0
- package/{src/scaffolds/index.ts → dist/src/scaffolds/index.d.ts} +0 -2
- package/dist/src/scaffolds/registerDatasource.d.ts +2 -0
- package/dist/src/scaffolds/updateDefaultFactoryConfig.d.ts +1 -0
- package/dist/src/utils/architecture.d.ts +31 -0
- package/dist/src/utils/contextUtils.d.ts +6 -0
- package/dist/src/utils/fileUtils.d.ts +5 -0
- package/dist/src/utils/flattenConfig.d.ts +2 -0
- package/dist/src/utils/legacyAliases.d.ts +1 -0
- package/dist/src/utils/loadConfig.d.ts +16 -0
- package/dist/src/utils/packageImports.d.ts +4 -0
- package/dist/src/utils/sharedFoundation.d.ts +20 -0
- package/dist/src/utils/viteConfig.d.ts +9 -0
- package/package.json +48 -36
- package/src/checks/architecture.js +154 -0
- package/src/checks/checkDomain.js +124 -154
- package/src/checks/checkFoldersStructure.js +64 -52
- package/src/checks/checkHooks.js +12 -12
- package/src/checks/checkImports.js +26 -142
- package/src/checks/checkInternal.js +27 -24
- package/src/checks/checkLayerBoundaries.js +22 -197
- package/src/checks/checkLayerImports.js +14 -134
- package/src/checks/checkPresenterNames.js +156 -143
- package/src/checks/checkProject.js +27 -26
- package/src/checks/checkProviderGraph.js +182 -166
- package/src/checks/checkProviderNames.js +123 -112
- package/src/generate/layers/generateRepository.js +1 -109
- package/src/generate/layers/generateRepositoryMethod.js +1 -90
- package/src/generate/layers/generateUseCases.js +1 -44
- package/src/generate/module/generateDomain.js +81 -105
- package/src/generate/module/generateModule.js +34 -68
- package/src/generate/module/generateModulePreset.js +188 -248
- package/src/generate/module/generatePresenter.js +441 -369
- package/src/generate/module/generateRepository.js +102 -121
- package/src/generate/module/generateRepositoryMethod.js +117 -0
- package/src/generate/module/generateSchemaFromType.js +283 -280
- package/src/generate/module/generateStore.js +41 -0
- package/src/generate/module/generateTypesJson.js +196 -195
- package/src/generate/module/generateUseCases.js +47 -55
- package/src/generate/module/repositoryModuleShared.js +480 -459
- package/src/generate/routes/generateRoutes.js +4 -7
- package/src/generate/ui/generateComponent.js +44 -64
- package/src/generate/ui/generateDesignComponent.js +21 -41
- package/src/generate/ui/generateWidget.js +21 -41
- package/src/git/commit.js +67 -0
- package/src/git/push.js +19 -0
- package/src/git/runGit.js +11 -0
- package/src/init/initAliases.js +19 -135
- package/src/init/initEdges.js +79 -54
- package/src/init/initStructure.js +31 -145
- package/src/install/configureCommitTools.js +1 -0
- package/src/install/installFresh.js +39 -68
- package/src/migrate/migrate.js +914 -0
- package/src/migrate/presenters.js +265 -0
- package/src/plugins/addLint.js +52 -219
- package/src/scaffolds/createCoreSchema.js +65 -91
- package/src/scaffolds/createDatasource.js +67 -101
- package/src/scaffolds/createProgram.js +8 -8
- package/src/scaffolds/createResponse.js +29 -29
- package/src/scaffolds/createSchemaRule.js +19 -19
- package/src/scaffolds/createTranslationManager.js +84 -37
- package/src/scaffolds/index.js +0 -2
- package/src/scaffolds/registerDatasource.js +59 -0
- package/src/scaffolds/updateDefaultFactoryConfig.js +31 -0
- package/src/utils/architecture.js +145 -0
- package/src/utils/contextUtils.js +32 -65
- package/src/utils/fileUtils.js +69 -50
- package/src/utils/flattenConfig.js +38 -0
- package/src/utils/legacyAliases.js +75 -0
- package/src/utils/loadConfig.js +76 -40
- package/src/utils/packageImports.js +63 -0
- package/src/utils/sharedFoundation.js +36 -42
- package/src/utils/viteConfig.js +295 -0
- package/.prettierrc +0 -7
- package/bin/azure-net.ts +0 -308
- package/eslint.config.js +0 -34
- package/src/checks/checkDomain.ts +0 -223
- package/src/checks/checkFoldersStructure.ts +0 -85
- package/src/checks/checkHooks.ts +0 -22
- package/src/checks/checkImports.ts +0 -193
- package/src/checks/checkInternal.ts +0 -48
- package/src/checks/checkLayerBoundaries.ts +0 -255
- package/src/checks/checkLayerImports.ts +0 -185
- package/src/checks/checkPresenterNames.ts +0 -203
- package/src/checks/checkProject.ts +0 -45
- package/src/checks/checkProviderGraph.ts +0 -234
- package/src/checks/checkProviderNames.ts +0 -165
- package/src/generate/layers/generateRepository.ts +0 -123
- package/src/generate/layers/generateRepositoryMethod.ts +0 -102
- package/src/generate/layers/generateUseCases.ts +0 -50
- package/src/generate/layers/repositoryShared.js +0 -506
- package/src/generate/layers/repositoryShared.ts +0 -662
- package/src/generate/module/generateDomain.ts +0 -147
- package/src/generate/module/generateModule.ts +0 -84
- package/src/generate/module/generateModulePreset.ts +0 -338
- package/src/generate/module/generatePresenter.ts +0 -498
- package/src/generate/module/generateRepository.ts +0 -158
- package/src/generate/module/generateSchemaFromType.ts +0 -368
- package/src/generate/module/generateTypesJson.ts +0 -259
- package/src/generate/module/generateUseCases.ts +0 -67
- package/src/generate/module/repositoryModuleShared.ts +0 -669
- package/src/generate/routes/generateRoutes.ts +0 -275
- package/src/generate/ui/generateComponent.ts +0 -100
- package/src/generate/ui/generateDesignComponent.ts +0 -71
- package/src/generate/ui/generateWidget.ts +0 -71
- package/src/init/initAliases.ts +0 -172
- package/src/init/initEdges.ts +0 -74
- package/src/init/initStructure.ts +0 -172
- package/src/install/installFresh.ts +0 -93
- package/src/plugins/addLint.ts +0 -274
- package/src/scaffolds/createCoreSchema.ts +0 -130
- package/src/scaffolds/createDatasource.ts +0 -161
- package/src/scaffolds/createDatasourceProvider.js +0 -127
- package/src/scaffolds/createDatasourceProvider.ts +0 -161
- package/src/scaffolds/createPresenterFactory.js +0 -83
- package/src/scaffolds/createPresenterFactory.ts +0 -102
- package/src/scaffolds/createProgram.ts +0 -19
- package/src/scaffolds/createResponse.ts +0 -66
- package/src/scaffolds/createSchemaRule.ts +0 -44
- package/src/scaffolds/createTranslationManager.ts +0 -80
- package/src/utils/contextUtils.ts +0 -88
- package/src/utils/fileUtils.ts +0 -74
- package/src/utils/loadConfig.ts +0 -68
- package/src/utils/sharedFoundation.ts +0 -70
- package/tsconfig.json +0 -19
- /package/{src/checks/index.ts → dist/src/checks/index.d.ts} +0 -0
- /package/{src/plugins/index.ts → dist/src/plugins/index.d.ts} +0 -0
|
@@ -1,506 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import prompts from 'prompts';
|
|
4
|
-
import { loadUserConfig } from '../../utils/loadConfig.js';
|
|
5
|
-
import { normalizeContexts } from '../../init/initAliases.js';
|
|
6
|
-
import { toCamelCase, toKebabCase, toPascalCase } from '../../utils/contextUtils.js';
|
|
7
|
-
import { updateIndexTs, writeIfNotExists } from '../../utils/fileUtils.js';
|
|
8
|
-
import { getFoundationConstructImportPath, getFoundationConstructPath, getSharedState } from '../../utils/sharedFoundation.js';
|
|
9
|
-
const toHttpMethod = (value) => {
|
|
10
|
-
const normalized = String(value ?? '').toLowerCase();
|
|
11
|
-
if (
|
|
12
|
-
normalized === 'get' ||
|
|
13
|
-
normalized === 'post' ||
|
|
14
|
-
normalized === 'put' ||
|
|
15
|
-
normalized === 'delete' ||
|
|
16
|
-
normalized === 'head' ||
|
|
17
|
-
normalized === 'options' ||
|
|
18
|
-
normalized === 'patch'
|
|
19
|
-
) {
|
|
20
|
-
return normalized;
|
|
21
|
-
}
|
|
22
|
-
return 'get';
|
|
23
|
-
};
|
|
24
|
-
const HTTP_METHODS = ['get', 'post', 'put', 'delete', 'head', 'options', 'patch'];
|
|
25
|
-
const normalizeAlias = (value, fallback) => {
|
|
26
|
-
const raw = String(value ?? '').trim();
|
|
27
|
-
if (!raw.length) return fallback;
|
|
28
|
-
return raw.startsWith('$') ? raw : `$${raw}`;
|
|
29
|
-
};
|
|
30
|
-
export const ensureRepositoryName = (rawName) => {
|
|
31
|
-
const normalized = toPascalCase(String(rawName ?? '').trim() || 'Repository');
|
|
32
|
-
return normalized.endsWith('Repository') ? normalized : `${normalized}Repository`;
|
|
33
|
-
};
|
|
34
|
-
const RESERVED_IDENTIFIERS = new Set([
|
|
35
|
-
'break',
|
|
36
|
-
'case',
|
|
37
|
-
'catch',
|
|
38
|
-
'class',
|
|
39
|
-
'const',
|
|
40
|
-
'continue',
|
|
41
|
-
'debugger',
|
|
42
|
-
'default',
|
|
43
|
-
'delete',
|
|
44
|
-
'do',
|
|
45
|
-
'else',
|
|
46
|
-
'enum',
|
|
47
|
-
'export',
|
|
48
|
-
'extends',
|
|
49
|
-
'false',
|
|
50
|
-
'finally',
|
|
51
|
-
'for',
|
|
52
|
-
'function',
|
|
53
|
-
'if',
|
|
54
|
-
'import',
|
|
55
|
-
'in',
|
|
56
|
-
'instanceof',
|
|
57
|
-
'new',
|
|
58
|
-
'null',
|
|
59
|
-
'return',
|
|
60
|
-
'super',
|
|
61
|
-
'switch',
|
|
62
|
-
'this',
|
|
63
|
-
'throw',
|
|
64
|
-
'true',
|
|
65
|
-
'try',
|
|
66
|
-
'typeof',
|
|
67
|
-
'var',
|
|
68
|
-
'void',
|
|
69
|
-
'while',
|
|
70
|
-
'with',
|
|
71
|
-
'as',
|
|
72
|
-
'implements',
|
|
73
|
-
'interface',
|
|
74
|
-
'let',
|
|
75
|
-
'package',
|
|
76
|
-
'private',
|
|
77
|
-
'protected',
|
|
78
|
-
'public',
|
|
79
|
-
'static',
|
|
80
|
-
'yield'
|
|
81
|
-
]);
|
|
82
|
-
const toSafeIdentifier = (value) => {
|
|
83
|
-
const normalized = toCamelCase(value || 'repository');
|
|
84
|
-
if (!normalized.length) return 'repository';
|
|
85
|
-
if (RESERVED_IDENTIFIERS.has(normalized)) return `${normalized}Repository`;
|
|
86
|
-
return normalized;
|
|
87
|
-
};
|
|
88
|
-
export const getRepositoryMeta = (repositoryClassName) => {
|
|
89
|
-
const className = ensureRepositoryName(repositoryClassName);
|
|
90
|
-
const baseName = className.replace(/Repository$/, '') || 'Repository';
|
|
91
|
-
const domainName = toKebabCase(baseName);
|
|
92
|
-
const repositoryInterfaceName = `I${baseName}Repository`;
|
|
93
|
-
const useCasesClassName = `${baseName}UseCases`;
|
|
94
|
-
const repositoryDependencyName = toSafeIdentifier(baseName || 'repository');
|
|
95
|
-
return {
|
|
96
|
-
className,
|
|
97
|
-
baseName,
|
|
98
|
-
domainName,
|
|
99
|
-
repositoryInterfaceName,
|
|
100
|
-
useCasesClassName,
|
|
101
|
-
repositoryDependencyName
|
|
102
|
-
};
|
|
103
|
-
};
|
|
104
|
-
export const getConfigState = async () => {
|
|
105
|
-
const config = await loadUserConfig();
|
|
106
|
-
const contexts = normalizeContexts(config.contexts);
|
|
107
|
-
const { sharedAlias, sharedContext } = await getSharedState();
|
|
108
|
-
return {
|
|
109
|
-
contexts,
|
|
110
|
-
sharedAlias,
|
|
111
|
-
sharedContextName: sharedContext.name
|
|
112
|
-
};
|
|
113
|
-
};
|
|
114
|
-
export const selectContext = async (message = 'Select context:') => {
|
|
115
|
-
const { contexts } = await getConfigState();
|
|
116
|
-
if (!contexts.length) {
|
|
117
|
-
console.error('❌ Cannot continue: `contexts` is missing or empty in azure-net.config.ts/js');
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
const { context } = await prompts({
|
|
121
|
-
type: 'select',
|
|
122
|
-
name: 'context',
|
|
123
|
-
message,
|
|
124
|
-
choices: contexts.map((item) => ({ title: item.name, value: item.name })),
|
|
125
|
-
initial: 0
|
|
126
|
-
});
|
|
127
|
-
return context ? String(context) : null;
|
|
128
|
-
};
|
|
129
|
-
export const resolveContextAlias = (contexts, contextName) => {
|
|
130
|
-
const found = contexts.find((item) => item.name === contextName);
|
|
131
|
-
if (!found) return `$${contextName}`;
|
|
132
|
-
return normalizeAlias(found.alias, `$${contextName}`);
|
|
133
|
-
};
|
|
134
|
-
export const resolveRepositoriesPath = (contextName) =>
|
|
135
|
-
path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'infrastructure', 'http', 'repositories');
|
|
136
|
-
export const resolveDatasourcesPath = (contextName) =>
|
|
137
|
-
path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'infrastructure', 'http', 'datasources');
|
|
138
|
-
export const resolveSharedDatasourcesPath = async () => {
|
|
139
|
-
const { sharedContext } = await getSharedState();
|
|
140
|
-
return getFoundationConstructPath(sharedContext.name, 'datasource');
|
|
141
|
-
};
|
|
142
|
-
export const resolveDomainRootPath = (contextName, domainName) =>
|
|
143
|
-
path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'domain', domainName);
|
|
144
|
-
export const resolveUseCasesPath = (contextName) =>
|
|
145
|
-
path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'application', 'use-cases');
|
|
146
|
-
export const getAvailableTsNames = async (targetPath) => {
|
|
147
|
-
try {
|
|
148
|
-
const files = await fs.readdir(targetPath);
|
|
149
|
-
return files.filter((item) => item.endsWith('.ts') && item !== 'index.ts').map((item) => item.replace(/\.ts$/, ''));
|
|
150
|
-
} catch {
|
|
151
|
-
return [];
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
export const selectDatasource = async (contextName) => {
|
|
155
|
-
const { contexts, sharedAlias } = await getConfigState();
|
|
156
|
-
const contextAlias = resolveContextAlias(contexts, contextName);
|
|
157
|
-
const sharedDatasources = await getAvailableTsNames(await resolveSharedDatasourcesPath());
|
|
158
|
-
const contextDatasources = await getAvailableTsNames(resolveDatasourcesPath(contextName));
|
|
159
|
-
const choices = [];
|
|
160
|
-
for (const datasource of sharedDatasources) {
|
|
161
|
-
choices.push({
|
|
162
|
-
title: `${datasource} (shared foundation)`,
|
|
163
|
-
value: {
|
|
164
|
-
name: datasource,
|
|
165
|
-
source: 'shared',
|
|
166
|
-
importPath: getFoundationConstructImportPath(sharedAlias, 'datasource')
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
for (const datasource of contextDatasources) {
|
|
171
|
-
choices.push({
|
|
172
|
-
title: `${datasource} (${contextName})`,
|
|
173
|
-
value: {
|
|
174
|
-
name: datasource,
|
|
175
|
-
source: 'context',
|
|
176
|
-
importPath: `${contextAlias}/layers/infrastructure/http/datasources`
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
if (!choices.length) {
|
|
181
|
-
console.error(`❌ No datasources found in shared foundation or context "${contextName}".`);
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
184
|
-
const { datasource } = await prompts({
|
|
185
|
-
type: 'select',
|
|
186
|
-
name: 'datasource',
|
|
187
|
-
message: 'Select datasource:',
|
|
188
|
-
choices,
|
|
189
|
-
initial: 0
|
|
190
|
-
});
|
|
191
|
-
return datasource ?? null;
|
|
192
|
-
};
|
|
193
|
-
const updateDomainIndex = async (domainRootPath) => {
|
|
194
|
-
const exports = [];
|
|
195
|
-
try {
|
|
196
|
-
await fs.access(path.join(domainRootPath, 'ports'));
|
|
197
|
-
exports.push(`export * from './ports';`);
|
|
198
|
-
} catch {
|
|
199
|
-
// ignore
|
|
200
|
-
}
|
|
201
|
-
try {
|
|
202
|
-
await fs.access(path.join(domainRootPath, 'model'));
|
|
203
|
-
exports.push(`export * from './model';`);
|
|
204
|
-
} catch {
|
|
205
|
-
// ignore
|
|
206
|
-
}
|
|
207
|
-
const content = exports.length ? `${exports.join('\n')}\n` : '';
|
|
208
|
-
await fs.mkdir(domainRootPath, { recursive: true });
|
|
209
|
-
await fs.writeFile(path.join(domainRootPath, 'index.ts'), content, 'utf-8');
|
|
210
|
-
};
|
|
211
|
-
const normalizeTypeName = (rawTypeName) => {
|
|
212
|
-
const normalized = toPascalCase(String(rawTypeName ?? '').trim());
|
|
213
|
-
return normalized || 'IType';
|
|
214
|
-
};
|
|
215
|
-
export const ensureDomainType = async ({ contextName, domainName, typeName, layer }) => {
|
|
216
|
-
const interfaceName = normalizeTypeName(typeName);
|
|
217
|
-
const domainRootPath = resolveDomainRootPath(contextName, domainName);
|
|
218
|
-
const preferredLayer = layer === 'model' ? 'model' : 'ports';
|
|
219
|
-
const portsTypePath = path.join(domainRootPath, 'ports', `${interfaceName}.ts`);
|
|
220
|
-
const modelTypePath = path.join(domainRootPath, 'model', `${interfaceName}.ts`);
|
|
221
|
-
try {
|
|
222
|
-
await fs.access(portsTypePath);
|
|
223
|
-
return { interfaceName, layer: 'ports' };
|
|
224
|
-
} catch {
|
|
225
|
-
// ignore
|
|
226
|
-
}
|
|
227
|
-
try {
|
|
228
|
-
await fs.access(modelTypePath);
|
|
229
|
-
return { interfaceName, layer: 'model' };
|
|
230
|
-
} catch {
|
|
231
|
-
// ignore
|
|
232
|
-
}
|
|
233
|
-
const layerPath = path.join(domainRootPath, preferredLayer);
|
|
234
|
-
const filePath = path.join(layerPath, `${interfaceName}.ts`);
|
|
235
|
-
await fs.mkdir(layerPath, { recursive: true });
|
|
236
|
-
await writeIfNotExists(filePath, `export interface ${interfaceName} {\n\t[key: string]: unknown;\n}\n`);
|
|
237
|
-
await updateIndexTs(layerPath);
|
|
238
|
-
await updateDomainIndex(domainRootPath);
|
|
239
|
-
return { interfaceName, layer: preferredLayer };
|
|
240
|
-
};
|
|
241
|
-
export const ensureRepositoryInterfaceFile = async ({ contextName, meta, methods }) => {
|
|
242
|
-
const domainRootPath = resolveDomainRootPath(contextName, meta.domainName);
|
|
243
|
-
const portsPath = path.join(domainRootPath, 'ports');
|
|
244
|
-
await fs.mkdir(portsPath, { recursive: true });
|
|
245
|
-
const resolveTypeLayer = async (typeName) => {
|
|
246
|
-
const portsTypePath = path.join(domainRootPath, 'ports', `${typeName}.ts`);
|
|
247
|
-
const modelTypePath = path.join(domainRootPath, 'model', `${typeName}.ts`);
|
|
248
|
-
try {
|
|
249
|
-
await fs.access(portsTypePath);
|
|
250
|
-
return 'ports';
|
|
251
|
-
} catch {
|
|
252
|
-
// ignore
|
|
253
|
-
}
|
|
254
|
-
try {
|
|
255
|
-
await fs.access(modelTypePath);
|
|
256
|
-
return 'model';
|
|
257
|
-
} catch {
|
|
258
|
-
// ignore
|
|
259
|
-
}
|
|
260
|
-
return null;
|
|
261
|
-
};
|
|
262
|
-
const importsByLayer = { ports: new Set(), model: new Set() };
|
|
263
|
-
for (const method of methods) {
|
|
264
|
-
const relatedTypes = [method.responseType, method.requestType].filter((item) => Boolean(item));
|
|
265
|
-
for (const typeName of relatedTypes) {
|
|
266
|
-
const typeLayer = await resolveTypeLayer(typeName);
|
|
267
|
-
if (typeLayer === 'ports') importsByLayer.ports.add(typeName);
|
|
268
|
-
if (typeLayer === 'model') importsByLayer.model.add(typeName);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
const importLines = [];
|
|
272
|
-
for (const typeName of Array.from(importsByLayer.ports)) {
|
|
273
|
-
importLines.push(`import type { ${typeName} } from './${typeName}.js';`);
|
|
274
|
-
}
|
|
275
|
-
for (const typeName of Array.from(importsByLayer.model)) {
|
|
276
|
-
importLines.push(`import type { ${typeName} } from '../model/${typeName}.js';`);
|
|
277
|
-
}
|
|
278
|
-
const signatures = methods
|
|
279
|
-
.map((method) => {
|
|
280
|
-
const arg = method.requestType ? `data: ${method.requestType}` : '';
|
|
281
|
-
return `\t${method.name}(${arg}): Promise<${method.responseType}>;`;
|
|
282
|
-
})
|
|
283
|
-
.join('\n');
|
|
284
|
-
const content = `${importLines.join('\n')}${importLines.length ? '\n\n' : ''}export interface ${meta.repositoryInterfaceName} {\n${signatures ? `${signatures}\n` : ''}}\n`;
|
|
285
|
-
await fs.writeFile(path.join(portsPath, `${meta.repositoryInterfaceName}.ts`), content, 'utf-8');
|
|
286
|
-
await updateIndexTs(portsPath);
|
|
287
|
-
await updateDomainIndex(domainRootPath);
|
|
288
|
-
};
|
|
289
|
-
const buildHttpCall = (method) => {
|
|
290
|
-
const httpMethod = method.httpMethod.toLowerCase();
|
|
291
|
-
const route = method.route || '/public-route';
|
|
292
|
-
if (!method.requestType) {
|
|
293
|
-
return `http.${httpMethod}('${route}')`;
|
|
294
|
-
}
|
|
295
|
-
if (httpMethod === 'get' || httpMethod === 'head' || httpMethod === 'options') {
|
|
296
|
-
return `http.${httpMethod}('${route}', { searchParams: query.toSearchParams(data) })`;
|
|
297
|
-
}
|
|
298
|
-
return `http.${httpMethod}('${route}', { json: data })`;
|
|
299
|
-
};
|
|
300
|
-
export const buildRepositoryMethodCode = (method) => {
|
|
301
|
-
const arg = method.requestType ? `data: ${method.requestType}` : '';
|
|
302
|
-
const call = buildHttpCall(method);
|
|
303
|
-
let getInCallback = `http`;
|
|
304
|
-
const httpMethod = String(method.httpMethod ?? '').toLowerCase();
|
|
305
|
-
if (httpMethod === 'get' || httpMethod === 'head' || httpMethod === 'options') {
|
|
306
|
-
getInCallback = `http, query`;
|
|
307
|
-
}
|
|
308
|
-
return `\tpublic async ${method.name}(${arg}) {\n\t\treturn this.datasource\n\t\t\t.createRequest<${method.responseType}>(({ ${getInCallback} }) => ${call})\n\t\t\t.then((res) => res.get());\n\t}`;
|
|
309
|
-
};
|
|
310
|
-
export const buildRepositoryContent = ({ meta, contextAlias, datasourceImportPath, datasourceName, methods }) => {
|
|
311
|
-
const importedTypes = new Set([meta.repositoryInterfaceName]);
|
|
312
|
-
for (const method of methods) {
|
|
313
|
-
importedTypes.add(method.responseType);
|
|
314
|
-
if (method.requestType) importedTypes.add(method.requestType);
|
|
315
|
-
}
|
|
316
|
-
const methodsCode = methods.map((method) => buildRepositoryMethodCode(method)).join('\n\n');
|
|
317
|
-
return `import { ${datasourceName} } from '${datasourceImportPath}';
|
|
318
|
-
import type { ${Array.from(importedTypes).join(', ')} } from '${contextAlias}/layers/domain/${meta.domainName}';
|
|
319
|
-
|
|
320
|
-
export class ${meta.className} implements ${meta.repositoryInterfaceName} {
|
|
321
|
-
\tconstructor(private readonly datasource: ${datasourceName}) {}
|
|
322
|
-
|
|
323
|
-
${methodsCode ? `${methodsCode}\n` : ''}
|
|
324
|
-
}
|
|
325
|
-
`;
|
|
326
|
-
};
|
|
327
|
-
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
328
|
-
const upsertImport = (content, source, names) => {
|
|
329
|
-
const uniqueNames = Array.from(new Set(names)).filter((item) => Boolean(item));
|
|
330
|
-
if (!uniqueNames.length) return content;
|
|
331
|
-
const importRegex = new RegExp(`import\\s+(type\\s+)?\\{([^}]+)\\}\\s*from\\s*['"]${escapeRegExp(source)}['"];?`);
|
|
332
|
-
const match = content.match(importRegex);
|
|
333
|
-
if (match) {
|
|
334
|
-
const existing = (match[2] ?? '')
|
|
335
|
-
.split(',')
|
|
336
|
-
.map((part) => part.trim())
|
|
337
|
-
.filter(Boolean);
|
|
338
|
-
const merged = Array.from(new Set([...existing, ...uniqueNames]));
|
|
339
|
-
return content.replace(importRegex, `import type { ${merged.join(', ')} } from '${source}';`);
|
|
340
|
-
}
|
|
341
|
-
const lines = content.split('\n');
|
|
342
|
-
let insertIndex = 0;
|
|
343
|
-
while (insertIndex < lines.length && (lines[insertIndex] ?? '').startsWith('import ')) {
|
|
344
|
-
insertIndex += 1;
|
|
345
|
-
}
|
|
346
|
-
lines.splice(insertIndex, 0, `import type { ${uniqueNames.join(', ')} } from '${source}';`);
|
|
347
|
-
return lines.join('\n');
|
|
348
|
-
};
|
|
349
|
-
export const appendRepositoryMethod = async ({ repositoryPath, method, contextAlias, domainName }) => {
|
|
350
|
-
let content = await fs.readFile(repositoryPath, 'utf-8');
|
|
351
|
-
const methodExistsRegex = new RegExp(`\\bpublic\\s+async\\s+${escapeRegExp(method.name)}\\s*\\(`);
|
|
352
|
-
if (methodExistsRegex.test(content)) {
|
|
353
|
-
console.warn(`⚠️ Method "${method.name}" already exists in repository, skipped.`);
|
|
354
|
-
return false;
|
|
355
|
-
}
|
|
356
|
-
content = upsertImport(content, `${contextAlias}/layers/domain/${domainName}`, [method.responseType, method.requestType]);
|
|
357
|
-
const methodCode = buildRepositoryMethodCode(method);
|
|
358
|
-
const classEnd = content.lastIndexOf('\n}');
|
|
359
|
-
if (classEnd === -1) {
|
|
360
|
-
throw new Error(`Repository class end was not found in ${repositoryPath}`);
|
|
361
|
-
}
|
|
362
|
-
content = `${content.slice(0, classEnd)}\n\n${methodCode}\n${content.slice(classEnd)}`;
|
|
363
|
-
await fs.writeFile(repositoryPath, content, 'utf-8');
|
|
364
|
-
return true;
|
|
365
|
-
};
|
|
366
|
-
export const parseRepositoryMethods = (content) => {
|
|
367
|
-
const methods = [];
|
|
368
|
-
const regex =
|
|
369
|
-
/public\s+async\s+(\w+)\(([^)]*)\)\s*\{[\s\S]*?\.createRequest<([^>]+)>\(\(\{ http \}\) =>\s*http\.(get|post|put|delete|head|options|patch)\(/g;
|
|
370
|
-
let match;
|
|
371
|
-
while ((match = regex.exec(content)) !== null) {
|
|
372
|
-
const name = match[1];
|
|
373
|
-
const params = match[2] ?? '';
|
|
374
|
-
const responseType = (match[3] ?? '').trim();
|
|
375
|
-
const httpMethod = toHttpMethod(match[4]);
|
|
376
|
-
const requestTypeMatch = params.match(/:\s*([A-Za-z0-9_]+)/);
|
|
377
|
-
const requestType = requestTypeMatch?.[1] ?? null;
|
|
378
|
-
methods.push({
|
|
379
|
-
name: name ?? 'method',
|
|
380
|
-
httpMethod,
|
|
381
|
-
responseType,
|
|
382
|
-
responseLayer: 'ports',
|
|
383
|
-
requestType,
|
|
384
|
-
requestLayer: requestType ? 'ports' : null,
|
|
385
|
-
route: '/public-route'
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
return methods;
|
|
389
|
-
};
|
|
390
|
-
export const ensureUseCasesFile = async ({ contextName, contextAlias, meta }) => {
|
|
391
|
-
const repositoryInterfacePath = path.join(
|
|
392
|
-
resolveDomainRootPath(contextName, meta.domainName),
|
|
393
|
-
'ports',
|
|
394
|
-
`${meta.repositoryInterfaceName}.ts`
|
|
395
|
-
);
|
|
396
|
-
let interfaceContent = '';
|
|
397
|
-
try {
|
|
398
|
-
interfaceContent = await fs.readFile(repositoryInterfacePath, 'utf-8');
|
|
399
|
-
} catch {
|
|
400
|
-
console.error(`❌ Repository interface not found: ${repositoryInterfacePath}`);
|
|
401
|
-
return false;
|
|
402
|
-
}
|
|
403
|
-
const methods = [];
|
|
404
|
-
const methodRegex = /(\w+)\(([^)]*)\)\s*:\s*Promise<[^>]+>;/g;
|
|
405
|
-
let methodMatch;
|
|
406
|
-
while ((methodMatch = methodRegex.exec(interfaceContent)) !== null) {
|
|
407
|
-
const methodName = methodMatch[1];
|
|
408
|
-
if (methodName) methods.push(methodName);
|
|
409
|
-
}
|
|
410
|
-
const declares = methods.map((methodName) => `\tdeclare ${methodName}: ${meta.repositoryInterfaceName}['${methodName}'];`).join('\n');
|
|
411
|
-
const useCasesPath = resolveUseCasesPath(contextName);
|
|
412
|
-
await fs.mkdir(useCasesPath, { recursive: true });
|
|
413
|
-
const content = `import { ClassMirror } from '@azure-net/kit';
|
|
414
|
-
import type { ${meta.repositoryInterfaceName} } from '${contextAlias}/layers/domain/${meta.domainName}';
|
|
415
|
-
|
|
416
|
-
export class ${meta.useCasesClassName} extends ClassMirror {
|
|
417
|
-
\tconstructor(private ${meta.repositoryDependencyName}: ${meta.repositoryInterfaceName}) {
|
|
418
|
-
\t\tsuper(${meta.repositoryDependencyName});
|
|
419
|
-
\t}
|
|
420
|
-
|
|
421
|
-
${declares ? `${declares}\n` : ''}
|
|
422
|
-
}
|
|
423
|
-
`;
|
|
424
|
-
await fs.writeFile(path.join(useCasesPath, `${meta.useCasesClassName}.ts`), content, 'utf-8');
|
|
425
|
-
await updateIndexTs(useCasesPath);
|
|
426
|
-
return true;
|
|
427
|
-
};
|
|
428
|
-
export const promptMethodDefinition = async () => {
|
|
429
|
-
const { methodNameRaw } = await prompts({
|
|
430
|
-
type: 'text',
|
|
431
|
-
name: 'methodNameRaw',
|
|
432
|
-
message: 'Method name:',
|
|
433
|
-
validate: (value) => (String(value ?? '').trim().length > 0 ? true : 'Method name is required')
|
|
434
|
-
});
|
|
435
|
-
const { httpMethod } = await prompts({
|
|
436
|
-
type: 'select',
|
|
437
|
-
name: 'httpMethod',
|
|
438
|
-
message: 'HTTP method:',
|
|
439
|
-
choices: HTTP_METHODS.map((value) => ({ title: value, value })),
|
|
440
|
-
initial: 0
|
|
441
|
-
});
|
|
442
|
-
const { responseTypeRaw } = await prompts({
|
|
443
|
-
type: 'text',
|
|
444
|
-
name: 'responseTypeRaw',
|
|
445
|
-
message: 'Response type name:',
|
|
446
|
-
validate: (value) => (String(value ?? '').trim().length > 0 ? true : 'Response type is required')
|
|
447
|
-
});
|
|
448
|
-
const { responseLayer } = await prompts({
|
|
449
|
-
type: 'select',
|
|
450
|
-
name: 'responseLayer',
|
|
451
|
-
message: 'Where to place response type?',
|
|
452
|
-
choices: [
|
|
453
|
-
{ title: 'ports', value: 'ports' },
|
|
454
|
-
{ title: 'model', value: 'model' }
|
|
455
|
-
],
|
|
456
|
-
initial: 0
|
|
457
|
-
});
|
|
458
|
-
const { routeRaw } = await prompts({
|
|
459
|
-
type: 'text',
|
|
460
|
-
name: 'routeRaw',
|
|
461
|
-
message: 'Request path (e.g. /public-route):',
|
|
462
|
-
initial: '/public-route',
|
|
463
|
-
validate: (value) => {
|
|
464
|
-
const raw = String(value ?? '').trim();
|
|
465
|
-
if (!raw.length) return 'Path is required';
|
|
466
|
-
return raw.startsWith('/') ? true : 'Path must start with "/"';
|
|
467
|
-
}
|
|
468
|
-
});
|
|
469
|
-
const { hasRequest } = await prompts({
|
|
470
|
-
type: 'confirm',
|
|
471
|
-
name: 'hasRequest',
|
|
472
|
-
message: 'Has request payload type?',
|
|
473
|
-
initial: false
|
|
474
|
-
});
|
|
475
|
-
let requestType = null;
|
|
476
|
-
let requestLayer = null;
|
|
477
|
-
if (hasRequest) {
|
|
478
|
-
const { requestTypeRaw } = await prompts({
|
|
479
|
-
type: 'text',
|
|
480
|
-
name: 'requestTypeRaw',
|
|
481
|
-
message: 'Request type name:',
|
|
482
|
-
validate: (value) => (String(value ?? '').trim().length > 0 ? true : 'Request type is required')
|
|
483
|
-
});
|
|
484
|
-
const layerResult = await prompts({
|
|
485
|
-
type: 'select',
|
|
486
|
-
name: 'requestLayer',
|
|
487
|
-
message: 'Where to place request type?',
|
|
488
|
-
choices: [
|
|
489
|
-
{ title: 'ports', value: 'ports' },
|
|
490
|
-
{ title: 'model', value: 'model' }
|
|
491
|
-
],
|
|
492
|
-
initial: 0
|
|
493
|
-
});
|
|
494
|
-
requestType = normalizeTypeName(requestTypeRaw);
|
|
495
|
-
requestLayer = layerResult.requestLayer ?? 'ports';
|
|
496
|
-
}
|
|
497
|
-
return {
|
|
498
|
-
name: toCamelCase(String(methodNameRaw ?? '').trim() || 'method'),
|
|
499
|
-
httpMethod: toHttpMethod(httpMethod),
|
|
500
|
-
responseType: normalizeTypeName(responseTypeRaw),
|
|
501
|
-
responseLayer: responseLayer ?? 'ports',
|
|
502
|
-
route: String(routeRaw ?? '/public-route').trim() || '/public-route',
|
|
503
|
-
requestType,
|
|
504
|
-
requestLayer
|
|
505
|
-
};
|
|
506
|
-
};
|