adding monkeytype
Some checks failed
Mark Stale PRs / stale (push) Has been cancelled

This commit is contained in:
Benjamin Falch
2026-04-23 13:53:44 +02:00
parent e214a2fd35
commit 2bc741fb78
1930 changed files with 7590652 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
{
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
"categories": {
"correctness": "error",
"suspicious": "error",
"perf": "error",
},
"plugins": ["typescript", "unicorn", "oxc", "import", "node", "promise"],
"extends": [
"./rules/enabled.jsonc",
"./rules/ts-enabled.jsonc",
"./rules/disabled.jsonc",
"./rules/ts-disabled.jsonc",
"./rules/consider.jsonc",
"./rules/ts-consider.jsonc",
"./rules/jsx.jsonc",
"./overrides.jsonc",
],
}

View File

@@ -0,0 +1,95 @@
{
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
"overrides": [
{
"files": ["*.js", "*.cjs"],
"rules": {
"typescript/strict-boolean-expressions": "off",
"typescript/await-thenable": "off",
"typescript/no-array-delete": "off",
"typescript/no-base-to-string": "off",
"typescript/no-confusing-void-expression": "off",
"typescript/no-duplicate-type-constituents": "off",
"typescript/no-floating-promises": "off",
"typescript/no-for-in-array": "off",
"typescript/no-implied-eval": "off",
"typescript/no-meaningless-void-operator": "off",
"typescript/no-misused-promises": "off",
"typescript/no-misused-spread": "off",
"typescript/no-mixed-enums": "off",
"typescript/no-redundant-type-constituents": "off",
"typescript/no-unnecessary-boolean-literal-compare": "off",
"typescript/no-unnecessary-template-expression": "off",
"typescript/no-unnecessary-type-arguments": "off",
"typescript/no-unnecessary-type-assertion": "off",
"typescript/no-unsafe-argument": "off",
"typescript/no-unsafe-assignment": "off",
"typescript/no-unsafe-call": "off",
"typescript/no-unsafe-enum-comparison": "off",
"typescript/no-unsafe-member-access": "off",
"typescript/no-unsafe-return": "off",
"typescript/no-unsafe-type-assertion": "off",
"typescript/no-unsafe-unary-minus": "off",
"typescript/non-nullable-type-assertion-style": "off",
"typescript/only-throw-error": "off",
"typescript/prefer-promise-reject-errors": "off",
"typescript/prefer-reduce-type-parameter": "off",
"typescript/prefer-return-this-type": "off",
"typescript/promise-function-async": "off",
"typescript/related-getter-setter-pairs": "off",
"typescript/require-array-sort-compare": "off",
"typescript/require-await": "off",
"typescript/restrict-plus-operands": "off",
"typescript/restrict-template-expressions": "off",
"typescript/return-await": "off",
"typescript/switch-exhaustiveness-check": "off",
"typescript/unbound-method": "off",
"typescript/use-unknown-in-catch-callback-variable": "off",
},
},
{
"files": ["__tests__/**"],
"plugins": [
"typescript",
"unicorn",
"oxc",
"import",
"node",
"promise",
"jest",
"vitest",
],
"rules": {
"no-explicit-any": "off", //155
"ban-ts-comment": "off", //9
"typescript/no-unsafe-assignment": "off", //386
"typescript/no-unsafe-member-access": "off", //133
"typescript/no-unsafe-argument": "off", //96
"typescript/await-thenable": "off", //41
"typescript/no-confusing-void-expression": "off", //30
"typescript/promise-function-async": "off", //19
"typescript/no-floating-promises": "off", //16
"typescript/no-unsafe-call": "off", //5
"consistent-type-definitions": "off", //5
"explicit-function-return-type": "off", //4
"eqeqeq": "off", //2
"typescript/no-unsafe-return": "off", //2
"no-empty-object-type": "off", //2
"typescript/strict-boolean-expressions": "off", //1
"typescript/no-unnecessary-type-assertion": "off", //1
"typescript/strict-void-return": "off",
"prefer-nullish-coalescing": "off",
"no-shadow": "off",
"no-non-null-assertion": "off",
},
},
{
"files": ["private/script.js"],
"rules": {
"no-shadow": "off",
},
},
],
}

