@azure-net/cli 1.4.2 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/bin/azure-net.js +88 -37
  2. package/package.json +1 -1
  3. package/src/commands/generate/generateCrud.js +350 -0
  4. package/src/commands/generate/generateDatasource.js +41 -0
  5. package/src/commands/generate/generateModule.js +23 -0
  6. package/src/commands/generate/generatePresenter.js +113 -0
  7. package/src/commands/generate/generateRepository.js +102 -0
  8. package/src/commands/generate/generateResponse.js +42 -0
  9. package/src/commands/generate/generateSchema.js +95 -0
  10. package/src/commands/generate/generateService.js +93 -0
  11. package/src/commands/generate/index.js +8 -0
  12. package/src/commands/init/index.js +7 -0
  13. package/src/commands/init/initAliases.js +50 -0
  14. package/src/commands/init/initEdges.js +42 -0
  15. package/src/commands/init/initMiddleware.js +43 -0
  16. package/src/commands/init/initSchema.js +19 -0
  17. package/src/commands/init/initStructure.js +79 -0
  18. package/src/commands/init/initTranslations.js +102 -0
  19. package/src/utils/contextUtils.js +53 -0
  20. package/src/utils/fileUtils.js +79 -0
  21. package/src/utils/loadConfig.js +2 -2
  22. package/src/utils/providerUtils.js +107 -0
  23. package/.idea/azure-net-ecosystem-cli.iml +0 -9
  24. package/.idea/modules.xml +0 -8
  25. package/src/commands/generateCrud.js +0 -135
  26. package/src/commands/generateDatasource.js +0 -44
  27. package/src/commands/generateRepo.js +0 -69
  28. package/src/commands/generateRequest.js +0 -95
  29. package/src/commands/generateService.js +0 -77
  30. package/src/commands/generateStructure.js +0 -65
  31. package/src/commands/initAliases.js +0 -101
  32. /package/src/commands/{initLint.js → init/initLint.js} +0 -0
