diff --git a/.eslintignore b/.eslintignore.legacy similarity index 100% rename from .eslintignore rename to .eslintignore.legacy diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..c98c1e3 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,137 @@ +const js = require('@eslint/js'); +const globals = require('globals'); +const importPlugin = require('eslint-plugin-import'); +const jestPlugin = require('eslint-plugin-jest'); +const reactPlugin = require('eslint-plugin-react'); +const reactHooksPlugin = require('eslint-plugin-react-hooks'); +const tsPlugin = require('@typescript-eslint/eslint-plugin'); +const tsParser = require('@typescript-eslint/parser'); +const unusedImportsPlugin = require('eslint-plugin-unused-imports'); + +module.exports = [ + { + ignores: [ + 'node_modules/**', + 'dist/**', + 'build/**', + 'coverage/**', + 'scripts/**', + 'src/renderer/bundle.js', + 'src/renderer/bundle.js.map', + 'src/renderer/bundle.js.LICENSE.txt', + 'src/renderer/index.js', + 'src/renderer/index.js.map', + 'src/renderer/index.js.LICENSE.txt', + 'src/renderer/output.css', + ], + }, + js.configs.recommended, + { + files: ['src/**/*.{js,jsx,ts,tsx}', 'tests/**/*.{js,jsx,ts,tsx}'], + languageOptions: { + ecmaVersion: 2022, + sourceType: 'module', + globals: { + ...globals.browser, + ...globals.node, + }, + }, + plugins: { + 'unused-imports': unusedImportsPlugin, + }, + rules: { + 'no-unused-vars': 'off', + 'unused-imports/no-unused-imports': 'error', + }, + }, + { + files: ['src/**/*.{js,jsx,ts,tsx}'], + plugins: { + import: importPlugin, + }, + rules: { + 'import/order': [ + 'error', + { + groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'], + 'newlines-between': 'always', + alphabetize: { order: 'asc', caseInsensitive: true }, + }, + ], + }, + }, + { + files: ['**/*.{jsx,tsx}'], + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + plugins: { + react: reactPlugin, + 'react-hooks': reactHooksPlugin, + }, + settings: { + react: { + version: 'detect', + }, + }, + rules: { + ...reactPlugin.configs.recommended.rules, + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'error', + 'react/react-in-jsx-scope': 'off', + 'react/prop-types': 'off', + }, + }, + { + files: ['**/*.{ts,tsx}'], + languageOptions: { + parser: tsParser, + parserOptions: { + ecmaVersion: 2022, + sourceType: 'module', + ecmaFeatures: { + jsx: true, + }, + }, + }, + plugins: { + '@typescript-eslint': tsPlugin, + }, + rules: { + ...tsPlugin.configs.recommended.rules, + 'no-undef': 'off', + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], + '@typescript-eslint/no-require-imports': 'off', + '@typescript-eslint/no-explicit-any': 'off', + }, + }, + { + files: [ + 'tests/**/*.{js,jsx,ts,tsx}', + 'src/**/__tests__/**/*.{js,jsx,ts,tsx}', + '**/*.test.{js,jsx,ts,tsx}', + '**/*.spec.{js,jsx,ts,tsx}', + ], + languageOptions: { + globals: { + ...globals.jest, + ...globals.node, + }, + }, + plugins: { + jest: jestPlugin, + }, + rules: { + ...jestPlugin.configs.recommended.rules, + 'no-console': 'off', + 'jest/expect-expect': 'off', + 'jest/no-conditional-expect': 'off', + 'jest/no-standalone-expect': 'off', + }, + }, +]; diff --git a/package-lock.json b/package-lock.json index 40463fb..ef8dd62 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,7 @@ "@babel/preset-react": "^7.26.3", "@babel/preset-typescript": "^7.26.0", "@electron/rebuild": "^4.0.3", + "@eslint/js": "^9.39.2", "@jest/globals": "^30.2.0", "@tailwindcss/cli": "^4.1.18", "@testing-library/jest-dom": "^6.6.3", @@ -53,12 +54,16 @@ "electron-builder": "^26.7.0", "electron-devtools-installer": "^4.0.0", "electron-icon-maker": "0.0.5", - "eslint": "^8.56.0", + "eslint": "^9.39.2", "eslint-config-prettier": "^10.1.8", + "eslint-plugin-import": "^2.32.0", + "eslint-plugin-jest": "^28.14.0", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.37.4", "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-tailwindcss": "^4.0.0-beta.0", + "eslint-plugin-unused-imports": "^4.4.1", + "globals": "^17.3.0", "husky": "^9.1.7", "identity-obj-proxy": "^3.0.0", "jest": "^30.2.0", @@ -2530,25 +2535,90 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", "dev": true, "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", + "js-yaml": "^4.1.1", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -2572,6 +2642,19 @@ "concat-map": "0.0.1" } }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/eslintrc/node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", @@ -2599,13 +2682,40 @@ } }, "node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", "dev": true, "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@floating-ui/core": { @@ -2690,44 +2800,28 @@ "react": ">= 16 || ^19.0.0-rc" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, "license": "Apache-2.0", - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, "engines": { - "node": ">=10.10.0" + "node": ">=18.18.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, - "license": "ISC", + "license": "Apache-2.0", "dependencies": { - "brace-expansion": "^1.1.7" + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" }, "engines": { - "node": "*" + "node": ">=18.18.0" } }, "node_modules/@humanwhocodes/module-importer": { @@ -2744,13 +2838,19 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, - "license": "BSD-3-Clause" + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, "node_modules/@isaacs/balanced-match": { "version": "4.0.1", @@ -4528,6 +4628,13 @@ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, "node_modules/@sinclair/typebox": { "version": "0.34.48", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", @@ -5268,6 +5375,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/katex": { "version": "0.16.8", "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.8.tgz", @@ -6808,6 +6922,28 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array.prototype.flat": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", @@ -8971,19 +9107,6 @@ "node": ">=8" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/dom-accessibility-api": { "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", @@ -9674,60 +9797,63 @@ } }, "node_modules/eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.1", - "@humanwhocodes/config-array": "^0.13.0", + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.2", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-config-prettier": { @@ -9746,6 +9872,163 @@ "eslint": ">=7.0.0" } }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-jest": { + "version": "28.14.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.14.0.tgz", + "integrity": "sha512-P9s/qXSMTpRTerE2FQ0qJet2gKbcGyFTPAJipoKxmWqR6uuFqIqk8FuEfg5yBieOezVrEfAMZrEwJ6yEp+1MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "engines": { + "node": "^16.10.0 || ^18.12.0 || >=20.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^6.0.0 || ^7.0.0 || ^8.0.0", + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0", + "jest": "*" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, "node_modules/eslint-plugin-prettier": { "version": "5.5.5", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz", @@ -9904,10 +10187,26 @@ "tailwindcss": "^3.4.0 || ^4.0.0" } }, + "node_modules/eslint-plugin-unused-imports": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.4.1.tgz", + "integrity": "sha512-oZGYUz1X3sRMGUB+0cZyK2VcvRX5lm/vB56PgNNcU+7ficUCKm66oZWKUubXWnOuPjQ8PvmXtCViXBMONPe7tQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0", + "eslint": "^10.0.0 || ^9.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + } + } + }, "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -9915,7 +10214,7 @@ "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -9934,13 +10233,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -9952,6 +10244,19 @@ "concat-map": "0.0.1" } }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/eslint/node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -9969,19 +10274,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/eslint/node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -10028,18 +10320,31 @@ } }, "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -10376,16 +10681,16 @@ } }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/file-type": { @@ -10469,35 +10774,17 @@ } }, "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "license": "MIT", "dependencies": { "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flat-cache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=16" } }, "node_modules/flatted": { @@ -10963,29 +11250,13 @@ } }, "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "version": "17.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.3.0.tgz", + "integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==", "dev": true, "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globals/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -11080,13 +11351,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, "node_modules/har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -12092,16 +12356,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -16353,6 +16607,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/object.values": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", @@ -19879,13 +20148,6 @@ "b4a": "^1.6.4" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, - "license": "MIT" - }, "node_modules/throttleit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.1.tgz", @@ -20109,6 +20371,42 @@ "typescript": ">=4.8.4" } }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", diff --git a/package.json b/package.json index 25ba3b4..38f38c0 100644 --- a/package.json +++ b/package.json @@ -17,12 +17,12 @@ "predev": "npm run build:ts && node scripts/clean-dev-assets.js", "dev": "node scripts/index.js dev", "clear-assets": "rimraf src/renderer/bundle.js src/renderer/bundle.js.map src/renderer/bundle.js.LICENSE.txt src/renderer/output.css", - "lint": "npm run format:check && cross-env ESLINT_USE_FLAT_CONFIG=false eslint src tests --ext .js,.jsx,.ts,.tsx --cache && npm run lint:md && npm run test:catalog && npm run changelog:validate", + "lint": "npm run format:check && eslint src tests --cache --max-warnings 0 && npm run lint:md && npm run test:catalog && npm run changelog:validate", "lint:md": "npm run lint:md:links && npm run lint:md:style", "lint:md:links": "node scripts/lint-markdown-links.js", "lint:md:style": "markdownlint \"**/*.{md,mdx}\" --config .markdownlint.json --ignore node_modules --ignore dist", "changelog:validate": "node scripts/validate-changelog.js", - "lint:tests": "cross-env ESLINT_USE_FLAT_CONFIG=false eslint tests --ext .js,.jsx,.ts,.tsx --cache", + "lint:tests": "eslint tests --cache --max-warnings 0", "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md,html,css}\"", "format:check": "prettier --check --end-of-line auto \"**/*.{json,md,html,css}\"", "test": "jest --config jest.config.js --passWithNoTests", @@ -73,11 +73,11 @@ "author": "AI Code Fusion ", "license": "GPL-3.0", "lint-staged": { - "{src,tests}/**/*.{js,jsx,ts,tsx}": [ - "eslint --fix" - ], "*.{json,md,html,css}": [ "prettier --write" + ], + "{src,tests}/**/*.{js,jsx,ts,tsx}": [ + "eslint --fix" ] }, "build": { @@ -149,6 +149,7 @@ "@babel/preset-react": "^7.26.3", "@babel/preset-typescript": "^7.26.0", "@electron/rebuild": "^4.0.3", + "@eslint/js": "^9.39.2", "@jest/globals": "^30.2.0", "@tailwindcss/cli": "^4.1.18", "@testing-library/jest-dom": "^6.6.3", @@ -170,12 +171,16 @@ "electron-builder": "^26.7.0", "electron-devtools-installer": "^4.0.0", "electron-icon-maker": "0.0.5", - "eslint": "^8.56.0", + "eslint": "^9.39.2", "eslint-config-prettier": "^10.1.8", + "eslint-plugin-import": "^2.32.0", + "eslint-plugin-jest": "^28.14.0", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.37.4", "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-tailwindcss": "^4.0.0-beta.0", + "eslint-plugin-unused-imports": "^4.4.1", + "globals": "^17.3.0", "husky": "^9.1.7", "identity-obj-proxy": "^3.0.0", "jest": "^30.2.0", diff --git a/prettier.config.js b/prettier.config.js index e99e087..ac793b0 100755 --- a/prettier.config.js +++ b/prettier.config.js @@ -4,7 +4,6 @@ module.exports = { embeddedLanguageFormatting: 'auto', htmlWhitespaceSensitivity: 'css', insertPragma: false, - jsxBracketSameLine: false, jsxSingleQuote: true, printWidth: 100, proseWrap: 'preserve', diff --git a/src/__tests__/ConfigTab.test.tsx b/src/__tests__/ConfigTab.test.tsx index 82d9acf..2aac54a 100755 --- a/src/__tests__/ConfigTab.test.tsx +++ b/src/__tests__/ConfigTab.test.tsx @@ -1,5 +1,6 @@ -import React from 'react'; import { render, screen, fireEvent } from '@testing-library/react'; +import React from 'react'; + import ConfigTab from '../renderer/components/ConfigTab'; describe('ConfigTab', () => { diff --git a/src/main/feature-flags.ts b/src/main/feature-flags.ts index 6ca7790..4979bf1 100644 --- a/src/main/feature-flags.ts +++ b/src/main/feature-flags.ts @@ -1,7 +1,9 @@ import { InMemoryProvider, OpenFeature } from '@openfeature/server-sdk'; -import type { UpdaterFlagOverrides } from '../types/ipc'; + import { getErrorMessage } from './errors'; +import type { UpdaterFlagOverrides } from '../types/ipc'; + const TRUE_VALUES = new Set(['1', 'true', 'yes', 'on']); const DEFAULT_FETCH_TIMEOUT_MS = 3000; diff --git a/src/main/index.ts b/src/main/index.ts index a21d85a..3e9a545 100755 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,25 +1,29 @@ -import { app, BrowserWindow, dialog, ipcMain, net, protocol } from 'electron'; -import { autoUpdater } from 'electron-updater'; import fs from 'fs'; import os from 'node:os'; -import path from 'path'; import { pathToFileURL } from 'node:url'; +import path from 'path'; + +import { app, BrowserWindow, dialog, ipcMain, net, protocol } from 'electron'; +import { autoUpdater } from 'electron-updater'; import yaml from 'yaml'; -import { getErrorMessage } from './errors'; -import { initializeUpdaterFeatureFlags } from './feature-flags'; -import { createUpdaterService, resolveUpdaterRuntimeOptions } from './updater'; + import { loadDefaultConfig } from '../utils/config-manager'; import { ContentProcessor } from '../utils/content-processor'; -import { FileAnalyzer, isBinaryFile } from '../utils/file-analyzer'; -import { getRelativePath, shouldExclude } from '../utils/filter-utils'; -import { GitignoreParser } from '../utils/gitignore-parser'; -import { TokenCounter } from '../utils/token-counter'; import { normalizeExportFormat, normalizeTokenCount, toXmlNumericAttribute, wrapXmlCdata, } from '../utils/export-format'; +import { FileAnalyzer, isBinaryFile } from '../utils/file-analyzer'; +import { getRelativePath, shouldExclude } from '../utils/filter-utils'; +import { GitignoreParser } from '../utils/gitignore-parser'; +import { TokenCounter } from '../utils/token-counter'; + +import { getErrorMessage } from './errors'; +import { initializeUpdaterFeatureFlags } from './feature-flags'; +import { createUpdaterService, resolveUpdaterRuntimeOptions } from './updater'; + import type { AnalyzeRepositoryOptions, AnalyzeRepositoryResult, diff --git a/src/main/preload.ts b/src/main/preload.ts index 21e8c50..5881b7e 100755 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -1,4 +1,5 @@ import { contextBridge, ipcRenderer, shell } from 'electron'; + import type { AnalyzeRepositoryOptions, AnalyzeRepositoryResult, diff --git a/src/main/updater.ts b/src/main/updater.ts index 3f1a6a9..3c4bb2f 100644 --- a/src/main/updater.ts +++ b/src/main/updater.ts @@ -1,11 +1,12 @@ -import type { AppUpdater } from 'electron-updater'; +import { getErrorMessage } from './errors'; + import type { UpdateCheckResult, UpdaterChannel, UpdaterFlagOverrides, UpdaterStatus, } from '../types/ipc'; -import { getErrorMessage } from './errors'; +import type { AppUpdater } from 'electron-updater'; export interface UpdaterRuntimeOptions extends UpdaterStatus { checkOnStart: boolean; diff --git a/src/renderer/components/App.tsx b/src/renderer/components/App.tsx index 7bcdbab..bc7bc79 100755 --- a/src/renderer/components/App.tsx +++ b/src/renderer/components/App.tsx @@ -1,12 +1,15 @@ import React, { useState, useEffect, useRef } from 'react'; -import TabBar from './TabBar'; -import SourceTab from './SourceTab'; -import ConfigTab from './ConfigTab'; -import ProcessedTab from './ProcessedTab'; -import DarkModeToggle from './DarkModeToggle'; -import { DarkModeProvider } from '../context/DarkModeContext'; import yaml from 'yaml'; + import { normalizeExportFormat } from '../../utils/export-format'; +import { DarkModeProvider } from '../context/DarkModeContext'; + +import ConfigTab from './ConfigTab'; +import DarkModeToggle from './DarkModeToggle'; +import ProcessedTab from './ProcessedTab'; +import SourceTab from './SourceTab'; +import TabBar from './TabBar'; + import type { AnalyzeRepositoryResult, ConfigObject, @@ -92,6 +95,7 @@ const App = () => { // Load config from localStorage or via API, no fallbacks const [configContent, setConfigContent] = useState('# Loading configuration...'); const appWindow = globalThis as Window & typeof globalThis; + const electronAPI = appWindow.electronAPI; // Load config from localStorage or default config useEffect(() => { @@ -99,9 +103,9 @@ const App = () => { const savedConfig = localStorage.getItem('configContent'); if (savedConfig) { setConfigContent(savedConfig); - } else if (appWindow.electronAPI?.getDefaultConfig) { + } else if (electronAPI?.getDefaultConfig) { // Otherwise load from the main process - appWindow.electronAPI + electronAPI .getDefaultConfig?.() .then((defaultConfig) => { if (defaultConfig) { @@ -119,8 +123,8 @@ const App = () => { if (savedRootPath) { setRootPath(savedRootPath); // Load directory tree for the saved path - if (appWindow.electronAPI?.getDirectoryTree) { - appWindow.electronAPI + if (electronAPI?.getDirectoryTree) { + electronAPI .getDirectoryTree?.(savedRootPath, localStorage.getItem('configContent')) .then((tree) => { setDirectoryTree(tree ?? []); @@ -130,6 +134,7 @@ const App = () => { }); } } + // eslint-disable-next-line react-hooks/exhaustive-deps -- electronAPI is a stable preload bridge on globalThis }, []); // Setup path change listener to keep all components in sync diff --git a/src/renderer/components/ConfigTab.tsx b/src/renderer/components/ConfigTab.tsx index 3ee27ca..6e7cb51 100755 --- a/src/renderer/components/ConfigTab.tsx +++ b/src/renderer/components/ConfigTab.tsx @@ -1,7 +1,9 @@ import React, { useCallback, useEffect, useState } from 'react'; import yaml from 'yaml'; -import { yamlArrayToPlainText } from '../../utils/formatters/list-formatter'; + import { normalizeExportFormat } from '../../utils/export-format'; +import { yamlArrayToPlainText } from '../../utils/formatters/list-formatter'; + import type { ConfigObject, ExportFormat, diff --git a/src/renderer/components/DarkModeToggle.tsx b/src/renderer/components/DarkModeToggle.tsx index fe91823..e83b8d7 100644 --- a/src/renderer/components/DarkModeToggle.tsx +++ b/src/renderer/components/DarkModeToggle.tsx @@ -1,4 +1,5 @@ import React from 'react'; + import { useDarkMode } from '../context/DarkModeContext'; const DarkModeToggle = () => { diff --git a/src/renderer/components/FileTree.tsx b/src/renderer/components/FileTree.tsx index 8e3307c..6d844ac 100755 --- a/src/renderer/components/FileTree.tsx +++ b/src/renderer/components/FileTree.tsx @@ -1,4 +1,5 @@ import React, { useMemo, useState } from 'react'; + import type { DirectoryTreeItem, SelectionHandler } from '../../types/ipc'; type FileTreeItemProps = { diff --git a/src/renderer/components/ProcessedTab.tsx b/src/renderer/components/ProcessedTab.tsx index fdf9735..b448f26 100755 --- a/src/renderer/components/ProcessedTab.tsx +++ b/src/renderer/components/ProcessedTab.tsx @@ -1,4 +1,5 @@ import React, { useState } from 'react'; + import type { ProcessRepositoryResult } from '../../types/ipc'; type ProcessedTabProps = { diff --git a/src/renderer/components/SourceTab.tsx b/src/renderer/components/SourceTab.tsx index 442dcb3..113865a 100755 --- a/src/renderer/components/SourceTab.tsx +++ b/src/renderer/components/SourceTab.tsx @@ -1,6 +1,8 @@ import React, { useEffect, useRef, useState } from 'react'; import yaml from 'yaml'; + import FileTree from './FileTree'; + import type { CountFilesTokensResult, DirectoryTreeItem, SelectionHandler } from '../../types/ipc'; type SourceTabProps = { diff --git a/src/renderer/components/TabBar.tsx b/src/renderer/components/TabBar.tsx index b99f85d..cd7d085 100755 --- a/src/renderer/components/TabBar.tsx +++ b/src/renderer/components/TabBar.tsx @@ -1,4 +1,5 @@ import React from 'react'; + import type { TabId } from '../../types/ipc'; type TabBarProps = { diff --git a/src/renderer/index.tsx b/src/renderer/index.tsx index e83c415..ca1eb28 100644 --- a/src/renderer/index.tsx +++ b/src/renderer/index.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { createRoot } from 'react-dom/client'; + import App from './components/App'; const container = document.getElementById('app'); diff --git a/src/utils/config-manager.ts b/src/utils/config-manager.ts index 5b8b195..076c687 100644 --- a/src/utils/config-manager.ts +++ b/src/utils/config-manager.ts @@ -1,6 +1,8 @@ import fs from 'fs'; import path from 'path'; + import yaml from 'yaml'; + import type { ConfigObject } from '../types/ipc'; const APP_ROOT = path.resolve(__dirname, '../../..'); diff --git a/src/utils/content-processor.ts b/src/utils/content-processor.ts index 355b602..db3e065 100755 --- a/src/utils/content-processor.ts +++ b/src/utils/content-processor.ts @@ -1,14 +1,16 @@ import fs from 'fs'; import path from 'path'; -import { isBinaryFile } from './file-analyzer'; -import type { TokenCounter } from './token-counter'; -import type { ExportFormat } from '../types/ipc'; + import { escapeXmlAttribute, normalizeExportFormat, normalizeTokenCount, wrapXmlCdata, } from './export-format'; +import { isBinaryFile } from './file-analyzer'; + +import type { TokenCounter } from './token-counter'; +import type { ExportFormat } from '../types/ipc'; interface AnalysisEntry { path: string; diff --git a/src/utils/file-analyzer.ts b/src/utils/file-analyzer.ts index 2cf490f..1d8048a 100755 --- a/src/utils/file-analyzer.ts +++ b/src/utils/file-analyzer.ts @@ -1,10 +1,12 @@ import fs from 'fs'; import path from 'path'; + import { shouldExclude } from './filter-utils'; import { scanContentForSecretsWithPolicy } from './secret-scanner'; -import type { ConfigObject } from '../types/ipc'; -import type { TokenCounter } from './token-counter'; + import type { GitignorePatterns } from './gitignore-parser'; +import type { TokenCounter } from './token-counter'; +import type { ConfigObject } from '../types/ipc'; // Helper function to check if a file is a binary file by examining content export const isBinaryFile = (filePath: string): boolean => { diff --git a/src/utils/filter-utils.ts b/src/utils/filter-utils.ts index 802ba16..d918da1 100644 --- a/src/utils/filter-utils.ts +++ b/src/utils/filter-utils.ts @@ -1,8 +1,10 @@ import path from 'path'; + import fnmatch from './fnmatch'; -import type { ConfigObject } from '../types/ipc'; import { shouldExcludeSensitiveFilePath } from './secret-scanner'; +import type { ConfigObject } from '../types/ipc'; + type ExcludePatterns = string[] & { includePatterns?: string[]; includeExtensions?: string[] }; export const normalizePath = (inputPath: string): string => inputPath.replaceAll('\\', '/'); diff --git a/src/utils/secret-scanner.ts b/src/utils/secret-scanner.ts index 65f9873..4852f4f 100644 --- a/src/utils/secret-scanner.ts +++ b/src/utils/secret-scanner.ts @@ -1,5 +1,6 @@ import fs from 'fs'; import path from 'path'; + import type { ConfigObject } from '../types/ipc'; type SecretRule = {