@rigour-labs/core 2.14.0 → 2.16.0
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/gates/ast-handlers/typescript.js +78 -3
- package/dist/services/context-engine.js +1 -1
- package/dist/templates/index.js +13 -0
- package/dist/types/index.d.ts +44 -0
- package/dist/types/index.js +14 -0
- package/dist/utils/logger.js +3 -3
- package/package.json +1 -1
- package/src/gates/ast-handlers/typescript.ts +85 -3
- package/src/services/context-engine.ts +1 -1
- package/src/templates/index.ts +13 -0
- package/src/types/index.ts +14 -0
- package/src/utils/logger.ts +3 -3
|
@@ -14,14 +14,89 @@ export class TypeScriptHandler extends ASTHandler {
|
|
|
14
14
|
}
|
|
15
15
|
analyzeSourceFile(sourceFile, relativePath, failures) {
|
|
16
16
|
const astConfig = this.config.ast || {};
|
|
17
|
+
const stalenessConfig = this.config.staleness || {};
|
|
18
|
+
const stalenessRules = stalenessConfig.rules || {};
|
|
17
19
|
const maxComplexity = astConfig.complexity || 10;
|
|
18
20
|
const maxMethods = astConfig.max_methods || 10;
|
|
19
21
|
const maxParams = astConfig.max_params || 5;
|
|
22
|
+
// Limit failures per file to avoid output bloat on large files
|
|
23
|
+
const MAX_FAILURES_PER_FILE = 50;
|
|
24
|
+
const fileFailureCount = {};
|
|
25
|
+
const addFailure = (failure) => {
|
|
26
|
+
const ruleId = failure.id;
|
|
27
|
+
fileFailureCount[ruleId] = (fileFailureCount[ruleId] || 0) + 1;
|
|
28
|
+
if (fileFailureCount[ruleId] <= MAX_FAILURES_PER_FILE) {
|
|
29
|
+
failures.push(failure);
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
// Add summary failure once when limit is reached
|
|
33
|
+
if (fileFailureCount[ruleId] === MAX_FAILURES_PER_FILE + 1) {
|
|
34
|
+
failures.push({
|
|
35
|
+
id: `${ruleId}_LIMIT_EXCEEDED`,
|
|
36
|
+
title: `More than ${MAX_FAILURES_PER_FILE} ${ruleId} violations in ${relativePath}`,
|
|
37
|
+
details: `Truncated output: showing first ${MAX_FAILURES_PER_FILE} violations. Consider fixing the root cause.`,
|
|
38
|
+
files: [relativePath],
|
|
39
|
+
hint: `This file has many violations. Fix them systematically or exclude the file if it's legacy code.`
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
};
|
|
44
|
+
// Helper to check if a staleness rule is enabled
|
|
45
|
+
const isRuleEnabled = (rule) => {
|
|
46
|
+
if (!stalenessConfig.enabled)
|
|
47
|
+
return false;
|
|
48
|
+
return stalenessRules[rule] !== false; // Enabled by default if staleness is on
|
|
49
|
+
};
|
|
20
50
|
const visit = (node) => {
|
|
51
|
+
// === STALENESS CHECKS (Rule-based) ===
|
|
52
|
+
// no-var: Forbid legacy 'var' keyword
|
|
53
|
+
if (isRuleEnabled('no-var') && ts.isVariableStatement(node)) {
|
|
54
|
+
const declarationList = node.declarationList;
|
|
55
|
+
// NodeFlags: Let = 1, Const = 2, None = 0 (var)
|
|
56
|
+
if ((declarationList.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) === 0) {
|
|
57
|
+
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
58
|
+
addFailure({
|
|
59
|
+
id: 'STALENESS_NO_VAR',
|
|
60
|
+
title: `Stale 'var' keyword at line ${line}`,
|
|
61
|
+
details: `Use 'const' or 'let' instead of 'var' in ${relativePath}:${line}`,
|
|
62
|
+
files: [relativePath],
|
|
63
|
+
hint: `Replace 'var' with 'const' (preferred) or 'let' for modern JavaScript.`
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// no-commonjs: Forbid require() in favor of import
|
|
68
|
+
if (isRuleEnabled('no-commonjs') && ts.isCallExpression(node)) {
|
|
69
|
+
if (ts.isIdentifier(node.expression) && node.expression.text === 'require') {
|
|
70
|
+
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
71
|
+
addFailure({
|
|
72
|
+
id: 'STALENESS_NO_COMMONJS',
|
|
73
|
+
title: `CommonJS require() at line ${line}`,
|
|
74
|
+
details: `Use ES6 'import' instead of 'require()' in ${relativePath}:${line}`,
|
|
75
|
+
files: [relativePath],
|
|
76
|
+
hint: `Replace require('module') with import module from 'module'.`
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// no-arguments: Forbid 'arguments' object (use rest params)
|
|
81
|
+
if (isRuleEnabled('no-arguments') && ts.isIdentifier(node) && node.text === 'arguments') {
|
|
82
|
+
// Check if it's actually the arguments keyword and not a variable named arguments
|
|
83
|
+
const parent = node.parent;
|
|
84
|
+
if (!ts.isVariableDeclaration(parent) && !ts.isPropertyAccessExpression(parent)) {
|
|
85
|
+
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
86
|
+
addFailure({
|
|
87
|
+
id: 'STALENESS_NO_ARGUMENTS',
|
|
88
|
+
title: `Legacy 'arguments' object at line ${line}`,
|
|
89
|
+
details: `Use rest parameters (...args) instead of 'arguments' in ${relativePath}:${line}`,
|
|
90
|
+
files: [relativePath],
|
|
91
|
+
hint: `Replace 'arguments' with rest parameters: function(...args) { }`
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// === COMPLEXITY CHECKS ===
|
|
21
96
|
if (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node) || ts.isArrowFunction(node)) {
|
|
22
97
|
const name = this.getNodeName(node);
|
|
23
98
|
if (node.parameters.length > maxParams) {
|
|
24
|
-
|
|
99
|
+
addFailure({
|
|
25
100
|
id: 'AST_MAX_PARAMS',
|
|
26
101
|
title: `Function '${name}' has ${node.parameters.length} parameters (max: ${maxParams})`,
|
|
27
102
|
details: `High parameter count detected in ${relativePath}`,
|
|
@@ -46,7 +121,7 @@ export class TypeScriptHandler extends ASTHandler {
|
|
|
46
121
|
};
|
|
47
122
|
ts.forEachChild(node, countComplexity);
|
|
48
123
|
if (complexity > maxComplexity) {
|
|
49
|
-
|
|
124
|
+
addFailure({
|
|
50
125
|
id: 'AST_COMPLEXITY',
|
|
51
126
|
title: `Function '${name}' has cyclomatic complexity of ${complexity} (max: ${maxComplexity})`,
|
|
52
127
|
details: `High complexity detected in ${relativePath}`,
|
|
@@ -59,7 +134,7 @@ export class TypeScriptHandler extends ASTHandler {
|
|
|
59
134
|
const name = node.name?.text || 'Anonymous Class';
|
|
60
135
|
const methods = node.members.filter(ts.isMethodDeclaration);
|
|
61
136
|
if (methods.length > maxMethods) {
|
|
62
|
-
|
|
137
|
+
addFailure({
|
|
63
138
|
id: 'AST_MAX_METHODS',
|
|
64
139
|
title: `Class '${name}' has ${methods.length} methods (max: ${maxMethods})`,
|
|
65
140
|
details: `God Object pattern detected in ${relativePath}`,
|
|
@@ -31,7 +31,7 @@ export class ContextEngine {
|
|
|
31
31
|
}
|
|
32
32
|
catch (e) { }
|
|
33
33
|
}
|
|
34
|
-
|
|
34
|
+
// Logs removed to avoid stdout pollution in JSON mode
|
|
35
35
|
// Convert envVars to anchors
|
|
36
36
|
for (const [name, count] of envVars.entries()) {
|
|
37
37
|
const confidence = count >= 2 ? 1 : 0.5;
|
package/dist/templates/index.js
CHANGED
|
@@ -192,6 +192,19 @@ export const UNIVERSAL_CONFIG = {
|
|
|
192
192
|
auto_classify: true,
|
|
193
193
|
doc_sources: {},
|
|
194
194
|
},
|
|
195
|
+
staleness: {
|
|
196
|
+
enabled: false,
|
|
197
|
+
rules: {
|
|
198
|
+
'no-var': true,
|
|
199
|
+
'no-commonjs': false,
|
|
200
|
+
'no-arguments': false,
|
|
201
|
+
'prefer-arrow': false,
|
|
202
|
+
'prefer-template': false,
|
|
203
|
+
'prefer-spread': false,
|
|
204
|
+
'prefer-rest': false,
|
|
205
|
+
'prefer-const': false,
|
|
206
|
+
},
|
|
207
|
+
},
|
|
195
208
|
},
|
|
196
209
|
output: {
|
|
197
210
|
report_path: 'rigour-report.json',
|
package/dist/types/index.d.ts
CHANGED
|
@@ -30,6 +30,16 @@ export declare const GatesSchema: z.ZodObject<{
|
|
|
30
30
|
max_class_dependencies?: number | undefined;
|
|
31
31
|
max_function_lines?: number | undefined;
|
|
32
32
|
}>>>;
|
|
33
|
+
staleness: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
34
|
+
enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
35
|
+
rules: z.ZodDefault<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodBoolean>>>;
|
|
36
|
+
}, "strip", z.ZodTypeAny, {
|
|
37
|
+
enabled: boolean;
|
|
38
|
+
rules: Record<string, boolean>;
|
|
39
|
+
}, {
|
|
40
|
+
enabled?: boolean | undefined;
|
|
41
|
+
rules?: Record<string, boolean> | undefined;
|
|
42
|
+
}>>>;
|
|
33
43
|
dependencies: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
34
44
|
forbid: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
35
45
|
trusted_registry: z.ZodOptional<z.ZodString>;
|
|
@@ -140,6 +150,10 @@ export declare const GatesSchema: z.ZodObject<{
|
|
|
140
150
|
max_class_dependencies: number;
|
|
141
151
|
max_function_lines: number;
|
|
142
152
|
};
|
|
153
|
+
staleness: {
|
|
154
|
+
enabled: boolean;
|
|
155
|
+
rules: Record<string, boolean>;
|
|
156
|
+
};
|
|
143
157
|
dependencies: {
|
|
144
158
|
forbid: string[];
|
|
145
159
|
trusted_registry?: string | undefined;
|
|
@@ -188,6 +202,10 @@ export declare const GatesSchema: z.ZodObject<{
|
|
|
188
202
|
max_class_dependencies?: number | undefined;
|
|
189
203
|
max_function_lines?: number | undefined;
|
|
190
204
|
} | undefined;
|
|
205
|
+
staleness?: {
|
|
206
|
+
enabled?: boolean | undefined;
|
|
207
|
+
rules?: Record<string, boolean> | undefined;
|
|
208
|
+
} | undefined;
|
|
191
209
|
dependencies?: {
|
|
192
210
|
forbid?: string[] | undefined;
|
|
193
211
|
trusted_registry?: string | undefined;
|
|
@@ -289,6 +307,16 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
289
307
|
max_class_dependencies?: number | undefined;
|
|
290
308
|
max_function_lines?: number | undefined;
|
|
291
309
|
}>>>;
|
|
310
|
+
staleness: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
311
|
+
enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
312
|
+
rules: z.ZodDefault<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodBoolean>>>;
|
|
313
|
+
}, "strip", z.ZodTypeAny, {
|
|
314
|
+
enabled: boolean;
|
|
315
|
+
rules: Record<string, boolean>;
|
|
316
|
+
}, {
|
|
317
|
+
enabled?: boolean | undefined;
|
|
318
|
+
rules?: Record<string, boolean> | undefined;
|
|
319
|
+
}>>>;
|
|
292
320
|
dependencies: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
293
321
|
forbid: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
294
322
|
trusted_registry: z.ZodOptional<z.ZodString>;
|
|
@@ -399,6 +427,10 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
399
427
|
max_class_dependencies: number;
|
|
400
428
|
max_function_lines: number;
|
|
401
429
|
};
|
|
430
|
+
staleness: {
|
|
431
|
+
enabled: boolean;
|
|
432
|
+
rules: Record<string, boolean>;
|
|
433
|
+
};
|
|
402
434
|
dependencies: {
|
|
403
435
|
forbid: string[];
|
|
404
436
|
trusted_registry?: string | undefined;
|
|
@@ -447,6 +479,10 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
447
479
|
max_class_dependencies?: number | undefined;
|
|
448
480
|
max_function_lines?: number | undefined;
|
|
449
481
|
} | undefined;
|
|
482
|
+
staleness?: {
|
|
483
|
+
enabled?: boolean | undefined;
|
|
484
|
+
rules?: Record<string, boolean> | undefined;
|
|
485
|
+
} | undefined;
|
|
450
486
|
dependencies?: {
|
|
451
487
|
forbid?: string[] | undefined;
|
|
452
488
|
trusted_registry?: string | undefined;
|
|
@@ -514,6 +550,10 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
514
550
|
max_class_dependencies: number;
|
|
515
551
|
max_function_lines: number;
|
|
516
552
|
};
|
|
553
|
+
staleness: {
|
|
554
|
+
enabled: boolean;
|
|
555
|
+
rules: Record<string, boolean>;
|
|
556
|
+
};
|
|
517
557
|
dependencies: {
|
|
518
558
|
forbid: string[];
|
|
519
559
|
trusted_registry?: string | undefined;
|
|
@@ -580,6 +620,10 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
580
620
|
max_class_dependencies?: number | undefined;
|
|
581
621
|
max_function_lines?: number | undefined;
|
|
582
622
|
} | undefined;
|
|
623
|
+
staleness?: {
|
|
624
|
+
enabled?: boolean | undefined;
|
|
625
|
+
rules?: Record<string, boolean> | undefined;
|
|
626
|
+
} | undefined;
|
|
583
627
|
dependencies?: {
|
|
584
628
|
forbid?: string[] | undefined;
|
|
585
629
|
trusted_registry?: string | undefined;
|
package/dist/types/index.js
CHANGED
|
@@ -19,6 +19,20 @@ export const GatesSchema = z.object({
|
|
|
19
19
|
max_class_dependencies: z.number().optional().default(5),
|
|
20
20
|
max_function_lines: z.number().optional().default(50),
|
|
21
21
|
}).optional().default({}),
|
|
22
|
+
staleness: z.object({
|
|
23
|
+
enabled: z.boolean().optional().default(false),
|
|
24
|
+
// Rule-based staleness detection (toggle individual rules)
|
|
25
|
+
rules: z.record(z.boolean()).optional().default({
|
|
26
|
+
'no-var': true, // var → const/let (ES6+)
|
|
27
|
+
'no-commonjs': false, // require() → import
|
|
28
|
+
'no-arguments': false, // arguments → rest params
|
|
29
|
+
'prefer-arrow': false, // function → arrow function
|
|
30
|
+
'prefer-template': false, // 'a' + b → `a${b}`
|
|
31
|
+
'prefer-spread': false, // apply() → spread
|
|
32
|
+
'prefer-rest': false, // arguments → ...args
|
|
33
|
+
'prefer-const': false, // let (unchanged) → const
|
|
34
|
+
}),
|
|
35
|
+
}).optional().default({}),
|
|
22
36
|
dependencies: z.object({
|
|
23
37
|
forbid: z.array(z.string()).optional().default([]),
|
|
24
38
|
trusted_registry: z.string().optional(),
|
package/dist/utils/logger.js
CHANGED
|
@@ -13,12 +13,12 @@ export class Logger {
|
|
|
13
13
|
}
|
|
14
14
|
static info(message) {
|
|
15
15
|
if (this.level <= LogLevel.INFO) {
|
|
16
|
-
console.
|
|
16
|
+
console.error(chalk.blue('info: ') + message);
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
static warn(message) {
|
|
20
20
|
if (this.level <= LogLevel.WARN) {
|
|
21
|
-
console.
|
|
21
|
+
console.error(chalk.yellow('warn: ') + message);
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
static error(message, error) {
|
|
@@ -31,7 +31,7 @@ export class Logger {
|
|
|
31
31
|
}
|
|
32
32
|
static debug(message) {
|
|
33
33
|
if (this.level <= LogLevel.DEBUG) {
|
|
34
|
-
console.
|
|
34
|
+
console.error(chalk.dim('debug: ') + message);
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
}
|
package/package.json
CHANGED
|
@@ -18,16 +18,98 @@ export class TypeScriptHandler extends ASTHandler {
|
|
|
18
18
|
|
|
19
19
|
private analyzeSourceFile(sourceFile: ts.SourceFile, relativePath: string, failures: Failure[]) {
|
|
20
20
|
const astConfig = this.config.ast || {};
|
|
21
|
+
const stalenessConfig = (this.config as any).staleness || {};
|
|
22
|
+
const stalenessRules = stalenessConfig.rules || {};
|
|
21
23
|
const maxComplexity = astConfig.complexity || 10;
|
|
22
24
|
const maxMethods = astConfig.max_methods || 10;
|
|
23
25
|
const maxParams = astConfig.max_params || 5;
|
|
24
26
|
|
|
27
|
+
// Limit failures per file to avoid output bloat on large files
|
|
28
|
+
const MAX_FAILURES_PER_FILE = 50;
|
|
29
|
+
const fileFailureCount: Record<string, number> = {};
|
|
30
|
+
|
|
31
|
+
const addFailure = (failure: Failure): boolean => {
|
|
32
|
+
const ruleId = failure.id;
|
|
33
|
+
fileFailureCount[ruleId] = (fileFailureCount[ruleId] || 0) + 1;
|
|
34
|
+
if (fileFailureCount[ruleId] <= MAX_FAILURES_PER_FILE) {
|
|
35
|
+
failures.push(failure);
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
// Add summary failure once when limit is reached
|
|
39
|
+
if (fileFailureCount[ruleId] === MAX_FAILURES_PER_FILE + 1) {
|
|
40
|
+
failures.push({
|
|
41
|
+
id: `${ruleId}_LIMIT_EXCEEDED`,
|
|
42
|
+
title: `More than ${MAX_FAILURES_PER_FILE} ${ruleId} violations in ${relativePath}`,
|
|
43
|
+
details: `Truncated output: showing first ${MAX_FAILURES_PER_FILE} violations. Consider fixing the root cause.`,
|
|
44
|
+
files: [relativePath],
|
|
45
|
+
hint: `This file has many violations. Fix them systematically or exclude the file if it's legacy code.`
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Helper to check if a staleness rule is enabled
|
|
52
|
+
const isRuleEnabled = (rule: string): boolean => {
|
|
53
|
+
if (!stalenessConfig.enabled) return false;
|
|
54
|
+
return stalenessRules[rule] !== false; // Enabled by default if staleness is on
|
|
55
|
+
};
|
|
56
|
+
|
|
25
57
|
const visit = (node: ts.Node) => {
|
|
58
|
+
// === STALENESS CHECKS (Rule-based) ===
|
|
59
|
+
|
|
60
|
+
// no-var: Forbid legacy 'var' keyword
|
|
61
|
+
if (isRuleEnabled('no-var') && ts.isVariableStatement(node)) {
|
|
62
|
+
const declarationList = node.declarationList;
|
|
63
|
+
// NodeFlags: Let = 1, Const = 2, None = 0 (var)
|
|
64
|
+
if ((declarationList.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) === 0) {
|
|
65
|
+
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
66
|
+
addFailure({
|
|
67
|
+
id: 'STALENESS_NO_VAR',
|
|
68
|
+
title: `Stale 'var' keyword at line ${line}`,
|
|
69
|
+
details: `Use 'const' or 'let' instead of 'var' in ${relativePath}:${line}`,
|
|
70
|
+
files: [relativePath],
|
|
71
|
+
hint: `Replace 'var' with 'const' (preferred) or 'let' for modern JavaScript.`
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// no-commonjs: Forbid require() in favor of import
|
|
77
|
+
if (isRuleEnabled('no-commonjs') && ts.isCallExpression(node)) {
|
|
78
|
+
if (ts.isIdentifier(node.expression) && node.expression.text === 'require') {
|
|
79
|
+
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
80
|
+
addFailure({
|
|
81
|
+
id: 'STALENESS_NO_COMMONJS',
|
|
82
|
+
title: `CommonJS require() at line ${line}`,
|
|
83
|
+
details: `Use ES6 'import' instead of 'require()' in ${relativePath}:${line}`,
|
|
84
|
+
files: [relativePath],
|
|
85
|
+
hint: `Replace require('module') with import module from 'module'.`
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// no-arguments: Forbid 'arguments' object (use rest params)
|
|
91
|
+
if (isRuleEnabled('no-arguments') && ts.isIdentifier(node) && node.text === 'arguments') {
|
|
92
|
+
// Check if it's actually the arguments keyword and not a variable named arguments
|
|
93
|
+
const parent = node.parent;
|
|
94
|
+
if (!ts.isVariableDeclaration(parent) && !ts.isPropertyAccessExpression(parent)) {
|
|
95
|
+
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
96
|
+
addFailure({
|
|
97
|
+
id: 'STALENESS_NO_ARGUMENTS',
|
|
98
|
+
title: `Legacy 'arguments' object at line ${line}`,
|
|
99
|
+
details: `Use rest parameters (...args) instead of 'arguments' in ${relativePath}:${line}`,
|
|
100
|
+
files: [relativePath],
|
|
101
|
+
hint: `Replace 'arguments' with rest parameters: function(...args) { }`
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// === COMPLEXITY CHECKS ===
|
|
107
|
+
|
|
26
108
|
if (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node) || ts.isArrowFunction(node)) {
|
|
27
109
|
const name = this.getNodeName(node);
|
|
28
110
|
|
|
29
111
|
if (node.parameters.length > maxParams) {
|
|
30
|
-
|
|
112
|
+
addFailure({
|
|
31
113
|
id: 'AST_MAX_PARAMS',
|
|
32
114
|
title: `Function '${name}' has ${node.parameters.length} parameters (max: ${maxParams})`,
|
|
33
115
|
details: `High parameter count detected in ${relativePath}`,
|
|
@@ -54,7 +136,7 @@ export class TypeScriptHandler extends ASTHandler {
|
|
|
54
136
|
ts.forEachChild(node, countComplexity);
|
|
55
137
|
|
|
56
138
|
if (complexity > maxComplexity) {
|
|
57
|
-
|
|
139
|
+
addFailure({
|
|
58
140
|
id: 'AST_COMPLEXITY',
|
|
59
141
|
title: `Function '${name}' has cyclomatic complexity of ${complexity} (max: ${maxComplexity})`,
|
|
60
142
|
details: `High complexity detected in ${relativePath}`,
|
|
@@ -69,7 +151,7 @@ export class TypeScriptHandler extends ASTHandler {
|
|
|
69
151
|
const methods = node.members.filter(ts.isMethodDeclaration);
|
|
70
152
|
|
|
71
153
|
if (methods.length > maxMethods) {
|
|
72
|
-
|
|
154
|
+
addFailure({
|
|
73
155
|
id: 'AST_MAX_METHODS',
|
|
74
156
|
title: `Class '${name}' has ${methods.length} methods (max: ${maxMethods})`,
|
|
75
157
|
details: `God Object pattern detected in ${relativePath}`,
|
|
@@ -50,7 +50,7 @@ export class ContextEngine {
|
|
|
50
50
|
} catch (e) { }
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
// Logs removed to avoid stdout pollution in JSON mode
|
|
54
54
|
|
|
55
55
|
// Convert envVars to anchors
|
|
56
56
|
for (const [name, count] of envVars.entries()) {
|
package/src/templates/index.ts
CHANGED
|
@@ -208,6 +208,19 @@ export const UNIVERSAL_CONFIG: Config = {
|
|
|
208
208
|
auto_classify: true,
|
|
209
209
|
doc_sources: {},
|
|
210
210
|
},
|
|
211
|
+
staleness: {
|
|
212
|
+
enabled: false,
|
|
213
|
+
rules: {
|
|
214
|
+
'no-var': true,
|
|
215
|
+
'no-commonjs': false,
|
|
216
|
+
'no-arguments': false,
|
|
217
|
+
'prefer-arrow': false,
|
|
218
|
+
'prefer-template': false,
|
|
219
|
+
'prefer-spread': false,
|
|
220
|
+
'prefer-rest': false,
|
|
221
|
+
'prefer-const': false,
|
|
222
|
+
},
|
|
223
|
+
},
|
|
211
224
|
},
|
|
212
225
|
output: {
|
|
213
226
|
report_path: 'rigour-report.json',
|
package/src/types/index.ts
CHANGED
|
@@ -20,6 +20,20 @@ export const GatesSchema = z.object({
|
|
|
20
20
|
max_class_dependencies: z.number().optional().default(5),
|
|
21
21
|
max_function_lines: z.number().optional().default(50),
|
|
22
22
|
}).optional().default({}),
|
|
23
|
+
staleness: z.object({
|
|
24
|
+
enabled: z.boolean().optional().default(false),
|
|
25
|
+
// Rule-based staleness detection (toggle individual rules)
|
|
26
|
+
rules: z.record(z.boolean()).optional().default({
|
|
27
|
+
'no-var': true, // var → const/let (ES6+)
|
|
28
|
+
'no-commonjs': false, // require() → import
|
|
29
|
+
'no-arguments': false, // arguments → rest params
|
|
30
|
+
'prefer-arrow': false, // function → arrow function
|
|
31
|
+
'prefer-template': false, // 'a' + b → `a${b}`
|
|
32
|
+
'prefer-spread': false, // apply() → spread
|
|
33
|
+
'prefer-rest': false, // arguments → ...args
|
|
34
|
+
'prefer-const': false, // let (unchanged) → const
|
|
35
|
+
}),
|
|
36
|
+
}).optional().default({}),
|
|
23
37
|
dependencies: z.object({
|
|
24
38
|
forbid: z.array(z.string()).optional().default([]),
|
|
25
39
|
trusted_registry: z.string().optional(),
|
package/src/utils/logger.ts
CHANGED
|
@@ -16,13 +16,13 @@ export class Logger {
|
|
|
16
16
|
|
|
17
17
|
static info(message: string) {
|
|
18
18
|
if (this.level <= LogLevel.INFO) {
|
|
19
|
-
console.
|
|
19
|
+
console.error(chalk.blue('info: ') + message);
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
static warn(message: string) {
|
|
24
24
|
if (this.level <= LogLevel.WARN) {
|
|
25
|
-
console.
|
|
25
|
+
console.error(chalk.yellow('warn: ') + message);
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -37,7 +37,7 @@ export class Logger {
|
|
|
37
37
|
|
|
38
38
|
static debug(message: string) {
|
|
39
39
|
if (this.level <= LogLevel.DEBUG) {
|
|
40
|
-
console.
|
|
40
|
+
console.error(chalk.dim('debug: ') + message);
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
}
|