diff --git a/apps/desktop/.prettierrc b/.prettierrc similarity index 100% rename from apps/desktop/.prettierrc rename to .prettierrc diff --git a/apps/bootstrap-installer/eslint.config.mjs b/apps/bootstrap-installer/eslint.config.mjs new file mode 100644 index 000000000000..ea43653a9bf7 --- /dev/null +++ b/apps/bootstrap-installer/eslint.config.mjs @@ -0,0 +1,5 @@ +import shared from '../../eslint.config.shared.mjs' + +export default [ + ...shared +] diff --git a/apps/bootstrap-installer/package.json b/apps/bootstrap-installer/package.json index 7550e3c7c989..ba231432350b 100644 --- a/apps/bootstrap-installer/package.json +++ b/apps/bootstrap-installer/package.json @@ -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" } } diff --git a/apps/desktop/eslint.config.mjs b/apps/desktop/eslint.config.mjs index 6cec34d289a6..61abc0c90a0d 100644 --- a/apps/desktop/eslint.config.mjs +++ b/apps/desktop/eslint.config.mjs @@ -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: { diff --git a/apps/shared/eslint.config.mjs b/apps/shared/eslint.config.mjs new file mode 100644 index 000000000000..ea43653a9bf7 --- /dev/null +++ b/apps/shared/eslint.config.mjs @@ -0,0 +1,5 @@ +import shared from '../../eslint.config.shared.mjs' + +export default [ + ...shared +] diff --git a/apps/shared/package.json b/apps/shared/package.json index 57b7f776bce9..83e2a31e3ffa 100644 --- a/apps/shared/package.json +++ b/apps/shared/package.json @@ -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" } } diff --git a/eslint.config.shared.mjs b/eslint.config.shared.mjs new file mode 100644 index 000000000000..c6723ece8fed --- /dev/null +++ b/eslint.config.shared.mjs @@ -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' + } + } +] diff --git a/package-lock.json b/package-lock.json index 2909052cd360..0102f7674a53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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": { diff --git a/ui-tui/.prettierrc b/ui-tui/.prettierrc deleted file mode 100644 index 12ec3ed7db1b..000000000000 --- a/ui-tui/.prettierrc +++ /dev/null @@ -1,11 +0,0 @@ -{ - "arrowParens": "avoid", - "bracketSpacing": true, - "endOfLine": "auto", - "printWidth": 120, - "semi": false, - "singleQuote": true, - "tabWidth": 2, - "trailingComma": "none", - "useTabs": false -} diff --git a/ui-tui/eslint.config.mjs b/ui-tui/eslint.config.mjs index 1b20c3244f3d..c3fed73f26ce 100644 --- a/ui-tui/eslint.config.mjs +++ b/ui-tui/eslint.config.mjs @@ -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.*'] } ] diff --git a/ui-tui/packages/hermes-ink/package.json b/ui-tui/packages/hermes-ink/package.json index 9f4be632bef1..fdee0856741c 100644 --- a/ui-tui/packages/hermes-ink/package.json +++ b/ui-tui/packages/hermes-ink/package.json @@ -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", diff --git a/web/package.json b/web/package.json index 8dfe84074a09..43ac160fe1d9 100644 --- a/web/package.json +++ b/web/package.json @@ -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",