diff --git a/.changeset/config-type-export.md b/.changeset/config-type-export.md new file mode 100644 index 0000000..a536507 --- /dev/null +++ b/.changeset/config-type-export.md @@ -0,0 +1,5 @@ +--- +"@docker-doctor/cli": patch +--- + +Export the `DockerDoctorConfig` type (plus `RuleCategory`) and a `defineConfig` helper for typed `docker-doctor.config.ts` files, and fix the config type's `categories` field to accept a subset of categories (`Partial>`) — matching the validator's actual behavior and the documented examples. diff --git a/apps/web/content/docs/reference/configuration.mdx b/apps/web/content/docs/reference/configuration.mdx index d5e9b7c..47cc7fd 100644 --- a/apps/web/content/docs/reference/configuration.mdx +++ b/apps/web/content/docs/reference/configuration.mdx @@ -45,4 +45,36 @@ export default { The five valid `categories` keys are `Security`, `Performance`, `Best Practices`, `Compose`, and `Image Size`. +## Typed config + +`@docker-doctor/cli` exports the `DockerDoctorConfig` type and a `defineConfig` helper. Which one to use depends on how you run docker-doctor: + +**Running via `npx` without installing** — use the type with `satisfies`. Type-only imports are erased when the config loads, so nothing needs to be installed at runtime (install `@docker-doctor/cli` as a devDependency if you want editor autocomplete): + +```ts +// docker-doctor.config.ts +import type { DockerDoctorConfig } from "@docker-doctor/cli"; + +export default { + rules: { + "docker-doctor/no-root-user": "error", + }, +} satisfies DockerDoctorConfig; +``` + +**`@docker-doctor/cli` installed as a devDependency** — `defineConfig` also works and gives the same autocomplete: + +```ts +// docker-doctor.config.ts +import { defineConfig } from "@docker-doctor/cli"; + +export default defineConfig({ + categories: { + "Image Size": "off", + }, +}); +``` + +`defineConfig` is a real runtime import — a config that uses it fails to load when the package isn't installed in the project, so prefer the `satisfies` form for npx-only projects. + Unknown top-level keys and unknown category names are silently dropped rather than rejected, matching the legacy config schema's behavior. Invalid severities throw a config error with the offending rule/category name. diff --git a/packages/core/src/config/define-config.ts b/packages/core/src/config/define-config.ts new file mode 100644 index 0000000..ffd1cdc --- /dev/null +++ b/packages/core/src/config/define-config.ts @@ -0,0 +1,7 @@ +import type { DockerDoctorConfig } from "../types/index"; + +// Identity helper for typed docker-doctor.config.ts files. Unlike a type-only +// import of DockerDoctorConfig, a config using this must be able to resolve +// @docker-doctor/cli at load time. +export const defineConfig = (config: DockerDoctorConfig): DockerDoctorConfig => + config; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 3c9df72..4f178b7 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -9,6 +9,7 @@ export { allComposeRules, findRule, } from "./rules/index"; +export { defineConfig } from "./config/define-config"; export { loadConfig } from "./config/loader"; export { calculateScore, getScoreBucket, SCORE_BUCKETS } from "./scoring"; export * from "./errors"; diff --git a/packages/core/src/schemas/config.ts b/packages/core/src/schemas/config.ts index 8c48c31..68a9f93 100644 --- a/packages/core/src/schemas/config.ts +++ b/packages/core/src/schemas/config.ts @@ -48,7 +48,7 @@ const validateRules = (value: unknown): Record => { const validateCategories = ( value: unknown -): Record => { +): Partial> => { if (!isPlainObject(value)) { throw new Error( `Invalid config: "categories" must be an object, got ${typeof value}` @@ -70,7 +70,7 @@ const validateCategories = ( result[key as RuleCategory] = severity; } - return result as Record; + return result; }; const validateIgnore = (value: unknown): { files?: string[] } => { diff --git a/packages/core/src/types/index.ts b/packages/core/src/types/index.ts index eec85e7..0b53181 100644 --- a/packages/core/src/types/index.ts +++ b/packages/core/src/types/index.ts @@ -58,7 +58,7 @@ export interface ComposeRule extends RuleDefinition { export interface DockerDoctorConfig { rules?: Record; - categories?: Record; + categories?: Partial>; ignore?: { files?: string[]; }; diff --git a/packages/docker-doctor/README.md b/packages/docker-doctor/README.md index e9e2ff2..0d2e2d1 100644 --- a/packages/docker-doctor/README.md +++ b/packages/docker-doctor/README.md @@ -50,15 +50,19 @@ npx @docker-doctor/cli@latest ### 4. Configure -```js +```ts // docker-doctor.config.ts +import type { DockerDoctorConfig } from "@docker-doctor/cli"; + export default { rules: { "docker-doctor/no-root-user": "error", }, -}; +} satisfies DockerDoctorConfig; ``` +A `defineConfig` helper is also exported for projects with `@docker-doctor/cli` installed — see the [configuration docs](https://docker-doctor.vercel.app/docs/reference/configuration). + ## How the score works Every scan produces a 0-100 health score alongside a label (`Excellent 🏆`, `Good ✅`, `Needs Work ⚠️`, `Critical 🚨`). diff --git a/packages/docker-doctor/src/index.ts b/packages/docker-doctor/src/index.ts index 71ab767..3aebf51 100644 --- a/packages/docker-doctor/src/index.ts +++ b/packages/docker-doctor/src/index.ts @@ -8,10 +8,16 @@ export { runDockerfileRules, runComposeRules, calculateScore, + defineConfig, loadConfig, allRules, findRule, toJsonReport, } from "@docker-doctor/core"; -export type { Diagnostic, RuleSeverity } from "@docker-doctor/core"; +export type { + Diagnostic, + DockerDoctorConfig, + RuleCategory, + RuleSeverity, +} from "@docker-doctor/core"; export const { version } = packageJson;