@deslop/coding-standards 0.1.1083 → 0.1.1086
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 +5 -78
- package/dist/{MutableList-Cpkiw3Cl.mjs → MutableList-CnvD7fcZ.mjs} +49 -14
- package/dist/install.js +18 -30
- package/dist/oxlint.d.ts +30 -25
- package/dist/oxlint.js +582 -362
- package/package.json +3 -3
- package/skills/design/SKILL.md +12 -15
- package/skills/engineering/SKILL.md +115 -110
- package/skills/review/SKILL.md +5 -6
- package/tsconfig.json +0 -2
package/dist/oxlint.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as dedupeWith, At as
|
|
1
|
+
import { A as dedupeWith, Ar as isNumber, At as firstSomeOf, B as flatMap$1, Br as constFalse, Bt as map, C as TaggedError, D as contains, Dr as isNotNullish, Dt as contains$1, E as appendAll, Er as isNotNull, Et as all, F as filter$1, Fr as isRegExp, Ft as getOrElse, G as getSomes, Gr as dual, Gt as toArray$1, H as forEach, Ht as none, I as filterMap, Ir as isString, It as getOrUndefined, J as isArrayNonEmpty, K as head, Kt as zipWith, L as findFirst, Lt as isNone, M as empty, Mt as flatten$1, N as ensure, Nt as fromNullishOr, O as containsWith, Ot as exists, P as every, Pr as isReadonlyObject, Pt as fromUndefinedOr, Q as last, Qr as pipeArguments, R as findFirstIndex, Rt as isSome, Sr as isBoolean, T as append, Ur as constVoid, Ut as orElse, V as flatten, Vt as match, W as get$1, Wt as some, X as isReadonlyArrayNonEmpty, Yr as pipe, Z as join, Zn as NodeInspectSymbol, _ as toUpperCase, an as strictEqual, b as parse, br as hasProperty, c as make$3, cr as byReferenceUnsafe, ct as take, d as Order, dt as get$2, et as map$1, f as includes, fr as combine, g as startsWith, gr as string, h as split, hr as optimize, ht as values, it as prepend, j as dropRight, jt as flatMap, k as dedupe, kr as isNullish, kt as filter, l as update, lr as equals, lt as takeWhile, m as replaceAll, mr as number, mt as remove, nt as matchRight, o as toArray, ot as some$1, p as match$1, pr as hash, q as isArrayEmpty, r as make$2, rt as of, s as get$3, st as sort, t as append$1, u as match$2, ur as symbol, ut as fromEntries, v as trim, vt as fromOption, yr as symbol$1 } from "./MutableList-CnvD7fcZ.mjs";
|
|
2
2
|
//#region ../../node_modules/.pnpm/effect@4.0.5/node_modules/effect/dist/internal/graph.js
|
|
3
3
|
/** @internal */
|
|
4
4
|
const TypeId = "~effect/Graph";
|
|
@@ -621,7 +621,7 @@ Object.freeze({
|
|
|
621
621
|
}
|
|
622
622
|
});
|
|
623
623
|
//#endregion
|
|
624
|
-
//#region ../../node_modules/.pnpm/oxlint@1.88.0_oxlint-tsgolint@7.0.2004_vite-plus@1.
|
|
624
|
+
//#region ../../node_modules/.pnpm/oxlint@1.88.0_oxlint-tsgolint@7.0.2004_vite-plus@1.3.0_@opentelemetry+api@1.9.1_@types+_73e40377152d496dbecb939f0b8a3193/node_modules/oxlint/dist/index.js
|
|
625
625
|
/**
|
|
626
626
|
* Define an Oxlint configuration with type inference.
|
|
627
627
|
*
|
|
@@ -633,6 +633,28 @@ function defineConfig(config) {
|
|
|
633
633
|
}
|
|
634
634
|
//#endregion
|
|
635
635
|
//#region src/rules/shared.ts
|
|
636
|
+
function inert(token) {
|
|
637
|
+
if (contains([
|
|
638
|
+
"Boolean",
|
|
639
|
+
"Identifier",
|
|
640
|
+
"Null",
|
|
641
|
+
"Numeric",
|
|
642
|
+
"String"
|
|
643
|
+
], token.type)) return true;
|
|
644
|
+
if (token.type === "Keyword") return contains([
|
|
645
|
+
"const",
|
|
646
|
+
"new",
|
|
647
|
+
"return"
|
|
648
|
+
], token.value);
|
|
649
|
+
return token.type === "Punctuator" && contains([
|
|
650
|
+
"(",
|
|
651
|
+
",",
|
|
652
|
+
":",
|
|
653
|
+
"=",
|
|
654
|
+
"[",
|
|
655
|
+
"{"
|
|
656
|
+
], token.value);
|
|
657
|
+
}
|
|
636
658
|
function variableFromScope(input) {
|
|
637
659
|
if (input.scope === null) return none();
|
|
638
660
|
const scope = input.scope;
|
|
@@ -690,7 +712,7 @@ function importedMember(input) {
|
|
|
690
712
|
importedName: input.importedName,
|
|
691
713
|
node: input.node.object,
|
|
692
714
|
source: "effect"
|
|
693
|
-
}) && (input.propertyName === void 0 || contains(memberName(input.node), input.propertyName));
|
|
715
|
+
}) && (input.propertyName === void 0 || contains$1(memberName(input.node), input.propertyName));
|
|
694
716
|
}
|
|
695
717
|
function matches(regExp) {
|
|
696
718
|
return (text) => isSome(pipe(text, match$1(regExp)));
|
|
@@ -711,7 +733,7 @@ function isReactUseState(input) {
|
|
|
711
733
|
node: input.node.callee,
|
|
712
734
|
source: "react"
|
|
713
735
|
});
|
|
714
|
-
return input.node.callee.type === "MemberExpression" && contains(memberName(input.node.callee), "useState") && input.node.callee.object.type === "Identifier" && isNamespaceImport({
|
|
736
|
+
return input.node.callee.type === "MemberExpression" && contains$1(memberName(input.node.callee), "useState") && input.node.callee.object.type === "Identifier" && isNamespaceImport({
|
|
715
737
|
context: input.context,
|
|
716
738
|
node: input.node.callee.object,
|
|
717
739
|
source: "react"
|
|
@@ -868,7 +890,7 @@ function schemaCycleNames(input) {
|
|
|
868
890
|
const indices = fromEntries(map$1(names, (name) => [name, addNode(mutable, name)]));
|
|
869
891
|
for (const edge of edges) for (const target of edge.targets) zipWith(get$2(indices, edge.name), get$2(indices, target), (source, sink) => addEdge(mutable, source, sink, "reads"));
|
|
870
892
|
});
|
|
871
|
-
return pipe(stronglyConnectedComponents(graph), map$1((component) => getSomes(map$1(component, (index) => getNode(graph, index)))), filter$1((component) => (component.length > 1 || some$1(edges, (edge) => contains
|
|
893
|
+
return pipe(stronglyConnectedComponents(graph), map$1((component) => getSomes(map$1(component, (index) => getNode(graph, index)))), filter$1((component) => (component.length > 1 || some$1(edges, (edge) => contains(component, edge.name) && contains(edge.targets, edge.name))) && some$1(component, (name) => contains(seeds, name))), flatten);
|
|
872
894
|
}
|
|
873
895
|
function hasSingleStatementExpression(node) {
|
|
874
896
|
if (node.body === null) return false;
|
|
@@ -908,7 +930,7 @@ const explicitEffectConcurrency = defineRule({
|
|
|
908
930
|
})) return;
|
|
909
931
|
const name = memberName(node.callee);
|
|
910
932
|
if (!exists(name, (method) => method === "all" || method === "forEach")) return;
|
|
911
|
-
const index = match$2(contains(name, "all") || isFunction(node.arguments[0]) || node.arguments[1]?.type === "ObjectExpression", {
|
|
933
|
+
const index = match$2(contains$1(name, "all") || isFunction(node.arguments[0]) || node.arguments[1]?.type === "ObjectExpression", {
|
|
912
934
|
onFalse: () => 2,
|
|
913
935
|
onTrue: () => 1
|
|
914
936
|
});
|
|
@@ -921,17 +943,105 @@ const explicitEffectConcurrency = defineRule({
|
|
|
921
943
|
meta: { type: "problem" }
|
|
922
944
|
});
|
|
923
945
|
//#endregion
|
|
946
|
+
//#region src/rules/module-constant.ts
|
|
947
|
+
const isConstantName = matches(/^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*$/u);
|
|
948
|
+
function literal(node) {
|
|
949
|
+
if (node.type === "Literal") return node.value !== null;
|
|
950
|
+
if (node.type === "TemplateLiteral") return isArrayEmpty(node.expressions);
|
|
951
|
+
if (node.type === "UnaryExpression") return node.operator === "-" && literal(node.argument);
|
|
952
|
+
if (node.type === "BinaryExpression") return literal(node.left) && literal(node.right);
|
|
953
|
+
if (node.type === "ArrayExpression") return every(node.elements, (element) => element !== null && element.type !== "SpreadElement" && literal(element));
|
|
954
|
+
if (node.type === "ObjectExpression") return every(node.properties, (property) => property.type === "Property" && !property.computed && property.kind === "init" && literal(property.value));
|
|
955
|
+
return pipe(typeWrapped(node), exists(literal));
|
|
956
|
+
}
|
|
957
|
+
function literalConstant(statement) {
|
|
958
|
+
if (statement.type !== "VariableDeclaration" || statement.kind !== "const" || statement.declarations.length !== 1) return none();
|
|
959
|
+
const [node] = statement.declarations;
|
|
960
|
+
if (node?.id.type !== "Identifier" || node.init === null || !literal(node.init)) return none();
|
|
961
|
+
return some({
|
|
962
|
+
id: node.id,
|
|
963
|
+
node
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
function renamed(input) {
|
|
967
|
+
if (input.reference.identifier.parent.type === "Property" && input.reference.identifier.parent.shorthand) return input.fixer.replaceText(input.reference.identifier, `${input.reference.identifier.name}: ${input.name}`);
|
|
968
|
+
return input.fixer.replaceText(input.reference.identifier, input.name);
|
|
969
|
+
}
|
|
970
|
+
function primitive(node) {
|
|
971
|
+
if (node.type === "Literal") return !hasProperty(node, "regex");
|
|
972
|
+
return contains([
|
|
973
|
+
"BinaryExpression",
|
|
974
|
+
"TemplateLiteral",
|
|
975
|
+
"UnaryExpression"
|
|
976
|
+
], node.type) || pipe(typeWrapped(node), exists(primitive));
|
|
977
|
+
}
|
|
978
|
+
function check$2(input) {
|
|
979
|
+
if (input.found.id.typeAnnotation === null && input.found.node.init !== null && primitive(input.found.node.init) && filter$1(input.variable.references, (reference) => reference.isRead()).length === 1 && !pipe(input.context.sourceCode.getText(input.found.node), includes("\n"))) {
|
|
980
|
+
input.context.report({
|
|
981
|
+
message: "Inline this value at its only use.",
|
|
982
|
+
node: input.found.node
|
|
983
|
+
});
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
if (!isConstantName(input.found.id.name)) {
|
|
987
|
+
const name = pipe(input.found.id.name, replaceAll(/(?<=[a-z0-9])(?=[A-Z])/gu, "_"), toUpperCase);
|
|
988
|
+
const taken = some$1([input.found.id, ...map$1(input.variable.references, (reference) => reference.identifier)], (node) => isSome(variableFromScope({
|
|
989
|
+
name,
|
|
990
|
+
scope: input.context.sourceCode.getScope(node)
|
|
991
|
+
})));
|
|
992
|
+
input.context.report({
|
|
993
|
+
fix: (fixer) => {
|
|
994
|
+
if (taken) return [];
|
|
995
|
+
return [fixer.replaceTextRange([input.found.id.range[0], input.found.id.range[0] + input.found.id.name.length], name), ...pipe(input.variable.references, filter$1((reference) => reference.identifier !== input.found.id), map$1((reference) => renamed({
|
|
996
|
+
fixer,
|
|
997
|
+
name,
|
|
998
|
+
reference
|
|
999
|
+
})))];
|
|
1000
|
+
},
|
|
1001
|
+
message: `Name this module constant ${name}.`,
|
|
1002
|
+
node: input.found.node
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
if (!input.leading) input.context.report({
|
|
1006
|
+
message: "Declare this module constant directly under the imports.",
|
|
1007
|
+
node: input.found.node
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
const moduleConstant = defineRule({
|
|
1011
|
+
create: (context) => ({ Program: (program) => {
|
|
1012
|
+
const leading = takeWhile(program.body, (statement) => statement.type === "ImportDeclaration" || isSome(literalConstant(statement)));
|
|
1013
|
+
for (const statement of program.body) pipe(literalConstant(statement), flatMap((found) => pipe(variableFromScope({
|
|
1014
|
+
name: found.id.name,
|
|
1015
|
+
scope: context.sourceCode.getScope(found.node)
|
|
1016
|
+
}), filter((variable) => !some$1(variable.references, (reference) => reference.identifier.parent.type === "ExportSpecifier")), map((variable) => ({
|
|
1017
|
+
found,
|
|
1018
|
+
variable
|
|
1019
|
+
})))), match({
|
|
1020
|
+
onNone: constVoid,
|
|
1021
|
+
onSome: (match) => check$2({
|
|
1022
|
+
...match,
|
|
1023
|
+
context,
|
|
1024
|
+
leading: contains(leading, statement)
|
|
1025
|
+
})
|
|
1026
|
+
}));
|
|
1027
|
+
} }),
|
|
1028
|
+
meta: {
|
|
1029
|
+
fixable: "code",
|
|
1030
|
+
type: "suggestion"
|
|
1031
|
+
}
|
|
1032
|
+
});
|
|
1033
|
+
//#endregion
|
|
924
1034
|
//#region src/rules/motion-limits.ts
|
|
925
1035
|
function utility(token) {
|
|
926
|
-
return pipe(token, split(":"), last, getOrElse(() => token),
|
|
1036
|
+
return pipe(token, split(":"), last, getOrElse(() => token), replaceAll(/^!|!$/gu, ""));
|
|
927
1037
|
}
|
|
928
1038
|
function amount(token, pattern) {
|
|
929
1039
|
return pipe(utility(token), match$1(pattern), flatMap((match) => fromNullishOr(match[1])), map((value) => pipe(parse(value), getOrElse(() => 0))));
|
|
930
1040
|
}
|
|
931
1041
|
function violation(token) {
|
|
932
|
-
if (exists(amount(token, /^duration-(\d+)$/u), (value) => value
|
|
1042
|
+
if (exists(amount(token, /^duration-(\d+)$/u), (value) => value >= 300)) return some("Keep a tween under 300 ms, such as duration-200.");
|
|
933
1043
|
if (matches(/^zoom-(?:in|out)$/u)(utility(token)) || exists(amount(token, /^zoom-(?:in|out)-(\d+)$/u), (value) => value < 90)) return some("Enter from 0.9 scale or more: zoom-in-90 or zoom-in-95.");
|
|
934
|
-
if (pipe(token, split(":"), dropRight(1), contains
|
|
1044
|
+
if (pipe(token, split(":"), dropRight(1), contains("starting")) && exists(amount(token, /^scale-(\d+)$/u), (value) => value < 90)) return some("Enter from 0.9 scale or more: starting:scale-90 or starting:scale-95.");
|
|
935
1045
|
return none();
|
|
936
1046
|
}
|
|
937
1047
|
function check$1(context, node, text) {
|
|
@@ -952,53 +1062,6 @@ const motionLimits = defineRule({
|
|
|
952
1062
|
meta: { type: "problem" }
|
|
953
1063
|
});
|
|
954
1064
|
//#endregion
|
|
955
|
-
//#region src/rules/no-array-wrap-ternary.ts
|
|
956
|
-
function testedValue(node) {
|
|
957
|
-
if (node.type !== "CallExpression" || node.callee.type !== "MemberExpression" || node.callee.object.type !== "Identifier" || node.callee.object.name !== "Array" || !contains(memberName(node.callee), "isArray") || node.arguments.length !== 1) return none();
|
|
958
|
-
const argument = node.arguments[0];
|
|
959
|
-
if (argument?.type !== "Identifier" && argument?.type !== "MemberExpression") return none();
|
|
960
|
-
return some(argument);
|
|
961
|
-
}
|
|
962
|
-
function isSingletonArray(input) {
|
|
963
|
-
if (input.node.type !== "ArrayExpression" || input.node.elements.length !== 1) return false;
|
|
964
|
-
const element = input.node.elements[0];
|
|
965
|
-
if (isNullish$1(element) || element.type === "SpreadElement") return false;
|
|
966
|
-
return input.context.sourceCode.getText(element) === input.context.sourceCode.getText(input.value);
|
|
967
|
-
}
|
|
968
|
-
function wrapsTestedValue(input) {
|
|
969
|
-
return exists(testedValue(input.test), (value) => input.context.sourceCode.getText(input.kept) === input.context.sourceCode.getText(value) && isSingletonArray({
|
|
970
|
-
context: input.context,
|
|
971
|
-
node: input.wrapped,
|
|
972
|
-
value
|
|
973
|
-
}));
|
|
974
|
-
}
|
|
975
|
-
function restatesArrayEnsure(input) {
|
|
976
|
-
if (input.node.test.type === "UnaryExpression" && input.node.test.operator === "!") return wrapsTestedValue({
|
|
977
|
-
context: input.context,
|
|
978
|
-
kept: input.node.alternate,
|
|
979
|
-
test: input.node.test.argument,
|
|
980
|
-
wrapped: input.node.consequent
|
|
981
|
-
});
|
|
982
|
-
return wrapsTestedValue({
|
|
983
|
-
context: input.context,
|
|
984
|
-
kept: input.node.consequent,
|
|
985
|
-
test: input.node.test,
|
|
986
|
-
wrapped: input.node.alternate
|
|
987
|
-
});
|
|
988
|
-
}
|
|
989
|
-
const noArrayWrapTernary = defineRule({
|
|
990
|
-
create: (context) => ({ ConditionalExpression: (node) => {
|
|
991
|
-
if (restatesArrayEnsure({
|
|
992
|
-
context,
|
|
993
|
-
node
|
|
994
|
-
})) context.report({
|
|
995
|
-
message: "Use Array.ensure; the ternary restates it.",
|
|
996
|
-
node
|
|
997
|
-
});
|
|
998
|
-
} }),
|
|
999
|
-
meta: { type: "problem" }
|
|
1000
|
-
});
|
|
1001
|
-
//#endregion
|
|
1002
1065
|
//#region src/rules/no-barrel-module.ts
|
|
1003
1066
|
const noBarrelModule = defineRule({
|
|
1004
1067
|
create: (context) => ({
|
|
@@ -1025,7 +1088,7 @@ const noCompilerOptOut = defineRule({
|
|
|
1025
1088
|
create: (context) => ({ ExpressionStatement: (node) => {
|
|
1026
1089
|
if (node.directive !== "use no memo" && node.directive !== "use no forget") return;
|
|
1027
1090
|
context.report({
|
|
1028
|
-
message: "
|
|
1091
|
+
message: "Remove this directive, rerun lint and fix the React Compiler diagnostic it hid.",
|
|
1029
1092
|
node
|
|
1030
1093
|
});
|
|
1031
1094
|
} }),
|
|
@@ -1105,7 +1168,7 @@ function isPipeCall(context, node) {
|
|
|
1105
1168
|
});
|
|
1106
1169
|
}
|
|
1107
1170
|
function enclosingPipes(context, node) {
|
|
1108
|
-
if (node.type === "Program" || contains
|
|
1171
|
+
if (node.type === "Program" || contains([
|
|
1109
1172
|
"ArrowFunctionExpression",
|
|
1110
1173
|
"FunctionDeclaration",
|
|
1111
1174
|
"FunctionExpression"
|
|
@@ -1117,7 +1180,7 @@ function enclosingPipes(context, node) {
|
|
|
1117
1180
|
const noDeepPipe = defineRule({
|
|
1118
1181
|
create: (context) => ({ CallExpression: (node) => {
|
|
1119
1182
|
if (isPipeCall(context, node) && enclosingPipes(context, node) >= 3) context.report({
|
|
1120
|
-
message: "
|
|
1183
|
+
message: "Merge this pipe into its parent, or move it into a named function declaration; pipe calls nest at most three deep.",
|
|
1121
1184
|
node
|
|
1122
1185
|
});
|
|
1123
1186
|
} }),
|
|
@@ -1126,7 +1189,7 @@ const noDeepPipe = defineRule({
|
|
|
1126
1189
|
//#endregion
|
|
1127
1190
|
//#region src/rules/no-destructured-parameter.ts
|
|
1128
1191
|
function namedTuple(input) {
|
|
1129
|
-
if (contains
|
|
1192
|
+
if (contains(input.seen, input.node)) return false;
|
|
1130
1193
|
if (input.node.type === "TSTupleType") return isArrayNonEmpty(input.node.elementTypes) && every(input.node.elementTypes, (element) => element.type === "TSNamedTupleMember");
|
|
1131
1194
|
if (input.node.type === "TSParenthesizedType" || input.node.type === "TSTypeOperator" && input.node.operator === "readonly") return namedTuple({
|
|
1132
1195
|
...input,
|
|
@@ -1200,6 +1263,9 @@ const noDestructuredParameter = defineRule({
|
|
|
1200
1263
|
});
|
|
1201
1264
|
//#endregion
|
|
1202
1265
|
//#region src/rules/no-discarded-failure.ts
|
|
1266
|
+
function logged(node) {
|
|
1267
|
+
return node.parent.type === "CallExpression" && node.parent.callee === node && some$1(node.parent.arguments, (argument) => argument.type === "ObjectExpression" && every(argument.properties, (property) => property.type === "Property") && some$1(argument.properties, (property) => !property.computed && (property.key.type === "Identifier" && property.key.name === "log" || property.key.type === "Literal" && property.key.value === "log") && property.value.type === "Literal" && (property.value.value === true || isString(property.value.value))));
|
|
1268
|
+
}
|
|
1203
1269
|
const noDiscardedFailure = defineRule({
|
|
1204
1270
|
create: (context) => ({ MemberExpression: (node) => {
|
|
1205
1271
|
if (!importedMember({
|
|
@@ -1207,13 +1273,15 @@ const noDiscardedFailure = defineRule({
|
|
|
1207
1273
|
importedName: "Effect",
|
|
1208
1274
|
node
|
|
1209
1275
|
})) return;
|
|
1210
|
-
if (!exists(memberName(node), (name) => contains
|
|
1276
|
+
if (!exists(memberName(node), (name) => contains([
|
|
1211
1277
|
"ignore",
|
|
1278
|
+
"ignoreCause",
|
|
1212
1279
|
"orDie",
|
|
1213
1280
|
"orDieWith"
|
|
1214
1281
|
], name))) return;
|
|
1282
|
+
if (exists(memberName(node), (name) => name !== "orDie" && name !== "orDieWith") && logged(node)) return;
|
|
1215
1283
|
context.report({
|
|
1216
|
-
message: "Map the failure to the service's error; an expected failure is never
|
|
1284
|
+
message: "Map the failure to the service's error, or log best-effort work with Effect.ignore({log: 'Warn', message}); an expected failure is never dropped silently or turned into a defect.",
|
|
1217
1285
|
node
|
|
1218
1286
|
});
|
|
1219
1287
|
} }),
|
|
@@ -1221,12 +1289,7 @@ const noDiscardedFailure = defineRule({
|
|
|
1221
1289
|
});
|
|
1222
1290
|
//#endregion
|
|
1223
1291
|
//#region src/rules/no-double-nullish-check.ts
|
|
1224
|
-
|
|
1225
|
-
if (node.type === "Literal" && node.value === null) return some("null");
|
|
1226
|
-
if (node.type === "Identifier" && node.name === "undefined") return some("undefined");
|
|
1227
|
-
return none();
|
|
1228
|
-
}
|
|
1229
|
-
const variants = {
|
|
1292
|
+
const VARIANTS = {
|
|
1230
1293
|
"&&": {
|
|
1231
1294
|
comparison: "!==",
|
|
1232
1295
|
message: "Use Predicate.isNotNullish."
|
|
@@ -1236,6 +1299,11 @@ const variants = {
|
|
|
1236
1299
|
message: "Use Predicate.isNullish."
|
|
1237
1300
|
}
|
|
1238
1301
|
};
|
|
1302
|
+
function nullishOperand(node) {
|
|
1303
|
+
if (node.type === "Literal" && node.value === null) return some("null");
|
|
1304
|
+
if (node.type === "Identifier" && node.name === "undefined") return some("undefined");
|
|
1305
|
+
return none();
|
|
1306
|
+
}
|
|
1239
1307
|
function operands(input) {
|
|
1240
1308
|
if (input.node.type !== "LogicalExpression" || input.node.operator !== input.operator) return [input.node];
|
|
1241
1309
|
return appendAll(operands({
|
|
@@ -1258,7 +1326,7 @@ const noDoubleNullishCheck = defineRule({
|
|
|
1258
1326
|
create: (context) => ({ LogicalExpression: (node) => {
|
|
1259
1327
|
if (node.operator === "??") return;
|
|
1260
1328
|
if (node.parent.type === "LogicalExpression" && node.parent.operator === node.operator) return;
|
|
1261
|
-
const variant =
|
|
1329
|
+
const variant = VARIANTS[node.operator];
|
|
1262
1330
|
const checks = pipe(operands({
|
|
1263
1331
|
node,
|
|
1264
1332
|
operator: node.operator
|
|
@@ -1312,14 +1380,14 @@ function calleeOf(node) {
|
|
|
1312
1380
|
}
|
|
1313
1381
|
function isEffectEmpty(input) {
|
|
1314
1382
|
const member = calleeOf(input.node);
|
|
1315
|
-
return member.type === "MemberExpression" && member.object.type === "Identifier" && contains(memberName(member), "empty") && isImportBinding({
|
|
1383
|
+
return member.type === "MemberExpression" && member.object.type === "Identifier" && contains$1(memberName(member), "empty") && isImportBinding({
|
|
1316
1384
|
context: input.context,
|
|
1317
1385
|
node: member.object,
|
|
1318
1386
|
source: "effect"
|
|
1319
1387
|
});
|
|
1320
1388
|
}
|
|
1321
1389
|
function isEmptyValue(input) {
|
|
1322
|
-
if (isNullish
|
|
1390
|
+
if (isNullish(input.node)) return false;
|
|
1323
1391
|
if (input.node.type === "Identifier") return input.node.name === "undefined";
|
|
1324
1392
|
if (input.node.type === "Literal") return input.node.value === null || input.node.value === "";
|
|
1325
1393
|
if (input.node.type === "ArrayExpression") return isArrayEmpty(input.node.elements);
|
|
@@ -1331,7 +1399,7 @@ function isEmptyValue(input) {
|
|
|
1331
1399
|
});
|
|
1332
1400
|
}
|
|
1333
1401
|
function isEmptySuccess(input) {
|
|
1334
|
-
if (isNullish
|
|
1402
|
+
if (isNullish(input.node)) return false;
|
|
1335
1403
|
if (importedMember({
|
|
1336
1404
|
context: input.context,
|
|
1337
1405
|
importedName: "Effect",
|
|
@@ -1372,7 +1440,7 @@ const noEmptyRecovery = defineRule({
|
|
|
1372
1440
|
name,
|
|
1373
1441
|
node: returnedExpression(argument)
|
|
1374
1442
|
})) context.report({
|
|
1375
|
-
message: "Let the failure flow,
|
|
1443
|
+
message: "Let the failure flow, map it with Effect.mapError(cause => ServiceError.make({cause, message})), or use Effect.option when absence is a valid result; an empty value hides the failure.",
|
|
1376
1444
|
node: argument
|
|
1377
1445
|
});
|
|
1378
1446
|
} }),
|
|
@@ -1380,19 +1448,20 @@ const noEmptyRecovery = defineRule({
|
|
|
1380
1448
|
});
|
|
1381
1449
|
//#endregion
|
|
1382
1450
|
//#region src/rules/no-error-message-assertion.ts
|
|
1451
|
+
const ASSERT_IDENTITY = "Assert the failure's identity, such as assert.containsSubset(error, {_tag: 'NotFound'}) on Effect.flip's result, never its message.";
|
|
1383
1452
|
function receiverName(node) {
|
|
1384
1453
|
if (node.type === "Identifier") return some(node.name);
|
|
1385
1454
|
if (node.type !== "MemberExpression") return none();
|
|
1386
1455
|
return memberName(node);
|
|
1387
1456
|
}
|
|
1388
1457
|
function isErrorMessage(node) {
|
|
1389
|
-
return node?.type === "MemberExpression" && contains(memberName(node), "message") && exists(receiverName(node.object), matches(/^(?:e|err|error|failure|cause|exception)$|Error$/iu));
|
|
1458
|
+
return node?.type === "MemberExpression" && contains$1(memberName(node), "message") && exists(receiverName(node.object), matches(/^(?:e|err|error|failure|cause|exception)$|Error$/iu));
|
|
1390
1459
|
}
|
|
1391
1460
|
function expectsErrorMessage(node) {
|
|
1392
1461
|
return node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "expect" && isErrorMessage(node.arguments[0]);
|
|
1393
1462
|
}
|
|
1394
1463
|
function expectsMessage(node) {
|
|
1395
|
-
if (node.object.type === "MemberExpression" && contains(memberName(node.object), "not")) return expectsErrorMessage(node.object.object);
|
|
1464
|
+
if (node.object.type === "MemberExpression" && contains$1(memberName(node.object), "not")) return expectsErrorMessage(node.object.object);
|
|
1396
1465
|
return expectsErrorMessage(node.object);
|
|
1397
1466
|
}
|
|
1398
1467
|
function assertsMessage(node) {
|
|
@@ -1403,13 +1472,13 @@ const noErrorMessageAssertion = defineRule({
|
|
|
1403
1472
|
create: (context) => ({ CallExpression: (node) => {
|
|
1404
1473
|
if (assertsMessage(node)) {
|
|
1405
1474
|
context.report({
|
|
1406
|
-
message:
|
|
1475
|
+
message: ASSERT_IDENTITY,
|
|
1407
1476
|
node
|
|
1408
1477
|
});
|
|
1409
1478
|
return;
|
|
1410
1479
|
}
|
|
1411
|
-
if (node.callee.type === "MemberExpression" && (node.arguments[0]?.type === "Literal" || node.arguments[0]?.type === "TemplateLiteral") && (exists(memberName(node.callee), (name) => contains
|
|
1412
|
-
message:
|
|
1480
|
+
if (node.callee.type === "MemberExpression" && (node.arguments[0]?.type === "Literal" || node.arguments[0]?.type === "TemplateLiteral") && (exists(memberName(node.callee), (name) => contains(["toThrow", "toThrowError"], name)) || expectsMessage(node.callee))) context.report({
|
|
1481
|
+
message: ASSERT_IDENTITY,
|
|
1413
1482
|
node
|
|
1414
1483
|
});
|
|
1415
1484
|
} }),
|
|
@@ -1425,7 +1494,7 @@ function propertyName(node) {
|
|
|
1425
1494
|
function hasUnusedSetter(input) {
|
|
1426
1495
|
if (input.node.parent.type !== "VariableDeclarator" || input.node.parent.id.type !== "ArrayPattern") return false;
|
|
1427
1496
|
const setter = input.node.parent.id.elements[1];
|
|
1428
|
-
if (isNullish
|
|
1497
|
+
if (isNullish(setter)) return true;
|
|
1429
1498
|
if (setter.type !== "Identifier") return false;
|
|
1430
1499
|
return pipe(variableFromScope({
|
|
1431
1500
|
name: setter.name,
|
|
@@ -1434,7 +1503,7 @@ function hasUnusedSetter(input) {
|
|
|
1434
1503
|
}
|
|
1435
1504
|
function isFakeRefState(input) {
|
|
1436
1505
|
if (!isReactUseState(input) || input.node.arguments[0]?.type !== "ArrowFunctionExpression" || input.node.arguments[0].body.type !== "ObjectExpression" || input.node.arguments[0].body.properties.length !== 1 || !hasUnusedSetter(input)) return false;
|
|
1437
|
-
return some$1(input.node.arguments[0].body.properties, (property) => property.type === "Property" && contains(propertyName(property), "current") && property.value.type === "Literal" && property.value.value === null);
|
|
1506
|
+
return some$1(input.node.arguments[0].body.properties, (property) => property.type === "Property" && contains$1(propertyName(property), "current") && property.value.type === "Literal" && property.value.value === null);
|
|
1438
1507
|
}
|
|
1439
1508
|
const noFakeRefState = defineRule({
|
|
1440
1509
|
create: (context) => ({ CallExpression: (node) => {
|
|
@@ -1449,21 +1518,44 @@ const noFakeRefState = defineRule({
|
|
|
1449
1518
|
meta: { type: "problem" }
|
|
1450
1519
|
});
|
|
1451
1520
|
//#endregion
|
|
1521
|
+
//#region src/rules/no-floating-derived-type.ts
|
|
1522
|
+
function derived(node) {
|
|
1523
|
+
if (node.type === "TSIndexedAccessType") return derived(node.objectType);
|
|
1524
|
+
return node.type === "TSTypeReference" && node.typeName.type === "Identifier" && contains([
|
|
1525
|
+
"Awaited",
|
|
1526
|
+
"ConstructorParameters",
|
|
1527
|
+
"InstanceType",
|
|
1528
|
+
"Parameters",
|
|
1529
|
+
"ReturnType"
|
|
1530
|
+
], node.typeName.name);
|
|
1531
|
+
}
|
|
1532
|
+
const noFloatingDerivedType = defineRule({
|
|
1533
|
+
create: (context) => ({ TSTypeAliasDeclaration: (node) => {
|
|
1534
|
+
const listed = some$1(context.sourceCode.ast.body, (statement) => statement.type === "ExportNamedDeclaration" && some$1(statement.specifiers, (specifier) => specifier.local.type === "Identifier" && specifier.local.name === node.id.name));
|
|
1535
|
+
if (node.parent.type === "ExportNamedDeclaration" || listed || !derived(node.typeAnnotation)) return;
|
|
1536
|
+
context.report({
|
|
1537
|
+
message: "Import the library's exported type, else write ReturnType<typeof f> in the signature that needs it, or pass the value straight to the call that types it.",
|
|
1538
|
+
node
|
|
1539
|
+
});
|
|
1540
|
+
} }),
|
|
1541
|
+
meta: { type: "problem" }
|
|
1542
|
+
});
|
|
1543
|
+
//#endregion
|
|
1452
1544
|
//#region src/rules/no-gradient.ts
|
|
1453
|
-
const
|
|
1545
|
+
const GRADIENT = /(?:^|[\s:])bg-(?:linear|radial|conic|gradient)(?:-|$|\s)|gradient\(/u;
|
|
1454
1546
|
function report$4(context, node) {
|
|
1455
1547
|
context.report({
|
|
1456
|
-
message: "Use a flat theme
|
|
1548
|
+
message: "Use a flat theme token such as bg-card or bg-muted; gradients are never part of the design.",
|
|
1457
1549
|
node
|
|
1458
1550
|
});
|
|
1459
1551
|
}
|
|
1460
1552
|
const noGradient = defineRule({
|
|
1461
1553
|
create: (context) => ({
|
|
1462
1554
|
Literal: (node) => {
|
|
1463
|
-
if (isString(node.value) &&
|
|
1555
|
+
if (isString(node.value) && GRADIENT.test(node.value)) report$4(context, node);
|
|
1464
1556
|
},
|
|
1465
1557
|
TemplateElement: (node) => {
|
|
1466
|
-
if (
|
|
1558
|
+
if (GRADIENT.test(node.value.raw)) report$4(context, node);
|
|
1467
1559
|
}
|
|
1468
1560
|
}),
|
|
1469
1561
|
meta: { type: "problem" }
|
|
@@ -1497,7 +1589,7 @@ function check(input) {
|
|
|
1497
1589
|
if (input.node.type === "UnaryExpression" && input.node.operator === "!" && input.node.argument.type === "CallExpression" && input.node.argument.callee.type === "MemberExpression" && isSubject({
|
|
1498
1590
|
name: "Array",
|
|
1499
1591
|
node: input.node.argument.callee.object
|
|
1500
|
-
}) && contains(memberName(input.node.argument.callee), "isArray") && input.node.argument.arguments.length === 1 && every(input.node.argument.arguments, (argument) => isSubject({
|
|
1592
|
+
}) && contains$1(memberName(input.node.argument.callee), "isArray") && input.node.argument.arguments.length === 1 && every(input.node.argument.arguments, (argument) => isSubject({
|
|
1501
1593
|
name: input.name,
|
|
1502
1594
|
node: argument
|
|
1503
1595
|
}))) return some(["!Array.isArray"]);
|
|
@@ -1521,11 +1613,11 @@ function check(input) {
|
|
|
1521
1613
|
subject: input.node.right
|
|
1522
1614
|
})]), map(of));
|
|
1523
1615
|
}
|
|
1524
|
-
function
|
|
1525
|
-
const predicate =
|
|
1526
|
-
const returned = returnedExpression(
|
|
1527
|
-
if (predicate?.type !== "TSTypePredicate" || predicate.asserts || predicate.parameterName.type !== "Identifier" || isNullish
|
|
1528
|
-
|
|
1616
|
+
function replaceableGuard(node) {
|
|
1617
|
+
const predicate = node.returnType?.typeAnnotation;
|
|
1618
|
+
const returned = returnedExpression(node);
|
|
1619
|
+
if (predicate?.type !== "TSTypePredicate" || predicate.asserts || predicate.parameterName.type !== "Identifier" || isNullish(returned)) return none();
|
|
1620
|
+
return pipe(check({
|
|
1529
1621
|
name: predicate.parameterName.name,
|
|
1530
1622
|
node: returned
|
|
1531
1623
|
}), map((checks) => pipe(checks, sort(Order), join(" && "))), flatMap((key) => get$2({
|
|
@@ -1551,12 +1643,12 @@ function report$3(input) {
|
|
|
1551
1643
|
"typeof === symbol": "isSymbol",
|
|
1552
1644
|
"typeof === undefined": "isUndefined"
|
|
1553
1645
|
}, key)));
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
});
|
|
1646
|
+
}
|
|
1647
|
+
function report$3(input) {
|
|
1648
|
+
map(replaceableGuard(input.node), (guard) => input.context.report({
|
|
1649
|
+
message: `Use Predicate.${guard} instead of this hand-written type guard.`,
|
|
1650
|
+
node: input.node
|
|
1651
|
+
}));
|
|
1560
1652
|
}
|
|
1561
1653
|
const noHandWrittenGuard = defineRule({
|
|
1562
1654
|
create: (context) => ({
|
|
@@ -1606,13 +1698,12 @@ const noLet = defineRule({
|
|
|
1606
1698
|
});
|
|
1607
1699
|
//#endregion
|
|
1608
1700
|
//#region src/rules/no-multiline-comment.ts
|
|
1609
|
-
const directive = /^\s*(?:eslint|oxlint|@effect-diagnostics|@ts-|\/\s*<reference|#region|#endregion)/u;
|
|
1610
1701
|
function adjacentLines(pair) {
|
|
1611
1702
|
return pair.first?.type === "Line" && pair.second?.type === "Line" && pair.second.loc.start.line === pair.first.loc.end.line + 1;
|
|
1612
1703
|
}
|
|
1613
1704
|
const noMultilineComment = defineRule({
|
|
1614
1705
|
create: (context) => ({ Program: () => {
|
|
1615
|
-
const comments = pipe(context.sourceCode.getAllComments(), filter$1((comment) => !
|
|
1706
|
+
const comments = pipe(context.sourceCode.getAllComments(), filter$1((comment) => !matches(/^\s*(?:eslint|oxlint|@effect-diagnostics|@ts-|\/\s*<reference|#region|#endregion)/u)(comment.value)));
|
|
1616
1707
|
pipe(comments, filter$1((comment, index) => adjacentLines({
|
|
1617
1708
|
first: comment,
|
|
1618
1709
|
second: comments[index + 1]
|
|
@@ -1628,13 +1719,13 @@ const noMultilineComment = defineRule({
|
|
|
1628
1719
|
});
|
|
1629
1720
|
//#endregion
|
|
1630
1721
|
//#region src/rules/no-native-control.ts
|
|
1631
|
-
const
|
|
1722
|
+
const PRIMITIVES = {
|
|
1632
1723
|
button: "Button",
|
|
1633
1724
|
input: "Input",
|
|
1634
1725
|
select: "Select",
|
|
1635
1726
|
textarea: "Textarea"
|
|
1636
1727
|
};
|
|
1637
|
-
const
|
|
1728
|
+
const CONTROL_STYLE = /(?:^|:)(?:hover|focus|focus-visible|active):|(?:^|:)(?:rounded|shadow|ring)(?:-|$)|(?:^|:)p[blrtxy]?-(?!0$)|(?:^|:)bg-(?!transparent$)|(?:^|:)border(?:-[blrtxy])?$|(?:^|:)border-(?!0$|[blrtxy]-0$)/u;
|
|
1638
1729
|
function classStrings(node) {
|
|
1639
1730
|
if (node.type === "Literal") return filter$1([node.value], isString);
|
|
1640
1731
|
if (node.type === "TemplateLiteral") return map$1(node.quasis, (quasi) => quasi.value.raw);
|
|
@@ -1649,11 +1740,11 @@ const noNativeControl = defineRule({
|
|
|
1649
1740
|
create: (context) => ({ JSXOpeningElement: (node) => {
|
|
1650
1741
|
if (node.name.type !== "JSXIdentifier") return;
|
|
1651
1742
|
const name = node.name.name;
|
|
1652
|
-
map(get$2(
|
|
1743
|
+
map(get$2(PRIMITIVES, name), (primitive) => {
|
|
1653
1744
|
const styled = pipe(node.attributes, findFirst((attribute) => attribute.type === "JSXAttribute" && attribute.name.type === "JSXIdentifier" && attribute.name.name === "className"), map((attribute) => {
|
|
1654
1745
|
if (attribute.type !== "JSXAttribute" || attribute.value === null) return empty();
|
|
1655
1746
|
return classStrings(attribute.value);
|
|
1656
|
-
}), exists((values) => some$1(values, (value) => some$1(split(value, /\s+/u), matches(
|
|
1747
|
+
}), exists((values) => some$1(values, (value) => some$1(split(value, /\s+/u), matches(CONTROL_STYLE)))));
|
|
1657
1748
|
if (name !== "select" && !styled) return;
|
|
1658
1749
|
context.report({
|
|
1659
1750
|
message: `Use the ${primitive} UI primitive instead of a styled native control.`,
|
|
@@ -1666,7 +1757,7 @@ const noNativeControl = defineRule({
|
|
|
1666
1757
|
//#endregion
|
|
1667
1758
|
//#region src/rules/no-native-emptiness-check.ts
|
|
1668
1759
|
function emptinessPredicate(node) {
|
|
1669
|
-
const comparesLength = node.left.type === "MemberExpression" && contains(memberName(node.left), "length") && node.right.type === "Literal" && node.right.value === 0;
|
|
1760
|
+
const comparesLength = node.left.type === "MemberExpression" && contains$1(memberName(node.left), "length") && node.right.type === "Literal" && node.right.value === 0;
|
|
1670
1761
|
if (comparesLength && node.operator === "===") return some("Array.isArrayEmpty, Array.isReadonlyArrayEmpty, or String.isEmpty");
|
|
1671
1762
|
if (comparesLength && node.operator === ">") return some("Array.isArrayNonEmpty, Array.isReadonlyArrayNonEmpty, or String.isNonEmpty");
|
|
1672
1763
|
return none();
|
|
@@ -1684,69 +1775,54 @@ const noNativeEmptinessCheck = defineRule({
|
|
|
1684
1775
|
});
|
|
1685
1776
|
//#endregion
|
|
1686
1777
|
//#region src/rules/no-native-method-call.ts
|
|
1687
|
-
const
|
|
1688
|
-
"at",
|
|
1689
|
-
|
|
1690
|
-
"charAt",
|
|
1691
|
-
"charCodeAt",
|
|
1692
|
-
"codePointAt",
|
|
1693
|
-
"concat",
|
|
1694
|
-
"endsWith",
|
|
1695
|
-
"entries",
|
|
1696
|
-
"
|
|
1697
|
-
"
|
|
1698
|
-
"
|
|
1699
|
-
"
|
|
1700
|
-
"
|
|
1701
|
-
"
|
|
1702
|
-
"
|
|
1703
|
-
"
|
|
1704
|
-
"
|
|
1705
|
-
"
|
|
1706
|
-
"
|
|
1707
|
-
"
|
|
1708
|
-
"
|
|
1709
|
-
"
|
|
1710
|
-
"
|
|
1711
|
-
"
|
|
1712
|
-
"
|
|
1713
|
-
"
|
|
1714
|
-
"
|
|
1715
|
-
"
|
|
1716
|
-
"
|
|
1717
|
-
"
|
|
1718
|
-
"
|
|
1719
|
-
"
|
|
1720
|
-
"
|
|
1721
|
-
"
|
|
1722
|
-
"
|
|
1723
|
-
"
|
|
1724
|
-
"
|
|
1725
|
-
"
|
|
1726
|
-
"
|
|
1727
|
-
"
|
|
1728
|
-
"
|
|
1729
|
-
"
|
|
1730
|
-
"
|
|
1731
|
-
"
|
|
1732
|
-
"
|
|
1733
|
-
"
|
|
1734
|
-
|
|
1735
|
-
"then",
|
|
1736
|
-
"toLocaleLowerCase",
|
|
1737
|
-
"toLocaleUpperCase",
|
|
1738
|
-
"toLowerCase",
|
|
1739
|
-
"toReversed",
|
|
1740
|
-
"toSorted",
|
|
1741
|
-
"toSpliced",
|
|
1742
|
-
"toUpperCase",
|
|
1743
|
-
"trim",
|
|
1744
|
-
"trimEnd",
|
|
1745
|
-
"trimStart",
|
|
1746
|
-
"unshift",
|
|
1747
|
-
"values",
|
|
1748
|
-
"with"
|
|
1749
|
-
];
|
|
1778
|
+
const REPLACEMENTS = {
|
|
1779
|
+
at: "Array.get or String.at",
|
|
1780
|
+
catch: "Effect.tryPromise and Effect.catchTag",
|
|
1781
|
+
charAt: "String.charAt",
|
|
1782
|
+
charCodeAt: "String.charCodeAt",
|
|
1783
|
+
codePointAt: "String.codePointAt",
|
|
1784
|
+
concat: "Array.appendAll or String.concat",
|
|
1785
|
+
endsWith: "String.endsWith",
|
|
1786
|
+
entries: "Array.map(items, (item, index) => [index, item]), Record.toEntries or HashMap.entries",
|
|
1787
|
+
fill: "Array.replicate or Array.makeBy",
|
|
1788
|
+
finally: "Effect.ensuring",
|
|
1789
|
+
flat: "Array.flatten",
|
|
1790
|
+
includes: "Array.contains or String.includes",
|
|
1791
|
+
indexOf: "Array.findFirstIndex or String.indexOf",
|
|
1792
|
+
keys: "Record.keys or HashMap.keys",
|
|
1793
|
+
lastIndexOf: "Array.findLastIndex or String.lastIndexOf",
|
|
1794
|
+
localeCompare: "String.localeCompare",
|
|
1795
|
+
match: "String.match",
|
|
1796
|
+
matchAll: "String.matchAll",
|
|
1797
|
+
normalize: "String.normalize",
|
|
1798
|
+
padEnd: "String.padEnd",
|
|
1799
|
+
padStart: "String.padStart",
|
|
1800
|
+
pop: "Array.last and Array.dropRight",
|
|
1801
|
+
push: "Array.append",
|
|
1802
|
+
repeat: "String.repeat",
|
|
1803
|
+
replace: "String.replace",
|
|
1804
|
+
replaceAll: "String.replaceAll",
|
|
1805
|
+
reverse: "Array.reverse",
|
|
1806
|
+
shift: "Array.head and Array.drop",
|
|
1807
|
+
slice: "Array.take, Array.drop or String.slice",
|
|
1808
|
+
sort: "Array.sort(items, Order.mapInput(...))",
|
|
1809
|
+
splice: "Array.remove or Array.insertAt",
|
|
1810
|
+
split: "String.split",
|
|
1811
|
+
startsWith: "String.startsWith",
|
|
1812
|
+
substring: "String.substring",
|
|
1813
|
+
then: "Effect.tryPromise and Effect.map",
|
|
1814
|
+
toLocaleLowerCase: "String.toLowerCase",
|
|
1815
|
+
toLocaleUpperCase: "String.toUpperCase",
|
|
1816
|
+
toLowerCase: "String.toLowerCase",
|
|
1817
|
+
toSpliced: "Array.remove",
|
|
1818
|
+
toUpperCase: "String.toUpperCase",
|
|
1819
|
+
trim: "String.trim",
|
|
1820
|
+
trimEnd: "String.trimEnd",
|
|
1821
|
+
trimStart: "String.trimStart",
|
|
1822
|
+
unshift: "Array.prepend",
|
|
1823
|
+
values: "Record.values or HashMap.values",
|
|
1824
|
+
with: "Array.replace"
|
|
1825
|
+
};
|
|
1750
1826
|
function freshRegExpFlags(input) {
|
|
1751
1827
|
if (input.node.type === "Literal" && hasProperty(input.node, "regex")) return some(pipe(input.context.sourceCode.getText(input.node), split("/"), last));
|
|
1752
1828
|
if (input.node.type !== "NewExpression" && input.node.type !== "CallExpression" || input.node.callee.type !== "Identifier" || input.node.callee.name !== "RegExp") return none();
|
|
@@ -1755,11 +1831,6 @@ function freshRegExpFlags(input) {
|
|
|
1755
1831
|
if (flags.type !== "Literal" || !isString(flags.value)) return some(none());
|
|
1756
1832
|
return some(some(flags.value));
|
|
1757
1833
|
}
|
|
1758
|
-
const nativeGlobals = [
|
|
1759
|
-
"Object",
|
|
1760
|
-
"Promise",
|
|
1761
|
-
"RegExp"
|
|
1762
|
-
];
|
|
1763
1834
|
function memberRoot(node) {
|
|
1764
1835
|
if (node.type === "MemberExpression") return memberRoot(node.object);
|
|
1765
1836
|
return node;
|
|
@@ -1771,7 +1842,7 @@ function chainRoot(node) {
|
|
|
1771
1842
|
return node;
|
|
1772
1843
|
}
|
|
1773
1844
|
function isYieldedService(node) {
|
|
1774
|
-
if (node?.type !== "YieldExpression" || !node.delegate || isNullish
|
|
1845
|
+
if (node?.type !== "YieldExpression" || !node.delegate || isNullish(node.argument)) return false;
|
|
1775
1846
|
if (node.argument.type === "Identifier") return matches(/^[A-Z]/u)(node.argument.name);
|
|
1776
1847
|
return node.argument.type === "MemberExpression" && exists(memberName(node.argument), (name) => matches(/^[A-Z]/u)(name));
|
|
1777
1848
|
}
|
|
@@ -1784,7 +1855,11 @@ function ownsMethod(input) {
|
|
|
1784
1855
|
if (root.type !== "Identifier") return false;
|
|
1785
1856
|
if (root.name === "path") return !throughCall;
|
|
1786
1857
|
return pipe(variableFor(input.context, root), filter((variable) => isReadonlyArrayNonEmpty(variable.defs)), match({
|
|
1787
|
-
onNone: () => !throughCall && matches(/^[A-Z]/u)(root.name) && !contains
|
|
1858
|
+
onNone: () => !throughCall && matches(/^[A-Z]/u)(root.name) && !contains([
|
|
1859
|
+
"Object",
|
|
1860
|
+
"Promise",
|
|
1861
|
+
"RegExp"
|
|
1862
|
+
], root.name),
|
|
1788
1863
|
onSome: (variable) => some$1(variable.defs, (definition) => {
|
|
1789
1864
|
if (definition.node.type === "VariableDeclarator") return !throughCall && isYieldedService(definition.node.init);
|
|
1790
1865
|
if (definition.type !== "ImportBinding" || definition.parent?.type !== "ImportDeclaration") return false;
|
|
@@ -1798,10 +1873,9 @@ const noNativeMethodCall = defineRule({
|
|
|
1798
1873
|
context,
|
|
1799
1874
|
receiver: node.callee.object
|
|
1800
1875
|
})) return;
|
|
1801
|
-
const receiver = node.callee.object;
|
|
1802
1876
|
const regExpFlags = freshRegExpFlags({
|
|
1803
1877
|
context,
|
|
1804
|
-
node:
|
|
1878
|
+
node: node.callee.object
|
|
1805
1879
|
});
|
|
1806
1880
|
if (isSome(regExpFlags) && exists(memberName(node.callee), (name) => name === "test" || name === "exec" && exists(flatten$1(regExpFlags), (flags) => !pipe(flags, includes("g"))))) {
|
|
1807
1881
|
context.report({
|
|
@@ -1810,62 +1884,21 @@ const noNativeMethodCall = defineRule({
|
|
|
1810
1884
|
});
|
|
1811
1885
|
return;
|
|
1812
1886
|
}
|
|
1813
|
-
pipe(memberName(node.callee), filter((name) =>
|
|
1887
|
+
pipe(memberName(node.callee), filter((name) => !contains([
|
|
1814
1888
|
"entries",
|
|
1815
1889
|
"keys",
|
|
1816
1890
|
"values"
|
|
1817
|
-
], name) || isArrayEmpty(node.arguments)
|
|
1818
|
-
|
|
1891
|
+
], name) || isArrayEmpty(node.arguments)), flatMap((name) => pipe(get$2(REPLACEMENTS, name), map((replacement) => ({
|
|
1892
|
+
name,
|
|
1893
|
+
replacement
|
|
1894
|
+
})))), map((method) => context.report({
|
|
1895
|
+
message: `Use ${method.replacement} instead of the native .${method.name}(); a third-party object's own method stays, with an inline disable naming it.`,
|
|
1819
1896
|
node: node.callee
|
|
1820
1897
|
})));
|
|
1821
1898
|
} }),
|
|
1822
1899
|
meta: { type: "problem" }
|
|
1823
1900
|
});
|
|
1824
1901
|
//#endregion
|
|
1825
|
-
//#region src/rules/no-nullish-ternary.ts
|
|
1826
|
-
const nullishPredicates = [
|
|
1827
|
-
"isNotNull",
|
|
1828
|
-
"isNotNullish",
|
|
1829
|
-
"isNotUndefined",
|
|
1830
|
-
"isNull",
|
|
1831
|
-
"isNullish",
|
|
1832
|
-
"isUndefined"
|
|
1833
|
-
];
|
|
1834
|
-
function isNullish(input) {
|
|
1835
|
-
if (input.node.type === "Literal") return input.node.value === null;
|
|
1836
|
-
return input.node.type === "Identifier" && input.node.name === "undefined";
|
|
1837
|
-
}
|
|
1838
|
-
function testsNullish(input) {
|
|
1839
|
-
if (input.node.type === "UnaryExpression" && input.node.operator === "!") return testsNullish({
|
|
1840
|
-
context: input.context,
|
|
1841
|
-
node: input.node.argument
|
|
1842
|
-
});
|
|
1843
|
-
if (input.node.type === "BinaryExpression" && (input.node.operator === "===" || input.node.operator === "!==")) return isNullish({
|
|
1844
|
-
context: input.context,
|
|
1845
|
-
node: input.node.left
|
|
1846
|
-
}) || isNullish({
|
|
1847
|
-
context: input.context,
|
|
1848
|
-
node: input.node.right
|
|
1849
|
-
});
|
|
1850
|
-
return input.node.type === "CallExpression" && input.node.callee.type === "MemberExpression" && importedMember({
|
|
1851
|
-
context: input.context,
|
|
1852
|
-
importedName: "Predicate",
|
|
1853
|
-
node: input.node.callee
|
|
1854
|
-
}) && exists(memberName(input.node.callee), (name) => contains$1(nullishPredicates, name));
|
|
1855
|
-
}
|
|
1856
|
-
const noNullishTernary = defineRule({
|
|
1857
|
-
create: (context) => ({ ConditionalExpression: (node) => {
|
|
1858
|
-
if (testsNullish({
|
|
1859
|
-
context,
|
|
1860
|
-
node: node.test
|
|
1861
|
-
})) context.report({
|
|
1862
|
-
message: "Use ?? for a default, or Option for a value that may be absent, instead of a nullish ternary.",
|
|
1863
|
-
node
|
|
1864
|
-
});
|
|
1865
|
-
} }),
|
|
1866
|
-
meta: { type: "problem" }
|
|
1867
|
-
});
|
|
1868
|
-
//#endregion
|
|
1869
1902
|
//#region src/rules/no-option-value-access.ts
|
|
1870
1903
|
function checks(input) {
|
|
1871
1904
|
if (input.test.type === "LogicalExpression" && input.test.operator === input.operator) return checks({
|
|
@@ -1883,7 +1916,7 @@ function checks(input) {
|
|
|
1883
1916
|
}) && input.test.arguments[0] !== void 0 && input.context.sourceCode.getText(input.test.arguments[0]) === input.subject;
|
|
1884
1917
|
}
|
|
1885
1918
|
function exits(node) {
|
|
1886
|
-
return contains
|
|
1919
|
+
return contains([
|
|
1887
1920
|
"ContinueStatement",
|
|
1888
1921
|
"ReturnStatement",
|
|
1889
1922
|
"ThrowStatement"
|
|
@@ -1920,7 +1953,7 @@ function guarded(input) {
|
|
|
1920
1953
|
}
|
|
1921
1954
|
const noOptionValueAccess = defineRule({
|
|
1922
1955
|
create: (context) => ({ MemberExpression: (node) => {
|
|
1923
|
-
if (!contains(memberName(node), "value")) return;
|
|
1956
|
+
if (!contains$1(memberName(node), "value")) return;
|
|
1924
1957
|
if (guarded({
|
|
1925
1958
|
context,
|
|
1926
1959
|
node,
|
|
@@ -1934,26 +1967,14 @@ const noOptionValueAccess = defineRule({
|
|
|
1934
1967
|
});
|
|
1935
1968
|
//#endregion
|
|
1936
1969
|
//#region src/rules/no-property-alias.ts
|
|
1937
|
-
const
|
|
1970
|
+
const FUNCTIONS = [
|
|
1938
1971
|
"ArrowFunctionExpression",
|
|
1939
1972
|
"FunctionDeclaration",
|
|
1940
1973
|
"FunctionExpression"
|
|
1941
1974
|
];
|
|
1942
|
-
const deferred = [
|
|
1943
|
-
"ChainExpression",
|
|
1944
|
-
"ConditionalExpression",
|
|
1945
|
-
"DoWhileStatement",
|
|
1946
|
-
"ForInStatement",
|
|
1947
|
-
"ForOfStatement",
|
|
1948
|
-
"ForStatement",
|
|
1949
|
-
"IfStatement",
|
|
1950
|
-
"LogicalExpression",
|
|
1951
|
-
"SwitchStatement",
|
|
1952
|
-
"WhileStatement"
|
|
1953
|
-
];
|
|
1954
1975
|
function insideFunction(node) {
|
|
1955
|
-
if (isNullish
|
|
1956
|
-
if (contains
|
|
1976
|
+
if (isNullish(node.parent) || node.parent.type === "Program") return false;
|
|
1977
|
+
if (contains(FUNCTIONS, node.parent.type)) return true;
|
|
1957
1978
|
return insideFunction(node.parent);
|
|
1958
1979
|
}
|
|
1959
1980
|
function sameRoot(input) {
|
|
@@ -1965,9 +1986,58 @@ function sameRoot(input) {
|
|
|
1965
1986
|
scope: input.context.sourceCode.getScope(input.read)
|
|
1966
1987
|
}));
|
|
1967
1988
|
}
|
|
1989
|
+
function pathTop(node) {
|
|
1990
|
+
if (node.parent?.type === "MemberExpression" && node.parent.object === node && !node.parent.computed) return pathTop(node.parent);
|
|
1991
|
+
return node;
|
|
1992
|
+
}
|
|
1993
|
+
function tested(node) {
|
|
1994
|
+
if (isNullish(node.parent)) return false;
|
|
1995
|
+
if (node.parent.type === "LogicalExpression") return node.parent.left === node || tested(node.parent);
|
|
1996
|
+
if (contains([
|
|
1997
|
+
"ChainExpression",
|
|
1998
|
+
"TSAsExpression",
|
|
1999
|
+
"TSNonNullExpression"
|
|
2000
|
+
], node.parent.type)) return tested(node.parent);
|
|
2001
|
+
return node.parent.type === "BinaryExpression" && contains([
|
|
2002
|
+
"!=",
|
|
2003
|
+
"!==",
|
|
2004
|
+
"==",
|
|
2005
|
+
"===",
|
|
2006
|
+
"in",
|
|
2007
|
+
"instanceof"
|
|
2008
|
+
], node.parent.operator) || node.parent.type === "UnaryExpression" && contains(["!", "typeof"], node.parent.operator) || node.parent.type === "ConditionalExpression" && node.parent.test === node || node.parent.type === "IfStatement" && node.parent.test === node || node.parent.type === "SwitchStatement" && node.parent.discriminant === node || node.parent.type === "CallExpression" && node.parent.callee !== node && !(node.parent.parent.type === "CallExpression" && node.parent.parent.callee === node.parent) && (predicate(node.parent.callee) || tested(node.parent));
|
|
2009
|
+
}
|
|
2010
|
+
function predicate(callee) {
|
|
2011
|
+
if (callee.type === "CallExpression") return predicate(callee.callee);
|
|
2012
|
+
if (callee.type === "Identifier") return matches(/^(?:has|is)(?:[A-Z]|$)/u)(callee.name);
|
|
2013
|
+
return callee.type === "MemberExpression" && callee.property.type === "Identifier" && matches(/^(?:has|is)(?:[A-Z]|$)/u)(callee.property.name);
|
|
2014
|
+
}
|
|
2015
|
+
function deferred(input) {
|
|
2016
|
+
if (isNullish(input.node.parent) || input.node.parent === input.block) return false;
|
|
2017
|
+
const parent = input.node.parent;
|
|
2018
|
+
if (contains(FUNCTIONS, parent.type) && !(parent.parent?.type === "CallExpression" && some$1(parent.parent.arguments, (argument) => argument === parent))) return true;
|
|
2019
|
+
return deferred({
|
|
2020
|
+
...input,
|
|
2021
|
+
node: parent
|
|
2022
|
+
});
|
|
2023
|
+
}
|
|
2024
|
+
function needsAlias(input) {
|
|
2025
|
+
if (input.declaration.parent.type !== "BlockStatement" || !contains(FUNCTIONS, input.declaration.parent.parent.type) || !pipe(input.declaration.parent.body, takeWhile((statement) => statement !== input.declaration), every((statement) => statement.type === "VariableDeclaration"))) return true;
|
|
2026
|
+
const block = input.declaration.parent;
|
|
2027
|
+
const paths = map$1(input.reads, (read) => ({
|
|
2028
|
+
callback: enclosed({
|
|
2029
|
+
block,
|
|
2030
|
+
node: read,
|
|
2031
|
+
types: FUNCTIONS
|
|
2032
|
+
}),
|
|
2033
|
+
node: pathTop(read),
|
|
2034
|
+
text: input.context.sourceCode.getText(pathTop(read))
|
|
2035
|
+
}));
|
|
2036
|
+
return some$1(paths, (checked) => tested(checked.node) && some$1(paths, (read) => read.callback && (read.text === checked.text || startsWith(`${checked.text}.`)(read.text))));
|
|
2037
|
+
}
|
|
1968
2038
|
function enclosed(input) {
|
|
1969
|
-
if (isNullish
|
|
1970
|
-
if (contains
|
|
2039
|
+
if (isNullish(input.node.parent) || input.node.parent === input.block) return false;
|
|
2040
|
+
if (contains(input.types, input.node.parent.type)) return true;
|
|
1971
2041
|
return enclosed({
|
|
1972
2042
|
...input,
|
|
1973
2043
|
node: input.node.parent
|
|
@@ -1986,24 +2056,6 @@ function written(node) {
|
|
|
1986
2056
|
if (node.type === "UpdateExpression" || node.type === "UnaryExpression" && node.operator === "delete") return objectRoot(node.argument);
|
|
1987
2057
|
return none();
|
|
1988
2058
|
}
|
|
1989
|
-
function inert(token) {
|
|
1990
|
-
if (contains$1([
|
|
1991
|
-
"Boolean",
|
|
1992
|
-
"Identifier",
|
|
1993
|
-
"Null",
|
|
1994
|
-
"Numeric",
|
|
1995
|
-
"String"
|
|
1996
|
-
], token.type)) return true;
|
|
1997
|
-
if (token.type === "Keyword") return contains$1(["const", "return"], token.value);
|
|
1998
|
-
return token.type === "Punctuator" && contains$1([
|
|
1999
|
-
"(",
|
|
2000
|
-
",",
|
|
2001
|
-
":",
|
|
2002
|
-
"=",
|
|
2003
|
-
"[",
|
|
2004
|
-
"{"
|
|
2005
|
-
], token.value);
|
|
2006
|
-
}
|
|
2007
2059
|
function replacement(input) {
|
|
2008
2060
|
if (input.read.parent?.type === "Property" && input.read.parent.shorthand) return `${input.name}: ${input.text}`;
|
|
2009
2061
|
return input.text;
|
|
@@ -2013,15 +2065,22 @@ function report$2(input) {
|
|
|
2013
2065
|
const text = input.context.sourceCode.getText(input.init);
|
|
2014
2066
|
pipe(rootName(input.init), filter((root) => isArrayNonEmpty(reads) && every(reads, (read) => {
|
|
2015
2067
|
const used = outermost(read);
|
|
2016
|
-
return read.parent.type !== "ExportSpecifier" && !
|
|
2068
|
+
return read.parent.type !== "ExportSpecifier" && (!needsAlias({
|
|
2069
|
+
context: input.context,
|
|
2070
|
+
declaration: input.declaration,
|
|
2071
|
+
reads
|
|
2072
|
+
}) || !enclosed({
|
|
2017
2073
|
block: input.declaration.parent,
|
|
2018
2074
|
node: read,
|
|
2019
|
-
types:
|
|
2075
|
+
types: FUNCTIONS
|
|
2076
|
+
})) && !deferred({
|
|
2077
|
+
block: input.declaration.parent,
|
|
2078
|
+
node: read
|
|
2020
2079
|
}) && !(used.parent?.type === "CallExpression" && used.parent.callee === used) && !(used.parent?.type === "TaggedTemplateExpression" && used.parent.tag === used) && !some$1(input.writes, (write) => between({
|
|
2021
2080
|
declaration: input.declaration,
|
|
2022
2081
|
node: write,
|
|
2023
2082
|
read
|
|
2024
|
-
}) && contains(written(write), root)) && sameRoot({
|
|
2083
|
+
}) && contains$1(written(write), root)) && sameRoot({
|
|
2025
2084
|
context: input.context,
|
|
2026
2085
|
declarator: input.node,
|
|
2027
2086
|
name: root,
|
|
@@ -2034,7 +2093,19 @@ function report$2(input) {
|
|
|
2034
2093
|
if (reads.length !== 1 || some$1(reads, (read) => startsWith("JSX")(read.parent.type) || enclosed({
|
|
2035
2094
|
block: input.declaration.parent,
|
|
2036
2095
|
node: read,
|
|
2037
|
-
types:
|
|
2096
|
+
types: [
|
|
2097
|
+
...FUNCTIONS,
|
|
2098
|
+
"ChainExpression",
|
|
2099
|
+
"ConditionalExpression",
|
|
2100
|
+
"DoWhileStatement",
|
|
2101
|
+
"ForInStatement",
|
|
2102
|
+
"ForOfStatement",
|
|
2103
|
+
"ForStatement",
|
|
2104
|
+
"IfStatement",
|
|
2105
|
+
"LogicalExpression",
|
|
2106
|
+
"SwitchStatement",
|
|
2107
|
+
"WhileStatement"
|
|
2108
|
+
]
|
|
2038
2109
|
}) || some$1(input.statements, (node) => between({
|
|
2039
2110
|
declaration: input.declaration,
|
|
2040
2111
|
node,
|
|
@@ -2080,6 +2151,19 @@ const noPropertyAlias = defineRule({
|
|
|
2080
2151
|
},
|
|
2081
2152
|
UpdateExpression: write,
|
|
2082
2153
|
VariableDeclarator: (node) => {
|
|
2154
|
+
if (node.parent.type === "VariableDeclaration" && node.parent.parent.type === "ExportNamedDeclaration" && node.id.type === "Identifier" && node.init?.type === "MemberExpression") {
|
|
2155
|
+
const init = node.init;
|
|
2156
|
+
pipe(rootName(init), flatMap((root) => variableFromScope({
|
|
2157
|
+
name: root,
|
|
2158
|
+
scope: context.sourceCode.getScope(node)
|
|
2159
|
+
})), filter((variable) => every(variable.defs, (definition) => definition.type === "Variable")), match({
|
|
2160
|
+
onNone: constVoid,
|
|
2161
|
+
onSome: (variable) => context.report({
|
|
2162
|
+
message: `Export ${variable.name} and read ${context.sourceCode.getText(init)} where it is used.`,
|
|
2163
|
+
node
|
|
2164
|
+
})
|
|
2165
|
+
}));
|
|
2166
|
+
}
|
|
2083
2167
|
if (node.parent.type === "VariableDeclaration" && node.parent.kind === "const" && node.parent.declarations.length === 1 && (node.parent.parent.type === "BlockStatement" || node.parent.parent.type === "StaticBlock") && node.id.type === "Identifier" && node.id.typeAnnotation === null && node.init?.type === "MemberExpression" && insideFunction(node.parent)) append$1(candidates, {
|
|
2084
2168
|
declaration: node.parent,
|
|
2085
2169
|
init: node.init,
|
|
@@ -2103,7 +2187,7 @@ function isTopLevelStatement(node) {
|
|
|
2103
2187
|
}
|
|
2104
2188
|
function insideCycleType(input) {
|
|
2105
2189
|
if (input.node.type === "Program") return false;
|
|
2106
|
-
if (input.node.type === "TSTypeAliasDeclaration" && isTopLevelStatement(input.node)) return contains
|
|
2190
|
+
if (input.node.type === "TSTypeAliasDeclaration" && isTopLevelStatement(input.node)) return contains(input.cycleNames, input.node.id.name);
|
|
2107
2191
|
return insideCycleType({
|
|
2108
2192
|
cycleNames: input.cycleNames,
|
|
2109
2193
|
node: input.node.parent
|
|
@@ -2144,7 +2228,7 @@ function shapeLiterals(input) {
|
|
|
2144
2228
|
return pipe(typeDeclarations({
|
|
2145
2229
|
context: input.context,
|
|
2146
2230
|
name: input.type.typeName
|
|
2147
|
-
}), filter$1((declaration) => declaration.type === "TSTypeAliasDeclaration"), filter$1((alias) => !contains
|
|
2231
|
+
}), filter$1((declaration) => declaration.type === "TSTypeAliasDeclaration"), filter$1((alias) => !contains(input.seen, alias)), flatMap$1((alias) => shapeLiterals({
|
|
2148
2232
|
...input,
|
|
2149
2233
|
seen: append(input.seen, alias),
|
|
2150
2234
|
type: alias.typeAnnotation
|
|
@@ -2292,7 +2376,7 @@ function isReactUseRef(input) {
|
|
|
2292
2376
|
node: input.node.callee,
|
|
2293
2377
|
source: "react"
|
|
2294
2378
|
});
|
|
2295
|
-
return input.node.callee.type === "MemberExpression" && contains(memberName(input.node.callee), "useRef") && input.node.callee.object.type === "Identifier" && isNamespaceImport({
|
|
2379
|
+
return input.node.callee.type === "MemberExpression" && contains$1(memberName(input.node.callee), "useRef") && input.node.callee.object.type === "Identifier" && isNamespaceImport({
|
|
2296
2380
|
context: input.context,
|
|
2297
2381
|
node: input.node.callee.object,
|
|
2298
2382
|
source: "react"
|
|
@@ -2318,16 +2402,10 @@ const noRedundantUseRefNullType = defineRule({
|
|
|
2318
2402
|
//#region src/rules/no-redundant-variable-annotation.ts
|
|
2319
2403
|
const noRedundantVariableAnnotation = defineRule({
|
|
2320
2404
|
create: (context) => ({ VariableDeclarator: (node) => {
|
|
2321
|
-
if (node.init
|
|
2322
|
-
if (
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
node: node.id.typeAnnotation
|
|
2326
|
-
});
|
|
2327
|
-
return;
|
|
2328
|
-
}
|
|
2329
|
-
if (node.init.type === "TSAsExpression" && context.sourceCode.getText(node.init.typeAnnotation) === context.sourceCode.getText(node.id.typeAnnotation.typeAnnotation)) context.report({
|
|
2330
|
-
message: "Drop this annotation; inference gives the same type.",
|
|
2405
|
+
if (node.init?.type !== "ArrayExpression" || isNullish(node.id.typeAnnotation)) return;
|
|
2406
|
+
if (!isArrayEmpty(node.init.elements)) return;
|
|
2407
|
+
context.report({
|
|
2408
|
+
message: "Write Array.empty<T>() instead of annotating an empty array.",
|
|
2331
2409
|
node: node.id.typeAnnotation
|
|
2332
2410
|
});
|
|
2333
2411
|
} }),
|
|
@@ -2367,7 +2445,7 @@ const shipped = [
|
|
|
2367
2445
|
schema: "Natural"
|
|
2368
2446
|
}
|
|
2369
2447
|
];
|
|
2370
|
-
function schemaMember(input) {
|
|
2448
|
+
function schemaMember$1(input) {
|
|
2371
2449
|
if (input.node.type !== "MemberExpression" || !importedMember({
|
|
2372
2450
|
context: input.context,
|
|
2373
2451
|
importedName: "Schema",
|
|
@@ -2377,16 +2455,16 @@ function schemaMember(input) {
|
|
|
2377
2455
|
}
|
|
2378
2456
|
function shippedSchema(input) {
|
|
2379
2457
|
if (input.filter.type !== "CallExpression") return none();
|
|
2380
|
-
const base = schemaMember({
|
|
2458
|
+
const base = schemaMember$1({
|
|
2381
2459
|
context: input.context,
|
|
2382
2460
|
node: input.base
|
|
2383
2461
|
});
|
|
2384
|
-
const check = schemaMember({
|
|
2462
|
+
const check = schemaMember$1({
|
|
2385
2463
|
context: input.context,
|
|
2386
2464
|
node: input.filter.callee
|
|
2387
2465
|
});
|
|
2388
2466
|
const [value, ...rest] = input.filter.arguments;
|
|
2389
|
-
return pipe(shipped, findFirst((entry) => contains(base, entry.base) && contains(check, entry.check) && isArrayEmpty(rest) && match(fromUndefinedOr(entry.only), {
|
|
2467
|
+
return pipe(shipped, findFirst((entry) => contains$1(base, entry.base) && contains$1(check, entry.check) && isArrayEmpty(rest) && match(fromUndefinedOr(entry.only), {
|
|
2390
2468
|
onNone: () => value === void 0,
|
|
2391
2469
|
onSome: (only) => value?.type === "Literal" && value.value === only
|
|
2392
2470
|
})), map((entry) => entry.schema));
|
|
@@ -2397,14 +2475,14 @@ function pipedBase(node) {
|
|
|
2397
2475
|
if (node.parent.arguments[1] !== node) return none();
|
|
2398
2476
|
return fromNullishOr(node.parent.arguments[0]);
|
|
2399
2477
|
}
|
|
2400
|
-
if (node.parent.callee.type !== "MemberExpression" || !contains(memberName(node.parent.callee), "pipe") || node.parent.arguments.length !== 1) return none();
|
|
2478
|
+
if (node.parent.callee.type !== "MemberExpression" || !contains$1(memberName(node.parent.callee), "pipe") || node.parent.arguments.length !== 1) return none();
|
|
2401
2479
|
return some(node.parent.callee.object);
|
|
2402
2480
|
}
|
|
2403
2481
|
function reinventedSchema(input) {
|
|
2404
|
-
if (input.node.callee.type !== "MemberExpression" || !contains(memberName(input.node.callee), "check")) return none();
|
|
2482
|
+
if (input.node.callee.type !== "MemberExpression" || !contains$1(memberName(input.node.callee), "check")) return none();
|
|
2405
2483
|
const callee = input.node.callee;
|
|
2406
2484
|
const [filter] = input.node.arguments;
|
|
2407
|
-
if (isSome(schemaMember({
|
|
2485
|
+
if (isSome(schemaMember$1({
|
|
2408
2486
|
context: input.context,
|
|
2409
2487
|
node: callee
|
|
2410
2488
|
}))) {
|
|
@@ -2500,7 +2578,7 @@ function recursive(input) {
|
|
|
2500
2578
|
const noReturnType = defineRule({
|
|
2501
2579
|
create: (context) => {
|
|
2502
2580
|
function check(node) {
|
|
2503
|
-
if (isNullish
|
|
2581
|
+
if (isNullish(node.returnType) || node.body === null || node.returnType.typeAnnotation.type === "TSTypePredicate" || node.parent.type === "CallExpression" && importedMember({
|
|
2504
2582
|
context,
|
|
2505
2583
|
importedName: "Schema",
|
|
2506
2584
|
node: node.parent.callee,
|
|
@@ -2524,7 +2602,7 @@ const noReturnType = defineRule({
|
|
|
2524
2602
|
});
|
|
2525
2603
|
//#endregion
|
|
2526
2604
|
//#region src/rules/no-single-use-value.ts
|
|
2527
|
-
const
|
|
2605
|
+
const REPEATING = [
|
|
2528
2606
|
"ArrowFunctionExpression",
|
|
2529
2607
|
"DoWhileStatement",
|
|
2530
2608
|
"ForInStatement",
|
|
@@ -2539,7 +2617,7 @@ function runsOnce(input) {
|
|
|
2539
2617
|
...input,
|
|
2540
2618
|
node: input.node
|
|
2541
2619
|
});
|
|
2542
|
-
if (contains
|
|
2620
|
+
if (contains(REPEATING, input.node.type) || input.node.type === "IfStatement" || input.node.type === "ConditionalExpression" || input.node.type === "LogicalExpression" || input.node.type === "ChainExpression" || input.node.type === "SwitchStatement" || input.node.type === "TryStatement") return false;
|
|
2543
2621
|
return climb({
|
|
2544
2622
|
...input,
|
|
2545
2623
|
node: input.node
|
|
@@ -2547,7 +2625,7 @@ function runsOnce(input) {
|
|
|
2547
2625
|
}
|
|
2548
2626
|
function climb(input) {
|
|
2549
2627
|
if (input.node === input.statement) return true;
|
|
2550
|
-
if (isNullish
|
|
2628
|
+
if (isNullish(input.node.parent)) return false;
|
|
2551
2629
|
return runsOnce({
|
|
2552
2630
|
child: input.node,
|
|
2553
2631
|
node: input.node.parent,
|
|
@@ -2562,8 +2640,16 @@ function insideConditional(input) {
|
|
|
2562
2640
|
node: input.node.parent
|
|
2563
2641
|
});
|
|
2564
2642
|
}
|
|
2565
|
-
function candidate(node) {
|
|
2566
|
-
return node.parent.type === "VariableDeclaration" && node.parent.kind === "const" && node.parent.declarations.length === 1 && node.parent.parent.type === "BlockStatement" && node.id.type === "Identifier" && node.id.typeAnnotation === null && node.init !== null && !contains
|
|
2643
|
+
function candidate(context, node) {
|
|
2644
|
+
return node.parent.type === "VariableDeclaration" && node.parent.kind === "const" && node.parent.declarations.length === 1 && node.parent.parent.type === "BlockStatement" && node.id.type === "Identifier" && node.id.typeAnnotation === null && node.init !== null && !contains([
|
|
2645
|
+
"ArrowFunctionExpression",
|
|
2646
|
+
"FunctionExpression",
|
|
2647
|
+
"JSXElement",
|
|
2648
|
+
"JSXFragment"
|
|
2649
|
+
], unwrapped(node.init).type) && !(node.init.type === "CallExpression" && isSchemaOperationCall({
|
|
2650
|
+
context,
|
|
2651
|
+
node: node.init
|
|
2652
|
+
})) && !(node.init.type === "MemberExpression" && isSome(rootName(node.init))) && !(node.init.type === "Literal" && !hasProperty(node.init, "regex")) && !(node.init.type === "TemplateLiteral" && isArrayEmpty(node.init.expressions));
|
|
2567
2653
|
}
|
|
2568
2654
|
function unwrapped(node) {
|
|
2569
2655
|
return pipe(typeWrapped(node), match({
|
|
@@ -2581,10 +2667,61 @@ function inlined(input) {
|
|
|
2581
2667
|
if (input.read.range[0] === input.next.range[0]) return `;${value}`;
|
|
2582
2668
|
return value;
|
|
2583
2669
|
}
|
|
2670
|
+
function executes(node) {
|
|
2671
|
+
if (contains([
|
|
2672
|
+
"AwaitExpression",
|
|
2673
|
+
"CallExpression",
|
|
2674
|
+
"MemberExpression",
|
|
2675
|
+
"NewExpression",
|
|
2676
|
+
"SpreadElement",
|
|
2677
|
+
"TaggedTemplateExpression",
|
|
2678
|
+
"YieldExpression"
|
|
2679
|
+
], node.type)) return true;
|
|
2680
|
+
if (contains([
|
|
2681
|
+
"ArrowFunctionExpression",
|
|
2682
|
+
"FunctionDeclaration",
|
|
2683
|
+
"FunctionExpression"
|
|
2684
|
+
], node.type)) return false;
|
|
2685
|
+
return some$1(childNodes(node), executes);
|
|
2686
|
+
}
|
|
2687
|
+
function suspends(node) {
|
|
2688
|
+
if (node.type === "YieldExpression" || node.type === "AwaitExpression") return true;
|
|
2689
|
+
if (contains([
|
|
2690
|
+
"ArrowFunctionExpression",
|
|
2691
|
+
"FunctionDeclaration",
|
|
2692
|
+
"FunctionExpression"
|
|
2693
|
+
], node.type)) return false;
|
|
2694
|
+
return some$1(childNodes(node), suspends);
|
|
2695
|
+
}
|
|
2696
|
+
function surface(node) {
|
|
2697
|
+
if (contains([
|
|
2698
|
+
"ExpressionStatement",
|
|
2699
|
+
"ReturnStatement",
|
|
2700
|
+
"VariableDeclaration"
|
|
2701
|
+
], node.type) || node.type === "ArrowFunctionExpression" && node.expression) return true;
|
|
2702
|
+
if (isNullish(node.parent) || !contains([
|
|
2703
|
+
"AwaitExpression",
|
|
2704
|
+
"JSXAttribute",
|
|
2705
|
+
"JSXElement",
|
|
2706
|
+
"JSXExpressionContainer",
|
|
2707
|
+
"JSXFragment",
|
|
2708
|
+
"JSXOpeningElement",
|
|
2709
|
+
"VariableDeclarator",
|
|
2710
|
+
"YieldExpression"
|
|
2711
|
+
], node.type)) return false;
|
|
2712
|
+
return surface(node.parent);
|
|
2713
|
+
}
|
|
2714
|
+
function readable(input) {
|
|
2715
|
+
if (!pipe(input.context.sourceCode.getText(input.init), includes("\n"))) return true;
|
|
2716
|
+
const slot = outermost(input.read);
|
|
2717
|
+
if (isNullish(slot.parent) || suspends(input.init)) return false;
|
|
2718
|
+
if (slot.parent.type === "JSXExpressionContainer" || slot.parent.type === "ReturnStatement") return true;
|
|
2719
|
+
return isNotNullish(slot.parent.parent) && (slot.parent.type === "CallExpression" && slot.parent.callee !== slot || slot.parent.type === "ArrayExpression") && surface(slot.parent.parent);
|
|
2720
|
+
}
|
|
2584
2721
|
function report$1(input) {
|
|
2585
2722
|
const declaration = input.node.parent;
|
|
2586
2723
|
const init = input.node.init;
|
|
2587
|
-
if (declaration.type !== "VariableDeclaration" || declaration.parent.type !== "BlockStatement" || input.node.id.type !== "Identifier" || init === null
|
|
2724
|
+
if (declaration.type !== "VariableDeclaration" || declaration.parent.type !== "BlockStatement" || input.node.id.type !== "Identifier" || init === null) return;
|
|
2588
2725
|
const statements = declaration.parent.body;
|
|
2589
2726
|
const name = input.node.id.name;
|
|
2590
2727
|
pipe(findFirstIndex(statements, (statement) => statement === declaration), flatMap((index) => get$1(statements, index + 1)), flatMap((next) => pipe(variableFromScope({
|
|
@@ -2593,6 +2730,10 @@ function report$1(input) {
|
|
|
2593
2730
|
}), map((variable) => filter$1(variable.references, (reference) => reference.isRead())), filter((reads) => reads.length === 1), flatMap(head), map((reference) => reference.identifier), filter((read) => read.range[0] >= next.range[0] && read.range[1] <= next.range[1] && climb({
|
|
2594
2731
|
node: read,
|
|
2595
2732
|
statement: next
|
|
2733
|
+
}) && readable({
|
|
2734
|
+
context: input.context,
|
|
2735
|
+
init,
|
|
2736
|
+
read
|
|
2596
2737
|
}) && !(init.type === "ConditionalExpression" && insideConditional({
|
|
2597
2738
|
node: read,
|
|
2598
2739
|
statement: next
|
|
@@ -2606,7 +2747,7 @@ function report$1(input) {
|
|
|
2606
2747
|
if (!fixable({
|
|
2607
2748
|
init,
|
|
2608
2749
|
read: found.read
|
|
2609
|
-
})) return [];
|
|
2750
|
+
}) || executes(init) && !every(input.context.sourceCode.getTokensBetween(declaration, found.read), inert)) return [];
|
|
2610
2751
|
return [fixer.remove(declaration), fixer.replaceText(found.read, inlined({
|
|
2611
2752
|
context: input.context,
|
|
2612
2753
|
init,
|
|
@@ -2641,7 +2782,7 @@ const noSingleUseValue = defineRule({
|
|
|
2641
2782
|
TaggedTemplateExpression: effect,
|
|
2642
2783
|
UpdateExpression: effect,
|
|
2643
2784
|
VariableDeclarator: (node) => {
|
|
2644
|
-
if (candidate(node)) update(candidates, append(node));
|
|
2785
|
+
if (candidate(context, node)) update(candidates, append(node));
|
|
2645
2786
|
},
|
|
2646
2787
|
YieldExpression: effect
|
|
2647
2788
|
};
|
|
@@ -2652,6 +2793,22 @@ const noSingleUseValue = defineRule({
|
|
|
2652
2793
|
}
|
|
2653
2794
|
});
|
|
2654
2795
|
//#endregion
|
|
2796
|
+
//#region src/rules/no-stored-jsx.ts
|
|
2797
|
+
function element(node) {
|
|
2798
|
+
if (node.type === "JSXElement" || node.type === "JSXFragment") return true;
|
|
2799
|
+
return pipe(typeWrapped(node), exists(element));
|
|
2800
|
+
}
|
|
2801
|
+
const noStoredJsx = defineRule({
|
|
2802
|
+
create: (context) => ({ VariableDeclarator: (node) => {
|
|
2803
|
+
if (node.init === null || !element(node.init)) return;
|
|
2804
|
+
context.report({
|
|
2805
|
+
message: "Write the element where it renders, or make it a component when several places render it.",
|
|
2806
|
+
node
|
|
2807
|
+
});
|
|
2808
|
+
} }),
|
|
2809
|
+
meta: { type: "problem" }
|
|
2810
|
+
});
|
|
2811
|
+
//#endregion
|
|
2655
2812
|
//#region src/rules/no-stored-schema-operation.ts
|
|
2656
2813
|
function storedSchemaOperation(node) {
|
|
2657
2814
|
return node.parent.type === "VariableDeclarator" && node.parent.init === node || node.parent.type === "Property" && node.parent.value === node || node.parent.type === "PropertyDefinition" && node.parent.value === node || node.parent.type === "ArrayExpression" || node.parent.type === "AssignmentExpression" && node.parent.right === node;
|
|
@@ -2673,7 +2830,7 @@ const noStoredSchemaOperation = defineRule({
|
|
|
2673
2830
|
const noSwitchStatement = defineRule({
|
|
2674
2831
|
create: (context) => ({ SwitchStatement: (node) => {
|
|
2675
2832
|
context.report({
|
|
2676
|
-
message: "Use Match instead of switch.",
|
|
2833
|
+
message: "Use pipe(Match.value(x), Match.tag(...) or Match.when(...), Match.exhaustive) instead of switch.",
|
|
2677
2834
|
node
|
|
2678
2835
|
});
|
|
2679
2836
|
} }),
|
|
@@ -2715,7 +2872,7 @@ function detachable(input) {
|
|
|
2715
2872
|
function forwardedChain(input) {
|
|
2716
2873
|
const chain = callGroups(input.returned);
|
|
2717
2874
|
function fixed(node) {
|
|
2718
|
-
return node.type === "Literal" || exists(rootName(node), (root) => !contains
|
|
2875
|
+
return node.type === "Literal" || exists(rootName(node), (root) => !contains(input.names, root));
|
|
2719
2876
|
}
|
|
2720
2877
|
return matchRight(chain.groups, {
|
|
2721
2878
|
onEmpty: constFalse,
|
|
@@ -2731,7 +2888,7 @@ function forwardedChain(input) {
|
|
|
2731
2888
|
function exactForwardingFunction(input) {
|
|
2732
2889
|
const names = pipe(input.node.params, map$1(parameterName), getSomes);
|
|
2733
2890
|
const returned = returnedExpression(input.node);
|
|
2734
|
-
if (input.node.async || input.node.generator || isArrayEmpty(names) || names.length !== input.node.params.length || isNullish
|
|
2891
|
+
if (input.node.async || input.node.generator || isArrayEmpty(names) || names.length !== input.node.params.length || isNullish(returned)) return false;
|
|
2735
2892
|
if (returned.type === "Identifier") return names.length === 1 && returned.name === names[0];
|
|
2736
2893
|
return returned.type === "NewExpression" && returned.callee.type === "Identifier" && forwarded({
|
|
2737
2894
|
args: returned.arguments,
|
|
@@ -2789,7 +2946,7 @@ function exportedAlias(input) {
|
|
|
2789
2946
|
return input.node.parent.type === "VariableDeclaration" && input.node.parent.kind === "const" && input.node.parent.parent.type === "ExportNamedDeclaration" && input.node.id.type === "Identifier" && input.node.init?.type === "Identifier" && pipe(variableFor(input.context, input.node.init), exists((variable) => some$1(variable.defs, (definition) => definition.type === "ImportBinding" || definition.type === "FunctionName" || definition.type === "ClassName" || definition.node.type === "VariableDeclarator" && definition.node.parent.type === "VariableDeclaration" && definition.node.parent.kind === "const")));
|
|
2790
2947
|
}
|
|
2791
2948
|
function singleUseLiteral(input) {
|
|
2792
|
-
if (input.node.parent.type !== "VariableDeclaration" || input.node.parent.kind !== "const" || isExport(input.node.parent.parent) || input.node.id.type !== "Identifier" || input.node.id.typeAnnotation !== null || !(input.node.init?.type === "Literal" && !hasProperty(input.node.init, "regex") || input.node.init?.type === "TemplateLiteral" && isArrayEmpty(input.node.init.expressions))) return false;
|
|
2949
|
+
if (input.node.parent.type !== "VariableDeclaration" || input.node.parent.kind !== "const" || isExport(input.node.parent.parent) || input.node.parent.parent.type === "Program" || input.node.id.type !== "Identifier" || input.node.id.typeAnnotation !== null || !(input.node.init?.type === "Literal" && !hasProperty(input.node.init, "regex") || input.node.init?.type === "TemplateLiteral" && isArrayEmpty(input.node.init.expressions))) return false;
|
|
2793
2950
|
return pipe(variableFromScope({
|
|
2794
2951
|
name: input.node.id.name,
|
|
2795
2952
|
scope: input.context.sourceCode.getScope(input.node)
|
|
@@ -2804,7 +2961,7 @@ function singleUseLayerPart(input) {
|
|
|
2804
2961
|
}), map((variable) => filter$1(variable.references, (reference) => reference.isRead())), exists((reads) => {
|
|
2805
2962
|
const consumer = reads[0]?.identifier.parent;
|
|
2806
2963
|
if (reads.length !== 1 || consumer?.type !== "CallExpression") return false;
|
|
2807
|
-
if (init.type === "MemberExpression") return contains(memberName(init), "layer") && (importedMember({
|
|
2964
|
+
if (init.type === "MemberExpression") return contains$1(memberName(init), "layer") && (importedMember({
|
|
2808
2965
|
context: input.context,
|
|
2809
2966
|
importedName: "Layer",
|
|
2810
2967
|
node: consumer.callee
|
|
@@ -2827,10 +2984,17 @@ function singleUseLayerPart(input) {
|
|
|
2827
2984
|
});
|
|
2828
2985
|
}));
|
|
2829
2986
|
}
|
|
2987
|
+
function isComponent(node) {
|
|
2988
|
+
if (node.type === "FunctionDeclaration") return exists(fromNullishOr(node.id), (id) => matches(/^[A-Z]/u)(id.name));
|
|
2989
|
+
return node.parent.type === "VariableDeclarator" && node.parent.id.type === "Identifier" && matches(/^[A-Z]/u)(node.parent.id.name);
|
|
2990
|
+
}
|
|
2991
|
+
function isCatchHandler(node) {
|
|
2992
|
+
return node.parent.type === "Property" && !node.parent.computed && node.parent.key.type === "Identifier" && node.parent.key.name === "catch";
|
|
2993
|
+
}
|
|
2830
2994
|
const noTrivialIndirection = defineRule({
|
|
2831
2995
|
create: (context) => ({
|
|
2832
2996
|
ArrowFunctionExpression: (node) => {
|
|
2833
|
-
if ((node.parent.type === "VariableDeclarator" || node.parent.type === "Property") && (exactForwardingFunction({
|
|
2997
|
+
if ((node.parent.type === "VariableDeclarator" || node.parent.type === "Property") && !isComponent(node) && !isCatchHandler(node) && (exactForwardingFunction({
|
|
2834
2998
|
context,
|
|
2835
2999
|
node
|
|
2836
3000
|
}) || singleUseThunk({
|
|
@@ -2848,7 +3012,7 @@ const noTrivialIndirection = defineRule({
|
|
|
2848
3012
|
});
|
|
2849
3013
|
},
|
|
2850
3014
|
FunctionDeclaration: (node) => {
|
|
2851
|
-
if (exactForwardingFunction({
|
|
3015
|
+
if (!isComponent(node) && (exactForwardingFunction({
|
|
2852
3016
|
context,
|
|
2853
3017
|
node
|
|
2854
3018
|
}) || singleUseThunk({
|
|
@@ -2857,7 +3021,7 @@ const noTrivialIndirection = defineRule({
|
|
|
2857
3021
|
}) || singleCallerWrapper({
|
|
2858
3022
|
context,
|
|
2859
3023
|
node
|
|
2860
|
-
})) context.report({
|
|
3024
|
+
}))) context.report({
|
|
2861
3025
|
message: indirectionMessage({
|
|
2862
3026
|
context,
|
|
2863
3027
|
node
|
|
@@ -2866,7 +3030,7 @@ const noTrivialIndirection = defineRule({
|
|
|
2866
3030
|
});
|
|
2867
3031
|
},
|
|
2868
3032
|
FunctionExpression: (node) => {
|
|
2869
|
-
if ((node.parent.type === "VariableDeclarator" || node.parent.type === "Property" || node.parent.type === "MethodDefinition" && node.parent.override !== true) && (exactForwardingFunction({
|
|
3033
|
+
if ((node.parent.type === "VariableDeclarator" || node.parent.type === "Property" || node.parent.type === "MethodDefinition" && node.parent.override !== true) && !isComponent(node) && !isCatchHandler(node) && (exactForwardingFunction({
|
|
2870
3034
|
context,
|
|
2871
3035
|
node
|
|
2872
3036
|
}) || singleUseThunk({
|
|
@@ -2895,7 +3059,7 @@ const noTrivialIndirection = defineRule({
|
|
|
2895
3059
|
context,
|
|
2896
3060
|
node
|
|
2897
3061
|
})) context.report({
|
|
2898
|
-
message:
|
|
3062
|
+
message: `Use ${context.sourceCode.getText(node.init)} directly; this name is an alias.`,
|
|
2899
3063
|
node
|
|
2900
3064
|
});
|
|
2901
3065
|
if (singleUseLiteral({
|
|
@@ -2917,13 +3081,57 @@ const noTrivialIndirection = defineRule({
|
|
|
2917
3081
|
meta: { type: "problem" }
|
|
2918
3082
|
});
|
|
2919
3083
|
//#endregion
|
|
3084
|
+
//#region src/rules/no-type-from-value.ts
|
|
3085
|
+
function schemaMember(name) {
|
|
3086
|
+
return name.type === "TSQualifiedName" && contains([
|
|
3087
|
+
"Encoded",
|
|
3088
|
+
"Type",
|
|
3089
|
+
"cases",
|
|
3090
|
+
"fields",
|
|
3091
|
+
"literals",
|
|
3092
|
+
"members"
|
|
3093
|
+
], name.right.name);
|
|
3094
|
+
}
|
|
3095
|
+
function valueQuery(node) {
|
|
3096
|
+
if (node.type === "TSParenthesizedType") return valueQuery(node.typeAnnotation);
|
|
3097
|
+
if (node.type === "TSIndexedAccessType") return valueQuery(node.objectType);
|
|
3098
|
+
return node.type === "TSTypeQuery" && (node.exprName.type === "Identifier" || node.exprName.type === "TSQualifiedName" && !schemaMember(node.exprName));
|
|
3099
|
+
}
|
|
3100
|
+
function nestedObject(node) {
|
|
3101
|
+
return node.parent?.type === "TSIndexedAccessType" && node.parent.objectType === node;
|
|
3102
|
+
}
|
|
3103
|
+
const noTypeFromValue = defineRule({
|
|
3104
|
+
create: (context) => {
|
|
3105
|
+
function report(node) {
|
|
3106
|
+
context.report({
|
|
3107
|
+
message: "Declare the closed set as a schema, such as Schema.Literals, and read its type with typeof X.Type.",
|
|
3108
|
+
node
|
|
3109
|
+
});
|
|
3110
|
+
}
|
|
3111
|
+
return {
|
|
3112
|
+
TSIndexedAccessType: (node) => {
|
|
3113
|
+
if (!nestedObject(node) && valueQuery(node.objectType)) report(node);
|
|
3114
|
+
},
|
|
3115
|
+
TSTypeOperator: (node) => {
|
|
3116
|
+
if (node.operator === "keyof" && !nestedObject(node) && valueQuery(node.typeAnnotation)) report(node);
|
|
3117
|
+
}
|
|
3118
|
+
};
|
|
3119
|
+
},
|
|
3120
|
+
meta: { type: "problem" }
|
|
3121
|
+
});
|
|
3122
|
+
//#endregion
|
|
2920
3123
|
//#region src/rules/no-typeof.ts
|
|
3124
|
+
function enclosingFunction(node) {
|
|
3125
|
+
if (node.type === "ArrowFunctionExpression" || node.type === "FunctionDeclaration" || node.type === "FunctionExpression") return some(node);
|
|
3126
|
+
return flatMap(fromNullishOr(node.parent), enclosingFunction);
|
|
3127
|
+
}
|
|
2921
3128
|
const noTypeof = defineRule({
|
|
2922
3129
|
create: (context) => ({ UnaryExpression: (node) => {
|
|
2923
3130
|
if (node.operator !== "typeof") return;
|
|
2924
3131
|
if (node.argument.type === "Identifier" && !exists(variableFor(context, node.argument), (variable) => isArrayNonEmpty(variable.defs))) return;
|
|
3132
|
+
if (exists(enclosingFunction(node), (fn) => isSome(replaceableGuard(fn)))) return;
|
|
2925
3133
|
context.report({
|
|
2926
|
-
message: "Replace typeof with
|
|
3134
|
+
message: "Replace typeof with its Predicate guard: Predicate.isString, isNumber, isBoolean, isFunction, isObject or isUndefined.",
|
|
2927
3135
|
node
|
|
2928
3136
|
});
|
|
2929
3137
|
} }),
|
|
@@ -3001,7 +3209,7 @@ const noUnvalidatedJsonDecode = defineRule({
|
|
|
3001
3209
|
//#region src/rules/preserve-error-cause.ts
|
|
3002
3210
|
function constructedFields(node) {
|
|
3003
3211
|
if (node?.type !== "CallExpression" && node?.type !== "NewExpression") return none();
|
|
3004
|
-
const constructs = node.type === "NewExpression" || node.callee.type === "MemberExpression" && contains(memberName(node.callee), "make");
|
|
3212
|
+
const constructs = node.type === "NewExpression" || node.callee.type === "MemberExpression" && contains$1(memberName(node.callee), "make");
|
|
3005
3213
|
const fields = node.arguments[0];
|
|
3006
3214
|
if (!constructs || fields?.type !== "ObjectExpression") return none();
|
|
3007
3215
|
return some(fields);
|
|
@@ -3024,7 +3232,7 @@ const preserveErrorCause = defineRule({
|
|
|
3024
3232
|
node: node.callee
|
|
3025
3233
|
})) return;
|
|
3026
3234
|
const name = memberName(node.callee);
|
|
3027
|
-
if (contains(name, "mapError")) {
|
|
3235
|
+
if (contains$1(name, "mapError")) {
|
|
3028
3236
|
forEach(filter$1(node.arguments, dropsCause), (callback) => report(context, callback));
|
|
3029
3237
|
return;
|
|
3030
3238
|
}
|
|
@@ -3085,7 +3293,7 @@ function isSchemaDefinition(input) {
|
|
|
3085
3293
|
context: input.context,
|
|
3086
3294
|
importedName: "Schema",
|
|
3087
3295
|
node: input.node.callee
|
|
3088
|
-
}) && contains(memberName(input.node.callee), "make")) return false;
|
|
3296
|
+
}) && contains$1(memberName(input.node.callee), "make")) return false;
|
|
3089
3297
|
if (!expressionUsesImport({
|
|
3090
3298
|
context: input.context,
|
|
3091
3299
|
importedName: "Schema",
|
|
@@ -3126,7 +3334,7 @@ function exportLists(program) {
|
|
|
3126
3334
|
};
|
|
3127
3335
|
}
|
|
3128
3336
|
function isExported(input) {
|
|
3129
|
-
return input.statement.type === "ExportNamedDeclaration" || contains
|
|
3337
|
+
return input.statement.type === "ExportNamedDeclaration" || contains(input.exports.values, input.name) || input.typeOnly && contains(input.exports.types, input.name);
|
|
3130
3338
|
}
|
|
3131
3339
|
function reportSchemaVariable(input) {
|
|
3132
3340
|
if (input.variable.id.type !== "Identifier" || !matches(/^[A-Z]/u)(input.variable.id.name)) return;
|
|
@@ -3136,7 +3344,7 @@ function reportSchemaVariable(input) {
|
|
|
3136
3344
|
})) return;
|
|
3137
3345
|
const name = input.variable.id.name;
|
|
3138
3346
|
const typeStatement = pipe(input.program.body, findFirstIndex((statement) => statement === input.statement), filter((index) => index > 0), flatMap((index) => get$1(input.program.body, index - 1)));
|
|
3139
|
-
if (contains
|
|
3347
|
+
if (contains(input.cycleNames, name)) {
|
|
3140
3348
|
if (!pipe(namedTypeAlias({
|
|
3141
3349
|
name,
|
|
3142
3350
|
typeStatement
|
|
@@ -3215,7 +3423,7 @@ const schemaTypePair = defineRule({
|
|
|
3215
3423
|
});
|
|
3216
3424
|
//#endregion
|
|
3217
3425
|
//#region src/oxlint.ts
|
|
3218
|
-
const
|
|
3426
|
+
const REACT_LEGACY_APIS = [
|
|
3219
3427
|
"Component",
|
|
3220
3428
|
"PureComponent",
|
|
3221
3429
|
"createRef",
|
|
@@ -3224,9 +3432,9 @@ const reactLegacyApis = [
|
|
|
3224
3432
|
"useCallback",
|
|
3225
3433
|
"useMemo"
|
|
3226
3434
|
];
|
|
3227
|
-
const
|
|
3228
|
-
const
|
|
3229
|
-
const
|
|
3435
|
+
const REACT_LEGACY_MESSAGE = "Use React 19 function components with refs as props; React Compiler owns memoization.";
|
|
3436
|
+
const MAP_MESSAGE = "Use HashMap, or MutableHashMap for local mutation; for identity keys, wrap objects the code owns in Equal.byReferenceUnsafe, which marks them reference-compared globally.";
|
|
3437
|
+
const REACT_HOOKS = [
|
|
3230
3438
|
"use",
|
|
3231
3439
|
"useActionState",
|
|
3232
3440
|
"useContext",
|
|
@@ -3245,8 +3453,8 @@ const reactHooks = [
|
|
|
3245
3453
|
"useSyncExternalStore",
|
|
3246
3454
|
"useTransition"
|
|
3247
3455
|
];
|
|
3248
|
-
const
|
|
3249
|
-
const
|
|
3456
|
+
const RUN_MESSAGE = "Run the entry point with NodeRuntime.runMain, and Effects in React through atoms. A Promise that an external contract demands takes Effect.runPromise once, with an inline disable that names the contract.";
|
|
3457
|
+
const RUNNERS = [
|
|
3250
3458
|
"runCallback",
|
|
3251
3459
|
"runCallbackWith",
|
|
3252
3460
|
"runFork",
|
|
@@ -3260,7 +3468,7 @@ const runners = [
|
|
|
3260
3468
|
"runSyncExitWith",
|
|
3261
3469
|
"runSyncWith"
|
|
3262
3470
|
];
|
|
3263
|
-
const
|
|
3471
|
+
const SET_MESSAGE = "Use HashSet, or MutableHashSet for local mutation; for identity keys, wrap objects the code owns in Equal.byReferenceUnsafe, which marks them reference-compared globally.";
|
|
3264
3472
|
const oxlint = defineConfig({
|
|
3265
3473
|
categories: {
|
|
3266
3474
|
correctness: "off",
|
|
@@ -3298,11 +3506,7 @@ const oxlint = defineConfig({
|
|
|
3298
3506
|
},
|
|
3299
3507
|
overrides: [
|
|
3300
3508
|
{
|
|
3301
|
-
files: [
|
|
3302
|
-
"**/*.config.ts",
|
|
3303
|
-
"**/app.plugin.js",
|
|
3304
|
-
"**/main.*"
|
|
3305
|
-
],
|
|
3509
|
+
files: ["**/*.config.ts", "**/app.plugin.js"],
|
|
3306
3510
|
rules: { "import/no-default-export": "off" }
|
|
3307
3511
|
},
|
|
3308
3512
|
{
|
|
@@ -3331,7 +3535,7 @@ const oxlint = defineConfig({
|
|
|
3331
3535
|
rules: { "react/rules-of-hooks": "off" }
|
|
3332
3536
|
},
|
|
3333
3537
|
{
|
|
3334
|
-
files: ["**/*.test.ts"
|
|
3538
|
+
files: ["**/*.test.ts"],
|
|
3335
3539
|
rules: { "@deslop/coding-standards/no-discarded-failure": "off" }
|
|
3336
3540
|
},
|
|
3337
3541
|
{
|
|
@@ -3356,8 +3560,8 @@ const oxlint = defineConfig({
|
|
|
3356
3560
|
],
|
|
3357
3561
|
rules: {
|
|
3358
3562
|
"@deslop/coding-standards/explicit-effect-concurrency": "error",
|
|
3563
|
+
"@deslop/coding-standards/module-constant": "error",
|
|
3359
3564
|
"@deslop/coding-standards/motion-limits": "error",
|
|
3360
|
-
"@deslop/coding-standards/no-array-wrap-ternary": "error",
|
|
3361
3565
|
"@deslop/coding-standards/no-barrel-module": "error",
|
|
3362
3566
|
"@deslop/coding-standards/no-compiler-opt-out": "error",
|
|
3363
3567
|
"@deslop/coding-standards/no-component-atom-allocation": "error",
|
|
@@ -3370,6 +3574,7 @@ const oxlint = defineConfig({
|
|
|
3370
3574
|
"@deslop/coding-standards/no-empty-recovery": "error",
|
|
3371
3575
|
"@deslop/coding-standards/no-error-message-assertion": "error",
|
|
3372
3576
|
"@deslop/coding-standards/no-fake-ref-state": "error",
|
|
3577
|
+
"@deslop/coding-standards/no-floating-derived-type": "error",
|
|
3373
3578
|
"@deslop/coding-standards/no-gradient": "error",
|
|
3374
3579
|
"@deslop/coding-standards/no-hand-written-guard": "error",
|
|
3375
3580
|
"@deslop/coding-standards/no-in-operator": "error",
|
|
@@ -3378,7 +3583,6 @@ const oxlint = defineConfig({
|
|
|
3378
3583
|
"@deslop/coding-standards/no-native-control": "error",
|
|
3379
3584
|
"@deslop/coding-standards/no-native-emptiness-check": "error",
|
|
3380
3585
|
"@deslop/coding-standards/no-native-method-call": "error",
|
|
3381
|
-
"@deslop/coding-standards/no-nullish-ternary": "error",
|
|
3382
3586
|
"@deslop/coding-standards/no-option-value-access": "error",
|
|
3383
3587
|
"@deslop/coding-standards/no-property-alias": "error",
|
|
3384
3588
|
"@deslop/coding-standards/no-readonly-type-syntax": "error",
|
|
@@ -3389,9 +3593,11 @@ const oxlint = defineConfig({
|
|
|
3389
3593
|
"@deslop/coding-standards/no-renamed-import": "error",
|
|
3390
3594
|
"@deslop/coding-standards/no-return-type": "error",
|
|
3391
3595
|
"@deslop/coding-standards/no-single-use-value": "error",
|
|
3596
|
+
"@deslop/coding-standards/no-stored-jsx": "error",
|
|
3392
3597
|
"@deslop/coding-standards/no-stored-schema-operation": "error",
|
|
3393
3598
|
"@deslop/coding-standards/no-switch-statement": "error",
|
|
3394
3599
|
"@deslop/coding-standards/no-trivial-indirection": "error",
|
|
3600
|
+
"@deslop/coding-standards/no-type-from-value": "error",
|
|
3395
3601
|
"@deslop/coding-standards/no-typeof": "error",
|
|
3396
3602
|
"@deslop/coding-standards/no-undestructured-use-state": "error",
|
|
3397
3603
|
"@deslop/coding-standards/no-unexplained-disable": "error",
|
|
@@ -3412,6 +3618,8 @@ const oxlint = defineConfig({
|
|
|
3412
3618
|
"effecttsgo/global-timers": "error",
|
|
3413
3619
|
"effecttsgo/global-timers-in-effect": "error",
|
|
3414
3620
|
"effecttsgo/node-builtin-import": "error",
|
|
3621
|
+
"effecttsgo/prefer-effect-array": "error",
|
|
3622
|
+
"effecttsgo/prefer-effect-record": "error",
|
|
3415
3623
|
"effecttsgo/prefer-schema-over-json": "error",
|
|
3416
3624
|
"effecttsgo/process-env": "error",
|
|
3417
3625
|
"effecttsgo/process-env-in-effect": "error",
|
|
@@ -3430,9 +3638,11 @@ const oxlint = defineConfig({
|
|
|
3430
3638
|
"effecttsgo/effect-in-void-success": "error",
|
|
3431
3639
|
"effecttsgo/effect-map-flatten": "error",
|
|
3432
3640
|
"effecttsgo/effect-map-void": "error",
|
|
3641
|
+
"effecttsgo/effect-sleep-zero-to-yield-now": "error",
|
|
3433
3642
|
"effecttsgo/effect-succeed-with-void": "error",
|
|
3434
3643
|
"effecttsgo/flat-map-ignored-param-to-and-then": "error",
|
|
3435
3644
|
"effecttsgo/flat-map-to-map": "error",
|
|
3645
|
+
"effecttsgo/flat-map-to-tap": "error",
|
|
3436
3646
|
"effecttsgo/floating-effect": "error",
|
|
3437
3647
|
"effecttsgo/floating-effect-in-vitest": "error",
|
|
3438
3648
|
"effecttsgo/layer-merge-all-with-dependencies": "error",
|
|
@@ -3470,11 +3680,10 @@ const oxlint = defineConfig({
|
|
|
3470
3680
|
"effecttsgo/catch-all-to-map-error": "error",
|
|
3471
3681
|
"effecttsgo/catch-chain-to-first-success-of": "error",
|
|
3472
3682
|
"effecttsgo/catch-conditional-refail-to-catch-if": "error",
|
|
3473
|
-
"effecttsgo/catch-die-to-or-die": "error",
|
|
3474
3683
|
"effecttsgo/catch-tag-to-catch-reason": "error",
|
|
3475
|
-
"effecttsgo/catch-to-ignore": "error",
|
|
3476
3684
|
"effecttsgo/catch-to-or-else-succeed": "error",
|
|
3477
3685
|
"effecttsgo/catch-unfailable-effect": "error",
|
|
3686
|
+
"effecttsgo/error-payload-shadows-name": "error",
|
|
3478
3687
|
"effecttsgo/flat-map-conditional-to-filter-or-fail": "error",
|
|
3479
3688
|
"effecttsgo/global-error-in-effect-catch": "error",
|
|
3480
3689
|
"effecttsgo/global-error-in-effect-failure": "error",
|
|
@@ -3483,10 +3692,8 @@ const oxlint = defineConfig({
|
|
|
3483
3692
|
"effecttsgo/redundant-map-error": "error",
|
|
3484
3693
|
"effecttsgo/redundant-or-die": "error",
|
|
3485
3694
|
"effecttsgo/timeout-catch-tag-to-timeout-or-else": "error",
|
|
3486
|
-
"effecttsgo/unknown-in-effect-catch": "error",
|
|
3487
3695
|
"effecttsgo/class-self-mismatch": "error",
|
|
3488
3696
|
"effecttsgo/deterministic-keys": "error",
|
|
3489
|
-
"effecttsgo/generic-effect-services": "error",
|
|
3490
3697
|
"effecttsgo/instance-of-schema": "error",
|
|
3491
3698
|
"effecttsgo/new-schema-class": "error",
|
|
3492
3699
|
"effecttsgo/obsolete-match-import": "error",
|
|
@@ -3499,6 +3706,7 @@ const oxlint = defineConfig({
|
|
|
3499
3706
|
"effecttsgo/schema-number": "error",
|
|
3500
3707
|
"effecttsgo/schema-struct-with-tag": "error",
|
|
3501
3708
|
"effecttsgo/schema-sync-in-effect": "error",
|
|
3709
|
+
"effecttsgo/schema-union-of-literals": "error",
|
|
3502
3710
|
"effecttsgo/service-not-as-class": "error",
|
|
3503
3711
|
"effecttsgo/unnecessary-typeof-type": "error",
|
|
3504
3712
|
"typescript/consistent-generic-constructors": "error",
|
|
@@ -3556,7 +3764,7 @@ const oxlint = defineConfig({
|
|
|
3556
3764
|
"typescript/unbound-method": "error",
|
|
3557
3765
|
"typescript/unified-signatures": "error",
|
|
3558
3766
|
"typescript/prefer-as-const": "error",
|
|
3559
|
-
"typescript/prefer-nullish-coalescing": "error",
|
|
3767
|
+
"typescript/prefer-nullish-coalescing": ["error", { ignoreTernaryTests: true }],
|
|
3560
3768
|
"typescript/prefer-optional-chain": "error",
|
|
3561
3769
|
"typescript/restrict-plus-operands": "error",
|
|
3562
3770
|
"typescript/restrict-template-expressions": "error",
|
|
@@ -3591,8 +3799,8 @@ const oxlint = defineConfig({
|
|
|
3591
3799
|
"no-restricted-imports": ["error", {
|
|
3592
3800
|
paths: [
|
|
3593
3801
|
{
|
|
3594
|
-
importNames:
|
|
3595
|
-
message:
|
|
3802
|
+
importNames: REACT_LEGACY_APIS,
|
|
3803
|
+
message: REACT_LEGACY_MESSAGE,
|
|
3596
3804
|
name: "react"
|
|
3597
3805
|
},
|
|
3598
3806
|
{
|
|
@@ -3621,6 +3829,10 @@ const oxlint = defineConfig({
|
|
|
3621
3829
|
}
|
|
3622
3830
|
],
|
|
3623
3831
|
patterns: [
|
|
3832
|
+
{
|
|
3833
|
+
message: "Prove a screen in the browser; no UI tests.",
|
|
3834
|
+
regex: "^(?:@testing-library/|jsdom$|happy-dom$)"
|
|
3835
|
+
},
|
|
3624
3836
|
{
|
|
3625
3837
|
message: "Use public package exports.",
|
|
3626
3838
|
regex: "^@[^/]+/[^/]+/(?:src|lib)(?:/|$)"
|
|
@@ -3655,7 +3867,6 @@ const oxlint = defineConfig({
|
|
|
3655
3867
|
}
|
|
3656
3868
|
]
|
|
3657
3869
|
}],
|
|
3658
|
-
"array-callback-return": "error",
|
|
3659
3870
|
"max-depth": ["error", 3],
|
|
3660
3871
|
"max-nested-callbacks": ["error", 6],
|
|
3661
3872
|
"no-cond-assign": "error",
|
|
@@ -3701,17 +3912,13 @@ const oxlint = defineConfig({
|
|
|
3701
3912
|
name: "Error"
|
|
3702
3913
|
},
|
|
3703
3914
|
{
|
|
3704
|
-
message:
|
|
3915
|
+
message: MAP_MESSAGE,
|
|
3705
3916
|
name: "Map"
|
|
3706
3917
|
},
|
|
3707
3918
|
{
|
|
3708
3919
|
message: "Import Number from 'effect'.",
|
|
3709
3920
|
name: "Number"
|
|
3710
3921
|
},
|
|
3711
|
-
{
|
|
3712
|
-
message: "Use Record or Struct from 'effect'.",
|
|
3713
|
-
name: "Object"
|
|
3714
|
-
},
|
|
3715
3922
|
{
|
|
3716
3923
|
message: "Use Number.parse.",
|
|
3717
3924
|
name: "parseFloat"
|
|
@@ -3729,7 +3936,7 @@ const oxlint = defineConfig({
|
|
|
3729
3936
|
name: "Reflect"
|
|
3730
3937
|
},
|
|
3731
3938
|
{
|
|
3732
|
-
message:
|
|
3939
|
+
message: SET_MESSAGE,
|
|
3733
3940
|
name: "Set"
|
|
3734
3941
|
},
|
|
3735
3942
|
{
|
|
@@ -3752,17 +3959,23 @@ const oxlint = defineConfig({
|
|
|
3752
3959
|
property: "pipe"
|
|
3753
3960
|
},
|
|
3754
3961
|
{
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3962
|
+
allowProperties: [
|
|
3963
|
+
"entries",
|
|
3964
|
+
"fromEntries",
|
|
3965
|
+
"hasOwn",
|
|
3966
|
+
"keys",
|
|
3967
|
+
"values"
|
|
3968
|
+
],
|
|
3969
|
+
message: "Use Record or Struct from 'effect'.",
|
|
3970
|
+
object: "Object"
|
|
3758
3971
|
},
|
|
3759
|
-
...map$1(
|
|
3760
|
-
message:
|
|
3972
|
+
...map$1(RUNNERS, (property) => ({
|
|
3973
|
+
message: RUN_MESSAGE,
|
|
3761
3974
|
object: "Effect",
|
|
3762
3975
|
property
|
|
3763
3976
|
})),
|
|
3764
3977
|
{
|
|
3765
|
-
message:
|
|
3978
|
+
message: RUN_MESSAGE,
|
|
3766
3979
|
object: "ManagedRuntime",
|
|
3767
3980
|
property: "make"
|
|
3768
3981
|
},
|
|
@@ -3786,12 +3999,12 @@ const oxlint = defineConfig({
|
|
|
3786
3999
|
object: "Math",
|
|
3787
4000
|
property: "round"
|
|
3788
4001
|
},
|
|
3789
|
-
...map$1(
|
|
3790
|
-
message:
|
|
4002
|
+
...map$1(REACT_LEGACY_APIS, (property) => ({
|
|
4003
|
+
message: REACT_LEGACY_MESSAGE,
|
|
3791
4004
|
object: "React",
|
|
3792
4005
|
property
|
|
3793
4006
|
})),
|
|
3794
|
-
...map$1(
|
|
4007
|
+
...map$1(REACT_HOOKS, (property) => ({
|
|
3795
4008
|
message: `Import ${property} by name from react.`,
|
|
3796
4009
|
object: "React",
|
|
3797
4010
|
property
|
|
@@ -3898,16 +4111,22 @@ const oxlint = defineConfig({
|
|
|
3898
4111
|
"no-ternary": "error",
|
|
3899
4112
|
"no-throw-literal": "error",
|
|
3900
4113
|
"no-unmodified-loop-condition": "error",
|
|
3901
|
-
"no-unneeded-ternary": "error",
|
|
3902
4114
|
"no-unreachable-loop": "error",
|
|
3903
4115
|
"no-unsafe-finally": "error",
|
|
3904
4116
|
"no-unsafe-optional-chaining": ["error", { disallowArithmeticOperators: true }],
|
|
3905
4117
|
"no-unused-expressions": "error",
|
|
3906
4118
|
"no-unused-vars": ["error", {
|
|
4119
|
+
args: "all",
|
|
3907
4120
|
argsIgnorePattern: "^_",
|
|
3908
4121
|
ignoreRestSiblings: true,
|
|
3909
4122
|
reportUsedIgnorePattern: true
|
|
3910
4123
|
}],
|
|
4124
|
+
"no-use-before-define": ["error", {
|
|
4125
|
+
classes: false,
|
|
4126
|
+
functions: false,
|
|
4127
|
+
typedefs: false,
|
|
4128
|
+
variables: true
|
|
4129
|
+
}],
|
|
3911
4130
|
"no-useless-assignment": "error",
|
|
3912
4131
|
"no-useless-backreference": "error",
|
|
3913
4132
|
"no-useless-call": "error",
|
|
@@ -3922,18 +4141,23 @@ const oxlint = defineConfig({
|
|
|
3922
4141
|
"no-void": "error",
|
|
3923
4142
|
"object-shorthand": "error",
|
|
3924
4143
|
"oxc/branches-sharing-code": "error",
|
|
3925
|
-
"oxc/no-accumulating-spread": "error",
|
|
3926
|
-
"oxc/no-map-spread": "error",
|
|
3927
4144
|
"oxc/only-used-in-recursion": "error",
|
|
3928
4145
|
"prefer-arrow-callback": "error",
|
|
3929
4146
|
"prefer-const": ["error", { destructuring: "all" }],
|
|
3930
4147
|
"prefer-template": "error",
|
|
3931
4148
|
"require-unicode-regexp": "error",
|
|
4149
|
+
"jsx-a11y/alt-text": "error",
|
|
4150
|
+
"jsx-a11y/aria-props": "error",
|
|
4151
|
+
"jsx-a11y/aria-proptypes": "error",
|
|
3932
4152
|
"jsx-a11y/autocomplete-valid": "error",
|
|
3933
4153
|
"jsx-a11y/click-events-have-key-events": "error",
|
|
3934
4154
|
"jsx-a11y/control-has-associated-label": "error",
|
|
4155
|
+
"jsx-a11y/heading-has-content": "error",
|
|
3935
4156
|
"jsx-a11y/interactive-supports-focus": "error",
|
|
3936
4157
|
"jsx-a11y/label-has-associated-control": "error",
|
|
4158
|
+
"jsx-a11y/no-aria-hidden-on-focusable": "error",
|
|
4159
|
+
"jsx-a11y/role-has-required-aria-props": "error",
|
|
4160
|
+
"jsx-a11y/tabindex-no-positive": "error",
|
|
3937
4161
|
"react-doctor/no-aria-invalid-without-description": "error",
|
|
3938
4162
|
"react-doctor/no-call-component-as-function": "error",
|
|
3939
4163
|
"react-doctor/no-controlled-input-value-without-state-update": "error",
|
|
@@ -4002,7 +4226,6 @@ const oxlint = defineConfig({
|
|
|
4002
4226
|
"react/jsx-no-script-url": "error",
|
|
4003
4227
|
"react/jsx-no-target-blank": "error",
|
|
4004
4228
|
"react/jsx-no-useless-fragment": "error",
|
|
4005
|
-
"react/memo-dependencies": "error",
|
|
4006
4229
|
"react/no-array-index-key": "error",
|
|
4007
4230
|
"react/no-children-prop": "error",
|
|
4008
4231
|
"react/no-clone-element": "error",
|
|
@@ -4010,8 +4233,6 @@ const oxlint = defineConfig({
|
|
|
4010
4233
|
"react/no-object-type-as-default-prop": "error",
|
|
4011
4234
|
"react/no-react-children": "error",
|
|
4012
4235
|
"react/no-unknown-property": "error",
|
|
4013
|
-
"react/no-unstable-nested-components": ["error", { allowAsProps: true }],
|
|
4014
|
-
"react/preserve-manual-memoization": "error",
|
|
4015
4236
|
"react/purity": "error",
|
|
4016
4237
|
"react/refs": "error",
|
|
4017
4238
|
"react/rule-suppression": "error",
|
|
@@ -4020,9 +4241,7 @@ const oxlint = defineConfig({
|
|
|
4020
4241
|
"react/set-state-in-effect": "error",
|
|
4021
4242
|
"react/set-state-in-render": "error",
|
|
4022
4243
|
"react/static-components": "error",
|
|
4023
|
-
"react/use-memo": "error",
|
|
4024
4244
|
"react/void-dom-elements-no-children": "error",
|
|
4025
|
-
"react/void-use-memo": "error",
|
|
4026
4245
|
"shadcn/no-arbitrary-values": "error",
|
|
4027
4246
|
"shadcn/no-inline-styles": "error",
|
|
4028
4247
|
"shadcn/no-raw-colors": "error",
|
|
@@ -4044,12 +4263,12 @@ const oxlint = defineConfig({
|
|
|
4044
4263
|
Date: "Use DateTime.",
|
|
4045
4264
|
Error: "Use Schema.TaggedError.",
|
|
4046
4265
|
Iterable: "Use T[].",
|
|
4047
|
-
Map:
|
|
4266
|
+
Map: MAP_MESSAGE,
|
|
4048
4267
|
Promise: "Use Effect.",
|
|
4049
4268
|
ReadonlyArray: "Use T[].",
|
|
4050
|
-
ReadonlyMap:
|
|
4051
|
-
ReadonlySet:
|
|
4052
|
-
Set:
|
|
4269
|
+
ReadonlyMap: MAP_MESSAGE,
|
|
4270
|
+
ReadonlySet: SET_MESSAGE,
|
|
4271
|
+
Set: SET_MESSAGE,
|
|
4053
4272
|
undefined: "Use an optional property or parameter, or let inference supply undefined."
|
|
4054
4273
|
} }],
|
|
4055
4274
|
"typescript/no-unnecessary-type-assertion": "error",
|
|
@@ -4063,10 +4282,9 @@ const oxlint = defineConfig({
|
|
|
4063
4282
|
"unicorn/no-process-exit": "error",
|
|
4064
4283
|
"unicorn/no-static-only-class": "error",
|
|
4065
4284
|
"unicorn/no-useless-fallback-in-spread": "error",
|
|
4066
|
-
"unicorn/no-useless-length-check": "error",
|
|
4067
4285
|
"unicorn/no-useless-spread": "error",
|
|
4068
|
-
"unicorn/prefer-logical-operator-over-ternary": "error",
|
|
4069
4286
|
"unicorn/prefer-optional-catch-binding": "error",
|
|
4287
|
+
"unicorn/prefer-string-replace-all": "error",
|
|
4070
4288
|
"use-isnan": "error"
|
|
4071
4289
|
},
|
|
4072
4290
|
settings: {
|
|
@@ -4084,8 +4302,8 @@ var oxlint_default = definePlugin({
|
|
|
4084
4302
|
meta: { name: "@deslop/coding-standards" },
|
|
4085
4303
|
rules: {
|
|
4086
4304
|
"explicit-effect-concurrency": explicitEffectConcurrency,
|
|
4305
|
+
"module-constant": moduleConstant,
|
|
4087
4306
|
"motion-limits": motionLimits,
|
|
4088
|
-
"no-array-wrap-ternary": noArrayWrapTernary,
|
|
4089
4307
|
"no-barrel-module": noBarrelModule,
|
|
4090
4308
|
"no-compiler-opt-out": noCompilerOptOut,
|
|
4091
4309
|
"no-component-atom-allocation": noComponentAtomAllocation,
|
|
@@ -4098,6 +4316,7 @@ var oxlint_default = definePlugin({
|
|
|
4098
4316
|
"no-empty-recovery": noEmptyRecovery,
|
|
4099
4317
|
"no-error-message-assertion": noErrorMessageAssertion,
|
|
4100
4318
|
"no-fake-ref-state": noFakeRefState,
|
|
4319
|
+
"no-floating-derived-type": noFloatingDerivedType,
|
|
4101
4320
|
"no-gradient": noGradient,
|
|
4102
4321
|
"no-hand-written-guard": noHandWrittenGuard,
|
|
4103
4322
|
"no-in-operator": noInOperator,
|
|
@@ -4106,7 +4325,6 @@ var oxlint_default = definePlugin({
|
|
|
4106
4325
|
"no-native-control": noNativeControl,
|
|
4107
4326
|
"no-native-emptiness-check": noNativeEmptinessCheck,
|
|
4108
4327
|
"no-native-method-call": noNativeMethodCall,
|
|
4109
|
-
"no-nullish-ternary": noNullishTernary,
|
|
4110
4328
|
"no-option-value-access": noOptionValueAccess,
|
|
4111
4329
|
"no-property-alias": noPropertyAlias,
|
|
4112
4330
|
"no-readonly-type-syntax": noReadonlyTypeSyntax,
|
|
@@ -4117,9 +4335,11 @@ var oxlint_default = definePlugin({
|
|
|
4117
4335
|
"no-renamed-import": noRenamedImport,
|
|
4118
4336
|
"no-return-type": noReturnType,
|
|
4119
4337
|
"no-single-use-value": noSingleUseValue,
|
|
4338
|
+
"no-stored-jsx": noStoredJsx,
|
|
4120
4339
|
"no-stored-schema-operation": noStoredSchemaOperation,
|
|
4121
4340
|
"no-switch-statement": noSwitchStatement,
|
|
4122
4341
|
"no-trivial-indirection": noTrivialIndirection,
|
|
4342
|
+
"no-type-from-value": noTypeFromValue,
|
|
4123
4343
|
"no-typeof": noTypeof,
|
|
4124
4344
|
"no-undestructured-use-state": noUndestructuredUseState,
|
|
4125
4345
|
"no-unexplained-disable": noUnexplainedDisable,
|