@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
|
@@ -3,11 +3,12 @@ import path from 'node:path';
|
|
|
3
3
|
import prompts from 'prompts';
|
|
4
4
|
import { writeIfNotExists, updateIndexTs } from '../utils/fileUtils.js';
|
|
5
5
|
import { toPascalCase } from '../utils/contextUtils.js';
|
|
6
|
-
import { loadUserConfig
|
|
6
|
+
import { loadUserConfig } from '../utils/loadConfig.js';
|
|
7
7
|
import { normalizeContexts } from '../init/initAliases.js';
|
|
8
|
+
import { getFoundationConstructImportPath, getFoundationConstructPath, getSharedState } from '../utils/sharedFoundation.js';
|
|
8
9
|
|
|
9
10
|
type NormalizedContext = { name: string; alias: string };
|
|
10
|
-
type DatasourceChoiceValue = null | { name: string; source: '
|
|
11
|
+
type DatasourceChoiceValue = null | { name: string; source: 'shared' | 'context' };
|
|
11
12
|
|
|
12
13
|
const createProviderTemplateWithoutDatasource = (
|
|
13
14
|
providerName: string,
|
|
@@ -45,26 +46,6 @@ export const ${providerName} = createBoundaryProvider('${contextPrefix}${provide
|
|
|
45
46
|
`;
|
|
46
47
|
};
|
|
47
48
|
|
|
48
|
-
const getCoreAliasOrThrow = async (): Promise<string> => {
|
|
49
|
-
const configRef = resolveConfigFile();
|
|
50
|
-
if (!configRef.exists) {
|
|
51
|
-
throw new Error(
|
|
52
|
-
'azure-net.config is not configured. Run "azure-net init folders-structure" and do not forget to fill contexts if you need them.'
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const content = await fs.readFile(configRef.filepath, 'utf-8');
|
|
57
|
-
const aliasMatch = content.match(/coreAlias\s*:\s*['"`]([^'"`]+)['"`]/);
|
|
58
|
-
const alias = aliasMatch?.[1]?.trim();
|
|
59
|
-
if (!alias) {
|
|
60
|
-
throw new Error(
|
|
61
|
-
'azure-net.config is not configured. Run "azure-net init folders-structure" and do not forget to fill contexts if you need them.'
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return alias.startsWith('$') ? alias : `$${alias}`;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
49
|
const getDatasourcesFromPath = async (targetPath: string): Promise<string[]> => {
|
|
69
50
|
try {
|
|
70
51
|
const files = await fs.readdir(targetPath);
|
|
@@ -74,11 +55,11 @@ const getDatasourcesFromPath = async (targetPath: string): Promise<string[]> =>
|
|
|
74
55
|
}
|
|
75
56
|
};
|
|
76
57
|
|
|
77
|
-
const resolveContextPrefix = (target: string): string => (target === '
|
|
58
|
+
const resolveContextPrefix = (target: string): string => (target === '__shared__' ? 'Shared' : toPascalCase(target));
|
|
78
59
|
|
|
79
60
|
const resolveProviderPath = (target: string): string => {
|
|
80
|
-
if (target === '
|
|
81
|
-
|
|
61
|
+
if (target === '__shared__') {
|
|
62
|
+
throw new Error('Shared provider path must be resolved async.');
|
|
82
63
|
}
|
|
83
64
|
|
|
84
65
|
return path.join(process.cwd(), 'src', 'app', target, 'layers', 'infrastructure', 'providers');
|
|
@@ -109,7 +90,10 @@ export default async function createDatasourceProvider(): Promise<void> {
|
|
|
109
90
|
const providerName = ensureProviderName(String(providerNameRaw ?? 'DatasourceProvider'));
|
|
110
91
|
const config = await loadUserConfig();
|
|
111
92
|
const contexts = normalizeContexts(config.contexts) as NormalizedContext[];
|
|
112
|
-
const contextChoices = [
|
|
93
|
+
const contextChoices = [
|
|
94
|
+
{ title: 'shared foundation', value: '__shared__' },
|
|
95
|
+
...contexts.map((context) => ({ title: context.name, value: context.name }))
|
|
96
|
+
];
|
|
113
97
|
|
|
114
98
|
const { target } = await prompts({
|
|
115
99
|
type: 'select',
|
|
@@ -119,18 +103,19 @@ export default async function createDatasourceProvider(): Promise<void> {
|
|
|
119
103
|
initial: 0
|
|
120
104
|
});
|
|
121
105
|
|
|
122
|
-
const selectedTarget = String(target ?? '
|
|
123
|
-
const
|
|
106
|
+
const selectedTarget = String(target ?? '__shared__');
|
|
107
|
+
const { sharedAlias, sharedContext } = await getSharedState();
|
|
108
|
+
const sharedDatasources = await getDatasourcesFromPath(getFoundationConstructPath(sharedContext.name, 'datasource'));
|
|
124
109
|
const contextDatasources =
|
|
125
|
-
selectedTarget === '
|
|
110
|
+
selectedTarget === '__shared__'
|
|
126
111
|
? []
|
|
127
112
|
: await getDatasourcesFromPath(
|
|
128
113
|
path.join(process.cwd(), 'src', 'app', selectedTarget, 'layers', 'infrastructure', 'http', 'datasources')
|
|
129
114
|
);
|
|
130
115
|
|
|
131
116
|
const datasourceChoices: Array<{ title: string; value: DatasourceChoiceValue }> = [{ title: 'No datasource', value: null }];
|
|
132
|
-
for (const datasource of
|
|
133
|
-
datasourceChoices.push({ title: `${datasource} (
|
|
117
|
+
for (const datasource of sharedDatasources) {
|
|
118
|
+
datasourceChoices.push({ title: `${datasource} (shared foundation)`, value: { name: datasource, source: 'shared' } });
|
|
134
119
|
}
|
|
135
120
|
for (const datasource of contextDatasources) {
|
|
136
121
|
datasourceChoices.push({ title: `${datasource} (${selectedTarget})`, value: { name: datasource, source: 'context' } });
|
|
@@ -152,14 +137,15 @@ export default async function createDatasourceProvider(): Promise<void> {
|
|
|
152
137
|
content = createProviderTemplateWithoutDatasource(providerName, contextPrefix);
|
|
153
138
|
} else {
|
|
154
139
|
const importPath =
|
|
155
|
-
selectedDatasource.source === '
|
|
156
|
-
?
|
|
140
|
+
selectedDatasource.source === 'shared'
|
|
141
|
+
? getFoundationConstructImportPath(sharedAlias, 'datasource')
|
|
157
142
|
: `${resolveContextAlias(contexts, selectedTarget)}/layers/infrastructure/http/datasources`;
|
|
158
143
|
|
|
159
144
|
content = createProviderTemplateWithDatasource(providerName, contextPrefix, selectedDatasource.name, importPath);
|
|
160
145
|
}
|
|
161
146
|
|
|
162
|
-
const providerPath =
|
|
147
|
+
const providerPath =
|
|
148
|
+
selectedTarget === '__shared__' ? getFoundationConstructPath(sharedContext.name, 'provider') : resolveProviderPath(selectedTarget);
|
|
163
149
|
await fs.mkdir(providerPath, { recursive: true });
|
|
164
150
|
|
|
165
151
|
const filePath = path.join(providerPath, `${providerName}.ts`);
|
|
@@ -4,7 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import prompts from 'prompts';
|
|
5
5
|
import { writeIfNotExists, updateIndexTs } from '../utils/fileUtils.js';
|
|
6
6
|
import { toPascalCase } from '../utils/contextUtils.js';
|
|
7
|
-
|
|
7
|
+
import { getFoundationConstructPath, getSharedState } from '../utils/sharedFoundation.js';
|
|
8
8
|
const createPresenterFactoryTemplate = (
|
|
9
9
|
factoryName
|
|
10
10
|
) => `import { createErrorHandler, createAsyncHelpers, createPresenterFactory } from '@azure-net/kit/delivery';
|
|
@@ -13,36 +13,27 @@ export const AsyncHelpers = createAsyncHelpers({ handler: createErrorHandler() }
|
|
|
13
13
|
|
|
14
14
|
export const ${factoryName} = createPresenterFactory({ ...AsyncHelpers });
|
|
15
15
|
`;
|
|
16
|
-
|
|
17
16
|
const resolveConfigFile = (cwd) => {
|
|
18
17
|
const tsPath = path.join(cwd, 'azure-net.config.ts');
|
|
19
18
|
const jsPath = path.join(cwd, 'azure-net.config.js');
|
|
20
|
-
|
|
21
19
|
if (fsSync.existsSync(tsPath)) {
|
|
22
20
|
return { filepath: tsPath, type: 'ts', exists: true, content: fsSync.readFileSync(tsPath, 'utf-8') };
|
|
23
21
|
}
|
|
24
22
|
if (fsSync.existsSync(jsPath)) {
|
|
25
23
|
return { filepath: jsPath, type: 'js', exists: true, content: fsSync.readFileSync(jsPath, 'utf-8') };
|
|
26
24
|
}
|
|
27
|
-
|
|
28
25
|
return { filepath: tsPath, type: 'ts', exists: false, content: '' };
|
|
29
26
|
};
|
|
30
|
-
|
|
31
27
|
const buildDefaultPresenterFactoryConfigEntry = (factoryName) => {
|
|
32
28
|
return `defaultPresenterFactory: '${factoryName}'`;
|
|
33
29
|
};
|
|
34
|
-
|
|
35
30
|
const upsertDefaultPresenterFactoryConfig = (content, factoryName) => {
|
|
36
31
|
const hasExportDefaultObject = /export\s+default\s*\{[\s\S]*\}\s*;?/.test(content);
|
|
37
32
|
const defaultFactoryEntry = buildDefaultPresenterFactoryConfigEntry(factoryName);
|
|
38
|
-
const coreAliasEntry = `coreAlias: '$core'`;
|
|
39
|
-
|
|
40
33
|
if (!hasExportDefaultObject) {
|
|
41
|
-
return `export default {\n\t${
|
|
34
|
+
return `export default {\n\t${defaultFactoryEntry}\n};\n`;
|
|
42
35
|
}
|
|
43
|
-
|
|
44
36
|
let nextContent = content;
|
|
45
|
-
|
|
46
37
|
if (/defaultPresenterFactory\s*:/.test(nextContent)) {
|
|
47
38
|
nextContent = nextContent.replace(/defaultPresenterFactory\s*:\s*(\{[\s\S]*?\n\s*\}|['"`][^'"`]+['"`])/m, defaultFactoryEntry);
|
|
48
39
|
} else if (/defaultPresenter\s*:/.test(nextContent)) {
|
|
@@ -53,27 +44,12 @@ const upsertDefaultPresenterFactoryConfig = (content, factoryName) => {
|
|
|
53
44
|
if (!trimmedRight.length) {
|
|
54
45
|
return `export default {\n\t${defaultFactoryEntry}\n};`;
|
|
55
46
|
}
|
|
56
|
-
|
|
57
47
|
const withComma = trimmedRight.endsWith(',') ? trimmedRight : `${trimmedRight},`;
|
|
58
48
|
return `export default {${withComma}\n\t${defaultFactoryEntry}\n};`;
|
|
59
49
|
});
|
|
60
50
|
}
|
|
61
|
-
|
|
62
|
-
if (/coreAlias\s*:/.test(nextContent)) {
|
|
63
|
-
return nextContent.replace(/coreAlias\s*:\s*['"`][^'"`]+['"`]/, coreAliasEntry);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return nextContent.replace(/export\s+default\s*\{([\s\S]*)\}\s*;?/, (_full, body) => {
|
|
67
|
-
const trimmedRight = body.replace(/\s*$/, '');
|
|
68
|
-
if (!trimmedRight.length) {
|
|
69
|
-
return `export default {\n\t${coreAliasEntry}\n};`;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const withComma = trimmedRight.endsWith(',') ? trimmedRight : `${trimmedRight},`;
|
|
73
|
-
return `export default {${withComma}\n\t${coreAliasEntry}\n};`;
|
|
74
|
-
});
|
|
51
|
+
return nextContent;
|
|
75
52
|
};
|
|
76
|
-
|
|
77
53
|
export default async function createPresenterFactory() {
|
|
78
54
|
const { presenterFactoryNameRaw } = await prompts({
|
|
79
55
|
type: 'text',
|
|
@@ -81,13 +57,11 @@ export default async function createPresenterFactory() {
|
|
|
81
57
|
message: 'Presenter factory name:',
|
|
82
58
|
initial: 'AppPresenter'
|
|
83
59
|
});
|
|
84
|
-
|
|
85
60
|
const presenterFactoryName = toPascalCase(String(presenterFactoryNameRaw ?? 'AppPresenter')) || 'AppPresenter';
|
|
86
|
-
const
|
|
61
|
+
const { sharedContext } = await getSharedState();
|
|
62
|
+
const presenterRootPath = getFoundationConstructPath(sharedContext.name, 'presenter');
|
|
87
63
|
const filePath = path.join(presenterRootPath, `${presenterFactoryName}.ts`);
|
|
88
|
-
|
|
89
64
|
await fs.mkdir(presenterRootPath, { recursive: true });
|
|
90
|
-
|
|
91
65
|
const created = await writeIfNotExists(filePath, createPresenterFactoryTemplate(presenterFactoryName));
|
|
92
66
|
if (!created) {
|
|
93
67
|
console.log(`⚠️ Presenter factory "${presenterFactoryName}" already exists. File was not overwritten.`);
|
|
@@ -95,16 +69,13 @@ export default async function createPresenterFactory() {
|
|
|
95
69
|
await updateIndexTs(presenterRootPath);
|
|
96
70
|
console.log(`✅ Presenter factory created: ${filePath}`);
|
|
97
71
|
}
|
|
98
|
-
|
|
99
72
|
const { useAsDefault } = await prompts({
|
|
100
73
|
type: 'confirm',
|
|
101
74
|
name: 'useAsDefault',
|
|
102
75
|
message: 'Use this presenter factory as default for the project?',
|
|
103
76
|
initial: true
|
|
104
77
|
});
|
|
105
|
-
|
|
106
78
|
if (!useAsDefault) return;
|
|
107
|
-
|
|
108
79
|
const config = resolveConfigFile(process.cwd());
|
|
109
80
|
const nextContent = upsertDefaultPresenterFactoryConfig(config.content, presenterFactoryName);
|
|
110
81
|
await fs.writeFile(config.filepath, nextContent, 'utf-8');
|
|
@@ -4,6 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import prompts from 'prompts';
|
|
5
5
|
import { writeIfNotExists, updateIndexTs } from '../utils/fileUtils.js';
|
|
6
6
|
import { toPascalCase } from '../utils/contextUtils.js';
|
|
7
|
+
import { getFoundationConstructPath, getSharedState } from '../utils/sharedFoundation.js';
|
|
7
8
|
|
|
8
9
|
type ConfigPath = 'ts' | 'js';
|
|
9
10
|
|
|
@@ -37,10 +38,9 @@ const buildDefaultPresenterFactoryConfigEntry = (factoryName: string): string =>
|
|
|
37
38
|
const upsertDefaultPresenterFactoryConfig = (content: string, factoryName: string): string => {
|
|
38
39
|
const hasExportDefaultObject = /export\s+default\s*\{[\s\S]*\}\s*;?/.test(content);
|
|
39
40
|
const defaultFactoryEntry = buildDefaultPresenterFactoryConfigEntry(factoryName);
|
|
40
|
-
const coreAliasEntry = `coreAlias: '$core'`;
|
|
41
41
|
|
|
42
42
|
if (!hasExportDefaultObject) {
|
|
43
|
-
return `export default {\n\t${
|
|
43
|
+
return `export default {\n\t${defaultFactoryEntry}\n};\n`;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
let nextContent = content;
|
|
@@ -60,20 +60,7 @@ const upsertDefaultPresenterFactoryConfig = (content: string, factoryName: strin
|
|
|
60
60
|
return `export default {${withComma}\n\t${defaultFactoryEntry}\n};`;
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
|
-
|
|
64
|
-
if (/coreAlias\s*:/.test(nextContent)) {
|
|
65
|
-
return nextContent.replace(/coreAlias\s*:\s*['"`][^'"`]+['"`]/, coreAliasEntry);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return nextContent.replace(/export\s+default\s*\{([\s\S]*)\}\s*;?/, (_full, body: string) => {
|
|
69
|
-
const trimmedRight = body.replace(/\s*$/, '');
|
|
70
|
-
if (!trimmedRight.length) {
|
|
71
|
-
return `export default {\n\t${coreAliasEntry}\n};`;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const withComma = trimmedRight.endsWith(',') ? trimmedRight : `${trimmedRight},`;
|
|
75
|
-
return `export default {${withComma}\n\t${coreAliasEntry}\n};`;
|
|
76
|
-
});
|
|
63
|
+
return nextContent;
|
|
77
64
|
};
|
|
78
65
|
|
|
79
66
|
export default async function createPresenterFactory(): Promise<void> {
|
|
@@ -85,7 +72,8 @@ export default async function createPresenterFactory(): Promise<void> {
|
|
|
85
72
|
});
|
|
86
73
|
|
|
87
74
|
const presenterFactoryName = toPascalCase(String(presenterFactoryNameRaw ?? 'AppPresenter')) || 'AppPresenter';
|
|
88
|
-
const
|
|
75
|
+
const { sharedContext } = await getSharedState();
|
|
76
|
+
const presenterRootPath = getFoundationConstructPath(sharedContext.name, 'presenter');
|
|
89
77
|
const filePath = path.join(presenterRootPath, `${presenterFactoryName}.ts`);
|
|
90
78
|
|
|
91
79
|
await fs.mkdir(presenterRootPath, { recursive: true });
|
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { writeIfNotExists } from '../utils/fileUtils.js';
|
|
3
|
-
|
|
4
3
|
const createProgramTemplate = () => `import { createApp } from '@azure-net/kit';
|
|
5
4
|
|
|
6
|
-
export const { register,
|
|
5
|
+
export const { register, Container } = createApp((app) => app);
|
|
7
6
|
`;
|
|
8
|
-
|
|
9
7
|
export default async function createProgram() {
|
|
10
8
|
const programPath = path.join(process.cwd(), 'src', 'program.ts');
|
|
11
9
|
const created = await writeIfNotExists(programPath, createProgramTemplate());
|
|
12
|
-
|
|
13
10
|
if (!created) {
|
|
14
11
|
console.log('⚠️ Program already exists. Nothing was overwritten.');
|
|
15
12
|
return;
|
|
16
13
|
}
|
|
17
|
-
|
|
18
14
|
console.log(`✅ Program created: ${programPath}`);
|
|
19
15
|
}
|
|
@@ -3,7 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import prompts from 'prompts';
|
|
4
4
|
import { writeIfNotExists, updateIndexTs } from '../utils/fileUtils.js';
|
|
5
5
|
import { toPascalCase } from '../utils/contextUtils.js';
|
|
6
|
-
|
|
6
|
+
import { getFoundationConstructPath, getSharedState } from '../utils/sharedFoundation.js';
|
|
7
7
|
const createResponseWithWrapperTemplate = (name) => {
|
|
8
8
|
const interfaceName = `I${name}`;
|
|
9
9
|
return `import { ResponseBuilder } from '@azure-net/kit/infra';
|
|
@@ -19,17 +19,14 @@ export class ${name}<TData = unknown, TMeta = unknown> extends ResponseBuilder<T
|
|
|
19
19
|
}
|
|
20
20
|
`;
|
|
21
21
|
};
|
|
22
|
-
|
|
23
22
|
const createResponseSimpleTemplate = (name) => `import { ResponseBuilder } from '@azure-net/kit/infra';
|
|
24
23
|
|
|
25
24
|
export class ${name}<TData = unknown, TMeta = unknown> extends ResponseBuilder<TData, TMeta> {}
|
|
26
25
|
`;
|
|
27
|
-
|
|
28
26
|
const ensureResponseName = (rawName) => {
|
|
29
27
|
const normalized = toPascalCase(rawName || 'ApiResponse') || 'ApiResponse';
|
|
30
28
|
return normalized.endsWith('Response') ? normalized : `${normalized}Response`;
|
|
31
29
|
};
|
|
32
|
-
|
|
33
30
|
export default async function createResponse() {
|
|
34
31
|
const { responseNameRaw } = await prompts({
|
|
35
32
|
type: 'text',
|
|
@@ -37,28 +34,23 @@ export default async function createResponse() {
|
|
|
37
34
|
message: 'Response class name:',
|
|
38
35
|
initial: 'ApiResponse'
|
|
39
36
|
});
|
|
40
|
-
|
|
41
37
|
const responseName = ensureResponseName(String(responseNameRaw ?? 'ApiResponse'));
|
|
42
|
-
|
|
43
38
|
const { useCommonResponseWrapper } = await prompts({
|
|
44
39
|
type: 'confirm',
|
|
45
40
|
name: 'useCommonResponseWrapper',
|
|
46
41
|
message: 'Use common backend response wrapper interface?',
|
|
47
42
|
initial: true
|
|
48
43
|
});
|
|
49
|
-
|
|
50
|
-
const responseRoot =
|
|
44
|
+
const { sharedContext } = await getSharedState();
|
|
45
|
+
const responseRoot = getFoundationConstructPath(sharedContext.name, 'response');
|
|
51
46
|
const filePath = path.join(responseRoot, `${responseName}.ts`);
|
|
52
47
|
await fs.mkdir(responseRoot, { recursive: true });
|
|
53
|
-
|
|
54
48
|
const template = useCommonResponseWrapper ? createResponseWithWrapperTemplate(responseName) : createResponseSimpleTemplate(responseName);
|
|
55
49
|
const created = await writeIfNotExists(filePath, template);
|
|
56
50
|
await updateIndexTs(responseRoot);
|
|
57
|
-
|
|
58
51
|
if (!created) {
|
|
59
52
|
console.log(`⚠️ Response "${responseName}" already exists. File was not overwritten.`);
|
|
60
53
|
return;
|
|
61
54
|
}
|
|
62
|
-
|
|
63
55
|
console.log(`✅ Response created: ${filePath}`);
|
|
64
56
|
}
|
|
@@ -3,6 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import prompts from 'prompts';
|
|
4
4
|
import { writeIfNotExists, updateIndexTs } from '../utils/fileUtils.js';
|
|
5
5
|
import { toPascalCase } from '../utils/contextUtils.js';
|
|
6
|
+
import { getFoundationConstructPath, getSharedState } from '../utils/sharedFoundation.js';
|
|
6
7
|
|
|
7
8
|
const createResponseWithWrapperTemplate = (name: string): string => {
|
|
8
9
|
const interfaceName = `I${name}`;
|
|
@@ -47,7 +48,8 @@ export default async function createResponse(): Promise<void> {
|
|
|
47
48
|
initial: true
|
|
48
49
|
});
|
|
49
50
|
|
|
50
|
-
const
|
|
51
|
+
const { sharedContext } = await getSharedState();
|
|
52
|
+
const responseRoot = getFoundationConstructPath(sharedContext.name, 'response');
|
|
51
53
|
const filePath = path.join(responseRoot, `${responseName}.ts`);
|
|
52
54
|
await fs.mkdir(responseRoot, { recursive: true });
|
|
53
55
|
|
|
@@ -3,7 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import prompts from 'prompts';
|
|
4
4
|
import { writeIfNotExists, updateIndexTs } from '../utils/fileUtils.js';
|
|
5
5
|
import { toPascalCase, toCamelCase } from '../utils/contextUtils.js';
|
|
6
|
-
|
|
6
|
+
import { getFoundationConstructPath, getSharedState } from '../utils/sharedFoundation.js';
|
|
7
7
|
const createSchemaRuleTemplate = (ruleName) => `import type { ValidationParams, ValidationRuleResult } from '@azure-net/kit/delivery';
|
|
8
8
|
|
|
9
9
|
export const ${ruleName} = <T = unknown, D = unknown>(): ValidationRuleResult<T, D> => {
|
|
@@ -12,7 +12,6 @@ export const ${ruleName} = <T = unknown, D = unknown>(): ValidationRuleResult<T,
|
|
|
12
12
|
\t};
|
|
13
13
|
};
|
|
14
14
|
`;
|
|
15
|
-
|
|
16
15
|
export default async function createSchemaRule() {
|
|
17
16
|
const { ruleNameRaw } = await prompts({
|
|
18
17
|
type: 'text',
|
|
@@ -20,21 +19,17 @@ export default async function createSchemaRule() {
|
|
|
20
19
|
message: 'Schema rule name:',
|
|
21
20
|
initial: 'File'
|
|
22
21
|
});
|
|
23
|
-
|
|
24
22
|
const pascalName = toPascalCase(String(ruleNameRaw ?? 'File')) || 'File';
|
|
25
23
|
const camelName = toCamelCase(pascalName) || 'file';
|
|
26
|
-
const
|
|
24
|
+
const { sharedContext } = await getSharedState();
|
|
25
|
+
const rulesPath = path.join(getFoundationConstructPath(sharedContext.name, 'schema'), 'custom-rules');
|
|
27
26
|
const filePath = path.join(rulesPath, `${pascalName}.ts`);
|
|
28
|
-
|
|
29
27
|
await fs.mkdir(rulesPath, { recursive: true });
|
|
30
|
-
|
|
31
28
|
const created = await writeIfNotExists(filePath, createSchemaRuleTemplate(camelName));
|
|
32
29
|
await updateIndexTs(rulesPath);
|
|
33
|
-
|
|
34
30
|
if (!created) {
|
|
35
31
|
console.log(`⚠️ Schema rule "${pascalName}" already exists. File was not overwritten.`);
|
|
36
32
|
return;
|
|
37
33
|
}
|
|
38
|
-
|
|
39
34
|
console.log(`✅ Schema rule created: ${filePath}`);
|
|
40
35
|
}
|
|
@@ -3,6 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import prompts from 'prompts';
|
|
4
4
|
import { writeIfNotExists, updateIndexTs } from '../utils/fileUtils.js';
|
|
5
5
|
import { toPascalCase, toCamelCase } from '../utils/contextUtils.js';
|
|
6
|
+
import { getFoundationConstructPath, getSharedState } from '../utils/sharedFoundation.js';
|
|
6
7
|
|
|
7
8
|
const createSchemaRuleTemplate = (
|
|
8
9
|
ruleName: string
|
|
@@ -25,7 +26,8 @@ export default async function createSchemaRule(): Promise<void> {
|
|
|
25
26
|
|
|
26
27
|
const pascalName = toPascalCase(String(ruleNameRaw ?? 'File')) || 'File';
|
|
27
28
|
const camelName = toCamelCase(pascalName) || 'file';
|
|
28
|
-
const
|
|
29
|
+
const { sharedContext } = await getSharedState();
|
|
30
|
+
const rulesPath = path.join(getFoundationConstructPath(sharedContext.name, 'schema'), 'custom-rules');
|
|
29
31
|
const filePath = path.join(rulesPath, `${pascalName}.ts`);
|
|
30
32
|
|
|
31
33
|
await fs.mkdir(rulesPath, { recursive: true });
|
|
@@ -2,17 +2,15 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import prompts from 'prompts';
|
|
4
4
|
import { writeIfNotExists } from '../utils/fileUtils.js';
|
|
5
|
-
|
|
5
|
+
import { getLocalizationPath, getSharedState } from '../utils/sharedFoundation.js';
|
|
6
6
|
const parseLocales = (rawLocales) => {
|
|
7
7
|
const parsed = String(rawLocales ?? '')
|
|
8
8
|
.split(',')
|
|
9
9
|
.map((item) => item.trim().toLowerCase())
|
|
10
10
|
.filter(Boolean);
|
|
11
|
-
|
|
12
11
|
const unique = Array.from(new Set(parsed));
|
|
13
12
|
return unique.length ? unique : ['en', 'ru'];
|
|
14
13
|
};
|
|
15
|
-
|
|
16
14
|
const createTranslationIndexTemplate = (defaultLocale) => `import { messages } from './locales/index.js';
|
|
17
15
|
import { createTranslations, type Path } from '@azure-net/kit/i18n';
|
|
18
16
|
|
|
@@ -30,17 +28,14 @@ export type MessageKeys = Path<MessagesList>;
|
|
|
30
28
|
export type TranslationParam = MessageKeys | string | TranslationExtended;
|
|
31
29
|
export type AvailableLocales = keyof typeof messages;
|
|
32
30
|
`;
|
|
33
|
-
|
|
34
31
|
const createLocalesIndexTemplate = (locales) => {
|
|
35
32
|
const lines = locales.map((locale) => `\t${locale}: () => import('./${locale}/index.js').then((res) => res.default)`);
|
|
36
33
|
return `export const messages = {\n${lines.join(',\n')}\n};\n`;
|
|
37
34
|
};
|
|
38
|
-
|
|
39
35
|
const createLocaleTemplate = () => `export default {
|
|
40
36
|
\ttest: 'test'
|
|
41
37
|
};
|
|
42
38
|
`;
|
|
43
|
-
|
|
44
39
|
export default async function createTranslationManager() {
|
|
45
40
|
const { localesRaw } = await prompts({
|
|
46
41
|
type: 'text',
|
|
@@ -48,9 +43,7 @@ export default async function createTranslationManager() {
|
|
|
48
43
|
message: 'Enter locales (comma separated):',
|
|
49
44
|
initial: 'en, ru'
|
|
50
45
|
});
|
|
51
|
-
|
|
52
46
|
const locales = parseLocales(String(localesRaw ?? 'en, ru'));
|
|
53
|
-
|
|
54
47
|
const { defaultLocale } = await prompts({
|
|
55
48
|
type: 'select',
|
|
56
49
|
name: 'defaultLocale',
|
|
@@ -58,21 +51,17 @@ export default async function createTranslationManager() {
|
|
|
58
51
|
choices: locales.map((locale) => ({ title: locale, value: locale })),
|
|
59
52
|
initial: 0
|
|
60
53
|
});
|
|
61
|
-
|
|
62
54
|
const selectedDefaultLocale = String(defaultLocale ?? locales[0] ?? 'en');
|
|
63
|
-
|
|
64
|
-
const translationRoot =
|
|
55
|
+
const { sharedContext } = await getSharedState();
|
|
56
|
+
const translationRoot = getLocalizationPath(sharedContext.name);
|
|
65
57
|
const localesRoot = path.join(translationRoot, 'locales');
|
|
66
58
|
await fs.mkdir(localesRoot, { recursive: true });
|
|
67
|
-
|
|
68
59
|
await writeIfNotExists(path.join(translationRoot, 'index.ts'), createTranslationIndexTemplate(selectedDefaultLocale));
|
|
69
60
|
await writeIfNotExists(path.join(localesRoot, 'index.ts'), createLocalesIndexTemplate(locales));
|
|
70
|
-
|
|
71
61
|
for (const locale of locales) {
|
|
72
62
|
const localeDir = path.join(localesRoot, locale);
|
|
73
63
|
await fs.mkdir(localeDir, { recursive: true });
|
|
74
64
|
await writeIfNotExists(path.join(localeDir, 'index.ts'), createLocaleTemplate());
|
|
75
65
|
}
|
|
76
|
-
|
|
77
66
|
console.log(`✅ Translation manager created: ${translationRoot}`);
|
|
78
67
|
}
|
|
@@ -2,6 +2,7 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import prompts from 'prompts';
|
|
4
4
|
import { writeIfNotExists } from '../utils/fileUtils.js';
|
|
5
|
+
import { getLocalizationPath, getSharedState } from '../utils/sharedFoundation.js';
|
|
5
6
|
|
|
6
7
|
const parseLocales = (rawLocales: string): string[] => {
|
|
7
8
|
const parsed = String(rawLocales ?? '')
|
|
@@ -61,7 +62,8 @@ export default async function createTranslationManager(): Promise<void> {
|
|
|
61
62
|
|
|
62
63
|
const selectedDefaultLocale = String(defaultLocale ?? locales[0] ?? 'en');
|
|
63
64
|
|
|
64
|
-
const
|
|
65
|
+
const { sharedContext } = await getSharedState();
|
|
66
|
+
const translationRoot = getLocalizationPath(sharedContext.name);
|
|
65
67
|
const localesRoot = path.join(translationRoot, 'locales');
|
|
66
68
|
await fs.mkdir(localesRoot, { recursive: true });
|
|
67
69
|
|
|
@@ -2,13 +2,9 @@ import fs from 'fs/promises';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import prompts from 'prompts';
|
|
4
4
|
import { loadUserConfig } from './loadConfig.js';
|
|
5
|
-
|
|
6
5
|
const APP_PATH = path.join(process.cwd(), 'src/app');
|
|
7
|
-
const CORE_PATH = path.join(process.cwd(), 'src/core');
|
|
8
|
-
|
|
9
6
|
function normalizeContextNames(rawContexts) {
|
|
10
7
|
if (!Array.isArray(rawContexts)) return [];
|
|
11
|
-
|
|
12
8
|
return rawContexts
|
|
13
9
|
.map((item) => {
|
|
14
10
|
if (typeof item === 'string') {
|
|
@@ -19,25 +15,20 @@ function normalizeContextNames(rawContexts) {
|
|
|
19
15
|
})
|
|
20
16
|
.filter(Boolean);
|
|
21
17
|
}
|
|
22
|
-
|
|
23
18
|
export async function selectContext(message = 'Select context:') {
|
|
24
19
|
const config = await loadUserConfig();
|
|
25
20
|
const contexts = normalizeContextNames(config.contexts);
|
|
26
|
-
|
|
27
21
|
const { context } = await prompts({
|
|
28
22
|
type: 'select',
|
|
29
23
|
name: 'context',
|
|
30
24
|
message,
|
|
31
|
-
choices:
|
|
25
|
+
choices: contexts.map((c) => ({ title: c, value: c }))
|
|
32
26
|
});
|
|
33
|
-
|
|
34
|
-
return context;
|
|
27
|
+
return context ? String(context) : null;
|
|
35
28
|
}
|
|
36
|
-
|
|
37
29
|
export function getContextPath(context) {
|
|
38
|
-
return
|
|
30
|
+
return path.join(APP_PATH, context, 'layers');
|
|
39
31
|
}
|
|
40
|
-
|
|
41
32
|
export async function getAvailableFiles(dir, pattern = '.ts') {
|
|
42
33
|
try {
|
|
43
34
|
const files = await fs.readdir(dir);
|
|
@@ -46,40 +37,34 @@ export async function getAvailableFiles(dir, pattern = '.ts') {
|
|
|
46
37
|
return [];
|
|
47
38
|
}
|
|
48
39
|
}
|
|
49
|
-
|
|
50
40
|
export async function getApplicationProviderServices(context) {
|
|
51
41
|
const contextPath = getContextPath(context);
|
|
52
42
|
const providerPath = path.join(contextPath, 'application/providers/ApplicationProvider.ts');
|
|
53
|
-
|
|
54
43
|
try {
|
|
55
44
|
const content = await fs.readFile(providerPath, 'utf-8');
|
|
56
45
|
// Extract service names from ApplicationProvider
|
|
57
46
|
const serviceRegex = /(\w+Service):\s*\(/g;
|
|
58
47
|
const services = [];
|
|
59
48
|
let match;
|
|
60
|
-
|
|
61
49
|
while ((match = serviceRegex.exec(content)) !== null) {
|
|
62
|
-
|
|
50
|
+
const serviceName = match[1];
|
|
51
|
+
if (serviceName) services.push(serviceName);
|
|
63
52
|
}
|
|
64
|
-
|
|
65
53
|
return services;
|
|
66
54
|
} catch {
|
|
67
55
|
return [];
|
|
68
56
|
}
|
|
69
57
|
}
|
|
70
|
-
|
|
71
58
|
export function toPascalCase(str) {
|
|
72
59
|
return str
|
|
73
60
|
.split(/[-_\s]+/)
|
|
74
61
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
75
62
|
.join('');
|
|
76
63
|
}
|
|
77
|
-
|
|
78
64
|
export function toCamelCase(str) {
|
|
79
65
|
const pascal = toPascalCase(str);
|
|
80
66
|
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
81
67
|
}
|
|
82
|
-
|
|
83
68
|
export function toKebabCase(str) {
|
|
84
69
|
return str
|
|
85
70
|
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
@@ -4,7 +4,6 @@ import prompts from 'prompts';
|
|
|
4
4
|
import { loadUserConfig } from './loadConfig.js';
|
|
5
5
|
|
|
6
6
|
const APP_PATH = path.join(process.cwd(), 'src/app');
|
|
7
|
-
const CORE_PATH = path.join(process.cwd(), 'src/core');
|
|
8
7
|
|
|
9
8
|
function normalizeContextNames(rawContexts: unknown): string[] {
|
|
10
9
|
if (!Array.isArray(rawContexts)) return [];
|
|
@@ -28,14 +27,14 @@ export async function selectContext(message = 'Select context:'): Promise<string
|
|
|
28
27
|
type: 'select',
|
|
29
28
|
name: 'context',
|
|
30
29
|
message,
|
|
31
|
-
choices:
|
|
30
|
+
choices: contexts.map((c) => ({ title: c, value: c }))
|
|
32
31
|
});
|
|
33
32
|
|
|
34
33
|
return context ? String(context) : null;
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
export function getContextPath(context: string): string {
|
|
38
|
-
return
|
|
37
|
+
return path.join(APP_PATH, context, 'layers');
|
|
39
38
|
}
|
|
40
39
|
|
|
41
40
|
export async function getAvailableFiles(dir: string, pattern = '.ts'): Promise<string[]> {
|