@archest/vitest 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +16 -0
- package/dist/index.js +1 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +44 -764
- package/dist/index.mjs.map +1 -1
- package/dist/matchers/index.d.ts +16 -0
- package/dist/matchers/models.d.ts +201 -0
- package/node_modules/@archest/core/dist/classes/locateClasses.d.ts +5 -0
- package/node_modules/@archest/core/dist/classes/types.d.ts +25 -0
- package/node_modules/@archest/core/dist/dto.d.ts +78 -0
- package/node_modules/@archest/core/dist/files/checkDependOnExternalModule.d.ts +3 -0
- package/node_modules/@archest/core/dist/files/getFileDependencies.d.ts +2 -0
- package/node_modules/@archest/core/dist/files/locateFiles.d.ts +4 -0
- package/node_modules/@archest/core/dist/files/types.d.ts +17 -0
- package/node_modules/@archest/core/dist/functions/locateFunctions.d.ts +5 -0
- package/node_modules/@archest/core/dist/functions/types.d.ts +19 -0
- package/node_modules/@archest/core/dist/index.d.ts +43 -0
- package/node_modules/@archest/core/dist/index.js +14 -0
- package/node_modules/@archest/core/dist/index.js.map +1 -0
- package/node_modules/@archest/core/dist/index.mjs +615 -0
- package/node_modules/@archest/core/dist/index.mjs.map +1 -0
- package/node_modules/@archest/core/dist/layers/createLayeredArchitecture.d.ts +3 -0
- package/node_modules/@archest/core/dist/layers/getLayerDependencies.d.ts +2 -0
- package/node_modules/@archest/core/dist/layers/types.d.ts +8 -0
- package/node_modules/@archest/core/dist/project/parseProject.d.ts +55 -0
- package/node_modules/@archest/core/dist/properties/locateProperties.d.ts +5 -0
- package/node_modules/@archest/core/dist/properties/types.d.ts +17 -0
- package/node_modules/@archest/core/dist/shared/sharedCheckHaveMaxCyclomaticComplexity.d.ts +1 -0
- package/node_modules/@archest/core/dist/shared/sharedCheckHaveMinMaintainabilityIndex.d.ts +1 -0
- package/node_modules/@archest/core/dist/shared/sharedCheckHaveModifier.d.ts +7 -0
- package/node_modules/@archest/core/dist/shared/sharedCheckHaveNameMatchingFileName.d.ts +3 -0
- package/node_modules/@archest/core/dist/shared/sharedCheckMatchNamePattern.d.ts +1 -0
- package/node_modules/@archest/core/dist/slices/locateSlices.d.ts +3 -0
- package/node_modules/@archest/core/dist/slices/types.d.ts +8 -0
- package/node_modules/@archest/core/package.json +49 -0
- package/node_modules/@archest/core-rust/core-rust.darwin-arm64.node +0 -0
- package/node_modules/@archest/core-rust/core-rust.linux-x64-gnu.node +0 -0
- package/node_modules/@archest/core-rust/core-rust.win32-x64-msvc.node +0 -0
- package/node_modules/@archest/core-rust/index.d.ts +16 -0
- package/node_modules/@archest/core-rust/index.js +316 -0
- package/node_modules/@archest/core-rust/package.json +34 -0
- package/package.json +12 -1
- package/dist/src/core/classes/locateClasses.d.ts +0 -3
- package/dist/src/core/classes/types.d.ts +0 -14
- package/dist/src/core/files/getFileDependencies.d.ts +0 -2
- package/dist/src/core/files/locateFiles.d.ts +0 -3
- package/dist/src/core/files/types.d.ts +0 -10
- package/dist/src/core/functions/locateFunctions.d.ts +0 -3
- package/dist/src/core/functions/types.d.ts +0 -11
- package/dist/src/core/layers/createLayeredArchitecture.d.ts +0 -3
- package/dist/src/core/layers/getLayerDependencies.d.ts +0 -2
- package/dist/src/core/layers/types.d.ts +0 -8
- package/dist/src/core/project/parseProject.d.ts +0 -22
- package/dist/src/core/properties/locateProperties.d.ts +0 -3
- package/dist/src/core/properties/types.d.ts +0 -10
- package/dist/src/core/shared/sharedCheckHaveMaxCyclomaticComplexity.d.ts +0 -1
- package/dist/src/core/shared/sharedCheckHaveMinMaintainabilityIndex.d.ts +0 -1
- package/dist/src/core/shared/sharedCheckHaveModifier.d.ts +0 -2
- package/dist/src/core/shared/sharedCheckHaveNameMatchingFileName.d.ts +0 -2
- package/dist/src/core/shared/sharedCheckMatchNamePattern.d.ts +0 -1
- package/dist/src/core/slices/locateSlices.d.ts +0 -3
- package/dist/src/core/slices/types.d.ts +0 -8
- package/dist/src/index.d.ts +0 -35
- package/dist/src/matchers/index.d.ts +0 -20
- package/src/core/classes/classCheckExtendClass.test.ts +0 -30
- package/src/core/classes/classCheckExtendClass.ts +0 -41
- package/src/core/classes/classCheckHaveMaxCyclomaticComplexity.test.ts +0 -8
- package/src/core/classes/classCheckHaveMaxCyclomaticComplexity.ts +0 -35
- package/src/core/classes/classCheckHaveModifier.test.ts +0 -30
- package/src/core/classes/classCheckHaveModifier.ts +0 -17
- package/src/core/classes/classCheckHaveNameMatchingFileName.test.ts +0 -30
- package/src/core/classes/classCheckHaveNameMatchingFileName.ts +0 -15
- package/src/core/classes/classCheckImplementInterface.test.ts +0 -30
- package/src/core/classes/classCheckImplementInterface.ts +0 -43
- package/src/core/classes/classCheckMatchNamePattern.test.ts +0 -8
- package/src/core/classes/classCheckMatchNamePattern.ts +0 -17
- package/src/core/classes/classCheckResideInFolder.test.ts +0 -8
- package/src/core/classes/classCheckResideInFolder.ts +0 -31
- package/src/core/classes/locateClasses.test.ts +0 -13
- package/src/core/classes/locateClasses.ts +0 -127
- package/src/core/classes/types.ts +0 -16
- package/src/core/files/checkDependOnFilesInFolder.test.ts +0 -7
- package/src/core/files/checkDependOnFilesInFolder.ts +0 -36
- package/src/core/files/fileCheckBeFreeOfCycles.test.ts +0 -7
- package/src/core/files/fileCheckBeFreeOfCycles.ts +0 -72
- package/src/core/files/fileCheckHaveMaxCyclomaticComplexity.test.ts +0 -8
- package/src/core/files/fileCheckHaveMaxCyclomaticComplexity.ts +0 -35
- package/src/core/files/fileCheckHaveMaxExportedFunctions.test.ts +0 -32
- package/src/core/files/fileCheckHaveMaxExportedFunctions.ts +0 -45
- package/src/core/files/fileCheckHaveMinMaintainabilityIndex.test.ts +0 -8
- package/src/core/files/fileCheckHaveMinMaintainabilityIndex.ts +0 -19
- package/src/core/files/fileCheckMatchNamePattern.test.ts +0 -30
- package/src/core/files/fileCheckMatchNamePattern.ts +0 -17
- package/src/core/files/getFileDependencies.ts +0 -43
- package/src/core/files/locateFiles.test.ts +0 -30
- package/src/core/files/locateFiles.ts +0 -31
- package/src/core/files/types.ts +0 -12
- package/src/core/functions/functionCheckHaveExplicitReturnType.test.ts +0 -30
- package/src/core/functions/functionCheckHaveExplicitReturnType.ts +0 -29
- package/src/core/functions/functionCheckHaveMaxCyclomaticComplexity.test.ts +0 -8
- package/src/core/functions/functionCheckHaveMaxCyclomaticComplexity.ts +0 -20
- package/src/core/functions/functionCheckHaveMinMaintainabilityIndex.test.ts +0 -8
- package/src/core/functions/functionCheckHaveMinMaintainabilityIndex.ts +0 -20
- package/src/core/functions/functionCheckHaveModifier.test.ts +0 -30
- package/src/core/functions/functionCheckHaveModifier.ts +0 -18
- package/src/core/functions/functionCheckHaveNameMatchingFileName.test.ts +0 -30
- package/src/core/functions/functionCheckHaveNameMatchingFileName.ts +0 -16
- package/src/core/functions/functionCheckMatchNamePattern.test.ts +0 -30
- package/src/core/functions/functionCheckMatchNamePattern.ts +0 -18
- package/src/core/functions/locateFunctions.test.ts +0 -16
- package/src/core/functions/locateFunctions.ts +0 -47
- package/src/core/functions/types.ts +0 -13
- package/src/core/layers/checkLayeredArchitecture.test.ts +0 -23
- package/src/core/layers/checkLayeredArchitecture.ts +0 -15
- package/src/core/layers/createLayeredArchitecture.test.ts +0 -13
- package/src/core/layers/createLayeredArchitecture.ts +0 -15
- package/src/core/layers/getLayerDependencies.ts +0 -45
- package/src/core/layers/layer.test.ts +0 -12
- package/src/core/layers/layer.ts +0 -10
- package/src/core/layers/layerShouldNotBeAccessedByAnyLayer.test.ts +0 -14
- package/src/core/layers/layerShouldNotBeAccessedByAnyLayer.ts +0 -40
- package/src/core/layers/layerShouldOnlyBeAccessedBy.test.ts +0 -15
- package/src/core/layers/layerShouldOnlyBeAccessedBy.ts +0 -48
- package/src/core/layers/types.ts +0 -9
- package/src/core/metrics/calculateCyclomaticComplexity.test.ts +0 -23
- package/src/core/metrics/calculateCyclomaticComplexity.ts +0 -36
- package/src/core/metrics/calculateMaintainabilityIndex.test.ts +0 -38
- package/src/core/metrics/calculateMaintainabilityIndex.ts +0 -47
- package/src/core/project/parseProject.test.ts +0 -19
- package/src/core/project/parseProject.ts +0 -216
- package/src/core/properties/locateProperties.test.ts +0 -19
- package/src/core/properties/locateProperties.ts +0 -41
- package/src/core/properties/propertyCheckBeReadonly.test.ts +0 -32
- package/src/core/properties/propertyCheckBeReadonly.ts +0 -27
- package/src/core/properties/types.ts +0 -12
- package/src/core/shared/sharedCheckHaveMaxCyclomaticComplexity.test.ts +0 -30
- package/src/core/shared/sharedCheckHaveMaxCyclomaticComplexity.ts +0 -23
- package/src/core/shared/sharedCheckHaveMinMaintainabilityIndex.test.ts +0 -30
- package/src/core/shared/sharedCheckHaveMinMaintainabilityIndex.ts +0 -23
- package/src/core/shared/sharedCheckHaveModifier.test.ts +0 -31
- package/src/core/shared/sharedCheckHaveModifier.ts +0 -51
- package/src/core/shared/sharedCheckHaveNameMatchingFileName.test.ts +0 -17
- package/src/core/shared/sharedCheckHaveNameMatchingFileName.ts +0 -36
- package/src/core/shared/sharedCheckMatchNamePattern.test.ts +0 -29
- package/src/core/shared/sharedCheckMatchNamePattern.ts +0 -22
- package/src/core/slices/locateSlices.test.ts +0 -22
- package/src/core/slices/locateSlices.ts +0 -35
- package/src/core/slices/sliceCheckBeFreeOfCycles.test.ts +0 -26
- package/src/core/slices/sliceCheckBeFreeOfCycles.ts +0 -91
- package/src/core/slices/sliceCheckHaveMaxDistanceFromMainSequence.test.ts +0 -27
- package/src/core/slices/sliceCheckHaveMaxDistanceFromMainSequence.ts +0 -110
- package/src/core/slices/types.ts +0 -9
- package/src/core/testUtils.ts +0 -61
- package/src/core/types.ts +0 -19
- package/src/core/utils/ruleBuilder.test.ts +0 -24
- package/src/core/utils/ruleBuilder.ts +0 -28
- package/src/index.ts +0 -35
- package/src/matchers/index.ts +0 -331
- package/test/architecture.test.ts +0 -152
- package/tsconfig.json +0 -18
- package/vite.config.ts +0 -18
- /package/{dist/src/core → node_modules/@archest/core/dist}/classes/classCheckExtendClass.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/classes/classCheckHaveMaxCyclomaticComplexity.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/classes/classCheckHaveModifier.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/classes/classCheckHaveNameMatchingFileName.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/classes/classCheckImplementInterface.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/classes/classCheckMatchNamePattern.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/classes/classCheckResideInFolder.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/files/checkDependOnFilesInFolder.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/files/fileCheckBeFreeOfCycles.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/files/fileCheckHaveMaxCyclomaticComplexity.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/files/fileCheckHaveMaxExportedFunctions.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/files/fileCheckHaveMinMaintainabilityIndex.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/files/fileCheckMatchNamePattern.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/functions/functionCheckHaveExplicitReturnType.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/functions/functionCheckHaveMaxCyclomaticComplexity.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/functions/functionCheckHaveMinMaintainabilityIndex.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/functions/functionCheckHaveModifier.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/functions/functionCheckHaveNameMatchingFileName.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/functions/functionCheckMatchNamePattern.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/layers/checkLayeredArchitecture.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/layers/layer.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/layers/layerShouldNotBeAccessedByAnyLayer.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/layers/layerShouldOnlyBeAccessedBy.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/metrics/calculateCyclomaticComplexity.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/metrics/calculateMaintainabilityIndex.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/properties/propertyCheckBeReadonly.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/slices/sliceCheckBeFreeOfCycles.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/slices/sliceCheckHaveMaxDistanceFromMainSequence.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/types.d.ts +0 -0
- /package/{dist/src/core → node_modules/@archest/core/dist}/utils/ruleBuilder.d.ts +0 -0
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { classCheckHaveMaxCyclomaticComplexity } from './classCheckHaveMaxCyclomaticComplexity';
|
|
3
|
-
|
|
4
|
-
describe('classCheckHaveMaxCyclomaticComplexity', () => {
|
|
5
|
-
it('should exist and be callable', () => {
|
|
6
|
-
expect(typeof classCheckHaveMaxCyclomaticComplexity).toBe('function');
|
|
7
|
-
});
|
|
8
|
-
});
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import * as ts from 'typescript';
|
|
2
|
-
import { calculateCyclomaticComplexity } from '../metrics/calculateCyclomaticComplexity';
|
|
3
|
-
import { sharedCheckHaveMaxCyclomaticComplexity } from '../shared/sharedCheckHaveMaxCyclomaticComplexity';
|
|
4
|
-
import type { RuleResult } from '../types';
|
|
5
|
-
import type { ClassLocatorData } from './types';
|
|
6
|
-
|
|
7
|
-
export function classCheckHaveMaxCyclomaticComplexity(
|
|
8
|
-
locator: ClassLocatorData,
|
|
9
|
-
max: number,
|
|
10
|
-
isNot: boolean,
|
|
11
|
-
): RuleResult {
|
|
12
|
-
return sharedCheckHaveMaxCyclomaticComplexity(
|
|
13
|
-
locator.classes,
|
|
14
|
-
(c) => c.name?.text,
|
|
15
|
-
(c) => {
|
|
16
|
-
let complexity = 0;
|
|
17
|
-
const visit = (node: ts.Node) => {
|
|
18
|
-
if (
|
|
19
|
-
ts.isMethodDeclaration(node) ||
|
|
20
|
-
ts.isConstructorDeclaration(node) ||
|
|
21
|
-
ts.isGetAccessor(node) ||
|
|
22
|
-
ts.isSetAccessor(node)
|
|
23
|
-
) {
|
|
24
|
-
complexity += calculateCyclomaticComplexity(node);
|
|
25
|
-
}
|
|
26
|
-
ts.forEachChild(node, visit);
|
|
27
|
-
};
|
|
28
|
-
visit(c);
|
|
29
|
-
return complexity;
|
|
30
|
-
},
|
|
31
|
-
'Class',
|
|
32
|
-
max,
|
|
33
|
-
isNot,
|
|
34
|
-
);
|
|
35
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { createSourceFile, getClasses } from '../testUtils';
|
|
3
|
-
import { classCheckHaveModifier } from './classCheckHaveModifier';
|
|
4
|
-
import type { ClassLocatorData } from './types';
|
|
5
|
-
|
|
6
|
-
describe('classCheckHaveModifier', () => {
|
|
7
|
-
it('should pass if class has modifier', () => {
|
|
8
|
-
const sourceFile = createSourceFile(`export class Test {}`);
|
|
9
|
-
const data: ClassLocatorData = {
|
|
10
|
-
type: 'ClassLocator',
|
|
11
|
-
classes: getClasses(sourceFile),
|
|
12
|
-
// biome-ignore lint/suspicious/noExplicitAny: Mocking TS types for tests
|
|
13
|
-
program: {} as any,
|
|
14
|
-
};
|
|
15
|
-
const result = classCheckHaveModifier(data, 'export', false);
|
|
16
|
-
expect(result.pass).toBe(true);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('should fail if class does not have modifier', () => {
|
|
20
|
-
const sourceFile = createSourceFile(`class Test {}`);
|
|
21
|
-
const data: ClassLocatorData = {
|
|
22
|
-
type: 'ClassLocator',
|
|
23
|
-
classes: getClasses(sourceFile),
|
|
24
|
-
// biome-ignore lint/suspicious/noExplicitAny: Mocking TS types for tests
|
|
25
|
-
program: {} as any,
|
|
26
|
-
};
|
|
27
|
-
const result = classCheckHaveModifier(data, 'export', false);
|
|
28
|
-
expect(result.pass).toBe(false);
|
|
29
|
-
});
|
|
30
|
-
});
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { sharedCheckHaveModifier } from '../shared/sharedCheckHaveModifier';
|
|
2
|
-
import type { RuleResult } from '../types';
|
|
3
|
-
import type { ClassLocatorData } from './types';
|
|
4
|
-
|
|
5
|
-
export function classCheckHaveModifier(
|
|
6
|
-
locator: ClassLocatorData,
|
|
7
|
-
modifierStr: string,
|
|
8
|
-
isNot: boolean,
|
|
9
|
-
): RuleResult {
|
|
10
|
-
return sharedCheckHaveModifier(
|
|
11
|
-
locator.classes,
|
|
12
|
-
(c) => c.name?.text,
|
|
13
|
-
'Class',
|
|
14
|
-
modifierStr,
|
|
15
|
-
isNot,
|
|
16
|
-
);
|
|
17
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { createSourceFile, getClasses } from '../testUtils';
|
|
3
|
-
import { classCheckHaveNameMatchingFileName } from './classCheckHaveNameMatchingFileName';
|
|
4
|
-
import type { ClassLocatorData } from './types';
|
|
5
|
-
|
|
6
|
-
describe('classCheckHaveNameMatchingFileName', () => {
|
|
7
|
-
it('should pass if class matches filename', () => {
|
|
8
|
-
const sourceFile = createSourceFile(`class Test {}`, 'Test.ts');
|
|
9
|
-
const data: ClassLocatorData = {
|
|
10
|
-
type: 'ClassLocator',
|
|
11
|
-
classes: getClasses(sourceFile),
|
|
12
|
-
// biome-ignore lint/suspicious/noExplicitAny: Mocking TS types for tests
|
|
13
|
-
program: {} as any,
|
|
14
|
-
};
|
|
15
|
-
const result = classCheckHaveNameMatchingFileName(data, false);
|
|
16
|
-
expect(result.pass).toBe(true);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('should fail if class does not match filename', () => {
|
|
20
|
-
const sourceFile = createSourceFile(`class Test {}`, 'Other.ts');
|
|
21
|
-
const data: ClassLocatorData = {
|
|
22
|
-
type: 'ClassLocator',
|
|
23
|
-
classes: getClasses(sourceFile),
|
|
24
|
-
// biome-ignore lint/suspicious/noExplicitAny: Mocking TS types for tests
|
|
25
|
-
program: {} as any,
|
|
26
|
-
};
|
|
27
|
-
const result = classCheckHaveNameMatchingFileName(data, false);
|
|
28
|
-
expect(result.pass).toBe(false);
|
|
29
|
-
});
|
|
30
|
-
});
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { sharedCheckHaveNameMatchingFileName } from '../shared/sharedCheckHaveNameMatchingFileName';
|
|
2
|
-
import type { RuleResult } from '../types';
|
|
3
|
-
import type { ClassLocatorData } from './types';
|
|
4
|
-
|
|
5
|
-
export function classCheckHaveNameMatchingFileName(
|
|
6
|
-
locator: ClassLocatorData,
|
|
7
|
-
isNot: boolean,
|
|
8
|
-
): RuleResult {
|
|
9
|
-
return sharedCheckHaveNameMatchingFileName(
|
|
10
|
-
locator.classes,
|
|
11
|
-
(c) => c.name?.text,
|
|
12
|
-
'Class',
|
|
13
|
-
isNot,
|
|
14
|
-
);
|
|
15
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { createSourceFile, getClasses } from '../testUtils';
|
|
3
|
-
import { classCheckImplementInterface } from './classCheckImplementInterface';
|
|
4
|
-
import type { ClassLocatorData } from './types';
|
|
5
|
-
|
|
6
|
-
describe('classCheckImplementInterface', () => {
|
|
7
|
-
it('should pass if class implements interface', () => {
|
|
8
|
-
const sourceFile = createSourceFile(`class Test implements IBase {}`);
|
|
9
|
-
const data: ClassLocatorData = {
|
|
10
|
-
type: 'ClassLocator',
|
|
11
|
-
classes: getClasses(sourceFile),
|
|
12
|
-
// biome-ignore lint/suspicious/noExplicitAny: Mocking TS types for tests
|
|
13
|
-
program: {} as any,
|
|
14
|
-
};
|
|
15
|
-
const result = classCheckImplementInterface(data, 'IBase', false);
|
|
16
|
-
expect(result.pass).toBe(true);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('should fail if class does not implement interface', () => {
|
|
20
|
-
const sourceFile = createSourceFile(`class Test {}`);
|
|
21
|
-
const data: ClassLocatorData = {
|
|
22
|
-
type: 'ClassLocator',
|
|
23
|
-
classes: getClasses(sourceFile),
|
|
24
|
-
// biome-ignore lint/suspicious/noExplicitAny: Mocking TS types for tests
|
|
25
|
-
program: {} as any,
|
|
26
|
-
};
|
|
27
|
-
const result = classCheckImplementInterface(data, 'IBase', false);
|
|
28
|
-
expect(result.pass).toBe(false);
|
|
29
|
-
});
|
|
30
|
-
});
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import * as ts from 'typescript';
|
|
2
|
-
import type { RuleResult } from '../types';
|
|
3
|
-
import type { ClassLocatorData } from './types';
|
|
4
|
-
|
|
5
|
-
export function classCheckImplementInterface(
|
|
6
|
-
locator: ClassLocatorData,
|
|
7
|
-
interfaceName: string,
|
|
8
|
-
isNot: boolean,
|
|
9
|
-
): RuleResult {
|
|
10
|
-
const violations: string[] = [];
|
|
11
|
-
for (const c of locator.classes) {
|
|
12
|
-
const name = c.name?.text || 'Anonymous';
|
|
13
|
-
let matches = false;
|
|
14
|
-
if (c.heritageClauses) {
|
|
15
|
-
for (const clause of c.heritageClauses) {
|
|
16
|
-
if (clause.token === ts.SyntaxKind.ImplementsKeyword) {
|
|
17
|
-
for (const type of clause.types) {
|
|
18
|
-
if (
|
|
19
|
-
ts.isIdentifier(type.expression) &&
|
|
20
|
-
type.expression.text === interfaceName
|
|
21
|
-
) {
|
|
22
|
-
matches = true;
|
|
23
|
-
break;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
if (isNot && matches) {
|
|
30
|
-
violations.push(
|
|
31
|
-
`Class ${name} implements ${interfaceName}, but it shouldn't.`,
|
|
32
|
-
);
|
|
33
|
-
} else if (!isNot && !matches) {
|
|
34
|
-
violations.push(
|
|
35
|
-
`Class ${name} does not implement ${interfaceName}, but it should.`,
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return {
|
|
40
|
-
pass: violations.length === 0,
|
|
41
|
-
message: () => violations.join('\n'),
|
|
42
|
-
};
|
|
43
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { classCheckMatchNamePattern } from './classCheckMatchNamePattern';
|
|
3
|
-
|
|
4
|
-
describe('classCheckMatchNamePattern', () => {
|
|
5
|
-
it('should exist and be callable', () => {
|
|
6
|
-
expect(typeof classCheckMatchNamePattern).toBe('function');
|
|
7
|
-
});
|
|
8
|
-
});
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { sharedCheckMatchNamePattern } from '../shared/sharedCheckMatchNamePattern';
|
|
2
|
-
import type { RuleResult } from '../types';
|
|
3
|
-
import type { ClassLocatorData } from './types';
|
|
4
|
-
|
|
5
|
-
export function classCheckMatchNamePattern(
|
|
6
|
-
locator: ClassLocatorData,
|
|
7
|
-
pattern: string | RegExp,
|
|
8
|
-
isNot: boolean,
|
|
9
|
-
): RuleResult {
|
|
10
|
-
return sharedCheckMatchNamePattern(
|
|
11
|
-
locator.classes,
|
|
12
|
-
(c) => c.name?.text,
|
|
13
|
-
'Class',
|
|
14
|
-
pattern,
|
|
15
|
-
isNot,
|
|
16
|
-
);
|
|
17
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { classCheckResideInFolder } from './classCheckResideInFolder';
|
|
3
|
-
|
|
4
|
-
describe('classCheckResideInFolder', () => {
|
|
5
|
-
it('should exist and be callable', () => {
|
|
6
|
-
expect(typeof classCheckResideInFolder).toBe('function');
|
|
7
|
-
});
|
|
8
|
-
});
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type { RuleResult } from '../types';
|
|
2
|
-
import type { ClassLocatorData } from './types';
|
|
3
|
-
|
|
4
|
-
export function classCheckResideInFolder(
|
|
5
|
-
locator: ClassLocatorData,
|
|
6
|
-
targetFolder: string,
|
|
7
|
-
isNot: boolean,
|
|
8
|
-
): RuleResult {
|
|
9
|
-
const violations: string[] = [];
|
|
10
|
-
for (const c of locator.classes) {
|
|
11
|
-
const className = c.name?.text || 'Anonymous Class';
|
|
12
|
-
const sourceFile = c.getSourceFile();
|
|
13
|
-
const inTargetFolder =
|
|
14
|
-
sourceFile.fileName.includes(`/${targetFolder}/`) ||
|
|
15
|
-
sourceFile.fileName.includes(`\\${targetFolder}\\`);
|
|
16
|
-
|
|
17
|
-
if (isNot && inTargetFolder) {
|
|
18
|
-
violations.push(
|
|
19
|
-
`Class ${className} resides in ${targetFolder}, but it shouldn't.`,
|
|
20
|
-
);
|
|
21
|
-
} else if (!isNot && !inTargetFolder) {
|
|
22
|
-
violations.push(
|
|
23
|
-
`Class ${className} does not reside in ${targetFolder}, but it should.`,
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return {
|
|
28
|
-
pass: violations.length === 0,
|
|
29
|
-
message: () => violations.join('\n'),
|
|
30
|
-
};
|
|
31
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { createSourceFile, getClasses } from '../testUtils';
|
|
3
|
-
import { locateClasses } from './locateClasses';
|
|
4
|
-
|
|
5
|
-
describe('locateClasses', () => {
|
|
6
|
-
it('should filter classes by inFolder', () => {
|
|
7
|
-
const file1 = createSourceFile('class A {}', 'src/domain/A.ts');
|
|
8
|
-
const classes = getClasses(file1);
|
|
9
|
-
// biome-ignore lint/suspicious/noExplicitAny: Mocking TS types for tests
|
|
10
|
-
const result = locateClasses(classes, {} as any, { inFolder: 'infra' });
|
|
11
|
-
expect(result.classes.length).toBe(0);
|
|
12
|
-
});
|
|
13
|
-
});
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import * as ts from 'typescript';
|
|
2
|
-
import type { ClassLocatorData, ClassQueryOptions } from './types';
|
|
3
|
-
|
|
4
|
-
export function locateClasses(
|
|
5
|
-
classes: ts.ClassDeclaration[],
|
|
6
|
-
program: ts.Program,
|
|
7
|
-
options?: ClassQueryOptions,
|
|
8
|
-
): ClassLocatorData {
|
|
9
|
-
let filtered = classes;
|
|
10
|
-
|
|
11
|
-
if (options?.inFolder) {
|
|
12
|
-
filtered = filtered.filter((c) => {
|
|
13
|
-
const sourceFile = c.getSourceFile
|
|
14
|
-
? c.getSourceFile()
|
|
15
|
-
: c.parent
|
|
16
|
-
? ts.getSourceFileOfNode(c)
|
|
17
|
-
: undefined;
|
|
18
|
-
if (!sourceFile) return false;
|
|
19
|
-
return (
|
|
20
|
-
sourceFile.fileName.includes(`/${options.inFolder}/`) ||
|
|
21
|
-
sourceFile.fileName.includes(`\\${options.inFolder}\\`)
|
|
22
|
-
);
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
if (options?.matchNamePattern) {
|
|
26
|
-
const regex =
|
|
27
|
-
typeof options.matchNamePattern === 'string'
|
|
28
|
-
? new RegExp(options.matchNamePattern)
|
|
29
|
-
: options.matchNamePattern;
|
|
30
|
-
filtered = filtered.filter((c) => {
|
|
31
|
-
const name = c.name?.text;
|
|
32
|
-
return name && regex.test(name);
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
if (options?.withDecorator) {
|
|
36
|
-
filtered = filtered.filter((c) => {
|
|
37
|
-
const decorators = ts.canHaveDecorators(c)
|
|
38
|
-
? ts.getDecorators(c)
|
|
39
|
-
: undefined;
|
|
40
|
-
let hasDecorator = false;
|
|
41
|
-
if (decorators) {
|
|
42
|
-
for (const dec of decorators) {
|
|
43
|
-
const exp = dec.expression;
|
|
44
|
-
let name = '';
|
|
45
|
-
if (ts.isIdentifier(exp)) {
|
|
46
|
-
name = exp.text;
|
|
47
|
-
} else if (
|
|
48
|
-
ts.isCallExpression(exp) &&
|
|
49
|
-
ts.isIdentifier(exp.expression)
|
|
50
|
-
) {
|
|
51
|
-
name = exp.expression.text;
|
|
52
|
-
}
|
|
53
|
-
if (name === options.withDecorator) {
|
|
54
|
-
hasDecorator = true;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return hasDecorator;
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
if (options?.extending) {
|
|
62
|
-
filtered = filtered.filter((c) => {
|
|
63
|
-
if (c.heritageClauses) {
|
|
64
|
-
for (const clause of c.heritageClauses) {
|
|
65
|
-
if (clause.token === ts.SyntaxKind.ExtendsKeyword) {
|
|
66
|
-
for (const type of clause.types) {
|
|
67
|
-
if (
|
|
68
|
-
ts.isIdentifier(type.expression) &&
|
|
69
|
-
type.expression.text === options.extending
|
|
70
|
-
) {
|
|
71
|
-
return true;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return false;
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
if (options?.implementing) {
|
|
81
|
-
filtered = filtered.filter((c) => {
|
|
82
|
-
if (c.heritageClauses) {
|
|
83
|
-
for (const clause of c.heritageClauses) {
|
|
84
|
-
if (clause.token === ts.SyntaxKind.ImplementsKeyword) {
|
|
85
|
-
for (const type of clause.types) {
|
|
86
|
-
if (
|
|
87
|
-
ts.isIdentifier(type.expression) &&
|
|
88
|
-
type.expression.text === options.implementing
|
|
89
|
-
) {
|
|
90
|
-
return true;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return false;
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
if (options?.havingModifier) {
|
|
100
|
-
let targetKind: ts.SyntaxKind;
|
|
101
|
-
switch (options.havingModifier) {
|
|
102
|
-
case 'export':
|
|
103
|
-
targetKind = ts.SyntaxKind.ExportKeyword;
|
|
104
|
-
break;
|
|
105
|
-
case 'default':
|
|
106
|
-
targetKind = ts.SyntaxKind.DefaultKeyword;
|
|
107
|
-
break;
|
|
108
|
-
case 'abstract':
|
|
109
|
-
targetKind = ts.SyntaxKind.AbstractKeyword;
|
|
110
|
-
break;
|
|
111
|
-
default:
|
|
112
|
-
throw new Error(
|
|
113
|
-
`Modifier ${options.havingModifier} is not fully supported.`,
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
filtered = filtered.filter((c) => {
|
|
117
|
-
const modifiers = ts.canHaveModifiers(c) ? ts.getModifiers(c) : undefined;
|
|
118
|
-
return modifiers?.some((m: ts.Modifier) => m.kind === targetKind);
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return {
|
|
123
|
-
type: 'ClassLocator',
|
|
124
|
-
classes: filtered,
|
|
125
|
-
program,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type * as ts from 'typescript';
|
|
2
|
-
|
|
3
|
-
export interface ClassLocatorData {
|
|
4
|
-
type: 'ClassLocator';
|
|
5
|
-
classes: ts.ClassDeclaration[];
|
|
6
|
-
program: ts.Program;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface ClassQueryOptions {
|
|
10
|
-
inFolder?: string;
|
|
11
|
-
matchNamePattern?: string | RegExp;
|
|
12
|
-
withDecorator?: string;
|
|
13
|
-
extending?: string;
|
|
14
|
-
implementing?: string;
|
|
15
|
-
havingModifier?: string;
|
|
16
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import type { RuleResult } from '../types';
|
|
2
|
-
import { getFileDependencies } from './getFileDependencies';
|
|
3
|
-
import type { FileLocatorData } from './types';
|
|
4
|
-
|
|
5
|
-
export function checkDependOnFilesInFolder(
|
|
6
|
-
locator: FileLocatorData,
|
|
7
|
-
targetFolder: string,
|
|
8
|
-
isNot: boolean,
|
|
9
|
-
): RuleResult {
|
|
10
|
-
const violations: string[] = [];
|
|
11
|
-
|
|
12
|
-
for (const file of locator.files) {
|
|
13
|
-
const dependencies = getFileDependencies(file, locator.program);
|
|
14
|
-
|
|
15
|
-
const dependsOnTarget = dependencies.some(
|
|
16
|
-
(depPath) =>
|
|
17
|
-
depPath.includes(`/${targetFolder}/`) ||
|
|
18
|
-
depPath.includes(`\\${targetFolder}\\`),
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
if (isNot && dependsOnTarget) {
|
|
22
|
-
violations.push(
|
|
23
|
-
`File ${file.fileName} depends on files in ${targetFolder}, but it shouldn't.`,
|
|
24
|
-
);
|
|
25
|
-
} else if (!isNot && !dependsOnTarget) {
|
|
26
|
-
violations.push(
|
|
27
|
-
`File ${file.fileName} does not depend on files in ${targetFolder}, but it should.`,
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
pass: violations.length === 0,
|
|
34
|
-
message: () => violations.join('\n'),
|
|
35
|
-
};
|
|
36
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import type { RuleResult } from '../types';
|
|
2
|
-
import { getFileDependencies } from './getFileDependencies';
|
|
3
|
-
import type { FileLocatorData } from './types';
|
|
4
|
-
|
|
5
|
-
export function fileCheckBeFreeOfCycles(
|
|
6
|
-
locator: FileLocatorData,
|
|
7
|
-
isNot: boolean,
|
|
8
|
-
): RuleResult {
|
|
9
|
-
const violations: string[] = [];
|
|
10
|
-
const targetedFilePaths = new Set<string>(
|
|
11
|
-
locator.files.map((f) => f.fileName),
|
|
12
|
-
);
|
|
13
|
-
const graph = new Map<string, string[]>();
|
|
14
|
-
|
|
15
|
-
for (const file of locator.files) {
|
|
16
|
-
const filePath = file.fileName;
|
|
17
|
-
const dependencies = getFileDependencies(file, locator.program).filter(
|
|
18
|
-
(p) => targetedFilePaths.has(p),
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
graph.set(filePath, dependencies);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const visited = new Set<string>();
|
|
25
|
-
const recStack = new Set<string>();
|
|
26
|
-
const cycles: string[][] = [];
|
|
27
|
-
|
|
28
|
-
const isCyclicUtil = (node: string, path: string[]) => {
|
|
29
|
-
if (recStack.has(node)) {
|
|
30
|
-
const cycleStartIndex = path.indexOf(node);
|
|
31
|
-
const cycleStr = [...path.slice(cycleStartIndex), node].join('->');
|
|
32
|
-
if (!cycles.some((c) => c.join('->') === cycleStr)) {
|
|
33
|
-
cycles.push([...path.slice(cycleStartIndex), node]);
|
|
34
|
-
}
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (visited.has(node)) return false;
|
|
39
|
-
|
|
40
|
-
visited.add(node);
|
|
41
|
-
recStack.add(node);
|
|
42
|
-
path.push(node);
|
|
43
|
-
|
|
44
|
-
const neighbors = graph.get(node) || [];
|
|
45
|
-
for (const neighbor of neighbors) {
|
|
46
|
-
isCyclicUtil(neighbor, path);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
recStack.delete(node);
|
|
50
|
-
path.pop();
|
|
51
|
-
return false;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
for (const node of graph.keys()) {
|
|
55
|
-
if (!visited.has(node)) {
|
|
56
|
-
isCyclicUtil(node, []);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (isNot && cycles.length === 0) {
|
|
61
|
-
violations.push('Expected cyclic dependencies, but found none.');
|
|
62
|
-
} else if (!isNot && cycles.length > 0) {
|
|
63
|
-
for (const cycle of cycles) {
|
|
64
|
-
violations.push(`Cycle detected: ${cycle.join(' -> ')}`);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
pass: violations.length === 0,
|
|
70
|
-
message: () => violations.join('\n'),
|
|
71
|
-
};
|
|
72
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { fileCheckHaveMaxCyclomaticComplexity } from './fileCheckHaveMaxCyclomaticComplexity';
|
|
3
|
-
|
|
4
|
-
describe('fileCheckHaveMaxCyclomaticComplexity', () => {
|
|
5
|
-
it('should exist and be callable', () => {
|
|
6
|
-
expect(typeof fileCheckHaveMaxCyclomaticComplexity).toBe('function');
|
|
7
|
-
});
|
|
8
|
-
});
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import * as ts from 'typescript';
|
|
2
|
-
import { calculateCyclomaticComplexity } from '../metrics/calculateCyclomaticComplexity';
|
|
3
|
-
import { sharedCheckHaveMaxCyclomaticComplexity } from '../shared/sharedCheckHaveMaxCyclomaticComplexity';
|
|
4
|
-
import type { RuleResult } from '../types';
|
|
5
|
-
import type { FileLocatorData } from './types';
|
|
6
|
-
|
|
7
|
-
export function fileCheckHaveMaxCyclomaticComplexity(
|
|
8
|
-
locator: FileLocatorData,
|
|
9
|
-
max: number,
|
|
10
|
-
isNot: boolean,
|
|
11
|
-
): RuleResult {
|
|
12
|
-
return sharedCheckHaveMaxCyclomaticComplexity(
|
|
13
|
-
locator.files,
|
|
14
|
-
(f) => f.fileName,
|
|
15
|
-
(file) => {
|
|
16
|
-
let complexity = 0;
|
|
17
|
-
const visit = (node: ts.Node) => {
|
|
18
|
-
if (
|
|
19
|
-
ts.isFunctionDeclaration(node) ||
|
|
20
|
-
ts.isMethodDeclaration(node) ||
|
|
21
|
-
ts.isArrowFunction(node) ||
|
|
22
|
-
ts.isFunctionExpression(node)
|
|
23
|
-
) {
|
|
24
|
-
complexity += calculateCyclomaticComplexity(node);
|
|
25
|
-
}
|
|
26
|
-
ts.forEachChild(node, visit);
|
|
27
|
-
};
|
|
28
|
-
visit(file);
|
|
29
|
-
return complexity;
|
|
30
|
-
},
|
|
31
|
-
'File',
|
|
32
|
-
max,
|
|
33
|
-
isNot,
|
|
34
|
-
);
|
|
35
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { createSourceFile } from '../testUtils';
|
|
3
|
-
import { fileCheckHaveMaxExportedFunctions } from './fileCheckHaveMaxExportedFunctions';
|
|
4
|
-
import type { FileLocatorData } from './types';
|
|
5
|
-
|
|
6
|
-
describe('fileCheckHaveMaxExportedFunctions', () => {
|
|
7
|
-
it('should pass if under max exports', () => {
|
|
8
|
-
const sourceFile = createSourceFile(`export function a() {}`);
|
|
9
|
-
const data: FileLocatorData = {
|
|
10
|
-
type: 'FileLocator',
|
|
11
|
-
files: [sourceFile],
|
|
12
|
-
// biome-ignore lint/suspicious/noExplicitAny: Mocking TS types for tests
|
|
13
|
-
program: {} as any,
|
|
14
|
-
};
|
|
15
|
-
const result = fileCheckHaveMaxExportedFunctions(data, 1, false);
|
|
16
|
-
expect(result.pass).toBe(true);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('should fail if over max exports', () => {
|
|
20
|
-
const sourceFile = createSourceFile(
|
|
21
|
-
`export function a() {} export function b() {}`,
|
|
22
|
-
);
|
|
23
|
-
const data: FileLocatorData = {
|
|
24
|
-
type: 'FileLocator',
|
|
25
|
-
files: [sourceFile],
|
|
26
|
-
// biome-ignore lint/suspicious/noExplicitAny: Mocking TS types for tests
|
|
27
|
-
program: {} as any,
|
|
28
|
-
};
|
|
29
|
-
const result = fileCheckHaveMaxExportedFunctions(data, 1, false);
|
|
30
|
-
expect(result.pass).toBe(false);
|
|
31
|
-
});
|
|
32
|
-
});
|