@azure-net/cli 5.0.0-3 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/bin/azure-net.js +5 -46
  2. package/bin/azure-net.ts +5 -5
  3. package/package.json +1 -1
  4. package/src/checks/checkDomain.js +8 -38
  5. package/src/checks/checkFoldersStructure.js +0 -14
  6. package/src/checks/checkHooks.js +0 -5
  7. package/src/checks/checkImports.js +0 -1
  8. package/src/checks/checkInternal.js +0 -1
  9. package/src/checks/checkLayerBoundaries.js +50 -54
  10. package/src/checks/checkLayerBoundaries.ts +55 -58
  11. package/src/checks/checkPresenterNames.js +1 -37
  12. package/src/checks/checkPresenterNames.ts +1 -6
  13. package/src/checks/checkProject.js +0 -6
  14. package/src/checks/checkProviderGraph.js +1 -38
  15. package/src/checks/checkProviderNames.js +0 -28
  16. package/src/checks/index.js +0 -1
  17. package/src/generate/layers/generateRepository.js +4 -19
  18. package/src/generate/layers/generateRepositoryMethod.js +0 -12
  19. package/src/generate/layers/generateUseCases.js +0 -6
  20. package/src/generate/layers/repositoryShared.js +41 -104
  21. package/src/generate/layers/repositoryShared.ts +17 -12
  22. package/src/generate/module/generateDomain.js +0 -25
  23. package/src/generate/module/generateModule.js +0 -10
  24. package/src/generate/module/generateModulePreset.js +10 -51
  25. package/src/generate/module/generateModulePreset.ts +9 -8
  26. package/src/generate/module/generatePresenter.js +29 -76
  27. package/src/generate/module/generatePresenter.ts +15 -10
  28. package/src/generate/module/generateRepository.js +0 -17
  29. package/src/generate/module/generateSchemaFromType.js +9 -58
  30. package/src/generate/module/generateSchemaFromType.ts +1 -2
  31. package/src/generate/module/generateTypesJson.js +4 -45
  32. package/src/generate/module/generateUseCases.js +0 -9
  33. package/src/generate/module/repositoryModuleShared.js +21 -98
  34. package/src/generate/module/repositoryModuleShared.ts +14 -10
  35. package/src/generate/ui/generateComponent.js +0 -16
  36. package/src/generate/ui/generateDesignComponent.js +0 -12
  37. package/src/generate/ui/generateWidget.js +0 -12
  38. package/src/init/initAliases.js +42 -64
  39. package/src/init/initAliases.ts +47 -42
  40. package/src/init/initEdges.js +0 -15
  41. package/src/init/initStructure.js +48 -21
  42. package/src/init/initStructure.ts +51 -3
  43. package/src/install/installFresh.js +0 -16
  44. package/src/plugins/addLint.js +11 -46
  45. package/src/scaffolds/createCoreSchema.js +6 -40
  46. package/src/scaffolds/createCoreSchema.ts +6 -18
  47. package/src/scaffolds/createDatasource.js +26 -61
  48. package/src/scaffolds/createDatasource.ts +26 -40
  49. package/src/scaffolds/createDatasourceProvider.js +19 -56
  50. package/src/scaffolds/createDatasourceProvider.ts +20 -34
  51. package/src/scaffolds/createPresenterFactory.js +5 -34
  52. package/src/scaffolds/createPresenterFactory.ts +5 -17
  53. package/src/scaffolds/createProgram.js +1 -5
  54. package/src/scaffolds/createResponse.js +3 -11
  55. package/src/scaffolds/createResponse.ts +3 -1
  56. package/src/scaffolds/createSchemaRule.js +3 -8
  57. package/src/scaffolds/createSchemaRule.ts +3 -1
  58. package/src/scaffolds/createTranslationManager.js +3 -14
  59. package/src/scaffolds/createTranslationManager.ts +3 -1
  60. package/src/utils/contextUtils.js +5 -20
  61. package/src/utils/contextUtils.ts +2 -3
  62. package/src/utils/fileUtils.js +3 -38
  63. package/src/utils/fileUtils.ts +0 -26
  64. package/src/utils/loadConfig.js +1 -14
  65. package/src/utils/loadConfig.ts +1 -3
  66. package/src/utils/sharedFoundation.js +48 -0
  67. package/src/utils/sharedFoundation.ts +70 -0
