@szum-tech/eslint-config
Version:
ESLint configuration for TypeScript projects
505 lines (498 loc) • 17.5 kB
JavaScript
'use strict';
var fs = require('fs');
var path = require('path');
var importPlugin = require('eslint-plugin-import');
var jestDomPlugin = require('eslint-plugin-jest-dom');
var playwrightPlugin = require('eslint-plugin-playwright');
var reactPlugin = require('eslint-plugin-react');
var reactHooksPlugin = require('eslint-plugin-react-hooks');
var storybookPlugin = require('eslint-plugin-storybook');
var tailwindcssPlugin = require('eslint-plugin-tailwindcss');
var testingLibraryPlugin = require('eslint-plugin-testing-library');
var globals = require('globals');
var tsEslint = require('typescript-eslint');
var nextPlugin = require('@next/eslint-plugin-next');
var vitestPlugin = require('@vitest/eslint-plugin');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var importPlugin__namespace = /*#__PURE__*/_interopNamespace(importPlugin);
var jestDomPlugin__default = /*#__PURE__*/_interopDefault(jestDomPlugin);
var playwrightPlugin__default = /*#__PURE__*/_interopDefault(playwrightPlugin);
var reactPlugin__default = /*#__PURE__*/_interopDefault(reactPlugin);
var reactHooksPlugin__namespace = /*#__PURE__*/_interopNamespace(reactHooksPlugin);
var storybookPlugin__default = /*#__PURE__*/_interopDefault(storybookPlugin);
var tailwindcssPlugin__default = /*#__PURE__*/_interopDefault(tailwindcssPlugin);
var testingLibraryPlugin__default = /*#__PURE__*/_interopDefault(testingLibraryPlugin);
var globals__default = /*#__PURE__*/_interopDefault(globals);
var tsEslint__default = /*#__PURE__*/_interopDefault(tsEslint);
var nextPlugin__default = /*#__PURE__*/_interopDefault(nextPlugin);
var vitestPlugin__default = /*#__PURE__*/_interopDefault(vitestPlugin);
// src/index.js
var logger = console;
function isPackageInstalled(packageName) {
const currentDir = process.cwd();
const packageJsonPath = findPackageJson(currentDir);
try {
const packageJson = fs.readFileSync(packageJsonPath, "utf-8");
const parsedPackageJson = JSON.parse(packageJson);
const dependencies = parsedPackageJson.dependencies || {};
const devDependencies = parsedPackageJson.devDependencies || {};
return dependencies.hasOwnProperty(packageName) || devDependencies.hasOwnProperty(packageName);
} catch (error) {
logger.error("Error reading package.json file:", error);
process.exit(1);
}
}
var PACKAGE_JSON = "package.json";
function findPackageJson(startDir) {
let currentDir = startDir;
while (true) {
const packageJsonPath = path.join(currentDir, PACKAGE_JSON);
if (fs.existsSync(packageJsonPath)) {
return packageJsonPath;
}
const parentDir = path.resolve(currentDir, "..");
if (parentDir === currentDir) {
break;
}
currentDir = parentDir;
}
return null;
}
var hasTypeScript = isPackageInstalled("typescript");
var hasTailwindcss = isPackageInstalled("tailwindcss");
var hasReact = isPackageInstalled("react");
var hasNext = isPackageInstalled("next");
var hasTestingLibrary = isPackageInstalled("@testing-library/dom");
var hasJestDom = isPackageInstalled("@testing-library/jest-dom");
var hasVitest = isPackageInstalled("vitest");
var hasPlaywright = isPackageInstalled("@playwright/test");
var hasStorybook = isPackageInstalled("storybook");
var typeScriptExtensions = [".ts", ".cts", ".mts", ".tsx"];
var allExtensions = [...typeScriptExtensions, ".js", ".jsx", ".mjs", ".cjs"];
var vitestFiles = ["**/__tests__/**/*", "**/*.test.*"];
var testFiles = ["**/tests/**", ...vitestFiles];
var playwrightFiles = ["**/e2e/**", "**/*.e2e.*"];
function showFeaturesTable() {
const tableData = [
{ Name: "TypeScript", Status: hasTypeScript ? "\u2714\uFE0F" : "\u274C" },
{ Name: "React", Status: hasReact ? "\u2714\uFE0F" : "\u274C" },
{ Name: "Next", Status: hasNext ? "\u2714\uFE0F" : "\u274C" },
{ Name: "Testing Library", Status: hasTestingLibrary ? "\u2714\uFE0F" : "\u274C" },
{ Name: "Jest Dom", Status: hasJestDom ? "\u2714\uFE0F" : "\u274C" },
{ Name: "Vitest", Status: hasVitest ? "\u2714\uFE0F" : "\u274C" },
{ Name: "Playwright", Status: hasPlaywright ? "\u2714\uFE0F" : "\u274C" },
{ Name: "Storybook", Status: hasStorybook ? "\u2714\uFE0F" : "\u274C" }
].sort((a, b) => {
const aHasCheck = a.Status.includes("\u2714\uFE0F");
const bHasCheck = b.Status.includes("\u2714\uFE0F");
if (aHasCheck && !bHasCheck) {
return -1;
} else if (!aHasCheck && bHasCheck) {
return 1;
} else {
return 0;
}
});
logger.log("Hello There!\nHere are the features detected in your project:");
logger.table(tableData);
logger.log(`Dear Developer
`);
logger.log("Thanks a lot for using '@szum-tech/eslint-config'");
logger.log("If you like it, leave a star \u2B50 \u{1F449} https://github.com/JanSzewczyk/handy-szumrak");
logger.log("And recommend to others\n");
logger.log(`May the SZUMRAK be with You \u{1F680}\u{1F680}\u{1F680}`);
}
showFeaturesTable();
var ERROR = "error";
var WARN = "warn";
var OFF = "off";
var config = [
{
name: "eslint/ignores",
ignores: [
"**/.cache/**",
"**/node_modules/**",
"**/build/**",
"**/public/build/**",
"**/playwright-report/**",
"**/server-build/**",
"**/dist/**",
"**/.next/**",
"**/storybook-static/**"
]
},
{
name: "eslint/config/base&import",
plugins: {
import: importPlugin__namespace
},
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: {
...globals__default.default.browser,
...globals__default.default.node
},
parserOptions: {
warnOnUnsupportedTypeScriptVersion: false
}
},
settings: hasTypeScript ? {
"import/extensions": allExtensions,
"import/external-module-folders": ["node_modules", "node_modules/@types"],
"import/parsers": {
"@typescript-eslint/parser": typeScriptExtensions
},
"import/resolver": {
node: {
extensions: allExtensions
}
}
} : {},
rules: {
"no-unexpected-multiline": ERROR,
"no-warning-comments": [ERROR, { terms: ["FIXME"], location: "anywhere" }],
"no-console": WARN,
"no-unused-vars": [
WARN,
{
args: "all",
argsIgnorePattern: "^_",
ignoreRestSiblings: true,
varsIgnorePattern: "^ignored"
}
],
// analysis/correctness
"import/default": ERROR,
"import/namespace": ERROR,
"import/export": ERROR,
"import/no-unresolved": OFF,
"import/named": OFF,
// red flags (thus, warnings)
"import/consistent-type-specifier-style": [WARN, "prefer-inline"],
"import/no-named-as-default": WARN,
"import/no-named-as-default-member": WARN,
"import/no-duplicates": [WARN, { "prefer-inline": true }],
"import/order": [
WARN,
{
groups: ["builtin", "external", "internal", "parent", "sibling", "index"],
pathGroups: [
{
pattern: "react",
group: "external",
position: "before"
},
{ pattern: "*/**", group: "internal" }
],
pathGroupsExcludedImportTypes: ["react"],
"newlines-between": "always",
alphabetize: {
order: "asc",
caseInsensitive: true
}
}
]
}
},
hasReact ? {
name: "eslint/config/react&react-hooks",
files: ["**/*.tsx", "**/*.jsx"],
plugins: {
react: reactPlugin__default.default,
"react-hooks": reactHooksPlugin__namespace
},
languageOptions: {
parser: tsEslint__default.default.parser,
parserOptions: {
jsx: true
}
},
settings: {
react: {
version: "detect"
}
},
rules: {
"react/display-name": ERROR,
"react/jsx-no-comment-textnodes": ERROR,
"react/jsx-no-duplicate-props": ERROR,
"react/jsx-no-target-blank": ERROR,
"react/jsx-no-undef": ERROR,
"react/jsx-uses-react": ERROR,
"react/jsx-uses-vars": ERROR,
"react/no-children-prop": ERROR,
"react/no-danger-with-children": ERROR,
"react/no-deprecated": ERROR,
"react/no-direct-mutation-state": ERROR,
"react/no-find-dom-node": ERROR,
"react/no-is-mounted": ERROR,
"react/no-render-return-value": ERROR,
"react/no-unescaped-entities": ERROR,
"react/no-unknown-property": ERROR,
"react/require-render-return": ERROR,
"react/jsx-key": WARN,
"react/react-in-jsx-scope": OFF,
"react/no-unsafe": OFF,
"react-hooks/rules-of-hooks": ERROR,
"react-hooks/exhaustive-deps": WARN
}
} : null,
hasTailwindcss ? {
name: "eslint/config/tailwindcss",
plugins: {
tailwindcss: tailwindcssPlugin__default.default
},
languageOptions: {
parserOptions: {
ecmaFeatures: {
jsx: true
}
}
},
rules: {
"tailwindcss/no-contradicting-classname": ERROR,
"tailwindcss/classnames-order": WARN,
"tailwindcss/enforces-negative-arbitrary-values": WARN,
"tailwindcss/enforces-shorthand": WARN,
"tailwindcss/migration-from-tailwind-2": WARN,
"tailwindcss/no-custom-classname": WARN,
"tailwindcss/no-unnecessary-arbitrary-value": WARN,
"tailwindcss/no-arbitrary-value": OFF
}
} : null,
hasNext ? {
name: "eslint/config/next",
files: ["**/*.ts?(x)", "**/*.js?(x)"],
plugins: {
"@next/next": nextPlugin__default.default
},
rules: {
"@next/next/inline-script-id": ERROR,
"@next/next/no-assign-module-variable": ERROR,
"@next/next/no-document-import-in-page": ERROR,
"@next/next/no-duplicate-head": ERROR,
"@next/next/no-head-import-in-document": ERROR,
"@next/next/no-script-component-in-head": ERROR,
"@next/next/google-font-display": WARN,
"@next/next/google-font-preconnect": WARN,
"@next/next/next-script-for-ga": WARN,
"@next/next/no-async-client-component": WARN,
"@next/next/no-before-interactive-script-outside-document": WARN,
"@next/next/no-css-tags": WARN,
"@next/next/no-head-element": WARN,
"@next/next/no-html-link-for-pages": WARN,
"@next/next/no-img-element": WARN,
"@next/next/no-page-custom-font": WARN,
"@next/next/no-styled-jsx-in-document": WARN,
"@next/next/no-sync-scripts": WARN,
"@next/next/no-title-in-document-head": WARN,
"@next/next/no-typos": WARN,
"@next/next/no-unwanted-polyfillio": WARN
}
} : null,
hasTypeScript ? {
name: "eslint/config/typescript",
files: ["**/*.ts?(x)"],
languageOptions: {
parser: tsEslint__default.default.parser,
parserOptions: {
projectService: true
},
sourceType: "module"
},
plugins: {
"@typescript-eslint": tsEslint__default.default.plugin
},
rules: {
"no-unused-expressions": OFF,
"no-array-constructor": OFF,
"no-unused-vars": OFF,
"@typescript-eslint/no-misused-promises": [ERROR, { checksVoidReturn: false }],
"@typescript-eslint/no-floating-promises": ERROR,
"@typescript-eslint/ban-ts-comment": ERROR,
"@typescript-eslint/no-array-constructor": ERROR,
"@typescript-eslint/no-duplicate-enum-values": ERROR,
"@typescript-eslint/no-explicit-any": ERROR,
"@typescript-eslint/no-extra-non-null-assertion": ERROR,
"@typescript-eslint/no-misused-new": ERROR,
"@typescript-eslint/no-namespace": ERROR,
"@typescript-eslint/no-non-null-asserted-optional-chain": ERROR,
"@typescript-eslint/no-require-imports": ERROR,
"@typescript-eslint/no-this-alias": ERROR,
"@typescript-eslint/no-unnecessary-type-constraint": ERROR,
"@typescript-eslint/no-unsafe-declaration-merging": ERROR,
"@typescript-eslint/no-unsafe-function-type": ERROR,
"@typescript-eslint/no-wrapper-object-types": ERROR,
"@typescript-eslint/prefer-as-const": "error",
"@typescript-eslint/prefer-namespace-keyword": ERROR,
"@typescript-eslint/triple-slash-reference": ERROR,
"@typescript-eslint/no-empty-object-type": WARN,
"@typescript-eslint/no-unused-vars": [
WARN,
{
args: "all",
argsIgnorePattern: "^_",
caughtErrors: "all",
caughtErrorsIgnorePattern: "^_",
destructuredArrayIgnorePattern: "^_",
varsIgnorePattern: "^_",
ignoreRestSiblings: true
}
],
"@typescript-eslint/consistent-type-imports": [
WARN,
{
prefer: "type-imports",
disallowTypeAnnotations: true,
fixStyle: "inline-type-imports"
}
]
}
} : null,
hasTestingLibrary ? {
name: "eslint/config/testing-library",
files: testFiles,
ignores: playwrightFiles,
plugins: {
"testing-library": testingLibraryPlugin__default.default
},
rules: {
"testing-library/no-unnecessary-act": [ERROR, { isStrict: false }],
"testing-library/no-wait-for-side-effects": ERROR,
"testing-library/prefer-find-by": ERROR
}
} : null,
hasJestDom ? {
name: "eslint/config/jest-dom",
files: testFiles,
ignores: playwrightFiles,
plugins: {
"jest-dom": jestDomPlugin__default.default
},
rules: {
"jest-dom/prefer-checked": ERROR,
"jest-dom/prefer-enabled-disabled": ERROR,
"jest-dom/prefer-focus": ERROR,
"jest-dom/prefer-empty": ERROR,
"jest-dom/prefer-to-have-value": ERROR,
"jest-dom/prefer-to-have-text-content": ERROR,
"jest-dom/prefer-required": ERROR
}
} : null,
hasVitest ? {
name: "eslint/config/vitest",
files: testFiles,
ignores: playwrightFiles,
plugins: {
vitest: vitestPlugin__default.default
},
settings: {
vitest: {
typecheck: hasTypeScript
}
},
languageOptions: {
globals: {
...vitestPlugin__default.default.environments.env.globals
}
},
rules: {
"vitest/expect-expect": ERROR,
"vitest/no-identical-title": ERROR,
"vitest/no-commented-out-tests": ERROR,
"vitest/valid-title": ERROR,
"vitest/valid-expect": ERROR,
"vitest/valid-describe-callback": ERROR,
"vitest/require-local-test-context-for-concurrent-snapshots": ERROR,
"vitest/no-import-node-test": ERROR,
"vitest/no-focused-tests": [WARN, { fixable: false }]
}
} : null,
hasPlaywright ? {
name: "eslint/config/playwright",
files: playwrightFiles,
plugins: {
playwright: playwrightPlugin__default.default
},
languageOptions: {
globals: globals__default.default["shared-node-browser"]
},
rules: {
"no-empty-pattern": OFF,
"playwright/missing-playwright-await": ERROR,
"playwright/no-focused-test": ERROR,
"playwright/no-networkidle": ERROR,
"playwright/no-unsafe-references": ERROR,
"playwright/valid-describe-callback": ERROR,
"playwright/valid-expect": ERROR,
"playwright/valid-expect-in-promise": ERROR,
"playwright/valid-title": ERROR,
"playwright/prefer-web-first-assertions": ERROR,
"playwright/no-standalone-expect": ERROR,
"playwright/expect-expect": WARN,
"playwright/max-nested-describe": WARN,
"playwright/no-conditional-expect": WARN,
"playwright/no-conditional-in-test": WARN,
"playwright/no-element-handle": WARN,
"playwright/no-eval": WARN,
"playwright/no-force-option": WARN,
"playwright/no-nested-step": WARN,
"playwright/no-page-pause": WARN,
"playwright/no-skipped-test": WARN,
"playwright/no-useless-await": WARN,
"playwright/no-useless-not": WARN,
"playwright/no-wait-for-selector": WARN,
"playwright/no-wait-for-timeout": WARN
}
} : null,
hasStorybook ? {
name: "eslint/config/storybook",
plugins: {
storybook: storybookPlugin__default.default
}
} : null,
hasStorybook ? {
name: "eslint/config/storybook/stories",
files: ["**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)", "**/*.story.@(ts|tsx|js|jsx|mjs|cjs)"],
rules: {
"storybook/await-interactions": ERROR,
"storybook/context-in-play-function": ERROR,
"storybook/default-exports": ERROR,
"storybook/story-exports": ERROR,
"storybook/use-storybook-expect": ERROR,
"storybook/use-storybook-testing-library": ERROR,
"storybook/no-redundant-story-name": WARN,
"storybook/prefer-pascal-case": WARN,
"storybook/hierarchy-separator": WARN,
"react-hooks/rules-of-hooks": OFF,
"import/no-anonymous-default-export": OFF
}
} : null,
hasStorybook ? {
name: "eslint/config/storybook/main",
files: [".storybook/main.@(js|cjs|mjs|ts|tsx)"],
rules: {
"storybook/no-uninstalled-addons": ERROR
}
} : null
].filter(Boolean);
var index_default = config;
module.exports = index_default;