@sarj/eslint-plugin 19.18.0 → 19.19.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 +53 -1
- package/dist/{chunk-E2O4LN7G.js → chunk-UI4ADSAA.js} +112 -54
- 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-UI4ADSAA.js";
|
|
6
6
|
|
|
7
7
|
// src/docs/directive-disable-enable-pair.ts
|
|
8
8
|
var DIRECTIVE_DISABLE_ENABLE_PAIR_DOCUMENTATION = {
|
|
@@ -6441,6 +6441,57 @@ var PRODUCTION_DERIVED_TEST_CASES_DOCUMENTATION = {
|
|
|
6441
6441
|
]
|
|
6442
6442
|
};
|
|
6443
6443
|
|
|
6444
|
+
// src/docs/react-prefer-updater-function.ts
|
|
6445
|
+
var CART = "import { useState } from 'react'; export function Cart({ load }: { load: () => Promise<string> }) { const [items, setItems] = useState<string[]>([]); const add = async () => { const item = await load(); ";
|
|
6446
|
+
var CART_END = ' }; return <button type="button" onClick={add}>{items.length}</button>; }';
|
|
6447
|
+
var RENDER_PHASE = "import { useState } from 'react'; export function Panel({ request }: { request: boolean }) { const [dialog, setDialog] = useState({ open: request, request }); if (dialog.request !== request) { setDialog({ open: request || dialog.open, request }); } return <p>{String(dialog.open)}</p>; }";
|
|
6448
|
+
var REACT_PREFER_UPDATER_FUNCTION_DOCUMENTATION = {
|
|
6449
|
+
defaultLevel: "warning",
|
|
6450
|
+
summary: "Use the updater form when the next React state is built from the current state.",
|
|
6451
|
+
rationale: "A state variable holds the snapshot from the render that created the callback. After an await, in a timer, promise callback, subscription or effect, or when two updates run before React re-renders, that snapshot is stale. `setItems([...items, item])` then drops whatever another update added in the meantime. An updater receives the latest queued state, so each update builds on the one before it.",
|
|
6452
|
+
remediation: "Pass an updater instead. Rewrite `setItems([...items, item])` as `setItems((previous) => [...previous, item])`, and read `previous` wherever the argument read the state or a local derived from it. Compute IDs, timestamps and other impure values before the call, because React runs updaters twice in Strict Mode development builds.",
|
|
6453
|
+
category: "correctness",
|
|
6454
|
+
limitations: [
|
|
6455
|
+
"The rule is advisory and has no automatic fix. In a synchronous event handler that updates once, the snapshot is still current, so the rewrite keeps today's behavior and protects the handler against a later await or a second update.",
|
|
6456
|
+
"The rule pairs a setter with its state through one array destructure of a React useState call, including renamed, default and namespace React imports, and resolves every read by scope. It ignores setters reached through props, aliases or custom hooks that return state tuples. It also ignores object-destructured state, useReducer dispatch and reassigned bindings.",
|
|
6457
|
+
"The rule reports only state reads that the setter argument itself evaluates. It ignores reads inside nested functions, type-only reads, a bare identity write and next values first stored in a local. It also ignores setter calls made directly in the function that declares the state, because they run during render, where the state belongs to the render in progress."
|
|
6458
|
+
],
|
|
6459
|
+
references: [
|
|
6460
|
+
"https://react.dev/reference/react/useState#updating-state-based-on-the-previous-state",
|
|
6461
|
+
"https://react.dev/learn/queueing-a-series-of-state-updates",
|
|
6462
|
+
"https://bun.com/docs/lint/rules/bun/react-prefer-updater-function"
|
|
6463
|
+
],
|
|
6464
|
+
examples: [
|
|
6465
|
+
{
|
|
6466
|
+
id: "snapshot-after-await",
|
|
6467
|
+
title: "Append to the render snapshot after an await",
|
|
6468
|
+
outcome: "match",
|
|
6469
|
+
files: [{ path: "src/cart.tsx", source: `${CART}setItems([...items, item]);${CART_END}` }],
|
|
6470
|
+
focusPath: "src/cart.tsx",
|
|
6471
|
+
expectedCount: 1,
|
|
6472
|
+
public: true
|
|
6473
|
+
},
|
|
6474
|
+
{
|
|
6475
|
+
id: "updater-after-await",
|
|
6476
|
+
title: "Append to the latest state through an updater",
|
|
6477
|
+
outcome: "no-match",
|
|
6478
|
+
files: [{ path: "src/cart.tsx", source: `${CART}setItems((previous) => [...previous, item]);${CART_END}` }],
|
|
6479
|
+
focusPath: "src/cart.tsx",
|
|
6480
|
+
expectedCount: 0,
|
|
6481
|
+
public: true
|
|
6482
|
+
},
|
|
6483
|
+
{
|
|
6484
|
+
id: "render-phase-update",
|
|
6485
|
+
title: "Adjust state from the render in progress",
|
|
6486
|
+
outcome: "no-match",
|
|
6487
|
+
files: [{ path: "src/panel.tsx", source: RENDER_PHASE }],
|
|
6488
|
+
focusPath: "src/panel.tsx",
|
|
6489
|
+
expectedCount: 0,
|
|
6490
|
+
public: false
|
|
6491
|
+
}
|
|
6492
|
+
]
|
|
6493
|
+
};
|
|
6494
|
+
|
|
6444
6495
|
// src/docs/repeated-static-call-cases.ts
|
|
6445
6496
|
var REPEATED_STATIC_CALL_CASES_DOCUMENTATION = {
|
|
6446
6497
|
summary: "Review three or more consecutive static-input call assertions as potential named cases.",
|
|
@@ -7578,6 +7629,7 @@ var RULE_DOCUMENTATION = {
|
|
|
7578
7629
|
"prefer-zod-infer": PREFER_ZOD_INFER_DOCUMENTATION,
|
|
7579
7630
|
"prefer-zod-parse-output-type": PREFER_ZOD_PARSE_OUTPUT_TYPE_DOCUMENTATION,
|
|
7580
7631
|
"production-derived-test-cases": PRODUCTION_DERIVED_TEST_CASES_DOCUMENTATION,
|
|
7632
|
+
"react-prefer-updater-function": REACT_PREFER_UPDATER_FUNCTION_DOCUMENTATION,
|
|
7581
7633
|
"repeated-static-call-cases": REPEATED_STATIC_CALL_CASES_DOCUMENTATION,
|
|
7582
7634
|
"require-assert-never": REQUIRE_ASSERT_NEVER_DOCUMENTATION,
|
|
7583
7635
|
"require-button-accessible-name": REQUIRE_BUTTON_ACCESSIBLE_NAME_DOCUMENTATION,
|
|
@@ -16467,6 +16467,9 @@ var VIEWS = /* @__PURE__ */ new WeakMap();
|
|
|
16467
16467
|
var DIRECT_EVAL = /* @__PURE__ */ new WeakMap();
|
|
16468
16468
|
var REACT_IMPORTS = /* @__PURE__ */ new WeakMap();
|
|
16469
16469
|
var PARAMETER_BINDINGS = /* @__PURE__ */ new WeakMap();
|
|
16470
|
+
function importsReactValue(source) {
|
|
16471
|
+
return source.ast.body.some((statement) => statement.type === "ImportDeclaration" && statement.source.value === "react" && statement.importKind !== "type");
|
|
16472
|
+
}
|
|
16470
16473
|
function renderFunction(node) {
|
|
16471
16474
|
let parent = parentOf(node);
|
|
16472
16475
|
while (parent !== null) {
|
|
@@ -16644,7 +16647,7 @@ var no_readonly_prop_state_default = defineRule({
|
|
|
16644
16647
|
tsrs = null;
|
|
16645
16648
|
pending = [];
|
|
16646
16649
|
const source = context.sourceCode;
|
|
16647
|
-
if (isGeneratedFile({ filename: context.filename, cwd: context.cwd }, source.text) || isTestFile({ filename: context.filename, cwd: context.cwd }) || !
|
|
16650
|
+
if (isGeneratedFile({ filename: context.filename, cwd: context.cwd }, source.text) || isTestFile({ filename: context.filename, cwd: context.cwd }) || !importsReactValue(source)) return false;
|
|
16648
16651
|
tsrs = tsrsServices(context);
|
|
16649
16652
|
return true;
|
|
16650
16653
|
},
|
|
@@ -31917,8 +31920,62 @@ var production_derived_test_cases_default = defineRule({
|
|
|
31917
31920
|
}
|
|
31918
31921
|
});
|
|
31919
31922
|
|
|
31923
|
+
// src/rules/react-prefer-updater-function.ts
|
|
31924
|
+
var NAME131 = "react-prefer-updater-function";
|
|
31925
|
+
function stateForSetter(source, setter) {
|
|
31926
|
+
const declarator = setter.defs[0]?.node;
|
|
31927
|
+
if (setter.defs.length !== 1 || declarator?.type !== "VariableDeclarator" || declarator.id.type !== "ArrayPattern" || declarator.init === null) return null;
|
|
31928
|
+
const [value, update] = declarator.id.elements;
|
|
31929
|
+
const call = unwrapExpression(declarator.init);
|
|
31930
|
+
if (value?.type !== "Identifier" || update?.type !== "Identifier" || !setter.identifiers.includes(update) || call.type !== "CallExpression" || !reactHook(source, call.callee, "useState") || !stableValueBinding(setter)) return null;
|
|
31931
|
+
const state = source.getDeclaredVariables(declarator).find((variable) => variable.identifiers.includes(value));
|
|
31932
|
+
return state !== void 0 && stableValueBinding(state) ? { state, owner: enclosingFunction(declarator) } : null;
|
|
31933
|
+
}
|
|
31934
|
+
function readsStateEagerly(state, argument) {
|
|
31935
|
+
return state.references.some((reference) => reference.isRead() && !typeOnlyReference(reference) && evaluatedBy(reference.identifier, argument));
|
|
31936
|
+
}
|
|
31937
|
+
function evaluatedBy(node, argument) {
|
|
31938
|
+
for (let current = node; current !== null && !isFunction(current); current = parentOf(current)) {
|
|
31939
|
+
if (current === argument) return true;
|
|
31940
|
+
}
|
|
31941
|
+
return false;
|
|
31942
|
+
}
|
|
31943
|
+
function isFunction(node) {
|
|
31944
|
+
return node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression" || node.type === "FunctionDeclaration";
|
|
31945
|
+
}
|
|
31946
|
+
var react_prefer_updater_function_default = defineRule({
|
|
31947
|
+
meta: {
|
|
31948
|
+
type: "problem",
|
|
31949
|
+
docs: { description: "Use the updater form when the next React state is built from the current state.", url: docsUrl(NAME131) },
|
|
31950
|
+
schema: [],
|
|
31951
|
+
messages: {
|
|
31952
|
+
preferUpdaterFunction: "`{{setter}}` builds the next state from the `{{state}}` snapshot, which is stale after an await, in timers and effects, or when two updates run before a re-render. Pass an updater: `{{setter}}((previous) => ...)`, reading `previous` wherever the argument read `{{state}}` or a value derived from it. Compute impure values such as IDs or timestamps before the call so the updater stays pure."
|
|
31953
|
+
}
|
|
31954
|
+
},
|
|
31955
|
+
createOnce(context) {
|
|
31956
|
+
return {
|
|
31957
|
+
before() {
|
|
31958
|
+
const source = context.sourceCode;
|
|
31959
|
+
return !isGeneratedFile({ filename: context.filename, cwd: context.cwd }, source.text) && importsReactValue(source);
|
|
31960
|
+
},
|
|
31961
|
+
CallExpression(node) {
|
|
31962
|
+
const [argument] = node.arguments;
|
|
31963
|
+
const callee = unwrapExpression(node.callee);
|
|
31964
|
+
if (node.arguments.length !== 1 || argument === void 0 || argument.type === "SpreadElement" || callee.type !== "Identifier") return;
|
|
31965
|
+
const value = unwrapExpression(argument);
|
|
31966
|
+
if (value.type === "Identifier" || isFunction(value)) return;
|
|
31967
|
+
const source = context.sourceCode;
|
|
31968
|
+
const setter = findVariable(source.getScope(callee), callee.name);
|
|
31969
|
+
const binding3 = setter === null ? null : stateForSetter(source, setter);
|
|
31970
|
+
if (setter === null || binding3 === null || enclosingFunction(node) === binding3.owner || !readsStateEagerly(binding3.state, argument)) return;
|
|
31971
|
+
context.report({ node, messageId: "preferUpdaterFunction", data: { setter: setter.name, state: binding3.state.name } });
|
|
31972
|
+
}
|
|
31973
|
+
};
|
|
31974
|
+
}
|
|
31975
|
+
});
|
|
31976
|
+
|
|
31920
31977
|
// src/rules/repeated-static-call-cases.ts
|
|
31921
|
-
var
|
|
31978
|
+
var NAME132 = "repeated-static-call-cases";
|
|
31922
31979
|
var TEST_MODULES2 = /* @__PURE__ */ new Set(["@jest/globals", "@playwright/test", "bun:test", "node:test", "vitest"]);
|
|
31923
31980
|
var TEST_NAMES3 = /* @__PURE__ */ new Set(["it", "test"]);
|
|
31924
31981
|
var TEST_MODIFIERS5 = /* @__PURE__ */ new Set(["concurrent", "fails", "only", "sequential", "skip"]);
|
|
@@ -32042,7 +32099,7 @@ var repeated_static_call_cases_default = defineRule({
|
|
|
32042
32099
|
type: "suggestion",
|
|
32043
32100
|
docs: {
|
|
32044
32101
|
description: "Review three or more consecutive static-input call assertions as potential named cases.",
|
|
32045
|
-
url: docsUrl(
|
|
32102
|
+
url: docsUrl(NAME132)
|
|
32046
32103
|
},
|
|
32047
32104
|
schema: [],
|
|
32048
32105
|
messages: { repeatedStaticCallCases: "These {{count}} consecutive assertions repeat a call with static inputs. If independent, use named parameterized cases or subtests; preserve ordered scenarios as one test." }
|
|
@@ -32185,7 +32242,7 @@ function isPrimitiveReturn(node, annotation) {
|
|
|
32185
32242
|
}
|
|
32186
32243
|
|
|
32187
32244
|
// src/rules/require-assert-never.ts
|
|
32188
|
-
var
|
|
32245
|
+
var NAME133 = "require-assert-never";
|
|
32189
32246
|
function isRuntimeHandlingStatement(statement) {
|
|
32190
32247
|
if (statement.type === "EmptyStatement") return false;
|
|
32191
32248
|
if (statement.type === "BreakStatement") return statement.label !== null;
|
|
@@ -32255,7 +32312,7 @@ var require_assert_never_default = defineRule({
|
|
|
32255
32312
|
fixable: "code",
|
|
32256
32313
|
docs: {
|
|
32257
32314
|
description: "Require a never witness in empty switch defaults so discriminated unions remain exhaustive at compile time.",
|
|
32258
|
-
url: docsUrl(
|
|
32315
|
+
url: docsUrl(NAME133)
|
|
32259
32316
|
},
|
|
32260
32317
|
schema: [],
|
|
32261
32318
|
messages: {
|
|
@@ -32299,7 +32356,7 @@ var require_assert_never_default = defineRule({
|
|
|
32299
32356
|
});
|
|
32300
32357
|
|
|
32301
32358
|
// src/rules/require-button-accessible-name.ts
|
|
32302
|
-
var
|
|
32359
|
+
var NAME134 = "require-button-accessible-name";
|
|
32303
32360
|
var DELEGATING_PROPS = /* @__PURE__ */ new Set(["render", "asChild"]);
|
|
32304
32361
|
function delegatesRendering(opening) {
|
|
32305
32362
|
return opening.attributes.some(
|
|
@@ -32312,7 +32369,7 @@ var require_button_accessible_name_default = defineRule({
|
|
|
32312
32369
|
type: "problem",
|
|
32313
32370
|
docs: {
|
|
32314
32371
|
description: "Require an accessible name on statically unnamed native or configured JSX buttons.",
|
|
32315
|
-
url: docsUrl(
|
|
32372
|
+
url: docsUrl(NAME134)
|
|
32316
32373
|
},
|
|
32317
32374
|
schema: [
|
|
32318
32375
|
{
|
|
@@ -32366,7 +32423,7 @@ var require_button_accessible_name_default = defineRule({
|
|
|
32366
32423
|
});
|
|
32367
32424
|
|
|
32368
32425
|
// src/rules/require-camelcase-properties.ts
|
|
32369
|
-
var
|
|
32426
|
+
var NAME135 = "require-camelcase-properties";
|
|
32370
32427
|
var SNAKE_CASE_RE2 = /^[a-z][a-z0-9]*(?:_[a-z0-9]+)+$/u;
|
|
32371
32428
|
function blocksRename(member3, name) {
|
|
32372
32429
|
if (member3.type === "TSIndexSignature" || "computed" in member3 && member3.computed) return true;
|
|
@@ -32380,7 +32437,7 @@ var require_camelcase_properties_default = defineRule({
|
|
|
32380
32437
|
fixable: "code",
|
|
32381
32438
|
docs: {
|
|
32382
32439
|
description: "Require unquoted lower snake_case TypeScript properties and dot access to use camelCase.",
|
|
32383
|
-
url: docsUrl(
|
|
32440
|
+
url: docsUrl(NAME135)
|
|
32384
32441
|
},
|
|
32385
32442
|
schema: [],
|
|
32386
32443
|
defaultOptions: [],
|
|
@@ -32444,7 +32501,7 @@ var require_camelcase_properties_default = defineRule({
|
|
|
32444
32501
|
});
|
|
32445
32502
|
|
|
32446
32503
|
// src/rules/require-constrained-schema-integer.ts
|
|
32447
|
-
var
|
|
32504
|
+
var NAME136 = "require-constrained-schema-integer";
|
|
32448
32505
|
var FIELD_MINIMUMS = /* @__PURE__ */ new Map([["limit", 1], ["version", 0]]);
|
|
32449
32506
|
var NUMBER_FACTORIES = /* @__PURE__ */ new Set(["number", "int", "int32", "uint32"]);
|
|
32450
32507
|
var OBJECT_FACTORIES = /* @__PURE__ */ new Set(["object", "strictObject", "looseObject"]);
|
|
@@ -32486,7 +32543,7 @@ var require_constrained_schema_integer_default = defineRule({
|
|
|
32486
32543
|
type: "suggestion",
|
|
32487
32544
|
docs: {
|
|
32488
32545
|
description: "Declare integer and lower-bound validation for numeric limit and version schema fields.",
|
|
32489
|
-
url: docsUrl(
|
|
32546
|
+
url: docsUrl(NAME136)
|
|
32490
32547
|
},
|
|
32491
32548
|
schema: [],
|
|
32492
32549
|
messages: { constrainInteger: "Declare `{{field}}` as an integer with a lower bound of {{minimum}} or greater, or reuse its named domain schema." }
|
|
@@ -32965,14 +33022,14 @@ function closedNominalInheritanceFix(sourceCode2, file, call, consumer, implemen
|
|
|
32965
33022
|
}
|
|
32966
33023
|
|
|
32967
33024
|
// src/rules/require-explicit-contract-implementation.ts
|
|
32968
|
-
var
|
|
33025
|
+
var NAME137 = "require-explicit-contract-implementation";
|
|
32969
33026
|
var require_explicit_contract_implementation_default = defineRule({
|
|
32970
33027
|
meta: {
|
|
32971
33028
|
type: "problem",
|
|
32972
33029
|
fixable: "code",
|
|
32973
33030
|
docs: {
|
|
32974
33031
|
description: "Require nominal inheritance when an injected abstract class is rejected by an instanceof guard.",
|
|
32975
|
-
url: docsUrl(
|
|
33032
|
+
url: docsUrl(NAME137)
|
|
32976
33033
|
},
|
|
32977
33034
|
schema: [],
|
|
32978
33035
|
messages: {
|
|
@@ -33663,7 +33720,7 @@ function abstractContractOperations(node, functionAliases) {
|
|
|
33663
33720
|
}
|
|
33664
33721
|
|
|
33665
33722
|
// src/rules/require-explicit-service-contract.ts
|
|
33666
|
-
var
|
|
33723
|
+
var NAME138 = "require-explicit-service-contract";
|
|
33667
33724
|
var JAVASCRIPT_FILE_RE2 = /\.(?:js|jsx|mjs|cjs)$/iu;
|
|
33668
33725
|
var BOUNDARY_SUFFIX = /(?:Service|ServiceImpl|Store|Repository|Provider|Executor|Enqueuer|Runner|Platform|Manager|ManagerImpl|Detector|Control|Refresher|Reader|Settlement|Retry|Stop|Catalog|DAO)$/u;
|
|
33669
33726
|
var NON_SERVICE_SUFFIX = /(?:Router|Middleware|Builder)$/u;
|
|
@@ -33679,7 +33736,7 @@ var require_explicit_service_contract_default = defineRule({
|
|
|
33679
33736
|
type: "problem",
|
|
33680
33737
|
docs: {
|
|
33681
33738
|
description: "Require explicit contracts for classes that invoke retained collaborators.",
|
|
33682
|
-
url: docsUrl(
|
|
33739
|
+
url: docsUrl(NAME138)
|
|
33683
33740
|
},
|
|
33684
33741
|
schema: [],
|
|
33685
33742
|
fixable: "code",
|
|
@@ -33715,7 +33772,7 @@ var require_explicit_service_contract_default = defineRule({
|
|
|
33715
33772
|
});
|
|
33716
33773
|
|
|
33717
33774
|
// src/rules/require-fetch-timeout.ts
|
|
33718
|
-
var
|
|
33775
|
+
var NAME139 = "require-fetch-timeout";
|
|
33719
33776
|
function emptySignal(expression, resolvesToGlobal) {
|
|
33720
33777
|
const value = unwrapExpression(expression);
|
|
33721
33778
|
if (value.type === "Literal" && value.value === null)
|
|
@@ -33777,7 +33834,7 @@ var require_fetch_timeout_default = defineRule({
|
|
|
33777
33834
|
type: "problem",
|
|
33778
33835
|
docs: {
|
|
33779
33836
|
description: "Require an explicit abort signal on locally analyzable global fetch calls.",
|
|
33780
|
-
url: docsUrl(
|
|
33837
|
+
url: docsUrl(NAME139)
|
|
33781
33838
|
},
|
|
33782
33839
|
schema: [
|
|
33783
33840
|
{
|
|
@@ -33869,13 +33926,13 @@ var require_fetch_timeout_default = defineRule({
|
|
|
33869
33926
|
});
|
|
33870
33927
|
|
|
33871
33928
|
// src/rules/require-ime-safe-enter-handler.ts
|
|
33872
|
-
var
|
|
33929
|
+
var NAME140 = "require-ime-safe-enter-handler";
|
|
33873
33930
|
var require_ime_safe_enter_handler_default = defineRule({
|
|
33874
33931
|
meta: {
|
|
33875
33932
|
type: "problem",
|
|
33876
33933
|
docs: {
|
|
33877
33934
|
description: "Preserve IME composition before Enter submits or blurs a native text control.",
|
|
33878
|
-
url: docsUrl(
|
|
33935
|
+
url: docsUrl(NAME140)
|
|
33879
33936
|
},
|
|
33880
33937
|
schema: [],
|
|
33881
33938
|
messages: {
|
|
@@ -33951,7 +34008,7 @@ function nativeAction(node, event) {
|
|
|
33951
34008
|
}
|
|
33952
34009
|
|
|
33953
34010
|
// src/rules/require-interface-for-exported-class.ts
|
|
33954
|
-
var
|
|
34011
|
+
var NAME141 = "require-interface-for-exported-class";
|
|
33955
34012
|
var JAVASCRIPT_FILE_RE3 = /\.(?:js|jsx|mjs|cjs)$/iu;
|
|
33956
34013
|
function isPublicInstanceMember2(member3) {
|
|
33957
34014
|
return !member3.static && member3.accessibility !== "private" && member3.accessibility !== "protected" && member3.key.type !== "PrivateIdentifier";
|
|
@@ -34012,7 +34069,7 @@ var require_interface_for_exported_class_default = defineRule({
|
|
|
34012
34069
|
type: "suggestion",
|
|
34013
34070
|
docs: {
|
|
34014
34071
|
description: "Require exported concrete classes with public behavior to declare a contract.",
|
|
34015
|
-
url: docsUrl(
|
|
34072
|
+
url: docsUrl(NAME141)
|
|
34016
34073
|
},
|
|
34017
34074
|
schema: [],
|
|
34018
34075
|
fixable: "code",
|
|
@@ -34054,7 +34111,7 @@ var require_interface_for_exported_class_default = defineRule({
|
|
|
34054
34111
|
});
|
|
34055
34112
|
|
|
34056
34113
|
// src/rules/require-jwt-lifetime.ts
|
|
34057
|
-
var
|
|
34114
|
+
var NAME142 = "require-jwt-lifetime";
|
|
34058
34115
|
function isJoseValueImport(declaration) {
|
|
34059
34116
|
return declaration.type === "ImportDeclaration" && declaration.importKind !== "type" && declaration.source.value === "jose";
|
|
34060
34117
|
}
|
|
@@ -34063,7 +34120,7 @@ var require_jwt_lifetime_default = defineRule({
|
|
|
34063
34120
|
type: "problem",
|
|
34064
34121
|
docs: {
|
|
34065
34122
|
description: "Require an expiration claim or a maximum token age when returning a verified JWT.",
|
|
34066
|
-
url: docsUrl(
|
|
34123
|
+
url: docsUrl(NAME142)
|
|
34067
34124
|
},
|
|
34068
34125
|
schema: [],
|
|
34069
34126
|
messages: { lifetime: "Require the JWT exp claim or set maxTokenAge before returning this verification result." }
|
|
@@ -34122,7 +34179,7 @@ var require_jwt_lifetime_default = defineRule({
|
|
|
34122
34179
|
});
|
|
34123
34180
|
|
|
34124
34181
|
// src/rules/require-pascal-case-zod-schema-name.ts
|
|
34125
|
-
var
|
|
34182
|
+
var NAME143 = "require-pascal-case-zod-schema-name";
|
|
34126
34183
|
var PASCAL_SCHEMA_NAME_RE = /^[A-Z][A-Za-z0-9]*Schema$/;
|
|
34127
34184
|
var BENCHMARK_PATH_RE2 = /(^|[\\/])(?:benchmarks?|bench)[\\/]/;
|
|
34128
34185
|
var NON_SCHEMA_TERMINALS2 = /* @__PURE__ */ new Set([
|
|
@@ -34293,7 +34350,7 @@ var require_pascal_case_zod_schema_name_default = defineRule({
|
|
|
34293
34350
|
fixable: "code",
|
|
34294
34351
|
docs: {
|
|
34295
34352
|
description: "Require confirmed module-level Zod schema contracts to use PascalCase with a `Schema` suffix.",
|
|
34296
|
-
url: docsUrl(
|
|
34353
|
+
url: docsUrl(NAME143)
|
|
34297
34354
|
},
|
|
34298
34355
|
schema: [],
|
|
34299
34356
|
defaultOptions: [],
|
|
@@ -34403,7 +34460,7 @@ var require_pascal_case_zod_schema_name_default = defineRule({
|
|
|
34403
34460
|
});
|
|
34404
34461
|
|
|
34405
34462
|
// src/rules/require-port-for-service.ts
|
|
34406
|
-
var
|
|
34463
|
+
var NAME144 = "require-port-for-service";
|
|
34407
34464
|
var HTTP_TRANSPORT_TYPE_RE = /^(?:KyInstance|AxiosInstance|Session)$/;
|
|
34408
34465
|
var TRANSPORT_WRAPPER_NAME_RE = /(?:Client$|^Http[A-Z])/;
|
|
34409
34466
|
var ERROR_VALUE_NAME_RE = /Error$/;
|
|
@@ -34537,7 +34594,7 @@ var require_port_for_service_default = defineRule({
|
|
|
34537
34594
|
type: "suggestion",
|
|
34538
34595
|
docs: {
|
|
34539
34596
|
description: "Advise when an exported service with injected collaborators has public methods not covered by its declared ports.",
|
|
34540
|
-
url: docsUrl(
|
|
34597
|
+
url: docsUrl(NAME144)
|
|
34541
34598
|
},
|
|
34542
34599
|
schema: [],
|
|
34543
34600
|
fixable: "code",
|
|
@@ -34577,7 +34634,7 @@ var require_port_for_service_default = defineRule({
|
|
|
34577
34634
|
});
|
|
34578
34635
|
|
|
34579
34636
|
// src/rules/require-sql-access-class.ts
|
|
34580
|
-
var
|
|
34637
|
+
var NAME145 = "require-sql-access-class";
|
|
34581
34638
|
var DIRECT_EXECUTION_METHODS = /* @__PURE__ */ new Set([
|
|
34582
34639
|
"all",
|
|
34583
34640
|
"batch",
|
|
@@ -34775,7 +34832,7 @@ var require_sql_access_class_default = defineRule({
|
|
|
34775
34832
|
type: "suggestion",
|
|
34776
34833
|
docs: {
|
|
34777
34834
|
description: "Keep SQL reads and writes inside a class that receives its database dependency.",
|
|
34778
|
-
url: docsUrl(
|
|
34835
|
+
url: docsUrl(NAME145)
|
|
34779
34836
|
},
|
|
34780
34837
|
schema: [],
|
|
34781
34838
|
messages: {
|
|
@@ -34847,7 +34904,7 @@ function staticMatcherFix(sourceCode2, property) {
|
|
|
34847
34904
|
}
|
|
34848
34905
|
|
|
34849
34906
|
// src/rules/require-static-next-matcher.ts
|
|
34850
|
-
var
|
|
34907
|
+
var NAME146 = "require-static-next-matcher";
|
|
34851
34908
|
var NEXT_ENTRY_FILE = /(?:^|[/\\])(?:middleware|proxy)\.[cm]?[jt]sx?$/u;
|
|
34852
34909
|
function isStaticValue(node) {
|
|
34853
34910
|
const value = unwrapExpression(node);
|
|
@@ -34879,7 +34936,7 @@ var require_static_next_matcher_default = defineRule({
|
|
|
34879
34936
|
fixable: "code",
|
|
34880
34937
|
docs: {
|
|
34881
34938
|
description: "Require Next.js middleware and proxy matcher configuration to contain only build-time literals.",
|
|
34882
|
-
url: docsUrl(
|
|
34939
|
+
url: docsUrl(NAME146)
|
|
34883
34940
|
},
|
|
34884
34941
|
schema: [],
|
|
34885
34942
|
messages: {
|
|
@@ -34923,7 +34980,7 @@ var require_static_next_matcher_default = defineRule({
|
|
|
34923
34980
|
});
|
|
34924
34981
|
|
|
34925
34982
|
// src/rules/require-svg-accessible-name.ts
|
|
34926
|
-
var
|
|
34983
|
+
var NAME147 = "require-svg-accessible-name";
|
|
34927
34984
|
var DECORATIVE_ROLES = /* @__PURE__ */ new Set(["none", "presentation"]);
|
|
34928
34985
|
var NAME_ATTRIBUTES = ["aria-label", "aria-labelledby"];
|
|
34929
34986
|
var NO_ICON_MODULES = [];
|
|
@@ -34932,7 +34989,7 @@ var require_svg_accessible_name_default = defineRule({
|
|
|
34932
34989
|
type: "problem",
|
|
34933
34990
|
docs: {
|
|
34934
34991
|
description: "Require a name or explicit decorative semantics on inline SVG elements.",
|
|
34935
|
-
url: docsUrl(
|
|
34992
|
+
url: docsUrl(NAME147)
|
|
34936
34993
|
},
|
|
34937
34994
|
schema: [],
|
|
34938
34995
|
messages: {
|
|
@@ -34964,7 +35021,7 @@ var require_svg_accessible_name_default = defineRule({
|
|
|
34964
35021
|
});
|
|
34965
35022
|
|
|
34966
35023
|
// src/rules/require-use-form-default-values.ts
|
|
34967
|
-
var
|
|
35024
|
+
var NAME148 = "require-use-form-default-values";
|
|
34968
35025
|
var UNSAFE_FORM_MEMBERS = /* @__PURE__ */ new Set(["__defineGetter__", "__defineSetter__", "__proto__", "valueOf"]);
|
|
34969
35026
|
function staticPropertyName2(property) {
|
|
34970
35027
|
return property.computed ? null : getPropertyName(property);
|
|
@@ -35007,7 +35064,7 @@ var require_use_form_default_values_default = defineRule({
|
|
|
35007
35064
|
type: "problem",
|
|
35008
35065
|
docs: {
|
|
35009
35066
|
description: "Require explicit form-level initialization or a field default for directly bound Controller fields.",
|
|
35010
|
-
url: docsUrl(
|
|
35067
|
+
url: docsUrl(NAME148)
|
|
35011
35068
|
},
|
|
35012
35069
|
schema: [],
|
|
35013
35070
|
messages: { requireUseFormDefaultValues: "Add non-undefined defaultValues/values to the associated useForm call or non-undefined defaultValue to this field." }
|
|
@@ -35146,7 +35203,7 @@ var require_use_form_default_values_default = defineRule({
|
|
|
35146
35203
|
});
|
|
35147
35204
|
|
|
35148
35205
|
// src/rules/require-use-server-in-actions-file.ts
|
|
35149
|
-
var
|
|
35206
|
+
var NAME149 = "require-use-server-in-actions-file";
|
|
35150
35207
|
var ACTION_MODULE_RE = /(?:^|\/)app\/.*\/(?:actions|[^/]+-actions)\.[cm]?[jt]s$/u;
|
|
35151
35208
|
function isUseServerDirective2(statement) {
|
|
35152
35209
|
return statement.type === "ExpressionStatement" && statement.directive === "use server";
|
|
@@ -35167,7 +35224,7 @@ var require_use_server_in_actions_file_default = defineRule({
|
|
|
35167
35224
|
type: "problem",
|
|
35168
35225
|
docs: {
|
|
35169
35226
|
description: "route action module missing the use server directive",
|
|
35170
|
-
url: docsUrl(
|
|
35227
|
+
url: docsUrl(NAME149)
|
|
35171
35228
|
},
|
|
35172
35229
|
schema: [],
|
|
35173
35230
|
messages: {
|
|
@@ -35193,7 +35250,7 @@ var require_use_server_in_actions_file_default = defineRule({
|
|
|
35193
35250
|
});
|
|
35194
35251
|
|
|
35195
35252
|
// src/rules/require-zod-form-validation.ts
|
|
35196
|
-
var
|
|
35253
|
+
var NAME150 = "require-zod-form-validation";
|
|
35197
35254
|
var ZOD_PARSE_METHODS = /* @__PURE__ */ new Set([
|
|
35198
35255
|
"parse",
|
|
35199
35256
|
"safeParse",
|
|
@@ -35252,7 +35309,7 @@ var require_zod_form_validation_default = defineRule({
|
|
|
35252
35309
|
type: "problem",
|
|
35253
35310
|
docs: {
|
|
35254
35311
|
description: "Require Zod validation (`Schema.parse(...)` / `Schema.safeParse(...)`) when reading values out of a `FormData` object.",
|
|
35255
|
-
url: docsUrl(
|
|
35312
|
+
url: docsUrl(NAME150)
|
|
35256
35313
|
},
|
|
35257
35314
|
schema: [],
|
|
35258
35315
|
messages: {
|
|
@@ -35540,7 +35597,7 @@ function declaresFramework(metadata, name) {
|
|
|
35540
35597
|
}
|
|
35541
35598
|
|
|
35542
35599
|
// src/rules/sole-export-matches-filename.ts
|
|
35543
|
-
var
|
|
35600
|
+
var NAME151 = "sole-export-matches-filename";
|
|
35544
35601
|
var EXCLUDED_STEMS = /* @__PURE__ */ new Set(["global", "index", "types"]);
|
|
35545
35602
|
function stem3(filename) {
|
|
35546
35603
|
const base = filename.replaceAll("\\", "/").split("/").at(-1) ?? "";
|
|
@@ -35554,7 +35611,7 @@ var sole_export_matches_filename_default = defineRule({
|
|
|
35554
35611
|
type: "suggestion",
|
|
35555
35612
|
docs: {
|
|
35556
35613
|
description: "Make a module filename reflect its sole named public runtime export.",
|
|
35557
|
-
url: docsUrl(
|
|
35614
|
+
url: docsUrl(NAME151)
|
|
35558
35615
|
},
|
|
35559
35616
|
schema: [],
|
|
35560
35617
|
messages: {
|
|
@@ -35608,7 +35665,7 @@ var source_coupled_test_default = createSourceCoupledRule(
|
|
|
35608
35665
|
);
|
|
35609
35666
|
|
|
35610
35667
|
// src/rules/stepdown.ts
|
|
35611
|
-
function
|
|
35668
|
+
function isFunction2(node) {
|
|
35612
35669
|
return node.type === "ArrowFunctionExpression" || node.type === "FunctionDeclaration" || node.type === "FunctionExpression";
|
|
35613
35670
|
}
|
|
35614
35671
|
function isErasedExpression2(node) {
|
|
@@ -35631,7 +35688,7 @@ function wrappedExpression(node) {
|
|
|
35631
35688
|
function enclosingFunction2(node) {
|
|
35632
35689
|
let current = parentOf(node);
|
|
35633
35690
|
while (current !== null) {
|
|
35634
|
-
if (
|
|
35691
|
+
if (isFunction2(current)) return current;
|
|
35635
35692
|
current = parentOf(current);
|
|
35636
35693
|
}
|
|
35637
35694
|
return void 0;
|
|
@@ -35890,7 +35947,7 @@ function isDeferredModuleStatement(statement) {
|
|
|
35890
35947
|
if (node === null || node.type === "FunctionDeclaration" || node.type === "TSDeclareFunction" || node.type === "TSInterfaceDeclaration" || node.type === "TSTypeAliasDeclaration" || node.type === "ImportDeclaration" || node.type === "ExportAllDeclaration" || node.type === "EmptyStatement") {
|
|
35891
35948
|
return true;
|
|
35892
35949
|
}
|
|
35893
|
-
return node.type === "VariableDeclaration" && node.declarations.every((declaration) => declaration.id.type === "Identifier" && declaration.init !== null &&
|
|
35950
|
+
return node.type === "VariableDeclaration" && node.declarations.every((declaration) => declaration.id.type === "Identifier" && declaration.init !== null && isFunction2(unwrapExpression4(declaration.init)));
|
|
35894
35951
|
}
|
|
35895
35952
|
function exportedNames(program) {
|
|
35896
35953
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -35931,7 +35988,7 @@ function moduleDefinitions(program) {
|
|
|
35931
35988
|
}
|
|
35932
35989
|
function constFunctionDefinition(declarator) {
|
|
35933
35990
|
const initializer = declarator.init === null ? null : unwrapExpression4(declarator.init);
|
|
35934
|
-
if (declarator.id.type !== "Identifier" || initializer === null || !
|
|
35991
|
+
if (declarator.id.type !== "Identifier" || initializer === null || !isFunction2(initializer)) return [];
|
|
35935
35992
|
const { name } = declarator.id;
|
|
35936
35993
|
return [{ key: name, name, node: declarator, functionNode: initializer, bindingNode: declarator }];
|
|
35937
35994
|
}
|
|
@@ -36010,7 +36067,7 @@ function referencedPropertyName(node) {
|
|
|
36010
36067
|
}
|
|
36011
36068
|
function walk3(node, visitorKeys, visit2, nestedFunction = false) {
|
|
36012
36069
|
visit2(node, nestedFunction);
|
|
36013
|
-
const nested = nestedFunction ||
|
|
36070
|
+
const nested = nestedFunction || isFunction2(node) || node.type === "ClassDeclaration" || node.type === "ClassExpression";
|
|
36014
36071
|
forEachAstChild(node, visitorKeys, (child) => {
|
|
36015
36072
|
walk3(child, visitorKeys, visit2, nested);
|
|
36016
36073
|
});
|
|
@@ -36031,7 +36088,7 @@ function classScope(state, node, computedReferences, privateBandOnly) {
|
|
|
36031
36088
|
const visitorKeys = state.sourceCode.visitorKeys;
|
|
36032
36089
|
const methods = node.body.body.flatMap((member3) => member3.type === "MethodDefinition" ? [member3] : []);
|
|
36033
36090
|
const counts = classMethodCounts(node.body, methods);
|
|
36034
|
-
const implementations = methods.filter((method) =>
|
|
36091
|
+
const implementations = methods.filter((method) => isFunction2(method.value));
|
|
36035
36092
|
const implementationCounts = classMethodCounts(node.body, implementations);
|
|
36036
36093
|
const scopeDefinitions = implementations.flatMap((method) => {
|
|
36037
36094
|
const name = methodName(method);
|
|
@@ -36069,7 +36126,7 @@ function classScope(state, node, computedReferences, privateBandOnly) {
|
|
|
36069
36126
|
return;
|
|
36070
36127
|
}
|
|
36071
36128
|
const methodFunction = method.value;
|
|
36072
|
-
if (!
|
|
36129
|
+
if (!isFunction2(methodFunction) || methodFunction.body === null) return;
|
|
36073
36130
|
const body3 = methodFunction.body;
|
|
36074
36131
|
const caller = methodKey(name, method.static);
|
|
36075
36132
|
const receivers = new Map(classReceivers);
|
|
@@ -36297,7 +36354,7 @@ var stepdown_default = defineRule({
|
|
|
36297
36354
|
});
|
|
36298
36355
|
|
|
36299
36356
|
// src/rules/store-insert-requires-on-conflict.ts
|
|
36300
|
-
var
|
|
36357
|
+
var NAME152 = "store-insert-requires-on-conflict";
|
|
36301
36358
|
var INSERT_WRITE = /\bINSERT\s+(?:OR\s+\w+\s+)?INTO\s+[\w."'`?$:@-]+\s*(?:\([^)]*\)\s*)?(?:VALUES|SELECT|DEFAULT\s+VALUES)\b/i;
|
|
36302
36359
|
var CONFLICT_HANDLED = /\bON\s+CONFLICT\b|\bON\s+DUPLICATE\s+KEY\b|\bINSERT\s+OR\s+(?:IGNORE|REPLACE)\b|\bINSERT\b[\s\S]*?\bSELECT\b[\s\S]*?\bWHERE\s+NOT\s+EXISTS\b/i;
|
|
36303
36360
|
var REPLAY_CONTRACT_NAME = /(?:enqueue|ensure|migrate|recordOnce|schedule|seed|upsert|getOrCreate|createIfAbsent|insertIfAbsent)/i;
|
|
@@ -36326,7 +36383,7 @@ var store_insert_requires_on_conflict_default = defineRule({
|
|
|
36326
36383
|
type: "problem",
|
|
36327
36384
|
docs: {
|
|
36328
36385
|
description: "Review conflict handling for embedded inserts in replay-named callables.",
|
|
36329
|
-
url: docsUrl(
|
|
36386
|
+
url: docsUrl(NAME152)
|
|
36330
36387
|
},
|
|
36331
36388
|
schema: [],
|
|
36332
36389
|
messages: {
|
|
@@ -36351,7 +36408,7 @@ var store_insert_requires_on_conflict_default = defineRule({
|
|
|
36351
36408
|
});
|
|
36352
36409
|
|
|
36353
36410
|
// src/rules/test-loops-over-literal-cases.ts
|
|
36354
|
-
var
|
|
36411
|
+
var NAME153 = "test-loops-over-literal-cases";
|
|
36355
36412
|
var TEST_CALLERS3 = /* @__PURE__ */ new Set(["it", "test"]);
|
|
36356
36413
|
var TEST_MODIFIERS6 = /* @__PURE__ */ new Set(["concurrent", "fails", "only", "sequential", "skip"]);
|
|
36357
36414
|
var ASSERTION_ROOTS = /* @__PURE__ */ new Set([
|
|
@@ -36447,7 +36504,7 @@ var test_loops_over_literal_cases_default = defineRule({
|
|
|
36447
36504
|
type: "suggestion",
|
|
36448
36505
|
docs: {
|
|
36449
36506
|
description: "Disallow assertions over an inline literal case loop in a test; parameterization reports and names every case independently.",
|
|
36450
|
-
url: docsUrl(
|
|
36507
|
+
url: docsUrl(NAME153)
|
|
36451
36508
|
},
|
|
36452
36509
|
schema: [],
|
|
36453
36510
|
messages: {
|
|
@@ -36511,7 +36568,7 @@ var test_loops_over_literal_cases_default = defineRule({
|
|
|
36511
36568
|
});
|
|
36512
36569
|
|
|
36513
36570
|
// src/rules/test-phase-label-comment.ts
|
|
36514
|
-
var
|
|
36571
|
+
var NAME154 = "test-phase-label-comment";
|
|
36515
36572
|
var PHASE_WORD = String.raw`arrange|act|assert(?:ion)?s?|given|when|then|exercise|execute|verif(?:y|ication)|cleanup|prepare|sanity(?:\s+check)?`;
|
|
36516
36573
|
var PHASE_RE = new RegExp(
|
|
36517
36574
|
String.raw`^[-=~*_#.\s]{0,40}(?:${PHASE_WORD})(?:\s*(?:[/&+,|]|->|and)\s*(?:${PHASE_WORD}))*[-=~*_#.\s:;!–—]{0,40}$`,
|
|
@@ -36539,7 +36596,7 @@ var test_phase_label_comment_default = defineRule({
|
|
|
36539
36596
|
fixable: "code",
|
|
36540
36597
|
docs: {
|
|
36541
36598
|
description: "Tests must not use bare Arrange, Act, Assert, Given, When, or Then phase comments.",
|
|
36542
|
-
url: docsUrl(
|
|
36599
|
+
url: docsUrl(NAME154)
|
|
36543
36600
|
},
|
|
36544
36601
|
schema: [],
|
|
36545
36602
|
messages: { removeLabel: "Bare test phase label \u2014 delete it and let the test names and helpers carry the structure." }
|
|
@@ -36722,6 +36779,7 @@ var RULES = {
|
|
|
36722
36779
|
"prefer-zod-infer": prefer_zod_infer_default,
|
|
36723
36780
|
"prefer-zod-parse-output-type": prefer_zod_parse_output_type_default,
|
|
36724
36781
|
"production-derived-test-cases": production_derived_test_cases_default,
|
|
36782
|
+
"react-prefer-updater-function": react_prefer_updater_function_default,
|
|
36725
36783
|
"repeated-static-call-cases": repeated_static_call_cases_default,
|
|
36726
36784
|
"require-assert-never": require_assert_never_default,
|
|
36727
36785
|
"require-button-accessible-name": require_button_accessible_name_default,
|
package/dist/index.js
CHANGED