Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/add-oxlint-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/oxlint-config': minor
---

Add `@tanstack/oxlint-config`, a port of `@tanstack/eslint-config` to Oxlint
4 changes: 4 additions & 0 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
{
"label": "Utilities",
"children": [
{
"label": "Oxlint",
"to": "oxlint"
},
{
"label": "ESLint",
"to": "eslint"
Expand Down
1 change: 1 addition & 0 deletions docs/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ The following tools are required to use these packages:

## Utilities

- [Oxlint](./oxlint.md)
- [ESLint](./eslint.md)
- [Publish](./publish.md)
- [Vite](./vite.md)
Expand Down
44 changes: 44 additions & 0 deletions docs/oxlint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
id: oxlint
title: Oxlint
---

## Purpose

This package unifies the shared [Oxlint](https://oxc.rs/docs/guide/usage/linter) config used across all TanStack projects. It is designed to be framework-agnostic, and does not include any framework-specific plugins.

## Installation

To install the package, run the following command:

```bash
pnpm add -D @tanstack/oxlint-config
```

## Setup

### package.json

- Make sure you have Oxlint v1.87+ installed
- `oxlint-tsgolint` is required, as the config enables type-aware linting

### oxlint.config.ts

```ts
import { tanstackConfig } from '@tanstack/oxlint-config'
import { defineConfig } from 'oxlint'

export default defineConfig({
extends: [tanstackConfig],
// Not inherited through `extends`
env: tanstackConfig.env,
ignorePatterns: tanstackConfig.ignorePatterns,
rules: {
// Custom rules go here
},
})
```

## Rules

You can inspect the enabled rules by browsing the source [here](https://github.com/TanStack/config/tree/main/packages/oxlint-config). Each rule has a comment explaining why it is included in the shared config.
2 changes: 1 addition & 1 deletion nx.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"cache": true,
"inputs": ["{workspaceRoot}/**/package.json"]
},
"test:eslint": {
"test:oxlint": {
"cache": true
},
"test:types": {
Expand Down
11 changes: 4 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
"scripts": {
"clean": "pnpm --filter \"./packages/**\" run clean",
"test": "pnpm run test:ci",
"test:pr": "nx affected --targets=test:sherif,test:docs,test:eslint,test:types,test:build,build",
"test:ci": "nx run-many --targets=test:sherif,test:docs,test:eslint,test:types,test:build,build",
"test:pr": "nx affected --targets=test:sherif,test:docs,test:oxlint,test:types,test:build,build",
"test:ci": "nx run-many --targets=test:sherif,test:docs,test:oxlint,test:types,test:build,build",
"test:build": "nx affected --target=test:build",
"test:types": "nx affected --target=test:types",
"test:eslint": "nx affected --target=test:eslint",
"test:oxlint": "nx affected --target=test:oxlint",
"test:sherif": "sherif",
"test:docs": "node scripts/verify-links.ts",
"build": "nx affected --target=build",
Expand All @@ -40,8 +40,6 @@
"@changesets/changelog-github": "catalog:",
"@changesets/cli": "catalog:",
"@types/node": "catalog:",
"eslint": "catalog:",
"jiti": "catalog:",
"jsdom": "catalog:",
"markdown-link-extractor": "catalog:",
"nx": "catalog:",
Expand All @@ -51,7 +49,6 @@
"sherif": "catalog:",
"tinyglobby": "catalog:",
"tsdown": "catalog:",
"typescript": "catalog:",
"typescript-eslint": "catalog:"
"typescript": "catalog:"
}
}
5 changes: 0 additions & 5 deletions packages/eslint-config/eslint.config.ts

This file was deleted.

8 changes: 8 additions & 0 deletions packages/eslint-config/oxlint.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { defineConfig } from 'oxlint'
import { tanstackConfig } from '@tanstack/oxlint-config'

export default defineConfig({
extends: [tanstackConfig],
env: tanstackConfig.env,
ignorePatterns: tanstackConfig.ignorePatterns,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
})
7 changes: 5 additions & 2 deletions packages/eslint-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
},
"scripts": {
"test:types": "tsc",
"test:eslint": "eslint --concurrency=auto ./src",
"test:oxlint": "oxlint ./src",
"build": "tsdown"
},
"type": "module",
Expand Down Expand Up @@ -50,8 +50,11 @@
"vue-eslint-parser": "catalog:"
},
"devDependencies": {
"@tanstack/oxlint-config": "workspace:*",
"@types/eslint": "catalog:",
"eslint": "catalog:"
"eslint": "catalog:",
"oxlint": "catalog:",
"oxlint-tsgolint": "catalog:"
},
"peerDependencies": {
"eslint": "^9.0.0 || ^10.0.0"
Expand Down
8 changes: 8 additions & 0 deletions packages/oxlint-config/oxlint.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { defineConfig } from 'oxlint'
import { tanstackConfig } from '@tanstack/oxlint-config'

export default defineConfig({
extends: [tanstackConfig],
env: tanstackConfig.env,
ignorePatterns: tanstackConfig.ignorePatterns,
})
56 changes: 56 additions & 0 deletions packages/oxlint-config/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"name": "@tanstack/oxlint-config",
"version": "0.0.0",
"description": "Shared Oxlint config used by TanStack projects.",
"author": "tannerlinsley",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/TanStack/config.git",
"directory": "packages/oxlint-config"
},
"homepage": "https://tanstack.com/config",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
},
"scripts": {
"test:types": "tsc",
"test:oxlint": "oxlint ./src",
"build": "tsdown"
},
"type": "module",
"types": "./dist/index.d.ts",
"exports": {
".": "./src/index.ts",
"./plugin": "./src/plugin.ts",
"./package.json": "./package.json"
},
"publishConfig": {
"exports": {
".": "./dist/index.js",
"./plugin": "./dist/plugin.js",
"./package.json": "./package.json"
}
},
"preferGlobal": false,
"sideEffects": false,
"files": [
"dist",
"src"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"dependencies": {
"@oxlint/plugins": "catalog:"
},
"devDependencies": {
"oxlint": "catalog:",
"oxlint-tsgolint": "catalog:"
},
"peerDependencies": {
"oxlint": "^1.87.0",
"oxlint-tsgolint": ">=7.0.2003"
}
}
22 changes: 22 additions & 0 deletions packages/oxlint-config/src/import.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { DummyRuleMap } from 'oxlint'

/**
* Port of `importRules` from `@tanstack/eslint-config`.
*
* Not ported: `import/order`, which Oxlint does not implement. Import order
* is left to the formatter, such as Oxfmt's `sortImports`.
*
* @see https://oxc.rs/docs/guide/usage/linter/rules.html
*/
export const importRules: DummyRuleMap = {
/** Bans the use of inline type-only markers for named imports */
'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
/** Reports any imports that come after non-import statements */
'import/first': 'error',
/** Stylistic preference */
'import/newline-after-import': 'error',
/** No require() or module.exports */
'import/no-commonjs': 'error',
/** Reports if a resolved path is imported more than once */
'import/no-duplicates': 'error',
}
56 changes: 56 additions & 0 deletions packages/oxlint-config/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { fileURLToPath } from 'node:url'
import { defineConfig } from 'oxlint'
import { javascriptRules } from './javascript.ts'
import { importRules } from './import.ts'
import { typescriptRules } from './typescript.ts'
import { nodeRules } from './node.ts'
import type { OxlintConfig } from 'oxlint'

/**
* JS plugins are resolved from this package, so consumers don't need to
* install them.
*/
const resolve = (specifier: string): string =>
fileURLToPath(import.meta.resolve(specifier))

/**
* Oxlint does not inherit `env`, `ignorePatterns` or `settings` through
* `extends`, so `env` and `ignorePatterns` are always set, for consumers to
* pass through.
*/
type TanstackConfig = OxlintConfig &
Required<Pick<OxlintConfig, 'env' | 'ignorePatterns'>>

export const tanstackConfig: TanstackConfig = defineConfig({
plugins: ['import', 'typescript', 'unicorn'],
jsPlugins: [
{ name: 'tanstack', specifier: resolve('@tanstack/oxlint-config/plugin') },
],
categories: {
correctness: 'off',
},
options: {
typeAware: true,
},
env: {
browser: true,
es2020: true,
},
ignorePatterns: [
'**/.nx/**',
'**/.svelte-kit/**',
'**/build/**',
'**/coverage/**',
'**/dist/**',
'**/snap/**',
'**/vite.config.*.timestamp-*.*',
],
rules: {
...javascriptRules,
...typescriptRules,
...importRules,
...nodeRules,
},
})

export { javascriptRules, importRules, typescriptRules, nodeRules }
61 changes: 61 additions & 0 deletions packages/oxlint-config/src/javascript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import type { DummyRuleMap } from 'oxlint'

/**
* Port of `javascriptRules` from `@tanstack/eslint-config`.
*
* Not ported: `no-octal`, which Oxlint does not implement. Legacy octal
* literals are already a syntax error in ES modules.
*
* @see https://oxc.rs/docs/guide/usage/linter/rules.html
*/
export const javascriptRules: DummyRuleMap = {
'for-direction': 'error',
'no-async-promise-executor': 'error',
'no-case-declarations': 'error',
'no-class-assign': 'error',
'no-compare-neg-zero': 'error',
'no-cond-assign': 'error',
'no-constant-binary-expression': 'error',
'no-constant-condition': 'error',
'no-control-regex': 'error',
'no-debugger': 'error',
'no-delete-var': 'error',
'no-dupe-else-if': 'error',
'no-duplicate-case': 'error',
'no-empty-character-class': 'error',
'no-empty-pattern': 'error',
'no-empty-static-block': 'error',
'no-ex-assign': 'error',
'no-extra-boolean-cast': 'error',
'no-fallthrough': 'error',
'no-global-assign': 'error',
'no-invalid-regexp': 'error',
'no-irregular-whitespace': 'error',
'no-loss-of-precision': 'error',
'no-misleading-character-class': 'error',
'no-nonoctal-decimal-escape': 'error',
'no-regex-spaces': 'error',
'no-self-assign': 'error',
/** Warn about variable with identical names in the outer scope */
'no-shadow': 'warn',
'no-shadow-restricted-names': 'error',
'no-sparse-arrays': 'error',
'no-unsafe-finally': 'error',
'no-unsafe-optional-chaining': 'error',
'no-unused-labels': 'error',
'no-unused-private-class-members': 'error',
'no-useless-backreference': 'error',
'no-useless-catch': 'error',
'no-useless-escape': 'error',
/** Prefer let and const */
'no-var': 'error',
'no-with': 'error',
/** Prefer const if never re-assigned */
'prefer-const': 'error',
'require-yield': 'error',
/** Stylistic consistency */
'sort-imports': ['error', { ignoreDeclarationSort: true }],
'use-isnan': 'error',
/** Enforce comparing typeof against valid strings */
'valid-typeof': 'error',
}
12 changes: 12 additions & 0 deletions packages/oxlint-config/src/node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { DummyRuleMap } from 'oxlint'

/**
* Port of `nodeRules` from `@tanstack/eslint-config`, using the native
* `unicorn` equivalent of `n/prefer-node-protocol`.
*
* @see https://oxc.rs/docs/guide/usage/linter/rules/unicorn/prefer-node-protocol.html
*/
export const nodeRules: DummyRuleMap = {
/** Enforce usage of the `node:` prefix for builtin imports */
'unicorn/prefer-node-protocol': 'error',
}
Loading
Loading