@archest/vitest 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/dist/index.js +13 -17
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.mjs +776 -489
  4. package/dist/index.mjs.map +1 -1
  5. package/dist/src/core/classes/classCheckExtendClass.d.ts +3 -0
  6. package/dist/src/core/classes/classCheckHaveMaxCyclomaticComplexity.d.ts +3 -0
  7. package/dist/src/core/classes/classCheckHaveModifier.d.ts +3 -0
  8. package/dist/src/core/classes/classCheckHaveNameMatchingFileName.d.ts +3 -0
  9. package/dist/src/core/classes/classCheckImplementInterface.d.ts +3 -0
  10. package/dist/src/core/classes/classCheckMatchNamePattern.d.ts +3 -0
  11. package/dist/src/core/classes/classCheckResideInFolder.d.ts +3 -0
  12. package/dist/src/core/classes/locateClasses.d.ts +3 -0
  13. package/dist/src/core/classes/types.d.ts +14 -0
  14. package/dist/src/core/files/checkDependOnFilesInFolder.d.ts +3 -0
  15. package/dist/src/core/files/fileCheckBeFreeOfCycles.d.ts +3 -0
  16. package/dist/src/core/files/fileCheckHaveMaxCyclomaticComplexity.d.ts +3 -0
  17. package/dist/src/core/files/fileCheckHaveMaxExportedFunctions.d.ts +3 -0
  18. package/dist/src/core/files/fileCheckHaveMinMaintainabilityIndex.d.ts +3 -0
  19. package/dist/src/core/files/fileCheckMatchNamePattern.d.ts +3 -0
  20. package/dist/src/core/files/getFileDependencies.d.ts +2 -0
  21. package/dist/src/core/files/locateFiles.d.ts +3 -0
  22. package/dist/src/core/files/types.d.ts +10 -0
  23. package/dist/src/core/functions/functionCheckHaveExplicitReturnType.d.ts +3 -0
  24. package/dist/src/core/functions/functionCheckHaveMaxCyclomaticComplexity.d.ts +3 -0
  25. package/dist/src/core/functions/functionCheckHaveMinMaintainabilityIndex.d.ts +3 -0
  26. package/dist/src/core/functions/functionCheckHaveModifier.d.ts +3 -0
  27. package/dist/src/core/functions/functionCheckHaveNameMatchingFileName.d.ts +3 -0
  28. package/dist/src/core/functions/functionCheckMatchNamePattern.d.ts +3 -0
  29. package/dist/src/core/functions/locateFunctions.d.ts +3 -0
  30. package/dist/src/core/functions/types.d.ts +11 -0
  31. package/dist/src/core/layers/checkLayeredArchitecture.d.ts +3 -0
  32. package/dist/src/core/layers/createLayeredArchitecture.d.ts +3 -0
  33. package/dist/src/core/layers/getLayerDependencies.d.ts +2 -0
  34. package/dist/src/core/layers/layer.d.ts +2 -0
  35. package/dist/src/core/layers/layerShouldNotBeAccessedByAnyLayer.d.ts +2 -0
  36. package/dist/src/core/layers/layerShouldOnlyBeAccessedBy.d.ts +2 -0
  37. package/dist/src/core/layers/types.d.ts +8 -0
  38. package/dist/src/core/metrics/calculateCyclomaticComplexity.d.ts +2 -0
  39. package/dist/src/core/metrics/calculateMaintainabilityIndex.d.ts +2 -0
  40. package/dist/src/core/project/parseProject.d.ts +22 -0
  41. package/dist/src/core/properties/locateProperties.d.ts +3 -0
  42. package/dist/src/core/properties/propertyCheckBeReadonly.d.ts +3 -0
  43. package/dist/src/core/properties/types.d.ts +10 -0
  44. package/dist/src/core/shared/sharedCheckHaveMaxCyclomaticComplexity.d.ts +1 -0
  45. package/dist/src/core/shared/sharedCheckHaveMinMaintainabilityIndex.d.ts +1 -0
  46. package/dist/src/core/shared/sharedCheckHaveModifier.d.ts +2 -0
  47. package/dist/src/core/shared/sharedCheckHaveNameMatchingFileName.d.ts +2 -0
  48. package/dist/src/core/shared/sharedCheckMatchNamePattern.d.ts +1 -0
  49. package/dist/src/core/slices/locateSlices.d.ts +3 -0
  50. package/dist/src/core/slices/sliceCheckBeFreeOfCycles.d.ts +3 -0
  51. package/dist/src/core/slices/sliceCheckHaveMaxDistanceFromMainSequence.d.ts +3 -0
  52. package/dist/src/core/slices/types.d.ts +8 -0
  53. package/dist/src/core/types.d.ts +7 -0
  54. package/dist/src/core/utils/ruleBuilder.d.ts +6 -0
  55. package/dist/src/index.d.ts +34 -8
  56. package/dist/src/matchers/index.d.ts +5 -0
  57. package/package.json +1 -1
  58. package/src/core/classes/classCheckExtendClass.test.ts +30 -0
  59. package/src/core/classes/classCheckExtendClass.ts +41 -0
  60. package/src/core/classes/classCheckHaveMaxCyclomaticComplexity.test.ts +8 -0
  61. package/src/core/classes/classCheckHaveMaxCyclomaticComplexity.ts +35 -0
  62. package/src/core/classes/classCheckHaveModifier.test.ts +30 -0
  63. package/src/core/classes/classCheckHaveModifier.ts +17 -0
  64. package/src/core/classes/classCheckHaveNameMatchingFileName.test.ts +30 -0
  65. package/src/core/classes/classCheckHaveNameMatchingFileName.ts +15 -0
  66. package/src/core/classes/classCheckImplementInterface.test.ts +30 -0
  67. package/src/core/classes/classCheckImplementInterface.ts +43 -0
  68. package/src/core/classes/classCheckMatchNamePattern.test.ts +8 -0
  69. package/src/core/classes/classCheckMatchNamePattern.ts +17 -0
  70. package/src/core/classes/classCheckResideInFolder.test.ts +8 -0
  71. package/src/core/classes/classCheckResideInFolder.ts +31 -0
  72. package/src/core/classes/locateClasses.test.ts +13 -0
  73. package/src/core/classes/locateClasses.ts +127 -0
  74. package/src/core/classes/types.ts +16 -0
  75. package/src/core/files/checkDependOnFilesInFolder.test.ts +7 -0
  76. package/src/core/files/checkDependOnFilesInFolder.ts +36 -0
  77. package/src/core/files/fileCheckBeFreeOfCycles.test.ts +7 -0
  78. package/src/core/files/fileCheckBeFreeOfCycles.ts +72 -0
  79. package/src/core/files/fileCheckHaveMaxCyclomaticComplexity.test.ts +8 -0
  80. package/src/core/files/fileCheckHaveMaxCyclomaticComplexity.ts +35 -0
  81. package/src/core/files/fileCheckHaveMaxExportedFunctions.test.ts +32 -0
  82. package/src/core/files/fileCheckHaveMaxExportedFunctions.ts +45 -0
  83. package/src/core/files/fileCheckHaveMinMaintainabilityIndex.test.ts +8 -0
  84. package/src/core/files/fileCheckHaveMinMaintainabilityIndex.ts +19 -0
  85. package/src/core/files/fileCheckMatchNamePattern.test.ts +30 -0
  86. package/src/core/files/fileCheckMatchNamePattern.ts +17 -0
  87. package/src/core/files/getFileDependencies.ts +43 -0
  88. package/src/core/files/locateFiles.test.ts +30 -0
  89. package/src/core/files/locateFiles.ts +31 -0
  90. package/src/core/files/types.ts +12 -0
  91. package/src/core/functions/functionCheckHaveExplicitReturnType.test.ts +30 -0
  92. package/src/core/functions/functionCheckHaveExplicitReturnType.ts +29 -0
  93. package/src/core/functions/functionCheckHaveMaxCyclomaticComplexity.test.ts +8 -0
  94. package/src/core/functions/functionCheckHaveMaxCyclomaticComplexity.ts +20 -0
  95. package/src/core/functions/functionCheckHaveMinMaintainabilityIndex.test.ts +8 -0
  96. package/src/core/functions/functionCheckHaveMinMaintainabilityIndex.ts +20 -0
  97. package/src/core/functions/functionCheckHaveModifier.test.ts +30 -0
  98. package/src/core/functions/functionCheckHaveModifier.ts +18 -0
  99. package/src/core/functions/functionCheckHaveNameMatchingFileName.test.ts +30 -0
  100. package/src/core/functions/functionCheckHaveNameMatchingFileName.ts +16 -0
  101. package/src/core/functions/functionCheckMatchNamePattern.test.ts +30 -0
  102. package/src/core/functions/functionCheckMatchNamePattern.ts +18 -0
  103. package/src/core/functions/locateFunctions.test.ts +16 -0
  104. package/src/core/functions/locateFunctions.ts +47 -0
  105. package/src/core/functions/types.ts +13 -0
  106. package/src/core/layers/checkLayeredArchitecture.test.ts +23 -0
  107. package/src/core/layers/checkLayeredArchitecture.ts +15 -0
  108. package/src/core/layers/createLayeredArchitecture.test.ts +13 -0
  109. package/src/core/layers/createLayeredArchitecture.ts +15 -0
  110. package/src/core/layers/getLayerDependencies.ts +45 -0
  111. package/src/core/layers/layer.test.ts +12 -0
  112. package/src/core/layers/layer.ts +10 -0
  113. package/src/core/layers/layerShouldNotBeAccessedByAnyLayer.test.ts +14 -0
  114. package/src/core/layers/layerShouldNotBeAccessedByAnyLayer.ts +40 -0
  115. package/src/core/layers/layerShouldOnlyBeAccessedBy.test.ts +15 -0
  116. package/src/core/layers/layerShouldOnlyBeAccessedBy.ts +48 -0
  117. package/src/core/layers/types.ts +9 -0
  118. package/src/core/metrics/calculateCyclomaticComplexity.test.ts +23 -0
  119. package/src/core/metrics/calculateCyclomaticComplexity.ts +36 -0
  120. package/src/core/metrics/calculateMaintainabilityIndex.test.ts +38 -0
  121. package/src/core/metrics/calculateMaintainabilityIndex.ts +47 -0
  122. package/src/core/project/parseProject.test.ts +19 -0
  123. package/src/core/project/parseProject.ts +216 -0
  124. package/src/core/properties/locateProperties.test.ts +19 -0
  125. package/src/core/properties/locateProperties.ts +41 -0
  126. package/src/core/properties/propertyCheckBeReadonly.test.ts +32 -0
  127. package/src/core/properties/propertyCheckBeReadonly.ts +27 -0
  128. package/src/core/properties/types.ts +12 -0
  129. package/src/core/shared/sharedCheckHaveMaxCyclomaticComplexity.test.ts +30 -0
  130. package/src/core/shared/sharedCheckHaveMaxCyclomaticComplexity.ts +23 -0
  131. package/src/core/shared/sharedCheckHaveMinMaintainabilityIndex.test.ts +30 -0
  132. package/src/core/shared/sharedCheckHaveMinMaintainabilityIndex.ts +23 -0
  133. package/src/core/shared/sharedCheckHaveModifier.test.ts +31 -0
  134. package/src/core/shared/sharedCheckHaveModifier.ts +51 -0
  135. package/src/core/shared/sharedCheckHaveNameMatchingFileName.test.ts +17 -0
  136. package/src/core/shared/sharedCheckHaveNameMatchingFileName.ts +36 -0
  137. package/src/core/shared/sharedCheckMatchNamePattern.test.ts +29 -0
  138. package/src/core/shared/sharedCheckMatchNamePattern.ts +22 -0
  139. package/src/core/slices/locateSlices.test.ts +22 -0
  140. package/src/core/slices/locateSlices.ts +35 -0
  141. package/src/core/slices/sliceCheckBeFreeOfCycles.test.ts +26 -0
  142. package/src/core/slices/sliceCheckBeFreeOfCycles.ts +91 -0
  143. package/src/core/slices/sliceCheckHaveMaxDistanceFromMainSequence.test.ts +27 -0
  144. package/src/core/slices/sliceCheckHaveMaxDistanceFromMainSequence.ts +110 -0
  145. package/src/core/slices/types.ts +9 -0
  146. package/src/core/testUtils.ts +61 -0
  147. package/src/core/types.ts +15 -0
  148. package/src/core/utils/ruleBuilder.test.ts +24 -0
  149. package/src/core/utils/ruleBuilder.ts +28 -0
  150. package/src/index.ts +34 -8
  151. package/src/matchers/index.ts +269 -51
  152. package/test/architecture.test.ts +109 -16
  153. package/tsconfig.json +2 -1
  154. package/vite.config.ts +2 -2
  155. package/dist/src/core/ClassLocator.d.ts +0 -20
  156. package/dist/src/core/FileLocator.d.ts +0 -15
  157. package/dist/src/core/FunctionLocator.d.ts +0 -15
  158. package/dist/src/core/LayerLocator.d.ts +0 -23
  159. package/dist/src/core/Project.d.ts +0 -20
  160. package/dist/src/core/PropertyLocator.d.ts +0 -12
  161. package/dist/src/core/SliceLocator.d.ts +0 -10
  162. package/src/core/ClassLocator.ts +0 -226
  163. package/src/core/FileLocator.ts +0 -168
  164. package/src/core/FunctionLocator.ts +0 -105
  165. package/src/core/LayerLocator.ts +0 -119
  166. package/src/core/Project.ts +0 -171
  167. package/src/core/PropertyLocator.ts +0 -50
  168. package/src/core/SliceLocator.ts +0 -92
