doctor.config.ts

The full config schema, defineConfig helper, extends layering, presets, and per-rule overrides.

The doctor reads its config from the first file it finds, in this order:

  1. The path passed to --config <path>
  2. doctor.config.ts in the project root
  3. doctor.config.mjs in the project root
  4. doctor.config.js in the project root
  5. doctor.config.json in the project root
  6. The "doctor" key in package.json
  7. Built-in defaults (the recommended preset)

TypeScript is the recommended format. The doctor ships defineConfig for autocompletion.

The defineConfig helper

doctor.config.ts
import { defineConfig } from '@geoql/doctor-core';

export default defineConfig({
  preset: 'recommended',
  failOn: 'error',
  rootDir: '.',
  include: ['app/**', 'components/**', 'pages/**'],
  exclude: ['node_modules', 'dist', '.doctor', '.nuxt', '.output'],
  rules: {
    'no-em-dash-in-str': 'off',
    'watch-without-cleanup': 'error',
  },
  fixExcludes: ['app/content/**'],
  extends: ['./doctor.base.ts'],
});

defineConfig is a type-only helper — it returns the config object unchanged at runtime. Its job is to give you autocompletion and a type error if you mistype a rule id.

Schema

The config object is typed as DoctorUserConfig. Every field is optional; missing fields fall back to the built-in defaults.

ts
interface DoctorUserConfig {
  /** Project root (relative to the config file). Default: '.'. */
  rootDir?: string;

  /** Glob patterns to include. Default: ['app/**', 'components/**', 'pages/**', 'composables/**', 'layouts/**', 'middleware/**', 'plugins/**', 'server/**', 'utils/**']. */
  include?: string[];

  /** Glob patterns to exclude. Default: ['node_modules', 'dist', '.nuxt', '.output', '.doctor', 'coverage']. */
  exclude?: string[];

  /** Severity threshold for non-zero exit. 'error' | 'warn' | 'none'. Default: 'error'. */
  failOn?: 'error' | 'warn' | 'none';

  /** Score threshold (0–100). The audit exits non-zero if the score falls below. Default: 0. */
  threshold?: number;

  /** Base rule set. 'minimal' | 'recommended' | 'strict' | 'all'. Default: 'recommended'. */
  preset?: 'minimal' | 'recommended' | 'strict' | 'all';

  /** Per-rule severity overrides. Use 'off' to disable. */
  rules?: Record<string, 'error' | 'warn' | 'info' | 'off'>;

  /** Other config files to layer on top of this one. */
  extends?: string[];

  /** Globs to never auto-fix, even when --fix is set. */
  fixExcludes?: string[];
}

Presets

Presets are the base layer of the rule set. They enable or disable rules by severity.

presetwhat it includes
minimalerror findings only — warn and info rules are off
recommendederror + warn findings (the default)
stricterror + warn + info at their registered severities
allalias of strict, reserved for future expansion

Use minimal for a fast first pass on a legacy codebase, then graduate to recommended once the error findings are triaged.

Per-rule overrides

The rules field is merged on top of the preset. The value can be error, warn, info, or off. The off switch is intentional — it's the only way to silence a rule project-wide that you don't want flagged:

doctor.config.ts
export default defineConfig({
  rules: {
    'no-em-dash-in-str': 'off',
    'no-destructure-props-without-toRefs': 'warn',
  },
});

To silence a rule for a single line, use an inline disable — see Quickstart.

extends

extends layers multiple config files. Later entries win.

doctor.config.ts
export default defineConfig({
  extends: [
    './doctor.base.ts', // team-wide baseline
    './doctor.local.ts', // your local overrides
  ],
});

The merge order is:

  1. The preset's base rule set
  2. The config file's rules (with extends applied in order)
  3. CLI --rule overrides

A field that appears in two layers is overwritten by the later layer. Arrays (include, exclude, fixExcludes) are concatenated.

fixExcludes

When --fix is set, the doctor will not modify files matching any glob in fixExcludes. The default is [] (everything is fixable unless excluded).

doctor.config.ts
export default defineConfig({
  fixExcludes: [
    'app/content/**', // generated content
    '**/*.generated.ts', // codegen output
  ],
});

JSON / package.json form

If you'd rather not have a .ts file, the doctor accepts JSON:

doctor.config.json
{
  "preset": "recommended",
  "rules": {
    "no-em-dash-in-str": "off"
  }
}

Or inline in package.json:

package.json
{
  "doctor": {
    "preset": "recommended",
    "rules": {
      "no-em-dash-in-str": "off"
    }
  }
}

The TypeScript form is still preferred — the type-checker catches typos in rule ids, and defineConfig gives you autocompletion.

Where to put it

doctor.config.ts lives at the project root, next to package.json. In a monorepo, you can put one in each package and let each audit target its own scope. The doctor walks up from the current directory until it finds a config, just like ESLint.