Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 2 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"svelte.svelte-vscode",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"vitest.explorer"
"vitest.explorer",
"oxc.oxc-vscode"
]
}
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"javascript.preferences.importModuleSpecifier": "non-relative",
"javascript.preferences.importModuleSpecifierEnding": "minimal",
"typescript.preferences.importModuleSpecifierEnding": "minimal",
"oxc.typeAware": true,
"vitest.nodeEnv": {
// Enabling browser test on VSC vitest,
// since it will not run browser tests by default
Expand Down
63 changes: 63 additions & 0 deletions frontend/.oxlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": [
"typescript",
"unicorn",
"import",
"vitest",
"promise"
],
"jsPlugins": [
"eslint-plugin-phosphor-svelte"
],
"env": {
"builtin": true
},
"ignorePatterns": [
"**/node_modules/**",
"**/dist/**",
"**/coverage/**",
"**/.cache/**",
"**/.vscode/**",
"**/.git/**",
"**/conductor/**"
],
"overrides": [
{
"files": [
"**/*.d.ts"
],
"rules": {
"no-unused-vars": "off"
}
},
{
"files": [
"**/*.svelte",
"**/*.svelte.ts"
],
"rules": {
"prefer-const": "off",
"no-unassigned-vars": "off"
}
},
{
"files": [
"**/test/**",
"**/*.test.ts",
"**/*.test.tsx",
"**/*.spec.ts"
],
"rules": {
"jest/require-to-throw-message": "off",
"jest/expect-expect": "off"
}
}
],
"rules": {
"phosphor-svelte/optimize-imports": "warn"
},
"extends": [
"scripts/linter/oxlint-eslint.json"
]
}
3 changes: 2 additions & 1 deletion frontend/common/logger/LogBridge.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// oxlint-disable no-console
// It's logger, so we should use console
/* eslint-disable no-console */

import type { StandardLogEntry, LogLevel, StructuredLogEntry } from "./Logger";
import type { StructuredLogLevel } from "./LogType";

Expand Down
89 changes: 89 additions & 0 deletions frontend/docs/rules/tools.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Tools Configuration

This document provides guidelines for configuring the linting and formatting tools in the ArisuTalk frontend project.

## Linter Configuration

This project uses **oxlint** as the primary linter for fast, type-aware linting, with **ESLint** as a fallback for rules that oxlint doesn't support yet.

### Oxlint

Fast linter for TypeScript and JavaScript, built on the Oxc compiler stack.

**Supports:**

- Most typescript-eslint rules, including type-aware rules
- ESLint's JS plugins via the `jsPlugins` feature
- Plugins: `typescript`, `unicorn`, `import`, `vitest`, `promise`

**Doesn't support:**

- Custom file formats and parsers (Svelte, Vue, Angular templates)
- Some HTML-superset code (only checks `<script>` blocks in `.svelte` files)

**Configuration:** `.oxlintrc.json` and `scripts/linter/`

#### Adding New Plugins

When using new ESLint plugins, try oxlint's [JS Plugins compatibility](https://oxc.rs/docs/guide/usage/linter/js-plugins.html) first:

1. Add the plugin to `jsPlugins` in `.oxlintrc.json`:

```json
{
"jsPlugins": ["eslint-plugin-foo"]
}
```

2. Add rules from the plugin under `rules`:

```json
{
"rules": {
"foo/rule-name": "error"
}
}
```

3. If the plugin requires Svelte/Vue parser support, fall back to ESLint (see below).

### ESLint

ESLint is used **only** for rules that oxlint doesn't support, primarily:

- Svelte template-specific rules (`eslint-plugin-svelte`)
- Any future plugins requiring custom parsers

**Configuration:** `eslint.config.js`

The `eslint-plugin-oxlint` integration automatically disables ESLint rules that oxlint already handles, preventing duplicate warnings.

### Running Linters

```bash
# Run oxlint only (fast, catches most issues)
pnpm run lint:oxlint

# Run ESLint only (Svelte template rules)
pnpm run lint:eslint

# Run both with auto-fix
pnpm run lint

# Run both without auto-fix (for CI)
pnpm run lint:check
```

