Files
fusion/eslint.config.mjs
Fusion fd70323a5f feat(FN-1756): add quality-gate inventory and rebaseline ESLint scope
- Add quality-gate-inventory.md documenting baseline verification artifacts
- Add before/after lint logs (pnpm-lint.before.log, pnpm-lint.after.log) as baseline comparison
- Add pnpm-test.before.log as test baseline artifact
- Update eslint.config.mjs with explicit context overrides to clean up linting scope
2026-04-15 01:31:41 -07:00

349 lines
13 KiB
JavaScript

import eslint from "@eslint/js";
import tseslint from "typescript-eslint";
/**
* ESLint Flat Config for Fusion Workspace
*
* Configuration hierarchy (order matters for flat configs):
* 1. Global ignores — files never linted (must come first)
* 2. Base recommendations — eslint/recommended + typescript-eslint/recommended
* 3. Context-specific overrides — production, test-support, node, sw, etc.
*
* Key scoping decisions:
* - Global ignores come first to prevent base configs from processing excluded files
* - Test support files use relaxed rules (no-explicit-any off) without blanket-ignoring them
* - Node scripts get proper Node globals (process, console, require, etc.)
* - Service worker gets browser SW globals (self, caches, fetch, etc.)
* - Production source keeps @typescript-eslint/no-explicit-any as warning
*/
export default tseslint.config(
// ─────────────────────────────────────────────────────────────
// GLOBAL IGNORES FIRST
// (per memory guidance: must come before recommended configs)
// ─────────────────────────────────────────────────────────────
{
ignores: [
// Node modules and build artifacts
"**/node_modules/**",
"**/dist/**",
"**/out/**",
"**/build/**",
"coverage/**",
// Project metadata (fn data, worktrees, etc.)
".fusion/**",
".worktrees/**",
// Lock files
"*.lock",
"pnpm-lock.yaml",
// Git internals
".git/**",
// Logs
"*.log",
// All test files matching standard patterns — never linted
"**/*.test.ts",
"**/*.test.tsx",
"**/*.spec.ts",
"**/*.spec.tsx",
"**/__tests__/**",
// Dashboard test support directory — test helpers, not production code
"packages/dashboard/app/test/**",
],
},
// ─────────────────────────────────────────────────────────────
// BASE RECOMMENDATIONS
// ─────────────────────────────────────────────────────────────
eslint.configs.recommended,
...tseslint.configs.recommended,
// ─────────────────────────────────────────────────────────────
// TEST SUPPORT FILES — relaxed rules for vitest setup/config
// (runs BEFORE production config to disable no-explicit-any for test helpers)
// ─────────────────────────────────────────────────────────────
{
// Dashboard vitest.setup.ts — test infrastructure, not production source
// Includes mock factories, vi.fn() signatures, etc. that legitimately use `any`
files: [
"packages/dashboard/vitest.setup.ts",
],
plugins: {
"@typescript-eslint": tseslint.plugin,
},
languageOptions: {
parser: tseslint.parser,
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
},
rules: {
// Test setup files commonly use `any` for mock types and event handlers
"@typescript-eslint/no-explicit-any": "off",
// Allow unused vars in test setup (globals, config, etc.)
"@typescript-eslint/no-unused-vars": "off",
"no-unused-vars": "off",
// Allow empty blocks in test setup
"no-empty": "off",
},
},
// ─────────────────────────────────────────────────────────────
// PRODUCTION TYPESCRIPT FILES — strict rules with project conventions
// Enforces @typescript-eslint/no-explicit-any for production source
// ─────────────────────────────────────────────────────────────
{
files: [
"packages/*/src/**/*.ts",
"packages/*/src/**/*.tsx",
"packages/dashboard/app/**/*.ts",
"packages/dashboard/app/**/*.tsx",
"packages/dashboard/src/**/*.ts",
"packages/dashboard/src/**/*.tsx",
// NOTE: vitest.setup.ts is excluded here (handled by test-support block above)
],
plugins: {
"@typescript-eslint": tseslint.plugin,
},
languageOptions: {
parser: tseslint.parser,
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
},
rules: {
"@typescript-eslint/no-unused-vars": [
"warn", // Warning for unused vars
{
vars: "all",
args: "after-used",
ignoreRestSiblings: true,
varsIgnorePattern: "^_",
argsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
},
],
// Keep no-explicit-any as WARN for production source
// (use @ts-expect-error or proper types when possible)
"@typescript-eslint/no-explicit-any": ["warn", {
"ignoreRestArgs": true,
}],
// Allow fallthrough with comment
"no-fallthrough": ["warn", { "commentPattern": ".*fallthrough.*" }],
// Allow useless escape
"no-useless-escape": "warn",
// Allow empty blocks
"no-empty": "warn",
// Allow case declarations
"no-case-declarations": "warn",
// Allow unused expressions (for intentional side effects)
"@typescript-eslint/no-unused-expressions": "warn",
// Allow empty object types
"@typescript-eslint/no-empty-object-type": "warn",
// Allow empty interface
"@typescript-eslint/no-empty-interface": "warn",
// Allow @ts-ignore comments
"@typescript-eslint/ban-ts-comment": "warn",
// Allow control regex
"no-control-regex": "warn",
// Allow prefer-const (warn instead of error)
"prefer-const": "warn",
// Allow useless catch
"no-useless-catch": "warn",
},
ignores: ["**/*.gen.ts", "**/*.gen.tsx"],
},
// ─────────────────────────────────────────────────────────────
// NODE SCRIPTS — proper Node.js globals
// (scripts/dev-with-memory.mjs, fix.cjs, etc.)
// ─────────────────────────────────────────────────────────────
{
files: [
"scripts/**/*.js",
"scripts/**/*.mjs",
"*.cjs",
"fix.cjs",
],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: {
// Node.js core globals
process: "readonly",
console: "readonly",
setTimeout: "readonly",
setInterval: "readonly",
clearTimeout: "readonly",
clearInterval: "readonly",
globalThis: "readonly",
require: "readonly",
module: "readonly",
__dirname: "readonly",
__filename: "readonly",
Buffer: "readonly",
},
},
rules: {
// Node scripts commonly use require()
"@typescript-eslint/no-require-imports": "off",
// Allow console in scripts (dev tooling)
"no-console": "off",
// Allow unused vars in scripts (tooling often has them)
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "off",
},
},
// ─────────────────────────────────────────────────────────────
// DEMO FILES — tooling/linting noise, not production code
// ─────────────────────────────────────────────────────────────
{
files: ["demo/**/*.ts"],
plugins: {
"@typescript-eslint": tseslint.plugin,
},
languageOptions: {
parser: tseslint.parser,
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
},
rules: {
// Allow explicit any in demo files
"@typescript-eslint/no-explicit-any": "off",
// Allow unused vars in demo files
"@typescript-eslint/no-unused-vars": "off",
// Allow console in demo files
"no-console": "off",
},
},
// ─────────────────────────────────────────────────────────────
// PLUGIN EXAMPLES — relaxed rules for plugin development
// ─────────────────────────────────────────────────────────────
{
files: ["plugins/**/*.ts", "plugins/**/*.tsx"],
plugins: {
"@typescript-eslint": tseslint.plugin,
},
languageOptions: {
parser: tseslint.parser,
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
},
rules: {
// Allow explicit any for mocks
"@typescript-eslint/no-explicit-any": "off",
// Allow unused vars in tests
"@typescript-eslint/no-unused-vars": "off",
// Allow unsafe function types
"@typescript-eslint/no-unsafe-function-type": "off",
// Allow prefer-const
"prefer-const": "off",
// Allow fallthrough
"no-fallthrough": "off",
// Allow useless escape
"no-useless-escape": "off",
},
},
// ─────────────────────────────────────────────────────────────
// AGENT SKILL TEMPLATES — template code with underscore prefix support
// (agent prompt templates use _prefixed placeholders intentionally)
// ─────────────────────────────────────────────────────────────
{
files: [".pi/agent/skills/**/*.ts", ".pi/agent/skills/**/*.tsx"],
plugins: {
"@typescript-eslint": tseslint.plugin,
},
languageOptions: {
parser: tseslint.parser,
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
},
rules: {
// Allow unused vars with underscore prefix (intentional placeholder pattern)
"@typescript-eslint/no-unused-vars": [
"warn",
{
vars: "all",
args: "after-used",
ignoreRestSiblings: true,
varsIgnorePattern: "^_",
argsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
},
],
// Allow explicit any in templates
"@typescript-eslint/no-explicit-any": "off",
},
},
// ─────────────────────────────────────────────────────────────
// ROOT-LEVEL MJS FILES — common JS/ESM patterns at project root
// ─────────────────────────────────────────────────────────────
{
files: ["*.mjs", "*.js"],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: {
// Common ESM globals
process: "readonly",
console: "readonly",
setTimeout: "readonly",
setInterval: "readonly",
clearTimeout: "readonly",
clearInterval: "readonly",
globalThis: "readonly",
},
},
rules: {
"no-console": "off",
"no-unused-vars": "off",
},
},
// ─────────────────────────────────────────────────────────────
// SERVICE WORKER FILES — browser service worker globals
// (packages/dashboard/app/public/sw.js uses self, caches, fetch, etc.)
// ─────────────────────────────────────────────────────────────
{
files: ["**/sw.js"],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: {
// Service worker globals
self: "readonly",
caches: "readonly",
fetch: "readonly",
console: "readonly",
URL: "readonly",
Promise: "readonly",
Request: "readonly",
Response: "readonly",
Headers: "readonly",
Cache: "readonly",
CacheStorage: "readonly",
ExtendableEvent: "readonly",
FetchEvent: "readonly",
Clients: "readonly",
Client: "readonly",
WindowClient: "readonly",
},
},
rules: {
"no-undef": "error",
"no-unused-vars": "off",
"no-console": "off",
},
},
);