@sun-asterisk/sunlint 1.3.34 → 1.3.36
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/core/architecture-integration.js +16 -7
- package/core/auto-performance-manager.js +1 -1
- package/core/cli-action-handler.js +102 -2
- package/core/cli-program.js +102 -138
- package/core/file-targeting-service.js +62 -4
- package/core/git-utils.js +19 -12
- package/core/github-annotate-service.js +326 -11
- package/core/html-report-generator.js +326 -731
- package/core/impact-integration.js +551 -0
- package/core/output-service.js +293 -21
- package/core/scoring-service.js +3 -2
- package/engines/arch-detect/core/analyzer.js +413 -0
- package/engines/arch-detect/core/index.js +22 -0
- package/engines/arch-detect/engine/hybrid-detector.js +176 -0
- package/engines/arch-detect/engine/index.js +24 -0
- package/engines/arch-detect/engine/rule-executor.js +228 -0
- package/engines/arch-detect/engine/score-calculator.js +214 -0
- package/engines/arch-detect/engine/violation-detector.js +616 -0
- package/engines/arch-detect/index.js +50 -0
- package/engines/arch-detect/rules/base-rule.js +187 -0
- package/engines/arch-detect/rules/index.js +35 -0
- package/engines/arch-detect/rules/layered/index.js +28 -0
- package/engines/arch-detect/rules/layered/l001-presentation-layer.js +237 -0
- package/engines/arch-detect/rules/layered/l002-business-layer.js +215 -0
- package/engines/arch-detect/rules/layered/l003-data-layer.js +229 -0
- package/engines/arch-detect/rules/layered/l004-model-layer.js +204 -0
- package/engines/arch-detect/rules/layered/l005-layer-separation.js +215 -0
- package/engines/arch-detect/rules/layered/l006-dependency-direction.js +221 -0
- package/engines/arch-detect/rules/layered/layered-rules-collection.js +445 -0
- package/engines/arch-detect/rules/modular/index.js +27 -0
- package/engines/arch-detect/rules/modular/m001-feature-modules.js +238 -0
- package/engines/arch-detect/rules/modular/m002-core-module.js +169 -0
- package/engines/arch-detect/rules/modular/m003-module-declaration.js +186 -0
- package/engines/arch-detect/rules/modular/m004-public-api.js +171 -0
- package/engines/arch-detect/rules/modular/m005-no-deep-imports.js +220 -0
- package/engines/arch-detect/rules/modular/modular-rules-collection.js +357 -0
- package/engines/arch-detect/rules/presentation/index.js +27 -0
- package/engines/arch-detect/rules/presentation/pr001-view-layer.js +221 -0
- package/engines/arch-detect/rules/presentation/pr002-presentation-logic.js +192 -0
- package/engines/arch-detect/rules/presentation/pr004-data-binding.js +187 -0
- package/engines/arch-detect/rules/presentation/pr006-router-layer.js +185 -0
- package/engines/arch-detect/rules/presentation/pr007-interactor-layer.js +181 -0
- package/engines/arch-detect/rules/presentation/presentation-rules-collection.js +507 -0
- package/engines/arch-detect/rules/project-scanner/index.js +31 -0
- package/engines/arch-detect/rules/project-scanner/ps001-project-root.js +213 -0
- package/engines/arch-detect/rules/project-scanner/ps002-language-detection.js +192 -0
- package/engines/arch-detect/rules/project-scanner/ps003-framework-detection.js +339 -0
- package/engines/arch-detect/rules/project-scanner/ps004-build-system.js +171 -0
- package/engines/arch-detect/rules/project-scanner/ps005-source-directory.js +163 -0
- package/engines/arch-detect/rules/project-scanner/ps006-test-directory.js +184 -0
- package/engines/arch-detect/rules/project-scanner/ps007-documentation.js +149 -0
- package/engines/arch-detect/rules/project-scanner/ps008-cicd-detection.js +163 -0
- package/engines/arch-detect/rules/project-scanner/ps009-code-quality.js +152 -0
- package/engines/arch-detect/rules/project-scanner/ps010-statistics.js +180 -0
- package/engines/arch-detect/rules/rule-registry.js +111 -0
- package/engines/arch-detect/types/context.types.js +60 -0
- package/engines/arch-detect/types/enums.js +161 -0
- package/engines/arch-detect/types/index.js +25 -0
- package/engines/arch-detect/types/result.types.js +7 -0
- package/engines/arch-detect/types/rule.types.js +7 -0
- package/engines/arch-detect/utils/file-scanner.js +411 -0
- package/engines/arch-detect/utils/index.js +23 -0
- package/engines/arch-detect/utils/pattern-matcher.js +328 -0
- package/engines/impact/cli.js +106 -0
- package/engines/impact/config/default-config.js +54 -0
- package/engines/impact/core/change-detector.js +258 -0
- package/engines/impact/core/detectors/database-detector.js +1317 -0
- package/engines/impact/core/detectors/endpoint-detector.js +55 -0
- package/engines/impact/core/impact-analyzer.js +124 -0
- package/engines/impact/core/report-generator.js +462 -0
- package/engines/impact/core/utils/ast-parser.js +241 -0
- package/engines/impact/core/utils/dependency-graph.js +159 -0
- package/engines/impact/core/utils/file-utils.js +116 -0
- package/engines/impact/core/utils/git-utils.js +203 -0
- package/engines/impact/core/utils/logger.js +13 -0
- package/engines/impact/core/utils/method-call-graph.js +1192 -0
- package/engines/impact/index.js +135 -0
- package/engines/impact/package.json +29 -0
- package/package.json +18 -43
- package/scripts/build-release.sh +0 -0
- package/scripts/copy-impact-analyzer.js +135 -0
- package/scripts/install.sh +0 -0
- package/scripts/manual-release.sh +0 -0
- package/scripts/pre-release-test.sh +0 -0
- package/scripts/prepare-release.sh +0 -0
- package/scripts/quick-performance-test.js +0 -0
- package/scripts/setup-github-registry.sh +0 -0
- package/scripts/trigger-release.sh +0 -0
- package/scripts/verify-install.sh +0 -0
- package/templates/combined-report.html +1418 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* M004: Public API Defined (Barrel Files)
|
|
4
|
+
* Xác nhận mỗi module có public API rõ ràng
|
|
5
|
+
*/
|
|
6
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
7
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
8
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
9
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
10
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
11
|
+
var _, done = false;
|
|
12
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
13
|
+
var context = {};
|
|
14
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
15
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
16
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
17
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
18
|
+
if (kind === "accessor") {
|
|
19
|
+
if (result === void 0) continue;
|
|
20
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
21
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
22
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
23
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
24
|
+
}
|
|
25
|
+
else if (_ = accept(result)) {
|
|
26
|
+
if (kind === "field") initializers.unshift(_);
|
|
27
|
+
else descriptor[key] = _;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
31
|
+
done = true;
|
|
32
|
+
};
|
|
33
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
34
|
+
var useValue = arguments.length > 2;
|
|
35
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
36
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
37
|
+
}
|
|
38
|
+
return useValue ? value : void 0;
|
|
39
|
+
};
|
|
40
|
+
var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
|
|
41
|
+
if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
|
|
42
|
+
return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.M004PublicApi = void 0;
|
|
46
|
+
const enums_1 = require("../../types/enums");
|
|
47
|
+
const file_scanner_1 = require("../../utils/file-scanner");
|
|
48
|
+
const base_rule_1 = require("../base-rule");
|
|
49
|
+
const rule_registry_1 = require("../rule-registry");
|
|
50
|
+
const rule = (0, base_rule_1.createRule)('M004', 'Public API Defined (Barrel Files)', 'Xác nhận mỗi module có public API rõ ràng (index.ts, public-api.ts, __init__.py, etc.)', enums_1.RuleCategory.STRUCTURE, 0.13, [enums_1.PatternType.MODULAR], {
|
|
51
|
+
filePatterns: [
|
|
52
|
+
'index.ts',
|
|
53
|
+
'index.js',
|
|
54
|
+
'public-api.ts',
|
|
55
|
+
'public_api.dart',
|
|
56
|
+
'__init__.py',
|
|
57
|
+
'mod.rs',
|
|
58
|
+
],
|
|
59
|
+
}, {
|
|
60
|
+
isKeyIndicator: true,
|
|
61
|
+
tags: ['modular', 'barrel', 'public-api'],
|
|
62
|
+
});
|
|
63
|
+
let M004PublicApi = (() => {
|
|
64
|
+
let _classDecorators = [(0, rule_registry_1.RegisterRule)(rule)];
|
|
65
|
+
let _classDescriptor;
|
|
66
|
+
let _classExtraInitializers = [];
|
|
67
|
+
let _classThis;
|
|
68
|
+
let _classSuper = base_rule_1.BaseRule;
|
|
69
|
+
var M004PublicApi = _classThis = class extends _classSuper {
|
|
70
|
+
constructor() {
|
|
71
|
+
super(...arguments);
|
|
72
|
+
this.rule = rule;
|
|
73
|
+
}
|
|
74
|
+
async customDetection(context) {
|
|
75
|
+
const matches = [];
|
|
76
|
+
let modulesWithBarrel = 0;
|
|
77
|
+
let totalModules = 0;
|
|
78
|
+
// Find module containers
|
|
79
|
+
const moduleContainers = ['features', 'modules', 'packages', 'libs'];
|
|
80
|
+
const moduleDirs = [];
|
|
81
|
+
for (const container of moduleContainers) {
|
|
82
|
+
const found = file_scanner_1.FileScanner.findFoldersByPattern(context.directoryTree, new RegExp(`^(src/)?${container}$`, 'i'));
|
|
83
|
+
for (const dir of found) {
|
|
84
|
+
for (const subDir of dir.subdirectories) {
|
|
85
|
+
moduleDirs.push(subDir.relativePath);
|
|
86
|
+
totalModules++;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Barrel file patterns
|
|
91
|
+
const barrelPatterns = [
|
|
92
|
+
'index.ts',
|
|
93
|
+
'index.js',
|
|
94
|
+
'index.tsx',
|
|
95
|
+
'index.jsx',
|
|
96
|
+
'public-api.ts',
|
|
97
|
+
'public_api.dart',
|
|
98
|
+
'__init__.py',
|
|
99
|
+
'mod.rs',
|
|
100
|
+
];
|
|
101
|
+
// Check each module for barrel files
|
|
102
|
+
for (const moduleDir of moduleDirs) {
|
|
103
|
+
const hasBarrel = barrelPatterns.some((barrel) => context.files.some((f) => f.relativePath === `${moduleDir}/${barrel}` ||
|
|
104
|
+
f.relativePath === `${moduleDir}/src/${barrel}`));
|
|
105
|
+
if (hasBarrel) {
|
|
106
|
+
modulesWithBarrel++;
|
|
107
|
+
matches.push({
|
|
108
|
+
type: enums_1.MatchType.FILE,
|
|
109
|
+
path: moduleDir,
|
|
110
|
+
matchedPattern: 'Module with barrel file',
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Also count standalone barrel files in likely module locations
|
|
115
|
+
const barrelFiles = context.files.filter((f) => barrelPatterns.includes(f.fileName) &&
|
|
116
|
+
!f.relativePath.includes('node_modules') &&
|
|
117
|
+
// Not at root level
|
|
118
|
+
f.relativePath.includes('/'));
|
|
119
|
+
// Add barrel files not in module directories
|
|
120
|
+
for (const file of barrelFiles) {
|
|
121
|
+
const alreadyMatched = matches.some((m) => file.relativePath.startsWith(m.path));
|
|
122
|
+
if (!alreadyMatched && matches.length < 15) {
|
|
123
|
+
matches.push({
|
|
124
|
+
type: enums_1.MatchType.FILE,
|
|
125
|
+
path: file.relativePath,
|
|
126
|
+
matchedPattern: 'Barrel file',
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Calculate score
|
|
131
|
+
let score = 0;
|
|
132
|
+
if (totalModules > 0) {
|
|
133
|
+
const ratio = modulesWithBarrel / totalModules;
|
|
134
|
+
if (ratio >= 1)
|
|
135
|
+
score = 1;
|
|
136
|
+
else if (ratio >= 0.8)
|
|
137
|
+
score = 0.8;
|
|
138
|
+
else if (ratio >= 0.5)
|
|
139
|
+
score = 0.5;
|
|
140
|
+
else
|
|
141
|
+
score = ratio * 0.4;
|
|
142
|
+
}
|
|
143
|
+
else if (barrelFiles.length > 0) {
|
|
144
|
+
// No formal module structure but has barrel files
|
|
145
|
+
score = Math.min(barrelFiles.length * 0.1, 0.6);
|
|
146
|
+
}
|
|
147
|
+
return { matches, violations: [], score };
|
|
148
|
+
}
|
|
149
|
+
getMetadata(context, matches) {
|
|
150
|
+
const withBarrel = matches.filter((m) => m.matchedPattern === 'Module with barrel file').length;
|
|
151
|
+
const barrelFiles = matches.filter((m) => m.matchedPattern === 'Barrel file').length;
|
|
152
|
+
return {
|
|
153
|
+
...super.getMetadata(context, matches),
|
|
154
|
+
modulesWithBarrel: withBarrel,
|
|
155
|
+
totalBarrelFiles: withBarrel + barrelFiles,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
__setFunctionName(_classThis, "M004PublicApi");
|
|
160
|
+
(() => {
|
|
161
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
162
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
163
|
+
M004PublicApi = _classThis = _classDescriptor.value;
|
|
164
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
165
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
166
|
+
})();
|
|
167
|
+
return M004PublicApi = _classThis;
|
|
168
|
+
})();
|
|
169
|
+
exports.M004PublicApi = M004PublicApi;
|
|
170
|
+
exports.default = M004PublicApi;
|
|
171
|
+
//# sourceMappingURL=m004-public-api.js.map
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* M005: No Deep Imports (Encapsulation)
|
|
4
|
+
* Xác nhận modules không import deep vào internal của module khác
|
|
5
|
+
*/
|
|
6
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
7
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
8
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
9
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
10
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
11
|
+
var _, done = false;
|
|
12
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
13
|
+
var context = {};
|
|
14
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
15
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
16
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
17
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
18
|
+
if (kind === "accessor") {
|
|
19
|
+
if (result === void 0) continue;
|
|
20
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
21
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
22
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
23
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
24
|
+
}
|
|
25
|
+
else if (_ = accept(result)) {
|
|
26
|
+
if (kind === "field") initializers.unshift(_);
|
|
27
|
+
else descriptor[key] = _;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
31
|
+
done = true;
|
|
32
|
+
};
|
|
33
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
34
|
+
var useValue = arguments.length > 2;
|
|
35
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
36
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
37
|
+
}
|
|
38
|
+
return useValue ? value : void 0;
|
|
39
|
+
};
|
|
40
|
+
var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
|
|
41
|
+
if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
|
|
42
|
+
return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.M005NoDeepImports = void 0;
|
|
46
|
+
const enums_1 = require("../../types/enums");
|
|
47
|
+
const base_rule_1 = require("../base-rule");
|
|
48
|
+
const rule_registry_1 = require("../rule-registry");
|
|
49
|
+
const rule = (0, base_rule_1.createRule)('M005', 'No Deep Imports (Encapsulation)', 'Xác nhận modules không import deep vào internal của module khác', enums_1.RuleCategory.STRUCTURE, 0.1, [enums_1.PatternType.MODULAR], {}, {
|
|
50
|
+
tags: ['modular', 'encapsulation', 'imports'],
|
|
51
|
+
});
|
|
52
|
+
let M005NoDeepImports = (() => {
|
|
53
|
+
let _classDecorators = [(0, rule_registry_1.RegisterRule)(rule)];
|
|
54
|
+
let _classDescriptor;
|
|
55
|
+
let _classExtraInitializers = [];
|
|
56
|
+
let _classThis;
|
|
57
|
+
let _classSuper = base_rule_1.BaseRule;
|
|
58
|
+
var M005NoDeepImports = _classThis = class extends _classSuper {
|
|
59
|
+
constructor() {
|
|
60
|
+
super(...arguments);
|
|
61
|
+
this.rule = rule;
|
|
62
|
+
}
|
|
63
|
+
async customDetection(context) {
|
|
64
|
+
const matches = [];
|
|
65
|
+
const violations = [];
|
|
66
|
+
let deepImportCount = 0;
|
|
67
|
+
let validImportCount = 0;
|
|
68
|
+
// Identify module boundaries
|
|
69
|
+
const modulePaths = this.identifyModulePaths(context);
|
|
70
|
+
if (modulePaths.length < 2) {
|
|
71
|
+
// Not enough modules to check cross-module imports
|
|
72
|
+
return { matches, violations, score: 0.5 };
|
|
73
|
+
}
|
|
74
|
+
// Check imports in source files
|
|
75
|
+
const sourceFiles = context.files
|
|
76
|
+
.filter((f) => !f.isTest &&
|
|
77
|
+
!f.isConfig &&
|
|
78
|
+
(f.extension === '.ts' || f.extension === '.js' || f.extension === '.tsx'))
|
|
79
|
+
.slice(0, 50);
|
|
80
|
+
const importPattern = /import\s+.*\s+from\s+['"]([^'"]+)['"]/g;
|
|
81
|
+
for (const file of sourceFiles) {
|
|
82
|
+
try {
|
|
83
|
+
const content = await context.helpers.readFile(file.absolutePath);
|
|
84
|
+
const fileModulePath = this.getModulePath(file.relativePath, modulePaths);
|
|
85
|
+
let match;
|
|
86
|
+
while ((match = importPattern.exec(content)) !== null) {
|
|
87
|
+
const importPath = match[1];
|
|
88
|
+
// Skip external packages
|
|
89
|
+
if (!importPath.startsWith('.') &&
|
|
90
|
+
!importPath.startsWith('@app') &&
|
|
91
|
+
!importPath.startsWith('@/')) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
// Check if this is a cross-module import
|
|
95
|
+
const targetModulePath = this.resolveImportToModule(importPath, file.relativePath, modulePaths);
|
|
96
|
+
if (targetModulePath && targetModulePath !== fileModulePath) {
|
|
97
|
+
// Cross-module import detected
|
|
98
|
+
const isDeepImport = this.isDeepImport(importPath, targetModulePath);
|
|
99
|
+
if (isDeepImport) {
|
|
100
|
+
deepImportCount++;
|
|
101
|
+
violations.push({
|
|
102
|
+
type: 'deep_import',
|
|
103
|
+
severity: enums_1.Severity.WARNING,
|
|
104
|
+
path: file.relativePath,
|
|
105
|
+
message: `Deep import into ${targetModulePath}: ${importPath}`,
|
|
106
|
+
suggestion: `Import from ${targetModulePath}/index instead`,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
validImportCount++;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// Skip
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Add summary match
|
|
120
|
+
matches.push({
|
|
121
|
+
type: enums_1.MatchType.CODE,
|
|
122
|
+
path: '',
|
|
123
|
+
matchedPattern: `${validImportCount} valid, ${deepImportCount} deep imports`,
|
|
124
|
+
});
|
|
125
|
+
// Calculate score
|
|
126
|
+
let score = 1;
|
|
127
|
+
if (deepImportCount === 0)
|
|
128
|
+
score = 1;
|
|
129
|
+
else if (deepImportCount <= 5)
|
|
130
|
+
score = 0.7;
|
|
131
|
+
else if (deepImportCount <= 10)
|
|
132
|
+
score = 0.4;
|
|
133
|
+
else
|
|
134
|
+
score = 0.1;
|
|
135
|
+
return { matches, violations, score };
|
|
136
|
+
}
|
|
137
|
+
identifyModulePaths(context) {
|
|
138
|
+
const modulePaths = [];
|
|
139
|
+
const moduleContainers = ['features', 'modules', 'packages', 'libs'];
|
|
140
|
+
for (const file of context.files) {
|
|
141
|
+
for (const container of moduleContainers) {
|
|
142
|
+
const regex = new RegExp(`^(src/)?${container}/([^/]+)`, 'i');
|
|
143
|
+
const match = file.relativePath.match(regex);
|
|
144
|
+
if (match) {
|
|
145
|
+
const modulePath = `${match[1] || ''}${container}/${match[2]}`;
|
|
146
|
+
if (!modulePaths.includes(modulePath)) {
|
|
147
|
+
modulePaths.push(modulePath);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return modulePaths;
|
|
153
|
+
}
|
|
154
|
+
getModulePath(filePath, modulePaths) {
|
|
155
|
+
for (const modulePath of modulePaths) {
|
|
156
|
+
if (filePath.startsWith(modulePath)) {
|
|
157
|
+
return modulePath;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
resolveImportToModule(importPath, fromFilePath, modulePaths) {
|
|
163
|
+
// Handle relative imports
|
|
164
|
+
if (importPath.startsWith('.')) {
|
|
165
|
+
const parts = fromFilePath.split('/');
|
|
166
|
+
parts.pop(); // Remove filename
|
|
167
|
+
const importParts = importPath.split('/');
|
|
168
|
+
for (const part of importParts) {
|
|
169
|
+
if (part === '..') {
|
|
170
|
+
parts.pop();
|
|
171
|
+
}
|
|
172
|
+
else if (part !== '.') {
|
|
173
|
+
parts.push(part);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const resolvedPath = parts.join('/');
|
|
177
|
+
return this.getModulePath(resolvedPath, modulePaths);
|
|
178
|
+
}
|
|
179
|
+
// Handle alias imports (@app/users, @/features/users)
|
|
180
|
+
if (importPath.startsWith('@app/') || importPath.startsWith('@/')) {
|
|
181
|
+
const cleaned = importPath.replace(/^@(app)?\//, '');
|
|
182
|
+
return this.getModulePath(cleaned, modulePaths);
|
|
183
|
+
}
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
isDeepImport(importPath, targetModulePath) {
|
|
187
|
+
// A deep import goes beyond the module's public API (index file)
|
|
188
|
+
// Example:
|
|
189
|
+
// Valid: @app/users or @app/users/index
|
|
190
|
+
// Deep: @app/users/services/user.service
|
|
191
|
+
const cleanPath = importPath.replace(/^@(app)?\//, '').replace(/\/index$/, '');
|
|
192
|
+
// Count path segments beyond module
|
|
193
|
+
const moduleSegments = targetModulePath.split('/').length;
|
|
194
|
+
const importSegments = cleanPath.split('/').length;
|
|
195
|
+
// If import has more segments than module path, it's a deep import
|
|
196
|
+
return importSegments > moduleSegments;
|
|
197
|
+
}
|
|
198
|
+
getMetadata(context, matches) {
|
|
199
|
+
const statsMatch = matches.find((m) => m.matchedPattern?.includes('valid,'));
|
|
200
|
+
const stats = statsMatch?.matchedPattern?.split(', ') || [];
|
|
201
|
+
return {
|
|
202
|
+
...super.getMetadata(context, matches),
|
|
203
|
+
validImports: parseInt(stats[0]?.split(' ')[0] || '0', 10),
|
|
204
|
+
deepImports: parseInt(stats[1]?.split(' ')[0] || '0', 10),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
__setFunctionName(_classThis, "M005NoDeepImports");
|
|
209
|
+
(() => {
|
|
210
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
211
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
212
|
+
M005NoDeepImports = _classThis = _classDescriptor.value;
|
|
213
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
214
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
215
|
+
})();
|
|
216
|
+
return M005NoDeepImports = _classThis;
|
|
217
|
+
})();
|
|
218
|
+
exports.M005NoDeepImports = M005NoDeepImports;
|
|
219
|
+
exports.default = M005NoDeepImports;
|
|
220
|
+
//# sourceMappingURL=m005-no-deep-imports.js.map
|