@azure-net/cli 5.1.1 → 6.0.0-next.2

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 (176) hide show
  1. package/README.md +189 -0
  2. package/bin/azure-net.js +236 -221
  3. package/dist/bin/azure-net.d.ts +2 -0
  4. package/dist/src/checks/architecture.d.ts +20 -0
  5. package/dist/src/checks/checkDomain.d.ts +1 -0
  6. package/dist/src/checks/checkFoldersStructure.d.ts +1 -0
  7. package/dist/src/checks/checkHooks.d.ts +1 -0
  8. package/dist/src/checks/checkImports.d.ts +1 -0
  9. package/dist/src/checks/checkInternal.d.ts +1 -0
  10. package/dist/src/checks/checkLayerBoundaries.d.ts +1 -0
  11. package/dist/src/checks/checkLayerImports.d.ts +1 -0
  12. package/dist/src/checks/checkPresenterNames.d.ts +1 -0
  13. package/dist/src/checks/checkProject.d.ts +1 -0
  14. package/dist/src/checks/checkProviderGraph.d.ts +1 -0
  15. package/dist/src/checks/checkProviderNames.d.ts +1 -0
  16. package/dist/src/generate/layers/generateRepository.d.ts +1 -0
  17. package/dist/src/generate/layers/generateRepositoryMethod.d.ts +1 -0
  18. package/dist/src/generate/layers/generateUseCases.d.ts +1 -0
  19. package/dist/src/generate/module/generateDomain.d.ts +8 -0
  20. package/dist/src/generate/module/generateModule.d.ts +1 -0
  21. package/dist/src/generate/module/generateModulePreset.d.ts +1 -0
  22. package/dist/src/generate/module/generatePresenter.d.ts +29 -0
  23. package/dist/src/generate/module/generateRepository.d.ts +15 -0
  24. package/dist/src/generate/module/generateRepositoryMethod.d.ts +2 -0
  25. package/dist/src/generate/module/generateSchemaFromType.d.ts +9 -0
  26. package/dist/src/generate/module/generateStore.d.ts +1 -0
  27. package/dist/src/generate/module/generateTypesJson.d.ts +2 -0
  28. package/dist/src/generate/module/generateUseCases.d.ts +1 -0
  29. package/dist/src/generate/module/repositoryModuleShared.d.ts +89 -0
  30. package/dist/src/generate/routes/generateRoutes.d.ts +14 -0
  31. package/dist/src/generate/ui/generateComponent.d.ts +1 -0
  32. package/dist/src/generate/ui/generateDesignComponent.d.ts +1 -0
  33. package/dist/src/generate/ui/generateWidget.d.ts +1 -0
  34. package/dist/src/git/commit.d.ts +23 -0
  35. package/dist/src/git/push.d.ts +11 -0
  36. package/dist/src/git/runGit.d.ts +2 -0
  37. package/dist/src/init/initAliases.d.ts +12 -0
  38. package/dist/src/init/initEdges.d.ts +2 -0
  39. package/dist/src/init/initStructure.d.ts +1 -0
  40. package/dist/src/install/configureCommitTools.d.ts +1 -0
  41. package/dist/src/install/installFresh.d.ts +1 -0
  42. package/dist/src/migrate/migrate.d.ts +22 -0
  43. package/dist/src/migrate/presenters.d.ts +7 -0
  44. package/dist/src/plugins/addLint.d.ts +5 -0
  45. package/dist/src/scaffolds/createCoreSchema.d.ts +1 -0
  46. package/dist/src/scaffolds/createDatasource.d.ts +1 -0
  47. package/dist/src/scaffolds/createProgram.d.ts +1 -0
  48. package/dist/src/scaffolds/createResponse.d.ts +1 -0
  49. package/dist/src/scaffolds/createSchemaRule.d.ts +1 -0
  50. package/dist/src/scaffolds/createTranslationManager.d.ts +1 -0
  51. package/{src/scaffolds/index.ts → dist/src/scaffolds/index.d.ts} +0 -2
  52. package/dist/src/scaffolds/registerDatasource.d.ts +2 -0
  53. package/dist/src/scaffolds/updateDefaultFactoryConfig.d.ts +1 -0
  54. package/dist/src/utils/architecture.d.ts +31 -0
  55. package/dist/src/utils/contextUtils.d.ts +6 -0
  56. package/dist/src/utils/fileUtils.d.ts +5 -0
  57. package/dist/src/utils/flattenConfig.d.ts +2 -0
  58. package/dist/src/utils/legacyAliases.d.ts +1 -0
  59. package/dist/src/utils/loadConfig.d.ts +16 -0
  60. package/dist/src/utils/packageImports.d.ts +4 -0
  61. package/dist/src/utils/sharedFoundation.d.ts +20 -0
  62. package/dist/src/utils/viteConfig.d.ts +9 -0
  63. package/package.json +48 -36
  64. package/src/checks/architecture.js +154 -0
  65. package/src/checks/checkDomain.js +124 -154
  66. package/src/checks/checkFoldersStructure.js +64 -52
  67. package/src/checks/checkHooks.js +12 -12
  68. package/src/checks/checkImports.js +26 -142
  69. package/src/checks/checkInternal.js +27 -24
  70. package/src/checks/checkLayerBoundaries.js +22 -197
  71. package/src/checks/checkLayerImports.js +14 -134
  72. package/src/checks/checkPresenterNames.js +156 -143
  73. package/src/checks/checkProject.js +27 -26
  74. package/src/checks/checkProviderGraph.js +182 -166
  75. package/src/checks/checkProviderNames.js +123 -112
  76. package/src/generate/layers/generateRepository.js +1 -109
  77. package/src/generate/layers/generateRepositoryMethod.js +1 -90
  78. package/src/generate/layers/generateUseCases.js +1 -44
  79. package/src/generate/module/generateDomain.js +81 -105
  80. package/src/generate/module/generateModule.js +34 -68
  81. package/src/generate/module/generateModulePreset.js +188 -248
  82. package/src/generate/module/generatePresenter.js +441 -369
  83. package/src/generate/module/generateRepository.js +102 -121
  84. package/src/generate/module/generateRepositoryMethod.js +117 -0
  85. package/src/generate/module/generateSchemaFromType.js +283 -280
  86. package/src/generate/module/generateStore.js +41 -0
  87. package/src/generate/module/generateTypesJson.js +196 -195
  88. package/src/generate/module/generateUseCases.js +47 -55
  89. package/src/generate/module/repositoryModuleShared.js +480 -459
  90. package/src/generate/routes/generateRoutes.js +4 -7
  91. package/src/generate/ui/generateComponent.js +44 -64
  92. package/src/generate/ui/generateDesignComponent.js +21 -41
  93. package/src/generate/ui/generateWidget.js +21 -41
  94. package/src/git/commit.js +67 -0
  95. package/src/git/push.js +19 -0
  96. package/src/git/runGit.js +11 -0
  97. package/src/init/initAliases.js +19 -135
  98. package/src/init/initEdges.js +79 -54
  99. package/src/init/initStructure.js +31 -145
  100. package/src/install/configureCommitTools.js +1 -0
  101. package/src/install/installFresh.js +39 -68
  102. package/src/migrate/migrate.js +914 -0
  103. package/src/migrate/presenters.js +265 -0
  104. package/src/plugins/addLint.js +52 -219
  105. package/src/scaffolds/createCoreSchema.js +65 -91
  106. package/src/scaffolds/createDatasource.js +67 -101
  107. package/src/scaffolds/createProgram.js +8 -8
  108. package/src/scaffolds/createResponse.js +29 -29
  109. package/src/scaffolds/createSchemaRule.js +19 -19
  110. package/src/scaffolds/createTranslationManager.js +84 -37
  111. package/src/scaffolds/index.js +0 -2
  112. package/src/scaffolds/registerDatasource.js +59 -0
  113. package/src/scaffolds/updateDefaultFactoryConfig.js +31 -0
  114. package/src/utils/architecture.js +145 -0
  115. package/src/utils/contextUtils.js +32 -65
  116. package/src/utils/fileUtils.js +69 -50
  117. package/src/utils/flattenConfig.js +38 -0
  118. package/src/utils/legacyAliases.js +75 -0
  119. package/src/utils/loadConfig.js +76 -40
  120. package/src/utils/packageImports.js +63 -0
  121. package/src/utils/sharedFoundation.js +36 -42
  122. package/src/utils/viteConfig.js +295 -0
  123. package/.prettierrc +0 -7
  124. package/bin/azure-net.ts +0 -308
  125. package/eslint.config.js +0 -34
  126. package/src/checks/checkDomain.ts +0 -223
  127. package/src/checks/checkFoldersStructure.ts +0 -85
  128. package/src/checks/checkHooks.ts +0 -22
  129. package/src/checks/checkImports.ts +0 -193
  130. package/src/checks/checkInternal.ts +0 -48
  131. package/src/checks/checkLayerBoundaries.ts +0 -255
  132. package/src/checks/checkLayerImports.ts +0 -185
  133. package/src/checks/checkPresenterNames.ts +0 -203
  134. package/src/checks/checkProject.ts +0 -45
  135. package/src/checks/checkProviderGraph.ts +0 -234
  136. package/src/checks/checkProviderNames.ts +0 -165
  137. package/src/generate/layers/generateRepository.ts +0 -123
  138. package/src/generate/layers/generateRepositoryMethod.ts +0 -102
  139. package/src/generate/layers/generateUseCases.ts +0 -50
  140. package/src/generate/layers/repositoryShared.js +0 -506
  141. package/src/generate/layers/repositoryShared.ts +0 -662
  142. package/src/generate/module/generateDomain.ts +0 -147
  143. package/src/generate/module/generateModule.ts +0 -84
  144. package/src/generate/module/generateModulePreset.ts +0 -338
  145. package/src/generate/module/generatePresenter.ts +0 -498
  146. package/src/generate/module/generateRepository.ts +0 -158
  147. package/src/generate/module/generateSchemaFromType.ts +0 -368
  148. package/src/generate/module/generateTypesJson.ts +0 -259
  149. package/src/generate/module/generateUseCases.ts +0 -67
  150. package/src/generate/module/repositoryModuleShared.ts +0 -669
  151. package/src/generate/routes/generateRoutes.ts +0 -275
  152. package/src/generate/ui/generateComponent.ts +0 -100
  153. package/src/generate/ui/generateDesignComponent.ts +0 -71
  154. package/src/generate/ui/generateWidget.ts +0 -71
  155. package/src/init/initAliases.ts +0 -172
  156. package/src/init/initEdges.ts +0 -74
  157. package/src/init/initStructure.ts +0 -172
  158. package/src/install/installFresh.ts +0 -93
  159. package/src/plugins/addLint.ts +0 -274
  160. package/src/scaffolds/createCoreSchema.ts +0 -130
  161. package/src/scaffolds/createDatasource.ts +0 -161
  162. package/src/scaffolds/createDatasourceProvider.js +0 -127
  163. package/src/scaffolds/createDatasourceProvider.ts +0 -161
  164. package/src/scaffolds/createPresenterFactory.js +0 -83
  165. package/src/scaffolds/createPresenterFactory.ts +0 -102
  166. package/src/scaffolds/createProgram.ts +0 -19
  167. package/src/scaffolds/createResponse.ts +0 -66
  168. package/src/scaffolds/createSchemaRule.ts +0 -44
  169. package/src/scaffolds/createTranslationManager.ts +0 -80
  170. package/src/utils/contextUtils.ts +0 -88
  171. package/src/utils/fileUtils.ts +0 -74
  172. package/src/utils/loadConfig.ts +0 -68
  173. package/src/utils/sharedFoundation.ts +0 -70
  174. package/tsconfig.json +0 -19
  175. /package/{src/checks/index.ts → dist/src/checks/index.d.ts} +0 -0
  176. /package/{src/plugins/index.ts → dist/src/plugins/index.d.ts} +0 -0