## Formatter Configuration

This project uses **Prettier** for code formatting. Prettier is configured in `.prettierrc` and runs via:

```bash
# Format all files
pnpm run format

# Check formatting without modifying files
pnpm run format:check
```

**Note:** `eslint-config-prettier` is **not** used, as CI already checks formatting separately and it can slow down TypeScript linting.
13 changes: 8 additions & 5 deletions frontend/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,19 @@ import { defineConfig } from "eslint/config";
import { dirname } from "path";
import { fileURLToPath } from "url";
import phosphorSvelte from "eslint-plugin-phosphor-svelte";
import oxlint from "eslint-plugin-oxlint";

const __dirname = dirname(fileURLToPath(import.meta.url));

const tsConfig = !process.env.SKIP_TYPE_LINT
? ts.configs.recommendedTypeChecked
: ts.configs.recommended;

const isCI = process.env.CI ? true : false;

export default defineConfig([
{
ignores: ["dist/", "node_modules/", "*.config.*"],
},
js.configs.recommended,
...tsConfig,
// No type check for it, oxlint does it.
...ts.configs.recommended,
...svelte.configs["flat/recommended"],
phosphorSvelte.configs.recommended,
{
Expand Down Expand Up @@ -60,4 +59,8 @@ export default defineConfig([
"no-console": "warn",
},
},
// Disable ESLint rules that are already handled by oxlint
...oxlint.buildFromOxlintConfigFile(".oxlintrc.json", {
typeAware: true,
}),
]);
11 changes: 8 additions & 3 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
"test:browser": "vitest run test/browser",
"test:ui": "vitest --ui",
"test:coverage": "vitest run test --coverage",
"check": "pnpm run format && pnpm run lint:fix && concurrently --group \"pnpm run test\" \"pnpm run test:browser\"",
"check": "pnpm run format && pnpm run lint && concurrently --group \"pnpm run test\" \"pnpm run test:browser\"",
"check:types": "concurrently --group \"svelte-check --tsconfig tsconfig.app.json --fail-on-warnings\" \"svelte-check --tsconfig tsconfig.worker.json --fail-on-warnings\" \"svelte-check --tsconfig tsconfig.test.json\"",
"lint": "pnpm run check:types && eslint --cache --cache-location ./node_modules/.cache/eslint/ src/ test/ worker/ common/",
"lint:fix": "pnpm run lint --fix",
"lint:oxlint": "oxlint . --type-aware --type-check --report-unused-disable-directives-severity=off",
"lint:eslint": "eslint --cache --cache-location ./node_modules/.cache/eslint/ src/ test/ worker/ common/",
"lint": "pnpm run lint:oxlint --fix && pnpm run lint:eslint --fix",
"lint:check": "pnpm run check:types && pnpm run lint:oxlint && pnpm run lint:eslint",
"format": "pnpm run format:check --write --list-different",
"format:check": "prettier --cache --cache-location ./node_modules/.cache/prettier/ src/ test/ worker/"
},
Expand Down Expand Up @@ -58,11 +60,14 @@
"concurrently": "^9.2.1",
"daisyui": "^5.5.13",
"eslint": "^9.39.1",
"eslint-plugin-oxlint": "^1.48.0",
"eslint-plugin-phosphor-svelte": "^0.0.2",
"eslint-plugin-svelte": "^3.13.1",
"fake-indexeddb": "^6.2.5",
"globals": "^16.5.0",
"happy-dom": "^20.0.10",
"oxlint": "^1.48.0",
"oxlint-tsgolint": "^0.14.0",
"playwright": "^1.57.0",
"postcss": "^8.5.6",
"prettier": "^3.8.0",
Expand Down
105 changes: 105 additions & 0 deletions frontend/scripts/linter/oxlint-eslint-error.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Base rule, which is similar to ESLint. Machine-generated, unrecommended to edit manually.
// Instead, extend this in .oxlintrc.json and override rules.
{
"$schema": "../../node_modules/oxlint/configuration_schema.json",
"rules": {
"@typescript-eslint/await-thenable": "error",
"@typescript-eslint/ban-ts-comment": "error",
"@typescript-eslint/no-array-delete": "error",
"@typescript-eslint/no-base-to-string": "error",
"@typescript-eslint/no-duplicate-enum-values": "error",
"@typescript-eslint/no-duplicate-type-constituents": "error",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-extra-non-null-assertion": "error",
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-for-in-array": "error",
"@typescript-eslint/no-implied-eval": "error",
"@typescript-eslint/no-misused-new": "error",
"@typescript-eslint/no-misused-promises": "error",
"@typescript-eslint/no-namespace": "error",
"@typescript-eslint/no-non-null-asserted-optional-chain": "error",
"@typescript-eslint/no-redundant-type-constituents": "error",
"@typescript-eslint/no-require-imports": "error",
"@typescript-eslint/no-this-alias": "error",
"@typescript-eslint/no-unnecessary-type-assertion": "error",
"@typescript-eslint/no-unnecessary-type-constraint": "error",
"@typescript-eslint/no-unsafe-argument": "error",
"@typescript-eslint/no-unsafe-assignment": "error",
"@typescript-eslint/no-unsafe-call": "error",
"@typescript-eslint/no-unsafe-declaration-merging": "error",
"@typescript-eslint/no-unsafe-enum-comparison": "error",
"@typescript-eslint/no-unsafe-function-type": "error",
"@typescript-eslint/no-unsafe-member-access": "error",
"@typescript-eslint/no-unsafe-return": "error",
"@typescript-eslint/no-unsafe-unary-minus": "error",
"@typescript-eslint/no-wrapper-object-types": "error",
"@typescript-eslint/only-throw-error": "error",
"@typescript-eslint/prefer-as-const": "error",
"@typescript-eslint/prefer-namespace-keyword": "error",
"@typescript-eslint/prefer-promise-reject-errors": "error",
"@typescript-eslint/restrict-plus-operands": "error",
"@typescript-eslint/restrict-template-expressions": "error",
"@typescript-eslint/triple-slash-reference": "error",
"constructor-super": "off",
"for-direction": "error",
"no-array-constructor": "error",
"no-async-promise-executor": "error",
"no-case-declarations": "error",
"no-class-assign": "off",
"no-compare-neg-zero": "error",
"no-cond-assign": "error",
"no-const-assign": "off",
"no-constant-binary-expression": "error",
"no-constant-condition": "error",
"no-control-regex": "error",
"no-debugger": "error",
"no-dupe-class-members": "off",
"no-dupe-else-if": "error",
"no-dupe-keys": "off",
"no-duplicate-case": "error",
"no-empty": "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-func-assign": "off",
"no-import-assign": "off",
"no-invalid-regexp": "error",
"no-irregular-whitespace": "error",
"no-loss-of-precision": "error",
"no-misleading-character-class": "error",
"no-new-native-nonconstructor": "off",
"no-nonoctal-decimal-escape": "error",
"no-obj-calls": "off",
"no-prototype-builtins": "error",
"no-redeclare": "off",
"no-regex-spaces": "error",
"no-self-assign": "error",
"no-setter-return": "off",
"no-shadow-restricted-names": "error",
"no-sparse-arrays": "error",
"no-this-before-super": "off",
"no-unassigned-vars": "error",
"no-unsafe-finally": "error",
"no-unsafe-negation": "off",
"no-unsafe-optional-chaining": "error",
"no-unused-expressions": "error",
"no-unused-labels": "error",
"no-unused-private-class-members": "error",
"no-unused-vars": "error",
"no-useless-backreference": "error",
"no-useless-catch": "error",
"no-useless-escape": "error",
"no-var": "error",
"prefer-const": "error",
"prefer-rest-params": "error",
"prefer-spread": "error",
"preserve-caught-error": "error",
"require-yield": "error",
"use-isnan": "error",
"valid-typeof": "error"
},
"categories": {}
}
Loading