@iris-code/core 0.1.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/README.md +38 -0
- package/dist/config.d.ts +147 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +550 -0
- package/dist/config.js.map +1 -0
- package/dist/csharp/analyser.d.ts +14 -0
- package/dist/csharp/analyser.d.ts.map +1 -0
- package/dist/csharp/analyser.js +436 -0
- package/dist/csharp/analyser.js.map +1 -0
- package/dist/csharp/lexer.d.ts +29 -0
- package/dist/csharp/lexer.d.ts.map +1 -0
- package/dist/csharp/lexer.js +182 -0
- package/dist/csharp/lexer.js.map +1 -0
- package/dist/duplicates.d.ts +53 -0
- package/dist/duplicates.d.ts.map +1 -0
- package/dist/duplicates.js +491 -0
- package/dist/duplicates.js.map +1 -0
- package/dist/fileAnalyser.d.ts +4 -0
- package/dist/fileAnalyser.d.ts.map +1 -0
- package/dist/fileAnalyser.js +8 -0
- package/dist/fileAnalyser.js.map +1 -0
- package/dist/fileNaming.d.ts +68 -0
- package/dist/fileNaming.d.ts.map +1 -0
- package/dist/fileNaming.js +343 -0
- package/dist/fileNaming.js.map +1 -0
- package/dist/gate.d.ts +49 -0
- package/dist/gate.d.ts.map +1 -0
- package/dist/gate.js +242 -0
- package/dist/gate.js.map +1 -0
- package/dist/go/analyser.d.ts +5 -0
- package/dist/go/analyser.d.ts.map +1 -0
- package/dist/go/analyser.js +576 -0
- package/dist/go/analyser.js.map +1 -0
- package/dist/guards.d.ts +16 -0
- package/dist/guards.d.ts.map +1 -0
- package/dist/guards.js +355 -0
- package/dist/guards.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +110 -0
- package/dist/index.js.map +1 -0
- package/dist/languages.d.ts +46 -0
- package/dist/languages.d.ts.map +1 -0
- package/dist/languages.js +155 -0
- package/dist/languages.js.map +1 -0
- package/dist/lines.d.ts +9 -0
- package/dist/lines.d.ts.map +1 -0
- package/dist/lines.js +58 -0
- package/dist/lines.js.map +1 -0
- package/dist/python/analyser.d.ts +5 -0
- package/dist/python/analyser.d.ts.map +1 -0
- package/dist/python/analyser.js +630 -0
- package/dist/python/analyser.js.map +1 -0
- package/dist/qualitySignals.d.ts +30 -0
- package/dist/qualitySignals.d.ts.map +1 -0
- package/dist/qualitySignals.js +101 -0
- package/dist/qualitySignals.js.map +1 -0
- package/dist/ruby/analyser.d.ts +5 -0
- package/dist/ruby/analyser.d.ts.map +1 -0
- package/dist/ruby/analyser.js +450 -0
- package/dist/ruby/analyser.js.map +1 -0
- package/dist/scoring.d.ts +4 -0
- package/dist/scoring.d.ts.map +1 -0
- package/dist/scoring.js +37 -0
- package/dist/scoring.js.map +1 -0
- package/dist/secrets.d.ts +5 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +245 -0
- package/dist/secrets.js.map +1 -0
- package/dist/securityContext.d.ts +2 -0
- package/dist/securityContext.d.ts.map +1 -0
- package/dist/securityContext.js +16 -0
- package/dist/securityContext.js.map +1 -0
- package/dist/sfc.d.ts +32 -0
- package/dist/sfc.d.ts.map +1 -0
- package/dist/sfc.js +228 -0
- package/dist/sfc.js.map +1 -0
- package/dist/slop.d.ts +37 -0
- package/dist/slop.d.ts.map +1 -0
- package/dist/slop.js +81 -0
- package/dist/slop.js.map +1 -0
- package/dist/suppressions.d.ts +19 -0
- package/dist/suppressions.d.ts.map +1 -0
- package/dist/suppressions.js +152 -0
- package/dist/suppressions.js.map +1 -0
- package/dist/ts/analyser.d.ts +4 -0
- package/dist/ts/analyser.d.ts.map +1 -0
- package/dist/ts/analyser.js +769 -0
- package/dist/ts/analyser.js.map +1 -0
- package/dist/types.d.ts +139 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +46 -0
- package/dist/types.js.map +1 -0
- package/package.json +43 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FILE_NAMING_GLOB_MAX_LENGTH = exports.FILE_NAMING_EXEMPTIONS = exports.FILE_NAMING_MULTI_DOT_SUFFIXES = void 0;
|
|
4
|
+
exports.isUsableFileNamingGlob = isUsableFileNamingGlob;
|
|
5
|
+
exports.matchesAnyFileNamingGlob = matchesAnyFileNamingGlob;
|
|
6
|
+
exports.resolveFileNamingPolicy = resolveFileNamingPolicy;
|
|
7
|
+
exports.checkStem = checkStem;
|
|
8
|
+
exports.suggestName = suggestName;
|
|
9
|
+
exports.isExemptPath = isExemptPath;
|
|
10
|
+
exports.checkFileNaming = checkFileNaming;
|
|
11
|
+
exports.detectDominantConvention = detectDominantConvention;
|
|
12
|
+
exports.FILE_NAMING_MULTI_DOT_SUFFIXES = [
|
|
13
|
+
'.test',
|
|
14
|
+
'.spec',
|
|
15
|
+
'.stories',
|
|
16
|
+
'.story',
|
|
17
|
+
'.generated',
|
|
18
|
+
];
|
|
19
|
+
exports.FILE_NAMING_EXEMPTIONS = {
|
|
20
|
+
nextReservedStems: [
|
|
21
|
+
'page', 'layout', 'route', 'loading', 'error', 'global-error', 'not-found',
|
|
22
|
+
'template', 'default', 'middleware', 'instrumentation', 'opengraph-image',
|
|
23
|
+
'sitemap', 'robots', 'manifest', '_app', '_document', '_error',
|
|
24
|
+
],
|
|
25
|
+
pythonReservedFiles: ['__init__.py', '__main__.py', 'conftest.py', 'setup.py', 'manage.py'],
|
|
26
|
+
generatedDirectories: ['vendor', 'node_modules', 'dist', 'build', '.next', '__pycache__'],
|
|
27
|
+
// SvelteKit routing is filename-based and every route file is `+`-prefixed:
|
|
28
|
+
// +page.svelte, +layout.svelte, +error.svelte, +server.ts, +page.server.ts,
|
|
29
|
+
// +layout.server.ts, and their .js variants. The framework resolves these by
|
|
30
|
+
// exact name, so they cannot be renamed - the same reason Next.js reserved
|
|
31
|
+
// stems and Python dunder files are exempt. They also broke the affordance,
|
|
32
|
+
// not just the count: `suggestName('+page')` returns '+page', so every
|
|
33
|
+
// SvelteKit route offered a rename to its own current name.
|
|
34
|
+
svelteKitRoutePrefix: '+',
|
|
35
|
+
};
|
|
36
|
+
const NEXT_RESERVED_STEMS = new Set(exports.FILE_NAMING_EXEMPTIONS.nextReservedStems);
|
|
37
|
+
const PYTHON_RESERVED = new Set(exports.FILE_NAMING_EXEMPTIONS.pythonReservedFiles);
|
|
38
|
+
const GENERATED_DIRS = new Set(exports.FILE_NAMING_EXEMPTIONS.generatedDirectories);
|
|
39
|
+
const GO_PLATFORM_SUFFIX = /_(?:windows|linux|darwin|freebsd|openbsd|netbsd|solaris|aix|android|ios|js|wasm|amd64|arm64|386|arm|ppc64|ppc64le|mips|mips64|mips64le|riscv64|s390x)$/;
|
|
40
|
+
function normalisePath(filePath) {
|
|
41
|
+
return filePath.replace(/\\/g, '/').replace(/^\.\//, '');
|
|
42
|
+
}
|
|
43
|
+
function basename(filePath) {
|
|
44
|
+
return normalisePath(filePath).split('/').pop() ?? filePath;
|
|
45
|
+
}
|
|
46
|
+
function extensionOf(fileName) {
|
|
47
|
+
const index = fileName.lastIndexOf('.');
|
|
48
|
+
return index > 0 ? fileName.slice(index) : '';
|
|
49
|
+
}
|
|
50
|
+
function stemOf(fileName) {
|
|
51
|
+
let stem = fileName.slice(0, fileName.length - extensionOf(fileName).length);
|
|
52
|
+
for (const suffix of exports.FILE_NAMING_MULTI_DOT_SUFFIXES) {
|
|
53
|
+
if (stem.endsWith(suffix)) {
|
|
54
|
+
stem = stem.slice(0, -suffix.length);
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (fileName.endsWith('.go'))
|
|
59
|
+
stem = stem.replace(GO_PLATFORM_SUFFIX, '');
|
|
60
|
+
return stem;
|
|
61
|
+
}
|
|
62
|
+
// Longest accepted naming glob. These come from `.irisconfig.json`, which
|
|
63
|
+
// arrives with a cloned repo, so the length is untrusted. Every glob construct
|
|
64
|
+
// EXPANDS when compiled - a single `?` becomes a four-character class, and a
|
|
65
|
+
// globstar becomes an eight-character group - so a long pattern produces a far
|
|
66
|
+
// longer regex and can exceed the engine's own size limit.
|
|
67
|
+
//
|
|
68
|
+
// Real globs are well under 100 characters; 1024 is deliberately generous.
|
|
69
|
+
exports.FILE_NAMING_GLOB_MAX_LENGTH = 1024;
|
|
70
|
+
/**
|
|
71
|
+
* True when a pattern can be compiled and used. An over-long or uncompilable
|
|
72
|
+
* glob is reported by the config resolver so the user is told, rather than the
|
|
73
|
+
* pattern silently doing nothing.
|
|
74
|
+
*/
|
|
75
|
+
function isUsableFileNamingGlob(pattern) {
|
|
76
|
+
return typeof pattern === 'string'
|
|
77
|
+
&& pattern.length <= exports.FILE_NAMING_GLOB_MAX_LENGTH
|
|
78
|
+
&& globToRegExp(pattern) !== null;
|
|
79
|
+
}
|
|
80
|
+
// Compiled patterns are cached because matchesAnyFileNamingGlob runs per FILE
|
|
81
|
+
// per pattern: a workspace scan with a handful of globs across 10k files
|
|
82
|
+
// otherwise recompiles the same regexes tens of thousands of times.
|
|
83
|
+
const globCache = new Map();
|
|
84
|
+
function globToRegExp(pattern) {
|
|
85
|
+
const cached = globCache.get(pattern);
|
|
86
|
+
if (cached !== undefined)
|
|
87
|
+
return cached;
|
|
88
|
+
const compiled = compileGlob(pattern);
|
|
89
|
+
// Bounded so a hostile config cannot grow the cache without limit.
|
|
90
|
+
if (globCache.size < 500)
|
|
91
|
+
globCache.set(pattern, compiled);
|
|
92
|
+
return compiled;
|
|
93
|
+
}
|
|
94
|
+
function compileGlob(pattern) {
|
|
95
|
+
if (typeof pattern !== 'string' || pattern.length > exports.FILE_NAMING_GLOB_MAX_LENGTH)
|
|
96
|
+
return null;
|
|
97
|
+
const normalised = normalisePath(pattern);
|
|
98
|
+
let result = '^';
|
|
99
|
+
for (let i = 0; i < normalised.length; i++) {
|
|
100
|
+
const char = normalised[i];
|
|
101
|
+
if (char === '*') {
|
|
102
|
+
if (normalised[i + 1] === '*') {
|
|
103
|
+
i++;
|
|
104
|
+
if (normalised[i + 1] === '/') {
|
|
105
|
+
i++;
|
|
106
|
+
result += '(?:.*/)?';
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
result += '.*';
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
result += '[^/]*';
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else if (char === '?') {
|
|
117
|
+
result += '[^/]';
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
result += char.replace(/[|\\{}()[\]^$+?.]/g, '\\$&');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
return new RegExp(`${result}$`);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// The engine refuses regexes past its own size limit ("Invalid regular
|
|
128
|
+
// expression"). This used to throw straight out of checkFileNaming, so a
|
|
129
|
+
// single long glob in a cloned repo's config aborted the whole scan with a
|
|
130
|
+
// stack trace and a 157 KB regex dumped to the terminal (found 2026-08-04).
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function matchesAnyFileNamingGlob(filePath, patterns) {
|
|
135
|
+
const relative = normalisePath(filePath);
|
|
136
|
+
// An unusable pattern matches NOTHING, which is the fail-safe direction for
|
|
137
|
+
// both callers: an `ignore` entry cannot silently exempt a file from its
|
|
138
|
+
// convention, and an override cannot silently take over a path.
|
|
139
|
+
return patterns.some(pattern => globToRegExp(pattern)?.test(relative) ?? false);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Resolve one workspace-relative path. Global ignores win; matching overrides
|
|
143
|
+
* are applied in declaration order with the last match winning.
|
|
144
|
+
*/
|
|
145
|
+
function resolveFileNamingPolicy(relativePath, config) {
|
|
146
|
+
if (!config)
|
|
147
|
+
return null;
|
|
148
|
+
const normalised = normalisePath(relativePath);
|
|
149
|
+
if (matchesAnyFileNamingGlob(normalised, config.ignore ?? []))
|
|
150
|
+
return null;
|
|
151
|
+
let resolved = {
|
|
152
|
+
convention: config.convention,
|
|
153
|
+
...(config.scope ? { scope: config.scope } : {}),
|
|
154
|
+
};
|
|
155
|
+
const overrides = 'overrides' in config ? config.overrides ?? [] : [];
|
|
156
|
+
for (const override of overrides) {
|
|
157
|
+
if (!matchesAnyFileNamingGlob(normalised, override.files))
|
|
158
|
+
continue;
|
|
159
|
+
const scope = override.scope ?? resolved.scope;
|
|
160
|
+
resolved = {
|
|
161
|
+
convention: override.convention,
|
|
162
|
+
...(scope ? { scope } : {}),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return resolved;
|
|
166
|
+
}
|
|
167
|
+
function checkSegment(segment, convention) {
|
|
168
|
+
if (convention === 'snake_case')
|
|
169
|
+
return /^[a-z0-9]+(?:_[a-z0-9]+)*$/.test(segment);
|
|
170
|
+
if (convention === 'kebab-case')
|
|
171
|
+
return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(segment);
|
|
172
|
+
if (convention === 'camelCase')
|
|
173
|
+
return /^[a-z][a-z0-9]*(?:[A-Z][a-z0-9]*)*$/.test(segment);
|
|
174
|
+
return /^[A-Z][A-Za-z0-9]*$/.test(segment);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* A stem may carry DOTS, and each dot-separated segment is judged on its own.
|
|
178
|
+
* .NET names projects and partial classes that way - `Shop.Api.Tests.csproj`,
|
|
179
|
+
* `Order.Validation.cs` - and so do build files like `vite.config.ts`. Judging
|
|
180
|
+
* the whole stem at once failed every one of them, and because `suggestName`
|
|
181
|
+
* did not split on dots either, the offered fix for `Shop.Api.csproj` was
|
|
182
|
+
* `Shop.api.csproj`: a rename that breaks the solution file and every
|
|
183
|
+
* ProjectReference pointing at it.
|
|
184
|
+
*/
|
|
185
|
+
function checkStem(stem, convention) {
|
|
186
|
+
const segments = stem.split('.');
|
|
187
|
+
return segments.length > 0 && segments.every(segment => checkSegment(segment, convention));
|
|
188
|
+
}
|
|
189
|
+
function splitWords(stem) {
|
|
190
|
+
return stem
|
|
191
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
192
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
|
|
193
|
+
.split(/[_\-\s]+/)
|
|
194
|
+
.map(word => word.toLowerCase())
|
|
195
|
+
.filter(Boolean);
|
|
196
|
+
}
|
|
197
|
+
function suggestSegment(segment, convention) {
|
|
198
|
+
const words = splitWords(segment);
|
|
199
|
+
if (words.length === 0)
|
|
200
|
+
return segment;
|
|
201
|
+
if (convention === 'snake_case')
|
|
202
|
+
return words.join('_');
|
|
203
|
+
if (convention === 'kebab-case')
|
|
204
|
+
return words.join('-');
|
|
205
|
+
if (convention === 'camelCase') {
|
|
206
|
+
return words[0] + words.slice(1).map(word => word[0].toUpperCase() + word.slice(1)).join('');
|
|
207
|
+
}
|
|
208
|
+
return words.map(word => word[0].toUpperCase() + word.slice(1)).join('');
|
|
209
|
+
}
|
|
210
|
+
/** Suggests per dot-separated segment, matching `checkStem`: the dots in
|
|
211
|
+
* `Shop.Api.Tests` are part of the name, not word separators. */
|
|
212
|
+
function suggestName(stem, convention) {
|
|
213
|
+
if (!stem.includes('.'))
|
|
214
|
+
return suggestSegment(stem, convention);
|
|
215
|
+
return stem
|
|
216
|
+
.split('.')
|
|
217
|
+
.map(segment => suggestSegment(segment, convention))
|
|
218
|
+
.join('.');
|
|
219
|
+
}
|
|
220
|
+
function isExemptPath(filePath) {
|
|
221
|
+
const relative = normalisePath(filePath);
|
|
222
|
+
const parts = relative.split('/').filter(Boolean);
|
|
223
|
+
const fileName = basename(relative);
|
|
224
|
+
const lowerName = fileName.toLowerCase();
|
|
225
|
+
const extension = extensionOf(fileName);
|
|
226
|
+
const stem = stemOf(fileName);
|
|
227
|
+
if (parts.some(part => part.startsWith('.') || GENERATED_DIRS.has(part)))
|
|
228
|
+
return true;
|
|
229
|
+
// Route-construct exemptions apply to the FILE's own name only, never to
|
|
230
|
+
// ancestor folders. A route group `(app)`, private `_modals`, parallel `@slot`,
|
|
231
|
+
// or dynamic `[slug]` FOLDER does not exempt the ordinary files inside it -
|
|
232
|
+
// those still follow the convention. Only a file literally named as a route
|
|
233
|
+
// construct (pages-router `[id].tsx`, or a `_`/`@`-prefixed private file) is exempt.
|
|
234
|
+
if (/^\[\[?\.{0,3}[^\]]+\]\]?$/.test(stem) ||
|
|
235
|
+
/^\(.+\)$/.test(stem) ||
|
|
236
|
+
fileName.startsWith('@') ||
|
|
237
|
+
fileName.startsWith('_') ||
|
|
238
|
+
fileName.startsWith(exports.FILE_NAMING_EXEMPTIONS.svelteKitRoutePrefix))
|
|
239
|
+
return true;
|
|
240
|
+
if (lowerName.endsWith('.d.ts') || lowerName.endsWith('_test.go') || lowerName.endsWith('.pb.go'))
|
|
241
|
+
return true;
|
|
242
|
+
if (PYTHON_RESERVED.has(lowerName) || /^__.+__\.py$/.test(lowerName))
|
|
243
|
+
return true;
|
|
244
|
+
if (lowerName === 'main.go' || stem === 'index')
|
|
245
|
+
return true;
|
|
246
|
+
if (/(?:^|\.)config$/.test(stem) || /^(?:vite|esbuild|webpack|rollup|parcel)$/.test(stem))
|
|
247
|
+
return true;
|
|
248
|
+
if (exports.FILE_NAMING_MULTI_DOT_SUFFIXES.some(suffix => suffix === '.generated' && fileName.includes(`${suffix}.`)))
|
|
249
|
+
return true;
|
|
250
|
+
const underNextRoute = parts.some(part => part === 'app' || part === 'pages');
|
|
251
|
+
if (underNextRoute && NEXT_RESERVED_STEMS.has(stem))
|
|
252
|
+
return true;
|
|
253
|
+
if (NEXT_RESERVED_STEMS.has(stem) && ['middleware', 'instrumentation', 'sitemap', 'robots', 'manifest'].includes(stem))
|
|
254
|
+
return true;
|
|
255
|
+
if (extension === '')
|
|
256
|
+
return true;
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
function checkFileNaming(relativePath, config) {
|
|
260
|
+
const policy = resolveFileNamingPolicy(relativePath, config.fileNaming);
|
|
261
|
+
if (!policy)
|
|
262
|
+
return null;
|
|
263
|
+
const normalised = normalisePath(relativePath);
|
|
264
|
+
if (isExemptPath(normalised))
|
|
265
|
+
return null;
|
|
266
|
+
const fileName = basename(normalised);
|
|
267
|
+
const extension = extensionOf(fileName);
|
|
268
|
+
const rawStem = fileName.slice(0, fileName.length - extension.length);
|
|
269
|
+
const stem = stemOf(fileName);
|
|
270
|
+
if (checkStem(stem, policy.convention))
|
|
271
|
+
return null;
|
|
272
|
+
const suggestion = `${suggestName(stem, policy.convention)}${rawStem.slice(stem.length)}${extension}`;
|
|
273
|
+
// A stem the word-splitter cannot reshape (leading punctuation, digits only)
|
|
274
|
+
// renames to itself. Reporting that costs a point and offers a Quick Fix and a
|
|
275
|
+
// batch-rename row that do nothing, so treat an unactionable suggestion as no
|
|
276
|
+
// finding at all - the check exists to drive a rename, not to deduct a point.
|
|
277
|
+
if (suggestion === fileName)
|
|
278
|
+
return null;
|
|
279
|
+
return {
|
|
280
|
+
file: normalised,
|
|
281
|
+
line: 1,
|
|
282
|
+
convention: policy.convention,
|
|
283
|
+
actual: fileName,
|
|
284
|
+
suggestion,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
const ALL_CONVENTIONS = ['snake_case', 'kebab-case', 'camelCase', 'PascalCase'];
|
|
288
|
+
/**
|
|
289
|
+
* Infer the naming convention a codebase already follows, so a workspace with
|
|
290
|
+
* no configured policy can be offered a one-click "adopt this" nudge.
|
|
291
|
+
*
|
|
292
|
+
* Single-word stems like `user` are consistent with snake_case, kebab-case and
|
|
293
|
+
* camelCase at once, so they cannot discriminate. The winner is chosen by the
|
|
294
|
+
* plurality of *discriminating* files (those consistent with exactly one
|
|
295
|
+
* convention, e.g. `user_row`, `user-row`, `userRow`, `UserRow`). Returns null
|
|
296
|
+
* when there is not enough discriminating signal to make an honest suggestion.
|
|
297
|
+
*/
|
|
298
|
+
function detectDominantConvention(relativePaths, options = {}) {
|
|
299
|
+
const minDiscriminating = options.minDiscriminating ?? 5;
|
|
300
|
+
const minShare = options.minShare ?? 0.6;
|
|
301
|
+
const discriminatingCount = {
|
|
302
|
+
snake_case: 0,
|
|
303
|
+
'kebab-case': 0,
|
|
304
|
+
camelCase: 0,
|
|
305
|
+
PascalCase: 0,
|
|
306
|
+
};
|
|
307
|
+
let totalConsidered = 0;
|
|
308
|
+
let totalDiscriminating = 0;
|
|
309
|
+
const stems = [];
|
|
310
|
+
for (const filePath of relativePaths) {
|
|
311
|
+
const normalised = normalisePath(filePath);
|
|
312
|
+
if (isExemptPath(normalised))
|
|
313
|
+
continue;
|
|
314
|
+
const stem = stemOf(basename(normalised));
|
|
315
|
+
if (!stem)
|
|
316
|
+
continue;
|
|
317
|
+
totalConsidered++;
|
|
318
|
+
stems.push(stem);
|
|
319
|
+
const consistent = ALL_CONVENTIONS.filter((convention) => checkStem(stem, convention));
|
|
320
|
+
if (consistent.length === 1) {
|
|
321
|
+
discriminatingCount[consistent[0]]++;
|
|
322
|
+
totalDiscriminating++;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (totalDiscriminating < minDiscriminating)
|
|
326
|
+
return null;
|
|
327
|
+
let winner = 'kebab-case';
|
|
328
|
+
for (const convention of ALL_CONVENTIONS) {
|
|
329
|
+
if (discriminatingCount[convention] > discriminatingCount[winner])
|
|
330
|
+
winner = convention;
|
|
331
|
+
}
|
|
332
|
+
const winnerShare = discriminatingCount[winner] / totalDiscriminating;
|
|
333
|
+
if (winnerShare < minShare)
|
|
334
|
+
return null;
|
|
335
|
+
const matched = stems.filter((stem) => checkStem(stem, winner)).length;
|
|
336
|
+
return {
|
|
337
|
+
convention: winner,
|
|
338
|
+
matched,
|
|
339
|
+
total: totalConsidered,
|
|
340
|
+
share: totalConsidered > 0 ? matched / totalConsidered : 0,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
//# sourceMappingURL=fileNaming.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileNaming.js","sourceRoot":"","sources":["../src/fileNaming.ts"],"names":[],"mappings":"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"}
|
package/dist/gate.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { FileAnalysis } from './types';
|
|
2
|
+
import { LanguageGateCaps } from './languages';
|
|
3
|
+
export interface GateConfig {
|
|
4
|
+
minScore?: number;
|
|
5
|
+
maxSecrets?: number;
|
|
6
|
+
maxComplexity?: number;
|
|
7
|
+
maxFileLength?: number;
|
|
8
|
+
maxSmellsPerFile?: number;
|
|
9
|
+
maxSecuritySmells?: number;
|
|
10
|
+
maxSuppressions?: number;
|
|
11
|
+
maxDuplicateBlocks?: number;
|
|
12
|
+
maxNamingViolations?: number;
|
|
13
|
+
}
|
|
14
|
+
export type GateRuleType = 'score' | 'secrets' | 'complexity' | 'file-length' | 'smells' | 'security-smells' | 'suppressions' | 'duplicate-blocks' | 'naming-violations';
|
|
15
|
+
export interface GateFailingFile {
|
|
16
|
+
path: string;
|
|
17
|
+
value: number;
|
|
18
|
+
threshold?: number;
|
|
19
|
+
}
|
|
20
|
+
export interface GateFailure {
|
|
21
|
+
rule: GateRuleType;
|
|
22
|
+
threshold: number;
|
|
23
|
+
actual: number;
|
|
24
|
+
files: GateFailingFile[];
|
|
25
|
+
message: string;
|
|
26
|
+
}
|
|
27
|
+
export interface GateSummary {
|
|
28
|
+
workspaceScore: number;
|
|
29
|
+
filesAnalysed: number;
|
|
30
|
+
totalSecrets: number;
|
|
31
|
+
maxComplexity: number;
|
|
32
|
+
maxFileLength: number;
|
|
33
|
+
totalSecuritySmells: number;
|
|
34
|
+
totalSuppressions: number;
|
|
35
|
+
totalDuplicateBlocks: number;
|
|
36
|
+
totalNamingViolations: number;
|
|
37
|
+
}
|
|
38
|
+
export interface GateResult {
|
|
39
|
+
passed: boolean;
|
|
40
|
+
failures: GateFailure[];
|
|
41
|
+
summary: GateSummary;
|
|
42
|
+
}
|
|
43
|
+
export interface GateFileEntry {
|
|
44
|
+
path: string;
|
|
45
|
+
analysis: FileAnalysis;
|
|
46
|
+
caps?: LanguageGateCaps;
|
|
47
|
+
}
|
|
48
|
+
export declare function evaluateGate(config: GateConfig, files: GateFileEntry[]): GateResult;
|
|
49
|
+
//# sourceMappingURL=gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAE9C,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC7B;AAED,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,SAAS,GAAG,YAAY,GAAG,aAAa,GAAG,QAAQ,GAAG,iBAAiB,GAAG,cAAc,GAAG,kBAAkB,GAAG,mBAAmB,CAAA;AAExK,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IAGb,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,YAAY,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,eAAe,EAAE,CAAA;IACxB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,cAAc,EAAE,MAAM,CAAA;IACtB,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;IACrB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,iBAAiB,EAAE,MAAM,CAAA;IACzB,oBAAoB,EAAE,MAAM,CAAA;IAC5B,qBAAqB,EAAE,MAAM,CAAA;CAC9B;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,OAAO,CAAA;IACf,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB,OAAO,EAAE,WAAW,CAAA;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,YAAY,CAAA;IAItB,IAAI,CAAC,EAAE,gBAAgB,CAAA;CACxB;AA+CD,wBAAgB,YAAY,CAC1B,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,aAAa,EAAE,GACrB,UAAU,CAkPZ"}
|
package/dist/gate.js
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.evaluateGate = evaluateGate;
|
|
4
|
+
function countSmells(analysis) {
|
|
5
|
+
const { codeSmells } = analysis;
|
|
6
|
+
return (codeSmells.consoleLogs.length +
|
|
7
|
+
codeSmells.todos.length +
|
|
8
|
+
codeSmells.deepNesting +
|
|
9
|
+
codeSmells.longParameterLists.length +
|
|
10
|
+
codeSmells.magicNumbers.length +
|
|
11
|
+
codeSmells.unusedVars.length +
|
|
12
|
+
codeSmells.unusedFunctions.length +
|
|
13
|
+
codeSmells.hardcodedSecrets.length +
|
|
14
|
+
(codeSmells.bareSuppressions ?? []).length +
|
|
15
|
+
(codeSmells.fileNamingViolations ?? []).length);
|
|
16
|
+
}
|
|
17
|
+
function countSuppressions(analysis) {
|
|
18
|
+
return (analysis.suppressed ?? []).length;
|
|
19
|
+
}
|
|
20
|
+
// Only primary findings count toward totals: a cross-file block leaves a
|
|
21
|
+
// non-primary mirror on its second file so the same block is never counted twice.
|
|
22
|
+
function countDuplicateBlocks(analysis) {
|
|
23
|
+
return (analysis.codeSmells.duplicateBlocks ?? []).filter(d => d.primary !== false).length;
|
|
24
|
+
}
|
|
25
|
+
function countDuplicateInvolvement(analysis) {
|
|
26
|
+
return (analysis.codeSmells.duplicateBlocks ?? []).length;
|
|
27
|
+
}
|
|
28
|
+
function countSecuritySmells(analysis) {
|
|
29
|
+
const { codeSmells } = analysis;
|
|
30
|
+
return ((codeSmells.evalUsage ?? []).length +
|
|
31
|
+
(codeSmells.sqlConcatenation ?? []).length +
|
|
32
|
+
(codeSmells.insecureRandom ?? []).length +
|
|
33
|
+
(codeSmells.unsafeRegex ?? []).length +
|
|
34
|
+
(codeSmells.hardcodedLocalhost ?? []).length +
|
|
35
|
+
(codeSmells.disabledTlsVerification ?? []).length +
|
|
36
|
+
(codeSmells.debugFlagsEnabled ?? []).length +
|
|
37
|
+
(codeSmells.weakHashing ?? []).length +
|
|
38
|
+
(codeSmells.openRedirect ?? []).length);
|
|
39
|
+
}
|
|
40
|
+
function evaluateGate(config, files) {
|
|
41
|
+
const failures = [];
|
|
42
|
+
if (files.length === 0) {
|
|
43
|
+
return {
|
|
44
|
+
passed: true,
|
|
45
|
+
failures: [],
|
|
46
|
+
summary: {
|
|
47
|
+
workspaceScore: 100,
|
|
48
|
+
filesAnalysed: 0,
|
|
49
|
+
totalSecrets: 0,
|
|
50
|
+
maxComplexity: 0,
|
|
51
|
+
maxFileLength: 0,
|
|
52
|
+
totalSecuritySmells: 0,
|
|
53
|
+
totalSuppressions: 0,
|
|
54
|
+
totalDuplicateBlocks: 0,
|
|
55
|
+
totalNamingViolations: 0,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
let totalScore = 0;
|
|
60
|
+
let totalSecrets = 0;
|
|
61
|
+
let maxComplexity = 0;
|
|
62
|
+
let maxFileLength = 0;
|
|
63
|
+
let totalSecuritySmells = 0;
|
|
64
|
+
let totalSuppressions = 0;
|
|
65
|
+
let totalDuplicateBlocks = 0;
|
|
66
|
+
let totalNamingViolations = 0;
|
|
67
|
+
for (const { analysis } of files) {
|
|
68
|
+
totalScore += analysis.healthScore;
|
|
69
|
+
totalSecrets += analysis.codeSmells.hardcodedSecrets.length;
|
|
70
|
+
totalSecuritySmells += countSecuritySmells(analysis);
|
|
71
|
+
totalSuppressions += countSuppressions(analysis);
|
|
72
|
+
totalDuplicateBlocks += countDuplicateBlocks(analysis);
|
|
73
|
+
totalNamingViolations += (analysis.codeSmells.fileNamingViolations ?? []).length;
|
|
74
|
+
if (analysis.complexityScore > maxComplexity)
|
|
75
|
+
maxComplexity = analysis.complexityScore;
|
|
76
|
+
if (analysis.lineCount > maxFileLength)
|
|
77
|
+
maxFileLength = analysis.lineCount;
|
|
78
|
+
}
|
|
79
|
+
const workspaceScore = Math.floor(totalScore / files.length);
|
|
80
|
+
if (config.minScore !== undefined) {
|
|
81
|
+
const threshold = config.minScore;
|
|
82
|
+
const failing = files
|
|
83
|
+
.filter(({ analysis }) => Math.floor(analysis.healthScore) < threshold)
|
|
84
|
+
.map(({ path, analysis }) => ({ path, value: Math.floor(analysis.healthScore) }))
|
|
85
|
+
.sort((a, b) => a.value - b.value || a.path.localeCompare(b.path));
|
|
86
|
+
if (failing.length > 0) {
|
|
87
|
+
failures.push({
|
|
88
|
+
rule: 'score',
|
|
89
|
+
threshold,
|
|
90
|
+
actual: failing[0].value,
|
|
91
|
+
files: failing,
|
|
92
|
+
message: `${failing.length} file${failing.length !== 1 ? 's' : ''} below minimum score ${threshold}`,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (config.maxSecrets !== undefined) {
|
|
97
|
+
const threshold = config.maxSecrets;
|
|
98
|
+
if (totalSecrets > threshold) {
|
|
99
|
+
const failing = files
|
|
100
|
+
.filter(({ analysis }) => analysis.codeSmells.hardcodedSecrets.length > 0)
|
|
101
|
+
.map(({ path, analysis }) => ({ path, value: analysis.codeSmells.hardcodedSecrets.length }))
|
|
102
|
+
.sort((a, b) => b.value - a.value || a.path.localeCompare(b.path));
|
|
103
|
+
failures.push({
|
|
104
|
+
rule: 'secrets',
|
|
105
|
+
threshold,
|
|
106
|
+
actual: totalSecrets,
|
|
107
|
+
files: failing,
|
|
108
|
+
message: `Found ${totalSecrets} hardcoded secret${totalSecrets !== 1 ? 's' : ''} (maximum ${threshold})`,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (config.maxComplexity !== undefined) {
|
|
113
|
+
const threshold = config.maxComplexity;
|
|
114
|
+
const failing = files
|
|
115
|
+
.filter(({ analysis }) => analysis.complexityScore > threshold)
|
|
116
|
+
.map(({ path, analysis }) => ({ path, value: analysis.complexityScore }))
|
|
117
|
+
.sort((a, b) => b.value - a.value || a.path.localeCompare(b.path));
|
|
118
|
+
if (failing.length > 0) {
|
|
119
|
+
failures.push({
|
|
120
|
+
rule: 'complexity',
|
|
121
|
+
threshold,
|
|
122
|
+
actual: failing[0].value,
|
|
123
|
+
files: failing,
|
|
124
|
+
message: `${failing.length} file${failing.length !== 1 ? 's' : ''} exceed${failing.length !== 1 ? '' : 's'} maximum complexity ${threshold}`,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (config.maxFileLength !== undefined) {
|
|
129
|
+
const threshold = config.maxFileLength;
|
|
130
|
+
const failing = files
|
|
131
|
+
.filter(({ analysis }) => analysis.lineCount > threshold)
|
|
132
|
+
.map(({ path, analysis }) => ({ path, value: analysis.lineCount }))
|
|
133
|
+
.sort((a, b) => b.value - a.value || a.path.localeCompare(b.path));
|
|
134
|
+
if (failing.length > 0) {
|
|
135
|
+
failures.push({
|
|
136
|
+
rule: 'file-length',
|
|
137
|
+
threshold,
|
|
138
|
+
actual: failing[0].value,
|
|
139
|
+
files: failing,
|
|
140
|
+
message: `${failing.length} file${failing.length !== 1 ? 's' : ''} exceed${failing.length !== 1 ? '' : 's'} maximum length of ${threshold} lines`,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Per-file rules honour a per-language cap (Phase 5.10) when the entry
|
|
145
|
+
// carries one; the rule also runs when ONLY language caps set a limit, so a
|
|
146
|
+
// config with `gateMaxSmellsPerFile` set solely inside a language block
|
|
147
|
+
// still gates files of that language.
|
|
148
|
+
const evaluatePerFileCapRule = (rule, baseThreshold, capFor, countFor, describe) => {
|
|
149
|
+
const hasAnyCap = files.some(entry => capFor(entry) !== undefined);
|
|
150
|
+
if (baseThreshold === undefined && !hasAnyCap)
|
|
151
|
+
return;
|
|
152
|
+
const failing = [];
|
|
153
|
+
const appliedThresholds = [];
|
|
154
|
+
for (const entry of files) {
|
|
155
|
+
const effective = capFor(entry) ?? baseThreshold;
|
|
156
|
+
if (effective === undefined)
|
|
157
|
+
continue;
|
|
158
|
+
const value = countFor(entry.analysis);
|
|
159
|
+
if (value > effective) {
|
|
160
|
+
failing.push({ path: entry.path, value, threshold: effective });
|
|
161
|
+
appliedThresholds.push(effective);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
failing.sort((a, b) => b.value - a.value || a.path.localeCompare(b.path));
|
|
165
|
+
if (failing.length > 0) {
|
|
166
|
+
const ruleThreshold = baseThreshold ?? Math.min(...appliedThresholds);
|
|
167
|
+
// Only expose per-file thresholds when they diverge from the rule-level
|
|
168
|
+
// one - keeps the common single-threshold output unchanged.
|
|
169
|
+
const normalised = failing.map(file => file.threshold === ruleThreshold ? { path: file.path, value: file.value } : file);
|
|
170
|
+
failures.push({
|
|
171
|
+
rule,
|
|
172
|
+
threshold: ruleThreshold,
|
|
173
|
+
actual: normalised[0].value,
|
|
174
|
+
files: normalised,
|
|
175
|
+
message: describe(normalised.length, ruleThreshold),
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
evaluatePerFileCapRule('smells', config.maxSmellsPerFile, entry => entry.caps?.maxSmellsPerFile, countSmells, (failingCount, threshold) => `${failingCount} file${failingCount !== 1 ? 's' : ''} exceed${failingCount !== 1 ? '' : 's'} maximum smell count of ${threshold}`);
|
|
180
|
+
if (config.maxSecuritySmells !== undefined) {
|
|
181
|
+
const threshold = config.maxSecuritySmells;
|
|
182
|
+
if (totalSecuritySmells > threshold) {
|
|
183
|
+
const failing = files
|
|
184
|
+
.filter(({ analysis }) => countSecuritySmells(analysis) > 0)
|
|
185
|
+
.map(({ path, analysis }) => ({ path, value: countSecuritySmells(analysis) }))
|
|
186
|
+
.sort((a, b) => b.value - a.value || a.path.localeCompare(b.path));
|
|
187
|
+
failures.push({
|
|
188
|
+
rule: 'security-smells',
|
|
189
|
+
threshold,
|
|
190
|
+
actual: totalSecuritySmells,
|
|
191
|
+
files: failing,
|
|
192
|
+
message: `Found ${totalSecuritySmells} security smell${totalSecuritySmells !== 1 ? 's' : ''} (maximum ${threshold})`,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
evaluatePerFileCapRule('suppressions', config.maxSuppressions, entry => entry.caps?.maxSuppressions, countSuppressions, (failingCount, threshold) => `${failingCount} file${failingCount !== 1 ? 's' : ''} exceed${failingCount !== 1 ? '' : 's'} maximum suppression count of ${threshold}`);
|
|
197
|
+
if (config.maxDuplicateBlocks !== undefined) {
|
|
198
|
+
const threshold = config.maxDuplicateBlocks;
|
|
199
|
+
if (totalDuplicateBlocks > threshold) {
|
|
200
|
+
const failing = files
|
|
201
|
+
.filter(({ analysis }) => countDuplicateInvolvement(analysis) > 0)
|
|
202
|
+
.map(({ path, analysis }) => ({ path, value: countDuplicateInvolvement(analysis) }))
|
|
203
|
+
.sort((a, b) => b.value - a.value || a.path.localeCompare(b.path));
|
|
204
|
+
failures.push({
|
|
205
|
+
rule: 'duplicate-blocks',
|
|
206
|
+
threshold,
|
|
207
|
+
actual: totalDuplicateBlocks,
|
|
208
|
+
files: failing,
|
|
209
|
+
message: `Found ${totalDuplicateBlocks} duplicate code block${totalDuplicateBlocks !== 1 ? 's' : ''} (maximum ${threshold})`,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (config.maxNamingViolations !== undefined && totalNamingViolations > config.maxNamingViolations) {
|
|
214
|
+
const failing = files
|
|
215
|
+
.filter(({ analysis }) => (analysis.codeSmells.fileNamingViolations ?? []).length > 0)
|
|
216
|
+
.map(({ path, analysis }) => ({ path, value: analysis.codeSmells.fileNamingViolations.length }))
|
|
217
|
+
.sort((a, b) => b.value - a.value || a.path.localeCompare(b.path));
|
|
218
|
+
failures.push({
|
|
219
|
+
rule: 'naming-violations',
|
|
220
|
+
threshold: config.maxNamingViolations,
|
|
221
|
+
actual: totalNamingViolations,
|
|
222
|
+
files: failing,
|
|
223
|
+
message: `Found ${totalNamingViolations} file naming violation${totalNamingViolations !== 1 ? 's' : ''} (maximum ${config.maxNamingViolations})`,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
passed: failures.length === 0,
|
|
228
|
+
failures,
|
|
229
|
+
summary: {
|
|
230
|
+
workspaceScore,
|
|
231
|
+
filesAnalysed: files.length,
|
|
232
|
+
totalSecrets,
|
|
233
|
+
maxComplexity,
|
|
234
|
+
maxFileLength,
|
|
235
|
+
totalSecuritySmells,
|
|
236
|
+
totalSuppressions,
|
|
237
|
+
totalDuplicateBlocks,
|
|
238
|
+
totalNamingViolations,
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=gate.js.map
|