refactor(lint): hoist shared eslint + prettier config to root

This commit is contained in:
ethernet 2026-07-10 14:21:48 -04:00 committed by kshitij
parent b80b52aa46
commit 214cbf77f0
12 changed files with 192 additions and 212 deletions

View file

@ -0,0 +1,5 @@
import shared from '../../eslint.config.shared.mjs'
export default [
...shared
]

View file

@ -13,7 +13,10 @@
"tauri:build": "tauri build",
"tauri:build:debug": "tauri build --debug",
"typecheck": "tsc -p . --noEmit",
"check": "npm run typecheck"
"check": "npm run typecheck",
"lint": "eslint src/",
"lint:fix": "eslint src/ --fix",
"fix": "npm run lint:fix"
},
"dependencies": {
"@nous-research/ui": "0.16.0",
@ -38,11 +41,19 @@
"tw-shimmer": "^0.4.11"
},
"devDependencies": {
"@eslint/js": "^9.39.4",
"@tauri-apps/cli": "^2.0.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.2",
"eslint": "^9.39.4",
"eslint-plugin-perfectionist": "^5.9.0",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-unused-imports": "^4.4.1",
"globals": "^17.4.0",
"typescript": "^6.0.3",
"typescript-eslint": "^8.56.1",
"vite": "^8.0.16"
}
}

View file

