eslint-config-sheriff
Version:
A comprehensive and opinionated TypeScript-first ESLint configuration.
1,040 lines (1,010 loc) • 31.4 kB
JavaScript
import { globalIgnores } from "eslint/config";
import getGitignorePatterns from "eslint-config-flat-gitignore";
import lodash from "lodash";
import astro from "eslint-plugin-astro";
import tseslint, { default as tseslint$1 } from "typescript-eslint";
import createNoRestrictedProperties from "eslint-no-restricted/properties";
import arrowReturnStyle from "eslint-plugin-arrow-return-style";
import fsecond from "eslint-plugin-fsecond";
import pluginImport from "eslint-plugin-import";
import jsdoc from "eslint-plugin-jsdoc";
import * as regexpPlugin from "eslint-plugin-regexp";
import simpleImportSort from "eslint-plugin-simple-import-sort";
import sonarjs from "eslint-plugin-sonarjs";
import tsdoc from "eslint-plugin-tsdoc";
import unicorn from "eslint-plugin-unicorn";
import globals from "globals";
import eslintJs from "@eslint/js";
import preferEarlyReturn from "@regru/eslint-plugin-prefer-early-return";
import stylistic from "@stylistic/eslint-plugin";
import confusingBrowserGlobals from "confusing-browser-globals";
import createNoRestrictedSyntax from "eslint-no-restricted/syntax";
import jest from "eslint-plugin-jest";
import reactAccessibility from "eslint-plugin-jsx-a11y";
import react from "eslint-plugin-react";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import reactYouMightNotNeedAnEffect from "eslint-plugin-react-you-might-not-need-an-effect";
import rel1cxReact from "@eslint-react/eslint-plugin";
import storybook from "eslint-plugin-storybook";
import vitest from "@vitest/eslint-plugin";
import lodashPlugin from "eslint-plugin-lodash-f";
import { fixupPluginRules } from "@eslint/compat";
import nextjs from "@next/eslint-plugin-next";
import playwright from "eslint-plugin-playwright";
import remedaPlugin from "eslint-plugin-remeda";
//#region ../sheriff-constants/src/index.ts
const sheriffStartingOptions = {
react: false,
lodash: false,
remeda: false,
next: false,
astro: false,
playwright: false,
storybook: true,
jest: false,
vitest: false
};
const jsExtensions = "js,mjs,cjs";
const jsxExtensions = "jsx,mjsx";
const tsExtensions = "ts,mts,cts";
const tsxExtensions = "tsx,mtsx";
const allJsExtensions = `${jsExtensions},${tsExtensions}`;
const allJsxExtensions = `${jsxExtensions},${tsxExtensions}`;
const supportedFileTypes = `**/*{${allJsExtensions},${allJsxExtensions},astro}`;
const testsFilePatterns = [
`**/*.{test,spec}.{${allJsExtensions}}`,
`**/tests/**/*.{${allJsExtensions}}`,
`**/__tests__/**/*.{${allJsExtensions}}`
];
const ignores = [
"**/node_modules/**",
"**/dist/**",
"**/build/**",
"**/artifacts/**",
"**/coverage/**",
"eslint.config.{js,mjs,cjs}"
];
//#endregion
//#region src/utils/getTsNamingConventionRule.ts
const getTsNamingConventionRule = ({ isTsx, isAstroEndpoint }) => {
const options = [
{
selector: "default",
format: ["camelCase", isTsx && "StrictPascalCase"].filter(Boolean),
leadingUnderscore: "forbid",
trailingUnderscore: "forbid"
},
{
selector: "variable",
format: ["camelCase", "UPPER_CASE"],
modifiers: ["const"],
types: ["string", "number"],
leadingUnderscore: "forbid",
trailingUnderscore: "forbid"
},
{
selector: "objectLiteralProperty",
format: null,
leadingUnderscore: "allowSingleOrDouble",
trailingUnderscore: "forbid"
},
{
selector: "typeLike",
format: ["PascalCase"],
leadingUnderscore: "forbid",
trailingUnderscore: "forbid"
},
{
selector: "variable",
types: ["boolean"],
format: ["PascalCase"],
prefix: [
"is",
"are",
"has",
"should",
"can"
],
leadingUnderscore: "forbid",
trailingUnderscore: "forbid"
},
{
selector: "variable",
modifiers: ["destructured"],
format: null
},
{
selector: "typeProperty",
format: null
}
];
if (isAstroEndpoint) options.push({
selector: ["variable", "function"],
types: ["function"],
modifiers: ["exported"],
format: null,
filter: "^(GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS|CONNECT|TRACE|ALL)$"
});
return { "@typescript-eslint/naming-convention": [2, ...options] };
};
//#endregion
//#region src/getAstroConfig.ts
const baseAstroJsxA11yConfig = astro.configs["flat/jsx-a11y-strict"].slice(0, -1);
const astroJsxA11yConfig = astro.configs["flat/jsx-a11y-strict"].pop();
const astroJsxA11yPlugin = astroJsxA11yConfig?.plugins?.["jsx-a11y"] ?? {};
const astroJsxA11yRules = astroJsxA11yConfig?.rules ?? {};
const getAstroConfig = (hasReact, customTSConfigPath) => {
return tseslint$1.config(astro.configs["flat/recommended"], hasReact ? {
extends: [baseAstroJsxA11yConfig],
plugins: { "astro/jsx-a11y": astroJsxA11yPlugin },
rules: astroJsxA11yRules
} : {}, {
files: ["**/*.astro"],
languageOptions: { parserOptions: {
parser: tseslint$1.parser,
project: customTSConfigPath || true,
extraFileExtensions: [".astro"]
} },
settings: { "import/parsers": {
"astro-eslint-parser": [".astro"],
"@typescript-eslint/parser": [
".ts",
".tsx",
"mts",
"cts"
],
espree: [".js"]
} },
rules: getTsNamingConventionRule({ isTsx: true })
}, {
files: ["**/*.astro/*.ts"],
languageOptions: { parserOptions: {
parser: tseslint$1.parser,
project: customTSConfigPath || true
} }
}, {
files: [`**/src/pages/**/*.{${allJsExtensions}}`],
rules: getTsNamingConventionRule({
isTsx: false,
isAstroEndpoint: true
})
});
};
//#endregion
//#region src/handpickedRules/getBaseEslintHandPickedRules.ts
const getBaseEslintHandPickedRules = () => {
return {
"func-style": 2,
"no-promise-executor-return": 2,
"no-unreachable-loop": 2,
"no-caller": 2,
"no-extend-native": 2,
"no-extra-bind": 2,
"no-extra-label": 2,
"no-implicit-coercion": 2,
"no-multi-str": 2,
"no-negated-condition": 2,
"no-new-wrappers": 2,
"no-object-constructor": 2,
strict: [2, "never"],
"no-octal-escape": 2,
"no-proto": 2,
"no-sequences": [2, { allowInParentheses: false }],
"no-unmodified-loop-condition": 2,
"no-void": 2,
"no-array-constructor": 2,
"no-multi-assign": 2,
"no-plusplus": 2,
"prefer-destructuring": [
2,
{
VariableDeclarator: {
array: false,
object: true
},
AssignmentExpression: {
array: false,
object: false
}
},
{ enforceForRenamedProperties: false }
],
"no-useless-call": 2,
"prefer-object-has-own": 2,
"no-lone-blocks": 2,
"no-eval": 2,
"no-return-assign": [2, "always"],
"no-else-return": [2, { allowElseIf: false }],
"prefer-template": 2,
"operator-assignment": [2, "never"],
"logical-assignment-operators": [2, "never"],
"prefer-object-spread": 2,
"prefer-rest-params": 2,
"no-param-reassign": 2,
"no-redeclare": 2,
"no-useless-computed-key": 2,
"array-callback-return": [2, {
allowImplicit: true,
checkForEach: true
}],
"object-shorthand": 2,
"no-unneeded-ternary": [2, { defaultAssignment: false }],
"require-atomic-updates": 2,
"no-nested-ternary": 2,
"no-console": [2, { allow: [
"warn",
"error",
"debug",
"info",
"table"
] }],
curly: [2, "all"],
eqeqeq: 2,
"prefer-arrow-callback": 2,
"no-useless-assignment": 2,
"no-restricted-imports": [2, {
paths: [{
name: "prop-types",
message: "Dont use prop-types. Use Typescript instead."
}],
patterns: [{
group: ["node_modules"],
message: "Imports from node_modules are likely a user mistake.",
importNamePattern: "^"
}, {
group: ["dist"],
message: "Imports from dist are likely a user mistake.",
importNamePattern: "^"
}]
}],
"no-restricted-globals": [2, ...confusingBrowserGlobals],
"no-return-await": 0,
"no-use-before-define": 0,
"no-unused-expressions": 0,
"no-empty-function": 0,
"dot-notation": 0,
"no-shadow": 0,
"default-param-last": 0,
"arrow-body-style": 0
};
};
//#endregion
//#region src/handpickedRules/importHandPickedRules.ts
const importHandPickedRules = {
"import/newline-after-import": 2,
"import/first": 2,
"import/no-default-export": 2,
"import/no-anonymous-default-export": 2,
"import/no-named-as-default": 2,
"import/no-duplicates": [2, { "prefer-inline": true }],
"import/no-useless-path-segments": [2, { noUselessIndex: true }]
};
//#endregion
//#region src/handpickedRules/jsdocHandPickedRules.ts
const jsdocHandPickedRules = {
"jsdoc/require-description": 2,
"jsdoc/require-description-complete-sentence": 2,
"jsdoc/require-hyphen-before-param-description": 2,
"jsdoc/require-returns-description": 2,
"jsdoc/require-param-name": 2,
"jsdoc/require-param-description": 2,
"jsdoc/require-asterisk-prefix": 2,
"jsdoc/no-types": 2,
"jsdoc/no-multi-asterisks": 2,
"jsdoc/no-defaults": 2,
"jsdoc/no-blank-block-descriptions": 2,
"jsdoc/check-indentation": 2,
"jsdoc/check-tag-names": [2, { jsxTags: true }],
"jsdoc/check-param-names": [2, {
checkDestructured: false,
enableFixer: false
}],
"jsdoc/sort-tags": 2,
"jsdoc/tag-lines": [
2,
"any",
{ startLines: 1 }
],
"jsdoc/convert-to-jsdoc-comments": 2
};
//#endregion
//#region src/handpickedRules/sonarjsHandPickedRules.ts
const sonarjsHandPickedRules = {
"sonarjs/cognitive-complexity": 0,
"sonarjs/prefer-immediate-return": 0,
"sonarjs/no-duplicate-string": 0
};
//#endregion
//#region src/handpickedRules/stylisticHandPickedRules.ts
const stylisticHandPickedRules = { "@stylistic/padding-line-between-statements": [
2,
{
blankLine: "always",
prev: ["const", "let"],
next: "*"
},
{
blankLine: "any",
prev: ["const", "let"],
next: ["const", "let"]
},
{
blankLine: "always",
prev: "*",
next: "return"
}
] };
//#endregion
//#region src/handpickedRules/typescriptHandPickedRules.ts
const typescriptHandPickedRules = {
"@typescript-eslint/no-unsafe-assignment": 0,
"@typescript-eslint/no-array-constructor": 0,
"@typescript-eslint/use-unknown-in-catch-callback-variable": 0,
"@typescript-eslint/no-require-imports": 0,
"@typescript-eslint/ban-ts-comment": [2, {
"ts-expect-error": false,
"ts-ignore": true,
"ts-nocheck": false,
"ts-check": false
}],
"@typescript-eslint/no-use-before-define": 2,
"@typescript-eslint/no-inferrable-types": 2,
"@typescript-eslint/no-loop-func": 2,
"@typescript-eslint/prefer-function-type": 2,
"@typescript-eslint/prefer-string-starts-ends-with": 2,
"@typescript-eslint/consistent-type-definitions": 2,
"@typescript-eslint/consistent-type-assertions": 2,
"@typescript-eslint/consistent-type-imports": [2, { fixStyle: "inline-type-imports" }],
"@typescript-eslint/consistent-type-exports": [2, { fixMixedExportsWithInlineTypeSpecifier: true }],
"@typescript-eslint/explicit-module-boundary-types": 2,
"@typescript-eslint/switch-exhaustiveness-check": 2,
"@typescript-eslint/method-signature-style": 2,
"@typescript-eslint/prefer-nullish-coalescing": [2, { ignorePrimitives: true }],
"@typescript-eslint/no-unused-expressions": [2, {
allowShortCircuit: true,
allowTernary: true,
allowTaggedTemplates: true,
enforceForJSX: true
}],
"@typescript-eslint/array-type": 2,
"@typescript-eslint/no-empty-function": 2,
"@typescript-eslint/prefer-optional-chain": 2,
"@typescript-eslint/dot-notation": 2,
"@typescript-eslint/no-import-type-side-effects": 2,
"@typescript-eslint/default-param-last": 2,
"@typescript-eslint/no-empty-object-type": [2, { allowInterfaces: "with-single-extends" }],
"@typescript-eslint/no-shadow": [2, {
hoist: "all",
allow: [
"resolve",
"reject",
"done",
"next",
"err",
"error"
],
ignoreTypeValueShadow: true,
ignoreFunctionTypeParameterNameValueShadow: true
}]
};
//#endregion
//#region src/handpickedRules/unicornHandPickedRules.ts
const unicornHandPickedRules = {
"unicorn/explicit-length-check": 2,
"unicorn/consistent-function-scoping": 2,
"unicorn/prefer-default-parameters": 2,
"unicorn/no-array-push-push": 2,
"unicorn/prefer-array-index-of": 2,
"unicorn/prefer-array-flat-map": 2,
"unicorn/prefer-array-some": 2,
"unicorn/prefer-array-find": 2,
"unicorn/prefer-array-flat": 2,
"unicorn/prefer-includes": 2,
"unicorn/prefer-top-level-await": 2,
"unicorn/prefer-spread": 2,
"unicorn/no-useless-spread": 2,
"unicorn/no-useless-fallback-in-spread": 2,
"unicorn/no-for-loop": 2,
"unicorn/prefer-set-size": 2,
"unicorn/prefer-type-error": 2,
"unicorn/prefer-object-from-entries": 2,
"unicorn/no-instanceof-array": 2,
"unicorn/prefer-native-coercion-functions": 2,
"unicorn/prefer-logical-operator-over-ternary": 2,
"unicorn/prefer-event-target": 2,
"unicorn/no-await-expression-member": 2,
"unicorn/no-new-array": 2,
"unicorn/throw-new-error": 2,
"unicorn/no-array-reduce": 2,
"unicorn/no-useless-length-check": 2,
"unicorn/prefer-prototype-methods": 2,
"unicorn/prefer-date-now": 2,
"unicorn/prefer-node-protocol": 2,
"unicorn/prefer-export-from": [2, { ignoreUsedVariables: true }],
"unicorn/no-new-buffer": 2,
"unicorn/prefer-query-selector": 2,
"unicorn/prefer-string-replace-all": 2,
"unicorn/prefer-switch": [2, { emptyDefaultCase: "do-nothing-comment" }],
"unicorn/switch-case-braces": 2,
"unicorn/catch-error-name": 2,
"unicorn/consistent-destructuring": 2,
"unicorn/prefer-string-slice": 2,
"unicorn/no-await-in-promise-methods": 2,
"unicorn/no-single-promise-in-promise-methods": 2,
"unicorn/consistent-empty-array-spread": 2,
"unicorn/no-unused-properties": 2
};
//#endregion
//#region src/utils/getLanguageOptionsTypescript.ts
const getLanguageOptionsTypescript = (userChosenTSConfig) => {
return {
parser: tseslint$1.parser,
parserOptions: {
ecmaFeatures: { modules: true },
project: userChosenTSConfig || true
}
};
};
//#endregion
//#region src/utils/noRestrictedSyntax.ts
const baseNoRestrictedSyntaxRules = [
{
selector: "LabeledStatement",
name: "noLabels",
message: "Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand."
},
{
selector: "ForInStatement",
name: "noForInLoops",
message: "for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array."
},
{
selector: "Identifier[name='Reflect']",
name: "noReflect",
message: "Avoid the Reflect API. It is a very low-level feature that has only rare and specific use-cases if building complex and hacky libraries. There is no need to use this feature for any kind of normal development."
},
{
selector: "Identifier[name='Proxy']",
name: "noProxy",
message: "Avoid Proxy."
},
{
selector: [
"PropertyDefinition[accessibility='public']",
"PropertyDefinition[accessibility='protected']",
"PropertyDefinition[accessibility='private']"
],
name: "noAccessModifiers",
message: "Avoid access modifiers. In Javascript modules there is no need to limit developer access to properties."
},
{
selector: ["Identifier[name='PropTypes']", "Identifier[name='propTypes']"],
name: "noPropTypes",
message: "Avoid PropTypes. Use Typescript instead."
},
{
selector: "UnaryExpression[operator='delete']",
name: "noDeleteOperator",
message: "Avoid the \"delete\" operator. Use omit() instead."
},
{
selector: "TSEnumDeclaration",
name: "noEnums",
message: "Avoid enums."
},
{
selector: ["ClassDeclaration", "ClassExpression"],
name: "noClasses",
message: "Avoid classes. Use functions and objects instead."
}
];
const noRestrictedSyntax = createNoRestrictedSyntax(...baseNoRestrictedSyntaxRules);
//#endregion
//#region src/getBaseConfig.ts
const noRestrictedProperties = createNoRestrictedProperties(...[{
name: "isFinite",
message: "Please use Number.isFinite instead",
property: [
{
object: "global",
property: "isFinite"
},
{
object: "self",
property: "isFinite"
},
{
object: "window",
property: "isFinite"
}
]
}, {
name: "isNaN",
message: "Please use Number.isNaN instead",
property: [
{
object: "global",
property: "isNaN"
},
{
object: "self",
property: "isNaN"
},
{
object: "window",
property: "isNaN"
}
]
}]);
const getBaseConfig = (userConfigChoices) => {
const customTSConfigPath = userConfigChoices.pathsOverrides?.tsconfigLocation;
return tseslint$1.config({
extends: [eslintJs.configs.recommended],
files: [supportedFileTypes]
}, {
files: [supportedFileTypes],
rules: getBaseEslintHandPickedRules()
}, {
extends: tseslint$1.configs.strictTypeChecked,
files: [supportedFileTypes]
}, {
files: [`**/*{${allJsExtensions}}`],
languageOptions: getLanguageOptionsTypescript(customTSConfigPath)
}, {
files: [supportedFileTypes],
rules: {
...typescriptHandPickedRules,
...getTsNamingConventionRule({ isTsx: false })
}
}, {
extends: [noRestrictedSyntax.configs.recommended],
files: [supportedFileTypes]
}, {
extends: [noRestrictedProperties.configs.recommended],
files: [supportedFileTypes]
}, {
files: [`**/*.{${tsExtensions},${tsxExtensions},astro}`],
plugins: { tsdoc },
rules: { "tsdoc/syntax": 2 }
}, {
files: [supportedFileTypes],
plugins: { "@stylistic": stylistic },
rules: stylisticHandPickedRules
}, {
files: [supportedFileTypes],
plugins: { "@regru/prefer-early-return": preferEarlyReturn },
rules: { "@regru/prefer-early-return/prefer-early-return": [2, { maximumStatements: 1 }] }
}, {
files: [supportedFileTypes],
languageOptions: { globals: globals.builtin },
plugins: { unicorn },
rules: unicornHandPickedRules
}, {
files: [supportedFileTypes],
plugins: { regexp: regexpPlugin },
rules: regexpPlugin.configs["flat/recommended"].rules
}, {
files: [supportedFileTypes],
plugins: { sonarjs },
rules: {
...sonarjs.configs.recommended.rules,
...sonarjsHandPickedRules
}
}, {
files: [supportedFileTypes],
plugins: { "arrow-return-style": arrowReturnStyle },
rules: {
"arrow-return-style/arrow-return-style": [2, { namedExportsAlwaysUseExplicitReturn: false }],
"arrow-return-style/no-export-default-arrow": 2
}
}, {
files: [supportedFileTypes],
plugins: { "simple-import-sort": simpleImportSort },
rules: {
"simple-import-sort/imports": [2, { groups: [[
"^\\u0000",
"^node:",
"^",
"^@",
"^@/",
"^#",
"^~",
"^\\."
]] }],
"simple-import-sort/exports": 2
}
}, {
files: [supportedFileTypes],
plugins: { import: pluginImport },
rules: importHandPickedRules,
settings: {
"import/parsers": {
"@typescript-eslint/parser": [
".ts",
".tsx",
".mts",
"cts"
],
espree: [".js"]
},
"import/resolver": {
typescript: { alwaysTryTypes: true },
node: true
}
}
}, {
files: ["**/*.d.ts"],
rules: { "import/no-default-export": 0 }
}, {
files: [supportedFileTypes],
plugins: { jsdoc },
rules: jsdocHandPickedRules,
settings: { jsdoc: { mode: "typescript" } }
}, {
extends: [fsecond.configs.recommended],
files: [supportedFileTypes],
rules: { "fsecond/valid-event-listener": 0 }
}, {
files: [allJsxExtensions],
plugins: { fsecond },
rules: { "fsecond/valid-event-listener": [2, { requireUseEventListenerHook: true }] }
}, {
files: [`**/*.config.{${allJsExtensions}}`],
rules: {
"import/no-default-export": 0,
"import/no-anonymous-default-export": 0,
"arrow-return-style/no-export-default-arrow": 0
}
}, {
files: [supportedFileTypes],
linterOptions: { reportUnusedDisableDirectives: "error" }
});
};
//#endregion
//#region src/handpickedRules/jestHandPickedRules.ts
const jestHandPickedRules = {
"jest/no-conditional-expect": 2,
"jest/no-conditional-in-test": 2,
"jest/no-alias-methods": 2,
"jest/no-export": 2,
"jest/no-duplicate-hooks": 2,
"jest/no-identical-title": 2,
"jest/no-focused-tests": 2,
"jest/no-jasmine-globals": 2,
"jest/no-standalone-expect": 2,
"jest/no-test-return-statement": 2,
"jest/valid-describe-callback": 2,
"jest/no-test-prefixes": 2,
"jest/require-top-level-describe": 2,
"jest/prefer-comparison-matcher": 2,
"jest/prefer-equality-matcher": 2,
"jest/prefer-expect-resolves": 2,
"jest/prefer-hooks-on-top": 2,
"jest/prefer-hooks-in-order": 2,
"jest/prefer-strict-equal": 2,
"jest/valid-title": 2,
"jest/valid-expect-in-promise": 2,
"jest/valid-expect": 2,
"jest/consistent-test-it": [2, {
fn: "test",
withinDescribe: "test"
}],
"jest/unbound-method": 2
};
//#endregion
//#region src/getJestConfig.ts
const getJestConfig = (pathsOverrides) => {
return tseslint$1.config({
files: pathsOverrides ?? [
`**/*.{test,spec}.{${allJsExtensions}}`,
`**/tests/**/*.{${allJsExtensions}}`,
`**/__tests__/**/*.{${allJsExtensions}}`
],
extends: [jest.configs["flat/style"]],
rules: {
...jestHandPickedRules,
"@typescript-eslint/unbound-method": 0
}
});
};
//#endregion
//#region src/handpickedRules/reactHandPickedRules.ts
const reactHandPickedRules = {
"react/prop-types": 0,
"react/no-unstable-nested-components": [2, { allowAsProps: false }],
"react/jsx-no-useless-fragment": [2, { allowExpressions: true }],
"react/function-component-definition": [2, { namedComponents: "arrow-function" }],
"react/jsx-boolean-value": 2,
"react/jsx-fragments": 2,
"react/jsx-key": [2, {
checkFragmentShorthand: true,
warnOnDuplicates: true
}],
"react/hook-use-state": 2,
"react/destructuring-assignment": 2,
"react/jsx-filename-extension": [2, { extensions: [
".jsx",
".tsx",
".mtsx",
".mjsx"
] }],
"react/no-multi-comp": 2,
"react/no-array-index-key": 2,
"react/no-this-in-sfc": 2,
"react/checked-requires-onchange-or-readonly": 2,
"react/jsx-sort-props": [2, {
callbacksLast: true,
shorthandFirst: true,
shorthandLast: false,
ignoreCase: true,
noSortAlphabetically: true,
multiline: "last",
reservedFirst: false
}]
};
//#endregion
//#region src/utils/getLanguageOptionsTypescriptReact.ts
const getLanguageOptionsTypescriptReact = (userChosenTSConfig) => {
return {
parser: tseslint$1.parser,
parserOptions: {
ecmaFeatures: {
modules: true,
jsx: true
},
project: userChosenTSConfig || true,
jsxPragma: null
},
globals: { ...globals.browser }
};
};
//#endregion
//#region src/getReactConfig.ts
const getReactConfig = (customTSConfigPath) => {
return tseslint$1.config({
files: [`**/*{${allJsExtensions},${allJsxExtensions}}`],
languageOptions: getLanguageOptionsTypescriptReact(customTSConfigPath)
}, {
files: [supportedFileTypes],
plugins: { react },
settings: { react: { version: "detect" } },
rules: {
...react.configs.flat.recommended.rules,
...react.configs.flat["jsx-runtime"].rules,
...reactHandPickedRules
}
}, {
files: [`**/*{${allJsxExtensions}}`],
rules: getTsNamingConventionRule({ isTsx: true })
}, {
files: [`**/*{${allJsxExtensions}}`],
plugins: { "react-refresh": reactRefresh },
rules: { "react-refresh/only-export-components": [2, { allowExportNames: ["loader"] }] }
}, {
extends: [reactAccessibility.flatConfigs.recommended],
files: [`**/*{${allJsxExtensions}}`]
}, {
files: [supportedFileTypes],
plugins: { "react-hooks": reactHooks },
rules: reactHooks.configs["recommended-latest"].rules
}, {
files: [supportedFileTypes],
plugins: {
"@eslint-react": rel1cxReact.configs.all.plugins["@eslint-react"],
"@eslint-react/hooks-extra": rel1cxReact.configs.all.plugins["@eslint-react/hooks-extra"]
},
rules: {
"@eslint-react/no-leaked-conditional-rendering": 2,
"@eslint-react/hooks-extra/ensure-custom-hooks-using-other-hooks": 2
}
}, {
files: [supportedFileTypes],
plugins: { "react-you-might-not-need-an-effect": reactYouMightNotNeedAnEffect },
rules: reactYouMightNotNeedAnEffect.configs.recommended.rules
}, {
files: [supportedFileTypes],
plugins: { fsecond },
rules: { "fsecond/valid-event-listener": 2 }
}, {
files: ["**/*.astro"],
rules: {
"react/no-unknown-property": 0,
"react/jsx-filename-extension": [2, {
allow: "always",
extensions: [
".jsx",
".tsx",
".mtsx",
".mjsx",
".astro"
]
}]
}
});
};
//#endregion
//#region src/getStorybookConfig.ts
const getStorybookConfig = () => {
return tseslint$1.config({
extends: [storybook.configs["flat/recommended"], storybook.configs["flat/csf"]],
rules: { "import/no-default-export": 0 },
ignores: ["!.storybook"]
});
};
//#endregion
//#region src/handpickedRules/vitestHandPickedRules.ts
const vitestHandPickedRules = {
"vitest/consistent-test-it": [2, {
fn: "test",
withinDescribe: "test"
}],
"vitest/expect-expect": 2,
"vitest/no-commented-out-tests": 2,
"vitest/no-conditional-expect": 2,
"vitest/no-conditional-in-test": 2,
"vitest/no-disabled-tests": 2,
"vitest/no-duplicate-hooks": 2,
"vitest/no-identical-title": 2,
"vitest/no-focused-tests": 2,
"vitest/no-standalone-expect": 2,
"vitest/no-test-prefixes": 2,
"vitest/no-test-return-statement": 2,
"vitest/prefer-comparison-matcher": 2,
"vitest/prefer-each": 2,
"vitest/prefer-equality-matcher": 2,
"vitest/prefer-expect-resolves": 2,
"vitest/prefer-hooks-in-order": 2,
"vitest/prefer-hooks-on-top": 2,
"vitest/prefer-lowercase-title": 2,
"vitest/prefer-mock-promise-shorthand": 2,
"vitest/prefer-spy-on": 2,
"vitest/prefer-strict-equal": 2,
"vitest/prefer-to-be": 2,
"vitest/prefer-to-be-falsy": 2,
"vitest/prefer-to-be-object": 2,
"vitest/prefer-to-be-truthy": 2,
"vitest/prefer-to-contain": 2,
"vitest/prefer-to-have-length": 2,
"vitest/prefer-todo": 2,
"vitest/require-hook": 2,
"vitest/require-to-throw-message": 2,
"vitest/require-top-level-describe": 2,
"vitest/valid-describe-callback": 2,
"vitest/valid-expect": 2
};
//#endregion
//#region src/getVitestConfig.ts
const getVitestConfig = (pathsOverrides) => {
return {
files: pathsOverrides ?? [
`**/*.{test,spec}.{${allJsExtensions}}`,
`**/tests/**/*.{${allJsExtensions}}`,
`**/__tests__/**/*.{${allJsExtensions}}`
],
plugins: { vitest },
rules: vitestHandPickedRules
};
};
//#endregion
//#region src/handpickedRules/lodashHandPickedRules.ts
const lodashHandPickedRules = {
"lodash-f/import-scope": [2, "member"],
"lodash-f/prefer-some": [2, { includeNative: false }],
"lodash-f/prefer-includes": [2, { includeNative: false }],
"lodash-f/prefer-lodash-method": 0,
"lodash-f/prefer-noop": 0,
"lodash-f/prefer-matches": 0,
"lodash-f/prefer-over-quantifier": 0,
"lodash-f/prefer-get": 0,
"unicorn/no-instanceof-array": 0
};
//#endregion
//#region src/lodashConfig.ts
const lodashConfig = {
files: [supportedFileTypes],
plugins: { "lodash-f": lodashPlugin },
rules: {
...lodashPlugin.configs.recommended.rules,
...lodashHandPickedRules
}
};
//#endregion
//#region src/nextjsConfig.ts
const nextjsConfig = [{
files: [`**/*.{${allJsExtensions},${allJsxExtensions}}`],
plugins: { "@next/next": fixupPluginRules(nextjs) },
rules: {
...nextjs.configs.recommended.rules,
...nextjs.configs["core-web-vitals"].rules,
"import/no-default-export": 0,
"react/function-component-definition": [2, {
namedComponents: "function-declaration",
unnamedComponents: "function-expression"
}],
"@next/next/no-html-link-for-pages": 0
}
}, {
files: [`**/page.{${allJsExtensions},${allJsxExtensions}}`, `**/layout.{${allJsExtensions},${allJsxExtensions}}`],
rules: { "react-refresh/only-export-components": [2, { allowExportNames: [
"metadata",
"generateMetadata",
"generateStaticParams"
] }] }
}];
//#endregion
//#region src/handpickedRules/playwrightHandPickedRules.ts
const playwrightHandPickedRules = {
"playwright/no-force-option": 0,
"playwright/prefer-lowercase-title": 2,
"playwright/prefer-to-have-length": 2,
"playwright/require-top-level-describe": 2,
"playwright/prefer-to-be": 2,
"playwright/prefer-to-have-count": 2,
"playwright/prefer-to-contain": 2,
"playwright/prefer-strict-equal": 2,
"playwright/prefer-native-locators": 2
};
//#endregion
//#region src/playwrightConfig.ts
const getPlaywrightConfig = (pathsOverrides) => {
return [{
...playwright.configs["flat/recommended"],
files: pathsOverrides ?? [`**/*{${allJsExtensions}}`],
rules: {
...playwright.configs["flat/recommended"].rules,
...playwrightHandPickedRules
}
}];
};
//#endregion
//#region src/remedaConfig.ts
const remedaConfig = tseslint$1.config({
files: [supportedFileTypes],
plugins: { remeda: remedaPlugin },
rules: remedaPlugin.configs.recommended.rules
});
//#endregion
//#region src/getExportableConfig.ts
const getExportableConfig = (userConfigChoices = sheriffStartingOptions, areAllRulesForced) => {
let exportableConfig = getBaseConfig(userConfigChoices);
const hasReact = Boolean(userConfigChoices.react);
if (hasReact || userConfigChoices.next) exportableConfig.push(...getReactConfig(userConfigChoices.pathsOverrides?.tsconfigLocation));
if (!areAllRulesForced && userConfigChoices.jest && userConfigChoices.vitest) throw new Error("Jest and Vitest support cannot be activated at once. Please choose one or the other.");
if (userConfigChoices.jest) exportableConfig.push(...getJestConfig(userConfigChoices.pathsOverrides?.tests));
if (userConfigChoices.vitest) exportableConfig.push(getVitestConfig(userConfigChoices.pathsOverrides?.tests));
if (userConfigChoices.next) exportableConfig.push(...nextjsConfig);
if (userConfigChoices.lodash) exportableConfig.push(lodashConfig);
if (userConfigChoices.remeda) exportableConfig.push(...remedaConfig);
if (userConfigChoices.astro) {
const customTSConfigPath = userConfigChoices.pathsOverrides?.tsconfigLocation;
exportableConfig.push(...getAstroConfig(hasReact, customTSConfigPath));
}
if (userConfigChoices.playwright) exportableConfig.push(...getPlaywrightConfig(userConfigChoices.pathsOverrides?.playwrightTests));
if (userConfigChoices.storybook !== false) exportableConfig.push(...getStorybookConfig());
if (userConfigChoices.files) {
const allowedPatterns = userConfigChoices.files.map((globPattern) => `!${globPattern}`);
exportableConfig = exportableConfig.map((configSlice) => {
if (configSlice.ignores?.length && !lodash.isEmpty(configSlice.ignores)) return configSlice;
return {
...configSlice,
ignores: ["**/*", ...allowedPatterns]
};
});
}
const hasIgnoresRecommended = lodash.isBoolean(userConfigChoices.ignores?.recommended) ? userConfigChoices.ignores?.recommended : true;
const hasIgnoresInheritedFromGitignore = lodash.isBoolean(userConfigChoices.ignores?.inheritedFromGitignore) ? userConfigChoices.ignores?.inheritedFromGitignore : true;
exportableConfig.push(globalIgnores([...hasIgnoresRecommended ? ignores : [], ...hasIgnoresInheritedFromGitignore ? getGitignorePatterns({ strict: false }).ignores : []]));
return exportableConfig;
};
//#endregion
//#region src/index.ts
const exportableAllJsExtensions = allJsExtensions;
const exportableAllJsxExtensions = allJsxExtensions;
const exportableIgnores = ignores;
const exportableSheriffStartingOptions = sheriffStartingOptions;
const exportableSupportedFileTypes = supportedFileTypes;
const exportableTestsFilePatterns = testsFilePatterns;
//#endregion
export { exportableAllJsExtensions as allJsExtensions, exportableAllJsxExtensions as allJsxExtensions, getExportableConfig as default, getExportableConfig as sheriff, exportableIgnores as ignores, exportableSheriffStartingOptions as sheriffStartingOptions, exportableSupportedFileTypes as supportedFileTypes, exportableTestsFilePatterns as testsFilePatterns, tseslint };
//# sourceMappingURL=index.js.map