View File

@@ -0,0 +1,11 @@
{
"name": "@monkeytype/oxlint-config",
"private": true,
"type": "module",
"exports": {
".": "./index.json"
},
"devDependencies": {
"@oxlint/plugins": "1.43.0"
}
}

View File

@@ -0,0 +1,33 @@
{
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
"categories": {
"correctness": "off",
"suspicious": "off",
"pedantic": "off",
"perf": "off",
"style": "off",
"restriction": "off",
"nursery": "off",
},
"jsPlugins": ["./plugins/monkeytype-rules.js"],
"rules": {
"monkeytype-rules/no-testing-access": "error",
"monkeytype-rules/no-mixed-nullish-coalescing": "error",
},
"overrides": [
{
"files": ["__tests__/**/*.ts"],
"rules": {
"monkeytype-rules/no-testing-access": "off",
},
},
{
"files": ["**/*.tsx"],
"rules": {
"monkeytype-rules/prefer-arrow-in-component": "error",
"monkeytype-rules/one-component-per-file": "error",
"monkeytype-rules/component-pascal-case": "error",
},
},
],
}

View File

@@ -0,0 +1,295 @@
import { defineRule } from "@oxlint/plugins";
/**
* Walk a function body looking for a ReturnStatement whose argument is
* JSXElement or JSXFragment. Only traverses control flow nodes — does NOT
* recurse into call arguments or JSX attribute values, preventing false
* positives on functions that pass JSX as a prop/argument. Stops at nested
* function boundaries so inner helpers returning JSX don't count.
*/
function containsJSXReturn(node) {
if (!node || typeof node !== "object" || !node.type) return false;
// Stop at nested function boundaries
if (
node.type === "FunctionDeclaration" ||
node.type === "FunctionExpression" ||
node.type === "ArrowFunctionExpression"
) {
return false;
}
// Arrow with concise body: const Foo = () => <div />
if (node.type === "JSXElement" || node.type === "JSXFragment") return true;
// return <...>
if (node.type === "ReturnStatement") {
return (
node.argument?.type === "JSXElement" ||
node.argument?.type === "JSXFragment"
);
}
// Only recurse through control flow / block nodes, not into expressions
const CONTROL_FLOW_KEYS = {
BlockStatement: ["body"],
Program: ["body"],
IfStatement: ["consequent", "alternate"],
SwitchStatement: ["cases"],
SwitchCase: ["consequent"],
TryStatement: ["block", "handler", "finalizer"],
CatchClause: ["body"],
WhileStatement: ["body"],
DoWhileStatement: ["body"],
ForStatement: ["body"],
ForInStatement: ["body"],
ForOfStatement: ["body"],
LabeledStatement: ["body"],
};
const keys = CONTROL_FLOW_KEYS[node.type];
if (!keys) return false;
for (const key of keys) {
const child = node[key];
if (!child) continue;
if (Array.isArray(child)) {
for (const item of child) {
if (containsJSXReturn(item)) return true;
}
} else if (containsJSXReturn(child)) {
return true;
}
}
return false;
}
const plugin = {
meta: {
name: "monkeytype-rules",
},
rules: {
"no-testing-access": defineRule({
createOnce(context) {
return {
MemberExpression(node) {
if (node.property?.name === "__testing") {
context.report({
node,
message: "__testing should only be accessed in test files.",
});
}
},
};
},
}),
"prefer-arrow-in-component": defineRule({
meta: {
hasSuggestions: true,
},
createOnce(context) {
const getComponentAncestor = (node) => {
let current = node.parent;
while (current) {
// function Foo() { return <...> }
if (
current.type === "FunctionDeclaration" &&
containsJSXReturn(current.body)
) {
return current.id?.name ?? "component";
}
// const Foo = () => { return <...> } or const Foo = function() { return <...> }
if (
(current.type === "ArrowFunctionExpression" ||
current.type === "FunctionExpression") &&
containsJSXReturn(current.body ?? current) &&
current.parent?.type === "VariableDeclarator"
) {
return current.parent.id?.name ?? "component";
}
current = current.parent;
}
return null;
};
return {
FunctionDeclaration(node) {
const componentName = getComponentAncestor(node);
if (componentName && node.id) {
const fnName = node.id.name;
context.report({
node,
message: `\`${fnName}\` should be a const arrow function`,
suggest: [
{
desc: `Convert to const arrow function (note: removes hoisting)`,
fix(fixer) {
const fullText = context.sourceCode.getText(node);
const nodeStart = node.range?.[0] ?? node.start;
const afterName =
(node.id.range?.[1] ?? node.id.end) - nodeStart;
const bodyStart =
(node.body.range?.[0] ?? node.body.start) - nodeStart;
const paramsAndReturn = fullText
.slice(afterName, bodyStart)
.trimEnd();
const body = fullText.slice(bodyStart);
const asyncPrefix = node.async ? "async " : "";
return fixer.replaceText(
node,
`const ${fnName} = ${asyncPrefix}${paramsAndReturn} => ${body}`,
);
},
},
],
});
}
},
};
},
}),
"one-component-per-file": defineRule({
createOnce(context) {
let exportedComponents;
return {
before() {
exportedComponents = [];
},
ExportNamedDeclaration(node) {
// export function Foo() { return <...> }
if (
node.declaration?.type === "FunctionDeclaration" &&
node.declaration.id?.name &&
containsJSXReturn(node.declaration.body)
) {
exportedComponents.push({
name: node.declaration.id.name,
node,
});
return;
}
// export const Foo = () => <...> or export const Foo = function() { return <...> }
if (node.declaration?.type === "VariableDeclaration") {
for (const decl of node.declaration.declarations) {
if (
decl.id?.name &&
(decl.init?.type === "ArrowFunctionExpression" ||
decl.init?.type === "FunctionExpression") &&
containsJSXReturn(decl.init.body ?? decl.init)
) {
exportedComponents.push({ name: decl.id.name, node });
}
}
}
},
"Program:exit"() {
if (exportedComponents.length > 1) {
for (const { name, node } of exportedComponents.slice(1)) {
context.report({
node,
message: `Only one exported component per file. Move \`${name}\` to its own file.`,
});
}
}
},
};
},
}),
"no-mixed-nullish-coalescing": defineRule({
createOnce(context) {
/**
* Returns true for expression node types that, when combined with ??
* without explicit parentheses, create confusing/ambiguous precedence.
* Excluded: UnaryExpression (clearly bound to its operand),
* LogicalExpression with ?? (same operator, unambiguous).
*/
const isParenthesized = (node, source) => {
// OXC strips ParenthesizedExpression from the AST before visiting,
// so check the raw source surrounding the node's range instead.
const start = node.range?.[0] ?? node.start;
const end = node.range?.[1] ?? node.end;
return source[start - 1] === "(" && source[end] === ")";
};
const isMixedOperatorNode = (node, source) => {
if (isParenthesized(node, source)) return false;
return (
node.type === "BinaryExpression" ||
(node.type === "LogicalExpression" && node.operator !== "??") ||
node.type === "ConditionalExpression"
);
};
return {
LogicalExpression(node) {
if (node.operator !== "??") return;
const source = context.sourceCode.getText();
if (isMixedOperatorNode(node.left, source)) {
context.report({
node: node.left,
message:
"Nullish coalescing (`??`) mixed with other operators without explicit parentheses. Extract to a helper variable or wrap in parentheses for clarity.",
});
}
if (isMixedOperatorNode(node.right, source)) {
context.report({
node: node.right,
message:
"Nullish coalescing (`??`) mixed with other operators without explicit parentheses. Extract to a helper variable or wrap in parentheses for clarity.",
});
}
},
};
},
}),
"component-pascal-case": defineRule({
createOnce(context) {
const isPascalCase = (name) => /^[A-Z][a-zA-Z0-9]*$/.test(name);
return {
FunctionDeclaration(node) {
const isTopLevel =
node.parent?.type === "Program" ||
node.parent?.type === "ExportNamedDeclaration";
if (!isTopLevel || !node.id) return;
const name = node.id.name;
if (!isPascalCase(name) && containsJSXReturn(node.body)) {
context.report({
node: node.id,
message: `Component \`${name}\` should be PascalCase.`,
});
}
},
VariableDeclarator(node) {
const isTopLevel =
node.parent?.parent?.type === "Program" ||
node.parent?.parent?.type === "ExportNamedDeclaration";
if (
!isTopLevel ||
node.id?.type !== "Identifier" ||
(node.init?.type !== "ArrowFunctionExpression" &&
node.init?.type !== "FunctionExpression")
) {
return;
}
const name = node.id.name;
if (!isPascalCase(name) && containsJSXReturn(node.init)) {
context.report({
node: node.id,
message: `Component \`${name}\` should be PascalCase.`,
});
}
},
};
},
}),
},
};
export default plugin;

