@azure-net/cli 5.1.1 → 6.0.0-next.3
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 +201 -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/presenterFactories.d.ts +9 -0
- package/dist/src/migrate/presenters.d.ts +13 -0
- package/dist/src/migrate/translations.d.ts +8 -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 +17 -7
- 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 +1027 -0
- package/src/migrate/presenterFactories.js +189 -0
- package/src/migrate/presenters.js +373 -0
- package/src/migrate/translations.js +213 -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
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import prompts from 'prompts';
|
|
4
|
+
import { writeIfNotExists } from './fileUtils.js';
|
|
5
|
+
export const SHARED_CONTEXT = 'SharedKernel';
|
|
6
|
+
export const SHARED_WORKSPACE = 'Shared';
|
|
7
|
+
export const ARCHITECTURE_ALIASES = {
|
|
8
|
+
'#contexts': './src/app/Contexts',
|
|
9
|
+
'#presentation': './src/app/Presentation',
|
|
10
|
+
'#platform': './src/app/Platform'
|
|
11
|
+
};
|
|
12
|
+
export const CONTEXT_DIRS = [
|
|
13
|
+
'Domain/Entities',
|
|
14
|
+
'Domain/Ports',
|
|
15
|
+
'Domain/Enums',
|
|
16
|
+
'Application/UseCases',
|
|
17
|
+
'Infrastructure/Repositories'
|
|
18
|
+
];
|
|
19
|
+
export const PRESENTATION_DIRS = ['Ui', 'Scopes', 'Shell'];
|
|
20
|
+
export function normalizePascalName(value) {
|
|
21
|
+
const raw = value.trim();
|
|
22
|
+
if (!/^[A-Za-z][A-Za-z0-9 _-]*$/.test(raw))
|
|
23
|
+
throw new Error(`Invalid architecture name: "${value}". Use letters, numbers, spaces, hyphens or underscores.`);
|
|
24
|
+
return raw
|
|
25
|
+
.split(/[-_\s]+/)
|
|
26
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
27
|
+
.join('');
|
|
28
|
+
}
|
|
29
|
+
export const getAppPath = (cwd = process.cwd()) => path.join(cwd, 'src', 'app');
|
|
30
|
+
export const getContextsPath = (cwd = process.cwd()) => path.join(getAppPath(cwd), 'Contexts');
|
|
31
|
+
export const getContextPath = (context, cwd = process.cwd()) => path.join(getContextsPath(cwd), normalizePascalName(context));
|
|
32
|
+
export const getPresentationPath = (workspace, cwd = process.cwd()) => workspace ? path.join(getAppPath(cwd), 'Presentation', normalizePascalName(workspace)) : path.join(getAppPath(cwd), 'Presentation');
|
|
33
|
+
export const getPlatformPath = (cwd = process.cwd()) => path.join(getAppPath(cwd), 'Platform');
|
|
34
|
+
export const getContextImportPath = (context) => `#contexts/${normalizePascalName(context)}`;
|
|
35
|
+
export const getPresentationImportPath = (workspace) => workspace ? `#presentation/${normalizePascalName(workspace)}` : '#presentation';
|
|
36
|
+
async function directories(root) {
|
|
37
|
+
try {
|
|
38
|
+
return (await fs.readdir(root, { withFileTypes: true }))
|
|
39
|
+
.filter((entry) => entry.isDirectory())
|
|
40
|
+
.map((entry) => entry.name)
|
|
41
|
+
.sort();
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if (error.code === 'ENOENT')
|
|
45
|
+
return [];
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export const discoverContexts = async (cwd = process.cwd()) => (await directories(getContextsPath(cwd))).filter((name) => /^[A-Z][A-Za-z0-9]*$/.test(name));
|
|
50
|
+
export const discoverPresentationWorkspaces = async (cwd = process.cwd()) => (await directories(getPresentationPath(undefined, cwd))).filter((name) => !PRESENTATION_DIRS.includes(name) && /^[A-Z][A-Za-z0-9]*$/.test(name));
|
|
51
|
+
export async function ensureContextStructure(context, cwd = process.cwd()) {
|
|
52
|
+
const name = normalizePascalName(context);
|
|
53
|
+
for (const dir of CONTEXT_DIRS) {
|
|
54
|
+
await writeIfNotExists(path.join(getContextPath(name, cwd), dir, 'index.ts'), 'export {};\n');
|
|
55
|
+
}
|
|
56
|
+
return name;
|
|
57
|
+
}
|
|
58
|
+
export async function ensurePresentationStructure(workspace, cwd = process.cwd()) {
|
|
59
|
+
for (const dir of PRESENTATION_DIRS)
|
|
60
|
+
await fs.mkdir(path.join(getPresentationPath(workspace, cwd), dir), { recursive: true });
|
|
61
|
+
}
|
|
62
|
+
export async function selectContext(message = 'Select context:') {
|
|
63
|
+
await ensureContextStructure(SHARED_CONTEXT);
|
|
64
|
+
const names = await discoverContexts();
|
|
65
|
+
const result = await prompts({
|
|
66
|
+
type: 'select',
|
|
67
|
+
name: 'context',
|
|
68
|
+
message,
|
|
69
|
+
choices: [...names.map((name) => ({ title: name, value: name })), { title: 'Create new context', value: '__create__' }]
|
|
70
|
+
});
|
|
71
|
+
if (!result.context)
|
|
72
|
+
return null;
|
|
73
|
+
if (result.context !== '__create__')
|
|
74
|
+
return String(result.context);
|
|
75
|
+
const answer = await prompts({
|
|
76
|
+
type: 'text',
|
|
77
|
+
name: 'name',
|
|
78
|
+
message: 'Context name:',
|
|
79
|
+
validate: (value) => {
|
|
80
|
+
try {
|
|
81
|
+
normalizePascalName(value);
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
return error.message;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
return answer.name ? ensureContextStructure(String(answer.name)) : null;
|
|
90
|
+
}
|
|
91
|
+
/** null means cancelled; undefined means the flat Presentation layout. */
|
|
92
|
+
export async function selectPresentationWorkspace(message = 'Select presentation workspace:') {
|
|
93
|
+
const workspaces = await discoverPresentationWorkspaces();
|
|
94
|
+
if (!workspaces.length)
|
|
95
|
+
return undefined;
|
|
96
|
+
await ensurePresentationStructure(SHARED_WORKSPACE);
|
|
97
|
+
if (!workspaces.includes(SHARED_WORKSPACE))
|
|
98
|
+
workspaces.push(SHARED_WORKSPACE);
|
|
99
|
+
const answer = await prompts({
|
|
100
|
+
type: 'select',
|
|
101
|
+
name: 'workspace',
|
|
102
|
+
message,
|
|
103
|
+
choices: workspaces.sort().map((name) => ({ title: name, value: name }))
|
|
104
|
+
});
|
|
105
|
+
return answer.workspace ? String(answer.workspace) : null;
|
|
106
|
+
}
|
|
107
|
+
export async function selectWorkspace() {
|
|
108
|
+
const name = await selectPresentationWorkspace();
|
|
109
|
+
if (name === null)
|
|
110
|
+
return null;
|
|
111
|
+
return { name: name ?? '', path: getPresentationPath(name), alias: getPresentationImportPath(name) };
|
|
112
|
+
}
|
|
113
|
+
export async function selectScope(workspace) {
|
|
114
|
+
const root = path.join(workspace.path, 'Scopes');
|
|
115
|
+
const scopes = (await directories(root)).filter((name) => /^[A-Z][A-Za-z0-9]*$/.test(name));
|
|
116
|
+
const answer = await prompts({
|
|
117
|
+
type: 'select',
|
|
118
|
+
name: 'scope',
|
|
119
|
+
message: 'Select scope:',
|
|
120
|
+
choices: [...scopes.map((name) => ({ title: name, value: name })), { title: 'Create new scope', value: '__create__' }]
|
|
121
|
+
});
|
|
122
|
+
if (!answer.scope)
|
|
123
|
+
return null;
|
|
124
|
+
if (answer.scope !== '__create__')
|
|
125
|
+
return String(answer.scope);
|
|
126
|
+
const created = await prompts({
|
|
127
|
+
type: 'text',
|
|
128
|
+
name: 'name',
|
|
129
|
+
message: 'Scope name:',
|
|
130
|
+
validate: (value) => {
|
|
131
|
+
try {
|
|
132
|
+
normalizePascalName(value);
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
return error.message;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
if (!created.name)
|
|
141
|
+
return null;
|
|
142
|
+
const name = normalizePascalName(String(created.name));
|
|
143
|
+
await fs.mkdir(path.join(root, name), { recursive: true });
|
|
144
|
+
return name;
|
|
145
|
+
}
|
|
@@ -1,73 +1,40 @@
|
|
|
1
|
-
import fs from 'fs/promises';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
const APP_PATH = path.join(process.cwd(), 'src/app');
|
|
6
|
-
function normalizeContextNames(rawContexts) {
|
|
7
|
-
if (!Array.isArray(rawContexts)) return [];
|
|
8
|
-
return rawContexts
|
|
9
|
-
.map((item) => {
|
|
10
|
-
if (typeof item === 'string') {
|
|
11
|
-
return item.trim();
|
|
12
|
-
}
|
|
13
|
-
if (!item || typeof item !== 'object') return '';
|
|
14
|
-
return String(item.name ?? '').trim();
|
|
15
|
-
})
|
|
16
|
-
.filter(Boolean);
|
|
17
|
-
}
|
|
18
|
-
export async function selectContext(message = 'Select context:') {
|
|
19
|
-
const config = await loadUserConfig();
|
|
20
|
-
const contexts = normalizeContextNames(config.contexts);
|
|
21
|
-
const { context } = await prompts({
|
|
22
|
-
type: 'select',
|
|
23
|
-
name: 'context',
|
|
24
|
-
message,
|
|
25
|
-
choices: contexts.map((c) => ({ title: c, value: c }))
|
|
26
|
-
});
|
|
27
|
-
return context ? String(context) : null;
|
|
28
|
-
}
|
|
29
|
-
export function getContextPath(context) {
|
|
30
|
-
return path.join(APP_PATH, context, 'layers');
|
|
31
|
-
}
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { getPlatformPath, normalizePascalName } from './architecture.js';
|
|
4
|
+
export { getContextPath, selectContext, selectPresentationWorkspace, discoverContexts, discoverPresentationWorkspaces, normalizePascalName } from './architecture.js';
|
|
32
5
|
export async function getAvailableFiles(dir, pattern = '.ts') {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
} catch {
|
|
55
|
-
return [];
|
|
56
|
-
}
|
|
6
|
+
try {
|
|
7
|
+
return (await fs.readdir(dir))
|
|
8
|
+
.filter((file) => file.endsWith(pattern) && file !== 'index.ts')
|
|
9
|
+
.map((file) => file.slice(0, -pattern.length));
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
if (error.code === 'ENOENT')
|
|
13
|
+
return [];
|
|
14
|
+
throw error;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export async function getApplicationProviderServices() {
|
|
18
|
+
try {
|
|
19
|
+
const content = await fs.readFile(path.join(getPlatformPath(), 'Foundation/DI/ApplicationProvider.ts'), 'utf8');
|
|
20
|
+
return Array.from(content.matchAll(/(\w+Service):\s*\(/g), (match) => match[1]);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (error.code === 'ENOENT')
|
|
24
|
+
return [];
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
57
27
|
}
|
|
58
28
|
export function toPascalCase(str) {
|
|
59
|
-
|
|
60
|
-
.split(/[-_\s]+/)
|
|
61
|
-
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
62
|
-
.join('');
|
|
29
|
+
return str.trim() ? normalizePascalName(str) : '';
|
|
63
30
|
}
|
|
64
31
|
export function toCamelCase(str) {
|
|
65
|
-
|
|
66
|
-
|
|
32
|
+
const pascal = toPascalCase(str);
|
|
33
|
+
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
67
34
|
}
|
|
68
35
|
export function toKebabCase(str) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
36
|
+
return str
|
|
37
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
38
|
+
.replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
|
|
39
|
+
.toLowerCase();
|
|
73
40
|
}
|
package/src/utils/fileUtils.js
CHANGED
|
@@ -1,60 +1,79 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
export async function writeIfNotExists(filepath, content) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
await fs.mkdir(path.dirname(filepath), { recursive: true });
|
|
5
|
+
try {
|
|
6
|
+
await fs.writeFile(filepath, content, { encoding: 'utf8', flag: 'wx' });
|
|
7
|
+
}
|
|
8
|
+
catch (error) {
|
|
9
|
+
if (error.code !== 'EEXIST')
|
|
10
|
+
throw error;
|
|
11
|
+
console.log(`Skip existing: ${filepath}`);
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
console.log(`Created: ${filepath}`);
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
export async function appendBarrelExports(filepath, sources) {
|
|
18
|
+
let content = '';
|
|
19
|
+
try {
|
|
20
|
+
content = await fs.readFile(filepath, 'utf8');
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (error.code !== 'ENOENT')
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
const additions = sources
|
|
27
|
+
.filter((source) => !content.includes("from '" + source + "'") && !content.includes('from "' + source + '"'))
|
|
28
|
+
.map((source) => 'export * from ' + JSON.stringify(source) + ';');
|
|
29
|
+
if (!additions.length && content.trim())
|
|
30
|
+
return;
|
|
31
|
+
await fs.mkdir(path.dirname(filepath), { recursive: true });
|
|
32
|
+
await fs.writeFile(filepath, content + (content && !content.endsWith('\n') ? '\n' : '') + (additions.length ? additions.join('\n') : 'export {};') + '\n');
|
|
14
33
|
}
|
|
15
34
|
export async function updateIndexTs(dir, filePattern = '.ts', ignore = ['index.ts']) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
} catch {
|
|
21
|
-
// Directory doesn't exist yet
|
|
22
|
-
}
|
|
35
|
+
const files = (await fs.readdir(dir))
|
|
36
|
+
.filter((file) => file.endsWith(filePattern) && !file.endsWith('.d.ts') && !ignore.includes(file))
|
|
37
|
+
.sort();
|
|
38
|
+
await appendBarrelExports(path.join(dir, 'index.ts'), files.map((file) => './' + file.slice(0, -filePattern.length)));
|
|
23
39
|
}
|
|
24
40
|
export async function ensureIndexExports(layerPath) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
41
|
+
try {
|
|
42
|
+
const subdirs = await fs.readdir(layerPath, { withFileTypes: true });
|
|
43
|
+
const exportLines = subdirs.filter((d) => d.isDirectory()).map((d) => `export * from './${d.name}';`);
|
|
44
|
+
if (exportLines.length > 0) {
|
|
45
|
+
await fs.writeFile(path.join(layerPath, 'index.ts'), exportLines.join('\n') + '\n', 'utf-8');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Directory doesn't exist
|
|
50
|
+
}
|
|
34
51
|
}
|
|
35
52
|
export async function injectIntoFile(filepath, searchPattern, newContent, position = 'after') {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
try {
|
|
54
|
+
let content = await fs.readFile(filepath, 'utf-8');
|
|
55
|
+
const index = content.indexOf(searchPattern);
|
|
56
|
+
if (index === -1) {
|
|
57
|
+
console.warn(`Pattern not found in ${filepath}`);
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
if (content.includes(newContent.trim())) {
|
|
61
|
+
console.log(`Content already exists in ${filepath}`);
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
if (position === 'after') {
|
|
65
|
+
const insertIndex = index + searchPattern.length;
|
|
66
|
+
content = content.slice(0, insertIndex) + '\n' + newContent + content.slice(insertIndex);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
content = content.slice(0, index) + newContent + '\n' + content.slice(index);
|
|
70
|
+
}
|
|
71
|
+
await fs.writeFile(filepath, content, 'utf-8');
|
|
72
|
+
console.log(`✅ Updated: ${filepath}`);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
console.error(`Failed to update ${filepath}:`, error instanceof Error ? error.message : String(error));
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
60
79
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
/** Flatten literal-only spreads without evaluating user configuration or duplicating effects. */
|
|
3
|
+
export function flattenConfigLiterals(content) {
|
|
4
|
+
const source = ts.createSourceFile('azure-net.config.ts', content, ts.ScriptTarget.Latest, true);
|
|
5
|
+
const exported = source.statements.find(ts.isExportAssignment);
|
|
6
|
+
if (!exported)
|
|
7
|
+
return content;
|
|
8
|
+
const unwrap = (node) => (ts.isParenthesizedExpression(node) ? unwrap(node.expression) : node);
|
|
9
|
+
const root = unwrap(exported.expression);
|
|
10
|
+
if (!ts.isObjectLiteralExpression(root))
|
|
11
|
+
return content;
|
|
12
|
+
let changed = false;
|
|
13
|
+
const flatten = (object) => object.properties.flatMap((property) => {
|
|
14
|
+
if (ts.isSpreadAssignment(property)) {
|
|
15
|
+
const value = unwrap(property.expression);
|
|
16
|
+
if (ts.isObjectLiteralExpression(value) &&
|
|
17
|
+
value.properties.every((p) => !ts.isGetAccessorDeclaration(p) &&
|
|
18
|
+
!ts.isSetAccessorDeclaration(p) &&
|
|
19
|
+
!(p.name && p.name.getText(source).replace(/['"]/g, '') === '__proto__'))) {
|
|
20
|
+
changed = true;
|
|
21
|
+
return flatten(value);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return [property];
|
|
25
|
+
});
|
|
26
|
+
const properties = flatten(root);
|
|
27
|
+
if (!changed)
|
|
28
|
+
return content;
|
|
29
|
+
const names = properties
|
|
30
|
+
.filter((p) => p.name && !ts.isComputedPropertyName(p.name))
|
|
31
|
+
.map((p) => p.name.getText(source).replace(/['"]/g, ''));
|
|
32
|
+
// Duplicate literal keys can produce invalid TypeScript; retain the original spread semantics.
|
|
33
|
+
if (new Set(names).size !== names.length)
|
|
34
|
+
return content;
|
|
35
|
+
const updated = ts.factory.updateObjectLiteralExpression(root, properties);
|
|
36
|
+
const printed = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }).printNode(ts.EmitHint.Expression, updated, source);
|
|
37
|
+
return content.slice(0, root.getStart(source)) + printed + content.slice(root.end);
|
|
38
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
export function mergeAliasesInSvelteConfig(content, aliases) {
|
|
3
|
+
const source = ts.createSourceFile('svelte.config.js', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
|
|
4
|
+
function resolve(expression, seen = new Set()) {
|
|
5
|
+
if (ts.isObjectLiteralExpression(expression))
|
|
6
|
+
return expression;
|
|
7
|
+
if (ts.isParenthesizedExpression(expression) || ts.isAsExpression(expression) || ts.isSatisfiesExpression(expression))
|
|
8
|
+
return resolve(expression.expression, seen);
|
|
9
|
+
if (ts.isIdentifier(expression) && !seen.has(expression.text)) {
|
|
10
|
+
seen.add(expression.text);
|
|
11
|
+
for (const statement of source.statements)
|
|
12
|
+
if (ts.isVariableStatement(statement)) {
|
|
13
|
+
for (const declaration of statement.declarationList.declarations)
|
|
14
|
+
if (ts.isIdentifier(declaration.name) && declaration.name.text === expression.text && declaration.initializer)
|
|
15
|
+
return resolve(declaration.initializer, seen);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
throw new Error('Cannot safely merge Svelte aliases: use an object literal or a local object variable for the config and kit.');
|
|
19
|
+
}
|
|
20
|
+
const exported = source.statements.find(ts.isExportAssignment);
|
|
21
|
+
if (!exported)
|
|
22
|
+
throw new Error('Cannot safely merge Svelte aliases: no default export found.');
|
|
23
|
+
const root = resolve(exported.expression);
|
|
24
|
+
const property = (object, name) => [...object.properties]
|
|
25
|
+
.reverse()
|
|
26
|
+
.find((entry) => entry.name && (ts.isIdentifier(entry.name) || ts.isStringLiteral(entry.name)) && entry.name.text === name);
|
|
27
|
+
const value = (entry) => {
|
|
28
|
+
if (ts.isPropertyAssignment(entry))
|
|
29
|
+
return entry.initializer;
|
|
30
|
+
if (ts.isShorthandPropertyAssignment(entry))
|
|
31
|
+
return entry.name;
|
|
32
|
+
throw new Error('Cannot safely merge Svelte aliases: unsupported property declaration.');
|
|
33
|
+
};
|
|
34
|
+
const insert = (object, text) => {
|
|
35
|
+
const last = object.properties.at(-1);
|
|
36
|
+
const comma = last && !object.properties.hasTrailingComma ? ',' : '';
|
|
37
|
+
// Insert the separator before any trailing line comment.
|
|
38
|
+
let next = content.slice(0, object.end - 1) + `\n${text}\n` + content.slice(object.end - 1);
|
|
39
|
+
if (last && comma)
|
|
40
|
+
next = next.slice(0, last.end) + comma + next.slice(last.end);
|
|
41
|
+
return next;
|
|
42
|
+
};
|
|
43
|
+
const entries = Object.entries(aliases)
|
|
44
|
+
.map(([key, target]) => `${JSON.stringify(key)}: ${JSON.stringify(target)}`)
|
|
45
|
+
.join(',\n');
|
|
46
|
+
const kit = property(root, 'kit');
|
|
47
|
+
if (!kit)
|
|
48
|
+
return insert(root, `kit: { alias: { ${entries} } }`);
|
|
49
|
+
const kitObject = resolve(value(kit));
|
|
50
|
+
const alias = property(kitObject, 'alias');
|
|
51
|
+
if (!alias)
|
|
52
|
+
return insert(kitObject, `alias: { ${entries} }`);
|
|
53
|
+
const expression = value(alias);
|
|
54
|
+
if (ts.isObjectLiteralExpression(expression)) {
|
|
55
|
+
const missing = Object.entries(aliases).filter(([key, target]) => {
|
|
56
|
+
const entry = property(expression, key);
|
|
57
|
+
if (!entry || !ts.isPropertyAssignment(entry) || !ts.isStringLiteral(entry.initializer) || entry.initializer.text !== target)
|
|
58
|
+
return true;
|
|
59
|
+
// Later spreads or computed keys can override an otherwise correct entry.
|
|
60
|
+
return expression.properties
|
|
61
|
+
.slice(expression.properties.indexOf(entry) + 1)
|
|
62
|
+
.some((later) => ts.isSpreadAssignment(later) || (later.name && ts.isComputedPropertyName(later.name)));
|
|
63
|
+
});
|
|
64
|
+
if (!missing.length)
|
|
65
|
+
return content;
|
|
66
|
+
if (missing.every(([key]) => !property(expression, key))) {
|
|
67
|
+
return insert(expression, missing.map(([key, target]) => `${JSON.stringify(key)}: ${JSON.stringify(target)}`).join(',\n'));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Preserve every user expression, spread and comment; fixed architecture aliases win.
|
|
71
|
+
const replacement = `{ ...(${expression.getText(source)}), ${entries} }`;
|
|
72
|
+
if (ts.isShorthandPropertyAssignment(alias))
|
|
73
|
+
return content.slice(0, alias.getStart(source)) + `alias: ${replacement}` + content.slice(alias.end);
|
|
74
|
+
return content.slice(0, expression.getStart(source)) + replacement + content.slice(expression.end);
|
|
75
|
+
}
|
package/src/utils/loadConfig.js
CHANGED
|
@@ -1,46 +1,82 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import fsSync from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
};
|
|
8
|
-
function toObjectFromDefaultExport(content) {
|
|
9
|
-
const source = String(content ?? '').trim();
|
|
10
|
-
if (!source.length) return {};
|
|
11
|
-
const replacedExport = source.replace(/^\s*export\s+default\s+/m, 'return ');
|
|
12
|
-
if (replacedExport === source) return {};
|
|
13
|
-
const withSemicolon = /;\s*$/.test(replacedExport) ? replacedExport : `${replacedExport};`;
|
|
14
|
-
try {
|
|
15
|
-
return new Function(withSemicolon)() ?? {};
|
|
16
|
-
} catch {
|
|
17
|
-
return {};
|
|
18
|
-
}
|
|
19
|
-
}
|
|
4
|
+
import { createRequire } from 'node:module';
|
|
5
|
+
import ts from 'typescript';
|
|
6
|
+
import { flattenConfigLiterals } from './flattenConfig.js';
|
|
20
7
|
export function resolveConfigFile(cwd = process.cwd()) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
8
|
+
for (const type of ['ts', 'js']) {
|
|
9
|
+
const filepath = path.join(cwd, `azure-net.config.${type}`);
|
|
10
|
+
if (fsSync.existsSync(filepath))
|
|
11
|
+
return { exists: true, filepath, type };
|
|
12
|
+
}
|
|
13
|
+
return { exists: false, filepath: path.join(cwd, 'azure-net.config.ts'), type: 'ts' };
|
|
14
|
+
}
|
|
15
|
+
export async function loadUserConfig(cwd = process.cwd()) {
|
|
16
|
+
const { exists, filepath } = resolveConfigFile(cwd);
|
|
17
|
+
if (!exists)
|
|
18
|
+
return {};
|
|
19
|
+
try {
|
|
20
|
+
const source = await fs.readFile(filepath, 'utf8');
|
|
21
|
+
const result = ts.transpileModule(source, {
|
|
22
|
+
fileName: filepath,
|
|
23
|
+
reportDiagnostics: true,
|
|
24
|
+
compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.CommonJS, esModuleInterop: true }
|
|
25
|
+
});
|
|
26
|
+
const errors = result.diagnostics?.filter((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error) ?? [];
|
|
27
|
+
if (errors.length)
|
|
28
|
+
throw new Error(errors.map((error) => ts.flattenDiagnosticMessageText(error.messageText, '\n')).join('\n'));
|
|
29
|
+
const module = { exports: {} };
|
|
30
|
+
new Function('module', 'exports', 'require', '__filename', '__dirname', result.outputText)(module, module.exports, createRequire(filepath), filepath, path.dirname(filepath));
|
|
31
|
+
const config = module.exports.default;
|
|
32
|
+
if (!config || typeof config !== 'object' || Array.isArray(config))
|
|
33
|
+
throw new Error('Expected a default-exported configuration object.');
|
|
34
|
+
const parsed = config;
|
|
35
|
+
if (parsed.presentationWorkspaces !== undefined &&
|
|
36
|
+
(!Array.isArray(parsed.presentationWorkspaces) ||
|
|
37
|
+
parsed.presentationWorkspaces.some((name) => typeof name !== 'string' || !/^[A-Za-z][A-Za-z0-9 _-]*$/.test(name.trim()))))
|
|
38
|
+
throw new Error('presentationWorkspaces must be an array of valid workspace names.');
|
|
39
|
+
if (parsed.runChecksBeforePush !== undefined && typeof parsed.runChecksBeforePush !== 'boolean')
|
|
40
|
+
throw new Error('runChecksBeforePush must be a boolean.');
|
|
41
|
+
return parsed;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
throw new Error(`Unable to load ${filepath}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
45
|
+
}
|
|
30
46
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
/** Add the default without rewriting or dropping legacy/user configuration. */
|
|
48
|
+
export async function initializeConfigDefaults(cwd = process.cwd()) {
|
|
49
|
+
const existing = resolveConfigFile(cwd);
|
|
50
|
+
if (existing.exists) {
|
|
51
|
+
const original = await fs.readFile(existing.filepath, 'utf8');
|
|
52
|
+
const flattened = flattenConfigLiterals(original);
|
|
53
|
+
if (flattened !== original)
|
|
54
|
+
await fs.writeFile(existing.filepath, flattened);
|
|
55
|
+
}
|
|
56
|
+
const config = await loadUserConfig(cwd);
|
|
57
|
+
if (config.runChecksBeforePush !== undefined)
|
|
58
|
+
return;
|
|
59
|
+
const resolved = resolveConfigFile(cwd);
|
|
60
|
+
if (!resolved.exists) {
|
|
61
|
+
await fs.writeFile(resolved.filepath, 'export default {\n\trunChecksBeforePush: true\n};\n', { flag: 'wx' });
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const content = await fs.readFile(resolved.filepath, 'utf8');
|
|
65
|
+
const source = ts.createSourceFile(resolved.filepath, content, ts.ScriptTarget.Latest, true);
|
|
66
|
+
const exported = source.statements.find(ts.isExportAssignment);
|
|
67
|
+
if (!exported || exported.isExportEquals)
|
|
68
|
+
throw new Error(`Cannot initialize ${resolved.filepath}: expected a default export.`);
|
|
69
|
+
const expression = exported.expression;
|
|
70
|
+
let nextContent;
|
|
71
|
+
if (ts.isObjectLiteralExpression(expression)) {
|
|
72
|
+
const last = expression.properties.at(-1);
|
|
73
|
+
nextContent = content.slice(0, expression.end - 1) + '\n\trunChecksBeforePush: true\n' + content.slice(expression.end - 1);
|
|
74
|
+
if (last && !expression.properties.hasTrailingComma)
|
|
75
|
+
nextContent = nextContent.slice(0, last.end) + ',' + nextContent.slice(last.end);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
const replacement = `{ ...(${expression.getText(source)}), runChecksBeforePush: true }`;
|
|
79
|
+
nextContent = content.slice(0, expression.getStart(source)) + replacement + content.slice(expression.end);
|
|
80
|
+
}
|
|
81
|
+
await fs.writeFile(resolved.filepath, nextContent, 'utf8');
|
|
46
82
|
}
|