@deslop/coding-standards 0.1.1076 → 0.1.1079
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{MutableRef-KfyWrSvW.mjs → MutableList-BCF17jPH.mjs} +536 -65
- package/dist/install.js +1611 -1752
- package/dist/oxlint.d.ts +12 -18
- package/dist/oxlint.js +631 -226
- package/package.json +1 -1
- package/skills/architecture/SKILL.md +1 -1
- package/skills/design/SKILL.md +4 -4
- package/skills/engineering/SKILL.md +39 -10
- package/skills/testing/SKILL.md +1 -1
package/dist/oxlint.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $ as
|
|
2
|
-
//#region ../../node_modules/.pnpm/effect@4.0.
|
|
1
|
+
import { $ as prepend, $t as strictEqual, A as filter$1, Ar as isString, At as getOrUndefined, B as getSomes, Bt as zipWith, C as contains$1, Cr as isNullish$1, Ct as filter, D as empty, Dt as fromNullishOr, E as dedupeWith, Et as flatten$1, F as flatMap$1, Ft as match, Gn as NodeInspectSymbol, H as isArrayEmpty, I as flatten, It as none, K as join, Kr as pipeArguments, L as forEach, Lr as constVoid, Lt as orElse, M as findFirst, Mt as isSome, N as findFirstIndex, O as ensure, Or as isReadonlyObject, Ot as fromUndefinedOr, Pr as constFalse, Pt as map, Q as of, Rt as some, S as appendAll, St as exists, T as dedupe, Tt as flatMap, U as isArrayNonEmpty, Ur as pipe, V as head, Y as map$1, Z as matchRight, at as get$2, bt as all, c as make$3, cr as number, ct as remove, d as includes, f as match$1, ft as fromOption, g as trim, gr as isBoolean, h as startsWith, ir as symbol, it as fromEntries, j as filterMap, k as every, kr as isRegExp, kt as getOrElse, l as update, lr as optimize, lt as values, m as split, mr as hasProperty, nr as byReferenceUnsafe, nt as sort, o as toArray, or as combine, p as replaceAll, pr as symbol$1, q as last, r as make$2, rr as equals, rt as take, s as get$3, sr as hash, t as append$1, tt as some$1, u as Order, ur as string, w as containsWith, wr as isNumber, wt as firstSomeOf, x as append, xr as isNotNullish, xt as contains, y as TaggedError, z as get$1, zr as dual, zt as toArray$1 } from "./MutableList-BCF17jPH.mjs";
|
|
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";
|
|
5
5
|
/** @internal */
|
|
@@ -94,7 +94,7 @@ const finalize = (source) => {
|
|
|
94
94
|
return graph;
|
|
95
95
|
};
|
|
96
96
|
//#endregion
|
|
97
|
-
//#region ../../node_modules/.pnpm/effect@4.0.
|
|
97
|
+
//#region ../../node_modules/.pnpm/effect@4.0.5/node_modules/effect/dist/internal/graphCsr.js
|
|
98
98
|
const cache = /*#__PURE__*/ new WeakMap();
|
|
99
99
|
const makeAdjacency = (csr, incoming) => {
|
|
100
100
|
const impl = toImpl(csr.graph);
|
|
@@ -168,7 +168,7 @@ const get = (graph) => {
|
|
|
168
168
|
return result;
|
|
169
169
|
};
|
|
170
170
|
//#endregion
|
|
171
|
-
//#region ../../node_modules/.pnpm/effect@4.0.
|
|
171
|
+
//#region ../../node_modules/.pnpm/effect@4.0.5/node_modules/effect/dist/Graph.js
|
|
172
172
|
/**
|
|
173
173
|
* Models relationships between indexed nodes and edges.
|
|
174
174
|
*
|
|
@@ -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.2.0_@opentelemetry+api@1.9.1_@types+
|
|
624
|
+
//#region ../../node_modules/.pnpm/oxlint@1.88.0_oxlint-tsgolint@7.0.2004_vite-plus@1.2.0_@opentelemetry+api@1.9.1_@types+_d0d0d8af4627f12721655e70fc027515/node_modules/oxlint/dist/index.js
|
|
625
625
|
/**
|
|
626
626
|
* Define an Oxlint configuration with type inference.
|
|
627
627
|
*
|
|
@@ -650,20 +650,35 @@ function variableFor(context, node) {
|
|
|
650
650
|
function isImportBinding(input) {
|
|
651
651
|
return pipe(variableFor(input.context, input.node), exists((variable) => some$1(variable.defs, (definition) => {
|
|
652
652
|
if (definition.type !== "ImportBinding" || definition.parent?.type !== "ImportDeclaration") return false;
|
|
653
|
-
|
|
654
|
-
if (
|
|
653
|
+
if (isString(input.source) && definition.parent.source.value !== input.source) return false;
|
|
654
|
+
if (isRegExp(input.source) && !input.source.test(definition.parent.source.value)) return false;
|
|
655
655
|
return input.importedName === void 0 || definition.node.type === "ImportSpecifier" && definition.node.imported.type === "Identifier" && definition.node.imported.name === input.importedName;
|
|
656
656
|
})));
|
|
657
657
|
}
|
|
658
658
|
function isNamespaceImport(input) {
|
|
659
659
|
return pipe(variableFor(input.context, input.node), exists((variable) => some$1(variable.defs, (definition) => definition.type === "ImportBinding" && definition.node.type === "ImportNamespaceSpecifier" && definition.parent?.type === "ImportDeclaration" && definition.parent.source.value === input.source)));
|
|
660
660
|
}
|
|
661
|
+
function typeWrapped(node) {
|
|
662
|
+
if (node.type === "TSAsExpression" || node.type === "TSInstantiationExpression" || node.type === "TSNonNullExpression" || node.type === "TSSatisfiesExpression" || node.type === "TSTypeAssertion") return some(node.expression);
|
|
663
|
+
return none();
|
|
664
|
+
}
|
|
665
|
+
function outermost(node) {
|
|
666
|
+
return pipe(fromNullishOr(node.parent), filter((parent) => isSome(typeWrapped(parent))), match({
|
|
667
|
+
onNone: () => node,
|
|
668
|
+
onSome: outermost
|
|
669
|
+
}));
|
|
670
|
+
}
|
|
661
671
|
function returnedExpression(node) {
|
|
662
672
|
if (node.body === null) return;
|
|
663
673
|
if (node.body.type !== "BlockStatement") return node.body;
|
|
664
674
|
if (node.body.body.length !== 1 || node.body.body[0]?.type !== "ReturnStatement") return;
|
|
665
675
|
return node.body.body[0].argument;
|
|
666
676
|
}
|
|
677
|
+
function rootName(node) {
|
|
678
|
+
if (node.type === "Identifier") return some(node.name);
|
|
679
|
+
if (node.type === "MemberExpression" && !node.computed && !node.optional) return rootName(node.object);
|
|
680
|
+
return none();
|
|
681
|
+
}
|
|
667
682
|
function memberName(node) {
|
|
668
683
|
if (!node.computed && node.property.type === "Identifier") return some(node.property.name);
|
|
669
684
|
if (node.computed && node.property.type === "Literal" && isString(node.property.value)) return some(node.property.value);
|
|
@@ -678,11 +693,9 @@ function importedMember(input) {
|
|
|
678
693
|
}) && (input.propertyName === void 0 || contains(memberName(input.node), input.propertyName));
|
|
679
694
|
}
|
|
680
695
|
function matches(regExp) {
|
|
681
|
-
return (text) => isSome(pipe(text, match(regExp)));
|
|
682
|
-
}
|
|
683
|
-
function isSchemaOperationName(name) {
|
|
684
|
-
return matches(/^(?:(?:decode|encode)(?:Unknown)?(?:Effect|Exit|Option|Promise|Result|Sync)|asserts|is)$/u)(name);
|
|
696
|
+
return (text) => isSome(pipe(text, match$1(regExp)));
|
|
685
697
|
}
|
|
698
|
+
const isSchemaOperationName = matches(/^(?:(?:decode|encode)(?:Unknown)?(?:Effect|Exit|Option|Promise|Result|Sync)|asserts|is)$/u);
|
|
686
699
|
function isSchemaOperationCall(input) {
|
|
687
700
|
if (input.node.callee.type !== "MemberExpression" || !importedMember({
|
|
688
701
|
context: input.context,
|
|
@@ -705,11 +718,13 @@ function isReactUseState(input) {
|
|
|
705
718
|
});
|
|
706
719
|
}
|
|
707
720
|
function statementDeclaration(statement) {
|
|
708
|
-
|
|
721
|
+
if (statement.type === "ExportNamedDeclaration") return statement.declaration;
|
|
722
|
+
return statement;
|
|
709
723
|
}
|
|
710
724
|
function typeAlias(statement) {
|
|
711
725
|
const declaration = statementDeclaration(statement);
|
|
712
|
-
|
|
726
|
+
if (declaration?.type !== "TSTypeAliasDeclaration") return none();
|
|
727
|
+
return some(declaration);
|
|
713
728
|
}
|
|
714
729
|
function isInferredType(input) {
|
|
715
730
|
return input.node.type === "TSTypeQuery" && input.node.exprName.type === "TSQualifiedName" && input.node.exprName.left.type === "Identifier" && input.node.exprName.left.name === input.name && input.node.exprName.right.name === "Type";
|
|
@@ -738,7 +753,10 @@ function isSchemaCodecType(input) {
|
|
|
738
753
|
}
|
|
739
754
|
function typeArgumentName(node) {
|
|
740
755
|
if (node.type !== "TSTypeReference" || node.typeArguments === null) return none();
|
|
741
|
-
return pipe(get$1(node.typeArguments.params, 0), flatMap((parameter) =>
|
|
756
|
+
return pipe(get$1(node.typeArguments.params, 0), flatMap((parameter) => {
|
|
757
|
+
if (parameter.type !== "TSTypeReference" || parameter.typeName.type !== "Identifier") return none();
|
|
758
|
+
return some(parameter.typeName.name);
|
|
759
|
+
}));
|
|
742
760
|
}
|
|
743
761
|
function suspendedTypeName(input) {
|
|
744
762
|
if (input.node.callee.type === "Super" || !importedMember({
|
|
@@ -747,7 +765,10 @@ function suspendedTypeName(input) {
|
|
|
747
765
|
node: input.node.callee,
|
|
748
766
|
propertyName: "suspend"
|
|
749
767
|
})) return none();
|
|
750
|
-
return pipe(get$1(input.node.arguments, 0), flatMap((argument) =>
|
|
768
|
+
return pipe(get$1(input.node.arguments, 0), flatMap((argument) => {
|
|
769
|
+
if (argument.type !== "ArrowFunctionExpression" && argument.type !== "FunctionExpression") return none();
|
|
770
|
+
return fromNullishOr(argument.returnType);
|
|
771
|
+
}), filter((returnType) => isSchemaCodecType({
|
|
751
772
|
context: input.context,
|
|
752
773
|
node: returnType.typeAnnotation
|
|
753
774
|
})), flatMap((returnType) => typeArgumentName(returnType.typeAnnotation)));
|
|
@@ -755,7 +776,8 @@ function suspendedTypeName(input) {
|
|
|
755
776
|
function referencedTypeNames(node) {
|
|
756
777
|
if (node.type === "TSTypeReference") {
|
|
757
778
|
const argumentNames = pipe(fromNullishOr(node.typeArguments), map((typeArguments) => pipe(typeArguments.params, flatMap$1(referencedTypeNames))), getOrElse(() => empty()));
|
|
758
|
-
|
|
779
|
+
if (node.typeName.type !== "Identifier") return argumentNames;
|
|
780
|
+
return prepend(argumentNames, node.typeName.name);
|
|
759
781
|
}
|
|
760
782
|
if (node.type === "TSArrayType") return referencedTypeNames(node.elementType);
|
|
761
783
|
if (node.type === "TSUnionType" || node.type === "TSIntersectionType") return pipe(node.types, flatMap$1(referencedTypeNames));
|
|
@@ -779,9 +801,13 @@ function referencedTypeNames(node) {
|
|
|
779
801
|
if (node.type === "TSTypeLiteral") return pipe(node.members, flatMap$1(memberTypeNames));
|
|
780
802
|
return empty();
|
|
781
803
|
}
|
|
804
|
+
function annotationTypeNames(annotation) {
|
|
805
|
+
return pipe(fromNullishOr(annotation), map((present) => referencedTypeNames(present.typeAnnotation)), getOrElse(() => empty()));
|
|
806
|
+
}
|
|
782
807
|
function memberTypeNames(member) {
|
|
783
808
|
if (member.type === "TSIndexSignature") return referencedTypeNames(member.typeAnnotation.typeAnnotation);
|
|
784
|
-
|
|
809
|
+
if (member.type === "TSPropertySignature") return annotationTypeNames(member.typeAnnotation);
|
|
810
|
+
return annotationTypeNames(member.returnType);
|
|
785
811
|
}
|
|
786
812
|
function isNode(value) {
|
|
787
813
|
return hasProperty(value, "type") && isString(value.type);
|
|
@@ -799,24 +825,28 @@ function suspendSeeds(input) {
|
|
|
799
825
|
return pipe(suspendedTypeName({
|
|
800
826
|
context: input.context,
|
|
801
827
|
node: input.node
|
|
802
|
-
}), match
|
|
828
|
+
}), match({
|
|
803
829
|
onNone: () => seeds,
|
|
804
830
|
onSome: (name) => prepend(seeds, name)
|
|
805
831
|
}));
|
|
806
832
|
}
|
|
807
833
|
function referenceNames(node) {
|
|
808
834
|
const nested = pipe(childNodes(node), filter$1((child) => !(node.type === "Property" && !node.computed && child === node.key) && !(node.type === "MemberExpression" && !node.computed && child === node.property)), flatMap$1(referenceNames));
|
|
809
|
-
|
|
835
|
+
if (node.type !== "Identifier") return nested;
|
|
836
|
+
return prepend(nested, node.name);
|
|
810
837
|
}
|
|
811
838
|
function schemaEdges(statement) {
|
|
812
839
|
const declaration = statementDeclaration(statement);
|
|
813
|
-
if (declaration?.type === "TSTypeAliasDeclaration")
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
840
|
+
if (declaration?.type === "TSTypeAliasDeclaration") {
|
|
841
|
+
if (isInferredType({
|
|
842
|
+
name: declaration.id.name,
|
|
843
|
+
node: declaration.typeAnnotation
|
|
844
|
+
})) return empty();
|
|
845
|
+
return [{
|
|
846
|
+
name: declaration.id.name,
|
|
847
|
+
targets: referencedTypeNames(declaration.typeAnnotation)
|
|
848
|
+
}];
|
|
849
|
+
}
|
|
820
850
|
if (declaration?.type !== "VariableDeclaration" || declaration.kind !== "const") return empty();
|
|
821
851
|
return flatMap$1(declaration.declarations, (declarator) => {
|
|
822
852
|
if (declarator.id.type !== "Identifier" || declarator.init === null) return empty();
|
|
@@ -873,15 +903,27 @@ function isSingletonArray(input) {
|
|
|
873
903
|
if (isNullish$1(element) || element.type === "SpreadElement") return false;
|
|
874
904
|
return input.context.sourceCode.getText(element) === input.context.sourceCode.getText(input.value);
|
|
875
905
|
}
|
|
876
|
-
function
|
|
877
|
-
|
|
878
|
-
const negated = test !== input.node.test;
|
|
879
|
-
return exists(testedValue(test), (value) => input.context.sourceCode.getText(negated ? input.node.alternate : input.node.consequent) === input.context.sourceCode.getText(value) && isSingletonArray({
|
|
906
|
+
function wrapsTestedValue(input) {
|
|
907
|
+
return exists(testedValue(input.test), (value) => input.context.sourceCode.getText(input.kept) === input.context.sourceCode.getText(value) && isSingletonArray({
|
|
880
908
|
context: input.context,
|
|
881
|
-
node:
|
|
909
|
+
node: input.wrapped,
|
|
882
910
|
value
|
|
883
911
|
}));
|
|
884
912
|
}
|
|
913
|
+
function restatesArrayEnsure(input) {
|
|
914
|
+
if (input.node.test.type === "UnaryExpression" && input.node.test.operator === "!") return wrapsTestedValue({
|
|
915
|
+
context: input.context,
|
|
916
|
+
kept: input.node.alternate,
|
|
917
|
+
test: input.node.test.argument,
|
|
918
|
+
wrapped: input.node.consequent
|
|
919
|
+
});
|
|
920
|
+
return wrapsTestedValue({
|
|
921
|
+
context: input.context,
|
|
922
|
+
kept: input.node.consequent,
|
|
923
|
+
test: input.node.test,
|
|
924
|
+
wrapped: input.node.alternate
|
|
925
|
+
});
|
|
926
|
+
}
|
|
885
927
|
const noArrayWrapTernary = defineRule({
|
|
886
928
|
create: (context) => ({ ConditionalExpression: (node) => {
|
|
887
929
|
if (restatesArrayEnsure({
|
|
@@ -949,7 +991,9 @@ function enclosingPipes(context, node) {
|
|
|
949
991
|
"FunctionDeclaration",
|
|
950
992
|
"FunctionExpression"
|
|
951
993
|
], node.type)) return 0;
|
|
952
|
-
|
|
994
|
+
const depth = enclosingPipes(context, node.parent);
|
|
995
|
+
if (isPipeCall(context, node.parent) && some$1(node.parent.arguments, (argument) => argument === node)) return depth + 1;
|
|
996
|
+
return depth;
|
|
953
997
|
}
|
|
954
998
|
const noDeepPipe = defineRule({
|
|
955
999
|
create: (context) => ({ CallExpression: (node) => {
|
|
@@ -980,22 +1024,28 @@ function namedTuple(input) {
|
|
|
980
1024
|
seen: append(input.seen, input.node)
|
|
981
1025
|
}))));
|
|
982
1026
|
}
|
|
1027
|
+
function componentName(node) {
|
|
1028
|
+
if (node.type !== "ArrowFunctionExpression" && node.id !== null) return some(node.id.name);
|
|
1029
|
+
return bindingName(node);
|
|
1030
|
+
}
|
|
983
1031
|
function component(node) {
|
|
984
|
-
|
|
985
|
-
if (name === void 0 || !matches(/^[A-Z]/u)(name.name) || node.params.length !== 1 || node.body === null) return false;
|
|
1032
|
+
if (!exists(componentName(node), matches(/^[A-Z]/u)) || node.params.length !== 1 || node.body === null) return false;
|
|
986
1033
|
if (node.body.type === "JSXElement" || node.body.type === "JSXFragment") return true;
|
|
987
1034
|
return node.body.type === "BlockStatement" && some$1(node.body.body, (statement) => statement.type === "ReturnStatement" && (statement.argument?.type === "JSXElement" || statement.argument?.type === "JSXFragment"));
|
|
988
1035
|
}
|
|
989
1036
|
function isPattern(input) {
|
|
990
|
-
|
|
991
|
-
|
|
1037
|
+
if (input.node.type === "AssignmentPattern") return isPattern({
|
|
1038
|
+
...input,
|
|
1039
|
+
node: input.node.left
|
|
1040
|
+
});
|
|
1041
|
+
if (input.node.type === "ArrayPattern") return !exists(fromNullishOr(input.node.typeAnnotation), (annotation) => namedTuple({
|
|
992
1042
|
context: input.context,
|
|
993
1043
|
node: annotation.typeAnnotation,
|
|
994
1044
|
seen: []
|
|
995
1045
|
}));
|
|
996
|
-
if (
|
|
997
|
-
const named = filter$1(
|
|
998
|
-
return !(input.component && named.length === 1 && named[0]?.key.type === "Identifier" && named[0].key.name === "ref" && some$1(
|
|
1046
|
+
if (input.node.type !== "ObjectPattern") return false;
|
|
1047
|
+
const named = filter$1(input.node.properties, (property) => property.type === "Property");
|
|
1048
|
+
return !(input.component && named.length === 1 && named[0]?.key.type === "Identifier" && named[0].key.name === "ref" && some$1(input.node.properties, (property) => property.type === "RestElement"));
|
|
999
1049
|
}
|
|
1000
1050
|
const noDestructuredParameter = defineRule({
|
|
1001
1051
|
create: (context) => {
|
|
@@ -1054,8 +1104,19 @@ const noDiscardedFailure = defineRule({
|
|
|
1054
1104
|
//#region src/rules/no-double-nullish-check.ts
|
|
1055
1105
|
function nullishOperand(node) {
|
|
1056
1106
|
if (node.type === "Literal" && node.value === null) return some("null");
|
|
1057
|
-
|
|
1107
|
+
if (node.type === "Identifier" && node.name === "undefined") return some("undefined");
|
|
1108
|
+
return none();
|
|
1058
1109
|
}
|
|
1110
|
+
const variants = {
|
|
1111
|
+
"&&": {
|
|
1112
|
+
comparison: "!==",
|
|
1113
|
+
message: "Use Predicate.isNotNullish."
|
|
1114
|
+
},
|
|
1115
|
+
"||": {
|
|
1116
|
+
comparison: "===",
|
|
1117
|
+
message: "Use Predicate.isNullish."
|
|
1118
|
+
}
|
|
1119
|
+
};
|
|
1059
1120
|
function operands(input) {
|
|
1060
1121
|
if (input.node.type !== "LogicalExpression" || input.node.operator !== input.operator) return [input.node];
|
|
1061
1122
|
return appendAll(operands({
|
|
@@ -1078,17 +1139,17 @@ const noDoubleNullishCheck = defineRule({
|
|
|
1078
1139
|
create: (context) => ({ LogicalExpression: (node) => {
|
|
1079
1140
|
if (node.operator === "??") return;
|
|
1080
1141
|
if (node.parent.type === "LogicalExpression" && node.parent.operator === node.operator) return;
|
|
1081
|
-
const
|
|
1142
|
+
const variant = variants[node.operator];
|
|
1082
1143
|
const checks = pipe(operands({
|
|
1083
1144
|
node,
|
|
1084
1145
|
operator: node.operator
|
|
1085
1146
|
}), filterMap((operand) => fromOption(comparedNullish({
|
|
1086
1147
|
context,
|
|
1087
1148
|
node: operand,
|
|
1088
|
-
operator: comparison
|
|
1149
|
+
operator: variant.comparison
|
|
1089
1150
|
}), () => void 0)));
|
|
1090
1151
|
if (some$1(checks, (check) => some$1(checks, (other) => other.subject === check.subject && other.nullish !== check.nullish))) context.report({
|
|
1091
|
-
message:
|
|
1152
|
+
message: variant.message,
|
|
1092
1153
|
node
|
|
1093
1154
|
});
|
|
1094
1155
|
} }),
|
|
@@ -1099,11 +1160,10 @@ const noDoubleNullishCheck = defineRule({
|
|
|
1099
1160
|
const noEffectPropertyArrow = defineRule({
|
|
1100
1161
|
create: (context) => ({ Property: (node) => {
|
|
1101
1162
|
if (node.value.type !== "ArrowFunctionExpression" || isArrayEmpty(node.value.params) || node.value.body.type !== "CallExpression") return;
|
|
1102
|
-
|
|
1103
|
-
if (callee.type === "Identifier" && isImportBinding({
|
|
1163
|
+
if (node.value.body.callee.type === "Identifier" && isImportBinding({
|
|
1104
1164
|
context,
|
|
1105
1165
|
importedName: "pipe",
|
|
1106
|
-
node: callee,
|
|
1166
|
+
node: node.value.body.callee,
|
|
1107
1167
|
source: "effect"
|
|
1108
1168
|
}) && some$1(node.value.body.arguments, (argument) => argument.type !== "SpreadElement" && importedMember({
|
|
1109
1169
|
context,
|
|
@@ -1113,10 +1173,10 @@ const noEffectPropertyArrow = defineRule({
|
|
|
1113
1173
|
context,
|
|
1114
1174
|
importedName: "Effect",
|
|
1115
1175
|
node: argument.callee
|
|
1116
|
-
})) || callee.type === "MemberExpression" && importedMember({
|
|
1176
|
+
})) || node.value.body.callee.type === "MemberExpression" && importedMember({
|
|
1117
1177
|
context,
|
|
1118
1178
|
importedName: "Effect",
|
|
1119
|
-
node: callee,
|
|
1179
|
+
node: node.value.body.callee,
|
|
1120
1180
|
propertyName: "tryPromise"
|
|
1121
1181
|
})) context.report({
|
|
1122
1182
|
message: "Define this property with Effect.fn or Effect.fnUntraced instead of an arrow.",
|
|
@@ -1129,14 +1189,18 @@ const noEffectPropertyArrow = defineRule({
|
|
|
1129
1189
|
//#region src/rules/no-error-message-assertion.ts
|
|
1130
1190
|
function receiverName(node) {
|
|
1131
1191
|
if (node.type === "Identifier") return some(node.name);
|
|
1132
|
-
|
|
1192
|
+
if (node.type !== "MemberExpression") return none();
|
|
1193
|
+
return memberName(node);
|
|
1133
1194
|
}
|
|
1134
1195
|
function isErrorMessage(node) {
|
|
1135
1196
|
return node?.type === "MemberExpression" && contains(memberName(node), "message") && exists(receiverName(node.object), matches(/^(?:e|err|error|failure|cause|exception)$|Error$/iu));
|
|
1136
1197
|
}
|
|
1198
|
+
function expectsErrorMessage(node) {
|
|
1199
|
+
return node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "expect" && isErrorMessage(node.arguments[0]);
|
|
1200
|
+
}
|
|
1137
1201
|
function expectsMessage(node) {
|
|
1138
|
-
|
|
1139
|
-
return
|
|
1202
|
+
if (node.object.type === "MemberExpression" && contains(memberName(node.object), "not")) return expectsErrorMessage(node.object.object);
|
|
1203
|
+
return expectsErrorMessage(node.object);
|
|
1140
1204
|
}
|
|
1141
1205
|
function assertsMessage(node) {
|
|
1142
1206
|
const [actual, expected] = node.arguments;
|
|
@@ -1166,9 +1230,8 @@ function propertyName(node) {
|
|
|
1166
1230
|
return none();
|
|
1167
1231
|
}
|
|
1168
1232
|
function hasUnusedSetter(input) {
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
const setter = declaration.id.elements[1];
|
|
1233
|
+
if (input.node.parent.type !== "VariableDeclarator" || input.node.parent.id.type !== "ArrayPattern") return false;
|
|
1234
|
+
const setter = input.node.parent.id.elements[1];
|
|
1172
1235
|
if (isNullish$1(setter)) return true;
|
|
1173
1236
|
if (setter.type !== "Identifier") return false;
|
|
1174
1237
|
return pipe(variableFromScope({
|
|
@@ -1226,10 +1289,13 @@ function check(input) {
|
|
|
1226
1289
|
node: argument
|
|
1227
1290
|
}))) return some(["!Array.isArray"]);
|
|
1228
1291
|
if (input.node.type !== "BinaryExpression") return none();
|
|
1229
|
-
if (input.node.operator === "instanceof")
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1292
|
+
if (input.node.operator === "instanceof") {
|
|
1293
|
+
if (!isSubject({
|
|
1294
|
+
name: input.name,
|
|
1295
|
+
node: input.node.left
|
|
1296
|
+
}) || input.node.right.type !== "Identifier") return none();
|
|
1297
|
+
return some([`instanceof ${input.node.right.name}`]);
|
|
1298
|
+
}
|
|
1233
1299
|
return pipe(firstSomeOf([comparison({
|
|
1234
1300
|
name: input.name,
|
|
1235
1301
|
operator: input.node.operator,
|
|
@@ -1242,7 +1308,7 @@ function check(input) {
|
|
|
1242
1308
|
subject: input.node.right
|
|
1243
1309
|
})]), map(of));
|
|
1244
1310
|
}
|
|
1245
|
-
function report$
|
|
1311
|
+
function report$2(input) {
|
|
1246
1312
|
const predicate = input.node.returnType?.typeAnnotation;
|
|
1247
1313
|
const returned = returnedExpression(input.node);
|
|
1248
1314
|
if (predicate?.type !== "TSTypePredicate" || predicate.asserts || predicate.parameterName.type !== "Identifier" || isNullish$1(returned)) return;
|
|
@@ -1282,19 +1348,19 @@ function report$1(input) {
|
|
|
1282
1348
|
const noHandWrittenGuard = defineRule({
|
|
1283
1349
|
create: (context) => ({
|
|
1284
1350
|
ArrowFunctionExpression: (node) => {
|
|
1285
|
-
report$
|
|
1351
|
+
report$2({
|
|
1286
1352
|
context,
|
|
1287
1353
|
node
|
|
1288
1354
|
});
|
|
1289
1355
|
},
|
|
1290
1356
|
FunctionDeclaration: (node) => {
|
|
1291
|
-
report$
|
|
1357
|
+
report$2({
|
|
1292
1358
|
context,
|
|
1293
1359
|
node
|
|
1294
1360
|
});
|
|
1295
1361
|
},
|
|
1296
1362
|
FunctionExpression: (node) => {
|
|
1297
|
-
report$
|
|
1363
|
+
report$2({
|
|
1298
1364
|
context,
|
|
1299
1365
|
node
|
|
1300
1366
|
});
|
|
@@ -1303,6 +1369,18 @@ const noHandWrittenGuard = defineRule({
|
|
|
1303
1369
|
meta: { type: "problem" }
|
|
1304
1370
|
});
|
|
1305
1371
|
//#endregion
|
|
1372
|
+
//#region src/rules/no-in-operator.ts
|
|
1373
|
+
const noInOperator = defineRule({
|
|
1374
|
+
create: (context) => ({ BinaryExpression: (node) => {
|
|
1375
|
+
if (node.operator !== "in") return;
|
|
1376
|
+
context.report({
|
|
1377
|
+
message: "Decode the value with Schema, or narrow it with Predicate.hasProperty, instead of the in operator.",
|
|
1378
|
+
node
|
|
1379
|
+
});
|
|
1380
|
+
} }),
|
|
1381
|
+
meta: { type: "problem" }
|
|
1382
|
+
});
|
|
1383
|
+
//#endregion
|
|
1306
1384
|
//#region src/rules/no-let.ts
|
|
1307
1385
|
const noLet = defineRule({
|
|
1308
1386
|
create: (context) => ({ VariableDeclaration: (node) => {
|
|
@@ -1317,9 +1395,7 @@ const noLet = defineRule({
|
|
|
1317
1395
|
//#region src/rules/no-multiline-comment.ts
|
|
1318
1396
|
const directive = /^\s*(?:eslint|oxlint|@effect-diagnostics|@ts-|\/\s*<reference|#region|#endregion)/u;
|
|
1319
1397
|
function adjacentLines(pair) {
|
|
1320
|
-
|
|
1321
|
-
const second = pair.second;
|
|
1322
|
-
return first?.type === "Line" && second?.type === "Line" && second.loc.start.line === first.loc.end.line + 1;
|
|
1398
|
+
return pair.first?.type === "Line" && pair.second?.type === "Line" && pair.second.loc.start.line === pair.first.loc.end.line + 1;
|
|
1323
1399
|
}
|
|
1324
1400
|
const noMultilineComment = defineRule({
|
|
1325
1401
|
create: (context) => ({ Program: () => {
|
|
@@ -1351,7 +1427,10 @@ const noNativeControl = defineRule({
|
|
|
1351
1427
|
if (node.name.type !== "JSXIdentifier") return;
|
|
1352
1428
|
const name = node.name.name;
|
|
1353
1429
|
map(get$2(primitives, name), (primitive) => {
|
|
1354
|
-
const styled = pipe(node.attributes, findFirst((attribute) => attribute.type === "JSXAttribute" && attribute.name.type === "JSXIdentifier" && attribute.name.name === "className"), flatMap((attribute) =>
|
|
1430
|
+
const styled = pipe(node.attributes, findFirst((attribute) => attribute.type === "JSXAttribute" && attribute.name.type === "JSXIdentifier" && attribute.name.name === "className"), flatMap((attribute) => {
|
|
1431
|
+
if (attribute.type !== "JSXAttribute" || attribute.value?.type !== "Literal") return none();
|
|
1432
|
+
return fromNullishOr(attribute.value.value);
|
|
1433
|
+
}), exists((value) => some$1(split(value, /\s+/u), matches(controlStyle))));
|
|
1355
1434
|
if (name !== "select" && !styled) return;
|
|
1356
1435
|
context.report({
|
|
1357
1436
|
message: `Use the ${primitive} UI primitive instead of a styled native control.`,
|
|
@@ -1450,7 +1529,8 @@ function freshRegExpFlags(input) {
|
|
|
1450
1529
|
if (input.node.type !== "NewExpression" && input.node.type !== "CallExpression" || input.node.callee.type !== "Identifier" || input.node.callee.name !== "RegExp") return none();
|
|
1451
1530
|
const flags = input.node.arguments[1];
|
|
1452
1531
|
if (flags === void 0) return some(some(""));
|
|
1453
|
-
|
|
1532
|
+
if (flags.type !== "Literal" || !isString(flags.value)) return some(none());
|
|
1533
|
+
return some(some(flags.value));
|
|
1454
1534
|
}
|
|
1455
1535
|
const noNativeMethodCall = defineRule({
|
|
1456
1536
|
create: (context) => ({ CallExpression: (node) => {
|
|
@@ -1553,9 +1633,8 @@ function exits(node) {
|
|
|
1553
1633
|
], node.type) || node.type === "BlockStatement" && some$1(node.body, exits);
|
|
1554
1634
|
}
|
|
1555
1635
|
function earlyExit(input) {
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
return some$1(take(parent.body, getOrElse(findFirstIndex(parent.body, (statement) => statement === input.node), () => 0)), (statement) => statement.type === "IfStatement" && exits(statement.consequent) && checks({
|
|
1636
|
+
if (input.node.parent?.type !== "BlockStatement" && input.node.parent?.type !== "Program") return false;
|
|
1637
|
+
return some$1(take(input.node.parent.body, getOrElse(findFirstIndex(input.node.parent.body, (statement) => statement === input.node), () => 0)), (statement) => statement.type === "IfStatement" && exits(statement.consequent) && checks({
|
|
1559
1638
|
...input,
|
|
1560
1639
|
name: "isNone",
|
|
1561
1640
|
operator: "||",
|
|
@@ -1563,8 +1642,7 @@ function earlyExit(input) {
|
|
|
1563
1642
|
}));
|
|
1564
1643
|
}
|
|
1565
1644
|
function guarded(input) {
|
|
1566
|
-
|
|
1567
|
-
if (parent === null || parent.type === "FunctionDeclaration") return false;
|
|
1645
|
+
if (input.node.parent === null || input.node.parent.type === "FunctionDeclaration") return false;
|
|
1568
1646
|
function some(test) {
|
|
1569
1647
|
return checks({
|
|
1570
1648
|
...input,
|
|
@@ -1573,14 +1651,14 @@ function guarded(input) {
|
|
|
1573
1651
|
test
|
|
1574
1652
|
});
|
|
1575
1653
|
}
|
|
1576
|
-
return parent.type === "IfStatement" && parent.consequent === input.node && some(parent.test) || parent.type === "ConditionalExpression" && parent.consequent === input.node && some(parent.test) || parent.type === "LogicalExpression" && parent.right === input.node && parent.operator === "&&" && some(parent.left) || parent.type === "LogicalExpression" && parent.right === input.node && parent.operator === "||" && checks({
|
|
1654
|
+
return input.node.parent.type === "IfStatement" && input.node.parent.consequent === input.node && some(input.node.parent.test) || input.node.parent.type === "ConditionalExpression" && input.node.parent.consequent === input.node && some(input.node.parent.test) || input.node.parent.type === "LogicalExpression" && input.node.parent.right === input.node && input.node.parent.operator === "&&" && some(input.node.parent.left) || input.node.parent.type === "LogicalExpression" && input.node.parent.right === input.node && input.node.parent.operator === "||" && checks({
|
|
1577
1655
|
...input,
|
|
1578
1656
|
name: "isNone",
|
|
1579
1657
|
operator: "||",
|
|
1580
|
-
test: parent.left
|
|
1658
|
+
test: input.node.parent.left
|
|
1581
1659
|
}) || earlyExit(input) || guarded({
|
|
1582
1660
|
...input,
|
|
1583
|
-
node: parent
|
|
1661
|
+
node: input.node.parent
|
|
1584
1662
|
});
|
|
1585
1663
|
}
|
|
1586
1664
|
const noOptionValueAccess = defineRule({
|
|
@@ -1598,6 +1676,169 @@ const noOptionValueAccess = defineRule({
|
|
|
1598
1676
|
meta: { type: "problem" }
|
|
1599
1677
|
});
|
|
1600
1678
|
//#endregion
|
|
1679
|
+
//#region src/rules/no-property-alias.ts
|
|
1680
|
+
const functions = [
|
|
1681
|
+
"ArrowFunctionExpression",
|
|
1682
|
+
"FunctionDeclaration",
|
|
1683
|
+
"FunctionExpression"
|
|
1684
|
+
];
|
|
1685
|
+
const deferred = [
|
|
1686
|
+
"ChainExpression",
|
|
1687
|
+
"ConditionalExpression",
|
|
1688
|
+
"DoWhileStatement",
|
|
1689
|
+
"ForInStatement",
|
|
1690
|
+
"ForOfStatement",
|
|
1691
|
+
"ForStatement",
|
|
1692
|
+
"IfStatement",
|
|
1693
|
+
"LogicalExpression",
|
|
1694
|
+
"SwitchStatement",
|
|
1695
|
+
"WhileStatement"
|
|
1696
|
+
];
|
|
1697
|
+
function insideFunction(node) {
|
|
1698
|
+
if (isNullish$1(node.parent) || node.parent.type === "Program") return false;
|
|
1699
|
+
if (contains$1(functions, node.parent.type)) return true;
|
|
1700
|
+
return insideFunction(node.parent);
|
|
1701
|
+
}
|
|
1702
|
+
function sameRoot(input) {
|
|
1703
|
+
return getOrUndefined(variableFromScope({
|
|
1704
|
+
name: input.name,
|
|
1705
|
+
scope: input.context.sourceCode.getScope(input.declarator)
|
|
1706
|
+
})) === getOrUndefined(variableFromScope({
|
|
1707
|
+
name: input.name,
|
|
1708
|
+
scope: input.context.sourceCode.getScope(input.read)
|
|
1709
|
+
}));
|
|
1710
|
+
}
|
|
1711
|
+
function enclosed(input) {
|
|
1712
|
+
if (isNullish$1(input.node.parent) || input.node.parent === input.block) return false;
|
|
1713
|
+
if (contains$1(input.types, input.node.parent.type)) return true;
|
|
1714
|
+
return enclosed({
|
|
1715
|
+
...input,
|
|
1716
|
+
node: input.node.parent
|
|
1717
|
+
});
|
|
1718
|
+
}
|
|
1719
|
+
function between(input) {
|
|
1720
|
+
return input.node.range[0] >= input.declaration.range[1] && input.node.range[1] <= input.read.range[0];
|
|
1721
|
+
}
|
|
1722
|
+
function objectRoot(node) {
|
|
1723
|
+
if (node.type === "Identifier") return some(node.name);
|
|
1724
|
+
if (node.type === "MemberExpression") return objectRoot(node.object);
|
|
1725
|
+
return none();
|
|
1726
|
+
}
|
|
1727
|
+
function written(node) {
|
|
1728
|
+
if (node.type === "AssignmentExpression") return objectRoot(node.left);
|
|
1729
|
+
if (node.type === "UpdateExpression" || node.type === "UnaryExpression" && node.operator === "delete") return objectRoot(node.argument);
|
|
1730
|
+
return none();
|
|
1731
|
+
}
|
|
1732
|
+
function inert(token) {
|
|
1733
|
+
if (contains$1([
|
|
1734
|
+
"Boolean",
|
|
1735
|
+
"Identifier",
|
|
1736
|
+
"Null",
|
|
1737
|
+
"Numeric",
|
|
1738
|
+
"String"
|
|
1739
|
+
], token.type)) return true;
|
|
1740
|
+
if (token.type === "Keyword") return contains$1(["const", "return"], token.value);
|
|
1741
|
+
return token.type === "Punctuator" && contains$1([
|
|
1742
|
+
"(",
|
|
1743
|
+
",",
|
|
1744
|
+
":",
|
|
1745
|
+
"=",
|
|
1746
|
+
"[",
|
|
1747
|
+
"{"
|
|
1748
|
+
], token.value);
|
|
1749
|
+
}
|
|
1750
|
+
function replacement(input) {
|
|
1751
|
+
if (input.read.parent?.type === "Property" && input.read.parent.shorthand) return `${input.name}: ${input.text}`;
|
|
1752
|
+
return input.text;
|
|
1753
|
+
}
|
|
1754
|
+
function report$1(input) {
|
|
1755
|
+
const reads = pipe(input.variable.references, filter$1((reference) => reference.isRead()), map$1((reference) => reference.identifier));
|
|
1756
|
+
const text = input.context.sourceCode.getText(input.init);
|
|
1757
|
+
pipe(rootName(input.init), filter((root) => isArrayNonEmpty(reads) && every(reads, (read) => {
|
|
1758
|
+
const used = outermost(read);
|
|
1759
|
+
return read.parent.type !== "ExportSpecifier" && !enclosed({
|
|
1760
|
+
block: input.declaration.parent,
|
|
1761
|
+
node: read,
|
|
1762
|
+
types: functions
|
|
1763
|
+
}) && !(used.parent?.type === "CallExpression" && used.parent.callee === used) && !(used.parent?.type === "TaggedTemplateExpression" && used.parent.tag === used) && !some$1(input.writes, (write) => between({
|
|
1764
|
+
declaration: input.declaration,
|
|
1765
|
+
node: write,
|
|
1766
|
+
read
|
|
1767
|
+
}) && contains(written(write), root)) && sameRoot({
|
|
1768
|
+
context: input.context,
|
|
1769
|
+
declarator: input.node,
|
|
1770
|
+
name: root,
|
|
1771
|
+
read
|
|
1772
|
+
});
|
|
1773
|
+
})), match({
|
|
1774
|
+
onNone: constVoid,
|
|
1775
|
+
onSome: () => input.context.report({
|
|
1776
|
+
fix: (fixer) => {
|
|
1777
|
+
if (reads.length !== 1 || some$1(reads, (read) => startsWith("JSX")(read.parent.type) || enclosed({
|
|
1778
|
+
block: input.declaration.parent,
|
|
1779
|
+
node: read,
|
|
1780
|
+
types: deferred
|
|
1781
|
+
}) || some$1(input.statements, (node) => between({
|
|
1782
|
+
declaration: input.declaration,
|
|
1783
|
+
node,
|
|
1784
|
+
read
|
|
1785
|
+
})) || !every(input.context.sourceCode.getTokensBetween(input.declaration, read), inert))) return [];
|
|
1786
|
+
return [fixer.remove(input.declaration), ...map$1(reads, (read) => fixer.replaceText(read, replacement({
|
|
1787
|
+
name: input.name,
|
|
1788
|
+
read,
|
|
1789
|
+
text
|
|
1790
|
+
})))];
|
|
1791
|
+
},
|
|
1792
|
+
message: `Read ${text} where it is used; this name adds no value.`,
|
|
1793
|
+
node: input.node
|
|
1794
|
+
})
|
|
1795
|
+
}));
|
|
1796
|
+
}
|
|
1797
|
+
const noPropertyAlias = defineRule({
|
|
1798
|
+
create: (context) => {
|
|
1799
|
+
const writes = make$2();
|
|
1800
|
+
const candidates = make$2();
|
|
1801
|
+
function write(node) {
|
|
1802
|
+
append$1(writes, node);
|
|
1803
|
+
}
|
|
1804
|
+
return {
|
|
1805
|
+
AssignmentExpression: write,
|
|
1806
|
+
"Program:exit": () => {
|
|
1807
|
+
const nodes = toArray(writes);
|
|
1808
|
+
for (const candidate of toArray(candidates)) pipe(variableFromScope({
|
|
1809
|
+
name: candidate.name,
|
|
1810
|
+
scope: context.sourceCode.getScope(candidate.node)
|
|
1811
|
+
}), match({
|
|
1812
|
+
onNone: constVoid,
|
|
1813
|
+
onSome: (variable) => report$1({
|
|
1814
|
+
...candidate,
|
|
1815
|
+
context,
|
|
1816
|
+
variable,
|
|
1817
|
+
writes: nodes
|
|
1818
|
+
})
|
|
1819
|
+
}));
|
|
1820
|
+
},
|
|
1821
|
+
UnaryExpression: (node) => {
|
|
1822
|
+
if (node.operator === "delete") write(node);
|
|
1823
|
+
},
|
|
1824
|
+
UpdateExpression: write,
|
|
1825
|
+
VariableDeclarator: (node) => {
|
|
1826
|
+
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, {
|
|
1827
|
+
declaration: node.parent,
|
|
1828
|
+
init: node.init,
|
|
1829
|
+
name: node.id.name,
|
|
1830
|
+
node,
|
|
1831
|
+
statements: node.parent.parent.body
|
|
1832
|
+
});
|
|
1833
|
+
}
|
|
1834
|
+
};
|
|
1835
|
+
},
|
|
1836
|
+
meta: {
|
|
1837
|
+
fixable: "code",
|
|
1838
|
+
type: "problem"
|
|
1839
|
+
}
|
|
1840
|
+
});
|
|
1841
|
+
//#endregion
|
|
1601
1842
|
//#region src/rules/no-readonly-type-syntax.ts
|
|
1602
1843
|
function isTopLevelStatement(node) {
|
|
1603
1844
|
if (node.parent.type === "Program") return true;
|
|
@@ -1615,10 +1856,13 @@ function typeDeclarations(input) {
|
|
|
1615
1856
|
if (input.name.type === "Identifier") return pipe(variableFromScope({
|
|
1616
1857
|
name: input.name.name,
|
|
1617
1858
|
scope: input.context.sourceCode.getScope(input.name)
|
|
1618
|
-
}), flatMap((variable) =>
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1859
|
+
}), flatMap((variable) => {
|
|
1860
|
+
if (variable.scope.block.type !== "ClassDeclaration" || !some$1(variable.defs, (definition) => definition.node === variable.scope.block)) return some(variable);
|
|
1861
|
+
return variableFromScope({
|
|
1862
|
+
name: variable.name,
|
|
1863
|
+
scope: variable.scope.upper
|
|
1864
|
+
});
|
|
1865
|
+
}), map((variable) => map$1(variable.defs, (definition) => definition.node)), getOrElse(() => empty()));
|
|
1622
1866
|
if (input.name.type !== "TSQualifiedName") return [];
|
|
1623
1867
|
const name = input.name;
|
|
1624
1868
|
return pipe(typeDeclarations({
|
|
@@ -1649,15 +1893,18 @@ function shapeLiterals(input) {
|
|
|
1649
1893
|
type: alias.typeAnnotation
|
|
1650
1894
|
})));
|
|
1651
1895
|
}
|
|
1896
|
+
function removeLeading(input) {
|
|
1897
|
+
return (fixer) => pipe(input.context.sourceCode.getText(input.node), match$1(input.pattern), map((match) => fixer.replaceTextRange([input.node.range[0], input.node.range[0] + match[0].length], match[1] ?? "")), getOrUndefined);
|
|
1898
|
+
}
|
|
1652
1899
|
const noReadonlyTypeSyntax = defineRule({
|
|
1653
1900
|
create: (context) => {
|
|
1654
1901
|
const cycleNames = schemaCycleNames({
|
|
1655
1902
|
context,
|
|
1656
1903
|
program: context.sourceCode.ast
|
|
1657
1904
|
});
|
|
1658
|
-
const shapes = make$
|
|
1659
|
-
const properties = make$
|
|
1660
|
-
const methods = make$
|
|
1905
|
+
const shapes = make$3(empty());
|
|
1906
|
+
const properties = make$3(empty());
|
|
1907
|
+
const methods = make$3(empty());
|
|
1661
1908
|
function exempt(node) {
|
|
1662
1909
|
return insideCycleType({
|
|
1663
1910
|
cycleNames,
|
|
@@ -1684,10 +1931,16 @@ const noReadonlyTypeSyntax = defineRule({
|
|
|
1684
1931
|
}
|
|
1685
1932
|
for (const node of get$3(properties)) {
|
|
1686
1933
|
if (inShape(node) && !node.readonly) context.report({
|
|
1934
|
+
fix: (fixer) => fixer.insertTextBefore(node, "readonly "),
|
|
1687
1935
|
message: "Mark this service property readonly.",
|
|
1688
1936
|
node
|
|
1689
1937
|
});
|
|
1690
1938
|
if (!inShape(node) && node.readonly && !exempt(node)) context.report({
|
|
1939
|
+
fix: removeLeading({
|
|
1940
|
+
context,
|
|
1941
|
+
node,
|
|
1942
|
+
pattern: /^readonly\s+/u
|
|
1943
|
+
}),
|
|
1691
1944
|
message: "Remove the readonly property modifier.",
|
|
1692
1945
|
node
|
|
1693
1946
|
});
|
|
@@ -1699,12 +1952,22 @@ const noReadonlyTypeSyntax = defineRule({
|
|
|
1699
1952
|
},
|
|
1700
1953
|
TSIndexSignature: (node) => {
|
|
1701
1954
|
if (node.readonly) context.report({
|
|
1955
|
+
fix: removeLeading({
|
|
1956
|
+
context,
|
|
1957
|
+
node,
|
|
1958
|
+
pattern: /^readonly\s+/u
|
|
1959
|
+
}),
|
|
1702
1960
|
message: "Remove the readonly index modifier.",
|
|
1703
1961
|
node
|
|
1704
1962
|
});
|
|
1705
1963
|
},
|
|
1706
1964
|
TSMappedType: (node) => {
|
|
1707
1965
|
if (node.readonly === true || node.readonly === "+") context.report({
|
|
1966
|
+
fix: removeLeading({
|
|
1967
|
+
context,
|
|
1968
|
+
node,
|
|
1969
|
+
pattern: /^(\{\s*)\+?readonly\s+/u
|
|
1970
|
+
}),
|
|
1708
1971
|
message: "Remove the readonly mapped-type modifier.",
|
|
1709
1972
|
node
|
|
1710
1973
|
});
|
|
@@ -1717,78 +1980,24 @@ const noReadonlyTypeSyntax = defineRule({
|
|
|
1717
1980
|
},
|
|
1718
1981
|
TSTypeOperator: (node) => {
|
|
1719
1982
|
if (node.operator === "readonly" && !exempt(node)) context.report({
|
|
1983
|
+
fix: (fixer) => fixer.replaceText(node, context.sourceCode.getText(node.typeAnnotation)),
|
|
1720
1984
|
message: "Use a mutable type shape.",
|
|
1721
1985
|
node
|
|
1722
1986
|
});
|
|
1723
1987
|
},
|
|
1724
1988
|
TSTypeReference: (node) => {
|
|
1725
1989
|
if (node.typeName.type === "Identifier" && node.typeName.name === "Readonly" && !exempt(node)) context.report({
|
|
1990
|
+
fix: (fixer) => pipe(fromNullishOr(node.typeArguments?.params[0]), map((shape) => fixer.replaceText(node, `(${context.sourceCode.getText(shape)})`)), getOrUndefined),
|
|
1726
1991
|
message: "Use a mutable type shape.",
|
|
1727
1992
|
node
|
|
1728
1993
|
});
|
|
1729
1994
|
}
|
|
1730
1995
|
};
|
|
1731
1996
|
},
|
|
1732
|
-
meta: {
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
function isExported(node) {
|
|
1737
|
-
if (node.parent.type === "ExportNamedDeclaration" || node.parent.type === "ExportDefaultDeclaration") return true;
|
|
1738
|
-
return node.parent.type === "VariableDeclarator" && node.parent.parent.type === "VariableDeclaration" && node.parent.parent.parent.type === "ExportNamedDeclaration";
|
|
1739
|
-
}
|
|
1740
|
-
function inferredIdentically(input) {
|
|
1741
|
-
const text = input.context.sourceCode.getText;
|
|
1742
|
-
const body = input.node.body;
|
|
1743
|
-
if (body === null) return false;
|
|
1744
|
-
const block = body.type === "BlockStatement";
|
|
1745
|
-
const values = body.type === "BlockStatement" ? map$1(input.returns, (statement) => statement.argument) : [body];
|
|
1746
|
-
const expressions = filter$1(values, isNotNull);
|
|
1747
|
-
if (input.type.type === "TSVoidKeyword") return block && isArrayEmpty(expressions);
|
|
1748
|
-
if (isArrayEmpty(expressions) || expressions.length !== values.length) return false;
|
|
1749
|
-
if (input.type.type === "TSStringKeyword") return every(expressions, (value) => value.type === "TemplateLiteral" && isArrayNonEmpty(value.expressions));
|
|
1750
|
-
if (isExported(input.node)) return false;
|
|
1751
|
-
return every(expressions, (value) => value.type === "TSAsExpression" && text(value.typeAnnotation) === text(input.type) || !block && value.type === "NewExpression" && input.type.type === "TSTypeReference" && input.type.typeArguments === null && text(value.callee) === text(input.type.typeName));
|
|
1752
|
-
}
|
|
1753
|
-
const noRedundantReturnType = defineRule({
|
|
1754
|
-
create: (context) => {
|
|
1755
|
-
const frames = make$2(empty());
|
|
1756
|
-
function enter() {
|
|
1757
|
-
update(frames, append({ returns: empty() }));
|
|
1758
|
-
}
|
|
1759
|
-
function exit(node) {
|
|
1760
|
-
const frame = last(get$3(frames));
|
|
1761
|
-
update(frames, dropRight(1));
|
|
1762
|
-
if (node.async || node.generator || isNullish$1(node.returnType)) return;
|
|
1763
|
-
if (inferredIdentically({
|
|
1764
|
-
context,
|
|
1765
|
-
node,
|
|
1766
|
-
returns: match$1(frame, {
|
|
1767
|
-
onNone: () => empty(),
|
|
1768
|
-
onSome: (top) => top.returns
|
|
1769
|
-
}),
|
|
1770
|
-
type: node.returnType.typeAnnotation
|
|
1771
|
-
})) context.report({
|
|
1772
|
-
message: "Drop this return type; inference gives the same type.",
|
|
1773
|
-
node: node.returnType
|
|
1774
|
-
});
|
|
1775
|
-
}
|
|
1776
|
-
return {
|
|
1777
|
-
ArrowFunctionExpression: enter,
|
|
1778
|
-
"ArrowFunctionExpression:exit": exit,
|
|
1779
|
-
FunctionDeclaration: enter,
|
|
1780
|
-
"FunctionDeclaration:exit": exit,
|
|
1781
|
-
FunctionExpression: enter,
|
|
1782
|
-
"FunctionExpression:exit": exit,
|
|
1783
|
-
ReturnStatement: (node) => {
|
|
1784
|
-
update(frames, (stack) => pipe(last(stack), match$1({
|
|
1785
|
-
onNone: () => stack,
|
|
1786
|
-
onSome: (top) => append(dropRight(stack, 1), { returns: append(top.returns, node) })
|
|
1787
|
-
})));
|
|
1788
|
-
}
|
|
1789
|
-
};
|
|
1790
|
-
},
|
|
1791
|
-
meta: { type: "problem" }
|
|
1997
|
+
meta: {
|
|
1998
|
+
fixable: "code",
|
|
1999
|
+
type: "problem"
|
|
2000
|
+
}
|
|
1792
2001
|
});
|
|
1793
2002
|
//#endregion
|
|
1794
2003
|
//#region src/rules/no-redundant-state-type.ts
|
|
@@ -1852,18 +2061,17 @@ const noRedundantUseRefNullType = defineRule({
|
|
|
1852
2061
|
//#region src/rules/no-redundant-variable-annotation.ts
|
|
1853
2062
|
const noRedundantVariableAnnotation = defineRule({
|
|
1854
2063
|
create: (context) => ({ VariableDeclarator: (node) => {
|
|
1855
|
-
|
|
1856
|
-
if (node.init === null || isNullish$1(annotation)) return;
|
|
2064
|
+
if (node.init === null || isNullish$1(node.id.typeAnnotation)) return;
|
|
1857
2065
|
if (node.init.type === "ArrayExpression" && isArrayEmpty(node.init.elements)) {
|
|
1858
2066
|
context.report({
|
|
1859
2067
|
message: "Write Array.empty<T>() instead of annotating an empty array.",
|
|
1860
|
-
node:
|
|
2068
|
+
node: node.id.typeAnnotation
|
|
1861
2069
|
});
|
|
1862
2070
|
return;
|
|
1863
2071
|
}
|
|
1864
|
-
if (node.init.type === "TSAsExpression" && context.sourceCode.getText(node.init.typeAnnotation) === context.sourceCode.getText(
|
|
2072
|
+
if (node.init.type === "TSAsExpression" && context.sourceCode.getText(node.init.typeAnnotation) === context.sourceCode.getText(node.id.typeAnnotation.typeAnnotation)) context.report({
|
|
1865
2073
|
message: "Drop this annotation; inference gives the same type.",
|
|
1866
|
-
node:
|
|
2074
|
+
node: node.id.typeAnnotation
|
|
1867
2075
|
});
|
|
1868
2076
|
} }),
|
|
1869
2077
|
meta: { type: "problem" }
|
|
@@ -1903,11 +2111,12 @@ const shipped = [
|
|
|
1903
2111
|
}
|
|
1904
2112
|
];
|
|
1905
2113
|
function schemaMember(input) {
|
|
1906
|
-
|
|
2114
|
+
if (input.node.type !== "MemberExpression" || !importedMember({
|
|
1907
2115
|
context: input.context,
|
|
1908
2116
|
importedName: "Schema",
|
|
1909
2117
|
node: input.node
|
|
1910
|
-
})
|
|
2118
|
+
})) return none();
|
|
2119
|
+
return memberName(input.node);
|
|
1911
2120
|
}
|
|
1912
2121
|
function shippedSchema(input) {
|
|
1913
2122
|
if (input.filter.type !== "CallExpression") return none();
|
|
@@ -1920,16 +2129,19 @@ function shippedSchema(input) {
|
|
|
1920
2129
|
node: input.filter.callee
|
|
1921
2130
|
});
|
|
1922
2131
|
const [value, ...rest] = input.filter.arguments;
|
|
1923
|
-
return pipe(shipped, findFirst((entry) => contains(base, entry.base) && contains(check, entry.check) && isArrayEmpty(rest) && match
|
|
2132
|
+
return pipe(shipped, findFirst((entry) => contains(base, entry.base) && contains(check, entry.check) && isArrayEmpty(rest) && match(fromUndefinedOr(entry.only), {
|
|
1924
2133
|
onNone: () => value === void 0,
|
|
1925
2134
|
onSome: (only) => value?.type === "Literal" && value.value === only
|
|
1926
2135
|
})), map((entry) => entry.schema));
|
|
1927
2136
|
}
|
|
1928
2137
|
function pipedBase(node) {
|
|
1929
|
-
|
|
1930
|
-
if (parent.type
|
|
1931
|
-
|
|
1932
|
-
|
|
2138
|
+
if (node.parent.type !== "CallExpression") return none();
|
|
2139
|
+
if (node.parent.callee.type === "Identifier" && node.parent.callee.name === "pipe" && node.parent.arguments.length === 2) {
|
|
2140
|
+
if (node.parent.arguments[1] !== node) return none();
|
|
2141
|
+
return fromNullishOr(node.parent.arguments[0]);
|
|
2142
|
+
}
|
|
2143
|
+
if (node.parent.callee.type !== "MemberExpression" || !contains(memberName(node.parent.callee), "pipe") || node.parent.arguments.length !== 1) return none();
|
|
2144
|
+
return some(node.parent.callee.object);
|
|
1933
2145
|
}
|
|
1934
2146
|
function reinventedSchema(input) {
|
|
1935
2147
|
if (input.node.callee.type !== "MemberExpression" || !contains(memberName(input.node.callee), "check")) return none();
|
|
@@ -1938,11 +2150,14 @@ function reinventedSchema(input) {
|
|
|
1938
2150
|
if (isSome(schemaMember({
|
|
1939
2151
|
context: input.context,
|
|
1940
2152
|
node: callee
|
|
1941
|
-
})))
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
2153
|
+
}))) {
|
|
2154
|
+
if (input.node.arguments.length !== 1 || filter === void 0) return none();
|
|
2155
|
+
return pipe(pipedBase(input.node), flatMap((base) => shippedSchema({
|
|
2156
|
+
base,
|
|
2157
|
+
context: input.context,
|
|
2158
|
+
filter
|
|
2159
|
+
})));
|
|
2160
|
+
}
|
|
1946
2161
|
return findFirst(input.node.arguments, (argument) => shippedSchema({
|
|
1947
2162
|
base: callee.object,
|
|
1948
2163
|
context: input.context,
|
|
@@ -1965,9 +2180,13 @@ const noReinventedSchema = defineRule({
|
|
|
1965
2180
|
});
|
|
1966
2181
|
//#endregion
|
|
1967
2182
|
//#region src/rules/no-renamed-import.ts
|
|
2183
|
+
function exportedName(node) {
|
|
2184
|
+
if (node.type === "Literal") return node.value;
|
|
2185
|
+
return node.name;
|
|
2186
|
+
}
|
|
1968
2187
|
const noRenamedImport = defineRule({
|
|
1969
2188
|
create: (context) => ({ ImportSpecifier: (node) => {
|
|
1970
|
-
const imported = node.imported
|
|
2189
|
+
const imported = exportedName(node.imported);
|
|
1971
2190
|
if (imported === node.local.name || imported === "it" && node.local.name === "test") return;
|
|
1972
2191
|
context.report({
|
|
1973
2192
|
message: `Import ${imported} under its own name.`,
|
|
@@ -1977,6 +2196,76 @@ const noRenamedImport = defineRule({
|
|
|
1977
2196
|
meta: { type: "problem" }
|
|
1978
2197
|
});
|
|
1979
2198
|
//#endregion
|
|
2199
|
+
//#region src/rules/no-return-type.ts
|
|
2200
|
+
function boundName(node) {
|
|
2201
|
+
if (node.parent.type === "CallExpression" && node.parent.parent.type === "VariableDeclarator" && node.parent.parent.id.type === "Identifier") return some(node.parent.parent.id.name);
|
|
2202
|
+
return bindingName(node);
|
|
2203
|
+
}
|
|
2204
|
+
function functionScopes(input) {
|
|
2205
|
+
const scope = input.context.sourceCode.getScope(input.node);
|
|
2206
|
+
return filter$1([scope, ...scope.childScopes], (child) => child.block.range[0] >= input.node.range[0] && child.block.range[1] <= input.node.range[1]);
|
|
2207
|
+
}
|
|
2208
|
+
function propertyRead(reference) {
|
|
2209
|
+
return reference.identifier.parent.type === "MemberExpression" && reference.identifier.parent.object === reference.identifier && !(reference.identifier.parent.parent.type === "CallExpression" && reference.identifier.parent.parent.callee === reference.identifier.parent);
|
|
2210
|
+
}
|
|
2211
|
+
function reaches(input) {
|
|
2212
|
+
const reached = pipe(input.nodes, flatMap$1((node) => functionScopes({
|
|
2213
|
+
context: input.context,
|
|
2214
|
+
node
|
|
2215
|
+
})), flatMap$1((scope) => scope.through), filter$1((reference) => !propertyRead(reference)), flatMap$1((reference) => toArray$1(fromNullishOr(reference.resolved))), dedupeWith(strictEqual()));
|
|
2216
|
+
const known = containsWith(strictEqual());
|
|
2217
|
+
if (some$1(reached, (variable) => known(input.targets, variable))) return true;
|
|
2218
|
+
const next = filter$1(reached, (variable) => !known(input.visited, variable));
|
|
2219
|
+
if (isArrayEmpty(next)) return false;
|
|
2220
|
+
return reaches({
|
|
2221
|
+
...input,
|
|
2222
|
+
nodes: pipe(next, flatMap$1((variable) => variable.defs), filter$1((definition) => definition.type === "FunctionName" || definition.type === "Variable"), map$1((definition) => definition.node)),
|
|
2223
|
+
visited: appendAll(input.visited, next)
|
|
2224
|
+
});
|
|
2225
|
+
}
|
|
2226
|
+
function selfMethodCall(input) {
|
|
2227
|
+
if (input.node.parent.type !== "MethodDefinition" || input.node.parent.key.type !== "Identifier") return false;
|
|
2228
|
+
return matches(new RegExp(`\\bthis\\.${replaceAll("$", "\\$")(input.node.parent.key.name)}\\(`, "u"))(input.context.sourceCode.getText(input.node));
|
|
2229
|
+
}
|
|
2230
|
+
function recursive(input) {
|
|
2231
|
+
const scope = input.context.sourceCode.getScope(input.node);
|
|
2232
|
+
const targets = pipe([boundName(input.node), fromNullishOr(input.node.id?.name)], getSomes, flatMap$1((name) => toArray$1(variableFromScope({
|
|
2233
|
+
name,
|
|
2234
|
+
scope
|
|
2235
|
+
}))), dedupeWith(strictEqual()));
|
|
2236
|
+
return selfMethodCall(input) || isArrayNonEmpty(targets) && reaches({
|
|
2237
|
+
context: input.context,
|
|
2238
|
+
nodes: [input.node],
|
|
2239
|
+
targets,
|
|
2240
|
+
visited: []
|
|
2241
|
+
});
|
|
2242
|
+
}
|
|
2243
|
+
const noReturnType = defineRule({
|
|
2244
|
+
create: (context) => {
|
|
2245
|
+
function check(node) {
|
|
2246
|
+
if (isNullish$1(node.returnType) || node.body === null || node.returnType.typeAnnotation.type === "TSTypePredicate" || node.parent.type === "CallExpression" && importedMember({
|
|
2247
|
+
context,
|
|
2248
|
+
importedName: "Schema",
|
|
2249
|
+
node: node.parent.callee,
|
|
2250
|
+
propertyName: "suspend"
|
|
2251
|
+
}) || recursive({
|
|
2252
|
+
context,
|
|
2253
|
+
node
|
|
2254
|
+
})) return;
|
|
2255
|
+
context.report({
|
|
2256
|
+
message: "Drop the return type and let inference supply it; check a returned object with satisfies.",
|
|
2257
|
+
node: node.returnType
|
|
2258
|
+
});
|
|
2259
|
+
}
|
|
2260
|
+
return {
|
|
2261
|
+
ArrowFunctionExpression: check,
|
|
2262
|
+
FunctionDeclaration: check,
|
|
2263
|
+
FunctionExpression: check
|
|
2264
|
+
};
|
|
2265
|
+
},
|
|
2266
|
+
meta: { type: "problem" }
|
|
2267
|
+
});
|
|
2268
|
+
//#endregion
|
|
1980
2269
|
//#region src/rules/no-single-use-value.ts
|
|
1981
2270
|
const repeating = [
|
|
1982
2271
|
"ArrowFunctionExpression",
|
|
@@ -1989,16 +2278,14 @@ const repeating = [
|
|
|
1989
2278
|
"WhileStatement"
|
|
1990
2279
|
];
|
|
1991
2280
|
function runsOnce(input) {
|
|
1992
|
-
|
|
1993
|
-
const child = input.child;
|
|
1994
|
-
if ((node.type === "ForOfStatement" || node.type === "ForInStatement") && node.right === child || node.type === "IfStatement" && node.test === child || node.type === "ConditionalExpression" && node.test === child || node.type === "LogicalExpression" && node.left === child) return climb({
|
|
2281
|
+
if ((input.node.type === "ForOfStatement" || input.node.type === "ForInStatement") && input.node.right === input.child || input.node.type === "IfStatement" && input.node.test === input.child || input.node.type === "ConditionalExpression" && input.node.test === input.child || input.node.type === "LogicalExpression" && input.node.left === input.child) return climb({
|
|
1995
2282
|
...input,
|
|
1996
|
-
node
|
|
2283
|
+
node: input.node
|
|
1997
2284
|
});
|
|
1998
|
-
if (contains$1(repeating, node.type) || node.type === "IfStatement" || node.type === "ConditionalExpression" || node.type === "LogicalExpression" || node.type === "ChainExpression" || node.type === "SwitchStatement" || node.type === "TryStatement") return false;
|
|
2285
|
+
if (contains$1(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;
|
|
1999
2286
|
return climb({
|
|
2000
2287
|
...input,
|
|
2001
|
-
node
|
|
2288
|
+
node: input.node
|
|
2002
2289
|
});
|
|
2003
2290
|
}
|
|
2004
2291
|
function climb(input) {
|
|
@@ -2019,9 +2306,23 @@ function insideConditional(input) {
|
|
|
2019
2306
|
});
|
|
2020
2307
|
}
|
|
2021
2308
|
function candidate(node) {
|
|
2022
|
-
const
|
|
2023
|
-
|
|
2024
|
-
|
|
2309
|
+
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$1(["ArrowFunctionExpression", "FunctionExpression"], node.init.type) && !(node.init.type === "MemberExpression" && isSome(rootName(node.init))) && !(node.init.type === "Literal" && !hasProperty(node.init, "regex")) && !(node.init.type === "TemplateLiteral" && isArrayEmpty(node.init.expressions));
|
|
2310
|
+
}
|
|
2311
|
+
function unwrapped(node) {
|
|
2312
|
+
return pipe(typeWrapped(node), match({
|
|
2313
|
+
onNone: () => node,
|
|
2314
|
+
onSome: unwrapped
|
|
2315
|
+
}));
|
|
2316
|
+
}
|
|
2317
|
+
function fixable(input) {
|
|
2318
|
+
const read = outermost(input.read);
|
|
2319
|
+
return !(unwrapped(input.init).type === "ClassExpression" || read.parent?.type === "CallExpression" && read.parent.callee === read || read.parent?.type === "TaggedTemplateExpression" && read.parent.tag === read);
|
|
2320
|
+
}
|
|
2321
|
+
function inlined(input) {
|
|
2322
|
+
const value = `(${input.context.sourceCode.getText(input.init)})`;
|
|
2323
|
+
if (input.read.parent?.type === "Property" && input.read.parent.shorthand) return `${input.name}: ${value}`;
|
|
2324
|
+
if (input.read.range[0] === input.next.range[0]) return `;${value}`;
|
|
2325
|
+
return value;
|
|
2025
2326
|
}
|
|
2026
2327
|
function report(input) {
|
|
2027
2328
|
const declaration = input.node.parent;
|
|
@@ -2038,17 +2339,33 @@ function report(input) {
|
|
|
2038
2339
|
}) && !(init.type === "ConditionalExpression" && insideConditional({
|
|
2039
2340
|
node: read,
|
|
2040
2341
|
statement: next
|
|
2041
|
-
})) && !some$1(input.effects, (effect) => effect.range[0] >= next.range[0] && effect.range[1] <= read.range[0]))
|
|
2042
|
-
|
|
2342
|
+
})) && !some$1(input.effects, (effect) => effect.range[0] >= next.range[0] && effect.range[1] <= read.range[0])), map((read) => ({
|
|
2343
|
+
next,
|
|
2344
|
+
read
|
|
2345
|
+
})))), match({
|
|
2346
|
+
onNone: constVoid,
|
|
2347
|
+
onSome: (found) => input.context.report({
|
|
2348
|
+
fix: (fixer) => {
|
|
2349
|
+
if (!fixable({
|
|
2350
|
+
init,
|
|
2351
|
+
read: found.read
|
|
2352
|
+
})) return [];
|
|
2353
|
+
return [fixer.remove(declaration), fixer.replaceText(found.read, inlined({
|
|
2354
|
+
context: input.context,
|
|
2355
|
+
init,
|
|
2356
|
+
name,
|
|
2357
|
+
...found
|
|
2358
|
+
}))];
|
|
2359
|
+
},
|
|
2043
2360
|
message: "Inline this value into the next statement, its only use.",
|
|
2044
2361
|
node: input.node
|
|
2045
|
-
})
|
|
2362
|
+
})
|
|
2046
2363
|
}));
|
|
2047
2364
|
}
|
|
2048
2365
|
const noSingleUseValue = defineRule({
|
|
2049
2366
|
create: (context) => {
|
|
2050
|
-
const effects = make$
|
|
2051
|
-
const candidates = make$
|
|
2367
|
+
const effects = make$3(empty());
|
|
2368
|
+
const candidates = make$3(empty());
|
|
2052
2369
|
function effect(node) {
|
|
2053
2370
|
update(effects, append(node));
|
|
2054
2371
|
}
|
|
@@ -2072,7 +2389,10 @@ const noSingleUseValue = defineRule({
|
|
|
2072
2389
|
YieldExpression: effect
|
|
2073
2390
|
};
|
|
2074
2391
|
},
|
|
2075
|
-
meta: {
|
|
2392
|
+
meta: {
|
|
2393
|
+
fixable: "code",
|
|
2394
|
+
type: "problem"
|
|
2395
|
+
}
|
|
2076
2396
|
});
|
|
2077
2397
|
//#endregion
|
|
2078
2398
|
//#region src/rules/no-stored-schema-operation.ts
|
|
@@ -2109,21 +2429,60 @@ function parameterName(parameter) {
|
|
|
2109
2429
|
if (parameter.type === "RestElement" && parameter.argument.type === "Identifier") return some(parameter.argument.name);
|
|
2110
2430
|
return none();
|
|
2111
2431
|
}
|
|
2112
|
-
function
|
|
2113
|
-
return input.
|
|
2432
|
+
function forwarded(input) {
|
|
2433
|
+
return input.args.length === input.names.length && every(input.args, (argument, index) => {
|
|
2114
2434
|
const name = input.names[index];
|
|
2115
2435
|
if (argument.type === "SpreadElement") return argument.argument.type === "Identifier" && argument.argument.name === name;
|
|
2116
2436
|
return argument.type === "Identifier" && argument.name === name;
|
|
2117
2437
|
});
|
|
2118
2438
|
}
|
|
2119
|
-
function
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2439
|
+
function callGroups(node) {
|
|
2440
|
+
if (node.type !== "CallExpression") return {
|
|
2441
|
+
callee: node,
|
|
2442
|
+
groups: []
|
|
2443
|
+
};
|
|
2444
|
+
const inner = callGroups(node.callee);
|
|
2445
|
+
return {
|
|
2446
|
+
callee: inner.callee,
|
|
2447
|
+
groups: append(inner.groups, node.arguments)
|
|
2448
|
+
};
|
|
2449
|
+
}
|
|
2450
|
+
function detachable(input) {
|
|
2451
|
+
if (input.node.type === "Identifier") return true;
|
|
2452
|
+
if (input.node.type !== "MemberExpression" || input.node.object.type !== "Identifier") return false;
|
|
2453
|
+
return pipe(rootName(input.node), flatMap((name) => variableFromScope({
|
|
2454
|
+
name,
|
|
2455
|
+
scope: input.context.sourceCode.getScope(input.node)
|
|
2456
|
+
})), exists((variable) => some$1(variable.defs, (definition) => definition.type === "ImportBinding" && definition.parent?.type === "ImportDeclaration" && (definition.parent.source.value === "effect" || definition.node.type === "ImportNamespaceSpecifier" && matches(/^effect\//u)(definition.parent.source.value)))));
|
|
2457
|
+
}
|
|
2458
|
+
function forwardedChain(input) {
|
|
2459
|
+
const chain = callGroups(input.returned);
|
|
2460
|
+
function fixed(node) {
|
|
2461
|
+
return node.type === "Literal" || exists(rootName(node), (root) => !contains$1(input.names, root));
|
|
2462
|
+
}
|
|
2463
|
+
return matchRight(chain.groups, {
|
|
2464
|
+
onEmpty: constFalse,
|
|
2465
|
+
onNonEmpty: (earlier, last) => fixed(chain.callee) && every(flatten(earlier), fixed) && forwarded({
|
|
2466
|
+
args: last,
|
|
2467
|
+
names: input.names
|
|
2468
|
+
}) && (isArrayNonEmpty(earlier) || detachable({
|
|
2469
|
+
context: input.context,
|
|
2470
|
+
node: chain.callee
|
|
2471
|
+
}))
|
|
2472
|
+
});
|
|
2473
|
+
}
|
|
2474
|
+
function exactForwardingFunction(input) {
|
|
2475
|
+
const names = pipe(input.node.params, map$1(parameterName), getSomes);
|
|
2476
|
+
const returned = returnedExpression(input.node);
|
|
2477
|
+
if (input.node.async || input.node.generator || isArrayEmpty(names) || names.length !== input.node.params.length || isNullish$1(returned)) return false;
|
|
2123
2478
|
if (returned.type === "Identifier") return names.length === 1 && returned.name === names[0];
|
|
2124
|
-
return
|
|
2479
|
+
return returned.type === "NewExpression" && returned.callee.type === "Identifier" && forwarded({
|
|
2480
|
+
args: returned.arguments,
|
|
2481
|
+
names
|
|
2482
|
+
}) || forwardedChain({
|
|
2483
|
+
context: input.context,
|
|
2125
2484
|
names,
|
|
2126
|
-
|
|
2485
|
+
returned
|
|
2127
2486
|
});
|
|
2128
2487
|
}
|
|
2129
2488
|
function isIdentity(node) {
|
|
@@ -2131,8 +2490,11 @@ function isIdentity(node) {
|
|
|
2131
2490
|
const returned = returnedExpression(node);
|
|
2132
2491
|
return node.params.length === 1 && parameter?.type === "Identifier" && returned?.type === "Identifier" && returned.name === parameter.name;
|
|
2133
2492
|
}
|
|
2134
|
-
function indirectionMessage(
|
|
2135
|
-
|
|
2493
|
+
function indirectionMessage(input) {
|
|
2494
|
+
if (isIdentity(input.node)) return "Pass identity from effect instead of this function.";
|
|
2495
|
+
const returned = returnedExpression(input.node);
|
|
2496
|
+
if (exactForwardingFunction(input) && returned?.type === "CallExpression") return `Use ${input.context.sourceCode.getText(returned.callee)} directly; this function only passes its parameters on.`;
|
|
2497
|
+
return "Inline this function at its only use site.";
|
|
2136
2498
|
}
|
|
2137
2499
|
function isExport(node) {
|
|
2138
2500
|
return node?.type === "ExportNamedDeclaration" || node?.type === "ExportDefaultDeclaration";
|
|
@@ -2211,38 +2573,56 @@ function singleUseLayerPart(input) {
|
|
|
2211
2573
|
const noTrivialIndirection = defineRule({
|
|
2212
2574
|
create: (context) => ({
|
|
2213
2575
|
ArrowFunctionExpression: (node) => {
|
|
2214
|
-
if ((node.parent.type === "VariableDeclarator" || node.parent.type === "Property") && (exactForwardingFunction(
|
|
2576
|
+
if ((node.parent.type === "VariableDeclarator" || node.parent.type === "Property") && (exactForwardingFunction({
|
|
2577
|
+
context,
|
|
2578
|
+
node
|
|
2579
|
+
}) || singleUseThunk({
|
|
2215
2580
|
context,
|
|
2216
2581
|
node
|
|
2217
2582
|
}) || singleCallerWrapper({
|
|
2218
2583
|
context,
|
|
2219
2584
|
node
|
|
2220
2585
|
}))) context.report({
|
|
2221
|
-
message: indirectionMessage(
|
|
2586
|
+
message: indirectionMessage({
|
|
2587
|
+
context,
|
|
2588
|
+
node
|
|
2589
|
+
}),
|
|
2222
2590
|
node
|
|
2223
2591
|
});
|
|
2224
2592
|
},
|
|
2225
2593
|
FunctionDeclaration: (node) => {
|
|
2226
|
-
if (exactForwardingFunction(
|
|
2594
|
+
if (exactForwardingFunction({
|
|
2595
|
+
context,
|
|
2596
|
+
node
|
|
2597
|
+
}) || singleUseThunk({
|
|
2227
2598
|
context,
|
|
2228
2599
|
node
|
|
2229
2600
|
}) || singleCallerWrapper({
|
|
2230
2601
|
context,
|
|
2231
2602
|
node
|
|
2232
2603
|
})) context.report({
|
|
2233
|
-
message: indirectionMessage(
|
|
2604
|
+
message: indirectionMessage({
|
|
2605
|
+
context,
|
|
2606
|
+
node
|
|
2607
|
+
}),
|
|
2234
2608
|
node
|
|
2235
2609
|
});
|
|
2236
2610
|
},
|
|
2237
2611
|
FunctionExpression: (node) => {
|
|
2238
|
-
if ((node.parent.type === "VariableDeclarator" || node.parent.type === "Property" || node.parent.type === "MethodDefinition" && node.parent.override !== true) && (exactForwardingFunction(
|
|
2612
|
+
if ((node.parent.type === "VariableDeclarator" || node.parent.type === "Property" || node.parent.type === "MethodDefinition" && node.parent.override !== true) && (exactForwardingFunction({
|
|
2613
|
+
context,
|
|
2614
|
+
node
|
|
2615
|
+
}) || singleUseThunk({
|
|
2239
2616
|
context,
|
|
2240
2617
|
node
|
|
2241
2618
|
}) || singleCallerWrapper({
|
|
2242
2619
|
context,
|
|
2243
2620
|
node
|
|
2244
2621
|
}))) context.report({
|
|
2245
|
-
message: indirectionMessage(
|
|
2622
|
+
message: indirectionMessage({
|
|
2623
|
+
context,
|
|
2624
|
+
node
|
|
2625
|
+
}),
|
|
2246
2626
|
node
|
|
2247
2627
|
});
|
|
2248
2628
|
},
|
|
@@ -2341,13 +2721,12 @@ function unknownJsonSchema(input) {
|
|
|
2341
2721
|
node: input.node.arguments[0],
|
|
2342
2722
|
propertyName: "Unknown"
|
|
2343
2723
|
})) return false;
|
|
2344
|
-
|
|
2345
|
-
if (parent.type !== "CallExpression" || parent.arguments[0] !== input.node || parent.callee.type !== "MemberExpression") return false;
|
|
2724
|
+
if (input.node.parent.type !== "CallExpression" || input.node.parent.arguments[0] !== input.node || input.node.parent.callee.type !== "MemberExpression") return false;
|
|
2346
2725
|
return importedMember({
|
|
2347
2726
|
context: input.context,
|
|
2348
2727
|
importedName: "Schema",
|
|
2349
|
-
node: parent.callee
|
|
2350
|
-
}) && pipe(memberName(parent.callee), exists(startsWith("decode")));
|
|
2728
|
+
node: input.node.parent.callee
|
|
2729
|
+
}) && pipe(memberName(input.node.parent.callee), exists(startsWith("decode")));
|
|
2351
2730
|
}
|
|
2352
2731
|
const noUnvalidatedJsonDecode = defineRule({
|
|
2353
2732
|
create: (context) => ({ CallExpression: (node) => {
|
|
@@ -2436,7 +2815,6 @@ function reportSchemaVariable(input) {
|
|
|
2436
2815
|
node: input.variable.init
|
|
2437
2816
|
})) return;
|
|
2438
2817
|
const name = input.variable.id.name;
|
|
2439
|
-
const annotation = input.variable.id.typeAnnotation;
|
|
2440
2818
|
const typeStatement = pipe(input.program.body, findFirstIndex((statement) => statement === input.statement), filter((index) => index > 0), flatMap((index) => get$1(input.program.body, index - 1)));
|
|
2441
2819
|
if (contains$1(input.cycleNames, name)) {
|
|
2442
2820
|
if (!pipe(namedTypeAlias({
|
|
@@ -2449,7 +2827,7 @@ function reportSchemaVariable(input) {
|
|
|
2449
2827
|
message: `Write \`type ${name} = ...\` by hand immediately before this recursive Schema; typeof ${name}.Type is circular.`,
|
|
2450
2828
|
node: input.variable
|
|
2451
2829
|
});
|
|
2452
|
-
if (isNotNullish(
|
|
2830
|
+
if (isNotNullish(input.variable.id.typeAnnotation)) input.context.report({
|
|
2453
2831
|
message: "Annotate the Schema.suspend thunk, not the recursive Schema.",
|
|
2454
2832
|
node: input.variable.id
|
|
2455
2833
|
});
|
|
@@ -2472,9 +2850,9 @@ function reportSchemaVariable(input) {
|
|
|
2472
2850
|
message: "Infer this schema instead of restating Schema.Schema.",
|
|
2473
2851
|
node: input.variable.init
|
|
2474
2852
|
});
|
|
2475
|
-
if (isNotNullish(
|
|
2853
|
+
if (isNotNullish(input.variable.id.typeAnnotation) && schemaSchemaType({
|
|
2476
2854
|
context: input.context,
|
|
2477
|
-
node:
|
|
2855
|
+
node: input.variable.id.typeAnnotation.typeAnnotation
|
|
2478
2856
|
})) input.context.report({
|
|
2479
2857
|
message: "Infer this schema instead of annotating Schema.Schema.",
|
|
2480
2858
|
node: input.variable.id
|
|
@@ -2512,7 +2890,40 @@ const reactLegacyApis = [
|
|
|
2512
2890
|
];
|
|
2513
2891
|
const reactLegacyMessage = "Use React 19 function components with refs as props; React Compiler owns memoization.";
|
|
2514
2892
|
const mapMessage = "Use HashMap, or MutableHashMap for local mutation; for identity keys, wrap objects the code owns in Equal.byReferenceUnsafe, which marks them reference-compared globally.";
|
|
2515
|
-
const
|
|
2893
|
+
const reactHooks = [
|
|
2894
|
+
"use",
|
|
2895
|
+
"useActionState",
|
|
2896
|
+
"useContext",
|
|
2897
|
+
"useDebugValue",
|
|
2898
|
+
"useDeferredValue",
|
|
2899
|
+
"useEffect",
|
|
2900
|
+
"useEffectEvent",
|
|
2901
|
+
"useId",
|
|
2902
|
+
"useImperativeHandle",
|
|
2903
|
+
"useInsertionEffect",
|
|
2904
|
+
"useLayoutEffect",
|
|
2905
|
+
"useOptimistic",
|
|
2906
|
+
"useReducer",
|
|
2907
|
+
"useRef",
|
|
2908
|
+
"useState",
|
|
2909
|
+
"useSyncExternalStore",
|
|
2910
|
+
"useTransition"
|
|
2911
|
+
];
|
|
2912
|
+
const runMessage = "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.";
|
|
2913
|
+
const runners = [
|
|
2914
|
+
"runCallback",
|
|
2915
|
+
"runCallbackWith",
|
|
2916
|
+
"runFork",
|
|
2917
|
+
"runForkWith",
|
|
2918
|
+
"runPromise",
|
|
2919
|
+
"runPromiseExit",
|
|
2920
|
+
"runPromiseExitWith",
|
|
2921
|
+
"runPromiseWith",
|
|
2922
|
+
"runSync",
|
|
2923
|
+
"runSyncExit",
|
|
2924
|
+
"runSyncExitWith",
|
|
2925
|
+
"runSyncWith"
|
|
2926
|
+
];
|
|
2516
2927
|
const setMessage = "Use HashSet, or MutableHashSet for local mutation; for identity keys, wrap objects the code owns in Equal.byReferenceUnsafe, which marks them reference-compared globally.";
|
|
2517
2928
|
const oxlint = defineConfig({
|
|
2518
2929
|
categories: {
|
|
@@ -2611,6 +3022,7 @@ const oxlint = defineConfig({
|
|
|
2611
3022
|
"@deslop/coding-standards/no-error-message-assertion": "error",
|
|
2612
3023
|
"@deslop/coding-standards/no-fake-ref-state": "error",
|
|
2613
3024
|
"@deslop/coding-standards/no-hand-written-guard": "error",
|
|
3025
|
+
"@deslop/coding-standards/no-in-operator": "error",
|
|
2614
3026
|
"@deslop/coding-standards/no-let": "error",
|
|
2615
3027
|
"@deslop/coding-standards/no-multiline-comment": "error",
|
|
2616
3028
|
"@deslop/coding-standards/no-native-control": "error",
|
|
@@ -2618,13 +3030,14 @@ const oxlint = defineConfig({
|
|
|
2618
3030
|
"@deslop/coding-standards/no-native-method-call": "error",
|
|
2619
3031
|
"@deslop/coding-standards/no-nullish-ternary": "error",
|
|
2620
3032
|
"@deslop/coding-standards/no-option-value-access": "error",
|
|
3033
|
+
"@deslop/coding-standards/no-property-alias": "error",
|
|
2621
3034
|
"@deslop/coding-standards/no-readonly-type-syntax": "error",
|
|
2622
|
-
"@deslop/coding-standards/no-redundant-return-type": "error",
|
|
2623
3035
|
"@deslop/coding-standards/no-redundant-state-type": "error",
|
|
2624
3036
|
"@deslop/coding-standards/no-redundant-use-ref-null-type": "error",
|
|
2625
3037
|
"@deslop/coding-standards/no-redundant-variable-annotation": "error",
|
|
2626
3038
|
"@deslop/coding-standards/no-reinvented-schema": "error",
|
|
2627
3039
|
"@deslop/coding-standards/no-renamed-import": "error",
|
|
3040
|
+
"@deslop/coding-standards/no-return-type": "error",
|
|
2628
3041
|
"@deslop/coding-standards/no-single-use-value": "error",
|
|
2629
3042
|
"@deslop/coding-standards/no-stored-schema-operation": "error",
|
|
2630
3043
|
"@deslop/coding-standards/no-switch-statement": "error",
|
|
@@ -2911,7 +3324,6 @@ const oxlint = defineConfig({
|
|
|
2911
3324
|
"no-misleading-character-class": "error",
|
|
2912
3325
|
"no-multi-assign": "error",
|
|
2913
3326
|
"no-negated-condition": "error",
|
|
2914
|
-
"no-nested-ternary": "error",
|
|
2915
3327
|
"no-new": "error",
|
|
2916
3328
|
"no-param-reassign": ["error", { props: true }],
|
|
2917
3329
|
"no-plusplus": "error",
|
|
@@ -2991,31 +3403,11 @@ const oxlint = defineConfig({
|
|
|
2991
3403
|
message: "Use Predicate.hasProperty.",
|
|
2992
3404
|
property: "hasOwnProperty"
|
|
2993
3405
|
},
|
|
2994
|
-
{
|
|
2995
|
-
message: runMessage,
|
|
2996
|
-
object: "Effect",
|
|
2997
|
-
property: "runFork"
|
|
2998
|
-
},
|
|
2999
|
-
{
|
|
3000
|
-
message: runMessage,
|
|
3001
|
-
object: "Effect",
|
|
3002
|
-
property: "runPromise"
|
|
3003
|
-
},
|
|
3004
|
-
{
|
|
3406
|
+
...map$1(runners, (property) => ({
|
|
3005
3407
|
message: runMessage,
|
|
3006
3408
|
object: "Effect",
|
|
3007
|
-
property
|
|
3008
|
-
},
|
|
3009
|
-
{
|
|
3010
|
-
message: runMessage,
|
|
3011
|
-
object: "Effect",
|
|
3012
|
-
property: "runSync"
|
|
3013
|
-
},
|
|
3014
|
-
{
|
|
3015
|
-
message: runMessage,
|
|
3016
|
-
object: "Effect",
|
|
3017
|
-
property: "runSyncExit"
|
|
3018
|
-
},
|
|
3409
|
+
property
|
|
3410
|
+
})),
|
|
3019
3411
|
{
|
|
3020
3412
|
message: runMessage,
|
|
3021
3413
|
object: "ManagedRuntime",
|
|
@@ -3026,6 +3418,11 @@ const oxlint = defineConfig({
|
|
|
3026
3418
|
object: "Math",
|
|
3027
3419
|
property: "max"
|
|
3028
3420
|
},
|
|
3421
|
+
{
|
|
3422
|
+
message: "Use Option.getOrUndefined; undefined marks an absent value.",
|
|
3423
|
+
object: "Option",
|
|
3424
|
+
property: "getOrNull"
|
|
3425
|
+
},
|
|
3029
3426
|
{
|
|
3030
3427
|
message: "Use Number.min.",
|
|
3031
3428
|
object: "Math",
|
|
@@ -3041,6 +3438,11 @@ const oxlint = defineConfig({
|
|
|
3041
3438
|
object: "React",
|
|
3042
3439
|
property
|
|
3043
3440
|
})),
|
|
3441
|
+
...map$1(reactHooks, (property) => ({
|
|
3442
|
+
message: `Import ${property} by name from react.`,
|
|
3443
|
+
object: "React",
|
|
3444
|
+
property
|
|
3445
|
+
})),
|
|
3044
3446
|
{
|
|
3045
3447
|
message: "Use Schema.Struct.",
|
|
3046
3448
|
object: "Schema",
|
|
@@ -3140,6 +3542,7 @@ const oxlint = defineConfig({
|
|
|
3140
3542
|
"String"
|
|
3141
3543
|
] }],
|
|
3142
3544
|
"no-sparse-arrays": "error",
|
|
3545
|
+
"no-ternary": "error",
|
|
3143
3546
|
"no-throw-literal": "error",
|
|
3144
3547
|
"no-unmodified-loop-condition": "error",
|
|
3145
3548
|
"no-unneeded-ternary": "error",
|
|
@@ -3316,6 +3719,7 @@ var oxlint_default = definePlugin({
|
|
|
3316
3719
|
"no-error-message-assertion": noErrorMessageAssertion,
|
|
3317
3720
|
"no-fake-ref-state": noFakeRefState,
|
|
3318
3721
|
"no-hand-written-guard": noHandWrittenGuard,
|
|
3722
|
+
"no-in-operator": noInOperator,
|
|
3319
3723
|
"no-let": noLet,
|
|
3320
3724
|
"no-multiline-comment": noMultilineComment,
|
|
3321
3725
|
"no-native-control": noNativeControl,
|
|
@@ -3323,13 +3727,14 @@ var oxlint_default = definePlugin({
|
|
|
3323
3727
|
"no-native-method-call": noNativeMethodCall,
|
|
3324
3728
|
"no-nullish-ternary": noNullishTernary,
|
|
3325
3729
|
"no-option-value-access": noOptionValueAccess,
|
|
3730
|
+
"no-property-alias": noPropertyAlias,
|
|
3326
3731
|
"no-readonly-type-syntax": noReadonlyTypeSyntax,
|
|
3327
|
-
"no-redundant-return-type": noRedundantReturnType,
|
|
3328
3732
|
"no-redundant-state-type": noRedundantStateType,
|
|
3329
3733
|
"no-redundant-use-ref-null-type": noRedundantUseRefNullType,
|
|
3330
3734
|
"no-redundant-variable-annotation": noRedundantVariableAnnotation,
|
|
3331
3735
|
"no-reinvented-schema": noReinventedSchema,
|
|
3332
3736
|
"no-renamed-import": noRenamedImport,
|
|
3737
|
+
"no-return-type": noReturnType,
|
|
3333
3738
|
"no-single-use-value": noSingleUseValue,
|
|
3334
3739
|
"no-stored-schema-operation": noStoredSchemaOperation,
|
|
3335
3740
|
"no-switch-statement": noSwitchStatement,
|