@deslop/coding-standards 0.1.1083 → 0.1.1085
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{MutableList-Cpkiw3Cl.mjs → MutableList-NTBI6AHW.mjs} +58 -1
- package/dist/install.js +2 -2
- package/dist/oxlint.d.ts +10 -0
- package/dist/oxlint.js +445 -137
- package/package.json +1 -1
- package/skills/design/SKILL.md +2 -2
- package/skills/engineering/SKILL.md +92 -77
package/dist/oxlint.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { $ as last, $r as pipeArguments, A as dedupe, Ar as isNullish$1, At as filter, Cr as isBoolean, D as appendAll, Dr as isNotNull, Dt as all, E as append, F as every, Fr as isReadonlyObject, Ft as fromUndefinedOr, G as get$1, Gt as some, H as flatten, Ht as match, I as filter$1, Ir as isRegExp, It as getOrElse, J as isArrayEmpty, K as getSomes, Kr as dual, Kt as toArray$1, L as filterMap, Lr as isString, Lt as getOrUndefined, M as dropRight, Mt as flatMap, N as empty, Nt as flatten$1, O as contains, Or as isNotNullish, Ot as contains$1, P as ensure, Pt as fromNullishOr, Q as join, Qn as NodeInspectSymbol, R as findFirst, Rt as isNone, U as forEach, Ut as none, V as flatMap$1, Vr as constFalse, Vt as map, Wr as constVoid, Wt as orElse, Xr as pipe, Y as isArrayNonEmpty, Z as isReadonlyArrayNonEmpty, _ as startsWith, _r as string, at as prepend, br as symbol$1, c as make$3, ct as sort, d as Order, dr as symbol, dt as fromEntries, f as includes, ft as get$2, g as split, gr as optimize, gt as values, h as replaceAll, hr as number, ht as remove, it as of, j as dedupeWith, jr as isNumber, jt as firstSomeOf, k as containsWith, kt as exists, l as update, lr as byReferenceUnsafe, lt as take, m as replace, mr as hash, o as toArray, on as strictEqual, p as match$1, pr as combine, q as head, qt as zipWith, r as make$2, rt as matchRight, s as get$3, st as some$1, t as append$1, tt as map$1, u as match$2, ur as equals, ut as takeWhile, v as toUpperCase, w as TaggedError, x as parse, xr as hasProperty, y as trim, yt as fromOption, z as findFirstIndex, zt as isSome } from "./MutableList-NTBI6AHW.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";
|
|
@@ -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,6 +943,94 @@ 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
1036
|
return pipe(token, split(":"), last, getOrElse(() => token), replace(/^!|!$/gu, ""));
|
|
@@ -931,7 +1041,7 @@ function amount(token, pattern) {
|
|
|
931
1041
|
function violation(token) {
|
|
932
1042
|
if (exists(amount(token, /^duration-(\d+)$/u), (value) => value > 300)) return some("Keep a transition under 300 ms: duration-300 or less.");
|
|
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) {
|
|
@@ -954,7 +1064,7 @@ const motionLimits = defineRule({
|
|
|
954
1064
|
//#endregion
|
|
955
1065
|
//#region src/rules/no-array-wrap-ternary.ts
|
|
956
1066
|
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();
|
|
1067
|
+
if (node.type !== "CallExpression" || node.callee.type !== "MemberExpression" || node.callee.object.type !== "Identifier" || node.callee.object.name !== "Array" || !contains$1(memberName(node.callee), "isArray") || node.arguments.length !== 1) return none();
|
|
958
1068
|
const argument = node.arguments[0];
|
|
959
1069
|
if (argument?.type !== "Identifier" && argument?.type !== "MemberExpression") return none();
|
|
960
1070
|
return some(argument);
|
|
@@ -1105,7 +1215,7 @@ function isPipeCall(context, node) {
|
|
|
1105
1215
|
});
|
|
1106
1216
|
}
|
|
1107
1217
|
function enclosingPipes(context, node) {
|
|
1108
|
-
if (node.type === "Program" || contains
|
|
1218
|
+
if (node.type === "Program" || contains([
|
|
1109
1219
|
"ArrowFunctionExpression",
|
|
1110
1220
|
"FunctionDeclaration",
|
|
1111
1221
|
"FunctionExpression"
|
|
@@ -1126,7 +1236,7 @@ const noDeepPipe = defineRule({
|
|
|
1126
1236
|
//#endregion
|
|
1127
1237
|
//#region src/rules/no-destructured-parameter.ts
|
|
1128
1238
|
function namedTuple(input) {
|
|
1129
|
-
if (contains
|
|
1239
|
+
if (contains(input.seen, input.node)) return false;
|
|
1130
1240
|
if (input.node.type === "TSTupleType") return isArrayNonEmpty(input.node.elementTypes) && every(input.node.elementTypes, (element) => element.type === "TSNamedTupleMember");
|
|
1131
1241
|
if (input.node.type === "TSParenthesizedType" || input.node.type === "TSTypeOperator" && input.node.operator === "readonly") return namedTuple({
|
|
1132
1242
|
...input,
|
|
@@ -1200,6 +1310,9 @@ const noDestructuredParameter = defineRule({
|
|
|
1200
1310
|
});
|
|
1201
1311
|
//#endregion
|
|
1202
1312
|
//#region src/rules/no-discarded-failure.ts
|
|
1313
|
+
function logged(node) {
|
|
1314
|
+
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))));
|
|
1315
|
+
}
|
|
1203
1316
|
const noDiscardedFailure = defineRule({
|
|
1204
1317
|
create: (context) => ({ MemberExpression: (node) => {
|
|
1205
1318
|
if (!importedMember({
|
|
@@ -1207,13 +1320,14 @@ const noDiscardedFailure = defineRule({
|
|
|
1207
1320
|
importedName: "Effect",
|
|
1208
1321
|
node
|
|
1209
1322
|
})) return;
|
|
1210
|
-
if (!exists(memberName(node), (name) => contains
|
|
1323
|
+
if (!exists(memberName(node), (name) => contains([
|
|
1211
1324
|
"ignore",
|
|
1212
1325
|
"orDie",
|
|
1213
1326
|
"orDieWith"
|
|
1214
1327
|
], name))) return;
|
|
1328
|
+
if (contains$1(memberName(node), "ignore") && logged(node)) return;
|
|
1215
1329
|
context.report({
|
|
1216
|
-
message: "Map the failure to the service's error; an expected failure is never
|
|
1330
|
+
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
1331
|
node
|
|
1218
1332
|
});
|
|
1219
1333
|
} }),
|
|
@@ -1221,12 +1335,7 @@ const noDiscardedFailure = defineRule({
|
|
|
1221
1335
|
});
|
|
1222
1336
|
//#endregion
|
|
1223
1337
|
//#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 = {
|
|
1338
|
+
const VARIANTS = {
|
|
1230
1339
|
"&&": {
|
|
1231
1340
|
comparison: "!==",
|
|
1232
1341
|
message: "Use Predicate.isNotNullish."
|
|
@@ -1236,6 +1345,11 @@ const variants = {
|
|
|
1236
1345
|
message: "Use Predicate.isNullish."
|
|
1237
1346
|
}
|
|
1238
1347
|
};
|
|
1348
|
+
function nullishOperand(node) {
|
|
1349
|
+
if (node.type === "Literal" && node.value === null) return some("null");
|
|
1350
|
+
if (node.type === "Identifier" && node.name === "undefined") return some("undefined");
|
|
1351
|
+
return none();
|
|
1352
|
+
}
|
|
1239
1353
|
function operands(input) {
|
|
1240
1354
|
if (input.node.type !== "LogicalExpression" || input.node.operator !== input.operator) return [input.node];
|
|
1241
1355
|
return appendAll(operands({
|
|
@@ -1258,7 +1372,7 @@ const noDoubleNullishCheck = defineRule({
|
|
|
1258
1372
|
create: (context) => ({ LogicalExpression: (node) => {
|
|
1259
1373
|
if (node.operator === "??") return;
|
|
1260
1374
|
if (node.parent.type === "LogicalExpression" && node.parent.operator === node.operator) return;
|
|
1261
|
-
const variant =
|
|
1375
|
+
const variant = VARIANTS[node.operator];
|
|
1262
1376
|
const checks = pipe(operands({
|
|
1263
1377
|
node,
|
|
1264
1378
|
operator: node.operator
|
|
@@ -1312,7 +1426,7 @@ function calleeOf(node) {
|
|
|
1312
1426
|
}
|
|
1313
1427
|
function isEffectEmpty(input) {
|
|
1314
1428
|
const member = calleeOf(input.node);
|
|
1315
|
-
return member.type === "MemberExpression" && member.object.type === "Identifier" && contains(memberName(member), "empty") && isImportBinding({
|
|
1429
|
+
return member.type === "MemberExpression" && member.object.type === "Identifier" && contains$1(memberName(member), "empty") && isImportBinding({
|
|
1316
1430
|
context: input.context,
|
|
1317
1431
|
node: member.object,
|
|
1318
1432
|
source: "effect"
|
|
@@ -1386,13 +1500,13 @@ function receiverName(node) {
|
|
|
1386
1500
|
return memberName(node);
|
|
1387
1501
|
}
|
|
1388
1502
|
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));
|
|
1503
|
+
return node?.type === "MemberExpression" && contains$1(memberName(node), "message") && exists(receiverName(node.object), matches(/^(?:e|err|error|failure|cause|exception)$|Error$/iu));
|
|
1390
1504
|
}
|
|
1391
1505
|
function expectsErrorMessage(node) {
|
|
1392
1506
|
return node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "expect" && isErrorMessage(node.arguments[0]);
|
|
1393
1507
|
}
|
|
1394
1508
|
function expectsMessage(node) {
|
|
1395
|
-
if (node.object.type === "MemberExpression" && contains(memberName(node.object), "not")) return expectsErrorMessage(node.object.object);
|
|
1509
|
+
if (node.object.type === "MemberExpression" && contains$1(memberName(node.object), "not")) return expectsErrorMessage(node.object.object);
|
|
1396
1510
|
return expectsErrorMessage(node.object);
|
|
1397
1511
|
}
|
|
1398
1512
|
function assertsMessage(node) {
|
|
@@ -1408,7 +1522,7 @@ const noErrorMessageAssertion = defineRule({
|
|
|
1408
1522
|
});
|
|
1409
1523
|
return;
|
|
1410
1524
|
}
|
|
1411
|
-
if (node.callee.type === "MemberExpression" && (node.arguments[0]?.type === "Literal" || node.arguments[0]?.type === "TemplateLiteral") && (exists(memberName(node.callee), (name) => contains
|
|
1525
|
+
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({
|
|
1412
1526
|
message: "Assert the failure, never its message wording.",
|
|
1413
1527
|
node
|
|
1414
1528
|
});
|
|
@@ -1434,7 +1548,7 @@ function hasUnusedSetter(input) {
|
|
|
1434
1548
|
}
|
|
1435
1549
|
function isFakeRefState(input) {
|
|
1436
1550
|
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);
|
|
1551
|
+
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
1552
|
}
|
|
1439
1553
|
const noFakeRefState = defineRule({
|
|
1440
1554
|
create: (context) => ({ CallExpression: (node) => {
|
|
@@ -1449,8 +1563,31 @@ const noFakeRefState = defineRule({
|
|
|
1449
1563
|
meta: { type: "problem" }
|
|
1450
1564
|
});
|
|
1451
1565
|
//#endregion
|
|
1566
|
+
//#region src/rules/no-floating-derived-type.ts
|
|
1567
|
+
function derived(node) {
|
|
1568
|
+
if (node.type === "TSIndexedAccessType") return derived(node.objectType);
|
|
1569
|
+
return node.type === "TSTypeReference" && node.typeName.type === "Identifier" && contains([
|
|
1570
|
+
"Awaited",
|
|
1571
|
+
"ConstructorParameters",
|
|
1572
|
+
"InstanceType",
|
|
1573
|
+
"Parameters",
|
|
1574
|
+
"ReturnType"
|
|
1575
|
+
], node.typeName.name);
|
|
1576
|
+
}
|
|
1577
|
+
const noFloatingDerivedType = defineRule({
|
|
1578
|
+
create: (context) => ({ TSTypeAliasDeclaration: (node) => {
|
|
1579
|
+
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));
|
|
1580
|
+
if (node.parent.type === "ExportNamedDeclaration" || listed || !derived(node.typeAnnotation)) return;
|
|
1581
|
+
context.report({
|
|
1582
|
+
message: "Import the library's own type, or pass the value straight to the call that types it.",
|
|
1583
|
+
node
|
|
1584
|
+
});
|
|
1585
|
+
} }),
|
|
1586
|
+
meta: { type: "problem" }
|
|
1587
|
+
});
|
|
1588
|
+
//#endregion
|
|
1452
1589
|
//#region src/rules/no-gradient.ts
|
|
1453
|
-
const
|
|
1590
|
+
const GRADIENT = /(?:^|[\s:])bg-(?:linear|radial|conic|gradient)(?:-|$|\s)|gradient\(/u;
|
|
1454
1591
|
function report$4(context, node) {
|
|
1455
1592
|
context.report({
|
|
1456
1593
|
message: "Use a flat theme color; gradients are never part of the design.",
|
|
@@ -1460,10 +1597,10 @@ function report$4(context, node) {
|
|
|
1460
1597
|
const noGradient = defineRule({
|
|
1461
1598
|
create: (context) => ({
|
|
1462
1599
|
Literal: (node) => {
|
|
1463
|
-
if (isString(node.value) &&
|
|
1600
|
+
if (isString(node.value) && GRADIENT.test(node.value)) report$4(context, node);
|
|
1464
1601
|
},
|
|
1465
1602
|
TemplateElement: (node) => {
|
|
1466
|
-
if (
|
|
1603
|
+
if (GRADIENT.test(node.value.raw)) report$4(context, node);
|
|
1467
1604
|
}
|
|
1468
1605
|
}),
|
|
1469
1606
|
meta: { type: "problem" }
|
|
@@ -1497,7 +1634,7 @@ function check(input) {
|
|
|
1497
1634
|
if (input.node.type === "UnaryExpression" && input.node.operator === "!" && input.node.argument.type === "CallExpression" && input.node.argument.callee.type === "MemberExpression" && isSubject({
|
|
1498
1635
|
name: "Array",
|
|
1499
1636
|
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({
|
|
1637
|
+
}) && contains$1(memberName(input.node.argument.callee), "isArray") && input.node.argument.arguments.length === 1 && every(input.node.argument.arguments, (argument) => isSubject({
|
|
1501
1638
|
name: input.name,
|
|
1502
1639
|
node: argument
|
|
1503
1640
|
}))) return some(["!Array.isArray"]);
|
|
@@ -1525,7 +1662,7 @@ function report$3(input) {
|
|
|
1525
1662
|
const predicate = input.node.returnType?.typeAnnotation;
|
|
1526
1663
|
const returned = returnedExpression(input.node);
|
|
1527
1664
|
if (predicate?.type !== "TSTypePredicate" || predicate.asserts || predicate.parameterName.type !== "Identifier" || isNullish$1(returned)) return;
|
|
1528
|
-
|
|
1665
|
+
map(pipe(check({
|
|
1529
1666
|
name: predicate.parameterName.name,
|
|
1530
1667
|
node: returned
|
|
1531
1668
|
}), map((checks) => pipe(checks, sort(Order), join(" && "))), flatMap((key) => get$2({
|
|
@@ -1550,8 +1687,7 @@ function report$3(input) {
|
|
|
1550
1687
|
"typeof === string": "isString",
|
|
1551
1688
|
"typeof === symbol": "isSymbol",
|
|
1552
1689
|
"typeof === undefined": "isUndefined"
|
|
1553
|
-
}, key)))
|
|
1554
|
-
map(name, (guard) => {
|
|
1690
|
+
}, key))), (guard) => {
|
|
1555
1691
|
input.context.report({
|
|
1556
1692
|
message: `Use Predicate.${guard} instead of this hand-written type guard.`,
|
|
1557
1693
|
node: input.node
|
|
@@ -1606,13 +1742,12 @@ const noLet = defineRule({
|
|
|
1606
1742
|
});
|
|
1607
1743
|
//#endregion
|
|
1608
1744
|
//#region src/rules/no-multiline-comment.ts
|
|
1609
|
-
const directive = /^\s*(?:eslint|oxlint|@effect-diagnostics|@ts-|\/\s*<reference|#region|#endregion)/u;
|
|
1610
1745
|
function adjacentLines(pair) {
|
|
1611
1746
|
return pair.first?.type === "Line" && pair.second?.type === "Line" && pair.second.loc.start.line === pair.first.loc.end.line + 1;
|
|
1612
1747
|
}
|
|
1613
1748
|
const noMultilineComment = defineRule({
|
|
1614
1749
|
create: (context) => ({ Program: () => {
|
|
1615
|
-
const comments = pipe(context.sourceCode.getAllComments(), filter$1((comment) => !
|
|
1750
|
+
const comments = pipe(context.sourceCode.getAllComments(), filter$1((comment) => !matches(/^\s*(?:eslint|oxlint|@effect-diagnostics|@ts-|\/\s*<reference|#region|#endregion)/u)(comment.value)));
|
|
1616
1751
|
pipe(comments, filter$1((comment, index) => adjacentLines({
|
|
1617
1752
|
first: comment,
|
|
1618
1753
|
second: comments[index + 1]
|
|
@@ -1628,13 +1763,13 @@ const noMultilineComment = defineRule({
|
|
|
1628
1763
|
});
|
|
1629
1764
|
//#endregion
|
|
1630
1765
|
//#region src/rules/no-native-control.ts
|
|
1631
|
-
const
|
|
1766
|
+
const PRIMITIVES = {
|
|
1632
1767
|
button: "Button",
|
|
1633
1768
|
input: "Input",
|
|
1634
1769
|
select: "Select",
|
|
1635
1770
|
textarea: "Textarea"
|
|
1636
1771
|
};
|
|
1637
|
-
const
|
|
1772
|
+
const CONTROL_STYLE = /(?:^|:)(?:hover|focus|focus-visible|active):|(?:^|:)(?:rounded|shadow|ring)(?:-|$)|(?:^|:)p[blrtxy]?-(?!0$)|(?:^|:)bg-(?!transparent$)|(?:^|:)border(?:-[blrtxy])?$|(?:^|:)border-(?!0$|[blrtxy]-0$)/u;
|
|
1638
1773
|
function classStrings(node) {
|
|
1639
1774
|
if (node.type === "Literal") return filter$1([node.value], isString);
|
|
1640
1775
|
if (node.type === "TemplateLiteral") return map$1(node.quasis, (quasi) => quasi.value.raw);
|
|
@@ -1649,11 +1784,11 @@ const noNativeControl = defineRule({
|
|
|
1649
1784
|
create: (context) => ({ JSXOpeningElement: (node) => {
|
|
1650
1785
|
if (node.name.type !== "JSXIdentifier") return;
|
|
1651
1786
|
const name = node.name.name;
|
|
1652
|
-
map(get$2(
|
|
1787
|
+
map(get$2(PRIMITIVES, name), (primitive) => {
|
|
1653
1788
|
const styled = pipe(node.attributes, findFirst((attribute) => attribute.type === "JSXAttribute" && attribute.name.type === "JSXIdentifier" && attribute.name.name === "className"), map((attribute) => {
|
|
1654
1789
|
if (attribute.type !== "JSXAttribute" || attribute.value === null) return empty();
|
|
1655
1790
|
return classStrings(attribute.value);
|
|
1656
|
-
}), exists((values) => some$1(values, (value) => some$1(split(value, /\s+/u), matches(
|
|
1791
|
+
}), exists((values) => some$1(values, (value) => some$1(split(value, /\s+/u), matches(CONTROL_STYLE)))));
|
|
1657
1792
|
if (name !== "select" && !styled) return;
|
|
1658
1793
|
context.report({
|
|
1659
1794
|
message: `Use the ${primitive} UI primitive instead of a styled native control.`,
|
|
@@ -1666,7 +1801,7 @@ const noNativeControl = defineRule({
|
|
|
1666
1801
|
//#endregion
|
|
1667
1802
|
//#region src/rules/no-native-emptiness-check.ts
|
|
1668
1803
|
function emptinessPredicate(node) {
|
|
1669
|
-
const comparesLength = node.left.type === "MemberExpression" && contains(memberName(node.left), "length") && node.right.type === "Literal" && node.right.value === 0;
|
|
1804
|
+
const comparesLength = node.left.type === "MemberExpression" && contains$1(memberName(node.left), "length") && node.right.type === "Literal" && node.right.value === 0;
|
|
1670
1805
|
if (comparesLength && node.operator === "===") return some("Array.isArrayEmpty, Array.isReadonlyArrayEmpty, or String.isEmpty");
|
|
1671
1806
|
if (comparesLength && node.operator === ">") return some("Array.isArrayNonEmpty, Array.isReadonlyArrayNonEmpty, or String.isNonEmpty");
|
|
1672
1807
|
return none();
|
|
@@ -1684,7 +1819,7 @@ const noNativeEmptinessCheck = defineRule({
|
|
|
1684
1819
|
});
|
|
1685
1820
|
//#endregion
|
|
1686
1821
|
//#region src/rules/no-native-method-call.ts
|
|
1687
|
-
const
|
|
1822
|
+
const NATIVE_METHODS = [
|
|
1688
1823
|
"at",
|
|
1689
1824
|
"catch",
|
|
1690
1825
|
"charAt",
|
|
@@ -1755,11 +1890,6 @@ function freshRegExpFlags(input) {
|
|
|
1755
1890
|
if (flags.type !== "Literal" || !isString(flags.value)) return some(none());
|
|
1756
1891
|
return some(some(flags.value));
|
|
1757
1892
|
}
|
|
1758
|
-
const nativeGlobals = [
|
|
1759
|
-
"Object",
|
|
1760
|
-
"Promise",
|
|
1761
|
-
"RegExp"
|
|
1762
|
-
];
|
|
1763
1893
|
function memberRoot(node) {
|
|
1764
1894
|
if (node.type === "MemberExpression") return memberRoot(node.object);
|
|
1765
1895
|
return node;
|
|
@@ -1784,7 +1914,11 @@ function ownsMethod(input) {
|
|
|
1784
1914
|
if (root.type !== "Identifier") return false;
|
|
1785
1915
|
if (root.name === "path") return !throughCall;
|
|
1786
1916
|
return pipe(variableFor(input.context, root), filter((variable) => isReadonlyArrayNonEmpty(variable.defs)), match({
|
|
1787
|
-
onNone: () => !throughCall && matches(/^[A-Z]/u)(root.name) && !contains
|
|
1917
|
+
onNone: () => !throughCall && matches(/^[A-Z]/u)(root.name) && !contains([
|
|
1918
|
+
"Object",
|
|
1919
|
+
"Promise",
|
|
1920
|
+
"RegExp"
|
|
1921
|
+
], root.name),
|
|
1788
1922
|
onSome: (variable) => some$1(variable.defs, (definition) => {
|
|
1789
1923
|
if (definition.node.type === "VariableDeclarator") return !throughCall && isYieldedService(definition.node.init);
|
|
1790
1924
|
if (definition.type !== "ImportBinding" || definition.parent?.type !== "ImportDeclaration") return false;
|
|
@@ -1810,7 +1944,7 @@ const noNativeMethodCall = defineRule({
|
|
|
1810
1944
|
});
|
|
1811
1945
|
return;
|
|
1812
1946
|
}
|
|
1813
|
-
pipe(memberName(node.callee), filter((name) => contains
|
|
1947
|
+
pipe(memberName(node.callee), filter((name) => contains(NATIVE_METHODS, name)), filter((name) => !contains([
|
|
1814
1948
|
"entries",
|
|
1815
1949
|
"keys",
|
|
1816
1950
|
"values"
|
|
@@ -1823,14 +1957,6 @@ const noNativeMethodCall = defineRule({
|
|
|
1823
1957
|
});
|
|
1824
1958
|
//#endregion
|
|
1825
1959
|
//#region src/rules/no-nullish-ternary.ts
|
|
1826
|
-
const nullishPredicates = [
|
|
1827
|
-
"isNotNull",
|
|
1828
|
-
"isNotNullish",
|
|
1829
|
-
"isNotUndefined",
|
|
1830
|
-
"isNull",
|
|
1831
|
-
"isNullish",
|
|
1832
|
-
"isUndefined"
|
|
1833
|
-
];
|
|
1834
1960
|
function isNullish(input) {
|
|
1835
1961
|
if (input.node.type === "Literal") return input.node.value === null;
|
|
1836
1962
|
return input.node.type === "Identifier" && input.node.name === "undefined";
|
|
@@ -1851,7 +1977,14 @@ function testsNullish(input) {
|
|
|
1851
1977
|
context: input.context,
|
|
1852
1978
|
importedName: "Predicate",
|
|
1853
1979
|
node: input.node.callee
|
|
1854
|
-
}) && exists(memberName(input.node.callee), (name) => contains
|
|
1980
|
+
}) && exists(memberName(input.node.callee), (name) => contains([
|
|
1981
|
+
"isNotNull",
|
|
1982
|
+
"isNotNullish",
|
|
1983
|
+
"isNotUndefined",
|
|
1984
|
+
"isNull",
|
|
1985
|
+
"isNullish",
|
|
1986
|
+
"isUndefined"
|
|
1987
|
+
], name));
|
|
1855
1988
|
}
|
|
1856
1989
|
const noNullishTernary = defineRule({
|
|
1857
1990
|
create: (context) => ({ ConditionalExpression: (node) => {
|
|
@@ -1883,7 +2016,7 @@ function checks(input) {
|
|
|
1883
2016
|
}) && input.test.arguments[0] !== void 0 && input.context.sourceCode.getText(input.test.arguments[0]) === input.subject;
|
|
1884
2017
|
}
|
|
1885
2018
|
function exits(node) {
|
|
1886
|
-
return contains
|
|
2019
|
+
return contains([
|
|
1887
2020
|
"ContinueStatement",
|
|
1888
2021
|
"ReturnStatement",
|
|
1889
2022
|
"ThrowStatement"
|
|
@@ -1920,7 +2053,7 @@ function guarded(input) {
|
|
|
1920
2053
|
}
|
|
1921
2054
|
const noOptionValueAccess = defineRule({
|
|
1922
2055
|
create: (context) => ({ MemberExpression: (node) => {
|
|
1923
|
-
if (!contains(memberName(node), "value")) return;
|
|
2056
|
+
if (!contains$1(memberName(node), "value")) return;
|
|
1924
2057
|
if (guarded({
|
|
1925
2058
|
context,
|
|
1926
2059
|
node,
|
|
@@ -1934,26 +2067,14 @@ const noOptionValueAccess = defineRule({
|
|
|
1934
2067
|
});
|
|
1935
2068
|
//#endregion
|
|
1936
2069
|
//#region src/rules/no-property-alias.ts
|
|
1937
|
-
const
|
|
2070
|
+
const FUNCTIONS = [
|
|
1938
2071
|
"ArrowFunctionExpression",
|
|
1939
2072
|
"FunctionDeclaration",
|
|
1940
2073
|
"FunctionExpression"
|
|
1941
2074
|
];
|
|
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
2075
|
function insideFunction(node) {
|
|
1955
2076
|
if (isNullish$1(node.parent) || node.parent.type === "Program") return false;
|
|
1956
|
-
if (contains
|
|
2077
|
+
if (contains(FUNCTIONS, node.parent.type)) return true;
|
|
1957
2078
|
return insideFunction(node.parent);
|
|
1958
2079
|
}
|
|
1959
2080
|
function sameRoot(input) {
|
|
@@ -1965,9 +2086,58 @@ function sameRoot(input) {
|
|
|
1965
2086
|
scope: input.context.sourceCode.getScope(input.read)
|
|
1966
2087
|
}));
|
|
1967
2088
|
}
|
|
2089
|
+
function pathTop(node) {
|
|
2090
|
+
if (node.parent?.type === "MemberExpression" && node.parent.object === node && !node.parent.computed) return pathTop(node.parent);
|
|
2091
|
+
return node;
|
|
2092
|
+
}
|
|
2093
|
+
function tested(node) {
|
|
2094
|
+
if (isNullish$1(node.parent)) return false;
|
|
2095
|
+
if (node.parent.type === "LogicalExpression") return node.parent.left === node || tested(node.parent);
|
|
2096
|
+
if (contains([
|
|
2097
|
+
"ChainExpression",
|
|
2098
|
+
"TSAsExpression",
|
|
2099
|
+
"TSNonNullExpression"
|
|
2100
|
+
], node.parent.type)) return tested(node.parent);
|
|
2101
|
+
return node.parent.type === "BinaryExpression" && contains([
|
|
2102
|
+
"!=",
|
|
2103
|
+
"!==",
|
|
2104
|
+
"==",
|
|
2105
|
+
"===",
|
|
2106
|
+
"in",
|
|
2107
|
+
"instanceof"
|
|
2108
|
+
], 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));
|
|
2109
|
+
}
|
|
2110
|
+
function predicate(callee) {
|
|
2111
|
+
if (callee.type === "CallExpression") return predicate(callee.callee);
|
|
2112
|
+
if (callee.type === "Identifier") return matches(/^(?:has|is)(?:[A-Z]|$)/u)(callee.name);
|
|
2113
|
+
return callee.type === "MemberExpression" && callee.property.type === "Identifier" && matches(/^(?:has|is)(?:[A-Z]|$)/u)(callee.property.name);
|
|
2114
|
+
}
|
|
2115
|
+
function deferred(input) {
|
|
2116
|
+
if (isNullish$1(input.node.parent) || input.node.parent === input.block) return false;
|
|
2117
|
+
const parent = input.node.parent;
|
|
2118
|
+
if (contains(FUNCTIONS, parent.type) && !(parent.parent?.type === "CallExpression" && some$1(parent.parent.arguments, (argument) => argument === parent))) return true;
|
|
2119
|
+
return deferred({
|
|
2120
|
+
...input,
|
|
2121
|
+
node: parent
|
|
2122
|
+
});
|
|
2123
|
+
}
|
|
2124
|
+
function needsAlias(input) {
|
|
2125
|
+
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;
|
|
2126
|
+
const block = input.declaration.parent;
|
|
2127
|
+
const paths = map$1(input.reads, (read) => ({
|
|
2128
|
+
callback: enclosed({
|
|
2129
|
+
block,
|
|
2130
|
+
node: read,
|
|
2131
|
+
types: FUNCTIONS
|
|
2132
|
+
}),
|
|
2133
|
+
node: pathTop(read),
|
|
2134
|
+
text: input.context.sourceCode.getText(pathTop(read))
|
|
2135
|
+
}));
|
|
2136
|
+
return some$1(paths, (checked) => tested(checked.node) && some$1(paths, (read) => read.callback && (read.text === checked.text || startsWith(`${checked.text}.`)(read.text))));
|
|
2137
|
+
}
|
|
1968
2138
|
function enclosed(input) {
|
|
1969
2139
|
if (isNullish$1(input.node.parent) || input.node.parent === input.block) return false;
|
|
1970
|
-
if (contains
|
|
2140
|
+
if (contains(input.types, input.node.parent.type)) return true;
|
|
1971
2141
|
return enclosed({
|
|
1972
2142
|
...input,
|
|
1973
2143
|
node: input.node.parent
|
|
@@ -1986,24 +2156,6 @@ function written(node) {
|
|
|
1986
2156
|
if (node.type === "UpdateExpression" || node.type === "UnaryExpression" && node.operator === "delete") return objectRoot(node.argument);
|
|
1987
2157
|
return none();
|
|
1988
2158
|
}
|
|
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
2159
|
function replacement(input) {
|
|
2008
2160
|
if (input.read.parent?.type === "Property" && input.read.parent.shorthand) return `${input.name}: ${input.text}`;
|
|
2009
2161
|
return input.text;
|
|
@@ -2013,15 +2165,22 @@ function report$2(input) {
|
|
|
2013
2165
|
const text = input.context.sourceCode.getText(input.init);
|
|
2014
2166
|
pipe(rootName(input.init), filter((root) => isArrayNonEmpty(reads) && every(reads, (read) => {
|
|
2015
2167
|
const used = outermost(read);
|
|
2016
|
-
return read.parent.type !== "ExportSpecifier" && !
|
|
2168
|
+
return read.parent.type !== "ExportSpecifier" && (!needsAlias({
|
|
2169
|
+
context: input.context,
|
|
2170
|
+
declaration: input.declaration,
|
|
2171
|
+
reads
|
|
2172
|
+
}) || !enclosed({
|
|
2017
2173
|
block: input.declaration.parent,
|
|
2018
2174
|
node: read,
|
|
2019
|
-
types:
|
|
2175
|
+
types: FUNCTIONS
|
|
2176
|
+
})) && !deferred({
|
|
2177
|
+
block: input.declaration.parent,
|
|
2178
|
+
node: read
|
|
2020
2179
|
}) && !(used.parent?.type === "CallExpression" && used.parent.callee === used) && !(used.parent?.type === "TaggedTemplateExpression" && used.parent.tag === used) && !some$1(input.writes, (write) => between({
|
|
2021
2180
|
declaration: input.declaration,
|
|
2022
2181
|
node: write,
|
|
2023
2182
|
read
|
|
2024
|
-
}) && contains(written(write), root)) && sameRoot({
|
|
2183
|
+
}) && contains$1(written(write), root)) && sameRoot({
|
|
2025
2184
|
context: input.context,
|
|
2026
2185
|
declarator: input.node,
|
|
2027
2186
|
name: root,
|
|
@@ -2034,7 +2193,19 @@ function report$2(input) {
|
|
|
2034
2193
|
if (reads.length !== 1 || some$1(reads, (read) => startsWith("JSX")(read.parent.type) || enclosed({
|
|
2035
2194
|
block: input.declaration.parent,
|
|
2036
2195
|
node: read,
|
|
2037
|
-
types:
|
|
2196
|
+
types: [
|
|
2197
|
+
...FUNCTIONS,
|
|
2198
|
+
"ChainExpression",
|
|
2199
|
+
"ConditionalExpression",
|
|
2200
|
+
"DoWhileStatement",
|
|
2201
|
+
"ForInStatement",
|
|
2202
|
+
"ForOfStatement",
|
|
2203
|
+
"ForStatement",
|
|
2204
|
+
"IfStatement",
|
|
2205
|
+
"LogicalExpression",
|
|
2206
|
+
"SwitchStatement",
|
|
2207
|
+
"WhileStatement"
|
|
2208
|
+
]
|
|
2038
2209
|
}) || some$1(input.statements, (node) => between({
|
|
2039
2210
|
declaration: input.declaration,
|
|
2040
2211
|
node,
|
|
@@ -2080,6 +2251,19 @@ const noPropertyAlias = defineRule({
|
|
|
2080
2251
|
},
|
|
2081
2252
|
UpdateExpression: write,
|
|
2082
2253
|
VariableDeclarator: (node) => {
|
|
2254
|
+
if (node.parent.type === "VariableDeclaration" && node.parent.parent.type === "ExportNamedDeclaration" && node.id.type === "Identifier" && node.init?.type === "MemberExpression") {
|
|
2255
|
+
const init = node.init;
|
|
2256
|
+
pipe(rootName(init), flatMap((root) => variableFromScope({
|
|
2257
|
+
name: root,
|
|
2258
|
+
scope: context.sourceCode.getScope(node)
|
|
2259
|
+
})), filter((variable) => every(variable.defs, (definition) => definition.type === "Variable")), match({
|
|
2260
|
+
onNone: constVoid,
|
|
2261
|
+
onSome: (variable) => context.report({
|
|
2262
|
+
message: `Export ${variable.name} and read ${context.sourceCode.getText(init)} where it is used.`,
|
|
2263
|
+
node
|
|
2264
|
+
})
|
|
2265
|
+
}));
|
|
2266
|
+
}
|
|
2083
2267
|
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
2268
|
declaration: node.parent,
|
|
2085
2269
|
init: node.init,
|
|
@@ -2103,7 +2287,7 @@ function isTopLevelStatement(node) {
|
|
|
2103
2287
|
}
|
|
2104
2288
|
function insideCycleType(input) {
|
|
2105
2289
|
if (input.node.type === "Program") return false;
|
|
2106
|
-
if (input.node.type === "TSTypeAliasDeclaration" && isTopLevelStatement(input.node)) return contains
|
|
2290
|
+
if (input.node.type === "TSTypeAliasDeclaration" && isTopLevelStatement(input.node)) return contains(input.cycleNames, input.node.id.name);
|
|
2107
2291
|
return insideCycleType({
|
|
2108
2292
|
cycleNames: input.cycleNames,
|
|
2109
2293
|
node: input.node.parent
|
|
@@ -2144,7 +2328,7 @@ function shapeLiterals(input) {
|
|
|
2144
2328
|
return pipe(typeDeclarations({
|
|
2145
2329
|
context: input.context,
|
|
2146
2330
|
name: input.type.typeName
|
|
2147
|
-
}), filter$1((declaration) => declaration.type === "TSTypeAliasDeclaration"), filter$1((alias) => !contains
|
|
2331
|
+
}), filter$1((declaration) => declaration.type === "TSTypeAliasDeclaration"), filter$1((alias) => !contains(input.seen, alias)), flatMap$1((alias) => shapeLiterals({
|
|
2148
2332
|
...input,
|
|
2149
2333
|
seen: append(input.seen, alias),
|
|
2150
2334
|
type: alias.typeAnnotation
|
|
@@ -2292,7 +2476,7 @@ function isReactUseRef(input) {
|
|
|
2292
2476
|
node: input.node.callee,
|
|
2293
2477
|
source: "react"
|
|
2294
2478
|
});
|
|
2295
|
-
return input.node.callee.type === "MemberExpression" && contains(memberName(input.node.callee), "useRef") && input.node.callee.object.type === "Identifier" && isNamespaceImport({
|
|
2479
|
+
return input.node.callee.type === "MemberExpression" && contains$1(memberName(input.node.callee), "useRef") && input.node.callee.object.type === "Identifier" && isNamespaceImport({
|
|
2296
2480
|
context: input.context,
|
|
2297
2481
|
node: input.node.callee.object,
|
|
2298
2482
|
source: "react"
|
|
@@ -2367,7 +2551,7 @@ const shipped = [
|
|
|
2367
2551
|
schema: "Natural"
|
|
2368
2552
|
}
|
|
2369
2553
|
];
|
|
2370
|
-
function schemaMember(input) {
|
|
2554
|
+
function schemaMember$1(input) {
|
|
2371
2555
|
if (input.node.type !== "MemberExpression" || !importedMember({
|
|
2372
2556
|
context: input.context,
|
|
2373
2557
|
importedName: "Schema",
|
|
@@ -2377,16 +2561,16 @@ function schemaMember(input) {
|
|
|
2377
2561
|
}
|
|
2378
2562
|
function shippedSchema(input) {
|
|
2379
2563
|
if (input.filter.type !== "CallExpression") return none();
|
|
2380
|
-
const base = schemaMember({
|
|
2564
|
+
const base = schemaMember$1({
|
|
2381
2565
|
context: input.context,
|
|
2382
2566
|
node: input.base
|
|
2383
2567
|
});
|
|
2384
|
-
const check = schemaMember({
|
|
2568
|
+
const check = schemaMember$1({
|
|
2385
2569
|
context: input.context,
|
|
2386
2570
|
node: input.filter.callee
|
|
2387
2571
|
});
|
|
2388
2572
|
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), {
|
|
2573
|
+
return pipe(shipped, findFirst((entry) => contains$1(base, entry.base) && contains$1(check, entry.check) && isArrayEmpty(rest) && match(fromUndefinedOr(entry.only), {
|
|
2390
2574
|
onNone: () => value === void 0,
|
|
2391
2575
|
onSome: (only) => value?.type === "Literal" && value.value === only
|
|
2392
2576
|
})), map((entry) => entry.schema));
|
|
@@ -2397,14 +2581,14 @@ function pipedBase(node) {
|
|
|
2397
2581
|
if (node.parent.arguments[1] !== node) return none();
|
|
2398
2582
|
return fromNullishOr(node.parent.arguments[0]);
|
|
2399
2583
|
}
|
|
2400
|
-
if (node.parent.callee.type !== "MemberExpression" || !contains(memberName(node.parent.callee), "pipe") || node.parent.arguments.length !== 1) return none();
|
|
2584
|
+
if (node.parent.callee.type !== "MemberExpression" || !contains$1(memberName(node.parent.callee), "pipe") || node.parent.arguments.length !== 1) return none();
|
|
2401
2585
|
return some(node.parent.callee.object);
|
|
2402
2586
|
}
|
|
2403
2587
|
function reinventedSchema(input) {
|
|
2404
|
-
if (input.node.callee.type !== "MemberExpression" || !contains(memberName(input.node.callee), "check")) return none();
|
|
2588
|
+
if (input.node.callee.type !== "MemberExpression" || !contains$1(memberName(input.node.callee), "check")) return none();
|
|
2405
2589
|
const callee = input.node.callee;
|
|
2406
2590
|
const [filter] = input.node.arguments;
|
|
2407
|
-
if (isSome(schemaMember({
|
|
2591
|
+
if (isSome(schemaMember$1({
|
|
2408
2592
|
context: input.context,
|
|
2409
2593
|
node: callee
|
|
2410
2594
|
}))) {
|
|
@@ -2524,7 +2708,7 @@ const noReturnType = defineRule({
|
|
|
2524
2708
|
});
|
|
2525
2709
|
//#endregion
|
|
2526
2710
|
//#region src/rules/no-single-use-value.ts
|
|
2527
|
-
const
|
|
2711
|
+
const REPEATING = [
|
|
2528
2712
|
"ArrowFunctionExpression",
|
|
2529
2713
|
"DoWhileStatement",
|
|
2530
2714
|
"ForInStatement",
|
|
@@ -2539,7 +2723,7 @@ function runsOnce(input) {
|
|
|
2539
2723
|
...input,
|
|
2540
2724
|
node: input.node
|
|
2541
2725
|
});
|
|
2542
|
-
if (contains
|
|
2726
|
+
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
2727
|
return climb({
|
|
2544
2728
|
...input,
|
|
2545
2729
|
node: input.node
|
|
@@ -2563,7 +2747,7 @@ function insideConditional(input) {
|
|
|
2563
2747
|
});
|
|
2564
2748
|
}
|
|
2565
2749
|
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
|
|
2750
|
+
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(["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));
|
|
2567
2751
|
}
|
|
2568
2752
|
function unwrapped(node) {
|
|
2569
2753
|
return pipe(typeWrapped(node), match({
|
|
@@ -2581,10 +2765,61 @@ function inlined(input) {
|
|
|
2581
2765
|
if (input.read.range[0] === input.next.range[0]) return `;${value}`;
|
|
2582
2766
|
return value;
|
|
2583
2767
|
}
|
|
2768
|
+
function executes(node) {
|
|
2769
|
+
if (contains([
|
|
2770
|
+
"AwaitExpression",
|
|
2771
|
+
"CallExpression",
|
|
2772
|
+
"MemberExpression",
|
|
2773
|
+
"NewExpression",
|
|
2774
|
+
"SpreadElement",
|
|
2775
|
+
"TaggedTemplateExpression",
|
|
2776
|
+
"YieldExpression"
|
|
2777
|
+
], node.type)) return true;
|
|
2778
|
+
if (contains([
|
|
2779
|
+
"ArrowFunctionExpression",
|
|
2780
|
+
"FunctionDeclaration",
|
|
2781
|
+
"FunctionExpression"
|
|
2782
|
+
], node.type)) return false;
|
|
2783
|
+
return some$1(childNodes(node), executes);
|
|
2784
|
+
}
|
|
2785
|
+
function suspends(node) {
|
|
2786
|
+
if (node.type === "YieldExpression" || node.type === "AwaitExpression") return true;
|
|
2787
|
+
if (contains([
|
|
2788
|
+
"ArrowFunctionExpression",
|
|
2789
|
+
"FunctionDeclaration",
|
|
2790
|
+
"FunctionExpression"
|
|
2791
|
+
], node.type)) return false;
|
|
2792
|
+
return some$1(childNodes(node), suspends);
|
|
2793
|
+
}
|
|
2794
|
+
function surface(node) {
|
|
2795
|
+
if (contains([
|
|
2796
|
+
"ExpressionStatement",
|
|
2797
|
+
"ReturnStatement",
|
|
2798
|
+
"VariableDeclaration"
|
|
2799
|
+
], node.type) || node.type === "ArrowFunctionExpression" && node.expression) return true;
|
|
2800
|
+
if (isNullish$1(node.parent) || !contains([
|
|
2801
|
+
"AwaitExpression",
|
|
2802
|
+
"JSXAttribute",
|
|
2803
|
+
"JSXElement",
|
|
2804
|
+
"JSXExpressionContainer",
|
|
2805
|
+
"JSXFragment",
|
|
2806
|
+
"JSXOpeningElement",
|
|
2807
|
+
"VariableDeclarator",
|
|
2808
|
+
"YieldExpression"
|
|
2809
|
+
], node.type)) return false;
|
|
2810
|
+
return surface(node.parent);
|
|
2811
|
+
}
|
|
2812
|
+
function readable(input) {
|
|
2813
|
+
if (!pipe(input.context.sourceCode.getText(input.init), includes("\n"))) return true;
|
|
2814
|
+
const slot = outermost(input.read);
|
|
2815
|
+
if (isNullish$1(slot.parent) || suspends(input.init)) return false;
|
|
2816
|
+
if (slot.parent.type === "JSXExpressionContainer" || slot.parent.type === "ReturnStatement") return true;
|
|
2817
|
+
return isNotNullish(slot.parent.parent) && (slot.parent.type === "CallExpression" && slot.parent.callee !== slot || slot.parent.type === "ArrayExpression") && surface(slot.parent.parent);
|
|
2818
|
+
}
|
|
2584
2819
|
function report$1(input) {
|
|
2585
2820
|
const declaration = input.node.parent;
|
|
2586
2821
|
const init = input.node.init;
|
|
2587
|
-
if (declaration.type !== "VariableDeclaration" || declaration.parent.type !== "BlockStatement" || input.node.id.type !== "Identifier" || init === null
|
|
2822
|
+
if (declaration.type !== "VariableDeclaration" || declaration.parent.type !== "BlockStatement" || input.node.id.type !== "Identifier" || init === null) return;
|
|
2588
2823
|
const statements = declaration.parent.body;
|
|
2589
2824
|
const name = input.node.id.name;
|
|
2590
2825
|
pipe(findFirstIndex(statements, (statement) => statement === declaration), flatMap((index) => get$1(statements, index + 1)), flatMap((next) => pipe(variableFromScope({
|
|
@@ -2593,6 +2828,10 @@ function report$1(input) {
|
|
|
2593
2828
|
}), 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
2829
|
node: read,
|
|
2595
2830
|
statement: next
|
|
2831
|
+
}) && readable({
|
|
2832
|
+
context: input.context,
|
|
2833
|
+
init,
|
|
2834
|
+
read
|
|
2596
2835
|
}) && !(init.type === "ConditionalExpression" && insideConditional({
|
|
2597
2836
|
node: read,
|
|
2598
2837
|
statement: next
|
|
@@ -2606,7 +2845,7 @@ function report$1(input) {
|
|
|
2606
2845
|
if (!fixable({
|
|
2607
2846
|
init,
|
|
2608
2847
|
read: found.read
|
|
2609
|
-
})) return [];
|
|
2848
|
+
}) || executes(init) && !every(input.context.sourceCode.getTokensBetween(declaration, found.read), inert)) return [];
|
|
2610
2849
|
return [fixer.remove(declaration), fixer.replaceText(found.read, inlined({
|
|
2611
2850
|
context: input.context,
|
|
2612
2851
|
init,
|
|
@@ -2652,6 +2891,22 @@ const noSingleUseValue = defineRule({
|
|
|
2652
2891
|
}
|
|
2653
2892
|
});
|
|
2654
2893
|
//#endregion
|
|
2894
|
+
//#region src/rules/no-stored-jsx.ts
|
|
2895
|
+
function element(node) {
|
|
2896
|
+
if (node.type === "JSXElement" || node.type === "JSXFragment") return true;
|
|
2897
|
+
return pipe(typeWrapped(node), exists(element));
|
|
2898
|
+
}
|
|
2899
|
+
const noStoredJsx = defineRule({
|
|
2900
|
+
create: (context) => ({ VariableDeclarator: (node) => {
|
|
2901
|
+
if (node.init === null || !element(node.init)) return;
|
|
2902
|
+
context.report({
|
|
2903
|
+
message: "Write the element where it renders, or make it a component when several places render it.",
|
|
2904
|
+
node
|
|
2905
|
+
});
|
|
2906
|
+
} }),
|
|
2907
|
+
meta: { type: "problem" }
|
|
2908
|
+
});
|
|
2909
|
+
//#endregion
|
|
2655
2910
|
//#region src/rules/no-stored-schema-operation.ts
|
|
2656
2911
|
function storedSchemaOperation(node) {
|
|
2657
2912
|
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;
|
|
@@ -2715,7 +2970,7 @@ function detachable(input) {
|
|
|
2715
2970
|
function forwardedChain(input) {
|
|
2716
2971
|
const chain = callGroups(input.returned);
|
|
2717
2972
|
function fixed(node) {
|
|
2718
|
-
return node.type === "Literal" || exists(rootName(node), (root) => !contains
|
|
2973
|
+
return node.type === "Literal" || exists(rootName(node), (root) => !contains(input.names, root));
|
|
2719
2974
|
}
|
|
2720
2975
|
return matchRight(chain.groups, {
|
|
2721
2976
|
onEmpty: constFalse,
|
|
@@ -2789,7 +3044,7 @@ function exportedAlias(input) {
|
|
|
2789
3044
|
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
3045
|
}
|
|
2791
3046
|
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;
|
|
3047
|
+
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
3048
|
return pipe(variableFromScope({
|
|
2794
3049
|
name: input.node.id.name,
|
|
2795
3050
|
scope: input.context.sourceCode.getScope(input.node)
|
|
@@ -2804,7 +3059,7 @@ function singleUseLayerPart(input) {
|
|
|
2804
3059
|
}), map((variable) => filter$1(variable.references, (reference) => reference.isRead())), exists((reads) => {
|
|
2805
3060
|
const consumer = reads[0]?.identifier.parent;
|
|
2806
3061
|
if (reads.length !== 1 || consumer?.type !== "CallExpression") return false;
|
|
2807
|
-
if (init.type === "MemberExpression") return contains(memberName(init), "layer") && (importedMember({
|
|
3062
|
+
if (init.type === "MemberExpression") return contains$1(memberName(init), "layer") && (importedMember({
|
|
2808
3063
|
context: input.context,
|
|
2809
3064
|
importedName: "Layer",
|
|
2810
3065
|
node: consumer.callee
|
|
@@ -2917,6 +3172,45 @@ const noTrivialIndirection = defineRule({
|
|
|
2917
3172
|
meta: { type: "problem" }
|
|
2918
3173
|
});
|
|
2919
3174
|
//#endregion
|
|
3175
|
+
//#region src/rules/no-type-from-value.ts
|
|
3176
|
+
function schemaMember(name) {
|
|
3177
|
+
return name.type === "TSQualifiedName" && contains([
|
|
3178
|
+
"Encoded",
|
|
3179
|
+
"Type",
|
|
3180
|
+
"cases",
|
|
3181
|
+
"fields",
|
|
3182
|
+
"literals",
|
|
3183
|
+
"members"
|
|
3184
|
+
], name.right.name);
|
|
3185
|
+
}
|
|
3186
|
+
function valueQuery(node) {
|
|
3187
|
+
if (node.type === "TSParenthesizedType") return valueQuery(node.typeAnnotation);
|
|
3188
|
+
if (node.type === "TSIndexedAccessType") return valueQuery(node.objectType);
|
|
3189
|
+
return node.type === "TSTypeQuery" && (node.exprName.type === "Identifier" || node.exprName.type === "TSQualifiedName" && !schemaMember(node.exprName));
|
|
3190
|
+
}
|
|
3191
|
+
function nestedObject(node) {
|
|
3192
|
+
return node.parent?.type === "TSIndexedAccessType" && node.parent.objectType === node;
|
|
3193
|
+
}
|
|
3194
|
+
const noTypeFromValue = defineRule({
|
|
3195
|
+
create: (context) => {
|
|
3196
|
+
function report(node) {
|
|
3197
|
+
context.report({
|
|
3198
|
+
message: "Declare the closed set as a schema, such as Schema.Literals, and read its type with typeof X.Type.",
|
|
3199
|
+
node
|
|
3200
|
+
});
|
|
3201
|
+
}
|
|
3202
|
+
return {
|
|
3203
|
+
TSIndexedAccessType: (node) => {
|
|
3204
|
+
if (!nestedObject(node) && valueQuery(node.objectType)) report(node);
|
|
3205
|
+
},
|
|
3206
|
+
TSTypeOperator: (node) => {
|
|
3207
|
+
if (node.operator === "keyof" && !nestedObject(node) && valueQuery(node.typeAnnotation)) report(node);
|
|
3208
|
+
}
|
|
3209
|
+
};
|
|
3210
|
+
},
|
|
3211
|
+
meta: { type: "problem" }
|
|
3212
|
+
});
|
|
3213
|
+
//#endregion
|
|
2920
3214
|
//#region src/rules/no-typeof.ts
|
|
2921
3215
|
const noTypeof = defineRule({
|
|
2922
3216
|
create: (context) => ({ UnaryExpression: (node) => {
|
|
@@ -3001,7 +3295,7 @@ const noUnvalidatedJsonDecode = defineRule({
|
|
|
3001
3295
|
//#region src/rules/preserve-error-cause.ts
|
|
3002
3296
|
function constructedFields(node) {
|
|
3003
3297
|
if (node?.type !== "CallExpression" && node?.type !== "NewExpression") return none();
|
|
3004
|
-
const constructs = node.type === "NewExpression" || node.callee.type === "MemberExpression" && contains(memberName(node.callee), "make");
|
|
3298
|
+
const constructs = node.type === "NewExpression" || node.callee.type === "MemberExpression" && contains$1(memberName(node.callee), "make");
|
|
3005
3299
|
const fields = node.arguments[0];
|
|
3006
3300
|
if (!constructs || fields?.type !== "ObjectExpression") return none();
|
|
3007
3301
|
return some(fields);
|
|
@@ -3024,7 +3318,7 @@ const preserveErrorCause = defineRule({
|
|
|
3024
3318
|
node: node.callee
|
|
3025
3319
|
})) return;
|
|
3026
3320
|
const name = memberName(node.callee);
|
|
3027
|
-
if (contains(name, "mapError")) {
|
|
3321
|
+
if (contains$1(name, "mapError")) {
|
|
3028
3322
|
forEach(filter$1(node.arguments, dropsCause), (callback) => report(context, callback));
|
|
3029
3323
|
return;
|
|
3030
3324
|
}
|
|
@@ -3085,7 +3379,7 @@ function isSchemaDefinition(input) {
|
|
|
3085
3379
|
context: input.context,
|
|
3086
3380
|
importedName: "Schema",
|
|
3087
3381
|
node: input.node.callee
|
|
3088
|
-
}) && contains(memberName(input.node.callee), "make")) return false;
|
|
3382
|
+
}) && contains$1(memberName(input.node.callee), "make")) return false;
|
|
3089
3383
|
if (!expressionUsesImport({
|
|
3090
3384
|
context: input.context,
|
|
3091
3385
|
importedName: "Schema",
|
|
@@ -3126,7 +3420,7 @@ function exportLists(program) {
|
|
|
3126
3420
|
};
|
|
3127
3421
|
}
|
|
3128
3422
|
function isExported(input) {
|
|
3129
|
-
return input.statement.type === "ExportNamedDeclaration" || contains
|
|
3423
|
+
return input.statement.type === "ExportNamedDeclaration" || contains(input.exports.values, input.name) || input.typeOnly && contains(input.exports.types, input.name);
|
|
3130
3424
|
}
|
|
3131
3425
|
function reportSchemaVariable(input) {
|
|
3132
3426
|
if (input.variable.id.type !== "Identifier" || !matches(/^[A-Z]/u)(input.variable.id.name)) return;
|
|
@@ -3136,7 +3430,7 @@ function reportSchemaVariable(input) {
|
|
|
3136
3430
|
})) return;
|
|
3137
3431
|
const name = input.variable.id.name;
|
|
3138
3432
|
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
|
|
3433
|
+
if (contains(input.cycleNames, name)) {
|
|
3140
3434
|
if (!pipe(namedTypeAlias({
|
|
3141
3435
|
name,
|
|
3142
3436
|
typeStatement
|
|
@@ -3215,7 +3509,7 @@ const schemaTypePair = defineRule({
|
|
|
3215
3509
|
});
|
|
3216
3510
|
//#endregion
|
|
3217
3511
|
//#region src/oxlint.ts
|
|
3218
|
-
const
|
|
3512
|
+
const REACT_LEGACY_APIS = [
|
|
3219
3513
|
"Component",
|
|
3220
3514
|
"PureComponent",
|
|
3221
3515
|
"createRef",
|
|
@@ -3224,9 +3518,9 @@ const reactLegacyApis = [
|
|
|
3224
3518
|
"useCallback",
|
|
3225
3519
|
"useMemo"
|
|
3226
3520
|
];
|
|
3227
|
-
const
|
|
3228
|
-
const
|
|
3229
|
-
const
|
|
3521
|
+
const REACT_LEGACY_MESSAGE = "Use React 19 function components with refs as props; React Compiler owns memoization.";
|
|
3522
|
+
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.";
|
|
3523
|
+
const REACT_HOOKS = [
|
|
3230
3524
|
"use",
|
|
3231
3525
|
"useActionState",
|
|
3232
3526
|
"useContext",
|
|
@@ -3245,8 +3539,8 @@ const reactHooks = [
|
|
|
3245
3539
|
"useSyncExternalStore",
|
|
3246
3540
|
"useTransition"
|
|
3247
3541
|
];
|
|
3248
|
-
const
|
|
3249
|
-
const
|
|
3542
|
+
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.";
|
|
3543
|
+
const RUNNERS = [
|
|
3250
3544
|
"runCallback",
|
|
3251
3545
|
"runCallbackWith",
|
|
3252
3546
|
"runFork",
|
|
@@ -3260,7 +3554,7 @@ const runners = [
|
|
|
3260
3554
|
"runSyncExitWith",
|
|
3261
3555
|
"runSyncWith"
|
|
3262
3556
|
];
|
|
3263
|
-
const
|
|
3557
|
+
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
3558
|
const oxlint = defineConfig({
|
|
3265
3559
|
categories: {
|
|
3266
3560
|
correctness: "off",
|
|
@@ -3356,6 +3650,7 @@ const oxlint = defineConfig({
|
|
|
3356
3650
|
],
|
|
3357
3651
|
rules: {
|
|
3358
3652
|
"@deslop/coding-standards/explicit-effect-concurrency": "error",
|
|
3653
|
+
"@deslop/coding-standards/module-constant": "error",
|
|
3359
3654
|
"@deslop/coding-standards/motion-limits": "error",
|
|
3360
3655
|
"@deslop/coding-standards/no-array-wrap-ternary": "error",
|
|
3361
3656
|
"@deslop/coding-standards/no-barrel-module": "error",
|
|
@@ -3370,6 +3665,7 @@ const oxlint = defineConfig({
|
|
|
3370
3665
|
"@deslop/coding-standards/no-empty-recovery": "error",
|
|
3371
3666
|
"@deslop/coding-standards/no-error-message-assertion": "error",
|
|
3372
3667
|
"@deslop/coding-standards/no-fake-ref-state": "error",
|
|
3668
|
+
"@deslop/coding-standards/no-floating-derived-type": "error",
|
|
3373
3669
|
"@deslop/coding-standards/no-gradient": "error",
|
|
3374
3670
|
"@deslop/coding-standards/no-hand-written-guard": "error",
|
|
3375
3671
|
"@deslop/coding-standards/no-in-operator": "error",
|
|
@@ -3389,9 +3685,11 @@ const oxlint = defineConfig({
|
|
|
3389
3685
|
"@deslop/coding-standards/no-renamed-import": "error",
|
|
3390
3686
|
"@deslop/coding-standards/no-return-type": "error",
|
|
3391
3687
|
"@deslop/coding-standards/no-single-use-value": "error",
|
|
3688
|
+
"@deslop/coding-standards/no-stored-jsx": "error",
|
|
3392
3689
|
"@deslop/coding-standards/no-stored-schema-operation": "error",
|
|
3393
3690
|
"@deslop/coding-standards/no-switch-statement": "error",
|
|
3394
3691
|
"@deslop/coding-standards/no-trivial-indirection": "error",
|
|
3692
|
+
"@deslop/coding-standards/no-type-from-value": "error",
|
|
3395
3693
|
"@deslop/coding-standards/no-typeof": "error",
|
|
3396
3694
|
"@deslop/coding-standards/no-undestructured-use-state": "error",
|
|
3397
3695
|
"@deslop/coding-standards/no-unexplained-disable": "error",
|
|
@@ -3591,8 +3889,8 @@ const oxlint = defineConfig({
|
|
|
3591
3889
|
"no-restricted-imports": ["error", {
|
|
3592
3890
|
paths: [
|
|
3593
3891
|
{
|
|
3594
|
-
importNames:
|
|
3595
|
-
message:
|
|
3892
|
+
importNames: REACT_LEGACY_APIS,
|
|
3893
|
+
message: REACT_LEGACY_MESSAGE,
|
|
3596
3894
|
name: "react"
|
|
3597
3895
|
},
|
|
3598
3896
|
{
|
|
@@ -3701,7 +3999,7 @@ const oxlint = defineConfig({
|
|
|
3701
3999
|
name: "Error"
|
|
3702
4000
|
},
|
|
3703
4001
|
{
|
|
3704
|
-
message:
|
|
4002
|
+
message: MAP_MESSAGE,
|
|
3705
4003
|
name: "Map"
|
|
3706
4004
|
},
|
|
3707
4005
|
{
|
|
@@ -3729,7 +4027,7 @@ const oxlint = defineConfig({
|
|
|
3729
4027
|
name: "Reflect"
|
|
3730
4028
|
},
|
|
3731
4029
|
{
|
|
3732
|
-
message:
|
|
4030
|
+
message: SET_MESSAGE,
|
|
3733
4031
|
name: "Set"
|
|
3734
4032
|
},
|
|
3735
4033
|
{
|
|
@@ -3756,13 +4054,13 @@ const oxlint = defineConfig({
|
|
|
3756
4054
|
message: "Use Predicate.hasProperty.",
|
|
3757
4055
|
property: "hasOwnProperty"
|
|
3758
4056
|
},
|
|
3759
|
-
...map$1(
|
|
3760
|
-
message:
|
|
4057
|
+
...map$1(RUNNERS, (property) => ({
|
|
4058
|
+
message: RUN_MESSAGE,
|
|
3761
4059
|
object: "Effect",
|
|
3762
4060
|
property
|
|
3763
4061
|
})),
|
|
3764
4062
|
{
|
|
3765
|
-
message:
|
|
4063
|
+
message: RUN_MESSAGE,
|
|
3766
4064
|
object: "ManagedRuntime",
|
|
3767
4065
|
property: "make"
|
|
3768
4066
|
},
|
|
@@ -3786,12 +4084,12 @@ const oxlint = defineConfig({
|
|
|
3786
4084
|
object: "Math",
|
|
3787
4085
|
property: "round"
|
|
3788
4086
|
},
|
|
3789
|
-
...map$1(
|
|
3790
|
-
message:
|
|
4087
|
+
...map$1(REACT_LEGACY_APIS, (property) => ({
|
|
4088
|
+
message: REACT_LEGACY_MESSAGE,
|
|
3791
4089
|
object: "React",
|
|
3792
4090
|
property
|
|
3793
4091
|
})),
|
|
3794
|
-
...map$1(
|
|
4092
|
+
...map$1(REACT_HOOKS, (property) => ({
|
|
3795
4093
|
message: `Import ${property} by name from react.`,
|
|
3796
4094
|
object: "React",
|
|
3797
4095
|
property
|
|
@@ -3908,6 +4206,12 @@ const oxlint = defineConfig({
|
|
|
3908
4206
|
ignoreRestSiblings: true,
|
|
3909
4207
|
reportUsedIgnorePattern: true
|
|
3910
4208
|
}],
|
|
4209
|
+
"no-use-before-define": ["error", {
|
|
4210
|
+
classes: false,
|
|
4211
|
+
functions: false,
|
|
4212
|
+
typedefs: false,
|
|
4213
|
+
variables: true
|
|
4214
|
+
}],
|
|
3911
4215
|
"no-useless-assignment": "error",
|
|
3912
4216
|
"no-useless-backreference": "error",
|
|
3913
4217
|
"no-useless-call": "error",
|
|
@@ -4044,12 +4348,12 @@ const oxlint = defineConfig({
|
|
|
4044
4348
|
Date: "Use DateTime.",
|
|
4045
4349
|
Error: "Use Schema.TaggedError.",
|
|
4046
4350
|
Iterable: "Use T[].",
|
|
4047
|
-
Map:
|
|
4351
|
+
Map: MAP_MESSAGE,
|
|
4048
4352
|
Promise: "Use Effect.",
|
|
4049
4353
|
ReadonlyArray: "Use T[].",
|
|
4050
|
-
ReadonlyMap:
|
|
4051
|
-
ReadonlySet:
|
|
4052
|
-
Set:
|
|
4354
|
+
ReadonlyMap: MAP_MESSAGE,
|
|
4355
|
+
ReadonlySet: SET_MESSAGE,
|
|
4356
|
+
Set: SET_MESSAGE,
|
|
4053
4357
|
undefined: "Use an optional property or parameter, or let inference supply undefined."
|
|
4054
4358
|
} }],
|
|
4055
4359
|
"typescript/no-unnecessary-type-assertion": "error",
|
|
@@ -4084,6 +4388,7 @@ var oxlint_default = definePlugin({
|
|
|
4084
4388
|
meta: { name: "@deslop/coding-standards" },
|
|
4085
4389
|
rules: {
|
|
4086
4390
|
"explicit-effect-concurrency": explicitEffectConcurrency,
|
|
4391
|
+
"module-constant": moduleConstant,
|
|
4087
4392
|
"motion-limits": motionLimits,
|
|
4088
4393
|
"no-array-wrap-ternary": noArrayWrapTernary,
|
|
4089
4394
|
"no-barrel-module": noBarrelModule,
|
|
@@ -4098,6 +4403,7 @@ var oxlint_default = definePlugin({
|
|
|
4098
4403
|
"no-empty-recovery": noEmptyRecovery,
|
|
4099
4404
|
"no-error-message-assertion": noErrorMessageAssertion,
|
|
4100
4405
|
"no-fake-ref-state": noFakeRefState,
|
|
4406
|
+
"no-floating-derived-type": noFloatingDerivedType,
|
|
4101
4407
|
"no-gradient": noGradient,
|
|
4102
4408
|
"no-hand-written-guard": noHandWrittenGuard,
|
|
4103
4409
|
"no-in-operator": noInOperator,
|
|
@@ -4117,9 +4423,11 @@ var oxlint_default = definePlugin({
|
|
|
4117
4423
|
"no-renamed-import": noRenamedImport,
|
|
4118
4424
|
"no-return-type": noReturnType,
|
|
4119
4425
|
"no-single-use-value": noSingleUseValue,
|
|
4426
|
+
"no-stored-jsx": noStoredJsx,
|
|
4120
4427
|
"no-stored-schema-operation": noStoredSchemaOperation,
|
|
4121
4428
|
"no-switch-statement": noSwitchStatement,
|
|
4122
4429
|
"no-trivial-indirection": noTrivialIndirection,
|
|
4430
|
+
"no-type-from-value": noTypeFromValue,
|
|
4123
4431
|
"no-typeof": noTypeof,
|
|
4124
4432
|
"no-undestructured-use-state": noUndestructuredUseState,
|
|
4125
4433
|
"no-unexplained-disable": noUnexplainedDisable,
|