@sarj/eslint-plugin 19.10.0 → 19.14.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/catalog.js +63 -3
- package/dist/{chunk-ZWOGM4CV.js → chunk-YWEV6GQM.js} +57 -25
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/catalog.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
publicDocumentation,
|
|
3
3
|
ruleSpec,
|
|
4
4
|
src_default
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-YWEV6GQM.js";
|
|
6
6
|
|
|
7
7
|
// src/docs/directive-disable-enable-pair.ts
|
|
8
8
|
var DIRECTIVE_DISABLE_ENABLE_PAIR_DOCUMENTATION = {
|
|
@@ -4163,9 +4163,9 @@ var PREFER_CODED_ERROR_RESULT_DOCUMENTATION = {
|
|
|
4163
4163
|
"Checks source-resolved local unions with required literal ok/success booleans, or status/state/kind/type/tag values ok/success versus err/error/failure/failed (case insensitive). Conflicting outcome tags abstain. A shared outcome tag does not also count as a domain error code. Named and namespace neverthrow Result/ResultAsync imports establish their generic error position.",
|
|
4164
4164
|
"Presence-selected unions require an error property on every failure branch and the same single required non-control value property on every success branch. Optional error/value fields, multiple value keys, loading states and error branches carrying the success value abstain. Required errorMessage and Error-suffixed failure channels are also reviewed; multiple custom channels abstain and a primary error payload retains precedence.",
|
|
4165
4165
|
"Broad string and unknown error channels, unshadowed native Error-family types and open string/unknown code fields receive review. Explicit Any stays with the existing no-explicit-any rule. Finite literal codes need no invented payload. Imported domain errors, validation-library errors, generic error parameters, ordinary formatters, legitimate absence and redacted failure states remain valid.",
|
|
4166
|
-
"Local aliases, unmerged interfaces without inheritance and generic substitutions are bounded to 16 resolution levels, 64 union members and 256 traversal steps. Imported local declarations, conditional/intersection/mapped types and unresolved contracts abstain. Optional discriminants, independent success/error payloads and partial-data failures are excluded.",
|
|
4166
|
+
"Local aliases, unmerged interfaces without inheritance and generic substitutions are bounded to 16 resolution levels, 64 union members and 256 traversal steps. Transparent aliases retain the proven neverthrow Result/ResultAsync error position, including renamed/namespace imports, generic defaults and unshadowed native Promise/PromiseLike wrappers. Imported local declarations, conditional/intersection/mapped types and unresolved contracts abstain. Optional discriminants, independent success/error payloads and partial-data failures are excluded.",
|
|
4167
4167
|
"Required code/kind/type/tag fields of unresolved or imported type defer to the type checker; a finite code does not prove that its cases or payloads are useful. Generic error parameters are retained rather than guessed. Ordinary string formatters, absence, validation-library errors and redacted failure states without a string payload are not flagged.",
|
|
4168
|
-
"A directly returned failure object, neverthrow err/errAsync call or new Err with an interpolated template or proven string concatenation receives review. Without a code, the diagnostic asks for a domain contract; with a literal code, it asks for consumer formatting. Literal-only compositions, including earlier stable local constants, remain valid codes. Nested plain error objects, expression-bodied arrows and
|
|
4168
|
+
"A directly returned failure object, neverthrow err/errAsync call or new Err with an interpolated template or proven string concatenation receives review. Without a code, the diagnostic asks for a domain contract; with a literal code, it asks for consumer formatting. Literal-only compositions, including earlier stable local constants, remain valid codes. Nested plain error objects, expression-bodied arrows and awaited returns are supported. Returned conditional branches are followed without visiting their conditions, call arguments or containers; logical/sequence expressions and arbitrary aliases abstain. The entire return expression abstains beyond 16 levels, 64 leaves or 256 traversal steps. Import identity and value-space shadowing are checked; spreads, accessors, computed keys, helper formatting calls and nonliteral codes abstain. This proves formatting ownership, not which context the domain must expose; presentation adapters remain a reviewed exception.",
|
|
4169
4169
|
"This warning requests domain review: an infrastructure adapter may intentionally retain unknown/Error, and a final presentation boundary may accept every failure and render one message. Neither proves a recoverable domain rejection. No fix invents error codes, fields, handlers or changes serialization, exception unwinding, transactions, retries or logging."
|
|
4170
4170
|
],
|
|
4171
4171
|
references: [
|
|
@@ -4173,6 +4173,66 @@ var PREFER_CODED_ERROR_RESULT_DOCUMENTATION = {
|
|
|
4173
4173
|
"https://github.com/supermacro/neverthrow"
|
|
4174
4174
|
],
|
|
4175
4175
|
examples: [
|
|
4176
|
+
{
|
|
4177
|
+
id: "conditional-message-failure",
|
|
4178
|
+
scenarioId: "conditional-result-producer",
|
|
4179
|
+
title: "A conditional return still needs a useful error contract",
|
|
4180
|
+
outcome: "match",
|
|
4181
|
+
files: [
|
|
4182
|
+
{
|
|
4183
|
+
path: "app/result.ts",
|
|
4184
|
+
source: "import { err, ok } from 'neverthrow'; export const save = (name: string, allowed: boolean) => allowed ? ok(name) : err(`Denied ${name}`);"
|
|
4185
|
+
}
|
|
4186
|
+
],
|
|
4187
|
+
focusPath: "app/result.ts",
|
|
4188
|
+
expectedCount: 1,
|
|
4189
|
+
public: true
|
|
4190
|
+
},
|
|
4191
|
+
{
|
|
4192
|
+
id: "conditional-coded-failure",
|
|
4193
|
+
scenarioId: "conditional-result-producer",
|
|
4194
|
+
title: "Preserve the code and context and let the consumer format",
|
|
4195
|
+
outcome: "no-match",
|
|
4196
|
+
files: [
|
|
4197
|
+
{
|
|
4198
|
+
path: "app/result.ts",
|
|
4199
|
+
source: "import { err, ok, type Result } from 'neverthrow'; type Denied = { code: 'denied'; name: string }; export function save(name: string, allowed: boolean): Result<string, Denied> { return allowed ? ok(name) : err({ code: 'denied', name }); } export function format(error: Denied): string { return `Denied ${error.name}`; }"
|
|
4200
|
+
}
|
|
4201
|
+
],
|
|
4202
|
+
focusPath: "app/result.ts",
|
|
4203
|
+
expectedCount: 0,
|
|
4204
|
+
public: true
|
|
4205
|
+
},
|
|
4206
|
+
{
|
|
4207
|
+
id: "aliased-library-message",
|
|
4208
|
+
scenarioId: "aliased-library-error",
|
|
4209
|
+
title: "A transparent alias must preserve the library error contract",
|
|
4210
|
+
outcome: "match",
|
|
4211
|
+
files: [
|
|
4212
|
+
{
|
|
4213
|
+
path: "app/result.ts",
|
|
4214
|
+
source: "import type { Result as NativeResult } from 'neverthrow'; type Saved<T, E> = Promise<NativeResult<T, E>>; export type Output = Saved<number, string>;"
|
|
4215
|
+
}
|
|
4216
|
+
],
|
|
4217
|
+
focusPath: "app/result.ts",
|
|
4218
|
+
expectedCount: 1,
|
|
4219
|
+
public: true
|
|
4220
|
+
},
|
|
4221
|
+
{
|
|
4222
|
+
id: "aliased-library-domain-error",
|
|
4223
|
+
scenarioId: "aliased-library-error",
|
|
4224
|
+
title: "Keep a finite error code and only the context its consumer needs",
|
|
4225
|
+
outcome: "no-match",
|
|
4226
|
+
files: [
|
|
4227
|
+
{
|
|
4228
|
+
path: "app/result.ts",
|
|
4229
|
+
source: "import type { Result as NativeResult } from 'neverthrow'; type Saved<T, E> = Promise<NativeResult<T, E>>; export type Output = Saved<number, { code: 'duplicate'; name: string } | { code: 'denied' }>;"
|
|
4230
|
+
}
|
|
4231
|
+
],
|
|
4232
|
+
focusPath: "app/result.ts",
|
|
4233
|
+
expectedCount: 0,
|
|
4234
|
+
public: true
|
|
4235
|
+
},
|
|
4176
4236
|
{
|
|
4177
4237
|
id: "presence-selected-message",
|
|
4178
4238
|
scenarioId: "presence-selected-result",
|
|
@@ -21588,14 +21588,15 @@ function resolve2(value, source, depth = 0) {
|
|
|
21588
21588
|
if (depth >= MAX_DEPTH) return null;
|
|
21589
21589
|
const node = value.node;
|
|
21590
21590
|
if (node.type !== "TSTypeReference") return value;
|
|
21591
|
-
if (node.typeName.type !== "Identifier")
|
|
21591
|
+
if (node.typeName.type !== "Identifier")
|
|
21592
|
+
return libraryResult(node, source) ? value : null;
|
|
21592
21593
|
const variable = typeBinding(source, node.typeName);
|
|
21593
21594
|
if (variable === null || variable.defs.length === 0) return value;
|
|
21594
21595
|
const bound = value.bindings.get(variable);
|
|
21595
21596
|
if (bound !== void 0) return resolve2(bound, source, depth + 1);
|
|
21596
21597
|
const declaration = variable.defs.length === 1 ? variable.defs[0]?.node : void 0;
|
|
21597
21598
|
if (declaration?.type !== "TSTypeAliasDeclaration" && declaration?.type !== "TSInterfaceDeclaration")
|
|
21598
|
-
return null;
|
|
21599
|
+
return libraryResult(node, source) ? value : null;
|
|
21599
21600
|
if (declaration.type === "TSInterfaceDeclaration" && declaration.extends.length !== 0)
|
|
21600
21601
|
return null;
|
|
21601
21602
|
const bindings = bindArguments(declaration, node, value, source);
|
|
@@ -21761,6 +21762,26 @@ function openStringField(fields2, owner, source, names, budget) {
|
|
|
21761
21762
|
}
|
|
21762
21763
|
return null;
|
|
21763
21764
|
}
|
|
21765
|
+
function outcomeFailures(value, source, depth = 0, budget = { remaining: MAX_TYPE_STEPS }) {
|
|
21766
|
+
if (depth >= MAX_DEPTH || --budget.remaining < 0) return [];
|
|
21767
|
+
const resolved = resolve2(value, source);
|
|
21768
|
+
if (resolved === null) return [];
|
|
21769
|
+
const structural = failureMessages(resolved, source);
|
|
21770
|
+
if (structural.length !== 0) return structural;
|
|
21771
|
+
const library = libraryError(resolved, source);
|
|
21772
|
+
if (library !== null) return [library];
|
|
21773
|
+
const node = resolved.node;
|
|
21774
|
+
if (node.type !== "TSTypeReference") return [];
|
|
21775
|
+
if (node.typeName.type !== "Identifier" || !["Promise", "PromiseLike"].includes(node.typeName.name) || (typeBinding(source, node.typeName)?.defs.length ?? 0) !== 0 || node.typeArguments?.params.length !== 1)
|
|
21776
|
+
return [];
|
|
21777
|
+
const argument = node.typeArguments.params[0];
|
|
21778
|
+
return argument === void 0 ? [] : outcomeFailures(
|
|
21779
|
+
{ ...resolved, node: argument, origin: argument },
|
|
21780
|
+
source,
|
|
21781
|
+
depth + 1,
|
|
21782
|
+
budget
|
|
21783
|
+
);
|
|
21784
|
+
}
|
|
21764
21785
|
function failureMessages(value, source) {
|
|
21765
21786
|
const members2 = variants(value, source);
|
|
21766
21787
|
if (members2 === null || members2.length < 2) return [];
|
|
@@ -21853,14 +21874,18 @@ function variants(value, source, depth = 0, budget = { remaining: MAX_TYPE_STEPS
|
|
|
21853
21874
|
}
|
|
21854
21875
|
return result;
|
|
21855
21876
|
}
|
|
21856
|
-
function libraryError(
|
|
21857
|
-
|
|
21858
|
-
if (
|
|
21859
|
-
libraryName(reference.typeName, source, true) ?? ""
|
|
21860
|
-
))
|
|
21877
|
+
function libraryError(value, source) {
|
|
21878
|
+
const reference = value.node;
|
|
21879
|
+
if (reference.type !== "TSTypeReference" || !libraryResult(reference, source))
|
|
21861
21880
|
return null;
|
|
21881
|
+
if (reference.typeArguments?.params.length !== 2) return null;
|
|
21862
21882
|
const error = reference.typeArguments.params[1];
|
|
21863
|
-
return error === void 0 ? null : weakError(
|
|
21883
|
+
return error === void 0 ? null : weakError({ ...value, node: error, origin: error }, source);
|
|
21884
|
+
}
|
|
21885
|
+
function libraryResult(reference, source) {
|
|
21886
|
+
return ["Result", "ResultAsync"].includes(
|
|
21887
|
+
libraryName(reference.typeName, source, true) ?? ""
|
|
21888
|
+
);
|
|
21864
21889
|
}
|
|
21865
21890
|
function libraryName(node, source, typeSpace) {
|
|
21866
21891
|
const reference = libraryReference(node);
|
|
@@ -21886,15 +21911,18 @@ function libraryReference(node) {
|
|
|
21886
21911
|
return { root: node.object, member: node.property.name };
|
|
21887
21912
|
return null;
|
|
21888
21913
|
}
|
|
21889
|
-
function
|
|
21890
|
-
if (depth >= MAX_DEPTH || --budget.remaining < 0) return
|
|
21891
|
-
|
|
21892
|
-
if (
|
|
21893
|
-
|
|
21894
|
-
if (
|
|
21895
|
-
|
|
21896
|
-
|
|
21897
|
-
|
|
21914
|
+
function returnedValues(node, depth = 0, budget = { remaining: MAX_TYPE_STEPS }) {
|
|
21915
|
+
if (depth >= MAX_DEPTH || --budget.remaining < 0) return null;
|
|
21916
|
+
const value = unwrapExpression(node);
|
|
21917
|
+
if (value.type === "AwaitExpression")
|
|
21918
|
+
return returnedValues(value.argument, depth + 1, budget);
|
|
21919
|
+
if (value.type !== "ConditionalExpression") return [value];
|
|
21920
|
+
const consequent = returnedValues(value.consequent, depth + 1, budget);
|
|
21921
|
+
if (consequent === null) return null;
|
|
21922
|
+
const alternate = returnedValues(value.alternate, depth + 1, budget);
|
|
21923
|
+
if (alternate === null || consequent.length + alternate.length > MAX_VARIANTS)
|
|
21924
|
+
return null;
|
|
21925
|
+
return [...consequent, ...alternate];
|
|
21898
21926
|
}
|
|
21899
21927
|
function renderedFailure(node, source) {
|
|
21900
21928
|
let value = unwrapExpression(node);
|
|
@@ -22017,9 +22045,7 @@ var prefer_coded_error_result_default = defineRule({
|
|
|
22017
22045
|
createOnce(context) {
|
|
22018
22046
|
const reported = /* @__PURE__ */ new Set();
|
|
22019
22047
|
function check(node) {
|
|
22020
|
-
const
|
|
22021
|
-
const library = node.type === "TSTypeReference" ? libraryError(node, context.sourceCode) : null;
|
|
22022
|
-
for (const candidate2 of library === null ? candidates2 : [...candidates2, library]) {
|
|
22048
|
+
for (const candidate2 of outcomeFailures(view(node), context.sourceCode)) {
|
|
22023
22049
|
if (reported.has(candidate2)) continue;
|
|
22024
22050
|
reported.add(candidate2);
|
|
22025
22051
|
context.report({
|
|
@@ -22030,13 +22056,19 @@ var prefer_coded_error_result_default = defineRule({
|
|
|
22030
22056
|
}
|
|
22031
22057
|
function checkProducer(owner, value) {
|
|
22032
22058
|
if (owner === null) return;
|
|
22033
|
-
const
|
|
22034
|
-
|
|
22059
|
+
const issues = (returnedValues(value) ?? []).flatMap((leaf) => {
|
|
22060
|
+
const issue = renderedFailure(leaf, context.sourceCode);
|
|
22061
|
+
return issue === null ? [] : [issue];
|
|
22062
|
+
});
|
|
22063
|
+
if (issues.length === 0) return;
|
|
22035
22064
|
const contract = owner.returnType?.typeAnnotation;
|
|
22036
|
-
if (contract !== void 0 &&
|
|
22065
|
+
if (contract !== void 0 && outcomeFailures(view(contract), context.sourceCode).length !== 0)
|
|
22037
22066
|
return;
|
|
22038
|
-
|
|
22039
|
-
|
|
22067
|
+
for (const issue of issues) {
|
|
22068
|
+
if (reported.has(issue.node)) continue;
|
|
22069
|
+
reported.add(issue.node);
|
|
22070
|
+
context.report(issue);
|
|
22071
|
+
}
|
|
22040
22072
|
}
|
|
22041
22073
|
return {
|
|
22042
22074
|
before() {
|
package/dist/index.js
CHANGED