diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 8f46c26..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,56 +0,0 @@ -/** @type {import('eslint').Linter.Config} */ -module.exports = { - rules: { - '@typescript-eslint/no-unsafe-enum-comparison': 'off', - '@typescript-eslint/consistent-type-definitions': 'off', - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-var-requires': 'warn', - '@typescript-eslint/no-unused-vars': ['error'], - '@typescript-eslint/no-floating-promises': ['error'], - '@typescript-eslint/no-explicit-any': ['error', { fixToUnknown: true }], - 'no-constant-binary-expression': 'error', - 'array-callback-return': 'error', - 'no-debugger': 'error', - 'no-alert': 'error', - 'no-console': ['error', { allow: ['debug', 'info', 'warn', 'error', 'trace', 'time', 'timeEnd'] }], - 'newline-before-return': 'error', - 'prefer-const': 'error', - 'no-else-return': 'error', - 'no-extra-semi': 'error', - curly: 'error', - eqeqeq: 'error', - 'default-case-last': 'error', - 'prettier/prettier': [ - 'error', - { - endOfLine: 'auto', - }, - ], - }, - env: { - es6: true, - browser: true, - node: true, - }, - parserOptions: { - ecmaVersion: 2018, - sourceType: 'module', - project: true, - }, - extends: [ - 'eslint:recommended', - 'plugin:prettier/recommended', - 'plugin:import/errors', - 'plugin:import/warnings', - 'plugin:import/typescript', - 'plugin:@typescript-eslint/recommended-type-checked', - 'plugin:@typescript-eslint/stylistic-type-checked', - ], - globals: { - Atomics: 'readonly', - SharedArrayBuffer: 'readonly', - }, - parser: '@typescript-eslint/parser', - plugins: ['@typescript-eslint', 'prettier'], -}; diff --git a/.eslintrc.react.js b/.eslintrc.react.js deleted file mode 100644 index 8a981f5..0000000 --- a/.eslintrc.react.js +++ /dev/null @@ -1,30 +0,0 @@ -/** @type {import('eslint').Linter.Config} */ -module.exports = { - rules: { - 'react/react-in-jsx-scope': 'off', - 'react/prop-types': 'off', - 'react/display-name': 'off', - 'react/forbid-component-props': ['warn', { forbid: ['style', 'className'] }], - '@typescript-eslint/no-misused-promises': [ - 'error', - { - checksVoidReturn: { - attributes: false, - }, - }, - ], - }, - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - }, - extends: ['./index', 'plugin:react/recommended'], - settings: { - react: { - pragma: 'React', - version: 'detect', - }, - }, - plugins: ['react', 'react-hooks'], -}; diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 542390a..b6838c2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,6 +13,13 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-node@v4 + with: + node-version: '22.x' + - run: npm install + - run: npm run build - name: Semantic Release uses: cycjimmy/semantic-release-action@v4 with: diff --git a/.gitignore b/.gitignore index 49d2b85..6682568 100644 --- a/.gitignore +++ b/.gitignore @@ -5,39 +5,8 @@ npm-debug.log* yarn-debug.log* yarn-error.log* -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - # Dependency directories node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ # Optional npm cache directory .npm @@ -45,20 +14,11 @@ typings/ # Optional eslint cache .eslintcache -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env - # next.js build output .next # NPM lock file package-lock.json + +# Distribution directory +dist diff --git a/.npmignore b/.npmignore deleted file mode 100644 index 8d5b334..0000000 --- a/.npmignore +++ /dev/null @@ -1,8 +0,0 @@ -package-lock.json -npm-debug.log* -node_modules/ -.npm -.eslintcache -.editorconfig -.gitignore -.github diff --git a/README.md b/README.md index 44ec663..109ec13 100644 --- a/README.md +++ b/README.md @@ -10,24 +10,28 @@ $ npm install eslint @smartive/eslint-config -D ## Usage -Create a `.eslintrc` file in the root of your project's directory (it should live where `package.json` does). This package offers two different rulesets, on for plain TypeScript applications (`@smartive/eslint-config`) and a separate one for React applications (`@smartive/eslint-config`). Your `.eslintrc` file should look like this: +This package offers two different rule sets, one for plain TypeScript applications and a separate one for React applications. -### Plain TypeScript applications +### Flat Config (`eslint.config.mjs`) -``` -{ - "extends": [ - "@smartive/eslint-config" - ] -} -``` +```javascript +import { configs } from '@smartive/eslint-config' -### React applications +// For plain TS applications .. +export default configs.typescript; +// .. or React applications +export default configs.react; ``` + +### Legacy Config (`.eslintrc`) + +```json { "extends": [ - "@smartive/eslint-config/react" + "@smartive/eslint-config/typescript-legacy" // Plain TS applications + // or + "@smartive/eslint-config/react-legacy" // React applications ] } ``` @@ -42,7 +46,3 @@ To use eslint add the following to your package.json: "lint:fix": "eslint . --fix" } ``` - -### TypeScript configuration - -Since there are some rules which require type information please make sure to set up a `tsconfig.json` configuration file in the root directory of your project. If your TypeScript configuration file is placed in another location you have to configure it using `parserOptions.project` in your ESLint configuration file. For more information have a look at the [typescript-eslint documentation](https://typescript-eslint.io/packages/parser/#project). diff --git a/index.js b/index.js deleted file mode 100644 index bd66436..0000000 --- a/index.js +++ /dev/null @@ -1,3 +0,0 @@ -const eslintrc = require('./.eslintrc'); - -module.exports = eslintrc; diff --git a/package.json b/package.json index f5d41c4..ff4c5f6 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,26 @@ "name": "@smartive/eslint-config", "version": "0.0.0-development", "description": "ESLint configuration by smartive", - "main": "index.js", + "files": [ + "README.md", + "LICENSE", + "dist" + ], + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/esm/index.js", + "require": "./dist/cjs/index.js" + }, + "./react-legacy": { + "import": "./dist/esm/react-legacy.js", + "require": "./dist/cjs/react-legacy.js" + }, + "./typescript-legacy": { + "import": "./dist/esm/typescript-legacy.js", + "require": "./dist/cjs/typescript-legacy.js" + } + }, "repository": { "type": "git", "url": "git+https://github.com/smartive/eslint-config.git" @@ -20,26 +39,37 @@ }, "homepage": "https://github.com/smartive/eslint-config#readme", "dependencies": { - "@typescript-eslint/eslint-plugin": "^6.7.4", - "@typescript-eslint/parser": "^6.7.4", - "eslint-config-prettier": "^9.0.0", - "eslint-plugin-import": "^2.25.3", - "eslint-plugin-prettier": ">=4.0.0 <6", - "eslint-plugin-react": "^7.27.0", - "eslint-plugin-react-hooks": "^4.3.0" + "@typescript-eslint/eslint-plugin": "^8.15.0", + "@typescript-eslint/parser": "^8.15.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-prettier": "^5.2.1", + "eslint-plugin-react": "^7.37.2", + "eslint-plugin-react-hooks": "^5.0.0", + "globals": "^15.13.0", + "typescript-eslint": "^8.15.0" }, "peerDependencies": { - "eslint": "^8.0.0" + "eslint": "^8.57.0 || ^9.0.0" }, "devDependencies": { - "@commitlint/cli": "^19.0.0", - "@commitlint/config-conventional": "^19.0.0", + "@commitlint/cli": "^19.6.0", + "@commitlint/config-conventional": "^19.6.0", + "@eslint/js": "^9.15.0", "@smartive/prettier-config": "^3.0.0", + "@types/node": "^22.9.1", "cz-conventional-changelog": "^3.3.0", - "husky": "^9.0.0", - "prettier": "^3.0.0" + "esbuild": "0.24.0", + "eslint": "^9.15.0", + "husky": "^9.1.7", + "prettier": "^3.3.3", + "typescript": "^5.6.3" }, "scripts": { + "build:types": "tsc --declaration --emitDeclarationOnly", + "build:cjs": "esbuild --platform=browser --format=cjs --outdir=dist/cjs/ ./src/*.ts", + "build:mjs": "esbuild --platform=neutral --format=esm --outdir=dist/esm/ ./src/*.ts", + "build": "rm -rf dist && npm run build:cjs && npm run build:mjs && npm run build:types", "commitlint": "commitlint --edit", "prepare": "[ ! -f node_modules/.bin/husky ] || husky" }, diff --git a/react.js b/react.js deleted file mode 100644 index a96a084..0000000 --- a/react.js +++ /dev/null @@ -1,3 +0,0 @@ -const eslintrc = require('./.eslintrc.react'); - -module.exports = eslintrc; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..dc3cb8e --- /dev/null +++ b/src/index.ts @@ -0,0 +1,122 @@ +import js from '@eslint/js'; +import type { Linter } from 'eslint'; +import { flatConfigs as eslintPluginImportConfigs } from 'eslint-plugin-import'; +import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; +import reactPlugin from 'eslint-plugin-react'; +import globals from 'globals'; +import tsEslint from 'typescript-eslint'; + +const reactRules: Linter.RulesRecord = { + 'react/prop-types': 'off', + 'react/display-name': 'off', + 'react/forbid-component-props': ['warn', { forbid: ['style', 'className'] }], + '@typescript-eslint/no-misused-promises': [ + 'error', + { + checksVoidReturn: { + attributes: false, + }, + }, + ], +}; + +const rules = (react: boolean): Linter.RulesRecord => ({ + '@typescript-eslint/no-unsafe-enum-comparison': 'off', + '@typescript-eslint/consistent-type-definitions': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-var-requires': 'warn', + '@typescript-eslint/no-unused-vars': ['error'], + '@typescript-eslint/no-floating-promises': ['error'], + '@typescript-eslint/no-explicit-any': ['error', { fixToUnknown: true }], + 'no-constant-binary-expression': 'error', + 'array-callback-return': 'error', + 'no-debugger': 'error', + 'no-alert': 'error', + 'no-console': ['error', { allow: ['debug', 'info', 'warn', 'error', 'trace', 'time', 'timeEnd'] }], + 'newline-before-return': 'error', + 'prefer-const': 'error', + 'no-else-return': 'error', + 'no-extra-semi': 'error', + curly: 'error', + eqeqeq: 'error', + 'default-case-last': 'error', + 'prettier/prettier': [ + 'error', + { + endOfLine: 'auto', + }, + ], + ...(react ? reactRules : {}), +}); + +export const generateLegacyConfig = (react: boolean): Linter.LegacyConfig => ({ + rules: rules(react), + env: { + es2020: true, + browser: true, + node: true, + }, + parserOptions: { + ecmaVersion: 2020, + sourceType: 'module', + projectService: true, + }, + extends: [ + 'eslint:recommended', + 'plugin:prettier/recommended', + 'plugin:import/errors', + 'plugin:import/warnings', + 'plugin:import/typescript', + 'plugin:@typescript-eslint/recommended-type-checked', + 'plugin:@typescript-eslint/stylistic-type-checked', + ...(react ? ['plugin:react/recommended', 'plugin:react/jsx-runtime'] : []), + ], + globals: { + Atomics: 'readonly', + SharedArrayBuffer: 'readonly', + }, + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint', 'prettier'], +}); + +const flatConfigTypescript = tsEslint.config( + js.configs.recommended, + eslintPluginPrettierRecommended, + eslintPluginImportConfigs.errors, + eslintPluginImportConfigs.warnings, + eslintPluginImportConfigs.typescript, + tsEslint.configs.recommendedTypeChecked, + tsEslint.configs.stylisticTypeChecked, + { + rules: rules(false), + languageOptions: { + ecmaVersion: 2020, + sourceType: 'module', + globals: { + ...globals.browser, + ...globals.node, + ...globals.es2020, + Atomics: 'readonly', + SharedArrayBuffer: 'readonly', + }, + parserOptions: { + ecmaVersion: 2020, + sourceType: 'module', + projectService: true, + }, + }, + }, +); + +const flatConfigReact = tsEslint.config( + flatConfigTypescript, + reactPlugin.configs.flat!.recommended as unknown as Linter.Config, + reactPlugin.configs.flat!['jsx-runtime'] as unknown as Linter.Config, + { rules: reactRules }, +); + +export const configs = { + typescript: flatConfigTypescript, + react: flatConfigReact, +}; diff --git a/src/react-legacy.ts b/src/react-legacy.ts new file mode 100644 index 0000000..c110ead --- /dev/null +++ b/src/react-legacy.ts @@ -0,0 +1,3 @@ +import { generateLegacyConfig } from './'; + +module.exports = generateLegacyConfig(true); diff --git a/src/typescript-legacy.ts b/src/typescript-legacy.ts new file mode 100644 index 0000000..83deb11 --- /dev/null +++ b/src/typescript-legacy.ts @@ -0,0 +1,3 @@ +import { generateLegacyConfig } from './'; + +module.exports = generateLegacyConfig(false); diff --git a/src/typings/eslint-plugin-import.d.ts b/src/typings/eslint-plugin-import.d.ts new file mode 100644 index 0000000..ab5073f --- /dev/null +++ b/src/typings/eslint-plugin-import.d.ts @@ -0,0 +1,35 @@ +import type { ESLint, Linter, Rule } from 'eslint'; + +declare module 'eslint-plugin-import' { + export = plugin; +} + +const plugin: ESLint.Plugin & { + meta: { + name: string; + version: string; + }; + configs: { + recommended: Linter.LegacyConfig; + errors: Linter.LegacyConfig; + warnings: Linter.LegacyConfig; + 'stage-0': Linter.LegacyConfig; + react: Linter.LegacyConfig; + 'react-native': Linter.LegacyConfig; + electron: Linter.LegacyConfig; + typescript: Linter.LegacyConfig; + }; + flatConfigs: { + recommended: Linter.FlatConfig; + errors: Linter.FlatConfig; + warnings: Linter.FlatConfig; + 'stage-0': Linter.FlatConfig; + react: Linter.FlatConfig; + 'react-native': Linter.FlatConfig; + electron: Linter.FlatConfig; + typescript: Linter.FlatConfig; + }; + rules: { + [key: string]: Rule.RuleModule; + }; +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..00df34f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "typeRoots": ["./src/typings","node_modules/@types"], + "declaration": true, + "outDir": "./dist", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + }, + "include": ["src/index.ts"], + "exclude": ["node_modules", "dist"] +}