This is the full developer documentation for jmlweb-tooling # jmlweb-tooling > Shared configuration packages for modern JavaScript and TypeScript projects # jmlweb-tooling [Section titled “jmlweb-tooling”](#jmlweb-tooling) A collection of shared configuration packages for modern JavaScript and TypeScript projects. These packages provide consistent, opinionated configurations for popular development tools. ## Package Categories [Section titled “Package Categories”](#package-categories) Oxfmt Fast code formatting with import sorting and Tailwind CSS class sorting. Recommended for new projects. Oxlint Fast linting for JavaScript, TypeScript, React and Node.js, with type-aware rules. Recommended for new projects. Prettier and ESLint The previous formatter and linter configurations, maintained for existing projects. ESLint still lints Astro files. TypeScript Strict TypeScript configurations for various project types including React, Next.js, and Node.js. Testing Pre-configured setups for Vitest and Jest testing frameworks. Build Tools Build configurations for tsdown and Vite with optimized defaults. Commit Commitlint configuration following Conventional Commits specification. ## Features [Section titled “Features”](#features) * **Zero Configuration**: Drop-in configurations that work out of the box * **Opinionated**: Sensible defaults based on industry best practices * **Extensible**: Easy to override and customize for your specific needs * **Type-Safe**: Full TypeScript support across all packages * **Monorepo Friendly**: Designed to work seamlessly in monorepo setups ## Quick Start [Section titled “Quick Start”](#quick-start) All packages are published to npm under the `@jmlweb` scope. Install the configurations you need: ```bash # Formatter pnpm add -D @jmlweb/oxfmt-config-base oxfmt # Linter pnpm add -D @jmlweb/oxlint-config-base oxlint oxlint-tsgolint # TypeScript configuration pnpm add -D @jmlweb/tsconfig-base typescript ``` See [Getting Started](getting-started/) for the setup, or [Migrate to Oxc](guides/migrate-to-oxc/) to move a project from Prettier and ESLint. ## Repository [Section titled “Repository”](#repository) All packages are maintained in a single monorepo: [github.com/jmlweb/tooling](https://github.com/jmlweb/tooling) Report issues and contribute at: [github.com/jmlweb/tooling/issues](https://github.com/jmlweb/tooling/issues) # base > Base tsup configuration for jmlweb projects with sensible defaults [![npm version](https://img.shields.io/npm/v/@jmlweb/tsup-config-base)](https://www.npmjs.com/package/@jmlweb/tsup-config-base) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D22.12.0-339933.svg)](https://nodejs.org/) [![tsup](https://img.shields.io/badge/tsup-8.0%2B-yellow.svg)](https://tsup.egoist.dev/) \> \[!WARNING] > **Deprecated and no longer maintained.** tsup is maintenance-only and recommends tsdown. Use [`@jmlweb/tsdown-config-base`](../../build-tools/tsdown/): same helper API and published file names, with a [migration guide](../../build-tools/tsdown/#from-jmlwebtsup-config-base). \> Base tsup configuration for jmlweb projects, with sensible defaults for TypeScript libraries and CLIs. ## ✨ Features [Section titled “✨ Features”](#-features) * 🎯 **Sensible defaults**: Pre-configured for TypeScript libraries * 📦 **Dual format**: CommonJS and ESM output by default * 📝 **Declarations**: `.d.ts` generation enabled * 🖥️ **CLI preset**: Shebang support * 🛠️ **Fully typed**: Exported option types ## 📦 Installation [Section titled “📦 Installation”](#-installation) ```bash npm install --save-dev @jmlweb/tsup-config-base tsup ``` ## 🚀 Quick Start [Section titled “🚀 Quick Start”](#-quick-start) Create `tsup.config.ts` in your project root: ```typescript import { createTsupConfig } from '@jmlweb/tsup-config-base'; export default createTsupConfig(); ``` ## 💡 Examples [Section titled “💡 Examples”](#-examples) ### External Dependencies [Section titled “External Dependencies”](#external-dependencies) ```typescript import { createTsupConfig } from '@jmlweb/tsup-config-base'; export default createTsupConfig({ external: ['@jmlweb/eslint-config-base-js', '@eslint/js', 'eslint'], }); ``` ### Multiple Entries and Extra Options [Section titled “Multiple Entries and Extra Options”](#multiple-entries-and-extra-options) ```typescript import { createTsupConfig } from '@jmlweb/tsup-config-base'; export default createTsupConfig({ entry: { index: 'src/index.ts', utils: 'src/utils/index.ts' }, external: ['commander'], options: { minify: true, sourcemap: true, }, }); ``` ### CLI Package [Section titled “CLI Package”](#cli-package) ```typescript import { createTsupCliConfig } from '@jmlweb/tsup-config-base'; export default createTsupCliConfig(); // Output: dist/cli.js with a #!/usr/bin/env node shebang ``` ### CLI with Library API [Section titled “CLI with Library API”](#cli-with-library-api) ```typescript import { createTsupCliConfig } from '@jmlweb/tsup-config-base'; export default createTsupCliConfig({ entry: { cli: 'src/cli.ts', index: 'src/index.ts' }, shebang: 'cli', // Only the cli entry gets a shebang external: ['commander'], }); ``` ## 📋 Configuration Details [Section titled “📋 Configuration Details”](#-configuration-details) ### `createTsupConfig(options?): Options` [Section titled “createTsupConfig(options?): Options”](#createtsupconfigoptions-options) | Option | Type | Default | Description | | ---------- | ------------------------------------ | ------------------ | -------------------------------- | | `entry` | `string[] \| Record` | `['src/index.ts']` | Entry point files | | `format` | `('cjs' \| 'esm' \| 'iife')[]` | `['cjs', 'esm']` | Output formats | | `dts` | `boolean` | `true` | Generate declaration files | | `clean` | `boolean` | `true` | Clean output before build | | `outDir` | `string` | `'dist'` | Output directory | | `external` | `(string \| RegExp)[]` | `[]` | Packages to exclude from bundle | | `options` | `Partial` | `{}` | Additional tsup options to merge | ### `createTsupCliConfig(options?): Options | Options[]` [Section titled “createTsupCliConfig(options?): Options | Options\[\]”](#createtsupcliconfigoptions-options--options) Same options, with `format` defaulting to `['esm']` and `entry` to `{ cli: 'src/cli.ts' }`, plus: | Option | Type | Default | Description | | --------- | ------------------------------- | ---------- | ------------------------------------------------- | | `target` | `NodeTarget` | `'node18'` | Node.js target version | | `shebang` | `boolean \| string \| string[]` | `true` | Shebang on all entries (`true`) or the named ones | Returns an array of configs when a selective shebang is used. With a selective shebang, `shebang` names refer to entry names. An object entry uses its keys. An array entry is named the way tsup names its output: the path relative to the deepest directory all entries share, without extension. `['src/cli.ts', 'src/admin/cli.ts']` gives `cli` and `admin/cli`, built to `dist/cli.js` and `dist/admin/cli.js`. A single entry gets its basename (`['src/cli.ts']` gives `cli`). On that path the config throws a `TsupConfigError` when two array entries map to the same name (`src/cli.ts` and `src/cli.js`), when an array entry is a glob, or when a `shebang` name matches no entry. Use the object form of `entry` to name entries yourself. A `banner` in `options` replaces the shebang banner, so the output has no shebang. To keep both, put the shebang first in your own banner: `banner: { js: '#!/usr/bin/env node\n/* license */' }`. [`@jmlweb/tsdown-config-base`](../../build-tools/tsdown/) combines them for you. ### Exports [Section titled “Exports”](#exports) * `createTsupConfig`, `createTsupCliConfig` - Helpers * `BASE_DEFAULTS`, `CLI_DEFAULTS` - Default values * `TsupConfigOptions`, `TsupCliConfigOptions`, `EntryConfig`, `NodeTarget` - Types * `Options` - Re-exported from tsup ## 🎯 When to Use [Section titled “🎯 When to Use”](#-when-to-use) **Do not use for new projects.** Use [`@jmlweb/tsdown-config-base`](../../build-tools/tsdown/) instead. Only keep this package while you have not yet migrated an existing tsup-based build. ## 🔧 Extending the Configuration [Section titled “🔧 Extending the Configuration”](#-extending-the-configuration) ```typescript import { createTsupConfig } from '@jmlweb/tsup-config-base'; export default createTsupConfig({ format: ['esm'], // ESM only dts: false, // No type declarations outDir: 'build', external: ['vitest'], // Peer, workspace and optional dependencies options: { minify: true, splitting: true, target: 'es2022', }, }); ``` ## 📝 Usage with Scripts [Section titled “📝 Usage with Scripts”](#-usage-with-scripts) ```json { "scripts": { "build": "tsup", "clean": "rm -rf dist", "prepublishOnly": "npm run build" } } ``` ```bash npm run build # Build the package npm run clean # Clean build output ``` ## 📋 Requirements [Section titled “📋 Requirements”](#-requirements) * **Node.js** >= 22.12.0 * **tsup** >= 8.0.0 (tsup 9 itself requires Node.js >= 22.18.0) ## 📦 Peer Dependencies [Section titled “📦 Peer Dependencies”](#-peer-dependencies) * `tsup` (>=8.0.0) ## 🔗 Related Packages [Section titled “🔗 Related Packages”](#-related-packages) * [`@jmlweb/tsdown-config-base`](../../build-tools/tsdown/) - Successor to this package (recommended) * [`@jmlweb/tsconfig-base`](../../typescript/base/) - TypeScript configuration * [`@jmlweb/vitest-config`](../../testing/vitest/) - Vitest configuration * [tsup](https://tsup.egoist.dev/) - TypeScript bundler powered by esbuild ## 🔄 Migration Guide [Section titled “🔄 Migration Guide”](#-migration-guide) ### From 1.x to 2.0.0 [Section titled “From 1.x to 2.0.0”](#from-1x-to-200) **Breaking:** Node.js >= 22.12.0 is required (18 and 20 are end-of-life). ### To `@jmlweb/tsdown-config-base` [Section titled “To @jmlweb/tsdown-config-base”](#to-jmlwebtsdown-config-base) Follow the [migration guide](../../build-tools/tsdown/#from-jmlwebtsup-config-base). See the [Changelog](https://github.com/jmlweb/tooling/blob/main/packages/tsup-config-base/CHANGELOG.md) for version history. ## 📄 License [Section titled “📄 License”](#-license) MIT # tsdown > Base tsdown configuration for jmlweb projects with sensible defaults [![npm version](https://img.shields.io/npm/v/@jmlweb/tsdown-config-base)](https://www.npmjs.com/package/@jmlweb/tsdown-config-base) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D22.18.0-339933.svg)](https://nodejs.org/) [![tsdown](https://img.shields.io/badge/tsdown-0.23.x-ff7e17.svg)](https://tsdown.dev/) \> Base [tsdown](https://tsdown.dev/) configuration for jmlweb projects. The successor to [`@jmlweb/tsup-config-base`](../../build-tools/base/) (deprecated), with the same helper API. Recommended build config. ## ✨ Features [Section titled “✨ Features”](#-features) * 🎯 **Sensible defaults**: Entry, dual CJS/ESM format, declarations, clean, `dist` * 🔁 **Plain tsdown options**: Every tsdown option goes at the top level; the presets only add defaults * 🧭 **Stable file names**: Keeps `.js`/`.cjs`/`.d.ts`/`.d.cts` so existing `exports` maps keep working * ⚡ **Rolldown + Oxc**: Fast builds, and declarations without the TypeScript compiler API when you opt in * 🖥️ **CLI preset**: Shebang injection, including per-entry shebangs in one config ## 📦 Installation [Section titled “📦 Installation”](#-installation) ```bash npm install --save-dev @jmlweb/tsdown-config-base tsdown typescript ``` \> `typescript` is needed for declarations unless you enable `isolatedDeclarations` (see [Declaration Files](#declaration-files-and-typescript-7)). ## 🚀 Quick Start [Section titled “🚀 Quick Start”](#-quick-start) Create `tsdown.config.ts` in your project root: ```typescript import { createTsdownConfig } from '@jmlweb/tsdown-config-base'; export default createTsdownConfig(); ``` ## 💡 Examples [Section titled “💡 Examples”](#-examples) ### External Dependencies [Section titled “External Dependencies”](#external-dependencies) ```typescript import { createTsdownConfig } from '@jmlweb/tsdown-config-base'; export default createTsdownConfig({ deps: { neverBundle: ['@jmlweb/eslint-config-base-js', '@eslint/js', 'eslint'], }, }); ``` ### Multiple Entries and Extra Options [Section titled “Multiple Entries and Extra Options”](#multiple-entries-and-extra-options) ```typescript import { createTsdownConfig } from '@jmlweb/tsdown-config-base'; export default createTsdownConfig({ entry: { index: 'src/index.ts', utils: 'src/utils/index.ts' }, deps: { neverBundle: ['vitest'] }, minify: true, sourcemap: true, }); ``` ### CLI Package [Section titled “CLI Package”](#cli-package) ```typescript import { createTsdownCliConfig } from '@jmlweb/tsdown-config-base'; export default createTsdownCliConfig(); // Output: dist/cli.js with a #!/usr/bin/env node shebang ``` ### CLI with Library API [Section titled “CLI with Library API”](#cli-with-library-api) ```typescript import { createTsdownCliConfig } from '@jmlweb/tsdown-config-base'; export default createTsdownCliConfig({ entry: { cli: 'src/cli.ts', index: 'src/index.ts' }, shebang: 'cli', // Only the cli entry gets a shebang deps: { neverBundle: ['commander'] }, }); ``` ## 📋 Configuration Details [Section titled “📋 Configuration Details”](#-configuration-details) ### Defaults [Section titled “Defaults”](#defaults) | Setting | Default | Description | | ---------------- | ---------------------- | ---------------------------------------------------------------------- | | `entry` | `['src/index.ts']` | Entry point(s) | | `format` | `['cjs', 'esm']` | Dual publishing | | `dts` | `true` | Generate declarations | | `clean` | `true` | Clean output before build | | `outDir` | `'dist'` | Output directory | | `fixedExtension` | `false` | `.js`/`.cjs` by `package.json` type, not `.mjs` | | `checks` | `{ legacyCjs: false }` | Silences tsdown’s CommonJS warning (dual is on purpose) | | `target` | From `engines.node` | Inferred by tsdown; without `engines.node` there is no syntax lowering | ### `createTsdownConfig(options?): UserConfig` [Section titled “createTsdownConfig(options?): UserConfig”](#createtsdownconfigoptions-userconfig) | Option | Type | Default | Description | | ---------- | --------------------------------------- | ------------------ | ------------------------------------------------ | | `entry` | `string[] \| Record` | `['src/index.ts']` | Entry point files | | `format` | `('cjs' \| 'esm' \| 'iife' \| 'umd')[]` | `['cjs', 'esm']` | Output formats | | `dts` | `boolean \| DtsOptions` | `true` | Declaration files (or tsdown dts options) | | `clean` | `boolean` | `true` | Clean output before build | | `outDir` | `string` | `'dist'` | Output directory | | `external` | `(string \| RegExp)[]` | `[]` | **Deprecated**: use `deps.neverBundle` | | `options` | `AdditionalOptions` | `{}` | **Deprecated**: pass tsdown options at top level | Any other tsdown option is accepted at the top level. ### `createTsdownCliConfig(options?): UserConfig` [Section titled “createTsdownCliConfig(options?): UserConfig”](#createtsdowncliconfigoptions-userconfig) Same options, with `format` defaulting to `['esm']` and `entry` to `{ cli: 'src/cli.ts' }`, plus: | Option | Type | Default | Description | | --------- | ------------------------------- | ------------------- | ------------------------------------------------- | | `target` | `NodeTarget` | From `engines.node` | Node.js target version | | `shebang` | `boolean \| string \| string[]` | `true` | Shebang on all entries (`true`) or the named ones | Shebang names are output names without extension (`cli` for `dist/cli.js`). An entry whose source already starts with a shebang keeps its own; no second one is added. A `banner` of your own (string, object or function form) is kept on every output. On the shebang entries the shebang goes first and your `js` banner follows on the next line. A `js` banner that already starts with `#!` is used as is. ### Conflicts and Deprecations [Section titled “Conflicts and Deprecations”](#conflicts-and-deprecations) * `external` maps to `deps.neverBundle`; passing both throws `TsdownConfigError` * Setting the same key at the top level and in `options` throws `TsdownConfigError` * `checks: { legacyCjs: true }` brings back the CommonJS warning; `fixedExtension: true` emits `.mjs`/`.cjs` (update your `exports` map) ### Exports [Section titled “Exports”](#exports) * `createTsdownConfig`, `createTsdownCliConfig` - Helpers * `BASE_DEFAULTS`, `CLI_DEFAULTS` - Default values * `TsdownConfigError` - Thrown on conflicting options * `TsdownConfigOptions`, `TsdownCliConfigOptions`, `EntryConfig`, `NodeTarget`, `OutputFormat`, `DepsOptions` - Types * `AdditionalOptions`, `AdditionalCliOptions` - Types of the deprecated `options` field * `UserConfig` - Re-exported from tsdown, also as `Options` ### Differences from `@jmlweb/tsup-config-base` [Section titled “Differences from @jmlweb/tsup-config-base”](#differences-from-jmlwebtsup-config-base) | Area | tsup-config-base | tsdown-config-base | | --------------------- | --------------------------------------------- | -------------------------------------------------------------- | | Helpers | `createTsupConfig`, `createTsupCliConfig` | `createTsdownConfig`, `createTsdownCliConfig` | | Config file | `tsup.config.ts` | `tsdown.config.ts` | | Return type | `Options` (`Options[]` for selective shebang) | Always one `UserConfig` | | `external` | tsup’s `external` | Deprecated alias of `deps.neverBundle` | | Selective shebang | Split configs | One config with a per-file `banner` | | `format` | `cjs`, `esm`, `iife` | Adds `umd` | | `splitting`, `bundle` | In `options` | Not available; use `unbundle: true` instead of `bundle: false` | | `noExternal` | In `options` | `deps.alwaysBundle` | | Declarations | rollup-plugin-dts | tsc, tsgo or Oxc | | Node.js | >= 22.12.0 | `^22.18.0 \|\| ^24.11.0 \|\| >=26.0.0` (tsdown’s requirement) | ### Declaration Files and TypeScript 7 [Section titled “Declaration Files and TypeScript 7”](#declaration-files-and-typescript-7) tsdown picks the declaration generator automatically (verified with tsdown 0.23.0): | Project setup | Generator | Needs the TypeScript compiler API? | | ----------------------------------------------- | --------- | ------------------------------------------------------------ | | `typescript` 5.x / 6.x installed | `tsc` | Yes | | `typescript` 7.x installed | `tsgo` | No: native binary (experimental in tsdown) | | `isolatedDeclarations: true` in `tsconfig.json` | `oxc` | No: works without `typescript` (needs explicit export types) | | Neither | - | Build fails: `TypeScript is not installed` | Force one with `dts: { generator: 'oxc' }` (or `'tsc'` / `'tsgo'`). \> ⚠️ Much of the toolchain does not support TypeScript 7 yet (`typescript-eslint` `<6.1.0`, `ts-jest` `<7`, `@astrojs/check` `^5 || ^6`). Stay on TypeScript 6 if you use them. ## 🎯 When to Use [Section titled “🎯 When to Use”](#-when-to-use) * ✅ Building a TypeScript library with dual CJS/ESM output and declarations * ✅ Migrating from `@jmlweb/tsup-config-base` without changing published file names * ✅ Declaration generation ready for TypeScript 7 * ✅ CLI packages with shebang injection **Staying on tsup?** [`@jmlweb/tsup-config-base`](../../build-tools/base/) still works but is deprecated and unmaintained. ## 🔧 Extending the Configuration [Section titled “🔧 Extending the Configuration”](#-extending-the-configuration) ```typescript import { createTsdownConfig } from '@jmlweb/tsdown-config-base'; export default createTsdownConfig({ format: ['esm'], // ESM only outDir: 'build', fixedExtension: true, // Emit .mjs/.d.mts unbundle: true, // Mirror src/ instead of bundling deps: { neverBundle: ['eslint'], alwaysBundle: ['tiny-utils'], }, }); ``` tsdown already externalizes `dependencies`, `peerDependencies` and `optionalDependencies`; `deps.neverBundle` covers anything else. ## 📝 Usage with Scripts [Section titled “📝 Usage with Scripts”](#-usage-with-scripts) ```json { "scripts": { "build": "tsdown", "clean": "rm -rf dist", "prepublishOnly": "npm run build" } } ``` ```bash npm run build # Build the package npm run clean # Clean build output ``` ## 📋 Requirements [Section titled “📋 Requirements”](#-requirements) * **Node.js** ^22.18.0, ^24.11.0 or >=26.0.0 * **tsdown** \~0.23.0 * **TypeScript** 5.x, 6.x or 7.x (optional with `isolatedDeclarations`) ## 📦 Peer Dependencies [Section titled “📦 Peer Dependencies”](#-peer-dependencies) * `tsdown` (\~0.23.0) tsdown is pre-1.0 and ships breaking changes in minor releases, so the range allows patch updates only. It is widened as tsdown releases are verified. ## 🔗 Related Packages [Section titled “🔗 Related Packages”](#-related-packages) * [`@jmlweb/tsup-config-base`](../../build-tools/base/) - Deprecated tsup equivalent * [`@jmlweb/tsconfig-base`](../../typescript/base/) - TypeScript configuration * [`@jmlweb/oxlint-config-base`](../../oxlint/base/) - Oxlint config for TypeScript projects * [`@jmlweb/vitest-config`](../../testing/vitest/) - Vitest configuration * [tsdown](https://tsdown.dev/) - Library bundler powered by Rolldown ## 🔄 Migration Guide [Section titled “🔄 Migration Guide”](#-migration-guide) ### From `@jmlweb/tsup-config-base` [Section titled “From @jmlweb/tsup-config-base”](#from-jmlwebtsup-config-base) 1. Install `@jmlweb/tsdown-config-base` and `tsdown`; remove `@jmlweb/tsup-config-base` and `tsup` 2. Rename `tsup.config.ts` to `tsdown.config.ts` and change the `build` script from `tsup` to `tsdown` 3. Change the import and helper name, and move `external` to `deps.neverBundle` (`external` still works, deprecated): ```typescript // Before import { createTsupConfig } from '@jmlweb/tsup-config-base'; export default createTsupConfig({ external: ['eslint'] }); // After import { createTsdownConfig } from '@jmlweb/tsdown-config-base'; export default createTsdownConfig({ deps: { neverBundle: ['eslint'] }, }); ``` 4. Move everything from `options` to the top level, and replace tsup-only options (`splitting`, `bundle`, `noExternal`, `esbuildPlugins`, …) using [Differences](#differences-from-jmlwebtsup-config-base) 5. Build and compare `dist/` with the previous output See the [Changelog](https://github.com/jmlweb/tooling/blob/main/packages/tsdown-config-base/CHANGELOG.md) for version history. ## 📄 License [Section titled “📄 License”](#-license) MIT # vite > Base Vite configuration for jmlweb projects with TypeScript support, build optimization, and optional React integration [![npm version](https://img.shields.io/npm/v/@jmlweb/vite-config)](https://www.npmjs.com/package/@jmlweb/vite-config) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D22.12.0-339933.svg)](https://nodejs.org/) [![Vite](https://img.shields.io/badge/Vite-5%2B-646CFF.svg)](https://vitejs.dev/) \> Base Vite configuration with sensible defaults and an optional React helper. ## ✨ Features [Section titled “✨ Features”](#-features) * ⚡ **Sensible defaults**: build, dev server and preview settings ready to go * 🎯 **Typed factory**: `createViteConfig()` with typed options * ⚛️ **React helper**: `createReactViteConfig()` takes your `@vitejs/plugin-react` * 🔀 **Dual format**: ESM and CommonJS builds ## 📦 Installation [Section titled “📦 Installation”](#-installation) ```bash npm install --save-dev @jmlweb/vite-config vite ``` For React projects, also install the plugin (optional): ```bash npm install --save-dev @vitejs/plugin-react ``` \> 💡 Upgrading? See the [Migration Guide](#-migration-guide). ## 🚀 Quick Start [Section titled “🚀 Quick Start”](#-quick-start) vite.config.ts ```typescript import { createViteConfig } from '@jmlweb/vite-config'; export default createViteConfig(); ``` React: vite.config.ts ```typescript import { createReactViteConfig } from '@jmlweb/vite-config'; import react from '@vitejs/plugin-react'; export default createReactViteConfig({ reactPlugin: react() }); ``` ## 💡 Examples [Section titled “💡 Examples”](#-examples) ### Path aliases [Section titled “Path aliases”](#path-aliases) ```typescript import { resolve } from 'node:path'; import { createViteConfig } from '@jmlweb/vite-config'; export default createViteConfig({ resolve: { alias: { '@': resolve(import.meta.dirname, 'src') } }, }); ``` ### Build and server settings [Section titled “Build and server settings”](#build-and-server-settings) ```typescript import { createViteConfig } from '@jmlweb/vite-config'; export default createViteConfig({ build: { sourcemap: true, target: ['es2020', 'chrome87', 'safari14'] }, server: { port: 3000, open: true }, preview: { port: 8080 }, }); ``` ### Any other Vite option [Section titled “Any other Vite option”](#any-other-vite-option) Use `options` for everything not covered by the typed fields (it is spread last): ```typescript import { createViteConfig } from '@jmlweb/vite-config'; export default createViteConfig({ options: { base: '/app/', define: { __APP_VERSION__: '"1.0.0"' } }, }); ``` ## 📋 Configuration Details [Section titled “📋 Configuration Details”](#-configuration-details) ### Defaults [Section titled “Defaults”](#defaults) | Option | Default | | ------------------- | ---------------------------------------------------- | | `build.outDir` | `'dist'` | | `build.sourcemap` | `false` | | `build.target` | `'esnext'` | | `build.minify` | Vite default (esbuild up to Vite 7, Oxc from Vite 8) | | `server.port` | `5173` | | `server.strictPort` | `false` | | `server.open` | `false` | | `server.host` | `'localhost'` | | `preview.port` | `4173` | `build`, `server` and `preview` are merged shallowly over these defaults. The defaults are exported as `BASE_DEFAULTS`. ### API [Section titled “API”](#api) | Export | Description | | ----------------------- | ---------------------------------------------------------------------------- | | `createViteConfig` | Options: `plugins`, `resolve.alias`, `build`, `server`, `preview`, `options` | | `createReactViteConfig` | Same options plus required `reactPlugin`, placed first in `plugins` | | `BASE_DEFAULTS` | Default `build`, `server` and `preview` values | | `ViteConfigOptions` | Options type | | `UserConfig`, `Plugin` | Re-exported Vite types | ## 🎯 When to Use [Section titled “🎯 When to Use”](#-when-to-use) * ✅ Vite apps (vanilla, React, Vue, …) with shared defaults * ✅ Consistent dev server and build settings Not for you if you need a fully custom Vite setup: write your own config. ## 🔧 Extending the Configuration [Section titled “🔧 Extending the Configuration”](#-extending-the-configuration) Add plugins through `plugins` (for React, they follow `reactPlugin`). Branch on the mode with a function config: ```typescript import { defineConfig } from 'vite'; import { createViteConfig } from '@jmlweb/vite-config'; export default defineConfig(({ mode }) => createViteConfig({ build: { sourcemap: mode !== 'production' }, }), ); ``` ## 📝 Usage with Scripts [Section titled “📝 Usage with Scripts”](#-usage-with-scripts) ```json { "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview" } } ``` ```bash npm run dev # dev server npm run build # production build npm run preview # preview the build ``` ## 📋 Requirements [Section titled “📋 Requirements”](#-requirements) * **Node.js** >= 22.12.0 * **Vite** >= 5.0.0 ## 📦 Peer Dependencies [Section titled “📦 Peer Dependencies”](#-peer-dependencies) * `vite` (`>=5.0.0`) * `@vitejs/plugin-react` (optional): only for `createReactViteConfig` ## 🔗 Related Packages [Section titled “🔗 Related Packages”](#-related-packages) * [`@jmlweb/vitest-config`](../../testing/vitest/) - Vitest config * [`@jmlweb/tsconfig-react`](../../typescript/react/) - TypeScript config for React * [`@jmlweb/oxlint-config-react`](../../oxlint/react/) - Oxlint config for React * [`@jmlweb/oxfmt-config-base`](../../oxfmt/base/) - Oxfmt config ## 🔄 Migration Guide [Section titled “🔄 Migration Guide”](#-migration-guide) ### 1.x to 2.0 [Section titled “1.x to 2.0”](#1x-to-20) **Breaking:** Node.js >= 22.12.0 is required. 1. Upgrade Node.js to 22.12.0 or later. 2. Update: `npm install --save-dev @jmlweb/vite-config@latest` ### 2.0.2: minify [Section titled “2.0.2: minify”](#202-minify) `minify` is no longer forced to `'esbuild'` (it fails on Vite 8 without `esbuild`). Vite’s own default applies; pass `build.minify` to pin one. See the [Changelog](https://github.com/jmlweb/tooling/blob/main/packages/vite-config/CHANGELOG.md) for details. ## 📄 License [Section titled “📄 License”](#-license) MIT # commitlint > Shared commitlint configuration for enforcing Conventional Commits [![npm version](https://img.shields.io/npm/v/@jmlweb/commitlint-config)](https://www.npmjs.com/package/@jmlweb/commitlint-config) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D22.12.0-339933.svg)](https://nodejs.org/) \> Shared commitlint configuration for Conventional Commits. Extends `@commitlint/config-conventional`. ## ✨ Features [Section titled “✨ Features”](#-features) * 📝 **Conventional Commits**: enforced types, kebab-case scopes, no trailing period * 🎯 **Zero config**: any scope allowed by default * 🔧 **Configurable**: `createCommitlintConfig()` for types, scopes, header length and more * 🔀 **Dual format**: ESM and CommonJS builds ## 📦 Installation [Section titled “📦 Installation”](#-installation) ```bash npm install --save-dev @jmlweb/commitlint-config @commitlint/cli @commitlint/config-conventional ``` \> 💡 Upgrading? See the [Migration Guide](#-migration-guide). ## 🚀 Quick Start [Section titled “🚀 Quick Start”](#-quick-start) commitlint.config.mjs ```javascript import commitlintConfig from '@jmlweb/commitlint-config'; export default commitlintConfig; ``` Run it on every commit with a Git hook, for example with Husky (`.husky/commit-msg`): ```bash npx --no -- commitlint --edit "$1" ``` ## 💡 Examples [Section titled “💡 Examples”](#-examples) ### Restrict scopes [Section titled “Restrict scopes”](#restrict-scopes) ```javascript import { createCommitlintConfig } from '@jmlweb/commitlint-config'; export default createCommitlintConfig({ scopes: ['api', 'ui', 'database', 'auth'], }); ``` ### Strict setup [Section titled “Strict setup”](#strict-setup) ```javascript import { createCommitlintConfig } from '@jmlweb/commitlint-config'; export default createCommitlintConfig({ scopes: ['core', 'utils', 'deps'], scopeRequired: true, headerMaxLength: 72, }); ``` ### Ignore some commits [Section titled “Ignore some commits”](#ignore-some-commits) ```javascript import { createCommitlintConfig } from '@jmlweb/commitlint-config'; export default createCommitlintConfig({ ignores: [ (commit) => commit.startsWith('Merge'), (commit) => /^chore\(deps\)/.test(commit), ], }); ``` Valid and invalid messages: ```text feat(auth): add login endpoint valid fix: resolve memory leak valid Add new feature invalid (no type) feat(Auth): add login invalid (scope not kebab-case) feat: add login. invalid (trailing period) ``` ## 📋 Configuration Details [Section titled “📋 Configuration Details”](#-configuration-details) ### Options [Section titled “Options”](#options) | Option | Type | Default | Description | | ------------------ | --------------------------------- | ------------------------------------------------------------------------- | ----------------------------------------------- | | `additionalTypes` | `string[]` | `[]` | Types added to the defaults | | `scopes` | `string[]` | none | Allowed scopes; any scope is allowed when unset | | `additionalScopes` | `string[]` | `[]` | Extra scopes; ignored when `scopes` is set | | `scopeRequired` | `boolean` | `false` | Require a scope | | `headerMaxLength` | `number` | `100` | Max header length | | `bodyRequired` | `boolean` | `false` | Require a body | | `subjectCase` | rule tuple or `false` | `['never', ['sentence-case', 'start-case', 'pascal-case', 'upper-case']]` | `subject-case` rule; `false` disables it | | `ignores` | `((commit: string) => boolean)[]` | none | Skip validation for matching commits | ### Rules [Section titled “Rules”](#rules) | Rule | Setting | | ------------------------------------------------ | ------------------------------------- | | `type-enum` | `COMMIT_TYPES` plus `additionalTypes` | | `type-case` / `scope-case` | lower-case / kebab-case | | `type-empty`, `subject-empty` | never empty | | `subject-full-stop` | never `.` | | `body-leading-blank`, `footer-leading-blank` | required | | `body-max-line-length`, `footer-max-line-length` | 100 | `COMMIT_TYPES`: `feat`, `fix`, `docs`, `chore`, `refactor`, `test`, `ci`, `perf`, `style`, `build`, `revert`. ### Exports [Section titled “Exports”](#exports) `default` (config with defaults), `createCommitlintConfig`, `COMMIT_TYPES`, `CommitType`, `DEFAULT_SUBJECT_CASE`, `CommitlintConfigOptions`, `UserConfig`. ## 🎯 When to Use [Section titled “🎯 When to Use”](#-when-to-use) * ✅ Projects following Conventional Commits * ✅ Shared commit rules across repositories * ✅ Optional scope allowlists per project Not for you if you need rules unrelated to Conventional Commits: write a custom commitlint config. ## 🔧 Extending the Configuration [Section titled “🔧 Extending the Configuration”](#-extending-the-configuration) Use `createCommitlintConfig` with the options above, or spread the result to add rules: ```javascript import { createCommitlintConfig } from '@jmlweb/commitlint-config'; const base = createCommitlintConfig({ additionalTypes: ['wip'] }); export default { ...base, rules: { ...base.rules, 'body-max-line-length': [2, 'always', 120] }, }; ``` ## 📝 Usage with Scripts [Section titled “📝 Usage with Scripts”](#-usage-with-scripts) ```json { "scripts": { "commitlint": "commitlint --edit", "commitlint:last": "commitlint --from HEAD~1 --to HEAD" } } ``` ```bash echo "feat: add feature" | npx commitlint # check a message npm run commitlint:last # check the last commit ``` ## 📋 Requirements [Section titled “📋 Requirements”](#-requirements) * **Node.js** >= 22.12.0 * **@commitlint/cli** >= 19.0.0 * **@commitlint/config-conventional** >= 19.0.0 ## 📦 Peer Dependencies [Section titled “📦 Peer Dependencies”](#-peer-dependencies) * `@commitlint/cli` (`>=19.0.0`) * `@commitlint/config-conventional` (`>=19.0.0`) ## 🔗 Related Packages [Section titled “🔗 Related Packages”](#-related-packages) * [`@jmlweb/oxlint-config-base`](../../oxlint/base/) - Oxlint config * [`@jmlweb/oxfmt-config-base`](../../oxfmt/base/) - Oxfmt config * [`@jmlweb/tsconfig-base`](../../typescript/base/) - TypeScript configuration ## 🔄 Migration Guide [Section titled “🔄 Migration Guide”](#-migration-guide) ### 4.0 to 4.1 [Section titled “4.0 to 4.1”](#40-to-41) `subject-case` now defaults to the `@commitlint/config-conventional` rule, so subjects can cite ids such as `TSK-010`. To keep the previous behavior: ```javascript import { createCommitlintConfig } from '@jmlweb/commitlint-config'; export default createCommitlintConfig({ subjectCase: ['always', 'lower-case'], }); ``` ### 3.x to 4.0 [Section titled “3.x to 4.0”](#3x-to-40) **Breaking:** Node.js >= 22.12.0 is required. Upgrade Node.js, then `npm install --save-dev @jmlweb/commitlint-config@latest`. See the [Changelog](https://github.com/jmlweb/tooling/blob/main/packages/commitlint-config/CHANGELOG.md) for details. ## 📄 License [Section titled “📄 License”](#-license) MIT # astro > ESLint configuration for Astro projects with TypeScript. Extends @jmlweb/eslint-config-base with Astro-specific rules. [![npm version](https://img.shields.io/npm/v/@jmlweb/eslint-config-astro)](https://www.npmjs.com/package/@jmlweb/eslint-config-astro) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D22.12.0-339933.svg)](https://nodejs.org/) [![ESLint](https://img.shields.io/badge/ESLint-9%20%7C%2010-4B32C3.svg)](https://eslint.org/) \> ESLint flat config for Astro with TypeScript. Extends [`@jmlweb/eslint-config-base`](../../eslint/base/) with `eslint-plugin-astro` rules for `.astro` files. \> ✅ **Recommended**: Oxlint cannot lint `.astro` templates, so this package is the way to lint them. In a project migrated to Oxlint, lint everything else with [`@jmlweb/oxlint-config-base`](../../oxlint/base/) and only `.astro` files with ESLint; see the [migration guide](https://jmlweb.github.io/tooling/guides/migrate-to-oxc/#astro-projects). ## ✨ Features [Section titled “✨ Features”](#-features) * 🚀 **Astro rules**: `eslint-plugin-astro` recommended config * 📝 **`.astro` parsing**: TypeScript parser for frontmatter, with type-checked rules disabled where `.astro` has no type info * 🔒 **Strict TypeScript**: everything from `@jmlweb/eslint-config-base` for `.ts` and `.tsx` * 📦 **Import sorting**: imports and exports sorted in `.astro`, `.ts` and `.tsx` * 🎨 **Prettier compatible**: disables rules that conflict with Prettier ## 📦 Installation [Section titled “📦 Installation”](#-installation) ```bash npm install --save-dev @jmlweb/eslint-config-astro @jmlweb/eslint-config-base eslint @eslint/js typescript-eslint eslint-config-prettier eslint-plugin-simple-import-sort eslint-plugin-astro ``` \> 💡 **Upgrading?** See the [Migration Guide](#-migration-guide). ## 🚀 Quick Start [Section titled “🚀 Quick Start”](#-quick-start) eslint.config.js ```javascript import astroConfig from '@jmlweb/eslint-config-astro'; export default [ ...astroConfig, // Project-specific overrides ]; ``` ## 💡 Examples [Section titled “💡 Examples”](#-examples) ### Test overrides and ignores [Section titled “Test overrides and ignores”](#test-overrides-and-ignores) eslint.config.js ```javascript import astroConfig from '@jmlweb/eslint-config-astro'; export default [ ...astroConfig, { files: ['**/*.test.ts'], rules: { '@typescript-eslint/no-explicit-any': 'off', 'no-console': 'off', }, }, { ignores: ['dist/', '.astro/', 'node_modules/'], }, ]; ``` ### Tune Astro rules [Section titled “Tune Astro rules”](#tune-astro-rules) eslint.config.js ```javascript import astroConfig from '@jmlweb/eslint-config-astro'; export default [ ...astroConfig, { files: ['**/*.astro'], rules: { 'astro/no-unused-define-vars-in-style': 'warn', }, }, ]; ``` ## 📋 Configuration Details [Section titled “📋 Configuration Details”](#-configuration-details) | Files | Applied | | --------------------- | ----------------------------------------------------------------------- | | `**/*.astro` | `astro` recommended, TypeScript parser, type-checked rules off, sorting | | `**/*.ts`, `**/*.tsx` | Strict TypeScript from `base`, import and export sorting | ### Included [Section titled “Included”](#included) * ✅ Everything from `@jmlweb/eslint-config-base` * ✅ `eslint-plugin-astro` recommended rules * ✅ `.astro` files parsed with `@typescript-eslint/parser` and `.astro` as an extra file extension * ✅ Prettier conflict resolution Type-checked rules are disabled for `.astro` files because `astro-eslint-parser` provides no type information. ## 🔄 Import Sorting [Section titled “🔄 Import Sorting”](#-import-sorting) Imports are sorted and type-only imports are enforced. **Before:** ```typescript import { Component } from './component'; import type { User } from './types'; import fs from 'fs'; ``` **After `eslint --fix`:** ```typescript import fs from 'fs'; import type { User } from './types'; import { Component } from './component'; ``` ## 🎯 When to Use [Section titled “🎯 When to Use”](#-when-to-use) * ✅ Astro projects written in TypeScript * ✅ Linting `.astro` files alongside Oxlint in a migrated project * ✅ Strict type-aware linting for `.ts` files in Astro projects For other TypeScript projects use [`@jmlweb/eslint-config-base`](../../eslint/base/), or [`@jmlweb/eslint-config-react`](../../eslint/react/) for React. ## 🔧 Extending the Configuration [Section titled “🔧 Extending the Configuration”](#-extending-the-configuration) Spread the config, then add objects after it. Later entries win. eslint.config.js ```javascript import astroConfig from '@jmlweb/eslint-config-astro'; export default [ ...astroConfig, { files: ['src/pages/**/*.astro'], rules: { 'no-console': 'off', }, }, ]; ``` ## 📝 Usage with Scripts [Section titled “📝 Usage with Scripts”](#-usage-with-scripts) ```json { "scripts": { "lint": "eslint .", "lint:fix": "eslint . --fix" } } ``` ```bash npm run lint ``` ## 📋 Requirements [Section titled “📋 Requirements”](#-requirements) * **Node.js** >= 22.12.0 * **ESLint** ^9.0.0 || ^10.0.0 (flat config) * **TypeScript** project with a `tsconfig.json` (versions supported by typescript-eslint 8) ## 📦 Peer Dependencies [Section titled “📦 Peer Dependencies”](#-peer-dependencies) * `eslint` (`^9.0.0 || ^10.0.0`) * `@eslint/js` (`^9.0.0 || ^10.0.0`) * `typescript-eslint` (`^8.0.0`) * `eslint-config-prettier` (`^9.1.0 || ^10.0.0`) * `eslint-plugin-astro` (`^1.5.0 || ^2.0.0 || ^3.0.0`) * `eslint-plugin-simple-import-sort` (`^12.0.0 || ^13.0.0 || ^14.0.0`) * `@jmlweb/eslint-config-base` (`^3.0.3`) ## 🔗 Related Packages [Section titled “🔗 Related Packages”](#-related-packages) * [`@jmlweb/eslint-config-base`](../../eslint/base/): TypeScript config (extended by this package, maintenance mode) * [`@jmlweb/oxlint-config-base`](../../oxlint/base/): Oxlint config for the non-`.astro` files * [`@jmlweb/tsconfig-astro`](../../typescript/astro/): TypeScript config for Astro * [`@jmlweb/prettier-config-base`](../../prettier/base/): Prettier config (maintenance mode) ## 🔄 Migration Guide [Section titled “🔄 Migration Guide”](#-migration-guide) ### 1.x to 2.0 [Section titled “1.x to 2.0”](#1x-to-20) Node.js 18 and 20 are end-of-life. Node.js >= 22.12.0 is now required: it is the first Node.js 22 release that can `require()` ES modules without a flag, which the CommonJS build needs to load ESM-only plugins. Peer ranges were also widened (`eslint-plugin-astro` 2 and 3, `eslint-plugin-simple-import-sort` 13 and 14). 1. Upgrade Node.js to 22.12.0 or later. 2. Update the package: `npm install --save-dev @jmlweb/eslint-config-astro@latest`. See the [Changelog](https://github.com/jmlweb/tooling/blob/main/packages/eslint-config-astro/CHANGELOG.md). ## 📄 License [Section titled “📄 License”](#-license) MIT # base > Base ESLint configuration for TypeScript projects with strict type checking and best practices [![npm version](https://img.shields.io/npm/v/@jmlweb/eslint-config-base)](https://www.npmjs.com/package/@jmlweb/eslint-config-base) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D22.12.0-339933.svg)](https://nodejs.org/) [![ESLint](https://img.shields.io/badge/ESLint-9%20%7C%2010-4B32C3.svg)](https://eslint.org/) \> Strict ESLint flat config for TypeScript projects. Extends [`@jmlweb/eslint-config-base-js`](../../eslint/base-js/) with strict type-checked rules. \> 🔧 **Maintenance mode**: only dependency and security updates, no new rules. For new projects use [`@jmlweb/oxlint-config-base`](../../oxlint/base/). To move an existing project, see the [migration guide](https://jmlweb.github.io/tooling/guides/migrate-to-oxc/). ## ✨ Features [Section titled “✨ Features”](#-features) * 🔒 **Strict type checking**: `strictTypeChecked` and `stylisticTypeChecked` from typescript-eslint, with `projectService` * 🛡️ **No `any`**: `no-explicit-any` is an error * 📦 **Type-only imports**: inline `type` style, plus automatic import and export sorting * 🎯 **Opinionated**: no enums (use const maps), no default exports, no parameter mutation * 🎨 **Prettier compatible**: disables rules that conflict with Prettier ## 📦 Installation [Section titled “📦 Installation”](#-installation) ```bash npm install --save-dev @jmlweb/eslint-config-base eslint @eslint/js typescript-eslint eslint-config-prettier eslint-plugin-simple-import-sort ``` `@jmlweb/eslint-config-base-js` is a regular dependency and installs automatically. \> 💡 **Upgrading?** See the [Migration Guide](#-migration-guide). ## 🚀 Quick Start [Section titled “🚀 Quick Start”](#-quick-start) eslint.config.js ```javascript import baseConfig from '@jmlweb/eslint-config-base'; export default [ ...baseConfig, // Project-specific overrides ]; ``` ## 💡 Examples [Section titled “💡 Examples”](#-examples) ### Test overrides and ignores [Section titled “Test overrides and ignores”](#test-overrides-and-ignores) eslint.config.js ```javascript import baseConfig from '@jmlweb/eslint-config-base'; export default [ ...baseConfig, { files: ['**/*.test.ts', '**/*.test.tsx'], rules: { '@typescript-eslint/no-explicit-any': 'off', 'no-console': 'off', }, }, { ignores: ['dist/', 'build/', 'node_modules/'], }, ]; ``` ### Allow default exports in config files [Section titled “Allow default exports in config files”](#allow-default-exports-in-config-files) eslint.config.js ```javascript import baseConfig from '@jmlweb/eslint-config-base'; export default [ ...baseConfig, { files: ['*.config.ts', '*.config.js'], rules: { 'no-restricted-exports': 'off', }, }, ]; ``` ### Less strict [Section titled “Less strict”](#less-strict) eslint.config.js ```javascript import baseConfig from '@jmlweb/eslint-config-base'; export default [ ...baseConfig, { files: ['**/*.ts', '**/*.tsx'], rules: { '@typescript-eslint/no-explicit-any': 'warn', '@typescript-eslint/consistent-type-imports': 'off', }, }, ]; ``` ## 📋 Configuration Details [Section titled “📋 Configuration Details”](#-configuration-details) Applied to `**/*.ts` and `**/*.tsx` (plus the JavaScript rules from `base-js` for `.js`, `.mjs` and `.cjs`). ### Included [Section titled “Included”](#included) * ✅ typescript-eslint `recommended`, `strictTypeChecked` and `stylisticTypeChecked` * ✅ `parserOptions.projectService: true` (requires a `tsconfig.json`) * ✅ Import and export sorting, Prettier conflict resolution * ✅ All rules from `@jmlweb/eslint-config-base-js` ### Key rules [Section titled “Key rules”](#key-rules) | Rule | Setting | | -------------------------------------------------- | ------------------------------------------------ | | `@typescript-eslint/no-explicit-any` | `error` | | `@typescript-eslint/explicit-function-return-type` | `off` (return types are inferred) | | `@typescript-eslint/no-unused-vars` | `error`, `_`-prefixed args and variables ignored | | `@typescript-eslint/consistent-type-imports` | `error`, `type-imports` with inline fix | | `@typescript-eslint/consistent-type-definitions` | `error`, prefer `type` | | `@typescript-eslint/no-require-imports` | `error` | | `@typescript-eslint/naming-convention` | `error`, see below | | `no-restricted-syntax` (`TSEnumDeclaration`) | `error`, use const maps | | `no-restricted-exports` | `error`, no default exports | | `no-param-reassign` | `error`, including properties | Naming: types `PascalCase`; variables `camelCase` or `UPPER_CASE` (exported consts may also be `PascalCase`); functions `camelCase`. ## 🔄 Import Sorting [Section titled “🔄 Import Sorting”](#-import-sorting) Imports are sorted and type-only imports are enforced. **Before:** ```typescript import { Component } from './component'; import React, { useState } from 'react'; import { User } from './types'; import fs from 'fs'; ``` **After `eslint --fix`:** ```typescript import fs from 'fs'; import React, { useState } from 'react'; import { type User } from './types'; import { Component } from './component'; ``` ## 🎯 When to Use [Section titled “🎯 When to Use”](#-when-to-use) * ✅ TypeScript projects that want strict, type-aware linting * ✅ Teams that want one consistent style (named exports, const maps, immutability) * ✅ A base for the React, Node.js and Astro configs For JavaScript-only projects use [`@jmlweb/eslint-config-base-js`](../../eslint/base-js/). For React, Node.js or Astro use [`-react`](../../eslint/react/), [`-node`](../../eslint/node/) or [`-astro`](../../eslint/astro/). For new projects prefer [`@jmlweb/oxlint-config-base`](../../oxlint/base/). ## 🔧 Extending the Configuration [Section titled “🔧 Extending the Configuration”](#-extending-the-configuration) Spread the config, then add objects after it. Later entries win. eslint.config.js ```javascript import baseConfig from '@jmlweb/eslint-config-base'; export default [ ...baseConfig, { files: ['**/*.ts'], rules: { '@typescript-eslint/explicit-function-return-type': 'error', }, }, ]; ``` ## 📝 Usage with Scripts [Section titled “📝 Usage with Scripts”](#-usage-with-scripts) ```json { "scripts": { "lint": "eslint .", "lint:fix": "eslint . --fix" } } ``` ```bash npm run lint ``` ## 📋 Requirements [Section titled “📋 Requirements”](#-requirements) * **Node.js** >= 22.12.0 * **ESLint** ^9.0.0 || ^10.0.0 (flat config) * **TypeScript** project with a `tsconfig.json` (versions supported by typescript-eslint 8) ## 📦 Peer Dependencies [Section titled “📦 Peer Dependencies”](#-peer-dependencies) * `eslint` (`^9.0.0 || ^10.0.0`) * `@eslint/js` (`^9.0.0 || ^10.0.0`) * `typescript-eslint` (`^8.0.0`) * `eslint-config-prettier` (`^9.1.0 || ^10.0.0`) * `eslint-plugin-simple-import-sort` (`^12.0.0 || ^13.0.0 || ^14.0.0`) ## 🔗 Related Packages [Section titled “🔗 Related Packages”](#-related-packages) * [`@jmlweb/eslint-config-base-js`](../../eslint/base-js/): JavaScript foundation (extended by this package) * [`@jmlweb/eslint-config-react`](../../eslint/react/): React config that extends this one * [`@jmlweb/eslint-config-node`](../../eslint/node/): Node.js config that extends this one * [`@jmlweb/eslint-config-astro`](../../eslint/astro/): Astro config that extends this one * [`@jmlweb/oxlint-config-base`](../../oxlint/base/): Oxlint equivalent, recommended for new projects * [`@jmlweb/tsconfig-base`](../../typescript/base/): TypeScript config * [`@jmlweb/prettier-config-base`](../../prettier/base/): Prettier config (maintenance mode) ## 🔄 Migration Guide [Section titled “🔄 Migration Guide”](#-migration-guide) ### 2.x to 3.0 [Section titled “2.x to 3.0”](#2x-to-30) Node.js 18 and 20 are end-of-life. Node.js >= 22.12.0 is now required: it is the first Node.js 22 release that can `require()` ES modules without a flag, which the CommonJS build needs to load ESM-only plugins. Peer ranges were also widened (`eslint-plugin-simple-import-sort` 13 and 14). 1. Upgrade Node.js to 22.12.0 or later. 2. Update the package: `npm install --save-dev @jmlweb/eslint-config-base@latest`. See the [Changelog](https://github.com/jmlweb/tooling/blob/main/packages/eslint-config-base/CHANGELOG.md). ## 📄 License [Section titled “📄 License”](#-license) MIT # base-js > Base ESLint configuration for JavaScript-only projects - foundation extended by @jmlweb/eslint-config-base [![npm version](https://img.shields.io/npm/v/@jmlweb/eslint-config-base-js)](https://www.npmjs.com/package/@jmlweb/eslint-config-base-js) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D22.12.0-339933.svg)](https://nodejs.org/) [![ESLint](https://img.shields.io/badge/ESLint-9%20%7C%2010-4B32C3.svg)](https://eslint.org/) \> Base ESLint flat config for JavaScript-only projects. Foundation extended by [`@jmlweb/eslint-config-base`](../../eslint/base/). \> 🔧 **Maintenance mode**: only dependency and security updates, no new rules. For new projects use [`@jmlweb/oxlint-config-base`](../../oxlint/base/). To move an existing project, see the [migration guide](https://jmlweb.github.io/tooling/guides/migrate-to-oxc/). ## ✨ Features [Section titled “✨ Features”](#-features) * 🎯 **Recommended rules**: `@eslint/js` recommended for modern JavaScript (ES modules) * 📦 **Import sorting**: imports and exports sorted by `eslint-plugin-simple-import-sort` * 🎨 **Prettier compatible**: disables rules that conflict with Prettier * 🌐 **Environment agnostic**: no Node.js or browser globals by default * 🔧 **Composable**: extended by the TypeScript, React, Node.js and Astro configs ## 📦 Installation [Section titled “📦 Installation”](#-installation) ```bash npm install --save-dev @jmlweb/eslint-config-base-js eslint @eslint/js eslint-config-prettier eslint-plugin-simple-import-sort ``` \> 💡 **Upgrading?** See the [Migration Guide](#-migration-guide). ## 🚀 Quick Start [Section titled “🚀 Quick Start”](#-quick-start) eslint.config.js ```javascript import baseJsConfig from '@jmlweb/eslint-config-base-js'; export default [ ...baseJsConfig, // Project-specific overrides ]; ``` ## 💡 Examples [Section titled “💡 Examples”](#-examples) ### Overrides and ignores [Section titled “Overrides and ignores”](#overrides-and-ignores) eslint.config.js ```javascript import baseJsConfig from '@jmlweb/eslint-config-base-js'; export default [ ...baseJsConfig, { files: ['**/*.test.js', '**/*.spec.js'], rules: { 'no-console': 'off', }, }, { ignores: ['dist/', 'build/', 'node_modules/'], }, ]; ``` ### Node.js globals [Section titled “Node.js globals”](#nodejs-globals) eslint.config.js ```javascript import baseJsConfig from '@jmlweb/eslint-config-base-js'; import globals from 'globals'; export default [ ...baseJsConfig, { languageOptions: { globals: globals.node, }, }, ]; ``` ### Browser globals [Section titled “Browser globals”](#browser-globals) eslint.config.js ```javascript import baseJsConfig from '@jmlweb/eslint-config-base-js'; import globals from 'globals'; export default [ ...baseJsConfig, { languageOptions: { globals: globals.browser, }, }, ]; ``` `globals` is not a peer dependency; install it with `npm install --save-dev globals`. ## 📋 Configuration Details [Section titled “📋 Configuration Details”](#-configuration-details) | Setting | Value | | -------------- | -------------------------------------- | | Files | `**/*.js`, `**/*.mjs`, `**/*.cjs` | | `ecmaVersion` | `latest` | | `sourceType` | `module` | | Rules | `@eslint/js` recommended | | Import sorting | `simple-import-sort/imports`: `error` | | Export sorting | `simple-import-sort/exports`: `error` | | Prettier | `eslint-config-prettier` rules applied | ## 🔄 Import Sorting [Section titled “🔄 Import Sorting”](#-import-sorting) Imports are grouped as side effects, Node.js built-ins and external packages, internal absolute imports, then relative imports. **Before:** ```javascript import './styles.css'; import { Component } from './component'; import React from 'react'; import fs from 'fs'; ``` **After `eslint --fix`:** ```javascript import './styles.css'; import fs from 'fs'; import React from 'react'; import { Component } from './component'; ``` ## 🎯 When to Use [Section titled “🎯 When to Use”](#-when-to-use) * ✅ JavaScript-only projects (no TypeScript) * ✅ Modern ESLint 9 or 10 flat config * ✅ A foundation for your own shared config For TypeScript projects use [`@jmlweb/eslint-config-base`](../../eslint/base/). For new projects of any kind prefer [`@jmlweb/oxlint-config-base`](../../oxlint/base/). ## 🔧 Extending the Configuration [Section titled “🔧 Extending the Configuration”](#-extending-the-configuration) Spread the config, then add objects after it. Later entries win. eslint.config.js ```javascript import baseJsConfig from '@jmlweb/eslint-config-base-js'; export default [ ...baseJsConfig, { files: ['**/*.test.js'], rules: { 'no-console': 'off', }, }, ]; ``` ## 📝 Usage with Scripts [Section titled “📝 Usage with Scripts”](#-usage-with-scripts) ```json { "scripts": { "lint": "eslint .", "lint:fix": "eslint . --fix" } } ``` ```bash npm run lint ``` ## 📋 Requirements [Section titled “📋 Requirements”](#-requirements) * **Node.js** >= 22.12.0 * **ESLint** ^9.0.0 || ^10.0.0 (flat config) ## 📦 Peer Dependencies [Section titled “📦 Peer Dependencies”](#-peer-dependencies) * `eslint` (`^9.0.0 || ^10.0.0`) * `@eslint/js` (`^9.0.0 || ^10.0.0`) * `eslint-config-prettier` (`^9.1.0 || ^10.0.0`) * `eslint-plugin-simple-import-sort` (`^12.0.0 || ^13.0.0 || ^14.0.0`) No `typescript-eslint` needed. ## 🔗 Related Packages [Section titled “🔗 Related Packages”](#-related-packages) * [`@jmlweb/eslint-config-base`](../../eslint/base/): TypeScript config that extends this package (maintenance mode) * [`@jmlweb/oxlint-config-base`](../../oxlint/base/): Oxlint equivalent, recommended for new projects * [`@jmlweb/prettier-config-base`](../../prettier/base/): Prettier config (maintenance mode) ## 🔄 Migration Guide [Section titled “🔄 Migration Guide”](#-migration-guide) ### 1.x to 2.0 [Section titled “1.x to 2.0”](#1x-to-20) Node.js 18 and 20 are end-of-life. Node.js >= 22.12.0 is now required: it is the first Node.js 22 release that can `require()` ES modules without a flag, which the CommonJS build needs to load ESM-only plugins. 1. Upgrade Node.js to 22.12.0 or later. 2. Update the package: `npm install --save-dev @jmlweb/eslint-config-base-js@latest`. See the [Changelog](https://github.com/jmlweb/tooling/blob/main/packages/eslint-config-base-js/CHANGELOG.md). ## 📄 License [Section titled “📄 License”](#-license) MIT # node > ESLint configuration for Node.js projects with TypeScript, extending base config with Node.js-specific rules [![npm version](https://img.shields.io/npm/v/@jmlweb/eslint-config-node)](https://www.npmjs.com/package/@jmlweb/eslint-config-node) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D22.12.0-339933.svg)](https://nodejs.org/) [![ESLint](https://img.shields.io/badge/ESLint-9%20%7C%2010-4B32C3.svg)](https://eslint.org/) \> ESLint flat config for Node.js with TypeScript. Extends [`@jmlweb/eslint-config-base`](../../eslint/base/) with `eslint-plugin-n` rules and Node.js globals. \> 🔧 **Maintenance mode**: only dependency and security updates, no new rules. For new projects use [`@jmlweb/oxlint-config-node`](../../oxlint/node/). To move an existing project, see the [migration guide](https://jmlweb.github.io/tooling/guides/migrate-to-oxc/). ## ✨ Features [Section titled “✨ Features”](#-features) * 🟢 **Node.js rules**: `eslint-plugin-n` recommended plus stricter module, process and API rules * 🌐 **Node.js globals**: `process`, `Buffer`, `__dirname` and friends * 🔒 **Strict TypeScript**: everything from `@jmlweb/eslint-config-base` * 📦 **`node:` protocol**: built-in modules must use the `node:` prefix * 🎨 **Prettier compatible**: disables rules that conflict with Prettier ## 📦 Installation [Section titled “📦 Installation”](#-installation) ```bash npm install --save-dev @jmlweb/eslint-config-node @jmlweb/eslint-config-base eslint @eslint/js typescript-eslint eslint-config-prettier eslint-plugin-simple-import-sort eslint-plugin-n globals ``` \> 💡 **Upgrading?** See the [Migration Guide](#-migration-guide). ## 🚀 Quick Start [Section titled “🚀 Quick Start”](#-quick-start) eslint.config.js ```javascript import nodeConfig from '@jmlweb/eslint-config-node'; export default [ ...nodeConfig, // Project-specific overrides ]; ``` ## 💡 Examples [Section titled “💡 Examples”](#-examples) ### Test overrides and ignores [Section titled “Test overrides and ignores”](#test-overrides-and-ignores) eslint.config.js ```javascript import nodeConfig from '@jmlweb/eslint-config-node'; export default [ ...nodeConfig, { files: ['**/*.test.ts', '**/*.spec.ts'], rules: { '@typescript-eslint/no-explicit-any': 'off', 'n/no-process-exit': 'off', }, }, { ignores: ['dist/', 'build/', 'node_modules/'], }, ]; ``` ### Soften Node.js rules [Section titled “Soften Node.js rules”](#soften-nodejs-rules) eslint.config.js ```javascript import nodeConfig from '@jmlweb/eslint-config-node'; export default [ ...nodeConfig, { files: ['**/*.ts', '**/*.js'], rules: { 'n/no-process-exit': 'warn', 'n/no-unpublished-import': 'off', }, }, ]; ``` ## 📋 Configuration Details [Section titled “📋 Configuration Details”](#-configuration-details) Applied to `**/*.ts`, `**/*.js`, `**/*.mjs` and `**/*.cjs`, with `globals.node` enabled. ### Included [Section titled “Included”](#included) * ✅ Everything from `@jmlweb/eslint-config-base` * ✅ `eslint-plugin-n` `flat/recommended` * ✅ Import and export sorting, Prettier conflict resolution ### Key rules [Section titled “Key rules”](#key-rules) | Rule | Level | | -------------------------------------------------------------------------------- | ------------------ | | `n/no-process-exit`, `n/process-exit-as-throw` | `error` | | `n/no-missing-import`, `n/no-missing-require` | `error` | | `n/no-unpublished-import`, `n/no-unpublished-require` | `error` | | `n/no-extraneous-import`, `n/no-extraneous-require` | `error` | | `n/no-deprecated-api` | `warn` | | `n/no-callback-literal`, `n/no-new-require`, `n/no-path-concat` | `error` | | `n/prefer-global/*` (`buffer`, `console`, `process`, `url`, `url-search-params`) | `error` (`always`) | | `n/prefer-promises/dns`, `n/prefer-promises/fs` | `error` | | `n/prefer-node-protocol` | `error` | ## 🔄 Import Sorting [Section titled “🔄 Import Sorting”](#-import-sorting) Imports are sorted and type-only imports are enforced. **Before:** ```typescript import { Component } from './component'; import express from 'express'; import fs from 'node:fs'; import { User } from './types'; ``` **After `eslint --fix`:** ```typescript import express from 'express'; import fs from 'node:fs'; import { type User } from './types'; import { Component } from './component'; ``` ## 🎯 When to Use [Section titled “🎯 When to Use”](#-when-to-use) * ✅ Node.js libraries, CLIs and services written in TypeScript * ✅ Catching missing or unpublished imports and deprecated APIs * ✅ Strict type-aware linting for backend code For non-Node.js TypeScript use [`@jmlweb/eslint-config-base`](../../eslint/base/). For new projects prefer [`@jmlweb/oxlint-config-node`](../../oxlint/node/). ## 🔧 Extending the Configuration [Section titled “🔧 Extending the Configuration”](#-extending-the-configuration) Spread the config, then add objects after it. Later entries win. eslint.config.js ```javascript import nodeConfig from '@jmlweb/eslint-config-node'; export default [ ...nodeConfig, { files: ['scripts/**/*.js'], rules: { 'n/no-process-exit': 'off', }, }, ]; ``` ## 📝 Usage with Scripts [Section titled “📝 Usage with Scripts”](#-usage-with-scripts) ```json { "scripts": { "lint": "eslint .", "lint:fix": "eslint . --fix" } } ``` ```bash npm run lint ``` ## 📋 Requirements [Section titled “📋 Requirements”](#-requirements) * **Node.js** >= 22.12.0 * **ESLint** ^9.0.0 || ^10.0.0 (flat config) * **TypeScript** project with a `tsconfig.json` (versions supported by typescript-eslint 8) ## 📦 Peer Dependencies [Section titled “📦 Peer Dependencies”](#-peer-dependencies) * `eslint` (`^9.0.0 || ^10.0.0`) * `@eslint/js` (`^9.0.0 || ^10.0.0`) * `typescript-eslint` (`^8.0.0`) * `eslint-config-prettier` (`^9.1.0 || ^10.0.0`) * `eslint-plugin-n` (`^17.0.0 || ^18.0.0`) * `eslint-plugin-simple-import-sort` (`^12.0.0 || ^13.0.0 || ^14.0.0`) * `globals` (`^15.0.0 || ^16.0.0 || ^17.0.0`) * `@jmlweb/eslint-config-base` (`^3.0.3`) ## 🔗 Related Packages [Section titled “🔗 Related Packages”](#-related-packages) * [`@jmlweb/eslint-config-base`](../../eslint/base/): TypeScript config (extended by this package) * [`@jmlweb/oxlint-config-node`](../../oxlint/node/): Oxlint equivalent, recommended for new projects * [`@jmlweb/tsconfig-node`](../../typescript/node/): TypeScript config for Node.js * [`@jmlweb/prettier-config-base`](../../prettier/base/): Prettier config (maintenance mode) ## 🔄 Migration Guide [Section titled “🔄 Migration Guide”](#-migration-guide) ### 2.x to 3.0 [Section titled “2.x to 3.0”](#2x-to-30) Node.js 18 and 20 are end-of-life. Node.js >= 22.12.0 is now required: it is the first Node.js 22 release that can `require()` ES modules without a flag, which the CommonJS build needs to load ESM-only plugins. Peer ranges were also widened (`eslint-plugin-n` 18, `eslint-plugin-simple-import-sort` 13 and 14). 1. Upgrade Node.js to 22.12.0 or later. 2. Update the package: `npm install --save-dev @jmlweb/eslint-config-node@latest`. See the [Changelog](https://github.com/jmlweb/tooling/blob/main/packages/eslint-config-node/CHANGELOG.md). ## 📄 License [Section titled “📄 License”](#-license) MIT # react > ESLint configuration for React libraries with TypeScript, extending base config with React-specific rules [![npm version](https://img.shields.io/npm/v/@jmlweb/eslint-config-react)](https://www.npmjs.com/package/@jmlweb/eslint-config-react) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D22.12.0-339933.svg)](https://nodejs.org/) [![ESLint](https://img.shields.io/badge/ESLint-9%20%7C%2010-4B32C3.svg)](https://eslint.org/) [![React](https://img.shields.io/badge/React-17%2B-61DAFB.svg)](https://react.dev/) \> ESLint flat config for React with TypeScript. Extends [`@jmlweb/eslint-config-base`](../../eslint/base/) with React, Hooks and JSX rules. \> 🔧 **Maintenance mode**: only dependency and security updates, no new rules. For new projects use [`@jmlweb/oxlint-config-react`](../../oxlint/react/). To move an existing project, see the [migration guide](https://jmlweb.github.io/tooling/guides/migrate-to-oxc/). ## ✨ Features [Section titled “✨ Features”](#-features) * ⚛️ **React rules**: `@eslint-react` `recommended-typescript` preset * 🪝 **Hooks and React Compiler rules**: official `eslint-plugin-react-hooks` `recommended` preset, applied to every file * 🧩 **JSX style**: PascalCase, self-closing, no needless braces or fragments, sorted props * 🔒 **Strict TypeScript**: everything from `@jmlweb/eslint-config-base` * 🎨 **Prettier compatible**: disables rules that conflict with Prettier ## 📦 Installation [Section titled “📦 Installation”](#-installation) ```bash npm install --save-dev @jmlweb/eslint-config-react @jmlweb/eslint-config-base eslint @eslint/js typescript-eslint eslint-config-prettier eslint-plugin-simple-import-sort @eslint-react/eslint-plugin @stylistic/eslint-plugin eslint-plugin-perfectionist eslint-plugin-react-hooks ``` \> 💡 **Upgrading?** See the [Migration Guide](#-migration-guide). ## 🚀 Quick Start [Section titled “🚀 Quick Start”](#-quick-start) eslint.config.js ```javascript import reactConfig from '@jmlweb/eslint-config-react'; export default [ ...reactConfig, // Project-specific overrides ]; ``` ## 💡 Examples [Section titled “💡 Examples”](#-examples) ### Test overrides and ignores [Section titled “Test overrides and ignores”](#test-overrides-and-ignores) eslint.config.js ```javascript import reactConfig from '@jmlweb/eslint-config-react'; export default [ ...reactConfig, { files: ['**/*.test.tsx', '**/*.spec.tsx'], rules: { '@typescript-eslint/no-explicit-any': 'off', '@eslint-react/no-array-index-key': 'off', }, }, { ignores: ['dist/', 'build/', 'node_modules/'], }, ]; ``` ### Explicit React version [Section titled “Explicit React version”](#explicit-react-version) eslint.config.js ```javascript import reactConfig from '@jmlweb/eslint-config-react'; export default [ ...reactConfig, { settings: { 'react-x': { version: '19.2', // default: 'detect' }, }, }, ]; ``` ### Lower a strict Hooks rule while adopting [Section titled “Lower a strict Hooks rule while adopting”](#lower-a-strict-hooks-rule-while-adopting) eslint.config.js ```javascript import reactConfig from '@jmlweb/eslint-config-react'; export default [ ...reactConfig, { rules: { 'react-hooks/set-state-in-effect': 'warn', }, }, ]; ``` ## 📋 Configuration Details [Section titled “📋 Configuration Details”](#-configuration-details) ### Scope [Section titled “Scope”](#scope) | Rules | Applied to | | ---------------------------------------------------- | -------------------------- | | `@eslint-react` preset, `react-hooks` preset | all files (hooks in `.ts`) | | JSX style, import sorting, PascalCase function names | `**/*.tsx`, `**/*.jsx` | Hooks rules come from `eslint-plugin-react-hooks`. `@eslint-react`’s ports of the same rules are turned off, so each problem is reported once as `react-hooks/*`. ### Key rules [Section titled “Key rules”](#key-rules) | Rule | Level | Description | | ----------------------------------------------- | ------- | -------------------------------------------- | | `react-hooks/rules-of-hooks` | `error` | Rules of Hooks | | `react-hooks/exhaustive-deps` | `warn` | Hook dependencies | | `react-hooks/purity`, `refs`, `immutability`, … | `error` | React Compiler rules | | `@eslint-react/no-missing-key` | `error` | Missing keys in lists | | `@eslint-react/no-array-index-key` | `warn` | Array index as key | | `@eslint-react/jsx-no-children-prop` | `error` | `children` passed as a prop | | `@eslint-react/jsx-no-useless-fragment` | `error` | Unnecessary fragments | | `@eslint-react/dom-no-unknown-property` | `error` | Unknown DOM properties (`class`, …) | | `@eslint-react/dom-no-unsafe-target-blank` | `error` | `target="_blank"` without `rel` | | `@stylistic/jsx-pascal-case` | `error` | PascalCase component names | | `@stylistic/jsx-self-closing-comp` | `error` | Self-close elements without children | | `@stylistic/jsx-curly-brace-presence` | `error` | No unnecessary curly braces | | `perfectionist/sort-jsx-props` | `error` | Reserved, shorthand, regular, then callbacks | The naming convention is relaxed so functions may be `PascalCase` (components). The React version is auto-detected. ## 🔄 Import Sorting [Section titled “🔄 Import Sorting”](#-import-sorting) Imports are sorted and type-only imports are enforced. **Before:** ```typescript import { Component } from './component'; import React, { useState } from 'react'; import { User } from './types'; ``` **After `eslint --fix`:** ```typescript import React, { useState } from 'react'; import { type User } from './types'; import { Component } from './component'; ``` ## 🎯 When to Use [Section titled “🎯 When to Use”](#-when-to-use) * ✅ React libraries and apps written in TypeScript * ✅ Strict Hooks and React Compiler checks, even without the Compiler * ✅ Consistent JSX style across a team For non-React TypeScript use [`@jmlweb/eslint-config-base`](../../eslint/base/). For new projects prefer [`@jmlweb/oxlint-config-react`](../../oxlint/react/). ## 🔧 Extending the Configuration [Section titled “🔧 Extending the Configuration”](#-extending-the-configuration) Spread the config, then add objects after it. Later entries win. eslint.config.js ```javascript import reactConfig from '@jmlweb/eslint-config-react'; export default [ ...reactConfig, { files: ['**/*.tsx'], rules: { '@stylistic/jsx-curly-brace-presence': 'off', }, }, ]; ``` ## 📝 Usage with Scripts [Section titled “📝 Usage with Scripts”](#-usage-with-scripts) ```json { "scripts": { "lint": "eslint .", "lint:fix": "eslint . --fix" } } ``` ```bash npm run lint ``` ## 📋 Requirements [Section titled “📋 Requirements”](#-requirements) * **Node.js** >= 22.12.0 * **ESLint** ^9.0.0 || ^10.0.0 (flat config) * **React** >= 17 (modern JSX transform) * **TypeScript** project with a `tsconfig.json` (versions supported by typescript-eslint 8) ## 📦 Peer Dependencies [Section titled “📦 Peer Dependencies”](#-peer-dependencies) * `eslint` (`^9.0.0 || ^10.0.0`) * `@eslint/js` (`^9.0.0 || ^10.0.0`) * `typescript-eslint` (`^8.0.0`) * `eslint-config-prettier` (`^9.1.0 || ^10.0.0`) * `eslint-plugin-simple-import-sort` (`^12.0.0 || ^13.0.0 || ^14.0.0`) * `@eslint-react/eslint-plugin` (`^5.0.0`) * `@stylistic/eslint-plugin` (`^5.0.0`) * `eslint-plugin-perfectionist` (`^5.0.0`) * `eslint-plugin-react-hooks` (`^7.0.0`) * `@jmlweb/eslint-config-base` (`^3.0.3`) ## 🔗 Related Packages [Section titled “🔗 Related Packages”](#-related-packages) * [`@jmlweb/eslint-config-base`](../../eslint/base/): TypeScript config (extended by this package) * [`@jmlweb/oxlint-config-react`](../../oxlint/react/): Oxlint equivalent, recommended for new projects * [`@jmlweb/tsconfig-react`](../../typescript/react/): TypeScript config for React * [`@jmlweb/prettier-config-base`](../../prettier/base/): Prettier config (maintenance mode) ## 🔄 Migration Guide [Section titled “🔄 Migration Guide”](#-migration-guide) ### 5.x to 6.0 [Section titled “5.x to 6.0”](#5x-to-60) Hooks rules now come from `eslint-plugin-react-hooks`’ `recommended` preset instead of two hand-picked rules. * React Compiler rules are enabled, most as errors (`purity`, `refs`, `immutability`, `set-state-in-effect`, `static-components`, …) * Hooks rules also apply to `.ts` files 1. Run `npx eslint .` and fix the new reports. 2. To adopt gradually, lower rules to `warn`, for example `'react-hooks/set-state-in-effect': 'warn'`. ### 4.x to 5.0 [Section titled “4.x to 5.0”](#4x-to-50) `eslint-plugin-react` is replaced by `@eslint-react/eslint-plugin`, which supports ESLint 10. * Remove `eslint-plugin-react`; add `@eslint-react/eslint-plugin`, `@stylistic/eslint-plugin` and `eslint-plugin-perfectionist` * `react/*` rules are now `@eslint-react/*`, `@stylistic/*` or `perfectionist/sort-jsx-props` * `jsx-boolean-value`, `jsx-fragments` and `no-unescaped-entities` are no longer enforced * `settings.react.version` is now `settings['react-x'].version` 1. Swap the dependencies: ```bash npm uninstall eslint-plugin-react npm install --save-dev @eslint-react/eslint-plugin @stylistic/eslint-plugin eslint-plugin-perfectionist ``` 2. Rename `react/*` rules in overrides and `eslint-disable` comments. Most keep their name under `@eslint-react/` (`react/no-array-index-key` becomes `@eslint-react/no-array-index-key`). These change name: | 4.x | 5.0 | | -------------------------------- | ------------------------------------------------------------------------------- | | `react/jsx-key` | `@eslint-react/no-missing-key` | | `react/no-children-prop` | `@eslint-react/jsx-no-children-prop` | | `react/no-danger-with-children` | `@eslint-react/dom-no-dangerously-set-innerhtml-with-children` | | `react/no-find-dom-node` | `@eslint-react/dom-no-find-dom-node` | | `react/no-render-return-value` | `@eslint-react/dom-no-render-return-value` | | `react/no-unknown-property` | `@eslint-react/dom-no-unknown-property` | | `react/no-deprecated` | `@eslint-react/no-component-will-*`, `@eslint-react/dom-no-render`, and similar | | `react/display-name` (off) | `@eslint-react/no-missing-component-display-name` (off) | | `react/jsx-pascal-case` | `@stylistic/jsx-pascal-case` | | `react/self-closing-comp` | `@stylistic/jsx-self-closing-comp` | | `react/jsx-curly-brace-presence` | `@stylistic/jsx-curly-brace-presence` | | `react/jsx-sort-props` | `perfectionist/sort-jsx-props` | 3. Run `npx eslint --fix .` and review the remaining reports. ### 3.x to 4.0 [Section titled “3.x to 4.0”](#3x-to-40) Node.js >= 22.12.0 is now required (see the [Changelog](https://github.com/jmlweb/tooling/blob/main/packages/eslint-config-react/CHANGELOG.md)). ## 📄 License [Section titled “📄 License”](#-license) MIT # Getting Started > Learn how to install and use jmlweb-tooling configuration packages Welcome to **jmlweb-tooling**! This guide will help you get started with our shared configuration packages for modern JavaScript and TypeScript projects. ## What is jmlweb-tooling? [Section titled “What is jmlweb-tooling?”](#what-is-jmlweb-tooling) jmlweb-tooling is a collection of shared configuration packages that provide consistent, opinionated settings for popular development tools: * **Oxfmt** - Fast code formatting with import sorting (recommended) * **Oxlint** - Fast linting for JavaScript, TypeScript, React and Node.js, with type-aware rules (recommended) * **Prettier and ESLint** - The previous formatter and linter configurations, in maintenance mode * **TypeScript** - Strict TypeScript configurations for various project types * **Testing** - Pre-configured Vitest and Jest setups * **Build Tools** - Ready-to-use tsdown and Vite configurations * **Commit Linting** - Conventional Commits enforcement ### Why Use Shared Configurations? [Section titled “Why Use Shared Configurations?”](#why-use-shared-configurations) * **Consistency** - Same code style across all your projects * **Zero Configuration** - Works out of the box with sensible defaults * **Best Practices** - Based on industry standards and modern conventions * **Easy Updates** - Update one package to get improvements across all projects * **Monorepo Friendly** - Designed to work seamlessly in monorepo setups Browse all available packages in the sidebar navigation or explore specific categories below. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) Before getting started, ensure you have: * **Node.js** >= 22.12.0 ([Download](https://nodejs.org/)) * A package manager: **pnpm** (recommended), npm, or yarn Check your Node.js version: ```bash node --version ``` If you don’t have pnpm installed: ```bash npm install -g pnpm ``` ## Quick Start [Section titled “Quick Start”](#quick-start) Let’s set up a new TypeScript project with Oxfmt, Oxlint and TypeScript configurations. ### 1. Install the Formatter [Section titled “1. Install the Formatter”](#1-install-the-formatter) Install the Oxfmt configuration and Oxfmt: ```bash pnpm add -D @jmlweb/oxfmt-config-base oxfmt ``` Create an `oxfmt.config.ts` file: ```typescript import baseConfig from '@jmlweb/oxfmt-config-base'; import { defineConfig } from 'oxfmt'; export default defineConfig({ ...baseConfig, }); ``` Format your code: ```bash pnpm exec oxfmt ``` ### 2. Install the Linter [Section titled “2. Install the Linter”](#2-install-the-linter) Install the Oxlint configuration, Oxlint and its type-aware engine: ```bash pnpm add -D @jmlweb/oxlint-config-base oxlint oxlint-tsgolint ``` Create an `oxlint.config.ts` file: ```typescript import baseConfig from '@jmlweb/oxlint-config-base'; import { defineConfig } from 'oxlint'; export default defineConfig({ extends: [baseConfig], options: { typeAware: true, }, }); ``` Run Oxlint: ```bash pnpm exec oxlint ``` ### 3. Install TypeScript Configuration [Section titled “3. Install TypeScript Configuration”](#3-install-typescript-configuration) Install the TypeScript configuration: ```bash pnpm add -D @jmlweb/tsconfig-base typescript ``` Create a `tsconfig.json` file: ```json { "extends": "@jmlweb/tsconfig-base", "compilerOptions": { "outDir": "dist" }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] } ``` Your project is now configured with Oxfmt, Oxlint and TypeScript! Already using the Prettier and ESLint packages? See [Migrate to Oxc](../guides/migrate-to-oxc/). ## Configuration Hierarchy [Section titled “Configuration Hierarchy”](#configuration-hierarchy) jmlweb-tooling packages follow a **base → variant** pattern, allowing you to start with sensible defaults and extend for specific use cases. ### Formatter Configurations [Section titled “Formatter Configurations”](#formatter-configurations) ```plaintext @jmlweb/oxfmt-config-base (formatting, import sorting, optional Tailwind CSS class sorting) ``` The Prettier configurations are maintained for existing projects: ```plaintext @jmlweb/prettier-config-base (base formatting rules) └── @jmlweb/prettier-config-tailwind (adds Tailwind CSS class sorting) ``` ### Linter Configurations [Section titled “Linter Configurations”](#linter-configurations) ```plaintext @jmlweb/oxlint-config-base (JavaScript and TypeScript linting) ├── @jmlweb/oxlint-config-react (adds React, hooks and JSX rules) └── @jmlweb/oxlint-config-node (adds Node.js rules) ``` The ESLint configurations are maintained for existing projects, and `eslint-config-astro` remains the way to lint `.astro` files: ```plaintext @jmlweb/eslint-config-base-js (JavaScript linting) └── @jmlweb/eslint-config-base (adds TypeScript support) ├── @jmlweb/eslint-config-react (adds React and JSX rules) ├── @jmlweb/eslint-config-node (adds Node.js specific rules) └── @jmlweb/eslint-config-astro (adds Astro framework rules) ``` ### TypeScript Configurations [Section titled “TypeScript Configurations”](#typescript-configurations) ```plaintext @jmlweb/tsconfig-base (strict base configuration) ├── @jmlweb/tsconfig-node (Node.js projects) ├── @jmlweb/tsconfig-react (React libraries) ├── @jmlweb/tsconfig-nextjs (Next.js applications) └── @jmlweb/tsconfig-astro (Astro projects) ``` **Example**: Extend `tsconfig-node` for backend services, `tsconfig-nextjs` for Next.js apps. ### Testing Configurations [Section titled “Testing Configurations”](#testing-configurations) ```plaintext @jmlweb/vitest-config (Vitest with TypeScript and coverage) @jmlweb/jest-config (Jest with TypeScript support) ``` ### Build Tool Configurations [Section titled “Build Tool Configurations”](#build-tool-configurations) ```plaintext @jmlweb/tsdown-config-base (tsdown for building TypeScript packages) @jmlweb/vite-config (Vite for frontend projects) @jmlweb/tsup-config-base (deprecated, use tsdown-config-base) ``` ## Common Patterns [Section titled “Common Patterns”](#common-patterns) ### New TypeScript Project [Section titled “New TypeScript Project”](#new-typescript-project) Setting up a basic TypeScript project from scratch: ```bash # Initialize project mkdir my-project && cd my-project pnpm init # Install configurations pnpm add -D @jmlweb/oxfmt-config-base oxfmt pnpm add -D @jmlweb/oxlint-config-base oxlint oxlint-tsgolint pnpm add -D @jmlweb/tsconfig-node typescript # Create source directory mkdir src ``` **package.json**: ```json { "name": "my-project", "type": "module", "scripts": { "build": "tsc", "lint": "oxlint", "format": "oxfmt" } } ``` **tsconfig.json**: ```json { "extends": "@jmlweb/tsconfig-node", "compilerOptions": { "outDir": "dist" }, "include": ["src/**/*"] } ``` Add the `oxfmt.config.ts` and `oxlint.config.ts` files from the [Quick Start](#quick-start). ### React TypeScript Project [Section titled “React TypeScript Project”](#react-typescript-project) For a React application with TypeScript: ```bash # Install dependencies pnpm add -D @jmlweb/oxfmt-config-base oxfmt pnpm add -D @jmlweb/oxlint-config-react oxlint oxlint-tsgolint @eslint-react/eslint-plugin eslint-plugin-perfectionist pnpm add -D @jmlweb/tsconfig-react typescript # For Vite-based projects pnpm add -D @jmlweb/vite-config vite ``` **oxlint.config.ts**: ```typescript import reactConfig from '@jmlweb/oxlint-config-react'; import { defineConfig } from 'oxlint'; export default defineConfig({ extends: [reactConfig], options: { typeAware: true, }, }); ``` **tsconfig.json**: ```json { "extends": "@jmlweb/tsconfig-react", "compilerOptions": { "outDir": "dist" }, "include": ["src"] } ``` **vite.config.ts** (if using Vite): ```typescript import { createReactViteConfig } from '@jmlweb/vite-config'; export default createReactViteConfig(); ``` For Tailwind CSS, enable class sorting in `oxfmt.config.ts` with `sortTailwindcss: { stylesheet: './src/index.css' }`. ### Next.js Project [Section titled “Next.js Project”](#nextjs-project) For a Next.js application: ```bash # Install configurations pnpm add -D @jmlweb/oxfmt-config-base oxfmt pnpm add -D @jmlweb/oxlint-config-react oxlint oxlint-tsgolint @eslint-react/eslint-plugin eslint-plugin-perfectionist pnpm add -D @jmlweb/tsconfig-nextjs typescript ``` **tsconfig.json**: ```json { "extends": "@jmlweb/tsconfig-nextjs", "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"] } ``` ### Node.js API Project [Section titled “Node.js API Project”](#nodejs-api-project) For a Node.js backend service: ```bash # Install configurations pnpm add -D @jmlweb/oxfmt-config-base oxfmt pnpm add -D @jmlweb/oxlint-config-node oxlint oxlint-tsgolint eslint-plugin-n pnpm add -D @jmlweb/tsconfig-node typescript pnpm add -D @jmlweb/vitest-config vitest ``` **oxlint.config.ts**: ```typescript import nodeConfig from '@jmlweb/oxlint-config-node'; import { defineConfig } from 'oxlint'; export default defineConfig({ extends: [nodeConfig], options: { typeAware: true, }, }); ``` **vitest.config.ts**: ```typescript import baseConfig from '@jmlweb/vitest-config'; import { defineConfig } from 'vitest/config'; export default defineConfig({ ...baseConfig, test: { ...baseConfig.test, }, }); ``` ### Monorepo Setup [Section titled “Monorepo Setup”](#monorepo-setup) For workspace/monorepo dependencies, use the `workspace:*` protocol: **Root package.json**: ```json { "devDependencies": { "@jmlweb/oxfmt-config-base": "workspace:*", "@jmlweb/oxlint-config-base": "workspace:*", "@jmlweb/tsconfig-base": "workspace:*" } } ``` This allows all packages in the monorepo to share the same configuration versions. See [Monorepos](../guides/migrate-to-oxc/#monorepos) for how Oxlint resolves configs and type-aware rules across packages. ## Extending Configurations [Section titled “Extending Configurations”](#extending-configurations) All configurations can be extended to customize for your specific needs: ### Extending Oxfmt [Section titled “Extending Oxfmt”](#extending-oxfmt) oxfmt.config.ts ```typescript import baseConfig from '@jmlweb/oxfmt-config-base'; import { defineConfig } from 'oxfmt'; export default defineConfig({ ...baseConfig, // Override specific options printWidth: 100, semi: false, }); ``` ### Extending Oxlint [Section titled “Extending Oxlint”](#extending-oxlint) oxlint.config.ts ```typescript import baseConfig from '@jmlweb/oxlint-config-base'; import { defineConfig } from 'oxlint'; export default defineConfig({ extends: [baseConfig], options: { typeAware: true, }, rules: { // Override or add specific rules 'eslint/no-unused-vars': 'warn', }, }); ``` ### Extending TypeScript [Section titled “Extending TypeScript”](#extending-typescript) ```json { "extends": "@jmlweb/tsconfig-base", "compilerOptions": { "target": "ES2022", "lib": ["ES2022"], "outDir": "dist", "rootDir": "src" } } ``` ## Package Manager Scripts [Section titled “Package Manager Scripts”](#package-manager-scripts) Add these common scripts to your `package.json`: ```json { "scripts": { "build": "tsc", "lint": "oxlint", "lint:fix": "oxlint --fix", "format": "oxfmt", "format:check": "oxfmt --check", "test": "vitest", "type-check": "tsc --noEmit" } } ``` Run them with: ```bash pnpm lint # Check for linting errors pnpm lint:fix # Fix auto-fixable errors pnpm format # Format all files pnpm test # Run tests ``` ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### Type-Aware Rules Do Not Report [Section titled “Type-Aware Rules Do Not Report”](#type-aware-rules-do-not-report) `options.typeAware` must be set in your own `oxlint.config.ts`, and `oxlint-tsgolint` must be installed. Oxlint ignores `options` in configs loaded through `extends`. ### IDE Integration [Section titled “IDE Integration”](#ide-integration) For VS Code, install the [Oxc extension](https://marketplace.visualstudio.com/items?itemName=oxc.oxc-vscode), which runs both Oxfmt and Oxlint. Add to `.vscode/settings.json`: ```json { "editor.defaultFormatter": "oxc.oxc-vscode", "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.oxc": "always" } } ``` ### Package Not Found [Section titled “Package Not Found”](#package-not-found) If you see `Cannot find package '@jmlweb/...'`: 1. Ensure you’ve installed the package: `pnpm add -D @jmlweb/package-name` 2. Check the package name is correct (all lowercase, with hyphens) 3. Run `pnpm install` to ensure dependencies are installed ## Next Steps [Section titled “Next Steps”](#next-steps) Now that you’re set up, explore the documentation for specific packages: * **[Oxfmt](../oxfmt/base/)** - Formatting options and import sorting * **[Oxlint](../oxlint/base/)** - Linting rules and configuration * **[Migrate to Oxc](../guides/migrate-to-oxc/)** - Move an existing project from Prettier and ESLint * **[TypeScript](../typescript/base/)** - Type checking and compiler options * **[Testing](../testing/vitest/)** - Test configuration and coverage * **[Build Tools](../build-tools/tsdown/)** - Building and bundling your code ### Contributing [Section titled “Contributing”](#contributing) Found an issue or have a suggestion? Contributions are welcome! * Report bugs or request features: [GitHub Issues](https://github.com/jmlweb/tooling/issues) * View source code: [GitHub Repository](https://github.com/jmlweb/tooling) ### Stay Updated [Section titled “Stay Updated”](#stay-updated) Follow semantic versioning for updates: * **Patch** (1.0.x) - Bug fixes, safe to update * **Minor** (1.x.0) - New features, backward compatible * **Major** (x.0.0) - Breaking changes, read migration guides Check changelogs in each package’s documentation for version history. # Migrate to Oxc > Move a project from the Prettier and ESLint packages to Oxfmt and Oxlint [Oxfmt](https://oxc.rs/docs/guide/usage/formatter.html) and [Oxlint](https://oxc.rs/docs/guide/usage/linter.html) are the recommended formatter and linter for new projects. They are written in Rust, run in milliseconds, and the `@jmlweb` Oxc packages keep the same conventions as the Prettier and ESLint ones. The Prettier and ESLint packages are in maintenance mode: they keep receiving dependency and security updates, but no new rules. Migrate a project when you are already touching its formatting or lint setup. ## Which Packages to Use [Section titled “Which Packages to Use”](#which-packages-to-use) | Project | Formatter | Linter | | ------------------ | --------------------------- | ------------------------------------------------------------------------------- | | JavaScript | `@jmlweb/oxfmt-config-base` | `@jmlweb/oxlint-config-base` | | TypeScript library | `@jmlweb/oxfmt-config-base` | `@jmlweb/oxlint-config-base` | | React | `@jmlweb/oxfmt-config-base` | `@jmlweb/oxlint-config-react` | | Node.js | `@jmlweb/oxfmt-config-base` | `@jmlweb/oxlint-config-node` | | Astro | `@jmlweb/oxfmt-config-base` | `@jmlweb/oxlint-config-base` + `@jmlweb/eslint-config-astro` for `.astro` files | Each package page lists what it replaces and what it does not port. ## Formatter: Prettier to Oxfmt [Section titled “Formatter: Prettier to Oxfmt”](#formatter-prettier-to-oxfmt) `@jmlweb/oxfmt-config-base` produces the same output as `@jmlweb/prettier-config-base`, except that it also sorts imports. 1. Install the packages and remove Prettier: ```bash pnpm add -D @jmlweb/oxfmt-config-base oxfmt pnpm remove @jmlweb/prettier-config-base @jmlweb/prettier-config-tailwind prettier prettier-plugin-tailwindcss ``` 2. Replace `.prettierrc*` (or the `prettier` field in `package.json`) with an `oxfmt.config.ts`: ```typescript import baseConfig from '@jmlweb/oxfmt-config-base'; import { defineConfig } from 'oxfmt'; export default defineConfig({ ...baseConfig, }); ``` For Tailwind CSS, replace `@jmlweb/prettier-config-tailwind` with Oxfmt’s built-in class sorting: ```typescript import baseConfig from '@jmlweb/oxfmt-config-base'; import { defineConfig } from 'oxfmt'; export default defineConfig({ ...baseConfig, sortTailwindcss: { stylesheet: './src/index.css', }, }); ``` 3. Move `.prettierignore` entries to `ignorePatterns` in the same file, then delete `.prettierignore`. 4. Update the scripts and `lint-staged`: ```json { "scripts": { "format": "oxfmt", "format:check": "oxfmt --check" }, "lint-staged": { "*.{js,mjs,cjs,ts,mts,cts,jsx,tsx,json,md,css}": ["oxfmt"] } } ``` 5. Run `pnpm format` once and commit the result. Expect import order changes and, rarely, a different line break than Prettier’s. With `sortTailwindcss.stylesheet`, Oxfmt also moves custom theme classes such as `bg-ocean-600` to their correct position: `@jmlweb/prettier-config-tailwind` never pointed the Tailwind plugin at a stylesheet, so it put them first. Oxfmt also formats Markdown by default and rewraps hand-written docs. To leave them as they are, add `'**/*.md'` to `ignorePatterns` and remove `md` from the `lint-staged` pattern. ### Import Sorting [Section titled “Import Sorting”](#import-sorting) Oxfmt sorts imports in the same order as `eslint-plugin-perfectionist`’s `sort-imports` defaults, so the linter no longer needs a sorting rule. If the project still lints with ESLint, its `eslint-plugin-simple-import-sort` disagrees with this order: set `sortImports: false` until you migrate the linter. Side-effect imports such as `import './polyfill'` are sorted into their group too. Keep order-dependent ones in a separate entry file. ## Linter: ESLint to Oxlint [Section titled “Linter: ESLint to Oxlint”](#linter-eslint-to-oxlint) The Oxlint packages port the ESLint ones rule by rule, including typescript-eslint’s type-aware rules. 1. Install the package for your project type with its peer dependencies, and remove ESLint: ```bash # TypeScript or JavaScript pnpm add -D @jmlweb/oxlint-config-base oxlint oxlint-tsgolint # React pnpm add -D @jmlweb/oxlint-config-react oxlint oxlint-tsgolint @eslint-react/eslint-plugin eslint-plugin-perfectionist # Node.js pnpm add -D @jmlweb/oxlint-config-node oxlint oxlint-tsgolint eslint-plugin-n pnpm remove eslint @eslint/js typescript-eslint eslint-config-prettier eslint-plugin-simple-import-sort @jmlweb/eslint-config-base ``` Also remove the `@jmlweb/eslint-config-*` package and plugins the old config used. `eslint-plugin-n` stays for Node.js projects, since Oxlint runs it as a plugin. The ESLint plugins that Oxlint runs, `@eslint-react/eslint-plugin`, `eslint-plugin-perfectionist` and `eslint-plugin-n`, declare `eslint` as a peer dependency. Your package manager installs it, so `eslint` stays in the lockfile even though nothing runs it. 2. Replace `eslint.config.*` with an `oxlint.config.ts`: ```typescript import reactConfig from '@jmlweb/oxlint-config-react'; import { defineConfig } from 'oxlint'; export default defineConfig({ extends: [reactConfig], options: { typeAware: true, }, }); ``` `options.typeAware` must be in your own config: Oxlint ignores `options` in configs loaded through `extends`, and skips the type-aware rules without telling you. 3. Update the scripts and `lint-staged`: ```json { "scripts": { "lint": "oxlint", "lint:fix": "oxlint --fix" }, "lint-staged": { "*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}": ["oxlint --fix"] } } ``` 4. Run `pnpm lint` and fix the reported problems. The rules are the same as before, so most findings point to code the ESLint setup did not cover, such as files it ignored. ### What Is Not Ported [Section titled “What Is Not Ported”](#what-is-not-ported) * **`@typescript-eslint/naming-convention`**: It needs typescript-eslint’s parser, which Oxlint cannot run * **Import sorting rules**: Oxfmt sorts imports (see [Import Sorting](#import-sorting)) * **`eslint-config-prettier`**: Not needed, the Oxlint configs enable no formatting rules * **React Compiler `config` and `gating` checks**: They only validate compiler options ## Astro Projects [Section titled “Astro Projects”](#astro-projects) Oxlint only lints the `