@geoql/doctor-core 0.1.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -67,6 +67,153 @@ const RULE_REGISTRY = [
67
67
  source: "oxlint-builtin",
68
68
  recommended: true
69
69
  },
70
+ {
71
+ id: "vue/no-arrow-functions-in-watch",
72
+ severity: "error",
73
+ category: "vue-builtin",
74
+ source: "oxlint-builtin",
75
+ recommended: true
76
+ },
77
+ {
78
+ id: "vue/no-deprecated-data-object-declaration",
79
+ severity: "error",
80
+ category: "vue-builtin",
81
+ source: "oxlint-builtin",
82
+ recommended: true
83
+ },
84
+ {
85
+ id: "vue/no-deprecated-events-api",
86
+ severity: "error",
87
+ category: "vue-builtin",
88
+ source: "oxlint-builtin",
89
+ recommended: true
90
+ },
91
+ {
92
+ id: "vue/no-deprecated-destroyed-lifecycle",
93
+ severity: "error",
94
+ category: "vue-builtin",
95
+ source: "oxlint-builtin",
96
+ recommended: true
97
+ },
98
+ {
99
+ id: "vue/no-deprecated-model-definition",
100
+ severity: "error",
101
+ category: "vue-builtin",
102
+ source: "oxlint-builtin",
103
+ recommended: true
104
+ },
105
+ {
106
+ id: "vue/no-deprecated-delete-set",
107
+ severity: "error",
108
+ category: "vue-builtin",
109
+ source: "oxlint-builtin",
110
+ recommended: true
111
+ },
112
+ {
113
+ id: "vue/no-deprecated-vue-config-keycodes",
114
+ severity: "error",
115
+ category: "vue-builtin",
116
+ source: "oxlint-builtin",
117
+ recommended: true
118
+ },
119
+ {
120
+ id: "vue/no-lifecycle-after-await",
121
+ severity: "error",
122
+ category: "vue-builtin",
123
+ source: "oxlint-builtin",
124
+ recommended: true
125
+ },
126
+ {
127
+ id: "vue/no-this-in-before-route-enter",
128
+ severity: "error",
129
+ category: "vue-builtin",
130
+ source: "oxlint-builtin",
131
+ recommended: true
132
+ },
133
+ {
134
+ id: "vue/return-in-computed-property",
135
+ severity: "error",
136
+ category: "vue-builtin",
137
+ source: "oxlint-builtin",
138
+ recommended: true
139
+ },
140
+ {
141
+ id: "vue/valid-define-emits",
142
+ severity: "error",
143
+ category: "vue-builtin",
144
+ source: "oxlint-builtin",
145
+ recommended: true
146
+ },
147
+ {
148
+ id: "vue/valid-define-props",
149
+ severity: "error",
150
+ category: "vue-builtin",
151
+ source: "oxlint-builtin",
152
+ recommended: true
153
+ },
154
+ {
155
+ id: "vue/no-required-prop-with-default",
156
+ severity: "warn",
157
+ category: "vue-builtin",
158
+ source: "oxlint-builtin",
159
+ recommended: true
160
+ },
161
+ {
162
+ id: "vue/prefer-import-from-vue",
163
+ severity: "warn",
164
+ category: "vue-builtin",
165
+ source: "oxlint-builtin",
166
+ recommended: true
167
+ },
168
+ {
169
+ id: "vue/no-import-compiler-macros",
170
+ severity: "warn",
171
+ category: "vue-builtin",
172
+ source: "oxlint-builtin",
173
+ recommended: true
174
+ },
175
+ {
176
+ id: "vue/no-multiple-slot-args",
177
+ severity: "warn",
178
+ category: "vue-builtin",
179
+ source: "oxlint-builtin",
180
+ recommended: true
181
+ },
182
+ {
183
+ id: "vue/require-default-export",
184
+ severity: "warn",
185
+ category: "vue-builtin",
186
+ source: "oxlint-builtin",
187
+ recommended: true
188
+ },
189
+ {
190
+ id: "vue/define-emits-declaration",
191
+ severity: "info",
192
+ category: "vue-builtin",
193
+ source: "oxlint-builtin",
194
+ recommended: false
195
+ },
196
+ {
197
+ id: "vue/define-props-declaration",
198
+ severity: "info",
199
+ category: "vue-builtin",
200
+ source: "oxlint-builtin",
201
+ recommended: false
202
+ },
203
+ {
204
+ id: "vue/define-props-destructuring",
205
+ severity: "info",
206
+ category: "vue-builtin",
207
+ source: "oxlint-builtin",
208
+ recommended: false
209
+ },
210
+ {
211
+ id: "vue/max-props",
212
+ severity: "info",
213
+ category: "vue-builtin",
214
+ source: "oxlint-builtin",
215
+ recommended: false
216
+ },
70
217
  {
71
218
  id: "vue-doctor/no-em-dash-in-string",
72
219
  severity: "warn",
@@ -546,6 +693,11 @@ function validateConfig(raw) {
546
693
  if (!Array.isArray(exclude)) throw new InvalidConfigError("exclude: must be an array of strings");
547
694
  if (!exclude.every((v) => typeof v === "string")) throw new InvalidConfigError("exclude: must be an array of strings");
548
695
  }
696
+ if ("fixExcludes" in config) {
697
+ const fixExcludes = config.fixExcludes;
698
+ if (!Array.isArray(fixExcludes)) throw new InvalidConfigError("fixExcludes: must be an array of strings");
699
+ if (!fixExcludes.every((v) => typeof v === "string")) throw new InvalidConfigError("fixExcludes: must be an array of strings");
700
+ }
549
701
  if ("rules" in config) {
550
702
  const rules = config.rules;
551
703
  if (rules === null || typeof rules !== "object" || Array.isArray(rules)) throw new InvalidConfigError("rules: must be an object");
@@ -620,10 +772,11 @@ async function loadDoctorConfig(rootDir, explicitPathOrOptions) {
620
772
  rules: mergedRules,
621
773
  preset: presetName,
622
774
  source,
623
- configFile
775
+ configFile,
776
+ ...raw.fixExcludes ? { fixExcludes: raw.fixExcludes } : {}
624
777
  };
625
778
  }
626
779
  //#endregion
627
780
  export { listRules as a, InvalidConfigError as c, RULE_REGISTRY as i, BUILT_IN_RECOMMENDED as l, load_exports as n, ConfigCycleError as o, validateConfig as r, ConfigFileNotFoundError as s, loadDoctorConfig as t };
628
781
 
629
- //# sourceMappingURL=load-BGjurxpY.js.map
782
+ //# sourceMappingURL=load-yXDi-5ti.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-yXDi-5ti.js","names":[],"sources":["../src/config/built-in.ts","../src/config/errors.ts","../src/rule-registry.ts","../src/config/presets.ts","../src/config/validate.ts","../src/config/load.ts"],"sourcesContent":["import type { Severity } from '../types.js';\n\nexport const BUILT_IN_RECOMMENDED: Omit<\n import('./types.js').ResolvedDoctorConfig,\n 'rootDir' | 'source' | 'configFile'\n> = {\n include: ['**/*.vue', '**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],\n exclude: ['node_modules', 'dist', '.nuxt', '.output', 'coverage'],\n failOn: 'error' as const,\n threshold: 0,\n rules: {} as Record<string, Severity>,\n};\n","export class ConfigFileNotFoundError extends Error {\n override name = 'ConfigFileNotFoundError' as const;\n\n constructor(path: string) {\n super(`Config file not found: ${path}`);\n }\n}\n\nexport class ConfigCycleError extends Error {\n override name = 'ConfigCycleError' as const;\n\n constructor(chain: string[]) {\n super(`Config extends cycle detected: ${chain.join(' -> ')}`);\n }\n}\n\nexport class InvalidConfigError extends Error {\n override name = 'InvalidConfigError' as const;\n}\n","import type { Severity } from './types.js';\n\nexport type RuleCategory =\n | 'ai-slop'\n | 'reactivity'\n | 'composition'\n | 'performance'\n | 'template'\n | 'template-perf'\n | 'build-quality'\n | 'deps'\n | 'dead-code'\n | 'sfc'\n | 'vue-builtin'\n | 'structure'\n | 'modules-deps'\n | 'nitro'\n | 'seo'\n | 'cloudflare'\n | 'server-routes'\n | 'hydration'\n | 'data-fetching';\n\nexport type RuleSource = 'doctor' | 'oxlint-builtin' | 'eslint-plugin-vue';\n\nexport interface RegisteredRule {\n readonly id: string;\n readonly severity: Severity;\n readonly category: RuleCategory;\n readonly source: RuleSource;\n readonly recommended: boolean;\n}\n\nexport const RULE_REGISTRY: readonly RegisteredRule[] = [\n {\n id: 'vue/no-export-in-script-setup',\n severity: 'error',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: true,\n },\n {\n id: 'vue/require-typed-ref',\n severity: 'warn',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: true,\n },\n {\n id: 'vue/no-arrow-functions-in-watch',\n severity: 'error',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: true,\n },\n {\n id: 'vue/no-deprecated-data-object-declaration',\n severity: 'error',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: true,\n },\n {\n id: 'vue/no-deprecated-events-api',\n severity: 'error',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: true,\n },\n {\n id: 'vue/no-deprecated-destroyed-lifecycle',\n severity: 'error',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: true,\n },\n {\n id: 'vue/no-deprecated-model-definition',\n severity: 'error',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: true,\n },\n {\n id: 'vue/no-deprecated-delete-set',\n severity: 'error',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: true,\n },\n {\n id: 'vue/no-deprecated-vue-config-keycodes',\n severity: 'error',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: true,\n },\n {\n id: 'vue/no-lifecycle-after-await',\n severity: 'error',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: true,\n },\n {\n id: 'vue/no-this-in-before-route-enter',\n severity: 'error',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: true,\n },\n {\n id: 'vue/return-in-computed-property',\n severity: 'error',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: true,\n },\n {\n id: 'vue/valid-define-emits',\n severity: 'error',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: true,\n },\n {\n id: 'vue/valid-define-props',\n severity: 'error',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: true,\n },\n {\n id: 'vue/no-required-prop-with-default',\n severity: 'warn',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: true,\n },\n {\n id: 'vue/prefer-import-from-vue',\n severity: 'warn',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: true,\n },\n {\n id: 'vue/no-import-compiler-macros',\n severity: 'warn',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: true,\n },\n {\n id: 'vue/no-multiple-slot-args',\n severity: 'warn',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: true,\n },\n {\n id: 'vue/require-default-export',\n severity: 'warn',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: true,\n },\n {\n id: 'vue/define-emits-declaration',\n severity: 'info',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: false,\n },\n {\n id: 'vue/define-props-declaration',\n severity: 'info',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: false,\n },\n {\n id: 'vue/define-props-destructuring',\n severity: 'info',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: false,\n },\n {\n id: 'vue/max-props',\n severity: 'info',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: false,\n },\n\n {\n id: 'vue-doctor/no-em-dash-in-string',\n severity: 'warn',\n category: 'ai-slop',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/no-destructure-props-without-to-refs',\n severity: 'error',\n category: 'ai-slop',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/no-destructure-reactive-without-to-refs',\n severity: 'error',\n category: 'ai-slop',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/no-non-null-assertion-on-ref-value',\n severity: 'warn',\n category: 'ai-slop',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/no-imports-from-vue-when-auto-imported',\n severity: 'warn',\n category: 'ai-slop',\n source: 'doctor',\n recommended: true,\n },\n\n {\n id: 'vue-doctor/reactivity/watch-without-cleanup',\n severity: 'warn',\n category: 'reactivity',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/reactivity/prefer-shallowRef-for-large-data',\n severity: 'info',\n category: 'reactivity',\n source: 'doctor',\n recommended: false,\n },\n {\n id: 'vue-doctor/reactivity/prefer-readonly-for-injected',\n severity: 'info',\n category: 'reactivity',\n source: 'doctor',\n recommended: false,\n },\n\n {\n id: 'vue-doctor/composition/prefer-script-setup-for-new-files',\n severity: 'warn',\n category: 'composition',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/composition/defineProps-typed',\n severity: 'warn',\n category: 'composition',\n source: 'doctor',\n recommended: true,\n },\n\n {\n id: 'vue-doctor/performance/prefer-defineAsyncComponent-on-route',\n severity: 'info',\n category: 'performance',\n source: 'doctor',\n recommended: false,\n },\n\n {\n id: 'vue-doctor/template/v-for-has-key',\n severity: 'error',\n category: 'template',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/template/v-if-v-for-precedence',\n severity: 'error',\n category: 'template',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/template/v-memo-on-large-list',\n severity: 'warn',\n category: 'performance',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/template/no-inline-object-prop-in-list',\n severity: 'warn',\n category: 'performance',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/template/no-computed-getter-in-template-loop',\n severity: 'warn',\n category: 'template-perf',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/template/avoid-deep-v-bind-spread-in-list',\n severity: 'info',\n category: 'template-perf',\n source: 'doctor',\n recommended: true,\n },\n\n {\n id: 'vue-doctor/sfc/no-mixed-options-and-composition-api',\n severity: 'warn',\n category: 'sfc',\n source: 'doctor',\n recommended: true,\n },\n\n {\n id: 'vue-doctor/build-quality/tsconfig-strict-required',\n severity: 'warn',\n category: 'build-quality',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/build-quality/vue-tsc-in-devDeps',\n severity: 'warn',\n category: 'build-quality',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/build-quality/no-vue-cli',\n severity: 'warn',\n category: 'build-quality',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/build-quality/eslint-plugin-vue-installed',\n severity: 'info',\n category: 'build-quality',\n source: 'doctor',\n recommended: true,\n },\n\n {\n id: 'vue-doctor/deps/duplicate-vue-versions',\n severity: 'error',\n category: 'deps',\n source: 'doctor',\n recommended: true,\n },\n\n {\n id: 'vue-doctor/deps/vue-major-current',\n severity: 'info',\n category: 'deps',\n source: 'doctor',\n recommended: false,\n },\n\n {\n id: 'dead-code/unused-file',\n severity: 'warn',\n category: 'dead-code',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'dead-code/unused-export',\n severity: 'warn',\n category: 'dead-code',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'dead-code/unused-type-export',\n severity: 'info',\n category: 'dead-code',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'dead-code/unused-member',\n severity: 'info',\n category: 'dead-code',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'dead-code/unused-dependency',\n severity: 'warn',\n category: 'dead-code',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'dead-code/unlisted-dependency',\n severity: 'error',\n category: 'dead-code',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'dead-code/duplicate-export',\n severity: 'warn',\n category: 'dead-code',\n source: 'doctor',\n recommended: true,\n },\n\n // ── Nuxt project post-checks ────────────────────────────────────────────────\n {\n id: 'nuxt-doctor/structure/uses-app-directory',\n severity: 'warn',\n category: 'structure',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/structure/nuxt-major-current',\n severity: 'info',\n category: 'structure',\n source: 'doctor',\n recommended: false,\n },\n {\n id: 'nuxt-doctor/modules-deps/no-modules-incompatible-with-nuxt-4',\n severity: 'warn',\n category: 'modules-deps',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/modules-deps/recommended-modules-installed',\n severity: 'info',\n category: 'modules-deps',\n source: 'doctor',\n recommended: false,\n },\n {\n id: 'nuxt-doctor/nitro/compatibilityDate-set',\n severity: 'error',\n category: 'nitro',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/nitro/preset-defined-for-deploy-target',\n severity: 'warn',\n category: 'nitro',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/nitro/runtime-config-typed',\n severity: 'info',\n category: 'nitro',\n source: 'doctor',\n recommended: false,\n },\n {\n id: 'nuxt-doctor/seo/lang-on-html',\n severity: 'warn',\n category: 'seo',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/seo/useSeoMeta-on-public-page',\n severity: 'warn',\n category: 'seo',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/seo/og-image-defined',\n severity: 'warn',\n category: 'seo',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/cloudflare/nitro-cloudflare-preset',\n severity: 'warn',\n category: 'cloudflare',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/cloudflare/og-image-via-satori',\n severity: 'info',\n category: 'cloudflare',\n source: 'doctor',\n recommended: false,\n },\n {\n id: 'nuxt-doctor/cloudflare/no-node-only-modules',\n severity: 'warn',\n category: 'cloudflare',\n source: 'doctor',\n recommended: true,\n },\n\n // ── Nuxt cross-file rules ────────────────────────────────────────────────────\n {\n id: 'nuxt-doctor/data-fetching/no-shared-key-across-pages',\n severity: 'warn',\n category: 'data-fetching',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/data-fetching/ssr-safe-onMounted-only-for-client',\n severity: 'warn',\n category: 'data-fetching',\n source: 'doctor',\n recommended: true,\n },\n\n // ── Nuxt SFC rules ──────────────────────────────────────────────────────────\n {\n id: 'nuxt-doctor/ai-slop/no-mixed-app-and-root-layout',\n severity: 'warn',\n category: 'ai-slop',\n source: 'doctor',\n recommended: true,\n },\n\n // ── Nuxt oxlint-plugin rules (source 'doctor') ──────────────────────────────\n {\n id: 'nuxt-doctor/ai-slop/no-process-client-server',\n severity: 'error',\n category: 'ai-slop',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/ai-slop/no-explicit-imports-of-auto-imported',\n severity: 'warn',\n category: 'ai-slop',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/ai-slop/no-useState-for-server-data',\n severity: 'warn',\n category: 'ai-slop',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/ai-slop/no-fetch-in-setup',\n severity: 'warn',\n category: 'ai-slop',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/data-fetching/useAsyncData-key-required-in-loop',\n severity: 'error',\n category: 'data-fetching',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/server-routes/defineEventHandler-typed',\n severity: 'warn',\n category: 'server-routes',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/server-routes/validate-body-with-h3-v2',\n severity: 'warn',\n category: 'server-routes',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/server-routes/createError-on-failure',\n severity: 'warn',\n category: 'server-routes',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/hydration/no-document-in-setup',\n severity: 'error',\n category: 'hydration',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/hydration/clientOnly-for-browser-apis',\n severity: 'error',\n category: 'hydration',\n source: 'doctor',\n recommended: true,\n },\n];\n\nexport interface ListRulesFilter {\n readonly preset?: 'recommended' | 'all';\n readonly category?: RuleCategory;\n readonly source?: RuleSource;\n readonly severity?: Severity;\n}\n\nexport function listRules(filter: ListRulesFilter = {}): RegisteredRule[] {\n let rules = [...RULE_REGISTRY];\n if (filter.preset === 'recommended') {\n rules = rules.filter((r) => r.recommended);\n }\n if (filter.category) {\n rules = rules.filter((r) => r.category === filter.category);\n }\n if (filter.source) {\n rules = rules.filter((r) => r.source === filter.source);\n }\n if (filter.severity) {\n rules = rules.filter((r) => r.severity === filter.severity);\n }\n return rules.sort((a, b) => a.id.localeCompare(b.id));\n}\n","import { RULE_REGISTRY } from '../rule-registry.js';\nimport type { Severity } from '../types.js';\n\nexport type PresetName = 'minimal' | 'recommended' | 'strict' | 'all';\n\nexport const PRESET_NAMES: readonly PresetName[] = [\n 'minimal',\n 'recommended',\n 'strict',\n 'all',\n];\n\nexport function isPresetName(value: string): value is PresetName {\n return (PRESET_NAMES as readonly string[]).includes(value);\n}\n\n/**\n * Resolve a preset name to its base ruleId -> Severity map.\n *\n * - `minimal` : errors only (warn/info turned off)\n * - `recommended` : errors + warns (info off) — same as today's default\n * - `strict` : errors + warns + infos all on at registered severity\n * - `all` : alias of `strict` for now; reserved to surface every\n * known ruleId regardless of preset opt-in policy\n *\n * The returned map is the BASE; downstream code merges user config\n * `rules:` on top, then CLI `--rule` overrides on top of that.\n */\nexport function resolvePreset(name: PresetName): Record<string, Severity> {\n const rules: Record<string, Severity> = {};\n for (const rule of RULE_REGISTRY) {\n if (name === 'minimal') {\n if (rule.severity === 'error') rules[rule.id] = 'error';\n } else if (name === 'recommended') {\n if (rule.severity === 'error' || rule.severity === 'warn') {\n rules[rule.id] = rule.severity;\n }\n } else {\n // strict | all\n rules[rule.id] = rule.severity;\n }\n }\n return rules;\n}\n","import { InvalidConfigError } from './errors.js';\nimport { isPresetName } from './presets.js';\n\nconst VALID_SEVERITIES = new Set(['error', 'warn', 'info', 'off']);\nconst VALID_FAIL_ON = new Set(['error', 'warn', 'none']);\n\nexport function validateConfig(raw: unknown): void {\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new InvalidConfigError('config: must be an object');\n }\n\n const config = raw as Record<string, unknown>;\n\n if ('threshold' in config) {\n const threshold = config.threshold;\n if (typeof threshold !== 'number' || !Number.isInteger(threshold)) {\n throw new InvalidConfigError(\n `threshold: must be an integer 0..100, got ${JSON.stringify(threshold)}`,\n );\n }\n if (threshold < 0 || threshold > 100) {\n throw new InvalidConfigError(\n `threshold: must be 0..100, got ${threshold}`,\n );\n }\n }\n\n if ('preset' in config) {\n const preset = config.preset;\n if (typeof preset !== 'string' || !isPresetName(preset)) {\n throw new InvalidConfigError(\n `preset: must be one of 'minimal', 'recommended', 'strict', 'all', got ${JSON.stringify(preset)}`,\n );\n }\n }\n\n if ('failOn' in config) {\n const failOn = config.failOn;\n if (typeof failOn !== 'string' || !VALID_FAIL_ON.has(failOn)) {\n throw new InvalidConfigError(\n `failOn: must be 'error', 'warn', or 'none', got ${JSON.stringify(failOn)}`,\n );\n }\n }\n\n if ('include' in config) {\n const include = config.include;\n if (!Array.isArray(include)) {\n throw new InvalidConfigError('include: must be an array of strings');\n }\n if (!include.every((v) => typeof v === 'string')) {\n throw new InvalidConfigError('include: must be an array of strings');\n }\n }\n\n if ('exclude' in config) {\n const exclude = config.exclude;\n if (!Array.isArray(exclude)) {\n throw new InvalidConfigError('exclude: must be an array of strings');\n }\n if (!exclude.every((v) => typeof v === 'string')) {\n throw new InvalidConfigError('exclude: must be an array of strings');\n }\n }\n\n if ('fixExcludes' in config) {\n const fixExcludes = config.fixExcludes;\n if (!Array.isArray(fixExcludes)) {\n throw new InvalidConfigError('fixExcludes: must be an array of strings');\n }\n if (!fixExcludes.every((v) => typeof v === 'string')) {\n throw new InvalidConfigError('fixExcludes: must be an array of strings');\n }\n }\n\n if ('rules' in config) {\n const rules = config.rules;\n if (rules === null || typeof rules !== 'object' || Array.isArray(rules)) {\n throw new InvalidConfigError('rules: must be an object');\n }\n for (const [key, value] of Object.entries(\n rules as Record<string, unknown>,\n )) {\n if (typeof value !== 'string' || !VALID_SEVERITIES.has(value)) {\n throw new InvalidConfigError(\n `rules.${key}: must be a severity ('error', 'warn', 'info', or 'off'), got ${JSON.stringify(value)}`,\n );\n }\n }\n }\n}\n","import { existsSync } from 'node:fs';\nimport { extname, resolve } from 'node:path';\nimport { loadConfig } from 'c12';\nimport { BUILT_IN_RECOMMENDED } from './built-in.js';\nimport {\n ConfigCycleError,\n ConfigFileNotFoundError,\n InvalidConfigError,\n} from './errors.js';\nimport { isPresetName, type PresetName, resolvePreset } from './presets.js';\nimport type {\n ConfigSource,\n DoctorUserConfig,\n ResolvedDoctorConfig,\n} from './types.js';\nimport { validateConfig } from './validate.js';\nimport type { Severity } from '../types.js';\n\nconst SOURCE_MAP: Record<string, ConfigSource> = {\n ts: 'ts',\n mjs: 'mjs',\n js: 'js',\n json: 'json',\n};\n\nexport interface LoadDoctorConfigOptions {\n readonly explicitPath?: string;\n readonly presetOverride?: string;\n}\n\nexport async function loadDoctorConfig(\n rootDir: string,\n explicitPathOrOptions?: string | LoadDoctorConfigOptions,\n): Promise<ResolvedDoctorConfig> {\n const opts: LoadDoctorConfigOptions =\n typeof explicitPathOrOptions === 'string'\n ? { explicitPath: explicitPathOrOptions }\n : (explicitPathOrOptions ?? {});\n const explicitPath = opts.explicitPath;\n if (opts.presetOverride !== undefined && !isPresetName(opts.presetOverride)) {\n throw new InvalidConfigError(\n `preset: must be one of 'minimal', 'recommended', 'strict', 'all', got ${JSON.stringify(opts.presetOverride)}`,\n );\n }\n if (explicitPath) {\n const absPath = resolve(rootDir, explicitPath);\n if (!existsSync(absPath)) {\n throw new ConfigFileNotFoundError(explicitPath);\n }\n }\n\n const chain: string[] = [];\n\n const result = await loadConfig<DoctorUserConfig>({\n cwd: rootDir,\n name: 'doctor',\n packageJson: 'doctor',\n rcFile: false,\n globalRc: false,\n ...(explicitPath ? { configFile: resolve(rootDir, explicitPath) } : {}),\n resolve(source, options) {\n const base = options.cwd;\n const key = resolve(base, source);\n if (chain.includes(key)) {\n throw new ConfigCycleError([...chain, key]);\n }\n chain.push(key);\n return undefined;\n },\n });\n\n const raw: DoctorUserConfig = result.config as DoctorUserConfig;\n\n validateConfig(raw);\n\n let source: ConfigSource;\n let configFile: string | undefined;\n\n if (explicitPath) {\n source = 'flag';\n configFile = resolve(rootDir, explicitPath);\n } else if (result._configFile) {\n const ext = extname(result._configFile).slice(1) as keyof typeof SOURCE_MAP;\n source = SOURCE_MAP[ext];\n configFile = result._configFile;\n } else if (Object.keys(raw).length > 0) {\n source = 'package.json';\n configFile = resolve(rootDir, 'package.json');\n } else {\n source = 'built-in';\n configFile = undefined;\n }\n\n // Resolve preset name: CLI override wins, then config file, default 'recommended'.\n const presetName: PresetName = (opts.presetOverride ??\n raw.preset ??\n 'recommended') as PresetName;\n const presetRules = resolvePreset(presetName);\n\n // User config: 'off' explicitly removes the rule from the base preset.\n const userRules: Record<string, Severity> = {};\n const userOff = new Set<string>();\n if (raw.rules && typeof raw.rules === 'object') {\n for (const [key, value] of Object.entries(raw.rules)) {\n if (value === 'off') {\n userOff.add(key);\n } else {\n userRules[key] = value as Severity;\n }\n }\n }\n\n // Merge: preset base -> user rules on top -> user 'off' removes entries.\n const mergedRules: Record<string, Severity> = {\n ...presetRules,\n ...userRules,\n };\n for (const key of userOff) delete mergedRules[key];\n\n return {\n rootDir,\n include: raw.include ?? BUILT_IN_RECOMMENDED.include,\n exclude: raw.exclude ?? BUILT_IN_RECOMMENDED.exclude,\n failOn: raw.failOn ?? BUILT_IN_RECOMMENDED.failOn,\n threshold: raw.threshold ?? BUILT_IN_RECOMMENDED.threshold,\n rules: mergedRules,\n preset: presetName,\n source,\n configFile,\n ...(raw.fixExcludes ? { fixExcludes: raw.fixExcludes } : {}),\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geoql/doctor-core",
3
- "version": "0.1.1",
3
+ "version": "1.0.0",
4
4
  "private": false,
5
5
  "description": "Audit engine for @geoql/vue-doctor and @geoql/nuxt-doctor. Hybrid two-pass: @vue/compiler-sfc template AST + oxlint subprocess. TypeScript, ESM.",
6
6
  "keywords": [
@@ -51,8 +51,8 @@
51
51
  "picocolors": "^1.1.1",
52
52
  "semver": "^7.8.1",
53
53
  "tinyglobby": "^0.2.16",
54
- "@geoql/oxlint-plugin-vue-doctor": "^0.1.1",
55
- "@geoql/oxlint-plugin-nuxt-doctor": "0.1.1"
54
+ "@geoql/oxlint-plugin-nuxt-doctor": "1.0.0",
55
+ "@geoql/oxlint-plugin-vue-doctor": "^1.0.0"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@types/node": "^25.9.1",
@@ -1 +0,0 @@
1
- {"version":3,"file":"load-BGjurxpY.js","names":[],"sources":["../src/config/built-in.ts","../src/config/errors.ts","../src/rule-registry.ts","../src/config/presets.ts","../src/config/validate.ts","../src/config/load.ts"],"sourcesContent":["import type { Severity } from '../types.js';\n\nexport const BUILT_IN_RECOMMENDED: Omit<\n import('./types.js').ResolvedDoctorConfig,\n 'rootDir' | 'source' | 'configFile'\n> = {\n include: ['**/*.vue', '**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],\n exclude: ['node_modules', 'dist', '.nuxt', '.output', 'coverage'],\n failOn: 'error' as const,\n threshold: 0,\n rules: {} as Record<string, Severity>,\n};\n","export class ConfigFileNotFoundError extends Error {\n override name = 'ConfigFileNotFoundError' as const;\n\n constructor(path: string) {\n super(`Config file not found: ${path}`);\n }\n}\n\nexport class ConfigCycleError extends Error {\n override name = 'ConfigCycleError' as const;\n\n constructor(chain: string[]) {\n super(`Config extends cycle detected: ${chain.join(' -> ')}`);\n }\n}\n\nexport class InvalidConfigError extends Error {\n override name = 'InvalidConfigError' as const;\n}\n","import type { Severity } from './types.js';\n\nexport type RuleCategory =\n | 'ai-slop'\n | 'reactivity'\n | 'composition'\n | 'performance'\n | 'template'\n | 'template-perf'\n | 'build-quality'\n | 'deps'\n | 'dead-code'\n | 'sfc'\n | 'vue-builtin'\n | 'structure'\n | 'modules-deps'\n | 'nitro'\n | 'seo'\n | 'cloudflare'\n | 'server-routes'\n | 'hydration'\n | 'data-fetching';\n\nexport type RuleSource = 'doctor' | 'oxlint-builtin' | 'eslint-plugin-vue';\n\nexport interface RegisteredRule {\n readonly id: string;\n readonly severity: Severity;\n readonly category: RuleCategory;\n readonly source: RuleSource;\n readonly recommended: boolean;\n}\n\nexport const RULE_REGISTRY: readonly RegisteredRule[] = [\n {\n id: 'vue/no-export-in-script-setup',\n severity: 'error',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: true,\n },\n {\n id: 'vue/require-typed-ref',\n severity: 'warn',\n category: 'vue-builtin',\n source: 'oxlint-builtin',\n recommended: true,\n },\n\n {\n id: 'vue-doctor/no-em-dash-in-string',\n severity: 'warn',\n category: 'ai-slop',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/no-destructure-props-without-to-refs',\n severity: 'error',\n category: 'ai-slop',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/no-destructure-reactive-without-to-refs',\n severity: 'error',\n category: 'ai-slop',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/no-non-null-assertion-on-ref-value',\n severity: 'warn',\n category: 'ai-slop',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/no-imports-from-vue-when-auto-imported',\n severity: 'warn',\n category: 'ai-slop',\n source: 'doctor',\n recommended: true,\n },\n\n {\n id: 'vue-doctor/reactivity/watch-without-cleanup',\n severity: 'warn',\n category: 'reactivity',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/reactivity/prefer-shallowRef-for-large-data',\n severity: 'info',\n category: 'reactivity',\n source: 'doctor',\n recommended: false,\n },\n {\n id: 'vue-doctor/reactivity/prefer-readonly-for-injected',\n severity: 'info',\n category: 'reactivity',\n source: 'doctor',\n recommended: false,\n },\n\n {\n id: 'vue-doctor/composition/prefer-script-setup-for-new-files',\n severity: 'warn',\n category: 'composition',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/composition/defineProps-typed',\n severity: 'warn',\n category: 'composition',\n source: 'doctor',\n recommended: true,\n },\n\n {\n id: 'vue-doctor/performance/prefer-defineAsyncComponent-on-route',\n severity: 'info',\n category: 'performance',\n source: 'doctor',\n recommended: false,\n },\n\n {\n id: 'vue-doctor/template/v-for-has-key',\n severity: 'error',\n category: 'template',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/template/v-if-v-for-precedence',\n severity: 'error',\n category: 'template',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/template/v-memo-on-large-list',\n severity: 'warn',\n category: 'performance',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/template/no-inline-object-prop-in-list',\n severity: 'warn',\n category: 'performance',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/template/no-computed-getter-in-template-loop',\n severity: 'warn',\n category: 'template-perf',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/template/avoid-deep-v-bind-spread-in-list',\n severity: 'info',\n category: 'template-perf',\n source: 'doctor',\n recommended: true,\n },\n\n {\n id: 'vue-doctor/sfc/no-mixed-options-and-composition-api',\n severity: 'warn',\n category: 'sfc',\n source: 'doctor',\n recommended: true,\n },\n\n {\n id: 'vue-doctor/build-quality/tsconfig-strict-required',\n severity: 'warn',\n category: 'build-quality',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/build-quality/vue-tsc-in-devDeps',\n severity: 'warn',\n category: 'build-quality',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/build-quality/no-vue-cli',\n severity: 'warn',\n category: 'build-quality',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'vue-doctor/build-quality/eslint-plugin-vue-installed',\n severity: 'info',\n category: 'build-quality',\n source: 'doctor',\n recommended: true,\n },\n\n {\n id: 'vue-doctor/deps/duplicate-vue-versions',\n severity: 'error',\n category: 'deps',\n source: 'doctor',\n recommended: true,\n },\n\n {\n id: 'vue-doctor/deps/vue-major-current',\n severity: 'info',\n category: 'deps',\n source: 'doctor',\n recommended: false,\n },\n\n {\n id: 'dead-code/unused-file',\n severity: 'warn',\n category: 'dead-code',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'dead-code/unused-export',\n severity: 'warn',\n category: 'dead-code',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'dead-code/unused-type-export',\n severity: 'info',\n category: 'dead-code',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'dead-code/unused-member',\n severity: 'info',\n category: 'dead-code',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'dead-code/unused-dependency',\n severity: 'warn',\n category: 'dead-code',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'dead-code/unlisted-dependency',\n severity: 'error',\n category: 'dead-code',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'dead-code/duplicate-export',\n severity: 'warn',\n category: 'dead-code',\n source: 'doctor',\n recommended: true,\n },\n\n // ── Nuxt project post-checks ────────────────────────────────────────────────\n {\n id: 'nuxt-doctor/structure/uses-app-directory',\n severity: 'warn',\n category: 'structure',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/structure/nuxt-major-current',\n severity: 'info',\n category: 'structure',\n source: 'doctor',\n recommended: false,\n },\n {\n id: 'nuxt-doctor/modules-deps/no-modules-incompatible-with-nuxt-4',\n severity: 'warn',\n category: 'modules-deps',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/modules-deps/recommended-modules-installed',\n severity: 'info',\n category: 'modules-deps',\n source: 'doctor',\n recommended: false,\n },\n {\n id: 'nuxt-doctor/nitro/compatibilityDate-set',\n severity: 'error',\n category: 'nitro',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/nitro/preset-defined-for-deploy-target',\n severity: 'warn',\n category: 'nitro',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/nitro/runtime-config-typed',\n severity: 'info',\n category: 'nitro',\n source: 'doctor',\n recommended: false,\n },\n {\n id: 'nuxt-doctor/seo/lang-on-html',\n severity: 'warn',\n category: 'seo',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/seo/useSeoMeta-on-public-page',\n severity: 'warn',\n category: 'seo',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/seo/og-image-defined',\n severity: 'warn',\n category: 'seo',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/cloudflare/nitro-cloudflare-preset',\n severity: 'warn',\n category: 'cloudflare',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/cloudflare/og-image-via-satori',\n severity: 'info',\n category: 'cloudflare',\n source: 'doctor',\n recommended: false,\n },\n {\n id: 'nuxt-doctor/cloudflare/no-node-only-modules',\n severity: 'warn',\n category: 'cloudflare',\n source: 'doctor',\n recommended: true,\n },\n\n // ── Nuxt cross-file rules ────────────────────────────────────────────────────\n {\n id: 'nuxt-doctor/data-fetching/no-shared-key-across-pages',\n severity: 'warn',\n category: 'data-fetching',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/data-fetching/ssr-safe-onMounted-only-for-client',\n severity: 'warn',\n category: 'data-fetching',\n source: 'doctor',\n recommended: true,\n },\n\n // ── Nuxt SFC rules ──────────────────────────────────────────────────────────\n {\n id: 'nuxt-doctor/ai-slop/no-mixed-app-and-root-layout',\n severity: 'warn',\n category: 'ai-slop',\n source: 'doctor',\n recommended: true,\n },\n\n // ── Nuxt oxlint-plugin rules (source 'doctor') ──────────────────────────────\n {\n id: 'nuxt-doctor/ai-slop/no-process-client-server',\n severity: 'error',\n category: 'ai-slop',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/ai-slop/no-explicit-imports-of-auto-imported',\n severity: 'warn',\n category: 'ai-slop',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/ai-slop/no-useState-for-server-data',\n severity: 'warn',\n category: 'ai-slop',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/ai-slop/no-fetch-in-setup',\n severity: 'warn',\n category: 'ai-slop',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/data-fetching/useAsyncData-key-required-in-loop',\n severity: 'error',\n category: 'data-fetching',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/server-routes/defineEventHandler-typed',\n severity: 'warn',\n category: 'server-routes',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/server-routes/validate-body-with-h3-v2',\n severity: 'warn',\n category: 'server-routes',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/server-routes/createError-on-failure',\n severity: 'warn',\n category: 'server-routes',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/hydration/no-document-in-setup',\n severity: 'error',\n category: 'hydration',\n source: 'doctor',\n recommended: true,\n },\n {\n id: 'nuxt-doctor/hydration/clientOnly-for-browser-apis',\n severity: 'error',\n category: 'hydration',\n source: 'doctor',\n recommended: true,\n },\n];\n\nexport interface ListRulesFilter {\n readonly preset?: 'recommended' | 'all';\n readonly category?: RuleCategory;\n readonly source?: RuleSource;\n readonly severity?: Severity;\n}\n\nexport function listRules(filter: ListRulesFilter = {}): RegisteredRule[] {\n let rules = [...RULE_REGISTRY];\n if (filter.preset === 'recommended') {\n rules = rules.filter((r) => r.recommended);\n }\n if (filter.category) {\n rules = rules.filter((r) => r.category === filter.category);\n }\n if (filter.source) {\n rules = rules.filter((r) => r.source === filter.source);\n }\n if (filter.severity) {\n rules = rules.filter((r) => r.severity === filter.severity);\n }\n return rules.sort((a, b) => a.id.localeCompare(b.id));\n}\n","import { RULE_REGISTRY } from '../rule-registry.js';\nimport type { Severity } from '../types.js';\n\nexport type PresetName = 'minimal' | 'recommended' | 'strict' | 'all';\n\nexport const PRESET_NAMES: readonly PresetName[] = [\n 'minimal',\n 'recommended',\n 'strict',\n 'all',\n];\n\nexport function isPresetName(value: string): value is PresetName {\n return (PRESET_NAMES as readonly string[]).includes(value);\n}\n\n/**\n * Resolve a preset name to its base ruleId -> Severity map.\n *\n * - `minimal` : errors only (warn/info turned off)\n * - `recommended` : errors + warns (info off) — same as today's default\n * - `strict` : errors + warns + infos all on at registered severity\n * - `all` : alias of `strict` for now; reserved to surface every\n * known ruleId regardless of preset opt-in policy\n *\n * The returned map is the BASE; downstream code merges user config\n * `rules:` on top, then CLI `--rule` overrides on top of that.\n */\nexport function resolvePreset(name: PresetName): Record<string, Severity> {\n const rules: Record<string, Severity> = {};\n for (const rule of RULE_REGISTRY) {\n if (name === 'minimal') {\n if (rule.severity === 'error') rules[rule.id] = 'error';\n } else if (name === 'recommended') {\n if (rule.severity === 'error' || rule.severity === 'warn') {\n rules[rule.id] = rule.severity;\n }\n } else {\n // strict | all\n rules[rule.id] = rule.severity;\n }\n }\n return rules;\n}\n","import { InvalidConfigError } from './errors.js';\nimport { isPresetName } from './presets.js';\n\nconst VALID_SEVERITIES = new Set(['error', 'warn', 'info', 'off']);\nconst VALID_FAIL_ON = new Set(['error', 'warn', 'none']);\n\nexport function validateConfig(raw: unknown): void {\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new InvalidConfigError('config: must be an object');\n }\n\n const config = raw as Record<string, unknown>;\n\n if ('threshold' in config) {\n const threshold = config.threshold;\n if (typeof threshold !== 'number' || !Number.isInteger(threshold)) {\n throw new InvalidConfigError(\n `threshold: must be an integer 0..100, got ${JSON.stringify(threshold)}`,\n );\n }\n if (threshold < 0 || threshold > 100) {\n throw new InvalidConfigError(\n `threshold: must be 0..100, got ${threshold}`,\n );\n }\n }\n\n if ('preset' in config) {\n const preset = config.preset;\n if (typeof preset !== 'string' || !isPresetName(preset)) {\n throw new InvalidConfigError(\n `preset: must be one of 'minimal', 'recommended', 'strict', 'all', got ${JSON.stringify(preset)}`,\n );\n }\n }\n\n if ('failOn' in config) {\n const failOn = config.failOn;\n if (typeof failOn !== 'string' || !VALID_FAIL_ON.has(failOn)) {\n throw new InvalidConfigError(\n `failOn: must be 'error', 'warn', or 'none', got ${JSON.stringify(failOn)}`,\n );\n }\n }\n\n if ('include' in config) {\n const include = config.include;\n if (!Array.isArray(include)) {\n throw new InvalidConfigError('include: must be an array of strings');\n }\n if (!include.every((v) => typeof v === 'string')) {\n throw new InvalidConfigError('include: must be an array of strings');\n }\n }\n\n if ('exclude' in config) {\n const exclude = config.exclude;\n if (!Array.isArray(exclude)) {\n throw new InvalidConfigError('exclude: must be an array of strings');\n }\n if (!exclude.every((v) => typeof v === 'string')) {\n throw new InvalidConfigError('exclude: must be an array of strings');\n }\n }\n\n if ('rules' in config) {\n const rules = config.rules;\n if (rules === null || typeof rules !== 'object' || Array.isArray(rules)) {\n throw new InvalidConfigError('rules: must be an object');\n }\n for (const [key, value] of Object.entries(\n rules as Record<string, unknown>,\n )) {\n if (typeof value !== 'string' || !VALID_SEVERITIES.has(value)) {\n throw new InvalidConfigError(\n `rules.${key}: must be a severity ('error', 'warn', 'info', or 'off'), got ${JSON.stringify(value)}`,\n );\n }\n }\n }\n}\n","import { existsSync } from 'node:fs';\nimport { extname, resolve } from 'node:path';\nimport { loadConfig } from 'c12';\nimport { BUILT_IN_RECOMMENDED } from './built-in.js';\nimport {\n ConfigCycleError,\n ConfigFileNotFoundError,\n InvalidConfigError,\n} from './errors.js';\nimport { isPresetName, type PresetName, resolvePreset } from './presets.js';\nimport type {\n ConfigSource,\n DoctorUserConfig,\n ResolvedDoctorConfig,\n} from './types.js';\nimport { validateConfig } from './validate.js';\nimport type { Severity } from '../types.js';\n\nconst SOURCE_MAP: Record<string, ConfigSource> = {\n ts: 'ts',\n mjs: 'mjs',\n js: 'js',\n json: 'json',\n};\n\nexport interface LoadDoctorConfigOptions {\n readonly explicitPath?: string;\n readonly presetOverride?: string;\n}\n\nexport async function loadDoctorConfig(\n rootDir: string,\n explicitPathOrOptions?: string | LoadDoctorConfigOptions,\n): Promise<ResolvedDoctorConfig> {\n const opts: LoadDoctorConfigOptions =\n typeof explicitPathOrOptions === 'string'\n ? { explicitPath: explicitPathOrOptions }\n : (explicitPathOrOptions ?? {});\n const explicitPath = opts.explicitPath;\n if (opts.presetOverride !== undefined && !isPresetName(opts.presetOverride)) {\n throw new InvalidConfigError(\n `preset: must be one of 'minimal', 'recommended', 'strict', 'all', got ${JSON.stringify(opts.presetOverride)}`,\n );\n }\n if (explicitPath) {\n const absPath = resolve(rootDir, explicitPath);\n if (!existsSync(absPath)) {\n throw new ConfigFileNotFoundError(explicitPath);\n }\n }\n\n const chain: string[] = [];\n\n const result = await loadConfig<DoctorUserConfig>({\n cwd: rootDir,\n name: 'doctor',\n packageJson: 'doctor',\n rcFile: false,\n globalRc: false,\n ...(explicitPath ? { configFile: resolve(rootDir, explicitPath) } : {}),\n resolve(source, options) {\n const base = options.cwd;\n const key = resolve(base, source);\n if (chain.includes(key)) {\n throw new ConfigCycleError([...chain, key]);\n }\n chain.push(key);\n return undefined;\n },\n });\n\n const raw: DoctorUserConfig = result.config as DoctorUserConfig;\n\n validateConfig(raw);\n\n let source: ConfigSource;\n let configFile: string | undefined;\n\n if (explicitPath) {\n source = 'flag';\n configFile = resolve(rootDir, explicitPath);\n } else if (result._configFile) {\n const ext = extname(result._configFile).slice(1) as keyof typeof SOURCE_MAP;\n source = SOURCE_MAP[ext];\n configFile = result._configFile;\n } else if (Object.keys(raw).length > 0) {\n source = 'package.json';\n configFile = resolve(rootDir, 'package.json');\n } else {\n source = 'built-in';\n configFile = undefined;\n }\n\n // Resolve preset name: CLI override wins, then config file, default 'recommended'.\n const presetName: PresetName = (opts.presetOverride ??\n raw.preset ??\n 'recommended') as PresetName;\n const presetRules = resolvePreset(presetName);\n\n // User config: 'off' explicitly removes the rule from the base preset.\n const userRules: Record<string, Severity> = {};\n const userOff = new Set<string>();\n if (raw.rules && typeof raw.rules === 'object') {\n for (const [key, value] of Object.entries(raw.rules)) {\n if (value === 'off') {\n userOff.add(key);\n } else {\n userRules[key] = value as Severity;\n }\n }\n }\n\n // Merge: preset base -> user rules on top -> user 'off' removes entries.\n const mergedRules: Record<string, Severity> = {\n ...presetRules,\n ...userRules,\n };\n for (const key of userOff) delete mergedRules[key];\n\n return {\n rootDir,\n include: raw.include ?? BUILT_IN_RECOMMENDED.include,\n exclude: raw.exclude ?? BUILT_IN_RECOMMENDED.exclude,\n failOn: raw.failOn ?? BUILT_IN_RECOMMENDED.failOn,\n threshold: raw.threshold ?? BUILT_IN_RECOMMENDED.threshold,\n rules: mergedRules,\n preset: presetName,\n source,\n configFile,\n };\n}\n"],"mappings":"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"}