{ "$schema": "./node_modules/oxlint/configuration_schema.json", "plugins": [ "typescript", "unicorn", "oxc", "react", "react-perf", "jsx-a11y", "import", "promise", "vitest" ], "jsPlugins": [ "eslint-plugin-functional", "eslint-plugin-react-you-might-not-need-an-effect", "oxlint-tailwindcss", "@mandujs/oxlint-plugin-fsd" ], "settings": { "tailwindcss": { "entryPoint": "./src/app/index.css" } }, "options": { "typeAware": true }, "categories": { "correctness": "error", "perf": "error" }, "env": { "browser": true, "es2024": true, "builtin": true, "commonjs": true, "node": true, "shared-node-browser": true }, "rules": { "react/react-in-jsx-scope": "off", "no-nested-ternary": "error", "typescript/array-type": "error", "typescript/no-explicit-any": "error", "typescript/no-non-null-assertion": "error", "typescript/no-deprecated": "error", "no-void": "error", "import/no-default-export": "error", "typescript/no-floating-promises": "off", "typescript/no-misused-promises": [ "error", { "checksVoidReturn": { "attributes": false } } ], "typescript/await-thenable": "error", "typescript/return-await": "error", "typescript/switch-exhaustiveness-check": "error", "typescript/only-throw-error": "error", "typescript/use-unknown-in-catch-callback-variable": "error", "typescript/no-base-to-string": "error", "typescript/restrict-template-expressions": "error", "typescript/restrict-plus-operands": "error", "typescript/no-unsafe-assignment": "error", "typescript/no-unsafe-argument": "error", "typescript/no-unsafe-call": "error", "typescript/no-unsafe-member-access": "error", "typescript/no-unsafe-return": "error", "typescript/prefer-nullish-coalescing": "error", "typescript/prefer-optional-chain": "error", "typescript/no-unnecessary-condition": "error", "typescript/no-unnecessary-boolean-literal-compare": "error", "typescript/no-unnecessary-type-assertion": "error", "typescript/no-unnecessary-type-conversion": "error", "typescript/no-unnecessary-template-expression": "error", "typescript/no-unnecessary-qualifier": "error", "typescript/no-useless-default-assignment": "error", "typescript/no-meaningless-void-operator": "error", "typescript/no-duplicate-type-constituents": "error", "typescript/no-redundant-type-constituents": "error", "typescript/no-unsafe-type-assertion": "error", "typescript/no-unsafe-unary-minus": "error", "typescript/promise-function-async": "error", "typescript/prefer-find": "error", "typescript/prefer-includes": "error", "typescript/prefer-string-starts-ends-with": "error", "typescript/prefer-regexp-exec": "error", "typescript/prefer-reduce-type-parameter": "error", "typescript/no-array-delete": "error", "typescript/no-for-in-array": "error", "typescript/no-implied-eval": "error", "typescript/no-mixed-enums": "error", "typescript/no-misused-spread": "error", "typescript/require-array-sort-compare": "error", "typescript/unbound-method": "error", "typescript/strict-boolean-expressions": "error", "typescript/strict-void-return": "error", "typescript/no-confusing-void-expression": [ "error", { "ignoreArrowShorthand": true } ], "typescript/dot-notation": "error", "typescript/consistent-type-imports": "error", "import/no-cycle": "error", "eqeqeq": ["error", "always"], "func-style": ["error", "expression"], "no-console": [ "warn", { "allow": ["warn", "error"] } ], "no-var": "error", "prefer-const": "error", "unicorn/no-null": "error", "unicorn/no-useless-undefined": [ "error", { "checkArguments": false } ], "no-restricted-globals": [ "error", { "name": "fetch", "message": "fetch는 src/shared/api 의 ky 클라이언트로 감싼다 — .ok 체크·zod 파싱·에러 변환을 그 경계에서 하고, 호출부엔 실패 처리를 둔다." } ], "react-perf/jsx-no-new-function-as-prop": "off", "react-perf/jsx-no-new-object-as-prop": "off", "react-perf/jsx-no-new-array-as-prop": "off", "react-perf/jsx-no-jsx-as-prop": "off", "no-restricted-imports": [ "error", { "paths": [ { "name": "react", "importNames": ["useCallback", "useMemo", "memo"], "message": "React Compiler가 자동 메모이즈한다 — 수동 memoization 금지 (P-013.1)." } ] } ], "functional/no-let": "error", "functional/no-classes": "error", "react-you-might-not-need-an-effect/no-derived-state": "error", "react-you-might-not-need-an-effect/no-chain-state-updates": "error", "react-you-might-not-need-an-effect/no-adjust-state-on-prop-change": "error", "react-you-might-not-need-an-effect/no-reset-all-state-on-prop-change": "error", "react-you-might-not-need-an-effect/no-initialize-state": "error", "react-you-might-not-need-an-effect/no-event-handler": "error", "react-you-might-not-need-an-effect/no-pass-live-state-to-parent": "error", "react-you-might-not-need-an-effect/no-pass-data-to-parent": "error", "react-you-might-not-need-an-effect/no-external-store-subscription": "error", "tailwindcss/no-unknown-classes": "error", "tailwindcss/no-duplicate-classes": "error", "tailwindcss/no-conflicting-classes": "error", "tailwindcss/no-deprecated-classes": "error", "tailwindcss/no-unnecessary-whitespace": "error", "tailwindcss/no-contradicting-variants": "error", "tailwindcss/enforce-canonical": "error", "tailwindcss/no-hardcoded-colors": "error", "tailwindcss/no-unnecessary-arbitrary-value": "error", "tailwindcss/prefer-theme-tokens": "error", "tailwindcss/no-dark-without-light": "error", "tailwindcss/enforce-shorthand": "error", "tailwindcss/enforce-negative-arbitrary-values": "error", "tailwindcss/enforce-consistent-variable-syntax": "error", "tailwindcss/enforce-consistent-important-position": "error", "tailwindcss/enforce-physical": "error", "tailwindcss/no-restricted-classes": [ "error", { "patterns": [ { "pattern": "^text-xs$|^text-\\[1?[0-9]px\\]", "message": "타이포 최소 스케일은 text-sm(14px) — 접근성/가독성. 더 작게가 꼭 필요하면 사유와 함께 disable." } ] } ], "fsd/forbidden-imports": "error", "fsd/no-cross-slice-dependency": "error", "max-depth": ["error", 3], "max-lines-per-function": [ "error", { "max": 120, "skipBlankLines": true, "skipComments": true } ], "max-lines": [ "error", { "max": 400, "skipBlankLines": true, "skipComments": true } ], "complexity": ["error", 12], "jsx-a11y/label-has-associated-control": [ "error", { "depth": 4 } ], "for-direction": "warn", "no-async-promise-executor": "warn", "no-caller": "warn", "no-class-assign": "warn", "no-compare-neg-zero": "warn", "no-cond-assign": "warn", "no-const-assign": "warn", "no-constant-binary-expression": "warn", "no-constant-condition": "warn", "no-control-regex": "warn", "no-debugger": "warn", "no-delete-var": "warn", "no-dupe-class-members": "warn", "no-dupe-else-if": "warn", "no-dupe-keys": "warn", "no-duplicate-case": "warn", "no-empty-character-class": "warn", "no-empty-pattern": "warn", "no-empty-static-block": "warn", "no-eval": "warn", "no-ex-assign": "warn", "no-extra-boolean-cast": "warn", "no-func-assign": "warn", "no-global-assign": "warn", "no-import-assign": "warn", "no-invalid-regexp": "warn", "no-irregular-whitespace": "warn", "no-loss-of-precision": "warn", "no-new-native-nonconstructor": "warn", "no-nonoctal-decimal-escape": "warn", "no-obj-calls": "warn", "no-self-assign": "warn", "no-setter-return": "warn", "no-shadow-restricted-names": "warn", "no-sparse-arrays": "warn", "no-this-before-super": "warn", "no-unsafe-finally": "warn", "no-unsafe-negation": "warn", "no-unsafe-optional-chaining": "warn", "no-unused-labels": "warn", "no-unused-private-class-members": "warn", "no-unused-vars": [ "warn", { "ignoreRestSiblings": true, "argsIgnorePattern": "^_" } ], "no-useless-backreference": "warn", "no-useless-catch": "warn", "no-useless-escape": "warn", "no-useless-rename": "warn", "no-with": "warn", "require-yield": "warn", "use-isnan": "warn", "valid-typeof": "warn", "no-use-before-define": [ "error", { "functions": false, "classes": true, "variables": false } ], "curly": ["error", "all"], "no-shadow": "error", "jsx_a11y/alt-text": "warn", "jsx_a11y/anchor-has-content": "warn", "jsx_a11y/anchor-is-valid": "warn", "jsx_a11y/aria-activedescendant-has-tabindex": "warn", "jsx_a11y/aria-props": "warn", "jsx_a11y/aria-role": "warn", "jsx_a11y/aria-unsupported-elements": "warn", "jsx_a11y/autocomplete-valid": "warn", "jsx_a11y/click-events-have-key-events": "off", "jsx_a11y/heading-has-content": "warn", "jsx_a11y/html-has-lang": "warn", "jsx_a11y/iframe-has-title": "off", "jsx_a11y/img-redundant-alt": "warn", "jsx_a11y/label-has-associated-control": "error", "jsx_a11y/lang": "warn", "jsx_a11y/media-has-caption": "warn", "jsx_a11y/mouse-events-have-key-events": "warn", "jsx_a11y/no-access-key": "warn", "jsx_a11y/no-aria-hidden-on-focusable": "warn", "jsx_a11y/no-autofocus": "off", "jsx_a11y/no-distracting-elements": "warn", "jsx_a11y/no-noninteractive-tabindex": "warn", "jsx_a11y/no-redundant-roles": "warn", "jsx_a11y/prefer-tag-over-role": "warn", "jsx_a11y/role-has-required-aria-props": "warn", "jsx_a11y/role-supports-aria-props": "warn", "jsx_a11y/scope": "warn", "jsx_a11y/tabindex-no-positive": "warn", "jsx_a11y/no-static-element-interactions": "off", "oxc/bad-array-method-on-arguments": "warn", "oxc/bad-char-at-comparison": "warn", "oxc/bad-comparison-sequence": "warn", "oxc/bad-min-max-func": "warn", "oxc/bad-object-literal-comparison": "warn", "oxc/bad-replace-all-arg": "warn", "oxc/const-comparisons": "warn", "oxc/double-comparisons": "warn", "oxc/erasing-op": "warn", "oxc/missing-throw": "warn", "oxc/number-arg-out-of-range": "warn", "oxc/only-used-in-recursion": "warn", "oxc/uninvoked-array-callback": "warn", "react/jsx-key": "warn", "react/jsx-no-duplicate-props": "warn", "react/jsx-no-target-blank": "warn", "react/jsx-no-undef": "warn", "react/no-children-prop": "warn", "react/no-danger-with-children": "warn", "react/no-direct-mutation-state": "warn", "react/no-find-dom-node": "warn", "react/no-is-mounted": "warn", "react/no-render-return-value": "warn", "react/no-string-refs": "warn", "react/void-dom-elements-no-children": "warn", "react/jsx-filename-extension": [ "warn", { "extensions": [".js", ".jsx", ".ts", ".tsx"] } ], "react/display-name": "off", "react/rules-of-hooks": "error", "react/exhaustive-deps": "warn", "react/jsx-curly-brace-presence": "warn", "typescript/no-duplicate-enum-values": "warn", "typescript/no-extra-non-null-assertion": "warn", "typescript/no-misused-new": "warn", "typescript/no-this-alias": "warn", "typescript/no-unsafe-declaration-merging": "warn", "typescript/no-useless-empty-export": "warn", "typescript/prefer-as-const": "warn", "typescript/triple-slash-reference": "warn", "typescript/no-shadow": "warn", "typescript/no-unused-expressions": [ "error", { "allowShortCircuit": true, "allowTernary": true, "allowTaggedTemplates": true } ], "typescript/no-namespace": "off", "typescript/no-empty-function": "off", "typescript/no-require-imports": "off", "typescript/ban-ts-comment": "off", "typescript/consistent-type-definitions": "off", "unicorn/no-await-in-promise-methods": "warn", "unicorn/no-empty-file": "warn", "unicorn/no-invalid-fetch-options": "warn", "unicorn/no-invalid-remove-event-listener": "warn", "unicorn/no-new-array": "warn", "unicorn/no-single-promise-in-promise-methods": "warn", "unicorn/no-thenable": "warn", "unicorn/no-unnecessary-await": "warn", "unicorn/no-useless-fallback-in-spread": "warn", "unicorn/no-useless-length-check": "warn", "unicorn/no-useless-spread": "warn", "unicorn/prefer-set-size": "warn", "unicorn/prefer-string-starts-ends-with": "warn", "unicorn/catch-error-name": "warn", "unicorn/consistent-date-clone": "warn", "unicorn/consistent-existence-index-check": "warn", "unicorn/empty-brace-spaces": "warn", "unicorn/error-message": "warn", "unicorn/no-array-method-this-argument": "warn", "unicorn/no-console-spaces": "warn", "unicorn/no-unreadable-array-destructuring": "warn", "unicorn/no-zero-fractions": "warn", "unicorn/number-literal-case": "warn", "unicorn/numeric-separators-style": "warn", "unicorn/prefer-array-index-of": "warn", "unicorn/prefer-dom-node-text-content": "warn", "unicorn/prefer-includes": "warn", "unicorn/prefer-logical-operator-over-ternary": "warn", "unicorn/prefer-modern-dom-apis": "warn", "unicorn/prefer-negative-index": "warn", "unicorn/prefer-object-from-entries": "warn", "unicorn/prefer-optional-catch-binding": "warn", "unicorn/prefer-reflect-apply": "warn", "unicorn/prefer-string-trim-start-end": "warn", "unicorn/prefer-structured-clone": "warn", "unicorn/require-array-join-separator": "warn", "unicorn/text-encoding-identifier-case": "warn", "unicorn/throw-new-error": "warn", "vitest/require-mock-type-parameters": "off" }, "ignorePatterns": [ "dist", "coverage", "node_modules", "scripts", "src/shared/ui", "src/shared/api/_generated", "**/use-mobile.ts", "**/convertToCamelCase.mjs", "**/*.mjs", "steiger.config.ts", ".dependency-cruiser.cjs" ], "overrides": [ { "files": ["*.config.ts", "*.config.mjs", "*.config.js"], "rules": { "import/no-default-export": "off" } }, { "files": [ "**/*.test.ts", "**/*.test.tsx", "**/*.spec.ts", "**/*.spec.tsx" ], "rules": { "no-console": "off", "typescript/no-non-null-assertion": "off", "typescript/no-unsafe-assignment": "off", "typescript/no-unsafe-call": "off", "typescript/no-unsafe-member-access": "off", "typescript/no-unsafe-return": "off", "typescript/no-unsafe-argument": "off", "max-lines-per-function": "off", "max-lines": "off" } }, { "files": ["src/shared/api/**", "src/shared/config/env.ts"], "rules": { "no-restricted-globals": "off", "functional/no-classes": "off", "typescript/no-unsafe-type-assertion": "off", "typescript/no-unsafe-assignment": "off", "typescript/no-unsafe-return": "off", "typescript/no-unsafe-member-access": "off", "typescript/no-unsafe-call": "off", "typescript/no-unsafe-argument": "off" } }, { "files": ["src/app/**"], "rules": { "fsd/no-cross-slice-dependency": "off" } } ] }