@@ -2,6 +2,7 @@ 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
+ import { SHARED_ESSENTIALS_DIR_NAME } from '../utils/sharedFoundation.js';
5
6
  const TARGET_EXTENSIONS = new Set(['.ts', '.js', '.svelte']);
6
7
  const IGNORED_DIRS = new Set(['node_modules', 'dist', '.svelte-kit', '.git']);
7
8
  const IMPORT_FROM_PATTERN = /\bimport\s+(?:type\s+)?[\s\S]*?\sfrom\s*['"]([^'"]+)['"]/g;
@@ -83,10 +84,10 @@ const resolveContextFromRelativeImport = (filePath, importPath, contexts) => {
83
84
  }
84
85
  return null;
85
86
  };
86
- const isSharedLikeContext = (contextName, alias) => {
87
- const normalizedName = contextName.toLowerCase();
88
- const normalizedAlias = alias.toLowerCase();
89
- return normalizedName.includes('shared') || normalizedAlias.includes('shared');
87
+ const isInside = (targetPath, rootPath) => targetPath === rootPath || targetPath.startsWith(`${rootPath}${path.sep}`);
88
+ const resolveAbsoluteRelativeImport = (filePath, importPath) => {
89
+ if (!importPath.startsWith('.')) return null;
90
+ return path.resolve(path.dirname(filePath), importPath);
90
91
  };
91
92
  export default async function checkLayerBoundaries() {
92
93
  const config = await loadUserConfig();
@@ -96,96 +97,92 @@ export default async function checkLayerBoundaries() {
96
97
  process.exitCode = 1;
97
98
  return;
98
99
  }
99
- const coreAlias = normalizeAlias(config.coreAlias, '$core');
100
100
  const sharedAliasRaw = normalizeAlias(config.sharedAlias, '');
101
101
  const sharedAlias = sharedAliasRaw.length ? sharedAliasRaw : null;
102
+ if (!sharedAlias) {
103
+ console.error('❌ sharedAlias is missing in azure-net.config.ts/js. Set it explicitly to the shared context alias.');
104
+ process.exitCode = 1;
105
+ return;
106
+ }
102
107
  const aliasToContext = new Map();
103
108
  for (const context of contexts) {
104
109
  aliasToContext.set(context.alias, context);
105
110
  }
106
- const knownAliases = [coreAlias, ...contexts.map((context) => context.alias)];
107
- if (sharedAlias && !knownAliases.includes(sharedAlias)) {
108
- knownAliases.push(sharedAlias);
111
+ const knownAliases = [...contexts.map((context) => context.alias)];
112
+ const sharedContext = aliasToContext.get(sharedAlias);
113
+ if (!sharedContext) {
114
+ console.error(`❌ sharedAlias "${sharedAlias}" does not point to any configured context.`);
115
+ process.exitCode = 1;
116
+ return;
109
117
  }
110
118
  const issues = [];
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
- }
143
119
  for (const context of contexts) {
144
120
  const contextRoot = path.join(process.cwd(), 'src', 'app', context.name);
121
+ const isSharedContext = context.alias === sharedAlias;
122
+ const sharedEssentialsRoot = isSharedContext ? path.join(contextRoot, SHARED_ESSENTIALS_DIR_NAME) : null;
145
123
  try {
146
124
  await fs.access(contextRoot);
147
125
  } catch {
148
126
  continue;
149
127
  }
150
128
  const files = await listFiles(contextRoot);
151
- const allowedAliases = new Set([context.alias, coreAlias]);
152
- if (sharedAlias && context.alias !== sharedAlias) {
129
+ const allowedAliases = new Set([context.alias]);
130
+ if (context.alias !== sharedAlias) {
153
131
  allowedAliases.add(sharedAlias);
154
132
  }
155
133
  for (const filePath of files) {
156
134
  const content = await fs.readFile(filePath, 'utf-8');
157
135
  const imports = extractImports(content);
136
+ const isSharedEssentialsFile = Boolean(sharedEssentialsRoot && isInside(filePath, sharedEssentialsRoot));
158
137
  for (const entry of imports) {
159
138
  const targetAlias = resolveAliasFromImport(entry.value, knownAliases);
160
139
  if (!targetAlias) continue;
161
- if (targetAlias === coreAlias) continue;
140
+ if (isSharedEssentialsFile && targetAlias === sharedAlias) {
141
+ const importRest = entry.value.slice(sharedAlias.length).replace(/^\/+/, '');
142
+ if (importRest.length && !importRest.startsWith(`${SHARED_ESSENTIALS_DIR_NAME}/`)) {
143
+ issues.push({
144
+ file: filePath,
145
+ line: entry.line,
146
+ message: `Layer boundary violation: shared essentials cannot import from shared "${importRest}". Essentials may depend only on shared essentials code.`
147
+ });
148
+ continue;
149
+ }
150
+ }
162
151
  if (allowedAliases.has(targetAlias)) continue;
163
152
  const targetContext = aliasToContext.get(targetAlias);
164
153
  const baseMessage = targetContext
165
154
  ? `Layer boundary violation: context "${context.name}" cannot import from context "${targetContext.name}" (${targetAlias})`
166
155
  : `Layer boundary violation: context "${context.name}" cannot import from "${targetAlias}"`;
167
- const withSharedHint =
168
- !sharedAlias && targetContext && isSharedLikeContext(targetContext.name, targetAlias)
169
- ? `${baseMessage}. sharedAlias is not configured in azure-net.config.ts/js; if this context is shared, set sharedAlias explicitly`
170
- : baseMessage;
171
156
  issues.push({
172
157
  file: filePath,
173
158
  line: entry.line,
174
- message: withSharedHint
159
+ message: baseMessage
175
160
  });
176
161
  }
177
162
  for (const entry of imports) {
163
+ const absoluteRelativeImport = resolveAbsoluteRelativeImport(filePath, entry.value);
164
+ if (
165
+ isSharedEssentialsFile &&
166
+ absoluteRelativeImport &&
167
+ sharedEssentialsRoot &&
168
+ isInside(absoluteRelativeImport, contextRoot) &&
169
+ !isInside(absoluteRelativeImport, sharedEssentialsRoot)
170
+ ) {
171
+ issues.push({
172
+ file: filePath,
173
+ line: entry.line,
174
+ message: 'Layer boundary violation: shared essentials cannot import from shared layers/ui via relative path.'
175
+ });
176
+ continue;
177
+ }
178
178
  const relativeTargetContext = resolveContextFromRelativeImport(filePath, entry.value, contexts);
179
179
  if (!relativeTargetContext || relativeTargetContext.name === context.name) continue;
180
+ if (context.alias !== sharedAlias && relativeTargetContext.alias === sharedAlias) continue;
180
181
  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;
185
182
  issues.push({
186
183
  file: filePath,
187
184
  line: entry.line,
188
- message: withSharedHint
185
+ message: baseMessage
189
186
  });
190
187
  }
191
188
  }
@@ -200,4 +197,3 @@ export default async function checkLayerBoundaries() {
200
197
  }
201
198
  process.exitCode = 1;
202
199
  }
203
- //# sourceMappingURL=checkLayerBoundaries.js.map
@@ -2,6 +2,7 @@ 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
+ import { SHARED_ESSENTIALS_DIR_NAME } from '../utils/sharedFoundation.js';
5
6
 
6
7
  type BoundaryIssue = {
7
8
  file: string;
@@ -115,10 +116,12 @@ const resolveContextFromRelativeImport = (filePath: string, importPath: string,
115
116
  return null;
116
117
  };
117
118
 
118
- const isSharedLikeContext = (contextName: string, alias: string): boolean => {
119
- const normalizedName = contextName.toLowerCase();
120
- const normalizedAlias = alias.toLowerCase();
121
- return normalizedName.includes('shared') || normalizedAlias.includes('shared');
119
+ const isInside = (targetPath: string, rootPath: string): boolean =>
120
+ targetPath === rootPath || targetPath.startsWith(`${rootPath}${path.sep}`);
121
+
122
+ const resolveAbsoluteRelativeImport = (filePath: string, importPath: string): string | null => {
123
+ if (!importPath.startsWith('.')) return null;
124
+ return path.resolve(path.dirname(filePath), importPath);
122
125
  };
123
126
 
124
127
  export default async function checkLayerBoundaries(): Promise<void> {
@@ -131,60 +134,33 @@ export default async function checkLayerBoundaries(): Promise<void> {
131
134
  return;
132
135
  }
133
136
 
134
- const coreAlias = normalizeAlias(config.coreAlias, '$core');
135
137
  const sharedAliasRaw = normalizeAlias(config.sharedAlias, '');
136
138
  const sharedAlias = sharedAliasRaw.length ? sharedAliasRaw : null;
139
+ if (!sharedAlias) {
140
+ console.error('❌ sharedAlias is missing in azure-net.config.ts/js. Set it explicitly to the shared context alias.');
141
+ process.exitCode = 1;
142
+ return;
143
+ }
137
144
 
138
145
  const aliasToContext = new Map<string, ContextConfig>();
139
146
  for (const context of contexts) {
140
147
  aliasToContext.set(context.alias, context);
141
148
  }
142
149
 
143
- const knownAliases = [coreAlias, ...contexts.map((context) => context.alias)];
144
- if (sharedAlias && !knownAliases.includes(sharedAlias)) {
145
- knownAliases.push(sharedAlias);
150
+ const knownAliases = [...contexts.map((context) => context.alias)];
151
+ const sharedContext = aliasToContext.get(sharedAlias);
152
+ if (!sharedContext) {
153
+ console.error(`❌ sharedAlias "${sharedAlias}" does not point to any configured context.`);
154
+ process.exitCode = 1;
155
+ return;
146
156
  }
147
157
 
148
158
  const issues: BoundaryIssue[] = [];
149
159
 
150
- const coreRoot = path.join(process.cwd(), 'src', 'core');
151
- try {
152
- await fs.access(coreRoot);
153
- const coreFiles = await listFiles(coreRoot);
154
- for (const filePath of coreFiles) {
155
- const content = await fs.readFile(filePath, 'utf-8');
156
- const imports = extractImports(content);
157
-
158
- for (const entry of imports) {
159
- const targetAlias = resolveAliasFromImport(entry.value, knownAliases);
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;
174
-
175
- issues.push({
176
- file: filePath,
177
- line: entry.line,
178
- message: `Layer boundary violation: core cannot import from context "${relativeTargetContext.name}" via relative path`
179
- });
180
- }
181
- }
182
- } catch {
183
- // Core folder is optional for the check.
184
- }
185
-
186
160
  for (const context of contexts) {
187
161
  const contextRoot = path.join(process.cwd(), 'src', 'app', context.name);
162
+ const isSharedContext = context.alias === sharedAlias;
163
+ const sharedEssentialsRoot = isSharedContext ? path.join(contextRoot, SHARED_ESSENTIALS_DIR_NAME) : null;
188
164
 
189
165
  try {
190
166
  await fs.access(contextRoot);
@@ -193,19 +169,32 @@ export default async function checkLayerBoundaries(): Promise<void> {
193
169
  }
194
170
 
195
171
  const files = await listFiles(contextRoot);
196
- const allowedAliases = new Set<string>([context.alias, coreAlias]);
197
- if (sharedAlias && context.alias !== sharedAlias) {
172
+ const allowedAliases = new Set<string>([context.alias]);
173
+ if (context.alias !== sharedAlias) {
198
174
  allowedAliases.add(sharedAlias);
199
175
  }
200
176
 
201
177
  for (const filePath of files) {
202
178
  const content = await fs.readFile(filePath, 'utf-8');
203
179
  const imports = extractImports(content);
180
+ const isSharedEssentialsFile = Boolean(sharedEssentialsRoot && isInside(filePath, sharedEssentialsRoot));
204
181
 
205
182
  for (const entry of imports) {
206
183
  const targetAlias = resolveAliasFromImport(entry.value, knownAliases);
207
184
  if (!targetAlias) continue;
208
- if (targetAlias === coreAlias) continue;
185
+
186
+ if (isSharedEssentialsFile && targetAlias === sharedAlias) {
187
+ const importRest = entry.value.slice(sharedAlias.length).replace(/^\/+/, '');
188
+ if (importRest.length && !importRest.startsWith(`${SHARED_ESSENTIALS_DIR_NAME}/`)) {
189
+ issues.push({
190
+ file: filePath,
191
+ line: entry.line,
192
+ message: `Layer boundary violation: shared essentials cannot import from shared "${importRest}". Essentials may depend only on shared essentials code.`
193
+ });
194
+ continue;
195
+ }
196
+ }
197
+
209
198
  if (allowedAliases.has(targetAlias)) continue;
210
199
 
211
200
  const targetContext = aliasToContext.get(targetAlias);
@@ -213,32 +202,40 @@ export default async function checkLayerBoundaries(): Promise<void> {
213
202
  ? `Layer boundary violation: context "${context.name}" cannot import from context "${targetContext.name}" (${targetAlias})`
214
203
  : `Layer boundary violation: context "${context.name}" cannot import from "${targetAlias}"`;
215
204
 
216
- const withSharedHint =
217
- !sharedAlias && targetContext && isSharedLikeContext(targetContext.name, targetAlias)
218
- ? `${baseMessage}. sharedAlias is not configured in azure-net.config.ts/js; if this context is shared, set sharedAlias explicitly`
219
- : baseMessage;
220
-
221
205
  issues.push({
222
206
  file: filePath,
223
207
  line: entry.line,
224
- message: withSharedHint
208
+ message: baseMessage
225
209
  });
226
210
  }
227
211
 
228
212
  for (const entry of imports) {
213
+ const absoluteRelativeImport = resolveAbsoluteRelativeImport(filePath, entry.value);
214
+ if (
215
+ isSharedEssentialsFile &&
216
+ absoluteRelativeImport &&
217
+ sharedEssentialsRoot &&
218
+ isInside(absoluteRelativeImport, contextRoot) &&
219
+ !isInside(absoluteRelativeImport, sharedEssentialsRoot)
220
+ ) {
221
+ issues.push({
222
+ file: filePath,
223
+ line: entry.line,
224
+ message: 'Layer boundary violation: shared essentials cannot import from shared layers/ui via relative path.'
225
+ });
226
+ continue;
227
+ }
228
+
229
229
  const relativeTargetContext = resolveContextFromRelativeImport(filePath, entry.value, contexts);
230
230
  if (!relativeTargetContext || relativeTargetContext.name === context.name) continue;
231
+ if (context.alias !== sharedAlias && relativeTargetContext.alias === sharedAlias) continue;
231
232
 
232
233
  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
234
 
238
235
  issues.push({
239
236
  file: filePath,
240
237
  line: entry.line,
241
- message: withSharedHint
238
+ message: baseMessage
242
239
  });
243
240
  }
244
241
  }
@@ -1,20 +1,15 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
-
4
3
  const TARGET_EXTENSIONS = new Set(['.ts', '.js', '.svelte']);
5
4
  const IGNORED_DIRS = new Set(['node_modules', 'dist', '.svelte-kit', '.git']);
6
-
7
5
  const CREATE_FACTORY_PATTERN = '(?:export\\s+)?(?:const|let|var)\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s*=\\s*createPresenterFactory\\s*\\(';
8
6
  const IMPORT_FROM_PRESENTER_PATTERN = 'import\\s*\\{([^}]+)\\}\\s*from\\s*[\'"]([^\'"]+)[\'"]';
9
7
  const PRESENTER_CALL_PATTERN = '\\b([A-Za-z_$][A-Za-z0-9_$]*)\\s*\\(\\s*([\'"`])([^\'"`]*)\\2';
10
-
11
8
  const toPosixPath = (value) => value.split(path.sep).join('/');
12
-
13
9
  const isPresenterImportPath = (importPath) => {
14
10
  const normalized = importPath.replace(/\\/g, '/');
15
11
  return /(^|\/)presenter(\/|$)/.test(normalized);
16
12
  };
17
-
18
13
  const parseImportLocals = (rawImports) => {
19
14
  const locals = [];
20
15
  for (const segment of rawImports.split(',')) {
@@ -25,7 +20,6 @@ const parseImportLocals = (rawImports) => {
25
20
  locals.push(aliasMatch[2]);
26
21
  continue;
27
22
  }
28
-
29
23
  const directMatch = item.match(/^([A-Za-z_$][A-Za-z0-9_$]*)$/);
30
24
  if (directMatch?.[1]) {
31
25
  locals.push(directMatch[1]);
@@ -33,22 +27,18 @@ const parseImportLocals = (rawImports) => {
33
27
  }
34
28
  return locals;
35
29
  };
36
-
37
30
  const listSourceFiles = async (rootDir) => {
38
31
  const result = [];
39
32
  const stack = [rootDir];
40
-
41
33
  while (stack.length) {
42
34
  const current = stack.pop();
43
35
  if (!current) continue;
44
-
45
36
  let entries = [];
46
37
  try {
47
38
  entries = await fs.readdir(current, { withFileTypes: true });
48
39
  } catch {
49
40
  continue;
50
41
  }
51
-
52
42
  for (const entry of entries) {
53
43
  if (entry.isDirectory()) {
54
44
  if (!IGNORED_DIRS.has(entry.name)) {
@@ -56,7 +46,6 @@ const listSourceFiles = async (rootDir) => {
56
46
  }
57
47
  continue;
58
48
  }
59
-
60
49
  if (!entry.isFile()) continue;
61
50
  const fullPath = path.join(current, entry.name);
62
51
  const extension = path.extname(fullPath);
@@ -64,10 +53,8 @@ const listSourceFiles = async (rootDir) => {
64
53
  result.push(fullPath);
65
54
  }
66
55
  }
67
-
68
56
  return result;
69
57
  };
70
-
71
58
  const getLineByIndex = (content, index) => {
72
59
  let line = 1;
73
60
  for (let i = 0; i < index; i += 1) {
@@ -75,7 +62,6 @@ const getLineByIndex = (content, index) => {
75
62
  }
76
63
  return line;
77
64
  };
78
-
79
65
  const extractFactoryNamesFromContent = (content) => {
80
66
  const createFactoryRegex = new RegExp(CREATE_FACTORY_PATTERN, 'g');
81
67
  const names = [];
@@ -86,7 +72,6 @@ const extractFactoryNamesFromContent = (content) => {
86
72
  }
87
73
  return names;
88
74
  };
89
-
90
75
  const extractImportedPresenterFactories = (content) => {
91
76
  const importFromPresenterRegex = new RegExp(IMPORT_FROM_PRESENTER_PATTERN, 'g');
92
77
  const names = [];
@@ -100,12 +85,10 @@ const extractImportedPresenterFactories = (content) => {
100
85
  }
101
86
  return names;
102
87
  };
103
-
104
88
  const collectPresenterOccurrences = (content, filePath, knownFactories) => {
105
89
  const presenterCallRegex = new RegExp(PRESENTER_CALL_PATTERN, 'g');
106
90
  const occurrences = [];
107
91
  const availableCallees = new Set(['createPresenter']);
108
-
109
92
  for (const name of extractFactoryNamesFromContent(content)) {
110
93
  availableCallees.add(name);
111
94
  }
@@ -115,7 +98,6 @@ const collectPresenterOccurrences = (content, filePath, knownFactories) => {
115
98
  for (const name of knownFactories) {
116
99
  availableCallees.add(name);
117
100
  }
118
-
119
101
  let match;
120
102
  while ((match = presenterCallRegex.exec(content)) !== null) {
121
103
  const callee = match[1];
@@ -124,7 +106,6 @@ const collectPresenterOccurrences = (content, filePath, knownFactories) => {
124
106
  if (!callee || !presenterId) continue;
125
107
  if (!availableCallees.has(callee)) continue;
126
108
  if (quote === '`' && presenterId.includes('${')) continue;
127
-
128
109
  occurrences.push({
129
110
  id: presenterId,
130
111
  file: filePath,
@@ -132,10 +113,8 @@ const collectPresenterOccurrences = (content, filePath, knownFactories) => {
132
113
  callee
133
114
  });
134
115
  }
135
-
136
116
  return occurrences;
137
117
  };
138
-
139
118
  export default async function checkPresenterNames() {
140
119
  const srcPath = path.join(process.cwd(), 'src');
141
120
  try {
@@ -145,51 +124,37 @@ export default async function checkPresenterNames() {
145
124
  process.exitCode = 1;
146
125
  return;
147
126
  }
148
-
149
127
  const files = await listSourceFiles(srcPath);
150
128
  const fileContents = new Map();
151
-
152
129
  for (const filePath of files) {
153
- // eslint-disable-next-line no-await-in-loop
154
130
  const content = await fs.readFile(filePath, 'utf-8');
155
131
  fileContents.set(filePath, content);
156
132
  }
157
-
158
133
  const globalFactoryNames = new Set();
159
- for (const [filePath, content] of fileContents.entries()) {
134
+ for (const content of fileContents.values()) {
160
135
  for (const name of extractFactoryNamesFromContent(content)) {
161
136
  globalFactoryNames.add(name);
162
137
  }
163
- if (filePath.endsWith(path.join('core', 'presenter', 'index.ts')) || filePath.endsWith(path.join('core', 'presenter', 'index.js'))) {
164
- for (const name of extractImportedPresenterFactories(content)) {
165
- globalFactoryNames.add(name);
166
- }
167
- }
168
138
  }
169
-
170
139
  const occurrences = [];
171
140
  for (const [filePath, content] of fileContents.entries()) {
172
141
  occurrences.push(...collectPresenterOccurrences(content, filePath, globalFactoryNames));
173
142
  }
174
-
175
143
  if (!occurrences.length) {
176
144
  console.log('✅ No presenter declarations found.');
177
145
  return;
178
146
  }
179
-
180
147
  const grouped = new Map();
181
148
  for (const occurrence of occurrences) {
182
149
  const list = grouped.get(occurrence.id) ?? [];
183
150
  list.push(occurrence);
184
151
  grouped.set(occurrence.id, list);
185
152
  }
186
-
187
153
  const duplicated = Array.from(grouped.entries()).filter(([, list]) => list.length > 1);
188
154
  if (!duplicated.length) {
189
155
  console.log(`✅ Presenter names are unique (${occurrences.length} found).`);
190
156
  return;
191
157
  }
192
-
193
158
  console.error('❌ Duplicate presenter names found:');
194
159
  for (const [presenterId, list] of duplicated) {
195
160
  console.error(`\n- "${presenterId}"`);
@@ -197,6 +162,5 @@ export default async function checkPresenterNames() {
197
162
  console.error(` • ${toPosixPath(path.relative(process.cwd(), item.file))}:${item.line} (${item.callee})`);
198
163
  }
199
164
  }
200
-
201
165
  process.exitCode = 1;
202
166
  }
@@ -162,15 +162,10 @@ export default async function checkPresenterNames(): Promise<void> {
162
162
  }
163
163
 
164
164
  const globalFactoryNames = new Set<string>();
165
- for (const [filePath, content] of fileContents.entries()) {
165
+ for (const content of fileContents.values()) {
166
166
  for (const name of extractFactoryNamesFromContent(content)) {
167
167
  globalFactoryNames.add(name);
168
168
  }
169
- if (filePath.endsWith(path.join('core', 'presenter', 'index.ts')) || filePath.endsWith(path.join('core', 'presenter', 'index.js'))) {
170
- for (const name of extractImportedPresenterFactories(content)) {
171
- globalFactoryNames.add(name);
172
- }
173
- }
174
169
  }
175
170
 
176
171
  const occurrences: PresenterOccurrence[] = [];
@@ -1,15 +1,12 @@
1
1
  import { execSync } from 'node:child_process';
2
2
  import { loadUserConfig } from '../utils/loadConfig.js';
3
3
  import checkInternal from './checkInternal.js';
4
-
5
4
  const isPackageManager = (value) => typeof value === 'string' && ['pnpm', 'npm', 'yarn', 'bun'].includes(value);
6
-
7
5
  const runScript = (manager, script) => {
8
6
  const command = `${manager} run ${script}`;
9
7
  console.log(`\n▶ Running: ${command}`);
10
8
  execSync(command, { stdio: 'inherit' });
11
9
  };
12
-
13
10
  const runInternalChecks = async () => {
14
11
  console.log('\n▶ Running: azure-net check internal');
15
12
  process.exitCode = 0;
@@ -18,17 +15,14 @@ const runInternalChecks = async () => {
18
15
  throw new Error('azure-net check internal failed');
19
16
  }
20
17
  };
21
-
22
18
  export default async function checkProject() {
23
19
  const config = await loadUserConfig();
24
20
  const manager = config.packageManager;
25
-
26
21
  if (!isPackageManager(manager)) {
27
22
  console.error('❌ packageManager is missing or invalid in azure-net.config.ts/js. Use one of: bun, pnpm, npm, yarn');
28
23
  process.exitCode = 1;
29
24
  return;
30
25
  }
31
-
32
26
  try {
33
27
  runScript(manager, 'format');
34
28
  await runInternalChecks();