feat: initial dev-configs monorepo
Shared configs for TypeScript projects: ESLint, Prettier, TypeScript, Vite, Jest, Playwright, Knip. Published as @vigdorov/* npm packages to Gitea registry. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
36
packages/eslint/package.json
Normal file
36
packages/eslint/package.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "@vigdorov/eslint-config",
|
||||
"version": "1.0.0",
|
||||
"description": "Shared ESLint configuration with flat config",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": ["dist"],
|
||||
"exports": {
|
||||
".": "./dist/index.js",
|
||||
"./base": "./dist/base.js",
|
||||
"./react": "./dist/react.js",
|
||||
"./node": "./dist/node.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">=9.0.0",
|
||||
"typescript": ">=5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"typescript-eslint": "^8.32.1",
|
||||
"@stylistic/eslint-plugin": "^4.2.0",
|
||||
"eslint-plugin-unused-imports": "^4.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^9.27.0",
|
||||
"typescript": "^5.8.3",
|
||||
"@types/eslint": "^9.6.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^5.2.0"
|
||||
}
|
||||
}
|
||||
30
packages/eslint/src/base.ts
Normal file
30
packages/eslint/src/base.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import tseslint from 'typescript-eslint';
|
||||
import stylistic from '@stylistic/eslint-plugin';
|
||||
import unusedImports from 'eslint-plugin-unused-imports';
|
||||
import {qualityRules, typescriptRules, unusedImportsRules, stylisticRules} from './rules.js';
|
||||
|
||||
type ConfigArray = ReturnType<typeof tseslint.config>;
|
||||
type ConfigOverride = (configs: ConfigArray) => ConfigArray;
|
||||
|
||||
export function base(override: ConfigOverride = (c) => c): ConfigArray {
|
||||
const configs = tseslint.config(
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
plugins: {
|
||||
'@stylistic': stylistic,
|
||||
'unused-imports': unusedImports,
|
||||
},
|
||||
rules: {
|
||||
...qualityRules,
|
||||
...typescriptRules,
|
||||
...unusedImportsRules,
|
||||
...stylisticRules,
|
||||
},
|
||||
},
|
||||
{
|
||||
ignores: ['dist/', 'node_modules/', 'coverage/'],
|
||||
},
|
||||
);
|
||||
|
||||
return override(configs);
|
||||
}
|
||||
3
packages/eslint/src/index.ts
Normal file
3
packages/eslint/src/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export {base} from './base.js';
|
||||
export {react} from './react.js';
|
||||
export {node} from './node.js';
|
||||
9
packages/eslint/src/node.ts
Normal file
9
packages/eslint/src/node.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import tseslint from 'typescript-eslint';
|
||||
import {base} from './base.js';
|
||||
|
||||
type ConfigArray = ReturnType<typeof tseslint.config>;
|
||||
type ConfigOverride = (configs: ConfigArray) => ConfigArray;
|
||||
|
||||
export function node(override: ConfigOverride = (c) => c): ConfigArray {
|
||||
return base(override);
|
||||
}
|
||||
33
packages/eslint/src/react.ts
Normal file
33
packages/eslint/src/react.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import tseslint from 'typescript-eslint';
|
||||
import reactPlugin from 'eslint-plugin-react';
|
||||
import reactHooksPlugin from 'eslint-plugin-react-hooks';
|
||||
import {base} from './base.js';
|
||||
import {reactRules, reactHooksRules} from './rules.js';
|
||||
|
||||
type ConfigArray = ReturnType<typeof tseslint.config>;
|
||||
type ConfigOverride = (configs: ConfigArray) => ConfigArray;
|
||||
|
||||
export function react(override: ConfigOverride = (c) => c): ConfigArray {
|
||||
const baseConfigs = base();
|
||||
|
||||
const configs = tseslint.config(
|
||||
...baseConfigs,
|
||||
{
|
||||
plugins: {
|
||||
'react': reactPlugin,
|
||||
'react-hooks': reactHooksPlugin,
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect',
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
...reactRules,
|
||||
...reactHooksRules,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
return override(configs);
|
||||
}
|
||||
91
packages/eslint/src/rules.ts
Normal file
91
packages/eslint/src/rules.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import type {Linter} from 'eslint';
|
||||
|
||||
type Rules = Partial<Linter.RulesRecord>;
|
||||
|
||||
export const qualityRules: Rules = {
|
||||
'eqeqeq': 'error',
|
||||
'no-console': ['warn', {allow: ['warn', 'error']}],
|
||||
'no-alert': 'warn',
|
||||
'no-param-reassign': ['error', {props: true}],
|
||||
'no-useless-concat': 'warn',
|
||||
'no-else-return': 'warn',
|
||||
'no-lonely-if': 'warn',
|
||||
'no-constructor-return': 'warn',
|
||||
'no-sequences': 'warn',
|
||||
'prefer-promise-reject-errors': 'warn',
|
||||
'require-await': 'warn',
|
||||
'no-new': 'warn',
|
||||
'no-multi-str': 'warn',
|
||||
'no-multi-assign': 'warn',
|
||||
'no-nested-ternary': 'warn',
|
||||
'no-useless-computed-key': 'warn',
|
||||
'no-useless-constructor': 'warn',
|
||||
'no-var': 'warn',
|
||||
'no-duplicate-imports': 'warn',
|
||||
'no-plusplus': 'warn',
|
||||
'no-bitwise': 'warn',
|
||||
'prefer-const': 'warn',
|
||||
'prefer-rest-params': 'warn',
|
||||
'prefer-template': 'warn',
|
||||
'array-callback-return': ['warn', {allowImplicit: true, checkForEach: true}],
|
||||
'default-param-last': 'warn',
|
||||
'yoda': 'warn',
|
||||
};
|
||||
|
||||
export const typescriptRules: Rules = {
|
||||
'@typescript-eslint/no-explicit-any': 'error',
|
||||
'@typescript-eslint/no-empty-object-type': 'error',
|
||||
'@typescript-eslint/no-unsafe-function-type': 'error',
|
||||
'@typescript-eslint/no-wrapper-object-types': 'error',
|
||||
'@typescript-eslint/no-use-before-define': 'warn',
|
||||
'@typescript-eslint/no-shadow': 'warn',
|
||||
'no-shadow': 'off',
|
||||
};
|
||||
|
||||
export const unusedImportsRules: Rules = {
|
||||
'unused-imports/no-unused-imports': 'warn',
|
||||
'unused-imports/no-unused-vars': [
|
||||
'warn',
|
||||
{
|
||||
vars: 'all',
|
||||
varsIgnorePattern: '^_',
|
||||
args: 'after-used',
|
||||
argsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const stylisticRules: Rules = {
|
||||
'@stylistic/no-multiple-empty-lines': ['warn', {max: 1}],
|
||||
'@stylistic/lines-between-class-members': ['warn', 'always'],
|
||||
'@stylistic/line-comment-position': ['warn', {position: 'above'}],
|
||||
'@stylistic/multiline-comment-style': ['warn', 'starred-block'],
|
||||
'@stylistic/capitalized-comments': 'warn',
|
||||
'@stylistic/max-len': [
|
||||
'warn',
|
||||
{
|
||||
code: 120,
|
||||
ignoreComments: true,
|
||||
ignoreUrls: true,
|
||||
ignoreStrings: true,
|
||||
ignoreTemplateLiterals: true,
|
||||
ignoreRegExpLiterals: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const reactRules: Rules = {
|
||||
'react/prop-types': 'off',
|
||||
'react/react-in-jsx-scope': 'off',
|
||||
'react/jsx-filename-extension': ['warn', {extensions: ['.tsx']}],
|
||||
'react/jsx-props-no-spreading': 'warn',
|
||||
'react/jsx-key': 'warn',
|
||||
'react/no-array-index-key': 'warn',
|
||||
'react/destructuring-assignment': 'warn',
|
||||
'react/prefer-stateless-function': 'warn',
|
||||
'react/jsx-fragments': ['off', 'element'],
|
||||
};
|
||||
|
||||
export const reactHooksRules: Rules = {
|
||||
'react-hooks/exhaustive-deps': 'warn',
|
||||
};
|
||||
8
packages/eslint/tsconfig.json
Normal file
8
packages/eslint/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
Reference in New Issue
Block a user