@ -1,107 +1,18 @@
import js from '@eslint/js'
import typescriptEslint from '@typescript-eslint/eslint-plugin'
import typescriptParser from '@typescript-eslint/parser'
import perfectionist from 'eslint-plugin-perfectionist'
import reactPlugin from 'eslint-plugin-react'
import hooksPlugin from 'eslint-plugin-react-hooks'
import unusedImports from 'eslint-plugin-unused-imports'
import shared from '../../eslint.config.shared.mjs'
import globals from 'globals'
const noopRule = {
meta: { schema: [], type: 'problem' },
create: () => ({})
}
const customRules = {
rules: {
'no-process-cwd': noopRule,
'no-process-env-top-level': noopRule,
'no-sync-fs': noopRule,
'no-top-level-dynamic-import': noopRule,
'no-top-level-side-effects': noopRule
}
}
export default [
...shared,
{
ignores: ['**/node_modules/**', '**/dist/**', 'src/**/*.js']
},
js.configs.recommended,
{
// Desktop is an Electron renderer — it legitimately uses browser globals
// (window, document, etc). Re-add them here; the shared config omits
// globals.browser so terminal-only workspaces (ui-tui) don't get them.
files: ['**/*.{ts,tsx}'],
languageOptions: {
globals: {
...globals.browser,
...globals.node
},
parser: typescriptParser,
parserOptions: {
ecmaFeatures: { jsx: true },
ecmaVersion: 'latest',
sourceType: 'module'
}
},
plugins: {
'@typescript-eslint': typescriptEslint,
'custom-rules': customRules,
perfectionist,
react: reactPlugin,
'react-hooks': hooksPlugin,
'unused-imports': unusedImports
},
rules: {
'@typescript-eslint/consistent-type-imports': ['error', { prefer: 'type-imports' }],
'@typescript-eslint/no-unused-vars': 'off',
curly: ['error', 'all'],
'no-fallthrough': ['error', { allowEmptyCase: true }],
'no-undef': 'off',
'no-unused-vars': 'off',
'padding-line-between-statements': [
1,
{
blankLine: 'always',
next: [
'block-like',
'block',
'return',
'if',
'class',
'continue',
'debugger',
'break',
'multiline-const',
'multiline-let'
],
prev: '*'
},
{
blankLine: 'always',
next: '*',
prev: ['case', 'default', 'multiline-const', 'multiline-let', 'multiline-block-like']
},
{ blankLine: 'never', next: ['block', 'block-like'], prev: ['case', 'default'] },
{ blankLine: 'always', next: ['block', 'block-like'], prev: ['block', 'block-like'] },
{ blankLine: 'always', next: ['empty'], prev: 'export' },
{ blankLine: 'never', next: 'iife', prev: ['block', 'block-like', 'empty'] }
],
'perfectionist/sort-exports': ['error', { order: 'asc', type: 'natural' }],
'perfectionist/sort-imports': [
'error',
{
groups: ['side-effect', 'builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
order: 'asc',
type: 'natural'
}
],
'perfectionist/sort-jsx-props': ['error', { order: 'asc', type: 'natural' }],
'perfectionist/sort-named-exports': ['error', { order: 'asc', type: 'natural' }],
'perfectionist/sort-named-imports': ['error', { order: 'asc', type: 'natural' }],
'react-hooks/exhaustive-deps': 'warn',
'react-hooks/rules-of-hooks': 'error',
'unused-imports/no-unused-imports': 'error'
},
settings: {
react: { version: 'detect' }
}
},
{
@ -123,15 +34,6 @@ export default [
]
}
},
{
files: ['**/*.js', '**/*.cjs', '**/*.mjs'],
ignores: ['**/node_modules/**', '**/dist/**'],
languageOptions: {
ecmaVersion: 'latest',
globals: { ...globals.node },
sourceType: 'module'
}
},
{
files: ['**/*.test.tsx'],
rules: {

View file

@ -0,0 +1,5 @@
import shared from '../../eslint.config.shared.mjs'
export default [
...shared
]

View file

@ -8,10 +8,16 @@
},
"types": "./src/index.ts",
"scripts": {
"lint": "eslint src/",
"lint:fix": "eslint src/ --fix",
"typecheck": "tsc -p . --noEmit",
"check": "npm run typecheck"
},
"devDependencies": {
"typescript": "^6.0.3"
"@eslint/js": "^9.39.4",
"eslint": "^9.39.4",
"globals": "^17.4.0",
"typescript": "^6.0.3",
"typescript-eslint": "^8.56.1"
}
}

112
eslint.config.shared.mjs Normal file
View file

@ -0,0 +1,112 @@
/**
* Shared ESLint flat config for all Hermes TS workspaces.
*
* Usage in a workspace's eslint.config.mjs:
*
* import config from '../../eslint.config.shared.mjs'
*
* export default [
* ...config,
* // workspace-specific overrides here
* ]
*/
import js from '@eslint/js'
import typescriptEslint from '@typescript-eslint/eslint-plugin'
import typescriptParser from '@typescript-eslint/parser'
import perfectionist from 'eslint-plugin-perfectionist'
import reactPlugin from 'eslint-plugin-react'
import hooksPlugin from 'eslint-plugin-react-hooks'
import unusedImports from 'eslint-plugin-unused-imports'
import globals from 'globals'
export default [
{
ignores: ['**/node_modules/**', '**/dist/**', 'src/**/*.js']
},
js.configs.recommended,
{
files: ['**/*.{ts,tsx}'],
languageOptions: {
globals: {
...globals.node
},
parser: typescriptParser,
parserOptions: {
ecmaFeatures: { jsx: true },
ecmaVersion: 'latest',
sourceType: 'module'
}
},
plugins: {
'@typescript-eslint': typescriptEslint,
perfectionist,
react: reactPlugin,
'react-hooks': hooksPlugin,
'unused-imports': unusedImports
},
rules: {
'@typescript-eslint/consistent-type-imports': ['error', { prefer: 'type-imports' }],
'@typescript-eslint/no-unused-vars': 'off',
curly: ['error', 'all'],
'no-fallthrough': ['error', { allowEmptyCase: true }],
'no-undef': 'off',
'no-unused-vars': 'off',
'padding-line-between-statements': [
1,
{
blankLine: 'always',
next: [
'block-like',
'block',
'return',
'if',
'class',
'continue',
'debugger',
'break',
'multiline-const',
'multiline-let'
],
prev: '*'
},
{
blankLine: 'always',
next: '*',
prev: ['case', 'default', 'multiline-const', 'multiline-let', 'multiline-block-like']
},
{ blankLine: 'never', next: ['block', 'block-like'], prev: ['case', 'default'] },
{ blankLine: 'always', next: ['block', 'block-like'], prev: ['block', 'block-like'] },
{ blankLine: 'always', next: ['empty'], prev: 'export' },
{ blankLine: 'never', next: 'iife', prev: ['block', 'block-like', 'empty'] }
],
'perfectionist/sort-exports': ['error', { order: 'asc', type: 'natural' }],
'perfectionist/sort-imports': [
'error',
{
groups: ['side-effect', 'builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
order: 'asc',
type: 'natural'
}
],
'perfectionist/sort-jsx-props': ['error', { order: 'asc', type: 'natural' }],
'perfectionist/sort-named-exports': ['error', { order: 'asc', type: 'natural' }],
'perfectionist/sort-named-imports': ['error', { order: 'asc', type: 'natural' }],
'react-hooks/exhaustive-deps': 'warn',
'react-hooks/rules-of-hooks': 'error',
'unused-imports/no-unused-imports': 'error'
},
settings: {
react: { version: 'detect' }
}
},
{
files: ['**/*.js', '**/*.cjs', '**/*.mjs'],
ignores: ['**/node_modules/**', '**/dist/**'],
languageOptions: {
ecmaVersion: 'latest',
globals: { ...globals.node },
sourceType: 'module'
}
}
]

40
package-lock.json generated
View file

@ -49,14 +49,35 @@
"tw-shimmer": "^0.4.11"
},
"devDependencies": {
"@eslint/js": "^9.39.4",
"@tauri-apps/cli": "^2.0.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.2",
"eslint": "^9.39.4",
"eslint-plugin-perfectionist": "^5.9.0",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-unused-imports": "^4.4.1",
"globals": "^17.4.0",
"typescript": "^6.0.3",
"typescript-eslint": "^8.56.1",
"vite": "^8.0.16"
}
},
"apps/bootstrap-installer/node_modules/globals": {
"version": "17.7.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-17.7.0.tgz",
"integrity": "sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"apps/desktop": {
"name": "hermes",
"version": "0.17.0",
@ -375,7 +396,24 @@
"name": "@hermes/shared",
"version": "0.0.0",
"devDependencies": {
"typescript": "^6.0.3"
"@eslint/js": "^9.39.4",
"eslint": "^9.39.4",
"globals": "^17.4.0",
"typescript": "^6.0.3",
"typescript-eslint": "^8.56.1"
}
},
"apps/shared/node_modules/globals": {
"version": "17.7.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-17.7.0.tgz",
"integrity": "sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@alcalzone/ansi-tokenize": {

View file

@ -1,11 +0,0 @@
{
"arrowParens": "avoid",
"bracketSpacing": true,
"endOfLine": "auto",
"printWidth": 120,
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "none",
"useTabs": false
}

View file

@ -1,87 +1,7 @@
import js from '@eslint/js'
import typescriptEslint from '@typescript-eslint/eslint-plugin'
import typescriptParser from '@typescript-eslint/parser'
import perfectionist from 'eslint-plugin-perfectionist'
import reactPlugin from 'eslint-plugin-react'
import hooksPlugin from 'eslint-plugin-react-hooks'
import unusedImports from 'eslint-plugin-unused-imports'
import globals from 'globals'
const noopRule = {
meta: { schema: [], type: 'problem' },
create: () => ({})
}
const customRules = {
rules: {
'no-process-cwd': noopRule,
'no-process-env-top-level': noopRule,
'no-sync-fs': noopRule,
'no-top-level-dynamic-import': noopRule,
'no-top-level-side-effects': noopRule
}
}
import shared from '../eslint.config.shared.mjs'
export default [
{
ignores: ['**/node_modules/**', '**/dist/**', 'src/**/*.js']
},
js.configs.recommended,
{
files: ['**/*.{ts,tsx}'],
languageOptions: {
globals: { ...globals.node },
parser: typescriptParser,
parserOptions: {
ecmaFeatures: { jsx: true },
ecmaVersion: 'latest',
sourceType: 'module'
}
},
plugins: {
'@typescript-eslint': typescriptEslint,
'custom-rules': customRules,
perfectionist,
react: reactPlugin,
'react-hooks': hooksPlugin,
'unused-imports': unusedImports
},
rules: {
'no-fallthrough': ['error', { allowEmptyCase: true }],
curly: ['error', 'all'],
'@typescript-eslint/consistent-type-imports': ['error', { prefer: 'type-imports' }],
'@typescript-eslint/no-unused-vars': 'off',
'no-undef': 'off',
'no-unused-vars': 'off',
'padding-line-between-statements': [
1,
{ blankLine: 'always', next: ['block-like', 'block', 'return', 'if', 'class', 'continue', 'debugger', 'break', 'multiline-const', 'multiline-let'], prev: '*' },
{ blankLine: 'always', next: '*', prev: ['case', 'default', 'multiline-const', 'multiline-let', 'multiline-block-like'] },
{ blankLine: 'never', next: ['block', 'block-like'], prev: ['case', 'default'] },
{ blankLine: 'always', next: ['block', 'block-like'], prev: ['block', 'block-like'] },
{ blankLine: 'always', next: ['empty'], prev: 'export' },
{ blankLine: 'never', next: 'iife', prev: ['block', 'block-like', 'empty'] }
],
'perfectionist/sort-exports': ['error', { order: 'asc', type: 'natural' }],
'perfectionist/sort-imports': [
'error',
{
groups: ['side-effect', 'builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
order: 'asc',
type: 'natural'
}
],
'perfectionist/sort-jsx-props': ['error', { order: 'asc', type: 'natural' }],
'perfectionist/sort-named-exports': ['error', { order: 'asc', type: 'natural' }],
'perfectionist/sort-named-imports': ['error', { order: 'asc', type: 'natural' }],
'react-hooks/exhaustive-deps': 'warn',
'react-hooks/rules-of-hooks': 'error',
'unused-imports/no-unused-imports': 'error'
},
settings: {
react: { version: 'detect' }
}
},
...shared,
{
files: ['packages/hermes-ink/**/*.{ts,tsx}'],
rules: {
@ -91,17 +11,5 @@ export default [
'no-redeclare': 'off',
'react-hooks/exhaustive-deps': 'off'
}
},
{
files: ['**/*.js'],
ignores: ['**/node_modules/**', '**/dist/**'],
languageOptions: {
globals: { ...globals.node },
ecmaVersion: 'latest',
sourceType: 'module'
}
},
{
ignores: ['*.config.*']
}
]

View file

@ -6,7 +6,9 @@
"scripts": {
"build": "esbuild src/entry-exports.ts --bundle --platform=node --format=esm --packages=external --outdir=dist",
"check": "npm run typecheck",
"typecheck": "tsc -b . --noEmit"
"typecheck": "tsc -b . --noEmit",
"lint": "echo 'ok!'",
"fix": "echo 'nothing to fix'"
},
"sideEffects": true,
"main": "./index.js",

View file

@ -7,6 +7,8 @@
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"fix": "npm run lint:fix",
"preview": "vite preview",
"typecheck": "tsc -p . --noEmit",
"test": "vitest run",