Skip to content
Merged
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
1 change: 1 addition & 0 deletions .changepacks/changepack_log_7KL_chs3ABYp_X9bFx5MV.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"changes":{"package.json":"Patch"},"note":"Improve performance","date":"2026-04-04T07:42:05.622193100Z"}
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,5 @@ stats.html
.nx/workspace-data
**/vite.config.{js,ts,mjs,mts,cjs,cts}.timestamp*

.claude
.claude
.omc
634 changes: 237 additions & 397 deletions bun.lock

Large diffs are not rendered by default.

15 changes: 10 additions & 5 deletions oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,19 @@
"devup/unused-imports/no-unused-vars": "off",

"devup/query/exhaustive-deps": "error",
"devup/query/stable-query-client": "error",
"devup/query/no-rest-destructuring": "error",
"devup/query/no-unstable-deps": "error",
"devup/query/infinite-query-property-order": "error",
"devup/query/no-void-query-fn": "error",
"devup/query/mutation-property-order": "error",
"devup/query/prefer-query-options": "error",

"no-console": ["error", { "allow": ["info", "debug", "warn", "error"] }],
"no-constant-condition": ["error", { "checkLoops": false }],
"no-debugger": "error",
"no-empty": "error",
"no-empty-pattern": "error",
"no-trailing-spaces": "error",
"no-unused-vars": [
"error",
{
Expand All @@ -94,17 +100,16 @@
"allowTernary": true
}
],
"spaced-comment": ["error", "always", { "markers": ["/"] }],

"react/react-in-jsx-scope": "off",
"react/prop-types": "off",
"devup/react/prop-types": "off",
"react/jsx-key": "error",
"react/jsx-no-comment-textnodes": "error",
"react/jsx-no-duplicate-props": "error",
"react/jsx-no-target-blank": "error",
"react/jsx-no-undef": "error",
"react/jsx-curly-brace-presence": "error",
"react/jsx-sort-props": [
"devup/react/jsx-sort-props": [
"error",
{
"callbacksLast": false,
Expand All @@ -124,7 +129,7 @@
"react/no-string-refs": "error",
"react/no-unescaped-entities": "error",
"react/no-unknown-property": "error",
"react/sort-default-props": "error",
"devup/react/sort-default-props": "error",
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": [
"warn",
Expand Down
18 changes: 9 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@
"license": "ISC",
"dependencies": {
"@devup-ui/eslint-plugin": ">=1.0",
"@eslint/js": ">=9.39",
"@eslint/js": ">=10.0",
"@tanstack/eslint-plugin-query": ">=5",
"eslint": ">=9.39",
"eslint": ">=10.2",
"eslint-config-prettier": ">=10",
"eslint-plugin-mdx": ">=3",
"eslint-plugin-prettier": ">=5",
Expand All @@ -51,22 +51,22 @@
"eslint-plugin-simple-import-sort": ">=12",
"eslint-plugin-unused-imports": ">=4",
"prettier": ">=3",
"typescript-eslint": ">=8.53"
"typescript-eslint": ">=8.58"
},
"peerDependencies": {
"eslint": "*"
},
"devDependencies": {
"@changesets/cli": "^2.29",
"@changesets/cli": "^2.30",
"@types/bun": "^1.3",
"@types/eslint": "^9.6",
"@typescript-eslint/rule-tester": "^8.53",
"@typescript-eslint/utils": "^8.53",
"@typescript-eslint/rule-tester": "^8.58",
"@typescript-eslint/utils": "^8.58",
"eslint-plugin-eslint-plugin": "^7.3",
"husky": "^9.1",
"oxlint": "^1.41.0",
"typescript": "^5.9",
"vite": "^7.3",
"oxlint": "^1.58.0",
"typescript": "^6.0",
"vite": "^8.0",
"vite-plugin-dts": "^4.5"
}
}
27 changes: 15 additions & 12 deletions src/oxlint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import type { Rule } from 'eslint'
import { rules as mdxRules } from 'eslint-plugin-mdx'
import eslintPluginPrettier from 'eslint-plugin-prettier'
// @ts-ignore
import reactPlugin from 'eslint-plugin-react'
// @ts-ignore
import simpleImportSortPlugin from 'eslint-plugin-simple-import-sort'
// @ts-ignore
import unusedImportsPlugin from 'eslint-plugin-unused-imports'
Expand All @@ -25,23 +27,15 @@ import { appPage, component, componentInterface } from './rules'
/**
* Wrap a rule to handle unsupported context properties in oxlint
* (e.g., context.parserPath throws in oxlint)
* Uses Object.create for O(1) property lookup instead of Proxy trap on every access.
*/
function wrapRuleForOxlint(rule: Rule.RuleModule): Rule.RuleModule {
return {
...rule,
create(context) {
const proxiedContext = new Proxy(context, {
get(target, prop) {
// Return undefined for unsupported properties instead of throwing
if (prop === 'parserPath') {
return undefined
}
// Pass through all other properties directly to preserve Proxy invariants
// (non-configurable properties must return their actual value)
return target[prop as keyof typeof target]
},
})
return rule.create(proxiedContext)
const wrapped = Object.create(context) as typeof context
Object.defineProperty(wrapped, 'parserPath', { value: undefined })
return rule.create(wrapped)
},
}
}
Expand Down Expand Up @@ -102,6 +96,15 @@ const plugin = {

// @tanstack/eslint-plugin-query rules (auto-wrapped for oxlint compatibility)
...buildWrappedRules(tanstackQueryPlugin.rules!, 'query'),

// eslint-plugin-react rules not natively supported by oxlint
'react/prop-types': wrapRuleForOxlint(reactPlugin.rules!['prop-types']),
'react/jsx-sort-props': wrapRuleForOxlint(
reactPlugin.rules!['jsx-sort-props'],
),
'react/sort-default-props': wrapRuleForOxlint(
reactPlugin.rules!['sort-default-props'],
),
},
}

Expand Down
7 changes: 5 additions & 2 deletions src/rules/component/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { ESLintUtils, TSESTree } from '@typescript-eslint/utils'
import { relative } from 'path'

const KEBAB_SNAKE_PATTERN = /[-_](.)/g
const FIRST_LOWER_PATTERN = /^[a-z]/

function toPascal(str: string) {
return str
.replace(/[-_](.)/g, (_, c: string) => c.toUpperCase())
.replace(/^[a-z]/, (c) => c.toUpperCase())
.replace(KEBAB_SNAKE_PATTERN, (_, c: string) => c.toUpperCase())
.replace(FIRST_LOWER_PATTERN, (c) => c.toUpperCase())
}

const createRule = ESLintUtils.RuleCreator(
Expand Down
7 changes: 5 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@
"resolveJsonModule": true,
"isolatedModules": true,
"emitDeclarationOnly": true,
"rootDir": "src",
"outDir": "dist",
"baseUrl": ".",
"types": ["bun"],
"jsx": "react-jsx"
}
},
"include": ["src"],
"exclude": ["src/**/__tests__/**"]
}
Loading