@sarj/eslint-plugin 15.17.7 → 15.17.8
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 +17 -85
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +17 -85
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -10080,8 +10080,8 @@ var NO_ZOD_NATIVE_ENUM_DOCUMENTATION = {
|
|
|
10080
10080
|
rationale: "Wrapping a TypeScript enum preserves its emitted runtime object and duplicates the schema's value definition across two constructs.",
|
|
10081
10081
|
remediation: "Pass string literals directly to `z.enum` and derive the TypeScript type with `z.infer`.",
|
|
10082
10082
|
category: "maintainability",
|
|
10083
|
-
autofix: "
|
|
10084
|
-
limitations: ["
|
|
10083
|
+
autofix: "none",
|
|
10084
|
+
limitations: ["Migration is manual: replacing an enum-like object with a value array changes the public schema.enum keys and can affect consumers."],
|
|
10085
10085
|
examples: [
|
|
10086
10086
|
{
|
|
10087
10087
|
id: "zod-literal-enum",
|
|
@@ -10099,8 +10099,7 @@ var NO_ZOD_NATIVE_ENUM_DOCUMENTATION = {
|
|
|
10099
10099
|
files: [{ path: "src/status.ts", source: 'import { z } from "zod"; const S = z.nativeEnum({ Active: "active", Inactive: "inactive" });' }],
|
|
10100
10100
|
focusPath: "src/status.ts",
|
|
10101
10101
|
expectedCount: 1,
|
|
10102
|
-
public: true
|
|
10103
|
-
fixedFiles: [{ path: "src/status.ts", source: 'import { z } from "zod"; const S = z.enum(["active", "inactive"]);' }]
|
|
10102
|
+
public: true
|
|
10104
10103
|
}
|
|
10105
10104
|
]
|
|
10106
10105
|
};
|
|
@@ -10125,26 +10124,6 @@ function unwrap2(node) {
|
|
|
10125
10124
|
}
|
|
10126
10125
|
return node;
|
|
10127
10126
|
}
|
|
10128
|
-
function stringValueTexts(node, sourceCode) {
|
|
10129
|
-
const texts = [];
|
|
10130
|
-
for (const prop of node.properties) {
|
|
10131
|
-
if (prop.type !== import_utils56.AST_NODE_TYPES.Property) {
|
|
10132
|
-
return null;
|
|
10133
|
-
}
|
|
10134
|
-
if (prop.computed || prop.shorthand || prop.method || prop.kind !== "init") {
|
|
10135
|
-
return null;
|
|
10136
|
-
}
|
|
10137
|
-
const value = prop.value;
|
|
10138
|
-
if (value.type !== import_utils56.AST_NODE_TYPES.Literal || typeof value.value !== "string") {
|
|
10139
|
-
return null;
|
|
10140
|
-
}
|
|
10141
|
-
const text = sourceCode.getText(value);
|
|
10142
|
-
if (!texts.includes(text)) {
|
|
10143
|
-
texts.push(text);
|
|
10144
|
-
}
|
|
10145
|
-
}
|
|
10146
|
-
return texts.length > 0 ? texts : null;
|
|
10147
|
-
}
|
|
10148
10127
|
function resolvesToLocalEnum(node, scope) {
|
|
10149
10128
|
let current = scope;
|
|
10150
10129
|
while (current !== null) {
|
|
@@ -10176,7 +10155,6 @@ var no_zod_native_enum_default = createRule({
|
|
|
10176
10155
|
documentation: NO_ZOD_NATIVE_ENUM_DOCUMENTATION,
|
|
10177
10156
|
meta: {
|
|
10178
10157
|
type: "suggestion",
|
|
10179
|
-
fixable: "code",
|
|
10180
10158
|
docs: {
|
|
10181
10159
|
description: 'Disallow `z.nativeEnum()` (and `z.enum()` over a TypeScript enum); use `z.enum(["a", "b"])` with a string-literal union instead.'
|
|
10182
10160
|
},
|
|
@@ -10221,30 +10199,6 @@ var no_zod_native_enum_default = createRule({
|
|
|
10221
10199
|
}
|
|
10222
10200
|
return false;
|
|
10223
10201
|
}
|
|
10224
|
-
function buildFix(node) {
|
|
10225
|
-
const callee = node.callee;
|
|
10226
|
-
if (callee.type !== import_utils56.AST_NODE_TYPES.MemberExpression || callee.property.type !== import_utils56.AST_NODE_TYPES.Identifier) {
|
|
10227
|
-
return null;
|
|
10228
|
-
}
|
|
10229
|
-
const arg = node.arguments[0];
|
|
10230
|
-
if (arg === void 0 || node.arguments.length !== 1 || arg.type === import_utils56.AST_NODE_TYPES.SpreadElement) {
|
|
10231
|
-
return null;
|
|
10232
|
-
}
|
|
10233
|
-
const inner = unwrap2(arg);
|
|
10234
|
-
if (inner.type !== import_utils56.AST_NODE_TYPES.ObjectExpression) {
|
|
10235
|
-
return null;
|
|
10236
|
-
}
|
|
10237
|
-
const values = stringValueTexts(inner, sourceCode);
|
|
10238
|
-
if (values === null) {
|
|
10239
|
-
return null;
|
|
10240
|
-
}
|
|
10241
|
-
const property = callee.property;
|
|
10242
|
-
const replacementArg = `[${values.join(", ")}]`;
|
|
10243
|
-
return (fixer) => [
|
|
10244
|
-
fixer.replaceText(property, "enum"),
|
|
10245
|
-
fixer.replaceText(arg, replacementArg)
|
|
10246
|
-
];
|
|
10247
|
-
}
|
|
10248
10202
|
return {
|
|
10249
10203
|
ImportDeclaration(node) {
|
|
10250
10204
|
if (!isZodModule2(node.source.value)) {
|
|
@@ -10265,11 +10219,9 @@ var no_zod_native_enum_default = createRule({
|
|
|
10265
10219
|
},
|
|
10266
10220
|
CallExpression(node) {
|
|
10267
10221
|
if (isZodMemberCall(node, "nativeEnum")) {
|
|
10268
|
-
const fix = buildFix(node);
|
|
10269
10222
|
context.report({
|
|
10270
10223
|
node,
|
|
10271
|
-
messageId: "nativeEnum"
|
|
10272
|
-
...fix === null ? {} : { fix }
|
|
10224
|
+
messageId: "nativeEnum"
|
|
10273
10225
|
});
|
|
10274
10226
|
return;
|
|
10275
10227
|
}
|
|
@@ -12876,11 +12828,11 @@ var PREFER_MULTI_VALUE_ZOD_LITERAL_DOCUMENTATION = {
|
|
|
12876
12828
|
rationale: "One multi-value literal expresses the same closed value domain without repeated schema wrappers.",
|
|
12877
12829
|
remediation: "Replace the union with z.literal([value1, value2, ...]).",
|
|
12878
12830
|
category: "maintainability",
|
|
12879
|
-
autofix: "
|
|
12831
|
+
autofix: "none",
|
|
12880
12832
|
limitations: [
|
|
12881
12833
|
"Bare zod imports are analyzed only when the rule option explicitly declares zodMajorVersion: 4; explicit zod/v4 entrypoints are self-declaring.",
|
|
12882
12834
|
"All-string domains are left to zod/prefer-enum-over-literal-union.",
|
|
12883
|
-
"
|
|
12835
|
+
"Migration is manual: ZodLiteral and ZodUnion expose different introspection APIs and validation error shapes even when they accept the same values."
|
|
12884
12836
|
],
|
|
12885
12837
|
examples: [
|
|
12886
12838
|
{
|
|
@@ -12903,10 +12855,6 @@ var PREFER_MULTI_VALUE_ZOD_LITERAL_DOCUMENTATION = {
|
|
|
12903
12855
|
path: "src/schema.ts",
|
|
12904
12856
|
source: "import { z } from 'zod'; export const Version = z.union([z.literal(1), z.literal(2), z.literal(3)]);"
|
|
12905
12857
|
}],
|
|
12906
|
-
fixedFiles: [{
|
|
12907
|
-
path: "src/schema.ts",
|
|
12908
|
-
source: "import { z } from 'zod'; export const Version = z.literal([1, 2, 3]);"
|
|
12909
|
-
}],
|
|
12910
12858
|
focusPath: "src/schema.ts",
|
|
12911
12859
|
expectedCount: 1,
|
|
12912
12860
|
public: true
|
|
@@ -12936,7 +12884,6 @@ var prefer_multi_value_zod_literal_default = createRule({
|
|
|
12936
12884
|
documentation: PREFER_MULTI_VALUE_ZOD_LITERAL_DOCUMENTATION,
|
|
12937
12885
|
meta: {
|
|
12938
12886
|
type: "suggestion",
|
|
12939
|
-
fixable: "code",
|
|
12940
12887
|
docs: {
|
|
12941
12888
|
description: "Use the Zod 4 multi-value literal API instead of a union of literal schemas."
|
|
12942
12889
|
},
|
|
@@ -13000,15 +12947,10 @@ var prefer_multi_value_zod_literal_default = createRule({
|
|
|
13000
12947
|
}
|
|
13001
12948
|
if (values.every(isStaticString)) return;
|
|
13002
12949
|
const namespace = node.callee.object.name;
|
|
13003
|
-
const hasComments = context.sourceCode.getCommentsInside(node).length > 0;
|
|
13004
12950
|
context.report({
|
|
13005
12951
|
node,
|
|
13006
12952
|
messageId: "useMultiValueLiteral",
|
|
13007
|
-
data: { zod: namespace }
|
|
13008
|
-
fix: hasComments ? null : (fixer) => fixer.replaceText(
|
|
13009
|
-
node,
|
|
13010
|
-
`${namespace}.literal([${values.map((value) => context.sourceCode.getText(value)).join(", ")}])`
|
|
13011
|
-
)
|
|
12953
|
+
data: { zod: namespace }
|
|
13012
12954
|
});
|
|
13013
12955
|
}
|
|
13014
12956
|
};
|
|
@@ -15439,13 +15381,14 @@ var MIN_RUN_LENGTH = 2;
|
|
|
15439
15381
|
var PREFER_WHOLE_OBJECT_ASSERTION_DOCUMENTATION = {
|
|
15440
15382
|
summary: "Collapse consecutive assertions on one object into a whole-object assertion so related mismatches are reported together.",
|
|
15441
15383
|
rationale: "One whole-object assertion presents related expectations together and produces a complete structural diff.",
|
|
15442
|
-
remediation: "
|
|
15384
|
+
remediation: "Consider one `toMatchObject` assertion for ordinary data objects. Preserve missing-property checks, identity, and getter or proxy behavior when deciding whether to combine assertions.",
|
|
15443
15385
|
category: "testing",
|
|
15444
15386
|
aliases: ["strict-test-assertions"],
|
|
15445
|
-
autofix: "
|
|
15387
|
+
autofix: "none",
|
|
15388
|
+
limitations: ["No automatic rewrite: whole-object matching can require previously absent properties and change observable getter or proxy reads."],
|
|
15446
15389
|
examples: [
|
|
15447
15390
|
{ id: "whole-object", title: "Assert the object once", outcome: "no-match", files: [{ path: "src/user.test.ts", source: "expect(user).toMatchObject({ id: 1, name: 'Ada' });" }], focusPath: "src/user.test.ts", expectedCount: 0, public: true },
|
|
15448
|
-
{ id: "member-run", title: "
|
|
15391
|
+
{ id: "member-run", title: "Consider grouping related data properties", outcome: "match", files: [{ path: "src/user.test.ts", source: "expect(user.id).toBe(1);\nexpect(user.name).toBe('Ada');" }], focusPath: "src/user.test.ts", expectedCount: 1, public: true }
|
|
15449
15392
|
]
|
|
15450
15393
|
};
|
|
15451
15394
|
function literalText(node, getText) {
|
|
@@ -15501,9 +15444,8 @@ var prefer_whole_object_assertion_default = createRule({
|
|
|
15501
15444
|
docs: {
|
|
15502
15445
|
description: "Collapse consecutive assertions on one object into a whole-object assertion so related mismatches are reported together."
|
|
15503
15446
|
},
|
|
15504
|
-
fixable: "code",
|
|
15505
15447
|
messages: {
|
|
15506
|
-
combineAssertions: "
|
|
15448
|
+
combineAssertions: "Consider combining these {{count}} assertions on `{{receiver}}` with `toMatchObject` when structural matching preserves property presence and getter or proxy behavior.",
|
|
15507
15449
|
assertArrayOnce: "These {{count}} assertions check `{{receiver}}[0]`\u2026`{{receiver}}[{{last}}]` one at a time, which never checks how long `{{receiver}}` is \u2014 extra elements pass unnoticed. Assert the array once: `expect({{receiver}}).{{matcher}}([ \u2026 ])`."
|
|
15508
15450
|
},
|
|
15509
15451
|
schema: []
|
|
@@ -15574,11 +15516,6 @@ var prefer_whole_object_assertion_default = createRule({
|
|
|
15574
15516
|
expectedIsLiteral: literal !== null
|
|
15575
15517
|
};
|
|
15576
15518
|
}
|
|
15577
|
-
function hasInterveningComment(run) {
|
|
15578
|
-
return run.some(
|
|
15579
|
-
(assertion, index) => sourceCode.getCommentsInside(assertion.statement).length > 0 || index > 0 && sourceCode.getCommentsBefore(assertion.statement).length > 0
|
|
15580
|
-
);
|
|
15581
|
-
}
|
|
15582
15519
|
function reportPropertyRun(run) {
|
|
15583
15520
|
const tree = /* @__PURE__ */ new Map();
|
|
15584
15521
|
const paths = [];
|
|
@@ -15625,16 +15562,10 @@ var prefer_whole_object_assertion_default = createRule({
|
|
|
15625
15562
|
return;
|
|
15626
15563
|
}
|
|
15627
15564
|
const receiverText = `${sourceCode.getText(first.receiver)}${commonPrefix.map((name) => `.${name}`).join("")}`;
|
|
15628
|
-
const renderTree = (value) => [...value.entries()].map(([name, child]) => `${name}: ${child instanceof Map ? `{ ${renderTree(child)} }` : child}`).join(", ");
|
|
15629
|
-
const properties = renderTree(tree);
|
|
15630
15565
|
context.report({
|
|
15631
15566
|
node: first.statement,
|
|
15632
15567
|
messageId: "combineAssertions",
|
|
15633
|
-
data: { count: String(run.length), receiver: receiverText }
|
|
15634
|
-
fix: hasInterveningComment(run) ? null : (fixer) => [
|
|
15635
|
-
fixer.replaceText(first.statement, `expect(${receiverText}).toMatchObject({ ${properties} });`),
|
|
15636
|
-
...run.slice(1).map((assertion) => fixer.remove(assertion.statement))
|
|
15637
|
-
]
|
|
15568
|
+
data: { count: String(run.length), receiver: receiverText }
|
|
15638
15569
|
});
|
|
15639
15570
|
}
|
|
15640
15571
|
function reportIndexRun(run) {
|
|
@@ -19758,7 +19689,7 @@ var RULES = {
|
|
|
19758
19689
|
};
|
|
19759
19690
|
var meta = {
|
|
19760
19691
|
name: "@sarj/eslint-plugin",
|
|
19761
|
-
version: "15.17.
|
|
19692
|
+
version: "15.17.8"
|
|
19762
19693
|
};
|
|
19763
19694
|
var APPLICATION_ONLY_RULES = [];
|
|
19764
19695
|
var LIBRARY_IMPORT_POLICY = ["error", {
|
|
@@ -19780,6 +19711,7 @@ var ADVISORY_RULES = [
|
|
|
19780
19711
|
"@sarj/prefer-nullish-filter-predicate",
|
|
19781
19712
|
"@sarj/prefer-shared-zod-enum",
|
|
19782
19713
|
"@sarj/prefer-switch-for-repeated-equality",
|
|
19714
|
+
"@sarj/prefer-whole-object-assertion",
|
|
19783
19715
|
"@sarj/require-interface-for-exported-class",
|
|
19784
19716
|
"@sarj/require-sql-access-class",
|
|
19785
19717
|
"@sarj/sole-export-matches-filename"
|
|
@@ -19858,7 +19790,7 @@ var RECOMMENDED_RULES = {
|
|
|
19858
19790
|
"@sarj/prefer-switch-for-repeated-equality": "warn",
|
|
19859
19791
|
"@sarj/prefer-semantic-colors": ["error", { requireSemanticTokens: true }],
|
|
19860
19792
|
"@sarj/prefer-server-actions": "error",
|
|
19861
|
-
"@sarj/prefer-whole-object-assertion": "
|
|
19793
|
+
"@sarj/prefer-whole-object-assertion": "warn",
|
|
19862
19794
|
"@sarj/repeated-static-call-cases": "error",
|
|
19863
19795
|
"@sarj/prefer-zod-infer": "error",
|
|
19864
19796
|
"@sarj/require-assert-never": "error",
|
|
@@ -19955,7 +19887,7 @@ var STRICT_RULES = {
|
|
|
19955
19887
|
"@sarj/prefer-switch-for-repeated-equality": "warn",
|
|
19956
19888
|
"@sarj/prefer-semantic-colors": ["error", { requireSemanticTokens: true }],
|
|
19957
19889
|
"@sarj/prefer-server-actions": "error",
|
|
19958
|
-
"@sarj/prefer-whole-object-assertion": "
|
|
19890
|
+
"@sarj/prefer-whole-object-assertion": "warn",
|
|
19959
19891
|
"@sarj/repeated-static-call-cases": "error",
|
|
19960
19892
|
"@sarj/prefer-zod-infer": "error",
|
|
19961
19893
|
"@sarj/require-assert-never": "error",
|
package/dist/index.d.cts
CHANGED
|
@@ -320,7 +320,7 @@ declare const RULES: {
|
|
|
320
320
|
/** @deprecated All repositories use one policy; retained for import compatibility. */
|
|
321
321
|
declare const APPLICATION_ONLY_RULES: readonly [];
|
|
322
322
|
/** Rules staged as non-blocking warnings while corpus adoption evidence accumulates. */
|
|
323
|
-
declare const ADVISORY_RULES: readonly ["@sarj/excessive-commentary", "@sarj/no-bespoke-api-case-conversion", "@sarj/no-restated-comment", "@sarj/no-restated-jsdoc", "@sarj/prefer-millisecond-control-duration-schema", "@sarj/prefer-module-level-refined-schema", "@sarj/prefer-multi-value-zod-literal", "@sarj/prefer-named-callback-domain", "@sarj/prefer-named-complex-return-type", "@sarj/prefer-node-crypto-hash", "@sarj/prefer-node-fs-promises", "@sarj/prefer-nullish-filter-predicate", "@sarj/prefer-shared-zod-enum", "@sarj/prefer-switch-for-repeated-equality", "@sarj/require-interface-for-exported-class", "@sarj/require-sql-access-class", "@sarj/sole-export-matches-filename"];
|
|
323
|
+
declare const ADVISORY_RULES: readonly ["@sarj/excessive-commentary", "@sarj/no-bespoke-api-case-conversion", "@sarj/no-restated-comment", "@sarj/no-restated-jsdoc", "@sarj/prefer-millisecond-control-duration-schema", "@sarj/prefer-module-level-refined-schema", "@sarj/prefer-multi-value-zod-literal", "@sarj/prefer-named-callback-domain", "@sarj/prefer-named-complex-return-type", "@sarj/prefer-node-crypto-hash", "@sarj/prefer-node-fs-promises", "@sarj/prefer-nullish-filter-predicate", "@sarj/prefer-shared-zod-enum", "@sarj/prefer-switch-for-repeated-equality", "@sarj/prefer-whole-object-assertion", "@sarj/require-interface-for-exported-class", "@sarj/require-sql-access-class", "@sarj/sole-export-matches-filename"];
|
|
324
324
|
declare const RECOMMENDED_RULES: {
|
|
325
325
|
readonly "no-restricted-imports": readonly ["error", {
|
|
326
326
|
readonly paths: {
|
|
@@ -582,7 +582,7 @@ declare const RECOMMENDED_RULES: {
|
|
|
582
582
|
readonly requireSemanticTokens: true;
|
|
583
583
|
}];
|
|
584
584
|
readonly "@sarj/prefer-server-actions": "error";
|
|
585
|
-
readonly "@sarj/prefer-whole-object-assertion": "
|
|
585
|
+
readonly "@sarj/prefer-whole-object-assertion": "warn";
|
|
586
586
|
readonly "@sarj/repeated-static-call-cases": "error";
|
|
587
587
|
readonly "@sarj/prefer-zod-infer": "error";
|
|
588
588
|
readonly "@sarj/require-assert-never": "error";
|
|
@@ -866,7 +866,7 @@ declare const STRICT_RULES: {
|
|
|
866
866
|
readonly requireSemanticTokens: true;
|
|
867
867
|
}];
|
|
868
868
|
readonly "@sarj/prefer-server-actions": "error";
|
|
869
|
-
readonly "@sarj/prefer-whole-object-assertion": "
|
|
869
|
+
readonly "@sarj/prefer-whole-object-assertion": "warn";
|
|
870
870
|
readonly "@sarj/repeated-static-call-cases": "error";
|
|
871
871
|
readonly "@sarj/prefer-zod-infer": "error";
|
|
872
872
|
readonly "@sarj/require-assert-never": "error";
|
|
@@ -893,7 +893,7 @@ type FlatPreset = {
|
|
|
893
893
|
declare const PLUGIN: {
|
|
894
894
|
readonly meta: {
|
|
895
895
|
readonly name: "@sarj/eslint-plugin";
|
|
896
|
-
readonly version: "15.17.
|
|
896
|
+
readonly version: "15.17.8";
|
|
897
897
|
};
|
|
898
898
|
readonly rules: {
|
|
899
899
|
readonly "excessive-commentary": DocumentedRule<readonly [], "excessive">;
|
package/dist/index.d.ts
CHANGED
|
@@ -320,7 +320,7 @@ declare const RULES: {
|
|
|
320
320
|
/** @deprecated All repositories use one policy; retained for import compatibility. */
|
|
321
321
|
declare const APPLICATION_ONLY_RULES: readonly [];
|
|
322
322
|
/** Rules staged as non-blocking warnings while corpus adoption evidence accumulates. */
|
|
323
|
-
declare const ADVISORY_RULES: readonly ["@sarj/excessive-commentary", "@sarj/no-bespoke-api-case-conversion", "@sarj/no-restated-comment", "@sarj/no-restated-jsdoc", "@sarj/prefer-millisecond-control-duration-schema", "@sarj/prefer-module-level-refined-schema", "@sarj/prefer-multi-value-zod-literal", "@sarj/prefer-named-callback-domain", "@sarj/prefer-named-complex-return-type", "@sarj/prefer-node-crypto-hash", "@sarj/prefer-node-fs-promises", "@sarj/prefer-nullish-filter-predicate", "@sarj/prefer-shared-zod-enum", "@sarj/prefer-switch-for-repeated-equality", "@sarj/require-interface-for-exported-class", "@sarj/require-sql-access-class", "@sarj/sole-export-matches-filename"];
|
|
323
|
+
declare const ADVISORY_RULES: readonly ["@sarj/excessive-commentary", "@sarj/no-bespoke-api-case-conversion", "@sarj/no-restated-comment", "@sarj/no-restated-jsdoc", "@sarj/prefer-millisecond-control-duration-schema", "@sarj/prefer-module-level-refined-schema", "@sarj/prefer-multi-value-zod-literal", "@sarj/prefer-named-callback-domain", "@sarj/prefer-named-complex-return-type", "@sarj/prefer-node-crypto-hash", "@sarj/prefer-node-fs-promises", "@sarj/prefer-nullish-filter-predicate", "@sarj/prefer-shared-zod-enum", "@sarj/prefer-switch-for-repeated-equality", "@sarj/prefer-whole-object-assertion", "@sarj/require-interface-for-exported-class", "@sarj/require-sql-access-class", "@sarj/sole-export-matches-filename"];
|
|
324
324
|
declare const RECOMMENDED_RULES: {
|
|
325
325
|
readonly "no-restricted-imports": readonly ["error", {
|
|
326
326
|
readonly paths: {
|
|
@@ -582,7 +582,7 @@ declare const RECOMMENDED_RULES: {
|
|
|
582
582
|
readonly requireSemanticTokens: true;
|
|
583
583
|
}];
|
|
584
584
|
readonly "@sarj/prefer-server-actions": "error";
|
|
585
|
-
readonly "@sarj/prefer-whole-object-assertion": "
|
|
585
|
+
readonly "@sarj/prefer-whole-object-assertion": "warn";
|
|
586
586
|
readonly "@sarj/repeated-static-call-cases": "error";
|
|
587
587
|
readonly "@sarj/prefer-zod-infer": "error";
|
|
588
588
|
readonly "@sarj/require-assert-never": "error";
|
|
@@ -866,7 +866,7 @@ declare const STRICT_RULES: {
|
|
|
866
866
|
readonly requireSemanticTokens: true;
|
|
867
867
|
}];
|
|
868
868
|
readonly "@sarj/prefer-server-actions": "error";
|
|
869
|
-
readonly "@sarj/prefer-whole-object-assertion": "
|
|
869
|
+
readonly "@sarj/prefer-whole-object-assertion": "warn";
|
|
870
870
|
readonly "@sarj/repeated-static-call-cases": "error";
|
|
871
871
|
readonly "@sarj/prefer-zod-infer": "error";
|
|
872
872
|
readonly "@sarj/require-assert-never": "error";
|
|
@@ -893,7 +893,7 @@ type FlatPreset = {
|
|
|
893
893
|
declare const PLUGIN: {
|
|
894
894
|
readonly meta: {
|
|
895
895
|
readonly name: "@sarj/eslint-plugin";
|
|
896
|
-
readonly version: "15.17.
|
|
896
|
+
readonly version: "15.17.8";
|
|
897
897
|
};
|
|
898
898
|
readonly rules: {
|
|
899
899
|
readonly "excessive-commentary": DocumentedRule<readonly [], "excessive">;
|
package/dist/index.js
CHANGED
|
@@ -10046,8 +10046,8 @@ var NO_ZOD_NATIVE_ENUM_DOCUMENTATION = {
|
|
|
10046
10046
|
rationale: "Wrapping a TypeScript enum preserves its emitted runtime object and duplicates the schema's value definition across two constructs.",
|
|
10047
10047
|
remediation: "Pass string literals directly to `z.enum` and derive the TypeScript type with `z.infer`.",
|
|
10048
10048
|
category: "maintainability",
|
|
10049
|
-
autofix: "
|
|
10050
|
-
limitations: ["
|
|
10049
|
+
autofix: "none",
|
|
10050
|
+
limitations: ["Migration is manual: replacing an enum-like object with a value array changes the public schema.enum keys and can affect consumers."],
|
|
10051
10051
|
examples: [
|
|
10052
10052
|
{
|
|
10053
10053
|
id: "zod-literal-enum",
|
|
@@ -10065,8 +10065,7 @@ var NO_ZOD_NATIVE_ENUM_DOCUMENTATION = {
|
|
|
10065
10065
|
files: [{ path: "src/status.ts", source: 'import { z } from "zod"; const S = z.nativeEnum({ Active: "active", Inactive: "inactive" });' }],
|
|
10066
10066
|
focusPath: "src/status.ts",
|
|
10067
10067
|
expectedCount: 1,
|
|
10068
|
-
public: true
|
|
10069
|
-
fixedFiles: [{ path: "src/status.ts", source: 'import { z } from "zod"; const S = z.enum(["active", "inactive"]);' }]
|
|
10068
|
+
public: true
|
|
10070
10069
|
}
|
|
10071
10070
|
]
|
|
10072
10071
|
};
|
|
@@ -10091,26 +10090,6 @@ function unwrap2(node) {
|
|
|
10091
10090
|
}
|
|
10092
10091
|
return node;
|
|
10093
10092
|
}
|
|
10094
|
-
function stringValueTexts(node, sourceCode) {
|
|
10095
|
-
const texts = [];
|
|
10096
|
-
for (const prop of node.properties) {
|
|
10097
|
-
if (prop.type !== AST_NODE_TYPES41.Property) {
|
|
10098
|
-
return null;
|
|
10099
|
-
}
|
|
10100
|
-
if (prop.computed || prop.shorthand || prop.method || prop.kind !== "init") {
|
|
10101
|
-
return null;
|
|
10102
|
-
}
|
|
10103
|
-
const value = prop.value;
|
|
10104
|
-
if (value.type !== AST_NODE_TYPES41.Literal || typeof value.value !== "string") {
|
|
10105
|
-
return null;
|
|
10106
|
-
}
|
|
10107
|
-
const text = sourceCode.getText(value);
|
|
10108
|
-
if (!texts.includes(text)) {
|
|
10109
|
-
texts.push(text);
|
|
10110
|
-
}
|
|
10111
|
-
}
|
|
10112
|
-
return texts.length > 0 ? texts : null;
|
|
10113
|
-
}
|
|
10114
10093
|
function resolvesToLocalEnum(node, scope) {
|
|
10115
10094
|
let current = scope;
|
|
10116
10095
|
while (current !== null) {
|
|
@@ -10142,7 +10121,6 @@ var no_zod_native_enum_default = createRule({
|
|
|
10142
10121
|
documentation: NO_ZOD_NATIVE_ENUM_DOCUMENTATION,
|
|
10143
10122
|
meta: {
|
|
10144
10123
|
type: "suggestion",
|
|
10145
|
-
fixable: "code",
|
|
10146
10124
|
docs: {
|
|
10147
10125
|
description: 'Disallow `z.nativeEnum()` (and `z.enum()` over a TypeScript enum); use `z.enum(["a", "b"])` with a string-literal union instead.'
|
|
10148
10126
|
},
|
|
@@ -10187,30 +10165,6 @@ var no_zod_native_enum_default = createRule({
|
|
|
10187
10165
|
}
|
|
10188
10166
|
return false;
|
|
10189
10167
|
}
|
|
10190
|
-
function buildFix(node) {
|
|
10191
|
-
const callee = node.callee;
|
|
10192
|
-
if (callee.type !== AST_NODE_TYPES41.MemberExpression || callee.property.type !== AST_NODE_TYPES41.Identifier) {
|
|
10193
|
-
return null;
|
|
10194
|
-
}
|
|
10195
|
-
const arg = node.arguments[0];
|
|
10196
|
-
if (arg === void 0 || node.arguments.length !== 1 || arg.type === AST_NODE_TYPES41.SpreadElement) {
|
|
10197
|
-
return null;
|
|
10198
|
-
}
|
|
10199
|
-
const inner = unwrap2(arg);
|
|
10200
|
-
if (inner.type !== AST_NODE_TYPES41.ObjectExpression) {
|
|
10201
|
-
return null;
|
|
10202
|
-
}
|
|
10203
|
-
const values = stringValueTexts(inner, sourceCode);
|
|
10204
|
-
if (values === null) {
|
|
10205
|
-
return null;
|
|
10206
|
-
}
|
|
10207
|
-
const property = callee.property;
|
|
10208
|
-
const replacementArg = `[${values.join(", ")}]`;
|
|
10209
|
-
return (fixer) => [
|
|
10210
|
-
fixer.replaceText(property, "enum"),
|
|
10211
|
-
fixer.replaceText(arg, replacementArg)
|
|
10212
|
-
];
|
|
10213
|
-
}
|
|
10214
10168
|
return {
|
|
10215
10169
|
ImportDeclaration(node) {
|
|
10216
10170
|
if (!isZodModule2(node.source.value)) {
|
|
@@ -10231,11 +10185,9 @@ var no_zod_native_enum_default = createRule({
|
|
|
10231
10185
|
},
|
|
10232
10186
|
CallExpression(node) {
|
|
10233
10187
|
if (isZodMemberCall(node, "nativeEnum")) {
|
|
10234
|
-
const fix = buildFix(node);
|
|
10235
10188
|
context.report({
|
|
10236
10189
|
node,
|
|
10237
|
-
messageId: "nativeEnum"
|
|
10238
|
-
...fix === null ? {} : { fix }
|
|
10190
|
+
messageId: "nativeEnum"
|
|
10239
10191
|
});
|
|
10240
10192
|
return;
|
|
10241
10193
|
}
|
|
@@ -12854,11 +12806,11 @@ var PREFER_MULTI_VALUE_ZOD_LITERAL_DOCUMENTATION = {
|
|
|
12854
12806
|
rationale: "One multi-value literal expresses the same closed value domain without repeated schema wrappers.",
|
|
12855
12807
|
remediation: "Replace the union with z.literal([value1, value2, ...]).",
|
|
12856
12808
|
category: "maintainability",
|
|
12857
|
-
autofix: "
|
|
12809
|
+
autofix: "none",
|
|
12858
12810
|
limitations: [
|
|
12859
12811
|
"Bare zod imports are analyzed only when the rule option explicitly declares zodMajorVersion: 4; explicit zod/v4 entrypoints are self-declaring.",
|
|
12860
12812
|
"All-string domains are left to zod/prefer-enum-over-literal-union.",
|
|
12861
|
-
"
|
|
12813
|
+
"Migration is manual: ZodLiteral and ZodUnion expose different introspection APIs and validation error shapes even when they accept the same values."
|
|
12862
12814
|
],
|
|
12863
12815
|
examples: [
|
|
12864
12816
|
{
|
|
@@ -12881,10 +12833,6 @@ var PREFER_MULTI_VALUE_ZOD_LITERAL_DOCUMENTATION = {
|
|
|
12881
12833
|
path: "src/schema.ts",
|
|
12882
12834
|
source: "import { z } from 'zod'; export const Version = z.union([z.literal(1), z.literal(2), z.literal(3)]);"
|
|
12883
12835
|
}],
|
|
12884
|
-
fixedFiles: [{
|
|
12885
|
-
path: "src/schema.ts",
|
|
12886
|
-
source: "import { z } from 'zod'; export const Version = z.literal([1, 2, 3]);"
|
|
12887
|
-
}],
|
|
12888
12836
|
focusPath: "src/schema.ts",
|
|
12889
12837
|
expectedCount: 1,
|
|
12890
12838
|
public: true
|
|
@@ -12914,7 +12862,6 @@ var prefer_multi_value_zod_literal_default = createRule({
|
|
|
12914
12862
|
documentation: PREFER_MULTI_VALUE_ZOD_LITERAL_DOCUMENTATION,
|
|
12915
12863
|
meta: {
|
|
12916
12864
|
type: "suggestion",
|
|
12917
|
-
fixable: "code",
|
|
12918
12865
|
docs: {
|
|
12919
12866
|
description: "Use the Zod 4 multi-value literal API instead of a union of literal schemas."
|
|
12920
12867
|
},
|
|
@@ -12978,15 +12925,10 @@ var prefer_multi_value_zod_literal_default = createRule({
|
|
|
12978
12925
|
}
|
|
12979
12926
|
if (values.every(isStaticString)) return;
|
|
12980
12927
|
const namespace = node.callee.object.name;
|
|
12981
|
-
const hasComments = context.sourceCode.getCommentsInside(node).length > 0;
|
|
12982
12928
|
context.report({
|
|
12983
12929
|
node,
|
|
12984
12930
|
messageId: "useMultiValueLiteral",
|
|
12985
|
-
data: { zod: namespace }
|
|
12986
|
-
fix: hasComments ? null : (fixer) => fixer.replaceText(
|
|
12987
|
-
node,
|
|
12988
|
-
`${namespace}.literal([${values.map((value) => context.sourceCode.getText(value)).join(", ")}])`
|
|
12989
|
-
)
|
|
12931
|
+
data: { zod: namespace }
|
|
12990
12932
|
});
|
|
12991
12933
|
}
|
|
12992
12934
|
};
|
|
@@ -15425,13 +15367,14 @@ var MIN_RUN_LENGTH = 2;
|
|
|
15425
15367
|
var PREFER_WHOLE_OBJECT_ASSERTION_DOCUMENTATION = {
|
|
15426
15368
|
summary: "Collapse consecutive assertions on one object into a whole-object assertion so related mismatches are reported together.",
|
|
15427
15369
|
rationale: "One whole-object assertion presents related expectations together and produces a complete structural diff.",
|
|
15428
|
-
remediation: "
|
|
15370
|
+
remediation: "Consider one `toMatchObject` assertion for ordinary data objects. Preserve missing-property checks, identity, and getter or proxy behavior when deciding whether to combine assertions.",
|
|
15429
15371
|
category: "testing",
|
|
15430
15372
|
aliases: ["strict-test-assertions"],
|
|
15431
|
-
autofix: "
|
|
15373
|
+
autofix: "none",
|
|
15374
|
+
limitations: ["No automatic rewrite: whole-object matching can require previously absent properties and change observable getter or proxy reads."],
|
|
15432
15375
|
examples: [
|
|
15433
15376
|
{ id: "whole-object", title: "Assert the object once", outcome: "no-match", files: [{ path: "src/user.test.ts", source: "expect(user).toMatchObject({ id: 1, name: 'Ada' });" }], focusPath: "src/user.test.ts", expectedCount: 0, public: true },
|
|
15434
|
-
{ id: "member-run", title: "
|
|
15377
|
+
{ id: "member-run", title: "Consider grouping related data properties", outcome: "match", files: [{ path: "src/user.test.ts", source: "expect(user.id).toBe(1);\nexpect(user.name).toBe('Ada');" }], focusPath: "src/user.test.ts", expectedCount: 1, public: true }
|
|
15435
15378
|
]
|
|
15436
15379
|
};
|
|
15437
15380
|
function literalText(node, getText) {
|
|
@@ -15487,9 +15430,8 @@ var prefer_whole_object_assertion_default = createRule({
|
|
|
15487
15430
|
docs: {
|
|
15488
15431
|
description: "Collapse consecutive assertions on one object into a whole-object assertion so related mismatches are reported together."
|
|
15489
15432
|
},
|
|
15490
|
-
fixable: "code",
|
|
15491
15433
|
messages: {
|
|
15492
|
-
combineAssertions: "
|
|
15434
|
+
combineAssertions: "Consider combining these {{count}} assertions on `{{receiver}}` with `toMatchObject` when structural matching preserves property presence and getter or proxy behavior.",
|
|
15493
15435
|
assertArrayOnce: "These {{count}} assertions check `{{receiver}}[0]`\u2026`{{receiver}}[{{last}}]` one at a time, which never checks how long `{{receiver}}` is \u2014 extra elements pass unnoticed. Assert the array once: `expect({{receiver}}).{{matcher}}([ \u2026 ])`."
|
|
15494
15436
|
},
|
|
15495
15437
|
schema: []
|
|
@@ -15560,11 +15502,6 @@ var prefer_whole_object_assertion_default = createRule({
|
|
|
15560
15502
|
expectedIsLiteral: literal !== null
|
|
15561
15503
|
};
|
|
15562
15504
|
}
|
|
15563
|
-
function hasInterveningComment(run) {
|
|
15564
|
-
return run.some(
|
|
15565
|
-
(assertion, index) => sourceCode.getCommentsInside(assertion.statement).length > 0 || index > 0 && sourceCode.getCommentsBefore(assertion.statement).length > 0
|
|
15566
|
-
);
|
|
15567
|
-
}
|
|
15568
15505
|
function reportPropertyRun(run) {
|
|
15569
15506
|
const tree = /* @__PURE__ */ new Map();
|
|
15570
15507
|
const paths = [];
|
|
@@ -15611,16 +15548,10 @@ var prefer_whole_object_assertion_default = createRule({
|
|
|
15611
15548
|
return;
|
|
15612
15549
|
}
|
|
15613
15550
|
const receiverText = `${sourceCode.getText(first.receiver)}${commonPrefix.map((name) => `.${name}`).join("")}`;
|
|
15614
|
-
const renderTree = (value) => [...value.entries()].map(([name, child]) => `${name}: ${child instanceof Map ? `{ ${renderTree(child)} }` : child}`).join(", ");
|
|
15615
|
-
const properties = renderTree(tree);
|
|
15616
15551
|
context.report({
|
|
15617
15552
|
node: first.statement,
|
|
15618
15553
|
messageId: "combineAssertions",
|
|
15619
|
-
data: { count: String(run.length), receiver: receiverText }
|
|
15620
|
-
fix: hasInterveningComment(run) ? null : (fixer) => [
|
|
15621
|
-
fixer.replaceText(first.statement, `expect(${receiverText}).toMatchObject({ ${properties} });`),
|
|
15622
|
-
...run.slice(1).map((assertion) => fixer.remove(assertion.statement))
|
|
15623
|
-
]
|
|
15554
|
+
data: { count: String(run.length), receiver: receiverText }
|
|
15624
15555
|
});
|
|
15625
15556
|
}
|
|
15626
15557
|
function reportIndexRun(run) {
|
|
@@ -19753,7 +19684,7 @@ var RULES = {
|
|
|
19753
19684
|
};
|
|
19754
19685
|
var meta = {
|
|
19755
19686
|
name: "@sarj/eslint-plugin",
|
|
19756
|
-
version: "15.17.
|
|
19687
|
+
version: "15.17.8"
|
|
19757
19688
|
};
|
|
19758
19689
|
var APPLICATION_ONLY_RULES = [];
|
|
19759
19690
|
var LIBRARY_IMPORT_POLICY = ["error", {
|
|
@@ -19775,6 +19706,7 @@ var ADVISORY_RULES = [
|
|
|
19775
19706
|
"@sarj/prefer-nullish-filter-predicate",
|
|
19776
19707
|
"@sarj/prefer-shared-zod-enum",
|
|
19777
19708
|
"@sarj/prefer-switch-for-repeated-equality",
|
|
19709
|
+
"@sarj/prefer-whole-object-assertion",
|
|
19778
19710
|
"@sarj/require-interface-for-exported-class",
|
|
19779
19711
|
"@sarj/require-sql-access-class",
|
|
19780
19712
|
"@sarj/sole-export-matches-filename"
|
|
@@ -19853,7 +19785,7 @@ var RECOMMENDED_RULES = {
|
|
|
19853
19785
|
"@sarj/prefer-switch-for-repeated-equality": "warn",
|
|
19854
19786
|
"@sarj/prefer-semantic-colors": ["error", { requireSemanticTokens: true }],
|
|
19855
19787
|
"@sarj/prefer-server-actions": "error",
|
|
19856
|
-
"@sarj/prefer-whole-object-assertion": "
|
|
19788
|
+
"@sarj/prefer-whole-object-assertion": "warn",
|
|
19857
19789
|
"@sarj/repeated-static-call-cases": "error",
|
|
19858
19790
|
"@sarj/prefer-zod-infer": "error",
|
|
19859
19791
|
"@sarj/require-assert-never": "error",
|
|
@@ -19950,7 +19882,7 @@ var STRICT_RULES = {
|
|
|
19950
19882
|
"@sarj/prefer-switch-for-repeated-equality": "warn",
|
|
19951
19883
|
"@sarj/prefer-semantic-colors": ["error", { requireSemanticTokens: true }],
|
|
19952
19884
|
"@sarj/prefer-server-actions": "error",
|
|
19953
|
-
"@sarj/prefer-whole-object-assertion": "
|
|
19885
|
+
"@sarj/prefer-whole-object-assertion": "warn",
|
|
19954
19886
|
"@sarj/repeated-static-call-cases": "error",
|
|
19955
19887
|
"@sarj/prefer-zod-infer": "error",
|
|
19956
19888
|
"@sarj/require-assert-never": "error",
|