@sarj/eslint-plugin 5.0.0 → 6.0.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/README.md +1 -0
- package/dist/index.cjs +487 -273
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +172 -109
- package/dist/index.d.ts +172 -109
- package/dist/index.js +484 -272
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,7 +4,8 @@ import { ESLintUtils, AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
|
4
4
|
// src/rules/_paths.ts
|
|
5
5
|
var SCRIPT_FILE_RE = /([\\/]scripts[\\/])|(\.mjs$)/;
|
|
6
6
|
var STORY_FILE_RE = /\.stories\.[cm]?[jt]sx?$/i;
|
|
7
|
-
var
|
|
7
|
+
var STORY_DIR_RE = /(^|\/)stories(?:[_-][^/]*)?\//i;
|
|
8
|
+
var GENERATED_FILE_RE = /([\\/](?:generated|openapi-gen|graphql[\\/]types|vendor|vendored|external|third[-_]?party)[\\/])|(\.gen\.[cm]?[jt]sx?$)|(\.generated\.[cm]?[jt]sx?$)|(\.d\.[cm]?ts$)|(\.types\.[cm]?ts$)/;
|
|
8
9
|
var GENERATED_MARKER_RE = /(?:@generated\b|generated (?:with|by)|generated (?:graphql )?types|do not edit(?: directly| manually)?)/i;
|
|
9
10
|
function isTestFile(filename) {
|
|
10
11
|
const normalized = filename.replaceAll("\\", "/");
|
|
@@ -12,10 +13,13 @@ function isTestFile(filename) {
|
|
|
12
13
|
if (/[.\-_](test|spec|e2e)\.[cm]?[jt]sx?$/.test(base) || /\.integration\.[cm]?[jt]sx?$/.test(base)) {
|
|
13
14
|
return true;
|
|
14
15
|
}
|
|
15
|
-
return /(^|\/)(tests?|__tests__|__mocks__|fixtures
|
|
16
|
+
return /(^|\/)(tests?|__tests__|__mocks__|__fixtures__|__testfixtures__|fixtures?|e2e|integration)\//.test(
|
|
17
|
+
normalized
|
|
18
|
+
);
|
|
16
19
|
}
|
|
17
20
|
function isStoryFile(filename) {
|
|
18
|
-
|
|
21
|
+
const normalized = filename.replaceAll("\\", "/");
|
|
22
|
+
return STORY_FILE_RE.test(normalized) || STORY_DIR_RE.test(normalized);
|
|
19
23
|
}
|
|
20
24
|
function isGeneratedFile(filename, sourceText = "") {
|
|
21
25
|
return GENERATED_FILE_RE.test(filename.replaceAll("\\", "/")) || GENERATED_MARKER_RE.test(sourceText.slice(0, 2048));
|
|
@@ -7689,8 +7693,190 @@ var trailing_value_narration_default = ESLintUtils43.RuleCreator(
|
|
|
7689
7693
|
}
|
|
7690
7694
|
});
|
|
7691
7695
|
|
|
7692
|
-
// src/rules/no-
|
|
7696
|
+
// src/rules/no-type-member-comment-wall.ts
|
|
7693
7697
|
import { AST_NODE_TYPES as AST_NODE_TYPES34, ESLintUtils as ESLintUtils44 } from "@typescript-eslint/utils";
|
|
7698
|
+
var DEFAULTS = {
|
|
7699
|
+
// Below three rows "a wall" is not a fair description of what the reader sees.
|
|
7700
|
+
minCommentedMembers: 3,
|
|
7701
|
+
// A minority of commented members is a GROUP LABEL, not a wall.
|
|
7702
|
+
minCommentedRatio: 0.6,
|
|
7703
|
+
// Room for one substantive row in four; a type where a quarter of the comments
|
|
7704
|
+
// say something real is a type someone was documenting, not decorating.
|
|
7705
|
+
minRestatedRatio: 0.75,
|
|
7706
|
+
// One word beyond the member's own text. Zero is `jsdoc-restates-signature`'s
|
|
7707
|
+
// test and is already covered there; two admits definitions ("Partial match"
|
|
7708
|
+
// beside "Exact match"), which the evidence file counts.
|
|
7709
|
+
maxNovelWords: 1
|
|
7710
|
+
};
|
|
7711
|
+
var VALUE_TAG_RE = /@(?:deprecated|see|example|throws|remarks|since|default|defaultvalue|link|internal|alpha|beta|experimental|template|typeparam|inheritdoc|todo|fixme|override)\b/i;
|
|
7712
|
+
var DEFAULT_RE = /^\s*@?default\b|\bdefaults? (?:to|:)/i;
|
|
7713
|
+
var DIGIT_RE = /\d/;
|
|
7714
|
+
var UNIT_WORD_RE = /\b(?:ms|milliseconds?|seconds?|minutes?|hours?|days?|weeks?|months?|years?|bytes?|kb|mb|gb|percent|pixels?|px|utc|epoch)\b/i;
|
|
7715
|
+
var EXAMPLE_RE = /["'`]|\be\.g\.|\bi\.e\./;
|
|
7716
|
+
var BANNER_RE = /[=\-─-╿*#~_.]{3,}/;
|
|
7717
|
+
var NON_ASCII_LETTER_RE2 = /[^\p{ASCII}\p{N}\p{P}\p{Z}]/u;
|
|
7718
|
+
var STOPWORDS4 = new Set(
|
|
7719
|
+
`the a an of to for in on with and or as at by is are was be been being
|
|
7720
|
+
this that it its if whether when where which what will would can could should
|
|
7721
|
+
must may into from over about not no does do done has have had used use uses
|
|
7722
|
+
using given provided specified current new existing all any each per via
|
|
7723
|
+
instance object value values data item
|
|
7724
|
+
items element callback handler prop props param arg return
|
|
7725
|
+
returns returning result optional required true false null undefined
|
|
7726
|
+
string number boolean array list promise set map record type name`.split(/\s+/)
|
|
7727
|
+
);
|
|
7728
|
+
var WORD_RE4 = /[A-Za-z][A-Za-z0-9]*/g;
|
|
7729
|
+
var BARE_LABEL_RE = /^[A-Za-z][A-Za-z0-9]*$/;
|
|
7730
|
+
function labelStems(body) {
|
|
7731
|
+
return splitIdentifier(body).map(stem).join(" ");
|
|
7732
|
+
}
|
|
7733
|
+
function isNamedMember(node) {
|
|
7734
|
+
return (node.type === AST_NODE_TYPES34.TSPropertySignature || node.type === AST_NODE_TYPES34.TSMethodSignature) && !node.computed;
|
|
7735
|
+
}
|
|
7736
|
+
function commentBody(comment) {
|
|
7737
|
+
return comment.value.replace(/^\*+/, "").replace(/^[ \t]*\*[ \t]?/gm, "").trim();
|
|
7738
|
+
}
|
|
7739
|
+
function carriesValue(body) {
|
|
7740
|
+
return isProtected(body) || VALUE_TAG_RE.test(body) || DEFAULT_RE.test(body) || DIGIT_RE.test(body) || UNIT_WORD_RE.test(body) || EXAMPLE_RE.test(body) || BANNER_RE.test(body) || NON_ASCII_LETTER_RE2.test(body);
|
|
7741
|
+
}
|
|
7742
|
+
function knownTokens(source) {
|
|
7743
|
+
const tokens = /* @__PURE__ */ new Set();
|
|
7744
|
+
for (const identifier of source.match(/[A-Za-z_$][\w$]*/g) ?? []) {
|
|
7745
|
+
for (const part of splitIdentifier(identifier)) {
|
|
7746
|
+
tokens.add(part);
|
|
7747
|
+
tokens.add(stem(part));
|
|
7748
|
+
}
|
|
7749
|
+
}
|
|
7750
|
+
return tokens;
|
|
7751
|
+
}
|
|
7752
|
+
function novelWords(body, known) {
|
|
7753
|
+
let novel = 0;
|
|
7754
|
+
for (const word of body.match(WORD_RE4) ?? []) {
|
|
7755
|
+
const lower = word.toLowerCase();
|
|
7756
|
+
if (lower.length < 2 || STOPWORDS4.has(lower)) continue;
|
|
7757
|
+
if (!known.has(lower) && !known.has(stem(lower))) novel += 1;
|
|
7758
|
+
}
|
|
7759
|
+
return novel;
|
|
7760
|
+
}
|
|
7761
|
+
var no_type_member_comment_wall_default = ESLintUtils44.RuleCreator(
|
|
7762
|
+
(name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
7763
|
+
)({
|
|
7764
|
+
name: "no-type-member-comment-wall",
|
|
7765
|
+
meta: {
|
|
7766
|
+
type: "suggestion",
|
|
7767
|
+
docs: {
|
|
7768
|
+
description: "Flag an object type whose member comments mostly re-spell the members' own names and types."
|
|
7769
|
+
},
|
|
7770
|
+
schema: [
|
|
7771
|
+
{
|
|
7772
|
+
type: "object",
|
|
7773
|
+
additionalProperties: false,
|
|
7774
|
+
properties: {
|
|
7775
|
+
minCommentedMembers: {
|
|
7776
|
+
type: "integer",
|
|
7777
|
+
minimum: 2,
|
|
7778
|
+
description: "Fewest commented members that can count as a wall."
|
|
7779
|
+
},
|
|
7780
|
+
minCommentedRatio: {
|
|
7781
|
+
type: "number",
|
|
7782
|
+
minimum: 0,
|
|
7783
|
+
maximum: 1,
|
|
7784
|
+
description: "Least share of the type's members that must be commented; below it the comments are group labels."
|
|
7785
|
+
},
|
|
7786
|
+
minRestatedRatio: {
|
|
7787
|
+
type: "number",
|
|
7788
|
+
minimum: 0,
|
|
7789
|
+
maximum: 1,
|
|
7790
|
+
description: "Least share of the member comments that must be restatements."
|
|
7791
|
+
},
|
|
7792
|
+
maxNovelWords: {
|
|
7793
|
+
type: "integer",
|
|
7794
|
+
minimum: 0,
|
|
7795
|
+
description: "Most content words a comment may add beyond its member's own source and still count as a restatement."
|
|
7796
|
+
}
|
|
7797
|
+
}
|
|
7798
|
+
}
|
|
7799
|
+
],
|
|
7800
|
+
messages: {
|
|
7801
|
+
commentWall: "{{restated}} of this type's {{commented}} member comments only re-spell the member's own name and type \u2014 delete them, and keep the rows that say what the name cannot."
|
|
7802
|
+
}
|
|
7803
|
+
},
|
|
7804
|
+
defaultOptions: [DEFAULTS],
|
|
7805
|
+
create(context, [provided]) {
|
|
7806
|
+
const options = { ...DEFAULTS, ...provided };
|
|
7807
|
+
const sourceCode = context.sourceCode;
|
|
7808
|
+
if (isGeneratedFile(context.filename, sourceCode.text) || isTestFile(context.filename) || isStoryFile(context.filename)) {
|
|
7809
|
+
return {};
|
|
7810
|
+
}
|
|
7811
|
+
const endingOn = /* @__PURE__ */ new Map();
|
|
7812
|
+
const startingOn = /* @__PURE__ */ new Map();
|
|
7813
|
+
for (const comment of sourceCode.getAllComments()) {
|
|
7814
|
+
endingOn.set(comment.loc.end.line, comment);
|
|
7815
|
+
if (!startingOn.has(comment.loc.start.line)) startingOn.set(comment.loc.start.line, comment);
|
|
7816
|
+
}
|
|
7817
|
+
function documentingComment(member) {
|
|
7818
|
+
const beforeMember = sourceCode.getTokenBefore(member, { includeComments: false });
|
|
7819
|
+
const ownsItsLine = beforeMember === null || beforeMember.loc.end.line < member.loc.start.line;
|
|
7820
|
+
const lead = ownsItsLine ? endingOn.get(member.loc.start.line - 1) : void 0;
|
|
7821
|
+
if (lead !== void 0) {
|
|
7822
|
+
const before = sourceCode.getTokenBefore(lead, { includeComments: false });
|
|
7823
|
+
if (before === null || before.loc.end.line < lead.loc.start.line) return lead;
|
|
7824
|
+
}
|
|
7825
|
+
const trail = startingOn.get(member.loc.end.line);
|
|
7826
|
+
return trail !== void 0 && trail.range[0] > member.range[0] ? trail : void 0;
|
|
7827
|
+
}
|
|
7828
|
+
function isGroupLabel(comment, member, headsRun) {
|
|
7829
|
+
if (comment.loc.end.line >= member.loc.start.line) return false;
|
|
7830
|
+
const body = commentBody(comment);
|
|
7831
|
+
if (!BARE_LABEL_RE.test(body)) return false;
|
|
7832
|
+
if (labelStems(body) === labelStems(sourceCode.getText(member.key))) return false;
|
|
7833
|
+
const lineAbove = sourceCode.lines[comment.loc.start.line - 2];
|
|
7834
|
+
return headsRun || lineAbove !== void 0 && lineAbove.trim().length === 0;
|
|
7835
|
+
}
|
|
7836
|
+
function check(node) {
|
|
7837
|
+
const members = node.type === AST_NODE_TYPES34.TSInterfaceBody ? node.body : node.members;
|
|
7838
|
+
const named = members.filter(isNamedMember);
|
|
7839
|
+
if (named.length === 0) return;
|
|
7840
|
+
const documented = named.map((member) => ({ member, comment: documentingComment(member) }));
|
|
7841
|
+
let commented = 0;
|
|
7842
|
+
let restated = 0;
|
|
7843
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
7844
|
+
for (const [index, { member, comment }] of documented.entries()) {
|
|
7845
|
+
if (comment === void 0 || claimed.has(comment)) continue;
|
|
7846
|
+
const next = documented[index + 1];
|
|
7847
|
+
if (isGroupLabel(comment, member, next !== void 0 && next.comment === void 0)) {
|
|
7848
|
+
continue;
|
|
7849
|
+
}
|
|
7850
|
+
claimed.add(comment);
|
|
7851
|
+
commented += 1;
|
|
7852
|
+
const body = commentBody(comment);
|
|
7853
|
+
if (body.length === 0 || carriesValue(body)) continue;
|
|
7854
|
+
if (novelWords(body, knownTokens(sourceCode.getText(member))) <= options.maxNovelWords) {
|
|
7855
|
+
restated += 1;
|
|
7856
|
+
}
|
|
7857
|
+
}
|
|
7858
|
+
if (commented < options.minCommentedMembers || commented / named.length < options.minCommentedRatio || restated / commented < options.minRestatedRatio) {
|
|
7859
|
+
return;
|
|
7860
|
+
}
|
|
7861
|
+
context.report({
|
|
7862
|
+
node,
|
|
7863
|
+
loc: {
|
|
7864
|
+
start: node.loc.start,
|
|
7865
|
+
end: { line: node.loc.start.line, column: node.loc.start.column + 1 }
|
|
7866
|
+
},
|
|
7867
|
+
messageId: "commentWall",
|
|
7868
|
+
data: { restated: String(restated), commented: String(commented) }
|
|
7869
|
+
});
|
|
7870
|
+
}
|
|
7871
|
+
return {
|
|
7872
|
+
TSInterfaceBody: check,
|
|
7873
|
+
TSTypeLiteral: check
|
|
7874
|
+
};
|
|
7875
|
+
}
|
|
7876
|
+
});
|
|
7877
|
+
|
|
7878
|
+
// src/rules/no-tautological-expect.ts
|
|
7879
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES35, ESLintUtils as ESLintUtils45 } from "@typescript-eslint/utils";
|
|
7694
7880
|
var EQUALITY_MATCHERS = /* @__PURE__ */ new Set(["toBe", "toEqual", "toStrictEqual"]);
|
|
7695
7881
|
var ZERO_ARG_MATCHERS = /* @__PURE__ */ new Set([
|
|
7696
7882
|
"toBeDefined",
|
|
@@ -7704,17 +7890,17 @@ var OPERAND_PREVIEW_CHARS = 40;
|
|
|
7704
7890
|
var NUMERIC_SIGNS = /* @__PURE__ */ new Set(["-", "+"]);
|
|
7705
7891
|
function isLiteral(node) {
|
|
7706
7892
|
switch (node.type) {
|
|
7707
|
-
case
|
|
7893
|
+
case AST_NODE_TYPES35.Literal:
|
|
7708
7894
|
return true;
|
|
7709
|
-
case
|
|
7895
|
+
case AST_NODE_TYPES35.TemplateLiteral:
|
|
7710
7896
|
return node.expressions.length === 0;
|
|
7711
|
-
case
|
|
7897
|
+
case AST_NODE_TYPES35.UnaryExpression:
|
|
7712
7898
|
return NUMERIC_SIGNS.has(node.operator) && isLiteral(node.argument);
|
|
7713
|
-
case
|
|
7899
|
+
case AST_NODE_TYPES35.ArrayExpression:
|
|
7714
7900
|
return node.elements.every((element) => element !== null && isLiteral(element));
|
|
7715
|
-
case
|
|
7901
|
+
case AST_NODE_TYPES35.ObjectExpression:
|
|
7716
7902
|
return node.properties.every(
|
|
7717
|
-
(property) => property.type ===
|
|
7903
|
+
(property) => property.type === AST_NODE_TYPES35.Property && !property.computed && isLiteral(property.value)
|
|
7718
7904
|
);
|
|
7719
7905
|
default:
|
|
7720
7906
|
return false;
|
|
@@ -7722,12 +7908,12 @@ function isLiteral(node) {
|
|
|
7722
7908
|
}
|
|
7723
7909
|
function expectOperand(callee) {
|
|
7724
7910
|
const receiver = callee.object;
|
|
7725
|
-
if (receiver.type !==
|
|
7911
|
+
if (receiver.type !== AST_NODE_TYPES35.CallExpression || receiver.callee.type !== AST_NODE_TYPES35.Identifier || receiver.callee.name !== "expect" || receiver.arguments.length !== 1) {
|
|
7726
7912
|
return null;
|
|
7727
7913
|
}
|
|
7728
7914
|
return receiver.arguments[0] ?? null;
|
|
7729
7915
|
}
|
|
7730
|
-
var no_tautological_expect_default =
|
|
7916
|
+
var no_tautological_expect_default = ESLintUtils45.RuleCreator(
|
|
7731
7917
|
(name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
7732
7918
|
)({
|
|
7733
7919
|
name: "no-tautological-expect",
|
|
@@ -7754,10 +7940,10 @@ var no_tautological_expect_default = ESLintUtils44.RuleCreator(
|
|
|
7754
7940
|
return {
|
|
7755
7941
|
CallExpression(node) {
|
|
7756
7942
|
const callee = node.callee;
|
|
7757
|
-
if (callee.type !==
|
|
7943
|
+
if (callee.type !== AST_NODE_TYPES35.MemberExpression || callee.computed) {
|
|
7758
7944
|
return;
|
|
7759
7945
|
}
|
|
7760
|
-
if (callee.property.type !==
|
|
7946
|
+
if (callee.property.type !== AST_NODE_TYPES35.Identifier) {
|
|
7761
7947
|
return;
|
|
7762
7948
|
}
|
|
7763
7949
|
const matcher = callee.property.name;
|
|
@@ -7791,26 +7977,26 @@ var no_tautological_expect_default = ESLintUtils44.RuleCreator(
|
|
|
7791
7977
|
});
|
|
7792
7978
|
|
|
7793
7979
|
// src/rules/require-interface-for-injected-service.ts
|
|
7794
|
-
import { AST_NODE_TYPES as
|
|
7980
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES36, ESLintUtils as ESLintUtils46 } from "@typescript-eslint/utils";
|
|
7795
7981
|
var CONFIGISH_TYPE_RE = /(?:Options|Opts|Config|Configuration|Settings|Params|Props|Args|Env|Environment|Callbacks|Flags)$/;
|
|
7796
7982
|
var CONFIGISH_NAME_RE = /^(?:options|opts|config|configuration|settings|params|props|args|env|environment|callbacks|flags|logger|log|clock)$/i;
|
|
7797
7983
|
var HTTP_TRANSPORT_TYPE_RE = /^(?:KyInstance|AxiosInstance|Session)$/;
|
|
7798
7984
|
var TRANSPORT_WRAPPER_NAME_RE = /Client$/;
|
|
7799
7985
|
var ROUTER_FACTORY_NAME = "Router";
|
|
7800
7986
|
var FRAMEWORK_HTTP_TYPES = /* @__PURE__ */ new Set(["Request", "Response", "NextFunction"]);
|
|
7801
|
-
var isExportedClass = (node) => node.parent.type ===
|
|
7802
|
-
var qualifiedName = (name) => name.type ===
|
|
7987
|
+
var isExportedClass = (node) => node.parent.type === AST_NODE_TYPES36.ExportNamedDeclaration || node.parent.type === AST_NODE_TYPES36.ExportDefaultDeclaration;
|
|
7988
|
+
var qualifiedName = (name) => name.type === AST_NODE_TYPES36.Identifier ? name.name : name.type === AST_NODE_TYPES36.TSQualifiedName ? `${qualifiedName(name.left)}.${name.right.name}` : "";
|
|
7803
7989
|
var typeReferenceName = (annotated) => {
|
|
7804
7990
|
let target = annotated;
|
|
7805
|
-
if (target.type ===
|
|
7806
|
-
if (target.type ===
|
|
7807
|
-
if (target.type !==
|
|
7991
|
+
if (target.type === AST_NODE_TYPES36.TSParameterProperty) target = target.parameter;
|
|
7992
|
+
if (target.type === AST_NODE_TYPES36.AssignmentPattern) target = target.left;
|
|
7993
|
+
if (target.type !== AST_NODE_TYPES36.Identifier) return null;
|
|
7808
7994
|
const annotation = target.typeAnnotation?.typeAnnotation;
|
|
7809
|
-
if (annotation === void 0 || annotation.type !==
|
|
7995
|
+
if (annotation === void 0 || annotation.type !== AST_NODE_TYPES36.TSTypeReference) {
|
|
7810
7996
|
return null;
|
|
7811
7997
|
}
|
|
7812
7998
|
const { typeName } = annotation;
|
|
7813
|
-
const rightmost = typeName.type ===
|
|
7999
|
+
const rightmost = typeName.type === AST_NODE_TYPES36.Identifier ? typeName.name : typeName.type === AST_NODE_TYPES36.TSQualifiedName ? typeName.right.name : null;
|
|
7814
8000
|
if (rightmost === null) return null;
|
|
7815
8001
|
return { name: target.name, typeName: rightmost, display: qualifiedName(typeName) };
|
|
7816
8002
|
};
|
|
@@ -7820,17 +8006,17 @@ var readConstructor = (ctor) => {
|
|
|
7820
8006
|
let constructedFields = 0;
|
|
7821
8007
|
if (body !== null && body !== void 0) {
|
|
7822
8008
|
for (const statement of body.body) {
|
|
7823
|
-
if (statement.type !==
|
|
8009
|
+
if (statement.type !== AST_NODE_TYPES36.ExpressionStatement) continue;
|
|
7824
8010
|
const expression = statement.expression;
|
|
7825
|
-
if (expression.type !==
|
|
8011
|
+
if (expression.type !== AST_NODE_TYPES36.AssignmentExpression || expression.operator !== "=" || expression.left.type !== AST_NODE_TYPES36.MemberExpression || expression.left.object.type !== AST_NODE_TYPES36.ThisExpression) {
|
|
7826
8012
|
continue;
|
|
7827
8013
|
}
|
|
7828
8014
|
const source = expression.right;
|
|
7829
|
-
if (source.type ===
|
|
8015
|
+
if (source.type === AST_NODE_TYPES36.NewExpression) {
|
|
7830
8016
|
constructedFields += 1;
|
|
7831
|
-
} else if (source.type ===
|
|
8017
|
+
} else if (source.type === AST_NODE_TYPES36.Identifier) {
|
|
7832
8018
|
storedFrom.add(source.name);
|
|
7833
|
-
} else if (source.type ===
|
|
8019
|
+
} else if (source.type === AST_NODE_TYPES36.MemberExpression && source.object.type === AST_NODE_TYPES36.Identifier) {
|
|
7834
8020
|
storedFrom.add(source.object.name);
|
|
7835
8021
|
}
|
|
7836
8022
|
}
|
|
@@ -7839,7 +8025,7 @@ var readConstructor = (ctor) => {
|
|
|
7839
8025
|
for (const parameter of ctor.value.params) {
|
|
7840
8026
|
const reference = typeReferenceName(parameter);
|
|
7841
8027
|
if (reference === null) continue;
|
|
7842
|
-
const stored = parameter.type ===
|
|
8028
|
+
const stored = parameter.type === AST_NODE_TYPES36.TSParameterProperty || storedFrom.has(reference.name);
|
|
7843
8029
|
if (!stored) continue;
|
|
7844
8030
|
if (CONFIGISH_TYPE_RE.test(reference.typeName)) continue;
|
|
7845
8031
|
if (CONFIGISH_NAME_RE.test(reference.name)) continue;
|
|
@@ -7869,19 +8055,19 @@ var subtreeHas = (root, found) => {
|
|
|
7869
8055
|
return hit;
|
|
7870
8056
|
};
|
|
7871
8057
|
var isFrameworkWiring = (body) => subtreeHas(body, (node) => {
|
|
7872
|
-
if (node.type ===
|
|
8058
|
+
if (node.type === AST_NODE_TYPES36.CallExpression) {
|
|
7873
8059
|
const { callee } = node;
|
|
7874
|
-
if (callee.type ===
|
|
7875
|
-
return callee.type ===
|
|
8060
|
+
if (callee.type === AST_NODE_TYPES36.Identifier) return callee.name === ROUTER_FACTORY_NAME;
|
|
8061
|
+
return callee.type === AST_NODE_TYPES36.MemberExpression && !callee.computed && callee.property.type === AST_NODE_TYPES36.Identifier && callee.property.name === ROUTER_FACTORY_NAME;
|
|
7876
8062
|
}
|
|
7877
|
-
return node.type ===
|
|
8063
|
+
return node.type === AST_NODE_TYPES36.TSTypeReference && node.typeName.type === AST_NODE_TYPES36.TSQualifiedName && FRAMEWORK_HTTP_TYPES.has(node.typeName.right.name);
|
|
7878
8064
|
});
|
|
7879
8065
|
var stem2 = (name) => name.replace(/^I(?=[A-Z])/, "").replace(/Impl$/, "");
|
|
7880
8066
|
var fileInterfaceNames = (program) => {
|
|
7881
8067
|
const names = [];
|
|
7882
8068
|
for (const statement of program.body) {
|
|
7883
|
-
const declaration = statement.type ===
|
|
7884
|
-
if (declaration?.type ===
|
|
8069
|
+
const declaration = statement.type === AST_NODE_TYPES36.ExportNamedDeclaration ? statement.declaration : statement;
|
|
8070
|
+
if (declaration?.type === AST_NODE_TYPES36.TSInterfaceDeclaration) names.push(declaration.id.name);
|
|
7885
8071
|
}
|
|
7886
8072
|
return names;
|
|
7887
8073
|
};
|
|
@@ -7899,16 +8085,16 @@ var isTransportWrapper = (className, collaborators, program) => {
|
|
|
7899
8085
|
var publicMethodNames = (body) => {
|
|
7900
8086
|
const names = [];
|
|
7901
8087
|
for (const member of body.body) {
|
|
7902
|
-
if (member.type !==
|
|
8088
|
+
if (member.type !== AST_NODE_TYPES36.MethodDefinition) continue;
|
|
7903
8089
|
if (member.kind !== "method" || member.static) continue;
|
|
7904
8090
|
if (member.accessibility === "private" || member.accessibility === "protected") continue;
|
|
7905
|
-
if (member.key.type ===
|
|
7906
|
-
if (member.key.type ===
|
|
8091
|
+
if (member.key.type === AST_NODE_TYPES36.PrivateIdentifier) continue;
|
|
8092
|
+
if (member.key.type === AST_NODE_TYPES36.Identifier) names.push(member.key.name);
|
|
7907
8093
|
else names.push("\u2026");
|
|
7908
8094
|
}
|
|
7909
8095
|
return names;
|
|
7910
8096
|
};
|
|
7911
|
-
var require_interface_for_injected_service_default =
|
|
8097
|
+
var require_interface_for_injected_service_default = ESLintUtils46.RuleCreator(
|
|
7912
8098
|
(name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
7913
8099
|
)({
|
|
7914
8100
|
name: "require-interface-for-injected-service",
|
|
@@ -7936,7 +8122,7 @@ var require_interface_for_injected_service_default = ESLintUtils45.RuleCreator(
|
|
|
7936
8122
|
if (node.implements.length > 0) return;
|
|
7937
8123
|
if (node.decorators.length > 0) return;
|
|
7938
8124
|
const ctor = node.body.body.find(
|
|
7939
|
-
(member) => member.type ===
|
|
8125
|
+
(member) => member.type === AST_NODE_TYPES36.MethodDefinition && member.kind === "constructor"
|
|
7940
8126
|
);
|
|
7941
8127
|
if (ctor === void 0) return;
|
|
7942
8128
|
const { collaborators, constructedFields } = readConstructor(ctor);
|
|
@@ -7961,26 +8147,26 @@ var require_interface_for_injected_service_default = ESLintUtils45.RuleCreator(
|
|
|
7961
8147
|
});
|
|
7962
8148
|
|
|
7963
8149
|
// src/rules/prefer-non-nullable-collection.ts
|
|
7964
|
-
import { AST_NODE_TYPES as
|
|
8150
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES37, ESLintUtils as ESLintUtils47 } from "@typescript-eslint/utils";
|
|
7965
8151
|
var ARRAY_TYPE_NAMES = /* @__PURE__ */ new Set(["Array", "ReadonlyArray"]);
|
|
7966
8152
|
function propertyName(node) {
|
|
7967
8153
|
const key = node.key;
|
|
7968
|
-
if (key.type ===
|
|
7969
|
-
if (key.type ===
|
|
8154
|
+
if (key.type === AST_NODE_TYPES37.Identifier) return key.name;
|
|
8155
|
+
if (key.type === AST_NODE_TYPES37.Literal) return String(key.value);
|
|
7970
8156
|
return "collection";
|
|
7971
8157
|
}
|
|
7972
8158
|
function isArrayType(node) {
|
|
7973
|
-
if (node.type ===
|
|
7974
|
-
return node.type ===
|
|
8159
|
+
if (node.type === AST_NODE_TYPES37.TSArrayType) return true;
|
|
8160
|
+
return node.type === AST_NODE_TYPES37.TSTypeReference && node.typeName.type === AST_NODE_TYPES37.Identifier && ARRAY_TYPE_NAMES.has(node.typeName.name);
|
|
7975
8161
|
}
|
|
7976
8162
|
function isNullishType(node) {
|
|
7977
|
-
return node.type ===
|
|
8163
|
+
return node.type === AST_NODE_TYPES37.TSNullKeyword || node.type === AST_NODE_TYPES37.TSUndefinedKeyword;
|
|
7978
8164
|
}
|
|
7979
8165
|
function isNullableArrayOnly(node) {
|
|
7980
8166
|
const values = node.types.filter((member) => !isNullishType(member));
|
|
7981
8167
|
return values.length > 0 && values.length < node.types.length && values.every(isArrayType);
|
|
7982
8168
|
}
|
|
7983
|
-
var prefer_non_nullable_collection_default =
|
|
8169
|
+
var prefer_non_nullable_collection_default = ESLintUtils47.RuleCreator(
|
|
7984
8170
|
(name) => `https://github.com/sarj-ai/standards/tree/main/packages/typescript#${name}`
|
|
7985
8171
|
)({
|
|
7986
8172
|
name: "prefer-non-nullable-collection",
|
|
@@ -8003,7 +8189,7 @@ var prefer_non_nullable_collection_default = ESLintUtils46.RuleCreator(
|
|
|
8003
8189
|
function checkOptionalProperty(node) {
|
|
8004
8190
|
const annotation = node.typeAnnotation?.typeAnnotation;
|
|
8005
8191
|
if (annotation === void 0) return;
|
|
8006
|
-
if (annotation.type !==
|
|
8192
|
+
if (annotation.type !== AST_NODE_TYPES37.TSUnionType || !isNullableArrayOnly(annotation)) {
|
|
8007
8193
|
return;
|
|
8008
8194
|
}
|
|
8009
8195
|
context.report({
|
|
@@ -8016,7 +8202,7 @@ var prefer_non_nullable_collection_default = ESLintUtils46.RuleCreator(
|
|
|
8016
8202
|
TSPropertySignature: checkOptionalProperty,
|
|
8017
8203
|
PropertyDefinition: checkOptionalProperty,
|
|
8018
8204
|
TSTypeAliasDeclaration(node) {
|
|
8019
|
-
if (node.typeAnnotation.type !==
|
|
8205
|
+
if (node.typeAnnotation.type !== AST_NODE_TYPES37.TSUnionType) return;
|
|
8020
8206
|
if (!isNullableArrayOnly(node.typeAnnotation)) return;
|
|
8021
8207
|
context.report({
|
|
8022
8208
|
node,
|
|
@@ -8029,7 +8215,7 @@ var prefer_non_nullable_collection_default = ESLintUtils46.RuleCreator(
|
|
|
8029
8215
|
});
|
|
8030
8216
|
|
|
8031
8217
|
// src/rules/strict-test-assertions.ts
|
|
8032
|
-
import { AST_NODE_TYPES as
|
|
8218
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES38, ESLintUtils as ESLintUtils48 } from "@typescript-eslint/utils";
|
|
8033
8219
|
var MERGEABLE_MATCHERS = /* @__PURE__ */ new Set(["toBe", "toEqual", "toStrictEqual"]);
|
|
8034
8220
|
var ARRAY_MATCHERS = /* @__PURE__ */ new Set(["toEqual", "toStrictEqual"]);
|
|
8035
8221
|
var COLLECTION_PROPERTIES = /* @__PURE__ */ new Set(["length", "size"]);
|
|
@@ -8037,11 +8223,11 @@ var NUMERIC_SIGNS2 = /* @__PURE__ */ new Set(["-", "+"]);
|
|
|
8037
8223
|
var MIN_RUN_LENGTH = 2;
|
|
8038
8224
|
function literalText(node, getText) {
|
|
8039
8225
|
switch (node.type) {
|
|
8040
|
-
case
|
|
8226
|
+
case AST_NODE_TYPES38.Literal:
|
|
8041
8227
|
return "regex" in node ? null : getText(node);
|
|
8042
|
-
case
|
|
8228
|
+
case AST_NODE_TYPES38.TemplateLiteral:
|
|
8043
8229
|
return node.expressions.length === 0 ? getText(node) : null;
|
|
8044
|
-
case
|
|
8230
|
+
case AST_NODE_TYPES38.UnaryExpression:
|
|
8045
8231
|
return NUMERIC_SIGNS2.has(node.operator) && literalText(node.argument, getText) !== null ? getText(node) : null;
|
|
8046
8232
|
default:
|
|
8047
8233
|
return null;
|
|
@@ -8049,15 +8235,15 @@ function literalText(node, getText) {
|
|
|
8049
8235
|
}
|
|
8050
8236
|
function isPureReceiver(node) {
|
|
8051
8237
|
switch (node.type) {
|
|
8052
|
-
case
|
|
8053
|
-
case
|
|
8238
|
+
case AST_NODE_TYPES38.Identifier:
|
|
8239
|
+
case AST_NODE_TYPES38.ThisExpression:
|
|
8054
8240
|
return true;
|
|
8055
|
-
case
|
|
8241
|
+
case AST_NODE_TYPES38.MemberExpression:
|
|
8056
8242
|
if (node.optional) {
|
|
8057
8243
|
return false;
|
|
8058
8244
|
}
|
|
8059
8245
|
if (node.computed) {
|
|
8060
|
-
return node.property.type ===
|
|
8246
|
+
return node.property.type === AST_NODE_TYPES38.Literal && isPureReceiver(node.object);
|
|
8061
8247
|
}
|
|
8062
8248
|
return isPureReceiver(node.object);
|
|
8063
8249
|
default:
|
|
@@ -8065,12 +8251,12 @@ function isPureReceiver(node) {
|
|
|
8065
8251
|
}
|
|
8066
8252
|
}
|
|
8067
8253
|
function literalIndex(node) {
|
|
8068
|
-
if (node.type !==
|
|
8254
|
+
if (node.type !== AST_NODE_TYPES38.Literal || typeof node.value !== "number") {
|
|
8069
8255
|
return null;
|
|
8070
8256
|
}
|
|
8071
8257
|
return Number.isInteger(node.value) && node.value >= 0 ? node.value : null;
|
|
8072
8258
|
}
|
|
8073
|
-
var strict_test_assertions_default =
|
|
8259
|
+
var strict_test_assertions_default = ESLintUtils48.RuleCreator(
|
|
8074
8260
|
(name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
8075
8261
|
)({
|
|
8076
8262
|
name: "strict-test-assertions",
|
|
@@ -8093,24 +8279,24 @@ var strict_test_assertions_default = ESLintUtils47.RuleCreator(
|
|
|
8093
8279
|
}
|
|
8094
8280
|
const { sourceCode } = context;
|
|
8095
8281
|
function parseAssertion(statement) {
|
|
8096
|
-
if (statement.type !==
|
|
8282
|
+
if (statement.type !== AST_NODE_TYPES38.ExpressionStatement) {
|
|
8097
8283
|
return null;
|
|
8098
8284
|
}
|
|
8099
8285
|
const call = statement.expression;
|
|
8100
|
-
if (call.type !==
|
|
8286
|
+
if (call.type !== AST_NODE_TYPES38.CallExpression) {
|
|
8101
8287
|
return null;
|
|
8102
8288
|
}
|
|
8103
8289
|
const callee = call.callee;
|
|
8104
|
-
if (callee.type !==
|
|
8290
|
+
if (callee.type !== AST_NODE_TYPES38.MemberExpression || callee.computed || callee.property.type !== AST_NODE_TYPES38.Identifier) {
|
|
8105
8291
|
return null;
|
|
8106
8292
|
}
|
|
8107
8293
|
const matcher = callee.property.name;
|
|
8108
8294
|
const expectCall = callee.object;
|
|
8109
|
-
if (expectCall.type !==
|
|
8295
|
+
if (expectCall.type !== AST_NODE_TYPES38.CallExpression || expectCall.callee.type !== AST_NODE_TYPES38.Identifier || expectCall.callee.name !== "expect" || expectCall.arguments.length !== 1) {
|
|
8110
8296
|
return null;
|
|
8111
8297
|
}
|
|
8112
8298
|
const actual = expectCall.arguments[0];
|
|
8113
|
-
if (actual === void 0 || actual.type !==
|
|
8299
|
+
if (actual === void 0 || actual.type !== AST_NODE_TYPES38.MemberExpression || actual.optional) {
|
|
8114
8300
|
return null;
|
|
8115
8301
|
}
|
|
8116
8302
|
if (!isPureReceiver(actual.object)) {
|
|
@@ -8124,7 +8310,7 @@ var strict_test_assertions_default = ESLintUtils47.RuleCreator(
|
|
|
8124
8310
|
}
|
|
8125
8311
|
key = { kind: "index", index };
|
|
8126
8312
|
} else {
|
|
8127
|
-
if (actual.property.type !==
|
|
8313
|
+
if (actual.property.type !== AST_NODE_TYPES38.Identifier || COLLECTION_PROPERTIES.has(actual.property.name)) {
|
|
8128
8314
|
return null;
|
|
8129
8315
|
}
|
|
8130
8316
|
key = { kind: "property", name: actual.property.name };
|
|
@@ -8136,7 +8322,7 @@ var strict_test_assertions_default = ESLintUtils47.RuleCreator(
|
|
|
8136
8322
|
return null;
|
|
8137
8323
|
}
|
|
8138
8324
|
const expected = call.arguments[0];
|
|
8139
|
-
if (call.arguments.length !== 1 || expected === void 0 || expected.type ===
|
|
8325
|
+
if (call.arguments.length !== 1 || expected === void 0 || expected.type === AST_NODE_TYPES38.SpreadElement) {
|
|
8140
8326
|
return null;
|
|
8141
8327
|
}
|
|
8142
8328
|
const literal = literalText(expected, (node) => sourceCode.getText(node));
|
|
@@ -8246,8 +8432,8 @@ var strict_test_assertions_default = ESLintUtils47.RuleCreator(
|
|
|
8246
8432
|
});
|
|
8247
8433
|
|
|
8248
8434
|
// src/rules/no-async-callback-in-waitfor.ts
|
|
8249
|
-
import { AST_NODE_TYPES as
|
|
8250
|
-
var no_async_callback_in_waitfor_default =
|
|
8435
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES39, ESLintUtils as ESLintUtils49 } from "@typescript-eslint/utils";
|
|
8436
|
+
var no_async_callback_in_waitfor_default = ESLintUtils49.RuleCreator(
|
|
8251
8437
|
(name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
8252
8438
|
)({
|
|
8253
8439
|
name: "no-async-callback-in-waitfor",
|
|
@@ -8268,9 +8454,9 @@ var no_async_callback_in_waitfor_default = ESLintUtils48.RuleCreator(
|
|
|
8268
8454
|
}
|
|
8269
8455
|
return {
|
|
8270
8456
|
CallExpression(node) {
|
|
8271
|
-
if (node.callee.type ===
|
|
8457
|
+
if (node.callee.type === AST_NODE_TYPES39.Identifier && node.callee.name === "waitFor") {
|
|
8272
8458
|
const callback = node.arguments[0];
|
|
8273
|
-
if (callback && (callback.type ===
|
|
8459
|
+
if (callback && (callback.type === AST_NODE_TYPES39.ArrowFunctionExpression || callback.type === AST_NODE_TYPES39.FunctionExpression) && callback.async) {
|
|
8274
8460
|
context.report({
|
|
8275
8461
|
node: callback,
|
|
8276
8462
|
messageId: "noAsyncCallbackInWaitFor"
|
|
@@ -8283,7 +8469,7 @@ var no_async_callback_in_waitfor_default = ESLintUtils48.RuleCreator(
|
|
|
8283
8469
|
});
|
|
8284
8470
|
|
|
8285
8471
|
// src/rules/no-hand-rolled-sleep.ts
|
|
8286
|
-
import { AST_NODE_TYPES as
|
|
8472
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES40, ESLintUtils as ESLintUtils50 } from "@typescript-eslint/utils";
|
|
8287
8473
|
var GLOBAL_OBJECTS2 = /* @__PURE__ */ new Set([
|
|
8288
8474
|
"globalThis",
|
|
8289
8475
|
"window",
|
|
@@ -8302,66 +8488,66 @@ function matchesAnyPattern3(filename, patterns) {
|
|
|
8302
8488
|
return false;
|
|
8303
8489
|
}
|
|
8304
8490
|
function isSetTimeoutCallee(callee) {
|
|
8305
|
-
if (callee.type ===
|
|
8491
|
+
if (callee.type === AST_NODE_TYPES40.Identifier) {
|
|
8306
8492
|
return callee.name === "setTimeout";
|
|
8307
8493
|
}
|
|
8308
|
-
return callee.type ===
|
|
8494
|
+
return callee.type === AST_NODE_TYPES40.MemberExpression && !callee.computed && callee.property.type === AST_NODE_TYPES40.Identifier && callee.property.name === "setTimeout" && callee.object.type === AST_NODE_TYPES40.Identifier && GLOBAL_OBJECTS2.has(callee.object.name);
|
|
8309
8495
|
}
|
|
8310
8496
|
function soleCall(fn) {
|
|
8311
|
-
if (fn.body.type !==
|
|
8312
|
-
return fn.body.type ===
|
|
8497
|
+
if (fn.body.type !== AST_NODE_TYPES40.BlockStatement) {
|
|
8498
|
+
return fn.body.type === AST_NODE_TYPES40.CallExpression ? fn.body : null;
|
|
8313
8499
|
}
|
|
8314
8500
|
if (fn.body.body.length !== 1) {
|
|
8315
8501
|
return null;
|
|
8316
8502
|
}
|
|
8317
8503
|
const [only] = fn.body.body;
|
|
8318
|
-
if (only?.type !==
|
|
8504
|
+
if (only?.type !== AST_NODE_TYPES40.ExpressionStatement) {
|
|
8319
8505
|
return null;
|
|
8320
8506
|
}
|
|
8321
|
-
return only.expression.type ===
|
|
8507
|
+
return only.expression.type === AST_NODE_TYPES40.CallExpression ? only.expression : null;
|
|
8322
8508
|
}
|
|
8323
8509
|
function isTimedDelay(delay) {
|
|
8324
8510
|
if (delay === void 0) {
|
|
8325
8511
|
return false;
|
|
8326
8512
|
}
|
|
8327
|
-
if (delay.type ===
|
|
8513
|
+
if (delay.type === AST_NODE_TYPES40.Literal && typeof delay.value === "number") {
|
|
8328
8514
|
return delay.value !== 0;
|
|
8329
8515
|
}
|
|
8330
8516
|
return true;
|
|
8331
8517
|
}
|
|
8332
8518
|
function settlesWithoutValue(callback, name) {
|
|
8333
|
-
if (callback.type ===
|
|
8519
|
+
if (callback.type === AST_NODE_TYPES40.Identifier) {
|
|
8334
8520
|
return callback.name === name;
|
|
8335
8521
|
}
|
|
8336
|
-
if (callback.type !==
|
|
8522
|
+
if (callback.type !== AST_NODE_TYPES40.ArrowFunctionExpression && callback.type !== AST_NODE_TYPES40.FunctionExpression) {
|
|
8337
8523
|
return false;
|
|
8338
8524
|
}
|
|
8339
8525
|
const call = soleCall(callback);
|
|
8340
|
-
return call !== null && call.arguments.length === 0 && call.callee.type ===
|
|
8526
|
+
return call !== null && call.arguments.length === 0 && call.callee.type === AST_NODE_TYPES40.Identifier && call.callee.name === name;
|
|
8341
8527
|
}
|
|
8342
8528
|
function rejectsInCallback(callback, name) {
|
|
8343
|
-
if (callback.type ===
|
|
8529
|
+
if (callback.type === AST_NODE_TYPES40.Identifier) {
|
|
8344
8530
|
return callback.name === name;
|
|
8345
8531
|
}
|
|
8346
|
-
if (callback.type !==
|
|
8532
|
+
if (callback.type !== AST_NODE_TYPES40.ArrowFunctionExpression && callback.type !== AST_NODE_TYPES40.FunctionExpression) {
|
|
8347
8533
|
return false;
|
|
8348
8534
|
}
|
|
8349
8535
|
const call = soleCall(callback);
|
|
8350
|
-
return call !== null && call.callee.type ===
|
|
8536
|
+
return call !== null && call.callee.type === AST_NODE_TYPES40.Identifier && call.callee.name === name;
|
|
8351
8537
|
}
|
|
8352
8538
|
function parameterName(fn, index) {
|
|
8353
8539
|
const parameter = fn.params[index];
|
|
8354
|
-
return parameter?.type ===
|
|
8540
|
+
return parameter?.type === AST_NODE_TYPES40.Identifier ? parameter.name : null;
|
|
8355
8541
|
}
|
|
8356
8542
|
function isRaceArm(node) {
|
|
8357
8543
|
const array = node.parent;
|
|
8358
|
-
if (array?.type !==
|
|
8544
|
+
if (array?.type !== AST_NODE_TYPES40.ArrayExpression) {
|
|
8359
8545
|
return false;
|
|
8360
8546
|
}
|
|
8361
8547
|
const call = array.parent;
|
|
8362
|
-
return call?.type ===
|
|
8548
|
+
return call?.type === AST_NODE_TYPES40.CallExpression && call.arguments[0] === array && call.callee.type === AST_NODE_TYPES40.MemberExpression && !call.callee.computed && call.callee.object.type === AST_NODE_TYPES40.Identifier && call.callee.object.name === "Promise" && call.callee.property.type === AST_NODE_TYPES40.Identifier && RACE_METHODS.has(call.callee.property.name);
|
|
8363
8549
|
}
|
|
8364
|
-
var no_hand_rolled_sleep_default =
|
|
8550
|
+
var no_hand_rolled_sleep_default = ESLintUtils50.RuleCreator(
|
|
8365
8551
|
(name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
8366
8552
|
)({
|
|
8367
8553
|
name: "no-hand-rolled-sleep",
|
|
@@ -8409,10 +8595,10 @@ var no_hand_rolled_sleep_default = ESLintUtils49.RuleCreator(
|
|
|
8409
8595
|
}
|
|
8410
8596
|
const program = sourceCode.ast;
|
|
8411
8597
|
for (const statement of program.body) {
|
|
8412
|
-
if (statement.type ===
|
|
8598
|
+
if (statement.type === AST_NODE_TYPES40.ExpressionStatement && statement.expression.type === AST_NODE_TYPES40.Literal && statement.expression.value === "use client") {
|
|
8413
8599
|
return true;
|
|
8414
8600
|
}
|
|
8415
|
-
if (statement.type ===
|
|
8601
|
+
if (statement.type === AST_NODE_TYPES40.ImportDeclaration && typeof statement.source.value === "string" && CLIENT_ONLY_MODULES.test(statement.source.value)) {
|
|
8416
8602
|
return true;
|
|
8417
8603
|
}
|
|
8418
8604
|
}
|
|
@@ -8428,11 +8614,11 @@ var no_hand_rolled_sleep_default = ESLintUtils49.RuleCreator(
|
|
|
8428
8614
|
};
|
|
8429
8615
|
return {
|
|
8430
8616
|
NewExpression(node) {
|
|
8431
|
-
if (node.callee.type !==
|
|
8617
|
+
if (node.callee.type !== AST_NODE_TYPES40.Identifier || node.callee.name !== "Promise") {
|
|
8432
8618
|
return;
|
|
8433
8619
|
}
|
|
8434
8620
|
const executor = node.arguments[0];
|
|
8435
|
-
if (executor?.type !==
|
|
8621
|
+
if (executor?.type !== AST_NODE_TYPES40.ArrowFunctionExpression && executor?.type !== AST_NODE_TYPES40.FunctionExpression) {
|
|
8436
8622
|
return;
|
|
8437
8623
|
}
|
|
8438
8624
|
const call = soleCall(executor);
|
|
@@ -8505,200 +8691,226 @@ var rules = {
|
|
|
8505
8691
|
"jsdoc-restates-signature": jsdoc_restates_signature_default,
|
|
8506
8692
|
"no-restated-comment": no_restated_comment_default,
|
|
8507
8693
|
"trailing-value-narration": trailing_value_narration_default,
|
|
8694
|
+
"no-type-member-comment-wall": no_type_member_comment_wall_default,
|
|
8508
8695
|
"no-tautological-expect": no_tautological_expect_default,
|
|
8509
8696
|
"require-interface-for-injected-service": require_interface_for_injected_service_default,
|
|
8510
8697
|
"strict-test-assertions": strict_test_assertions_default,
|
|
8511
8698
|
"prefer-non-nullable-collection": prefer_non_nullable_collection_default,
|
|
8512
8699
|
"no-async-callback-in-waitfor": no_async_callback_in_waitfor_default
|
|
8513
8700
|
};
|
|
8701
|
+
var meta = {
|
|
8702
|
+
name: "@sarj/eslint-plugin",
|
|
8703
|
+
version: "6.0.0"
|
|
8704
|
+
};
|
|
8705
|
+
var recommendedRules = {
|
|
8706
|
+
"@sarj/zod-naming-convention": "warn",
|
|
8707
|
+
"@sarj/require-assert-never": "error",
|
|
8708
|
+
"@sarj/require-zod-form-validation": "error",
|
|
8709
|
+
"@sarj/enforce-file-structure": "warn",
|
|
8710
|
+
"@sarj/no-client-side-data-fetching": "warn",
|
|
8711
|
+
"@sarj/prefer-server-actions": "warn",
|
|
8712
|
+
"@sarj/no-unnecessary-use-client": "warn",
|
|
8713
|
+
"@sarj/prefer-schema-for-api-payload": "warn",
|
|
8714
|
+
// Distilled from sarj-audit skills — warn in recommended, error in strict.
|
|
8715
|
+
"@sarj/no-sentinel-return-on-catch": "warn",
|
|
8716
|
+
"@sarj/no-log-only-catch": "warn",
|
|
8717
|
+
"@sarj/no-insecure-random-id": "warn",
|
|
8718
|
+
"@sarj/no-json-stringify-error": "warn",
|
|
8719
|
+
"@sarj/no-string-concat-in-loop": "warn",
|
|
8720
|
+
"@sarj/prefer-discriminated-union": "warn",
|
|
8721
|
+
"@sarj/no-comment-cruft": "warn",
|
|
8722
|
+
// Frontend / styling — distilled from frontend PR-review mining.
|
|
8723
|
+
"@sarj/prefer-semantic-colors": [
|
|
8724
|
+
"warn",
|
|
8725
|
+
{ requireSemanticTokens: true }
|
|
8726
|
+
],
|
|
8727
|
+
// Ported from sarj-python-lint (SARJ), corpus-validated FP~0.
|
|
8728
|
+
"@sarj/no-fat-try-blocks": "warn",
|
|
8729
|
+
"@sarj/no-cors-wildcard-with-credentials": "warn",
|
|
8730
|
+
"@sarj/no-secret-in-log": "warn",
|
|
8731
|
+
"@sarj/no-unsafe-mock-casting": "warn",
|
|
8732
|
+
"@sarj/prefer-string-literal-union": "warn",
|
|
8733
|
+
"@sarj/prefer-zod-enum": "warn",
|
|
8734
|
+
// A type hand-written beside the Zod schema it restates drifts the
|
|
8735
|
+
// moment the schema gains a field. 30,759-file, 17-repo sweep
|
|
8736
|
+
// (2026-07): 5 reports, 5 true positives, all in public repos.
|
|
8737
|
+
// `requireIdenticalShape: false` widens it to 8 reports, 1 of them noise.
|
|
8738
|
+
"@sarj/prefer-zod-infer": "warn",
|
|
8739
|
+
// Mined from 2y of PR review feedback + 5-repo code-smell audit (2026-07).
|
|
8740
|
+
"@sarj/require-fetch-timeout": "warn",
|
|
8741
|
+
"@sarj/no-silent-promise-catch": "warn",
|
|
8742
|
+
// Second SARJ port wave — the TS/Python parity gap. Each targets a
|
|
8743
|
+
// defect class seen in production Workers code: timing-leaky secret
|
|
8744
|
+
// compares, non-idempotent store writes under queue redelivery,
|
|
8745
|
+
// O(N) pagination, implicit row contracts, flaky timed tests.
|
|
8746
|
+
"@sarj/prefer-constant-time-secret-compare": "error",
|
|
8747
|
+
"@sarj/store-insert-requires-on-conflict": "warn",
|
|
8748
|
+
"@sarj/no-offset-pagination": "warn",
|
|
8749
|
+
"@sarj/no-select-star": "warn",
|
|
8750
|
+
// Uncancellable hand-rolled timers. Verified against the shipped
|
|
8751
|
+
// strict config (205 enabled rules) that nothing already reports this
|
|
8752
|
+
// position; `unicorn` 72 has no promisified-timer rule at all.
|
|
8753
|
+
"@sarj/no-hand-rolled-sleep": "warn",
|
|
8754
|
+
"@sarj/no-sleep-in-test-body": "warn",
|
|
8755
|
+
"@sarj/no-conditional-in-test": "warn",
|
|
8756
|
+
"@sarj/no-repeated-string-literal": "warn",
|
|
8757
|
+
"@sarj/no-positional-tuple-return": "warn",
|
|
8758
|
+
// Injection guard — low FP, applies to any repo touching SQL.
|
|
8759
|
+
"@sarj/no-dynamic-sql": "warn",
|
|
8760
|
+
// Mined from two years of PR review (SARJ-928). Schema-layer sibling of
|
|
8761
|
+
// `no-enum`; autofixable for inline string-literal objects.
|
|
8762
|
+
"@sarj/no-zod-native-enum": "warn",
|
|
8763
|
+
// Mined from two years of PR review — the single most frequent uncovered
|
|
8764
|
+
// theme (~37 PRs). Measured 17 hits / 1085 real TS files, all true
|
|
8765
|
+
// positives, so it is safe to run everywhere.
|
|
8766
|
+
"@sarj/prefer-module-level-constant": "warn",
|
|
8767
|
+
// Anti-comment-verbosity family (2026-07), from a 37,918-comment,
|
|
8768
|
+
// nine-repo measurement study. Each is a deletion-class finding, so each
|
|
8769
|
+
// was validated against pydantic / trio / attrs as well as the maintained
|
|
8770
|
+
// repos: `no-restated-comment` 0 hits in the flagship first-party
|
|
8771
|
+
// repo and 4 in the three famous
|
|
8772
|
+
// corpora combined; `trailing-value-narration` 18 hits, 18 true
|
|
8773
|
+
// positives; `jsdoc-restates-signature` 36 hits, 0 measured false
|
|
8774
|
+
// positives, and it offers a suggestion rather than a `--fix` because a
|
|
8775
|
+
// wrong deletion is silent information loss.
|
|
8776
|
+
"@sarj/no-restated-comment": "warn",
|
|
8777
|
+
"@sarj/jsdoc-restates-signature": "warn",
|
|
8778
|
+
"@sarj/trailing-value-narration": "warn",
|
|
8779
|
+
// The VOLUME arm of the same family (2026-07). Its siblings judge one
|
|
8780
|
+
// comment at a time and can only condemn one that adds nothing; this
|
|
8781
|
+
// one judges a TYPE, so it can report ten rows that each add a word.
|
|
8782
|
+
// 33 OSS TS repos / 46,861 files: 22 findings, all read, 0 false; zero
|
|
8783
|
+
// across ten first-party repos, where the generated-file sniff alone
|
|
8784
|
+
// removed 321 of the 407 raw hits. Measurements and the six false
|
|
8785
|
+
// positives that shaped the guards: docs/rules/no-type-member-comment-wall.md
|
|
8786
|
+
"@sarj/no-type-member-comment-wall": "warn",
|
|
8787
|
+
// The TS half of SARJ057 (2026-07). Python has caught the
|
|
8788
|
+
// assertion-FREE test since 0.15.0 (SARJ043) and had no TS
|
|
8789
|
+
// counterpart, which is how `expect(true).toBe(true); // placeholder`
|
|
8790
|
+
// survived in a first-party repo: the file HAS an assertion.
|
|
8791
|
+
// Measured across 5,819 .ts/.tsx files (1,003 of them test files) in
|
|
8792
|
+
// six internal repos plus got / hono / swr / trpc: 3 hits, 3 true
|
|
8793
|
+
// positives, 0 false positives.
|
|
8794
|
+
"@sarj/no-tautological-expect": "warn",
|
|
8795
|
+
// Substitutability: an exported service class with injected
|
|
8796
|
+
// collaborators and no interface above it can only be tested by
|
|
8797
|
+
// mocking. 11-repo sweep: 229 exported classes, 82% already carry a
|
|
8798
|
+
// port, 29 fire, 28 of them true positives.
|
|
8799
|
+
"@sarj/require-interface-for-injected-service": "warn",
|
|
8800
|
+
"@sarj/prefer-non-nullable-collection": "warn",
|
|
8801
|
+
"@sarj/no-async-callback-in-waitfor": "warn",
|
|
8802
|
+
// `strict-test-assertions` shipped in `rules` but in NEITHER preset, so a
|
|
8803
|
+
// consumer using `configs.recommended`/`configs.strict` instead of the shared
|
|
8804
|
+
// `eslint.strict.mjs` had silently never run it. `flat-presets.test.ts`
|
|
8805
|
+
// asserts strict wires every rule the plugin ships, so it cannot recur.
|
|
8806
|
+
"@sarj/strict-test-assertions": "warn"
|
|
8807
|
+
};
|
|
8808
|
+
var strictRules = {
|
|
8809
|
+
"@sarj/zod-naming-convention": "error",
|
|
8810
|
+
"@sarj/require-assert-never": "error",
|
|
8811
|
+
"@sarj/require-zod-form-validation": "error",
|
|
8812
|
+
"@sarj/enforce-file-structure": "error",
|
|
8813
|
+
"@sarj/no-raw-env": "error",
|
|
8814
|
+
"@sarj/no-enum": "error",
|
|
8815
|
+
"@sarj/no-client-side-data-fetching": "error",
|
|
8816
|
+
"@sarj/prefer-server-actions": "error",
|
|
8817
|
+
"@sarj/no-unnecessary-use-client": "error",
|
|
8818
|
+
"@sarj/prefer-schema-for-api-payload": "error",
|
|
8819
|
+
// Distilled from sarj-audit skills.
|
|
8820
|
+
"@sarj/no-sentinel-return-on-catch": "error",
|
|
8821
|
+
"@sarj/no-log-only-catch": "error",
|
|
8822
|
+
"@sarj/no-insecure-random-id": "error",
|
|
8823
|
+
"@sarj/no-json-stringify-error": "error",
|
|
8824
|
+
"@sarj/no-string-concat-in-loop": "error",
|
|
8825
|
+
"@sarj/prefer-discriminated-union": "error",
|
|
8826
|
+
"@sarj/no-comment-cruft": "error",
|
|
8827
|
+
// Frontend / styling — distilled from frontend PR-review mining. Stylistic,
|
|
8828
|
+
// no autofix → warn (rollout should prove the FP rate before raising it).
|
|
8829
|
+
"@sarj/prefer-semantic-colors": [
|
|
8830
|
+
"error",
|
|
8831
|
+
{ requireSemanticTokens: true }
|
|
8832
|
+
],
|
|
8833
|
+
// Ported from sarj-python-lint (SARJ), corpus-validated FP~0.
|
|
8834
|
+
"@sarj/no-fat-try-blocks": "error",
|
|
8835
|
+
"@sarj/no-cors-wildcard-with-credentials": "error",
|
|
8836
|
+
"@sarj/no-secret-in-log": "error",
|
|
8837
|
+
"@sarj/no-unsafe-mock-casting": "error",
|
|
8838
|
+
// Promoted to error 2026-07-25 — strict means strict (user directive).
|
|
8839
|
+
"@sarj/prefer-string-literal-union": "error",
|
|
8840
|
+
"@sarj/prefer-zod-enum": "error",
|
|
8841
|
+
// See the `recommended` block for the measured counts.
|
|
8842
|
+
"@sarj/prefer-zod-infer": "error",
|
|
8843
|
+
// Mined from 2y of PR review feedback + 5-repo code-smell audit (2026-07).
|
|
8844
|
+
"@sarj/require-fetch-timeout": "error",
|
|
8845
|
+
"@sarj/no-silent-promise-catch": "error",
|
|
8846
|
+
// Second SARJ port wave — the TS/Python parity gap.
|
|
8847
|
+
"@sarj/prefer-constant-time-secret-compare": "error",
|
|
8848
|
+
"@sarj/store-insert-requires-on-conflict": "error",
|
|
8849
|
+
"@sarj/no-offset-pagination": "error",
|
|
8850
|
+
"@sarj/no-select-star": "error",
|
|
8851
|
+
// Uncancellable hand-rolled timers. Verified against the shipped
|
|
8852
|
+
// strict config (205 enabled rules) that nothing already reports this
|
|
8853
|
+
// position; `unicorn` 72 has no promisified-timer rule at all.
|
|
8854
|
+
"@sarj/no-hand-rolled-sleep": "error",
|
|
8855
|
+
"@sarj/no-sleep-in-test-body": "error",
|
|
8856
|
+
"@sarj/no-conditional-in-test": "error",
|
|
8857
|
+
"@sarj/no-repeated-string-literal": "error",
|
|
8858
|
+
// API-shape advice rather than a runtime defect — a corpus sweep found its
|
|
8859
|
+
// only hits are parser `[value, cursor]` returns, which are conventional.
|
|
8860
|
+
// Warn even in strict until a rollout justifies more.
|
|
8861
|
+
"@sarj/no-positional-tuple-return": "error",
|
|
8862
|
+
"@sarj/no-dynamic-sql": "error",
|
|
8863
|
+
// Architectural: both need per-repo config to be meaningful, so they
|
|
8864
|
+
// are strict-only. `no-storage-in-stateless-modules` is a no-op until
|
|
8865
|
+
// its `modules` option names the directories a team declared stateless;
|
|
8866
|
+
// `no-raw-fetch-outside-clients` defaults to the `clients/` convention
|
|
8867
|
+
// and takes an `allow` list for repos that lay their client layer out
|
|
8868
|
+
// differently.
|
|
8869
|
+
"@sarj/no-raw-fetch-outside-clients": "error",
|
|
8870
|
+
"@sarj/no-storage-in-stateless-modules": "error",
|
|
8871
|
+
// Mined from two years of PR review (SARJ-928).
|
|
8872
|
+
"@sarj/no-zod-native-enum": "error",
|
|
8873
|
+
"@sarj/prefer-module-level-constant": "error",
|
|
8874
|
+
// Anti-comment-verbosity family (2026-07) — see the `recommended` block
|
|
8875
|
+
// for the measured hit counts and false-positive rates.
|
|
8876
|
+
"@sarj/no-restated-comment": "error",
|
|
8877
|
+
"@sarj/jsdoc-restates-signature": "error",
|
|
8878
|
+
"@sarj/trailing-value-narration": "error",
|
|
8879
|
+
"@sarj/no-type-member-comment-wall": "error",
|
|
8880
|
+
// TS half of SARJ057 — see the `recommended` block for the measurement.
|
|
8881
|
+
"@sarj/no-tautological-expect": "error",
|
|
8882
|
+
// Substitutability: the TS sibling of the Python `prefer-real-store-in-tests`
|
|
8883
|
+
// / `prefer-library-fake` wave. The convention already exists in the
|
|
8884
|
+
// corpus (175 `implements` clauses vs 29 hits), so strict enforces it.
|
|
8885
|
+
"@sarj/require-interface-for-injected-service": "error",
|
|
8886
|
+
"@sarj/prefer-non-nullable-collection": "error",
|
|
8887
|
+
"@sarj/no-async-callback-in-waitfor": "error",
|
|
8888
|
+
// See the `recommended` block.
|
|
8889
|
+
"@sarj/strict-test-assertions": "error"
|
|
8890
|
+
};
|
|
8514
8891
|
var plugin = {
|
|
8515
|
-
meta
|
|
8516
|
-
name: "@sarj/eslint-plugin",
|
|
8517
|
-
version: "5.0.0"
|
|
8518
|
-
},
|
|
8892
|
+
meta,
|
|
8519
8893
|
rules,
|
|
8520
8894
|
configs: {
|
|
8521
|
-
recommended: {
|
|
8522
|
-
|
|
8523
|
-
rules: {
|
|
8524
|
-
"@sarj/zod-naming-convention": "warn",
|
|
8525
|
-
"@sarj/require-assert-never": "error",
|
|
8526
|
-
"@sarj/require-zod-form-validation": "error",
|
|
8527
|
-
"@sarj/enforce-file-structure": "warn",
|
|
8528
|
-
"@sarj/no-client-side-data-fetching": "warn",
|
|
8529
|
-
"@sarj/prefer-server-actions": "warn",
|
|
8530
|
-
"@sarj/no-unnecessary-use-client": "warn",
|
|
8531
|
-
"@sarj/prefer-schema-for-api-payload": "warn",
|
|
8532
|
-
// Distilled from sarj-audit skills — warn in recommended, error in strict.
|
|
8533
|
-
"@sarj/no-sentinel-return-on-catch": "warn",
|
|
8534
|
-
"@sarj/no-log-only-catch": "warn",
|
|
8535
|
-
"@sarj/no-insecure-random-id": "warn",
|
|
8536
|
-
"@sarj/no-json-stringify-error": "warn",
|
|
8537
|
-
"@sarj/no-string-concat-in-loop": "warn",
|
|
8538
|
-
"@sarj/prefer-discriminated-union": "warn",
|
|
8539
|
-
"@sarj/no-comment-cruft": "warn",
|
|
8540
|
-
// Frontend / styling — distilled from frontend PR-review mining.
|
|
8541
|
-
"@sarj/prefer-semantic-colors": [
|
|
8542
|
-
"warn",
|
|
8543
|
-
{ requireSemanticTokens: true }
|
|
8544
|
-
],
|
|
8545
|
-
// Ported from sarj-python-lint (SARJ), corpus-validated FP~0.
|
|
8546
|
-
"@sarj/no-fat-try-blocks": "warn",
|
|
8547
|
-
"@sarj/no-cors-wildcard-with-credentials": "warn",
|
|
8548
|
-
"@sarj/no-secret-in-log": "warn",
|
|
8549
|
-
"@sarj/no-unsafe-mock-casting": "warn",
|
|
8550
|
-
"@sarj/prefer-string-literal-union": "warn",
|
|
8551
|
-
"@sarj/prefer-zod-enum": "warn",
|
|
8552
|
-
// A type hand-written beside the Zod schema it restates drifts the
|
|
8553
|
-
// moment the schema gains a field. 30,759-file, 17-repo sweep
|
|
8554
|
-
// (2026-07): 5 reports, 5 true positives, all in public repos.
|
|
8555
|
-
// `requireIdenticalShape: false` widens it to 8 reports, 1 of them noise.
|
|
8556
|
-
"@sarj/prefer-zod-infer": "warn",
|
|
8557
|
-
// Mined from 2y of PR review feedback + 5-repo code-smell audit (2026-07).
|
|
8558
|
-
"@sarj/require-fetch-timeout": "warn",
|
|
8559
|
-
"@sarj/no-silent-promise-catch": "warn",
|
|
8560
|
-
// Second SARJ port wave — the TS/Python parity gap. Each targets a
|
|
8561
|
-
// defect class seen in production Workers code: timing-leaky secret
|
|
8562
|
-
// compares, non-idempotent store writes under queue redelivery,
|
|
8563
|
-
// O(N) pagination, implicit row contracts, flaky timed tests.
|
|
8564
|
-
"@sarj/prefer-constant-time-secret-compare": "error",
|
|
8565
|
-
"@sarj/store-insert-requires-on-conflict": "warn",
|
|
8566
|
-
"@sarj/no-offset-pagination": "warn",
|
|
8567
|
-
"@sarj/no-select-star": "warn",
|
|
8568
|
-
// Uncancellable hand-rolled timers. Verified against the shipped
|
|
8569
|
-
// strict config (205 enabled rules) that nothing already reports this
|
|
8570
|
-
// position; `unicorn` 72 has no promisified-timer rule at all.
|
|
8571
|
-
"@sarj/no-hand-rolled-sleep": "warn",
|
|
8572
|
-
"@sarj/no-sleep-in-test-body": "warn",
|
|
8573
|
-
"@sarj/no-conditional-in-test": "warn",
|
|
8574
|
-
"@sarj/no-repeated-string-literal": "warn",
|
|
8575
|
-
"@sarj/no-positional-tuple-return": "warn",
|
|
8576
|
-
// Injection guard — low FP, applies to any repo touching SQL.
|
|
8577
|
-
"@sarj/no-dynamic-sql": "warn",
|
|
8578
|
-
// Mined from two years of PR review (SARJ-928). Schema-layer sibling of
|
|
8579
|
-
// `no-enum`; autofixable for inline string-literal objects.
|
|
8580
|
-
"@sarj/no-zod-native-enum": "warn",
|
|
8581
|
-
// Mined from two years of PR review — the single most frequent uncovered
|
|
8582
|
-
// theme (~37 PRs). Measured 17 hits / 1085 real TS files, all true
|
|
8583
|
-
// positives, so it is safe to run everywhere.
|
|
8584
|
-
"@sarj/prefer-module-level-constant": "warn",
|
|
8585
|
-
// Anti-comment-verbosity family (2026-07), from a 37,918-comment,
|
|
8586
|
-
// nine-repo measurement study. Each is a deletion-class finding, so each
|
|
8587
|
-
// was validated against pydantic / trio / attrs as well as the maintained
|
|
8588
|
-
// repos: `no-restated-comment` 0 hits in the flagship first-party
|
|
8589
|
-
// repo and 4 in the three famous
|
|
8590
|
-
// corpora combined; `trailing-value-narration` 18 hits, 18 true
|
|
8591
|
-
// positives; `jsdoc-restates-signature` 36 hits, 0 measured false
|
|
8592
|
-
// positives, and it offers a suggestion rather than a `--fix` because a
|
|
8593
|
-
// wrong deletion is silent information loss.
|
|
8594
|
-
"@sarj/no-restated-comment": "warn",
|
|
8595
|
-
"@sarj/jsdoc-restates-signature": "warn",
|
|
8596
|
-
"@sarj/trailing-value-narration": "warn",
|
|
8597
|
-
// The TS half of SARJ057 (2026-07). Python has caught the
|
|
8598
|
-
// assertion-FREE test since 0.15.0 (SARJ043) and had no TS
|
|
8599
|
-
// counterpart, which is how `expect(true).toBe(true); // placeholder`
|
|
8600
|
-
// survived in a first-party repo: the file HAS an assertion.
|
|
8601
|
-
// Measured across 5,819 .ts/.tsx files (1,003 of them test files) in
|
|
8602
|
-
// six internal repos plus got / hono / swr / trpc: 3 hits, 3 true
|
|
8603
|
-
// positives, 0 false positives.
|
|
8604
|
-
"@sarj/no-tautological-expect": "warn",
|
|
8605
|
-
// Substitutability: an exported service class with injected
|
|
8606
|
-
// collaborators and no interface above it can only be tested by
|
|
8607
|
-
// mocking. 11-repo sweep: 229 exported classes, 82% already carry a
|
|
8608
|
-
// port, 29 fire, 28 of them true positives.
|
|
8609
|
-
"@sarj/require-interface-for-injected-service": "warn",
|
|
8610
|
-
"@sarj/prefer-non-nullable-collection": "warn",
|
|
8611
|
-
"@sarj/no-async-callback-in-waitfor": "warn"
|
|
8612
|
-
}
|
|
8613
|
-
},
|
|
8614
|
-
strict: {
|
|
8615
|
-
plugins: ["@sarj"],
|
|
8616
|
-
rules: {
|
|
8617
|
-
"@sarj/zod-naming-convention": "error",
|
|
8618
|
-
"@sarj/require-assert-never": "error",
|
|
8619
|
-
"@sarj/require-zod-form-validation": "error",
|
|
8620
|
-
"@sarj/enforce-file-structure": "error",
|
|
8621
|
-
"@sarj/no-raw-env": "error",
|
|
8622
|
-
"@sarj/no-enum": "error",
|
|
8623
|
-
"@sarj/no-client-side-data-fetching": "error",
|
|
8624
|
-
"@sarj/prefer-server-actions": "error",
|
|
8625
|
-
"@sarj/no-unnecessary-use-client": "error",
|
|
8626
|
-
"@sarj/prefer-schema-for-api-payload": "error",
|
|
8627
|
-
// Distilled from sarj-audit skills.
|
|
8628
|
-
"@sarj/no-sentinel-return-on-catch": "error",
|
|
8629
|
-
"@sarj/no-log-only-catch": "error",
|
|
8630
|
-
"@sarj/no-insecure-random-id": "error",
|
|
8631
|
-
"@sarj/no-json-stringify-error": "error",
|
|
8632
|
-
"@sarj/no-string-concat-in-loop": "error",
|
|
8633
|
-
"@sarj/prefer-discriminated-union": "error",
|
|
8634
|
-
"@sarj/no-comment-cruft": "error",
|
|
8635
|
-
// Frontend / styling — distilled from frontend PR-review mining. Stylistic,
|
|
8636
|
-
// no autofix → warn (rollout should prove the FP rate before raising it).
|
|
8637
|
-
"@sarj/prefer-semantic-colors": [
|
|
8638
|
-
"error",
|
|
8639
|
-
{ requireSemanticTokens: true }
|
|
8640
|
-
],
|
|
8641
|
-
// Ported from sarj-python-lint (SARJ), corpus-validated FP~0.
|
|
8642
|
-
"@sarj/no-fat-try-blocks": "error",
|
|
8643
|
-
"@sarj/no-cors-wildcard-with-credentials": "error",
|
|
8644
|
-
"@sarj/no-secret-in-log": "error",
|
|
8645
|
-
"@sarj/no-unsafe-mock-casting": "error",
|
|
8646
|
-
// Promoted to error 2026-07-25 — strict means strict (user directive).
|
|
8647
|
-
"@sarj/prefer-string-literal-union": "error",
|
|
8648
|
-
"@sarj/prefer-zod-enum": "error",
|
|
8649
|
-
// See the `recommended` block for the measured counts.
|
|
8650
|
-
"@sarj/prefer-zod-infer": "error",
|
|
8651
|
-
// Mined from 2y of PR review feedback + 5-repo code-smell audit (2026-07).
|
|
8652
|
-
"@sarj/require-fetch-timeout": "error",
|
|
8653
|
-
"@sarj/no-silent-promise-catch": "error",
|
|
8654
|
-
// Second SARJ port wave — the TS/Python parity gap.
|
|
8655
|
-
"@sarj/prefer-constant-time-secret-compare": "error",
|
|
8656
|
-
"@sarj/store-insert-requires-on-conflict": "error",
|
|
8657
|
-
"@sarj/no-offset-pagination": "error",
|
|
8658
|
-
"@sarj/no-select-star": "error",
|
|
8659
|
-
// Uncancellable hand-rolled timers. Verified against the shipped
|
|
8660
|
-
// strict config (205 enabled rules) that nothing already reports this
|
|
8661
|
-
// position; `unicorn` 72 has no promisified-timer rule at all.
|
|
8662
|
-
"@sarj/no-hand-rolled-sleep": "error",
|
|
8663
|
-
"@sarj/no-sleep-in-test-body": "error",
|
|
8664
|
-
"@sarj/no-conditional-in-test": "error",
|
|
8665
|
-
"@sarj/no-repeated-string-literal": "error",
|
|
8666
|
-
// API-shape advice rather than a runtime defect — a corpus sweep found its
|
|
8667
|
-
// only hits are parser `[value, cursor]` returns, which are conventional.
|
|
8668
|
-
// Warn even in strict until a rollout justifies more.
|
|
8669
|
-
"@sarj/no-positional-tuple-return": "error",
|
|
8670
|
-
"@sarj/no-dynamic-sql": "error",
|
|
8671
|
-
// Architectural: both need per-repo config to be meaningful, so they
|
|
8672
|
-
// are strict-only. `no-storage-in-stateless-modules` is a no-op until
|
|
8673
|
-
// its `modules` option names the directories a team declared stateless;
|
|
8674
|
-
// `no-raw-fetch-outside-clients` defaults to the `clients/` convention
|
|
8675
|
-
// and takes an `allow` list for repos that lay their client layer out
|
|
8676
|
-
// differently.
|
|
8677
|
-
"@sarj/no-raw-fetch-outside-clients": "error",
|
|
8678
|
-
"@sarj/no-storage-in-stateless-modules": "error",
|
|
8679
|
-
// Mined from two years of PR review (SARJ-928).
|
|
8680
|
-
"@sarj/no-zod-native-enum": "error",
|
|
8681
|
-
"@sarj/prefer-module-level-constant": "error",
|
|
8682
|
-
// Anti-comment-verbosity family (2026-07) — see the `recommended` block
|
|
8683
|
-
// for the measured hit counts and false-positive rates.
|
|
8684
|
-
"@sarj/no-restated-comment": "error",
|
|
8685
|
-
"@sarj/jsdoc-restates-signature": "error",
|
|
8686
|
-
"@sarj/trailing-value-narration": "error",
|
|
8687
|
-
// TS half of SARJ057 — see the `recommended` block for the measurement.
|
|
8688
|
-
"@sarj/no-tautological-expect": "error",
|
|
8689
|
-
// Substitutability: the TS sibling of the Python `prefer-real-store-in-tests`
|
|
8690
|
-
// / `prefer-library-fake` wave. The convention already exists in the
|
|
8691
|
-
// corpus (175 `implements` clauses vs 29 hits), so strict enforces it.
|
|
8692
|
-
"@sarj/require-interface-for-injected-service": "error",
|
|
8693
|
-
"@sarj/prefer-non-nullable-collection": "error",
|
|
8694
|
-
"@sarj/no-async-callback-in-waitfor": "error"
|
|
8695
|
-
}
|
|
8696
|
-
}
|
|
8895
|
+
recommended: {},
|
|
8896
|
+
strict: {}
|
|
8697
8897
|
}
|
|
8698
8898
|
};
|
|
8899
|
+
plugin.configs.recommended = {
|
|
8900
|
+
name: "@sarj/recommended",
|
|
8901
|
+
plugins: { "@sarj": plugin },
|
|
8902
|
+
rules: recommendedRules
|
|
8903
|
+
};
|
|
8904
|
+
plugin.configs.strict = {
|
|
8905
|
+
name: "@sarj/strict",
|
|
8906
|
+
plugins: { "@sarj": plugin },
|
|
8907
|
+
rules: strictRules
|
|
8908
|
+
};
|
|
8699
8909
|
var index_default = plugin;
|
|
8700
8910
|
export {
|
|
8701
8911
|
index_default as default,
|
|
8702
|
-
|
|
8912
|
+
recommendedRules,
|
|
8913
|
+
rules,
|
|
8914
|
+
strictRules
|
|
8703
8915
|
};
|
|
8704
8916
|
//# sourceMappingURL=index.js.map
|