View File

@@ -0,0 +1,9 @@
{
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
"rules": {
"no-array-for-each": "off",
"no-nested-ternary": "off",
"no-array-sort": "off",
"preserve-caught-error": "off",
},
}

View File

@@ -0,0 +1,16 @@
{
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
"rules": {
"no-await-in-loop": "off",
"consistent-function-scoping": "off",
"prefer-add-event-listener": "off",
"no-new": "off",
"no-new-array": "off",
"no-useless-spread": "off",
"no-async-endpoint-handlers": "off",
"no-this-alias": "off",
"no-unassigned-import": "off",
"no-array-reverse": "off", // disabled for compatibility
"no-map-spread": "off",
},
}

View File

@@ -0,0 +1,88 @@
{
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
"rules": {
"no-unused-vars": [
"error",
{
"argsIgnorePattern": "^(_|e|event)",
"caughtErrorsIgnorePattern": "^(_|e|error)",
"varsIgnorePattern": "^_",
"fix": {
"imports": "safe-fix",
},
},
],
"no-var": "error",
"no-non-null-assertion": "error",
"no-non-null-asserted-nullish-coalescing": "error",
"no-explicit-any": "error",
"no-empty-object-type": "error",
"explicit-function-return-type": [
"error",
{
"allowExpressions": true,
},
],
"no-unused-expressions": [
"error",
{
"allowTernary": true,
},
],
"no-unsafe-function-type": "error",
"prefer-for-of": "error",
"consistent-type-definitions": ["error", "type"],
"no-var-requires": "error",
"no-named-as-default": "error",
"no-named-as-default-member": "error",
"no-array-constructor": "error",
"no-dynamic-delete": "error",
"no-extraneous-class": "error",
"no-require-imports": "error",
"no-empty-function": "error",
"no-redeclare": "error",
"no-empty": [
"error",
{
"allowEmptyCatch": true,
},
],
"no-self-compare": "error",
"no-throw-literal": "error",
"no-constant-condition": "error",
"no-constant-binary-expression": "error",
"no-unnecessary-type-constraint": "error",
"no-useless-constructor": "error",
"prefer-literal-enum-member": "error",
"prefer-namespace-keyword": "error",
"prefer-rest-params": "error",
"prefer-spread": "error",
"no-duplicates": "error",
"no-case-declarations": "error",
"no-fallthrough": "error",
"no-inner-declarations": "error",
"no-prototype-builtins": "error",
"no-regex-spaces": "error",
"typescript/no-namespace": "error",
"eqeqeq": "error",
"ban-ts-comment": "error",
"no-unassigned-vars": "error",
"max-depth": [
"error",
{
"max": 5,
},
],
"always-return": [
"error",
{
"ignoreLastCallback": true,
},
],
"unicorn/prefer-includes": "error",
"unicorn/prefer-structured-clone": "error",
"curly": ["error", "multi-line", "consistent"],
"no-sequences": "error",
"import/no-cycle": "error",
},
}

