@rigour-labs/core 6.10.0 → 6.10.1
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/context.js
CHANGED
|
@@ -23,7 +23,7 @@ export class ContextGate extends Gate {
|
|
|
23
23
|
return [];
|
|
24
24
|
const files = await FileScanner.findFiles({
|
|
25
25
|
cwd: context.cwd,
|
|
26
|
-
patterns: ['**/*.{ts,js,tsx,jsx,py,go,java,cs,rb,kt,swift,rs,php}'],
|
|
26
|
+
patterns: ['**/*.{ts,js,tsx,jsx,py,go,java,cs,rb,kt,scala,swift,rs,php}'],
|
|
27
27
|
ignore: [...(context.ignore || []), '**/node_modules/**', '**/dist/**', '**/build/**',
|
|
28
28
|
'**/studio-dist/**', '**/.next/**', '**/coverage/**', '**/out/**',
|
|
29
29
|
'**/*.test.*', '**/*.spec.*', '**/examples/**', '**/docs/**'],
|
|
@@ -93,10 +93,10 @@ export class ContextGate extends Gate {
|
|
|
93
93
|
const casing = this.detectCasing(match[1]);
|
|
94
94
|
this.addPattern(patterns, 'function', { casing, file, count: 1 });
|
|
95
95
|
}
|
|
96
|
-
// Class declarations: `class` opening a line (after modifiers: C# partial, Kotlin enum/inner/value/annotation
|
|
97
|
-
// and the rest), its name on the same line. Not the end of
|
|
96
|
+
// Class declarations: `class` opening a line (after modifiers: C# partial, Kotlin enum/inner/value/annotation,
|
|
97
|
+
// Scala case/implicit and a qualified `private[pkg]`, and the rest), its name on the same line. Not the end of
|
|
98
98
|
// `dataclass`, and not the word in a comment or a string ("# this class of errors").
|
|
99
|
-
const classMatches = content.matchAll(/^[ \t]*(?:(?:export|default|abstract|public|private|protected|internal|final|sealed|static|data|open|partial|enum|inner|value|annotation)[ \t]+)*class[ \t]+([A-Za-z_$][A-Za-z0-9_$]*)/gm);
|
|
99
|
+
const classMatches = content.matchAll(/^[ \t]*(?:(?:export|default|abstract|public|private|protected|internal|final|sealed|static|data|open|partial|enum|inner|value|annotation|case|implicit)(?:\[[\w.]+\])?[ \t]+)*class[ \t]+([A-Za-z_$][A-Za-z0-9_$]*)/gm);
|
|
100
100
|
for (const match of classMatches) {
|
|
101
101
|
const casing = this.detectCasing(match[1]);
|
|
102
102
|
this.addPattern(patterns, 'class', { casing, file, count: 1 });
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* equals on `col` (SQL excludes NULL there);
|
|
6
6
|
* nullable-filtered-column the query filters `col` non-null, the row type still says `| null`,
|
|
7
7
|
* so every guard on it is dead;
|
|
8
|
-
* optional-always-supplied an optional property every host object supplies
|
|
8
|
+
* optional-always-supplied an optional property every host object supplies (unless values of the type are
|
|
9
|
+
* also read back from JSON, where data written before the member existed lacks it);
|
|
9
10
|
* write-only-property a property the hosts set and nothing reads. When the value only
|
|
10
11
|
* leaves through serialisation to a callee the program does not declare
|
|
11
12
|
* (a wire payload), that is a hint for the reviewer, not a block.
|
|
@@ -263,6 +264,53 @@ function redundancyFailures(typed, changedLines, config) {
|
|
|
263
264
|
escapeMemo.set(t.sym, found);
|
|
264
265
|
return found;
|
|
265
266
|
};
|
|
267
|
+
// Values of the type read back from JSON (JSON.parse, readJson, a response's .json()) as the type: an assertion,
|
|
268
|
+
// an annotated variable, a type argument, or the declared return of the function that returns them. Data written
|
|
269
|
+
// before a member existed lacks it, so the member stays optional however every host in the code supplies it now.
|
|
270
|
+
const holdsType = (t, ty) => {
|
|
271
|
+
if (!ty)
|
|
272
|
+
return false;
|
|
273
|
+
const awaited = checker.getAwaitedType(ty) ?? ty;
|
|
274
|
+
return mentionsType(t, awaited) || checker.getIndexInfosOfType(awaited).some(info => mentionsType(t, info.type));
|
|
275
|
+
};
|
|
276
|
+
const JSON_READ = /(?:^|\.)(?:JSON\.parse|readJson|readJsonSync|json)$/;
|
|
277
|
+
const persistedMemo = new Map();
|
|
278
|
+
const readFromJson = (t) => {
|
|
279
|
+
if (persistedMemo.has(t.sym))
|
|
280
|
+
return persistedMemo.get(t.sym);
|
|
281
|
+
let found;
|
|
282
|
+
for (const sf of typed.appSources) {
|
|
283
|
+
if (found)
|
|
284
|
+
break;
|
|
285
|
+
if (!/JSON\.parse|readJson|\.json\(/.test(sf.text))
|
|
286
|
+
continue;
|
|
287
|
+
walk(sf, node => {
|
|
288
|
+
if (found || !ts.isCallExpression(node) || !JSON_READ.test(node.expression.getText()))
|
|
289
|
+
return;
|
|
290
|
+
if (node.typeArguments?.some(a => holdsType(t, checker.getTypeFromTypeNode(a)))) {
|
|
291
|
+
found = at(node);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
let site = node;
|
|
295
|
+
while (ts.isParenthesizedExpression(site.parent) || ts.isAwaitExpression(site.parent) || ts.isNonNullExpression(site.parent))
|
|
296
|
+
site = site.parent;
|
|
297
|
+
const p = site.parent;
|
|
298
|
+
const target = ts.isAsExpression(p) || ts.isTypeAssertionExpression(p) || ts.isSatisfiesExpression(p) ? p.type
|
|
299
|
+
: ts.isVariableDeclaration(p) && p.initializer === site ? p.type
|
|
300
|
+
: undefined;
|
|
301
|
+
if (target && holdsType(t, checker.getTypeFromTypeNode(target))) {
|
|
302
|
+
found = at(node);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
const fn = ts.isReturnStatement(p) ? ts.findAncestor(p, ts.isFunctionLike) : ts.isArrowFunction(p) && p.body === site ? p : undefined;
|
|
306
|
+
const signature = fn && checker.getSignatureFromDeclaration(fn);
|
|
307
|
+
if (fn?.type && signature && holdsType(t, checker.getReturnTypeOfSignature(signature)))
|
|
308
|
+
found = at(node);
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
persistedMemo.set(t.sym, found);
|
|
312
|
+
return found;
|
|
313
|
+
};
|
|
266
314
|
// A property is read when any non-test file accesses or destructures it; a spread only passes it on.
|
|
267
315
|
const isWriteTarget = (access) => ts.isBinaryExpression(access.parent) && access.parent.left === access && access.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken;
|
|
268
316
|
const isRead = (t, name) => {
|
|
@@ -317,7 +365,10 @@ function redundancyFailures(typed, changedLines, config) {
|
|
|
317
365
|
const all = hosts.length + outside.length;
|
|
318
366
|
if (all > 0 && supplied.length === hosts.length && outside.every(s => supplyRe(name).test(s.text))) {
|
|
319
367
|
const text = `${member} is optional but every host supplies it (${all} host(s), e.g. ${hosts[0] ? at(hosts[0]) : outside[0].file}).`;
|
|
320
|
-
|
|
368
|
+
const stored = readFromJson(t);
|
|
369
|
+
if (stored)
|
|
370
|
+
hints.push(`optional-always-supplied ${at(m)}: ${text} Values of it are read back from JSON at ${stored}, where data written before the member existed lacks it: keep it optional unless that data is migrated`);
|
|
371
|
+
else if (published)
|
|
321
372
|
publish('optional-always-supplied', m, text);
|
|
322
373
|
else
|
|
323
374
|
report('optional-always-supplied', 'Optional member every host supplies', anchor, text, 'Make it required and drop the `?.` and the fallbacks on it.');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rigour-labs/core",
|
|
3
|
-
"version": "6.10.
|
|
3
|
+
"version": "6.10.1",
|
|
4
4
|
"description": "Rigour's review engine: deterministic gates on changed lines, rules and lessons learned from your team's fixes, and per-check precision from what you fix versus dismiss, across TypeScript, JavaScript, Python, Go, Ruby and C#.",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22.13"
|
|
@@ -75,11 +75,11 @@
|
|
|
75
75
|
"@anthropic-ai/sdk": "^0.132.1",
|
|
76
76
|
"pg": "^8.16.3",
|
|
77
77
|
"openai": "^5.23.2",
|
|
78
|
-
"@rigour-labs/brain-darwin-
|
|
79
|
-
"@rigour-labs/brain-
|
|
80
|
-
"@rigour-labs/brain-
|
|
81
|
-
"@rigour-labs/brain-
|
|
82
|
-
"@rigour-labs/brain-
|
|
78
|
+
"@rigour-labs/brain-darwin-arm64": "6.10.1",
|
|
79
|
+
"@rigour-labs/brain-linux-arm64": "6.10.1",
|
|
80
|
+
"@rigour-labs/brain-win-x64": "6.10.1",
|
|
81
|
+
"@rigour-labs/brain-darwin-x64": "6.10.1",
|
|
82
|
+
"@rigour-labs/brain-linux-x64": "6.10.1"
|
|
83
83
|
},
|
|
84
84
|
"devDependencies": {
|
|
85
85
|
"@types/fs-extra": "^11.0.4",
|