{"_id":"@dnbhq/lintstaged-config","_rev":"2-3c577a06447a679223458f35a8457744","name":"@dnbhq/lintstaged-config","dist-tags":{"latest":"0.2.1"},"versions":{"0.2.0":{"name":"@dnbhq/lintstaged-config","version":"0.2.0","keywords":["lint-staged","git hook","husky","config","linting"],"author":{"url":"https://github.com/davidsneighbour","name":"Patrick Kollitsch","email":"davidsneighbourdev+gh@gmail.com"},"license":"MIT","_id":"@dnbhq/lintstaged-config@0.2.0","maintainers":[{"name":"davidsneighbour","email":"pkollitsch@gmail.com"}],"homepage":"https://github.com/dnbhq/lintstaged-config#readme","bugs":{"url":"https://github.com/dnbhq/lintstaged-config/issues"},"bin":{"lintstaged-config":"dist/cli.js"},"dist":{"shasum":"a5aa22f19975e6e5dc81f9277bf5335b6ee31265","tarball":"https://registry.npmjs.org/@dnbhq/lintstaged-config/-/lintstaged-config-0.2.0.tgz","fileCount":12,"integrity":"sha512-oVglyoNhDfPhcO04wLYXMwbTSWcVqawxgdoOo5pUz2UGEXkA6iFciDyP1XHE10CG3NJzVqQnsUzkI37GaY6QpQ==","signatures":[{"sig":"MEYCIQCeIwQj/IF3JH72+2FmQps6rFwavDNEjEt+mqDxDhr9tgIhAI4kcH9y0SkrawRf5rySANDGL0GH/pkE8jPr2sO3SHhq","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":37098},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"92448451b1cd406b4199066b33b6271edcba3404","scripts":{"test":"npm run build && vitest run","build":"tsc","clean":"rimraf dist","doctor":"node ./dist/cli.js doctor","format":"prettier --write .","release":"release-it --ci","test:watch":"vitest","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"davidsneighbour","email":"pkollitsch@gmail.com"},"repository":{"url":"git+https://github.com/dnbhq/lintstaged-config.git","type":"git"},"_npmVersion":"12.0.1","description":"Shared, configurable lint-staged task factory for DNBHQ projects.","directories":{},"sideEffects":false,"_nodeVersion":"26.3.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"husky":"9.1.7","rimraf":"6.1.3","vitest":"4.1.8","prettier":"3.8.3","release-it":"20.2.0","typescript":"6.0.3","@types/node":"25.9.2","lint-staged":"17.0.7","@dnbhq/tsconfig":"0.1.6","@dnbhq/release-config":"1.1.0","@release-it/conventional-changelog":"11.0.1"},"peerDependencies":{"lint-staged":">=15.0.0"},"_npmOperationalInternal":{"tmp":"tmp/lintstaged-config_0.2.0_1787002096401_0.13889764021366546","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@dnbhq/lintstaged-config","description":"Shared, configurable lint-staged task factory for DNBHQ projects.","version":"0.2.1","license":"MIT","repository":{"type":"git","url":"git+https://github.com/dnbhq/lintstaged-config.git"},"author":{"name":"Patrick Kollitsch","email":"davidsneighbourdev+gh@gmail.com","url":"https://github.com/davidsneighbour"},"maintainers":[{"name":"davidsneighbour","email":"pkollitsch@gmail.com"}],"bugs":{"url":"https://github.com/dnbhq/lintstaged-config/issues"},"homepage":"https://github.com/dnbhq/lintstaged-config#readme","keywords":["lint-staged","git hook","husky","config","linting"],"peerDependencies":{"lint-staged":">=15.0.0"},"devDependencies":{"@dnbhq/release-config":"1.1.0","@dnbhq/tsconfig":"0.1.6","@release-it/conventional-changelog":"11.0.1","@types/node":"25.9.2","husky":"9.1.7","lint-staged":"17.0.7","prettier":"3.8.3","release-it":"20.2.0","rimraf":"6.1.3","typescript":"6.0.3","vitest":"4.1.8"},"scripts":{"build":"tsc","clean":"rimraf dist","doctor":"node ./dist/cli.js doctor","format":"prettier --write .","prepublishOnly":"npm run clean && npm run build","release":"release-it --ci","test":"npm run build && vitest run","test:watch":"vitest"},"main":"dist/index.js","bin":{"lintstaged-config":"dist/cli.js"},"engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"publishConfig":{"access":"public","provenance":true},"sideEffects":false,"type":"module","types":"dist/index.d.ts","gitHead":"dea03a3e5c3807c174d889b33c4f743f62c5cd2a","_id":"@dnbhq/lintstaged-config@0.2.1","_nodeVersion":"24.19.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-gvgSQsilzd41iqTfSxfDKY+SJEpPpmww+3rM0GNJNOOqM/rX5EISobivUc36TSujjx1GX9sAn5LYdbX5oeHiaA==","shasum":"4c0534aa2623829f8446b624ccdc2a442f0df163","tarball":"https://registry.npmjs.org/@dnbhq/lintstaged-config/-/lintstaged-config-0.2.1.tgz","fileCount":12,"unpackedSize":37215,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@dnbhq%2flintstaged-config@0.2.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFG0WhdYsI4m/114v+x3rXyK5Ox7AkhkQy/qEf+tubHgAiEAx6LgY/YScIOMDLc0leTAE3gWQOH5fQZQQMW/Fjfu4/I="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:741f71f1-7166-401f-ae68-694a5240ef58"}},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/lintstaged-config_0.2.1_1787002207933_0.8330064962011801"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-17T21:28:16.211Z","modified":"2026-08-17T21:30:08.866Z","0.2.0":"2026-08-17T21:28:16.537Z","0.2.1":"2026-08-17T21:30:08.076Z"},"bugs":{"url":"https://github.com/dnbhq/lintstaged-config/issues"},"author":{"name":"Patrick Kollitsch","email":"davidsneighbourdev+gh@gmail.com","url":"https://github.com/davidsneighbour"},"license":"MIT","homepage":"https://github.com/dnbhq/lintstaged-config#readme","keywords":["lint-staged","git hook","husky","config","linting"],"repository":{"type":"git","url":"git+https://github.com/dnbhq/lintstaged-config.git"},"description":"Shared, configurable lint-staged task factory for DNBHQ projects.","maintainers":[{"name":"davidsneighbour","email":"pkollitsch@gmail.com"}],"readme":"# @dnbhq/lintstaged-config\n\n> Shared, configurable [lint-staged](https://github.com/lint-staged/lint-staged) task factory for DNBHQ projects\n\n[![Version][npm-image]][npm-url] [![PR Workflow][github-workflows-pr-image]][github-workflows-pr-url]\n\n- [Why this package](#why-this-package)\n- [Installation](#installation)\n- [Quick start (config file)](#quick-start-config-file)\n- [Quick start (package.json)](#quick-start-packagejson)\n- [Default tasks](#default-tasks)\n- [Enabling an opt-in task](#enabling-an-opt-in-task)\n- [Disabling a default task](#disabling-a-default-task)\n- [Overriding a task's glob or commands](#overriding-a-tasks-glob-or-commands)\n- [Adding entries with no built-in task](#adding-entries-with-no-built-in-task)\n- [Per-task configuration options](#per-task-configuration-options)\n- [Checking that the required tools are installed](#checking-that-the-required-tools-are-installed)\n- [Testing your lint-staged setup](#testing-your-lint-staged-setup)\n- [TypeScript config file support](#typescript-config-file-support)\n- [Release](#release)\n- [Notes](#notes)\n\n## Why this package\n\nlint-staged configuration is a flat object: a glob pattern mapped to one or more commands. There is no `extends` field like Biome or markdownlint-cli2 have, so sharing a baseline across repositories means either copy-pasting the object everywhere, or wrapping it in a small factory function that can be imported and customised. This package is that factory.\n\nIt **does not** install the linters it wires up. Most of the underlying tools (`secretlint`, `@biomejs/biome`, `markdownlint-cli2`, `stylelint`) are npm packages you add as dev dependencies yourself, so their versions stay under your project's control (the same reasoning [`@dnbhq/tsconfig`](https://github.com/dnbhq/tsconfig) uses for TypeScript, and [`@dnbhq/biome-config`](https://github.com/dnbhq/biome-config) for Biome). A few others (`yamllint`, `jsonnetfmt`) are not npm packages at all — they're Python/Go binaries — so this package could not install them even if it wanted to. See [Checking that the required tools are installed](#checking-that-the-required-tools-are-installed) for the `doctor` command that checks for them instead.\n\n## Installation\n\n```bash\nnpm install --save-dev @dnbhq/lintstaged-config lint-staged\n```\n\nThen add whichever of the default tools you plan to use (see [Default tasks](#default-tasks)) as dev dependencies, for example:\n\n```bash\nnpm install --save-dev secretlint @biomejs/biome markdownlint-cli2\n```\n\n## Quick start (config file)\n\nCreate `lint-staged.config.ts` in the repository root:\n\n```ts\nimport { createLintStagedConfig } from '@dnbhq/lintstaged-config';\nimport type { Configuration } from 'lint-staged';\n\nconst config: Configuration = createLintStagedConfig();\n\nexport default config;\n```\n\nWire it into a git hook with [Husky](https://github.com/typicode/husky):\n\n```bash\nnpx husky init\necho \"npx lint-staged\" > .husky/pre-commit\n```\n\nRunning `.ts` config files directly requires Node.js 22.6+ with `--experimental-strip-types`, or Node.js 23.6+ where it is unflagged — see [TypeScript config file support](#typescript-config-file-support) if your Node version is older.\n\n## Quick start (package.json)\n\nlint-staged's `package.json` field only accepts a plain JSON object — it cannot `require()` or `import` a function, so `createLintStagedConfig()` cannot be referenced from it directly. Two options if a standalone config file is not what you want:\n\n**Pipe the resolved default config into lint-staged via stdin.** lint-staged reads JSON config from stdin when given `--config -`, and this package's CLI prints its default configuration as single-line JSON on stdout:\n\n```json\n{\n  \"scripts\": {\n    \"lint-staged\": \"lintstaged-config print | lint-staged --config -\"\n  }\n}\n```\n\nThis only covers the built-in defaults with no customisation — see the caveat in [Per-task configuration options](#per-task-configuration-options). Anything beyond flipping which tasks are enabled needs the config-file approach above.\n\n**Or copy the printed JSON once** into the `lint-staged` key of `package.json` and edit it by hand from there:\n\n```bash\nnpx lintstaged-config print\n```\n\nThis gives you a static starting point rather than a live extension of the shared config — you will not pick up future changes to the defaults until you re-run and re-paste. Prefer the config-file approach in [Quick start (config file)](#quick-start-config-file) whenever you expect the defaults to evolve.\n\n## Default tasks\n\n`createLintStagedConfig()` builds its result from eight named tasks. Three are enabled out of the box because they only depend on npm-installable tools; the rest are opt-in.\n\n| Task | Enabled by default | Glob | Command(s) | Requires |\n| --- | --- | --- | --- | --- |\n| `secrets` | Yes | `*` | `secretlint --no-glob` | `secretlint` |\n| `javascript` | Yes | `*.{js,cjs,mjs,jsx,ts,tsx,cts,mts,json,jsonc}` | `biome check --write --no-errors-on-unmatched` | `@biomejs/biome` |\n| `markdown` | Yes | `!(CHANGELOG)**/*.{md,markdown,mdx}` | `markdownlint-cli2 --fix` | `markdownlint-cli2` |\n| `styles` | No | `*.{css,scss}` | `stylelint --fix` | `stylelint` |\n| `yaml` | No | `*.{yaml,yml}` | `yamllint` | `yamllint` (not npm) |\n| `images` | No | `*.{png,jpeg,jpg,gif,svg}` | `sharp-lint-staged` | `@dnbhq/sharp-lint-staged` |\n| `astro` | No | `*.astro` | `npx astro check --minimumFailingSeverity=error --minimumSeverity=error` | `astro` |\n| `jsonnet` | No | `*.jsonnet` | `jsonnetfmt --in-place` | `jsonnetfmt` (not npm) |\n\n`styles`, `yaml`, `images`, and `astro` are opt-in because they only apply to some projects. `yaml` and `jsonnet` are opt-in for an additional reason: `yamllint` and `jsonnetfmt` are not npm packages, so pulling them in as a default would fail silently for anyone without them on `PATH`.\n\n## Enabling an opt-in task\n\nSet `enabled: true` on the task:\n\n```ts\nimport { createLintStagedConfig } from '@dnbhq/lintstaged-config';\nimport type { Configuration } from 'lint-staged';\n\nconst config: Configuration = createLintStagedConfig({\n  styles: { enabled: true },\n  images: { enabled: true },\n});\n\nexport default config;\n```\n\n## Disabling a default task\n\nSet `enabled: false` on the task. This removes its glob entry entirely, rather than replacing it with an empty command list:\n\n```ts\nimport { createLintStagedConfig } from '@dnbhq/lintstaged-config';\nimport type { Configuration } from 'lint-staged';\n\nconst config: Configuration = createLintStagedConfig({\n  markdown: { enabled: false },\n});\n\nexport default config;\n```\n\n## Overriding a task's glob or commands\n\nEach task accepts `glob` and `commands` independently, so you can keep the default glob and swap the command, keep the command and swap the glob, or replace both:\n\n```ts\nimport { createLintStagedConfig } from '@dnbhq/lintstaged-config';\nimport type { Configuration } from 'lint-staged';\n\nconst config: Configuration = createLintStagedConfig({\n  javascript: {\n    // Keep the default glob, run an extra Biome pass.\n    commands: ['biome check --write --no-errors-on-unmatched', 'biome lint --write --no-errors-on-unmatched'],\n  },\n  markdown: {\n    // Keep the default command, narrow the glob to docs/ only.\n    glob: 'docs/**/*.md',\n  },\n});\n\nexport default config;\n```\n\n## Adding entries with no built-in task\n\nPass `overrides` for anything this package does not model as a task — a completely custom glob, or a function-based entry like [lint-staged's advanced JS config](https://github.com/lint-staged/lint-staged#using-js-configuration-files) supports. `overrides` is applied last with a shallow `{ ...generated, ...overrides }` spread:\n\n```ts\nimport { createLintStagedConfig } from '@dnbhq/lintstaged-config';\nimport type { Configuration } from 'lint-staged';\n\nconst config: Configuration = createLintStagedConfig({\n  overrides: {\n    '.vscode/settings.json': () => 'node scripts/vscode/merge-vscode-config.ts --audit',\n    '*.jsonnet': ['jsonnetfmt --in-place', 'git add'],\n  },\n});\n\nexport default config;\n```\n\nBecause the spread happens last, an `overrides` key that reuses a default task's exact glob string fully replaces that task's commands — this is an alternative to the `javascript: { commands: [...] }` form above when you would rather not touch the task options at all. The two approaches produce the same result; `overrides` is there for entries a task option cannot express (functions, or globs no task owns).\n\n## Per-task configuration options\n\n`secrets`, `markdown`, `styles`, and `yaml` accept a `configPath` (and `secrets` also an `ignorePath`) that gets appended as the tool's own config flag, instead of relying on the tool's automatic config discovery:\n\n```ts\nimport { createLintStagedConfig } from '@dnbhq/lintstaged-config';\nimport type { Configuration } from 'lint-staged';\n\nconst config: Configuration = createLintStagedConfig({\n  secrets: {\n    configPath: 'config/.secretlintrc.json',\n    ignorePath: 'config/.secretlintignore',\n  },\n  markdown: {\n    configPath: 'config/.markdownlint-cli2.jsonc',\n  },\n  yaml: {\n    enabled: true,\n    configPath: 'config/yamllint.yaml',\n  },\n});\n\nexport default config;\n```\n\nSetting `configPath` and setting `commands` together is redundant: `configPath` only has an effect on the command the task builds internally, so if you also set `commands` your explicit command list wins and `configPath` is ignored.\n\n## Checking that the required tools are installed\n\nSince this package does not install any of the underlying linters, it ships a `doctor` command that checks whether they are reachable — on `PATH`, or in the project's local `node_modules/.bin` — without running them:\n\n```bash\nnpx lintstaged-config doctor\n```\n\n```text\n✔ secretlint (secrets)\n✖ markdownlint-cli2 (markdown)\n  npm install --save-dev markdownlint-cli2 (or @dnbhq/markdownlint-config)\n✔ biome (javascript)\n...\n```\n\nRestrict the check to specific tasks by passing their names:\n\n```bash\nnpx lintstaged-config doctor javascript markdown\n```\n\n`doctor` exits with code `1` when any checked tool is missing, so it is safe to wire into CI as a setup-verification step:\n\n```json\n{\n  \"scripts\": {\n    \"postinstall\": \"lintstaged-config doctor secrets javascript markdown || true\"\n  }\n}\n```\n\n(the `|| true` keeps a missing optional tool from failing `npm install`; drop it if you want a hard failure instead).\n\n## Testing your lint-staged setup\n\n\"Testing\" a lint-staged config means two different things, and this package supports both:\n\n1. **Testing this package itself** — if you are contributing to `@dnbhq/lintstaged-config`, `npm test` builds the package and runs its vitest suite (`tests/*.spec.ts`), which covers task enabling/disabling, glob/command overrides, the `overrides` merge, the `doctor` tool-detection logic, and the `print` CLI output.\n\n2. **Testing a consuming project's `lint-staged.config.ts`** — do this with lint-staged's own dry-run tooling rather than a unit test:\n\n   ```bash\n   # Stage a file that should be picked up, then run lint-staged without\n   # letting it touch your working tree:\n   git add some-file.ts\n   npx lint-staged --debug --no-stash\n   ```\n\n   `--debug` prints which glob matched which staged files and which commands ran, and `--no-stash` skips lint-staged's usual git stash so you can inspect the result directly. Revert or re-stage afterwards as needed.\n\n   To sanity-check the resolved configuration itself without running any tools, print it and read the JSON:\n\n   ```bash\n   npx lintstaged-config print\n   # or, for a customised config file:\n   node --experimental-strip-types -e \"import('./lint-staged.config.ts').then(m => console.log(JSON.stringify(m.default, null, 2)))\"\n   ```\n\n## TypeScript config file support\n\nlint-staged loads `.ts` config files by handing them to Node's own type-stripping, not by bundling `ts-node`/`jiti`/`tsx`. That means:\n\n* Node.js 22.6+ requires the file to be run with `NODE_OPTIONS=--experimental-strip-types` (or `node --experimental-strip-types` when invoking `lint-staged` directly).\n* Node.js 23.6+ has this unflagged, so `lint-staged.config.ts` works with no extra setup.\n* The config file itself must use only [erasable TypeScript syntax](https://nodejs.org/api/typescript.html#type-stripping) — type annotations, interfaces, and `import type` are fine (that's all the examples in this README use), but `enum`, namespaces with runtime values, and parameter properties are not, because Node only strips types, it does not transpile.\n\nIf your project's Node version does not support this yet, use a `.mjs` config file instead and keep type-checking via a JSDoc annotation, per [lint-staged's own TypeScript documentation](https://github.com/lint-staged/lint-staged#typescript):\n\n```js\n/**\n * @type {import('lint-staged').Configuration}\n */\nimport { createLintStagedConfig } from '@dnbhq/lintstaged-config';\n\nexport default createLintStagedConfig();\n```\n\n## Release\n\nDry run:\n\n```bash\nnpm run release:dry\n```\n\nRelease:\n\n```bash\nnpm run release\n```\n\nReleases are handled by `release-it`, configured through [`@dnbhq/release-config`](https://github.com/dnbhq/release-config), with changelog generation via `@release-it/conventional-changelog`. Commit messages should follow Conventional Commits. Publishing is handled by the `Publish` GitHub Actions workflow when a `v*.*.*` tag is pushed.\n\n## Notes\n\n* This package has no runtime dependencies. `lint-staged` is a peer dependency; the individual linters (`secretlint`, `@biomejs/biome`, `markdownlint-cli2`, `stylelint`, ...) are dependencies of *your* project, not of this package, so their versions stay under your control.\n* `TASK_NAMES`, `TOOL_REQUIREMENTS`, `checkTools`, and `isToolAvailable` are also exported from the package root for anyone scripting around `doctor` instead of shelling out to the CLI.\n* See [`examples/`](examples/) for complete config files covering the default setup, enabling optional tasks, and combining disables with overrides.\n\n[npm-image]: https://img.shields.io/npm/v/@dnbhq/lintstaged-config.svg?style=flat-square\n[npm-url]: https://www.npmjs.org/package/@dnbhq/lintstaged-config\n[github-workflows-pr-image]: https://github.com/dnbhq/lintstaged-config/actions/workflows/pr.yml/badge.svg\n[github-workflows-pr-url]: https://github.com/dnbhq/lintstaged-config/actions/workflows/pr.yml\n","readmeFilename":"README.md"}