@gobing-ai/ts-rule-engine 0.2.6 → 0.2.8
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/README.md +563 -1
- package/dist/config/extensions.d.ts +48 -0
- package/dist/config/extensions.d.ts.map +1 -0
- package/dist/config/extensions.js +67 -0
- package/dist/config/loader.d.ts +20 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +52 -26
- package/dist/engine.d.ts +26 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +79 -0
- package/dist/evaluators/exit-code-evaluator.d.ts +1 -1
- package/dist/evaluators/exit-code-evaluator.d.ts.map +1 -1
- package/dist/evaluators/exit-code-evaluator.js +22 -9
- package/dist/evaluators/forbidden-import-evaluator.d.ts +16 -3
- package/dist/evaluators/forbidden-import-evaluator.d.ts.map +1 -1
- package/dist/evaluators/forbidden-import-evaluator.js +71 -6
- package/dist/evaluators/import-boundary-evaluator.d.ts +19 -0
- package/dist/evaluators/import-boundary-evaluator.d.ts.map +1 -0
- package/dist/evaluators/import-boundary-evaluator.js +85 -0
- package/dist/evaluators/path-evaluator.d.ts +15 -2
- package/dist/evaluators/path-evaluator.d.ts.map +1 -1
- package/dist/evaluators/path-evaluator.js +49 -3
- package/dist/evaluators/regex-evaluator.d.ts.map +1 -1
- package/dist/evaluators/regex-evaluator.js +43 -8
- package/dist/evaluators/schema-artifact-evaluator.d.ts +21 -0
- package/dist/evaluators/schema-artifact-evaluator.d.ts.map +1 -0
- package/dist/evaluators/schema-artifact-evaluator.js +102 -0
- package/dist/evaluators/secrets-scanner-evaluator.d.ts +13 -2
- package/dist/evaluators/secrets-scanner-evaluator.d.ts.map +1 -1
- package/dist/evaluators/secrets-scanner-evaluator.js +72 -9
- package/dist/evaluators/sg-evaluator.d.ts +19 -0
- package/dist/evaluators/sg-evaluator.d.ts.map +1 -0
- package/dist/evaluators/sg-evaluator.js +112 -0
- package/dist/evaluators/test-location-evaluator.d.ts +14 -1
- package/dist/evaluators/test-location-evaluator.d.ts.map +1 -1
- package/dist/evaluators/test-location-evaluator.js +42 -22
- package/dist/evaluators/tsdoc-export-evaluator.js +19 -5
- package/dist/fixers/fixers.d.ts +86 -0
- package/dist/fixers/fixers.d.ts.map +1 -0
- package/dist/fixers/fixers.js +230 -0
- package/dist/fixers/test-stub-fixer.d.ts +49 -0
- package/dist/fixers/test-stub-fixer.d.ts.map +1 -0
- package/dist/fixers/test-stub-fixer.js +91 -0
- package/dist/host/builtins.d.ts.map +1 -1
- package/dist/host/builtins.js +12 -1
- package/dist/host/rule-engine-host.d.ts +3 -0
- package/dist/host/rule-engine-host.d.ts.map +1 -1
- package/dist/host/rule-engine-host.js +3 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/resolvers/test-path-resolver.d.ts +72 -0
- package/dist/resolvers/test-path-resolver.d.ts.map +1 -0
- package/dist/resolvers/test-path-resolver.js +112 -0
- package/dist/types.d.ts +36 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +10 -0
- package/package.json +4 -3
- package/schemas/preset.schema.json +40 -0
- package/schemas/rule-file.schema.json +49 -0
- package/src/config/extensions.ts +115 -0
- package/src/config/loader.ts +81 -25
- package/src/engine.ts +99 -2
- package/src/evaluators/exit-code-evaluator.ts +27 -9
- package/src/evaluators/forbidden-import-evaluator.ts +101 -7
- package/src/evaluators/import-boundary-evaluator.ts +135 -0
- package/src/evaluators/path-evaluator.ts +66 -3
- package/src/evaluators/regex-evaluator.ts +53 -12
- package/src/evaluators/schema-artifact-evaluator.ts +134 -0
- package/src/evaluators/secrets-scanner-evaluator.ts +89 -11
- package/src/evaluators/sg-evaluator.ts +133 -0
- package/src/evaluators/test-location-evaluator.ts +47 -35
- package/src/evaluators/tsdoc-export-evaluator.ts +19 -5
- package/src/fixers/fixers.ts +294 -0
- package/src/fixers/test-stub-fixer.ts +118 -0
- package/src/host/builtins.ts +17 -1
- package/src/host/rule-engine-host.ts +4 -0
- package/src/index.ts +4 -0
- package/src/resolvers/test-path-resolver.ts +133 -0
- package/src/types.ts +40 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pluggable test-path resolution for the test-location evaluator.
|
|
3
|
+
*
|
|
4
|
+
* Each implementation maps a source file path to its conventional test file path
|
|
5
|
+
* for one language, so the same evaluator works across TypeScript, Python, Go, and
|
|
6
|
+
* Rust projects. Resolvers are registered on the host and selected per rule via
|
|
7
|
+
* `evaluator.config.resolver`.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** Metadata about an exported symbol, used when generating a test skeleton. */
|
|
11
|
+
export interface ExportInfo {
|
|
12
|
+
/** Symbol name. */
|
|
13
|
+
readonly name: string;
|
|
14
|
+
/** Declaration kind. */
|
|
15
|
+
readonly kind: 'function' | 'class' | 'const' | 'type' | 'interface' | 'unknown';
|
|
16
|
+
/** One-based source line, when known. */
|
|
17
|
+
readonly line?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Maps source files to expected test files for a project type. */
|
|
21
|
+
export interface TestPathResolver {
|
|
22
|
+
/** Language name this resolver handles (registry key). */
|
|
23
|
+
readonly name: string;
|
|
24
|
+
/** Compute the expected test file path for a source file. */
|
|
25
|
+
resolveTestPath(srcRelPath: string): string;
|
|
26
|
+
/** Optionally generate a test skeleton for a source file. */
|
|
27
|
+
generateSkeleton?(srcRelPath: string, exports: ExportInfo[]): string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* TypeScript conventions:
|
|
32
|
+
* src/foo/bar.ts → tests/foo/bar.test.ts
|
|
33
|
+
* packages/core/src/foo/bar.ts → packages/core/tests/foo/bar.test.ts
|
|
34
|
+
*/
|
|
35
|
+
export class TypeScriptTestPathResolver implements TestPathResolver {
|
|
36
|
+
/** Registry key. */
|
|
37
|
+
readonly name = 'typescript';
|
|
38
|
+
|
|
39
|
+
constructor() {}
|
|
40
|
+
|
|
41
|
+
/** Map a TS/JS source path to its `tests/…test.ts` counterpart. */
|
|
42
|
+
resolveTestPath(srcRelPath: string): string {
|
|
43
|
+
if (srcRelPath.includes('.test.') || srcRelPath.includes('.spec.')) return srcRelPath;
|
|
44
|
+
const srcIdx = srcRelPath.indexOf('/src/');
|
|
45
|
+
if (srcIdx !== -1) {
|
|
46
|
+
const pkg = srcRelPath.slice(0, srcIdx);
|
|
47
|
+
const rel = srcRelPath.slice(srcIdx + '/src/'.length).replace(/\.(ts|tsx|js|jsx)$/, '.test.ts');
|
|
48
|
+
return `${pkg}/tests/${rel}`;
|
|
49
|
+
}
|
|
50
|
+
const withoutExt = srcRelPath.replace(/\.(ts|tsx|js|jsx)$/, '');
|
|
51
|
+
return `tests/${withoutExt.replace(/^src\//, '')}.test.ts`;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Python conventions (pytest):
|
|
57
|
+
* src/foo/bar.py → tests/foo/test_bar.py
|
|
58
|
+
*/
|
|
59
|
+
export class PythonTestPathResolver implements TestPathResolver {
|
|
60
|
+
/** Registry key. */
|
|
61
|
+
readonly name = 'python';
|
|
62
|
+
|
|
63
|
+
constructor() {}
|
|
64
|
+
|
|
65
|
+
/** Map a Python source path to its `tests/…/test_*.py` counterpart. */
|
|
66
|
+
resolveTestPath(srcRelPath: string): string {
|
|
67
|
+
if (!srcRelPath) throw new Error('empty source path');
|
|
68
|
+
if (srcRelPath.endsWith('_test.py') || srcRelPath.includes('/test_') || srcRelPath.startsWith('test_')) {
|
|
69
|
+
return srcRelPath;
|
|
70
|
+
}
|
|
71
|
+
if (srcRelPath.startsWith('tests/')) return srcRelPath;
|
|
72
|
+
if (!srcRelPath.endsWith('.py')) throw new Error(`unsupported extension for python resolver: ${srcRelPath}`);
|
|
73
|
+
const srcIdx = srcRelPath.indexOf('/src/');
|
|
74
|
+
if (srcIdx !== -1) {
|
|
75
|
+
const pkg = srcRelPath.slice(0, srcIdx);
|
|
76
|
+
return `${pkg}/tests/${testify(srcRelPath.slice(srcIdx + '/src/'.length))}`;
|
|
77
|
+
}
|
|
78
|
+
if (srcRelPath.startsWith('src/')) return `tests/${testify(srcRelPath.slice(4))}`;
|
|
79
|
+
return `tests/${testify(srcRelPath)}`;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Go conventions:
|
|
85
|
+
* foo/bar.go → foo/bar_test.go (sibling)
|
|
86
|
+
*/
|
|
87
|
+
export class GoTestPathResolver implements TestPathResolver {
|
|
88
|
+
/** Registry key. */
|
|
89
|
+
readonly name = 'go';
|
|
90
|
+
|
|
91
|
+
constructor() {}
|
|
92
|
+
|
|
93
|
+
/** Map a Go source path to its sibling `_test.go` file. */
|
|
94
|
+
resolveTestPath(srcRelPath: string): string {
|
|
95
|
+
if (!srcRelPath) throw new Error('empty source path');
|
|
96
|
+
if (srcRelPath.endsWith('_test.go')) return srcRelPath;
|
|
97
|
+
if (!srcRelPath.endsWith('.go')) throw new Error(`unsupported extension for go resolver: ${srcRelPath}`);
|
|
98
|
+
return srcRelPath.replace(/\.go$/, '_test.go');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Rust conventions (Cargo integration tests):
|
|
104
|
+
* crate/src/foo.rs → crate/tests/foo.rs
|
|
105
|
+
*/
|
|
106
|
+
export class RustTestPathResolver implements TestPathResolver {
|
|
107
|
+
/** Registry key. */
|
|
108
|
+
readonly name = 'rust';
|
|
109
|
+
|
|
110
|
+
constructor() {}
|
|
111
|
+
|
|
112
|
+
/** Map a Rust source path to its `tests/` integration-test counterpart. */
|
|
113
|
+
resolveTestPath(srcRelPath: string): string {
|
|
114
|
+
if (!srcRelPath) throw new Error('empty source path');
|
|
115
|
+
if (srcRelPath.startsWith('tests/') || srcRelPath.includes('/tests/')) return srcRelPath;
|
|
116
|
+
if (!srcRelPath.endsWith('.rs')) throw new Error(`unsupported extension for rust resolver: ${srcRelPath}`);
|
|
117
|
+
const srcIdx = srcRelPath.indexOf('/src/');
|
|
118
|
+
if (srcIdx !== -1) {
|
|
119
|
+
const crate = srcRelPath.slice(0, srcIdx);
|
|
120
|
+
return `${crate}/tests/${srcRelPath.slice(srcIdx + '/src/'.length)}`;
|
|
121
|
+
}
|
|
122
|
+
if (srcRelPath.startsWith('src/')) return `tests/${srcRelPath.slice(4)}`;
|
|
123
|
+
return `tests/${srcRelPath}`;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Prefix the basename of a Python source path with `test_`. */
|
|
128
|
+
function testify(rel: string): string {
|
|
129
|
+
const lastSlash = rel.lastIndexOf('/');
|
|
130
|
+
const dir = lastSlash >= 0 ? rel.slice(0, lastSlash + 1) : '';
|
|
131
|
+
const base = lastSlash >= 0 ? rel.slice(lastSlash + 1) : rel;
|
|
132
|
+
return `${dir}test_${base}`;
|
|
133
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -46,6 +46,8 @@ export interface RuleFixConfig {
|
|
|
46
46
|
|
|
47
47
|
/** Rule file shape before normalization. */
|
|
48
48
|
export interface ConstraintRuleFile {
|
|
49
|
+
/** Optional JSON Schema ref used by editors and loader validation. */
|
|
50
|
+
$schema?: string;
|
|
49
51
|
/** File-level default include patterns. */
|
|
50
52
|
include?: string[];
|
|
51
53
|
/** File-level default exclude patterns. */
|
|
@@ -56,8 +58,22 @@ export interface ConstraintRuleFile {
|
|
|
56
58
|
rules: ConstraintRule[];
|
|
57
59
|
}
|
|
58
60
|
|
|
61
|
+
/** Relative module paths a preset contributes per capability kind. */
|
|
62
|
+
export interface PresetExtensions {
|
|
63
|
+
/** Test-path resolver module paths. */
|
|
64
|
+
resolvers?: string[];
|
|
65
|
+
/** Evaluator module paths. */
|
|
66
|
+
evaluators?: string[];
|
|
67
|
+
/** Fixer module paths. */
|
|
68
|
+
fixers?: string[];
|
|
69
|
+
/** Formatter module paths. */
|
|
70
|
+
formatters?: string[];
|
|
71
|
+
}
|
|
72
|
+
|
|
59
73
|
/** Preset definition that composes category folders or other presets. */
|
|
60
74
|
export interface PresetDefinition {
|
|
75
|
+
/** Optional JSON Schema ref used by editors and loader validation. */
|
|
76
|
+
$schema?: string;
|
|
61
77
|
/** Preset name. */
|
|
62
78
|
name: string;
|
|
63
79
|
/** Category folders or preset names to compose. */
|
|
@@ -66,6 +82,8 @@ export interface PresetDefinition {
|
|
|
66
82
|
disable?: string[];
|
|
67
83
|
/** Per-rule overrides. */
|
|
68
84
|
overrides?: Record<string, { fix?: { mode: FixMode } }>;
|
|
85
|
+
/** Custom capability modules contributed by this preset (opt-in to load). */
|
|
86
|
+
extensions?: PresetExtensions;
|
|
69
87
|
}
|
|
70
88
|
|
|
71
89
|
/** Candidate fix emitted by an evaluator or fixer. */
|
|
@@ -84,6 +102,16 @@ export interface Fix {
|
|
|
84
102
|
mode: Exclude<FixMode, 'none'>;
|
|
85
103
|
}
|
|
86
104
|
|
|
105
|
+
/**
|
|
106
|
+
* What a finding represents.
|
|
107
|
+
*
|
|
108
|
+
* - `violation`: the rule ran and the project breached its policy (the default).
|
|
109
|
+
* - `error`: the rule could not run — a misconfiguration or runtime fault in the
|
|
110
|
+
* evaluator itself. These are not policy breaches and callers may surface them
|
|
111
|
+
* separately (e.g. "rule misconfigured") rather than as project violations.
|
|
112
|
+
*/
|
|
113
|
+
export type FindingKind = 'violation' | 'error';
|
|
114
|
+
|
|
87
115
|
/** Finding emitted by a constraint rule. */
|
|
88
116
|
export interface ConstraintFinding {
|
|
89
117
|
/** Rule identifier. */
|
|
@@ -100,6 +128,8 @@ export interface ConstraintFinding {
|
|
|
100
128
|
column?: number;
|
|
101
129
|
/** Machine-readable evaluator/source code. */
|
|
102
130
|
code?: string;
|
|
131
|
+
/** Whether this is a policy violation or an evaluator error. Absent means `violation`. */
|
|
132
|
+
kind?: FindingKind;
|
|
103
133
|
}
|
|
104
134
|
|
|
105
135
|
/** Aggregate result returned by a rule evaluator. */
|
|
@@ -180,6 +210,7 @@ export const ConstraintRuleSchema = z.object({
|
|
|
180
210
|
|
|
181
211
|
/** Zod schema for a constraint rule file. */
|
|
182
212
|
export const ConstraintRuleFileSchema = z.object({
|
|
213
|
+
$schema: z.string().optional(),
|
|
183
214
|
include: z.array(z.string()).optional(),
|
|
184
215
|
exclude: z.array(z.string()).optional(),
|
|
185
216
|
severity: z.enum(['error', 'warning', 'info']).optional(),
|
|
@@ -188,10 +219,19 @@ export const ConstraintRuleFileSchema = z.object({
|
|
|
188
219
|
|
|
189
220
|
/** Zod schema for a preset definition. */
|
|
190
221
|
export const PresetDefinitionSchema = z.object({
|
|
222
|
+
$schema: z.string().optional(),
|
|
191
223
|
name: z.string().min(1),
|
|
192
224
|
extends: z.array(z.string()).default([]),
|
|
193
225
|
disable: z.array(z.string()).optional(),
|
|
194
226
|
overrides: z
|
|
195
227
|
.record(z.string(), z.object({ fix: z.object({ mode: z.enum(['none', 'suggest', 'auto']) }).optional() }))
|
|
196
228
|
.optional(),
|
|
229
|
+
extensions: z
|
|
230
|
+
.object({
|
|
231
|
+
resolvers: z.array(z.string()).optional(),
|
|
232
|
+
evaluators: z.array(z.string()).optional(),
|
|
233
|
+
fixers: z.array(z.string()).optional(),
|
|
234
|
+
formatters: z.array(z.string()).optional(),
|
|
235
|
+
})
|
|
236
|
+
.optional(),
|
|
197
237
|
});
|