@sun-asterisk/sunlint 1.3.34 → 1.3.35
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 +92 -2
- package/core/cli-program.js +96 -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 +433 -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,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* L002: Business Logic Layer Exists
|
|
4
|
+
* Xác nhận có tầng xử lý business logic tách biệt
|
|
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.L002BusinessLayer = 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)('L002', 'Business Logic Layer Exists', 'Xác nhận có tầng xử lý business logic tách biệt (Service, UseCase, Manager)', enums_1.RuleCategory.CORE, 0.16, [enums_1.PatternType.LAYERED], {
|
|
50
|
+
folderPatterns: [
|
|
51
|
+
'services',
|
|
52
|
+
'service',
|
|
53
|
+
'business',
|
|
54
|
+
'domain',
|
|
55
|
+
'usecases',
|
|
56
|
+
'use-cases',
|
|
57
|
+
'application',
|
|
58
|
+
'managers',
|
|
59
|
+
'core',
|
|
60
|
+
],
|
|
61
|
+
filePatterns: ['*Service.*', '*UseCase.*', '*Manager.*', '*Business.*', '*Interactor.*'],
|
|
62
|
+
codePatterns: [
|
|
63
|
+
// Java
|
|
64
|
+
{ language: enums_1.Language.JAVA, type: enums_1.CodePatternType.ANNOTATION, pattern: /@Service\b/ },
|
|
65
|
+
{ language: enums_1.Language.JAVA, type: enums_1.CodePatternType.ANNOTATION, pattern: /@Transactional\b/ },
|
|
66
|
+
// TypeScript/JavaScript
|
|
67
|
+
{
|
|
68
|
+
language: enums_1.Language.TYPESCRIPT,
|
|
69
|
+
type: enums_1.CodePatternType.ANNOTATION,
|
|
70
|
+
pattern: /@Injectable\s*\(/,
|
|
71
|
+
},
|
|
72
|
+
{ language: enums_1.Language.TYPESCRIPT, type: enums_1.CodePatternType.PATTERN, pattern: /\.service\.ts$/ },
|
|
73
|
+
// C#
|
|
74
|
+
{ language: enums_1.Language.CSHARP, type: enums_1.CodePatternType.PATTERN, pattern: /class\s+\w+Service\b/ },
|
|
75
|
+
{
|
|
76
|
+
language: enums_1.Language.CSHARP,
|
|
77
|
+
type: enums_1.CodePatternType.PATTERN,
|
|
78
|
+
pattern: /interface\s+I\w+Service\b/,
|
|
79
|
+
},
|
|
80
|
+
// PHP
|
|
81
|
+
{ language: enums_1.Language.PHP, type: enums_1.CodePatternType.PATTERN, pattern: /class\s+\w+Service\b/ },
|
|
82
|
+
// Python
|
|
83
|
+
{ language: enums_1.Language.PYTHON, type: enums_1.CodePatternType.PATTERN, pattern: /class\s+\w+Service\b/ },
|
|
84
|
+
// Go
|
|
85
|
+
{
|
|
86
|
+
language: enums_1.Language.GO,
|
|
87
|
+
type: enums_1.CodePatternType.PATTERN,
|
|
88
|
+
pattern: /type\s+\w+Service\s+struct/,
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
}, {
|
|
92
|
+
isKeyIndicator: true,
|
|
93
|
+
tags: ['layered', 'business', 'service'],
|
|
94
|
+
});
|
|
95
|
+
let L002BusinessLayer = (() => {
|
|
96
|
+
let _classDecorators = [(0, rule_registry_1.RegisterRule)(rule)];
|
|
97
|
+
let _classDescriptor;
|
|
98
|
+
let _classExtraInitializers = [];
|
|
99
|
+
let _classThis;
|
|
100
|
+
let _classSuper = base_rule_1.BaseRule;
|
|
101
|
+
var L002BusinessLayer = _classThis = class extends _classSuper {
|
|
102
|
+
constructor() {
|
|
103
|
+
super(...arguments);
|
|
104
|
+
this.rule = rule;
|
|
105
|
+
}
|
|
106
|
+
async customDetection(context) {
|
|
107
|
+
const matches = [];
|
|
108
|
+
let hasFolders = false;
|
|
109
|
+
let hasFiles = false;
|
|
110
|
+
let hasCode = false;
|
|
111
|
+
// Check for service folders
|
|
112
|
+
const serviceFolders = [
|
|
113
|
+
'services',
|
|
114
|
+
'service',
|
|
115
|
+
'business',
|
|
116
|
+
'domain',
|
|
117
|
+
'usecases',
|
|
118
|
+
'use-cases',
|
|
119
|
+
'application',
|
|
120
|
+
'managers',
|
|
121
|
+
];
|
|
122
|
+
for (const folder of serviceFolders) {
|
|
123
|
+
const found = context.files.some((f) => f.relativePath.toLowerCase().includes(`/${folder}/`) ||
|
|
124
|
+
f.relativePath.toLowerCase().startsWith(`${folder}/`));
|
|
125
|
+
if (found) {
|
|
126
|
+
matches.push({
|
|
127
|
+
type: enums_1.MatchType.FOLDER,
|
|
128
|
+
path: folder,
|
|
129
|
+
matchedPattern: 'Service folder',
|
|
130
|
+
});
|
|
131
|
+
hasFolders = true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Check for service files
|
|
135
|
+
const serviceFiles = context.files.filter((f) => /Service\.(ts|js|java|cs|php|py|rb|go)$/i.test(f.fileName) ||
|
|
136
|
+
/UseCase\.(ts|js|java|cs|php|py|rb|go)$/i.test(f.fileName) ||
|
|
137
|
+
/Manager\.(ts|js|java|cs|php|py|rb|go)$/i.test(f.fileName) ||
|
|
138
|
+
/\.service\.(ts|js)$/i.test(f.fileName));
|
|
139
|
+
if (serviceFiles.length > 0) {
|
|
140
|
+
for (const file of serviceFiles.slice(0, 5)) {
|
|
141
|
+
matches.push({
|
|
142
|
+
type: enums_1.MatchType.FILE,
|
|
143
|
+
path: file.relativePath,
|
|
144
|
+
matchedPattern: 'Service file',
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
hasFiles = true;
|
|
148
|
+
}
|
|
149
|
+
// Check for code patterns
|
|
150
|
+
const sourceFiles = context.files
|
|
151
|
+
.filter((f) => !f.isTest && !f.isConfig && f.language)
|
|
152
|
+
.slice(0, 50);
|
|
153
|
+
const codePatterns = [
|
|
154
|
+
/@Service\b/,
|
|
155
|
+
/@Injectable\s*\(/,
|
|
156
|
+
/@Transactional\b/,
|
|
157
|
+
/class\s+\w+Service\b/,
|
|
158
|
+
/interface\s+I\w+Service\b/,
|
|
159
|
+
/type\s+\w+Service\s+struct/,
|
|
160
|
+
];
|
|
161
|
+
for (const file of sourceFiles) {
|
|
162
|
+
try {
|
|
163
|
+
const content = await context.helpers.readFile(file.absolutePath);
|
|
164
|
+
for (const pattern of codePatterns) {
|
|
165
|
+
if (pattern.test(content)) {
|
|
166
|
+
hasCode = true;
|
|
167
|
+
matches.push({
|
|
168
|
+
type: enums_1.MatchType.CODE,
|
|
169
|
+
path: file.relativePath,
|
|
170
|
+
matchedPattern: 'Service pattern',
|
|
171
|
+
});
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (hasCode && matches.length >= 10)
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// Skip unreadable files
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// Calculate score
|
|
183
|
+
let score = 0;
|
|
184
|
+
if (hasFolders && hasFiles)
|
|
185
|
+
score = 1;
|
|
186
|
+
else if (hasFiles)
|
|
187
|
+
score = 0.7;
|
|
188
|
+
else if (hasCode)
|
|
189
|
+
score = 0.6;
|
|
190
|
+
else if (hasFolders)
|
|
191
|
+
score = 0.4;
|
|
192
|
+
return { matches, violations: [], score };
|
|
193
|
+
}
|
|
194
|
+
getMetadata(context, matches) {
|
|
195
|
+
const serviceCount = matches.filter((m) => m.matchedPattern === 'Service file').length;
|
|
196
|
+
return {
|
|
197
|
+
...super.getMetadata(context, matches),
|
|
198
|
+
serviceCount,
|
|
199
|
+
hasDedicatedFolder: matches.some((m) => m.matchedPattern === 'Service folder'),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
__setFunctionName(_classThis, "L002BusinessLayer");
|
|
204
|
+
(() => {
|
|
205
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
206
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
207
|
+
L002BusinessLayer = _classThis = _classDescriptor.value;
|
|
208
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
209
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
210
|
+
})();
|
|
211
|
+
return L002BusinessLayer = _classThis;
|
|
212
|
+
})();
|
|
213
|
+
exports.L002BusinessLayer = L002BusinessLayer;
|
|
214
|
+
exports.default = L002BusinessLayer;
|
|
215
|
+
//# sourceMappingURL=l002-business-layer.js.map
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* L003: Data Access Layer Exists
|
|
4
|
+
* Xác nhận có tầng truy cập dữ liệu tách biệt (Repository, DAO)
|
|
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.L003DataLayer = 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)('L003', 'Data Access Layer Exists', 'Xác nhận có tầng truy cập dữ liệu tách biệt (Repository, DAO, Gateway)', enums_1.RuleCategory.CORE, 0.14, [enums_1.PatternType.LAYERED], {
|
|
50
|
+
folderPatterns: [
|
|
51
|
+
'repositories',
|
|
52
|
+
'repository',
|
|
53
|
+
'dao',
|
|
54
|
+
'daos',
|
|
55
|
+
'data',
|
|
56
|
+
'persistence',
|
|
57
|
+
'infrastructure',
|
|
58
|
+
'gateways',
|
|
59
|
+
'store',
|
|
60
|
+
],
|
|
61
|
+
filePatterns: ['*Repository.*', '*Dao.*', '*Gateway.*', '*DataSource.*', '*Store.*'],
|
|
62
|
+
codePatterns: [
|
|
63
|
+
// Java
|
|
64
|
+
{ language: enums_1.Language.JAVA, type: enums_1.CodePatternType.ANNOTATION, pattern: /@Repository\b/ },
|
|
65
|
+
{
|
|
66
|
+
language: enums_1.Language.JAVA,
|
|
67
|
+
type: enums_1.CodePatternType.CLASS,
|
|
68
|
+
pattern: /extends\s+(Jpa|Crud|Mongo)Repository\b/,
|
|
69
|
+
},
|
|
70
|
+
{ language: enums_1.Language.JAVA, type: enums_1.CodePatternType.ANNOTATION, pattern: /@Query\b/ },
|
|
71
|
+
// TypeScript/JavaScript
|
|
72
|
+
{
|
|
73
|
+
language: enums_1.Language.TYPESCRIPT,
|
|
74
|
+
type: enums_1.CodePatternType.ANNOTATION,
|
|
75
|
+
pattern: /@EntityRepository\s*\(/,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
language: enums_1.Language.TYPESCRIPT,
|
|
79
|
+
type: enums_1.CodePatternType.PATTERN,
|
|
80
|
+
pattern: /\.repository\.ts$/,
|
|
81
|
+
},
|
|
82
|
+
// C#
|
|
83
|
+
{ language: enums_1.Language.CSHARP, type: enums_1.CodePatternType.PATTERN, pattern: /DbContext\b/ },
|
|
84
|
+
{ language: enums_1.Language.CSHARP, type: enums_1.CodePatternType.PATTERN, pattern: /DbSet\s*</ },
|
|
85
|
+
{
|
|
86
|
+
language: enums_1.Language.CSHARP,
|
|
87
|
+
type: enums_1.CodePatternType.PATTERN,
|
|
88
|
+
pattern: /interface\s+I\w+Repository\b/,
|
|
89
|
+
},
|
|
90
|
+
// PHP
|
|
91
|
+
{ language: enums_1.Language.PHP, type: enums_1.CodePatternType.PATTERN, pattern: /DB::table\s*\(/ },
|
|
92
|
+
// Python
|
|
93
|
+
{
|
|
94
|
+
language: enums_1.Language.PYTHON,
|
|
95
|
+
type: enums_1.CodePatternType.PATTERN,
|
|
96
|
+
pattern: /class\s+\w+Repository\b/,
|
|
97
|
+
},
|
|
98
|
+
// Go
|
|
99
|
+
{
|
|
100
|
+
language: enums_1.Language.GO,
|
|
101
|
+
type: enums_1.CodePatternType.PATTERN,
|
|
102
|
+
pattern: /type\s+\w+Repository\s+struct/,
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
}, {
|
|
106
|
+
isKeyIndicator: true,
|
|
107
|
+
tags: ['layered', 'data', 'repository'],
|
|
108
|
+
});
|
|
109
|
+
let L003DataLayer = (() => {
|
|
110
|
+
let _classDecorators = [(0, rule_registry_1.RegisterRule)(rule)];
|
|
111
|
+
let _classDescriptor;
|
|
112
|
+
let _classExtraInitializers = [];
|
|
113
|
+
let _classThis;
|
|
114
|
+
let _classSuper = base_rule_1.BaseRule;
|
|
115
|
+
var L003DataLayer = _classThis = class extends _classSuper {
|
|
116
|
+
constructor() {
|
|
117
|
+
super(...arguments);
|
|
118
|
+
this.rule = rule;
|
|
119
|
+
}
|
|
120
|
+
async customDetection(context) {
|
|
121
|
+
const matches = [];
|
|
122
|
+
let hasFolders = false;
|
|
123
|
+
let hasFiles = false;
|
|
124
|
+
let hasCode = false;
|
|
125
|
+
// Check for repository folders
|
|
126
|
+
const repoFolders = [
|
|
127
|
+
'repositories',
|
|
128
|
+
'repository',
|
|
129
|
+
'dao',
|
|
130
|
+
'daos',
|
|
131
|
+
'data',
|
|
132
|
+
'persistence',
|
|
133
|
+
'infrastructure',
|
|
134
|
+
'gateways',
|
|
135
|
+
];
|
|
136
|
+
for (const folder of repoFolders) {
|
|
137
|
+
const found = context.files.some((f) => f.relativePath.toLowerCase().includes(`/${folder}/`) ||
|
|
138
|
+
f.relativePath.toLowerCase().startsWith(`${folder}/`));
|
|
139
|
+
if (found) {
|
|
140
|
+
matches.push({
|
|
141
|
+
type: enums_1.MatchType.FOLDER,
|
|
142
|
+
path: folder,
|
|
143
|
+
matchedPattern: 'Repository folder',
|
|
144
|
+
});
|
|
145
|
+
hasFolders = true;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Check for repository files
|
|
149
|
+
const repoFiles = context.files.filter((f) => /Repository\.(ts|js|java|cs|php|py|rb|go)$/i.test(f.fileName) ||
|
|
150
|
+
/Dao\.(ts|js|java|cs|php|py|rb|go)$/i.test(f.fileName) ||
|
|
151
|
+
/Gateway\.(ts|js|java|cs|php|py|rb|go)$/i.test(f.fileName) ||
|
|
152
|
+
/\.repository\.(ts|js)$/i.test(f.fileName));
|
|
153
|
+
if (repoFiles.length > 0) {
|
|
154
|
+
for (const file of repoFiles.slice(0, 5)) {
|
|
155
|
+
matches.push({
|
|
156
|
+
type: enums_1.MatchType.FILE,
|
|
157
|
+
path: file.relativePath,
|
|
158
|
+
matchedPattern: 'Repository file',
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
hasFiles = true;
|
|
162
|
+
}
|
|
163
|
+
// Check for code patterns
|
|
164
|
+
const sourceFiles = context.files
|
|
165
|
+
.filter((f) => !f.isTest && !f.isConfig && f.language)
|
|
166
|
+
.slice(0, 50);
|
|
167
|
+
const codePatterns = [
|
|
168
|
+
/@Repository\b/,
|
|
169
|
+
/extends\s+(Jpa|Crud|Mongo)Repository\b/,
|
|
170
|
+
/@EntityRepository\s*\(/,
|
|
171
|
+
/DbContext\b/,
|
|
172
|
+
/DbSet\s*</,
|
|
173
|
+
/type\s+\w+Repository\s+struct/,
|
|
174
|
+
];
|
|
175
|
+
for (const file of sourceFiles) {
|
|
176
|
+
try {
|
|
177
|
+
const content = await context.helpers.readFile(file.absolutePath);
|
|
178
|
+
for (const pattern of codePatterns) {
|
|
179
|
+
if (pattern.test(content)) {
|
|
180
|
+
hasCode = true;
|
|
181
|
+
matches.push({
|
|
182
|
+
type: enums_1.MatchType.CODE,
|
|
183
|
+
path: file.relativePath,
|
|
184
|
+
matchedPattern: 'Repository pattern',
|
|
185
|
+
});
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (hasCode && matches.length >= 10)
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// Skip unreadable files
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// Calculate score
|
|
197
|
+
let score = 0;
|
|
198
|
+
if (hasFolders && hasFiles)
|
|
199
|
+
score = 1;
|
|
200
|
+
else if (hasFiles)
|
|
201
|
+
score = 0.7;
|
|
202
|
+
else if (hasCode)
|
|
203
|
+
score = 0.6;
|
|
204
|
+
else if (hasFolders)
|
|
205
|
+
score = 0.4;
|
|
206
|
+
return { matches, violations: [], score };
|
|
207
|
+
}
|
|
208
|
+
getMetadata(context, matches) {
|
|
209
|
+
const repoCount = matches.filter((m) => m.matchedPattern === 'Repository file').length;
|
|
210
|
+
return {
|
|
211
|
+
...super.getMetadata(context, matches),
|
|
212
|
+
repositoryCount: repoCount,
|
|
213
|
+
hasDedicatedFolder: matches.some((m) => m.matchedPattern === 'Repository folder'),
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
__setFunctionName(_classThis, "L003DataLayer");
|
|
218
|
+
(() => {
|
|
219
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
220
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
221
|
+
L003DataLayer = _classThis = _classDescriptor.value;
|
|
222
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
223
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
224
|
+
})();
|
|
225
|
+
return L003DataLayer = _classThis;
|
|
226
|
+
})();
|
|
227
|
+
exports.L003DataLayer = L003DataLayer;
|
|
228
|
+
exports.default = L003DataLayer;
|
|
229
|
+
//# sourceMappingURL=l003-data-layer.js.map
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* L004: Model/Entity Layer Exists
|
|
4
|
+
* Xác nhận có data models/entities định nghĩa 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.L004ModelLayer = 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)('L004', 'Model/Entity Layer Exists', 'Xác nhận có data models/entities định nghĩa rõ ràng', enums_1.RuleCategory.CORE, 0.1, [enums_1.PatternType.LAYERED], {
|
|
50
|
+
folderPatterns: ['models', 'model', 'entities', 'entity', 'domain', 'dtos', 'dto'],
|
|
51
|
+
filePatterns: ['*Entity.*', '*Model.*', '*Dto.*', '*VO.*', '*Domain.*'],
|
|
52
|
+
codePatterns: [
|
|
53
|
+
// Java
|
|
54
|
+
{ language: enums_1.Language.JAVA, type: enums_1.CodePatternType.ANNOTATION, pattern: /@Entity\b/ },
|
|
55
|
+
{ language: enums_1.Language.JAVA, type: enums_1.CodePatternType.ANNOTATION, pattern: /@Table\b/ },
|
|
56
|
+
{ language: enums_1.Language.JAVA, type: enums_1.CodePatternType.ANNOTATION, pattern: /@Data\b/ },
|
|
57
|
+
// Kotlin
|
|
58
|
+
{ language: enums_1.Language.KOTLIN, type: enums_1.CodePatternType.CLASS, pattern: /data\s+class\s+\w+/ },
|
|
59
|
+
// TypeScript/JavaScript
|
|
60
|
+
{ language: enums_1.Language.TYPESCRIPT, type: enums_1.CodePatternType.ANNOTATION, pattern: /@Entity\s*\(/ },
|
|
61
|
+
{ language: enums_1.Language.TYPESCRIPT, type: enums_1.CodePatternType.ANNOTATION, pattern: /@Column\s*\(/ },
|
|
62
|
+
{
|
|
63
|
+
language: enums_1.Language.TYPESCRIPT,
|
|
64
|
+
type: enums_1.CodePatternType.PATTERN,
|
|
65
|
+
pattern: /interface\s+\w+\s*\{/,
|
|
66
|
+
},
|
|
67
|
+
// C#
|
|
68
|
+
{ language: enums_1.Language.CSHARP, type: enums_1.CodePatternType.ANNOTATION, pattern: /\[Table\s*\(/ },
|
|
69
|
+
{ language: enums_1.Language.CSHARP, type: enums_1.CodePatternType.ANNOTATION, pattern: /\[Key\]/ },
|
|
70
|
+
// PHP
|
|
71
|
+
{ language: enums_1.Language.PHP, type: enums_1.CodePatternType.CLASS, pattern: /extends\s+Model\b/ },
|
|
72
|
+
// Python
|
|
73
|
+
{ language: enums_1.Language.PYTHON, type: enums_1.CodePatternType.ANNOTATION, pattern: /@dataclass\b/ },
|
|
74
|
+
// Go
|
|
75
|
+
{ language: enums_1.Language.GO, type: enums_1.CodePatternType.PATTERN, pattern: /type\s+\w+\s+struct\s*\{/ },
|
|
76
|
+
],
|
|
77
|
+
}, {
|
|
78
|
+
isKeyIndicator: true,
|
|
79
|
+
tags: ['layered', 'model', 'entity'],
|
|
80
|
+
});
|
|
81
|
+
let L004ModelLayer = (() => {
|
|
82
|
+
let _classDecorators = [(0, rule_registry_1.RegisterRule)(rule)];
|
|
83
|
+
let _classDescriptor;
|
|
84
|
+
let _classExtraInitializers = [];
|
|
85
|
+
let _classThis;
|
|
86
|
+
let _classSuper = base_rule_1.BaseRule;
|
|
87
|
+
var L004ModelLayer = _classThis = class extends _classSuper {
|
|
88
|
+
constructor() {
|
|
89
|
+
super(...arguments);
|
|
90
|
+
this.rule = rule;
|
|
91
|
+
}
|
|
92
|
+
async customDetection(context) {
|
|
93
|
+
const matches = [];
|
|
94
|
+
let hasFolders = false;
|
|
95
|
+
let hasFiles = false;
|
|
96
|
+
let hasCode = false;
|
|
97
|
+
// Check for model folders
|
|
98
|
+
const modelFolders = [
|
|
99
|
+
'models',
|
|
100
|
+
'model',
|
|
101
|
+
'entities',
|
|
102
|
+
'entity',
|
|
103
|
+
'domain',
|
|
104
|
+
'dtos',
|
|
105
|
+
'dto',
|
|
106
|
+
'types',
|
|
107
|
+
];
|
|
108
|
+
for (const folder of modelFolders) {
|
|
109
|
+
const found = context.files.some((f) => f.relativePath.toLowerCase().includes(`/${folder}/`) ||
|
|
110
|
+
f.relativePath.toLowerCase().startsWith(`${folder}/`));
|
|
111
|
+
if (found) {
|
|
112
|
+
matches.push({
|
|
113
|
+
type: enums_1.MatchType.FOLDER,
|
|
114
|
+
path: folder,
|
|
115
|
+
matchedPattern: 'Model folder',
|
|
116
|
+
});
|
|
117
|
+
hasFolders = true;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Check for model/entity files
|
|
121
|
+
const modelFiles = context.files.filter((f) => /Entity\.(ts|js|java|cs|php|py|rb|go)$/i.test(f.fileName) ||
|
|
122
|
+
/Model\.(ts|js|java|cs|php|py|rb|go)$/i.test(f.fileName) ||
|
|
123
|
+
/Dto\.(ts|js|java|cs|php|py|rb|go)$/i.test(f.fileName) ||
|
|
124
|
+
/\.entity\.(ts|js)$/i.test(f.fileName) ||
|
|
125
|
+
/\.model\.(ts|js)$/i.test(f.fileName) ||
|
|
126
|
+
/\.dto\.(ts|js)$/i.test(f.fileName));
|
|
127
|
+
if (modelFiles.length > 0) {
|
|
128
|
+
for (const file of modelFiles.slice(0, 5)) {
|
|
129
|
+
matches.push({
|
|
130
|
+
type: enums_1.MatchType.FILE,
|
|
131
|
+
path: file.relativePath,
|
|
132
|
+
matchedPattern: 'Model file',
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
hasFiles = true;
|
|
136
|
+
}
|
|
137
|
+
// Check for code patterns
|
|
138
|
+
const sourceFiles = context.files
|
|
139
|
+
.filter((f) => !f.isTest && !f.isConfig && f.language)
|
|
140
|
+
.slice(0, 50);
|
|
141
|
+
const codePatterns = [
|
|
142
|
+
/@Entity\b/,
|
|
143
|
+
/@Table\b/,
|
|
144
|
+
/@Data\b/,
|
|
145
|
+
/data\s+class\s+\w+/,
|
|
146
|
+
/\[Table\s*\(/,
|
|
147
|
+
/extends\s+Model\b/,
|
|
148
|
+
/@dataclass\b/,
|
|
149
|
+
];
|
|
150
|
+
for (const file of sourceFiles) {
|
|
151
|
+
try {
|
|
152
|
+
const content = await context.helpers.readFile(file.absolutePath);
|
|
153
|
+
for (const pattern of codePatterns) {
|
|
154
|
+
if (pattern.test(content)) {
|
|
155
|
+
hasCode = true;
|
|
156
|
+
matches.push({
|
|
157
|
+
type: enums_1.MatchType.CODE,
|
|
158
|
+
path: file.relativePath,
|
|
159
|
+
matchedPattern: 'Model pattern',
|
|
160
|
+
});
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (hasCode && matches.length >= 10)
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
// Skip unreadable files
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// Calculate score
|
|
172
|
+
let score = 0;
|
|
173
|
+
if (hasFolders && hasFiles)
|
|
174
|
+
score = 1;
|
|
175
|
+
else if (hasFiles)
|
|
176
|
+
score = 0.7;
|
|
177
|
+
else if (hasCode)
|
|
178
|
+
score = 0.6;
|
|
179
|
+
else if (hasFolders)
|
|
180
|
+
score = 0.4;
|
|
181
|
+
return { matches, violations: [], score };
|
|
182
|
+
}
|
|
183
|
+
getMetadata(context, matches) {
|
|
184
|
+
const modelCount = matches.filter((m) => m.matchedPattern === 'Model file').length;
|
|
185
|
+
return {
|
|
186
|
+
...super.getMetadata(context, matches),
|
|
187
|
+
modelCount,
|
|
188
|
+
hasDedicatedFolder: matches.some((m) => m.matchedPattern === 'Model folder'),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
__setFunctionName(_classThis, "L004ModelLayer");
|
|
193
|
+
(() => {
|
|
194
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
195
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
196
|
+
L004ModelLayer = _classThis = _classDescriptor.value;
|
|
197
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
198
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
199
|
+
})();
|
|
200
|
+
return L004ModelLayer = _classThis;
|
|
201
|
+
})();
|
|
202
|
+
exports.L004ModelLayer = L004ModelLayer;
|
|
203
|
+
exports.default = L004ModelLayer;
|
|
204
|
+
//# sourceMappingURL=l004-model-layer.js.map
|