@sarj/eslint-plugin 16.39.0 → 16.39.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/index.cjs +9 -4
- package/dist/index.js +9 -4
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3098,6 +3098,8 @@ var CONFIG = { path: "tsconfig.json", source: '{"compilerOptions":{"strict":true
|
|
|
3098
3098
|
var EXAMPLE = 'export {}; interface Animal {readonly name: string} interface Dog extends Animal {readonly breed: string} export type KeptDog = Dog; const dog: Dog = {name: "Ada", breed: "Collie"}; function label(value: Dog | Animal): string {return value.name;} label(dog);';
|
|
3099
3099
|
var NULLABLE_EXAMPLE = EXAMPLE.replace("readonly name: string", "readonly name: string | null").replace("return value.name;", "return value.name ?? '';");
|
|
3100
3100
|
var OPTIONAL_EXAMPLE = EXAMPLE.replace("readonly breed: string", "readonly breed?: string");
|
|
3101
|
+
var SCALAR_HELPER_EXAMPLE = EXAMPLE.replace("return value.name;", "return formatName(value.name);").replace("function label", "function formatName(value: string): string {return '[' + value + ']';} function label");
|
|
3102
|
+
var WHOLE_OBJECT_HELPER_EXAMPLE = EXAMPLE.replace("return value.name;", "return formatAnimal(value);").replace("function label", "function formatAnimal(value: Animal): string {return value.name;} function label");
|
|
3101
3103
|
var NO_REDUNDANT_CONSUMER_UNION_DOCUMENTATION = {
|
|
3102
3104
|
defaultLevel: "warning",
|
|
3103
3105
|
summary: "Keep the covering object contract in a private scalar consumer union.",
|
|
@@ -3107,12 +3109,15 @@ var NO_REDUNDANT_CONSUMER_UNION_DOCUMENTATION = {
|
|
|
3107
3109
|
autofix: "safe",
|
|
3108
3110
|
limitations: [
|
|
3109
3111
|
"Automatic fixes additionally require every existing identifier caller to be natively assignable to the original contract, without Any, unknown, error or unresolved types on the relevant surface. Comments inside the annotation and TypeScript suppression directives keep the finding manual. Removed named types/imports must retain an independent top-level exported type-alias or direct type-export read. Other removable annotations/assertions, the covering annotation and own-definition/self references cannot keep one another live; otherwise simultaneous fixes can introduce unused-type errors. The exact authored covering type and terminator token sequence are retained; edits that would merge distinct CR and LF terminators remain manual; no producer, call or function body is rewritten.",
|
|
3110
|
-
"Only an inline binary parameter union in a strict module's top-level private scalar consumer is supported. Exactly one parameter, an explicit broad primitive return annotation, a single
|
|
3112
|
+
"Only an inline binary parameter union in a strict module's top-level private scalar consumer is supported. Exactly one parameter, an explicit broad primitive return annotation, a single supported returned scalar expression and direct identifier-argument callers are required. Type queries or type-only exports of the callable, public APIs, callbacks, contextual signatures, overloads, generics, decorators, async functions, direct eval and fresh-object callers prevent findings.",
|
|
3113
|
+
"Scalar values may pass through one implemented same-file top-level function or raw const-arrow helper with an explicit scalar return annotation and the exact native resolved signature. Every argument must already satisfy the scalar read view; the containing source must pass the existing native checked-source proof. Imported, ambient, aliased, overloaded, generic or contextually typed helpers, member calls, optional calls, spread arguments and whole-object arguments remain unsupported. Helper permission does not add auxiliary consumer parameters or replace the required object-field read anchor. Helper bodies need not be pure because no call or body is rewritten.",
|
|
3111
3114
|
"Both alternatives must have complete ordinary finite structural surfaces consisting only of explicitly readonly broad string, number, boolean or bigint properties, optionally paired directly with null or undefined in an exact two-part union. Required and optional nullable fields may be read under the same native coverage proof. Every field must have one actual declaration origin throughout each alternative's inheritance/intersection graph. Shared ancestors retain that origin; repeated merged fields, explicit overrides and independently declared overlapping fields abstain, including valid compatible declarations. This excludes recovered conflicting properties without claiming whole-source health. Aliases, imports, re-exports and ordinary inheritance are resolved by the parser-owned TypeScript checker. Any, unknown, missing imports, open/generic/mapped/indexed types, classes, brands, literal discriminants, methods, properties with scalar type aliases, parenthesized or more complex property unions, callbacks and recursive or nested object fields are unsupported.",
|
|
3112
3115
|
"Reports proper native assignability, where covered is assignable to covering and the reverse is false, or mutual native assignability with a strict smaller declared covering surface and only optional covered-only members. Every covering member, including optional members, must exist in the covered type's enumerated declared surface. Otherwise an existing identifier may carry an incompatible extra field and be rejected after simplification; implicit Object members containing Any cannot replace explicit declared coverage or the native Optional requirement. The covering type must declare every read field and a read required field; an all-optional reduced contract can reject existing callers. Identical alternatives and primitive/top/bottom redundancy remain upstream-owned; equal-key and incomparable mutually assignable structures are excluded. The mutual case additionally requires inline contracts or locally bound interfaces/type aliases whose alias and heritage references can be traced through the parser-owned type scope. Merged interface fragments must all resolve to the reference's same native symbol; mixed exported/local recovery abstains. Imported, global and qualified-namespace references in that path abstain; proper one-way coverage retains its existing alias/import support.",
|
|
3113
|
-
"General union simplification can change Extract, conditional types, in-narrowing, fresh-literal checking and public presentation. Those contexts are excluded even when ordinary assignment succeeds. Flat destructuring counts every bound field as read; whole-object escape, mutation,
|
|
3116
|
+
"General union simplification can change Extract, conditional types, in-narrowing, fresh-literal checking and public presentation. Those contexts are excluded even when ordinary assignment succeeds. Flat destructuring counts every bound field as read; whole-object escape, mutation, unproved helper calls, closures, reflection, serialization, dynamic reads, implicit Object capabilities and JSX prevent findings. Imported Python protocol assignability is not approximated by this rule."
|
|
3114
3117
|
],
|
|
3115
3118
|
examples: [
|
|
3119
|
+
{ id: "local-scalar-helper", scenarioId: "local-scalar-helper", title: "A local helper receives only the required name", outcome: "match", files: [{ path: "src/case.ts", source: SCALAR_HELPER_EXAMPLE }, CONFIG], focusPath: "src/case.ts", expectedCount: 1, public: true, fixedFiles: [{ path: "src/case.ts", source: SCALAR_HELPER_EXAMPLE.replace("Dog | Animal", "Animal") }] },
|
|
3120
|
+
{ id: "whole-object-helper", scenarioId: "local-scalar-helper", title: "Passing the whole object remains outside the scalar helper proof", outcome: "no-match", files: [{ path: "src/case.ts", source: WHOLE_OBJECT_HELPER_EXAMPLE }, CONFIG], focusPath: "src/case.ts", expectedCount: 0, public: true },
|
|
3116
3121
|
{ id: "covered-alternative", title: "The private consumer needs the covering Animal contract", outcome: "match", files: [{ path: "src/case.ts", source: EXAMPLE }, CONFIG], focusPath: "src/case.ts", expectedCount: 1, public: true, fixedFiles: [{ path: "src/case.ts", source: EXAMPLE.replace("Dog | Animal", "Animal") }] },
|
|
3117
3122
|
{ id: "public-contract", title: "A public union retains its observable alternatives", outcome: "no-match", files: [{ path: "src/case.ts", source: EXAMPLE.replace("function label", "export function label") }, CONFIG], focusPath: "src/case.ts", expectedCount: 0, public: true },
|
|
3118
3123
|
{ id: "optional-covered-alternative", scenarioId: "optional-readonly-contract", title: "An unread optional breed retains the smaller Animal surface", outcome: "match", files: [{ path: "src/case.ts", source: OPTIONAL_EXAMPLE }, CONFIG], focusPath: "src/case.ts", expectedCount: 1, public: true, fixedFiles: [{ path: "src/case.ts", source: OPTIONAL_EXAMPLE.replace("Dog | Animal", "Animal") }] },
|
|
@@ -3237,7 +3242,7 @@ var no_redundant_consumer_union_default = createRule({
|
|
|
3237
3242
|
return surfaces.get(type) ?? null;
|
|
3238
3243
|
};
|
|
3239
3244
|
for (const candidate2 of candidates) {
|
|
3240
|
-
const consumer = privateScalarConsumer(candidate2, context.sourceCode, services, exports2);
|
|
3245
|
+
const consumer = privateScalarConsumer(candidate2, context.sourceCode, services, exports2, { scalarCalls: true });
|
|
3241
3246
|
const annotation = consumer?.parameter.type;
|
|
3242
3247
|
if (!consumer || !annotation || !import_typescript7.default.isUnionTypeNode(annotation) || annotation.types.length !== 2) continue;
|
|
3243
3248
|
const leftNode = annotation.types[0];
|
|
@@ -4415,7 +4420,7 @@ function isSearchParamReceiver(call, services) {
|
|
|
4415
4420
|
// package.json
|
|
4416
4421
|
var package_default = {
|
|
4417
4422
|
name: "@sarj/eslint-plugin",
|
|
4418
|
-
version: "16.39.
|
|
4423
|
+
version: "16.39.1",
|
|
4419
4424
|
packageManager: "npm@12.2.1",
|
|
4420
4425
|
description: "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
|
|
4421
4426
|
type: "module",
|
package/dist/index.js
CHANGED
|
@@ -3047,6 +3047,8 @@ var CONFIG = { path: "tsconfig.json", source: '{"compilerOptions":{"strict":true
|
|
|
3047
3047
|
var EXAMPLE = 'export {}; interface Animal {readonly name: string} interface Dog extends Animal {readonly breed: string} export type KeptDog = Dog; const dog: Dog = {name: "Ada", breed: "Collie"}; function label(value: Dog | Animal): string {return value.name;} label(dog);';
|
|
3048
3048
|
var NULLABLE_EXAMPLE = EXAMPLE.replace("readonly name: string", "readonly name: string | null").replace("return value.name;", "return value.name ?? '';");
|
|
3049
3049
|
var OPTIONAL_EXAMPLE = EXAMPLE.replace("readonly breed: string", "readonly breed?: string");
|
|
3050
|
+
var SCALAR_HELPER_EXAMPLE = EXAMPLE.replace("return value.name;", "return formatName(value.name);").replace("function label", "function formatName(value: string): string {return '[' + value + ']';} function label");
|
|
3051
|
+
var WHOLE_OBJECT_HELPER_EXAMPLE = EXAMPLE.replace("return value.name;", "return formatAnimal(value);").replace("function label", "function formatAnimal(value: Animal): string {return value.name;} function label");
|
|
3050
3052
|
var NO_REDUNDANT_CONSUMER_UNION_DOCUMENTATION = {
|
|
3051
3053
|
defaultLevel: "warning",
|
|
3052
3054
|
summary: "Keep the covering object contract in a private scalar consumer union.",
|
|
@@ -3056,12 +3058,15 @@ var NO_REDUNDANT_CONSUMER_UNION_DOCUMENTATION = {
|
|
|
3056
3058
|
autofix: "safe",
|
|
3057
3059
|
limitations: [
|
|
3058
3060
|
"Automatic fixes additionally require every existing identifier caller to be natively assignable to the original contract, without Any, unknown, error or unresolved types on the relevant surface. Comments inside the annotation and TypeScript suppression directives keep the finding manual. Removed named types/imports must retain an independent top-level exported type-alias or direct type-export read. Other removable annotations/assertions, the covering annotation and own-definition/self references cannot keep one another live; otherwise simultaneous fixes can introduce unused-type errors. The exact authored covering type and terminator token sequence are retained; edits that would merge distinct CR and LF terminators remain manual; no producer, call or function body is rewritten.",
|
|
3059
|
-
"Only an inline binary parameter union in a strict module's top-level private scalar consumer is supported. Exactly one parameter, an explicit broad primitive return annotation, a single
|
|
3061
|
+
"Only an inline binary parameter union in a strict module's top-level private scalar consumer is supported. Exactly one parameter, an explicit broad primitive return annotation, a single supported returned scalar expression and direct identifier-argument callers are required. Type queries or type-only exports of the callable, public APIs, callbacks, contextual signatures, overloads, generics, decorators, async functions, direct eval and fresh-object callers prevent findings.",
|
|
3062
|
+
"Scalar values may pass through one implemented same-file top-level function or raw const-arrow helper with an explicit scalar return annotation and the exact native resolved signature. Every argument must already satisfy the scalar read view; the containing source must pass the existing native checked-source proof. Imported, ambient, aliased, overloaded, generic or contextually typed helpers, member calls, optional calls, spread arguments and whole-object arguments remain unsupported. Helper permission does not add auxiliary consumer parameters or replace the required object-field read anchor. Helper bodies need not be pure because no call or body is rewritten.",
|
|
3060
3063
|
"Both alternatives must have complete ordinary finite structural surfaces consisting only of explicitly readonly broad string, number, boolean or bigint properties, optionally paired directly with null or undefined in an exact two-part union. Required and optional nullable fields may be read under the same native coverage proof. Every field must have one actual declaration origin throughout each alternative's inheritance/intersection graph. Shared ancestors retain that origin; repeated merged fields, explicit overrides and independently declared overlapping fields abstain, including valid compatible declarations. This excludes recovered conflicting properties without claiming whole-source health. Aliases, imports, re-exports and ordinary inheritance are resolved by the parser-owned TypeScript checker. Any, unknown, missing imports, open/generic/mapped/indexed types, classes, brands, literal discriminants, methods, properties with scalar type aliases, parenthesized or more complex property unions, callbacks and recursive or nested object fields are unsupported.",
|
|
3061
3064
|
"Reports proper native assignability, where covered is assignable to covering and the reverse is false, or mutual native assignability with a strict smaller declared covering surface and only optional covered-only members. Every covering member, including optional members, must exist in the covered type's enumerated declared surface. Otherwise an existing identifier may carry an incompatible extra field and be rejected after simplification; implicit Object members containing Any cannot replace explicit declared coverage or the native Optional requirement. The covering type must declare every read field and a read required field; an all-optional reduced contract can reject existing callers. Identical alternatives and primitive/top/bottom redundancy remain upstream-owned; equal-key and incomparable mutually assignable structures are excluded. The mutual case additionally requires inline contracts or locally bound interfaces/type aliases whose alias and heritage references can be traced through the parser-owned type scope. Merged interface fragments must all resolve to the reference's same native symbol; mixed exported/local recovery abstains. Imported, global and qualified-namespace references in that path abstain; proper one-way coverage retains its existing alias/import support.",
|
|
3062
|
-
"General union simplification can change Extract, conditional types, in-narrowing, fresh-literal checking and public presentation. Those contexts are excluded even when ordinary assignment succeeds. Flat destructuring counts every bound field as read; whole-object escape, mutation,
|
|
3065
|
+
"General union simplification can change Extract, conditional types, in-narrowing, fresh-literal checking and public presentation. Those contexts are excluded even when ordinary assignment succeeds. Flat destructuring counts every bound field as read; whole-object escape, mutation, unproved helper calls, closures, reflection, serialization, dynamic reads, implicit Object capabilities and JSX prevent findings. Imported Python protocol assignability is not approximated by this rule."
|
|
3063
3066
|
],
|
|
3064
3067
|
examples: [
|
|
3068
|
+
{ id: "local-scalar-helper", scenarioId: "local-scalar-helper", title: "A local helper receives only the required name", outcome: "match", files: [{ path: "src/case.ts", source: SCALAR_HELPER_EXAMPLE }, CONFIG], focusPath: "src/case.ts", expectedCount: 1, public: true, fixedFiles: [{ path: "src/case.ts", source: SCALAR_HELPER_EXAMPLE.replace("Dog | Animal", "Animal") }] },
|
|
3069
|
+
{ id: "whole-object-helper", scenarioId: "local-scalar-helper", title: "Passing the whole object remains outside the scalar helper proof", outcome: "no-match", files: [{ path: "src/case.ts", source: WHOLE_OBJECT_HELPER_EXAMPLE }, CONFIG], focusPath: "src/case.ts", expectedCount: 0, public: true },
|
|
3065
3070
|
{ id: "covered-alternative", title: "The private consumer needs the covering Animal contract", outcome: "match", files: [{ path: "src/case.ts", source: EXAMPLE }, CONFIG], focusPath: "src/case.ts", expectedCount: 1, public: true, fixedFiles: [{ path: "src/case.ts", source: EXAMPLE.replace("Dog | Animal", "Animal") }] },
|
|
3066
3071
|
{ id: "public-contract", title: "A public union retains its observable alternatives", outcome: "no-match", files: [{ path: "src/case.ts", source: EXAMPLE.replace("function label", "export function label") }, CONFIG], focusPath: "src/case.ts", expectedCount: 0, public: true },
|
|
3067
3072
|
{ id: "optional-covered-alternative", scenarioId: "optional-readonly-contract", title: "An unread optional breed retains the smaller Animal surface", outcome: "match", files: [{ path: "src/case.ts", source: OPTIONAL_EXAMPLE }, CONFIG], focusPath: "src/case.ts", expectedCount: 1, public: true, fixedFiles: [{ path: "src/case.ts", source: OPTIONAL_EXAMPLE.replace("Dog | Animal", "Animal") }] },
|
|
@@ -3186,7 +3191,7 @@ var no_redundant_consumer_union_default = createRule({
|
|
|
3186
3191
|
return surfaces.get(type) ?? null;
|
|
3187
3192
|
};
|
|
3188
3193
|
for (const candidate2 of candidates) {
|
|
3189
|
-
const consumer = privateScalarConsumer(candidate2, context.sourceCode, services, exports);
|
|
3194
|
+
const consumer = privateScalarConsumer(candidate2, context.sourceCode, services, exports, { scalarCalls: true });
|
|
3190
3195
|
const annotation = consumer?.parameter.type;
|
|
3191
3196
|
if (!consumer || !annotation || !ts7.isUnionTypeNode(annotation) || annotation.types.length !== 2) continue;
|
|
3192
3197
|
const leftNode = annotation.types[0];
|
|
@@ -4368,7 +4373,7 @@ function isSearchParamReceiver(call, services) {
|
|
|
4368
4373
|
// package.json
|
|
4369
4374
|
var package_default = {
|
|
4370
4375
|
name: "@sarj/eslint-plugin",
|
|
4371
|
-
version: "16.39.
|
|
4376
|
+
version: "16.39.1",
|
|
4372
4377
|
packageManager: "npm@12.2.1",
|
|
4373
4378
|
description: "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
|
|
4374
4379
|
type: "module",
|