View File

@@ -0,0 +1,44 @@
{
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
"overrides": [
{
"plugins": [
"typescript",
"unicorn",
"oxc",
"import",
"node",
"promise",
"react",
],
"files": ["src/**/*.tsx"],
"rules": {
"react/react-in-jsx-scope": "off",
"react/button-has-type": "error",
"react/jsx-no-duplicate-props": "error",
"react/jsx-no-undef": "error",
"react/jsx-props-no-spread-multi": "error",
"react/void-dom-elements-no-children": "error",
"react/no-unknown-property": [
"error",
{
"ignore": [
"autocomplete",
"class",
"classList",
"innerHTML",
"onScrollEnd",
"router-link",
],
},
],
"react/jsx-no-comment-textnodes": "error",
"react/style-prop-object": "error",
"react/checked-requires-onchange-or-readonly": "error",
"react/jsx-no-useless-fragment": "error",
"react/no-unescaped-entities": "error",
"react/jsx-pascal-case": "error",
},
},
],
}

View File

@@ -0,0 +1,21 @@
{
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
"rules": {
//936, no options on this one. super strict, it doesnt allow casting to a narrower type
"typescript/no-unsafe-type-assertion": "off",
//224 errors, very easy to fix.
// adds unnecessary promise overhead and pushing the function to the microtask queue, creating a delay
// all though performance impact probably minimal
// anything that needs to be absolutely as fast as possible should not be async (if not using await)
"typescript/require-await": "off",
//388, when allowing numbers only 27, when also allowing arrays 12
// could be nice to avoid some weird things showing up in templated strings
"typescript/restrict-template-expressions": [
"off",
{
"allowNumber": true,
"allowArray": true,
},
],
},
}

