@azure-net/cli 4.1.0 → 5.0.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.
- package/bin/azure-net.js +17 -17
- package/bin/azure-net.ts +9 -17
- package/package.json +1 -1
- package/src/checks/checkHooks.js +22 -0
- package/src/checks/checkHooks.ts +22 -0
- package/src/checks/checkImports.js +147 -0
- package/src/checks/checkImports.ts +28 -1
- package/src/checks/checkInternal.js +5 -4
- package/src/checks/checkInternal.ts +2 -0
- package/src/checks/checkLayerBoundaries.js +63 -38
- package/src/checks/checkLayerBoundaries.ts +48 -6
- package/src/checks/index.js +3 -0
- package/src/checks/index.ts +1 -0
- package/src/install/installFresh.js +2 -10
- package/src/install/installFresh.ts +2 -10
- package/src/scaffolds/createProgram.js +19 -0
- package/src/scaffolds/createProgram.ts +19 -0
- package/src/scaffolds/index.js +1 -2
- package/src/scaffolds/index.ts +1 -2
- package/src/scaffolds/createCoreMiddleware.js +0 -27
- package/src/scaffolds/createCoreMiddleware.ts +0 -27
- package/src/scaffolds/createMiddleware.js +0 -105
- package/src/scaffolds/createMiddleware.ts +0 -105
package/bin/azure-net.js
CHANGED
|
@@ -5,7 +5,7 @@ import { addLint } from '../src/plugins/index.js';
|
|
|
5
5
|
|
|
6
6
|
const program = new Command();
|
|
7
7
|
|
|
8
|
-
program.name('azure-net').description('Azure-Net CLI generator').version('
|
|
8
|
+
program.name('azure-net').description('Azure-Net CLI generator').version('4.1.0');
|
|
9
9
|
|
|
10
10
|
const addCommand = program.command('add').description('Add project integrations and plugins');
|
|
11
11
|
const createCommand = program.command('create').description('Create project boilerplate artifacts');
|
|
@@ -28,14 +28,6 @@ createCommand
|
|
|
28
28
|
await handler();
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
createCommand
|
|
32
|
-
.command('middleware-manager')
|
|
33
|
-
.description('Create middleware manager in src/core/middleware-manager')
|
|
34
|
-
.action(async () => {
|
|
35
|
-
const handler = (await import('../src/scaffolds/createCoreMiddleware.js')).default;
|
|
36
|
-
await handler();
|
|
37
|
-
});
|
|
38
|
-
|
|
39
31
|
createCommand
|
|
40
32
|
.command('translation-manager')
|
|
41
33
|
.description('Create translation manager in src/core/translation')
|
|
@@ -60,14 +52,6 @@ createCommand
|
|
|
60
52
|
await handler();
|
|
61
53
|
});
|
|
62
54
|
|
|
63
|
-
createCommand
|
|
64
|
-
.command('middleware')
|
|
65
|
-
.description('Create middleware in src/core/middleware-manager/middlewares')
|
|
66
|
-
.action(async () => {
|
|
67
|
-
const handler = (await import('../src/scaffolds/createMiddleware.js')).default;
|
|
68
|
-
await handler();
|
|
69
|
-
});
|
|
70
|
-
|
|
71
55
|
createCommand
|
|
72
56
|
.command('response')
|
|
73
57
|
.description('Create response in src/core/response')
|
|
@@ -257,6 +241,22 @@ checkCommand
|
|
|
257
241
|
await handler();
|
|
258
242
|
});
|
|
259
243
|
|
|
244
|
+
checkCommand
|
|
245
|
+
.command('imports')
|
|
246
|
+
.description('Check import extensions and import group order in src')
|
|
247
|
+
.action(async () => {
|
|
248
|
+
const handler = (await import('../src/checks/checkImports.js')).default;
|
|
249
|
+
await handler();
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
checkCommand
|
|
253
|
+
.command('hooks')
|
|
254
|
+
.description('Check that manual SvelteKit hooks are not present')
|
|
255
|
+
.action(async () => {
|
|
256
|
+
const handler = (await import('../src/checks/checkHooks.js')).default;
|
|
257
|
+
await handler();
|
|
258
|
+
});
|
|
259
|
+
|
|
260
260
|
checkCommand
|
|
261
261
|
.command('layer-boundaries')
|
|
262
262
|
.description('Check layer boundaries between contexts by aliases')
|
package/bin/azure-net.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { addLint } from '../src/plugins/index.js';
|
|
|
5
5
|
|
|
6
6
|
const program = new Command();
|
|
7
7
|
|
|
8
|
-
program.name('azure-net').description('Azure-Net CLI generator').version('4.
|
|
8
|
+
program.name('azure-net').description('Azure-Net CLI generator').version('4.1.0');
|
|
9
9
|
|
|
10
10
|
const addCommand = program.command('add').description('Add project integrations and plugins');
|
|
11
11
|
const createCommand = program.command('create').description('Create project boilerplate artifacts');
|
|
@@ -28,14 +28,6 @@ createCommand
|
|
|
28
28
|
await handler();
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
createCommand
|
|
32
|
-
.command('middleware-manager')
|
|
33
|
-
.description('Create middleware manager in src/core/middleware-manager')
|
|
34
|
-
.action(async () => {
|
|
35
|
-
const handler = (await import('../src/scaffolds/createCoreMiddleware.js')).default;
|
|
36
|
-
await handler();
|
|
37
|
-
});
|
|
38
|
-
|
|
39
31
|
createCommand
|
|
40
32
|
.command('translation-manager')
|
|
41
33
|
.description('Create translation manager in src/core/translation')
|
|
@@ -60,14 +52,6 @@ createCommand
|
|
|
60
52
|
await handler();
|
|
61
53
|
});
|
|
62
54
|
|
|
63
|
-
createCommand
|
|
64
|
-
.command('middleware')
|
|
65
|
-
.description('Create middleware in src/core/middleware-manager/middlewares')
|
|
66
|
-
.action(async () => {
|
|
67
|
-
const handler = (await import('../src/scaffolds/createMiddleware.js')).default;
|
|
68
|
-
await handler();
|
|
69
|
-
});
|
|
70
|
-
|
|
71
55
|
createCommand
|
|
72
56
|
.command('response')
|
|
73
57
|
.description('Create response in src/core/response')
|
|
@@ -265,6 +249,14 @@ checkCommand
|
|
|
265
249
|
await handler();
|
|
266
250
|
});
|
|
267
251
|
|
|
252
|
+
checkCommand
|
|
253
|
+
.command('hooks')
|
|
254
|
+
.description('Check that manual SvelteKit hooks are not present')
|
|
255
|
+
.action(async () => {
|
|
256
|
+
const handler = (await import('../src/checks/checkHooks.js')).default;
|
|
257
|
+
await handler();
|
|
258
|
+
});
|
|
259
|
+
|
|
268
260
|
checkCommand
|
|
269
261
|
.command('layer-boundaries')
|
|
270
262
|
.description('Check layer boundaries between contexts by aliases')
|
package/package.json
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
const forbiddenHooks = ['hooks.server.ts', 'hooks.server.js', 'hooks.client.ts', 'hooks.client.js'];
|
|
5
|
+
|
|
6
|
+
export default async function checkHooks() {
|
|
7
|
+
const srcPath = path.join(process.cwd(), 'src');
|
|
8
|
+
const found = forbiddenHooks.map((filename) => path.join(srcPath, filename)).filter((filepath) => fs.existsSync(filepath));
|
|
9
|
+
|
|
10
|
+
if (!found.length) {
|
|
11
|
+
console.log('✅ No manual SvelteKit hooks found.');
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
console.error('❌ Manual SvelteKit hooks are not allowed with @azure-net/kit AzureNetPlugin.');
|
|
16
|
+
console.error('Move lifecycle code to src/program.ts through createApp(). Found:');
|
|
17
|
+
for (const filepath of found) {
|
|
18
|
+
console.error(` - ${path.relative(process.cwd(), filepath)}`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
process.exitCode = 1;
|
|
22
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
const forbiddenHooks = ['hooks.server.ts', 'hooks.server.js', 'hooks.client.ts', 'hooks.client.js'];
|
|
5
|
+
|
|
6
|
+
export default async function checkHooks(): Promise<void> {
|
|
7
|
+
const srcPath = path.join(process.cwd(), 'src');
|
|
8
|
+
const found = forbiddenHooks.map((filename) => path.join(srcPath, filename)).filter((filepath) => fs.existsSync(filepath));
|
|
9
|
+
|
|
10
|
+
if (!found.length) {
|
|
11
|
+
console.log('✅ No manual SvelteKit hooks found.');
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
console.error('❌ Manual SvelteKit hooks are not allowed with @azure-net/kit AzureNetPlugin.');
|
|
16
|
+
console.error('Move lifecycle code to src/program.ts through createApp(). Found:');
|
|
17
|
+
for (const filepath of found) {
|
|
18
|
+
console.error(` - ${path.relative(process.cwd(), filepath)}`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
process.exitCode = 1;
|
|
22
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const TARGET_EXTENSIONS = new Set(['.ts', '.js', '.svelte']);
|
|
4
|
+
const IGNORED_DIRS = new Set(['node_modules', 'dist', '.svelte-kit', '.git']);
|
|
5
|
+
const IMPORT_DECLARATION_PATTERN = /(^|\n)\s*import\s+(?:[\s\S]*?\s+from\s*)?['"]([^'"]+)['"]\s*;?/g;
|
|
6
|
+
const EXPORT_DECLARATION_PATTERN = /(^|\n)\s*export\s+(?:type\s+)?(?:[\s\S]*?\s+from\s*)['"]([^'"]+)['"]\s*;?/g;
|
|
7
|
+
const FORBIDDEN_IMPORT_EXTENSION_PATTERN = /\.(?:ts|js)(?:$|[?#])/;
|
|
8
|
+
const toPosixPath = (value) => value.split(path.sep).join('/');
|
|
9
|
+
const getRelativePath = (filePath) => toPosixPath(path.relative(process.cwd(), filePath));
|
|
10
|
+
const findLineByIndex = (content, index) => {
|
|
11
|
+
let line = 1;
|
|
12
|
+
for (let i = 0; i < index; i += 1) {
|
|
13
|
+
if (content[i] === '\n') line += 1;
|
|
14
|
+
}
|
|
15
|
+
return line;
|
|
16
|
+
};
|
|
17
|
+
const listFiles = async (rootDir) => {
|
|
18
|
+
const files = [];
|
|
19
|
+
const stack = [rootDir];
|
|
20
|
+
while (stack.length) {
|
|
21
|
+
const current = stack.pop();
|
|
22
|
+
if (!current) continue;
|
|
23
|
+
let entries = [];
|
|
24
|
+
try {
|
|
25
|
+
entries = await fs.readdir(current, { withFileTypes: true });
|
|
26
|
+
} catch {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
for (const entry of entries) {
|
|
30
|
+
if (entry.isDirectory()) {
|
|
31
|
+
if (!IGNORED_DIRS.has(entry.name)) {
|
|
32
|
+
stack.push(path.join(current, entry.name));
|
|
33
|
+
}
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (!entry.isFile()) continue;
|
|
37
|
+
const filePath = path.join(current, entry.name);
|
|
38
|
+
if (!TARGET_EXTENSIONS.has(path.extname(filePath))) continue;
|
|
39
|
+
files.push(filePath);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return files;
|
|
43
|
+
};
|
|
44
|
+
const getImportKind = (raw) => {
|
|
45
|
+
const normalized = raw.trim();
|
|
46
|
+
if (/^import\s+type\b/.test(normalized)) return 'type';
|
|
47
|
+
if (/^import\s*['"]/.test(normalized)) return 'side-effect';
|
|
48
|
+
return 'value';
|
|
49
|
+
};
|
|
50
|
+
const getKindOrder = (kind) => {
|
|
51
|
+
if (kind === 'value') return 0;
|
|
52
|
+
if (kind === 'type') return 1;
|
|
53
|
+
return 2;
|
|
54
|
+
};
|
|
55
|
+
const extractImports = (content) => {
|
|
56
|
+
const imports = [];
|
|
57
|
+
const regex = new RegExp(IMPORT_DECLARATION_PATTERN.source, 'g');
|
|
58
|
+
let match;
|
|
59
|
+
while ((match = regex.exec(content)) !== null) {
|
|
60
|
+
const raw = match[0].trim();
|
|
61
|
+
const source = match[2];
|
|
62
|
+
if (!source) continue;
|
|
63
|
+
imports.push({
|
|
64
|
+
kind: getImportKind(raw),
|
|
65
|
+
line: findLineByIndex(content, match.index + (match[1] === '\n' ? 1 : 0)),
|
|
66
|
+
source,
|
|
67
|
+
raw
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return imports.sort((a, b) => a.line - b.line);
|
|
71
|
+
};
|
|
72
|
+
const extractExportSources = (content) => {
|
|
73
|
+
const exports = [];
|
|
74
|
+
const regex = new RegExp(EXPORT_DECLARATION_PATTERN.source, 'g');
|
|
75
|
+
let match;
|
|
76
|
+
while ((match = regex.exec(content)) !== null) {
|
|
77
|
+
const source = match[2];
|
|
78
|
+
if (!source) continue;
|
|
79
|
+
exports.push({
|
|
80
|
+
source,
|
|
81
|
+
line: findLineByIndex(content, match.index + (match[1] === '\n' ? 1 : 0))
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return exports;
|
|
85
|
+
};
|
|
86
|
+
const hasForbiddenExtension = (source) => {
|
|
87
|
+
const sourceWithoutQuery = source.split('?')[0] ?? source;
|
|
88
|
+
return FORBIDDEN_IMPORT_EXTENSION_PATTERN.test(sourceWithoutQuery);
|
|
89
|
+
};
|
|
90
|
+
const collectIssuesForFile = (filePath, content) => {
|
|
91
|
+
const issues = [];
|
|
92
|
+
const imports = extractImports(content);
|
|
93
|
+
let lastKindOrder = 0;
|
|
94
|
+
for (const importEntry of imports) {
|
|
95
|
+
if (hasForbiddenExtension(importEntry.source)) {
|
|
96
|
+
issues.push({
|
|
97
|
+
file: filePath,
|
|
98
|
+
line: importEntry.line,
|
|
99
|
+
message: `Import "${importEntry.source}" must not include .ts/.js extension`
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
const currentOrder = getKindOrder(importEntry.kind);
|
|
103
|
+
if (currentOrder < lastKindOrder) {
|
|
104
|
+
issues.push({
|
|
105
|
+
file: filePath,
|
|
106
|
+
line: importEntry.line,
|
|
107
|
+
message: 'Import order violation: value imports first, then type imports, then side-effect imports'
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
lastKindOrder = Math.max(lastKindOrder, currentOrder);
|
|
111
|
+
}
|
|
112
|
+
for (const exportEntry of extractExportSources(content)) {
|
|
113
|
+
if (!hasForbiddenExtension(exportEntry.source)) continue;
|
|
114
|
+
issues.push({
|
|
115
|
+
file: filePath,
|
|
116
|
+
line: exportEntry.line,
|
|
117
|
+
message: `Export source "${exportEntry.source}" must not include .ts/.js extension`
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
return issues;
|
|
121
|
+
};
|
|
122
|
+
export default async function checkImports() {
|
|
123
|
+
const srcRoot = path.join(process.cwd(), 'src');
|
|
124
|
+
try {
|
|
125
|
+
await fs.access(srcRoot);
|
|
126
|
+
} catch {
|
|
127
|
+
console.error('❌ src folder not found.');
|
|
128
|
+
process.exitCode = 1;
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const issues = [];
|
|
132
|
+
const files = await listFiles(srcRoot);
|
|
133
|
+
for (const filePath of files) {
|
|
134
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
135
|
+
issues.push(...collectIssuesForFile(filePath, content));
|
|
136
|
+
}
|
|
137
|
+
if (!issues.length) {
|
|
138
|
+
console.log('✅ Imports check passed: extensions and import groups are valid.');
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
console.error('❌ Imports check failed:');
|
|
142
|
+
for (const issue of issues) {
|
|
143
|
+
console.error(`• ${getRelativePath(issue.file)}:${issue.line} — ${issue.message}`);
|
|
144
|
+
}
|
|
145
|
+
process.exitCode = 1;
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=checkImports.js.map
|
|
@@ -19,7 +19,8 @@ type ImportEntry = {
|
|
|
19
19
|
const TARGET_EXTENSIONS = new Set(['.ts', '.js', '.svelte']);
|
|
20
20
|
const IGNORED_DIRS = new Set(['node_modules', 'dist', '.svelte-kit', '.git']);
|
|
21
21
|
const IMPORT_DECLARATION_PATTERN = /(^|\n)\s*import\s+(?:[\s\S]*?\s+from\s*)?['"]([^'"]+)['"]\s*;?/g;
|
|
22
|
-
const
|
|
22
|
+
const EXPORT_DECLARATION_PATTERN = /(^|\n)\s*export\s+(?:type\s+)?(?:[\s\S]*?\s+from\s*)['"]([^'"]+)['"]\s*;?/g;
|
|
23
|
+
const FORBIDDEN_IMPORT_EXTENSION_PATTERN = /\.(?:ts|js)(?:$|[?#])/;
|
|
23
24
|
|
|
24
25
|
const toPosixPath = (value: string): string => value.split(path.sep).join('/');
|
|
25
26
|
|
|
@@ -100,6 +101,23 @@ const extractImports = (content: string): ImportEntry[] => {
|
|
|
100
101
|
return imports.sort((a, b) => a.line - b.line);
|
|
101
102
|
};
|
|
102
103
|
|
|
104
|
+
const extractExportSources = (content: string): Array<{ source: string; line: number }> => {
|
|
105
|
+
const exports: Array<{ source: string; line: number }> = [];
|
|
106
|
+
const regex = new RegExp(EXPORT_DECLARATION_PATTERN.source, 'g');
|
|
107
|
+
let match: RegExpExecArray | null;
|
|
108
|
+
|
|
109
|
+
while ((match = regex.exec(content)) !== null) {
|
|
110
|
+
const source = match[2];
|
|
111
|
+
if (!source) continue;
|
|
112
|
+
exports.push({
|
|
113
|
+
source,
|
|
114
|
+
line: findLineByIndex(content, match.index + (match[1] === '\n' ? 1 : 0))
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return exports;
|
|
119
|
+
};
|
|
120
|
+
|
|
103
121
|
const hasForbiddenExtension = (source: string): boolean => {
|
|
104
122
|
const sourceWithoutQuery = source.split('?')[0] ?? source;
|
|
105
123
|
return FORBIDDEN_IMPORT_EXTENSION_PATTERN.test(sourceWithoutQuery);
|
|
@@ -130,6 +148,15 @@ const collectIssuesForFile = (filePath: string, content: string): ImportIssue[]
|
|
|
130
148
|
lastKindOrder = Math.max(lastKindOrder, currentOrder);
|
|
131
149
|
}
|
|
132
150
|
|
|
151
|
+
for (const exportEntry of extractExportSources(content)) {
|
|
152
|
+
if (!hasForbiddenExtension(exportEntry.source)) continue;
|
|
153
|
+
issues.push({
|
|
154
|
+
file: filePath,
|
|
155
|
+
line: exportEntry.line,
|
|
156
|
+
message: `Export source "${exportEntry.source}" must not include .ts/.js extension`
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
133
160
|
return issues;
|
|
134
161
|
};
|
|
135
162
|
|
|
@@ -4,26 +4,26 @@ import checkProviderGraph from './checkProviderGraph.js';
|
|
|
4
4
|
import checkDomain from './checkDomain.js';
|
|
5
5
|
import checkLayerBoundaries from './checkLayerBoundaries.js';
|
|
6
6
|
import checkFoldersStructure from './checkFoldersStructure.js';
|
|
7
|
-
|
|
7
|
+
import checkImports from './checkImports.js';
|
|
8
|
+
import checkHooks from './checkHooks.js';
|
|
8
9
|
const tasks = [
|
|
9
10
|
{ name: 'presenter-names', run: checkPresenterNames },
|
|
10
11
|
{ name: 'provider-names', run: checkProviderNames },
|
|
11
12
|
{ name: 'provider-graph', run: checkProviderGraph },
|
|
12
13
|
{ name: 'domain', run: checkDomain },
|
|
14
|
+
{ name: 'imports', run: checkImports },
|
|
15
|
+
{ name: 'hooks', run: checkHooks },
|
|
13
16
|
{ name: 'layer-boundaries', run: checkLayerBoundaries },
|
|
14
17
|
{ name: 'folders-structure', run: checkFoldersStructure }
|
|
15
18
|
];
|
|
16
|
-
|
|
17
19
|
const runTask = async (task) => {
|
|
18
20
|
console.log(`\n▶ Running check ${task.name}...`);
|
|
19
21
|
process.exitCode = 0;
|
|
20
22
|
await task.run();
|
|
21
|
-
|
|
22
23
|
if ((process.exitCode ?? 0) !== 0) {
|
|
23
24
|
throw new Error(`Check "${task.name}" failed`);
|
|
24
25
|
}
|
|
25
26
|
};
|
|
26
|
-
|
|
27
27
|
export default async function checkInternal() {
|
|
28
28
|
try {
|
|
29
29
|
for (const task of tasks) {
|
|
@@ -35,3 +35,4 @@ export default async function checkInternal() {
|
|
|
35
35
|
process.exitCode = 1;
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
+
//# sourceMappingURL=checkInternal.js.map
|
|
@@ -5,6 +5,7 @@ import checkDomain from './checkDomain.js';
|
|
|
5
5
|
import checkLayerBoundaries from './checkLayerBoundaries.js';
|
|
6
6
|
import checkFoldersStructure from './checkFoldersStructure.js';
|
|
7
7
|
import checkImports from './checkImports.js';
|
|
8
|
+
import checkHooks from './checkHooks.js';
|
|
8
9
|
|
|
9
10
|
type CheckTask = {
|
|
10
11
|
name: string;
|
|
@@ -17,6 +18,7 @@ const tasks: CheckTask[] = [
|
|
|
17
18
|
{ name: 'provider-graph', run: checkProviderGraph },
|
|
18
19
|
{ name: 'domain', run: checkDomain },
|
|
19
20
|
{ name: 'imports', run: checkImports },
|
|
21
|
+
{ name: 'hooks', run: checkHooks },
|
|
20
22
|
{ name: 'layer-boundaries', run: checkLayerBoundaries },
|
|
21
23
|
{ name: 'folders-structure', run: checkFoldersStructure }
|
|
22
24
|
];
|
|
@@ -2,22 +2,17 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { loadUserConfig } from '../utils/loadConfig.js';
|
|
4
4
|
import { normalizeContexts } from '../init/initAliases.js';
|
|
5
|
-
|
|
6
5
|
const TARGET_EXTENSIONS = new Set(['.ts', '.js', '.svelte']);
|
|
7
6
|
const IGNORED_DIRS = new Set(['node_modules', 'dist', '.svelte-kit', '.git']);
|
|
8
|
-
|
|
9
7
|
const IMPORT_FROM_PATTERN = /\bimport\s+(?:type\s+)?[\s\S]*?\sfrom\s*['"]([^'"]+)['"]/g;
|
|
10
8
|
const EXPORT_FROM_PATTERN = /\bexport\s+(?:type\s+)?\{[\s\S]*?\}\sfrom\s*['"]([^'"]+)['"]/g;
|
|
11
9
|
const DYNAMIC_IMPORT_PATTERN = /\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
12
|
-
|
|
13
10
|
const toPosixPath = (value) => value.split(path.sep).join('/');
|
|
14
|
-
|
|
15
11
|
const normalizeAlias = (value, fallback = '') => {
|
|
16
12
|
const raw = String(value ?? '').trim();
|
|
17
13
|
if (!raw.length) return fallback;
|
|
18
14
|
return raw.startsWith('$') ? raw : `$${raw}`;
|
|
19
15
|
};
|
|
20
|
-
|
|
21
16
|
const findLineByIndex = (content, index) => {
|
|
22
17
|
let line = 1;
|
|
23
18
|
for (let i = 0; i < index; i += 1) {
|
|
@@ -25,24 +20,19 @@ const findLineByIndex = (content, index) => {
|
|
|
25
20
|
}
|
|
26
21
|
return line;
|
|
27
22
|
};
|
|
28
|
-
|
|
29
23
|
const getRelativePath = (filePath) => toPosixPath(path.relative(process.cwd(), filePath));
|
|
30
|
-
|
|
31
24
|
const listFiles = async (rootDir) => {
|
|
32
25
|
const files = [];
|
|
33
26
|
const stack = [rootDir];
|
|
34
|
-
|
|
35
27
|
while (stack.length) {
|
|
36
28
|
const current = stack.pop();
|
|
37
29
|
if (!current) continue;
|
|
38
|
-
|
|
39
30
|
let entries = [];
|
|
40
31
|
try {
|
|
41
32
|
entries = await fs.readdir(current, { withFileTypes: true });
|
|
42
33
|
} catch {
|
|
43
34
|
continue;
|
|
44
35
|
}
|
|
45
|
-
|
|
46
36
|
for (const entry of entries) {
|
|
47
37
|
if (entry.isDirectory()) {
|
|
48
38
|
if (!IGNORED_DIRS.has(entry.name)) {
|
|
@@ -50,21 +40,17 @@ const listFiles = async (rootDir) => {
|
|
|
50
40
|
}
|
|
51
41
|
continue;
|
|
52
42
|
}
|
|
53
|
-
|
|
54
43
|
if (!entry.isFile()) continue;
|
|
55
44
|
const filePath = path.join(current, entry.name);
|
|
56
45
|
if (!TARGET_EXTENSIONS.has(path.extname(filePath))) continue;
|
|
57
46
|
files.push(filePath);
|
|
58
47
|
}
|
|
59
48
|
}
|
|
60
|
-
|
|
61
49
|
return files;
|
|
62
50
|
};
|
|
63
|
-
|
|
64
51
|
const extractImports = (content) => {
|
|
65
52
|
const matches = [];
|
|
66
53
|
const patterns = [IMPORT_FROM_PATTERN, EXPORT_FROM_PATTERN, DYNAMIC_IMPORT_PATTERN];
|
|
67
|
-
|
|
68
54
|
for (const pattern of patterns) {
|
|
69
55
|
const regex = new RegExp(pattern.source, 'g');
|
|
70
56
|
let match;
|
|
@@ -74,10 +60,8 @@ const extractImports = (content) => {
|
|
|
74
60
|
matches.push({ value, line: findLineByIndex(content, match.index) });
|
|
75
61
|
}
|
|
76
62
|
}
|
|
77
|
-
|
|
78
63
|
return matches;
|
|
79
64
|
};
|
|
80
|
-
|
|
81
65
|
const resolveAliasFromImport = (importPath, aliases) => {
|
|
82
66
|
const sorted = [...aliases].sort((a, b) => b.length - a.length);
|
|
83
67
|
for (const alias of sorted) {
|
|
@@ -87,74 +71,117 @@ const resolveAliasFromImport = (importPath, aliases) => {
|
|
|
87
71
|
}
|
|
88
72
|
return null;
|
|
89
73
|
};
|
|
90
|
-
|
|
74
|
+
const resolveContextFromRelativeImport = (filePath, importPath, contexts) => {
|
|
75
|
+
if (!importPath.startsWith('.')) return null;
|
|
76
|
+
const absoluteImportPath = path.resolve(path.dirname(filePath), importPath);
|
|
77
|
+
const appRoot = path.join(process.cwd(), 'src', 'app');
|
|
78
|
+
for (const context of contexts) {
|
|
79
|
+
const contextRoot = path.join(appRoot, context.name);
|
|
80
|
+
if (absoluteImportPath === contextRoot || absoluteImportPath.startsWith(`${contextRoot}${path.sep}`)) {
|
|
81
|
+
return context;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
};
|
|
91
86
|
const isSharedLikeContext = (contextName, alias) => {
|
|
92
87
|
const normalizedName = contextName.toLowerCase();
|
|
93
88
|
const normalizedAlias = alias.toLowerCase();
|
|
94
89
|
return normalizedName.includes('shared') || normalizedAlias.includes('shared');
|
|
95
90
|
};
|
|
96
|
-
|
|
97
91
|
export default async function checkLayerBoundaries() {
|
|
98
92
|
const config = await loadUserConfig();
|
|
99
93
|
const contexts = normalizeContexts(config.contexts);
|
|
100
|
-
|
|
101
94
|
if (!contexts.length) {
|
|
102
95
|
console.error('❌ No contexts found in azure-net.config.ts/js. Fill `contexts` first.');
|
|
103
96
|
process.exitCode = 1;
|
|
104
97
|
return;
|
|
105
98
|
}
|
|
106
|
-
|
|
107
99
|
const coreAlias = normalizeAlias(config.coreAlias, '$core');
|
|
108
100
|
const sharedAliasRaw = normalizeAlias(config.sharedAlias, '');
|
|
109
101
|
const sharedAlias = sharedAliasRaw.length ? sharedAliasRaw : null;
|
|
110
|
-
|
|
111
102
|
const aliasToContext = new Map();
|
|
112
103
|
for (const context of contexts) {
|
|
113
104
|
aliasToContext.set(context.alias, context);
|
|
114
105
|
}
|
|
115
|
-
|
|
116
106
|
const knownAliases = [coreAlias, ...contexts.map((context) => context.alias)];
|
|
117
107
|
if (sharedAlias && !knownAliases.includes(sharedAlias)) {
|
|
118
108
|
knownAliases.push(sharedAlias);
|
|
119
109
|
}
|
|
120
|
-
|
|
121
110
|
const issues = [];
|
|
122
|
-
|
|
111
|
+
const coreRoot = path.join(process.cwd(), 'src', 'core');
|
|
112
|
+
try {
|
|
113
|
+
await fs.access(coreRoot);
|
|
114
|
+
const coreFiles = await listFiles(coreRoot);
|
|
115
|
+
for (const filePath of coreFiles) {
|
|
116
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
117
|
+
const imports = extractImports(content);
|
|
118
|
+
for (const entry of imports) {
|
|
119
|
+
const targetAlias = resolveAliasFromImport(entry.value, knownAliases);
|
|
120
|
+
if (targetAlias && targetAlias !== coreAlias) {
|
|
121
|
+
const targetContext = aliasToContext.get(targetAlias);
|
|
122
|
+
issues.push({
|
|
123
|
+
file: filePath,
|
|
124
|
+
line: entry.line,
|
|
125
|
+
message: targetContext
|
|
126
|
+
? `Layer boundary violation: core cannot import from context "${targetContext.name}" (${targetAlias})`
|
|
127
|
+
: `Layer boundary violation: core cannot import from "${targetAlias}"`
|
|
128
|
+
});
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const relativeTargetContext = resolveContextFromRelativeImport(filePath, entry.value, contexts);
|
|
132
|
+
if (!relativeTargetContext) continue;
|
|
133
|
+
issues.push({
|
|
134
|
+
file: filePath,
|
|
135
|
+
line: entry.line,
|
|
136
|
+
message: `Layer boundary violation: core cannot import from context "${relativeTargetContext.name}" via relative path`
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} catch {
|
|
141
|
+
// Core folder is optional for the check.
|
|
142
|
+
}
|
|
123
143
|
for (const context of contexts) {
|
|
124
|
-
const
|
|
125
|
-
|
|
144
|
+
const contextRoot = path.join(process.cwd(), 'src', 'app', context.name);
|
|
126
145
|
try {
|
|
127
|
-
await fs.access(
|
|
146
|
+
await fs.access(contextRoot);
|
|
128
147
|
} catch {
|
|
129
148
|
continue;
|
|
130
149
|
}
|
|
131
|
-
|
|
132
|
-
const files = await listFiles(layersRoot);
|
|
150
|
+
const files = await listFiles(contextRoot);
|
|
133
151
|
const allowedAliases = new Set([context.alias, coreAlias]);
|
|
134
|
-
if (sharedAlias) {
|
|
152
|
+
if (sharedAlias && context.alias !== sharedAlias) {
|
|
135
153
|
allowedAliases.add(sharedAlias);
|
|
136
154
|
}
|
|
137
|
-
|
|
138
155
|
for (const filePath of files) {
|
|
139
156
|
const content = await fs.readFile(filePath, 'utf-8');
|
|
140
157
|
const imports = extractImports(content);
|
|
141
|
-
|
|
142
158
|
for (const entry of imports) {
|
|
143
159
|
const targetAlias = resolveAliasFromImport(entry.value, knownAliases);
|
|
144
160
|
if (!targetAlias) continue;
|
|
145
161
|
if (targetAlias === coreAlias) continue;
|
|
146
162
|
if (allowedAliases.has(targetAlias)) continue;
|
|
147
|
-
|
|
148
163
|
const targetContext = aliasToContext.get(targetAlias);
|
|
149
164
|
const baseMessage = targetContext
|
|
150
165
|
? `Layer boundary violation: context "${context.name}" cannot import from context "${targetContext.name}" (${targetAlias})`
|
|
151
166
|
: `Layer boundary violation: context "${context.name}" cannot import from "${targetAlias}"`;
|
|
152
|
-
|
|
153
167
|
const withSharedHint =
|
|
154
168
|
!sharedAlias && targetContext && isSharedLikeContext(targetContext.name, targetAlias)
|
|
155
169
|
? `${baseMessage}. sharedAlias is not configured in azure-net.config.ts/js; if this context is shared, set sharedAlias explicitly`
|
|
156
170
|
: baseMessage;
|
|
157
|
-
|
|
171
|
+
issues.push({
|
|
172
|
+
file: filePath,
|
|
173
|
+
line: entry.line,
|
|
174
|
+
message: withSharedHint
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
for (const entry of imports) {
|
|
178
|
+
const relativeTargetContext = resolveContextFromRelativeImport(filePath, entry.value, contexts);
|
|
179
|
+
if (!relativeTargetContext || relativeTargetContext.name === context.name) continue;
|
|
180
|
+
const baseMessage = `Layer boundary violation: context "${context.name}" cannot import from context "${relativeTargetContext.name}" via relative path`;
|
|
181
|
+
const withSharedHint =
|
|
182
|
+
!sharedAlias && isSharedLikeContext(relativeTargetContext.name, relativeTargetContext.alias)
|
|
183
|
+
? `${baseMessage}. sharedAlias is not configured in azure-net.config.ts/js; if this context is shared, set sharedAlias explicitly`
|
|
184
|
+
: baseMessage;
|
|
158
185
|
issues.push({
|
|
159
186
|
file: filePath,
|
|
160
187
|
line: entry.line,
|
|
@@ -163,16 +190,14 @@ export default async function checkLayerBoundaries() {
|
|
|
163
190
|
}
|
|
164
191
|
}
|
|
165
192
|
}
|
|
166
|
-
|
|
167
193
|
if (!issues.length) {
|
|
168
194
|
console.log('✅ Layer boundaries check passed.');
|
|
169
195
|
return;
|
|
170
196
|
}
|
|
171
|
-
|
|
172
197
|
console.error('❌ Layer boundaries check failed:');
|
|
173
198
|
for (const issue of issues) {
|
|
174
199
|
console.error(`• ${getRelativePath(issue.file)}:${issue.line} — ${issue.message}`);
|
|
175
200
|
}
|
|
176
|
-
|
|
177
201
|
process.exitCode = 1;
|
|
178
202
|
}
|
|
203
|
+
//# sourceMappingURL=checkLayerBoundaries.js.map
|
|
@@ -99,6 +99,22 @@ const resolveAliasFromImport = (importPath: string, aliases: string[]): string |
|
|
|
99
99
|
return null;
|
|
100
100
|
};
|
|
101
101
|
|
|
102
|
+
const resolveContextFromRelativeImport = (filePath: string, importPath: string, contexts: ContextConfig[]): ContextConfig | null => {
|
|
103
|
+
if (!importPath.startsWith('.')) return null;
|
|
104
|
+
|
|
105
|
+
const absoluteImportPath = path.resolve(path.dirname(filePath), importPath);
|
|
106
|
+
const appRoot = path.join(process.cwd(), 'src', 'app');
|
|
107
|
+
|
|
108
|
+
for (const context of contexts) {
|
|
109
|
+
const contextRoot = path.join(appRoot, context.name);
|
|
110
|
+
if (absoluteImportPath === contextRoot || absoluteImportPath.startsWith(`${contextRoot}${path.sep}`)) {
|
|
111
|
+
return context;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return null;
|
|
116
|
+
};
|
|
117
|
+
|
|
102
118
|
const isSharedLikeContext = (contextName: string, alias: string): boolean => {
|
|
103
119
|
const normalizedName = contextName.toLowerCase();
|
|
104
120
|
const normalizedAlias = alias.toLowerCase();
|
|
@@ -141,16 +157,25 @@ export default async function checkLayerBoundaries(): Promise<void> {
|
|
|
141
157
|
|
|
142
158
|
for (const entry of imports) {
|
|
143
159
|
const targetAlias = resolveAliasFromImport(entry.value, knownAliases);
|
|
144
|
-
if (
|
|
145
|
-
|
|
160
|
+
if (targetAlias && targetAlias !== coreAlias) {
|
|
161
|
+
const targetContext = aliasToContext.get(targetAlias);
|
|
162
|
+
issues.push({
|
|
163
|
+
file: filePath,
|
|
164
|
+
line: entry.line,
|
|
165
|
+
message: targetContext
|
|
166
|
+
? `Layer boundary violation: core cannot import from context "${targetContext.name}" (${targetAlias})`
|
|
167
|
+
: `Layer boundary violation: core cannot import from "${targetAlias}"`
|
|
168
|
+
});
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const relativeTargetContext = resolveContextFromRelativeImport(filePath, entry.value, contexts);
|
|
173
|
+
if (!relativeTargetContext) continue;
|
|
146
174
|
|
|
147
|
-
const targetContext = aliasToContext.get(targetAlias);
|
|
148
175
|
issues.push({
|
|
149
176
|
file: filePath,
|
|
150
177
|
line: entry.line,
|
|
151
|
-
message:
|
|
152
|
-
? `Layer boundary violation: core cannot import from context "${targetContext.name}" (${targetAlias})`
|
|
153
|
-
: `Layer boundary violation: core cannot import from "${targetAlias}"`
|
|
178
|
+
message: `Layer boundary violation: core cannot import from context "${relativeTargetContext.name}" via relative path`
|
|
154
179
|
});
|
|
155
180
|
}
|
|
156
181
|
}
|
|
@@ -199,6 +224,23 @@ export default async function checkLayerBoundaries(): Promise<void> {
|
|
|
199
224
|
message: withSharedHint
|
|
200
225
|
});
|
|
201
226
|
}
|
|
227
|
+
|
|
228
|
+
for (const entry of imports) {
|
|
229
|
+
const relativeTargetContext = resolveContextFromRelativeImport(filePath, entry.value, contexts);
|
|
230
|
+
if (!relativeTargetContext || relativeTargetContext.name === context.name) continue;
|
|
231
|
+
|
|
232
|
+
const baseMessage = `Layer boundary violation: context "${context.name}" cannot import from context "${relativeTargetContext.name}" via relative path`;
|
|
233
|
+
const withSharedHint =
|
|
234
|
+
!sharedAlias && isSharedLikeContext(relativeTargetContext.name, relativeTargetContext.alias)
|
|
235
|
+
? `${baseMessage}. sharedAlias is not configured in azure-net.config.ts/js; if this context is shared, set sharedAlias explicitly`
|
|
236
|
+
: baseMessage;
|
|
237
|
+
|
|
238
|
+
issues.push({
|
|
239
|
+
file: filePath,
|
|
240
|
+
line: entry.line,
|
|
241
|
+
message: withSharedHint
|
|
242
|
+
});
|
|
243
|
+
}
|
|
202
244
|
}
|
|
203
245
|
}
|
|
204
246
|
|
package/src/checks/index.js
CHANGED
|
@@ -2,7 +2,10 @@ export { default as checkPresenterNames } from './checkPresenterNames.js';
|
|
|
2
2
|
export { default as checkProviderNames } from './checkProviderNames.js';
|
|
3
3
|
export { default as checkProviderGraph } from './checkProviderGraph.js';
|
|
4
4
|
export { default as checkDomain } from './checkDomain.js';
|
|
5
|
+
export { default as checkImports } from './checkImports.js';
|
|
6
|
+
export { default as checkHooks } from './checkHooks.js';
|
|
5
7
|
export { default as checkLayerBoundaries } from './checkLayerBoundaries.js';
|
|
6
8
|
export { default as checkFoldersStructure } from './checkFoldersStructure.js';
|
|
7
9
|
export { default as checkInternal } from './checkInternal.js';
|
|
8
10
|
export { default as checkProject } from './checkProject.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
package/src/checks/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ export { default as checkProviderNames } from './checkProviderNames.js';
|
|
|
3
3
|
export { default as checkProviderGraph } from './checkProviderGraph.js';
|
|
4
4
|
export { default as checkDomain } from './checkDomain.js';
|
|
5
5
|
export { default as checkImports } from './checkImports.js';
|
|
6
|
+
export { default as checkHooks } from './checkHooks.js';
|
|
6
7
|
export { default as checkLayerBoundaries } from './checkLayerBoundaries.js';
|
|
7
8
|
export { default as checkFoldersStructure } from './checkFoldersStructure.js';
|
|
8
9
|
export { default as checkInternal } from './checkInternal.js';
|
|
@@ -5,12 +5,11 @@ import initStructure from '../init/initStructure.js';
|
|
|
5
5
|
import { normalizeContexts } from '../init/initAliases.js';
|
|
6
6
|
import { loadUserConfig } from '../utils/loadConfig.js';
|
|
7
7
|
import {
|
|
8
|
-
createCoreMiddleware,
|
|
9
8
|
createCoreSchema,
|
|
10
9
|
createDatasource,
|
|
11
10
|
createDatasourceProvider,
|
|
12
|
-
createMiddleware,
|
|
13
11
|
createPresenterFactory,
|
|
12
|
+
createProgram,
|
|
14
13
|
createResponse,
|
|
15
14
|
createTranslationManager
|
|
16
15
|
} from '../scaffolds/index.js';
|
|
@@ -60,19 +59,12 @@ export default async function installFresh() {
|
|
|
60
59
|
|
|
61
60
|
await initStructure();
|
|
62
61
|
await initEdges();
|
|
62
|
+
await createProgram();
|
|
63
63
|
|
|
64
64
|
await runOptionalStep('Add commit tools (lint/commit conventions)?', async () => {
|
|
65
65
|
await addLint();
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
await runOptionalStep('Create middleware manager?', async () => {
|
|
69
|
-
await createCoreMiddleware();
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
await runOptionalStep('Create starter middleware?', async () => {
|
|
73
|
-
await createMiddleware();
|
|
74
|
-
});
|
|
75
|
-
|
|
76
68
|
await runOptionalStep('Create schema factory?', async () => {
|
|
77
69
|
await createCoreSchema();
|
|
78
70
|
});
|
|
@@ -5,12 +5,11 @@ import initStructure from '../init/initStructure.js';
|
|
|
5
5
|
import { normalizeContexts } from '../init/initAliases.js';
|
|
6
6
|
import { loadUserConfig } from '../utils/loadConfig.js';
|
|
7
7
|
import {
|
|
8
|
-
createCoreMiddleware,
|
|
9
8
|
createCoreSchema,
|
|
10
9
|
createDatasource,
|
|
11
10
|
createDatasourceProvider,
|
|
12
|
-
createMiddleware,
|
|
13
11
|
createPresenterFactory,
|
|
12
|
+
createProgram,
|
|
14
13
|
createResponse,
|
|
15
14
|
createTranslationManager
|
|
16
15
|
} from '../scaffolds/index.js';
|
|
@@ -60,19 +59,12 @@ export default async function installFresh(): Promise<void> {
|
|
|
60
59
|
|
|
61
60
|
await initStructure();
|
|
62
61
|
await initEdges();
|
|
62
|
+
await createProgram();
|
|
63
63
|
|
|
64
64
|
await runOptionalStep('Add commit tools (lint/commit conventions)?', async () => {
|
|
65
65
|
await addLint();
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
await runOptionalStep('Create middleware manager?', async () => {
|
|
69
|
-
await createCoreMiddleware();
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
await runOptionalStep('Create starter middleware?', async () => {
|
|
73
|
-
await createMiddleware();
|
|
74
|
-
});
|
|
75
|
-
|
|
76
68
|
await runOptionalStep('Create schema factory?', async () => {
|
|
77
69
|
await createCoreSchema();
|
|
78
70
|
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { writeIfNotExists } from '../utils/fileUtils.js';
|
|
3
|
+
|
|
4
|
+
const createProgramTemplate = () => `import { createApp } from '@azure-net/kit';
|
|
5
|
+
|
|
6
|
+
export const { register, App } = createApp((app) => app);
|
|
7
|
+
`;
|
|
8
|
+
|
|
9
|
+
export default async function createProgram() {
|
|
10
|
+
const programPath = path.join(process.cwd(), 'src', 'program.ts');
|
|
11
|
+
const created = await writeIfNotExists(programPath, createProgramTemplate());
|
|
12
|
+
|
|
13
|
+
if (!created) {
|
|
14
|
+
console.log('⚠️ Program already exists. Nothing was overwritten.');
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
console.log(`✅ Program created: ${programPath}`);
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { writeIfNotExists } from '../utils/fileUtils.js';
|
|
3
|
+
|
|
4
|
+
const createProgramTemplate = (): string => `import { createApp } from '@azure-net/kit';
|
|
5
|
+
|
|
6
|
+
export const { register, Container } = createApp((app) => app);
|
|
7
|
+
`;
|
|
8
|
+
|
|
9
|
+
export default async function createProgram(): Promise<void> {
|
|
10
|
+
const programPath = path.join(process.cwd(), 'src', 'program.ts');
|
|
11
|
+
const created = await writeIfNotExists(programPath, createProgramTemplate());
|
|
12
|
+
|
|
13
|
+
if (!created) {
|
|
14
|
+
console.log('⚠️ Program already exists. Nothing was overwritten.');
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
console.log(`✅ Program created: ${programPath}`);
|
|
19
|
+
}
|
package/src/scaffolds/index.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
export { default as createCoreSchema } from './createCoreSchema.js';
|
|
2
|
-
export { default as createCoreMiddleware } from './createCoreMiddleware.js';
|
|
3
2
|
export { default as createTranslationManager } from './createTranslationManager.js';
|
|
4
3
|
export { default as createPresenterFactory } from './createPresenterFactory.js';
|
|
5
4
|
export { default as createSchemaRule } from './createSchemaRule.js';
|
|
6
|
-
export { default as createMiddleware } from './createMiddleware.js';
|
|
7
5
|
export { default as createResponse } from './createResponse.js';
|
|
8
6
|
export { default as createDatasource } from './createDatasource.js';
|
|
9
7
|
export { default as createDatasourceProvider } from './createDatasourceProvider.js';
|
|
8
|
+
export { default as createProgram } from './createProgram.js';
|
package/src/scaffolds/index.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
export { default as createCoreSchema } from './createCoreSchema.js';
|
|
2
|
-
export { default as createCoreMiddleware } from './createCoreMiddleware.js';
|
|
3
2
|
export { default as createTranslationManager } from './createTranslationManager.js';
|
|
4
3
|
export { default as createPresenterFactory } from './createPresenterFactory.js';
|
|
5
4
|
export { default as createSchemaRule } from './createSchemaRule.js';
|
|
6
|
-
export { default as createMiddleware } from './createMiddleware.js';
|
|
7
5
|
export { default as createResponse } from './createResponse.js';
|
|
8
6
|
export { default as createDatasource } from './createDatasource.js';
|
|
9
7
|
export { default as createDatasourceProvider } from './createDatasourceProvider.js';
|
|
8
|
+
export { default as createProgram } from './createProgram.js';
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { writeIfNotExists } from '../utils/fileUtils.js';
|
|
4
|
-
|
|
5
|
-
const createMiddlewareManagerTemplate = () => `import { createMiddlewareManager } from '@azure-net/kit';
|
|
6
|
-
|
|
7
|
-
export const { clientMiddleware, serverMiddleware, executeMiddlewares } = createMiddlewareManager([]);
|
|
8
|
-
`;
|
|
9
|
-
|
|
10
|
-
export default async function createCoreMiddleware() {
|
|
11
|
-
const middlewareManagerPath = path.join(process.cwd(), 'src', 'core', 'middleware-manager');
|
|
12
|
-
const middlewaresPath = path.join(middlewareManagerPath, 'middlewares');
|
|
13
|
-
const middlewareManagerFile = path.join(middlewareManagerPath, 'MiddlewareManager.ts');
|
|
14
|
-
const indexFile = path.join(middlewareManagerPath, 'index.ts');
|
|
15
|
-
|
|
16
|
-
await fs.mkdir(middlewaresPath, { recursive: true });
|
|
17
|
-
|
|
18
|
-
const createdManager = await writeIfNotExists(middlewareManagerFile, createMiddlewareManagerTemplate());
|
|
19
|
-
const createdIndex = await writeIfNotExists(indexFile, `export * from './MiddlewareManager';\n`);
|
|
20
|
-
|
|
21
|
-
if (!createdManager && !createdIndex) {
|
|
22
|
-
console.log('⚠️ Core middleware manager already exists. Nothing was overwritten.');
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
console.log(`✅ Core middleware manager created: ${middlewareManagerPath}`);
|
|
27
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { writeIfNotExists } from '../utils/fileUtils.js';
|
|
4
|
-
|
|
5
|
-
const createMiddlewareManagerTemplate = (): string => `import { createMiddlewareManager } from '@azure-net/kit';
|
|
6
|
-
|
|
7
|
-
export const { clientMiddleware, serverMiddleware, executeMiddlewares } = createMiddlewareManager([]);
|
|
8
|
-
`;
|
|
9
|
-
|
|
10
|
-
export default async function createCoreMiddleware(): Promise<void> {
|
|
11
|
-
const middlewareManagerPath = path.join(process.cwd(), 'src', 'core', 'middleware-manager');
|
|
12
|
-
const middlewaresPath = path.join(middlewareManagerPath, 'middlewares');
|
|
13
|
-
const middlewareManagerFile = path.join(middlewareManagerPath, 'MiddlewareManager.ts');
|
|
14
|
-
const indexFile = path.join(middlewareManagerPath, 'index.ts');
|
|
15
|
-
|
|
16
|
-
await fs.mkdir(middlewaresPath, { recursive: true });
|
|
17
|
-
|
|
18
|
-
const createdManager = await writeIfNotExists(middlewareManagerFile, createMiddlewareManagerTemplate());
|
|
19
|
-
const createdIndex = await writeIfNotExists(indexFile, `export * from './MiddlewareManager';\n`);
|
|
20
|
-
|
|
21
|
-
if (!createdManager && !createdIndex) {
|
|
22
|
-
console.log('⚠️ Core middleware manager already exists. Nothing was overwritten.');
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
console.log(`✅ Core middleware manager created: ${middlewareManagerPath}`);
|
|
27
|
-
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import prompts from 'prompts';
|
|
4
|
-
import { writeIfNotExists, updateIndexTs } from '../utils/fileUtils.js';
|
|
5
|
-
import { toPascalCase } from '../utils/contextUtils.js';
|
|
6
|
-
|
|
7
|
-
const createMiddlewareTemplate = (middlewareName) => `import { type IMiddleware } from '@azure-net/kit';
|
|
8
|
-
|
|
9
|
-
export const ${middlewareName}: IMiddleware = async ({ next }) => {
|
|
10
|
-
\treturn next();
|
|
11
|
-
};
|
|
12
|
-
`;
|
|
13
|
-
|
|
14
|
-
const addMiddlewareToManager = (content, middlewareName) => {
|
|
15
|
-
const wordRegex = new RegExp(`\\b${middlewareName}\\b`);
|
|
16
|
-
if (wordRegex.test(content)) return content;
|
|
17
|
-
|
|
18
|
-
const importLine = `import { ${middlewareName} } from './middlewares';`;
|
|
19
|
-
if (!content.includes(importLine)) {
|
|
20
|
-
const importMatches = Array.from(content.matchAll(/^import[^\n]*$/gm));
|
|
21
|
-
if (importMatches.length) {
|
|
22
|
-
const lastImport = importMatches[importMatches.length - 1];
|
|
23
|
-
const insertPos = (lastImport.index ?? 0) + lastImport[0].length;
|
|
24
|
-
content = `${content.slice(0, insertPos)}\n${importLine}${content.slice(insertPos)}`;
|
|
25
|
-
} else {
|
|
26
|
-
content = `${importLine}\n${content}`;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const managerCallRegex = /createMiddlewareManager\s*\(\s*\[([\s\S]*?)\]\s*\)/m;
|
|
31
|
-
const match = content.match(managerCallRegex);
|
|
32
|
-
if (!match) return content;
|
|
33
|
-
|
|
34
|
-
const arrayBody = match[1] ?? '';
|
|
35
|
-
if (wordRegex.test(arrayBody)) return content;
|
|
36
|
-
|
|
37
|
-
const isMultiline = arrayBody.includes('\n');
|
|
38
|
-
const hasItems = arrayBody.trim().length > 0;
|
|
39
|
-
|
|
40
|
-
if (!isMultiline) {
|
|
41
|
-
const trimmed = arrayBody.trim();
|
|
42
|
-
const nextArrayBody = hasItems ? `${trimmed}, ${middlewareName}` : middlewareName;
|
|
43
|
-
return content.replace(managerCallRegex, `createMiddlewareManager([${nextArrayBody}])`);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (!hasItems) {
|
|
47
|
-
const baseIndent = arrayBody.match(/\n([ \t]*)$/)?.[1] ?? '\t';
|
|
48
|
-
const nextArrayBody = `\n${baseIndent}${middlewareName}\n`;
|
|
49
|
-
return content.replace(managerCallRegex, `createMiddlewareManager([${nextArrayBody}])`);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const trimmedBody = arrayBody.trimEnd();
|
|
53
|
-
const lastItemIndent = trimmedBody.match(/(?:^|\n)([ \t]*)\S[^\n]*$/)?.[1] ?? '\t';
|
|
54
|
-
const nextArrayBody = `${trimmedBody},\n${lastItemIndent}${middlewareName}\n`;
|
|
55
|
-
|
|
56
|
-
return content.replace(managerCallRegex, `createMiddlewareManager([${nextArrayBody}])`);
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export default async function createMiddleware() {
|
|
60
|
-
const { middlewareNameRaw } = await prompts({
|
|
61
|
-
type: 'text',
|
|
62
|
-
name: 'middlewareNameRaw',
|
|
63
|
-
message: 'Middleware name:',
|
|
64
|
-
initial: 'RoutesGuard'
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
const middlewareName = toPascalCase(String(middlewareNameRaw ?? 'RoutesGuard')) || 'RoutesGuard';
|
|
68
|
-
const middlewareManagerPath = path.join(process.cwd(), 'src', 'core', 'middleware-manager');
|
|
69
|
-
const middlewaresPath = path.join(middlewareManagerPath, 'middlewares');
|
|
70
|
-
const middlewareFilePath = path.join(middlewaresPath, `${middlewareName}.ts`);
|
|
71
|
-
const middlewaresIndexPath = path.join(middlewaresPath, 'index.ts');
|
|
72
|
-
const middlewareManagerFile = path.join(middlewareManagerPath, 'MiddlewareManager.ts');
|
|
73
|
-
|
|
74
|
-
await fs.mkdir(middlewaresPath, { recursive: true });
|
|
75
|
-
|
|
76
|
-
const created = await writeIfNotExists(middlewareFilePath, createMiddlewareTemplate(middlewareName));
|
|
77
|
-
await updateIndexTs(middlewaresPath);
|
|
78
|
-
|
|
79
|
-
if (!created) {
|
|
80
|
-
console.log(`⚠️ Middleware "${middlewareName}" already exists. File was not overwritten.`);
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
let managerContent;
|
|
85
|
-
try {
|
|
86
|
-
managerContent = await fs.readFile(middlewareManagerFile, 'utf-8');
|
|
87
|
-
} catch {
|
|
88
|
-
console.log(`⚠️ Middleware manager not found at ${middlewareManagerFile}. Run "azure-net create middleware-manager" first.`);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const nextManagerContent = addMiddlewareToManager(managerContent, middlewareName);
|
|
93
|
-
await fs.writeFile(middlewareManagerFile, nextManagerContent, 'utf-8');
|
|
94
|
-
|
|
95
|
-
const hasIndex = await fs
|
|
96
|
-
.access(middlewaresIndexPath)
|
|
97
|
-
.then(() => true)
|
|
98
|
-
.catch(() => false);
|
|
99
|
-
|
|
100
|
-
console.log(`✅ Middleware created: ${middlewareFilePath}`);
|
|
101
|
-
if (hasIndex) {
|
|
102
|
-
console.log(`✅ Middlewares index updated: ${middlewaresIndexPath}`);
|
|
103
|
-
}
|
|
104
|
-
console.log(`✅ Middleware manager updated: ${middlewareManagerFile}`);
|
|
105
|
-
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import prompts from 'prompts';
|
|
4
|
-
import { writeIfNotExists, updateIndexTs } from '../utils/fileUtils.js';
|
|
5
|
-
import { toPascalCase } from '../utils/contextUtils.js';
|
|
6
|
-
|
|
7
|
-
const createMiddlewareTemplate = (middlewareName: string): string => `import { type IMiddleware } from '@azure-net/kit';
|
|
8
|
-
|
|
9
|
-
export const ${middlewareName}: IMiddleware = async ({ next }) => {
|
|
10
|
-
\treturn next();
|
|
11
|
-
};
|
|
12
|
-
`;
|
|
13
|
-
|
|
14
|
-
const addMiddlewareToManager = (content: string, middlewareName: string): string => {
|
|
15
|
-
const wordRegex = new RegExp(`\\b${middlewareName}\\b`);
|
|
16
|
-
if (wordRegex.test(content)) return content;
|
|
17
|
-
|
|
18
|
-
const importLine = `import { ${middlewareName} } from './middlewares';`;
|
|
19
|
-
if (!content.includes(importLine)) {
|
|
20
|
-
const importMatches = Array.from(content.matchAll(/^import[^\n]*$/gm));
|
|
21
|
-
const lastImport = importMatches.at(-1);
|
|
22
|
-
if (lastImport) {
|
|
23
|
-
const insertPos = (lastImport.index ?? 0) + lastImport[0].length;
|
|
24
|
-
content = `${content.slice(0, insertPos)}\n${importLine}${content.slice(insertPos)}`;
|
|
25
|
-
} else {
|
|
26
|
-
content = `${importLine}\n${content}`;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const managerCallRegex = /createMiddlewareManager\s*\(\s*\[([\s\S]*?)\]\s*\)/m;
|
|
31
|
-
const match = content.match(managerCallRegex);
|
|
32
|
-
if (!match) return content;
|
|
33
|
-
|
|
34
|
-
const arrayBody = match[1] ?? '';
|
|
35
|
-
if (wordRegex.test(arrayBody)) return content;
|
|
36
|
-
|
|
37
|
-
const isMultiline = arrayBody.includes('\n');
|
|
38
|
-
const hasItems = arrayBody.trim().length > 0;
|
|
39
|
-
|
|
40
|
-
if (!isMultiline) {
|
|
41
|
-
const trimmed = arrayBody.trim();
|
|
42
|
-
const nextArrayBody = hasItems ? `${trimmed}, ${middlewareName}` : middlewareName;
|
|
43
|
-
return content.replace(managerCallRegex, `createMiddlewareManager([${nextArrayBody}])`);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (!hasItems) {
|
|
47
|
-
const baseIndent = arrayBody.match(/\n([ \t]*)$/)?.[1] ?? '\t';
|
|
48
|
-
const nextArrayBody = `\n${baseIndent}${middlewareName}\n`;
|
|
49
|
-
return content.replace(managerCallRegex, `createMiddlewareManager([${nextArrayBody}])`);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const trimmedBody = arrayBody.trimEnd();
|
|
53
|
-
const lastItemIndent = trimmedBody.match(/(?:^|\n)([ \t]*)\S[^\n]*$/)?.[1] ?? '\t';
|
|
54
|
-
const nextArrayBody = `${trimmedBody},\n${lastItemIndent}${middlewareName}\n`;
|
|
55
|
-
|
|
56
|
-
return content.replace(managerCallRegex, `createMiddlewareManager([${nextArrayBody}])`);
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export default async function createMiddleware(): Promise<void> {
|
|
60
|
-
const { middlewareNameRaw } = await prompts({
|
|
61
|
-
type: 'text',
|
|
62
|
-
name: 'middlewareNameRaw',
|
|
63
|
-
message: 'Middleware name:',
|
|
64
|
-
initial: 'RoutesGuard'
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
const middlewareName = toPascalCase(String(middlewareNameRaw ?? 'RoutesGuard')) || 'RoutesGuard';
|
|
68
|
-
const middlewareManagerPath = path.join(process.cwd(), 'src', 'core', 'middleware-manager');
|
|
69
|
-
const middlewaresPath = path.join(middlewareManagerPath, 'middlewares');
|
|
70
|
-
const middlewareFilePath = path.join(middlewaresPath, `${middlewareName}.ts`);
|
|
71
|
-
const middlewaresIndexPath = path.join(middlewaresPath, 'index.ts');
|
|
72
|
-
const middlewareManagerFile = path.join(middlewareManagerPath, 'MiddlewareManager.ts');
|
|
73
|
-
|
|
74
|
-
await fs.mkdir(middlewaresPath, { recursive: true });
|
|
75
|
-
|
|
76
|
-
const created = await writeIfNotExists(middlewareFilePath, createMiddlewareTemplate(middlewareName));
|
|
77
|
-
await updateIndexTs(middlewaresPath);
|
|
78
|
-
|
|
79
|
-
if (!created) {
|
|
80
|
-
console.log(`⚠️ Middleware "${middlewareName}" already exists. File was not overwritten.`);
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
let managerContent: string;
|
|
85
|
-
try {
|
|
86
|
-
managerContent = await fs.readFile(middlewareManagerFile, 'utf-8');
|
|
87
|
-
} catch {
|
|
88
|
-
console.log(`⚠️ Middleware manager not found at ${middlewareManagerFile}. Run "azure-net create middleware-manager" first.`);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const nextManagerContent = addMiddlewareToManager(managerContent, middlewareName);
|
|
93
|
-
await fs.writeFile(middlewareManagerFile, nextManagerContent, 'utf-8');
|
|
94
|
-
|
|
95
|
-
const hasIndex = await fs
|
|
96
|
-
.access(middlewaresIndexPath)
|
|
97
|
-
.then(() => true)
|
|
98
|
-
.catch(() => false);
|
|
99
|
-
|
|
100
|
-
console.log(`✅ Middleware created: ${middlewareFilePath}`);
|
|
101
|
-
if (hasIndex) {
|
|
102
|
-
console.log(`✅ Middlewares index updated: ${middlewaresIndexPath}`);
|
|
103
|
-
}
|
|
104
|
-
console.log(`✅ Middleware manager updated: ${middlewareManagerFile}`);
|
|
105
|
-
}
|