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