View File

@@ -0,0 +1,13 @@
{
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
"rules": {
"typescript/non-nullable-type-assertion-style": "off",
"typescript/switch-exhaustiveness-check": "off",
"typescript/unbound-method": "off",
"typescript/prefer-promise-reject-errors": "off",
"typescript/no-redundant-type-constituents": "off",
"typescript/require-array-sort-compare": "off",
//unnecessary, might aswell keep template strings in case a string might be added in the future
"typescript/no-unnecessary-template-expression": "off",
},
}

View File

@@ -0,0 +1,64 @@
{
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
"rules": {
"typescript/strict-boolean-expressions": [
"error",
{ "allowNullableBoolean": true },
],
"typescript/only-throw-error": "error",
"typescript/no-unsafe-member-access": "error",
"typescript/no-unsafe-call": "error",
"typescript/no-unsafe-argument": "error",
"typescript/no-unsafe-assignment": "error",
"typescript/no-unnecessary-type-assertion": "error",
"typescript/no-confusing-void-expression": [
"error",
{ "ignoreArrowShorthand": true },
],
"typescript/no-misused-promises": [
"error",
{
"checksVoidReturn": false,
},
],
"typescript/promise-function-async": "error",
"typescript/no-floating-promises": "error",
"typescript/no-array-delete": "error",
"typescript/no-base-to-string": "error",
"typescript/no-duplicate-type-constituents": "error",
"typescript/no-for-in-array": "error",
"typescript/no-implied-eval": "error",
"typescript/no-meaningless-void-operator": "error",
"typescript/no-mixed-enums": "error",
"typescript/no-unnecessary-boolean-literal-compare": "error",
"typescript/no-unsafe-enum-comparison": "error",
"typescript/no-unsafe-return": "error",
"typescript/no-unsafe-unary-minus": "error",
"typescript/prefer-reduce-type-parameter": "error",
"typescript/prefer-return-this-type": "error",
"typescript/related-getter-setter-pairs": "error",
//todo: consider "always" or "in-try-catch"
"typescript/return-await": ["error", "error-handling-correctness-only"],
"typescript/use-unknown-in-catch-callback-variable": "error",
"typescript/await-thenable": "error",
"typescript/no-unnecessary-type-arguments": "error",
"typescript/restrict-plus-operands": [
"error",
{
"allowNumberAndString": true,
},
],
"typescript/no-deprecated": "error",
"typescript/prefer-includes": "error",
"typescript/prefer-nullish-coalescing": "error",
"typescript/no-invalid-void-type": "error",
"typescript/unified-signatures": "error",
"typescript/parameter-properties": "error",
"typescript/dot-notation": "error",
"typescript/no-useless-default-assignment": "error",
"typescript/prefer-string-starts-ends-with": "error",
"typescript/prefer-regexp-exec": "error",
"typescript/prefer-find": "error",
"typescript/consistent-type-exports": "error",
},
}