@hyperse/eslint-config-hyperse
Version:
🛠These are settings for TypeScript / ESLint / Prettier in a project
206 lines (205 loc) • 6.54 kB
JavaScript
import { defineConfig } from "eslint/config";
import appRoot from "app-root-path";
import simpleImportSort from "eslint-plugin-simple-import-sort";
import { getTsconfig } from "get-tsconfig";
import pluginJsonc from "eslint-plugin-jsonc";
import * as pluginMdx from "eslint-plugin-mdx";
import pluginNext from "@next/eslint-plugin-next";
import eslintPluginPrettier from "eslint-plugin-prettier/recommended";
import tseslint from "typescript-eslint";
import eslintJs from "@eslint/js";
import eslintReact from "@eslint-react/eslint-plugin";
import regexpPlugin from "eslint-plugin-regexp";
import sonarjs from "eslint-plugin-sonarjs";
import globals from "globals";
import pluginVitest from "@vitest/eslint-plugin";
//#region src/rules/imports.ts
function extractPaths(paths) {
return Object.keys(paths).map((key) => {
return key.split("/")[0];
});
}
const tsConfig = getTsconfig(appRoot.path, "tsconfig.json");
const jsConfig = getTsconfig(appRoot.path, "jsconfig.json");
let pathsNames = [];
if (tsConfig && tsConfig.config.compilerOptions?.paths) pathsNames = extractPaths(tsConfig.config.compilerOptions.paths);
else if (jsConfig && jsConfig.config.compilerOptions?.paths) pathsNames = extractPaths(jsConfig.config.compilerOptions.paths);
const imports = defineConfig([{
plugins: { "simple-import-sort": simpleImportSort },
rules: {
"simple-import-sort/exports": "error",
"simple-import-sort/imports": ["error", { groups: [[
"^react",
"^next",
"^[a-z]",
`^(${pathsNames.join("|")})(/.*|$)`,
"^@",
"^\\.",
"^\\u0000",
"^\\u0000\\."
]] }]
}
}]);
//#endregion
//#region src/rules/jsonc.ts
const jsonc = defineConfig([...pluginJsonc.configs["flat/recommended-with-jsonc"], { rules: {} }]);
//#endregion
//#region src/rules/mdx.ts
const mdx = defineConfig([{ ...pluginMdx.flat }]);
defineConfig([{
plugins: { "@next/next": pluginNext },
rules: {
...pluginNext.configs.recommended.rules,
...pluginNext.configs["core-web-vitals"].rules
}
}, { ignores: [".next/*"] }]);
//#endregion
//#region src/prettierShared.ts
const TAILWIND_PLUGIN = "prettier-plugin-tailwindcss";
const prettierBaseOptions = {
semi: true,
singleQuote: true,
trailingComma: "es5"
};
/**
* Ensures `prettier-plugin-tailwindcss` is present exactly once and last, per
* https://github.com/tailwindlabs/prettier-plugin-tailwindcss#compatibility-with-other-prettier-plugins
*/
function finalizePrettierPlugins(plugins) {
return [...normalizePlugins(plugins).filter((p) => p !== TAILWIND_PLUGIN), TAILWIND_PLUGIN];
}
function normalizePlugins(plugins) {
if (plugins === void 0) return [];
return Array.isArray(plugins) ? [...plugins] : [plugins];
}
//#endregion
//#region src/definePrettierConfig.ts
const definePrettierConfig = (config = {}) => {
const { plugins, ...rest } = config;
return {
...prettierBaseOptions,
...rest,
plugins: finalizePrettierPlugins(plugins)
};
};
//#endregion
//#region src/rules/prettier.ts
const { ...prettierConfig } = definePrettierConfig();
const prettier = defineConfig([{
...eslintPluginPrettier,
rules: {
...eslintPluginPrettier.rules,
"prettier/prettier": ["error", prettierConfig]
}
}]);
const react = defineConfig([{
files: { files: ["**/*.{jsx,tsx}"] }.files,
extends: [
eslintJs.configs.recommended,
tseslint.configs.recommended,
eslintReact.configs["recommended-typescript"]
],
languageOptions: {
parser: tseslint.parser,
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname
}
},
rules: {},
settings: { react: { version: "detect" } }
}]);
const regexp = defineConfig([{
files: { files: ["**/*.{js,jsx,jsx,tsx,mts,cts,mjs,cjs}"] }.files,
plugins: { regexp: regexpPlugin },
rules: {
...regexpPlugin.configs.recommended.rules,
"regexp/prefer-result-array-groups": "off"
}
}]);
//#endregion
//#region src/rules/sonar.ts
const sonarPatterns = {
files: ["**/*.{js,jsx,ts,tsx,mts,cts,mjs,cjs}"],
excludedFiles: [
"**/?(*.)+(test).{js,jsx,ts,tsx,mts,cts,mjs,cjs}",
"**/?(*.)+(spec).{js,jsx,ts,tsx,mts,cts,mjs,cjs}",
"*.stories.{js,ts,jsx,tsx,mts,cts,mjs,cjs}"
]
};
defineConfig([{
files: sonarPatterns.files,
ignores: sonarPatterns.excludedFiles,
plugins: { sonarjs },
rules: {
...sonarjs.configs.recommended.rules,
"sonarjs/cognitive-complexity": ["error", 20],
"sonarjs/max-switch-cases": ["error", 20],
"sonarjs/no-all-duplicated-branches": "error",
"sonarjs/no-element-overwrite": "error",
"sonarjs/no-identical-functions": "error",
"sonarjs/no-identical-expressions": "error",
"sonarjs/no-hook-setter-in-body": "off",
"sonarjs/public-static-readonly": "off",
"sonarjs/no-nested-conditional": "off",
"sonarjs/fixme-tag": "warn",
"sonarjs/todo-tag": "warn",
"sonarjs/no-commented-code": "off",
"sonarjs/deprecation": "off"
}
}]);
//#endregion
//#region src/rules/typescript.ts
const typescript = defineConfig({ languageOptions: { globals: { ...globals.node } } }, eslintJs.configs.recommended, ...tseslint.configs.recommended);
const vitest = defineConfig([{
files: { files: ["**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"] }.files,
plugins: { vitest: pluginVitest },
rules: {
...pluginVitest.configs.recommended.rules,
"vitest/max-nested-describe": ["error", { max: 3 }],
"vitest/no-disabled-tests": 2,
"vitest/valid-title": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-object-literal-type-assertion": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/ban-ts-comment": "off"
},
settings: { vitest: { typecheck: true } },
languageOptions: { globals: { ...pluginVitest.environments.env.globals } }
}]);
//#endregion
//#region src/main/overrides.ts
const overrides = defineConfig([{
...tseslint.configs.base,
languageOptions: { parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
emitDecoratorMetadata: true
} },
files: ["**/*.ts", "**/*.tsx"],
rules: {
"@typescript-eslint/no-unused-vars": ["warn", {
args: "all",
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_"
}],
"@typescript-eslint/consistent-type-imports": ["error", { disallowTypeAnnotations: false }],
"@typescript-eslint/no-deprecated": "warn"
}
}]);
//#endregion
//#region src/main/reactjs.ts
const reactjs = defineConfig([...defineConfig([
...typescript,
...vitest,
...imports,
...prettier,
...regexp,
...jsonc,
...mdx,
...overrides
]), ...react]);
//#endregion
export { reactjs };