@@ -0,0 +1,102 @@
1
+ import { writeIfNotExists, updateIndexTs, injectIntoFile } from '../../utils/fileUtils.js';
2
+ import path from 'path';
3
+
4
+ const translationsPath = path.join(process.cwd(), 'src/app/core/Translation');
5
+ const layoutServerPath = path.join(process.cwd(), 'src/routes/+layout.server.ts');
6
+ const layoutTsPath = path.join(process.cwd(), 'src/routes/+layout.ts');
7
+
8
+ const translationProviderTemplate = `import { messages } from './Locales';
9
+ import { createTranslations } from '@azure-net/kit/i18n';
10
+
11
+ export const Translation = createTranslations({
12
+ \tmessages,
13
+ \tinitLang: 'en',
14
+ \tinitLangFromAcceptLanguage: true,
15
+ \tcookieName: 'lang'
16
+ });`;
17
+
18
+ const enLocaleTemplate = `export default {
19
+ \tapp: {
20
+ \t\ttitle: 'Application'
21
+ \t}
22
+ };`;
23
+
24
+ const ruLocaleTemplate = `export default {
25
+ \tapp: {
26
+ \t\ttitle: 'Приложение'
27
+ \t}
28
+ };`;
29
+
30
+ const localesIndexTemplate = `export const messages = {
31
+ \ten: () => import('./en').then((res) => res.default),
32
+ \tru: () => import('./ru').then((res) => res.default)
33
+ };`;
34
+
35
+ export default async function initTranslations() {
36
+ // Create translation files
37
+ await writeIfNotExists(
38
+ path.join(translationsPath, 'index.ts'),
39
+ translationProviderTemplate
40
+ );
41
+
42
+ await writeIfNotExists(
43
+ path.join(translationsPath, 'Locales', 'en.ts'),
44
+ enLocaleTemplate
45
+ );
46
+
47
+ await writeIfNotExists(
48
+ path.join(translationsPath, 'Locales', 'ru.ts'),
49
+ ruLocaleTemplate
50
+ );
51
+
52
+ await writeIfNotExists(
53
+ path.join(translationsPath, 'Locales', 'index.ts'),
54
+ localesIndexTemplate
55
+ );
56
+
57
+ // Add to hooks.server.ts
58
+ await injectIntoFile(
59
+ path.join(process.cwd(), 'src/hooks.server.ts'),
60
+ 'import',
61
+ `import { Translation } from '$core';`,
62
+ 'before'
63
+ );
64
+
65
+ await injectIntoFile(
66
+ path.join(process.cwd(), 'src/hooks.server.ts'),
67
+ 'await serverMiddleware();',
68
+ `\t\t\tconst { preloadTranslation, applyHtmlLocaleAttr } = Translation();\n\t\t\tawait preloadTranslation(edgesEvent);`,
69
+ 'after'
70
+ );
71
+
72
+ await injectIntoFile(
73
+ path.join(process.cwd(), 'src/hooks.server.ts'),
74
+ 'transformPageChunk: ({ html })',
75
+ ' => {\n\t\t\t\tconst serialized = serialize(html);\n\t\t\t\treturn applyHtmlLocaleAttr(serialized);\n\t\t\t}',
76
+ 'after'
77
+ );
78
+
79
+ // Add to layout files
80
+ await injectIntoFile(
81
+ layoutServerPath,
82
+ 'export const load',
83
+ `\treturn { lang: locals.lang };`,
84
+ 'after'
85
+ );
86
+
87
+ await injectIntoFile(
88
+ layoutTsPath,
89
+ 'import',
90
+ `import { Translation } from '$core';\nimport { browser } from '$app/environment';`,
91
+ 'before'
92
+ );
93
+
94
+ await injectIntoFile(
95
+ layoutTsPath,
96
+ 'export const load',
97
+ `\tconst { syncTranslation } = Translation();\n\tif (browser) {\n\t\tawait syncTranslation({ lang: data.lang }, false);\n\t}`,
98
+ 'after'
99
+ );
100
+
101
+ console.log('✅ Translations initialized');
102
+ }
@@ -0,0 +1,53 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import prompts from 'prompts';
4
+ import { loadUserConfig } from './loadConfig.js';
5
+
6
+ const CONTEXTS_PATH = path.join(process.cwd(), 'src/app/contexts');
7
+ const CORE_PATH = path.join(process.cwd(), 'src/app/core');
8
+
9
+ export async function selectContext(message = 'Select context:') {
10
+ const config = await loadUserConfig();
11
+ const contexts = config.contexts || ['app'];
12
+
13
+ const { context } = await prompts({
14
+ type: 'select',
15
+ name: 'context',
16
+ message,
17
+ choices: [
18
+ ...contexts.map(c => ({ title: c, value: c })),
19
+ { title: 'core (shared)', value: 'core' }
20
+ ]
21
+ });
22
+
23
+ return context;
24
+ }
25
+
26
+ export function getContextPath(context) {
27
+ return context === 'core'
28
+ ? CORE_PATH
29
+ : path.join(CONTEXTS_PATH, context);
30
+ }
31
+
32
+ export async function getAvailableFiles(dir, pattern = '.ts') {
33
+ try {
34
+ const files = await fs.readdir(dir);
35
+ return files
36
+ .filter(f => f.endsWith(pattern) && f !== 'index.ts')
37
+ .map(f => f.replace(pattern, ''));
38
+ } catch {
39
+ return [];
40
+ }
41
+ }
42
+
43
+ export function toPascalCase(str) {
44
+ return str
45
+ .split(/[-_\s]+/)
46
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
47
+ .join('');
48
+ }
49
+
50
+ export function toCamelCase(str) {
51
+ const pascal = toPascalCase(str);
52
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
53
+ }
@@ -0,0 +1,79 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+
4
+ export async function writeIfNotExists(filepath, content) {
5
+ try {
6
+ await fs.access(filepath);
7
+ console.log(`⚠️ Skip existing: ${filepath}`);
8
+ return false;
9
+ } catch {
10
+ await fs.mkdir(path.dirname(filepath), { recursive: true });
11
+ await fs.writeFile(filepath, content, 'utf-8');
12
+ console.log(`✅ Created: ${filepath}`);
13
+ return true;
14
+ }
15
+ }
16
+
17
+ export async function updateIndexTs(dir, filePattern = '.ts', ignore = ['index.ts']) {
18
+ try {
19
+ const files = (await fs.readdir(dir)).filter(f =>
20
+ f.endsWith(filePattern) && !ignore.includes(f)
21
+ );
22
+ const content = files
23
+ .map(f => `export * from './${f.replace('.ts', '')}';`)
24
+ .join('\n') + '\n';
25
+ await fs.writeFile(path.join(dir, 'index.ts'), content, 'utf-8');
26
+ } catch (error) {
27
+ // Directory doesn't exist yet
28
+ }
29
+ }
30
+
31
+ export async function ensureIndexExports(layerPath) {
32
+ try {
33
+ const subdirs = await fs.readdir(layerPath, { withFileTypes: true });
34
+ const exportLines = subdirs
35
+ .filter(d => d.isDirectory())
36
+ .map(d => `export * from './${d.name}';`);
37
+
38
+ if (exportLines.length > 0) {
39
+ await fs.writeFile(
40
+ path.join(layerPath, 'index.ts'),
41
+ exportLines.join('\n') + '\n',
42
+ 'utf-8'
43
+ );
44
+ }
45
+ } catch (error) {
46
+ // Directory doesn't exist
47
+ }
48
+ }
49
+
50
+ export async function injectIntoFile(filepath, searchPattern, newContent, position = 'after') {
51
+ try {
52
+ let content = await fs.readFile(filepath, 'utf-8');
53
+ const index = content.indexOf(searchPattern);
54
+
55
+ if (index === -1) {
56
+ console.warn(`Pattern not found in ${filepath}`);
57
+ return false;
58
+ }
59
+
60
+ if (content.includes(newContent.trim())) {
61
+ console.log(`Content already exists in ${filepath}`);
62
+ return false;
63
+ }
64
+
65
+ if (position === 'after') {
66
+ const insertIndex = index + searchPattern.length;
67
+ content = content.slice(0, insertIndex) + '\n' + newContent + content.slice(insertIndex);
68
+ } else {
69
+ content = content.slice(0, index) + newContent + '\n' + content.slice(index);
70
+ }
71
+
72
+ await fs.writeFile(filepath, content, 'utf-8');
73
+ console.log(`✅ Updated: ${filepath}`);
74
+ return true;
75
+ } catch (error) {
76
+ console.error(`Failed to update ${filepath}:`, error.message);
77
+ return false;
78
+ }
79
+ }
@@ -8,8 +8,8 @@ export async function loadUserConfig() {
8
8
  return userConfig.default || {};
9
9
  } catch {
10
10
  return {
11
- modules: ['admin', 'auth'],
12
- datasourceAlias: ''
11
+ contexts: ['app'],
12
+ defaultContext: 'app'
13
13
  };
14
14
  }