@@ -1,223 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import { loadUserConfig } from '../utils/loadConfig.js';
4
- import { normalizeContexts } from '../init/initAliases.js';
5
-
6
- type DomainIssue = {
7
- file: string;
8
- line: number;
9
- message: string;
10
- };
11
-
12
- type Declaration = {
13
- kind: 'interface' | 'type';
14
- name: string;
15
- line: number;
16
- };
17
-
18
- const DOMAIN_FILE_EXTENSIONS = new Set(['.ts']);
19
- const IGNORED_DIRS = new Set(['node_modules', 'dist', '.svelte-kit', '.git']);
20
-
21
- const INTERFACE_DECLARATION_PATTERN = /(?:^|\n)\s*(?:export\s+)?interface\s+([A-Za-z_$][A-Za-z0-9_$]*)\b/g;
22
- const TYPE_DECLARATION_PATTERN = /(?:^|\n)\s*(?:export\s+)?type\s+([A-Za-z_$][A-Za-z0-9_$]*)\s*=/g;
23
-
24
- const EXPORT_STAR_PATTERN = /export\s+\*\s+from\s*['"][^'"]+['"]\s*;?/g;
25
- const EXPORT_FROM_PATTERN = /export\s+(?:type\s+)?\{[\s\S]*?\}\s+from\s*['"][^'"]+['"]\s*;?/g;
26
- const EXPORT_LOCAL_PATTERN = /export\s+(?:type\s+)?\{[\s\S]*?\}\s*;?/g;
27
-
28
- const toPosixPath = (value: string): string => value.split(path.sep).join('/');
29
-
30
- const stripComments = (content: string): string => content.replace(/\/\*[\s\S]*?\*\//g, '').replace(/(^|[^\\])\/\/.*$/gm, '$1');
31
-
32
- const stripExportStatements = (content: string): string =>
33
- content.replace(EXPORT_STAR_PATTERN, '').replace(EXPORT_FROM_PATTERN, '').replace(EXPORT_LOCAL_PATTERN, '');
34
-
35
- const findLineByIndex = (content: string, index: number): number => {
36
- let line = 1;
37
- for (let i = 0; i < index; i += 1) {
38
- if (content[i] === '\n') line += 1;
39
- }
40
- return line;
41
- };
42
-
43
- const getRelativeFilePath = (filePath: string): string => toPosixPath(path.relative(process.cwd(), filePath));
44
-
45
- const isEnumsPath = (filePath: string): boolean => {
46
- const normalized = toPosixPath(filePath);
47
- return normalized.includes('/enums/');
48
- };
49
-
50
- const isIndexFile = (filePath: string): boolean => path.basename(filePath) === 'index.ts';
51
-
52
- const startsWithInterfacePrefix = (filePath: string): boolean => /^I[A-Z]/.test(path.basename(filePath, '.ts'));
53
-
54
- const isExportOnlyIndex = (content: string): boolean => {
55
- const withoutComments = stripComments(content);
56
- const withoutExports = stripExportStatements(withoutComments);
57
- return withoutExports.trim().length === 0;
58
- };
59
-
60
- const validateName = (declaration: Declaration): string | null => {
61
- if (declaration.kind === 'interface') {
62
- return /^I[A-Z]/.test(declaration.name) ? null : `Interface "${declaration.name}" must start with "I" and have uppercase second letter`;
63
- }
64
-
65
- return /^[IT][A-Z]/.test(declaration.name)
66
- ? null
67
- : `Type "${declaration.name}" must start with "I" or "T" and have uppercase second letter`;
68
- };
69
-
70
- const extractDeclarations = (content: string): Declaration[] => {
71
- const cleaned = stripComments(content);
72
- const declarations: Declaration[] = [];
73
-
74
- const interfaceRegex = new RegExp(INTERFACE_DECLARATION_PATTERN.source, 'g');
75
- let interfaceMatch: RegExpExecArray | null;
76
- while ((interfaceMatch = interfaceRegex.exec(cleaned)) !== null) {
77
- const name = interfaceMatch[1];
78
- if (!name) continue;
79
- declarations.push({
80
- kind: 'interface',
81
- name,
82
- line: findLineByIndex(cleaned, interfaceMatch.index)
83
- });
84
- }
85
-
86
- const typeRegex = new RegExp(TYPE_DECLARATION_PATTERN.source, 'g');
87
- let typeMatch: RegExpExecArray | null;
88
- while ((typeMatch = typeRegex.exec(cleaned)) !== null) {
89
- const name = typeMatch[1];
90
- if (!name) continue;
91
- declarations.push({
92
- kind: 'type',
93
- name,
94
- line: findLineByIndex(cleaned, typeMatch.index)
95
- });
96
- }
97
-
98
- return declarations.sort((a, b) => a.line - b.line);
99
- };
100
-
101
- const listDomainFiles = async (domainRoot: string): Promise<string[]> => {
102
- const files: string[] = [];
103
- const stack: string[] = [domainRoot];
104
-
105
- while (stack.length) {
106
- const current = stack.pop();
107
- if (!current) continue;
108
-
109
- let entries: import('node:fs').Dirent[] = [];
110
- try {
111
- entries = await fs.readdir(current, { withFileTypes: true });
112
- } catch {
113
- continue;
114
- }
115
-
116
- for (const entry of entries) {
117
- if (entry.isDirectory()) {
118
- if (!IGNORED_DIRS.has(entry.name)) {
119
- stack.push(path.join(current, entry.name));
120
- }
121
- continue;
122
- }
123
-
124
- if (!entry.isFile()) continue;
125
- const filePath = path.join(current, entry.name);
126
- if (!DOMAIN_FILE_EXTENSIONS.has(path.extname(filePath))) continue;
127
- files.push(filePath);
128
- }
129
- }
130
-
131
- return files;
132
- };
133
-
134
- const collectIssuesForFile = (filePath: string, content: string): DomainIssue[] => {
135
- const issues: DomainIssue[] = [];
136
-
137
- if (isIndexFile(filePath) && !isExportOnlyIndex(content)) {
138
- issues.push({
139
- file: filePath,
140
- line: 1,
141
- message: 'index.ts must contain only exports. Move declarations to separate files'
142
- });
143
- }
144
-
145
- if (isEnumsPath(filePath)) {
146
- return issues;
147
- }
148
-
149
- if (!isIndexFile(filePath) && startsWithInterfacePrefix(filePath)) {
150
- issues.push({
151
- file: filePath,
152
- line: 1,
153
- message: 'Domain file names must not start with "I". Keep "I" only in interface/type names'
154
- });
155
- }
156
-
157
- const declarations = extractDeclarations(content);
158
- if (declarations.length > 5) {
159
- issues.push({
160
- file: filePath,
161
- line: declarations[5]?.line ?? 1,
162
- message: `Too many declarations in one file (${declarations.length}). Maximum is 5`
163
- });
164
- }
165
-
166
- for (const declaration of declarations) {
167
- const validationError = validateName(declaration);
168
- if (!validationError) continue;
169
- issues.push({
170
- file: filePath,
171
- line: declaration.line,
172
- message: validationError
173
- });
174
- }
175
-
176
- return issues;
177
- };
178
-
179
- export default async function checkDomain(): Promise<void> {
180
- const config = await loadUserConfig();
181
- const contexts = normalizeContexts(config.contexts);
182
-
183
- if (!contexts.length) {
184
- console.error('❌ No contexts found in azure-net.config.ts/js. Fill `contexts` first.');
185
- process.exitCode = 1;
186
- return;
187
- }
188
-
189
- const issues: DomainIssue[] = [];
190
-
191
- for (const context of contexts) {
192
- const domainRoot = path.join(process.cwd(), 'src', 'app', context.name, 'layers', 'domain');
193
-
194
- try {
195
- await fs.access(domainRoot);
196
- } catch {
197
- issues.push({
198
- file: domainRoot,
199
- line: 1,
200
- message: `Domain folder not found for context "${context.name}"`
201
- });
202
- continue;
203
- }
204
-
205
- const files = await listDomainFiles(domainRoot);
206
- for (const filePath of files) {
207
- const content = await fs.readFile(filePath, 'utf-8');
208
- issues.push(...collectIssuesForFile(filePath, content));
209
- }
210
- }
211
-
212
- if (!issues.length) {
213
- console.log('✅ Domain check passed: naming, per-file limits and index exports are valid.');
214
- return;
215
- }
216
-
217
- console.error('❌ Domain check failed:');
218
- for (const issue of issues) {
219
- console.error(`• ${getRelativeFilePath(issue.file)}:${issue.line} — ${issue.message}`);
220
- }
221
-
222
- process.exitCode = 1;
223
- }
@@ -1,85 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import { loadUserConfig } from '../utils/loadConfig.js';
4
- import { normalizeContexts } from '../init/initAliases.js';
5
-
6
- type StructureIssue = {
7
- file: string;
8
- message: string;
9
- };
10
-
11
- type ContextConfig = {
12
- name: string;
13
- alias: string;
14
- };
15
-
16
- const ALLOWED_LAYERS_FOLDERS = new Set(['domain', 'presentation', 'infrastructure', 'application']);
17
- const ALLOWED_UI_FOLDERS = new Set(['components', 'widgets', 'design', 'style']);
18
- const ALLOWED_COMPONENTS_FOLDERS = new Set(['page', 'layout', 'shared']);
19
-
20
- const toPosixPath = (value: string): string => value.split(path.sep).join('/');
21
-
22
- const getRelativePath = (filePath: string): string => toPosixPath(path.relative(process.cwd(), filePath));
23
-
24
- const getDirectories = async (dirPath: string): Promise<string[] | null> => {
25
- try {
26
- const entries = await fs.readdir(dirPath, { withFileTypes: true });
27
- return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
28
- } catch {
29
- return null;
30
- }
31
- };
32
-
33
- const checkOnlyAllowedFolders = async (
34
- basePath: string,
35
- allowed: Set<string>,
36
- scopeLabel: string,
37
- issues: StructureIssue[]
38
- ): Promise<void> => {
39
- const directories = await getDirectories(basePath);
40
- if (!directories) return;
41
-
42
- for (const dir of directories) {
43
- if (allowed.has(dir)) continue;
44
- issues.push({
45
- file: basePath,
46
- message: `${scopeLabel} can contain only: ${Array.from(allowed).join(', ')}. Found: ${dir}`
47
- });
48
- }
49
- };
50
-
51
- export default async function checkFoldersStructure(): Promise<void> {
52
- const config = await loadUserConfig();
53
- const contexts = normalizeContexts(config.contexts) as ContextConfig[];
54
-
55
- if (!contexts.length) {
56
- console.error('❌ No contexts found in azure-net.config.ts/js. Fill `contexts` first.');
57
- process.exitCode = 1;
58
- return;
59
- }
60
-
61
- const issues: StructureIssue[] = [];
62
-
63
- for (const context of contexts) {
64
- const contextRoot = path.join(process.cwd(), 'src', 'app', context.name);
65
- const layersPath = path.join(contextRoot, 'layers');
66
- const uiPath = path.join(contextRoot, 'ui');
67
- const componentsPath = path.join(uiPath, 'components');
68
-
69
- await checkOnlyAllowedFolders(layersPath, ALLOWED_LAYERS_FOLDERS, `layers (${context.name})`, issues);
70
- await checkOnlyAllowedFolders(uiPath, ALLOWED_UI_FOLDERS, `ui (${context.name})`, issues);
71
- await checkOnlyAllowedFolders(componentsPath, ALLOWED_COMPONENTS_FOLDERS, `ui/components (${context.name})`, issues);
72
- }
73
-
74
- if (!issues.length) {
75
- console.log('✅ Folders structure check passed.');
76
- return;
77
- }
78
-
79
- console.error('❌ Folders structure check failed:');
80
- for (const issue of issues) {
81
- console.error(`• ${getRelativePath(issue.file)} — ${issue.message}`);
82
- }
83
-
84
- process.exitCode = 1;
85
- }
@@ -1,22 +0,0 @@
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
- }
@@ -1,193 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
-
4
- type ImportIssue = {
5
- file: string;
6
- line: number;
7
- message: string;
8
- };
9
-
10
- type ImportKind = 'value' | 'type' | 'side-effect';
11
-
12
- type ImportEntry = {
13
- kind: ImportKind;
14
- line: number;
15
- source: string;
16
- raw: string;
17
- };
18
-
19
- const TARGET_EXTENSIONS = new Set(['.ts', '.js', '.svelte']);
20
- const IGNORED_DIRS = new Set(['node_modules', 'dist', '.svelte-kit', '.git']);
21
- const IMPORT_DECLARATION_PATTERN = /(^|\n)\s*import\s+(?:[\s\S]*?\s+from\s*)?['"]([^'"]+)['"]\s*;?/g;
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)(?:$|[?#])/;
24
-
25
- const toPosixPath = (value: string): string => value.split(path.sep).join('/');
26
-
27
- const getRelativePath = (filePath: string): string => toPosixPath(path.relative(process.cwd(), filePath));
28
-
29
- const findLineByIndex = (content: string, index: number): number => {
30
- let line = 1;
31
- for (let i = 0; i < index; i += 1) {
32
- if (content[i] === '\n') line += 1;
33
- }
34
- return line;
35
- };
36
-
37
- const listFiles = async (rootDir: string): Promise<string[]> => {
38
- const files: string[] = [];
39
- const stack: string[] = [rootDir];
40
-
41
- while (stack.length) {
42
- const current = stack.pop();
43
- if (!current) continue;
44
-
45
- let entries: import('node:fs').Dirent[] = [];
46
- try {
47
- entries = await fs.readdir(current, { withFileTypes: true });
48
- } catch {
49
- continue;
50
- }
51
-
52
- for (const entry of entries) {
53
- if (entry.isDirectory()) {
54
- if (!IGNORED_DIRS.has(entry.name)) {
55
- stack.push(path.join(current, entry.name));
56
- }
57
- continue;
58
- }
59
-
60
- if (!entry.isFile()) continue;
61
- const filePath = path.join(current, entry.name);
62
- if (!TARGET_EXTENSIONS.has(path.extname(filePath))) continue;
63
- files.push(filePath);
64
- }
65
- }
66
-
67
- return files;
68
- };
69
-
70
- const getImportKind = (raw: string): ImportKind => {
71
- const normalized = raw.trim();
72
- if (/^import\s+type\b/.test(normalized)) return 'type';
73
- if (/^import\s*['"]/.test(normalized)) return 'side-effect';
74
- return 'value';
75
- };
76
-
77
- const getKindOrder = (kind: ImportKind): number => {
78
- if (kind === 'value') return 0;
79
- if (kind === 'type') return 1;
80
- return 2;
81
- };
82
-
83
- const extractImports = (content: string): ImportEntry[] => {
84
- const imports: ImportEntry[] = [];
85
- const regex = new RegExp(IMPORT_DECLARATION_PATTERN.source, 'g');
86
- let match: RegExpExecArray | null;
87
-
88
- while ((match = regex.exec(content)) !== null) {
89
- const raw = match[0].trim();
90
- const source = match[2];
91
- if (!source) continue;
92
-
93
- imports.push({
94
- kind: getImportKind(raw),
95
- line: findLineByIndex(content, match.index + (match[1] === '\n' ? 1 : 0)),
96
- source,
97
- raw
98
- });
99
- }
100
-
101
- return imports.sort((a, b) => a.line - b.line);
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
-
121
- const hasForbiddenExtension = (source: string): boolean => {
122
- const sourceWithoutQuery = source.split('?')[0] ?? source;
123
- return FORBIDDEN_IMPORT_EXTENSION_PATTERN.test(sourceWithoutQuery);
124
- };
125
-
126
- const collectIssuesForFile = (filePath: string, content: string): ImportIssue[] => {
127
- const issues: ImportIssue[] = [];
128
- const imports = extractImports(content);
129
- let lastKindOrder = 0;
130
-
131
- for (const importEntry of imports) {
132
- if (hasForbiddenExtension(importEntry.source)) {
133
- issues.push({
134
- file: filePath,
135
- line: importEntry.line,
136
- message: `Import "${importEntry.source}" must not include .ts/.js extension`
137
- });
138
- }
139
-
140
- const currentOrder = getKindOrder(importEntry.kind);
141
- if (currentOrder < lastKindOrder) {
142
- issues.push({
143
- file: filePath,
144
- line: importEntry.line,
145
- message: 'Import order violation: value imports first, then type imports, then side-effect imports'
146
- });
147
- }
148
- lastKindOrder = Math.max(lastKindOrder, currentOrder);
149
- }
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
-
160
- return issues;
161
- };
162
-
163
- export default async function checkImports(): Promise<void> {
164
- const srcRoot = path.join(process.cwd(), 'src');
165
-
166
- try {
167
- await fs.access(srcRoot);
168
- } catch {
169
- console.error('❌ src folder not found.');
170
- process.exitCode = 1;
171
- return;
172
- }
173
-
174
- const issues: ImportIssue[] = [];
175
- const files = await listFiles(srcRoot);
176
-
177
- for (const filePath of files) {
178
- const content = await fs.readFile(filePath, 'utf-8');
179
- issues.push(...collectIssuesForFile(filePath, content));
180
- }
181
-
182
- if (!issues.length) {
183
- console.log('✅ Imports check passed: extensions and import groups are valid.');
184
- return;
185
- }
186
-
187
- console.error('❌ Imports check failed:');
188
- for (const issue of issues) {
189
- console.error(`• ${getRelativePath(issue.file)}:${issue.line} — ${issue.message}`);
190
- }
191
-
192
- process.exitCode = 1;
193
- }
@@ -1,48 +0,0 @@
1
- import checkPresenterNames from './checkPresenterNames.js';
2
- import checkProviderNames from './checkProviderNames.js';
3
- import checkProviderGraph from './checkProviderGraph.js';
4
- import checkDomain from './checkDomain.js';
5
- import checkLayerBoundaries from './checkLayerBoundaries.js';
6
- import checkFoldersStructure from './checkFoldersStructure.js';
7
- import checkImports from './checkImports.js';
8
- import checkHooks from './checkHooks.js';
9
- import checkLayerImports from './checkLayerImports.js';
10
-
11
- type CheckTask = {
12
- name: string;
13
- run: () => Promise<void>;
14
- };
15
-
16
- const tasks: CheckTask[] = [
17
- { name: 'presenter-names', run: checkPresenterNames },
18
- { name: 'provider-names', run: checkProviderNames },
19
- { name: 'provider-graph', run: checkProviderGraph },
20
- { name: 'domain', run: checkDomain },
21
- { name: 'imports', run: checkImports },
22
- { name: 'hooks', run: checkHooks },
23
- { name: 'layer-imports', run: checkLayerImports },
24
- { name: 'layer-boundaries', run: checkLayerBoundaries },
25
- { name: 'folders-structure', run: checkFoldersStructure }
26
- ];
27
-
28
- const runTask = async (task: CheckTask): Promise<void> => {
29
- console.log(`\n▶ Running check ${task.name}...`);
30
- process.exitCode = 0;
31
- await task.run();
32
-
33
- if ((process.exitCode ?? 0) !== 0) {
34
- throw new Error(`Check "${task.name}" failed`);
35
- }
36
- };
37
-
38
- export default async function checkInternal(): Promise<void> {
39
- try {
40
- for (const task of tasks) {
41
- await runTask(task);
42
- }
43
- console.log('\n✅ Internal checks passed.');
44
- } catch (error) {
45
- console.error(`\n❌ Internal checks failed: ${error instanceof Error ? error.message : String(error)}`);
46
- process.exitCode = 1;
47
- }
48
- }