@@ -1,171 +0,0 @@
1
- import * as ts from 'typescript';
2
- import * as path from 'path';
3
- import { FileLocator, FileQueryOptions } from './FileLocator';
4
- import { ClassLocator, ClassQueryOptions } from './ClassLocator';
5
- import { LayeredArchitectureBuilder } from './LayerLocator';
6
- import { FunctionLocator, FunctionQueryOptions } from './FunctionLocator';
7
- import { PropertyLocator, PropertyQueryOptions } from './PropertyLocator';
8
- import { SliceLocator } from './SliceLocator';
9
-
10
- export class Project {
11
- private program: ts.Program;
12
- private typeChecker: ts.TypeChecker;
13
-
14
- constructor(options: { tsConfigFilePath?: string } = {}) {
15
- let compilerOptions: ts.CompilerOptions = {};
16
- const configPath = options.tsConfigFilePath || ts.findConfigFile(
17
- process.cwd(),
18
- ts.sys.fileExists,
19
- 'tsconfig.json'
20
- );
21
-
22
- if (configPath) {
23
- const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
24
- const parsedCommandLine = ts.parseJsonConfigFileContent(
25
- configFile.config,
26
- ts.sys,
27
- path.dirname(configPath),
28
- undefined,
29
- configPath,
30
- undefined,
31
- [
32
- { extension: '.vue', isMixedContent: true, scriptKind: ts.ScriptKind.TS },
33
- { extension: '.svelte', isMixedContent: true, scriptKind: ts.ScriptKind.TS }
34
- ]
35
- );
36
- compilerOptions = parsedCommandLine.options;
37
-
38
- const host = ts.createCompilerHost(compilerOptions);
39
- const originalGetSourceFile = host.getSourceFile;
40
-
41
- let vueCompiler: any;
42
- try {
43
- vueCompiler = require('@vue/compiler-sfc');
44
- } catch (e) {
45
- // Not a Vue project
46
- }
47
-
48
- host.getSourceFile = (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
49
- if (fileName.endsWith('.vue')) {
50
- const content = ts.sys.readFile(fileName);
51
- if (content) {
52
- let scriptContent = '';
53
- if (vueCompiler) {
54
- const parsed = vueCompiler.parse(content);
55
- const script = parsed.descriptor.script;
56
- const scriptSetup = parsed.descriptor.scriptSetup;
57
- if (script) scriptContent += script.content + '\n';
58
- if (scriptSetup) scriptContent += scriptSetup.content + '\n';
59
- } else {
60
- // Fallback for Svelte or if compiler-sfc is missing (fragile but works for simple cases)
61
- const match = content.match(/<script[^>]*>(.*?)<\/script>/s);
62
- if (match) scriptContent = match[1];
63
- }
64
- return ts.createSourceFile(fileName, scriptContent, languageVersion, true, ts.ScriptKind.TS);
65
- }
66
- }
67
-
68
- if (fileName.endsWith('.svelte')) {
69
- const content = ts.sys.readFile(fileName);
70
- if (content) {
71
- let scriptContent = '';
72
- const match = content.match(/<script[^>]*>(.*?)<\/script>/s);
73
- if (match) scriptContent = match[1];
74
- return ts.createSourceFile(fileName, scriptContent, languageVersion, true, ts.ScriptKind.TS);
75
- }
76
- }
77
-
78
- return originalGetSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
79
- };
80
-
81
- this.program = ts.createProgram({
82
- rootNames: parsedCommandLine.fileNames,
83
- options: compilerOptions,
84
- host: host
85
- });
86
- } else {
87
- throw new Error("Could not find tsconfig.json");
88
- }
89
-
90
- this.typeChecker = this.program.getTypeChecker();
91
- }
92
-
93
- getFiles(options?: FileQueryOptions) {
94
- const sourceFiles = this.program.getSourceFiles().filter(
95
- sf => !sf.isDeclarationFile && !sf.fileName.includes('node_modules')
96
- );
97
- return new FileLocator(sourceFiles, this.program, options);
98
- }
99
-
100
- getClasses(options?: ClassQueryOptions) {
101
- const sourceFiles = this.program.getSourceFiles().filter(
102
- sf => !sf.isDeclarationFile && !sf.fileName.includes('node_modules')
103
- );
104
-
105
- const allClasses: ts.ClassDeclaration[] = [];
106
- for (const sf of sourceFiles) {
107
- const visit = (node: ts.Node) => {
108
- if (ts.isClassDeclaration(node)) {
109
- allClasses.push(node);
110
- }
111
- ts.forEachChild(node, visit);
112
- };
113
- visit(sf);
114
- }
115
- return new ClassLocator(allClasses, this.program, options);
116
- }
117
-
118
- layeredArchitecture() {
119
- const sourceFiles = this.program.getSourceFiles().filter(
120
- sf => !sf.isDeclarationFile && !sf.fileName.includes('node_modules')
121
- );
122
- return new LayeredArchitectureBuilder(sourceFiles, this.program);
123
- }
124
-
125
- getFunctions(options?: FunctionQueryOptions) {
126
- const sourceFiles = this.program.getSourceFiles().filter(
127
- sf => !sf.isDeclarationFile && !sf.fileName.includes('node_modules')
128
- );
129
-
130
- const allFunctions: ts.FunctionLikeDeclaration[] = [];
131
- for (const sf of sourceFiles) {
132
- const visit = (node: ts.Node) => {
133
- if (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node) || ts.isArrowFunction(node)) {
134
- allFunctions.push(node);
135
- }
136
- ts.forEachChild(node, visit);
137
- };
138
- visit(sf);
139
- }
140
- return new FunctionLocator(allFunctions, this.program, options);
141
- }
142
-
143
- getProperties(options?: PropertyQueryOptions) {
144
- const sourceFiles = this.program.getSourceFiles().filter(
145
- sf => !sf.isDeclarationFile && !sf.fileName.includes('node_modules')
146
- );
147
-
148
- const allProperties: ts.PropertyDeclaration[] = [];
149
- for (const sf of sourceFiles) {
150
- const visit = (node: ts.Node) => {
151
- if (ts.isPropertyDeclaration(node)) {
152
- allProperties.push(node);
153
- }
154
- ts.forEachChild(node, visit);
155
- };
156
- visit(sf);
157
- }
158
- return new PropertyLocator(allProperties, this.program, options);
159
- }
160
-
161
- getSlices(pattern: string) {
162
- const sourceFiles = this.program.getSourceFiles().filter(
163
- sf => !sf.isDeclarationFile && !sf.fileName.includes('node_modules')
164
- );
165
- return new SliceLocator(sourceFiles, this.program, pattern);
166
- }
167
- }
168
-
169
- export function parseProject(tsConfigFilePath?: string): Project {
170
- return new Project({ tsConfigFilePath });
171
- }
@@ -1,50 +0,0 @@
1
- import * as ts from 'typescript';
2
- import { RuleResult } from './types';
3
-
4
- export interface PropertyQueryOptions {
5
- inFolder?: string;
6
- matchNamePattern?: string | RegExp;
7
- }
8
-
9
- export class PropertyLocator {
10
- public readonly properties: ts.PropertyDeclaration[];
11
-
12
- constructor(properties: ts.PropertyDeclaration[], private program: ts.Program, options?: PropertyQueryOptions) {
13
- let filtered = properties;
14
-
15
- if (options?.inFolder) {
16
- filtered = filtered.filter((p) => {
17
- const sourceFile = p.getSourceFile();
18
- return sourceFile.fileName.includes(`/${options.inFolder}/`) || sourceFile.fileName.includes(`\\${options.inFolder}\\`);
19
- });
20
- }
21
-
22
- if (options?.matchNamePattern) {
23
- const regex = typeof options.matchNamePattern === 'string' ? new RegExp(options.matchNamePattern) : options.matchNamePattern;
24
- filtered = filtered.filter((p) => {
25
- return ts.isIdentifier(p.name) && regex.test(p.name.text);
26
- });
27
- }
28
-
29
- this.properties = filtered;
30
- }
31
-
32
- checkBeReadonly(isNot: boolean): RuleResult {
33
- const violations: string[] = [];
34
- for (const p of this.properties) {
35
- const name = ts.isIdentifier(p.name) ? p.name.text : 'Anonymous Property';
36
- const modifiers = ts.canHaveModifiers(p) ? ts.getModifiers(p) : undefined;
37
- const matches = modifiers && modifiers.some((m: ts.Modifier) => m.kind === ts.SyntaxKind.ReadonlyKeyword);
38
-
39
- if (isNot && matches) {
40
- violations.push(`Property ${name} is readonly, but it shouldn't be.`);
41
- } else if (!isNot && !matches) {
42
- violations.push(`Property ${name} is not readonly, but it should be.`);
43
- }
44
- }
45
- return {
46
- pass: violations.length === 0,
47
- message: () => violations.join('\n'),
48
- };
49
- }
50
- }
@@ -1,92 +0,0 @@
1
- import * as ts from 'typescript';
2
- import { RuleResult } from './types';
3
-
4
- export class SliceLocator {
5
- private slicePattern: RegExp;
6
- private sliceIds: Set<string> = new Set();
7
- private sliceFiles: Map<string, ts.SourceFile[]> = new Map();
8
-
9
- constructor(sourceFiles: ts.SourceFile[], private program: ts.Program, pattern: string) {
10
- const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
11
- const regexStr = escaped.replace(/\*/g, '([^/\\\\]+)');
12
- this.slicePattern = new RegExp(regexStr);
13
-
14
- for (const sf of sourceFiles) {
15
- const match = sf.fileName.match(this.slicePattern);
16
- if (match && match[1]) {
17
- const sliceId = match[1];
18
- this.sliceIds.add(sliceId);
19
- if (!this.sliceFiles.has(sliceId)) {
20
- this.sliceFiles.set(sliceId, []);
21
- }
22
- this.sliceFiles.get(sliceId)!.push(sf);
23
- }
24
- }
25
- }
26
-
27
- checkBeFreeOfCycles(isNot: boolean): RuleResult {
28
- const graph: Map<string, Set<string>> = new Map();
29
- for (const slice of this.sliceIds) {
30
- graph.set(slice, new Set());
31
- }
32
-
33
- for (const [sliceId, files] of this.sliceFiles.entries()) {
34
- for (const sf of files) {
35
- ts.forEachChild(sf, (node) => {
36
- if (ts.isImportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
37
- const importPath = node.moduleSpecifier.text;
38
- const resolved = ts.resolveModuleName(importPath, sf.fileName, this.program.getCompilerOptions(), ts.sys);
39
- if (resolved.resolvedModule && resolved.resolvedModule.resolvedFileName) {
40
- const targetMatch = resolved.resolvedModule.resolvedFileName.match(this.slicePattern);
41
- if (targetMatch && targetMatch[1]) {
42
- const targetSlice = targetMatch[1];
43
- if (targetSlice !== sliceId && this.sliceIds.has(targetSlice)) {
44
- graph.get(sliceId)!.add(targetSlice);
45
- }
46
- }
47
- }
48
- }
49
- });
50
- }
51
- }
52
-
53
- const visited = new Set<string>();
54
- const recursionStack = new Set<string>();
55
- const violations: string[] = [];
56
-
57
- const dfs = (node: string, path: string[]): boolean => {
58
- visited.add(node);
59
- recursionStack.add(node);
60
-
61
- for (const neighbor of (graph.get(node) || [])) {
62
- if (!visited.has(neighbor)) {
63
- if (dfs(neighbor, [...path, neighbor])) return true;
64
- } else if (recursionStack.has(neighbor)) {
65
- violations.push(`Cycle detected between slices: ${path.join(' -> ')} -> ${neighbor}`);
66
- return true;
67
- }
68
- }
69
-
70
- recursionStack.delete(node);
71
- return false;
72
- };
73
-
74
- for (const slice of this.sliceIds) {
75
- if (!visited.has(slice)) {
76
- dfs(slice, [slice]);
77
- }
78
- }
79
-
80
- if (isNot) {
81
- return {
82
- pass: violations.length > 0,
83
- message: () => violations.length > 0 ? '' : 'Expected cycles between slices but found none.'
84
- };
85
- } else {
86
- return {
87
- pass: violations.length === 0,
88
- message: () => violations.join('\n')
89
- };
90
- }
91
- }
92
- }