15
15
  }
@@ -0,0 +1,107 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import { toPascalCase, toCamelCase } from './contextUtils.js';
4
+ import { writeIfNotExists, updateIndexTs } from './fileUtils.js';
5
+
6
+ export async function ensureProvider(context, layer, dependencies = {}) {
7
+ const contextName = toPascalCase(context);
8
+ const providerName = `${layer}Provider`;
9
+ const boundaryName = `${contextName}${layer}BoundaryProvider`;
10
+
11
+ const contextPath = path.join(process.cwd(), 'src/app/contexts', context);
12
+ const providerPath = path.join(contextPath, layer, 'Providers', `${layer}Provider.ts`);
13
+
14
+ try {
15
+ await fs.access(providerPath);
16
+ return { exists: true, name: providerName, path: providerPath };
17
+ } catch {
18
+ // Provider doesn't exist, create it
19
+ const content = generateProviderContent(context, layer, dependencies);
20
+ await writeIfNotExists(providerPath, content);
21
+ await updateIndexTs(path.dirname(providerPath));
22
+
23
+ // Update layer index to export provider
24
+ const layerIndexPath = path.join(contextPath, layer, 'index.ts');
25
+ await writeIfNotExists(layerIndexPath, `export { ${providerName} } from './Providers';`);
26
+
27
+ return { exists: false, name: providerName, path: providerPath };
28
+ }
29
+ }
30
+
31
+ function generateProviderContent(context, layer, dependencies) {
32
+ const contextName = toPascalCase(context);
33
+ const boundaryName = `${contextName}${layer}BoundaryProvider`;
34
+ const varName = `${layer}Provider`;
35
+
36
+ let imports = [`import { createBoundaryProvider } from '@azure-net/kit';`];
37
+ let deps = [];
38
+ let depParams = '';
39
+
40
+ if (dependencies.hasDatasource && layer === 'Infrastructure') {
41
+ imports.push(`import { DatasourceProvider } from './DatasourceProvider';`);
42
+ deps.push('DatasourceProvider');
43
+ }
44
+
45
+ if (dependencies.infrastructure && layer === 'Application') {
46
+ imports.push(`import { InfrastructureProvider } from '$${context}/Infrastructure';`);
47
+ deps.push('InfrastructureProvider');
48
+ }
49
+
50
+ if (deps.length > 0) {
51
+ depParams = `{ ${deps.join(', ')} }`;
52
+ }
53
+
54
+ const dependsOnStr = deps.length > 0
55
+ ? `,\n\t{ dependsOn: { ${deps.join(', ')} } }`
56
+ : '';
57
+
58
+ return `${imports.join('\n')}
59
+
60
+ export const ${varName} = createBoundaryProvider(
61
+ \t'${boundaryName}',
62
+ \t(${depParams ? depParams : ''}) => ({
63
+ \t\t// Services will be added here
64
+ \t})${dependsOnStr}
65
+ );`;
66
+ }
67
+
68
+ export async function addToProvider(providerPath, serviceName, importPath, dependency = null) {
69
+ let content = await fs.readFile(providerPath, 'utf-8');
70
+
71
+ // Add import if not exists
72
+ const importStatement = `import { ${serviceName} } from '${importPath}';`;
73
+ if (!content.includes(serviceName)) {
74
+ const lastImportIndex = content.lastIndexOf('import ');
75
+ const endOfImportLine = content.indexOf('\n', lastImportIndex);
76
+ content = content.slice(0, endOfImportLine + 1) + importStatement + '\n' + content.slice(endOfImportLine + 1);
77
+ }
78
+
79
+ // Add factory method with dependency
80
+ const factoryLine = dependency
81
+ ? `\t\t${serviceName}: () => new ${serviceName}(${dependency})`
82
+ : `\t\t${serviceName}: () => new ${serviceName}()`;
83
+
84
+ if (!content.includes(`${serviceName}:`)) {
85
+ // Find the position to insert
86
+ const returnIndex = content.indexOf('=> ({');
87
+ const endOfReturn = content.indexOf('\t})', returnIndex);
88
+
89
+ // Check if there are existing services
90
+ const servicesSection = content.substring(returnIndex, endOfReturn);
91
+ const hasServices = servicesSection.includes(':');
92
+
93
+ if (hasServices) {
94
+ // Add comma before new service
95
+ const beforeEnd = content.lastIndexOf('\n', endOfReturn);
96
+ content = content.slice(0, beforeEnd) + ',\n' + factoryLine + content.slice(beforeEnd);
97
+ } else {
98
+ // First service, replace comment
99
+ content = content.replace(
100
+ '// Services will be added here',
101
+ factoryLine
102
+ );
103
+ }
104
+ }
105
+
106
+ await fs.writeFile(providerPath, content, 'utf-8');
107
+ }
@@ -1,9 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <module type="WEB_MODULE" version="4">
3
- <component name="Go" enabled="true" />
4
- <component name="NewModuleRootManager">
5
- <content url="file://$MODULE_DIR$" />
6
- <orderEntry type="inheritedJdk" />
7
- <orderEntry type="sourceFolder" forTests="false" />
8
- </component>
9
- </module>
package/.idea/modules.xml DELETED
@@ -1,8 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="ProjectModuleManager">
4
- <modules>
5
- <module fileurl="file://$PROJECT_DIR$/.idea/azure-net-ecosystem-cli.iml" filepath="$PROJECT_DIR$/.idea/azure-net-ecosystem-cli.iml" />
6
- </modules>
7
- </component>
8
- </project>
@@ -1,135 +0,0 @@
1
- import prompts from 'prompts';
2
- import fs from 'fs/promises';
3
- import path from 'path';
4
- import ejs from 'ejs';
5
- import { loadUserConfig } from '../utils/loadConfig.js';
6
-
7
- const __dirname = path.dirname(new URL(import.meta.url).pathname);
8
- const CORE_DATASOURCES_PATH = path.join(process.cwd(), 'src/app/core/datasources');
9
-
10
- async function writeIfNotExists(filepath, content) {
11
- try {
12
- await fs.access(filepath);
13
- console.log(`⚠️ Skip existing: ${filepath}`);
14
- } catch {
15
- await fs.mkdir(path.dirname(filepath), { recursive: true });
16
- await fs.writeFile(filepath, content, 'utf-8');
17
- console.log(`✅ Created: ${filepath}`);
18
- }
19
- }
20
-
21
- async function updateIndexTs(dir, filePattern = '.ts', ignore = ['index.ts']) {
22
- const files = (await fs.readdir(dir)).filter(f => f.endsWith(filePattern) && !ignore.includes(f));
23
- const content = files.map(f => `export * from './${f.replace('.ts', '')}';`).join('\n') + '\n';
24
- await fs.writeFile(path.join(dir, 'index.ts'), content, 'utf-8');
25
- }
26
-
27
- async function getAvailableDatasources() {
28
- try {
29
- const files = await fs.readdir(CORE_DATASOURCES_PATH);
30
- return files
31
- .filter(f => f.endsWith('.ts') && !f.startsWith('_'))
32
- .map(f => f.replace('.ts', ''));
33
- } catch {
34
- return [];
35
- }
36
- }
37
-
38
- export default async function generateCrud() {
39
- const config = await loadUserConfig();
40
- const datasources = await getAvailableDatasources();
41
-
42
- const answers = await prompts([
43
- config.modules?.length
44
- ? {
45
- type: 'select',
46
- name: 'module',
47
- message: 'Select target module:',
48
- choices: [...config.modules, 'shared-context'].map(m => ({
49
- title: m === 'shared-context' ? 'shared-context (shared)' : m,
50
- value: m
51
- }))
52
- }
53
- : null,
54
- { type: 'text', name: 'className', message: 'Class name (PascalCase):' },
55
- { type: 'text', name: 'endpoint', message: 'API endpoint:' },
56
- {
57
- type: 'select',
58
- name: 'datasource',
59
- message: 'Select datasource:',
60
- choices: datasources.map(d => ({ title: d, value: d }))
61
- }
62
- ].filter(Boolean));
63
-
64
- const rawName = answers.className.trim();
65
- const capital = rawName[0].toUpperCase() + rawName.slice(1);
66
- const lower = rawName[0].toLowerCase() + rawName.slice(1);
67
- const endpoint = answers.endpoint.trim();
68
- const datasource = answers.datasource.trim();
69
- const selectedModule = answers.module;
70
-
71
- const baseArch = path.join(process.cwd(), 'src/app/contexts');
72
- const basePath = selectedModule ? path.join(baseArch, selectedModule) : path.join(baseArch, 'app-context');
73
-
74
- const domainPath = path.join(basePath, 'Domain', capital);
75
- const serviceDir = path.join(basePath, 'Application', 'Services');
76
- const repoPath = path.join(basePath, 'Infra', 'Repo');
77
-
78
- const aliasCurrent = selectedModule === 'shared-context'
79
- ? '$shared-context'
80
- : selectedModule
81
- ? `$${selectedModule}`
82
- : '$app-context';
83
- const aliasShared = '$shared-context';
84
-
85
- const templateData = {
86
- camelCaseName: lower,
87
- pascalCaseName: capital,
88
- endpoint,
89
- datasource,
90
- aliasDomain: `${aliasCurrent}/Domain`,
91
- aliasShared,
92
- aliasApplication: `${aliasCurrent}/Application`,
93
- aliasInfra: `${aliasCurrent}/Infra`,
94
- aliasRepo: `${aliasCurrent}/Infra/Repo`
95
- };
96
-
97
- // === 1) Abstracts
98
- const abstractsDir = path.join(domainPath, 'Abstracts');
99
- for (const file of ['Class', 'ClassCreateRequest', 'ClassUpdateRequest', 'ClassCollectionQuery']) {
100
- const tpl = await fs.readFile(path.join(__dirname, `../templates/domain/abstracts/${file}.ejs`), 'utf-8');
101
- const result = ejs.render(tpl, templateData);
102
- await writeIfNotExists(path.join(abstractsDir, `${capital}${file.replace('Class', '')}.ts`), result);
103
- }
104
- await updateIndexTs(abstractsDir);
105
-
106
- // === 2) Request
107
- const requestDir = path.join(domainPath, 'Request');
108
- for (const file of ['ClassCreateRequest', 'ClassUpdateRequest']) {
109
- const tpl = await fs.readFile(path.join(__dirname, `../templates/domain/request/${file}.ejs`), 'utf-8');
110
- const result = ejs.render(tpl, templateData);
111
- await writeIfNotExists(path.join(requestDir, `${capital}${file.replace('Class', '')}.ts`), result);
112
- }
113
- await updateIndexTs(requestDir);
114
-
115
- // === 3) Services
116
- const serviceTpl = await fs.readFile(path.join(__dirname, '../templates/domain/services/ClassService.ejs'), 'utf-8');
117
- const serviceResult = ejs.render(serviceTpl, templateData);
118
- await writeIfNotExists(path.join(serviceDir, `${capital}Service.ts`), serviceResult);
119
- await updateIndexTs(serviceDir);
120
-
121
- // === 4) Domain Index
122
- await writeIfNotExists(
123
- path.join(domainPath, 'index.ts'),
124
- [`export * from './Abstracts';`, `export * from './Request';`].join('\n')
125
- );
126
- //await updateIndexTs(domainPath);
127
-
128
- // === 5) Repo
129
- const repoTpl = await fs.readFile(path.join(__dirname, '../templates/data/repo/ClassRepo.ejs'), 'utf-8');
130
- const repoResult = ejs.render(repoTpl, templateData);
131
- await writeIfNotExists(path.join(repoPath, `${capital}Repo.ts`), repoResult);
132
- await updateIndexTs(repoPath);
133
-
134
- console.log(`✅ CRUD for ${capital} generated in ${selectedModule || 'app-context'} structure`);
135
- }
@@ -1,44 +0,0 @@
1
- import prompts from 'prompts';
2
- import fs from 'fs/promises';
3
- import path from 'path';
4
-
5
- const CORE_DATASOURCES_PATH = path.join(process.cwd(), 'src/app/core/datasources');
6
-
7
- async function writeIfNotExists(filepath, content) {
8
- try {
9
- await fs.access(filepath);
10
- console.log(`⚠️ Skip existing: ${filepath}`);
11
- } catch {
12
- await fs.mkdir(path.dirname(filepath), { recursive: true });
13
- await fs.writeFile(filepath, content, 'utf-8');
14
- console.log(`✅ Created: ${filepath}`);
15
- }
16
- }
17
-
18
- async function updateIndexTs(dir) {
19
- const files = (await fs.readdir(dir)).filter(f => f.endsWith('.ts') && f !== 'index.ts');
20
- const content = files.map(f => `export * from './${f.replace('.ts', '')}';`).join('\n') + '\n';
21
- await fs.writeFile(path.join(dir, 'index.ts'), content, 'utf-8');
22
- }
23
-
24
- export default async function generateDatasource() {
25
- const { name } = await prompts({
26
- type: 'text',
27
- name: 'name',
28
- message: 'Datasource class name (PascalCase, e.g., BackendApi):'
29
- });
30
-
31
- const capital = name.trim();
32
- const filePath = path.join(CORE_DATASOURCES_PATH, `${capital}Datasource.ts`);
33
-
34
- const content = `import { BaseHttpDatasource, HttpService } from '@azure-net/kit';
35
-
36
- export class ${capital}Datasource extends BaseHttpDatasource {
37
- constructor() {
38
- super({ http: new HttpService({ baseUrl: '' }) }); // TODO: fill generated class
39
- }
40
- }`;
41
-
42
- await writeIfNotExists(filePath, content);
43
- await updateIndexTs(CORE_DATASOURCES_PATH);
44
- }
@@ -1,69 +0,0 @@
1
- import prompts from 'prompts';
2
- import fs from 'fs/promises';
3
- import path from 'path';
4
- import { loadUserConfig } from '../utils/loadConfig.js';
5
-
6
- const CORE_DATASOURCES_PATH = path.join(process.cwd(), 'src/app/core/datasources');
7
- const CONTEXTS_PATH = path.join(process.cwd(), 'src/app/contexts');
8
-
9
- async function getAvailableDatasources() {
10
- try {
11
- const files = await fs.readdir(CORE_DATASOURCES_PATH);
12
- return files.filter(f => f.endsWith('.ts') && !f.startsWith('_')).map(f => f.replace('.ts', ''));
13
- } catch {
14
- return [];
15
- }
16
- }
17
-
18
- async function updateIndexTs(dir) {
19
- const files = (await fs.readdir(dir)).filter(f => f.endsWith('.ts') && f !== 'index.ts');
20
- const content = files.map(f => `export * from './${f.replace('.ts', '')}';`).join('\n') + '\n';
21
- await fs.writeFile(path.join(dir, 'index.ts'), content, 'utf-8');
22
- }
23
-
24
- export default async function generateRepo() {
25
- const config = await loadUserConfig();
26
- const datasources = await getAvailableDatasources();
27
-
28
- const answers = await prompts([
29
- config.modules?.length ? {
30
- type: 'select',
31
- name: 'module',
32
- message: 'Select target module:',
33
- choices: [...config.modules, 'shared-context'].map(m => ({ title: m, value: m }))
34
- } : null,
35
- { type: 'text', name: 'name', message: 'Repository class name (PascalCase):' },
36
- {
37
- type: 'select',
38
- name: 'datasource',
39
- message: 'Select datasource:',
40
- choices: datasources.map(d => ({ title: d, value: d }))
41
- }
42
- ].filter(Boolean));
43
-
44
- const { name, module, datasource } = answers;
45
- const capital = name.trim();
46
-
47
- const basePath = path.join(CONTEXTS_PATH, module || 'app-context');
48
- const repoDir = path.join(basePath, 'Infra', 'Repo');
49
- const filePath = path.join(repoDir, `${capital}Repo.ts`);
50
-
51
- const aliasRepo = module === 'shared-context'
52
- ? '$shared-context/Infra/Repo'
53
- : `$${module}/Infra/Repo`;
54
-
55
- const content = `import { ${datasource} } from '$core/datasources';
56
-
57
- export class ${capital}Repo {
58
- private source = new ${datasource}();
59
-
60
- constructor() {
61
- // TODO: fill generated class
62
- }
63
- }`;
64
-
65
- await fs.mkdir(repoDir, { recursive: true });
66
- await fs.writeFile(filePath, content);
67
- await updateIndexTs(repoDir);
68
- console.log(`✅ Repo created at ${filePath}`);
69
- }