[
  {
    "path": ".agents/MODULE_GRAPH.md",
    "content": "# Module Graph\n\nConcise walk-through from CLI to reporter output.\n\nCore module graph and AST traversal.\n\n## Implementation walk-through\n\nThe sequence from [CLI][1]:\n\n1. [Create options][2]\n2. [Run][3]\n   1. Normalize user config\n   2. Get workspaces\n   3. [Build module graph][4]\n      1. [Run enabled plugins][5] in each workspace\n      2. Store entry points and referenced dependencies\n      3. [Create TS programs][6]\n      4. [Get imports and exports][7] using TS AST traversal/visitors\n      5. [Get dependencies/binaries from scripts][8]\n   4. [Analyze module graph][9]\n      1. Find [unused exports][10] (respecting [namespaces & members][11])\n      2. Settle unused files\n      3. [Settle unused/unlisted dependencies][12]\n      4. Settle unused catalog entries\n3. [Run default reporter][13]\n\n[1]: ./packages/knip/src/cli.ts\n[2]: ./packages/knip/src/util/create-options.ts\n[3]: ./packages/knip/src/run.ts\n[4]: ./packages/knip/src/graph/build.ts\n[5]: ./packages/knip/src/WorkspaceWorker.ts\n[6]: ./packages/knip/src/ProjectPrincipal.ts\n[7]: ./packages/knip/src/typescript/get-imports-and-exports.ts\n[8]: ./packages/knip/src/binaries/bash-parser.ts\n[9]: ./packages/knip/src/graph/analyze.ts\n[10]: ./packages/knip/src/graph-explorer/operations/is-referenced.ts\n[11]:\n  ./packages/knip/src/graph-explorer/operations/has-strictly-ns-references.ts\n[12]: ./packages/knip/src/DependencyDeputy.ts\n[13]: ./packages/knip/src/reporters/symbols.ts\n"
  },
  {
    "path": ".agents/PLUGINS.md",
    "content": "# Plugins\n\n## General\n\nRead [Writing A Plugin][1] first to understand:\n\n- Plugin responsibilities\n- Functions like `resolveConfig` and `Input` type definition\n- Consider `resolveFromAST` only for custom plugin-specific needs (core takes\n  care of module resolution, imports, exports, external dependencies)\n\n## Creating a new plugin\n\nTo create a new plugin for a certain package/tool/framework:\n\n- Come up with a kebab-cased `name`.\n- Run `pnpm create-plugin --name [name]` from the `packages/knip` directory.\n- Update the plugin's `types.ts`: add only relevant types, remove if unused.\n- Consult similar plugins and the tool's website before implementation\n- Update and fill out the blanks in the generated files.\n- Remove unused variables and empty arrays from the template\n- Don't forget: [run tests][2] individually first.\n\n[1]: ../packages/docs/src/content/docs/writing-a-plugin/index.md\n[2]: ../AGENTS.md#test\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Convert all files to use \"\\n\" line endings.\n* text eol=lf\n\n# Specify the file type for some binary files to prevent Git from changing the line endings upon\n# cloning the repository.\n*.gif binary\n*.ico binary\n*.jpeg binary\n*.jpg binary\n*.png binary\n*.otf binary\n*.mov binary\n*.mp3 binary\n*.mp4 binary\n*.webm binary\n*.webp binary\n"
  },
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "content": "# Code of Conduct\n\n- **Be kind & respectful**: Treat everyone with kindness, empathy, and respect. No personal attacks, offensive language,\n  or discrimination.\n- **Collaborate**: Share your ideas, help others, and work together. We're all here to learn and grow!\n- **Communicate clearly**: Be open and honest, but also considerate. Keep your messages clear and concise.\n- **Stay on topic**: Keep discussions relevant to the project. Off-topic conversations can happen elsewhere.\n- **Be patient**: We all have different skill levels and backgrounds. Give people time to respond and learn.\n- **Report issues**: If you see any violations of this Code of Conduct, please report them to the [project\n  maintainer][1].\n\nThis document is a summary of [the Contributor Covenant Code Of Conduct][2].\n\nBy participating in this project, you agree to follow this Code of Conduct.\n\n[1]: https://github.com/webpro\n[2]: https://www.contributor-covenant.org/version/2/1/code_of_conduct/\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "# Contributing\n\nThank you for investing your time in contributing to Knip!\n\nRead our [Code of Conduct][1] to keep our community approachable and\nrespectable.\n\n## Types of contributions\n\n- Star the project to show your support\n- Share [Knip][2] in social media or blog posts\n- [Open an issue][3]\n- [Open a pull request][4] to:\n  - Fix a bug\n  - Add a new feature\n  - Add a new plugin\n  - Improve output or add a new reporter\n  - Improve documentation\n  - Improve performance or architecture\n\nThe main goal of Knip is to keep projects clean & tidy. Everything that\ncontributes to that goal is welcome!\n\n## Open an issue\n\nFeel free to open an issue for things like this:\n\n- Knip throws an error.\n- Knip shows incorrect output, e.g. it reports false negatives.\n- Documentation is incorrect or something is missing.\n- Questions about configuration or how to run Knip.\n- Discuss whether to start a pull request.\n- Request a new feature.\n\nBefore you open an issue:\n\n- Make sure you're using the **latest** version of Knip (see\n  [https://github.com/webpro-nl/knip/releases][5])\n- Use GitHub search to find related questions, known issues, etc.\n- If relevant, use the `--debug` flag which might reveal errors, potential\n  issues with configuration, etc.\n\n## Open a pull request\n\nPull requests are welcome!\n\nPlease consider this before opening a pull request:\n\n- Open an issue before starting the work if for any reason you're not 100% sure\n  about it. We can optimize both your and our time if we discuss ideas and\n  choose a direction upfront.\n- Pull requests may not align with the general philosophy, or may not be\n  affordable to maintain, and therefore rejected.\n- Generated (\"vibe-coded\") pull requests may be rejected without explanation.\n- Read [development][6] instructions and guidelines.\n- If you think your PR is not ready for review yet, [set it as a draft][7].\n- No need to worry about commit messages, they will probably be squashed into a\n  single commit when merged.\n- After your first PR is merged, you are automatically added to the [list of\n  contributors][8].\n\n[1]: ./CODE_OF_CONDUCT.md\n[2]: https://knip.dev\n[3]: #open-an-issue\n[4]: #open-a-pull-request\n[5]: https://github.com/webpro-nl/knip/releases\n[6]: ./DEVELOPMENT.md\n[7]:\n  https://docs.github.com/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request\n[8]: https://knip.dev/#created-by-awesome-contributors\n"
  },
  {
    "path": ".github/DEVELOPMENT.md",
    "content": "# Development\n\nDevelopment in this repository is using:\n\n- pnpm\n- TypeScript\n- Biome\n\nThis document describes commands and tasks that might help during development.\nUse what fits your workflow best, but make sure [QA][1] passes.\n\n> [!TIP]\n>\n> tl;dr The quickest way to get started: `git clone`, `pnpm install`, find a\n> relevant test file in `packages/knip/test`, and [hit F5 in VS Code or\n> WebStorm][2].\n\n## Contents\n\n- [Getting started][3]\n- [Agents][4]\n- [Contributing a plugin?][5]\n- [Running Knip][6]\n- [Tests][7]\n- [QA][1]\n- [GitHub Action][8]\n\n## Getting started\n\nThis guide assumes familiarity with concepts like [forking][9], [cloning a\nrepo][10] and working with a package manager.\n\n- Fork the project using the [GitHub website][11] or the [`gh` CLI][12]\n- Clone the repository\n- Install dependencies\n\nExample terminal commands on your machine to get started:\n\n```shell\ngit clone git@github.com:[username]/knip.git\n# Or using gh CLI: gh repo fork webpro-nl/knip --clone\ncd knip\npnpm install\ncd packages/knip\npnpm build\npnpm test\n```\n\nTo skip slower tests related to CLI and `--fix`, while still covering all the\nessentials and plugins:\n\n```shell\npnpm test:smoke\nbun test:bun:smoke\n```\n\n## Agents\n\nUsing coding agents cq AI-powered tooling? Inform it about [AGENTS.md][13]. Take\nresponsibility and make sure to not cause unnecessary review and \"wall of text\"\noverhead to maintainers. Also [consider this before opening a pull request][14].\n\n## Contributing a plugin?\n\nIn addition to the generic guidelines in this document, there's a guide for\n[writing a plugin][15].\n\n## Running Knip\n\nKnip is written in TypeScript, and there are a few options to run it including\nyour changes:\n\n- [Compile][16] ahead of time to JavaScript to run in Node.js\n- [Without compilation][17]\n  - Transpile on the fly using e.g `tsx` to run in Node.js\n  - Use a runtime that supports TypeScript (i.e. Bun)\n\n### Compile\n\nUse `pnpm build` to compile using `tsc` once. To recompile on changes:\n\n```shell\npnpm watch\n```\n\nOn source code changes, `tsc` will compile to JavaScript, and the `knip`\nexecutable is available globally to run from any directory.\n\n### Without compilation\n\nRun Knip without compilation:\n\n```shell\nnode path/to/knip/packages/knip/src/cli.ts\n```\n\n#### Alias\n\nExpanding on this idea, set up an alias like so:\n\n```shell\nalias k=\"node --inspect ~/p/knip/packages/knip/src/cli.ts\"\n```\n\nInvoke `k` to run Knip including any local changes. And if it's in the built-in\nterminal, it will stop at breakpoints. For the rest of this document, `knip` or\n`node --inspect` can be replaced with `k`.\n\n## Tests\n\nMost pull requests should probably include one or more tests.\n\nAssuming you've created `test/feature.test.ts` and `fixtures/feature` (the\nplugin create command does for you), here's a few ideas to run and debug Knip\nfrom a test.\n\nCreating a new plugin? The [plugin guide][18] has a command to set up a test\nwith fixtures for you.\n\n### Run single test file\n\n```shell\nnode --test test/my-feature.test.ts\nbun test test/plugins/my-plugin.test.ts\n```\n\n### Run Knip in the directory\n\n```shell\nknip --directory fixtures/feature\n```\n\n### Attach debugger to Node.js\n\nTo debug Knip in an IDE (e.g. [VS Code][19] or [WebStorm][20]), open the\nbuilt-in terminal and allow the debugger to connect:\n\n```shell\ncd fixtures/feature\nnode --inspect ../../src/cli.ts\n```\n\nMake sure VS Code is set up to attach to the Node.js process (\"Always\" or \"With\nflag\").\n\n### Attach debugger from inside a test file\n\nRun configurations for VS Code and WebStorm² are set up in the repo. This a\ngreat way to debug almost anything in Knip.\n\n- Using Node.js\n  - From any test file, run the \"Debug test with tsx/Node.js\" launch config\n- Using Bun\n  - VS Code: ensure the [Bun extension][21] is enabled\n  - WebStorm: ensure the [Bun plugin][22] is enabled\n  - From any test file, run the \"Debug test with Bun\" launch config\n\nFrom now on, just set a breakpoint and hit `F5` (Code) or `ctrl-r` (WS) from any\ntest file to run and debug.\n\n² Requires at least WebStorm 2025.2 EAP\n\n### Attach debugger to tests\n\nIn case you're wondering if or why some code is ever hit, attach the debugger to\neach test. Set a breakpoint and run all tests in one of the following ways:\n\n- From built-in terminal: `tsx --inspect --test test/**/*.test.ts`\n- Use the \"Debug all tests with Bun\" launch config.\n\n## QA\n\nKnip has a few tools set up to verify code quality and to format code and\ndocumentation:\n\n```shell\npnpm format\npnpm lint\npnpm knip\npnpm knip --strict\npnpm test\n```\n\n## GitHub Action\n\nThe [ci.yml][23] workflow runs the tests across Bun, recent Node.js versions,\nUbuntu, macOS and Windows. QA in CI must be all green before a pull request can\nbe merged. The [integration.yml][24] workflow runs Knip in multiple repositories\nusing Knip, against the latest version of the code.\n\n## Previews\n\nThanks to [pkg.pr.new][25] pull requests can be previewed by installing it as a\nregular package. Every push is published to their registry. Look for the\n`pkg-pr-new` bot in your pull request.\n\n[1]: #qa\n[2]: #attach-debugger-to-bun-from-a-test\n[3]: #getting-started\n[4]: #agents\n[5]: #contributing-a-plugin\n[6]: #running-knip\n[7]: #tests\n[8]: #github-action\n[9]: https://docs.github.com/get-started/quickstart/fork-a-repo\n[10]:\n  https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository\n[11]: https://github.com/webpro-nl/knip\n[12]: https://cli.github.com/\n[13]: ../AGENTS.md\n[14]: ./CONTRIBUTING.md#open-a-pull-request\n[15]: https://knip.dev/guides/writing-a-plugin/\n[16]: #compile\n[17]: #without-compilation\n[18]: https://knip.dev/guides/writing-a-plugin#create-a-new-plugin\n[19]: https://code.visualstudio.com/docs/nodejs/nodejs-debugging\n[20]: https://www.jetbrains.com/help/webstorm/running-and-debugging-node-js.html\n[21]: https://marketplace.visualstudio.com/items?itemName=oven.bun-vscode\n[22]: https://www.jetbrains.com/help/webstorm/bun.html#bun_before_you_start\n[23]: https://github.com/webpro-nl/knip/actions/workflows/ci.yml\n[24]: https://github.com/webpro-nl/knip/actions/workflows/integration.yml\n[25]: https://pkg.pr.new\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: webpro\nopen_collective: knip\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/01-bug_report.yaml",
    "content": "name: 🐛 Bug report\ndescription: Create a report to help us improve\ntitle: '🐛 '\nlabels: ['bug']\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Use common sense and provide the necessary information that helps us to help you.\n\n        A minimal reproduction is mandatory: only the code and configuration required to demonstrate the issue.\n\n        Issues that do not meet these requirements may be closed without further investigation.\n  - type: checkboxes\n    attributes:\n      label: Prerequisites\n      description: Please check existing information about your issue\n      options:\n        - label: I'm using the latest version\n          required: true\n        - label: I've read the relevant [documentation](https://knip.dev)\n          required: true\n        - label: I've searched for [existing issues](https://github.com/webpro-nl/knip/issues?q=is%3Aissue)\n          required: true\n        - label: I've checked the [list of known issues](https://knip.dev/reference/known-issues)\n          required: true\n        - label: I've read the [issue reproduction guide](https://knip.dev/guides/issue-reproduction)\n          required: true\n  - type: input\n    id: reproduction\n    attributes:\n      label: Reproduction url\n      description: Please link to the location of the issue reproduction\n    validations:\n      required: true\n  - type: checkboxes\n    attributes:\n      label: Reproduction access\n      description: For instance, a CodeSandbox seems private by default\n      options:\n        - label: I've made sure the reproduction is publicly accessible\n          required: true\n  - type: textarea\n    id: description\n    attributes:\n      label: Description of the issue\n      description: What happened, and what would you expect to happen?\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/02-regression.yaml",
    "content": "name: 🔄 Regression\ndescription: Something that worked a certain way before, but no longer does\ntitle: '🔄 '\nlabels: ['regression']\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Please make sure you have read the docs and are using the latest version of Knip.\n        Use common sense and provide the necessary information that helps me or others to help you.\n\n        A minimal reproduction is mandatory: only the code and configuration required to demonstrate the issue.\n\n        Issues that do not meet these requirements may be closed without further investigation.\n  - type: checkboxes\n    attributes:\n      label: Prerequisites\n      description: Please check existing information about your issue\n      options:\n        - label: I've read the relevant [documentation](https://knip.dev)\n          required: true\n        - label: I've searched for [existing\n            issues](https://github.com/webpro-nl/knip/issues?q=is%3Aissue)\n          required: true\n        - label: I've read the [issue reproduction\n            guide](https://knip.dev/guides/issue-reproduction)\n          required: true\n  - type: input\n    id: reproduction\n    attributes:\n      label: Reproduction url\n      description: Please link to the location of the issue reproduction\n    validations:\n      required: true\n  - type: checkboxes\n    attributes:\n      label: Reproduction access\n      description: For instance, a CodeSandbox seems private by default\n      options:\n        - label: I've made sure the reproduction is publicly accessible\n          required: true\n  - type: input\n    id: good-version\n    attributes:\n      label: Good version\n      description: What's the latest version it worked as expected?\n    validations:\n      required: true\n  - type: input\n    id: bad-version\n    attributes:\n      label: Bad version\n      description: What's the first version it no longer works as expected?\n    validations:\n      required: true\n  - type: textarea\n    id: description\n    attributes:\n      label: Description of the regression\n      description: What happened before, what happens now?\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/03-feature_request.yaml",
    "content": "name: 💡 Feature Request (RFC)\ndescription: Suggest an idea for Knip\ntitle: '💡 '\nlabels: ['feature request']\nbody:\n  - type: markdown\n    attributes:\n      value: Use common sense and provide the necessary information to explain why\n        the feature is useful to you and to others. Thanks!\n  - type: textarea\n    id: description\n    attributes:\n      label: Suggest an idea for Knip\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/04-documentation.yaml",
    "content": "name: 📘 Documentation\ndescription: An idea for or issue with the documentation website (knip.dev)\ntitle: '📘 '\nlabels: ['documentation']\nbody:\n  - type: markdown\n    attributes:\n      value: An idea for or issue with the documentation website\n  - type: input\n    id: link\n    attributes:\n      label: Link\n      description: If possible or relevant, please link to the location\n  - type: textarea\n    id: description\n    attributes:\n      label: Description of the issue\n      description: Please describe the issue\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/05-everything-else.yaml",
    "content": "name: 🧩 Everything Else\ndescription: Discuss anything related to Knip\ntitle: '🧩 '\nlabels: ['discussion']\nbody:\n  - type: markdown\n    attributes:\n      value: Feel free to discuss something related to Knip that does not fit the other categories.\n  - type: textarea\n    id: description\n    attributes:\n      label: Discuss anything related to Knip\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!--\n\n- Try to author code and/or docs similar to the rest of the repository\n- See [DEVELOPMENT.md][1] for development and QA guidelines\n\nThrough [the CI workflow][2] the changes will be tested in Ubuntu, macOS and Windows.\nThe changes will also be [tested against a number of external projects][3].\n\n[1]: https://github.com/webpro-nl/knip/blob/main/.github/DEVELOPMENT.md\n[2]: https://github.com/webpro-nl/knip/blob/main/.github/workflows/ci.yml\n[3]: https://github.com/webpro-nl/knip/blob/main/.github/workflows/integration.yml\n\n-->\n"
  },
  {
    "path": ".github/copilot-instructions.md",
    "content": "See [AGENTS.md][1].\n\n[1]: ../AGENTS.md\n"
  },
  {
    "path": ".github/workflows/ci-bun.yml",
    "content": "name: Tests (Bun)\n\non:\n  workflow_dispatch:\n  pull_request:\n  push:\n    branches:\n      - '**'\n    tags:\n      - '!**'\n\njobs:\n  test:\n    strategy:\n      matrix:\n        os:\n          - macos-latest\n          - ubuntu-latest\n          - windows-latest\n    runs-on: ${{ matrix.os }}\n    name: ${{ matrix.os }}\n    steps:\n      - uses: actions/checkout@v6\n      - uses: oven-sh/setup-bun@v2\n        with:\n          bun-version: 1.2.22\n      - name: Install dependencies\n        run: bun install --ignore-scripts\n      - name: Run linter and formatter\n        run: bun ci\n      - name: Build knip\n        run: bun run build\n        working-directory: packages/knip\n      - name: Run knip\n        run: ./packages/knip/bin/knip-bun.js\n      - name: Run knip (production/strict)\n        run: ./packages/knip/bin/knip-bun.js --production --strict\n      - name: Test knip\n        run: bun run test:bun\n        working-directory: packages/knip\n"
  },
  {
    "path": ".github/workflows/ci-ts-latest.yml",
    "content": "name: Tests (against typescript@latest in Node.js v22)\n\non:\n  workflow_dispatch:\n  pull_request:\n  push:\n    branches:\n      - '**'\n    tags:\n      - '!**'\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    name: Ubuntu/Node v22\n    steps:\n      - uses: actions/checkout@v6\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 22\n      - uses: pnpm/action-setup@v5\n      - name: Install dependencies\n        run: pnpm install\n      - name: Install latest peer dependencies\n        run: pnpm add typescript@latest @types/node@22\n        working-directory: packages/knip\n      - name: Build knip\n        run: pnpm run build\n        working-directory: packages/knip\n      - name: Test knip\n        run: pnpm run test:node\n        working-directory: packages/knip\n      - name: Run knip\n        run: ./packages/knip/bin/knip.js\n      - name: Run knip in strict mode\n        run: ./packages/knip/bin/knip.js --production --strict\n"
  },
  {
    "path": ".github/workflows/ci-ts-next.yml",
    "content": "name: Tests (against typescript@next in Node.js v24)\n\non:\n  workflow_dispatch:\n  pull_request:\n  push:\n    branches:\n      - '**'\n    tags:\n      - '!**'\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    name: Ubuntu/Node v24\n    steps:\n      - uses: actions/checkout@v6\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 24\n      - uses: pnpm/action-setup@v5\n      - name: Install dependencies\n        run: pnpm install\n      - name: Install latest peer dependencies\n        run: pnpm add typescript@next @types/node@24\n        working-directory: packages/knip\n      - name: Build knip\n        run: pnpm run build\n        working-directory: packages/knip\n      - name: Test knip\n        run: pnpm run test:node\n        working-directory: packages/knip\n      - name: Run knip\n        run: ./packages/knip/bin/knip.js\n      - name: Run knip in strict mode\n        run: ./packages/knip/bin/knip.js --production --strict\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: Tests (against typescript@5.0.4 in Node.js v20)\n\non:\n  workflow_dispatch:\n  pull_request:\n  push:\n    branches:\n      - '**'\n    tags:\n      - '!**'\n\njobs:\n  test:\n    strategy:\n      matrix:\n        os:\n          - macos-latest\n          - ubuntu-latest\n          - windows-latest\n        node:\n          - 20\n          - 22\n\n    runs-on: ${{ matrix.os }}\n    name: ${{ matrix.os }} (Node v${{ matrix.node }})\n\n    steps:\n      - uses: actions/checkout@v6\n      - uses: actions/setup-node@v6\n        with:\n          node-version: ${{ matrix.node }}\n      - uses: pnpm/action-setup@v5\n      - name: Install dependencies\n        run: pnpm install\n      - name: Build knip\n        run: pnpm run build\n        working-directory: packages/knip\n      - name: Install earliest supported peer dependencies\n        run: pnpm add typescript@5.0.4 @types/node@20\n        working-directory: packages/knip\n      - name: Type-check against TS 5.0.4\n        run: |\n          node -e \"let f=require('fs'),p='tsconfig.json';f.writeFileSync(p,f.readFileSync(p,'utf8').replace(/.*(?:rewriteRelativeImportExtensions|erasableSyntaxOnly).*\\n/g,''))\"\n          pnpm tsc --noEmit\n        working-directory: packages/knip\n      - name: Test knip\n        run: pnpm run test:smoke\n        working-directory: packages/knip\n      - name: Run knip\n        run: ./packages/knip/bin/knip.js --reporter github-actions\n      - name: Run knip in strict mode\n        run: ./packages/knip/bin/knip.js --production --strict\n"
  },
  {
    "path": ".github/workflows/integration.yml",
    "content": "name: Publish preview & run ecosystem tests\n\non:\n  workflow_dispatch:\n  pull_request:\n  push:\n    branches:\n      - '**'\n    tags:\n      - '!**'\n\npermissions:\n  issues: write\n\njobs:\n  publish:\n    name: Build and publish Knip\n    runs-on: ubuntu-latest\n    outputs:\n      sha: ${{ steps.publish.outputs.sha }}\n    steps:\n      - uses: actions/checkout@v6\n      - uses: pnpm/action-setup@v5\n      - run: pnpm install --frozen-lockfile\n        working-directory: packages/knip\n      - run: pnpm run build\n        working-directory: packages/knip\n      - id: publish\n        run: |\n          pnpx pkg-pr-new publish --compact './packages/knip' './packages/language-server' './packages/mcp-server'\n      - name: Comment on referenced issues\n        if: github.event_name == 'push'\n        uses: actions/github-script@v8\n        with:\n          script: |\n            const sha = '${{ steps.publish.outputs.sha }}';\n            const commits = context.payload.commits || [];\n            const messages = commits.map(commit => commit.message).join('\\n');\n            const issueRefs = messages.match(/#(\\d+)/g) || [];\n            const issues = new Set(issueRefs.map(ref => parseInt(ref.slice(1))));\n            const isClosingMatch = /(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\\s+#(\\d+)/gi;\n            const closingIssues = new Set([...messages.matchAll(isClosingMatch)].map(m => parseInt(m[1])));\n            for (const issue_number of issues) {\n              try {\n                const { owner, repo } = context.repo;\n                const { data } = await github.rest.issues.get({ owner, repo, issue_number });\n                if (data.state === 'open' || closingIssues.has(issue_number)) {\n                  const body = `Preview release available:\\n\\`\\`\\`sh\\nnpm i -D https://pkg.pr.new/knip@${sha}\\n\\`\\`\\``;\n                  await github.rest.issues.createComment({ owner, repo, issue_number, body });\n                }\n              } catch(error) {\n               console.log(`Could not comment on #${issue_number}: ${error.message}`);}\n            }\n\n  integration:\n    name: Run Knip in ${{ matrix.project.name }}\n    needs: publish\n    runs-on: ubuntu-latest\n    env:\n      PKG_URL: https://pkg.pr.new/knip@${{ needs.publish.outputs.sha }}\n    strategy:\n      fail-fast: false\n      matrix:\n        project:\n          - name: 10ten-ja-reader\n            repo: birchill/10ten-ja-reader\n            commands: |\n              pnpm install\n              pnpm dlx $PKG_URL\n\n          - name: argos\n            repo: argos-ci/argos\n            commands: |\n              pnpm install\n              pnpm build\n              pnpm add -D -w $PKG_URL\n              pnpm run knip\n\n          - name: astro\n            repo: withastro/astro\n            commands: |\n              pnpm install\n              pnpm build\n              pnpm add -D -w $PKG_URL\n\n          - name: create-typescript-app\n            repo: JoshuaKGoldberg/create-typescript-app\n            commands: |\n              pnpm install\n              pnpm add -D $PKG_URL\n              pnpm lint:knip\n\n          - name: DefinitelyTyped-tools\n            repo: microsoft/DefinitelyTyped-tools\n            commands: |\n              pnpm install\n              pnpm dlx $PKG_URL\n\n          - name: eslint\n            repo: eslint/eslint\n            commands: |\n              npm install\n              npm install --prefix docs\n              npm install -D $PKG_URL\n              npm run lint:unused -- --cache\n              npm run lint:unused -- --cache\n\n          - name: mocha\n            repo: mochajs/mocha\n            commands: |\n              npm ci\n              npm install -D $PKG_URL\n              npm run lint:knip\n\n          - name: InvokeAI\n            repo: invoke-ai/InvokeAI\n            sparse-checkout: invokeai/frontend/web\n            commands: |\n              cd invokeai/frontend/web\n              pnpm install\n              pnpm add -D $PKG_URL\n              bunx --bun knip --tags=-knipignore\n              bunx --bun knip --tags=-knipignore --production --fix --allow-remove-files --format\n              bunx --bun knip --tags=-knipignore --production\n\n          - name: npmx.dev\n            repo: npmx-dev/npmx.dev\n            commands: |\n              pnpm install\n              pnpm add -D -w $PKG_URL\n              pnpm knip\n              pnpm knip --production --exclude dependencies\n\n          - name: prettier\n            repo: prettier/prettier\n            commands: |\n              yarn\n              yarn --cwd scripts/release\n              yarn --cwd scripts/tools/bundle-test\n              yarn --cwd scripts/tools/eslint-plugin-prettier-internal-rules\n              yarn --cwd website\n              yarn add -D knip@$PKG_URL\n              yarn knip\n\n          - name: query\n            repo: TanStack/query\n            commands: |\n              pnpm install\n              pnpm add -D -w $PKG_URL\n              pnpm test:knip --cache\n              pnpm test:knip --cache\n\n          - name: rolldown\n            repo: rolldown/rolldown\n            commands: |\n              pnpm install\n              pnpm add -D -w knip@$PKG_URL\n              pnpm knip\n\n          - name: sentry\n            repo: getsentry/sentry\n            commands: |\n              pnpm install\n              pnpm add -D $PKG_URL\n              pnpm run knip\n              pnpm run knip:prod\n\n          - name: slonik\n            repo: gajus/slonik\n            commands: |\n              pnpm install --no-frozen-lockfile\n              pnpm dlx $PKG_URL\n\n          - name: TypeScript\n            repo: microsoft/TypeScript\n            commands: |\n              npm ci\n              npm install -D $PKG_URL\n              npm run knip\n\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: Check out ${{ matrix.project.repo }}\n        uses: actions/checkout@v6\n        with:\n          repository: ${{ matrix.project.repo }}\n          path: ${{ matrix.project.name }}\n          sparse-checkout: ${{ matrix.project.sparse-checkout }}\n\n      - name: Apply patch if exists\n        working-directory: ${{ matrix.project.name }}\n        run: |\n          PATCH_FILE=\"${{ github.workspace }}/.github/workflows/patches/${{ matrix.project.name }}.patch\"\n          [ -f \"$PATCH_FILE\" ] && git apply --verbose \"$PATCH_FILE\" || true\n\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 24\n\n      - uses: oven-sh/setup-bun@v2\n\n      - uses: pnpm/action-setup@v5\n\n      - name: Run Knip in ${{ matrix.project.repo }}\n        working-directory: ${{ matrix.project.name }}\n        run: |\n          set -x\n          ${{ matrix.project.commands }}\n"
  },
  {
    "path": ".github/workflows/markdown-link-check.json",
    "content": "{\n  \"aliveStatusCodes\": [0, 200, 429],\n  \"ignorePatterns\": [\n    { \"generated\": \"packages/docs/src/content/docs/reference/plugins/*.md\" }\n  ]\n}\n"
  },
  {
    "path": ".github/workflows/markdown-link-check.yml",
    "content": "name: Check Markdown links\n\non:\n  push:\n    branches:\n      - '**'\n    tags:\n      - '!**'\n\njobs:\n  markdown-link-check:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@main\n\n      - name: Check Markdown links\n        uses: gaurav-nelson/github-action-markdown-link-check@v1\n        with:\n          config-file: .github/workflows/markdown-link-check.json\n          use-quiet-mode: 'yes'\n          use-verbose-mode: 'yes'\n"
  },
  {
    "path": ".github/workflows/patches/.gitkeep",
    "content": ""
  },
  {
    "path": ".github/workflows/patches/TypeScript.patch",
    "content": "diff --git a/knip.jsonc b/knip.jsonc\nindex f471ada9b2..83844be8aa 100644\n--- a/knip.jsonc\n+++ b/knip.jsonc\n@@ -32,6 +32,7 @@\n     \"ignoreExportsUsedInFile\": {\n         \"enum\": true,\n         \"interface\": true,\n+        \"namespace\": true,\n         \"type\": true\n     },\n     \"mocha\": false\n"
  },
  {
    "path": ".github/workflows/patches/argos.patch",
    "content": "diff --git a/knip.json b/knip.json\n--- a/knip.json\n+++ b/knip.json\n@@ -11,7 +11,8 @@\n         \"src/graphql/definitions/*.ts\",\n         \"scripts/*.ts\"\n       ],\n-      \"ignore\": [\"**/bin/**/*\"]\n+      \"ignore\": [\"**/bin/**/*\"],\n+      \"ignoreDependencies\": [\"@argos/knex-scripts\", \"moment\"]\n     },\n     \"apps/frontend\": {\n       \"ignore\": [\"vite.config.mts\"],\n"
  },
  {
    "path": ".github/workflows/patches/create-typescript-app.patch",
    "content": "diff --git a/knip.json b/knip.json\nindex 8bbe72d..23921d7 100644\n--- a/knip.json\n+++ b/knip.json\n@@ -1,6 +1,6 @@\n {\n \t\"$schema\": \"https://unpkg.com/knip@5.71.0/schema.json\",\n-\t\"entry\": [\"src/**/*.test.*\", \"src/index.ts\"],\n+\t\"entry\": [\"src/**/*.test.*\"],\n \t\"ignoreDependencies\": [\n \t\t\"all-contributors-cli\",\n \t\t\"cspell-populate-words\",\n"
  },
  {
    "path": ".github/workflows/patches/npmx.dev.patch",
    "content": "diff --git a/knip.ts b/knip.ts\nindex f885994f..27d68897 100644\n--- a/knip.ts\n+++ b/knip.ts\n@@ -4,18 +4,6 @@ const config: KnipConfig = {\n   workspaces: {\n     '.': {\n       entry: [\n-        'app/router.options.ts!',\n-        'app/app.vue!',\n-        'app/error.vue!',\n-        'app/pages/**/*.vue!',\n-        'app/components/**/*.vue!',\n-        'app/components/**/*.d.vue.ts!',\n-        'app/composables/**/*.ts!',\n-        'app/middleware/**/*.ts!',\n-        'app/plugins/**/*.ts!',\n-        'app/utils/**/*.ts!',\n-        'server/**/*.ts!',\n-        'modules/**/*.ts!',\n         'config/**/*.ts!',\n         'lunaria/**/*.ts!',\n         'shared/**/*.ts!',\n@@ -24,11 +12,12 @@ const config: KnipConfig = {\n         'pwa-assets.config.ts',\n         '.lighthouserc.cjs',\n         'lighthouse-setup.cjs',\n-        'uno-preset-rtl.ts!',\n+        'uno-preset-*.ts!',\n         'scripts/**/*.ts',\n       ],\n       project: [\n-        '**/*.{ts,vue,cjs,mjs}',\n+        '**/*.{ts,vue,cjs,mjs}!',\n+        '!test/**!',\n         '!test/fixtures/**',\n         '!test/test-utils/**',\n         '!test/e2e/helpers/**',\n@@ -48,31 +37,39 @@ const config: KnipConfig = {\n         /** Some components import types from here, but installing it directly could lead to a version mismatch */\n         'vue-router',\n\n-        /** Required by @nuxtjs/i18n at runtime but not directly imported in production code */\n-        '@intlify/shared',\n-\n         /** Oxlint plugins don't get picked up yet */\n         '@e18e/eslint-plugin',\n         'eslint-plugin-regexp',\n\n         /** Used in test/e2e/helpers/ which is excluded from knip project scope */\n         'h3-next',\n+\n+        /** Not referenced in production source code (either pending/inactive, or effectively devDependencies) */\n+        '@deno/doc!',\n+        '@floating-ui/vue!',\n+        '@intlify/shared!',\n+        '@shikijs/langs!',\n+        '@shikijs/themes!',\n+        '@upstash/redis!',\n+        '@vueuse/integrations!',\n+        'algoliasearch!',\n+        'focus-trap!',\n+        'ipaddr.js!',\n+        'marked!',\n+        'sanitize-html!',\n+        'semver!',\n+        'shiki!',\n+        'virtua!',\n+        'vue-data-ui!',\n       ],\n-      ignoreUnresolved: ['#components', '#oauth/config'],\n+      ignoreUnresolved: ['#oauth/config'],\n     },\n     'cli': {\n       project: ['src/**/*.ts!', '!src/mock-*.ts'],\n     },\n     'docs': {\n       entry: ['app/**/*.{ts,vue,css}'],\n-      ignoreDependencies: [\n-        'docus',\n-        'better-sqlite3',\n-        '@nuxtjs/mdc',\n-        'nuxt!',\n-        '@nuxt/ui',\n-        'tailwindcss',\n-      ],\n+      ignoreDependencies: ['docus', 'better-sqlite3', '@nuxtjs/mdc', 'nuxt!'],\n     },\n   },\n }\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\n/.idea/*\n!/.idea/runConfigurations\n.DS_Store\n.npmrc\npackages/*/dist\n"
  },
  {
    "path": ".idea/runConfigurations/Debug_Bun_test.xml",
    "content": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"Debug Bun test\" type=\"BunRunConfiguration\">\n    <option name=\"program\" value=\"$FilePath$\" />\n    <option name=\"runtimeParameters\">\n      <list>\n        <option value=\"test\" />\n      </list>\n    </option>\n    <option name=\"workingDirectory\" value=\"$PROJECT_DIR$/packages/knip\" />\n    <method v=\"2\" />\n  </configuration>\n</component>\n"
  },
  {
    "path": ".idea/runConfigurations/Debug_Node_test.xml",
    "content": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"Debug Node test\" type=\"NodeJSConfigurationType\" node-parameters=\"--test --import tsx --import ./transform-test.js\" path-to-js-file=\"$FilePath$\" working-dir=\"$PROJECT_DIR$/packages/knip\">\n    <method v=\"2\" />\n  </configuration>\n</component>"
  },
  {
    "path": ".oxfmtrc.json",
    "content": "{\n  \"printWidth\": 120,\n  \"singleQuote\": true,\n  \"arrowParens\": \"avoid\",\n  \"trailingComma\": \"es5\",\n  \"overrides\": [\n    {\n      \"files\": [\"**/*.json\", \"**/*.jsonc\"],\n      \"options\": { \"printWidth\": 80 }\n    },\n    {\n      \"files\": [\"*.css\"],\n      \"options\": { \"singleQuote\": false }\n    },\n    {\n      \"files\": [\"packages/knip/test/util/get-inputs-from-scripts.test.ts\"],\n      \"options\": { \"printWidth\": 200 }\n    }\n  ]\n}\n"
  },
  {
    "path": ".oxlintrc.json",
    "content": "{\n  \"$schema\": \"./node_modules/oxlint/configuration_schema.json\",\n  \"plugins\": [\"typescript\", \"import\"],\n  \"categories\": {\n    \"correctness\": \"error\"\n  },\n  \"rules\": {\n    \"no-unused-vars\": [\n      \"error\",\n      {\n        \"argsIgnorePattern\": \"^_\",\n        \"caughtErrorsIgnorePattern\": \"^_\",\n        \"destructuredArrayIgnorePattern\": \"^_\"\n      }\n    ],\n    \"no-unused-expressions\": [\n      \"error\",\n      { \"allowShortCircuit\": true, \"allowTernary\": true }\n    ],\n    \"no-console\": \"error\",\n    \"@typescript-eslint/no-explicit-any\": \"off\",\n    \"no-param-reassign\": \"off\",\n    \"dot-notation\": \"off\",\n    \"array-callback-return\": \"off\"\n  },\n  \"ignorePatterns\": [\n    \"**/dist\",\n    \"**/tmp\",\n    \"**/vendor\",\n    \"packages/docs/.astro\",\n    \".vscode\",\n    \"templates\"\n  ],\n  \"overrides\": [\n    {\n      \"files\": [\"**/*.astro\"],\n      \"rules\": { \"no-unused-vars\": \"off\" }\n    },\n    {\n      \"files\": [\"packages/knip/**\"],\n      \"rules\": {\n        \"no-restricted-imports\": [\n          \"error\",\n          {\n            \"paths\": [\n              {\n                \"name\": \"node:path\",\n                \"message\": \"Please use src/util/path.js instead.\"\n              },\n              {\n                \"name\": \"path\",\n                \"message\": \"Please use src/util/path.js instead.\"\n              },\n              {\n                \"name\": \"node:assert\",\n                \"message\": \"Please use node:assert/strict instead.\"\n              }\n            ]\n          }\n        ],\n        \"import/extensions\": [\"error\", \"always\", { \"ignorePackages\": true }]\n      }\n    },\n    {\n      \"files\": [\"packages/docs/**\", \"packages/create-config/**\"],\n      \"rules\": { \"no-restricted-imports\": \"off\", \"no-console\": \"off\" }\n    },\n    {\n      \"files\": [\"packages/language-server/**\"],\n      \"rules\": { \"no-console\": \"off\" }\n    },\n    {\n      \"files\": [\n        \"packages/knip/src/reporters/**\",\n        \"packages/knip/scripts/**\",\n        \"packages/vscode-knip/scripts/**\",\n        \"packages/vscode-knip/test/**\"\n      ],\n      \"rules\": { \"no-console\": \"off\" }\n    },\n    {\n      \"files\": [\"packages/knip/fixtures/**\"],\n      \"rules\": {\n        \"no-unused-expressions\": \"off\",\n        \"no-unused-vars\": \"off\",\n        \"no-console\": \"off\",\n        \"no-duplicate-enum-values\": \"off\",\n        \"no-restricted-imports\": \"off\",\n        \"triple-slash-reference\": \"off\",\n        \"import/namespace\": \"off\",\n        \"import/extensions\": \"off\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": ".prettierignore",
    "content": "**/dist\n**/tmp\n**/fixtures\n**/*.md\n**/*.mdx\ntemplates/language-server-client\npackages/docs/.astro\n"
  },
  {
    "path": ".release-it.json",
    "content": "{\n  \"$schema\": \"https://unpkg.com/release-it@19/schema/release-it.json\",\n  \"git\": {\n    \"commitMessage\": \"Release ${npm.name}@${version}\",\n    \"tagName\": \"${npm.name}@${version}\"\n  },\n  \"npm\": {\n    \"publishPackageManager\": \"pnpm\",\n    \"publishArgs\": [\"--no-git-checks\"]\n  }\n}\n"
  },
  {
    "path": ".vscode/extensions.json",
    "content": "{\n  \"recommendations\": [\n    \"oxc.oxc-vscode\",\n    \"remcohaszing.vscode-mdxlint\",\n    \"unifiedjs.vscode-remark\"\n  ]\n}\n"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"type\": \"node\",\n      \"request\": \"launch\",\n      \"name\": \"Debug test with Node.js\",\n      \"runtimeArgs\": [\"--test\"],\n      \"args\": [\"${file}\"],\n      \"cwd\": \"${workspaceFolder}/packages/knip\",\n      \"console\": \"integratedTerminal\"\n    },\n    {\n      \"type\": \"bun\",\n      \"request\": \"launch\",\n      \"name\": \"Debug test with Bun\",\n      \"program\": \"${file}\",\n      \"cwd\": \"${workspaceFolder}/packages/knip\",\n      \"runtime\": \"bun\",\n      \"runtimeArgs\": [\"test\", \"--timeout\", \"999999999\"],\n      \"internalConsoleOptions\": \"openOnSessionStart\"\n    },\n    {\n      \"type\": \"bun\",\n      \"request\": \"launch\",\n      \"name\": \"Debug all tests with Bun\",\n      \"program\": \".\",\n      \"cwd\": \"${workspaceFolder}/packages/knip\",\n      \"runtime\": \"bun\",\n      \"runtimeArgs\": [\"test\", \"--timeout\", \"999999999\"],\n      \"internalConsoleOptions\": \"openOnSessionStart\"\n    },\n    {\n      \"name\": \"Run Extension on Playground Monorepo (w/ MCP Server)\",\n      \"type\": \"extensionHost\",\n      \"request\": \"launch\",\n      \"runtimeExecutable\": \"${execPath}\",\n      \"args\": [\n        \"--extensionDevelopmentPath=${workspaceFolder}/packages/vscode-knip\",\n        \"${workspaceFolder}/templates/language-server-client\" // Point at any repo\n      ],\n      \"outFiles\": [\n        \"${workspaceFolder}/packages/vscode-knip/**/*.js\",\n        \"${workspaceFolder}/packages/language-server/**/*.js\",\n        \"${workspaceFolder}/packages/mcp-server/**/*.js\"\n      ]\n    },\n    {\n      \"name\": \"Attach to Knip Server\",\n      \"type\": \"node\",\n      \"request\": \"attach\",\n      \"port\": 6009,\n      \"restart\": true,\n      \"outFiles\": [\n        \"${workspaceFolder}/packages/language-server/**/*.js\",\n        \"${workspaceFolder}/packages/knip/dist/**/*.js\",\n        \"${workspaceFolder}/packages/knip/src/**/*.ts\"\n      ],\n      \"sourceMaps\": true,\n      \"resolveSourceMapLocations\": [\n        \"${workspaceFolder}/**\",\n        \"!**/node_modules/**\"\n      ]\n    }\n  ],\n  \"compounds\": [\n    {\n      \"name\": \"Run + Attach Knip LSP\",\n      \"configurations\": [\n        \"Run Extension on Playground Monorepo (w/ MCP Server)\",\n        \"Attach to Knip Server\"\n      ]\n    }\n  ],\n  \"inputs\": [\n    {\n      \"id\": \"projectPath\",\n      \"type\": \"promptString\",\n      \"description\": \"Enter absolute path to y${lineNumber}our project dir\",\n      \"default\": \"${workspaceFolder}/templates/language-server-client\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"[javascript]\": {\n    \"editor.defaultFormatter\": \"oxc.oxc-vscode\"\n  },\n  \"[json]\": {\n    \"editor.defaultFormatter\": \"oxc.oxc-vscode\"\n  },\n  \"[jsonc]\": {\n    \"editor.defaultFormatter\": \"oxc.oxc-vscode\"\n  },\n  \"[markdown]\": {\n    \"editor.defaultFormatter\": \"unifiedjs.vscode-remark\"\n  },\n  \"[mdx]\": {\n    \"editor.defaultFormatter\": \"remcohaszing.vscode-mdxlint\"\n  },\n  \"[typescript]\": {\n    \"editor.defaultFormatter\": \"oxc.oxc-vscode\"\n  },\n  \"[typescriptreact]\": {\n    \"editor.defaultFormatter\": \"oxc.oxc-vscode\"\n  },\n  \"oxc.fmt.configPath\": \".oxfmtrc.json\",\n  \"cSpell.enabled\": true,\n  \"cSpell.words\": [\n    \"astro\",\n    \"codebase\",\n    \"codegen\",\n    \"codeowners\",\n    \"commitlint\",\n    \"devkit\",\n    \"execa\",\n    \"jiti\",\n    \"knip\",\n    \"lefthook\",\n    \"preprocess\",\n    \"preprocessor\",\n    \"stylelint\",\n    \"timerify\",\n    \"tsup\",\n    \"unimported\",\n    \"webp\"\n  ],\n  \"editor.formatOnSave\": true\n}\n"
  },
  {
    "path": "AGENTS.md",
    "content": "# Knip\n\nKnip is a tool to find and fix unused dependencies, exports and files in\nJavaScript and TypeScript projects.\n\n## Context: project overview\n\n- Monorepo\n- Main package is core in `packages/knip` (TypeScript)\n- Language Server in `packages/language-server` (JS + JSDoc for types)\n- VS Code Extension in `packages/vscode-knip` (JS + JSDoc for types)\n- [Documentation][1] content in `packages/docs` (Astro + MD/MDX)\n\n## Principles\n\n- Ask yourself: \"Would a staff engineer approve this?\" — maintain high standards\n- If something goes sideways, stop and re-plan immediately - don't keep pushing\n- For non-trivial changes: pause and ask \"is there a more elegant way?\"\n- If a fix feels hacky: \"Knowing everything I know now, implement the elegant solution\" — but skip this for simple, obvious fixes (i.e. don't over-engineer)\n- Challenge your own work before presenting it\n- Your training data is stale — verify packages, APIs, and syntax against current docs\n- If you say \"I will do X\", actually do X — don't just announce intentions\n- Don't blindly follow instructions: question the user if the request would not result in something better or faster\n\n## Communication\n\n- Zero context switching required from the user\n- When reporting information to the user, be extremely concise and sacrifice\n  grammar for the sake of concision\n- Don't add comments to code, unless explicitly asked for.\n\n## Planning\n\n1. Plan: write plan to `.agents/tasks/todo-(name).md` with checkable items\n2. Get alignment: check in with user before starting implementation, question any doubts or noise\n3. Track progress: mark items complete as you go\n4. Explain changes: high-level summary at each step\n5. Document results: add review section to `.agents/tasks/todo-(name).md`\n6. Capture lessons: update `.agents/lessons.md` after corrections\n\n- Use subagents liberally to keep main context window clean\n- Offload research, exploration, and parallel analysis to subagents\n\n## Workflow\n\n- Read broadly before editing — understand surrounding code, not just the target\n- Commit chunks of verified work — don't let unrelated changes accumulate\n- Make small, testable, incremental changes — not big-bang edits\n- Avoid manual edits and regex-based refactors, prefer AST-based tools and codemods (jscodeshift) — except for tiny edits\n- Reflect on outcomes between steps — don't blindly chain actions\n- Diff behavior between main and your changes when relevant\n\n## Code style\n\n- Performance is key, both high level (design) and low level (impl).\n- Avoid redundant code and abstractions.\n- Avoid unnecessary complexity and nesting.\n- Concise one-liners are fine, but prioritize clarity over cleverness.\n- JavaScript\n  - Prefer plain `for..in/of` loops over iterator methods like `map`/`reduce`.\n- TypeScript\n  - Avoid `any` and type casting (`as`)\n  - Avoid runtime overhead just to get the types right\n\n## Verification\n\n- Insufficient testing is the #1 failure mode — test rigorously, not hopefully\n- State verification method _before_ implementing (test, CLI output, linter, screenshot)\n- Prefer TDD for new features — write or update tests before implementing\n- For UI or integration changes: screenshots or CLI output as evidence\n- Tailor to the domain: run a bash command, check a web page, use a linter — whatever is most direct\n- Every completed task must answer: \"How was this verified?\"\n- Document verification steps in `.agents/tasks/todo-(name).md`\n- Maintain \"known pitfalls\" in `.agents/lessons.md` — check it before starting, update it after corrections\n\n## Issues and Pull Requests\n\n- When given a bug report, first confirm the behavior is actually wrong. Reproduce, then check if the reported behavior is correct-by-design before writing any fix\n- Find repositories/CodeSandbox/StackBlitz source files and local fixtures to actually reproduce the issue at hand\n- To fetch stackblitz.com reproduction url: `pnpx stackblitz-zip https://stackblitz.com/edit/{name} {filename}.zip`\n\n## Domain Knowledge\n\n- Unused file → unused exports/dependencies is a chain, not a bug\n- Use `--performance` or `--performance-fn [name]` to profile (→ [timerify][2])\n- If creating or modifying a plugin, read [PLUGINS.md][3] first.\n- If modifying core module graph, AST traversal, or CLI sequence, read [MODULE_GRAPH.md][4] first.\n\n## Environment\n\n- Before using `sed`, `awk`, etc. — verify GNU or POSIX-compatible tools are installed (gnu-sed, coreutils)\n- Always look at root lockfile and package.json to choose between npm/npx, pnpm/pnpx, etc.\n\n## Run & Debug\n\nImportant: debug, don't guess.\n\nRun the CLI directly using `node` or `bun`. The rest of this document shows\n`knip` in commands for consistency. Replace it with `node (path/to/)src/cli.ts`\nor `bun (path/to/)src/cli.ts` and keep using what works.\n\n- Run `knip` directly in a fixture or temp directory (over creating test scripts\n  that import the `main` function).\n- Knip requires `package.json` in root dir.\n- Enable [debug & helpers][5] with `--debug` (not `DEBUG=`). Warning: noisy.\n- Use [trace][6] to debug\n  - exported identifiers (`knip --trace-export [name] --trace-file [file]`)\n  - external dependencies (`knip --trace-dependency [name] --workspace [dir]`)\n\n## Test\n\nPrefer `bun` over `node` for speed. Don't run all tests at once (slow & noisy).\nStart out with running the relevant test(s) first:\n\n```sh\ncd packages/knip\nbun test test/util/get-inputs-from-scripts.test.ts\nnode --test test/commonjs.test.ts\n```\n\nTo run all relevant tests without having to build `knip`:\n\n```sh\ncd packages/knip\npnpm run test:bun:smoke\n```\n\nUse `node` if Bun is not available:\n\n```sh\ncd packages/knip\nnode --test test/commonjs.test.ts\npnpm test:smoke\n```\n\nBuild core package and run all tests only if there are changes in auto-fix,\nformatting and reporter related functionality:\n\n```sh\ncd packages/knip\npnpm build\npnpm test\n```\n\n## Fixtures\n\nThere are plenty of directories with fixtures in `packages/knip/fixtures`.\n\n- In general, tests have their own fixture directory.\n- Plugin fixture directories at `packages/knip/fixtures/plugins/[plugin-name]*`.\n- For trivial changes or fixes, extend an existing fixture.\n- Don't use \"foo\" or vague names. One fixture should consist of descriptive file\n  and variable names like `module.ts` and `barrel.ts`, or build upon a \"theme\"\n  such as fruits or animals to indicate relation/hierarchy.\n- Use empty files if sufficient (e.g. to verify import specifier or entry file).\n- For debugging, it might be useful to run Knip from the fixture directory and\n  see output in terminal. Example:\n\n```sh\ncd packages/knip/fixtures/commonjs\nknip\n```\n\n## Build\n\nTo type-check `knip` with `tsc`:\n\n```sh\ncd packages/knip\npnpm build\n```\n\n[1]: https://knip.dev\n[2]: ./packages/knip/src/util/Performance.ts\n[3]: ./.agents/PLUGINS.md\n[4]: ./.agents/MODULE_GRAPH.md\n[5]: ./packages/knip/src/util/debug.ts\n[6]: ./packages/docs/src/content/docs/guides/troubleshooting.md#trace\n"
  },
  {
    "path": "knip.json",
    "content": "{\n  \"$schema\": \"https://unpkg.com/knip@6/schema.json\",\n  \"workspaces\": {\n    \".\": {\n      \"project\": [\"!templates/**\"]\n    },\n    \"packages/knip\": {\n      \"project\": [\n        \"src/**/*.ts!\",\n        \"test/**/*.ts\",\n        \"!src/util/empty.ts\",\n        \"!**/_template/**\"\n      ],\n      \"ignoreDependencies\": [\"prettier\"]\n    },\n    \"packages/docs\": {\n      \"entry\": [\"{remark,scripts}/*.ts\"]\n    },\n    \"packages/vscode-knip\": {\n      \"entry\": [\"src/index.js!\", \"scripts/*.js\", \"test/*.mjs\"],\n      \"ignoreDependencies\": [\"vscode!\"]\n    }\n  }\n}\n"
  },
  {
    "path": "license",
    "content": "ISC License (ISC)\n\nCopyright 2022-2025 Lars Kappert\n\nPermission to use, copy, modify, and/or distribute this software for any purpose\nwith or without fee is hereby granted, provided that the above copyright notice\nand this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND\nFITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS\nOF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER\nTORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\nTHIS SOFTWARE.\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"@knip/root\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"description\": \"Find unused dependencies, exports and files in your TypeScript and JavaScript projects\",\n  \"homepage\": \"https://knip.dev\",\n  \"bugs\": \"https://github.com/webpro-nl/knip/issues\",\n  \"license\": \"ISC\",\n  \"author\": {\n    \"name\": \"Lars Kappert\",\n    \"email\": \"lars@webpro.nl\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/webpro-nl/knip\"\n  },\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"type\": \"module\",\n  \"scripts\": {\n    \"watch\": \"pnpm run --dir packages/knip watch\",\n    \"docs\": \"pnpm run --dir packages/docs dev\",\n    \"test\": \"pnpm run --dir packages/knip test\",\n    \"format\": \"oxfmt\",\n    \"lint\": \"oxlint\",\n    \"ci\": \"oxlint && oxfmt --check && installed-check --ignore-dev\",\n    \"release\": \"./release.sh\"\n  },\n  \"devDependencies\": {\n    \"@release-it/bumper\": \"^7.0.5\",\n    \"installed-check\": \"^9.3.0\",\n    \"mdxlint\": \"^1.0.0\",\n    \"mdxlint-preset-webpro\": \"^1.1.1\",\n    \"oxfmt\": \"^0.35.0\",\n    \"oxlint\": \"^1.50.0\",\n    \"release-it\": \"^19.2.4\",\n    \"remark-cli\": \"12.0.1\",\n    \"remark-preset-webpro\": \"^2.1.1\"\n  },\n  \"prettier\": {\n    \"arrowParens\": \"avoid\",\n    \"singleQuote\": true,\n    \"trailingComma\": \"es5\"\n  },\n  \"remarkConfig\": {\n    \"plugins\": [\n      \"preset-webpro\"\n    ]\n  },\n  \"engines\": {\n    \"node\": \">=22.0.0\"\n  },\n  \"packageManager\": \"pnpm@10.24.0\",\n  \"mdxlint\": {\n    \"plugins\": [\n      \"mdxlint-preset-webpro\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/create-config/README.md",
    "content": "# @knip/create-config\n\nAdd Knip to a repository.\n\n```shell\nnpm init @knip/config\n```\n\nThis adds Knip and its peer dependencies to `devDependencies`.\n\nExample result:\n\n```json\n{\n  \"name\": \"my-package\",\n  \"scripts\": {\n    \"knip\": \"knip\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.14.8\",\n    \"knip\": \"^5.30.1\",\n    \"typescript\": \"^5.5.4\"\n  }\n}\n```\n"
  },
  {
    "path": "packages/create-config/index.js",
    "content": "#!/usr/bin/env node\nimport { execSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport { EOL } from 'node:os';\nimport path from 'node:path';\n\nconst fileExists = filePath => {\n  const stat = fs.statSync(filePath, { throwIfNoEntry: false });\n  return stat?.isFile();\n};\n\nconst hasAccess = filePath => {\n  try {\n    fs.accessSync(filePath, fs.constants.R_OK);\n    return true;\n  } catch {\n    return false;\n  }\n};\n\nconst readFirstBytes = (filePath, length = 128) => {\n  const fd = fs.openSync(filePath, 'r');\n  const buffer = Buffer.alloc(length);\n  const bytesRead = fs.readSync(fd, buffer, 0, length, 0);\n  fs.closeSync(fd);\n  return buffer.subarray(0, bytesRead).toString('utf-8');\n};\n\nconst getPackageManager = () => {\n  // get the root of the repository\n  let repositoryRoot = '';\n  try {\n    repositoryRoot = execSync('git rev-parse --show-toplevel', { stdio: [null, null, 'ignore'] })\n      .toString()\n      .trim();\n  } catch {}\n\n  if (fileExists(path.join(repositoryRoot, 'bun.lock'))) return 'bun';\n  if (fileExists(path.join(repositoryRoot, 'bun.lockb'))) return 'bun';\n  if (hasAccess(path.join(repositoryRoot, 'yarn.lock'))) {\n    const yarnLock = readFirstBytes(path.join(repositoryRoot, 'yarn.lock'), 128);\n    return yarnLock.includes('yarn lockfile v1') ? 'yarn' : 'yarn-berry';\n  }\n  if (fileExists(path.join(repositoryRoot, 'pnpm-lock.yaml'))) return 'pnpm';\n  return 'npm';\n};\n\nconst getBinX = pm => {\n  if (pm === 'npm') return 'npx';\n  return pm;\n};\n\nconst hasWorkspaces = manifest => {\n  if (manifest.workspaces) {\n    if (Array.isArray(manifest.workspaces) && manifest.workspaces.length > 0) return true;\n    if (typeof manifest.workspaces === 'object' && manifest.workspaces.packages?.length > 0) return true;\n  }\n\n  if (hasAccess('pnpm-workspace.yaml')) {\n    try {\n      const content = fs.readFileSync('pnpm-workspace.yaml', 'utf-8');\n      return /(^|\\n)packages:\\n/.test(content);\n    } catch {\n      return false;\n    }\n  }\n\n  return false;\n};\n\nconst findPackageJsonFiles = (dir, depth = 0) => {\n  if (depth > 3) return [];\n  const dirs = [];\n  const entries = fs.readdirSync(dir, { withFileTypes: true });\n  for (const entry of entries) {\n    if (entry.name === 'node_modules' || entry.name.startsWith('.')) continue;\n    const fullPath = path.join(dir, entry.name);\n    if (entry.isDirectory()) {\n      dirs.push(...findPackageJsonFiles(fullPath, depth + 1));\n    } else if (entry.name === 'package.json' && dir !== process.cwd()) {\n      dirs.push(path.relative(process.cwd(), dir));\n    }\n  }\n  return dirs;\n};\n\nconst getWorkspaceFlag = (manifest, pm) => {\n  if (pm === 'pnpm') return hasWorkspaces(manifest) ? '-w' : undefined;\n  if (pm === 'yarn') return hasWorkspaces(manifest) ? '-W' : undefined;\n};\n\nconst getPackageManagerFromPackageJson = manifest => {\n  if (!manifest.packageManager) return undefined;\n\n  let [pmName, pmVersion] = manifest.packageManager.split('@');\n  if (pmName === 'yarn' && pmVersion?.[0] > 1) {\n    pmName = 'yarn-berry';\n  }\n\n  const validPackageManagers = ['bun', 'yarn', 'yarn-berry', 'pnpm', 'npm'];\n  if (!validPackageManagers.includes(pmName)) return undefined;\n\n  return pmName;\n};\n\nconst main = () => {\n  if (!fileExists('package.json')) {\n    console.error('Please run this command from the root of a repository with a package.json.');\n    return;\n  }\n\n  const packageJsonPath = path.join(process.cwd(), 'package.json');\n  const manifest = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));\n\n  // Differentiate yarn v1 and v2+ but call them both with `yarn`\n  const pm = getPackageManagerFromPackageJson(manifest) ?? getPackageManager();\n  const bin = pm === 'yarn-berry' ? 'yarn' : pm;\n\n  const cmd = [bin, 'add', getWorkspaceFlag(manifest, pm), '-D', 'knip', 'typescript', '@types/node']\n    .filter(Boolean)\n    .join(' ');\n\n  execSync(cmd, { stdio: 'inherit' });\n\n  console.info('');\n  console.info('✓ Install Knip');\n\n  const knipConfig = {\n    $schema: 'https://unpkg.com/knip@6/schema.json',\n    tags: ['-lintignore'],\n  };\n\n  const hasExplicitWorkspaces = hasWorkspaces(manifest);\n\n  if (hasExplicitWorkspaces) knipConfig.workspaces = { '.': {} };\n\n  try {\n    if (!fileExists('knip.json')) {\n      fs.writeFileSync('knip.json', `${JSON.stringify(knipConfig, null, 2)}${EOL}`);\n      console.info('✓ Create knip.json');\n    } else {\n      console.info('✓ Detected knip.json');\n    }\n  } catch (error) {\n    console.warn('× Failed to create knip.json:', error.message);\n  }\n\n  try {\n    execSync('npm pkg set scripts.knip=knip', { stdio: ['inherit', 'inherit', 'ignore'] });\n    console.info('✓ Add knip to package.json#scripts');\n  } catch {\n    console.warn('× Failed to add knip to package.json#scripts');\n  }\n\n  if (!hasExplicitWorkspaces) {\n    const packages = findPackageJsonFiles(process.cwd());\n    if (packages.length > 0) {\n      console.info('');\n      console.info('⚠ Found package.json files in subdirectories:');\n      for (const pkg of packages.slice(0, 5)) console.info(`- ${pkg}`);\n      if (packages.length > 5) console.info(`- ...and ${packages.length - 5} more`);\n      console.info('Consider adding workspaces to knip.json:');\n      console.info('More details: https://knip.dev/features/monorepos-and-workspaces#additional-workspaces');\n    }\n  }\n\n  console.info('');\n  console.info(`→ Run \\`${getBinX(bin)} knip --max-show-issues 5\\` to run Knip for the first time`);\n\n  console.info(`→ Continue with https://knip.dev/overview/configuration`);\n};\n\nmain();\n"
  },
  {
    "path": "packages/create-config/package.json",
    "content": "{\n  \"name\": \"@knip/create-config\",\n  \"version\": \"1.2.1\",\n  \"homepage\": \"https://knip.dev\",\n  \"bugs\": \"https://github.com/webpro-nl/knip/issues\",\n  \"license\": \"ISC\",\n  \"author\": {\n    \"name\": \"Lars Kappert\",\n    \"email\": \"lars@webpro.nl\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/webpro-nl/knip\",\n    \"directory\": \"packages/create-config\"\n  },\n  \"funding\": [\n    {\n      \"type\": \"github\",\n      \"url\": \"https://github.com/sponsors/webpro\"\n    },\n    {\n      \"type\": \"opencollective\",\n      \"url\": \"https://opencollective.com/knip\"\n    }\n  ],\n  \"bin\": \"index.js\",\n  \"type\": \"module\",\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"release\": \"release-it -c ../../.release-it.json\"\n  }\n}\n"
  },
  {
    "path": "packages/docs/.gitignore",
    "content": ".astro/\n.DS_Store\n.env\ndist/\nnode_modules/\nsrc/content/docs/reference/plugins/\n"
  },
  {
    "path": "packages/docs/README.md",
    "content": "# knip.dev\n\nThe source of [knip.dev][1].\n\n[1]: https://knip.dev\n"
  },
  {
    "path": "packages/docs/astro.config.ts",
    "content": "import starlight from '@astrojs/starlight';\nimport type { ExpressiveCodeTheme } from '@astrojs/starlight/expressive-code';\nimport { defineConfig } from 'astro/config';\nimport remarkDirective from 'remark-directive';\nimport { fixInternalLinks } from './remark/fixInternalLinks.ts';\nimport { transformDirectives } from './remark/transformDirectives.ts';\n\nconst setForeground = (theme: ExpressiveCodeTheme, scope: string, value: string) => {\n  const settings = theme.settings.find(setting => setting.scope?.includes(scope));\n  if (settings) settings.settings.foreground = value;\n};\n\nexport default defineConfig({\n  site: 'https://knip.dev',\n  base: '/',\n  // @ts-expect-error\n  sitemap: false,\n  trailingSlash: 'never',\n  redirects: {\n    '/guides/commonjs': '/guides/working-with-commonjs',\n    '/guides/writing-a-plugin': '/writing-a-plugin',\n  },\n  markdown: {\n    remarkPlugins: [fixInternalLinks, transformDirectives, remarkDirective],\n  },\n  integrations: [\n    starlight({\n      title: 'Knip',\n      logo: {\n        light: './src/assets/title-light.svg',\n        dark: './src/assets/title-dark.svg',\n        replacesTitle: true,\n      },\n      social: [\n        { icon: 'github', label: 'GitHub', href: 'https://github.com/webpro-nl/knip' },\n        { icon: 'blueSky', label: 'Bluesky', href: 'https://bsky.app/profile/webpro.nl' },\n        { icon: 'npm', label: 'npm', href: 'https://www.npmjs.com/package/knip' },\n      ],\n      components: {\n        Head: './src/components/Head.astro',\n        Footer: './src/components/Footer.astro',\n      },\n      customCss: ['./src/styles/custom.css', './src/fonts/font-face.css'],\n      editLink: {\n        baseUrl: 'https://github.com/webpro-nl/knip/edit/main/packages/docs/',\n      },\n      sidebar: [\n        {\n          label: 'Overview',\n          autogenerate: { directory: 'overview' },\n        },\n        {\n          label: 'Understanding Knip',\n          autogenerate: { directory: 'explanations' },\n        },\n        {\n          label: 'Features',\n          autogenerate: { directory: 'features' },\n        },\n        {\n          label: 'Guides',\n          autogenerate: { directory: 'guides' },\n        },\n        {\n          label: 'Writing a Plugin',\n          autogenerate: { directory: 'writing-a-plugin' },\n        },\n        {\n          label: 'Reference',\n          autogenerate: { directory: 'reference' },\n        },\n        {\n          label: 'Blog',\n          autogenerate: { directory: 'blog' },\n        },\n        {\n          label: 'Read more',\n          collapsed: true,\n          autogenerate: { directory: 'typescript' },\n        },\n      ],\n      expressiveCode: {\n        emitExternalStylesheet: true,\n        styleOverrides: {\n          // @ts-expect-error deal with it\n          'frm-tooltipSuccessBg': 'var(--sl-color-orange)',\n          'frm-tooltipSuccessFg': 'var(--sl-color-white)',\n          'frm-edBg': 'var(--sl-color-black)',\n          'frm-edActTabBg': 'var(--sl-color-black)',\n          'frm-edActTabBrdCol': 'var(--sl-color-black)',\n          'frm-edTabBarBg': 'none',\n          'frm-edTabBarBrdBtmCol': 'none',\n          'frm-frameBoxShdCssVal': 'none',\n        },\n        frames: {\n          showCopyToClipboardButton: true,\n        },\n        themes: ['min-dark'],\n        customizeTheme: theme => {\n          setForeground(theme, 'entity.name.tag', '#f68a22');\n          setForeground(theme, 'entity.name.type', '#ededed');\n          setForeground(theme, 'string', '#ededed');\n          return theme;\n        },\n      },\n    }),\n  ],\n});\n"
  },
  {
    "path": "packages/docs/mock/contributors.json",
    "content": "[\n  {\n    \"login\": \"webpro\",\n    \"id\": 456426,\n    \"node_id\": \"MDQ6VXNlcjQ1NjQyNg==\",\n    \"avatar_url\": \"https://avatars.githubusercontent.com/u/456426?v=4\",\n    \"gravatar_id\": \"\",\n    \"url\": \"https://api.github.com/users/webpro\",\n    \"html_url\": \"https://github.com/webpro\",\n    \"followers_url\": \"https://api.github.com/users/webpro/followers\",\n    \"following_url\": \"https://api.github.com/users/webpro/following{/other_user}\",\n    \"gists_url\": \"https://api.github.com/users/webpro/gists{/gist_id}\",\n    \"starred_url\": \"https://api.github.com/users/webpro/starred{/owner}{/repo}\",\n    \"subscriptions_url\": \"https://api.github.com/users/webpro/subscriptions\",\n    \"organizations_url\": \"https://api.github.com/users/webpro/orgs\",\n    \"repos_url\": \"https://api.github.com/users/webpro/repos\",\n    \"events_url\": \"https://api.github.com/users/webpro/events{/privacy}\",\n    \"received_events_url\": \"https://api.github.com/users/webpro/received_events\",\n    \"type\": \"User\",\n    \"site_admin\": false,\n    \"contributions\": 1282\n  }\n]\n"
  },
  {
    "path": "packages/docs/package.json",
    "content": "{\n  \"name\": \"@knip/docs\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"description\": \"Documentation for Knip, hosted at knip.dev\",\n  \"homepage\": \"https://knip.dev\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"github:webpro-nl/knip\",\n    \"directory\": \"packages/docs\"\n  },\n  \"type\": \"module\",\n  \"scripts\": {\n    \"prebuild\": \"node ./scripts/generate-plugin-docs.ts\",\n    \"build\": \"astro check && astro build\",\n    \"postbuild\": \"[ \\\"$CF_PAGES\\\" = \\\"1\\\" ] && find dist -mindepth 2 -type f -name 'index.html' -exec bash -c 'f=\\\"$1\\\"; d=$(dirname \\\"$f\\\"); bn=$(basename \\\"$d\\\"); mv -v \\\"$f\\\" \\\"$d/../$bn.html\\\"' _ {} \\\\; && find dist -mindepth 1 -type d -empty -print -delete || true\",\n    \"dev\": \"astro dev\",\n    \"remark\": \"remark README.md 'src/content/docs/**/*.md' --verbose -o && mdxlint 'src/content/docs/**/*.mdx' --verbose -o\"\n  },\n  \"dependencies\": {\n    \"@astro-community/astro-embed-youtube\": \"0.5.10\",\n    \"@astrojs/starlight\": \"0.38.1\",\n    \"sharp\": \"0.34.5\"\n  },\n  \"devDependencies\": {\n    \"@astrojs/check\": \"0.9.8\",\n    \"@octokit/graphql\": \"^9.0.3\",\n    \"@types/mdast\": \"4.0.4\",\n    \"@types/unist\": \"3.0.3\",\n    \"astro\": \"6.0.5\",\n    \"hastscript\": \"9.0.1\",\n    \"picocolors\": \"^1.1.1\",\n    \"remark-directive\": \"4.0.0\",\n    \"remark-frontmatter\": \"5.0.0\",\n    \"remark-gfm\": \"4.0.1\",\n    \"remark-parse\": \"11.0.0\",\n    \"remark-stringify\": \"11.0.0\",\n    \"unified\": \"11.0.5\",\n    \"unist-builder\": \"4.0.0\",\n    \"unist-util-visit\": \"5.1.0\"\n  },\n  \"engines\": {\n    \"node\": \">=22.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/docs/public/fonts/fonts.conf",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE fontconfig SYSTEM \"fonts.dtd\">\n<fontconfig>\n  <dir>/vercel/path0/docs/public/fonts/</dir>\n  <cachedir>/vercel/path0/docs/.astro/</cachedir>\n  <config></config>\n</fontconfig>\n"
  },
  {
    "path": "packages/docs/public/manifest.json",
    "content": "{\n  \"name\": \"Knip\",\n  \"short_name\": \"Knip\",\n  \"icons\": [\n    {\n      \"src\": \"/favicon.svg\",\n      \"type\": \"image/svg+xml\",\n      \"sizes\": \"512x512\"\n    },\n    {\n      \"src\": \"/icon-192x192.png\",\n      \"sizes\": \"192x192\",\n      \"type\": \"image/png\"\n    },\n    {\n      \"src\": \"/icon-512x512.png\",\n      \"sizes\": \"512x512\",\n      \"type\": \"image/png\"\n    }\n  ],\n  \"theme_color\": \"#fbfbfb\",\n  \"background_color\": \"#f56e0f\",\n  \"display\": \"standalone\",\n  \"scope\": \"/\",\n  \"start_url\": \"/overview/getting-started/\"\n}\n"
  },
  {
    "path": "packages/docs/public/robots.txt",
    "content": "User-agent: *\nAllow: /\n\nSitemap: https://knip.dev/sitemap.txt\n"
  },
  {
    "path": "packages/docs/remark/fixInternalLinks.ts",
    "content": "import pc from 'picocolors';\nimport type { Node, Parent } from 'unist';\nimport { type Visitor, visit } from 'unist-util-visit';\n\ninterface LinkNode extends Node {\n  url: string;\n}\n\nconst isLinkNode = (node: Node): node is LinkNode =>\n  (node.type === 'link' || node.type === 'definition') && typeof (node as LinkNode).url === 'string';\n\nconst dateTimeFormat = new Intl.DateTimeFormat([], {\n  hour: '2-digit',\n  minute: '2-digit',\n  second: '2-digit',\n  hour12: false,\n});\n\nexport const fixInternalLinks = () => (tree: Parent) => {\n  const visitor: Visitor = node => {\n    if (isLinkNode(node) && node.url.startsWith('.')) {\n      const url = node.url;\n      node.url = url.replace(/(?:\\/index)?\\.mdx?(#.+)?$/, '$1');\n      console.log(`${pc.dim(dateTimeFormat.format(new Date()))} ${pc.cyan('[fix-link]')} Modify ${url} → ${node.url}`);\n    }\n  };\n  visit(tree, visitor);\n};\n"
  },
  {
    "path": "packages/docs/remark/transformDirectives.ts",
    "content": "import { h } from 'hastscript';\nimport type { Node, Parent } from 'unist';\nimport { type Visitor, visit } from 'unist-util-visit';\n\ninterface DirectiveNode extends Node {\n  type: 'textDirective' | 'leafDirective' | 'containerDirective';\n  name: string;\n  attributes: Record<string, any>;\n  data: Record<string, any>;\n}\n\nconst isDirectiveNode = (node: Node): node is DirectiveNode =>\n  node.type === 'textDirective' || node.type === 'leafDirective' || node.type === 'containerDirective';\n\nexport const transformDirectives = () => (tree: Parent) => {\n  const visitor: Visitor<Node> = node => {\n    if (isDirectiveNode(node)) {\n      const hast = h(node.name, node.attributes);\n      // oxlint-disable-next-line no-cond-assign\n      const data = node.data || (node.data = {});\n      data.hName = hast.tagName;\n      data.hProperties = hast.properties;\n    }\n  };\n  visit(tree, visitor);\n};\n"
  },
  {
    "path": "packages/docs/scripts/generate-plugin-docs.ts",
    "content": "import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { Root } from 'mdast';\nimport remarkDirective from 'remark-directive';\nimport remarkFrontmatter from 'remark-frontmatter';\nimport remarkParse from 'remark-parse';\nimport remarkStringify from 'remark-stringify';\nimport { unified } from 'unified';\nimport type { Node } from 'unist';\nimport { u } from 'unist-builder';\nimport type { Plugin } from '../../knip/src/types/config.ts';\n\nconst rootDir = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');\nconst referenceDocsDir = path.join(rootDir, 'src/content/docs/reference');\nconst knipDir = path.join(rootDir, '../../packages/knip');\nconst pluginsDir = path.join(knipDir, 'src/plugins');\nconst directories = await fs.opendir(pluginsDir);\nconst plugins = [];\nconst srcBaseUrl = new URL('webpro-nl/knip/blob/main/packages/knip/src/plugins', 'https://github.com');\n\nconst parseFragment = (text: string) => {\n  const tree = unified().use(remarkParse).parse(text);\n  return tree.children;\n};\n\nconst writeTree = async (tree: Node, filePath: string) => {\n  try {\n    const file = await unified().run(tree);\n    const markdown = unified()\n      .use(remarkFrontmatter, ['yaml'])\n      .use(remarkDirective)\n      .use(remarkStringify, {\n        bullet: '-',\n      })\n      .stringify(file as Root);\n\n    await fs.writeFile(filePath, markdown);\n  } catch (err) {\n    console.error(err);\n  }\n};\n\nfor await (const dir of directories) {\n  if (dir.isDirectory() && dir.name !== '_template') {\n    const pluginName = dir.name;\n    const pluginDir = path.join(pluginsDir, pluginName);\n    const mod = await import(path.join(pluginDir, 'index.ts'));\n    const plugin: Plugin = mod.default;\n    const docs: undefined | { note: string; entry?: string[]; production?: string[] } = mod.docs;\n\n    const { title, enablers, args, config, entry, production, project } = plugin;\n\n    plugins.push([title, pluginName]);\n\n    const frontmatter = u('yaml', `title: ${title}\\nsidebar:\\n  hidden: true`);\n\n    const defaults: Record<string, string[]> = {};\n    const configFiles = typeof config === 'function' ? config({ cwd: pluginDir }) : config;\n    if (configFiles && configFiles.length > 0) defaults.config = configFiles;\n    if (entry && entry.length > 0) defaults.entry = entry;\n    if (docs?.entry && docs.entry.length > 0) defaults.entry = [...(defaults.entry ?? []), ...docs.entry];\n    if (production && production.length > 0) defaults.entry = [...(defaults.entry ?? []), ...production];\n    if (docs?.production && docs.production.length > 0)\n      defaults.entry = [...(defaults.entry ?? []), ...docs.production];\n    if (project && project.length > 0) defaults.project = project;\n\n    const hasDefaultConfig = Object.values(defaults).some(v => v.length > 0);\n\n    const enabledText =\n      Array.isArray(enablers) && enablers.length > 0\n        ? enablers.length === 1 && typeof enablers[0] === 'string'\n          ? parseFragment(\n              `This plugin is enabled if \\`\"${enablers[0]}\"\\` is listed in \\`\"dependencies\"\\` or \\`\"devDependencies\"\\` in \\`package.json\\`.`\n            )\n          : [\n              ...parseFragment(\n                `This plugin is enabled if there's a match in \\`\"dependencies\"\\` or \\`\"devDependencies\"\\` in \\`package.json\\`:`\n              ),\n              u(\n                'list',\n                enablers.map((enabler: string | RegExp) =>\n                  u('listItem', [u('inlineCode', typeof enabler === 'string' ? enabler : enabler.source)])\n                )\n              ),\n            ]\n        : typeof enablers === 'string'\n          ? parseFragment(enablers)\n          : [u('paragraph', [u('text', 'This plugin is always enabled.')])];\n\n    const defaultConfig = hasDefaultConfig\n      ? [\n          u('heading', { depth: 2 }, [u('text', 'Default configuration')]),\n          ...parseFragment('If this plugin is enabled, the following configuration is added automatically:'),\n          u('code', {\n            lang: 'json', // TODO How to set attributes/properties/props properly?\n            value: JSON.stringify({ [pluginName]: defaults }, null, 2),\n          }),\n          ...parseFragment('Depending on local configuration, plugins may modify the defaults as shown.'),\n          ...parseFragment('Custom `config` or `entry` options override default values, they are not merged.'),\n          ...parseFragment(\n            'See [Plugins](../../explanations/plugins.md) for more details about plugins and their `entry` and `config` options.'\n          ),\n        ]\n      : [];\n\n    const notes = docs?.note ? [u('heading', { depth: 2 }, [u('text', 'Note')]), ...parseFragment(docs.note)] : [];\n\n    const printCode = (value: unknown): string =>\n      Array.isArray(value)\n        ? `[${value.map(printCode).join(', ')}]`\n        : typeof value === 'function'\n          ? value.toString()\n          : JSON.stringify(value).replace(/([,:])/g, '$1 ');\n\n    const argsText = args\n      ? [\n          ...parseFragment(\n            `## Shell commands\\n\\nThis plugin adds argument parsing for the <code>${args.binaries ? args.binaries.join(' and ') : pluginName}</code>\n            ${args.binaries && args.binaries.length > 1 ? 'binaries' : 'binary'}. Configuration:`\n          ),\n          ...parseFragment(\n            `\\`\\`\\`\\n${Object.entries(args)\n              .filter(([key]) => key !== 'binaries')\n              .map(([key, value]) => `${key}: ${printCode(value)}`)\n              .join('\\n')}\\n\\`\\`\\``\n          ),\n          ...parseFragment(\n            'The configuration was generated from source code. Also see [Script Parser](../../features/script-parser.md).'\n          ),\n        ]\n      : [];\n\n    const generated = parseFragment(\n      `## Generated from source\\n\\nThis page was generated from the [${pluginName} plugin source code](${srcBaseUrl}/${dir.name}/index.ts).`\n    );\n\n    const tree = u('root', [\n      frontmatter,\n      u('heading', { depth: 2 }, [u('text', 'Enabled')]),\n      ...enabledText,\n      ...defaultConfig,\n      ...notes,\n      ...argsText,\n      ...generated,\n    ]);\n\n    console.log(`Writing ${pluginName} docs to plugins/${pluginName}.md`);\n    await writeTree(tree, path.join(referenceDocsDir, `plugins/${pluginName}.md`));\n  }\n}\n\nplugins.sort((a, b) => (a[1] < b[1] ? -1 : 1));\n\nconst frontmatter = u('yaml', `title: Plugins (${plugins.length})\\ntableOfContents: false`);\n\nconst tree = u('root', [\n  frontmatter,\n  u('containerDirective', { name: 'section{.columns.min200}' }, [\n    u(\n      'list',\n      { spread: false, ordered: false },\n      plugins.map(plugin =>\n        u('listItem', [u('link', { title: plugin[0], url: `/reference/plugins/${plugin[1]}` }, [u('text', plugin[0])])])\n      )\n    ),\n    u('paragraph'),\n  ]),\n]);\n\nconsole.log('Writing plugin list to plugins.md');\nawait writeTree(tree, path.join(referenceDocsDir, 'plugins.md'));\n"
  },
  {
    "path": "packages/docs/scripts/get-monthly-sponsorships-github.ts",
    "content": "import { graphql } from '@octokit/graphql';\n\nconst token = process.env.GITHUB_TOKEN_SPONSORSHIPS;\n\ntype Options = {\n  token?: string;\n  startDate: Date;\n  endDate?: Date;\n  recurringOnly: boolean;\n};\n\ninterface SponsorActivity {\n  action: 'NEW_SPONSORSHIP' | 'CANCELLED_SPONSORSHIP';\n  timestamp: string;\n  sponsorsTier: {\n    monthlyPriceInDollars: number;\n    isOneTime: boolean;\n  };\n  sponsor: {\n    login: string;\n  };\n}\n\ninterface GraphQLResponse {\n  viewer: {\n    sponsorsActivities: {\n      nodes: SponsorActivity[];\n    };\n  };\n}\n\nconst getMonthlyTotals = async (options: Options) => {\n  const { token, startDate, recurringOnly } = options;\n\n  const { viewer } = await graphql<GraphQLResponse>({\n    query: `\n      query {\n        viewer {\n          sponsorsActivities(first: 100, period: ALL) {\n            nodes {\n              action\n              timestamp\n              sponsorsTier {\n                monthlyPriceInDollars\n                isOneTime\n              }\n              sponsor {\n                ... on User { login }\n                ... on Organization { login }\n              }\n            }\n          }\n        }\n      }\n    `,\n    headers: {\n      authorization: `token ${token}`,\n    },\n  });\n\n  const activities = [...viewer.sponsorsActivities.nodes].sort(\n    (a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()\n  );\n\n  const activeRecurring = new Map<string, number>();\n  const monthlyTotals = new Map<string, number>();\n  const now = new Date();\n\n  for (let d = new Date(startDate); d <= now; d = new Date(d.getFullYear(), d.getMonth() + 1, 1)) {\n    monthlyTotals.set(d.toISOString().substring(0, 7), 0);\n  }\n\n  for (const activity of activities) {\n    const { action, sponsor, sponsorsTier, timestamp } = activity;\n    if (recurringOnly && sponsorsTier?.isOneTime) continue;\n    const amount = sponsorsTier?.monthlyPriceInDollars || 0;\n    const monthYear = new Date(timestamp).toISOString().substring(0, 7);\n\n    if (sponsorsTier?.isOneTime) {\n      if (!recurringOnly && action === 'NEW_SPONSORSHIP') {\n        monthlyTotals.set(monthYear, (monthlyTotals.get(monthYear) || 0) + amount);\n      }\n    } else {\n      if (action === 'NEW_SPONSORSHIP') activeRecurring.set(sponsor.login, amount);\n      if (action === 'CANCELLED_SPONSORSHIP') activeRecurring.delete(sponsor.login);\n      const total = Array.from(activeRecurring.values()).reduce((sum, a) => sum + a, 0);\n      for (const month of monthlyTotals.keys()) {\n        if (month >= monthYear) monthlyTotals.set(month, total);\n      }\n    }\n  }\n\n  return monthlyTotals;\n};\n\nexport const getGitHubTotals = async (options: Options) => {\n  if (!token) throw new Error('GITHUB_TOKEN_SPONSORSHIPS is not set');\n  const monthlyData = await getMonthlyTotals({ ...options, token });\n  const startMonth = options.startDate.toISOString().substring(0, 7);\n  const endMonth = options.endDate?.toISOString().substring(0, 7);\n  for (const month of monthlyData.keys()) {\n    if (month < startMonth || (endMonth && month > endMonth)) monthlyData.delete(month);\n  }\n  return monthlyData;\n};\n"
  },
  {
    "path": "packages/docs/scripts/get-monthly-sponsorships-opencollective.ts",
    "content": "import { graphql } from '@octokit/graphql';\n\nconst token = process.env.OPENCOLLECTIVE_TOKEN;\nconst RATE_EUR_TO_USD = 1.08;\n\ntype Options = {\n  token?: string;\n  startDate: Date;\n  endDate?: Date;\n  recurringOnly: boolean;\n};\n\ninterface Transaction {\n  id: string;\n  type: string;\n  kind: string;\n  amount: {\n    value: number;\n    currency: string;\n  };\n  createdAt: string;\n  fromAccount: {\n    name: string;\n  };\n}\n\ninterface Expense {\n  id: string;\n  amount: number;\n  currency: string;\n  createdAt: string;\n  type: string;\n  status: string;\n}\n\ninterface GraphQLResponse {\n  account: {\n    transactions: {\n      nodes: Transaction[];\n    };\n  };\n  expenses: {\n    nodes: Expense[];\n  };\n}\n\nconst getMonthlyTotals = async (options: Options): Promise<Map<string, number>> => {\n  const { token, startDate, recurringOnly } = options;\n\n  const { account, expenses } = await graphql<GraphQLResponse>({\n    query: `\n      query {\n        account(slug: \"knip\") {\n          transactions(type: CREDIT) {\n            nodes {\n              id\n              type\n              kind\n              amount {\n                value\n                currency\n              }\n              createdAt\n              fromAccount {\n                name\n              }\n            }\n          }\n        }\n        expenses(fromAccount: { slug: \"webpro\" }) {\n          nodes {\n            id\n            amount\n            currency\n            createdAt\n            type\n            status\n          }\n        }\n      }\n    `,\n    url: 'https://api.opencollective.com/graphql/v2',\n    headers: {\n      'Api-Key': token,\n      Accept: 'application/json',\n    },\n  });\n\n  const monthlyTotals = new Map<string, number>();\n  const now = new Date();\n\n  for (let d = new Date(startDate); d <= now; d = new Date(d.getFullYear(), d.getMonth() + 1, 1)) {\n    monthlyTotals.set(d.toISOString().substring(0, 7), 0);\n  }\n\n  for (const transaction of account.transactions.nodes) {\n    if (recurringOnly && transaction.kind !== 'CONTRIBUTION') continue;\n\n    const month = new Date(transaction.createdAt).toISOString().substring(0, 7);\n    const amount = Math.round(transaction.amount.value);\n    monthlyTotals.set(month, (monthlyTotals.get(month) || 0) + amount);\n  }\n\n  if (!recurringOnly) {\n    for (const expense of expenses.nodes) {\n      const month = new Date(expense.createdAt).toISOString().substring(0, 7);\n      const amount =\n        expense.currency === 'EUR'\n          ? Math.round((expense.amount / 100) * RATE_EUR_TO_USD)\n          : Math.round(expense.amount / 100);\n      monthlyTotals.set(month, (monthlyTotals.get(month) || 0) + amount);\n    }\n  }\n\n  return monthlyTotals;\n};\n\nexport const getOpenCollectiveTotals = async (options: Options) => {\n  if (!token) throw new Error('OPENCOLLECTIVE_TOKEN is not set');\n  const monthlyData = await getMonthlyTotals({ ...options, token });\n  const startMonth = options.startDate.toISOString().substring(0, 7);\n  const endMonth = options.endDate?.toISOString().substring(0, 7);\n  for (const month of monthlyData.keys()) {\n    if (month < startMonth || (endMonth && month > endMonth)) monthlyData.delete(month);\n  }\n  return monthlyData;\n};\n"
  },
  {
    "path": "packages/docs/scripts/get-monthly-sponsorships.ts",
    "content": "import { Table } from '../../knip/src/util/table.ts';\nimport { getGitHubTotals } from './get-monthly-sponsorships-github.ts';\nimport { getOpenCollectiveTotals } from './get-monthly-sponsorships-opencollective.ts';\n\nconst START_DATE = new Date('2023-11-01');\nconst END_DATE = new Date();\nEND_DATE.setDate(1);\nEND_DATE.setHours(-1);\n\nconst main = async () => {\n  const [monthlyGHS, monthlyGHSRO, monthlyOC, monthlyOCRO] = await Promise.all([\n    getGitHubTotals({ startDate: START_DATE, endDate: END_DATE, recurringOnly: false }),\n    getGitHubTotals({ startDate: START_DATE, endDate: END_DATE, recurringOnly: true }),\n    getOpenCollectiveTotals({ startDate: START_DATE, endDate: END_DATE, recurringOnly: false }),\n    getOpenCollectiveTotals({ startDate: START_DATE, endDate: END_DATE, recurringOnly: true }),\n  ]);\n\n  const monthly = new Map<string, number[]>();\n  for (const [month, amount] of monthlyGHS) monthly.set(month, [...(monthly.get(month) || []), amount]);\n  for (const [month, amount] of monthlyOC) monthly.set(month, [...(monthly.get(month) || []), amount]);\n\n  const monthlyRO = new Map<string, number[]>();\n  for (const [month, amount] of monthlyGHSRO) monthlyRO.set(month, [...(monthlyRO.get(month) || []), amount]);\n  for (const [month, amount] of monthlyOCRO) monthlyRO.set(month, [...(monthlyRO.get(month) || []), amount]);\n\n  const table = new Table();\n  const digits = (n: number) => (v: any) => (typeof v === 'number' ? v.toFixed(n) : v);\n  const sum = Array.from(monthly.values()).reduce((a, c) => a + c.reduce((a, b) => a + b, 0), 0);\n  const sumGHS = Array.from(monthlyGHS.values()).reduce((a, c) => a + c, 0);\n  const sumOC = Array.from(monthlyOC.values()).reduce((a, c) => a + c, 0);\n  const sumRO = Array.from(monthlyRO.values()).reduce((a, c) => a + c.reduce((a, b) => a + b, 0), 0);\n  for (const [key, value] of [\n    ['months', monthly.size],\n    ['github', sumGHS],\n    ['OC', sumOC],\n    ['sum', sum],\n    ['avg', sum / monthly.size],\n    ['recurring sum', sumRO],\n    ['recurring avg', sumRO / monthlyRO.size],\n  ]) {\n    table.row();\n    table.cell('key', key);\n    table.cell('value', value, digits(0));\n  }\n\n  console.log(table.toString());\n\n  const url = new URL('/', 'https://try.venz.dev');\n  url.searchParams.set('type', 'pivot');\n  url.searchParams.set('lp', 'tr');\n  url.searchParams.set('br', '0');\n  url.searchParams.set('labelX', 'month');\n  url.searchParams.set('labelY', 'amount ($)');\n  url.searchParams.append('l', 'GitHub Sponsors');\n  url.searchParams.append('color', '#fbfbfb');\n  url.searchParams.append('l', 'Open Collective');\n  url.searchParams.append('color', '#2487ff');\n  for (const [month, amount] of monthly) {\n    url.searchParams.append('label', month);\n    url.searchParams.append('data', amount.join(','));\n  }\n\n  console.log('\\nVenz link');\n  const text = `${url.origin}?${decodeURIComponent(url.searchParams.toString()).replaceAll('#', '%23')}`;\n  console.log(text);\n};\n\nmain().catch(console.error);\n"
  },
  {
    "path": "packages/docs/src/assets/testimonials.json",
    "content": "{\n  \"data\": [\n    {\n      \"entities\": {\n        \"urls\": [\n          {\n            \"url\": \"cdn.bsky.app/img/dummy-02\",\n            \"display_url\": \"cdn.bsky.app/img/julien.deniau.me\",\n            \"media_key\": \"4\"\n          }\n        ]\n      },\n      \"id\": \"3mdrubg3y5c2z\",\n      \"author_id\": \"11\",\n      \"created_at\": \"2026-02-01T07:57:00.000Z\",\n      \"attachments\": {},\n      \"text\": \"Done on a mono-repo in two times.\\nWhat's impressive is that there were no false positive!\\ncdn.bsky.app/img/dummy-02\"\n    },\n    {\n      \"entities\": {\n        \"urls\": [\n          {\n            \"url\": \"https://t.co/EwIeRiHaXf\",\n            \"display_url\": \"pic.x.com/EwIeRiHaXf\",\n            \"media_key\": \"6\"\n          }\n        ]\n      },\n      \"id\": \"2017122504914161979\",\n      \"author_id\": \"16\",\n      \"created_at\": \"2026-01-30T06:27:00.000Z\",\n      \"text\": \"Good morning 🌄\\nA tool I discovered lately.\\nIf you care about clean codebases, this one's a gem 💎\"\n    },\n    {\n      \"entities\": {\n        \"urls\": []\n      },\n      \"id\": \"3mcky5ps3r22p\",\n      \"author_id\": \"12\",\n      \"created_at\": \"2026-01-16T20:53:09.000Z\",\n      \"text\": \"Knip is a cracking-looking open source tool for trimming unused JavaScript.\"\n    },\n    {\n      \"entities\": {\n        \"urls\": [\n          {\n            \"url\": \"cdn.bsky.app/img/dummy-03\",\n            \"display_url\": \"cdn.bsky.app/img/patak.dev\",\n            \"media_key\": \"5\"\n          }\n        ]\n      },\n      \"id\": \"3mcjtmv35wc2y\",\n      \"author_id\": \"15\",\n      \"created_at\": \"2026-01-16T09:59:29.000Z\",\n      \"attachments\": {},\n      \"text\": \"From the report, we could improve the situation if we stop sending unused JavaScript. @webpro.nl is doing an incredible job growing the usage of knip.dev, and that should hopefully make a dent in the future.\\ncdn.bsky.app/img/dummy-03\"\n    },\n    {\n      \"entities\": {\n        \"urls\": [\n          {\n            \"url\": \"https://knip.dev/blog/for-editors-and-agents\",\n            \"expanded_url\": \"https://knip.dev/blog/for-editors-and-agents\",\n            \"display_url\": \"knip.dev/blog/for-edi...\"\n          }\n        ]\n      },\n      \"id\": \"3mby3zkzhfs2t\",\n      \"author_id\": \"13\",\n      \"created_at\": \"2026-01-09T08:41:49.000Z\",\n      \"text\": \"The VSCode/Cursor extension is ace!\\nAnd you can use the Knip MCP to create the config file.\\nIt works for Svelte as well.\\nKnip is really really good 🙏\"\n    },\n    {\n      \"entities\": {\n        \"urls\": [\n          {\n            \"url\": \"https://t.co/6rcslWBqIV\",\n            \"display_url\": \"pic.x.com/6rcslWBqIV\",\n            \"media_key\": \"6\"\n          }\n        ]\n      },\n      \"id\": \"2007481125610914217\",\n      \"author_id\": \"17\",\n      \"created_at\": \"2026-01-03T15:56:00.000Z\",\n      \"attachments\": { \"media_keys\": [\"6\"] },\n      \"text\": \"This is both embarrassing and satisfying after running @Knip https://t.co/6rcslWBqIV\"\n    },\n    {\n      \"entities\": {\n        \"urls\": [\n          {\n            \"url\": \"https://t.co/hneEWMqdnm\",\n            \"expanded_url\": \"https://knip.dev\",\n            \"display_url\": \"Knip.dev\"\n          }\n        ]\n      },\n      \"id\": \"2007221517562904675\",\n      \"author_id\": \"18\",\n      \"created_at\": \"2026-01-02T23:44:00.000Z\",\n      \"text\": \"You can't spell Opus without https://t.co/hneEWMqdnm\"\n    },\n    {\n      \"entities\": {\n        \"urls\": []\n      },\n      \"id\": \"3mbggqrihuk2r\",\n      \"author_id\": \"14\",\n      \"created_at\": \"2026-01-02T08:05:50.000Z\",\n      \"text\": \"Knip is amazing and you should be using it.\"\n    },\n    {\n      \"entities\": {\n        \"urls\": []\n      },\n      \"id\": \"1932778410691949034\",\n      \"author_id\": \"10\",\n      \"created_at\": \"2025-06-11T14:34:00.000Z\",\n      \"text\": \"\\\"Thanks Knip!\\\"\\neveryday,\\nevery team member,\\nliterally..\\nknip.dev 🏅\"\n    },\n    {\n      \"entities\": {\n        \"urls\": []\n      },\n      \"url\": \"https://github.com/webpro-nl/knip/issues/1120#issuecomment-2943978262\",\n      \"author_id\": \"9\",\n      \"created_at\": \"2025-06-05T14:00:00.000Z\",\n      \"text\": \"Thank you so much for this incredible piece of software! Knip is one of the most impressive tools I've seen in my career.\"\n    },\n    {\n      \"entities\": {\n        \"urls\": [\n          {\n            \"url\": \"https://github.com/webpro-nl/knip/pull/1022\",\n            \"expanded_url\": \"https://github.com/webpro-nl/knip/pull/1022\",\n            \"display_url\": \"https://github.com/webpro-nl/knip/pull/1022\",\n            \"title\": \"feat: add create-typescript-app plugin\",\n            \"description\": \"feat: add create-typescript-app plugin\",\n            \"unwound_url\": \"https://github.com/webpro-nl/knip/pull/1022\"\n          }\n        ]\n      },\n      \"id\": \"3lmhgsj2n622o\",\n      \"author_id\": \"7\",\n      \"created_at\": \"2025-04-10T13:50:00.000Z\",\n      \"attachments\": {},\n      \"text\": \"Got my first plugin merged into Knip by @webpro.nl today! ☺️\\n\\nThe contribution experience was fantastic: the docs were super clear, the code was straightforward to navigate, and the helper script to generate a new plugin got me 90% of the way there. Awesome. Thanks Lars!\\n\\nhttps://github.com/webpro-nl/knip/pull/1022\"\n    },\n    {\n      \"entities\": {\n        \"urls\": []\n      },\n      \"id\": \"1904580358307250634\",\n      \"author_id\": \"6\",\n      \"created_at\": \"2025-03-25T18:05:00.000Z\",\n      \"attachments\": {},\n      \"text\": \"All of the TanStack monorepos make use of knip.dev\\n\\nIt's like eslint for detecting unused dependencies or even your own files/modules.\"\n    },\n    {\n      \"entities\": {\n        \"urls\": [\n          {\n            \"url\": \"cdn.bsky.app/img/dummy-01\",\n            \"display_url\": \"cdn.bsky.app/img/opw3iapscc55nlgrx6q2yjah\",\n            \"media_key\": \"2\"\n          }\n        ]\n      },\n      \"id\": \"3ll5ioywml223\",\n      \"author_id\": \"8\",\n      \"created_at\": \"2025-03-24T20:32:00.000Z\",\n      \"attachments\": {},\n      \"text\": \"The joy of adding knip.dev (by @webpro.nl) on a codebase! Right in time for spring cleaning 🧹🍃\\ncdn.bsky.app/img/dummy-01\"\n    },\n    {\n      \"entities\": {\n        \"urls\": []\n      },\n      \"id\": \"1874692207472726401\",\n      \"author_id\": \"5\",\n      \"created_at\": \"2025-01-02T06:40:00.000Z\",\n      \"attachments\": {},\n      \"text\": \"Knip helped us delete ~300k lines of unused code at Vercel.\"\n    },\n    {\n      \"entities\": {\n        \"urls\": [\n          {\n            \"url\": \"https://t.co/kA81p5gkmP\",\n            \"expanded_url\": \"https://www.npmjs.com/package/knip\",\n            \"display_url\": \"npmjs.com/package/knip\",\n            \"title\": \"Knip\",\n            \"description\": \"Find unused dependencies, exports and files in your TypeScript and JavaScript projects.\",\n            \"unwound_url\": \"https://www.npmjs.com/package/knip\"\n          }\n        ]\n      },\n      \"id\": \"1850975288362221628\",\n      \"author_id\": \"5\",\n      \"created_at\": \"2024-10-28T19:57:00.000Z\",\n      \"attachments\": {},\n      \"text\": \"Knip may be the greatest tool ever conceived.\\nhttps://t.co/kA81p5gkmP\"\n    },\n    {\n      \"entities\": {\n        \"urls\": []\n      },\n      \"id\": \"3k3obqi65oy2p\",\n      \"author_id\": \"4\",\n      \"created_at\": \"2023-07-29T17:33:00.000Z\",\n      \"attachments\": {},\n      \"text\": \"`knip` is the best code maintenance tool I've used in my 20 years of development, just an absolute work of art\"\n    },\n    {\n      \"entities\": {\n        \"urls\": []\n      },\n      \"id\": \"1808672067900092505\",\n      \"author_id\": \"3\",\n      \"created_at\": \"2024-07-04T03:19:00.000Z\",\n      \"attachments\": {},\n      \"text\": \"knip is great.\\n\\nA great example of stuff just working. It detects your environment so well and its defaults are almost too good because I didn’t realise it was doing everything automatically.\"\n    },\n    {\n      \"entities\": {\n        \"urls\": []\n      },\n      \"id\": \"1808808208229707835\",\n      \"author_id\": \"3\",\n      \"created_at\": \"2024-07-04T12:20:00.000Z\",\n      \"attachments\": {},\n      \"text\": \"Then was like “this is how tooling should be, this has taken me an hour not a day and it’s faster and more accurate and more future proof”\\n\\nFive stars ⭐️⭐️⭐️⭐️⭐️\"\n    },\n    {\n      \"entities\": {\n        \"urls\": []\n      },\n      \"id\": \"1803013555907698921\",\n      \"author_id\": \"2\",\n      \"created_at\": \"2024-06-18T12:35:00.000Z\",\n      \"attachments\": {},\n      \"text\": \"Crazy useful tool. Thought it would be a nightmare to setup in my Next.js+Workers repo but it has crazy good automatic detection for code dependencies. In short, Knip creates a graph of code use and highlights dead code for you to snip (knip).\"\n    },\n    {\n      \"entities\": {\n        \"urls\": []\n      },\n      \"id\": \"1793913676896031033\",\n      \"author_id\": \"44217212\",\n      \"created_at\": \"2024-05-24T09:55:00.000Z\",\n      \"attachments\": {},\n      \"text\": \"Lars is singlehandedly building the best tool to keep your code clean and your dependencies minimal. And he's so engaging and helpful. Can't imagine going back to not using knip 👏\"\n    },\n    {\n      \"entities\": {\n        \"urls\": [\n          {\n            \"url\": \"https://t.co/dummy-01\",\n            \"display_url\": \"pic.twitter.com/uOuO1ch5ij\",\n            \"media_key\": \"1\"\n          }\n        ]\n      },\n      \"id\": \"1745972825490604506\",\n      \"author_id\": \"1\",\n      \"created_at\": \"2024-01-13T01:55:00.000Z\",\n      \"attachments\": { \"media_keys\": [\"1\"] },\n      \"text\": \"warning: do not use a tool like knip.dev + code search to delete a ton of unused code. It works too well.\\n\\nI just did this in https://github.com/sourcegraph/cody/pull/2705, and my boss said if I end the week with net negative lines of code committed, I'm in deep trouble. https://t.co/dummy-01\"\n    },\n    {\n      \"entities\": {\n        \"mentions\": [\n          {\n            \"username\": \"argos_ci\",\n            \"id\": \"1554000987022663681\"\n          },\n          {\n            \"username\": \"webprolific\",\n            \"id\": \"218833730\"\n          }\n        ],\n        \"annotations\": [\n          {\n            \"probability\": 0.6699,\n            \"type\": \"Other\",\n            \"normalized_text\": \"Prettier\"\n          },\n          {\n            \"probability\": 0.7586,\n            \"type\": \"Other\",\n            \"normalized_text\": \"ESLint\"\n          }\n        ],\n        \"urls\": [\n          {\n            \"url\": \"https://t.co/uOuO1ch5ij\",\n            \"expanded_url\": \"https://twitter.com/gregberge_/status/1730180003453927560/photo/1\",\n            \"display_url\": \"pic.twitter.com/uOuO1ch5ij\",\n            \"media_key\": \"3_1730179997682552832\"\n          }\n        ]\n      },\n      \"author_id\": \"22918124\",\n      \"text\": \"Ran knip.dev in @argos_ci, and boom 💥! Dead code detected instantly. With a good config, it can run on CI to keep projects clean. Installing this tool should be a no-brainer, like Prettier or ESLint.\\n\\nKudos, @webprolific! 👏 https://t.co/uOuO1ch5ij\",\n\n      \"id\": \"1730180003453927560\",\n      \"created_at\": \"2023-11-30T11:00:32.000Z\",\n      \"edit_history_tweet_ids\": [\"1730180003453927560\"],\n      \"attachments\": { \"media_keys\": [\"3_1730179997682552832\"] }\n    },\n    {\n      \"note_tweet\": {\n        \"text\": \"Problem: \\n🚫 Your project has unused files.\\n🚫 It has unused npm dependencies.\\n🚫 It has unused TypeScript exports.\\n\\nBut you haven't noticed, because these things are hard to spot.\\n\\nSolution: knip\\n\\nI just used knip to find and resolve dozens of issues.\\n\\nhttps://t.co/QmN3sNlmbm\",\n        \"entities\": {\n          \"urls\": [\n            {\n              \"url\": \"https://t.co/QmN3sNlmbm\",\n              \"expanded_url\": \"https://github.com/webpro-nl/knip\",\n              \"display_url\": \"github.com/webpro-nl/knip\"\n            }\n          ]\n        }\n      },\n      \"entities\": {\n        \"annotations\": [\n          {\n            \"probability\": 0.8569,\n            \"type\": \"Other\",\n            \"normalized_text\": \"TypeScript\"\n          }\n        ],\n        \"urls\": [\n          {\n            \"url\": \"https://t.co/grbeTqMtt6\",\n            \"expanded_url\": \"https://github.com/webpro-nl/knip\",\n            \"display_url\": \"github.com/webpro-nl/knip\",\n            \"images\": [\n              {\n                \"url\": \"https://pbs.twimg.com/news_img/1729485824314548224/0xZughNs?format=png&name=orig\",\n                \"width\": 1280,\n                \"height\": 640\n              },\n              {\n                \"url\": \"https://pbs.twimg.com/news_img/1729485824314548224/0xZughNs?format=png&name=150x150\",\n                \"width\": 150,\n                \"height\": 150\n              }\n            ],\n            \"title\": \"GitHub - webpro-nl/knip: ✂️ Find unused dependencies, exports and files in your JavaScript and TypeScript projects. Knip it before you ship it!\",\n            \"description\": \"✂️ Find unused dependencies, exports and files in your JavaScript and TypeScript projects. Knip it before you ship it! - GitHub - webpro-nl/knip: ✂️ Find unused dependencies, exports and files in yo...\",\n            \"unwound_url\": \"https://github.com/webpro-nl/knip\"\n          },\n          {\n            \"url\": \"https://t.co/qaOXBlD3eo\",\n            \"expanded_url\": \"https://twitter.com/i/web/status/1691460974518353920\",\n            \"display_url\": \"twitter.com/i/web/status/1…\"\n          }\n        ]\n      },\n      \"author_id\": \"19268321\",\n      \"text\": \"Problem: \\n🚫 Your project has unused files.\\n🚫 It has unused npm dependencies.\\n🚫 It has unused TypeScript exports.\\n\\nBut you haven't noticed, because these things are hard to spot.\\n\\nSolution: knip\\n\\nI just used knip to find and resolve dozens of issues.\\n\\nhttps://t.co/grbeTqMtt6 https://t.co/qaOXBlD3eo\",\n\n      \"id\": \"1691460974518353920\",\n      \"created_at\": \"2023-08-15T14:44:56.000Z\",\n      \"edit_history_tweet_ids\": [\"1691460974518353920\"]\n    },\n    {\n      \"entities\": {\n        \"mentions\": [\n          {\n            \"username\": \"webprolific\",\n            \"id\": \"218833730\"\n          }\n        ],\n        \"annotations\": [\n          {\n            \"probability\": 0.6322,\n            \"type\": \"Other\",\n            \"normalized_text\": \"Knip\"\n          }\n        ]\n      },\n      \"author_id\": \"3126044440\",\n      \"text\": \"@webprolific Knip helped me get rid of over 41k lines of code in legacy codebase 🥺🥺💕\",\n\n      \"id\": \"1729181106715632088\",\n      \"created_at\": \"2023-11-27T16:51:17.000Z\",\n      \"edit_history_tweet_ids\": [\"1729181106715632088\"]\n    },\n    {\n      \"entities\": {\n        \"mentions\": [\n          {\n            \"username\": \"contra\",\n            \"id\": \"973313523790082053\"\n          }\n        ],\n        \"annotations\": [\n          {\n            \"probability\": 0.8474,\n            \"type\": \"Other\",\n            \"normalized_text\": \"Knip\"\n          }\n        ],\n        \"urls\": [\n          {\n            \"url\": \"https://t.co/UgCunYQWzu\",\n            \"expanded_url\": \"https://twitter.com/webprolific/status/1729105865683435542\",\n            \"display_url\": \"twitter.com/webprolific/st…\"\n          }\n        ]\n      },\n      \"author_id\": \"95668959\",\n      \"text\": \"Big fans of Knip at @Contra. Such an extremely well developed and maintained project https://t.co/UgCunYQWzu\",\n\n      \"id\": \"1729157761215369264\",\n      \"created_at\": \"2023-11-27T15:18:31.000Z\",\n      \"edit_history_tweet_ids\": [\"1729157761215369264\"]\n    },\n    {\n      \"entities\": {\n        \"mentions\": [\n          {\n            \"username\": \"webprolific\",\n            \"id\": \"218833730\"\n          }\n        ],\n        \"urls\": [\n          {\n            \"url\": \"https://t.co/ZUYYdvkBJc\",\n            \"expanded_url\": \"https://github.com/getsentry/sentry/\",\n            \"display_url\": \"github.com/getsentry/sent…\",\n            \"title\": \"GitHub - getsentry/sentry: Developer-first error tracking and performance monitoring\",\n            \"description\": \"Developer-first error tracking and performance monitoring - GitHub - getsentry/sentry: Developer-first error tracking and performance monitoring\",\n            \"unwound_url\": \"https://github.com/getsentry/sentry/\"\n          }\n        ]\n      },\n      \"author_id\": \"1632752318994153472\",\n      \"text\": \"@webprolific Just tried this on https://t.co/ZUYYdvkBJc - got some super useful results! Nice work.\",\n\n      \"id\": \"1727040036334424406\",\n      \"created_at\": \"2023-11-21T19:03:26.000Z\",\n      \"edit_history_tweet_ids\": [\"1727040036334424406\"]\n    },\n    {\n      \"entities\": {\n        \"mentions\": [\n          { \"username\": \"TkDodo\", \"id\": \"44217212\" },\n          {\n            \"username\": \"webprolific\",\n            \"id\": \"218833730\"\n          },\n          {\n            \"username\": \"webprolific\",\n            \"id\": \"218833730\"\n          }\n        ]\n      },\n      \"author_id\": \"1513218780167655427\",\n      \"text\": \"@TkDodo @webprolific I've manage to delete 6k LOC in the last 30 minutes 🫣\\n\\nGreat job here @webprolific 👍🏽\",\n\n      \"id\": \"1726807293583609935\",\n      \"created_at\": \"2023-11-21T03:38:35.000Z\",\n      \"edit_history_tweet_ids\": [\"1726807293583609935\"]\n    },\n    {\n      \"entities\": {\n        \"mentions\": [\n          {\n            \"username\": \"webprolific\",\n            \"id\": \"218833730\"\n          }\n        ],\n        \"urls\": [\n          {\n            \"url\": \"https://t.co/Tg5E6C7gqa\",\n            \"expanded_url\": \"https://twitter.com/housecor/status/1713975785273303286\",\n            \"display_url\": \"twitter.com/housecor/statu…\"\n          }\n        ]\n      },\n      \"author_id\": \"44217212\",\n      \"text\": \"knip is an amazing tool. Shoutout to @webprolific for building it 🙌.\\n\\nNot cleaning up correctly has a real maintenance cost. I've deleted lots of dead code - functions that were only used in tests and components that were only used in stories - all thanks to knip 🚀. https://t.co/Tg5E6C7gqa\",\n\n      \"id\": \"1714023231689031941\",\n      \"created_at\": \"2023-10-16T20:59:18.000Z\",\n      \"edit_history_tweet_ids\": [\"1714023231689031941\"]\n    },\n    {\n      \"entities\": {\n        \"annotations\": [\n          {\n            \"probability\": 0.9562,\n            \"type\": \"Other\",\n            \"normalized_text\": \"JavaScript\"\n          },\n          {\n            \"probability\": 0.8666,\n            \"type\": \"Other\",\n            \"normalized_text\": \"TypeScript\"\n          }\n        ],\n        \"urls\": [\n          {\n            \"url\": \"https://t.co/f3fzC5UPtR\",\n            \"expanded_url\": \"https://github.com/webpro-nl/knip\",\n            \"display_url\": \"github.com/webpro-nl/knip\",\n            \"images\": [\n              {\n                \"url\": \"https://pbs.twimg.com/news_img/1729485824314548224/0xZughNs?format=png&name=orig\",\n                \"width\": 1280,\n                \"height\": 640\n              },\n              {\n                \"url\": \"https://pbs.twimg.com/news_img/1729485824314548224/0xZughNs?format=png&name=150x150\",\n                \"width\": 150,\n                \"height\": 150\n              }\n            ],\n            \"title\": \"GitHub - webpro-nl/knip: ✂️ Find unused dependencies, exports and files in your JavaScript and TypeScript projects. Knip it before you ship it!\",\n            \"description\": \"✂️ Find unused dependencies, exports and files in your JavaScript and TypeScript projects. Knip it before you ship it! - GitHub - webpro-nl/knip: ✂️ Find unused dependencies, exports and files in yo...\",\n            \"unwound_url\": \"https://github.com/webpro-nl/knip\"\n          }\n        ]\n      },\n      \"author_id\": \"481186762\",\n      \"text\": \"🛠️ Knip\\n\\n👉🏻 Knip finds unused dependencies, exports and files in your JavaScript and TypeScript projects.\\n👉🏻 Less code and dependencies lead to improved performance, less maintenance and easier refactorings. \\n\\nhttps://t.co/f3fzC5UPtR\",\n      \"id\": \"1696221274039595363\",\n      \"created_at\": \"2023-08-28T18:00:40.000Z\",\n      \"edit_history_tweet_ids\": [\"1696221274039595363\"]\n    },\n    {\n      \"entities\": {\n        \"annotations\": [\n          {\n            \"probability\": 0.9354,\n            \"type\": \"Other\",\n            \"normalized_text\": \"JavaScript\"\n          },\n          {\n            \"probability\": 0.7027,\n            \"type\": \"Other\",\n            \"normalized_text\": \"TypeScript\"\n          },\n          {\n            \"probability\": 0.7208,\n            \"type\": \"Other\",\n            \"normalized_text\": \"Josh\"\n          }\n        ],\n        \"urls\": [\n          {\n            \"url\": \"https://t.co/nKtvr2wEg6\",\n            \"expanded_url\": \"https://twitter.com/JoshuaKGoldberg/status/1693713311836115286\",\n            \"display_url\": \"twitter.com/JoshuaKGoldberg\"\n          }\n        ]\n      },\n      \"author_id\": \"416394303\",\n      \"text\": \"Knip is wonderful at finding out unused code/dependencies in a legacy JavaScript/TypeScript application\\n\\nRecommend 💯\\n\\nHere's how Josh used it to remove code bloat in Centered: https://t.co/nKtvr2wEg6\",\n      \"id\": \"1693944495472046382\",\n      \"created_at\": \"2023-08-22T11:13:34.000Z\",\n      \"edit_history_tweet_ids\": [\"1693944495472046382\"]\n    },\n    {\n      \"entities\": {\n        \"annotations\": [\n          {\n            \"probability\": 0.5513,\n            \"type\": \"Other\",\n            \"normalized_text\": \"Knip\"\n          }\n        ],\n        \"urls\": [\n          {\n            \"url\": \"https://t.co/L8w9V11PRL\",\n            \"expanded_url\": \"https://www.smashingmagazine.com/2023/08/knip-automated-tool-find-unused-files-exports-dependencies/\",\n            \"display_url\": \"smashingmagazine.com/2023/08/knip-a…\",\n            \"images\": [\n              {\n                \"url\": \"https://pbs.twimg.com/news_img/1727384011872366592/W-M5klkJ?format=jpg&name=orig\",\n                \"width\": 1200,\n                \"height\": 675\n              },\n              {\n                \"url\": \"https://pbs.twimg.com/news_img/1727384011872366592/W-M5klkJ?format=jpg&name=150x150\",\n                \"width\": 150,\n                \"height\": 150\n              }\n            ],\n            \"title\": \"Knip: An Automated Tool For Finding Unused Files, Exports, And Dependencies — Smashing Magazine\",\n            \"description\": \"Most of the projects have at least a few unused files, exports, and dependencies lying around, often because it’s difficult knowing when one thing relies on another and scary removing something you’re not sure is in use. Lars Kappert shares a tool he’s been working on that offers a solution.\",\n            \"unwound_url\": \"https://www.smashingmagazine.com/2023/08/knip-automated-tool-find-unused-files-exports-dependencies/\"\n          }\n        ]\n      },\n      \"author_id\": \"15736190\",\n      \"text\": \"Unused files, exports, and dependencies often clutter projects, as it’s difficult knowing when one thing relies on another and scary to remove something you’re not sure is in use. 👀\\n\\n↬ Meet Knip, which scours projects for these unused artifacts: https://t.co/L8w9V11PRL\",\n      \"id\": \"1691120132901240832\",\n      \"created_at\": \"2023-08-14T16:10:33.000Z\",\n      \"edit_history_tweet_ids\": [\"1691120132901240832\"]\n    },\n    {\n      \"entities\": {\n        \"mentions\": [\n          {\n            \"username\": \"typescript\",\n            \"id\": \"809233214\"\n          }\n        ],\n        \"urls\": [\n          {\n            \"url\": \"https://t.co/1mRMwvjIpw\",\n            \"expanded_url\": \"https://effectivetypescript.com/2023/07/29/knip/\",\n            \"display_url\": \"effectivetypescript.com/2023/07/29/kni…\",\n            \"images\": [\n              {\n                \"url\": \"https://pbs.twimg.com/news_img/1696204795353219085/ZjmFlTVW?format=jpg&name=orig\",\n                \"width\": 560,\n                \"height\": 735\n              },\n              {\n                \"url\": \"https://pbs.twimg.com/news_img/1696204795353219085/ZjmFlTVW?format=jpg&name=150x150\",\n                \"width\": 150,\n                \"height\": 150\n              }\n            ],\n            \"title\": \"Effective TypeScript › Recommendation Update: ✂️ Use knip to detect dead code and types\",\n            \"description\": \"TL;DR: Use ts-prune is in maintenance mode. Use knip to find dead code instead. It's great!\",\n            \"unwound_url\": \"https://effectivetypescript.com/2023/07/29/knip/\"\n          }\n        ]\n      },\n      \"author_id\": \"21046936\",\n      \"text\": \"Recommendation update: use knip to find dead code, types and dependencies in your @typescript ✂️https://t.co/1mRMwvjIpw\",\n      \"id\": \"1685298103094554625\",\n      \"created_at\": \"2023-07-29T14:35:53.000Z\",\n      \"edit_history_tweet_ids\": [\"1685298103094554625\"]\n    },\n    {\n      \"entities\": {\n        \"annotations\": [\n          {\n            \"probability\": 0.9661,\n            \"type\": \"Other\",\n            \"normalized_text\": \"GitHub\"\n          }\n        ],\n        \"urls\": [\n          {\n            \"url\": \"https://t.co/vnxNbiJs1I\",\n            \"expanded_url\": \"https://github.com/webpro-nl/knip\",\n            \"display_url\": \"github.com/webpro-nl/knip\",\n            \"images\": [\n              {\n                \"url\": \"https://pbs.twimg.com/news_img/1729485824314548224/0xZughNs?format=png&name=orig\",\n                \"width\": 1280,\n                \"height\": 640\n              },\n              {\n                \"url\": \"https://pbs.twimg.com/news_img/1729485824314548224/0xZughNs?format=png&name=150x150\",\n                \"width\": 150,\n                \"height\": 150\n              }\n            ],\n            \"title\": \"GitHub - webpro-nl/knip: ✂️ Find unused dependencies, exports and files in your JavaScript and TypeScript projects. Knip it before you ship it!\",\n            \"description\": \"✂️ Find unused dependencies, exports and files in your JavaScript and TypeScript projects. Knip it before you ship it! - GitHub - webpro-nl/knip: ✂️ Find unused dependencies, exports and files in yo...\",\n            \"unwound_url\": \"https://github.com/webpro-nl/knip\"\n          }\n        ]\n      },\n      \"author_id\": \"380616577\",\n      \"text\": \"This is worth checking out on GitHub. 👍 https://t.co/vnxNbiJs1I\",\n      \"id\": \"1581910299846012928\",\n      \"created_at\": \"2022-10-17T07:29:40.000Z\",\n      \"edit_history_tweet_ids\": [\"1581910299846012928\"]\n    },\n    {\n      \"note_tweet\": {\n        \"text\": \"Streamlining React projects with Knip:\\n\\nRecently, I employed Knip for a project, and it worked wonders! \\n\\nIt efficiently resolved issues with removing\\n✅ Unused files, \\n✅ Unused npm dependencies, \\n✅ Unneeded TypeScript exports.\\n\\n A real time-saver for maintaining a clean and efficient codebase. 🛠️ #ReactDevelopment \\n\\nhttps://t.co/IbJH2fIzsE\",\n        \"entities\": {\n          \"urls\": [\n            {\n              \"url\": \"https://t.co/IbJH2fIzsE\",\n              \"expanded_url\": \"https://github.com/webpro-nl/knip\",\n              \"display_url\": \"github.com/webpro-nl/knip\"\n            }\n          ],\n          \"hashtags\": [{ \"tag\": \"ReactDevelopment\" }]\n        }\n      },\n      \"entities\": {\n        \"annotations\": [\n          {\n            \"probability\": 0.743,\n            \"type\": \"Other\",\n            \"normalized_text\": \"Knip\"\n          },\n          {\n            \"probability\": 0.7667,\n            \"type\": \"Other\",\n            \"normalized_text\": \"Knip\"\n          }\n        ],\n        \"urls\": [\n          {\n            \"url\": \"https://t.co/P39xg0qCrT\",\n            \"expanded_url\": \"https://twitter.com/i/web/status/1693502146128281657\",\n            \"display_url\": \"twitter.com/i/web/status/1…\"\n          }\n        ]\n      },\n      \"author_id\": \"827145937030148097\",\n      \"text\": \"Streamlining React projects with Knip:\\n\\nRecently, I employed Knip for a project, and it worked wonders! \\n\\nIt efficiently resolved issues with removing\\n✅ Unused files, \\n✅ Unused npm dependencies, \\n✅ Unneeded TypeScript exports.\\n\\n A real time-saver for maintaining a clean and… https://t.co/P39xg0qCrT\",\n      \"id\": \"1693502146128281657\",\n      \"created_at\": \"2023-08-21T05:55:50.000Z\",\n      \"edit_history_tweet_ids\": [\"1693502146128281657\"]\n    },\n    {\n      \"entities\": {\n        \"annotations\": [\n          {\n            \"probability\": 0.9331,\n            \"type\": \"Other\",\n            \"normalized_text\": \"JavaScript\"\n          },\n          {\n            \"probability\": 0.5907,\n            \"type\": \"Other\",\n            \"normalized_text\": \"Knip\"\n          }\n        ]\n      },\n      \"author_id\": \"18191376\",\n      \"text\": \"Found a bunch of unused code, -dependencies, and unnecessary exports. Had just one false positive but overall pretty good. 10/10 would recommend.\\n\\nIf you’ve got a JavaScript package/project use Knip and remove unnecessary code. ✂️\",\n      \"id\": \"1692942539614028086\",\n      \"created_at\": \"2023-08-19T16:52:09.000Z\",\n      \"edit_history_tweet_ids\": [\"1692942539614028086\"]\n    }\n  ],\n  \"includes\": {\n    \"media\": [\n      {\n        \"media_key\": \"1\",\n        \"url\": \"https://pbs.twimg.com/media/GDrwdB3aYAAu4am?format=png&name=orig\",\n        \"type\": \"photo\"\n      },\n      {\n        \"media_key\": \"2\",\n        \"url\": \"https://cdn.bsky.app/img/feed_fullsize/plain/did:plc:opw3iapscc55nlgrx6q2yjah/bafkreihzszb56agbe6dtuf6xfutl65mjg5ethnrvw264d5bljbm377jora@jpeg\",\n        \"type\": \"photo\"\n      },\n      {\n        \"media_key\": \"3_1730179997682552832\",\n        \"url\": \"https://pbs.twimg.com/media/GALVin9a0AAsn0-.jpg\",\n        \"type\": \"photo\"\n      },\n      {\n        \"media_key\": \"4\",\n        \"url\": \"https://cdn.bsky.app/img/feed_fullsize/plain/did:plc:4dsclu5hjmht3shxutxtge6h/bafkreid63o3hwvwpk6emgzj64vywusztofhz2yihuvwkywuuajn4eekvom@jpeg\",\n        \"type\": \"photo\"\n      },\n      {\n        \"media_key\": \"5\",\n        \"url\": \"https://cdn.bsky.app/img/feed_fullsize/plain/did:plc:2gkh62xvzokhlf6li4ol3b3d/bafkreiahk2jbsb2stw276lkrkxrkvialqs25zrpgo2n3cigzfb7qvgfsmq@jpeg\",\n        \"type\": \"photo\"\n      },\n      {\n        \"media_key\": \"6\",\n        \"url\": \"https://pbs.twimg.com/media/G9wBaMFWoAAkgv5.png\",\n        \"type\": \"photo\"\n      }\n    ],\n    \"users\": [\n      {\n        \"id\": \"11\",\n        \"username\": \"julien.deniau.me\",\n        \"profile_image_url\": \"https://cdn.bsky.app/img/avatar/plain/did:plc:4dsclu5hjmht3shxutxtge6h/bafkreiht3iqxcmelwe7oysawrdpynzevf4dt2ndzeln4f5c2izf62zm57q@jpeg\",\n        \"name\": \"Julien Deniau\"\n      },\n      {\n        \"id\": \"12\",\n        \"username\": \"alexharfordseo.bsky.social\",\n        \"profile_image_url\": \"https://cdn.bsky.app/img/avatar/plain/did:plc:y3arowkhgiexpyvmmxvquavg/bafkreiazlcx5fdxylsqtsdng3beq3ayuoc7nzyqcddeequhi7uploqcjny@jpeg\",\n        \"name\": \"AlexHarford-TechSEO\"\n      },\n      {\n        \"id\": \"13\",\n        \"username\": \"fubits.dev\",\n        \"profile_image_url\": \"https://cdn.bsky.app/img/avatar/plain/did:plc:6aglx53tojyuwxwueap5og3h/bafkreiclnob3mnfvo74an6ugrnbdikrkxcskndjojrdtkwf5vwjuu77z5u@jpeg\",\n        \"name\": \"Ilja\"\n      },\n      {\n        \"id\": \"14\",\n        \"username\": \"alexanderkaran.bsky.social\",\n        \"profile_image_url\": \"https://cdn.bsky.app/img/avatar/plain/did:plc:3n4fccuhdomkkebojjdurnco/bafkreiggltcvdoh4igf5yxvrxggysmasv5efn6hffyav2o6swxslbc3lp4@jpeg\",\n        \"name\": \"Alexander Karan\"\n      },\n      {\n        \"id\": \"15\",\n        \"username\": \"patak.dev\",\n        \"profile_image_url\": \"https://cdn.bsky.app/img/avatar/plain/did:plc:2gkh62xvzokhlf6li4ol3b3d/bafkreifgzl4e5jqlakd77ajvnilsb5tufsv24h2sxfwmitkzxrh3sk6mhq@jpeg\",\n        \"name\": \"patak\"\n      },\n      {\n        \"id\": \"16\",\n        \"username\": \"psparwez\",\n        \"profile_image_url\": \"https://unavatar.io/x/psparwez\",\n        \"name\": \"PS\"\n      },\n      {\n        \"id\": \"17\",\n        \"username\": \"k_shehadeh\",\n        \"profile_image_url\": \"https://unavatar.io/x/k_shehadeh\",\n        \"name\": \"Karim Shehadeh\"\n      },\n      {\n        \"id\": \"18\",\n        \"username\": \"cramforce\",\n        \"profile_image_url\": \"https://unavatar.io/x/cramforce\",\n        \"name\": \"Malte Ubl\"\n      },\n      {\n        \"id\": \"1\",\n        \"username\": \"sqs\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1519180377604034560/NxI03WYE_400x400.jpg\",\n        \"name\": \"Quinn Slack\"\n      },\n      {\n        \"id\": \"2\",\n        \"username\": \"jokull\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1943338796122083328/NsFCJwMd_400x400.jpg\",\n        \"name\": \"Jökull Solberg\"\n      },\n      {\n        \"id\": \"3\",\n        \"username\": \"Hicksyfern\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1559181703352094720/NFL2PBbK_400x400.jpg\",\n        \"name\": \"Tom Hicks\"\n      },\n      {\n        \"id\": \"4\",\n        \"username\": \"daviduzumeri.bsky.social\",\n        \"profile_image_url\": \"https://cdn.bsky.app/img/avatar/plain/did:plc:6ykajndqngy2kxozo37mpsgc/bafkreif2xu6bini3tpv2hfauw53ng43zbefgqol36yt4baehcbdfxahihu@jpeg\",\n        \"name\": \"Old Man Uzi\"\n      },\n      {\n        \"id\": \"5\",\n        \"username\": \"gary__tyr\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1947080916045631488/m42HwWiy_400x400.jpg\",\n        \"name\": \"Gary Tyr\"\n      },\n      {\n        \"id\": \"6\",\n        \"username\": \"KevinVanCott\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1419785698887012357/V533Pwsr_400x400.jpg\",\n        \"name\": \"Kevin Thomas Van Cott\"\n      },\n      {\n        \"id\": \"7\",\n        \"username\": \"joshuakgoldberg.com\",\n        \"profile_image_url\": \"https://cdn.bsky.app/img/avatar/plain/did:plc:hwtki3j7oghodc7h6gqnrtro/bafkreicvkrpjmc7yoviwf5vhkecgcgyn23x24ix4aqdwiikcl226c64k2q@jpeg\",\n        \"name\": \"Josh Goldberg 💖\"\n      },\n      {\n        \"id\": \"8\",\n        \"username\": \"beaussan.io\",\n        \"profile_image_url\": \"https://cdn.bsky.app/img/avatar/plain/did:plc:opw3iapscc55nlgrx6q2yjah/bafkreiaj72657msogojjdk4wr76fe3umimydon3pzzrquuj7opcftntrqa@jpeg\",\n        \"name\": \"Nicolas Beaussart\"\n      },\n      {\n        \"id\": \"9\",\n        \"username\": \"MidnightDesign\",\n        \"name\": \"Rudolph Gottesheim\",\n        \"profile_image_url\": \"https://avatars.githubusercontent.com/u/743172?v=4\"\n      },\n      {\n        \"id\": \"10\",\n        \"username\": \"stephaneledorze\",\n        \"name\": \"stephane le dorze\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1587561006317899777/y8cgxyI7_400x400.jpg\"\n      },\n      {\n        \"id\": \"22918124\",\n        \"username\": \"gregberge_\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1722358890807861248/75S7CB3G_normal.jpg\",\n        \"name\": \"Greg Bergé\"\n      },\n      {\n        \"id\": \"19268321\",\n        \"username\": \"housecor\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1963593369306750976/7gPWqEa8_400x400.jpg\",\n        \"name\": \"Cory House\"\n      },\n      {\n        \"id\": \"3126044440\",\n        \"username\": \"pkgacek\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/585047168899244033/kpQ-Reb7_normal.jpg\",\n        \"name\": \"Piotr Gacek 🐟\"\n      },\n      {\n        \"id\": \"95668959\",\n        \"username\": \"kuizinas\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1978959812172894209/yQXTNMuJ_400x400.jpg\",\n        \"name\": \"Gajus\"\n      },\n      {\n        \"id\": \"1632752318994153472\",\n        \"username\": \"imabhiprasad\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1917768224612962304/7RHnN131_400x400.jpg\",\n        \"name\": \"Abhijeet Prasad\"\n      },\n      {\n        \"id\": \"1513218780167655427\",\n        \"username\": \"fernandoeeu_dev\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1513219705317822473/9CR0LjqB_normal.jpg\",\n        \"name\": \"Fernando\"\n      },\n      {\n        \"id\": \"44217212\",\n        \"username\": \"TkDodo\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1803004614372937728/OSIQ4AGx_400x400.jpg\",\n        \"name\": \"Dominik 🔮\"\n      },\n      {\n        \"id\": \"481186762\",\n        \"username\": \"Mokkapps\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1825498937911889920/FtBqvqgK_400x400.jpg\",\n        \"name\": \"Michael Hoffmann\"\n      },\n      {\n        \"id\": \"416394303\",\n        \"username\": \"nicoespeon\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1649018862527041536/9s6Nlwyj_normal.jpg\",\n        \"name\": \"Nicolas Carlo\"\n      },\n      {\n        \"id\": \"15736190\",\n        \"username\": \"smashingmag\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1669591955653685248/mTxJyTGY_normal.jpg\",\n        \"name\": \"Smashing Magazine 🇺🇦 🏳️‍🌈\"\n      },\n      {\n        \"id\": \"21046936\",\n        \"username\": \"danvdk\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/485135064557035520/zATSd81r_normal.jpeg\",\n        \"name\": \"Dan Vanderkam\"\n      },\n      {\n        \"id\": \"380616577\",\n        \"username\": \"stefanjudis\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1465780299380502537/pptBYF44_normal.jpg\",\n        \"name\": \"Stefan Judis\"\n      },\n      {\n        \"id\": \"827145937030148097\",\n        \"username\": \"BKailaash\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/839048279442718720/SiC-Q2Nc_normal.jpg\",\n        \"name\": \"Kailaash\"\n      },\n      {\n        \"id\": \"18191376\",\n        \"username\": \"waldekm\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1670040743006617602/N5VnH4YU_normal.jpg\",\n        \"name\": \"Waldek Mastykarz\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/docs/src/components/Contributors.astro",
    "content": "---\nimport { readFile } from 'node:fs/promises';\n\nconst ENVIRONMENT = import.meta.env.ENVIRONMENT;\nconst GITHUB_TOKEN = import.meta.env.GITHUB_TOKEN;\n\nconst isFetch = ENVIRONMENT !== 'development' && Boolean(GITHUB_TOKEN);\n\ninterface Contributor {\n  html_url: string;\n  avatar_url: string;\n  login: string;\n}\n\nconst url = new URL('/repos/webpro-nl/knip/contributors', 'https://api.github.com');\nurl.searchParams.set('per_page', '100');\n\nconst getAllContributors = async (url: URL) => {\n  const allContributors: Contributor[] = [];\n  let nextUrl: string = url.href;\n\n  console.log('\\n');\n  while (nextUrl) {\n    console.log(`Fetching contributors from ${nextUrl}`);\n    const response = await fetch(nextUrl, {\n      headers: {\n        Accept: 'application/vnd.github+json',\n        Authorization: `Bearer ${GITHUB_TOKEN}`,\n        'X-GitHub-Api-Version': '2022-11-28',\n      },\n    });\n\n    if (!response.ok) {\n      console.error('GitHub API request failed:');\n      console.log(await response.text());\n      return [];\n    }\n\n    const contributors = await response.json();\n    allContributors.push(...contributors);\n\n    const linkHeader = response.headers.get('Link');\n    nextUrl = linkHeader?.match(/<([^>]+)>;\\s*rel=\"next\"/)?.[1] || '';\n  }\n\n  return allContributors;\n};\n\nconst contributors: Contributor[] = isFetch\n  ? await getAllContributors(url)\n  : JSON.parse(await readFile('mock/contributors.json', 'utf-8'));\n\nif (!Array.isArray(contributors)) console.log(contributors);\n---\n\n<style>\n  .contributors {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 0.5rem;\n  }\n  .contributors a {\n    border: none !important;\n    opacity: 0.8;\n    max-height: 100px;\n    overflow: hidden;\n  }\n  .contributors a:hover {\n    opacity: 1;\n  }\n</style>\n\n<div class=\"contributors\">\n  {\n    (Array.isArray(contributors) ? contributors : []).map(contributor => (\n      <a href={contributor.html_url}>\n        <img\n          src={contributor.avatar_url}\n          alt={contributor.login}\n          width=\"100\"\n          height=\"100\"\n        />\n      </a>\n    ))\n  }\n</div>\n"
  },
  {
    "path": "packages/docs/src/components/EmojiBlastButton.astro",
    "content": "<style>\n  div {\n    display: none;\n    justify-content: center;\n  }\n\n  button {\n    color: var(--sl-color-text-accent);\n    margin-top: 2rem;\n    padding: 1rem;\n    background-color: var(--sl-color-orange);\n    border: 2px solid var(--sl-color-text-accent);\n    border-radius: 8px;\n    cursor: pointer;\n    font-size: 1.25rem;\n  }\n\n  button:hover {\n    background-color: var(--sl-color-bright-orange);\n  }\n</style>\n\n<div data-button-container>\n  <button>Hooray, release the clutter!</button>\n</div>\n\n<script is:inline type=\"module\" id=\"emoji-blasts\">\n  import { emojiBlast  } from 'https://unpkg.com/emoji-blast@0.10.1';\n  const container = document.querySelector('[data-button-container]');\n  const button = container.querySelector('button');\n  if(emojiBlast && button) {\n    const cb = () => emojiBlast({ emojis: [\"✂️\", \"🚀\", \"❤️\", \"🎉\", \"🧡\", \"2️⃣\"] });\n    container.style.display = 'flex';\n    button.addEventListener('click', cb);\n  };\n</script>\n"
  },
  {
    "path": "packages/docs/src/components/Footer.astro",
    "content": "---\nimport Default from '@astrojs/starlight/components/Footer.astro';\n---\n\n<style>\n  p {\n    border-top: 1px solid var(--sl-color-gray-3);\n    margin-top: 5rem !important;\n    padding-top: 1rem;\n    text-align: center;\n  }\n  a {\n    color: var(--sl-color-white);\n    margin: 0 0.5rem;\n  }\n</style>\n\n<Default {...Astro.props} />\n<p>\n  <a href=\"https://github.com/webpro-nl/knip/blob/main/packages/knip/license\"\n    >ISC License</a\n  > © 2024\n  <a href=\"https://www.webpro.nl\">Lars Kappert</a>\n</p>\n<script async defer src=\"https://smplnltcs.knip.dev/latest.js\"></script>\n<noscript>\n  <img\n    src=\"https://smplnltcs.knip.dev/noscript.gif\"\n    alt=\"\"\n    referrerpolicy=\"no-referrer-when-downgrade\" />\n</noscript>\n"
  },
  {
    "path": "packages/docs/src/components/Head.astro",
    "content": "---\nimport Default from '@astrojs/starlight/components/Head.astro';\n\nconst slug = Astro.locals.starlightRoute.slug;\n\nconst url = new URL(Astro.url);\nurl.pathname = `/og/docs${slug ? `/${slug}` : ''}.webp`;\n---\n\n<Default {...Astro.props} />\n<meta property=\"og:image\" content={url.href} />\n<meta name=\"twitter:image\" content={url.href} />\n<meta name=\"twitter:card\" content=\"summary_large_image\" />\n<link rel=\"sitemap\" href=\"/sitemap.txt\" />\n<link rel=\"icon\" href=\"/favicon.ico\" sizes=\"any\" />\n<link rel=\"icon\" href=\"/favicon.svg\" type=\"image/svg+xml\" />\n<link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"/apple-touch-icon.png\" />\n<link rel=\"manifest\" href=\"/manifest.json\" />\n"
  },
  {
    "path": "packages/docs/src/components/Post.astro",
    "content": "---\nimport { formatTimestamp, type PostWithUser, replaceShortenedUrls } from '../util/post.js';\n\nconst post: PostWithUser = replaceShortenedUrls(Astro.props.data);\n---\n\n<style>\n  .text {\n    color: var(--sl-color-text-accent);\n  }\n  .n .name,\n  .engagement > * {\n    margin-top: 0 !important;\n  }\n  header {\n    display: flex;\n    align-items: center;\n    line-height: 1.3;\n  }\n  .n .profile {\n    border-radius: 9999px;\n    width: 48px;\n    height: 48px;\n  }\n  .n {\n    display: flex;\n    gap: 1rem;\n  }\n  .n .name {\n    display: flex;\n    flex-direction: column;\n  }\n  .engagement {\n    display: flex;\n    align-items: center;\n    justify-content: space-around;\n    color: #595959;\n  }\n  .engagement > * {\n    display: flex;\n    gap: 0.5rem;\n    white-space: nowrap;\n    align-items: center;\n  }\n  .engagement svg {\n    fill: currentColor;\n  }\n</style>\n\n{\n  post && (\n    <>\n      <header class=\"n\">\n        <img class=\"profile\" src={post.user.profile_image_url} />\n        <div class=\"name\">\n          <span class=\"real\">{post.user.name}</span>\n          <span class=\"screen\">@{post.user.username}</span>\n        </div>\n      </header>\n      <p class=\"text\" set:html={post.text.trim().replace(/\\n/g, '<br />')} />\n      <p class=\"timestamp\">\n        <a\n          href={`${post.url ? post.url : post.id.length > 17 ? `https://twitter.com/${post.user.username}/status/${post.id}` : `https://bsky.app/profile/${post.user.username}/post/${post.id}`}`}>\n          {formatTimestamp(post.created_at)}\n        </a>\n      </p>\n    </>\n  )\n}\n"
  },
  {
    "path": "packages/docs/src/components/Posts.astro",
    "content": "---\nimport { readFile } from 'node:fs/promises';\nimport { Card, CardGrid } from '@astrojs/starlight/components';\nimport type { PostResponse } from '../util/post.js';\nimport Post from './Post.astro';\n\nconst testimonials: PostResponse = JSON.parse(await readFile('src/assets/testimonials.json', 'utf-8'));\n---\n\n<div class=\"no-title-cards\">\n  <CardGrid stagger>\n    {\n      testimonials.data.map(testimonial => {\n        return (\n          <Card title=\"\">\n          <Post\n            data={{\n              ...testimonial,\n              user: testimonials.includes.users.find(\n                user => user.id === testimonial.author_id\n              ),\n              media: testimonials.includes.media?.filter(media =>\n                testimonial.entities.urls\n                  ?.map(url => url.media_key)\n                  .includes(media.media_key)\n              ),\n            }}\n          />\n        </Card>\n        );\n      })\n    }\n    <CardGrid />\n  </CardGrid>\n</div>\n"
  },
  {
    "path": "packages/docs/src/components/Projects.astro",
    "content": "---\nimport Adobe from '../assets/projects/adobe.svg';\nimport AGGrid from '../assets/projects/ag-grid.svg';\nimport AGGrid2 from '../assets/projects/ag-grid2.svg';\nimport Anthropic from '../assets/projects/anthropic.svg';\nimport ArkType from '../assets/projects/arktype.svg';\nimport AstroLogo from '../assets/projects/astro.svg';\nimport AWS from '../assets/projects/aws.svg';\nimport Backstage from '../assets/projects/backstage.svg';\nimport Cloudflare from '../assets/projects/cloudflare.svg';\nimport CreateTypeScriptApp from '../assets/projects/create-typescript-app.png';\nimport Datadog from '../assets/projects/datadog.svg';\nimport ESLint from '../assets/projects/eslint.svg';\nimport FreeCodeCamp from '../assets/projects/freecodecamp.svg';\nimport Google from '../assets/projects/google.svg';\nimport Grafana from '../assets/projects/grafana.svg';\nimport Guardian from '../assets/projects/guardian.svg';\nimport Microsoft from '../assets/projects/microsoft.svg';\nimport Mocha from '../assets/projects/mocha.svg';\nimport Nuxt from '../assets/projects/nuxt.svg';\nimport Prettier from '../assets/projects/prettier.svg';\nimport Sanity from '../assets/projects/sanity.svg';\nimport SAP from '../assets/projects/sap.svg';\nimport Sentry from '../assets/projects/sentry.svg';\nimport Shopify from '../assets/projects/shopify.svg';\nimport SourceGraph from '../assets/projects/sourcegraph.svg';\nimport Stately from '../assets/projects/stately.svg';\nimport Storybook from '../assets/projects/storybook.svg';\nimport Svelte from '../assets/projects/svelte.svg';\nimport TanStack from '../assets/projects/tanstack.png';\nimport TypeScriptESLint from '../assets/projects/typescript-eslint.svg';\nimport Vercel from '../assets/projects/vercel.svg';\n---\n\n<style>\n  div {\n    margin-top: 4rem;\n    display: flex;\n    flex-wrap: wrap;\n    align-content: center;\n    align-items: center;\n    justify-content: space-between;\n    gap: 3.5rem;\n    list-style: none;\n    padding: 0;\n  }\n\n  a {\n    border-bottom: none !important;\n    display: flex;\n  }\n\n  svg, img {\n    width: auto;\n    height: 32px !important;\n    margin: 0 !important;\n    opacity: 0.9;\n    transition: all 0.3s ease;\n    filter: brightness(0.6) grayscale(100%)\n  }\n\n  [data-theme=light] svg {\n    opacity: 0.7;\n  }\n\n  .contrast svg {\n    opacity: 1;\n    filter: contrast(1) grayscale(100%)\n  }\n  .shrink {\n    margin: 0 -3%;\n    svg {\n      scale: 0.75;\n    }\n  }\n  .grow svg {\n    scale: 1.4;\n  }\n  .blowup svg {\n    scale: 2.5;\n  }\n  .raise svg {\n    translate: 0 -6px;\n  }\n  .sink svg {\n    translate: 0 6px;\n  }\n\n\n  a:hover svg,\n  a:hover img {\n    filter: none;\n  }\n\n  [data-theme=light] a:hover svg,\n  [data-theme=light] a:hover img {\n    opacity: 1;\n    filter: none;\n  }\n</style>\n\n<div id=\"projects\">\n    <a href=\"https://github.com/search?type=code&q=org:adobe knip\" class=\"contrast grow\"><Adobe /></a>\n    <a href=\"https://github.com/search?type=code&q=org:ag-grid knip\"><AGGrid /><AGGrid2 /></a>\n    <a href=\"https://github.com/search?type=code&q=org:anthropics knip\" class=\"shrink\"><Anthropic /></a>\n    <a href=\"https://github.com/search?type=code&q=org:arktypeio knip\" class=\"grow\"><ArkType  /></a>\n    <a href=\"https://github.com/search?type=code&q=org:withastro knip\"><AstroLogo /></a>\n    <a href=\"https://github.com/search?type=code&q=org:aws-samples knip\" class=\"sink\" ><AWS/></a>\n    <a href=\"https://github.com/search?type=code&q=org:backstage knip\"><Backstage /></a>\n    <a href=\"https://github.com/search?type=code&q=org:cloudflare knip\" class=\"grow\"><Cloudflare /></a>\n    <a href=\"https://github.com/search?type=code&q=org:datadog knip\"><Datadog /></a>\n    <a href=\"https://github.com/search?type=code&q=org:eslint knip\" class=\"contrast\"><ESLint  /></a>\n    <a href=\"https://github.com/search?type=code&q=org:freeCodeCamp knip\"><FreeCodeCamp  /></a>\n    <a href=\"https://github.com/search?type=code&q=org:google-gemini knip\"><Google /></a>\n    <a href=\"https://github.com/search?type=code&q=org:grafana knip\"><Grafana /></a>\n    <a href=\"https://github.com/search?type=code&q=org:guardian knip\"><Guardian /></a>\n    <a href=\"https://github.com/search?type=code&q=org:JoshuaKGoldberg knip\"><img src={CreateTypeScriptApp.src}/></a>\n    <a href=\"https://github.com/search?type=code&q=org:microsoft knip\"><Microsoft /></a>\n    <a href=\"https://github.com/search?type=code&q=org:mochajs knip\" class=\"contrast grow\"><Mocha /></a>\n    <a href=\"https://github.com/search?type=code&q=org:nuxt knip\"><Nuxt /></a>\n    <a href=\"https://github.com/search?type=code&q=org:prettier knip\" class=\"contrast grow\"><Prettier /></a>\n    <a href=\"https://github.com/search?type=code&q=org:sanity-io knip\"><Sanity /></a>\n    <a href=\"https://github.com/search?type=code&q=org:SAP knip\" class=\"blowup\"><SAP /></a>\n    <a href=\"https://github.com/search?type=code&q=org:getsentry knip\"><Sentry /></a>\n    <a href=\"https://github.com/search?type=code&q=org:shopify knip\"><Shopify /></a>\n    <a href=\"https://github.com/search?type=code&q=org:sourcegraph knip\"><SourceGraph /></a>\n    <a href=\"https://github.com/search?type=code&q=org:statelyai knip\"><Stately /></a>\n    <a href=\"https://github.com/search?type=code&q=org:storybookjs knip\"><Storybook  /></a>\n    <a href=\"https://github.com/search?type=code&q=org:sveltejs knip\" class=\"contrast\"><Svelte /></a>\n    <a href=\"https://github.com/search?type=code&q=org:tanstack knip\" class=\"contrast\"><img src={TanStack.src}/></a>\n    <a href=\"https://github.com/search?type=code&q=org:typescript-eslint knip\" class=\"contrast grow\"><TypeScriptESLint/></a>\n    <a href=\"https://github.com/search?type=pullrequests&q=org:vercel knip\" class=\"shrink\"><Vercel/></a>\n</div>\n"
  },
  {
    "path": "packages/docs/src/components/Sponsors.astro",
    "content": "---\nconst { showAll = true } = Astro.props;\n---\n\n<div class=`sponsors ${showAll ? '' : 'front'}`>\n  <a class=\"smaller\" href=\"https://datadoghq.com\">\n    <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 800.5 203.19\" class=\"w500\">\n      <style type=\"text/css\">\n        .st0{fill-rule:evenodd;clip-rule:evenodd;fill:#632CA6;}\n        [data-theme=\"dark\"] .sponsors .st0 { fill:#FFF; }\n        [data-theme=\"dark\"] .datadog-light { display: none; }\n        [data-theme=\"light\"] .datadog-dark { display: none; }\n      </style>\n      <g>\n        <g>\n          <path class=\"st0\" d=\"M260.87,144.65h-37.4v-86.1h37.4c26.94,0,40.43,13.57,40.43,40.7C301.29,129.51,287.81,144.65,260.87,144.65z\n            M239.45,130.79h19c17.9,0,26.84-10.51,26.84-31.55c0-17.91-8.95-26.87-26.84-26.87h-19L239.45,130.79L239.45,130.79z\"/>\n          <polygon class=\"st0\" points=\"318.04,144.65 301.62,144.65 338.25,58.55 355.44,58.55 392.85,144.65 375.66,144.65 364.8,121.17\n            337.17,121.17 342.66,107.32 360.58,107.32 346.46,74.98\"/>\n          <polygon class=\"st0\" points=\"383.82,58.55 449.28,58.55 449.28,72.39 424.55,72.39 424.55,144.65 408.57,144.65 408.57,72.39\n            383.82,72.39\"/>\n          <polygon class=\"st0\" points=\"457.5,144.65 441.08,144.65 477.71,58.55 494.9,58.55 532.31,144.65 515.1,144.65 504.24,121.17\n            476.61,121.17 482.1,107.32 500.02,107.32 485.91,74.98\"/>\n          <path class=\"st0\" d=\"M580.32,144.65h-37.4v-86.1h37.4c26.96,0,40.43,13.57,40.43,40.7C620.75,129.51,607.28,144.65,580.32,144.65z\n            M558.91,130.79h19c17.89,0,26.86-10.51,26.86-31.55c0-17.91-8.96-26.87-26.86-26.87h-19V130.79z\"/>\n          <path class=\"st0\" d=\"M631.58,101.72c0-29.2,14.45-43.79,43.33-43.79c28.44,0,42.64,14.59,42.64,43.79\n            c0,29.03-14.21,43.55-42.64,43.55C647.31,145.27,632.87,130.75,631.58,101.72z M674.91,131.39c17.36,0,26.05-10.01,26.05-30.05\n            c0-19.72-8.69-29.59-26.05-29.59c-17.82,0-26.73,9.87-26.73,29.59C648.18,121.38,657.09,131.39,674.91,131.39z\"/>\n          <path class=\"st0\" d=\"M784.26,109.81v20.16c-3.69,0.96-6.99,1.44-9.9,1.44c-19.55,0-29.31-10.34-29.31-31.01\n            c0-19.09,10.36-28.62,31.07-28.62c8.65,0,16.69,1.61,24.13,4.82V62.14c-7.44-2.8-15.89-4.21-25.34-4.21\n            c-30.97,0-46.46,14.15-46.46,42.47c0,29.9,15.22,44.87,45.67,44.87c10.47,0,19.17-1.52,26.13-4.58V95.64h-25.82l-5.4,14.16\n            L784.26,109.81L784.26,109.81z\"/>\n        </g>\n\n        <g class=\"datadog-dark\">\n          <path class=\"st0\" d=\"M167.2,98.4l-54.9,10c-1.4,1.8-4.7,4.8-6.3,5.6c-6.9,3.5-11.6,2.5-15.7,1.4c-2.6-0.7-4.1-1-6.3-2l-13.4,1.8\n            l8.1,67.9l94.1-17L167.2,98.4z M83.4,176.7l-0.8-7.4L98,145.9l17.4,5.1l15-25.1l18,11.9l13.7-28.7l4.9,52.2L83.4,176.7z M79.5,110\n            c-6.8-4.4-13.8-10.6-16.9-14.1c-0.5-0.4-0.4-2.1-0.4-2.1c2.8,2.2,14.3,10.4,26.4,14.1c4.3,1.3,10.9,1.8,16.6-1.4\n            c4.4-2.5,9.6-6.8,12.7-11.2l0.6,1c0,0.7-1.5,3.9-2.2,5.2c1.3,0.8,2.3,1,3.8,1.4l10.1-1.6c3.6-5.8,6.2-15.2,3.4-24.2\n            c-1.6-5.1-9.8-15.4-10.4-15.9c-2.1-2,0.3-9.6-3.7-18c-4.3-8.8-15.6-18-20.5-22.1c1.4,1,10.3,4.6,14.4,9.6c0.4-0.5,0.6-3.2,0.9-3.9\n            c-3.6-4.6-3.8-12.9-3.8-15.1c0-4.1-2.1-8.7-2.1-8.7s3.6,2.8,4.5,7.7c1.1,5.7,3.4,10.3,6.5,14.1c5.8,7.2,11,10.9,13.7,8.2\n            c3.2-3.2-3.2-17.5-11.3-25.5c-9.5-9.3-12-8.1-17.5-6.1c-4.4,1.5-6.8,14.2-18.4,13.9c-2-0.2-7-0.4-9.5-0.3c1.3-1.8,2.4-3.2,2.4-3.2\n            s-3.9,1.6-7.2,3.6l-0.3-0.4c1.1-2.4,2.3-3.8,2.3-3.8s-3.1,1.9-5.9,4.1c0.5-2.8,2.5-4.6,2.5-4.6s-3.9,0.7-8.9,6.2\n            c-5.7,1.5-7,2.6-11.6,4.6c-7.4-1.6-10.8-4.2-14.1-9c-2.5-3.6-7-4.2-11.6-2.3c-6.7,2.8-15.1,6.5-15.1,6.5s2.8-0.1,5.6,0\n            c-3.9,1.5-7.7,3.5-7.7,3.5s1.8-0.1,4.1,0c-1.6,1.3-2.4,1.9-3.9,2.9c-3.6,2.6-6.5,5.6-6.5,5.6s2.4-1.1,4.6-1.7\n            c-1.5,3.5-4.6,6.1-4,10.4c0.5,3.9,5.3,12,11.6,17c0.5,0.4,9,8.3,15.4,5.1c6.4-3.2,8.9-6.1,10-10.5c1.2-5.1,0.5-8.9-2.1-19.9\n            c-0.9-3.6-3.1-11.1-4.2-14.6l0.2-0.2c2.1,4.4,7.4,16.1,9.6,23.9c3.4,12.2,2.3,18.4,0.8,20.7c-4.7,6.8-16.6,7.8-22,4\n            c-0.8,13.1,2.1,18.9,3.1,21.8c-0.5,3.3,1.7,9.6,1.7,9.6s0.2-2.8,1.2-4.3c0.3,3.3,1.9,7.3,1.9,7.3s-0.1-2.4,0.7-4.5\n            c1.1,1.8,1.9,2.2,2.9,3.6c1,3.6,3,6.2,3,6.2s-0.3-1.9-0.2-3.9c5,4.8,5.8,11.8,6.3,17.1c1.4,14.7-23.2,26.4-28,35.6\n            c-3.6,5.4-5.8,14.1,0.3,19.2c14.8,12.3,9.1,15.7,16.5,21.1c10.2,7.4,22.9,4.1,27.2-1.9c6-8.4,4.5-16.3,2.2-23.7\n            c-1.8-5.8-6.5-15.4-12.4-19.1c-6-3.8-11.9-4.5-16.8-4l0.5-0.5c7.1-1.4,14.6-0.6,20,2.8c6.1,3.9,11.7,10.7,14.6,21\n            c3.3-0.5,3.8-0.7,6.8-1.1L65,111.6L79.5,110z M113.8,43.3c6.3,2.9,5.5,8.5,5.6,11.8c0.1,0.9,0,1.6-0.1,2c-0.9-0.5-2.2-0.8-4.4-0.7\n            c-0.6,0-1.3,0.1-1.9,0.2c-2.3-1.2-3.7-3.7-5-6.3c-0.1-0.2-0.2-0.5-0.3-0.7c0-0.1-0.1-0.2-0.1-0.3c0,0,0-0.1,0-0.1\n            c-0.7-2.2-0.2-2.7,0.1-3.3s1.4-1.3-0.2-1.8c-0.1,0-0.3-0.1-0.5-0.1C108.1,42.6,111.4,42.2,113.8,43.3z M106,79.9\n            c1.2-0.9,6.8-2.7,12-3.4c2.7-0.3,6.6-0.5,7.4,0c1.6,0.9,1.6,3.7,0.5,6.3c-1.6,3.8-3.9,7.9-6.6,8.2c-4.3,0.6-8.3-1.7-12.9-5.2\n            C104.8,84.6,103.8,81.6,106,79.9z M65.6,51.4c6.8-4.6,15.5-2.8,14-1.4c-2.8,2.7,0.9,1.9,1.3,6.8c0.3,3.6-0.9,5.6-1.9,6.7\n            c-2.1,0.3-4.7,0.8-7.8,1.8c-1.8,0.6-3.4,1.2-4.8,1.9c-0.4-0.2-0.8-0.5-1.2-0.9C60.5,62.1,60.7,54.8,65.6,51.4z\"/>\n        </g>\n\n        <g class=\"datadog-light\">\n          <g>\n            <path class=\"st0\" d=\"M158.87,144.16L142,133.04l-14.07,23.5l-16.36-4.78l-14.41,21.99l0.74,6.92l78.33-14.43l-4.55-48.94\n              L158.87,144.16z M85.82,123.07l12.57-1.73c2.03,0.91,3.45,1.26,5.89,1.88c3.8,0.99,8.19,1.94,14.7-1.34\n              c1.51-0.75,4.67-3.64,5.94-5.28l51.49-9.34l5.25,63.57l-88.21,15.9L85.82,123.07z M181.46,100.16l-5.08,0.97L166.62,0.25\n              L0.25,19.54l20.5,166.33l19.47-2.83c-1.55-2.22-3.98-4.91-8.11-8.35c-5.74-4.76-3.71-12.86-0.32-17.97\n              c4.47-8.63,27.54-19.61,26.23-33.41c-0.47-5.02-1.27-11.55-5.93-16.03c-0.17,1.86,0.14,3.65,0.14,3.65s-1.91-2.44-2.87-5.77\n              c-0.95-1.28-1.69-1.68-2.7-3.39c-0.72,1.97-0.62,4.26-0.62,4.26s-1.56-3.7-1.82-6.82c-0.93,1.4-1.16,4.05-1.16,4.05\n              s-2.03-5.83-1.57-8.97c-0.93-2.73-3.68-8.15-2.9-20.47c5.08,3.56,16.26,2.71,20.61-3.71c1.45-2.13,2.44-7.93-0.72-19.36\n              c-2.03-7.33-7.05-18.25-9.01-22.4l-0.23,0.17c1.03,3.34,3.16,10.33,3.98,13.73c2.47,10.29,3.13,13.87,1.97,18.61\n              c-0.99,4.12-3.35,6.82-9.35,9.84c-6,3.03-13.96-4.34-14.47-4.74c-5.83-4.64-10.34-12.22-10.84-15.9\n              c-0.52-4.03,2.32-6.45,3.76-9.74c-2.05,0.59-4.34,1.63-4.34,1.63s2.73-2.83,6.1-5.27c1.4-0.92,2.21-1.51,3.68-2.73\n              c-2.13-0.03-3.86,0.02-3.86,0.02s3.55-1.92,7.23-3.31c-2.69-0.12-5.27-0.02-5.27-0.02S35.75,27.1,42,24.5\n              c4.3-1.76,8.5-1.24,10.86,2.17c3.1,4.47,6.35,6.9,13.25,8.41c4.24-1.88,5.52-2.84,10.84-4.29c4.68-5.15,8.36-5.82,8.36-5.82\n              s-1.82,1.67-2.31,4.3c2.66-2.09,5.57-3.84,5.57-3.84s-1.13,1.39-2.18,3.6l0.24,0.36c3.1-1.86,6.74-3.32,6.74-3.32\n              s-1.04,1.32-2.26,3.02c2.34-0.02,7.08,0.1,8.91,0.31c10.86,0.24,13.11-11.6,17.28-13.08c5.22-1.86,7.55-2.99,16.44,5.74\n              c7.63,7.5,13.59,20.91,10.63,23.92c-2.48,2.49-7.38-0.97-12.8-7.74c-2.87-3.58-5.03-7.81-6.05-13.19\n              c-0.86-4.54-4.19-7.17-4.19-7.17s1.93,4.31,1.93,8.11c0,2.08,0.26,9.84,3.59,14.19c-0.33,0.64-0.48,3.15-0.85,3.63\n              c-3.87-4.68-12.19-8.03-13.54-9.02c4.59,3.76,15.14,12.4,19.19,20.68c3.83,7.83,1.57,15.01,3.51,16.87\n              c0.55,0.53,8.24,10.11,9.72,14.93c2.58,8.39,0.15,17.21-3.22,22.68l-9.43,1.47c-1.38-0.38-2.31-0.58-3.55-1.29\n              c0.68-1.21,2.04-4.22,2.05-4.84l-0.53-0.93c-2.94,4.16-7.85,8.2-11.94,10.52c-5.35,3.03-11.51,2.56-15.52,1.32\n              c-11.39-3.51-22.16-11.21-24.75-13.23c0,0-0.08,1.61,0.41,1.98c2.87,3.24,9.45,9.1,15.81,13.18l-13.55,1.49l6.41,49.89\n              c-2.84,0.41-3.28,0.61-6.39,1.05c-2.74-9.68-7.98-16.01-13.71-19.69c-5.05-3.25-12.02-3.98-18.7-2.66l-0.43,0.5\n              c4.64-0.48,10.12,0.19,15.74,3.75c5.52,3.49,9.97,12.51,11.61,17.94c2.1,6.94,3.55,14.36-2.1,22.23\n              c-4.02,5.59-15.74,8.68-25.22,2c2.53,4.07,5.95,7.4,10.55,8.02c6.84,0.93,13.33-0.26,17.79-4.84c3.81-3.92,5.84-12.12,5.3-20.75\n              l6.03-0.87l2.18,15.49l99.88-12.03L181.46,100.16z M120.69,58.08c-0.28,0.64-0.72,1.05-0.06,3.12l0.04,0.12l0.1,0.27l0.27,0.62\n              c1.19,2.42,2.49,4.71,4.66,5.88c0.56-0.09,1.15-0.16,1.75-0.19c2.04-0.09,3.33,0.23,4.15,0.68c0.07-0.41,0.09-1,0.04-1.88\n              c-0.16-3.07,0.61-8.29-5.29-11.04c-2.23-1.03-5.35-0.72-6.39,0.58c0.19,0.02,0.36,0.06,0.49,0.11\n              C122.04,56.89,120.98,57.43,120.69,58.08 M137.23,86.73c-0.77-0.43-4.39-0.26-6.93,0.04c-4.84,0.57-10.07,2.25-11.22,3.14\n              c-2.08,1.61-1.14,4.42,0.4,5.57c4.32,3.22,8.1,5.39,12.09,4.86c2.45-0.32,4.61-4.2,6.14-7.73\n              C138.77,90.19,138.77,87.58,137.23,86.73 M94.36,61.88c1.37-1.3-6.8-3-13.14,1.32c-4.67,3.19-4.82,10.03-0.35,13.9\n              c0.45,0.38,0.82,0.66,1.16,0.88c1.31-0.62,2.8-1.24,4.51-1.79c2.9-0.94,5.3-1.43,7.28-1.68c0.95-1.06,2.05-2.92,1.77-6.29\n              C95.22,63.63,91.75,64.36,94.36,61.88\"/>\n          </g>\n        </g>\n      </g>\n    </svg>\n  </a>\n\n  <a href=\"https://workleap.com\" class=\"smaller\">\n    <svg viewBox=\"0 5 156 30\"  xmlns=\"http://www.w3.org/2000/svg\" class=\"w500\">\n      <style>\n        .fill {\n          fill: var(--sl-color-white);\n          /* #171417 */\n        }\n        @media (prefers-color-scheme: light) {\n          .fill {\n            fill: var(--sl-color-black);\n          }\n        }\n      </style>\n      <path class=\"fill\" d=\"M42.4298 10.5439C36.7859 10.5439 32.8051 14.2893 32.8051 20.0761C32.8051 25.7624 36.5132 29.8466 42.4298 29.8466C48.1054 29.8466 52.0532 26.0007 52.0532 20.0761C52.0532 14.562 48.3163 10.5439 42.4298 10.5439ZM42.4298 25.4552C39.7765 25.4552 38.076 23.3434 38.076 20.0761C38.076 16.9451 39.7765 14.9353 42.4298 14.9353C45.0832 14.9353 46.7852 16.9451 46.7852 20.0761C46.7895 23.3434 45.089 25.4552 42.4341 25.4552H42.4298ZM85.9012 10.5439H79.4694L72.4994 17.9227V5.31261H67.2601V29.403H72.5037V23.9478L75.1801 21.3035L80.2586 29.3973H86.2858L78.8552 17.693L85.9012 10.5439ZM87.8787 29.403H93.1194V5.31261H87.8801L87.8787 29.403ZM128.972 13.8643C127.879 11.866 125.57 10.5439 122.787 10.5439C118.023 10.5439 114.619 14.4601 114.619 20.112C114.619 25.8643 118.023 29.8466 122.856 29.8466C125.271 29.8466 127.279 28.8604 128.435 27.2252V29.403H133.676V10.9874H129.407L128.972 13.8643ZM124.181 25.5226C121.662 25.5226 119.927 23.2745 119.921 20.0574C119.921 16.9106 121.658 14.6955 124.179 14.6955C126.7 14.6955 128.435 16.8762 128.435 19.9885C128.435 23.2386 126.696 25.5212 124.181 25.5226ZM146.831 10.5482C144.047 10.5482 141.74 11.8646 140.648 13.8658L140.217 10.9946H135.948V35.0735H141.187V27.2295C142.344 28.8632 144.361 29.8509 146.768 29.8509C151.601 29.8509 155.002 25.8672 155.002 20.1148C154.998 14.4644 151.597 10.5482 146.831 10.5482ZM145.432 25.5241C142.912 25.5241 141.176 23.276 141.176 20.0689C141.176 17.0068 142.911 14.8606 145.432 14.8606C147.953 14.8606 149.69 17.0398 149.69 20.1378C149.69 23.3104 147.953 25.5241 145.432 25.5241ZM104.173 10.5439C98.6988 10.5439 94.8543 14.5261 94.8543 20.2756C94.8543 25.9232 98.8351 29.8351 104.509 29.8466C108.486 29.8466 111.455 27.9459 112.736 24.5752L108.011 23.4841C107.501 24.7761 106.045 25.5226 104.38 25.5226C101.93 25.5226 100.389 23.8531 100.019 21.4025H113.052L113.094 20.913C113.164 20.1706 113.164 19.4231 113.094 18.6807C112.577 13.5743 108.943 10.5439 104.173 10.5439ZM100.22 18.2672C100.738 16.0608 102.325 14.6955 104.222 14.6955C106.223 14.6955 107.642 15.8885 107.916 18.2672H100.22ZM58.3745 14.5965L57.8277 10.9817H53.5585V29.403H58.7992V20.5182C58.7992 17.4188 60.9776 15.5813 64.6513 15.5813H65.4348V10.5439H64.9096C61.6205 10.5439 59.6229 11.8445 58.3745 14.5965ZM28.1097 10.9817L23.8046 24.077L19.7463 10.9874H14.6333L10.5622 27.1865L6.22119 10.9874H0.712115L7.34051 35.0778H13.0333L17.1949 18.7998L21.1268 29.8538H27.2085L33.6216 10.9946L28.1097 10.9817Z\" />\n    </svg>\n  </a>\n\n  <a href=\"https://sourcegraph.com/community\">\n    <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 312.8 55.5\" class=\"w500\">\n      <style>\n        .fill {\n          fill: var(--sl-color-white);\n        }\n        @media (prefers-color-scheme: light) {\n          .fill {\n            fill: var(--sl-color-black);\n          }\n        }\n      </style>\n      <path\n        d=\"M34.8 53.8c-2.8.5-5.5-1.3-6-4.1L21.2 8.2c-.5-2.8 1.3-5.5 4.1-6s5.5 1.3 6 4.1l7.6 41.5c.5 2.8-1.4 5.5-4.1 6z\"\n        style=\"fill:#ff5543\"></path>\n      <path\n        d=\"M14.9 46.7c-1.8.3-3.6-.3-4.9-1.7-1.8-2.2-1.6-5.4.6-7.2l32.1-27.3c2.2-1.8 5.4-1.6 7.2.6 1.8 2.2 1.6 5.4-.6 7.2l-32 27.3c-.7.6-1.6 1-2.4 1.1z\"\n        style=\"fill:#a112ff\"></path>\n      <path\n        d=\"M50.8 40.1c-.9.2-1.8.1-2.6-.2L8.4 25.8c-2.7-1-4.1-3.9-3.1-6.6s3.9-4.1 6.6-3.1l39.7 14.1c2.7 1 4.1 3.9 3.1 6.6-.6 1.8-2.2 3-3.9 3.3z\"\n        style=\"fill:#00cbec\"></path>\n      <path\n        class=\"fill\"\n        d=\"M84 35.1c0-1-.4-1.8-1.1-2.4-.7-.6-1.6-1.2-2.7-1.7s-2.3-1-3.6-1.6c-1.3-.5-2.5-1.2-3.6-2s-2-1.8-2.7-3c-.7-1.2-1.1-2.7-1.1-4.5 0-1.6.3-3 .8-4.1.5-1.2 1.3-2.1 2.3-2.9 1-.8 2.1-1.3 3.5-1.7 1.3-.4 2.8-.6 4.5-.6 1.9 0 3.7.2 5.3.5s3.1.8 4.1 1.4l-2 5.3c-.7-.4-1.7-.8-3.1-1.1-1.4-.4-2.8-.5-4.4-.5-1.5 0-2.6.3-3.4.9-.8.6-1.2 1.4-1.2 2.4 0 .9.4 1.7 1.1 2.3.7.6 1.6 1.2 2.7 1.7s2.3 1.1 3.6 1.6c1.3.6 2.5 1.2 3.6 2s2 1.8 2.7 2.9c.7 1.2 1.1 2.6 1.1 4.3 0 1.7-.3 3.2-.9 4.5-.6 1.3-1.4 2.3-2.4 3.1-1 .8-2.3 1.5-3.8 1.9-1.5.4-3.1.6-4.9.6-2.3 0-4.4-.2-6.1-.7-1.8-.4-3.1-.9-3.9-1.3l2-5.4c.3.2.8.4 1.3.6.5.2 1.2.4 1.8.6.7.2 1.4.3 2.2.5.8.1 1.5.2 2.3.2 1.9 0 3.3-.3 4.3-1 1.2-.6 1.7-1.5 1.7-2.8zM93.1 32.2c0-3.9 1-7 2.9-9.1 1.9-2.1 4.6-3.2 8.1-3.2 1.9 0 3.5.3 4.8.9 1.4.6 2.5 1.4 3.4 2.5.9 1.1 1.6 2.4 2 3.9.4 1.5.7 3.2.7 5 0 3.9-1 7-2.9 9.1-1.9 2.1-4.6 3.2-8.1 3.2-1.9 0-3.5-.3-4.8-.9-1.4-.6-2.5-1.4-3.4-2.5-.9-1.1-1.6-2.4-2-3.9-.5-1.5-.7-3.2-.7-5zm6.2 0c0 1 .1 2 .3 2.8.2.9.5 1.6.8 2.3.4.7.9 1.2 1.5 1.5.6.4 1.3.5 2.2.5 1.6 0 2.8-.6 3.5-1.7.8-1.1 1.2-3 1.2-5.4 0-2.1-.4-3.9-1.1-5.2-.7-1.3-1.9-2-3.6-2-1.5 0-2.7.6-3.5 1.7-.9 1.1-1.3 2.9-1.3 5.5zM124.1 20.6v13.2c0 1.9.2 3.3.7 4.1.4.8 1.3 1.3 2.6 1.3 1.1 0 2.1-.3 2.9-1 .8-.7 1.3-1.5 1.7-2.5v-15h6v16.2c0 1.3.1 2.5.2 3.7.1 1.2.3 2.3.6 3.3h-4.6l-1.1-3.4h-.2c-.7 1.2-1.7 2.2-3 2.9-1.3.8-2.8 1.2-4.5 1.2-1.2 0-2.2-.2-3.2-.5-.9-.3-1.7-.8-2.3-1.5-.6-.7-1.1-1.7-1.4-2.9s-.5-2.8-.5-4.7V20.6h6.1zM155.5 26.2c-1-.3-1.8-.5-2.6-.5-1.1 0-2 .3-2.7.9-.7.6-1.2 1.3-1.5 2.2v15h-6V20.6h4.7l.7 3.1h.2c.5-1.1 1.2-2 2.1-2.7s2-.9 3.2-.9c.8 0 2.5.4 3.4.8l-1.5 5.3zM175.1 42.4c-.9.7-2.1 1.2-3.4 1.6-1.3.4-2.7.5-4.1.5-1.9 0-3.4-.3-4.7-.9-1.3-.6-2.3-1.4-3.1-2.5-.8-1.1-1.4-2.4-1.7-3.9-.4-1.5-.5-3.2-.5-5 0-3.9.9-7 2.7-9.1 1.8-2.1 4.3-3.2 7.7-3.2 1.7 0 3.1.1 4.1.4 1 .3 2 .6 2.8 1.1l-1.4 4.9c-.7-.3-1.4-.6-2.1-.8-.7-.2-1.5-.3-2.4-.3-1.7 0-2.9.6-3.8 1.7-.9 1.1-1.3 2.9-1.3 5.3 0 1 .1 1.9.3 2.7s.5 1.6 1 2.2c.4.6 1 1.1 1.7 1.5.7.4 1.5.5 2.4.5 1 0 1.9-.1 2.6-.4.7-.3 1.3-.6 1.9-1l1.3 4.7zM196.4 41.8c-.9.7-2.2 1.4-3.8 1.9-1.6.5-3.3.8-5.1.8-3.8 0-6.5-1.1-8.2-3.3-1.7-2.2-2.6-5.2-2.6-9 0-4.1 1-7.2 2.9-9.2s4.7-3.1 8.2-3.1c1.2 0 2.3.2 3.4.5s2.1.8 3 1.5 1.6 1.7 2.1 2.9.8 2.7.8 4.5c0 .7 0 1.3-.1 2.1-.1.7-.2 1.5-.3 2.3h-14c.1 2 .6 3.4 1.5 4.4.9 1 2.4 1.5 4.4 1.5 1.3 0 2.4-.2 3.4-.6 1-.4 1.8-.8 2.3-1.2l2.1 4zm-8.7-17.1c-1.6 0-2.8.5-3.5 1.4-.8.9-1.2 2.2-1.4 3.8h8.6c.1-1.7-.1-3-.8-3.9-.5-.8-1.5-1.3-2.9-1.3zM220.6 43.8c0 3.4-.9 5.9-2.7 7.5s-4.4 2.4-7.7 2.4c-2.2 0-4-.2-5.3-.5s-2.3-.6-2.9-1l1.3-4.8c.7.3 1.5.6 2.5.8s2.1.4 3.5.4c2.1 0 3.5-.5 4.3-1.4.8-.9 1.1-2.2 1.1-3.8V42h-.2c-1.1 1.5-3 2.2-5.8 2.2-3 0-5.2-.9-6.7-2.8s-2.2-4.8-2.2-8.7c0-4.2 1-7.3 3-9.4 2-2.1 4.9-3.2 8.6-3.2 2 0 3.8.1 5.3.4s2.8.6 3.8 1v22.3zm-10.2-4.5c1.2 0 2.1-.3 2.7-.8.6-.5 1.1-1.3 1.5-2.4V25.7c-1-.4-2.2-.6-3.6-.6-1.6 0-2.8.6-3.6 1.7-.9 1.2-1.3 3-1.3 5.6 0 2.3.4 4 1.1 5.2.7 1.2 1.8 1.7 3.2 1.7zM238.1 26.2c-1-.3-1.8-.5-2.6-.5-1.1 0-2 .3-2.7.9-.7.6-1.2 1.3-1.5 2.2v15h-6V20.6h4.7l.7 3.1h.2c.5-1.1 1.2-2 2.1-2.7s2-.9 3.2-.9c.8 0 1.7.2 2.7.5l-.8 5.6zM241 21.7c1.2-.6 2.1-.8 3.8-1.1 1.7-.3 3.5-.5 5.3-.5 1.6 0 3 .2 4 .6s1.9.9 2.6 1.7c.6.7 1.1 1.6 1.3 2.6.3 1 .4 2.1.4 3.3 0 1.4 0 2.7-.1 4.1-.1 1.4-.1 2.7-.2 4.1 0 1.3 0 2.6.1 3.9s.3 2.4.7 3.6H254l-1-3.2c-.6 1-1.5 1.8-2.6 2.5s-2.5 1-4.3 1c-1.1 0-2.1-.2-2.9-.5-.9-.3-1.6-.8-2.2-1.4s-1.1-1.3-1.4-2.1c-.3-.8-.5-1.7-.5-2.8 0-1.4.3-2.6 1-3.6s1.5-1.8 2.7-2.4c1.2-.6 2.6-1 4.3-1.3s3.5-.3 5.6-.2c.2-1.7.1-3-.4-3.7-.5-.8-1.5-1.1-3.1-1.1-1.2 0-2.5.1-3.8.4s-1.9.4-2.7.8l-1.7-4.7zm7.1 17.5c1.2 0 2.2-.3 2.9-.8.7-.5 1.2-1.1 1.6-1.7v-3c-1-.1-1.9-.1-2.8 0-.9.1-1.7.2-2.3.4s-1.2.5-1.6.9c-.4.4-.6.9-.6 1.5 0 .9.3 1.5.8 2 .4.5 1.1.7 2 .7zM263.1 20.6h4.4l.7 2.8h.2c.8-1.2 1.8-2 2.9-2.6 1.1-.6 2.4-.8 4-.8 2.9 0 5.1.9 6.6 2.8s2.2 4.8 2.2 8.9c0 2-.2 3.8-.7 5.4s-1.2 3-2.1 4.1c-.9 1.1-2 2-3.3 2.6-1.3.6-2.8.9-4.5.9-1 0-1.8-.1-2.4-.2-.6-.1-1.2-.4-1.9-.7v9.5h-6V20.6zm10.3 4.4c-1.2 0-2.1.3-2.8.9s-1.2 1.5-1.6 2.7v9.7c.4.3.9.6 1.4.8.5.2 1.2.3 2 .3 1.7 0 3-.6 3.9-1.8.9-1.2 1.3-3.2 1.3-6.1 0-2-.3-3.6-1-4.7-.6-1.2-1.7-1.8-3.2-1.8zM301.6 43.8V30.6c0-1.9-.3-3.3-.8-4.1s-1.5-1.3-2.9-1.3c-1 0-2 .3-2.8 1-.9.7-1.4 1.6-1.7 2.7v14.8h-6V11.3h6v11.9h.2c.7-1 1.7-1.8 2.7-2.4 1.1-.6 2.5-.9 4.1-.9 1.2 0 2.2.2 3.1.5.9.3 1.7.8 2.3 1.5s1.1 1.7 1.3 2.9.4 2.7.4 4.5v14.5h-5.9z\"\n      ></path>\n    </svg>\n  </a>\n\n  {showAll &&\n\n  <a href=\"https://birchill.co.jp\">\n    <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 559.6 120.8\" class=\"w400\">\n      <style>\n        .fill {\n          fill: var(--sl-color-white);\n        }\n        @media (prefers-color-scheme: light) {\n          .fill {\n            fill: var(--sl-color-black);\n          }\n        }\n      </style>\n      <path\n        class=\"fill\"\n        d=\"M104.8 55l-.4-.1c-.4-.2-.8-.7-.8-1.2V21.2a1.2 1.2 0 0 1 1.2-1.2c.3 0 .7.1 1 .4A60 60 0 0 1 117 38.9c.1.4 0 .9-.2 1.2l-11 14.3c-.3.4-.6.5-1 .5zM64.9 120.8a1.2 1.2 0 0 1-1.2-1.2c-.6-23.4-2.7-65-9.9-80.7v-.7a168 168 0 0 0 2.9-27.8V1.3c0-.7.5-1.2 1.1-1.2a49.4 49.4 0 0 1 13.3.8c.5.1 1 .6 1 1.2v27.6c0 26.8 2.7 61 7 87.2.1.6-.2 1.1-.8 1.3a60.2 60.2 0 0 1-13.3 2.5h-.1zM87.2 114.6c-.2 0-.4 0-.6-.2-.3-.1-.5-.5-.6-.8a558.1 558.1 0 0 1-5.5-51.7c2.6-18.2 8-29.7 13.6-39.4a1.2 1.2 0 0 1 1.3-.6c.5.2 1 .6 1 1.2v85.4c0 .4-.3.8-.6 1a60.7 60.7 0 0 1-8.6 5zM32.4 87.1c-.3 0-.7-.1-.9-.3a78 78 0 0 1-13.2-22.6V17.7a1.2 1.2 0 0 1 1.4-1.1c.5 0 .8.4.9.8l5.9 20c4.6 15.7 7 32 7 48.5 0 .5-.2 1-.7 1.2h-.4zM80.6 31.6h-.3c-.5-.1-1-.6-1-1.2V4.7A1.2 1.2 0 0 1 81 3.6C84.9 5 88.6 6.8 92.2 9c.5.3.7 1 .4 1.6l-1.3 2.2c-3 5.1-6.5 11-9.6 18.1-.2.5-.7.7-1.1.7zM104.8 101l-.4-.1c-.5-.2-.8-.7-.8-1.2v-30c0-.3.1-.6.3-.8L118 50.5a1.2 1.2 0 0 1 1.2-.5c.5.2.8.5.9 1a61.2 61.2 0 0 1-14.5 49.5c-.2.3-.5.4-.9.4zM9.7 92.3c-.4 0-.8-.2-1-.6a60.3 60.3 0 0 1 0-62.5 1.2 1.2 0 0 1 2.2.6v61.3c0 .6-.3 1-.8 1.2h-.4zM32.4 113.8l-.6-.1a60.8 60.8 0 0 1-13.2-9.6c-.2-.2-.4-.6-.4-.9V85a1.2 1.2 0 0 1 1.2-1.1c.4 0 .8.1 1 .5 4 5.9 8.2 10.3 12.6 13.2.4.2.6.6.6 1v14c0 .5-.3.9-.6 1-.2.2-.4.2-.6.2zM40 60.8c-.6 0-1-.5-1.1-1-1.3-8.3-3-16.5-5.4-24.5l-7-23.8c-.2-.5 0-1 .4-1.4a59.7 59.7 0 0 1 21.3-8.8c.2 0 .5 0 .7.3.3.2.5.5.5.9v8c0 13-3.3 32.8-8.2 49.4-.2.5-.6.9-1.2.9zM55 120.7a60 60 0 0 1-13.3-2.8c-.5-.1-.8-.6-.8-1.1v-30a187.1 187.1 0 0 0 0-5.6 123 123 0 0 0 8.3-22.8 1.2 1.2 0 0 1 2.3.1 518 518 0 0 1 4.9 61c0 .3-.2.6-.4.8-.2.3-.5.4-.8.4H55zM180.6 95A19.3 19.3 0 0 1 170 84a23.9 23.9 0 0 1 4.1-23.7c1.8-2 3.9-3.4 6.3-4.5 2.5-1.1 5.3-1.7 8.3-1.7a20 20 0 0 1 14.6 6.1c1.8 1.9 3.2 4.1 4.1 6.7 1 2.7 1.4 5.5 1.4 8.5s-.4 5.9-1.4 8.5a19.2 19.2 0 0 1-18.7 12.7c-3 0-5.6-.5-8.1-1.6m-24.8-81.3c-1.4 1.1-2 2.8-2 5.2v53.4c0 6.3.8 11.9 2.6 16.6a30.2 30.2 0 0 0 18.5 18.8 41 41 0 0 0 28.3-.3 32 32 0 0 0 18.5-18.1c1.7-4.3 2.5-9 2.5-13.9 0-5-.8-9.7-2.5-13.9a31.6 31.6 0 0 0-17.6-18.1 33.9 33.9 0 0 0-26.5.4c-3.8 2-6.8 4.3-9 7V13.5l-2.6-.8c-1.3-.4-2.7-.6-4.3-.6-2.5 0-4.5.6-5.9 1.7m83.9 30.6c-1.4 1-2.1 2.9-2.1 5.3v58.6h14.8V44.1l-2.6-.8a15 15 0 0 0-4.3-.6c-2.5 0-4.4.5-5.8 1.6m-1-28.4a8.7 8.7 0 0 0-2.7 6.4c0 2.5.9 4.7 2.6 6.4a8.7 8.7 0 0 0 6.4 2.6c2.5 0 4.7-.9 6.4-2.6a8.7 8.7 0 0 0 2.6-6.4c0-2.5-.8-4.7-2.6-6.4a8.7 8.7 0 0 0-6.4-2.6c-2.5 0-4.6.9-6.4 2.6m37.2 31.5c-5 4.4-7.4 10.6-7.4 18.6v42.2h14.9V70.3c0-10.7 5-16.1 15.2-16.1a27.6 27.6 0 0 1 13 2.8c.5-.3 1.2-1.2 2-2.6a8.3 8.3 0 0 0 1.2-4.3c0-3.1-1.7-5.5-5.1-7a33.4 33.4 0 0 0-13.4-2.3c-8.7 0-15.5 2.2-20.4 6.6m64.7-4.3a31 31 0 0 0-19 17.7 38 38 0 0 0-2.7 15c0 5.5 1 10.4 2.8 14.7a31.1 31.1 0 0 0 18.9 17.3 48.5 48.5 0 0 0 34-2.2c1.5-.7 2.8-1.4 3.8-2.1l-6.2-11.1c-1 .6-2.8 1.5-5.5 2.7a26.4 26.4 0 0 1-10.5 1.7c-3 0-5.9-.4-8.5-1.2A17.7 17.7 0 0 1 336 85.1c-1.1-2.7-1.7-6-1.7-9.8 0-3.7.6-6.8 1.6-9.5A17.7 17.7 0 0 1 347 55.2c2.6-.9 5.4-1.3 8.3-1.3a26.4 26.4 0 0 1 17.8 6.3c1-.8 2.1-2 3.3-3.4 1.2-1.5 1.8-3.1 1.8-4.9 0-1.3-.4-2.6-1.3-3.9a11 11 0 0 0-4-3.5c-1.9-1-4.3-2-7.3-2.6a49.4 49.4 0 0 0-25 1.2m48.3-29.4c-1.4 1.1-2.1 2.9-2.1 5.2v89.3h14.9V73.1a20 20 0 0 1 4.5-13.6c3-3.5 7.5-5.2 13.4-5.2 5.8 0 10 1.5 12.3 4.4 2.4 3 3.6 7.3 3.6 13v36.5h14.9V67.3c0-4.4-.7-8.3-2-11.6-1.3-3.3-3.2-6-5.6-8.3a24.2 24.2 0 0 0-8.7-5c-3.3-1.1-7-1.7-11-1.7-5.6 0-10 1-13.6 2.7-3.5 1.9-6 3.8-7.8 6v-36l-2.6-.8c-1.3-.4-2.8-.6-4.3-.6-2.6 0-4.5.6-6 1.7m79.5 30.6c-1.4 1-2.1 2.9-2.1 5.3v58.6H481V44.1l-2.7-.8a15 15 0 0 0-4.3-.6c-2.5 0-4.4.5-5.8 1.6m-1-28.4a8.7 8.7 0 0 0-2.7 6.4c0 2.5.9 4.7 2.6 6.4a8.7 8.7 0 0 0 6.4 2.6c2.5 0 4.7-.9 6.4-2.6a8.7 8.7 0 0 0 2.6-6.4c0-2.5-.9-4.7-2.6-6.4a8.7 8.7 0 0 0-6.4-2.6c-2.5 0-4.6.9-6.4 2.6m32.5-2.2c-1.4 1.1-2 2.9-2 5.2v89.3h14.8V13.4l-2.6-.8c-1.3-.4-2.7-.6-4.3-.6-2.5 0-4.5.6-5.9 1.7M544 38.4v-25l-2.6-.8c-1.3-.4-2.8-.6-4.3-.6-2.6 0-4.5.6-6 1.7-1.3 1.1-2 2.9-2 5.2v89.3H544V53l14.7-28.3L544 38.4z\"\n      ></path>\n      <defs>\n        <linearGradient\n          id=\"grad1\"\n          x1=\"-0.6\"\n          x2=\"1\"\n          y1=\"0\"\n          y2=\"0\"\n          gradientUnits=\"objectBoundingBox\">\n          <stop offset=\"0\" stop-color=\"#57595b\"></stop>\n          <stop offset=\"1\" stop-color=\"#fff\" stop-opacity=\"0.5\"></stop>\n        </linearGradient>\n      </defs>\n      <path\n        fill=\"url(#grad1)\"\n        role=\"presentation\"\n        d=\"M177.7 43.8c-3.9 2-6.9 4.3-9 7v22a22.9 22.9 0 0 1 5.4-12.5c1.8-2 3.9-3.4 6.4-4.5a20.4 20.4 0 0 1 12.3-1.3V41h-2c-5 0-9.3 1-13.2 2.9\"\n      ></path>\n      <path\n        fill=\"url(#grad1)\"\n        role=\"presentation\"\n        d=\"M401.7 31.4v46V73a20 20 0 0 1 4.5-13.7c3-3.4 7.4-5.1 13.4-5.1 2.3 0 4.4.2 6.2.7V40.8H423c-5.6 0-10.1.8-13.6 2.6-3.5 1.9-6 3.8-7.8 6v-18z\"\n      ></path>\n    </svg>\n  </a>\n\n  <a href=\"https://forge42.dev\">\n    <img src=\"/sponsors/f42.png\" alt=\"Forge 42\" class=\"w150\" />\n  </a>\n\n  <a href=\"http://ustark.de\">\n    <img src=\"/sponsors/ulrichstark.png\" alt=\"Ulrich Stark\" class=\"w150\" />\n  </a>\n\n  <a href=\"https://webdriver.io\">\n    <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 64 64\" class=\"w150\">\n      <title>WebdriverIO</title>\n      <g\n        id=\"Logo-Regular\"\n        stroke=\"none\"\n        stroke-width=\"1\"\n        fill=\"none\"\n        fill-rule=\"evenodd\">\n        <rect\n          id=\"Rectangle\"\n          fill=\"#EA5906\"\n          x=\"0\"\n          y=\"0\"\n          width=\"64\"\n          height=\"64\"\n          rx=\"5\"></rect>\n        <path\n          d=\"M8,16 L8,48 L6,48 L6,16 L8,16 Z M43,16 C51.836556,16 59,23.163444 59,32 C59,40.836556 51.836556,48 43,48 C34.163444,48 27,40.836556 27,32 C27,23.163444 34.163444,16 43,16 Z M27,16 L14.106,47.9992078 L11.999,47.9992078 L24.894,16 L27,16 Z M43,18 C35.2680135,18 29,24.2680135 29,32 C29,39.7319865 35.2680135,46 43,46 C50.7319865,46 57,39.7319865 57,32 C57,24.2680135 50.7319865,18 43,18 Z\"\n          id=\"Combined-Shape\"\n          fill=\"#FFFFFF\"></path>\n      </g>\n    </svg>\n  </a>\n\n  <a href=\"https://understandlegacycode.com\">\n    <img src=\"/sponsors/nicoespeon.png\" alt=\"Nicolas Carlo\" class=\"w150\" />\n  </a>\n\n  <a href=\"https://github.com/mintuhouse\">\n    <img src=\"/sponsors/hasankumar.jpg\" alt=\"Hasan Kumar\" class=\"w150\" />\n  </a>\n\n  <a href=\"https://kodfabrik.se\">\n    <img src=\"/sponsors/voxpelli.png\" alt=\"Pelle Wessman\" class=\"w150\" />\n  </a>\n\n  <a href=\"https://www.comet.com\">\n    <svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"10 0 140 59\" class=\"w200\">\n      <defs><image xlink:href=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALgAAAC4CAYAAABQMybHAAA44ElEQVR42uy9B3gc5bX//z3vzHZp1WVLli3LtnA3xaZXOxe49AQiX26KE1IggQSS3JDcJE+ucJ7kpt1AAimYACEhBP72LxCIgVBlijEY995tWbYk26qrrVPe8392dle7qpZsg1d4Ds8ib5udnf3Mme973nPOK2CbbR9hswG3zQbcNttswG2zzQbcNttswG2zzQbcNttswG075U21D8HIsM21i53TcqKO/Wu3OB0BXQG6QB06afs7VGKT4q9hn8LO4nyD8ws5fj9SXqL7qsuNUSvdUVoy3zwVjxvZ6GSHcW2tWL20SfFE2qni/PNzokr7GG1jfYGrqdVDwZiqVJdXGLl5FRQMl7Cm+633aLqTO4P53b+jIJP8vk44nAYDrPrch9njaqeDh3ZxU2d7NN9lirFlwdwpVS1arLXx0KN7Y9MunSax7B6TiNgG3LYTam2LFudFnqobLQ4e8DvHVcwwA4GZdKi9mEiUwO0eJzW9gGKaRzGlwkQKAw4CBINpkN8wA9Q4tGwSQ2dASodikMMZEk61BeFIvUGiU5YVNzvdrpXm3qbdOPO0LnnhzObRdy8I2YDbNjwPzSwaP/G1AkdzZwHy/LOw98BpqstdLXV9JoKRYhD8APnjGCZ8KSdppV7sWs8O9Xfr9drk1qxHCZIIJChGzB2SKAh/brOU5rqQxnsjY0rXBqPGjrFXnd1WvvC2sA24bX2s7tJL1VkVl3n5UPNpSlvrZFOoc7k1MJWJqoRhFMOUqvUbEPV1vh/WiZf8n8lAzGBETOYuFnrQFE3I8e5wFvjfFi71zfzpEzdujKztmL9kiWkDfqp76y/8PLdl1+ZJTLgQRzouJpNPF5HYGGZ4EpGrpAvtz8l+aGAz4kI9ajBCBiNoMKIGoEvraoOkCorvnCnc7kOKx7nGWVbytKvS99KcZ37dZAN+yg0SF3lbXlteYXLsYrUzcgHH9AvJkKPB0t9LZ5y8fQQg457alAjqQNCQCBuAIeOPU88LSEohMRMnzkcm1dHuyvO95SgoeEw5veL1c59YGLAB/whbLWrFnZ9uyzE6telqR9dcbuu8EroxDYYsyji2fHKPc5zRhASJmIyAzujSGDGTLdjjuxZ/npDy2on7KbWf/pt6XkBxqQ3OspI/eXJzHj571R8abMA/gra75ta8vCOhORyOflKEopcgqk0AsyvBAWXJcWUYMiFB2rX4XwlDJgFOeuz4nrK0RsEWDczCgoJT3lwmuE5CnniHBISqhp0FuUvd0yv/95yXfrUhW8OMNuDDPF7Nn/hKiegMXCragv+OaOwyMs0xkHCdjAHiYFJEY7ZkSKcmLcBNmYQ64aOtf8dvKSw5zXji8UzIMz05J//FICGErvp9K3zTJv7o7NvPW0bzs28yyQZ8aMDQ/mu+mu8Jt52thLRPIBi5FrpRlnR3dPJlSPr0SntsaQ0cTZOS1FI3wKnQSQ/ICUnPPgS5koI84f+h5uW+kztl/DfOfuPXq7LNkys2vkcBZ9EiR0CMOVM53HSX6Ax+A6HIpZBmAeJop/Cm7r8n6ZbQ02GTcSTGaIlJRCSlPXZSWVCS2wTkyecoER1PQU6c8uDcI4KZkimJ91jXAEq+nmVMH2OEoxWHXn1v5SO7322zPfhIAPvSWrXdv2Oy2RauEYHIjaSbkxjSnQ7vZYcUid+ipkSHxujUuDsiQpyWFhbfqQFlj/tpT46MKEq31x5ErnRvCIn3kipi7vLiR0oumPk/U//y/Vbbg2exzg7ccUdRV2v99Wjt+q4Sitwc19ks4LDYPuneOsVUYv49rrHjXjsQlyOc2rWUFqHUn7SGSn6FdCQ+7qXTs6bW+0VG+Cf5XGrc3H0FIKTVWXxzklUZ0yfooeiBsbd/fP0bb7zBNuBZZnW1tep9+RXTjfrWu0R7512kaTNBcGePFEk72rjXPqJJtGpx0ClFXFKGJBBO/EWGFOkpV9KBzF6Qc/fmhi5XBMUHAF4YRskZZuGyRbvfbrMlShZZ5xe/WKgdDHxCtAa+QFFtFgO+9NAreyZqTAl06hJtUbYiJVLGYUuQzxm6hbvlxBDlSio4MlS5khF9yZQrJETUO370TwpmTPzF9CULtZN9zE75fPDFNTXKx5yFk43dzZ8XgWANDDmWKfPKRlkCd2Jypi0uR3QJw9LZlPTMnPTeScgpM+qRIVc4Q64kH+8hV5KenDM8uSVXUtGV5EYTXjsjqpLaDsdHnNIdPdx5Y6C98+8AttqAn0xovn6/6/C+ty5BU/2dHIpdSsy5EJxtIUoLrrCRiI6E9Qz90H0ZTsDVDSxnTM5QAmaZkiucIVdSMiYzuiIHkyvoKVdS75MZciUOeTQ6xWhqvSgbAD9lJUpgwYIiI2jM54OH7oBmTEkkQVHWHQ+TExGS9hhDMzmR1sJseWuZKT8yptPpWORKjzh3wlsfS3QlfjLF985VXLBUmVn+xYtffOCI7cE/TI/ITB0fv7lSqz/yZSUa+0/oZiULZMQNssd0Mw52AnAjqSMy5iIhMvLFUxKDE0O+YcsVEpSGdCC5QmnQe8iVHlcMsi6CRld4ll+6JgGwAf8w4Q5+85tT0NV1twiHbwRzDhTrp822/bS8dasmreQoiXTYwgI3FaKLg5mcqAFnhAAHkysJP9998e6ehmWZPIEoHVLkociVZAiRe+SsELEsCTe2zgawwgb8QxpMdnzpSxfyvsZvIhS6AgRvMuWZs0mqxXcmYkq0xBghnbulQIoeTkIuu0FLQC45HfNOefLUwNMykdDKnERYUjoPJfXtu0OHspcUyXhh92syw5Gc1uSpz4eme/UjrZOZWSEi0wb8A7TN06Y5S6PRK9AWuBPhyMVWbDsVEM4quNkaRB6JSkR7DSZTsGVCnopzxyEXIiNDUKRyTxgskq/N9L4WqD3lCicjI33kSgrsAeUK9ZErVkmHZCg+d+WOXy0tANBiA/5BQTN7tqN91Kh5HAh+G7pxbhpuULZobgscCURNtjx31ARYJCVILzlhCYOkRxUZcoWHIlcynSx6ypWemvz45Er30ZWyNLr+vVwb8A/Kc9fUONsDHTchGLxLmDyLAXdqiJRdoUBGKA53JBHrTmnaFIqcOZjMkCvJfKeEXEnKkiHJlW6tnCFXepUzd1/gMrX1MOSKpf01PTe8s8lta/APwHZedZWrMND+KY7EfkBSjmciJe2Qsit3O6IzWmMSMebuSRTqjulQN96ZkFtyRMhkuitlSJFB5ErGwLO3XOEMyPuVKzR0uZL4S9C7Qvnm1qjHBvwDkCUtqnm56Ip9i8ETIdBrOJU9njsOd1uMEZOwUl7i8qMbpH4gR2/IhyNX0oKkj1wRLC2wT4hcsWZ+BGCYqsaaYgN+IqMlgNI+btTHRFfobgZPSgSLs28Cx5p6N1KaO+EG0xGStMdO7zh1l9xIpMvNTpRcQa8Zz0HlijVmGFiuJM4jCSXXG0C+L4rtNuAnBprFNUrgZXWeebD5u2Qa54DY3YeTrICboJkSrXHPbSbYScWpudsbZ0Cemo2kJLipczbF3VDlSpL0/uQKUleAociVXqm2veUKJ84sCI8r6K0ebwN+QqCprRWBFa1nmkd23gVNOw8CbmT8rNmkuQ0p0RFjRIzkY4kkkIRXzfDkR5MrqTySIcuVtL/tI1cyZzwHliu9ErQGkCup4KvqUtsKq8vCtkQ5AXYk3FTlrN//Xwh1zUvAzVk3oLQwNoFADAhmxLlTntJCJAlnplxBRip6CqT41xNJuUIZcoUpPTkkM6QIWWVtmbKEugsaUv/OnG4fMEGL+0/QSn1GD08e1RoC8y5px3024Mdlhz939Wh1545vcFi7ngie9Pxe9unukMEIaJwGKsO1pyAXvSBH7wEdUhUMfeUK+pUrmUeEepRIZ8bJkVmXST2jK+n4E2XMkaXlSvpecruKYpKgXXOum2N78OOC5uffyW3fsOWzIqZ9nEl60+nN2Zf2GpckHVFplZbFiRCQSWmRhoiT+d2U8S0GlCv9Qd5LrlDys1KyhNC/XOETKFfi21dUtc192riN2Htyj7s60geVwTcarqCuzi8wy/JkTmAWem5ANxid0UR5WfdESHJyRHK6HIwy2gIeTa5wRnSlhybPkCsYilwZNEFraHKl+6gnoisMFfVmJLzhZB/7Eb2ESdcbco5x8MDdMLTTIFgkhvrJJOcsucUhk0jIkqie1LyUvqxbgKTyS3o9Hn+vSDnqVL1z93YzSjWTJ0qyOjK9rTjccbmSisiIlAfm9H5kbIOT/1GPAuXMGs/0/qWiJZQhBru9P5HuKSt6ufCTNzaebEZGrAcP136tPLZrx5dh6jMhUnXgWVjAwYywxghq3B2O5+5J8rS2PdlyJTPPnI+j/M161uNq5lzPK9V3Xh2zAT8G2/n1q1z63p03Ihi4AcTedPJU1rENzQQCUYbZXUiQ4UVTQ7NjlCv9hhD7kysZCVrpVNvUwJTTlUF0fOVvSfduqD7PG6fNqliDN07+bzASAadSkXOJGWn6IpEsSF8ws88kJzq5asw9inupu4I93W+k3/zqQaIroveMZ6/oCnp5cvRJtU3nk2eGG5HhydOnzxCiK5x4XPW6d7srSh4peiA7WiuPOMDbvnVzhWxquoUMbVp8sJ613juZRJVZJEyZGdacXG2Hccxypc+MZ39yZYDclR5yJQn5UcvfelQA9ZUrpFBYKc5fMvqMqStRlx2/w4gCnGtrnB1N7deTHp3HYEc6sJt98W5DAsEYw5TUay0H2T2MS0E2kFzhjIUgesqVzMf70eTpwspB88nphOWTW48ZjlzvGyjIe2zsfd+KZMtvMaIA7+pS5lBX8HPMsgSCsrYjQPxHD2mMmM7py3eGTs4M0xEGlytyALmS0tIYKEFrCPnkPcrfjjmf3DqZDNXv2+CbXPWLM5//3e5s+i1GDOAdP/1qgblj2wIytVnWtRmctXDrOhCKZcQpqGfGV3fedEojDyBXUpAjs6K+OwGqrybvV64Mlk+eqcmPMZ/cij66XfvcY0p/Zo6ZtTzbfo8RAXj86hm868AlRix8JRO7QOAs3leEdcAwqYeCoh5CJAnlEORKSrv3L1eGn6B1XOVvPQaeia4q5HIecBTn3Jt3MPe56jdu023Aj8Fa77m5zBls/IyQZoUkRrY2LIpDoWtAOJYRWaB0U550v5F0f7/jkSvdJ8QAIcThyJXh5JMnm+ya5PNsc5cU/zw/r3BJ9eoHYtn4m2Q94HW1l6pqa+AqqUUuhGA1m1txSQlrUseUGdKkV7SCMsGndCyajxJd6U+ucIbXP265MuTyN2sJorCam/Ome3zZ/S5fxevVWdBkc8QCfkY0vwLRI58gyGLO4sQCa10cA4jqGQ20MzDijJhySq6IjAIYQcnQ4DDkSs8Q4vHJlaPlkyf7JpnCoTY5CwueUSaMefjMxQ9syvY17kV2a28mCgfnka6dzWA1kfhw8pvP99u3G4yIxjBM7p56omSeSbqJfDqM2PuxtCdPh+Io2UgwM0cls4oGfXJDON3wPgVod15MRq5LKneF0qved+euUMZpkG5yL4VDaVML/S/kVFfcWTz73P+Zs+S3G7Md7qz34J0/nD+etPBNzEZhOizIWem9DROIakmdmxE3Rj/SpL/H0pMsveUKDJCICEUJQcojUqJJzfG3Kl5fGxQRkprZQSzN5JDTJRQ1D9LMMbrChXqga5QqlNHMXCQ100vMrrjQ6K/8jTLL30jGv4SpqGoQirJP8fpWO/NyXxCTq947/Q8/bhwJYCMdn8/WSZ1a0Rlc8SVqb1vIUo7Oxn4mmYAHw0BHZ6rTa6qyhbpXLutO6kiF2VJFCKBejeWt72mAlC4S6gG43ZvIlBucY8fuJa93d2z79uaiqtMjrivP10pKzjZw2Xg90WwNiVVaD7zr2PXM+w65YpMz3HgoxzWlYpze2FKtH26bQKpjuqHrY7grWMgQ/oSDE4o1fo3fTBlRVDUgcnOa2ZA7lRzPRseYsStz/O5tEx/6eSdGoGUt4OGff6E8Vr/99xQJXgsikc37Gvfe7R2EaCw5ypNpbU0yvZICkkBLmREh4W6FY31tcrqaSVXfdRSU1hlSWzlu7pUH8m+/vf24T8K6OrV5496C9jfezA/V7y9wlY8dLTxOL4iKmBGWhhHQm5vbomGjrfCCszsKz720ZfSCK0IY4UbZ6r2DvPYm2XTw1yzN8qxuY05AOBIHPFFv2R1xSDrjhHdOe21wxvqUiYGGhFA6FZdnHeX5X3L68pc5/IXbx+fnB2jhQvlBj3GWLFkiNtfU8EIiiY+gZaUG78hf5xf72q6RbJYmMr05iwfCgKanoxA9YxTp2HOmJkeitzeDRJdwujYpuf5nxeixL7pmn7+z6pZboh9e3N7S0iY+wpadg8zD0ZlsxC4iwSqyN+UkMbjUE4B3hyJ6tV9L5V0nIhzdKbMmKWoj+XKfyR1V+VjlmV/dRLfN0YGHYNtHHHBetcjRsfipiyD1MmS594ZVa5kxLd+dLy2T0e7M9NPElI1QREi4vG8ppWV/QNH4ZeN/8Ysu4BGbxFMF8PZn/j5a1ULzJFmrCmf30WOGpqerXtDPXGGGXGFSlEbKK3zKWVr86KT7/7p1JIXbbMBPkDl8znO5y6gmsoqIs/rgSZOsavn+VgvuGeuGLhzO/Q5//u/lhWf9ufprP23FA0/Y9J1qgO984X4Xlj15AVgfk83aG8lZPkMDTDNjCr3H1HcyYYqgKS7vKioouddp5j1f9bWfRm3sTlHA8ze9PA4wLwRBxQi4eusGpbusMvUAPZkZqAm3d5VaNvbnWtHkf1UtzN6kpI+qiSwKSZAa06ZLPVppJVVl+S2OsW6kgicZPdgomUMi2FA83nXOUeN/1an5X5xuw31qe/Cdd17lFIXBuRBchGyfcqDEpI5hpPK+uUf+dvwR4fLsc44q+0WbnvPPOQ89pNuoneKAF5xRVoSdG6oBqYyEflumJAtySuasMqe7Q5GiNAqf75dUnPP8nIU23Dbg8R1paZ4siKdLAmX7ANMC3EznT3VnW8eViRAdSl7+4+bUyiervvuoPaC0AU9aNDRJmtGiRBVAdg8w417aNEV3fmOq+gYEqXq8y9SJ0x6q/u5vu2y8bMAta/3r1/1Uv+EiCPb0bJqdpYBzojwtta5NaqpSOJ1bKL/gt+N/+Nu9Nlo24N3mrl/nMyOd49gqKcn+8CAb6fg30n35wo7cvCXqpHnvAEttsmzA0yZLKydxw4bRiegJZX0EJS5RpEzft9qtOdwbnMVjnhrzrezp6mRbtmjwzsbpQnCFFJz1yVUJqMlaelt09+IWzUpBwSPlZ0Z220jZgPf14OEjxSR1N4kRED6x6rtSoEurAl14vOsco8f/i+Y/aNpIZZed9Igzr1qUp7j9E0EsICSy/cbE6NHPVlHC8HpeHPuDPzTaONkevK862fJqoQJ9CgkIxghw4Km+JImJHVac6gYqrngZduqrDXi/O7Bvh5dVvZDTPcayfpCZ+ksKR8lf8Nr48eP32SjZgPfPiwNOlrrT6hE24IuYe7BPJ3H9+UTPyUToW6gdUNQ36ZaF9oylDfgA7I6eVMHNm3PTI7dUoIJNCCUMxdlBLFulobeRNA1WhEqKsxAkimHqeZCGh5nUD9ODJyKEkuF0bHZWTN0KvGiTZAPejzOsrRVhdfcoEnAnlkKK/4+ipDjryeldwyTepcKJu9lZcEA9tLbV7Nqtq6UzHGpedbEeaR9LgYYqUzcuID14HkxjDEM68CEJeRKKIZye9R2BhkM2Rjbg/dv06WRG1heAoJCAhFAOkcv/AvLKnvJOmLqaLv5ZO/Burzc1A3i1EYC1yCiv+Obfwhu3XyDDRz7DWvDfiI2CDzo6RAoABV3OUeVrKn/4pI6FZJNkA97XVre/KqaJYKEhICAcW9ld+CtZPPnZvPmPtAHPZsjeOndgy24viSjlTpkdBv4VI0o0xaHz72sDsLTr0Zr3HBy8yejcfyfM2JQP0pMLYeV/H5EdhzbbhcMjIyZwkiTKNGeo2PUbSPNs4SpY6Jl82Ys0d6HBm2ud0f3vlrOqzuLonsnSVCqhugvBJEhqbcShJsqZsFWRzvWtsfKD5dc9ZC34zy/c7wodeOYGGT70QxjatA/Ck1ttktsFIqHSZTRuxmfGfuvBgzZGtgfv34q2EBuTo+Qp/JXntmVLcQ9R5LUbK2MHXrnCjLbfBNanQ8ZKCdJJRgowhsnCpM7dbVIo2wrc7c911d20NActu2junbFVixY9M93//5xG8MCP2dDGnfiGnQxSGcIpmsdceH0X8KBNke3BB/Lgl6rRaUXnut35G/b5x+qlcuVcDjbcDj1wCbPMzVj9PCNJnDIXoGKyIi2edfCOeTDcUfSP0vlLgi1vfyfXuf3d74tg051SsveE7jMzjKCQwSOen1U+sOEHNkI24EcDRhzacLcnt2n1fA4fuBtm5DQGK+heXvToHpWtjlGuBnLkP+ilsgfp2ufbY09/dqrevP5h1oIXZHxbkyAMCKGDmViaLmsx2WHqdTOmxoIt/u9V/HLVfTZCtkQ5is2nnOYj18pww/dgRiYlcvSGs8BropaGzdg44rZvhVyKxqtu/QP2/NsOw737BZaRsyClA0I9Qg7POmJtGzzFR2BEHYgFK0hxzmA9OJkl5yXX/aCjnVCKW9FdpYVBGx8b8KNauC58BsIHvg0ZTcJ97I3updSLobV9LdK6u95Ts+jvtORvr1CsvQZOZ6PDN3qRs3zWe1i2sgUL37Cy/na9cL9zbGx1hRHYd5USa/2CjIVnsmD1aBkDDElQTTs2aAM+uHWu+GIht6y+HWb09OSSY8exikPybWa00gwf+ErszfmrNd2701F02q9VoS53fvL/7SIQN6//pW/UNeNz4y+dVNEaoPI/7+ZVixbF6l/axm07f8Ra6Nzk+lADDzKJ2OFSpY2PDfigpgT2zuZY55VgdqSXUjruYQWRGZkjYx1X5H3q+49gS+nfaPp0jTff49Sb552hHXz230MyNotIgFt2bA68fPnLOLxplevGm+uiTy1aJDsbJoL10sE/wlq+xmXjk/2mnLTB5YpveszI7ttY75iXXIHxRG7dBRllZ8f+f9FZdwR58WKly1h6tdlV/wtoh29ivWsG613TyAhcQGZstuk2mh2rd2+P+sobSGs+k8zYlO6hQJ9bYvkG0+C3f/b84eU2QtltJ63gIRrcWSIjbeeBSTnxfSKYIM1ZXWE5JX5PL358iuja/T0YobOYydVdBs/sYD1wugzu/a5eos3Mve6hFuH1vQJVDaN7+b3et8SiwArB1uA24INoo5xRYwEek2ydc+JhkbFiEdw2Lv5PQ5oXQUan9/qcZLceUtiMztIjzVdai/kWT97JqtqZgjnDc0sS0ElRoiRgQNVUGx9bgw9oeqytnBg5H0wiB4FhuBThGMW80xV+6YtTGfD0HcSm1vCVbg43TVhSA3GNw9kKGDGkC6CZSAmzcG8jElvhKW5BLJAnnM4WZlbiit/GyAa8r4jQu3Ik2PnBfQA5TMWbB0wiUl3BVJHZAONSk1VfqGYaOBKTThLUvQqJEI598JU97MireqZ1z5b6Ms8oAy5NOVJypeIB7EiKLVEG+GCh6oQPcuk6NgWZWvxiAeFYIyA6BiyJI6VVza16nxZCstlZBEUICLBQHS3CV/Zb70U3/MZ1+eNby2bP1uFvyAmiJafEvcW0MwltwAeWyM7iFggKf3CfQAYirYfiEoLJ9w5chS+DRKTngJaZCLpw5NVJR8Uya/BrYJ3wlj4uVLUZTv9yHn3GUzT67hC/fX1uqGXLJ8OxwO9I5UfDnYe+HnntU5U2Qjbg/WsjSfUAN38wlcYMUjwd8Iy3Wjn45i5pVoum/JhcxY+R4q4HiTCRCEH17CN36Z+kt/xHMrfIiANbOPnillbPab9i35g/QXUt9V7020bmxUok2PpphA7eJ/XAfDZj18jg3v8xg+u/z2s/l29jZGvwPhbSoo1OR95GGNGZH0Q8niH2GIXjd6Tuuy54chuvXPDDcDDwD3RuPg2ssMiftsv0la0SnQfKRXPd/5pGdFx4d9NThmvck2rR7EdcsYDDqija0V4gtfC/s9RGJWKFDGL2wYxd0XXowGMAVtgo2YD3sIJ/WxyIvjrvVRlrvU5KI/+EJjaSiCru/DdzCj09mvHQOX9pBfBy8gZgW+Jke3b6F0ytc0FcOJEeyM8PNb/umrtlbyLL8FHAHXYwwS/IKjVOr8TDRq6iN+faGNkSpZ/ABTF8E96A4luFE9pSliGEaxd8FX+n6gdiQzsf1DARaUQkSTjbnT6vZq1rmVq/vaMoIAhbJVPGOjsEKN5dav5suyeK7cH7N/eBwIFQbtGfyQxPYdbHHH+vE4YgtRPu4ic9cuZG4OmhnWx5U5+j4B4fwGVEzmeXBsoPA6vTz5++IKS9VfOoznqptPLLhUqKukdx5v964xa33Qs8i+2kTze3vlDr9+CF75nhQ18Fm/5jn9W0Fu/TyOl/mvxn3u2d+8SBYb2bWQX2qURVAzbxiQ9CWdHPkkw+EubmlXLGxrlzFxo2Rjbgg1r4vS9VcMv6/5aR5k8DMm/4npxBpESh5r7s81d8n+a9tPmYTpHDi3M6dr1RlG+2uGKqQ+qFZ3TkNM7uoLlzjZ4nQ3K9KdtswIcM+YoFY7h1yxc51rqAWa8ED00+WXOOwtFEjsJnvPnjHsBF/9g5HPj21tW6S+SO04S+72IZ6zqbhDKe2cwFCZ0gmoj1bfCUrpCc/67v8meO2GDbgB+zNb/8WZ/P3H8FGe0LOBY4l2AUspUrjoyELGtyhpnJECQ6oXq2wV3wF7Nw9jP+c37XOpzPC6xcUKR0NtzIoUOfYSN0JsHI7VnezGAWJimOJjgLnidX4cOe9oK1NH+JnX9iA36sSpopvPKOUejYeRbpnR8zYy0zoLjKidkdlwaCoDPrh8mRt5XdxXWKUvCe+7Ip+1ONgIZqweWfKBWdR+40Ig1fJmmUpJDum5Bl1T7HTyoNwr3B4Sxd6LzmG/8imm9DbgN+nLC/cJULuTl+3TNqNJlw6SRJVYVmxIwWj+NAO2b/M3Is+SC89q78UON7/0Wxpq9I0yg6+sA20fLeWiFLca1nX+VduVe+/qaNjw34h3cyMNOWLfc4pkXKGbNvNQYCn2trRfTit2uMzt3/h2GHJq1IjUnuoiVcOOuunAsfP2wjZAP+AYNdp2qv/KoqZrZVq+7iKmapKYjtZnP0Lnf7dQdpfk8pwWu/VxI+sPRBGW37RFqWDO8TidRWxVv1Nc/Vy/4/GyEb8A8O7rdumRDqWPN5GJFrYUYqGeQFWBIpASjencJdtFjmjl+c6WkjL154mRE++ASkXn7sX58hnMV/jU774h1F1XcGbIyy10Zs2VV0xWcmhdrWfx+xthqwzOFUBZrlY3Uvyc7R0oxWC2mU8MoF99M5f2llZkV7/YrTEbJ09zFfMxILHOtT1S3PlwKwAc9iEyPSc9fdnmO2bP0KR478B1tw945NW8FqZhkbZUaab421N8znuktV7LvHYRjaRBzXihBxcU9gI1CsRjb5bIRsDz5ka197V7638Z3SUKzZ6ck9L7CnfFrz9OkLtd6vi6H5PNbbbgRL78Bt3pLaWmqjjUjjLeScUeds3lLPpubLcPbHI/4Vh2C7st724EPxyLVq5KV5FzgPvvMTQ+t83CXFU2Zk+x+qGl79cvi1a8ZkvnbxYigytP8ylsaYIcsMMzJZhvfOgr/GJOHsohNRZEFk6tIuWbMBH4IZ2uvnmOEDvzSjTV+RRugcyeZ0jrVcJYMHfiJDB+/it7/QnXNdE/qcQ8aOjAVL51DH0SxNn06usZhWw6R69jCgH9/InEGO3MN68RldNkI24INa4NU7iqLRjrtghM4nZpGsmGFAELORB6PzUxGt5XLmpBw45xyVFYdzOFoCIEGkOohIJ0NfS8Jx5HhSSqTVSsK5Oi//+ubuT9lb5+bt/1fM9b8viA9mbbRsDW6ZEnmnQhrB2Yk2O6me4BmrrbJRyuGmi7csmb4UgIbDd0QFT2mRJCSsE+LoWoKIY2SG2uL39MIxW5TY4XfZ1MYcW6kcs1CdjeTKf43m3BZurLu2ODfadXFo0zfPZ+GsVCCjvOHBbZG6a99y55avoTkPhW3MTmHAhcPjl7EWz0CDRSnZQbEjpdNy1MRzl8EUr1eulh3bOsFmwRCABIS7STh8Vn2a/5y/tMZeufJvmhk6G2asanjhQus0NISS+4qnYEYd/ytYFu7aezcb0U9CRseIxIr2AAvNNGLbYtHW3/PLl/+VrnglZKN2ikoUQ4ZbSTg7B5IMQoio4inbh4Y8mYyNcBRFdYojZyUNraehRo7clyNFU9enHnDmTHtZKP4/CqG0D6svIkFnR+6bnDf+/naRY0Q07W5ogdvYjI5lJiGZrCg5QzrZCM40wodqw4jdzIsX25LlVAU8p+jiA6TmvckgrTdsFsBC3Uc5JS/Rbau7B4b5857YL9T830BxryfAHNjfsg5n7jKHf+KDhXMe6uze7vn3Rbz+qX+Ee9S9QnHvA3jwzECrLyGFSPW/4sif+UPfJc+u87a8e740wzfJRKiyTxw+MbiNlSF05LPRkifG2qidooDTuQ8EKLfqYXLk1QkSYRBkHKi4FIBQmxRH0cPuZtf7vVQ1uzw/fsXhKv0hqd4VJCiAOOjW+6yma4YQSotw5T3PudN+9JPXZ6zv87lzn2zxjj/71yJnwg+EM7+OSLSCSE9/vrUdE4SQUNw7yV2yyFky5bueSxa/y4AijfD5bETHDLLcSqL63gzNlOFDU2zUTlENHrefL5ux5vuXi7uM0KFPUrBhNsuYm1yFh4Sr8Fn36HGv0OmPR/oCOteorcUL37rsym0iEr1caC0XSr29EBBMrqIm4R1Vx+6KOt+FjzYtnPd0vzKEpv8+yItrFkfLRy/nUOPZMKMXysiBiSx1h3X2uwtb2Vm6hlhbHig7d/Po0/8vZHHb8qxHCmclAcrR9Q17TGdBqY3aSXKg2bQzvLnW2Vb/hLsw2ErIH2vg8vXhoeR7My9WsOweD440xUU1CvLnGLj85Uh324chWsOKb3p8DY85u0euJbNMXHZPlKhXTWbzy77Qmnvu5VD9rUc9hIQw+ad+KeeKV560cTvFAR8pxswUef7cu8xo00/B0j3wYWQQOfYruWX/6blyxTt9t1MrgHsw3BPRthGkwUekVyBi9ox5U1E9WwbPaiFJrvy33FMu3dobbH73kmqsee0GrLmkhtecfT6v/6yduPVR1eDHai3bvpPra1heIFr3uuECIo4iQ5Re0pn7fHE7LVwoj91D14rQ62+VRFu35Su+UdH80KQmmr+kR9KXN69kYyjiv49M+T8sIxMAUjK3QCAdjpz3ha/kD1T5s/YM7y+w7pLrIPd+B6HANDCrEK42ODr/yisX3JtsL2fbqSxReO0N+eGmlvOZ9Eughc9iraM0sRCDrwvO/O0QRp3mn/5WQWP04HAr4JlBxmvXzdGj++80I60zyOE7oniKHnGbef+gq1+M9Rwz1DhD+w/fCL3l89CCM0GcC2YJUlvh9L+luMsedv/bv5ZntprgrZ8pQ+fyRxE78u/pw8+AcB6Bb+oX6azl/7SxPHU9OEVf/48JkQM7bme98ybIWDkzHN1xZzMEREIXklBvchob6oIF4+9nrnmLaOiQdz7/qXwVm7+OSOvN8ePDegiSUBHxeuthtZrNjMIs0Wq5dvE9q7YvD7e3TiXVO5lJhp1aaGuYT9+Wc/l9bX0+oPWFSkh5Zh8/I7UCsDyPd77wMlVfHbPRPAUBD75200wjuGkhYp1XMtjT9wKUiEmzNPIRa7mOZGxs5NXKHzMvXjrUNg+Kj0fJtsBZBFZTuTFSD1ZRx46JvQGP20JaKBcCDQAamBe/BtQkm3a+PsApak13iv6UvSVzJlXbVJ6Kg8zAygVFFN71VRnruCYB96DpgAyQynrXmTJY/239zf9v1lA/J8ehhgUpbdaETyLbK76pLuks6Dj64HO+edSIiP+SA3Dkr09uP+OXUFthGKuJqm3vfaoBbq1Q2X7w31nvup5AjkEXlOr2htZfhc3g2XpXw2ebX/720KIUjROahbfsb0Su/QBrJNR21Vnwj9xxZ608IV9mxhkH4Zr0Gyj+1SCKAqxDeJrhKnsEmvNVG8lTcJDJa2vzIw3P/M7U2m4GD+ekTDTsEapvK+VPWeCd+9yqIb1rVa03euj1OQbM0xRhHuaiKSt95/y5+YR9H16sYN2iyYg1zoZw5EDBdvhnrqLqJ+wC5lMR8K5Xa6YpoU1PSi0wi49hl4koBFf+rTnXbfxbN2S1tQIfX+ZHjEuQW+BAZHs7iv6jnaoWRocM6s77XYg+kQ8xugiKU4FW347iM9qofOg54MxMg83W8qpFDsw+jXvPptr2EQI88vSEiwyp/x0sS49tl9lk37jv5l71zr3WJM3Oq1zoCp4L/dB/Qm+fA5gusKMZrolL4c57mma9eNTe4rzm5nLQgRsQ2X81ZLjSurKQ4zDcFXVwjVuCGX/ffjydaLmhxoPOvWdBiU2DKeMibSv801fT2CURG9uPWBTFiIVUUlWVjw3u7pI1694LX3ehfeUC6PvuhhFKTtDEOdRnILr5fMjS83j9dT+g0/+5d2C4z5uK2Pu1MFqvgjRzE8uRx91FbBpC289HrPlyrJ5dy7x62bFAzo3XetG26pswW74MLVyWGC15DqNLe4Abah6wIf+IDTIVX2mUyRE5tmJ4IpA0SEYTiVtjN8yEtv8uGKHqJNzcnfLKZg60wx9HbNfnmWv7rfvkzTWF0Nr/C3rLjZCGP11il2xMC+mG2XkhtJbvY+u1k45Nk+0/C7Lly5ChSgDx/XCAwxWIHbod4f2zbWw/YoDruee0CKev8dgWN4n7fUdIsNZgFS7r+jxwaFJG3Sf1DNgYHsSCV2LbvvJ+N6ftnw3j8DVg09FPHD4BO0sBs+MChBquODZ9YkyHES5Huu0KgeM6PTIaijidh1SL2vs6Viv4FEyuGxGA+1FxAHC8DwjzmAhXvVuU4vM24d35bhjhKZBw9B9HjwuKOAN6OYLbS/t671onWL0Yplk4OCsESMMLaV7Ae2uHv1AsW7F07me8xDD0YQ82eV9NFfYsW4CtF5xnRXBswLNsJDx3YVR4ipaQcOwaVg2ltaSg2gFn0dO79rfVo6ICIDWW6Ow2CKGCTCiy74RNZLUKMzgGFB+7DLYfSdljRirQuWX4WYKKuh5q7p6+B8JbD46tGk56LddBRcfbX0PXe/fD2PMT1P9znA14FlrzgYp34S79ixBq29Agt1aK0qH66zR/1ZLp85doqLg3BqFsBDg2eJarcz/8sw/1ebyoSEKokURrlCFMNClqGIpqDHwaXKryKni5DjncCC+vutUaCKP6kvVwVv0GSu42gEIAhSF8O+AqfwDqZZv6bGPPpyq5/nOzecctE3hzTd+xg2JKsEnWbiNqR1Gy0apu+XM0tLLmUW4RfkQO3gI2SgZuXs9MJCLkyH/F4Sms9V30+P4EdiR55cdeheJfBzNwPvqEZeKO19UBNf9ZTJzbCPyh59PjH9PQcck6ECJgDL7CsTUVr27GjB91AH2LeXjXxWOxo+s6qLnzkBf041BOF/xr3+AdH1tK9MAe5ppHsdtcCU2dZtVVk9yKKTduJEr3amS+1YHta+dDe+krYBGXVG1wVPyR+dbHiR7SE1c/GLxz9m9gHFwBLbQPldPqTyXAR9ygI7DyjiKl5f0bZKzzM+DYdJJGfnKhKksUQKghFo495CxaKh0lj/uvWLqz92AL7/zjKqD1e5BdZ0KanqTTlSDXITiKH0PelHtp6j/6zcvmdVefhvDGP0FvP38QL85QvHvhq76Nzny7z/Q7bz/ndGj7amF0Xm5FboQEpACEGoTifxtKxUKatubdo16j9pw7C107/wrZMTN9Tc7dAt9ZC2hS3Wo7hjICCx785/yudfHimj/fUCbeNiItF8vQ/pkQjrLEAFF2Ko7cHeyZ8KanumwDjb2vb7EyLZTMtS9i46pmRLffCNOcCRYesN4ET8mrUCf/k6b+rX3AHTj987ux+ucPQUbLYUbHJWLgGaBb1fiONqij/4RRH3sHeLsnlBvnjYK26b+ht92QoJoYMrn4ldRyIFuvhDRiXH/lV6nypaZBD0YsMglmV1WGr2LI6DgQzWTmNceyfpENeBbY/EQRw474zWqqUzPZjSMqIXTYoKq5UWDwvKjkimyrmRevQ2MkH0abEw5vJ5XfFs5cwrv/9843eedVT4HVKLSGr8CMzAbriYEkqRqEaxtco/8MFY9R+cK+U/aO9nMR6byyG+4efeqsvowEMzQP4aZLATw16M6YoXaQiFhXgWT4xWpvZ2gdNtwjGPAewCXW4Dmm1mjJHPFhl4hR9Ysx5tol2LJlBbrWnQ3icRAOFWa0CblT1iAydied+ZDed2SwyIHtj84GpK8X3OgJuemF8M7gnS+4Bi1+8FWsRVB/EmbLp8FaPsjRBSX/GQgst9H+iAB+0k6sxFVgf/KWkTS1d5B3bRKQ0cJk9IoGHBclJlZdmHRo0DESVb3RwXuv/imiDZsQ2zUFrrF74Jj6HE169oj9C9mAn2DghyIJ7jegXBg/AwwkKob6HZ9aXpz1DuDzBnDL4J9b9UIzgD8m7u1I3mzrHnPbh+DEG9f/dwHv+NgE3nXDWCulNn0SmCDlbQhXw+C/iqsRrC8noqxJkeWdn/Zz05Xjefd382wPfqqCzfe7sOXBuQgt+QRYmwxWwuBNb/OuMxfTpLW7rBd55myEdvBxyIPfBOv5PSIwVt9/RyfUUU9AnrYKSLRU5FoIfKqmEkrT6TADueDCw8jNX0Vj/vGhtJjgPfNGIfL+19ESvRCifhXvufq3NOGFERFPtztb9YEUhH21eQhsq4DZ6oa7ohMebqCqP0eP+r4dZ1yP6J5fwAyd1j1TSqoGpfhpeCd+hya9ZXluPvi9IgT+/mVohxbADFeBpANMBhRfA9T8J1B8+R9o1MPdM6m8/bpZ0NcshNl1EZhdgAhA9T+N4rE/ptHvHB7WCbhvRRW0g7lwTzlAlQ81Del47P63zyD4zm8gowUQjijUwq/RjKZHbA8+0uDeeZULa9uuRfTRT4Mx1WrLFt3ejk5awauveBRnvbyWehcLp+zAgnJoL309DXdSk7PhhNH6CUS97wL4jcX8mJ+28uaae5HT/jy6Nk6FmlcCdLbCNXk71KqtNOrh7pOJN9+eA+3ZO2EevhbSTP1eudAjX0Crp4n50l8SvXFUKcOHa3OwbdGXoR/6HMD5iO3czNtO/19MWf/O4ItyMWDOc4OlmiReATSnLVFGGtyrbnWg4935iB2qhRmemAhJAzB5PEjMALafhvVXfQd4cW2/G4g2joXUq5HO40oVPjOguyAcM/nw4hwqnR9EsqcKgI3xG3NzMgITd9pv9txuPpWjJTYHbGYOSuPU+aA3XIINO+5PDFqPYl3vnQut4S7IYGViF8U4xMww1t6wGWc+2zHY4Jnrr3kNWuE/YLZdALV0DVzT3wBetAeZI4vw5gmIdXwNZmhionc+pRrZMxgOaG2XIbLzM8yL+/deZlQZ4HgmqRQqFE0MOwKjxz0miX63OZzESr1lEqRZkvDV8bdLAkJV8Pv9R9Wxlc/vgXf6tyHdN0GpvIuqX9xiR1FGmsktMyE7pvUdmySn0YlVyOg8rPtjZb/vd1c1gkRD/2E/xYQZ3YHCTweHvV850QNQ1DU9c+Hj21TDcFUvhz5bG9J2XPn7YPV7SZ1MQkLx7QainUMarE185TCd1bGepr7VZIcJR9zAkgkidzRYugZYrCGRQW4GRyGysf8Chr37GyD8j0O4mxIEcjI7UdGh5r8JZ/HSY2mTTMWPdsE3/UGo+a+CnG0gZ8jqo6IULgZyHqc5q4e25mfM+w7UogdBrh0g9QCUvFfhmv57TFj8kW5VYUdRUpC/N30BYvsfBPfXEi7pNRX/NjirPknnLN884ECu5YkF0AM3g8yxAEVB6hqoZb+lqWtX9H9y1Qps2eJF7uhREBEfoEQQbGrGBlc41TjUOgEb5k1AVP8YjFAFlLzNcOQuown/7JOzbq3Jf1mQgGvN5Gxr+rn1s3xw+6aBRQ6crj2oem3/QPLI2q/VTQpml/XZjg34SAR89UWzEdnxJMxw9QCAM1zlT6Hsxjsy2yH3fVWNgoOjyxBaXwZ4Yyiau5eKv9vV/wlxaQ7a9Wug7fo4TOM0CNUDNmNgcy+ck5Yiv/o5Kn+ypUfILlm61l+1Pm+YMxku59Xg8GgoOe/DKK4bKO130GOx93NuGA2XQwbPhVC3IdL1PM3a2G4DPpIBX1HjAW35L+gHvwHTKErPvyT6b0L1bIdryrdpzpsvnJDP2zytEDL8dZgtt0FGy9BDvcQ/29kGR/Hf4Sj7KU1eufeo29s+ewqi++6DDF0CZgeE6IBj7B9RcvpPqXTJsLQ/bzt3LrSti2DGKkFKB9zTvofJ7z82EleisDV4Cqnzl0RQdP0iOCsegOLdASAIUASktkPNXw5HxT3I++RrJwbuWifIWADj0Dcgw2WWb2ZC+hb/L1YI/fBnYbTdwYdvzxl8DFGngpXrILvmgjUvYKiQsRIYB25Gy97qYY5HBFSaDiNcCTYc4FgRWEwDljlH4u9qx8EzIa/+6RFuuPcXaP7bcxDO6VB9RTC7GqDnrsXZF9QT3XliPJh39VR0NX8JHMsf4EpKyYQrN4zm/0D7u/GrxusDbm/fMhWsjwWskGJ6OXTW/VCcBcM6BkSSt56xHcJ5GDJaATi6IGg7cJlmA/5RgHzstyIA1sZvzKxYCVKWvXLiPiS271KY2oQBcsJ7ykczOgqMucyL3yaa3z9k4+/RsO2fawARAsy8VIcJwLMfZrhh+KHJ6rfBoTvB+izAUQ/vuOdG6kJZNuCDezPzA9lwZMdkkOk5eqMuyxEr4OAEbPmd06rWGcjr7r/+ZRjtT8Jouh5s+KB4G+Eo/R3C5+wD1g3zJLdawz3DzM8ljsHOEfsb2oCflBGtKYY1+pFShZI3ePHDuOcauf5T30dw0xI4kA/N3I2p07amquuz6gS3Af+Im2fifkTrNcAYysBNWhM7volHzTehSqtYOkOrbz7lD7UdRTkZ5hq/HKQ2Hr2ZqDXNH4QQy/vrEGCbDXh2mr9iLRyjXgQJY2DIra7PEo6CZXCOedM+aDbgI2fwWvxoF5TC30AteRZCDfWFnBmkxKCWvgLv9P9N1l3adizH2j4EJ3GsuWnuRNCez8LovBYyVAEiBRzX3L5DUAtegzrtMZq8dL19pGzARy7kq1Y5kPOVCTCpCu4cL8yoBkWrh+fa3f02DrLNNttsszW4bTbgttlmA26bbTbgttlmA26bbTbgttlmA26bbTbgttmA22abDbhtttmA22bbSbH/PwAA//8xbkaaA8KvmwAAAABJRU5ErkJggg==\" id=\"comet-icon\" width=\"59\" height=\"59\"/></defs>\n      <g transform=\"translate(-159 -293.118)\">\n        <path class=\"fill\" d=\"M254.632 315.691c2.012-2.633 6.422-3.33 8.589 1.007a3.4 3.4 0 0 1 .464-.542 5.56 5.56 0 0 1 5.26-2.556 5.83 5.83 0 0 1 5.109 3.948 8.2 8.2 0 0 1 .542 3.1c.077 2.555 0 5.111 0 7.588 0 1.316-.929 2.091-2.4 2.091a2.06 2.06 0 0 1-2.321-2.091v-7.977a2.655 2.655 0 0 0-2.167-2.555 2.58 2.58 0 0 0-2.863 1.781 4.8 4.8 0 0 0-.155 1.471v6.969c0 .852.077 1.7-.929 2.091a3.09 3.09 0 0 1-3.25-.387 1.64 1.64 0 0 1-.464-1.239v-7.9a2.85 2.85 0 0 0-2.321-2.865 2.55 2.55 0 0 0-2.707 1.623 6 6 0 0 0-.232 1.626v7.356c0 1.239-.851 1.936-2.244 1.936s-2.321-.7-2.321-1.936v-12.462c0-1.549.464-2.013 2.012-2.168a2.28 2.28 0 0 1 2.398 2.091m-22.13 6.269a7.28 7.28 0 0 1 2.863-6.891c4.178-3.252 10.833-1.24 11.92 3.872a14.45 14.45 0 0 1 0 6.04c-.619 3.252-4.024 5.653-7.351 5.575a7.634 7.634 0 0 1-7.274-5.807 10.8 10.8 0 0 1-.158-2.789m10.524.078a8.4 8.4 0 0 1-.077-1.394 2.9 2.9 0 1 0-5.8-.077 19 19 0 0 0 0 2.942 3.215 3.215 0 0 0 2.708 2.942 2.945 2.945 0 0 0 2.94-2.323 16 16 0 0 0 .229-2.09m38.685 1.24a3.29 3.29 0 0 0 3.327 3.639 8.16 8.16 0 0 0 3.792-1.007 1.79 1.79 0 0 1 2.63 1.936 2.44 2.44 0 0 1-.851 1.316 7.26 7.26 0 0 1-4.489 1.471 10.2 10.2 0 0 1-5.8-1.316 6.82 6.82 0 0 1-3.327-6.04c0-1.084.077-2.168.077-3.252 0-3.407 3.714-6.969 8.357-6.427a6.6 6.6 0 0 1 6.114 4.8 7 7 0 0 1 .154 2.555c-.154 1.7-.927 2.323-2.63 2.323h-7.354Zm0-2.788h4.41c.387 0 1.084-.155 1.162-.31a2.9 2.9 0 0 0-.155-1.626 3.14 3.14 0 0 0-3.637-1.007c-1.39.388-1.932 1.239-1.78 2.943m17.024-2.865v6.814c0 1.316.465 1.7 1.858 1.7h1.316a1.98 1.98 0 0 1 2.011 1.936 1.864 1.864 0 0 1-1.779 2.168 18.5 18.5 0 0 1-4.641-.461 4.33 4.33 0 0 1-3.25-4.336c-.077-5.653 0-11.305 0-17.035a1.48 1.48 0 0 1 .542-1.084 3.02 3.02 0 0 1 3.482 0 2.04 2.04 0 0 1 .542 1.161c.077 1.7 0 3.407 0 5.265h3.327c1.316 0 1.858.542 1.858 1.858s-.542 1.858-1.858 1.858h-3.1c-.073.078-.151.078-.308.156m-76.293 4.413v1.316c0 2.4 1.548 3.33 3.792 3.1a8 8 0 0 0 1.934-.619 1.981 1.981 0 0 1 2.012 3.407 7.92 7.92 0 0 1-5.262 1.316 9.7 9.7 0 0 1-2.089-.31 6.19 6.19 0 0 1-4.875-5.885 30 30 0 0 1 .077-5.265c.464-3.485 3.018-5.42 6.887-5.575a7.6 7.6 0 0 1 5.108 1.31 1.838 1.838 0 0 1 .542 2.71 1.7 1.7 0 0 1-2.321.852 9.5 9.5 0 0 0-2.94-.619c-1.78-.078-2.786 1.007-2.863 2.865a8.4 8.4 0 0 0-.002 1.397\"/>\n        <use xlink:href=\"#comet-icon\" transform=\"translate(159 293.118)\"/>\n      </g>\n    </svg>\n  </a>}\n</div>\n"
  },
  {
    "path": "packages/docs/src/components/SponsorsChart.astro",
    "content": "---\nimport Chart from '../assets/venz-chart.svg';\n---\n\n<Chart />\n"
  },
  {
    "path": "packages/docs/src/content/docs/blog/brief-history.md",
    "content": "---\ntitle: A Brief History Of Knip\ndate: 2023-10-15\nsidebar:\n  order: 8\n---\n\n_Published: 2023-10-15_\n\nIf you are fond of short lists and brief histories, then this page was written\njust for you!\n\n- 2022-10-04: The [initial commit][1]. Still so tiny at that point, but the seed\n  was planted. Starting out with finding unused files and exports, the name was\n  Exportman! 🦸\n- 2022-10-09: Big plans and a rename 5 days later, the first published version\n  of Knip was [v0.1.2][2].\n- 2022-11-22: Unused dependencies and support for workspaces and plugins in the\n  [first alpha of v1][3].\n- 2023-01-10: Lots of testing and fixes led to [Knip v1][4].\n- 2023-03-22: [Knip v2][5] saw a full rewrite of the TypeScript backend.\n- 2023-10-15: [Introduction of Knip v3][6].\n\n[1]: https://github.com/webpro-nl/knip/commit/9589dfe22608da7d89f2613383da6db5826226d2\n[2]: https://github.com/webpro-nl/knip/tree/0.1.2\n[3]: https://github.com/webpro-nl/knip/releases/tag/1.0.0-alpha.0\n[4]: https://github.com/webpro-nl/knip/tree/1.0.0\n[5]: https://github.com/webpro-nl/knip/issues/73\n[6]: ./knip-v3.mdx\n"
  },
  {
    "path": "packages/docs/src/content/docs/blog/for-editors-and-agents.md",
    "content": "---\ntitle: Knip for Editors & Agents\ndate: 2025-12-17\nsidebar:\n  order: 2\n---\n\n_Published: 2025-12-17_\n\nThree years in, Knip has found its place in [over 10.000 projects][1] and is\ndownloaded [over 18M times/month][2]. A long period of steady growth in usage\nand stability allows Knip to become more accessible to more people. That's why\nI'm excited and proud to introduce the brand new [Editor Extension][3] **and**\nMCP Server. For humans and coding agents alike, Knip will help keep your\ncodebases tidy.\n\nDon't forget... Knip it before you ship it!\n\n## Editor Extension\n\nThis one is for you.\n\n[The usual suspects][4] like red squiggles for unused exports are there. What\nreally moves the needle for DX with Knip's module graph is **navigation**. A\ncompletely unique way to view & fly through codebases. Connect the dots during\ndevelopment and refactors, while keeping things in check. We're starting out\nwith [3 key features][5]:\n\n1. **Hover over Export** for import & usage locations\n2. **Imports Tree View** for direct links to implementations\n3. **Exports Tree View** for direct links to import & usage locations\n\nThe extension has a [built-in MCP Server][6] with a command and resources to\nconfigure Knip for you, _completely automated_.\n\nFind [Knip on the VS Code Marketplace][7] and find [Knip in the Open VSX\nRegistry][8].\n\n## MCP Server\n\nConfiguring Knip has always been a major headache to many. No more. Tell your\ncoding agent to \"configure knip\" and it will RTFM so you don't have to. Using a\nnewer model like Opus 4.5 or GPT 5.2 results in an optimized `knip.json` file\nand an uncluttered codebase.\n\nThe [MCP Server is available][9] separately and built into the VS Code\nExtension.\n\n## Language Server\n\nThe VS Code Extension and the MCP Server are powered by the new Language Server.\nIt's a custom server that builds the full module graph of your project, and\nprovides a session with a graph explorer to request all sorts of interesting\ninformation. Queries like \"where is an export imported\" or \"is this import part\nof a circular dependency\" are just scratching the surface here.\n\nExtensions for other IDEs can be built on top. See\n[language-server/README.md][10]\n\n## Screenshots\n\n- [Lint Findings][4]\n- [Imports & Exports][5]\n- [Contention][11]\n  - [Circular Dependencies][12]\n  - [Conflicts][13]\n  - [Branching][14]\n- [VS Code Extension Settings][15]\n\n### Lint Findings\n\n![Lint Findings][16]\n\n### Imports & Exports\n\n![hover][17]\n\n### Contention\n\nThe IDE extension shows extra issues in the tree views like circular\ndependencies. We're starting out with some extra novelties like conflicting and\nbranched/diamond-shaped import chains.\n\n#### Circular Dependencies\n\nIf an import is part of a circular dependency, Knip will display:\n\n![Circular Dependencies][18]\n\n#### Conflicts\n\nTypeScript shows direct conflicts when importing or re-exporting the same named\nexport from different files. Except when the problem is more subtle and the\nchain spans more than one file. Knip warns:\n\n![Conflicts][19]\n\n#### Branching\n\nBranched or diamond-shaped imports chains indicate unnecessary re-exports and\ncomplexity. They help to untangle large codebases and shrink or get rid of\nbarrel files. Knip warns:\n\n![Branching][20]\n\n### VS Code Extension Settings\n\n![VS Code Extension Settings][21]\n\n[1]: https://github.com/webpro-nl/knip/network/dependents\n[2]: https://www.npmjs.com/package/knip\n[3]: #editor-extension\n[4]: #lint-findings\n[5]: #imports--exports\n[6]: #mcp-server\n[7]: https://marketplace.visualstudio.com/items?itemName=webpro.vscode-knip\n[8]: https://open-vsx.org/extension/webpro/vscode-knip\n[9]: https://www.npmjs.com/package/@knip/mcp\n[10]: https://github.com/webpro-nl/knip/blob/main/packages/language-server/README.md\n[11]: #contention\n[12]: #circular-dependencies\n[13]: #conflicts\n[14]: #branching\n[15]: #vs-code-extension-settings\n[16]: /screenshots/editors-and-agents/diagnostics.webp\n[17]: /screenshots/editors-and-agents/imports-exports.webp\n[18]: /screenshots/editors-and-agents/circular-dependency.webp\n[19]: /screenshots/editors-and-agents/conflict.webp\n[20]: /screenshots/editors-and-agents/branch.webp\n[21]: /screenshots/editors-and-agents/vscode-extension-settings.webp\n"
  },
  {
    "path": "packages/docs/src/content/docs/blog/knip-v3.mdx",
    "content": "---\ntitle: Announcing Knip v3\ndate: 2023-10-15\nsidebar:\n  order: 9\n---\n\nimport { Tabs, TabItem } from '@astrojs/starlight/components';\n\n_Published: 2023-10-15_\n\nLots of new users got introduced to Knip, coming with clear bug reports, helpful\ninsights, superb reproductions and great suggestions this year. You're all a\nfriendly and helpful bunch! Recently I've opened a Discord channel where more\ncommunication, collaboration, ideas and updates are happening: feel free to join\n[The Knip Barn][1]!\n\nToday, Knip has [over 140k weekly downloads on npm][2], [almost 4000 stars on\nGitHub][3], and [over 500 repositories][4] using it. While numbers are just\nnumbers, they do add to the positive feedback I'm receiving daily. Everything\ncombined makes me think I'm on the right track which is very motivating to keep\nworking on Knip.\n\n## So... What's Been Cooking Lately?\n\n- Migration to a monorepo setup\n- This very website built with Starlight 🌟\n- Extended documentation for just about everything\n- Improved JSON reporter for external integrations (e.g. [GitHub Action][5])\n- Some breaking changes, but you probably don't need to make any changes\n\n## Breaking Changes\n\nA major bump comes with breaking changes, but most likely no changes necessary\non your end:\n\n- Removed support for Node.js v16, Knip v3 requires at least Node.js v18.6\n- Simplified [exit codes][6]\n- [Production mode][7] now includes types by default (add `--exclude types` for\n  previous behavior)\n- Removed `--ignore-internal` flag; [`@internal`][8] exports ignored in\n  production mode now\n- The `--debug-file-filter` flag is removed\n- The `jsonExt` reporter is now the default [JSON reporter][9] (the previous one\n  is gone)\n- Moved `typescript` to `peerDependencies` (requires `>=5.0.4`)\n\n## Installation\n\nTry out the latest Knip v3 release today!\n\n<Tabs syncKey=\"pm\">\n  <TabItem label=\"npm\">\n    ```shell\n    npm install -D knip\n    ```\n  </TabItem>\n\n  <TabItem label=\"pnpm\">\n    ```shell\n    pnpm add -D knip\n    ```\n  </TabItem>\n\n  <TabItem label=\"bun\">\n    ```shell\n    bun add -D knip\n    ```\n  </TabItem>\n\n  <TabItem label=\"yarn\">\n    ```shell\n    yarn add -D knip\n    ```\n  </TabItem>\n</Tabs>\n\nRemember, Knip it before you ship it! Have a great day ☀️\n\n[1]: https://discord.gg/r5uXTtbTpc\n[2]: https://www.npmjs.com/package/knip\n[3]: https://github.com/webpro-nl/knip/stargazers\n[4]: https://github.com/webpro-nl/knip/network/dependents\n[5]: https://github.com/marketplace/actions/knip-reporter\n[6]: ../reference/cli.md#--no-exit-code\n[7]: ../features/production-mode.md\n[8]: ../reference/jsdoc-tsdoc-tags.md#internal\n[9]: ../features/reporters.md#json\n"
  },
  {
    "path": "packages/docs/src/content/docs/blog/knip-v4.mdx",
    "content": "---\ntitle: Announcing Knip v4\ndate: 2024-01-16\nsidebar:\n  order: 6\n---\n\nimport { Tabs, TabItem } from '@astrojs/starlight/components';\n\n_Published: 2024-01-16_\n\nI'm happy to announce that Knip v4 is available!\n\nThe work took over a month and the process of [slimming down to speed up][1]\nended up really well: significant faster runs and reduced memory usage. In the\nmeantime, v3 continued to receive more contributions, plugins and bug fixes.\n\n## Highlights\n\nCompared to v3, here are the highlights:\n\n- Performance: significant speed bump (up to 80%!)\n- Performance: globbing in combo with `.gitignore` is a lot more efficient\n- Configuration: [built-in compilers][2] (for Astro, MDX, Svelte & Vue)\n- The `ignore` option has been improved\n- Internal refactoring to serialize data for future improvements like caching.\n\nThe actual performance win in your projects depends on various factors like size\nand complexity.\n\n## Major Changes\n\nThe changes have been tested against various repositories, but it's possible\nthat you will encounter false positives caused by the major refactoring that has\nbeen done. If you do, [please report][3]!\n\n### Unused Class Members\n\nFinding unused class members is no longer enabled by default. Here's why it's\nnow opt-in:\n\n- When using Knip for the first time on a large repository it can crash after a\n  while with an out of memory error. This is a terrible experience.\n- Plenty of codebases don't use classes at all, keeping TS programs in memory is\n  a waste of resources.\n- Many configurations already exclude `classMembers` from the output.\n\nEnable unused class members by using the CLI argument or the configuration\noption:\n\n```shell\nknip --include classMembers\n```\n\n```json\n{\n  \"include\": [\"classMembers\"]\n}\n```\n\nNow that unused class members is opt-in and better organized within Knip, it\nmight be interesting to start looking at opt-ins for other unused members, such\nas those of types and interfaces.\n\nBy the way, enum members are \"cheap\" with the v4 refactor, so those are still\nincluded by default.\n\n### Compilers\n\nYou can remove the `compilers` option from your configuration. Since you can\noverride them, your custom compilers can stay where they are. This also means\nthat you can go back from `knip.ts` to `knip.json` if you prefer.\n\n### Ignore Files\n\nThe `ignore` option accepted patterns like `examples/`, but if you want to\nignore the files inside this folder you should update to globs like\n`examples/**`.\n\n## What's Next?\n\nThe refactoring for this release opens the door to more optimizations, such as\ncaching. I'm also very excited to see how deeper integrations such as in GitHub\nActions or IDEs like VS Code or WebStorm may further develop.\n\nRemember, if you are you using Knip at work your company can [sponsor me][4]!\n\n## One More Thing...\n\nAn idea I've been toying with is \"tagged exports\". The idea is that you can tag\nexports in a JSDoc comment. The tag does not need to be part of the JSDoc or\nTSDoc spec. For example:\n\n```ts\n/** @custom */\nexport const myExport = 1;\n```\n\nThen, include or exclude such tagged exports from the report like so:\n\n```shell\nknip --experimental-tags=+custom\nknip --experimental-tags=-custom,-internal\n```\n\nThis way, you can either focus on or ignore specific tagged exports with tags\nyou define yourself. This also works for individual class or enum members.\n\nOnce this feature is intuitive and stable, the `experimental` flag will be\nremoved and option(s) added to the Knip configuration file. The docs are in the\n[CLI reference][5].\n\n## Let's Go!\n\nWhat are you waiting for? Start using Knip v4 today!\n\n<Tabs syncKey=\"pm\">\n  <TabItem label=\"npm\">\n    ```shell\n    npm install -D knip\n    ```\n  </TabItem>\n\n  <TabItem label=\"pnpm\">\n    ```shell\n    pnpm add -D knip\n    ```\n  </TabItem>\n\n  <TabItem label=\"bun\">\n    ```shell\n    bun add -D knip\n    ```\n  </TabItem>\n\n  <TabItem label=\"yarn\">\n    ```shell\n    yarn add -D knip\n    ```\n  </TabItem>\n</Tabs>\n\nRemember, Knip it before you ship it! Have a great day ☀️\n\n[1]: ./slim-down-to-speed-up.md\n[2]: ../features/compilers.md\n[3]: ../guides/issue-reproduction.md\n[4]: https://github.com/sponsors/webpro\n[5]: ../reference/cli.md#--tags\n"
  },
  {
    "path": "packages/docs/src/content/docs/blog/knip-v5.mdx",
    "content": "---\ntitle: Announcing Knip v5\ndate: 2024-02-10\nsidebar:\n  order: 5\n---\n\nimport { Tabs, TabItem } from '@astrojs/starlight/components';\n\n_Published: 2024-02-10_\n\nToday brings the smallest major release so far. Tiny yet mighty!\n\nBelow are two cases to demonstrate the change in how unused exports are\nreported.\n\n## Case 1\n\nThe first case shows two exports with a namespaced import that references one of\nthose exports explicitly:\n\n```ts title=\"knip.js\"\nexport const version = 'v5';\nexport const getRocket = () => '🚀';\n```\n\n```ts title=\"index.js\"\nimport * as NS from './knip.js';\n\nconsole.log(NS.version);\n```\n\nIn this case we see that `getRocket` is an unused export.\n\nPreviously it would go into the \"Unused exports in namespaces\" category\n(`nsExports`). This issue has been moved to the \"Unused exports\" category\n(`exports`).\n\n## Case 2\n\nThe second case is similar, but only the imported namespace itself is\nreferenced. None of the individual exports is referenced:\n\n```ts title=\"index.js\"\nimport * as NS from './knip.js';\nimport send from 'stats';\n\nsend(NS);\n```\n\nAre the `version` and `getRocket` exports used? We can't know. The same is true\nfor the spread object pattern:\n\n```ts title=\"index.js\"\nimport * as NS from './knip.js';\n\nconst Spread = { ...NS };\n```\n\nPreviously those exports would go into the \"Unused exports in namespaces\"\ncategory. This is still the case, but this category is no longer enabled by\ndefault.\n\n## Include unused exports in namespaces\n\nTo enable this type of issues in Knip v5, add this argument to the command:\n\n```shell\nknip --include nsExports\n```\n\nOr in your configuration file:\n\n```json title=\"knip.json\"\n{\n  \"include\": [\"nsExports\", \"nsTypes\"]\n}\n```\n\nNow `version` and `getRocket` will be reported as \"Unused exports in\nnamespaces\".\n\nNote that `nsExports` and `nsTypes` are split for more granular control.\n\n## Handling exports in namespaced imports\n\nYou have a few options to handle namespaced imports when it comes to unused\nexports.\n\n### 1. Use named imports\n\nRegardless of whether `nsExports` is enabled or not, it's often good practice to\nreplace the namespaced imports with named imports:\n\n```ts title=\"index.js\"\nimport { version, getRocket } from './knip.js';\n\nsend({ version, getRocket });\n```\n\nWhenever possible, explicit over implicit is often the better choice.\n\n### 2. Standardized JSDoc tags\n\nUsing one of the available JSDoc tags like `@public` or `@internal`:\n\n```ts title=\"knip.js\"\nexport const version = 'v5';\n/** @public */\nexport const getRocket = () => '🚀';\n```\n\nAssuming only imported using a namespace (like in the example cases above), this\nwill exclude the `getRocket` export from the report, even though it isn't\nexplicitly referenced.\n\n### 3. Arbitrary JSDoc tags\n\nAnother solution is to tag individual exports arbitrarily:\n\n```ts title=\"knip.js\"\nexport const version = 'v5';\n/** @launch */\nexport const getRocket = () => '🚀';\n```\n\nAnd then exclude the tag like so:\n\n```shell\n$ knip --experimental-tags=-launch\nExports in used namespace (1)\nversion  NS  unknown  knip.js:1:1\n```\n\nAssuming only imported using a namespace (like in the example cases above), this\nwill exclude the `getRocket` export from the report, even though it isn't\nexplicitly referenced.\n\n## A better default\n\nI believe this behavior in v5 is the better default: have all exports you want\nto know about in a single category, and those you probably want to ignore in\nanother that's disabled by default.\n\nBefore the [v4 refactoring][1], this would be a lot harder to implement. That\nrefactoring turns out to be a better investment than expected. Combined with a\nbetter understanding of how people write code and use Knip, this change is a\nnatural iteration.\n\nWhy the major bump? It's not breaking for the large majority of users, but for\nsome it may be breaking. For instance when relying on the [JSON reporter][2],\nother reporter output, or custom [preprocessing][3]. It's not a bug fix, it's\nnot a new feature, but since semver is all about setting expectations I feel the\nchange is large enough to warrant a major bump.\n\n## Let's Go!\n\nWhat are you waiting for? Start using Knip v5 today!\n\n<Tabs syncKey=\"pm\">\n  <TabItem label=\"npm\">\n    ```shell\n    npm install -D knip\n    ```\n  </TabItem>\n\n  <TabItem label=\"pnpm\">\n    ```shell\n    pnpm add -D knip\n    ```\n  </TabItem>\n\n  <TabItem label=\"bun\">\n    ```shell\n    bun add -D knip\n    ```\n  </TabItem>\n\n  <TabItem label=\"yarn\">\n    ```shell\n    yarn add -D knip\n    ```\n  </TabItem>\n</Tabs>\n\nRemember, Knip it before you ship it! Have a great day ☀️\n\n[1]: ../blog/slim-down-to-speed-up.md\n[2]: ../features/reporters.md#json\n[3]: ../features/reporters.md#preprocessors\n"
  },
  {
    "path": "packages/docs/src/content/docs/blog/knip-v6.md",
    "content": "---\ntitle: Announcing Knip v6\ndate: 2026-03-20\nsidebar:\n  order: 1\n---\n\n_Published: 2026-03-20_\n\n## Knip v6 is out!\n\nThis release is all about replacing the TypeScript backend entirely with\n`oxc-parser` and `oxc-resolver`, and making Knip a whole lot faster!\n\n## From TypeScript to oxc\n\nTwo years ago, the [\"slim down to speed up\"][1] and [Knip v4][2] work removed a\nlot of overhead around TypeScript programs, made serialization and caching\npractical, and improved memory efficiency a lot. But there was still a ceiling:\nparsing and module resolution still depended on TypeScript APIs designed for\nIDEs and language servers — not for the kind of single-pass static analysis Knip\ndoes.\n\nStarting today, Knip v6 parses your source files with [oxc-parser][3]. This is\nmore than just a parser swap for the sake of using the latest 'n greatest.\n\nKnip has always been designed to parse each file only once, but the TypeScript\nbackend carried the overhead of wiring up an entire program along with the\ntypechecker. That's useful for IDEs keeping symbols connected, but much less so\nwhen you only need to traverse an AST once to collect imports and exports.\nThe TypeScript backend made the setup as a whole harder and slower than it\nneeded to be, especially to keep large monorepos in check.\n\nNow with TypeScript itself Go-ing places, replacing that backend was only a\nmatter of time.\n\nUnsurprisingly, the search didn't take long: `oxc-parser` offers everything we\nneed and its (experimental) raw transfer is crazy fast. Massive props to\n[overlookmotel][4], [Boshen][5] and all contributors for all the work on\n[the oxc suite][6]!\n\n## Performance tuning\n\nNext to this major refactor, I've been having a ball tuning Knip's performance\nfurther. One thing to highlight here is that a few more plugins have been\nrefactored to statically analyze configuration files directly, as opposed to\nactually importing them (including transitive dependencies...). This includes\nthe ESLint (\"flat config\"), tsdown and tsup plugins.\n\n## The numbers\n\nComparing v5 and v6 in some projects using Knip, all boosts are in the **2-4x** range:\n\n[![venz-chart][8]][7]\n\nTrust me, I could look at this chart all day long! The same numbers in a table:\n\n| Project          | v5.88.0 | v6.0.0 |\n| ---------------- | ------: | -----: |\n| [astro][9]       |    4.0s |   2.0s |\n| [query][10]      |    3.8s |   1.7s |\n| [rolldown][11]   |    3.7s |   1.7s |\n| [sentry][12]     |   11.0s |   4.0s |\n| [TypeScript][13] |    3.7s |   0.9s |\n\n## What's new\n\n- Did I already mention Knip got 2-4x faster?\n- Support for TS namespaces (and modules), new issue type `namespaceMembers`:\n\n```ts\nexport namespace MyNamespace {\n  export const myName = 'knip'; // we were ignored in v5,\n  export type MyType = string; // yet in v6 we are included\n}\n```\n\n## Breaking changes\n\nGranted, most of you won't even notice. Here's the list:\n\n- Dropped support for Node.js v18 → Knip v6 requires Node.js v20.19.0 or newer\n- Dropped issue type `classMembers`\n- Dropped `--include-libs` → this is now the default and only behavior\n- Dropped `--isolate-workspaces` → this is now the default and only behavior\n- Dropped `--experimental-tags` → use [`--tags`][14]\n- In [reporter functions][15], `issues.files` is consistent with other issue shapes. Removed `issues._files`.\n- In the [JSON reporter][16], issues are consistently arrays for any issue type. Removed root `files`.\n\n## Editor Extensions\n\n[Editor extensions][17] benefit from the core upgrades, for being faster and more\nmemory-efficient. Regardless of new extension releases, the local version of\nKnip will be detected and used. Upgrade `knip` in your dependencies when you're\nready.\n\n## What about classMembers?\n\nI feel you. Even Knip itself was using it. Until today.\n\nThe problem is that the implementation relies on the JS-based `ts.LanguageService`\nAPI that exposes the `findReferences` method. TypeScript v6 is the last JS-based\nrelease, and TypeScript v7 is a full rewrite in Go. I am left wondering if it\never will be feasible and practical to build such features using primitives\n(i.e. not via LSP) in a JS-based CLI (references: [microsoft/typescript-go#455][18],\n[@typescript/api][19]). Knip was already pretty unique for even trying this in\na CLI tool.\n\nNot that many projects seem to be using it either:\n[github.com search for \"classMembers path\\:knip.json\"][20].\n\nIf your project relies on it, feel free to open an issue on GitHub or contact me\nand maybe we can work something out. Maybe a separate dedicated tool could work,\nor extended support for Knip v5.\n\n## Upgrade today\n\n```sh\nnpm install -D knip@latest\n```\n\n## Deep closing thoughts...\n\nRemember, Knip it before you ship it! Have a great day ☀️\n\n[1]: ./slim-down-to-speed-up.md\n[2]: ./knip-v4.mdx\n[3]: https://oxc.rs/docs/guide/usage/parser\n[4]: https://github.com/overlookmotel\n[5]: https://github.com/Boshen\n[6]: https://oxc.rs\n[7]: https://try.venz.dev/?type=bar&labelX=Knip&labelY=duration+(s)&label=astro&label=query&label=rolldown&label=sentry&label=typescript&l=v5.88.0&l=v6.0.0&data=4*2&data=3.8*1.7&data=3.7*1.7&data=11*4&data=3.7*0.9\n[8]: https://cdn.venz.dev/i/chart.svg?pad=0&type=bar&labelX=Knip&labelY=duration+(s)&label=astro&label=query&label=rolldown&label=sentry&label=typescript&l=v5.88.0&l=v6.0.0&data=4*2&data=3.8*1.7&data=3.7*1.7&data=11*4&data=3.7*0.9&theme=dark\n[9]: https://github.com/withastro/astro\n[10]: https://github.com/TanStack/query\n[11]: https://github.com/rolldown/rolldown\n[12]: https://github.com/getsentry/sentry\n[13]: https://github.com/microsoft/TypeScript\n[14]: ../reference/configuration.md#tags\n[15]: ../features/reporters.md#custom-reporters\n[16]: ../features/reporters.md#json\n[17]: ../reference/integrations.md\n[18]: https://github.com/microsoft/typescript-go/discussions/455\n[19]: https://github.com/microsoft/typescript-go/tree/main/_packages/api\n[20]: https://github.com/search?q=classMembers%20path%3Aknip.json&type=code\n"
  },
  {
    "path": "packages/docs/src/content/docs/blog/migration-to-v1.md",
    "content": "---\ntitle: Migration to v1\n---\n\n_2023-01-04_\n\nWhen coming from version v0.13.3 or before, there are some breaking changes:\n\n- The `entryFiles` and `projectFiles` options have been renamed to `entry` and\n  `project`.\n- The `--dev` argument and `dev: true` option are gone, this is now the default\n  mode (see [production mode][1]).\n- Workspaces have been moved from the root of the config to the `workspaces` key\n  (see [workspaces][2]).\n- The `--dir` argument has been renamed to `--workspace`.\n\n## Example\n\nA configuration like this in v0.13.3 or before...\n\n```json\n{\n  \"entryFiles\": [\"src/index.ts\"],\n  \"projectFiles\": [\"src/**/*.ts\", \"!**/*.spec.ts\"],\n  \"dev\": {\n    \"entryFiles\": [\"src/index.ts\", \"src/**/*.spec.ts\", \"src/**/*.e2e.ts\"],\n    \"projectFiles\": [\"src/**/*.ts\"]\n  }\n}\n```\n\n...should become this for v1...\n\n```json\n{\n  \"entry\": [\"src/index.ts!\"],\n  \"project\": [\"src/**/*.ts!\"]\n}\n```\n\nMuch cleaner, right? For some more details:\n\n- The `dev` property for the `--dev` flag is now the default mode.\n- Use `--production` to analyze only the `entry` and `project` files suffixed\n  with `!`.\n- The glob patterns for both types of test files (`*.spec.ts` and `*.e2e.ts`)\n  are no longer needed:\n  - Regular test files like `*.test.js` and `*.spec.ts` etc. are automatically\n    handled by Knip.\n  - The `*.e2e.ts` files is configured with the Cypress or other plugin. Note\n    that Cypress uses `*.cy.ts` for spec files, but this could be overridden\n    like so:\n\n```json\n{\n  \"entry\": \"src/index.ts!\",\n  \"project\": \"src/**/*.ts!\",\n  \"cypress\": {\n    \"entry\": \"src/**/*.e2e.ts\"\n  }\n}\n```\n\n[1]: ../features/production-mode.md\n[2]: ../features/monorepos-and-workspaces.md\n"
  },
  {
    "path": "packages/docs/src/content/docs/blog/release-notes-v2.md",
    "content": "---\ntitle: Release Notes v2\nsidebar:\n  order: 10\n---\n\n_2023-03-22_\n\n## Breaking changes\n\nWhen coming from v1, there are no breaking changes in terms of configuration.\n\n## Changes\n\nThere are some changes regarding CLI arguments and output:\n\n- Knip now runs on every \\[workspace]\\[1] automatically (except for the ones in\n  `ignoreWorkspaces: []`).\n- The \"Unlisted or unresolved dependencies\" is split in \"Unlisted dependencies\"\n  and \"Unresolved imports\".\n- Bug fixes and increased correctness impact output (potentially causing CI to\n  now succeed or fail).\n\n## New features\n\nRewriting a major part of Knip's core from scratch allows for some new exciting\nfeatures:\n\n- **Performance**. Files are read only once, and their ASTs are traversed only\n  once. Projects of any size will notice the difference. Total running time for\n  some projects decreases with 90%.\n- **Compilers**. You can now include other file types such as `.mdx`, `.vue` and\n  `.svelte` in the analysis.\n\nInternally, the `ts-morph` dependency is replaced by `typescript` itself.\n\n## Other improvements\n\n- Improved support for workspaces.\n- Improved module resolutions, self-referencing imports, and other things you\n  don't want to worry about.\n- Configure `ignoreDependencies` and `ignoreBinaries` at the workspace level.\n- Simplified plugins model: plugin dependency finder may now return any type of\n  dependency in a single array: npm packages, local workspace packages, local\n  files, etc. (module and path resolution are handled outside the plugin).\n- Many bugfixes.\n"
  },
  {
    "path": "packages/docs/src/content/docs/blog/slim-down-to-speed-up.md",
    "content": "---\ntitle: Slim down to speed up\ndate: 2023-12-14\nsidebar:\n  order: 7\n---\n\n_Published: 2023-12-14_\n\n**tl;dr;** Memory usage is up to 50% lower, runs are up to 60% faster and you\ncan start using v4 canary today. No \"unused class members\" for the time being,\nbut this feature is planned to be restored.\n\n## Introduction\n\nHonestly, performance has always been a challenge for Knip. A longstanding\nbottleneck has finally been eliminated and Knip is going to be a lot faster.\nSkip straight to the bottom to install v4 canary and try it out! Or grab\nyourself a nice drink and read on if you're interested in where we are coming\nfrom, and where we are heading.\n\n## Projects & Workspaces\n\nFrom the start, Knip has relied on TypeScript for its robust parser for\nJavaScript and TypeScript files. And on lots of machinery important to Knip,\nlike module resolution and accurately finding references to exported values.\nParts of it can be customized, such as the (virtual) file system and the module\nresolver.\n\nIn TypeScript terms, a \"project\" is like a workspace in a monorepo. Same as each\nworkspace has a `package.json`, each project has a `tsconfig.json`. The\n`ts.createProgram()` method is used to create a program based on a\n`tsconfig.json` and the machinery starts to read and parse source code files,\nresolve modules, and so on.\n\nUp until v2, when Knip wanted to find unused things in a monorepo, all programs\nfor all workspaces were loaded into memory. Workspaces often depend on each\nother, so Knip couldn't load one project, analyze it and dispose it. This way,\nconnections across workspaces would be lost.\n\n## Shared Workspaces\n\nKnip v2 said goodbye to this approach and implemented its own TypeScript backend\n(after using `ts-morph` for this). Based on the compatibility of\n`compilerOptions`, workspaces were merged into shared programs whenever\npossible. Having less programs in memory led to significant performance\nimprovements. Yet ultimately it was still a stopgap, since everything was still\nkept in memory for the duration of the process.\n\n\"Why does everything need to stay in memory?\", you may wonder. The answer is\nthat Knip uses `findReferences` at the end of the process. Knip relied on this\nTypeScript Language Server method for everything that's not easy to find. More\nabout that later in [the story of findReferences][1]\n\n## Serialization\n\nFortunately, everything that's imported and exported from source files\n(including things like members of namespaces and enums) can be found relatively\neasily during AST traversal. This way, references to exports don't have to be\n\"traced back\" later on.\n\nIt's mostly class members that are harder to find due to their dynamic nature.\nWithout these, all information can be serialized for storage and retrieval (in\nmemory or on disk). Slimming down by taking class members out of the equation\nsimplifies things a lot and paves the way for all sorts of improvements.\n\n## We Have To Slim Down\n\nThe relevant part in the linting process can be summarized in 5 steps:\n\n1. Collect entry files and feed them to TypeScript\n2. Read files, resolve modules, and create ASTs\n3. Traverse ASTs and collect imports & exports\n4. Match exports against imports to determine what's unused\n5. Find references to hard-to-find exported values and members\n\nIf we would hold on to reporting unused class members, then especially steps 2\nand 5 are hard to decouple. The program and the language service containing the\nsource files used to eventually trace back references can't really be decoupled.\nSo class members had to go. Sometimes you have to slim down to keep moving. One\nstep back, two steps forward.\n\nIf you rely on this feature, fear not. I plan to bring it back before the final\nv4, but possibly behind a flag.\n\n## What's In Store?\n\nSo with this out of the way, everything becomes a lot clearer and we can finally\nreally start thinking about significant memory and performance improvements. So\nwhat's in store here? A lot!\n\n- We no longer need to keep everything in memory, so workspaces are read and\n  disposed in isolation, one at a time. Memory usage will be spread out more\n  even. This does not make it faster, but reducing \"out of memory\" issues is\n  definitely a Good Thing™️ in my book.\n- Knip could recover from unexpected exits and continue from the last completed\n  workspace.\n- The imports and exports are in a format that can be serialized for storage and\n  retrieval. This opens up interesting opportunities, such as local caching on\n  disk, skipping work in subsequent runs, remote caching, and so on.\n- Handling workspaces in isolation and serialization result in parallelization\n  becoming a possibility. This becomes essential, as module resolution and AST\n  creation and traversal are now the slowest parts of the process and are not\n  easy to optimize significantly (unless perhaps switching to e.g Rust).\n- No longer relying on `findReferences` speeds up the export/import matching\n  part part significantly. So far I've seen **improvements of up to 60% on total\n  runtime**, and my guess is that some larger codebases may profit even more.\n- The serialization format is still being explored and there is no caching yet,\n  but having the steps more decoupled is another Good Thing™️ that future me\n  should be happy about.\n\n## Back It Up, Please\n\nI heard you. Here's some example data. You can get it directly from Knip using\nthe `--performance` flag when running it on any codebase. Below we have some\ndata after linting the [Remix monorepo][2].\n\n### Knip v3\n\n```sh\n$ knip --performance\n\nName                           size  min     max      median   sum\n-----------------------------  ----  ------  -------  -------  -------\nfindReferences                  223    0.55  2252.35     8.46  5826.95\ncreateProgram                     2   50.78  1959.92  1005.35  2010.70\ngetTypeChecker                    2    5.04   667.45   336.24   672.48\ngetImportsAndExports            396    0.00     7.19     0.11   104.46\n\nTotal running time: 9.7s (mem: 1487.39MB)\n```\n\n### Knip v4\n\n```sh\n$ knip --performance\n\n...\n\nName                           size  min     max      median   sum\n-----------------------------  ----  ------  -------  -------  -------\ncreateProgram                     2   54.36  2138.45  1096.40  2192.81\ngetTypeChecker                    2    7.40   664.83   336.12   672.23\ngetImportsAndExports            396    0.00    36.36     0.16   224.37\ngetSymbolAtLocation            2915    0.00    29.71     0.00    65.63\n\nTotal running time: 4.3s (mem: 729.67MB)\n```\n\n### Takeaways\n\nThe main takeaways here:\n\n- In v3,`findReferences` is where Knip potentially spends most of its time\n- In v4, total running time is down over 50%\n- In v4, memory usage is down 50% (calculated using\n  `process.memoryUsage().heapUsage`)\n- In v4, `getImportsAndExports` is more comprehensive to compensate for the\n  absence of `findReferences` - more on that below\n\nRemember, unused class members are no longer reported by default in v4.\n\n## The story of `findReferences`\n\nDid I mention Knip uses `findReferences`...? Knip relied on it for everything\nthat's not easy to find. Here's an example of an export/import match that **is**\neasy to find:\n\n```ts title=\"import.ts\"\nimport { MyThing } from './thing.ts';\n```\n\n```ts title=\"export.ts\"\nexport const MyThing = 'cool';\n```\n\nIn v2 and v3, Knip collects many of such easy patterns. Other patterns are\nharder to find with static analysis. This is especially true for class members.\nLet's take a look at the next example:\n\n```ts title=\"MyClass.ts\"\nclass MyClass {\n  constructor() {\n    this.method();\n  }\n  method() {}\n  do() {}\n}\n\nexport const OtherName = MyClass;\n```\n\n```ts title=\"instance.ts\"\nimport * as MyNamespace from './MyClass.ts';\n\nconst { OtherName } = MyNamespace;\n\nconst instance = new OtherName();\n\ninstance.do();\n```\n\nWithout a call or `new` expression to instantiate `OtherName`, its `method`\nmember would not be used (since the constructor would not be executed). To\nfigure this out using static analysis goes a long way. Through export\ndeclarations, import declarations, aliases, initializers, call expressions...\nthe list goes on and on. Yet all this magic is exactly what happens when you use\n\"Find all references\" or \"Go to definition\" in VS Code.\n\nKnip used `findReferences` extensively, but it's what makes a part of Knip\nrather slow. TypeScript needs to wire things up (through\n`ts.createLanguageService` and `program.getTypeChecker`) before it can use this,\nand then it tries hard to find all references to anything you throw at it. It\ndoes this very well, but the more class members, enum members and namespaced\nimports your codebase has, the longer it inevitably takes to complete the\nprocess.\n\nBesides letting go of class members, a slightly more comprehensive AST traversal\nis required to compensate for the absence of `findReferences` (it's the\n`getImportsAndExports` function in the metrics above). I'd like to give you an\nidea of what \"more comprehensive\" means here.\n\nIn the following example, `referencedExport` was stored as export from\n`namespace.ts`, but it was not imported directly as such:\n\n```ts title=\"namespace.ts\"\nexport const referencedExport = () => {};\n```\n\n```ts title=\"index.ts\"\nimport * as NS from './namespace.ts';\n\nNS.referencedExport();\n```\n\nPreviously, Knip used `findReferences()` to \"trace back\" the usage of the\nexported `referencedExport`.\n\nThe gist of the optimization is to pre-determine all imports and exports. During\nAST traversal of `index.ts` , Knip sees that `referencedExport` is attached to\nthe imported `NS` namespace, and stores that as an imported identifier of\n`namespace.ts`. When matching exports against imports, this lookup comes at no\nextra cost. Additionally, this can be stored as strings, so it can be serialized\ntoo. And that means it can be cached.\n\nKnip already did this for trivial cases as shown in the first example of this\narticle. This has now been extended to cover more patterns. This is also what\nneeds to be tested more extensively before v4 can be released. Its own test\nsuite and the projects in the integration tests are already covered so we're\nwell on our way.\n\nFor the record, `findReferences` is an absolute gem of functionality provided by\nTypeScript. Knip is still backed by TypeScript, and tries to speed things up by\nshaking things off. In the end it's all about trade-offs.\n\n## Let's Go!\n\nYou can start using Knip v4 today, feel free to try it out! You might find a\nfalse positive that wasn't there in v3, please [report this][3].\n\n```sh\nnpm install -D knip@canary\n```\n\nRemember, Knip it before you ship it! Have a great day ☀️\n\n[1]: #the-story-of-findreferences\n[2]: https://github.com/remix-run/remix\n[3]: https://github.com/webpro-nl/knip/issues\n"
  },
  {
    "path": "packages/docs/src/content/docs/blog/state-of-knip.md",
    "content": "---\ntitle: The State of Knip\ndate: 2025-02-28\nsidebar:\n  order: 3\n---\n\n_Published: 2025-02-28_\n\nHonestly, Knip was a bit of a \"cursed\" project from the get-go. Getting anywhere\nnear a level of being broadly-ish valuable requires a good amount of\n~~foolishness~~ determination, and it has always been clear it would stay far\nfrom perfect. It's telling that most of [similar projects][1] have been\nabandoned.\n\nAnd even though Knip is in its infancy, this update is meant as a sign we feel\nwe're still on to something. External indicators include increased usage looking\nat numbers such as dependent repositories on GitHub and weekly downloads on npm,\nand bug reports about increasingly less rudimentary issues.\n\n## Two Cases\n\nFor those interested, let's take a look at two cases that hopefully give an\nimpression of how Knip works under the hood and the level of issues we're\ncurrently dealing with. It's assumed you already have a basic understanding of\nKnip (otherwise please consider to read at least [entry files][2] and\n[plugins][3] first).\n\n### Case 1: Next.js\n\nLet's say this default configuration represents, greatly simplified, [the\ndefault `entry` patterns][4] for projects using Next.js:\n\n```json\n{\n  \"next\": {\n    \"entry\": [\"next.config.ts\", \"src/pages/**/*.tsx\"]\n  }\n}\n```\n\nThose files will be searched for and then statically analyzed to collect\n`import` statements and find other local files and external dependencies. This\nis the generic way Knip handles all source files.\n\nHowever, the game changes if the project uses the following Next.js\nconfiguration:\n\n```ts title=\"next.config.ts\"\nconst nextConfig = {\n  pageExtensions: ['page.tsx'],\n};\n\nexport default nextConfig;\n```\n\nNext.js will now look for files matching `src/pages/**/*.page.tsx` instead (note\nthe subtle change of the glob pattern). Knip should respect this to find used\nand unused files properly.\n\nMoving the burden to users for them to either not notice at all and get\nincorrect results, or having to override the `next.entry` patterns and include\n`src/pages/**/*.page.tsx` isn't good DX. Knip should take care of it.\n\nTo get the configuration object and the value of `pageExtensions`, Knip has to\nactually load and execute `next.config.ts` ¹... and trouble is right around the\ncorner:\n\n```ts title=\"next.config.ts\"\nconst nextConfig = {\n  pageExtensions: ['page.tsx'],\n  env: {\n    BASE_URL: process.env.BASE_URL.toLowerCase(),\n  },\n};\n\nexport default nextConfig;\n```\n\n```shell\n$ knip\n💥 LoaderError: Error loading next.config.ts\n💥 Reason: Cannot read properties of undefined (reading 'toLowerCase')\n```\n\nObviously a contrived example, but the gist is that lots of tooling\nconfiguration expects environment variables to be defined. But when running Knip\nthere might not be a mechanism to set those. Clearly a breaking change when Knip\nstarts doing this, only for Next.js projects with a configuration file that\ndoesn't read environment variables safely (or has other contextual\ndependencies).\n\nBy the way, [the ESLint v9 plugin][5] has a similar issue.\n\n¹ Another approach could be to statically analyze the `next.config.ts`\nconfiguration file. That would require some additional efforts and get us only\nso far, but is definitely useful in some cases and on the radar.\n\n**EDIT:** This has been solved in the Next.js plugin in v5.48.0.\n\n### Case 2: Knip does that?!\n\nTo further bring down user configuration and the number of false positives, the\nsystem required more components. New components have been introduced to keep\nimproving and nail it for an increasing number of projects. This case is an\nillustration of some of those components.\n\nLet's just dive into this example and find out what's happening:\n\n```json title=\"package.json\"\n{\n  \"scripts\": {\n    \"test\": \"yarn --cwd packages/frontend vitest -c vitest.components.config.ts\"\n  }\n}\n```\n\nOrchestration is necessary between various components within Knip, such as:\n\n- Plugins, the Vitest plugin parses `vitest.components.config.ts`\n- Custom CLI argument parsing for executables, e.g. `yarn --cwd [dir]` and\n  `vitest --config [file]`\n- The workspace graph, to see `packages/frontend` is a descendant workspace of\n  the root workspace\n\nPatterns like in the script above do not occur only in `package.json` files, but\ncould be anywhere. Here's a similar example in a GitHub Actions workflow:\n\n```yaml title=\".github/workflows/test.yml\"\njobs:\n  integration:\n    runs-on: ubuntu-latest\n    steps:\n      - run: playwright test -c playwright.e2e.config.ts\n        working-directory: e2e\n```\n\nThe pattern is very similar, because Knip needs to assign a configuration file\nto a specific workspace (assuming there's one in `./e2e`) and apply the Vitest\nconfiguration to that particular workspace with its own set of directory and\nentry file patterns.\n\nAn essential part of Knip is to build up the module graph for source files. With\nthe configuration files still in mind, this is the pattern Knip follows towards\nthis goal:\n\n- Find configuration files at default and custom locations\n- Assign them to the right workspace\n- Run plugins in their own workspace to take entry file patterns from the\n  configuration objects\n- Load and parse configuration files to get referenced dependencies\n\nThe referenced dependencies are stored in the `DependencyDeputy` class to\neventually determine what dependencies are unused or missing in `package.json`\nin each workspace.\n\nBoth the configuration and entry files are then used to start building up the\nmodule graph.\n\n## Comprehensive\n\nDiscussing the two cases briefly covers only part of the whole process. This\nmight give a sense of the reason why Knip is pretty comprehensive. After all,\nbuilding the module graph for internal source files to find unused files and\nexports requires the list of external dependencies including internal\nworkspaces. And on the other hand, a complete module graph is required to find\nunused or missing external dependencies.\n\nThe comprehensiveness also requires a range of components in the system, such as\nthe aforementioned ones, [compilers for popular frameworks][6] and a [script\nparser][7], and other affordances such as [auto-fix][8].\n\nThat said, code organization could be improved to make it more accessible for\ncontributions and, for instance, expose programmatic APIs to use the generated\nmodule graph outside of Knip. Additionally, existing plugins can better take\nadvantage of existing components in the system, and new plugins can be developed\nto further reduce user configuration and false positives.\n\n## The End\n\nThat's all for today, thanks for reading! Have a great one, and don't forget:\nKnip it before you ship it! ✂️\n\n[1]: ../explanations/comparison-and-migration.md\n[2]: ../explanations/entry-files.md\n[3]: ../explanations/plugins.md\n[4]: ../reference/plugins/next.md#default-configuration\n[5]: ../reference/plugins/eslint.md#eslint-v9\n[6]: ../features/compilers.md\n[7]: ../features/script-parser.md\n[8]: ../features/auto-fix.mdx\n"
  },
  {
    "path": "packages/docs/src/content/docs/blog/two-years.mdx",
    "content": "---\ntitle: Two Years\ndate: 2024-10-04\nsidebar:\n  order: 4\n---\n\n_Published: 2024-10-04_\n\nimport EmojiBlastButton from '../../../components/EmojiBlastButton.astro';\nimport Projects from '../../../components/Projects.astro';\nimport Sponsors from '../../../components/Sponsors.astro';\n\nExactly two years ago the first commit was pushed to GitHub and the first\nversion of Knip was published to the npm registry. The name was initially\n[Exportman][1]! We've come a loooong way... The JavaScript ecosystem is highly\ndynamic and I've been crazy enough to even start, try and keep up with it! But\nhere we are.\n\nOctober 4th is World Animal Day, so there was really no choice but bring in the\ncrazy mascot that early adopters may remember:\n\n![Crazy cow with orange scissors in Van Gogh style][2]\n\nToday we celebrate an unknown but CRAZY amount of clutter removed from so many\ncodebases with Knip's help. Every single day I see many of those little red\nblocks for thousands of lines of deleted code and dependencies. Call me crazy,\nbut to me this is pure joy and never gets old!    🟩 🟥 🟥 🟥 🟥\n\n<EmojiBlastButton />\n\n## Smiling faces\n\nThe actual amount of code and dependencies removed and the number of smiling\nfaces this brings is what matters most, but also remain a good mystery. Clearly\nmore and more projects add Knip to their projects and CI workflows to keep\never-growing codebases tidy. It's wonderful to see if Knip plays its part in\ntoday's ecosystem to help with that. Thanks for bearing with me, here's to a lot\nmore little red blocks in your PRs!    🟩 🟥 🟥 🟥 🟥\n\n## Updates\n\nWhy not throw in some freshly cooked updates in [v5.31.0][3] for you while we're\nat it:\n\n- [The auto-fix feature][4] has been completely revamped, it's much better and a\n  lot more comprehensive! You have to see it to believe it.\n- Knip has upgraded to [Jiti v2][5], resolving a bunch of known issues when\n  loading configuration files authored in TypeScript and ESM, such as:\n\n```\nCannot use 'import.meta' outside a module\nawait is only valid in async functions and the top level bodies of modules\nUnexpected identifier 'Promise'\nReflect.metadata is not a function\n```\n\nAnd that pesky \"CJS build of Vite's Node API is deprecated\" warning is finally\ngone!\n\nThanks to everyone involved in making this happen, it's truly much appreciated.\n\n## Stable\n\nIf you haven't tried Knip recently, it's worth taking another look! Version 5\nwas released 8 months ago, and even though there were no breaking changes, it\nincludes many enhancements. In fact, Knip has been largely stable since version\n3, which came out a year ago. Many releases have a compound effect, as Knip has\nkept the pace for two years now.\n\n## Projects using Knip\n\nThis list of projects using Knip to keep their codebases tidy is something I\ncouldn't be more proud of:\n\n:::section{.columns.min200}\n\n<Projects />\n\n:::\n\nAnd so many more on and off the radar. Very, very cool!\n\n## Sponsors\n\nLast but not least, eternal gratitude for all the sponsors that have been\nsupporting me along the way. THANK YOU, THANK YOU, THANK YOU!\n\nAnd eh.. gotta take my chances: how about [joining this awesome club][6]?\n\n:::section{.columns.min300.mt}\n\n<Sponsors />\n\n:::\n\n## Acknowledgements\n\nThanks to Joshua Goldberg for [emoji-blast][7]! 🎉\n\n[1]: https://www.npmjs.com/package/exportman/v/0.0.1\n[2]: /cow-with-orange-scissors-van-gogh-style.webp\n[3]: https://github.com/webpro-nl/knip/releases/tag/5.31.0\n[4]: ../features/auto-fix.mdx\n[5]: https://github.com/unjs/jiti\n[6]: /sponsors\n[7]: https://www.emojiblast.dev\n"
  },
  {
    "path": "packages/docs/src/content/docs/explanations/comparison-and-migration.md",
    "content": "---\ntitle: Comparison & Migration\n---\n\nFirst of all, Knip owes a lot to the projects on this page and they've all been\ninspirational in their own way. For best results, Knip has [a vision embracing\ncomprehensiveness][1] which is larger in scope than any of the alternatives. So\nif any of those tools has the right scope for your requirements, then by all\nmeans, use what suits you best. Note that most projects are no longer\nmaintained.\n\nAll tools have in common that they have less features and don't support the\nconcept of [monorepos/workspaces][2]. Feel free to send in projects that Knip\ndoes not handle better, Knip loves to be challenged!\n\n## Migration\n\nA migration consists of deleting the dependency and its configuration file and\n[getting started with Knip][3]. You should end up with less configuration.\n\n## Comparison\n\n### depcheck\n\n> [Depcheck][4] is a tool for analyzing the dependencies in a project to see:\n> how each dependency is used, which dependencies are useless, and which\n> dependencies are missing from package.json.\n\nThe project has plugins (specials), yet not as many as Knip has and they're not\nas advanced. It also supports compilers (parsers) for non-standard files.\n\nThe following commands are similar:\n\n```sh\ndepcheck\nknip --dependencies\n```\n\n**Project status**: The project is archived and recommends Knip.\n\n### unimported\n\n> Find and fix dangling files and unused dependencies in your JavaScript\n> projects.\n\n[unimported][5] is fast and works well. It works in what Knip calls \"production\nmode\" exclusively. If you're fine with a little bit of configuration and don't\nwant or need to deal with non-production items (such as `devDependencies` and\ntest files), then this might work well for you.\n\nThe following commands are similar:\n\n```sh\nunimported\nknip --production --dependencies --files\n```\n\n**Project status**: The project is archived and recommends Knip.\n\n### ts-prune\n\n> Find unused exports in a typescript project. 🛀\n\n[ts-prune][6] aims to find potentially unused exports in your TypeScript project\nwith zero configuration.\n\nThe following commands are similar:\n\n```sh\nts-prune\nknip --include exports,types,nsExports,nsTypes\n```\n\nUse `knip --exports` to also include enum and namespace members.\n\n**Project status**: The project is archived and recommends Knip.\n\n### ts-unused-exports\n\n> [ts-unused-exports][7] finds unused exported symbols in your Typescript\n> project\n\nThe following commands are similar:\n\n```sh\nts-unused-exports\nknip --include exports,types,nsExports,nsTypes\n```\n\nUse `knip --exports` to also include enum and namespace members.\n\n### tsr\n\n> Remove unused code from your TypeScript Project\n\n[tsr][8] (previously `ts-remove-unused`) removes unused exports, and works based\non a single `tsconfig.json` file (`includes` and `excludes`) and requires no\nconfiguration. It removes the `export` keyword or the whole export declaration.\n\n**Project status**: The project is archived and recommends Knip.\n\n## Related projects\n\nAdditional alternative and related projects include:\n\n- [deadfile][9]\n- [DepClean][10]\n- [dependency-check][11]\n- [find-unused-exports][12]\n- [next-unused][13]\n- [npm-check][14]\n- [renoma][15]\n\nIn general, the [e18e.dev][16] website and in particular the [Cleanup][17]\nsection is a great resource when dealing with technical debt.\n\n[1]: ./why-use-knip.md#comprehensive\n[2]: ../features/monorepos-and-workspaces.md\n[3]: ../overview/getting-started.mdx\n[4]: https://github.com/depcheck/depcheck\n[5]: https://github.com/smeijer/unimported\n[6]: https://github.com/nadeesha/ts-prune\n[7]: https://github.com/pzavolinsky/ts-unused-exports\n[8]: https://github.com/line/tsr\n[9]: https://github.com/M-Izadmehr/deadfile\n[10]: https://github.com/mysteryven/depclean\n[11]: https://github.com/dependency-check-team/dependency-check\n[12]: https://github.com/jaydenseric/find-unused-exports\n[13]: https://github.com/pacocoursey/next-unused\n[14]: https://github.com/dylang/npm-check\n[15]: https://github.com/bluwy/renoma\n[16]: https://e18e.dev\n[17]: https://e18e.dev/guide/cleanup.html\n"
  },
  {
    "path": "packages/docs/src/content/docs/explanations/entry-files.md",
    "content": "---\ntitle: Entry Files\nsidebar:\n  order: 1\n---\n\nEntry files are the starting point for Knip to determine what files are used in\nthe codebase. More entry files lead to increased coverage of the codebase. This\nalso leads to more dependencies to be discovered. This page explains how Knip\nand its plugins try to find entry files so you don't need to configure them\nyourself.\n\n## Default entry file patterns\n\nFor brevity, the [default configuration][1] on the previous page mentions only\n`index.js` and `index.ts`, but the default set of file names and extensions is\nactually a bit larger:\n\n- `index`, `main` and `cli`\n- `js`, `mjs`, `cjs`, `jsx`, `ts`, `mts`, `cts` and `tsx`\n\nThis means files like `main.cjs` and `src/cli.ts` are automatically added as\nentry files. Here's the default configuration in full:\n\n```json\n{\n  \"entry\": [\n    \"{index,cli,main}.{js,cjs,mjs,jsx,ts,cts,mts,tsx}\",\n    \"src/{index,cli,main}.{js,cjs,mjs,jsx,ts,cts,mts,tsx}\"\n  ],\n  \"project\": [\"**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}!\"]\n}\n```\n\nNext to the default locations, Knip looks for `entry` files in other places. In\na monorepo, this is done for each workspace separately.\n\nThe values you set override the default values, they are not merged.\n\nAlso see [FAQ: Where does Knip look for entry files?][2]\n\n## Plugins\n\nPlugins often add entry files. For instance, if the Remix, Storybook and Vitest\nplugins are enabled in your project, they'll add additional entry files. See\n[the next page about plugins][3] for more details about this.\n\n## Scripts in package.json\n\nThe `package.json` is scanned for entry files. The `main`, `bin`, and `exports`\nfields may contain entry files. The `scripts` are also parsed to find entry\nfiles and dependencies. See [Script Parser][4] for more details.\n\n## Ignored files\n\nKnip respects `.gitignore` files. By default, ignored files are not added as\nentry files. This behavior can be disabled by using the [`--no-gitignore`][5]\nflag on the CLI.\n\n## Configuring project files\n\nSee [configuring project files][6] for guidance on tuning `entry` and `project`\nand when to use `ignore`.\n\n[1]: ../overview/configuration.md#defaults\n[2]: ../reference/faq.md#where-does-knip-look-for-entry-files\n[3]: ./plugins.md\n[4]: ../features/script-parser.md\n[5]: ../reference/cli.md#--no-gitignore\n[6]: ../guides/configuring-project-files.md\n"
  },
  {
    "path": "packages/docs/src/content/docs/explanations/plugins.md",
    "content": "---\ntitle: Plugins\nsidebar:\n  order: 2\n---\n\nThis page describes why Knip uses plugins and the difference between `config`\nand `entry` files.\n\nKnip has an extensive and growing [list of built-in plugins][1]. Feel free to\n[write a plugin][2] so others can benefit too!\n\n## What does a plugin do?\n\nPlugins are enabled if the related package is listed in the list of dependencies\nin `package.json`. For instance, if `astro` is listed in `dependencies` or\n`devDependencies`, then the Astro plugin is enabled. And this means that this\nplugin will:\n\n- Handle [configuration files][3] like `astro.config.mjs`\n- Add [entry files][4] such as `src/pages/**/*.astro`\n- Define [command-line arguments][5]\n\n## Configuration files\n\nKnip uses [entry files][6] as starting points to scan your source code and\nresolve other internal files and external dependencies. The module graph can be\nstatically resolved through the `require` and `import` statements in those\nsource files. However, configuration files reference external dependencies in\nvarious ways. Knip uses a plugin for each tool to parse configuration files and\nfind those dependencies.\n\n### Example: ESLint\n\nIn the first example we look at [the ESLint plugin][7]. The default `config`\nfile patterns include `.eslintrc.json`. Here's a minimal example:\n\n```json title=\".eslintrc.json\"\n{\n  \"extends\": [\"airbnb\", \"prettier\"],\n  \"plugins\": [\"@typescript-eslint\"]\n}\n```\n\nConfiguration files like this don't `import` or `require` anything, but they do\nrequire the referenced dependencies to be installed.\n\nIn this case, the plugin will return three dependencies:\n\n- `eslint-config-airbnb`\n- `eslint-config-prettier`\n- `@typescript-eslint/eslint-plugin`\n\nKnip will then look for missing dependencies in `package.json` and report those\nas unlisted. And vice versa, if there are any ESLint plugins listed in\n`package.json`, but unused, those will be reported as well.\n\n### Example: Vitest\n\nThe second example uses [the Vitest plugin][8]. Here's a minimal example of a\nVitest configuration file:\n\n```ts title=\"vitest.config.ts\"\nimport { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n  test: {\n    coverage: {\n      provider: 'istanbul',\n    },\n    environment: 'happy-dom',\n  },\n});\n```\n\nThe Vitest plugin reads this configuration and returns two dependencies:\n\n- `@vitest/coverage-istanbul`\n- `vitest-environment-happy-dom`\n\nKnip will look for missing and unused dependencies in `package.json` and report\naccordingly.\n\nSome tools allow configuration to be stored in `package.json`, that's why some\nplugins contain `package.json` in the list of `config` files.\n\n:::tip[Summary]\n\nPlugins load configuration files to find referenced dependencies, and determine\nunused and unlisted dependencies.\n\n:::\n\n## Entry files\n\nMany plugins have default `entry` files configured. When the plugin is enabled,\nKnip will add entry files as configured by the plugin to resolve used files and\ndependencies.\n\nFor example, if `next` is listed as a dependency in `package.json`, the Next.js\nplugin will automatically add multiple patterns as entry files, such as\n`pages/**/*.{js,jsx,ts,tsx}`. If `vitest` is listed, the Vitest plugin adds\n`**/*.{test,test-d,spec,spec-d}.ts` as entry file patterns. Most plugins have\nentry files configured, so you don't have to.\n\nIt's mostly plugins for meta frameworks and test runners that have `entry` files\nconfigured.\n\n:::tip[Plugins result in less configuration]\n\nPlugins uses entry file patterns as defined in your configuration file of these\ntools. So you don't need to repeat this in your Knip configuration.\n\n:::\n\nFor example, let's say your Playwright configuration contains the following:\n\n```ts title=\"playwright.config.ts\"\nimport type { PlaywrightTestConfig } from '@playwright/test';\n\nconst config: PlaywrightTestConfig = {\n  testDir: 'integration',\n  testMatch: ['**/*-test.ts'],\n};\n\nexport default config;\n```\n\nThe Playwright plugin will read this configuration file and return those entry\npatterns (`integration/**/*-test.ts`). Knip will then not use the default entry\npatterns.\n\nYou can still override this behavior in your Knip configuration:\n\n```json title=\"knip.json\"\n{\n  \"playwright\": {\n    \"entry\": \"src/**/*.integration.ts\"\n  }\n}\n```\n\nThis should not be necessary though. Please consider opening a pull request or a\nbug report if any plugin is not behaving as expected.\n\n:::tip[Summary]\n\nPlugins try hard to automatically add the correct entry files.\n\n:::\n\n## Entry files from config files\n\nEntry files are part of plugin configuration (as described in the previous\nsection). Yet plugins can also return additional entry files after parsing\nconfiguration files. Below are some examples of configuration files parsed by\nplugins to return additional entry files. The goal of these examples is to give\nyou an idea about the various ways Knip and its plugins try to find entry files\nso you don't need to configure them yourself.\n\n### Angular\n\nThe Angular plugin parses the Angular configuration file. Here's a fragment:\n\n```json title=\"angular.json\"\n{\n  \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n  \"projects\": {\n    \"knip-angular-example\": {\n      \"architect\": {\n        \"build\": {\n          \"builder\": \"@angular-devkit/build-angular:browser\",\n          \"options\": {\n            \"outputPath\": \"dist/knip-angular-example\",\n            \"main\": \"src/main.ts\",\n            \"tsConfig\": \"tsconfig.app.json\"\n          }\n        }\n      }\n    }\n  }\n}\n```\n\nThis will result in `src/main.ts` being added as an entry file (and\n`@angular-devkit/build-angular` as a referenced dependency).\n\nAdditionally, the Angular plugin returns `tsconfig.app.json` as a configuration\nfile for the TypeScript plugin.\n\n### GitHub Actions\n\nThis plugin parses workflow YAML files. This fragment contains three `run`\nscripts:\n\n```yml title=\".github/workflows/deploy.yml\"\njobs:\n  integration:\n    runs-on: ubuntu-latest\n    steps:\n      - run: npm install\n      - run: node scripts/build.js\n      - run: node --loader tsx scripts/deploy.ts\n      - run: playwright test -c playwright.web.config.ts\n        working-dir: e2e\n```\n\nFrom these scripts, the `scripts/build.js` and `scripts/deploy.ts` files will be\nadded as entry files by the GitHub Actions plugin.\n\nAdditionally, the file `e2e/playwright.web.config.ts` is detected and will be\nhanded over as a Playwright configuration file.\n\nRead more about this in [command-line arguments][5].\n\n### webpack\n\nLet's take a look at this example webpack configuration file:\n\n```js title=\"webpack.config.js\"\nmodule.exports = env => {\n  return {\n    entry: {\n      main: './src/app.ts',\n      vendor: './src/vendor.ts',\n    },\n    module: {\n      rules: [\n        {\n          test: /\\.(woff|ttf|ico|woff2|jpg|jpeg|png|webp)$/i,\n          use: 'base64-inline-loader',\n        },\n      ],\n    },\n  };\n};\n```\n\nThe webpack plugin will parse this and add `./src/app.ts` and `./src/vendor.ts`\nas entry files. It will also add `base64-inline-loader` as a referenced\ndependency.\n\n:::tip[Summary]\n\nIn your config files, plugins can find additional entry files and also other\nconfig files recursively.\n\n:::\n\n## Bringing it all together\n\nSometimes a configuration file is a JavaScript or TypeScript file that imports\ndependencies, but also contains configuration that needs to be parsed by a\nplugin to find additional dependencies.\n\nLet's take a look at this example Vite configuration file:\n\n```ts title=\"vite.config.ts\"\nimport { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\nexport default defineConfig(async ({ mode, command }) => {\n  return {\n    plugins: [react()],\n    test: {\n      setupFiles: ['./setup-tests.ts'],\n      environment: 'happy-dom',\n      coverage: {\n        provider: 'c8',\n      },\n    },\n  };\n});\n```\n\nThis file imports `vite` and `@vitejs/plugin-react` directly, but also\nindirectly references the `happy-dom` and `@vitest/coverage-c8` packages.\n\nThe Vite plugin of Knip will **dynamically** load this configuration file and\nparse the exported configuration. But it's not aware of the `vite` and\n`@vitejs/plugin-react` imports. This is why such `config` files are also\nautomatically added as `entry` files for Knip to **statically** resolve the\n`import` and `require` statements.\n\nAdditionally, `./setup-tests.ts` will be added as an `entry` file.\n\n:::note\n\nWhen plugins dynamically load configuration files, conditional dependencies may\nnot be detected if the condition evaluates differently during analysis. See\n[conditional or dynamic dependencies][9] for details and workarounds.\n\n:::\n\n## Command-Line Arguments\n\nPlugins may define the arguments where Knip should look for entry files,\nconfiguration files and dependencies. We've already seen some examples above:\n\n```sh\nnode --loader tsx scripts/deploy.ts\nplaywright test -c playwright.web.config.ts\n```\n\nPlease see [script parser][10] for more details.\n\n## Config File Location\n\nIf configuration files aren't in their default location and they are not\nreferenced through some script like `vite -c ./dir/vite.config.ts`, then make\nsure to tell Knip about it. Two examples:\n\n```json title=\"knip.jsonc\"\n{\n  \"playwright\": { \"config\": [\"e2e/playwright.config.ts\"] },\n  \"vite\": \"packages/*/vite.config.ts\" // shorthand without `config` and array notation\n}\n```\n\nThis is common in projects where a directory like `packages/lib` is not an\nactual workspace with a `package.json` file. Also see [integrated monorepos][11]\nfor similar cases.\n\n## Summary\n\n:::tip[Summary]\n\nPlugins are configured with two distinct types of files:\n\n- `config` files are dynamically loaded and parsed by the plugin\n- `entry` files are added to the module graph\n- Both can recursively lead to additional entry files, config files and\n  dependencies\n\n:::\n\n[1]: ../reference/plugins.md\n[2]: ../writing-a-plugin/index.md\n[3]: #configuration-files\n[4]: #entry-files\n[5]: #command-line-arguments\n[6]: ./entry-files.md\n[7]: ../reference/plugins/eslint.md\n[8]: ../reference/plugins/vitest.md\n[9]: ../guides/handling-issues.mdx#conditional-or-dynamic-dependencies\n[10]: ../features/script-parser.md\n[11]: ../features/integrated-monorepos.md\n"
  },
  {
    "path": "packages/docs/src/content/docs/explanations/why-use-knip.md",
    "content": "---\ntitle: Why use Knip?\nsidebar:\n  order: 3\n---\n\nThe value of removing clutter is clear, but finding it manually is tedious. This\nis where Knip comes in: comprehensive and accurate results at any scale.\n\n:::tip[TL;DR]\n\nKnip finds and fixes unused dependencies, exports and files.\n\nDeep analysis from [fine-grained entry points][1] based on the actual frameworks\nand tooling in [(mono)repos][2] for accurate and actionable results. Advanced\nfeatures for maximum coverage:\n\n- [Custom module resolution][3]\n- [Configuration file parsers][4]\n- [Advanced shell script parser][5]\n- [Built-in and custom compilers][6]\n- [Auto-fix most issues][7]\n\n:::\n\n## Less is more\n\nThere are plenty of reasons to delete unused files, dependencies and \"dead\ncode\":\n\n- Easier maintenance: things are easier to manage when there's less of it.\n- Improved performance: startup time, build time and/or bundle size can be\n  negatively impacted when unused code, files and/or dependencies are included.\n  Relying on tree-shaking when bundling code helps, but it's not a silver\n  bullet.\n- Easier onboarding: there should be no doubts about whether files, dependencies\n  and exports are actually in use or not. Especially for people new to the\n  project and/or taking over responsibilities this is harder to grasp.\n- Prevent regressions: tools like TypeScript, ESLint and Prettier do all sorts\n  of checks and linting to report violations and prevent regressions. Knip does\n  the same for dependencies, exports and files that are obsolete.\n- Keeping dead code around has a negative value on readability, as it can be\n  misleading and distracting. Even if it serves no purpose it will need to be\n  maintained (source: [Safe dead code removal → YAGNI][8]).\n- Also see [Why are unused dependencies a problem?][9] and [Why are unused\n  exports a problem?][10].\n\n## Automation\n\nCode and dependency management is usually not the most exciting task for most of\nus. Knip's mission is to automate finding clutter. This is such a tedious job if\nyou were to do it manually, and where would you even start? Knip applies many\ntechniques and heuristics to report what you need and save a lot of time.\n\n:::tip\n\nKnip not only finds clutter, it can also [remove clutter][7]!\n\nUse Knip next to a linter like ESLint or Biome: after removing unused variables\ninside files, Knip might find even more unused code. Rinse and repeat!\n\n:::\n\n## Comprehensive\n\nYou can use alternative tools that do the same. However, the advantage of a\nstrategy that addresses all of dependencies, exports and files is in their\nsynergy:\n\n- Utilizing plugins to find their dependencies includes the capacity to find\n  additional entry and configuration files. This results in more resolved and\n  used files. Better coverage gives better insights into unused files and\n  exports.\n- Analyzing more files reveals more unused exports and dependency usage,\n  refining the list of both unused and unlisted dependencies.\n- This approach is amplified in a monorepo setting. In fact, files and internal\n  dependencies can recursively reference each other (across workspaces).\n\n## Greenfield or Legacy\n\nInstalling Knip in greenfield projects ensures the project stays neat and tidy\nfrom the start. Add it to your CI workflow and prevent any regressions from\nentering the codebase.\n\n:::tip\n\nUse Knip in a CI environment to prevent future regressions.\n\n:::\n\nIn large and/or legacy projects, Knip may report false positives and require\nsome configuration. It aims to be a great assistant when cleaning up parts of\nthe project or doing large refactors. Even a list of results with a few false\npositives is many times better and faster than if you were to do it manually.\n\n## Unobtrusive\n\nKnip does not introduce new syntax for you to learn. This may sound obvious, but\nconsider comments like the following:\n\n```js\n// eslint-disable-next-line\n// prettier-ignore\n// @ts-expect-error\n```\n\nMaybe you wonder why Knip does not have similar comments like `// knip-ignore`\nso you can get rid of false positives? A variety of reasons:\n\n1. A false positive may be a bug in Knip, and should be reported, not dismissed.\n2. Instead of proprietary comments, use [standardized annotations][11] that also\n   serve as documentation.\n3. In the event you want to remove Knip, just uninstall `knip` without having to\n   remove useless comments scattered throughout the codebase.\n\nTip: use `@lintignore` in JSDoc comments, so other linters can use the same.\n\n[1]: ./entry-files.md\n[2]: ../features/monorepos-and-workspaces.md\n[3]: ../reference/faq.md#why-doesnt-knip-use-an-existing-module-resolver\n[4]: ./plugins.md#configuration-files\n[5]: ../features/script-parser.md\n[6]: ../features/compilers.md\n[7]: ../features/auto-fix.mdx\n[8]: https://jfmengels.net/safe-dead-code-removal/#yagni-you-arent-gonna-need-it\n[9]: ../typescript/unused-dependencies.md#why-are-unused-dependencies-a-problem\n[10]: ../typescript/unused-exports.md#why-are-unused-exports-a-problem\n[11]: ../reference/jsdoc-tsdoc-tags.md\n"
  },
  {
    "path": "packages/docs/src/content/docs/features/auto-fix.mdx",
    "content": "---\ntitle: Auto-fix\n---\n\nimport { Tabs, TabItem } from '@astrojs/starlight/components';\nimport { Badge } from '@astrojs/starlight/components';\n\nRun Knip as you normally would, and if the report looks good then run it again\nwith the `--fix` flag to let Knip automatically apply fixes. It fixes the\nfollowing [issue types][1]:\n\n- Remove `export` keyword for unused exports, re-exports, and exported types\n- Remove `export default` keywords for unused default exports\n- Remove unused enum and namespace members\n- Remove unused `dependencies` and `devDependencies` from `package.json`\n- Remove unused files\n- Remove unused catalog entries\n\n:::caution\n\nUse a VCS (version control system) like Git to review and undo changes as\nnecessary.\n\n:::\n\n## Flags\n\n### Fix\n\nAdd the `--fix` flag to remove unused exports and dependencies:\n\n```sh\nknip --fix\n```\n\nAdd `--allow-remove-files` to allow Knip to remove unused files:\n\n```sh\nknip --fix --allow-remove-files\n```\n\nUse `--fix-type` to fix only specific issue types:\n\n- `dependencies`\n- `exports`\n- `types`\n- `files`\n- `catalog`\n\nExample:\n\n```sh\nknip --fix-type exports,types\nknip --fix-type exports --fix-type types   # same as above\n```\n\n### Format\n\nAdd `--format` to format the modified files using the formatter and\nconfiguration in your project. Supports Biome, deno fmt, dprint and Prettier\n(using [Formatly][2]):\n\n```sh\nknip --fix --format\n```\n\n## Demo\n\n<video controls width=\"500\">\n  <source src=\"/screenshots/fix.mp4\" type=\"video/mp4\" />\n\n  <source src=\"/screenshots/fix.webm\" type=\"video/webm\" />\n</video>\n\n## Post-fix\n\nAfter Knip has fixed issues, there are four things to consider:\n\n### 1. Use a formatter\n\nUse a tool like Prettier or Biome if the code needs formatting. Knip removes the\nminimum amount of code while leaving it in a working state.\n\n:::tip\n\nAdd the `--format` flag to format the modified files using the formatter and\nconfiguration in your project.\n\n:::\n\n### 2. Unused variables\n\nUse a tool like ESLint or Biome to find and remove unused imports and variables\ninside files. Even better, try [remove-unused-vars][3] to remove unused\nvariables within files.\n\nThis may result in more deleted code, and Knip may then find more unused code.\nRinse and repeat!\n\n### 3. Unused dependencies\n\nVerify changes in `package.json` and update dependencies using your package\nmanager.\n\n<Tabs syncKey=\"pm\">\n  <TabItem label=\"npm\">\n    ```shell\n    npm install\n    ```\n  </TabItem>\n\n  <TabItem label=\"pnpm\">\n    ```shell\n    pnpm install\n    ```\n  </TabItem>\n\n  <TabItem label=\"bun\">\n    ```shell\n    bun install\n    ```\n  </TabItem>\n\n  <TabItem label=\"yarn\">\n    ```shell\n    yarn\n    ```\n  </TabItem>\n</Tabs>\n\n### 4. Install unlisted dependencies\n\nIf Knip reports unlisted dependencies or binaries, they should be installed\nusing the package manager in the project, for example:\n\n<Tabs syncKey=\"pm\">\n  <TabItem label=\"npm\">\n    ```shell\n    npm install unlisted-package\n    ```\n  </TabItem>\n\n  <TabItem label=\"pnpm\">\n    ```shell\n    pnpm add unlisted-package\n    ```\n  </TabItem>\n\n  <TabItem label=\"bun\">\n    ```shell\n    bun add unlisted-package\n    ```\n  </TabItem>\n\n  <TabItem label=\"yarn\">\n    ```shell\n    yarn add unlisted-package\n    ```\n  </TabItem>\n</Tabs>\n\n## Example results\n\n### Exports\n\nThe `export` keyword for unused exports is removed:\n\n```diff title=\"module.ts\"\n-export const unused = 1;\n-export default class MyClass {}\n+const unused = 1;\n+class MyClass {}\n```\n\nThe `default` keyword was also removed here.\n\nKnip removes the whole or part of export declarations:\n\n```diff title=\"module.ts\"\ntype Snake = 'python' | 'anaconda';\nconst Owl = 'Hedwig';\nconst Hawk = 'Tony';\n-export type { Snake };\n-export { Owl, Hawk };\n+;\n+;\n```\n\n### Re-exports\n\nKnip removes the whole or part of re-exports:\n\n```diff title=\"file.js\"\n-export { Cat, Dog } from './pets';\n-export { Lion, Elephant } from './jungle';\n+export { Elephant } from './jungle'\n```\n\nAlso across any chain of re-exports:\n\n<Tabs>\n  <TabItem label=\"module.ts\">\n    ```diff\n    export const Hawk = 'Tony';\n    -export const Owl = 'Hedwig';\n    +const Owl = 'Hedwig';\n    ```\n  </TabItem>\n\n  <TabItem label=\"barrel.ts\">\n    ```diff\n    export * from './module.js';\n    ```\n  </TabItem>\n\n  <TabItem label=\"index.ts\">\n    ```diff\n    -export { Hawk, Owl } from './barrel.js';\n    +export { Hawk } from './barrel.js'\n    ```\n  </TabItem>\n</Tabs>\n\n### Export assignments\n\nKnip removes individual exported items in \"export assignments\", but does not\nremove the entire export declaration if it's empty:\n\n```diff title=\"file.js\"\n-export const { a, b  } = fn();\n+export const {   } = fn();\n\n-export const [c, d] = [c, d];\n+export const [, ] = [c, d];\n```\n\nReason: the right-hand side of the assignment might have side-effects. It's not\nsafe to always remove the whole declaration. This could be improved in the\nfuture (feel free to open an issue/RFC).\n\n### Enum members\n\nUnused members of enums are removed:\n\n```diff title=\"file.ts\"\nexport enum Directions {\n  North = 1,\n  East = 2,\n-  South = 3,\n  West = 4,\n}\n```\n\n### CommonJS\n\nKnip supports CommonJS and removes unused exports:\n\n```diff title=\"common.js\"\n-module.exports = { identifier, unused };\n+module.exports = { identifier,  };\n\n-module.exports.UNUSED = 1;\n-module.exports['ACCESS'] = 1;\n+\n+\n```\n\nWarning: the right-hand side of such an assignment might have side-effects. Knip\ncurrently removes the whole declaration (feel free to open an issue/RFC).\n\n### Dependencies\n\nUnused dependencies are removed from `package.json`:\n\n```diff title=\"package.json\"\n {\n   \"name\": \"my-package\",\n   \"dependencies\": {\n-    \"rimraf\": \"*\",\n-    \"unused-dependency\": \"*\"\n+    \"rimraf\": \"*\"\n   },\n-  \"devDependencies\": {\n-    \"unreferenced-package\": \"5.3.3\"\n-  }\n+  \"devDependencies\": {}\n }\n```\n\n### Catalog entries\n\nUnused [catalog][4] entries are removed from `pnpm-workspace.yaml`:\n\n```diff title=\"pnpm-workspace.yaml\"\n packages:\n   - 'packages/*'\n catalog:\n   react: ^18.0.0\n-  unused-package: ^1.0.0\n```\n\nCatalogs in `package.json` are supported as well.\n\n## What's not included\n\nOperations that auto-fix does not (yet) perform and why:\n\n- Add unlisted (dev) dependencies to `package.json` (should it go into\n  `dependencies` or `devDependencies`? For monorepos in current workspace or\n  root?)\n- Add unlisted binaries (which package and package version contains the used\n  binary?)\n- Fix duplicate exports (which one should be removed?)\n\n[1]: ../reference/issue-types.md\n[2]: https://github.com/JoshuaKGoldberg/formatly\n[3]: https://github.com/webpro-nl/remove-unused-vars\n[4]: https://pnpm.io/catalogs\n"
  },
  {
    "path": "packages/docs/src/content/docs/features/compilers.md",
    "content": "---\ntitle: Compilers\n---\n\nProjects may have source files that are not JavaScript or TypeScript, and thus\nrequire compilation (or transpilation, or pre-processing, you name it). Files\nlike `.mdx`, `.astro`, `.vue` and `.svelte` may also import other source files\nand external dependencies. So ideally, these files are included when linting the\nproject. That's why Knip supports compilers.\n\n## Built-in compilers\n\nKnip has built-in \"compilers\" for the following file extensions:\n\n- `.astro`\n- `.css` (only enabled by `tailwindcss`)\n- `.mdx`\n- `.prisma`\n- `.sass` + `.scss`\n- `.svelte`\n- `.vue`\n\nKnip does not include real compilers for those files, but regular expressions to\ncollect `import` statements. This is fast, requires no dependencies, and enough\nfor Knip to build the module graph.\n\nOn the other hand, real compilers may expose their own challenges in the context\nof Knip. For instance, the Svelte compiler keeps `exports` intact, while they\nmight represent component properties. This results in those exports being\nreported as unused by Knip.\n\nThe built-in functions seem to do a decent job, but override them however you\nlike.\n\nCompilers are enabled only if certain dependencies are found. If that's not\nworking for your project, set `true` and enable any compiler manually:\n\n```ts title=\"knip.ts\"\nexport default {\n  compilers: {\n    mdx: true,\n  },\n};\n```\n\n## Custom compilers\n\nBuilt-in compilers can be overridden, and additional compilers can be added.\nSince compilers are functions, the Knip configuration file must be a dynamic\n`.js` or `.ts` file.\n\n### Interface\n\nThe compiler function interface is straightforward. Text in, text out:\n\n```ts\n(source: string, filename: string) => string;\n```\n\nThis may also be an `async` function.\n\n:::tip[Note]\n\nCompilers will automatically have their extension added as a default extension\nto Knip. This means you don't need to add something like `**/*.{ts,vue}` to the\n`entry` or `project` file patterns manually.\n\n:::\n\n### Examples\n\n- [CSS][1]\n- [MDX][2]\n- [Svelte][3]\n- [Vue][4]\n\n#### CSS\n\nHere's an example, minimal compiler for CSS files:\n\n```ts title=\"knip.ts\"\nexport default {\n  compilers: {\n    css: (text: string) => [...text.matchAll(/(?<=@)import[^;]+/g)].join('\\n'),\n  },\n};\n```\n\nYou may wonder why the CSS compiler is not included by default. It's currently\nnot clear if it should be included. And if so, what would be the best way to\ndetermine it should be enabled, and what syntax(es) it should support. Note that\nTailwind CSS and SASS/SCSS compilers are included.\n\n#### MDX\n\nAnother example, in case the built-in MDX compiler is not enough:\n\n```ts\nimport { compile } from '@mdx-js/mdx';\n\nexport default {\n  compilers: {\n    mdx: async text => (await compile(text)).toString(),\n  },\n};\n```\n\n#### Svelte\n\nIn a Svelte project, the compiler is automatically enabled. Override and use\nSvelte's compiler for better results if the built-in \"compiler\" is not enough:\n\n```ts\nimport type { KnipConfig } from 'knip';\nimport { compile } from 'svelte/compiler';\n\nexport default {\n  compilers: {\n    svelte: (source: string) => compile(source, {}).js.code,\n  },\n} satisfies KnipConfig;\n```\n\n#### Vue\n\nIn a Vue project, the compiler is automatically enabled. Override and use Vue's\nparser for better results if the built-in \"compiler\" is not enough:\n\n```ts\nimport type { KnipConfig } from 'knip';\nimport {\n  parse,\n  type SFCScriptBlock,\n  type SFCStyleBlock,\n} from 'vue/compiler-sfc';\n\nfunction getScriptBlockContent(block: SFCScriptBlock | null): string[] {\n  if (!block) return [];\n  if (block.src) return [`import '${block.src}'`];\n  return [block.content];\n}\n\nfunction getStyleBlockContent(block: SFCStyleBlock | null): string[] {\n  if (!block) return [];\n  if (block.src) return [`@import '${block.src}';`];\n  return [block.content];\n}\n\nfunction getStyleImports(content: string): string {\n  return [...content.matchAll(/(?<=@)import[^;]+/g)].join('\\n');\n}\n\nconst config = {\n  compilers: {\n    vue: (text: string, filename: string) => {\n      const { descriptor } = parse(text, { filename, sourceMap: false });\n      return [\n        ...getScriptBlockContent(descriptor.script),\n        ...getScriptBlockContent(descriptor.scriptSetup),\n        ...descriptor.styles.flatMap(getStyleBlockContent).map(getStyleImports),\n      ].join('\\n');\n    },\n  },\n} satisfies KnipConfig;\n\nexport default config;\n```\n\n[1]: #css\n[2]: #mdx\n[3]: #svelte\n[4]: #vue\n"
  },
  {
    "path": "packages/docs/src/content/docs/features/integrated-monorepos.md",
    "content": "---\ntitle: Integrated Monorepos\nsidebar:\n  order: 3\n---\n\nSome repositories have a single `package.json`, but consist of multiple projects\nwith configuration files across the repository. A good example is the [Nx\nintegrated monorepo style][1].\n\n:::tip\n\nAn integrated monorepo is a single workspace.\n\n:::\n\n## Entry Files\n\nThe default entrypoints files might not be enough. Here's an idea that might fit\nthis type of monorepo:\n\n```json title=\"knip.json\"\n{\n  \"entry\": [\"{apps,libs}/**/src/index.{ts,tsx}\"],\n  \"project\": [\"{apps,libs}/**/src/**/*.{ts,tsx}\"]\n}\n```\n\n## Plugins\n\nLet's assume some of these projects are applications (\"apps\") which have their\nown ESLint configuration files and Cypress configuration and test files. This\nmay result in those files getting reported as unused, and consequently also the\ndependencies they import and refer to.\n\nIn that case, we could configure the ESLint and Cypress plugins like this:\n\n```json title=\"knip.json\"\n{\n  \"eslint\": {\n    \"config\": [\"{apps,libs}/**/.eslintrc.json\"]\n  },\n  \"cypress\": {\n    \"entry\": [\"apps/**/cypress.config.ts\", \"apps/**/cypress/e2e/*.spec.ts\"]\n  }\n}\n```\n\nAdapt the file patterns to your project, and the relevant `config` and `entry`\nfiles and dependencies should no longer be reported as unused.\n\n## Internal Workspace Dependencies\n\nA note about repositories with multiple `package.json` files and **internal**\nworkspace packages: it is recommended to list all dependencies in each consuming\n`package.json`, allowing Knip to do fine-grained reporting of both unused and\nunlisted dependencies.\n\nAn alternative is to `ignoreDependencies: [\"@internal/*\"]`.\n\n[1]: https://nx.dev/getting-started/tutorials/integrated-repo-tutorial\n"
  },
  {
    "path": "packages/docs/src/content/docs/features/monorepos-and-workspaces.md",
    "content": "---\ntitle: Monorepos & Workspaces\nsidebar:\n  order: 2\n---\n\nWorkspaces are handled out-of-the-box by Knip.\n\nWorkspaces are sometimes also referred to as package-based monorepos, or as\npackages in a monorepo. Knip uses the term workspace exclusively to indicate a\ndirectory that has a `package.json`.\n\n## Configuration\n\nHere's example configuration with custom `entry` and `project` patterns:\n\n```json title=\"knip.json\"\n{\n  \"workspaces\": {\n    \".\": {\n      \"entry\": \"scripts/*.js\",\n      \"project\": \"scripts/**/*.js\"\n    },\n    \"packages/*\": {\n      \"entry\": \"{index,cli}.ts\",\n      \"project\": \"**/*.ts\"\n    },\n    \"packages/cli\": {\n      \"entry\": \"bin/cli.js\"\n    }\n  }\n}\n```\n\n:::tip\n\nRun Knip without any configuration to see if and where custom `entry` and/or\n`project` files are necessary per workspace.\n\n:::\n\nEach workspace has the same [default configuration][1].\n\nThe root workspace is named `\".\"` under `workspaces` (like in the example\nabove).\n\n:::caution\n\nIn a project with workspaces, the `entry` and `project` options at the root\nlevel are ignored. Use the workspace named `\".\"` for those (like in the example\nabove).\n\n:::\n\n## Workspaces\n\nKnip reads workspaces from four possible locations:\n\n1. The `workspaces` array in `package.json` (npm, Bun, Yarn, Lerna)\n2. The `packages` array in `pnpm-workspace.yaml` (pnpm)\n3. The `workspaces.packages` array in `package.json` (legacy)\n4. The `workspaces` object in Knip configuration\n\nThe `workspaces` in Knip configuration (4) not already defined in the root\n`package.json` or `pnpm-workspace.yaml` (1, 2, 3) are added to the analysis.\n\n:::caution\n\nA workspace must have a `package.json` file.\n\n:::\n\nFor projects with only a root `package.json`, please see [integrated\nmonorepos][2].\n\n## Additional workspaces\n\nIf a workspaces is not configured as such in `package.json#workspaces` (or\n`pnpm-workspace.yaml`) it can be added to the Knip configuration manually. Add\ntheir path to the `workspaces` configuration object the same way as\n`\"packages/cli\": {}` in the example above.\n\n## Source mapping\n\nSee [Source Mapping][3].\n\n## Additional options\n\nThe following options are available inside workspace configurations:\n\n- [ignore][4]\n- [ignoreBinaries][5]\n- [ignoreDependencies][6]\n- [ignoreMembers][7]\n- [ignoreUnresolved][8]\n- [includeEntryExports][9]\n\n[Plugins][10] can be configured separately per workspace.\n\nUse `--debug` for verbose output and see the workspaces Knip includes, their\nconfigurations, enabled plugins, glob options and resolved files.\n\n## Filter workspaces\n\nUse the `--workspace` (or `-W`) argument to select one or more workspaces:\n\n```sh\nknip --workspace packages/my-lib\n```\n\nThe filter supports multiple formats:\n\n```sh\nknip --workspace @myorg/my-lib     # Package name\nknip --workspace '@myorg/*'        # Package name glob\nknip --workspace packages/my-lib   # Directory path\nknip --workspace './apps/*'        # Directory glob\n```\n\nCombine selectors to include or exclude workspaces:\n\n```sh\nknip --workspace @myorg/* --workspace '!@myorg/legacy'\nknip --workspace './apps/*' --workspace '@shared/utils'\n```\n\nThis will include the target workspace(s), but also ancestor and dependent\nworkspaces. For two reasons:\n\n- Ancestor workspaces may list dependencies in `package.json` the linted\n  workspace uses.\n- Dependent workspaces may reference exports from the linted workspace.\n\nTo lint the workspace in isolation, there are two options:\n\n- Combine the `workspace` argument with [strict production mode][11].\n- Run Knip from inside the workspace directory.\n\n[1]: ../overview/configuration.md#defaults\n[2]: ./integrated-monorepos.md\n[3]: ./source-mapping.md\n[4]: ../reference/configuration.md#ignore\n[5]: ../reference/configuration.md#ignorebinaries\n[6]: ../reference/configuration.md#ignoredependencies\n[7]: ../reference/configuration.md#ignoremembers\n[8]: ../reference/configuration.md#ignoreunresolved\n[9]: ../reference/configuration.md#includeentryexports\n[10]: ../reference/configuration.md#plugins\n[11]: ./production-mode.md#strict-mode\n"
  },
  {
    "path": "packages/docs/src/content/docs/features/production-mode.md",
    "content": "---\ntitle: Production Mode\nsidebar:\n  order: 1\n---\n\nThe default mode for Knip is comprehensive and targets all project code,\nincluding configuration files, test files, Storybook stories, and so on. Test\nfiles usually import production files. This prevents production files or their\nexports from being reported as unused, while sometimes both of them can be\ndeleted. Knip features a \"production mode\" to focus only on the code that you\nship.\n\n## Configuration\n\nTo tell Knip what is production code, add an exclamation mark behind each\n`pattern!` that represents production code:\n\n```json title=\"knip.json\"\n{\n  \"entry\": [\"src/index.ts!\", \"build/script.js\"],\n  \"project\": [\"src/**/*.ts!\", \"build/*.js\"]\n}\n```\n\nDepending on file structure and enabled plugins, you might not need to modify\nyour configuration at all.\n\nRun Knip with the `--production` flag:\n\n```sh\nknip --production\n```\n\nHere's what's included in production mode:\n\n- Only `entry` and `project` patterns suffixed with `!`\n- Only production `entry` file patterns exported by plugins (such as Next.js and\n  Remix)\n- Only the `start` script (of `package.json#scripts`)\n- Ignore exports with the [`@internal` tag][1]\n\n:::note\n\nThe production run does not replace the default run. Depending on your needs you\ncan run either of them or both separately. Usually both modes can share the same\nconfiguration.\n\n:::\n\nTo see the difference between default and production mode in great detail, use\nthe `--debug` flag and inspect what entry and project files are used, and the\nplugins that are enabled. For instance, in production mode this shows that files\nsuch as tests and Storybook files (stories) are excluded from the analysis.\n\nIn case files like mocks and test helpers are reported as unused files, use\nnegated patterns to exclude those files in production mode:\n\n```json title=\"knip.json\"\n{\n  \"entry\": [\"src/index.ts!\"],\n  \"project\": [\"src/**/*.ts!\", \"!src/test-helpers/**!\"]\n}\n```\n\nAlso see [configuring project files][2] to align `entry` and `project` with\nproduction mode.\n\n## Strict Mode\n\nIn production mode, only `dependencies` (not `devDependencies`) are considered\nwhen finding unused or unlisted dependencies.\n\nAdditionally, the `--strict` flag can be added to:\n\n- Verify isolation: workspaces should use strictly their own `dependencies`\n- Include `peerDependencies` when finding unused or unlisted dependencies\n- Report type-only imports listed in `dependencies`\n\n```sh\nknip --production --strict\n```\n\nUsing `--strict` implies `--production`, so the latter can be omitted.\n\n## Types\n\nAdd `--exclude types` if you don't want to include types in the report:\n\n```sh\nknip --production --exclude types\n```\n\n[1]: ../reference/jsdoc-tsdoc-tags.md#internal\n[2]: ../guides/configuring-project-files.md\n"
  },
  {
    "path": "packages/docs/src/content/docs/features/reporters.md",
    "content": "---\ntitle: Reporters & Preprocessors\n---\n\n## Built-in Reporters\n\nKnip provides the following built-in reporters:\n\n- `codeowners`\n- `compact`\n- [`disclosure`][1]\n- [`github-actions`][2]\n- [`json`][3]\n- [`markdown`][4]\n- [`codeclimate`][5]\n- `symbols` (default)\n\nExample usage:\n\n```sh\nknip --reporter compact\n```\n\n### JSON\n\nThe built-in `json` reporter output is meant to be consumed by other tools. It\nreports in JSON format with unused `files` and `issues` as an array with one\nobject per file structured like this:\n\n```json\n{\n  \"issues\": [\n    {\n      \"file\": \"package.json\",\n      \"owners\": [\"@org/admin\"],\n      \"dependencies\": [{ \"name\": \"jquery\", \"line\": 5, \"col\": 6, \"pos\": 71 }],\n      \"devDependencies\": [{ \"name\": \"lodash\", \"line\": 9, \"col\": 6, \"pos\": 99 }],\n      \"unlisted\": [{ \"name\": \"react\" }, { \"name\": \"@org/unresolved\" }],\n      \"exports\": [],\n      \"types\": [],\n      \"duplicates\": []\n    },\n    {\n      \"file\": \"src/Registration.tsx\",\n      \"owners\": [\"@org/owner\"],\n      \"dependencies\": [],\n      \"devDependencies\": [],\n      \"binaries\": [],\n      \"unresolved\": [\n        { \"name\": \"./unresolved\", \"line\": 8, \"col\": 23, \"pos\": 407 }\n      ],\n      \"exports\": [{ \"name\": \"unusedExport\", \"line\": 1, \"col\": 14, \"pos\": 13 }],\n      \"types\": [\n        { \"name\": \"unusedEnum\", \"line\": 3, \"col\": 13, \"pos\": 71 },\n        { \"name\": \"unusedType\", \"line\": 8, \"col\": 14, \"pos\": 145 }\n      ],\n      \"enumMembers\": [\n        {\n          \"namespace\": \"MyEnum\",\n          \"name\": \"unusedMember\",\n          \"line\": 13,\n          \"col\": 3,\n          \"pos\": 167\n        },\n        {\n          \"namespace\": \"MyEnum\",\n          \"name\": \"unusedKey\",\n          \"line\": 15,\n          \"col\": 3,\n          \"pos\": 205\n        }\n      ],\n      \"duplicates\": [\"Registration\", \"default\"]\n    }\n  ]\n}\n```\n\nThe keys match the [reported issue types][6]. Example usage:\n\n```sh\nknip --reporter json\n```\n\n### GitHub Actions\n\nUse the GitHub Actions reporter in a workflow for annotations in pull requests.\nExample usage:\n\n```sh\nknip --reporter github-actions\n```\n\nChanged files in pull requests will now contain inline annotations for lint\nfindings.\n\n### Markdown\n\nThe built-in `markdown` reporter output is meant to be saved to a Markdown file.\nThis allows following the changes in issues over time. It reports issues in\nMarkdown tables separated by issue types as headings, for example:\n\n```md\n# Knip report\n\n## Unused files (1)\n\n- src/unused.ts\n\n## Unlisted dependencies (2)\n\n| Name            | Location          | Severity |\n| :-------------- | :---------------- | :------- |\n| unresolved      | src/index.ts:8:23 | error    |\n| @org/unresolved | src/index.ts:9:23 | error    |\n\n## Unresolved imports (1)\n\n| Name         | Location           | Severity |\n| :----------- | :----------------- | :------- |\n| ./unresolved | src/index.ts:10:12 | error    |\n```\n\n### Disclosure\n\nThis reporter is useful for sharing large reports. Groups of issues are rendered\nin a closed state initially. The reporter renders this:\n\n````text\n$ knip --reporter disclosure\n\n<details>\n<summary>Unused files (2)</summary>\n\n```\nunused.ts\ndangling.js\n```\n\n</details>\n\n<details>\n<summary>Unused dependencies (2)</summary>\n\n```\nmy-package     package.json:17:5\nunused-dep     package.json:20:5\n```\n\n</details>\n````\n\nThe above can be copy-pasted where HTML and Markdown is supported, such as a\nGitHub issue or pull request, and renders like so:\n\n<details>\n  <summary>Unused files (2)</summary>\n\n```\nunused.ts\ndangling.js\n```\n\n</details>\n\n<details>\n  <summary>Unused dependencies (2)</summary>\n\n```\nmy-package     package.json:17:5\nunused-dep     package.json:20:5\n```\n\n</details>\n\n### CodeClimate\n\nThe built-in `codeclimate` reporter generates output in the Code Climate Report\nJSON format. Example usage:\n\n```text\n$ knip --reporter codeclimate\n\n[\n  {\n    \"type\": \"issue\",\n    \"check_name\": \"Unused exports\",\n    \"description\": \"isUnused\",\n    \"categories\": [\"Bug Risk\"],\n    \"location\": {\n      \"path\": \"path/to/file.ts\",\n      \"positions\": {\n        \"begin\": {\n          \"line\": 6,\n          \"column\": 1\n        }\n      }\n    }\n    \"severity\": \"major\",\n    \"fingerprint\": \"e9789995c1fe9f7d75eed6a0c0f89e84\",\n  }\n]\n```\n\n## Custom Reporters\n\nWhen the provided built-in reporters are not sufficient, a custom local reporter\ncan be implemented or an external reporter can be used. Multiple reporters can\nbe used at once by repeating the `--reporter` argument.\n\nThe results are passed to the function from its default export and can be used\nto write issues to `stdout`, a JSON or CSV file, or sent to a service. It\nsupports a local JavaScript or TypeScript file or an external dependency.\n\n### Local\n\nThe default export of the reporter should be a function with this interface:\n\n```ts\ntype Reporter = async (options: ReporterOptions): void;\n\ntype ReporterOptions = {\n  report: Report;\n  issues: Issues;\n  counters: Counters;\n  configurationHints: ConfigurationHints;\n  isDisableConfigHints: boolean;\n  isTreatConfigHintsAsErrors: boolean;\n  cwd: string;\n  isProduction: boolean;\n  isShowProgress: boolean;\n  options: string;\n};\n```\n\nThe data can then be used to write issues to `stdout`, a JSON or CSV file, or\nsent to a service.\n\nHere's a most minimal reporter example:\n\n```ts title=\"./my-reporter.ts\"\nimport type { Reporter } from 'knip';\n\nconst reporter: Reporter = function (options) {\n  console.log(options.issues);\n  console.log(options.counters);\n};\n\nexport default reporter;\n```\n\nExample usage:\n\n```sh\nknip --reporter ./my-reporter.ts\n```\n\n### External\n\nPass `--reporter [pkg-name]` to use an external reporter. The default exported\nfunction of the `main` script (default: `index.js`) will be invoked with the\n`ReporterOptions`, just like a local reporter.\n\n## Preprocessors\n\nA preprocessor is a function that runs after the analysis is finished. It\nreceives the results from the analysis and should return data in the same\nshape/structure (unless you pass it to only your own reporter).\n\nThe data goes through the preprocessors before the final data is passed to the\nreporters. There are no built-in preprocessors. Just like reporters, use e.g.\n`--preprocessor ./my-preprocessor` from the command line (can be repeated).\n\nThe default export of the preprocessor should be a function with this interface:\n\n```ts\ntype Preprocessor = async (options: ReporterOptions) => ReporterOptions;\n```\n\nLike reporters, you can use local JavaScript or TypeScript files and external\nnpm packages as preprocessors.\n\nExample preprocessor:\n\n```ts title=\"./preprocess.ts\"\nimport type { Preprocessor } from 'knip';\n\nconst preprocess: Preprocessor = function (options) {\n  // modify options.issues and options.counters\n  return options;\n};\n\nexport default preprocess;\n```\n\nExample usage:\n\n```sh\nknip --preprocessor ./preprocess.ts\n```\n\n[1]: #disclosure\n[2]: #github-actions\n[3]: #json\n[4]: #markdown\n[5]: #codeclimate\n[6]: ../reference/issue-types.md\n"
  },
  {
    "path": "packages/docs/src/content/docs/features/rules-and-filters.md",
    "content": "---\ntitle: Rules & Filters\nsidebar:\n  order: 5\n---\n\nUse rules or filters to customize Knip's output. This has various use cases, a\nfew examples:\n\n- Temporarily focus on a specific issue type.\n- You don't want to see unused `type`, `interface` and `enum` exports reported.\n- Specific issue types should be printed, but not counted against the total\n  error count.\n\nIf you're looking to handle one-off exceptions, also see [JSDoc tags][1].\n\n## Filters\n\nYou can `--include` or `--exclude` any of the reported issue types to slice &\ndice the report to your needs. Alternatively, they can be added to the\nconfiguration (e.g. `\"exclude\": [\"dependencies\"]`).\n\nUse `--include` to report only specific issue types. The following example\ncommands do the same:\n\n```sh\nknip --include files --include dependencies\nknip --include files,dependencies\n```\n\nOr the other way around, use `--exclude` to ignore the types you're not\ninterested in:\n\n```sh\nknip --include files --exclude enumMembers,duplicates\n```\n\nAlso see the [list of issue types][2].\n\n### Shorthands\n\nKnip has shortcuts to include only specific issue types.\n\n1. The `--dependencies` flag includes:\n   - `dependencies` (and `devDependencies` + `optionalPeerDependencies`)\n   - `unlisted`\n   - `binaries`\n   - `unresolved`\n   - `catalog`\n\n2. The `--exports` flag includes:\n   - `exports`\n   - `types`\n   - `enumMembers`\n   - `namespaceMembers`\n   - `duplicates`\n\n3. The `--files` flag is a shortcut for `--include files`\n\n## Rules\n\nUse `rules` in the configuration to customize the issue types that count towards\nthe total error count, or to exclude them altogether.\n\n| Value     | Default | Printed | Counted | Description                       |\n| :-------- | :-----: | :-----: | :-----: | :-------------------------------- |\n| `\"error\"` |    ✓    |    ✓    |    ✓    | Similar to the `--include` filter |\n| `\"warn\"`  |    -    |    ✓    |    -    | Printed in faded/gray color       |\n| `\"off\"`   |    -    |    -    |    -    | Similar to the `--exclude` filter |\n\nExample:\n\n```json title=\"knip.json\"\n{\n  \"rules\": {\n    \"files\": \"warn\",\n    \"duplicates\": \"off\"\n  }\n}\n```\n\nAlso see the [issue types overview][2].\n\nNOTE: If the `dependencies` issue type is included, the `devDependencies` and\n`optionalPeerDependencies` types can still be set to `\"warn\"` separately.\n\nThe rules are modeled after the ESLint `rules` configuration, and could be\nextended in the future.\n\n## Rules or filters?\n\nFilters are meant to be used as command-line flags, rules allow for more\nfine-grained configuration.\n\n- Rules are more fine-grained since they also have \"warn\".\n- Rules could be extended in the future.\n- Filters can be set in configuration and from CLI (rules only in\n  configuration).\n- Filters have shorthands (rules don't have this).\n\n[1]: ../reference/jsdoc-tsdoc-tags.md\n[2]: ../reference/issue-types.md\n"
  },
  {
    "path": "packages/docs/src/content/docs/features/script-parser.md",
    "content": "---\ntitle: Script Parser\n---\n\nKnip parses shell commands and scripts to find additional dependencies, entry\nfiles and configuration files in various places:\n\n- In [`package.json`][1]\n- In [CLI arguments][2]\n- In [scripts][3]\n- In [source code][4]\n\nShell scripts can be read and statically analyzed, but they're not executed.\n\n## package.json\n\nThe `main`, `bin`, `exports` and `scripts` fields may contain entry files. Let's\ntake a look at this example:\n\n```json title=\"package.json\"\n{\n  \"name\": \"my-package\",\n  \"main\": \"index.js\",\n  \"exports\": {\n    \"./lib\": {\n      \"import\": \"./dist/index.mjs\",\n      \"require\": \"./dist/index.cjs\"\n    }\n  },\n  \"bin\": {\n    \"program\": \"bin/cli.js\"\n  },\n  \"scripts\": {\n    \"build\": \"rollup src/entry.ts\",\n    \"start\": \"node --loader tsx server.ts\"\n  }\n}\n```\n\nFrom this example, Knip automatically adds the following files as entry files:\n\n- `index.js`\n- `./dist/index.mjs`\n- `./dist/index.cjs`\n- `bin/cli.js`\n- `src/entry.ts`\n- `server.ts`\n\n### Excluded files\n\nKnip would not add the `exports` if the `dist` folder is matching a pattern in a\nrelevant `.gitignore` file or `ignore` option.\n\nKnip does not add scripts without a standard extension. For instance, the\n`bin/tool` file might be a valid executable for Node.js, but wouldn't be added\nor parsed by Knip.\n\n### CLI Arguments\n\nWhen parsing the `scripts` of `package.json` and other files, Knip detects\nvarious types of inputs. Some examples:\n\n- The first positional argument is usually an entry file\n- Configuration files are often in the `-c` or `--config` argument\n- The `--require`, `--loader` or `--import` arguments are often dependencies\n\n```json\n{\n  \"name\": \"my-lib\",\n  \"scripts\": {\n    \"start\": \"node --import tsx/esm run.ts\",\n    \"bundle\": \"tsup -c tsup.lib.config.ts\",\n    \"type-check\": \"tsc -p tsconfig.app.json\"\n  }\n}\n```\n\nThe `\"start\"` script will have `tsx` marked as a referenced dependency, and adds\n`run.ts` as an entry file.\n\nAdditionally, the following files are detected as configuration files:\n\n- `tsup.lib.config.ts` - to be handled by the tsup plugin\n- `tsconfig.app.json` - to be handled by the TypeScript plugin\n\nSuch executables and their arguments are all defined in plugins separately for\nfine-grained results.\n\n## Scripts\n\nPlugins may also use the script parser to extract entry files and dependencies\nfrom commands. A few examples:\n\n- GitHub Actions: workflow files may contain `run` commands (e.g.\n  `.github/workflows/ci.yml`)\n- Husky & Lefthook: Git hooks such as `.git/hooks/pre-push` contain scripts;\n  also `lefthook.yml` has `run` commands\n- Lint Staged: configuration values are all commands\n- Nx: task executors and `nx:run-commands` executors in `project.json` contains\n  scripts\n- Release It: `hooks` contain commands\n\nPlugins can also return configuration files. Some examples:\n\n- The Angular plugin detects `options.tsConfig` as a TypeScript config file\n- The GitHub Actions plugin parses `run` commands which may contain\n  configuration file paths\n\n## Source Code\n\nWhen Knip is walking the abstract syntax trees (ASTs) of JavaScript and\nTypeScript source code files, it looks for imports and exports. But there's a\nfew more (rather obscure) things that Knip detects in the process. Below are\nexamples of additional scripts Knip parses to find entry files and dependencies.\n\n### bun\n\nIf the `bun` dependency is imported in source code, Knip considers the contents\nof `$` template tags to be scripts:\n\n```ts\nimport { $ } from 'bun';\nawait $`bun boxen I ❤ unicorns`;\nawait $`boxen I ❤ unicorns`;\n```\n\nParsing the script results in the `boxen` binary (the `boxen-cli` dependency) as\nreferenced (twice).\n\n### execa\n\nIf the `execa` dependency is imported in source code, Knip considers the\ncontents of `$` template tags to be scripts:\n\n```ts\nawait $({ stdio: 'inherit' })`c8 node hydrate.js`;\n```\n\nParsing the script results in `hydrate.js` added as an entry file and the `c8`\nbinary/dependency as referenced.\n\n### zx\n\nIf the `zx` dependency is imported in source code, Knip considers the contents\nof `$` template tags to be scripts:\n\n```ts\nawait $`node scripts/parse.js`;\n```\n\nThis will add `scripts/parse.js` as an entry file.\n\n[1]: #packagejson\n[2]: #cli-arguments\n[3]: #scripts\n[4]: #source-code\n"
  },
  {
    "path": "packages/docs/src/content/docs/features/source-mapping.md",
    "content": "---\ntitle: Source Mapping\nsidebar:\n  order: 4\n---\n\nKnip is mostly interested in source code. Analyzing build artifacts hurts\nperformance and often leads to false positives, as they potentially contain\nbundled code and unresolvable imports.\n\nThat's why Knip tries to map such build artifacts back to their original source\nfiles and analyze those instead. This is done based on `tsconfig.json` settings.\n\n## Example 1: package.json\n\nLet's look at an example case with `package.json` and `tsconfig.json` files, and\nsee how \"dist\" files are mapped to \"src\" files.\n\n```jsonc title=\"package.json\"\n{\n  \"name\": \"my-workspace\",\n  \"main\": \"index.js\",\n  \"exports\": {\n    \".\": \"./src/entry.js\",\n    \"./feat\": \"./lib/feat.js\",\n    \"./public\": \"./dist/app.js\",\n    \"./public/*\": \"./dist/*.js\",\n    \"./public/*.js\": \"./dist/*.js\",\n    \"./dist/internal/*\": null,\n  },\n}\n```\n\nWith this TypeScript configuration:\n\n```json title=\"tsconfig.json\"\n{\n  \"compilerOptions\": {\n    \"baseUrl\": \"src\",\n    \"outDir\": \"dist\"\n  }\n}\n```\n\n- `./src/entry.js` is not in an `outDir` folder, so it's added as an entry file\n- `./lib/feat.js` is not in an `outDir` folder, so it's added as an entry file\n- `./dist/app.js` is in a `dist` folder and mapped to `./src/app.{js,ts}` (¹)\n- `./dist/*.js` is in a `dist` folder and mapped to `./src/**/*.{js,ts}` (¹)\n- `./dist/internal/*` is translated to `./dist/internal/**` and files in this\n  directory and deeper are ignored when globbing entry files\n\n(¹) full extensions list is actually: `js`, `mjs`, `cjs`, `jsx`, `ts`, `tsx`,\n`mts`, `cts`\n\nIn `--debug` mode, look for \"Source mapping\" to see this in action.\n\n:::tip\n\nUsing `./dist/*.js` means that all files matching `./src/**/*.{js,ts}` are added\nas entry files. By default, unused exports of entry files are not reported. Use\n[includeEntryExports][1] to include them.\n\n:::\n\n## Example 2: monorepo\n\nLet's say we have this module in a monorepo that imports `helper` from another\nworkspace in the same monorepo:\n\n```ts title=\"index.js\"\nimport { helper } from '@org/shared';\n```\n\nThe target workspace `@org/shared` has this `package.json`:\n\n```json title=\"package.json\"\n{\n  \"name\": \"@org/shared\",\n  \"main\": \"dist/index.js\"\n}\n```\n\nThe module resolver will resolve `@org/shared` to `dist/index.js`. That file is\nusually compiled and git-ignored, while Knip wants the source file instead.\n\n:::tip\n\nYou may need to compile build artifacts to `outDir` first before Knip can\nsuccessfully apply source mapping for internal references in a monorepo.\n\n:::\n\nIf the target workspace has a `tsconfig.json` file with an `outDir` option, Knip\nwill try to map the \"dist\" file to the \"src\" file. Then if `src/index.ts`\nexists, Knip will use that file instead of `dist/index.js`.\n\nCurrently this only works based on `tsconfig.json`, in the future more source\nmappings may be added.\n\n[1]: ../reference/configuration.md#includeentryexports\n"
  },
  {
    "path": "packages/docs/src/content/docs/guides/configuring-project-files.md",
    "content": "---\ntitle: Configuring Project Files\nsidebar:\n  order: 1\n---\n\nThe `entry` and `project` file patterns are the first and most important\noptions. Getting those right is essential to help you delete more code and make\nKnip faster:\n\n- Start with defaults. Only add targeted `entry` overrides when needed.\n- Use `project` patterns (with negations) to define the scope for Knip.\n- Use production mode to exclude tests and other non-production files.\n- Use `ignore` only to suppress issues in specific files; it does not exclude\n  files from analysis.\n\nLet's dive in and expand on all of these.\n\n## Entry files\n\nAvoid adding too many files as `entry` files:\n\n1. Knip does not report [unused exports][1] in entry files by default.\n2. Proper `entry` and `project` patterns allow Knip to find unused files and\n   exports.\n\n## Unused files\n\nFiles are reported as unused if they are in the set of `project` files, but are\nnot resolved from the `entry` files:\n\n```\nunused files = project files - (entry files + resolved files)\n```\n\nSee [entry files][2] to see where Knip looks for entry files. Fine-tune `entry`\nand adjust `project` to fit your codebase.\n\n:::tip\n\nUse negated `project` patterns to precisely include/exclude files for unused\nfiles detection.\n\nUse `ignore*` to suppress specific issues in matching files; it does not exclude\nfiles from analysis.\n\n:::\n\n## Negated patterns\n\nWhen there are too many files in the analysis, start here.\n\nFor instance, routes are entry files except those prefixed with an underscore:\n\n```json\n{\n  \"entry\": [\"src/routes/*.ts\", \"!src/routes/_*.ts\"]\n}\n```\n\nSome files are not part of your source and are reported as unused (false\npositives)? Use negated `project` patterns:\n\n```json\n{\n  \"entry\": [\"src/index.ts\"],\n  \"project\": [\"src/**/*.ts\", \"!src/exclude/**\"]\n}\n```\n\n❌   Don't use `ignore` for generated artifacts:\n\n```json title=\"knip.json\"\n{\n  \"entry\": [\"src/index.ts\", \"scripts/*.ts\"],\n  \"ignore\": [\"build/**\", \"dist/**\", \"src/generated.ts\"]\n}\n```\n\n✅   Do define your project boundaries:\n\n```json title=\"knip.json\"\n{\n  \"entry\": [\"src/index.ts\", \"scripts/*.ts\"],\n  \"project\": [\"src/**\", \"scripts/**\"],\n  \"ignore\": [\"src/generated.ts\"]\n}\n```\n\nWhy this is better:\n\n- `project` defines what belongs to the codebase, so build outputs are excluded\n  from analysis and unused file detection\n- `ignore` is for the few files that should be analyzed but contain exceptions\n- Improves performance by analyzing fewer files\n\n## Ignore issues in specific files\n\nUse `ignore` when a specific analyzed file is not handled properly by Knip or\nintentionally contains unused exports (e.g. generated files exporting\n\"everything\"):\n\n```json\n{\n  \"entry\": [\"src/index.ts\"],\n  \"project\": [\"src/**/*.ts\"],\n  \"ignore\": [\"src/generated.ts\"]\n}\n```\n\nAlso see [ignoreExportsUsedInFile][3] for a more targeted approach.\n\n## Production Mode\n\nDefault mode includes tests and other non-production files in the analysis. To\nfocus on production code, use [production mode][4].\n\nDon't try to exclude tests via `ignore` or negated `project` patterns. That's\ninefficient and ineffective due to entries added by plugins. Use production mode\ninstead.\n\n❌   Don't do this:\n\n```json\n{\n  \"ignore\": [\"**/*.test.js\"]\n}\n```\n\nWhy not: `ignore` hides issues from the report; it does not exclude files from\nanalysis.\n\n❌   Also don't do this:\n\n```json\n{\n  \"entry\": [\"index.ts\", \"!**/*.test.js\"]\n}\n```\n\nWhy not: plugins add test files as `entry` files; you can't and shouldn't\noverride that globally.\n\n❌   Or this:\n\n```json\n{\n  \"project\": [\"**/*.ts\", \"!**/*.spec.ts\"]\n}\n```\n\nWhy not: `project` is for unused file detection; negating test files is\nineffective because they are `entry` files.\n\n✅   Do this instead:\n\n```shell\nknip --production\n```\n\nTo fine-tune the resulting production file set, for instance to exclude test\nhelper files that still show as unused, use the exclamation mark suffix on\nproduction patterns:\n\n```json\n{\n  \"entry\": [\"src/index.ts!\"],\n  \"project\": [\"src/**/*.ts!\", \"!src/test-helpers/**!\"]\n}\n```\n\nRemember to keep adding the exclamation mark `suffix!` for production file\npatterns.\n\n:::tip\n\nUse the exclamation mark (`!`) on both ends (`!`) to exclude files in production\nmode.\n\n:::\n\n## Defaults & Plugins\n\nTo reiterate, the default `entry` and `project` files for each workspace:\n\n```json\n{\n  \"entry\": [\n    \"{index,cli,main}.{js,cjs,mjs,jsx,ts,cts,mts,tsx}\",\n    \"src/{index,cli,main}.{js,cjs,mjs,jsx,ts,cts,mts,tsx}\"\n  ],\n  \"project\": [\"**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}!\"]\n}\n```\n\nNext to this, there are other places where [Knip looks for entry files][2].\n\nAdditionally, [plugins have plenty of entry files configured][5] that are\nautomatically added as well.\n\n[1]: ../typescript/unused-exports.md\n[2]: ../explanations/entry-files.md\n[3]: ../reference/configuration.md#ignoreexportsusedinfile\n[4]: ../features/production-mode.md\n[5]: ../explanations/plugins.md#entry-files\n"
  },
  {
    "path": "packages/docs/src/content/docs/guides/contributing.md",
    "content": "---\ntitle: Contributing to Knip\n---\n\nHere are some ways to contribute to Knip:\n\n- Spread the word!\n- [Star the project][1]\n- [File an issue with a reproduction][2]\n- [Pull requests are welcome][3]\n\n[Writing a plugin][4] is a great and fun way to get started.\n\nThe [CONTRIBUTING.md][3] and [DEVELOPMENT.md][5] guides should get you up and\nrunning quickly.\n\nThe main goal of Knip is to keep projects clean & tidy. Everything that\ncontributes to that goal is welcome!\n\n[1]: https://github.com/webpro-nl/knip\n[2]: ./issue-reproduction.md\n[3]: https://github.com/webpro-nl/knip/blob/main/.github/CONTRIBUTING.md\n[4]: ../writing-a-plugin/index.md\n[5]: https://github.com/webpro-nl/knip/blob/main/.github/DEVELOPMENT.md\n"
  },
  {
    "path": "packages/docs/src/content/docs/guides/handling-issues.mdx",
    "content": "---\ntitle: Handling Issues\nsidebar:\n  order: 3\n---\n\nimport { Tabs, TabItem } from '@astrojs/starlight/components';\n\nIssues reported by Knip may contain false positives, but also tons of useful\ninformation. Getting the most out of Knip may require some configuration.\n\nGo over the issue types one by one. For instance, reducing the number of unused\nfiles will also reduce the number of unused dependencies.\n\n1. [Unused files][1]\n2. [Unused dependencies][2]\n3. [Unresolved imports][3]\n4. [Unused exports][4]\n\n:::tip\n\nTry the [Knip Editor Extension][5]! Let your coding agent use the built-in\nMCP Server and create a custom `knip.json` for you.\n\n:::\n\n## Unused files\n\nGetting the list of unused files right trickles down into the other issue types\nas well, so we start here. Files are reported as unused if they are in the set\nof `project` files, but not in the set of files resolved from the `entry` files:\n\n```\nunused files = project files - (entry files + resolved files)\n```\n\n:::tip\n\nAddressing reported [Configuration Hints][6] first might help significantly,\nespecially when handling unused files.\n\n:::\n\nCommon causes for unused files include:\n\n- [Missing generated files][7]\n- [Dynamic import specifiers][8]\n- [Unsupported arguments in scripts][9]\n- [Unsupported file formats][10]\n- [Missing plugin][11]\n- [Incomplete plugin][12]\n- [TypeScript path aliases in monorepos][13]\n- [Relative paths across workspaces][14]\n- [Integrated monorepos][15]\n- [Auto-mocking or auto-imports][16]\n\nIn most cases you can add `entry` patterns manually.\n\nUse `--files` to [filter the report][17] and focus only on unused files:\n\n```sh\nknip --files\n```\n\nThis works with other issue types as well. For instance, use `--dependencies` to\nfocus only on dependencies and exclude issues related to unused files and\nexports.\n\n:::caution\n\nDon't add unused files to the `ignore` option before reading [configuring\nproject files][18]. Learn why and when to use `entry`, `project`, production\nmode and `ignore*` patterns for better results and performance.\n\n:::\n\n### Missing generated files\n\nFor certain features, Knip needs to run after relevant files are generated. For\ninstance, [source mapping][19] in a monorepo may require files to be built into\n`dist` folders first. And generated files in the `src` directory may import\nother files. For instance, the `src/routeTree.gen.ts` file generated by\n`@tanstack/router` must exist so Knip can find the imported route files.\n\n**Solution**: compile and/or generate the relevant files first so Knip can\nresolve and find the source files.\n\nIf Knip still reports false positives, you may need to strategically add an\n`entry` file manually.\n\n### Dynamic import specifiers\n\nDynamic import specifiers aren't resolved, such as:\n\n```ts\nconst entry = await import(path.join(baseDir, 'entry.ts'));\n```\n\n**Solution**: add `entry.ts` to `entry` patterns.\n\n### Unsupported arguments in scripts\n\nSome tooling command arguments aren't recognized:\n\n```json\n{\n  \"name\": \"my-lib\",\n  \"version\": \"1.0.0\",\n  \"scripts\": {\n    \"build\": \"unknown-build-cli --entry production.ts\"\n  }\n}\n```\n\n**Solution**: add `production.ts` to `entry` patterns.\n\nThis works the same for any script, also those in GitHub Actions workflows or\nGit hooks. See [script parser][20] for more details about Knip's script parser.\n\n### Unsupported file formats\n\nEntry files referenced in HTML files (e.g. `<script src=\"production.js\">`).\n\n```html\n<html>\n  <body>\n    <script type=\"module\" src=\"production.js\"></script>\n  </body>\n</html>\n```\n\n**Solution**: add `production.js` to `entry` patterns. Or add an `.html`\ncompiler to extract and resolve the value of `<script src>` elements.\n\nKnip has support for some popular framework formats through [compilers][21], and\nadditional compilers can be added for any file type. The recommended solution is\nusually to add the file as shown in each example as an `entry` file.\n\n### Missing plugin\n\nYou might be using a tool or framework for which Knip doesn't have a plugin\n(yet). Configuration and entry files (and related dependencies) may be reported\nas unused because there is no plugin yet that would include those files. Two\nexamples:\n\n- Configuration file `tool.config.js` contains a reference to the package\n  `\"@tool/plugin\"` → both the file and the dependency are reported as unused.\n- A framework automatically imports all files matching `src/models/*.ts` → those\n  files are reported as unused.\n\n**Solution**: [create a new plugin][22] for the tool or framework that's not [in\nthe list][23] yet. Or work around it and add `entry` patterns and maybe ignore a\ndependency or two (using [`ignoreDependencies`][24]).\n\n### Incomplete plugin\n\nFiles may be reported as unused if existing plugins do not include that entry\nfile pattern yet. See the [plugins section of entry files][25] for more details.\n\n**Solution**: [override plugin configuration][26] to customize default patterns\nfor existing plugins. Or even better: send a pull request to improve the plugin.\n\n### TypeScript path aliases in monorepos\n\nWhen using TypeScript path aliases in import specifiers, aliases referencing\nother workspaces are not special-cased. This may cause false positives. For\nKnip, it's better to be explicit and list other workspaces as dependencies in\n`package.json`.\n\n**Solution**: move such \"workspace aliases\" from `compilerOptions`...\n\n```json title=\"tsconfig.json\"\n{\n  \"compilerOptions\": {\n    \"paths\": {\n      \"@org/common/*\": [\"packages/common/*\"]\n    }\n  }\n}\n```\n\n...to `dependencies` or `devDependencies` in `package.json`:\n\n```json title=\"package.json\"\n{\n  \"name\": \"@org/lib\",\n  \"dependencies\": {\n    \"@org/common\": \"workspace:*\"\n  }\n}\n```\n\nAn additional benefit is that Knip will report unused and unlisted dependencies\nfrom now on.\n\nAlso see [FAQ: Why can't I use path aliases to reference other workspaces?][27]\n\n### Relative paths across workspaces\n\nRelative paths to import from other workspaces are not special-cased. For Knip,\nit's better to be explicit and list other workspaces as dependencies in\n`package.json` to be used in imports.\n\n**Solution**: migrate from relative paths...\n\n```ts\nimport { something } from '../../common/file.ts';\n```\n\n...to dependency-based imports...\n\n```ts\nimport { something } from '@org/common';\n```\n\nAn additional benefit is that Knip will report unused and unlisted dependencies\nfrom now on.\n\nAlso see [TypeScript path aliases in monorepos][13].\n\n### Integrated monorepos\n\nMultiple instances of configuration files like `.eslintrc` and\n`jest.config.json` across the repository may be reported as unused when working\nin a (mono)repo with a single `package.json`.\n\n**Solution**: see [integrated monorepos][28] for more details and how to\nconfigure plugins to target those configuration files.\n\n### Auto-mocking or auto-imports\n\nSome frameworks have features like \"auto-mocking\" or \"auto-imports\" enabled,\nsuch as Jest and Nuxt.\n\n**Solution**: include such entry files by extending the `entry` file patterns.\nThis is recommended in most cases:\n\n```json\n{\n  \"entry\": [\"src/index.ts\", \"src/models/*.ts\"]\n}\n```\n\nAlternatively, exceptions and outliers can be excluded from the analysis using\nnegated `project` patterns:\n\n```json\n{\n  \"project\": [\"src/**/*.ts\", \"!**/__mocks__/**\"]\n}\n```\n\n## Unused dependencies\n\nDependencies imported in unused files are reported as unused dependencies.\nThat's why it's strongly recommended to try and remedy [unused files][1] first.\nBetter `entry` and `project` file coverage will solve many cases of reported\nunused dependencies.\n\nThe most common causes for unused dependencies include:\n\n- [Missing or incomplete plugins][29]\n- [Unrecognized references][30]\n- [Type Definition Packages][31]\n- [Dependencies named after Node.js builtins][32]\n\nUse `--dependencies` to [filter the report][17] and focus only on issues related\nto dependencies:\n\n```sh\nknip --dependencies\n```\n\n:::caution[Monorepo]\n\nIn a monorepo, a dependency is unused in the root workspace's `package.json` if\nit's also listed in a descendant workspace, and referenced only in the\ndescendant workspace.\n\n:::\n\n### Missing or incomplete plugin\n\nIf a plugin exists and the dependency is referenced in the configuration file,\nbut its custom dependency finder does not detect it, then that's a false\npositive. Please open a pull request or issue to fix it.\n\n**Solution**: adding the configuration file as an `entry` file pattern may be a\ntemporary stopgap that fixes your situation, but it's better to create a new\nplugin or fix an existing one.\n\n### Unrecognized reference\n\nSometimes a reference to a dependency is unrecognizable or unreachable to Knip,\nso it's a false positive and incorrectly reported as unused.\n\n**Solution**: add a new plugin or improve an existing one. If you don't feel\nlike a plugin could solve it, a last resort is to use [ignoreDependencies][24].\n\nIf a binary (or \"executable\") is referenced you'll want to use `ignoreBinaries`\ninstead. See [unlisted binaries][33].\n\n### Dependencies named after Node.js builtins\n\nSome packages have the same name as a Node.js builtin (for instance `buffer` or\n`process`).\n\n**Solution**: if Knip reports such a dependency as unused, add it to\n[ignoreDependencies][24].\n\n### Conditional or dynamic dependencies\n\nDependencies added conditionally in configuration files may not be detected by\nKnip. This happens because Knip loads and executes config files, and conditional\nlogic may evaluate differently during analysis.\n\nFor example, this Playwright configuration conditionally adds a reporter:\n\n```ts title=\"playwright.config.ts\"\nimport { defineConfig } from '@playwright/test';\n\nconst reporters: any[] = [['list']];\nif (process.env.REPORT_PORTAL_ENABLED) {\n  reporters.push(['@reportportal/agent-js-playwright', config]);\n}\n\nexport default defineConfig({\n  reporter: reporters,\n});\n```\n\nIf `process.env.REPORT_PORTAL_ENABLED` evaluates to `false` when Knip runs, the\n`@reportportal/agent-js-playwright` dependency won't be detected and may be\nreported as unused.\n\nThis limitation exists because Knip executes configuration files to parse their\nexported value. While Knip can parse configuration files statically using AST\n(Abstract Syntax Tree) analysis, this approach becomes complex very quickly and\nmost of the time it is easier to use the [`ignoreDependencies`][24]\nconfiguration option for conditionals.\n\n```json title=\"knip.json\"\n{\n  \"ignoreDependencies\": [\"@reportportal/agent-js-playwright\"]\n}\n```\n\nThis pattern can be applied to any plugin that loads configuration files.\n\n### Type Definition Packages\n\n#### Bundled types\n\nMany packages come with their type definitions bundled. This means the\n`package.json#types` field in the package points to an internal/local type\ndefinition file. In this case, the separate types package is obsolete.\n\nKnip reporting this is also useful for future regressions: if a package had a\nDefinitelyTyped or similar package for its types before and later on starts\nshipping those types bundled with the source code, Knip will report the obsolete\ntypes dependency as unused.\n\nExamples include ESLint, Webpack and React Router, rendering the\n`@types/eslint`, `@types/webpack` and `@types/react-router` dependencies\nobsolete respectively.\n\n**Solution**: remove the types dependency (often `@types/...`)\n\n#### Production types\n\nKnip is strict in the divide between `dependencies` and `devDependencies`. Some\npackages are published with one or more type packages listed in `dependencies`.\nIn strict production mode, even when re-exported and part of the package's\npublic API, Knip does not try to figure out what exactly are \"production types\"\nand expects those in `devDependencies`.\n\n**Solution**: list exceptions in [ignoreDependencies][24].\n\n### Unlisted dependencies\n\nThis means that a dependency is referenced directly in source code or\nconfiguration, but not listed in `package.json`.\n\nAn unlisted dependency is usually a transitive dependency that's imported or\nreferenced directly. The dependency is installed (since it's a dependency of\nanother dependency) and lives in `node_modules`, but it's not listed explicitly\nin `package.json`.\n\nYou should not rely on transitive dependencies for various reasons, including\ncontrol, security and stability.\n\n**Solution**: install and list the dependency in `dependencies` or\n`devDependencies`.\n\n### Unlisted binaries\n\nBinaries are executable Node.js scripts. Some npm packages, when installed, add\none or more executable files to be used from scripts in `package.json`. Examples\ninclude TypeScript that comes with the `tsc` binary, ESLint comes with `eslint`,\nNext.js with `next`, and so on.\n\nKnip detects such binaries in scripts and checks whether there's a package\ninstalled that includes that binary. It looks up the `bin` field in the\n`package.json` file of installed packages. If it doesn't find it, it will be\nreported as an unlisted binary as there is no package that contains it.\n\nBinaries that are installed on the OS already and thus likely not meant to be\ninstalled from npm are not reported as unlisted (details: [list of ignored\nbinaries in source][34]).\n\n#### Missing binaries\n\nAn unused dependency and an unlisted binary with the same name indicates\n`node_modules` not containing the relevant package. And this might be caused by\neither the way your package manager installs dependencies and binaries, or by\nnot running Knip from the root of the repository.\n\n**Solution**: run Knip from the project root. If needed, [lint workspaces\nindividually][35].\n\nSometimes binaries and how they're reported can be a bit confusing. See this\nexample:\n\n```json\n{\n  \"name\": \"lib\",\n  \"scripts\": {\n    \"commitlint\": \"commitlint --edit\"\n  },\n  \"devDependencies\": {\n    \"@commitlint/cli\": \"*\"\n  }\n}\n```\n\nThis example works fine without anything reported, as the `@commitlint/cli`\npackage includes the `commitlint` binary. However, some script may contain `npx\ncommitlint` and here Knip assumes `commitlint` is the name of the package. This\ntechnically works, as `commitlint` is a transitive dependency of\n`@commitlint/cli`.\n\n**Solution**: use `npx @commitlint/cli`\n\nIn some cases, using `npx` in a script may result in Knip not understanding\nintention without an explicit `--yes` or `--no-install` flag.\n\n**Solution**: use `npx --yes` or `npx --no-install` so Knip will either ignore\nor consider the binary and package(s) referenced, respectively.\n\n## Unresolved imports\n\nKnip may ignore or be unable to resolve an import specifier or dependency\nreferences. The most common causes for unresolved imports:\n\n- [Template strings][36]\n- [Extensionless imports][30]\n- [Unrecognized path aliases][37]\n- [External aliased imports][38]\n\n### Template strings\n\nUsing template strings in dynamic imports might be ignored or not handled\nproperly by Knip, resulting in false positives. Examples of dynamic import\ntemplate strings:\n\n```ts\nimport(`./${value}.ts`);\nimport(`@org/name/dist/${value}.js`);\n```\n\n**Solution**: for internal source files, add the file(s) to the `entry`\npatterns. For external dependencies, add the dependency to the\n`ignoreDependencies` list.\n\n### Extensionless imports\n\nKnip does not support extensionless imports for some non-standard extensions,\nsuch as for `.svg` files. Bundlers like Webpack may support this, but Knip does\nnot. Here's an example:\n\n```ts title=\"App.vue\"\nimport Component from './Component'; // → Should resolve to ./Component.vue\nimport ArrowIcon from '../icons/Arrow'; // → Does NOT resolve to ../icons/Arrow.svg\n```\n\nThe first import is resolved properly, because `.vue` is a known extension if\nthe Vue plugin is enabled. The second import might not be resolved, because\n`.svg` is not a known extension.\n\n**Solution**: include the extension. Hosts supporting ES Modules (e.g. Node.js,\nbrowsers) require a file extension for import specifiers.\n\n### Unrecognized path aliases\n\nKnip considers TS config path aliases and [paths configured in knip.json][39],\nbut not those in e.g. Webpack or Vite configurations.\n\n**Solution**: configure [paths][39] or try relative imports. Otherwise, use\n[`ignoreUnresolved`][40] as a last resort.\n\n### External aliased imports\n\nExternal libraries may use aliased imports that aren't resolved by Knip.\n\nFor instance, [unplugin-icons][41] does this to import icons from icon sets as\ncomponents. Such imports are reported as unused. Use the [`paths` configuration\noption][39] to tell Knip where to find the icon types:\n\n```json title=\"knip.json\"\n{\n  \"paths\": {\n    \"~icons/*\": [\"node_modules/unplugin-icons/types/[framework].d.ts\"]\n  }\n}\n```\n\nWhere `[framework]` is the name of the framework you're using (see [available\ntypes][42]).\n\n**Solution**: configure [paths][39].\n\n## Unused exports\n\nBy default, Knip does not report unused exports of `entry` files.\n\nThe most common causes for unused exports include:\n\n- [Namespace enumerations][43]\n- [External libraries][44]\n\nUse the `--exports` flag to [filter][17] and focus only on issues related to\nexports:\n\n```sh\nknip --exports\n```\n\nUse [includeEntryExports][45] to report unused exports of entry files as well.\nThis can be set per workspace.\n\n### Namespace enumerations\n\nFor exports on an imported namespace, Knip considers all exports referenced if\nthat namespace is used in certain patterns like enumeration. Individual exports\nare then **not** reported.\n\n**Solution**: if all exports on imported namespaces should be considered\nindividually, include the `nsExports` issue type to disable the heuristic.\n\nSee [namespace imports][46] to see all related patterns.\n\n### External libraries\n\nAre the exports consumed or imported by an external library, resulting in a\nnon-standard consumption of your exports? Here's an example:\n\n<Tabs>\n  <TabItem label=\"index.js\">\n    ```ts\n    import loadable from '@loadable/component';\n\n    export const DynamicApple = dynamic(() =>\n      import('./components.js').then(mod => mod.Apple)\n    );\n\n    export const LoadableOrange = loadable(() => import('./components.js'), {\n      resolveComponent: components => components.Orange,\n    });\n    ```\n  </TabItem>\n\n  <TabItem label=\"components.js\">\n    ```ts\n    export const Apple = () => 'Apple';\n    export const Orange = () => 'Orange';\n    ```\n  </TabItem>\n</Tabs>\n\nKnip understands `Apple` is used, since it's standard usage. But `Orange` is\nreferenced through a function of an external library. Knip includes external\ntype definitions to trace such usage, but in some cases it may not be able to\nfollow the reference through the library's API.\n\n### Exclude exports from the report\n\nTo exclude false positives from the report, there are a few options:\n\n- [Ignore exports used in file][47] for exports used internally.\n- Individual exports can be [tagged using JSDoc syntax][48].\n- Have the export in an entry file:\n\n  - Add the file to the `entry` file patterns array in the configuration.\n  - Move the export(s) to an entry file.\n  - Add the file to the `exports` field of `package.json`\n- Re-export the unused export(s) from an entry file.\n\n### Missing unused exports?\n\nDid you expect certain exports in the report, but are they missing? They might\nbe exported from an entry file. In that case, use [--include-entry-exports][45]\nto make Knip also report unused exports in entry files.\n\nThe exports of non-standard extensions like `.astro`, `.mdx`, `.vue` or\n`.svelte` are not available by default. See [compilers][21] for more details on\nhow to include them.\n\n### Enum members\n\nUnused enums and unused members of exported enums are reported by default.\nReporting such members can be disabled:\n\n```sh\nknip --exclude enumMembers\n```\n\nIndividual enum members can be [tagged using JSDoc syntax][48].\n\nEnums exported from entry files are ignored, and so are their members. Use\n[--include-entry-exports][45] to make Knip also report members of unused exports\nin entry files.\n\n### Namespace members\n\nUnused members of exported TypeScript namespaces are reported by default.\nReporting such members can be disabled:\n\n```sh\nknip --exclude namespaceMembers\n```\n\nIndividual namespace members can be [tagged using JSDoc syntax][48].\n\nNamespaces exported from entry files are ignored, and so are their members. Use\n[--include-entry-exports][45] to make Knip also report members of unused exports\nin entry files.\n\n## Feedback or false positives?\n\nIf you believe Knip incorrectly reports something as unused (i.e. there's a\nfalse positive), feel free to create a [minimal reproduction][49] and open an\nissue on GitHub. It'll make Knip better for everyone!\n\n[1]: #unused-files\n[2]: #unused-dependencies\n[3]: #unresolved-imports\n[4]: #unused-exports\n[5]: ../reference/integrations.md\n[6]: ../reference/configuration-hints.md\n[7]: #missing-generated-files\n[8]: #dynamic-import-specifiers\n[9]: #unsupported-arguments-in-scripts\n[10]: #unsupported-file-formats\n[11]: #missing-plugin\n[12]: #incomplete-plugin\n[13]: #typescript-path-aliases-in-monorepos\n[14]: #relative-paths-across-workspaces\n[15]: #integrated-monorepos\n[16]: #auto-mocking-or-auto-imports\n[17]: ../features/rules-and-filters.md#filters\n[18]: ./configuring-project-files.md\n[19]: ../features/source-mapping.md\n[20]: ../features/script-parser.md\n[21]: ../features/compilers.md\n[22]: ../writing-a-plugin/index.md\n[23]: ../reference/plugins.md\n[24]: ../reference/configuration.md#ignoredependencies\n[25]: ../explanations/plugins.md#entry-files\n[26]: ../explanations/entry-files.md#plugins\n[27]: ../reference/faq.md#why-cant-i-use-path-aliases-to-reference-other-workspaces\n[28]: ../features/integrated-monorepos.md\n[29]: #missing-or-incomplete-plugin\n[30]: #unrecognized-reference\n[31]: #type-definition-packages\n[32]: #dependencies-named-after-nodejs-builtins\n[33]: #unlisted-binaries\n[34]: https://github.com/webpro-nl/knip/blob/e031018e676c47d84873cc2403251c0111ef318d/packages/knip/src/constants.ts#L39-L148\n[35]: ../features/monorepos-and-workspaces.md#filter-workspaces\n[36]: #template-strings\n[37]: #unrecognized-path-aliases\n[38]: #external-aliased-imports\n[39]: ../reference/configuration.md#paths\n[40]: ../reference/configuration.md#ignoreunresolved\n[41]: https://www.npmjs.com/package/unplugin-icons\n[42]: https://github.com/unplugin/unplugin-icons/tree/main/types\n[43]: #namespace-enumerations\n[44]: #external-libraries\n[45]: ../reference/configuration.md#includeentryexports\n[46]: ../guides/namespace-imports.md\n[47]: ../reference/configuration.md#ignoreexportsusedinfile\n[48]: ../reference/jsdoc-tsdoc-tags.md\n[49]: ../guides/issue-reproduction.md\n"
  },
  {
    "path": "packages/docs/src/content/docs/guides/issue-reproduction.md",
    "content": "---\ntitle: Issue Reproduction\nsidebar:\n  order: 4\n---\n\nIf you encounter an issue or false positives when using Knip, you can [open an\nissue on GitHub][1]. This will help you in your project, and will also improve\nKnip for everyone else!\n\nThink of Knip as a kitchen sink, it handles a large amount of projects and\nconfigurations, and your project is different from all others. Many factors may\ninfluence the issue at hand, such as:\n\n- Code syntax, import and export structure in source files\n- Dependencies, scripts and entry files in `package.json`\n- TypeScript configuration in `tsconfig.json`\n- Enabled plugins and related configuration files\n- Dependent or depending workspaces in a monorepo\n- Knip configuration in `knip.json`\n\n## Keep it minimal\n\nCreate the minimum of source code and configuration with a few files to\nreproduce and demonstrate the issue. Having this as a basis has many benefits:\n\n- Minimize barriers and unrelated contextual overhead\n- Optimize shared understanding of the situation\n- Serves as a \"contract\" to fix the actual issue\n- Files serve as a fixture to the project\n\nProviding this with an issue description will help us help you and improve the\nchances the issue can be looked into efficiently and in a timely manner.\n\nUseful **complements** to a minimal reproduction might include:\n\n- Screenshots, videos, code snippets or log output\n- Links to an existing project repository\n- Applications scaffolded by tools like Angular or Svelte CLI\n\n## Before opening an issue\n\nBefore opening an issue, please make sure you:\n\n- are using the latest version\n- have read the relevant documentation\n- have searched [existing issues][1]\n- have checked the list of [known issues][2]\n\nPlease file only a single issue at a time, so each of them can be labeled and\ntracked separately.\n\nThere is no need to open an issue if you're going to submit a pull request to\nclose it right away anyway. In that case it's preferred to keep things central\nin either an issue or pull request.\n\n## Templates\n\nA convenient way to create a minimal reproduction is by starting with one of\nthese templates in CodeSandbox or StackBlitz:\n\n| Template |                  |                 |\n| :------- | ---------------- | --------------- |\n| Basic    | [CodeSandbox][3] | [StackBlitz][4] |\n| Monorepo | [CodeSandbox][5] | [StackBlitz][6] |\n\nShoutout to [CodeSandbox][7] and [StackBlitz][8] for generously providing these\nfree dev containers!\n\n## Alternatives\n\nOther solutions to share a minimal and reproducible case may work well too,\nincluding:\n\n- A public repository on e.g. GitHub or GitLab.\n- A pull request with a new [test][9] and/or [fixture folder][10].\n\nThe goal is to have an easy and common understanding and reproduction. If you're\nunable to create a reproduction using one of the methods described then please\nclearly explain this in the issue or [contact me][11].\n\n## Pull Request\n\nThe optimal way is to add fixtures and failing tests to the Knip repository, and\nopen a pull request to discuss the issue! Also see [instructions for\ndevelopment][12].\n\n[1]: https://github.com/webpro-nl/knip/issues?q=is%3Aissue\n[2]: https://knip.dev/reference/known-issues\n[3]: https://codesandbox.io/p/devbox/github/webpro-nl/knip/main/templates/issue-reproduction/basic\n[4]: https://stackblitz.com/github/webpro-nl/knip/tree/main/templates/issue-reproduction/basic\n[5]: https://codesandbox.io/p/devbox/github/webpro-nl/knip/main/templates/issue-reproduction/monorepo\n[6]: https://stackblitz.com/github/webpro-nl/knip/tree/main/templates/issue-reproduction/monorepo\n[7]: https://codesandbox.io\n[8]: https://stackblitz.com\n[9]: https://github.com/webpro-nl/knip/blob/main/.github/DEVELOPMENT.md#tests\n[10]: https://github.com/webpro-nl/knip/tree/main/packages/knip/fixtures\n[11]: https://github.com/webpro\n[12]: https://github.com/webpro-nl/knip/blob/main/.github/DEVELOPMENT.md\n"
  },
  {
    "path": "packages/docs/src/content/docs/guides/namespace-imports.md",
    "content": "---\ntitle: Namespace Imports\n---\n\nThe intention of exports used through namespace imports may not always be clear\nto Knip. Here's a guide to better understand how Knip handles such exports.\n\n## Example\n\nWe start off by having two exports:\n\n```ts title=\"my-namespace.js\"\nexport const version = 'v5';\nexport const getRocket = () => '🚀';\n```\n\nThe next snippet shows how to import all the exports above on a namespace. All\nexports of the `my-namespace.js` module will be members on the `NS` object:\n\n```ts title=\"my-module.ts\"\nimport * as NS from './my-namespace.js';\nimport send from 'stats';\nsend(NS);\n```\n\nThe intention of export usage is not always clear. In the example above is\n`version` or `getRocket` used? We're not sure, but we _probably_ don't want them\nto be reported as unused. The same goes for the next example:\n\n```ts title=\"my-module.ts\"\nimport * as NS from './my-namespace.js';\n\nexport { NS };\n```\n\nIf this all usage of the `NS` namespace object, we also don't know whether\nindividual exports like `version` or `getRocket` will be used. However, if at\nleast one reference to a property such as `NS.version` is found, then the\nindividual exports are considered separately again and `getRocket` will be\nmarked as unused:\n\n```ts title=\"index.ts\"\nimport { NS } from './my-module.js';\n\nconst version = NS.version;\n```\n\n## The default heuristic\n\nKnip uses the following heuristic to determine which of the individual exports\nare used:\n\n- If there's one or more references to the import namespace object, but without\n  any property access, all exports on that namespace are considered used.\n- Otherwise, exports are considered separately.\n\nBelow are a few more examples, and a way to disable this default behavior.\n\n## Examples\n\nLet's take a look at more examples:\n\n```ts title=\"my-namespace.ts\"\nexport const start = 1;\n\nexport const end = 1;\n```\n\nIn the following cases all exports of `my-namespace.ts` are considered used:\n\n```ts title=\"index.ts\"\nimport * as NS from './my-namespace.js';\nimport send from 'stats';\n\nsend(NS);\n\nconst spread = { ...NS };\n\nconst shorthand = { NS };\n\nconst assignment = NS;\n\nconst item = [NS];\n\ntype TypeOf = typeof NS;\n\nObject.values(NS);\n\nfor (const fruit in Fruits) {\n  //\n}\n\nexport { NS };\n\nexport { NS as AliasedNS };\n\nexport = NS;\n```\n\nHowever, this is no longer the case when one of the properties is accessed:\n\n```ts title=\"index.js\"\nimport * as NS from './namespace.js';\n\nconst begin = NS.start;\n\nsend(NS);\n```\n\nIn this case, the `end` export will be reported as unused, even though the `NS`\nobject itself is referenced on its own as well.\n\n## Include `nsExports` and `nsTypes`\n\nTo disable the heuristic as explained above, and enforce Knip to consider each\nexport on a namespace individually, include the `nsExports` issue type:\n\n```json\n{\n  \"include\": [\"nsExports\"]\n}\n```\n\nOr use the `--include nsExports` argument from the CLI. The `nsTypes` can be\nadded as well to do the same for exported types.\n"
  },
  {
    "path": "packages/docs/src/content/docs/guides/performance.md",
    "content": "---\ntitle: Performance\n---\n\nThis page describes a few topics around Knip's performance, and how you might\nimprove it.\n\nKnip does not want to tell you how to structure files or how to write your code,\nbut it might still be good to understand inefficient patterns for Knip.\n\nUse the `--debug` and `--performance` flags to find potential bottlenecks.\n\n## Cache\n\nUse `--cache` to speed up consecutive runs.\n\n## Ignoring files\n\nFiles matching the `ignore` patterns are not excluded from the analysis. They're\njust not printed in the report. Use negated `entry` and `project` patterns to\nexclude files from the analysis.\n\nRead [configuring project files][1] for details and examples. Improving\nconfiguration may have a significant impact on performance.\n\n## Metrics\n\nUse [the `--performance` flag][2] to see how many times potentially expensive\nfunctions (e.g. `findReferences`) are invoked and how much time is spent in\nthose functions. Example usage:\n\n```sh\nknip --performance\n```\n\n## ignoreExportsUsedInFile\n\nThe [ignoreExportsUsedInFile][3] option slows down the process slightly.\nTypically, anywhere between 0.25% and 10% of total running time. To find out:\n\n```sh\nknip --performance-fn hasRefsInFile\n```\n\n## A last resort\n\nIn case Knip is unbearably slow (or even crashes), you could resort to [lint\nindividual workspaces][4].\n\n[1]: ./configuring-project-files.md\n[2]: ../reference/cli.md#--performance\n[3]: ../reference/configuration.md#ignoreexportsusedinfile\n[4]: ../features/monorepos-and-workspaces.md#filter-workspaces\n"
  },
  {
    "path": "packages/docs/src/content/docs/guides/troubleshooting.md",
    "content": "---\ntitle: Troubleshooting\nsidebar:\n  order: 2\n---\n\nWe can distinguish two types of issues:\n\n- [Lint issues reported by Knip][1]\n- [Exceptions thrown by Knip][2]\n\nAlso see the [debug][3] and [trace][4] options below that can help to\ntroubleshoot issues.\n\n:::note[Rationale]\n\nThe JavaScript/TypeScript ecosystem has a vast amount of frameworks and tools.\nAdditionally, file locations, configuration semantics, command-line arguments\nand so on vary wildly. Files and dependencies are referenced in many ways. Knip\ntries harder than you think to cover it all.\n\nKnip is intentionally strict to maximize its potential. It may initially report\nmany unused files. However, getting this right will result in great reports and\ntidy codebases.\n\nIf it doesn't work out at first, a small change can go a long way.\n\n:::\n\n## Lint issues reported by Knip\n\nKnip reports lint issues in your codebase. See [handling issues][5] to deal with\nthe reported issues.\n\nIf you're considering filing an issue, please read [issue reproduction][6]\nfirst.\n\nExit code 1 indicates a successful run, but lint issues were found.\n\n## Exceptions thrown by Knip\n\nKnip may throw an exception, resulting in an unsuccessful run.\n\nSee [known issues][7] as it may be listed there and a workaround may be\navailable. If it isn't clear what's throwing the exception, try another run with\n`--debug` to locate the cause of the issue with more details.\n\nIf you're considering filing an issue, please read [issue reproduction][6]\nfirst.\n\nExit code 2 indicates an exception was thrown by Knip.\n\n## Debug\n\nTo better understand why Knip reports what it does, run it in debug mode by\nadding `--debug` to the command:\n\n```sh\nknip --debug\n```\n\nThis will give a lengthy output, including:\n\n- Included workspaces\n- Used configuration per workspace\n- Enabled plugins per workspace\n- Glob patterns and options followed by matching file paths\n- Plugin config file paths and found dependencies per plugin\n- Compiled non-standard source files\n\n## Trace\n\nUse `--trace` to see where exports or dependencies are used:\n\n- Use `--trace-file [path]` to output this only for the given file.\n- Use `--trace-export [name]` to output this only for the given export name.\n- Use `--trace-dependency [name]` to find where a dependency is imported\n- Use both to trace a specific named or default export of a certain file.\n\nThis works across re-exports, barrel files and workspaces. Here's an example\nscreenshot:\n\n<img src=\"/screenshots/trace-export.png\" alt=\"trace export\" class=\"mw500\" />\n\nIt's like a reversed module graph. Instead of traversing imports it goes in the\nopposite direction and shows where exports are imported.\n\nThe `--trace-dependency` accepts strings for exact matches, but if it looks like\na regex that works too:\n\n<img src=\"/screenshots/trace-dependency.png\" alt=\"trace dependency\" class=\"mw500\" />\n\nUse [--workspace \\[filter\\]][8] to filter accordingly.\n\n#### Legend\n\n|     | Description                                 |\n| --- | :------------------------------------------ |\n| `✓` | Contains import and reference to the export |\n| `x` | Is not imported                             |\n| `◯` | Entry file                                  |\n\n## Opening an issue\n\nIf you want to open an issue, please see [issue reproduction][6].\n\n## Understanding Knip\n\nLooking to better understand how Knip works? The [entry files][9] and\n[plugins][10] explanations cover two core concepts. After this you might want to\ncheck out features like [production mode][11] and [monorepos & workspaces][12].\n\nIn a more general sense, [Why use Knip?][13] explains what Knip can do for you.\n\n## Asking for help\n\nIf you can't find your answer in any of the aforementioned resources, feel free\nto [open an issue on GitHub][14].\n\n[1]: #lint-issues-reported-by-knip\n[2]: #exceptions-thrown-by-knip\n[3]: #debug\n[4]: #trace\n[5]: ../guides/handling-issues.mdx\n[6]: ./issue-reproduction.md\n[7]: ../reference/known-issues.md\n[8]: ../reference/cli.md#--workspace-filter\n[9]: ../explanations/entry-files.md\n[10]: ../explanations/plugins.md\n[11]: ../features/production-mode.md\n[12]: ../features/monorepos-and-workspaces.md\n[13]: ../explanations/why-use-knip.md\n[14]: https://github.com/webpro-nl/knip/issues\n"
  },
  {
    "path": "packages/docs/src/content/docs/guides/using-knip-in-ci.md",
    "content": "---\ntitle: Using Knip in CI\n---\n\nKnip is your companion during local development. But it is even more valuable in\na continuous integration (CI) environment to prevent regressions over time. Knip\nwill notify you of unused dependencies, exports and files if you forgot to\nremove them.\n\nKnip will exit the process with code `1` if there are one or more issues.\n\n## GitHub Actions\n\nHere's an example workflow configuration for GitHub Actions:\n\n```yaml\nname: Lint project\n\non: push\n\njobs:\n  lint:\n    runs-on: ubuntu-latest\n    name: Ubuntu/Node v24\n    steps:\n      - uses: actions/checkout@v6\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 24\n      - name: Install dependencies\n        run: npm install --ignore-scripts\n      - name: Run knip\n        run: npm run knip\n```\n\n## Notes\n\n- Use [--cache][1] to speed up consecutive runs (default location:\n  `./node_modules/.cache/knip`).\n- See [Reporters][2] including the GitHub Actions reporter.\n- Consider running Knip twice: a default run and a [production mode][3] run.\n- See [CLI arguments → Output][4] for some relevant options such as\n  `--treat-config-hints-as-errors` and `--no-exit-code`.\n- In CI environments, the [--no-progress][5] flag is set automatically.\n\n[1]: ../reference/cli.md#--cache\n[2]: ../features/reporters.md\n[3]: ../features/production-mode.md\n[4]: ../reference/cli.md#output\n[5]: ../reference/cli.md#--no-progress\n"
  },
  {
    "path": "packages/docs/src/content/docs/guides/working-with-commonjs.md",
    "content": "---\ntitle: Working with CommonJS\n---\n\nCommonJS is the JavaScript module system using `require()` and `module.exports`\nstatements.\n\nKnip works well with CommonJS. You don't need to use ES Modules or a\n`tsconfig.json` to use Knip.\n\nThe dynamic nature of CommonJS leaves room for ambiguity: it's sometimes unclear\nwhether an export is a default or a named export (and thus how it should be\nimported). So we'll have to agree on a few conventions to prevent false\npositives. Those conventions are designed to minimize impact on existing\ncodebases, improve consistency, and ease migration to ES Modules or TypeScript.\n\nFor **named exports**, the recommendation is to assign keys to `module.exports`:\n\n```js\nconst B = function () {};\n\nmodule.exports.A = { option: true };\nmodule.exports.B = B;\n```\n\nAlternatively, assign an object with ONLY shorthand property assignments to\n`module.exports`:\n\n```js\nconst A = function () {};\nconst B = { option: true };\n\nmodule.exports = { A, B };\n```\n\nAnything else assigned to `module.exports` is considered a default export, and\nshould be imported as such.\n\nThe following **default import** of the **named exports** above will result in\nall those exports reported as unused, even when referenced like below:\n\n```js\nconst DefaultImport = require('./common.js');\nconst runtime = [DefaultImport.A, DefaultImport.B];\n```\n\nInstead, do this:\n\n```js\nconst { A, B } = require('./common.js');\nconst runtime = [A, B];\n```\n\nNot recommended per se, but the following import syntax also results in the\nnamed export `A` being used:\n\n```js\nconst runtime = [require('./common.js').A];\n```\n\nAdd a non-shorthand property to turn the named object notation into a single\n**default export**:\n\n```js\nconst A = function () {};\nconst B = { option: true };\n\nmodule.exports = { __esModule: true, A, B };\n```\n\nThe `__esModule` key could be named differently (but makes sense given it's an\ninformal \"CJS/ESM interop\" standard amongst compilers and bundlers).\n"
  },
  {
    "path": "packages/docs/src/content/docs/index.mdx",
    "content": "---\ntitle: Declutter your JavaScript & TypeScript projects\ndescription: Project linter to find unused dependencies, exports and files\ntemplate: splash\nhero:\n  title: Knip v6\n  tagline: Declutter your JavaScript & TypeScript projects\n  image:\n    file: ../../assets/logo.svg\n  actions:\n    - text: Let's Get Started\n      link: ./overview/getting-started\n      icon: right-arrow\n      variant: primary\n    - text: 'NEW: Knip v6'\n      link: /blog/knip-v6\n      icon: rocket\n      variant: secondary\n    - text: View on GitHub\n      link: https://github.com/webpro-nl/knip\n      icon: external\n      variant: minimal\n---\n\nimport { Card, CardGrid } from '@astrojs/starlight/components';\nimport { YouTube } from '@astro-community/astro-embed-youtube';\nimport Posts from '../../components/Posts.astro';\nimport Sponsors from '../../components/Sponsors.astro';\nimport Projects from '../../components/Projects.astro';\nimport Contributors from '../../components/Contributors.astro';\n\n:::section{.badges}\n\n[![NPM Version][2]][1] [![NPM Downloads][3]][1] [![GitHub Repo stars][5]][4]\n\n:::\n\n:::div{.sponsor}\n\n[Sponsor Me][6] / [Hire Me][7]\n\n:::\n\n:a[Sponsored by:]{.sponsors-intro href=\"/sponsors\"}\n\n<Sponsors showAll={false} />\n\n<CardGrid>\n  <Card title=\"Why use Knip?\" icon=\"approve-check\">\n    Knip finds and fixes unused dependencies, exports and files. Use it for\n    enhanced code and dependency management.\n  </Card>\n\n  <Card title=\"How does it work?\" icon=\"setting\">\n    Advanced analysis starting from fine-grained entry points based on the\n    actual frameworks and tooling in (mono)repos for accurate and actionable\n    results.\n  </Card>\n\n  <Card title=\"Battle-tested\" icon=\"approve-check\">\n    Thousands of projects are already using Knip!\n  </Card>\n\n  <Card title=\"Plugins\" icon=\"open-book\">\n    Knip comes with [100+ plugins][8] for tools and frameworks like Astro,\n    Cypress, ESLint, Jest, GitHub Actions, Next.js, Nx, Remix, Storybook,\n    Svelte, Vite, Vitest, Webpack and many, many more.\n  </Card>\n\n  <Card title=\"Playground\" icon=\"rocket\">\n    Try Knip on the [playground][9].\n  </Card>\n\n  <Card title=\"Troubleshooting\" icon=\"warning\">\n    Need help? [Start troubleshooting][10].\n  </Card>\n</CardGrid>\n\n## Trusted by the world's best software teams\n\n:::section{.projects}\n\n<Projects />\n\n:::\n\n## Introduction video\n\n[James Shopland][11] has a great introduction and overview of Knip (thanks\nJames!):\n\n<YouTube id=\"uhEkgWt-pUM\" />\n\n## Created by awesome contributors\n\nSpecial thanks to the wonderful people who have contributed to this project:\n\n<Contributors />\n\n## Articles about Knip\n\nA curated selection of articles about Knip, most recent first:\n\n- Tom McWright: [How to keep package.json under control][12] (2025-09-11)\n- Mohammed Farmaan: [Declutter Your JavaScript and TypeScript Projects][13]\n  (2025-08-13)\n- dip Engineer Blog: [Knipで安心してデッドコードを撲滅する][14]\n  (2025-04-04/Japanese 🇯🇵)\n- Tom MacWright: [Knip: good software for cleaning up TypeScript tech debt][15]\n  (2024-10-25)\n- Neng Apichet: [มาทำความสะอาด Project โค้ดของเราด้วย Knip กัน][16]\n  (2024-10-22/Thai 🇹🇭)\n- Anthony Pena: [Knip: l'ultime outil pour faire le ménage dans vos\n  projets!][17] (2024-10-08/French 🇫🇷)\n- Taro: [TypeScript/JavaScriptの不要なコードを削除するツール「Knip」の紹介][18]\n  (2024-07-25/Japanese 🇯🇵)\n- Kevin Bailey: [Delete Code with Knip][19] (2025-10-31)\n- Maddy Miller: [Using Knip to find dead code in a high-traffic git repo][20]\n  (2023-09-17)\n- Josh Goldberg: [Speeding Up Centered Part 4: Unused Code Bloat][21]\n  (2023-08-21)\n- Smashing Magazine: [Knip: An Automated Tool For Finding Unused Files, Exports,\n  And Dependencies][22] (2023-08-14)\n- Effective TypeScript: [Recommendation Update: ✂️ Use knip to detect dead code\n  and types][23] (2023-07-29)\n\n## Don't just take our word for it\n\n<Posts />\n\n## Read More\n\n- [Unused dependencies][24]\n- [Unused exports][25]\n\n[1]: https://www.npmjs.com/package/knip\n[2]: https://img.shields.io/npm/v/knip?color=f56e0f\n[3]: https://img.shields.io/npm/dm/knip?color=f56e0f\n[4]: https://github.com/webpro-nl/knip\n[5]: https://img.shields.io/github/stars/webpro-nl/knip?style=flat&color=f56e0f\n[6]: /sponsors\n[7]: https://webpro.nl/hire-me\n[8]: ./reference/plugins.md\n[9]: /playground\n[10]: ./guides/troubleshooting.md\n[11]: https://www.jamesshopland.com\n[12]: https://blog.val.town/gardening-dependencies\n[13]: https://farmaan.dev/writing/using-knip\n[14]: https://developer.dip-net.co.jp/entry/2025/04/04/Knipで安心してデッドコードを撲滅する\n[15]: https://macwright.com/2024/10/25/good-software-knip\n[16]: https://engineering.thinknet.co.th/มาทำความสะอาด-project-โค้ดของเราด้วย-knip-กัน-20dbd65f6b58\n[17]: https://k49.fr.nf/knip-l-ultime-outil-pour-faire-le-menage-dans-vos-projets/\n[18]: https://tech.basemachina.jp/entry/introduction-knip\n[19]: https://kevinabailey.com/delete-code-with-knip\n[20]: https://madelinemiller.dev/blog/knip-dead-code/\n[21]: https://www.joshuakgoldberg.com/blog/speeding-up-centered-part-4-unused-code-bloat/\n[22]: https://www.smashingmagazine.com/2023/08/knip-automated-tool-find-unused-files-exports-dependencies/\n[23]: https://effectivetypescript.com/2023/07/29/knip/\n[24]: ./typescript/unused-dependencies.md\n[25]: ./typescript/unused-exports.md\n"
  },
  {
    "path": "packages/docs/src/content/docs/overview/configuration.md",
    "content": "---\ntitle: Configuration\ndescription: config\n---\n\n## Defaults\n\nKnip has good defaults and aims for \"zero config\". Here's a simplified version\nof the default `entry` and `project` values:\n\n```json\n{\n  \"entry\": [\"index.{js,ts}\", \"src/index.{js,ts}\"],\n  \"project\": [\"**/*.{js,ts}\"]\n}\n```\n\nEntry files are the starting point for Knip to find more source files and\nexternal dependencies.\n\n## Location\n\nBy default, Knip will look for a configuration file with the following names:\n\n- `knip.json`\n- `knip.jsonc`\n- `.knip.json`\n- `.knip.jsonc`\n- `knip.ts`\n- `knip.js`\n- `knip.config.ts`\n- `knip.config.js`\n- `package.json` (in the `\"knip\"` property)\n\nIf you want to use a custom file name or path, use the `--config` flag:\n\n```sh\nknip --config path/to/knip.json\n```\n\n## Customize\n\nYour project structure may not match the default `entry` and `project` files.\nHere's an example custom configuration to include `.js` files in the `scripts`\nfolder:\n\n```json title=\"knip.json\"\n{\n  \"$schema\": \"https://unpkg.com/knip@6/schema.json\",\n  \"entry\": [\"src/index.ts\", \"scripts/{build,create}.js\"],\n  \"project\": [\"src/**/*.ts\", \"scripts/**/*.js\"]\n}\n```\n\nThe set of `project` files determines the analysis scope. Use negated `project`\npatterns to exclude files from the analysis. See [configuring project files][1]\nfor details.\n\nThe values you set override the default values, they are not merged.\n\n:::tip\n\nBe specific with `entry` files. Minimize the number of entry files and wildcards\nfor better results.\n\nPlugins are enabled automatically, and their entry files are added\nautomatically. Such as for Next.js, Astro, Remix, Vitest, Playwright and many\nmore.\n\n:::\n\nKnip looks for entry files in many places. Learn more in the next page about\n[entry files][2].\n\n## Configuration Options\n\nSee [configuration file options][3].\n\nUse JavaScript or TypeScript in a [dynamic configuration file][4].\n\n## What's next?\n\nThe best way to understand Knip and what it can do for you is to read the pages\nin the \"Understanding Knip\" sections, starting with [entry files][2].\n\nWant to learn more about some of the main features?\n\n- Working with [monorepos & workspaces][5].\n- Learn more about [production mode][6].\n\nHaving trouble configuring Knip?\n\n- [Configuring project files][1]\n- [Troubleshooting][7]\n\nSearch this website using the bar at the top (`Ctrl+K` or `⌘+K`).\n\n[1]: ../guides/configuring-project-files.md\n[2]: ../explanations/entry-files.md\n[3]: ../reference/configuration.md\n[4]: ../reference/dynamic-configuration.mdx\n[5]: ../features/monorepos-and-workspaces.md\n[6]: ../features/production-mode.md\n[7]: ../guides/troubleshooting.md\n"
  },
  {
    "path": "packages/docs/src/content/docs/overview/features.md",
    "content": "---\ntitle: Features\n---\n\nOverview of capabilities in support of the core feature: find many [types of\nissues][1].\n\nAlso see [related tooling][2].\n\n## Overview\n\n| Name                         | Description or example                                                |\n| :--------------------------- | :-------------------------------------------------------------------- |\n| [Auto-fix][3]                | Use `--fix` to auto-fix issues                                        |\n| [Cache][4]                   | Use `--cache` to speed up consecutive runs                            |\n| Catalog                      | Report & fix unused catalog entries                                   |\n| [CommonJS][5]                | CommonJS is still widely used & supported, but conditions apply       |\n| [Compilers][6]               | Support for Astro, MDX, Svelte, Vue and custom compilers              |\n| Configuration hints          | Display configuration hints to keep `knip.json` tidy                  |\n| [Debug][7]                   | Use `--debug` for troubleshooting                                     |\n| [Filters][8]                 | Exclude or focus on specific issue types                              |\n| [Format][9]                  | Use `--format` with `--fix` to auto-format modified files             |\n| [JSDoc tags][10]             | Tag and exclude specific exports from the report                      |\n| [Memory usage][11]           | Use `--memory` for detailed memory usage insights                     |\n| [Monorepos][12]              | Workspaces are first-class citizen                                    |\n| [Performance][13]            | Use `--performance` for detailed timing insights                      |\n| [Plugins][14]                | Over 100 plugins with custom entry paths and config parsing           |\n| [Plugins: inputs][15]        | Inputs are an affective mechanism to add entries, dependencies & more |\n| [Plugins: CLI arguments][16] | Tool-specific CLI argument parsing make plugins go the extra mile     |\n| [Preprocessors][17]          | Preprocess issues before being reported                               |\n| [Production mode][18]        | Use `--production` to lint only production code                       |\n| [Reporters][19]              | Choose from many built-in reporters or use your own                   |\n| [Rules][20]                  | Exclude or focus on specific issue types                              |\n| [Script parser][21]          | Shell scripts and `package.json` contain entry paths and dependencies |\n| [Source mapping][22]         | Map `dist` files back to `src` files                                  |\n| [Strict mode][23]            | Use `--strict` to tighten and lint only production `dependencies`     |\n| [Trace][24]                  | Trace exports to find where they are used                             |\n| [Watch mode][25]             | Use `--watch` for live updates of unused files and exports            |\n| [Workspace][26]              | Use `--workspace` to filter workspaces in a monorepo                  |\n\n[1]: ../reference/issue-types.md\n[2]: ../reference/related-tooling.md\n[3]: ../features/auto-fix.mdx\n[4]: ../reference/cli.md#--cache\n[5]: ../guides/working-with-commonjs.md\n[6]: ../features/compilers.md\n[7]: ../guides/troubleshooting.md#debug\n[8]: ../features/rules-and-filters.md#filters\n[9]: ../features/auto-fix.mdx#format\n[10]: ../reference/jsdoc-tsdoc-tags.md\n[11]: ../reference/cli.md#--memory\n[12]: ../features/monorepos-and-workspaces.md\n[13]: ../reference/cli.md#--performance\n[14]: ../explanations/plugins.md\n[15]: ../writing-a-plugin/inputs.md\n[16]: ../writing-a-plugin/argument-parsing.md\n[17]: ../features/reporters.md#preprocessors\n[18]: ../features/production-mode.md\n[19]: ../features/reporters.md\n[20]: ../features/rules-and-filters.md#rules\n[21]: ../features/script-parser.md\n[22]: ../features/source-mapping.md\n[23]: ../features/production-mode.md#strict-mode\n[24]: ../guides/troubleshooting.md#trace\n[25]: ../reference/cli.md#--watch\n[26]: ../features/monorepos-and-workspaces.md#filter-workspaces\n"
  },
  {
    "path": "packages/docs/src/content/docs/overview/getting-started.mdx",
    "content": "---\ntitle: Getting Started\nsidebar:\n  order: 1\n---\n\nimport { Tabs, TabItem } from '@astrojs/starlight/components';\n\n## Requirements\n\nKnip v6 requires at least Node.js v20.19.0. Or Bun.\n\nWant to try Knip without installation? Visit [the playground][1].\n\n## Installation\n\nThis is the easiest and recommended way to install Knip:\n\n<Tabs syncKey=\"pm\">\n  <TabItem label=\"npm\">\n    ```shell\n    npm init @knip/config\n    ```\n  </TabItem>\n\n  <TabItem label=\"pnpm\">\n    ```shell\n    pnpm create @knip/config\n    ```\n  </TabItem>\n\n  <TabItem label=\"bun\">\n    ```shell\n    bun create @knip/config\n    ```\n  </TabItem>\n\n  <TabItem label=\"yarn\">\n    ```shell\n    yarn create @knip/config\n    ```\n  </TabItem>\n</Tabs>\n\nNow you can run Knip to lint your project:\n\n<Tabs syncKey=\"pm\">\n  <TabItem label=\"npm\">\n    ```shell\n    npm run knip\n    ```\n  </TabItem>\n\n  <TabItem label=\"pnpm\">\n    ```shell\n    pnpm knip\n    ```\n  </TabItem>\n\n  <TabItem label=\"bun\">\n    ```shell\n    bun knip\n    ```\n  </TabItem>\n\n  <TabItem label=\"yarn\">\n    ```shell\n    yarn knip\n    ```\n  </TabItem>\n</Tabs>\n\nKnip will lint your project and report unused dependencies, exports and files.\n\nIf the output makes sense to you, feel free to go to the next page:\n[configuration][2].\n\n:::tip\n\nTry the [Knip Editor Extension][3]! Let your coding agent use the built-in MCP\nServer and create a custom `knip.json` for you.\n\n:::\n\n## Too Much?\n\nIn large or complex codebases the output might be overwhelming.\n\nStart by\nlimiting the number of shown issues per type:\n\n<Tabs syncKey=\"pm\">\n  <TabItem label=\"npm\">\n    ```shell\n    npm run knip -- --max-show-issues 5\n    ```\n  </TabItem>\n\n  <TabItem label=\"pnpm\">\n    ```shell\n    pnpm knip --max-show-issues 5\n    ```\n  </TabItem>\n\n  <TabItem label=\"bun\">\n    ```shell\n    bun knip --max-show-issues 5\n    ```\n  </TabItem>\n\n  <TabItem label=\"yarn\">\n    ```shell\n    yarn knip --max-show-issues 5\n    ```\n  </TabItem>\n</Tabs>\n\nThe output is easier to digest and may include some configuration hints to get\nan idea of what's left to configure. Many unused files? Go to [configuration][2]\nand follow up with [troubleshooting][4] if needed.\n\n:::tip\n\nDo not use the `ignore` option just to get rid of unwanted output. Read\n[Configuring Project Files][5] to get the most out of Knip.\n\n:::\n\n## Manual\n\nAlternatively, manually install Knip using your package manager:\n\n<Tabs syncKey=\"pm\">\n  <TabItem label=\"npm\">\n    ```shell\n    npm install -D knip typescript @types/node\n    ```\n  </TabItem>\n\n  <TabItem label=\"pnpm\">\n    ```shell\n    pnpm add -D knip typescript @types/node\n    ```\n  </TabItem>\n\n  <TabItem label=\"bun\">\n    ```shell\n    bun add -D knip typescript @types/node\n    ```\n  </TabItem>\n\n  <TabItem label=\"yarn\">\n    ```shell\n    yarn add -D knip typescript @types/node\n    ```\n  </TabItem>\n</Tabs>\n\nKnip uses `typescript` and ` @types/node` as peer dependencies to increase\ncompatibility with your project. No worries, they're probably in your\n`node_modules` already.\n\nThen add a `knip` script to your `package.json`:\n\n```json title=\"package.json\"\n{\n  \"name\": \"my-project\",\n  \"scripts\": {\n    \"knip\": \"knip\"\n  }\n}\n```\n\n## Without installation\n\nTo run Knip without adding it to your project:\n\n<Tabs syncKey=\"pm\">\n  <TabItem label=\"npm\">\n    ```shell\n    npx knip\n    ```\n  </TabItem>\n\n  <TabItem label=\"pnpm\">\n    ```shell\n    pnpm dlx knip\n    ```\n  </TabItem>\n\n  <TabItem label=\"bun\">\n    ```shell\n    bunx knip\n    ```\n  </TabItem>\n</Tabs>\n\nIn this scenario `typescript` and `@types/node` are expected to be installed\nalready.\n\n[1]: /playground\n[2]: ./configuration.md\n[3]: ../blog/for-editors-and-agents.md\n[4]: ../guides/troubleshooting.md\n[5]: ../guides/configuring-project-files.md\n"
  },
  {
    "path": "packages/docs/src/content/docs/overview/screenshots-videos.md",
    "content": "---\ntitle: Screenshots & videos\n---\n\n## Watch & auto-fix\n\nThis video demonstrates using the `--watch` and `--fix` options inside Visual\nStudio Code:\n\n<video controls width=\"500\">\n  <source src=\"/screenshots/watch-fix.mp4\" type=\"video/mp4\" />\n\n  <source src=\"/screenshots/watch-fix.webm\" type=\"video/webm\" />\n</video>\n\nThis works in any terminal. See [--watch][1] and [auto-fix][2] for more details.\n\n## Trace\n\nHere's an example screenshot that traces the `mapIterator` export in the\nTypeScript codebase:\n\n<img src=\"/screenshots/trace-export.png\" alt=\"trace export\" class=\"mw500\" />\n\nInspect complicated import and re-export chains at a glance:\n\n<img src=\"/screenshots/trace-file.png\" alt=\"trace file\" class=\"mw500\" />\n\nSee [Trace][3] for more details.\n\n## Performance\n\nAn example screenshot showing `--performance` output for the Knip codebase:\n\n<img src=\"/screenshots/performance.png\" alt=\"performance\" class=\"mw500\" />\n\nAlso see [--performance][4].\n\n[1]: ../reference/cli.md#--watch\n[2]: ../features/auto-fix.mdx\n[3]: ../guides/troubleshooting.md#trace\n[4]: ../reference/cli.md#--performance\n"
  },
  {
    "path": "packages/docs/src/content/docs/playground.mdx",
    "content": "---\ntitle: 'Playground'\ndescription: 'Playground for Knip'\ntemplate: splash\n---\n\nWelcome to the Knip playground. The codebases below have linting issues on\npurpose in the codebases, so Knip will report various types of issues.\n\nFeel free to play around! There are currently two playgrounds:\n\n- [Basic][1]\n- [Monorepo][2]\n\nShoutout to <a href=\"https://stackblitz.com\">StackBlitz</a> for generously\nproviding these free dev containers.\n\n## Basic\n\nVisit this [Knip playground on StackBlitz][3] to try out Knip:\n\n<div class=\"playground\">\n  <iframe src=\"https://stackblitz.com/github/webpro-nl/knip/tree/main/templates/playground/basic?file=README.md&view=editor\" />\n</div>\n\n## Monorepo\n\nVisit this [Knip playground on StackBlitz][4] to try out Knip in a monorepo\nsetting:\n\n<div class=\"playground\">\n  <iframe src=\"https://stackblitz.com/github/webpro-nl/knip/tree/main/templates/playground/monorepo?file=README.md&view=editor\" />\n</div>\n\n[1]: #basic\n[2]: #monorepo\n[3]: https://stackblitz.com/github/webpro-nl/knip/tree/main/templates/playground/basic?file=README.md&view=editor\n[4]: https://stackblitz.com/github/webpro-nl/knip/tree/main/templates/playground/monorepo?file=README.md&view=editor\n"
  },
  {
    "path": "packages/docs/src/content/docs/reference/cli.md",
    "content": "---\ntitle: CLI Arguments\n---\n\n## General\n\n### `--help`\n\nShortcut: `-h`\n\nPrints a summary of this page.\n\n### `--version`\n\nShortcut: `-V`\n\nPrint the version number.\n\n### `--no-progress`\n\nShortcut: `-n`\n\nDon't show dynamic progress updates. Progress is automatically disabled in CI\nenvironments.\n\n### `--config [file]`\n\nUse an alternative path for the configuration file. Default locations:\n\n- `knip.json`\n- `knip.jsonc`\n- `.knip.json`\n- `.knip.jsonc`\n- `knip.js`\n- `knip.ts`\n- `package.json#knip`\n\nShortcut: `-c`\n\n### `--use-tsconfig-files`\n\nUse `tsconfig.json` to define project files (override `project` patterns).\n\n### `--tsConfig [file]`\n\nShortcut: `-t`\n\nUse an alternative path for the TypeScript configuration file.\n\nUsing `-t jsconfig.json` is also supported.\n\nDefault location: `tsconfig.json`\n\n### `knip-bun`\n\nRun Knip using the Bun runtime (instead of Node.js + jiti).\n\n```shell\nknip-bun\n```\n\nThis is equal to `bunx --bun knip`\n\nRequires [Bun][1] to be installed. Also see [known issues][2] for the type of\nissues this might help with.\n\n### NO_COLOR\n\nThe built-in reporters use the [NO_COLOR][3] friendly [picocolors][4]:\n\n```sh\nNO_COLOR=1 knip\n```\n\n## Mode\n\n### `--cache`\n\nEnable caching.\n\nConsecutive runs are 10-40% faster as the results of file analysis (AST\ntraversal) are cached. Conservative. Cache strategy based on file meta data\n(modification time + file size).\n\n### `--cache-location`\n\nProvide alternative cache location.\n\nDefault location: `./node_modules/.cache/knip`\n\n### `--include-entry-exports`\n\nWhen a repository is self-contained or private, you may want to include entry\nfiles when reporting unused exports:\n\n```sh\nknip --include-entry-exports\n```\n\nAlso see [includeEntryExports][5].\n\n### `--no-gitignore`\n\nIgnore `.gitignore` files.\n\n### `--production`\n\nLint only production source files. This excludes:\n\n- entry files defined by plugins:\n  - test files\n  - configuration files\n  - Storybook stories\n- `devDependencies` from `package.json`\n\nRead more at [Production Mode][6].\n\n### `--strict`\n\nIsolate workspaces and consider only direct dependencies. Implies [production\nmode][7].\n\nRead more at [Production Mode][6].\n\n### `--watch`\n\nWatch current directory, and update reported issues when a file is modified,\nadded or deleted.\n\nWatch mode focuses on imports and exports in source files. During watch mode,\nchanges in `package.json` or `node_modules` may not cause an updated report.\n\n## Scope\n\n### `--workspace [filter]`\n\nSelect one or multiple workspaces (including its ancestor and dependent\nworkspaces). The default behavior is to lint all configured workspaces.\n\nShortcut: `-W`\n\nSee [filter workspaces][8] for more details and examples.\n\n### `--directory [dir]`\n\nDefault: `cwd` (current directory)\n\nRun the process from a different directory.\n\n### `--exclude`\n\nExclude provided issue types from report. Can be comma-separated or repeated.\n\nExample:\n\n```sh\nknip --exclude enumMembers\n```\n\n### `--include`\n\nReport only provided issue types. Can be comma-separated or repeated.\n\nExample:\n\n```sh\nknip --include files,dependencies\nknip --include files --include dependencies\n```\n\nAvailable [issue types][9] when filtering output using `--include` or\n`--exclude`:\n\n- `files`\n- `dependencies`\n- `unlisted`\n- `unresolved`\n- `exports`\n- `nsExports`\n- `types`\n- `nsTypes`\n- `enumMembers`\n- `namespaceMembers`\n- `duplicates`\n- `catalog`\n\n### `--dependencies`\n\nShortcut to include all types of dependency issues:\n\n```sh\n--include dependencies,unlisted,binaries,unresolved,catalog\n```\n\n### `--exports`\n\nShortcut to include all types of export issues:\n\n```sh\n--include exports,nsExports,types,nsTypes,enumMembers,namespaceMembers,duplicates\n```\n\n### `--files`\n\nShortcut to include file issues:\n\n```sh\n--include files\n```\n\n### `--tags`\n\nExports can be tagged with known or arbitrary JSDoc/TSDoc tags:\n\n```ts\n/**\n * Description of my exported value\n *\n * @type number\n * @internal Important matters\n * @lintignore\n */\nexport const myExport = 1;\n```\n\nAnd then include (`+`) or exclude (`-`) these tagged exports from the report\nlike so:\n\n```shell\nknip --tags=-lintignore,-internal\nknip --tags=+custom\n```\n\nThis way, you can either focus on or ignore specific tagged exports with tags\nyou define yourself. This also works for individual enum and namespace members.\n\nThe default directive is `+` (include) and the `@` prefix is ignored, so the\nnotation below is valid and will report only exports tagged `@lintignore` or\n`@internal`:\n\n```shell\nknip --tags @lintignore --tags @internal\n```\n\n## Fix\n\n### `--fix`\n\nRead more at [auto-fix][10].\n\n### `--fix-type`\n\nFix only issues of type, can be comma-separated or repeated.\n\nMore info about fixable types at [issue types][9]\n\n### `--allow-remove-files`\n\nAllow Knip to remove files (with `--fix`).\n\n### `--format`\n\nFormat modified files after `--fix` using the local formatter.\n\n## Output\n\n### `--preprocessor [preprocessor]`\n\nPreprocess the results before providing it to the [reporter(s)][11].\n\nCan be repeated. Examples:\n\n```sh\nknip --preprocessor ./my-preprocessor.ts\n```\n\n```sh\nknip --preprocessor preprocessor-package\n```\n\nAlso see [Reporters & Preprocessors][12].\n\n### `--preprocessor-options [json]`\n\nPass extra options to the preprocessor as JSON string.\n\n```sh\nknip --preprocessor ./preproc.ts --preprocessor-options '{\"key\":\"value\"}'\n```\n\n### `--reporter [reporter]`\n\nAvailable reporters:\n\n- `symbols` (default)\n- `compact`\n- `codeowners`\n- `json`\n- `codeclimate`\n- `markdown`\n- `disclosure`\n- `github-actions`\n\nCan be repeated. Example:\n\n```sh\nknip --reporter compact\n```\n\nAlso see [Reporters & Preprocessors][12].\n\n### `--reporter-options [json]`\n\nPass extra options to the reporter (as JSON string):\n\nExample:\n\n```sh\nknip --reporter codeowners --reporter-options '{\"path\":\".github/CODEOWNERS\"}'\n```\n\n### `--no-config-hints`\n\nSuppress configuration hints.\n\n### `--treat-config-hints-as-errors`\n\nExit with non-zero exit code (`1`) if there are any configuration hints.\n\n### `--max-issues`\n\nMaximum number of issues before non-zero exit code. Default: `0`\n\n### `--max-show-issues`\n\nMaximum number of issues per type to display (does not affect exit code).\n\n### `--no-exit-code`\n\nAlways exit with code zero (`0`), even when there are lint issues.\n\nThe default exit codes:\n\n| Code | Description                                                      |\n| :--: | :--------------------------------------------------------------- |\n| `0`  | Knip ran successfully, no lint issues                            |\n| `1`  | Knip ran successfully, but there is at least one lint issue      |\n| `2`  | Knip did not run successfully due to bad input or internal error |\n\n## Troubleshooting\n\n### `--debug`\n\nShortcut: `-d`\n\nShow [debug output][13].\n\n### `--memory`\n\n```txt frame=terminal\nknip --memory\n\n(results)\n\n # heapUsed  heapTotal  freemem\n-- --------  ---------  -------\n 0    42.09      70.91  2251.00\n 1   927.04    1042.58  1166.47\n 2   973.29    1047.33  1160.92\n 3   971.54    1079.83  1121.66\n 4   997.80    1080.33  1120.34\n 5  1001.88    1098.08  1100.72\n 6  1038.69    1116.58  1100.72\n 7  1082.12    1166.33  1100.72\n 8  1145.46    1224.50  1100.72\n 9  1115.82    1240.25  1100.72\n10  1182.35    1249.75   973.05\n11   637.32    1029.17   943.63\n12   674.30    1029.33   943.39\n13   682.24    1029.33   941.63\n14   707.70    1029.33   937.48\n\nTotal running time: 4.3s\n```\n\n### `--memory-realtime`\n\nUse this if Knip crashes to still see memory usage info over time:\n\n```txt frame=terminal\nknip --memory-realtime\n\n#  heapUsed  heapTotal  freemem\n-  --------  ---------  -------\n0     42.09      70.91  2251.00\n1    927.04    1042.58  1166.47\n...mem info keeps being logged...\n\n(results)\n```\n\n### `--performance`\n\nUse this flag to get the count and execution time of potentially expensive\nfunctions in a table. Example:\n\n```txt frame=terminal\n$ knip --performance\n\n(results)\n\nName                           size  min       max       median    sum\n-----------------------------  ----  --------  --------  --------  --------\nfindReferences                  648     84.98   7698.61     96.41  70941.70\ncreateProgram                     2   6295.84   7064.68   6680.26  13360.52\nglob                              6      0.05    995.78    513.82   3150.87\nfindESLintDependencies            2      0.01     74.41     37.21     74.41\nfindGithubActionsDependencies     6      0.16     12.71      0.65     23.45\nfindBabelDependencies             2      0.00     38.75     19.37     38.75\n...\n\nTotal running time: 5s\n```\n\n- `name`: the internal Knip function name\n- `size`: number of function invocations\n- `min`: the fastest invocation\n- `max`: the slowest invocation\n- `median`: the median invocation\n- `sum` the accumulated time of all invocations\n\nThis is not yet available in Bun, since it does not support\n`performance.timerify` ([GitHub issue][14]).\n\n### `--performance-fn`\n\nLimit the output of `--performance` to a single function to minimize the\noverhead of the `timerify` Node.js built-in and focus on that function alone:\n\n```txt frame=terminal\n$ knip --performance-fn resolveSync\n\n(results)\n\nName         size   min   max   median   sum\n-----------  -----  ----  ----  ------  ------\nresolveSync  66176  0.00  5.69    0.00  204.85\n\nTotal running time: 12.9s\n```\n\n### `--trace`\n\nTrace exports to see where they are imported.\n\nAlso see [Trace][15].\n\n### `--trace-dependency [name]`\n\nTrace package or binary name to see where it's referenced. Implies\n[--trace][16].\n\n### `--trace-export [name]`\n\nTrace export name to see where it's imported. Implies [--trace][16].\n\n### `--trace-file [path]`\n\nTrace file to see where its exports are imported. Implies [--trace][16].\n\n[1]: https://bun.sh\n[2]: ../reference/known-issues.md\n[3]: https://no-color.org/\n[4]: https://www.npmjs.com/package/picocolors\n[5]: ./configuration.md#includeentryexports\n[6]: ../features/production-mode.md\n[7]: #--production\n[8]: ../features/monorepos-and-workspaces.md#filter-workspaces\n[9]: ./issue-types.md\n[10]: ../features/auto-fix.mdx\n[11]: #--reporter-reporter\n[12]: ../features/reporters.md\n[13]: ../guides/troubleshooting.md#debug\n[14]: https://github.com/oven-sh/bun/issues/9271\n[15]: ../guides/troubleshooting.md#trace\n[16]: #--trace\n"
  },
  {
    "path": "packages/docs/src/content/docs/reference/configuration-hints.md",
    "content": "---\ntitle: Configuration Hints\n---\n\nKnip emits configuration hints to help keep your configuration file tidy and\nreduce drift.\n\nThey're warnings by default, not lint findings. But addressing the hints might\nimprove results significantly. The hints on this page are sorted by\nimpact/importance (most impactful first).\n\nUse [`--treat-config-hints-as-errors`][1] or [`treatConfigHintsAsErrors`][2] to\nmake any configuration hint result in a non-zero exit code and fail CI.\n\n## Unconfigured projects\n\nToo many unused files in a single workspace-codebase, caused by a missing\nconfiguration file or incomplete configuration:\n\n```\nCreate knip.json configuration file, and add entry and/or refine project files (42 unused files)\n```\n\nOr if you have a config file:\n\n```\nAdd entry and/or refine project files (42 unused files)\n```\n\n**Solution**: Add and refine `entry` and/or `project` patterns.\n\nRelated resources:\n\n- [Configuration][3]\n- [Monorepos & Workspaces][4]\n\n## Unconfigured workspaces\n\nToo many unused files in a monorepo, caused by a missing configuration file or\nincomplete configuration:\n\n```\nCreate knip.json configuration file with workspaces[\"packages/app\"] object (42 unused files)\n```\n\nOr if you have a config file:\n\n```\nAdd entry and/or refine project files in workspaces[\"packages/app\"] (42 unused files)\n```\n\n**Solution**: add a `workspaces` object to configuration. Add and refine `entry`\nand/or `project` patterns. For instance, the example message translates to\nconfiguration like this:\n\n```json\n{\n  \"workspaces\" {\n    \"packages/app\": {\n      \"entry\": [\"src/App.tsx\"],\n      \"project\": [\"src/**/*.ts\"]\n    }\n  }\n}\n```\n\nRelated resources:\n\n- [Configuring Project Files][5]\n- [Monorepos & Workspaces][4]\n\n## Top-level entry/project\n\nIn monorepos, Knip uses only `entry` and `project` under `workspaces`. The\ntop-level keys with the same name are ignored.\n\n```\nRemove, or move unused top-level entry to one of \"workspaces\"\nRemove, or move unused top-level project to one of \"workspaces\"\n```\n\n**Solution**: Remove top-level `entry` and `project` keys, use these options in\n`workspaces` instead.\n\n```jsonc\n{\n  \"entry\": [\"src/App.tsx\"], // move entry/project from here... ↴\n  \"project\": [\"src/**/*.ts\"],\n  \"workspaces\": {\n    \".\": {\n      \"entry\": [\"src/App.tsx\"], // ...to the correct workspace(s) ↲\n      \"project\": [\"src/**/*.ts\"],\n    },\n  },\n}\n```\n\n## Unused entry in ignore group\n\nAn entry of an ignore list is no longer needed, remove it.\n\n```\nRemove from ignoreWorkspaces\nRemove from ignoreDependencies\nRemove from ignoreBinaries\nRemove from ignoreUnresolved\n```\n\n**Solution**: Remove the entry from the array.\n\n## Useless patterns\n\nA glob pattern in `entry` or `project` does not match any files.\n\n```\nRefine entry pattern (no matches)\nRefine project pattern (no matches)\n```\n\n**Solution**: Refine or remove the `entry` or `project` entry.\n\n## Redundant patterns\n\nA glob/pattern is redundant, because it's already covered by defaults or added\nby an enabled plugin.\n\n```\nRemove redundant entry pattern\nRemove redundant project pattern\n```\n\n**Solution**: Remove the `entry` or `project` entry.\n\n## Missing package entry file\n\n```\nPackage entry file not found\n```\n\n**Solution**: Refine or remove the entry in `package.json`.\n\n[1]: ./cli.md#--treat-config-hints-as-errors\n[2]: ./configuration.md#treatconfighintsaserrors\n[3]: ../overview/configuration.md\n[4]: ../features/monorepos-and-workspaces.md\n[5]: ../guides/configuring-project-files.md\n"
  },
  {
    "path": "packages/docs/src/content/docs/reference/configuration.md",
    "content": "---\ntitle: Configuration\n---\n\nThis page lists all configuration file options.\n\n## File Types\n\n### JSON Schema\n\nA `$schema` field is a URL that you put at the top of your JSON file. This\nallows you to get red squiggly lines inside of your IDE when you make a typo or\nprovide an otherwise invalid configuration option.\n\nIn JSON, use the provided JSON schema:\n\n```json title=\"knip.json\"\n{\n  \"$schema\": \"https://unpkg.com/knip@6/schema.json\"\n}\n```\n\n### JSONC\n\nIn JSONC, use the provided JSONC schema:\n\n```json title=\"knip.jsonc\"\n{\n  \"$schema\": \"https://unpkg.com/knip@6/schema-jsonc.json\"\n}\n```\n\nUse JSONC if you want to use comments and/or trailing commas.\n\n### TypeScript\n\nSee [dynamic configuration][1] about dynamic and typed configuration files.\n\n## Project\n\n### `entry`\n\nArray of glob patterns to find entry files. Prefix with `!` for negation.\nExample:\n\n```json title=\"knip.json\"\n{\n  \"entry\": [\"src/index.ts\", \"scripts/*.ts\", \"!scripts/except-this-one.ts\"]\n}\n```\n\nAlso see [configuration][2] and [entry files][3].\n\n### `project`\n\nArray of glob patterns to find project files. Example:\n\n```json title=\"knip.json\"\n{\n  \"project\": [\"src/**/*.ts\", \"scripts/**/*.ts\"]\n}\n```\n\nAlso see [configuration][2] and [entry files][3].\n\n### `paths`\n\nTools like TypeScript, webpack and Babel support import aliases in various ways.\nKnip automatically includes `compilerOptions.paths` from the TypeScript\nconfiguration, but does not automatically use other types of import aliases.\nThey can be configured manually:\n\n```json title=\"knip.json\"\n{\n  \"paths\": {\n    \"@lib\": [\"./lib/index.ts\"],\n    \"@lib/*\": [\"./lib/*\"]\n  }\n}\n```\n\nEach workspace can have its own `paths` configured. Knip `paths` follow the\nTypeScript semantics:\n\n- Path values are an array of relative paths.\n- Paths without an `*` are exact matches.\n\n## Workspaces\n\nIndividual workspace configurations may contain all other options listed on this\npage, except for the following root-only options:\n\n- `exclude` / `include`\n- `ignoreExportsUsedInFile`\n- `ignoreWorkspaces`\n- `workspaces`\n\nWorkspaces can't be nested in a Knip configuration, but they can be nested in a\nmonorepo folder structure.\n\nAlso see [Monorepos and workspaces][4].\n\n## Plugins\n\nThere are a few options to modify the behavior of a plugin:\n\n- Override a plugin's `config` or `entry` location\n- Force-enable a plugin by setting its value to `true`\n- Disable a plugin by setting its value to `false`\n\n```json title=\"knip.json\"\n{\n  \"mocha\": {\n    \"config\": \"config/mocha.config.js\",\n    \"entry\": [\"**/*.spec.js\"]\n  },\n  \"playwright\": true,\n  \"webpack\": false\n}\n```\n\nIt should be rarely necessary to override the `entry` patterns, since plugins\nalso read custom entry file patterns from the tooling configuration (see\n[Plugins → entry files][5]).\n\nPlugin configuration can be set on root and on a per-workspace level. If enabled\non root level, it can be disabled on workspace level by setting it to `false`\nthere, and vice versa.\n\nAlso see [Plugins][6].\n\n## Rules & Filters\n\n### `rules`\n\nSee [Rules & Filters][7].\n\n### `include`\n\nSee [Rules & Filters][7].\n\n### `exclude`\n\nSee [Rules & Filters][7].\n\n### `tags`\n\nExports can be tagged with known or arbitrary JSDoc/TSDoc tags:\n\n```ts\n/**\n * Description of my exported value\n *\n * @type number\n * @internal Important matters\n * @lintignore\n */\nexport const myExport = 1;\n```\n\nAnd then include (`+`) or exclude (`-`) these tagged exports from the report\nlike so:\n\n```json\n{\n  \"tags\": [\"-lintignore\"]\n}\n```\n\nThis way, you can either focus on or ignore specific tagged exports with tags\nyou define yourself. This also works for individual enum and namespace members.\n\nThe default directive is `+` (include) and the `@` prefix is ignored, so the\nnotation below is valid and will report only exports tagged `@lintignore` or\n`@internal`:\n\n```json\n{\n  \"tags\": [\"@lintignore\", \"@internal\"]\n}\n```\n\n:::caution\n\nTags must not contain hyphens or plus symbols, so it is recommended to stick to\nletters and avoid snake-case.\n\n:::\n\nAlso see [JSDoc & TSDoc Tags][8].\n\n### `treatConfigHintsAsErrors`\n\nExit with non-zero code (1) if there are any configuration hints.\n\n```json title=\"knip.json\"\n{\n  \"treatConfigHintsAsErrors\": true\n}\n```\n\n## Ignore Issues\n\n### `ignore`\n\n:::tip\n\nPlease read [configuring project files][9] before using the `ignore` option.\n\n:::\n\nAvoid `ignore` patterns. There is almost always a better solution:\n\n- Follow up on configuration hints (if there are any).\n- Fine-tune `entry` and `project` patterns.\n- Use [production mode][10].\n- Other `ignore*` options.\n\n**NOTE**: An exception to the rule: to _temporarily_ report only issues in files\nthat match the negated `ignore` pattern:\n\n```json title=\"knip.json\"\n{\n  \"ignore\": [\"!src/dir/**\"]\n}\n```\n\n### `ignoreFiles`\n\nArray of glob patterns of files to exclude from the \"Unused files\" section only.\n\nUnlike `ignore`, which suppresses all issue types for matching files,\n`ignoreFiles` only affects the `files` issue type. Use this when a file should\nremain analyzed for other issues (exports, dependencies, unresolved) but should\nnot be considered for unused file detection.\n\n```json title=\"knip.json\"\n{\n  \"ignoreFiles\": [\"src/generated/**\", \"fixtures/**\"]\n}\n```\n\nSuffix an item with `!` to enable it only in production mode.\n\n### `ignoreBinaries`\n\nExclude binaries that are used but not provided by any dependency from the\nreport. Value is an array of binary names or regular expressions. Example:\n\n```json title=\"knip.json\"\n{\n  \"ignoreBinaries\": [\"zip\", \"docker-compose\", \"pm2-.+\"]\n}\n```\n\nActual regular expressions can be used in dynamic configurations:\n\n```ts title=\"knip.ts\"\nexport default {\n  ignoreBinaries: [/^pm2-.+/],\n};\n```\n\nSuffix an item with `!` to enable it only in production mode.\n\n### `ignoreDependencies`\n\nArray of package names to exclude from the report. Regular expressions allowed.\nExample:\n\n```json title=\"knip.json\"\n{\n  \"ignoreDependencies\": [\"hidden-package\", \"@org/.+\"]\n}\n```\n\nActual regular expressions can be used in dynamic configurations:\n\n```ts title=\"knip.ts\"\nexport default {\n  ignoreDependencies: [/@org\\/.*/, /^lib-.+/],\n};\n```\n\nSuffix an item with `!` to enable it only in production mode.\n\nAlso see [Unused dependencies][11].\n\n### `ignoreMembers`\n\nArray of enum and namespace members to exclude from the report. Regular\nexpressions allowed. Example:\n\n```json title=\"knip.json\"\n{\n  \"ignoreMembers\": [\"render\", \"on.+\"]\n}\n```\n\nActual regular expressions can be used in dynamic configurations.\n\n### `ignoreUnresolved`\n\nArray of specifiers to exclude from the report. Regular expressions allowed.\nExample:\n\n```json title=\"knip.json\"\n{\n  \"ignoreUnresolved\": [\"ignore-unresolved-import\", \"#virtual/.+\"]\n}\n```\n\nActual regular expressions can be used in dynamic configurations:\n\n```ts title=\"knip.ts\"\nexport default {\n  ignoreUnresolved: [/^#/.+/],\n};\n```\n\n### `ignoreWorkspaces`\n\nArray of workspaces to ignore, globs allowed. Example:\n\n```json title=\"knip.json\"\n{\n  \"ignoreWorkspaces\": [\n    \"packages/go-server\",\n    \"packages/flat/*\",\n    \"packages/deep/**\"\n  ]\n}\n```\n\nSuffix an item with `!` to enable it only in production mode.\n\nPrefix an item with `!` to override an earlier wildcard.\n\n### `ignoreIssues`\n\nIgnore specific issue types for specific file patterns. Keys are glob patterns\nand values are arrays of issue types to ignore for matching files. This allows\nignoring specific issues (like unused exports) in generated files while still\nreporting other issues in those same files.\n\n```json title=\"knip.json\"\n{\n  \"ignoreIssues\": {\n    \"src/generated/**\": [\"exports\", \"types\"],\n    \"**/*.generated.ts\": [\"exports\", \"enumMembers\", \"namespaceMembers\"]\n  }\n}\n```\n\n## Exports\n\n### `ignoreExportsUsedInFile`\n\nIn files with multiple exports, some of them might be used only internally. If\nthese exports should not be reported, there is a `ignoreExportsUsedInFile`\noption available. With this option enabled, when something is also no longer\nused internally, it will be reported as unused.\n\n```json title=\"knip.json\"\n{\n  \"ignoreExportsUsedInFile\": true\n}\n```\n\nIn a more fine-grained manner, to ignore only specific issue types:\n\n```json title=\"knip.json\"\n{\n  \"ignoreExportsUsedInFile\": {\n    \"interface\": true,\n    \"type\": true\n  }\n}\n```\n\n### `includeEntryExports`\n\nBy default, Knip does not report unused exports in entry files. When a\nrepository (or workspace) is self-contained or private, you may want to include\nentry files when reporting unused exports:\n\n```json title=\"knip.json\"\n{\n  \"includeEntryExports\": true\n}\n```\n\nIf enabled, Knip will report unused exports in entry source files. But not in\nentry and configuration files as configured by plugins, such as `next.config.js`\nor `src/routes/+page.svelte`.\n\nThis will also enable reporting unused members of exported enums and namespaces.\n\nSet this option at root level to enable this globally, or within workspace\nconfigurations individually.\n\n## Compilers\n\nKnip supports custom compilers to transform files before analysis.\n\n:::note\n\nSince compilers are functions, they can only be used in dynamic configuration\nfiles (`.js` or `.ts`), not in JSON configuration files.\n\n:::\n\n### `compilers`\n\nOverride built-in compilers or add custom compilers for additional file types.\n\nAlso see [Compilers][12].\n\n[1]: ../reference/dynamic-configuration.mdx\n[2]: ../overview/configuration.md\n[3]: ../explanations/entry-files.md\n[4]: ../features/monorepos-and-workspaces.md\n[5]: ../explanations/plugins.md#entry-files\n[6]: ../explanations/plugins.md\n[7]: ../features/rules-and-filters.md#filters\n[8]: ./jsdoc-tsdoc-tags.md\n[9]: ../guides/configuring-project-files.md\n[10]: ../features/production-mode.md\n[11]: ../guides/handling-issues.mdx#unused-dependencies\n[12]: ../features/compilers.md\n"
  },
  {
    "path": "packages/docs/src/content/docs/reference/dynamic-configuration.mdx",
    "content": "---\ntitle: Dynamic Configuration\n---\n\nimport { Tabs, TabItem } from '@astrojs/starlight/components';\n\n## TypeScript\n\nInstead of a JSON file, you can use a JavaScript or TypeScript file for a\ndynamic configuration and type annotations:\n\n<Tabs syncKey=\"lang\">\n  <TabItem label=\"TypeScript\">\n    ```ts title=\"knip.ts\"\n    import type { KnipConfig } from 'knip';\n\n    const config: KnipConfig = {\n      entry: ['src/index.ts'],\n      project: ['src/**/*.ts'],\n    };\n\n    export default config;\n    ```\n  </TabItem>\n\n  <TabItem label=\"JavaScript\">\n    ```js title=\"knip.js\"\n    /** @type {import('knip').KnipConfig} */\n    const config = {\n      entry: ['src/index.ts'],\n      project: ['src/**/*.ts'],\n    };\n\n    export default config;\n    ```\n  </TabItem>\n</Tabs>\n\n## Function\n\nYou can export a regular or async function that returns the configuration object\nas well:\n\n<Tabs syncKey=\"lang\">\n  <TabItem label=\"TypeScript\">\n    ```ts title=\"knip.ts\"\n    import type { KnipConfig } from 'knip';\n\n    const config = async (): Promise<KnipConfig> => {\n      const items = await fetchRepoInfo();\n\n      return {\n        entry: ['src/index.ts', ...items],\n        project: ['src/**/*.ts'],\n      };\n    };\n\n    export default config;\n    ```\n  </TabItem>\n\n  <TabItem label=\"JavaScript\">\n    ```ts title=\"knip.js\"\n    const config = async () => ({\n      entry: ['src/index.ts'],\n      project: ['src/**/*.ts'],\n    });\n\n    export default config;\n    ```\n  </TabItem>\n</Tabs>\n"
  },
  {
    "path": "packages/docs/src/content/docs/reference/faq.md",
    "content": "---\ntitle: FAQ\ndate: 2024-08-20\n---\n\n## Introduction\n\nKnip finds and fixes unused dependencies, exports and files. As a \"kitchen sink\"\nin the npm ecosystem, it creates comprehensive module and dependency graphs of\nyour project.\n\n:::note[Rationale]\n\nThe JavaScript/TypeScript ecosystem has a vast amount of frameworks and tools.\nAdditionally, file locations, configuration semantics, command-line arguments\nand so on vary wildly. Files and dependencies are referenced in many ways. Knip\ntries harder than you think to cover it all.\n\n:::\n\nThis FAQ is an attempt to provide some perspective on a few design decisions and\nwhy certain things work the way they do. Here and there it's intentionally a bit\nmore in-depth than the rest of the docs.\n\n## Why should I even bother to do all this?\n\nBecause the payoff is huge. While it might take a bit of effort to configure\nKnip correctly initially, a clean module graph gives you absolute confidence in\nyour codebase. You can delete dead code, remove unused dependencies, and\nrefactor with certainty. It prevents the slow accumulation of technical debt and\nkeeps your project lean and fast. Once configured, Knip runs quickly in CI and\nkeeps your codebase pristine automatically.\n\n:::tip\n\nTry the [Knip MCP Server][1] or the [Knip Editor Extension][2]. Let your coding\nagent use the built-in MCP Server and create a custom `knip.json` for you, so\nyou don't have to.\n\n:::\n\n## Common Pitfalls\n\n### Why shouldn't I ignore or disable configuration hints?\n\nConfiguration hints are critical for building a healthy and accurate module\ngraph. They usually indicate that Knip cannot resolve a dependency, plugin, or\nentry file. If you ignore or disable these hints, Knip's understanding of your\nproject will be incomplete, which inevitably leads to false positives (reporting\nused code as unused). Always address configuration hints first before looking at\nother reported issues.\n\n### Why is it a bad idea to use `ignore` patterns like I do in ESLint?\n\nKnip is not a regular file-based linter like ESLint. It works by analyzing the\nentire interconnected module graph of your project. Using `ignore` patterns does\nnot exclude files from the analysis, it only suppresses the reporting of issues\nin those files. This hides real issues and creates blind spots. Instead of\nignoring files, ensure your entry points and plugins are configured correctly,\nand use `project` patterns to define the boundaries of your codebase. Read more:\n[Configuring Project Files][3].\n\nIf you have specific exports (such as types) that are only used within the file\nthey are defined, use the [ignoreExportsUsedInFile][4] configuration option\nrather than ignoring the file entirely.\n\n### How should I exclude tests and development tools from the analysis?\n\nA common mistake is trying to exclude test files, storybooks, or development\ntools using `project` or `ignore` patterns. The correct approach is to use\n[production mode][5]. This mode is specifically designed to strictly analyze\nonly your production source code and `dependencies`, automatically excluding\ntests and `devDependencies` without requiring complex ignore rules.\n\n### Why shouldn't I run `knip --fix` immediately?\n\nRunning `knip --fix` before your configuration is fully settled is dangerous. If\nyour configuration is missing entry points or has unresolved hints, Knip might\nthink perfectly valid, actively used code is unused. Auto-fixing in this state\ncan lead to deleting code that your application relies on. Always verify the\nreported issues manually and ensure your configuration is solid before using the\n`--fix` flag.\n\n## Comparison\n\n### Why isn't Knip an ESLint plugin?\n\nLinters like ESLint analyze files separately, while Knip lints projects as a\nwhole.\n\nKnip requires full module and dependency graphs to find clutter across the\nproject. Creating these comprehensive graphs is not a trivial task and it seems\nno such tool exists today, even more so when it comes to monorepos.\n\nFile-oriented linters like ESLint are complementary to Knip.\n\n### Isn't tree-shaking enough?\n\nIn short: no. They share an important goal: improve UX by removing unused code.\nBut tree-shaking and Knip are different and complementary tools.\n\nTree-shaking is a build or compile-time activity to reduce production bundle\nsize. It typically operates on bundled production code, which might include\nexternal/third-party code. A build time optimization and \"out of your hands\".\n\nOn the other hand, Knip is a project linter that should be part of the\ndevelopment and QA phase. It lints, reports and fixes only your own source code.\nMoreover, in contrast with other linters, focuses on inter-file dependencies, so\ndead code within a file may not be caught by Knip.\n\nIssues reported by the linter are for you to handle and review.\n\nBesides those differences, Knip has a broader scope:\n\n- Improve DX (see [less is more][6]).\n- Unless using [production mode][5], also lint all source code like tests,\n  scripts and Storybook stories.\n- Handle more [types of issues][7] (such as unlisted dependencies).\n\n## Synergy\n\n### Why does Knip have plugins?\n\nPlugins are an essential part of Knip. They prevent you from a lot of\nconfiguration out of the box, by adding entry files as accurately as possible\nand only for the tools actually installed. Yet the real magic is in their custom\nparsers for configuration files and command-line argument definitions.\n\nFor instance, Vitest has the `environment` configuration option. The Vitest\nplugin knows `\"node\"` is the default value for `environment` which does not\nrequire an extra package, but will translate `\"edge-runtime\"` to the\n`@edge-runtime/vm` package. This allows Knip to report it if this package is not\nlisted in `package.json`, or when it is no longer used after changes in the\nVitest configuration.\n\nConfiguration files may also contain references to entry files. For instance,\nJest has `setupFilesAfterEnv: \"<rootDir>/jest.setup.js\"` or a reference may\npoint to a file in another workspace in the same monorepo, e.g.\n`setupFiles: ['@org/shared/jest-setup.ts']`. Those entry files may also contain\nimports of internal modules or external dependencies, and so on.\n\n### Why is Knip so heavily engineered?\n\nEven though a modular approach has its merits, for Knip it makes sense to have\nall the pieces in a single tool.\n\nBuilding up the module and dependency graphs requires more than standard module\nresolution and not only static but also dynamic analysis (i.e. actually load and\nexecute modules), such as for parsers of plugins to receive the exported value\nof dynamic tooling configuration files. Additionally, [exports consumed by\nexternal libraries][8] require type information. Last but not least, shell\nscript parsing is required to find the right entry files, configuration files\nand dependencies accurately.\n\nThe rippling effect of plugins and recursively adding entry files and\ndependencies to build up the graphs is also exactly what's meant by\n[\"comprehensive\" here][9].\n\n## Building the graphs\n\n### Where does Knip look for entry files?\n\n- In default locations such as `index.js` and `src/index.ts`\n- In `main`, `bin` and `exports` fields in `package.json`\n- In the entry files as configured by enabled plugins\n- In `config` files as configured and parsed by enabled plugins\n- The `config` files themselves are entry files\n- In dynamic imports (i.e. `require()` and `import()` calls)\n- In `require.resolve('./entry.js')`\n- In `import.meta.resolve('./entry.mjs')`\n- Through scripts inside template strings in source files such as:\n\n```ts\nawait $({ stdio: \"inherit\" })`c8 node hydrate.js`; // execa\nawait $`node scripts/parse.js`; // bun/zx\n```\n\n- Through scripts in `package.json` such as:\n\n```json\n{\n  \"name\": \"my-lib\",\n  \"scripts\": {\n    \"start\": \"node --import tsx/esm run.ts\",\n    \"start\": \"vitest -c config/vitest.config.ts\"\n  }\n}\n```\n\n- Through plugins handling CI workflow files like `.github/workflows/ci.yml`:\n\n```yaml\njobs:\n  test:\n    steps:\n      run: playwright test e2e/**/*.spec.ts --config playwright.e2e.config.ts\n      run: node --import tsx/esm run.ts\n```\n\nScripts like the ones shown here may also contain references to configuration\nfiles (`config/vitest.config.ts` and `playwright.e2e.config.ts` in the examples\nabove). They're recognized as configuration files and passed to their respective\nplugins, and may contain additional entry files.\n\nEntry files are added to the module graph. [Module resolution][10] might result\nin additional entry files recursively until no more entry files are found.\n\n### What does Knip look for in source files?\n\noxc-parser is powerful and fault-tolerant. Knip visits all nodes of the\ngenerated AST to find:\n\n- Imports and dynamic imports of internal modules and external dependencies\n- Exports\n- Accessed properties on namespace imports and re-exports to track individual\n  export usage\n- Calls to `require.resolve`, `import.meta.resolve` and more.\n- Scripts in template strings (passed to [script parser][11])\n\n### What's in the graphs?\n\nOnce the module and dependency graphs are created, they contain the information\nrequired to create the report including all issue types:\n\n- Unused files\n- Unused dependencies\n- Unused devDependencies\n- Referenced optional peerDependencies\n- Unlisted dependencies\n- Unlisted binaries\n- Unresolved imports\n- Unused exports\n- Unused exported types\n- Unused exported enum members\n- Unused exported namespace members\n- Duplicate exports\n\nAnd optionally more issue types like individual exports and exported types in\nnamespace imports.\n\nThe graphs allows to report more interesting details, such as:\n\n- Circular references\n- Usage numbers per export\n- Export usage across workspaces in a monorepo\n- List of all binaries used\n- List of all used (OS) binaries not installed in `node_modules`\n\n### Why doesn't Knip just read the lockfile?\n\nKnip reads the `package.json` file of each dependency. Most of the information\nrequired is in the lockfile as well, which would be more efficient. However,\nlockfiles lack some data, including:\n\n- It requires lockfile parsing for each lockfile format and version of each\n  package manager.\n- The lockfile doesn't contain whether the package [has types included][12].\n- The lockfile doesn't contain entry point fields (`main`, `module`, `exports`)\n  needed to resolve what a dependency actually exposes.\n- The lockfile doesn't contain `bin` entries to determine installed binaries.\n\n## Module Resolution\n\n### Why doesn't Knip use an existing module resolver?\n\nRuntimes like Node.js provide `require.resolve` and `import.meta.resolve`.\nTypeScript comes with module resolution built-in. More module resolvers are out\nthere and bundlers are known to use or come with module resolvers. None of them\nseem to meet all requirements to be usable on its own by Knip:\n\n- Support non-standard extensions like `.css`, `.svelte` and `.png`\n- Support path aliases\n- Support `exports` map in `package.json`\n- Support self-referencing imports\n- Rewire `package.json#main` build artifacts like `dist/module.js` to its source\n  at `src/module.ts`\n- Don't resolve to type definition paths like `module.d.ts` but source code at\n  `module.js`\n\nA few strategies have been tried and tweaked, and Knip currently uses\n[oxc-resolver][13] with customizations for extension aliases, path aliases and\nTypeScript-style resolution. This covers module resolution across all\nworkspaces, [script parsing][11] and references to files in other workspaces.\n\n### How does Knip handle non-standard import syntax?\n\nKnip tries to be resilient against import syntax like what's used by e.g.\nwebpack loaders or Vite asset imports. Knip strips off the prefixes and suffixes\nin import specifiers like this:\n\n```ts title=\"component.ts\"\nimport Icon from \"./icon.svg?raw\";\nimport Styles from \"-!style-loader!css-loader?modules!./styles.css\";\n```\n\nIn this example, the `style-loader` and `css-loader` dependencies should be\ndependencies found in webpack configuration, handled by Knip's webpack plugin.\n\n## TypeScript\n\n### What's the difference between workspaces, projects and programs?\n\nA workspace is a directory with a `package.json` file. They're configured in\n`package.json#workspaces` (or `pnpm-workspaces.yml`). In case a directory has a\n`package.json` file, but is not a workspace (from a package manager\nperspective), it can be added as a workspace to the Knip configuration.\n\nProjects - in the context of TypeScript - are directories with a `tsconfig.json`\nfile. They're not a concept in Knip.\n\nKnip analyzes all workspaces using a single module graph with a shared module\nresolver.\n\n### Why doesn't Knip match my TypeScript project structure?\n\nRepositories and workspaces in a monorepo aren't necessarily structured like\nTypeScript projects. Put simply, the location of `package.json` files isn't\nalways adjacent to `tsconfig.json` files. Knip follows the structure of\nworkspaces in a monorepo.\n\nAn additional layering of TypeScript projects would complicate things. The\ndownside is that a `tsconfig.json` file not used by Knip may have conflicting\nmodule resolution settings, potentially resulting in missed files.\n\nIn practice, this is rarely an issue. Knip sticks to the workspaces structure\nand installs a single \"kitchen sink\" module resolver function per workspace.\nDifferent strategies might add more complexity and performance penalties, while\nthe current strategy is simple, fast and good enough.\n\nNote that any directory with a `package.json` not listed in the root\n`package.json#workspaces` can be added to the Knip configuration manually to\nhave it handled as a separate workspace.\n\n### Why doesn't Knip just use `ts.findReferences`?\n\nTypeScript has a very good \"Find references\" feature, that you might be using in\nyour IDE as well. There are a few reasons Knip doesn't use it:\n\n- It requires the full TypeScript language service, which is heavy to\n  initialize.\n- It must be called per symbol. A project with thousands of exports would need\n  thousands of calls, each scanning potentially all files. Knip parses each file\n  once and resolves all export usages from the resulting graph.\n- It operates within a single TypeScript program. Monorepos with multiple\n  `tsconfig.json` files would need separate language service instances.\n- It cannot see into non-standard files like `.vue`, `.svelte` and `.astro`.\n\nKnip's module graph is also serializable and cacheable, and usable for other\ntools to build upon.\n\n### Why can't I use path aliases to reference other workspaces?\n\nProjects can use `compilerOptions.paths` to alias paths in other workspaces in\nthe same monorepo. Knip doesn't understand those paths might represent internal\nworkspaces and might report false positives.\n\nThe recommendation and best practice is to list such workspaces/dependencies in\n`package.json`, and import them as such. Other tooling should not have any\nissues with this standard approach either.\n\nAlso see the example in [TypeScript path aliases in monorepos][14].\n\n### What's up with that configurable `tsconfig.json` location?\n\nThere's a difference between `--tsConfig [file]` as a CLI argument and the\n`typescript.config` option in Knip configuration.\n\nThe [`--tsConfig [file]` option][15] is used to provide an alternative location\nfor the default root `tsconfig.json` file. Relevant `compilerOptions` include\n`paths` and `moduleResolution`. This setting is only available at the root\nlevel.\n\nOn the other hand, the [`config` option of the plugin][16] can be set per\nworkspace. The TypeScript plugin extracts referenced external dependencies such\nas those in `extends`, `compilerOptions.types` and JSX settings:\n\n```json title=\"tsconfig.json\"\n{\n  \"extends\": \"@tsconfig/node20/tsconfig.json\",\n  \"compilerOptions\": {\n    \"jsxImportSource\": \"hastscript/svg\"\n  }\n}\n```\n\nFrom this example, Knip can determine whether the `@tsconfig/node20` and\n`hastscript` dependencies are properly listed in `package.json`.\n\n#### Notes\n\n- The TypeScript plugin doesn't add support for TypeScript to Knip (that's\n  already built-in). Like other plugins, it extracts dependencies from\n  `tsconfig.json`. With the `typescript.config` option an alternative location\n  for `tsconfig.json` can be set per workspace.\n- In case path aliases from `compilerOptions.paths` aren't picked up by Knip,\n  either use `--tsConfig [file]` to target a different `tsconfig.json`, or\n  manually add [paths][17] to the Knip configuration. The latter can be done per\n  workspace.\n\n## Compilers\n\n### How does Knip handle Svelte or Astro files?\n\nTo further increase the coverage of the module graph, non-standard files other\nthan JavaScript and TypeScript modules should be included as well. For instance,\n`.mdx` and `.astro` files can import each other, internal modules and external\ndependencies.\n\nKnip includes basic \"compilers\" for a few common file types (Astro, MDX, Svelte,\nVue). These are lightweight regex-based extractors, not actual compilers. You\ncan override the built-in compilers with your project's actual compiler, and add\nadditional ones for other file types.\n\n### Why are the exports of my `.vue` files not used?\n\nKnip comes with basic \"compilers\" for a few common non-standard file types.\nThey're not actual compilers, they're regular expressions only to extract import\nstatements. Override the built-in Vue \"compiler\" with the real one in your\nproject. Also see the answer to the previous question and [Compilers][18].\n\n## Miscellaneous\n\n### Why isn't production mode the default?\n\nThe default mode of Knip includes all source files, tests, dependencies, dev\ndependencies and tooling configuration.\n\nOn the other hand, production mode considers only source files and production\ndependencies. Plugins add only production entry files.\n\nWhich mode should've been the default? They both have their merits:\n\n- Production mode catches dead production code and dependencies. This mode has\n  the most impact on UX, since less code tends to be faster and safer.\n- Default mode potentially catches more issues, e.g. lots of unused plugins of\n  tooling, including most issues found in production mode. This mode has the\n  most impact on DX, for the same reason.\n\nAlso see [production mode][5].\n\n### Why doesn't Knip have...?\n\nExamples of features that have been requested include:\n\n- Expose programmatic API\n- Add local/custom plugins\n- Expose the module and dependency graphs\n- Custom AST visitors, e.g. to find and return:\n  - Unused interface/type members\n  - Unused object members (and e.g. React component props)\n  - Unused object props in function return values\n- Analyze workspaces in parallel\n- Support Deno\n- Improve internal code structures and accessibility to support contributions\n\nThese are all interesting ideas, but most increase the API surface area, and all\nrequire more development efforts and maintenance. Time is limited and\n[sponsorships][19] currently don't cover - this can change though!\n\n[1]: ../reference/integrations.md#mcp-server\n[2]: ../reference/integrations.md#vs-code-extension\n[3]: ../guides/configuring-project-files.md\n[4]: ../reference/configuration.md#ignoreexportsusedinfile\n[5]: ../features/production-mode.md\n[6]: ../explanations/why-use-knip.md#less-is-more\n[7]: ./issue-types.md\n[8]: ../guides/handling-issues.mdx#external-libraries\n[9]: ../explanations/why-use-knip.md#comprehensive\n[10]: #module-resolution\n[11]: ../features/script-parser.md\n[12]: ../guides/handling-issues.mdx#type-definition-packages\n[13]: https://oxc.rs/docs/guide/usage/resolver.html\n[14]: ../guides/handling-issues.mdx#typescript-path-aliases-in-monorepos\n[15]: ../reference/cli.md#--tsconfig-file\n[16]: ../explanations/plugins.md#configuration-files\n[17]: ../reference/configuration.md#paths\n[18]: ../features/compilers.md\n[19]: /sponsors\n"
  },
  {
    "path": "packages/docs/src/content/docs/reference/integrations.md",
    "content": "---\ntitle: Integrations\n---\n\n- [VS Code/VSX Extension][1]\n- [JetBrains Plugin][2]\n- [MCP Server][3]\n- [Language Server][4]\n\n## VS Code Extension\n\nThe official VS Code extension provides a rich integration with the [Knip\nLanguage Server][4]:\n\n- **Diagnostics**: Inline warnings for unused dependencies, exports and files\n- **Hover Information**: Hover over exports to see import and usage locations\n- **Imports Tree View**: Direct links to implementations\n- **Exports Tree View**: Direct links to import and usage locations\n- **Contention Detection**: Warnings for circular dependencies, conflicts and\n  branched import chains\n- **Built-in MCP Server**: Automated configuration support for coding agents\n\nFind [Knip on the VS Code Marketplace][5] and find [Knip in the Open VSX\nRegistry][6].\n\nSee below for [screenshots][7].\n\n## JetBrains Plugin\n\nA community plugin is available for JetBrains IDEs including WebStorm, IntelliJ\nIDEA, and others. The plugin is powered by the [Knip Language Server][4] and\nprovides diagnostics. Find [Knip on the JetBrains Marketplace][8].\n\n## MCP Server\n\nThe standalone MCP Server enables coding agents to configure Knip automatically.\nTell your agent to \"configure knip\" and it will use the available tools to\ncreate, analyze and optimize your `knip.json` configuration.\n\nThe [Knip MCP Server][9] is available separately and built into the [Knip VS\nCode Extension][1].\n\nStart:\n\n```sh\nnpx @knip/mcp\n```\n\nNote: The VS Code extension has this MCP Server built-in.\n\n## Language Server\n\nThe IDE integrations are powered by the Knip Language Server. It builds the full\nmodule graph of your project and provides a session with a graph explorer. See\nthe [Knip Language Server documentation][10] for more details and information on\nintegrating Knip.\n\n## VS Code Extension Screenshots\n\n### Lint Findings\n\n![Lint Findings][11]\n\n### Imports & Exports\n\n![Hover over imports and exports][12]\n\n### Contention\n\nThe IDE extension shows extra issues in the tree views like circular\ndependencies.\n\n#### Circular Dependencies\n\n![Circular Dependencies][13]\n\n#### Conflicts\n\n![Conflicts][14]\n\n#### Branching\n\n![Branching][15]\n\n### Settings\n\n![VS Code Extension Settings][16]\n\n[1]: #vs-code-extension\n[2]: #jetbrains-plugin\n[3]: #mcp-server\n[4]: #language-server\n[5]: https://marketplace.visualstudio.com/items?itemName=webpro.vscode-knip\n[6]: https://open-vsx.org/extension/webpro/vscode-knip\n[7]: #vs-code-extension-screenshots\n[8]: https://plugins.jetbrains.com/plugin/29765-knip\n[9]: https://www.npmjs.com/package/@knip/mcp\n[10]: https://github.com/webpro-nl/knip/blob/main/packages/language-server/README.md\n[11]: /screenshots/editors-and-agents/diagnostics.webp\n[12]: /screenshots/editors-and-agents/imports-exports.webp\n[13]: /screenshots/editors-and-agents/circular-dependency.webp\n[14]: /screenshots/editors-and-agents/conflict.webp\n[15]: /screenshots/editors-and-agents/branch.webp\n[16]: /screenshots/editors-and-agents/vscode-extension-settings.webp\n"
  },
  {
    "path": "packages/docs/src/content/docs/reference/issue-types.md",
    "content": "---\ntitle: Issue Types\ntableOfContents: false\n---\n\nKnip reports the following types of issues:\n\n| Title                                | Description                                                |       | Key                |\n| :----------------------------------- | :--------------------------------------------------------- | ----- | :----------------- |\n| Unused files                         | Unable to find a reference to this file                    | 🔧    | `files`            |\n| Unused dependencies                  | Unable to find a reference to this dependency              | 🔧    | `dependencies`     |\n| Unused devDependencies               | Unable to find a reference to this devDependency           | 🔧    | `dependencies`     |\n| Referenced optional peerDependencies | Optional peer dependency is referenced                     |       | `dependencies`     |\n| Unlisted dependencies                | Used dependencies not listed in package.json               |       | `unlisted`         |\n| Unlisted binaries                    | Binaries from dependencies not listed in package.json      |       | `binaries`         |\n| Unused catalog entries               | Unable to find a reference to this catalog entry           | 🔧    | `catalog`          |\n| Unresolved imports                   | Unable to resolve this (import) specifier                  |       | `unresolved`       |\n| Unused exports                       | Unable to find a reference to this export                  | 🔧    | `exports`          |\n| Unused exported types                | Unable to find a reference to this exported type           | 🔧    | `types`            |\n| Exports in used namespace            | Namespace with export is referenced, but not export itself | 🔧 🟠 | `nsExports`        |\n| Exported types in used namespace     | Namespace with type is referenced, but not type itself     | 🔧 🟠 | `nsTypes`          |\n| Unused exported enum members         | Unable to find a reference to this enum member             | 🔧    | `enumMembers`      |\n| Unused exported namespace members    | Unable to find a reference to this namespace member        | 🔧    | `namespaceMembers` |\n| Duplicate exports                    | This is exported more than once                            |       | `duplicates`       |\n\n## Legend\n\n|     | Description                                         |\n| --- | :-------------------------------------------------- |\n| 🔧  | [Auto-fixable][1] issue types                       |\n| 🟠  | Not included by default (include with [filters][2]) |\n\n## Notes\n\n- When an issue type has zero issues, it is not shown.\n- Including or excluding `dependencies` (via CLI or configuration) automatically\n  includes or excludes `devDependencies` and `optionalPeerDependencies`. In\n  [rules][3], each key can be set individually.\n- In [strict production mode][4], `devDependencies` are not included.\n- The `types` issue type includes `enum`, `interface` and `type` exports.\n\n[1]: ../features/auto-fix.mdx\n[2]: ../features/rules-and-filters.md#filters\n[3]: ../features/rules-and-filters.md#rules\n[4]: ../features/production-mode.md#strict-mode\n"
  },
  {
    "path": "packages/docs/src/content/docs/reference/jsdoc-tsdoc-tags.md",
    "content": "---\ntitle: JSDoc & TSDoc Tags\n---\n\nJSDoc or TSDoc tags can be used to make exceptions for unused or duplicate\nexports.\n\nKnip tries to minimize configuration and introduces no new syntax. That's why it\nhooks into JSDoc and TSDoc tags.\n\n:::caution\n\nAdding tags or excluding a certain type of issues from the report is usually not\nrecommended. It hides issues, which is often a sign of code smell or ambiguity\nand ends up harder to maintain. It's usually better to refactor the code (or\nreport an issue with Knip for false positives).\n\n:::\n\nJSDoc comments always start with `/**` (not `//`) and can be single or\nmulti-line.\n\n## Tags\n\nUse arbitrary [tags][1] to exclude or include tagged exports from the report.\nExample:\n\n```ts\n/** @lintignore */\nexport const myUnusedExport = 1;\n\n/** @lintignore */\nimport Unresolved from './generated/lib.js';\n```\n\nAnd then include (`+`) or exclude (`-`) these tagged exports from the report\nlike so:\n\n```shell\nknip --tags=-lintignore,-internal\n```\n\nTags can also be [configured in `knip.json`][2].\n\nWhen an excluded tag is no longer necessary because the export is actually used,\nKnip reports a **tag hint**:\n\n```sh\nTag hints (1)\nignored  unimported.ts  Unused tag in unimported.ts: ignored → @custom\n```\n\nThis helps you clean up tags that were added to suppress issues that have since\nbeen resolved.\n\n## `@public`\n\nBy default, Knip reports unused exports in non-entry files.\n\nTag the export as `@public` and Knip will not report it.\n\nExample:\n\n```ts\n/**\n * @public\n */\nexport const unusedFunction = () => {};\n```\n\nThis tag can also be used to make exceptions in entry files when using\n[--include-entry-exports][3].\n\n[JSDoc: @public][4] and [TSDoc: @public][5]\n\n## `@internal`\n\nInternal exports are not meant for public consumption, but only for internal\nusage such as tests. This means they would be reported in [production mode][6].\n\nMark the export with `@internal` and Knip will not report the export in\nproduction mode.\n\nExample:\n\n```ts\n/** @internal */\nexport const internalTestedFunction = () => {};\n```\n\nIn general it's not recommended to expose and test implementation details, but\nexceptions are possible. Those should not be reported as false positives, so\nwhen using production mode you'll need to help Knip out by tagging them as\n`@internal`.\n\n[TSDoc: @internal][7]\n\n## `@alias`\n\nKnip reports duplicate exports. To prevent this, tag one of the exports as\n`@alias`.\n\nExample:\n\n```ts\nexport const Component = () => {};\n\n/** @alias */\nexport default Component;\n```\n\nAn alternative solution is to use `--exclude duplicates` and exclude all\nduplicates from being reported.\n\n[JSDoc: @alias][8]\n\n## `@beta`\n\nWorks identical to [`@public`][9]. Knip ignores other tags like `@alpha` and\n`@experimental`.\n\n[TSDoc: @beta][10]\n\n[1]: ../reference/cli.md#--tags\n[2]: ./configuration.md#tags\n[3]: ./cli.md#--include-entry-exports\n[4]: https://jsdoc.app/tags-public.html\n[5]: https://tsdoc.org/pages/tags/public/\n[6]: ../features/production-mode.md\n[7]: https://tsdoc.org/pages/tags/internal/\n[8]: https://jsdoc.app/tags-alias.html\n[9]: #public\n[10]: https://tsdoc.org/pages/tags/beta/\n"
  },
  {
    "path": "packages/docs/src/content/docs/reference/known-issues.md",
    "content": "---\ntitle: Known Issues\n---\n\nList of known issues and workarounds for exceptions thrown during a Knip run.\n\nSee [handling issues][1] to learn more about dealing with lint issues.\n\n## Exceptions from config files\n\nAn exception may be thrown when a Knip plugin loads a JavaScript or TypeScript\nconfiguration file such as `webpack.config.js` or `vite.config.ts`:\n\n```sh\n$ knip\nError loading .../vite.config.ts\n```\n\nKnip may load such files differently, in a different environment, with missing\nenvironment variables, missing path aliases, etcetera. Use `--debug` to locate\nthe cause of the issue with more details.\n\nPotential workarounds:\n\n- [Set path aliases][2] for \"Cannot find module\" errors\n- Set missing environment variable(s), potential solutions:\n  - Use a helper package like [dotenvx][3]\n  - `KEY=VAL knip`\n  - `node --env-file .env $(which knip)`\n- Disable loading the file by overriding the default `config` for that plugin.\n  - Example: `vite: { config: [] }`\n  - In a monorepo, be more specific like so:\n    `workspaces: { \"packages/lib\": { vite: { config: [] } } }`\n  - If this helps, add the file as an `entry` file for static analysis.\n- Disable the related plugin.\n  - Example: `eslint: false`\n  - In a monorepo, be more specific like so:\n    `workspaces: { \"packages/lib\": { eslint: false } }`\n  - If this helps, add the file as an `entry` file for static analysis.\n- As a last resort, ignore the workspace: `ignoreWorkspaces: [\"packages/lib\"]`.\n\n## Path aliases in config files\n\nLoading the configuration file (e.g. `cypress.config.ts`) for one of Knip's\nplugins may give an error:\n\n```sh\n$ knip\nAnalyzing workspace ....\nError loading .../cypress.config.ts\nReason: Cannot find module '@alias/name'\nRequire stack:\n- .../cypress.config.ts\n```\n\nSome tools (such as Cypress and Jest) support using TypeScript path aliases in\nthe configuration file.\n\nPotential workarounds:\n\n- Rewrite the import in the configuration file to a relative import.\n- Inject support with a module like `tsx`: `NODE_OPTIONS=\"--import tsx\" knip`\n- Or `tsconfig-paths`: `NODE_OPTIONS=\"--import tsconfig-paths/register.js\" knip`\n- Use Bun with [knip-bun][4].\n- See [exceptions from config files][5] for more potential workarounds.\n\n## Nx Daemon\n\nIn Nx projects you might encounter this error:\n\n```sh\nNX   Daemon process terminated and closed the connection\n```\n\nThe solution is to [disable the Nx Daemon][6]:\n\n```sh\nNX_DAEMON=false knip\n```\n\n[1]: ../guides/handling-issues.mdx\n[2]: #path-aliases-in-config-files\n[3]: https://dotenvx.com/\n[4]: ./cli.md#knip-bun\n[5]: #exceptions-from-config-files\n[6]: https://nx.dev/concepts/nx-daemon#turning-it-off\n"
  },
  {
    "path": "packages/docs/src/content/docs/reference/plugins/.gitkeep",
    "content": ""
  },
  {
    "path": "packages/docs/src/content/docs/reference/plugins.md",
    "content": "---\ntitle: Plugins (137)\ntableOfContents: false\n---\n\n:::section{.columns.min200}\n- [Angular](/reference/plugins/angular \"Angular\")\n- [Astro](/reference/plugins/astro \"Astro\")\n- [Astro DB](/reference/plugins/astro-db \"Astro DB\")\n- [astro-og-canvas](/reference/plugins/astro-og-canvas \"astro-og-canvas\")\n- [Ava](/reference/plugins/ava \"Ava\")\n- [Babel](/reference/plugins/babel \"Babel\")\n- [Biome](/reference/plugins/biome \"Biome\")\n- [bumpp](/reference/plugins/bumpp \"bumpp\")\n- [Bun](/reference/plugins/bun \"Bun\")\n- [c8](/reference/plugins/c8 \"c8\")\n- [Capacitor](/reference/plugins/capacitor \"Capacitor\")\n- [Changelogen](/reference/plugins/changelogen \"Changelogen\")\n- [Changelogithub](/reference/plugins/changelogithub \"Changelogithub\")\n- [Changesets](/reference/plugins/changesets \"Changesets\")\n- [Commitizen](/reference/plugins/commitizen \"Commitizen\")\n- [commitlint](/reference/plugins/commitlint \"commitlint\")\n- [Convex](/reference/plugins/convex \"Convex\")\n- [create-typescript-app](/reference/plugins/create-typescript-app \"create-typescript-app\")\n- [CSpell](/reference/plugins/cspell \"CSpell\")\n- [Cucumber](/reference/plugins/cucumber \"Cucumber\")\n- [Cypress](/reference/plugins/cypress \"Cypress\")\n- [Danger](/reference/plugins/danger \"Danger\")\n- [dependency-cruiser](/reference/plugins/dependency-cruiser \"dependency-cruiser\")\n- [Docusaurus](/reference/plugins/docusaurus \"Docusaurus\")\n- [dotenv](/reference/plugins/dotenv \"dotenv\")\n- [Drizzle](/reference/plugins/drizzle \"Drizzle\")\n- [Eleventy](/reference/plugins/eleventy \"Eleventy\")\n- [ESLint](/reference/plugins/eslint \"ESLint\")\n- [execa](/reference/plugins/execa \"execa\")\n- [Expo](/reference/plugins/expo \"Expo\")\n- [Expressive Code](/reference/plugins/expressive-code \"Expressive Code\")\n- [Gatsby](/reference/plugins/gatsby \"Gatsby\")\n- [GitHub Action](/reference/plugins/github-action \"GitHub Action\")\n- [GitHub Actions](/reference/plugins/github-actions \"GitHub Actions\")\n- [glob](/reference/plugins/glob \"glob\")\n- [GraphQL Codegen](/reference/plugins/graphql-codegen \"GraphQL Codegen\")\n- [Hardhat](/reference/plugins/hardhat \"Hardhat\")\n- [husky](/reference/plugins/husky \"husky\")\n- [i18next Parser](/reference/plugins/i18next-parser \"i18next Parser\")\n- [Jest](/reference/plugins/jest \"Jest\")\n- [Karma](/reference/plugins/karma \"Karma\")\n- [Knex](/reference/plugins/knex \"Knex\")\n- [Ladle](/reference/plugins/ladle \"Ladle\")\n- [Lefthook](/reference/plugins/lefthook \"Lefthook\")\n- [lint-staged](/reference/plugins/lint-staged \"lint-staged\")\n- [LintHTML](/reference/plugins/linthtml \"LintHTML\")\n- [lockfile-lint](/reference/plugins/lockfile-lint \"lockfile-lint\")\n- [Lost Pixel](/reference/plugins/lost-pixel \"Lost Pixel\")\n- [markdownlint](/reference/plugins/markdownlint \"markdownlint\")\n- [MDX](/reference/plugins/mdx \"MDX\")\n- [mdxlint](/reference/plugins/mdxlint \"mdxlint\")\n- [Metro](/reference/plugins/metro \"Metro\")\n- [Mocha](/reference/plugins/mocha \"Mocha\")\n- [moonrepo](/reference/plugins/moonrepo \"moonrepo\")\n- [Mock Service Worker](/reference/plugins/msw \"Mock Service Worker\")\n- [Nano Staged](/reference/plugins/nano-staged \"Nano Staged\")\n- [Nest](/reference/plugins/nest \"Nest\")\n- [Netlify](/reference/plugins/netlify \"Netlify\")\n- [Next.js](/reference/plugins/next \"Next.js\")\n- [next-intl](/reference/plugins/next-intl \"next-intl\")\n- [Next.js MDX](/reference/plugins/next-mdx \"Next.js MDX\")\n- [Nitro](/reference/plugins/nitro \"Nitro\")\n- [Node.js](/reference/plugins/node \"Node.js\")\n- [node-modules-inspector](/reference/plugins/node-modules-inspector \"node-modules-inspector\")\n- [nodemon](/reference/plugins/nodemon \"nodemon\")\n- [npm-package-json-lint](/reference/plugins/npm-package-json-lint \"npm-package-json-lint\")\n- [Nuxt](/reference/plugins/nuxt \"Nuxt\")\n- [Nx](/reference/plugins/nx \"Nx\")\n- [nyc](/reference/plugins/nyc \"nyc\")\n- [oclif](/reference/plugins/oclif \"oclif\")\n- [Oxlint](/reference/plugins/oxlint \"Oxlint\")\n- [Parcel](/reference/plugins/parcel \"Parcel\")\n- [Payload CMS](/reference/plugins/payload \"Payload CMS\")\n- [Playwright](/reference/plugins/playwright \"Playwright\")\n- [Playwright for components](/reference/plugins/playwright-ct \"Playwright for components\")\n- [playwright-test](/reference/plugins/playwright-test \"playwright-test\")\n- [Plop](/reference/plugins/plop \"Plop\")\n- [pm2](/reference/plugins/pm2 \"pm2\")\n- [pnpm](/reference/plugins/pnpm \"pnpm\")\n- [PostCSS](/reference/plugins/postcss \"PostCSS\")\n- [Preconstruct](/reference/plugins/preconstruct \"Preconstruct\")\n- [Prettier](/reference/plugins/prettier \"Prettier\")\n- [Prisma](/reference/plugins/prisma \"Prisma\")\n- [Qwik](/reference/plugins/qwik \"Qwik\")\n- [React Cosmos](/reference/plugins/react-cosmos \"React Cosmos\")\n- [React Native](/reference/plugins/react-native \"React Native\")\n- [React Router](/reference/plugins/react-router \"React Router\")\n- [Relay](/reference/plugins/relay \"Relay\")\n- [Release It!](/reference/plugins/release-it \"Release It!\")\n- [Remark](/reference/plugins/remark \"Remark\")\n- [Remix](/reference/plugins/remix \"Remix\")\n- [Rollup](/reference/plugins/rollup \"Rollup\")\n- [Rsbuild](/reference/plugins/rsbuild \"Rsbuild\")\n- [Rslib](/reference/plugins/rslib \"Rslib\")\n- [Rspack](/reference/plugins/rspack \"Rspack\")\n- [Rstest](/reference/plugins/rstest \"Rstest\")\n- [Semantic Release](/reference/plugins/semantic-release \"Semantic Release\")\n- [Sentry](/reference/plugins/sentry \"Sentry\")\n- [simple-git-hooks](/reference/plugins/simple-git-hooks \"simple-git-hooks\")\n- [size-limit](/reference/plugins/size-limit \"size-limit\")\n- [SST](/reference/plugins/sst \"SST\")\n- [Starlight](/reference/plugins/starlight \"Starlight\")\n- [Storybook](/reference/plugins/storybook \"Storybook\")\n- [Stryker](/reference/plugins/stryker \"Stryker\")\n- [Stylelint](/reference/plugins/stylelint \"Stylelint\")\n- [Svelte](/reference/plugins/svelte \"Svelte\")\n- [SvelteKit](/reference/plugins/sveltekit \"SvelteKit\")\n- [SVGO](/reference/plugins/svgo \"SVGO\")\n- [SVGR](/reference/plugins/svgr \"SVGR\")\n- [SWC](/reference/plugins/swc \"SWC\")\n- [Syncpack](/reference/plugins/syncpack \"Syncpack\")\n- [Tailwind](/reference/plugins/tailwind \"Tailwind\")\n- [TanStack Router](/reference/plugins/tanstack-router \"TanStack Router\")\n- [Taskfile](/reference/plugins/taskfile \"Taskfile\")\n- [Travis CI](/reference/plugins/travis \"Travis CI\")\n- [ts-node](/reference/plugins/ts-node \"ts-node\")\n- [tsdown](/reference/plugins/tsdown \"tsdown\")\n- [tsup](/reference/plugins/tsup \"tsup\")\n- [tsx](/reference/plugins/tsx \"tsx\")\n- [TypeDoc](/reference/plugins/typedoc \"TypeDoc\")\n- [TypeScript](/reference/plugins/typescript \"TypeScript\")\n- [unbuild](/reference/plugins/unbuild \"unbuild\")\n- [UnoCSS](/reference/plugins/unocss \"UnoCSS\")\n- [Vercel OG](/reference/plugins/vercel-og \"Vercel OG\")\n- [Vike](/reference/plugins/vike \"Vike\")\n- [Vite](/reference/plugins/vite \"Vite\")\n- [VitePress](/reference/plugins/vitepress \"VitePress\")\n- [Vitest](/reference/plugins/vitest \"Vitest\")\n- [Vue](/reference/plugins/vue \"Vue\")\n- [WebdriverIO](/reference/plugins/webdriver-io \"WebdriverIO\")\n- [webpack](/reference/plugins/webpack \"webpack\")\n- [Wireit](/reference/plugins/wireit \"Wireit\")\n- [Wrangler](/reference/plugins/wrangler \"Wrangler\")\n- [xo](/reference/plugins/xo \"xo\")\n- [Yarn](/reference/plugins/yarn \"Yarn\")\n- [yorkie](/reference/plugins/yorkie \"yorkie\")\n- [zx](/reference/plugins/zx \"zx\")\n\n\n:::\n"
  },
  {
    "path": "packages/docs/src/content/docs/reference/related-tooling.md",
    "content": "---\ntitle: Related Tooling\n---\n\nThis is an overview of related tooling for features Knip does not support.\n\n## Unused imports & variables\n\nKnip doesn't look for unused imports and variables within a file. The focus is\non exported values and types across files.\n\nUse [ESLint][1], [Biome][2] or [oxlint][3] to find unused variables within\nfiles.\n\nUse [remove-unused-vars][4] to remove unused code within files, but in a more\nvaliant way. Using input from any of the above linters, it actually removes a\nlot more unused code. This pairs great with Knip.\n\n## Unused properties\n\nKnip does not yet support finding unused members of types, interfaces and\nobjects. This includes returned objects from exported functions and objects\npassed as React component props.\n\nKnip does support finding unused members of enums and classes, and exported\nvalues and types on imported namespaces.\n\n## Circular dependencies\n\nKnip has no issues with circular dependencies, and does not report them. Tools\nthat do support this include [DPDM][5], [Madge][6] and [skott][7].\n\n## Cleanup\n\nThe [e18e.dev][8] website and in particular the [Cleanup][9] section is a great\nresource when dealing with technical debt.\n\n[1]: https://eslint.org\n[2]: https://biomejs.dev/linter/\n[3]: https://oxc.rs/docs/guide/usage/linter.html\n[4]: https://github.com/webpro-nl/remove-unused-vars\n[5]: https://github.com/acrazing/dpdm\n[6]: https://github.com/pahen/madge\n[7]: https://github.com/antoine-coulon/skott\n[8]: https://e18e.dev\n[9]: https://e18e.dev/guide/cleanup.html\n"
  },
  {
    "path": "packages/docs/src/content/docs/sponsors.mdx",
    "content": "---\ntitle: 'Become a sponsor!'\ntemplate: splash\n---\n\nimport StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';\nimport Posts from '../../components/Posts.astro';\nimport Projects from '../../components/Projects.astro';\nimport SponsorsComponent from '../../components/Sponsors.astro';\nimport SponsorsChart from '../../components/SponsorsChart.astro';\n\n:::article{.prose}\n\nKnip is a powerful tool that helps thousands of developers keep their JavaScript\nand TypeScript projects lean and maintainable. Knip saves teams valuable time\nand resources.\n\nBuilding and maintaining Knip has been, and remains, a lot of work. Supportive\nresources allow me to focus on maintenance and development of Knip, which is\n[relied upon][1] [by many][2] in the JavaScript community. Please consider\nsupporting the project! The following platforms are available to support Knip:\n\n- [GitHub Sponsors][3]\n- [OpenCollective][4]\n\nYou or your company logo with a backlink to your website will be added to this\npage if you decide to support the project on an ongoing basis. Eternal gratitude\nto the companies and people supporting the project!\n\n:::\n\n<SponsorsComponent showAll={true} />\n\n## Monthly Overview\n\nOverview of gross GitHub and OpenCollective sponsorships and invoices, starting\nfrom the moment the project openly asked for support. Actual development started\none year before that.\n\n<SponsorsChart />\n\n- The monthly aggregated average over the charted period is $513.\n- The monthly recurring average without one-time payments is $350.\n- GitHub Sponsors is for my GitHub account. This has more repositories, but in\n  practice targets mostly Knip (and perhaps some [release-it][5]).\n- [Chart generated][6] using [Venz][7].\n\n## Trusted by the world's best software teams\n\n<section class=\"columns min300\">\n  <Projects />\n</section>\n\n## Don't just take our word for it\n\n<Posts />\n\n[1]: #trusted-by-the-worlds-best-software-teams\n[2]: https://github.com/webpro-nl/knip/network/dependents\n[3]: https://github.com/sponsors/webpro\n[4]: https://opencollective.com/knip\n[5]: https://github.com/release-it/release-it\n[6]: https://try.venz.dev?type=pivot&lp=tr&br=0&labelX=month&labelY=amount+\\($\\)&l=GitHub+Sponsors&color=%23fbfbfb&l=Open+Collective&color=%232487ff&label=2023-11&data=9,0&label=2023-12&data=74,0&label=2024-01&data=239,0&label=2024-02&data=489,0&label=2024-03&data=189,0&label=2024-04&data=204,750&label=2024-05&data=304,101&label=2024-06&data=1204,0&label=2024-07&data=204,35&label=2024-08&data=229,25&label=2024-09&data=2729,25&label=2024-10&data=410,45&label=2024-11&data=222,45&label=2024-12&data=222,145&label=2025-01&data=342,145&label=2025-02&data=244,145&label=2025-03&data=222,145&label=2025-04&data=222,145&label=2025-05&data=242,145&label=2025-06&data=247,145&label=2025-07&data=297,145&label=2025-08&data=268,145&label=2025-09&data=350,145&label=2025-10&data=375,145&label=2025-11&data=549,145&label=2025-12&data=355,145\n[7]: https://try.venz.dev\n"
  },
  {
    "path": "packages/docs/src/content/docs/typescript/unused-dependencies.md",
    "content": "---\ntitle: Unused dependencies\ndescription: Find and remove unused dependencies with Knip\nprev: false\nnext: false\n---\n\nOne of Knip's core features is finding unused dependencies in your JavaScript\nand TypeScript projects. And it comes with many more features to remove clutter\nand keep your projects in great shape.\n\n## Why are unused dependencies a problem?\n\nHaving unused dependencies in your `package.json` is an issue for various\nreasons:\n\n- They might end up in the final production bundle, increasing size and load\n  times for end users.\n- They waste space in `node_modules` and add to the installation time of the\n  project.\n- They slow down tooling such as linters and bundlers that analyze dependencies.\n- They are confusing and noisy in `package.json`.\n- They cause unnecessary extra work when managing and upgrading dependencies.\n- They can cause version conflicts with other dependencies in use.\n- They can cause false security alerts.\n- They might have restrictive licenses and make your project subject to theirs.\n- They usually come with transitive dependencies that have the same issues.\n\n## How do I find unused dependencies?\n\nUse Knip to find and remove unused dependencies. It also finds dependencies that\nare missing in `package.json` and has a lot more features to keep your\nJavaScript and TypeScript projects tidy.\n\nIt's easy to [get started][1] and make package management easier and more fun!\n\n<div style=\"display: flex; justify-content: center; margin: 4rem auto;\">\n  <img src=\"/logo.svg\" alt=\"Logo of Knip, to find unused dependencies, exports and files\" class=\"logo-border\" />\n</div>\n\n## How does Knip identify unused dependencies?\n\nKnip works by analyzing `package.json` files, source code and configuration\nfiles for other tooling in the project to find unused and missing dependencies.\nKnip has many heuristics, [plugins][2] and [compilers][3] to fully automate the\nprocess.\n\n## Can Knip remove unused dependencies?\n\nYes, Knip can automatically remove unused dependencies installed by a package\nmanager like npm or pnpm for you. Add the `--fix` argument to [auto-fix][4] and\nremove unused dependencies from `package.json`.\n\n## Can Knip detect missing dependencies?\n\nYes, Knip detects missing dependencies. It analyzes `package.json` files, and\nreports packages that are missing. They should be added to `package.json` to\navoid relying on transitive dependencies that can cause version mismatches and\nbreakage.\n\n## Does Knip work with monorepos?\n\nYes, Knip has first-class support for [monorepos and workspaces][5]. It analyzes\nall workspaces in the project and understands their relationship.\n\nFor instance, if a dependency is listed in the root `package.json` it does not\nneed to be listed in other workspaces. Except if you enable `--strict` checking.\n\n## Does Knip separate dependencies and devDependencies?\n\nYes, Knip understands the difference between dependencies and devDependencies.\nIt has a [production mode][6] to focus on production code only and find dead\ncode and dependencies that would otherwise only be referenced by tests and other\ntooling. This allows you to remove both unused exported code and their tests.\n\n## Does Knip work with my package manager?\n\nYes, Knip works with any package manager: npm, pnpm, Bun and Yarn are all\nsupported. It's easy to [get started][1] with any package manager.\n\n[1]: ../overview/getting-started.mdx\n[2]: ../reference/plugins.md\n[3]: ../features/compilers.md\n[4]: ../features/auto-fix.mdx\n[5]: ../features/monorepos-and-workspaces.md\n[6]: ../features/production-mode.md\n"
  },
  {
    "path": "packages/docs/src/content/docs/typescript/unused-exports.md",
    "content": "---\ntitle: Unused exports\ndescription: Find and remove unused exports with Knip\nprev: false\nnext: false\n---\n\nFinding unused exports in your JavaScript and TypeScript projects is one of\nKnip's core features. And it comes with even more features to identify and\nremove clutter to keep your projects in great shape.\n\n## Why are unused exports a problem?\n\nHaving unused exports in your codebase is problematic for several reasons:\n\n- They increase bundle sizes if not properly eliminated by tree-shaking.\n- They clutter the codebase and make it harder to navigate and understand.\n- They mislead developers into thinking certain code is used when it's not.\n- They make refactoring and maintaining the codebase more difficult.\n- They slow down tooling that analyze the codebase, such as bundlers, linters\n  and type checkers.\n- They may represent dead code that is no longer needed but hasn't been cleaned\n  up.\n\n## How do I find unused exports?\n\nKnip is a powerful tool that can help you find and remove unused exports in your\nJavaScript and TypeScript projects. It analyzes the codebase, identifies exports\nthat are not imported anywhere, and reports them.\n\n[Get started and install Knip][1] to run it on your project. Knip will scan your\nfiles and provide a detailed report of unused exports, and much more.\n\n<div style=\"display: flex; justify-content: center; margin: 4rem auto;\">\n  <img src=\"/logo.svg\" alt=\"Logo of Knip, to find unused dependencies, exports and files\" class=\"logo-border\" />\n</div>\n\n## How does Knip identify unused exports?\n\nKnip performs both static and dynamic analysis to determine which exports are\nactually being used in your codebase. It looks at import statements, export\nusage, and [a lot more code patterns][2] to identify unused exports.\n\nKnip supports JavaScript and TypeScript projects, and handles both [CommonJS][3]\nand ES Modules syntax.\n\n## Can Knip remove unused exports?\n\nYes, Knip not only finds unused exports but can also remove them for you. Run\nKnip with the `--fix` flag to enable [the auto-fix feature][4], and it will\nmodify your source code and remove the unused exports.\n\nIt's always recommended to review the changes made by Knip before committing\nthem to ensure no unintended modifications were made.\n\n## Can Knip handle large codebases?\n\nAbsolutely. Knip supports [monorepos with workspaces][5] to efficiently analyze\nlarge monorepos. This makes it easier and more fun to manage and optimize large\nmulti-package projects.\n\n## Does Knip work with my favorite editor or IDE?\n\nKnip is a command-line tool that runs independently of your editor or IDE.\nHowever, if you run Knip inside an integrated IDE terminal, the report contains\nfile names and positions in a format IDEs like VS Code and WebStorm understand\nto easily navigate around.\n\n## How is Knip different from ESLint for finding unused exports?\n\nWhile linters like ESLint can find unused imports and variables within\nindividual files, Knip analyzes the entire project to determine which exports\nare actually unused. By building [a comprehensive module graph][6], Knip\nidentifies exports that are not imported or used anywhere in the codebase. This\nallows Knip to catch unused exports and dead code that ESLint and other linters\nwould miss.\n\nAlso see [Why isn't Knip an ESLint plugin?][7]\n\n[1]: ../overview/getting-started.mdx\n[2]: ../reference/faq.md#what-does-knip-look-for-in-source-files\n[3]: ../guides/working-with-commonjs.md\n[4]: ../features/auto-fix.mdx\n[5]: ../features/monorepos-and-workspaces.md\n[6]: ../reference/faq.md#whats-in-the-graphs\n[7]: ../reference/faq.md#why-isnt-knip-an-eslint-plugin\n"
  },
  {
    "path": "packages/docs/src/content/docs/writing-a-plugin/argument-parsing.md",
    "content": "---\ntitle: Argument Parsing\nsidebar:\n  order: 3\n---\n\nSome plugins have an `arg` object in their implementation. It's a way for\nplugins to customize how command-line arguments are parsed for their tool's\nexecutables. Argument parsing in plugins help Knip identify dependencies and\nentry files from scripts.\n\nKnip uses [minimist][1] for argument parsing and some options are identical\n([alias][2], [boolean][3], [string][4]).\n\nAlso see [type definitions][5] and [examples in existing plugins][6].\n\n- [alias][2]\n- [args][7]\n- [binaries][8]\n- [boolean][3]\n- [config][9]\n- [fromArgs][10]\n- [nodeImportArgs][11]\n- [positional][12]\n- [resolve][13]\n- [resolveInputs][14]\n- [string][4]\n\n## alias\n\nDefine aliases.\n\nExample:\n\n```ts\n{\n  require: ['r'];\n}\n```\n\nAlso see [nodeImportArgs][11].\n\n## args\n\nModify or filter arguments before parsing. For edge cases preprocessing is\nuseful, e.g. if minimist has trouble parsing or to modify/discard arguments.\n\nExample:\n\n```ts\n{\n  args: (args: string[]) => args.filter(arg => arg !== 'omit');\n}\n```\n\n## binaries\n\nExecutables for the dependency.\n\nExample:\n\n```ts\n{\n  binaries: ['tsc'];\n}\n```\n\nDefault: plugin name, e.g. for the ESLint plugin the value is `[\"eslint\"]`\n\n## boolean\n\nMark arguments as boolean. By default, arguments are expected to have string\nvalues.\n\n## config\n\nDefine arguments that contain the configuration file path. Usually you'll want\nto set aliases too. Use `true` for shorthand to set `alias` + `string` +\n`config`.\n\nExample:\n\n```ts\n{\n  config: true;\n}\n```\n\nThe `tsup` plugin has this. Now `tsup --config tsup.client.json` will have\n`tsup.client.json` go through `resolveConfig` (also `-c` alias).\n\nExample:\n\n```ts\n{\n  config: ['p'];\n}\n```\n\nThis will mark e.g. `tsc -p tsconfig.lib.json` as a configuration file and it\nwill be handled by `resolveConfig` of the (typescript) plugin.\n\n## fromArgs\n\nParse return value as a new script. Can be a an array of strings, or function\nthat returns an array of strings and those values will be parsed separately.\n\nExample:\n\n```ts\n{\n  fromArgs: ['exec'];\n}\n```\n\nThen this script:\n\n```sh\nnodemon --exec \"node index.js\"\n```\n\nWill have `\"node index.js\"` being parsed as a new script.\n\n## nodeImportArgs\n\nSet to `true` as a shorthand for this [alias][2]:\n\n```ts\n{\n  import: ['r', 'experimental-loader', 'require', 'loader']\n}\n```\n\nExample:\n\n```ts\n{\n  nodeImportArgs: true;\n}\n```\n\n## positional\n\nSet to `true` to use the first positional argument as an entry point.\n\nExample:\n\n```ts\n{\n  positional: true;\n}\n```\n\nThe `tsx` plugin has this and `\"tsx script.ts\"` as a script will result in the\n`script.ts` file being an entry point.\n\n## resolve\n\nList of arguments to resolve to a dependency or entry file path.\n\nExample:\n\n```ts\n{\n  resolve: ['plugin'];\n}\n```\n\nNow for a script like `\"program --plugin package\"` this will result in\n`\"package\"` being resolved as a dependency.\n\n## resolveInputs\n\nReturn inputs from parsed arguments\n\n```ts\n{\n  resolveInputs: (parsed: ParsedArgs) =>\n    parsed['flag'] ? [toDependency('package')] : [];\n}\n```\n\n## string\n\nMark arguments as string. This is the default, but number-looking arguments are\nreturned as numbers by minimist.\n\n[1]: https://www.npmjs.com/package/minimist\n[2]: #alias\n[3]: #boolean\n[4]: #string\n[5]: https://github.com/webpro-nl/knip/blob/main/packages/knip/src/types/args.ts\n[6]: https://github.com/search?q=repo%3Awebpro-nl%2Fknip++path%3Apackages%2Fknip%2Fsrc%2Fplugins+%22const+args+%3D%22&type=code\n[7]: #args\n[8]: #binaries\n[9]: #config\n[10]: #fromargs\n[11]: #nodeimportargs\n[12]: #positional\n[13]: #resolve\n[14]: #resolveinputs\n"
  },
  {
    "path": "packages/docs/src/content/docs/writing-a-plugin/index.md",
    "content": "---\ntitle: Writing A Plugin\nsidebar:\n  order: 1\n---\n\nPlugins provide Knip with entry files and dependencies it would be unable to\nfind otherwise. Plugins always do at least one of the following:\n\n1. Define entry file patterns\n2. Find dependencies in configuration files\n\nKnip v5.1.0 introduced a new plugin API, which makes them a breeze to write and\nmaintain.\n\n:::tip[The new plugin API]\n\nEasy things should be easy, and complex things possible.\n\n:::\n\nThis tutorial walks through example plugins so you'll be ready to write your\nown! The following examples demonstrate the elements a plugin can implement.\n\nThere's a handy command available to easily [create a new plugin][1] and get\nstarted right away.\n\n## Example 1: entry\n\nLet's dive right in. Here's the entire source code of the Tailwind plugin:\n\n```ts\nimport type { IsPluginEnabled, Plugin } from '../../types/config.js';\nimport { hasDependency } from '../../util/plugin.js';\n\nconst title = 'Tailwind';\n\nconst enablers = ['tailwindcss'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) =>\n  hasDependency(dependencies, enablers);\n\nconst entry = ['tailwind.config.{js,cjs,mjs,ts}'];\n\nconst plugin: Plugin {\n  title,\n  enablers,\n  isEnabled,\n  entry,\n};\n\nexport default plugin;\n```\n\nYes, that's the entire plugin! Let's go over each item one by one:\n\n### 1. `title`\n\nThe title of the plugin displayed in the [list of plugins][2] and in debug\noutput.\n\n### 2. `enablers`\n\nAn array of strings to match one or more dependencies in `package.json` so the\n`isEnabled` function can determine whether the plugin should be enabled or not.\nRegular expressions are allowed as well.\n\n### 3. `isEnabled`\n\nThis function checks whether a match is found in the `dependencies` or\n`devDependencies` in `package.json`. The plugin is enabled if the dependency is\nlisted in `package.json`.\n\nThis function can be kept straightforward with the `hasDependency` helper.\n\n### 4. `entry`\n\nThis plugin exports `entry` file patterns. This means that if the Tailwind\nplugin is enabled, then `tailwind.config.*` files are added as entry files. A\nTailwind configuration file does not contain anything particular, so adding it\nas an `entry` to treat it as a regular source file is enough.\n\nThe next example shows how to handle a tool that has its own particular\nconfiguration object.\n\n## Example 2: config\n\nHere's the full source code of the `nyc` plugin:\n\n```ts\nimport { toDeferResolve } from '../../util/input.js';\nimport { hasDependency } from '../../util/plugin.js';\nimport type { NycConfig } from './types.js';\nimport type {\n  IsPluginEnabled,\n  Plugin,\n  ResolveConfig,\n} from '../../types/config.js';\n\nconst title = 'nyc';\n\nconst enablers = ['nyc'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) =>\n  hasDependency(dependencies, enablers);\n\nconst config = [\n  '.nycrc',\n  '.nycrc.{json,yml,yaml}',\n  'nyc.config.js',\n  'package.json',\n];\n\nconst resolveConfig: ResolveConfig<NycConfig> = config => {\n  const extend = config?.extends ?? [];\n  const requires = config?.require ?? [];\n  return [extend, requires].flat().map(id => toDeferResolve(id));\n};\n\nconst plugin: Plugin {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig\n};\n\nexport default plugin;\n```\n\nHere's an example `config` file that will be handled by this plugin:\n\n```json title=\".nycrc.json\"\n{\n  \"extends\": \"@istanbuljs/nyc-config-typescript\",\n  \"check-coverage\": true\n}\n```\n\nCompared to the first example, this plugin has two new variables:\n\n### 5. `config`\n\nThe `config` array contains all possible locations of the config file for the\ntool. Knip loads matching files and passes the results (i.e. its default export)\ninto the `resolveConfig` function:\n\n### 6. `resolveConfig`\n\nThis function receives the exported value of the `config` file, and executes the\n`resolveConfig` function with this object. The plugin should return the entry\npaths and dependencies referenced in this object.\n\nKnip supports JSON, YAML, TOML, JavaScript and TypeScript config files. Files\nwithout an extension are provided as plain text strings.\n\n:::tip[Should I implement resolveConfig?]\n\nYou should implement `resolveConfig` if any of these are true:\n\n- The `config` file contains one or more options that represent [entry\n  points][3]\n- The `config` file references dependencies by strings (not import statements)\n\n:::\n\n## Example 3: entry paths\n\n### 7. entry and production\n\nSome tools operate mostly on entry files, some examples:\n\n- Mocha looks for test files at `test/*.{js,cjs,mjs}`\n- Storybook looks for stories at `*.stories.@(mdx|js|jsx|tsx)`\n\nAnd some of those tools allow to configure those locations and patterns in\nconfiguration files, such as `next.config.js` or `vite.config.ts`. If that's the\ncase we can define `resolveConfig` in our plugin to take this from the\nconfiguration object and return it to Knip:\n\nHere's an example from the Mocha plugin:\n\n```ts\nconst entry = ['**/test/*.{js,cjs,mjs}'];\n\nconst resolveConfig: ResolveConfig<MochaConfig> = localConfig => {\n  const entryPatterns = localConfig.spec ? [localConfig.spec].flat() : entry;\n  return entryPatterns.map(id => toEntry(id));\n};\n\nexport default {\n  entry,\n  resolveConfig,\n};\n```\n\nWith Mocha, you can configure `spec` file patterns. The result of implementing\n`resolveConfig` is that users don't need to duplicate this configuration in both\nthe tool (e.g. Mocha) and Knip.\n\nUse `production` entries to target source files that represent production code.\n\n:::tip\n\nRegardless of the presence of `resolveConfig`, add `entry` and `production` to\nthe default export so they will be displayed in the plugin's documentation as\ndefault values.\n\n:::\n\n## Example 4: Use the AST directly\n\nIf the `resolveFromConfig` function is implemented, Knip loads the configuration\nfile and passes the default-exported object to this plugin function. However,\nthat object might then not contain the information we need.\n\nHere's an example `astro.config.ts` configuration file with a Starlight\nintegration:\n\n```ts\nimport starlight from '@astrojs/starlight';\nimport { defineConfig } from 'astro/config';\n\nexport default defineConfig({\n  integrations: [\n    starlight({\n      components: {\n        Head: './src/components/Head.astro',\n        Footer: './src/components/Footer.astro',\n      },\n    }),\n  ],\n});\n```\n\nWith Starlight, components can be defined to override the default internal ones.\nThey're not otherwise referenced in your source code, so you'd have to manually\nadd them as entry files ([Knip itself did this][4]).\n\nIn the Astro plugin, there's no way to access this object containing\n`components` to add the component files as entry files if we were to try:\n\n```ts\nconst resolveConfig: ResolveConfig<AstroConfig> = async config => {\n  console.log(config); //  ¯\\_(ツ)_/¯\n};\n```\n\nThis is why plugins can implement the `resolveFromAST` function.\n\n### 8. resolveFromAST\n\nLet's take a look at the Astro plugin implementation. This example assumes some\nfamiliarity with Abstract Syntax Trees (AST). Knip provides AST helpers to make\nimplementing plugins more fun and a little less tedious.\n\nAnyway, let's dive in. Here's how we're adding the Starlight `components` paths\nto the default `production` file patterns:\n\n```ts\nimport type { Program } from 'oxc-parser';\nimport {\n  findCallArg,\n  getDefaultImportName,\n  getImportMap,\n  getPropertyValues,\n} from '../../typescript/ast-helpers.ts';\n\nconst title = 'Astro';\n\nconst production = [\n  'src/pages/**/*.{astro,mdx,js,ts}',\n  'src/content/**/*.mdx',\n  'src/middleware.{js,ts}',\n  'src/actions/index.{js,ts}',\n];\n\nconst getComponentPaths = (program: Program) => {\n  const importMap = getImportMap(program);\n  const importName = getDefaultImportName(importMap, '@astrojs/starlight');\n  if (!importName) return new Set<string>();\n  const starlightConfig = findCallArg(program, importName);\n  return getPropertyValues(starlightConfig, 'components');\n};\n\nconst resolveFromAST: ResolveFromAST = (program: Program) => {\n  const componentPaths = getComponentPaths(program);\n  return [...production, ...componentPaths].map(id => toProductionEntry(id));\n};\n\nconst plugin: Plugin = {\n  title,\n  production,\n  resolveFromAST,\n};\n\nexport default plugin;\n```\n\n## None Of The Above\n\n### 9. `resolve`\n\nIf there is no configuration file to parse or there is a need for customization,\nuse `resolve`:\n\n```ts\nconst resolve: Resolve = async options => {\n  return toDependency('troublesome', { optional: true });\n};\n```\n\n## Inputs\n\nYou may have noticed functions like `toDeferResolve` and `toEntry`. They're a\nway for plugins to tell what they've found and how Knip should handle those. The\nmore precision a plugin can provide here, the better results and performance\nwill be.\n\nFind all the details over at [Writing A Plugin → Inputs][5].\n\n## Argument Parsing\n\nAs part of the [script parser][6], Knip parses command-line arguments. Plugins\ncan implement the `arg` object to add custom argument parsing tailored to the\ntool.\n\nRead more in [Writing A Plugin → Argument Parsing][7].\n\n## Create a new plugin\n\nThe easiest way to create a new plugin is to use the `create-plugin` script:\n\n```sh\ncd packages/knip\npnpm create-plugin --name tool\n```\n\nThis adds source and test files and fixtures to get you started. Keep fixtures\ntidy, remove clutter that isn't required to test the plugin properly. E.g. using\na starterkit, script or wizard often creates more than what we need here.\n\nThe script adds the plugin to the JSON Schema and type definitions.\n\nRun the test for your new plugin using one of the following commands:\n\n```sh\nnode --test test/plugins/tool.test.ts\nbun test test/plugins/tool.test.ts\n```\n\nYou're ready to implement and submit a new Knip plugin! 🆕 🎉\n\n## Wrapping Up\n\nFeel free to check out the implementation of other similar plugins, and borrow\nideas and code from those!\n\nThe documentation website takes care of generating the [plugin list and the\nindividual plugin pages][2] from the exported plugin values.\n\nThanks for reading. If you have been following this guide to create a new\nplugin, this might be the right time to open a pull request!\n\n[1]: #create-a-new-plugin\n[2]: ../reference/plugins.md\n[3]: ../explanations/plugins.md#entry-files-from-config-files\n[4]: https://github.com/webpro-nl/knip/blob/6a6954386b33ee8a2919005230a4bc094e11bc03/knip.json#L12\n[5]: ./inputs.md\n[6]: ../features/script-parser.md\n[7]: ./argument-parsing.md\n"
  },
  {
    "path": "packages/docs/src/content/docs/writing-a-plugin/inputs.md",
    "content": "---\ntitle: Inputs\nsidebar:\n  order: 2\n---\n\nYou may have noticed functions like `toDeferResolve` and `toEntry`. They're a\nway for plugins to tell what they've found and how Knip should handle those. The\nmore precise a plugin can be, the better it is for results and performance.\nHere's an overview of all input type functions:\n\n- [toEntry][1]\n- [toProductionEntry][2]\n- [toProject][3]\n- [toDependency][4]\n- [toProductionDependency][5]\n- [toDeferResolve][6]\n- [toDeferResolveEntry][7]\n- [toConfig][8]\n- [toBinary][9]\n- [toAlias][10]\n- [Options][11]\n\n## toEntry\n\nAn `entry` input is just like an `entry` in the configuration. It should either\nbe an absolute or relative path, and glob patterns are allowed.\n\n## toProductionEntry\n\nA production `entry` input is just like an `production` in the configuration. It\nshould either be an absolute or relative path, and it can have glob patterns.\n\n## toProject\n\nA `project` input is the equivalent of `project` patterns in the configuration.\nIt should either be an absolute or relative path, and (negated) glob patterns\nare allowed.\n\n## toDependency\n\nThe `dependency` indicates the entry is a dependency, belonging in either the\n`\"dependencies\"` or `\"devDependencies\"` section of `package.json`.\n\n## toProductionDependency\n\nThe production `dependency` indicates the entry is a production dependency,\nexpected to be listed in `\"dependencies\"`.\n\n## toDeferResolve\n\nThe `deferResolve` input type is used to defer the resolution of a specifier.\nThis could be resolved to a dependency or an entry file. For instance, the\nspecifier `\"input\"` could be resolved to `\"input.js\"`, `\"input.tsx\"`,\n`\"input/index.js\"` or the `\"input\"` package name. Local files are added as entry\nfiles, package names are external dependencies.\n\nIf this does not lead to a resolution, the specifier will be reported under\n\"unresolved imports\".\n\n## toDeferResolveEntry\n\nThe `deferResolveEntry` input type is similar to `deferResolve`, but it's used\nfor entry files only (not dependencies) and unresolved inputs are ignored. It's\ndifferent from `toEntry` as glob patterns are not supported.\n\n## toConfig\n\nThe `config` input type is a way for plugins to reference a configuration file\nthat should be handled by a different plugin. For instance, Angular\nconfigurations might contain references to `tsConfig` and `karmaConfig` files,\nso these `config` files can then be handled by the TypeScript and Karma plugins,\nrespectively.\n\nExample:\n\n```ts\ntoConfig('typescript', './path/to/tsconfig.json');\n```\n\nFor instance, the Angular plugin uses this to tell Knip about its `tsConfig`\nvalue in `angular.json` projects.\n\n## toBinary\n\nThe `binary` input type isn't used by plugins directly, but by the shell script\nparser (through the `getInputsFromScripts` helper). Think of GitHub Actions\nworkflow YAML files or husky scripts. Using this input type, a binary is\n\"assigned\" to the dependency that has it as a `\"bin\"` in their `package.json`.\n\n## toAlias\n\nThe `alias` input type adds path aliases to the core module resolver. They're\nadded to `compilerOptions.paths` so the syntax is identical.\n\n## Options\n\nWhen creating inputs from specifiers, an extra `options` object as the second\nargument can be provided.\n\n### dir\n\nThe optional `dir` option assigns the input to a different workspace. For\ninstance, GitHub Action workflows are always stored in the root workspace, and\nsupport `working-directory` in job steps. For example:\n\n```yaml\njobs:\n  stylelint:\n    runs-on: ubuntu-latest\n    steps:\n      - run: npx esbuild\n        working-directory: packages/app\n```\n\nThe GitHub Action plugin understands `working-directory` and adds this `dir` to\nthe input:\n\n```ts\ntoDependency('esbuild', { dir: 'packages/app' });\n```\n\nKnip now understands `esbuild` is a dependency of the workspace in the\n`packages/app` directory.\n\n### optional\n\nUse the `optional` flag to indicate the dependency is optional. Then, a\ndependency won't be flagged as unlisted if it isn't.\n\n### allowIncludeExports\n\nBy default, exports of entry files such as `src/index.ts` or the files in\n`package.json#exports` are not reported as unused. When using the\n`--include-entry-exports` flag or `isIncludeExports: true` option, unused\nexports on such entry files are also reported.\n\nExports of entry files coming from plugins are not included in the analysis,\neven with the option enabled. This is because certain tools and frameworks\nconsume named exports from entry files, causing false positives.\n\nThe `allowIncludeExports` option allows the exports of entry files to be\nreported as unused when using `--include-entry-exports`. This option is\ntypically used with the [toProductionEntry][2] input type.\n\nExample:\n\n```ts\ntoProductionEntry('./entry.ts', { allowIncludeExports: true });\n```\n\n[1]: #toentry\n[2]: #toproductionentry\n[3]: #toproject\n[4]: #todependency\n[5]: #toproductiondependency\n[6]: #todeferresolve\n[7]: #todeferresolveentry\n[8]: #toconfig\n[9]: #tobinary\n[10]: #toalias\n[11]: #options\n"
  },
  {
    "path": "packages/docs/src/content.config.ts",
    "content": "import { defineCollection } from 'astro:content';\nimport { docsLoader, i18nLoader } from '@astrojs/starlight/loaders';\nimport { docsSchema, i18nSchema } from '@astrojs/starlight/schema';\n\nexport const collections = {\n  docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),\n  i18n: defineCollection({ loader: i18nLoader(), schema: i18nSchema() }),\n};\n"
  },
  {
    "path": "packages/docs/src/fonts/font-face.css",
    "content": "/*!\n * Source Sans Pro https://fonts.adobe.com./source-sans\n * Copyright 2010, 2012, 2014 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name ‘Source’.\n * This Font Software is licensed under the SIL Open Font License, Version 1.1  (http://scripts.sil.org/OFL).\n */\n\n@font-face {\n  font-family: \"Source Sans Pro\";\n  font-style: italic;\n  font-weight: 200;\n  font-display: swap;\n  src: url(\"./6xKwdSBYKcSV-LCoeQqfX1RYOo3qPZYokSds18S0xR41.woff2\") format(\"woff2\");\n  unicode-range:\n    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,\n    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n@font-face {\n  font-family: \"Source Sans Pro\";\n  font-style: italic;\n  font-weight: 300;\n  font-display: swap;\n  src: url(\"./6xKwdSBYKcSV-LCoeQqfX1RYOo3qPZZMkids18S0xR41.woff2\") format(\"woff2\");\n  unicode-range:\n    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,\n    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n@font-face {\n  font-family: \"Source Sans Pro\";\n  font-style: normal;\n  font-weight: 300;\n  font-display: swap;\n  src: url(\"./6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdu3cOWxw.woff2\") format(\"woff2\");\n  unicode-range:\n    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,\n    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n@font-face {\n  font-family: \"Source Sans Pro\";\n  font-style: normal;\n  font-weight: 400;\n  font-display: swap;\n  src: url(\"./6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7lujVj9w.woff2\") format(\"woff2\");\n  unicode-range:\n    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,\n    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n/*!\n *  Hack typeface https://github.com/source-foundry/Hack\n *  License: https://github.com/source-foundry/Hack/blob/master/LICENSE.md\n */\n@font-face {\n  font-family: \"Hack\";\n  src: url(\"./hack-regular-subset.woff2\") format(\"woff2\");\n  font-weight: 400;\n  font-style: normal;\n  font-display: swap;\n}\n"
  },
  {
    "path": "packages/docs/src/pages/og/[...route].ts",
    "content": "import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, resolve } from 'node:path';\n\nconst require = createRequire(import.meta.url);\n// ts-expect-error TS80005\nconst sharp = require('sharp');\n\nconst template = readFileSync(resolve('src/assets/og-template.svg'), 'utf-8');\nconst cacheDir = resolve('node_modules/.astro/og');\n\nconst getPages = async () => {\n  const data = import.meta.glob(['/src/content/**/*.{md,mdx}'], { eager: true });\n  const pages: Record<string, unknown> = {};\n  for (const [filePath, page] of Object.entries(data)) {\n    const imagePath = filePath.replace(/^\\/src\\/content\\//, '').replace(/(\\/index)?\\.(md|mdx)$/, '.webp');\n    pages[imagePath] = page;\n  }\n  return pages;\n};\n\nconst getFromCache = (route: string) => {\n  const cachePath = resolve(cacheDir, route);\n  if (existsSync(cachePath)) return readFileSync(cachePath) as unknown as ArrayBuffer;\n  return null;\n};\n\nconst saveToCache = (route: string, data: ArrayBuffer) => {\n  const cachePath = resolve(cacheDir, route);\n  mkdirSync(dirname(cachePath), { recursive: true });\n  writeFileSync(cachePath, new Uint8Array(data));\n};\n\nconst renderSVG = ({ title }: { title: string }) => {\n  const lines = balanceText(title, 30);\n\n  const titleText = `\n    <text\n      text-anchor=\"start\"\n      text-rendering=\"optimizeLegibility\"\n      font-size=\"${lines.length === 1 ? 80 : 64}\"\n      fill=\"#fff\"\n      x=\"75\"\n      y=\"500\"\n    >\n      ${lines.map((line, i) => `<tspan x=\"75\" dy=\"${i === 0 ? '0' : '1.2em'}\" >${encodeXML(line)}</tspan>`).join('')}\n    </text>\n  `;\n\n  return template.replace('<!-- titleText -->', titleText);\n};\n\nfunction encodeXML(text: string): string {\n  return text\n    .replace(/&/g, '&amp;')\n    .replace(/</g, '&lt;')\n    .replace(/>/g, '&gt;')\n    .replace(/\"/g, '&quot;')\n    .replace(/'/g, '&apos;');\n}\n\nfunction balanceText(text: string, maxLen: number): string[] {\n  const words = text.split(' ');\n  if (words.join(' ').length <= maxLen) return [text];\n\n  let bestSplit = 0;\n  let bestDiff = Number.POSITIVE_INFINITY;\n\n  for (let i = 0; i < words.length - 1; i++) {\n    const line1 = words.slice(0, i + 1).join(' ');\n    const line2 = words.slice(i + 1).join(' ');\n    const diff = Math.abs(line1.length - line2.length);\n    if (diff < bestDiff) {\n      bestDiff = diff;\n      bestSplit = i + 1;\n    }\n  }\n\n  return [words.slice(0, bestSplit).join(' '), words.slice(bestSplit).join(' ')];\n}\n\nexport const GET = async ({ params }: { params: { route: string } }) => {\n  const pages = await getPages();\n  const pageEntry = pages[params.route];\n  if (!pageEntry) return new Response('Page not found', { status: 404 });\n\n  // @ts-expect-error TODO type properly\n  const title = pageEntry.frontmatter.hero?.tagline ?? pageEntry.frontmatter.title;\n\n  let body = getFromCache(params.route);\n  if (!body) {\n    const svgBuffer = Buffer.from(renderSVG({ title }));\n    body = (await sharp(svgBuffer).resize(1200, 630).webp({ lossless: true }).toBuffer()) as unknown as ArrayBuffer;\n    saveToCache(params.route, body);\n  }\n\n  return new Response(body, {\n    headers: {\n      'Content-Type': 'image/webp',\n      'Cache-Control': 'no-cache, no-store, must-revalidate',\n      Pragma: 'no-cache',\n      Expires: '0',\n    },\n  });\n};\n\nexport const getStaticPaths = async () => {\n  const pages = await getPages();\n  return Object.keys(pages).map(route => ({ params: { route } }));\n};\n"
  },
  {
    "path": "packages/docs/src/pages/sitemap.txt.ts",
    "content": "import type { APIContext } from 'astro';\n\ninterface Page {\n  frontmatter: {\n    draft: boolean;\n  };\n}\n\nconst data: Record<string, Page> = import.meta.glob(['/src/content/**/!([)*.{md,mdx}'], { eager: true });\nconst plugins = import.meta.glob(['/dist/reference/plugins/*/index.html'], { eager: true });\n\nconst pages = new Set<string>();\nfor (const [filePath, page] of Object.entries(data)) {\n  if (page?.frontmatter?.draft === true) continue;\n  pages.add(filePath.replace(/^\\/src\\/content\\/docs\\//, '').replace(/(\\/?index)?\\.(md|mdx)$/, ''));\n}\n\nfor (const [filePath] of Object.entries(plugins)) {\n  pages.add(filePath.replace(/^\\/dist\\//, '').replace(/\\/index.html$/, ''));\n}\n\nexport async function GET(context: APIContext) {\n  const baseUrl = context.site?.href;\n  const documents = [...pages].map(entry => new URL(entry, baseUrl).href).sort();\n  return new Response(documents.join('\\n'));\n}\n"
  },
  {
    "path": "packages/docs/src/styles/cards.css",
    "content": ".no-title-cards .card-grid .card {\n  gap: 0;\n}\n\n:root {\n  .card .title .icon {\n    border: unset;\n    background-color: var(--sl-color-orange);\n  }\n}\n\n:root[data-theme=\"dark\"] {\n  .card {\n    border: none;\n  }\n}\n\n:root[data-theme=\"light\"] {\n  .card .title .icon {\n    color: var(--sl-color-black);\n  }\n}\n"
  },
  {
    "path": "packages/docs/src/styles/content.css",
    "content": "html {\n  scroll-behavior: smooth;\n}\n\n.content-panel + .content-panel {\n  border: unset;\n}\n\n.sl-markdown-content h2 {\n  margin-bottom: 2rem;\n}\n\n.sl-container .sl-markdown-content ul li p {\n  margin: 0;\n}\n\n.sl-markdown-content table {\n  overflow: unset;\n}\n\n.sl-markdown-content table th,\n.sl-markdown-content table td {\n  white-space: nowrap;\n  padding: 0.3rem 1rem 0.3rem 0;\n  border: none;\n}\n\n.sl-markdown-content table thead {\n  border-bottom: 1px solid var(--sl-color-gray-1);\n}\n\n.sl-markdown-content em {\n  font-weight: bold;\n}\n\n.sl-markdown-content h2 code,\n.sl-markdown-content h3 code {\n  font-size: 0.9em;\n  color: var(--sl-color-white);\n}\n\n.sl-markdown-content p code,\n.sl-markdown-content ul code {\n  white-space: nowrap;\n  padding: 0 1px;\n}\n\n.sl-markdown-content a code {\n  padding-left: 0;\n  padding-right: 0;\n}\n\n.sl-markdown-content blockquote {\n  border-inline-start-color: var(--sl-color-gray-1);\n}\n\n.sl-markdown-content section.columns.mt {\n  margin-top: 3rem;\n}\n\nsection.columns ul {\n  padding: 0;\n  list-style: none;\n  display: grid;\n  gap: 1rem;\n  white-space: nowrap;\n  margin-bottom: 3rem;\n}\n\nsection.columns.min200 ul {\n  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n}\n\nsection.columns.min300 ul {\n  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));\n}\n\nsection.plugins li {\n  margin: 0;\n}\n\n:root {\n  .header {\n    border-bottom-color: var(--sl-color-orange);\n  }\n}\n\n:root[data-theme=\"light\"] {\n  .header {\n    border-bottom-color: var(--sl-color-orange);\n  }\n}\n\n:root[data-theme=\"dark\"] .sl-markdown-content table td code {\n  padding-left: 0;\n}\n\n.sl-markdown-content .sponsor {\n  font-size: 2.5rem;\n  text-align: center;\n  margin: 4rem 0;\n}\n\n.sl-badge.caution,\n.sl-badge.outline {\n  --sl-color-bg-badge: transparent;\n  --sl-color-border-badge: var(--sl-color-orange);\n  color: var(--sl-color-white);\n}\n\n.sl-markdown-content .mw500 {\n  max-width: 500px;\n}\n\n.logo-border {\n  width: 200px;\n  border: 2px solid var(--sl-color-white);\n  border-radius: 40px;\n}\n\n.playground iframe {\n  width: 100%;\n  height: 600px;\n  border: 1px solid var(--sl-color-orange);\n}\n\n.sl-markdown-content p + lite-youtube {\n  border: 1px solid var(--sl-color-orange);\n  margin-top: 2rem;\n}\n\n.sl-markdown-content .starlight-aside {\n  margin-top: 2rem;\n}\n\n.sl-markdown-content p > img {\n  margin: 2rem 0;\n}\n\n:root[data-theme=\"light\"] {\n  .logo-border {\n    border-color: var(--sl-color-black);\n  }\n}\n\n#starlight__search .pagefind-ui__result-title:not(:where(.pagefind-ui__result-nested *)):focus-within,\n#starlight__search .pagefind-ui__result-nested:focus-within {\n  .pagefind-ui__result-link,\n  .pagefind-ui__result-excerpt,\n  .pagefind-ui__result-excerpt mark {\n    color: var(--sl-color-text-accent);\n  }\n}\n\n:root[data-theme=\"light\"] {\n  #starlight__search .pagefind-ui__result-title:not(:where(.pagefind-ui__result-nested *)):focus-within,\n  #starlight__search .pagefind-ui__result-nested:focus-within {\n    .pagefind-ui__result-link,\n    .pagefind-ui__result-excerpt,\n    .pagefind-ui__result-excerpt mark {\n      color: var(--sl-color-black);\n    }\n  }\n}\n\n.social-icons a:hover {\n  opacity: 1;\n  svg {\n    color: var(--sl-color-bright-orange);\n  }\n}\n\n.badges {\n  margin: 4rem auto;\n  p {\n    display: flex;\n    justify-content: center;\n    gap: 1rem;\n\n    a {\n      border-bottom: none;\n    }\n  }\n}\n\n.projects {\n  margin: 6rem auto;\n}\n"
  },
  {
    "path": "packages/docs/src/styles/custom.css",
    "content": "@import url(\"./theme-dark.css\");\n@import url(\"./theme-light.css\");\n@import url(\"./hero.css\");\n@import url(\"./content.css\");\n@import url(\"./cards.css\");\n@import url(\"./links.css\");\n@import url(\"./expressive-code.css\");\n@import url(\"./sponsors.css\");\n"
  },
  {
    "path": "packages/docs/src/styles/expressive-code.css",
    "content": ".expressive-code figure {\n  box-shadow: none;\n}\n\n.expressive-code .frame.has-title:not(.is-terminal) .header .title::after {\n  border-bottom: none;\n}\n\n:root[data-theme=\"light\"] {\n  .expressive-code .frame pre,\n  .expressive-code .frame.has-title:not(.is-terminal) .header .title {\n    background-color: #151419;\n    border-color: #151419;\n  }\n}\n\n.expressive-code .frame pre,\n.expressive-code .frame div {\n  margin-top: unset;\n  letter-spacing: -0.5px;\n}\n"
  },
  {
    "path": "packages/docs/src/styles/hero.css",
    "content": ".hero {\n  padding-bottom: 1rem;\n}\n\n.hero img {\n  border: 4px solid var(--sl-color-white);\n  border-radius: 80px;\n}\n\n.hero h1 {\n  text-indent: -0.0666em;\n  font-size: 9rem;\n}\n\n.tagline {\n  font-size: var(--sl-text-3xl);\n}\n\n.actions .sl-link-button.primary {\n  background-color: var(--sl-color-orange);\n}\n\n.actions .sl-link-button.primary:hover {\n  background-color: var(--sl-color-bright-orange);\n}\n\n.actions .sl-link-button.secondary:hover {\n  background-color: var(--sl-color-orange);\n}\n\n:root {\n  .actions .sl-link-button.primary {\n    color: var(--sl-color-white);\n    border: 1px solid var(--sl-color-white);\n  }\n}\n\n:root[data-theme=\"light\"] {\n  .hero img {\n    border-color: var(--sl-color-black);\n  }\n\n  .actions .sl-link-button.primary {\n    color: var(--sl-color-black);\n    border: 2px solid var(--sl-color-black);\n  }\n\n  .actions .sl-link-button.primary svg {\n    color: var(--sl-color-black);\n  }\n}\n"
  },
  {
    "path": "packages/docs/src/styles/links.css",
    "content": ".sl-markdown-content a {\n  text-decoration: none;\n  padding-bottom: 0.0675rem;\n  transition: border-color 300ms;\n}\n\n.sl-markdown-content a:not(:where(.not-content *)) {\n  color: var(--sl-color-text);\n  border-bottom: 1px solid var(--sl-color-gray-1);\n}\n\n.sl-markdown-content a:not(:where(.not-content *)):hover {\n  border-color: var(--sl-color-white);\n}\n\n.sl-markdown-content .sl-anchor-link {\n  color: var(--sl-color-text);\n  text-decoration: none;\n  border-bottom: none;\n}\n\n.sl-markdown-content .sl-anchor-link:hover {\n  color: var(--sl-color-text-accent);\n}\n\n.right-sidebar a:hover,\n.sidebar-content a:hover,\n.sidebar-content a:hover {\n  color: var(--sl-color-orange);\n}\n\n.right-sidebar a,\n.right-sidebar a:hover,\n.sidebar-content a,\n.sidebar-content a:hover {\n  background-color: unset;\n}\n\nfooter .pagination-links a {\n  border-color: var(--sl-color-black);\n  box-shadow: var(--sl-shadow-lg);\n}\n\nfooter .pagination-links a:hover {\n  border-color: var(--sl-color-gray-3);\n  box-shadow: var(--sl-shadow-md);\n}\n\n:root {\n  .starlight-aside a {\n    color: var(--sl-color-white);\n  }\n}\n\n:root[data-theme=\"light\"] {\n  .sl-markdown-content a:hover {\n    border-color: var(--sl-color-orange);\n  }\n\n  .tab a[role=\"tab\"] {\n    border-color: var(--sl-color-gray-5);\n  }\n  .tab a[role=\"tab\"]:hover {\n    border-color: var(--sl-color-white);\n  }\n  .tab a[role=\"tab\"][aria-selected] {\n    border-color: var(--sl-color-white);\n  }\n}\n"
  },
  {
    "path": "packages/docs/src/styles/sponsors.css",
    "content": "article.prose {\n  max-width: 72ch;\n  margin-bottom: 4rem;\n}\n\n.sl-markdown-content .sponsors-intro {\n  font-style: italic;\n  border: unset;\n  &:hover {\n    border-bottom: inherit;\n  }\n}\n\n.sl-markdown-content p:has(> .sponsors-intro) {\n  text-align: center;\n}\n\n.sponsors {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  flex-wrap: wrap;\n  gap: 4rem;\n}\n\n.sponsors.front {\n  margin-bottom: 8rem;\n  gap: 1rem;\n  flex-direction: row;\n  flex-wrap: nowrap;\n}\n\n.sponsors a {\n  border-bottom: none;\n}\n\n.sponsors img {\n  border: 1px solid var(--sl-color-white);\n  border-radius: 16px;\n}\n\n.sponsors .w500 {\n  width: 500px;\n}\n\n.sponsors .smaller svg {\n  transform: scale(0.85);\n}\n\n.sponsors .w400 {\n  width: 400px;\n}\n\n.sponsors .w300 {\n  width: 300px;\n}\n\n.sponsors .w200 {\n  width: 200px;\n}\n\n.sponsors .w150 {\n  width: 150px;\n}\n\n.sponsors .w100 {\n  width: 100px;\n}\n"
  },
  {
    "path": "packages/docs/src/styles/theme-dark.css",
    "content": "/* Colors borrowed from https://twitter.com/tushtisachdeva/status/1738940090628571177 */\n\n:root {\n  --sl-font: \"Source Sans Pro\", sans-serif;\n\n  --sl-color-orange: #f56e0f;\n  --sl-color-bright-orange: #f68a22;\n\n  --sl-color-white: #fbfbfb;\n  --sl-color-gray-1: #bdbdbd;\n  --sl-color-gray-2: #878787;\n  --sl-color-gray-3: #878787;\n  --sl-color-gray-4: #353841;\n  --sl-color-gray-5: #262626;\n  --sl-color-gray-6: #1b1b1e;\n  --sl-color-black: #151419;\n\n  --sl-color-text: var(--sl-color-gray-1);\n  --sl-color-accent-low: var(--sl-color-orange);\n  --sl-color-text-accent: var(--sl-color-white);\n\n  --sl-color-bg: var(--sl-color-gray-6);\n  --sl-color-bg-nav: var(--sl-color-black);\n  --sl-color-bg-sidebar: var(--sl-color-black);\n  --sl-color-bg-inline-code: rgb(30, 30, 30);\n  --astro-code-color-background: var(--sl-color-black);\n\n  --sl-color-hairline-light: var(--sl-color-gray-5);\n  --sl-color-hairline: var(--sl-color-gray-5);\n}\n"
  },
  {
    "path": "packages/docs/src/styles/theme-light.css",
    "content": ":root[data-theme=\"light\"] {\n  --sl-color-white: hsl(224, 10%, 10%);\n  --sl-color-black: hsl(0, 0%, 100%);\n  --sl-color-gray-1: hsl(224, 14%, 16%);\n  --sl-color-gray-2: hsl(224, 10%, 23%);\n  --sl-color-gray-3: hsl(224, 7%, 36%);\n  --sl-color-gray-4: hsl(224, 6%, 56%);\n  --sl-color-gray-5: hsl(224, 6%, 77%);\n  --sl-color-gray-6: hsl(224, 20%, 94%);\n  --sl-color-gray-7: hsl(224, 19%, 97%);\n\n  --sl-color-orange: #f56e0f;\n\n  --sl-color-text-accent: var(--sl-color-white);\n  --sl-color-text-invert: var(--sl-color-orange);\n  --sl-color-accent: var(--sl-color-orange);\n  --sl-color-accent-low: var(--sl-color-orange);\n  --sl-icon-color: var(--sl-color-white);\n  --sl-color-bg-inline-code: var(--sl-color-gray-6);\n}\n"
  },
  {
    "path": "packages/docs/src/util/post.ts",
    "content": "export const replaceShortenedUrls = (post: PostWithUser) => {\n  let text = (post.note_tweet?.text ?? post.text).replace(/^(@[^ ]+ )*/, '');\n  if (!post.entities.urls) return { ...post, text };\n  post.entities.urls.sort((a, b) => b.start - a.start);\n  for (const urlEntity of post.entities.urls) {\n    if (urlEntity.media_key) {\n      const media = (post.media ?? []).find(media => media.media_key === urlEntity.media_key);\n      if (media && media.type === 'photo') {\n        text = text.replace(urlEntity.url, `<img src=\"${media.url}\" alt=\"${media.alt_text}\" />`);\n      }\n    } else if (urlEntity.expanded_url.includes('twitter.com')) {\n      text = text.replace(urlEntity.url, '');\n    } else {\n      text = text.replace(urlEntity.url, `<a href=\"${urlEntity.expanded_url}\">${urlEntity.expanded_url}</a>`);\n    }\n  }\n  if (post.media && post.note_tweet?.text) {\n    text = text + post.media.map(media => `<img src=\"${media.url}\" alt=\"${media.alt_text}\" />`).join('');\n  }\n  post.text = text;\n  return post;\n};\n\nconst timeFormatter = new Intl.DateTimeFormat('en-US', {\n  hour: 'numeric',\n  minute: 'numeric',\n  hour12: true,\n  timeZone: 'UTC',\n});\n\nconst dateFormatter = new Intl.DateTimeFormat('en-US', {\n  day: 'numeric',\n  month: 'short',\n  year: 'numeric',\n  hour12: true,\n  timeZone: 'UTC',\n});\n\nexport const formatTimestamp = (date: string) => {\n  const d = new Date(date);\n  return `${timeFormatter.format(d)} · ${dateFormatter.format(d)}`;\n};\n\ninterface Post {\n  public_metrics: {\n    retweet_count: number;\n    reply_count: number;\n    like_count: number;\n    quote_count: number;\n    bookmark_count: number;\n    impression_count: number;\n  };\n  author_id: string;\n  entities: {\n    urls: UrlEntity[];\n    mentions?: MentionEntity[];\n    annotations?: AnnotationEntity[];\n  };\n  text: string;\n  note_tweet: {\n    text: string;\n  };\n  id: string;\n  url?: string;\n  edit_history_tweet_ids: string[];\n  created_at: string;\n}\n\ninterface UrlEntity {\n  start: number;\n  end: number;\n  url: string;\n  expanded_url: string;\n  display_url: string;\n  images?: ImageEntity[];\n  status?: number;\n  title?: string;\n  description?: string;\n  unwound_url?: string;\n  media_key?: string;\n}\n\ninterface MentionEntity {\n  start: number;\n  end: number;\n  username: string;\n  id: string;\n}\n\ninterface AnnotationEntity {\n  start: number;\n  end: number;\n  probability: number;\n  type: string;\n  normalized_text: string;\n}\n\ninterface ImageEntity {\n  url: string;\n  width: number;\n  height: number;\n}\n\ninterface User {\n  username: string;\n  profile_image_url: string;\n  name: string;\n  id: string;\n}\n\ninterface Media {\n  url: string;\n  type: 'photo';\n  alt_text: string;\n  media_key: string;\n}\n\nexport interface PostWithUser extends Post {\n  media?: Media[];\n  user: User;\n}\n\nexport interface PostResponse {\n  data: Post[];\n  includes: {\n    users: User[];\n    media: Media[];\n  };\n}\n"
  },
  {
    "path": "packages/docs/tsconfig.json",
    "content": "{\n  \"extends\": \"astro/tsconfigs/strict\",\n  \"mdx\": {\n    \"plugins\": [\"remark-directive\", \"remark-frontmatter\", \"remark-gfm\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/.gitignore",
    "content": "/coverage\n/dist\n/tmp\n/node_modules\n!/fixtures/**\n/fixtures/**/.DS_Store\n.cache\nbin/knip\nbin/knip-bun"
  },
  {
    "path": "packages/knip/.release-it.json",
    "content": "{\n  \"$schema\": \"https://unpkg.com/release-it@19/schema/release-it.json\",\n  \"extends\": \"../../.release-it.json\",\n  \"hooks\": {\n    \"before:init\": [\n      \"pnpm lint\",\n      \"pnpm build\",\n      \"pnpm knip\",\n      \"pnpm knip:production\",\n      \"pnpm test\"\n    ]\n  },\n  \"github\": {\n    \"release\": true,\n    \"releaseNotes\": {\n      \"commit\": \"* ${commit.subject} (${sha}){ - thanks @${author.login}!}\",\n      \"excludeMatches\": [\"webpro\"]\n    },\n    \"comments\": {\n      \"submit\": true,\n      \"issue\": \":rocket: _This issue has been resolved in v${version}. See [${releaseName}](${releaseUrl}) for release notes._\\n\\n_Using Knip in a commercial project? Please consider [becoming a sponsor](https://knip.dev/sponsors)._\",\n      \"pr\": \":rocket: _This pull request is included in v${version}. See [${releaseName}](${releaseUrl}) for release notes._\\n\\n_Using Knip in a commercial project? Please consider [becoming a sponsor](https://knip.dev/sponsors)._\"\n    }\n  },\n  \"plugins\": {\n    \"@release-it/bumper\": {\n      \"out\": \"{dist,src}/version.*\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/README.md",
    "content": "<h1 align=\"center\">\n  <br />\n  <a href=\"https://knip.dev\">\n    <img height=\"200\" width=\"200\" src=\"https://knip.dev/favicon.svg\" alt=\"Knip\" />\n  </a>\n  <br />\n  <br />\n</h1>\n\n<div align=\"center\">\n\n[![NPM Version][2]][1] [![NPM Downloads][3]][1] [![GitHub Repo stars][5]][4]\n\n</div>\n\nKnip finds and fixes **unused dependencies, exports and files** in your\nJavaScript and TypeScript projects. Less code and dependencies lead to improved\nperformance, less maintenance and easier refactorings.\n\n- Website: [knip.dev][6]\n- GitHub repo: [webpro-nl/knip][4]\n- Official npm packages: [knip][1], [@knip/create-config][7],\n  [@knip/language-server][8], [@knip/mcp][9]\n- [Knip on the VS Code Marketplace][10], [Knip on the Open VSX Registry][11]\n- [Contributing Guide][12]\n- Follow [@webpro.nl on Bluesky][13] for updates\n- [Sponsor Knip!][14]\n\n## Contributors\n\nSpecial thanks to [the wonderful people who have contributed to Knip][15]!\n\n## Knip\n\n/'knɪp/ means \"(to) cut\" and is [pronounced with a hard \"K\"][16] 🇳🇱\n\n## License\n\nKnip is free and open-source software licensed under the [ISC License][17].\n\nParts of Knip have been inspired by and/or partially copy code from the\nfollowing projects:\n\n- [@npmcli/package-json][18] ([ISC][19])\n- [@pnpm/deps.graph-sequencer][20] ([MIT][21])\n- [file-entry-cache][22] ([MIT][23])\n- [json-parse-even-better-errors][24] ([MIT][25])\n\n[1]: https://www.npmjs.com/package/knip\n[2]: https://img.shields.io/npm/v/knip?color=f56e0f\n[3]: https://img.shields.io/npm/dm/knip?color=f56e0f\n[4]: https://github.com/webpro-nl/knip\n[5]:\n  https://img.shields.io/github/stars/webpro-nl/knip?style=flat-square&color=f56e0f\n[6]: https://knip.dev\n[7]: https://www.npmjs.com/package/@knip/create-config\n[8]: https://www.npmjs.com/package/@knip/language-server\n[9]: https://www.npmjs.com/package/@knip/mcp\n[10]: https://marketplace.visualstudio.com/items?itemName=webpro.vscode-knip\n[11]: https://open-vsx.org/extension/webpro/vscode-knip\n[12]: https://github.com/webpro-nl/knip/blob/main/.github/CONTRIBUTING.md\n[13]: https://bsky.app/profile/webpro.nl\n[14]: https://knip.dev/sponsors\n[15]: https://knip.dev/#created-by-awesome-contributors\n[16]: https://www.youtube.com/watch?v=PE7h7KvQoUI&t=9s\n[17]: ./license\n[18]: https://github.com/npm/package-json\n[19]: https://github.com/npm/package-json/blob/main/LICENSE\n[20]: https://github.com/pnpm/pnpm/tree/main/deps/graph-sequencer\n[21]: https://github.com/pnpm/pnpm/blob/main/LICENSE\n[22]: https://github.com/jaredwray/cacheable/tree/main/packages/file-entry-cache\n[23]:\n  https://github.com/jaredwray/cacheable/blob/main/packages/file-entry-cache/LICENSE\n[24]: https://github.com/npm/json-parse-even-better-errors\n[25]: https://github.com/npm/json-parse-even-better-errors/blob/main/LICENSE.md\n"
  },
  {
    "path": "packages/knip/bin/knip-bun.js",
    "content": "#!/usr/bin/env bun\nimport '../dist/cli.js';\n"
  },
  {
    "path": "packages/knip/bin/knip.js",
    "content": "#!/usr/bin/env node\nimport '../dist/cli.js';\n"
  },
  {
    "path": "packages/knip/fixtures/barrel-namespace-chain/consumer.ts",
    "content": "import * as lib from './lib.ts';\n\nlib.server.protocol.usedExport;\n"
  },
  {
    "path": "packages/knip/fixtures/barrel-namespace-chain/fn-consumer.ts",
    "content": "import * as lib from './lib.ts';\n\nfunction process(arg: unknown) { return arg; }\nprocess(lib);\n"
  },
  {
    "path": "packages/knip/fixtures/barrel-namespace-chain/index.ts",
    "content": "import './consumer.ts';\nimport './opaque-consumer.ts';\nimport './fn-consumer.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/barrel-namespace-chain/lib.ts",
    "content": "export * as server from './server.ts';\nexport { Debug } from './utils.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/barrel-namespace-chain/opaque-consumer.ts",
    "content": "import { Debug } from './lib.ts';\n\nObject.keys(Debug);\n"
  },
  {
    "path": "packages/knip/fixtures/barrel-namespace-chain/package.json",
    "content": "{\n  \"name\": \"@fixtures/barrel-namespace-chain\",\n  \"knip\": {\n    \"tags\": [\"-knipignore\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/barrel-namespace-chain/protocol.ts",
    "content": "export const usedExport = 1;\nexport const unusedExport = 2;\n\n/** @knipignore */\nexport const taggedExport = 3;\n"
  },
  {
    "path": "packages/knip/fixtures/barrel-namespace-chain/server.ts",
    "content": "export * as protocol from './protocol.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/barrel-namespace-chain/utils.ts",
    "content": "export const Debug = {\n  log(msg: string) { console.log(msg); },\n  warn(msg: string) { console.warn(msg); },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/binaries/dir/index.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/binaries/main.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/binaries/require.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/binaries/script.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/catalog-named/index.js",
    "content": "import 'react';\nimport 'vue';\nimport '@ex/press';\n"
  },
  {
    "path": "packages/knip/fixtures/catalog-named/package.json",
    "content": "{\n  \"name\": \"@fixtures/catalog-named\",\n  \"private\": true,\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"dependencies\": {\n    \"react\": \"catalog:\",\n    \"vue\": \"catalog:frontend\",\n    \"@ex/press\": \"catalog:backend\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/catalog-named/pnpm-workspace.yaml",
    "content": "packages:\n  - 'packages/*'\n\ncatalog:\n  react: ^18.0.0\n  lodash: ^4.17.21\n\ncatalogs:\n  frontend:\n    vue: ^3.0.0\n    \"@nu/xt\": ^3.0.0\n  backend:\n    '@ex/press': ^4.18.0\n    fastify: ^4.0.0\n"
  },
  {
    "path": "packages/knip/fixtures/catalog-named-empty/index.js",
    "content": "import 'express';\n"
  },
  {
    "path": "packages/knip/fixtures/catalog-named-empty/package.json",
    "content": "{\n  \"name\": \"@fixtures/catalog-named-empty\",\n  \"private\": true,\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"dependencies\": {\n    \"express\": \"catalog:prod\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/catalog-named-empty/pnpm-workspace.yaml",
    "content": "packages:\n  - 'packages/*'\n\ncatalogs:\n  dev:\n  prod:\n    express: ^4.18.0\n    lodash: ^4.17.21\n"
  },
  {
    "path": "packages/knip/fixtures/catalog-named-package-json/index.js",
    "content": "import 'react';\nimport 'vue';\nimport 'express';\n"
  },
  {
    "path": "packages/knip/fixtures/catalog-named-package-json/package.json",
    "content": "{\n  \"name\": \"@fixtures/catalog-named-package-json\",\n  \"private\": true,\n  \"dependencies\": {\n    \"react\": \"catalog:\",\n    \"vue\": \"catalog:frontend\",\n    \"express\": \"catalog:backend\"\n  },\n  \"workspaces\": {\n    \"packages\": [\n      \"packages/*\"\n    ],\n    \"catalog\": {\n      \"react\": \"^18.0.0\",\n      \"lodash\": \"^4.17.21\"\n    },\n    \"catalogs\": {\n      \"frontend\": {\n        \"vue\": \"^3.0.0\",\n        \"nuxt\": \"^3.0.0\"\n      },\n      \"backend\": {\n        \"express\": \"^4.18.0\",\n        \"fastify\": \"^4.0.0\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/catalog-named-package-json-root/index.js",
    "content": "import 'react';\nimport 'vue';\nimport 'express';\n"
  },
  {
    "path": "packages/knip/fixtures/catalog-named-package-json-root/package.json",
    "content": "{\n  \"name\": \"@fixtures/catalog-named-package-json-root\",\n  \"private\": true,\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"dependencies\": {\n    \"react\": \"catalog:\",\n    \"vue\": \"catalog:frontend\",\n    \"express\": \"catalog:backend\"\n  },\n  \"catalog\": {\n    \"react\": \"^18.0.0\",\n    \"lodash\": \"^4.17.21\"\n  },\n  \"catalogs\": {\n    \"frontend\": {\n      \"vue\": \"^3.0.0\",\n      \"@nu/xt\": \"^3.0.0\"\n    },\n    \"backend\": {\n      \"express\": \"^4.18.0\",\n      \"fastify\": \"^4.0.0\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/catalog-pnpm/package.json",
    "content": "{\n  \"name\": \"@fixtures/catalog-pnpm\",\n  \"private\": true,\n  \"workspaces\": [\n    \"packages/*\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/catalog-pnpm/packages/app/index.ts",
    "content": "import React from 'react';\n\nexport const App = () => React.createElement('div', null, 'Hello World');\n"
  },
  {
    "path": "packages/knip/fixtures/catalog-pnpm/packages/app/package.json",
    "content": "{\n  \"name\": \"app\",\n  \"dependencies\": {\n    \"react\": \"catalog:\"\n  },\n  \"devDependencies\": {\n    \"typescript\": \"catalog:\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/catalog-pnpm/pnpm-workspace.yaml",
    "content": "packages:\n  - 'packages/*'\n\ncatalog:\n  react: ^18.0.0\n  typescript: ^5.0.0\n  lodash: ^4.17.21\n"
  },
  {
    "path": "packages/knip/fixtures/catalog-yarn/.yarnrc.yml",
    "content": "packages:\n  - \"packages/*\"\n\ncatalog:\n  solid-js: 1.9.10\n  typescript: ^5.0.0\n  \"@lo/dash\": ^4.17.21\n"
  },
  {
    "path": "packages/knip/fixtures/catalog-yarn/package.json",
    "content": "{\n  \"name\": \"@fixtures/catalog-yarn\",\n  \"private\": true,\n  \"workspaces\": [\n    \"packages/*\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/catalog-yarn/packages/app/index.ts",
    "content": "import { createSignal } from 'solid-js';\n"
  },
  {
    "path": "packages/knip/fixtures/catalog-yarn/packages/app/package.json",
    "content": "{\n  \"name\": \"app\",\n  \"dependencies\": {\n    \"solid-js\": \"catalog:\"\n  },\n  \"devDependencies\": {\n    \"typescript\": \"catalog:\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/cli/index.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/cli/package.json",
    "content": "{\n  \"name\": \"@fixtures/cli\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/cli-preprocessor/index.js",
    "content": "export default function (options) {\n  console.log('hi from js preprocessor');\n  return options;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/cli-preprocessor/index.ts",
    "content": "export default function (options) {\n  console.log('hi from ts preprocessor');\n  return options;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/cli-preprocessor/package.json",
    "content": "{\n  \"name\": \"@fixtures/cli-preprocessor\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/cli-reporter/index.js",
    "content": "export default function () {\n  console.log('hi from js reporter');\n}\n"
  },
  {
    "path": "packages/knip/fixtures/cli-reporter/index.ts",
    "content": "export default function () {\n  console.log('hi from ts reporter');\n}\n"
  },
  {
    "path": "packages/knip/fixtures/cli-reporter/package.json",
    "content": "{\n  \"name\": \"@fixtures/cli-reporter\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/commonjs/dir/exports.js",
    "content": "exports.add = () => {};\n\nexports.unused = () => {};\n\n// exports = { ignored: true };\n"
  },
  {
    "path": "packages/knip/fixtures/commonjs/dir/mod.js",
    "content": "import 'another-unlisted'; // insane\n\nconst staticResolve = () => {\n  return require('string-literal');\n};\n\nmodule.exports.staticResolve = staticResolve;\n"
  },
  {
    "path": "packages/knip/fixtures/commonjs/dir/mod1.js",
    "content": "function identifier() {}\nmodule.exports = function fn() {};\nmodule.exports.identifier = identifier;\nmodule.exports['identifier2'] = identifier;\n"
  },
  {
    "path": "packages/knip/fixtures/commonjs/dir/mod2.js",
    "content": "function identifier5() {}\nfunction identifier6() {}\n\nmodule.exports = { identifier5, identifier6, identifier7: 1, identifier8: '1' };\n"
  },
  {
    "path": "packages/knip/fixtures/commonjs/dir/mod3.js",
    "content": "function identifier9() {}\nfunction identifier10() {}\n\nmodule.exports = { identifier9, identifier10 };\n"
  },
  {
    "path": "packages/knip/fixtures/commonjs/index.js",
    "content": "require('side-effects');\nrequire('./odd');\nconst path = require('node:path');\nconst { named: renamed } = require('aliased-binding');\nconst defaultName = require('default-identifier');\nconst { named } = require('named-object-binding');\nconst all = require('./dir/mod');\nconst { staticResolve } = require('./dir/mod');\nconst { add } = require('./dir/exports');\nconst ts = require('./ts-ext');\n\nconst dynamicRequire = value => {\n  return require(`./dir/${value}`);\n};\n\nconst templateStringExternal = value => {\n  return require('no-substitution-tpl-literal');\n};\n\nconst templateStringInternal = value => {\n  const baz = require('./dir/mod1');\n  const { identifier } = require('./dir/mod1');\n\n  baz;\n  identifier;\n};\n\nconst requireResolve = value => {\n  return require.resolve('./dir/mod2');\n};\n\nconst requireExportedShorthandsHeuristic = value => {\n  const { identifier9, identifier10 } = require('./dir/mod3');\n  [identifier9, identifier10];\n};\n\nconst staticResolve2 = () => {\n  return require.resolve('string-literal-resolve');\n};\n\nconst staticResolve3 = () => {\n  return require.resolve('resolved');\n};\n\nconst dynamicResolve = () => {\n  return require.resolve(path.join(process.cwd(), 'package.json'));\n};\n\nrenamed;\ndefaultName;\nnamed;\nall;\nstaticResolve;\nadd;\n"
  },
  {
    "path": "packages/knip/fixtures/commonjs/odd.js",
    "content": "require(id);\nrequire('str' + id);\nrequire(`str${id}`);\nrequire.resolve(id);\nrequire.resolve(`str${id}`);\nrequire.resolve('hide' + 'me');\n"
  },
  {
    "path": "packages/knip/fixtures/commonjs/package.json",
    "content": "{\n  \"name\": \"@fixtures/commonjs\",\n  \"dependencies\": {\n    \"resolved\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/commonjs/ts-ext.ts",
    "content": "export default function fn() {}\n\nfn();\n"
  },
  {
    "path": "packages/knip/fixtures/commonjs-tsconfig/dir/exports.js",
    "content": "exports.add = () => {};\n\nexports.unused = () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/commonjs-tsconfig/dir/module1.ts",
    "content": "export function fn1() {\n  return 1;\n}\n\nexport const unused = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/commonjs-tsconfig/dir/module2.ts",
    "content": "export function fn2() {\n  return 2;\n}\n\nexport const unused = 2;\n"
  },
  {
    "path": "packages/knip/fixtures/commonjs-tsconfig/dir/module3.js",
    "content": "export function fn3() {\n  return 3;\n}\n\nexport const unused = 3;\n"
  },
  {
    "path": "packages/knip/fixtures/commonjs-tsconfig/dir/script1.js",
    "content": "import 'another-unlisted'; // insane\n\nconst staticResolve = () => {\n  return require('string-literal');\n};\n\nmodule.exports.staticResolve = staticResolve;\n"
  },
  {
    "path": "packages/knip/fixtures/commonjs-tsconfig/dir/script2.js",
    "content": "function identifier() {}\nmodule.exports = function fn() {};\nmodule.exports.identifier = identifier;\nmodule.exports['identifier2'] = identifier;\n"
  },
  {
    "path": "packages/knip/fixtures/commonjs-tsconfig/dir/script3.js",
    "content": "function identifier5() {}\nfunction identifier6() {}\n\nmodule.exports = { identifier5, identifier6, identifier7: 1, identifier8: '1' };\n"
  },
  {
    "path": "packages/knip/fixtures/commonjs-tsconfig/index.js",
    "content": "const all = require('./dir/script1');\nconst { staticResolve } = require('./dir/script1');\nconst { add } = require('./dir/exports');\nconst ts = require('./ts-ext');\n\nconst templateStringInternal = value => {\n  const baz = require(`./dir/script2`);\n  baz;\n};\n\nconst requireResolve = value => {\n  return require.resolve('./dir/script3');\n};\n\nrenamed;\ndefaultName;\nnamed;\nall;\nstaticResolve;\nadd;\nts;\n"
  },
  {
    "path": "packages/knip/fixtures/commonjs-tsconfig/main.ts",
    "content": "const { fn1 } = require('./dir/module1');\nconst { fn2 } = require('./dir/module2.ts');\nconst { fn3 } = require('./dir/module3.js');\n\nfn1;\nfn2;\nfn3;\n"
  },
  {
    "path": "packages/knip/fixtures/commonjs-tsconfig/package.json",
    "content": "{\n  \"name\": \"@fixtures/commonjs-tsconfig\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/commonjs-tsconfig/ts-ext.ts",
    "content": "export default function fn() {}\n\nfn();\n"
  },
  {
    "path": "packages/knip/fixtures/commonjs-tsconfig/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"module\": \"commonjs\",\n    \"allowJs\": true\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/compact-reporter/knip.json",
    "content": "{\n  \"ignoreBinaries\": [\"unknown-binary\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/compact-reporter/package.json",
    "content": "{\n  \"name\": \"@fixtures/compact-reporter\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"test\": \"unknown-binary\"\n  },\n  \"dependencies\": {\n    \"unused-dep\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/compilers/Component.vue",
    "content": "<script setup lang=\"ts\">\nimport { Enum } from './enum';\nEnum.Member;\n</script>\n"
  },
  {
    "path": "packages/knip/fixtures/compilers/component.tsx",
    "content": "export default () => <div>1</div>;\n"
  },
  {
    "path": "packages/knip/fixtures/compilers/enum.ts",
    "content": "export enum Enum {\n  Member = 'member',\n}\n"
  },
  {
    "path": "packages/knip/fixtures/compilers/grid.css",
    "content": "@import \"./grid.css\";\n"
  },
  {
    "path": "packages/knip/fixtures/compilers/index.ts",
    "content": "import identifier from './module.mdx';\nimport Component from './Component.vue';\nimport { createApp } from 'vue';\n\nidentifier;\ncreateApp(Component);\n"
  },
  {
    "path": "packages/knip/fixtures/compilers/knip.ts",
    "content": "import '@mdx-js/mdx';\n\nexport default {\n  compilers: {\n    md: (_text, path) => {\n      if (!path) throw new Error('Path not passed to compiler');\n      return '';\n    },\n    css: async (text: string) => {\n      return [...text.matchAll(/(?<=@)import[^;]+/g)].join('\\n');\n    },\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/compilers/module.mdx",
    "content": "import Component from './component';\nimport Docs from './readme.md';\nimport Styles from './styles.css';\n\n# Header\n\n<Component />\n\n<Docs />\n\nUsing a top-level `import type` declaration (`import type { A, B } from 'fake-module'`) ensures the entire import statement is removed during transpilation.\n\nYou can also use `import { something } from 'another-fake'` inline.\n\nMultiple inline codes: `import x from 'x'` and `import y from 'y'` on same line.\n\nNested backticks won't happen but edge case: `import Default from \"double-quotes\"`.\n\n```js\nimport { fenced } from 'fenced-module';\nimport * as ns from 'namespace-module';\n\nconst x = 1;\n```\n\n```typescript\nimport type { Type } from 'typed-module';\nexport const y = 2;\n```\n\n```\nimport { noLang } from 'no-lang-module';\n```\n\nReal import after fenced block should still work (see actual imports at top).\n"
  },
  {
    "path": "packages/knip/fixtures/compilers/package.json",
    "content": "{\n  \"name\": \"@fixtures/compilers\",\n  \"devDependencies\": {\n    \"@mdx-js/mdx\": \"*\",\n    \"vue\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/compilers/readme.md",
    "content": "# Header\n"
  },
  {
    "path": "packages/knip/fixtures/compilers/styles.css",
    "content": "@import \"./grid.css\";\n"
  },
  {
    "path": "packages/knip/fixtures/compilers/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2015\",\n    \"jsx\": \"preserve\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/compilers/unused.css",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/compilers/unused.md",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/compilers-manual/component.tsx",
    "content": "export default () => <div>1</div>;\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-manual/index.ts",
    "content": "import identifier from './module.mdx';\nimport './styles.scss';\n\nidentifier;\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-manual/knip.ts",
    "content": "export default {\n  compilers: {\n    mdx: true,\n    scss: true,\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-manual/module.mdx",
    "content": "import Component from './component';\n\n# Header\n\n<Component />\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-manual/package.json",
    "content": "{\n  \"name\": \"@fixtures/compilers-manual\",\n  \"devDependencies\": {}\n}\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-manual/styles.scss",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/compilers-manual/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2015\",\n    \"jsx\": \"preserve\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-prisma/package.json",
    "content": "{\n  \"name\": \"@fixtures/compilers-prisma\",\n  \"scripts\": {\n    \"prisma:generate\": \"prisma generate\"\n  },\n  \"devDependencies\": {\n    \"prisma\": \"*\",\n    \"prisma-json-types-generator\": \"*\",\n    \"prisma-zod-generator\": \"*\",\n    \"prisma-openapi\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-prisma/prisma/schema.prisma",
    "content": "generator client {\n  provider     = \"prisma-client\"\n  output       = \"../src/generated/prisma\"\n  engineType   = \"client\"\n  moduleFormat = \"esm\"\n}\n\ngenerator zod {\n  provider = \"prisma-zod-generator\"\n  output   = \"../src/generated/prisma-zod\"\n}\n\ngenerator json {\n  provider = \"prisma-json-types-generator\"\n}\n\ndatasource db {\n  provider = \"postgresql\"\n  url      = env(\"DATABASE_URL\")\n}\n\nmodel User {\n  id    Int     @id @default(autoincrement())\n  email String  @unique\n  name  String?\n}\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-scss/_grid.scss",
    "content": "$max-width: 1200px;\n$gutter: 16px;\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-scss/_partial.scss",
    "content": "$border-radius: 4px;\n$spacing: 1rem;\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-scss/buttons.scss",
    "content": "@use \"pkg:@fortawesome/fontawesome-free/scss/fontawesome\";\n@use \"./variables\" as *;\n\n.button {\n  background-color: $primary;\n  padding: 0.5rem 1rem;\n  border: none;\n  border-radius: 4px;\n\n  &:hover {\n    background-color: darken($primary, 10%);\n  }\n\n  &--secondary {\n    background-color: $secondary;\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-scss/cards.sass",
    "content": "@use \"./variables\" as *\n\n.card\n  background: white\n  border-radius: 8px\n  padding: 1rem\n  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1)\n\n  &__header\n    border-bottom: 1px solid $secondary\n    padding-bottom: 0.5rem\n\n  &__body\n    padding: 1rem 0\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-scss/components.scss",
    "content": ".component {\n  color: red;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-scss/indented.sass",
    "content": "@use \"./mixins\"\n@use './variables' as vars\n@import \"./legacy\"\n@forward \"./utils\"\n\n.indented\n  color: vars.$secondary\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-scss/index.ts",
    "content": "import './styles.scss';\nimport './indented.sass';\nimport './buttons.scss';\nimport './cards.sass';\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-scss/legacy.scss",
    "content": ".legacy-component {\n  display: block;\n  margin: 0 auto;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-scss/mixins.scss",
    "content": "@mixin center {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-scss/package.json",
    "content": "{\n  \"name\": \"@fixtures/compilers-scss\",\n  \"dependencies\": {\n    \"@fortawesome/fontawesome-free\": \"*\"\n  },\n  \"devDependencies\": {\n    \"bootstrap\": \"*\",\n    \"sass\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-scss/styles.scss",
    "content": "@use \"sass:math\";\n@use \"pkg:@fortawesome/fontawesome-free\";\n@use \"pkg:bootstrap/scss/bootstrap\";\n@use \"components\";\n@use './variables' as vars;\n@use \"./theme\" as *;\n@use \"./partial\";\n@use \"./_grid\";\n@forward \"./mixins\";\n@forward \"./utils\" as util-*;\n@import \"./legacy\";\n\n.icon {\n  @include fa-icon;\n  color: vars.$primary;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-scss/theme.scss",
    "content": "$font-size-base: 1rem;\n$line-height-base: 1.5;\n\nbody {\n  font-size: $font-size-base;\n  line-height: $line-height-base;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-scss/unused.scss",
    "content": ".unused {\n  display: none;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-scss/utils.scss",
    "content": "@mixin clearfix {\n  &::after {\n    content: \"\";\n    display: table;\n    clear: both;\n  }\n}\n\n@mixin visually-hidden {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  border: 0;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-scss/variables.scss",
    "content": "$primary: #007bff;\n$secondary: #6c757d;\n$success: #28a745;\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-tailwind/components.css",
    "content": "@import \"./unused.css\";\n\n.component {\n  @apply bg-blue-500;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-tailwind/index.ts",
    "content": "import './styles.css';\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-tailwind/local-js-plugin.js",
    "content": "export default function () {}\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-tailwind/local-ts-plugin.ts",
    "content": "import { heroui } from '@heroui/react';\n\nexport default heroui();\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-tailwind/package.json",
    "content": "{\n  \"name\": \"@fixtures/compilers-tailwind\",\n  \"dependencies\": {\n    \"tailwindcss\": \"*\"\n  },\n  \"devDependencies\": {\n    \"@heroui/react\": \"*\",\n    \"@tailwindcss/forms\": \"*\",\n    \"@tailwindcss/typography\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-tailwind/styles.css",
    "content": "@import \"tailwindcss\";\n@import \"./components.css\";\n\n@plugin \"@tailwindcss/forms\";\n@plugin \"@tailwindcss/typography\";\n\n@plugin \"./local-js-plugin.js\";\n@plugin \"./local-ts-plugin.ts\";\n@plugin \"./missing-plugin.ts\";\n"
  },
  {
    "path": "packages/knip/fixtures/compilers-tailwind/unused.css",
    "content": ".unused {\n  color: red;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/config-js-async/dangling.js",
    "content": "export const unusedFile = true;\n"
  },
  {
    "path": "packages/knip/fixtures/config-js-async/index.js",
    "content": "import * as MyNamespace from './my-namespace';\n\nexport const b = MyNamespace.y;\n"
  },
  {
    "path": "packages/knip/fixtures/config-js-async/knip.config.js",
    "content": "module.exports = async () => ({\n  ignore: ['dangling.js'],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/config-js-async/my-namespace.js",
    "content": "const x = 1;\nexport const y = () => x;\n"
  },
  {
    "path": "packages/knip/fixtures/config-js-async/package.json",
    "content": "{\n  \"name\": \"@fixtures/config-js-async\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/config-js-flat/dangling.js",
    "content": "export const unusedFile = true;\n"
  },
  {
    "path": "packages/knip/fixtures/config-js-flat/index.js",
    "content": "import * as MyNamespace from './my-namespace';\n\nexport const b = MyNamespace.y;\n"
  },
  {
    "path": "packages/knip/fixtures/config-js-flat/knip.js",
    "content": "module.exports = {\n  ignore: ['dangling.js'],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/config-js-flat/my-namespace.js",
    "content": "const x = 1;\nexport const y = () => x;\n"
  },
  {
    "path": "packages/knip/fixtures/config-js-flat/package.json",
    "content": "{\n  \"name\": \"@fixtures/config-js-flat\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/config-json/dangling.js",
    "content": "export const unusedFile = true;\n"
  },
  {
    "path": "packages/knip/fixtures/config-json/index.js",
    "content": "import * as MyNamespace from './my-namespace';\n\nexport const b = MyNamespace.y;\n"
  },
  {
    "path": "packages/knip/fixtures/config-json/knip.json",
    "content": "{\n  \"ignore\": [\"dangling.js\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/config-json/my-namespace.js",
    "content": "const x = 1;\nexport const y = () => x;\n"
  },
  {
    "path": "packages/knip/fixtures/config-json/package.json",
    "content": "{\n  \"name\": \"@fixtures/config-json\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/config-mjs-async/dangling.js",
    "content": "export const unusedFile = true;\n"
  },
  {
    "path": "packages/knip/fixtures/config-mjs-async/index.js",
    "content": "import * as MyNamespace from './my-namespace';\n\nexport const b = MyNamespace.y;\n"
  },
  {
    "path": "packages/knip/fixtures/config-mjs-async/knip.mjs",
    "content": "const config = async () => ({\n  ignore: ['dangling.js'],\n});\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/config-mjs-async/my-namespace.js",
    "content": "const x = 1;\nexport const y = () => x;\n"
  },
  {
    "path": "packages/knip/fixtures/config-mjs-async/package.json",
    "content": "{\n  \"name\": \"@fixtures/config-mjs-async\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/config-mjs-flat/dangling.js",
    "content": "export const unusedFile = true;\n"
  },
  {
    "path": "packages/knip/fixtures/config-mjs-flat/index.js",
    "content": "import * as MyNamespace from './my-namespace';\n\nexport const b = MyNamespace.y;\n"
  },
  {
    "path": "packages/knip/fixtures/config-mjs-flat/knip.mjs",
    "content": "const config = {\n  ignore: ['dangling.js'],\n};\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/config-mjs-flat/my-namespace.js",
    "content": "const x = 1;\nexport const y = () => x;\n"
  },
  {
    "path": "packages/knip/fixtures/config-mjs-flat/package.json",
    "content": "{\n  \"name\": \"@fixtures/config-mjs-flat\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/config-package-json/dangling.js",
    "content": "export const unusedFile = true;\n"
  },
  {
    "path": "packages/knip/fixtures/config-package-json/index.js",
    "content": "import * as MyNamespace from './my-namespace';\n\nexport const b = MyNamespace.y;\n"
  },
  {
    "path": "packages/knip/fixtures/config-package-json/my-namespace.js",
    "content": "const x = 1;\nexport const y = () => x;\n"
  },
  {
    "path": "packages/knip/fixtures/config-package-json/package.json",
    "content": "{\n  \"name\": \"@fixtures/config-package-json\",\n  \"knip\": {\n    \"ignore\": [\n      \"dangling.js\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/config-ts-async/dangling.js",
    "content": "export const unusedFile = true;\n"
  },
  {
    "path": "packages/knip/fixtures/config-ts-async/index.js",
    "content": "import * as MyNamespace from './my-namespace';\n\nexport const b = MyNamespace.y;\n"
  },
  {
    "path": "packages/knip/fixtures/config-ts-async/knip.ts",
    "content": "import type { KnipConfig } from '../../src/index';\n\nconst config = async (): Promise<KnipConfig> => ({\n  ignore: ['dangling.js'],\n});\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/config-ts-async/my-namespace.js",
    "content": "const x = 1;\nexport const y = () => x;\n"
  },
  {
    "path": "packages/knip/fixtures/config-ts-async/package.json",
    "content": "{\n  \"name\": \"@fixtures/config-ts-async\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/config-ts-async/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"types\": [\"node\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/config-ts-flat/dangling.js",
    "content": "export const unusedFile = true;\n"
  },
  {
    "path": "packages/knip/fixtures/config-ts-flat/index.js",
    "content": "import * as MyNamespace from './my-namespace';\n\nexport const b = MyNamespace.y;\n"
  },
  {
    "path": "packages/knip/fixtures/config-ts-flat/knip.config.ts",
    "content": "import type { KnipConfig } from '../../src/index';\n\nconst config: KnipConfig = {\n  ignore: ['dangling.js'],\n};\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/config-ts-flat/my-namespace.js",
    "content": "const x = 1;\nexport const y = () => x;\n"
  },
  {
    "path": "packages/knip/fixtures/config-ts-flat/package.json",
    "content": "{\n  \"name\": \"@fixtures/config-ts-flat\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/config-ts-flat/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"types\": [\"node\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/config-ts-function/dangling.js",
    "content": "export const unusedFile = true;\n"
  },
  {
    "path": "packages/knip/fixtures/config-ts-function/index.js",
    "content": "import * as MyNamespace from './my-namespace';\n\nexport const b = MyNamespace.y;\n"
  },
  {
    "path": "packages/knip/fixtures/config-ts-function/knip.ts",
    "content": "import type { KnipConfig } from '../../src/index';\n\nconst config = (): KnipConfig => ({\n  ignore: ['dangling.js'],\n});\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/config-ts-function/my-namespace.js",
    "content": "const x = 1;\nexport const y = () => x;\n"
  },
  {
    "path": "packages/knip/fixtures/config-ts-function/package.json",
    "content": "{\n  \"name\": \"@fixtures/config-ts-function\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/config-ts-function/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"types\": [\"node\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/config-yaml/dangling.js",
    "content": "export const unusedFile = true;\n"
  },
  {
    "path": "packages/knip/fixtures/config-yaml/index.js",
    "content": "import * as MyNamespace from './my-namespace';\n\nexport const b = MyNamespace.y;\n"
  },
  {
    "path": "packages/knip/fixtures/config-yaml/knip.yaml",
    "content": "ignore:\n  - 'dangling.js'"
  },
  {
    "path": "packages/knip/fixtures/config-yaml/my-namespace.js",
    "content": "const x = 1;\nexport const y = () => x;\n"
  },
  {
    "path": "packages/knip/fixtures/config-yaml/package.json",
    "content": "{\n  \"name\": \"@fixtures/config-yaml\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/configuration-hints/knip.json",
    "content": "{\n  \"$schema\": \"https://unpkg.com/knip@6/schema.json\",\n  \"entry\": [\"src/entry.js\"],\n  \"project\": [\"src/**\"],\n  \"workspaces\": {}\n}\n"
  },
  {
    "path": "packages/knip/fixtures/configuration-hints/lib/index.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/configuration-hints/lib/package.json",
    "content": "{\n  \"name\": \"@fixtures/configuration-hints__lib\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/configuration-hints/package.json",
    "content": "{\n  \"name\": \"@fixtures/configuration-hints\",\n  \"workspaces\": [\n    \"lib\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/configuration-hints/src/entry.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/configuration-hints-plugin/create-typescript-app.config.js",
    "content": "export default {};\n"
  },
  {
    "path": "packages/knip/fixtures/configuration-hints-plugin/knip.json",
    "content": "{\n  \"entry\": [\"create-typescript-app.config.js\", \"svgo.config.mjs\"],\n  \"project\": [\"create-typescript-app.config.js\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/configuration-hints-plugin/package.json",
    "content": "{\n  \"name\": \"@fixtures/configuration-hints-plugin\",\n  \"dependencies\": {\n    \"create-typescript-app\": \"*\",\n    \"svgo\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/configuration-hints-plugin/svgo.config.mjs",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/configuration-hints-plugin-override/create-typescript-app.config.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/configuration-hints-plugin-override/knip.json",
    "content": "{\n  \"$schema\": \"https://unpkg.com/knip@6/schema.json\",\n  \"entry\": [\"svgo.config.js\", \"yarn.config.cjs\"],\n  \"create-typescript-app\": {\n    \"entry\": [\"create-typescript-app.config.ts\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/configuration-hints-plugin-override/package.json",
    "content": "{\n  \"name\": \"@fixtures/configuration-hints-plugin-override\",\n  \"dependencies\": {\n    \"create-typescript-app\": \"*\",\n    \"svgo\": \"*\",\n    \"yarn\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/configuration-hints-plugin-override/svgo.config.js",
    "content": "export default {};\n"
  },
  {
    "path": "packages/knip/fixtures/configuration-hints-plugin-override/yarn.config.cjs",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/configuration-hints2/knip.json",
    "content": "{\n  \"$schema\": \"https://unpkg.com/knip@6/schema.json\",\n  \"entry\": [\"src/entry.js\", \"lib/index.js\"],\n  \"project\": [\"src/**\"],\n  \"workspaces\": {\n    \".\": {\n      \"entry\": [\"src/entry.js\", \"lib/index.js\"],\n      \"project\": [\"src/**\", \"lib/**\"]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/configuration-hints2/lib/index.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/configuration-hints2/lib/package.json",
    "content": "{\n  \"name\": \"@fixtures/configuration-hints__lib\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/configuration-hints2/package.json",
    "content": "{\n  \"name\": \"@fixtures/configuration-hints\",\n  \"workspaces\": [\n    \"lib\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/configuration-hints2/src/entry.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/cross-workspace-inputs/.github/workflows/test.yml",
    "content": "on: push\n\njobs:\n  integration:\n    runs-on: ubuntu-latest\n    steps:\n      - run: playwright test -c playwright.e2e.config.ts\n        working-directory: e2e\n"
  },
  {
    "path": "packages/knip/fixtures/cross-workspace-inputs/components/package.json",
    "content": "{\n  \"name\": \"@fixtures/cross-workspace-inputs__components\",\n  \"devDependencies\": {\n    \"vitest\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/cross-workspace-inputs/components/vitest.components.config.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/cross-workspace-inputs/e2e/package.json",
    "content": "{\n  \"name\": \"@fixtures/cross-workspace-inputs__e2e\",\n  \"devDependencies\": {\n    \"@playwright/test\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/cross-workspace-inputs/e2e/playwright.e2e.config.ts",
    "content": "const config = {\n  testDir: 'tests',\n  testMatch: ['**/*.ts'],\n};\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/cross-workspace-inputs/e2e/tests/feature.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/cross-workspace-inputs/knip.json",
    "content": "{}\n"
  },
  {
    "path": "packages/knip/fixtures/cross-workspace-inputs/package.json",
    "content": "{\n  \"name\": \"@fixtures/cross-workspace-inputs\",\n  \"workspaces\": [\n    \"e2e\",\n    \"components\"\n  ],\n  \"scripts\": {\n    \"test\": \"yarn --cwd components vitest -c vitest.components.config.ts\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/custom-paths-workspaces/index.ts",
    "content": "import anything from '~/my-module';\nanything;\n"
  },
  {
    "path": "packages/knip/fixtures/custom-paths-workspaces/knip.json",
    "content": "{\n  \"$schema\": \"https://unpkg.com/knip@2/schema.json\",\n  \"workspaces\": {\n    \".\": {\n      \"paths\": {\n        \"~/*\": [\"./*\"],\n        \"@lib\": [\"./lib/index.ts\"],\n        \"@lib/*\": [\"./lib/*\"]\n      }\n    },\n    \"ws\": {\n      \"paths\": {\n        \"#utilities/*\": [\"./util/*\"],\n        \"~images/*\": [\"./assets/img/*\"],\n        \"lib/*\": [\"./lib/*\"]\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/custom-paths-workspaces/lib/fn.ts",
    "content": "export default () => void 0;\n"
  },
  {
    "path": "packages/knip/fixtures/custom-paths-workspaces/lib/index.ts",
    "content": "export default () => void 0;\n"
  },
  {
    "path": "packages/knip/fixtures/custom-paths-workspaces/my-module.ts",
    "content": "import index from '@lib';\nimport fn from '@lib/fn';\nindex;\nfn;\n"
  },
  {
    "path": "packages/knip/fixtures/custom-paths-workspaces/package.json",
    "content": "{\n  \"name\": \"@fixtures/custom-paths-workspaces\",\n  \"workspaces\": [\n    \"ws\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/custom-paths-workspaces/ws/index.ts",
    "content": "import main from 'lib/main';\nimport lang from '#utilities/lang';\nimport svg from '~images/logo.svg';\nmain;\nlang;\nsvg;\n"
  },
  {
    "path": "packages/knip/fixtures/custom-paths-workspaces/ws/lib/main.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/custom-paths-workspaces/ws/package.json",
    "content": "{\n  \"name\": \"@fixtures/custom-paths-workspaces__ws\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/custom-paths-workspaces/ws/util/lang.ts",
    "content": "export default () => void 0;\n"
  },
  {
    "path": "packages/knip/fixtures/definitely-typed/index.ts",
    "content": "import micromatch from 'micromatch';\nimport anything from 'next';\nimport type { Things } from '@types/without-the-package';\nimport type { Expression } from 'estree';\nimport type { Schema } from 'type-only-production-types';\n"
  },
  {
    "path": "packages/knip/fixtures/definitely-typed/package.json",
    "content": "{\n  \"name\": \"@fixtures/definitely-typed\",\n  \"scripts\": {\n    \"test\": \"mocha\"\n  },\n  \"dependencies\": {\n    \"next\": \"*\",\n    \"micromatch\": \"*\",\n    \"type-only-production-types\": \"*\"\n  },\n  \"devDependencies\": {\n    \"@types/estree\": \"*\",\n    \"@types/micromatch\": \"*\",\n    \"@types/mocha\": \"*\",\n    \"@types/react-dom\": \"*\",\n    \"@types/node\": \"*\",\n    \"@types/unused\": \"*\",\n    \"@types/without-the-package\": \"*\",\n    \"mocha\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/dependencies/entry.ts",
    "content": "import is from '@sindresorhus/is';\nimport has, { program } from './my-module.js';\n\nconst what = is(has);\n\nconst match = typeof program === 'function';\n\nwhat;\nmatch;\n"
  },
  {
    "path": "packages/knip/fixtures/dependencies/knip.json",
    "content": "{\n  \"entry\": [\"entry.ts!\"],\n  \"project\": [\"*.ts!\"],\n  \"ignoreDependencies\": [\"stream\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/dependencies/my-module.ts",
    "content": "import has from 'has';\nimport JSONStream from 'JSONStream';\n\nJSONStream;\n\nasync function main() {\n  return [require('ansi-regex'), import('ansi-regex')];\n}\n\nexport const program = main();\n\nexport default has(Object.prototype, 'hasOwnProperty');\n"
  },
  {
    "path": "packages/knip/fixtures/dependencies/package.json",
    "content": "{\n  \"name\": \"@fixtures/dependencies\",\n  \"scripts\": {\n    \"start\": \"start-server\",\n    \"test\": \"jest\"\n  },\n  \"dependencies\": {\n    \"@tootallnate/once\": \"*\",\n    \"@sindresorhus/is\": \"*\",\n    \"fs-extra\": \"*\",\n    \"has\": \"*\",\n    \"stream\": \"*\",\n    \"JSONStream\": \"*\"\n  },\n  \"peerDependencies\": {\n    \"ansi-regex\": \"*\",\n    \"jquery\": \"*\"\n  },\n  \"optionalDependencies\": {\n    \"fs-optional\": \"*\"\n  },\n  \"devDependencies\": {\n    \"mocha\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/dependencies/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"allowJs\": true,\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/dependencies/unused-module.ts",
    "content": "import { EventEmitter } from 'node:events';\nimport once from '@tootallnate/once';\n\nconst emitter = new EventEmitter();\n\nsetTimeout(() => emitter.emit('event', 'name'), 1);\n\nconst result = await once(emitter, 'event');\nresult;\n"
  },
  {
    "path": "packages/knip/fixtures/dependencies-types/index.ts",
    "content": "import { meta } from '@eslint/js';\nimport { Ajv } from 'ajv';\n\nmeta;\nAjv;\n"
  },
  {
    "path": "packages/knip/fixtures/dependencies-types/package.json",
    "content": "{\n  \"name\": \"@fixtures/dependencies-types\",\n  \"dependencies\": {\n    \"@eslint/js\": \"^9.20.0\",\n    \"ajv\": \"^8.17.1\"\n  },\n  \"devDependencies\": {\n    \"@types/ajv\": \"^0.0.5\",\n    \"@types/eslint__js\": \"^8.42.3\",\n    \"@types/node\": \"^20.14.8\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/dts/assets.d.ts",
    "content": "/// <reference path=\"./index.d.ts\" />\n\ndeclare module '*.svg';\n\ndeclare module '*.html?raw' {\n  const value: string;\n  export default value;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/dts/block.html",
    "content": "<div>content</div>\n"
  },
  {
    "path": "packages/knip/fixtures/dts/index.ts",
    "content": "import b from './block.html?raw';\nimport s from './image.svg';\nimport n from './normal';\nimport type { MyConfig } from './types.js';\nimport './types';\nimport { Palette } from './module-augmentation';\n\nb;\ns;\nn;\nPalette.red;\n"
  },
  {
    "path": "packages/knip/fixtures/dts/module-augmentation.ts",
    "content": "interface AppTheme {\n  color: string;\n}\n\ndeclare module '@org/ui' {\n  export interface Theme extends AppTheme {}\n}\n\nexport namespace Palette {\n  export const red = '#f00';\n  export const blue = '#00f';\n}\n"
  },
  {
    "path": "packages/knip/fixtures/dts/normal.ts",
    "content": "export default 1;\n"
  },
  {
    "path": "packages/knip/fixtures/dts/package.json",
    "content": "{\n  \"name\": \"@fixtures/dts\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/dts/svg.d.ts",
    "content": "declare module '*.svg' {\n  export const ReactComponent: SVGElement;\n  const src: string;\n  export default src;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/dts/tsconfig.json",
    "content": "{}\n"
  },
  {
    "path": "packages/knip/fixtures/dts/types.d.ts",
    "content": "export interface MyConfig {\n  name: string;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/dts-baseurl-implicit-relative/package.json",
    "content": "{\n  \"name\": \"@fixtures/dts-baseurl-implicit-relative\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/dts-baseurl-implicit-relative/src/components/Button.d.ts",
    "content": "export const Button: () => null;\n"
  },
  {
    "path": "packages/knip/fixtures/dts-baseurl-implicit-relative/src/components/Button.jsx",
    "content": "export const Button = () => null;\n"
  },
  {
    "path": "packages/knip/fixtures/dts-baseurl-implicit-relative/src/dir/subdir/index.d.ts",
    "content": "const h: () => number;\nexport default h;\n"
  },
  {
    "path": "packages/knip/fixtures/dts-baseurl-implicit-relative/src/dir/subdir/index.js",
    "content": "const h = () => {\n  return 1;\n};\nexport default h;\n"
  },
  {
    "path": "packages/knip/fixtures/dts-baseurl-implicit-relative/src/index.ts",
    "content": "import h from 'dir/subdir';\nimport { fn } from 'utils/fn';\nimport { obj } from 'utils/obj.cjs';\nimport { str } from 'utils/str.mjs';\nimport { Button } from 'components/Button';\n\nh;\nfn;\nobj;\nstr;\nButton;\n"
  },
  {
    "path": "packages/knip/fixtures/dts-baseurl-implicit-relative/src/utils/fn.d.ts",
    "content": "export const fn: () => void;\n"
  },
  {
    "path": "packages/knip/fixtures/dts-baseurl-implicit-relative/src/utils/fn.js",
    "content": "export const fn = () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/dts-baseurl-implicit-relative/src/utils/obj.cjs",
    "content": "module.exports.obj = () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/dts-baseurl-implicit-relative/src/utils/obj.d.cts",
    "content": "export const obj: () => void;\n"
  },
  {
    "path": "packages/knip/fixtures/dts-baseurl-implicit-relative/src/utils/str.d.mts",
    "content": "export const str: () => void;\n"
  },
  {
    "path": "packages/knip/fixtures/dts-baseurl-implicit-relative/src/utils/str.mjs",
    "content": "export const str = () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/dts-baseurl-implicit-relative/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \"src\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/dts-compiled/knip.ts",
    "content": "import type { KnipConfig } from 'knip';\n\nconst config: KnipConfig = {\n  compilers: {\n    graphql: () => '',\n  },\n};\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/dts-compiled/package.json",
    "content": "{\n  \"name\": \"@fixtures/dts-compiled\",\n  \"private\": true,\n  \"type\": \"module\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/dts-compiled/src/App.tsx",
    "content": "import ExampleQuery from './ExampleQuery.graphql';\n\nfunction App() {\n  return (\n    <>\n      <h1>Vite + React</h1>\n      <pre>\n        <code>{JSON.stringify(ExampleQuery, null, 2)}</code>\n      </pre>\n    </>\n  );\n}\n\nexport default App;\n"
  },
  {
    "path": "packages/knip/fixtures/dts-compiled/src/ExampleQuery.graphql",
    "content": "query ExampleQuery {\n  example {\n    name\n    description\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/dts-compiled/src/ExampleQuery.graphql.d.ts",
    "content": "import * as Types from './types';\n\nexport type ExampleQueryQueryVariables = Types.Exact<{ [key: string]: never }>;\n\nexport type ExampleQueryQuery = {\n  __typename?: 'Query';\n  example?: { __typename?: 'Example'; name?: string | null; description?: string | null } | null;\n};\n"
  },
  {
    "path": "packages/knip/fixtures/dts-compiled/src/UnusedQuery.graphql",
    "content": "query UnusedQuery {\n  example {\n    name\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/dts-compiled/src/UnusedQuery.graphql.d.ts",
    "content": "import * as Types from './types';\n\nexport type UnusedQueryQueryVariables = Types.Exact<{ [key: string]: never }>;\n\nexport type UnusedQueryQuery = {\n  __typename?: 'Query';\n  example?: { __typename?: 'Example'; name?: string | null } | null;\n};\n"
  },
  {
    "path": "packages/knip/fixtures/dts-compiled/src/main.tsx",
    "content": "import App from './App.tsx';\nApp;\n"
  },
  {
    "path": "packages/knip/fixtures/dts-compiled/src/types.ts",
    "content": "export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };\n"
  },
  {
    "path": "packages/knip/fixtures/dts-compiled/tsconfig.json",
    "content": "{}\n"
  },
  {
    "path": "packages/knip/fixtures/duplicate-dependencies/index.ts",
    "content": "import ts from 'typescript';\n\nts;\n"
  },
  {
    "path": "packages/knip/fixtures/duplicate-dependencies/knip.json",
    "content": "{\n  \"entry\": [\"index.ts!\"],\n  \"project\": [\"*.ts!\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/duplicate-dependencies/package.json",
    "content": "{\n  \"name\": \"@fixtures/duplicate-dependencies\",\n  \"dependencies\": {\n    \"typescript\": \"*\"\n  },\n  \"devDependencies\": {\n    \"typescript\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/duplicate-exports-alias/helpers.ts",
    "content": "export const isAlias = true;\n\n/** @alias */\nexport default isAlias;\n\nexport const isUntagged = true;\n\nexport const isUntaggedAlias = isUntagged;\n\nexport { reExportedValue } from './reexported';\n\nexport const reExportedAlias = reExportedValue;\n"
  },
  {
    "path": "packages/knip/fixtures/duplicate-exports-alias/index.ts",
    "content": "import { isAlias, reExportedAlias } from './helpers';\nimport { handler } from './specifier-default';\nisAlias;\nreExportedAlias;\nhandler;\n"
  },
  {
    "path": "packages/knip/fixtures/duplicate-exports-alias/package.json",
    "content": "{\n  \"name\": \"@fixtures/duplicate-exports-alias\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/duplicate-exports-alias/reexported.ts",
    "content": "export const reExportedValue = true;\n"
  },
  {
    "path": "packages/knip/fixtures/duplicate-exports-alias/specifier-default.ts",
    "content": "function handler() {\n  return true;\n}\n\nexport { handler };\n\nexport default handler;\n"
  },
  {
    "path": "packages/knip/fixtures/empty-main/index.ts",
    "content": "export const main = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/empty-main/knip.json",
    "content": "{\n  \"entry\": [\"index.ts\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/empty-main/package.json",
    "content": "{\n  \"name\": \"@fixtures/empty-main\",\n  \"main\": \"\",\n  \"module\": \"\",\n  \"types\": \"\",\n  \"bin\": \"\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/entry-exports-enum-members/fruit.ts",
    "content": "import type { Tree } from './tree';\n\nexport enum Fruit {\n  apple = 'apple',\n  orange = 'orange',\n}\n\nexport interface Farmer {\n  plants: Tree[];\n}\n"
  },
  {
    "path": "packages/knip/fixtures/entry-exports-enum-members/index.ts",
    "content": "export { Fruit, Farmer } from './fruit';\nexport { Tree } from './tree';\n"
  },
  {
    "path": "packages/knip/fixtures/entry-exports-enum-members/main.ts",
    "content": "import { Fruit } from '.';\n\nconst fruitBasket = {\n  apple: Fruit.apple,\n  orange: Fruit.orange,\n};\n"
  },
  {
    "path": "packages/knip/fixtures/entry-exports-enum-members/package.json",
    "content": "{\n  \"name\": \"@fixtures/entry-exports-enum-members\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/entry-exports-enum-members/tree.ts",
    "content": "import type { Fruit } from './fruit';\n\nexport interface Tree {\n  fruit: Fruit;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/entry-exports-namespace/index.ts",
    "content": "export { NS } from './mid';\n"
  },
  {
    "path": "packages/knip/fixtures/entry-exports-namespace/main.ts",
    "content": "import { NS } from './mid';\n\nNS.x;\n"
  },
  {
    "path": "packages/knip/fixtures/entry-exports-namespace/mid.ts",
    "content": "export * as NS from './ns';\n"
  },
  {
    "path": "packages/knip/fixtures/entry-exports-namespace/ns.ts",
    "content": "export const x = 1;\nexport const y = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/entry-exports-namespace/package.json",
    "content": "{\n  \"name\": \"@fixtures/entry-exports-namespace\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/entry-files/cli.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/entry-files/export-index.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/entry-files/local-default.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/entry-files/local-import.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/entry-files/local-node.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/entry-files/local-require.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/entry-files/main.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/entry-files/package.json",
    "content": "{\n  \"name\": \"@fixtures/entry-files\",\n  \"main\": \"main.js\",\n  \"bin\": \"./cli.js\",\n  \"exports\": {\n    \".\": \"./export-index.js\",\n    \"./local\": {\n      \"default\": \"./local-default.js\",\n      \"import\": \"./local-import.js\",\n      \"node\": \"./local-node.js\",\n      \"require\": \"./local-require.js\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/entry-js/dangling.js",
    "content": "export const unusedFile = true;\n"
  },
  {
    "path": "packages/knip/fixtures/entry-js/index.js",
    "content": "import { myExport } from './my-module.js';\n\nexport const ignoredExportInEntryFile = myExport;\n"
  },
  {
    "path": "packages/knip/fixtures/entry-js/my-module.ts",
    "content": "import * as MyNamespace from './my-namespace.js';\n\nconst x = MyNamespace.x;\nconst y = MyNamespace.y;\n\nexport const unused = 1;\n\nexport const myExport = y(x);\n\nexport type AnyType = any;\n\nexport default myExport;\n"
  },
  {
    "path": "packages/knip/fixtures/entry-js/my-namespace.ts",
    "content": "export const x = 1;\nexport const y = (x: number) => x;\nexport const key = 3;\n\nexport interface MyNamespace {}\n"
  },
  {
    "path": "packages/knip/fixtures/entry-js/package.json",
    "content": "{\n  \"name\": \"@fixtures/entry-js\",\n  \"knip\": {\n    \"entry\": [\n      \"index.js\"\n    ],\n    \"project\": [\n      \"*.{js,ts}\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/entry-js/tsconfig.json",
    "content": "{\n  \"$schema\": \"https://json.schemastore.org/tsconfig\",\n  \"compilerOptions\": {\n    \"allowJs\": true\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/enum-members/index.ts",
    "content": "import { MyEnum, TestEnum, Category } from './members';\n\nconst a: MyEnum.A_UsedExternal = 1;\n\nconst b: TestEnum = { ['']: 'test' };\n\nexport enum EntryEnum {\n  UsedMemberInEntryEnum = 1,\n  UnusedMemberInEntryEnum = 1,\n}\n\nfunction setCategory(cat: Category) {}\n\ntype CategoryKey = keyof typeof Category;\n"
  },
  {
    "path": "packages/knip/fixtures/enum-members/members.ts",
    "content": "import { type EntryEnum } from './index';\n\nenum UnexportedEnun {\n  Member = 1,\n}\n\nexport enum MyEnum {\n  A_UsedExternal = 1,\n  B_Unused = 1,\n  C_UsedInternal = 1,\n  'D-Key' = 'D-Value',\n}\n\nconst myNumber: MyEnum.C_UsedInternal = 1;\n\ntype Used = EntryEnum;\n\nconst U: EntryEnum.UsedMemberInEntryEnum = 1;\n\nexport enum TestEnum {\n  '' = 'test',\n}\n\nexport enum Category {\n  Ambient = 'Ambient',\n  Playback = 'Playback',\n}\n"
  },
  {
    "path": "packages/knip/fixtures/enum-members/package.json",
    "content": "{\n  \"name\": \"@fixtures/enum-members\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/enum-members-enumerated/directions.ts",
    "content": "export enum Direction {\n  NORTH = 'north',\n  SOUTH = 'south',\n  EAST = 'east',\n  WEST = 'west',\n}\n\ninterface Route {\n  direction?: Direction;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/enum-members-enumerated/fruits.ts",
    "content": "export enum Fruits {\n  apple = 'apple',\n  orange = 'orange',\n}\n"
  },
  {
    "path": "packages/knip/fixtures/enum-members-enumerated/index.ts",
    "content": "import { Fruits } from './fruits';\nimport { Direction } from './directions';\n\nFruits.apple;\n\nObject.keys(Fruits);\nObject.values(Fruits);\nObject.entries(Fruits);\n\nObject.getOwnPropertyNames(Direction);\n"
  },
  {
    "path": "packages/knip/fixtures/enum-members-enumerated/package.json",
    "content": "{\n  \"name\": \"@fixtures/enum-members-enumerated\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/export-default-type/class.js",
    "content": "class MyClass {}\n\nexport default MyClass;\n"
  },
  {
    "path": "packages/knip/fixtures/export-default-type/const.js",
    "content": "const fruit = 1;\n\nexport default fruit;\n"
  },
  {
    "path": "packages/knip/fixtures/export-default-type/function.js",
    "content": "function fn() {}\n\nexport default fn;\n"
  },
  {
    "path": "packages/knip/fixtures/export-default-type/index.js",
    "content": "import './const.js';\nimport './class.js';\nimport './let.js';\nimport './var.js';\nimport './function.js';\n"
  },
  {
    "path": "packages/knip/fixtures/export-default-type/let.js",
    "content": "let fruit = 1;\n\nexport default fruit;\n"
  },
  {
    "path": "packages/knip/fixtures/export-default-type/package.json",
    "content": "{\n  \"name\": \"@fixtures/export-default-type\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/export-default-type/var.js",
    "content": "var fruit = 1;\n\nexport default fruit;\n"
  },
  {
    "path": "packages/knip/fixtures/export-spread/array.ts",
    "content": "const numbers = [1, 2, 3];\n\nexport const [FIRST, ...SPREAD_ARR] = numbers;\n"
  },
  {
    "path": "packages/knip/fixtures/export-spread/index.ts",
    "content": "import { FIRST, SPREAD_ARR } from './array.ts';\nimport { FOURTH, SPREAD_OBJ } from './object.ts';\n\nFIRST;\nSPREAD_ARR;\nFOURTH;\nSPREAD_OBJ;\n"
  },
  {
    "path": "packages/knip/fixtures/export-spread/object.ts",
    "content": "const numbers = { FOURTH: 4, FIFTH: 5, SIXTH: 6 };\n\nexport const { FOURTH, ...SPREAD_OBJ } = numbers;\n"
  },
  {
    "path": "packages/knip/fixtures/export-spread/package.json",
    "content": "{\n  \"name\": \"@fixed/export-spread\",\n  \"type\": \"module\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/exports/default-arrow-function.ts",
    "content": "export default () => 1;\n"
  },
  {
    "path": "packages/knip/fixtures/exports/default-class.ts",
    "content": "export default class {}\n"
  },
  {
    "path": "packages/knip/fixtures/exports/default-function.ts",
    "content": "export default function () {}\n"
  },
  {
    "path": "packages/knip/fixtures/exports/default-generator-function.ts",
    "content": "export default function* () {}\n"
  },
  {
    "path": "packages/knip/fixtures/exports/default-named-class.ts",
    "content": "export default class ClassName {}\n"
  },
  {
    "path": "packages/knip/fixtures/exports/default-named-function.ts",
    "content": "export default function functionName() {}\n"
  },
  {
    "path": "packages/knip/fixtures/exports/default-named-generator-function.ts",
    "content": "export default function* generatorFunctionName() {}\n"
  },
  {
    "path": "packages/knip/fixtures/exports/default.ts",
    "content": "export const NamedExport = 1;\n\nexport default () => 1;\n"
  },
  {
    "path": "packages/knip/fixtures/exports/dynamic-import.ts",
    "content": "export default () => {};\n\nexport const unusedZero = 0;\n\nexport const used = true;\n"
  },
  {
    "path": "packages/knip/fixtures/exports/export-is.ts",
    "content": "const x = 1;\n\nexport = x;\n"
  },
  {
    "path": "packages/knip/fixtures/exports/index.ts",
    "content": "import './odd';\nimport { exportedResult } from './my-module.js';\nimport {\n  num,\n  str,\n  functionName,\n  className,\n  generatorFunctionName,\n  name1,\n  name4,\n  exportedA,\n  exportedB,\n} from './named-exports';\nimport type { MyNum, MyString, MyInterface } from './types';\n\nnum;\nstr;\nfunctionName;\nclassName;\ngeneratorFunctionName;\nname1;\nname4;\nexportedA;\nexportedB;\n\ntype Used = MyNum | MyString | MyInterface;\n\nconst dynamic = await import('./dynamic-import');\ndynamic;\n\nasync function main() {\n  const { used } = await import('./dynamic-import');\n  used;\n}\n\nexport const entryFileExport = exportedResult;\n\nexport type EntryFileExportType = any;\n\nexport type { MixType } from './my-mix';\n\nexport { MixClass } from './my-mix';\n"
  },
  {
    "path": "packages/knip/fixtures/exports/my-mix.ts",
    "content": "export const unusedInMix = 1;\n\nexport interface MixType {}\n\nexport class MixClass {}\n"
  },
  {
    "path": "packages/knip/fixtures/exports/my-module.ts",
    "content": "import defaultArrowFn from './default-arrow-function';\nimport defaultClass from './default-class';\nimport defaultFn from './default-function';\nimport defaultGenFn from './default-generator-function';\nimport defaultNamedClass from './default-named-class';\nimport defaultNamedFn from './default-named-function';\nimport defaultNamedGenFn from './default-named-generator-function';\nimport _default from './default.js';\nimport * as MyNamespace from './my-namespace.js';\n\ndefaultArrowFn;\ndefaultClass;\ndefaultFn;\ndefaultGenFn;\ndefaultNamedClass;\ndefaultNamedFn;\ndefaultNamedGenFn;\n_default;\n\nconst nsNumber = MyNamespace.nsNumber;\nconst nsFunction = MyNamespace.nsFunction;\n\nexport const unusedNumber = 1;\nexport const unusedFunction = async <T>(arg: T) => arg;\n\nexport const exportedResult = nsFunction(nsNumber);\n\nexport type MyAnyType = any;\n\nexport default exportedResult;\n"
  },
  {
    "path": "packages/knip/fixtures/exports/my-namespace.ts",
    "content": "export const nsNumber = 1;\nexport const nsFunction = (x: number) => x;\nexport const nsUnusedKey = 3;\n\nexport interface MyNamespace {}\n"
  },
  {
    "path": "packages/knip/fixtures/exports/named-exports.ts",
    "content": "export const num = 1;\nexport const str = 'a';\nexport function functionName() {}\nexport class className {}\nexport function* generatorFunctionName() {}\nexport const { name1, name2: renamedExport } = {};\nexport const [namedExport, name4] = [];\n\nfunction exportedA() {}\nfunction exportedB() {}\nexport { exportedA, exportedB };\n"
  },
  {
    "path": "packages/knip/fixtures/exports/odd.ts",
    "content": "import one = require('./export-is');\none;\n"
  },
  {
    "path": "packages/knip/fixtures/exports/package.json",
    "content": "{\n  \"name\": \"@fixtures/exports\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/exports/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"types\": [\"node\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/exports/types.ts",
    "content": "export type MyNum = number;\nexport type MyString = string;\nexport enum MyEnum {}\nexport interface MyInterface {}\n\ntype MyType = 1;\n\nexport type { MyType };\n"
  },
  {
    "path": "packages/knip/fixtures/exports-default-interface/enum.ts",
    "content": "enum MyEnum {\n  id = 0,\n}\n\nexport default MyEnum;\n"
  },
  {
    "path": "packages/knip/fixtures/exports-default-interface/index.ts",
    "content": "import Default_MyType from './type';\nimport Default_MyEnum from './enum';\nimport Default_MyInterface from './interface';\n\nconst i: Default_MyInterface = { id: 0 };\nconst e: Default_MyEnum = 0;\nconst m: Default_MyEnum.id = 0;\nconst t: Default_MyType = {};\n\ni;\ne;\nm;\nt;\n"
  },
  {
    "path": "packages/knip/fixtures/exports-default-interface/interface.ts",
    "content": "export default interface MyInterface {\n  id: number;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/exports-default-interface/package.json",
    "content": "{\n  \"name\": \"@fixtures/exports-default-interface\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/exports-default-interface/type.ts",
    "content": "type MyType = unknown;\n\nexport default MyType;\n"
  },
  {
    "path": "packages/knip/fixtures/exports-special-characters/exports.ts",
    "content": "export const $dollar = '$';\nexport const dollar$ = '$$';\nexport const _underscore = '_';\nexport const __underscores = '__';\n\nexport class DollarMembers {\n  $member: string;\n  member$: string;\n  $method: () => string;\n  method$: () => string;\n}\n\nexport class $Dollar {}\n\nexport type $DollarType = string;\n\nexport enum Characters {\n  Used = 1,\n  ' ' = ' ',\n  '-' = '-',\n  ',' = ',',\n  ':' = ':',\n  '?' = '?',\n  '.' = '.',\n  '(' = '(',\n  ')' = ')',\n  '[' = '[',\n  ']' = ']',\n  '{' = '{',\n  '}' = '}',\n  '@' = '@',\n  '*' = '*',\n  '/' = '/',\n  '\\\\' = '\\\\',\n  '+' = '+',\n  '|' = '|',\n  $ = '$',\n  Slash = '/',\n  Space = ' ',\n}\n"
  },
  {
    "path": "packages/knip/fixtures/exports-special-characters/index.ts",
    "content": "import { type Characters, DollarMembers } from './exports';\n\ntype Ref = Characters;\n\nconst u: Characters.Used = 1;\n\nconst instance = new DollarMembers();\n"
  },
  {
    "path": "packages/knip/fixtures/exports-special-characters/package.json",
    "content": "{\n  \"name\": \"@fixtures/exports-special-characters\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/exports-value-refs/index.ts",
    "content": "import type { MyInterface } from './refs.js';\n\nconst x: MyInterface = { _class: 1, _type: { key: 1 }, _fn: () => {}, _const: 1 };\n"
  },
  {
    "path": "packages/knip/fixtures/exports-value-refs/package.json",
    "content": "{\n  \"name\": \"@fixtures/exports-value-refs\",\n  \"knip\": {\n    \"ignoreExportsUsedInFile\": {\n      \"interface\": true,\n      \"type\": true\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/exports-value-refs/refs.ts",
    "content": "export interface MyInterface {\n  _class: MyClass;\n  _type: MyType;\n  _fn: typeof fn;\n  _const: typeof myNumber;\n}\n\nexport type MyType = {\n  key: 1;\n};\n\nexport class MyClass {}\n\nexport class NotInExportedType {}\n\nexport interface FnOptions {\n  verbose: boolean;\n}\n\nexport function fn(n: unknown, options?: FnOptions) {}\n\nexport const myNumber = 1;\n\nconst instance = new MyClass();\n\nconst inst = new NotInExportedType();\n\nconst total = myNumber + myNumber;\ntotal;\n\ninterface MyInterface2 {\n  _class2: NotInExportedType;\n}\n\nconst x: MyInterface2 = { _class2: 1 };\n\nexport const myValue: unknown = {};\nexport const myResult: unknown = fn(myValue);\n"
  },
  {
    "path": "packages/knip/fixtures/exports-value-refs-default/index.ts",
    "content": "import { Bird } from './refs.js';\n\nconst bird: Bird = {\n  ref1: () => ({ skin: 'green' }),\n};\n"
  },
  {
    "path": "packages/knip/fixtures/exports-value-refs-default/package.json",
    "content": "{\n  \"name\": \"@fixtures/exports-value-refs-default\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/exports-value-refs-default/refs.ts",
    "content": "export interface Lizard {\n  skin: string;\n}\n\nexport class Reptile {\n  skin: string;\n}\n\nexport const SkinColor = 'green';\n\nexport type Bird = {\n  ref0: typeof Reptile;\n  ref1: () => Lizard;\n  ref2: typeof SkinColor;\n};\n\nexport function logger(s: string) {\n  return s;\n}\n\nfunction setLogger(log: typeof logger): void {\n  log;\n}\n\nexport type UnusedTypeInUnusedExport = { id: number };\n\nexport interface UnusedInterface {\n  api: UnusedTypeInUnusedExport;\n}\n\nexport class UnusedClass {}\n\nexport type UnusedTypeWithClass = typeof UnusedClass;\n\nconst obj = {\n  destrRefObj: 1,\n};\n\nconst arr = [1, 2, 3];\n\nexport const { destrRefObj } = obj;\n\nexport const [destrRefArr] = arr;\n\ndestrRefObj;\ndestrRefArr;\n"
  },
  {
    "path": "packages/knip/fixtures/extensions-css-ts/index.ts",
    "content": "import styling1 from './styles1.css.ts';\nimport styling2 from './styles2.css.js';\nimport styling3 from './styles3.css.ts';\nstyling1;\nstyling2;\nstyling3;\n"
  },
  {
    "path": "packages/knip/fixtures/extensions-css-ts/package.json",
    "content": "{\n  \"name\": \"@fixtures/extensions-css-ts\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/extensions-css-ts/styles1.css.ts",
    "content": "import styles from './styles1a.css.ts';\nexport default styles;\n"
  },
  {
    "path": "packages/knip/fixtures/extensions-css-ts/styles1a.css.ts",
    "content": "export default {};\n"
  },
  {
    "path": "packages/knip/fixtures/extensions-css-ts/styles2.css.ts",
    "content": "import styles from './styles2a.css.js';\nexport default styles;\n"
  },
  {
    "path": "packages/knip/fixtures/extensions-css-ts/styles2a.css.ts",
    "content": "export default {};\n"
  },
  {
    "path": "packages/knip/fixtures/extensions-css-ts/styles3.css.ts",
    "content": "import styles from './styles3a.css';\nexport default styles;\n"
  },
  {
    "path": "packages/knip/fixtures/extensions-css-ts/styles3a.css.ts",
    "content": "export default {};\n"
  },
  {
    "path": "packages/knip/fixtures/fix/.prettierrc",
    "content": "{\n  \"singleQuote\": true,\n  \"arrowParens\": \"avoid\",\n  \"trailingComma\": \"es5\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/fix/access.js",
    "content": "module.exports.USED = 1;\nmodule.exports.UNUSED = 1;\nmodule.exports['ACCESS'] = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/fix/default-x.mjs",
    "content": "const x = 1;\n\nexport default x;\n\nexport const dx = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/fix/default.mjs",
    "content": "export default 1;\nexport const d = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/fix/exports.js",
    "content": "const identifier = 1;\nconst identifier2 = 2;\n\nmodule.exports = { identifier, identifier2 };\n"
  },
  {
    "path": "packages/knip/fixtures/fix/ignored.ts",
    "content": "export const a = 1;\nexport const b = 2;\n\nexport type T = number;\n"
  },
  {
    "path": "packages/knip/fixtures/fix/index.mjs",
    "content": "import { d } from './default.mjs';\nimport { dx } from './default-x.mjs';\nimport _ from 'lodash';\nimport { z, f, g, i } from './mod';\nimport { USED } from './access';\nimport { identifier } from './exports';\nimport { a } from './ignored';\nimport * as NS from './reexports.mjs';\n\nd;\ndx;\n_;\nz;\nf;\ng;\ni;\nUSED;\nidentifier;\na;\nNS.One;\nNS.Rectangle;\nNS.Nine;\nNS.setter;\n"
  },
  {
    "path": "packages/knip/fixtures/fix/knip.ts",
    "content": "export default {\n  ignore: ['ignored.ts'],\n  ignoreDependencies: ['ignored'],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/fix/mod.ts",
    "content": "export const x = 1;\nexport const y = 2;\n\nexport interface McInterFace {}\nexport type McType = {};\nexport enum McEnum {}\n\nexport const z = x + y;\n\nexport const { a, b } = { a: 1, b: 1 };\n\nexport const [c, d] = [3, 4];\n\nexport const [e, f] = [5, 6];\n\nexport const [g, h, i] = [7, 8, 9];\n\nexport default class MyClass {}\n\n/** @lintignore */\nexport type U = number;\n"
  },
  {
    "path": "packages/knip/fixtures/fix/package.json",
    "content": "{\n  \"name\": \"@fixtures/fix\",\n  \"dependencies\": {\n    \"lodash\": \"*\",\n    \"unused\": \"*\",\n    \"ignored\": \"*\"\n  },\n  \"devDependencies\": {\n    \"unreferenced\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/fix/reexported.ts",
    "content": "const Two = 2;\nconst Three = 3;\nconst Four = 4;\nconst Five = 5;\n\nexport { Two, Three };\n\nexport { Four as Fourth, Five as Fifth };\n\nexport { Four as Rectangle, Five as Pentagon };\n\ntype Six = any;\ntype Seven = unknown;\nconst Eight = 8;\nconst Nine = 9;\ntype Ten = unknown[];\n\nexport type { Six };\n\nexport { type Seven, Eight, Nine, type Ten };\n\nexport const One = 1;\n\nconst fn = () => ({ get: () => 1, set: () => 1 });\n\nexport const { get: getter, set: setter } = fn();\n"
  },
  {
    "path": "packages/knip/fixtures/fix/reexports.mjs",
    "content": "export { RangeSlider } from './reexported';\nexport { Rating } from './reexported';\nexport { One, Rectangle, Six, Seven, Eight, Nine, Ten, getter, setter } from './reexported';\nexport { Col, Col as KCol } from './reexported';\nexport { Row as KRow, Row } from './reexported';\n"
  },
  {
    "path": "packages/knip/fixtures/fix-members/class.ts",
    "content": "export class Rectangle {\n  constructor(\n    public width: number,\n    public height: number\n  ) {}\n\n  static Key = 1;\n\n  public get unusedGetter(): string {\n    return 'unusedGetter';\n  }\n\n  private set unusedSetter(w: number) {\n    this.width = w;\n  }\n\n  area() {\n    return this.width * this.height;\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/fix-members/enums.ts",
    "content": "export enum Directions {\n  North = 1,\n  East = 2,\n  South = 3,\n  West = 4,\n}\n\nexport enum Fruits {\n  apple = 'apple',\n  orange = 'orange',\n}\n"
  },
  {
    "path": "packages/knip/fixtures/fix-members/index.js",
    "content": "import { Rectangle } from './class.js';\nimport { Directions, Fruits } from './enums.js';\nimport { Animals } from './namespaces.js';\n\nDirections.East;\n\nFruits.apple;\n\nAnimals.cat;\nAnimals.swim();\nAnimals.Birds.eagle;\n\nconst rect = new Rectangle();\nrect.area();\n"
  },
  {
    "path": "packages/knip/fixtures/fix-members/namespaces.ts",
    "content": "export namespace Animals {\n  export const cat = 'cat';\n  export const unusedDog = 'dog';\n  export function swim() {}\n\n  export namespace Birds {\n    export const eagle = 'eagle';\n    export const unusedParrot = 'parrot';\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/fix-members/package.json",
    "content": "{\n  \"name\": \"@fixtures/fix-members\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/fix-workspaces/exports.ts",
    "content": "export const c = 1;\nexport const d = 2;\n\nexport type T = number;\n\n/** @lintignore */\nexport type U = number;\n"
  },
  {
    "path": "packages/knip/fixtures/fix-workspaces/ignored.ts",
    "content": "export const a = 1;\nexport const b = 2;\n\nexport type T = number;\n"
  },
  {
    "path": "packages/knip/fixtures/fix-workspaces/index.js",
    "content": "import { a } from './ignored';\nimport { c } from './exports';\n\na;\nc;\n"
  },
  {
    "path": "packages/knip/fixtures/fix-workspaces/knip.ts",
    "content": "export default {\n  ignoreWorkspaces: ['packages/ignored'],\n  workspaces: {\n    '.': {\n      ignore: ['ignored.ts'],\n      ignoreDependencies: ['ignored'],\n    },\n    'packages/lib': {\n      ignore: ['ignored.ts'],\n      ignoreDependencies: ['ignored'],\n    },\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/fix-workspaces/package.json",
    "content": "{\n  \"name\": \"@fixtures/fix-workspaces\",\n  \"dependencies\": {\n    \"unused\": \"*\",\n    \"ignored\": \"*\"\n  },\n  \"workspaces\": [\n    \"packages/*\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/fix-workspaces/packages/ignored/exports.ts",
    "content": "export const a = 1;\nexport const b = 2;\n\nexport type T = number;\n"
  },
  {
    "path": "packages/knip/fixtures/fix-workspaces/packages/ignored/index.js",
    "content": "import { a } from './exports';\n\na;\n"
  },
  {
    "path": "packages/knip/fixtures/fix-workspaces/packages/ignored/package.json",
    "content": "{\n  \"name\": \"@fixtures/fix-workspaces__ignored\",\n  \"dependencies\": {\n    \"unused\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/fix-workspaces/packages/lib/exports.ts",
    "content": "export const c = 1;\nexport const d = 2;\n\nexport type T = number;\n\n/** @lintignore */\nexport type U = number;\n"
  },
  {
    "path": "packages/knip/fixtures/fix-workspaces/packages/lib/ignored.ts",
    "content": "export const a = 1;\nexport const b = 2;\n\nexport type T = number;\n"
  },
  {
    "path": "packages/knip/fixtures/fix-workspaces/packages/lib/index.js",
    "content": "import { a } from './ignored';\nimport { c } from './exports';\n\na;\nc;\n"
  },
  {
    "path": "packages/knip/fixtures/fix-workspaces/packages/lib/package.json",
    "content": "{\n  \"name\": \"@fixtures/fix-workspaces__lib\",\n  \"dependencies\": {\n    \"unused\": \"*\",\n    \"ignored\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/git-branch-file/index.ts",
    "content": "export const main = true;\n"
  },
  {
    "path": "packages/knip/fixtures/git-branch-file/package.json",
    "content": "{\n  \"name\": \"@fixtures/git-branch-file\",\n  \"knip\": {\n    \"entry\": \"index.ts\",\n    \"project\": \"**/*.ts\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/gitignore/.gitignore",
    "content": "build\n/b\n"
  },
  {
    "path": "packages/knip/fixtures/gitignore/package.json",
    "content": "{\n  \"name\": \"@fixtures/gitignore\",\n  \"workspaces\": [\n    \"packages/a\"\n  ],\n  \"knip\": {\n    \"workspaces\": {\n      \".\": {\n        \"entry\": \"index.ts\",\n        \"project\": \"**/*.{js,ts}\"\n      },\n      \"packages/a\": {\n        \"entry\": \"index.ts\",\n        \"project\": \"**/*.{js,ts}\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/gitignore/packages/a/.gitignore",
    "content": "build\ndist.ts\n"
  },
  {
    "path": "packages/knip/fixtures/gitignore/packages/a/index.ts",
    "content": "export const a = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/gitignore/packages/a/libs/.gitignore",
    "content": "*.ts\n"
  },
  {
    "path": "packages/knip/fixtures/gitignore/packages/a/libs/util/type/.gitignore",
    "content": "*.js\n"
  },
  {
    "path": "packages/knip/fixtures/gitignore/packages/a/package.json",
    "content": "{\n  \"name\": \"@fixtures/gitignore__a\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/glob/.gitignore",
    "content": "a/b/c\n"
  },
  {
    "path": "packages/knip/fixtures/glob/a/.gitignore",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/glob/a/b/.gitignore",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/glob/a/b/c/.gitignore",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/glob-worktree/.gitignore",
    "content": "beyond-worktree-ignored\n"
  },
  {
    "path": "packages/knip/fixtures/glob-worktree/mock-git-dir/info/exclude",
    "content": "worktree-exclude-ignored\n"
  },
  {
    "path": "packages/knip/fixtures/glob-worktree/root/.gitignore",
    "content": "worktree-ignored\n"
  },
  {
    "path": "packages/knip/fixtures/glob-worktree/root/dot-git",
    "content": "gitdir: ../mock-git-dir\n"
  },
  {
    "path": "packages/knip/fixtures/glob-worktree/root/subdir/.gitignore",
    "content": "subdir-ignored\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-dependencies-binaries/index.ts",
    "content": "import Picker from 'rc-picker/lib/interface';\nimport TreeSelect from 'rc-tree-select/lib/interface';\nimport Select from 'rc-select/lib/interface';\n\nimport P1 from '@org/pkg1';\nimport P2 from '@org/pkg2';\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-dependencies-binaries/knip.ts",
    "content": "export default {\n  ignoreBinaries: ['eslint', /^ts.+/, /.*unused-bins.*/, 'executable!'],\n  ignoreDependencies: ['stream', /^@org\\/.*/, /^rc-.*/, /.+unused-deps.+/, 'package!'],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-dependencies-binaries/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-dependencies-binaries\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"build\": \"tsc\",\n    \"c\": \"tsx index.ts\",\n    \"d\": \"ts-node index.ts\",\n    \"e\": \"eslint\",\n    \"f\": \"formatter\",\n    \"g\": \"executable\",\n    \"h\": \"package\"\n  },\n  \"devDependencies\": {\n    \"tsx\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-dependencies-binaries-json/index.ts",
    "content": "import Picker from 'rc-picker/lib/interface';\nimport TreeSelect from 'rc-tree-select/lib/interface';\nimport Select from 'rc-select/lib/interface';\n\nimport P1 from '@org/pkg1';\nimport P2 from '@org/pkg2';\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-dependencies-binaries-json/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-dependencies-binaries-json\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"build\": \"tsc\",\n    \"c\": \"tsx index.ts\",\n    \"d\": \"ts-node index.ts\",\n    \"e\": \"eslint\",\n    \"f\": \"formatter\"\n  },\n  \"devDependencies\": {\n    \"tsx\": \"*\"\n  },\n  \"knip\": {\n    \"ignoreBinaries\": [\n      \"eslint\",\n      \"^ts.+\",\n      \".*unused-bins.*\"\n    ],\n    \"ignoreDependencies\": [\n      \"stream\",\n      \"^@org/.*\",\n      \"^rc-.*\",\n      \".+unused-deps.+\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-dependencies-eslint/.eslintrc.js",
    "content": "const path = require('node:path');\n\nmodule.exports = {\n  extends: ['@local/eslint-config'],\n  plugins: ['import', 'prettier'],\n  root: true,\n  settings: {\n    'import/resolver': {\n      typescript: {\n        project: path.resolve(__dirname, 'tsconfig.json'),\n      },\n    },\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-dependencies-eslint/knip.json",
    "content": "{\n  \"eslint\": true,\n  \"ignoreDependencies\": [\"^eslint-.*\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-dependencies-eslint/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-dependencies-eslint\",\n  \"devDependencies\": {\n    \"@local/eslint-config\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file/computed-access.ts",
    "content": "export const QUERY_KEY = 'live';\n\nconst query: Record<string, string> = {};\nconst value = query[QUERY_KEY];\nquery[QUERY_KEY] = 'updated';\ndelete query[QUERY_KEY];\nvalue;\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file/control-flow.ts",
    "content": "export const WARRIOR = 'warrior';\nexport const MAGE = 'mage';\n\nfunction selectClass(role: string) {\n  switch (role) {\n    case WARRIOR:\n      return 1;\n    case MAGE:\n      return 2;\n  }\n}\n\nexport const BASE_DAMAGE = 10;\n\nfunction computeDamage(multiplier = BASE_DAMAGE) {\n  return multiplier;\n}\n\nselectClass('warrior');\ncomputeDamage();\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file/expressions.ts",
    "content": "export const quest = Promise.resolve('dragon');\n\nasync function embark() {\n  const result = await quest;\n  return result;\n}\n\nexport const loot = () => 'gold';\n\nconst reward = () => loot;\n\nembark();\nreward();\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file/imported.ts",
    "content": "export interface ReferencedNeverInterface {\n  hello: boolean;\n}\n\nexport interface ReferencedInterfaceInternally {\n  message: string;\n  repeat?: number;\n}\n\nexport function referencedInternallyFunction(value: unknown) {\n  //\n}\n\nexport function usedFunction(options: ReferencedInterfaceInternally) {\n  referencedInternallyFunction(options);\n}\n\nexport function referencedNeverFunction() {\n  //\n}\n\nfunction declaredThenExportedDefault() {}\nexport default declaredThenExportedDefault;\n\nfunction DeclaredThenExportedNamed() {}\nexport { DeclaredThenExportedNamed };\n\nexport const scout = () => {};\nexport const ranger = () => {};\n\nexport class Paladin {\n  static ally = scout;\n}\n\n(0, ranger)();\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file/index.ts",
    "content": "import { usedFunction } from './imported';\nimport './loops';\nimport './control-flow';\nimport './expressions';\nimport './jsx';\nimport './tagged-template';\nimport './type-assertions';\nimport './more';\nimport './computed-access';\n\nusedFunction({ message: 'Hello, world!' });\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file/jsx.tsx",
    "content": "export const Shield = () => <div>shield</div>;\n\nexport const durability = 100;\n\nexport const enchantments = { fire: true };\n\nconst ArmorSet = () => (\n  <div>\n    <Shield />\n    <span data-durability={durability} />\n    <span {...enchantments} />\n  </div>\n);\n\nArmorSet;\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file/knip.json",
    "content": "{\n  \"ignoreExportsUsedInFile\": true\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file/loops.ts",
    "content": "export const inventory = ['axe', 'bow', 'crossbow'];\n\nfor (const weapon of inventory) {\n  weapon;\n}\n\nexport const armory = { sword: 1, shield: 2 };\n\nfor (const slot in armory) {\n  slot;\n}\n\nexport let patrol = true;\n\nwhile (patrol) {\n  patrol = false;\n}\n\nexport let guard = true;\n\ndo {\n  guard = false;\n} while (guard);\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file/more.ts",
    "content": "export const sentinel = 10;\nfor (let i = 0; i < sentinel; i++) {}\n\nexport const ore = {};\nconst refined = <unknown>ore;\nrefined;\n\nexport const gem = 42;\nconst polished = (gem);\npolished;\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-exports-used-in-file\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file/tagged-template.ts",
    "content": "export function enchant(strings: TemplateStringsArray, ...values: unknown[]) {\n  return strings.join('');\n}\n\nconst spell = enchant`fire`;\n\nspell;\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file/tsconfig.json",
    "content": "{\n  \"compilerOptions\": { \"jsx\": \"react-jsx\" },\n  \"include\": [\"*.ts\", \"*.tsx\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file/type-assertions.ts",
    "content": "export const blade = { damage: 50 };\n\nexport const hilt = { grip: 'leather' };\n\nconst forged = blade as unknown;\n\nconst reinforced = hilt!;\n\nforged;\nreinforced;\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-alias-exclude/exports.ts",
    "content": "const ash = 'ash';\nconst balau = 'balau';\n\nbalau;\n\nexport { ash, balau };\n\nexport const cedar = 'cedar';\n\ncedar;\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-alias-exclude/index.ts",
    "content": "import './exports';\nimport './more';\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-alias-exclude/more.ts",
    "content": "const kauri = 'kauri';\nconst larch = 'larch';\n\nexport {\nkauri,\n\n  larch };\n\n\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-alias-exclude/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-exports-used-in-file-alias-exclude\",\n  \"knip\": {\n    \"ignoreExportsUsedInFile\": true\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-false/imported.ts",
    "content": "interface ReferencedInterfaceInternally {\n  message: string;\n}\n\nfunction referencedInternallyFunction(value: unknown) {\n  //\n}\n\nexport function usedFunction(options: ReferencedInterfaceInternally) {\n  referencedInternallyFunction(options);\n}\n\nfunction declaredThenExportedDefault() {}\nexport default declaredThenExportedDefault;\n\nfunction DeclaredThenExportedNamed() {}\nexport { DeclaredThenExportedNamed };\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-false/index.ts",
    "content": "import { usedFunction } from './imported';\n\nusedFunction({ message: 'test' });\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-false/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-exports-used-in-file-false\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-id-chars/imported.ts",
    "content": "export const unusedFunction = () => 1;\nexport const unusedFunction2 = () => 1;\nunusedFunction2();\n\nexport const unusedVar = 1;\nexport const _unusedVar = 1;\n_unusedVar;\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-id-chars/index.ts",
    "content": "import './imported';\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-id-chars/knip.json",
    "content": "{\n  \"ignoreExportsUsedInFile\": true\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-id-chars/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-exports-used-in-file-id-chars\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-id-underscores/imported.ts",
    "content": "export const __underscoresUnused = 'underscoresUnused';\nexport const __underscoresUsed = 'underscoresUsed';\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-id-underscores/index.ts",
    "content": "import { __underscoresUsed } from './imported';\nimport * as NS from './namespace';\n\n__underscoresUsed;\nNS.__underscoresUsed;\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-id-underscores/knip.json",
    "content": "{\n  \"ignoreExportsUsedInFile\": true\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-id-underscores/namespace.ts",
    "content": "export const __underscoresUnused = 'underscoresUnused';\nexport const __underscoresUsed = 'underscoresUsed';\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-id-underscores/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-exports-used-in-file-id-underscores\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-id-underscores/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"types\": [\"node\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-re-export/component.ts",
    "content": "import type { ComponentProps } from './interface';\n\nexport type { ComponentProps };\n\nexport default function Wrapper(props: ComponentProps) {\n  return props;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-re-export/index.ts",
    "content": "import Component from './component';\n\nComponent;\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-re-export/interface.ts",
    "content": "export interface ComponentProps {\n  n: number;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-re-export/knip.json",
    "content": "{\n  \"ignoreExportsUsedInFile\": true\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-re-export/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-exports-used-in-file-re-export\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-shorthand/knip.json",
    "content": "{\n  \"ignoreExportsUsedInFile\": true\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-shorthand/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-exports-used-in-file-shorthand\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-shorthand/src/antecedents.js",
    "content": "export const variableA = 1;\nexport const variableB = 1;\nexport const variableC = 1;\n\nconst x = {\n  variableC,\n  depth: {\n    variableB,\n  },\n  variableA,\n};\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-shorthand/src/fn.js",
    "content": "export function fn() {}\n\nconst obj = { fn };\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-shorthand/src/index.js",
    "content": "import './antecedents';\nimport './fn';\nimport { slice } from './slice';\n\nslice;\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-shorthand/src/slice.js",
    "content": "export const initialState = { value: 0 };\nexport const shorthandInitialState = { value: 0 };\n\nexport const slice = {\n  name: 'counter',\n  initialState: initialState,\n  shorthandInitialState,\n};\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-some/imported.ts",
    "content": "export interface ReferencedNeverInterface {\n  hello: boolean;\n}\n\nexport interface ReferencedInterfaceInternally {\n  message: string;\n  repeat?: number;\n}\n\nexport function referencedInternallyFunction(value: unknown) {\n  //\n}\n\nexport function usedFunction(options: ReferencedInterfaceInternally) {\n  referencedInternallyFunction(options);\n}\n\nexport function referencedNeverFunction() {\n  //\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-some/index.ts",
    "content": "import { usedFunction } from './imported';\n\nusedFunction({ message: 'Hello, world!' });\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-some/knip.json",
    "content": "{\n  \"ignoreExportsUsedInFile\": {\n    \"interface\": true,\n    \"type\": true\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-some/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-exports-used-in-file-some\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-some/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"types\": [\"node\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-typeof-class/knip.json",
    "content": "{\n  \"ignoreExportsUsedInFile\": {\n    \"enum\": true,\n    \"interface\": true,\n    \"type\": true\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-typeof-class/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-exports-used-in-file-typeof-class\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-typeof-class/src/api.ts",
    "content": "export function logger(): void {}\n\nexport function setLogger(fn: typeof logger): void {\n  void fn;\n}\n\nexport class TreeNode {\n  children: TreeNode[] = [];\n}\n\nexport class TreeLeaf {\n  value = '';\n}\n\nexport function createTree(): TreeNode {\n  const node = new TreeNode();\n  node.children.push(new TreeLeaf() as unknown as TreeNode);\n  return node;\n}\n\nexport class Leaf {\n  text = '';\n}\n\nexport interface Collection {\n  isLeaf(): this is Leaf;\n}\n\nexport class Node {\n  value = 0;\n}\n\nexport interface Walker {\n  visit(n: Node): void;\n}\n\nfunction internal(w: Walker) {\n  w.visit(new Node());\n}\n\ninternal({ visit: () => {} });\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-exports-used-in-file-typeof-class/src/index.ts",
    "content": "import { setLogger, createTree, type Collection } from './api';\n\nsetLogger(() => {});\ncreateTree();\n\nconst c: Collection = { isLeaf: () => false };\nc.isLeaf();\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-files/apples/index.js",
    "content": "import './used.js';\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-files/apples/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-files__apples\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-files/apples/rooted.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/ignore-files/apples/unused.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/ignore-files/apples/used.js",
    "content": "export const unused = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-files/bananas/index.js",
    "content": "import './used.js';\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-files/bananas/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-files__bananas\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-files/bananas/rooted.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/ignore-files/bananas/unused.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/ignore-files/bananas/used.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/ignore-files/index.js",
    "content": "import './used.js';\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-files/knip.json",
    "content": "{\n  \"$schema\": \"https://unpkg.com/knip@6/schema.json\",\n  \"ignoreFiles\": [\"bananas/**\", \"rooted.js\"],\n  \"workspaces\": {\n    \"apples\": {\n      \"ignoreFiles\": [\"**/unused.js\", \"used.js\"]\n    },\n    \"bananas\": {\n      \"ignoreFiles\": []\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-files/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-files\",\n  \"workspaces\": [\n    \"apples\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-files/rooted.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/ignore-files/unused.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/ignore-files/used.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/ignore-issues/knip.json",
    "content": "{\n  \"$schema\": \"../../schema.json\",\n  \"includeEntryExports\": true,\n  \"ignoreIssues\": {\n    \"src/generated/**\": [\"exports\", \"types\"],\n    \"**/*.generated.ts\": [\"exports\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-issues/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-issues\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-issues/src/generated/types.ts",
    "content": "// This file is generated, unused exports should be ignored\nexport const unusedGenerated = 'unused';\nexport type UnusedType = string;\n\n// But dependencies should still be reported\nimport { nonExistent } from 'nonexistent-package';\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-issues/src/index.ts",
    "content": "export const used = 'used';\n\n// Import files to ensure they're analyzed\nimport './generated/types.js';\nimport './model.generated.js';\nimport './regular.js';\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-issues/src/model.generated.ts",
    "content": "// This file is generated, unused exports and class members should be ignored\nexport class GeneratedModel {\n  unusedMethod() {\n    return 'unused';\n  }\n\n  anotherUnusedMethod() {\n    return 'also unused';\n  }\n}\n\nexport const unusedExport = 'unused';\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-issues/src/regular.ts",
    "content": "// This is a regular file, unused exports SHOULD be reported\nexport const unusedRegular = 'unused';\n\nexport class RegularClass {\n  unusedMethod() {\n    return 'unused';\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-members/MyClass.ts",
    "content": "class BaseClass {\n  constructor() {}\n  init() {}\n  used() {}\n  unused() {}\n}\n\nexport class MyClass extends BaseClass {\n  constructor() {\n    super();\n  }\n  ignored() {}\n  init() {\n    super.init();\n  }\n  implemented() {\n    this.used();\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-members/enums.ts",
    "content": "export enum Direction {\n  Up = 1,\n  Down,\n  Left,\n  Right,\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-members/index.ts",
    "content": "import { MyClass } from './MyClass';\nimport { Direction } from './enums';\n\nMyClass.init();\nDirection.Up;\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-members/knip.ts",
    "content": "export default {\n  ignoreMembers: ['ignored', /Left|Right/],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-members/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-members\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-members/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"jsx\": \"preserve\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-negated/index.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/ignore-negated/knip.json",
    "content": "{\n  \"entry\": [\"index.js\"],\n  \"project\": [\"**/*.js\"],\n  \"ignore\": [\"!src/modules/B/**/*.js\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-negated/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-negated\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-negated/src/modules/A/unusedFileA.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/ignore-negated/src/modules/B/unusedFileB.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/ignore-patterns/.gitignore",
    "content": "dist\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-patterns/index.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/ignore-patterns/knip.json",
    "content": "{\n  \"ignore\": [\"dist/**\", \"generated/**\", \"scripts/**\"],\n  \"ignoreFiles\": [\"temp/**\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-patterns/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-patterns\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-patterns/scripts/build.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/ignore-patterns-monorepo/index.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/ignore-patterns-monorepo/knip.json",
    "content": "{\n  \"ignore\": [\"build/**\"],\n  \"workspaces\": {\n    \"packages/lib\": {\n      \"ignore\": [\"output/**\", \"cache/**\"]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-patterns-monorepo/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-patterns-monorepo\",\n  \"workspaces\": [\n    \"packages/*\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-patterns-monorepo/packages/lib/index.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/ignore-patterns-monorepo/packages/lib/output/generated.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/ignore-patterns-monorepo/packages/lib/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-patterns-monorepo__lib\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-unresolved/index.ts",
    "content": "import 'missing-module';\nimport '#/ignored-unresolved-module';\nimport './ignored-by-regex';\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-unresolved/knip.ts",
    "content": "export default {\n  ignoreUnresolved: ['#/ignored-unresolved-module', /ignored.*regex/, 'unused-ignore'],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-unresolved/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-unresolved\",\n  \"type\": \"module\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-unresolved2/knip.json",
    "content": "{\n  \"$schema\": \"https://unpkg.com/knip@6/schema.json\",\n  \"ignoreUnresolved\": [\n    \"./unresolved-top-level\",\n    \"unused-top-level\",\n    \"./unresolved-workspace\"\n  ],\n  \"workspaces\": {\n    \".\": {\n      \"ignoreUnresolved\": [\".+unresolved-root\", \"unused-root\"]\n    },\n    \"packages/client\": {\n      \"ignoreUnresolved\": [\".*unresolved-workspace\", \"unused-workspace\"]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-unresolved2/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-unresolved2\",\n  \"workspaces\": [\n    \"packages/*\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-unresolved2/packages/client/package.json",
    "content": "{\n  \"name\": \"@fixtures/ignore-unresolved2__client\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ignore-unresolved2/packages/client/src/index.ts",
    "content": "import './unresolved-top-level';\nimport './unresolved-root';\nimport './unresolved-workspace';\n"
  },
  {
    "path": "packages/knip/fixtures/import-equals/index.ts",
    "content": "import * as NS from './my-module.js';\nimport local = require('./local.js');\nimport external = require('external');\nimport something = NS.something;\nlocal;\nexternal;\nsomething;\n"
  },
  {
    "path": "packages/knip/fixtures/import-equals/local.ts",
    "content": "export default () => 1;\n"
  },
  {
    "path": "packages/knip/fixtures/import-equals/my-module.ts",
    "content": "export const something = {};\n"
  },
  {
    "path": "packages/knip/fixtures/import-equals/package.json",
    "content": "{\n  \"name\": \"@fixtures/import-equals\",\n  \"dependencies\": {\n    \"external\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/import-equals/tsconfig.json",
    "content": "{}\n"
  },
  {
    "path": "packages/knip/fixtures/import-errors/index.ts",
    "content": "import 'package';\n"
  },
  {
    "path": "packages/knip/fixtures/import-errors/package.json",
    "content": "{\n  \"name\": \"@fixtures/import-errors\",\n  \"devDependencies\": {\n    \"package\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/import-meta-glob/animals/cat.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/import-meta-glob/animals/dog.ts",
    "content": "import 'vite';\n"
  },
  {
    "path": "packages/knip/fixtures/import-meta-glob/animals/horse.ts",
    "content": "import 'vite';\n"
  },
  {
    "path": "packages/knip/fixtures/import-meta-glob/flowers/rose.astro",
    "content": "---\nimport 'astro';\n---\n"
  },
  {
    "path": "packages/knip/fixtures/import-meta-glob/flowers/tulip.astro",
    "content": "---\nimport 'astro';\n---\n"
  },
  {
    "path": "packages/knip/fixtures/import-meta-glob/index.ts",
    "content": "import.meta.glob('./shapes/*.vue');\n\nimport.meta.glob(['./animals/*.ts', '!./animals/cat.ts', './flowers/**/*.astro']);\n"
  },
  {
    "path": "packages/knip/fixtures/import-meta-glob/package.json",
    "content": "{\n  \"name\": \"@fixtures/import-meta-glob\",\n  \"devDependencies\": {\n    \"astro\": \"*\",\n    \"vite\": \"*\",\n    \"vue\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/import-meta-glob/shapes/circle.vue",
    "content": "<script>import 'vue'</script>\n"
  },
  {
    "path": "packages/knip/fixtures/import-meta-glob/shapes/square.vue",
    "content": "<script>import 'vue'</script>\n"
  },
  {
    "path": "packages/knip/fixtures/import-named-default-id/index.ts",
    "content": "import utilOne from './utils';\nutilOne();\n"
  },
  {
    "path": "packages/knip/fixtures/import-named-default-id/package.json",
    "content": "{\n  \"name\": \"@fixtures/import-named-default-id\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/import-named-default-id/utils.ts",
    "content": "export const utilOne = () => 1;\nconst utilTwo = () => 2;\nexport default utilTwo;\n"
  },
  {
    "path": "packages/knip/fixtures/import-star-iteration/fruit.ts",
    "content": "export class Orange {\n  public message = 'I am an orange';\n}\n\nexport class Apple {\n  public message = 'I am an apple';\n}\n"
  },
  {
    "path": "packages/knip/fixtures/import-star-iteration/index.ts",
    "content": "import * as fruitClasses from './fruit';\nimport * as veggieClasses from './vegetables';\n\n// Outputs:\n// I am an orange\n// I am an apple\n// I am broccoli\n// I am spinach\n\nfor (const className in fruitClasses) {\n  const classInstance = new fruitClasses[className]();\n  classInstance.message;\n}\n\nfor (const myClass of veggieClasses) {\n  const classInstance = new myClass();\n  classInstance.message;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/import-star-iteration/package.json",
    "content": "{\n  \"name\": \"@fixtures/import-star-iteration\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/import-star-iteration/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {}\n}\n"
  },
  {
    "path": "packages/knip/fixtures/import-star-iteration/vegetables.ts",
    "content": "class Broccoli {\n  public message = 'I am broccoli';\n}\n\nclass Spinach {\n  public message = 'I am spinach';\n}\n\n// This is contrived, but this leads to us being able to use for (...of) in index.ts\nconst veggieClasses = [Broccoli, Spinach];\nexport = veggieClasses; // Makes the file a module\n"
  },
  {
    "path": "packages/knip/fixtures/imports/aliased-binding.ts",
    "content": "export const named = 'named';\n"
  },
  {
    "path": "packages/knip/fixtures/imports/await-import-call.ts",
    "content": "import console = require('node:console');\nconsole.log('side effect');\n"
  },
  {
    "path": "packages/knip/fixtures/imports/await-import.ts",
    "content": "export default () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/imports/catch.ts",
    "content": "export const identifier15 = 0;\n"
  },
  {
    "path": "packages/knip/fixtures/imports/default-and-named-binding.ts",
    "content": "export const named = 'named';\nexport default 'default';\n"
  },
  {
    "path": "packages/knip/fixtures/imports/default-identifier.ts",
    "content": "export default 'default';\n"
  },
  {
    "path": "packages/knip/fixtures/imports/default-prop-access.ts",
    "content": "export default 'default-prop-access';\n\nexport const elementAccess = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/imports/dir/import-b.ts",
    "content": "export default 1;\n\nexport const dynamic = () => 1;\n\nexport const named = { default: 1, identifier13: 13, identifier14: 14 };\n"
  },
  {
    "path": "packages/knip/fixtures/imports/dir/import-f.ts",
    "content": "export const dynamic = () => 1;\n\nexport const named = { default: 1, identifier13: 13, identifier14: 14 };\n"
  },
  {
    "path": "packages/knip/fixtures/imports/dir/mod.ts",
    "content": "export default 1;\n"
  },
  {
    "path": "packages/knip/fixtures/imports/empty-named-bindings.ts",
    "content": "// side-effects\n"
  },
  {
    "path": "packages/knip/fixtures/imports/import-a.ts",
    "content": "export default 'identifierA';\n"
  },
  {
    "path": "packages/knip/fixtures/imports/import-c.ts",
    "content": "export default 'c';\n\nexport const namedC = 'namedC';\n"
  },
  {
    "path": "packages/knip/fixtures/imports/import-d.ts",
    "content": "export default 'd';\n"
  },
  {
    "path": "packages/knip/fixtures/imports/import-e.ts",
    "content": "export default 'e';\n"
  },
  {
    "path": "packages/knip/fixtures/imports/import-g.ts",
    "content": "export function func() {}\n"
  },
  {
    "path": "packages/knip/fixtures/imports/import-meta-resolve.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/imports/index.ts",
    "content": "import './side-effects';\nimport { named as renamed } from './aliased-binding.js';\nimport defaultName1, { named as renamed2 } from './default-and-named-binding.js';\nimport defaultName2 from './default-identifier.js';\nimport { named as renamed3 } from './named-object-binding.js';\nimport type {} from './empty-named-bindings.js';\n\nconst fn = (_: any) => {};\n\nconst topLevel = await import('./top-level-await-import.js');\nconst { top } = await import('./top-level-await-import.js');\n\nconst dynamicB = () => import('./dir/import-b.js').then(m => m.dynamic);\n\nconst dynamicF = () => import('./dir/import-f.js').then(({ dynamic, named: renamed }) => [dynamic, renamed]);\n\nconst dynamicG = import('./import-g.js');\ndynamicG.then(module => module.func());\n\nimport('./top-level-side-effects-call.js');\n\nasync function main() {\n  import('./side-effects-call.js');\n  await import('./await-import-call.js');\n  const { default: defaultName, identifier11: renamedIdentifier, identifier12 } = await import('./object-bindings.js');\n\n  [defaultName, renamedIdentifier, identifier12];\n}\n\nconst dynamicImport = (value: string) => {\n  return import(`./dir/${value}`);\n};\n\nconst templateStringExternal = () => {\n  return import('./no-substitution-tpl-literal.js');\n};\n\nconst templateStringLiteral = () => {\n  return import('./string-literal.js');\n};\n\nconst templateStringInternal = () => {\n  return import('./dir/mod.js');\n};\n\nconst importMetaResolve = () => {\n  return import.meta.resolve('./import-meta-resolve.js');\n};\n\nfunction promiseAll() {\n  return {\n    async fn() {\n      const [identifierA, { default: identifierB }] = await Promise.all([\n        import('./import-a.js'),\n        import('./dir/import-b.js'),\n        import('./dir/import-b.js'),\n      ]);\n\n      [identifierA, identifierB];\n    },\n  };\n}\n\nfunction promiseTail() {\n  return {\n    async fn() {\n      const [, , identifierB] = await Promise.all([\n        import('./dir/import-b.js'),\n        import('./dir/import-b.js'),\n        import('./dir/import-b.js'),\n      ]);\n\n      [identifierB];\n    },\n  };\n}\n\n(await import('./prop-access.js')).propAccess;\n\nconst {\n  default: defaultName3,\n  identifier13: renamedIdentifier,\n  identifier14,\n} = (await import('./dir/import-b.js'))['named'];\n\nconst defaultName4 = (await import('./default-prop-access.js')).default;\n\n(await import('./default-prop-access.js'))['elementAccess'];\n\nimport('./promise-like').then(f => f).catch(err => err);\n\nconst [defaultName5, { default: renamedIdentifier2, namedC }] = await Promise.all([\n  import('./import-a.js'),\n  import('./import-c.js'),\n]);\n\nconst child1 = fn(() => import('./import-c.js'));\n\nconst { identifier15 } = await import('./catch.js').catch(() => {\n  throw new Error('caught');\n});\n\nconst importPromise = import('./await-import.js');\nconst { default: awaitedImport } = await importPromise;\nawaitedImport();\n\nexport default fn({\n  components: {\n    child1,\n    child2: () => import('./import-d.js'),\n    child3: import('./import-e.js'),\n  },\n});\n\n[\n  topLevel,\n  top,\n  dynamicB,\n  dynamicF,\n  defaultName1,\n  defaultName2,\n  defaultName3,\n  defaultName4,\n  elementAccess,\n  defaultName5,\n  renamed,\n  renamed2,\n  renamed3,\n  renamedIdentifier,\n  renamedIdentifier2,\n  namedC,\n  identifier14,\n  identifier15,\n];\n"
  },
  {
    "path": "packages/knip/fixtures/imports/named-object-binding.ts",
    "content": "export const named = 'named';\n"
  },
  {
    "path": "packages/knip/fixtures/imports/no-substitution-tpl-literal.ts",
    "content": "export default 'no-substitution-tpl-literal';\n"
  },
  {
    "path": "packages/knip/fixtures/imports/object-bindings.ts",
    "content": "export default 'default';\nexport const identifier11 = 'identifier11';\nexport const identifier12 = 'identifier12';\n"
  },
  {
    "path": "packages/knip/fixtures/imports/package.json",
    "content": "{\n  \"name\": \"@fixtures/imports\",\n  \"type\": \"module\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/imports/promise-like.ts",
    "content": "export default 'f';\n"
  },
  {
    "path": "packages/knip/fixtures/imports/prop-access.ts",
    "content": "export const propAccess = 'prop-access';\n"
  },
  {
    "path": "packages/knip/fixtures/imports/side-effects-call.ts",
    "content": "import console = require('node:console');\nconsole.log('side effect');\n"
  },
  {
    "path": "packages/knip/fixtures/imports/side-effects.ts",
    "content": "// side effect\n"
  },
  {
    "path": "packages/knip/fixtures/imports/string-literal.ts",
    "content": "export default 'string-literal';\n"
  },
  {
    "path": "packages/knip/fixtures/imports/top-level-await-import.ts",
    "content": "export default 'top-level';\n\nexport const top = 'top';\n"
  },
  {
    "path": "packages/knip/fixtures/imports/top-level-side-effects-call.ts",
    "content": "import console from 'node:console';\nconsole.log('side effect');\n"
  },
  {
    "path": "packages/knip/fixtures/imports/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"types\": [\"node\"],\n    \"esModuleInterop\": true,\n    \"module\": \"nodenext\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/imports-destructure-spread/index.js",
    "content": "import * as trees from './trees';\nimport { cedar } from './trees';\n\nconst { oak, ...rest } = trees;\n\noak;\ncedar;\nrest;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-destructure-spread/package.json",
    "content": "{\n  \"name\": \"@fixtures/imports-destructure-spread\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/imports-destructure-spread/trees.js",
    "content": "export const oak = 1;\nexport const pine = 1;\nexport const maple = 1;\nexport const cedar = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-dynamic-access/fruits.ts",
    "content": "export default 'fruit';\nexport const apple = 'apple';\nexport const banana = 'banana';\nexport const cherry = 'cherry';\nexport const durian = 'durian';\nexport const elderberry = 'elderberry';\nexport const fig = 'fig';\nexport const grape = 'grape';\n"
  },
  {
    "path": "packages/knip/fixtures/imports-dynamic-access/index.ts",
    "content": "async function fn() {\n  const fruit = await import('./fruits.ts');\n  fruit.default;\n\n  const { apple, banana: b } = await import('./fruits.ts');\n  apple;\n  b;\n\n  const module = await import('./fruits.ts');\n  module.cherry;\n\n  {\n    const { durian, elderberry: e } = module;\n    durian;\n    e;\n  }\n\n  const notModule = { fig: false, grape: false };\n  const { grape } = notModule;\n  notModule.fig;\n  grape;\n}\n\nawait fn();\n\nexport {};\n"
  },
  {
    "path": "packages/knip/fixtures/imports-dynamic-access/package.json",
    "content": "{\n  \"name\": \"@fixtures/imports-dynamic\",\n  \"type\": \"module\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace/index.ts",
    "content": "export * as ReExported from './re-exported-module';\nimport * as NS from './namespace';\nimport * as NS2 from './namespace2';\nimport * as NS3 from './namespace3';\nimport * as NS3_OPAQUE from './namespace3-opaque';\nimport * as NS4 from './namespace4';\nimport * as NS5 from './namespace5';\nimport * as NS5_OPAQUE from './namespace5-opaque';\nimport * as NS6 from './namespace6';\nimport * as NS6_OPAQUE from './namespace6-opaque';\nimport * as NS7 from './namespace7';\nimport * as NS8 from './namespace8';\nimport * as NS9 from './namespace9';\nimport * as NS10 from './namespace10';\nimport fn from 'external';\n\nNS.identifier15;\nNS['identifier16'];\nNS.identifier17();\n\nconst { identifier18, identifier19, identifier20 } = NS2;\n\nNS2.identifier21.method();\n\nfunction usage() {\n  const hello = { NS3 };\n  hello.NS3.identifier31;\n  const goodbye = { NS3_OPAQUE };\n}\n\nfn(NS4);\n\nconst spread = { ...NS5 };\nspread.identifier35;\n\nconst butter = { ...NS5_OPAQUE };\n\nconst assign = NS6;\nassign.identifier37;\n\nconst allot = NS6_OPAQUE;\n\nfn([NS7]);\n\nfn({ NS8 });\n\nconst func = () => {\n  const cond = fn() ? NS9 : { identifier43: 43 };\n  cond.identifier43;\n};\n\nconst props: { values?: { identifier45: number; identifier46: number } } = {};\nconst { values = NS10 } = props;\nvalues.identifier45;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace/namespace.ts",
    "content": "export const identifier15 = 1;\nexport const identifier16 = 1;\nexport const identifier17 = () => 1;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace/namespace10.ts",
    "content": "export const identifier45 = 45;\nexport const identifier46 = 46;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace/namespace2.ts",
    "content": "export const identifier18 = 1;\nexport const identifier19 = 1;\nexport const identifier20 = () => 1;\nexport const identifier21 = { method: () => 1 };\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace/namespace3-opaque.ts",
    "content": "export const identifier31 = 31;\nexport const identifier32 = 32;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace/namespace3.ts",
    "content": "export const identifier31 = 31;\nexport const identifier32 = 32;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace/namespace4.ts",
    "content": "export const identifier33 = 33;\nexport const identifier34 = 34;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace/namespace5-opaque.ts",
    "content": "export const identifier35 = 35;\nexport const identifier36 = 36;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace/namespace5.ts",
    "content": "export const identifier35 = 35;\nexport const identifier36 = 36;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace/namespace6-opaque.ts",
    "content": "export const identifier37 = 37;\nexport const identifier38 = 38;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace/namespace6.ts",
    "content": "export const identifier37 = 37;\nexport const identifier38 = 38;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace/namespace7.ts",
    "content": "export const identifier39 = 39;\nexport const identifier40 = 40;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace/namespace8.ts",
    "content": "export const identifier41 = 41;\nexport const identifier42 = 42;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace/namespace9.ts",
    "content": "export const identifier43 = 43;\nexport const identifier44 = 44;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace/package.json",
    "content": "{\n  \"name\": \"@fixtures/imports-namespace\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace/re-exported-module.ts",
    "content": "const myFunction = () => void 0;\n\nexport default myFunction;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace-jsx/components.tsx",
    "content": "export const Container = () => <div />;\nexport const Header = () => <h1 />;\nexport const Footer = () => <footer />;\nexport const Unused = () => <span />;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace-jsx/index.tsx",
    "content": "import * as Layout from './components';\n\nexport const App = () => (\n  <Layout.Container>\n    <Layout.Header />\n    <Layout.Footer />\n  </Layout.Container>\n);\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace-jsx/package.json",
    "content": "{\n  \"name\": \"imports-namespace-jsx\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace-with-nsexports/index.ts",
    "content": "export * as ReExported from './re-exported-module';\nimport * as NS from './namespace';\nimport * as NS2 from './namespace2';\nimport * as NS3 from './namespace3';\nimport * as NS4 from './namespace4';\nimport * as NS5 from './namespace5';\nimport * as NS6 from './namespace6';\nimport fn from 'external';\n\nNS.identifier15;\nNS['identifier16'];\nNS.identifier17();\n\nconst { identifier18, identifier19, identifier20 } = NS2;\n\nNS2.identifier21.method();\n\nfunction usage() {\n  const hello = { NS3 };\n}\n\nfn(NS4);\n\nconst spread = { ...NS5 };\n\nconst assign = NS6;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace-with-nsexports/namespace.ts",
    "content": "export const identifier15 = 1;\nexport const identifier16 = 1;\nexport const identifier17 = () => 1;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace-with-nsexports/namespace2.ts",
    "content": "export const identifier18 = 1;\nexport const identifier19 = 1;\nexport const identifier20 = () => 1;\nexport const identifier21 = { method: () => 1 };\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace-with-nsexports/namespace3.ts",
    "content": "export const identifier31 = 31;\nexport const identifier32 = 32;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace-with-nsexports/namespace4.ts",
    "content": "export const identifier33 = 33;\nexport const identifier34 = 34;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace-with-nsexports/namespace5.ts",
    "content": "export const identifier35 = 35;\nexport const identifier36 = 36;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace-with-nsexports/namespace6.ts",
    "content": "export const identifier37 = 37;\nexport const identifier38 = 38;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace-with-nsexports/package.json",
    "content": "{\n  \"name\": \"@fixtures/imports-namespace-with-nsexports\",\n  \"knip\": {\n    \"include\": [\n      \"nsExports\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/imports-namespace-with-nsexports/re-exported-module.ts",
    "content": "const myFunction = () => void 0;\n\nexport default myFunction;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-opaque/arrow.ts",
    "content": "export default 1;\nexport const named = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-opaque/assignment.ts",
    "content": "export default 1;\nexport const named = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-opaque/awaited-arrow.ts",
    "content": "export default 1;\nexport const named = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-opaque/awaited-assignment.ts",
    "content": "export default 1;\nexport const named = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-opaque/awaited-fn-arg.ts",
    "content": "export default 1;\nexport const named = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-opaque/awaited-return.ts",
    "content": "export default 1;\nexport const named = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-opaque/direct.ts",
    "content": "export default 1;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-opaque/fn-arg.ts",
    "content": "export default 1;\nexport const named = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-opaque/index.ts",
    "content": "function fn(a: any, b: any) {\n  return import('./return.js');\n}\n\nasync function asyncFn(a: any) {\n  return await import('./awaited-return.js');\n}\n\nfn(0, await import('./awaited-fn-arg.js'));\n\nfn(import('./fn-arg.js'), 0);\n\n() => import('./arrow.js');\n\nasync () => await import('./awaited-arrow.js');\n\nconst obj = {\n  key: import('./assignment.js'),\n  key2: await import('./awaited-assignment.js'),\n  ...(await import('./obj-spread.js')),\n};\n\nconst loader = () => import('./variable.js');\nfunction fnRun(load: () => Promise<unknown>) {\n  return load();\n}\nfnRun(loader);\n\nconst direct = import('./direct.js');\nfn(direct, 0);\n"
  },
  {
    "path": "packages/knip/fixtures/imports-opaque/obj-spread.ts",
    "content": "export default 1;\nexport const named = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-opaque/package.json",
    "content": "{\n  \"name\": \"@fixtures/imports-opaque\",\n  \"type\": \"module\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/imports-opaque/return.ts",
    "content": "export default 1;\nexport const named = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-opaque/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"module\": \"nodenext\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/imports-opaque/variable.ts",
    "content": "export default 1;\nexport const named = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-prop-access-call/exists.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/imports-prop-access-call/index.ts",
    "content": "require.resolve('./exists.js');\n\nif ('serviceWorker' in navigator) {\n  // TODO Implement?\n  // navigator.serviceWorker.register('worker3.js');\n}\n"
  },
  {
    "path": "packages/knip/fixtures/imports-prop-access-call/package.json",
    "content": "{\n  \"name\": \"@fixtures/imports-prop-access-call\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/imports-self/exports.js",
    "content": "import * as NS from './exports.js';\n\nexport { NS };\n\nexport const member = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-self/index.js",
    "content": "import * as NS from './exports.js';\n\nNS.member;\nNS.member.member;\nNS.member.member.member;\nNS.NS.member;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-self/package.json",
    "content": "{\n  \"name\": \"@fixtures/imports-self\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/imports-typeof/index.ts",
    "content": "let externalModule: typeof import('external') | undefined;\n"
  },
  {
    "path": "packages/knip/fixtures/imports-typeof/package.json",
    "content": "{\n  \"name\": \"@fixtures/imports-typeof\",\n  \"devDependencies\": {\n    \"external\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/include-entry-exports/cli.js",
    "content": "module.exports.a = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/include-entry-exports/index.ts",
    "content": "import { y } from './mod';\ny;\nexport default 1;\n\nexport type EntryType = {};\nexport interface EntryInterface {}\nexport enum EntryEnum {}\n"
  },
  {
    "path": "packages/knip/fixtures/include-entry-exports/main.ts",
    "content": "export const x = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/include-entry-exports/mod.ts",
    "content": "export const y = 0;\n"
  },
  {
    "path": "packages/knip/fixtures/include-entry-exports/package.json",
    "content": "{\n  \"name\": \"@fixtures/include-entry-exports\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/include-entry-exports-scripts/knip.json",
    "content": "{\n  \"project\": [\"src/**/*.ts\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/include-entry-exports-scripts/next.config.js",
    "content": "module.exports = () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/include-entry-exports-scripts/package.json",
    "content": "{\n  \"name\": \"@fixtures/include-entry-exports-scripts\",\n  \"description\": \"\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n    \"module1\": \"tsx ./src/script1.ts\",\n    \"module2\": \"tsx ./src/script2.ts\",\n    \"module3\": \"tsx ./src/script3.ts\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"next\": \"*\"\n  },\n  \"devDependencies\": {\n    \"knip\": \"2.13.0\",\n    \"tsx\": \"3.12.7\",\n    \"typescript\": \"5.1.3\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/include-entry-exports-scripts/src/pages/index.js",
    "content": "import Head from 'next/head';\n\nfunction IndexPage() {\n  return (\n    <div>\n      <Head>\n        <title>My page title</title>\n      </Head>\n    </div>\n  );\n}\n\nexport default IndexPage;\n"
  },
  {
    "path": "packages/knip/fixtures/include-entry-exports-scripts/src/script1.ts",
    "content": "export function sum(a: number, b: number) {\n  return a + b;\n}\n\nexport function multiply(a: number, b: number) {\n  return a * b;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/include-entry-exports-scripts/src/script2.ts",
    "content": "export function subtract(a: number, b: number) {\n  return a - b;\n}\n\nexport const test = 23;\n"
  },
  {
    "path": "packages/knip/fixtures/include-entry-exports-scripts/src/script3.ts",
    "content": "import { subtract } from './script2.js';\n\nexport function calculate(a: number, b: number) {\n  return subtract(a, b);\n}\n\nexport const smth = 'smth';\n"
  },
  {
    "path": "packages/knip/fixtures/include-entry-reexports/package.json",
    "content": "{\n  \"name\": \"@fixtures/include-entry-reexports\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"workspaces\": [\n    \"packages/*\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/include-entry-reexports/packages/app/index.mjs",
    "content": "import { identifierA } from '@fixtures/include-entry-reexports__shared';\n\nidentifierA();\n"
  },
  {
    "path": "packages/knip/fixtures/include-entry-reexports/packages/app/package.json",
    "content": "{\n  \"name\": \"@fixtures/include-entry-reexports__app\",\n  \"private\": true,\n  \"main\": \"index.mjs\",\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@fixtures/include-entry-reexports__shared\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/include-entry-reexports/packages/shared/index.mjs",
    "content": "export * from './module-a.mjs';\nexport * from './module-b.mjs';\n"
  },
  {
    "path": "packages/knip/fixtures/include-entry-reexports/packages/shared/module-a.mjs",
    "content": "export const identifierA = () => 'identifier-a';\n"
  },
  {
    "path": "packages/knip/fixtures/include-entry-reexports/packages/shared/module-b.mjs",
    "content": "export const identifierB = () => 'identifier-b';\n"
  },
  {
    "path": "packages/knip/fixtures/include-entry-reexports/packages/shared/package.json",
    "content": "{\n  \"name\": \"@fixtures/include-entry-reexports__shared\",\n  \"private\": true,\n  \"main\": \"index.mjs\",\n  \"type\": \"module\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/include-libs/components.ts",
    "content": "export const Apple = () => 'Apple!';\nexport const Orange = () => 'Orange!';\n"
  },
  {
    "path": "packages/knip/fixtures/include-libs/index.tsx",
    "content": "import { LoadableApple, LoadableOrange } from './loadable.js';\n\nLoadableApple;\nLoadableOrange;\n"
  },
  {
    "path": "packages/knip/fixtures/include-libs/loadable.ts",
    "content": "import loadable from '@loadable/component';\n\nexport const LoadableApple = loadable(() => import('./components.js'), {\n  resolveComponent: components => components.Apple,\n});\n\nexport const LoadableOrange = loadable(() => import('./components.js'), {\n  resolveComponent: components => components.Orange,\n});\n"
  },
  {
    "path": "packages/knip/fixtures/include-libs/package.json",
    "content": "{\n  \"name\": \"@fixtures/include-libs\",\n  \"dependencies\": {\n    \"@loadable/component\": \"^5.16.3\",\n    \"@types/loadable__component\": \"^5.13.9\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/include-libs/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"jsx\": \"preserve\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/js-only/dangling.js",
    "content": "export const unusedFile = true;\n"
  },
  {
    "path": "packages/knip/fixtures/js-only/index.js",
    "content": "import * as MyNamespace from './my-namespace';\n\nexport const b = MyNamespace.y;\n"
  },
  {
    "path": "packages/knip/fixtures/js-only/my-namespace.js",
    "content": "export const x = 1;\nexport const y = () => x;\nexport const z = 3;\n"
  },
  {
    "path": "packages/knip/fixtures/js-only/package.json",
    "content": "{\n  \"name\": \"@fixtures/js-only\",\n  \"knip\": {\n    \"entry\": [\n      \"index.js\"\n    ],\n    \"project\": [\n      \"*.js\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/jsdoc/index.ts",
    "content": "const ignorePatterns = [];\n\n/** @type {import('some-types').Module} */\nconst obj = {};\n\n/**\n * @returns {Promise<import('type-fest').PackageJson>}\n */\nconst getPackageManifest = async () => ({});\n\n/** @type {string | null} */\nconst str = 'str';\n\nfunction fn() {\n  /** @type {import('more-types')} */\n  const obj = {};\n}\n\n/** @import { SomeType } from \"some-module\" */\n\n/**\n * @param {SomeType} myValue\n */\nfunction doSomething1(myValue) {\n  // ...\n}\n\n/** @import * as someModule from \"some-other-module\" */\n\n/**\n * @param {someModule.SomeType} myValue\n */\nfunction doSomething2(myValue) {\n  // ...\n}\n\n/** @type {import('@jest/types')} */\nmodule.exports = {};\n\n// import('./should-not-resolve')\n// See import('./also-should-not-resolve') for details\n\n/**\n * Example usage:\n * const LazyComponent = lazy(() => import('./myComponent'))\n * <LazyLoad component={LazyComponent} />\n */\nfunction lazyLoad() {}\n"
  },
  {
    "path": "packages/knip/fixtures/jsdoc/package.json",
    "content": "{\n  \"name\": \"@fixtures/jsdoc\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/jsdoc-exports/index.ts",
    "content": "export { publicFn } from './module';\n"
  },
  {
    "path": "packages/knip/fixtures/jsdoc-exports/module.test.js",
    "content": "import { test } from 'bun:test';\n\nimport { publicFn, internalTestedFn } from './module';\n\n/** @param {import('./module').UsedViaJSDoc} config */\nfunction configure(config) {}\n\ntest('public fn', () => {\n  publicFn();\n});\n\ntest('internal fn', () => {\n  internalTestedFn();\n});\n"
  },
  {
    "path": "packages/knip/fixtures/jsdoc-exports/module.ts",
    "content": "export const unusedFn = () => {};\n\n/** @internal */\nexport const internalTestedFn = () => {};\n\n/** @internal */\nexport const internalUnusedFn = () => {};\n\n/** @alpha */\nexport const alphaFn = () => {};\n\n/** @beta */\nexport const betaFn = () => {};\n\n/** @public */\nexport const publicFn = () => {};\n\n/** @unknown @ invalid */\nexport const invalidTaggedFn = () => {};\n\n/** @public */\nexport function overloadFn(x: number): string;\nexport function overloadFn(x: boolean): number;\nexport function overloadFn(x: number | boolean): string | number {\n  return '0';\n}\n\n/** @ignoreunresolved */\nimport unresolvedAndIgnored from './unresolved';\n\n/** @ignoreunresolved */\nexport * from './something.generated';\n\n/** @ignoreunresolved */\nconst NS = require('./commmonjs');\n\nexport interface UsedViaJSDoc {\n  name: string;\n}\n\nexport interface UnusedInterface {\n  value: number;\n}\n\n/** @internal */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface InternalWithLineComment {}\n"
  },
  {
    "path": "packages/knip/fixtures/jsdoc-exports/package.json",
    "content": "{\n  \"name\": \"@fixtures/jsdoc-exports\",\n  \"scripts\": {\n    \"test\": \"node --test\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/jsx/App.tsx",
    "content": "import React from 'react';\n\nconst Component = React.lazy(() => import('./Component'));\n\nfunction MainComponent() {\n  return (\n    <main>\n      <Component />\n    </main>\n  );\n}\n"
  },
  {
    "path": "packages/knip/fixtures/jsx/Component.tsx",
    "content": "/** @jsxImportSource preact */\n\nfunction MyComponent() {\n  return <div>exciting</div>;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/jsx/package.json",
    "content": "{\n  \"name\": \"@fixtures/jsx\",\n  \"dependencies\": {\n    \"react\": \"*\",\n    \"preact\": \"*\"\n  },\n  \"knip\": {\n    \"entry\": [\n      \"App.tsx\"\n    ],\n    \"project\": [\n      \"*.tsx\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/jsx/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"jsx\": \"react-jsx\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/load-cjs/index.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/load-cjs/package.json",
    "content": "{\n  \"name\": \"@fixtures/load-cjs\",\n  \"type\": \"commonjs\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/load-esm/index.js",
    "content": "// The CJS build of Vite's Node API is deprecated. See https://vitejs.dev/guide/troubleshooting.html#vite-cjs-node-api-deprecated for more details.\n\n// SyntaxError: Cannot use 'import.meta' outside a module\n\nimport.meta;\nimport.meta.resolve;\nimport.meta.url;\n\n// SyntaxError: await is only valid in async functions and the top level bodies of modules\n\n// SyntaxError: missing ) after argument list\n\n// SyntaxError: Unexpected identifier 'Promise'\nPromise;\n\n// Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No \"exports\" main defined in [...]/node_modules/estree-walker/package.json\n\nawait Promise.resolve('TLA');\n"
  },
  {
    "path": "packages/knip/fixtures/load-esm/package.json",
    "content": "{\n  \"name\": \"@fixtures/load-esm\",\n  \"type\": \"module\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/load-esm-ts/index.ts",
    "content": "// SyntaxError: Cannot use 'import.meta' outside a module\n\nimport.meta;\nimport.meta.resolve;\nimport.meta.url;\n\n// SyntaxError: Unexpected identifier 'Promise'\n\nPromise;\n\n// TypeError: Reflect.metadata is not a function\n\nfunction meow(target: unknown, _: unknown) {}\n\n@meow\nexport class Cat {\n  constructor() {\n    this;\n  }\n}\n\n// SyntaxError: await is only valid in async functions and the top level bodies of modules\n\nawait Promise.resolve('TLA');\n"
  },
  {
    "path": "packages/knip/fixtures/load-esm-ts/package.json",
    "content": "{\n  \"name\": \"@fixtures/load-esm-ts\",\n  \"type\": \"module\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/load-esm-ts/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"module\": \"esnext\",\n    \"experimentalDecorators\": true,\n    \"emitDecoratorMetadata\": true\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/load-json5/config.json5",
    "content": "{\n  // This is a JSON5 file with comments\n  \"name\": \"test-config\",\n  \"plugins\": [\n    \"prettier-plugin-xml\",\n    \"prettier-plugin-tailwindcss\",\n  ], // trailing comma\n  \"enabled\": true,\n}"
  },
  {
    "path": "packages/knip/fixtures/module-block/index.ts",
    "content": "import './types';\n"
  },
  {
    "path": "packages/knip/fixtures/module-block/package.json",
    "content": "{\n  \"name\": \"@fixtures/module-block\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/module-block/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"isolatedModules\": false\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/module-block/types.ts",
    "content": "namespace MyNamespace {\n  export type Identifier = number;\n  export const identifier = 2;\n}\n\ntype NS_ID = MyNamespace.Identifier;\nconst ns_id = MyNamespace.identifier;\n"
  },
  {
    "path": "packages/knip/fixtures/module-register/ignored-loader.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/module-register/index.ts",
    "content": "import { register } from 'node:module';\nimport module from 'module';\n\nregister('@nodejs-loaders/tsx', import.meta.url);\nmodule.register('@nodejs-loaders/css-module', import.meta.url);\n\nregister('./loader.js', import.meta.url);\n\nregister('./ignored-loader.js', new URL('.', import.meta.url).href);\n\nregister('./ignored-loader.js', 'data:');\n\nregister('./ignored-loader.js', '..');\n\nregister('./ignored-loader.js');\n"
  },
  {
    "path": "packages/knip/fixtures/module-register/loader.js",
    "content": "export const initialize = 1;\nexport const load = 2;\n"
  },
  {
    "path": "packages/knip/fixtures/module-register/package.json",
    "content": "{\n  \"name\": \"@fixtures/module-register\",\n  \"devDependencies\": {\n    \"@nodejs-loaders/css-module\": \"*\",\n    \"@nodejs-loaders/tsx\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-baseurl-implicit-relative/package.json",
    "content": "{\n  \"name\": \"@fixtures/module-resolution-baseurl-implicit-relative\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-baseurl-implicit-relative/src/dir/main.ts",
    "content": "import Image from 'assets/image.svg';\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-baseurl-implicit-relative/src/hello/world.ts",
    "content": "export const helloWorld = 'Hello world!';\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-baseurl-implicit-relative/src/index.ts",
    "content": "import 'dir/main';\nimport { helloWorld } from 'hello/world';\n\nhelloWorld;\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-baseurl-implicit-relative/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \"src\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-non-std/.gitignore",
    "content": "dist\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-non-std/package.json",
    "content": "{\n  \"name\": \"@fixtures/module-resolution-non-std\",\n  \"dependencies\": {\n    \"@svg-icons/fa-brands\": \"*\",\n    \"@svg-icons/heroicons-outline\": \"*\"\n  },\n  \"devDependencies\": {\n    \"style-loader\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-non-std/src/globals.css",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/module-resolution-non-std/src/index.ts",
    "content": "import bin from '../dist/cli.cjs';\nimport Icon from './icon.svg?raw';\nimport Icon404 from './icon-404.svg';\nimport Styles1 from 'style-loader!css-loader?modules!./styles.css';\nimport Styles2 from '!style-loader!css-loader?modules!./styles.css';\nimport Styles3 from '!!style-loader!css-loader?modules!./styles.css';\nimport Styles4 from '-!style-loader!css-loader?modules!./styles.css';\nimport unresolved from './unresolved';\nimport unresolvedPkg from 'unresolved';\nimport unresolvedOrg from '@org/unresolved';\n\nimport Github from '@svg-icons/fa-brands/github.svg';\nimport Briefcase from '@svg-icons/heroicons-outline/briefcase.svg';\n\nimport SomeSVG from 'common/image.svg';\nimport SomePNG from 'common/image.png';\n\nimport './globals.css';\nimport 'styles/base.css';\n\nimport '~/styles/aliased.css';\nimport '~/common/aliased.svg';\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-non-std/src/styles/aliased.css",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/module-resolution-non-std/src/styles/base.css",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/module-resolution-non-std/src/unused.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/module-resolution-non-std/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"common/*\": [\"src/common/*\"],\n      \"styles/*\": [\"src/styles/*\"],\n      \"~/*\": [\"src/*\"]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-non-std-absolute/package.json",
    "content": "{\n  \"name\": \"@fixtures/module-resolution-non-std-absolute\",\n  \"workspaces\": [\n    \"x-other\",\n    \"x-self\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-non-std-absolute/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {}\n}\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-non-std-absolute/x-other/absolute.css",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/module-resolution-non-std-absolute/x-other/package.json",
    "content": "{\n  \"name\": \"x-other\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-non-std-absolute/x-self/absolute.css",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/module-resolution-non-std-absolute/x-self/index.ts",
    "content": "import 'x-other/absolute.svg';\nimport 'x-other/absolute.css';\nimport 'x-self';\nimport 'x-self/absolute.css';\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-non-std-absolute/x-self/package.json",
    "content": "{\n  \"name\": \"x-self\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-non-std-implicit/package.json",
    "content": "{\n  \"name\": \"@fixtures/module-resolution-non-std-implicit\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-non-std-implicit/src/dir/main.ts",
    "content": "import ImageSVG from '../assets/image.svg';\nimport ImagePNG from '../assets/image.png';\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-non-std-implicit/src/global.css",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/module-resolution-non-std-implicit/src/index.ts",
    "content": "import 'dir/main.ts';\n\nimport SomeSVG from './common/image.svg';\n\nimport Icon from './icon.svg';\n\nimport './global.css';\n\nimport 'styles/base.css';\n\nimport png from './not-found.png';\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-non-std-implicit/src/styles/base.css",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/module-resolution-tsconfig-paths/aliased-dir/a.ts",
    "content": "export const a = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-tsconfig-paths/components/IndexComponent/index.tsx",
    "content": "export const IndexComponent = () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-tsconfig-paths/components/MyComponent.tsx",
    "content": "export const MyComponent = () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-tsconfig-paths/index.ts",
    "content": "import { a } from '@alias/a';\nimport { pkg } from '@internal';\nimport * as Unknown from '@unknown';\nimport { unprefixed } from 'unprefixed';\nimport unresolved from 'unresolved/dir';\n\nconst LazyComponent = lazy(async () => ({\n  default: (await import('@/components/MyComponent')).MyComponent,\n}));\n\nconst IndexComponent = lazy(async () => ({\n  default: (await import('./components/IndexComponent')).IndexComponent,\n}));\n\na;\npkg;\nunprefixed;\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-tsconfig-paths/internal-package/index.ts",
    "content": "export const pkg = 1;\n\nexport const unused = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-tsconfig-paths/package.json",
    "content": "{\n  \"name\": \"@fixtures/module-resolution-tsconfig-paths\",\n  \"dependencies\": {\n    \"internal\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-tsconfig-paths/tsconfig.json",
    "content": "{\n  \"include\": [\"*.ts\"],\n  \"exclude\": [\"exclude.ts\"],\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"types\": [\"node\"],\n    \"paths\": {\n      \"@/*\": [\"./*\"],\n      \"@alias/*\": [\"aliased-dir/*\"],\n      \"@internal\": [\"internal-package/index.ts\"],\n      \"unprefixed\": [\"unprefixed/module.ts\"]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/module-resolution-tsconfig-paths/unprefixed/module.ts",
    "content": "export const unprefixed = 1;\n\nexport const unused = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/negated-production-paths/index.test.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/negated-production-paths/index.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/negated-production-paths/knip.json",
    "content": "{\n  \"entry\": [\"*.ts!\", \"!*.test.ts!\"],\n  \"project\": [\"**/*.ts!\", \"!*.test.ts!\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/negated-production-paths/package.json",
    "content": "{\n  \"name\": \"@fixtures/negated-production-paths\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/npm-scripts/.gitignore",
    "content": "dist\n"
  },
  {
    "path": "packages/knip/fixtures/npm-scripts/entry.ts",
    "content": "export const value = 42;\n"
  },
  {
    "path": "packages/knip/fixtures/npm-scripts/ignore.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/npm-scripts/package.json",
    "content": "{\n  \"name\": \"@fixtures/npm-scripts\",\n  \"scripts\": {\n    \"nodemon\": \"nodemon script.js\",\n    \"build\": \"rm -rf && dotenv -- nx build npm-scripts\",\n    \"pm2\": \"NODE_ENV=production pm2 start index.js\",\n    \"dev\": \"pm2-dev start index.js\",\n    \"lint\": \"eslint\",\n    \"commitlint\": \"npx commitlint\",\n    \"test\": \"bash test/unit.sh && bash test/e2e.sh\",\n    \"ignore\": \"node ignore.js\",\n    \"ignore-artifact\": \"node dist/ignore.js\",\n    \"start\": \"package\",\n    \"go\": \"runnable\",\n    \"go-live\": \"deploy -r repo_name\",\n    \"tsup\": \"tsup -c tsup.cool.json\",\n    \"tsuppy\": \"tsup --config tsup.cool.json\",\n    \"serve\": \"http-server\",\n    \"bun\": \"bun run ./entry\",\n    \"approuter\": \"node node_modules/@sap/approuter/approuter.js\",\n    \"@ignore\": \"ignored\",\n    \"#ignore\": \"ignored\"\n  },\n  \"dependencies\": {\n    \"@sap/approuter\": \"*\",\n    \"express\": \"*\",\n    \"package-cli\": \"*\"\n  },\n  \"peerDependencies\": {\n    \"unused-peer-dep\": \"*\"\n  },\n  \"devDependencies\": {\n    \"pm2\": \"*\",\n    \"nx\": \"*\",\n    \"tsup\": \"*\",\n    \"unused\": \"*\",\n    \"eslint\": \"8.28.0\",\n    \"eslint-v6\": \"npm:eslint@6.0.1\",\n    \"eslint-v7\": \"npm:eslint@7.0.0\",\n    \"eslint-v8\": \"npm:eslint@8.0.2\",\n    \"@commitlint/cli\": \"*\",\n    \"@org/runnable\": \"*\"\n  },\n  \"knip\": {\n    \"ignore\": \"ignore.js\",\n    \"ignoreBinaries\": [\n      \"rm\",\n      \"bash\",\n      \"deploy\",\n      \"eslint\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/npm-scripts/script.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/ns-spread-reexport/aliased-import.ts",
    "content": "// Tests importsForExport.importAs: `import { foo as bar }`\nimport { resolvers as myResolvers } from './resolvers.js';\n\nexport function useAliasedResolvers() {\n  return myResolvers;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ns-spread-reexport/animals.ts",
    "content": "export const cat = 'cat';\nexport const dog = 'dog';\n"
  },
  {
    "path": "packages/knip/fixtures/ns-spread-reexport/colors.ts",
    "content": "export const red = 'red';\nexport const blue = 'blue';\n"
  },
  {
    "path": "packages/knip/fixtures/ns-spread-reexport/consumer.ts",
    "content": "// Consumes the spread namespace via named import\nimport { utilsAlias } from './ns-alias-reexport.js';\n\nexport function useUtils() {\n  return utilsAlias;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ns-spread-reexport/destructured.ts",
    "content": "// NOT strictly NS: destructured from namespace\nimport * as Animals from './animals.js';\n\nconst { cat } = Animals;\n\nexport function useAnimal() {\n  return cat;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ns-spread-reexport/fruits.ts",
    "content": "export const apple = 'apple';\nexport const banana = 'banana';\n"
  },
  {
    "path": "packages/knip/fixtures/ns-spread-reexport/hello.resolver.ts",
    "content": "export const Hello = 'hi';\n"
  },
  {
    "path": "packages/knip/fixtures/ns-spread-reexport/index.ts",
    "content": "import { resolvers } from './resolvers.js';\nimport { useAliasedResolvers } from './aliased-import.js';\nimport { useUtils } from './consumer.js';\nimport { useFruit } from './member-access.js';\nimport { useAnimal } from './destructured.js';\nimport { allColors, justRed } from './mixed-usage.js';\n\nexport function useResolvers() {\n  return [resolvers, useAliasedResolvers, useUtils, useFruit, useAnimal, allColors, justRed];\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ns-spread-reexport/member-access.ts",
    "content": "// NOT strictly NS: direct member access (NS.member)\nimport * as Fruits from './fruits.js';\n\nexport function useFruit() {\n  return Fruits.apple;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ns-spread-reexport/mixed-usage.ts",
    "content": "// NOT strictly NS: mixed usage (spread + member access)\nimport * as Colors from './colors.js';\n\nexport const allColors = { ...Colors };\nexport const justRed = Colors.red;\n"
  },
  {
    "path": "packages/knip/fixtures/ns-spread-reexport/ns-alias-reexport.ts",
    "content": "// Tests nsAliases traversal: namespace spread re-exported as alias\nimport * as Utils from './utils.js';\n\nexport const utilsAlias = { ...Utils };\n"
  },
  {
    "path": "packages/knip/fixtures/ns-spread-reexport/package.json",
    "content": "{\n  \"name\": \"@fixtures/ns-spread-reexport\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ns-spread-reexport/resolvers.barrel.ts",
    "content": "export * from './hello.resolver.js';\n"
  },
  {
    "path": "packages/knip/fixtures/ns-spread-reexport/resolvers.ts",
    "content": "import * as resolverBarrel from './resolvers.barrel.js';\n\nexport const resolvers = {\n  ...resolverBarrel,\n};\n"
  },
  {
    "path": "packages/knip/fixtures/ns-spread-reexport/utils.ts",
    "content": "export const helper = 'helper';\n"
  },
  {
    "path": "packages/knip/fixtures/package-entry-points/.gitignore",
    "content": "dist\n"
  },
  {
    "path": "packages/knip/fixtures/package-entry-points/feature/internal/no-entry.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/package-entry-points/feature/internal/system/unused.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/package-entry-points/feature/internal/system/used.ts",
    "content": "export const used = 1;\nexport const unused = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/package-entry-points/feature/my-feature.js",
    "content": "import './internal/no-entry.js';\nimport { used } from './internal/system/used.js';\n\nused;\n\nexport const unused = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/package-entry-points/lib/deep/er/file.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/package-entry-points/lib/deep/main.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/package-entry-points/lib/index.js",
    "content": "export const entryExport = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/package-entry-points/package.json",
    "content": "{\n  \"name\": \"@fixtures/package-entry-points\",\n  \"exports\": {\n    \".\": \"./lib/index.js\",\n    \"./app\": \"./dist/app.js\",\n    \"./lib\": \"./lib/index.js\",\n    \"./lib/*\": \"./lib/*.js\",\n    \"./lib/*.js\": \"./lib/*.js\",\n    \"./feature\": \"./feature/index.js\",\n    \"./feature/*\": \"./feature/*.js\",\n    \"./feature/*.js\": \"./feature/*.js\",\n    \"./feature/internal/*\": null,\n    \"./*\": \"./src/public/*/index.ts\",\n    \"./runtime/*\": \"./dist/runtime/*\",\n    \"./ignored\": \"./*\",\n    \"./not-found\": \"./not-found.tsx\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/package-entry-points/src/app.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/package-entry-points/src/public/lib/rary/index.ts",
    "content": "export const entryExport = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/package-entry-points/src/public/lib/rary/lost.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/package-entry-points/src/runtime/deep/deno.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/package-entry-points/src/runtime/node.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/package-entry-points/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/path-aliases/abc/main.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/path-aliases/index.ts",
    "content": "import index from '@lib';\nimport fn from '@lib/fn';\nimport customExt from '@lib/data.ext';\nimport js from 'xyz/main.js';\nimport anything from '~/my-module';\ncustomExt;\nindex;\nfn;\njs;\nanything;\n"
  },
  {
    "path": "packages/knip/fixtures/path-aliases/knip.json",
    "content": "{\n  \"paths\": {\n    \"~/*\": [\"./*\"],\n    \"@lib\": [\"./lib/index.ts\"],\n    \"@lib/*\": [\"./lib/*\"],\n    \"xyz/*\": [\"abc/*\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/path-aliases/lib/data.ext",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/path-aliases/lib/fn.ts",
    "content": "export default () => void 0;\n"
  },
  {
    "path": "packages/knip/fixtures/path-aliases/lib/index.ts",
    "content": "export default () => void 0;\n"
  },
  {
    "path": "packages/knip/fixtures/path-aliases/my-module.ts",
    "content": "export default 1;\n"
  },
  {
    "path": "packages/knip/fixtures/path-aliases/package.json",
    "content": "{\n  \"name\": \"@fixtures/path-aliases\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/path-aliases2/index.ts",
    "content": "import 'lodash';\nimport 'filenamify';\nimport 'jquery';\n\nimport 'ts-components/file';\nimport 'ts-shared/file';\n\nimport '#file';\nimport '#wild/file';\nimport 'webpack-components/file';\nimport 'webpack-shared/file';\nimport 'webpack-exact';\n\nimport '@/file';\nimport '@vite-component/file.js';\nimport '@vite-file';\n\nimport '~/file';\nimport '~/component/file.js';\nimport '~vitest-dir/file.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/path-aliases2/package.json",
    "content": "{\n  \"name\": \"@fixtures/path-aliases2\",\n  \"devDependencies\": {\n    \"filenamify\": \"*\",\n    \"jquery\": \"*\",\n    \"lodash-es\": \"*\",\n    \"vite\": \"*\",\n    \"webpack\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/path-aliases2/ts/components/file.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/path-aliases2/ts/shared/file.ts",
    "content": "import 'webpack';\n"
  },
  {
    "path": "packages/knip/fixtures/path-aliases2/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \"./\",\n    \"module\": \"es2022\",\n    \"paths\": {\n      \"ts-components/*\": [\"ts/components/*\"],\n      \"ts-shared/*\": [\"ts/shared/*\"]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/path-aliases2/vite/component/file.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/path-aliases2/vite/dir/file.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/path-aliases2/vite/file.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/path-aliases2/vite.config.ts",
    "content": "import { defineConfig } from 'vite';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nexport default defineConfig({\n  resolve: {\n    alias: {\n      '@': path.resolve(__dirname, './vite'),\n      '@vite-component': path.resolve(__dirname, './vite/component'),\n      '@vite-file': path.join(fileURLToPath(new URL('./vite/dir', import.meta.url)), 'file.ts'),\n    },\n  },\n  test: {\n    alias: {\n      '~': path.resolve(__dirname, './vitest'),\n      '~vitest-dir': fileURLToPath(new URL('./vitest/dir', import.meta.url)),\n    },\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/path-aliases2/vitest/component/file.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/path-aliases2/vitest/dir/file.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/path-aliases2/vitest/file.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/path-aliases2/webpack/components/file.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/path-aliases2/webpack/file.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/path-aliases2/webpack/match.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/path-aliases2/webpack/shared/file.ts",
    "content": "import 'webpack';\n"
  },
  {
    "path": "packages/knip/fixtures/path-aliases2/webpack/wild/file.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/path-aliases2/webpack.config.ts",
    "content": "import path from 'node:path';\n\nexport default {\n  entry: './entry.ts',\n  resolve: {\n    alias: {\n      '#*': path.resolve(__dirname, 'webpack/*'),\n      'webpack-components': path.resolve(__dirname, 'webpack', 'components'),\n      'webpack-shared': path.resolve(__dirname, 'webpack', 'shared'),\n      'webpack-exact$': path.resolve(__dirname, 'webpack', 'match.ts'),\n      handlebars: false,\n      filenamify: 'filenamify/browser',\n      lodash: 'lodash-es',\n      jquery: 'jquery/dist/jquery.slim.min.js',\n    },\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/pathless/package.json",
    "content": "{\n  \"name\": \"@fixtures/pathless\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/pathless/src/dir/module-a.ts",
    "content": "import same from 'same';\nsame;\n\nexport default 1;\n"
  },
  {
    "path": "packages/knip/fixtures/pathless/src/index.ts",
    "content": "import one from 'dir/module-a';\nimport same from 'same';\nsame;\n\nexport default one;\n"
  },
  {
    "path": "packages/knip/fixtures/pathless/src/same.ts",
    "content": "export default 0;\n"
  },
  {
    "path": "packages/knip/fixtures/pathless/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \"src\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/peer-dependencies/index.ts",
    "content": "import host from 'host';\n"
  },
  {
    "path": "packages/knip/fixtures/peer-dependencies/package.json",
    "content": "{\n  \"name\": \"@fixtures/peer-dependencies\",\n  \"dependencies\": {\n    \"host\": \"*\",\n    \"peer\": \"*\",\n    \"unused\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/peer-dependencies-circular/package.json",
    "content": "{\n  \"name\": \"@fixtures/peer-dependencies-circular\",\n  \"dependencies\": {\n    \"core\": \"*\",\n    \"peer-1\": \"*\",\n    \"peer-2\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/peer-dependencies-optional/index.ts",
    "content": "import type { Pool, PoolClient } from 'pg';\n"
  },
  {
    "path": "packages/knip/fixtures/peer-dependencies-optional/package.json",
    "content": "{\n  \"name\": \"@fixtures/peer-dependencies-optional\",\n  \"devDependencies\": {\n    \"@types/pg\": \"*\"\n  },\n  \"peerDependencies\": {\n    \"@types/pg\": \"*\",\n    \"pg\": \"*\"\n  },\n  \"peerDependenciesMeta\": {\n    \"@types/pg\": {\n      \"optional\": true\n    },\n    \"pg\": {\n      \"optional\": true\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/peer-dependencies-optional-host/index.ts",
    "content": "import 'vue-demi';\n"
  },
  {
    "path": "packages/knip/fixtures/peer-dependencies-optional-host/package.json",
    "content": "{\n  \"name\": \"@fixtures/peer-dependencies-optional-host\",\n  \"dependencies\": {\n    \"vue-demi\": \"*\"\n  },\n  \"devDependencies\": {\n    \"@vue/composition-api\": \"*\",\n    \"vue\": \"*\"\n  },\n  \"peerDependencies\": {\n    \"@vue/composition-api\": \"*\",\n    \"vue\": \"*\"\n  },\n  \"peerDependenciesMeta\": {\n    \"@vue/composition-api\": {\n      \"optional\": true\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/peer-dependencies-optional-ignored/index.ts",
    "content": "import type { Pool, PoolClient } from 'pg';\n"
  },
  {
    "path": "packages/knip/fixtures/peer-dependencies-optional-ignored/knip.json",
    "content": "{}\n"
  },
  {
    "path": "packages/knip/fixtures/peer-dependencies-optional-ignored/package.json",
    "content": "{\n  \"name\": \"@fixtures/peer-dependencies-optional-ignored\",\n  \"peerDependencies\": {\n    \"@types/pg\": \"*\",\n    \"pg\": \"*\"\n  },\n  \"peerDependenciesMeta\": {\n    \"@types/pg\": {\n      \"optional\": true\n    },\n    \"pg\": {\n      \"optional\": true\n    }\n  },\n  \"devDependencies\": {\n    \"@types/pg\": \"*\",\n    \"pg\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/peer-dependencies-optional-strict/index.ts",
    "content": "import type { Pool, PoolClient } from 'pg';\n"
  },
  {
    "path": "packages/knip/fixtures/peer-dependencies-optional-strict/package.json",
    "content": "{\n  \"name\": \"@fixtures/peer-dependencies-optional-strict\",\n  \"peerDependencies\": {\n    \"@types/pg\": \"*\",\n    \"pg\": \"*\",\n    \"required-peer\": \"*\"\n  },\n  \"peerDependenciesMeta\": {\n    \"@types/pg\": {\n      \"optional\": true\n    },\n    \"pg\": {\n      \"optional\": true\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/peer-dependencies-optional-strict-unreferenced/index.ts",
    "content": "import 'used-dep';\n"
  },
  {
    "path": "packages/knip/fixtures/peer-dependencies-optional-strict-unreferenced/package.json",
    "content": "{\n  \"name\": \"@fixtures/peer-dependencies-optional-strict-unreferenced\",\n  \"dependencies\": {\n    \"used-dep\": \"*\"\n  },\n  \"peerDependencies\": {\n    \"optional-peer\": \"*\"\n  },\n  \"peerDependenciesMeta\": {\n    \"optional-peer\": {\n      \"optional\": true\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugin-config/index.ts",
    "content": "export default 1;\n"
  },
  {
    "path": "packages/knip/fixtures/plugin-config/index.vitest.ts",
    "content": "export default 1;\n"
  },
  {
    "path": "packages/knip/fixtures/plugin-config/knip.json",
    "content": "{\n  \"vitest\": {\n    \"config\": [\"vitest.config.ts\"],\n    \"entry\": [\"**/*.vitest.ts\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugin-config/package.json",
    "content": "{\n  \"name\": \"@fixtures/plugin-config\",\n  \"scripts\": {\n    \"test\": \"vitest\"\n  },\n  \"devDependencies\": {\n    \"vitest\": \"*\",\n    \"happy-dom\": \"*\"\n  },\n  \"workspaces\": [\n    \"packages/*\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugin-config/vitest.config.ts",
    "content": "import { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n  test: {\n    include: ['**/*.vitest.ts'],\n    environment: 'happy-dom',\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugin-disable/knip.json",
    "content": "{\n  \"workspaces\": {\n    \"ws\": {}\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugin-disable/package.json",
    "content": "{\n  \"name\": \"@fixtures/plugin-disable\",\n  \"workspaces\": [\n    \"ws\"\n  ],\n  \"scripts\": {\n    \"test\": \"vitest -c vite.config.ts\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugin-disable/vite.config.ts",
    "content": "const getValues = () => {\n  throw new Error(\"This plugin should've been ignored\");\n};\n\nexport default {\n  someValue: getValues(),\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugin-disable/ws/package.json",
    "content": "{\n  \"name\": \"@fixtures/plugin-disable__ws\",\n  \"scripts\": {\n    \"playwright-test\": \"playwright test --config playwright.config.ts\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugin-disable/ws/playwright.config.ts",
    "content": "const getValues = () => {\n  throw new Error(\"This plugin should've been ignored\");\n};\n\nexport default {\n  someValue: getValues(),\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugin-negated-entry-globs/knip.json",
    "content": "{\n  \"$schema\": \"https://unpkg.com/knip@6/schema.json\",\n  \"storybook\": {\n    \"entry\": [\"**/*.stories.ts\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugin-negated-entry-globs/package.json",
    "content": "{\n  \"name\": \"@fixtures/plugin-entry-globs\",\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"astro\": \"*\"\n  },\n  \"devDependencies\": {\n    \"@storybook/chapter\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugin-negated-entry-globs/src/pages/_stories/index.stories.ts",
    "content": "import '@storybook/chapter';\nexport default [];\n"
  },
  {
    "path": "packages/knip/fixtures/plugin-negated-entry-globs/src/pages/_util.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugin-negated-entry-globs/src/pages/about/index.astro",
    "content": "\nLorem ipsum dolor sit amet, consectetur adipiscing elit.\n"
  },
  {
    "path": "packages/knip/fixtures/plugin-negated-entry-globs/src/pages/blog/_util/index.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugin-negated-entry-globs/src/pages/blog/_util.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugin-negated-entry-globs/src/pages/blog/index.astro",
    "content": "---\nimport 'astro';\n---\n\n<html></html>\n"
  },
  {
    "path": "packages/knip/fixtures/plugin-negated-entry-globs/src/pages/index.astro",
    "content": "<html></html>\n"
  },
  {
    "path": "packages/knip/fixtures/plugin-overlap/package.json",
    "content": "{\n  \"name\": \"@fixtures/plugin-overlap\",\n  \"private\": true,\n  \"scripts\": {\n    \"docs\": \"typedoc\"\n  },\n  \"devDependencies\": {\n    \"@tsconfig/node16\": \"*\",\n    \"@types/node\": \"*\",\n    \"typedoc\": \"*\",\n    \"typedoc-plugin-markdown\": \"*\",\n    \"typescript\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugin-overlap/tsconfig.json",
    "content": "{\n  \"extends\": \"@tsconfig/node16\",\n  \"compilerOptions\": {}\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugin-overlap/typedoc.json",
    "content": "{\n  \"$schema\": \"https://typedoc.org/schema.json\",\n  \"plugin\": [\"typedoc-plugin-markdown\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/_template/package.json",
    "content": "{\n  \"name\": \"@plugins/_template\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/angular/angular.json",
    "content": "{\n  \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n  \"version\": 1,\n  \"newProjectRoot\": \"projects\",\n  \"projects\": {\n    \"knip-angular-example\": {\n      \"projectType\": \"application\",\n      \"schematics\": {\n        \"@schematics/angular:component\": {\n          \"style\": \"scss\"\n        }\n      },\n      \"root\": \"\",\n      \"sourceRoot\": \"src\",\n      \"prefix\": \"app\",\n      \"architect\": {\n        \"build\": {\n          \"builder\": \"@angular-devkit/build-angular:browser\",\n          \"options\": {\n            \"outputPath\": \"dist/knip-angular-example\",\n            \"index\": \"src/index.html\",\n            \"main\": \"src/main.ts\",\n            \"polyfills\": \"src/polyfill.js\",\n            \"tsConfig\": \"tsconfig.app.json\",\n            \"inlineStyleLanguage\": \"scss\",\n            \"assets\": [\"src/favicon.ico\", \"src/assets\"],\n            \"styles\": [\"src/styles.scss\"],\n            \"scripts\": [\"src/script.js\"]\n          },\n          \"configurations\": {\n            \"production\": {\n              \"budgets\": [\n                {\n                  \"type\": \"initial\",\n                  \"maximumWarning\": \"500kb\",\n                  \"maximumError\": \"1mb\"\n                },\n                {\n                  \"type\": \"anyComponentStyle\",\n                  \"maximumWarning\": \"2kb\",\n                  \"maximumError\": \"4kb\"\n                }\n              ],\n              \"outputHashing\": \"all\"\n            },\n            \"development\": {\n              \"buildOptimizer\": false,\n              \"optimization\": false,\n              \"vendorChunk\": true,\n              \"extractLicenses\": false,\n              \"sourceMap\": true,\n              \"namedChunks\": true\n            }\n          },\n          \"defaultConfiguration\": \"production\"\n        },\n        \"serve\": {\n          \"builder\": \"@angular-devkit/build-angular:dev-server\",\n          \"configurations\": {\n            \"production\": {\n              \"browserTarget\": \"knip-angular-example:build:production\"\n            },\n            \"development\": {\n              \"browserTarget\": \"knip-angular-example:build:development\"\n            }\n          },\n          \"defaultConfiguration\": \"development\"\n        },\n        \"extract-i18n\": {\n          \"builder\": \"@angular-devkit/build-angular:extract-i18n\",\n          \"options\": {\n            \"browserTarget\": \"knip-angular-example:build\"\n          }\n        },\n        \"test\": {\n          \"builder\": \"@angular-devkit/build-angular:karma\",\n          \"options\": {\n            \"polyfills\": [\"zone.js\", \"zone.js/testing\"],\n            \"tsConfig\": \"tsconfig.spec.json\",\n            \"inlineStyleLanguage\": \"scss\",\n            \"assets\": [\"src/favicon.ico\", \"src/assets\"],\n            \"styles\": [\"src/styles.scss\"],\n            \"scripts\": []\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/angular/package.json",
    "content": "{\n  \"name\": \"@plugins/angular\",\n  \"scripts\": {\n    \"ng\": \"ng\",\n    \"start\": \"ng serve\",\n    \"build\": \"ng build\",\n    \"watch\": \"ng build --watch --configuration development\",\n    \"test\": \"ng test\"\n  },\n  \"dependencies\": {\n    \"zone.js\": \"*\"\n  },\n  \"devDependencies\": {\n    \"@angular/cli\": \"*\",\n    \"karma\": \"*\",\n    \"karma-chrome-launcher\": \"*\",\n    \"karma-coverage\": \"*\",\n    \"karma-jasmine\": \"*\",\n    \"karma-jasmine-html-reporter\": \"*\",\n    \"jasmine-core\": \"*\",\n    \"typescript\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/angular/src/app/app.component.spec.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/angular/src/main.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/angular/src/polyfill.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/angular/src/script.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/angular/tsconfig.app.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"types\": []\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/angular/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \"./\",\n    \"lib\": [\"ES2022\", \"dom\"]\n  },\n  \"angularCompilerOptions\": {\n    \"enableI18nLegacyMessageIdFormat\": false,\n    \"strictInjectionParameters\": true,\n    \"strictInputAccessModifiers\": true,\n    \"strictTemplates\": true\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/angular/tsconfig.spec.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"types\": [\"jasmine\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/angular2/angular.json",
    "content": "{\n  \"version\": 1,\n  \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n  \"projects\": {\n    \"app\": {\n      \"root\": \"./\",\n      \"sourceRoot\": \"src\",\n      \"projectType\": \"application\",\n      \"prefix\": \"app\",\n      \"architect\": {\n        \"build-esbuild\": {\n          \"builder\": \"@angular-builders/custom-esbuild:application\",\n          \"options\": {\n            \"browser\": \"./src/browser.ts\",\n            \"index\": \"./src/index.html\",\n            \"outputPath\": \"./www\",\n            \"tsConfig\": \"./tsconfig.app.json\",\n            \"inlineStyleLanguage\": \"scss\",\n            \"crossOrigin\": \"anonymous\"\n          }\n        },\n        \"build\": {\n          \"builder\": \"@angular/build:application\",\n          \"options\": {\n            \"outputPath\": \"dist/blorp\",\n            \"index\": \"src/index.html\",\n            \"browser\": \"src/main.ts\",\n            \"polyfills\": [\"zone.js\"],\n            \"tsConfig\": \"tsconfig.app.json\",\n            \"assets\": [\n              {\n                \"glob\": \"**/*\",\n                \"input\": \"public\"\n              }\n            ],\n            \"styles\": [\"src/styles.css\"],\n            \"scripts\": [],\n            \"server\": \"src/main.server.ts\",\n            \"prerender\": true,\n            \"ssr\": {\n              \"entry\": \"src/server.ts\"\n            }\n          },\n          \"configurations\": {\n            \"production\": {\n              \"budgets\": [\n                {\n                  \"type\": \"initial\",\n                  \"maximumWarning\": \"500kB\",\n                  \"maximumError\": \"1MB\"\n                },\n                {\n                  \"type\": \"anyComponentStyle\",\n                  \"maximumWarning\": \"2kB\",\n                  \"maximumError\": \"4kB\"\n                }\n              ],\n              \"outputHashing\": \"all\"\n            },\n            \"development\": {\n              \"optimization\": false,\n              \"extractLicenses\": false,\n              \"sourceMap\": true,\n              \"fileReplacements\": [\n                {\n                  \"replace\": \"src/environments/environment.ts\",\n                  \"with\": \"src/environments/environment.development.ts\"\n                }\n              ]\n            }\n          },\n          \"defaultConfiguration\": \"production\"\n        },\n        \"serve\": {\n          \"builder\": \"@angular/build:dev-server\",\n          \"configurations\": {\n            \"production\": {\n              \"buildTarget\": \"blorp:build:production\"\n            },\n            \"development\": {\n              \"buildTarget\": \"blorp:build:development\"\n            }\n          },\n          \"defaultConfiguration\": \"development\"\n        },\n        \"extract-i18n\": {\n          \"builder\": \"@angular/build:extract-i18n\"\n        },\n        \"test\": {\n          \"builder\": \"@angular/build:karma\",\n          \"options\": {\n            \"polyfills\": [\"zone.js\", \"zone.js/testing\"],\n            \"tsConfig\": \"tsconfig.spec.json\",\n            \"assets\": [\n              {\n                \"glob\": \"**/*\",\n                \"input\": \"public\"\n              }\n            ],\n            \"styles\": [\"src/styles.css\"],\n            \"scripts\": [],\n            \"karmaConfig\": \"another-karma.conf.js\"\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/angular2/another-karma.conf.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/angular2/karma.conf.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/angular2/package.json",
    "content": "{\n  \"name\": \"@plugins/angular2\",\n  \"scripts\": {\n    \"ng\": \"ng\"\n  },\n  \"dependencies\": {\n    \"zone.js\": \"*\"\n  },\n  \"devDependencies\": {\n    \"@angular/cli\": \"*\",\n    \"@angular/ssr\": \"*\",\n    \"@angular-builders/custom-esbuild\": \"*\",\n    \"@angular/build\": \"*\",\n    \"karma\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/angular2/src/browser.ts",
    "content": "import { environment } from './environments/environment';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/angular2/src/environments/environment.development.ts",
    "content": "export const environment = {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/angular2/src/environments/environment.ts",
    "content": "export const environment = {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/angular2/src/main.server.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/angular2/src/server.ts",
    "content": "import { CommonEngine } from '@angular/ssr';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/angular2/tsconfig.app.json",
    "content": "{}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/angular3/angular.json",
    "content": "{\n  \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n  \"version\": 1,\n  \"newProjectRoot\": \"projects\",\n  \"projects\": {\n    \"knip-angular-example\": {\n      \"projectType\": \"application\",\n      \"root\": \"\",\n      \"sourceRoot\": \"src\",\n      \"prefix\": \"app\",\n      \"architect\": {\n        \"build\": {\n          \"builder\": \"@angular-devkit/build-angular:application\",\n          \"options\": {\n            \"browser\": \"src/main.ts\",\n            \"ssr\": {\n              \"entry\": \"src/server.ts\"\n            },\n            \"server\": \"src/main.server-for-non-prod.ts\",\n            \"polyfills\": [\"src/polyfill.js\"]\n          },\n          \"configurations\": {\n            \"production\": {\n              \"server\": \"src/main.server.ts\",\n              \"scripts\": [\"src/script.js\"]\n            },\n            \"development\": {\n              \"scripts\": [\"src/script-for-non-prod.js\"],\n              \"polyfills\": [\"src/polyfill-for-non-prod.js\"]\n            }\n          }\n        },\n        \"a-non-prod-target\": {\n          \"builder\": \"@angular-devkit/build-angular:browser\",\n          \"options\": {\n            \"main\": \"src/main-for-non-prod.ts\"\n          }\n        },\n        \"test\": {\n          \"builder\": \"@angular-devkit/build-angular:karma\",\n          \"options\": {\n            \"main\": \"src/main-for-testing.ts\"\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/angular3/package.json",
    "content": "{\n  \"name\": \"@plugins/angular3\",\n  \"devDependencies\": {\n    \"@angular-devkit/build-angular\": \"*\",\n    \"@angular/cli\": \"*\",\n    \"jasmine-core\": \"*\",\n    \"karma-chrome-launcher\": \"*\",\n    \"karma-coverage\": \"*\",\n    \"karma-jasmine\": \"*\",\n    \"karma-jasmine-html-reporter\": \"*\",\n    \"typescript\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/angular3/src/main-for-non-prod.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/angular3/src/main-for-testing.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/angular3/src/main.server-for-non-prod.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/angular3/src/main.server.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/angular3/src/main.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/angular3/src/polyfill-for-non-prod.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/angular3/src/polyfill.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/angular3/src/script-for-non-prod.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/angular3/src/script.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/angular3/src/server.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/.astro/types.d.ts",
    "content": "declare module 'astro:content' {\n  interface Render {\n    '.mdx': Promise<{\n      Content: import('astro').MarkdownInstance['Content'];\n      headings: import('astro').MarkdownHeading[];\n      remarkPluginFrontmatter: Record<string, any>;\n    }>;\n  }\n}\n\ndeclare module 'astro:content' {\n  interface Render {\n    '.md': Promise<{\n      Content: import('astro').MarkdownInstance['Content'];\n      headings: import('astro').MarkdownHeading[];\n      remarkPluginFrontmatter: Record<string, any>;\n    }>;\n  }\n}\n\ndeclare module 'astro:content' {\n  export { z } from 'astro/zod';\n\n  type Flatten<T> = T extends { [K: string]: infer U } ? U : never;\n\n  export type CollectionKey = keyof AnyEntryMap;\n  export type CollectionEntry<C extends CollectionKey> = Flatten<AnyEntryMap[C]>;\n\n  export type ContentCollectionKey = keyof ContentEntryMap;\n  export type DataCollectionKey = keyof DataEntryMap;\n\n  // This needs to be in sync with ImageMetadata\n  export type ImageFunction = () => import('astro/zod').ZodObject<{\n    src: import('astro/zod').ZodString;\n    width: import('astro/zod').ZodNumber;\n    height: import('astro/zod').ZodNumber;\n    format: import('astro/zod').ZodUnion<\n      [\n        import('astro/zod').ZodLiteral<'png'>,\n        import('astro/zod').ZodLiteral<'jpg'>,\n        import('astro/zod').ZodLiteral<'jpeg'>,\n        import('astro/zod').ZodLiteral<'tiff'>,\n        import('astro/zod').ZodLiteral<'webp'>,\n        import('astro/zod').ZodLiteral<'gif'>,\n        import('astro/zod').ZodLiteral<'svg'>,\n        import('astro/zod').ZodLiteral<'avif'>,\n      ]\n    >;\n  }>;\n\n  type BaseSchemaWithoutEffects =\n    | import('astro/zod').AnyZodObject\n    | import('astro/zod').ZodUnion<[BaseSchemaWithoutEffects, ...BaseSchemaWithoutEffects[]]>\n    | import('astro/zod').ZodDiscriminatedUnion<string, import('astro/zod').AnyZodObject[]>\n    | import('astro/zod').ZodIntersection<BaseSchemaWithoutEffects, BaseSchemaWithoutEffects>;\n\n  type BaseSchema = BaseSchemaWithoutEffects | import('astro/zod').ZodEffects<BaseSchemaWithoutEffects>;\n\n  export type SchemaContext = { image: ImageFunction };\n\n  type DataCollectionConfig<S extends BaseSchema> = {\n    type: 'data';\n    schema?: S | ((context: SchemaContext) => S);\n  };\n\n  type ContentCollectionConfig<S extends BaseSchema> = {\n    type?: 'content';\n    schema?: S | ((context: SchemaContext) => S);\n  };\n\n  type CollectionConfig<S> = ContentCollectionConfig<S> | DataCollectionConfig<S>;\n\n  export function defineCollection<S extends BaseSchema>(input: CollectionConfig<S>): CollectionConfig<S>;\n\n  type AllValuesOf<T> = T extends any ? T[keyof T] : never;\n  type ValidContentEntrySlug<C extends keyof ContentEntryMap> = AllValuesOf<ContentEntryMap[C]>['slug'];\n\n  export function getEntryBySlug<C extends keyof ContentEntryMap, E extends ValidContentEntrySlug<C> | (string & {})>(\n    collection: C,\n    // Note that this has to accept a regular string too, for SSR\n    entrySlug: E\n  ): E extends ValidContentEntrySlug<C> ? Promise<CollectionEntry<C>> : Promise<CollectionEntry<C> | undefined>;\n\n  export function getDataEntryById<C extends keyof DataEntryMap, E extends keyof DataEntryMap[C]>(\n    collection: C,\n    entryId: E\n  ): Promise<CollectionEntry<C>>;\n\n  export function getCollection<C extends keyof AnyEntryMap, E extends CollectionEntry<C>>(\n    collection: C,\n    filter?: (entry: CollectionEntry<C>) => entry is E\n  ): Promise<E[]>;\n  export function getCollection<C extends keyof AnyEntryMap>(\n    collection: C,\n    filter?: (entry: CollectionEntry<C>) => unknown\n  ): Promise<CollectionEntry<C>[]>;\n\n  export function getEntry<C extends keyof ContentEntryMap, E extends ValidContentEntrySlug<C> | (string & {})>(entry: {\n    collection: C;\n    slug: E;\n  }): E extends ValidContentEntrySlug<C> ? Promise<CollectionEntry<C>> : Promise<CollectionEntry<C> | undefined>;\n  export function getEntry<C extends keyof DataEntryMap, E extends keyof DataEntryMap[C] | (string & {})>(entry: {\n    collection: C;\n    id: E;\n  }): E extends keyof DataEntryMap[C] ? Promise<DataEntryMap[C][E]> : Promise<CollectionEntry<C> | undefined>;\n  export function getEntry<C extends keyof ContentEntryMap, E extends ValidContentEntrySlug<C> | (string & {})>(\n    collection: C,\n    slug: E\n  ): E extends ValidContentEntrySlug<C> ? Promise<CollectionEntry<C>> : Promise<CollectionEntry<C> | undefined>;\n  export function getEntry<C extends keyof DataEntryMap, E extends keyof DataEntryMap[C] | (string & {})>(\n    collection: C,\n    id: E\n  ): E extends keyof DataEntryMap[C] ? Promise<DataEntryMap[C][E]> : Promise<CollectionEntry<C> | undefined>;\n\n  /** Resolve an array of entry references from the same collection */\n  export function getEntries<C extends keyof ContentEntryMap>(\n    entries: {\n      collection: C;\n      slug: ValidContentEntrySlug<C>;\n    }[]\n  ): Promise<CollectionEntry<C>[]>;\n  export function getEntries<C extends keyof DataEntryMap>(\n    entries: {\n      collection: C;\n      id: keyof DataEntryMap[C];\n    }[]\n  ): Promise<CollectionEntry<C>[]>;\n\n  export function reference<C extends keyof AnyEntryMap>(\n    collection: C\n  ): import('astro/zod').ZodEffects<\n    import('astro/zod').ZodString,\n    C extends keyof ContentEntryMap\n      ? {\n          collection: C;\n          slug: ValidContentEntrySlug<C>;\n        }\n      : {\n          collection: C;\n          id: keyof DataEntryMap[C];\n        }\n  >;\n  // Allow generic `string` to avoid excessive type errors in the config\n  // if `dev` is not running to update as you edit.\n  // Invalid collection names will be caught at build time.\n  export function reference<C extends string>(\n    collection: C\n  ): import('astro/zod').ZodEffects<import('astro/zod').ZodString, never>;\n\n  type ReturnTypeOrOriginal<T> = T extends (...args: any[]) => infer R ? R : T;\n  type InferEntrySchema<C extends keyof AnyEntryMap> = import('astro/zod').infer<\n    ReturnTypeOrOriginal<Required<ContentConfig['collections'][C]>['schema']>\n  >;\n\n  type ContentEntryMap = {\n    blog: {\n      'first-post.md': {\n        id: 'first-post.md';\n        slug: 'first-post';\n        body: string;\n        collection: 'blog';\n        data: InferEntrySchema<'blog'>;\n      } & { render(): Render['.md'] };\n      'using-mdx.mdx': {\n        id: 'using-mdx.mdx';\n        slug: 'using-mdx';\n        body: string;\n        collection: 'blog';\n        data: InferEntrySchema<'blog'>;\n      } & { render(): Render['.mdx'] };\n    };\n  };\n\n  type AnyEntryMap = ContentEntryMap & DataEntryMap;\n\n  type ContentConfig = typeof import('../src/content/config');\n}\n\ndeclare module 'astro:env/client' {\n  export const AWESOME_URL: string;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/astro.config.mjs",
    "content": "import { defineConfig } from 'astro/config';\nimport mdx from '@astrojs/mdx';\n\nimport sitemap from '@astrojs/sitemap';\n\n// https://astro.build/config\nexport default defineConfig({\n  site: 'https://example.com',\n  integrations: [mdx(), sitemap()],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/knip.ts",
    "content": "export default {\n  ignore: '.astro/types.d.ts',\n  compilers: {\n    css: (text: string) => [...text.matchAll(/(?<=@)import[^;]+/g)].join('\\n'),\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/package.json",
    "content": "{\n  \"name\": \"@plugins/astro\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"astro dev\",\n    \"start\": \"astro dev\",\n    \"build\": \"astro build\",\n    \"preview\": \"astro preview\",\n    \"astro\": \"astro\",\n    \"check\": \"astro check\"\n  },\n  \"dependencies\": {\n    \"@astrojs/mdx\": \"*\",\n    \"@astrojs/rss\": \"*\",\n    \"@astrojs/sitemap\": \"*\",\n    \"astro\": \"*\"\n  },\n  \"devDependencies\": {\n    \"@astrojs/check\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/components/BaseHead.astro",
    "content": "---\n// Import the global.css file here so that it is included on\n// all pages through the use of the <BaseHead /> component.\nimport '../styles/global.css';\n\ninterface Props {\n  title: string;\n  description: string;\n  image?: string;\n}\n\nconst canonicalURL = new URL(Astro.url.pathname, Astro.site);\n\nconst { title, description, image = '/favicon.svg' } = Astro.props;\n---\n\n<!-- Global Metadata -->\n<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n<link rel=\"icon\" type=\"image/svg+xml\" href=\"/favicon.svg\" />\n<meta name=\"generator\" content={Astro.generator} />\n\n<!-- Canonical URL -->\n<link rel=\"canonical\" href={canonicalURL} />\n\n<!-- Primary Meta Tags -->\n<title>{title}</title>\n<meta name=\"title\" content={title} />\n<meta name=\"description\" content={description} />\n\n<!-- Open Graph / Facebook -->\n<meta property=\"og:type\" content=\"website\" />\n<meta property=\"og:url\" content={Astro.url} />\n<meta property=\"og:title\" content={title} />\n<meta property=\"og:description\" content={description} />\n<meta property=\"og:image\" content={new URL(image, Astro.url)} />\n\n<!-- Twitter -->\n<meta property=\"twitter:card\" content=\"summary_large_image\" />\n<meta property=\"twitter:url\" content={Astro.url} />\n<meta property=\"twitter:title\" content={title} />\n<meta property=\"twitter:description\" content={description} />\n<meta property=\"twitter:image\" content={new URL(image, Astro.url)} />\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/components/Footer.astro",
    "content": "---\nconst today = new Date();\n---\n\n<footer>\n\t&copy; {today.getFullYear()} Your name here. All rights reserved.\n\t<div class=\"social-links\">\n\t\t<a href=\"https://m.webtoo.ls/@astro\" target=\"_blank\">\n\t\t\t<span class=\"sr-only\">Follow Astro on Mastodon</span>\n\t\t\t<svg\n\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\taria-hidden=\"true\"\n\t\t\t\twidth=\"32\"\n\t\t\t\theight=\"32\"\n\t\t\t\tastro-icon=\"social/mastodon\"\n\t\t\t\t><path\n\t\t\t\t\tfill=\"currentColor\"\n\t\t\t\t\td=\"M11.19 12.195c2.016-.24 3.77-1.475 3.99-2.603.348-1.778.32-4.339.32-4.339 0-3.47-2.286-4.488-2.286-4.488C12.062.238 10.083.017 8.027 0h-.05C5.92.017 3.942.238 2.79.765c0 0-2.285 1.017-2.285 4.488l-.002.662c-.004.64-.007 1.35.011 2.091.083 3.394.626 6.74 3.78 7.57 1.454.383 2.703.463 3.709.408 1.823-.1 2.847-.647 2.847-.647l-.06-1.317s-1.303.41-2.767.36c-1.45-.05-2.98-.156-3.215-1.928a3.614 3.614 0 0 1-.033-.496s1.424.346 3.228.428c1.103.05 2.137-.064 3.188-.189zm1.613-2.47H11.13v-4.08c0-.859-.364-1.295-1.091-1.295-.804 0-1.207.517-1.207 1.541v2.233H7.168V5.89c0-1.024-.403-1.541-1.207-1.541-.727 0-1.091.436-1.091 1.296v4.079H3.197V5.522c0-.859.22-1.541.66-2.046.456-.505 1.052-.764 1.793-.764.856 0 1.504.328 1.933.983L8 4.39l.417-.695c.429-.655 1.077-.983 1.934-.983.74 0 1.336.259 1.791.764.442.505.661 1.187.661 2.046v4.203z\"\n\t\t\t\t></path></svg\n\t\t\t>\n\t\t</a>\n\t\t<a href=\"https://twitter.com/astrodotbuild\" target=\"_blank\">\n\t\t\t<span class=\"sr-only\">Follow Astro on Twitter</span>\n\t\t\t<svg viewBox=\"0 0 16 16\" aria-hidden=\"true\" width=\"32\" height=\"32\" astro-icon=\"social/twitter\"\n\t\t\t\t><path\n\t\t\t\t\tfill=\"currentColor\"\n\t\t\t\t\td=\"M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z\"\n\t\t\t\t></path></svg\n\t\t\t>\n\t\t</a>\n\t\t<a href=\"https://github.com/withastro/astro\" target=\"_blank\">\n\t\t\t<span class=\"sr-only\">Go to Astro's GitHub repo</span>\n\t\t\t<svg viewBox=\"0 0 16 16\" aria-hidden=\"true\" width=\"32\" height=\"32\" astro-icon=\"social/github\"\n\t\t\t\t><path\n\t\t\t\t\tfill=\"currentColor\"\n\t\t\t\t\td=\"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z\"\n\t\t\t\t></path></svg\n\t\t\t>\n\t\t</a>\n\t</div>\n</footer>\n<style>\n\tfooter {\n\t\tpadding: 2em 1em 6em 1em;\n\t\tbackground: linear-gradient(var(--gray-gradient)) no-repeat;\n\t\tcolor: rgb(var(--gray));\n\t\ttext-align: center;\n\t}\n\t.social-links {\n\t\tdisplay: flex;\n\t\tjustify-content: center;\n\t\tgap: 1em;\n\t\tmargin-top: 1em;\n\t}\n\t.social-links a {\n\t\ttext-decoration: none;\n\t\tcolor: rgb(var(--gray));\n\t}\n\t.social-links a:hover {\n\t\tcolor: rgb(var(--gray-dark));\n\t}\n</style>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/components/FormattedDate.astro",
    "content": "---\ninterface Props {\n  date: Date;\n}\n\nconst { date } = Astro.props;\n---\n\n<time datetime={date.toISOString()}>\n\t{\n\t\tdate.toLocaleDateString('en-us', {\n\t\t\tyear: 'numeric',\n\t\t\tmonth: 'short',\n\t\t\tday: 'numeric',\n\t\t})\n\t}\n</time>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/components/Header.astro",
    "content": "---\nimport HeaderLink from './HeaderLink.astro';\nimport { SITE_TITLE } from '../consts';\n---\n\n<header>\n\t<nav>\n\t\t<h2><a href=\"/\">{SITE_TITLE}</a></h2>\n\t\t<div class=\"internal-links\">\n\t\t\t<HeaderLink href=\"/\">Home</HeaderLink>\n\t\t\t<HeaderLink href=\"/blog\">Blog</HeaderLink>\n\t\t\t<HeaderLink href=\"/about\">About</HeaderLink>\n\t\t</div>\n\t\t<div class=\"social-links\">\n\t\t\t<a href=\"https://m.webtoo.ls/@astro\" target=\"_blank\">\n\t\t\t\t<span class=\"sr-only\">Follow Astro on Mastodon</span>\n\t\t\t\t<svg viewBox=\"0 0 16 16\" aria-hidden=\"true\" width=\"32\" height=\"32\"\n\t\t\t\t\t><path\n\t\t\t\t\t\tfill=\"currentColor\"\n\t\t\t\t\t\td=\"M11.19 12.195c2.016-.24 3.77-1.475 3.99-2.603.348-1.778.32-4.339.32-4.339 0-3.47-2.286-4.488-2.286-4.488C12.062.238 10.083.017 8.027 0h-.05C5.92.017 3.942.238 2.79.765c0 0-2.285 1.017-2.285 4.488l-.002.662c-.004.64-.007 1.35.011 2.091.083 3.394.626 6.74 3.78 7.57 1.454.383 2.703.463 3.709.408 1.823-.1 2.847-.647 2.847-.647l-.06-1.317s-1.303.41-2.767.36c-1.45-.05-2.98-.156-3.215-1.928a3.614 3.614 0 0 1-.033-.496s1.424.346 3.228.428c1.103.05 2.137-.064 3.188-.189zm1.613-2.47H11.13v-4.08c0-.859-.364-1.295-1.091-1.295-.804 0-1.207.517-1.207 1.541v2.233H7.168V5.89c0-1.024-.403-1.541-1.207-1.541-.727 0-1.091.436-1.091 1.296v4.079H3.197V5.522c0-.859.22-1.541.66-2.046.456-.505 1.052-.764 1.793-.764.856 0 1.504.328 1.933.983L8 4.39l.417-.695c.429-.655 1.077-.983 1.934-.983.74 0 1.336.259 1.791.764.442.505.661 1.187.661 2.046v4.203z\"\n\t\t\t\t\t></path></svg\n\t\t\t\t>\n\t\t\t</a>\n\t\t\t<a href=\"https://twitter.com/astrodotbuild\" target=\"_blank\">\n\t\t\t\t<span class=\"sr-only\">Follow Astro on Twitter</span>\n\t\t\t\t<svg viewBox=\"0 0 16 16\" aria-hidden=\"true\" width=\"32\" height=\"32\"\n\t\t\t\t\t><path\n\t\t\t\t\t\tfill=\"currentColor\"\n\t\t\t\t\t\td=\"M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z\"\n\t\t\t\t\t></path></svg\n\t\t\t\t>\n\t\t\t</a>\n\t\t\t<a href=\"https://github.com/withastro/astro\" target=\"_blank\">\n\t\t\t\t<span class=\"sr-only\">Go to Astro's GitHub repo</span>\n\t\t\t\t<svg viewBox=\"0 0 16 16\" aria-hidden=\"true\" width=\"32\" height=\"32\"\n\t\t\t\t\t><path\n\t\t\t\t\t\tfill=\"currentColor\"\n\t\t\t\t\t\td=\"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z\"\n\t\t\t\t\t></path></svg\n\t\t\t\t>\n\t\t\t</a>\n\t\t</div>\n\t</nav>\n</header>\n<style>\n\theader {\n\t\tmargin: 0;\n\t\tpadding: 0 1em;\n\t\tbackground: white;\n\t\tbox-shadow: 0 2px 8px rgba(var(--black), 5%);\n\t}\n\th2 {\n\t\tmargin: 0;\n\t\tfont-size: 1em;\n\t}\n\n\th2 a,\n\th2 a.active {\n\t\ttext-decoration: none;\n\t}\n\tnav {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: space-between;\n\t}\n\tnav a {\n\t\tpadding: 1em 0.5em;\n\t\tcolor: var(--black);\n\t\tborder-bottom: 4px solid transparent;\n\t\ttext-decoration: none;\n\t}\n\tnav a.active {\n\t\ttext-decoration: none;\n\t\tborder-bottom-color: var(--accent);\n\t}\n\t.social-links,\n\t.social-links a {\n\t\tdisplay: flex;\n\t}\n\t@media (max-width: 720px) {\n\t\t.social-links {\n\t\t\tdisplay: none;\n\t\t}\n\t}\n</style>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/components/HeaderLink.astro",
    "content": "---\nimport type { HTMLAttributes } from 'astro/types';\n\ntype Props = HTMLAttributes<'a'>;\n\nconst { href, class: className, ...props } = Astro.props;\n\nconst { pathname } = Astro.url;\nconst isActive = href === pathname || href === pathname.replace(/\\/$/, '');\n---\n\n<a href={href} class:list={[className, { active: isActive }]} {...props}>\n\t<slot />\n</a>\n<style>\n\ta {\n\t\tdisplay: inline-block;\n\t\ttext-decoration: none;\n\t}\n\ta.active {\n\t\tfont-weight: bolder;\n\t\ttext-decoration: underline;\n\t}\n</style>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/consts.ts",
    "content": "// Place any global data in this file.\n// You can import this data from anywhere in your site by using the `import` keyword.\n\nexport const SITE_TITLE = 'Astro Blog';\nexport const SITE_DESCRIPTION = 'Welcome to my website!';\nexport const UNUSED = true;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/content/blog/first-post.md",
    "content": "---\ntitle: 'First post'\ndescription: 'Lorem ipsum dolor sit amet'\npubDate: 'Jul 08 2022'\nheroImage: '/favicon.svg'\n---\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Vitae ultricies leo integer malesuada nunc vel risus commodo viverra. Adipiscing enim eu turpis egestas pretium. Euismod elementum nisi quis eleifend quam adipiscing. In hac habitasse platea dictumst vestibulum. Sagittis purus sit amet volutpat. Netus et malesuada fames ac turpis egestas. Eget magna fermentum iaculis eu non diam phasellus vestibulum lorem. Varius sit amet mattis vulputate enim. Habitasse platea dictumst quisque sagittis. Integer quis auctor elit sed vulputate mi. Dictumst quisque sagittis purus sit amet.\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/content/blog/using-mdx.mdx",
    "content": "---\ntitle: 'Using MDX'\ndescription: 'Lorem ipsum dolor sit amet'\npubDate: 'Jul 02 2022'\nheroImage: '/blog-placeholder-5.jpg'\n---\n\nThis theme comes with the [@astrojs/mdx](https://docs.astro.build/en/guides/integrations-guide/mdx/) integration installed and configured in your `astro.config.mjs` config file. If you prefer not to use MDX, you can disable support by removing the integration from your config file.\n\n## Why MDX?\n\nMDX is a special flavor of Markdown that supports embedded JavaScript & JSX syntax. This unlocks the ability to [mix JavaScript and UI Components into your Markdown content](https://docs.astro.build/en/guides/markdown-content/#mdx-features) for things like interactive charts or alerts.\n\nIf you have existing content authored in MDX, this integration will hopefully make migrating to Astro a breeze.\n\n## Example\n\nHere is how you import and use a UI component inside of MDX.  \nWhen you open this page in the browser, you should see the clickable button below.\n\nimport HeaderLink from '../../components/HeaderLink.astro';\n\n<HeaderLink href=\"#\" onclick=\"alert('clicked!')\">\n\tEmbedded component in MDX\n</HeaderLink>\n\n## More Links\n\n- [MDX Syntax Documentation](https://mdxjs.com/docs/what-is-mdx)\n- [Astro Usage Documentation](https://docs.astro.build/en/guides/markdown-content/#markdown-and-mdx-pages)\n- **Note:** [Client Directives](https://docs.astro.build/en/reference/directives-reference/#client-directives) are still required to create interactive components. Otherwise, all components in your MDX will render as static HTML (no JavaScript) by default.\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/content/config.ts",
    "content": "import { defineCollection, z } from 'astro:content';\n\nconst blog = defineCollection({\n  // Type-check frontmatter using a schema\n  schema: z.object({\n    title: z.string(),\n    description: z.string(),\n    // Transform string to Date object\n    pubDate: z.coerce.date(),\n    updatedDate: z.coerce.date().optional(),\n    heroImage: z.string().optional(),\n  }),\n});\n\nexport const collections = { blog };\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/env.d.ts",
    "content": "/// <reference path=\"../.astro/types.d.ts\" />\n/// <reference types=\"astro/client\" />\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/layouts/BlogPost.astro",
    "content": "---\nimport type { CollectionEntry } from 'astro:content';\nimport BaseHead from '../components/BaseHead.astro';\nimport Header from '../components/Header.astro';\nimport Footer from '../components/Footer.astro';\nimport FormattedDate from '../components/FormattedDate.astro';\n\ntype Props = CollectionEntry<'blog'>['data'];\n\nconst { title, description, pubDate, updatedDate, heroImage } = Astro.props;\n---\n\n<html lang=\"en\">\n\t<head>\n\t\t<BaseHead title={title} description={description} />\n\t\t<style>\n\t\t\tmain {\n\t\t\t\twidth: calc(100% - 2em);\n\t\t\t\tmax-width: 100%;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\t\t\t.hero-image {\n\t\t\t\twidth: 100%;\n\t\t\t}\n\t\t\t.hero-image img {\n\t\t\t\tdisplay: block;\n\t\t\t\tmargin: 0 auto;\n\t\t\t\tborder-radius: 12px;\n\t\t\t\tbox-shadow: var(--box-shadow);\n\t\t\t}\n\t\t\t.prose {\n\t\t\t\twidth: 720px;\n\t\t\t\tmax-width: calc(100% - 2em);\n\t\t\t\tmargin: auto;\n\t\t\t\tpadding: 1em;\n\t\t\t\tcolor: rgb(var(--gray-dark));\n\t\t\t}\n\t\t\t.title {\n\t\t\t\tmargin-bottom: 1em;\n\t\t\t\tpadding: 1em 0;\n\t\t\t\ttext-align: center;\n\t\t\t\tline-height: 1;\n\t\t\t}\n\t\t\t.title h1 {\n\t\t\t\tmargin: 0 0 0.5em 0;\n\t\t\t}\n\t\t\t.date {\n\t\t\t\tmargin-bottom: 0.5em;\n\t\t\t\tcolor: rgb(var(--gray));\n\t\t\t}\n\t\t\t.last-updated-on {\n\t\t\t\tfont-style: italic;\n\t\t\t}\n\t\t</style>\n\t</head>\n\n\t<body>\n\t\t<Header />\n\t\t<main>\n\t\t\t<article>\n\t\t\t\t<div class=\"hero-image\">\n\t\t\t\t\t{heroImage && <img width={1020} height={510} src={heroImage} alt=\"\" />}\n\t\t\t\t</div>\n\t\t\t\t<div class=\"prose\">\n\t\t\t\t\t<div class=\"title\">\n\t\t\t\t\t\t<div class=\"date\">\n\t\t\t\t\t\t\t<FormattedDate date={pubDate} />\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tupdatedDate && (\n\t\t\t\t\t\t\t\t\t<div class=\"last-updated-on\">\n\t\t\t\t\t\t\t\t\t\tLast updated on <FormattedDate date={updatedDate} />\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<h1>{title}</h1>\n\t\t\t\t\t\t<hr />\n\t\t\t\t\t</div>\n\t\t\t\t\t<slot />\n\t\t\t\t</div>\n\t\t\t</article>\n\t\t</main>\n\t\t<Footer />\n\t</body>\n</html>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/layouts/Layout.astro",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/pages/_top-level-dir-unused/index.ts",
    "content": "export const unused = true;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/pages/_top-level-file-unused.ts",
    "content": "export const unused = true;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/pages/about/mdx-with-layout.mdx",
    "content": "---\nlayout: \"../../layouts/Layout.astro\"\ntitle: \"MDX with Layout\"\n---\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit."
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/pages/about.astro",
    "content": "---\nimport Layout from '../layouts/BlogPost.astro';\n---\n\n<Layout\n\ttitle=\"About Me\"\n\tdescription=\"Lorem ipsum dolor sit amet\"\n\tpubDate={new Date('August 08 2021')}\n\theroImage=\"/blog-placeholder-about.jpg\"\n>\n\t<p>\n\t\tLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut\n\t\tlabore et dolore magna aliqua. Vitae ultricies leo integer malesuada nunc vel risus commodo\n\t\tviverra. Adipiscing enim eu turpis egestas pretium. Euismod elementum nisi quis eleifend quam\n\t\tadipiscing. In hac habitasse platea dictumst vestibulum. Sagittis purus sit amet volutpat. Netus\n\t\tet malesuada fames ac turpis egestas. Eget magna fermentum iaculis eu non diam phasellus\n\t\tvestibulum lorem. Varius sit amet mattis vulputate enim. Habitasse platea dictumst quisque\n\t\tsagittis. Integer quis auctor elit sed vulputate mi. Dictumst quisque sagittis purus sit amet.\n\t</p>\n\n\t<p>\n\t\tMorbi tristique senectus et netus. Id semper risus in hendrerit gravida rutrum quisque non\n\t\ttellus. Habitasse platea dictumst quisque sagittis purus sit amet. Tellus molestie nunc non\n\t\tblandit massa. Cursus vitae congue mauris rhoncus. Accumsan tortor posuere ac ut. Fringilla urna\n\t\tporttitor rhoncus dolor. Elit ullamcorper dignissim cras tincidunt lobortis. In cursus turpis\n\t\tmassa tincidunt dui ut ornare lectus. Integer feugiat scelerisque varius morbi enim nunc.\n\t\tBibendum neque egestas congue quisque egestas diam. Cras ornare arcu dui vivamus arcu felis\n\t\tbibendum. Dignissim suspendisse in est ante in nibh mauris. Sed tempus urna et pharetra pharetra\n\t\tmassa massa ultricies mi.\n\t</p>\n\n\t<p>\n\t\tMollis nunc sed id semper risus in. Convallis a cras semper auctor neque. Diam sit amet nisl\n\t\tsuscipit. Lacus viverra vitae congue eu consequat ac felis donec. Egestas integer eget aliquet\n\t\tnibh praesent tristique magna sit amet. Eget magna fermentum iaculis eu non diam. In vitae\n\t\tturpis massa sed elementum. Tristique et egestas quis ipsum suspendisse ultrices. Eget lorem\n\t\tdolor sed viverra ipsum. Vel turpis nunc eget lorem dolor sed viverra. Posuere ac ut consequat\n\t\tsemper viverra nam. Laoreet suspendisse interdum consectetur libero id faucibus. Diam phasellus\n\t\tvestibulum lorem sed risus ultricies tristique. Rhoncus dolor purus non enim praesent elementum\n\t\tfacilisis. Ultrices tincidunt arcu non sodales neque. Tempus egestas sed sed risus pretium quam\n\t\tvulputate. Viverra suspendisse potenti nullam ac tortor vitae purus faucibus ornare. Fringilla\n\t\turna porttitor rhoncus dolor purus non. Amet dictum sit amet justo donec enim.\n\t</p>\n\n\t<p>\n\t\tMattis ullamcorper velit sed ullamcorper morbi tincidunt. Tortor posuere ac ut consequat semper\n\t\tviverra. Tellus mauris a diam maecenas sed enim ut sem viverra. Venenatis urna cursus eget nunc\n\t\tscelerisque viverra mauris in. Arcu ac tortor dignissim convallis aenean et tortor at. Curabitur\n\t\tgravida arcu ac tortor dignissim convallis aenean et tortor. Egestas tellus rutrum tellus\n\t\tpellentesque eu. Fusce ut placerat orci nulla pellentesque dignissim enim sit amet. Ut enim\n\t\tblandit volutpat maecenas volutpat blandit aliquam etiam. Id donec ultrices tincidunt arcu. Id\n\t\tcursus metus aliquam eleifend mi.\n\t</p>\n\n\t<p>\n\t\tTempus quam pellentesque nec nam aliquam sem. Risus at ultrices mi tempus imperdiet. Id porta\n\t\tnibh venenatis cras sed felis eget velit. Ipsum a arcu cursus vitae. Facilisis magna etiam\n\t\ttempor orci eu lobortis elementum. Tincidunt dui ut ornare lectus sit. Quisque non tellus orci\n\t\tac. Blandit libero volutpat sed cras. Nec tincidunt praesent semper feugiat nibh sed pulvinar\n\t\tproin gravida. Egestas integer eget aliquet nibh praesent tristique magna.\n\t</p>\n</Layout>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/pages/blog/[...slug].astro",
    "content": "---\nimport { type CollectionEntry, getCollection } from 'astro:content';\nimport BlogPost from '../../layouts/BlogPost.astro';\n\nexport async function getStaticPaths() {\n  const posts = await getCollection('blog');\n  return posts.map(post => ({\n    params: { slug: post.slug },\n    props: post,\n  }));\n}\ntype Props = CollectionEntry<'blog'>;\n\nconst post = Astro.props;\nconst { Content } = await post.render();\n---\n\n<BlogPost {...post.data}>\n\t<Content />\n</BlogPost>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/pages/blog/_nested-unused-file.ts",
    "content": "export const unused = true;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/pages/blog/_util/nested/deeply-nested-unused-file.ts",
    "content": "export const nestedOne = true;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/pages/blog/_util/unused-component.astro",
    "content": "---\n---\n<h1>Unused</h1>"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/pages/blog/index.astro",
    "content": "---\nimport BaseHead from '../../components/BaseHead.astro';\nimport Header from '../../components/Header.astro';\nimport Footer from '../../components/Footer.astro';\nimport { SITE_TITLE, SITE_DESCRIPTION } from '../../consts';\nimport { getCollection } from 'astro:content';\nimport FormattedDate from '../../components/FormattedDate.astro';\n\nconst posts = (await getCollection('blog')).sort((a, b) => a.data.pubDate.valueOf() - b.data.pubDate.valueOf());\n---\n\n<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />\n\t\t<style>\n\t\t\tmain {\n\t\t\t\twidth: 960px;\n\t\t\t}\n\t\t\tul {\n\t\t\t\tdisplay: flex;\n\t\t\t\tflex-wrap: wrap;\n\t\t\t\tgap: 2rem;\n\t\t\t\tlist-style-type: none;\n\t\t\t\tmargin: 0;\n\t\t\t\tpadding: 0;\n\t\t\t}\n\t\t\tul li {\n\t\t\t\twidth: calc(50% - 1rem);\n\t\t\t}\n\t\t\tul li * {\n\t\t\t\ttext-decoration: none;\n\t\t\t\ttransition: 0.2s ease;\n\t\t\t}\n\t\t\tul li:first-child {\n\t\t\t\twidth: 100%;\n\t\t\t\tmargin-bottom: 1rem;\n\t\t\t\ttext-align: center;\n\t\t\t}\n\t\t\tul li:first-child img {\n\t\t\t\twidth: 100%;\n\t\t\t}\n\t\t\tul li:first-child .title {\n\t\t\t\tfont-size: 2.369rem;\n\t\t\t}\n\t\t\tul li img {\n\t\t\t\tmargin-bottom: 0.5rem;\n\t\t\t\tborder-radius: 12px;\n\t\t\t}\n\t\t\tul li a {\n\t\t\t\tdisplay: block;\n\t\t\t}\n\t\t\t.title {\n\t\t\t\tmargin: 0;\n\t\t\t\tcolor: rgb(var(--black));\n\t\t\t\tline-height: 1;\n\t\t\t}\n\t\t\t.date {\n\t\t\t\tmargin: 0;\n\t\t\t\tcolor: rgb(var(--gray));\n\t\t\t}\n\t\t\tul li a:hover h4,\n\t\t\tul li a:hover .date {\n\t\t\t\tcolor: rgb(var(--accent));\n\t\t\t}\n\t\t\tul a:hover img {\n\t\t\t\tbox-shadow: var(--box-shadow);\n\t\t\t}\n\t\t\t@media (max-width: 720px) {\n\t\t\t\tul {\n\t\t\t\t\tgap: 0.5em;\n\t\t\t\t}\n\t\t\t\tul li {\n\t\t\t\t\twidth: 100%;\n\t\t\t\t\ttext-align: center;\n\t\t\t\t}\n\t\t\t\tul li:first-child {\n\t\t\t\t\tmargin-bottom: 0;\n\t\t\t\t}\n\t\t\t\tul li:first-child .title {\n\t\t\t\t\tfont-size: 1.563em;\n\t\t\t\t}\n\t\t\t}\n\t\t</style>\n\t</head>\n\t<body>\n\t\t<Header />\n\t\t<main>\n\t\t\t<section>\n\t\t\t\t<ul>\n\t\t\t\t\t{\n\t\t\t\t\t\tposts.map((post) => (\n\t\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t\t<a href={`/blog/${post.slug}/`}>\n\t\t\t\t\t\t\t\t\t<img width={720} height={360} src={post.data.heroImage} alt=\"\" />\n\t\t\t\t\t\t\t\t\t<h4 class=\"title\">{post.data.title}</h4>\n\t\t\t\t\t\t\t\t\t<p class=\"date\">\n\t\t\t\t\t\t\t\t\t\t<FormattedDate date={post.data.pubDate} />\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t))\n\t\t\t\t\t}\n\t\t\t\t</ul>\n\t\t\t</section>\n\t\t</main>\n\t\t<Footer />\n\t</body>\n</html>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/pages/index.astro",
    "content": "---\nimport BaseHead from '../components/BaseHead.astro';\nimport Header from '../components/Header.astro';\nimport Footer from '../components/Footer.astro';\nimport { SITE_TITLE, SITE_DESCRIPTION } from '../consts';\nimport { AWESOME_URL } from 'astro:env/client';\n---\n\n<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />\n\t</head>\n\t<body>\n\t\t<Header title={SITE_TITLE} />\n\t\t<main>\n\t\t\t<h1>🧑‍🚀 Hello, Astronaut!</h1>\n\t\t\t<p>\n\t\t\t\tWelcome to the official <a href=\"https://astro.build/\">Astro</a> blog starter template. This\n\t\t\t\ttemplate serves as a lightweight, minimally-styled starting point for anyone looking to\n\t\t\t\tbuild a personal website, blog, or portfolio with Astro.\n\t\t\t</p>\n\t\t\t<p>\n\t\t\t\tThis template comes with a few integrations already configured in your\n\t\t\t\t<code>astro.config.mjs</code> file. You can customize your setup with\n\t\t\t\t<a href=\"https://astro.build/integrations\">Astro Integrations</a> to add tools like\n\t\t\t\tTailwind, React, or Vue to your project.\n\t\t\t</p>\n\t\t\t<p>Here are a few ideas on how to get started with the template:</p>\n\t\t\t<ul>\n\t\t\t\t<li>Edit this page in <code>src/pages/index.astro</code></li>\n\t\t\t\t<li>Edit the site header items in <code>src/components/Header.astro</code></li>\n\t\t\t\t<li>Add your name to the footer in <code>src/components/Footer.astro</code></li>\n\t\t\t\t<li>Check out the included blog posts in <code>src/pages/blog/</code></li>\n\t\t\t\t<li>Customize the blog post page layout in <code>src/layouts/BlogPost.astro</code></li>\n\t\t\t</ul>\n\t\t\t<p>\n\t\t\t\tHave fun! If you get stuck, remember to <a href=\"https://docs.astro.build/\"\n\t\t\t\t\t>read the docs\n\t\t\t\t</a> or <a href=\"https://astro.build/chat\">join us on Discord</a> to ask questions.\n\t\t\t</p>\n\t\t\t<p>\n\t\t\t\tLooking for a blog template with a bit more personality? Check out <a\n\t\t\t\t\thref=\"https://github.com/Charca/astro-blog-template\"\n\t\t\t\t\t>astro-blog-template\n\t\t\t\t</a> by <a href=\"https://twitter.com/Charca\">Maxi Ferreira</a>.\n\t\t\t</p>\n      <span>I am an awesome url: {AWESOME_URL}</span>\n\t\t</main>\n\t\t<Footer />\n\t</body>\n</html>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/pages/rss.xml.js",
    "content": "import rss from '@astrojs/rss';\nimport { getCollection } from 'astro:content';\nimport { SITE_TITLE, SITE_DESCRIPTION } from '../consts';\n\nexport async function GET(context) {\n  const posts = await getCollection('blog');\n  return rss({\n    title: SITE_TITLE,\n    description: SITE_DESCRIPTION,\n    site: context.site,\n    items: posts.map(post => ({\n      ...post.data,\n      link: `/blog/${post.slug}/`,\n    })),\n  });\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/src/styles/global.css",
    "content": "/*\n  The CSS in this style tag is based off of Bear Blog's default CSS.\n  https://github.com/HermanMartinus/bearblog/blob/297026a877bc2ab2b3bdfbd6b9f7961c350917dd/templates/styles/blog/default.css\n  License MIT: https://github.com/HermanMartinus/bearblog/blob/master/LICENSE.md\n */\n\n:root {\n  --accent: #2337ff;\n  --accent-dark: #000d8a;\n  --black: 15, 18, 25;\n  --gray: 96, 115, 159;\n  --gray-light: 229, 233, 240;\n  --gray-dark: 34, 41, 57;\n  --gray-gradient: rgba(var(--gray-light), 50%), #fff;\n  --box-shadow: 0 2px 6px rgba(var(--gray), 25%), 0 8px 24px rgba(var(--gray), 33%), 0 16px 32px rgba(var(--gray), 33%);\n}\nbody {\n  font-family: sans-serif;\n  margin: 0;\n  padding: 0;\n  text-align: left;\n  background: linear-gradient(var(--gray-gradient)) no-repeat;\n  background-size: 100% 600px;\n  word-wrap: break-word;\n  overflow-wrap: break-word;\n  color: rgb(var(--gray-dark));\n  font-size: 20px;\n  line-height: 1.7;\n}\nmain {\n  width: 720px;\n  max-width: calc(100% - 2em);\n  margin: auto;\n  padding: 3em 1em;\n}\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n  margin: 0 0 0.5rem 0;\n  color: rgb(var(--black));\n  line-height: 1.2;\n}\nh1 {\n  font-size: 3.052em;\n}\nh2 {\n  font-size: 2.441em;\n}\nh3 {\n  font-size: 1.953em;\n}\nh4 {\n  font-size: 1.563em;\n}\nh5 {\n  font-size: 1.25em;\n}\nstrong,\nb {\n  font-weight: 700;\n}\na {\n  color: var(--accent);\n}\na:hover {\n  color: var(--accent);\n}\np {\n  margin-bottom: 1em;\n}\n.prose p {\n  margin-bottom: 2em;\n}\ntextarea {\n  width: 100%;\n  font-size: 16px;\n}\ninput {\n  font-size: 16px;\n}\ntable {\n  width: 100%;\n}\nimg {\n  max-width: 100%;\n  height: auto;\n  border-radius: 8px;\n}\ncode {\n  padding: 2px 5px;\n  background-color: rgb(var(--gray-light));\n  border-radius: 2px;\n}\npre {\n  padding: 1.5em;\n  border-radius: 8px;\n}\npre > code {\n  all: unset;\n}\nblockquote {\n  border-left: 4px solid var(--accent);\n  padding: 0 0 0 20px;\n  margin: 0px;\n  font-size: 1.333em;\n}\nhr {\n  border: none;\n  border-top: 1px solid rgb(var(--gray-light));\n}\n@media (max-width: 720px) {\n  body {\n    font-size: 18px;\n  }\n  main {\n    padding: 1em;\n  }\n}\n\n.sr-only {\n  border: 0;\n  padding: 0;\n  margin: 0;\n  position: absolute !important;\n  height: 1px;\n  width: 1px;\n  overflow: hidden;\n  /* IE6, IE7 - a 0 height clip, off to the bottom right of the visible 1px box */\n  clip: rect(1px 1px 1px 1px);\n  /* maybe deprecated but we need to support legacy browsers */\n  clip: rect(1px, 1px, 1px, 1px);\n  /* modern browsers, clip-path works inwards from each corner */\n  clip-path: inset(50%);\n  /* added line to stop words getting smushed together (as they go onto separate lines and some screen readers do not understand line feeds as a space */\n  white-space: nowrap;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro/tsconfig.json",
    "content": "{\n  \"extends\": \"astro/tsconfigs/strict\",\n  \"compilerOptions\": {\n    \"strictNullChecks\": true\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro-db/astro.config.mjs",
    "content": "// @ts-check\nimport { defineConfig } from 'astro/config';\n\nimport db from '@astrojs/db';\n\n// https://astro.build/config\nexport default defineConfig({\n  integrations: [db()],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro-db/db/config.ts",
    "content": "import { defineDb } from 'astro:db';\n\n// https://astro.build/db/config\nexport default defineDb({\n  tables: {},\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro-db/db/seed.ts",
    "content": "import { db } from 'astro:db';\n\n// https://astro.build/db/seed\nexport default async function seed() {\n  // TODO\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro-db/package.json",
    "content": "{\n  \"name\": \"@plugins/astro-db\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"astro dev\",\n    \"build\": \"astro build\",\n    \"preview\": \"astro preview\",\n    \"astro\": \"astro\"\n  },\n  \"dependencies\": {\n    \"@astrojs/db\": \"^0.18.2\",\n    \"astro\": \"^5.15.3\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro-db/src/pages/index.astro",
    "content": "---\n\n---\n\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<link rel=\"icon\" type=\"image/svg+xml\" href=\"/favicon.svg\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width\" />\n\t\t<meta name=\"generator\" content={Astro.generator} />\n\t\t<title>Astro</title>\n\t</head>\n\t<body>\n\t\t<h1>Astro</h1>\n\t</body>\n</html>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro-db/tsconfig.json",
    "content": "{\n  \"extends\": \"astro/tsconfigs/strict\",\n  \"include\": [\".astro/types.d.ts\", \"**/*\"],\n  \"exclude\": [\"dist\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro-og-canvas/index.js",
    "content": "import 'astro-og-canvas';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro-og-canvas/package.json",
    "content": "{\n  \"dependencies\": {\n    \"astro-og-canvas\": \"^0.10.0\",\n    \"canvaskit-wasm\": \"^0.40.0\"\n  },\n  \"name\": \"@plugins/astro-og-canvas\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro-sharp-image-service/astro.config.mjs",
    "content": "import { defineConfig, sharpImageService } from 'astro/config';\n\nexport default defineConfig({\n  image: {\n    service: sharpImageService(),\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro-sharp-image-service/package.json",
    "content": "{\n  \"name\": \"@plugins/astro-sharp-image-service\",\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"astro\": \"*\",\n    \"sharp\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/astro-sharp-image-service/src/pages/index.astro",
    "content": "<html>\n  <body>Astro</body>\n</html>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/ava/ava.config.mjs",
    "content": "const avaConfig = {\n  files: ['**/*.test.*'],\n  extensions: ['js'],\n  require: ['tsconfig-paths/register'],\n  typescript: {\n    rewritePaths: {\n      'src/': 'build/src/',\n      'tests/': 'build/tests/',\n    },\n    compile: false,\n  },\n};\n\nexport default avaConfig;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/ava/package.json",
    "content": "{\n  \"name\": \"@plugins/ava\",\n  \"devDependencies\": {\n    \"ava\": \"*\"\n  },\n  \"scripts\": {\n    \"test\": \"ava\"\n  },\n  \"ava\": {\n    \"files\": [\n      \"**/*.test.*\"\n    ],\n    \"extensions\": {\n      \"ts\": \"module\"\n    },\n    \"nodeArguments\": [\n      \"--loader=ts-node/esm/transpile-only\",\n      \"--experimental-specifier-resolution=node\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/ava2/__tests__/__helpers__/index.cjs",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/ava2/__tests__/mod.cjs",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/ava2/ava.config.mjs",
    "content": "const avaConfig = {};\n\nexport default avaConfig;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/ava2/index.cjs",
    "content": "require('./mod.cjs');\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/ava2/mod.cjs",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/ava2/package.json",
    "content": "{\n  \"name\": \"@plugins/ava2\",\n  \"knip\": {\n    \"ava\": true\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/ava2/test.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/ava3/ava.config.mjs",
    "content": "const avaConfig = {\n  files: ['**/*.test.*'],\n  extensions: ['js', 'ts'],\n};\n\nexport default avaConfig;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/ava3/index.cjs",
    "content": "require('./mod.cjs');\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/ava3/mod.cjs",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/ava3/mod.test.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/ava3/mod.test.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/ava3/package.json",
    "content": "{\n  \"name\": \"@plugins/ava3\",\n  \"knip\": {\n    \"ava\": true\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/ava3/src/mod.test.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/ava3/src/mod.test.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/ava3/test.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/ava3/test.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/babel/.babelrc",
    "content": "{\n  \"presets\": [\"@babel/env\", \"@babel/typescript\"],\n  \"plugins\": [\n    \"react-hot-loader/babel\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"@babel/plugin-syntax-dynamic-import\",\n    \"babel-plugin-macros\"\n  ],\n  \"env\": {\n    \"production\": {\n      \"presets\": [\"minify\"]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/babel/.babelrc.js",
    "content": "require('dotenv').config({ path: '../.env' });\n\nconst config = {\n  presets: [\n    [\n      '@babel/preset-env',\n      {\n        loose: true,\n        modules: false,\n        useBuiltIns: 'usage',\n        corejs: 2,\n        shippedProposals: true,\n        targets: {\n          browsers: ['>0.25%', 'not dead'],\n        },\n      },\n    ],\n    '@babel/preset-typescript',\n  ],\n  plugins: [\n    'preval',\n    '@babel/plugin-syntax-dynamic-import',\n    'babel-plugin-macros',\n    [\n      '@babel/plugin-transform-runtime',\n      {\n        helpers: true,\n        regenerator: true,\n      },\n    ],\n    [\n      'babel-plugin-transform-imports',\n      {\n        'react-bootstrap': {\n          transform: 'react-bootstrap/lib/${member}',\n          preventFullImport: true,\n        },\n      },\n    ],\n    // Simulates a required plugin, should not be added to the unspecified plugins array\n    function myCustomPlugin() {},\n  ],\n};\n\nif (process.env.NODE_ENV === 'development' && process.env.CODESEE === 'true') {\n  config.plugins.push(['@codesee/instrument', { hosted: true }]);\n}\n\nmodule.exports = config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/babel/babel.config.cts",
    "content": "module.exports = api => {\n  return {\n    presets: [\n      '/dir/preset.js',\n      './dir/preset.js',\n      'mod',\n      'mod/preset',\n      'babel-preset-mod2',\n      '@babel/mod',\n      '@babel/preset-mod2',\n      '@babel/mod/preset',\n      '@scope',\n      '@scope2/babel-preset',\n      '@scope/mod',\n      '@scope2/babel-preset-mod',\n      '@scope/prefix-babel-preset-mod',\n      '@scope/mod/preset',\n      'module:my-preset',\n    ],\n    plugins: [\n      '/dir/plugin.js',\n      './dir/plugin.js',\n      'mod',\n      'mod/plugin',\n      'babel-plugin-mod2',\n      '@babel/mod',\n      '@babel/plugin-mod2',\n      '@babel/mod/plugin',\n      '@scope',\n      '@scope2/babel-plugin',\n      '@scope/mod',\n      '@scope2/babel-plugin-mod',\n      '@scope/prefix-babel-plugin-mod',\n      '@scope/mod/plugin',\n      'module:my-plugin',\n    ],\n  };\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/babel/babel.config.js",
    "content": "const _ = require('underscore');\n\nconst isNodeCaller = caller => caller && (caller.name === '@babel/register' || caller.name === 'babel-jest');\nconst isDistCaller = caller => !!(caller && caller.name === 'babel-gulp');\nconst supportsESM = caller => !!((caller && caller.name === 'babel-loader') || caller.useESModules);\n\nmodule.exports = api => {\n  api.cache(true);\n\n  const isDistBundle = api.caller(isDistCaller);\n  const isNode = api.caller(isNodeCaller);\n  const useESModules = !isNode && api.caller(supportsESM);\n\n  const presets = [\n    [\n      '@babel/preset-env',\n      {\n        loose: true,\n        modules: useESModules ? false : 'cjs',\n        targets: isNode ? { node: '10' } : undefined,\n        exclude: ['proposal-object-rest-spread', 'transform-async-to-generator'],\n      },\n    ],\n    ['@babel/preset-typescript', { allowNamespaces: true }],\n  ];\n  const plugins = [\n    ['@babel/plugin-proposal-class-properties', { loose: true }],\n    ['@babel/plugin-proposal-nullish-coalescing-operator', { loose: true }],\n    ['@babel/plugin-proposal-object-rest-spread', { loose: true, useBuiltIns: true }],\n    ['@babel/plugin-proposal-optional-chaining', { loose: true }],\n    '@babel/plugin-syntax-dynamic-import',\n    ['@babel/plugin-transform-runtime', { useESModules }],\n\n    useESModules && 'babel-plugin-iife-wrap-react-components',\n    useESModules && [\n      'babel-plugin-annotate-pure-imports',\n      {\n        imports: {\n          '@fluentui/react-bindings': 'compose',\n          '@fluentui/react-context-selector': 'createContext',\n          '../utils/createSvgIcon': ['createSvgIcon'],\n        },\n      },\n    ],\n    isDistBundle && 'lodash',\n  ].filter(Boolean);\n\n  return {\n    presets,\n    plugins,\n  };\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/babel/package.json",
    "content": "{\n  \"name\": \"@plugins/babel\",\n  \"scripts\": {},\n  \"devDependencies\": {\n    \"@babel/plugin-syntax-dynamic-import\": \"*\",\n    \"@babel/preset-env\": \"*\",\n    \"@babel/preset-react\": \"*\",\n    \"@babel/preset-typescript\": \"*\",\n    \"babel-plugin-macros\": \"*\",\n    \"@babel/runtime\": \"*\",\n    \"babel-plugin-prismjs\": \"*\",\n    \"underscore\": \"*\"\n  },\n  \"babel\": {\n    \"presets\": [\n      \"@babel/env\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/biome/biome.json",
    "content": "{\n  \"$schema\": \"https://biomejs.dev/schemas/2.3.8/schema.json\",\n  \"root\": false,\n  \"extends\": [\"@org/shared-configs/biome\", \"./shared/base.json\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/biome/index.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/biome/package.json",
    "content": "{\n  \"name\": \"@plugins/biome\",\n  \"module\": \"index.ts\",\n  \"type\": \"module\",\n  \"private\": true,\n  \"scripts\": {\n    \"lint\": \"biome lint .\"\n  },\n  \"devDependencies\": {\n    \"@biomejs/biome\": \"*\",\n    \"@org/shared-configs\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/biome/shared/base.json",
    "content": "{}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/biome-workspace/biome.jsonc",
    "content": "{\n  \"$schema\": \"https://biomejs.dev/schemas/2.3.8/schema.json\",\n  \"root\": false,\n  \"extends\": [\"@org/shared-configs/biome\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/biome-workspace/index.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/biome-workspace/package.json",
    "content": "{\n  \"name\": \"@plugins/biome-workspace\",\n  \"module\": \"index.ts\",\n  \"type\": \"module\",\n  \"private\": true,\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"scripts\": {\n    \"lint\": \"biome lint .\"\n  },\n  \"devDependencies\": {\n    \"@biomejs/biome\": \"*\",\n    \"@org/shared-configs\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/biome-workspace/packages/stub/biome.json",
    "content": "{\n  \"$schema\": \"https://biomejs.dev/schemas/2.3.8/schema.json\",\n  \"extends\": \"//\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/biome-workspace/packages/stub/package.json",
    "content": "{\n    \"name\": \"stub-package\"\n}"
  },
  {
    "path": "packages/knip/fixtures/plugins/bumpp/bump.config.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/bumpp/package.json",
    "content": "{\n  \"name\": \"@plugins/bumpp\",\n  \"scripts\": {\n    \"bump-version\": \"bumpp\"\n  },\n  \"devDependencies\": {\n    \"bumpp\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/bun/index.spec.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/bun/index.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/bun/package.json",
    "content": "{\n  \"name\": \"@plugins/bun\",\n  \"scripts\": {\n    \"test\": \"bun test\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/bun2/package.json",
    "content": "{\n  \"name\": \"@plugins/bun2\",\n  \"scripts\": {\n    \"test\": \"bun test src/**/*.check.ts\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/bun2/src/index.check.ts",
    "content": "import './index.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/bun2/src/index.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/bun3/bunfig.toml",
    "content": "[test]\npreload = [\"./preload.ts\"]\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/bun3/index.spec.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/bun3/package.json",
    "content": "{\n  \"name\": \"@plugins/bun3\",\n  \"scripts\": {\n    \"test\": \"bun test --preload ./setup.ts --preload ./setup-env.ts\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/bun3/preload.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/bun3/setup-env.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/bun3/setup.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/bun4/index.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/bun4/package.json",
    "content": "{\n  \"name\": \"@plugins/bun4\",\n  \"scripts\": {\n    \"test\": \"bun test ./test/\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/bun4/test/index.test.ts",
    "content": "import '../index.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/capacitor/android/capacitor.settings.gradle",
    "content": "// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME \"capacitor update\" IS RUN\ninclude ':capacitor-android'\nproject(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor')\n\ninclude ':capacitor-app'\nproject(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android')\n\ninclude ':capacitor-haptics'\nproject(':capacitor-haptics').projectDir = new File('../node_modules/@capacitor/haptics/android')\n\ninclude ':capacitor-keyboard'\nproject(':capacitor-keyboard').projectDir = new File('../node_modules/@capacitor/keyboard/android')\n\ninclude ':capacitor-status-bar'\nproject(':capacitor-status-bar').projectDir = new File('../node_modules/@capacitor/status-bar/android')\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/capacitor/capacitor.config.json",
    "content": "{\n  \"appId\": \"com.company.name\",\n  \"appName\": \"name\",\n  \"includePlugins\": [\n    \"@capacitor-community/http\",\n    \"@capacitor/app\",\n    \"@capacitor/splash-screen\",\n    \"@capacitor/status-bar\",\n    \"@capacitor/storage\",\n    \"cordova-plugin-inappbrowser\"\n  ],\n  \"plugins\": {}\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/capacitor/capacitor.config.ts",
    "content": "import type { CapacitorConfig } from '@capacitor/cli';\n\nconst config: CapacitorConfig = {\n  appId: 'com.company.name',\n  appName: 'name',\n  includePlugins: [\n    '@capacitor-community/http',\n    '@capacitor/app',\n    '@capacitor/splash-screen',\n    '@capacitor/status-bar',\n    '@capacitor/storage',\n    'cordova-plugin-inappbrowser',\n  ],\n  plugins: {},\n};\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/capacitor/ios/App/Podfile",
    "content": "require_relative '../../node_modules/@capacitor/ios/scripts/pods_helpers'\n\nplatform :ios, '13.0'\nuse_frameworks!\n\n# workaround to avoid Xcode caching of Pods that requires\n# Product -> Clean Build Folder after new Cordova plugins installed\n# Requires CocoaPods 1.6 or newer\ninstall! 'cocoapods', :disable_input_output_paths => true\n\ndef capacitor_pods\n  pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'\n  pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'\n  pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app'\n  pod 'CapacitorHaptics', :path => '../../node_modules/@capacitor/haptics'\n  pod 'CapacitorKeyboard', :path => '../../node_modules/@capacitor/keyboard'\n  pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'\nend\n\ntarget 'App' do\n  capacitor_pods\n  # Add your Pods here\nend\n\npost_install do |installer|\n  assertDeploymentTarget(installer)\nend\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/capacitor/package.json",
    "content": "{\n  \"name\": \"@plugins/capacitor\",\n  \"devDependencies\": {\n    \"@capacitor/cli\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/changelogen/changelog.config.ts",
    "content": "export default {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/changelogen/package.json",
    "content": "{\n  \"name\": \"@plugins/changelogen\",\n  \"scripts\": {\n    \"changelogen\": \"changelogen --config ./changelogen.config.ts\"\n  },\n  \"devDependencies\": {\n    \"changelogen\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/changelogithub/changelogithub.config.ts",
    "content": "export default {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/changelogithub/package.json",
    "content": "{\n  \"name\": \"@plugins/changelogithub\",\n  \"scripts\": {\n    \"changelogithub\": \"changelogithub\"\n  },\n  \"devDependencies\": {\n    \"changelogithub\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/changesets/.changeset/config.json",
    "content": "{\n  \"$schema\": \"../node_modules/@changesets/config/schema.json\",\n  \"changelog\": [\"@changesets/changelog-github\", { \"repo\": \"my/repo\" }]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/changesets/package.json",
    "content": "{\n  \"name\": \"@plugins/changesets\",\n  \"devDependencies\": {\n    \"@changesets/cli\": \"*\",\n    \"@changesets/config\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/commitizen/.czrc",
    "content": "{\n  \"path\": \"cz-conventional-changelog\"\n}"
  },
  {
    "path": "packages/knip/fixtures/plugins/commitizen/package.json",
    "content": "{\n  \"name\": \"@plugins/commitizen\",\n  \"devDependencies\": {\n    \"commitizen\": \"*\"\n  },\n  \"config\": {\n    \"commitizen\": {\n      \"path\": \"cz-conventional-changelog\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/commitlint/.commitlintrc.json",
    "content": "{\n  \"extends\": [\"@commitlint/config-conventional\"],\n  \"plugins\": [\"commitlint-plugin-tense\"],\n  \"parserPreset\": \"conventional-changelog-atom\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/commitlint/commitlint.config.js",
    "content": "module.exports = {\n  extends: ['lerna', '@commitlint/config-conventional'],\n  parserPreset: {\n    parserOpts: {\n      headerPattern: /^(\\w*)(?:\\((.*)\\))?!?: (.*)$/u,\n    },\n  },\n  formatter: '@commitlint/format',\n  plugins: [\n    {\n      rules: {\n        'contains-issue': () => {},\n        'dollar-sign': () => {},\n      },\n    },\n    'commitlint-plugin-tense',\n  ],\n  rules: {\n    'type-enum': [2, 'always', ['oh-no']],\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/commitlint/package.json",
    "content": "{\n  \"name\": \"@plugins/commitlint\",\n  \"devDependencies\": {\n    \"@commitlint/cli\": \"*\"\n  },\n  \"commitlint\": {\n    \"extends\": [\n      \"@commitlint/config-conventional\"\n    ],\n    \"plugins\": [\n      \"commitlint-plugin-tense\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/convex/convex/_generated/dataModel.d.ts",
    "content": "import schema from '../schema.js';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/convex/convex/auth.config.ts",
    "content": "export default {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/convex/convex/schema.ts",
    "content": "import { defineSchema } from 'convex/server';\n\nexport default defineSchema({});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/convex/package.json",
    "content": "{\n  \"name\": \"@plugins/convex\",\n  \"dependencies\": {\n    \"convex\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/create-typescript-app/create-typescript-app.config.js",
    "content": "import { defineConfig } from 'create-typescript-app';\n\nexport default defineConfig();\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/create-typescript-app/package.json",
    "content": "{\n  \"name\": \"@plugins/create-typescript-app\",\n  \"devDependencies\": {\n    \"create-typescript-app\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/cspell/.cspell.json",
    "content": "{\n  \"import\": [\"@cspell/dict-cryptocurrencies/cspell-ext.json\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/cspell/package.json",
    "content": "{\n  \"name\": \"@plugins/cspell\",\n  \"devDependencies\": {\n    \"cspell\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/cucumber/cucumber.js",
    "content": "export default {\n  import: ['steps/**/*.ts'],\n  format: ['@cucumber/pretty-formatter'],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/cucumber/package.json",
    "content": "{\n  \"name\": \"@plugins/cucumber\",\n  \"devDependencies\": {\n    \"@cucumber/cucumber\": \"*\",\n    \"@cucumber/pretty-formatter\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/cucumber/steps/step.ts",
    "content": "import { When, Then } from '@cucumber/cucumber';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/cypress/cypress/support/commands.ts",
    "content": "import { faker } from '@faker-js/faker';\n\nfunction login() {\n  return faker;\n}\n\nCypress.Commands.add('login', login);\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/cypress/cypress/support/e2e.ts",
    "content": "import '@testing-library/cypress/add-commands';\nimport './commands';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/cypress/cypress.config.ts",
    "content": "import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';\nimport { defineConfig } from 'cypress';\n\nconst cypressJsonConfig = {};\n\nexport default defineConfig({\n  e2e: {\n    ...nxE2EPreset(__dirname, {}),\n    ...cypressJsonConfig,\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/cypress/package.json",
    "content": "{\n  \"name\": \"@plugins/cypress\",\n  \"devDependencies\": {\n    \"cypress\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/cypress-multi-reporter/cypress/support/commands.ts",
    "content": "import { faker } from '@faker-js/faker';\n\nfunction login() {\n  return faker;\n}\n\nCypress.Commands.add('login', login);\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/cypress-multi-reporter/cypress/support/e2e.ts",
    "content": "import '@testing-library/cypress/add-commands';\nimport './commands';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/cypress-multi-reporter/cypress.config.ts",
    "content": "import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';\nimport { defineConfig } from 'cypress';\n\nconst cypressJsonConfig = {};\n\nexport default defineConfig({\n  reporter: 'cypress-multi-reporters',\n  reporterOptions: {\n    configFile: 'reporter-config.json',\n  },\n  e2e: {\n    ...nxE2EPreset(__dirname, {}),\n    ...cypressJsonConfig,\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/cypress-multi-reporter/package.json",
    "content": "{\n  \"name\": \"@plugins/cypress-multi-reporter\",\n  \"devDependencies\": {\n    \"cypress\": \"*\",\n    \"cypress-multi-reporters\": \"*\",\n    \"mocha-junit-reporter\": \"*\",\n    \"mochawesome\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/cypress-multi-reporter/reporter-config.json",
    "content": "{\n  \"reporterEnabled\": \"mochawesome, mocha-junit-reporter ,  @testing-library/my-fake-reporter\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/cypress2/cypress/support/commands.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/cypress2/cypress/support/component.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/cypress2/cypress/support/e2e.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/cypress2/cypress.config.js",
    "content": "import { defineConfig } from 'cypress';\n\nexport default defineConfig({\n  e2e: {\n    specPattern: '**/*.e2e.js',\n  },\n  component: {\n    specPattern: ['**/*.component.js'],\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/cypress2/e2e/mod.e2e.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/cypress2/mod.component.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/cypress2/package.json",
    "content": "{\n  \"name\": \"@plugins/cypress2\",\n  \"devDependencies\": {\n    \"cypress\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/danger/dangerfile.js",
    "content": "import { message } from 'danger';\n\nmessage('Everything is fine.');\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/danger/package.json",
    "content": "{\n  \"name\": \"@plugins/danger\",\n  \"devDependencies\": {\n    \"danger\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/dependency-cruiser/.dependency-cruiser.js",
    "content": "module.exports = {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/dependency-cruiser/baseline.config.js",
    "content": "module.exports = {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/dependency-cruiser/custom-depcruise-config.js",
    "content": "module.exports = {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/dependency-cruiser/dependency-cruise.config.js",
    "content": "module.exports = {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/dependency-cruiser/package.json",
    "content": "{\n  \"name\": \"@plugins/dependency-cruiser\",\n  \"devDependencies\": {\n    \"dependency-cruiser\": \"*\"\n  },\n  \"scripts\": {\n    \"test\": \"dependency-cruiser\",\n    \"test:custom\": \"depcruise --config custom-depcruise-config.js\",\n    \"test:baseline\": \"depcruise-baseline -c baseline.config.js\",\n    \"test:dependency-cruise\": \"dependency-cruise -c dependency-cruise.config.js\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/docusaurus/blog/2021-08-01-mdx-blog-post.mdx",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/docusaurus/docs/tutorial-basics/markdown-features.mdx",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/docusaurus/docusaurus.config.js",
    "content": "export default {\n  title: 'Docusaurus',\n  url: 'https://docusaurus.io',\n  scripts: ['/js/custom.js', { src: '/js/analytics.js', async: true }, 'https://example.com/external.js'],\n  stylesheets: ['/css/custom.css', { href: '/css/theme.css' }, 'https://example.com/external.css'],\n  future: {\n    experimental_faster: true,\n  },\n  themes: ['@docusaurus/theme-mermaid', '@docusaurus/theme-search-algolia'],\n  markdown: {\n    mermaid: true,\n  },\n  plugins: [\n    [\n      '@docusaurus/plugin-content-blog',\n      {\n        path: 'blog',\n        routeBasePath: 'blog',\n        include: ['*.md', '*.mdx'],\n        sidebarPath: './sidebars.js',\n      },\n    ],\n    '@docusaurus/plugin-content-pages',\n    ['sitemap', { changefreq: 'weekly' }],\n    'awesome',\n    '@my-company',\n    '@my-company/awesome',\n    '@my-company/cool/web',\n  ],\n  presets: [\n    [\n      '@docusaurus/preset-classic',\n      {\n        debug: false,\n      },\n    ],\n  ],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/docusaurus/docusaurus.config.ts",
    "content": "import type { Config } from '@docusaurus/types';\n\nexport default {\n  title: 'Docusaurus',\n  url: 'https://docusaurus.io',\n  presets: [\n    [\n      '@docusaurus/preset-classic',\n      {\n        debug: false,\n        docs: {\n          sidebarPath: './sidebars.ts',\n        },\n      },\n    ],\n  ],\n} satisfies Config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/docusaurus/package.json",
    "content": "{\n  \"name\": \"@plugins/docusaurus\",\n  \"scripts\": {\n    \"docusaurus\": \"docusaurus\",\n    \"start\": \"docusaurus start\",\n    \"build\": \"docusaurus build\"\n  },\n  \"dependencies\": {\n    \"@docusaurus/core\": \"*\",\n    \"@docusaurus/faster\": \"*\",\n    \"@docusaurus/plugin-content-pages\": \"*\",\n    \"@docusaurus/plugin-sitemap\": \"*\",\n    \"@docusaurus/preset-classic\": \"*\",\n    \"@docusaurus/theme-mermaid\": \"*\",\n    \"@mdx-js/react\": \"*\",\n    \"@my-company/docusaurus-plugin\": \"*\",\n    \"@my-company/docusaurus-plugin-awesome\": \"*\",\n    \"@my-company/docusaurus-plugin-cool\": \"*\",\n    \"docusaurus-plugin-awesome\": \"*\"\n  },\n  \"devDependencies\": {\n    \"@docusaurus/types\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/docusaurus/sidebars.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/docusaurus/sidebars.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/docusaurus/src/components/HomepageFeatures/index.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/docusaurus/src/pages/index.js",
    "content": "import Link from '@docusaurus/Link';\nimport useDocusaurusContext from '@docusaurus/useDocusaurusContext';\nimport Layout from '@theme/Layout';\nimport HomepageFeatures from '@site/src/components/HomepageFeatures';\n\nimport Heading from '@theme/Heading';\n\nfunction HomepageHeader() {\n  const { siteConfig } = useDocusaurusContext();\n  return (\n    <header>\n      <div className=\"container\">\n        <Heading as=\"h1\" className=\"hero__title\">\n          {siteConfig.title}\n        </Heading>\n        <p className=\"hero__subtitle\">{siteConfig.tagline}</p>\n        <div>\n          <Link className=\"button button--secondary button--lg\" to=\"/docs/intro\">\n            Docusaurus Tutorial - 5min ⏱️\n          </Link>\n        </div>\n      </div>\n    </header>\n  );\n}\n\nexport default function Home() {\n  const { siteConfig } = useDocusaurusContext();\n  return (\n    <Layout title={`Hello from ${siteConfig.title}`} description=\"Description will go into a meta tag in <head />\">\n      <HomepageHeader />\n      <main>\n        <HomepageFeatures />\n      </main>\n    </Layout>\n  );\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/docusaurus/static/css/custom.css",
    "content": "/* custom styles */\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/docusaurus/static/css/theme.css",
    "content": "/* theme styles */\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/docusaurus/static/js/analytics.js",
    "content": "// analytics script\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/docusaurus/static/js/custom.js",
    "content": "// custom script\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/drizzle/drizzle.config.ts",
    "content": "import type { Config } from 'drizzle-kit';\n\nexport default {\n  schema: ['./src/schema.ts', './src/schema/*.ts'],\n} satisfies Config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/drizzle/package.json",
    "content": "{\n  \"name\": \"@plugins/drizzle\",\n  \"scripts\": {\n    \"migrate\": \"drizzle-kit push:pg\"\n  },\n  \"devDependencies\": {\n    \"drizzle-orm\": \"*\",\n    \"drizzle-kit\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/drizzle/src/schema/users.ts",
    "content": "import { serial, text, pgTable } from 'drizzle-orm/pg-core';\n\nexport const users = pgTable('user', {\n  id: serial('id'),\n  name: text('name'),\n  email: text('email'),\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/drizzle/src/schema.ts",
    "content": "import { serial, text, pgTable } from 'drizzle-orm/pg-core';\n\nexport const users = pgTable('user', {\n  id: serial('id'),\n  name: text('name'),\n  email: text('email'),\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eleventy/_data/global.cjs",
    "content": "const data = {\n  title: 'Eleventy',\n};\n\nmodule.exports = data;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eleventy/_includes/footer.njk",
    "content": "<footer>\n  Build on Eleventy\n</footer>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eleventy/eleventy.config.cjs",
    "content": "module.exports = {\n  htmlTemplateEngine: 'njk',\n  markdownTemplateEngine: 'njk',\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eleventy/package.json",
    "content": "{\n  \"name\": \"@plugins/eleventy\",\n  \"scripts\": {\n    \"start\": \"npx @11ty/eleventy --serve\",\n    \"build\": \"npx @11ty/eleventy\"\n  },\n  \"devDependencies\": {\n    \"@11ty/eleventy\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eleventy/pages/index.njk",
    "content": "---\npermalink: 'index.html'\n---\n\n{% set title = global.title %}\n\n<h1> {{ title }} </h1>\n\n<main>Index content</main>\n\n{% include 'footer.njk' %}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eleventy2/_data/global.cjs",
    "content": "const data = {\n  title: 'Eleventy',\n};\n\nmodule.exports = data;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eleventy2/_includes/footer.njk",
    "content": "<footer>\n  Build on Eleventy\n</footer>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eleventy2/eleventy.config.cjs",
    "content": "// https://github.com/11ty/eleventy-base-blog/blob/main/eleventy.config.js\nmodule.exports = eleventyConfig => {\n  eleventyConfig.addPassthroughCopy({\n    './public/': '/',\n    './node_modules/prismjs/themes/prism-okaidia.css': '/css/prism-okaidia.css',\n    './js/client/script.js': 'script.js',\n  });\n  eleventyConfig.addWatchTarget('content/**/*.{svg,webp,png,jpeg}');\n  eleventyConfig.addPlugin();\n  eleventyConfig.addPlugin();\n  eleventyConfig.addPlugin();\n  eleventyConfig.addPlugin(undefined, {\n    preAttributes: { tabindex: 0 },\n  });\n  eleventyConfig.addPlugin();\n  eleventyConfig.addPlugin();\n  eleventyConfig.addPlugin();\n  eleventyConfig.addFilter('readableDate', (dateObj, format, zone) => {});\n  eleventyConfig.addFilter('htmlDateString', dateObj => {});\n  eleventyConfig.addFilter('head', (array, n) => {\n    if (!Array.isArray(array) || array.length === 0) {\n      return [];\n    }\n    if (n < 0) {\n      return array.slice(n);\n    }\n\n    return array.slice(0, n);\n  });\n  eleventyConfig.addFilter('min', (...numbers) => {\n    return Math.min.apply(null, numbers);\n  });\n  eleventyConfig.addFilter('getAllTags', collection => {\n    const tagSet = new Set();\n    for (const item of collection) {\n      (item.data.tags || []).forEach(tag => tagSet.add(tag));\n    }\n    return Array.from(tagSet);\n  });\n  eleventyConfig.addFilter('filterTagList', function filterTagList(tags) {\n    return (tags || []).filter(tag => ['all', 'nav', 'post', 'posts'].indexOf(tag) === -1);\n  });\n  eleventyConfig.amendLibrary('md', mdLib => {\n    eleventyConfig.getFilter('slugify');\n  });\n  return {\n    templateFormats: ['md', 'njk', 'html', 'liquid'],\n    markdownTemplateEngine: 'njk',\n    htmlTemplateEngine: 'njk',\n    dir: {\n      input: 'content',\n      includes: '../_includes',\n      data: '../_data',\n      output: '_site',\n    },\n    pathPrefix: '/',\n  };\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eleventy2/package.json",
    "content": "{\n  \"name\": \"@plugins/eleventy2\",\n  \"scripts\": {\n    \"start\": \"npx @11ty/eleventy --serve\",\n    \"build\": \"npx @11ty/eleventy\"\n  },\n  \"devDependencies\": {\n    \"@11ty/eleventy\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eleventy2/pages/index.njk",
    "content": "---\npermalink: 'index.html'\n---\n\n{% set title = global.title %}\n\n<h1> {{ title }} </h1>\n\n<main>Index content</main>\n\n{% include 'footer.njk' %}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eleventy3/.eleventy.js",
    "content": "'use strict';\n\nconst eleventyNavigationPlugin = require('@11ty/eleventy-navigation');\nconst syntaxHighlight = require('@11ty/eleventy-plugin-syntaxhighlight');\nconst path = require('node:path');\nconst highlighter = require('./src/_plugins/syntax-highlighter');\npath;\nhighlighter;\n\nmodule.exports = function (eleventyConfig) {\n  eleventyConfig.addGlobalData('site_name', 'example');\n  eleventyConfig.addDataExtension('yml', () => {});\n  eleventyConfig.addFilter('limitTo', () => {});\n  eleventyConfig.addFilter('sortByPageOrder', () => {});\n  eleventyConfig.addPlugin(eleventyNavigationPlugin);\n  eleventyConfig.addPlugin(syntaxHighlight, {\n    alwaysWrapLineHighlights: true,\n    templateFormats: ['liquid', 'njk'],\n  });\n  eleventyConfig.setLibrary('md', () => {});\n  eleventyConfig.addNunjucksShortcode('link', () => {});\n  eleventyConfig.addShortcode('related_rules', () => {});\n  eleventyConfig.addWatchTarget('./src/assets/');\n  eleventyConfig.addPassthroughCopy({\n    'src/_includes/abc.js': '/assets/abc.js',\n  });\n  eleventyConfig.addPassthroughCopy({\n    './src/static': '/',\n  });\n  eleventyConfig.addPassthroughCopy('./src/assets/');\n  eleventyConfig.addPassthroughCopy('src/static');\n  eleventyConfig.addCollection('docs', () => {});\n  eleventyConfig.addCollection('library', () => {});\n  eleventyConfig.ignores.add('src/static/sitemap.njk');\n\n  eleventyConfig.addPassthroughCopy({\n    './node_modules/@org/lib/dist/build.js': '/assets/lib.js',\n  });\n\n  return {\n    passthroughFileCopy: true,\n    pathPrefix: '/docs/head/',\n    markdownTemplateEngine: 'njk',\n    htmlTemplateEngine: 'njk',\n    dir: {\n      input: 'src',\n      includes: '_includes',\n      layouts: '_includes/layouts',\n      data: '_data',\n      output: '_site',\n    },\n  };\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eleventy3/_includes/footer.njk",
    "content": "<footer>\n  Build on Eleventy\n</footer>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eleventy3/package.json",
    "content": "{\n  \"name\": \"@plugins/eleventy3\",\n  \"scripts\": {\n    \"start\": \"npx @11ty/eleventy --serve\",\n    \"build\": \"npx @11ty/eleventy\",\n    \"build:website:watch\": \"eleventy --serve --port=2023\"\n  },\n  \"devDependencies\": {\n    \"@11ty/eleventy\": \"*\",\n    \"@11ty/eleventy-navigation\": \"*\",\n    \"@11ty/eleventy-plugin-syntaxhighlight\": \"*\",\n    \"@org/lib\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eleventy3/pages/index.njk",
    "content": "---\npermalink: 'index.html'\n---\n\n{% set title = global.title %}\n\n<h1> {{ title }} </h1>\n\n<main>Index content</main>\n\n{% include 'footer.njk' %}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eleventy3/src/_includes/abc.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/eleventy3/src/_plugins/syntax-highlighter.js",
    "content": "export default () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eleventy3/src/assets/index.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/eleventy3/src/static/index.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/eleventy4/.eleventy.js",
    "content": "'use strict';\n\nmodule.exports = async function (eleventyConfig) {\n  eleventyConfig.addPassthroughCopy('./src/assets/');\n  eleventyConfig.addBundle('css', { hoist: true });\n};\n\nmodule.exports.config = {\n  dir: {\n    input: 'src',\n    data: '_siteData',\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eleventy4/package.json",
    "content": "{\n  \"name\": \"@plugins/eleventy4\",\n  \"scripts\": {\n    \"start\": \"npx @11ty/eleventy --serve\",\n    \"build\": \"npx @11ty/eleventy\",\n    \"build:website:watch\": \"eleventy --serve --port=2023\"\n  },\n  \"devDependencies\": {\n    \"@11ty/eleventy\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eleventy4/src/_siteData/global.cjs",
    "content": "const data = {\n  title: 'Eleventy',\n};\n\nmodule.exports = data;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eleventy4/src/assets/index.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/eslint/.eslintrc.cjs",
    "content": "module.exports = {\n  extends: [\n    'eslint:recommended',\n    'plugin:@typescript-eslint/strict',\n    'plugin:prettier/recommended',\n    'eslint-config-airbnb',\n    'plugin:@shopify/esnext',\n    'next',\n  ],\n  plugins: ['eslint-plugin-import', '@scope/name', '@scope'],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eslint/.eslintrc.js",
    "content": "module.exports = {\n  root: true,\n  extends: [\n    require.resolve('./base.eslint.json'),\n    'airbnb',\n    'plugin:@typescript-eslint/recommended',\n    'plugin:@typescript-eslint/eslint-recommended',\n    'plugin:@typescript-eslint/stylistic-type-checked',\n    'next/core-web-vitals',\n    'plugin:@next/next/recommended',\n    'plugin:eslint-comments/recommended',\n    'plugin:eslint-plugin/all',\n    '@scope/eslint-config/file',\n    'plugin:prettier/recommended',\n  ],\n  plugins: ['@typescript-eslint', '@nrwl/nx', 'eslint-plugin-cypress', '@scope-only', '@scope/eslint-plugin'],\n  settings: {\n    'import/resolver': {\n      typescript: true,\n      exports: true,\n    },\n    'import/parsers': {\n      '@typescript-eslint/parser': ['.ts', '.tsx'],\n    },\n  },\n  parserOptions: {\n    sourceType: 'module',\n    babelOptions: {\n      plugins: [['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: true }]],\n    },\n  },\n  overrides: [\n    {\n      extends: ['plugin:@org/name/typescript'],\n      rules: {\n        '@other-org/no-unused-expressions': 'error',\n        '@other-org/no-unused-vars': 'error',\n      },\n    },\n  ],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eslint/.eslintrc.json",
    "content": "{\n  \"root\": true,\n  \"extends\": [\n    \"./base.eslint.json\",\n    \"airbnb\",\n    \"plugin:@typescript-eslint/eslint-recommended\",\n    \"plugin:@typescript-eslint/recommended\",\n    \"prettier\"\n  ],\n  \"plugins\": [\"@typescript-eslint\"],\n  \"parserOptions\": {\n    \"babelOptions\": {\n      \"plugins\": [\"@babel/plugin-syntax-import-assertions\"]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eslint/.eslintrc.yml",
    "content": "extends:\n  - '@sinonjs/eslint-config'\n\nplugins:\n  - '@sinonjs/no-prototype-methods'\n\nrules:\n  '@sinonjs/no-prototype-methods/no-prototype-methods': error\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eslint/base.eslint.json",
    "content": "{\n  \"extends\": [\"eslint:recommended\"],\n  \"parser\": \"@typescript-eslint/parser\",\n  \"plugins\": [\"import\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eslint/index.ts",
    "content": "export const x = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eslint/package.json",
    "content": "{\n  \"name\": \"@plugins/eslint\",\n  \"devDependencies\": {\n    \"eslint\": \"*\",\n    \"@nrwl/eslint-plugin-nx\": \"*\",\n    \"@typescript-eslint/eslint-plugin\": \"*\",\n    \"@typescript-eslint/parser\": \"*\",\n    \"eslint-config-prettier\": \"*\",\n    \"eslint-plugin-prettier\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eslint2/eslint.config.ts",
    "content": "import eslint from '@eslint/js';\nimport tseslint from 'typescript-eslint';\n\nexport default [\n  ...(tseslint.default ?? tseslint).config(eslint.configs.recommended, tseslint.configs.recommended),\n  {\n    files: ['**/*.{ts,tsx}'],\n    settings: {\n      'import/parsers': {\n        '@typescript-eslint/parser': ['.ts', '.tsx'],\n      },\n      'import/resolver': {\n        typescript: true,\n        node: true,\n      },\n    },\n    rules: {},\n  },\n];\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eslint2/knip.json",
    "content": "{\n  \"eslint\": [\"eslint.config.ts\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eslint2/package.json",
    "content": "{\n  \"name\": \"@plugins/eslint2\",\n  \"scripts\": {\n    \"lint\": \"eslint --inspect-config --format codeframe\"\n  },\n  \"devDependencies\": {\n    \"@eslint/config-inspector\": \"*\",\n    \"@eslint/js\": \"*\",\n    \"eslint\": \"*\",\n    \"eslint-formatter-codeframe\": \"*\",\n    \"eslint-import-resolver-typescript\": \"*\",\n    \"typescript-eslint\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eslint3/eslint.config.js",
    "content": "import { defineConfig } from 'eslint/config';\nimport prettier from 'eslint-plugin-prettier';\nimport noSecrets from 'eslint-plugin-no-secrets';\n\nexport default defineConfig([\n  'eslint:recommended',\n  {\n    plugins: {\n      prettier,\n      'no-secrets': noSecrets,\n    },\n  },\n]);\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eslint3/index.ts",
    "content": "export const x = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eslint3/package.json",
    "content": "{\n  \"name\": \"@plugins/eslint3\",\n  \"devDependencies\": {\n    \"eslint\": \"*\",\n    \"eslint-plugin-no-secrets\": \"*\",\n    \"eslint-plugin-prettier\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eslint4/eslint.config.ts",
    "content": "import js from '@eslint/js';\nimport type { Linter } from 'eslint';\n\nexport default [\n  js.configs.recommended,\n  {\n    rules: {\n      'no-console': [0],\n    },\n  },\n] satisfies Linter.Config[];\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eslint4/index.ts",
    "content": "export const x = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eslint4/package.json",
    "content": "{\n  \"name\": \"@plugins/eslint4\",\n  \"scripts\": {\n    \"lint\": \"eslint --fix --format codeframe .\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"*\",\n    \"eslint-formatter-codeframe\": \"*\",\n    \"@eslint/js\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eslint5/eslint.config.ts",
    "content": "import js from '@eslint/js';\nimport { importX } from 'eslint-plugin-import-x';\nimport tsParser from '@typescript-eslint/parser';\n\nexport default [\n  js.configs.recommended,\n  importX.flatConfigs.recommended,\n  importX.flatConfigs.typescript,\n  {\n    files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],\n    languageOptions: {\n      parser: tsParser,\n      ecmaVersion: 'latest',\n      sourceType: 'module',\n    },\n    rules: {\n      'import-x/no-dynamic-require': 'warn',\n      'import-x/no-nodejs-modules': 'warn',\n    },\n  },\n];\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eslint5/knip.json",
    "content": "{\n  \"eslint\": [\"eslint.config.ts\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/eslint5/package.json",
    "content": "{\n  \"name\": \"@plugins/eslint2\",\n  \"devDependencies\": {\n    \"@eslint/js\": \"*\",\n    \"eslint-import-resolver-typescript\": \"*\",\n    \"eslint-plugin-import-x\": \"*\",\n    \"@typescript-eslint/parser\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/expo/app.config.ts",
    "content": "const config = {\n  name: 'Knip',\n  updates: {\n    enabled: true,\n  },\n  notification: {\n    color: '#ffffff',\n  },\n  userInterfaceStyle: 'automatic',\n  ios: {\n    backgroundColor: '#ffffff',\n  },\n  plugins: [\n    ['@config-plugins/detox', { subdomains: '*' }],\n    '@sentry/react-native/expo',\n    ['expo-splash-screen', { backgroundColor: '#ffffff' }],\n  ],\n};\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/expo/package.json",
    "content": "{\n  \"name\": \"@plugins/expo\",\n  \"scripts\": {\n    \"start\": \"expo start\"\n  },\n  \"dependencies\": {\n    \"@config-plugins/detox\": \"*\",\n    \"@sentry/react-native\": \"*\",\n    \"expo\": \"*\",\n    \"expo-atlas\": \"*\",\n    \"expo-dev-client\": \"*\",\n    \"expo-notifications\": \"*\",\n    \"expo-router\": \"*\",\n    \"expo-splash-screen\": \"*\",\n    \"expo-system-ui\": \"*\",\n    \"expo-updates\": \"*\",\n    \"react\": \"*\",\n    \"react-native\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/expo/src/app/index.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/expo2/app.config.js",
    "content": "const config = {\n  name: 'Knip',\n  platforms: ['android'],\n  androidNavigationBar: {\n    visible: true,\n  },\n  android: {\n    userInterfaceStyle: 'dark',\n  },\n  plugins: [\n    'expo-camera',\n    [\n      'expo-router',\n      {\n        root: 'src/routes',\n      },\n    ],\n  ],\n};\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/expo2/package.json",
    "content": "{\n  \"name\": \"@plugins/expo2\",\n  \"main\": \"expo-router/entry\",\n  \"scripts\": {\n    \"start\": \"expo start\"\n  },\n  \"dependencies\": {\n    \"expo\": \"*\",\n    \"expo-dev-client\": \"*\",\n    \"expo-insights\": \"*\",\n    \"expo-navigation-bar\": \"*\",\n    \"expo-router\": \"*\",\n    \"react\": \"*\",\n    \"react-native\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/expo2/src/routes/index.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/expo3/app/_layout.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/expo3/app.json",
    "content": "{\n  \"expo\": {\n    \"name\": \"Knip\",\n    \"slug\": \"knip\",\n    \"platforms\": [\"ios\", \"web\"],\n    \"updates\": {\n      \"enabled\": false\n    },\n    \"plugins\": [\"react-native-ble-plx\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/expo3/package.json",
    "content": "{\n  \"name\": \"@plugins/expo3\",\n  \"main\": \"expo-router/entry\",\n  \"scripts\": {\n    \"start\": \"expo start\"\n  },\n  \"dependencies\": {\n    \"@expo/metro-runtime\": \"*\",\n    \"expo\": \"*\",\n    \"expo-system-ui\": \"*\",\n    \"expo-updates\": \"*\",\n    \"react\": \"*\",\n    \"react-dom\": \"*\",\n    \"react-native\": \"*\",\n    \"react-native-web\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/expressive-code/app/page.tsx",
    "content": "export default function Page() {\n  return null;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/expressive-code/next.config.mjs",
    "content": "import createMDX from '@next/mdx';\nimport rehypeExpressiveCode from 'rehype-expressive-code';\nimport { pluginCollapsibleSections } from '@expressive-code/plugin-collapsible-sections';\n\n/** @type {import('rehype-expressive-code').RehypeExpressiveCodeOptions} */\nconst rehypeExpressiveCodeOptions = {\n  themes: ['github-dark', 'github-light'],\n  plugins: [pluginCollapsibleSections()],\n};\n\n/** @type {import('next').NextConfig} */\nconst nextConfig = {\n  pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'],\n};\n\nconst withMDX = createMDX({\n  options: {\n    rehypePlugins: [[rehypeExpressiveCode, rehypeExpressiveCodeOptions]],\n  },\n});\n\nexport default withMDX(nextConfig);\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/expressive-code/package.json",
    "content": "{\n  \"name\": \"@fixtures/expressive-code-nextjs\",\n  \"scripts\": {\n    \"dev\": \"next dev\",\n    \"build\": \"next build\"\n  },\n  \"dependencies\": {\n    \"next\": \"*\",\n    \"@next/mdx\": \"*\",\n    \"rehype-expressive-code\": \"*\",\n    \"@expressive-code/plugin-collapsible-sections\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/expressive-code2/astro.config.mjs",
    "content": "import { defineConfig } from 'astro/config';\nimport mdx from '@astrojs/mdx';\nimport sitemap from '@astrojs/sitemap';\nimport expressiveCode from 'astro-expressive-code';\n\n// https://astro.build/config\nexport default defineConfig({\n  site: 'https://example.com',\n  integrations: [expressiveCode(), mdx(), sitemap()],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/expressive-code2/ec.config.mjs",
    "content": "import { defineEcConfig } from 'astro-expressive-code';\nimport { pluginCollapsibleSections } from '@expressive-code/plugin-collapsible-sections';\n\nexport default defineEcConfig({\n  themes: ['dracula', 'github-light'],\n  plugins: [pluginCollapsibleSections()],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/expressive-code2/package.json",
    "content": "{\n  \"name\": \"@fixtures/expressive-code-astro\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"astro dev\",\n    \"build\": \"astro build\"\n  },\n  \"dependencies\": {\n    \"@astrojs/mdx\": \"*\",\n    \"@astrojs/sitemap\": \"*\",\n    \"astro-expressive-code\": \"*\",\n    \"@expressive-code/plugin-collapsible-sections\": \"*\",\n    \"astro\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/expressive-code3/astro.config.mjs",
    "content": "import starlight from '@astrojs/starlight';\nimport { defineConfig } from 'astro/config';\n\nexport default defineConfig({\n  integrations: [\n    starlight({\n      title: 'My Docs',\n    }),\n  ],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/expressive-code3/ec.config.mjs",
    "content": "import { pluginLineNumbers } from '@expressive-code/plugin-line-numbers';\n\n/** @type {import('@astrojs/starlight/expressive-code').StarlightExpressiveCodeOptions} */\nexport default {\n  themes: ['starlight-dark', 'starlight-light'],\n  plugins: [pluginLineNumbers()],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/expressive-code3/package.json",
    "content": "{\n  \"name\": \"@fixtures/expressive-code-starlight\",\n  \"dependencies\": {\n    \"@astrojs/starlight\": \"*\",\n    \"@expressive-code/plugin-line-numbers\": \"*\",\n    \"astro\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/gatsby/gatsby-config.js",
    "content": "module.exports = {\n  siteMetadata: {\n    title: 'knip',\n  },\n  plugins: [\n    {\n      resolve: '@sentry/gatsby',\n      options: {},\n    },\n    {\n      resolve: 'gatsby-plugin-webpack-bundle-analyser-v2',\n      options: {},\n    },\n    'gatsby-plugin-react-helmet',\n    'gatsby-plugin-postcss',\n    {\n      resolve: 'gatsby-plugin-create-client-paths',\n    },\n    {\n      resolve: 'gatsby-source-filesystem',\n    },\n    {\n      resolve: 'gatsby-transformer-remark',\n    },\n    {\n      resolve: 'gatsby-remark-node-identity',\n    },\n    {\n      resolve: 'gatsby-remark-node-identity',\n      options: {\n        identity: 'superBlockIntroMarkdown',\n      },\n    },\n    {\n      resolve: 'gatsby-plugin-manifest',\n    },\n    'gatsby-plugin-remove-serviceworker',\n  ],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/gatsby/gatsby-node.js",
    "content": "exports.onCreateBabelConfig = ({ actions }) => {\n  actions.setBabelPlugin({\n    name: '@babel/plugin-proposal-function-bind',\n  });\n  actions.setBabelPlugin({\n    name: '@babel/plugin-proposal-export-default-from',\n  });\n  actions.setBabelPlugin({\n    name: 'babel-plugin-transform-imports',\n    options: {\n      preventFullImport: true,\n    },\n  });\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/gatsby/package.json",
    "content": "{\n  \"name\": \"@plugins/gatsby\",\n  \"scripts\": {\n    \"serve\": \"gatsby serve -p 8000\"\n  },\n  \"devDependencies\": {\n    \"gatsby\": \"*\",\n    \"gatsby-cli\": \"*\",\n    \"gatsby-plugin-advanced-sitemap\": \"*\",\n    \"gatsby-plugin-create-client-paths\": \"*\",\n    \"gatsby-plugin-manifest\": \"*\",\n    \"gatsby-plugin-postcss\": \"*\",\n    \"gatsby-plugin-react-helmet\": \"*\",\n    \"gatsby-plugin-remove-serviceworker\": \"*\",\n    \"gatsby-remark-prismjs\": \"*\",\n    \"gatsby-source-filesystem\": \"*\",\n    \"gatsby-transformer-remark\": \"*\",\n    \"@babel/plugin-proposal-export-default-from\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/github-action/action.yml",
    "content": "name: 'My Action'\ndescription: 'My Action'\nruns:\n  using: 'node20'\n  main: 'dist/index.js'\n  post: 'dist/index.js'\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/github-action/dist/index.js",
    "content": "import 'not-found';\nimport './404.js';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/github-action/package.json",
    "content": "{\n  \"name\": \"@plugins/github-action\",\n  \"dependencies\": {\n    \"@actions/core\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/github-action/src/main.ts",
    "content": "import '@actions/core';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/github-actions/.github/actions/composite/action.yml",
    "content": "name: Action Composite\ndescription: Description\n\nruns:\n  using: 'composite'\n  steps:\n    - run: asdf install '${{ matrix.tool }}' '${{ steps.version.outputs.latest }}'\n      shell: /bin/bash\n    - run: node -r esbuild-register ./comment.ts\n      shell: /bin/bash\n    - run: npx playwright install --with-deps\n      shell: /bin/bash\n    - run: pnpm eslint\n      shell: /bin/bash\n    - run: yarn workspace @apps/website exe script.ts\n      shell: /bin/bash\n    - run: yarn workspace @apps/website playwright test --project=e2e\n      shell: /bin/bash\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/github-actions/.github/actions/node-a/action.yml",
    "content": "name: Action Node A\ndescription: Description\n\nruns:\n  using: node16\n  pre: './dist/pre.js'\n  main: main.js\n  post: './post.js'\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/github-actions/.github/actions/node-a/dist/pre.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/github-actions/.github/actions/node-a/main.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/github-actions/.github/actions/node-a/post.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/github-actions/.github/actions/node-b/action.yaml",
    "content": "name: Action Node B\ndescription: Description\n\nruns:\n  using: 'node20'\n  main: main.js\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/github-actions/.github/actions/node-b/main.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/github-actions/.github/workflows/test.yml",
    "content": "name: workflow\n\non:\n  pull_request:\n\nenv:\n  ROLE_ARN: !Sub 'arn:aws:iam::${AWS::AccountId}:role/role-name'\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - run: asdf install '${{ matrix.tool }}' '${{ steps.version.outputs.latest }}'\n      - run: cat var/logs/frontend.log\n      - run: cd code\n      - run: curl -X POST \"https://example.org/${{ secrets.SECRET }}\"\n      - run: docker-compose -f apps/site/docker-compose.dev.yml down\n      - run: docker-compose up -d && sleep 3\n      - run: git config user.email \"${GITHUB_ACTOR}@users.noreply.github.com\"\n      - run: git config user.name \"${GITHUB_ACTOR}\"\n      - run: git pull --force --no-tags origin main:main\n      - run: kill -2 $(lsof -t -i:3000)\n      - run: node -r esbuild-register ./comment.ts\n      - run: node ./scripts/check-dependencies.js\n      - run: npm ci\n      - run: npm config set //registry.npmjs.org/:_authToken $NPM_TOKEN\n      - run: npm install\n      - run: npm install\n      - run: npm run build\n      - run: npm run publish\n      - run: npx playwright install --with-deps\n      - run: npx prisma migrate reset --force\n      - run: pnpm build\n      - run: pnpm eslint\n      - run: pnpm lint\n      - run: pnpm self-update\n      - run: pnpm release-it\n      - run: pnpm run setup:test\n      - run: pnpm run test --coverage\n      - run: semgrep ci --sarif --output=semgrep.sarif\n      - run: unzip ${{ matrix.stack.name }}.zip\n      - run: yarn --frozen-lockfile\n      - run: yarn build\n      - run: yarn dev:seed-db\n      - run: yarn exec knip\n      - run: yarn install\n      - run: yarn nyc report --reporter=lcovonly --reporter=text\n      - run: yarn playwright install-deps chrome\n      - run: yarn playwright install-deps chrome\n      - run: yarn task --task compile --start-from=auto --no-link\n      - run: yarn workspace @apps/website exe script.ts\n      - run: yarn workspace @apps/website playwright test --project=e2e\n      - run: |\n          echo \"registry=https://registry.npmjs.org\" >> ~/.npmrc\n          echo \"//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}\" >> ~/.npmrc\n      - run: |\n          package=$(node ./script.js)\n          echo \"package=${package}\" >> $GITHUB_OUTPUT\n      - run: |\n          for STR in \"A\" \"B\"; do\n            npx retry-cli@0.6.0 -- curl --output /dev/null --silent --write-out \"@curl-format.txt\" \"${STR}\"\n            npx @scope/retry-cli@0.6.0 -- curl --output /dev/null --silent --write-out \"@curl-format.txt\" \"${STR}\"\n          done\n      - run: |\n          git push\n          yarn changeset publish --tag canary\n      - run: |\n          yarn dev:seed-db 2>&1 | tee var/logs/db.log &\n          yarn wait-on --timeout 60000 tcp:0.0.0.0:27017\n      - run: |\n          yarn workspace @apps/website build\n          yarn workspace @apps/website start 2>&1 | tee var/logs/frontend.log &\n          yarn wait-on --timeout 10000 http://0.0.0.0:3000\n      - run: |\n          git config user.email \"${GITHUB_ACTOR}@users.noreply.github.com\"\n          git config user.name \"${GITHUB_ACTOR}\"\n      - run: |\n          curl -X POST https://example.org/dispatches \\\n            -H 'Accept: application/vnd.github.v3+json' \\\n            -u ${{ secrets.SECRET }} \\\n            --data '{\"event_type\": \"request-create-frontpage-branch\" }}'\n      - run: |\n          VERSION=\"v$(jq -r '.version' < package.json)\"\n          echo \"version=$VERSION\" >> \"$GITHUB_OUTPUT\"\n          if [ \"$(git tag --list \"$VERSION\")\" ]; then\n            echo 'released=true' >> \"$GITHUB_OUTPUT\"\n          else\n            echo 'released=false' >> \"$GITHUB_OUTPUT\"\n            {\n              echo 'release_notes<<EOF'\n              node scripts/get-release-notes.js\n              echo 'EOF'\n            } >> \"$GITHUB_OUTPUT\"\n          fi\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/github-actions/.github/workflows/working-directory.yml",
    "content": "name: CI\n\non: push\n\njobs:\n  job1:\n    runs-on: ubuntu-latest\n    name: work\n\n    steps:\n      - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938\n\n      - run: node scripts/no-working-dir\n\n  job2:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@4\n        with:\n          path: some-dir\n\n      - run: node scripts/from-working-dir-with-path\n        working-directory: ./some-dir\n\n  job3:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@4\n\n      - run: node from-working-dir\n        working-directory: scripts\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/github-actions/comment.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/github-actions/package.json",
    "content": "{\n  \"name\": \"@plugins/github-actions\",\n  \"scripts\": {\n    \"build\": \"\",\n    \"dev:seed-db\": \"\",\n    \"lint\": \"\",\n    \"setup:test\": \"\",\n    \"task\": \"\",\n    \"test:integration\": \"\",\n    \"test\": \"\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/github-actions/scripts/check-dependencies.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/github-actions/scripts/from-working-dir-with-path.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/github-actions/scripts/from-working-dir.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/github-actions/scripts/get-release-notes.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/github-actions/scripts/no-working-dir.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/github-actions-workspaces/.github/workflows/dir.yml",
    "content": "name: CI\n\non: push\n\njobs:\n  stylelint:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n      - name: Run stylelint check\n        run: yarn stylelint\n        working-directory: packages/app\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/github-actions-workspaces/package.json",
    "content": "{\n  \"name\": \"@plugins/github-actions-workspaces\",\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"scripts\": {\n    \"hello-app\": \"yarn run --cwd packages/app goodbye\"\n  },\n  \"dependencies\": {\n    \"hello\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/github-actions-workspaces/packages/app/package.json",
    "content": "{\n  \"name\": \"@plugins/github-actions-workspaces__app\",\n  \"scripts\": {\n    \"stylelint\": \"stylelint\",\n    \"hello-world\": \"yarn run --top-level hello\"\n  },\n  \"devDependencies\": {\n    \"stylelint\": \"*\",\n    \"goodbye\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/github-actions-workspaces/packages/lib/package.json",
    "content": "{\n  \"name\": \"@plugins/github-actions-workspaces__lib\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/graphql-codegen/codegen.ts",
    "content": "module.exports = {\n  schema: 'schema.graphql',\n  overwrite: true,\n  generates: {\n    './graphql.schema.json': {\n      plugins: ['introspection', 'graphql-codegen-typescript-validation-schema'],\n    },\n    './graphql.schema.graphql': {\n      'schema-ast': {},\n    },\n    './src/generated/graphql.ts': {\n      documents: ['./src/**/*.tsx'],\n      preset: '@graphql-codegen/graphql-modules-preset',\n      plugins: ['typescript', 'typescript-operations', 'typescript-urql'],\n    },\n    './lib/': {\n      documents: ['./lib/**/*.tsx'],\n      preset: 'near-operation-file-preset',\n      plugins: ['typescript-operations', 'typescript-msw', 'graphql-codegen-apollo-next-ssr'],\n    },\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/graphql-codegen/codegen.yaml",
    "content": "generates:\n  '../../packages/graphql-types/src/types.ts':\n    config:\n    documents:\n      'src/documents/**/*.graphql':\n        skipGraphQLImport: false\n    plugins:\n      - typescript\n      - typescript-operations\n      - typed-document-node\n      - ./codegen/UserErrorsPlugin.cjs\n      - add:\n          content: \"/* cSpell:disable */\\nimport { type SerializedMoney } from '@contra/money';\"\n  'src/contra-api/__generated__/types.ts':\n    config:\n      contextType: '../types.js#ResolverContext'\n      mappers:\n        ActivePaidProjectAnalytics: '../types.js#ActivePaidProjectAnalytics'\n        Workspace: '@contra/temporary-common/domain/workspace/WorkspaceService.js#Workspace'\n    plugins:\n      - typescript\n      - typescript-resolvers\n      - ./codegen/UserErrorsPlugin.cjs\n      - ./codegen/GraphQLResolverBuilderPlugin.cjs\n      - add:\n          content:\n            \"/* cSpell:disable */\\nimport type { DeepPartial } from 'utility-types';\\nimport { type Money as\n            ContraMoney, type USD } from '@contra/money';\"\nschema:\n  - node_modules/@contra/graphql-schema/index.js\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/graphql-codegen/package.json",
    "content": "{\n  \"name\": \"@plugins/graphql-codegen\",\n  \"scripts\": {\n    \"codegen\": \"graphql-codegen\"\n  },\n  \"devDependencies\": {\n    \"@graphql-codegen/cli\": \"*\"\n  },\n  \"codegen\": {\n    \"schema\": \"schema.graphql\",\n    \"documents\": [\n      \"src/**/*.tsx\",\n      \"!src/gql/**/*\"\n    ],\n    \"generates\": {\n      \"./src/gql/\": {\n        \"preset\": \"client\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/graphql-codegen-graphql-config/.graphqlrc",
    "content": "projects:\n  app:\n    schema: schema.graphql\n    extensions:\n      codegen:\n        overwrite: true\n        generates:\n          ../../packages/graphql-types/src/types.ts:\n            config:\n              documents:\n                'src/documents/**/*.graphql':\n              skipGraphQLImport: false\n            plugins:\n              - typescript\n              - typescript-operations\n              - typed-document-node\n              - ./codegen/UserErrorsPlugin.cjs\n              - add:\n                  content: \"/* cSpell:disable */\\nimport { type SerializedMoney } from '@contra/money';\"\n  server:\n    schema: schema.graphql\n    extensions:\n      codegen:\n        overwrite: true\n        generates:\n          src/contra-api/__generated__/types.ts:\n            config:\n              contextType: ../types.js#ResolverContext\n              mappers:\n                ActivePaidProjectAnalytics: ../types.js#ActivePaidProjectAnalytics\n                Workspace: '@contra/temporary-common/domain/workspace/WorkspaceService.js#Workspace'\n            plugins:\n              - typescript\n              - typescript-resolvers\n              - ./codegen/UserErrorsPlugin.cjs\n              - ./codegen/GraphQLResolverBuilderPlugin.cjs\n              - add:\n                  content:\n                    \"/* cSpell:disable */\\nimport type { DeepPartial } from 'utility-types';\\nimport { type Money as\n                    ContraMoney, type USD } from '@contra/money';\"\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/graphql-codegen-graphql-config/graphql.config.toml",
    "content": "[projects.app]\nschema = \"schema.graphql\"\n\n[projects.app.extensions.codegen]\noverwrite = true\n\n[projects.app.extensions.codegen.generates.\"../../packages/graphql-types/src/types.ts\"]\nconfig.documents = \"src/documents/**/*.graphql\"\nconfig.skipGraphQLImport = false\nplugins = [\n    \"typescript\",\n    \"typescript-operations\",\n    \"typed-document-node\",\n    \"./codegen/UserErrorsPlugin.cjs\",\n    { add = { content = \"/* cSpell:disable */\\nimport { type SerializedMoney } from '@contra/money';\" } }\n]\n\n[projects.server]\nschema = \"schema.graphql\"\n\n[projects.server.extensions.codegen]\noverwrite = true\n\n[projects.server.extensions.codegen.generates.\"src/contra-api/__generated__/types.ts\"]\nconfig.contextType = \"../types.js#ResolverContext\"\nconfig.mappers.ActivePaidProjectAnalytics = \"../types.js#ActivePaidProjectAnalytics\"\nconfig.mappers.Workspace = \"@contra/temporary-common/domain/workspace/WorkspaceService.js#Workspace\"\nplugins = [\n    \"typescript\",\n    \"typescript-resolvers\",\n    \"./codegen/UserErrorsPlugin.cjs\",\n    \"./codegen/GraphQLResolverBuilderPlugin.cjs\",\n    { add = { content = \"/* cSpell:disable */\\nimport type { DeepPartial } from 'utility-types';\\nimport { type Money as ContraMoney, type USD } from '@contra/money';\" } }\n]\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/graphql-codegen-graphql-config/graphql.config.ts",
    "content": "import type { IGraphQLConfig } from 'graphql-config';\n\nconst config: IGraphQLConfig = {\n  schema: 'schema.graphql',\n  extensions: {\n    codegen: {\n      overwrite: true,\n      generates: {\n        './graphql.schema.json': {\n          plugins: ['introspection'],\n        },\n        './graphql.schema.graphql': {\n          'schema-ast': {},\n        },\n        './src/generated/graphql.ts': {\n          documents: ['./src/**/*.tsx'],\n          plugins: ['typescript', 'typescript-operations', 'typescript-urql'],\n        },\n        './lib/': {\n          documents: ['./lib/**/*.tsx'],\n          preset: 'near-operation-file-preset',\n          plugins: ['typescript-operations', 'typescript-msw'],\n        },\n      },\n    },\n  },\n};\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/graphql-codegen-graphql-config/package.json",
    "content": "{\n  \"name\": \"@plugins/graphql-codegen-graphql-config\",\n  \"scripts\": {\n    \"codegen\": \"graphql-codegen\"\n  },\n  \"devDependencies\": {\n    \"@graphql-codegen/cli\": \"*\",\n    \"graphql-config\": \"*\"\n  },\n  \"graphql\": {\n    \"schema\": \"schema.graphql\",\n    \"documents\": [\n      \"src/**/*.tsx\",\n      \"!src/gql/**/*\"\n    ],\n    \"extensions\": {\n      \"codegen\": {\n        \"generates\": {\n          \"./src/gql/\": {\n            \"preset\": \"client\"\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/graphql-codegen-graphql-config2/package.json",
    "content": "{\n  \"name\": \"@plugins/graphql-codegen-graphql-config2\",\n  \"scripts\": {\n    \"codegen\": \"graphql-codegen\"\n  },\n  \"devDependencies\": {\n    \"@graphql-codegen/cli\": \"*\"\n  },\n  \"graphql\": {\n    \"schema\": \"schema.graphql\",\n    \"documents\": [\n      \"src/**/*.tsx\",\n      \"!src/gql/**/*\"\n    ],\n    \"extensions\": {\n      \"codegen\": {\n        \"generates\": {\n          \"./src/gql/\": {\n            \"preset\": \"client\"\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/hardhat/hardhat.config.ts",
    "content": "const config = {};\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/hardhat/package.json",
    "content": "{\n  \"name\": \"@plugins/hardhat\",\n  \"devDependencies\": {\n    \"hardhat\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/husky-lint-staged/package.json",
    "content": "{\n  \"name\": \"@plugins/husky-lint-staged\",\n  \"devDependencies\": {\n    \"husky\": \"*\",\n    \"lint-staged\": \"*\",\n    \"prettier\": \"*\"\n  },\n  \"husky\": {\n    \"hooks\": {\n      \"pre-commit\": \"lint-staged\"\n    }\n  },\n  \"lint-staged\": {\n    \"*.*\": [\n      \"prettier --write\",\n      \"git add\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/husky-v8/.husky/post-checkout",
    "content": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nyarn install\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/husky-v8/.husky/pre-commit",
    "content": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nnpx lint-staged\nnpx --no-install commitlint --edit \"$1\"\nnpm test\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/husky-v8/.husky/pre-push",
    "content": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nyarn jest\nyarn pretty-quick --check\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/husky-v8/.husky/pre-rebase",
    "content": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\npnpm build\npnpm eslint\npnpm lint-staged\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/husky-v8/package.json",
    "content": "{\n  \"name\": \"@plugins/husky-v8\",\n  \"scripts\": {\n    \"prepare\": \"husky install\",\n    \"build\": \"node\",\n    \"test\": \"node\"\n  },\n  \"devDependencies\": {\n    \"@commitlint/cli\": \"*\",\n    \"husky\": \"8.0.0\",\n    \"lint-staged\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/husky-v9/.husky/post-checkout",
    "content": "yarn install\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/husky-v9/.husky/pre-commit",
    "content": "npx lint-staged\nnpx --no-install commitlint --edit \"$1\"\nnpm test\n\nif ! command -v yalc check &> /dev/null\nthen\n    echo \"yalc could not be found\"\n    exit\nfi\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/husky-v9/.husky/pre-push",
    "content": "yarn jest\nyarn pretty-quick --check\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/husky-v9/.husky/pre-rebase",
    "content": "pnpm build\npnpm eslint\npnpm lint-staged\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/husky-v9/package.json",
    "content": "{\n  \"name\": \"@plugins/husky-v9\",\n  \"scripts\": {\n    \"prepare\": \"husky\",\n    \"build\": \"node\",\n    \"test\": \"node\"\n  },\n  \"devDependencies\": {\n    \"@commitlint/cli\": \"*\",\n    \"husky\": \"9.0.0\",\n    \"lint-staged\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/husky-v9-1/.husky/pre-commit",
    "content": "lint-staged\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/husky-v9-1/package.json",
    "content": "{\n  \"name\": \"@plugins/husky-v9-1\",\n  \"scripts\": {\n    \"prepare\": \"husky\"\n  },\n  \"devDependencies\": {\n    \"husky\": \"^9.1.3\",\n    \"lint-staged\": \"^15.2.7\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/i18next-parser/custom.config.js",
    "content": "export default {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/i18next-parser/i18next-parser.config.js",
    "content": "export default {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/i18next-parser/package.json",
    "content": "{\n  \"name\": \"@plugins/i18next-parser\",\n  \"scripts\": {\n    \"i18next\": \"i18next -c custom.config.js\"\n  },\n  \"devDependencies\": {\n    \"i18next-parser\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/jest/customSuiteProperties.cjs",
    "content": "module.exports = {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/jest/jest.config.js",
    "content": "/** @type {import('@jest/types').Config.InitialOptions} */\nmodule.exports = {\n  ...require('./jest.config.shared'),\n  displayName: 'dev',\n  runtime: '@side/jest-runtime',\n  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],\n  setupFiles: [],\n  moduleNameMapper: {\n    '\\\\.(jpg|jpeg|...|aac|oga)$': '<rootDir>/__mocks__/fileMock.js',\n    '\\\\.(css|less)$': 'identity-obj-proxy',\n    '^(\\\\.{1,2}/.*)\\\\.js$': '$1',\n    '^src/(.*)$': '<rootDir>/../src/$1',\n  },\n  reporters: [\n    'default',\n    'jest-silent-reporter',\n    [\n      'jest-junit',\n      {\n        outputDirectory: 'reports',\n        outputName: 'report.xml',\n        testSuitePropertiesFile: 'customSuiteProperties.cjs',\n        testSuitePropertiesDirectory: '<rootDir>',\n      },\n    ],\n    ['github-actions', { silent: false }],\n    'summary',\n  ],\n  projects: [\n    {\n      displayName: 'lint',\n      runner: 'jest-runner-eslint',\n      testMatch: ['<rootDir>/**/*.js'],\n    },\n  ],\n  testResultsProcessor: 'jest-phabricator',\n  snapshotResolver: '<rootDir>/snapshotResolver.js',\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/jest/jest.config.shared.js",
    "content": "const ignorePatterns = ['\\\\/build\\\\/', '\\\\/coverage\\\\/', '\\\\/\\\\.vscode\\\\/', '\\\\/\\\\.tmp\\\\/', '\\\\/\\\\.cache\\\\/'];\n/** @type {import('@jest/types').Config.InitialOptions} */\nmodule.exports = {\n  preset: './local-preset/jest-preset.js',\n  modulePathIgnorePatterns: ignorePatterns,\n  watchPathIgnorePatterns: [...ignorePatterns, '\\\\/node_modules\\\\/'],\n  testMatch: ['<rootDir>/**/*-test.[jt]s?(x)'],\n  setupFiles: ['<rootDir>/__tests__/setup.ts'],\n  transform: {\n    '^(?!.*\\\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest',\n    '^.+\\\\.[tj]sx?$': ['babel-jest', { cwd: __dirname, configFile: './babel-jest.config.json' }],\n    '^.+\\\\.[jt]sx?$': '<rootDir>/jest.transform.js',\n  },\n  watchPlugins: [require.resolve('jest-watch-select-projects')],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/jest/jest.setup.js",
    "content": "import '@testing-library/jest-dom/extend-expect';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/jest/jest.transform.js",
    "content": "module.exports = {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/jest/local-preset/jest-preset.js",
    "content": "module.exports = {\n  testEnvironment: 'jsdom',\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/jest/package.json",
    "content": "{\n  \"name\": \"@plugins/jest\",\n  \"scripts\": {},\n  \"devDependencies\": {\n    \"jest\": \"*\",\n    \"jest-environment-jsdom\": \"*\",\n    \"jest-watch-select-projects\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/jest/snapshotResolver.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/jest2/jest.config.js",
    "content": "module.exports = {\n  displayName: 'dev',\n  testEnvironment: '<rootDir>/jest.environment.js',\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/jest2/jest.environment.js",
    "content": "module.exports = {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/jest2/knip.ts",
    "content": "export default {\n  jest: {\n    config: ['jest.config.js', 'project1/jest.config.js', 'package.json'],\n    entry: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'],\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/jest2/package.json",
    "content": "{\n  \"name\": \"@plugins/jest2\",\n  \"scripts\": {},\n  \"devDependencies\": {\n    \"jest\": \"*\",\n    \"@testing-library/jest-dom\": \"*\",\n    \"jest-junit\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/jest2/project1/customProperties.cjs",
    "content": "module.exports = {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/jest2/project1/jest.config.js",
    "content": "const path = require('node:path');\n\nmodule.exports = {\n  rootDir: path.join(__dirname, '../'),\n  displayName: 'project1',\n  setupFilesAfterEnv: ['<rootDir>/project1/setupFiles/setup.js'],\n  reporters: [\n    'default',\n    [\n      'jest-junit',\n      {\n        outputDirectory: '<rootDir>',\n        outputName: 'junit-vcr.xml',\n        testCasePropertiesFile: 'customProperties.cjs',\n        testCasePropertiesDirectory: '<rootDir>/project1',\n      },\n    ],\n  ],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/jest2/project1/setupFiles/setup.js",
    "content": "import '@testing-library/jest-dom/extend-expect';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/jest3/__mocks__/used.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/jest3/index.spec.ts",
    "content": "import 'jest';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/jest3/jest.config.js",
    "content": "module.exports = {\n  testEnvironment: 'node',\n  reporters: [\n    'default',\n    [\n      'jest-junit',\n      {\n        outputDirectory: 'reports/junit',\n        outputName: 'junit.xml',\n      },\n    ],\n  ],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/jest3/package.json",
    "content": "{\n  \"name\": \"@plugins/jest3\",\n  \"scripts\": {},\n  \"devDependencies\": {\n    \"jest\": \"*\",\n    \"jest-junit\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/karma/karma.conf.js",
    "content": "// Karma configuration\n// Generated on Fri Dec 06 2024 13:19:12 GMT+0100 (Central European Standard Time)\n// With command `bunx karma@6.4.4 init`\n\nmodule.exports = config => {\n  config.set({\n    // base path that will be used to resolve all patterns (eg. files, exclude)\n    basePath: '',\n\n    // frameworks to use\n    // available frameworks: https://www.npmjs.com/search?q=keywords:karma-adapter\n    frameworks: ['jasmine'],\n\n    // list of files / patterns to load in the browser\n    files: [],\n\n    // list of files / patterns to exclude\n    exclude: [],\n\n    // preprocess matching files before serving them to the browser\n    // available preprocessors: https://www.npmjs.com/search?q=keywords:karma-preprocessor\n    preprocessors: {},\n\n    // test results reporter to use\n    // possible values: 'dots', 'progress'\n    // available reporters: https://www.npmjs.com/search?q=keywords:karma-reporter\n    reporters: ['progress'],\n\n    // web server port\n    port: 9876,\n\n    // enable / disable colors in the output (reporters and logs)\n    colors: true,\n\n    // level of logging\n    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG\n    logLevel: config.LOG_INFO,\n\n    // enable / disable watching file and executing tests whenever any file changes\n    autoWatch: true,\n\n    // start these browsers\n    // available browser launchers: https://www.npmjs.com/search?q=keywords:karma-launcher\n    browsers: ['Chrome'],\n\n    // Continuous Integration mode\n    // if true, Karma captures browsers, runs the tests and exits\n    singleRun: false,\n\n    // Concurrency level\n    // how many browser instances should be started simultaneously\n    concurrency: Number.POSITIVE_INFINITY,\n  });\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/karma/package.json",
    "content": "{\n  \"name\": \"@plugins/karma\",\n  \"scripts\": {},\n  \"devDependencies\": {\n    \"karma\": \"*\",\n    \"karma-coverage\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/karma2/karma.conf.js",
    "content": "module.exports = config => {\n  config.set({\n    basePath: 'src',\n    files: ['**/*.spec.js'],\n    exclude: ['**/*excluded*.js'],\n  });\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/karma2/out-of-base-path/example.spec.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/karma2/package.json",
    "content": "{\n  \"name\": \"@plugins/karma2\",\n  \"scripts\": {},\n  \"devDependencies\": {\n    \"karma\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/karma2/src/another-example.spec.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/karma2/src/example.spec.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/karma2/src/excluded.spec.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/karma3/karma-plugin.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/karma3/karma.conf.js",
    "content": "module.exports = config => {\n  config.set({\n    plugins: ['karma-jasmine', 'karma-coverage', './karma-plugin'],\n  });\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/karma3/package.json",
    "content": "{\n  \"name\": \"@plugins/karma3\",\n  \"scripts\": {},\n  \"devDependencies\": {\n    \"karma\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/knex/index.ts",
    "content": "import knex from 'knex';\n\nconst config = require('./knexfile');\nconst db = knex(config.development);\n\nexport default db;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/knex/knexfile.js",
    "content": "module.exports = {\n  client: 'pg',\n  connection: {\n    host: '127.0.0.1',\n    database: 'my_db',\n    user: 'username',\n    password: 'password',\n  },\n  migrations: {\n    directory: './migrations',\n  },\n  seeds: {\n    directory: './seeds',\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/knex/migrations/20231201_create_users_table.js",
    "content": "exports.up = knex =>\n  knex.schema.createTable('users', table => {\n    table.increments('id');\n    table.string('email').notNullable().unique();\n    table.string('name');\n    table.timestamps(true, true);\n  });\n\nexports.down = knex => knex.schema.dropTableIfExists('users');\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/knex/package.json",
    "content": "{\n  \"name\": \"@plugins/knex\",\n  \"dependencies\": {\n    \"knex\": \"*\",\n    \"pg\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/knex/seeds/01_users.js",
    "content": "exports.seed = async knex => {\n  await knex('users').del();\n  await knex('users').insert([\n    { id: 1, email: 'user1@example.com', name: 'User One' },\n    { id: 2, email: 'user2@example.com', name: 'User Two' },\n    { id: 3, email: 'user3@example.com', name: 'User Three' },\n  ]);\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/ladle/.ladle/components.tsx",
    "content": "import { type GlobalProvider } from '@ladle/react';\nimport React from 'react';\n\n// For details see https://ladle.dev/docs/providers\nexport const Provider: GlobalProvider = ({ children }) => <div>{children}</div>;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/ladle/.ladle/config.mjs",
    "content": "// For details see https://ladle.dev/docs/config\n/** @type {import('@ladle/react').UserConfig} */\nexport default {\n  stories: 'app/**/*.stories.{tsx,mdx}',\n  viteConfig: './.ladle/vite.config.ts',\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/ladle/.ladle/vite.config.ts",
    "content": "import { defineConfig } from 'vite';\n\nexport default defineConfig({\n  server: {\n    open: false,\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/ladle/app/basic.stories.tsx",
    "content": "import React from 'react';\n\nexport const Hello = () => <h2>Simple Story</h2>;\n\nexport const Responsive = () => {\n  return (\n    <>\n      <div\n        style={{\n          width: '100',\n          background: '#000',\n          color: '#FFF',\n          padding: '32px 32px',\n          border: '1px solid black',\n          fontFamily: 'arial',\n          fontSize: 28,\n        }}\n      >\n        Header\n      </div>\n      <button\n        type=\"button\"\n        style={{\n          padding: '16px 102px',\n          fontFamily: 'arial',\n          fontSize: 22,\n          margin: 32,\n          borderRadius: 8,\n          color: '#174291',\n          border: '2px solid #174291',\n          background: '#FFF',\n        }}\n      >\n        Ladle v4\n      </button>\n    </>\n  );\n};\nResponsive.meta = {\n  width: 'xsmall',\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/ladle/app/control.stories.tsx",
    "content": "import type { Story } from '@ladle/react';\n\nexport const Controls: Story<{\n  label: string;\n  disabled: boolean;\n  count: number;\n  colors: string[];\n  variant: string;\n  size: string;\n}> = ({ count, disabled, label, colors, variant, size }) => (\n  <>\n    <p>Count: {count}</p>\n    <p>Disabled: {disabled ? 'yes' : 'no'}</p>\n    <p>Label: {label}</p>\n    <p>Colors: {colors.join(',')}</p>\n    <p>Variant: {variant}</p>\n    <p>Size: {size}</p>\n  </>\n);\n\nControls.args = {\n  label: 'Hello world',\n  disabled: false,\n  count: 2,\n  colors: ['Red', 'Blue'],\n};\nControls.argTypes = {\n  variant: {\n    options: ['primary', 'secondary'],\n    control: { type: 'radio' },\n    defaultValue: 'primary',\n  },\n  size: {\n    options: ['small', 'medium', 'big', 'huuuuge'],\n    control: { type: 'select' },\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/ladle/package.json",
    "content": "{\n  \"name\": \"@plugins/ladle\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"ladle serve --port 4123\",\n    \"build\": \"ladle build\"\n  },\n  \"dependencies\": {\n    \"@ladle/react\": \"*\",\n    \"react\": \"*\",\n    \"react-dom\": \"*\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"*\",\n    \"@types/react\": \"*\",\n    \"@types/react-dom\": \"*\",\n    \"typescript\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/ladle/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"DOM\", \"DOM.Iterable\", \"ESNext\"],\n    \"allowJs\": false,\n    \"skipLibCheck\": false,\n    \"esModuleInterop\": false,\n    \"allowSyntheticDefaultImports\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\"\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/lefthook/example.mjs",
    "content": "export default 1;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/lefthook/lefthook.yml",
    "content": "post-merge:\n  commands:\n    example:\n      glob: .env.example\n      run: node example.mjs\n\npre-push:\n  commands:\n    eslint:\n      files: git diff --name-only HEAD @{push}\n      glob: '*.{ts,tsx}'\n      run: npx eslint {files}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/lefthook/package.json",
    "content": "{\n  \"name\": \"@plugins/lefthook\",\n  \"scripts\": {\n    \"postinstall\": \"lefthook install\"\n  },\n  \"devDependencies\": {\n    \"@arkweid/lefthook\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/lefthook-ci/lefthook.yml",
    "content": "pre-push:\n  parallel: true\n\npre-commit:\n  commands:\n    frontend-linter:\n      run: yarn eslint {staged_files}\n    backend-linter:\n      run: bundle exec rubocop --force-exclusion {all_files}\n    frontend-style:\n      files: git diff --name-only HEAD @{push}\n      run: yarn stylelint {files}\n\npost-commit:\n  parallel: true\n  commands:\n    format:\n      glob: '*.{ts,tsx,md,json,vue,yml}'\n      run: npx --no prettier {staged_files} --w\n    eslint:\n      glob: '*.{js,ts,jsx,tsx,vue}'\n      run: npx --no eslint --color --fix {staged_files}\n\nfixer:\n  commands:\n    ruby-fixer:\n      run: bundle exec rubocop --force-exclusion --safe-auto-correct {staged_files}\n    js-fixer:\n      run: yarn eslint --fix {staged_files}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/lefthook-ci/package.json",
    "content": "{\n  \"name\": \"@plugins/lefthook-ci\",\n  \"scripts\": {},\n  \"devDependencies\": {\n    \"@evilmartians/lefthook\": \"*\",\n    \"eslint\": \"*\",\n    \"prettier\": \"*\",\n    \"stylelint\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/lefthook-v1/_git/HEAD",
    "content": "ref: refs/heads/main\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/lefthook-v1/_git/config",
    "content": "[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = false\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/lefthook-v1/_git/hooks/prepare-commit-msg",
    "content": "#!/bin/sh\n\nif [ \"$LEFTHOOK\" = \"0\" ]; then\n  exit 0\nfi\n\ncall_lefthook()\n{\n  dir=\"$(git rev-parse --show-toplevel)\"\n  osArch=$(uname | tr '[:upper:]' '[:lower:]')\n  cpuArch=$(uname -m | sed 's/aarch64/arm64/')\n\n  if lefthook -h >/dev/null 2>&1\n  then\n    lefthook \"$@\"\n  elif test -f \"$dir/node_modules/lefthook/bin/index.js\"\n  then\n    \"$dir/node_modules/lefthook/bin/index.js\" \"$@\"\n  elif test -f \"$dir/node_modules/@evilmartians/lefthook/bin/lefthook_${osArch}_${cpuArch}/lefthook\"\n  then\n    \"$dir/node_modules/@evilmartians/lefthook/bin/lefthook_${osArch}_${cpuArch}/lefthook\" \"$@\"\n  elif test -f \"$dir/node_modules/@evilmartians/lefthook-installer/bin/lefthook_${osArch}_${cpuArch}/lefthook\"\n  then\n    \"$dir/node_modules/@evilmartians/lefthook-installer/bin/lefthook_${osArch}_${cpuArch}/lefthook\" \"$@\"\n  elif bundle exec lefthook -h >/dev/null 2>&1\n  then\n    bundle exec lefthook \"$@\"\n  elif yarn lefthook -h >/dev/null 2>&1\n  then\n    yarn lefthook \"$@\"\n  elif pnpm lefthook -h >/dev/null 2>&1\n  then\n    pnpm lefthook \"$@\"\n  elif command -v npx >/dev/null 2>&1\n  then\n    npx @evilmartians/lefthook \"$@\"\n  else\n    echo \"Can't find lefthook in PATH\"\n  fi\n}\n\ncall_lefthook run \"prepare-commit-msg\" \"$@\"\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/lefthook-v1/_git/objects/.gitkeep",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/lefthook-v1/_git/refs/heads/.gitkeep",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/lefthook-v1/lefthook.yml",
    "content": "pre-push:\n  parallel: true\n\npre-commit:\n  commands:\n    frontend-linter:\n      run: yarn eslint {staged_files}\n    backend-linter:\n      run: bundle exec rubocop --force-exclusion {all_files}\n    frontend-style:\n      files: git diff --name-only HEAD @{push}\n      run: yarn stylelint {files}\n\npost-commit:\n  parallel: true\n  commands:\n    format:\n      glob: '*.{ts,tsx,md,json,vue,yml}'\n      run: npx --no prettier {staged_files} --w\n    eslint:\n      glob: '*.{js,ts,jsx,tsx,vue}'\n      run: npx --no eslint --color --fix {staged_files}\n\nfixer:\n  commands:\n    ruby-fixer:\n      run: bundle exec rubocop --force-exclusion --safe-auto-correct {staged_files}\n    js-fixer:\n      run: yarn eslint --fix {staged_files}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/lefthook-v1/package.json",
    "content": "{\n  \"name\": \"@plugins/lefthook-v1\",\n  \"scripts\": {},\n  \"devDependencies\": {\n    \"eslint\": \"*\",\n    \"lefthook\": \"*\",\n    \"prettier\": \"*\",\n    \"stylelint\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/lint-staged/.lintstagedrc.js",
    "content": "module.exports = {\n  '*.(js|ts|tsx)': async files => {\n    return ['eslint --max-warnings=0 --cache --fix .', 'prettier --write .'];\n  },\n  '*.!(js|ts|tsx)': files => {\n    return files.map(filename => `prettier --write --ignore-unknown '${filename}'`);\n  },\n  '**/*': ['prettier --write --ignore-unknown', () => 'npm run knip', () => 'npx tsc --noEmit'],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/lint-staged/package.json",
    "content": "{\n  \"name\": \"@plugins/lint-staged\",\n  \"devDependencies\": {\n    \"lint-staged\": \"*\"\n  },\n  \"lint-staged\": {\n    \"*.js\": [\n      \"eslint\",\n      \"prettier --write\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/lint-staged-comment/.lintstagedrc.json",
    "content": "{\n  \"*.+(css|json|md|html|yaml|yml)\": [\"prettier --write\"],\n  \"*.+(js|jsx|ts|tsx)\": [\"eslint --fix\", \"prettier --write\"],\n  \"_comment\": \"This file is generated. Note that while it is possible to alter this file, changes should be made carefully.\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/lint-staged-comment/package.json",
    "content": "{\n  \"name\": \"@fixtures/lint-staged-comment\",\n  \"devDependencies\": {\n    \"lint-staged\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/linthtml/.linthtmlrc.js",
    "content": "/** @typedef {import('@linthtml/linthtml').LinterConfig} LinterConfig */\n\n/** @type {LinterConfig} */\nconst config = {\n  extends: '@linthtml/linthtml-config-recommended',\n  rules: {\n    'attr-quote-style': [true, 'double'],\n    'title-max-len': [true, 60],\n  },\n};\n\nmodule.exports = config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/linthtml/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<meta charset=\"UTF-8\" />\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n\t<title>LintHTML</title>\n</head>\n<body>\n  <h1>LintHTML</h1>\n</body>\n</html>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/linthtml/package.json",
    "content": "{\n  \"name\": \"@plugins/linthtml\",\n  \"scripts\": {\n    \"lint:html\": \"npx @linthtml/linthtml \\\"**/*.html\\\"\"\n  },\n  \"devDependencies\": {\n    \"@linthtml/linthtml\": \"*\",\n    \"@linthtml/linthtml-config-recommended\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/lockfile-lint/.lockfile-lintrc.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/lockfile-lint/package.json",
    "content": "{\n  \"name\": \"@plugins/lockfile-lint\",\n  \"scripts\": {\n    \"lockfile-lint\": \"lockfile-lint\"\n  },\n  \"dependencies\": {\n    \"lockfile-lint\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/lost-pixel/lostpixel.config.ts",
    "content": "import type { CustomProjectConfig } from 'lost-pixel';\n\nexport const config: CustomProjectConfig = {\n  apiKey: process.env.LOST_PIXEL_API_KEY,\n\n  ladleShots: {\n    ladleUrl: 'dist/ladle',\n  },\n\n  lostPixelProjectId: process.env.LOST_PIXEL_PROJECT_ID,\n\n  shotConcurrency: 5,\n  setPendingStatusCheck: true,\n\n  imagePathBaseline: '.lostpixel/baseline',\n  imagePathCurrent: '.lostpixel/current',\n  imagePathDifference: '.lostpixel/difference',\n\n  ciBuildId: process.env.BUILD_ID,\n  ciBuildNumber: process.env.BUILD_NUMBER,\n  repository: process.env.REPOSITORY_NAME,\n  commitRefName: process.env.CHANGE_BRANCH ?? process.env.BRANCH_NAME,\n  commitHash: process.env.GIT_COMMIT,\n\n  configureBrowser: () => ({\n    userAgent:\n      'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36',\n    viewport: {\n      width: 800,\n      height: 600,\n    },\n  }),\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/lost-pixel/package.json",
    "content": "{\n  \"name\": \"@plugins/lost-pixel\",\n  \"devDependencies\": {\n    \"lost-pixel\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/markdownlint/.markdownlint.json",
    "content": "{\n  \"extends\": \"markdownlint/style/prettier\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/markdownlint/package.json",
    "content": "{\n  \"name\": \"@plugins/markdownlint\",\n  \"scripts\": {\n    \"md\": \"markdownlint \\\"**/*.md\\\" --rules rule-package\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/mdx/package.json",
    "content": "{\n  \"name\": \"@plugins/mdx\",\n  \"devDependencies\": {\n    \"astro\": \"*\",\n    \"remark-directive\": \"*\",\n    \"remark-frontmatter\": \"*\",\n    \"typescript\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/mdx/tsconfig.json",
    "content": "{\n  \"extends\": \"astro/tsconfigs/strict\",\n  \"mdx\": {\n    \"plugins\": [[\"remark-frontmatter\", [\"toml\", \"yaml\"]], \"remark-directive\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/mdxlint/.mdxlintrc.mjs",
    "content": "import remarkDirective from 'remark-directive';\n\nexport default {\n  plugins: [remarkDirective, 'remark-gfm'],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/mdxlint/package.json",
    "content": "{\n  \"name\": \"@plugins/mdxlint\",\n  \"scripts\": {\n    \"mdx\": \"mdxlint\"\n  },\n  \"devDependencies\": {\n    \"mdxlint\": \"*\",\n    \"remark-directive\": \"*\",\n    \"remark-gfm\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/metro/custom-metro-transformer.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/metro/metro.config.js",
    "content": "module.exports = {\n  projectRoot: './src/app',\n  transformer: {\n    minifierPath: 'metro-minify-esbuild',\n    assetPlugins: ['expo-asset/tools/hashAssetFiles'],\n    babelTransformerPath: 'react-native-svg-transformer',\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/metro/package.json",
    "content": "{\n  \"name\": \"@plugins/metro\",\n  \"dependencies\": {\n    \"@react-native/metro-config\": \"*\",\n    \"metro-minify-esbuild\": \"*\",\n    \"react-native-svg-transformer\": \"*\"\n  },\n  \"metro\": {\n    \"transformerPath\": \"./custom-metro-transformer.js\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/metro/src/app/index.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/metro-defaults/metro.config.js",
    "content": "module.exports = {\n  projectRoot: './src/app',\n  transformerPath: 'metro-transform-worker',\n  transformer: {\n    minifierPath: 'metro-minify-terser',\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/metro-defaults/package.json",
    "content": "{\n  \"name\": \"@plugins/metro-defaults\",\n  \"dependencies\": {\n    \"metro\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/metro-defaults/src/app/index.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/metro-react-native/metro.config.js",
    "content": "require('@react-native/metro-config');\n\nmodule.exports = {\n  projectRoot: './src/app',\n  transformer: {\n    assetPlugins: ['expo-asset/tools/hashAssetFiles'],\n    babelTransformerPath: 'react-native-svg-transformer',\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/metro-react-native/package.json",
    "content": "{\n  \"name\": \"@plugins/metro-react-native\",\n  \"dependencies\": {\n    \"@react-native-community/cli\": \"*\",\n    \"@react-native-community/cli-platform-android\": \"*\",\n    \"@react-native-community/cli-platform-ios\": \"*\",\n    \"@react-native/metro-config\": \"*\",\n    \"expo-asset\": \"*\",\n    \"react\": \"*\",\n    \"react-native\": \"*\",\n    \"react-native-svg-transformer\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/metro-react-native/src/app/index.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/mocha/.mocharc.json",
    "content": "{\n  \"spec\": [\"**/*.test.ts\"],\n  \"require\": [\"ts-node/register\", \"hooks.ts\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/mocha/.mocharc.yml",
    "content": "spec:\n  - \"**/*.test.ts\"\nrequire:\n  - ts-node/register\n  - hooks.ts\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/mocha/example.test.ts",
    "content": "import 'mocha';\n\ndescribe('Tests', () => {});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/mocha/hooks.ts",
    "content": "export const mochaHooks = {\n  beforeAll: () => {},\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/mocha/package.json",
    "content": "{\n  \"name\": \"@plugins/mocha\",\n  \"mocha\": {\n    \"spec\": [\n      \"**/*.test.ts\"\n    ],\n    \"require\": [\n      \"ts-node/register\",\n      \"hooks.ts\"\n    ]\n  },\n  \"devDependencies\": {\n    \"@types/mocha\": \"*\",\n    \"mocha\": \"*\",\n    \"ts-node\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/moonrepo/.moon/tasks/typescript.yml",
    "content": "tasks:\n  lint:\n    deps:\n      - lint.typecheck\n      - lint.code\n  lint.typecheck:\n    command: tsc --noEmit\n  lint.code:\n    command: eslint . --ignore-path $workspaceRoot/.gitignore --no-error-on-unmatched-pattern\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/moonrepo/.moon/tasks.yml",
    "content": "tasks:\n  lint.filenames:\n    command: 'ls-lint -workdir . -config $workspaceRoot/.ls-lint.yml'"
  },
  {
    "path": "packages/knip/fixtures/plugins/moonrepo/apps/a/package.json",
    "content": "{\n  \"name\": \"@plugins/moonrepo__a\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/moonrepo/libs/b/package.json",
    "content": "{\n  \"name\": \"@plugins/moonrepo__b\",\n  \"devDependencies\": {\n    \"vitest\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/moonrepo/libs/b/server/server.ts",
    "content": "export const hi = 'hi';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/moonrepo/moon.yml",
    "content": "$schema: 'https://moonrepo.dev/schemas/project.json'\ntasks:\n  lint.readme:\n    command: 'vite-node $workspaceRoot/tools/linters/lint-readme.ts -- $projectRoot'\n  start.prod:\n    command: 'vitest ./server/server.ts'\n  build:\n    command:\n      - 'tsx'\n      - '$projectRoot/tools/build.ts'\n      - '--'\n      - '--verbose'\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/moonrepo/package.json",
    "content": "{\n  \"name\": \"@plugins/moonrepo\",\n  \"devDependencies\": {\n    \"@moonrepo/cli\": \"*\",\n    \"vite-node\": \"*\",\n    \"eslint\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/moonrepo/pnpm-workspace.yaml",
    "content": "packages:\n  - 'apps/*'\n  - 'libs/*'\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/moonrepo/tools/build.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/moonrepo/tools/linters/lint-readme.ts",
    "content": "export const hi = 'hello';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/moonrepo/tsconfig.json",
    "content": "{\n  \"extends\": [\"@tsconfig/node20/tsconfig.json\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/msw/bob/mockServiceWorker.js",
    "content": "/**\n * Mock Service Worker (2.2.0).\n * @see https://github.com/mswjs/msw\n * - Please do NOT modify this file.\n * - Please do NOT serve this file on production.\n */\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/msw/mocks/browser.ts",
    "content": "import './handlers';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/msw/mocks/handlers.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/msw/mocks/index.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/msw/mocks/server.ts",
    "content": "import './handlers';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/msw/package.json",
    "content": "{\n  \"name\": \"@plugins/msw\",\n  \"knip\": {\n    \"msw\": true\n  },\n  \"devDependencies\": {\n    \"msw\": \"*\"\n  },\n  \"msw\": {\n    \"workerDirectory\": \"bob\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nano-staged/.nano-staged.js",
    "content": "export default {\n  '*.(js|ts|tsx)': ['eslint --max-warnings=0 --cache --fix .', 'prettier --write .'],\n  '*.!(js|ts|tsx)': api => `prettier --write --ignore-unknown ${api.filenames.join(' ')}`,\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nano-staged/package.json",
    "content": "{\n  \"name\": \"@plugins/nano-staged\",\n  \"devDependencies\": {\n    \"nano-staged\": \"*\"\n  },\n  \"nano-staged\": {\n    \"*.js\": [\n      \"eslint\",\n      \"prettier --write\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nest/knip.json",
    "content": "{\n  \"jest\": {\n    \"config\": \"./test/jest-e2e.json\",\n    \"entry\": [\"src/**/*.spec.ts\", \"test/**/*.e2e-spec.ts\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nest/nest-cli.json",
    "content": "{\n  \"$schema\": \"https://json.schemastore.org/nest-cli\",\n  \"collection\": \"@nestjs/schematics\",\n  \"sourceRoot\": \"src\",\n  \"compilerOptions\": {\n    \"deleteOutDir\": true\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nest/package.json",
    "content": "{\n  \"name\": \"@plugins/nest\",\n  \"scripts\": {\n    \"start\": \"nest start\"\n  },\n  \"dependencies\": {\n    \"@nestjs/common\": \"*\",\n    \"@nestjs/core\": \"*\"\n  },\n  \"devDependencies\": {\n    \"@nestjs/cli\": \"*\",\n    \"@nestjs/schematics\": \"*\",\n    \"@nestjs/testing\": \"*\",\n    \"supertest\": \"*\",\n    \"ts-jest\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nest/src/app.controller.spec.ts",
    "content": "import { Test, TestingModule } from '@nestjs/testing';\nimport { AppController } from './app.controller';\nimport { AppService } from './app.service';\n\ndescribe('AppController', () => {\n  let appController: AppController;\n\n  beforeEach(async () => {\n    const app: TestingModule = await Test.createTestingModule({\n      controllers: [AppController],\n      providers: [AppService],\n    }).compile();\n\n    appController = app.get<AppController>(AppController);\n  });\n\n  describe('root', () => {\n    it('should return \"Hello World!\"', () => {\n      expect(appController.getHello()).toBe('Hello World!');\n    });\n  });\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nest/src/app.controller.ts",
    "content": "import { Controller, Get } from '@nestjs/common';\nimport { AppService } from './app.service';\n\n@Controller()\nexport class AppController {\n  constructor(private readonly appService: AppService) {}\n\n  @Get()\n  getHello(): string {\n    return this.appService.getHello();\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nest/src/app.module.ts",
    "content": "import { Module } from '@nestjs/common';\nimport { AppController } from './app.controller';\nimport { AppService } from './app.service';\n\n@Module({\n  imports: [],\n  controllers: [AppController],\n  providers: [AppService],\n})\nexport class AppModule {}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nest/src/app.service.ts",
    "content": "import { Injectable } from '@nestjs/common';\n\n@Injectable()\nexport class AppService {\n  getHello(): string {\n    return 'Hello World!';\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nest/src/main.ts",
    "content": "import { NestFactory } from '@nestjs/core';\nimport { AppModule } from './app.module';\n\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  await app.listen(3000);\n}\nbootstrap();\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nest/test/app.e2e-spec.ts",
    "content": "import { Test, TestingModule } from '@nestjs/testing';\nimport { INestApplication } from '@nestjs/common';\nimport * as request from 'supertest';\nimport { AppModule } from './../src/app.module';\n\ndescribe('AppController (e2e)', () => {\n  let app: INestApplication;\n\n  beforeEach(async () => {\n    const moduleFixture: TestingModule = await Test.createTestingModule({\n      imports: [AppModule],\n    }).compile();\n\n    app = moduleFixture.createNestApplication();\n    await app.init();\n  });\n\n  it('/ (GET)', () => {\n    return request(app.getHttpServer()).get('/').expect(200).expect('Hello World!');\n  });\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nest/test/jest-e2e.json",
    "content": "{\n  \"moduleFileExtensions\": [\"js\", \"json\", \"ts\"],\n  \"rootDir\": \".\",\n  \"testEnvironment\": \"node\",\n  \"testRegex\": \".e2e-spec.ts$\",\n  \"transform\": {\n    \"^.+\\\\.(t|j)s$\": \"ts-jest\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/netlify/netlify.toml",
    "content": "[[plugins]]\npackage = \"netlify-plugin-check-output-for-puppy-references\"\n  [plugins.inputs]\n  breeds = [\"pomeranian\", \"chihuahua\", \"bulldog\"]\n\n[functions]\n  directory = \"myfunctions/\"\n  node_bundler = \"esbuild\"\n  external_node_modules = [\"package-1\"]\n  included_files = [\"files/*.md\"]\n\n[functions.\"api_*\"]\n  external_node_modules = [\"package-2\"]\n  included_files = [\"!files/post-1.md\"]\n\n[functions.api_payment]\n  external_node_modules = [\"package-3\", \"package-4\"]\n  included_files = [\"!files/post-2.md\", \"package.json\", \"images/**\"]\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/netlify/package.json",
    "content": "{\n  \"name\": \"@plugins/netlify\",\n  \"devDependencies\": {\n    \"netlify-cli\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next/app/api/auth/route.ts",
    "content": "import { helloWorld } from './util';\n\nexport function GET() {\n  helloWorld();\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next/app/api/auth/util.ts",
    "content": "export function helloWorld() {}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next/app/home/page.tsx",
    "content": "import React from 'react';\n\nexport default () => <main />;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next/app/home/sitemap.ts",
    "content": "export default function sitemap() {\n  return [];\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next/app/layout.tsx",
    "content": "import React from 'react';\n\nexport default () => <main />;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next/app/sitemap.ts",
    "content": "export default function sitemap() {\n  return [];\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next/app/unused.ts",
    "content": "export function unused() {}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next/instrumentation.ts",
    "content": "export function register() {}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next/middleware.ts",
    "content": "export function middleware() {}\n\nexport const config = {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next/next.config.js",
    "content": "const { PHASE_DEVELOPMENT_SERVER } = require('next/constants');\n\nconst withTM = require('next-transpile-modules')([]);\n\nmodule.exports = phase => {\n  const config = withTM({});\n  return {\n    pageExtensions: ['ts', 'tsx'],\n    ...config,\n  };\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next/package.json",
    "content": "{\n  \"name\": \"@plugins/next\",\n  \"scripts\": {\n    \"dev\": \"next dev\"\n  },\n  \"devDependencies\": {\n    \"next\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next/pages/[[...route]].tsx",
    "content": "import React from 'react';\nimport { Helmet } from 'react-helmet';\n\nexport default () => <main />;\nexport const getServerSideProps = () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next/pages/home.tsx",
    "content": "import Head from 'next/head';\nimport React from 'react';\n\nexport default () => <main />;\nexport const getServerSideProps = () => ({});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next/pages/unused.jsx",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/next-intl/i18n/request.ts",
    "content": "import { getRequestConfig } from 'next-intl/server';\n\nexport default getRequestConfig(async () => {\n  const locale = 'en';\n  return {\n    locale,\n    messages: (await import(`../../messages/${locale}.json`)).default,\n  };\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next-intl/package.json",
    "content": "{\n  \"name\": \"@plugins/next-intl\",\n  \"dependencies\": {\n    \"next-intl\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next-mdx/mdx-components.tsx",
    "content": "export function useMDXComponents(components) {\n  return {\n    ...components,\n  };\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next-mdx/next.config.js",
    "content": "const createMDX = require('@next/mdx');\n\nconst withMDX = createMDX({\n  options: {\n    remarkPlugins: ['remark-frontmatter', ['remark-mdx-frontmatter', { name: 'metadata' }]],\n    rehypePlugins: [['rehype-starry-night']],\n    recmaPlugins: ['recma-export-filepath'],\n  },\n});\n\nmodule.exports = withMDX({\n  pageExtensions: ['ts', 'tsx', 'mdx'],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next-mdx/package.json",
    "content": "{\n  \"name\": \"@plugins/next-mdx\",\n  \"dependencies\": {\n    \"@next/mdx\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next-middleware/next.config.js",
    "content": "/** @type {import('next').NextConfig} */\nconst nextConfig = {};\n\nmodule.exports = nextConfig;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next-middleware/package.json",
    "content": "{\n  \"name\": \"@plugins/next-middleware\",\n  \"dependencies\": {\n    \"next\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next-middleware/src/app/page.tsx",
    "content": "export default function Page() {\n  return <div>Page</div>;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next-middleware/src/middleware.ts",
    "content": "export function middleware() {\n  //\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next-middleware/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"jsx\": \"react-jsx\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next-page-extensions/next.config.js",
    "content": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n  pageExtensions: ['page.ts', 'page.tsx', 'api.ts'],\n};\n\nmodule.exports = nextConfig;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next-page-extensions/package.json",
    "content": "{\n  \"name\": \"@plugins/next-page-extensions\",\n  \"dependencies\": {\n    \"next\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next-page-extensions/src/instrumentation.page.ts",
    "content": "export async function register() {}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next-page-extensions/src/middleware.page.ts",
    "content": "export function middleware() {}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/next-page-extensions/src/unused.ts",
    "content": "export const unused = true;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nitro/.nitro/types/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"strict\": false,\n    \"noEmit\": true,\n    \"skipLibCheck\": true,\n    \"target\": \"ESNext\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Bundler\",\n    \"paths\": {\n      \"#imports\": [\"./nitro-imports\"]\n    }\n  },\n  \"include\": [\"./nitro.d.ts\", \"../../**/*\", \"../../server/**/*\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nitro/nitro.config.ts",
    "content": "import { defineNitroConfig } from 'nitropack/config';\n\n// https://nitro.build/config\nexport default defineNitroConfig({\n  compatibilityDate: '2025-12-13',\n  srcDir: 'server',\n  imports: false,\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nitro/package.json",
    "content": "{\n  \"name\": \"@plugins/nitro\",\n  \"scripts\": {\n    \"build\": \"nitro build\",\n    \"dev\": \"nitro dev\",\n    \"preview\": \"node .output/server/index.mjs\",\n    \"postinstall\": \"nitro prepare\"\n  },\n  \"devDependencies\": {\n    \"h3\": \"*\",\n    \"nitropack\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nitro/server/routes/index.ts",
    "content": "import { eventHandler } from 'h3';\n\nimport { fn } from '../utils/fn';\n\nexport default eventHandler(event => {\n  fn();\n\n  return `\n      <meta charset=\"utf-8\">\n      <h1>This is your brand new Nitro project 🚀 </h1>\n      <p>Get started by editing the <code>server/routes/index.ts</code> file.</p>\n      <p>Learn more from 📖 <a href=\"https://nitro.build/guide\" target=\"_blank\">Nitro Documentation</a></p>\n    `;\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nitro/server/utils/fn.ts",
    "content": "export const fn = () => null;\n\nexport const unused = () => null;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nitro/tsconfig.json",
    "content": "{\n  \"extends\": \"./.nitro/types/tsconfig.json\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/node/package.json",
    "content": "{\n  \"name\": \"@plugins/node\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/node-modules-inspector/.nmrc.js",
    "content": "import { defineConfig } from 'node-modules-inspector';\n\nexport default defineConfig({\n  defaultSettings: {\n    moduleTypeSimple: true,\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/node-modules-inspector/node-modules-inspector.config.ts",
    "content": "import { defineConfig } from 'node-modules-inspector';\n\nexport default defineConfig({\n  // Experimental publint.dev integration\n  publint: true,\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/node-modules-inspector/package.json",
    "content": "{\n  \"name\": \"@plugins/node-modules-inspector\",\n  \"dependencies\": {\n    \"node-modules-inspector\": \"*\"\n  },\n  \"scripts\": {\n    \"build\": \"node-modules-inspector build\",\n    \"build-3\": \"node-modules-inspector --config .nmrc.js\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/node-test-runner/index.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/node-test-runner/index.test.js",
    "content": "import test from 'node:test';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/node-test-runner/package.json",
    "content": "{\n  \"name\": \"@plugins/node-test-runner\",\n  \"scripts\": {\n    \"test\": \"node --test\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nodemon/package.json",
    "content": "{\n  \"name\": \"@plugins/nodemon\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/npm-package-json-lint/.npmpackagejsonlintrc.json",
    "content": "{\n  \"extends\": \"npm-package-json-lint-config-default\",\n  \"rules\": {}\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/npm-package-json-lint/package.json",
    "content": "{\n  \"name\": \"@plugins/npm-package-json-lint\",\n  \"devDependencies\": {\n    \"npm-package-json-lint\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt/.gitignore",
    "content": ".nuxt\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt/app.tsx",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt/app.vue",
    "content": "<script setup lang=\"ts\">\nimport { fn } from '~/utils/fn';\nfn();\n</script>\n\n<template>\n  <div>\n    <NuxtRouteAnnouncer />\n    <NuxtWelcome />\n  </div>\n</template>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt/nuxt.config.ts",
    "content": "import { defineNuxtConfig } from 'nuxt/config';\n\n// https://nuxt.com/docs/api/configuration/nuxt-config\nexport default defineNuxtConfig({\n  compatibilityDate: '2024-04-03',\n  devtools: { enabled: true },\n  modules: ['@nuxt/eslint', '@pinia/nuxt'],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt/package.json",
    "content": "{\n  \"name\": \"@plugins/nuxt\",\n  \"scripts\": {\n    \"build\": \"nuxt build\",\n    \"dev\": \"nuxt dev\",\n    \"generate\": \"nuxt generate\",\n    \"preview\": \"nuxt preview\",\n    \"postinstall\": \"nuxt prepare\"\n  },\n  \"dependencies\": {\n    \"nuxt\": \"*\",\n    \"vue\": \"*\",\n    \"@pinia/nuxt\": \"*\",\n    \"@nuxt/eslint\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt/server/api/health.get.ts",
    "content": "export default defineEventHandler((): ApiResponse => {\n  const db = getDb();\n  return { data: capitalize(db.connected ? 'ok' : 'fail'), status: 200 };\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt/server/utils/db.ts",
    "content": "export const getDb = () => ({ connected: true });\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt/shared/types/api.ts",
    "content": "export interface ApiResponse {\n  data: unknown;\n  status: number;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt/shared/utils/capitalize.ts",
    "content": "export const capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1);\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt/tsconfig.json",
    "content": "{\n  \"extends\": \"./.nuxt/tsconfig.json\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt/utils/fn.ts",
    "content": "export const fn = () => null;\n\nexport const unused = () => null;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-auto-import/.gitignore",
    "content": ".nuxt\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-auto-import/app.vue",
    "content": "<script setup lang=\"ts\">\nconst { count } = useCounter();\nconst date = ref(formatDate(new Date()));\n</script>\n\n<template>\n  <div>\n    <AppHeader :title=\"date\" />\n    {{ count }}\n  </div>\n</template>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-auto-import/components/AppHeader.vue",
    "content": "<template>\n  <header>{{ title }}</header>\n</template>\n\n<script setup lang=\"ts\">\ndefineProps<{ title: string }>();\n</script>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-auto-import/components/StatusBadge.vue",
    "content": "<template>\n  <span>{{ label }}</span>\n</template>\n\n<script setup lang=\"ts\">\ndefineProps<{ label: string }>();\n</script>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-auto-import/composables/useCounter.ts",
    "content": "export const useCounter = () => ({ count: 0 });\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-auto-import/composables/useTheme.ts",
    "content": "export const useTheme = () => ({ theme: 'light' });\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-auto-import/nuxt.config.ts",
    "content": "import { defineNuxtConfig } from 'nuxt/config';\n\nexport default defineNuxtConfig({\n  modules: ['@pinia/nuxt'],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-auto-import/package.json",
    "content": "{\n  \"name\": \"@plugins/nuxt-auto-import\",\n  \"dependencies\": {\n    \"nuxt\": \"*\",\n    \"vue\": \"*\",\n    \"@pinia/nuxt\": \"*\",\n    \"@vueuse/nuxt\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-auto-import/tsconfig.json",
    "content": "{\n  \"extends\": \"./.nuxt/tsconfig.json\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-auto-import/utils/format.ts",
    "content": "export const formatDate = (d: Date) => d.toISOString();\n\nexport const formatNumber = (n: number) => n.toLocaleString();\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-auto-import-disabled/.gitignore",
    "content": ".nuxt\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-auto-import-disabled/app.vue",
    "content": "<script setup lang=\"ts\">\nimport { ref } from 'vue';\nimport { useCounter } from '~/composables/useCounter';\nimport { formatDate } from '~/utils/format';\n\nconst { count } = useCounter();\nconst date = ref(formatDate(new Date()));\n</script>\n\n<template>\n  <div>\n    <AppHeader :title=\"date\" />\n    {{ count }}\n  </div>\n</template>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-auto-import-disabled/components/AppHeader.vue",
    "content": "<template>\n  <header>{{ title }}</header>\n</template>\n\n<script setup lang=\"ts\">\ndefineProps<{ title: string }>();\n</script>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-auto-import-disabled/components/StatusBadge.vue",
    "content": "<template>\n  <span>{{ label }}</span>\n</template>\n\n<script setup lang=\"ts\">\ndefineProps<{ label: string }>();\n</script>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-auto-import-disabled/composables/useCounter.ts",
    "content": "export const useCounter = () => ({ count: 0 });\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-auto-import-disabled/composables/useTheme.ts",
    "content": "export const useTheme = () => ({ theme: 'light' });\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-auto-import-disabled/nuxt.config.ts",
    "content": "import { defineNuxtConfig } from 'nuxt/config';\n\nexport default defineNuxtConfig({\n  modules: ['@pinia/nuxt'],\n  imports: { autoImport: false },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-auto-import-disabled/package.json",
    "content": "{\n  \"name\": \"@plugins/nuxt-auto-import-disabled\",\n  \"dependencies\": {\n    \"nuxt\": \"*\",\n    \"vue\": \"*\",\n    \"@pinia/nuxt\": \"*\",\n    \"@vueuse/nuxt\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-auto-import-disabled/tsconfig.json",
    "content": "{\n  \"extends\": \"./.nuxt/tsconfig.json\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-auto-import-disabled/utils/format.ts",
    "content": "export const formatDate = (d: Date) => d.toISOString();\n\nexport const formatNumber = (n: number) => n.toLocaleString();\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-config/.gitignore",
    "content": ".nuxt\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-config/app.vue",
    "content": "<script setup lang=\"ts\">\nlogInfo('mounted');\n</script>\n\n<template>\n  <div>\n    <Button />\n    <Card />\n  </div>\n</template>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-config/components/Button.vue",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-config/custom-utils/logger.ts",
    "content": "export const logInfo = (msg: string) => console.log(msg);\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-config/local-module/index.ts",
    "content": "export default {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-config/my-plugins/auth.ts",
    "content": "export default defineNuxtPlugin(() => {});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-config/nuxt.config.ts",
    "content": "export default defineNuxtConfig({\n  extends: ['~~/some-layer', 'a-nuxt-theme'],\n  modules: ['a-module', '~~/local-module'],\n  dir: {\n    plugins: 'my-plugins',\n  },\n  components: ['~/components', '~/other-components'],\n  imports: {\n    dirs: ['custom-utils'],\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-config/other-components/Card.vue",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-config/package.json",
    "content": "{\n  \"name\": \"@plugins/nuxt-config\",\n  \"scripts\": {\n    \"build\": \"nuxt build\"\n  },\n  \"dependencies\": {\n    \"nuxt\": \"*\",\n    \"a-module\": \"*\",\n    \"a-nuxt-theme\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-config/some-layer/nuxt.config.ts",
    "content": "export default defineNuxtConfig({});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nuxt-config/tsconfig.json",
    "content": "{\n  \"extends\": \"./.nuxt/tsconfig.json\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nx/apps/a/project.json",
    "content": "{\n  \"sourceRoot\": \"apps/a\",\n  \"projectType\": \"application\",\n  \"targets\": {\n    \"build\": {\n      \"executor\": \"@nrwl/next:build\"\n    },\n    \"serve\": {\n      \"executor\": \"@nrwl/next:server\"\n    },\n    \"lint\": {\n      \"executor\": \"@nrwl/linter:eslint\"\n    },\n    \"test\": {\n      \"executor\": \"@nrwl/cypress:cypress\"\n    },\n    \"tsc\": {\n      \"executor\": \"./tools/executors/tsc:tsc\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nx/apps/a/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.base.json\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nx/apps/b/project.json",
    "content": "{\n  \"sourceRoot\": \"apps/b\",\n  \"projectType\": \"application\",\n  \"targets\": {\n    \"build\": {\n      \"executor\": \"@nx/next:build\"\n    },\n    \"serve\": {\n      \"executor\": \"@nx/next:server\"\n    },\n    \"lint\": {\n      \"executor\": \"@nx/linter:eslint\"\n    },\n    \"test\": {\n      \"executor\": \"@js/cypress:cypress\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nx/apps/b/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.base.json\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nx/knip.json",
    "content": "{\n  \"typescript\": {\n    \"config\": [\"{apps,libs}/**/tsconfig.json\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nx/libs/b/.eslintrc.json",
    "content": "{\n  \"extends\": [\"../../.eslintrc.json\"],\n  \"ignorePatterns\": [\"!**/*\"],\n  \"overrides\": [\n    {\n      \"files\": [\"*.ts\", \"*.tsx\", \"*.js\", \"*.jsx\"],\n      \"rules\": {}\n    }\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nx/libs/b/jest.config.ts",
    "content": "export default {\n  displayName: 'b',\n  preset: '../../jest.preset.js',\n  testEnvironment: 'node',\n  transform: {\n    '^.+\\\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],\n  },\n  moduleFileExtensions: ['ts', 'js', 'html'],\n  coverageDirectory: '../../coverage/libs/b',\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nx/libs/b/project.json",
    "content": "{\n  \"sourceRoot\": \"libs/b\",\n  \"projectType\": \"application\",\n  \"targets\": {\n    \"ls-project-root\": {\n      \"executor\": \"nx:run-commands\",\n      \"options\": {\n        \"commands\": [\"ls apps/frontend/src\"]\n      }\n    },\n    \"webpack\": {\n      \"executor\": \"nx:run-commands\",\n      \"options\": {\n        \"command\": \"webpack\"\n      }\n    },\n    \"generate-docs\": {\n      \"executor\": \"nx:run-commands\",\n      \"options\": {\n        \"command\": \"npx -- compodoc -p apps/frontend/tsconfig.app.json\"\n      }\n    },\n    \"test\": {\n      \"executor\": \"@nrwl/jest:jest\",\n      \"options\": {\n        \"jestConfig\": \"libs/b/jest.config.ts\"\n      }\n    },\n    \"tsc\": {\n      \"executor\": \"./tools/executors/tsc:tsc\",\n      \"options\": {\n        \"tsConfig\": \"libs/b/tsconfig.lib.json\"\n      }\n    },\n    \"lint\": {\n      \"executor\": \"nx:run-commands\",\n      \"options\": {\n        \"commands\": [{ \"command\": \"biome lint .\" }]\n      }\n    },\n    \"eslint\": {\n      \"executor\": \"@nx/linter:eslint\",\n      \"options\": {\n        \"eslintConfig\": \"libs/b/.eslintrc.json\"\n      }\n    },\n    \"test-vitest\": {\n      \"executor\": \"@nx/vitest:test\",\n      \"options\": {\n        \"vitestConfig\": \"libs/b/vitest.config.ts\"\n      }\n    },\n    \"build-webpack\": {\n      \"executor\": \"@nx/webpack:webpack\",\n      \"options\": {\n        \"webpackConfig\": \"libs/b/webpack.config.js\"\n      }\n    },\n    \"build-from-subdir\": {\n      \"executor\": \"nx:run-commands\",\n      \"options\": {\n        \"command\": \"tsc -p tsconfig.build.json\",\n        \"cwd\": \"libs/b\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nx/libs/b/tsconfig.build.json",
    "content": "{ \"extends\": \"@tsconfig/strictest/tsconfig.json\" }\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nx/libs/b/tsconfig.lib.json",
    "content": "{\n  \"extends\": \"../../tsconfig.base.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"../../dist/out-tsc\",\n    \"declaration\": true,\n    \"types\": [\"node\"]\n  },\n  \"include\": [\"src/**/*.ts\"],\n  \"exclude\": [\"jest.config.ts\", \"src/**/*.spec.ts\", \"src/**/*.test.ts\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nx/libs/b/vitest.config.ts",
    "content": "import { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n  test: {\n    globals: true,\n    environment: 'node',\n    coverage: {\n      provider: 'v8',\n      reporter: ['text', 'json', 'html'],\n    },\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nx/libs/b/webpack.config.js",
    "content": "const { composePlugins, withNx } = require('@nx/webpack');\n\nmodule.exports = composePlugins(withNx(), config => {\n  return config;\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nx/package.json",
    "content": "{\n  \"name\": \"@plugins/nx\",\n  \"scripts\": {\n    \"nx\": \"nx\"\n  },\n  \"devDependencies\": {\n    \"@nx/next\": \"*\",\n    \"@nx/linter\": \"*\",\n    \"@nx/cypress\": \"*\",\n    \"@nrwl/cypress\": \"*\",\n    \"@nrwl/devkit\": \"*\",\n    \"@nrwl/jest\": \"*\",\n    \"@nrwl/linter\": \"*\",\n    \"@nrwl/next\": \"*\",\n    \"@nrwl/storybook\": \"*\",\n    \"@nrwl/web\": \"*\",\n    \"@nrwl/workspace\": \"*\",\n    \"@tsconfig/node20\": \"*\",\n    \"@tsconfig/strictest\": \"*\",\n    \"rimraf\": \"*\"\n  },\n  \"nx\": {\n    \"targets\": {\n      \"clean\": {\n        \"executor\": \"nx:run-commands\",\n        \"options\": {\n          \"command\": \"rimraf dist\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nx/tsconfig.base.json",
    "content": "{\n  \"extends\": [\"@tsconfig/node20/tsconfig.json\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nx-crystal/nx.json",
    "content": "{\n  \"plugins\": [\n    {\n      \"plugin\": \"@nx/webpack/plugin\",\n      \"options\": {\n        \"buildTargetName\": \"build\",\n        \"serveTargetName\": \"serve\",\n        \"previewTargetName\": \"preview\"\n      }\n    },\n    {\n      \"plugin\": \"@nx/eslint/plugin\",\n      \"options\": {\n        \"targetName\": \"lint\"\n      }\n    },\n    {\n      \"plugin\": \"@nx/playwright/plugin\",\n      \"options\": {\n        \"targetName\": \"e2e\"\n      }\n    },\n    {\n      \"plugin\": \"@nx/jest/plugin\",\n      \"options\": {\n        \"targetName\": \"test\"\n      }\n    },\n    {\n      \"plugin\": \"@nx/vite/plugin\",\n      \"options\": {\n        \"buildTargetName\": \"build\",\n        \"previewTargetName\": \"preview\",\n        \"testTargetName\": \"test\",\n        \"serveTargetName\": \"serve\",\n        \"serveStaticTargetName\": \"serve-static\"\n      }\n    },\n    \"@nx/nuxt/plugin\"\n  ],\n  \"generators\": {\n    \"@nx/react\": {\n      \"application\": {\n        \"babel\": true,\n        \"style\": \"css\",\n        \"linter\": \"eslint\",\n        \"bundler\": \"webpack\"\n      },\n      \"component\": {\n        \"style\": \"css\"\n      },\n      \"library\": {\n        \"style\": \"css\",\n        \"linter\": \"eslint\",\n        \"unitTestRunner\": \"vitest\"\n      }\n    }\n  },\n  \"targetDefaults\": {\n    \"@nx/rollup:rollup\": {\n      \"cache\": true,\n      \"dependsOn\": [\"^build\"],\n      \"inputs\": [\"production\", \"^production\"]\n    },\n    \"my-custom-thing\": {\n      \"cache\": true,\n      \"dependsOn\": [\"^my-custom-thing\"],\n      \"inputs\": [\"production\", \"^production\"]\n    },\n    \"test:custom-thing\": {\n      \"cache\": true,\n      \"dependsOn\": [\"^my-custom-thing\"],\n      \"inputs\": [\"production\", \"^production\"]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nx-crystal/package.json",
    "content": "{\n  \"name\": \"@plugins/nx-crystal\",\n  \"scripts\": {\n    \"nx\": \"nx\"\n  },\n  \"devDependencies\": {\n    \"@nx/webpack\": \"*\",\n    \"@nx/eslint\": \"*\",\n    \"@nx/playwright\": \"*\",\n    \"@nx/jest\": \"*\",\n    \"@nx/vite\": \"*\",\n    \"@nx/cypress\": \"*\",\n    \"@nx/react\": \"*\",\n    \"@nx/rollup\": \"*\",\n    \"@nrwl/workspace\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nyc/.nycrc.json",
    "content": "{\n  \"extends\": \"@istanbuljs/nyc-config-typescript\",\n  \"all\": true,\n  \"check-coverage\": true\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/nyc/package.json",
    "content": "{\n  \"name\": \"@plugins/nyc\",\n  \"devDependencies\": {\n    \"nyc\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/oclif/package.json",
    "content": "{\n  \"name\": \"@plugins/oclif\",\n  \"scripts\": {\n    \"start\": \"oclif\"\n  },\n  \"dependencies\": {\n    \"oclif\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/openapi-ts/openapi-ts.config.ts",
    "content": "export default {\n  input: 'https://api.example.com/openapi.json',\n  output: 'src/client',\n  plugins: [\n    '@hey-api/typescript',\n    '@hey-api/client-fetch',\n    {\n      name: '@hey-api/sdk',\n    },\n  ],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/openapi-ts/package.json",
    "content": "{\n  \"name\": \"@plugins/openapi-ts\",\n  \"devDependencies\": {\n    \"@hey-api/openapi-ts\": \"*\"\n  },\n  \"scripts\": {\n    \"generate\": \"openapi-ts\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/oxfmt/.oxfmtrc-1.jsonc",
    "content": "{}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/oxfmt/.oxfmtrc.json",
    "content": "{}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/oxfmt/.oxfmtrc.jsonc",
    "content": "{}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/oxfmt/package.json",
    "content": "{\n  \"name\": \"@plugins/oxfmt\",\n  \"private\": true,\n  \"scripts\": {\n    \"format\": \"oxfmt\",\n    \"format:custom\": \"oxfmt --config .oxfmtrc-1.jsonc\"\n  },\n  \"devDependencies\": {\n    \"oxfmt\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/oxlint/.oxlintrc-0.json",
    "content": "{}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/oxlint/.oxlintrc-3.json",
    "content": "{}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/oxlint/.oxlintrc-4.json",
    "content": "{}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/oxlint/oxlint-5.config.ts",
    "content": "import { defineConfig } from \"oxlint\";\n\nexport default defineConfig({});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/oxlint/package.json",
    "content": "{\n  \"name\": \"@plugins/oxlint\",\n  \"devDependencies\": {\n    \"oxlint\": \"*\"\n  },\n  \"scripts\": {\n    \"build\": \"oxlint --config .oxlintrc-0.json\",\n    \"build-3\": \"oxlint --config .oxlintrc-3.json\",\n    \"build-4\": \"oxlint -c .oxlintrc-4.json\",\n    \"build-5\": \"oxlint --config oxlint-5.config.ts\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/parcel/.parcelrc",
    "content": "{\n  \"extends\": \"@parcel/config-default\",\n  \"transformers\": {\n    \"*.{js,mjs,jsx,cjs,ts,tsx}\": [\n      \"@parcel/transformer-js\",\n      \"@parcel/transformer-react-refresh-wrap\"\n    ],\n    \"*.svg\": [\"@parcel/transformer-svg-react\"]\n  },\n  \"optimizers\": {\n    \"*.{js,mjs,cjs}\": [\"@parcel/optimizer-terser\"],\n    \"*.css\": [\"@parcel/optimizer-cssnano\"]\n  },\n  \"reporters\": [\"@parcel/reporter-cli\", \"@parcel/reporter-bundle-analyzer\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/parcel/package.json",
    "content": "{\n  \"name\": \"@plugins/parcel\",\n  \"scripts\": {\n    \"start\": \"parcel src/index.html\",\n    \"build\": \"parcel build src/index.html\"\n  },\n  \"devDependencies\": {\n    \"parcel\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/payload/migrations/20260218.ts",
    "content": "import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres';\n\nexport async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {\n  await db.execute(sql``);\n}\n\nexport async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {\n  await db.execute(sql``);\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/payload/package.json",
    "content": "{\n  \"name\": \"@plugins/payload\",\n  \"dependencies\": {\n    \"payload\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/payload/payload-types.ts",
    "content": "import type { Config } from 'payload';\n\ndeclare module 'payload' {\n  export interface GeneratedTypes extends Config {}\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/payload/payload.config.ts",
    "content": "import type { Config } from 'payload';\n\nconst buildConfig = async (config: Config) => config;\n\nexport default buildConfig({\n  admin: {\n    importMap: {\n      importMapFile: 'src/app/(payload)/importMap.js',\n    },\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/payload/src/app/(payload)/importMap.js",
    "content": "import { ImportMapComponent as ImportMapComponent_ref } from '../../components/ImportMapComponent.tsx';\n\nexport const importMap = {\n  '/components/ImportMapComponent.tsx#ImportMapComponent': ImportMapComponent_ref,\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/payload/src/components/ImportMapComponent.tsx",
    "content": "export const ImportMapComponent = () => null;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/playwright/custom-reporter.ts",
    "content": "import type { Reporter } from '@playwright/test/reporter';\nimport Table from 'table';\n\nexport default class implements Reporter {}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/playwright/package.json",
    "content": "{\n  \"name\": \"@plugins/playwright\",\n  \"scripts\": {\n    \"test\": \"playwright test\"\n  },\n  \"devDependencies\": {\n    \"@playwright/test\": \"*\",\n    \"table\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/playwright/playwright.config.ts",
    "content": "import { devices } from '@playwright/test';\nimport type { PlaywrightTestConfig } from '@playwright/test';\n\nconst config: PlaywrightTestConfig = {\n  projects: [\n    {\n      name: 'chromium',\n      use: devices['Desktop Chrome'],\n    },\n  ],\n  reporter: ['html', ['junit', { outputFile: '' }], ['./custom-reporter.ts']],\n};\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/playwright/test/some.spec.ts",
    "content": "import { test, expect } from '@playwright/test';\n\ntest.describe('stuff', () => {\n  test('thing', async () => {\n    expect(null).toMatch(null);\n  });\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/playwright-ct/package.json",
    "content": "{\n  \"name\": \"@plugins/playwright-ct\",\n  \"scripts\": {\n    \"test-ct\": \"playwright test -c playwright-ct.config.ts\"\n  },\n  \"devDependencies\": {\n    \"@playwright/experimental-ct-react\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/playwright-ct/playwright/common.css",
    "content": ".common {\n  color: red;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/playwright-ct/playwright/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Testing Page</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"./index.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/playwright-ct/playwright/index.tsx",
    "content": "// Import styles, initialize component theme here.\nimport './common.css';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/playwright-ct/playwright-ct.config.ts",
    "content": "import { defineConfig, devices } from '@playwright/experimental-ct-react';\n\nexport default defineConfig({\n  projects: [\n    {\n      name: 'chromium',\n      use: devices['Desktop Chrome'],\n    },\n  ],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/playwright-ct/test/some.spec.ts",
    "content": "import { test, expect } from '@playwright/experimental-ct-react';\n\ntest.describe('stuff', () => {\n  test('thing', async () => {\n    expect(null).toMatch(null);\n  });\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/playwright-ct2/package.json",
    "content": "{\n  \"name\": \"@plugins/playwright-ct2\",\n  \"knip\": {\n    \"playwright-ct\": true\n  },\n  \"devDependencies\": {\n    \"@playwright/experimental-ct-vue\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/playwright-ct2/playwright-ct.config.ts",
    "content": "const defineConfig = c => c;\n\nexport default defineConfig({\n  testDir: './tests/component',\n  testMatch: ['**/*.spec.ts'],\n  projects: [\n    {\n      name: 'chromium',\n    },\n  ],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/playwright-ct2/src/unused.spec.ts",
    "content": "import { defineConfig, devices } from '@playwright/experimental-ct-vue';\n\ntest.describe('stuff', () => {\n  test('thing', async () => {\n    expect(null).toMatch(null);\n  });\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/playwright-ct2/tests/component/some.spec.ts",
    "content": "import { defineConfig, devices } from '@playwright/experimental-ct-vue';\n\ntest.describe('stuff', () => {\n  test('thing', async () => {\n    expect(null).toMatch(null);\n  });\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/playwright2/integration/some-test.ts",
    "content": "import { test, expect } from '@playwright/test';\n\ntest.describe('stuff', () => {\n  test('thing', async () => {\n    expect(null).toMatch(null);\n  });\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/playwright2/package.json",
    "content": "{\n  \"name\": \"@plugins/playwright2\",\n  \"knip\": {\n    \"playwright\": true\n  },\n  \"devDependencies\": {\n    \"@playwright/test\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/playwright2/playwright.config.ts",
    "content": "import type { PlaywrightTestConfig } from '@playwright/test';\nimport { devices } from '@playwright/test';\n\nconst config: PlaywrightTestConfig = {\n  testDir: '.',\n  testMatch: ['**/*-test.ts'],\n  projects: [\n    {\n      name: 'chromium',\n      use: devices['Desktop Chrome'],\n    },\n    {\n      name: 'webkit',\n      use: devices['Desktop Safari'],\n    },\n  ],\n};\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/plop/package.json",
    "content": "{\n  \"name\": \"@plugins/plop\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"plop\": \"plop\"\n  },\n  \"dependencies\": {\n    \"plop\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/plop/plopfile.js",
    "content": "const page = {\n  description: 'Create a documentation page',\n\n  prompts: [\n    {\n      type: 'input',\n      name: 'name',\n      message: 'What is the page name',\n    },\n  ],\n\n  actions(prompts) {\n    return [\n      {\n        type: 'add',\n        path: './doc/{{ dashCase name }}.md',\n        templateFile: 'template.hbs',\n      },\n    ];\n  },\n};\n\nfunction plopConfig(plop) {\n  plop.setGenerator('Page', page);\n}\n\nexport default plopConfig;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/plop/template.hbs",
    "content": "# {{ name }}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/pm2/package.json",
    "content": "{\n  \"name\": \"@plugins/pm2\",\n  \"scripts\": {\n    \"start\": \"pm2 start pm2.config.json\"\n  },\n  \"devDependencies\": {\n    \"pm2\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/pm2/pm2.config.json",
    "content": "{\n  \"apps\": [\n    {\n      \"name\": \"api\",\n      \"script\": \"./src/index.js\"\n    },\n    {\n      \"name\": \"another\",\n      \"script\": \"./src/another.js\"\n    }\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/pm2/src/another.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/pm2/src/index.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/pm2/src/unused.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/pm2-ecosystem/ecosystem.config.json",
    "content": "{\n  \"apps\": [\n    {\n      \"name\": \"api\",\n      \"script\": \"./src/index.js\"\n    },\n    {\n      \"name\": \"another\",\n      \"script\": \"./src/another.js\"\n    }\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/pm2-ecosystem/package.json",
    "content": "{\n  \"name\": \"@plugins/pm2-ecosystem\",\n  \"scripts\": {\n    \"start\": \"pm2 start ecosystem.config.json\"\n  },\n  \"devDependencies\": {\n    \"pm2\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/pm2-ecosystem/src/another.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/pm2-ecosystem/src/index.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/pm2-ecosystem/src/unused.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/pnpm/.pnpmfile.cjs",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/pnpm/package.json",
    "content": "{\n  \"packageManager\": \"pnpm@10.14.0\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/pnpm2/.pnpmfile.cjs",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/pnpm2/package.json",
    "content": "{\n  \"name\": \"@plugins/pnpm2\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/pnpm2/pnpm-workspace.yaml",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/postcss/package.json",
    "content": "{\n  \"name\": \"@plugins/postcss\",\n  \"scripts\": {\n    \"postcss\": \"postcss\"\n  },\n  \"devDependencies\": {\n    \"postcss\": \"*\"\n  },\n  \"postcss\": {\n    \"plugins\": [\n      \"autoprefixer\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/postcss/postcss.config.js",
    "content": "module.exports = {\n  plugins: [require('autoprefixer')],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/postcss-cjs/package.json",
    "content": "{\n  \"name\": \"@plugins/postcss-cjs\",\n  \"scripts\": {\n    \"postcss\": \"postcss\"\n  },\n  \"devDependencies\": {\n    \"postcss\": \"*\"\n  },\n  \"postcss\": {\n    \"plugins\": [\n      \"autoprefixer\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/postcss-cjs/postcss.config.cjs",
    "content": "module.exports = {\n  plugins: [require('autoprefixer')],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/postcss-next/package.json",
    "content": "{\n  \"name\": \"@plugins/postcss-next\",\n  \"scripts\": {\n    \"start\": \"next\"\n  },\n  \"dependencies\": {\n    \"next\": \"*\"\n  },\n  \"devDependencies\": {\n    \"postcss-rtlcss\": \"*\"\n  },\n  \"postcss\": {\n    \"plugins\": [\n      \"autoprefixer\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/postcss-next/postcss.config.json",
    "content": "{\n  \"plugins\": [\n    \"autoprefixer\",\n    [\n      \"postcss-rtlcss\",\n      {\n        \"safeBothPrefix\": true\n      }\n    ]\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/postcss-tailwindcss/package.json",
    "content": "{\n  \"name\": \"@plugins/postcss-tailwindcss\",\n  \"devDependencies\": {\n    \"postcss\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/postcss-tailwindcss/postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/postcss-tailwindcss2/package.json",
    "content": "{\n  \"name\": \"@plugins/postcss-tailwindcss2\",\n  \"devDependencies\": {\n    \"postcss\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/postcss-tailwindcss2/postcss.config.mjs",
    "content": "const config = {\n  plugins: ['@tailwindcss/postcss'],\n};\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/preconstruct/package.json",
    "content": "{\n  \"name\": \"@plugins/preconstruct\",\n  \"scripts\": {\n    \"build\": \"preconstruct build\"\n  },\n  \"devDependencies\": {\n    \"@preconstruct/cli\": \"*\"\n  },\n  \"preconstruct\": {\n    \"entrypoints\": [\n      \"index.js\",\n      \"other.js\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/preconstruct/src/index.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/preconstruct/src/other.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/prettier/package.json",
    "content": "{\n  \"name\": \"@plugins/prettier\",\n  \"type\": \"module\",\n  \"devDependencies\": {\n    \"prettier\": \"*\"\n  },\n  \"prettier\": \"@company/prettier-config\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/prettier/prettier.config.js",
    "content": "export default {\n  plugins: ['prettier-plugin-xml', import.meta.resolve('prettier-plugin-astro')],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/prettier-json5/.prettierrc.json5",
    "content": "{\n  // Prettier config with JSON5 syntax\n  \"semi\": false,\n  \"singleQuote\": true,\n  \"plugins\": [\n    \"prettier-plugin-tailwindcss\",\n  ], // trailing comma is allowed in JSON5\n}"
  },
  {
    "path": "packages/knip/fixtures/plugins/prettier-json5/package.json",
    "content": "{\n  \"name\": \"@fixtures/prettier-json5\",\n  \"devDependencies\": {\n    \"prettier\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/prettier-reexport/knip.json",
    "content": "{\n  \"ignoreDependencies\": [\"prettier-plugin-test\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/prettier-reexport/package.json",
    "content": "{\n  \"name\": \"@fixtures/prettier-reexport\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"format\": \"prettier\"\n  },\n  \"devDependencies\": {\n    \"prettier\": \"*\",\n    \"@org/prettier-config\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/prettier-reexport/prettier.config.js",
    "content": "export { default } from '@org/prettier-config';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/prisma/.config/prisma.ts",
    "content": "import { defineConfig } from 'prisma/config';\n\nexport default defineConfig({\n  schema: 'prisma/schema-dot-config.prisma',\n  migrations: {\n    seed: 'tsx ../prisma/seed-dot-config.ts',\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/prisma/config/prisma.custom-config.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/prisma/package.json",
    "content": "{\n  \"name\": \"@plugins/prisma\",\n  \"dependencies\": {\n    \"@prisma/client\": \"*\",\n    \"@prisma/client2\": \"*\",\n    \"@prisma/client3\": \"*\"\n  },\n  \"devDependencies\": {\n    \"lint-staged\": \"*\",\n    \"prisma\": \"*\",\n    \"tsx\": \"*\"\n  },\n  \"scripts\": {\n    \"lint-staged\": \"lint-staged\",\n    \"generate\": \"prisma generate --schema prisma/schema-script.prisma --config config/prisma.custom-config.ts\",\n    \"generate-multi-schema\": \"prisma generate --schema prisma-multi-schema\"\n  },\n  \"prisma\": {\n    \"schema\": \"prisma/schema-package-json.prisma\",\n    \"seed\": \"tsx prisma/seed-package-json.ts\"\n  },\n  \"lint-staged\": {\n    \"*.prisma\": \"prisma format --schema\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/prisma/prisma/schema-dot-config.prisma",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/prisma/prisma/schema-package-json.prisma",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/prisma/prisma/schema-root-config.prisma",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/prisma/prisma/schema-script.prisma",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/prisma/prisma/seed-dot-config.ts",
    "content": "import { PrismaClient } from '@prisma/client3';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/prisma/prisma/seed-package-json.ts",
    "content": "import { PrismaClient } from '@prisma/client';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/prisma/prisma/seed-root-config.ts",
    "content": "import { PrismaClient } from '@prisma/client2';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/prisma/prisma-multi-schema/model/post.prisma",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/prisma/prisma-multi-schema/model/user.prisma",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/prisma/prisma-multi-schema/schema.prisma",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/prisma/prisma.config.ts",
    "content": "import { defineConfig } from 'prisma/config';\n\nexport default defineConfig({\n  schema: 'prisma/schema-root-config.prisma',\n  migrations: {\n    seed: 'tsx prisma/seed-root-config.ts',\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/prisma2/package.json",
    "content": "{\n  \"name\": \"@plugins/prisma-2\",\n  \"scripts\": {\n    \"generate\": \"prisma generate\"\n  },\n  \"devDependencies\": {\n    \"prisma\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/prisma2/prisma/schema.prisma",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/prisma2/schema.prisma",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/qwik/package.json",
    "content": "{\n  \"name\": \"@plugins/qwik\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"build\": \"qwik build\",\n    \"dev\": \"vite --mode ssr\",\n    \"preview\": \"qwik build preview && vite preview --open\"\n  },\n  \"devDependencies\": {\n    \"@builder.io/qwik\": \"*\",\n    \"@builder.io/qwik-city\": \"*\",\n    \"vite\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/qwik/src/components/router-head.tsx",
    "content": "export const RouterHead = () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/qwik/src/entry.dev.tsx",
    "content": "export default function () {}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/qwik/src/entry.preview.tsx",
    "content": "export default function () {}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/qwik/src/entry.ssr.tsx",
    "content": "export default function () {}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/qwik/src/root.tsx",
    "content": "import { RouterHead } from './components/router-head';\nexport default () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/qwik/src/routes/index.tsx",
    "content": "export default () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/qwik/src/routes/layout.tsx",
    "content": "export default () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/qwik/vite.config.ts",
    "content": "import { qwikVite } from '@builder.io/qwik/optimizer';\nimport { qwikCity } from '@builder.io/qwik-city/vite';\nimport { defineConfig } from 'vite';\n\nexport default defineConfig(() => {\n  return {\n    plugins: [qwikCity(), qwikVite()],\n  };\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/qwik-custom-dirs/docs/components/mdx-note.tsx",
    "content": "export const MdxNote = () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/qwik-custom-dirs/docs/entry.dev.tsx",
    "content": "export default function () {}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/qwik-custom-dirs/docs/entry.preview.tsx",
    "content": "export default function () {}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/qwik-custom-dirs/docs/entry.ssr.tsx",
    "content": "export default function () {}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/qwik-custom-dirs/docs/extra-pages/index.tsx",
    "content": "export default () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/qwik-custom-dirs/docs/pages/guide.mdx",
    "content": "import { MdxNote } from '../components/mdx-note';\n\n# Guide\n\n<MdxNote />\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/qwik-custom-dirs/docs/pages/index.tsx",
    "content": "export default () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/qwik-custom-dirs/docs/root.tsx",
    "content": "export default () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/qwik-custom-dirs/knip.ts",
    "content": "export default {\n  project: ['**/*.{js,ts,tsx,md,mdx}'],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/qwik-custom-dirs/package.json",
    "content": "{\n  \"name\": \"@plugins/qwik-custom-dirs\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"build\": \"qwik build\",\n    \"dev\": \"vite --mode ssr\"\n  },\n  \"devDependencies\": {\n    \"@builder.io/qwik\": \"*\",\n    \"@builder.io/qwik-city\": \"*\",\n    \"vite\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/qwik-custom-dirs/vite.config.ts",
    "content": "import { qwikVite } from '@builder.io/qwik/optimizer';\nimport { qwikCity } from '@builder.io/qwik-city/vite';\nimport { defineConfig } from 'vite';\n\nexport default defineConfig(() => ({\n  plugins: [qwikCity({ routesDir: ['docs/pages', 'docs/extra-pages'] }), qwikVite({ srcDir: 'docs' })],\n}));\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/raycast/package.json",
    "content": "{\n  \"name\": \"@fixtures/raycast\",\n  \"dependencies\": {\n    \"@raycast/api\": \"^1.0.0\"\n  },\n  \"commands\": [\n    {\n      \"name\": \"search-bookmarks\"\n    },\n    {\n      \"name\": \"search-bookmarks\"\n    },\n    {},\n    {\n      \"name\": 123\n    }\n  ],\n  \"tools\": [\n    {\n      \"name\": \"organize-tabs\"\n    },\n    {\n      \"name\": \"organize-tabs\"\n    },\n    {},\n    {\n      \"name\": 456\n    }\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/raycast/src/search-bookmarks.tsx",
    "content": "import { closeMainWindow } from '@raycast/api';\nimport { loadBookmarks } from './shared/load-bookmarks';\n\nexport default async function Command() {\n  await closeMainWindow();\n  return loadBookmarks();\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/raycast/src/shared/load-bookmarks.ts",
    "content": "export const loadBookmarks = () => ['docs', 'repo'];\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/raycast/src/tools/organize-tabs.ts",
    "content": "import { showHUD } from '@raycast/api';\n\nexport default async function organizeTabs() {\n  await showHUD('done');\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/raycast/src/unused.ts",
    "content": "export const unused = 'unused';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/react-cosmos/__fixtures__/any.mdx",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/react-cosmos/cosmos.config.json",
    "content": "{\n  \"$schema\": \"http://json.schemastore.org/cosmos-config\",\n  \"plugins\": [\n    \"react-cosmos-plugin-open-fixture\",\n    \"react-cosmos-plugin-boolean-input\",\n    \"react-cosmos-plugin-webpack\",\n    \"./src/my-cosmos-plugin.ts\"\n  ],\n  \"webpack\": {\n    \"configPath\": \"./webpack.config.cosmos.js\",\n    \"overridePath\": \"./webpack.override.js\"\n  },\n  \"vite\": {\n    \"configPath\": \"./tools/vite.config.js\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/react-cosmos/package.json",
    "content": "{\n  \"name\": \"@plugins/react-cosmos\",\n  \"scripts\": {\n    \"cosmos\": \"cosmos\",\n    \"cosmos-export\": \"cosmos-export\",\n    \"cosmos-native\": \"cosmos-native\"\n  },\n  \"devDependencies\": {\n    \"react-cosmos\": \"*\",\n    \"react-cosmos-native\": \"*\",\n    \"react-cosmos-plugin-boolean-input\": \"*\",\n    \"react-cosmos-plugin-open-fixture\": \"*\",\n    \"react-cosmos-plugin-webpack\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/react-cosmos/src/cosmos.decorator.tsx",
    "content": "export default function MyDecorator() {}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/react-cosmos/src/my-cosmos-plugin.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/react-native/package.json",
    "content": "{\n  \"name\": \"@plugins/react-native\",\n  \"dependencies\": {\n    \"@react-native-community/cli\": \"*\",\n    \"@react-native-community/cli-platform-android\": \"*\",\n    \"@react-native-community/cli-platform-ios\": \"*\",\n    \"react-native\": \"*\",\n    \"react-native-macos\": \"*\",\n    \"react-native-vector-icons\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/react-native/react-native.config.js",
    "content": "module.exports = {\n  dependencies: {\n    'react-native-vector-icons': {\n      platforms: {\n        ios: null,\n      },\n    },\n  },\n  platforms: {\n    macos: {\n      npmPackageName: 'react-native-macos',\n    },\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/react-router/app/root.tsx",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/react-router/app/routes/$/route.tsx",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/react-router/app/routes/$.tsx",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/react-router/app/routes/another-route.tsx",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/react-router/app/routes/home.tsx",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/react-router/app/routes/layout.tsx",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/react-router/app/routes/main+/nested+/_index.tsx",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/react-router/app/routes/route.(with).$special[.chars].tsx",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/react-router/app/routes.ts",
    "content": "export default [\n  { file: 'routes/home.tsx', index: true },\n  {\n    file: 'routes/layout.tsx',\n    children: [{ file: './routes/another-route.tsx' }],\n  },\n  {\n    file: 'routes/route.(with).$special[.chars].tsx',\n  },\n  {\n    file: 'routes/$/route.tsx',\n  },\n  {\n    file: 'routes/$.tsx',\n  },\n  {\n    file: 'routes/main+/nested+/_index.tsx',\n  },\n];\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/react-router/package.json",
    "content": "{\n  \"name\": \"@plugins/react-router\",\n  \"dependencies\": {\n    \"@react-router/node\": \"*\",\n    \"isbot\": \"*\"\n  },\n  \"devDependencies\": {\n    \"@react-router/dev\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/react-router/react-router.config.ts",
    "content": "import type { Config } from '@react-router/dev/config';\n\nexport default {\n  // Config options...\n  // Server-side render by default, to enable SPA mode set this to `false`\n  ssr: true,\n} satisfies Config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/react-router-with-server-entry/app/entry.server.tsx",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/react-router-with-server-entry/app/root.tsx",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/react-router-with-server-entry/app/routes/home.tsx",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/react-router-with-server-entry/app/routes.ts",
    "content": "export default [{ file: 'routes/home.tsx', index: true }];\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/react-router-with-server-entry/package.json",
    "content": "{\n  \"name\": \"@plugins/react-router\",\n  \"devDependencies\": {\n    \"@react-router/dev\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/react-router-with-server-entry/react-router.config.ts",
    "content": "import type { Config } from '@react-router/dev/config';\n\nexport default {\n  // Config options...\n  // Server-side render by default, to enable SPA mode set this to `false`\n  ssr: true,\n} satisfies Config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/relay/__generated__/artifact.graphql.ts",
    "content": "import type { Used } from '../used';\n\nexport type Artifact = {\n  used: Used;\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/relay/myrelay.config.js",
    "content": "module.exports = {\n  artifactDirectory: './__generated__',\n  requireCustomScalarTypes: true,\n  customScalarTypes: {\n    Used: { path: '../used.ts' },\n    Unused: { path: '../unused.ts' },\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/relay/package.json",
    "content": "{\n  \"name\": \"@plugins/relay\",\n  \"scripts\": {\n    \"build\": \"relay-compiler myrelay.config.js\",\n    \"dev\": \"vite dev\"\n  },\n  \"devDependencies\": {\n    \"relay-compiler\": \"*\",\n    \"vite-plugin-relay\": \"*\",\n    \"vite\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/relay/unused.ts",
    "content": "export type Unused = string;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/relay/used.ts",
    "content": "export type Used = string;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/relay/vite.config.ts",
    "content": "import relay from 'vite-plugin-relay';\n\nexport default {\n  plugins: [relay],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/relay2/package.json",
    "content": "{\n  \"name\": \"@plugins/relay2\",\n  \"scripts\": {\n    \"build\": \"relay-compiler\",\n    \"dev\": \"vite dev\"\n  },\n  \"devDependencies\": {\n    \"relay-compiler\": \"*\",\n    \"vite-plugin-relay\": \"*\",\n    \"vite\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/relay2/relay.config.js",
    "content": "module.exports = {\n  customScalarTypes: {\n    Used: { path: '../../used.ts' },\n    Unused: { path: '../../unused.ts' },\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/relay2/src/__generated__/artifact.graphql.ts",
    "content": "import type { Used } from '../../used';\n\nexport type Artifact = {\n  used: Used;\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/relay2/unused.ts",
    "content": "export type Unused = string;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/relay2/used.ts",
    "content": "export type Used = string;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/relay2/vite.config.ts",
    "content": "import relay from 'vite-plugin-relay';\n\nexport default {\n  plugins: [relay],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/release-it/.release-it.json",
    "content": "{\n  \"github\": {\n    \"release\": true,\n    \"releaseNotes\": \"node ./bin/release-notes.js ${version}\"\n  },\n  \"npm\": {\n    \"publish\": true\n  },\n  \"plugins\": {\n    \"@release-it/bumper\": {},\n    \"@release-it/conventional-changelog\": {\n      \"infile\": \"CHANGELOG.md\",\n      \"preset\": \"angular\"\n    }\n  },\n  \"hooks\": {\n    \"before:init\": [\"npm run lint\", \"npm test\"],\n    \"after:bump\": \"from-hook\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/release-it/bin/release-notes.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/release-it/package.json",
    "content": "{\n  \"name\": \"@plugins/release-it\",\n  \"devDependencies\": {\n    \"release-it\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/remark/package.json",
    "content": "{\n  \"name\": \"@plugins/remark\",\n  \"scripts\": {\n    \"format\": \"remark README.md -o\"\n  },\n  \"devDependencies\": {\n    \"remark-cli\": \"*\"\n  },\n  \"remarkConfig\": {\n    \"plugins\": [\n      \"preset-webpro\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/remix/app/entry.client.tsx",
    "content": "import { RemixBrowser } from '@remix-run/react';\nimport { startTransition, StrictMode } from 'react';\nimport { hydrateRoot } from 'react-dom/client';\n\nconst hydrate = () => {\n  startTransition(() => {\n    hydrateRoot(\n      document,\n      <StrictMode>\n        <RemixBrowser />\n      </StrictMode>\n    );\n  });\n};\n\nwindow.requestIdleCallback(hydrate);\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/remix/app/entry.server.tsx",
    "content": "import { PassThrough } from 'stream';\nimport { Response } from '@remix-run/node';\nimport { RemixServer } from '@remix-run/react';\nimport { renderToPipeableStream } from 'react-dom/server';\nimport type { EntryContext } from '@remix-run/node';\n\nexport default function handleRequest(\n  request: Request,\n  responseStatusCode: number,\n  responseHeaders: Headers,\n  remixContext: EntryContext\n) {\n  return Promise.resolve();\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/remix/app/root.tsx",
    "content": "import { json } from '@remix-run/node';\nimport { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration } from '@remix-run/react';\nimport { getUser } from './session.server';\nimport tailwindStylesheetUrl from './styles/tailwind.css';\nimport type { LinksFunction, LoaderArgs, MetaFunction } from '@remix-run/node';\n\nexport const links: LinksFunction = () => {\n  return [{ rel: 'stylesheet', href: tailwindStylesheetUrl }];\n};\n\nexport const meta: MetaFunction = () => ({\n  charset: 'utf-8',\n  title: 'Remix Notes',\n  viewport: 'width=device-width,initial-scale=1',\n});\n\nexport async function loader({ request }: LoaderArgs) {\n  return json({\n    user: await getUser(request),\n  });\n}\n\nexport default function App() {\n  return (\n    <html lang=\"en\" className=\"h-full\">\n      <head>\n        <Meta />\n        <Links />\n      </head>\n      <body className=\"h-full\">\n        <Outlet />\n        <ScrollRestoration />\n        <Scripts />\n        <LiveReload />\n      </body>\n    </html>\n  );\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/remix/app/routes/index.tsx",
    "content": "import { Link } from '@remix-run/react';\nimport { useOptionalUser } from '~/utils';\n\nexport default function Index() {\n  const user = useOptionalUser();\n  return (\n    <main>\n      <Link to=\"/\">link</Link>\n    </main>\n  );\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/remix/app/utils.ts",
    "content": "import { useMatches } from '@remix-run/react';\nimport { useMemo } from 'react';\n\nexport function useOptionalUser() {}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/remix/package.json",
    "content": "{\n  \"name\": \"@plugins/remix\",\n  \"scripts\": {\n    \"build\": \"run-s \\\"build:*\\\"\",\n    \"build:css\": \"npm run generate:css -- --minify\",\n    \"build:remix\": \"node ./node_modules/@remix-run/dev/dist/cli build\",\n    \"dev\": \"run-p \\\"dev:*\\\"\",\n    \"dev:css\": \"cross-env NODE_ENV=development npm run generate:css -- --watch\",\n    \"dev:remix\": \"cross-env NODE_ENV=development node ./node_modules/@remix-run/dev/dist/cli watch\",\n    \"dev:server\": \"cross-env NODE_ENV=development node --inspect --require ./node_modules/dotenv/config ./server.js\",\n    \"generate:css\": \"tailwindcss -o ./app/styles/tailwind.css\",\n    \"setup\": \"prisma generate && prisma migrate deploy && prisma db seed\",\n    \"start\": \"cross-env NODE_ENV=production node --inspect --require ./node_modules/dotenv/config ./server.js\"\n  },\n  \"devDependencies\": {\n    \"@remix-run/dev\": \"*\",\n    \"npm-run-all\": \"*\"\n  },\n  \"prisma\": {\n    \"seed\": \"ts-node --require tsconfig-paths/register prisma/seed.ts\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/remix/remix.config.js",
    "content": "/** @type {import('@remix-run/dev').AppConfig} */\nmodule.exports = {\n  cacheDirectory: '',\n  ignoredRouteFiles: [],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/remix/remix.init/index.js",
    "content": "const { execSync } = require('node:child_process');\n\nconst main = async ({ rootDirectory }) => {};\n\nmodule.exports = main;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/remix/server.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/remix/tsconfig.json",
    "content": "{\n  \"exclude\": [],\n  \"include\": [\"remix.env.d.ts\", \"**/*.ts\", \"**/*.tsx\"],\n  \"compilerOptions\": {\n    \"lib\": [\"DOM\", \"DOM.Iterable\", \"ES2019\"],\n    \"types\": [\"vitest/globals\"],\n    \"isolatedModules\": true,\n    \"esModuleInterop\": true,\n    \"jsx\": \"react-jsx\",\n    \"module\": \"CommonJS\",\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"target\": \"ES2019\",\n    \"strict\": true,\n    \"allowJs\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"~/*\": [\"./app/*\"]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/rollup/do-not-bundle.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/rollup/package.json",
    "content": "{\n  \"name\": \"@plugins/rollup\",\n  \"scripts\": {\n    \"build\": \"rollup\"\n  },\n  \"devDependencies\": {\n    \"rollup\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/rollup/rollup.config.js",
    "content": "import { fileURLToPath } from 'node:url';\nimport { terser } from 'rollup-plugin-terser';\nimport resolve from '@rollup/plugin-node-resolve';\nimport commonjs from '@rollup/plugin-commonjs';\nimport jsx from 'acorn-jsx';\n\nexport default {\n  input: 'main.js',\n  external: [\n    'some-externally-required-library',\n    fileURLToPath(new URL('do-not-bundle.js', import.meta.url)),\n    /node_modules/,\n  ],\n  plugins: [resolve(), commonjs()],\n  acornInjectPlugins: [jsx()],\n  output: [\n    {\n      file: 'bundle.js',\n      format: 'es',\n    },\n    {\n      file: 'bundle.min.js',\n      format: 'es',\n      plugins: [terser()],\n    },\n  ],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/rsbuild/entry-1.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/rsbuild/entry-2.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/rsbuild/entry-3.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/rsbuild/entry-4.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/rsbuild/entry-5.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/rsbuild/entry-6.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/rsbuild/entry-7.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/rsbuild/entry-8.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/rsbuild/package.json",
    "content": "{\n  \"name\": \"@plugins/rsbuild\",\n  \"scripts\": {\n    \"dev\": \"rsbuild dev --open\",\n    \"build\": \"rsbuild build\",\n    \"preview\": \"rsbuild preview\"\n  },\n  \"devDependencies\": {\n    \"@rsbuild/core\": \"*\",\n    \"@rsbuild/plugin-react\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/rsbuild/pre-entry-1.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/rsbuild/pre-entry-2.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/rsbuild/pre-entry-3.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/rsbuild/rsbuild.config.ts",
    "content": "import { defineConfig } from '@rsbuild/core';\nimport { pluginReact } from '@rsbuild/plugin-react';\n\nexport default defineConfig({\n  plugins: [pluginReact()],\n  source: {\n    entry: {\n      entry1: 'entry-1.ts',\n      entry2: ['entry-2.ts'],\n      entry3: { import: 'entry-3.ts' },\n      entry4: { import: ['entry-4.ts'] },\n    },\n    preEntry: 'pre-entry-1.ts',\n  },\n  environments: {\n    test: {\n      source: {\n        entry: {\n          entry5: 'entry-5.ts',\n          entry6: ['entry-6.ts'],\n          entry7: { import: 'entry-7.ts' },\n          entry8: { import: ['entry-8.ts'] },\n        },\n        preEntry: ['pre-entry-2.ts', 'pre-entry-3.ts'],\n      },\n    },\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/rslib/package.json",
    "content": "{\n  \"name\": \"@plugins/rslib\",\n  \"devDependencies\": {\n    \"@rslib/core\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/rslib/rslib.config.ts",
    "content": "import { defineConfig } from '@rslib/core';\n\nexport default defineConfig({});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/rspack/package.json",
    "content": "{\n  \"name\": \"@plugins/rspack\",\n  \"scripts\": {\n    \"dev\": \"rspack serve\",\n    \"build\": \"rspack build\"\n  },\n  \"devDependencies\": {\n    \"@rspack/core\": \"*\",\n    \"@rspack/cli\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/rspack/rspack.config.js",
    "content": "module.exports = {\n  entry: {\n    main: './src/entry.ts',\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.jsx$/,\n        use: {\n          loader: 'builtin:swc-loader',\n          options: {\n            jsc: {\n              parser: {\n                syntax: 'ecmascript',\n                jsx: true,\n              },\n              transform: {\n                react: {\n                  pragma: 'React.createElement',\n                  pragmaFrag: 'React.Fragment',\n                  throwIfNamespace: true,\n                  development: false,\n                  useBuiltins: false,\n                },\n              },\n            },\n          },\n        },\n        type: 'javascript/auto',\n      },\n    ],\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/rspack/src/entry.ts",
    "content": "import './index.css';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/rspack/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"lib\": [\"DOM\", \"ES2020\"],\n    \"module\": \"ESNext\",\n    \"noEmit\": true,\n    \"strict\": true,\n    \"skipLibCheck\": true,\n    \"isolatedModules\": true,\n    \"resolveJsonModule\": true,\n    \"moduleResolution\": \"bundler\",\n    \"useDefineForClassFields\": true,\n    \"allowImportingTsExtensions\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/rstest/included.test.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/rstest/not-included.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/rstest/package.json",
    "content": "{\n  \"name\": \"@plugins/rstest\",\n  \"devDependencies\": {\n    \"@rstest/core\": \"*\",\n    \"happy-dom\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/rstest/rstest.config.ts",
    "content": "import { defineConfig } from '@rstest/core';\n\nexport default defineConfig({\n  testEnvironment: 'happy-dom',\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/rstest2/__mocks__/lodash.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/rstest2/excluded.test.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/rstest2/included.test.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/rstest2/not-included.spec.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/rstest2/package.json",
    "content": "{\n  \"name\": \"@plugins/rstest2\",\n  \"devDependencies\": {\n    \"@rstest/core\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/rstest2/rstest.config.ts",
    "content": "import { defineConfig } from '@rstest/core';\n\nexport default defineConfig({\n  include: ['./*.test.ts'],\n  exclude: ['./excluded.test.ts'],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sanity/package.json",
    "content": "{\n  \"name\": \"@plugins/sanity\",\n  \"dependencies\": {\n    \"@sanity/blueprints\": \"*\",\n    \"@sanity/vision\": \"*\",\n    \"sanity\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sanity/sanity.blueprint.ts",
    "content": "import { defineBlueprint, defineDocumentFunction } from '@sanity/blueprints';\n\nexport default defineBlueprint({\n  resources: [\n    defineDocumentFunction({\n      name: 'on-publish',\n      event: { type: 'document.publish' },\n    }),\n  ],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sanity/sanity.cli.ts",
    "content": "import { defineCliConfig } from 'sanity/cli';\n\nexport default defineCliConfig({\n  api: {\n    projectId: 'project-id',\n    dataset: 'production',\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sanity/sanity.config.ts",
    "content": "import { defineConfig } from 'sanity';\nimport { structureTool } from 'sanity/structure';\nimport { visionTool } from '@sanity/vision';\nimport { schemaTypes } from './schema';\n\nexport default defineConfig({\n  name: 'default',\n  title: 'My Studio',\n  projectId: 'project-id',\n  dataset: 'production',\n  plugins: [structureTool(), visionTool()],\n  schema: { types: schemaTypes },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sanity/schema.ts",
    "content": "export const schemaTypes = [];\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/semantic-release/.releaserc",
    "content": "branches:\n  - main\n  - name: next\n    channel: next\n    prerelease: next\nplugins:\n  - - '@semantic-release/commit-analyzer'\n    - preset: angular\n      parserOpts:\n        headerPattern: '^(\\w*)(?:\\((.*)\\))?!?: (.*)$'\n        breakingHeaderPattern: '^(\\w*)(?:\\((.*)\\))?!: (.*)$'\n      releaseRules:\n        - breaking: true\n          release: major\n        - revert: true\n          release: patch\n        - type: feat\n          release: minor\n        - type: fix\n          release: patch\n        - type: perf\n          release: patch\n        - type: build\n          scope: deps\n          release: patch\n        - type: build\n          scope: peer-deps\n          release: patch\n        - type: build\n          scope: release-patch\n          release: patch\n        - type: build\n          scope: release-minor\n          release: minor\n        - type: build\n          scope: release-major\n          release: major\n  - - '@semantic-release/release-notes-generator'\n    - preset: angular\n      parserOpts:\n        headerPattern: '^(\\w*)(?:\\((.*)\\))?!?: (.*)$'\n        breakingHeaderPattern: '^(\\w*)(?:\\((.*)\\))?!: (.*)$'\n  - - '@semantic-release/changelog'\n    - changelogTitle: '# Changelog\n\n        All notable changes to this project will be documented in this file. Dates are displayed in UTC.'\n  - - '@semantic-release/git'\n    - assets:\n        - CHANGELOG.md\n  - '@semantic-release/npm'\n  - - '@semantic-release/github'\n    - releasedLabels:\n        - 'Status: Released'\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/semantic-release/package.json",
    "content": "{\n  \"name\": \"@plugins/semantic-release\",\n  \"devDependencies\": {\n    \"semantic-release\": \"*\"\n  },\n  \"release\": {\n    \"branches\": [\n      \"main\"\n    ],\n    \"plugins\": [\n      \"@semantic-release/commit-analyzer\",\n      \"@semantic-release/release-notes-generator\",\n      \"@semantic-release/changelog\",\n      \"@semantic-release/git\",\n      \"@semantic-release/npm\",\n      [\n        \"@semantic-release/github\",\n        {\n          \"releasedLabels\": [\n            \"Status: Released\"\n          ]\n        }\n      ]\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sentry/package.json",
    "content": "{\n  \"name\": \"@plugins/sentry\",\n  \"scripts\": {},\n  \"dependencies\": {\n    \"@sentry/nextjs\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sentry/sentry.client.config.ts",
    "content": "import * as Sentry from '@sentry/nextjs';\n\nconst SENTRY_DSN = process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN;\n\nSentry.init({\n  dsn: SENTRY_DSN || 'https://examplePublicKey@o0.ingest.sentry.io/0',\n  tracesSampleRate: 1.0,\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sentry/sentry.edge.config.ts",
    "content": "import './sentry.client.config';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sentry/sentry.server.config.ts",
    "content": "import './sentry.client.config';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/simple-git-hooks/package.json",
    "content": "{\n  \"name\": \"@plugins/simple-git-hooks\",\n  \"devDependencies\": {\n    \"simple-git-hooks\": \"*\",\n    \"lint-staged\": \"*\"\n  },\n  \"simple-git-hooks\": {\n    \"pre-commit\": \"pnpm lint-staged\"\n  },\n  \"lint-staged\": {\n    \"*\": \"eslint --fix\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/size-limit/.size-limit.cjs",
    "content": "module.exports = {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/size-limit/package.json",
    "content": "{\n  \"name\": \"@plugins/size-limit\",\n  \"scripts\": {\n    \"size\": \"size-limit\"\n  },\n  \"dependencies\": {\n    \"size-limit\": \"*\",\n    \"@size-limit/preset-app\": \"*\"\n  },\n  \"devDependencies\": {\n    \"@size-limit/file\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sst/handlers/auth.ts",
    "content": "import 'sst-auth-dep';\n\nexport const handler = () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sst/handlers/other-auth.ts",
    "content": "import 'sst-auth-handler-dep';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sst/handlers/some-route.ts",
    "content": "import 'sst-some-dep';\n\nexport const handler = () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sst/package.json",
    "content": "{\n  \"name\": \"@plugins/sst\",\n  \"dependencies\": {\n    \"sst\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sst/sst.config.ts",
    "content": "import { sst, SSTConfig } from 'sst';\nimport { d } from 'sst-config-dep';\nimport { AuthStack } from './stacks/AuthStack';\nimport { AuthHandlerStack } from './stacks/AuthHandlerStack';\n\nnew sst.aws.Function('MyFunction', {\n  handler: 'handlers/some-route.handler', // v3\n  environment: {\n    ACCOUNT: aws.getCallerIdentityOutput({}).accountId,\n    REGION: aws.getRegionOutput().name,\n  },\n});\n\nexport default {\n  config(_input) {\n    return {\n      name: 'MyService',\n      region: 'eu-west-2',\n      stage: 'production',\n    };\n  },\n  stacks(app) {\n    app.stack(AuthStack).stack(AuthHandlerStack); // v2\n  },\n} satisfies SSTConfig;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sst/stacks/AuthHandlerStack.ts",
    "content": "import sst from 'sst';\nimport 'sst-auth-handler-stack-dep';\nimport { StackContext } from 'sst/constructs';\n\nexport function AuthHandlerStack({ stack, app }: StackContext) {\n  new sst.aws.Function('MyFunction', {\n    handler: 'handlers/other-auth.handler',\n    timeout: '3 minutes',\n    memory: '1024 MB',\n  });\n\n  return {\n    handler: 'handler',\n  };\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sst/stacks/AuthStack.ts",
    "content": "import sst from 'sst';\nimport 'sst-auth-stack-dep';\nimport { StackContext, Function, FunctionProps } from 'sst/constructs';\n\nexport function AuthStack({ stack, app }: StackContext) {\n  // Create single Lambda handler\n  const handlerProps: FunctionProps = {\n    handler: 'handlers/auth.handler',\n    permissions: ['perm1', 'perm2'],\n  };\n\n  const handler = new Function(stack, 'MyHandler', handlerProps);\n\n  return {\n    handler,\n  };\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sst2/package.json",
    "content": "{\n  \"name\": \"@plugins/sst2\",\n  \"dependencies\": {\n    \"sst\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sst2/route.ts",
    "content": "import 'sst';\n\nexport const handler = async () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sst2/sst.config.ts",
    "content": "/// <reference path=\"./.sst/platform/config.d.ts\" />\n\nexport default $config({\n  app(input) {\n    return {\n      name: 'aws-apig-auth',\n      home: 'aws',\n      removal: input?.stage === 'production' ? 'retain' : 'remove',\n    };\n  },\n  async run() {\n    const api = new sst.aws.ApiGatewayV2('MyApi', {\n      domain: {\n        name: 'api.ion.sst.sh',\n        path: 'v1',\n      },\n    });\n    api.route('GET /', {\n      handler: 'route.handler',\n    });\n    api.route('GET /endpoint-a', 'route.handler', { auth: { iam: true } });\n    api.route('GET /endpoint-b', 'route.handler', {\n      auth: {\n        jwt: {\n          issuer: 'https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Rq4d8zILG',\n          audiences: ['user@example.com'],\n        },\n      },\n    });\n    api.route('$default', 'route.handler');\n\n    return {\n      api: api.url,\n    };\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/starlight/astro.config.mjs",
    "content": "import starlight from '@astrojs/starlight';\nimport { defineConfig } from 'astro/config';\n\nexport default defineConfig({\n  integrations: [\n    starlight({\n      components: {\n        Head: './components/Head.astro',\n        Footer: './components/Footer.astro',\n      },\n    }),\n  ],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/starlight/components/Footer.astro",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/starlight/components/Head.astro",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/starlight/package.json",
    "content": "{\n  \"name\": \"@plugins/starlight\",\n  \"dependencies\": {\n    \"@astrojs/starlight\": \"*\",\n    \"astro\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/storybook/.storybook/main.js",
    "content": "const path = require('node:path');\nconst { TsconfigPathsPlugin } = require('tsconfig-paths-webpack-plugin');\n\nmodule.exports = {\n  features: {\n    // Enables code splitting\n    storyStoreV7: true,\n  },\n  stories: [],\n  addons: [\n    '@storybook/addon-essentials',\n    '@storybook/addon-a11y',\n    '@storybook/addon-knobs/preset',\n    'storybook-addon-export-to-codesandbox',\n    '../addon/register',\n    '@storybook/addon-vitest',\n  ],\n  webpackFinal: config => {\n    return config;\n  },\n  core: {\n    builder: 'webpack5',\n    lazyCompilation: true,\n  },\n  framework: {\n    name: '@storybook/react-webpack5',\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/storybook/.storybook/preview.js",
    "content": "import 'cypress-storybook/react';\n\nexport const decorators = [withFluentProvider, withStrictMode];\n\nexport const parameters = {\n  viewMode: 'docs',\n  controls: {\n    disable: true,\n    expanded: true,\n  },\n  docs: {\n    source: {\n      excludeDecorators: true,\n    },\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/storybook/.storybook/vitest.setup.ts",
    "content": "import { beforeAll } from 'vitest';\n// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.\nimport { setProjectAnnotations } from '@storybook/your-framework';\nimport * as previewAnnotations from './preview';\n\nconst annotations = setProjectAnnotations([previewAnnotations]);\n\n// Run Storybook's beforeAll hook\nbeforeAll(annotations.beforeAll);\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/storybook/addon/register.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/storybook/package.json",
    "content": "{\n  \"name\": \"@plugins/storybook\",\n  \"scripts\": {\n    \"storybook\": \"storybook\"\n  },\n  \"devDependencies\": {\n    \"tsconfig-paths-webpack-plugin\": \"*\",\n    \"@storybook/addon-essentials\": \"*\",\n    \"@storybook/addon-a11y\": \"*\",\n    \"@storybook/addon-vitest\": \"*\",\n    \"storybook-addon-performance\": \"*\",\n    \"vitest\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/storybook2/.rnstorybook/index.tsx",
    "content": "import { view } from './storybook.requires';\nimport AsyncStorage from '@react-native-async-storage/async-storage';\n\nconst StorybookUIRoot = view.getStorybookUI({\n  storage: {\n    getItem: AsyncStorage.getItem,\n    setItem: AsyncStorage.setItem,\n  },\n});\n\nexport default StorybookUIRoot;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/storybook2/.rnstorybook/main.ts",
    "content": "import { StorybookConfig } from '@storybook/react-native';\n\nconst main: StorybookConfig = {\n  stories: ['../components/**/*.stories.?(ts|tsx|js|jsx)'],\n  addons: [\n    '@storybook/addon-ondevice-notes',\n    '@storybook/addon-ondevice-controls',\n    '@storybook/addon-ondevice-backgrounds',\n    '@storybook/addon-ondevice-actions',\n  ],\n};\n\nexport default main;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/storybook2/.rnstorybook/preview.tsx",
    "content": "import { withBackgrounds } from '@storybook/addon-ondevice-backgrounds';\nimport { Preview } from '@storybook/react';\n\nconst preview: Preview = {\n  decorators: [withBackgrounds],\n};\n\nexport default preview;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/storybook2/.rnstorybook/storybook.requires.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/storybook2/components/Button/Button.stories.tsx",
    "content": "import type { Meta, StoryObj } from '@storybook/react';\nimport { fn } from 'storybook/test';\nimport { MyButton } from './Button';\n\nconst meta = {\n  component: MyButton,\n} satisfies Meta<typeof MyButton>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\nexport const Basic: Story = {\n  args: {\n    text: 'Hello World',\n    color: 'purple',\n    onPress: fn(),\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/storybook2/components/Button/Button.tsx",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/storybook2/package.json",
    "content": "{\n  \"name\": \"@plugins/storybook\",\n  \"scripts\": {\n    \"storybook\": \"storybook\"\n  },\n  \"dependencies\": {\n    \"@react-native-async-storage/async-storage\": \"*\"\n  },\n  \"devDependencies\": {\n    \"@storybook/addon-ondevice-actions\": \"*\",\n    \"@storybook/addon-ondevice-backgrounds\": \"*\",\n    \"@storybook/addon-ondevice-controls\": \"*\",\n    \"@storybook/addon-ondevice-notes\": \"*\",\n    \"@storybook/react\": \"*\",\n    \"@storybook/react-native\": \"*\",\n    \"storybook\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/stryker/.stryker.conf.js",
    "content": "export default {\n  testRunner: 'mocha',\n  checkers: ['typescript'],\n  plugins: ['@stryker-mutator/jasmine-framework', '@stryker-mutator/karma-runner'],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/stryker/package.json",
    "content": "{\n  \"name\": \"@plugins/stryker\",\n  \"scripts\": {\n    \"stryker\": \"stryker\"\n  },\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@stryker-mutator/core\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/stryker/stryker.conf.cjs",
    "content": "module.exports = {\n  testRunner: 'mocha',\n  checkers: ['typescript'],\n  plugins: ['@stryker-mutator/jasmine-framework', '@stryker-mutator/karma-runner'],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/stryker/stryker.conf.json",
    "content": "{\n  \"testRunner\": \"karma\",\n  \"checkers\": [\"typescript\"],\n  \"plugins\": [\"@stryker-mutator/karma-runner\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/stryker/stryker.conf.mjs",
    "content": "const config = {\n  testRunner: 'mocha',\n  checkers: ['typescript'],\n  plugins: ['@stryker-mutator/jasmine-framework', '@stryker-mutator/karma-runner'],\n};\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/stylelint/.stylelintrc",
    "content": "{\n  \"customSyntax\": \"postcss-less\",\n  \"extends\": [\"stylelint-config-standard\", \"./myExtendableConfig\"],\n  \"plugins\": [\"stylelint-order\"],\n  \"rules\": {\n    \"alpha-value-notation\": \"number\"\n  },\n  \"overrides\": [\n    {\n      \"files\": [\n        \"**/*.html\"\n      ],\n      \"extends\": [\"stylelint-config-html/html\", \"stylelint-config-standard\"],\n      \"plugins\": [\"stylelint-order\"]\n    }\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/stylelint/package.json",
    "content": "{\n  \"name\": \"@plugins/stylelint\",\n  \"devDependencies\": {\n    \"stylelint\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/stylelint2/.stylelintrc.cjs",
    "content": "/** @type {import('stylelint').Config} */\nmodule.exports = {\n  customSyntax: require('postcss-less'),\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/stylelint2/.stylelintrc.mjs",
    "content": "import less from 'postcss-less';\n\n/** @type {import('stylelint').Config} */\nconst config = {\n  customSyntax: less,\n  extends: ['stylelint-config-standard'],\n  rules: {\n    'alpha-value-notation': 'number',\n  },\n  overrides: [\n    {\n      files: ['**/*.html'],\n      extends: ['stylelint-config-html/html.js', 'stylelint-config-standard'],\n    },\n  ],\n};\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/stylelint2/myCustomPlugin.js",
    "content": "export default {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/stylelint2/myExtendableConfig.js",
    "content": "/** @type {import('stylelint').Config} */\nconst myExtendableConfig = {\n  plugins: ['stylelint-order'],\n};\n\nmodule.exports = myExtendableConfig;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/stylelint2/package.json",
    "content": "{\n  \"name\": \"@plugins/stylelint2\",\n  \"devDependencies\": {\n    \"postcss-less\": \"*\",\n    \"postcss-styl\": \"*\",\n    \"stylelint\": \"*\",\n    \"stylelint-config-recommended\": \"*\",\n    \"stylelint-config-standard\": \"*\"\n  },\n  \"dependencies\": {\n    \"stylelint-config-html\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/stylelint2/stylelint.config.js",
    "content": "const less = require('postcss-less');\nconst stylus = require('postcss-styl');\nconst myCustomPlugin = require('./myCustomPlugin');\n\n/** @type {import('stylelint').Config} */\nconst config = {\n  customSyntax: less,\n  extends: [require.resolve('stylelint-config-recommended'), './myExtendableConfig'],\n  plugins: ['./myCustomPlugin.js'],\n  rules: {\n    'alpha-value-notation': 'number',\n  },\n  overrides: [\n    {\n      plugins: ['./myCustomPlugin.js', myCustomPlugin],\n    },\n    {\n      files: ['**/*.html'],\n      extends: ['stylelint-config-html/html', 'stylelint-config-standard'],\n    },\n    {\n      files: ['*.styl', '**/*.styl', '*.stylus', '**/*.stylus'],\n      customSyntax: stylus,\n    },\n  ],\n};\n\nmodule.exports = config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/stylelint3/package.json",
    "content": "{\n  \"name\": \"@fixtures/stylelint3\",\n  \"devDependencies\": {\n    \"stylelint\": \"*\",\n    \"stylelint-config-recommended\": \"*\",\n    \"stylelint-plugin-logical-css\": \"*\",\n    \"postcss-styled-syntax\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/stylelint3/stylelint.config.mjs",
    "content": "/** @type {import('stylelint').Config} */\nexport default {\n  extends: [\n    {\n      extends: ['stylelint-config-recommended'],\n      customSyntax: 'postcss-styled-syntax',\n      plugins: ['stylelint-plugin-logical-css'],\n      rules: {},\n    },\n  ],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/svelte/.svelte-kit/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"paths\": {\n      \"$lib\": [\"../src/lib\"],\n      \"$lib/*\": [\"../src/lib/*\"]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/svelte/knip.ts",
    "content": "export default {\n  ignore: ['.svelte-kit'],\n  compilers: {\n    css: (text: string) => [...text.matchAll(/(?<=@)import[^;]+/g)].join('\\n'),\n    svelte: (text: string) => [...text.matchAll(/import[^;]+/g)].join('\\n'),\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/svelte/package.json",
    "content": "{\n  \"name\": \"@fixtures/svelte\",\n  \"type\": \"module\",\n  \"devDependencies\": {\n    \"@sveltejs/adapter-auto\": \"*\",\n    \"@sveltejs/kit\": \"*\",\n    \"svelte\": \"*\",\n    \"typescript\": \"*\",\n    \"vite\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/svelte/src/app.d.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/svelte/src/app.html",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/svelte/src/hooks.client.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/svelte/src/hooks.server.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/svelte/src/instrumentation.server.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/svelte/src/lib/component.svelte",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/svelte/src/lib/store.svelte.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/svelte/src/params/lang.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/svelte/src/routes/+layout.svelte",
    "content": "<script>\nimport Header from './Header.svelte';\nimport './styles.css';\n</script>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/svelte/src/routes/+page.svelte",
    "content": "<script lang=\"ts\">\nimport Counter from './Counter.svelte';\nimport Component from '$lib/component.svelte';\nimport store from '$lib/store.svelte.ts';\nimport type { Config } from '@sveltejs/kit';\n// import('./should-not-resolve')\n</script>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/svelte/src/routes/+page.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/svelte/src/routes/Counter.svelte",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/svelte/src/routes/Header.svelte",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/svelte/src/routes/login/+server.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/svelte/src/routes/styles.css",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/svelte/src/service-worker.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/svelte/svelte.config.js",
    "content": "import adapter from '@sveltejs/adapter-auto';\nimport { vitePreprocess } from '@sveltejs/kit/vite';\n\n/** @type {import('@sveltejs/kit').Config} */\nconst config = {\n  preprocess: vitePreprocess(),\n  kit: {\n    adapter: adapter(),\n  },\n};\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/svelte/tsconfig.json",
    "content": "{\n  \"extends\": \"./.svelte-kit/tsconfig.json\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/svelte/vite.config.ts",
    "content": "import { sveltekit } from '@sveltejs/kit/vite';\nimport { defineConfig } from 'vite';\n\nexport default defineConfig({\n  plugins: [sveltekit()],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit/.svelte-kit/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"paths\": {\n      \"$lib\": [\"../src/lib\"],\n      \"$lib/*\": [\"../src/lib/*\"]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit/knip.ts",
    "content": "export default {\n  ignore: ['.svelte-kit'],\n  compilers: {\n    css: (text: string) => [...text.matchAll(/(?<=@)import[^;]+/g)].join('\\n'),\n    svelte: (text: string) => [...text.matchAll(/import[^;]+/g)].join('\\n'),\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit/package.json",
    "content": "{\n  \"name\": \"@fixtures/sveltekit\",\n  \"type\": \"module\",\n  \"devDependencies\": {\n    \"@sveltejs/adapter-auto\": \"*\",\n    \"@sveltejs/kit\": \"*\",\n    \"svelte\": \"*\",\n    \"typescript\": \"*\",\n    \"vite\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit/src/app.d.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit/src/app.html",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit/src/hooks.client.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit/src/hooks.server.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit/src/instrumentation.server.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit/src/lib/component.svelte",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit/src/lib/store.svelte.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit/src/params/lang.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit/src/routes/+layout.svelte",
    "content": "<script>\nimport Header from './Header.svelte';\nimport './styles.css';\n</script>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit/src/routes/+page.svelte",
    "content": "<script>\nimport Counter from './Counter.svelte';\nimport Component from '$lib/component.svelte';\nimport store from '$lib/store.svelte.ts';\n</script>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit/src/routes/+page.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit/src/routes/Counter.svelte",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit/src/routes/Header.svelte",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit/src/routes/login/+server.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit/src/routes/styles.css",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit/src/service-worker.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit/svelte.config.js",
    "content": "import adapter from '@sveltejs/adapter-auto';\nimport { vitePreprocess } from '@sveltejs/kit/vite';\n\n/** @type {import('@sveltejs/kit').Config} */\nconst config = {\n  preprocess: vitePreprocess(),\n  kit: {\n    adapter: adapter(),\n  },\n};\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit/tsconfig.json",
    "content": "{\n  \"extends\": \"./.svelte-kit/tsconfig.json\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit/vite.config.ts",
    "content": "import { sveltekit } from '@sveltejs/kit/vite';\nimport { defineConfig } from 'vite';\n\nexport default defineConfig({\n  plugins: [sveltekit()],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit2/knip.ts",
    "content": "export default {\n  compilers: {\n    svelte: (text: string) => [...text.matchAll(/import[^;]+/g)].join('\\n'),\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit2/lib/helper.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit2/package.json",
    "content": "{\n  \"name\": \"@fixtures/sveltekit2\",\n  \"type\": \"module\",\n  \"devDependencies\": {\n    \"@sveltejs/kit\": \"*\",\n    \"svelte\": \"*\",\n    \"typescript\": \"*\",\n    \"vite\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit2/src/routes/+page.svelte",
    "content": "<script>\nimport { helper } from '$lib/helper';\n</script>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/sveltekit2/svelte.config.js",
    "content": "import adapter from '@sveltejs/adapter-auto';\n\n/** @type {import('@sveltejs/kit').Config} */\nconst config = {\n  kit: {\n    adapter: adapter(),\n    files: {\n      lib: 'lib',\n    },\n  },\n};\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/svgo/package.json",
    "content": "{\n  \"name\": \"@plugins/svgo\",\n  \"scripts\": {\n    \"svgo\": \"svgo --folder icons\"\n  },\n  \"dependencies\": {\n    \"svgo\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/svgo/svgo.config.js",
    "content": "module.exports = {\n  plugins: [\n    'preset-default',\n    'convertStyleToAttrs',\n    'removeOffCanvasPaths',\n    'removeScriptElement',\n    'removeStyleElement',\n    'removeDimensions',\n    'reusePaths',\n    'sortAttrs',\n  ],\n  multipass: true,\n  precision: 2,\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/svgr/.svgrrc",
    "content": "{\n  \"plugins\": [\"@svgr/plugin-jsx\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/svgr/package.json",
    "content": "{\n  \"name\": \"@plugins/svgr\",\n  \"devDependencies\": {\n    \"@svgr/cli\": \"*\",\n    \"@svgr/plugin-jsx\": \"*\"\n  },\n  \"scripts\": {\n    \"build\": \"svgr\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/swc/.swcrc",
    "content": "{\n  \"$schema\": \"https://swc.rs/schema.json\",\n  \"jsc\": {\n    \"experimental\": {\n      \"plugins\": [\n        [\n          \"@swc/plugin-styled-components\",\n          {}\n        ]\n      ]\n    },\n    \"externalHelpers\": true\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/swc/package.json",
    "content": "{\n  \"name\": \"@plugins/swc\",\n  \"scripts\": {\n    \"build\": \"swc src -d dist\"\n  },\n  \"dependencies\": {\n    \"@swc/helpers\": \"*\"\n  },\n  \"devDependencies\": {\n    \"@swc/cli\": \"*\",\n    \"@swc/core\": \"*\",\n    \"@swc/plugin-styled-components\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/syncpack/package.json",
    "content": "{\n  \"name\": \"@plugins/syncpack\",\n  \"devDependencies\": {\n    \"syncpack\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tailwind/package.json",
    "content": "{\n  \"name\": \"@plugins/tailwind\",\n  \"scripts\": {\n    \"css\": \"tailwindcss\"\n  },\n  \"devDependencies\": {\n    \"tailwindcss\": \"*\",\n    \"@tailwindcss/typography\": \"*\",\n    \"@tailwindcss/forms\": \"*\",\n    \"@acmecorp/tailwind-base\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tailwind/tailwind.config.js",
    "content": "const plugin = require('tailwindcss/plugin');\n\nmodule.exports = {\n  plugins: [plugin(() => {}), require('@tailwindcss/typography'), require('@tailwindcss/forms')],\n  presets: [require('@acmecorp/tailwind-base')],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tailwind2/index.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <link href=\"./styles.css\" rel=\"stylesheet\" />\n  </head>\n  <body></body>\n</html>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tailwind2/knip.ts",
    "content": "import { KnipConfig } from 'knip';\n\nexport default {\n  compilers: {\n    html: (text: string) =>\n      [...text.matchAll(/<link[^>]+href=\"([^\"]+)\"/g)].map(match => `import '${match[1]}';`).join('\\n'),\n    css: (text: string) =>\n      [...text.matchAll(/(?<=@)(?:import|plugin)[^;]+/g)].map(m => m[0].replace(/^plugin\\b/, 'import')).join('\\n'),\n  },\n} satisfies KnipConfig;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tailwind2/package.json",
    "content": "{\n  \"name\": \"@plugins/tailwind2\",\n  \"dependencies\": {\n    \"tailwindcss\": \"^4.0.3\"\n  },\n  \"devDependencies\": {\n    \"@tailwindcss/forms\": \"0.5.10\",\n    \"@tailwindcss/typography\": \"0.5.16\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tailwind2/styles.css",
    "content": "@import \"tailwindcss\";\n\n@source \"./src/**\";\n\n@plugin \"@tailwindcss/forms\";\n@plugin \"@tailwindcss/typography\";\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tanstack-router/package.json",
    "content": "{\n  \"name\": \"@plugins/tanstack-router\",\n  \"dependencies\": {\n    \"@tanstack/react-router\": \"*\",\n    \"react\": \"*\",\n    \"react-dom\": \"*\"\n  },\n  \"devDependencies\": {\n    \"@tanstack/router-cli\": \"*\",\n    \"typescript\": \"*\",\n    \"vite\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tanstack-router/src/routeTree.gen.ts",
    "content": "// This file is auto-generated by TanStack Router\nimport { Route as rootRoute } from './routes/__root';\nimport { Route as IndexRoute } from './routes/index';\nimport { Route as PostsPostIdRoute } from './routes/posts/$postId';\n\ndeclare module '@tanstack/react-router' {\n  interface FileRoutesByPath {\n    '/': {\n      preLoaderRoute: typeof IndexRoute;\n      parentRoute: typeof rootRoute;\n    };\n    '/posts/$postId': {\n      preLoaderRoute: typeof PostsPostIdRoute;\n      parentRoute: typeof rootRoute;\n    };\n  }\n}\n\nexport const routeTree = rootRoute.addChildren([IndexRoute, PostsPostIdRoute]);\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tanstack-router/src/routes/__root.tsx",
    "content": "import { createRootRoute, Outlet } from '@tanstack/react-router';\nimport { TanStackRouterDevtools } from '@tanstack/router-devtools';\n\nexport const Route = createRootRoute({\n  component: () => (\n    <>\n      <div>\n        <Outlet />\n      </div>\n      <TanStackRouterDevtools />\n    </>\n  ),\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tanstack-router/src/routes/index.tsx",
    "content": "import { createFileRoute } from '@tanstack/react-router';\n\nexport const Route = createFileRoute('/')({\n  component: Index,\n});\n\nfunction Index() {\n  return (\n    <div>\n      <h1>Home</h1>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tanstack-router/src/routes/posts/$postId.tsx",
    "content": "import { createFileRoute } from '@tanstack/react-router';\n\nexport const Route = createFileRoute('/posts/$postId')({\n  component: PostComponent,\n});\n\nfunction PostComponent() {\n  const { postId } = Route.useParams();\n  return <div>Post {postId}</div>;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tanstack-router/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"lib\": [\"ES2020\", \"DOM\"],\n    \"jsx\": \"react-jsx\",\n    \"strict\": true,\n    \"moduleResolution\": \"bundler\",\n    \"resolveJsonModule\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tanstack-router/tsr.config.json",
    "content": "{\n  \"routesDirectory\": \"./src/routes\",\n  \"generatedRouteTree\": \"./src/routeTree.gen.ts\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/taskfile/Taskfile.yml",
    "content": "version: '3'\n\nincludes:\n  # String shorthand format\n  nested: nested/Taskfile.yml\n  # Object format with taskfile property\n  shared:\n    taskfile: shared/Taskfile.yml\n\ntasks:\n  build:\n    cmds:\n      - npm run build\n      - node scripts/build.js\n\n  test:\n    cmd: pnpm test\n\n  lint:\n    cmds:\n      - eslint src/\n      - npx prettier --check .\n\n  dev:\n    cmd: node scripts/dev.js\n\n  deploy:\n    cmds:\n      - node scripts/deploy.js\n      - yarn publish\n\n  check:\n    cmds:\n      - node -r esbuild-register ./check.ts\n      - npx knip\n\n  # Test command object format\n  test-command-object:\n    cmds:\n      - cmd: npx test-command-object-binary\n        silent: true\n\n  # Test deferred command format\n  test-defer:\n    cmds:\n      - echo \"Starting\"\n      - defer: echo \"Cleanup\"\n      - defer:\n          cmd: npx deferred-binary\n\n  # Test for loop format\n  test-for:\n    cmds:\n      - for: [item1, item2]\n        cmd: npx for-loop-binary {{.ITEM}}\n\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/taskfile/check.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/taskfile/nested/Taskfile.yml",
    "content": "version: '3'\n\ntasks:\n  nested-task:\n    cmd: node ../scripts/nested.js\n  nested-binary:\n    cmd: npx some-nonexistent-binary\n\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/taskfile/package.json",
    "content": "{\n  \"name\": \"@plugins/taskfile\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/taskfile/scripts/build.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/taskfile/scripts/deep.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/taskfile/scripts/deploy.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/taskfile/scripts/dev.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/taskfile/scripts/nested.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/taskfile/scripts/shared.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/taskfile/shared/Taskfile.yml",
    "content": "version: '3'\n\nincludes:\n  # Deep nesting - shared includes another nested file\n  deep:\n    taskfile: deep/Taskfile.yml\n\ntasks:\n  shared-task:\n    cmd: node ../scripts/shared.js\n  shared-binary:\n    cmd: npx another-nonexistent-binary\n\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/taskfile/shared/deep/Taskfile.yml",
    "content": "version: '3'\n\ntasks:\n  deep-task:\n    cmd: node ../../scripts/deep.js\n  deep-binary:\n    cmd: npx yet-another-nonexistent-binary\n\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/taskfile2/custom-taskfile.yml",
    "content": "version: '3'\n\ntasks:\n  custom-build:\n    cmd: node scripts/custom-build.js\n  custom-test:\n    cmds:\n      - npx jest\n      - node scripts/custom-test.js\n\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/taskfile2/knip.json",
    "content": "{\n  \"taskfile\": {\n    \"config\": [\"custom-taskfile.yml\", \"tasks/*.yml\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/taskfile2/package.json",
    "content": "{\n  \"name\": \"@plugins/taskfile2\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/taskfile2/scripts/custom-build.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/taskfile2/scripts/custom-test.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/taskfile2/scripts/subtask.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/taskfile2/tasks/subtask.yml",
    "content": "version: '3'\n\ntasks:\n  subtask:\n    cmd: node ../scripts/subtask.js\n\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/travis/.travis.yml",
    "content": "sudo: false\n\nlanguage: node_js\n\nnode_js:\n  - 12\n\nbefore_deploy:\n  - ./node_modules/.bin/patch-version\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/travis/package.json",
    "content": "{\n  \"name\": \"@plugins/travis\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tsdown/entry-1.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/tsdown/entry-2.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/tsdown/entry-3.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/tsdown/entry-4.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/tsdown/entry-5.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/tsdown/package.json",
    "content": "{\n  \"name\": \"@plugins/tsdown\",\n  \"devDependencies\": {\n    \"tsdown\": \"*\"\n  },\n  \"scripts\": {\n    \"build\": \"tsdown\",\n    \"build-3\": \"tsdown --config tsdown.config-3.ts\",\n    \"build-4\": \"tsdown -c tsdown.config-4.ts\"\n  },\n  \"tsdown\": {\n    \"entry\": [\n      \"entry-5.ts\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tsdown/tsdown.config-3.ts",
    "content": "import { defineConfig } from 'tsdown';\n\nexport default defineConfig({\n  entry: ['entry-3.ts'],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tsdown/tsdown.config-4.ts",
    "content": "import { defineConfig } from 'tsdown';\n\nexport default defineConfig({\n  entry: ['entry-4.ts'],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tsdown/tsdown.config.json",
    "content": "{\n  \"entry\": [\"entry-1.ts\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tsdown/tsdown.config.ts",
    "content": "import { defineConfig } from 'tsdown';\n\nexport default defineConfig({\n  entry: ['entry-2.ts'],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tsgo/package.json",
    "content": "{\n  \"name\": \"tsgo\",\n  \"scripts\": {\n    \"build\": \"tsgo\"\n  },\n  \"devDependencies\": {\n    \"@typescript/native-preview\": \"*\",\n    \"@types/node\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tsgo/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"types\": [\"node\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tsup/entry-1.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/tsup/entry-2.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/tsup/entry-3.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/tsup/entry-4.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/tsup/entry-5.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/tsup/package.json",
    "content": "{\n  \"name\": \"@plugins/tsup\",\n  \"devDependencies\": {\n    \"tsup\": \"*\"\n  },\n  \"scripts\": {\n    \"build\": \"tsup\",\n    \"build-3\": \"tsup --config tsup.config-3.ts\",\n    \"build-4\": \"tsup -c tsup.config-4.ts\"\n  },\n  \"tsup\": {\n    \"entry\": [\n      \"entry-5.ts\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tsup/tsup.config-3.ts",
    "content": "import { defineConfig } from 'tsup';\n\nexport default defineConfig({\n  entry: ['entry-3.ts'],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tsup/tsup.config-4.ts",
    "content": "import { defineConfig } from 'tsup';\n\nexport default defineConfig({\n  entry: ['entry-4.ts'],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tsup/tsup.config.json",
    "content": "{\n  \"entry\": [\"entry-1.ts\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tsup/tsup.config.ts",
    "content": "import { defineConfig } from 'tsup';\n\nexport default defineConfig({\n  entry: ['entry-2.ts'],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tsx/package.json",
    "content": "{\n  \"name\": \"@plugins/tsx\",\n  \"devDependencies\": {\n    \"tsx\": \"*\"\n  },\n  \"scripts\": {\n    \"test\": \"tsx --test\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/tsx/test/a.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/tsx/test/dir/b.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/typedoc/package.json",
    "content": "{\n  \"name\": \"@plugins/typedoc\",\n  \"devDependencies\": {\n    \"typedoc\": \"*\"\n  },\n  \"typedocOptions\": {\n    \"plugin\": [\n      \"typedoc-plugin-umami\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/typedoc/tsconfig.json",
    "content": "{\n  \"typedocOptions\": {\n    \"plugin\": [\"typedoc-plugin-zod\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/typedoc/typedoc.json",
    "content": "{\n  \"plugin\": [\n    \"@appium/typedoc-plugin-appium\",\n    \"typedoc-plugin-expand-object-like-types\",\n    \"./dist/index.cjs\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/typescript/package.json",
    "content": "{\n  \"name\": \"@plugins/typescript\",\n  \"scripts\": {\n    \"build-base\": \"tsc -p tsconfig.base.json\",\n    \"build-ext\": \"tsc -p tsconfig.ext.json\",\n    \"build-preact\": \"tsc -p tsconfig.jsx-import-source-preact.json\",\n    \"build-react\": \"tsc -p tsconfig.jsx-import-source-react.json\",\n    \"build-jsx\": \"tsc -p tsconfig.jsx-preserve.json\"\n  },\n  \"devDependencies\": {\n    \"@tsconfig/node16\": \"*\",\n    \"@tsconfig/node20\": \"*\",\n    \"@tsconfig/node22\": \"*\",\n    \"typescript\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/typescript/tsconfig.app.json",
    "content": "{\n  \"extends\": \"@tsconfig/node22/tsconfig.json\",\n  \"compilerOptions\": {}\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/typescript/tsconfig.base.json",
    "content": "{\n  \"extends\": [\"./tsconfig.ext.json\"],\n  \"compilerOptions\": {}\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/typescript/tsconfig.ext.json",
    "content": "{\n  \"extends\": [\"@tsconfig/node20/tsconfig.json\"],\n  \"compilerOptions\": {}\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/typescript/tsconfig.json",
    "content": "{\n  \"extends\": \"@tsconfig/node16/tsconfig.json\",\n  \"compilerOptions\": {\n    \"importHelpers\": true,\n    \"paths\": {\n      \"@\": [\"../trailing\"],\n      \"~\": [\"../comma\"]\n    },\n    \"plugins\": [\n      {\n        \"name\": \"typescript-eslint-language-service\"\n      },\n      {\n        \"name\": \"ts-graphql-plugin\",\n        \"schema\": \"path-or-url-to-your-schema.graphql\",\n        \"tag\": \"gql\"\n      }\n    ]\n  },\n  \"references\": [\n    {\n      \"path\": \"./tsconfig.app.json\"\n    }\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/typescript/tsconfig.jsx-import-source-preact.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"esnext\",\n    \"module\": \"commonjs\",\n    \"jsx\": \"react-jsx\",\n    \"jsxImportSource\": \"preact\",\n    \"types\": [\"preact\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/typescript/tsconfig.jsx-import-source-react.json",
    "content": "{\n  \"compilerOptions\": {\n    \"types\": [\"vitest/globals\"],\n    \"jsx\": \"react\",\n    \"jsxImportSource\": \"hastscript/svg\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/typescript/tsconfig.jsx-preserve.json",
    "content": "{\n  \"compilerOptions\": {\n    \"jsx\": \"preserve\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/typescript/tsconfig.jsx.json",
    "content": "{\n  \"compilerOptions\": {\n    \"jsx\": \"react\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/typescript2/package.json",
    "content": "{\n  \"name\": \"@plugins/typescript2\",\n  \"workspaces\": [\n    \"packages/*\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/typescript2/packages/lib/package.json",
    "content": "{\n  \"name\": \"@plugins/typescript2__lib\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/typescript2/packages/lib/src/index.ts",
    "content": "export const lib = true;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/typescript2/packages/lib/tsconfig.json",
    "content": "{\n  \"$schema\": \"http://json.schemastore.org/tsconfig\",\n  \"extends\": \"../tsconfig.json\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/typescript2/packages/tsconfig.json",
    "content": "{\n  \"$schema\": \"http://json.schemastore.org/tsconfig\",\n  \"extends\": \"../tsconfig.json\",\n  \"references\": [{ \"path\": \"lib\" }]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/typescript2/tsconfig.json",
    "content": "{\n  \"$schema\": \"http://json.schemastore.org/tsconfig\",\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"declaration\": true,\n    \"declarationMap\": true,\n    \"incremental\": true,\n    \"lib\": [\"es2020\"],\n    \"module\": \"commonjs\",\n    \"sourceMap\": true,\n    \"strict\": true,\n    \"target\": \"es2020\",\n    \"types\": []\n  },\n  \"references\": [{ \"path\": \"packages\" }]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/unbuild/build.config.ts",
    "content": "import { defineBuildConfig } from 'unbuild';\n\nexport default defineBuildConfig({\n  entries: [\n    './src/index',\n    {\n      builder: 'mkdist',\n      input: './src/package/components/',\n      outDir: './build/components',\n    },\n  ],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/unbuild/package.json",
    "content": "{\n  \"name\": \"@plugins/unbuild\",\n  \"devDependencies\": {\n    \"unbuild\": \"^2.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/unocss/main.ts",
    "content": "import 'virtual:uno.css';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/unocss/package.json",
    "content": "{\n  \"name\": \"@plugins/unocss\",\n  \"scripts\": {\n    \"dev\": \"unocss \\\"site/{snippets,templates}/**/*.php\\\" --watch\",\n    \"build\": \"unocss \\\"site/{snippets,templates}/**/*.php\\\"\"\n  },\n  \"devDependencies\": {\n    \"@unocss/cli\": \"*\",\n    \"unocss\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/unocss/unocss.config.ts",
    "content": "import { defineConfig, presetAttributify, presetIcons, presetUno, presetWebFonts } from 'unocss';\n\nexport default defineConfig({\n  shortcuts: [\n    [\n      'btn',\n      'px-4 py-1 rounded inline-block bg-teal-600 text-white cursor-pointer hover:bg-teal-700 disabled:cursor-default disabled:bg-gray-600 disabled:opacity-50',\n    ],\n    [\n      'icon-btn',\n      'text-[0.9em] inline-block cursor-pointer select-none opacity-75 transition duration-200 ease-in-out hover:opacity-100 hover:text-teal-600 !outline-none',\n    ],\n  ],\n  presets: [\n    presetUno(),\n    presetAttributify(),\n    presetIcons({\n      scale: 1.2,\n      warn: true,\n    }),\n    presetWebFonts({\n      fonts: {\n        sans: 'DM Sans',\n        serif: 'DM Serif Display',\n        mono: 'DM Mono',\n      },\n    }),\n  ],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vercel-og/app/api/og/route.jsx",
    "content": "import { ImageResponse } from 'next/og';\n// App router includes @vercel/og.\n// No need to install it.\n\nexport const runtime = 'edge';\n\nexport async function GET() {\n  return new ImageResponse(\n    <div\n      style={{\n        fontSize: 40,\n        color: 'black',\n        background: 'white',\n        width: '100%',\n        height: '100%',\n        padding: '50px 200px',\n        textAlign: 'center',\n        justifyContent: 'center',\n        alignItems: 'center',\n      }}\n    >\n      👋 Hello\n    </div>,\n    {\n      width: 1200,\n      height: 630,\n    }\n  );\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vercel-og/package.json",
    "content": "{\n  \"name\": \"@plugins/vercel-og\",\n  \"dependencies\": {\n    \"@vercel/og\": \"*\",\n    \"next\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vercel-og/src/pages/api/og.tsx",
    "content": "import { ImageResponse } from '@vercel/og';\n\nexport const config = {\n  runtime: 'edge',\n};\n\nexport default async function handler() {\n  return new ImageResponse(\n    <div\n      style={{\n        fontSize: 40,\n        color: 'black',\n        background: 'white',\n        width: '100%',\n        height: '100%',\n        padding: '50px 200px',\n        textAlign: 'center',\n        justifyContent: 'center',\n        alignItems: 'center',\n      }}\n    >\n      👋 Hello 你好 नमस्ते こんにちは สวัสดีค่ะ 안녕 добрий день Hallá\n    </div>,\n    {\n      width: 1200,\n      height: 630,\n    }\n  );\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/package.json",
    "content": "{\n  \"name\": \"@plugins/vike\",\n  \"scripts\": {\n    \"dev\": \"npm run server:dev\",\n    \"prod\": \"npm run lint && npm run build && npm run server:prod\",\n    \"build\": \"vite build\",\n    \"server\": \"node --loader ts-node/esm ./server/index.ts\"\n  },\n  \"dependencies\": {\n    \"@types/compression\": \"^1.7.5\",\n    \"@types/express\": \"^4.17.21\",\n    \"@types/node\": \"^20.10.4\",\n    \"@types/react\": \"^18.2.42\",\n    \"@types/react-dom\": \"^18.2.17\",\n    \"@vitejs/plugin-react\": \"^4.2.1\",\n    \"compression\": \"^1.7.4\",\n    \"express\": \"^4.18.2\",\n    \"node-fetch\": \"^3.3.2\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"sirv\": \"^2.0.3\",\n    \"ts-node\": \"^10.9.1\",\n    \"typescript\": \"^5.4.0\",\n    \"vike\": \"^0.4.193\",\n    \"vite\": \"^5.4.0\"\n  },\n  \"type\": \"module\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/pages/_error/+Page.tsx",
    "content": "export { Page };\n\nimport { usePageContext } from '../../renderer/usePageContext';\n\nfunction Page() {\n  const pageContext = usePageContext();\n  let { abortReason } = pageContext;\n  if (!abortReason) {\n    abortReason = pageContext.is404 ? 'Page not found.' : 'Something went wrong.';\n  }\n  return (\n    <Center>\n      <p style={{ fontSize: '1.3em' }}>{abortReason}</p>\n    </Center>\n  );\n}\n\nfunction Center({ children }: { children: React.ReactNode }) {\n  return (\n    <div\n      style={{\n        height: 'calc(100vh - 100px)',\n        display: 'flex',\n        justifyContent: 'center',\n        alignItems: 'center',\n      }}\n    >\n      {children}\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/pages/about/+Page.tsx",
    "content": "import './code.css';\n\nexport { Page };\n\nfunction Page() {\n  return (\n    <>\n      <h1>About</h1>\n      <p>Example of using Vike.</p>\n    </>\n  );\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/pages/about/code.css",
    "content": "code {\n  font-family: monospace;\n  background-color: #eaeaea;\n  padding: 3px 5px;\n  border-radius: 4px;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/pages/index/+Page.tsx",
    "content": "export { Page };\n\nimport { Counter } from './Counter';\n\nfunction Page() {\n  return (\n    <>\n      <h1>Welcome</h1>\n      This page is:\n      <ul>\n        <li>Rendered to HTML.</li>\n        <li>\n          Interactive. <Counter />\n        </li>\n      </ul>\n    </>\n  );\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/pages/index/Counter.tsx",
    "content": "export { Counter };\n\nimport { useState } from 'react';\n\nfunction Counter() {\n  const [count, setCount] = useState(0);\n  return (\n    <button type=\"button\" onClick={() => setCount(count => count + 1)}>\n      Counter {count}\n    </button>\n  );\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/pages/star-wars/@id/+Page.tsx",
    "content": "export { Page };\n\nimport { useData } from '../../../renderer/useData';\nimport type { Data } from './+data';\n\nfunction Page() {\n  const { movie } = useData<Data>();\n  return (\n    <>\n      <h1>{movie.title}</h1>\n      Release Date: {movie.release_date}\n      <br />\n      Director: {movie.director}\n      <br />\n      Producer: {movie.producer}\n    </>\n  );\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/pages/star-wars/@id/+data.ts",
    "content": "// https://vike.dev/data\nexport { data };\nexport type Data = Awaited<ReturnType<typeof data>>;\n\n// The node-fetch package (which only works on the server-side) can be used since\n// this file always runs on the server-side, see https://vike.dev/data#server-side\nimport fetch from 'node-fetch';\nimport type { MovieDetails } from '../types';\nimport type { PageContextServer } from 'vike/types';\n\nconst data = async (pageContext: PageContextServer) => {\n  await sleep(300); // Simulate slow network\n\n  const response = await fetch(`https://brillout.github.io/star-wars/api/films/${pageContext.routeParams.id}.json`);\n  let movie = (await response.json()) as MovieDetails;\n\n  // We remove data we don't need because the data is passed to the client; we should\n  // minimize what is sent over the network.\n  movie = minimize(movie);\n\n  return {\n    movie,\n    // The page's <title>\n    title: movie.title,\n  };\n};\n\nfunction minimize(movie: MovieDetails & Record<string, unknown>): MovieDetails {\n  const { id, title, release_date, director, producer } = movie;\n  movie = { id, title, release_date, director, producer };\n  return movie;\n}\n\nfunction sleep(milliseconds: number) {\n  return new Promise(r => setTimeout(r, milliseconds));\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/pages/star-wars/index/+Page.tsx",
    "content": "export default Page;\n\nimport { useData } from '../../../renderer/useData';\nimport type { Data } from './+data';\n\nfunction Page() {\n  const { movies } = useData<Data>();\n  return (\n    <>\n      <h1>Star Wars Movies</h1>\n      <ol>\n        {movies.map(({ id, title, release_date }) => (\n          <li key={id}>\n            <a href={`/star-wars/${id}`}>{title}</a> ({release_date})\n          </li>\n        ))}\n      </ol>\n      <p>\n        Source: <a href=\"https://brillout.github.io/star-wars/\">brillout.github.io/star-wars</a>.\n      </p>\n      <p>\n        Data can be fetched by using the <code>data()</code> hook.\n      </p>\n    </>\n  );\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/pages/star-wars/index/+data.ts",
    "content": "// https://vike.dev/data\nexport { data };\nexport type Data = Awaited<ReturnType<typeof data>>;\n\n// The node-fetch package (which only works on the server-side) can be used since\n// this file always runs on the server-side, see https://vike.dev/data#server-side\nimport fetch from 'node-fetch';\nimport type { MovieDetails, Movie } from '../types';\nimport type { PageContextServer } from 'vike/types';\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst data = async (pageContext: PageContextServer) => {\n  await sleep(700); // Simulate slow network\n\n  const response = await fetch('https://brillout.github.io/star-wars/api/films.json');\n  const moviesData = (await response.json()) as MovieDetails[];\n\n  // We remove data we don't need because the data is passed to the client; we should\n  // minimize what is sent over the network.\n  const movies = minimize(moviesData);\n\n  return {\n    movies,\n    // The page's <title>\n    title: `${movies.length} Star Wars Movies`,\n  };\n};\n\nfunction minimize(movies: MovieDetails[]): Movie[] {\n  return movies.map(movie => {\n    const { title, release_date, id } = movie;\n    return { title, release_date, id };\n  });\n}\n\nfunction sleep(milliseconds: number) {\n  return new Promise(r => setTimeout(r, milliseconds));\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/pages/star-wars/types.ts",
    "content": "export type Movie = {\n  id: string;\n  title: string;\n  release_date: string;\n};\nexport type MovieDetails = Movie & {\n  director: string;\n  producer: string;\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/renderer/+config.ts",
    "content": "import type { Config } from 'vike/types';\n\n// https://vike.dev/config\nexport default {\n  // https://vike.dev/clientRouting\n  clientRouting: true,\n  // https://vike.dev/meta\n  meta: {\n    // Define new setting 'title'\n    title: {\n      env: { server: true, client: true },\n    },\n    // Define new setting 'description'\n    description: {\n      env: { server: true },\n    },\n  },\n  hydrationCanBeAborted: true,\n} satisfies Config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/renderer/+onPageTransitionEnd.ts",
    "content": "// https://vike.dev/onPageTransitionEnd\nexport { onPageTransitionEnd };\n\nimport type { OnPageTransitionEndAsync } from 'vike/types';\n\nconst onPageTransitionEnd: OnPageTransitionEndAsync = async (): ReturnType<OnPageTransitionEndAsync> => {\n  //\n  document.querySelector('body')?.classList.remove('page-is-transitioning');\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/renderer/+onPageTransitionStart.ts",
    "content": "// https://vike.dev/onPageTransitionStart\nexport { onPageTransitionStart };\n\nimport type { OnPageTransitionStartAsync } from 'vike/types';\n\nconst onPageTransitionStart: OnPageTransitionStartAsync = async (): ReturnType<OnPageTransitionStartAsync> => {\n  document.querySelector('body')?.classList.add('page-is-transitioning');\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/renderer/+onRenderClient.tsx",
    "content": "// https://vike.dev/onRenderClient\nexport { onRenderClient };\n\nimport ReactDOM from 'react-dom/client';\nimport { Layout } from './Layout';\nimport { getPageTitle } from './getPageTitle';\nimport type { OnRenderClientAsync } from 'vike/types';\n\nlet root: ReactDOM.Root;\nconst onRenderClient: OnRenderClientAsync = async (pageContext): ReturnType<OnRenderClientAsync> => {\n  const { Page } = pageContext;\n\n  // This onRenderClient() hook only supports SSR, see https://vike.dev/render-modes for how to modify onRenderClient()\n  // to support SPA\n  if (!Page) throw new Error('My onRenderClient() hook expects pageContext.Page to be defined');\n\n  const container = document.getElementById('react-root');\n  if (!container) throw new Error('DOM element #react-root not found');\n\n  const page = (\n    <Layout pageContext={pageContext}>\n      <Page />\n    </Layout>\n  );\n  if (pageContext.isHydration) {\n    root = ReactDOM.hydrateRoot(container, page);\n  } else {\n    if (!root) {\n      root = ReactDOM.createRoot(container);\n    }\n    root.render(page);\n  }\n  document.title = getPageTitle(pageContext);\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/renderer/+onRenderHtml.tsx",
    "content": "// https://vike.dev/onRenderHtml\nexport { onRenderHtml };\n\nimport ReactDOMServer from 'react-dom/server';\nimport { Layout } from './Layout';\nimport { escapeInject, dangerouslySkipEscape } from 'vike/server';\nimport logoUrl from './logo.svg';\nimport type { OnRenderHtmlAsync } from 'vike/types';\nimport { getPageTitle } from './getPageTitle';\n\nconst onRenderHtml: OnRenderHtmlAsync = async (pageContext): ReturnType<OnRenderHtmlAsync> => {\n  const { Page } = pageContext;\n\n  // This onRenderHtml() hook only supports SSR, see https://vike.dev/render-modes for how to modify\n  // onRenderHtml() to support SPA\n  if (!Page) throw new Error('My onRenderHtml() hook expects pageContext.Page to be defined');\n\n  // Alternatively, we can use an HTML stream, see https://vike.dev/streaming\n  const pageHtml = ReactDOMServer.renderToString(\n    <Layout pageContext={pageContext}>\n      <Page />\n    </Layout>\n  );\n\n  const title = getPageTitle(pageContext);\n  const desc = pageContext.data?.description || pageContext.config.description || 'Demo of using Vike';\n\n  const documentHtml = escapeInject`<!DOCTYPE html>\n    <html lang=\"en\">\n      <head>\n        <meta charset=\"UTF-8\" />\n        <link rel=\"icon\" href=\"${logoUrl}\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n        <meta name=\"description\" content=\"${desc}\" />\n        <title>${title}</title>\n      </head>\n      <body>\n        <div id=\"react-root\">${dangerouslySkipEscape(pageHtml)}</div>\n      </body>\n    </html>`;\n\n  return {\n    documentHtml,\n    pageContext: {\n      // We can add custom pageContext properties here, see https://vike.dev/pageContext#custom\n    },\n  };\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/renderer/Layout.css",
    "content": "#sidebar a {\n  padding: 2px 10px;\n  margin-left: -10px;\n}\n#sidebar a.is-active {\n  background-color: #eee;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/renderer/Layout.tsx",
    "content": "export { Layout };\n\nimport React from 'react';\nimport logoUrl from './logo.svg';\nimport { PageContextProvider } from './usePageContext';\nimport { Link } from './Link';\nimport type { PageContext } from 'vike/types';\nimport './css/index.css';\nimport './Layout.css';\n\nfunction Layout({ children, pageContext }: { children: React.ReactNode; pageContext: PageContext }) {\n  return (\n    <React.StrictMode>\n      <PageContextProvider pageContext={pageContext}>\n        <Frame>\n          <Sidebar>\n            <Logo />\n            <Link href=\"/\">Welcome</Link>\n            <Link href=\"/about\">About</Link>\n            <Link href=\"/star-wars\">Data Fetching</Link>\n          </Sidebar>\n          <Content>{children}</Content>\n        </Frame>\n      </PageContextProvider>\n    </React.StrictMode>\n  );\n}\n\nfunction Frame({ children }: { children: React.ReactNode }) {\n  return (\n    <div\n      style={{\n        display: 'flex',\n        maxWidth: 900,\n        margin: 'auto',\n      }}\n    >\n      {children}\n    </div>\n  );\n}\n\nfunction Sidebar({ children }: { children: React.ReactNode }) {\n  return (\n    <div\n      id=\"sidebar\"\n      style={{\n        padding: 20,\n        flexShrink: 0,\n        display: 'flex',\n        flexDirection: 'column',\n        lineHeight: '1.8em',\n        borderRight: '2px solid #eee',\n      }}\n    >\n      {children}\n    </div>\n  );\n}\n\nfunction Content({ children }: { children: React.ReactNode }) {\n  return (\n    <div id=\"page-container\">\n      <div\n        id=\"page-content\"\n        style={{\n          padding: 20,\n          paddingBottom: 50,\n          minHeight: '100vh',\n        }}\n      >\n        {children}\n      </div>\n    </div>\n  );\n}\n\nfunction Logo() {\n  return (\n    <div\n      style={{\n        marginTop: 20,\n        marginBottom: 10,\n      }}\n    >\n      <a href=\"/\">\n        <img src={logoUrl} height={64} width={64} alt=\"logo\" />\n      </a>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/renderer/Link.tsx",
    "content": "import { usePageContext } from './usePageContext';\n\nexport { Link };\n\nfunction Link(props: { href: string; className?: string; children: React.ReactNode }) {\n  const pageContext = usePageContext();\n  const { urlPathname } = pageContext;\n  const { href } = props;\n  const isActive = href === '/' ? urlPathname === href : urlPathname.startsWith(href);\n  const className = [props.className, isActive && 'is-active'].filter(Boolean).join(' ');\n  return <a {...props} className={className} />;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/renderer/getPageTitle.ts",
    "content": "export { getPageTitle };\n\nimport type { PageContext } from 'vike/types';\n\nfunction getPageTitle(pageContext: PageContext): string {\n  const title =\n    // Title defined dynamically by data()\n    pageContext.data?.title ||\n    // Title defined statically by /pages/some-page/+title.js (or by `export default { title }` in /pages/some-page/+config.js)\n    // The setting 'pageContext.config.title' is a custom setting we defined at ./+config.ts\n    pageContext.config.title ||\n    'Vike Demo';\n  return title;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/renderer/useData.ts",
    "content": "// https://vike.dev/useData\nexport { useData };\n\nimport { usePageContext } from './usePageContext';\n\n/** https://vike.dev/useData */\nfunction useData<Data>() {\n  const { data } = usePageContext();\n  return data as Data;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/renderer/usePageContext.tsx",
    "content": "// https://vike.dev/usePageContext\n// eslint-disable-next-line react-refresh/only-export-components\nexport { usePageContext };\nexport { PageContextProvider };\n\nimport React, { useContext } from 'react';\nimport type { PageContext } from 'vike/types';\n\nconst Context = React.createContext<PageContext>(undefined as unknown as PageContext);\n\nfunction PageContextProvider({ pageContext, children }: { pageContext: PageContext; children: React.ReactNode }) {\n  return <Context.Provider value={pageContext}>{children}</Context.Provider>;\n}\n\n/** https://vike.dev/usePageContext */\nfunction usePageContext() {\n  const pageContext = useContext(Context);\n  return pageContext;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/server/index.ts",
    "content": "// This file isn't processed by Vite, see https://github.com/vikejs/vike/issues/562\n// Consequently:\n//  - When changing this file, you needed to manually restart your server for your changes to take effect.\n//  - To use your environment variables defined in your .env files, you need to install dotenv, see https://vike.dev/env\n//  - To use your path aliases defined in your vite.config.js, you need to tell Node.js about them, see https://vike.dev/path-aliases\n\n// If you want Vite to process your server code then use one of these:\n//  - vavite (https://github.com/cyco130/vavite)\n//     - See vavite + Vike examples at https://github.com/cyco130/vavite/tree/main/examples\n//  - vite-node (https://github.com/antfu/vite-node)\n//  - HatTip (https://github.com/hattipjs/hattip)\n//    - You can use Bati (https://batijs.dev/) to scaffold a Vike + HatTip app. Note that Bati generates apps that use the V1 design (https://vike.dev/migration/v1-design) and Vike packages (https://vike.dev/vike-packages)\n\nimport express from 'express';\nimport compression from 'compression';\nimport { renderPage } from 'vike/server';\nimport { root } from './root.js';\nconst isProduction = process.env.NODE_ENV === 'production';\n\nstartServer();\n\nasync function startServer() {\n  const app = express();\n\n  app.use(compression());\n\n  // Vite integration\n  if (isProduction) {\n    // In production, we need to serve our static assets ourselves.\n    // (In dev, Vite's middleware serves our static assets.)\n    const sirv = (await import('sirv')).default;\n    app.use(sirv(`${root}/dist/client`));\n  } else {\n    // We instantiate Vite's development server and integrate its middleware to our server.\n    // ⚠️ We instantiate it only in development. (It isn't needed in production and it\n    // would unnecessarily bloat our production server.)\n    const vite = await import('vite');\n    const viteDevMiddleware = (\n      await vite.createServer({\n        root,\n        server: { middlewareMode: true },\n      })\n    ).middlewares;\n    app.use(viteDevMiddleware);\n  }\n\n  // ...\n  // Other middlewares (e.g. some RPC middleware such as Telefunc)\n  // ...\n\n  // Vike middleware. It should always be our last middleware (because it's a\n  // catch-all middleware superseding any middleware placed after it).\n  app.get('*', async (req, res, next) => {\n    const pageContextInit = {\n      urlOriginal: req.originalUrl,\n      headersOriginal: req.headers,\n    };\n    const pageContext = await renderPage(pageContextInit);\n    if (pageContext.errorWhileRendering) {\n      // Install error tracking here, see https://vike.dev/errors\n    }\n    const { httpResponse } = pageContext;\n    if (!httpResponse) {\n      return next();\n    }\n    const { body, statusCode, headers, earlyHints } = httpResponse;\n    if (res.writeEarlyHints) res.writeEarlyHints({ link: earlyHints.map(e => e.earlyHintLink) });\n    headers.forEach(([name, value]) => res.setHeader(name, value));\n    res.status(statusCode);\n    // For HTTP streams use httpResponse.pipe() instead, see https://vike.dev/streaming\n    res.send(body);\n  });\n\n  const port = process.env.PORT || 3000;\n  app.listen(port);\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/server/root.ts",
    "content": "export { root };\n\nimport { dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst root = `${__dirname}/..`;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/server/tsconfig.json",
    "content": "{\n  \"extends\": \"../tsconfig.json\",\n  // Make IDEs complain about missing file extension .js in import paths.\n  // Alternatively, we could always set \"module\" to \"Node16\" and add the file extension .js to import paths everywhere.\n  \"compilerOptions\": {\n    \"module\": \"Node16\",\n    \"moduleResolution\": \"Node16\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vike/vite.config.ts",
    "content": "import react from '@vitejs/plugin-react';\nimport vike from 'vike/plugin';\nimport { UserConfig } from 'vite';\n\nconst config: UserConfig = {\n  plugins: [react(), vike()],\n};\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vite/package.json",
    "content": "{\n  \"name\": \"@plugins/vite\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\"\n  },\n  \"devDependencies\": {\n    \"@vitejs/plugin-react\": \"*\",\n    \"@emotion/babel-plugin\": \"*\",\n    \"vite\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vite/src/vite-env.d.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/vite/vite.config.ts",
    "content": "import react from '@vitejs/plugin-react';\nimport { defineConfig } from 'vite';\n\nexport default defineConfig({\n  plugins: [\n    react({\n      babel: {\n        plugins: ['@emotion/babel-plugin'],\n      },\n    }),\n  ],\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vite2/package.json",
    "content": "{\n  \"name\": \"@plugins/vite2\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\"\n  },\n  \"devDependencies\": {\n    \"@vitejs/plugin-react\": \"*\",\n    \"@emotion/babel-plugin\": \"*\",\n    \"vite\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vite2/src/mock.desktop.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/vite2/src/mock.desktop.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/vite2/vite.config.ts",
    "content": "import { defineConfig } from 'vite';\n\n// https://vite.dev/config/\nconst isDesktop = true;\nfunction getExtensions() {\n  const extensions = ['.js', '.ts', '.tsx', '.json'];\n  if (isDesktop) {\n    return [...extensions.map(ext => `.desktop${ext}`), ...extensions];\n  }\n\n  return extensions;\n}\n\nexport default defineConfig({\n  resolve: {\n    extensions: getExtensions(),\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vite3/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite App</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"/src/App.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vite3/package.json",
    "content": "{\n  \"name\": \"@plugins/vite3\",\n  \"scripts\": {\n    \"dev\": \"vite\"\n  },\n  \"devDependencies\": {\n    \"vite\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vite3/src/App.tsx",
    "content": "export const App = () => null;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vite3/src/unused.ts",
    "content": "export const unused = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vite4/app/component.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/vite4/app/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <title>Vite App</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"./main.ts\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vite4/app/main.ts",
    "content": "import './component.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vite4/package.json",
    "content": "{\n  \"name\": \"@plugins/vite4\",\n  \"scripts\": {\n    \"dev\": \"vite\"\n  },\n  \"devDependencies\": {\n    \"vite\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vite4/vite.config.ts",
    "content": "import { defineConfig } from 'vite';\n\nexport default defineConfig({\n  root: 'app',\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitepress/.vitepress/config.mts",
    "content": "import { defineConfig } from 'vitepress';\n\n// https://vitepress.dev/reference/site-config\nexport default defineConfig({\n  title: 'My Awesome Project',\n  description: 'A VitePress Site',\n  themeConfig: {\n    // https://vitepress.dev/reference/default-theme-config\n    nav: [{ text: 'Home', link: '/' }],\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitepress/.vitepress/theme/index.ts",
    "content": "import DefaultTheme from 'vitepress/theme';\n\nexport default {\n  extends: DefaultTheme,\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitepress/index.md",
    "content": "---\n# https://vitepress.dev/reference/default-theme-home-page\nlayout: home\n\nhero:\n  name: \"My Awesome Project\"\n  text: \"A VitePress Site\"\n  tagline: My great project tagline\n  actions:\n    - theme: brand\n      text: Markdown Examples\n      link: /markdown-examples\n    - theme: alt\n      text: API Examples\n      link: /api-examples\n\nfeatures:\n  - title: Feature A\n    details: Lorem ipsum dolor sit amet, consectetur adipiscing elit\n  - title: Feature B\n    details: Lorem ipsum dolor sit amet, consectetur adipiscing elit\n  - title: Feature C\n    details: Lorem ipsum dolor sit amet, consectetur adipiscing elit\n---\n\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitepress/package.json",
    "content": "{\n  \"name\": \"@plugins/vitepress\",\n  \"dependencies\": {\n    \"vitepress\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest/__mocks__/mockedModule.ts",
    "content": "export default {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest/package.json",
    "content": "{\n  \"name\": \"@plugins/vitest\",\n  \"scripts\": {\n    \"test\": \"vitest --config vitest-default-coverage.config.ts\",\n    \"test:ui\": \"vitest --ui\",\n    \"test:coverage\": \"vitest --coverage.provider istanbul\",\n    \"test:reporter\": \"vitest --reporter vitest-sonar-reporter\",\n    \"test:reporter:builtin\": \"vitest --reporter verbose\",\n    \"test:env\": \"vitest --environment jsdom\",\n    \"test:typecheck\": \"vitest --typecheck.checker vue-tsc\",\n    \"test:typecheck:tsc\": \"vitest --typecheck.checker tsc\"\n  },\n  \"devDependencies\": {\n    \"vitest\": \"*\",\n    \"@vitest/ui\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest/src/setupTests.ts",
    "content": "export default {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest/src/vite-env.d.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest/test/basic.bench.ts",
    "content": "import { bench } from 'vitest';\n\nbench('Math.sqrt()', () => {\n  Math.sqrt(4);\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest/test/basic.spec-d.ts",
    "content": "import { expectTypeOf, test } from 'vitest';\n\ntest('Type A', () => {\n  expectTypeOf(Math.sqrt(4)).toEqualTypeOf<number>();\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest/test/basic.spec.ts",
    "content": "import { assert, expect, test } from 'vitest';\n\ntest('Math.sqrt()', () => {\n  expect(Math.sqrt(4)).toBe(2);\n});\n\ntest('JSON', () => {\n  const input = {};\n  assert.deepEqual(JSON.parse(JSON.stringify(input)), input, 'matches original');\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest/test/basic.test-d.ts",
    "content": "import { expectTypeOf, test } from 'vitest';\n\ntest('Type B', () => {\n  expectTypeOf(Math.sqrt(4)).toEqualTypeOf<number>();\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest/vite.config.ts",
    "content": "import { defineConfig } from 'vitest/config';\n\nexport default defineConfig(async ({ mode, command }) => {\n  if (mode === 'development') {\n    return {\n      test: {\n        coverage: {\n          reporter: ['html', 'lcov'],\n          provider: 'c8',\n        },\n      },\n    };\n  }\n  if (mode === 'production') {\n    return {\n      test: {\n        environment: 'edge-runtime',\n      },\n    };\n  }\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest/vitest-default-coverage.config.ts",
    "content": "import { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n  test: {\n    environment: 'jsdom',\n    coverage: {\n      enabled: true,\n      all: true,\n    },\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest/vitest.config.ts",
    "content": "import { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n  test: {\n    environment: 'happy-dom',\n    setupFiles: ['setup.js', './src/setupTests.ts'],\n    coverage: {\n      reporter: ['html', 'lcov'],\n      provider: 'istanbul',\n    },\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest/vitest.workspace.ts",
    "content": "import { defineWorkspace } from 'vitest/config';\n\nexport default defineWorkspace([\n  'apps/*',\n  'packages/*',\n  {\n    test: {\n      include: ['tests/**/*.{edge}.test.{ts,js}'],\n      name: 'edge',\n      environment: 'edge-runtime',\n    },\n  },\n]);\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest-npm-script/package.json",
    "content": "{\n  \"name\": \"@plugins/vitest-npm-script\",\n  \"scripts\": {\n    \"test:coverage\": \"vitest --coverage\"\n  },\n  \"devDependencies\": {\n    \"vitest\": \"^0.34.6\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest-npm-script/vitest.config.ts",
    "content": "export default {\n  test: {\n    coverage: {\n      enabled: false,\n      provider: 'v8',\n    },\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest2/package.json",
    "content": "{\n  \"name\": \"@plugins/vitest2\",\n  \"devDependencies\": {\n    \"vitest\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest2/src/basic.spec.ts",
    "content": "import { expect, test } from 'vitest';\n\ntest('Unit A', () => {\n  expect(true).toBe(!false);\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest2/src/unit.vitest.ts",
    "content": "import { expect, test } from 'vitest';\n\ntest('Unit A', () => {\n  expect(true).toBe(!false);\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest2/vitest.config.ts",
    "content": "import { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n  test: {\n    include: ['**/*.vitest.ts'],\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest3/knip.ts",
    "content": "const config = {\n  workspaces: {\n    '.': {\n      husky: {\n        config: ['.husky/pre-commit'],\n      },\n    },\n  },\n};\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest3/package.json",
    "content": "{\n  \"name\": \"@plugins/vitest3\",\n  \"devDependencies\": {\n    \"@swc/plugin-styled-components\": \"*\",\n    \"@vitejs/plugin-react-swc\": \"*\",\n    \"vite\": \"*\",\n    \"vite-plugin-checker\": \"*\",\n    \"vite-plugin-svgr\": \"*\",\n    \"vitest\": \"^0.34.6\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest3/src/basic.test.ts",
    "content": "import { expect, test } from 'vitest';\n\ntest('Unit A', () => {\n  expect(true).toBe(!false);\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest3/src/unit.test.tsx",
    "content": "import { expect, test } from 'vitest';\n\ntest('Unit A', () => {\n  expect(true).toBe(!false);\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest3/vite.config.ts",
    "content": "/// <reference types=\"vitest\" />\nimport react from '@vitejs/plugin-react-swc';\nimport { defineConfig } from 'vite';\nimport svgr from 'vite-plugin-svgr';\nimport checker from 'vite-plugin-checker';\n\nexport default defineConfig(() => {\n  return {\n    root: './',\n    server: {\n      proxy: {\n        '/api': {\n          target: 'http://localhost:5000',\n          changeOrigin: true,\n          cookieDomainRewrite: '',\n        },\n      },\n    },\n    plugins: [\n      react({ plugins: [['@swc/plugin-styled-components', {}]] }),\n      svgr(),\n      checker({\n        typescript: true,\n      }),\n    ],\n  };\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest3/vitest.config.ts",
    "content": "import { defineConfig, mergeConfig } from 'vitest/config';\nimport viteConfig from './vite.config';\n\nexport default defineConfig(configEnv =>\n  mergeConfig(\n    viteConfig(configEnv),\n    defineConfig({\n      test: {\n        environment: 'jsdom',\n        globals: true,\n        include: ['**/*.test.{ts,tsx}'],\n        setupFiles: ['./src/setupTests.tsx'],\n        reporters: [\n          'basic',\n          'verbose',\n          'dot',\n          'junit',\n          'json',\n          'html',\n          'tap',\n          'tap-flat',\n          'hanging-process',\n          'github-actions',\n        ],\n        mockReset: true,\n        coverage: {\n          provider: 'v8',\n          all: true,\n          include: ['src'],\n          exclude: [\n            '**/*.test.*',\n            '**/*.d.ts',\n            '**/types',\n            '**/*.pact.ts',\n            '**/queries/*',\n            '**/testUtils.tsx',\n            '**/setupTests.tsx',\n            '**/pactUtils.ts',\n          ],\n          reporter: ['text', 'json', 'lcov', 'html'],\n        },\n      },\n    })\n  )\n);\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest4/package.json",
    "content": "{\n  \"name\": \"@plugins/vitest4\",\n  \"devDependencies\": {\n    \"vitest\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest4/src/unused.test.ts",
    "content": "import { expect, test } from 'vitest';\n\ntest('Unit A', () => {\n  expect(true).toBe(!false);\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest4/tests/adder.test.ts",
    "content": "import { expect, test } from 'vitest';\n\ntest('Unit A', () => {\n  expect(true).toBe(!false);\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest4/tests/setup.ts",
    "content": "export default {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest4/vitest.config.ts",
    "content": "import { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n  test: {\n    root: 'tests',\n    include: ['*.test.ts'],\n    setupFiles: ['./setup.ts'],\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest5/package.json",
    "content": "{\n  \"name\": \"@plugins/vitest5\",\n  \"devDependencies\": {\n    \"vitest\": \"*\",\n    \"@vitest/coverage-istanbul\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest5/src/index.test.ts",
    "content": "import { expect, test } from 'vitest';\n\ntest('Unit A', () => {\n  expect(true).toBe(!false);\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest5/src/index.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest5/vite.config.ts",
    "content": "import { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n  test: {\n    coverage: {\n      provider: 'istanbul',\n    },\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest6/config/fiep.ts",
    "content": "import { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n  test: {\n    root: './tests',\n    include: ['*.test.ts'],\n    setupFiles: ['../config/setup.ts'],\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest6/config/setup.ts",
    "content": "export default {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest6/package.json",
    "content": "{\n  \"name\": \"@plugins/vitest6\",\n  \"scripts\": {\n    \"test\": \"vitest -c config/fiep.ts\"\n  },\n  \"devDependencies\": {\n    \"vitest\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest6/src/unused.test.ts",
    "content": "import { expect, test } from 'vitest';\n\ntest('Unit A', () => {\n  expect(true).toBe(!false);\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest6/tests/adder.test.ts",
    "content": "import { expect, test } from 'vitest';\n\ntest('Unit A', () => {\n  expect(true).toBe(!false);\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest7/package.json",
    "content": "{\n  \"name\": \"@plugins/vitest7\",\n  \"devDependencies\": {\n    \"vitest\": \"*\",\n    \"ReporterString\": \"*\",\n    \"ReporterArray\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest7/src/index.test.ts",
    "content": "import { expect, test } from 'vitest';\n\ntest('Unit A', () => {\n  expect(true).toBe(!false);\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest7/src/index.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest7/vitest.config.ts",
    "content": "import { defineConfig } from 'vitest/config';\n\nclass ReporterClass {}\n\nexport default defineConfig({\n  test: {\n    reporters: ['ReporterString', ['ReporterArray', { options: {} }], ReporterClass],\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest8/my-env.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest8/package.json",
    "content": "{\n  \"name\": \"@plugins/vitest8\",\n  \"devDependencies\": {\n    \"vitest\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest8/src/custom-env.test.ts",
    "content": "// @vitest-environment ../my-env\nimport { expect, test } from 'vitest';\n\ntest('Unit A', () => {\n  expect(true).toBe(!false);\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest8/src/index.test.ts",
    "content": "// @vitest-environment node\nimport { expect, test } from 'vitest';\n\ntest('Unit A', () => {\n  expect(true).toBe(!false);\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest8/src/index.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest8/vitest.config.ts",
    "content": "import { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n  test: {\n    workspace: [\n      {\n        test: {\n          name: 'integration',\n          setupFiles: './vitest.integration.setup.mjs',\n        },\n      },\n      {\n        test: {\n          globalSetup: './vitest.unit.setup.ts',\n          name: 'unit',\n        },\n      },\n    ],\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest9/package.json",
    "content": "{\n  \"name\": \"@fixtures/vitest9\",\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"devDependencies\": {\n    \"vitest\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest9/packages/client/e2e/another-setup.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest9/packages/client/e2e/client.test.ts",
    "content": "import { test, expect } from 'vitest';\n\ntest('client e2e test', () => {\n  expect(true).toBe(true);\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest9/packages/client/e2e-setup.ts",
    "content": "export function setup() {\n  // E2E test setup\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest9/packages/client/package.json",
    "content": "{\n  \"name\": \"@fixtures/vitest9-client\",\n  \"devDependencies\": {\n    \"vitest\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest9/packages/client/vitest.config.e2e.ts",
    "content": "export default {\n  test: {\n    name: 'client-e2e',\n    include: ['e2e/**/*.test.ts'],\n    setupFiles: ['./e2e-setup.ts', 'e2e/another-setup.js'],\n    environment: 'happy-dom',\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest9/src/example.test.ts",
    "content": "// Unit test file\nimport { describe, it, expect } from 'vitest';\n\ndescribe('example unit test', () => {\n  it('should work', () => {\n    expect(1 + 1).toBe(2);\n  });\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest9/src/unit.setup.ts",
    "content": "export function setup() {\n  // Unit test setup\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vitest9/vitest.config.ts",
    "content": "export default {\n  test: {\n    projects: [\n      {\n        name: 'unit',\n        test: {\n          include: ['src/**/*.test.ts'],\n          setupFiles: ['./src/unit.setup.ts'],\n          environment: 'jsdom',\n        },\n      },\n      'packages/*/vitest.config.e2e.ts',\n    ],\n    coverage: {\n      enabled: false,\n    },\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vue/index.vue",
    "content": "<script lang=\"ts\">\nimport { defineComponent } from 'vue';\n</script>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vue/knip.ts",
    "content": "const compiler = /<script\\b[^>]*>([\\s\\S]*?)<\\/script>/gm;\n\nexport default {\n  compilers: {\n    vue: text => {\n      const scripts = [];\n      let match: any[] | null;\n      while ((match = compiler.exec(text))) scripts.push(match[1]);\n      return scripts.join(';');\n    },\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vue/package.json",
    "content": "{\n  \"name\": \"@plugins/vue\",\n  \"dependencies\": {\n    \"vue\": \"*\"\n  },\n  \"devDependencies\": {\n    \"@vue/cli-service\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vue/vue.config.js",
    "content": "const { defineConfig } = require('@vue/cli-service');\n\nmodule.exports = defineConfig({\n  transpileDependencies: true,\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vue-webpack/child1.vue",
    "content": "<template></template>\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\nexport default defineComponent({});\n</script>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vue-webpack/child2.vue",
    "content": "<template></template>\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\nexport default defineComponent({});\n</script>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vue-webpack/child3.vue",
    "content": "<template></template>\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\nexport default defineComponent({});\n</script>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vue-webpack/knip.ts",
    "content": "export default {\n  entry: ['parent.vue'],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vue-webpack/package.json",
    "content": "{\n  \"name\": \"@plugins/vue-webpack\",\n  \"dependencies\": {\n    \"vue\": \"*\"\n  },\n  \"devDependencies\": {\n    \"@vue/cli-service\": \"*\",\n    \"babel-loader\": \"*\",\n    \"circular-dependency-plugin\": \"*\",\n    \"graphql-tag\": \"*\",\n    \"raw-loader\": \"*\",\n    \"webpack\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vue-webpack/parent.vue",
    "content": "<script lang=\"ts\">\nimport { defineComponent, defineAsyncComponent } from 'vue';\n\nconst child1 = defineAsyncComponent(() => import('./child1.vue'));\n\nexport default defineComponent({\n  components: {\n    child1,\n    child2: defineAsyncComponent(() => import('./child2.vue')),\n  },\n});\n</script>\n\n<script>\nfunction showDialog(componentImport) {\n  Dialog.create({\n    component: defineAsyncComponent(componentImport),\n  });\n}\n\nshowDialog(() => import('./child3.vue'));\nshowDialog(defineAsyncComponent(() => import('./child3.vue')));\n</script>\n\n<template>\n  <child1></child1>\n  <child2></child2>\n</template>\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vue-webpack/tsconfig.json",
    "content": "{}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/vue-webpack/vue.config.js",
    "content": "const CircularDependencyPlugin = require('circular-dependency-plugin');\nconst webpack = require('webpack');\nconst { defineConfig } = require('@vue/cli-service');\n\nmodule.exports = defineConfig({\n  css: {\n    loaderOptions: {\n      scss: {\n        additionalData: `\n          @use 'sass:math';\n          @import \"@/styles/config.scss\";\n        `,\n      },\n    },\n    extract: {\n      ignoreOrder: true,\n    },\n  },\n  configureWebpack: config => {\n    config.resolve.fallback = {\n      fs: false,\n    };\n\n    config.plugins.push(new CircularDependencyPlugin({}));\n\n    config.module.rules.push({\n      test: /\\.md?$/,\n      use: ['babel-loader', 'raw-loader'],\n    });\n\n    config.module.rules.push({\n      test: /\\.gql$/,\n      use: 'graphql-tag/loader',\n    });\n\n    config.resolve.symlinks = false;\n\n    config.plugins.push(\n      new webpack.DefinePlugin({\n        'import.meta.env.APP_VERSION': JSON.stringify(require('./package.json').version),\n      })\n    );\n  },\n  chainWebpack: config => {\n    config.resolve.symlinks(false);\n    config.resolve.alias.set('vue', '@vue/compat');\n  },\n  devServer: {\n    port: 3000,\n    client: {\n      logging: 'info',\n      progress: false,\n    },\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/webdriver-io/package.json",
    "content": "{\n  \"name\": \"@plugins/webdriver-io\",\n  \"scripts\": {\n    \"test\": \"wdio\"\n  },\n  \"dependencies\": {\n    \"@wdio/cli\": \"*\",\n    \"@wdio/mocha-framework\": \"*\",\n    \"@wdio/concise-reporter\": \"*\",\n    \"@wdio/browser-runner\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/webdriver-io/wdio.conf.js",
    "content": "exports.config = {\n  runner: ['browser', {}],\n  framework: 'mocha',\n  reporters: ['concise'],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack/merge.js",
    "content": "module.exports = function dumbMerge(c1, c2) {\n  c1.mode = c2.mode;\n  c1.entry = c2.entry;\n  c1.plugins.push(...c2.plugins);\n  c1.module.rules.push(...c2.module.rules);\n  c1.optimization.minimizer = c2.optimization?.minimizer ?? c1.optimization.minimizer;\n  return c1;\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack/package.json",
    "content": "{\n  \"name\": \"@plugins/webpack\",\n  \"imports\": {\n    \"#subpath/*\": \"./src/*\"\n  },\n  \"scripts\": {\n    \"build\": \"webpack\",\n    \"dev\": \"webpack serve\"\n  },\n  \"dependencies\": {},\n  \"devDependencies\": {\n    \"@babel/plugin-proposal-class-properties\": \"*\",\n    \"@babel/plugin-proposal-object-rest-spread\": \"*\",\n    \"@babel/plugin-transform-react-jsx\": \"*\",\n    \"@babel/preset-env\": \"*\",\n    \"@babel/preset-typescript\": \"*\",\n    \"babel-loader\": \"*\",\n    \"babel-plugin-macros\": \"*\",\n    \"babel-plugin-styled-components\": \"*\",\n    \"base64-inline-loader\": \"*\",\n    \"buffer\": \"*\",\n    \"copy-webpack-plugin\": \"*\",\n    \"css-loader\": \"*\",\n    \"esbuild\": \"*\",\n    \"esbuild-loader\": \"*\",\n    \"file-loader\": \"*\",\n    \"html-minimizer-webpack-plugin\": \"*\",\n    \"jquery\": \"*\",\n    \"less-loader\": \"*\",\n    \"lodash\": \"*\",\n    \"mini-css-extract-plugin\": \"*\",\n    \"module1\": \"*\",\n    \"module2\": \"*\",\n    \"svg-url-loader\": \"*\",\n    \"ts-loader\": \"*\",\n    \"url-loader\": \"*\",\n    \"vue\": \"*\",\n    \"webpack\": \"*\",\n    \"webpack-cli\": \"*\",\n    \"webpack-dev-server\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack/src/app-dep.ts",
    "content": "export default {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack/src/app.tsx",
    "content": "import dep from './app-dep';\nimport '!my-loader!./app-dep';\ndep;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack/src/components/button.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack/src/components/input.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack/src/entry.js",
    "content": "const components = require.context('./components', true, /\\.js$/);\ncomponents;\n\nconst routes = require.context('.', false, /\\.\\/routes\\.ts$/);\nroutes;\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack/src/module1.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack/src/my-custom-loader.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack/src/routes.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack/src/subpath-entry.ts",
    "content": "export const subpathEntry = 'subpath-entry';\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack/src/unused.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack/src/vendor.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack/webpack.babel.js",
    "content": "/** @type {import(\"webpack\").Configuration} */\nmodule.exports = {\n  mode: 'none',\n  entry: {\n    eslint: ['core-js/stable', 'regenerator-runtime/runtime', './lib/linter/linter.js'],\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.m?js$/u,\n        loader: 'babel-loader',\n        options: {\n          presets: [['@babel/preset-env', {}]],\n        },\n      },\n    ],\n  },\n  resolve: {\n    mainFields: ['browser', 'main', 'module'],\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack/webpack.common.js",
    "content": "const path = require('node:path');\nconst esbuild = require('esbuild');\nconst HtmlMinimizerPlugin = require('html-minimizer-webpack-plugin');\nconst MiniCssExtractPlugin = require('mini-css-extract-plugin');\nconst webpack = require('webpack');\n\nmodule.exports = () => {\n  return {\n    module: {\n      rules: [\n        { test: /\\.svg/, use: 'svg-url-loader' },\n        {\n          test: /\\.(js|ts)$/,\n          exclude: /node_modules/,\n          use: {\n            loader: 'babel-loader',\n            options: {\n              presets: [['@babel/preset-env'], ['@babel/typescript', { jsxPragma: 'h' }]],\n              plugins: [\n                '@babel/proposal-class-properties',\n                [\n                  '@babel/plugin-transform-react-jsx',\n                  {\n                    pragma: 'h',\n                    pragmaFrag: 'Fragment',\n                  },\n                ],\n                'babel-plugin-macros',\n                'babel-plugin-styled-components',\n              ],\n            },\n          },\n        },\n        {\n          test: /(\\.ts)x|\\.ts$/,\n          use: () => [\n            {\n              loader: 'ts-loader',\n            },\n          ],\n        },\n        {\n          test: /(\\.js)x|\\.js$/,\n          loader: 'esbuild-loader',\n          options: {\n            implementation: esbuild,\n          },\n        },\n        {\n          test: /\\.less$/,\n          use: [\n            MiniCssExtractPlugin.loader,\n            'css-loader',\n            {\n              loader: 'less-loader',\n            },\n          ],\n        },\n        info => ({\n          loader: 'svgo-loader',\n          options: {\n            plugins: [\n              {\n                cleanupIDs: { prefix: path.basename(info.resource) },\n              },\n            ],\n          },\n        }),\n        {\n          test: /\\.css$/,\n          oneOf: [\n            {\n              resourceQuery: /inline/,\n              loader: 'url-loader',\n            },\n            {\n              resourceQuery: /external/,\n              use: [\n                {\n                  loader: 'file-loader',\n                },\n              ],\n            },\n          ],\n        },\n      ],\n    },\n    plugins: [\n      new MiniCssExtractPlugin(),\n      new webpack.ProvidePlugin({\n        identifier1: 'module1',\n        identifier2: ['module2', 'property'],\n        identifier3: path.resolve(path.join(__dirname, 'src/module1')),\n        Buffer: ['buffer', 'Buffer'],\n        _map: ['lodash', 'map'],\n        'window.jQuery': 'jquery',\n        Vue: ['vue/dist/vue.esm.js', 'default'],\n      }),\n    ],\n    resolveLoader: {\n      alias: {\n        'my-loader': path.resolve(__dirname, 'src/my-custom-loader.js'),\n      },\n    },\n    optimization: {\n      minimizer: [new HtmlMinimizerPlugin({ parallel: true })],\n    },\n  };\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack/webpack.config.js",
    "content": "const getDevelopmentConfig = require('./webpack.dev.js');\nconst getProductionConfig = require('./webpack.prod.js');\n\nmodule.exports = (env, argv) => (env.production ? getProductionConfig(env) : getDevelopmentConfig(env));\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack/webpack.dev.js",
    "content": "const EslintPlugin = require('eslint-webpack-plugin');\nconst merge = require('./merge.js');\nconst common = require('./webpack.common.js');\n\nmodule.exports = env =>\n  merge(common(), {\n    mode: env.production ? 'production' : 'development',\n    entry: {\n      main: './src/app',\n      vendor: './src/vendor',\n      subpath: '#subpath/subpath-entry',\n    },\n    module: {\n      rules: [\n        {\n          test: /\\.(woff|ttf|ico|woff2|jpg|jpeg|png|webp)$/i,\n          use: 'base64-inline-loader',\n        },\n      ],\n    },\n    plugins: [new EslintPlugin({ extensions: ['ts'], fix: true })],\n  });\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack/webpack.prod.js",
    "content": "const CopyWebpackPlugin = require('copy-webpack-plugin');\nconst TerserWebpackPlugin = require('terser-webpack-plugin');\nconst merge = require('./merge.js');\nconst common = require('./webpack.common.js');\n\nmodule.exports = env =>\n  merge(common(), {\n    mode: 'production',\n    entry: './src/entry.js',\n    module: {\n      rules: [],\n    },\n    plugins: [new CopyWebpackPlugin()],\n    optimization: {\n      minimizer: [new TerserWebpackPlugin()],\n    },\n  });\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack-cli/package.json",
    "content": "{\n  \"name\": \"@plugins/webpack-cli\",\n  \"scripts\": {\n    \"build\": \"webpack\"\n  },\n  \"dependencies\": {},\n  \"devDependencies\": {\n    \"webpack-cli\": \"*\",\n    \"copy-webpack-plugin\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack-cli/webpack.config.js",
    "content": "const CopyWebpackPlugin = require('copy-webpack-plugin');\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack-reexport/package.json",
    "content": "{\n  \"name\": \"@fixtures/webpack-reexport\",\n  \"scripts\": {\n    \"build\": \"webpack\"\n  },\n  \"devDependencies\": {\n    \"@org/webpack-config\": \"*\",\n    \"webpack\": \"*\",\n    \"webpack-cli\": \"*\"\n  },\n  \"knip\": {\n    \"ignoreDependencies\": [\n      \"style-loader\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack-reexport/src/index.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack-reexport/webpack.config.js",
    "content": "module.exports = require('@org/webpack-config');\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack2/entry.dev.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack2/entry.prod.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack2/package.json",
    "content": "{\n  \"name\": \"@plugins/webpack2\",\n  \"scripts\": {\n    \"dev\": \"webpack --config webpack.dev.js\",\n    \"prod\": \"webpack --config webpack.prod.js\"\n  },\n  \"dependencies\": {},\n  \"devDependencies\": {\n    \"webpack\": \"*\",\n    \"webpack-cli\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack2/webpack.dev.js",
    "content": "import 'webpack';\n\nexport default () => {\n  return {\n    mode: 'development',\n    entry: './entry.dev.js',\n  };\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/webpack2/webpack.prod.js",
    "content": "import 'webpack';\n\nexport default () => {\n  return {\n    mode: 'production',\n    entry: './entry.prod.js',\n  };\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/wireit/apps/example-configuration/package.json",
    "content": "{\n  \"name\": \"@plugins/wireit__example-configuration\",\n  \"scripts\": {\n    \"build\": \"wireit\",\n    \"bundle\": \"wireit\"\n  },\n  \"wireit\": {\n    \"build\": {\n      \"command\": \"tsc\",\n      \"files\": [\n        \"src/**/*.ts\",\n        \"tsconfig.json\"\n      ],\n      \"output\": [\n        \"lib/**\"\n      ]\n    },\n    \"bundle\": {\n      \"command\": \"rollup -c\",\n      \"dependencies\": [\n        \"build\"\n      ],\n      \"files\": [\n        \"rollup.config.json\"\n      ],\n      \"output\": [\n        \"dist/bundle.js\"\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/wireit/apps/example-configuration/rollup.config.json",
    "content": "{}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/wireit/apps/example-configuration/tsconfig.json",
    "content": "{}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/wireit/apps/missing/package.json",
    "content": "{\n  \"name\": \"@plugins/wireit__missing\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/wireit/package.json",
    "content": "{\n  \"name\": \"@plugins/wireit\",\n  \"workspaces\": [\n    \"apps/*\"\n  ],\n  \"scripts\": {\n    \"build\": \"wireit\",\n    \"dev\": \"wireit\"\n  },\n  \"devDependencies\": {\n    \"wireit\": \"*\"\n  },\n  \"wireit\": {\n    \"build\": {\n      \"command\": \"tsc\"\n    },\n    \"dev\": {\n      \"command\": \"tsc --watch\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/wrangler/package.json",
    "content": "{\n  \"name\": \"@plugins/wrangler\",\n  \"scripts\": {\n    \"dev\": \"wrangler dev\"\n  },\n  \"dependencies\": {\n    \"wrangler\": \"^3.37.0\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/wrangler/worker-test-entry.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/plugins/wrangler/wrangler.toml",
    "content": "name = 'test'\nmain = 'worker-test-entry.ts'\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/xo/.xo-config.js",
    "content": "module.exports = {\n  space: true,\n  prettier: true,\n  plugins: ['unused-imports'],\n  parserOptions: { emitDecoratorMetadata: true },\n  rules: {\n    'func-names': ['error', 'always'],\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/xo/package.json",
    "content": "{\n  \"name\": \"@plugins/xo\",\n  \"scripts\": {\n    \"test\": \"xo\"\n  },\n  \"devDependencies\": {\n    \"xo\": \"^0.24.0\"\n  },\n  \"xo\": {\n    \"space\": true,\n    \"plugins\": [\n      \"eslint-comments\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/xo/xo.config.cjs",
    "content": "const mySharedConfig = require('glob');\n\nmodule.exports = {\n  ...mySharedConfig,\n  rules: {\n    'func-names': 'off',\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/yarn/package.json",
    "content": "{\n  \"name\": \"@plugins/yarn\",\n  \"devDependencies\": {\n    \"@yarnpkg/types\": \"latest\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/yarn/yarn.config.cjs",
    "content": "// https://yarnpkg.com/features/constraints\n\n/** @type {import('@yarnpkg/types')} */\nconst { defineConfig } = require('@yarnpkg/types');\n\nmodule.exports = defineConfig({\n  async constraints({ Yarn }) {\n    // `Yarn` is now well-typed ✨\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/yarn-berry/.gitgnore",
    "content": ".yarn/*\n!.yarn/cache\n!.yarn/patches\n!.yarn/plugins\n!.yarn/releases\n!.yarn/sdks\n!.yarn/versions\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/yarn-berry/.yarn/plugins/@yarnpkg/plugin-bar.cjs",
    "content": "module.exports = {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/yarn-berry/.yarn/plugins/@yarnpkg/plugin-foo.cjs",
    "content": "module.exports = {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/yarn-berry/.yarn/releases/yarn-4.12.0.cjs",
    "content": "module.exports = {};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/yarn-berry/.yarnrc.yml",
    "content": "nodeLinker: node-modules\n\nplugins:\n  # A plugin can be represented by a dict containing the path\n  - checksum: 1234567890abcdef\n    path: .yarn/plugins/@yarnpkg/plugin-foo.cjs\n    spec: \"https://example.com/@yarnpkg/plugin-foo.js\"\n  # A plugin can also be represented by the path alone\n  - .yarn/plugins/@yarnpkg/plugin-bar.cjs\n\nyarnPath: .yarn/releases/yarn-4.12.0.cjs\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/yarn-berry/package.json",
    "content": "{\n  \"name\": \"@plugins/yarn-berry\",\n  \"packageManager\": \"yarn@4.12.0\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/yarn-berry/yarn.config.cjs",
    "content": "module.exports = {\n  constraints: async () => {},\n};\n"
  },
  {
    "path": "packages/knip/fixtures/plugins/yorkie/package.json",
    "content": "{\n  \"name\": \"@plugins/yorkie\",\n  \"gitHooks\": {\n    \"pre-commit\": \"lint-staged\"\n  },\n  \"lint-staged\": {\n    \"*.js\": \"eslint --fix\",\n    \"*.md\": \"markdownlint --fix\",\n    \"docs/**/*.svg\": [\n      \"npx svgo -r --multipass\"\n    ]\n  },\n  \"devDependencies\": {\n    \"lint-staged\": \"^11.0.0\",\n    \"yorkie\": \"^2.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/pragma/index.test.ts",
    "content": "// @vitest-environment node\n// @vitest-environment ./local-env.js\n\n/**\n * @vitest-environment happy-dom\n * @jest-environment jsdom\n */\n\nimport 'vitest';\n\ntest('ehm comments & environments overload', () => {\n  const element = document.createElement('div');\n  expect(element).not.toBeNull();\n});\n"
  },
  {
    "path": "packages/knip/fixtures/pragma/index.ts",
    "content": "import './jsx.js';\nimport './reference-types.js';\n"
  },
  {
    "path": "packages/knip/fixtures/pragma/jsx.tsx",
    "content": "/** @jsxImportSource preact */\n\nconst View = () => <div role=\"presentation\">jsx pragma</div>;\n"
  },
  {
    "path": "packages/knip/fixtures/pragma/local-env.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/pragma/package.json",
    "content": "{\n  \"name\": \"@fixtures/pragma\",\n  \"devDependencies\": {\n    \"@types/oh-my-types\": \"*\",\n    \"happy-dom\": \"*\",\n    \"jsdom\": \"*\",\n    \"preact\": \"*\",\n    \"vitest\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/pragma/reference-types.ts",
    "content": "/// <reference types=\"oh-my-types\" />\n\nconst getEnvName = () => expect.getState().currentTestName ?? 'unknown';\n"
  },
  {
    "path": "packages/knip/fixtures/pragma/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"jsx\": \"react-jsx\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/preprocessors/identity.ts",
    "content": "import { ReporterOptions } from '../../src/types/issues';\n\nexport default function identity(value: ReporterOptions): ReporterOptions {\n  return value;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/preprocessors/minimal.ts",
    "content": "import { Preprocessor } from '../../src/types/issues';\n\nconst minimal: Preprocessor = value => ({\n  ...value,\n  issues: {\n    ...value.issues,\n    files: new Set([...value.issues.files, import.meta.url]),\n  },\n});\n\nexport default minimal;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports/1-entry.ts",
    "content": "import { something } from './2-re-export-star.js';\nsomething;\n\nexport {\n  /** @public */\n  somethingToIgnore,\n  somethingNotToIgnore,\n} from './2-re-export-star.js';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports/2-re-export-star.ts",
    "content": "export * from './3-re-export-named.js';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports/3-re-export-named.ts",
    "content": "export { something, somethingToIgnore, somethingNotToIgnore } from './4-my-module';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports/4-my-module.ts",
    "content": "export const something = {};\n\nexport const somethingToIgnore = {};\nexport const somethingNotToIgnore = {};\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports\",\n  \"knip\": {\n    \"entry\": [\n      \"1-entry.ts\"\n    ],\n    \"project\": [\n      \"*.ts\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-aliased-ns/1-first.ts",
    "content": "export const first = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-aliased-ns/2-second.ts",
    "content": "export const second = 2;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-aliased-ns/3-barrel.ts",
    "content": "export * from './1-first';\nexport * from './2-second';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-aliased-ns/4-collect.ts",
    "content": "import * as NS from './3-barrel';\nexport { NS as aliased };\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-aliased-ns/index.ts",
    "content": "import { aliased } from './4-collect';\n\naliased.first;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-aliased-ns/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-aliased-ns\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-cjs/1-entry.js",
    "content": "const { something } = require('./2-re-export-star');\nsomething;\n\nmodule.exports.somethingToIgnore = require('./2-re-export-star').somethingToIgnore;\n\nmodule.exports.somethingNotToIgnore = require('./2-re-export-star').somethingNotToIgnore;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-cjs/2-re-export-star.js",
    "content": "module.exports = require('./3-my-module');\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-cjs/3-my-module.js",
    "content": "module.exports.something = {};\nmodule.exports.somethingToIgnore = {};\nmodule.exports.somethingNotToIgnore = {};\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-cjs/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-cjs\",\n  \"knip\": {\n    \"entry\": [\n      \"1-entry.js\"\n    ],\n    \"project\": [\n      \"*.js\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-deep/1-entry.ts",
    "content": "import { something } from './2-re-export-star';\nsomething;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-deep/2-re-export-star.ts",
    "content": "export * from './3-re-export-named';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-deep/3-re-export-named.ts",
    "content": "export { something } from './4-re-export-star.js';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-deep/4-re-export-star.ts",
    "content": "export * from './5-re-export-named';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-deep/5-re-export-named.ts",
    "content": "export { something } from './6-re-export-star';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-deep/6-re-export-star.ts",
    "content": "export * from './7-my-module';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-deep/7-my-module.ts",
    "content": "export const something = {};\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-deep/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-deep\",\n  \"knip\": {\n    \"entry\": [\n      \"1-entry.ts\"\n    ],\n    \"project\": [\n      \"*.ts\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-default-renamed/default-reexported/blueberry.ts",
    "content": "const blueberry = 'blueberry';\nexport default blueberry;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-default-renamed/default-reexported/index.ts",
    "content": "export { default as blueberry } from './blueberry';\nexport { default as pear } from './pear';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-default-renamed/default-reexported/pear.ts",
    "content": "const pear = 'pear';\nexport default pear;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-default-renamed/index.ts",
    "content": "import * as notReexported from './not-reexported';\nimport * as namedReexported from './named-reexported';\nimport * as defaultReexported from './default-reexported';\n\nObject.keys(namedReexported);\nObject.values(notReexported);\nObject.entries(defaultReexported);\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-default-renamed/named-reexported/apricot.ts",
    "content": "export const apricot = 'apricot';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-default-renamed/named-reexported/index.ts",
    "content": "export { apricot } from './apricot';\nexport { peach } from './peach';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-default-renamed/named-reexported/peach.ts",
    "content": "export const peach = 'peach';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-default-renamed/not-reexported/index.ts",
    "content": "export const apple = 'apple';\nexport const orange = 'orange';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-default-renamed/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-default-renamed\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-default-renamed-deep/deep.ts",
    "content": "export * as _notReexported from './not-reexported';\nexport * as _namedReexported from './named-reexported';\nexport * as _defaultReexported from './default-reexported';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-default-renamed-deep/default-reexported/blueberry.ts",
    "content": "const blueberry = 'blueberry';\nexport default blueberry;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-default-renamed-deep/default-reexported/index.ts",
    "content": "export { default as blueberry } from './blueberry';\nexport { default as pear } from './pear';\nexport { default as pineapple } from './pineapple';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-default-renamed-deep/default-reexported/pear.ts",
    "content": "const pear = 'pear';\nexport default pear;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-default-renamed-deep/default-reexported/pineapple.ts",
    "content": "const pineapple = 'pineapple';\nexport default pineapple;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-default-renamed-deep/index.ts",
    "content": "import * as NS from './intermediate';\n\nObject.getOwnPropertyNames(NS);\n// Uncommenting any of the following will result in all the others being unused (or w/ --include nsExports)\n// NS.notReexported.banana;\n// NS.namedReexported.coconut;\n// NS.defaultReexported.pineapple;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-default-renamed-deep/intermediate.ts",
    "content": "export { _notReexported as notReexported } from './deep';\nexport { _namedReexported as namedReexported } from './deep';\nexport { _defaultReexported as defaultReexported } from './deep';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-default-renamed-deep/named-reexported/apricot.ts",
    "content": "export const apricot = 'apricot';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-default-renamed-deep/named-reexported/coconut.ts",
    "content": "export const coconut = 'coconut';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-default-renamed-deep/named-reexported/index.ts",
    "content": "export { apricot } from './apricot';\nexport { peach } from './peach';\nexport { coconut } from './coconut';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-default-renamed-deep/named-reexported/peach.ts",
    "content": "export const peach = 'peach';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-default-renamed-deep/not-reexported/index.ts",
    "content": "export const apple = 'apple';\nexport const orange = 'orange';\nexport const banana = 'banana';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-default-renamed-deep/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-default-renamed-deep\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-destructure-spread/animal.ts",
    "content": "export const cow = 1;\nexport const fly = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-destructure-spread/animals.ts",
    "content": "export * as animals from './animal.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-destructure-spread/farm.ts",
    "content": "import { animals } from './animals';\n\nexport const farm = { barn: { ...animals } };\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-destructure-spread/index.ts",
    "content": "import { farm } from './farm';\n\nconst { cow } = farm.barn;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-destructure-spread/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-destructure-spread\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-enum/index.ts",
    "content": "import MyEnum from './mid';\nimport { MyNextEnum } from './mid';\n\nMyEnum.One;\nMyEnum.Two;\nMyNextEnum.Three;\nMyNextEnum.Four;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-enum/mid.ts",
    "content": "import { MyEnum } from './myEnum';\nimport { MyNextEnum } from './myNextEnum';\n\nexport default MyEnum;\nexport { MyNextEnum };\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-enum/myEnum.ts",
    "content": "export enum MyEnum {\n  One = 0,\n  Two = 1,\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-enum/myNextEnum.ts",
    "content": "export enum MyNextEnum {\n  Three,\n  Four,\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-enum/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-enum\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-enum-members-workspace/index.ts",
    "content": "import { Density, Consistency } from '@fixtures/re-exports-enum-members-workspace__lib';\n\nDensity.HIGH;\nConsistency.PROFOUND;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-enum-members-workspace/lib/enums.ts",
    "content": "export enum Density {\n  HIGH = 'HIGH',\n  MEDIUM = 'MEDIUM',\n}\n\nexport enum Consistency {\n  LOW = 'LOW',\n  PROFOUND = 'PROFOUND',\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-enum-members-workspace/lib/index.ts",
    "content": "export { Consistency } from './enums';\n\nimport { Density } from './enums';\nexport { Density };\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-enum-members-workspace/lib/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-enum-members-workspace__lib\",\n  \"exports\": \"./index.ts\",\n  \"types\": \"./index.ts\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-enum-members-workspace/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-enum-members-workspace\",\n  \"workspaces\": [\n    \"lib\"\n  ],\n  \"dependencies\": {\n    \"@fixtures/re-exports-enum-members-workspace__lib\": \"workspaces:*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-enum-unused/index.ts",
    "content": "import MyEnum from './mid';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-enum-unused/mid.ts",
    "content": "import { MyEnum } from './myEnum';\nexport default MyEnum;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-enum-unused/myEnum.ts",
    "content": "export enum MyEnum {\n  One,\n  Two,\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-enum-unused/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-enum-unused\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-export-declaration/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-export-declaration\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-export-declaration/src/exporter/exporterA.ts",
    "content": "const cb = () => {};\n\nexport { cb };\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-export-declaration/src/exporter/exporterB.ts",
    "content": "export { cb, fn };\n\nfunction cb() {}\n\nfunction fn() {}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-export-declaration/src/exporter/exporterC.ts",
    "content": "export { cb };\n\nfunction cb() {}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-export-declaration/src/exporter/exporterD.ts",
    "content": "export { cb };\n\nfunction cb() {}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-export-declaration/src/exporter/index.ts",
    "content": "import * as exporterA from './exporterA';\nimport * as exporterB from './exporterB';\nimport * as exporterC from './exporterC';\nimport * as exporterD from './exporterD';\n\nexporterB.fn();\n\nexport { exporterA as reExporterA, exporterB as reExporterB, exporterC };\nexport { exporterD };\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-export-declaration/src/index.ts",
    "content": "import { reExporterA, reExporterB, exporterC, exporterD } from './exporter';\n\nreExporterA.cb();\n\nreExporterB.cb();\n\nexporterC.cb();\n\nexporterD.cb();\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-export-ns/1-root.ts",
    "content": "import { NS } from './2-psuedo-re-exporter';\n\nconst x: NS.EnumA.InternalUsedProp = 1;\n\nexport function exportedFnOnNs() {\n  NS.fnA();\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-export-ns/2-psuedo-re-exporter.ts",
    "content": "import * as NS from './3-branch';\nexport { NS };\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-export-ns/3-branch.ts",
    "content": "export * from './4-leaf-A';\nexport * from './4-leaf-B';\nexport * from './4-leaf-C';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-export-ns/4-leaf-A.ts",
    "content": "import { fnB } from './3-branch';\n\nexport function fnA() {\n  return fnB();\n}\n\nexport enum EnumA {\n  InternalUsedProp = 1,\n  UnusedProp = 2,\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-export-ns/4-leaf-B.ts",
    "content": "export function fnB() {}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-export-ns/4-leaf-C.ts",
    "content": "export function fnC() {}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-export-ns/index.ts",
    "content": "import * as ROOT from './1-root';\n\nexport = ROOT;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-export-ns/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-export-ns\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ignore-exports-used-in-file/export.ts",
    "content": "export const cherry = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ignore-exports-used-in-file/index.ts",
    "content": "export * from './reexport';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ignore-exports-used-in-file/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-ignore-exports-used-in-file\",\n  \"knip\": {\n    \"ignoreExportsUsedInFile\": true,\n    \"includeEntryExports\": true\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ignore-exports-used-in-file/reexport.ts",
    "content": "export { cherry } from './export';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ns-member/index.ts",
    "content": "import * as NS from './sub3';\n\nconst { memberA, memberB } = NS.sub;\n\nconst { memberC, memberD } = NS.pseudo;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ns-member/member-ab.ts",
    "content": "export const memberA = 1;\nexport const memberB = 1;\nexport const unusedMemberA = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ns-member/member-cd.ts",
    "content": "export const memberC = 1;\nexport const memberD = 1;\nexport const unusedMemberC = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ns-member/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-ns-member\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ns-member/sub2-pseudo.ts",
    "content": "import * as pseudo from './member-cd';\nexport { pseudo };\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ns-member/sub2-sub.ts",
    "content": "export * as sub from './member-ab';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ns-member/sub3.ts",
    "content": "export * from './sub2-sub';\nexport * from './sub2-pseudo';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ns-type/assets.tsx",
    "content": "export { default as cat } from './image.png';\nexport { default as dog } from './image.png';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ns-type/barrel.ts",
    "content": "export * as pluginA from './pluginA.ts';\nexport * as pluginB from './pluginB.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ns-type/index.tsx",
    "content": "import * as NS from './barrel';\nimport * as Styled from './styled';\nimport * as pictures from './assets';\n\ntype Key = keyof typeof NS;\n\nconst key = 'anything';\n\nNS[key as Key].resolve();\n\nconst Heading = ({ as }: { as: keyof typeof Styled }) => {\n  const Element = as ? Styled[as] || Styled.h1 : Styled.h1;\n  return <Element />;\n};\n\ntype PictureKey = keyof typeof pictures;\n\ninterface Props {\n  pictureKey: PictureKey;\n}\n\nconst pictureToken = (): PictureKey => 'dog';\n\nconst Picture = (props: Props) => <img source={pictures[pictureToken()]} alt=\"\" />;\n\nconst PictureB = (props: Props) => <Picture pictureKey={pictureToken()} />;\n\nconst PictureC = (props: Props) => <Picture pictureKey=\"dog\" />;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ns-type/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-ns-type\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ns-type/pluginA.ts",
    "content": "export const resolve = () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ns-type/pluginB.ts",
    "content": "export const resolve = () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ns-type/styled.ts",
    "content": "export const h1 = 'h1';\nexport const h2 = 'h2';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ns-type/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"jsx\": \"preserve\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ns-type2/index.ts",
    "content": "import type { MySchemaType } from './types';\n\nconst obj: MySchemaType = { id: '123456' };\nobj;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ns-type2/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-ns-type2\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ns-type2/schema.ts",
    "content": "export const MySchema = {\n  id: 'string',\n};\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-ns-type2/types.ts",
    "content": "import * as Schema from './schema';\n\nexport type MySchemaType = typeof Schema.MySchema;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-pseudo/index.ts",
    "content": "import { namespaceL, namespaceR } from './pseudo';\n\nconst callfuncDynamically = () => {\n  const funcs = Math ? namespaceL : namespaceR;\n  funcs.fn();\n};\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-pseudo/left.ts",
    "content": "export const fn = () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-pseudo/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-pseudo\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-pseudo/pseudo.ts",
    "content": "import * as NS from './left';\nexport const namespaceL = NS;\n\nexport * as namespaceR from './right';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-pseudo/right.ts",
    "content": "export const fn = () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-public/index.ts",
    "content": "import { something } from './module.js';\nsomething;\n\n/** @public */\nexport {\n  /** @public */\n  somethingToIgnore,\n  somethingIgnoredAnyway,\n} from './module.js';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-public/module.ts",
    "content": "export const something = 1;\nexport const somethingToIgnore = 1;\nexport const somethingIgnoredAnyway = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-public/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-public\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-recursive/barrel.ts",
    "content": "export * from './module.js';\nexport * as default from './barrel.js';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-recursive/cycle-a.ts",
    "content": "export * from './cycle-b.js';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-recursive/cycle-b.ts",
    "content": "export * from './cycle-a.js';\nexport const y = 2;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-recursive/index.ts",
    "content": "import './barrel.ts';\nimport './cycle-a.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-recursive/module.ts",
    "content": "import './barrel.ts';\n\nexport const x = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-recursive/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-recursive\",\n  \"type\": \"module\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-renamed/fileA.ts",
    "content": "export const A = 'A';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-renamed/fileB.ts",
    "content": "export * as B from './fileA.js';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-renamed/index.ts",
    "content": "import { B as C } from './fileB.js';\n\nC.A;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-renamed/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-renamed\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-spread/animal.ts",
    "content": "export const cow = 1;\nexport const fly = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-spread/animals.ts",
    "content": "export * as animals from './animal.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-spread/farm.ts",
    "content": "import { animals } from './animals';\n\nexport const farm = { ...animals };\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-spread/index.ts",
    "content": "import { farm } from './farm';\n\nconst { cow } = farm;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-spread/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-spread\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-twice/dir/default.ts",
    "content": "export default 1;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-twice/dir/index.ts",
    "content": "export * from './named';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-twice/dir/named.ts",
    "content": "export { default as named } from './default';\nexport * from './default';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-twice/index.ts",
    "content": "import { named } from './dir';\n\nnamed;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-twice/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-twice\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-with-decorator/barrel.ts",
    "content": "export { default as NS } from './my-module.js';\nexport * from './decorator.js';\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-with-decorator/decorator.ts",
    "content": "function MyDeco() {\n  return (...args: any[]) => {};\n}\n\n@MyDeco()\nexport class MyDecorated {}\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-with-decorator/entry.ts",
    "content": "import { NS } from './barrel.js';\nimport { MyDecorated } from './barrel.js';\n\nNS;\nNS.KEY;\nMyDecorated;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-with-decorator/my-module.ts",
    "content": "const obj = {};\n\nenum MyEnum {\n  KEY = 1,\n}\n\nexport default MyEnum;\n"
  },
  {
    "path": "packages/knip/fixtures/re-exports-with-decorator/package.json",
    "content": "{\n  \"name\": \"@fixtures/re-exports-with-decorator\",\n  \"knip\": {\n    \"entry\": [\n      \"entry.ts\"\n    ],\n    \"project\": [\n      \"*.ts\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/rules/exports.ts",
    "content": "export const used = 1;\nexport const unused = 1;\n\nexport type UsedType = unknown;\nexport type UnusedType = unknown;\n\nexport default used;\n\nexport class MyClass {\n  unused: 1;\n}\n\nexport enum MyEnum {\n  used = 1,\n  unused = 1,\n}\n"
  },
  {
    "path": "packages/knip/fixtures/rules/index.ts",
    "content": "import 'used';\nimport '@dev/used';\nimport 'optional-peer-dep';\nimport './unresolved';\nimport 'unlisted';\nimport * as NS from './ns';\nimport default_, { used, type UsedType, MyClass, MyEnum } from './exports';\n\nconst x: UsedType | NS.UsedType = [default_, used, MyEnum.used, NS.used];\nconst y = new MyClass();\n"
  },
  {
    "path": "packages/knip/fixtures/rules/ns.ts",
    "content": "export const used = 1;\nexport const unused = 1;\n\nexport type UsedType = unknown;\nexport type UnusedType = unknown;\n"
  },
  {
    "path": "packages/knip/fixtures/rules/package.json",
    "content": "{\n  \"name\": \"@fixtures/rules\",\n  \"dependencies\": {\n    \"used\": \"*\",\n    \"unused\": \"*\"\n  },\n  \"devDependencies\": {\n    \"@dev/used\": \"*\",\n    \"@dev/unused\": \"*\"\n  },\n  \"peerDependencies\": {\n    \"optional-peer-dep\": \"*\"\n  },\n  \"peerDependenciesMeta\": {\n    \"optional-peer-dep\": {\n      \"optional\": true\n    }\n  },\n  \"scripts\": {\n    \"unlisted-binary\": \"unlisted\"\n  },\n  \"knip\": {\n    \"include\": [\n      \"nsExports\",\n      \"nsTypes\"\n    ],\n    \"rules\": {\n      \"files\": \"warn\",\n      \"dependencies\": \"warn\",\n      \"devDependencies\": \"warn\",\n      \"optionalPeerDependencies\": \"warn\",\n      \"unlisted\": \"warn\",\n      \"binaries\": \"warn\",\n      \"unresolved\": \"warn\",\n      \"exports\": \"warn\",\n      \"types\": \"warn\",\n      \"nsExports\": \"warn\",\n      \"nsTypes\": \"warn\",\n      \"duplicates\": \"warn\",\n      \"enumMembers\": \"warn\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/rules/unused.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/script-visitors-bun/index.ts",
    "content": "import { $ } from 'bun';\n\nawait $`MY_VAR=${process.argv.at(2)} bun script.ts`;\n"
  },
  {
    "path": "packages/knip/fixtures/script-visitors-bun/package.json",
    "content": "{\n  \"name\": \"@fixtures/script-visitors-bun\",\n  \"dependencies\": {\n    \"boxen-cli\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/script-visitors-bun/script.ts",
    "content": "import { $ } from 'bun';\n\nawait $`cd .. && rm -rf node_modules/rimraf`;\n\nawait $`bun boxen I ❤ unicorns`;\n\nawait $`oh-my not supported yet`;\n\nawait $`ls *.*`;\n"
  },
  {
    "path": "packages/knip/fixtures/script-visitors-execa/execa-docs.mjs",
    "content": "import { $ } from 'execa';\n\nawait $`cat package.json | grep name`;\n\nconst branch = await $`git branch --show-current`;\nawait $`dep deploy --branch=${branch}`;\n\nawait Promise.all([$`executable1; echo 1`, $`executable2; echo 2`, $`executable3; echo 3`]);\n\nconst name = 'content';\nawait $`mkdir /tmp/${name}`;\n"
  },
  {
    "path": "packages/knip/fixtures/script-visitors-execa/main.js",
    "content": "export default 1;\n"
  },
  {
    "path": "packages/knip/fixtures/script-visitors-execa/methods.mjs",
    "content": "import { execa, execaSync, execaCommand, execaCommandSync, execaNode, $sync } from 'execa';\n\n$sync`pnpm dlx executable4`;\n\nawait execa('bun x', ['executable5']);\n\nexecaSync('npx', ['executable6']);\n\nawait execaCommand('bunx executable7');\n\nexecaCommandSync('pnpx executable8');\n"
  },
  {
    "path": "packages/knip/fixtures/script-visitors-execa/node.mjs",
    "content": "import { $ } from 'execa';\n\nawait $`node main.js`;\n"
  },
  {
    "path": "packages/knip/fixtures/script-visitors-execa/options.mjs",
    "content": "import { $ } from 'execa';\n\nawait $({ stdio: 'inherit' })`c8 node hydrate.js`;\n"
  },
  {
    "path": "packages/knip/fixtures/script-visitors-execa/package.json",
    "content": "{\n  \"name\": \"@fixtures/script-visitors-execa\",\n  \"scripts\": {\n    \"execa1\": \"node ./execa-docs.mjs\",\n    \"execa2\": \"node ./script.js\",\n    \"execa3\": \"node ./node.mjs\",\n    \"execa4\": \"node ./options.mjs\",\n    \"execa5\": \"node ./methods.mjs\"\n  },\n  \"dependencies\": {\n    \"dep\": \"*\"\n  },\n  \"devDependencies\": {\n    \"all-contributors-cli\": \"*\",\n    \"c8\": \"*\",\n    \"octokit\": \"*\",\n    \"execa\": \"*\"\n  },\n  \"knip\": {\n    \"ignoreBinaries\": [\n      \"executable1\",\n      \"executable2\",\n      \"executable3\",\n      \"executable4\",\n      \"executable5\",\n      \"executable6\",\n      \"executable7\",\n      \"executable8\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/script-visitors-execa/script.js",
    "content": "import { $, $sync } from 'execa';\n\n/* global $ */\nimport { EOL } from 'node:os';\nimport { Octokit } from 'octokit';\n\nawait $`pnpm all-contributors generate`;\n\nawait $sync`npx -y all-contributors-cli@6.25 add user`;\n"
  },
  {
    "path": "packages/knip/fixtures/script-visitors-zx/main.js",
    "content": "export default 1;\n"
  },
  {
    "path": "packages/knip/fixtures/script-visitors-zx/node.mjs",
    "content": "#!/usr/bin/env zx\n\nawait $`node main.js`;\n"
  },
  {
    "path": "packages/knip/fixtures/script-visitors-zx/package.json",
    "content": "{\n  \"name\": \"@fixtures/script-visitors-zx\",\n  \"scripts\": {\n    \"zx1\": \"zx ./zx-docs.mjs\",\n    \"zx2\": \"zx --quiet ./script.js\",\n    \"zx3\": \"npx --yes zx --quiet ./node.mjs\"\n  },\n  \"dependencies\": {\n    \"dep\": \"*\"\n  },\n  \"devDependencies\": {\n    \"all-contributors-cli\": \"*\",\n    \"octokit\": \"*\",\n    \"zx\": \"*\"\n  },\n  \"knip\": {\n    \"ignoreBinaries\": [\n      \"sleep\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/script-visitors-zx/script.js",
    "content": "#!/usr/bin/env zx\n\n/* global $ */\nimport { EOL } from 'node:os';\nimport { Octokit } from 'octokit';\n\nawait $`pnpm all-contributors generate`;\n"
  },
  {
    "path": "packages/knip/fixtures/script-visitors-zx/zx-docs.mjs",
    "content": "#!/usr/bin/env zx\n\nawait $`cat package.json | grep name`;\n\nconst branch = await $`git branch --show-current`;\nawait $`dep deploy --branch=${branch}`;\n\nawait Promise.all([$`sleep 1; echo 1`, $`sleep 2; echo 2`, $`sleep 3; echo 3`]);\n\nconst name = 'content';\nawait $`mkdir /tmp/${name}`;\n"
  },
  {
    "path": "packages/knip/fixtures/self-reference/.gitignore",
    "content": "dist\n"
  },
  {
    "path": "packages/knip/fixtures/self-reference/entry.ts",
    "content": "import external from 'external-package';\nimport index from '@fixtures/self-reference';\nimport build from '@fixtures/self-reference/build';\nimport local from '@fixtures/self-reference/local';\n\nindex;\nbuild;\nlocal;\nexternal;\n"
  },
  {
    "path": "packages/knip/fixtures/self-reference/knip.json",
    "content": "{\n  \"entry\": [\"entry.ts!\"],\n  \"project\": [\"**/*.{js,ts}!\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/self-reference/lib/module.js",
    "content": "export default 42;\n"
  },
  {
    "path": "packages/knip/fixtures/self-reference/package.json",
    "content": "{\n  \"name\": \"@fixtures/self-reference\",\n  \"exports\": {\n    \".\": {\n      \"require\": \"./dist/index.cjs\",\n      \"import\": \"./dist/index.mjs\",\n      \"default\": \"./dist/index.mjs\"\n    },\n    \"./build\": {\n      \"require\": \"./dist/build.cjs\",\n      \"import\": \"./dist/build.mjs\"\n    },\n    \"./local\": {\n      \"require\": \"./lib/module.js\"\n    }\n  },\n  \"dependencies\": {\n    \"external-package\": \"1.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/self-reference/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"moduleResolution\": \"node16\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/self-reference-from-plugin/.eslintrc.json",
    "content": "{\n  \"root\": true,\n  \"extends\": [\n    \"@fixtures/x-self-reference-from-plugin\",\n    \"@fixtures/x-self-reference-from-plugin/local\",\n    \"@fixtures/x-self-reference-from-plugin/data\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/self-reference-from-plugin/.gitignore",
    "content": "dist\n"
  },
  {
    "path": "packages/knip/fixtures/self-reference-from-plugin/data.json",
    "content": "{\n  \"hello\": \"world\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/self-reference-from-plugin/entry.ts",
    "content": "//\n"
  },
  {
    "path": "packages/knip/fixtures/self-reference-from-plugin/knip.json",
    "content": "{\n  \"entry\": [\"entry.ts!\"],\n  \"project\": [\"*.{js,ts}!\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/self-reference-from-plugin/lokal.js",
    "content": "module.exports = 42;\n"
  },
  {
    "path": "packages/knip/fixtures/self-reference-from-plugin/package.json",
    "content": "{\n  \"name\": \"@fixtures/eslint-config-x-self-reference-from-plugin\",\n  \"type\": \"commonjs\",\n  \"exports\": {\n    \".\": {\n      \"require\": \"./dist/build.js\"\n    },\n    \"./data\": {\n      \"node\": \"./data.json\"\n    },\n    \"./local\": {\n      \"default\": \"./lokal.js\"\n    }\n  },\n  \"devDependencies\": {\n    \"eslint\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/session/Harness.Parallel.Host.ts",
    "content": "export * from './host.js';\n"
  },
  {
    "path": "packages/knip/fixtures/session/Harness.Parallel.Worker.ts",
    "content": "export * from './worker.js';\n"
  },
  {
    "path": "packages/knip/fixtures/session/Harness.Parallel.ts",
    "content": "export * from '../parallel/shared.js';\nimport * as Host from './Harness.Parallel.Host.js';\nexport { Host };\nimport * as Worker from './Harness.Parallel.Worker.js';\nexport { Worker };\n"
  },
  {
    "path": "packages/knip/fixtures/session/Harness.ts",
    "content": "import * as Parallel from './Harness.Parallel.js';\nexport { Parallel };\n"
  },
  {
    "path": "packages/knip/fixtures/session/a.ts",
    "content": "import './b.ts';\n\nexport const A = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/session/b.ts",
    "content": "import './c.ts';\n\nexport * from './a';\n"
  },
  {
    "path": "packages/knip/fixtures/session/c.ts",
    "content": "import './a.ts';\n\nexport * from './a';\n"
  },
  {
    "path": "packages/knip/fixtures/session/default-export.ts",
    "content": "const defaultValue = 123;\n\nexport default defaultValue;\n"
  },
  {
    "path": "packages/knip/fixtures/session/diamond-base.ts",
    "content": "export const DIAMOND = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/session/diamond-left.ts",
    "content": "export * from './diamond-base.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/session/diamond-right.ts",
    "content": "export * from './diamond-base.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/session/diamond-top.ts",
    "content": "export * from './diamond-left.ts';\nexport * from './diamond-right.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/session/flowers.ts",
    "content": "import * as flowers from './rose.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/session/host.ts",
    "content": "export function start() {}\n"
  },
  {
    "path": "packages/knip/fixtures/session/index.ts",
    "content": "export * from './a';\nexport * from './b';\nexport * from './c';\nexport * from './rose.ts';\n\nimport { OVERLOAD } from './overload-1.ts';\nOVERLOAD;\n\nimport { DIAMOND } from './diamond-top.ts';\nDIAMOND;\n\nimport defaultValue from './default-export.ts';\ndefaultValue;\n\nimport './runner.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/session/knip.json",
    "content": "{\n  \"entry\": [\"index.ts\", \"src/index.ts\"],\n  \"project\": [\"**/*.ts\"],\n  \"compilers\": {\n    \"md\": true\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/session/overload-1.ts",
    "content": "export const OVERLOAD = 1;\n\nexport * from './overload-2.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/session/overload-2.ts",
    "content": "export const OVERLOAD = 2;\n\nexport * from './overload-3.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/session/overload-3.ts",
    "content": "export const OVERLOAD = 3;\n"
  },
  {
    "path": "packages/knip/fixtures/session/package.json",
    "content": "{\n  \"name\": \"@fixtures/session\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/session/rose.ts",
    "content": "export const rose = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/session/runner.ts",
    "content": "import { Parallel } from './Harness.js';\n\nParallel.Worker.start();\nParallel.Host.start();\n"
  },
  {
    "path": "packages/knip/fixtures/session/src/core/app/types.ts",
    "content": "export type SSRManifest = {};\n"
  },
  {
    "path": "packages/knip/fixtures/session/src/index.ts",
    "content": "export type * from './types/public/index.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/session/src/types/public/index.ts",
    "content": "export type { SSRManifest } from '../../core/app/types.js';\nexport type * from './internal.js';\n"
  },
  {
    "path": "packages/knip/fixtures/session/src/types/public/internal.ts",
    "content": "export type { SSRManifest } from '../../core/app/types.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/session/theme-reexport.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/session/theme.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/session/worker.ts",
    "content": "export function start() {}\n"
  },
  {
    "path": "packages/knip/fixtures/session-dependencies/knip.json",
    "content": "{\n  \"entry\": [\"src/index.ts\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/session-dependencies/package.json",
    "content": "{\n  \"name\": \"@fixtures/session-dependencies\",\n  \"dependencies\": {\n    \"lodash\": \"*\",\n    \"zod\": \"*\",\n    \"unused-dep\": \"*\"\n  },\n  \"devDependencies\": {\n    \"typescript\": \"*\",\n    \"vitest\": \"*\",\n    \"unused-dev-dep\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/session-dependencies/src/index.ts",
    "content": "import { debounce } from 'lodash';\nimport { z } from 'zod';\nimport type { infer as Infer } from 'zod';\nimport { helper } from './utils.js';\n\nconst schema = z.object({ name: z.string() });\ntype Schema = Infer<typeof schema>;\n\nconst debouncedFn = debounce(() => 'debounced');\n\nexport { debouncedFn, schema, helper };\nexport type { Schema };\n"
  },
  {
    "path": "packages/knip/fixtures/session-dependencies/src/utils.ts",
    "content": "import _ from 'lodash';\n\nexport const helper = () => _.identity('helper');\n"
  },
  {
    "path": "packages/knip/fixtures/session-dependencies/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"strict\": true,\n    \"esModuleInterop\": true\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/session-re-exports/app/consumer-2.ts",
    "content": "import { Box } from 'pkg/public';\n\nBox;\n"
  },
  {
    "path": "packages/knip/fixtures/session-re-exports/app/consumer-3.ts",
    "content": "import { Box } from 'pkg/public';\n\nBox;\n"
  },
  {
    "path": "packages/knip/fixtures/session-re-exports/app/index.ts",
    "content": "import './consumer-2.ts';\nimport './consumer-3.ts';\nimport 'pkg/internal';\n"
  },
  {
    "path": "packages/knip/fixtures/session-re-exports/app/package.json",
    "content": "{\n  \"name\": \"@fixtures/session-re-exports-app\",\n  \"dependencies\": {\n    \"pkg\": \"file:../pkg\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/session-re-exports/knip.json",
    "content": "{\n  \"workspaces\": {\n    \"app\": {\n      \"entry\": [\"index.ts\"]\n    },\n    \"pkg\": {}\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/session-re-exports/package.json",
    "content": "{\n  \"name\": \"@fixtures/session-re-exports\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/session-re-exports/pkg/internal/consumer-1.ts",
    "content": "import { Box, internal } from '../public/implementation.ts';\n\ninternal;\nBox;\n"
  },
  {
    "path": "packages/knip/fixtures/session-re-exports/pkg/package.json",
    "content": "{\n  \"name\": \"@fixtures/session-re-exports-pkg\",\n  \"exports\": {\n    \"./public\": \"./public/barrel.ts\",\n    \"./internal\": \"./internal/consumer-1.ts\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/session-re-exports/pkg/public/barrel.ts",
    "content": "export { Box } from './implementation.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/session-re-exports/pkg/public/implementation.ts",
    "content": "export const internal = 2;\nexport const Box = 3;\n"
  },
  {
    "path": "packages/knip/fixtures/skip-exports-analysis/e2e/used.e2e.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/skip-exports-analysis/lib/index.js",
    "content": "import { used } from './used';\n\nused;\n\nexport { reexport } from './used';\n"
  },
  {
    "path": "packages/knip/fixtures/skip-exports-analysis/lib/used.js",
    "content": "export default 1;\nexport const used = 1;\nexport const unused = 1;\nexport const reexport = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/skip-exports-analysis/lib/used.test.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/skip-exports-analysis/package.json",
    "content": "{\n  \"name\": \"@fixtures/skip-exports-analysis\",\n  \"scripts\": {\n    \"test\": \"node --test\",\n    \"dev\": \"node --experimental-strip-types src/dev.ts\",\n    \"e2e\": \"playwright 'e2e/*.js'\",\n    \"watch\": \"nodemon --watch 'lib/**/*.js'\",\n    \"lib\": \"node lib/index.js\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/skip-exports-analysis/src/index.js",
    "content": "import { used } from './used';\n\nused;\n\nexport { reexport } from './used';\n"
  },
  {
    "path": "packages/knip/fixtures/skip-exports-analysis/src/used.js",
    "content": "export default 1;\nexport const used = 1;\nexport const unused = 1;\nexport const reexport = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/skip-exports-analysis/src/used.test.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/subpath-import/entry.ts",
    "content": "import dep from '#dep';\ndep;\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-import/knip.json",
    "content": "{\n  \"entry\": [\"entry.ts!\"],\n  \"project\": [\"**/*.{js,ts}!\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-import/lib/dep-polyfill.d.ts",
    "content": "declare const identifier: string;\nexport default identifier;\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-import/lib/dep-polyfill.js",
    "content": "export default 'dep-polyfill';\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-import/package.json",
    "content": "{\n  \"name\": \"@fixtures/subpath-import\",\n  \"imports\": {\n    \"#dep\": {\n      \"node\": \"dep-node-native\",\n      \"default\": \"./lib/dep-polyfill.js\"\n    }\n  },\n  \"dependencies\": {\n    \"dep-node-native\": \"^1.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-import/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"moduleResolution\": \"node16\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-import-from-plugin/.eslintrc.json",
    "content": "{\n  \"root\": true,\n  \"extends\": [\"#dep\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-import-from-plugin/entry.ts",
    "content": "//\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-import-from-plugin/knip.json",
    "content": "{\n  \"entry\": [\"entry.ts!\"],\n  \"project\": [\"**/*.{js,ts}!\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-import-from-plugin/lib/dep-polyfill.d.ts",
    "content": "declare const identifier: string;\nexport default identifier;\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-import-from-plugin/lib/dep-polyfill.js",
    "content": "export default 'dep-polyfill';\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-import-from-plugin/package.json",
    "content": "{\n  \"name\": \"@fixtures/subpath-import-from-plugin\",\n  \"imports\": {\n    \"#dep\": {\n      \"node\": \"dep-node-native\",\n      \"default\": \"./lib/dep-polyfill.js\"\n    }\n  },\n  \"dependencies\": {\n    \"dep-node-native\": \"^1.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-import-from-plugin/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"moduleResolution\": \"node16\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-imports-outdir/dist/src/index.d.ts",
    "content": "export * from './test/test1.js';\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-imports-outdir/dist/src/index.js",
    "content": "export * from './test/test1.js';\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-imports-outdir/dist/src/test/test1.d.ts",
    "content": "export declare const test1: () => string;\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-imports-outdir/dist/src/test/test1.js",
    "content": "import { test2 } from '#src/test/test2.js';\nexport const test1 = () => {\n    return test2();\n};\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-imports-outdir/dist/src/test/test2.d.ts",
    "content": "export declare const test2: () => string;\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-imports-outdir/dist/src/test/test2.js",
    "content": "export const test2 = () => {\n    return 'test2';\n};\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-imports-outdir/knip.json",
    "content": "{\n  \"entry\": [\"src/index.ts\"],\n  \"project\": [\"src/**/*.ts\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-imports-outdir/package.json",
    "content": "{\n  \"name\": \"@fixtures/subpath-imports-outdir\",\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./dist/src/index.d.ts\",\n      \"default\": \"./dist/src/index.js\"\n    }\n  },\n  \"imports\": {\n    \"#src/*\": \"./dist/src/*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-imports-outdir/src/index.ts",
    "content": "export * from './test/test1.js';\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-imports-outdir/src/test/test1.ts",
    "content": "import { test2 } from '#src/test/test2.js';\n\nexport const test1 = () => {\n  return test2();\n};\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-imports-outdir/src/test/test2.ts",
    "content": "export const test2 = () => {\n  return 'test2';\n};\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-imports-outdir/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"module\": \"NodeNext\",\n    \"moduleResolution\": \"NodeNext\",\n    \"outDir\": \"dist\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-patterns/knip.json",
    "content": "{\n  \"entry\": [\"src/entry.ts!\"],\n  \"project\": [\"src/**/*.{js,ts}!\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-patterns/package.json",
    "content": "{\n  \"name\": \"@fixtures/subpath-patterns\",\n  \"imports\": {\n    \"#internals/*\": \"./src/internals/*.ts\",\n    \"#internals-explicit-ext/*\": \"./src/internals/*\",\n    \"#internals-alias/used.alt\": \"./src/internals/used.alt\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-patterns/src/entry.ts",
    "content": "import used from '#internals/used';\nimport '#internals-explicit-ext/used.ext';\nimport '#internals-alias/used.alt';\nused;\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-patterns/src/internals/unused.ts",
    "content": "export default 'unused';\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-patterns/src/internals/used.alt",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/subpath-patterns/src/internals/used.ext",
    "content": "\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-patterns/src/internals/used.ts",
    "content": "export default 'content';\n"
  },
  {
    "path": "packages/knip/fixtures/subpath-patterns/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"module\": \"Node16\",\n    \"moduleResolution\": \"node16\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tagged-template-literal/Component.astro",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/tagged-template-literal/ScriptTag.astro",
    "content": "---\nimport dedent from 'dedent';\n---\n\n<div>Content</div>\n\n<script>\n  import { app } from './app.ts';\n  console.log(app);\n</script>\n"
  },
  {
    "path": "packages/knip/fixtures/tagged-template-literal/app.ts",
    "content": "export const app = 'app';\n"
  },
  {
    "path": "packages/knip/fixtures/tagged-template-literal/index.astro",
    "content": "---\nimport dedent from 'dedent';\nimport Component from './Component.astro';\n\nAstro.response.headers.set('Cache-Control', `max-age=1800, s-maxage=1800`);\n\nconst dedented = dedent`\n  import Button from './Button'\n  import type { FC } from 'react'\n`;\n\nconst message = `Hello ${dedented}`;\n---\n\n<Component>\n"
  },
  {
    "path": "packages/knip/fixtures/tagged-template-literal/index.ts",
    "content": "import 'astro';\nimport dedent from 'dedent';\nimport './app.ts';\nimport './index.astro';\nimport './Component.astro';\nimport './ScriptTag.astro';\n\nconst dedented = dedent`\n  import Button from './Button'\n  import type { FC } from 'react'\n`;\n\ndedented;\n"
  },
  {
    "path": "packages/knip/fixtures/tagged-template-literal/package.json",
    "content": "{\n  \"name\": \"@fixtures/tagged-template-literal\",\n  \"dependencies\": {\n    \"astro\": \"*\",\n    \"dedent\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tags-cli/index.ts",
    "content": "import * as NS from './tags.js';\nimport { MyClass, MyEnum } from './tags.js';\nimport { ignored, notIgnored } from './unimported.js';\n\nconst xm = MyEnum.UsedUntagged;\n\nconst y = new MyClass();\n"
  },
  {
    "path": "packages/knip/fixtures/tags-cli/package.json",
    "content": "{\n  \"name\": \"@fixtures/tags-cli\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tags-cli/tags.ts",
    "content": "export const UnusedUntagged = 1;\n\n/**\n * @custom\n */\nexport const UnusedCustom = 1;\n\n/**\n * @internal\n */\nexport const UnusedInternal = 1;\n\n/**\n * @internal\n * @custom\n */\nexport const UnusedCustomAndInternal = 1;\n\n/**\n * @internal\n */\nexport enum MyEnum {\n  UsedUntagged = 1,\n\n  UnusedUntagged = 1,\n\n  /**\n   * @custom\n   */\n  UnusedCustom = 1,\n\n  /**\n   * @internal\n   */\n  UnusedInternal = 1,\n\n  /**\n   * @internal\n   * @custom\n   */\n  UnusedCustomAndInternal = 1,\n}\n\nexport class MyClass {\n  UnusedUntagged = 1;\n\n  /**\n   * @custom\n   */\n  UnusedCustom = 1;\n\n  /**\n   * @internal\n   */\n  UnusedInternal = 1;\n\n  /**\n   * @internal\n   * @custom\n   */\n  UnusedCustomAndInternal = 1;\n}\n\n/** @custom */\nexport class MyCustomClass {}\n\n/** @custom */\nexport enum MyCustomEnum {}\n"
  },
  {
    "path": "packages/knip/fixtures/tags-cli/unimported.ts",
    "content": "/** @custom */\nexport const ignored = 1;\n\nexport const notIgnored = 1;\n\n/** @custom */\nexport const unimported = 1;\n\nexport const unimportedUntagged = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/tags-exclude/index.ts",
    "content": "import * as NS from './tags.js';\n\nNS;\n"
  },
  {
    "path": "packages/knip/fixtures/tags-exclude/knip.json",
    "content": "{\n  \"tags\": [\"-lintignore\", \"-@tagged\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tags-exclude/package.json",
    "content": "{\n  \"name\": \"@fixtures/tags-exclude\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tags-exclude/tags.ts",
    "content": "export const untagged = 1;\n\n/** @lintignore */\nexport const tagged = 0;\n\n/** @tagged */\nexport const taggedToo = 2;\n"
  },
  {
    "path": "packages/knip/fixtures/tags-include/index.ts",
    "content": "import * as NS from './tags.js';\n\nNS;\n"
  },
  {
    "path": "packages/knip/fixtures/tags-include/knip.json",
    "content": "{\n  \"tags\": [\"lintignore\", \"+@tagged\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tags-include/package.json",
    "content": "{\n  \"name\": \"@fixtures/tags-include\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tags-include/tags.ts",
    "content": "export const untagged = 1;\n\n/** @lintignore */\nexport const tagged = 0;\n\n/** @tagged */\nexport const taggedToo = 2;\n"
  },
  {
    "path": "packages/knip/fixtures/trace/barrel.ts",
    "content": "export * from './require.ts';\nexport * as STR from './string.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/trace/index.ts",
    "content": "import './module.ts';\n"
  },
  {
    "path": "packages/knip/fixtures/trace/module.ts",
    "content": "import { truncate } from './string.ts';\nimport { truncate as trunc } from './string.ts';\nimport * as NS from './barrel.ts';\nimport { CONTAINER as ROOT } from './shared.ts';\n\ntruncate;\ntrunc;\nNS.STR.truncate;\nNS.STR.leftPad;\nROOT.NS.resolve;\n"
  },
  {
    "path": "packages/knip/fixtures/trace/package.json",
    "content": "{\n  \"name\": \"@fixtures/trace\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/trace/require.ts",
    "content": "export const resolve = () => {};\nexport default function require() {}\n"
  },
  {
    "path": "packages/knip/fixtures/trace/shared.ts",
    "content": "export { resolve } from './barrel.ts';\nexport { truncate as shorten } from './string.ts';\n\nimport * as NS from './require.ts';\nexport const CONTAINER = { NS };\n"
  },
  {
    "path": "packages/knip/fixtures/trace/string.ts",
    "content": "export const truncate = () => {};\nexport const leftPad = () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/treat-config-hints-as-errors/index.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/treat-config-hints-as-errors/package.json",
    "content": "{\n  \"name\": \"@fixtures/treat-config-hints-as-errors\",\n  \"knip\": {\n    \"ignoreDependencies\": [\n      \"pineapple\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/treat-config-hints-as-errors2/index.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/treat-config-hints-as-errors2/package.json",
    "content": "{\n  \"name\": \"@fixtures/treat-config-hints-as-errors2\",\n  \"knip\": {\n    \"treatConfigHintsAsErrors\": true,\n    \"ignoreDependencies\": [\n      \"bananas\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ts-namespace/index.ts",
    "content": "import { Fruits, Animals } from './members.ts';\nimport * as NS from './members.ts';\nimport { standalone, Standalone } from './members.ts';\nimport { Types } from './types.ts';\nimport { Validator, format, Status } from './merged.ts';\nimport { Colors } from './modules.ts';\n\nconsole.log(Fruits.apple + Fruits.Tropical.mango);\n\nconsole.log(Animals.cat + Animals.Birds.eagle);\n\nconsole.log(NS.Shapes.circle + NS.Shapes.Nested.triangle);\n\nconsole.log(standalone + Standalone.value + Standalone.Nested.deep);\n\ntype T = Types.UsedType;\nconst v: Types.UsedInterface = { value: '' };\n\nnew Validator().validate();\nconsole.log(Validator.maxLength);\n\nconsole.log(format('hello') + format.separator);\n\nconsole.log(Status.Active, Status.label(Status.Active));\n\nconsole.log(Colors.red + Colors.Shades.dark);\n"
  },
  {
    "path": "packages/knip/fixtures/ts-namespace/members.ts",
    "content": "export namespace Fruits {\n  export const apple = 1;\n  export const unusedBanana = 2;\n\n  export namespace Tropical {\n    export const mango = 3;\n    export const unusedPapaya = 4;\n  }\n}\n\nexport namespace Animals {\n  export const cat = 1;\n  export const unusedDog = 2;\n\n  export namespace Birds {\n    export const eagle = 3;\n  }\n}\n\nexport namespace Shapes {\n  export const circle = 1;\n  export const unusedSquare = 2;\n\n  export namespace Nested {\n    export const triangle = 3;\n  }\n}\n\nexport const standalone = 0;\nexport namespace Standalone {\n  export const value = 1;\n  export const unusedValue = 2;\n\n  export namespace Nested {\n    export const deep = 3;\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ts-namespace/merged.ts",
    "content": "export class Validator {\n  validate() {\n    return true;\n  }\n}\nexport namespace Validator {\n  export const maxLength = 255;\n  export const unusedMinLength = 0;\n}\n\nexport function format(value: string) {\n  return value.trim();\n}\nexport namespace format {\n  export const separator = ',';\n  export const unusedPadding = ' ';\n}\n\nexport enum Status {\n  Active,\n  Inactive,\n}\nexport namespace Status {\n  export function label(s: Status) {\n    return s === Status.Active ? 'active' : 'inactive';\n  }\n  export const unusedDefault = Status.Active;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ts-namespace/modules.ts",
    "content": "// oxlint-disable typescript/prefer-namespace-keyword\nexport module Colors {\n  export const red = 1;\n  export const unusedBlue = 2;\n\n  export module Shades {\n    export const dark = 3;\n    export const unusedLight = 4;\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ts-namespace/package.json",
    "content": "{\n  \"name\": \"@fixtures/ts-namespace\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/ts-namespace/types.ts",
    "content": "export namespace Types {\n  export type UsedType = string;\n  export type UnusedType = number;\n  export interface UsedInterface {\n    value: string;\n  }\n  export interface UnusedInterface {\n    value: number;\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tsc-files-mode/package.json",
    "content": "{\n  \"name\": \"@fixtures/tsc-files-mode\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tsc-files-mode/src/declare-global.ts",
    "content": "declare global {\n  const MY_GLOBAL: string;\n}\n\nexport {};\n"
  },
  {
    "path": "packages/knip/fixtures/tsc-files-mode/src/declare-module.ts",
    "content": "declare module 'some-module';\n"
  },
  {
    "path": "packages/knip/fixtures/tsc-files-mode/src/excluded.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/tsc-files-mode/src/index.ts",
    "content": "import { helper } from './module.js';\n\nexport const main = () => helper();\n"
  },
  {
    "path": "packages/knip/fixtures/tsc-files-mode/src/module.ts",
    "content": "export const helper = () => 'helper';\n\nexport const unused = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/tsc-files-mode/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"strict\": true\n  },\n  \"include\": [\"src/**/*.ts\"],\n  \"exclude\": [\"src/excluded.ts\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-extends/boilerplate/tsconfig.base.json",
    "content": "{\n  \"extends\": [\"@tsconfig/esm/tsconfig.json\"],\n  \"compilerOptions\": {\n    \"sourceMap\": true\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-extends/package.json",
    "content": "{\n  \"name\": \"@fixtures/tsconfig-extends\",\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"devDependencies\": {\n    \"@tsconfig/esm\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-extends/packages/frontend/package.json",
    "content": "{\n  \"name\": \"@fixtures/tsconfig-extends__frontend\",\n  \"dependencies\": {\n    \"hastscript\": \"*\"\n  },\n  \"devDependencies\": {\n    \"@types/hast\": \"*\",\n    \"typescript\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-extends/packages/frontend/src/index.tsx",
    "content": "import type { Element, Root } from 'hast';\n\nexport const Identifier = (): Element | Root => <g />;\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-extends/packages/frontend/tsconfig.json",
    "content": "{\n  \"extends\": \"../../boilerplate/tsconfig.base.json\",\n  \"compilerOptions\": {\n    \"jsx\": \"react-jsx\",\n    \"jsxImportSource\": \"hastscript/svg\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-include-dir/package.json",
    "content": "{\n  \"name\": \"tsconfig-include-dir\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-include-dir/src/helper.ts",
    "content": "export const helper = () => 'help';\nexport const unused = () => 'unused';\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-include-dir/src/index.ts",
    "content": "import { helper } from './helper';\nhelper();\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-include-dir/src/types.d.ts",
    "content": "declare module '*.svg' {\n  const content: string;\n  export default content;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-include-dir/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"strict\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-nested-paths/package.json",
    "content": "{\n  \"name\": \"@fixtures/tsconfig-nested-paths\",\n  \"dependencies\": {\n    \"typescript\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-nested-paths/shared/utils.ts",
    "content": "export const rootUtil = () => 'root';\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-nested-paths/src/index.ts",
    "content": "import { rootUtil } from '@root/utils';\nimport { Button } from '@ui/button';\nimport { handler } from '@server/handler';\n\nrootUtil();\nButton();\nhandler();\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-nested-paths/src/server/handler.ts",
    "content": "export const handler = () => 'handler';\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-nested-paths/src/server/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@server/*\": [\"./*\"]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-nested-paths/src/ui/button.ts",
    "content": "export const Button = () => 'button';\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-nested-paths/src/ui/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@ui/*\": [\"./*\"]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-nested-paths/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@root/*\": [\"./shared/*\"]\n    }\n  },\n  \"references\": [\n    { \"path\": \"./src/ui/tsconfig.json\" },\n    { \"path\": \"./src/server/tsconfig.json\" }\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-paths-extends/package.json",
    "content": "{\n  \"name\": \"@fixtures/tsconfig-paths-extends\",\n  \"knip\": {\n    \"entry\": \"src/cli.ts\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-paths-extends/src/cli.ts",
    "content": "import { main, util } from '~/main';\n\nmain;\nutil;\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-paths-extends/src/index.ts",
    "content": "import main from '~/lib/main';\nimport util from '~/util/index';\n\nexport { main, util };\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-paths-extends/src/lib/main.ts",
    "content": "export default 1;\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-paths-extends/src/util/index.ts",
    "content": "export default 1;\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-paths-extends/tsconfig.base.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \"./src\",\n    \"paths\": {\n      \"~/main\": [\"./index.ts\"],\n      \"~/lib/*\": [\"./lib/*\"],\n      \"~/util/*\": [\"./util/*\"]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-paths-extends/tsconfig.json",
    "content": "{\n  \"extends\": \"./tsconfig.base\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-preset-strict/index.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/tsconfig-preset-strict/package.json",
    "content": "{\n  \"name\": \"@fixtures/tsconfig-preset-strict\",\n  \"devDependencies\": {\n    \"@tsconfig/strictest\": \"*\",\n    \"typescript\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-preset-strict/tsconfig.json",
    "content": "{\n  \"extends\": [\"@tsconfig/strictest/tsconfig.json\"],\n  \"compilerOptions\": {}\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-rootdirs/generated/src/index.generated.ts",
    "content": "export type NUM = number;\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-rootdirs/package.json",
    "content": "{\n  \"name\": \"@fixtures/tsconfig-rootdirs\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-rootdirs/src/index.ts",
    "content": "import type { NUM } from './index.generated.js';\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-rootdirs/tsconfig.json",
    "content": "{\n  \"files\": [],\n  \"references\": [\n    {\n      \"path\": \"./tsconfig.source.json\"\n    }\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/tsconfig-rootdirs/tsconfig.source.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"rootDirs\": [\".\", \"./generated\"]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/type-in-type/index.ts",
    "content": "import './module.js';\n"
  },
  {
    "path": "packages/knip/fixtures/type-in-type/module.ts",
    "content": "import type { Union, Wrapped, Mapped, Tuple, Intersection, Conditional, Nested } from './types.js';\n\nexport const create = (): Union => ({\n  foo: 'bar',\n});\n\nconst list: Wrapped = [];\n\nconst map: Mapped = new Map();\n\nconst tuple: Tuple = [{} as any, {} as any];\n\nconst both: Intersection = {} as any;\n\nconst cond: Conditional = {} as any;\n\nconst nested: Nested = new Set();\n"
  },
  {
    "path": "packages/knip/fixtures/type-in-type/package.json",
    "content": "{\n  \"name\": \"@fixtures/type-in-type\",\n  \"type\": \"module\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/type-in-type/types.ts",
    "content": "interface SharedProps {\n  foo: string;\n}\n\nexport type B = SharedProps;\n\nexport interface A extends SharedProps {\n  transform: () => void;\n}\n\nexport type Union = A | B;\n\nexport type Wrapped = Array<A | B>;\n\nexport type Mapped = Map<string, A>;\n\nexport type Tuple = [A, B];\n\nexport type Intersection = A & B;\n\nexport type Conditional = A extends B ? A : B;\n\nexport type Nested = Set<Array<A>>;\n\nexport type Func = () => void;\n"
  },
  {
    "path": "packages/knip/fixtures/type-in-value-export/package.json",
    "content": "{\n  \"name\": \"@fixtures/type-in-value-export\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/type-in-value-export/src/api.ts",
    "content": "interface PointsData {\n  items: string[];\n  total: number;\n}\n\nexport type GetPoints = Promise<PointsData>;\nexport type GetPointsResponse = PointsData;\nexport type GetPointsParams = { limit: number };\n\nexport async function fetchPoints(params: GetPointsParams): GetPoints {\n  const data: GetPointsResponse = { items: [], total: params.limit };\n  return data;\n}\n"
  },
  {
    "path": "packages/knip/fixtures/type-in-value-export/src/index.ts",
    "content": "import { fetchPoints } from './api';\n\nfetchPoints({ limit: 10 });\n"
  },
  {
    "path": "packages/knip/fixtures/types/index.ts",
    "content": "import m from 'micromatch';\nm;\n"
  },
  {
    "path": "packages/knip/fixtures/types/package.json",
    "content": "{\n  \"name\": \"@fixtures/types\",\n  \"scripts\": {\n    \"pack\": \"webpack\"\n  },\n  \"devDependencies\": {\n    \"micromatch\": \"*\",\n    \"webpack\": \"*\",\n    \"webpack-cli\": \"*\",\n    \"@types/micromatch\": \"*\",\n    \"@types/webpack\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/types/tsconfig.json",
    "content": "{}\n"
  },
  {
    "path": "packages/knip/fixtures/url-import-meta-url/file.css",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/url-import-meta-url/file.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/url-import-meta-url/index.ts",
    "content": "new URL('./file.css', import.meta.url);\nnew URL('./file.js', import.meta.url);\nnew URL('./404.js', import.meta.url);\n"
  },
  {
    "path": "packages/knip/fixtures/url-import-meta-url/knip.js",
    "content": "export default { compilers: { css: _ => '' } };\n"
  },
  {
    "path": "packages/knip/fixtures/url-import-meta-url/package.json",
    "content": "{\n  \"name\": \"@fixtures/url-import-meta-url\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces/apps/backend/index.ts",
    "content": "import defaultA, { usedExportFromLibA } from '@fixtures/workspaces__shared';\nimport defaultB, { usedExportFromLibB } from '@fixtures/workspaces__tools';\nimport { globby } from 'globby';\nimport yaml from 'js-yaml';\n\ndefaultA;\ndefaultB;\nusedExportFromLibA;\nusedExportFromLibB;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces/apps/backend/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces__backend\",\n  \"main\": \"./index.js\",\n  \"dependencies\": {\n    \"@fixtures/workspaces__shared\": \"1.0.0\",\n    \"@fixtures/workspaces__tools\": \"1.0.0\",\n    \"picomatch\": \"*\",\n    \"next\": \"*\"\n  },\n  \"devDependencies\": {\n    \"@fixtures/workspaces__tsconfig\": \"1.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces/apps/backend/tsconfig.json",
    "content": "{\n  \"extends\": \"@fixtures/workspaces__tsconfig/tsconfig.base.json\",\n  \"compilerOptions\": {\n    \"baseUrl\": \"./\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces/apps/frontend/index.ts",
    "content": "import a from '@fixtures/workspaces__shared';\nimport b from '@fixtures/workspaces__tools';\nimport { n } from 'vanilla-js';\n\na;\nb;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces/apps/frontend/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces__frontend\",\n  \"main\": \"./index.js\",\n  \"dependencies\": {\n    \"@fixtures/workspaces__shared\": \"1.0.0\",\n    \"@fixtures/workspaces__tools\": \"1.0.0\"\n  },\n  \"devDependencies\": {\n    \"@fixtures/workspaces__tsconfig\": \"1.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces/apps/frontend/tsconfig.json",
    "content": "{\n  \"extends\": \"@fixtures/workspaces__tsconfig/tsconfig.base.json\",\n  \"compilerOptions\": {\n    \"baseUrl\": \"./\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces/apps/ignored-workspace/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces__ignored-workspace\",\n  \"dependencies\": {\n    \"ghost\": \"*\",\n    \"packages\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces/docs/dangling.ts",
    "content": "export const docs = '';\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces/docs/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces__docs\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces/local/tsconfig/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces__tsconfig\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces/local/tsconfig/tsconfig.base.json",
    "content": "{\n  \"$schema\": \"http://json.schemastore.org/tsconfig\",\n  \"compilerOptions\": {}\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces\",\n  \"workspaces\": [\n    \"docs\",\n    \"apps/*\",\n    \"local/tsconfig\",\n    \"packages/*\"\n  ],\n  \"dependencies\": {\n    \"cypress\": \"*\",\n    \"minimist\": \"*\",\n    \"typescript\": \"*\",\n    \"zod\": \"*\"\n  },\n  \"scripts\": {\n    \"build\": \"tsc\"\n  },\n  \"knip\": {\n    \"ignoreWorkspaces\": [\n      \"apps/ignored-workspace\"\n    ],\n    \"workspaces\": {\n      \"apps/*\": {\n        \"entry\": \"index.ts!\",\n        \"project\": \"**/*.ts!\"\n      },\n      \"packages/*\": {\n        \"entry\": \"index.ts!\",\n        \"project\": [\n          \"**/*.ts!\",\n          \"!ignored/*.ts\"\n        ]\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces/packages/shared/index.ts",
    "content": "import type { UsedInt } from './types';\n\nexport default 1;\n\nexport const usedExportFromLibA: UsedInt = 2;\n\nexport const unusedExportFromLibA: UsedInt = 3;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces/packages/shared/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces__shared\",\n  \"main\": \"./index.js\",\n  \"scripts\": {\n    \"e2e\": \"cypress\"\n  },\n  \"devDependencies\": {\n    \"@fixtures/workspaces__tsconfig\": \"1.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces/packages/shared/tsconfig.json",
    "content": "{\n  \"extends\": \"@fixtures/workspaces__tsconfig/tsconfig.base.json\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces/packages/shared/types.ts",
    "content": "export type UsedInt = number;\n\nexport enum UnusedEnum {\n  KEY,\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces/packages/tools/ignored/index.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/workspaces/packages/tools/index.ts",
    "content": "import { usefulUsedUtil } from './utils';\n\nexport default 1;\n\nexport const usedExportFromLibB = usefulUsedUtil + 2;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces/packages/tools/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces__tools\",\n  \"main\": \"./index.js\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces/packages/tools/tsconfig.json",
    "content": "{\n  \"extends\": \"@fixtures/workspaces__tsconfig/tsconfig.base.json\",\n  \"compilerOptions\": {\n    \"baseUrl\": \"./\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces/packages/tools/utils.ts",
    "content": "export const usefulUsedUtil = 0;\n\nexport const helperFn = () => 1;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\",\n    \"paths\": {\n      \"@fixtures/workspaces__shared\": [\"./packages/shared/index.ts\"],\n      \"@fixtures/workspaces__tools\": [\"./packages/tools/index.ts\"]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-bun-test/package.json",
    "content": "{\n  \"name\": \"@knip/workspaces-bun-test\",\n  \"scripts\": {\n    \"test\": \"bun test\"\n  },\n  \"workspaces\": [\n    \"packages/*\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-bun-test/packages/lib/package.json",
    "content": "{\n  \"name\": \"@knip/workspaces-bun-test__lib\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-bun-test/packages/lib/src/index.ts",
    "content": "export const sharedFunction = () => {\n  const shared = 'sha' + 'red';\n  return shared;\n};\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-bun-test/packages/lib/tests/index.test.ts",
    "content": "import { sharedFunction } from '../src/index';\nimport { it, expect } from 'bun:test';\n\nit('lib', () => {\n  expect(sharedFunction()).toBe('shared');\n});\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-bun-test/packages/lib/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"declaration\": true\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"dist/**/*\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-circular/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-circular\",\n  \"workspaces\": [\n    \"packages/*\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-circular/packages/lib-a/index.ts",
    "content": "import circularB from '@fixtures/workspaces__lib-b';\nimport circularC from '@fixtures/workspaces__lib-c';\n\ncircularB;\ncircularC;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-circular/packages/lib-a/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-circular__lib-a\",\n  \"dependencies\": {\n    \"@fixtures/workspaces__lib-b\": \"1.0.0\",\n    \"@fixtures/workspaces__lib-c\": \"1.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-circular/packages/lib-b/index.ts",
    "content": "export default 1;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-circular/packages/lib-b/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-circular__lib-b\",\n  \"dependencies\": {}\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-circular/packages/lib-c/index.ts",
    "content": "import circularA from '@fixtures/workspaces__lib-a';\n\ncircularA;\n\nexport default 1;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-circular/packages/lib-c/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-circular__lib-c\",\n  \"dependencies\": {\n    \"@fixtures/workspaces__lib-a\": \"1.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-circular-symlinks/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-symlinks\",\n  \"workspaces\": [\n    \"packages/*\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-circular-symlinks/packages/lib-a/index.ts",
    "content": "export const libA = 'lib-a';\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-circular-symlinks/packages/lib-a/package.json",
    "content": "{\n  \"name\": \"@fixtures/lib-a\",\n  \"version\": \"1.0.0\",\n  \"main\": \"./index.ts\",\n  \"exports\": {\n    \".\": \"./index.ts\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-circular-symlinks/packages/lib-b/index.ts",
    "content": "export const libB = 'lib-b';\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-circular-symlinks/packages/lib-b/package.json",
    "content": "{\n  \"name\": \"@fixtures/lib-b\",\n  \"version\": \"1.0.0\",\n  \"main\": \"./index.ts\",\n  \"exports\": {\n    \".\": \"./index.ts\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-cross-reference/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-cross-reference\",\n  \"workspaces\": [\n    \"packages/*\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-cross-reference/packages/lib-a/index.ts",
    "content": "import a from './mod-a';\na;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-cross-reference/packages/lib-a/mod-a.ts",
    "content": "import circularA, { B1 } from '@fixtures/workspaces-cross-reference__lib-b/mod-b';\n\nexport const A = B1;\n\nexport const unused = true;\n\nexport default circularA;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-cross-reference/packages/lib-a/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-cross-reference__lib-a\",\n  \"dependencies\": {\n    \"@fixtures/workspaces-cross-reference__lib-b\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-cross-reference/packages/lib-a/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \"./\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-cross-reference/packages/lib-b/index.ts",
    "content": "export default 1;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-cross-reference/packages/lib-b/mod-b.ts",
    "content": "import { A } from '@fixtures/workspaces-cross-reference__lib-a/mod-a';\n\nexport const B1 = 'B1';\n\nexport const unused = true;\n\nexport default A;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-cross-reference/packages/lib-b/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-cross-reference__lib-b\",\n  \"dependencies\": {\n    \"@fixtures/workspaces-cross-reference__lib-a\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-cross-reference/packages/lib-b/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \"./\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-dts/.gitignore",
    "content": "node_modules\nbuild\n.DS_Store\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-dts/knip.ts",
    "content": "/** @type {import('knip').KnipConfig} */\nconst config = {\n  workspaces: {\n    'packages/shared': {\n      includeEntryExports: true,\n    },\n  },\n};\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-dts/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-dts\",\n  \"private\": true,\n  \"workspaces\": [\n    \"packages/*\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-dts/packages/client/client.d.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/workspaces-dts/packages/client/index.js",
    "content": "import { usedFunction } from '@fixtures/workspaces-dts__shared';\n\nusedFunction();\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-dts/packages/client/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-dts__client\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"types\": \"client.d.ts\",\n  \"dependencies\": {\n    \"@fixtures/workspaces-dts__shared\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-dts/packages/client/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"include\": [\"**/*.js\"],\n  \"exclude\": []\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-dts/packages/server/index.js",
    "content": "import { usedFunction } from '@fixtures/workspaces-dts__shared';\n\nusedFunction();\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-dts/packages/server/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-dts__server\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"typings\": \"server.d.ts\",\n  \"dependencies\": {\n    \"@fixtures/workspaces-dts__shared\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-dts/packages/server/server.d.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/workspaces-dts/packages/server/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"include\": [\"**/*.js\"],\n  \"exclude\": []\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-dts/packages/shared/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-dts__shared\",\n  \"private\": true,\n  \"main\": \"src/index.js\",\n  \"types\": \"build/index.d.ts\",\n  \"type\": \"module\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-dts/packages/shared/src/index.js",
    "content": "export { unusedFunction } from './unused-function.js';\nexport { usedFunction } from './used-function.js';\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-dts/packages/shared/src/unused-function.js",
    "content": "export const unusedFunction = () => 'bar';\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-dts/packages/shared/src/used-function.js",
    "content": "export const usedFunction = () => 'bar';\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-dts/packages/shared/tsconfig.build.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"include\": [\"src\"],\n  \"compilerOptions\": {\n    \"declaration\": true,\n    \"declarationMap\": true,\n    \"sourceMap\": true,\n    \"emitDeclarationOnly\": true,\n    \"outDir\": \"build\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-dts/packages/shared/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"exclude\": [\"build\"],\n  \"include\": [\"**/*.js\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-dts/tsconfig.json",
    "content": "{\n  \"include\": [\"./**/*.js\"],\n  \"exclude\": [\"node_modules\", \"apps\", \"packages\"],\n  \"compilerOptions\": {\n    \"lib\": [\"DOM\", \"DOM.Iterable\", \"ESNext\"],\n    \"allowJs\": true,\n    \"checkJs\": true,\n    \"target\": \"ESNext\",\n    \"module\": \"NodeNext\",\n    \"moduleResolution\": \"NodeNext\",\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": true,\n    \"strict\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"exactOptionalPropertyTypes\": true,\n    \"noPropertyAccessFromIndexSignature\": true\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-entry-files/.storybook/main.ts",
    "content": "const config = {\n  stories: ['../packages/**/src/**/*.stories.*'],\n};\n\nexport default config;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-entry-files/knip.json",
    "content": "{\n  \"$schema\": \"https://unpkg.com/knip@6/schema.json\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-entry-files/package.json",
    "content": "{\n  \"scripts\": {\n    \"storybook\": \"storybook dev\"\n  },\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"devDependencies\": {\n    \"storybook\": \"*\",\n    \"@storybook/react\": \"*\"\n  },\n  \"name\": \"@fixtures/workspaces-entry-files\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-entry-files/packages/client/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-entry-files__client\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"main\": \"src/index.ts\",\n  \"dependencies\": {\n    \"@monorepo/shared\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-entry-files/packages/client/src/index.stories.tsx",
    "content": "import { Meta, StoryObj } from '@storybook/react';\n\nexport default {\n  title: 'test',\n} as Meta;\n\nexport const _default: StoryObj = {\n  render: () => null,\n};\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-entry-files/packages/client/src/index.ts",
    "content": "import { sharedFunction } from '@monorepo/shared';\n\nsharedFunction();\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-entry-files/packages/client/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"declaration\": true\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"dist/**/*\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-entry-files/packages/shared/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-entry-files__shared\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"main\": \"src/index.ts\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-entry-files/packages/shared/src/index.ts",
    "content": "export const sharedFunction = () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-entry-files/packages/shared/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"declaration\": true\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"dist/**/*\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-ignored/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-ignored\",\n  \"knip\": {\n    \"ignoreWorkspaces\": [\n      \"packages/c\",\n      \"packages/d*\",\n      \"packages/not-found\",\n      \"packages/g\",\n      \"packages/production!\",\n      \"packages/production-not-found!\",\n      \"packages/deep/**\",\n      \"packages/flat/*\",\n      \"packages/un/**/used\",\n      \"packages/wut/*\",\n      \"!packages/deep/unignored\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-ignored/packages/a/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-ignored__a\",\n  \"scripts\": {\n    \"start\": \"ignored\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-ignored/packages/b1/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-ignored__b1\",\n  \"scripts\": {\n    \"start\": \"ignored\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-ignored/packages/b2/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-ignored__b2\",\n  \"scripts\": {\n    \"start\": \"ignored\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-ignored/packages/c/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-ignored__c\",\n  \"scripts\": {\n    \"start\": \"ignored\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-ignored/packages/d1/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-ignored__d1\",\n  \"scripts\": {\n    \"start\": \"ignored\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-ignored/packages/d2/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-ignored__d2\",\n  \"scripts\": {\n    \"start\": \"ignored\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-ignored/packages/deep/er/h2/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-ignored__h2\",\n  \"scripts\": {\n    \"start\": \"ignored\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-ignored/packages/deep/unignored/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-ignored__unignored\",\n  \"scripts\": {\n    \"start\": \"unignored\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-ignored/packages/e/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-ignored__e\",\n  \"scripts\": {\n    \"start\": \"not-ignored\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-ignored/packages/flat/i1/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-ignored__i1\",\n  \"scripts\": {\n    \"start\": \"ignored\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-ignored/packages/g/main.c",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/workspaces-ignored/packages/production/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-ignored__production\",\n  \"scripts\": {\n    \"start\": \"ignored-in-production-mode\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-ignored/pnpm-workspace.yaml",
    "content": "packages:\n  - 'packages/*'\n  - 'packages/flat/*'\n  - 'packages/deep/**'\n  - '!packages/a'\n  - '!packages/b*'\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-include-entry-exports/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-include-entry-exports\",\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"knip\": {\n    \"workspaces\": {\n      \"packages/app\": {},\n      \"packages/lib\": {\n        \"includeEntryExports\": true\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-include-entry-exports/packages/app/index.js",
    "content": "export const unused = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-include-entry-exports/packages/app/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-include-entry-exports__app\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-include-entry-exports/packages/lib/index.js",
    "content": "export const unused = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-include-entry-exports/packages/lib/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-include-entry-exports__lib\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-module-resolution/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-module-resolution\",\n  \"private\": true,\n  \"workspaces\": [\n    \"packages/*\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-module-resolution/packages/workspace-a/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-module-resolution__workspace-a\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"main\": \"src/index.ts\",\n  \"dependencies\": {\n    \"@fixtures/workspaces-module-resolution__workspace-b\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-module-resolution/packages/workspace-a/src/index.ts",
    "content": "import { usedFunction } from '@fixtures/workspaces-module-resolution__workspace-b';\n\nusedFunction;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-module-resolution/packages/workspace-a/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"baseUrl\": \"src\",\n    \"declaration\": true,\n    \"paths\": {\n      \"#dummy\": [\"src\"]\n    }\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"dist/**/*\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-module-resolution/packages/workspace-b/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-module-resolution__workspace-b\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": \"./src/index.ts\",\n    \"./*\": \"./dist/*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-module-resolution/packages/workspace-b/src/exports.ts",
    "content": "export const someFunction = () => 'bar';\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-module-resolution/packages/workspace-b/src/index.ts",
    "content": "import '@fixtures/workspaces-module-resolution__workspace-b/self.js';\nexport * from 'exports';\nexport { usedFunction } from 'used-fn';\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-module-resolution/packages/workspace-b/src/self.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/workspaces-module-resolution/packages/workspace-b/src/used-fn.ts",
    "content": "import { someFunction } from 'exports';\n\nexport const usedFunction = () => {\n  someFunction();\n  return 'bar';\n};\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-module-resolution/packages/workspace-b/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"baseUrl\": \"src\",\n    \"declaration\": true,\n    \"paths\": {\n      \"#dummy-shared\": [\"src\"]\n    }\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"dist/**/*\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-module-resolution/tsconfig.json",
    "content": "{\n  \"files\": [],\n  \"compilerOptions\": {\n    \"allowSyntheticDefaultImports\": true,\n    \"declaration\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"module\": \"nodenext\",\n    \"moduleResolution\": \"nodenext\",\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"target\": \"ES2022\"\n  },\n  \"references\": [\n    { \"path\": \"packages/workspace-a\" },\n    { \"path\": \"packages/workspace-b\" }\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-nested/L-1-1/L-1-2/L-1-3/.eslintrc.json",
    "content": "{\n  \"plugins\": [\"prefer-let\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-nested/L-1-1/L-1-2/L-1-3/index.ts",
    "content": "import ignoredDependency from 'ignored-dep-L-3';\nimport global from 'ignored-dep-global';\nimport parentPkg from 'package-1-2';\nimport pkg from 'package-1-3';\n\nexport const program = () => null;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-nested/L-1-1/L-1-2/L-1-3/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-nested__L-1-3\",\n  \"scripts\": {\n    \"dev\": \"pm2-dev\",\n    \"lint\": \"eslint\",\n    \"start\": \"ignored-bin-L-2\",\n    \"global\": \"ignored-bin-global\"\n  },\n  \"dependencies\": {\n    \"package-1-3\": \"*\",\n    \"package-1-3-dev\": \"*\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-nested/L-1-1/L-1-2/index.ts",
    "content": "import global from 'ignored-dep-global';\nimport ignoredDependency from 'ignored-dep-L-3';\nimport pkg from 'package-1-2';\n\nexport const program = () => null;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-nested/L-1-1/L-1-2/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-nested__L-1-2\",\n  \"scripts\": {\n    \"start\": \"ignored-bin-L-2\",\n    \"global\": \"ignored-bin-global\"\n  },\n  \"dependencies\": {\n    \"package-1-2\": \"*\"\n  },\n  \"devDependencies\": {\n    \"package-1-2-dev\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-nested/L-1-1/index.ts",
    "content": "import pkg from 'package-1-1';\n\nexport const program = () => null;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-nested/L-1-1/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-nested__L-1-1\",\n  \"dependencies\": {\n    \"package-1-1\": \"*\"\n  },\n  \"devDependencies\": {\n    \"package-1-1-dev\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-nested/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-nested\",\n  \"workspaces\": [\n    \"L-1-1\",\n    \"L-1-1/L-1-2\",\n    \"L-1-1/L-1-2/L-1-3\"\n  ],\n  \"dependencies\": {\n    \"ignored-dep-global\": \"*\"\n  },\n  \"devDependencies\": {\n    \"unused-ignored-listed-dep\": \"*\",\n    \"pm2\": \"*\",\n    \"eslint-plugin-prefer-let\": \"*\"\n  },\n  \"knip\": {\n    \"ignoreBinaries\": [\n      \"ignored-bin-global\",\n      \"unused-ignored-bin-global\"\n    ],\n    \"ignoreDependencies\": [\n      \"ignored-dep-global\",\n      \"unused-ignored-listed-dep\",\n      \"unused-ignored-dep-global\"\n    ],\n    \"ignoreWorkspaces\": [\n      \"unused-ignored-workspace\"\n    ],\n    \"workspaces\": {\n      \".\": {},\n      \"L-1-1\": {},\n      \"L-1-1/L-1-2\": {\n        \"ignoreBinaries\": [\n          \"ignored-bin-L-2\",\n          \"unused-ignored-bin-L-2\"\n        ]\n      },\n      \"L-1-1/L-1-2/L-1-3\": {\n        \"ignoreDependencies\": [\n          \"ignored-dep-L-3\",\n          \"unused-ignored-dep-L-3\"\n        ]\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-noconfig-plugin/lib/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-noconfig-plugin__lib\",\n  \"exports\": {\n    \"./util\": \"./src/util.ts\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-noconfig-plugin/lib/src/util.test.ts",
    "content": "import { it } from 'jest';\nimport { funktion } from './util';\n\nit('runs funktion()', () => {\n  funktion();\n});\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-noconfig-plugin/lib/src/util.ts",
    "content": "export function funktion() {\n  //\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-noconfig-plugin/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-noconfig-plugin\",\n  \"workspaces\": [\n    \"lib\"\n  ],\n  \"dependencies\": {\n    \"@fixtures/workspaces__lib\": \"workspace:*\"\n  },\n  \"devDependencies\": {\n    \"jest\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-noconfig-plugin/src/index.ts",
    "content": "import { funktion } from '@fixtures/workspaces__lib/util';\n\nfunktion();\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-noconfig-plugin/src/util.test.ts",
    "content": "import { it } from 'jest';\nimport { funktion } from '@fixtures/workspaces__lib/util';\n\nit('runs funktion()', () => {\n  funktion();\n});\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-node-test/package.json",
    "content": "{\n  \"name\": \"@knip/workspaces-node-test\",\n  \"scripts\": {\n    \"test\": \"node --test\"\n  },\n  \"workspaces\": [\n    \"packages/*\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-node-test/packages/lib/package.json",
    "content": "{\n  \"name\": \"@knip/workspaces-node-test__lib\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-node-test/packages/lib/src/index.ts",
    "content": "export const sharedFunction = () => {\n  const shared = 'sha' + 'red';\n  return shared;\n};\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-node-test/packages/lib/tests/index.test.ts",
    "content": "import { sharedFunction } from '../src/index';\nimport { it } from 'node:test';\nimport assert from 'node:assert/strict';\n\nit('lib', () => {\n  assert.strictEqual(sharedFunction(), 'shared');\n});\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-node-test/packages/lib/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"declaration\": true\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"dist/**/*\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-paths\",\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"dependencies\": {},\n  \"knip\": {\n    \"workspaces\": {\n      \"packages/*\": {\n        \"entry\": \"src/index.ts\",\n        \"project\": \"src/**/*.ts\"\n      },\n      \"packages/lib-e\": {\n        \"paths\": {\n          \"@/*\": [\n            \"src/*\"\n          ]\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-a/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-paths__lib-a\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-a/src/dir/module-a.ts",
    "content": "export default 1;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-a/src/index.ts",
    "content": "import same from 'src/same';\nimport one from '@/dir/module-a';\nsame;\n\nexport default one;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-a/src/same.ts",
    "content": "export default 0;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-a/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \"./\",\n    \"paths\": {\n      \"@/*\": [\"src/*\"]\n    }\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-b/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-paths__lib-b\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-b/src/dir/module-b.ts",
    "content": "export default 2;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-b/src/index.ts",
    "content": "import two from '@/dir/module-b';\n\nexport default two;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-b/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \"./\",\n    \"paths\": {\n      \"@/*\": [\"src/*\"]\n    }\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-c/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-paths__lib-c\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-c/src/dir/module.ts",
    "content": "export default 3;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-c/src/index.ts",
    "content": "import three from 'dir/module';\n\nexport default three;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-c/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \"./src\"\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-d/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-paths__lib-d\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-d/src/dir/module.ts",
    "content": "export default 4;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-d/src/index.ts",
    "content": "import one from './dir/module';\n\nexport default one;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-d/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {}\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-e/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-paths__lib-e\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-e/src/dir/module-e.ts",
    "content": "export default 5;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-e/src/index.ts",
    "content": "import unresolved from 'not/found';\nimport five from '@/dir/module-e';\nunresolved;\n\nexport default five;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-e/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {}\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-f/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-paths__lib-f\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-f/src/index.ts",
    "content": "import alias from '#/same';\n\nexport default alias;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-f/src/same.ts",
    "content": "export default 0;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-f/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"paths\": {\n      \"#/*\": [\"./src/*\"]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-g/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-paths__lib-g\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-g/src/index.ts",
    "content": "import alias from '@dir/same';\n\nexport default alias;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-g/src/same.ts",
    "content": "export default 0;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths/packages/lib-g/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"paths\": {\n      \"@dir/*\": [\"./src/*\"]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths-compilers/knip.ts",
    "content": "const compiler = /<script\\b[^>]*>([\\s\\S]*?)<\\/script>/gm;\n\nexport default {\n  compilers: {\n    vue: text => {\n      const scripts = [];\n      let match: any[];\n      while ((match = compiler.exec(text))) scripts.push(match[1]);\n      return scripts.join(';');\n    },\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths-compilers/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-paths-compilers\",\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"dependencies\": {\n    \"vue\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths-compilers/packages/app/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-paths-compilers__app\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths-compilers/packages/app/src/components/App.vue",
    "content": "<script lang=\"ts\">\nimport SubComponent from '@components/Sub.vue';\nSubComponent;\n</script>\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths-compilers/packages/app/src/components/Child.vue",
    "content": "<template></template>\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\nexport default defineComponent({});\n</script>\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths-compilers/packages/app/src/components/Sub.vue",
    "content": "<template></template>\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\nexport default defineComponent({});\n</script>\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths-compilers/packages/app/src/main.ts",
    "content": "import App from '@components/App.vue';\nimport router from '@/router';\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths-compilers/packages/app/src/pages/Home.vue",
    "content": "<script lang=\"ts\">\nimport ChildComponent from '@components/Child.vue';\nChildComponent;\n</script>\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths-compilers/packages/app/src/router.ts",
    "content": "const routes = {\n  Home: import(/* vuery descriptive comment */ '@/pages/Home.vue'),\n};\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths-compilers/packages/app/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"baseUrl\": \"src\",\n    \"paths\": {\n      \"@/*\": [\"./*\"],\n      \"@components/*\": [\"./components/*\"]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-paths-compilers/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {}\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-circular/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-plugin-circular\",\n  \"workspaces\": [\n    \"packages/*\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-circular/packages/lib/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-plugin-circular__lib\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-circular/packages/lib/tsconfig.app.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"include\": [\"src/**/*.ts\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-circular/packages/lib/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.base.json\",\n  \"references\": [\n    { \"path\": \"./tsconfig.app.json\" },\n    { \"path\": \"./tsconfig.spec.json\" }\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-circular/packages/lib/tsconfig.spec.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-circular/tsconfig.base.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"rootDir\": \".\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/knip.json",
    "content": "{\n  \"vitest\": {\n    \"config\": [\"config/vitest.config.ts\"]\n  },\n  \"eslint\": true,\n  \"jest\": \"jest.config.js\",\n  \"babel\": { \"config\": \"rollup.config.ts\" },\n  \"workspaces\": {\n    \"packages/*\": {},\n    \"packages/shared\": {\n      \"storybook\": {\n        \"config\": \"components/storybook/main.ts\",\n        \"entry\": [\"components/storybook/{manager,preview}.ts\"]\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-plugin-config\",\n  \"scripts\": {\n    \"test\": \"jest\"\n  },\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"devDependencies\": {\n    \"eslint-config-airbnb\": \"*\",\n    \"jest\": \"^29.7.0\",\n    \"jest-phabricator\": \"^29.7.0\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/backend/config/vitest.config.ts",
    "content": "import { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n  test: {\n    root: '..',\n    include: ['**/*.vitest.ts'],\n    environment: 'node',\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/backend/eslint.config.js",
    "content": "import prettier from 'eslint-plugin-prettier';\nimport noSecrets from 'eslint-plugin-no-secrets';\n\nexport default [\n  'eslint:recommended',\n  {\n    plugins: {\n      prettier,\n      'no-secrets': noSecrets,\n    },\n  },\n];\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/backend/index.ts",
    "content": "export default 1;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/backend/index.vitest.ts",
    "content": "import { test } from 'vitest';\n\ntest('it', () => {});\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/backend/jest.config.js",
    "content": "module.exports = {\n  testResultsProcessor: 'jest-phabricator',\n  setupFiles: ['@fixtures/workspaces-plugin-config__shared/jest-setup.ts'],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/backend/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-plugin-config__backend\",\n  \"scripts\": {\n    \"test\": \"vitest\"\n  },\n  \"devDependencies\": {\n    \"@fixtures/workspaces-plugin-config__shared\": \"*\",\n    \"eslint-plugin-no-secrets\": \"*\",\n    \"eslint-plugin-prettier\": \"*\",\n    \"vitest\": \"^0.34.6\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/frontend/.eslintrc.js",
    "content": "module.exports = {\n  extends: ['airbnb'],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/frontend/components/component.js",
    "content": "export default function () {}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/frontend/components/component.test.js",
    "content": "import component from './component';\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/frontend/config/vitest.config.ts",
    "content": "import { defineProject } from 'vitest/config';\n\nexport default defineProject({\n  test: {\n    root: '..',\n    include: ['**/*.vitest.ts'],\n    environment: 'jsdom',\n  },\n});\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/frontend/index.ts",
    "content": "export default 1;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/frontend/index.vitest.ts",
    "content": "export default 1;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/frontend/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-plugin-config__frontend\",\n  \"scripts\": {\n    \"dev\": \"next dev\",\n    \"start\": \"next start\",\n    \"test\": \"vitest\"\n  },\n  \"dependencies\": {\n    \"next\": \"^14.0.1\"\n  },\n  \"devDependencies\": {\n    \"@fixtures/workspaces-plugin-config__tailwind\": \"*\",\n    \"@rollup/plugin-commonjs\": \"*\",\n    \"jsdom\": \"*\",\n    \"vitest\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/frontend/postcss.config.js",
    "content": "module.exports = require('@fixtures/workspaces-plugin-config__tailwind/postcss');\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/frontend/rollup.config.ts",
    "content": "import commonjs from '@rollup/plugin-commonjs';\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/shared/ava.config.js",
    "content": "export default {\n  files: ['**/*.ava.js'],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/shared/components/component.ava.js",
    "content": "import component from './component';\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/shared/components/component.js",
    "content": "export default function () {}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/shared/components/component.tales.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/shared/components/epic/component.fable.tsx",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/shared/components/epic/component.stories.mdx",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/shared/components/storybook/main.ts",
    "content": "module.exports = {\n  stories: [\n    '../*.tales.js',\n    {\n      directory: '../epic',\n    },\n    {\n      files: '*.fable.tsx',\n      directory: '../epic',\n    },\n  ],\n  core: {\n    builder: '@storybook/builder-vite',\n  },\n};\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/shared/components/storybook/manager.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/shared/components/storybook/preview.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/shared/dev-entry.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/shared/jest-setup.ts",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/shared/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-plugin-config__shared\",\n  \"scripts\": {\n    \"test\": \"ava\"\n  },\n  \"devDependencies\": {\n    \"ava\": \"^5.3.1\",\n    \"webpack\": \"*\",\n    \"@storybook/builder-vite\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/shared/production-entry.js",
    "content": ""
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/shared/webpack.config.js",
    "content": "require('webpack');\n\nconst config = [\n  {\n    entry: './production-entry.js',\n  },\n  {\n    mode: 'development',\n    entry: './dev-entry.js',\n  },\n];\n\nmodule.exports = config;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/tailwind/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-plugin-config__tailwind\",\n  \"main\": \"./postcss.js\",\n  \"dependencies\": {\n    \"@tailwindcss/typography\": \"^0.5.9\",\n    \"autoprefixer\": \"^10.4.14\",\n    \"postcss-import\": \"^15.1.0\",\n    \"tailwindcss\": \"^3.2.7\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-config/packages/tailwind/postcss.js",
    "content": "const config = {\n  plugins: [\n    require('tailwindcss'),\n    require('autoprefixer'),\n    require('postcss-import'),\n    require('@tailwindcss/typography'),\n  ],\n};\n\nmodule.exports = config;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-overlap/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-plugin-overlap\",\n  \"private\": true,\n  \"workspaces\": [\n    \"packages/*\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-overlap/packages/workspace-a/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-plugin-overlap__workspace-a\",\n  \"private\": true,\n  \"scripts\": {\n    \"docs\": \"typedoc\"\n  },\n  \"devDependencies\": {\n    \"@tsconfig/node16\": \"*\",\n    \"@types/node\": \"*\",\n    \"typedoc\": \"*\",\n    \"typedoc-plugin-markdown\": \"*\",\n    \"typescript\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-overlap/packages/workspace-a/tsconfig.json",
    "content": "{\n  \"extends\": \"@tsconfig/node16\",\n  \"compilerOptions\": {}\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-plugin-overlap/packages/workspace-a/typedoc.json",
    "content": "{\n  \"$schema\": \"https://typedoc.org/schema.json\",\n  \"plugin\": [\"typedoc-plugin-markdown\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-pnpm/apps/app-a/index.ts",
    "content": "import defaultA, { usedExportFromLibA } from '@fixtures/workspaces-pnpm__lib-a';\nimport defaultB, { usedExportFromLibB } from '@fixtures/workspaces-pnpm__lib-b';\nimport { c } from 'unlisted';\n\ndefaultA;\ndefaultB;\nusedExportFromLibA;\nusedExportFromLibB;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-pnpm/apps/app-a/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-pnpm__app-a\",\n  \"main\": \"./index.js\",\n  \"dependencies\": {\n    \"@fixtures/workspaces-pnpm__lib-a\": \"*\",\n    \"@fixtures/workspaces-pnpm__lib-b\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-pnpm/apps/app-b/index.ts",
    "content": "import a from '@fixtures/workspaces-pnpm__lib-a';\nimport b from '@fixtures/workspaces-pnpm__lib-b';\n\na;\nb;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-pnpm/apps/app-b/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-pnpm__app-b\",\n  \"main\": \"./index.js\",\n  \"dependencies\": {\n    \"@fixtures/workspaces-pnpm__lib-a\": \"*\",\n    \"@fixtures/workspaces-pnpm__lib-b\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-pnpm/docs/dangling.ts",
    "content": "export const docs = '';\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-pnpm/docs/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-pnpm__docs\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-pnpm/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-pnpm\",\n  \"knip\": {\n    \"workspaces\": {\n      \"{apps,packages}/*\": {\n        \"entry\": \"index.ts!\",\n        \"project\": \"**/*.ts!\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-pnpm/packages/lib-a/index.ts",
    "content": "export default 1;\n\nexport const usedExportFromLibA = 2;\n\nexport const unusedExportFromLibA = 3;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-pnpm/packages/lib-a/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-pnpm__lib-a\",\n  \"main\": \"./index.js\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-pnpm/packages/lib-b/index.ts",
    "content": "export default 1;\n\nexport const usedExportFromLibB = 2;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-pnpm/packages/lib-b/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-pnpm__lib-b\",\n  \"main\": \"./index.js\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-pnpm/pnpm-workspace.yaml",
    "content": "packages:\n  - '!docs'\n  - 'apps/*'\n  - 'packages/*'\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-root/app/index.ts",
    "content": "import { n } from 'vanilla-js';\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-root/app/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces__frontend\",\n  \"main\": \"./index.js\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-root/app/tsconfig.json",
    "content": "{\n  \"extends\": \"@fixtures/workspaces__tsconfig/tsconfig.base.json\",\n  \"compilerOptions\": {\n    \"baseUrl\": \"./\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-root/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces\",\n  \"workspaces\": [\n    \"app\"\n  ],\n  \"dependencies\": {\n    \"minimist\": \"*\",\n    \"typescript\": \"*\"\n  },\n  \"scripts\": {\n    \"build\": \"tsc\"\n  },\n  \"knip\": {\n    \"ignoreWorkspaces\": [\n      \"apps/ignored-workspace\"\n    ],\n    \"workspaces\": {\n      \".\": {\n        \"entry\": \"scripts/*.ts\",\n        \"project\": \"scripts/**/*.ts\"\n      },\n      \"app\": {\n        \"entry\": \"index.ts!\",\n        \"project\": \"**/*.ts!\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-root/scripts/index.ts",
    "content": "import {} from 'js-yaml';\n\nexport type ScriptUnUsedExport = unknown;\nexport const a = 1;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-root/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\",\n    \"paths\": {\n      \"@fixtures/workspaces__shared\": [\"./packages/shared/index.ts\"],\n      \"@fixtures/workspaces__tools\": [\"./packages/tools/index.ts\"]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-and-cross-ref/.gitignore",
    "content": "dist\nbuild\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-and-cross-ref/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-self-and-cross-ref\",\n  \"type\": \"module\",\n  \"private\": true\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-and-cross-ref/packages/app/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-self-and-cross-ref__app\",\n  \"type\": \"module\",\n  \"private\": true,\n  \"dependencies\": {\n    \"@fixtures/workspaces-self-and-cross-ref__lib\": \"workspace:*\",\n    \"@fixtures/workspaces-self-and-cross-ref__shared\": \"workspace:*\"\n  },\n  \"sideEffects\": false\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-and-cross-ref/packages/app/src/index.ts",
    "content": "import '@fixtures/workspaces-self-and-cross-ref__shared/polyfills/polyfills.client';\n\nimport { sharedFunction } from '@fixtures/workspaces-self-and-cross-ref__lib';\n\nimport { alpha } from '@fixtures/workspaces-self-and-cross-ref__lib/alpha.js';\n\nsharedFunction;\nalpha;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-and-cross-ref/packages/app/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.base.json\",\n  \"include\": [\"src\"],\n  \"references\": [{ \"path\": \"../shared\" }, { \"path\": \"../lib\" }],\n  \"compilerOptions\": {\n    \"module\": \"es2022\",\n    \"outDir\": \"build/types\",\n    \"rootDir\": \"src\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-and-cross-ref/packages/lib/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-self-and-cross-ref__lib\",\n  \"type\": \"module\",\n  \"private\": true,\n  \"exports\": {\n    \".\": \"./dist/index.js\",\n    \"./*\": \"./dist/*\"\n  },\n  \"files\": [\n    \"dist/**/*.js\"\n  ],\n  \"sideEffects\": false\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-and-cross-ref/packages/lib/src/alpha.ts",
    "content": "export function alpha() {}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-and-cross-ref/packages/lib/src/beta.ts",
    "content": "export function beta() {}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-and-cross-ref/packages/lib/src/index.ts",
    "content": "import { alpha } from '@fixtures/workspaces-self-and-cross-ref__lib/alpha.js';\nalpha;\n\nexport { beta } from './beta.js';\n\nexport const sharedFunction = () => {};\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-and-cross-ref/packages/lib/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.base.json\",\n  \"include\": [\"src\"],\n  \"compilerOptions\": {\n    \"module\": \"es2022\",\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-and-cross-ref/packages/shared/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-self-and-cross-ref__shared\",\n  \"type\": \"module\",\n  \"private\": true,\n  \"exports\": {\n    \"./*\": {\n      \"default\": \"./build/*.js\"\n    }\n  },\n  \"sideEffects\": false\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-and-cross-ref/packages/shared/src/polyfills/polyfills.client.ts",
    "content": "//\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-and-cross-ref/packages/shared/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.base.json\",\n  \"include\": [\"src\"],\n  \"compilerOptions\": {\n    \"module\": \"es2022\",\n    \"outDir\": \"build\",\n    \"rootDir\": \"src\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-and-cross-ref/pnpm-workspace.yaml",
    "content": "packages:\n  - packages/*\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-and-cross-ref/tsconfig.base.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"moduleResolution\": \"Bundler\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-and-cross-ref/tsconfig.json",
    "content": "{\n  \"extends\": \"./tsconfig.base.json\",\n  \"references\": [{ \"path\": \"packages/app\" }]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-reference/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-self-reference\",\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"knip\": {\n    \"workspaces\": {\n      \"packages/*\": {\n        \"entry\": [\n          \"entry.ts!\"\n        ],\n        \"project\": [\n          \"*.ts!\",\n          \"*.js\"\n        ]\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-reference/packages/eslint-config-x-self-reference/.gitignore",
    "content": "dist\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-reference/packages/eslint-config-x-self-reference/entry.ts",
    "content": "import index from '@fixtures/eslint-config-x-self-reference';\nimport build from '@fixtures/eslint-config-x-self-reference/build';\nimport local from '@fixtures/workspaces-self-reference__from-plugin/local';\n\nindex;\nbuild;\nlocal;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-reference/packages/eslint-config-x-self-reference/package.json",
    "content": "{\n  \"name\": \"@fixtures/eslint-config-x-self-reference\",\n  \"exports\": {\n    \".\": {\n      \"require\": \"./dist/index.cjs\",\n      \"import\": \"./dist/index.mjs\"\n    },\n    \"./build\": {\n      \"require\": \"./dist/build.cjs\",\n      \"import\": \"./dist/build.mjs\"\n    },\n    \"./module\": {\n      \"require\": \"./lib/module.js\",\n      \"import\": \"./lib/module.js\"\n    }\n  },\n  \"dependencies\": {\n    \"@fixtures/workspaces-self-reference__from-plugin\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-reference/packages/eslint-config-x-self-reference/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"allowJs\": true,\n    \"moduleResolution\": \"node16\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-reference/packages/from-plugin/.eslintrc.json",
    "content": "{\n  \"root\": true,\n  \"extends\": [\"@fixtures/x-self-reference/build\", \"./file\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-reference/packages/from-plugin/data.json",
    "content": "{\n  \"hello\": \"world\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-reference/packages/from-plugin/entry.ts",
    "content": "//\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-reference/packages/from-plugin/file.js",
    "content": "module.exports = 42;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-reference/packages/from-plugin/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-self-reference__from-plugin\",\n  \"scripts\": {\n    \"lint\": \"eslint\"\n  },\n  \"exports\": {\n    \".\": {\n      \"default\": \"./data.json\"\n    },\n    \"./local\": {\n      \"require\": \"./file.js\",\n      \"import\": \"./file.js\"\n    }\n  },\n  \"devDependencies\": {\n    \"eslint\": \"*\",\n    \"@fixtures/eslint-config-x-self-reference\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-self-reference/packages/from-plugin/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"allowJs\": true,\n    \"moduleResolution\": \"node16\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-tooling/knip.json",
    "content": "{\n  \"workspaces\": {\n    \"packages/eslint-config-custom\": {\n      \"eslint\": \"index.js\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-tooling/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-tooling\",\n  \"workspaces\": [\n    \"./packages/*\"\n  ]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-tooling/packages/backend/.eslintrc.js",
    "content": "module.exports = {\n  root: true,\n  extends: ['custom'],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-tooling/packages/backend/index.ts",
    "content": "export default 1;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-tooling/packages/backend/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-tooling__backend\",\n  \"scripts\": {\n    \"lint\": \"eslint\",\n    \"typeorm\": \"node --require @swc/register ../../node_modules/.bin/typeorm\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"*\",\n    \"eslint-config-custom\": \"*\",\n    \"typeorm\": \"*\",\n    \"@swc/register\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-tooling/packages/eslint-config-custom/index.js",
    "content": "module.exports = {\n  extends: ['eslint:recommended', 'prettier'],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-tooling/packages/eslint-config-custom/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-tooling__eslint-config-custom\",\n  \"dependencies\": {\n    \"eslint\": \"*\",\n    \"eslint-config-prettier\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-tooling/packages/frontend/.eslintrc.js",
    "content": "module.exports = {\n  root: true,\n  extends: ['custom'],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-tooling/packages/frontend/index.ts",
    "content": "export default 1;\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-tooling/packages/frontend/jest.config.js",
    "content": "module.exports = {\n  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],\n};\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-tooling/packages/frontend/jest.setup.js",
    "content": "module.exports = {};\n"
  },
  {
    "path": "packages/knip/fixtures/workspaces-tooling/packages/frontend/package.json",
    "content": "{\n  \"name\": \"@fixtures/workspaces-tooling__frontend\",\n  \"scripts\": {\n    \"lint\": \"eslint\",\n    \"test\": \"jest\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"*\",\n    \"eslint-config-custom\": \"*\",\n    \"jest\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/knip/fixtures/zero-config/exclude.ts",
    "content": "export const excludedFile = true;\n"
  },
  {
    "path": "packages/knip/fixtures/zero-config/index.ts",
    "content": "import { myExport } from './my-module.js';\n\nexport const main = myExport;\n"
  },
  {
    "path": "packages/knip/fixtures/zero-config/my-module.ts",
    "content": "import * as MyNamespace from './my-namespace.js';\n\nconst x = MyNamespace.x;\nconst y = MyNamespace.y;\n\nexport const unused = 1;\n\nexport const myExport = y(x);\n\nexport type AnyType = any;\n\nexport default myExport;\n"
  },
  {
    "path": "packages/knip/fixtures/zero-config/my-namespace.ts",
    "content": "export const x = 1;\nexport const y = (x: number) => x;\nexport const z = 3;\n\nexport interface NS {}\n"
  },
  {
    "path": "packages/knip/fixtures/zero-config/package.json",
    "content": "{\n  \"name\": \"@fixtures/zero-config\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/zero-config/tsconfig.json",
    "content": "{\n  \"include\": [\"*.ts\"],\n  \"exclude\": [\"exclude.ts\"]\n}\n"
  },
  {
    "path": "packages/knip/fixtures/מסמכים/.gitignore",
    "content": "dist\n"
  },
  {
    "path": "packages/knip/fixtures/מסמכים/package.json",
    "content": "{\n  \"name\": \"@fixtures/x-unresolved-rtl\"\n}\n"
  },
  {
    "path": "packages/knip/fixtures/מסמכים/src/index.ts",
    "content": "import bin from '../dist/cli.cjs';\nimport Icon from './icon.svg?raw';\n"
  },
  {
    "path": "packages/knip/package.json",
    "content": "{\n  \"name\": \"knip\",\n  \"version\": \"6.0.1\",\n  \"description\": \"Find and fix unused dependencies, exports and files in your TypeScript and JavaScript projects\",\n  \"keywords\": [\n    \"analysis\",\n    \"analyze\",\n    \"class\",\n    \"cli\",\n    \"dead code\",\n    \"dependencies\",\n    \"detect\",\n    \"devDependencies\",\n    \"duplicate\",\n    \"entropy\",\n    \"enum\",\n    \"export\",\n    \"files\",\n    \"find\",\n    \"javascript\",\n    \"lint\",\n    \"maintenance\",\n    \"members\",\n    \"missing\",\n    \"monorepo\",\n    \"namespace\",\n    \"package\",\n    \"scan\",\n    \"types\",\n    \"typescript\",\n    \"unreferenced\",\n    \"unresolved\",\n    \"unused\",\n    \"workspace\"\n  ],\n  \"homepage\": \"https://knip.dev\",\n  \"bugs\": \"https://github.com/webpro-nl/knip/issues\",\n  \"license\": \"ISC\",\n  \"author\": {\n    \"name\": \"Lars Kappert\",\n    \"email\": \"lars@webpro.nl\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/webpro-nl/knip.git\",\n    \"directory\": \"packages/knip\"\n  },\n  \"funding\": [\n    {\n      \"type\": \"github\",\n      \"url\": \"https://github.com/sponsors/webpro\"\n    },\n    {\n      \"type\": \"opencollective\",\n      \"url\": \"https://opencollective.com/knip\"\n    }\n  ],\n  \"bin\": {\n    \"knip\": \"bin/knip.js\",\n    \"knip-bun\": \"bin/knip-bun.js\"\n  },\n  \"files\": [\n    \"dist\",\n    \"schema.json\",\n    \"schema-jsonc.json\"\n  ],\n  \"type\": \"module\",\n  \"types\": \"./dist/types.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./dist/types.d.ts\",\n      \"default\": \"./dist/index.js\"\n    },\n    \"./session\": {\n      \"types\": \"./dist/session/index.d.ts\",\n      \"default\": \"./dist/session/index.js\"\n    }\n  },\n  \"scripts\": {\n    \"knip\": \"node ./src/cli.ts --directory ../..\",\n    \"knip:production\": \"node ./src/cli.ts --directory ../.. --production --strict\",\n    \"lint\": \"oxlint\",\n    \"format\": \"oxfmt -c ../../.oxfmtrc.json --ignore-path ../../.prettierignore\",\n    \"test\": \"node scripts/run-test.ts\",\n    \"test:node\": \"tsx --test test/*.test.ts test/**/*.test.ts\",\n    \"test:bun\": \"bun test test/*.test.ts test/**/*.test.ts\",\n    \"test:smoke\": \"glob-bin -c \\\"tsx --test\\\" \\\"test/*.test.ts\\\" && glob-bin -c \\\"tsx --test\\\" \\\"test/{plugins,util}/*.test.ts\\\"\",\n    \"test:bun:smoke\": \"bun test test/*.test.ts test/{plugins,util}/*.test.ts\",\n    \"watch\": \"npm link && tsc --watch\",\n    \"prebuild\": \"pnpm run generate-plugin-defs && node rmdir.js dist\",\n    \"build\": \"tsc\",\n    \"qa\": \"pnpm lint && pnpm build && pnpm knip && pnpm knip:production && pnpm run test\",\n    \"release\": \"release-it\",\n    \"create-plugin\": \"node ./scripts/create-new-plugin.ts\",\n    \"postcreate-plugin\": \"pnpm run build && (oxfmt schema.json schema-jsonc.json src/schema/plugins.ts || true)\",\n    \"generate-plugin-defs\": \"node ./scripts/generate-plugin-defs.js && (oxfmt src/plugins/index.ts src/types/PluginNames.ts src/schema/plugins.ts || true)\"\n  },\n  \"dependencies\": {\n    \"@nodelib/fs.walk\": \"^1.2.3\",\n    \"fast-glob\": \"^3.3.3\",\n    \"formatly\": \"^0.3.0\",\n    \"get-tsconfig\": \"4.13.6\",\n    \"jiti\": \"^2.6.0\",\n    \"minimist\": \"^1.2.8\",\n    \"oxc-parser\": \"^0.120.0\",\n    \"oxc-resolver\": \"^11.19.1\",\n    \"picocolors\": \"^1.1.1\",\n    \"picomatch\": \"^4.0.1\",\n    \"smol-toml\": \"^1.5.2\",\n    \"strip-json-comments\": \"5.0.3\",\n    \"unbash\": \"^2.2.0\",\n    \"yaml\": \"^2.8.2\",\n    \"zod\": \"^4.1.11\"\n  },\n  \"devDependencies\": {\n    \"@jest/types\": \"^29.6.3\",\n    \"@types/bun\": \"^1.3.3\",\n    \"@types/minimist\": \"^1.2.5\",\n    \"@types/picomatch\": \"^4.0.1\",\n    \"@types/webpack\": \"^5.28.5\",\n    \"@wdio/types\": \"^9.20.0\",\n    \"codeclimate-types\": \"^0.3.1\",\n    \"glob-bin\": \"^1.0.0\",\n    \"prettier\": \"^3.8.1\",\n    \"tsx\": \"^4.21.0\",\n    \"typescript\": \"^5.8.3\"\n  },\n  \"engines\": {\n    \"node\": \"^20.19.0 || >=22.12.0\"\n  },\n  \"engineStrict\": true\n}\n"
  },
  {
    "path": "packages/knip/rmdir.js",
    "content": "#!/usr/bin/env node\nimport { rmSync } from 'node:fs';\n\nrmSync(process.argv[2], { force: true, recursive: true });\n"
  },
  {
    "path": "packages/knip/schema-jsonc.json",
    "content": "{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Knip configuration for JSONC\",\n  \"description\": \"See https://knip.dev/reference/configuration\",\n  \"allOf\": [\n    {\n      \"$ref\": \"https://unpkg.com/knip@6/schema.json\"\n    }\n  ],\n  \"allowTrailingCommas\": true\n}\n"
  },
  {
    "path": "packages/knip/schema.json",
    "content": "{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Knip configuration for JSON\",\n  \"description\": \"See https://github.com/webpro-nl/knip\",\n  \"type\": \"object\",\n  \"allOf\": [\n    {\n      \"$ref\": \"#/definitions/workspace\"\n    },\n    {\n      \"$ref\": \"#/definitions/plugins\"\n    },\n    {\n      \"properties\": {\n        \"$schema\": {\n          \"type\": \"string\",\n          \"title\": \"JSON Schema\",\n          \"description\": \"Pointer to the schema against which this document should be validated.\"\n        }\n      }\n    }\n  ],\n  \"properties\": {\n    \"ignoreBinaries\": {\n      \"$ref\": \"#/definitions/ignoreBinaries\"\n    },\n    \"ignoreDependencies\": {\n      \"$ref\": \"#/definitions/ignoreDependencies\"\n    },\n    \"ignoreFiles\": {\n      \"$ref\": \"#/definitions/ignoreFiles\"\n    },\n    \"ignoreMembers\": {\n      \"$ref\": \"#/definitions/ignoreMembers\"\n    },\n    \"ignoreUnresolved\": {\n      \"$ref\": \"#/definitions/ignoreUnresolved\"\n    },\n    \"ignoreWorkspaces\": {\n      \"title\": \"Workspaces to exclude from the report\",\n      \"examples\": [\"packages/ignore-me\"],\n      \"$ref\": \"#/definitions/list\"\n    },\n    \"include\": {\n      \"title\": \"Include issue types in the report\",\n      \"examples\": [\"files\", \"dependencies\"],\n      \"$ref\": \"#/definitions/issueTypes\"\n    },\n    \"exclude\": {\n      \"title\": \"Exclude issue types from the report\",\n      \"examples\": [\"enumMembers\"],\n      \"$ref\": \"#/definitions/issueTypes\"\n    },\n    \"ignoreExportsUsedInFile\": {\n      \"title\": \"Ignore exports used in file\",\n      \"examples\": [\n        {\n          \"ignoreExportsUsedInFile\": true\n        },\n        {\n          \"ignoreExportsUsedInFile\": {\n            \"interface\": true,\n            \"type\": true\n          }\n        }\n      ],\n      \"anyOf\": [\n        {\n          \"type\": \"boolean\"\n        },\n        {\n          \"type\": \"object\",\n          \"properties\": {\n            \"class\": {\n              \"type\": \"boolean\"\n            },\n            \"enum\": {\n              \"type\": \"boolean\"\n            },\n            \"function\": {\n              \"type\": \"boolean\"\n            },\n            \"interface\": {\n              \"type\": \"boolean\"\n            },\n            \"member\": {\n              \"type\": \"boolean\"\n            },\n            \"type\": {\n              \"type\": \"boolean\"\n            },\n            \"variable\": {\n              \"type\": \"boolean\"\n            }\n          }\n        }\n      ]\n    },\n    \"ignoreIssues\": {\n      \"title\": \" Ignore specific issue types for specific file patterns\",\n      \"examples\": [\n        {\n          \"src/generated/**\": [\"exports\", \"types\"],\n          \"**/*.generated.ts\": [\"exports\"]\n        }\n      ],\n      \"type\": \"object\",\n      \"additionalProperties\": {\n        \"$ref\": \"#/definitions/issueTypes\"\n      }\n    },\n    \"includeEntryExports\": {\n      \"$ref\": \"#/definitions/includeEntryExports\"\n    },\n    \"tags\": {\n      \"title\": \"Exclude (-) or include (+) exports with the specified JSDoc/TSDoc tags\",\n      \"examples\": [\"+custom\", \"-lintignore\", \"-@internal\"],\n      \"$ref\": \"#/definitions/list\"\n    },\n    \"workspaces\": {\n      \"title\": \"Configuration for workspaces\",\n      \"type\": \"object\",\n      \"additionalProperties\": {\n        \"allOf\": [\n          {\n            \"$ref\": \"#/definitions/workspace\"\n          },\n          {\n            \"$ref\": \"#/definitions/plugins\"\n          }\n        ],\n        \"unevaluatedProperties\": false\n      }\n    },\n    \"rules\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"binaries\": {\n          \"$ref\": \"#/definitions/ruleValue\"\n        },\n        \"catalog\": {\n          \"$ref\": \"#/definitions/ruleValue\"\n        },\n        \"dependencies\": {\n          \"$ref\": \"#/definitions/ruleValue\"\n        },\n        \"devDependencies\": {\n          \"$ref\": \"#/definitions/ruleValue\"\n        },\n        \"optionalPeerDependencies\": {\n          \"$ref\": \"#/definitions/ruleValue\"\n        },\n        \"duplicates\": {\n          \"$ref\": \"#/definitions/ruleValue\"\n        },\n        \"enumMembers\": {\n          \"$ref\": \"#/definitions/ruleValue\"\n        },\n        \"namespaceMembers\": {\n          \"$ref\": \"#/definitions/ruleValue\"\n        },\n        \"exports\": {\n          \"$ref\": \"#/definitions/ruleValue\"\n        },\n        \"files\": {\n          \"$ref\": \"#/definitions/ruleValue\"\n        },\n        \"nsExports\": {\n          \"$ref\": \"#/definitions/ruleValue\"\n        },\n        \"nsTypes\": {\n          \"$ref\": \"#/definitions/ruleValue\"\n        },\n        \"types\": {\n          \"$ref\": \"#/definitions/ruleValue\"\n        },\n        \"unlisted\": {\n          \"$ref\": \"#/definitions/ruleValue\"\n        },\n        \"unresolved\": {\n          \"$ref\": \"#/definitions/ruleValue\"\n        }\n      }\n    },\n    \"treatConfigHintsAsErrors\": {\n      \"title\": \"Exit with non-zero code (1) if there are any configuration hints\",\n      \"type\": \"boolean\"\n    }\n  },\n  \"unevaluatedProperties\": false,\n  \"definitions\": {\n    \"list\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\"\n      }\n    },\n    \"ignoreBinaries\": {\n      \"title\": \"Binaries to exclude from the report (regex allowed)\",\n      \"examples\": [\"rm\", \"docker-compose\", \"curl\"],\n      \"$ref\": \"#/definitions/list\"\n    },\n    \"ignoreDependencies\": {\n      \"title\": \"Dependencies to exclude from the report (regex allowed)\",\n      \"examples\": [\"husky\", \"lint-staged\"],\n      \"$ref\": \"#/definitions/list\"\n    },\n    \"ignoreFiles\": {\n      \"title\": \"Unused files to exclude from the report\",\n      \"examples\": [\"husky\", \"lint-staged\", \"**/fixtures/**\", \"mocks/**\"],\n      \"default\": [],\n      \"$ref\": \"#/definitions/globPatterns\"\n    },\n    \"ignoreUnresolved\": {\n      \"title\": \"Unresolved imports to exclude from the report (regex allowed)\",\n      \"examples\": [\"#/virtual\"],\n      \"$ref\": \"#/definitions/list\"\n    },\n    \"includeEntryExports\": {\n      \"title\": \"Include entry files when reporting unused exports\",\n      \"type\": \"boolean\"\n    },\n    \"ignoreMembers\": {\n      \"title\": \"Enum and namespace members to exclude from the report (regex allowed)\",\n      \"examples\": [\"render\", \"on.*\"],\n      \"$ref\": \"#/definitions/list\"\n    },\n    \"issueTypes\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\",\n        \"enum\": [\n          \"files\",\n          \"dependencies\",\n          \"devDependencies\",\n          \"optionalPeerDependencies\",\n          \"unlisted\",\n          \"binaries\",\n          \"unresolved\",\n          \"exports\",\n          \"types\",\n          \"nsExports\",\n          \"nsTypes\",\n          \"duplicates\",\n          \"enumMembers\",\n          \"namespaceMembers\",\n          \"catalog\"\n        ]\n      }\n    },\n    \"globPatterns\": {\n      \"description\": \"Use file paths and glob patterns to match files. Knip uses fast-glob and picomatch (https://github.com/micromatch/picomatch)\",\n      \"anyOf\": [\n        {\n          \"type\": \"string\"\n        },\n        {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        }\n      ]\n    },\n    \"workspace\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"entry\": {\n          \"title\": \"The entry files target the starting point(s) to resolve the rest of the imported code.\",\n          \"example\": [\"lib/index.ts\"],\n          \"default\": [\"index.{js,ts,tsx}\", \"src/index.{js,ts,tsx}\"],\n          \"$ref\": \"#/definitions/globPatterns\"\n        },\n        \"project\": {\n          \"title\": \"The project files should contain all files to match against the files resolved from the entry files, including potentially unused files.\",\n          \"example\": [\"lib/**/*.ts\"],\n          \"default\": [\"**/*.{js,ts,tsx}\"],\n          \"$ref\": \"#/definitions/globPatterns\"\n        },\n        \"paths\": {\n          \"title\": \"\",\n          \"example\": {\n            \"~\": [\".\"]\n          },\n          \"default\": {},\n          \"$ref\": \"#/definitions/paths\"\n        },\n        \"ignore\": {\n          \"title\": \"Files to exclude from the report (any issue type)\",\n          \"example\": [\"**/fixtures/**\", \"mocks/**\"],\n          \"default\": [],\n          \"$ref\": \"#/definitions/globPatterns\"\n        },\n        \"ignoreFiles\": {\n          \"$ref\": \"#/definitions/ignoreFiles\"\n        },\n        \"ignoreBinaries\": {\n          \"$ref\": \"#/definitions/ignoreBinaries\"\n        },\n        \"ignoreDependencies\": {\n          \"$ref\": \"#/definitions/ignoreDependencies\"\n        },\n        \"ignoreMembers\": {\n          \"$ref\": \"#/definitions/ignoreMembers\"\n        },\n        \"ignoreUnresolved\": {\n          \"$ref\": \"#/definitions/ignoreUnresolved\"\n        },\n        \"includeEntryExports\": {\n          \"$ref\": \"#/definitions/includeEntryExports\"\n        }\n      }\n    },\n    \"plugin\": {\n      \"description\": \"Knip plugin configuration. See https://knip.dev/explanations/plugins\",\n      \"anyOf\": [\n        {\n          \"type\": \"boolean\"\n        },\n        {\n          \"$ref\": \"#/definitions/globPatterns\"\n        },\n        {\n          \"type\": \"object\",\n          \"properties\": {\n            \"config\": {\n              \"title\": \"The custom dependency resolver of this plugin is applied to the files listed here. Also see https://knip.dev/explanations/plugins\",\n              \"examples\": [\".eslintrc.json\"],\n              \"$ref\": \"#/definitions/globPatterns\"\n            },\n            \"entry\": {\n              \"title\": \"The entry files target the starting point(s) to resolve its imported dependencies, like regular source code.\",\n              \"examples\": [\"**/*.story.ts\", \"**/*.spec.ts\"],\n              \"$ref\": \"#/definitions/globPatterns\"\n            },\n            \"project\": {\n              \"title\": \"The project files should contain all files to match against the files resolved from the entry files for this plugin, including potentially unused files.\",\n              \"$ref\": \"#/definitions/globPatterns\"\n            }\n          },\n          \"additionalProperties\": false\n        }\n      ]\n    },\n    \"plugins\": {\n      \"properties\": {\n        \"angular\": {\n          \"title\": \"angular plugin configuration (https://knip.dev/reference/plugins/angular)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"astro\": {\n          \"title\": \"astro plugin configuration (https://knip.dev/reference/plugins/astro)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"astro-db\": {\n          \"title\": \"astro-db plugin configuration (https://knip.dev/reference/plugins/astro-db)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"ava\": {\n          \"title\": \"ava plugin configuration (https://knip.dev/reference/plugins/ava)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"babel\": {\n          \"title\": \"Babel plugin configuration (https://knip.dev/reference/plugins/babel)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"biome\": {\n          \"title\": \"biome plugin configuration (https://knip.dev/reference/plugins/biome)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"bumpp\": {\n          \"title\": \"bumpp plugin configuration (https://knip.dev/reference/plugins/bumpp)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"bun\": {\n          \"title\": \"bun plugin configuration (https://knip.dev/reference/plugins/bun)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"c8\": {\n          \"title\": \"c8 plugin configuration (https://knip.dev/reference/plugins/c8)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"capacitor\": {\n          \"title\": \"Capacitor plugin configuration (https://knip.dev/reference/plugins/capacitor)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"changelogen\": {\n          \"title\": \"changelogen plugin configuration (https://knip.dev/reference/plugins/changelogen)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"changelogithub\": {\n          \"title\": \"changelogithub plugin configuration (https://knip.dev/reference/plugins/changelogithub)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"changesets\": {\n          \"title\": \"Changesets plugin configuration (https://knip.dev/reference/plugins/changesets)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"commitizen\": {\n          \"title\": \"commitizen plugin configuration (https://knip.dev/reference/plugins/commitizen)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"commitlint\": {\n          \"title\": \"commitlint plugin configuration (https://knip.dev/reference/plugins/commitlint)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"convex\": {\n          \"title\": \"convex plugin configuration (https://knip.dev/reference/plugins/convex)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"create-typescript-app\": {\n          \"title\": \"create-typescript-app plugin configuration (https://knip.dev/reference/plugins/create-typescript-app)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"cspell\": {\n          \"title\": \"cspell plugin configuration (https://knip.dev/reference/plugins/cspell)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"cucumber\": {\n          \"title\": \"cucumber plugin configuration (https://knip.dev/reference/plugins/cucumber)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"cypress\": {\n          \"title\": \"Cypress plugin configuration (https://knip.dev/reference/plugins/cypress)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"danger\": {\n          \"title\": \"danger plugin configuration (https://knip.dev/reference/plugins/danger)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"dependency-cruiser\": {\n          \"title\": \"dependency-cruiser plugin configuration (https://knip.dev/reference/plugins/dependency-cruiser)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"docusaurus\": {\n          \"title\": \"Docusaurus plugin configuration (https://knip.dev/reference/plugins/docusaurus)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"dotenv\": {\n          \"title\": \"dotenv plugin configuration (https://knip.dev/reference/plugins/dotenv)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"drizzle\": {\n          \"title\": \"Drizzle plugin configuration (https://knip.dev/reference/plugins/drizzle)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"eleventy\": {\n          \"title\": \"eleventy plugin configuration (https://knip.dev/reference/plugins/eleventy)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"eslint\": {\n          \"title\": \"ESLint plugin configuration (https://knip.dev/reference/plugins/eslint)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"execa\": {\n          \"title\": \"execa plugin configuration (https://knip.dev/reference/plugins/execa)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"expo\": {\n          \"title\": \"Expo plugin configuration (https://knip.dev/reference/plugins/expo)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"expressive-code\": {\n          \"title\": \"expressive-code plugin configuration (https://knip.dev/reference/plugins/expressive-code)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"gatsby\": {\n          \"title\": \"Gatsby plugin configuration (https://knip.dev/reference/plugins/gatsby)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"github-action\": {\n          \"title\": \"github-action plugin configuration (https://knip.dev/reference/plugins/github-action)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"github-actions\": {\n          \"title\": \"github-actions plugin configuration (https://knip.dev/reference/plugins/github-actions)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"graphql-codegen\": {\n          \"title\": \"graphql-codegen plugin configuration (https://knip.dev/reference/plugins/graphql-codegen)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"hardhat\": {\n          \"title\": \"hardhat plugin configuration (https://knip.dev/reference/plugins/hardhat)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"husky\": {\n          \"title\": \"husky plugin configuration (https://knip.dev/reference/plugins/husky)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"i18next-parser\": {\n          \"title\": \"i18next-parser plugin configuration (https://knip.dev/reference/plugins/i18next-parser)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"jest\": {\n          \"title\": \"Jest plugin configuration (https://knip.dev/reference/plugins/jest)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"karma\": {\n          \"title\": \"karma plugin configuration (https://knip.dev/reference/plugins/karma)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"knex\": {\n          \"title\": \"knex plugin configuration (https://knip.dev/reference/plugins/knex)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"ladle\": {\n          \"title\": \"ladle plugin configuration (https://knip.dev/reference/plugins/ladle)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"lefthook\": {\n          \"title\": \"lefthook plugin configuration (https://knip.dev/reference/plugins/lefthook)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"lint-staged\": {\n          \"title\": \"lint-staged plugin configuration (https://knip.dev/reference/plugins/lint-staged)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"linthtml\": {\n          \"title\": \"linthtml plugin configuration (https://knip.dev/reference/plugins/linthtml)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"lockfile-lint\": {\n          \"title\": \"lockfile-lint plugin configuration (https://knip.dev/reference/plugins/lockfile-lint)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"lost-pixel\": {\n          \"title\": \"lost-pixel plugin configuration (https://knip.dev/reference/plugins/lost-pixel)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"markdownlint\": {\n          \"title\": \"markdownlint plugin configuration (https://knip.dev/reference/plugins/markdownlint)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"mdx\": {\n          \"title\": \"mdx plugin configuration (https://knip.dev/reference/plugins/mdx)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"mdxlint\": {\n          \"title\": \"mdxlint plugin configuration (https://knip.dev/reference/plugins/mdxlint)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"metro\": {\n          \"title\": \"metro plugin configuration (https://knip.dev/reference/plugins/metro)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"mocha\": {\n          \"title\": \"Mocha plugin configuration (https://knip.dev/reference/plugins/mocha)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"moonrepo\": {\n          \"title\": \"moonrepo plugin configuration (https://knip.dev/reference/plugins/moonrepo)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"msw\": {\n          \"title\": \"Mocha plugin configuration (https://knip.dev/reference/plugins/msw)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"nano-staged\": {\n          \"title\": \"nano-staged plugin configuration (https://knip.dev/reference/plugins/nano-staged)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"nest\": {\n          \"title\": \"nest plugin configuration (https://knip.dev/reference/plugins/nest)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"netlify\": {\n          \"title\": \"Netlify plugin configuration (https://knip.dev/reference/plugins/netlify)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"next\": {\n          \"title\": \"Next.js plugin configuration (https://knip.dev/reference/plugins/main)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"next-intl\": {\n          \"title\": \"next-intl plugin configuration (https://knip.dev/reference/plugins/next-intl)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"next-mdx\": {\n          \"title\": \"next-mdx plugin configuration (https://knip.dev/reference/plugins/next-mdx)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"nitro\": {\n          \"title\": \"nitro plugin configuration (https://knip.dev/reference/plugins/nitro)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"node\": {\n          \"title\": \"node plugin configuration (https://knip.dev/reference/plugins/node)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"node-modules-inspector\": {\n          \"title\": \"node-modules-inspector plugin configuration (https://knip.dev/reference/plugins/node-modules-inspector)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"node-test-runner\": {\n          \"title\": \"node-test-runner plugin configuration (https://knip.dev/reference/plugins/node-test-runner)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"nodemon\": {\n          \"title\": \"nodemon plugin configuration (https://knip.dev/reference/plugins/nodemon)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"npm-package-json-lint\": {\n          \"title\": \"npm-package-json-lint plugin configuration (https://knip.dev/reference/plugins/npm-package-json-lint)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"nuxt\": {\n          \"title\": \"Nuxt plugin configuration (https://knip.dev/reference/plugins/nuxt)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"nx\": {\n          \"title\": \"Nx plugin configuration (https://knip.dev/reference/plugins/nx)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"nyc\": {\n          \"title\": \"nyc plugin configuration (https://knip.dev/reference/plugins/nyc)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"openapi-ts\": {\n          \"title\": \"openapi-ts plugin configuration (https://knip.dev/reference/plugins/openapi-ts)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"oclif\": {\n          \"title\": \"oclif plugin configuration (https://knip.dev/reference/plugins/oclif)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"oxlint\": {\n          \"title\": \"oxlint plugin configuration (https://knip.dev/reference/plugins/oxlint)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"oxfmt\": {\n          \"title\": \"oxfmt plugin configuration (https://knip.dev/reference/plugins/oxfmt)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"payload\": {\n          \"title\": \"payload plugin configuration (https://knip.dev/reference/plugins/payload)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"playwright\": {\n          \"title\": \"Playwright plugin configuration (https://knip.dev/reference/plugins/playwright)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"playwright-ct\": {\n          \"title\": \"Playwright for components plugin configuration (https://knip.dev/reference/plugins/playwright-ct)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"playwright-test\": {\n          \"title\": \"playwright-test plugin configuration (https://knip.dev/reference/plugins/playwright-test)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"pm2\": {\n          \"title\": \"pm2 plugin configuration (https://knip.dev/reference/plugins/pm2)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"pnpm\": {\n          \"title\": \"pnpm plugin configuration (https://knip.dev/reference/plugins/pnpm)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"postcss\": {\n          \"title\": \"PostCSS plugin configuration (https://knip.dev/reference/plugins/postcss)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"preconstruct\": {\n          \"title\": \"preconstruct plugin configuration (https://knip.dev/reference/plugins/preconstruct)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"prettier\": {\n          \"title\": \"Prettier plugin configuration (https://knip.dev/reference/plugins/prettier)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"prisma\": {\n          \"title\": \"Prisma plugin configuration (https://knip.dev/reference/plugins/prisma)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"qwik\": {\n          \"title\": \"qwik plugin configuration (https://knip.dev/reference/plugins/qwik)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"react-cosmos\": {\n          \"title\": \"react-cosmos plugin configuration (https://knip.dev/reference/plugins/react-cosmos)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"react-native\": {\n          \"title\": \"react-native plugin configuration (https://knip.dev/reference/plugins/react-native)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"react-router\": {\n          \"title\": \"react-router plugin configuration (https://knip.dev/reference/plugins/react-router)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"relay\": {\n          \"title\": \"relay plugin configuration (https://knip.dev/reference/plugins/relay)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"release-it\": {\n          \"title\": \"Release It plugin configuration (https://knip.dev/reference/plugins/release-it)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"remark\": {\n          \"title\": \"Remark plugin configuration (https://knip.dev/reference/plugins/remark)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"remix\": {\n          \"title\": \"Remix plugin configuration (https://knip.dev/reference/plugins/remix)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"rollup\": {\n          \"title\": \"Rollup plugin configuration (https://knip.dev/reference/plugins/rollup)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"rsbuild\": {\n          \"title\": \"rsbuild plugin configuration (https://knip.dev/reference/plugins/rsbuild)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"rslib\": {\n          \"title\": \"rslib plugin configuration (https://knip.dev/reference/plugins/rslib)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"rspack\": {\n          \"title\": \"rspack plugin configuration (https://knip.dev/reference/plugins/rspack)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"rstest\": {\n          \"title\": \"rstest plugin configuration (https://knip.dev/reference/plugins/rstest)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"sanity\": {\n          \"title\": \"sanity plugin configuration (https://knip.dev/reference/plugins/sanity)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"semantic-release\": {\n          \"title\": \"semantic-release plugin configuration (https://knip.dev/reference/plugins/semantic-release)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"sentry\": {\n          \"title\": \"Sentry plugin configuration (https://knip.dev/reference/plugins/sentry)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"simple-git-hooks\": {\n          \"title\": \"simple-git-hooks plugin configuration (https://knip.dev/reference/plugins/simple-git-hooks)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"size-limit\": {\n          \"title\": \"size-limit plugin configuration (https://knip.dev/reference/plugins/size-limit)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"sst\": {\n          \"title\": \"sst plugin configuration (https://knip.dev/reference/plugins/sst)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"starlight\": {\n          \"title\": \"starlight plugin configuration (https://knip.dev/reference/plugins/starlight)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"storybook\": {\n          \"title\": \"Storybook plugin configuration (https://knip.dev/reference/plugins/storybook)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"stryker\": {\n          \"title\": \"Stryker plugin configuration (https://knip.dev/reference/plugins/stryker)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"stylelint\": {\n          \"title\": \"stylelint plugin configuration (https://knip.dev/reference/plugins/stylelint)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"svelte\": {\n          \"title\": \"svelte plugin configuration (https://knip.dev/reference/plugins/svelte)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"sveltekit\": {\n          \"title\": \"sveltekit plugin configuration (https://knip.dev/reference/plugins/sveltekit)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"svgo\": {\n          \"title\": \"svgo plugin configuration (https://knip.dev/reference/plugins/svgo)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"svgr\": {\n          \"title\": \"svgr plugin configuration (https://knip.dev/reference/plugins/svgr)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"swc\": {\n          \"title\": \"swc plugin configuration (https://knip.dev/reference/plugins/swc)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"syncpack\": {\n          \"title\": \"syncpack plugin configuration (https://knip.dev/reference/plugins/syncpack)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"tailwind\": {\n          \"title\": \"tailwind plugin configuration (https://knip.dev/reference/plugins/tailwind)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"tanstack-router\": {\n          \"title\": \"tanstack-router plugin configuration (https://knip.dev/reference/plugins/tanstack-router)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"tanstack-start\": {\n          \"title\": \"tanstack-start plugin configuration (https://knip.dev/reference/plugins/tanstack-start)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"taskfile\": {\n          \"title\": \"taskfile plugin configuration (https://knip.dev/reference/plugins/taskfile)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"travis\": {\n          \"title\": \"travis plugin configuration (https://knip.dev/reference/plugins/travis)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"ts-node\": {\n          \"title\": \"ts-node plugin configuration (https://knip.dev/reference/plugins/ts-node)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"tsdown\": {\n          \"title\": \"tsdown plugin configuration (https://knip.dev/reference/plugins/tsdown)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"tsup\": {\n          \"title\": \"tsup plugin configuration (https://knip.dev/reference/plugins/tsup)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"tsx\": {\n          \"title\": \"tsx plugin configuration (https://knip.dev/reference/plugins/tsx)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"typedoc\": {\n          \"title\": \"typedoc plugin configuration (https://knip.dev/reference/plugins/typedoc)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"typescript\": {\n          \"title\": \"TypeScript plugin configuration (https://knip.dev/reference/plugins/typescript)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"unbuild\": {\n          \"title\": \"unbuild plugin configuration (https://knip.dev/reference/plugins/unbuild)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"unocss\": {\n          \"title\": \"unocss plugin configuration (https://knip.dev/reference/plugins/unocss)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"vercel-og\": {\n          \"title\": \"vercel-og plugin configuration (https://knip.dev/reference/plugins/vercel-og)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"vike\": {\n          \"title\": \"vike plugin configuration (https://knip.dev/reference/plugins/vike)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"vite\": {\n          \"title\": \"vite plugin configuration (https://knip.dev/reference/plugins/vite)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"vitepress\": {\n          \"title\": \"vitepress plugin configuration (https://knip.dev/reference/plugins/vitepress)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"vitest\": {\n          \"title\": \"vitest plugin configuration (https://knip.dev/reference/plugins/vitest)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"vue\": {\n          \"title\": \"vue plugin configuration (https://knip.dev/reference/plugins/vue)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"webdriver-io\": {\n          \"title\": \"webdriver-io plugin configuration (https://knip.dev/reference/plugins/webdriver-io)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"webpack\": {\n          \"title\": \"Webpack plugin configuration (https://knip.dev/reference/plugins/webpack)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"wireit\": {\n          \"title\": \"Wireit plugin configuration (https://knip.dev/reference/plugins/wireit)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"wrangler\": {\n          \"title\": \"wrangler plugin configuration (https://knip.dev/reference/plugins/wrangler)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"xo\": {\n          \"title\": \"xo plugin configuration (https://knip.dev/reference/plugins/xo)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"yarn\": {\n          \"title\": \"yarn plugin configuration (https://knip.dev/reference/plugins/yarn)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"yorkie\": {\n          \"title\": \"yorkie plugin configuration (https://knip.dev/reference/plugins/yorkie)\",\n          \"$ref\": \"#/definitions/plugin\"\n        },\n        \"zx\": {\n          \"title\": \"zx plugin configuration (https://knip.dev/reference/plugins/zx)\",\n          \"$ref\": \"#/definitions/plugin\"\n        }\n      }\n    },\n    \"paths\": {\n      \"type\": \"object\",\n      \"additionalProperty\": {\n        \"type\": \"array\",\n        \"items\": {\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"ruleValue\": {\n      \"type\": \"string\",\n      \"enum\": [\"error\", \"warn\", \"off\"]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/scripts/create-new-plugin.ts",
    "content": "import fs from 'node:fs/promises';\n// oxlint-disable-next-line no-restricted-imports\nimport path from 'node:path';\nimport { parseArgs } from 'node:util';\n\nconst {\n  values: { name, force = false, graceful = !force },\n} = parseArgs({\n  options: {\n    name: { type: 'string' },\n    force: { type: 'boolean' },\n    graceful: { type: 'boolean' },\n  },\n});\n\nconst errorOnExist = force ? false : !graceful;\n\nif (!name) {\n  console.error('Usage: pnpm create-plugin --name [name]');\n  process.exit(1);\n}\n\nif (!/[a-z][a-z0-9_-]+/.test(name)) {\n  console.error('Name must contain only lowercased letters, dashes and underscores (you can adjust the title later)');\n  process.exit(1);\n}\n\nconst cwd = process.cwd();\nconst pluginsDir = path.join(cwd, 'src/plugins');\nconst templateDir = path.join(pluginsDir, '_template');\nconst newPluginDir = path.join(pluginsDir, name);\nconst newPluginFile = path.join(newPluginDir, 'index.ts');\nconst schemaFilePath = path.join(cwd, 'schema.json');\nconst pluginTestsDir = path.join(cwd, 'test/plugins');\nconst pluginSchemaPath = path.join(cwd, 'src/schema/plugins.ts');\nconst pluginTestTemplateFilePath = path.join(pluginTestsDir, '_template.test.ts');\nconst pluginTestFilePath = path.join(pluginTestsDir, `${name}.test.ts`);\nconst pluginTestFixturesDir = path.join(cwd, 'fixtures/plugins');\nconst pluginTestFixtureTemplateDir = path.join(pluginTestFixturesDir, '_template');\nconst pluginTestFixturePluginDir = path.join(pluginTestFixturesDir, name);\nconst pluginTestFixtureManifest = path.join(pluginTestFixturePluginDir, 'package.json');\n\nconst relative = (to: string) => path.relative(cwd, to);\n\n// Copy plugin implementation\nawait fs.cp(templateDir, newPluginDir, {\n  recursive: true,\n  errorOnExist,\n  force,\n});\n\n// Add plugin to Zod validator\nconst validatorContent = String(await fs.readFile(pluginSchemaPath));\nconst pluginsPrefix = 'const pluginsSchema = z.object({';\nconst pluginsReplacement = `${pluginsPrefix}\\n'${name}': pluginSchema,`;\nawait fs.writeFile(pluginSchemaPath, validatorContent.replace(pluginsPrefix, pluginsReplacement));\n\n// Copy fixtures\nawait fs.cp(pluginTestFixtureTemplateDir, pluginTestFixturePluginDir, {\n  recursive: true,\n  errorOnExist,\n  force,\n});\n\n// Copy test file\nawait fs.cp(pluginTestTemplateFilePath, pluginTestFilePath, {\n  errorOnExist,\n  force,\n});\n\n// String replacements\nfor (const filePath of [newPluginFile, pluginTestFilePath, pluginTestFixtureManifest]) {\n  try {\n    const content = String(await fs.readFile(filePath));\n    await fs.writeFile(filePath, content.replaceAll('_template', name).replaceAll('__PLUGIN_NAME__', name));\n  } catch {}\n}\n\n// Add plugin to JSON Schema\nconst { default: schema } = await import(schemaFilePath);\nconst { plugins } = schema.definitions;\nconst { properties } = plugins;\n\nproperties[name] = {\n  title: `${name} plugin configuration (https://knip.dev/reference/plugins/${name})`,\n  $ref: '#/definitions/plugin',\n};\n\nplugins.properties = Object.keys(properties)\n  .sort()\n  .reduce((props, key) => ({ ...props, [key]: properties[key] }), {});\n\nawait fs.writeFile(schemaFilePath, JSON.stringify(schema, null, 2));\n\nconsole.log(`✔️  Created new plugin in ${relative(newPluginDir)}`);\nconsole.log(`✔️  Created a test file at ${relative(pluginTestFilePath)}`);\nconsole.log(`✔️  Added plugin to ${relative(schemaFilePath)}`);\nconsole.log('');\nconsole.log('Documentation: https://knip.dev/guides/writing-a-plugin');\n"
  },
  {
    "path": "packages/knip/scripts/generate-plugin-defs.js",
    "content": "import fs from 'node:fs';\nimport { EOL } from 'node:os';\n// oxlint-disable-next-line no-restricted-imports\nimport path from 'node:path';\n\nconst HEADER = '// This file is generated (no need to edit)';\nconst cc = str => str.toLowerCase().replace(/[^a-zA-Z0-9]+(.)/g, (_m, char) => char.toUpperCase());\n\nconst pluginsDir = path.resolve('src/plugins');\nconst outputFileTypes = path.resolve('src/types/PluginNames.ts');\nconst outputFilePlugins = path.resolve('src/plugins/index.ts');\nconst outputFileSchema = path.resolve('src/schema/plugins.ts');\n\nconst pluginNames = fs\n  .readdirSync(pluginsDir, { withFileTypes: true })\n  .filter(dirent => dirent.isDirectory() && !dirent.name.startsWith('_'))\n  .map(dirent => dirent.name)\n  .sort();\n\nconst typeDefinition = `export type PluginName = ${pluginNames.map(name => `'${name}'`).join(' | ')};`;\n\nconst values = `export const pluginNames = [${pluginNames.map(name => `'${name}'`).join(',')}] as const;`;\n\nfs.writeFileSync(outputFileTypes, HEADER + EOL + typeDefinition + EOL + EOL + values);\n\nconst imports = pluginNames.map(name => `import { default as ${cc(name)} } from './${name}/index.ts';`).join(EOL);\nconst pluginsObj = `export const Plugins = {${pluginNames\n  .map(name => (name === cc(name) ? `${name},` : `'${name}': ${cc(name)},`))\n  .join(EOL)} };`;\n\nfs.writeFileSync(outputFilePlugins, HEADER + EOL + imports + EOL + EOL + pluginsObj);\n\nconst pluginSchemas = pluginNames.map(name => `'${name}': pluginSchema`).join(`,${EOL}`);\nconst pluginSchema = `import { z } from 'zod/mini';\nexport const globSchema = z.union([z.string(), z.array(z.string())]);\n\nexport const pluginSchema = z.union([\n  z.boolean(),\n  globSchema,\n  z.object({\n    config: z.optional(globSchema),\n    entry: z.optional(globSchema),\n    project: z.optional(globSchema),\n  }),\n]);\n\nexport const pluginsSchema = z.object({${pluginSchemas}});`;\n\nfs.writeFileSync(outputFileSchema, HEADER + EOL + pluginSchema);\n"
  },
  {
    "path": "packages/knip/scripts/run-test.ts",
    "content": "#!/usr/bin/env node\nimport { spawnSync } from 'node:child_process';\n\nconst args = process.argv.slice(2);\nconst bun = spawnSync('bun', ['--version'], { stdio: 'ignore' });\nconst command = !bun.error && bun.status === 0 ? 'test:bun' : 'test:node';\nconst result = spawnSync('pnpm', ['run', command, ...args], { stdio: 'inherit' });\n\nprocess.exit(result.status ?? 1);\n"
  },
  {
    "path": "packages/knip/scripts/tsconfig.json",
    "content": "{\n  \"$schema\": \"https://json.schemastore.org/tsconfig\",\n  \"compilerOptions\": {\n    \"allowImportingTsExtensions\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"NodeNext\",\n    \"moduleResolution\": \"NodeNext\",\n    \"rewriteRelativeImportExtensions\": true\n  },\n  \"include\": [\"**/*.ts\"]\n}\n"
  },
  {
    "path": "packages/knip/scripts/verify-fixtures.ts",
    "content": "import { readFileSync, writeFileSync } from 'node:fs';\nimport { glob } from 'fast-glob';\nimport { dirname, join } from '../src/util/path.ts';\n\nconst FIXTURES_DIR = join(process.cwd(), 'fixtures');\n\nfunction renderTree(tree: Record<string, any>, prefix = ''): string[] {\n  const lines: string[] = [];\n  const entries = Object.entries(tree).sort(([a], [b]) => a.localeCompare(b));\n  for (const [key, value] of entries) {\n    const isLast = key === entries.at(-1)[0];\n    lines.push(\n      `${prefix}${isLast ? '└── ' : '├── '}${tree[key]._name ? `${key} (${tree[key]._name})` : key}`,\n      ...renderTree(value, prefix + (isLast ? '    ' : '│   '))\n    );\n  }\n  return lines;\n}\n\nasync function main(isFix = false) {\n  const filePaths = await glob('**/package.json', {\n    cwd: FIXTURES_DIR,\n    ignore: ['**/node_modules/**'],\n  });\n\n  const tree = {};\n  const issues: [string, string, string][] = [];\n  const fixed: string[] = [];\n\n  for (const filePath of filePaths) {\n    const absPath = join(FIXTURES_DIR, filePath);\n    const parts = dirname(filePath).split('/');\n    const expectedName =\n      parts.at(0) === 'plugins'\n        ? `@plugins/${parts.at(1)}${parts.length > 2 ? `__${parts.at(-1)}` : ''}`\n        : `@fixtures/${parts.at(0)}${parts.length > 1 ? `__${parts.at(-1)}` : ''}`;\n\n    if (parts.at(0) !== 'plugins') {\n      if (expectedName.includes('__')) tree[parts.at(0)][parts.at(-1)] = {};\n      else tree[parts.at(0)] = {};\n    }\n\n    const pkg = JSON.parse(readFileSync(absPath, 'utf8'));\n    const name = pkg.name;\n\n    if (name && !name.startsWith('x-') && !name.includes('/x-') && !name.includes('-x-') && name !== expectedName) {\n      issues.push([filePath, expectedName, name ?? '(unnamed)']);\n      if (isFix) {\n        pkg.name = expectedName;\n        writeFileSync(absPath, `${JSON.stringify(pkg, null, 2)}\\n`);\n        fixed.push(filePath);\n      }\n    }\n  }\n\n  console.log('.');\n  console.log(renderTree(tree).join('\\n'));\n\n  if (issues.length) {\n    process.exitCode = 1;\n    console.log('\\nIssues:');\n    for (const issue of issues) console.log(`${issue[0]}: Expected \"${issue[1]}\", got \"${issue[2]}\"`);\n\n    if (isFix) {\n      console.log('\\nFixed:');\n      for (const file of fixed) console.log(`- ${file}`);\n    } else console.log('\\nRun with --fix to update package names');\n  } else {\n    process.exitCode = 0;\n    console.log('\\n✅ All good');\n  }\n}\n\nmain(process.argv.includes('--fix'));\n"
  },
  {
    "path": "packages/knip/src/CacheConsultant.ts",
    "content": "import type { MainOptions } from './util/create-options.ts';\nimport { type FileDescriptor, FileEntryCache } from './util/file-entry-cache.ts';\nimport { timerify } from './util/Performance.ts';\nimport { version } from './version.ts';\n\nconst dummyFileDescriptor: FileDescriptor<any> = { key: '', changed: true, notFound: true };\n\nexport class CacheConsultant<T> {\n  private isEnabled: boolean;\n  private cache: undefined | FileEntryCache<T>;\n\n  constructor(name: string, options: MainOptions) {\n    this.isEnabled = options.isCache;\n    if (this.isEnabled) {\n      const cacheName = `${name.replace(/[^a-z0-9]/g, '-').replace(/-*$/, '')}-${options.isProduction ? '-prod' : ''}-${version}`;\n      this.cache = new FileEntryCache(cacheName, options.cacheLocation);\n      this.reconcile = timerify(this.cache.reconcile).bind(this.cache);\n      this.getFileDescriptor = timerify(this.cache.getFileDescriptor).bind(this.cache);\n    }\n  }\n  public getFileDescriptor(filePath: string): FileDescriptor<T> {\n    if (this.isEnabled && this.cache) return this.cache.getFileDescriptor(filePath);\n    return dummyFileDescriptor;\n  }\n  public reconcile() {\n    if (this.isEnabled && this.cache) this.cache.reconcile();\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/CatalogCounselor.ts",
    "content": "import { readFile } from 'node:fs/promises';\nimport { ROOT_WORKSPACE_NAME } from './constants.ts';\nimport { JsonCatalogPeeker } from './JsonCatalogPeeker.ts';\nimport type { Fixes } from './types/exports.ts';\nimport type { Issue } from './types/issues.ts';\nimport type { Catalog, Catalogs, PackageJson } from './types/package-json.ts';\nimport { extractCatalogReferences, parseCatalog } from './util/catalog.ts';\nimport type { MainOptions } from './util/create-options.ts';\nimport { extname } from './util/path.ts';\nimport { YamlCatalogPeeker } from './YamlCatalogPeeker.ts';\n\nexport type CatalogContainer = { filePath: string; catalog?: Catalog; catalogs?: Catalogs };\n\nexport class CatalogCounselor {\n  private filePath: string;\n  private entries = new Set<string>();\n  private referencedEntries = new Set<string>();\n  private fileContent?: string;\n\n  constructor(options: MainOptions) {\n    this.filePath = options.catalog.filePath;\n    this.entries = parseCatalog(options.catalog);\n  }\n\n  private addReferencedCatalogEntry(entryName: string) {\n    this.referencedEntries.add(entryName);\n  }\n\n  public addWorkspace(manifest: PackageJson) {\n    if (this.entries.size === 0) return;\n    const catalogReferences = extractCatalogReferences(manifest);\n    for (const catalogEntryName of catalogReferences) this.addReferencedCatalogEntry(catalogEntryName);\n  }\n\n  public async settleCatalogIssues(options: MainOptions) {\n    if (this.entries.size === 0) return [];\n\n    const filePath = this.filePath;\n    const workspace = ROOT_WORKSPACE_NAME;\n    const catalogIssues: Issue[] = [];\n\n    if (this.entries.size > this.referencedEntries.size) {\n      this.fileContent = await readFile(filePath, 'utf-8');\n      const isYaml = ['.yml', '.yaml'].includes(extname(filePath));\n      const Peeker = isYaml ? YamlCatalogPeeker : JsonCatalogPeeker;\n      const peeker = new Peeker(this.fileContent);\n\n      for (const entry of this.entries.keys()) {\n        if (!this.referencedEntries.has(entry)) {\n          const [parentSymbol, symbol] = entry.split(':');\n          const pos = peeker.getLocation(parentSymbol, symbol);\n          const fixes: Fixes = [];\n          if (options.isFix && isYaml && pos) fixes.push([pos.line, 0, 0]);\n          catalogIssues.push({ type: 'catalog', filePath, workspace, symbol, parentSymbol, fixes, ...pos });\n        }\n      }\n    }\n\n    return catalogIssues;\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/ConfigurationChief.ts",
    "content": "import picomatch from 'picomatch';\nimport type { SyncCompilers } from './compilers/types.ts';\nimport { DEFAULT_EXTENSIONS, ROOT_WORKSPACE_NAME } from './constants.ts';\nimport type {\n  Configuration,\n  IgnorePatterns,\n  PluginsConfiguration,\n  RawConfiguration,\n  RawPluginConfiguration,\n  WorkspaceConfiguration,\n} from './types/config.ts';\nimport type { ConfigurationHint } from './types/issues.ts';\nimport { type PluginName, pluginNames } from './types/PluginNames.ts';\nimport type { WorkspacePackage } from './types/package-json.ts';\nimport { arrayify, compact, partition } from './util/array.ts';\nimport type { MainOptions } from './util/create-options.ts';\nimport { createWorkspaceGraph, type WorkspaceGraph } from './util/create-workspace-graph.ts';\nimport { isDirectory, isFile } from './util/fs.ts';\nimport { _dirGlob, removeProductionSuffix } from './util/glob.ts';\nimport { graphSequencer } from './util/graph-sequencer.ts';\nimport mapWorkspaces from './util/map-workspaces.ts';\nimport { join, relative } from './util/path.ts';\nimport { normalizePluginConfig } from './util/plugin.ts';\nimport { toRegexOrString } from './util/regex.ts';\nimport { ELLIPSIS } from './util/string.ts';\nimport { byPathDepth } from './util/workspace.ts';\nimport { createWorkspaceFilePathFilter, type WorkspaceFilePathFilter } from './util/workspace-file-filter.ts';\nimport { selectWorkspaces } from './util/workspace-selectors.ts';\n\nconst defaultBaseFilenamePattern = '{index,cli,main}';\n\nexport const isDefaultPattern = (type: 'entry' | 'project', id: string) => {\n  if (type === 'project') return id.startsWith('**/*.{js,mjs,cjs,jsx,ts,tsx,mts,cts');\n  return (\n    id.startsWith('{index,cli,main}.{js,mjs,cjs,jsx,ts,tsx,mts,cts') ||\n    id.startsWith('src/{index,cli,main}.{js,mjs,cjs,jsx,ts,tsx,mts,cts')\n  );\n};\n\nconst getDefaultWorkspaceConfig = (extensions: string[] = []) => {\n  const exts = [...DEFAULT_EXTENSIONS, ...extensions].map(ext => ext.slice(1)).join(',');\n  return {\n    entry: [`${defaultBaseFilenamePattern}.{${exts}}!`, `src/${defaultBaseFilenamePattern}.{${exts}}!`],\n    project: [`**/*.{${exts}}!`],\n  };\n};\n\nconst isPluginName = (name: string): name is PluginName => pluginNames.includes(name as PluginName);\n\nconst defaultConfig: Configuration = {\n  ignore: [],\n  ignoreBinaries: [],\n  ignoreDependencies: [],\n  ignoreFiles: [],\n  ignoreIssues: {},\n  ignoreMembers: [],\n  ignoreUnresolved: [],\n  ignoreWorkspaces: [],\n  ignoreExportsUsedInFile: false,\n  isIncludeEntryExports: false,\n  syncCompilers: new Map(),\n  asyncCompilers: new Map(),\n  rootPluginConfigs: {},\n};\n\nexport type Workspace = {\n  name: string;\n  pkgName: string;\n  dir: string;\n  ancestors: string[];\n  config: WorkspaceConfiguration;\n  manifestPath: string;\n  manifestStr: string;\n  ignoreMembers: IgnorePatterns;\n  srcDir?: string;\n  outDir?: string;\n};\n\n/**\n * - Normalizes raw workspaces config\n * - Determines workspaces to analyze\n * - Hands out workspace and plugin configs\n */\nexport class ConfigurationChief {\n  cwd: string;\n  rawConfig?: RawConfiguration;\n  isProduction: boolean;\n  isStrict: boolean;\n  isIncludeEntryExports: boolean;\n  config: Configuration;\n  workspace: string | string[] | undefined;\n  selectedWorkspaces: Set<string> | undefined;\n  workspaceFilePathFilter: WorkspaceFilePathFilter = () => true;\n\n  workspaces: string[];\n  ignoredWorkspacePatterns: string[] = [];\n  workspacePackages = new Map<string, WorkspacePackage>();\n  workspacesByPkgName = new Map<string, Workspace>();\n  workspacesByDir = new Map<string, Workspace>();\n  additionalWorkspaceNames = new Set<string>();\n  availableWorkspaceNames: string[] = [];\n  availableWorkspacePkgNames = new Set<string>();\n  availableWorkspaceDirs: string[] = [];\n  workspaceGraph: WorkspaceGraph = new Map();\n  private workspaceByFileCache = new Map<string, Workspace | undefined>();\n\n  constructor(options: MainOptions) {\n    this.cwd = options.cwd;\n    this.isProduction = options.isProduction;\n    this.isStrict = options.isStrict;\n    this.isIncludeEntryExports = options.isIncludeEntryExports;\n    this.workspace = options.workspace;\n    this.workspaces = options.workspaces;\n    this.rawConfig = options.parsedConfig;\n    this.config = this.normalize(options.parsedConfig ?? {});\n  }\n\n  public getConfigurationHints() {\n    const hints: ConfigurationHint[] = [];\n    if (this.rawConfig) {\n      if (this.workspacePackages.size > 1) {\n        const entry = arrayify(this.rawConfig.entry);\n        if (entry.length > 0) {\n          const identifier = `[${entry[0]}${entry.length > 1 ? `, ${ELLIPSIS}` : ''}]`;\n          hints.push({ type: 'entry-top-level', identifier });\n        }\n        const project = arrayify(this.rawConfig.project);\n        if (project.length > 0) {\n          const identifier = `[${project[0]}${project.length > 1 ? `, ${ELLIPSIS}` : ''}]`;\n          hints.push({ type: 'project-top-level', identifier });\n        }\n      }\n    }\n    return hints;\n  }\n\n  private normalize(rawConfig: RawConfiguration): Configuration {\n    const ignore = arrayify(rawConfig.ignore ?? defaultConfig.ignore);\n    const ignoreFiles = arrayify(rawConfig.ignoreFiles ?? defaultConfig.ignoreFiles);\n    const ignoreBinaries = rawConfig.ignoreBinaries ?? [];\n    const ignoreDependencies = rawConfig.ignoreDependencies ?? [];\n    const ignoreMembers = rawConfig.ignoreMembers ?? [];\n    const ignoreUnresolved = rawConfig.ignoreUnresolved ?? [];\n    const ignoreExportsUsedInFile = rawConfig.ignoreExportsUsedInFile ?? false;\n    const ignoreIssues = rawConfig.ignoreIssues ?? {};\n    const ignoreWorkspaces = rawConfig.ignoreWorkspaces ?? defaultConfig.ignoreWorkspaces;\n    const isIncludeEntryExports = rawConfig.includeEntryExports ?? this.isIncludeEntryExports;\n\n    const { syncCompilers, asyncCompilers } = rawConfig;\n\n    const rootPluginConfigs: Partial<PluginsConfiguration> = {};\n\n    for (const [pluginName, pluginConfig] of Object.entries(rawConfig)) {\n      if (isPluginName(pluginName)) {\n        rootPluginConfigs[pluginName] = normalizePluginConfig(pluginConfig as RawPluginConfiguration);\n      }\n    }\n\n    return {\n      ignore,\n      ignoreFiles,\n      ignoreBinaries,\n      ignoreDependencies,\n      ignoreMembers,\n      ignoreUnresolved,\n      ignoreExportsUsedInFile,\n      ignoreIssues,\n      ignoreWorkspaces,\n      isIncludeEntryExports,\n      syncCompilers: new Map(Object.entries(syncCompilers ?? {})) as SyncCompilers,\n      asyncCompilers: new Map(Object.entries(asyncCompilers ?? {})),\n      rootPluginConfigs,\n    };\n  }\n\n  public async getWorkspaces() {\n    this.ignoredWorkspacePatterns = this.getIgnoredWorkspacePatterns();\n\n    this.additionalWorkspaceNames = await this.getAdditionalWorkspaceNames();\n    const workspaceNames = compact([...this.getListedWorkspaces(), ...this.additionalWorkspaceNames]);\n\n    const [packages, wsPkgNames] = await mapWorkspaces(this.cwd, [...workspaceNames, '.']);\n\n    this.workspacePackages = packages;\n\n    this.availableWorkspaceNames = this.getAvailableWorkspaceNames(packages.keys());\n    this.availableWorkspacePkgNames = wsPkgNames;\n    this.availableWorkspaceDirs = this.availableWorkspaceNames\n      .sort(byPathDepth)\n      .reverse()\n      .map(dir => join(this.cwd, dir));\n\n    this.workspaceGraph = createWorkspaceGraph(this.cwd, this.availableWorkspaceNames, wsPkgNames, packages);\n\n    this.selectedWorkspaces = this.getSelectedWorkspaces();\n\n    this.workspaceFilePathFilter = createWorkspaceFilePathFilter(\n      this.cwd,\n      this.selectedWorkspaces,\n      this.availableWorkspaceNames\n    );\n\n    const includedWorkspaces = this.getIncludedWorkspaces();\n\n    for (const workspace of includedWorkspaces) {\n      this.workspacesByPkgName.set(workspace.pkgName, workspace);\n      this.workspacesByDir.set(workspace.dir, workspace);\n    }\n\n    const sorted = graphSequencer(\n      this.workspaceGraph,\n      Array.from(this.workspacesByDir.keys()).filter(dir => this.workspaceGraph.has(dir))\n    );\n    const [root, rest] = partition(sorted.chunks.flat(), dir => dir === this.cwd);\n    // oxlint-disable-next-line @typescript-eslint/no-non-null-assertion\n    return [...root, ...rest.reverse()].map(dir => this.workspacesByDir.get(dir)!);\n  }\n\n  private getListedWorkspaces() {\n    return this.workspaces.map(pattern => pattern.replace(/(?<=!?)\\.\\//, ''));\n  }\n\n  private getIgnoredWorkspaces() {\n    const ignoreWorkspaces = this.config.ignoreWorkspaces;\n    if (this.isProduction) return ignoreWorkspaces.map(removeProductionSuffix);\n    return ignoreWorkspaces.filter(pattern => !pattern.endsWith('!'));\n  }\n\n  private getIgnoredWorkspacePatterns() {\n    const ignoredWorkspacesManifest = this.getListedWorkspaces()\n      .filter(name => name.startsWith('!'))\n      .map(name => name.replace(/^!/, ''));\n    return [...ignoredWorkspacesManifest, ...this.getIgnoredWorkspaces()];\n  }\n\n  private getConfiguredWorkspaceKeys() {\n    const initialWorkspaces = this.rawConfig?.workspaces\n      ? Object.keys(this.rawConfig.workspaces)\n      : [ROOT_WORKSPACE_NAME];\n    const ignoreWorkspaces = this.getIgnoredWorkspaces();\n    return initialWorkspaces.filter(workspaceName => !ignoreWorkspaces.includes(workspaceName));\n  }\n\n  private async getAdditionalWorkspaceNames() {\n    const workspaceKeys = this.getConfiguredWorkspaceKeys();\n    const patterns = workspaceKeys.filter(key => key.includes('*'));\n    const dirs = workspaceKeys.filter(key => !key.includes('*'));\n    const globbedDirs = await _dirGlob({ patterns, cwd: this.cwd });\n    return new Set(\n      [...dirs, ...globbedDirs].filter(\n        name =>\n          name !== ROOT_WORKSPACE_NAME &&\n          !this.workspacePackages.has(name) &&\n          !picomatch.isMatch(name, this.ignoredWorkspacePatterns)\n      )\n    );\n  }\n\n  private getAvailableWorkspaceNames(names: Iterable<string>) {\n    const availableWorkspaceNames = [];\n    const [ignore, patterns] = partition(this.ignoredWorkspacePatterns, pattern => pattern.startsWith('!'));\n    const ignoreSliced = ignore.map(pattern => pattern.slice(1));\n    for (const name of names) {\n      if (!picomatch.isMatch(name, patterns, { ignore: ignoreSliced })) {\n        availableWorkspaceNames.push(name);\n      }\n    }\n    return availableWorkspaceNames;\n  }\n\n  private getIncludedWorkspaces() {\n    const selectedWorkspaces = this.selectedWorkspaces;\n\n    const isAncestor = (name: string, ancestor: string) =>\n      ancestor !== name && (ancestor === ROOT_WORKSPACE_NAME || name.startsWith(`${ancestor}/`));\n\n    const getAncestors = (name: string) => this.availableWorkspaceNames.filter(a => isAncestor(name, a));\n\n    const workspaceNames = selectedWorkspaces\n      ? Array.from(selectedWorkspaces).flatMap(name => [...getAncestors(name), name])\n      : this.availableWorkspaceNames;\n\n    const ws = new Set<string>();\n\n    if (selectedWorkspaces && this.isStrict) {\n      for (const name of selectedWorkspaces) ws.add(name);\n    } else if (selectedWorkspaces) {\n      const graph = this.workspaceGraph;\n      if (graph) {\n        const seen = new Set<string>();\n        const initialWorkspaces = new Set(workspaceNames.map(name => join(this.cwd, name)));\n        const workspaceDirsWithDependents = new Set(initialWorkspaces);\n        const addDependents = (dir: string) => {\n          seen.add(dir);\n          const dirs = graph.get(dir);\n          if (!dirs || dirs.size === 0) return;\n          for (const d of dirs)\n            if (initialWorkspaces.has(d)) {\n              workspaceDirsWithDependents.add(dir);\n              break;\n            }\n          for (const dir of dirs) if (!seen.has(dir)) addDependents(dir);\n        };\n        for (const dir of this.availableWorkspaceDirs) addDependents(dir);\n        for (const dir of workspaceDirsWithDependents) ws.add(relative(this.cwd, dir));\n      }\n    } else {\n      for (const name of workspaceNames) ws.add(name);\n    }\n\n    return Array.from(ws)\n      .sort(byPathDepth)\n      .map((name): Workspace => {\n        const dir = join(this.cwd, name);\n        const pkg = this.workspacePackages.get(name);\n        const pkgName = pkg?.pkgName ?? `KNIP_ADDED_${name}`;\n        const manifestPath = pkg?.manifestPath ?? join(dir, 'package.json');\n        const manifestStr = pkg?.manifestStr ?? '';\n        const workspaceConfig = this.getWorkspaceConfig(name);\n        const ignoreMembers = workspaceConfig.ignoreMembers?.map(toRegexOrString) ?? [];\n        return {\n          name,\n          pkgName,\n          dir,\n          config: this.getConfigForWorkspace(name),\n          ancestors: getAncestors(name),\n          manifestPath,\n          manifestStr,\n          ignoreMembers,\n        };\n      });\n  }\n\n  public getManifestForWorkspace(name: string) {\n    return this.workspacePackages.get(name)?.manifest;\n  }\n\n  private getDescendentWorkspaces(name: string) {\n    const prefix = `${name}/`;\n    return this.availableWorkspaceNames.filter(\n      workspaceName => workspaceName !== name && (name === ROOT_WORKSPACE_NAME || workspaceName.startsWith(prefix))\n    );\n  }\n\n  public getIgnoredWorkspacesFor(name: string) {\n    return this.ignoredWorkspacePatterns\n      .filter(workspaceName => workspaceName !== name)\n      .filter(workspaceName => name === ROOT_WORKSPACE_NAME || workspaceName.startsWith(name));\n  }\n\n  public createIgnoredWorkspaceMatcher(name: string, dir: string) {\n    const ignoredWorkspaces = this.getIgnoredWorkspacesFor(name);\n    if (ignoredWorkspaces.length === 0) return () => false;\n    return (filePath: string) => {\n      const relativePath = filePath.startsWith(dir) ? filePath.slice(dir.length + 1) : filePath;\n      return picomatch.isMatch(relativePath, ignoredWorkspaces);\n    };\n  }\n\n  public getNegatedWorkspacePatterns(name: string) {\n    const descendentWorkspaces = this.getDescendentWorkspaces(name);\n    const matchName = new RegExp(`^${name}/`);\n    const ignoredWorkspaces = this.getIgnoredWorkspacesFor(name);\n    const endMatch = /\\/\\*{1,2}$|\\/$|$/;\n    return [...ignoredWorkspaces, ...descendentWorkspaces]\n      .map(workspaceName => workspaceName.replace(matchName, ''))\n      .map(workspaceName => `!${workspaceName.replace(endMatch, '/**')}`);\n  }\n\n  private getConfigKeyForWorkspace(workspaceName: string) {\n    return this.getConfiguredWorkspaceKeys()\n      .sort(byPathDepth)\n      .reverse()\n      .find(pattern => picomatch.isMatch(workspaceName, pattern));\n  }\n\n  private getSelectedWorkspaces() {\n    if (!this.workspace) return;\n    const workspaceSelectors = Array.isArray(this.workspace) ? this.workspace : [this.workspace];\n    return selectWorkspaces(workspaceSelectors, this.cwd, this.workspacePackages, this.availableWorkspaceNames);\n  }\n\n  public getWorkspaceConfig(workspaceName: string) {\n    const key = this.getConfigKeyForWorkspace(workspaceName);\n    const workspaces = this.rawConfig?.workspaces ?? {};\n    return (\n      (key\n        ? key === ROOT_WORKSPACE_NAME && !(ROOT_WORKSPACE_NAME in workspaces)\n          ? this.rawConfig\n          : workspaces[key]\n        : {}) ?? {}\n    );\n  }\n\n  public getIgnores(workspaceName: string) {\n    const workspaceConfig = this.getWorkspaceConfig(workspaceName);\n    const ignoreBinaries = workspaceConfig.ignoreBinaries ?? [];\n    const ignoreDependencies = workspaceConfig.ignoreDependencies ?? [];\n    const ignoreUnresolved = workspaceConfig.ignoreUnresolved ?? [];\n    if (workspaceName === ROOT_WORKSPACE_NAME) {\n      const {\n        ignoreBinaries: rootIgnoreBinaries,\n        ignoreDependencies: rootIgnoreDependencies,\n        ignoreUnresolved: rootIgnoreUnresolved,\n      } = this.rawConfig ?? {};\n      return {\n        ignoreBinaries: compact([...ignoreBinaries, ...(rootIgnoreBinaries ?? [])]),\n        ignoreDependencies: compact([...ignoreDependencies, ...(rootIgnoreDependencies ?? [])]),\n        ignoreUnresolved: compact([...ignoreUnresolved, ...(rootIgnoreUnresolved ?? [])]),\n      };\n    }\n    return { ignoreBinaries, ignoreDependencies, ignoreUnresolved };\n  }\n\n  public getConfigForWorkspace(workspaceName: string, extensions?: string[]) {\n    const baseConfig = getDefaultWorkspaceConfig(extensions);\n    const workspaceConfig = this.getWorkspaceConfig(workspaceName);\n\n    const entry = workspaceConfig.entry ? arrayify(workspaceConfig.entry) : baseConfig.entry;\n    const project = workspaceConfig.project ? arrayify(workspaceConfig.project) : baseConfig.project;\n    const paths = workspaceConfig.paths ?? {};\n    const ignore = arrayify(workspaceConfig.ignore);\n    const ignoreFiles = arrayify(workspaceConfig.ignoreFiles);\n    const isIncludeEntryExports = workspaceConfig.includeEntryExports ?? this.config.isIncludeEntryExports;\n\n    const plugins: Partial<PluginsConfiguration> = {};\n\n    for (const [pluginName, pluginConfig] of Object.entries(this.config.rootPluginConfigs)) {\n      if (typeof pluginConfig !== 'undefined') plugins[pluginName as PluginName] = pluginConfig;\n    }\n\n    for (const [pluginName, pluginConfig] of Object.entries(workspaceConfig)) {\n      if (isPluginName(pluginName)) {\n        plugins[pluginName] = normalizePluginConfig(pluginConfig as RawPluginConfiguration);\n      }\n    }\n\n    return { entry, project, paths, ignore, ignoreFiles, isIncludeEntryExports, ...plugins };\n  }\n\n  public findWorkspaceByFilePath(filePath: string) {\n    if (this.workspaceByFileCache.has(filePath)) return this.workspaceByFileCache.get(filePath);\n    const workspaceDir = this.availableWorkspaceDirs.find(workspaceDir => filePath.startsWith(`${workspaceDir}/`));\n    const workspace = workspaceDir ? this.workspacesByDir.get(workspaceDir) : undefined;\n    this.workspaceByFileCache.set(filePath, workspace);\n    return workspace;\n  }\n\n  public getUnusedIgnoredWorkspaces() {\n    const ignoredWorkspaceNames = this.config.ignoreWorkspaces.map(removeProductionSuffix);\n    const matchesWorkspace = (pattern: string) => {\n      for (const name of this.workspacePackages.keys()) if (picomatch.isMatch(name, pattern)) return true;\n      for (const name of this.additionalWorkspaceNames) if (picomatch.isMatch(name, pattern)) return true;\n      return false;\n    };\n    return ignoredWorkspaceNames\n      .filter(ignoredWorkspaceName => !matchesWorkspace(ignoredWorkspaceName))\n      .filter(ignoredWorkspaceName => {\n        const dir = join(this.cwd, ignoredWorkspaceName);\n        return !isDirectory(dir) || isFile(dir, 'package.json');\n      });\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/ConsoleStreamer.ts",
    "content": "import type { MainOptions } from './util/create-options.ts';\n\n/**\n * - Casts messages as a stream to stdout during the process\n */\nexport class ConsoleStreamer {\n  isEnabled = false;\n  isWatch = false;\n  private lines = 0;\n\n  constructor(options: MainOptions) {\n    this.isEnabled = options.isShowProgress;\n    this.isWatch = options.isWatch;\n  }\n\n  private clearLines(count: number) {\n    if (count > 0) {\n      for (let i = 0; i < count; i++) {\n        process.stdout.moveCursor(0, -1);\n        process.stdout.clearLine(1);\n      }\n    }\n    process.stdout.cursorTo(0);\n  }\n\n  private clearScreen() {\n    process.stdout.write('\\x1b[2J\\x1b[1;1f');\n  }\n\n  private update(messages: string[]) {\n    this.clear();\n    process.stdout.write(`${messages.join('\\n')}\\n`);\n    this.lines = messages.length;\n  }\n\n  cast(message: string | string[], sub?: string) {\n    if (!this.isEnabled) return;\n    if (Array.isArray(message)) this.update(message);\n    else this.update([`${message}${!sub || sub === '.' ? '' : ` (${sub})`}…`]);\n  }\n\n  clear() {\n    if (!this.isEnabled) return;\n    if (this.isWatch) this.clearScreen();\n    else this.clearLines(this.lines);\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/DependencyDeputy.ts",
    "content": "import { isBuiltin } from 'node:module';\nimport type { Workspace } from './ConfigurationChief.ts';\nimport {\n  DT_SCOPE,\n  IGNORE_DEFINITELY_TYPED,\n  IGNORED_DEPENDENCIES,\n  IGNORED_GLOBAL_BINARIES,\n  IGNORED_RUNTIME_DEPENDENCIES,\n  ROOT_WORKSPACE_NAME,\n} from './constants.ts';\nimport { getDependencyMetaData } from './manifest/index.ts';\nimport { PackagePeeker } from './PackagePeeker.ts';\nimport type { ConfigurationHint, Counters, Issue, Issues, IssueType } from './types/issues.ts';\nimport type { PackageJson } from './types/package-json.ts';\nimport type {\n  DependencyArray,\n  DependencySet,\n  HostDependencies,\n  InstalledBinaries,\n  WorkspaceManifests,\n} from './types/workspace.ts';\nimport type { MainOptions } from './util/create-options.ts';\nimport {\n  getDefinitelyTypedFor,\n  getPackageFromDefinitelyTyped,\n  getPackageNameFromModuleSpecifier,\n  isDefinitelyTyped,\n} from './util/modules.ts';\nimport { findMatch, toRegexOrString } from './util/regex.ts';\n\nconst filterIsProduction = (id: string | RegExp, isProduction: boolean): string | RegExp | never[] =>\n  typeof id === 'string' ? (isProduction || !id.endsWith('!') ? id.replace(/!$/, '') : []) : id;\n\n/**\n * - Stores manifests\n * - Stores referenced external dependencies\n * - Stores binaries and peer dependencies\n * - Settles dependency issues\n * - Provides configuration hints\n */\nexport class DependencyDeputy {\n  isProduction;\n  isStrict;\n  isReportDependencies;\n  _manifests: WorkspaceManifests = new Map();\n  referencedDependencies: Map<string, Set<string>>;\n  referencedBinaries: Map<string, Set<string>>;\n  hostDependencies: Map<string, HostDependencies>;\n  installedBinaries: Map<string, InstalledBinaries>;\n  hasTypesIncluded: Map<string, Set<string>>;\n\n  constructor({ isProduction, isStrict, isReportDependencies }: MainOptions) {\n    this.isProduction = isProduction;\n    this.isStrict = isStrict;\n    this.isReportDependencies = isReportDependencies;\n    this.referencedDependencies = new Map();\n    this.referencedBinaries = new Map();\n    this.hostDependencies = new Map();\n    this.installedBinaries = new Map();\n    this.hasTypesIncluded = new Map();\n  }\n\n  public addWorkspace({\n    name,\n    cwd,\n    dir,\n    manifestPath,\n    manifestStr,\n    manifest,\n    ignoreDependencies: id,\n    ignoreBinaries: ib,\n    ignoreUnresolved: iu,\n  }: {\n    name: string;\n    cwd: string;\n    dir: string;\n    manifestPath: string;\n    manifestStr: string;\n    manifest: PackageJson;\n    ignoreDependencies: (string | RegExp)[];\n    ignoreBinaries: (string | RegExp)[];\n    ignoreUnresolved: (string | RegExp)[];\n  }) {\n    const dependencies = Object.keys(manifest.dependencies ?? {});\n    const peerDependencies = Object.keys(manifest.peerDependencies ?? {});\n    const optionalDependencies = Object.keys(manifest.optionalDependencies ?? {});\n    const optionalPeerDependencies: Set<string> = new Set();\n    if (manifest.peerDependenciesMeta) {\n      for (const dep of peerDependencies) {\n        if (manifest.peerDependenciesMeta[dep]?.optional) optionalPeerDependencies.add(dep);\n      }\n    }\n    const requiredPeerDependencies = peerDependencies.filter(dep => !optionalPeerDependencies.has(dep));\n    const devDependencies = Object.keys(manifest.devDependencies ?? {});\n    const allDependencies = [...dependencies, ...devDependencies, ...peerDependencies, ...optionalDependencies];\n\n    const packageNames = [\n      ...dependencies,\n      ...(this.isStrict ? peerDependencies : []),\n      ...(this.isProduction ? [] : devDependencies),\n    ];\n\n    if (this.isReportDependencies) {\n      const { hostDependencies, installedBinaries, hasTypesIncluded } = getDependencyMetaData({\n        packageNames,\n        dir,\n        cwd,\n      });\n\n      this.setHostDependencies(name, hostDependencies);\n      this.setInstalledBinaries(name, installedBinaries);\n      this.setHasTypesIncluded(name, hasTypesIncluded);\n    }\n\n    const ignoreDependencies = id.flatMap(id => filterIsProduction(id, this.isProduction)).map(toRegexOrString);\n    const ignoreBinaries = ib.flatMap(ib => filterIsProduction(ib, this.isProduction)).map(toRegexOrString);\n    const ignoreUnresolved = iu.map(toRegexOrString);\n\n    this._manifests.set(name, {\n      workspaceDir: dir,\n      manifestPath,\n      manifestStr,\n      ignoreDependencies,\n      ignoreBinaries,\n      ignoreUnresolved,\n      unusedIgnoreDependencies: new Set(ignoreDependencies),\n      unusedIgnoreBinaries: new Set(ignoreBinaries),\n      unusedIgnoreUnresolved: new Set(ignoreUnresolved),\n      dependencies,\n      devDependencies,\n      peerDependencies: new Set(peerDependencies),\n      optionalPeerDependencies,\n      requiredPeerDependencies,\n      allDependencies: new Set(allDependencies),\n    });\n  }\n\n  getWorkspaceManifest(workspaceName: string) {\n    return this._manifests.get(workspaceName);\n  }\n\n  getProductionDependencies(workspaceName: string): DependencyArray {\n    const manifest = this._manifests.get(workspaceName);\n    if (!manifest) return [];\n    if (this.isStrict) return [...manifest.dependencies, ...manifest.requiredPeerDependencies];\n    return manifest.dependencies;\n  }\n\n  getDevDependencies(workspaceName: string): DependencyArray {\n    return this._manifests.get(workspaceName)?.devDependencies ?? [];\n  }\n\n  getDependencies(workspaceName: string): DependencySet {\n    const manifest = this._manifests.get(workspaceName);\n    if (!manifest) return new Set();\n    return new Set([...manifest.dependencies, ...manifest.devDependencies]);\n  }\n\n  setInstalledBinaries(workspaceName: string, installedBinaries: Map<string, Set<string>>) {\n    this.installedBinaries.set(workspaceName, installedBinaries);\n  }\n\n  getInstalledBinaries(workspaceName: string) {\n    return this.installedBinaries.get(workspaceName);\n  }\n\n  setHasTypesIncluded(workspaceName: string, hasTypesIncluded: Set<string>) {\n    this.hasTypesIncluded.set(workspaceName, hasTypesIncluded);\n  }\n\n  getHasTypesIncluded(workspaceName: string) {\n    return this.hasTypesIncluded.get(workspaceName);\n  }\n\n  addReferencedDependency(workspaceName: string, packageName: string) {\n    if (!this.referencedDependencies.has(workspaceName)) {\n      this.referencedDependencies.set(workspaceName, new Set());\n    }\n    this.referencedDependencies.get(workspaceName)?.add(packageName);\n  }\n\n  addReferencedBinary(workspaceName: string, binaryName: string) {\n    if (!this.referencedBinaries.has(workspaceName)) {\n      this.referencedBinaries.set(workspaceName, new Set());\n    }\n    this.referencedBinaries.get(workspaceName)?.add(binaryName);\n  }\n\n  setHostDependencies(workspaceName: string, hostDependencies: HostDependencies) {\n    this.hostDependencies.set(workspaceName, hostDependencies);\n  }\n\n  getHostDependenciesFor(workspaceName: string, dependency: string) {\n    return this.hostDependencies.get(workspaceName)?.get(dependency) ?? [];\n  }\n\n  getOptionalPeerDependencies(workspaceName: string): DependencySet {\n    return this._manifests.get(workspaceName)?.optionalPeerDependencies ?? new Set();\n  }\n\n  /**\n   * Returns `true` to indicate the external dependency has been handled properly. When `false`, the call-site probably\n   * wants to mark the dependency as \"unlisted\".\n   */\n  public maybeAddReferencedExternalDependency(workspace: Workspace, packageName: string, isDevOnly?: boolean): boolean {\n    if (!this.isReportDependencies) return true;\n    if (isBuiltin(packageName)) return true;\n    if (IGNORED_RUNTIME_DEPENDENCIES.has(packageName)) return true;\n\n    // Ignore self-referenced imports\n    if (packageName === workspace.pkgName) return true;\n\n    const workspaceNames = this.isStrict ? [workspace.name] : [workspace.name, ...[...workspace.ancestors].reverse()];\n    const closestWorkspaceName = workspaceNames.find(name => this.isInDependencies(name, packageName, isDevOnly));\n\n    // Prevent false positives by also marking the `@types/packageName` dependency as referenced\n    const typesPackageName = !isDefinitelyTyped(packageName) && getDefinitelyTypedFor(packageName);\n    const closestWorkspaceNameForTypes =\n      typesPackageName && workspaceNames.find(name => this.isInDependencies(name, typesPackageName, isDevOnly));\n\n    if (closestWorkspaceName || closestWorkspaceNameForTypes) {\n      if (closestWorkspaceName) this.addReferencedDependency(closestWorkspaceName, packageName);\n      if (closestWorkspaceNameForTypes && !this.hasTypesIncluded.get(closestWorkspaceNameForTypes)?.has(packageName))\n        this.addReferencedDependency(closestWorkspaceNameForTypes, typesPackageName);\n      return true;\n    }\n    this.addReferencedDependency(workspace.name, packageName);\n\n    return false;\n  }\n\n  public maybeAddReferencedBinary(workspace: Workspace, binaryName: string): Set<string> | undefined {\n    if (!this.isReportDependencies) return new Set();\n    if (IGNORED_GLOBAL_BINARIES.has(binaryName)) return new Set();\n\n    this.addReferencedBinary(workspace.name, binaryName);\n\n    const workspaceNames = this.isStrict ? [workspace.name] : [workspace.name, ...[...workspace.ancestors].reverse()];\n\n    for (const name of workspaceNames) {\n      const binaries = this.getInstalledBinaries(name);\n      if (binaries?.has(binaryName)) {\n        const dependencies = binaries.get(binaryName);\n        if (dependencies?.size) {\n          for (const dependency of dependencies) this.addReferencedDependency(name, dependency);\n          return dependencies;\n        }\n      }\n    }\n\n    return;\n  }\n\n  private isInDependencies(workspaceName: string, packageName: string, isDevOnly?: boolean) {\n    const manifest = this._manifests.get(workspaceName);\n    if (!manifest) return false;\n    if (this.isStrict && !isDevOnly) return this.getProductionDependencies(workspaceName).includes(packageName);\n    return manifest.allDependencies.has(packageName);\n  }\n\n  public settleDependencyIssues() {\n    const dependencyIssues: Issue[] = [];\n    const devDependencyIssues: Issue[] = [];\n    const optionalPeerDependencyIssues: Issue[] = [];\n\n    for (const [workspace, { manifestPath: filePath, manifestStr }] of this._manifests) {\n      const referencedDependencies = this.referencedDependencies.get(workspace);\n      const hasTypesIncluded = this.getHasTypesIncluded(workspace);\n      const peeker = new PackagePeeker(manifestStr);\n\n      // Keeping track of peer dependencies to prevent infinite loops for circularly referenced peer deps\n      const peerDepCount: Record<string, number> = {};\n\n      const isReferencedDependency = (dependency: string, isPeerDep?: boolean): boolean => {\n        // Is referenced, ignore\n        if (referencedDependencies?.has(dependency)) return true;\n\n        // Returning peer dependency, ignore\n        if (isPeerDep && peerDepCount[dependency]) return false;\n\n        const [scope, typedDependency] = dependency.split('/');\n        if (scope === DT_SCOPE) {\n          const typedPackageName = getPackageFromDefinitelyTyped(typedDependency);\n          // Ignore `@types/*` packages that don't have a related dependency (e.g. `@types/node`)\n          if (IGNORE_DEFINITELY_TYPED.has(typedPackageName)) return true;\n\n          // The `pkg` dependency already has types included, i.e. this `@types/pkg` is obsolete\n          if (hasTypesIncluded?.has(typedPackageName)) return false;\n\n          // Ignore typed dependencies that have a host dependency that's referenced\n          // Example: `next` (host) has `react-dom` and/or `@types/react-dom` (peer), peers can be ignored if host `next` is referenced\n          const hostDependencies = [\n            ...this.getHostDependenciesFor(workspace, dependency),\n            ...this.getHostDependenciesFor(workspace, typedPackageName),\n          ];\n          if (hostDependencies.length) return !!hostDependencies.find(host => isReferencedDependency(host.name, true));\n\n          if (!referencedDependencies?.has(dependency)) return false;\n\n          return referencedDependencies.has(typedPackageName);\n        }\n\n        // A dependency may not be referenced, but it may be a peer dep of another.\n        // If that host is also not referenced we'll report this dependency as unused.\n        // Except if the host has this dependency as an optional peer dep itself.\n        const hostDependencies = this.getHostDependenciesFor(workspace, dependency);\n\n        for (const { name } of hostDependencies) {\n          if (!peerDepCount[name]) peerDepCount[name] = 1;\n          else peerDepCount[name]++;\n        }\n\n        return hostDependencies.some(\n          hostDependency =>\n            (isPeerDep === false || !hostDependency.isPeerOptional) && isReferencedDependency(hostDependency.name, true)\n        );\n      };\n\n      const isNotReferencedDependency = (dependency: string): boolean => !isReferencedDependency(dependency, false);\n\n      for (const symbol of this.getProductionDependencies(workspace).filter(isNotReferencedDependency)) {\n        const position = peeker.getLocation('dependencies', symbol);\n        dependencyIssues.push({ type: 'dependencies', workspace, filePath, symbol, fixes: [], ...position });\n      }\n\n      const manifest = this._manifests.get(workspace)!;\n\n      for (const symbol of this.getDevDependencies(workspace)) {\n        if (!manifest.dependencies.includes(symbol) && !isNotReferencedDependency(symbol)) continue;\n        const position = peeker.getLocation('devDependencies', symbol);\n        devDependencyIssues.push({ type: 'devDependencies', filePath, workspace, symbol, fixes: [], ...position });\n      }\n      for (const symbol of this.getOptionalPeerDependencies(workspace)) {\n        if (!isReferencedDependency(symbol)) continue;\n        if (manifest.dependencies.includes(symbol) || manifest.devDependencies.includes(symbol)) continue;\n        const pos = peeker.getLocation('optionalPeerDependencies', symbol);\n        optionalPeerDependencyIssues.push({\n          type: 'optionalPeerDependencies',\n          filePath,\n          workspace,\n          symbol,\n          fixes: [],\n          ...pos,\n        });\n      }\n    }\n\n    return { dependencyIssues, devDependencyIssues, optionalPeerDependencyIssues };\n  }\n\n  handleIgnoredDependencies(issues: Issues, counters: Counters, type: IssueType) {\n    for (const key in issues[type]) {\n      const issueSet = issues[type][key];\n      for (const issueKey in issueSet) {\n        const issue = issueSet[issueKey];\n        const packageName = getPackageNameFromModuleSpecifier(issue.symbol);\n        if (!packageName) continue;\n        if (IGNORED_DEPENDENCIES.has(packageName)) {\n          // Don't ignore a devDependency that duplicates a production dependency\n          if (type === 'devDependencies') {\n            const manifest = this.getWorkspaceManifest(issue.workspace);\n            if (manifest?.dependencies.includes(packageName)) continue;\n          }\n          delete issueSet[issueKey];\n          counters[type]--;\n        } else {\n          const manifest = this.getWorkspaceManifest(issue.workspace);\n          if (manifest) {\n            const ignoreItem = findMatch(manifest.ignoreDependencies, packageName);\n            if (ignoreItem) {\n              delete issueSet[issueKey];\n              counters[type]--;\n              manifest.unusedIgnoreDependencies.delete(ignoreItem);\n            } else if (issue.workspace !== ROOT_WORKSPACE_NAME) {\n              const manifest = this.getWorkspaceManifest(ROOT_WORKSPACE_NAME);\n              if (manifest) {\n                const ignoreItem = findMatch(manifest.ignoreDependencies, packageName);\n                if (ignoreItem) {\n                  delete issueSet[issueKey];\n                  counters[type]--;\n                  manifest.unusedIgnoreDependencies.delete(ignoreItem);\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n\n  handleIgnoredBinaries(issues: Issues, counters: Counters, type: IssueType) {\n    for (const key in issues[type]) {\n      const issueSet = issues[type][key];\n      for (const issueKey in issueSet) {\n        const issue = issueSet[issueKey];\n        if (IGNORED_GLOBAL_BINARIES.has(issue.symbol)) {\n          delete issueSet[issueKey];\n          counters[type]--;\n          continue;\n        }\n        const manifest = this.getWorkspaceManifest(issue.workspace);\n        if (manifest) {\n          const ignoreItem = findMatch(manifest.ignoreBinaries, issue.symbol);\n          if (ignoreItem) {\n            delete issueSet[issueKey];\n            counters[type]--;\n            manifest.unusedIgnoreBinaries.delete(ignoreItem);\n          } else {\n            const manifest = this.getWorkspaceManifest(ROOT_WORKSPACE_NAME);\n            if (manifest) {\n              const ignoreItem = findMatch(manifest.ignoreBinaries, issue.symbol);\n              if (ignoreItem) {\n                delete issueSet[issueKey];\n                counters[type]--;\n                manifest.unusedIgnoreBinaries.delete(ignoreItem);\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n\n  handleIgnoredUnresolved(issues: Issues, counters: Counters) {\n    for (const key in issues.unresolved) {\n      const issueSet = issues.unresolved[key];\n      for (const issueKey in issueSet) {\n        const issue = issueSet[issueKey];\n        const manifest = this.getWorkspaceManifest(issue.workspace);\n        if (manifest) {\n          const ignoreItem = findMatch(manifest.ignoreUnresolved, issue.symbol);\n          if (ignoreItem) {\n            delete issueSet[issueKey];\n            counters.unresolved--;\n            manifest.unusedIgnoreUnresolved.delete(ignoreItem);\n          } else {\n            const manifest = this.getWorkspaceManifest(ROOT_WORKSPACE_NAME);\n            if (manifest) {\n              const ignoreItem = findMatch(manifest.ignoreUnresolved, issue.symbol);\n              if (ignoreItem) {\n                delete issueSet[issueKey];\n                counters.unresolved--;\n                manifest.unusedIgnoreUnresolved.delete(ignoreItem);\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n\n  public removeIgnoredIssues({ issues, counters }: { issues: Issues; counters: Counters }) {\n    this.handleIgnoredDependencies(issues, counters, 'dependencies');\n    this.handleIgnoredDependencies(issues, counters, 'devDependencies');\n    this.handleIgnoredDependencies(issues, counters, 'optionalPeerDependencies');\n    this.handleIgnoredDependencies(issues, counters, 'unlisted');\n    this.handleIgnoredDependencies(issues, counters, 'unresolved');\n    this.handleIgnoredBinaries(issues, counters, 'binaries');\n    this.handleIgnoredUnresolved(issues, counters);\n  }\n\n  public getConfigurationHints() {\n    const configurationHints: ConfigurationHint[] = [];\n\n    for (const [workspaceName, manifest] of this._manifests) {\n      for (const identifier of manifest.unusedIgnoreDependencies) {\n        configurationHints.push({ workspaceName, identifier, type: 'ignoreDependencies' });\n      }\n\n      for (const identifier of manifest.unusedIgnoreBinaries) {\n        configurationHints.push({ workspaceName, identifier, type: 'ignoreBinaries' });\n      }\n\n      for (const identifier of manifest.unusedIgnoreUnresolved) {\n        configurationHints.push({ workspaceName, identifier, type: 'ignoreUnresolved' });\n      }\n    }\n\n    return configurationHints;\n  }\n\n  public addIgnoredDependencies(workspaceName: string, identifier: string) {\n    this._manifests.get(workspaceName)?.ignoreDependencies.push(toRegexOrString(identifier));\n  }\n\n  public addIgnoredBinaries(workspaceName: string, identifier: string) {\n    this._manifests.get(workspaceName)?.ignoreBinaries.push(toRegexOrString(identifier));\n  }\n\n  public addIgnoredUnresolved(workspaceName: string, identifier: string) {\n    this._manifests.get(workspaceName)?.ignoreUnresolved.push(toRegexOrString(identifier));\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/IssueCollector.ts",
    "content": "import picomatch from 'picomatch';\nimport type { IgnoreIssues } from './types/config.ts';\nimport type { ConfigurationHint, ConfigurationHints, Issue, IssueType, Rules, TagHint } from './types/issues.ts';\nimport { partition } from './util/array.ts';\nimport type { MainOptions } from './util/create-options.ts';\nimport { initCounters, initIssues } from './util/issue-initializers.ts';\nimport { relative } from './util/path.ts';\nimport type { WorkspaceFilePathFilter } from './util/workspace-file-filter.ts';\n\nconst createMatcher = (patterns: Set<string>) => {\n  const [negated, positive] = partition(patterns, p => p[0] === '!');\n  if (positive.length === 0) {\n    if (negated.length === 0) return () => false;\n    return picomatch(negated, { dot: true });\n  }\n  return picomatch(positive, { dot: true, ignore: negated.map(p => p.slice(1)) });\n};\n\nexport type CollectorIssues = ReturnType<IssueCollector['getIssues']>;\n\ntype TrackedPattern = { hint: ConfigurationHint; isMatch: (path: string) => boolean };\n\n/**\n * - Collects issues and counts them\n * - Hands them out, to be consumed by reporters\n */\nexport class IssueCollector {\n  private cwd: string;\n  private rules: Rules;\n  private workspaceFilter: (filePath: string) => boolean;\n  private issues = initIssues();\n  private counters = initCounters();\n  private referencedFiles = new Set<string>();\n  private configurationHints: ConfigurationHints = new Map();\n  private tagHints = new Set<TagHint>();\n  private ignorePatterns = new Set<string>();\n  private ignoreFilesPatterns = new Set<string>();\n  private isMatch: (filePath: string) => boolean;\n  private isFileMatch: (filePath: string) => boolean;\n  private issueMatchers: Map<IssueType, (filePath: string) => boolean> = new Map();\n  private isTrackUnusedIgnorePatterns: boolean;\n  private unusedIgnorePatterns: Map<string, TrackedPattern> = new Map();\n  private unusedIgnoreFilesPatterns: Map<string, TrackedPattern> = new Map();\n\n  constructor(options: MainOptions) {\n    this.cwd = options.cwd;\n    this.rules = options.rules;\n    this.workspaceFilter = () => true;\n    this.isMatch = () => false;\n    this.isFileMatch = () => false;\n    this.isTrackUnusedIgnorePatterns = !options.isDisableConfigHints;\n  }\n\n  setWorkspaceFilter(workspaceFilePathFilter: WorkspaceFilePathFilter | undefined) {\n    if (workspaceFilePathFilter) this.workspaceFilter = workspaceFilePathFilter;\n  }\n\n  addIgnorePatterns(entries: { pattern: string; id: string; workspaceName?: string }[]) {\n    for (const entry of entries) {\n      this.ignorePatterns.add(entry.pattern);\n      if (!this.isTrackUnusedIgnorePatterns) continue;\n      if (entry.pattern.startsWith('!')) continue;\n      if (this.unusedIgnorePatterns.has(entry.pattern)) continue;\n      this.unusedIgnorePatterns.set(entry.pattern, {\n        hint: { type: 'ignore', identifier: entry.id, workspaceName: entry.workspaceName },\n        isMatch: picomatch(entry.pattern, { dot: true }),\n      });\n    }\n    this.isMatch = createMatcher(this.ignorePatterns);\n  }\n\n  addIgnoreFilesPatterns(entries: { pattern: string; id: string; workspaceName?: string }[]) {\n    for (const entry of entries) {\n      this.ignoreFilesPatterns.add(entry.pattern);\n      if (!this.isTrackUnusedIgnorePatterns) continue;\n      if (entry.pattern.startsWith('!')) continue;\n      if (this.unusedIgnoreFilesPatterns.has(entry.pattern)) continue;\n      this.unusedIgnoreFilesPatterns.set(entry.pattern, {\n        hint: { type: 'ignoreFiles', identifier: entry.id, workspaceName: entry.workspaceName },\n        isMatch: picomatch(entry.pattern, { dot: true }),\n      });\n    }\n    this.isFileMatch = createMatcher(this.ignoreFilesPatterns);\n  }\n\n  private markUsedPatterns(filePath: string, unused: typeof this.unusedIgnorePatterns) {\n    if (unused.size === 0) return;\n    for (const [pattern, { isMatch }] of unused) {\n      if (isMatch(filePath)) unused.delete(pattern);\n    }\n  }\n\n  setIgnoreIssues(ignoreIssues?: IgnoreIssues) {\n    if (!ignoreIssues) return;\n\n    // Pre-compile matchers for each issue type\n    const issueTypePatterns = new Map<IssueType, string[]>();\n    for (const [pattern, issueTypes] of Object.entries(ignoreIssues)) {\n      for (const issueType of issueTypes) {\n        if (!issueTypePatterns.has(issueType)) {\n          issueTypePatterns.set(issueType, []);\n        }\n        issueTypePatterns.get(issueType)?.push(pattern);\n      }\n    }\n\n    for (const [issueType, patterns] of issueTypePatterns) {\n      this.issueMatchers.set(issueType, picomatch(patterns, { dot: true }));\n    }\n  }\n\n  private shouldIgnoreIssue(filePath: string, issueType: IssueType): boolean {\n    const matcher = this.issueMatchers.get(issueType);\n    if (!matcher) return false;\n    return matcher(relative(this.cwd, filePath));\n  }\n\n  addFileCounts({ processed, unused }: { processed: number; unused: number }) {\n    this.counters.processed += processed;\n    this.counters.total += processed + unused;\n  }\n\n  addFilesIssues(filePaths: string[]) {\n    for (const filePath of filePaths) {\n      if (!this.workspaceFilter(filePath)) continue;\n      if (this.referencedFiles.has(filePath)) continue;\n      if (this.isMatch(filePath)) {\n        this.markUsedPatterns(filePath, this.unusedIgnorePatterns);\n        continue;\n      }\n      if (this.isFileMatch(filePath)) {\n        this.markUsedPatterns(filePath, this.unusedIgnoreFilesPatterns);\n        continue;\n      }\n      if (this.shouldIgnoreIssue(filePath, 'files')) continue;\n\n      const symbol = relative(this.cwd, filePath);\n      this.issues.files[symbol] = {\n        [symbol]: { type: 'files', filePath, symbol, workspace: '', severity: this.rules.files, fixes: [] },\n      };\n\n      this.counters.files++;\n      this.counters.processed++;\n    }\n  }\n\n  addIssue(issue: Issue) {\n    if (!this.workspaceFilter(issue.filePath)) return;\n    if (this.isMatch(issue.filePath)) {\n      this.markUsedPatterns(issue.filePath, this.unusedIgnorePatterns);\n      return;\n    }\n    if (this.shouldIgnoreIssue(issue.filePath, issue.type)) return;\n    if (this.rules[issue.type] === 'off') return;\n    const key = relative(this.cwd, issue.filePath);\n    issue.severity = this.rules[issue.type];\n    const issues = this.issues[issue.type];\n    issues[key] = issues[key] ?? {};\n    const symbol = issue.parentSymbol ? `${issue.parentSymbol}.${issue.symbol}` : issue.symbol;\n    if (!issues[key][symbol]) {\n      issues[key][symbol] = issue;\n      this.counters[issue.type]++;\n    }\n    return true;\n  }\n\n  addConfigurationHint(issue: ConfigurationHint) {\n    const key = `${issue.workspaceName}::${issue.type}::${issue.identifier}`;\n    if (!this.configurationHints.has(key)) this.configurationHints.set(key, issue);\n  }\n\n  addTagHint(issue: TagHint) {\n    this.tagHints.add(issue);\n  }\n\n  purge() {\n    const unusedFiles = new Set<string>();\n    for (const issues of Object.values(this.issues.files)) {\n      for (const issue of Object.values(issues)) unusedFiles.add(issue.filePath);\n    }\n    this.issues = initIssues();\n    this.counters = initCounters();\n    return unusedFiles;\n  }\n\n  getIssues() {\n    return {\n      issues: this.issues,\n      counters: this.counters,\n      tagHints: this.tagHints,\n      configurationHints: Array.from(this.configurationHints.values()),\n    };\n  }\n\n  getUnusedIgnorePatternHints(options: MainOptions) {\n    if (!options.isReportFiles) return [];\n    const hints: ConfigurationHint[] = [];\n    for (const p of this.unusedIgnorePatterns.values()) hints.push(p.hint);\n    for (const p of this.unusedIgnoreFilesPatterns.values()) hints.push(p.hint);\n    return hints;\n  }\n\n  // Retain issues from `handleInput` that would otherwise get lost between analysis runs (e.g. in watch mode)\n  private retainedIssues: Issue[] = [];\n  retainIssue(issue: Issue) {\n    this.retainedIssues.push(issue);\n  }\n  getRetainedIssues() {\n    return this.retainedIssues;\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/IssueFixer.ts",
    "content": "import { readFile, rm, writeFile } from 'node:fs/promises';\nimport { formatly } from 'formatly';\nimport type { Fixes } from './types/exports.ts';\nimport type { Counters, Issue, Issues, IssueType } from './types/issues.ts';\nimport { DEFAULT_CATALOG } from './util/catalog.ts';\nimport type { MainOptions } from './util/create-options.ts';\nimport { debugLogArray, debugLogObject } from './util/debug.ts';\nimport { load, save } from './util/package-json.ts';\nimport { extname, join } from './util/path.ts';\nimport { removeExport } from './util/remove-export.ts';\n\nexport const fix = async (issues: Issues, counters: Counters, options: MainOptions) => {\n  const fixer = new IssueFixer(options);\n  const touchedFiles = await fixer.fixIssues(issues);\n\n  for (const type in issues) {\n    const group = issues[type as IssueType];\n    if (group instanceof Set) continue;\n    const counterType = (type === '_files' ? 'files' : type) as IssueType;\n    for (const filePath in group)\n      for (const key in group[filePath]) if (group[filePath][key].isFixed) counters[counterType]--;\n  }\n\n  if (options.isFormat) {\n    const report = await formatly(Array.from(touchedFiles));\n    if (report.ran && report.result && (report.result.runner === 'virtual' || report.result.code === 0)) {\n      debugLogArray('*', `Formatted files using ${report.formatter.name} (${report.formatter.runner})`, touchedFiles);\n    } else {\n      debugLogObject('*', 'Formatting files failed', report);\n    }\n  }\n};\n\nclass IssueFixer {\n  options: MainOptions;\n\n  constructor(options: MainOptions) {\n    this.options = options;\n  }\n\n  public async fixIssues(issues: Issues) {\n    const touchedFiles = new Set<string>();\n    await this.removeUnusedFiles(issues);\n    for (const filePath of await this.removeUnusedExports(issues)) touchedFiles.add(filePath);\n    for (const filePath of await this.removeUnusedDependencies(issues)) touchedFiles.add(filePath);\n    for (const filePath of await this.removeUnusedCatalogEntries(issues)) touchedFiles.add(filePath);\n    return touchedFiles;\n  }\n\n  private async removeUnusedFiles(issues: Issues) {\n    if (!this.options.isFixFiles) return;\n\n    for (const issue of Object.values(issues.files).flatMap(Object.values)) {\n      await rm(issue.filePath);\n      issue.isFixed = true;\n    }\n  }\n\n  private async removeUnusedExports(issues: Issues) {\n    const touchedFiles = new Set<string>();\n\n    const types = [\n      ...(this.options.isFixUnusedTypes ? (['types', 'nsTypes', 'enumMembers', 'namespaceMembers'] as const) : []),\n      ...(this.options.isFixUnusedExports ? (['exports', 'nsExports'] as const) : []),\n    ];\n\n    if (types.length === 0) return touchedFiles;\n\n    const allFixes = new Map<string, Fixes>();\n\n    for (const type of types) {\n      for (const [filePath, issueMap] of Object.entries(issues[type])) {\n        const fixes = allFixes.get(filePath) ?? [];\n        for (const issue of Object.values(issueMap)) fixes.push(...issue.fixes);\n        allFixes.set(filePath, fixes);\n      }\n    }\n\n    for (const [filePath, fixes] of allFixes) {\n      if (fixes.length === 0) continue;\n      const absFilePath = join(this.options.cwd, filePath);\n      const sourceFileText = fixes\n        .sort((a, b) => b[0] - a[0])\n        .reduce(\n          (text, [start, end, flags]) => removeExport({ text, start, end, flags }),\n          await readFile(absFilePath, 'utf-8')\n        );\n\n      await writeFile(absFilePath, sourceFileText);\n\n      touchedFiles.add(absFilePath);\n\n      for (const type of types) {\n        const issueMap = issues[type]?.[filePath];\n        if (issueMap) for (const issue of Object.values(issueMap)) issue.isFixed = true;\n      }\n    }\n\n    return touchedFiles;\n  }\n\n  private async removeUnusedDependencies(issues: Issues) {\n    const touchedFiles = new Set<string>();\n    if (!this.options.isFixDependencies) return touchedFiles;\n\n    const filePaths = new Set([...Object.keys(issues.dependencies), ...Object.keys(issues.devDependencies)]);\n\n    for (const filePath of filePaths) {\n      const absFilePath = join(this.options.cwd, filePath);\n      const pkg = await load(absFilePath);\n\n      if (filePath in issues.dependencies) {\n        for (const dependency of Object.keys(issues.dependencies[filePath])) {\n          if (pkg.dependencies) {\n            delete pkg.dependencies[dependency];\n            issues.dependencies[filePath][dependency].isFixed = true;\n          }\n        }\n      }\n\n      if (filePath in issues.devDependencies) {\n        for (const dependency of Object.keys(issues.devDependencies[filePath])) {\n          if (pkg.devDependencies) {\n            delete pkg.devDependencies[dependency];\n            issues.devDependencies[filePath][dependency].isFixed = true;\n          }\n        }\n      }\n\n      await save(absFilePath, pkg);\n\n      touchedFiles.add(absFilePath);\n    }\n\n    return touchedFiles;\n  }\n\n  private async removeUnusedCatalogEntries(issues: Issues) {\n    const touchedFiles = new Set<string>();\n    if (!this.options.isFixCatalog) return touchedFiles;\n\n    const filePaths = new Set(Object.keys(issues.catalog));\n\n    for (const filePath of filePaths) {\n      if (['.yml', '.yaml'].includes(extname(filePath))) {\n        const absFilePath = join(this.options.cwd, filePath);\n        const fileContent = await readFile(absFilePath, 'utf-8');\n        const issuesForFile = Object.values(issues.catalog[filePath]);\n        const takeLine = (issue: Issue) => issue.fixes.map(fix => fix[0]);\n        const remove = new Set(issuesForFile.flatMap(takeLine));\n        const keep = (_: string, i: number) => !remove.has(i + 1);\n\n        await writeFile(absFilePath, fileContent.split('\\n').filter(keep).join('\\n'));\n\n        for (const issue of issuesForFile) issue.isFixed = true;\n\n        touchedFiles.add(filePath);\n      } else {\n        const absFilePath = join(this.options.cwd, filePath);\n        const pkg = await load(absFilePath);\n        const catalog = pkg.catalog || (!Array.isArray(pkg.workspaces) && pkg.workspaces?.catalog);\n        const catalogs = pkg.catalogs || (!Array.isArray(pkg.workspaces) && pkg.workspaces?.catalogs);\n\n        for (const [key, issue] of Object.entries(issues.catalog[filePath])) {\n          if (issue.parentSymbol === DEFAULT_CATALOG) {\n            if (catalog) {\n              delete catalog[issue.symbol];\n              issues.catalog[filePath][key].isFixed = true;\n            }\n          } else {\n            if (catalogs && issue.parentSymbol) {\n              delete catalogs[issue.parentSymbol][issue.symbol];\n              issues.catalog[filePath][key].isFixed = true;\n            }\n          }\n        }\n\n        await save(absFilePath, pkg);\n\n        touchedFiles.add(absFilePath);\n      }\n    }\n\n    return touchedFiles;\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/JsonCatalogPeeker.ts",
    "content": "import { DEFAULT_CATALOG } from './util/catalog.ts';\n\nexport class JsonCatalogPeeker {\n  private lines: string[] = [];\n  private sections: Record<string, { start: number; end: number }> = {};\n  private ready = false;\n  private fileContent: string;\n\n  constructor(fileContent: string) {\n    this.fileContent = fileContent;\n  }\n\n  private init() {\n    this.lines = this.fileContent.split('\\n');\n    let inCatalogs = false;\n    let catalogName: string | undefined;\n    let braceLevel = 0;\n\n    for (let i = 0; i < this.lines.length; i++) {\n      const line = this.lines[i];\n      const trimmedLine = line.trim();\n      if (catalogName) {\n        if (line.includes('{')) braceLevel++;\n        if (line.includes('}')) braceLevel--;\n        if (braceLevel === 0) {\n          this.sections[catalogName].end = i;\n          catalogName = undefined;\n        }\n      } else if (trimmedLine.startsWith('\"catalog\":')) {\n        catalogName = DEFAULT_CATALOG;\n        this.sections[catalogName] = { start: i, end: 0 };\n        braceLevel = (line.match(/{/g) ?? []).length - (line.match(/}/g) ?? []).length;\n        if (braceLevel === 0) {\n          this.sections[catalogName].end = i;\n          catalogName = undefined;\n        }\n      } else if (trimmedLine.startsWith('\"catalogs\":')) {\n        inCatalogs = true;\n      } else if (inCatalogs) {\n        if (trimmedLine.startsWith('}')) {\n          inCatalogs = false;\n          continue;\n        }\n        const match = trimmedLine.match(/\"(.*?)\"/);\n        if (match) {\n          catalogName = match[1];\n          this.sections[catalogName] = { start: i, end: 0 };\n          braceLevel = (line.match(/{/g) ?? []).length - (line.match(/}/g) ?? []).length;\n        }\n      }\n    }\n    this.ready = true;\n  }\n\n  public getLocation(parentSymbol: string, symbol: string) {\n    if (!this.ready) this.init();\n\n    const section = this.sections[parentSymbol];\n\n    if (!section) return;\n\n    for (let i = section.start + 1; i < section.end; i++) {\n      const line = this.lines[i];\n      if (line.trim().startsWith(`\"${symbol}\":`)) {\n        return { line: i + 1, col: line.indexOf(`\"${symbol}\"`) + 1 };\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/PackagePeeker.ts",
    "content": "export class PackagePeeker {\n  private lines: string[] = [];\n  private sections: Record<string, { startLine: number; startPos: number }> = {};\n  private ready = false;\n  private manifestStr: string;\n\n  constructor(manifestStr: string) {\n    this.manifestStr = manifestStr;\n  }\n\n  private init() {\n    this.lines = this.manifestStr.split('\\n');\n    let pos = 0;\n\n    for (let i = 0; i < this.lines.length; i++) {\n      const line = this.lines[i];\n      const section =\n        line.indexOf('\"dependencies\"') !== -1\n          ? 'dependencies'\n          : line.indexOf('\"devDependencies\"') !== -1\n            ? 'devDependencies'\n            : line.indexOf('\"optionalPeerDependencies\"') !== -1\n              ? 'optionalPeerDependencies'\n              : undefined;\n      if (section) this.sections[section] = { startLine: i, startPos: pos };\n      pos += line.length + 1;\n    }\n\n    this.ready = true;\n  }\n\n  getLocation(type: 'dependencies' | 'devDependencies' | 'optionalPeerDependencies', packageName: string) {\n    if (!this.ready) this.init();\n\n    const lines = this.lines;\n    const section = this.sections[type];\n\n    if (lines.length === 0 || !section) return;\n\n    let pos = section.startPos + lines[section.startLine].length + 1;\n\n    for (let i = section.startLine + 1; i < lines.length; i++) {\n      const line = lines[i];\n      if (line.includes(`\"${packageName}\"`)) {\n        const col = line.indexOf(packageName);\n        return { line: i + 1, col: col + 1, pos: pos + col };\n      }\n      pos += line.length + 1;\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/ProjectPrincipal.ts",
    "content": "import type { ParseResult, Visitor } from 'oxc-parser';\nimport { extractSpecifiers } from './typescript/follow-imports.ts';\nimport { parseFile } from './typescript/visitors/helpers.ts';\nimport { CacheConsultant } from './CacheConsultant.ts';\nimport { getCompilerExtensions } from './compilers/index.ts';\nimport type { AsyncCompilers, SyncCompilers } from './compilers/types.ts';\nimport { DEFAULT_EXTENSIONS } from './constants.ts';\nimport type {\n  GetImportsAndExportsOptions,\n  IgnoreExportsUsedInFile,\n  PluginVisitorContext,\n  PluginVisitorObject,\n} from './types/config.ts';\nimport type { FileNode, ModuleGraph } from './types/module-graph.ts';\nimport type { Paths } from './types/project.ts';\nimport { _getImportsAndExports } from './typescript/get-imports-and-exports.ts';\nimport { createBunShellVisitor } from './typescript/visitors/script-visitors.ts';\nimport { buildVisitor } from './typescript/visitors/walk.ts';\nimport { createCustomModuleResolver } from './typescript/resolve-module-names.ts';\nimport type { ResolveModule } from './typescript/visitors/helpers.ts';\nimport { SourceFileManager } from './typescript/SourceFileManager.ts';\nimport { compact } from './util/array.ts';\nimport type { MainOptions } from './util/create-options.ts';\nimport { timerify } from './util/Performance.ts';\nimport { extname, isInNodeModules, toAbsolute } from './util/path.ts';\nimport type { ToSourceFilePath } from './util/to-source-path.ts';\n\nexport class ProjectPrincipal {\n  entryPaths = new Set<string>();\n  projectPaths = new Set<string>();\n  programPaths = new Set<string>();\n  skipExportsAnalysis = new Set<string>();\n\n  pluginCtx: PluginVisitorContext = {\n    filePath: '',\n    sourceText: '',\n    addScript: () => {},\n    addImport: () => {},\n  };\n  pluginVisitorObjects: PluginVisitorObject[] = [];\n  private _visitor: Visitor | undefined;\n\n  syncCompilers: SyncCompilers = new Map();\n  asyncCompilers: AsyncCompilers = new Map();\n  private paths: Record<string, string[]> = {};\n  private extensions = new Set(DEFAULT_EXTENSIONS);\n\n  cache: CacheConsultant<FileNode>;\n  toSourceFilePath: ToSourceFilePath;\n\n  fileManager: SourceFileManager;\n  private resolveModule: ResolveModule = () => undefined;\n\n  resolvedFiles = new Set<string>();\n  deletedFiles = new Set<string>();\n\n  constructor(options: MainOptions, toSourceFilePath: ToSourceFilePath) {\n    this.cache = new CacheConsultant('root', options);\n    this.toSourceFilePath = toSourceFilePath;\n    this.pluginVisitorObjects.push(createBunShellVisitor(this.pluginCtx));\n    this.fileManager = new SourceFileManager({\n      compilers: [this.syncCompilers, this.asyncCompilers],\n    });\n  }\n\n  addCompilers(compilers: [SyncCompilers, AsyncCompilers]) {\n    for (const [ext, compiler] of compilers[0]) {\n      if (!this.syncCompilers.has(ext)) {\n        this.syncCompilers.set(ext, compiler);\n        this.extensions.add(ext);\n      }\n    }\n    for (const [ext, compiler] of compilers[1]) {\n      if (!this.asyncCompilers.has(ext)) {\n        this.asyncCompilers.set(ext, compiler);\n        this.extensions.add(ext);\n      }\n    }\n  }\n\n  addPaths(paths: Paths, basePath: string) {\n    if (!paths) return;\n    for (const key in paths) {\n      const prefixes = paths[key].map(prefix => toAbsolute(prefix, basePath));\n      if (key in this.paths) {\n        this.paths[key] = compact([...this.paths[key], ...prefixes]);\n      } else {\n        this.paths[key] = prefixes;\n      }\n    }\n  }\n\n  init() {\n    this.extensions = new Set([\n      ...DEFAULT_EXTENSIONS,\n      ...getCompilerExtensions([this.syncCompilers, this.asyncCompilers]),\n    ]);\n    const customCompilerExtensions = getCompilerExtensions([this.syncCompilers, this.asyncCompilers]);\n    const pathsOrUndefined = Object.keys(this.paths).length > 0 ? this.paths : undefined;\n    this.resolveModule = createCustomModuleResolver(\n      { paths: pathsOrUndefined },\n      customCompilerExtensions,\n      this.toSourceFilePath\n    );\n  }\n\n  readFile(filePath: string): string {\n    return this.fileManager.readFile(filePath);\n  }\n\n  private hasAcceptedExtension(filePath: string) {\n    return this.extensions.has(extname(filePath));\n  }\n\n  addEntryPath(filePath: string, options?: { skipExportsAnalysis: boolean }) {\n    if (!isInNodeModules(filePath) && this.hasAcceptedExtension(filePath)) {\n      this.entryPaths.add(filePath);\n      this.projectPaths.add(filePath);\n      if (options?.skipExportsAnalysis) this.skipExportsAnalysis.add(filePath);\n    }\n  }\n\n  addEntryPaths(filePaths: Set<string> | string[], options?: { skipExportsAnalysis: boolean }) {\n    for (const filePath of filePaths) this.addEntryPath(filePath, options);\n  }\n\n  addProgramPath(filePath: string) {\n    if (!isInNodeModules(filePath) && this.hasAcceptedExtension(filePath)) {\n      this.programPaths.add(filePath);\n    }\n  }\n\n  addProjectPath(filePath: string) {\n    if (!isInNodeModules(filePath) && this.hasAcceptedExtension(filePath)) {\n      this.projectPaths.add(filePath);\n      this.deletedFiles.delete(filePath);\n    }\n  }\n\n  removeProjectPath(filePath: string) {\n    this.entryPaths.delete(filePath);\n    this.projectPaths.delete(filePath);\n    this.invalidateFile(filePath);\n    this.deletedFiles.add(filePath);\n  }\n\n  async runAsyncCompilers() {\n    const add = timerify(this.fileManager.compileAndAddSourceFile.bind(this.fileManager));\n    const extensions = Array.from(this.asyncCompilers.keys());\n    const files = Array.from(this.projectPaths).filter(filePath => extensions.includes(extname(filePath)));\n    for (const filePath of files) {\n      await add(filePath);\n    }\n  }\n\n  walkAndAnalyze(\n    analyzeFile: (\n      filePath: string,\n      parseResult: ParseResult | undefined,\n      sourceText: string\n    ) => Iterable<string> | undefined\n  ) {\n    this.resolvedFiles.clear();\n    const queue = [...this.entryPaths, ...this.programPaths];\n    const visited = new Set<string>();\n    let lastEntrySize = this.entryPaths.size;\n    let lastProgramSize = this.programPaths.size;\n\n    for (let i = 0; i < queue.length; i++) {\n      const filePath = queue[i];\n      if (visited.has(filePath)) continue;\n      visited.add(filePath);\n\n      const sourceText = this.fileManager.readFile(filePath);\n\n      if (!sourceText) {\n        if (this.projectPaths.has(filePath)) analyzeFile(filePath, undefined, '');\n        continue;\n      }\n\n      try {\n        const result = parseFile(filePath, sourceText);\n\n        this.fileManager.sourceTextCache.delete(filePath);\n\n        if (this.projectPaths.has(filePath)) {\n          const internalPaths = analyzeFile(filePath, result, sourceText);\n          if (internalPaths) {\n            for (const p of internalPaths) {\n              if (!visited.has(p)) queue.push(p);\n            }\n          }\n        } else {\n          for (const specifier of extractSpecifiers(result, sourceText, filePath)) {\n            const resolved = this.resolveSpecifier(specifier, filePath);\n            if (resolved && !isInNodeModules(resolved) && !visited.has(resolved)) {\n              queue.push(resolved);\n            }\n          }\n        }\n\n        if (this.entryPaths.size > lastEntrySize || this.programPaths.size > lastProgramSize) {\n          for (const p of this.entryPaths) {\n            if (!visited.has(p)) queue.push(p);\n          }\n          for (const p of this.programPaths) {\n            if (!visited.has(p)) queue.push(p);\n          }\n          lastEntrySize = this.entryPaths.size;\n          lastProgramSize = this.programPaths.size;\n        }\n      } catch {\n        // Parse error — skip this file\n      }\n    }\n\n    this.resolvedFiles = visited;\n  }\n\n  getUsedResolvedFiles() {\n    this.resolvedFiles.clear();\n    const queue = [...this.entryPaths, ...this.programPaths];\n    const visited = new Set<string>();\n\n    for (let i = 0; i < queue.length; i++) {\n      const filePath = queue[i];\n      if (visited.has(filePath)) continue;\n      visited.add(filePath);\n\n      const sourceText = this.fileManager.readFile(filePath);\n      if (!sourceText) continue;\n\n      try {\n        const result = parseFile(filePath, sourceText);\n        for (const specifier of extractSpecifiers(result, sourceText, filePath)) {\n          const resolved = this.resolveSpecifier(specifier, filePath);\n          if (resolved && !isInNodeModules(resolved) && !visited.has(resolved)) {\n            queue.push(resolved);\n          }\n        }\n      } catch {\n        // Parse error — skip this file\n      }\n    }\n\n    this.resolvedFiles = visited;\n    return Array.from(this.projectPaths).filter(filePath => visited.has(filePath));\n  }\n\n  private resolveSpecifier(specifier: string, containingFile: string): string | undefined {\n    return this.resolveModule(specifier, containingFile)?.resolvedFileName;\n  }\n\n  getUnreferencedFiles() {\n    return Array.from(this.projectPaths).filter(filePath => !this.resolvedFiles.has(filePath));\n  }\n\n  analyzeSourceFile(\n    filePath: string,\n    options: GetImportsAndExportsOptions,\n    ignoreExportsUsedInFile: IgnoreExportsUsedInFile,\n    parseResult?: ParseResult,\n    sourceText?: string\n  ) {\n    const fd = this.cache.getFileDescriptor(filePath);\n    if (!fd.changed && fd.meta?.data) return fd.meta.data;\n\n    sourceText ??= this.fileManager.readFile(filePath);\n\n    const skipExports = this.skipExportsAnalysis.has(filePath);\n\n    if (options.isFixExports || options.isFixTypes) {\n      const ext = extname(filePath);\n      if (!DEFAULT_EXTENSIONS.has(ext) && (this.syncCompilers.has(ext) || this.asyncCompilers.has(ext))) {\n        options = { ...options, isFixExports: false, isFixTypes: false };\n      }\n    }\n\n    if (!this._visitor) this._visitor = buildVisitor(this.pluginVisitorObjects);\n\n    return _getImportsAndExports(\n      filePath,\n      sourceText,\n      this.resolveModule,\n      options,\n      ignoreExportsUsedInFile,\n      skipExports,\n      this._visitor,\n      this.pluginVisitorObjects.length > 0 ? this.pluginCtx : undefined,\n      parseResult\n    );\n  }\n\n  invalidateFile(filePath: string) {\n    this.fileManager.invalidate(filePath);\n  }\n\n  reconcileCache(graph: ModuleGraph) {\n    for (const [filePath, file] of graph) {\n      const fd = this.cache.getFileDescriptor(filePath);\n      if (!fd?.meta) continue;\n      fd.meta.data = { ...file, internalImportCache: undefined, importedBy: undefined };\n    }\n    this.cache.reconcile();\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/WorkspaceWorker.ts",
    "content": "import picomatch from 'picomatch';\nimport { _getInputsFromScripts } from './binaries/index.ts';\nimport { CacheConsultant } from './CacheConsultant.ts';\nimport { isDefaultPattern, type Workspace } from './ConfigurationChief.ts';\nimport { ROOT_WORKSPACE_NAME } from './constants.ts';\nimport { getFilteredScripts } from './manifest/helpers.ts';\nimport { PluginEntries, Plugins } from './plugins.ts';\nimport type {\n  EnsuredPluginConfiguration,\n  GetInputsFromScriptsPartial,\n  HandleInput,\n  Plugin,\n  RegisterCompiler,\n  RegisterVisitorsOptions,\n  WorkspaceConfiguration,\n} from './types/config.ts';\nimport type { ConfigurationHint } from './types/issues.ts';\nimport type { PluginName } from './types/PluginNames.ts';\nimport type { PackageJson } from './types/package-json.ts';\nimport type { DependencySet } from './types/workspace.ts';\nimport { collectStringLiterals, isExternalReExportsOnly } from './typescript/ast-helpers.ts';\nimport { parseFile } from './typescript/visitors/helpers.ts';\nimport { compact } from './util/array.ts';\nimport type { MainOptions } from './util/create-options.ts';\nimport { debugLogArray, debugLogObject } from './util/debug.ts';\nimport { _glob, hasNoProductionSuffix, hasProductionSuffix, negate } from './util/glob.ts';\nimport {\n  type ConfigInput,\n  type Input,\n  isConfig,\n  isDeferResolve,\n  isDependency,\n  toConfig,\n  toDebugString,\n  toEntry,\n  toProductionEntry,\n} from './util/input.ts';\nimport { getPackageNameFromSpecifier } from './util/modules.ts';\nimport { getKeysByValue } from './util/object.ts';\nimport { timerify } from './util/Performance.ts';\nimport { basename, dirname, isInternal, join } from './util/path.ts';\nimport { loadConfigForPlugin } from './util/plugin.ts';\nimport { ELLIPSIS } from './util/string.ts';\n\ntype WorkspaceManagerOptions = {\n  name: string;\n  dir: string;\n  config: WorkspaceConfiguration;\n  manifest: PackageJson;\n  dependencies: DependencySet;\n  rootManifest: PackageJson | undefined;\n  handleInput: HandleInput;\n  findWorkspaceByFilePath: (filePath: string) => Workspace | undefined;\n  readFile: (filePath: string) => string;\n  negatedWorkspacePatterns: string[];\n  ignoredWorkspacePatterns: string[];\n  enabledPluginsInAncestors: string[];\n  configFilesMap: Map<string, Map<PluginName, Set<string>>>;\n  options: MainOptions;\n};\n\ntype CacheItem = { resolveConfig?: Input[]; resolveFromAST?: Input[]; configFile?: Input };\n\nconst nullConfig: EnsuredPluginConfiguration = { config: null, entry: null, project: null };\n\nconst initEnabledPluginsMap = () =>\n  Object.keys(Plugins).reduce(\n    (enabled, pluginName) => ({ ...enabled, [pluginName]: false }),\n    {} as Record<PluginName, boolean>\n  );\n\n/**\n * - Determines enabled plugins\n * - Hands out workspace and plugin glob patterns\n * - Calls enabled plugins to find referenced dependencies\n */\nexport class WorkspaceWorker {\n  name: string;\n  dir: string;\n  config: WorkspaceConfiguration;\n  manifest: PackageJson;\n  rootManifest: PackageJson | undefined;\n  dependencies: DependencySet;\n  handleInput: HandleInput;\n  findWorkspaceByFilePath: (filePath: string) => Workspace | undefined;\n  readFile: (filePath: string) => string;\n  negatedWorkspacePatterns: string[] = [];\n  ignoredWorkspacePatterns: string[] = [];\n\n  options: MainOptions;\n\n  enabledPluginsMap = initEnabledPluginsMap();\n  enabledPlugins: PluginName[] = [];\n  enabledPluginsInAncestors: string[];\n\n  cache: CacheConsultant<CacheItem>;\n\n  configFilesMap: Map<string, Map<PluginName, Set<string>>>;\n\n  constructor({\n    name,\n    dir,\n    config,\n    manifest,\n    dependencies,\n    rootManifest,\n    negatedWorkspacePatterns,\n    ignoredWorkspacePatterns,\n    enabledPluginsInAncestors,\n    handleInput,\n    findWorkspaceByFilePath,\n    readFile,\n    configFilesMap,\n    options,\n  }: WorkspaceManagerOptions) {\n    this.name = name;\n    this.dir = dir;\n    this.config = config;\n    this.manifest = manifest;\n    this.rootManifest = rootManifest;\n    this.dependencies = dependencies;\n    this.negatedWorkspacePatterns = negatedWorkspacePatterns;\n    this.ignoredWorkspacePatterns = ignoredWorkspacePatterns;\n    this.enabledPluginsInAncestors = enabledPluginsInAncestors;\n    this.configFilesMap = configFilesMap;\n\n    this.handleInput = handleInput;\n    this.findWorkspaceByFilePath = findWorkspaceByFilePath;\n    this.readFile = readFile;\n\n    this.options = options;\n\n    this.cache = new CacheConsultant(`plugins-${name}`, options);\n\n    this.getConfigurationHints = timerify(this.getConfigurationHints.bind(this), 'worker.getConfigurationHints');\n  }\n\n  public async init() {\n    this.enabledPlugins = await this.determineEnabledPlugins();\n  }\n\n  private async determineEnabledPlugins() {\n    const manifest = this.manifest;\n\n    for (const [pluginName, plugin] of PluginEntries) {\n      if (this.config[pluginName] === false) continue;\n      if (this.options.cwd !== this.dir && plugin.isRootOnly) continue;\n      if (this.config[pluginName]) {\n        this.enabledPluginsMap[pluginName] = true;\n        continue;\n      }\n      const isEnabledInAncestor = this.enabledPluginsInAncestors.includes(pluginName);\n      if (\n        isEnabledInAncestor ||\n        (typeof plugin.isEnabled === 'function' &&\n          (await plugin.isEnabled({ cwd: this.dir, manifest, dependencies: this.dependencies, config: this.config })))\n      ) {\n        this.enabledPluginsMap[pluginName] = true;\n      }\n    }\n\n    return getKeysByValue(this.enabledPluginsMap, true);\n  }\n\n  private getConfigForPlugin(pluginName: PluginName): EnsuredPluginConfiguration {\n    const config = this.config[pluginName];\n    return typeof config === 'undefined' || typeof config === 'boolean' ? nullConfig : config;\n  }\n\n  getEntryFilePatterns() {\n    const { entry } = this.config;\n    if (entry.length === 0) return [];\n    const excludeProductionNegations = entry.filter(pattern => !(pattern.startsWith('!') && pattern.endsWith('!')));\n    return [excludeProductionNegations, this.negatedWorkspacePatterns].flat();\n  }\n\n  getProjectFilePatterns(projectFilePatterns: string[]) {\n    const { project } = this.config;\n    if (project.length === 0) return [];\n    const excludeProductionNegations = project.filter(pattern => !(pattern.startsWith('!') && pattern.endsWith('!')));\n    return [excludeProductionNegations, projectFilePatterns, this.negatedWorkspacePatterns].flat();\n  }\n\n  getPluginProjectFilePatterns(patterns: string[] = []) {\n    for (const [pluginName, plugin] of PluginEntries) {\n      const pluginConfig = this.getConfigForPlugin(pluginName);\n      if (this.enabledPluginsMap[pluginName]) {\n        const { entry, project } = pluginConfig;\n        patterns.push(...(project ?? entry ?? plugin.project ?? []));\n      }\n    }\n    return [patterns, this.negatedWorkspacePatterns].flat();\n  }\n\n  private getPluginConfig(plugin: Plugin) {\n    return typeof plugin.config === 'function' ? plugin.config({ cwd: this.dir }) : plugin.config;\n  }\n\n  getPluginConfigPatterns() {\n    const patterns: string[] = [];\n    for (const [pluginName, plugin] of PluginEntries) {\n      const pluginConfig = this.getConfigForPlugin(pluginName);\n      if (this.enabledPluginsMap[pluginName] && pluginConfig) {\n        patterns.push(...(pluginConfig.config ?? this.getPluginConfig(plugin) ?? []));\n      }\n    }\n    return patterns;\n  }\n\n  getPluginEntryFilePatterns(patterns: string[]) {\n    const negateWorkspaces = patterns.some(pattern => pattern.startsWith('**/')) ? this.negatedWorkspacePatterns : [];\n    return [patterns, negateWorkspaces, this.ignoredWorkspacePatterns.map(negate)].flat();\n  }\n\n  getProductionEntryFilePatterns(negatedTestFilePatterns: string[]) {\n    const entry = this.config.entry.filter(hasProductionSuffix);\n    if (entry.length === 0) return [];\n    const negatedEntryFiles = this.config.entry.filter(hasNoProductionSuffix).map(negate);\n    return [entry, negatedEntryFiles, negatedTestFilePatterns, this.negatedWorkspacePatterns].flat();\n  }\n\n  getProductionProjectFilePatterns(negatedTestFilePatterns: string[]) {\n    const project = this.config.project;\n    if (project.length === 0) return this.getProductionEntryFilePatterns(negatedTestFilePatterns);\n    const _project = this.config.project.map(pattern => {\n      if (!(pattern.endsWith('!') || pattern.startsWith('!'))) return negate(pattern);\n      return pattern;\n    });\n    const negatedEntryFiles = this.config.entry.filter(hasNoProductionSuffix).map(negate);\n    const negatedPluginConfigPatterns = this.getPluginConfigPatterns().map(negate);\n    const negatedPluginProjectFilePatterns = this.getPluginProjectFilePatterns().map(negate);\n\n    return [\n      _project,\n      negatedEntryFiles,\n      negatedPluginConfigPatterns,\n      negatedPluginProjectFilePatterns,\n      negatedTestFilePatterns,\n      this.negatedWorkspacePatterns,\n    ].flat();\n  }\n\n  private getConfigurationFilePatterns(pluginName: PluginName) {\n    const plugin = Plugins[pluginName];\n    const pluginConfig = this.getConfigForPlugin(pluginName);\n    return pluginConfig.config ?? this.getPluginConfig(plugin) ?? [];\n  }\n\n  public async registerCompilers(registerCompiler: RegisterCompiler) {\n    const cwd = this.dir;\n    const hasDependency = (packageName: string) => this.dependencies.has(packageName);\n    for (const [pluginName, plugin] of PluginEntries) {\n      if (!plugin.registerCompilers) continue;\n      if (this.config[pluginName] === false) continue;\n      if (this.options.cwd !== this.dir && plugin.isRootOnly) continue;\n      await plugin.registerCompilers({ cwd, hasDependency, registerCompiler });\n    }\n  }\n\n  public registerVisitors(options: RegisterVisitorsOptions) {\n    for (const pluginName of this.enabledPlugins) {\n      if (options.registeredPlugins.has(pluginName)) continue;\n      const plugin = Plugins[pluginName];\n      if (plugin.registerVisitors) {\n        options.registeredPlugins.add(pluginName);\n        plugin.registerVisitors(options);\n      }\n    }\n  }\n\n  public async runPlugins() {\n    const wsName = this.name;\n    const cwd = this.dir;\n    const rootCwd = this.options.cwd;\n    const manifest = this.manifest;\n    const containingFilePath = join(cwd, 'package.json');\n    const isProduction = this.options.isProduction;\n    const knownBinsOnly = false;\n\n    const manifestScriptNames = new Set(Object.keys(manifest.scripts ?? {}));\n    const rootManifest = this.rootManifest;\n    const baseOptions = { manifestScriptNames, rootManifest, cwd, rootCwd, containingFilePath, knownBinsOnly };\n\n    // Get dependencies from package.json#scripts\n    const baseScriptOptions = { ...baseOptions, manifest, isProduction, enabledPlugins: this.enabledPlugins };\n    const [productionScripts, developmentScripts] = getFilteredScripts(manifest.scripts ?? {});\n    const inputsFromManifest = _getInputsFromScripts(Object.values(developmentScripts), baseOptions);\n    const productionInputsFromManifest = _getInputsFromScripts(Object.values(productionScripts), baseOptions);\n\n    const hasProductionInput = (input: Input) =>\n      productionInputsFromManifest.find(d => d.specifier === input.specifier && d.type === input.type);\n\n    const createGetInputsFromScripts =\n      (containingFilePath: string): GetInputsFromScriptsPartial =>\n      (scripts, options) =>\n        _getInputsFromScripts(scripts, { ...baseOptions, ...options, containingFilePath });\n\n    const inputs: Input[] = [];\n    const remainingPlugins = new Set(this.enabledPlugins);\n\n    const configFilesMap = this.configFilesMap;\n    const configFiles = this.configFilesMap.get(wsName);\n    const seen = new Map<string, Set<string>>();\n    const parsedConfigCache = new Map<string, ReturnType<typeof parseFile> | undefined>();\n\n    const storeConfigFilePath = (pluginName: PluginName, input: ConfigInput) => {\n      const configFilePath = this.handleInput(input);\n      if (configFilePath) {\n        const workspace = this.findWorkspaceByFilePath(configFilePath);\n        if (workspace) {\n          // TODO Are we handling root → child and vice-versa transfers properly?\n          const name = this.name === ROOT_WORKSPACE_NAME ? workspace.name : this.name;\n          if (!configFilesMap.has(name)) configFilesMap.set(name, new Map());\n          if (!configFilesMap.get(name)?.has(pluginName)) configFilesMap.get(name)?.set(pluginName, new Set());\n          configFilesMap.get(name)?.get(pluginName)?.add(configFilePath);\n        }\n      }\n    };\n\n    for (const input of [...inputsFromManifest, ...productionInputsFromManifest]) {\n      if (isConfig(input)) {\n        storeConfigFilePath(input.pluginName, { ...input, containingFilePath });\n      } else if (!isProduction || (isProduction && (input.production || hasProductionInput(input)))) {\n        inputs.push({ ...input, containingFilePath });\n      }\n    }\n\n    const runPlugin = async (pluginName: PluginName, patterns: string[]) => {\n      const plugin = Plugins[pluginName];\n      const config = this.getConfigForPlugin(pluginName);\n\n      if (!config) return [];\n\n      const inputs: Input[] = [];\n      const addInput = (input: Input, containingFilePath = input.containingFilePath) => {\n        if (isConfig(input)) {\n          storeConfigFilePath(input.pluginName, { ...input, containingFilePath });\n        } else {\n          inputs.push(Object.assign(input, { containingFilePath }));\n        }\n      };\n\n      const label = 'config file';\n      const configFilePaths = await _glob({ patterns, cwd: rootCwd, dir: cwd, gitignore: false, label });\n\n      const options = {\n        ...baseScriptOptions,\n        config,\n        configFilePath: containingFilePath,\n        configFileDir: cwd,\n        configFileName: '',\n        getInputsFromScripts: createGetInputsFromScripts(containingFilePath),\n      };\n\n      if (config.entry) {\n        const toInput = isProduction && plugin.production && plugin.production.length > 0 ? toProductionEntry : toEntry;\n        for (const id of config.entry) inputs.push(toInput(id));\n      } else if (\n        (!plugin.resolveConfig && !plugin.resolveFromAST) ||\n        (configFilePaths.filter(path => basename(path) !== 'package.json').length === 0 &&\n          (!this.configFilesMap.get(wsName)?.get(pluginName) ||\n            this.configFilesMap.get(wsName)?.get(pluginName)?.size === 0))\n      ) {\n        if (plugin.entry) for (const id of plugin.entry) inputs.push(toEntry(id));\n        if (plugin.production) for (const id of plugin.production) inputs.push(toProductionEntry(id));\n      }\n\n      if (typeof plugin.setup === 'function') await plugin.setup();\n\n      for (const configFilePath of configFilePaths) {\n        const isManifest = basename(configFilePath) === 'package.json';\n        const fd = isManifest ? undefined : this.cache.getFileDescriptor(configFilePath);\n\n        if (fd?.meta?.data && !fd.changed) {\n          const data = fd.meta.data;\n          if (data.resolveConfig) for (const id of data.resolveConfig) addInput(id, configFilePath);\n          if (data.resolveFromAST) for (const id of data.resolveFromAST) addInput(id, configFilePath);\n          if (data.configFile) addInput(data.configFile);\n          continue;\n        }\n\n        let parsed: ReturnType<typeof parseFile> | undefined;\n        if (!isManifest) {\n          if (parsedConfigCache.has(configFilePath)) {\n            parsed = parsedConfigCache.get(configFilePath);\n          } else {\n            const sourceText = this.readFile(configFilePath);\n            parsed = sourceText ? parseFile(configFilePath, sourceText) : undefined;\n            parsedConfigCache.set(configFilePath, parsed);\n          }\n        }\n\n        const resolveOpts = {\n          ...options,\n          getInputsFromScripts: createGetInputsFromScripts(configFilePath),\n          configFilePath,\n          configFileDir: dirname(configFilePath),\n          configFileName: basename(configFilePath),\n        };\n\n        const cache: CacheItem = {};\n\n        const key = `${wsName}:${pluginName}`;\n        if (plugin.resolveConfig && !seen.get(key)?.has(configFilePath)) {\n          if (parsed && isExternalReExportsOnly(parsed)) {\n            cache.resolveConfig = [];\n          }\n\n          if (!cache.resolveConfig) {\n            const isLoad =\n              typeof plugin.isLoadConfig === 'function' ? plugin.isLoadConfig(resolveOpts, this.dependencies) : true;\n            const localConfig = isLoad && (await loadConfigForPlugin(configFilePath, plugin, resolveOpts, pluginName));\n            if (localConfig) {\n              const inputs = await plugin.resolveConfig(localConfig, resolveOpts);\n              if (plugin.isFilterTransitiveDependencies && !isManifest) {\n                this.filterTransitiveDependencies(inputs, configFilePath);\n              }\n              for (const input of inputs) addInput(input, configFilePath);\n              cache.resolveConfig = inputs;\n            }\n          }\n        }\n\n        if (plugin.resolveFromAST && parsed) {\n          const resolveASTOpts = { ...resolveOpts, readFile: this.readFile };\n          const inputs = plugin.resolveFromAST(parsed.program, resolveASTOpts);\n          for (const input of inputs) addInput(input, configFilePath);\n          cache.resolveFromAST = inputs;\n        }\n\n        if (!isManifest) {\n          inputs.push(toEntry(configFilePath));\n          storeConfigFilePath(pluginName, toConfig(pluginName, configFilePath));\n          cache.configFile = toEntry(configFilePath);\n\n          if (fd?.changed && fd.meta && !seen.get(key)?.has(configFilePath)) {\n            fd.meta.data = cache;\n          }\n\n          if (!seen.has(key)) seen.set(key, new Set());\n          seen.get(key)?.add(configFilePath);\n        }\n      }\n\n      if (plugin.resolve) {\n        const dependencies = (await plugin.resolve(options)) ?? [];\n        for (const id of dependencies) addInput(id, containingFilePath);\n      }\n\n      // Any negated pattern will move all plugin inputs to new group\n      if (inputs.some(input => input.specifier.startsWith('!'))) for (const input of inputs) input.group = pluginName;\n\n      return inputs;\n    };\n\n    const enabledPluginTitles = this.enabledPlugins.map(name => Plugins[name].title);\n    debugLogObject(this.name, 'Enabled plugins', enabledPluginTitles);\n\n    for (const pluginName of this.enabledPlugins) {\n      const patterns = [...this.getConfigurationFilePatterns(pluginName), ...(configFiles?.get(pluginName) ?? [])];\n      configFiles?.delete(pluginName);\n      for (const input of await runPlugin(pluginName, compact(patterns))) inputs.push(input);\n      remainingPlugins.delete(pluginName);\n    }\n\n    {\n      // Handle config files added from root or current workspace recursively\n      const configFiles = this.configFilesMap.get(wsName);\n      if (configFiles) {\n        do {\n          for (const [pluginName, dependencies] of configFiles) {\n            configFiles.delete(pluginName);\n            if (this.enabledPlugins.includes(pluginName)) {\n              for (const input of await runPlugin(pluginName, Array.from(dependencies))) inputs.push(input);\n            } else for (const id of dependencies) inputs.push(toEntry(id));\n          }\n        } while (remainingPlugins.size > 0 && configFiles.size > 0);\n      }\n    }\n\n    debugLogArray(wsName, 'Plugin dependencies', () => compact(inputs.map(input => toDebugString(input, rootCwd))));\n\n    return inputs;\n  }\n\n  private filterTransitiveDependencies(inputs: Input[], configFilePath: string) {\n    const literals = new Set<string>();\n    const visited = new Set<string>();\n    const collect = (filePath: string) => {\n      if (visited.has(filePath)) return;\n      visited.add(filePath);\n      const sourceText = this.readFile(filePath);\n      if (!sourceText) return;\n      for (const literal of collectStringLiterals(sourceText, filePath)) {\n        literals.add(literal);\n        if (isInternal(literal)) collect(join(dirname(filePath), literal));\n      }\n    };\n    collect(configFilePath);\n    for (const input of inputs) {\n      if (!input.optional && (isDeferResolve(input) || isDependency(input))) {\n        const name = getPackageNameFromSpecifier(input.specifier);\n        if (name && !literals.has(name)) input.optional = true;\n      }\n    }\n  }\n\n  public getConfigurationHints(\n    type: 'entry' | 'project',\n    patterns: string[],\n    filePaths: string[],\n    includedPaths: Set<string>\n  ) {\n    const hints: ConfigurationHint[] = [];\n    const entries = this.config[type].filter(pattern => !pattern.startsWith('!'));\n    const workspaceName = this.name;\n    const userDefinedPatterns = entries.filter(id => !isDefaultPattern(type, id));\n\n    if (userDefinedPatterns.length === 0) return hints;\n\n    if (filePaths.length === 0) {\n      const identifier = `[${entries[0]}${entries.length > 1 ? `, ${ELLIPSIS}` : ''}]`;\n      hints.push({ type: `${type}-empty`, identifier, workspaceName });\n      return hints;\n    }\n\n    for (const pattern of patterns) {\n      if (pattern.startsWith('!')) continue;\n      const filePathOrPattern = join(this.dir, pattern.replace(/!$/, ''));\n      if (includedPaths.has(filePathOrPattern)) {\n        hints.push({ type: `${type}-redundant`, identifier: pattern, workspaceName });\n      } else {\n        const matcher = picomatch(filePathOrPattern);\n        if (!filePaths.some(filePath => matcher(filePath))) {\n          hints.push({ type: `${type}-empty`, identifier: pattern, workspaceName });\n        }\n      }\n    }\n\n    return hints;\n  }\n\n  public onDispose() {\n    this.cache.reconcile();\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/YamlCatalogPeeker.ts",
    "content": "import { DEFAULT_CATALOG } from './util/catalog.ts';\n\nfunction matchesKey(line: string, indent: string, key: string) {\n  return (\n    line.startsWith(`${indent}${key}:`) ||\n    line.startsWith(`${indent}\"${key}\":`) ||\n    line.startsWith(`${indent}'${key}':`)\n  );\n}\n\nexport class YamlCatalogPeeker {\n  private lines: string[] = [];\n  private sections: Record<string, number> = {};\n  private ready = false;\n  private fileContent: string;\n\n  constructor(fileContent: string) {\n    this.fileContent = fileContent;\n  }\n\n  private init() {\n    this.lines = this.fileContent.split('\\n');\n    for (let i = 0; i < this.lines.length; i++) {\n      const line = this.lines[i];\n      if (line.startsWith('catalog:')) {\n        this.sections.catalog = i;\n      } else if (line.startsWith('catalogs:')) {\n        this.sections.catalogs = i;\n      }\n    }\n    this.ready = true;\n  }\n\n  public getLocation(parentSymbol: string, symbol: string) {\n    if (!this.ready) this.init();\n\n    const isDefault = parentSymbol === DEFAULT_CATALOG;\n    const startLine = this.sections[isDefault ? 'catalog' : 'catalogs'];\n\n    if (typeof startLine === 'undefined') return;\n\n    if (isDefault) {\n      for (let i = startLine + 1; i < this.lines.length; i++) {\n        const line = this.lines[i];\n        if (!line.startsWith('  ')) break;\n        if (matchesKey(line, '  ', symbol)) {\n          return { line: i + 1, col: line.indexOf(symbol) + 1 };\n        }\n      }\n    } else {\n      let inTargetCatalog = false;\n      for (let i = startLine + 1; i < this.lines.length; i++) {\n        const line = this.lines[i];\n        if (!line.startsWith('  ')) break;\n\n        if (matchesKey(line, '  ', parentSymbol)) {\n          inTargetCatalog = true;\n        } else if (inTargetCatalog) {\n          if (!line.startsWith('    ')) {\n            inTargetCatalog = false;\n            continue;\n          }\n          if (matchesKey(line, '    ', symbol)) {\n            return { line: i + 1, col: line.indexOf(symbol) + 1 };\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/binaries/bash-parser.ts",
    "content": "import { parse, type Node, type Script, type Statement, type Word } from 'unbash';\nimport { Plugins, pluginArgsMap } from '../plugins.ts';\nimport type { FromArgs, GetInputsFromScriptsOptions } from '../types/config.ts';\nimport { debugLogObject } from '../util/debug.ts';\nimport { type Input, toBinary, toDeferResolve } from '../util/input.ts';\nimport { extractBinary, isValidBinary } from '../util/modules.ts';\nimport { relative } from '../util/path.ts';\nimport { truncate } from '../util/string.ts';\nimport { resolve as fallbackResolve } from './fallback.ts';\nimport KnownResolvers from './resolvers/index.ts';\nimport { resolve as resolverFromPlugins } from './plugins.ts';\nimport { parseNodeArgs } from './util.ts';\n\ntype KnownResolver = keyof typeof KnownResolvers;\n\nconst spawningBinaries = ['cross-env', 'retry-cli'];\n\nconst collectExpansionScripts = (word: Word, out: Script[]) => {\n  if (!word.parts) return;\n  for (const part of word.parts) {\n    if ((part.type === 'CommandExpansion' || part.type === 'ProcessSubstitution') && part.script) {\n      out.push(part.script);\n    } else if (part.type === 'DoubleQuoted' || part.type === 'LocaleString') {\n      for (const child of part.parts) {\n        if (child.type === 'CommandExpansion' && child.script) out.push(child.script);\n      }\n    }\n  }\n};\n\nexport const getDependenciesFromScript = (script: string, options: GetInputsFromScriptsOptions): Input[] => {\n  if (!script) return [];\n\n  // Helper for recursive calls\n  const fromArgs: FromArgs = (args, opts): Input[] => {\n    if (args.length === 0 || !isValidBinary(args[0].split(' ')[0])) return [];\n    return getDependenciesFromScript(args.filter(arg => arg !== '--').join(' '), {\n      ...options,\n      knownBinsOnly: false,\n      ...opts,\n    });\n  };\n\n  const definedFunctions = new Set<string>();\n  const collectFunctionNames = (statements: Statement[]): void => {\n    for (const stmt of statements) if (stmt.command.type === 'Function') definedFunctions.add(stmt.command.name.text);\n  };\n\n  const processScript = (s: Script): Input[] => {\n    collectFunctionNames(s.commands);\n    const pending: Script[] = [];\n    const mainDeps = getDependenciesFromStatements(s.commands, pending);\n    const expansionDeps = pending.flatMap(inner => processScript(inner));\n    return [...mainDeps, ...expansionDeps];\n  };\n\n  const getDependenciesFromStatements = (statements: Statement[], pending: Script[]): Input[] =>\n    statements.flatMap(stmt => getDependenciesFromNode(stmt.command, pending));\n\n  const getDependenciesFromNode = (node: Node, pending: Script[]): Input[] => {\n    switch (node.type) {\n      case 'Command': {\n        const text = node.name?.value;\n        const binary = text ? extractBinary(text) : text;\n\n        if (node.name) collectExpansionScripts(node.name, pending);\n        for (const prefix of node.prefix) if (prefix.value) collectExpansionScripts(prefix.value, pending);\n        for (const suffix of node.suffix) collectExpansionScripts(suffix, pending);\n\n        // Bunch of early bail outs for things we can't or don't want to resolve\n        if (!binary || binary === '.' || binary === 'source' || binary === '[') return [];\n        if (binary.startsWith('-') || binary.startsWith('..')) return [];\n        if (definedFunctions.has(binary)) return [];\n\n        const args = node.suffix.map(w => w.value);\n\n        // Commands that precede other commands, try again with the rest\n        if (['!', 'test'].includes(binary)) return fromArgs(args);\n\n        const fromNodeOptions = node.prefix\n          .filter(a => a.name === 'NODE_OPTIONS' && a.value)\n          .map(a => a.value!.value)\n          .map(arg => parseNodeArgs(arg.split(' ')))\n          .filter(args => args.require)\n          .flatMap(arg => arg.require)\n          .map(id => toDeferResolve(id));\n\n        if (binary in KnownResolvers) {\n          const resolver = KnownResolvers[binary as KnownResolver];\n          return resolver(binary, args, { ...options, fromArgs });\n        }\n\n        if (pluginArgsMap.has(binary)) {\n          return [...resolverFromPlugins(binary, args, { ...options, fromArgs }), ...fromNodeOptions];\n        }\n\n        if (spawningBinaries.includes(binary)) {\n          const rest = node.suffix\n            .filter(w => w.text !== '--')\n            .map(w => w.text)\n            .join(' ');\n          return [toBinary(binary), ...getDependenciesFromScript(rest, options)];\n        }\n\n        if (binary in Plugins) {\n          return [...fallbackResolve(binary, args, { ...options, fromArgs }), ...fromNodeOptions];\n        }\n\n        // Before using the fallback resolver, we need a way to bail out for scripts in CI environments like GitHub\n        // Actions, which are provisioned with lots of unknown global binaries.\n        if (options.knownBinsOnly && !text?.startsWith('.')) return [];\n\n        return [...fallbackResolve(binary, args, { ...options, fromArgs }), ...fromNodeOptions];\n      }\n      case 'AndOr':\n      case 'Pipeline':\n        return node.commands.flatMap(n => getDependenciesFromNode(n, pending));\n      case 'If':\n        return [\n          ...getDependenciesFromStatements(node.clause.commands, pending),\n          ...getDependenciesFromStatements(node.then.commands, pending),\n          ...(node.else ? getDependenciesFromNode(node.else, pending) : []),\n        ];\n      case 'While':\n        return [\n          ...getDependenciesFromStatements(node.clause.commands, pending),\n          ...getDependenciesFromStatements(node.body.commands, pending),\n        ];\n      case 'For':\n      case 'Select':\n      case 'Subshell':\n      case 'BraceGroup':\n        return getDependenciesFromStatements(node.body.commands, pending);\n      case 'CompoundList':\n        return getDependenciesFromStatements(node.commands, pending);\n      case 'Function':\n      case 'Coproc':\n        return getDependenciesFromNode(node.body, pending);\n      case 'Statement':\n        return getDependenciesFromNode(node.command, pending);\n      default:\n        return [];\n    }\n  };\n\n  try {\n    const parsed = parse(script);\n    if (!parsed.commands) return [];\n    return processScript(parsed);\n  } catch (error) {\n    const msg = `Warning: failed to parse and ignoring script in ${relative(options.cwd, options.containingFilePath)} (${truncate(script, 30)})`;\n    debugLogObject('*', msg, error);\n    return [];\n  }\n};\n"
  },
  {
    "path": "packages/knip/src/binaries/fallback.ts",
    "content": "import parseArgs from 'minimist';\nimport type { BinaryResolver } from '../types/config.ts';\nimport { compact } from '../util/array.ts';\nimport { toBinary, toDeferResolve, toEntry } from '../util/input.ts';\nimport { isValidBinary } from '../util/modules.ts';\n\n// Generic fallbacks for basic handling of binaries that don't have a plugin nor a custom resolver\n\n// Binaries that have a new script behind the double-dash/end-of-command\nconst endOfCommandBinaries = ['dotenvx', 'env-cmd', 'op'];\n\n// Binaries with entry at first positional arg\nconst positionals = new Set(['babel-node', 'esbuild', 'execa', 'jiti', 'oxnode', 'vite-node', 'zx']);\n\n// Binaries where each positional arg is a separate script\nconst positionalBinaries = new Set(['concurrently']);\n\nexport const resolve: BinaryResolver = (binary, args, { fromArgs }) => {\n  const parsed = parseArgs(args, { boolean: ['quiet', 'verbose'], '--': endOfCommandBinaries.includes(binary) });\n  const bin = binary.startsWith('.') ? toEntry(binary) : isValidBinary(binary) ? toBinary(binary) : undefined;\n  const pos = positionals.has(binary) ? [toDeferResolve(parsed._[0])] : [];\n  const newCommand = parsed['--'] && parsed['--'].length > 0 ? fromArgs(parsed['--']) : [];\n  const commands = positionalBinaries.has(binary) ? parsed._.flatMap(cmd => fromArgs([cmd])) : [];\n  return compact([bin, ...pos, ...newCommand, ...commands]);\n};\n"
  },
  {
    "path": "packages/knip/src/binaries/index.ts",
    "content": "import type { GetInputsFromScripts } from '../types/config.ts';\nimport { fromBinary, type Input, isBinary, isDependency } from '../util/input.ts';\nimport { timerify } from '../util/Performance.ts';\nimport { getDependenciesFromScript } from './bash-parser.ts';\n\nconst getInputsFromScripts: GetInputsFromScripts = (npmScripts, options) => {\n  const scripts = typeof npmScripts === 'string' ? [npmScripts] : Array.from(npmScripts);\n  const results = scripts.flatMap(script => getDependenciesFromScript(script, options));\n  const inputs = new Set<Input>();\n\n  for (const input of results) {\n    if (!input.specifier) continue;\n    if (isDependency(input) && input.specifier.startsWith('http')) continue;\n    if (isBinary(input) && !/^\\b/.test(fromBinary(input))) continue;\n    inputs.add(input);\n  }\n\n  return Array.from(inputs);\n};\n\nexport const _getInputsFromScripts = timerify(getInputsFromScripts);\n"
  },
  {
    "path": "packages/knip/src/binaries/plugins.ts",
    "content": "import parseArgs from 'minimist';\nimport { pluginArgsMap } from '../plugins.ts';\nimport type { BinaryResolver } from '../types/config.ts';\nimport { compact } from '../util/array.ts';\nimport { type Input, toBinary, toConfig, toDeferResolve, toDeferResolveEntry, toEntry } from '../util/input.ts';\nimport { extractBinary } from '../util/modules.ts';\nimport { dirname } from '../util/path.ts';\nimport { resolve as fallbackResolve } from './fallback.ts';\n\nconst isGlobLikeMatch = /(^!|[*+\\\\(|{^$])/;\nconst isGlobLike = (value: string) => isGlobLikeMatch.test(value);\n\nconst nodeLoadersArgs = { import: ['r', 'experimental-loader', 'require', 'loader'] };\n\nexport const resolve: BinaryResolver = (binary, _args, options) => {\n  const { cwd, fromArgs, containingFilePath } = options;\n  const [pluginName, pluginArgs] = pluginArgsMap.get(binary) ?? [];\n\n  if (!pluginArgs) return fallbackResolve(binary, _args, options);\n\n  const inputOpts = {};\n  if (cwd && dirname(containingFilePath) !== cwd) Object.assign(inputOpts, { dir: cwd });\n\n  const args = typeof pluginArgs.args === 'function' ? pluginArgs.args(_args) : _args;\n\n  const parsed = parseArgs(args, {\n    string: [\n      ...(pluginArgs.nodeImportArgs ? ['import'] : []),\n      ...(pluginArgs.config === true ? ['config'] : []),\n      ...(pluginArgs.string ?? []),\n    ],\n    boolean: ['quiet', 'verbose', 'watch', ...(pluginArgs.boolean ?? [])],\n    alias: {\n      ...(pluginArgs.nodeImportArgs ? nodeLoadersArgs : {}),\n      ...(pluginArgs.config === true ? { config: ['c'] } : {}),\n      ...pluginArgs.alias,\n    },\n    '--': true,\n  });\n\n  const positionals = [];\n  if (pluginArgs.positional && parsed._[0]) {\n    const id = parsed._[0]; // let's start out safe, but sometimes we'll want more\n    if (isGlobLike(id)) positionals.push(toEntry(id));\n    else {\n      if (id.includes('node_modules/.bin/')) positionals.push(toBinary(extractBinary(id)));\n      else positionals.push(toDeferResolveEntry(id, { optional: true }));\n    }\n  }\n\n  const mapToParsedKey = (id: string) => parsed[id];\n  const resolved = compact(pluginArgs.resolve ? pluginArgs.resolve.flatMap(mapToParsedKey) : []);\n\n  const resolvedImports = pluginArgs.nodeImportArgs && parsed.import ? [parsed.import].flat() : [];\n\n  const resolvedFromArgs =\n    typeof pluginArgs.fromArgs === 'function'\n      ? fromArgs(pluginArgs.fromArgs(parsed, args))\n      : Array.isArray(pluginArgs.fromArgs)\n        ? fromArgs(pluginArgs.fromArgs.flatMap(mapToParsedKey).filter(Boolean))\n        : [];\n\n  const config = pluginArgs.config === true ? ['config'] : pluginArgs.config || [];\n  const mapToConfigPattern = (value: string | [string, (value: string) => string]) => {\n    if (typeof value === 'string')\n      return parsed[value] && pluginName ? [toConfig(pluginName, parsed[value], inputOpts)] : [];\n    const [id, fn] = value;\n    return parsed[id] && pluginName ? [toConfig(pluginName, fn(parsed[id]), inputOpts)] : [];\n  };\n  const configFilePaths = config.flatMap(mapToConfigPattern);\n\n  const inputs: Input[] = pluginArgs.resolveInputs?.(parsed, { args, cwd }) ?? [];\n\n  return [\n    toBinary(binary, inputOpts),\n    ...positionals,\n    ...resolved.map(id => toDeferResolve(id)),\n    ...resolvedImports.map(id => toDeferResolve(id)),\n    ...resolvedFromArgs,\n    ...configFilePaths,\n    ...inputs,\n  ];\n};\n"
  },
  {
    "path": "packages/knip/src/binaries/resolvers/bun.ts",
    "content": "import parseArgs from 'minimist';\nimport type { BinaryResolver } from '../../types/config.ts';\nimport { toEntry } from '../../util/input.ts';\nimport { isAbsolute, join } from '../../util/path.ts';\nimport { _resolveSync } from '../../util/resolve.ts';\nimport { resolveX } from './bunx.ts';\n\nconst commands = new Set([\n  'add',\n  'audit',\n  'auth',\n  'build',\n  'c',\n  'ci',\n  'cloud',\n  'completions',\n  'config',\n  'create',\n  'deploy',\n  'discord',\n  'exec',\n  'feedback',\n  'fuzzilli',\n  'getcompletes',\n  'help',\n  'i',\n  'info',\n  'init',\n  'install',\n  'link',\n  'list',\n  'login',\n  'logout',\n  'outdated',\n  'patch',\n  'patch-commit',\n  'pm',\n  'prune',\n  'publish',\n  'r',\n  'remove',\n  'repl',\n  'rm',\n  'run',\n  'test',\n  'unlink',\n  'uninstall',\n  'update',\n  'upgrade',\n  'use',\n  'whoami',\n  'why',\n  'x',\n]);\n\nexport const resolve: BinaryResolver = (_binary, args, options) => {\n  const parsed = parseArgs(args, { string: ['cwd'] });\n  const [command, script] = parsed._;\n\n  if (command === 'x') {\n    const argsForX = args.filter(arg => arg !== 'x');\n    return resolveX(argsForX, options);\n  }\n\n  const { manifestScriptNames, cwd, fromArgs } = options;\n\n  if (command === 'run' && manifestScriptNames.has(script)) return [];\n  if (manifestScriptNames.has(command)) return [];\n  if (command !== 'run' && commands.has(command)) return [];\n\n  const filePath = command === 'run' ? script : command;\n  if (!filePath) return [];\n  const _cwd = parsed.cwd ? join(cwd, parsed.cwd) : cwd;\n  const resolved = _resolveSync(isAbsolute(filePath) ? filePath : join(_cwd, filePath), _cwd);\n  if (resolved) return [toEntry(resolved)];\n\n  const dir = parsed.cwd ? join(cwd, parsed.cwd) : undefined;\n  const opts = dir ? { cwd: dir } : {};\n  return command === 'run' ? [] : fromArgs(args, opts);\n};\n"
  },
  {
    "path": "packages/knip/src/binaries/resolvers/bunx.ts",
    "content": "import parseArgs from 'minimist';\nimport type { BinaryResolver, BinaryResolverOptions } from '../../types/config.ts';\nimport { toBinary, toDependency } from '../../util/input.ts';\nimport { stripVersionFromSpecifier } from '../../util/modules.ts';\nimport { isInternal } from '../../util/path.ts';\nimport { argsFrom } from '../util.ts';\n\nexport const resolveX = (args: string[], options: BinaryResolverOptions) => {\n  const { fromArgs } = options;\n  const parsed = parseArgs(args, { boolean: ['bun'] });\n  const packageSpecifier = parsed._[0];\n  const specifier = packageSpecifier ? stripVersionFromSpecifier(packageSpecifier) : '';\n  const packages = parsed.package && !parsed.yes ? [parsed.package].flat().map(stripVersionFromSpecifier) : [];\n  const command = parsed['shell-mode'] ? fromArgs([parsed['shell-mode']]) : [];\n  const restArgs = argsFrom(args, packageSpecifier);\n  const isBinary = specifier && !packageSpecifier.includes('@') && !isInternal(specifier);\n  const dependency = isBinary ? toBinary(specifier, { optional: true }) : toDependency(specifier, { optional: true });\n  const specifiers = specifier ? [dependency] : [];\n  return [...specifiers, ...packages.map(id => toDependency(id)), ...command, ...fromArgs(restArgs).slice(1)];\n};\n\nexport const resolve: BinaryResolver = (_binary, args, options) => {\n  return resolveX(args, options);\n};\n"
  },
  {
    "path": "packages/knip/src/binaries/resolvers/find.ts",
    "content": "import type { BinaryResolver } from '../../types/config.ts';\nimport { toBinary } from '../../util/input.ts';\n\nconst execFlags = new Set(['-exec', '-execdir']);\nconst execTerminators = new Set([';', '\\\\;', '+']);\n\nexport const resolve: BinaryResolver = (binary, args, { fromArgs }) => {\n  const execIdx = args.findIndex(a => execFlags.has(a));\n  if (execIdx >= 0) {\n    const cmdWords = [];\n    for (let i = execIdx + 1; i < args.length; i++) {\n      const v = args[i];\n      if (execTerminators.has(v)) break;\n      if (v !== '{}') cmdWords.push(v);\n    }\n    if (cmdWords.length > 0) return [toBinary(binary), ...fromArgs(cmdWords)];\n  }\n  return [toBinary(binary)];\n};\n"
  },
  {
    "path": "packages/knip/src/binaries/resolvers/index.ts",
    "content": "import * as bun from './bun.ts';\nimport * as bunx from './bunx.ts';\nimport * as find from './find.ts';\nimport * as npm from './npm.ts';\nimport * as npx from './npx.ts';\nimport * as pnpm from './pnpm.ts';\nimport * as pnpx from './pnpx.ts';\nimport * as yarn from './yarn.ts';\n\nexport default {\n  bun: bun.resolve,\n  bunx: bunx.resolve,\n  find: find.resolve,\n  npm: npm.resolve,\n  npx: npx.resolve,\n  pnpm: pnpm.resolve,\n  pnpx: pnpx.resolve,\n  yarn: yarn.resolve,\n};\n"
  },
  {
    "path": "packages/knip/src/binaries/resolvers/npm.ts",
    "content": "import parseArgs from 'minimist';\nimport type { BinaryResolver } from '../../types/config.ts';\n\nexport const resolve: BinaryResolver = (_binary, args, options) => {\n  const { fromArgs, manifestScriptNames } = options;\n  const parsed = parseArgs(args, { '--': true });\n  const [command, script] = parsed._;\n  const _childArgs = parsed['--'] && parsed['--'].length > 0 ? fromArgs(parsed['--'], { knownBinsOnly: true }) : [];\n  if (command === 'exec') return _childArgs;\n  if (command === 'run' && manifestScriptNames.has(script)) return _childArgs;\n  return [];\n};\n"
  },
  {
    "path": "packages/knip/src/binaries/resolvers/npx.ts",
    "content": "import parseArgs from 'minimist';\nimport type { BinaryResolver } from '../../types/config.ts';\nimport { toBinary, toDependency } from '../../util/input.ts';\nimport { stripVersionFromSpecifier } from '../../util/modules.ts';\nimport { isInternal } from '../../util/path.ts';\nimport { argsFrom } from '../util.ts';\n\nexport const resolve: BinaryResolver = (_binary, args, options) => {\n  const { fromArgs } = options;\n  const parsed = parseArgs(args, {\n    boolean: ['yes', 'no', 'quiet'],\n    alias: { yes: 'y', no: 'no-install', package: 'p', call: 'c' },\n  });\n\n  const packageSpecifier = parsed._[0];\n  const specifier = packageSpecifier ? stripVersionFromSpecifier(packageSpecifier) : '';\n\n  const packages = parsed.package && !parsed.yes ? [parsed.package].flat().map(stripVersionFromSpecifier) : [];\n  const command = parsed.call ? fromArgs([parsed.call]) : [];\n  const restArgs = argsFrom(args, packageSpecifier);\n\n  const isBinary = specifier && !packageSpecifier.includes('@') && !isInternal(specifier);\n  const dependency = isBinary ? toBinary(specifier) : toDependency(specifier, { optional: !parsed.no });\n  const specifiers = dependency && !parsed.yes ? [dependency] : [];\n\n  return [\n    ...specifiers,\n    ...packages.map(id => toDependency(id, { optional: true })),\n    ...command,\n    ...fromArgs(restArgs).slice(1),\n  ];\n};\n"
  },
  {
    "path": "packages/knip/src/binaries/resolvers/pnpm.ts",
    "content": "import parseArgs from 'minimist';\nimport type { BinaryResolver } from '../../types/config.ts';\nimport { toBinary } from '../../util/input.ts';\nimport { isValidBinary } from '../../util/modules.ts';\nimport { resolveDlx } from './pnpx.ts';\n\n// https://pnpm.io/cli/add\n\nconst commands = [\n  'add',\n  'approve-builds',\n  'audit',\n  'bin',\n  'cache',\n  'cat-file',\n  'cat-index',\n  'config',\n  'dedupe',\n  'deploy',\n  'dlx',\n  'doctor',\n  'env',\n  'fetch',\n  'find-hash',\n  'i',\n  'ignored-builds',\n  'import',\n  'init',\n  'install-test',\n  'install',\n  'it',\n  'licenses',\n  'link',\n  'list',\n  'ln',\n  'ls',\n  'outdated',\n  'pack',\n  'patch-commit',\n  'patch-remove',\n  'patch',\n  'pkg',\n  'prepare',\n  'prune',\n  'publish',\n  'rb',\n  'rebuild',\n  'remove',\n  'rm',\n  'root',\n  'run',\n  'self-update',\n  'server',\n  'setup',\n  'start',\n  'store',\n  't',\n  'test',\n  'tst',\n  'un',\n  'uninstall',\n  'unlink',\n  'up',\n  'update',\n  'upgrade',\n  'version',\n  'why',\n];\n\nexport const resolve: BinaryResolver = (_binary, args, options) => {\n  const parsed = parseArgs(args, {\n    boolean: ['aggregate-output', 'if-present', 'parallel', 'recursive', 'reverse', 'shell-mode', 'silent', 'stream'],\n    alias: { recursive: 'r', silent: 's', 'shell-mode': 'c', filter: 'F' },\n    '--': true,\n  });\n  const [command] = parsed._;\n\n  if (command === 'dlx') {\n    const argsForDlx = args.filter(arg => arg !== 'dlx');\n    return resolveDlx(argsForDlx, options);\n  }\n\n  const { manifestScriptNames, fromArgs } = options;\n\n  if (parsed.filter && !parsed.recursive) return [];\n\n  const childInputs = parsed['--'] && parsed['--'].length > 0 ? fromArgs(parsed['--'], { knownBinsOnly: true }) : [];\n\n  if (command === 'exec') {\n    return childInputs.length > 0 ? childInputs : fromArgs(parsed._.slice(1));\n  }\n\n  const isScript = manifestScriptNames.has(command);\n  if (isScript || commands.includes(command)) return childInputs;\n\n  return command && isValidBinary(command) ? [toBinary(command)] : [];\n};\n"
  },
  {
    "path": "packages/knip/src/binaries/resolvers/pnpx.ts",
    "content": "import parseArgs from 'minimist';\nimport type { BinaryResolver, BinaryResolverOptions } from '../../types/config.ts';\nimport { toDependency } from '../../util/input.ts';\nimport { stripVersionFromSpecifier } from '../../util/modules.ts';\n\nexport const resolveDlx = (args: string[], options: BinaryResolverOptions) => {\n  const parsed = parseArgs(args, {\n    boolean: ['silent'],\n    alias: { package: 'p', 'shell-mode': 'c' },\n  });\n  const packageSpecifier = parsed._[0];\n  const specifier = packageSpecifier ? stripVersionFromSpecifier(packageSpecifier) : '';\n  const packages = parsed.package && !parsed.yes ? [parsed.package].flat().map(stripVersionFromSpecifier) : [];\n  const command = parsed['shell-mode'] ? options.fromArgs([parsed['shell-mode']]) : [];\n  const dependency = specifier ? [toDependency(specifier, { optional: true })] : [];\n  return [...dependency, ...packages.map(id => toDependency(id, { optional: true })), ...command];\n};\n\nexport const resolve: BinaryResolver = (_binary, args, options) => {\n  return resolveDlx(args, options);\n};\n"
  },
  {
    "path": "packages/knip/src/binaries/resolvers/yarn.ts",
    "content": "import parseArgs from 'minimist';\nimport type { BinaryResolver, BinaryResolverOptions } from '../../types/config.ts';\nimport { isBinary, isDependency, toBinary, toDependency } from '../../util/input.ts';\nimport { stripVersionFromSpecifier } from '../../util/modules.ts';\nimport { join } from '../../util/path.ts';\nimport { argsFrom } from '../util.ts';\n\n// https://yarnpkg.com/cli\n\nconst commands = [\n  'add',\n  'bin',\n  'cache',\n  'config',\n  'constraints',\n  'dedupe',\n  'dlx',\n  'explain',\n  'global',\n  'info',\n  'init',\n  'install',\n  'link',\n  'pack',\n  'patch-commit',\n  'patch',\n  'plugin',\n  'publish',\n  'rebuild',\n  'remove',\n  'search',\n  'set',\n  'stage',\n  'unlink',\n  'unplug',\n  'up',\n  'upgrade-interactive',\n  'upgrade',\n  'version',\n  'why',\n  'workspace',\n  'workspaces',\n];\n\nconst resolveDlx = (args: string[], options: BinaryResolverOptions) => {\n  const parsed = parseArgs(args, {\n    boolean: ['quiet'],\n    alias: { package: 'p', quiet: 'q' },\n  });\n  const packageSpecifier = parsed._[0];\n  const specifier = packageSpecifier ? stripVersionFromSpecifier(packageSpecifier) : '';\n  const packages = parsed.package && !parsed.yes ? [parsed.package].flat().map(stripVersionFromSpecifier) : [];\n  const command = specifier ? options.fromArgs(parsed._) : [];\n  return [...packages.map(id => toDependency(id)), ...command].map(id =>\n    isDependency(id) || isBinary(id) ? Object.assign(id, { optional: true }) : id\n  );\n};\n\nexport const resolve: BinaryResolver = (_binary, args, options) => {\n  const { manifestScriptNames, fromArgs, cwd, rootCwd } = options;\n  const parsed = parseArgs(args, { boolean: ['top-level'], string: ['cwd'], '--': true });\n  const dir = parsed['top-level'] ? rootCwd : parsed.cwd ? join(cwd, parsed.cwd) : undefined;\n  const [command, binary] = parsed._;\n\n  if (!command && !binary) return [];\n\n  const _childArgs = parsed['--'] && parsed['--'].length > 0 ? fromArgs(parsed['--'], { knownBinsOnly: true }) : [];\n\n  if (command === 'run') {\n    if (manifestScriptNames.has(binary)) return _childArgs;\n    const bin = toBinary(binary, { optional: true });\n    if (dir) Object.assign(bin, { dir });\n    return [bin, ..._childArgs];\n  }\n\n  if (command === 'node') return fromArgs(parsed._);\n\n  if (command === 'dlx') {\n    const argsForDlx = args.filter(arg => arg !== 'dlx');\n    return resolveDlx(argsForDlx, options);\n  }\n\n  if ((!dir && manifestScriptNames.has(command)) || commands.includes(command)) return _childArgs;\n\n  const opts = dir ? { cwd: dir } : {};\n  return fromArgs(argsFrom(args, command === 'exec' ? binary : command), opts);\n};\n"
  },
  {
    "path": "packages/knip/src/binaries/util.ts",
    "content": "import parseArgs from 'minimist';\n\nexport const argsFrom = (args: string[], from: string) => args.slice(args.indexOf(from));\n\nexport const parseNodeArgs = (args: string[]) =>\n  parseArgs(args, {\n    string: ['r'],\n    alias: { require: ['r', 'loader', 'experimental-loader', 'test-reporter', 'watch', 'import'] },\n  });\n"
  },
  {
    "path": "packages/knip/src/cli.ts",
    "content": "/* oxlint-disable no-console */\nimport { fix } from './IssueFixer.ts';\nimport { run } from './run.ts';\nimport type { IssueType, ReporterOptions } from './types/issues.ts';\nimport parseArgs, { helpText } from './util/cli-arguments.ts';\nimport { createOptions } from './util/create-options.ts';\nimport {\n  getKnownErrors,\n  hasErrorCause,\n  isConfigurationError,\n  isKnownError,\n  isLoaderError,\n  isModuleNotFoundError,\n} from './util/errors.ts';\nimport { logError } from './util/log.ts';\nimport { perfObserver } from './util/Performance.ts';\nimport { runPreprocessors, runReporters } from './util/reporter.ts';\nimport { prettyMilliseconds } from './util/string.ts';\nimport { version } from './version.ts';\n\nlet args: ReturnType<typeof parseArgs> = {};\ntry {\n  args = parseArgs();\n} catch (error: unknown) {\n  if (error instanceof Error) {\n    console.error(error.message);\n    console.log(`\\n${helpText}`);\n    process.exit(1);\n  }\n  throw error;\n}\n\nconst main = async () => {\n  try {\n    if (args.help) {\n      console.log(helpText);\n      process.exit(0);\n    }\n\n    if (args.version) {\n      console.log(version);\n      process.exit(0);\n    }\n\n    const options = await createOptions({ args });\n\n    const { results } = await run(options);\n\n    const {\n      issues,\n      counters,\n      tagHints,\n      configurationHints,\n      includedWorkspaceDirs,\n      enabledPlugins,\n      selectedWorkspaces,\n    } = results;\n\n    // These modes have their own reporting mechanism\n    if (options.isWatch || options.isTrace) return;\n\n    const initialData: ReporterOptions = {\n      report: options.includedIssueTypes,\n      issues,\n      counters,\n      tagHints,\n      configurationHints,\n      enabledPlugins,\n      includedWorkspaceDirs,\n      cwd: options.cwd,\n      configFilePath: options.configFilePath,\n      isDisableConfigHints: options.isDisableConfigHints,\n      isProduction: options.isProduction,\n      isShowProgress: options.isShowProgress,\n      isTreatConfigHintsAsErrors: options.isTreatConfigHintsAsErrors,\n      maxShowIssues: args['max-show-issues'] ? Number(args['max-show-issues']) : undefined,\n      options: args['reporter-options'] ?? '',\n      preprocessorOptions: args['preprocessor-options'] ?? '',\n      selectedWorkspaces,\n    };\n\n    const finalData = await runPreprocessors(args.preprocessor ?? [], initialData);\n\n    if (options.isFix) await fix(finalData.issues, finalData.counters, options);\n\n    await runReporters(args.reporter ?? ['symbols'], finalData);\n\n    const totalErrorCount = (Object.keys(finalData.report) as IssueType[])\n      .filter(reportGroup => finalData.report[reportGroup] && options.rules[reportGroup] === 'error')\n      .reduce((errorCount: number, reportGroup) => errorCount + finalData.counters[reportGroup], 0);\n\n    if (perfObserver.isEnabled) await perfObserver.finalize();\n    if (perfObserver.isTimerifyFunctions) console.log(`\\n${perfObserver.getTimerifiedFunctionsTable()}`);\n    if (perfObserver.isMemoryUsageEnabled && !args['memory-realtime'])\n      console.log(`\\n${perfObserver.getMemoryUsageTable()}`);\n\n    if (perfObserver.isEnabled) {\n      const duration = perfObserver.getCurrentDurationInMs();\n      console.log('\\nTotal running time:', prettyMilliseconds(duration));\n      perfObserver.reset();\n    }\n\n    if (\n      (!args['no-exit-code'] && totalErrorCount > Number(args['max-issues'] ?? 0)) ||\n      (!options.isDisableConfigHints && options.isTreatConfigHintsAsErrors && configurationHints.length > 0)\n    ) {\n      process.exit(1);\n    }\n  } catch (error: unknown) {\n    process.exitCode = 2;\n    if (!args.debug && error instanceof Error && isKnownError(error)) {\n      const knownErrors = getKnownErrors(error);\n      for (const knownError of knownErrors) logError('ERROR', knownError.message);\n      if (hasErrorCause(knownErrors[0])) {\n        console.error('Reason:', knownErrors[0].cause.message);\n        if (isModuleNotFoundError(knownErrors[0].cause))\n          console.log('Module load error? Visit https://knip.dev/reference/known-issues');\n        if (isLoaderError(knownErrors[0]))\n          console.log('Configuration file load error? Visit https://knip.dev/reference/known-issues');\n      }\n      if (isConfigurationError(knownErrors[0])) console.log('\\nRun `knip --help` or visit https://knip.dev for help');\n      process.exit(2);\n    }\n    // We shouldn't arrive here, but not swallow either, so re-throw\n    throw error;\n  }\n\n  process.exit(0);\n};\n\nawait main();\n"
  },
  {
    "path": "packages/knip/src/compilers/compilers.ts",
    "content": "import type { CompilerSync } from './types.ts';\n\nexport const fencedCodeBlockMatcher = /```[\\s\\S]*?```/g;\nexport const inlineCodeMatcher = /`[^`]+`/g;\n\n// Extract imports from body of <script> nodes\nconst scriptExtractor = /<script\\b[^>]*>([\\s\\S]*?)<\\/script>/gm;\nexport const importMatcher = /import[^'\"]+['\"][^'\"]+['\"]/g;\nexport const importsWithinScripts: CompilerSync = (text: string) => {\n  const scripts = [];\n  let scriptMatch: RegExpExecArray | null;\n  // oxlint-disable-next-line no-cond-assign\n  while ((scriptMatch = scriptExtractor.exec(text))) {\n    for (const importMatch of scriptMatch[1].matchAll(importMatcher)) {\n      scripts.push(importMatch);\n    }\n  }\n  return scripts.join(';\\n');\n};\n\n// Extract body of <script>、<script lang=\"ts\">、<script setup>、<script lang=\"ts\" setup> etc. nodes\nconst scriptBodyExtractor = /<script\\b[^>]*>(?<body>[\\s\\S]*?)<\\/script>/gm;\nexport const scriptBodies: CompilerSync = (text: string) => {\n  const scripts = [];\n  let scriptMatch: RegExpExecArray | null;\n  // oxlint-disable-next-line no-cond-assign\n  while ((scriptMatch = scriptBodyExtractor.exec(text))) {\n    if (scriptMatch.groups?.body) scripts.push(scriptMatch.groups.body);\n  }\n  return scripts.join(';\\n');\n};\n\n// Extract paths as imports from frontmatter for given keys (e.g., 'layout')\nexport const frontmatterMatcher = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/;\nexport const importsWithinFrontmatter = (text: string, keys: string[] = []) => {\n  const frontmatter = text.match(frontmatterMatcher)?.[1];\n  if (!frontmatter) return '';\n\n  const imports = keys.flatMap(key => {\n    const valueMatcher = new RegExp(`${key}:\\\\s*[\"']([^\"']+)[\"']`, 'i');\n    const match = frontmatter.match(valueMatcher);\n    return match?.[1] ? [`import ${key} from \"${match[1]}\";`] : [];\n  });\n  return imports.join('\\n');\n};\n"
  },
  {
    "path": "packages/knip/src/compilers/index.ts",
    "content": "import type { RawConfiguration } from '../types/config.ts';\nimport type { DependencySet } from '../types/workspace.ts';\nimport MDX from './mdx.ts';\nimport SCSS from './scss.ts';\nimport type {\n  AsyncCompilers,\n  CompilerAsync,\n  CompilerSync,\n  Compilers,\n  RawSyncCompilers,\n  SyncCompilers,\n} from './types.ts';\n\n// TODO This does not detect functions returning a promise (just the async keyword)\nconst isAsyncCompiler = (fn?: CompilerSync | CompilerAsync) => (fn ? fn.constructor.name === 'AsyncFunction' : false);\n\nexport const normalizeCompilerExtension = (ext: string) => ext.replace(/^\\.*/, '.');\n\nexport const partitionCompilers = (rawLocalConfig: RawConfiguration) => {\n  const syncCompilers: Record<string, CompilerSync | true> = {};\n  const asyncCompilers: Record<string, CompilerAsync> = {};\n\n  for (const extension in rawLocalConfig.compilers) {\n    const ext = normalizeCompilerExtension(extension);\n    const compilerFn = rawLocalConfig.compilers[extension];\n    if (typeof compilerFn === 'function') {\n      if (!rawLocalConfig.asyncCompilers?.[ext] && isAsyncCompiler(compilerFn)) {\n        asyncCompilers[ext] = compilerFn as CompilerAsync;\n      } else {\n        syncCompilers[ext] = compilerFn as CompilerSync;\n      }\n    } else if (compilerFn === true) {\n      syncCompilers[ext] = true;\n    }\n  }\n\n  for (const extension in rawLocalConfig.asyncCompilers) {\n    const ext = normalizeCompilerExtension(extension);\n    asyncCompilers[ext] = rawLocalConfig.asyncCompilers[extension] as CompilerAsync;\n  }\n\n  return { ...rawLocalConfig, syncCompilers, asyncCompilers };\n};\n\nconst compilers = new Map([\n  ['.mdx', MDX],\n  ['.sass', SCSS],\n  ['.scss', SCSS],\n]);\n\nexport const getIncludedCompilers = (\n  syncCompilers: RawSyncCompilers,\n  asyncCompilers: AsyncCompilers,\n  dependencies: DependencySet\n): Compilers => {\n  const hasDependency = (packageName: string) => dependencies.has(packageName);\n  for (const [extension, { condition, compiler }] of compilers) {\n    if ((!syncCompilers.has(extension) && condition(hasDependency)) || syncCompilers.get(extension) === true) {\n      syncCompilers.set(extension, compiler);\n    }\n  }\n  return [syncCompilers as SyncCompilers, asyncCompilers];\n};\n\nexport const getCompilerExtensions = (compilers: [SyncCompilers, AsyncCompilers]) => [\n  ...compilers[0].keys(),\n  ...compilers[1].keys(),\n];\n"
  },
  {
    "path": "packages/knip/src/compilers/mdx.ts",
    "content": "import { fencedCodeBlockMatcher, frontmatterMatcher, inlineCodeMatcher } from './compilers.ts';\nimport type { HasDependency } from './types.ts';\n\n// https://mdxjs.com/packages/\nconst mdxDependencies = [\n  '@mdx-js/esbuild',\n  '@mdx-js/loader',\n  '@mdx-js/mdx',\n  '@mdx-js/node-loader',\n  '@mdx-js/preact',\n  '@mdx-js/react',\n  '@mdx-js/rollup',\n  '@mdx-js/vue',\n  'remark-mdx',\n];\n\nconst condition = (hasDependency: HasDependency) => mdxDependencies.some(hasDependency);\n\nconst mdxImportMatcher = /^import[^'\"]+['\"][^'\"]+['\"]/gm;\n\nconst compiler = (text: string) =>\n  [\n    ...text\n      .replace(frontmatterMatcher, '')\n      .replace(fencedCodeBlockMatcher, '')\n      .replace(inlineCodeMatcher, '')\n      .matchAll(mdxImportMatcher),\n  ].join('\\n');\n\nexport default { condition, compiler };\n"
  },
  {
    "path": "packages/knip/src/compilers/scss.ts",
    "content": "import { existsSync } from 'node:fs';\nimport { basename, dirname, join } from '../util/path.ts';\nimport type { CompilerSync, HasDependency } from './types.ts';\n\nconst condition = (hasDependency: HasDependency) =>\n  hasDependency('sass') || hasDependency('sass-embedded') || hasDependency('node-sass');\n\nconst importMatcher = /@(?:use|import|forward)\\s+['\"](pkg:)?([^'\"]+)['\"]/g;\n\n// Resolve _partials here to not soil the main resolver\nconst resolvePartial = (specifier: string, containingFile: string) => {\n  const rel = specifier.startsWith('.') ? specifier : `./${specifier}`;\n  const name = basename(rel);\n  if (name.startsWith('_')) return rel;\n  const dir = dirname(rel);\n  const partial = name.endsWith('.scss') ? `_${name}` : `_${name}.scss`;\n  if (existsSync(join(dirname(containingFile), dir, partial))) return `${dir}/_${name}`;\n  return rel;\n};\n\nconst compiler: CompilerSync = (text, filePath) =>\n  [...text.matchAll(importMatcher)]\n    .filter(match => match[2] && !match[2].startsWith('sass:'))\n    .map((match, i) => `import _$${i} from '${match[1] ? match[2] : resolvePartial(match[2], filePath)}';`)\n    .join('\\n');\n\nexport default { condition, compiler };\n"
  },
  {
    "path": "packages/knip/src/compilers/types.ts",
    "content": "type FileExtension = string;\n\nexport type CompilerSync = (source: string, path: string) => string;\nexport type CompilerAsync = (source: string, path: string) => Promise<string>;\n\nexport type RawSyncCompilers = Map<FileExtension, CompilerSync | true>;\nexport type SyncCompilers = Map<FileExtension, CompilerSync>;\nexport type AsyncCompilers = Map<FileExtension, CompilerAsync>;\nexport type Compilers = [SyncCompilers, AsyncCompilers];\n\nexport type HasDependency = (pkgName: string) => boolean;\n"
  },
  {
    "path": "packages/knip/src/constants.ts",
    "content": "export const ROOT_WORKSPACE_NAME = '.';\n\nexport const IMPORT_STAR = '*';\n\nexport const KNIP_CONFIG_LOCATIONS = [\n  'knip.json',\n  'knip.jsonc',\n  '.knip.json',\n  '.knip.jsonc',\n  'knip.ts',\n  'knip.js',\n  'knip.config.ts',\n  'knip.config.js',\n];\n\nexport const DEFAULT_EXTENSIONS = new Set(['.js', '.mjs', '.cjs', '.jsx', '.ts', '.tsx', '.mts', '.cts']);\n\nexport const DTS_EXTENSIONS = ['.d.ts', '.d.mts', '.d.cts'];\n\nexport const IS_DTS = /\\.d\\.(c|m)?ts$/;\n\nexport const GLOBAL_IGNORE_PATTERNS: readonly string[] = ['**/node_modules/**', '.yarn', '.git'];\n\nexport const PUBLIC_TAG = '@public';\nexport const INTERNAL_TAG = '@internal';\nexport const BETA_TAG = '@beta';\nexport const ALIAS_TAG = '@alias';\n\nexport const DT_SCOPE = '@types';\n\nexport const PROTOCOL_VIRTUAL = 'virtual:';\n\n/**\n * Binaries that are expected to be globally available. The package at\n * https://www.npmjs.com/package/[name] might exist, but is not expected to be\n * listed in package.json because last npm publish was >6 years ago OR weekly downloads <5_000\n */\nexport const IGNORED_GLOBAL_BINARIES = new Set([\n  'amplify',\n  'aws',\n  'base64',\n  'basename',\n  'bash',\n  'bun',\n  'bundle',\n  'bunx',\n  'cargo',\n  'cat',\n  'cd',\n  'chmod',\n  'chown',\n  'cksum',\n  'clear',\n  'cmd',\n  'comm',\n  'command',\n  'corepack',\n  'cp',\n  'curl',\n  'cut',\n  'date',\n  'deno',\n  'df',\n  'dir',\n  'dirname',\n  'docker',\n  'echo',\n  'env',\n  'exec',\n  'exit',\n  'expand',\n  'export',\n  'expr',\n  'factor',\n  'false',\n  'find',\n  'gem',\n  'git',\n  'grep',\n  'groups',\n  'gzip',\n  'head',\n  'id',\n  'join',\n  'kill',\n  'ln',\n  'logname',\n  'ls',\n  'md5sum',\n  'mkdir',\n  'mknod',\n  'mv',\n  'nice',\n  'nl',\n  'node',\n  'nohup',\n  'npm',\n  'nproc',\n  'npx',\n  'paste',\n  'pnpm',\n  'pnpx',\n  'pr',\n  'printenv',\n  'pwd',\n  'rm',\n  'rmdir',\n  'rsync',\n  'scp',\n  'sed',\n  'seq',\n  'set',\n  'sh',\n  'sha1sum',\n  'sha512sum',\n  'shred',\n  'shuf',\n  'sort',\n  'split',\n  'ssh',\n  'stat',\n  'stty',\n  'sudo',\n  'sync',\n  'tac',\n  'tee',\n  'test', // exception (node built-in module)\n  'time',\n  'timeout',\n  'touch',\n  'tr',\n  'true',\n  'tsort',\n  'tty',\n  'uname',\n  'unexpand',\n  'uniq',\n  'unzip',\n  'wc',\n  'who',\n  'whoami',\n  'xargs',\n  'xcodebuild',\n  'xvfb-run',\n  'yarn',\n  'yes',\n  'zip',\n]);\n\nexport const IGNORED_DEPENDENCIES = new Set(['knip', 'typescript']);\n\nexport const IGNORED_RUNTIME_DEPENDENCIES = new Set(['node', 'bun', 'deno']);\n\nexport const FOREIGN_FILE_EXTENSIONS = new Set([\n  '.avif',\n  '.css',\n  '.eot',\n  '.gif',\n  '.html',\n  '.ico',\n  '.jpeg',\n  '.jpg',\n  '.less',\n  '.mp3',\n  '.png',\n  '.sass',\n  '.scss',\n  '.sh',\n  '.svg',\n  '.ttf',\n  '.webp',\n  '.woff',\n  '.woff2',\n  '.yaml',\n  '.yml',\n]);\n\nexport const IGNORE_DEFINITELY_TYPED = new Set([\n  // The `@types/node` dependency does not require the `node` dependency\n  'node',\n  'bun',\n  // Packages that confusingly include `package.json#types` but also recommend to install DT pkg\n  'jest',\n]);\n\nexport const ISSUE_TYPES = [\n  'files',\n  'dependencies',\n  'devDependencies',\n  'optionalPeerDependencies',\n  'unlisted',\n  'binaries',\n  'unresolved',\n  'exports',\n  'nsExports',\n  'types',\n  'nsTypes',\n  'enumMembers',\n  'namespaceMembers',\n  'duplicates',\n  'catalog',\n] as const;\n\nexport const ISSUE_TYPE_TITLE = {\n  files: 'Unused files',\n  dependencies: 'Unused dependencies',\n  devDependencies: 'Unused devDependencies',\n  optionalPeerDependencies: 'Referenced optional peerDependencies',\n  unlisted: 'Unlisted dependencies',\n  binaries: 'Unlisted binaries',\n  unresolved: 'Unresolved imports',\n  exports: 'Unused exports',\n  nsExports: 'Exports in used namespace',\n  types: 'Unused exported types',\n  nsTypes: 'Exported types in used namespace',\n  enumMembers: 'Unused exported enum members',\n  namespaceMembers: 'Unused exported namespace members',\n  duplicates: 'Duplicate exports',\n  catalog: 'Unused catalog entries',\n} as const;\n\nexport const SYMBOL_TYPE = {\n  CLASS: 'class',\n  ENUM: 'enum',\n  FUNCTION: 'function',\n  INTERFACE: 'interface',\n  MEMBER: 'member',\n  NAMESPACE: 'namespace',\n  TYPE: 'type',\n  UNKNOWN: 'unknown',\n  VARIABLE: 'variable',\n} as const;\n\nexport const FIX_FLAGS = {\n  NONE: 0,\n  OBJECT_BINDING: 1 << 0, // remove next comma\n  EMPTY_DECLARATION: 1 << 1, // remove declaration if empty\n  WITH_NEWLINE: 1 << 2, // remove with newline\n} as const;\n\nexport const SIDE_EFFECTS = '__side-effects';\n\nexport const OPAQUE = '__opaque';\n\nexport const IMPORT_FLAGS = {\n  NONE: 0,\n  RE_EXPORT: 1 << 0,\n  TYPE_ONLY: 1 << 1,\n  ENTRY: 1 << 2, // entry path, ignore exports\n  BRIDGE: 1 << 3, // add require() target in ts module to program\n  OPTIONAL: 1 << 4, // no error if not resolved\n  SIDE_EFFECTS: 1 << 5,\n  OPAQUE: 1 << 6,\n} as const;\n"
  },
  {
    "path": "packages/knip/src/graph/analyze.ts",
    "content": "import type { CatalogCounselor } from '../CatalogCounselor.ts';\nimport type { ConfigurationChief } from '../ConfigurationChief.ts';\nimport type { ConsoleStreamer } from '../ConsoleStreamer.ts';\nimport type { DependencyDeputy } from '../DependencyDeputy.ts';\nimport { createGraphExplorer } from '../graph-explorer/explorer.ts';\nimport { getIssueType, hasStrictlyEnumReferences } from '../graph-explorer/utils.ts';\nimport type { IssueCollector } from '../IssueCollector.ts';\nimport traceReporter from '../reporters/trace.ts';\nimport type { Export, ModuleGraph } from '../types/module-graph.ts';\nimport type { MainOptions } from '../util/create-options.ts';\nimport { getPackageNameFromModuleSpecifier } from '../util/modules.ts';\nimport { perfObserver } from '../util/Performance.ts';\nimport { findMatch } from '../util/regex.ts';\nimport { getShouldIgnoreHandler, getShouldIgnoreTagHandler } from '../util/tag.ts';\n\ninterface AnalyzeOptions {\n  analyzedFiles: Set<string>;\n  counselor: CatalogCounselor;\n  chief: ConfigurationChief;\n  collector: IssueCollector;\n  deputy: DependencyDeputy;\n  entryPaths: Set<string>;\n  graph: ModuleGraph;\n  streamer: ConsoleStreamer;\n  unreferencedFiles: Set<string>;\n  options: MainOptions;\n}\n\nexport const analyze = async ({\n  analyzedFiles,\n  counselor,\n  chief,\n  collector,\n  deputy,\n  entryPaths,\n  graph,\n  streamer,\n  unreferencedFiles,\n  options,\n}: AnalyzeOptions) => {\n  const shouldIgnore = getShouldIgnoreHandler(options.isProduction);\n  const shouldIgnoreTags = getShouldIgnoreTagHandler(options.tags);\n\n  const explorer = createGraphExplorer(graph, entryPaths);\n\n  const isReferencedInUsedExport = (\n    exportedItem: Export,\n    filePath: string,\n    includeEntryExports: boolean,\n    visited?: Set<string>\n  ) => {\n    if (!exportedItem.referencedIn) return false;\n    const file = graph.get(filePath);\n    if (!file) return false;\n    for (const containingExport of exportedItem.referencedIn) {\n      if (explorer.isReferenced(filePath, containingExport, { includeEntryExports })[0]) return true;\n      const inExport = file.exports.get(containingExport);\n      if (!inExport) continue;\n      if (inExport.hasRefsInFile && (inExport.type === 'type' || inExport.type === 'interface')) return true;\n      if (inExport.referencedIn) {\n        const v = visited ?? new Set();\n        if (!v.has(containingExport)) {\n          v.add(containingExport);\n          if (isReferencedInUsedExport(inExport, filePath, includeEntryExports, v)) return true;\n        }\n      }\n    }\n    return false;\n  };\n\n  const analyzeGraph = async () => {\n    if (options.isReportValues || options.isReportTypes) {\n      streamer.cast('Connecting the dots');\n\n      for (const [filePath, file] of graph) {\n        const exportItems = file.exports;\n\n        if (!exportItems || exportItems.size === 0) continue;\n\n        const workspace = chief.findWorkspaceByFilePath(filePath);\n\n        if (workspace) {\n          const { isIncludeEntryExports } = workspace.config;\n\n          const isEntry = entryPaths.has(filePath);\n\n          // Bail out when in entry file (unless `isIncludeEntryExports`)\n          if (!isIncludeEntryExports && isEntry) {\n            continue;\n          }\n\n          const importsForExport = file.importedBy;\n\n          for (const [identifier, exportedItem] of exportItems) {\n            // Skip tagged exports\n            if (shouldIgnore(exportedItem.jsDocTags)) continue;\n\n            const isIgnored = shouldIgnoreTags(exportedItem.jsDocTags);\n\n            if (importsForExport) {\n              const [isReferenced, reExportingEntryFile] = explorer.isReferenced(filePath, identifier, {\n                includeEntryExports: isIncludeEntryExports,\n              });\n\n              if (\n                isIgnored &&\n                (isReferenced || isReferencedInUsedExport(exportedItem, filePath, isIncludeEntryExports))\n              ) {\n                for (const tagName of exportedItem.jsDocTags) {\n                  if (options.tags[1].includes(tagName.replace(/^@/, ''))) {\n                    collector.addTagHint({ type: 'tag', filePath, identifier, tagName });\n                  }\n                }\n              }\n\n              if (isIgnored) continue;\n\n              if (reExportingEntryFile && !isReferenced) {\n                if (!isIncludeEntryExports) {\n                  continue;\n                }\n                // Skip exports if re-exported from entry file and tagged\n                const reExportedItem = graph.get(reExportingEntryFile)?.exports.get(identifier);\n                if (reExportedItem && shouldIgnore(reExportedItem.jsDocTags)) continue;\n              }\n\n              if (isReferenced) {\n                const isEnumMembers = options.includedIssueTypes.enumMembers && exportedItem.type === 'enum';\n                const isNsMembers =\n                  options.includedIssueTypes.namespaceMembers &&\n                  exportedItem.members.length > 0 &&\n                  exportedItem.type !== 'enum';\n\n                if ((isEnumMembers || isNsMembers) && exportedItem.members.length > 0) {\n                  if (!options.includedIssueTypes.nsTypes && importsForExport.refs.has(identifier)) continue;\n                  if (isEnumMembers && hasStrictlyEnumReferences(importsForExport, identifier)) continue;\n\n                  const issueType = isEnumMembers ? 'enumMembers' : 'namespaceMembers';\n\n                  for (const member of exportedItem.members) {\n                    if (findMatch(workspace.ignoreMembers, member.identifier)) continue;\n                    if (shouldIgnore(member.jsDocTags)) continue;\n\n                    if (!member.hasRefsInFile) {\n                      const id = `${identifier}.${member.identifier}`;\n                      const [isMemberReferenced] = explorer.isReferenced(filePath, id, {\n                        includeEntryExports: true,\n                      });\n                      const isIgnored = shouldIgnoreTags(member.jsDocTags);\n\n                      if (!isMemberReferenced) {\n                        if (isIgnored) continue;\n\n                        collector.addIssue({\n                          type: issueType,\n                          filePath,\n                          workspace: workspace.name,\n                          symbol: member.identifier,\n                          parentSymbol: identifier,\n                          pos: member.pos,\n                          line: member.line,\n                          col: member.col,\n                          fixes: member.fix ? [member.fix] : [],\n                        });\n                      } else if (isIgnored) {\n                        for (const tagName of exportedItem.jsDocTags) {\n                          if (options.tags[1].includes(tagName.replace(/^@/, ''))) {\n                            collector.addTagHint({ type: 'tag', filePath, identifier: id, tagName });\n                          }\n                        }\n                      }\n                    }\n                  }\n                }\n\n                // This id was imported, so we bail out early\n                continue;\n              }\n            }\n\n            const [hasStrictlyNsRefs, namespace] = explorer.hasStrictlyNsReferences(filePath, identifier);\n\n            const isType = ['enum', 'type', 'interface'].includes(exportedItem.type);\n\n            if (\n              isIgnored ||\n              exportedItem.hasRefsInFile ||\n              isReferencedInUsedExport(exportedItem, filePath, isIncludeEntryExports) ||\n              (hasStrictlyNsRefs &&\n                ((!options.includedIssueTypes.nsTypes && isType) || !(options.includedIssueTypes.nsExports || isType)))\n            ) {\n              continue;\n            }\n\n            const type = getIssueType(hasStrictlyNsRefs, isType);\n            collector.addIssue({\n              type,\n              filePath,\n              workspace: workspace.name,\n              symbol: identifier,\n              symbolType: exportedItem.type,\n              parentSymbol: namespace,\n              pos: exportedItem.pos,\n              line: exportedItem.line,\n              col: exportedItem.col,\n              fixes: exportedItem.fixes,\n            });\n          }\n        }\n      }\n    }\n\n    for (const [filePath, file] of graph) {\n      const ws = chief.findWorkspaceByFilePath(filePath);\n\n      if (ws) {\n        if (file.duplicates && options.includedIssueTypes.duplicates) {\n          for (const symbols of file.duplicates) {\n            if (symbols.length > 1) {\n              const symbol = symbols.map(s => s.symbol).join('|');\n              collector.addIssue({ type: 'duplicates', filePath, workspace: ws.name, symbol, symbols, fixes: [] });\n            }\n          }\n        }\n\n        if (file.imports?.external) {\n          for (const extImport of file.imports.external) {\n            const packageName = getPackageNameFromModuleSpecifier(extImport.specifier);\n            const isHandled = packageName && deputy.maybeAddReferencedExternalDependency(ws, packageName);\n            if (!isHandled)\n              collector.addIssue({\n                type: 'unlisted',\n                filePath,\n                workspace: ws.name,\n                symbol: packageName ?? extImport.specifier,\n                specifier: extImport.specifier,\n                pos: extImport.pos,\n                line: extImport.line,\n                col: extImport.col,\n                fixes: [],\n              });\n          }\n        }\n\n        if (file.imports?.unresolved) {\n          for (const unresolvedImport of file.imports.unresolved) {\n            const { specifier, pos, line, col } = unresolvedImport;\n            collector.addIssue({\n              type: 'unresolved',\n              filePath,\n              workspace: ws.name,\n              symbol: specifier,\n              pos,\n              line,\n              col,\n              fixes: [],\n            });\n          }\n        }\n      }\n    }\n\n    const unusedFiles = options.isReportFiles\n      ? [...unreferencedFiles].filter(filePath => !analyzedFiles.has(filePath))\n      : [];\n\n    if (options.isReportFiles) collector.addFilesIssues(unusedFiles);\n\n    collector.addFileCounts({ processed: analyzedFiles.size, unused: unusedFiles.length });\n\n    if (options.isReportDependencies) {\n      const { dependencyIssues, devDependencyIssues, optionalPeerDependencyIssues } = deputy.settleDependencyIssues();\n      for (const issue of dependencyIssues) collector.addIssue(issue);\n      if (!options.isProduction) for (const issue of devDependencyIssues) collector.addIssue(issue);\n      for (const issue of optionalPeerDependencyIssues) collector.addIssue(issue);\n\n      deputy.removeIgnoredIssues(collector.getIssues());\n\n      const configurationHints = deputy.getConfigurationHints();\n      for (const hint of configurationHints) collector.addConfigurationHint(hint);\n    }\n\n    const catalogIssues = await counselor.settleCatalogIssues(options);\n    for (const issue of catalogIssues) collector.addIssue(issue);\n\n    const unusedIgnoredWorkspaces = chief.getUnusedIgnoredWorkspaces();\n    for (const identifier of unusedIgnoredWorkspaces) {\n      collector.addConfigurationHint({ type: 'ignoreWorkspaces', identifier });\n    }\n\n    for (const hint of collector.getUnusedIgnorePatternHints(options)) {\n      collector.addConfigurationHint(hint);\n    }\n\n    for (const hint of chief.getConfigurationHints()) collector.addConfigurationHint(hint);\n  };\n\n  await analyzeGraph();\n\n  perfObserver.addMemoryMark('analyze');\n\n  if (options.isTrace) {\n    traceReporter({ graph, explorer, options, workspaceFilePathFilter: chief.workspaceFilePathFilter });\n  }\n\n  return analyzeGraph;\n};\n"
  },
  {
    "path": "packages/knip/src/graph/build.ts",
    "content": "import { _getInputsFromScripts } from '../binaries/index.ts';\nimport type { CatalogCounselor } from '../CatalogCounselor.ts';\nimport type { ConfigurationChief, Workspace } from '../ConfigurationChief.ts';\nimport type { ConsoleStreamer } from '../ConsoleStreamer.ts';\nimport { getCompilerExtensions, getIncludedCompilers, normalizeCompilerExtension } from '../compilers/index.ts';\nimport { DEFAULT_EXTENSIONS, FOREIGN_FILE_EXTENSIONS, IS_DTS } from '../constants.ts';\nimport type { DependencyDeputy } from '../DependencyDeputy.ts';\nimport type { IssueCollector } from '../IssueCollector.ts';\nimport type { ProjectPrincipal } from '../ProjectPrincipal.ts';\nimport type { GetImportsAndExportsOptions, RegisterCompiler } from '../types/config.ts';\nimport type { Issue } from '../types/issues.ts';\nimport type { Import, ModuleGraph } from '../types/module-graph.ts';\nimport type { PluginName } from '../types/PluginNames.ts';\nimport { partition } from '../util/array.ts';\nimport { createInputHandler, type ExternalRefsFromInputs } from '../util/create-input-handler.ts';\nimport type { MainOptions } from '../util/create-options.ts';\nimport { debugLog, debugLogArray } from '../util/debug.ts';\nimport { existsSync } from 'node:fs';\nimport { _glob, _syncGlob, negate, prependDirToPattern as prependDir } from '../util/glob.ts';\nimport {\n  type Input,\n  isAlias,\n  isConfig,\n  isDeferResolveEntry,\n  isDeferResolveProductionEntry,\n  isEntry,\n  isIgnore,\n  isProductionEntry,\n  isProject,\n  toProductionEntry,\n} from '../util/input.ts';\nimport { loadTSConfig } from '../util/load-tsconfig.ts';\nimport { createFileNode, updateImportMap } from '../util/module-graph.ts';\nimport { getPackageNameFromModuleSpecifier, isStartsLikePackageName, sanitizeSpecifier } from '../util/modules.ts';\nimport { perfObserver } from '../util/Performance.ts';\nimport { getEntrySpecifiersFromManifest, getManifestImportDependencies } from '../util/package-json.ts';\nimport { dirname, extname, isAbsolute, isInNodeModules, join, relative } from '../util/path.ts';\nimport { augmentWorkspace, getToSourcePathsHandler } from '../util/to-source-path.ts';\nimport { WorkspaceWorker } from '../WorkspaceWorker.ts';\n\ninterface BuildOptions {\n  chief: ConfigurationChief;\n  collector: IssueCollector;\n  counselor: CatalogCounselor;\n  deputy: DependencyDeputy;\n  principal: ProjectPrincipal;\n  isGitIgnored: (path: string) => boolean;\n  streamer: ConsoleStreamer;\n  workspaces: Workspace[];\n  options: MainOptions;\n}\n\nexport async function build({\n  chief,\n  collector,\n  counselor,\n  deputy,\n  principal,\n  isGitIgnored,\n  streamer,\n  workspaces,\n  options,\n}: BuildOptions) {\n  const configFilesMap = new Map<string, Map<PluginName, Set<string>>>();\n\n  const enabledPluginsStore = new Map<string, string[]>();\n  const registeredVisitorPlugins = new Set<string>();\n\n  const toSourceFilePaths = getToSourcePathsHandler(chief);\n\n  const addIssue = (issue: Issue) => collector.addIssue(issue) && options.isWatch && collector.retainIssue(issue);\n\n  const externalRefsFromInputs: ExternalRefsFromInputs | undefined = options.isSession ? new Map() : undefined;\n\n  const handleInput = createInputHandler(deputy, chief, isGitIgnored, addIssue, externalRefsFromInputs, options);\n\n  const rootManifest = chief.getManifestForWorkspace('.');\n\n  for (const workspace of workspaces) {\n    const { name, dir, manifestPath, manifestStr } = workspace;\n    const manifest = chief.getManifestForWorkspace(name);\n    if (!manifest) continue;\n\n    deputy.addWorkspace({\n      name,\n      cwd: options.cwd,\n      dir,\n      manifestPath,\n      manifestStr,\n      manifest,\n      ...chief.getIgnores(name),\n    });\n\n    counselor.addWorkspace(manifest);\n  }\n\n  collector.addIgnorePatterns(chief.config.ignore.map(id => ({ pattern: prependDir(options.cwd, id), id })));\n  collector.addIgnoreFilesPatterns(chief.config.ignoreFiles.map(id => ({ pattern: prependDir(options.cwd, id), id })));\n\n  if (options.configFilePath) {\n    principal.addEntryPath(options.configFilePath, { skipExportsAnalysis: true });\n  }\n\n  for (const workspace of workspaces) {\n    const { name, dir, ancestors, manifestPath: filePath } = workspace;\n\n    streamer.cast('Analyzing workspace', name);\n\n    const manifest = chief.getManifestForWorkspace(name);\n    if (!manifest) continue;\n\n    const dependencies = deputy.getDependencies(name);\n    const baseConfig = chief.getConfigForWorkspace(name);\n\n    const tsConfigFilePath = join(dir, options.tsConfigFile ?? 'tsconfig.json');\n    const { isFile, compilerOptions, fileNames } = await loadTSConfig(tsConfigFilePath);\n    const [definitionPaths, tscSourcePaths] = partition(fileNames, filePath => IS_DTS.test(filePath));\n\n    if (isFile) augmentWorkspace(workspace, dir, compilerOptions);\n\n    const worker = new WorkspaceWorker({\n      name,\n      dir,\n      config: baseConfig,\n      manifest,\n      dependencies,\n      rootManifest,\n      handleInput: (input: Input) => handleInput(input, workspace),\n      findWorkspaceByFilePath: chief.findWorkspaceByFilePath.bind(chief),\n      negatedWorkspacePatterns: chief.getNegatedWorkspacePatterns(name),\n      ignoredWorkspacePatterns: chief.getIgnoredWorkspacesFor(name),\n      enabledPluginsInAncestors: ancestors.flatMap(ancestor => enabledPluginsStore.get(ancestor) ?? []),\n      readFile: (filePath: string) => principal.readFile(filePath),\n      configFilesMap,\n      options,\n    });\n\n    await worker.init();\n\n    const compilers = getIncludedCompilers(chief.config.syncCompilers, chief.config.asyncCompilers, dependencies);\n    const registerCompiler: RegisterCompiler = async ({ extension, compiler }) => {\n      const ext = normalizeCompilerExtension(extension);\n      if (compilers[0].has(ext)) return;\n      compilers[0].set(ext, compiler);\n    };\n\n    await worker.registerCompilers(registerCompiler);\n\n    principal.addCompilers(compilers);\n\n    const extensions = getCompilerExtensions(compilers);\n    const extensionGlobStr = `.{${[...DEFAULT_EXTENSIONS, ...extensions].map(ext => ext.slice(1)).join(',')}}`;\n    const config = chief.getConfigForWorkspace(name, extensions);\n    worker.config = config;\n\n    const inputs = new Set<Input>();\n\n    if (definitionPaths.length > 0) {\n      debugLogArray(name, 'Definition paths', definitionPaths);\n      for (const id of definitionPaths) inputs.add(toProductionEntry(id, { containingFilePath: tsConfigFilePath }));\n    }\n\n    const sharedGlobOptions = { cwd: options.cwd, dir, gitignore: options.gitignore };\n\n    const fn = (id: string) => ({ pattern: prependDir(options.cwd, prependDir(name, id)), id, workspaceName: name });\n    collector.addIgnorePatterns(config.ignore.map(fn));\n    collector.addIgnoreFilesPatterns(config.ignoreFiles.map(fn));\n\n    const entrySpecifiersFromManifest = getEntrySpecifiersFromManifest(manifest);\n    const label = 'entry paths from package.json';\n    for (const filePath of await toSourceFilePaths(entrySpecifiersFromManifest, dir, extensionGlobStr, label)) {\n      inputs.add(toProductionEntry(filePath));\n    }\n\n    for (const identifier of entrySpecifiersFromManifest) {\n      if (!identifier.startsWith('!') && !isGitIgnored(join(dir, identifier))) {\n        const exists = identifier.includes('*')\n          ? _syncGlob({ patterns: [identifier], cwd: dir }).length > 0\n          : existsSync(join(dir, identifier));\n        if (!exists) {\n          collector.addConfigurationHint({ type: 'package-entry', filePath, identifier, workspaceName: name });\n        }\n      }\n    }\n\n    for (const dep of getManifestImportDependencies(manifest)) deputy.addReferencedDependency(name, dep);\n\n    principal.addPaths(config.paths, dir);\n\n    const inputsFromPlugins = await worker.runPlugins();\n    for (const id of inputsFromPlugins) inputs.add(Object.assign(id, { skipExportsAnalysis: !id.allowIncludeExports }));\n    enabledPluginsStore.set(name, worker.enabledPlugins);\n\n    worker.registerVisitors({\n      ctx: principal.pluginCtx,\n      registerVisitor: visitors => principal.pluginVisitorObjects.push(visitors),\n      registeredPlugins: registeredVisitorPlugins,\n    });\n\n    const DEFAULT_GROUP = 'default';\n    type PatternMap = Map<string, Set<string>>;\n    const createPatternMap = (): PatternMap => new Map([[DEFAULT_GROUP, new Set()]]);\n\n    const groups = new Set([DEFAULT_GROUP]);\n    const entryPatterns = createPatternMap();\n    const entryPatternsSkipExports = createPatternMap();\n    const productionPatterns = createPatternMap();\n    const productionPatternsSkipExports = createPatternMap();\n    const projectFilePatterns = new Set<string>();\n\n    const addPattern = (map: PatternMap, input: Input, pattern: string) => {\n      if (input.group && !map.has(input.group)) map.set(input.group, new Set());\n      // oxlint-disable-next-line @typescript-eslint/no-non-null-assertion\n      map.get(input.group ?? DEFAULT_GROUP)!.add(pattern);\n    };\n\n    const toWorkspaceRelative = (path: string) => (isAbsolute(path) ? relative(dir, path) : path);\n\n    for (const input of inputs) {\n      if (input.group) groups.add(input.group);\n      const specifier = input.specifier;\n      if (isEntry(input)) {\n        const targetMap = input.skipExportsAnalysis ? entryPatternsSkipExports : entryPatterns;\n        addPattern(targetMap, input, toWorkspaceRelative(specifier));\n      } else if (isProductionEntry(input)) {\n        const targetMap = input.skipExportsAnalysis ? productionPatternsSkipExports : productionPatterns;\n        addPattern(targetMap, input, toWorkspaceRelative(specifier));\n      } else if (isProject(input)) {\n        projectFilePatterns.add(toWorkspaceRelative(specifier));\n      } else if (isAlias(input)) {\n        principal.addPaths({ [input.specifier]: input.prefixes }, input.dir ?? dir);\n      } else if (isIgnore(input)) {\n        if (input.issueType === 'dependencies' || input.issueType === 'unlisted') {\n          deputy.addIgnoredDependencies(name, input.specifier);\n        } else if (input.issueType === 'binaries') {\n          deputy.addIgnoredBinaries(name, input.specifier);\n        } else if (input.issueType === 'unresolved') {\n          deputy.addIgnoredUnresolved(name, input.specifier);\n        }\n      } else if (!isConfig(input)) {\n        const ws = (input.containingFilePath && chief.findWorkspaceByFilePath(input.containingFilePath)) || workspace;\n        const resolvedFilePath = handleInput(input, ws);\n        if (resolvedFilePath) {\n          if (isDeferResolveProductionEntry(input)) {\n            addPattern(productionPatternsSkipExports, input, resolvedFilePath);\n          } else if (isDeferResolveEntry(input)) {\n            if (!options.isProduction || !input.optional) addPattern(entryPatternsSkipExports, input, resolvedFilePath);\n          } else {\n            principal.addEntryPath(resolvedFilePath, { skipExportsAnalysis: true });\n          }\n        }\n      }\n    }\n\n    const negatedEntryPatterns: string[] = [];\n    if (options.isProduction) {\n      for (const map of [entryPatterns, entryPatternsSkipExports]) {\n        for (const patterns of map.values()) for (const pattern of patterns) negatedEntryPatterns.push(negate(pattern));\n      }\n    }\n\n    const userEntryPatterns = options.isProduction\n      ? worker.getProductionEntryFilePatterns(negatedEntryPatterns)\n      : worker.getEntryFilePatterns();\n    const userEntryPaths = await _glob({\n      ...sharedGlobOptions,\n      patterns: userEntryPatterns,\n      gitignore: false,\n      label: 'entry paths',\n    });\n\n    for (const group of groups) {\n      {\n        const patterns = worker.getPluginEntryFilePatterns([\n          ...((!options.isProduction && entryPatterns.get(group)) || []),\n          ...((!options.isProduction && group === DEFAULT_GROUP && worker.getPluginConfigPatterns()) || []),\n          ...(productionPatterns.get(group) ?? []),\n        ]);\n        const label = `entry paths from plugins${group !== DEFAULT_GROUP ? ` - ${group}` : ''}`;\n        const pluginWorkspaceEntryPaths = await _glob({ ...sharedGlobOptions, patterns, label });\n        principal.addEntryPaths(pluginWorkspaceEntryPaths);\n      }\n\n      {\n        const patterns = worker.getPluginEntryFilePatterns([\n          ...((!options.isProduction && entryPatternsSkipExports.get(group)) || []),\n          ...(productionPatternsSkipExports.get(group) ?? []),\n        ]);\n        const label = `entry paths from plugins (ignore exports)${group !== DEFAULT_GROUP ? ` - ${group}` : ''}`;\n        const pluginWorkspaceEntryPaths = await _glob({ ...sharedGlobOptions, patterns, label });\n        principal.addEntryPaths(pluginWorkspaceEntryPaths, { skipExportsAnalysis: true });\n      }\n    }\n\n    if (!options.isProduction) {\n      const hints = worker.getConfigurationHints('entry', userEntryPatterns, userEntryPaths, principal.entryPaths);\n      for (const hint of hints) collector.addConfigurationHint(hint);\n    }\n\n    principal.addEntryPaths(userEntryPaths);\n\n    if (options.isUseTscFiles && isFile) {\n      const isIgnoredWorkspace = chief.createIgnoredWorkspaceMatcher(name, dir);\n      debugLogArray(name, 'Using tsconfig files as project files', tscSourcePaths);\n      for (const filePath of tscSourcePaths) {\n        if (!isGitIgnored(filePath) && !isIgnoredWorkspace(filePath)) {\n          principal.addProgramPath(filePath);\n          principal.addProjectPath(filePath);\n        }\n      }\n    } else {\n      const patterns = options.isProduction\n        ? worker.getProductionProjectFilePatterns(negatedEntryPatterns)\n        : worker.getProjectFilePatterns([\n            ...(productionPatternsSkipExports.get(DEFAULT_GROUP) ?? []),\n            ...projectFilePatterns,\n            ...worker.getPluginProjectFilePatterns(),\n          ]);\n      const projectPaths = await _glob({ ...sharedGlobOptions, patterns, label: 'project paths' });\n\n      if (!options.isProduction) {\n        const hints = worker.getConfigurationHints('project', config.project, projectPaths, principal.projectPaths);\n        for (const hint of hints) collector.addConfigurationHint(hint);\n      }\n\n      for (const projectPath of projectPaths) principal.addProjectPath(projectPath);\n    }\n\n    worker.onDispose();\n    perfObserver.addMemoryMark(name);\n  }\n\n  debugLog('*', `Created 1 principal for ${workspaces.length} workspaces`);\n\n  const graph: ModuleGraph = new Map();\n  const analyzedFiles = new Set<string>();\n  const unreferencedFiles = new Set<string>();\n  const entryPaths = new Set<string>();\n\n  const isInternalWorkspace = (packageName: string) => chief.availableWorkspacePkgNames.has(packageName);\n\n  const analyzeOpts: GetImportsAndExportsOptions = {\n    isFixExports: options.isFixUnusedExports,\n    isFixTypes: options.isFixUnusedTypes,\n    isReportExports: options.isReportExports,\n    skipTypeOnly: options.isStrict,\n    tags: options.tags,\n  };\n\n  const analyzeSourceFile = (\n    filePath: string,\n    pp: ProjectPrincipal,\n    parseResult?: import('oxc-parser').ParseResult,\n    sourceText?: string\n  ) => {\n    if (!options.isWatch && !options.isSession && analyzedFiles.has(filePath)) return;\n    analyzedFiles.add(filePath);\n\n    const workspace = chief.findWorkspaceByFilePath(filePath);\n\n    if (workspace) {\n      const file = pp.analyzeSourceFile(\n        filePath,\n        analyzeOpts,\n        chief.config.ignoreExportsUsedInFile,\n        parseResult,\n        sourceText\n      );\n\n      const unresolvedImports = new Set<Import>();\n      for (const unresolvedImport of file.imports.unresolved) {\n        const { specifier } = unresolvedImport;\n\n        if (specifier.startsWith('http')) continue;\n\n        const sanitizedSpecifier = sanitizeSpecifier(specifier);\n        if (isStartsLikePackageName(sanitizedSpecifier)) {\n          file.imports.external.add({ ...unresolvedImport, specifier: sanitizedSpecifier });\n        } else {\n          if (!isGitIgnored(join(dirname(filePath), sanitizedSpecifier))) {\n            const ext = extname(sanitizedSpecifier);\n            if (!ext || (ext !== '.json' && !FOREIGN_FILE_EXTENSIONS.has(ext))) unresolvedImports.add(unresolvedImport);\n          }\n        }\n      }\n\n      for (const filePath of file.imports.programFiles) {\n        const isIgnored = isGitIgnored(filePath);\n        if (!isIgnored) pp.addProgramPath(filePath);\n      }\n\n      for (const filePath of file.imports.entryFiles) {\n        const isIgnored = isGitIgnored(filePath);\n        if (!isIgnored) pp.addEntryPath(filePath, { skipExportsAnalysis: true });\n      }\n\n      for (const _import of file.imports.imports) {\n        if (_import.filePath) {\n          const packageName = getPackageNameFromModuleSpecifier(_import.specifier);\n          if (packageName && isInternalWorkspace(packageName)) {\n            file.imports.external.add({ ..._import, specifier: packageName });\n            if (!isGitIgnored(_import.filePath)) {\n              pp.addProgramPath(_import.filePath);\n            }\n          }\n        }\n      }\n\n      if (file.scripts && file.scripts.size > 0) {\n        const dependencies = deputy.getDependencies(workspace.name);\n        const manifestScriptNames = new Set(Object.keys(chief.getManifestForWorkspace(workspace.name)?.scripts ?? {}));\n        const dir = dirname(filePath);\n        const opts = {\n          cwd: dir,\n          rootCwd: options.cwd,\n          containingFilePath: filePath,\n          dependencies,\n          manifestScriptNames,\n          rootManifest,\n        };\n        const inputs = _getInputsFromScripts(file.scripts, opts);\n        for (const input of inputs) {\n          input.containingFilePath ??= filePath;\n          input.dir ??= dir;\n          const specifierFilePath = handleInput(input, workspace);\n          if (specifierFilePath) pp.addEntryPath(specifierFilePath, { skipExportsAnalysis: true });\n        }\n      }\n\n      file.imports.unresolved = unresolvedImports;\n\n      const pluginRefs = externalRefsFromInputs?.get(filePath);\n      if (pluginRefs) for (const ref of pluginRefs) file.imports.externalRefs.add(ref);\n\n      const node = graph.get(filePath);\n      if (node) {\n        node.imports = file.imports;\n        node.exports = file.exports;\n        node.duplicates = file.duplicates;\n        node.scripts = file.scripts;\n        updateImportMap(node, file.imports.internal, graph);\n        node.internalImportCache = file.imports.internal;\n      } else {\n        updateImportMap(file, file.imports.internal, graph);\n        file.internalImportCache = file.imports.internal;\n        graph.set(filePath, file);\n      }\n    }\n  };\n\n  principal.init();\n\n  if (principal.asyncCompilers.size > 0) {\n    streamer.cast('Running async compilers');\n    await principal.runAsyncCompilers();\n  }\n\n  streamer.cast('Analyzing source files');\n\n  principal.walkAndAnalyze((filePath, parseResult, sourceText) => {\n    analyzeSourceFile(filePath, principal, parseResult, sourceText);\n    const node = graph.get(filePath);\n    if (!node) return;\n    const paths: string[] = [];\n    for (const importPath of node.imports.internal.keys()) {\n      if (!isInNodeModules(importPath)) paths.push(importPath);\n    }\n    return paths;\n  });\n\n  for (const filePath of principal.getUnreferencedFiles()) unreferencedFiles.add(filePath);\n  for (const filePath of principal.entryPaths) entryPaths.add(filePath);\n\n  principal.reconcileCache(graph);\n\n  perfObserver.addMemoryMark('build');\n\n  if (externalRefsFromInputs) {\n    for (const [filePath, refs] of externalRefsFromInputs) {\n      if (!graph.has(filePath)) graph.set(filePath, createFileNode());\n      // oxlint-disable-next-line @typescript-eslint/no-non-null-assertion\n      for (const ref of refs) graph.get(filePath)!.imports.externalRefs.add(ref);\n    }\n  }\n\n  return {\n    graph,\n    entryPaths,\n    analyzedFiles,\n    unreferencedFiles,\n    analyzeSourceFile,\n    enabledPluginsStore,\n  };\n}\n"
  },
  {
    "path": "packages/knip/src/graph-explorer/cache.ts",
    "content": "import type { Import, ModuleGraph } from '../types/module-graph.ts';\nimport type { UsageResult } from './operations/get-usage.ts';\nimport type { DefinitionResult } from './operations/resolve-definition.ts';\n\ninterface ExplorerCache {\n  definitions: Map<string, Map<string, DefinitionResult | null>>;\n  usage: Map<string, Map<string, UsageResult>>;\n  importLookup: Map<string, Map<string, Map<string, Import>>>;\n  exportedIdentifiers: Map<string, Map<string, boolean>>;\n  generation: number;\n}\n\nconst caches = new WeakMap<ModuleGraph, ExplorerCache>();\n\nconst createEmptyCache = (): ExplorerCache => ({\n  definitions: new Map(),\n  usage: new Map(),\n  importLookup: new Map(),\n  exportedIdentifiers: new Map(),\n  generation: 0,\n});\n\nconst getCache = (graph: ModuleGraph): ExplorerCache => {\n  let cache = caches.get(graph);\n  if (!cache) {\n    cache = createEmptyCache();\n    caches.set(graph, cache);\n  }\n  return cache;\n};\n\nexport const invalidateCache = (graph: ModuleGraph): void => {\n  const cache = caches.get(graph);\n  if (cache) {\n    cache.definitions.clear();\n    cache.usage.clear();\n    cache.importLookup.clear();\n    cache.exportedIdentifiers.clear();\n    cache.generation++;\n  }\n};\n\nexport const getCachedDefinition = (\n  graph: ModuleGraph,\n  filePath: string,\n  identifier: string\n): DefinitionResult | null | undefined => {\n  const cache = caches.get(graph);\n  if (!cache) return undefined;\n  const fileCache = cache.definitions.get(filePath);\n  if (!fileCache) return undefined;\n  return fileCache.has(identifier) ? fileCache.get(identifier) : undefined;\n};\n\nexport const setCachedDefinition = (\n  graph: ModuleGraph,\n  filePath: string,\n  identifier: string,\n  result: DefinitionResult | null\n): void => {\n  const cache = getCache(graph);\n  let fileCache = cache.definitions.get(filePath);\n  if (!fileCache) {\n    fileCache = new Map();\n    cache.definitions.set(filePath, fileCache);\n  }\n  fileCache.set(identifier, result);\n};\n\nexport const getCachedUsage = (graph: ModuleGraph, filePath: string, identifier: string): UsageResult | undefined => {\n  const cache = caches.get(graph);\n  if (!cache) return undefined;\n  const fileCache = cache.usage.get(filePath);\n  if (!fileCache) return undefined;\n  return fileCache.get(identifier);\n};\n\nexport const setCachedUsage = (graph: ModuleGraph, filePath: string, identifier: string, result: UsageResult): void => {\n  const cache = getCache(graph);\n  let fileCache = cache.usage.get(filePath);\n  if (!fileCache) {\n    fileCache = new Map();\n    cache.usage.set(filePath, fileCache);\n  }\n  fileCache.set(identifier, result);\n};\n\nexport const getCachedExportedIdentifiers = (\n  graph: ModuleGraph,\n  filePath: string\n): Map<string, boolean> | undefined => {\n  const cache = caches.get(graph);\n  if (!cache) return undefined;\n  return cache.exportedIdentifiers.get(filePath);\n};\n\nexport const setCachedExportedIdentifiers = (\n  graph: ModuleGraph,\n  filePath: string,\n  result: Map<string, boolean>\n): void => {\n  const cache = getCache(graph);\n  cache.exportedIdentifiers.set(filePath, result);\n};\n"
  },
  {
    "path": "packages/knip/src/graph-explorer/constants.ts",
    "content": "export const CONTINUE = 'continue';\n\nexport const STOP = 'stop';\n\nexport const RE_EXPORT_KIND = {\n  ALIAS: 'alias',\n  NAMESPACE: 'namespace',\n  PASSTHROUGH: 'passthrough',\n  SELF: 'self',\n  STAR: 'star',\n} as const;\n"
  },
  {
    "path": "packages/knip/src/graph-explorer/explorer.ts",
    "content": "import type { ModuleGraph } from '../types/module-graph.ts';\nimport { invalidateCache as invalidateCacheInternal } from './cache.ts';\nimport { buildExportsTree } from './operations/build-exports-tree.ts';\nimport { findCycles } from './operations/find-cycles.ts';\nimport { getContention } from './operations/get-contention.ts';\nimport { getDependencyUsage } from './operations/get-dependency-usage.ts';\nimport { getUsage } from './operations/get-usage.ts';\nimport { hasStrictlyNsReferences } from './operations/has-strictly-ns-references.ts';\nimport { isReferenced } from './operations/is-referenced.ts';\nimport { resolveDefinition } from './operations/resolve-definition.ts';\n\nexport const createGraphExplorer = (graph: ModuleGraph, entryPaths: Set<string>) => {\n  return {\n    /**\n     * Is exported `identifier` imported/referenced in the module graph?\n     * @returns `[isReferenced, reExportingEntryFile]` → [is export used, entry path if traversing through re-exports]\n     */\n    isReferenced: (filePath: string, identifier: string, options: { includeEntryExports: boolean }) =>\n      isReferenced(graph, entryPaths, filePath, identifier, options),\n    hasStrictlyNsReferences: (filePath: string, identifier: string) =>\n      hasStrictlyNsReferences(graph, filePath, graph.get(filePath)?.importedBy, identifier),\n    buildExportsTree: (options: { filePath?: string; identifier?: string }) =>\n      buildExportsTree(graph, entryPaths, options),\n    getDependencyUsage: (pattern?: string | RegExp) => getDependencyUsage(graph, pattern),\n    resolveDefinition: (filePath: string, identifier: string) => resolveDefinition(graph, filePath, identifier),\n    getUsage: (filePath: string, identifier: string) => getUsage(graph, entryPaths, filePath, identifier),\n    findCycles: (filePath: string, maxDepth?: number) => findCycles(graph, filePath, maxDepth),\n    getContention: (filePath: string) => getContention(graph, filePath),\n    invalidateCache: () => invalidateCacheInternal(graph),\n  };\n};\n\nexport type GraphExplorer = ReturnType<typeof createGraphExplorer>;\n"
  },
  {
    "path": "packages/knip/src/graph-explorer/operations/build-exports-tree.ts",
    "content": "import type { FileNode, Identifier, ImportMaps, ModuleGraph } from '../../types/module-graph.ts';\nimport { CONTINUE } from '../constants.ts';\nimport type { Via } from '../walk-down.ts';\nimport { walkDown } from '../walk-down.ts';\n\n/** @internal */\nexport interface ExportsTreeNode {\n  filePath: string;\n  identifier: string;\n  originalId: string | undefined;\n  via: Via | undefined;\n  refs: string[];\n  isEntry: boolean;\n  children: ExportsTreeNode[];\n}\n\nexport const buildExportsTree = (\n  graph: ModuleGraph,\n  entryPaths: Set<string>,\n  options: { filePath?: string; identifier?: Identifier }\n) => {\n  const traces: ExportsTreeNode[] = [];\n\n  const processFile = (filePath: string, file: FileNode) => {\n    for (const exportId of options.identifier ? [options.identifier] : file.exports.keys()) {\n      if (!options.identifier || file.exports.has(exportId)) {\n        const trace = buildExportTree(graph, entryPaths, filePath, exportId);\n        if (trace) traces.push(trace);\n      }\n    }\n  };\n\n  if (options.filePath) {\n    const file = graph.get(options.filePath);\n    if (file) processFile(options.filePath, file);\n  } else {\n    for (const [filePath, file] of graph) processFile(filePath, file);\n  }\n\n  return traces;\n};\n\nconst buildExportTree = (\n  graph: ModuleGraph,\n  entryPaths: Set<string>,\n  filePath: string,\n  identifier: string\n): ExportsTreeNode => {\n  const file = graph.get(filePath);\n\n  const rootNode: ExportsTreeNode = {\n    filePath,\n    identifier,\n    refs: filterRefs(file?.importedBy?.refs, identifier),\n    isEntry: entryPaths.has(filePath),\n    children: [],\n    originalId: undefined,\n    via: undefined,\n  };\n\n  const nodeMap = new Map<string, ExportsTreeNode>();\n  nodeMap.set(`${filePath}:${identifier}`, rootNode);\n\n  walkDown(\n    graph,\n    filePath,\n    identifier,\n    (sourceFile, sourceId, importingFile, id, isEntry, via) => {\n      const importMaps = graph.get(importingFile)?.imports.internal.get(sourceFile);\n      const importRefs = importMaps?.refs;\n      const ns = id.split('.')[0];\n      if (via === 'importNS' && !hasRelevantRef(importRefs, id) && !isNsReExported(importMaps, ns)) return CONTINUE;\n      const key = `${importingFile}:${id}`;\n      const isRenamed = via.endsWith('As') && sourceId !== ns;\n      const refs = filterRefs(importRefs, id);\n      const childNode = nodeMap.get(key) ?? {\n        filePath: importingFile,\n        identifier: id,\n        originalId: isRenamed ? sourceId : undefined,\n        via,\n        refs,\n        isEntry,\n        children: [],\n      };\n      nodeMap.set(key, childNode);\n      let parentNode = nodeMap.get(`${sourceFile}:${sourceId}`);\n      if (!parentNode) {\n        for (const [k, v] of nodeMap) {\n          if (k.startsWith(`${sourceFile}:${sourceId}.`) || k === `${sourceFile}:${sourceId}`) {\n            parentNode = v;\n            break;\n          }\n        }\n      }\n      (parentNode ?? rootNode).children.push(childNode);\n      return CONTINUE;\n    },\n    entryPaths\n  );\n\n  pruneReExportStarOnlyBranches(rootNode);\n\n  return rootNode;\n};\n\nconst filterRefs = (refs: Set<string> | undefined, id: string): string[] => {\n  if (!refs) return [];\n  return Array.from(refs).filter(ref => id === ref || id.startsWith(`${ref}.`) || ref.startsWith(`${id}.`));\n};\n\nconst hasRelevantRef = (refs: Set<string> | undefined, id: string): boolean => {\n  if (!refs || refs.size === 0) return false;\n  return Array.from(refs).some(ref => ref === id || ref.startsWith(`${id}.`));\n};\n\nconst isNsReExported = (importMaps: ImportMaps | undefined, ns: string): boolean => {\n  if (!importMaps) return false;\n  return importMaps.reExportAs.has(ns) || importMaps.reExportNs.has(ns);\n};\n\nconst hasNonReExportStar = (node: ExportsTreeNode): boolean => {\n  if (node.via && node.via !== 'reExportStar') return true;\n  return node.children.some(child => hasNonReExportStar(child));\n};\n\nconst pruneReExportStarOnlyBranches = (node: ExportsTreeNode): void => {\n  node.children = node.children.filter(child => hasNonReExportStar(child));\n  for (const child of node.children) pruneReExportStarOnlyBranches(child);\n};\n"
  },
  {
    "path": "packages/knip/src/graph-explorer/operations/find-cycles.ts",
    "content": "import type { Cycle } from '../../session/types.ts';\nimport type { ModuleGraph } from '../../types/module-graph.ts';\n\nexport const findCycles = (graph: ModuleGraph, filePath: string, maxDepth = 16) => {\n  const cycles: Cycle[] = [];\n  const visited = new Set<string>();\n  const pathSet = new Set<string>([filePath]);\n  const path: string[] = [filePath];\n\n  const visit = (currentPath: string) => {\n    if (path.length > maxDepth) return;\n    const node = graph.get(currentPath);\n    if (!node?.imports?.internal) return;\n\n    const nonTypeOnlyImports = new Set<string>();\n    for (const _import of node.imports.imports) {\n      if (_import.filePath && !_import.isTypeOnly) nonTypeOnlyImports.add(_import.filePath);\n    }\n\n    for (const [importedPath] of node.imports.internal) {\n      if (!nonTypeOnlyImports.has(importedPath)) continue;\n\n      if (importedPath === filePath) {\n        cycles.push([...path, importedPath]);\n        continue;\n      }\n      if (visited.has(importedPath)) continue;\n      if (!pathSet.has(importedPath)) {\n        path.push(importedPath);\n        pathSet.add(importedPath);\n        visit(importedPath);\n        pathSet.delete(importedPath);\n        path.pop();\n      }\n    }\n    visited.add(currentPath);\n  };\n\n  visit(filePath);\n\n  return cycles;\n};\n"
  },
  {
    "path": "packages/knip/src/graph-explorer/operations/get-contention.ts",
    "content": "import { IMPORT_STAR } from '../../constants.ts';\nimport type { ContentionDetails } from '../../session/types.ts';\nimport type { ModuleGraph } from '../../types/module-graph.ts';\nimport { getExportedIdentifiers } from '../utils.ts';\nimport { forEachAliasReExport, forEachPassThroughReExport, getStarReExportSources } from '../visitors.ts';\n\ninterface ReExportNetwork {\n  files: Set<string>;\n  definitions: Set<string>;\n  reExportsFrom: Map<string, Set<string>>;\n}\n\nexport const getContention = (graph: ModuleGraph, filePath: string): Map<string, ContentionDetails> => {\n  const node = graph.get(filePath);\n  if (!node) return new Map();\n\n  const exportedIdentifiers = getExportedIdentifiers(graph, filePath);\n  const result = new Map<string, ContentionDetails>();\n\n  for (const identifier of exportedIdentifiers.keys()) {\n    if (identifier === 'default') continue;\n    const details = getContentionForIdentifier(graph, filePath, identifier);\n    if (details && (details.branching.length > 0 || details.conflict.length > 0)) {\n      result.set(identifier, details);\n    }\n  }\n\n  return result;\n};\n\nconst getContentionForIdentifier = (\n  graph: ModuleGraph,\n  startFilePath: string,\n  identifier: string\n): ContentionDetails | null => {\n  const network = buildReExportNetwork(graph, startFilePath, identifier);\n\n  if (network.files.size <= 1) return null;\n\n  const branchingFiles: string[] = [];\n  for (const file of network.files) {\n    const sourceCount = network.reExportsFrom.get(file)?.size ?? 0;\n    if (sourceCount > 1) {\n      branchingFiles.push(file);\n    }\n  }\n\n  const hasConflict = network.definitions.size > 1;\n\n  if (branchingFiles.length === 0 && !hasConflict) return null;\n\n  return {\n    branching: branchingFiles.sort(),\n    conflict: hasConflict ? Array.from(network.definitions).sort() : [],\n  };\n};\n\nconst buildReExportNetwork = (graph: ModuleGraph, startFilePath: string, identifier: string): ReExportNetwork => {\n  const network: ReExportNetwork = {\n    files: new Set(),\n    definitions: new Set(),\n    reExportsFrom: new Map(),\n  };\n\n  const upVisited = new Set<string>();\n  const downVisited = new Set<string>();\n\n  walkUp(graph, network, startFilePath, identifier, upVisited);\n  walkDown(graph, network, startFilePath, identifier, downVisited);\n\n  for (const file of network.files) {\n    if (!upVisited.has(file)) {\n      walkUp(graph, network, file, identifier, upVisited);\n    }\n  }\n\n  for (const definitionFile of network.definitions) {\n    if (!downVisited.has(definitionFile)) walkDown(graph, network, definitionFile, identifier, downVisited);\n  }\n\n  return network;\n};\n\nconst walkUp = (\n  graph: ModuleGraph,\n  network: ReExportNetwork,\n  filePath: string,\n  identifier: string,\n  visited: Set<string>\n) => {\n  if (visited.has(filePath)) return;\n  visited.add(filePath);\n\n  const node = graph.get(filePath);\n  if (!node) return;\n\n  const exportedIds = getExportedIdentifiers(graph, filePath);\n  if (!exportedIds.has(identifier)) return;\n\n  network.files.add(filePath);\n\n  const exp = node.exports.get(identifier);\n  if (exp && !exp.isReExport) {\n    network.definitions.add(filePath);\n  }\n\n  for (const [sourcePath, importMaps] of node.imports.internal) {\n    forEachPassThroughReExport(importMaps, (id, _sources) => {\n      if (id !== identifier) return;\n      addEdge(network, sourcePath, filePath);\n      walkUp(graph, network, sourcePath, identifier, visited);\n    });\n\n    forEachAliasReExport(importMaps, (sourceId, alias, _sources) => {\n      if (alias !== identifier) return;\n      addEdge(network, sourcePath, filePath);\n      walkUp(graph, network, sourcePath, sourceId, visited);\n    });\n\n    const starSources = getStarReExportSources(importMaps);\n    if (starSources) {\n      const sourceExports = getExportedIdentifiers(graph, sourcePath);\n      if (sourceExports.has(identifier)) {\n        addEdge(network, sourcePath, filePath);\n        walkUp(graph, network, sourcePath, identifier, visited);\n      }\n    }\n  }\n};\n\nconst walkDown = (\n  graph: ModuleGraph,\n  network: ReExportNetwork,\n  filePath: string,\n  identifier: string,\n  visited: Set<string>\n) => {\n  if (visited.has(filePath)) return;\n  visited.add(filePath);\n\n  const node = graph.get(filePath);\n  if (!node?.importedBy) return;\n\n  const processConsumer = (consumerPath: string) => {\n    network.files.add(consumerPath);\n    addEdge(network, filePath, consumerPath);\n\n    const consumerExport = graph.get(consumerPath)?.exports.get(identifier);\n    if (consumerExport && !consumerExport.isReExport) network.definitions.add(consumerPath);\n\n    walkDown(graph, network, consumerPath, identifier, visited);\n  };\n\n  const directConsumers = node.importedBy.reExport.get(identifier);\n  if (directConsumers) {\n    for (const consumerPath of directConsumers) processConsumer(consumerPath);\n  }\n\n  for (const [sourceId, aliasMap] of node.importedBy.reExportAs) {\n    if (sourceId === identifier) {\n      for (const [_alias, consumers] of aliasMap) {\n        for (const consumerPath of consumers) processConsumer(consumerPath);\n      }\n    }\n  }\n\n  const starConsumers = node.importedBy.reExport.get(IMPORT_STAR);\n  if (starConsumers) {\n    for (const consumerPath of starConsumers) {\n      const consumerExports = getExportedIdentifiers(graph, consumerPath);\n      if (consumerExports.has(identifier)) processConsumer(consumerPath);\n    }\n  }\n};\n\nconst addEdge = (network: ReExportNetwork, source: string, consumer: string) => {\n  let sources = network.reExportsFrom.get(consumer);\n  if (!sources) {\n    sources = new Set();\n    network.reExportsFrom.set(consumer, sources);\n  }\n  sources.add(source);\n};\n"
  },
  {
    "path": "packages/knip/src/graph-explorer/operations/get-dependency-usage.ts",
    "content": "import type { ModuleGraph } from '../../types/module-graph.ts';\nimport { getPackageNameFromModuleSpecifier } from '../../util/modules.ts';\n\nexport interface DependencyNode {\n  filePath: string;\n  specifier: string;\n  binaryName: string | undefined;\n  pos: number | undefined;\n  line: number | undefined;\n  col: number | undefined;\n}\n\nexport interface DependencyNodes {\n  packageName: string;\n  imports: DependencyNode[];\n}\n\nexport const getDependencyUsage = (graph: ModuleGraph, pattern?: string | RegExp): Map<string, DependencyNodes> => {\n  const result = new Map<string, DependencyNodes>();\n\n  const isMatch = (packageName: string, binaryName?: string) => {\n    if (!pattern) return true;\n    if (typeof pattern === 'string') return packageName === pattern || binaryName === pattern;\n    return pattern.test(packageName) || (binaryName !== undefined && pattern.test(binaryName));\n  };\n\n  const addEntry = (\n    packageName: string,\n    filePath: string,\n    specifier: string,\n    binaryName: string | undefined,\n    pos: number | undefined,\n    line: number | undefined,\n    col: number | undefined\n  ) => {\n    let entry = result.get(packageName);\n    if (!entry) {\n      entry = { packageName, imports: [] };\n      result.set(packageName, entry);\n    }\n    entry.imports.push({ filePath, specifier, binaryName, pos, line, col });\n  };\n\n  for (const [filePath, file] of graph) {\n    if (file.imports?.external) {\n      for (const _import of file.imports.external) {\n        const packageName = getPackageNameFromModuleSpecifier(_import.specifier);\n        if (packageName && isMatch(packageName)) {\n          addEntry(packageName, filePath, _import.specifier, undefined, _import.pos, _import.line, _import.col);\n        }\n      }\n    }\n\n    if (file.imports?.externalRefs) {\n      for (const ref of file.imports.externalRefs) {\n        const packageName = getPackageNameFromModuleSpecifier(ref.specifier);\n        if (packageName && isMatch(packageName, ref.identifier)) {\n          addEntry(packageName, filePath, ref.specifier, ref.identifier, undefined, undefined, undefined);\n        }\n      }\n    }\n  }\n\n  return result;\n};\n"
  },
  {
    "path": "packages/knip/src/graph-explorer/operations/get-usage.ts",
    "content": "import { IMPORT_STAR } from '../../constants.ts';\nimport type { Identifier, ModuleGraph, Position } from '../../types/module-graph.ts';\nimport { getCachedUsage, setCachedUsage } from '../cache.ts';\nimport { CONTINUE } from '../constants.ts';\nimport { findImportRef } from '../utils.ts';\nimport { type Via, walkDown } from '../walk-down.ts';\n\nexport interface UsageLocation extends Position {\n  filePath: string;\n  identifier: string;\n  isEntry: boolean;\n  via: Via;\n}\n\nexport interface UsageResult {\n  locations: UsageLocation[];\n  reExportingEntryFile: string | undefined;\n}\n\nexport const getUsage = (\n  graph: ModuleGraph,\n  entryPaths: Set<string>,\n  filePath: string,\n  identifier: Identifier\n): UsageResult => {\n  const cached = getCachedUsage(graph, filePath, identifier);\n  if (cached) return cached;\n\n  const locations: UsageLocation[] = [];\n  let reExportingEntryFile: string | undefined;\n\n  if (entryPaths.has(filePath)) {\n    reExportingEntryFile = filePath;\n  }\n\n  walkDown(\n    graph,\n    filePath,\n    identifier,\n    (sourceFile, sourceId, importingFile, id, isEntry, via) => {\n      const lookupId = via === 'importNS' ? IMPORT_STAR : sourceId;\n      const importRef = findImportRef(graph, importingFile, sourceFile, lookupId);\n      locations.push({\n        filePath: importingFile,\n        identifier: id,\n        pos: importRef?.pos ?? 0,\n        line: importRef?.line ?? 0,\n        col: importRef?.col ?? 0,\n        isEntry,\n        via,\n      });\n\n      if (isEntry && !reExportingEntryFile) reExportingEntryFile = importingFile;\n\n      return CONTINUE;\n    },\n    entryPaths\n  );\n\n  const result: UsageResult = { locations, reExportingEntryFile };\n  setCachedUsage(graph, filePath, identifier, result);\n  return result;\n};\n"
  },
  {
    "path": "packages/knip/src/graph-explorer/operations/has-strictly-ns-references.ts",
    "content": "import type { ImportMaps, ModuleGraph } from '../../types/module-graph.ts';\nimport { getAliasReExportMap, getPassThroughReExportSources, getStarReExportSources } from '../visitors.ts';\n\nexport const hasStrictlyNsReferences = (\n  graph: ModuleGraph,\n  filePath: string,\n  importsForExport: ImportMaps | undefined,\n  identifier: string\n): [boolean, string?] => {\n  const seen = new Set<string>();\n\n  const walkDown = (path: string, importMaps: ImportMaps | undefined, id: string): [boolean, string?] => {\n    if (!importMaps) return [false];\n\n    if (seen.has(path)) return [false];\n    seen.add(path);\n\n    let namespace: string | undefined;\n\n    const follow = (sources: Set<string>, nextId: string): [boolean, string?] | undefined => {\n      for (const filePath of sources) {\n        const file = graph.get(filePath);\n        if (!file?.importedBy) continue;\n        const result = walkDown(filePath, file.importedBy, nextId);\n        if (result[0] === false && result[1]) return result;\n        if (result[1] && !namespace) namespace = result[1];\n      }\n      return undefined;\n    };\n\n    for (const ns of importMaps.importNs.keys()) {\n      if (!importMaps.refs.has(ns)) return [false, ns];\n\n      let hasMemberRef = false;\n      for (const ref of importMaps.refs) {\n        if (ref.startsWith(`${ns}.`)) {\n          hasMemberRef = true;\n          break;\n        }\n      }\n      if (hasMemberRef) return [false, ns];\n\n      namespace = ns;\n\n      const nsAliases = getAliasReExportMap(importMaps, ns);\n      if (nsAliases) {\n        for (const [alias, sources] of nsAliases) {\n          const result = follow(sources, alias);\n          if (result) return result;\n        }\n      }\n    }\n\n    const directSources = getPassThroughReExportSources(importMaps, id);\n    if (directSources) {\n      const result = follow(directSources, id);\n      if (result) return result;\n    }\n\n    const starSources = getStarReExportSources(importMaps);\n    if (starSources) {\n      const result = follow(starSources, id);\n      if (result) return result;\n    }\n\n    const [_id, ...rest] = id.split('.');\n    const aliasEntries = getAliasReExportMap(importMaps, _id);\n    if (aliasEntries) {\n      for (const [alias, sources] of aliasEntries) {\n        const result = follow(sources, [alias, ...rest].join('.'));\n        if (result) return result;\n      }\n    }\n\n    for (const [ns, sources] of importMaps.reExportNs) {\n      const result = follow(sources, `${ns}.${id}`);\n      if (result) return result;\n    }\n\n    const importedSources = importMaps.import.get(id);\n    if (importedSources) {\n      const result = follow(importedSources, id);\n      if (result) return result;\n    }\n\n    const importAsMap = importMaps.importAs.get(id);\n    if (importAsMap) {\n      for (const [alias, sources] of importAsMap) {\n        const result = follow(sources, alias);\n        if (result) return result;\n      }\n    }\n\n    if (namespace) return [true, namespace];\n    return [false];\n  };\n\n  return walkDown(filePath, importsForExport, identifier);\n};\n"
  },
  {
    "path": "packages/knip/src/graph-explorer/operations/is-referenced.ts",
    "content": "import { OPAQUE } from '../../constants.ts';\nimport type { Identifier, ImportMaps, ModuleGraph } from '../../types/module-graph.ts';\nimport {\n  getAliasReExportMap,\n  getNamespaceReExportSources,\n  getPassThroughReExportSources,\n  getStarReExportSources,\n} from '../visitors.ts';\n\nconst hasOnlyNsRefs = (file: ImportMaps): boolean => {\n  if (file.importNs.size === 0) return false;\n  for (const ns of file.importNs.keys()) {\n    if (!file.refs.has(ns)) return false;\n    for (const ref of file.refs) {\n      if (ref.startsWith(`${ns}.`)) return false;\n    }\n  }\n  return true;\n};\n\nexport const isReferenced = (\n  graph: ModuleGraph,\n  entryPaths: Set<string>,\n  filePath: string,\n  id: Identifier,\n  options: { includeEntryExports: boolean }\n) => {\n  const seen = new Set<string>();\n\n  const walkDown = (path: string, id: string): [boolean, string | undefined] => {\n    const isEntryFile = entryPaths.has(path);\n    let reExportingEntryFile: string | undefined = isEntryFile ? path : undefined;\n\n    if (seen.has(path)) return [false, reExportingEntryFile];\n    seen.add(path);\n\n    const restIds = id.split('.');\n    const identifier = restIds.shift();\n    const file = graph.get(path)?.importedBy;\n\n    if (!identifier || !file) {\n      return [false, reExportingEntryFile];\n    }\n\n    const follow = (sources: Set<string>, nextId: string): boolean => {\n      for (const byFilePath of sources) {\n        if (seen.has(byFilePath)) continue;\n        const result = walkDown(byFilePath, nextId);\n        if (result[1]) reExportingEntryFile = result[1];\n        if (result[0]) return true;\n      }\n      return false;\n    };\n\n    if (\n      (file.import.get(OPAQUE) && !hasOnlyNsRefs(file)) ||\n      ((identifier === id || (identifier !== id && file.refs.has(id))) &&\n        (file.import.has(identifier) || file.importAs.has(identifier)))\n    ) {\n      return [true, reExportingEntryFile];\n    }\n\n    for (const [exportId, aliases] of file.importAs) {\n      if (identifier === exportId) {\n        for (const alias of aliases.keys()) {\n          const aliasedRef = [alias, ...restIds].join('.');\n          if (file.refs.has(aliasedRef)) {\n            return [true, reExportingEntryFile];\n          }\n        }\n      }\n    }\n\n    for (const namespace of file.importNs.keys()) {\n      if (file.refs.has(`${namespace}.${id}`)) {\n        return [true, reExportingEntryFile];\n      }\n\n      const nsAliasMap = getAliasReExportMap(file, namespace);\n      if (nsAliasMap) {\n        for (const [alias, sources] of nsAliasMap) {\n          if (follow(sources, `${alias}.${id}`)) return [true, reExportingEntryFile];\n        }\n      }\n\n      const nsReExportSources = getNamespaceReExportSources(file, namespace);\n      if (nsReExportSources) {\n        if (follow(nsReExportSources, `${namespace}.${id}`)) return [true, reExportingEntryFile];\n      }\n    }\n\n    if (isEntryFile && !options.includeEntryExports) return [false, reExportingEntryFile];\n\n    const aliasMap = getAliasReExportMap(file, identifier);\n    if (aliasMap) {\n      for (const [alias, sources] of aliasMap) {\n        if (follow(sources, [alias, ...restIds].join('.'))) return [true, reExportingEntryFile];\n      }\n    }\n\n    const directSources = getPassThroughReExportSources(file, identifier);\n    const starSources = getStarReExportSources(file);\n\n    if (directSources) {\n      if (follow(directSources, id)) return [true, reExportingEntryFile];\n    } else if (starSources) {\n      if (follow(starSources, id)) return [true, reExportingEntryFile];\n    }\n\n    for (const [namespace, sources] of file.reExportNs) {\n      if (follow(sources, `${namespace}.${id}`)) {\n        return [true, reExportingEntryFile];\n      }\n    }\n\n    return [false, reExportingEntryFile];\n  };\n\n  return walkDown(filePath, id);\n};\n"
  },
  {
    "path": "packages/knip/src/graph-explorer/operations/resolve-definition.ts",
    "content": "import type { ReExportKind } from '../../session/types.ts';\nimport type { Export, Identifier, ModuleGraph } from '../../types/module-graph.ts';\nimport { getCachedDefinition, setCachedDefinition } from '../cache.ts';\nimport { CONTINUE, STOP } from '../constants.ts';\nimport { walkUp } from '../walk-up.ts';\n\ninterface TraversalStep {\n  filePath: string;\n  identifier: string;\n  via: ReExportKind;\n}\n\nexport interface DefinitionResult {\n  type: 'symbol' | 'namespace';\n  filePath: string;\n  identifier: string;\n  exportNode: Export | undefined;\n  chain: TraversalStep[];\n}\n\nexport const resolveDefinition = (\n  graph: ModuleGraph,\n  filePath: string,\n  identifier: Identifier\n): DefinitionResult | null => {\n  const cached = getCachedDefinition(graph, filePath, identifier);\n  if (cached !== undefined) return cached;\n\n  const chain: TraversalStep[] = [];\n  let result: DefinitionResult | null = null;\n\n  walkUp(graph, filePath, identifier, (resolvedPath, resolvedId, via) => {\n    chain.push({ filePath: resolvedPath, identifier: resolvedId, via });\n\n    if (via === 'self') {\n      const node = graph.get(resolvedPath);\n      const exportNode = node?.exports.get(resolvedId);\n      result = {\n        type: 'symbol',\n        filePath: resolvedPath,\n        identifier: resolvedId,\n        exportNode,\n        chain,\n      };\n      return STOP;\n    }\n\n    if (via === 'namespace') {\n      result = {\n        type: 'namespace',\n        filePath: resolvedPath,\n        identifier: resolvedId,\n        exportNode: undefined,\n        chain,\n      };\n      return STOP;\n    }\n\n    return CONTINUE;\n  });\n\n  setCachedDefinition(graph, filePath, identifier, result);\n  return result;\n};\n"
  },
  {
    "path": "packages/knip/src/graph-explorer/utils.ts",
    "content": "import type { Import, ImportMaps, ModuleGraph } from '../types/module-graph.ts';\nimport { getCachedExportedIdentifiers, setCachedExportedIdentifiers } from './cache.ts';\nimport {\n  forEachAliasReExport,\n  forEachNamespaceReExport,\n  forEachPassThroughReExport,\n  getStarReExportSources,\n} from './visitors.ts';\n\nexport const getExportedIdentifiers = (\n  graph: ModuleGraph,\n  filePath: string,\n  visited = new Set<string>()\n): Map<string, boolean> => {\n  if (visited.has(filePath)) return new Map();\n  visited.add(filePath);\n\n  const cached = getCachedExportedIdentifiers(graph, filePath);\n  if (cached) return cached;\n\n  const node = graph.get(filePath);\n  if (!node) return new Map();\n\n  const identifiers = new Map<string, boolean>();\n\n  const addIdentifier = (identifier: string, isDuplicate = false) => {\n    if (identifiers.has(identifier)) {\n      identifiers.set(identifier, true);\n    } else {\n      identifiers.set(identifier, isDuplicate);\n    }\n  };\n\n  for (const identifier of node.exports.keys()) {\n    if (identifier === 'default') continue;\n    addIdentifier(identifier);\n  }\n\n  if (node.imports?.internal) {\n    for (const [importedPath, importDetails] of node.imports.internal) {\n      forEachPassThroughReExport(importDetails, (id, _sources) => {\n        if (id !== 'default') addIdentifier(id);\n      });\n\n      forEachAliasReExport(importDetails, (_id, alias, _sources) => {\n        addIdentifier(alias);\n      });\n\n      forEachNamespaceReExport(importDetails, (namespace, _sources) => {\n        addIdentifier(namespace, true);\n      });\n\n      const starSources = getStarReExportSources(importDetails);\n      if (starSources) {\n        const nestedIdentifiers = getExportedIdentifiers(graph, importedPath, new Set(visited));\n        for (const [nestedId, isNestedDuplicate] of nestedIdentifiers) {\n          if (nestedId !== 'default') addIdentifier(nestedId, isNestedDuplicate);\n        }\n      }\n    }\n  }\n\n  setCachedExportedIdentifiers(graph, filePath, identifiers);\n  return identifiers;\n};\n\nexport const hasStrictlyEnumReferences = (importsForExport: ImportMaps | undefined, identifier: string): boolean => {\n  if (!importsForExport || !importsForExport.refs.has(identifier)) return false;\n  for (const ref of importsForExport.refs) {\n    if (ref.startsWith(`${identifier}.`)) return false;\n  }\n  return true;\n};\n\nexport const getIssueType = (hasOnlyNsReference: boolean, isType: boolean) => {\n  if (hasOnlyNsReference) return isType ? 'nsTypes' : 'nsExports';\n  return isType ? 'types' : 'exports';\n};\n\nexport const findImportRef = (\n  graph: ModuleGraph,\n  importingFile: string,\n  importedFile: string,\n  identifier: string\n): Import | undefined => {\n  const node = graph.get(importingFile);\n  if (!node) return undefined;\n  for (const _import of node.imports.imports) {\n    if (_import.filePath === importedFile && _import.identifier === identifier) return _import;\n  }\n};\n"
  },
  {
    "path": "packages/knip/src/graph-explorer/visitors.ts",
    "content": "/* oxlint-disable @typescript-eslint/no-invalid-void-type */\nimport { IMPORT_STAR } from '../constants.ts';\nimport type { ImportMaps } from '../types/module-graph.ts';\n\ntype PassThroughReExportCallback = (identifier: string, sources: Set<string>) => boolean | void;\n\ntype AliasReExportCallback = (identifier: string, alias: string, sources: Set<string>) => boolean | void;\n\ntype NamespaceReExportCallback = (namespace: string, sources: Set<string>) => boolean | void;\n\nexport const forEachPassThroughReExport = (importMaps: ImportMaps, callback: PassThroughReExportCallback): boolean => {\n  for (const [identifier, sources] of importMaps.reExport) {\n    if (identifier === IMPORT_STAR) continue;\n    if (callback(identifier, sources) === false) return false;\n  }\n  return true;\n};\n\nexport const forEachAliasReExport = (importMaps: ImportMaps, callback: AliasReExportCallback): boolean => {\n  for (const [identifier, aliasMap] of importMaps.reExportAs) {\n    for (const [alias, sources] of aliasMap) {\n      if (callback(identifier, alias, sources) === false) return false;\n    }\n  }\n  return true;\n};\n\nexport const forEachNamespaceReExport = (importMaps: ImportMaps, callback: NamespaceReExportCallback): boolean => {\n  for (const [namespace, sources] of importMaps.reExportNs) {\n    if (callback(namespace, sources) === false) return false;\n  }\n  return true;\n};\n\nexport const getStarReExportSources = (importMaps: ImportMaps) => importMaps.reExport.get(IMPORT_STAR);\n\nexport const getPassThroughReExportSources = (importMaps: ImportMaps, identifier: string) =>\n  importMaps.reExport.get(identifier);\n\nexport const getAliasReExportMap = (importMaps: ImportMaps, identifier: string) =>\n  importMaps.reExportAs.get(identifier);\n\nexport const getNamespaceReExportSources = (importMaps: ImportMaps, namespace: string) =>\n  importMaps.reExportNs.get(namespace);\n"
  },
  {
    "path": "packages/knip/src/graph-explorer/walk-down.ts",
    "content": "import type { ModuleGraph } from '../types/module-graph.ts';\nimport { STOP } from './constants.ts';\nimport { getAliasReExportMap, getPassThroughReExportSources, getStarReExportSources } from './visitors.ts';\n\nexport type Via = 'import' | 'importAs' | 'importNS' | 'reExport' | 'reExportAs' | 'reExportNS' | 'reExportStar';\n\ntype Visitor = (\n  sourceFile: string,\n  identifier: string,\n  importingFile: string,\n  identifierPath: string,\n  isEntry: boolean,\n  via: Via\n) => 'continue' | 'stop' | undefined;\n\nexport const walkDown = (\n  graph: ModuleGraph,\n  filePath: string,\n  identifier: string,\n  visitor: Visitor,\n  entryPaths: Set<string>,\n  visited: Set<string> = new Set()\n): boolean => {\n  const key = `${filePath}:${identifier}`;\n  if (visited.has(key)) return false;\n  visited.add(key);\n\n  const file = graph.get(filePath);\n  if (!file?.importedBy) return false;\n\n  const restIds = identifier.split('.');\n  const id = restIds.shift();\n  if (!id) return false;\n\n  const imported = file.importedBy;\n\n  const importedByFiles = imported.import.get(id);\n  if (importedByFiles) {\n    for (const importingFile of importedByFiles) {\n      const isEntry = entryPaths.has(importingFile);\n      if (visitor(filePath, id, importingFile, id, isEntry, 'import') === STOP) return true;\n    }\n  }\n\n  const importAsAliases = imported.importAs.get(id);\n  if (importAsAliases) {\n    for (const [alias, byFilePaths] of importAsAliases) {\n      for (const importingFile of byFilePaths) {\n        const isEntry = entryPaths.has(importingFile);\n        if (visitor(filePath, id, importingFile, alias, isEntry, 'importAs') === STOP) return true;\n      }\n    }\n  }\n\n  for (const [namespace, byFilePaths] of imported.importNs) {\n    for (const importingFile of byFilePaths) {\n      const isEntry = entryPaths.has(importingFile);\n      if (visitor(filePath, identifier, importingFile, `${namespace}.${identifier}`, isEntry, 'importNS') === STOP) {\n        return true;\n      }\n    }\n  }\n\n  let done = false;\n\n  if (!done) {\n    const passThroughSources = getPassThroughReExportSources(imported, id);\n    if (passThroughSources) {\n      for (const reExportingFile of passThroughSources) {\n        const isEntry = entryPaths.has(reExportingFile);\n        if (visitor(filePath, id, reExportingFile, id, isEntry, 'reExport') === STOP) {\n          done = true;\n          break;\n        }\n        if (walkDown(graph, reExportingFile, identifier, visitor, entryPaths, visited)) {\n          done = true;\n          break;\n        }\n      }\n    }\n  }\n\n  if (!done) {\n    const aliasReExportMap = getAliasReExportMap(imported, id);\n    if (aliasReExportMap) {\n      for (const [alias, sources] of aliasReExportMap) {\n        for (const reExportingFile of sources) {\n          const isEntry = entryPaths.has(reExportingFile);\n          if (visitor(filePath, id, reExportingFile, alias, isEntry, 'reExportAs') === STOP) {\n            done = true;\n            break;\n          }\n          if (walkDown(graph, reExportingFile, [alias, ...restIds].join('.'), visitor, entryPaths, visited)) {\n            done = true;\n            break;\n          }\n        }\n        if (done) break;\n      }\n    }\n  }\n\n  if (!done) {\n    for (const [namespace, sources] of imported.reExportNs) {\n      for (const reExportingFile of sources) {\n        const isEntry = entryPaths.has(reExportingFile);\n        if (\n          visitor(filePath, identifier, reExportingFile, `${namespace}.${identifier}`, isEntry, 'reExportNS') === STOP\n        ) {\n          done = true;\n          break;\n        }\n        if (walkDown(graph, reExportingFile, `${namespace}.${identifier}`, visitor, entryPaths, visited)) {\n          done = true;\n          break;\n        }\n      }\n      if (done) break;\n    }\n  }\n\n  if (!done) {\n    const starSources = getStarReExportSources(imported);\n    if (starSources) {\n      for (const reExportingFile of starSources) {\n        const isEntry = entryPaths.has(reExportingFile);\n        if (visitor(filePath, id, reExportingFile, id, isEntry, 'reExportStar') === STOP) {\n          done = true;\n          break;\n        }\n        if (walkDown(graph, reExportingFile, identifier, visitor, entryPaths, visited)) {\n          done = true;\n          break;\n        }\n      }\n    }\n  }\n\n  return done;\n};\n"
  },
  {
    "path": "packages/knip/src/graph-explorer/walk-up.ts",
    "content": "import type { ModuleGraph } from '../types/module-graph.ts';\nimport { RE_EXPORT_KIND, STOP } from './constants.ts';\nimport {\n  forEachAliasReExport,\n  getNamespaceReExportSources,\n  getPassThroughReExportSources,\n  getStarReExportSources,\n} from './visitors.ts';\n\ntype ReExportKind = (typeof RE_EXPORT_KIND)[keyof typeof RE_EXPORT_KIND];\n\ntype Visitor = (filePath: string, identifier: string, via: ReExportKind) => 'continue' | 'stop' | undefined;\n\nexport const walkUp = (\n  graph: ModuleGraph,\n  filePath: string,\n  identifier: string,\n  visitor: Visitor,\n  visited: Set<string> = new Set()\n): boolean => {\n  const key = `${filePath}:${identifier}`;\n  if (visited.has(key)) return false;\n  visited.add(key);\n\n  const node = graph.get(filePath);\n  if (!node) return false;\n\n  const nodeExport = node.exports.get(identifier);\n  if (nodeExport && !nodeExport.isReExport) {\n    return visitor(filePath, identifier, RE_EXPORT_KIND.SELF) === STOP;\n  }\n\n  const starResolvers: Array<() => boolean> = [];\n\n  if (node.imports?.internal) {\n    for (const [importedFrom, importDetails] of node.imports.internal) {\n      let done = false;\n\n      if (!done) {\n        if (getPassThroughReExportSources(importDetails, identifier)) {\n          if (visitor(importedFrom, identifier, RE_EXPORT_KIND.PASSTHROUGH) === STOP) {\n            done = true;\n          } else if (walkUp(graph, importedFrom, identifier, visitor, visited)) {\n            done = true;\n          }\n        }\n      }\n\n      if (!done) {\n        forEachAliasReExport(importDetails, (id, alias) => {\n          if (alias !== identifier) return;\n          if (visitor(importedFrom, id, RE_EXPORT_KIND.ALIAS) === STOP) {\n            done = true;\n            return false;\n          }\n          if (walkUp(graph, importedFrom, id, visitor, visited)) {\n            done = true;\n            return false;\n          }\n        });\n      }\n\n      if (!done) {\n        if (getNamespaceReExportSources(importDetails, identifier)) {\n          if (visitor(importedFrom, identifier, RE_EXPORT_KIND.NAMESPACE) === STOP) {\n            done = true;\n          }\n        }\n      }\n\n      if (!done) {\n        if (getStarReExportSources(importDetails)) {\n          starResolvers.push(() => {\n            if (visitor(importedFrom, identifier, RE_EXPORT_KIND.STAR) === STOP) return true;\n            return walkUp(graph, importedFrom, identifier, visitor, visited);\n          });\n        }\n      }\n\n      if (done) return true;\n    }\n  }\n\n  for (const resolveStar of starResolvers) if (resolveStar()) return true;\n\n  return false;\n};\n"
  },
  {
    "path": "packages/knip/src/index.ts",
    "content": "import { fix } from './IssueFixer.ts';\nimport { run } from './run.ts';\nimport type { MainOptions } from './util/create-options.ts';\n\nexport const main = async (options: MainOptions) => {\n  const { results } = await run(options);\n  if (options.isFix) await fix(results.issues, results.counters, options);\n  return results;\n};\n"
  },
  {
    "path": "packages/knip/src/manifest/helpers.ts",
    "content": "import type { Scripts } from '../types/package-json.ts';\nimport { join } from '../util/path.ts';\nimport { _require } from '../util/require.ts';\n\ntype LoadPackageManifestOptions = { dir: string; packageName: string; cwd: string };\n\nexport const loadPackageManifest = ({ dir, packageName, cwd }: LoadPackageManifestOptions) => {\n  // TODO Not sure what's the most efficient way to get a package.json, but this seems to do the job across package\n  // managers (npm, Yarn, pnpm)\n  try {\n    return _require(join(dir, 'node_modules', packageName, 'package.json'));\n  } catch (_error) {\n    if (dir !== cwd) {\n      try {\n        return _require(join(cwd, 'node_modules', packageName, 'package.json'));\n      } catch (_error) {\n        // Explicitly suppressing errors here\n      }\n    }\n    // Explicitly suppressing errors here\n  }\n};\n\nexport const getFilteredScripts = (scripts: Scripts) => {\n  if (!scripts) return [{}, {}];\n\n  const productionScripts: Scripts = {};\n  const developmentScripts: Scripts = {};\n\n  for (const scriptName in scripts) {\n    if (!/^\\w/.test(scriptName)) continue;\n    if (scriptName === 'start') productionScripts[scriptName] = scripts[scriptName];\n    else developmentScripts[scriptName] = scripts[scriptName];\n  }\n\n  return [productionScripts, developmentScripts];\n};\n"
  },
  {
    "path": "packages/knip/src/manifest/index.ts",
    "content": "import type { HostDependencies, InstalledBinaries } from '../types/workspace.ts';\nimport { isDefinitelyTyped } from '../util/modules.ts';\nimport { timerify } from '../util/Performance.ts';\nimport { loadPackageManifest } from './helpers.ts';\n\ntype Options = {\n  packageNames: string[];\n  dir: string;\n  cwd: string;\n};\n\nconst getMetaDataFromPackageJson = ({ cwd, dir, packageNames }: Options) => {\n  const hostDependencies: HostDependencies = new Map();\n\n  // Find all binaries for each dependency\n  const installedBinaries: InstalledBinaries = new Map();\n\n  const hasTypesIncluded = new Set<string>();\n\n  for (const packageName of packageNames) {\n    const manifest = loadPackageManifest({ cwd, dir, packageName });\n    if (manifest) {\n      // Read and store installed binaries\n      const binaryName = packageName.replace(/^@[^/]+\\//, '');\n      const binaries = typeof manifest.bin === 'string' ? [binaryName] : Object.keys(manifest.bin ?? {});\n      for (const binaryName of binaries) {\n        if (installedBinaries.has(binaryName)) {\n          installedBinaries.get(binaryName)?.add(packageName);\n        } else {\n          installedBinaries.set(binaryName, new Set([packageName]));\n        }\n        if (installedBinaries.has(packageName)) {\n          installedBinaries.get(packageName)?.add(binaryName);\n        } else {\n          installedBinaries.set(packageName, new Set([binaryName]));\n        }\n      }\n\n      // Read and store peer dependencies\n      const packagePeerDependencies = Object.keys(manifest.peerDependencies ?? {});\n      for (const packagePeerDependency of packagePeerDependencies) {\n        const hostDependency = {\n          name: packageName,\n          isPeerOptional: manifest.peerDependenciesMeta?.[packagePeerDependency]?.optional ?? false,\n        };\n        if (hostDependencies.has(packagePeerDependency)) {\n          hostDependencies.get(packagePeerDependency)?.push(hostDependency);\n        } else {\n          hostDependencies.set(packagePeerDependency, [hostDependency]);\n        }\n      }\n\n      if (!isDefinitelyTyped(packageName) && (manifest.types || manifest.typings)) hasTypesIncluded.add(packageName);\n    }\n  }\n\n  return {\n    hostDependencies,\n    installedBinaries,\n    hasTypesIncluded,\n  };\n};\n\nexport const getDependencyMetaData = timerify(getMetaDataFromPackageJson);\n"
  },
  {
    "path": "packages/knip/src/plugins/_template/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDeferResolve } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { PluginConfig } from './types.ts';\n\n// link to __PLUGIN_NAME__ docs\n\nconst title = '__PLUGIN_NAME__';\n\nconst enablers = ['__PLUGIN_NAME__'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config: string[] = [];\n\nconst entry: string[] = [];\n\nconst production: string[] = [];\n\nconst resolveConfig: ResolveConfig<PluginConfig> = async config => {\n  const inputs = config?.plugins ?? [];\n  return [...inputs].map(id => toDeferResolve(id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  entry,\n  production,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/_template/types.ts",
    "content": "export type PluginConfig = {\n  plugins?: string[];\n  entryPathsOrPatterns?: string[];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/angular/index.ts",
    "content": "import { existsSync } from 'node:fs';\nimport type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { type Input, toConfig, toDeferResolve, toDependency, toEntry, toProductionEntry } from '../../util/input.ts';\nimport { isInternal, join } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport * as karma from '../karma/helpers.ts';\nimport type {\n  AngularCLIWorkspaceConfiguration,\n  KarmaTarget,\n  Project,\n  WebpackBrowserSchemaForBuildFacade,\n} from './types.ts';\n\n// https://angular.io/guide/workspace-config\n\nconst title = 'Angular';\n\nconst enablers = ['@angular/cli'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['angular.json'];\n\nconst resolveConfig: ResolveConfig<AngularCLIWorkspaceConfiguration> = async (config, options) => {\n  if (!config?.projects) return [];\n\n  const inputs = new Set<Input>();\n\n  for (const project of Object.values(config.projects)) {\n    if (!project.architect) return [];\n    for (const [targetName, target] of Object.entries(project.architect)) {\n      const { options: opts, configurations: configs } = target;\n      const [packageName] = typeof target.builder === 'string' ? target.builder.split(':') : [];\n      if (packageName) inputs.add(toDependency(packageName));\n      if (opts) {\n        if ('tsConfig' in opts && typeof opts.tsConfig === 'string') {\n          inputs.add(toConfig('typescript', opts.tsConfig));\n        }\n      }\n      const defaultEntriesByOption: EntriesByOption = opts ? entriesByOption(opts) : new Map();\n      const entriesByOptionByConfig: Map<string, EntriesByOption> = new Map(\n        configs ? Object.entries(configs).map(([name, opts]) => [name, entriesByOption(opts)]) : []\n      );\n      const productionEntriesByOption: EntriesByOption =\n        entriesByOptionByConfig.get(PRODUCTION_CONFIG_NAME) ?? new Map();\n      const isBuildTarget = targetName === BUILD_TARGET_NAME;\n      const maybeExternal = (option: string) => option === 'polyfills';\n      const toInput = (specifier: string, opts: { isProduction: boolean; maybeExternal: boolean }): Input => {\n        const normalizedPath = join(options.cwd, specifier);\n        // 👇 `isInternal` will report `false` for specifiers not starting with `.`\n        //    However, relative imports are usually specified in `angular.json` without `.` prefix\n        //    Hence checking also that file doesn't exist before considering it external\n        if (opts.maybeExternal && !isInternal(specifier) && !existsSync(normalizedPath)) {\n          return toDeferResolve(specifier);\n        }\n        return opts.isProduction ? toProductionEntry(normalizedPath) : toEntry(normalizedPath);\n      };\n      for (const [configName, entriesByOption] of entriesByOptionByConfig) {\n        for (const [option, entries] of entriesByOption) {\n          for (const entry of entries) {\n            inputs.add(\n              toInput(entry, {\n                isProduction: isBuildTarget && configName === PRODUCTION_CONFIG_NAME,\n                maybeExternal: maybeExternal(option),\n              })\n            );\n          }\n        }\n      }\n      for (const [option, entries] of defaultEntriesByOption) {\n        for (const entry of entries) {\n          inputs.add(\n            toInput(entry, {\n              isProduction: isBuildTarget && !productionEntriesByOption.get(option)?.length,\n              maybeExternal: maybeExternal(option),\n            })\n          );\n        }\n      }\n      if (target.builder && isAngularBuilderRefWithName({ builderRef: target.builder, name: 'karma' }) && opts) {\n        const karmaBuilderOptions = opts as KarmaTarget;\n        // https://github.com/angular/angular-cli/blob/19.0.6/packages/angular_devkit/build_angular/src/builders/karma/schema.json#L143\n        const testFilePatterns = karmaBuilderOptions.include ?? ['**/*.spec.ts'];\n        for (const testFilePattern of testFilePatterns) {\n          inputs.add(toEntry(testFilePattern));\n        }\n        // https://github.com/angular/angular-cli/blob/19.0.6/packages/angular_devkit/build_angular/src/builders/karma/schema.json#L146\n        const excludedTestFilePatterns = karmaBuilderOptions.exclude ?? [];\n        for (const excludedTestFilePattern of excludedTestFilePatterns) {\n          inputs.add(toEntry(`!${excludedTestFilePattern}`));\n        }\n        const karmaConfig = karmaBuilderOptions.karmaConfig;\n        if (!karmaConfig) {\n          // Hardcoded default Karma config from Angular builder\n          // https://github.com/angular/angular-cli/blob/19.0.6/packages/angular_devkit/build_angular/src/builders/karma/index.ts#L115\n          karma\n            .inputsFromPlugins(\n              ['karma-jasmine', 'karma-chrome-launcher', 'karma-jasmine-html-reporter', 'karma-coverage'],\n              options.manifest.devDependencies\n            )\n            .forEach(inputs.add, inputs);\n          karma.inputsFromFrameworks(['jasmine']).forEach(inputs.add, inputs);\n        }\n        if (karmaConfig && !karma.configFiles.includes(karmaConfig)) {\n          inputs.add(toConfig('karma', karmaConfig, { containingFilePath: options.configFilePath }));\n        }\n      }\n    }\n  }\n\n  return Array.from(inputs);\n};\n\nconst entriesByOption = (opts: TargetOptions): EntriesByOption =>\n  new Map(\n    Object.entries({\n      main: 'main' in opts && opts.main && typeof opts.main === 'string' ? [opts.main] : [],\n      scripts:\n        'scripts' in opts && opts.scripts && Array.isArray(opts.scripts)\n          ? (opts.scripts as ScriptsBuildOption).map(scriptStringOrObject =>\n              typeof scriptStringOrObject === 'string' ? scriptStringOrObject : scriptStringOrObject.input\n            )\n          : [],\n      polyfills:\n        'polyfills' in opts && opts.polyfills\n          ? Array.isArray(opts.polyfills)\n            ? opts.polyfills\n            : [opts.polyfills]\n          : [],\n      fileReplacements:\n        'fileReplacements' in opts && opts.fileReplacements && Array.isArray(opts.fileReplacements)\n          ? (opts.fileReplacements as FileReplacementsBuildOption).map(fileReplacement =>\n              'with' in fileReplacement ? fileReplacement.with : fileReplacement.replaceWith\n            )\n          : [],\n      browser: 'browser' in opts && opts.browser && typeof opts.browser === 'string' ? [opts.browser] : [],\n      server: 'server' in opts && opts.server && typeof opts.server === 'string' ? [opts.server] : [],\n      ssrEntry:\n        'ssr' in opts &&\n        opts.ssr &&\n        typeof opts.ssr === 'object' &&\n        'entry' in opts.ssr &&\n        typeof opts.ssr.entry === 'string'\n          ? [opts.ssr.entry]\n          : [],\n    })\n  );\n\nconst isAngularBuilderRefWithName = ({ builderRef, name }: { builderRef: string; name: string }) => {\n  const [pkg, builderName] = builderRef.split(':');\n  return (pkg === '@angular-devkit/build-angular' || pkg === '@angular/build') && builderName === name;\n};\n\ntype TargetOptions = Exclude<Target['options'], undefined>;\ntype Target = Architect[string];\ntype Architect = Exclude<Project['architect'], undefined>;\n\ntype EntriesByOption = Map<string, readonly string[]>;\n\n//👇 Using Webpack-based browser schema to support old `replaceWith` file replacements\ntype FileReplacementsBuildOption = Exclude<WebpackBrowserSchemaForBuildFacade['fileReplacements'], undefined>;\ntype ScriptsBuildOption = Exclude<WebpackBrowserSchemaForBuildFacade['scripts'], undefined>;\n\nconst PRODUCTION_CONFIG_NAME = 'production';\nconst BUILD_TARGET_NAME = 'build';\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/angular/types.ts",
    "content": "/* eslint-disable */\n/**\n * This file was automatically generated by json-schema-to-typescript.\n * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,\n * and run json-schema-to-typescript to regenerate this file.\n */\n\n/**\n * File format version\n */\ntype FileVersion = number;\n/**\n * This interface was referenced by `undefined`'s JSON-Schema definition\n * via the `patternProperty` \"^(?:@[a-zA-Z0-9._-]+/)?[a-zA-Z0-9._-]+$\".\n */\nexport type Project = Project1 & {\n  cli?: {\n    [k: string]: unknown;\n  };\n  schematics?: SchematicOptions;\n  /**\n   * The prefix to apply to generated selectors.\n   */\n  prefix?: string;\n  /**\n   * Root of the project files.\n   */\n  root: string;\n  i18n?: I18N;\n  /**\n   * The root of the source files, assets and index.html file structure.\n   */\n  sourceRoot?: string;\n  /**\n   * Project type.\n   */\n  projectType: 'application' | 'library';\n  architect?: {\n    [k: string]:\n      | {\n          /**\n           * The builder used for this package.\n           */\n          builder: string;\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: {\n            [k: string]: unknown;\n          };\n          /**\n           * A map of alternative target options.\n           */\n          configurations?: {\n            [k: string]: {\n              [k: string]: unknown;\n            };\n          };\n        }\n      | {\n          builder?: '@angular/build:application';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: ApplicationSchemaForBuildFacade;\n          configurations?: {\n            [k: string]: ApplicationSchemaForBuildFacade;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:application';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: ApplicationSchemaForBuildFacade;\n          configurations?: {\n            [k: string]: ApplicationSchemaForBuildFacade;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:app-shell';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: AppShellTarget;\n          configurations?: {\n            [k: string]: AppShellTarget;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:browser';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: WebpackBrowserSchemaForBuildFacade;\n          configurations?: {\n            [k: string]: WebpackBrowserSchemaForBuildFacade;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:browser-esbuild';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: EsbuildBrowserSchemaForBuildFacade;\n          configurations?: {\n            [k: string]: EsbuildBrowserSchemaForBuildFacade;\n          };\n        }\n      | {\n          builder?: '@angular/build:dev-server';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: DevServerTarget;\n          configurations?: {\n            [k: string]: DevServerTarget;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:dev-server';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: DevServerTarget1;\n          configurations?: {\n            [k: string]: DevServerTarget1;\n          };\n        }\n      | {\n          builder?: '@angular/build:extract-i18n';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: ExtractI18NTarget;\n          configurations?: {\n            [k: string]: ExtractI18NTarget;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:extract-i18n';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: ExtractI18NTarget1;\n          configurations?: {\n            [k: string]: ExtractI18NTarget1;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:karma';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: KarmaTarget;\n          configurations?: {\n            [k: string]: KarmaTarget;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:jest';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: JestBrowserSchemaForBuildFacade;\n          configurations?: {\n            [k: string]: JestBrowserSchemaForBuildFacade;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:web-test-runner';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: WebTestRunnerTarget;\n          configurations?: {\n            [k: string]: WebTestRunnerTarget;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:prerender';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: PrerenderTarget;\n          configurations?: {\n            [k: string]: PrerenderTarget;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:ssr-dev-server';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: SSRDevServerTarget;\n          configurations?: {\n            [k: string]: SSRDevServerTarget;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:server';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: UniversalTarget;\n          configurations?: {\n            [k: string]: UniversalTarget;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:ng-packagr';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: NgPackagrTarget;\n          configurations?: {\n            [k: string]: NgPackagrTarget;\n          };\n        };\n  };\n  targets?: {\n    [k: string]:\n      | {\n          /**\n           * The builder used for this package.\n           */\n          builder: string;\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: {\n            [k: string]: unknown;\n          };\n          /**\n           * A map of alternative target options.\n           */\n          configurations?: {\n            [k: string]: {\n              [k: string]: unknown;\n            };\n          };\n        }\n      | {\n          builder?: '@angular/build:application';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: ApplicationSchemaForBuildFacade;\n          configurations?: {\n            [k: string]: ApplicationSchemaForBuildFacade;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:application';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: ApplicationSchemaForBuildFacade;\n          configurations?: {\n            [k: string]: ApplicationSchemaForBuildFacade;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:app-shell';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: AppShellTarget;\n          configurations?: {\n            [k: string]: AppShellTarget;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:browser';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: WebpackBrowserSchemaForBuildFacade;\n          configurations?: {\n            [k: string]: WebpackBrowserSchemaForBuildFacade;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:browser-esbuild';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: EsbuildBrowserSchemaForBuildFacade;\n          configurations?: {\n            [k: string]: EsbuildBrowserSchemaForBuildFacade;\n          };\n        }\n      | {\n          builder?: '@angular/build:dev-server';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: DevServerTarget;\n          configurations?: {\n            [k: string]: DevServerTarget;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:dev-server';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: DevServerTarget1;\n          configurations?: {\n            [k: string]: DevServerTarget1;\n          };\n        }\n      | {\n          builder?: '@angular/build:extract-i18n';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: ExtractI18NTarget;\n          configurations?: {\n            [k: string]: ExtractI18NTarget;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:extract-i18n';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: ExtractI18NTarget1;\n          configurations?: {\n            [k: string]: ExtractI18NTarget1;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:karma';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: KarmaTarget;\n          configurations?: {\n            [k: string]: KarmaTarget;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:jest';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: JestBrowserSchemaForBuildFacade;\n          configurations?: {\n            [k: string]: JestBrowserSchemaForBuildFacade;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:web-test-runner';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: WebTestRunnerTarget;\n          configurations?: {\n            [k: string]: WebTestRunnerTarget;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:prerender';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: PrerenderTarget;\n          configurations?: {\n            [k: string]: PrerenderTarget;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:ssr-dev-server';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: SSRDevServerTarget;\n          configurations?: {\n            [k: string]: SSRDevServerTarget;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:server';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: UniversalTarget;\n          configurations?: {\n            [k: string]: UniversalTarget;\n          };\n        }\n      | {\n          builder?: '@angular-devkit/build-angular:ng-packagr';\n          /**\n           * A default named configuration to use when a target configuration is not provided.\n           */\n          defaultConfiguration?: string;\n          options?: NgPackagrTarget;\n          configurations?: {\n            [k: string]: NgPackagrTarget;\n          };\n        };\n  };\n  /**\n   * This interface was referenced by `undefined`'s JSON-Schema definition\n   * via the `patternProperty` \"^[a-z]{1,3}-.*\".\n   */\n  [k: string]: unknown;\n};\ntype Project1 = {\n  [k: string]: unknown;\n};\ntype PrerenderTarget = {\n  [k: string]: unknown;\n};\n\nexport interface AngularCLIWorkspaceConfiguration {\n  $schema?: string;\n  version: FileVersion;\n  cli?: CliOptions;\n  schematics?: SchematicOptions;\n  /**\n   * Path where new projects will be created.\n   */\n  newProjectRoot?: string;\n  projects?: {\n    [k: string]: Project;\n  };\n}\ninterface CliOptions {\n  /**\n   * The list of schematic collections to use.\n   */\n  schematicCollections?: string[];\n  /**\n   * Specify which package manager tool to use.\n   */\n  packageManager?: 'npm' | 'cnpm' | 'yarn' | 'pnpm' | 'bun';\n  /**\n   * Control CLI specific console warnings\n   */\n  warnings?: {\n    /**\n     * Show a warning when the global version is newer than the local one.\n     */\n    versionMismatch?: boolean;\n  };\n  /**\n   * Share pseudonymous usage data with the Angular Team at Google.\n   */\n  analytics?: boolean | string;\n  /**\n   * Control disk cache.\n   */\n  cache?: {\n    /**\n     * Configure in which environment disk cache is enabled.\n     */\n    environment?: 'local' | 'ci' | 'all';\n    /**\n     * Configure whether disk caching is enabled.\n     */\n    enabled?: boolean;\n    /**\n     * Cache base path.\n     */\n    path?: string;\n  };\n}\ninterface SchematicOptions {\n  '@schematics/angular:application'?: AngularApplicationOptionsSchema;\n  '@schematics/angular:class'?: AngularClassOptionsSchema;\n  '@schematics/angular:component'?: AngularComponentOptionsSchema;\n  '@schematics/angular:directive'?: AngularDirectiveOptionsSchema;\n  '@schematics/angular:enum'?: AngularEnumOptionsSchema;\n  '@schematics/angular:guard'?: AngularGuardOptionsSchema;\n  '@schematics/angular:interceptor'?: AngularInterceptorOptionsSchema;\n  '@schematics/angular:interface'?: AngularInterfaceOptionsSchema;\n  '@schematics/angular:library'?: LibraryOptionsSchema;\n  '@schematics/angular:pipe'?: AngularPipeOptionsSchema;\n  '@schematics/angular:ng-new'?: AngularNgNewOptionsSchema;\n  '@schematics/angular:resolver'?: AngularResolverOptionsSchema;\n  '@schematics/angular:service'?: AngularServiceOptionsSchema;\n  '@schematics/angular:web-worker'?: AngularWebWorkerOptionsSchema;\n  [k: string]: unknown;\n}\n/**\n * Generates a new basic application definition in the \"projects\" subfolder of the workspace.\n */\ninterface AngularApplicationOptionsSchema {\n  /**\n   * The root directory of the new application.\n   */\n  projectRoot?: string;\n  /**\n   * The name of the new application.\n   */\n  name: string;\n  /**\n   * Include styles inline in the root component.ts file. Only CSS styles can be included inline. Default is false, meaning that an external styles file is created and referenced in the root component.ts file.\n   */\n  inlineStyle?: boolean;\n  /**\n   * Include template inline in the root component.ts file. Default is false, meaning that an external template file is created and referenced in the root component.ts file.\n   */\n  inlineTemplate?: boolean;\n  /**\n   * The view encapsulation strategy to use in the new application.\n   */\n  viewEncapsulation?: 'Emulated' | 'None' | 'ShadowDom';\n  /**\n   * Creates an application with routing enabled.\n   */\n  routing?: boolean;\n  /**\n   * A prefix to apply to generated selectors.\n   */\n  prefix?: string;\n  /**\n   * The file extension or preprocessor to use for style files.\n   */\n  style?: 'css' | 'scss' | 'sass' | 'less';\n  /**\n   * Do not create \"spec.ts\" test files for the application.\n   */\n  skipTests?: boolean;\n  /**\n   * Do not add dependencies to the \"package.json\" file.\n   */\n  skipPackageJson?: boolean;\n  /**\n   * Create a bare-bones project without any testing frameworks. (Use for learning purposes only.)\n   */\n  minimal?: boolean;\n  /**\n   * Skip installing dependency packages.\n   */\n  skipInstall?: boolean;\n  /**\n   * Creates an application with stricter bundle budgets settings.\n   */\n  strict?: boolean;\n  /**\n   * Creates an application based upon the standalone API, without NgModules.\n   */\n  standalone?: boolean;\n  /**\n   * Creates an application with Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering) enabled.\n   */\n  ssr?: boolean;\n  /**\n   * Creates a server application using the Server Routing and App Engine APIs (Developer Preview).\n   */\n  serverRouting?: boolean;\n  /**\n   * Create an application that does not utilize zone.js.\n   */\n  experimentalZoneless?: boolean;\n}\n/**\n * Creates a new, generic class definition in the given project.\n */\ninterface AngularClassOptionsSchema {\n  /**\n   * The name of the new class.\n   */\n  name: string;\n  /**\n   * The path at which to create the class, relative to the workspace root.\n   */\n  path?: string;\n  /**\n   * The name of the project.\n   */\n  project: string;\n  /**\n   * Do not create \"spec.ts\" test files for the new class.\n   */\n  skipTests?: boolean;\n  /**\n   * Adds a developer-defined type to the filename, in the format \"name.type.ts\".\n   */\n  type?: string;\n}\n/**\n * Creates a new, generic component definition in the given project.\n */\ninterface AngularComponentOptionsSchema {\n  /**\n   * The path at which to create the component file, relative to the current workspace. Default is a folder with the same name as the component in the project root.\n   */\n  path?: string;\n  /**\n   * The name of the project.\n   */\n  project: string;\n  /**\n   * The name of the component.\n   */\n  name: string;\n  /**\n   * Specifies if the style will contain `:host { display: block; }`.\n   */\n  displayBlock?: boolean;\n  /**\n   * Include styles inline in the component.ts file. Only CSS styles can be included inline. By default, an external styles file is created and referenced in the component.ts file.\n   */\n  inlineStyle?: boolean;\n  /**\n   * Include template inline in the component.ts file. By default, an external template file is created and referenced in the component.ts file.\n   */\n  inlineTemplate?: boolean;\n  /**\n   * Whether the generated component is standalone.\n   */\n  standalone?: boolean;\n  /**\n   * The view encapsulation strategy to use in the new component.\n   */\n  viewEncapsulation?: 'Emulated' | 'None' | 'ShadowDom';\n  /**\n   * The change detection strategy to use in the new component.\n   */\n  changeDetection?: 'Default' | 'OnPush';\n  /**\n   * The prefix to apply to the generated component selector.\n   */\n  prefix?: {\n    [k: string]: unknown;\n  } & string;\n  /**\n   * The file extension or preprocessor to use for style files, or 'none' to skip generating the style file.\n   */\n  style?: 'css' | 'scss' | 'sass' | 'less' | 'none';\n  /**\n   * Adds a developer-defined type to the filename, in the format \"name.type.ts\".\n   */\n  type?: string;\n  /**\n   * Do not create \"spec.ts\" test files for the new component.\n   */\n  skipTests?: boolean;\n  /**\n   * Create the new files at the top level of the current project.\n   */\n  flat?: boolean;\n  /**\n   * Do not import this component into the owning NgModule.\n   */\n  skipImport?: boolean;\n  /**\n   * The HTML selector to use for this component.\n   */\n  selector?: string;\n  /**\n   * Specifies if the component should have a selector or not.\n   */\n  skipSelector?: boolean;\n  /**\n   * The declaring NgModule.\n   */\n  module?: string;\n  /**\n   * The declaring NgModule exports this component.\n   */\n  export?: boolean;\n  /**\n   * Use default export for the component instead of a named export.\n   */\n  exportDefault?: boolean;\n}\n/**\n * Creates a new, generic directive definition in the given project.\n */\ninterface AngularDirectiveOptionsSchema {\n  /**\n   * The name of the new directive.\n   */\n  name: string;\n  /**\n   * The path at which to create the interface that defines the directive, relative to the workspace root.\n   */\n  path?: string;\n  /**\n   * The name of the project.\n   */\n  project: string;\n  /**\n   * A prefix to apply to generated selectors.\n   */\n  prefix?: {\n    [k: string]: unknown;\n  } & string;\n  /**\n   * Do not create \"spec.ts\" test files for the new class.\n   */\n  skipTests?: boolean;\n  /**\n   * Do not import this directive into the owning NgModule.\n   */\n  skipImport?: boolean;\n  /**\n   * The HTML selector to use for this directive.\n   */\n  selector?: string;\n  /**\n   * Whether the generated directive is standalone.\n   */\n  standalone?: boolean;\n  /**\n   * When true (the default), creates the new files at the top level of the current project.\n   */\n  flat?: boolean;\n  /**\n   * The declaring NgModule.\n   */\n  module?: string;\n  /**\n   * The declaring NgModule exports this directive.\n   */\n  export?: boolean;\n}\n/**\n * Generates a new, generic enum definition in the given project.\n */\ninterface AngularEnumOptionsSchema {\n  /**\n   * The name of the enum.\n   */\n  name: string;\n  /**\n   * The path at which to create the enum definition, relative to the current workspace.\n   */\n  path?: string;\n  /**\n   * The name of the project in which to create the enum. Default is the configured default project for the workspace.\n   */\n  project: string;\n  /**\n   * Adds a developer-defined type to the filename, in the format \"name.type.ts\".\n   */\n  type?: string;\n}\n/**\n * Generates a new, generic route guard definition in the given project.\n */\ninterface AngularGuardOptionsSchema {\n  /**\n   * The name of the new route guard.\n   */\n  name: string;\n  /**\n   * Do not create \"spec.ts\" test files for the new guard.\n   */\n  skipTests?: boolean;\n  /**\n   * When true (the default), creates the new files at the top level of the current project.\n   */\n  flat?: boolean;\n  /**\n   * The path at which to create the interface that defines the guard, relative to the current workspace.\n   */\n  path?: string;\n  /**\n   * The name of the project.\n   */\n  project: string;\n  /**\n   * Specifies whether to generate a guard as a function.\n   */\n  functional?: boolean;\n  /**\n   * Specifies which type of guard to create.\n   *\n   * @minItems 1\n   */\n  implements?: [\n    'CanActivate' | 'CanActivateChild' | 'CanDeactivate' | 'CanMatch',\n    ...('CanActivate' | 'CanActivateChild' | 'CanDeactivate' | 'CanMatch')[],\n  ];\n}\n/**\n * Creates a new, generic interceptor definition in the given project.\n */\ninterface AngularInterceptorOptionsSchema {\n  /**\n   * The name of the interceptor.\n   */\n  name: string;\n  /**\n   * The path at which to create the interceptor, relative to the workspace root.\n   */\n  path?: string;\n  /**\n   * The name of the project.\n   */\n  project: string;\n  /**\n   * When true (the default), creates files at the top level of the project.\n   */\n  flat?: boolean;\n  /**\n   * Do not create \"spec.ts\" test files for the new interceptor.\n   */\n  skipTests?: boolean;\n  /**\n   * Creates the interceptor as a `HttpInterceptorFn`.\n   */\n  functional?: boolean;\n}\n/**\n * Creates a new, generic interface definition in the given project.\n */\ninterface AngularInterfaceOptionsSchema {\n  /**\n   * The name of the interface.\n   */\n  name: string;\n  /**\n   * The path at which to create the interface, relative to the workspace root.\n   */\n  path?: string;\n  /**\n   * The name of the project.\n   */\n  project: string;\n  /**\n   * A prefix to apply to generated selectors.\n   */\n  prefix?: string;\n  /**\n   * Adds a developer-defined type to the filename, in the format \"name.type.ts\".\n   */\n  type?: string;\n}\n/**\n * Creates a new, generic library project in the current workspace.\n */\ninterface LibraryOptionsSchema {\n  /**\n   * The name of the library.\n   */\n  name: string;\n  /**\n   * The path at which to create the library's public API file, relative to the workspace root.\n   */\n  entryFile?: string;\n  /**\n   * A prefix to apply to generated selectors.\n   */\n  prefix?: string;\n  /**\n   * Do not add dependencies to the \"package.json\" file.\n   */\n  skipPackageJson?: boolean;\n  /**\n   * Do not install dependency packages.\n   */\n  skipInstall?: boolean;\n  /**\n   * Do not update \"tsconfig.json\" to add a path mapping for the new library. The path mapping is needed to use the library in an app, but can be disabled here to simplify development.\n   */\n  skipTsConfig?: boolean;\n  /**\n   * The root directory of the new library.\n   */\n  projectRoot?: string;\n  /**\n   * Creates a library based upon the standalone API, without NgModules.\n   */\n  standalone?: boolean;\n}\n/**\n * Creates a new, generic pipe definition in the given project.\n */\ninterface AngularPipeOptionsSchema {\n  /**\n   * The name of the pipe.\n   */\n  name: string;\n  /**\n   * The path at which to create the pipe, relative to the workspace root.\n   */\n  path?: string;\n  /**\n   * The name of the project.\n   */\n  project: string;\n  /**\n   * When true (the default) creates files at the top level of the project.\n   */\n  flat?: boolean;\n  /**\n   * Do not create \"spec.ts\" test files for the new pipe.\n   */\n  skipTests?: boolean;\n  /**\n   * Do not import this pipe into the owning NgModule.\n   */\n  skipImport?: boolean;\n  /**\n   * Whether the generated pipe is standalone.\n   */\n  standalone?: boolean;\n  /**\n   * The declaring NgModule.\n   */\n  module?: string;\n  /**\n   * The declaring NgModule exports this pipe.\n   */\n  export?: boolean;\n}\n/**\n * Creates a new project by combining the workspace and application schematics.\n */\ninterface AngularNgNewOptionsSchema {\n  /**\n   * The directory name to create the workspace in.\n   */\n  directory?: string;\n  /**\n   * The name of the new workspace and initial project.\n   */\n  name: string;\n  /**\n   * Do not install dependency packages.\n   */\n  skipInstall?: boolean;\n  /**\n   * Do not initialize a git repository.\n   */\n  skipGit?: boolean;\n  /**\n   * Initial git repository commit information.\n   */\n  commit?:\n    | boolean\n    | {\n        name: string;\n        email: string;\n        message?: string;\n        [k: string]: unknown;\n      };\n  /**\n   * The path where new projects will be created, relative to the new workspace root.\n   */\n  newProjectRoot?: string;\n  /**\n   * Include styles inline in the component TS file. By default, an external styles file is created and referenced in the component TypeScript file.\n   */\n  inlineStyle?: boolean;\n  /**\n   * Include template inline in the component TS file. By default, an external template file is created and referenced in the component TypeScript file.\n   */\n  inlineTemplate?: boolean;\n  /**\n   * The view encapsulation strategy to use in the initial project.\n   */\n  viewEncapsulation?: 'Emulated' | 'None' | 'ShadowDom';\n  /**\n   * The version of the Angular CLI to use.\n   */\n  version: string;\n  /**\n   * Enable routing in the initial project.\n   */\n  routing?: boolean;\n  /**\n   * The prefix to apply to generated selectors for the initial project.\n   */\n  prefix?: string;\n  /**\n   * The file extension or preprocessor to use for style files.\n   */\n  style?: 'css' | 'scss' | 'sass' | 'less';\n  /**\n   * Do not generate \"spec.ts\" test files for the new project.\n   */\n  skipTests?: boolean;\n  /**\n   * Create a new initial application project in the 'src' folder of the new workspace. When false, creates an empty workspace with no initial application. You can then use the generate application command so that all applications are created in the projects folder.\n   */\n  createApplication?: boolean;\n  /**\n   * Create a workspace without any testing frameworks. (Use for learning purposes only.)\n   */\n  minimal?: boolean;\n  /**\n   * Creates a workspace with stricter type checking and stricter bundle budgets settings. This setting helps improve maintainability and catch bugs ahead of time. For more information, see https://angular.dev/tools/cli/template-typecheck#strict-mode\n   */\n  strict?: boolean;\n  /**\n   * The package manager used to install dependencies.\n   */\n  packageManager?: 'npm' | 'yarn' | 'pnpm' | 'cnpm' | 'bun';\n  /**\n   * Creates an application based upon the standalone API, without NgModules.\n   */\n  standalone?: boolean;\n  /**\n   * Creates an application with Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering) enabled.\n   */\n  ssr?: boolean;\n  /**\n   * Creates a server application using the Server Routing and App Engine APIs (Developer Preview).\n   */\n  serverRouting?: boolean;\n  /**\n   * Create an application that does not utilize zone.js.\n   */\n  experimentalZoneless?: boolean;\n}\n/**\n * Generates a new, generic resolver definition in the given project.\n */\ninterface AngularResolverOptionsSchema {\n  /**\n   * The name of the new resolver.\n   */\n  name: string;\n  /**\n   * Do not create \"spec.ts\" test files for the new resolver.\n   */\n  skipTests?: boolean;\n  /**\n   * When true (the default), creates the new files at the top level of the current project.\n   */\n  flat?: boolean;\n  /**\n   * Creates the resolver as a `ResolveFn`.\n   */\n  functional?: boolean;\n  /**\n   * The path at which to create the interface that defines the resolver, relative to the current workspace.\n   */\n  path?: string;\n  /**\n   * The name of the project.\n   */\n  project: string;\n}\n/**\n * Creates a new, generic service definition in the given project.\n */\ninterface AngularServiceOptionsSchema {\n  /**\n   * The name of the service.\n   */\n  name: string;\n  /**\n   * The path at which to create the service, relative to the workspace root.\n   */\n  path?: string;\n  /**\n   * The name of the project.\n   */\n  project: string;\n  /**\n   * When true (the default), creates files at the top level of the project.\n   */\n  flat?: boolean;\n  /**\n   * Do not create \"spec.ts\" test files for the new service.\n   */\n  skipTests?: boolean;\n}\n/**\n * Creates a new, generic web worker definition in the given project.\n */\ninterface AngularWebWorkerOptionsSchema {\n  /**\n   * The path at which to create the worker file, relative to the current workspace.\n   */\n  path?: string;\n  /**\n   * The name of the project.\n   */\n  project: string;\n  /**\n   * The name of the worker.\n   */\n  name: string;\n  /**\n   * Add a worker creation snippet in a sibling file of the same name.\n   */\n  snippet?: boolean;\n}\n/**\n * Project i18n options\n */\ninterface I18N {\n  sourceLocale?:\n    | string\n    | {\n        /**\n         * Specifies the locale code of the source locale\n         */\n        code?: string;\n        /**\n         * HTML base HREF to use for the locale (defaults to the locale code)\n         */\n        baseHref?: string;\n      };\n  locales?: {\n    /**\n     * This interface was referenced by `undefined`'s JSON-Schema definition\n     * via the `patternProperty` \"^[a-zA-Z]{2,3}(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-[a-zA-Z]{5,8})?(-x(-[a-zA-Z0-9]{1,8})+)?$\".\n     */\n    [k: string]:\n      | string\n      | string[]\n      | {\n          translation?: string | string[];\n          /**\n           * HTML base HREF to use for the locale (defaults to the locale code)\n           */\n          baseHref?: string;\n        };\n  };\n}\n/**\n * Application builder target options\n */\ninterface ApplicationSchemaForBuildFacade {\n  /**\n   * List of static application assets.\n   */\n  assets?: (\n    | {\n        /**\n         * Allow glob patterns to follow symlink directories. This allows subdirectories of the symlink to be searched.\n         */\n        followSymlinks?: boolean;\n        /**\n         * The pattern to match.\n         */\n        glob: string;\n        /**\n         * The input directory path in which to apply 'glob'. Defaults to the project root.\n         */\n        input: string;\n        /**\n         * An array of globs to ignore.\n         */\n        ignore?: string[];\n        /**\n         * Absolute path within the output.\n         */\n        output?: string;\n      }\n    | string\n  )[];\n  /**\n   * The full path for the browser entry point to the application, relative to the current workspace.\n   */\n  browser: string;\n  /**\n   * The full path for the server entry point to the application, relative to the current workspace.\n   */\n  server?: (string | false) & string;\n  /**\n   * A list of polyfills to include in the build. Can be a full path for a file, relative to the current workspace or module specifier. Example: 'zone.js'.\n   */\n  polyfills?: string[];\n  /**\n   * The full path for the TypeScript configuration file, relative to the current workspace.\n   */\n  tsConfig: string;\n  /**\n   * Customize the base path for the URLs of resources in 'index.html' and component stylesheets. This option is only necessary for specific deployment scenarios, such as with Angular Elements or when utilizing different CDN locations.\n   */\n  deployUrl?: string;\n  /**\n   * Security features to protect against XSS and other common attacks\n   */\n  security?: {\n    /**\n     * Enables automatic generation of a hash-based Strict Content Security Policy (https://web.dev/articles/strict-csp#choose-hash) based on scripts in index.html. Will default to true once we are out of experimental/preview phases.\n     */\n    autoCsp?:\n      | {\n          /**\n           * Include the `unsafe-eval` directive (https://web.dev/articles/strict-csp#remove-eval) in the auto-CSP. Please only enable this if you are absolutely sure that you need to, as allowing calls to eval will weaken the XSS defenses provided by the auto-CSP.\n           */\n          unsafeEval?: boolean;\n        }\n      | boolean;\n  };\n  /**\n   * Global scripts to be included in the build.\n   */\n  scripts?: (\n    | {\n        /**\n         * The file to include.\n         */\n        input: string;\n        /**\n         * The bundle name for this extra entry point.\n         */\n        bundleName?: string;\n        /**\n         * If the bundle will be referenced in the HTML file.\n         */\n        inject?: boolean;\n      }\n    | string\n  )[];\n  /**\n   * Global styles to be included in the build.\n   */\n  styles?: (\n    | {\n        /**\n         * The file to include.\n         */\n        input: string;\n        /**\n         * The bundle name for this extra entry point.\n         */\n        bundleName?: string;\n        /**\n         * If the bundle will be referenced in the HTML file.\n         */\n        inject?: boolean;\n      }\n    | string\n  )[];\n  /**\n   * The stylesheet language to use for the application's inline component styles.\n   */\n  inlineStyleLanguage?: 'css' | 'less' | 'sass' | 'scss';\n  /**\n   * Options to pass to style preprocessors.\n   */\n  stylePreprocessorOptions?: {\n    /**\n     * Paths to include. Paths will be resolved to workspace root.\n     */\n    includePaths?: string[];\n    /**\n     * Options to pass to the sass preprocessor.\n     */\n    sass?: {\n      /**\n       * A set of deprecations to treat as fatal. If a deprecation warning of any provided type is encountered during compilation, the compiler will error instead. If a Version is provided, then all deprecations that were active in that compiler version will be treated as fatal.\n       */\n      fatalDeprecations?: string[];\n      /**\n       *  A set of active deprecations to ignore. If a deprecation warning of any provided type is encountered during compilation, the compiler will ignore it instead.\n       */\n      silenceDeprecations?: string[];\n      /**\n       * A set of future deprecations to opt into early. Future deprecations passed here will be treated as active by the compiler, emitting warnings as necessary.\n       */\n      futureDeprecations?: string[];\n    };\n  };\n  /**\n   * Exclude the listed external dependencies from being bundled into the bundle. Instead, the created bundle relies on these dependencies to be available during runtime.\n   */\n  externalDependencies?: string[];\n  /**\n   * Automatically clear the terminal screen during rebuilds.\n   */\n  clearScreen?: boolean;\n  /**\n   * Enables optimization of the build output. Including minification of scripts and styles, tree-shaking, dead-code elimination, inlining of critical CSS and fonts inlining. For more information, see https://angular.dev/reference/configs/workspace-config#optimization-configuration.\n   */\n  optimization?:\n    | {\n        /**\n         * Enables optimization of the scripts output.\n         */\n        scripts?: boolean;\n        /**\n         * Enables optimization of the styles output.\n         */\n        styles?:\n          | {\n              /**\n               * Minify CSS definitions by removing extraneous whitespace and comments, merging identifiers and minimizing values.\n               */\n              minify?: boolean;\n              /**\n               * Extract and inline critical CSS definitions to improve first paint time.\n               */\n              inlineCritical?: boolean;\n              /**\n               * Remove comments in global CSS that contains '@license' or '@preserve' or that starts with '//!' or '/*!'.\n               */\n              removeSpecialComments?: boolean;\n            }\n          | boolean;\n        /**\n         * Enables optimization for fonts. This option requires internet access. `HTTPS_PROXY` environment variable can be used to specify a proxy server.\n         */\n        fonts?:\n          | {\n              /**\n               * Reduce render blocking requests by inlining external Google Fonts and Adobe Fonts CSS definitions in the application's HTML index file. This option requires internet access. `HTTPS_PROXY` environment variable can be used to specify a proxy server.\n               */\n              inline?: boolean;\n            }\n          | boolean;\n      }\n    | boolean;\n  /**\n   * Defines the type of loader to use with a specified file extension when used with a JavaScript `import`. `text` inlines the content as a string; `binary` inlines the content as a Uint8Array; `file` emits the file and provides the runtime location of the file; `empty` considers the content to be empty and not include it in bundles.\n   */\n  loader?: {\n    /**\n     * This interface was referenced by `undefined`'s JSON-Schema definition\n     * via the `patternProperty` \"^\\.\\S+$\".\n     */\n    [k: string]: 'text' | 'binary' | 'file' | 'empty';\n  };\n  /**\n   * Defines global identifiers that will be replaced with a specified constant value when found in any JavaScript or TypeScript code including libraries. The value will be used directly. String values must be put in quotes. Identifiers within Angular metadata such as Component Decorators will not be replaced.\n   */\n  define?: {\n    [k: string]: string;\n  };\n  /**\n   * Replace compilation source files with other compilation source files in the build.\n   */\n  fileReplacements?: FileReplacement[];\n  /**\n   * Specify the output path relative to workspace root.\n   */\n  outputPath:\n    | {\n        /**\n         * Specify the output path relative to workspace root.\n         */\n        base: string;\n        /**\n         * The output directory name of your browser build within the output path base. Defaults to 'browser'.\n         */\n        browser?: string;\n        /**\n         * The output directory name of your server build within the output path base. Defaults to 'server'.\n         */\n        server?: string;\n        /**\n         * The output directory name of your media files within the output browser directory. Defaults to 'media'.\n         */\n        media?: string;\n      }\n    | string;\n  /**\n   * Build using Ahead of Time compilation.\n   */\n  aot?: boolean;\n  /**\n   * Output source maps for scripts and styles. For more information, see https://angular.dev/reference/configs/workspace-config#source-map-configuration.\n   */\n  sourceMap?:\n    | {\n        /**\n         * Output source maps for all scripts.\n         */\n        scripts?: boolean;\n        /**\n         * Output source maps for all styles.\n         */\n        styles?: boolean;\n        /**\n         * Output source maps used for error reporting tools.\n         */\n        hidden?: boolean;\n        /**\n         * Resolve vendor packages source maps.\n         */\n        vendor?: boolean;\n      }\n    | boolean;\n  /**\n   * Base url for the application being built.\n   */\n  baseHref?: string;\n  /**\n   * Adds more details to output logging.\n   */\n  verbose?: boolean;\n  /**\n   * Log progress to the console while building.\n   */\n  progress?: boolean;\n  /**\n   * How to handle missing translations for i18n.\n   */\n  i18nMissingTranslation?: 'warning' | 'error' | 'ignore';\n  /**\n   * How to handle duplicate translations for i18n.\n   */\n  i18nDuplicateTranslation?: 'warning' | 'error' | 'ignore';\n  /**\n   * Translate the bundles in one or more locales.\n   */\n  localize?: boolean | [string, ...string[]];\n  /**\n   * Run build when files change.\n   */\n  watch?: boolean;\n  /**\n   * Define the output filename cache-busting hashing mode.\n   */\n  outputHashing?: 'none' | 'all' | 'media' | 'bundles';\n  /**\n   * Enable and define the file watching poll time period in milliseconds.\n   */\n  poll?: number;\n  /**\n   * Delete the output path before building.\n   */\n  deleteOutputPath?: boolean;\n  /**\n   * Do not use the real path when resolving modules. If unset then will default to `true` if NodeJS option --preserve-symlinks is set.\n   */\n  preserveSymlinks?: boolean;\n  /**\n   * Extract all licenses in a separate file.\n   */\n  extractLicenses?: boolean;\n  /**\n   * Use file name for lazy loaded chunks.\n   */\n  namedChunks?: boolean;\n  /**\n   * Enables the use of subresource integrity validation.\n   */\n  subresourceIntegrity?: boolean;\n  /**\n   * Generates a service worker configuration.\n   */\n  serviceWorker?: string | false;\n  /**\n   * Configures the generation of the application's HTML index.\n   */\n  index:\n    | string\n    | {\n        /**\n         * The path of a file to use for the application's generated HTML index.\n         */\n        input: string;\n        /**\n         * The output path of the application's generated HTML index file. The full provided path will be used and will be considered relative to the application's configured output path.\n         */\n        output?: string;\n        /**\n         * Generates 'preload', 'modulepreload', and 'preconnect' link elements for initial application files and resources.\n         */\n        preloadInitial?: boolean;\n        [k: string]: unknown;\n      }\n    | false;\n  /**\n   * Generates a 'stats.json' file which can be analyzed with https://esbuild.github.io/analyze/.\n   */\n  statsJson?: boolean;\n  /**\n   * Budget thresholds to ensure parts of your application stay within boundaries which you set.\n   */\n  budgets?: Budget[];\n  /**\n   * TypeScript configuration for Web Worker modules.\n   */\n  webWorkerTsConfig?: string;\n  /**\n   * Define the crossorigin attribute setting of elements that provide CORS support.\n   */\n  crossOrigin?: 'none' | 'anonymous' | 'use-credentials';\n  /**\n   * A list of CommonJS or AMD packages that are allowed to be used without a build time warning. Use `'*'` to allow all.\n   */\n  allowedCommonJsDependencies?: string[];\n  /**\n   * Prerender (SSG) pages of your application during build time.\n   */\n  prerender?:\n    | boolean\n    | {\n        /**\n         * The path to a file that contains a list of all routes to prerender, separated by newlines. This option is useful if you want to prerender routes with parameterized URLs.\n         */\n        routesFile?: string;\n        /**\n         * Whether the builder should process the Angular Router configuration to find all unparameterized routes and prerender them.\n         */\n        discoverRoutes?: boolean;\n      };\n  /**\n   * Server side render (SSR) pages of your application during runtime.\n   */\n  ssr?:\n    | boolean\n    | {\n        /**\n         * The server entry-point that when executed will spawn the web server.\n         */\n        entry?: string;\n        /**\n         * Specifies the platform for which the server bundle is generated. This affects the APIs and modules available in the server-side code.\n         *\n         * - `node`:  (Default) Generates a bundle optimized for Node.js environments.\n         * - `neutral`: Generates a platform-neutral bundle suitable for environments like edge workers, and other serverless platforms. This option avoids using Node.js-specific APIs, making the bundle more portable.\n         *\n         * Please note that this feature does not provide polyfills for Node.js modules. Additionally, it is experimental, and the schematics may undergo changes in future versions.\n         */\n        experimentalPlatform?: 'node' | 'neutral';\n      };\n  /**\n   * Generates an application shell during build time.\n   */\n  appShell?: boolean;\n  /**\n   * Defines the build output target. 'static': Generates a static site for deployment on any static hosting service. 'server': Produces an application designed for deployment on a server that supports server-side rendering (SSR).\n   */\n  outputMode?: 'static' | 'server';\n}\ninterface FileReplacement {\n  replace: string;\n  with: string;\n}\ninterface Budget {\n  /**\n   * The type of budget.\n   */\n  type: 'all' | 'allScript' | 'any' | 'anyScript' | 'anyComponentStyle' | 'bundle' | 'initial';\n  /**\n   * The name of the bundle.\n   */\n  name?: string;\n  /**\n   * The baseline size for comparison.\n   */\n  baseline?: string;\n  /**\n   * The maximum threshold for warning relative to the baseline.\n   */\n  maximumWarning?: string;\n  /**\n   * The maximum threshold for error relative to the baseline.\n   */\n  maximumError?: string;\n  /**\n   * The minimum threshold for warning relative to the baseline.\n   */\n  minimumWarning?: string;\n  /**\n   * The minimum threshold for error relative to the baseline.\n   */\n  minimumError?: string;\n  /**\n   * The threshold for warning relative to the baseline (min & max).\n   */\n  warning?: string;\n  /**\n   * The threshold for error relative to the baseline (min & max).\n   */\n  error?: string;\n}\n/**\n * App Shell target options for Build Facade.\n */\ninterface AppShellTarget {\n  /**\n   * A browser builder target use for rendering the application shell in the format of `project:target[:configuration]`. You can also pass in more than one configuration name as a comma-separated list. Example: `project:target:production,staging`.\n   */\n  browserTarget: string;\n  /**\n   * A server builder target use for rendering the application shell in the format of `project:target[:configuration]`. You can also pass in more than one configuration name as a comma-separated list. Example: `project:target:production,staging`.\n   */\n  serverTarget: string;\n  /**\n   * Script that exports the Server AppModule to render. This should be the main JavaScript outputted by the server target. By default we will resolve the outputPath of the serverTarget and find a bundle named 'main' in it (whether or not there's a hash tag).\n   */\n  appModuleBundle?: string;\n  /**\n   * The route to render.\n   */\n  route?: string;\n  /**\n   * The input path for the index.html file. By default uses the output index.html of the browser target.\n   */\n  inputIndexPath?: string;\n  /**\n   * The output path of the index.html file. By default will overwrite the input file.\n   */\n  outputIndexPath?: string;\n}\n/**\n * Browser target options\n */\nexport interface WebpackBrowserSchemaForBuildFacade {\n  /**\n   * List of static application assets.\n   */\n  assets?: (\n    | {\n        /**\n         * Allow glob patterns to follow symlink directories. This allows subdirectories of the symlink to be searched.\n         */\n        followSymlinks?: boolean;\n        /**\n         * The pattern to match.\n         */\n        glob: string;\n        /**\n         * The input directory path in which to apply 'glob'. Defaults to the project root.\n         */\n        input: string;\n        /**\n         * An array of globs to ignore.\n         */\n        ignore?: string[];\n        /**\n         * Absolute path within the output.\n         */\n        output?: string;\n      }\n    | string\n  )[];\n  /**\n   * The full path for the main entry point to the app, relative to the current workspace.\n   */\n  main: string;\n  /**\n   * Polyfills to be included in the build.\n   */\n  polyfills?: string[] | string;\n  /**\n   * The full path for the TypeScript configuration file, relative to the current workspace.\n   */\n  tsConfig: string;\n  /**\n   * Global scripts to be included in the build.\n   */\n  scripts?: (\n    | {\n        /**\n         * The file to include.\n         */\n        input: string;\n        /**\n         * The bundle name for this extra entry point.\n         */\n        bundleName?: string;\n        /**\n         * If the bundle will be referenced in the HTML file.\n         */\n        inject?: boolean;\n      }\n    | string\n  )[];\n  /**\n   * Global styles to be included in the build.\n   */\n  styles?: (\n    | {\n        /**\n         * The file to include.\n         */\n        input: string;\n        /**\n         * The bundle name for this extra entry point.\n         */\n        bundleName?: string;\n        /**\n         * If the bundle will be referenced in the HTML file.\n         */\n        inject?: boolean;\n      }\n    | string\n  )[];\n  /**\n   * The stylesheet language to use for the application's inline component styles.\n   */\n  inlineStyleLanguage?: 'css' | 'less' | 'sass' | 'scss';\n  /**\n   * Options to pass to style preprocessors.\n   */\n  stylePreprocessorOptions?: {\n    /**\n     * Paths to include. Paths will be resolved to workspace root.\n     */\n    includePaths?: string[];\n  };\n  /**\n   * Enables optimization of the build output. Including minification of scripts and styles, tree-shaking, dead-code elimination, inlining of critical CSS and fonts inlining. For more information, see https://angular.dev/reference/configs/workspace-config#optimization-configuration.\n   */\n  optimization?:\n    | {\n        /**\n         * Enables optimization of the scripts output.\n         */\n        scripts?: boolean;\n        /**\n         * Enables optimization of the styles output.\n         */\n        styles?:\n          | {\n              /**\n               * Minify CSS definitions by removing extraneous whitespace and comments, merging identifiers and minimizing values.\n               */\n              minify?: boolean;\n              /**\n               * Extract and inline critical CSS definitions to improve first paint time.\n               */\n              inlineCritical?: boolean;\n            }\n          | boolean;\n        /**\n         * Enables optimization for fonts. This option requires internet access. `HTTPS_PROXY` environment variable can be used to specify a proxy server.\n         */\n        fonts?:\n          | {\n              /**\n               * Reduce render blocking requests by inlining external Google Fonts and Adobe Fonts CSS definitions in the application's HTML index file. This option requires internet access. `HTTPS_PROXY` environment variable can be used to specify a proxy server.\n               */\n              inline?: boolean;\n            }\n          | boolean;\n      }\n    | boolean;\n  /**\n   * Replace compilation source files with other compilation source files in the build.\n   */\n  fileReplacements?: (\n    | {\n        src: string;\n        replaceWith: string;\n      }\n    | {\n        replace: string;\n        with: string;\n      }\n  )[];\n  /**\n   * The full path for the new output directory, relative to the current workspace.\n   */\n  outputPath: string;\n  /**\n   * The path where style resources will be placed, relative to outputPath.\n   */\n  resourcesOutputPath?: string;\n  /**\n   * Build using Ahead of Time compilation.\n   */\n  aot?: boolean;\n  /**\n   * Output source maps for scripts and styles. For more information, see https://angular.dev/reference/configs/workspace-config#source-map-configuration.\n   */\n  sourceMap?:\n    | {\n        /**\n         * Output source maps for all scripts.\n         */\n        scripts?: boolean;\n        /**\n         * Output source maps for all styles.\n         */\n        styles?: boolean;\n        /**\n         * Output source maps used for error reporting tools.\n         */\n        hidden?: boolean;\n        /**\n         * Resolve vendor packages source maps.\n         */\n        vendor?: boolean;\n      }\n    | boolean;\n  /**\n   * Generate a separate bundle containing only vendor libraries. This option should only be used for development to reduce the incremental compilation time.\n   */\n  vendorChunk?: boolean;\n  /**\n   * Generate a separate bundle containing code used across multiple bundles.\n   */\n  commonChunk?: boolean;\n  /**\n   * Base url for the application being built.\n   */\n  baseHref?: string;\n  /**\n   * Customize the base path for the URLs of resources in 'index.html' and component stylesheets. This option is only necessary for specific deployment scenarios, such as with Angular Elements or when utilizing different CDN locations.\n   */\n  deployUrl?: string;\n  /**\n   * Adds more details to output logging.\n   */\n  verbose?: boolean;\n  /**\n   * Log progress to the console while building.\n   */\n  progress?: boolean;\n  /**\n   * How to handle missing translations for i18n.\n   */\n  i18nMissingTranslation?: 'warning' | 'error' | 'ignore';\n  /**\n   * How to handle duplicate translations for i18n.\n   */\n  i18nDuplicateTranslation?: 'warning' | 'error' | 'ignore';\n  /**\n   * Translate the bundles in one or more locales.\n   */\n  localize?: boolean | [string, ...string[]];\n  /**\n   * Run build when files change.\n   */\n  watch?: boolean;\n  /**\n   * Define the output filename cache-busting hashing mode.\n   */\n  outputHashing?: 'none' | 'all' | 'media' | 'bundles';\n  /**\n   * Enable and define the file watching poll time period in milliseconds.\n   */\n  poll?: number;\n  /**\n   * Delete the output path before building.\n   */\n  deleteOutputPath?: boolean;\n  /**\n   * Do not use the real path when resolving modules. If unset then will default to `true` if NodeJS option --preserve-symlinks is set.\n   */\n  preserveSymlinks?: boolean;\n  /**\n   * Extract all licenses in a separate file.\n   */\n  extractLicenses?: boolean;\n  /**\n   * Enables advanced build optimizations when using the 'aot' option.\n   */\n  buildOptimizer?: boolean;\n  /**\n   * Use file name for lazy loaded chunks.\n   */\n  namedChunks?: boolean;\n  /**\n   * Enables the use of subresource integrity validation.\n   */\n  subresourceIntegrity?: boolean;\n  /**\n   * Generates a service worker config for production builds.\n   */\n  serviceWorker?: boolean;\n  /**\n   * Path to ngsw-config.json.\n   */\n  ngswConfigPath?: string;\n  /**\n   * Configures the generation of the application's HTML index.\n   */\n  index:\n    | string\n    | {\n        /**\n         * The path of a file to use for the application's generated HTML index.\n         */\n        input: string;\n        /**\n         * The output path of the application's generated HTML index file. The full provided path will be used and will be considered relative to the application's configured output path.\n         */\n        output?: string;\n        [k: string]: unknown;\n      };\n  /**\n   * Generates a 'stats.json' file which can be analyzed using tools such as 'webpack-bundle-analyzer'.\n   */\n  statsJson?: boolean;\n  /**\n   * Budget thresholds to ensure parts of your application stay within boundaries which you set.\n   */\n  budgets?: Budget1[];\n  /**\n   * TypeScript configuration for Web Worker modules.\n   */\n  webWorkerTsConfig?: string;\n  /**\n   * Define the crossorigin attribute setting of elements that provide CORS support.\n   */\n  crossOrigin?: 'none' | 'anonymous' | 'use-credentials';\n  /**\n   * A list of CommonJS or AMD packages that are allowed to be used without a build time warning. Use `'*'` to allow all.\n   */\n  allowedCommonJsDependencies?: string[];\n}\ninterface Budget1 {\n  /**\n   * The type of budget.\n   */\n  type: 'all' | 'allScript' | 'any' | 'anyScript' | 'anyComponentStyle' | 'bundle' | 'initial';\n  /**\n   * The name of the bundle.\n   */\n  name?: string;\n  /**\n   * The baseline size for comparison.\n   */\n  baseline?: string;\n  /**\n   * The maximum threshold for warning relative to the baseline.\n   */\n  maximumWarning?: string;\n  /**\n   * The maximum threshold for error relative to the baseline.\n   */\n  maximumError?: string;\n  /**\n   * The minimum threshold for warning relative to the baseline.\n   */\n  minimumWarning?: string;\n  /**\n   * The minimum threshold for error relative to the baseline.\n   */\n  minimumError?: string;\n  /**\n   * The threshold for warning relative to the baseline (min & max).\n   */\n  warning?: string;\n  /**\n   * The threshold for error relative to the baseline (min & max).\n   */\n  error?: string;\n}\n/**\n * Browser target options\n */\ninterface EsbuildBrowserSchemaForBuildFacade {\n  /**\n   * List of static application assets.\n   */\n  assets?: (\n    | {\n        /**\n         * Allow glob patterns to follow symlink directories. This allows subdirectories of the symlink to be searched.\n         */\n        followSymlinks?: boolean;\n        /**\n         * The pattern to match.\n         */\n        glob: string;\n        /**\n         * The input directory path in which to apply 'glob'. Defaults to the project root.\n         */\n        input: string;\n        /**\n         * An array of globs to ignore.\n         */\n        ignore?: string[];\n        /**\n         * Absolute path within the output.\n         */\n        output?: string;\n      }\n    | string\n  )[];\n  /**\n   * The full path for the main entry point to the app, relative to the current workspace.\n   */\n  main: string;\n  /**\n   * Polyfills to be included in the build.\n   */\n  polyfills?: string[] | string;\n  /**\n   * The full path for the TypeScript configuration file, relative to the current workspace.\n   */\n  tsConfig: string;\n  /**\n   * Global scripts to be included in the build.\n   */\n  scripts?: (\n    | {\n        /**\n         * The file to include.\n         */\n        input: string;\n        /**\n         * The bundle name for this extra entry point.\n         */\n        bundleName?: string;\n        /**\n         * If the bundle will be referenced in the HTML file.\n         */\n        inject?: boolean;\n      }\n    | string\n  )[];\n  /**\n   * Global styles to be included in the build.\n   */\n  styles?: (\n    | {\n        /**\n         * The file to include.\n         */\n        input: string;\n        /**\n         * The bundle name for this extra entry point.\n         */\n        bundleName?: string;\n        /**\n         * If the bundle will be referenced in the HTML file.\n         */\n        inject?: boolean;\n      }\n    | string\n  )[];\n  /**\n   * The stylesheet language to use for the application's inline component styles.\n   */\n  inlineStyleLanguage?: 'css' | 'less' | 'sass' | 'scss';\n  /**\n   * Options to pass to style preprocessors.\n   */\n  stylePreprocessorOptions?: {\n    /**\n     * Paths to include. Paths will be resolved to workspace root.\n     */\n    includePaths?: string[];\n  };\n  /**\n   * Exclude the listed external dependencies from being bundled into the bundle. Instead, the created bundle relies on these dependencies to be available during runtime.\n   */\n  externalDependencies?: string[];\n  /**\n   * Enables optimization of the build output. Including minification of scripts and styles, tree-shaking, dead-code elimination, inlining of critical CSS and fonts inlining. For more information, see https://angular.dev/reference/configs/workspace-config#optimization-configuration.\n   */\n  optimization?:\n    | {\n        /**\n         * Enables optimization of the scripts output.\n         */\n        scripts?: boolean;\n        /**\n         * Enables optimization of the styles output.\n         */\n        styles?:\n          | {\n              /**\n               * Minify CSS definitions by removing extraneous whitespace and comments, merging identifiers and minimizing values.\n               */\n              minify?: boolean;\n              /**\n               * Extract and inline critical CSS definitions to improve first paint time.\n               */\n              inlineCritical?: boolean;\n            }\n          | boolean;\n        /**\n         * Enables optimization for fonts. This option requires internet access. `HTTPS_PROXY` environment variable can be used to specify a proxy server.\n         */\n        fonts?:\n          | {\n              /**\n               * Reduce render blocking requests by inlining external Google Fonts and Adobe Fonts CSS definitions in the application's HTML index file. This option requires internet access. `HTTPS_PROXY` environment variable can be used to specify a proxy server.\n               */\n              inline?: boolean;\n            }\n          | boolean;\n      }\n    | boolean;\n  /**\n   * Replace compilation source files with other compilation source files in the build.\n   */\n  fileReplacements?: FileReplacement1[];\n  /**\n   * The full path for the new output directory, relative to the current workspace.\n   */\n  outputPath: string;\n  /**\n   * The path where style resources will be placed, relative to outputPath.\n   */\n  resourcesOutputPath?: string;\n  /**\n   * Build using Ahead of Time compilation.\n   */\n  aot?: boolean;\n  /**\n   * Output source maps for scripts and styles. For more information, see https://angular.dev/reference/configs/workspace-config#source-map-configuration.\n   */\n  sourceMap?:\n    | {\n        /**\n         * Output source maps for all scripts.\n         */\n        scripts?: boolean;\n        /**\n         * Output source maps for all styles.\n         */\n        styles?: boolean;\n        /**\n         * Output source maps used for error reporting tools.\n         */\n        hidden?: boolean;\n        /**\n         * Resolve vendor packages source maps.\n         */\n        vendor?: boolean;\n      }\n    | boolean;\n  /**\n   * Generate a separate bundle containing only vendor libraries. This option should only be used for development to reduce the incremental compilation time.\n   */\n  vendorChunk?: boolean;\n  /**\n   * Generate a separate bundle containing code used across multiple bundles.\n   */\n  commonChunk?: boolean;\n  /**\n   * Base url for the application being built.\n   */\n  baseHref?: string;\n  /**\n   * Customize the base path for the URLs of resources in 'index.html' and component stylesheets. This option is only necessary for specific deployment scenarios, such as with Angular Elements or when utilizing different CDN locations.\n   */\n  deployUrl?: string;\n  /**\n   * Adds more details to output logging.\n   */\n  verbose?: boolean;\n  /**\n   * Log progress to the console while building.\n   */\n  progress?: boolean;\n  /**\n   * How to handle missing translations for i18n.\n   */\n  i18nMissingTranslation?: 'warning' | 'error' | 'ignore';\n  /**\n   * How to handle duplicate translations for i18n.\n   */\n  i18nDuplicateTranslation?: 'warning' | 'error' | 'ignore';\n  /**\n   * Translate the bundles in one or more locales.\n   */\n  localize?: boolean | [string, ...string[]];\n  /**\n   * Run build when files change.\n   */\n  watch?: boolean;\n  /**\n   * Define the output filename cache-busting hashing mode.\n   */\n  outputHashing?: 'none' | 'all' | 'media' | 'bundles';\n  /**\n   * Enable and define the file watching poll time period in milliseconds.\n   */\n  poll?: number;\n  /**\n   * Delete the output path before building.\n   */\n  deleteOutputPath?: boolean;\n  /**\n   * Do not use the real path when resolving modules. If unset then will default to `true` if NodeJS option --preserve-symlinks is set.\n   */\n  preserveSymlinks?: boolean;\n  /**\n   * Extract all licenses in a separate file.\n   */\n  extractLicenses?: boolean;\n  /**\n   * Enables advanced build optimizations when using the 'aot' option.\n   */\n  buildOptimizer?: boolean;\n  /**\n   * Use file name for lazy loaded chunks.\n   */\n  namedChunks?: boolean;\n  /**\n   * Enables the use of subresource integrity validation.\n   */\n  subresourceIntegrity?: boolean;\n  /**\n   * Generates a service worker config for production builds.\n   */\n  serviceWorker?: boolean;\n  /**\n   * Path to ngsw-config.json.\n   */\n  ngswConfigPath?: string;\n  /**\n   * Configures the generation of the application's HTML index.\n   */\n  index:\n    | string\n    | {\n        /**\n         * The path of a file to use for the application's generated HTML index.\n         */\n        input: string;\n        /**\n         * The output path of the application's generated HTML index file. The full provided path will be used and will be considered relative to the application's configured output path.\n         */\n        output?: string;\n        [k: string]: unknown;\n      }\n    | false;\n  /**\n   * Generates a 'stats.json' file which can be analyzed using tools such as 'webpack-bundle-analyzer'.\n   */\n  statsJson?: boolean;\n  /**\n   * Budget thresholds to ensure parts of your application stay within boundaries which you set.\n   */\n  budgets?: Budget2[];\n  /**\n   * TypeScript configuration for Web Worker modules.\n   */\n  webWorkerTsConfig?: string;\n  /**\n   * Define the crossorigin attribute setting of elements that provide CORS support.\n   */\n  crossOrigin?: 'none' | 'anonymous' | 'use-credentials';\n  /**\n   * A list of CommonJS or AMD packages that are allowed to be used without a build time warning. Use `'*'` to allow all.\n   */\n  allowedCommonJsDependencies?: string[];\n}\ninterface FileReplacement1 {\n  replace: string;\n  with: string;\n}\ninterface Budget2 {\n  /**\n   * The type of budget.\n   */\n  type: 'all' | 'allScript' | 'any' | 'anyScript' | 'anyComponentStyle' | 'bundle' | 'initial';\n  /**\n   * The name of the bundle.\n   */\n  name?: string;\n  /**\n   * The baseline size for comparison.\n   */\n  baseline?: string;\n  /**\n   * The maximum threshold for warning relative to the baseline.\n   */\n  maximumWarning?: string;\n  /**\n   * The maximum threshold for error relative to the baseline.\n   */\n  maximumError?: string;\n  /**\n   * The minimum threshold for warning relative to the baseline.\n   */\n  minimumWarning?: string;\n  /**\n   * The minimum threshold for error relative to the baseline.\n   */\n  minimumError?: string;\n  /**\n   * The threshold for warning relative to the baseline (min & max).\n   */\n  warning?: string;\n  /**\n   * The threshold for error relative to the baseline (min & max).\n   */\n  error?: string;\n}\n/**\n * Dev Server target options for Build Facade.\n */\ninterface DevServerTarget {\n  /**\n   * A build builder target to serve in the format of `project:target[:configuration]`. You can also pass in more than one configuration name as a comma-separated list. Example: `project:target:production,staging`.\n   */\n  buildTarget: string;\n  /**\n   * Port to listen on.\n   */\n  port?: number;\n  /**\n   * Host to listen on.\n   */\n  host?: string;\n  /**\n   * Proxy configuration file. For more information, see https://angular.dev/tools/cli/serve#proxying-to-a-backend-server.\n   */\n  proxyConfig?: string;\n  /**\n   * Serve using HTTPS.\n   */\n  ssl?: boolean;\n  /**\n   * SSL key to use for serving HTTPS.\n   */\n  sslKey?: string;\n  /**\n   * SSL certificate to use for serving HTTPS.\n   */\n  sslCert?: string;\n  /**\n   * Custom HTTP headers to be added to all responses.\n   */\n  headers?: {\n    [k: string]: string;\n  };\n  /**\n   * Opens the url in default browser.\n   */\n  open?: boolean;\n  /**\n   * Adds more details to output logging.\n   */\n  verbose?: boolean;\n  /**\n   * Whether to reload the page on change, using live-reload.\n   */\n  liveReload?: boolean;\n  /**\n   * The pathname where the application will be served.\n   */\n  servePath?: string;\n  /**\n   * Enable hot module replacement. Defaults to the value of 'liveReload'. Currently, only global and component stylesheets are supported.\n   */\n  hmr?: boolean;\n  /**\n   * Rebuild on change.\n   */\n  watch?: boolean;\n  /**\n   * Enable and define the file watching poll time period in milliseconds.\n   */\n  poll?: number;\n  /**\n   * Activate debugging inspector. This option only has an effect when 'SSR' or 'SSG' are enabled.\n   */\n  inspect?: string | boolean;\n  /**\n   * Enable and control the Vite-based development server's prebundling capabilities. To enable prebundling, the Angular CLI cache must also be enabled.\n   */\n  prebundle?:\n    | boolean\n    | {\n        /**\n         * List of package imports that should not be prebundled by the development server. The packages will be bundled into the application code itself.\n         */\n        exclude: string[];\n      };\n}\n/**\n * Dev Server target options for Build Facade.\n */\ninterface DevServerTarget1 {\n  /**\n   * A build builder target to serve in the format of `project:target[:configuration]`. You can also pass in more than one configuration name as a comma-separated list. Example: `project:target:production,staging`.\n   */\n  buildTarget: string;\n  /**\n   * Port to listen on.\n   */\n  port?: number;\n  /**\n   * Host to listen on.\n   */\n  host?: string;\n  /**\n   * Proxy configuration file. For more information, see https://angular.dev/tools/cli/serve#proxying-to-a-backend-server.\n   */\n  proxyConfig?: string;\n  /**\n   * Serve using HTTPS.\n   */\n  ssl?: boolean;\n  /**\n   * SSL key to use for serving HTTPS.\n   */\n  sslKey?: string;\n  /**\n   * SSL certificate to use for serving HTTPS.\n   */\n  sslCert?: string;\n  /**\n   * Custom HTTP headers to be added to all responses.\n   */\n  headers?: {\n    [k: string]: string;\n  };\n  /**\n   * Opens the url in default browser.\n   */\n  open?: boolean;\n  /**\n   * Adds more details to output logging.\n   */\n  verbose?: boolean;\n  /**\n   * Whether to reload the page on change, using live-reload.\n   */\n  liveReload?: boolean;\n  /**\n   * The URL that the browser client (or live-reload client, if enabled) should use to connect to the development server. Use for a complex dev server setup, such as one with reverse proxies. This option has no effect when using the 'application' or other esbuild-based builders.\n   */\n  publicHost?: string;\n  /**\n   * List of hosts that are allowed to access the dev server. This option has no effect when using the 'application' or other esbuild-based builders.\n   */\n  allowedHosts?: string[];\n  /**\n   * The pathname where the application will be served.\n   */\n  servePath?: string;\n  /**\n   * Don't verify connected clients are part of allowed hosts. This option has no effect when using the 'application' or other esbuild-based builders.\n   */\n  disableHostCheck?: boolean;\n  /**\n   * Enable hot module replacement.\n   */\n  hmr?: boolean;\n  /**\n   * Rebuild on change.\n   */\n  watch?: boolean;\n  /**\n   * Enable and define the file watching poll time period in milliseconds.\n   */\n  poll?: number;\n  /**\n   * Activate debugging inspector. This option only has an effect when 'SSR' or 'SSG' are enabled.\n   */\n  inspect?: string | boolean;\n  /**\n   * Force the development server to use the 'browser-esbuild' builder when building. This is a developer preview option for the esbuild-based build system.\n   */\n  forceEsbuild?: boolean;\n  /**\n   * Enable and control the Vite-based development server's prebundling capabilities. To enable prebundling, the Angular CLI cache must also be enabled. This option has no effect when using the 'browser' or other Webpack-based builders.\n   */\n  prebundle?:\n    | boolean\n    | {\n        /**\n         * List of package imports that should not be prebundled by the development server. The packages will be bundled into the application code itself.\n         */\n        exclude: string[];\n      };\n}\n/**\n * Extract i18n target options for Build Facade.\n */\ninterface ExtractI18NTarget {\n  /**\n   * A builder target to extract i18n messages in the format of `project:target[:configuration]`. You can also pass in more than one configuration name as a comma-separated list. Example: `project:target:production,staging`.\n   */\n  buildTarget?: string;\n  /**\n   * Output format for the generated file.\n   */\n  format?: 'xmb' | 'xlf' | 'xlif' | 'xliff' | 'xlf2' | 'xliff2' | 'json' | 'arb' | 'legacy-migrate';\n  /**\n   * Log progress to the console.\n   */\n  progress?: boolean;\n  /**\n   * Path where output will be placed.\n   */\n  outputPath?: string;\n  /**\n   * Name of the file to output.\n   */\n  outFile?: string;\n}\n/**\n * Extract i18n target options for Build Facade.\n */\ninterface ExtractI18NTarget1 {\n  /**\n   * A builder target to extract i18n messages in the format of `project:target[:configuration]`. You can also pass in more than one configuration name as a comma-separated list. Example: `project:target:production,staging`.\n   */\n  buildTarget?: string;\n  /**\n   * Output format for the generated file.\n   */\n  format?: 'xmb' | 'xlf' | 'xlif' | 'xliff' | 'xlf2' | 'xliff2' | 'json' | 'arb' | 'legacy-migrate';\n  /**\n   * Log progress to the console.\n   */\n  progress?: boolean;\n  /**\n   * Path where output will be placed.\n   */\n  outputPath?: string;\n  /**\n   * Name of the file to output.\n   */\n  outFile?: string;\n}\n/**\n * Karma target options for Build Facade.\n */\nexport interface KarmaTarget {\n  /**\n   * The name of the main entry-point file.\n   */\n  main?: string;\n  /**\n   * The name of the TypeScript configuration file.\n   */\n  tsConfig: string;\n  /**\n   * The name of the Karma configuration file.\n   */\n  karmaConfig?: string;\n  /**\n   * Polyfills to be included in the build.\n   */\n  polyfills?: string[] | string;\n  /**\n   * List of static application assets.\n   */\n  assets?: (\n    | {\n        /**\n         * The pattern to match.\n         */\n        glob: string;\n        /**\n         * The input directory path in which to apply 'glob'. Defaults to the project root.\n         */\n        input: string;\n        /**\n         * Absolute path within the output.\n         */\n        output?: string;\n        /**\n         * An array of globs to ignore.\n         */\n        ignore?: string[];\n      }\n    | string\n  )[];\n  /**\n   * Global scripts to be included in the build.\n   */\n  scripts?: (\n    | {\n        /**\n         * The file to include.\n         */\n        input: string;\n        /**\n         * The bundle name for this extra entry point.\n         */\n        bundleName?: string;\n        /**\n         * If the bundle will be referenced in the HTML file.\n         */\n        inject?: boolean;\n      }\n    | string\n  )[];\n  /**\n   * Global styles to be included in the build.\n   */\n  styles?: (\n    | {\n        /**\n         * The file to include.\n         */\n        input: string;\n        /**\n         * The bundle name for this extra entry point.\n         */\n        bundleName?: string;\n        /**\n         * If the bundle will be referenced in the HTML file.\n         */\n        inject?: boolean;\n      }\n    | string\n  )[];\n  /**\n   * The stylesheet language to use for the application's inline component styles.\n   */\n  inlineStyleLanguage?: 'css' | 'less' | 'sass' | 'scss';\n  /**\n   * Options to pass to style preprocessors\n   */\n  stylePreprocessorOptions?: {\n    /**\n     * Paths to include. Paths will be resolved to workspace root.\n     */\n    includePaths?: string[];\n  };\n  /**\n   * Globs of files to include, relative to project root.\n   * There are 2 special cases:\n   *  - when a path to directory is provided, all spec files ending \".spec.@(ts|tsx)\" will be included\n   *  - when a path to a file is provided, and a matching spec file exists it will be included instead.\n   */\n  include?: string[];\n  /**\n   * Globs of files to exclude, relative to the project root.\n   */\n  exclude?: string[];\n  /**\n   * Output source maps for scripts and styles. For more information, see https://angular.dev/reference/configs/workspace-config#source-map-configuration.\n   */\n  sourceMap?:\n    | {\n        /**\n         * Output source maps for all scripts.\n         */\n        scripts?: boolean;\n        /**\n         * Output source maps for all styles.\n         */\n        styles?: boolean;\n        /**\n         * Resolve vendor packages source maps.\n         */\n        vendor?: boolean;\n      }\n    | boolean;\n  /**\n   * Log progress to the console while building.\n   */\n  progress?: boolean;\n  /**\n   * Run build when files change.\n   */\n  watch?: boolean;\n  /**\n   * Enable and define the file watching poll time period in milliseconds.\n   */\n  poll?: number;\n  /**\n   * Do not use the real path when resolving modules. If unset then will default to `true` if NodeJS option --preserve-symlinks is set.\n   */\n  preserveSymlinks?: boolean;\n  /**\n   * Override which browsers tests are run against. Set to `false` to not use any browser.\n   */\n  browsers?: string | false;\n  /**\n   * Output a code coverage report.\n   */\n  codeCoverage?: boolean;\n  /**\n   * Globs to exclude from code coverage.\n   */\n  codeCoverageExclude?: string[];\n  /**\n   * Replace compilation source files with other compilation source files in the build.\n   */\n  fileReplacements?: (\n    | {\n        src: string;\n        replaceWith: string;\n      }\n    | {\n        replace: string;\n        with: string;\n      }\n  )[];\n  /**\n   * Karma reporters to use. Directly passed to the karma runner.\n   */\n  reporters?: string[];\n  /**\n   * Determines how to build the code under test. If set to 'detect', attempts to follow the development builder.\n   */\n  builderMode?: 'detect' | 'browser' | 'application';\n  /**\n   * TypeScript configuration for Web Worker modules.\n   */\n  webWorkerTsConfig?: string;\n}\n/**\n * Jest target options\n */\ninterface JestBrowserSchemaForBuildFacade {\n  /**\n   * Globs of files to include, relative to project root.\n   */\n  include?: string[];\n  /**\n   * Globs of files to exclude, relative to the project root.\n   */\n  exclude?: string[];\n  /**\n   * The name of the TypeScript configuration file.\n   */\n  tsConfig: string;\n  /**\n   * A list of polyfills to include in the build. Can be a full path for a file, relative to the current workspace or module specifier. Example: 'zone.js'.\n   */\n  polyfills?: string[];\n}\n/**\n * Web Test Runner target options for Build Facade.\n */\ninterface WebTestRunnerTarget {\n  /**\n   * The name of the main entry-point file.\n   */\n  main?: string;\n  /**\n   * The name of the TypeScript configuration file.\n   */\n  tsConfig: string;\n  /**\n   * Polyfills to be included in the build.\n   */\n  polyfills?: string[] | string;\n  /**\n   * List of static application assets.\n   */\n  assets?: (\n    | {\n        /**\n         * The pattern to match.\n         */\n        glob: string;\n        /**\n         * The input directory path in which to apply 'glob'. Defaults to the project root.\n         */\n        input: string;\n        /**\n         * Absolute path within the output.\n         */\n        output?: string;\n        /**\n         * An array of globs to ignore.\n         */\n        ignore?: string[];\n      }\n    | string\n  )[];\n  /**\n   * Global scripts to be included in the build.\n   */\n  scripts?: (\n    | {\n        /**\n         * The file to include.\n         */\n        input: string;\n        /**\n         * The bundle name for this extra entry point.\n         */\n        bundleName?: string;\n        /**\n         * If the bundle will be referenced in the HTML file.\n         */\n        inject?: boolean;\n      }\n    | string\n  )[];\n  /**\n   * Global styles to be included in the build.\n   */\n  styles?: (\n    | {\n        /**\n         * The file to include.\n         */\n        input: string;\n        /**\n         * The bundle name for this extra entry point.\n         */\n        bundleName?: string;\n        /**\n         * If the bundle will be referenced in the HTML file.\n         */\n        inject?: boolean;\n      }\n    | string\n  )[];\n  /**\n   * The stylesheet language to use for the application's inline component styles.\n   */\n  inlineStyleLanguage?: 'css' | 'less' | 'sass' | 'scss';\n  /**\n   * Options to pass to style preprocessors\n   */\n  stylePreprocessorOptions?: {\n    /**\n     * Paths to include. Paths will be resolved to workspace root.\n     */\n    includePaths?: string[];\n  };\n  /**\n   * Globs of files to include, relative to project root.\n   * There are 2 special cases:\n   *  - when a path to directory is provided, all spec files ending \".spec.@(ts|tsx)\" will be included\n   *  - when a path to a file is provided, and a matching spec file exists it will be included instead.\n   */\n  include?: string[];\n  /**\n   * Globs of files to exclude, relative to the project root.\n   */\n  exclude?: string[];\n  /**\n   * Output source maps for scripts and styles. For more information, see https://angular.dev/reference/configs/workspace-config#source-map-configuration.\n   */\n  sourceMap?:\n    | {\n        /**\n         * Output source maps for all scripts.\n         */\n        scripts?: boolean;\n        /**\n         * Output source maps for all styles.\n         */\n        styles?: boolean;\n        /**\n         * Resolve vendor packages source maps.\n         */\n        vendor?: boolean;\n      }\n    | boolean;\n  /**\n   * Log progress to the console while building.\n   */\n  progress?: boolean;\n  /**\n   * Run build when files change.\n   */\n  watch?: boolean;\n  /**\n   * Enable and define the file watching poll time period in milliseconds.\n   */\n  poll?: number;\n  /**\n   * Do not use the real path when resolving modules. If unset then will default to `true` if NodeJS option --preserve-symlinks is set.\n   */\n  preserveSymlinks?: boolean;\n  /**\n   * Override which browsers tests are run against.\n   */\n  browsers?: string;\n  /**\n   * Output a code coverage report.\n   */\n  codeCoverage?: boolean;\n  /**\n   * Globs to exclude from code coverage.\n   */\n  codeCoverageExclude?: string[];\n  /**\n   * Replace compilation source files with other compilation source files in the build.\n   */\n  fileReplacements?: (\n    | {\n        src: string;\n        replaceWith: string;\n      }\n    | {\n        replace: string;\n        with: string;\n      }\n  )[];\n  /**\n   * TypeScript configuration for Web Worker modules.\n   */\n  webWorkerTsConfig?: string;\n}\n/**\n * SSR Dev Server target options for Build Facade.\n */\ninterface SSRDevServerTarget {\n  /**\n   * Browser target to build.\n   */\n  browserTarget: string;\n  /**\n   * Server target to build.\n   */\n  serverTarget: string;\n  /**\n   * Host to listen on.\n   */\n  host?: string;\n  /**\n   * Port to start the development server at. Default is 4200. Pass 0 to get a dynamically assigned port.\n   */\n  port?: number;\n  /**\n   * Rebuild on change.\n   */\n  watch?: boolean;\n  /**\n   * The URL that the browser client should use to connect to the development server. Use for a complex dev server setup, such as one with reverse proxies.\n   */\n  publicHost?: string;\n  /**\n   * Opens the url in default browser.\n   */\n  open?: boolean;\n  /**\n   * Log progress to the console while building.\n   */\n  progress?: boolean;\n  /**\n   * Launch the development server in inspector mode and listen on address and port '127.0.0.1:9229'.\n   */\n  inspect?: boolean;\n  /**\n   * Serve using HTTPS.\n   */\n  ssl?: boolean;\n  /**\n   * SSL key to use for serving HTTPS.\n   */\n  sslKey?: string;\n  /**\n   * SSL certificate to use for serving HTTPS.\n   */\n  sslCert?: string;\n  /**\n   * Proxy configuration file.\n   */\n  proxyConfig?: string;\n  /**\n   * Adds more details to output logging.\n   */\n  verbose?: boolean;\n}\ninterface UniversalTarget {\n  /**\n   * List of static application assets.\n   */\n  assets?: (\n    | {\n        /**\n         * Allow glob patterns to follow symlink directories. This allows subdirectories of the symlink to be searched.\n         */\n        followSymlinks?: boolean;\n        /**\n         * The pattern to match.\n         */\n        glob: string;\n        /**\n         * The input directory path in which to apply 'glob'. Defaults to the project root.\n         */\n        input: string;\n        /**\n         * An array of globs to ignore.\n         */\n        ignore?: string[];\n        /**\n         * Absolute path within the output.\n         */\n        output?: string;\n      }\n    | string\n  )[];\n  /**\n   * The name of the main entry-point file.\n   */\n  main: string;\n  /**\n   * The name of the TypeScript configuration file.\n   */\n  tsConfig: string;\n  /**\n   * The stylesheet language to use for the application's inline component styles.\n   */\n  inlineStyleLanguage?: 'css' | 'less' | 'sass' | 'scss';\n  /**\n   * Options to pass to style preprocessors\n   */\n  stylePreprocessorOptions?: {\n    /**\n     * Paths to include. Paths will be resolved to workspace root.\n     */\n    includePaths?: string[];\n  };\n  /**\n   * Enables optimization of the build output. Including minification of scripts and styles, tree-shaking and dead-code elimination. For more information, see https://angular.dev/reference/configs/workspace-config#optimization-configuration.\n   */\n  optimization?:\n    | {\n        /**\n         * Enables optimization of the scripts output.\n         */\n        scripts?: boolean;\n        /**\n         * Enables optimization of the styles output.\n         */\n        styles?: boolean;\n      }\n    | boolean;\n  /**\n   * Replace compilation source files with other compilation source files in the build.\n   */\n  fileReplacements?: (\n    | {\n        src: string;\n        replaceWith: string;\n      }\n    | {\n        replace: string;\n        with: string;\n      }\n  )[];\n  /**\n   * Path where output will be placed.\n   */\n  outputPath: string;\n  /**\n   * The path where style resources will be placed, relative to outputPath.\n   */\n  resourcesOutputPath?: string;\n  /**\n   * Output source maps for scripts and styles. For more information, see https://angular.dev/reference/configs/workspace-config#source-map-configuration.\n   */\n  sourceMap?:\n    | {\n        /**\n         * Output source maps for all scripts.\n         */\n        scripts?: boolean;\n        /**\n         * Output source maps for all styles.\n         */\n        styles?: boolean;\n        /**\n         * Output source maps used for error reporting tools.\n         */\n        hidden?: boolean;\n        /**\n         * Resolve vendor packages source maps.\n         */\n        vendor?: boolean;\n      }\n    | boolean;\n  /**\n   * Customize the base path for the URLs of resources in 'index.html' and component stylesheets. This option is only necessary for specific deployment scenarios, such as with Angular Elements or when utilizing different CDN locations.\n   */\n  deployUrl?: string;\n  /**\n   * Generate a separate bundle containing only vendor libraries. This option should only be used for development to reduce the incremental compilation time.\n   */\n  vendorChunk?: boolean;\n  /**\n   * Adds more details to output logging.\n   */\n  verbose?: boolean;\n  /**\n   * Log progress to the console while building.\n   */\n  progress?: boolean;\n  /**\n   * How to handle missing translations for i18n.\n   */\n  i18nMissingTranslation?: 'warning' | 'error' | 'ignore';\n  /**\n   * How to handle duplicate translations for i18n.\n   */\n  i18nDuplicateTranslation?: 'warning' | 'error' | 'ignore';\n  /**\n   * Translate the bundles in one or more locales.\n   */\n  localize?: boolean | [string, ...string[]];\n  /**\n   * Define the output filename cache-busting hashing mode.\n   */\n  outputHashing?: 'none' | 'all' | 'media' | 'bundles';\n  /**\n   * Delete the output path before building.\n   */\n  deleteOutputPath?: boolean;\n  /**\n   * Do not use the real path when resolving modules. If unset then will default to `true` if NodeJS option --preserve-symlinks is set.\n   */\n  preserveSymlinks?: boolean;\n  /**\n   * Extract all licenses in a separate file, in the case of production builds only.\n   */\n  extractLicenses?: boolean;\n  /**\n   * Enables advanced build optimizations.\n   */\n  buildOptimizer?: boolean;\n  /**\n   * Use file name for lazy loaded chunks.\n   */\n  namedChunks?: boolean;\n  /**\n   * Exclude the listed external dependencies from being bundled into the bundle. Instead, the created bundle relies on these dependencies to be available during runtime.\n   */\n  externalDependencies?: string[];\n  /**\n   * Generates a 'stats.json' file which can be analyzed using tools such as 'webpack-bundle-analyzer'.\n   */\n  statsJson?: boolean;\n  /**\n   * Run build when files change.\n   */\n  watch?: boolean;\n  /**\n   * Enable and define the file watching poll time period in milliseconds.\n   */\n  poll?: number;\n}\n/**\n * ng-packagr target options for Build Architect. Use to build library projects.\n */\ninterface NgPackagrTarget {\n  /**\n   * The file path for the ng-packagr configuration file, relative to the current workspace.\n   */\n  project: string;\n  /**\n   * The full path for the TypeScript configuration file, relative to the current workspace.\n   */\n  tsConfig?: string;\n  /**\n   * Run build when files change.\n   */\n  watch?: boolean;\n  /**\n   * Enable and define the file watching poll time period in milliseconds.\n   */\n  poll?: number;\n}\n"
  },
  {
    "path": "packages/knip/src/plugins/angular/update-types.sh",
    "content": "#!/usr/bin/env sh\nset -eu\nroot_dir=\"$(git rev-parse --show-toplevel)\"\nangular_plugin_dir=\"$root_dir/packages/knip/src/plugins/angular\"\ntmp_dir=\"$(mktemp -d)\"\ncleanup() {\n  rm  -rf \"$tmp_dir\"\n}\ntrap cleanup EXIT\ngit clone --depth 1 https://github.com/angular/angular-cli \"$tmp_dir\"\ncd \"$tmp_dir/packages/angular/cli/lib/config\"\nbunx json-schema-to-typescript -i workspace-schema.json -o \"$angular_plugin_dir/types.ts\"\n\"$root_dir/packages/knip/bin/knip-bun.js\" --directory \"$root_dir\" --fix"
  },
  {
    "path": "packages/knip/src/plugins/astro/compiler-mdx.ts",
    "content": "import {\n  fencedCodeBlockMatcher,\n  importMatcher,\n  importsWithinFrontmatter,\n  inlineCodeMatcher,\n} from '../../compilers/compilers.ts';\n\n// Fields in frontmatter that could contain imports\nconst frontmatterImportFields = ['layout'];\n\nconst compiler = (text: string) => {\n  const imports = text.replace(fencedCodeBlockMatcher, '').replace(inlineCodeMatcher, '').matchAll(importMatcher);\n\n  const frontmatterImports = importsWithinFrontmatter(text, frontmatterImportFields);\n\n  return [...imports, frontmatterImports].join('\\n');\n};\n\nexport default compiler;\n"
  },
  {
    "path": "packages/knip/src/plugins/astro/compiler.ts",
    "content": "import { frontmatterMatcher, scriptBodies } from '../../compilers/compilers.ts';\n\nconst compiler = (text: string, path: string) => {\n  const scripts = [];\n\n  const frontmatter = text.match(frontmatterMatcher);\n  if (frontmatter?.[1]) scripts.push(frontmatter[1]);\n\n  const scriptContent = scriptBodies(text, path);\n  if (scriptContent) scripts.push(scriptContent);\n\n  return scripts.join('\\n');\n};\n\nexport default compiler;\n"
  },
  {
    "path": "packages/knip/src/plugins/astro/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, RegisterCompilers, Resolve, ResolveFromAST } from '../../types/config.ts';\nimport { toDependency, toEntry, toProductionEntry } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport compiler from './compiler.ts';\nimport mdxCompiler from './compiler-mdx.ts';\nimport { getSrcDir, usesSharpImageService } from './resolveFromAST.ts';\n\n// https://docs.astro.build/en/reference/configuration-reference/\n\nconst title = 'Astro';\n\nconst enablers = ['astro'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nexport const config = ['astro.config.{js,cjs,mjs,ts,mts}'];\n\nconst entry = ['src/content/config.ts', 'src/content.config.ts'];\n\nconst production = [\n  'src/pages/**/*.{astro,mdx,js,ts}',\n  '!src/pages/**/_*', // negate files prefixed with _.\n  '!src/pages/**/_*/**', // negate folders prefixed with _. The pattern _** would be collapsed into _* so we have to use **/_*/**\n  'src/content/**/*.mdx',\n  'src/middleware.{js,ts}',\n  'src/actions/index.{js,ts}',\n];\n\nconst resolveFromAST: ResolveFromAST = program => {\n  const srcDir = getSrcDir(program);\n  const setSrcDir = (entry: string) => entry.replace(/^src\\//, `${srcDir}/`);\n  const inputs = [\n    ...entry.map(setSrcDir).map(path => toEntry(path)),\n    ...production.map(setSrcDir).map(path => toProductionEntry(path)),\n  ];\n\n  if (usesSharpImageService(program)) inputs.push(toDependency('sharp'));\n\n  return inputs;\n};\n\n// https://docs.astro.build/en/guides/integrations-guide/mdx/\nconst registerCompilers: RegisterCompilers = ({ registerCompiler, hasDependency }) => {\n  if (hasDependency('astro')) registerCompiler({ extension: '.astro', compiler });\n  if (hasDependency('@astrojs/mdx') || hasDependency('@astrojs/starlight')) {\n    registerCompiler({ extension: '.mdx', compiler: mdxCompiler });\n  }\n};\n\nconst resolve: Resolve = options => {\n  const { manifest, isProduction } = options;\n  const inputs = [];\n\n  if (\n    !isProduction &&\n    manifest.scripts &&\n    Object.values(manifest.scripts).some(script => /(?<=^|\\s)astro(\\s|\\s.+\\s)check(?=\\s|$)/.test(script))\n  ) {\n    inputs.push(toDependency('@astrojs/check'));\n  }\n\n  return inputs;\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  entry,\n  production,\n  registerCompilers,\n  resolveFromAST,\n  resolve,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/astro/resolveFromAST.ts",
    "content": "import type { Program } from 'oxc-parser';\nimport { collectPropertyValues, hasImportSpecifier } from '../../typescript/ast-helpers.ts';\n\nexport const getSrcDir = (program: Program): string => {\n  const values = collectPropertyValues(program, 'srcDir');\n  return values.size > 0 ? Array.from(values)[0] : 'src';\n};\n\nexport const usesSharpImageService = (program: Program) =>\n  hasImportSpecifier(program, 'astro/config', 'sharpImageService');\n"
  },
  {
    "path": "packages/knip/src/plugins/astro-db/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://docs.astro.build/en/guides/astro-db/\n\nconst title = 'Astro DB';\n\nconst enablers = ['@astrojs/db'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst entry = ['db/config.{js,ts}', 'db/seed.{js,ts}'];\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  entry,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/astro-og-canvas/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, Resolve } from '../../types/config.ts';\nimport { toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://github.com/delucis/astro-og-canvas\n\nconst title = 'astro-og-canvas';\n\nconst enablers = ['astro-og-canvas'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst resolve: Resolve = async () => {\n  return [toDependency('canvaskit-wasm', { optional: true })];\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  resolve,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/ava/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toEntry } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { AvaConfig } from './types.ts';\n\n// https://github.com/avajs/ava/blob/main/docs/06-configuration.md\n\nconst title = 'Ava';\n\nconst enablers = ['ava'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['ava.config.{js,cjs,mjs}', 'package.json'];\n\nconst entry = [\n  'test.{js,cjs,mjs,ts}',\n  '{src,source}/test.{js,cjs,mjs,ts}',\n  '**/__tests__/**/*.{js,cjs,mjs,ts}',\n  '**/*.spec.{js,cjs,mjs,ts}',\n  '**/*.test.{js,cjs,mjs,ts}',\n  '**/test-*.{js,cjs,mjs,ts}',\n  '**/test/**/*.{js,cjs,mjs,ts}',\n  '**/tests/**/*.{js,cjs,mjs,ts}',\n  '!**/__tests__/**/__{helper,fixture}?(s)__/**/*',\n  '!**/test?(s)/**/{helper,fixture}?(s)/**/*',\n];\n\nconst resolveConfig: ResolveConfig<AvaConfig> = async (localConfig, options) => {\n  if (typeof localConfig === 'function') localConfig = localConfig();\n\n  const files = (localConfig?.files ?? entry).map(id => toEntry(id));\n  const nodeArgs = localConfig.nodeArguments ?? [];\n  const requireArgs = (localConfig.require ?? []).map(require => `--require ${require}`);\n  const fakeCommand = `node ${nodeArgs.join(' ')} ${requireArgs.join(' ')}`;\n\n  return files.concat(options.getInputsFromScripts(fakeCommand, { knownBinsOnly: true }));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  entry,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/ava/types.ts",
    "content": "type Config = {\n  files?: string[];\n  require?: string[];\n  nodeArguments?: string[];\n  extensions?: string[];\n};\n\nexport type AvaConfig = Config | (() => Config);\n"
  },
  {
    "path": "packages/knip/src/plugins/babel/helpers.ts",
    "content": "import { isAbsolute, isInternal } from '../../util/path.ts';\n\nexport const resolveName = (identifier: string, namespace: 'preset' | 'plugin') => {\n  if (isAbsolute(identifier) || isInternal(identifier)) return identifier;\n  if (identifier.startsWith('module:')) return identifier.replace(/^module:/, '');\n  if (identifier.startsWith('@')) {\n    const [scope, name, ...rest] = identifier.split('/');\n    if (rest.length > 0) return identifier;\n    if (scope) {\n      if (!name) return [scope, `babel-${namespace}`].join('/');\n      if (scope === '@babel') {\n        if (name.startsWith(namespace)) return identifier;\n        return `@babel/${namespace}-${name}`;\n      }\n      if (name.includes(`babel-${namespace}`)) return identifier;\n      return [scope, `babel-${namespace}-${name}`].join('/');\n    }\n  }\n  const [name, ...rest] = identifier.split('/');\n  if (rest.length > 0) return identifier;\n  if (name.startsWith(`babel-${namespace}`)) return identifier;\n  return `babel-${namespace}-${name}`;\n};\n\nconst cacheFn = () => void 0;\ncacheFn.forever = () => cacheFn;\ncacheFn.never = () => cacheFn;\ncacheFn.using = () => cacheFn;\ncacheFn.invalidate = () => cacheFn;\n\nexport const api = {\n  assertVersion: () => true,\n  cache: cacheFn,\n  caller: () => true,\n  env: (env?: string) => (typeof env === 'string' ? true : 'development'),\n  version: '0.0.0',\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/babel/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { compact } from '../../util/array.ts';\nimport { type Input, toDeferResolve } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { api, resolveName } from './helpers.ts';\nimport type { BabelConfig, BabelConfigObj } from './types.ts';\n\n// https://babeljs.io/docs/configuration\n// https://babeljs.io/docs/options#name-normalization\n\nconst title = 'Babel';\n\nconst enablers = [/^@babel\\//];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['babel.config.{json,js,cjs,mjs,cts,ts}', '.babelrc.{json,js,cjs,mjs,cts}', '.babelrc', 'package.json'];\n\nconst getName = (value: string | [string, unknown]) =>\n  [Array.isArray(value) ? value[0] : value].filter(name => typeof name === 'string');\n\nexport const getDependenciesFromConfig = (config: BabelConfigObj): Input[] => {\n  const presets = config.presets?.flatMap(getName).map(name => resolveName(name, 'preset')) ?? [];\n  const plugins = config.plugins?.flatMap(getName).map(name => resolveName(name, 'plugin')) ?? [];\n  const nested = config.env ? Object.values(config.env).flatMap(getDependenciesFromConfig) : [];\n  const overrides = config.overrides ? [config.overrides].flat().flatMap(getDependenciesFromConfig) : [];\n  return compact([\n    ...presets.map(id => toDeferResolve(id)),\n    ...plugins.map(id => toDeferResolve(id)),\n    ...(plugins.includes('@babel/plugin-transform-runtime')\n      ? [toDeferResolve('@babel/runtime', { optional: true })]\n      : []),\n    ...nested,\n    ...overrides,\n  ]);\n};\n\nconst resolveConfig: ResolveConfig<BabelConfig> = async config => {\n  if (typeof config === 'function') config = config(api);\n\n  if (!config) return [];\n\n  return getDependenciesFromConfig(config);\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/babel/types.ts",
    "content": "import type { api } from './helpers.ts';\n\ntype BabelConfigFn = (options: typeof api) => BabelConfigObj;\n\nexport type BabelConfigObj = {\n  plugins?: (string | [string, unknown])[];\n  presets?: (string | [string, unknown])[];\n  env?: Record<string, BabelConfigObj>;\n  overrides?: BabelConfigObj[];\n};\n\nexport type BabelConfig = BabelConfigObj | BabelConfigFn;\n"
  },
  {
    "path": "packages/knip/src/plugins/biome/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, PluginOptions, ResolveConfig } from '../../types/config.ts';\nimport { arrayify } from '../../util/array.ts';\nimport { type Input, toConfig } from '../../util/input.ts';\nimport { join } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { BiomeConfig } from './types.ts';\n\nconst title = 'Biome';\n\nconst enablers = ['@biomejs/biome'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config: string[] = ['biome.json', 'biome.jsonc'];\n\nconst isRootConfigReference = (specifier: string) => specifier === '//';\n\nconst resolveExtends = (extendsArray: string[], options: PluginOptions): Input[] => {\n  return extendsArray.map(specifier => {\n    if (isRootConfigReference(specifier)) {\n      return toConfig('biome', join(options.rootCwd, 'biome'), { containingFilePath: options.configFilePath });\n    }\n\n    return toConfig('biome', specifier, { containingFilePath: options.configFilePath });\n  });\n};\n\nconst resolveConfig: ResolveConfig<BiomeConfig> = (config, options) => {\n  return [...resolveExtends(arrayify(config.extends), options)];\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/biome/types.ts",
    "content": "export type BiomeConfig = {\n  extends?: string[];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/bumpp/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { toC12config } from '../../util/plugin-config.ts';\n\n// https://github.com/antfu-collective/bumpp#bumpp\n\nconst title = 'bumpp';\n\nconst enablers = ['bumpp'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst entry = ['package.json', ...toC12config('bump')];\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  entry,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/bun/index.ts",
    "content": "import parseArgs from 'minimist';\nimport type { IsPluginEnabled, Plugin, Resolve, ResolveConfig } from '../../types/config.ts';\nimport { toDeferResolve, toEntry } from '../../util/input.ts';\nimport type { BunfigConfig } from './types.ts';\n\n// https://bun.sh/docs/cli/test\n\nconst title = 'Bun';\n\nconst enablers = ['bun'];\n\nconst hasBunTest = (scripts: Record<string, string> | undefined) =>\n  scripts && Object.values(scripts).some(script => /(?<=^|\\s)bun test/.test(script));\n\nconst isEnabled: IsPluginEnabled = ({ manifest }) => !!hasBunTest(manifest.scripts);\n\nconst config = ['bunfig.toml'];\n\nconst patterns = ['**/*.{test,spec}.{js,jsx,ts,tsx}', '**/*_{test,spec}.{js,jsx,ts,tsx}'];\n\nconst resolveConfig: ResolveConfig<BunfigConfig> = localConfig => {\n  const preload = localConfig.test?.preload ?? [];\n  return preload.map(specifier => toDeferResolve(specifier));\n};\n\nconst toPatterns = (arg: string) => {\n  if (/[*{?]/.test(arg)) return [arg];\n  const dir = arg.replace(/\\/+$/, '');\n  return patterns.map(pattern => `${dir}/${pattern}`);\n};\n\nconst resolve: Resolve = options => {\n  const scripts = { ...options.rootManifest?.scripts, ...options.manifest.scripts };\n  for (const script of Object.values(scripts)) {\n    if (/(?<=^|\\s)bun test/.test(script)) {\n      const parsed = parseArgs(script.split(' '), { string: ['timeout', 'rerun-each', 'preload'] });\n      const args = parsed._.filter(id => id !== 'bun' && id !== 'test');\n      const inputs = (args.length === 0 ? patterns : args.flatMap(toPatterns)).map(toEntry);\n      for (const specifier of [parsed.preload ?? []].flat()) inputs.push(toDeferResolve(specifier));\n      return inputs;\n    }\n  }\n  return [];\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolve,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/bun/types.ts",
    "content": "export type BunfigConfig = {\n  test?: {\n    preload?: string[];\n  };\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/c8/index.ts",
    "content": "import type { ParsedArgs } from 'minimist';\nimport { argsFrom } from '../../binaries/util.ts';\nimport type { Plugin } from '../../types/config.ts';\n\n// https://www.npmjs.com/package/c8\n\nconst title = 'c8';\n\nconst args = {\n  args: (args: string[]) => args.filter(arg => arg !== 'check-coverage'),\n  boolean: ['all', 'check-coverage', 'clean', 'exclude-after-remap', 'per-file', 'skip-full'],\n  fromArgs: (parsed: ParsedArgs, args: string[]) => (parsed._[0] ? argsFrom(args, parsed._[0]) : (parsed['--'] ?? [])),\n};\n\nconst plugin: Plugin = {\n  title,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/capacitor/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { isFile } from '../../util/fs.ts';\nimport { toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { CapacitorConfig } from './types.ts';\n\n// https://capacitorjs.com/docs/config\n\nconst title = 'Capacitor';\n\nconst enablers = [/^@capacitor\\//];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['capacitor.config.{json,ts}'];\n\nconst resolveConfig: ResolveConfig<CapacitorConfig> = async (config, { configFileDir }) => {\n  const plugins = config.includePlugins ?? [];\n  const android = isFile(configFileDir, 'android/capacitor.settings.gradle') ? ['@capacitor/android'] : [];\n  const ios = isFile(configFileDir, 'ios/App/Podfile') ? ['@capacitor/ios'] : [];\n\n  return [...plugins, ...android, ...ios].map(id => toDependency(id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/capacitor/types.ts",
    "content": "export type CapacitorConfig = {\n  includePlugins?: string[];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/changelogen/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { toC12config } from '../../util/plugin-config.ts';\n\n// https://github.com/unjs/changelogen\n\nconst title = 'Changelogen';\n\nconst enablers = ['changelogen'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst entry = ['package.json', ...toC12config('changelog')];\n\nconst isRootOnly = true;\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  isRootOnly,\n  entry,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/changelogithub/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { toC12config } from '../../util/plugin-config.ts';\n\n// https://github.com/antfu/changelogithub\n\nconst title = 'Changelogithub';\n\nconst enablers = ['changelogithub'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst entry = ['package.json', ...toC12config('changelogithub')];\n\nconst isRootOnly = true;\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  isRootOnly,\n  entry,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/changesets/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDependency } from '../../util/input.ts';\nimport { getPackageNameFromFilePath } from '../../util/modules.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { ChangesetsConfig } from './types.ts';\n\n// https://github.com/changesets/changesets/blob/main/docs/config-file-options.md\n\nconst title = 'Changesets';\n\nconst enablers = ['@changesets/cli'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst isRootOnly = true;\n\nconst config = ['.changeset/config.json'];\n\nconst resolveConfig: ResolveConfig<ChangesetsConfig> = config => {\n  const inputs = (\n    Array.isArray(config.changelog)\n      ? [config.changelog[0]]\n      : typeof config.changelog === 'string'\n        ? [config.changelog]\n        : []\n  ).map(id => toDependency(id));\n\n  if (config.$schema?.includes('node_modules/')) {\n    const packageName = getPackageNameFromFilePath(config.$schema);\n    if (packageName) inputs.push(toDependency(packageName));\n  }\n\n  return inputs;\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  isRootOnly,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/changesets/types.ts",
    "content": "export type ChangesetsConfig = {\n  $schema?: string;\n  changelog: string | string[];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/commitizen/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { CommitizenConfig } from './types.ts';\n\n// https://github.com/commitizen/cz-cli\n\nconst title = 'Commitizen';\n\nconst enablers = ['commitizen'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst isRootOnly = true;\n\nconst packageJsonPath = 'config.commitizen';\n\nconst config = ['.czrc', '.cz.json', 'package.json'];\n\nconst resolveConfig: ResolveConfig<CommitizenConfig> = config => {\n  return config.path ? [toDependency(config.path)] : [];\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  isRootOnly,\n  packageJsonPath,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/commitizen/types.ts",
    "content": "export type CommitizenConfig = {\n  path?: string;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/commitlint/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { toCosmiconfig } from '../../util/plugin-config.ts';\nimport type { CommitLintConfig } from './types.ts';\n\n// https://commitlint.js.org\n// https://github.com/conventional-changelog/commitlint#config\n// https://github.com/conventional-changelog/commitlint/blob/master/%40commitlint/load/src/utils/load-config.ts\n\nconst title = 'commitlint';\n\nconst enablers = ['@commitlint/cli'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['package.json', 'package.yaml', ...toCosmiconfig('commitlint', { additionalExtensions: ['cts'] })];\n\nconst resolveConfig: ResolveConfig<CommitLintConfig> = async config => {\n  const extendsConfigs = config.extends\n    ? [config.extends]\n        .flat()\n        .map(id => (id.startsWith('@') || id.startsWith('commitlint-config-') ? id : `commitlint-config-${id}`))\n    : [];\n  const plugins = config.plugins ? [config.plugins].flat().filter(s => typeof s === 'string') : [];\n  const formatter = config.formatter ? [config.formatter] : [];\n  const parserPreset = await config.parserPreset;\n  const parserPresetPaths: string[] = parserPreset\n    ? typeof parserPreset === 'string'\n      ? [parserPreset]\n      : parserPreset.path\n        ? [parserPreset.path ?? parserPreset]\n        : []\n    : [];\n  return [...extendsConfigs, ...plugins, ...formatter, ...parserPresetPaths].map(id => toDependency(id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/commitlint/types.ts",
    "content": "export type CommitLintConfig = {\n  extends?: string | string[];\n  plugins?: string[];\n  formatter?: string;\n  parserPreset?: string | ParserPreset | Promise<ParserPreset>;\n};\n\ntype ParserPreset = {\n  name?: string;\n  path?: string;\n  parserOpts?: unknown;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/convex/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://docs.convex.dev/home\n\nconst title = 'Convex';\n\nconst enablers = ['convex'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst entry: string[] = ['convex/*.config.@(js|ts)', 'convex/**/_generated/*.@(js|ts)'];\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  entry,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/create-typescript-app/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://github.com/JoshuaKGoldberg/create-typescript-app\n\nconst title = 'create-typescript-app';\n\nconst enablers = ['create-typescript-app'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst entry = ['create-typescript-app.config.{js,cjs,mjs,ts}'];\n\nconst plugin: Plugin = {\n  enablers,\n  entry,\n  isEnabled,\n  title,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/cspell/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDeferResolve } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { CSpellConfig } from './types.ts';\n\n// https://cspell.org/configuration/\n\nconst title = 'CSpell';\n\nconst enablers = ['cspell'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = [\n  'cspell.config.{js,cjs,mjs,ts,mts,json,yaml,yml}',\n  'cspell.{json,yaml,yml}',\n  '.c{s,S}pell.json',\n  'c{s,S}pell.json',\n];\n\nconst resolveConfig: ResolveConfig<CSpellConfig> = config => {\n  return [config?.import ?? []].flat().map(id => toDeferResolve(id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/cspell/types.ts",
    "content": "export type CSpellConfig = {\n  import?: string | string[];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/cucumber/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDeferResolve, toEntry } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { CucumberConfig } from './types.ts';\n\n// https://github.com/cucumber/cucumber-js/blob/main/docs/configuration.md\n\nconst title = 'Cucumber';\n\nconst enablers = ['@cucumber/cucumber'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['cucumber.{json,yaml,yml,js,cjs,mjs}'];\n\nconst entry = ['features/**/*.@(js|cjs|mjs)'];\n\nconst resolveConfig: ResolveConfig<CucumberConfig> = config => {\n  const imports = (config?.import ? config.import : entry).map(id => toEntry(id));\n  const formatters = config?.format ? config.format : [];\n  const requires = config?.require ? config.require : [];\n  return imports.concat([...formatters, ...requires].map(id => toDeferResolve(id)));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  entry,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/cucumber/types.ts",
    "content": "export type CucumberConfig = {\n  publishQuiet?: boolean;\n  import?: string[];\n  require?: string[];\n  format?: string[];\n  parallel?: number;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/cypress/helpers.ts",
    "content": "import type { PluginOptions } from '../../types/config.ts';\nimport { isInternal, toAbsolute } from '../../util/path.ts';\nimport { load } from '../../util/plugin.ts';\nimport type { CypressConfig } from './types.ts';\n\ninterface ReporterConfig {\n  reporterEnabled: string;\n}\n\nexport const resolveDependencies = async (config: CypressConfig, options: PluginOptions) => {\n  const { reporter } = config;\n  const { configFileDir } = options;\n\n  // Initialize the array of reporters with the initial reporter if present.\n  const reporters: Set<string> = reporter ? new Set([reporter]) : new Set();\n\n  // https://github.com/YOU54F/cypress-plugins/tree/master/cypress-multi-reporters#configuring-reporters\n  if (reporter === 'cypress-multi-reporters' && config.reporterOptions?.configFile) {\n    // Try to resolve the config file if present and attach the reporters listed in it.\n    const { configFile } = config.reporterOptions;\n    const configFilePath = toAbsolute(configFile, configFileDir);\n    if (isInternal(configFilePath)) {\n      const reporterConfig: ReporterConfig = await load(configFilePath);\n      if (typeof reporterConfig === 'object' && reporterConfig.reporterEnabled) {\n        const { reporterEnabled: reporterConcatenatedNames } = reporterConfig;\n        // Pulled from the reporter source code, https://github.com/YOU54F/cypress-plugins/blob/master/cypress-multi-reporters/lib/MultiReporters.js#L50-L58\n        // Not sure why they allow for extra whitespace characters, but let's handle it the same as them.\n        const reporterNames = reporterConcatenatedNames.split(',');\n        for (const reporterName of reporterNames) {\n          reporters.add(reporterName.trim());\n        }\n      }\n    }\n  }\n  return [...reporters];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/cypress/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDeferResolve, toEntry } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { resolveDependencies } from './helpers.ts';\nimport type { CypressConfig } from './types.ts';\n\n// https://docs.cypress.io/guides/references/configuration\n\nconst title = 'Cypress';\n\nconst enablers = ['cypress'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['cypress.config.{js,ts,mjs,cjs}'];\n\nconst TEST_FILE_PATTERNS = ['cypress/e2e/**/*.cy.{js,jsx,ts,tsx}'];\n\nconst SUPPORT_FILE_PATTERNS = [\n  'cypress/support/e2e.{js,jsx,ts,tsx}',\n  'cypress/support/commands.{js,ts}',\n  'cypress/support/component.{js,ts}',\n  'cypress/plugins/index.js', // Deprecated since Cypress v10\n];\n\nconst entry = [...TEST_FILE_PATTERNS, ...SUPPORT_FILE_PATTERNS];\n\nconst resolveConfig: ResolveConfig<CypressConfig> = async (localConfig, options) => {\n  const specPatterns = [localConfig.e2e?.specPattern ?? [], localConfig.component?.specPattern ?? []].flat();\n  const supportFiles = [localConfig.e2e?.supportFile || [], localConfig.component?.supportFile || []].flat();\n  const inputs = await resolveDependencies(localConfig, options);\n  return [\n    ...inputs.map(id => toDeferResolve(id)),\n    ...(specPatterns.length > 0 ? specPatterns : TEST_FILE_PATTERNS).map(id => toEntry(id)),\n    ...(supportFiles.length > 0 ? supportFiles : SUPPORT_FILE_PATTERNS).map(id => toEntry(id)),\n  ];\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  entry,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/cypress/types.ts",
    "content": "export interface CypressConfig {\n  reporter: string;\n  reporterOptions?: { configFile?: string };\n  component?: {\n    specPattern?: string[];\n    supportFile?: string;\n  };\n  e2e?: {\n    specPattern?: string[];\n    supportFile?: string;\n  };\n}\n"
  },
  {
    "path": "packages/knip/src/plugins/danger/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://danger.systems/js/guides/getting_started\n\nconst title = 'Danger';\n\nconst enablers = ['danger'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst entry = ['dangerfile.{js,cjs,mjs,ts}'];\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  entry,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/dependency-cruiser/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://github.com/sverweij/dependency-cruiser\n\nconst title = 'dependency-cruiser';\n\nconst enablers = ['dependency-cruiser'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['.dependency-cruiser.{js,cjs,mjs,json}'];\n\nconst args = {\n  binaries: ['depcruise', 'dependency-cruise', 'dependency-cruiser', 'depcruise-baseline'],\n  config: true,\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/docusaurus/helpers.ts",
    "content": "import type { PluginOptions as Options } from '../../types/config.ts';\nimport { type Input, toDeferResolve, toProductionEntry } from '../../util/input.ts';\nimport { findWebpackDependenciesFromConfig } from '../webpack/index.ts';\nimport type { ConfigItem, ModuleType, PluginOptions, PresetOptions } from './types.ts';\n\nconst FIRST_PARTY_MODULES = new Set([\n  'content-docs',\n  'content-blog',\n  'content-pages',\n  'debug',\n  'sitemap',\n  'svgr',\n  'rsdoctor',\n  'pwa',\n  'client-redirects',\n  'ideal-image',\n  'google-analytics',\n  'google-gtag',\n  'google-tag-manager',\n  'classic',\n  'live-codeblock',\n  'search-algolia',\n  'mermaid',\n]);\n\nexport const CORE_CLIENT_API = [\n  'BrowserOnly',\n  'ComponentCreator',\n  'constants',\n  'ExecutionEnvironment',\n  'Head',\n  'Interpolate',\n  'isInternalUrl',\n  'Link',\n  'Noop',\n  'renderRoutes',\n  'router',\n  'Translate',\n  'useBaseUrl',\n  'useBrokenLinks',\n  'useDocusaurusContext',\n  'useGlobalData',\n  'useIsBrowser',\n  'useIsomorphicLayoutEffect',\n  'useRouteContext',\n];\n\nconst resolveModuleName = (name: string, type: ModuleType): string => {\n  // If it's already a full package name, return it\n  if (name.includes(`${type}-`)) return name;\n\n  if (!name.startsWith('@')) {\n    const prefix = FIRST_PARTY_MODULES.has(name) ? '@docusaurus/' : 'docusaurus-';\n    return `${prefix}${type}-${name}`;\n  }\n\n  const [scope, ...rest] = name.split('/');\n  const baseName = rest.length ? `-${rest.join('/')}` : '';\n  return `${scope}/docusaurus-${type}${baseName}`;\n};\n\nconst resolveSidebarPath = (config: PresetOptions | PluginOptions): string | undefined => {\n  const path = config?.sidebarPath ?? (config as PresetOptions)?.docs?.sidebarPath;\n  return typeof path === 'string' ? path : undefined;\n};\n\nconst resolveArrayConfig = ([name, config]: [string, unknown], type: ModuleType) => {\n  if (typeof name !== 'string') return [];\n\n  const resolvedName = resolveModuleName(name, type);\n  const sidebarPath = type !== 'theme' ? resolveSidebarPath(config as PresetOptions | PluginOptions) : undefined;\n\n  return [toDeferResolve(resolvedName), ...(sidebarPath ? [toProductionEntry(sidebarPath)] : [])];\n};\n\nexport const resolveConfigItems = async (items: ConfigItem[], type: ModuleType, options: Options) => {\n  const inputs = new Set<Input>();\n\n  for (let item of items) {\n    if (typeof item === 'function') item = item();\n\n    if (!item) continue;\n\n    if (typeof item === 'string') {\n      inputs.add(toDeferResolve(resolveModuleName(item, type)));\n    } else if (Array.isArray(item)) {\n      for (const input of resolveArrayConfig(item, type)) inputs.add(input);\n    } else if (typeof item.configureWebpack === 'function') {\n      const utils = { getStyleLoaders: () => [], getJSLoader: () => null };\n      const config = item.configureWebpack({}, false, utils);\n      for (const input of await findWebpackDependenciesFromConfig(config, options)) inputs.add(input);\n    } else if (typeof item.configurePostCss === 'function') {\n      // ignore\n    }\n  }\n\n  return inputs;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/docusaurus/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { type Input, toAlias, toDependency, toEntry, toIgnore, toProductionEntry } from '../../util/input.ts';\nimport { join } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { CORE_CLIENT_API, resolveConfigItems } from './helpers.ts';\nimport type { DocusaurusConfig } from './types.ts';\n\n// https://docusaurus.io/docs/configuration\n\nconst title = 'Docusaurus';\n\nconst enablers = ['@docusaurus/core'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['docusaurus.config.{js,mjs,ts}'];\n\nconst production = ['src/pages/**/*.{js,ts,jsx,tsx}', '{blog,docs}/**/*.mdx', 'versioned_docs/**/*.{mdx,jsx,tsx}'];\n\nconst entry = ['babel.config.{js,cjs,mjs,cts}'];\n\nconst resolveStaticAssets = (items: DocusaurusConfig['scripts'] | DocusaurusConfig['stylesheets'], cwd: string) => {\n  const entries: Input[] = [];\n  for (const item of items ?? []) {\n    const value = typeof item === 'string' ? item : (item.src ?? item.href);\n    if (typeof value === 'string' && !value.includes('://'))\n      entries.push(toProductionEntry(join(cwd, 'static', value)));\n  }\n  return entries;\n};\n\nconst resolveConfig: ResolveConfig<DocusaurusConfig> = async (config, options) => {\n  const themes = await resolveConfigItems(config.themes ?? [], 'theme', options);\n  const plugins = await resolveConfigItems(config.plugins ?? [], 'plugin', options);\n  const presets = await resolveConfigItems(config.presets ?? [], 'preset', options);\n\n  const hasClassicTheme =\n    options.manifest.dependencies?.['@docusaurus/theme-classic'] ||\n    options.manifest.dependencies?.['@docusaurus/preset-classic'];\n\n  const scripts = resolveStaticAssets(config.scripts ?? [], options.cwd);\n  const stylesheets = resolveStaticAssets(config.stylesheets ?? [], options.cwd);\n\n  return [\n    toAlias('@site/*', './*'),\n    toDependency('@docusaurus/module-type-aliases', { optional: true }),\n    // Ignore aliases for @docusaurus/theme-classic/lib/theme/ https://docusaurus.io/docs/advanced/client#theme-aliases\n    ...(hasClassicTheme ? [toIgnore('(@theme|@theme-init|@theme-original)/*', 'dependencies')] : []),\n    // Ignore aliases for @docusaurus/core/lib/client/exports/ https://docusaurus.io/docs/docusaurus-core\n    toIgnore(`@docusaurus/(${CORE_CLIENT_API.join('|')})`, 'dependencies'),\n    // https://docusaurus.io/blog/releases/3.8\n    ...(config.future?.experimental_faster ? [toDependency('@docusaurus/faster')] : []),\n    ...production.map(id => toProductionEntry(id)),\n    ...entry.map(id => toEntry(id)),\n    ...themes,\n    ...plugins,\n    ...presets,\n    ...scripts,\n    ...stylesheets,\n  ];\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  entry,\n  production,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/docusaurus/types.ts",
    "content": "import type { WebpackConfig } from '../webpack/types.ts';\n\nexport type ModuleType = 'plugin' | 'theme' | 'preset';\n\ntype DocsConfig = {\n  sidebarPath?: string;\n  [key: string]: unknown;\n};\n\nexport type PluginOptions = {\n  sidebarPath?: string;\n  [key: string]: unknown;\n};\n\nexport type PresetOptions = {\n  docs?: DocsConfig;\n  [key: string]: unknown;\n};\n\ntype Loader = unknown;\n\ntype PluginConfig =\n  | string\n  | [string, PluginOptions]\n  | false\n  | null\n  | {\n      name?: string;\n      configureWebpack?: (\n        config?: PluginConfig,\n        isServer?: boolean,\n        utils?: {\n          getStyleLoaders(isServer: boolean, cssOptions: { [key: string]: any }): Loader[];\n          // oxlint-disable-next-line @typescript-eslint/no-empty-object-type\n          getJSLoader(isServer: boolean, cacheOptions?: {}): Loader | null;\n        },\n        content?: unknown\n      ) => WebpackConfig;\n      configurePostCss?: (postcssOptions: { plugins: unknown[] }) => { plugins: unknown[] };\n    };\n\ntype PresetConfig = string | [string, PresetOptions] | false | null;\n\ntype Config = PresetConfig | PluginConfig;\n\nexport type ConfigItem = Config | (() => Config);\n\ntype ScriptTag = { src: string; [key: string]: unknown };\ntype StylesheetLink = { href: string; [key: string]: unknown };\n\nexport type DocusaurusConfig = {\n  title: string;\n  url: string;\n  themes?: PluginConfig[];\n  plugins?: PluginConfig[];\n  presets: PresetConfig[];\n  scripts?: (string | ScriptTag)[];\n  stylesheets?: (string | StylesheetLink)[];\n  future?: {\n    experimental_faster?: boolean | { [key: string]: unknown };\n    [key: string]: unknown;\n  };\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/dotenv/index.ts",
    "content": "import type { ParsedArgs } from 'minimist';\nimport { argsFrom } from '../../binaries/util.ts';\nimport type { Plugin } from '../../types/config.ts';\n\n// https://www.npmjs.com/package/dotenv\n\nconst title = 'dotenv';\n\nconst args = {\n  fromArgs: (parsed: ParsedArgs, args: string[]) => {\n    if (parsed._[0]) return argsFrom(args, parsed._[0]);\n    if (!parsed['--'] || parsed['--'].length === 0) return [];\n    const script = parsed['--'].map(arg => (arg.includes(' ') ? `\"${arg}\"` : arg)).join(' ');\n    return [script];\n  },\n};\n\nconst plugin: Plugin = {\n  title,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/drizzle/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toProductionEntry } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { DrizzleConfig } from './types.ts';\n\n// https://orm.drizzle.team/kit-docs/overview\n\nconst title = 'Drizzle';\n\nconst enablers = ['drizzle-kit'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['drizzle.config.{ts,js,json}'];\n\nconst resolveConfig: ResolveConfig<DrizzleConfig> = config => {\n  if (!config.schema) return [];\n  return [config.schema].flat().map(id => toProductionEntry(id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/drizzle/types.ts",
    "content": "export interface DrizzleConfig {\n  schema?: string | string[];\n}\n"
  },
  {
    "path": "packages/knip/src/plugins/eleventy/helpers.ts",
    "content": "// https://github.com/11ty/eleventy/blob/main/src/UserConfig.js\n\n/** @public */\nexport class DummyEleventyConfig {\n  _getUniqueId() {}\n  reset() {}\n  versionCheck() {}\n  on() {}\n  emit() {}\n  _enablePluginExecution() {}\n  addMarkdownHighlighter() {}\n  addLiquidTag() {}\n  addLiquidFilter() {}\n  addNunjucksAsyncFilter() {}\n  addNunjucksFilter() {}\n  addHandlebarsHelper() {}\n  addFilter() {}\n  addAsyncFilter() {}\n  getFilter() {}\n  addNunjucksTag() {}\n  addGlobalData() {}\n  addNunjucksGlobal() {}\n  addTransform() {}\n  addLinter() {}\n  addLayoutAlias() {}\n  setLayoutResolution() {}\n  enableLayoutResolution() {}\n  getCollections() {}\n  addCollection() {}\n  addPlugin() {}\n  _getPluginName() {}\n  _executePlugin() {}\n  getNamespacedName() {}\n  namespace() {}\n  addPassthroughCopy(input: string | Record<string, string>) {\n    if (typeof input === 'string') {\n      this.passthroughCopies[input] = {};\n    } else {\n      for (const [inputPath] of Object.entries(input)) {\n        this.passthroughCopies[inputPath] = {};\n      }\n    }\n  }\n  _normalizeTemplateFormats() {}\n  setTemplateFormats() {}\n  addTemplateFormats() {}\n  setLibrary() {}\n  amendLibrary() {}\n  setPugOptions() {}\n  setLiquidOptions() {}\n  setNunjucksEnvironmentOptions() {}\n  setNunjucksPrecompiledTemplates() {}\n  setEjsOptions() {}\n  setDynamicPermalinks() {}\n  setUseGitIgnore() {}\n  addShortcode() {}\n  addAsyncShortcode() {}\n  addNunjucksAsyncShortcode() {}\n  addNunjucksShortcode() {}\n  addLiquidShortcode() {}\n  addHandlebarsShortcode() {}\n  addPairedShortcode() {}\n  addPairedAsyncShortcode() {}\n  addPairedNunjucksAsyncShortcode() {}\n  addPairedNunjucksShortcode() {}\n  addPairedLiquidShortcode() {}\n  addPairedHandlebarsShortcode() {}\n  addJavaScriptFunction() {}\n  setDataDeepMerge() {}\n  isDataDeepMergeModified() {}\n  addWatchTarget() {}\n  setWatchJavaScriptDependencies() {}\n  setServerOptions() {}\n  setBrowserSyncConfig() {}\n  setChokidarConfig() {}\n  setWatchThrottleWaitTime() {}\n  setFrontMatterParsingOptions() {}\n  setQuietMode() {}\n  addExtension() {}\n  addDataExtension() {}\n  setUseTemplateCache() {}\n  setPrecompiledCollections() {}\n  setServerPassthroughCopyBehavior() {}\n  addUrlTransform() {}\n  setDataFileSuffixes() {}\n  setDataFileBaseName() {}\n  getMergingConfigObject() {}\n  isVirtualTemplate() {}\n  setInputDirectory() {}\n  setOutputDirectory() {}\n  setDataDirectory() {}\n  setIncludesDirectory() {}\n  setLayoutsDirectory() {}\n  setFreezeReservedData() {}\n  addDateParsing() {}\n  addBundle() {}\n\n  _uniqueId = {};\n  events = {};\n  benchmarkManager = {};\n  benchmarks = {};\n  collections = {};\n  precompiledCollections = {};\n  templateFormats = {};\n  liquidOptions = {};\n  liquidTags = {};\n  liquidFilters = {};\n  liquidShortcodes = {};\n  liquidPairedShortcodes = {};\n  nunjucksEnvironmentOptions = {};\n  nunjucksPrecompiledTemplates = {};\n  nunjucksFilters = {};\n  nunjucksAsyncFilters = {};\n  nunjucksTags = {};\n  nunjucksGlobals = {};\n  nunjucksShortcodes = {};\n  nunjucksAsyncShortcodes = {};\n  nunjucksPairedShortcodes = {};\n  nunjucksAsyncPairedShortcodes = {};\n  javascriptFunctions = {};\n  markdownHighlighter = null;\n  libraryOverrides = {};\n  passthroughCopies: Record<string, Record<never, never>> = {};\n  layoutAliases = {};\n  layoutResolution = true;\n  linters = {};\n  transforms = {};\n  activeNamespace = '';\n  DateTime = {};\n  dynamicPermalinks = true;\n  useGitIgnore = true;\n  ignores = new Set();\n  watchIgnores = new Set();\n  dataDeepMerge = true;\n  extensionMap = new Set();\n  watchJavaScriptDependencies = true;\n  additionalWatchTargets = [];\n  serverOptions = {};\n  globalData = {};\n  chokidarConfig = {};\n  watchThrottleWaitTime = 0;\n  dataExtensions = new Map();\n  quietMode = false;\n  plugins = [];\n  _pluginExecution = false;\n  useTemplateCache = true;\n  dataFilterSelectors = new Set();\n  libraryAmendments = {};\n  serverPassthroughCopyBehavior = '';\n  urlTransforms = [];\n  dataFileSuffixesOverride = false;\n  dataFileDirBaseNameOverride = false;\n  frontMatterParsingOptions = {\n    engines: {},\n  };\n  templateFormatsAdded = {};\n}\n\n// https://www.11ty.dev/docs/config/#configuration-options\nexport const defaultEleventyConfig = {\n  dir: {\n    input: '.',\n    output: '_site',\n    includes: '_includes',\n    layouts: '_includes',\n    data: '_data',\n  },\n  templateFormats: '11ty.js',\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/eleventy/index.ts",
    "content": "import { DEFAULT_EXTENSIONS } from '../../constants.ts';\nimport type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { isDirectory } from '../../util/fs.ts';\nimport { toDeferResolve, toProductionEntry } from '../../util/input.ts';\nimport { isInNodeModules, join } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { DummyEleventyConfig, defaultEleventyConfig } from './helpers.ts';\nimport type { EleventyConfig, EleventyConfigOrFn } from './types.ts';\n\n// https://www.11ty.dev/docs/\n\nconst title = 'Eleventy';\n\nconst enablers = ['@11ty/eleventy'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['.eleventy.js', 'eleventy.config.{js,cjs,mjs}'];\n\nconst production = ['posts/**/*.11tydata.js', '_data/**/*.{js,cjs,mjs}'];\n\nconst resolveConfig: ResolveConfig<EleventyConfigOrFn> = async (localConfig, options) => {\n  const { configFileDir } = options;\n\n  const dummyUserConfig = new DummyEleventyConfig();\n\n  if (typeof localConfig === 'function') localConfig = (await localConfig(dummyUserConfig)) as EleventyConfig;\n\n  const inputDir = localConfig?.dir?.input || defaultEleventyConfig.dir.input;\n  const dataDir = localConfig?.dir?.data || defaultEleventyConfig.dir.data;\n  const templateFormats = localConfig?.templateFormats || defaultEleventyConfig.templateFormats;\n\n  const exts = [...DEFAULT_EXTENSIONS].map(extname => extname.slice(1)).join(',');\n  const copiedEntries = new Set<string>();\n  const copiedPackages = new Set<string>();\n\n  for (const path of Object.keys(dummyUserConfig.passthroughCopies)) {\n    const isDir = !path.includes('*') && isDirectory(configFileDir, path);\n    if (isDir) {\n      copiedEntries.add(join(path, `**/*.{${exts}}`));\n    } else if (!isInNodeModules(path)) {\n      copiedEntries.add(path);\n    }\n  }\n\n  for (const path of Object.keys(dummyUserConfig.passthroughCopies)) {\n    if (isInNodeModules(path)) copiedPackages.add(path);\n  }\n\n  return Array.from(copiedPackages)\n    .map(id => toDeferResolve(id))\n    .concat(\n      [\n        join(inputDir, dataDir, '**/*.{js,cjs,mjs}'),\n        join(inputDir, `**/*.{${typeof templateFormats === 'string' ? templateFormats : templateFormats.join(',')}}`),\n        join(inputDir, '**/*.11tydata.js'),\n        ...copiedEntries,\n      ].map(id => toProductionEntry(id))\n    );\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  production,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/eleventy/types.ts",
    "content": "import type { DummyEleventyConfig } from './helpers.ts';\n\nexport type EleventyConfig = {\n  dir?: {\n    input?: string;\n    output?: string;\n    includes?: string;\n    layouts?: string;\n    data?: string;\n  };\n  templateFormats?: string | string[];\n};\n\nexport type EleventyConfigOrFn =\n  | Partial<EleventyConfig>\n  | ((arg: DummyEleventyConfig) => Promise<Partial<EleventyConfig>>);\n"
  },
  {
    "path": "packages/knip/src/plugins/eslint/helpers.ts",
    "content": "import type { PluginOptions } from '../../types/config.ts';\nimport { compact } from '../../util/array.ts';\nimport { type ConfigInput, type Input, toConfig, toDeferResolve, toDependency } from '../../util/input.ts';\nimport { getPackageNameFromFilePath, getPackageNameFromModuleSpecifier } from '../../util/modules.ts';\nimport { extname, isAbsolute, isInternal } from '../../util/path.ts';\nimport { getDependenciesFromConfig } from '../babel/index.ts';\nimport type { ESLintConfig, ESLintConfigDeprecated, OverrideConfigDeprecated } from './types.ts';\n\nexport const isFlatConfig = (fileName: string) => /eslint\\.config/.test(fileName);\n\nexport const getInputs = (\n  config: ESLintConfigDeprecated | OverrideConfigDeprecated | ESLintConfig,\n  options: PluginOptions\n): (Input | ConfigInput)[] => {\n  const { configFileName } = options;\n\n  if (extname(configFileName) === '.json' || !isFlatConfig(configFileName)) {\n    return getInputsDeprecated(config as ESLintConfigDeprecated | OverrideConfigDeprecated, options);\n  }\n\n  const configArray = Array.isArray(config) ? config : [config];\n  const dependencies = configArray.flatMap(config =>\n    config.settings ? getDependenciesFromSettings(config.settings) : []\n  );\n\n  dependencies.push('eslint-import-resolver-typescript');\n\n  return compact(dependencies).map(id => toDeferResolve(id, { optional: true }));\n};\n\nconst getInputsDeprecated = (\n  config: ESLintConfigDeprecated | OverrideConfigDeprecated,\n  options: PluginOptions\n): (Input | ConfigInput)[] => {\n  const extendsSpecifiers = config.extends ? compact([config.extends].flat().map(resolveExtendSpecifier)) : [];\n  // https://github.com/prettier/eslint-plugin-prettier#recommended-configuration\n  if (extendsSpecifiers.some(specifier => specifier?.startsWith('eslint-plugin-prettier')))\n    extendsSpecifiers.push('eslint-config-prettier');\n  const extendConfigs = extendsSpecifiers.map(specifier =>\n    toConfig('eslint', specifier, { containingFilePath: options.configFilePath })\n  );\n  const plugins = config.plugins ? config.plugins.map(resolvePluginSpecifier) : [];\n  const parser = config.parser ?? config.parserOptions?.parser;\n  const babelDependencies = config.parserOptions?.babelOptions\n    ? getDependenciesFromConfig(config.parserOptions.babelOptions)\n    : [];\n  const settings = config.settings ? getDependenciesFromSettings(config.settings) : [];\n  // const rules = getDependenciesFromRules(config.rules); // TODO enable in next major? Unexpected/breaking in certain cases w/ eslint v8\n  const rules = getDependenciesFromRules({});\n  const overrides = config.overrides ? [config.overrides].flat().flatMap(d => getInputsDeprecated(d, options)) : [];\n  const deferred = compact([...extendsSpecifiers, ...plugins, parser, ...settings, ...rules]).map(id =>\n    toDeferResolve(id)\n  );\n  return [...extendConfigs, ...deferred, ...babelDependencies, ...overrides];\n};\n\nconst isQualifiedSpecifier = (specifier: string) =>\n  specifier === 'eslint' ||\n  /\\/eslint-(config|plugin)$/.test(specifier) ||\n  /.+eslint-(config|plugin)\\//.test(specifier) ||\n  /eslint-(config|plugin)-/.test(specifier);\n\nconst resolveSpecifier = (namespace: 'eslint-plugin' | 'eslint-config', rawSpecifier: string) => {\n  const specifier = rawSpecifier.replace(/(^plugin:|:.+$)/, '');\n  if (isQualifiedSpecifier(specifier)) return specifier;\n  if (!specifier.startsWith('@')) {\n    const id = rawSpecifier.startsWith('plugin:')\n      ? getPackageNameFromModuleSpecifier(specifier)\n      : specifier.split('/')[0];\n    return `${namespace}-${id}`;\n  }\n  const [scope, name, ...rest] = specifier.split('/');\n  if (rawSpecifier.startsWith('plugin:') && rest.length === 0) return [scope, namespace].join('/');\n  return [scope, name ? `${namespace}-${name}` : namespace, ...rest].join('/');\n};\n\nconst resolvePluginSpecifier = (specifier: string) => resolveSpecifier('eslint-plugin', specifier);\n\nconst resolveExtendSpecifier = (specifier: string) => {\n  if (isInternal(specifier)) return;\n\n  const namespace = specifier.startsWith('plugin:') ? 'eslint-plugin' : 'eslint-config';\n  return resolveSpecifier(namespace, specifier);\n};\n\nconst getDependenciesFromRules = (rules: ESLintConfigDeprecated['rules'] = {}) =>\n  Object.keys(rules).flatMap(ruleKey =>\n    ruleKey.includes('/') ? [resolveSpecifier('eslint-plugin', ruleKey.split('/').slice(0, -1).join('/'))] : []\n  );\n\nconst getDependenciesFromSettings = (settings: ESLintConfigDeprecated['settings'] = {}) => {\n  return Object.entries(settings).flatMap(([settingKey, settings]) => {\n    if (settingKey === 'import/resolver') {\n      return (typeof settings === 'string' ? [settings] : Object.keys(settings))\n        .filter(key => key !== 'node')\n        .map(key => {\n          // TODO Resolve properly\n          if (isInternal(key)) return key;\n          if (isAbsolute(key)) return getPackageNameFromFilePath(key);\n          return `eslint-import-resolver-${key}`;\n        });\n    }\n    if (settingKey === 'import/parsers') {\n      return (typeof settings === 'string' ? [settings] : Object.keys(settings)).map(key => {\n        // TODO Resolve properly\n        if (isAbsolute(key)) return getPackageNameFromFilePath(key);\n        return key;\n      });\n    }\n  });\n};\n\nconst builtinFormatters = new Set(['html', 'json-with-metadata', 'json', 'stylish']);\nexport const resolveFormatters = (formatters: string | string[]) => {\n  const inputs: Set<Input> = new Set();\n  for (const formatter of [formatters].flat()) {\n    if (builtinFormatters.has(formatter)) continue;\n    else if (isInternal(formatter)) inputs.add(toDeferResolve(formatter));\n    else inputs.add(toDependency(`eslint-formatter-${formatter}`));\n  }\n  return inputs;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/eslint/index.ts",
    "content": "import type { ParsedArgs } from 'minimist';\nimport type { IsLoadConfig, IsPluginEnabled, Plugin, ResolveConfig, ResolveFromAST } from '../../types/config.ts';\nimport { type Input, toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { getInputs, isFlatConfig, resolveFormatters } from './helpers.ts';\nimport { getInputsFromFlatConfigAST } from './resolveFromAST.ts';\nimport type { ESLintConfigDeprecated } from './types.ts';\n\n// https://eslint.org/docs/latest/use/configure/configuration-files\n// Deprecated: https://eslint.org/docs/latest/use/configure/configuration-files-deprecated\n\n// Note: shareable configs should use `peerDependencies` for plugins\n// https://eslint.org/docs/latest/extend/shareable-configs#publishing-a-shareable-config\n\nconst title = 'ESLint';\n\nconst enablers = ['eslint', '@eslint/js'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies, manifest }) =>\n  hasDependency(dependencies, enablers) ||\n  Boolean(manifest.name && /(^eslint-config|\\/eslint-config)/.test(manifest.name));\n\nconst packageJsonPath = 'eslintConfig';\n\nconst config = [\n  'eslint.config.{js,cjs,mjs,ts,cts,mts}',\n  '.eslintrc',\n  '.eslintrc.{js,json,cjs}',\n  '.eslintrc.{yml,yaml}',\n  'package.json',\n];\n\nconst isLoadConfig: IsLoadConfig = ({ configFileName, manifest }, dependencies) => {\n  // Flat configs (eslint.config.*) are handled by resolveFromAST — skip loading\n  if (isFlatConfig(configFileName)) return false;\n\n  const version = manifest.devDependencies?.['eslint'] || manifest.dependencies?.['eslint'];\n  if (version) {\n    const major = version.match(/\\d+/);\n    if (major && Number.parseInt(major[0], 10) === 9 && dependencies.has('eslint-config-next')) {\n      return false;\n    }\n  }\n  return true;\n};\n\nconst resolveConfig: ResolveConfig<ESLintConfigDeprecated> = (localConfig, options) => getInputs(localConfig, options);\n\nconst resolveFromAST: ResolveFromAST = (program, options) => {\n  if (isFlatConfig(options.configFileName)) return getInputsFromFlatConfigAST(program);\n  return [];\n};\n\nconst note = `### ESLint v9\n\nThe ESLint plugin config resolver is disabled when using \\`eslint-config-next\\` (\\`next lint\\`).\n\nRoot cause: [microsoft/rushstack#4965](https://github.com/microsoft/rushstack/issues/4965)/[#5049](https://github.com/microsoft/rushstack/issues/5049)\n\n### ESLint v8\n\nIf relying on [configuration cascading](https://eslint.org/docs/v8.x/use/configure/configuration-files#cascading-and-hierarchy),\nconsider using an extended glob pattern like this:\n\n\\`\\`\\`json\n{\n  \"eslint\": [\"**/.eslintrc.js\"]\n}\n\\`\\`\\`\n`;\n\n/** @public */\nexport const docs = { note };\n\nconst args = {\n  config: true,\n  alias: { format: ['f'] },\n  boolean: ['inspect-config'],\n  resolveInputs: (parsed: ParsedArgs) => {\n    const inputs: Input[] = [];\n    if (parsed['inspect-config']) inputs.push(toDependency('@eslint/config-inspector', { optional: true }));\n    if (parsed['format']) for (const input of resolveFormatters(parsed['format'])) inputs.push(input);\n    return inputs;\n  },\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  packageJsonPath,\n  config,\n  args,\n  isLoadConfig,\n  resolveConfig,\n  resolveFromAST,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/eslint/resolveFromAST.ts",
    "content": "import type { Program } from 'oxc-parser';\nimport { Visitor } from 'oxc-parser';\nimport { type Input, toDeferResolve } from '../../util/input.ts';\nimport { findProperty } from '../../typescript/ast-helpers.ts';\nimport { isInternal } from '../../util/path.ts';\n\nexport const getInputsFromFlatConfigAST = (program: Program): Input[] => {\n  const inputs: Input[] = [];\n\n  const visitor = new Visitor({\n    ObjectExpression(node) {\n      const settingsNode = findProperty(node, 'settings');\n      if (!settingsNode || settingsNode.type !== 'ObjectExpression') return;\n\n      for (const prop of settingsNode.properties ?? []) {\n        if (prop.type !== 'Property') continue;\n        const key = prop.key?.name ?? prop.key?.value;\n        if (key === 'import/resolver' || key === 'import/parsers') {\n          if (prop.value?.type === 'ObjectExpression') {\n            for (const p of prop.value.properties ?? []) {\n              if (p.type !== 'Property') continue;\n              const resolver = p.key?.name ?? p.key?.value;\n              if (resolver && resolver !== 'node' && !isInternal(resolver)) {\n                const dep = key === 'import/resolver' ? `eslint-import-resolver-${resolver}` : resolver;\n                inputs.push(toDeferResolve(dep, { optional: true }));\n              }\n            }\n          } else if (\n            prop.value?.type === 'StringLiteral' ||\n            (prop.value?.type === 'Literal' && typeof prop.value.value === 'string')\n          ) {\n            const resolver = prop.value.value;\n            if (resolver && resolver !== 'node' && !isInternal(resolver)) {\n              const dep = key === 'import/resolver' ? `eslint-import-resolver-${resolver}` : resolver;\n              inputs.push(toDeferResolve(dep, { optional: true }));\n            }\n          }\n        }\n      }\n    },\n  });\n  visitor.visit(program);\n\n  inputs.push(toDeferResolve('eslint-import-resolver-typescript', { optional: true }));\n\n  return inputs;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/eslint/types.ts",
    "content": "type ParserOptions = {\n  project?: string;\n  parser?: string;\n  babelOptions?: {\n    plugins: string[];\n    presets: string[];\n  };\n};\n\ntype Settings = Record<string, Record<string, unknown> | string>;\n\ntype Rules = Record<string, string | number>;\n\ntype BaseConfig = {\n  extends?: string | string[];\n  parser?: string;\n  parserOptions?: ParserOptions;\n  processor?: string;\n  plugins?: string[];\n  settings?: Settings;\n  rules?: Rules;\n};\n\nexport type ESLintConfig = BaseConfig[];\n\nexport type OverrideConfigDeprecated = BaseConfig & { files: string[]; overrides: OverrideConfigDeprecated };\n\nexport type ESLintConfigDeprecated = BaseConfig & {\n  env?: Record<string, boolean>;\n  overrides?: OverrideConfigDeprecated[];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/execa/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, RegisterVisitors } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { createExecaVisitor } from './visitors/execa.ts';\n\n// https://github.com/sindresorhus/execa\n\nconst title = 'execa';\n\nconst enablers = ['execa'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst registerVisitors: RegisterVisitors = ({ ctx, registerVisitor }) => {\n  registerVisitor(createExecaVisitor(ctx));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  registerVisitors,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/execa/visitors/execa.ts",
    "content": "import type { PluginVisitorContext, PluginVisitorObject } from '../../../types/config.ts';\nimport { getStringValue, isStringLiteral } from '../../../typescript/visitors/helpers.ts';\n\nconst tags = new Set(['$', '$sync']);\nconst methods = new Set(['execa', 'execaSync', 'execaCommand', 'execaCommandSync', '$sync']);\n\nexport function createExecaVisitor(ctx: PluginVisitorContext): PluginVisitorObject {\n  return {\n    TaggedTemplateExpression(node) {\n      const tag = node.tag;\n      const tagName =\n        tag.type === 'Identifier'\n          ? tag.name\n          : tag.type === 'CallExpression' && tag.callee.type === 'Identifier'\n            ? tag.callee.name\n            : undefined;\n      if (tagName && tags.has(tagName)) {\n        for (const q of node.quasi.quasis) {\n          if (q.value.raw) ctx.addScript(q.value.raw);\n        }\n      }\n    },\n    CallExpression(node) {\n      if (node.callee.type !== 'Identifier' || !methods.has(node.callee.name)) return;\n      const fnName = node.callee.name;\n      if (fnName.startsWith('execaCommand')) {\n        if (node.arguments[0] && isStringLiteral(node.arguments[0])) {\n          const val = getStringValue(node.arguments[0]);\n          if (val) ctx.addScript(val);\n        }\n      } else {\n        const executable = node.arguments[0];\n        if (executable && isStringLiteral(executable)) {\n          const executableStr = getStringValue(executable)!;\n          const args = node.arguments[1];\n          if (args?.type === 'ArrayExpression') {\n            const argStrings: string[] = [];\n            for (const a of args.elements) {\n              if (a && isStringLiteral(a)) argStrings.push(getStringValue(a)!);\n            }\n            ctx.addScript([executableStr, ...argStrings].join(' '));\n          } else {\n            ctx.addScript(executableStr);\n          }\n        }\n      }\n    },\n  };\n}\n"
  },
  {
    "path": "packages/knip/src/plugins/expo/helpers.ts",
    "content": "import type { PluginOptions, ResolveConfig } from '../../types/config.ts';\nimport { type Input, toDependency, toProductionDependency } from '../../util/input.ts';\nimport { getPackageNameFromModuleSpecifier } from '../../util/modules.ts';\nimport { join } from '../../util/path.ts';\nimport type { ExpoConfig } from './types.ts';\n\nconst getDummyConfigContext = (options: PluginOptions) => ({\n  projectRoot: options.cwd,\n  staticConfigPath: null,\n  packageJsonPath: join(options.cwd, 'package.json'),\n  config: {\n    plugins: [],\n  },\n});\n\nexport const getConfig = (localConfig: ExpoConfig, options: PluginOptions) => {\n  const expoConfig = typeof localConfig === 'function' ? localConfig(getDummyConfigContext(options)) : localConfig;\n  return 'expo' in expoConfig ? expoConfig.expo : expoConfig;\n};\n\n// https://docs.expo.dev/versions/latest/config/app\n\nexport const getDependencies: ResolveConfig<ExpoConfig> = async (localConfig, options) => {\n  const { manifest } = options;\n  const config = getConfig(localConfig, options);\n\n  const platforms = config.platforms ?? ['ios', 'android'];\n\n  const pluginPackages =\n    (config.plugins\n      ?.map(plugin => {\n        const pluginName = Array.isArray(plugin) ? plugin[0] : plugin;\n        return getPackageNameFromModuleSpecifier(pluginName);\n      })\n      .filter(Boolean) as string[]) ?? [];\n\n  const inputs = new Set<Input>(pluginPackages.map(id => toDependency(id)));\n\n  const allowedPackages = ['expo-atlas', 'expo-dev-client'];\n  const allowedProductionPackages = ['expo-insights'];\n\n  const manifestDependencies = Object.keys(manifest.dependencies ?? {});\n\n  for (const pkg of allowedPackages) {\n    if (manifestDependencies.includes(pkg)) {\n      inputs.add(toDependency(pkg));\n    }\n  }\n\n  for (const pkg of allowedProductionPackages) {\n    if (manifestDependencies.includes(pkg)) {\n      inputs.add(toProductionDependency(pkg));\n    }\n  }\n\n  if (config.updates?.enabled !== false) {\n    inputs.add(toProductionDependency('expo-updates'));\n  }\n\n  if (config.notification) {\n    inputs.add(toProductionDependency('expo-notifications'));\n  }\n\n  const isExpoRouter = manifest.main === 'expo-router/entry';\n\n  // https://docs.expo.dev/router/installation/#setup-entry-point\n  if (isExpoRouter) {\n    inputs.add(toProductionDependency('expo-router'));\n  }\n\n  // https://docs.expo.dev/workflow/web/#install-web-dependencies\n  if (platforms.includes('web')) {\n    inputs.add(toProductionDependency('react-native-web'));\n    inputs.add(toProductionDependency('react-dom'));\n\n    // https://github.com/expo/expo/tree/main/packages/@expo/metro-runtime\n    if (!isExpoRouter) {\n      inputs.add(toDependency('@expo/metro-runtime'));\n    }\n  }\n\n  if (\n    (platforms.includes('android') && (config.userInterfaceStyle || config.android?.userInterfaceStyle)) ||\n    (platforms.includes('ios') && (config.backgroundColor || config.ios?.backgroundColor))\n  ) {\n    inputs.add(toProductionDependency('expo-system-ui'));\n  }\n\n  if (platforms.includes('android') && config.androidNavigationBar) {\n    inputs.add(toProductionDependency('expo-navigation-bar'));\n  }\n\n  return [...inputs];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/expo/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toProductionEntry } from '../../util/input.ts';\nimport { join } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { getConfig, getDependencies } from './helpers.ts';\nimport type { ExpoConfig } from './types.ts';\n\n// https://docs.expo.dev/\n\nconst title = 'Expo';\n\nconst enablers = ['expo'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['app.json', 'app.config.{ts,js}'];\n\nconst production = ['app/**/*.{js,jsx,ts,tsx}', 'src/app/**/*.{js,jsx,ts,tsx}'];\n\nconst resolveConfig: ResolveConfig<ExpoConfig> = async (localConfig, options) => {\n  const { manifest } = options;\n  const config = getConfig(localConfig, options);\n\n  // https://docs.expo.dev/router/installation/#setup-entry-point\n  if (manifest.main === 'expo-router/entry') {\n    let patterns = [...production];\n\n    const normalizedPlugins =\n      config.plugins?.map(plugin => (Array.isArray(plugin) ? plugin : ([plugin] as const))) ?? [];\n    const expoRouterPlugin = normalizedPlugins.find(([plugin]) => plugin === 'expo-router');\n\n    if (expoRouterPlugin) {\n      const [, options] = expoRouterPlugin;\n\n      if (typeof options?.root === 'string') {\n        patterns = [join(options.root, '**/*.{js,jsx,ts,tsx}')];\n      }\n    }\n\n    return patterns.map(entry => toProductionEntry(entry)).concat(await getDependencies(localConfig, options));\n  }\n\n  return production.map(entry => toProductionEntry(entry)).concat(await getDependencies(localConfig, options));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  production,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/expo/types.ts",
    "content": "// https://github.com/expo/expo/blob/main/packages/%40expo/config-types/src/ExpoConfig.ts\n// https://github.com/expo/expo/blob/main/packages/%40expo/config/src/Config.types.ts\n\ntype BaseConfig = {\n  platforms?: ('ios' | 'android' | 'web')[];\n  notification?: Record<string, unknown>;\n  updates?: {\n    enabled?: boolean;\n  };\n  backgroundColor?: string;\n  userInterfaceStyle?: 'automatic' | 'light' | 'dark';\n  ios?: {\n    backgroundColor?: string;\n  };\n  android?: {\n    userInterfaceStyle?: 'automatic' | 'light' | 'dark';\n  };\n  androidNavigationBar?: Record<string, unknown>;\n  plugins?: (string | [string, Record<string, unknown>])[];\n};\n\ntype ConfigContext = {\n  projectRoot: string;\n  staticConfigPath: string | null;\n  packageJsonPath: string | null;\n  config: Partial<BaseConfig>;\n};\n\ntype ExpoConfigOrProp = BaseConfig | { expo: BaseConfig };\n\nexport type ExpoConfig = ExpoConfigOrProp | ((cfg: ConfigContext) => ExpoConfigOrProp);\n"
  },
  {
    "path": "packages/knip/src/plugins/expressive-code/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://expressive-code.com/reference/configuration/\n\nconst title = 'Expressive Code';\n\nconst enablers = [\n  'astro-expressive-code', // Astro integration\n  'rehype-expressive-code', // Next.js/rehype integration\n  '@astrojs/starlight', // Starlight (has expressive-code built-in)\n];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['ec.config.{js,mjs,cjs,ts}'];\n\nexport default {\n  title,\n  enablers,\n  isEnabled,\n  config,\n} satisfies Plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/gatsby/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDeferResolve, toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { GatsbyActions, GatsbyConfig, GatsbyNode } from './types.ts';\n\n// https://github.com/gatsbyjs/gatsby/blob/master/docs/docs/reference/gatsby-project-structure.md\n// https://github.com/gatsbyjs/gatsby/blob/master/docs/docs/reference/config-files/gatsby-config.md\n\nconst title = 'Gatsby';\n\nconst enablers = ['gatsby', 'gatsby-cli'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['gatsby-{config,node}.{js,jsx,ts,tsx}', 'plugins/**/gatsby-node.{js,jsx,ts,tsx}'];\n\nconst production = [\n  'gatsby-{browser,ssr}.{js,jsx,ts,tsx}',\n  'src/api/**/*.{js,ts}',\n  'src/pages/**/*.{js,jsx,ts,tsx}',\n  'src/templates/**/*.{js,jsx,ts,tsx}',\n  'src/html.{js,jsx,ts,tsx}',\n  'plugins/**/gatsby-{browser,ssr}.{js,jsx,ts,tsx}',\n];\n\nconst resolveConfig: ResolveConfig<GatsbyConfig | GatsbyNode> = async (localConfig, options) => {\n  const { configFileName } = options;\n\n  if (/gatsby-config/.test(configFileName)) {\n    return (localConfig as GatsbyConfig).plugins\n      .map(plugin => (typeof plugin === 'string' ? plugin : plugin.resolve))\n      .map(id => toDeferResolve(id));\n  }\n\n  if (/gatsby-node/.test(configFileName)) {\n    const plugins = new Set<string>();\n    const actions: GatsbyActions['actions'] = { setBabelPlugin: plugin => plugins.add(plugin.name) };\n    const _config = localConfig as GatsbyNode;\n    if (typeof _config.onCreateBabelConfig === 'function') {\n      _config.onCreateBabelConfig({ actions });\n    }\n    return Array.from(plugins).map(id => toDependency(id));\n  }\n\n  return [];\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  production,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/gatsby/types.ts",
    "content": "export type GatsbyConfig = {\n  plugins: (string | { resolve: string })[];\n};\n\nexport type GatsbyActions = {\n  actions: {\n    setBabelPlugin: ({ name }: { name: string }) => void;\n  };\n};\n\nexport type GatsbyNode = {\n  onCreateBabelConfig: (options: GatsbyActions) => void;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/github-action/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toEntry, toProject } from '../../util/input.ts';\nimport { relative } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { getActionDependencies } from '../github-actions/index.ts';\n\n// https://docs.github.com/en/actions/sharing-automations/creating-actions/metadata-syntax-for-github-actions\n\nconst title = 'GitHub Action';\n\nconst enablers = ['@actions/core'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['action.{yml,yaml}'];\n\nconst isAssumeArtifact = (specifier: string) => /^(dist|build)\\//.test(specifier);\n\nconst resolveConfig: ResolveConfig = async (config, options) => {\n  const inputs = [];\n  const filePaths = getActionDependencies(config, options);\n  for (const filePath of new Set(filePaths)) {\n    const relativePath = relative(options.cwd, filePath);\n    if (isAssumeArtifact(relativePath)) inputs.push(toProject(`!${relativePath}`));\n    else inputs.push(toEntry(relativePath));\n  }\n  return inputs;\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/github-actions/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, PluginOptions, ResolveConfig } from '../../types/config.ts';\nimport { hasFileWithExtension } from '../../util/fs.ts';\nimport { type Input, isDeferResolveEntry, toEntry } from '../../util/input.ts';\nimport { findByKeyDeep } from '../../util/object.ts';\nimport { join, relative } from '../../util/path.ts';\nimport type { Job, Runs } from './types.ts';\n\n// https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions\n\nconst title = 'GitHub Actions';\n\nconst enablers = 'This plugin is enabled when a `.yml` or `.yaml` file is found in the `.github/workflows` folder.';\n\nconst isEnabled: IsPluginEnabled = ({ cwd }) => hasFileWithExtension(cwd, '.github/workflows', ['.yml', '.yaml']);\n\nconst isRootOnly = true;\n\nconst config = ['.github/workflows/*.{yml,yaml}', '.github/**/action.{yml,yaml}'];\n\nconst isString = (value: unknown): value is string => typeof value === 'string';\n\nexport const getActionDependencies = (config: any, options: PluginOptions) => {\n  const { configFileDir, configFileName } = options;\n  const isActionManifest = configFileName === 'action.yml' || configFileName === 'action.yaml';\n  if (!(isActionManifest && config?.runs?.using?.startsWith('node'))) return [];\n  const runs: Runs = config.runs;\n  const scripts = [runs.pre, runs.main, runs.post].filter(isString);\n  return scripts.map(script => join(configFileDir, script));\n};\n\nconst resolveConfig: ResolveConfig = async (config, options) => {\n  const { rootCwd, getInputsFromScripts, isProduction } = options;\n\n  const inputs = new Set<Input>();\n\n  const jobs = findByKeyDeep<Job>(config, 'steps');\n\n  for (const steps of jobs) {\n    if (!Array.isArray(steps.steps)) continue;\n    const action = steps.steps.find(\n      step => step.uses?.startsWith('actions/checkout@') && typeof step.with?.path === 'string' && !step.with.repository\n    );\n    const path = action?.with?.path;\n    for (const step of steps.steps) {\n      const workingDir = step['working-directory'];\n      const dir = join(rootCwd, path && workingDir ? relative(workingDir, path) : workingDir ? workingDir : '.');\n      if (step.run) {\n        for (const input of getInputsFromScripts([step.run], { knownBinsOnly: true })) {\n          if (isDeferResolveEntry(input) && path && !workingDir) {\n            input.specifier = relative(join(dir, path), join(rootCwd, input.specifier));\n          }\n          if (isProduction) Object.assign(input, { optional: true });\n          inputs.add({ ...input, dir });\n        }\n      }\n    }\n  }\n\n  return [...inputs, ...getActionDependencies(config, options).map(id => toEntry(id))];\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  isRootOnly,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/github-actions/types.ts",
    "content": "type Step = {\n  run?: string;\n  uses?: string;\n  with?: {\n    repository: string;\n    path: string;\n  };\n  'working-directory'?: string;\n};\n\ntype Steps = Step[];\n\nexport type Job = {\n  steps: Steps;\n};\n\nexport type Runs = {\n  using: string;\n  main?: string;\n  pre?: string;\n  post?: string;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/glob/index.ts",
    "content": "import type { Plugin } from '../../types/config.ts';\n\n// https://github.com/isaacs/node-glob\n\nconst title = 'glob';\n\nconst args = {\n  positional: true,\n  alias: { cmd: ['c'] },\n  fromArgs: ['cmd'],\n};\n\nconst plugin: Plugin = {\n  title,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/graphql-codegen/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDependency, toEntry } from '../../util/input.ts';\nimport { get } from '../../util/object.ts';\nimport { isInternal } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type {\n  ConfiguredPlugin,\n  GraphqlCodegenTypes,\n  GraphqlConfigTypes,\n  GraphqlProjectsConfigTypes,\n  PresetNames,\n} from './types.ts';\nimport { isConfigurationOutput, isGraphqlConfigTypes, isGraphqlProjectsConfigTypes } from './types.ts';\n\n// Both use Cosmiconfig with custom searchPlaces - not using helper as a result\n// Codegen:\n// https://the-guild.dev/graphql/codegen/docs/config-reference/codegen-config\n// https://github.com/dotansimha/graphql-code-generator/blob/master/packages/graphql-codegen-cli/src/config.ts\n// Config:\n// https://the-guild.dev/graphql/config/docs/user/usage#config-search-places\n\nconst title = 'GraphQL Codegen';\n\nconst enablers = [/^@graphql-codegen\\//, 'graphql-config'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst packageJsonPath: Plugin['packageJsonPath'] = manifest => get(manifest, 'codegen') ?? get(manifest, 'graphql');\n\nconst config = [\n  'package.json',\n  // graphql-codegen config files\n  'codegen.{json,yml,yaml,js,ts}',\n  '.codegenrc.{json,yml,yaml,js,ts}',\n  'codegen.config.js',\n  // graphql-config config files\n  '.graphqlrc',\n  '.graphqlrc.{json,yml,yaml,toml,js,ts}',\n  'graphql.config.{json,yml,yaml,toml,js,cjs,ts}',\n];\n\n// https://github.com/dotansimha/graphql-code-generator/blob/master/packages/graphql-codegen-cli/src/plugins.ts#L8-L18\nconst getPluginPackageName = (name: string) => {\n  if (name.startsWith('@') || name.includes('codegen-')) return name;\n  return `@graphql-codegen/${name}`;\n};\n\nconst resolveConfig: ResolveConfig<GraphqlCodegenTypes | GraphqlConfigTypes | GraphqlProjectsConfigTypes> = config => {\n  const codegenConfigs = isGraphqlProjectsConfigTypes(config)\n    ? Object.values(config.projects).flatMap(project => project.extensions?.codegen ?? [])\n    : isGraphqlConfigTypes(config)\n      ? [config.extensions?.codegen]\n      : [config];\n  const generateSet = codegenConfigs\n    .filter((config): config is GraphqlCodegenTypes => Boolean(config?.generates))\n    .flatMap(config => Object.values(config.generates));\n\n  const configurationOutput = generateSet.filter(isConfigurationOutput);\n\n  const presets = configurationOutput\n    .map(configOutput => (configOutput.preset ? configOutput.preset : undefined))\n    .filter((preset): preset is PresetNames => typeof preset === 'string')\n    .map(presetName =>\n      // https://github.com/dotansimha/graphql-code-generator/blob/master/packages/graphql-codegen-cli/src/presets.ts#L8-L11\n      presetName.startsWith('@graphql-codegen/')\n        ? presetName\n        : `@graphql-codegen/${presetName}${presetName.endsWith('-preset') ? '' : '-preset'}`\n    );\n\n  const flatPlugins = generateSet\n    .filter((config): config is ConfiguredPlugin => !isConfigurationOutput(config))\n    .flatMap(item => Object.keys(item))\n    .map(plugin => getPluginPackageName(plugin));\n\n  const nestedPlugins = configurationOutput\n    .flatMap(configOutput => (configOutput.plugins ? configOutput.plugins : []))\n    .flatMap(plugin => {\n      if (typeof plugin === 'object') return Object.keys(plugin);\n      return [plugin];\n    })\n    .flatMap(plugin => {\n      if (typeof plugin !== 'string') return [];\n      if (isInternal(plugin)) return [toEntry(plugin)];\n      return [toDependency(getPluginPackageName(plugin))];\n    });\n\n  return [...presets, ...flatPlugins, ...nestedPlugins].map(id => (typeof id === 'string' ? toDependency(id) : id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  packageJsonPath,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/graphql-codegen/types.ts",
    "content": "type PluginConfig<T = unknown> = { [key: string]: T };\nexport interface ConfiguredPlugin {\n  [name: string]: PluginConfig;\n}\ntype NamedPlugin = string;\n\ntype OutputConfig = NamedPlugin | ConfiguredPlugin;\ntype PresetNamesBase = 'client' | 'near-operation-file' | 'gql-tag-operations' | 'graphql-modules' | 'import-types';\nexport type PresetNames = `${PresetNamesBase}-preset` | PresetNamesBase;\n\ntype OutputPreset = {\n  buildGeneratesSection: (options: unknown) => Promise<unknown>;\n  prepareDocuments?: (outputFilePath: string, outputSpecificDocuments: unknown) => Promise<unknown>;\n};\n\nexport function isConfigurationOutput(config: ConfiguredOutput | ConfiguredPlugin[]): config is ConfiguredOutput {\n  return 'preset' in config || 'plugins' in config;\n}\n\ninterface ConfiguredOutput {\n  /**\n   * @type array\n   * @items { \"$ref\": \"#/definitions/GeneratedPluginsMap\" }\n   * @description List of plugins to apply to this current output file.\n   *\n   * You can either specify plugins from the community using the NPM package name (after you installed it in your project), or you can use a path to a local file for custom plugins.\n   *\n   * You can find a list of available plugins here: https://the-guild.dev/graphql/codegen/docs/plugins/index\n   * Need a custom plugin? read this: https://the-guild.dev/graphql/codegen/docs/custom-codegen/index\n   */\n  plugins?: OutputConfig[];\n  /**\n   * @description If your setup uses Preset to have a more dynamic setup and output, set the name of your preset here.\n   *\n   * Presets are a way to have more than one file output, for example: https://the-guild.dev/graphql/codegen/docs/presets/near-operation-file\n   *\n   * You can either specify a preset from the community using the NPM package name (after you installed it in your project), or you can use a path to a local file for a custom preset.\n   *\n   * List of available presets: https://graphql-code-generator.com/docs/presets/presets-index\n   */\n  preset?: PresetNames | OutputPreset;\n}\n// Extracted from https://github.com/dotansimha/graphql-code-generator/blob/master/packages/utils/plugins-helpers/src/types.ts\nexport interface GraphqlCodegenTypes {\n  /**\n   * @description A map where the key represents an output path for the generated code and the value represents a set of options which are relevant for that specific file.\n   *\n   * For more details: https://graphql-code-generator.com/docs/config-reference/codegen-config\n   */\n  generates: {\n    [outputPath: string]: ConfiguredOutput | ConfiguredPlugin[];\n  };\n}\n\nexport const isGraphqlConfigTypes = (\n  config: GraphqlCodegenTypes | GraphqlConfigTypes | GraphqlProjectsConfigTypes\n): config is GraphqlConfigTypes => {\n  return 'extensions' in config;\n};\n\n// Extracted from https://github.com/kamilkisiela/graphql-config/blob/master/src/types.ts\nexport interface GraphqlConfigTypes {\n  extensions?: {\n    codegen?: GraphqlCodegenTypes;\n  };\n}\n\nexport const isGraphqlProjectsConfigTypes = (\n  config: GraphqlCodegenTypes | GraphqlConfigTypes | GraphqlProjectsConfigTypes\n): config is GraphqlProjectsConfigTypes => {\n  return 'projects' in config;\n};\n\nexport interface GraphqlProjectsConfigTypes {\n  projects: {\n    [projectName: string]: GraphqlConfigTypes;\n  };\n}\n"
  },
  {
    "path": "packages/knip/src/plugins/hardhat/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, Resolve } from '../../types/config.ts';\nimport { toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://hardhat.org/docs\n\nconst title = 'Hardhat';\n\nconst enablers = ['hardhat'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst entry: string[] = ['hardhat.config.{js,cjs,mjs,ts}'];\n\nconst resolve: Resolve = async () => {\n  return [toDependency('hardhat')];\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  entry,\n  resolve,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/husky/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { getGitHookPaths } from '../../util/git.ts';\nimport { toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://typicode.github.io/husky\n\nconst title = 'husky';\n\nconst enablers = ['husky'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst isRootOnly = true;\n\n// husky v9 registers hooks in .husky/_/, so need to set \"false\" here to get same lookup as in v8\nconst gitHookPaths = getGitHookPaths('.husky', false);\n\n// Add patterns for both v8 and v9 because we can't know which version is installed at this point\nconst config = [...gitHookPaths, 'package.json'];\n\nconst resolveConfig: ResolveConfig = (script, options) => {\n  if (!script || options.isProduction) return [];\n\n  if (options.configFileName === 'package.json') {\n    const hooks = script.hooks;\n    if (hooks) {\n      const scripts: string[] = Object.values(hooks);\n      return [toDependency('husky'), ...options.getInputsFromScripts(scripts, { ...options })];\n    }\n  }\n\n  return options.getInputsFromScripts(String(script), { knownBinsOnly: true });\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  isRootOnly,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/i18next-parser/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://github.com/i18next/i18next-parser\n\nconst title = 'i18next Parser';\n\nconst enablers = ['i18next-parser'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['i18next-parser.config.{js,mjs,json,ts,yaml,yml}'];\n\nconst args = {\n  binaries: ['i18next'],\n  config: true,\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/index.ts",
    "content": "// This file is generated (no need to edit)\nimport { default as angular } from './angular/index.ts';\nimport { default as astro } from './astro/index.ts';\nimport { default as astroDb } from './astro-db/index.ts';\nimport { default as astroOgCanvas } from './astro-og-canvas/index.ts';\nimport { default as ava } from './ava/index.ts';\nimport { default as babel } from './babel/index.ts';\nimport { default as biome } from './biome/index.ts';\nimport { default as bumpp } from './bumpp/index.ts';\nimport { default as bun } from './bun/index.ts';\nimport { default as c8 } from './c8/index.ts';\nimport { default as capacitor } from './capacitor/index.ts';\nimport { default as changelogen } from './changelogen/index.ts';\nimport { default as changelogithub } from './changelogithub/index.ts';\nimport { default as changesets } from './changesets/index.ts';\nimport { default as commitizen } from './commitizen/index.ts';\nimport { default as commitlint } from './commitlint/index.ts';\nimport { default as convex } from './convex/index.ts';\nimport { default as createTypescriptApp } from './create-typescript-app/index.ts';\nimport { default as cspell } from './cspell/index.ts';\nimport { default as cucumber } from './cucumber/index.ts';\nimport { default as cypress } from './cypress/index.ts';\nimport { default as danger } from './danger/index.ts';\nimport { default as dependencyCruiser } from './dependency-cruiser/index.ts';\nimport { default as docusaurus } from './docusaurus/index.ts';\nimport { default as dotenv } from './dotenv/index.ts';\nimport { default as drizzle } from './drizzle/index.ts';\nimport { default as eleventy } from './eleventy/index.ts';\nimport { default as eslint } from './eslint/index.ts';\nimport { default as execa } from './execa/index.ts';\nimport { default as expo } from './expo/index.ts';\nimport { default as expressiveCode } from './expressive-code/index.ts';\nimport { default as gatsby } from './gatsby/index.ts';\nimport { default as githubAction } from './github-action/index.ts';\nimport { default as githubActions } from './github-actions/index.ts';\nimport { default as glob } from './glob/index.ts';\nimport { default as graphqlCodegen } from './graphql-codegen/index.ts';\nimport { default as hardhat } from './hardhat/index.ts';\nimport { default as husky } from './husky/index.ts';\nimport { default as i18nextParser } from './i18next-parser/index.ts';\nimport { default as jest } from './jest/index.ts';\nimport { default as karma } from './karma/index.ts';\nimport { default as knex } from './knex/index.ts';\nimport { default as ladle } from './ladle/index.ts';\nimport { default as lefthook } from './lefthook/index.ts';\nimport { default as lintStaged } from './lint-staged/index.ts';\nimport { default as linthtml } from './linthtml/index.ts';\nimport { default as lockfileLint } from './lockfile-lint/index.ts';\nimport { default as lostPixel } from './lost-pixel/index.ts';\nimport { default as markdownlint } from './markdownlint/index.ts';\nimport { default as mdx } from './mdx/index.ts';\nimport { default as mdxlint } from './mdxlint/index.ts';\nimport { default as metro } from './metro/index.ts';\nimport { default as mocha } from './mocha/index.ts';\nimport { default as moonrepo } from './moonrepo/index.ts';\nimport { default as msw } from './msw/index.ts';\nimport { default as nanoStaged } from './nano-staged/index.ts';\nimport { default as nest } from './nest/index.ts';\nimport { default as netlify } from './netlify/index.ts';\nimport { default as next } from './next/index.ts';\nimport { default as nextIntl } from './next-intl/index.ts';\nimport { default as nextMdx } from './next-mdx/index.ts';\nimport { default as nitro } from './nitro/index.ts';\nimport { default as node } from './node/index.ts';\nimport { default as nodeModulesInspector } from './node-modules-inspector/index.ts';\nimport { default as nodemon } from './nodemon/index.ts';\nimport { default as npmPackageJsonLint } from './npm-package-json-lint/index.ts';\nimport { default as nuxt } from './nuxt/index.ts';\nimport { default as nx } from './nx/index.ts';\nimport { default as nyc } from './nyc/index.ts';\nimport { default as oclif } from './oclif/index.ts';\nimport { default as openapiTs } from './openapi-ts/index.ts';\nimport { default as oxfmt } from './oxfmt/index.ts';\nimport { default as oxlint } from './oxlint/index.ts';\nimport { default as parcel } from './parcel/index.ts';\nimport { default as payload } from './payload/index.ts';\nimport { default as playwright } from './playwright/index.ts';\nimport { default as playwrightCt } from './playwright-ct/index.ts';\nimport { default as playwrightTest } from './playwright-test/index.ts';\nimport { default as plop } from './plop/index.ts';\nimport { default as pm2 } from './pm2/index.ts';\nimport { default as pnpm } from './pnpm/index.ts';\nimport { default as postcss } from './postcss/index.ts';\nimport { default as preconstruct } from './preconstruct/index.ts';\nimport { default as prettier } from './prettier/index.ts';\nimport { default as prisma } from './prisma/index.ts';\nimport { default as qwik } from './qwik/index.ts';\nimport { default as raycast } from './raycast/index.ts';\nimport { default as reactCosmos } from './react-cosmos/index.ts';\nimport { default as reactNative } from './react-native/index.ts';\nimport { default as reactRouter } from './react-router/index.ts';\nimport { default as relay } from './relay/index.ts';\nimport { default as releaseIt } from './release-it/index.ts';\nimport { default as remark } from './remark/index.ts';\nimport { default as remix } from './remix/index.ts';\nimport { default as rollup } from './rollup/index.ts';\nimport { default as rsbuild } from './rsbuild/index.ts';\nimport { default as rslib } from './rslib/index.ts';\nimport { default as rspack } from './rspack/index.ts';\nimport { default as rstest } from './rstest/index.ts';\nimport { default as sanity } from './sanity/index.ts';\nimport { default as semanticRelease } from './semantic-release/index.ts';\nimport { default as sentry } from './sentry/index.ts';\nimport { default as simpleGitHooks } from './simple-git-hooks/index.ts';\nimport { default as sizeLimit } from './size-limit/index.ts';\nimport { default as sst } from './sst/index.ts';\nimport { default as starlight } from './starlight/index.ts';\nimport { default as storybook } from './storybook/index.ts';\nimport { default as stryker } from './stryker/index.ts';\nimport { default as stylelint } from './stylelint/index.ts';\nimport { default as svelte } from './svelte/index.ts';\nimport { default as sveltekit } from './sveltekit/index.ts';\nimport { default as svgo } from './svgo/index.ts';\nimport { default as svgr } from './svgr/index.ts';\nimport { default as swc } from './swc/index.ts';\nimport { default as syncpack } from './syncpack/index.ts';\nimport { default as tailwind } from './tailwind/index.ts';\nimport { default as tanstackRouter } from './tanstack-router/index.ts';\nimport { default as taskfile } from './taskfile/index.ts';\nimport { default as travis } from './travis/index.ts';\nimport { default as tsNode } from './ts-node/index.ts';\nimport { default as tsdown } from './tsdown/index.ts';\nimport { default as tsup } from './tsup/index.ts';\nimport { default as tsx } from './tsx/index.ts';\nimport { default as typedoc } from './typedoc/index.ts';\nimport { default as typescript } from './typescript/index.ts';\nimport { default as unbuild } from './unbuild/index.ts';\nimport { default as unocss } from './unocss/index.ts';\nimport { default as vercelOg } from './vercel-og/index.ts';\nimport { default as vike } from './vike/index.ts';\nimport { default as vite } from './vite/index.ts';\nimport { default as vitepress } from './vitepress/index.ts';\nimport { default as vitest } from './vitest/index.ts';\nimport { default as vue } from './vue/index.ts';\nimport { default as webdriverIo } from './webdriver-io/index.ts';\nimport { default as webpack } from './webpack/index.ts';\nimport { default as wireit } from './wireit/index.ts';\nimport { default as wrangler } from './wrangler/index.ts';\nimport { default as xo } from './xo/index.ts';\nimport { default as yarn } from './yarn/index.ts';\nimport { default as yorkie } from './yorkie/index.ts';\nimport { default as zx } from './zx/index.ts';\n\nexport const Plugins = {\n  angular,\n  astro,\n  'astro-db': astroDb,\n  'astro-og-canvas': astroOgCanvas,\n  ava,\n  babel,\n  biome,\n  bumpp,\n  bun,\n  c8,\n  capacitor,\n  changelogen,\n  changelogithub,\n  changesets,\n  commitizen,\n  commitlint,\n  convex,\n  'create-typescript-app': createTypescriptApp,\n  cspell,\n  cucumber,\n  cypress,\n  danger,\n  'dependency-cruiser': dependencyCruiser,\n  docusaurus,\n  dotenv,\n  drizzle,\n  eleventy,\n  eslint,\n  execa,\n  expo,\n  'expressive-code': expressiveCode,\n  gatsby,\n  'github-action': githubAction,\n  'github-actions': githubActions,\n  glob,\n  'graphql-codegen': graphqlCodegen,\n  hardhat,\n  husky,\n  'i18next-parser': i18nextParser,\n  jest,\n  karma,\n  knex,\n  ladle,\n  lefthook,\n  'lint-staged': lintStaged,\n  linthtml,\n  'lockfile-lint': lockfileLint,\n  'lost-pixel': lostPixel,\n  markdownlint,\n  mdx,\n  mdxlint,\n  metro,\n  mocha,\n  moonrepo,\n  msw,\n  'nano-staged': nanoStaged,\n  nest,\n  netlify,\n  next,\n  'next-intl': nextIntl,\n  'next-mdx': nextMdx,\n  nitro,\n  node,\n  'node-modules-inspector': nodeModulesInspector,\n  nodemon,\n  'npm-package-json-lint': npmPackageJsonLint,\n  nuxt,\n  nx,\n  nyc,\n  oclif,\n  'openapi-ts': openapiTs,\n  oxfmt,\n  oxlint,\n  parcel,\n  payload,\n  playwright,\n  'playwright-ct': playwrightCt,\n  'playwright-test': playwrightTest,\n  plop,\n  pm2,\n  pnpm,\n  postcss,\n  preconstruct,\n  prettier,\n  prisma,\n  qwik,\n  raycast,\n  'react-cosmos': reactCosmos,\n  'react-native': reactNative,\n  'react-router': reactRouter,\n  relay,\n  'release-it': releaseIt,\n  remark,\n  remix,\n  rollup,\n  rsbuild,\n  rslib,\n  rspack,\n  rstest,\n  sanity,\n  'semantic-release': semanticRelease,\n  sentry,\n  'simple-git-hooks': simpleGitHooks,\n  'size-limit': sizeLimit,\n  sst,\n  starlight,\n  storybook,\n  stryker,\n  stylelint,\n  svelte,\n  sveltekit,\n  svgo,\n  svgr,\n  swc,\n  syncpack,\n  tailwind,\n  'tanstack-router': tanstackRouter,\n  taskfile,\n  travis,\n  'ts-node': tsNode,\n  tsdown,\n  tsup,\n  tsx,\n  typedoc,\n  typescript,\n  unbuild,\n  unocss,\n  'vercel-og': vercelOg,\n  vike,\n  vite,\n  vitepress,\n  vitest,\n  vue,\n  'webdriver-io': webdriverIo,\n  webpack,\n  wireit,\n  wrangler,\n  xo,\n  yarn,\n  yorkie,\n  zx,\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/jest/helpers.ts",
    "content": "import type { PluginOptions } from '../../types/config.ts';\nimport { dirname, isInternal, join, toAbsolute } from '../../util/path.ts';\nimport { load } from '../../util/plugin.ts';\nimport type { JestInitialOptions } from './types.ts';\n\nexport const resolveExtensibleConfig = async (configFilePath: string) => {\n  let config = await load(configFilePath);\n  if (config?.preset) {\n    const { preset } = config;\n    if (isInternal(preset)) {\n      const presetConfigPath = toAbsolute(preset, dirname(configFilePath));\n      const presetConfig = await resolveExtensibleConfig(presetConfigPath);\n      config = Object.assign({}, presetConfig, config);\n    }\n  }\n  return config;\n};\n\nconst getStringPropOrFallback = (prop: unknown, fallback: string): string => {\n  return typeof prop === 'string' ? prop : fallback;\n};\n\nexport const getReportersDependencies = (config: JestInitialOptions, options: PluginOptions) => {\n  // Resolve dependencies for jest-junit reporter config\n  const jUnitReporterDeps: string[] = [];\n  for (const reporter of config.reporters ?? []) {\n    if (typeof reporter !== 'string' && reporter[0] === 'jest-junit') {\n      const {\n        testCasePropertiesFile,\n        testCasePropertiesDirectory,\n        testSuitePropertiesFile,\n        testSuitePropertiesDirectory,\n      } = reporter[1];\n\n      if (testCasePropertiesFile) {\n        const fileName = getStringPropOrFallback(testCasePropertiesFile, 'junitProperties.js');\n        const dir = getStringPropOrFallback(testCasePropertiesDirectory, options.rootCwd);\n        jUnitReporterDeps.push(join(dir, fileName));\n      }\n\n      if (testSuitePropertiesFile) {\n        const fileName = getStringPropOrFallback(testSuitePropertiesFile, 'junitTestCaseProperties.js');\n        const dir = getStringPropOrFallback(testSuitePropertiesDirectory, options.rootCwd);\n        jUnitReporterDeps.push(join(dir, fileName));\n      }\n    }\n  }\n\n  const reporters = config.reporters\n    ? config.reporters\n        .map(reporter => (typeof reporter === 'string' ? reporter : reporter[0]))\n        .filter(reporter => !['default', 'github-actions', 'summary'].includes(reporter))\n    : [];\n\n  return [...reporters, ...jUnitReporterDeps];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/jest/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, PluginOptions, ResolveConfig } from '../../types/config.ts';\nimport { type Input, toDeferResolve, toEntry } from '../../util/input.ts';\nimport { isInternal, join, normalize, toAbsolute } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { getReportersDependencies, resolveExtensibleConfig } from './helpers.ts';\nimport type { JestConfig, JestInitialOptions } from './types.ts';\n\n// https://jestjs.io/docs/configuration\n\nconst title = 'Jest';\n\nconst enablers = ['jest'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies, manifest }) =>\n  hasDependency(dependencies, enablers) || Boolean(manifest.name?.startsWith('jest-presets'));\n\nconst config = ['jest.config.{js,ts,mjs,cjs,json}', 'package.json'];\n\nconst mocks = ['**/__mocks__/**/*.[jt]s?(x)'];\n\nconst entry = ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)', ...mocks];\n\nconst resolveDependencies = async (config: JestInitialOptions, options: PluginOptions): Promise<Input[]> => {\n  const { configFileDir } = options;\n\n  if (config?.preset) {\n    const { preset } = config;\n    if (isInternal(preset)) {\n      const presetConfigPath = toAbsolute(preset, configFileDir);\n      const presetConfig = await resolveExtensibleConfig(presetConfigPath);\n      config = Object.assign({}, presetConfig, config);\n    }\n  }\n\n  const presets = (config.preset ? [config.preset] : []).map(preset =>\n    isInternal(preset) ? preset : join(preset, 'jest-preset')\n  );\n\n  const projects = [];\n  for (const project of config.projects ?? []) {\n    if (typeof project === 'string') {\n      projects.push(project);\n    } else {\n      const dependencies = await resolveDependencies(project, options);\n      for (const dependency of dependencies) projects.push(dependency);\n    }\n  }\n\n  const runner = config.runner ? [config.runner] : [];\n  const runtime = config.runtime && config.runtime !== 'jest-circus' ? [config.runtime] : [];\n  const environments =\n    config.testEnvironment === 'jsdom'\n      ? ['jest-environment-jsdom']\n      : config.testEnvironment\n        ? [config.testEnvironment]\n        : [];\n  const resolvers = config.resolver ? [config.resolver] : [];\n  const reporters = getReportersDependencies(config, options);\n  const watchPlugins =\n    config.watchPlugins?.map(watchPlugin => (typeof watchPlugin === 'string' ? watchPlugin : watchPlugin[0])) ?? [];\n  const transform = config.transform\n    ? Object.values(config.transform).map(transform => (typeof transform === 'string' ? transform : transform[0]))\n    : [];\n  const moduleNameMapper = (\n    config.moduleNameMapper\n      ? Object.values(config.moduleNameMapper).map(mapper => (typeof mapper === 'string' ? mapper : mapper[0]))\n      : []\n  ).filter(value => !/\\$[0-9]/.test(value));\n\n  const testResultsProcessor = config.testResultsProcessor ? [config.testResultsProcessor] : [];\n  const snapshotResolver = config.snapshotResolver ? [config.snapshotResolver] : [];\n  const snapshotSerializers = config.snapshotSerializers ?? [];\n  const testSequencer = config.testSequencer ? [config.testSequencer] : [];\n\n  const setupFiles = config.setupFiles ?? [];\n  const setupFilesAfterEnv = config.setupFilesAfterEnv ?? [];\n  const globalSetup = config.globalSetup ? [config.globalSetup] : [];\n  const globalTeardown = config.globalTeardown ? [config.globalTeardown] : [];\n\n  return [\n    ...presets,\n    ...projects,\n    ...runner,\n    ...runtime,\n    ...environments,\n    ...resolvers,\n    ...reporters,\n    ...watchPlugins,\n    ...setupFiles,\n    ...setupFilesAfterEnv,\n    ...transform,\n    ...moduleNameMapper,\n    ...testResultsProcessor,\n    ...snapshotResolver,\n    ...snapshotSerializers,\n    ...testSequencer,\n    ...globalSetup,\n    ...globalTeardown,\n  ].map(id => (typeof id === 'string' ? toDeferResolve(id) : id));\n};\n\nconst resolveConfig: ResolveConfig<JestConfig> = async (localConfig, options) => {\n  const { configFileDir } = options;\n  if (typeof localConfig === 'function') localConfig = await localConfig();\n  const rootDir = localConfig.rootDir ?? configFileDir;\n  const replaceRootDir = (name: string) => name.replace(/<rootDir>/, rootDir);\n\n  const inputs = await resolveDependencies(localConfig, options);\n\n  const entries = localConfig.testMatch\n    ? localConfig.testMatch.map(replaceRootDir).map(id => toEntry(id))\n    : entry.map(id => toEntry(id));\n\n  if (localConfig.testMatch && !options.config.entry) entries.push(...mocks.map(id => toEntry(id)));\n\n  const result = inputs.map(dependency => {\n    dependency.specifier = normalize(replaceRootDir(dependency.specifier));\n    return dependency;\n  });\n\n  return entries.concat(result);\n};\n\nconst args = {\n  config: true,\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  entry,\n  resolveConfig,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/jest/types.ts",
    "content": "import type { Config } from '@jest/types';\n\nexport type JestInitialOptions = Config.InitialOptions;\n\nexport type JestConfig = JestInitialOptions | (() => JestInitialOptions) | (() => Promise<JestInitialOptions>);\n"
  },
  {
    "path": "packages/knip/src/plugins/karma/helpers.ts",
    "content": "import { type Input, toDeferResolveEntry, toDependency } from '../../util/input.ts';\nimport { isInternal } from '../../util/path.ts';\nimport type { Config, ConfigOptions } from './types.ts';\n\n//👇 All but CoffeeScript ones. Low usage nowadays compared to the effort to implement support for those files\nexport const configFiles = ['karma.conf.js', 'karma.conf.ts', '.config/karma.conf.js', '.config/karma.conf.ts'];\n\nexport const inputsFromFrameworks = (frameworks: readonly string[]): readonly Input[] =>\n  frameworks.map(framework => {\n    return toDependency(framework === 'jasmine' ? 'jasmine-core' : framework);\n  });\n\nexport const inputsFromPlugins = (\n  plugins: ConfigOptions['plugins'],\n  devDependencies: Record<string, string> | undefined\n): readonly Input[] => {\n  if (!plugins) {\n    const karmaPluginDevDeps = Object.keys(devDependencies ?? {}).filter(name => name.startsWith('karma-'));\n    return karmaPluginDevDeps.map(karmaPluginDevDep => toDependency(karmaPluginDevDep));\n  }\n  return plugins\n    .map(plugin => {\n      if (typeof plugin !== 'string') return;\n      return isInternal(plugin) ? toDeferResolveEntry(plugin) : toDependency(plugin);\n    })\n    .filter((input): input is Input => !!input);\n};\n\nexport type ConfigFile = (config: Config) => void;\nexport const loadConfig = (configFile: ConfigFile): ConfigOptions | undefined => {\n  if (typeof configFile !== 'function') return;\n  const inMemoryConfig = new InMemoryConfig();\n  configFile(inMemoryConfig);\n  return inMemoryConfig.config ?? {};\n};\n\n/**\n * Dummy configuration class with no default config options\n * Relevant config options' defaults are empty, so that's good enough\n * Real class: https://github.com/karma-runner/karma/blob/v6.4.4/lib/config.js#L275\n */\nclass InMemoryConfig implements Config {\n  config?: ConfigOptions;\n  /**\n   * Real method merges configurations with Lodash's `mergeWith`\n   * https://github.com/karma-runner/karma/blob/v6.4.4/lib/config.js#L343\n   */\n  set(config: ConfigOptions) {\n    this.config = config;\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/plugins/karma/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { type Input, toEntry } from '../../util/input.ts';\nimport { isAbsolute, join } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { type ConfigFile, configFiles, inputsFromFrameworks, inputsFromPlugins, loadConfig } from './helpers.ts';\n\n// https://karma-runner.github.io/latest/config/configuration-file.html\n\nconst title = 'Karma';\n\nconst enablers = ['karma'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = configFiles;\n\nconst resolveConfig: ResolveConfig<ConfigFile> = async (localConfig, options) => {\n  const inputs = new Set<Input>();\n\n  const config = loadConfig(localConfig);\n  if (!config) return [];\n\n  if (config.frameworks) {\n    inputsFromFrameworks(config.frameworks).forEach(inputs.add, inputs);\n  }\n\n  inputsFromPlugins(config.plugins, options.manifest.devDependencies).forEach(inputs.add, inputs);\n\n  const basePath = config.basePath ?? '';\n  if (config.files) {\n    for (const fileOrPatternObj of config.files) {\n      const fileOrPattern = typeof fileOrPatternObj === 'string' ? fileOrPatternObj : fileOrPatternObj.pattern;\n      const absPath = isAbsolute(fileOrPattern) ? fileOrPattern : join(options.configFileDir, basePath, fileOrPattern);\n      inputs.add(toEntry(absPath));\n    }\n  }\n  if (config.exclude) {\n    for (const fileOrPattern of config.exclude) {\n      const absPath = isAbsolute(fileOrPattern) ? fileOrPattern : join(options.configFileDir, basePath, fileOrPattern);\n      inputs.add(toEntry(`!${absPath}`));\n    }\n  }\n\n  return Array.from(inputs);\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/karma/types.ts",
    "content": "// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/6dfee7b81f699209d0b25b06018c633e0c67d38c/types/karma/index.d.ts#L202-L579\n// Just relevant parts are copy/pasted because:\n//  - Some types depend on other deps (`log4js`)\n//  - Needs manual fixing of some linter issues\n\n//👇 Partial copy/paste with relevant options for Knip\nexport interface Config {\n  set: (config: ConfigOptions) => void;\n}\n\n//👇 Partial copy/paste with relevant options for Knip\nexport interface ConfigOptions {\n  /**\n   * @default ''\n   * @description The root path location that will be used to resolve all relative paths defined in <code>files</code> and <code>exclude</code>.\n   * If the basePath configuration is a relative path then it will be resolved to\n   * the <code>__dirname</code> of the configuration file.\n   */\n  basePath?: string | undefined;\n  /**\n   * @default []\n   * @description List of files/patterns to exclude from loaded files.\n   */\n  exclude?: string[] | undefined;\n  /**\n   * @default []\n   * @description List of files/patterns to load in the browser.\n   */\n  files?: Array<FilePattern | string> | undefined;\n  /**\n   * @default []\n   * @description List of test frameworks you want to use. Typically, you will set this to ['jasmine'], ['mocha'] or ['qunit']...\n   * Please note just about all frameworks in Karma require an additional plugin/framework library to be installed (via NPM).\n   */\n  frameworks?: string[] | undefined;\n  /**\n   * @default ['karma-*']\n   * @description List of plugins to load. A plugin can be a string (in which case it will be required\n   * by Karma) or an inlined plugin - Object.\n   * By default, Karma loads all sibling NPM modules which have a name starting with karma-*.\n   * Note: Just about all plugins in Karma require an additional library to be installed (via NPM).\n   */\n  plugins?: Array<PluginName | InlinePluginDef> | undefined;\n}\n\ntype PluginName = string;\ntype InlinePluginDef = Record<PluginName, InlinePluginType>;\ntype InlinePluginType = FactoryFnType | ConstructorFnType | ValueType;\ntype FactoryFnType = ['factory', FactoryFn];\ntype FactoryFn = (...params: any[]) => any;\ntype ConstructorFnType = ['type', ConstructorFn];\n// oxlint-disable-next-line @typescript-eslint/no-empty-object-type\ntype ConstructorFn = Function | (new (...params: any[]) => any);\ntype ValueType = ['value', any];\n\n//👇 Partial extraction of relevant options for Knip\ninterface FilePattern {\n  /**\n   * The pattern to use for matching.\n   */\n  pattern: string;\n}\n"
  },
  {
    "path": "packages/knip/src/plugins/knex/helpers.ts",
    "content": "const CLIENT_MAPPING: Record<string, string[]> = {\n  pg: ['pg'],\n  postgres: ['pg'],\n  postgresql: ['pg'],\n  mysql: ['mysql', 'mysql2'],\n  mysql2: ['mysql2'],\n  sqlite3: ['sqlite3'],\n  'better-sqlite3': ['better-sqlite3'],\n  mssql: ['tedious'],\n  tedious: ['tedious'],\n  oracledb: ['oracledb'],\n  oracle: ['oracledb'],\n  cockroachdb: ['pg'],\n  redshift: ['pg'],\n};\n\nexport const clientToPackages = (client: string): string[] => {\n  const normalizedClient = client.toLowerCase();\n  return CLIENT_MAPPING[normalizedClient] ?? [];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/knex/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { type Input, toDependency, toEntry } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { clientToPackages } from './helpers.ts';\nimport type { KnexConfig } from './types.ts';\n\n// https://knexjs.org\n\nconst title = 'Knex';\n\nconst enablers = ['knex'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['knexfile.{js,cjs,mjs,ts,cts,mts}'];\n\nconst processKnexConfig = (config: KnexConfig): Input[] => {\n  const inputs: Input[] = [];\n  if (config.client) {\n    const packages = clientToPackages(config.client);\n    inputs.push(...packages.map(pkg => toDependency(pkg, { optional: true })));\n  }\n  if (config.migrations?.directory) {\n    const dirs = Array.isArray(config.migrations.directory)\n      ? config.migrations.directory\n      : [config.migrations.directory];\n    inputs.push(...dirs.map(dir => toEntry(`${dir}/*.{js,ts}`)));\n  }\n  if (config.seeds?.directory) {\n    const dirs = Array.isArray(config.seeds.directory) ? config.seeds.directory : [config.seeds.directory];\n    inputs.push(...dirs.map(dir => toEntry(`${dir}/*.{js,ts}`)));\n  }\n  return inputs;\n};\n\nconst resolveConfig: ResolveConfig<KnexConfig | Record<string, KnexConfig>> = config => {\n  const configs =\n    'client' in config && config.client ? [config as KnexConfig] : Object.values(config as Record<string, KnexConfig>);\n\n  return configs.flatMap(cfg => (typeof cfg === 'object' && cfg !== null ? processKnexConfig(cfg) : []));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/knex/types.ts",
    "content": "export interface KnexConfig {\n  client?: string;\n  connection?: unknown;\n  migrations?: {\n    directory?: string | string[];\n    tableName?: string;\n  };\n  seeds?: {\n    directory?: string | string[];\n  };\n}\n"
  },
  {
    "path": "packages/knip/src/plugins/ladle/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toEntry } from '../../util/input.ts';\nimport { toAbsolute } from '../../util/path.ts';\nimport { hasDependency, load } from '../../util/plugin.ts';\nimport { resolveConfig as resolveVitestConfig } from '../vitest/index.ts';\nimport type { LadleConfig } from './types.ts';\n\n// https://ladle.dev/docs/config\n\nconst title = 'Ladle';\n\nconst enablers = ['@ladle/react'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['.ladle/config.{mjs,js,ts}'];\n\nconst stories = ['src/**/*.stories.{js,jsx,ts,tsx,mdx}'];\nconst restEntry = ['.ladle/components.{js,jsx,ts,tsx}'];\nconst entry = [...restEntry, ...stories];\n\nconst project = ['.ladle/**/*.{js,jsx,ts,tsx}'];\n\nconst resolveConfig: ResolveConfig<LadleConfig> = async (localConfig, options) => {\n  const localStories = typeof localConfig.stories === 'string' ? [localConfig.stories] : localConfig.stories;\n  const viteConfig = localConfig.viteConfig ? [toAbsolute(localConfig.viteConfig, options.cwd)] : [];\n  const patterns = [...restEntry, ...(localStories ?? stories), ...viteConfig];\n  const entries = patterns.map(id => toEntry(id));\n\n  if (localConfig.viteConfig) {\n    const viteConfigPath = toAbsolute(localConfig.viteConfig, options.cwd);\n    const viteConfig = await load(viteConfigPath);\n    return entries.concat(await resolveVitestConfig(viteConfig, options));\n  }\n\n  return entries;\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  entry,\n  project,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/ladle/types.ts",
    "content": "export type LadleConfig = {\n  stories?: string | string[];\n  viteConfig?: string;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/lefthook/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { getGitHookPaths } from '../../util/git.ts';\nimport { fromBinary, toDependency } from '../../util/input.ts';\nimport { findByKeyDeep } from '../../util/object.ts';\nimport { extname } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://github.com/evilmartians/lefthook\n\nconst title = 'Lefthook';\n\nconst enablers = ['lefthook', '@arkweid/lefthook', '@evilmartians/lefthook'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\ntype Command = {\n  run: string;\n  root: string;\n};\n\nconst resolveConfig: ResolveConfig = async (localConfig, options) => {\n  if (options.isProduction) return [];\n\n  const { manifest, configFileName, cwd, getInputsFromScripts } = options;\n\n  const inputs = manifest.devDependencies ? Object.keys(manifest.devDependencies).map(id => toDependency(id)) : [];\n\n  if (extname(configFileName) === '.yml') {\n    const scripts = findByKeyDeep<Command>(localConfig, 'run').flatMap(command => {\n      const deps = getInputsFromScripts([command.run], { ...options, knownBinsOnly: true });\n      const dir = command.root ?? cwd;\n      return deps.flatMap(dependency => ({ ...dependency, dir }));\n    });\n\n    const lefthook = process.env.CI\n      ? enablers.filter(dependency => inputs.some(d => d.specifier === dependency)).map(id => toDependency(id))\n      : [];\n\n    return [...scripts, ...lefthook];\n  }\n\n  const script = localConfig;\n\n  if (!script) return [];\n\n  const scriptInputs = getInputsFromScripts(script);\n  const matches = scriptInputs.find(dep => inputs.some(d => d.specifier === fromBinary(dep)));\n  return matches ? [matches] : [];\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config: options => ['lefthook.yml', ...getGitHookPaths('.git/hooks', true, options.cwd)],\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/lint-staged/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport type { Input } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { toLilconfig } from '../../util/plugin-config.ts';\nimport type { Entry, LintStagedConfig } from './types.ts';\n\n// https://github.com/okonet/lint-staged\n\nconst title = 'lint-staged';\n\nconst enablers = ['lint-staged'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = [\n  'package.json',\n  'package.yaml',\n  'package.yml',\n  ...toLilconfig('lint-staged'),\n  ...toLilconfig('lintstaged'),\n];\n\nconst resolveEntry = async (value: Entry): Promise<string[]> => {\n  if (Array.isArray(value)) return (await Promise.all(value.map(resolveEntry))).flat();\n  if (typeof value === 'function') return [await value([])].flat().filter(item => typeof item === 'string');\n  return typeof value === 'string' ? [value] : [];\n};\n\nconst resolveConfig: ResolveConfig<LintStagedConfig> = async (config, options) => {\n  if (options.isProduction) return [];\n\n  const cfg = typeof config === 'function' ? await config([]) : config;\n\n  if (!cfg) return [];\n\n  const inputs = new Set<Input>();\n\n  for (const [key, entry] of Object.entries(cfg)) {\n    // Skip non-glob keys (comments, metadata, etc.)\n    if (key.startsWith('_')) continue;\n\n    const scripts = await resolveEntry(entry);\n    for (const id of options.getInputsFromScripts(scripts)) inputs.add(id);\n  }\n\n  return Array.from(inputs);\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/lint-staged/types.ts",
    "content": "// https://github.com/lint-staged/lint-staged/blob/main/lib/index.d.ts\n\ntype SyncGenerateTask = (stagedFileNames: readonly string[]) => string | string[];\n\ntype AsyncGenerateTask = (stagedFileNames: readonly string[]) => Promise<string | string[]>;\n\ntype GenerateTask = SyncGenerateTask | AsyncGenerateTask;\n\ntype TaskFunction = {\n  title: string;\n  task: (stagedFileNames: readonly string[]) => void | Promise<void>;\n};\n\nexport type Entry = string | TaskFunction | GenerateTask | (string | GenerateTask)[];\n\nexport type LintStagedConfig = Record<string, Entry> | GenerateTask;\n"
  },
  {
    "path": "packages/knip/src/plugins/linthtml/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDeferResolve } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { toCosmiconfig } from '../../util/plugin-config.ts';\nimport type { LintHTMLConfig } from './types.ts';\n\n// https://linthtml.vercel.app/\n\nconst title = 'LintHTML';\n\nconst packageJsonPath = 'linthtmlConfig';\n\nconst enablers = ['@linthtml/linthtml'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['package.json', ...toCosmiconfig('linthtml')];\n\nconst resolveConfig: ResolveConfig<LintHTMLConfig> = config => {\n  const extensions = config.extends ?? [];\n  const plugins = config.plugins ?? [];\n  return [extensions, plugins].flat().map(id => toDeferResolve(id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  packageJsonPath,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/linthtml/types.ts",
    "content": "export type LintHTMLConfig = {\n  extends?: string | string[];\n  plugins?: string[];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/lockfile-lint/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { toCosmiconfig } from '../../util/plugin-config.ts';\n\n// https://github.com/lirantal/lockfile-lint/blob/main/packages/lockfile-lint/README.md\n\nconst title = 'lockfile-lint';\n\nconst enablers = ['lockfile-lint'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['package.json', ...toCosmiconfig('lockfile-lint', { additionalExtensions: ['toml'] })];\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/lost-pixel/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://docs.lost-pixel.com/user-docs/api-reference/lost-pixel.config.js-or-ts\n\nconst title = 'Lost Pixel';\n\nconst enablers = ['lost-pixel'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['lostpixel.config.{js,ts}'];\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/markdownlint/helpers.ts",
    "content": "export const getArgumentValues = (value: string, matcher: RegExp) => {\n  const match = value.match(matcher);\n  if (match) return match.map(value => value.trim().split(/[ =]/)[1].trim());\n  return [];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/markdownlint/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { getArgumentValues } from './helpers.ts';\nimport type { MarkdownlintConfig } from './types.ts';\n\n// https://github.com/igorshubovych/markdownlint-cli\n\nconst title = 'markdownlint';\n\nconst enablers = ['markdownlint-cli'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['.markdownlint.{json,jsonc}', '.markdownlint.{yml,yaml}'];\n\nconst resolveConfig: ResolveConfig<MarkdownlintConfig> = (config, options) => {\n  const { manifest } = options;\n  const extend = config?.extends ? [config.extends] : [];\n  const scripts = manifest?.scripts\n    ? Object.values(manifest.scripts).filter((script): script is string => typeof script === 'string')\n    : [];\n  const uses = scripts\n    .filter(script => script.includes('markdownlint '))\n    .flatMap(script => getArgumentValues(script, / (--rules|-r)[ =]([^ ]+)/g));\n  return [...extend, ...uses].map(id => toDependency(id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/markdownlint/types.ts",
    "content": "export type MarkdownlintConfig = {\n  extends?: string;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/mdx/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { type Input, toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { MdxConfig } from './types.ts';\n\nconst title = 'MDX';\n\nconst enablers = ['astro', 'mdxlint'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['tsconfig.json'];\n\nconst takeDependencies = (config: MdxConfig) => {\n  const inputs: Input[] = [];\n  if (Array.isArray(config.plugins)) {\n    for (const plugin of config.plugins) {\n      if (typeof plugin === 'string') inputs.push(toDependency(plugin));\n      else if (typeof plugin[0] === 'string') inputs.push(toDependency(plugin[0]));\n    }\n  }\n  return inputs;\n};\n\nconst resolveConfig: ResolveConfig<MdxConfig | { mdx: MdxConfig }> = async (config, options) => {\n  const { configFileName } = options;\n\n  // read by @mdx-js/typescript-plugin (https://github.com/mdx-js/mdx-analyzer#plugins)\n  if (configFileName === 'tsconfig.json' && 'mdx' in config) return takeDependencies(config.mdx);\n\n  return [];\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/mdx/types.ts",
    "content": "export type MdxConfig = {\n  plugins?: (string | [string, unknown])[];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/mdxlint/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDeferResolve } from '../../util/input.ts';\nimport { isInternal } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { MdxlintConfig } from './types.ts';\n\n// https://github.com/remcohaszing/mdxlint\n\nconst title = 'mdxlint';\n\nconst enablers = ['mdxlint'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['.mdxlintrc', '.mdxlintrc.{json,js,cjs,mjs,yml,yaml}', 'package.json'];\n\nconst resolveConfig: ResolveConfig<MdxlintConfig> = config => {\n  const plugins =\n    config.plugins\n      ?.flatMap(plugin => {\n        if (typeof plugin === 'string') return plugin;\n        if (Array.isArray(plugin) && typeof plugin[0] === 'string') return plugin[0];\n        return [];\n      })\n      .map(plugin =>\n        isInternal(plugin)\n          ? plugin\n          : plugin.startsWith('remark-') || plugin.startsWith('mdxlint-')\n            ? plugin\n            : `remark-${plugin}`\n      ) ?? [];\n  return plugins.map(id => toDeferResolve(id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/mdxlint/types.ts",
    "content": "export type MdxlintConfig = {\n  plugins?: (string | [string, unknown] | unknown)[];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/metro/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { compact } from '../../util/array.ts';\nimport { type Input, toDeferResolve, toProductionEntry } from '../../util/input.ts';\nimport { join } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { MetroConfig } from './types.ts';\n\n// https://metrobundler.dev/docs/configuration\n\nconst title = 'Metro';\n\nconst enablers = ['metro', '@react-native/metro-config'];\n\nconst isEnabled: IsPluginEnabled = options => hasDependency(options.dependencies, enablers);\n\nconst config = ['metro.config.{js,cjs,json}', 'package.json'];\n\nconst DEFAULT_PLATFORMS = ['ios', 'android', 'windows', 'web'];\nconst PLATFORMS = [...DEFAULT_PLATFORMS, 'native', 'default'];\nconst DEFAULT_EXTENSIONS = ['js', 'jsx', 'json', 'ts', 'tsx'];\nconst DEFAULT_TRANSFORMER_PACKAGE = 'metro-transform-worker';\nconst DEFAULT_MINIFIER_PACKAGE = 'metro-minify-terser';\n\nconst production = [`src/**/*.{${PLATFORMS.join(',')}}.{${DEFAULT_EXTENSIONS.join(',')}}`];\n\nconst resolveConfig: ResolveConfig<MetroConfig> = async config => {\n  const { transformerPath, transformer } = config;\n  const i = new Set<Input>();\n  const inputs: string[] = [];\n\n  const platformEntryPatterns = compact(PLATFORMS.concat(config.resolver?.platforms ?? []));\n  const sourceExts = config.resolver?.sourceExts ?? DEFAULT_EXTENSIONS;\n  const pattern = `src/**/*.{${platformEntryPatterns.join(',')}}.{${sourceExts.join(',')}}`;\n\n  if (!config.projectRoot) {\n    i.add(toProductionEntry(pattern));\n  } else {\n    const entryFilePattern = 'index.{js,jsx,ts,tsx}';\n    const entryFilePath = join(config.projectRoot, entryFilePattern);\n    const entryFilePaths = join(config.projectRoot, pattern);\n    i.add(toProductionEntry(entryFilePath));\n    i.add(toProductionEntry(entryFilePaths));\n  }\n\n  if (transformerPath) inputs.push(transformerPath);\n  if (transformer?.assetPlugins) inputs.push(...transformer.assetPlugins);\n  if (transformer?.minifierPath) inputs.push(transformer.minifierPath);\n  if (transformer?.babelTransformerPath) inputs.push(transformer.babelTransformerPath);\n\n  return Array.from(i).concat(\n    [...inputs].map(id =>\n      toDeferResolve(id, {\n        optional: id === DEFAULT_TRANSFORMER_PACKAGE || id === DEFAULT_MINIFIER_PACKAGE,\n      })\n    )\n  );\n};\n\nconst isFilterTransitiveDependencies = true;\n\nconst note = `False positives for platform-specific unused files?\nOverride the default \\`entry\\` patterns to match platforms and extensions.`;\n\n/** @public */\nexport const docs = { note };\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  production,\n  resolveConfig,\n  isFilterTransitiveDependencies,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/metro/types.ts",
    "content": "// https://github.com/facebook/metro/blob/main/packages/metro-config/types/configTypes.d.ts\n\nexport type MetroConfig = {\n  projectRoot?: string;\n  transformerPath?: string;\n  transformer?: {\n    minifierPath?: string;\n    assetPlugins?: string[];\n    babelTransformerPath?: string;\n  };\n  resolver?: {\n    platforms?: string[];\n    sourceExts?: string[];\n  };\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/mocha/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { type Input, toDeferResolve, toEntry } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { MochaConfig } from './types.ts';\n\n// https://mochajs.org/#configuring-mocha-nodejs\n\nconst title = 'Mocha';\n\nconst enablers = ['mocha'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['.mocharc.{js,cjs,json,jsonc,yml,yaml}', 'package.json'];\n\nconst entry = ['**/test/*.{js,cjs,mjs}'];\n\nconst resolveConfig: ResolveConfig<MochaConfig> = localConfig => {\n  const entryPatterns = localConfig.spec ? [localConfig.spec].flat() : entry;\n  const require = localConfig.require ? [localConfig.require].flat() : [];\n\n  const inputs: Input[] = [];\n  inputs.push(...entryPatterns.map(id => toEntry(id)));\n  inputs.push(...require.map(id => toDeferResolve(id)));\n  return inputs;\n};\n\nconst args = {\n  nodeImportArgs: true,\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  entry,\n  resolveConfig,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/mocha/types.ts",
    "content": "export interface MochaConfig {\n  require?: string | string[];\n  spec?: string | string[];\n}\n"
  },
  {
    "path": "packages/knip/src/plugins/moonrepo/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { MoonConfiguration } from './types.ts';\n\n// https://moonrepo.dev/docs\n\nconst title = 'moonrepo';\n\nconst enablers = ['@moonrepo/cli'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst isRootOnly = true;\n\nconst config = ['moon.yml', '.moon/tasks.yml', '.moon/tasks/*.yml'];\n\nconst resolveConfig: ResolveConfig<MoonConfiguration> = async (config, options) => {\n  const tasks = config.tasks ? Object.values(config.tasks) : [];\n  const inputs = tasks\n    .map(task => task.command)\n    .filter(command => command)\n    .map(command => (Array.isArray(command) ? command.join(' ') : command))\n    .map(command => command.replace('$workspaceRoot', options.rootCwd))\n    .map(command => command.replace('$projectRoot', options.cwd))\n    .flatMap(command => options.getInputsFromScripts(command));\n  return [...inputs];\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  isRootOnly,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/moonrepo/types.ts",
    "content": "export interface MoonConfiguration {\n  tasks?: {\n    [taskName: string]: {\n      command: string | string[];\n    };\n  };\n}\n"
  },
  {
    "path": "packages/knip/src/plugins/msw/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toEntry } from '../../util/input.ts';\nimport { join } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { MSWConfig } from './types.ts';\n\n// https://mswjs.io/docs/integrations/browser\n\nconst title = 'Mock Service Worker';\n\nconst enablers = ['msw'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['package.json'];\n\nconst entry = ['mockServiceWorker.js'];\n\nconst resolveConfig: ResolveConfig<MSWConfig> = async localConfig => {\n  const workerDirectory = localConfig?.workerDirectory;\n  const dir = workerDirectory ? [workerDirectory].flat()[0] : '.';\n  return entry.map(pattern => toEntry(join(dir, pattern)));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  entry,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/msw/types.ts",
    "content": "export interface MSWConfig {\n  workerDirectory?: string;\n}\n"
  },
  {
    "path": "packages/knip/src/plugins/nano-staged/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport type { Input } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { NanoStagedConfig } from './types.ts';\n\n// https://github.com/usmanyunusov/nano-staged\n\nconst title = 'Nano Staged';\n\nconst enablers = ['nano-staged'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config: string[] = [\n  'package.json',\n  '.nano-staged.{js,cjs,mjs,json}',\n  'nano-staged.{js,cjs,mjs,json}',\n  '.nanostagedrc',\n];\n\nconst resolveConfig: ResolveConfig<NanoStagedConfig> = async (config, options) => {\n  if (options.isProduction) return [];\n\n  if (typeof config === 'function') config = config();\n\n  if (!config) return [];\n\n  const inputs = new Set<Input>();\n\n  for (const entry of Object.values(config).flat()) {\n    const api = { filenames: ['./example.js'] };\n    const scripts = [typeof entry === 'function' ? await entry(api) : entry].flat();\n    for (const id of options.getInputsFromScripts(scripts)) inputs.add(id);\n  }\n\n  return Array.from(inputs);\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/nano-staged/types.ts",
    "content": "type Script = string | string[];\n\ntype Entry = Script | ((api: { filenames: string[] }) => Script | Promise<Script>);\n\ntype Config = Record<string, Entry>;\n\nexport type NanoStagedConfig = Config | (() => Config);\n"
  },
  {
    "path": "packages/knip/src/plugins/nest/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { NestConfig } from './types.ts';\n\n// https://docs.nestjs.com\n\nconst title = 'Nest';\n\nconst enablers = [/^@nestjs\\/.*/];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['nest-cli.json', '.nestcli.json', '.nest-cli.json', 'nest.json'];\n\nconst resolveConfig: ResolveConfig<NestConfig> = async config => {\n  const inputs = config?.collection ? [config.collection] : [];\n  return [...inputs].map(id => toDependency(id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/nest/types.ts",
    "content": "export type NestConfig = {\n  collection?: string;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/netlify/helpers.ts",
    "content": "import type { FunctionsConfig } from './types.ts';\n\nexport const extractFunctionsConfigProperty = (config: FunctionsConfig, property: keyof FunctionsConfig) => [\n  ...(config[property] ?? []),\n  ...(Object.values(config).filter(x => typeof x === 'object' && property in x) as FunctionsConfig[]).flatMap(\n    x => x[property] || []\n  ),\n];\n"
  },
  {
    "path": "packages/knip/src/plugins/netlify/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDependency, toProductionEntry } from '../../util/input.ts';\nimport { join } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { extractFunctionsConfigProperty } from './helpers.ts';\nimport type { NetlifyConfig } from './types.ts';\n\n// https://docs.netlify.com\n// https://docs.netlify.com/functions/get-started/\n\nconst title = 'Netlify';\n\nconst enablers = [/^@netlify\\/plugin-/, 'netlify-cli', '@netlify/functions'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['netlify.toml'];\n\nconst NETLIFY_FUNCTIONS_DIR = 'netlify/functions';\nconst NETLIFY_FUNCTIONS_EXTS = 'js,mjs,cjs,ts,mts,cts';\n\nconst production = [`${NETLIFY_FUNCTIONS_DIR}/**/*.{${NETLIFY_FUNCTIONS_EXTS}}`];\n\nconst resolveConfig: ResolveConfig<NetlifyConfig> = async localConfig => {\n  return [\n    ...extractFunctionsConfigProperty(localConfig.functions || {}, 'included_files'),\n    join(localConfig.functions?.directory ?? NETLIFY_FUNCTIONS_DIR, `**/*.{${NETLIFY_FUNCTIONS_EXTS}}`),\n  ]\n    .filter(file => !file.startsWith('!'))\n    .map(id => toProductionEntry(id))\n    .concat([\n      ...(localConfig?.plugins?.map(plugin => plugin.package) ?? []).map(id => toDependency(id)),\n      ...extractFunctionsConfigProperty(localConfig.functions || {}, 'external_node_modules').map(id =>\n        toDependency(id)\n      ),\n    ]);\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  production,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/netlify/types.ts",
    "content": "export type NetlifyConfig = {\n  plugins?: {\n    package: string;\n  }[];\n  functions?: FunctionsConfig;\n};\n\nexport type FunctionsConfig = {\n  directory?: string;\n  external_node_modules?: string[];\n  included_files?: string[];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/next/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveFromAST } from '../../types/config.ts';\nimport { toProductionEntry } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { getPageExtensions } from './resolveFromAST.ts';\n\n// https://nextjs.org/docs/getting-started/project-structure\n\nconst title = 'Next.js';\n\nconst enablers = ['next'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['next.config.{js,ts,cjs,mjs}'];\n\nconst defaultPageExtensions = ['{js,jsx,ts,tsx}'];\n\nconst productionEntryFilePatterns = [\n  'app/{,[(]*[)]/}{manifest,robots}.{js,ts}',\n  'app/**/sitemap.{js,ts}',\n  'app/**/{icon,apple-icon,opengraph-image,twitter-image}.{js,jsx,ts,tsx}',\n];\n\nconst getEntryFilePatterns = (pageExtensions = defaultPageExtensions) => {\n  const ext = pageExtensions.length === 1 ? pageExtensions[0] : `{${pageExtensions.join(',')}}`;\n  return [\n    ...productionEntryFilePatterns,\n    `{instrumentation,instrumentation-client,middleware,proxy}.${ext}`,\n    `app/global-{error,not-found}.${ext}`,\n    `app/**/{default,error,forbidden,loading,not-found,unauthorized}.${ext}`,\n    `app/**/{layout,page,route,template}.${ext}`,\n    `pages/**/*.${ext}`,\n  ].flatMap(pattern => [pattern, `src/${pattern}`]);\n};\n\nconst production = getEntryFilePatterns();\n\nconst resolveFromAST: ResolveFromAST = program => {\n  const pageExtensions = getPageExtensions(program);\n  const extensions = pageExtensions.length > 0 ? pageExtensions : defaultPageExtensions;\n  const patterns = getEntryFilePatterns(extensions);\n  return patterns.map(id => toProductionEntry(id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  production,\n  resolveFromAST,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/next/resolveFromAST.ts",
    "content": "import type { Program } from 'oxc-parser';\nimport { collectPropertyValues } from '../../typescript/ast-helpers.ts';\n\nexport const getPageExtensions = (program: Program) => Array.from(collectPropertyValues(program, 'pageExtensions'));\n"
  },
  {
    "path": "packages/knip/src/plugins/next-intl/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://next-intl.dev/docs/getting-started/app-router\n\nconst title = 'next-intl';\n\nconst enablers = ['next-intl'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst production = ['{src/,}i18n/request.{js,jsx,ts,tsx}'];\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  production,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/next-mdx/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveFromAST } from '../../types/config.ts';\nimport { toDependency, toProductionEntry } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { getMdxPlugins } from './resolveFromAST.ts';\n\n// https://nextjs.org/docs/pages/building-your-application/configuring/mdx\n\nconst title = 'Next.js MDX';\n\nconst enablers = ['@next/mdx'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['next.config.{js,ts,cjs,mjs}'];\n\nconst production = ['{src/,}mdx-components.{js,jsx,ts,tsx}'];\n\nconst resolveFromAST: ResolveFromAST = program => {\n  const mdxPlugins = getMdxPlugins(program);\n  return [...production.map(id => toProductionEntry(id)), ...Array.from(mdxPlugins).map(id => toDependency(id))];\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  production,\n  resolveFromAST,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/next-mdx/resolveFromAST.ts",
    "content": "import type { Program } from 'oxc-parser';\nimport { Visitor } from 'oxc-parser';\nimport { findProperty, getDefaultImportName, getImportMap, getStringValues } from '../../typescript/ast-helpers.ts';\n\nexport const getMdxPlugins = (program: Program) => {\n  const plugins = new Set<string>();\n\n  const importMap = getImportMap(program);\n  const mdxImportName = getDefaultImportName(importMap, '@next/mdx');\n  if (!mdxImportName) return plugins;\n\n  const visitor = new Visitor({\n    CallExpression(node) {\n      if (node.callee?.type !== 'Identifier' || node.callee.name !== mdxImportName) return;\n      const options = findProperty(node.arguments?.[0], 'options');\n      if (options?.type !== 'ObjectExpression') return;\n      for (const pluginType of ['remarkPlugins', 'rehypePlugins', 'recmaPlugins']) {\n        for (const v of getStringValues(findProperty(options, pluginType))) plugins.add(v);\n      }\n    },\n  });\n  visitor.visit(program);\n\n  return plugins;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/nitro/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport type { Input } from '../../util/input.ts';\nimport { toDependency, toProductionEntry } from '../../util/input.ts';\nimport { join } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { NitroConfig } from './types.ts';\n\nconst title = 'Nitro';\n\nconst enablers = ['nitropack', 'nitro'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['nitro.config.{js,mjs,ts}'];\n\nconst production = [\n  'server.{js,mjs,ts}',\n  'api/**/*.ts',\n  'routes/**/*.ts',\n  'middleware/**/*.ts',\n  'plugins/**/*.ts',\n  '.nitro/types/*.d.ts',\n];\n\nconst setup = async () => {\n  if (globalThis && !('defineNitroConfig' in globalThis)) {\n    Object.defineProperty(globalThis, 'defineNitroConfig', {\n      value: (id: any) => id,\n      writable: true,\n      configurable: true,\n    });\n  }\n};\n\nconst resolveConfig: ResolveConfig<NitroConfig> = async localConfig => {\n  const srcDir = localConfig.srcDir ?? '.';\n\n  const patterns = [\n    toProductionEntry('.nitro/types/*.d.ts'),\n    ...[\n      typeof localConfig.serverEntry === 'string' ? localConfig.serverEntry : 'server.{js,mjs,ts}',\n      join(typeof localConfig.apiDir === 'string' ? localConfig.apiDir : 'api', '**/*.ts'),\n      join(typeof localConfig.routesDir === 'string' ? localConfig.routesDir : 'routes', '**/*.ts'),\n      'middleware/**/*.ts',\n      'plugins/**/*.ts',\n    ].map(pattern => toProductionEntry(join(srcDir, pattern))),\n  ];\n\n  const deps =\n    localConfig.modules?.reduce<Input[]>((acc, id) => {\n      if (Array.isArray(id) && typeof id[0] === 'string') acc.push(toDependency(id[0]));\n      if (typeof id === 'string') acc.push(toDependency(id));\n      return acc;\n    }, []) ?? [];\n\n  return [...deps, ...patterns];\n};\n\nconst note = `Knip works best with [manual imports](https://nitro.build/guide/utils#manual-imports).\nNitro allows you to disable auto-imports by setting \\`imports: false\\` in your Nitro config.`;\n\n/** @public */\nexport const docs = { note };\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  production,\n  setup,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/nitro/types.ts",
    "content": "export interface NitroConfig {\n  srcDir?: string;\n  apiDir?: string;\n  routesDir?: string;\n  serverEntry?: string;\n  modules?: Array<string | ((inlineOptions: any, nitro: any) => any) | [string, Record<string, any>]>;\n}\n"
  },
  {
    "path": "packages/knip/src/plugins/node/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, Resolve } from '../../types/config.ts';\nimport { toEntry, toProductionEntry } from '../../util/input.ts';\n\nconst title = 'Node.js';\n\nconst isEnabled: IsPluginEnabled = () => true;\n\n// From https://nodejs.org/api/test.html#running-tests-from-the-command-line\nconst patterns = [\n  '**/*{.,-,_}test.{cjs,mjs,js,cts,mts,ts}',\n  '**/test-*.{cjs,mjs,js,cts,mts,ts}',\n  '**/test.{cjs,mjs,js,cts,mts,ts}',\n  '**/test/**/*.{cjs,mjs,js,cts,mts,ts}',\n];\n\nconst hasNodeTest = (scripts: Record<string, string> | undefined) =>\n  scripts && Object.values(scripts).some(script => /(?<=^|\\s)node\\s(.*)--test/.test(script));\n\nconst entry = ['server.js'];\n\nconst resolve: Resolve = options => {\n  const entries = entry.map(id => toProductionEntry(id));\n\n  if (hasNodeTest(options.manifest.scripts) || hasNodeTest(options.rootManifest?.scripts)) {\n    entries.push(...patterns.map(toEntry));\n  }\n\n  return entries;\n};\n\nconst args = {\n  positional: true,\n  nodeImportArgs: true,\n  resolve: ['test-reporter'],\n  boolean: [\n    'deprecation',\n    'experimental-strip-types',\n    'experimental-transform-types',\n    'harmony',\n    'inspect-brk',\n    'inspect-wait',\n    'inspect',\n    'test-only',\n    'test',\n    'warnings',\n    'watch',\n  ],\n  args: (args: string[]) => args.filter(arg => !/--test-reporter[= ](spec|tap|dot|junit|lcov)/.test(arg)),\n};\n\nconst plugin: Plugin = {\n  title,\n  isEnabled,\n  entry,\n  resolve,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/node-modules-inspector/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { toUnconfig } from '../../util/plugin-config.ts';\n\n// https://github.com/antfu/node-modules-inspector\n\nconst title = 'node-modules-inspector';\n\nconst enablers = ['node-modules-inspector'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config: string[] = [...toUnconfig('node-modules-inspector.config')];\n\nconst args = {\n  config: true,\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/nodemon/index.ts",
    "content": "import type { Plugin } from '../../types/config.ts';\n\nconst title = 'nodemon';\n\nconst args = {\n  positional: false,\n  nodeImportArgs: true,\n  string: ['exec'],\n  fromArgs: ['exec'],\n};\n\nconst plugin: Plugin = {\n  title,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/npm-package-json-lint/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { toCosmiconfig } from '../../util/plugin-config.ts';\nimport type { NpmPkgJsonLintConfig } from './types.ts';\n\n// https://npmpackagejsonlint.org/docs/\n\nconst title = 'npm-package-json-lint';\n\nconst enablers = ['npm-package-json-lint'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst packageJsonPath = 'npmpackagejsonlint';\n\nconst config = ['package.json', ...toCosmiconfig('npmpackagejsonlint')];\n\nconst resolveConfig: ResolveConfig<NpmPkgJsonLintConfig> = localConfig => {\n  return localConfig?.extends ? [localConfig.extends].map(id => toDependency(id)) : [];\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  packageJsonPath,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/npm-package-json-lint/types.ts",
    "content": "export type NpmPkgJsonLintConfig = {\n  extends?: string;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/nuxt/helpers.ts",
    "content": "import { readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { Visitor, type ParseResult } from 'oxc-parser';\nimport { scriptBodies } from '../../compilers/compilers.ts';\nimport { parseFile } from '../../typescript/visitors/helpers.ts';\nimport { basename, dirname, isInNodeModules, join } from '../../util/path.ts';\nimport type { TemplateAstNode, VueSfc } from './types.ts';\n\nexport const getVueSfc = (cwd: string): VueSfc => {\n  try {\n    return createRequire(join(cwd, 'package.json'))('vue/compiler-sfc');\n  } catch {}\n  return {\n    parse: (source: string, path: string) => ({\n      descriptor: { script: { content: scriptBodies(source, path) }, scriptSetup: null, template: { content: '' } },\n    }),\n  };\n};\n\nconst readFile = (filePath: string): string => {\n  try {\n    return readFileSync(filePath, 'utf8');\n  } catch {\n    return '';\n  }\n};\n\nexport const readAndParseFile = (filePath: string) => parseFile(filePath, readFile(filePath));\n\nexport const collectIdentifiers = (source: string, fileName: string) => {\n  const identifiers = new Set<string>();\n  const visitor = new Visitor({\n    Identifier(node) {\n      identifiers.add(node.name);\n    },\n  });\n  visitor.visit(parseFile(fileName, source).program);\n  return identifiers;\n};\n\nexport const collectTemplateInfo = (tree: TemplateAstNode) => {\n  const tags = new Set<string>();\n  const identifiers = new Set<string>();\n  const addExprIdentifiers = (expr: string) => {\n    for (const id of collectIdentifiers(expr, 'expr.ts')) identifiers.add(id);\n  };\n  const visit = (node: TemplateAstNode) => {\n    if (node.tag) tags.add(node.tag);\n    if (node.type === 5 && node.content && !node.content.isStatic) addExprIdentifiers(node.content.content);\n    if (node.props) {\n      for (const prop of node.props) {\n        if (prop.type === 7) {\n          if (prop.exp && !prop.exp.isStatic) addExprIdentifiers(prop.exp.content);\n          if (prop.arg && !prop.arg.isStatic) addExprIdentifiers(prop.arg.content);\n        }\n      }\n    }\n    if (node.children) for (const child of node.children) visit(child);\n  };\n  visit(tree);\n  return { tags, identifiers };\n};\n\nexport const toKebabCase = (s: string) => s.replace(/[A-Z]/g, (m, i) => (i ? '-' : '') + m.toLowerCase());\n\nconst isLocalSpecifier = (specifier: string) => specifier.startsWith('.') && !isInNodeModules(specifier);\n\nexport const collectLocalImportPaths = (filePath: string, result: ParseResult) => {\n  const dir = dirname(filePath);\n  const paths = new Set<string>();\n  const visitor = new Visitor({\n    TSImportType(node) {\n      const specifier = node.source.value;\n      if (isLocalSpecifier(specifier)) paths.add(join(dir, specifier));\n    },\n  });\n  visitor.visit(result.program);\n  return paths;\n};\n\nexport function buildAutoImportMap(filePath: string, result: ParseResult) {\n  const dir = dirname(filePath);\n  const isComponents = basename(filePath) === 'components.d.ts';\n\n  const importMap = new Map<string, string>();\n  const componentMap = new Map<string, string[]>();\n\n  const importTypes: { start: number; end: number; specifier: string }[] = [];\n  const collectVisitor = new Visitor({\n    TSImportType(node) {\n      importTypes.push({ start: node.start, end: node.end, specifier: node.source.value });\n    },\n  });\n  collectVisitor.visit(result.program);\n\n  const matchVisitor = new Visitor({\n    VariableDeclarator(node) {\n      if (node.id?.type !== 'Identifier') return;\n      const name = node.id.name;\n      if (name.startsWith('Lazy')) return;\n      const importType = importTypes.find(it => it.start >= node.start && it.end <= node.end);\n      if (!importType) return;\n      if (!isLocalSpecifier(importType.specifier)) return;\n      const absSpecifier = join(dir, importType.specifier);\n      if (isComponents) {\n        const components = componentMap.get(name);\n        if (components) components.push(absSpecifier);\n        else componentMap.set(name, [absSpecifier]);\n      } else {\n        importMap.set(name, absSpecifier);\n      }\n    },\n    ExportNamedDeclaration(node) {\n      if (!node.source) return;\n      const specifier = node.source.value;\n      if (!isLocalSpecifier(specifier)) return;\n      const absSpecifier = join(dir, specifier);\n      for (const s of node.specifiers) {\n        const name = s.exported.type === 'Identifier' ? s.exported.name : s.exported.value;\n        if (name) importMap.set(name, absSpecifier);\n      }\n    },\n  });\n  matchVisitor.visit(result.program);\n\n  return { importMap, componentMap };\n}\n"
  },
  {
    "path": "packages/knip/src/plugins/nuxt/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, RegisterCompilers, ResolveConfig } from '../../types/config.ts';\nimport { isDirectory } from '../../util/fs.ts';\nimport { _syncGlob } from '../../util/glob.ts';\nimport type { Input } from '../../util/input.ts';\nimport {\n  toAlias,\n  toConfig,\n  toDeferResolveProductionEntry,\n  toDependency,\n  toEntry,\n  toIgnore,\n  toProductionEntry,\n} from '../../util/input.ts';\nimport { loadTSConfig } from '../../util/load-tsconfig.ts';\nimport { join } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport {\n  buildAutoImportMap,\n  collectIdentifiers,\n  collectLocalImportPaths,\n  collectTemplateInfo,\n  getVueSfc,\n  readAndParseFile,\n  toKebabCase,\n} from './helpers.ts';\nimport type { NuxtConfig } from './types.ts';\n\nconst title = 'Nuxt';\n\nconst enablers = ['nuxt', 'nuxt-nightly'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['nuxt.config.{js,mjs,ts}'];\n\nconst entry = ['app.config.ts', '**/*.d.vue.ts'];\n\nconst app = ['app.{vue,jsx,tsx}', 'error.{vue,jsx,tsx}', 'router.options.ts'];\nconst layout = (dir = 'layouts') => join(dir, '**/*.{vue,jsx,tsx}');\nconst middleware = (dir = 'middleware') => join(dir, '**/*.ts');\nconst pages = (dir = 'pages') => join(dir, '**/*.{vue,jsx,tsx}');\nconst plugins = (dir = 'plugins') => join(dir, '**/*.ts');\nconst modules = 'modules/**/*.{ts,vue}';\nconst server = ['api/**/*.ts', 'middleware/**/*.ts', 'plugins/**/*.ts', 'routes/**/*.ts', 'tasks/**/*.ts'];\n\nconst production: string[] = [\n  ...app,\n  layout(),\n  middleware(),\n  pages(),\n  plugins(),\n  modules,\n  ...server.map(id => join('server', id)),\n];\n\nconst setup = async () => {\n  if (globalThis && !('defineNuxtConfig' in globalThis)) {\n    Object.defineProperty(globalThis, 'defineNuxtConfig', {\n      value: (id: any) => id,\n      writable: true,\n      configurable: true,\n    });\n  }\n};\n\n// Workaround to pre-resolve specifiers from root, as no tsconfig.json/project references covers\nconst resolveAlias = (specifier: string, srcDir: string, rootDir: string) => {\n  if (specifier.startsWith('~~/') || specifier.startsWith('@@/')) return join(rootDir, specifier.slice(3));\n  if (specifier.startsWith('~/') || specifier.startsWith('@/')) return join(srcDir, specifier.slice(2));\n  return specifier;\n};\n\nconst addAppEntries = (inputs: Input[], srcDir: string, serverDir: string, config: NuxtConfig, dir: string) => {\n  for (const id of entry) inputs.push(toEntry(join(srcDir, id)));\n  for (const id of app) inputs.push(toProductionEntry(join(srcDir, id)));\n  inputs.push(toProductionEntry(join(srcDir, layout(config.dir?.layouts))));\n  inputs.push(toProductionEntry(join(srcDir, middleware(config.dir?.middleware))));\n  inputs.push(toProductionEntry(join(srcDir, pages(config.dir?.pages))));\n  inputs.push(toProductionEntry(join(srcDir, plugins(config.dir?.plugins))));\n  inputs.push(toProductionEntry(join(srcDir, 'components/global/**/*.{vue,jsx,tsx}')));\n  for (const id of server) inputs.push(toProductionEntry(join(dir, serverDir, id)));\n  inputs.push(toProductionEntry(join(dir, modules)));\n  if (config.css)\n    for (const id of config.css) inputs.push(toDeferResolveProductionEntry(resolveAlias(id, srcDir, dir)));\n};\n\nconst findLayerConfigs = (cwd: string): string[] => _syncGlob({ cwd, patterns: [`layers/*/${config.at(0)}`] });\n\nconst registerCompilers: RegisterCompilers = async ({ cwd, hasDependency, registerCompiler }) => {\n  if (hasDependency('nuxt') || hasDependency('nuxt-nightly')) {\n    const vueSfc = getVueSfc(cwd);\n\n    const importMap = new Map<string, string>();\n    const componentMap = new Map<string, string[]>();\n\n    const definitionFiles = [\n      '.nuxt/imports.d.ts',\n      '.nuxt/components.d.ts',\n      '.nuxt/types/nitro-routes.d.ts',\n      '.nuxt/types/nitro-imports.d.ts',\n    ];\n\n    for (const file of definitionFiles) {\n      const path = join(cwd, file);\n      const result = readAndParseFile(path);\n      const maps = buildAutoImportMap(path, result);\n      for (const [id, specifier] of maps.importMap) importMap.set(id, specifier);\n      for (const [id, components] of maps.componentMap) {\n        const store = componentMap.get(id);\n        if (store) store.push(...components);\n        else componentMap.set(id, [...components]);\n      }\n    }\n\n    const getSyntheticImports = (identifiers: Set<string>, templateTags?: Set<string>) => {\n      const syntheticImports: string[] = [];\n\n      for (const [name, specifier] of importMap) {\n        if (identifiers.has(name)) syntheticImports.push(`import { ${name} } from '${specifier}';`);\n      }\n\n      if (templateTags) {\n        for (const [name, specifiers] of componentMap) {\n          const kebab = toKebabCase(name);\n          if (\n            templateTags.has(name) ||\n            templateTags.has(kebab) ||\n            templateTags.has(`Lazy${name}`) ||\n            templateTags.has(`lazy-${kebab}`)\n          ) {\n            syntheticImports.push(`import { default as ${name} } from '${specifiers[0]}';`);\n            for (let i = 1; i < specifiers.length; i++) syntheticImports.push(`import '${specifiers[i]}';`);\n          }\n        }\n      }\n\n      return syntheticImports;\n    };\n\n    const compiler = (source: string, path: string) => {\n      const { descriptor } = vueSfc.parse(source, path);\n      const scripts: string[] = [];\n\n      if (descriptor.script?.content) scripts.push(descriptor.script.content);\n      if (descriptor.scriptSetup?.content) scripts.push(descriptor.scriptSetup.content);\n\n      const identifiers = collectIdentifiers(scripts.join('\\n'), path);\n      let templateTags: Set<string> | undefined;\n      if (descriptor.template?.ast) {\n        const info = collectTemplateInfo(descriptor.template.ast);\n        templateTags = info.tags;\n        for (const id of info.identifiers) identifiers.add(id);\n      }\n      const synthetic = getSyntheticImports(identifiers, templateTags);\n      scripts.push(...synthetic);\n\n      return scripts.join(';\\n');\n    };\n\n    const tsCompiler = (source: string, path: string) => {\n      // TODO Can we filter out more files that are outside the realm of auto-imports?\n      if (path.endsWith('.d.ts') || path.endsWith('.config.ts')) return source;\n      const identifiers = collectIdentifiers(source, path);\n      const syntheticImports = getSyntheticImports(identifiers);\n      if (syntheticImports.length === 0) return source;\n      return `${source}\\n${syntheticImports.join('\\n')}`;\n    };\n\n    registerCompiler({ extension: '.vue', compiler });\n    registerCompiler({ extension: '.ts', compiler: tsCompiler });\n  }\n};\n\nconst resolveConfig: ResolveConfig<NuxtConfig> = async (localConfig, options) => {\n  const { configFileDir: cwd } = options;\n  const hasAppDir = isDirectory(cwd, 'app');\n  const srcDir = localConfig.srcDir ?? (hasAppDir ? join(cwd, 'app') : cwd);\n  const serverDir = localConfig.serverDir ?? 'server';\n  const inputs: Input[] = [];\n\n  for (const id of localConfig.modules ?? []) {\n    if (Array.isArray(id) && typeof id[0] === 'string') inputs.push(toDependency(id[0]));\n    if (typeof id === 'string') inputs.push(toDependency(id));\n  }\n\n  addAppEntries(inputs, srcDir, serverDir, localConfig, cwd);\n\n  const aliases = localConfig.alias;\n  if (aliases) {\n    for (const key in aliases) {\n      const prefix = resolveAlias(aliases[key], srcDir, cwd);\n      inputs.push(toAlias(key, prefix));\n      if (prefix.endsWith('/') || isDirectory(prefix)) {\n        inputs.push(toAlias(join(key, '*'), join(prefix, '*'), { dir: cwd }));\n      }\n    }\n  }\n\n  for (const ext of localConfig.extends ?? []) {\n    const resolved = resolveAlias(ext, srcDir, cwd);\n    const configs = _syncGlob({ cwd: resolved, patterns: config });\n    if (configs.length > 0) for (const cfg of configs) inputs.push(toConfig('nuxt', join(resolved, cfg)));\n    else inputs.push(toDependency(ext));\n  }\n\n  for (const layerConfig of findLayerConfigs(cwd)) {\n    inputs.push(toConfig('nuxt', layerConfig));\n  }\n\n  if (cwd !== options.cwd) return inputs;\n\n  for (const file of _syncGlob({ cwd, patterns: ['.nuxt/module/*.d.ts'] })) {\n    const fp = join(cwd, file);\n    const result = readAndParseFile(fp);\n    for (const p of collectLocalImportPaths(fp, result)) inputs.push(toProductionEntry(p));\n  }\n\n  // In case typescript isn't listed\n  const dir = join(cwd, '.nuxt');\n  const tsConfig = await loadTSConfig(join(dir, 'tsconfig.json'));\n  const paths = tsConfig.compilerOptions?.paths;\n  if (paths) {\n    for (const key in paths) {\n      if (key === '#imports' || key === '#components') continue;\n      inputs.push(toAlias(key, paths[key], { dir }));\n    }\n  }\n\n  inputs.push(toIgnore('#imports', 'unresolved'));\n  inputs.push(toIgnore('#components', 'unresolved'));\n\n  return inputs;\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  entry,\n  production,\n  setup,\n  resolveConfig,\n  registerCompilers,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/nuxt/types.ts",
    "content": "export interface NuxtConfig {\n  srcDir?: string;\n  buildDir?: string;\n  serverDir?: string;\n  dir?: {\n    pages?: string;\n    layouts?: string;\n    middleware?: string;\n    plugins?: string;\n    shared?: string;\n  };\n  modules?: Array<string | ((inlineOptions: any, nuxt: any) => any) | [string, Record<string, any>]>;\n  imports?: {\n    autoImport?: boolean;\n    dirs?: string[];\n  };\n  extends?: string[];\n  components?: Array<string | { path: string }> | { dirs?: Array<string | { path: string }> };\n  css?: string[];\n  alias?: Record<string, string>;\n}\n\nexport interface TemplateExpressionNode {\n  content: string;\n  isStatic: boolean;\n}\n\nexport interface TemplateAstProp {\n  type: number;\n  exp?: TemplateExpressionNode;\n  arg?: TemplateExpressionNode;\n}\n\nexport interface TemplateAstNode {\n  type?: number;\n  tag?: string;\n  props?: TemplateAstProp[];\n  content?: TemplateExpressionNode;\n  children?: TemplateAstNode[];\n}\n\nexport interface Descriptor {\n  script: { content: string } | null;\n  scriptSetup: { content: string } | null;\n  template: { content: string; ast?: TemplateAstNode } | null;\n}\n\nexport type VueSfc = { parse: (source: string, path: string) => { descriptor: Descriptor } };\n"
  },
  {
    "path": "packages/knip/src/plugins/nx/index.ts",
    "content": "import type { ParsedArgs } from 'minimist';\nimport type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { compact } from '../../util/array.ts';\nimport { toConfig, toDependency } from '../../util/input.ts';\nimport { join } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { NxConfigRoot, NxProjectConfiguration } from './types.ts';\n\nconst title = 'Nx';\n\nconst enablers = ['nx', /^@nrwl\\//, /^@nx\\//];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['nx.json', 'project.json', '{apps,libs}/**/project.json', 'package.json'];\n\nconst findNxDependenciesInNxJson: ResolveConfig<NxConfigRoot> = async localConfig => {\n  const targetsDefault = localConfig.targetDefaults\n    ? Object.keys(localConfig.targetDefaults)\n        // Ensure we only grab executors from plugins instead of manual targets\n        // Limiting to scoped packages to ensure we don't have false positives\n        .filter(it => it.includes(':') && it.startsWith('@'))\n        .map(it => it.split(':')[0])\n    : [];\n\n  const plugins =\n    localConfig.plugins && Array.isArray(localConfig.plugins)\n      ? localConfig.plugins\n          .map(value => (typeof value === 'string' ? value : value.plugin))\n          .filter(value => value !== undefined)\n      : [];\n\n  const generators = localConfig.generators\n    ? Object.keys(localConfig.generators)\n        .filter(value => value !== undefined)\n        .map(value => value.split(':')[0])\n    : [];\n\n  return compact([...targetsDefault, ...plugins, ...generators]).map(id => toDependency(id));\n};\n\nconst resolveConfig: ResolveConfig<NxProjectConfiguration | NxConfigRoot> = async (localConfig, options) => {\n  const { configFileName } = options;\n\n  if (configFileName === 'nx.json') {\n    return findNxDependenciesInNxJson(localConfig as NxConfigRoot, options);\n  }\n\n  const config = localConfig as NxProjectConfiguration;\n\n  const targets = config.targets ? Object.values(config.targets) : [];\n\n  const executors = targets\n    .map(target => target?.executor)\n    .filter(executor => executor && !executor.startsWith('.'))\n    .map(executor => executor?.split(':')[0]);\n\n  const inputs = targets\n    .filter(target => target.executor === 'nx:run-commands' || target.command)\n    .flatMap(target => {\n      let commands: string[] = [];\n      if (target.command) commands = [target.command];\n      else if (target.options?.command) commands = [target.options.command];\n      else if (target.options?.commands)\n        commands = target.options.commands.map(commandConfig =>\n          typeof commandConfig === 'string' ? commandConfig : commandConfig.command\n        );\n      const cwd = target.options?.cwd ? join(options.cwd, target.options.cwd) : undefined;\n      return options.getInputsFromScripts(commands, { cwd });\n    });\n\n  const configInputs = targets.flatMap(target => {\n    const opts = target.options;\n    if (!opts) return [];\n\n    const configs = [];\n\n    if ('eslintConfig' in opts && typeof opts.eslintConfig === 'string') {\n      configs.push(toConfig('eslint', opts.eslintConfig));\n    }\n\n    if ('jestConfig' in opts && typeof opts.jestConfig === 'string') {\n      configs.push(toConfig('jest', opts.jestConfig));\n    }\n\n    if ('tsConfig' in opts && typeof opts.tsConfig === 'string') {\n      configs.push(toConfig('typescript', opts.tsConfig));\n    }\n\n    if ('vitestConfig' in opts && typeof opts.vitestConfig === 'string') {\n      configs.push(toConfig('vitest', opts.vitestConfig));\n    }\n\n    if ('webpackConfig' in opts && typeof opts.webpackConfig === 'string') {\n      configs.push(toConfig('webpack', opts.webpackConfig));\n    }\n\n    return configs;\n  });\n\n  return compact([...executors, ...inputs, ...configInputs]).map(id =>\n    typeof id === 'string' ? toDependency(id) : id\n  );\n};\n\nconst args = {\n  fromArgs: (parsed: ParsedArgs) => (parsed._[0] === 'exec' ? [...parsed._.slice(1), ...(parsed['--'] ?? [])] : []),\n};\n\n/** @public */\nexport const docs = {\n  note: `Also see [integrated monorepos](/features/integrated-monorepos) and the note regarding internal workspace dependencies.`,\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/nx/types.ts",
    "content": "export interface NxProjectConfiguration {\n  targets?: {\n    [targetName: string]: {\n      command?: string;\n      executor?: string;\n      options?: {\n        command?: string;\n        commands?: Array<string | { command: string }>;\n        cwd?: string;\n        eslintConfig?: string;\n        jestConfig?: string;\n        tsConfig?: string;\n        vitestConfig?: string;\n        webpackConfig?: string;\n      };\n    };\n  };\n}\n\nexport interface NxConfigRoot {\n  plugins?: Array<\n    | string\n    | {\n        plugin: string;\n      }\n  >;\n  generators?: Record<string, unknown>;\n  targetDefaults?: Record<string, unknown>;\n}\n"
  },
  {
    "path": "packages/knip/src/plugins/nyc/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDeferResolve } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { NycConfig } from './types.ts';\n\n// https://www.npmjs.com/package/nyc\n\nconst title = 'nyc';\n\nconst enablers = ['nyc'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['.nycrc', '.nycrc.{json,yml,yaml}', 'nyc.config.js', 'package.json'];\n\nconst resolveConfig: ResolveConfig<NycConfig> = config => {\n  const extend = config?.extends ?? [];\n  const requires = config?.require ?? [];\n  return [extend, requires].flat().map(id => toDeferResolve(id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/nyc/types.ts",
    "content": "export type NycConfig = {\n  extends?: string;\n  require?: string[];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/oclif/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { OclifConfig } from './types.ts';\n\n// https://oclif.io/docs/configuring_your_cli\n\nconst title = 'oclif';\n\nconst enablers = ['oclif'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['package.json'];\n\nconst resolveConfig: ResolveConfig<OclifConfig> = async config => {\n  const plugins = config?.plugins ?? [];\n  const devPlugins = config?.devPlugins ?? [];\n  return [...plugins, ...devPlugins].map(id => toDependency(id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/oclif/types.ts",
    "content": "export type OclifConfig = {\n  plugins?: string[];\n  devPlugins?: string[];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/openapi-ts/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { toC12config } from '../../util/plugin-config.ts';\n\n// https://heyapi.dev/openapi-ts/configuration\n\nconst title = 'openapi-ts';\n\nconst enablers = ['@hey-api/openapi-ts'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['package.json', ...toC12config('openapi-ts')];\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/oxfmt/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://oxc.rs/docs/guide/usage/formatter/config.html\n\nconst title = 'Oxfmt';\n\nconst enablers = ['oxfmt'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['.oxfmtrc.json', '.oxfmtrc.jsonc', 'oxfmt.config.ts'];\n\nconst args = {\n  config: true,\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/oxlint/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://oxc.rs/docs/guide/usage/linter/config.html\n\nconst title = 'Oxlint';\n\nconst enablers = ['oxlint'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config: string[] = ['.oxlintrc.json', 'oxlint.config.ts'];\n\nconst args = {\n  config: true,\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/parcel/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDeferResolve } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { ParcelConfig } from './types.ts';\n\n// https://parceljs.org/plugin-system/configuration/\n\nconst title = 'Parcel';\n\nconst enablers = ['parcel', '@parcel/core'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['.parcelrc'];\n\nconst resolveConfig: ResolveConfig<ParcelConfig> = async config => {\n  const dependencies: string[] = [];\n\n  if (typeof config.extends === 'string') {\n    dependencies.push(config.extends);\n  } else if (Array.isArray(config.extends)) {\n    dependencies.push(...config.extends);\n  }\n\n  const extractPlugins = (plugins: string | string[] | undefined) => {\n    if (!plugins) return [];\n    return typeof plugins === 'string' ? [plugins] : plugins;\n  };\n\n  const extractPluginsFromMap = (pluginMap: Record<string, string | string[]> | undefined) => {\n    if (!pluginMap) return [];\n    return Object.values(pluginMap).flatMap(extractPlugins);\n  };\n\n  if (config.resolvers) dependencies.push(...extractPlugins(config.resolvers));\n  if (config.transformers) dependencies.push(...extractPluginsFromMap(config.transformers));\n  if (config.bundler) dependencies.push(config.bundler);\n  if (config.namers) dependencies.push(...extractPlugins(config.namers));\n  if (config.runtimes) dependencies.push(...extractPluginsFromMap(config.runtimes));\n  if (config.packagers) dependencies.push(...extractPluginsFromMap(config.packagers));\n  if (config.optimizers) dependencies.push(...extractPluginsFromMap(config.optimizers));\n  if (config.compressors) dependencies.push(...extractPluginsFromMap(config.compressors));\n  if (config.reporters) dependencies.push(...extractPlugins(config.reporters));\n  if (config.validators) dependencies.push(...extractPluginsFromMap(config.validators));\n\n  return dependencies.map(id => toDeferResolve(id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/parcel/types.ts",
    "content": "export type ParcelConfig = {\n  extends?: string | string[];\n  resolvers?: string | string[];\n  transformers?: Record<string, string | string[]>;\n  bundler?: string;\n  namers?: string | string[];\n  runtimes?: Record<string, string | string[]>;\n  packagers?: Record<string, string | string[]>;\n  optimizers?: Record<string, string | string[]>;\n  compressors?: Record<string, string | string[]>;\n  reporters?: string | string[];\n  validators?: Record<string, string | string[]>;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/payload/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDeferResolve } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { PayloadConfig } from './types.ts';\n\n// https://payloadcms.com/docs/configuration/overview\n\nconst title = 'Payload CMS';\n\nconst enablers = ['payload'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['payload.config.ts', 'src/payload.config.ts'];\n\nconst resolveConfig: ResolveConfig<PayloadConfig> = async config => {\n  const awaitedConfig = await config;\n\n  const importMapFile = awaitedConfig?.admin?.importMap?.importMapFile;\n  if (importMapFile) {\n    return [toDeferResolve(importMapFile, { optional: true })];\n  }\n\n  const adminRoute = awaitedConfig?.routes?.admin ?? '/admin';\n  // Payload searches for these paths by default if the `importMapFile` is not explicitly specified\n  // Ref: https://github.com/payloadcms/payload/blob/677596c503e8c0977b89b4579ac6b6d8a294b329/packages/payload/src/bin/generateImportMap/utilities/resolveImportMapFilePath.ts#L39-L40\n  const possibleImportMapPaths = [\n    `app/(payload)${adminRoute}/importMap.js`,\n    `src/app/(payload)${adminRoute}/importMap.js`,\n  ];\n\n  return possibleImportMapPaths.map(id => toDeferResolve(id, { optional: true }));\n};\n\nconst project = ['!migrations/**', '!src/migrations/**', '!payload-types.ts', '!src/payload-types.ts'];\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n  project,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/payload/types.ts",
    "content": "export type PayloadConfig = Promise<{\n  admin?: {\n    importMap?: {\n      // Note that the `admin.importMap.baseDir` config only affects how component paths are written,\n      // and is unrelated to the location of importMap.\n      importMapFile?: string;\n    };\n  };\n  routes?: {\n    admin?: string;\n  };\n}>;\n"
  },
  {
    "path": "packages/knip/src/plugins/playwright/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { arrayify } from '../../util/array.ts';\nimport { type Input, toDeferResolve, toEntry } from '../../util/input.ts';\nimport { join, relative } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { PlaywrightTestConfig } from './types.ts';\n\n// https://playwright.dev/docs/test-configuration\n\nconst title = 'Playwright';\n\nconst enablers = ['@playwright/test'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['playwright.config.{js,ts,mjs}'];\n\nexport const entry = ['**/*.@(spec|test).?(c|m)[jt]s?(x)'];\n\nconst toEntryPatterns = (\n  testMatch: string | RegExp | Array<string | RegExp>,\n  cwd: string,\n  configDir: string,\n  localConfig: PlaywrightTestConfig,\n  rootConfig: PlaywrightTestConfig\n) => {\n  const testDir = localConfig.testDir ?? rootConfig.testDir;\n  const dir = relative(cwd, testDir ? join(configDir, testDir) : configDir);\n  const patterns = [testMatch].flat().filter((p): p is string => typeof p === 'string');\n  return patterns.map(pattern => toEntry(join(dir, pattern)));\n};\n\nconst builtinReporters = ['dot', 'line', 'list', 'junit', 'html', 'blob', 'json', 'github', 'null'];\n\nexport const resolveConfig: ResolveConfig<PlaywrightTestConfig> = async (localConfig, options) => {\n  const { cwd, configFileDir } = options;\n\n  const inputs: Input[] = [];\n  for (const id of arrayify(localConfig.globalSetup)) inputs.push(toEntry(id));\n  for (const id of arrayify(localConfig.globalTeardown)) inputs.push(toEntry(id));\n\n  const projects = localConfig.projects ? [localConfig, ...localConfig.projects] : [localConfig];\n\n  const reporters = [localConfig.reporter].flat().flatMap(reporter => {\n    const name = typeof reporter === 'string' ? reporter : reporter?.[0];\n    if (!name || builtinReporters.includes(name)) return [];\n    return [name];\n  });\n\n  return projects\n    .flatMap(config => toEntryPatterns(config.testMatch ?? entry, cwd, configFileDir, config, localConfig))\n    .concat(reporters.map(id => toDeferResolve(id)))\n    .concat(inputs);\n};\n\nconst args = {\n  positional: true,\n  args: (args: string[]) => args.filter(arg => arg !== 'install' && arg !== 'test'),\n  config: true,\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  entry,\n  resolveConfig,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/playwright/types.ts",
    "content": "// Copied from https://github.com/microsoft/playwright/blob/main/packages/playwright/types/test.d.ts\n\ntype LiteralUnion<T extends U, U = string> = T | (U & { zz_IGNORE_ME?: never });\n\ntype BlobReporterOptions = { outputDir?: string; fileName?: string };\ntype ListReporterOptions = { printSteps?: boolean };\ntype JUnitReporterOptions = {\n  outputFile?: string;\n  stripANSIControlSequences?: boolean;\n  includeProjectInTestName?: boolean;\n};\ntype JsonReporterOptions = { outputFile?: string };\ntype HtmlReporterOptions = {\n  outputFolder?: string;\n  open?: 'always' | 'never' | 'on-failure';\n  host?: string;\n  port?: number;\n  attachmentsBaseURL?: string;\n  title?: string;\n  noSnippets?: boolean;\n};\n\ntype ReporterDescription = Readonly<\n  | ['blob']\n  | ['blob', BlobReporterOptions]\n  | ['dot']\n  | ['line']\n  | ['list']\n  | ['list', ListReporterOptions]\n  | ['github']\n  | ['junit']\n  | ['junit', JUnitReporterOptions]\n  | ['json']\n  | ['json', JsonReporterOptions]\n  | ['html']\n  | ['html', HtmlReporterOptions]\n  | ['null']\n  | [string]\n  | [string, any]\n>;\n\ntype Project = {\n  name: string;\n  use: string;\n  testMatch?: string | RegExp | (string | RegExp)[]; // regexp not supported by Knip\n};\n\nexport type PlaywrightTestConfig = {\n  projects?: Project[];\n  testMatch?: string | RegExp | (string | RegExp)[]; // regexp not supported by Knip\n  testDir?: string;\n  reporter?:\n    | LiteralUnion<'dot' | 'line' | 'list' | 'junit' | 'html' | 'json' | 'github' | 'null', string>\n    | ReporterDescription[];\n  globalSetup?: string | Array<string>;\n  globalTeardown?: string | Array<string>;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/playwright-ct/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { type Input, toEntry } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { entry as playwrightEntry, resolveConfig as playwrightResolveConfig } from '../playwright/index.ts';\nimport type { PlaywrightTestConfig } from '../playwright/types.ts';\n\n// https://playwright.dev/docs/test-components\n\nconst title = 'Playwright for components';\n\nconst enablers = [/^@playwright\\/experimental-ct-/];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['playwright-ct.config.{js,ts}'];\n\nconst ctEntry = 'playwright/index.{js,ts,jsx,tsx}';\n\nconst entry = [...playwrightEntry, ctEntry];\n\nconst resolveConfig: ResolveConfig<PlaywrightTestConfig> = async (localConfig, options) => {\n  const inputs: Input[] = await playwrightResolveConfig(localConfig, options);\n  inputs.push(toEntry(ctEntry));\n  return inputs;\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  entry,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/playwright-test/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://www.npmjs.com/package/playwright-test\n\nconst title = 'playwright-test';\n\nconst enablers = ['playwright-test'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst args = {\n  positional: true,\n  args: (args: string[]) => args.filter(arg => arg !== 'install' && arg !== 'test'),\n  config: true,\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/plop/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://github.com/plopjs/plop/blob/main/README.md\n\nconst title = 'Plop';\n\nconst enablers = ['plop'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['plopfile.{cjs,mjs,js,ts}'];\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/pm2/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toEntry } from '../../util/input.ts';\nimport { isInternal } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { PM2Application, PM2Config } from './types.ts';\n\nconst title = 'pm2';\n\nconst enablers = ['pm2'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['pm2.config.{json,js,cjs,mjs}', 'ecosystem.config.{json,js,cjs,mjs}'];\n\nconst addApplicationEntry = (application: PM2Application, entries: ReturnType<typeof toEntry>[]) => {\n  if (application.script && isInternal(application.script)) entries.push(toEntry(application.script));\n};\n\nconst resolveConfig: ResolveConfig<PM2Config> = config => {\n  const entries: ReturnType<typeof toEntry>[] = [];\n\n  if (Array.isArray(config)) {\n    for (const application of config) addApplicationEntry(application, entries);\n    return entries;\n  }\n\n  addApplicationEntry(config, entries);\n\n  if (!config.apps) return entries;\n\n  const applications = Array.isArray(config.apps) ? config.apps : [config.apps];\n  for (const application of applications) addApplicationEntry(application, entries);\n\n  return entries;\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/pm2/types.ts",
    "content": "export type PM2Application = {\n  script?: string;\n};\n\ntype PM2Applications = PM2Application | PM2Application[];\n\ntype PM2RootConfig = PM2Application & {\n  apps?: PM2Applications;\n};\n\nexport type PM2Config = PM2RootConfig | PM2Application[];\n"
  },
  {
    "path": "packages/knip/src/plugins/pnpm/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { isFile } from '../../util/fs.ts';\n\n// https://pnpm.io/pnpmfile\n\nconst title = 'pnpm';\n\nconst isEnabled: IsPluginEnabled = async ({ cwd, manifest }) =>\n  manifest.packageManager?.startsWith('pnpm@') || isFile(cwd, 'pnpm-lock.yaml') || isFile(cwd, 'pnpm-workspace.yaml');\n\nconst isRootOnly = true;\n\nconst config: string[] = ['.pnpmfile.cjs'];\n\nconst plugin: Plugin = {\n  title,\n  isEnabled,\n  isRootOnly,\n  config,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/postcss/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDeferResolve, toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { toLilconfig } from '../../util/plugin-config.ts';\nimport type { PostCSSConfig } from './types.ts';\n\n// https://github.com/postcss/postcss-load-config/blob/main/src/index.js#L110\n// Additionally postcss.config.json is loaded by nextjs\n\nconst title = 'PostCSS';\n\nconst enablers = ['postcss', 'postcss-cli', 'next'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = [\n  'package.json',\n  'postcss.config.json',\n  ...toLilconfig('postcss', { configDir: false, additionalExtensions: ['mts', 'cts', 'yaml', 'yml'] }),\n];\n\nconst resolveConfig: ResolveConfig<PostCSSConfig> = config => {\n  const plugins = config.plugins\n    ? (Array.isArray(config.plugins) ? config.plugins : Object.keys(config.plugins)).flatMap(plugin => {\n        if (typeof plugin === 'string') return plugin;\n        if (Array.isArray(plugin) && typeof plugin[0] === 'string') return plugin[0];\n        return [];\n      })\n    : [];\n\n  const inputs = plugins.map(id => toDeferResolve(id));\n\n  // Because postcss is not included in peerDependencies of tailwindcss\n  return ['tailwindcss', '@tailwindcss/postcss'].some(tailwindPlugin => plugins.includes(tailwindPlugin))\n    ? [...inputs, toDependency('postcss')]\n    : inputs;\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/postcss/types.ts",
    "content": "export type PostCSSConfig = {\n  plugins?: string[] | [string, unknown][] | Record<string, unknown>;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/preconstruct/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toProductionEntry } from '../../util/input.ts';\nimport { join } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { PreconstructConfig } from './types.ts';\n\n// https://preconstruct.tools/configuration\n\nconst title = 'Preconstruct';\n\nconst enablers = ['@preconstruct/cli'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['package.json'];\n\nconst resolveConfig: ResolveConfig<PreconstructConfig> = async config => {\n  return (config.entrypoints ?? []).map(id => toProductionEntry(join('src', id), { allowIncludeExports: true }));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/preconstruct/types.ts",
    "content": "export type PreconstructConfig = {\n  entrypoints?: string[];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/prettier/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDeferResolve, toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { PrettierConfig } from './types.ts';\n\n// https://prettier.io/docs/en/configuration.html\n// https://github.com/prettier/prettier/blob/main/src/config/prettier-config/config-searcher.js\n\nconst title = 'Prettier';\n\nconst enablers = ['prettier'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = [\n  '.prettierrc',\n  '.prettierrc.{json,js,cjs,mjs,ts,cts,mts,yml,yaml,toml,json5}',\n  'prettier.config.{js,cjs,mjs,ts,cts,mts}',\n  'package.{json,yaml}',\n];\n\nconst resolveConfig: ResolveConfig<PrettierConfig> = config => {\n  if (typeof config === 'string') return [toDeferResolve(config)];\n\n  return Array.isArray(config.plugins)\n    ? config.plugins.filter((plugin): plugin is string => typeof plugin === 'string').map(id => toDependency(id))\n    : [];\n};\n\nconst isFilterTransitiveDependencies = true;\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n  isFilterTransitiveDependencies,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/prettier/types.ts",
    "content": "export type PrettierConfig = {\n  plugins?: (\n    | string\n    | {\n        parsers?: Record<string, unknown>;\n        printers?: Record<string, unknown>;\n        languages?: unknown[];\n        options?: Record<string, unknown>;\n      }\n  )[];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/prisma/compiler.ts",
    "content": "const directiveMatcher = /generator\\s+(?!client)\\w+\\s*\\{\\s*provider\\s*=\\s*\"([^\"]+)\"[^}]*\\}/g;\n\nconst compiler = (text: string) => {\n  const imports = [];\n  let match: RegExpExecArray | null;\n\n  // oxlint-disable-next-line no-cond-assign\n  while ((match = directiveMatcher.exec(text))) {\n    if (match[1]) {\n      imports.push(`import '${match[1]}';`);\n    }\n  }\n\n  return imports.join('\\n');\n};\n\nexport default compiler;\n"
  },
  {
    "path": "packages/knip/src/plugins/prisma/index.ts",
    "content": "import type { ParsedArgs } from 'minimist';\nimport type { Args } from '../../types/args.ts';\nimport type { IsPluginEnabled, Plugin, RegisterCompilers, ResolveConfig } from '../../types/config.ts';\nimport { isDirectory } from '../../util/fs.ts';\nimport { type Input, toEntry } from '../../util/input.ts';\nimport { join } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport compiler from './compiler.ts';\nimport type { PrismaConfig } from './types.ts';\n\n// https://www.prisma.io/docs/orm/prisma-client/setup-and-configuration\n// https://www.prisma.io/docs/orm/reference/prisma-config-reference\n// https://www.prisma.io/docs/orm/prisma-schema/overview/location#prisma-schema-location\n\nconst title = 'Prisma';\n\nconst enablers = ['prisma', /^@prisma\\/.*/];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst entry: string[] = ['prisma/schema.prisma', 'schema.prisma'];\n\nconst config: string[] = [\n  'prisma.config.{js,ts,mjs,cjs,mts,cts}',\n  '.config/prisma.{js,ts,mjs,cjs,mts,cts}',\n  'package.json',\n];\n\nconst resolveSchema = (path: string, cwd: string) => {\n  if (!isDirectory(cwd, path)) {\n    return toEntry(path);\n  }\n  // Multi-file schema directory\n  return toEntry(join(path, '**/*.prisma'));\n};\n\nconst resolveConfig: ResolveConfig<PrismaConfig> = async (config, options) => {\n  const inputs: Input[] = [];\n\n  // Binary\n  if (config.seed) {\n    // package.json\n    inputs.push(...options.getInputsFromScripts(config.seed));\n  } else if (config.migrations?.seed) {\n    // Prisma config file\n    inputs.push(...options.getInputsFromScripts(config.migrations.seed));\n  }\n\n  // Entry/Schema\n  if (config.schema) {\n    // package.json and Prisma config file\n    inputs.push(resolveSchema(config.schema, options.cwd));\n  }\n\n  return inputs;\n};\n\nconst registerCompilers: RegisterCompilers = ({ registerCompiler, hasDependency }) => {\n  if (hasDependency('prisma')) registerCompiler({ extension: '.prisma', compiler });\n};\n\nconst args: Args = {\n  config: true,\n  resolveInputs: (parsed: ParsedArgs, { cwd }) => {\n    const inputs: Input[] = [];\n    if (typeof parsed['schema'] === 'string') {\n      inputs.push(resolveSchema(parsed['schema'], cwd));\n    }\n    return inputs;\n  },\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  entry,\n  config,\n  args,\n  resolveConfig,\n  registerCompilers,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/prisma/types.ts",
    "content": "export type PrismaConfig = {\n  // package.json\n  seed?: string;\n\n  // Prisma config file\n  migrations?: {\n    seed?: string;\n  };\n\n  // both\n  schema?: string;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/qwik/index.ts",
    "content": "import MDX from '../../compilers/mdx.ts';\nimport type { IsPluginEnabled, Plugin, RegisterCompilers, ResolveFromAST } from '../../types/config.ts';\nimport { type Input, toEntry, toIgnore, toProductionEntry } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { config } from '../vite/index.ts';\nimport { getRoutesDirs, getSrcDir } from './resolveFromAST.ts';\n\n// https://qwik.dev/docs/project-structure/\n\nconst title = 'Qwik';\n\nconst enablers = ['@builder.io/qwik'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst entry = ['src/entry.dev.tsx'];\n\nconst production = ['src/root.tsx', 'src/entry.*.tsx'];\n\nconst routeProduction = ['src/routes/**/*.{tsx,ts,md,mdx}'];\n\nconst resolveFromAST: ResolveFromAST = program => {\n  const srcDir = getSrcDir(program);\n  const routesDirs = getRoutesDirs(program, srcDir);\n  const setSrcDir = (pattern: string) => pattern.replace(/^src\\//, `${srcDir}/`);\n  const setRoutesDir = (pattern: string, routesDir: string) => pattern.replace(/^src\\/routes\\//, `${routesDir}/`);\n\n  const routeEntries: Input[] = [];\n  for (const routesDir of routesDirs) {\n    for (const pattern of routeProduction) {\n      routeEntries.push(toProductionEntry(setRoutesDir(pattern, routesDir)));\n    }\n  }\n\n  return [\n    ...entry.map(setSrcDir).map(path => toEntry(path)),\n    ...production.map(setSrcDir).map(path => toProductionEntry(path)),\n    ...routeEntries,\n    toIgnore('@qwik-city-plan', 'unlisted'),\n    toIgnore('@qwik-city-sw-register', 'unlisted'),\n    toIgnore('@qwik-client-manifest', 'unlisted'),\n  ];\n};\n\nconst registerCompilers: RegisterCompilers = ({ registerCompiler, hasDependency }) => {\n  if (hasDependency('@builder.io/qwik-city')) {\n    registerCompiler({ extension: '.mdx', compiler: MDX.compiler });\n  }\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  entry,\n  production: [...production, ...routeProduction],\n  registerCompilers,\n  resolveFromAST,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/qwik/resolveFromAST.ts",
    "content": "import type { Program } from 'oxc-parser';\nimport { findCallArg, getPropertyValues } from '../../typescript/ast-helpers.ts';\n\nexport const getSrcDir = (program: Program): string => {\n  const arg = findCallArg(program, 'qwikVite');\n  if (arg) {\n    const values = getPropertyValues(arg, 'srcDir');\n    if (values.size > 0) return Array.from(values)[0];\n  }\n  return 'src';\n};\n\nexport const getRoutesDirs = (program: Program, srcDir: string): string[] => {\n  const arg = findCallArg(program, 'qwikCity');\n  if (arg) {\n    const values = getPropertyValues(arg, 'routesDir');\n    if (values.size > 0) return Array.from(values);\n  }\n  return [`${srcDir}/routes`];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/raycast/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport type { PackageJson } from '../../types/package-json.ts';\nimport { compact } from '../../util/array.ts';\nimport { toProductionEntry } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { RaycastManifest } from './types.ts';\n\n// https://developers.raycast.com/\n\nconst title = 'Raycast';\n\nconst enablers = ['@raycast/api'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['package.json'];\n\nconst packageJsonPath = (manifest: PackageJson) => manifest;\n\nconst mapEntries = (items: { name?: unknown }[] | undefined, directory: string) => {\n  const names = compact((items ?? []).map(item => (typeof item.name === 'string' ? item.name : undefined)));\n  return names.map(name => toProductionEntry(`${directory}${name}.{js,jsx,ts,tsx}`));\n};\n\nconst resolveConfig: ResolveConfig<RaycastManifest> = async manifest => [\n  ...mapEntries(manifest.commands, 'src/'),\n  ...mapEntries(manifest.tools, 'src/tools/'),\n];\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  packageJsonPath,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/raycast/types.ts",
    "content": "export type RaycastManifestCommand = {\n  name?: unknown;\n};\n\nexport type RaycastManifestTool = {\n  name?: unknown;\n};\n\nexport type RaycastManifest = {\n  commands?: RaycastManifestCommand[];\n  tools?: RaycastManifestTool[];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/react-cosmos/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDeferResolve, toEntry } from '../../util/input.ts';\nimport { join } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { ReactCosmosConfig } from './types.ts';\n\n// https://reactcosmos.org/docs\n\nconst title = 'React Cosmos';\n\nconst enablers = ['react-cosmos'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['cosmos.config.json'];\n\nconst ext = '{js,jsx,ts,tsx,md,mdx}';\n\nconst fixtureEntry = [`**/*.fixture.${ext}`, `__fixtures__/**/*.${ext}`, `**/fixture.${ext}`];\n\nconst decoratorEntry = ['**/cosmos.decorator.{jsx,tsx}'];\n\nconst entry = [...fixtureEntry, ...decoratorEntry];\n\nconst resolveConfig: ResolveConfig<ReactCosmosConfig> = async localConfig => {\n  const { fixturesDir, fixtureFileSuffix } = localConfig;\n  const entries = [\n    join(fixturesDir ?? '__fixtures__', `**/*.${ext}`),\n    join(fixturesDir ?? '', `**/*.${fixtureFileSuffix ?? 'fixture'}.${ext}`),\n    join(fixturesDir ?? '', `**/${fixtureFileSuffix ?? 'fixture'}.${ext}`),\n  ];\n  return [...entries, ...decoratorEntry]\n    .map(id => toEntry(id))\n    .concat((localConfig?.plugins ?? []).map(id => toDeferResolve(id)));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  entry,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/react-cosmos/types.ts",
    "content": "export type ReactCosmosConfig = {\n  plugins?: string[];\n  fixtureFileSuffix?: string;\n  fixturesDir?: string;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/react-native/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, Resolve, ResolveConfig } from '../../types/config.ts';\nimport { type Input, toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { ReactNativeConfig } from './types.ts';\n\n// https://github.com/react-native-community/cli/blob/main/docs/configuration.md\n\nconst title = 'React Native';\n\nconst enablers = ['react-native'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['react-native.config.js'];\n\nconst RN_CLI_PACKAGES = [\n  '@react-native-community/cli',\n  '@react-native-community/cli-platform-android',\n  '@react-native-community/cli-platform-ios',\n];\n\nconst resolveConfig: ResolveConfig<ReactNativeConfig> = async config => {\n  const inputs: Input[] = [];\n\n  if (config.dependencies) {\n    for (const name of Object.keys(config.dependencies)) {\n      inputs.push(toDependency(name));\n    }\n  }\n\n  if (config.platforms) {\n    for (const platform of Object.values(config.platforms)) {\n      if (platform.npmPackageName) inputs.push(toDependency(platform.npmPackageName));\n    }\n  }\n\n  return inputs;\n};\n\nconst resolve: Resolve = () => {\n  return RN_CLI_PACKAGES.map(pkg => toDependency(pkg, { optional: true }));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n  resolve,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/react-native/types.ts",
    "content": "// https://github.com/react-native-community/cli/blob/main/docs/configuration.md\n\nexport type ReactNativeConfig = {\n  reactNativePath?: string;\n  dependencies?: Record<string, unknown>;\n  platforms?: Record<string, { npmPackageName?: string }>;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/react-router/index.ts",
    "content": "import { existsSync } from 'node:fs';\nimport os from 'node:os';\nimport type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { _glob } from '../../util/glob.ts';\nimport { toEntry, toProductionDependency, toProductionEntry } from '../../util/input.ts';\nimport { join, toAbsolute } from '../../util/path.ts';\nimport { hasDependency, load } from '../../util/plugin.ts';\nimport vite from '../vite/index.ts';\nimport type { PluginConfig, RouteConfigEntry } from './types.ts';\n\nconst isWindows = os.platform() === 'win32';\n// https://reactrouter.com/start/framework/routing\n\nconst title = 'React Router';\n\nconst enablers = ['@react-router/dev'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst viteConfig = typeof vite.config === 'function' ? [] : (vite.config ?? []);\nconst config = ['react-router.config.{js,ts}', ...viteConfig];\n\nconst resolveConfig: ResolveConfig<PluginConfig> = async (localConfig, options) => {\n  const { configFileDir } = options;\n  const appDirectory = localConfig.appDirectory ?? 'app';\n  const appDir = toAbsolute(appDirectory, configFileDir);\n\n  // If using flatRoutes from @react-router/fs-routes it will throw an error if this variable is not defined\n  // @ts-expect-error\n  globalThis.__reactRouterAppDirectory = appDir;\n\n  let routeConfig: RouteConfigEntry[] = [];\n\n  const routesPathTs = join(appDir, 'routes.ts');\n  const routesPathJs = join(appDir, 'routes.js');\n\n  if (existsSync(routesPathTs)) {\n    routeConfig = await load(routesPathTs);\n  } else if (existsSync(routesPathJs)) {\n    routeConfig = await load(routesPathJs);\n  }\n\n  const mapRoute = (route: RouteConfigEntry): string[] => {\n    return [join(appDir, route.file), ...(route.children ? route.children.flatMap(mapRoute) : [])];\n  };\n\n  const routes = routeConfig\n    .flatMap(mapRoute)\n    // Since these are literal paths, we need to escape any special characters that might\n    // trip up micromatch/fast-glob.\n    // See:\n    //  - https://reactrouter.com/how-to/file-route-conventions#optional-segments\n    //  - https://www.npmjs.com/package/fast-glob#advanced-syntax\n    .map(route => (isWindows ? route : route.replace(/[\\^*?()[\\]]/g, '\\\\$&')));\n\n  const resolved = [\n    // routes.{ts,js} is only used as input to the bundler build system\n    toEntry(join(appDir, 'routes.{js,ts}')),\n    // routes and entries are part of the actual build\n    toProductionEntry(join(appDir, 'root.{jsx,tsx}')),\n    toProductionEntry(join(appDir, 'entry.{client,server}.{js,jsx,ts,tsx}')),\n    ...routes.map(id => toProductionEntry(id)),\n  ];\n\n  const serverEntries = await _glob({\n    cwd: appDir,\n    patterns: ['entry.server.{js,ts,jsx,tsx}'],\n  });\n\n  // If there are no server entries, then we need to add these as implicit\n  // production dependencies, as @react-router/dev will add the\n  // default entry.server.tsx automatically which depends on these.\n  // See: https://github.com/remix-run/react-router/blob/dev/packages/react-router-dev/config/defaults/entry.server.node.tsx\n  if (serverEntries.length === 0) {\n    resolved.push(toProductionDependency('@react-router/node'));\n    resolved.push(toProductionDependency('isbot'));\n  }\n\n  return resolved;\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/react-router/types.ts",
    "content": "export type PluginConfig = {\n  appDirectory?: string;\n};\n\nexport interface RouteConfigEntry {\n  file: string;\n  children?: RouteConfigEntry[];\n}\n"
  },
  {
    "path": "packages/knip/src/plugins/relay/index.ts",
    "content": "import parseArgs from 'minimist';\nimport type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toProductionEntry } from '../../util/input.ts';\nimport { join } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { RelayConfig } from './types.ts';\n\n// https://relay.dev/docs/next/guides/compiler/#configuration\n// https://github.com/facebook/relay/blob/main/compiler/crates/relay-compiler/relay-compiler-config-schema.json\n\nconst title = 'Relay';\n\nconst enablers = ['vite-plugin-relay', '@swc/plugin-relay', 'babel-plugin-relay'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config: string[] = ['relay.config.json', 'relay.config.js'];\n\nconst resolveConfig: ResolveConfig<RelayConfig> = async config => {\n  const projects = 'projects' in config ? Object.values(config.projects) : [config];\n\n  return projects.map(project => {\n    const artifactDirectory = project.artifactDirectory;\n\n    if (artifactDirectory == null) {\n      return toProductionEntry('**/__generated__/*');\n    }\n\n    return toProductionEntry(join(artifactDirectory, '**'));\n  });\n};\n\nconst args = {\n  binaries: ['relay-compiler'],\n  args: (args: string[]) => ['-c', parseArgs(args)._[0]], // first positional argument is config file\n  config: true,\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/relay/types.ts",
    "content": "type RelayProject = {\n  artifactDirectory?: string;\n  requireCustomScalarTypes?: boolean;\n  customScalarTypes?: Record<\n    string,\n    | string\n    | {\n        name: string;\n        path: string;\n      }\n  >;\n};\n\nexport type RelayConfig =\n  | RelayProject\n  | {\n      projects: Record<string, RelayProject>;\n    };\n"
  },
  {
    "path": "packages/knip/src/plugins/release-it/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { ReleaseItConfig } from './types.ts';\n\n// https://github.com/release-it/release-it/blob/master/docs/plugins.md#using-a-plugin\n// Uses CosmiConfig but with custom searchPlaces\n// https://github.com/release-it/release-it/blob/main/lib/config.js\n\nconst title = 'Release It!';\n\nconst enablers = ['release-it'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['.release-it.{json,js,cjs,ts,yml,yaml,toml}', 'package.json'];\n\nconst resolveConfig: ResolveConfig<ReleaseItConfig> = (config, options) => {\n  const plugins = config.plugins ? Object.keys(config.plugins) : [];\n  const scripts = config.hooks ? Object.values(config.hooks).flat() : [];\n  if (typeof config.github?.releaseNotes === 'string') {\n    scripts.push(config.github.releaseNotes);\n  }\n  if (typeof config.gitlab?.releaseNotes === 'string') {\n    scripts.push(config.gitlab.releaseNotes);\n  }\n  const inputs = options.getInputsFromScripts(scripts);\n\n  return [...plugins.map(id => toDependency(id)), ...inputs];\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/release-it/types.ts",
    "content": "export type ReleaseItConfig = {\n  github?: {\n    releaseNotes?: string | (() => string) | null;\n  };\n  gitlab?: {\n    releaseNotes?: string | (() => string) | null;\n  };\n  plugins?: Record<string, unknown>;\n  hooks?: Record<string, string | string[]>;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/remark/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDeferResolve } from '../../util/input.ts';\nimport { isInternal } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { RemarkConfig } from './types.ts';\n\n// https://github.com/remarkjs/remark/blob/main/packages/remark-cli/readme.md\n\nconst title = 'Remark';\n\nconst enablers = ['remark-cli'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst packageJsonPath = 'remarkConfig';\n\nconst config = ['package.json', '.remarkrc', '.remarkrc.json', '.remarkrc.{js,cjs,mjs}', '.remarkrc.{yml,yaml}'];\n\nconst resolveConfig: ResolveConfig<RemarkConfig> = config => {\n  const plugins =\n    config.plugins\n      ?.flatMap(plugin => {\n        if (typeof plugin === 'string') return plugin;\n        if (Array.isArray(plugin) && typeof plugin[0] === 'string') return plugin[0];\n        return [];\n      })\n      .map(plugin => (isInternal(plugin) ? plugin : plugin.startsWith('remark-') ? plugin : `remark-${plugin}`)) ?? [];\n  return plugins.map(id => toDeferResolve(id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  packageJsonPath,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/remark/types.ts",
    "content": "export type RemarkConfig = {\n  plugins?: (string | [string, unknown] | unknown)[];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/remix/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://remix.run/docs/en/v1/api/conventions\n\nconst title = 'Remix';\n\nconst enablers = [/^@remix-run\\//];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst entry = ['remix.config.js', 'remix.init/index.js'];\n\nconst production = [\n  'app/root.tsx',\n  'app/entry.{client,server}.{js,jsx,ts,tsx}',\n  'app/routes/**/*.{js,ts,tsx}',\n  'server.{js,ts}',\n];\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  entry,\n  production,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/rollup/index.ts",
    "content": "import type { Args } from '../../types/args.ts';\nimport type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://rollupjs.org/guide/en/#configuration-files\n\nconst title = 'Rollup';\n\nconst enablers = ['rollup'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst entry = ['rollup.config.{js,cjs,mjs,ts}'];\n\nconst args: Args = {\n  alias: { plugin: ['p'] },\n  // minimist has an issue with dots like in `--watch.onEnd` so we remap it\n  args: (args: string[]) => args.map(arg => (arg.startsWith('--watch.onEnd') ? `--_exec${arg.slice(13)}` : arg)),\n  fromArgs: ['_exec'],\n  resolve: ['plugin', 'configPlugin'],\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  entry,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/rsbuild/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toProductionEntry } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { RsbuildConfig } from './types.ts';\n\n// https://rsbuild.rs/config/\n\nconst title = 'Rsbuild';\n\nconst enablers = ['@rsbuild/core'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['rsbuild*.config.{mjs,ts,js,cjs,mts,cts}'];\n\nconst resolveConfig: ResolveConfig<RsbuildConfig> = async config => {\n  const entries = new Set<string>();\n\n  const checkSource = (source: RsbuildConfig['source']) => {\n    if (source?.entry) {\n      for (const entry of Object.values(source.entry)) {\n        if (typeof entry === 'string') entries.add(entry);\n        else if (Array.isArray(entry)) for (const e of entry) entries.add(e);\n        else {\n          if (typeof entry.import === 'string') entries.add(entry.import);\n          else if (Array.isArray(entry.import)) for (const e of entry.import) entries.add(e);\n        }\n      }\n    }\n\n    if (source?.preEntry) {\n      const entry = source.preEntry;\n      if (typeof entry === 'string') entries.add(entry);\n      else if (Array.isArray(entry)) for (const e of entry) entries.add(e);\n    }\n  };\n\n  checkSource(config.source);\n\n  if (config.environments) {\n    for (const environment of Object.values(config.environments)) {\n      checkSource(environment.source);\n    }\n  }\n\n  return Array.from(entries).map(input => toProductionEntry(input));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/rsbuild/types.ts",
    "content": "type EntryDescription = Record<string, unknown>;\n\ntype Entry = Record<string, string | string[] | (EntryDescription & { html?: boolean })>;\n\nexport type RsbuildConfig = {\n  plugins?: unknown[];\n  source?: { entry?: Entry; preEntry?: string | string[] };\n  environments?: {\n    [k: string]: Pick<RsbuildConfig, 'plugins' | 'source'>;\n  };\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/rslib/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { RslibConfig } from './types.ts';\n\n// https://rslib.rs/guide/basic/configure-rslib\n\nconst title = 'Rslib';\n\nconst enablers = ['@rslib/core'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst entry = ['rslib*.config.{mjs,ts,js,cjs,mts,cts}'];\n\nconst resolveConfig: ResolveConfig<RslibConfig> = () => {\n  return [];\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  entry,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/rslib/types.ts",
    "content": "// oxlint-disable-next-line @typescript-eslint/no-empty-object-type\nexport type RslibConfig = {};\n"
  },
  {
    "path": "packages/knip/src/plugins/rspack/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { findWebpackDependenciesFromConfig } from '../webpack/index.ts';\nimport type { WebpackConfig } from '../webpack/types.ts';\n\n// https://rspack.rs/config/\n\nconst title = 'Rspack';\n\nconst enablers = ['@rspack/core'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['rspack.config*.{js,ts,mjs,mts,cjs,cts}'];\n\nconst resolveConfig: ResolveConfig<WebpackConfig> = async (localConfig, options) => {\n  const inputs = await findWebpackDependenciesFromConfig(localConfig, options);\n\n  return inputs.filter(input => !input.specifier.startsWith('builtin:'));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/rstest/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDeferResolve, toEntry } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { RstestConfig } from './types.ts';\n\n// https://rstest.rs/\n\nconst title = 'Rstest';\n\nconst enablers = ['@rstest/core'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\n// https://rstest.rs/guide/basic/configure-rstest#configuration-file\nconst config: string[] = ['rstest.config.{js,cjs,mjs,ts,cts,mts}'];\n\n// https://rstest.rs/api/rstest/mockModules#rsmock\nconst mocks = ['**/__mocks__/**/*.?(c|m)[jt]s?(x)'];\n\n// https://rstest.rs/config/test/include\nconst entry = ['**/*.{test,spec}.?(c|m)[jt]s?(x)'];\n\nfunction testEnvironment(config: RstestConfig) {\n  if (!config.testEnvironment || config.testEnvironment === 'node') return [];\n  return [config.testEnvironment];\n}\n\nconst resolveConfig: ResolveConfig<RstestConfig> = async config => {\n  const entries = (config.include ?? entry)\n    .concat(...mocks)\n    .map(toEntry)\n    .concat(...(config.exclude ?? []).map(id => toEntry(`!${id}`)));\n\n  const environments = testEnvironment(config);\n\n  const setupFiles = config.setupFiles ?? [];\n\n  return [...environments, ...setupFiles, ...entries].map(id => (typeof id === 'string' ? toDeferResolve(id) : id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/rstest/types.ts",
    "content": "export type RstestConfig = {\n  // https://rstest.rs/config/test/include#include\n  include?: string[];\n  // https://rstest.rs/config/test/exclude#exclude\n  exclude?: string[];\n  // https://rstest.rs/config/test/testenvironment#testenvironment\n  testEnvironment?: 'node' | 'jsdom' | 'happy-dom';\n  // https://rstest.rs/config/test/setupFiles#setupfiles\n  setupFiles?: string[];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/sanity/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://www.sanity.io/docs/configuration\n\nconst title = 'Sanity';\n\nconst enablers = ['sanity'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst entry = ['sanity.config.{js,jsx,ts,tsx}', 'sanity.cli.{ts,js}', 'sanity.blueprint.{ts,js,json}'];\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  entry,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/semantic-release/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDeferResolve } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { toCosmiconfig } from '../../util/plugin-config.ts';\nimport type { SemanticReleaseConfig } from './types.ts';\n\n// https://github.com/semantic-release/semantic-release/blob/master/docs/usage/configuration.md#configuration-file\n\nconst title = 'Semantic Release';\n\nconst enablers = ['semantic-release'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst isRootOnly = true;\n\nconst packageJsonPath = 'release';\n\nconst config = ['package.json', ...toCosmiconfig('release')];\n\nconst excludePackages = [\n  '@semantic-release/commit-analyzer',\n  '@semantic-release/github',\n  '@semantic-release/npm',\n  '@semantic-release/release-notes-generator',\n];\n\nconst resolveConfig: ResolveConfig<SemanticReleaseConfig> = config => {\n  const plugins = (config?.plugins ?? []).map(plugin => (Array.isArray(plugin) ? plugin[0] : plugin));\n  return plugins.filter(plugin => !excludePackages.includes(plugin)).map(id => toDeferResolve(id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  isRootOnly,\n  packageJsonPath,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/semantic-release/types.ts",
    "content": "export type SemanticReleaseConfig = {\n  plugins?: (string | [string, Record<string, unknown>])[];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/sentry/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://docs.sentry.io/platforms/javascript/configuration/\n\nconst title = 'Sentry';\n\nconst enablers = [/^@sentry\\//];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst production = ['sentry.{client,server,edge}.config.{js,ts}'];\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  production,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/simple-git-hooks/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport type { Input } from '../../util/input.ts';\nimport { toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { SimpleGitHooksConfig } from './types.ts';\n\n// https://github.com/toplenboren/simple-git-hooks\n\nconst title = 'simple-git-hooks';\n\nconst enablers = ['simple-git-hooks'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['.simple-git-hooks.{js,cjs,json}', 'simple-git-hooks.{js,cjs,json}', 'package.json'];\n\nconst resolveConfig: ResolveConfig<SimpleGitHooksConfig> = async (config, options) => {\n  if (options.isProduction) return [];\n\n  if (typeof config === 'function') config = config();\n\n  if (!config) return [];\n\n  const inputs = new Set<Input>();\n\n  for (const hook of Object.values(config)) {\n    for (const id of options.getInputsFromScripts(hook)) inputs.add(id);\n  }\n\n  return [toDependency('simple-git-hooks'), ...Array.from(inputs)];\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/simple-git-hooks/types.ts",
    "content": "// https://github.com/toplenboren/simple-git-hooks?tab=readme-ov-file#additional-configuration-options\n// {\n//   \"pre-commit\": \"npx lint-staged\",\n//   \"pre-push\": \"npm run format\"\n// }\ntype Config = Record<string, string>;\n\nexport type SimpleGitHooksConfig = Config | (() => Config);\n"
  },
  {
    "path": "packages/knip/src/plugins/size-limit/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, Resolve } from '../../types/config.ts';\nimport { toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { toLilconfig } from '../../util/plugin-config.ts';\n\n// https://github.com/ai/size-limit\n// Uses lilconfig but with custom searchPlaces\n// https://github.com/ai/size-limit/blob/main/packages/size-limit/get-config.js\n\nconst title = 'size-limit';\n\nconst enablers = ['size-limit'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst entry = [...toLilconfig('size-limit', { configDir: false, additionalExtensions: ['mts', 'cts'], rcSuffix: '' })];\n\nconst resolve: Resolve = options => {\n  const allDeps = [\n    ...Object.keys(options.manifest.dependencies || {}),\n    ...Object.keys(options.manifest.devDependencies || {}),\n  ];\n\n  const sizeLimitDeps = allDeps.filter(dep => dep.startsWith('@size-limit/'));\n\n  return sizeLimitDeps.map(dep => toDependency(dep));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  entry,\n  resolve,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/sst/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveFromAST } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { getInputsFromHandlers } from './resolveFromAST.ts';\n\n// https://v2.sst.dev\n// https://sst.dev/docs/\n\nconst title = 'SST';\n\nconst enablers = ['sst'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['sst.config.ts'];\n\nconst resolveFromAST: ResolveFromAST = (program, options) => {\n  const inputs = getInputsFromHandlers(program, options);\n  return inputs;\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveFromAST,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/sst/resolveFromAST.ts",
    "content": "import { Visitor } from 'oxc-parser';\nimport type { ResolveFromAST } from '../../types/config.ts';\nimport { collectPropertyValues, getImportMap } from '../../typescript/ast-helpers.ts';\nimport { parseFile } from '../../typescript/visitors/helpers.ts';\nimport { toDeferResolveProductionEntry } from '../../util/input.ts';\nimport { dirname } from '../../util/path.ts';\nimport { _resolveSync } from '../../util/resolve.ts';\n\nexport const getInputsFromHandlers: ResolveFromAST = (program, options) => {\n  const entries = new Set<string>();\n\n  const addHandlers = (values: Set<string>) => {\n    for (const specifier of values) entries.add(specifier.substring(0, specifier.lastIndexOf('.')));\n  };\n\n  try {\n    const importMap = getImportMap(program);\n\n    addHandlers(collectPropertyValues(program, 'handler'));\n\n    const visitor = new Visitor({\n      CallExpression(node) {\n        if (node.callee?.type !== 'MemberExpression') return;\n        const propName = !node.callee.computed ? node.callee.property?.name : undefined;\n        if (propName === 'stack') {\n          const arg = node.arguments?.[0];\n          if (arg?.type === 'Identifier') {\n            const importPath = importMap.get(arg.name);\n            if (importPath) {\n              const resolvedPath = _resolveSync(importPath, dirname(options.configFilePath));\n              if (resolvedPath) {\n                const stackText = options.readFile(resolvedPath);\n                if (stackText) {\n                  const stackResult = parseFile('stack.ts', stackText);\n                  addHandlers(collectPropertyValues(stackResult.program, 'handler'));\n                }\n              }\n            }\n          }\n        }\n        if (propName === 'route' && node.arguments?.length >= 2) {\n          const handlerArg = node.arguments[1];\n          if (handlerArg?.type === 'Literal' && typeof handlerArg.value === 'string') entries.add(handlerArg.value);\n        }\n      },\n    });\n    visitor.visit(program);\n  } catch {}\n\n  return Array.from(entries).map(specifier =>\n    toDeferResolveProductionEntry(specifier, { containingFilePath: options.configFilePath })\n  );\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/starlight/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveFromAST } from '../../types/config.ts';\nimport { toProductionEntry } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { config } from '../astro/index.ts';\nimport { getComponentPathsFromSourceFile } from './resolveFromAST.ts';\n\n// https://starlight.astro.build/reference/configuration/\n\nconst title = 'Starlight';\n\nconst enablers = ['@astrojs/starlight'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst resolveFromAST: ResolveFromAST = program => {\n  const componentPaths = getComponentPathsFromSourceFile(program);\n  return Array.from(componentPaths).map(id => toProductionEntry(id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveFromAST,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/starlight/resolveFromAST.ts",
    "content": "import type { Program } from 'oxc-parser';\nimport { findCallArg, getDefaultImportName, getImportMap, getPropertyValues } from '../../typescript/ast-helpers.ts';\n\nexport const getComponentPathsFromSourceFile = (program: Program) => {\n  const importMap = getImportMap(program);\n  const starlightImportName = getDefaultImportName(importMap, '@astrojs/starlight');\n  if (!starlightImportName) return new Set<string>();\n  const arg = findCallArg(program, starlightImportName);\n  return arg ? getPropertyValues(arg, 'components') : new Set<string>();\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/storybook/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { type Input, toConfig, toDeferResolve, toDependency, toEntry } from '../../util/input.ts';\nimport { join, relative } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { StorybookConfig } from './types.ts';\n\n// https://storybook.js.org/docs/react/configure/overview\n\nconst title = 'Storybook';\n\nconst enablers = [/^@storybook\\//, '@nrwl/storybook'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['.{storybook,rnstorybook}/{main,test-runner}.{js,ts,mts}'];\n\nconst stories = ['**/*.@(mdx|stories.@(mdx|js|jsx|mjs|ts|tsx))'];\n\nconst restEntry = ['.{storybook,rnstorybook}/{manager,preview,index,vitest.setup}.{js,jsx,ts,tsx}'];\n\nconst entry = [...restEntry, ...stories];\n\nconst project = ['.{storybook,rnstorybook}/**/*.{js,jsx,ts,tsx,mts}'];\n\nconst resolveConfig: ResolveConfig<StorybookConfig> = async (localConfig, options) => {\n  const { cwd, configFileDir, configFilePath } = options;\n  const strs = typeof localConfig?.stories === 'function' ? await localConfig.stories(stories) : localConfig?.stories;\n  const relativePatterns = strs?.map(pattern => {\n    if (typeof pattern === 'string') return relative(cwd, join(configFileDir, pattern));\n    return relative(cwd, join(configFileDir, pattern.directory, pattern.files ?? stories[0]));\n  });\n  const patterns = [\n    ...(options.config.entry ?? restEntry),\n    ...(relativePatterns && relativePatterns.length > 0 ? relativePatterns : stories),\n  ];\n\n  const addons = localConfig.addons?.map(addon => (typeof addon === 'string' ? addon : addon.name)) ?? [];\n  const builder =\n    localConfig?.core?.builder &&\n    (typeof localConfig.core.builder === 'string' ? localConfig.core.builder : localConfig.core.builder.name);\n  const builderPackages = builder\n    ? builder.startsWith('webpack')\n      ? [`@storybook/builder-${builder}`, `@storybook/manager-${builder}`]\n      : [builder]\n    : [];\n\n  const framework = localConfig.framework;\n  const frameworkName = typeof framework === 'string' ? framework : framework?.name;\n  const frameworks = frameworkName ? [frameworkName] : [];\n\n  const viteConfigPath =\n    typeof framework === 'object' &&\n    framework?.name === '@storybook/react-vite' &&\n    framework?.options?.builder?.viteConfigPath;\n\n  const configs: Input[] = viteConfigPath\n    ? [toConfig('vite', viteConfigPath, { dir: cwd, containingFilePath: configFilePath })]\n    : [];\n\n  return [\n    ...patterns.map(id => toEntry(id)),\n    ...addons.map(id => toDeferResolve(id)),\n    ...builderPackages.map(id => toDependency(id)),\n    ...frameworks.map(id => toDependency(id)),\n    ...configs,\n  ];\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  entry,\n  project,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/storybook/types.ts",
    "content": "type Stories = (string | { directory: string; files?: string; titlePrefix?: string })[];\n\nexport type StorybookConfig = {\n  stories?: Stories | ((patterns: string[]) => Promise<string[]>);\n  addons?: (string | { name: string })[];\n  core?: {\n    builder?: string | { name?: string };\n  };\n  framework?:\n    | string\n    | {\n        name?: string;\n        options?: {\n          builder?: {\n            viteConfigPath?: string;\n          };\n        };\n      };\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/stryker/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDeferResolve } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { StrykerConfig } from './types.ts';\n\n// https://stryker-mutator.io/docs/stryker-js/config-file/\n\nconst title = 'Stryker';\n\nconst enablers = ['@stryker-mutator/core'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['?(.)stryker.{conf,config}.{js,mjs,cjs,json}'];\n\nconst resolveConfig: ResolveConfig<StrykerConfig> = localConfig => {\n  const runners = localConfig.testRunner ? [`@stryker-mutator/${localConfig.testRunner}-runner`] : [];\n  const checkers = localConfig.checkers\n    ? localConfig.checkers.map(checker => `@stryker-mutator/${checker}-checker`)\n    : [];\n  const plugins = localConfig.plugins ?? [];\n\n  return [...runners, ...checkers, ...plugins].map(id => toDeferResolve(id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/stryker/types.ts",
    "content": "export type StrykerConfig = {\n  testRunner?: string;\n  checkers?: string[];\n  plugins?: string[];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/stylelint/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { type Input, toDeferResolve } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { toCosmiconfig } from '../../util/plugin-config.ts';\nimport type { StyleLintConfig } from './types.ts';\n\n// https://stylelint.io/user-guide/configure/\n\nconst title = 'Stylelint';\n\nconst enablers = ['stylelint'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['package.json', ...toCosmiconfig('stylelint')];\n\nconst resolveConfig: ResolveConfig<StyleLintConfig> = async (config, options) => {\n  const inputs: Input[] = [];\n  const extend = Array.isArray(config.extends) ? config.extends : config.extends ? [config.extends] : [];\n  for (const id of extend) {\n    if (typeof id === 'string') inputs.push(toDeferResolve(id));\n    else for (const x of await resolveConfig(id, options)) inputs.push(x);\n  }\n  for (const plugin of config.plugins ?? []) if (typeof plugin === 'string') inputs.push(toDeferResolve(plugin));\n  if (typeof config.customSyntax === 'string') inputs.push(toDeferResolve(config.customSyntax));\n  for (const override of config.overrides ?? []) {\n    for (const input of await resolveConfig(override, options)) inputs.push(input);\n  }\n  return inputs;\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/stylelint/types.ts",
    "content": "interface BaseStyleLintConfig {\n  customSyntax?: string;\n  extends?: string | string[];\n  plugins?: (string | BaseStyleLintConfig)[];\n}\n\nexport interface StyleLintConfig extends BaseStyleLintConfig {\n  overrides?: BaseStyleLintConfig[];\n}\n"
  },
  {
    "path": "packages/knip/src/plugins/svelte/compiler.ts",
    "content": "import { importsWithinScripts } from '../../compilers/compilers.ts';\n\nconst compiler = importsWithinScripts;\n\nexport default compiler;\n"
  },
  {
    "path": "packages/knip/src/plugins/svelte/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, RegisterCompilers } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { config as viteConfig } from '../vite/index.ts';\nimport compiler from './compiler.ts';\n\n// https://svelte.dev/docs\n\nconst title = 'Svelte';\n\nconst enablers = ['svelte'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst entry = ['svelte.config.js', ...viteConfig];\n\nconst registerCompilers: RegisterCompilers = ({ registerCompiler, hasDependency }) => {\n  if (hasDependency('svelte')) registerCompiler({ extension: '.svelte', compiler });\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  entry,\n  registerCompilers,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/sveltekit/index.ts",
    "content": "import type { Program } from 'oxc-parser';\nimport type { IsPluginEnabled, Plugin, ResolveFromAST } from '../../types/config.ts';\nimport { toAlias, toIgnore, toProductionEntry } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { collectPropertyValues } from '../../typescript/ast-helpers.ts';\n\n// https://svelte.dev/docs/kit\n\nconst title = 'SvelteKit';\n\nconst enablers = ['@sveltejs/kit'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['svelte.config.js'];\n\nconst production = [\n  'src/routes/**/+{page,server,page.server,error,layout,layout.server}{,@*}.{js,ts,svelte}',\n  'src/hooks.{server,client}.{js,ts}',\n  'src/params/*.{js,ts}',\n  'src/service-worker.{js,ts}',\n  'src/service-worker/index.{js,ts}',\n  'src/instrumentation.server.{js,ts}',\n];\n\nconst getLibPath = (program: Program): string => {\n  const values = collectPropertyValues(program, 'lib');\n  return values.size > 0 ? Array.from(values)[0] : 'src/lib';\n};\n\nconst resolveFromAST: ResolveFromAST = program => {\n  const lib = getLibPath(program);\n  return [\n    ...production.map(pattern => toProductionEntry(pattern)),\n    toAlias('$lib', [`./${lib}`]),\n    toAlias('$lib/*', [`./${lib}/*`]),\n    toIgnore('\\\\$app/.+', 'unresolved'),\n    toIgnore('\\\\$env/.+', 'unresolved'),\n    toIgnore('\\\\$service-worker', 'unresolved'),\n  ];\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveFromAST,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/svgo/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://github.com/svg/svgo\n// https://github.com/svg/svgo/blob/main/lib/svgo-node.js\n\nconst title = 'SVGO';\n\nconst enablers = ['svgo', '@svgr/plugin-svgo'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst entry = ['svgo.config.{js,cjs,mjs}'];\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  entry,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/svgr/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { type Input, toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { SvgrConfig } from './types.ts';\n\n// https://react-svgr.com/docs/configuration-files/\n\nconst title = 'SVGR';\n\nconst enablers = ['@svgr/cli', '@svgr/core'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['.svgrrc', '.svgrrc.{yaml,yml,json,js}', 'svgr.config.{js,cjs}', 'package.json'];\n\nconst resolveConfig: ResolveConfig<SvgrConfig> = async config => {\n  const inputs: Input[] = [];\n  if (config.plugins) {\n    for (const plugin of config.plugins) {\n      if (typeof plugin === 'string') inputs.push(toDependency(plugin));\n    }\n  }\n  return inputs;\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/svgr/types.ts",
    "content": "export type SvgrConfig = {\n  plugins?: (string | unknown)[];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/swc/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { compact } from '../../util/array.ts';\nimport { toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { SWCConfig } from './types.ts';\n\n// https://swc.rs/\n// https://swc.rs/docs/configuration/swcrc\n\nconst title = 'SWC';\n\nconst enablers = ['@swc/core'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config: string[] = ['.swcrc'];\n\nconst resolveConfig: ResolveConfig<SWCConfig> = async config => {\n  const inputs = config?.jsc?.experimental?.plugins ?? [];\n  const externalHelpers = config.jsc?.externalHelpers ? ['@swc/helpers'] : [];\n  return compact([...inputs.map(([id]) => id), ...externalHelpers]).map(id => toDependency(id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/swc/types.ts",
    "content": "export type SWCConfig = {\n  jsc?: {\n    experimental?: {\n      plugins?: Array<[pluginName: string, pluginOptions: Record<string, unknown>]>;\n    };\n    externalHelpers?: boolean;\n  };\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/syncpack/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { toCosmiconfig } from '../../util/plugin-config.ts';\n\n// https://jamiemason.github.io/syncpack/config/syncpackrc/\n\nconst title = 'Syncpack';\n\nconst enablers = ['syncpack'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['package.json', ...toCosmiconfig('syncpack')];\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/tailwind/compiler.ts",
    "content": "const directiveMatcher = /@(?:import|config|plugin)\\s+['\"]([^'\"]+)['\"][^;]*;/g;\n\nconst compiler = (text: string) => {\n  const imports = [];\n  let match: RegExpExecArray | null;\n  let index = 0;\n\n  // oxlint-disable-next-line no-cond-assign\n  while ((match = directiveMatcher.exec(text))) if (match[1]) imports.push(`import _$${index++} from '${match[1]}';`);\n\n  return imports.join('\\n');\n};\n\nexport default compiler;\n"
  },
  {
    "path": "packages/knip/src/plugins/tailwind/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, RegisterCompilers } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport compiler from './compiler.ts';\n\n// https://tailwindcss.com/docs/configuration\n// Tailwinds lilconfig dependency is only used for postcss\n\nconst title = 'Tailwind';\n\nconst enablers = ['tailwindcss'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst entry = ['tailwind.config.{js,cjs,mjs,ts}'];\n\nconst registerCompilers: RegisterCompilers = ({ registerCompiler, hasDependency }) => {\n  if (hasDependency('tailwindcss')) registerCompiler({ extension: '.css', compiler });\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  entry,\n  registerCompilers,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/tanstack-router/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toProductionEntry } from '../../util/input.ts';\nimport { toAbsolute } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { TanStackRouterConfig } from './types.ts';\n\n// https://tanstack.com/router/latest/docs/framework/react/routing/file-based-routing\n\nconst title = 'TanStack Router';\n\nconst enablers = [\n  '@tanstack/react-router',\n  '@tanstack/solid-router',\n  '@tanstack/vue-router',\n  '@tanstack/svelte-router',\n  '@tanstack/router-cli',\n  '@tanstack/router-plugin',\n];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['tsr.config.json'];\n\nconst production = ['src/routeTree.gen.{ts,js}'];\n\nconst resolveConfig: ResolveConfig<TanStackRouterConfig> = async (localConfig, options) => {\n  const { configFileDir } = options;\n\n  const generatedRouteTree = localConfig.generatedRouteTree ?? './src/routeTree.gen.ts';\n  const routeTreePath = toAbsolute(generatedRouteTree, configFileDir);\n\n  return [toProductionEntry(routeTreePath)];\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  production,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/tanstack-router/types.ts",
    "content": "export interface TanStackRouterConfig {\n  routesDirectory?: string;\n  generatedRouteTree?: string;\n  quoteStyle?: 'single' | 'double';\n  semicolons?: boolean;\n  disableTypes?: boolean;\n  addExtensions?: boolean;\n  disableLogging?: boolean;\n  disableManifestGeneration?: boolean;\n  apiBase?: string;\n  routeFilePrefix?: string;\n  routeFileIgnorePrefix?: string;\n  routeFileIgnorePattern?: string;\n  routeToken?: string;\n  routeTreeFileHeader?: string[];\n  routeTreeFileFooter?: string[];\n  indexToken?: string;\n  autoCodeSplitting?: boolean;\n  experimental?: {\n    enableCodeSplitting?: boolean;\n  };\n}\n"
  },
  {
    "path": "packages/knip/src/plugins/taskfile/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { isFile } from '../../util/fs.ts';\nimport type { Input } from '../../util/input.ts';\nimport { toConfig } from '../../util/input.ts';\nimport { join, relative } from '../../util/path.ts';\nimport type { TaskfileCommand, TaskfileConfig, TaskfileTask } from './types.ts';\n\n// https://taskfile.dev/\n\nconst title = 'Taskfile';\n\nconst enablers =\n  'This plugin is enabled when a Taskfile is found (Taskfile.yml, taskfile.yml, Taskfile.yaml, taskfile.yaml, etc.).';\n\nconst isRootOnly = true;\n\nconst taskFiles = [\n  'Taskfile.yml',\n  'taskfile.yml',\n  'Taskfile.yaml',\n  'taskfile.yaml',\n  'Taskfile.dist.yml',\n  'taskfile.dist.yml',\n  'Taskfile.dist.yaml',\n  'taskfile.dist.yaml',\n];\n\nconst isEnabled: IsPluginEnabled = async ({ cwd, config }) => {\n  if (config.taskfile) return true;\n  return taskFiles.some(file => isFile(cwd, file));\n};\n\nconst extractScriptsFromCommand = (command: TaskfileCommand): string[] => {\n  const scripts: string[] = [];\n  if (typeof command === 'string') {\n    scripts.push(command);\n  } else if (command && typeof command === 'object') {\n    if (command.cmd && typeof command.cmd === 'string') {\n      scripts.push(command.cmd);\n    }\n    if (command.defer) {\n      if (typeof command.defer === 'string') {\n        scripts.push(command.defer);\n      } else if (command.defer && typeof command.defer === 'object' && 'cmd' in command.defer) {\n        if (typeof command.defer.cmd === 'string') {\n          scripts.push(command.defer.cmd);\n        }\n      }\n    }\n    if (command.for && 'cmd' in command && typeof command.cmd === 'string') {\n      scripts.push(command.cmd);\n    }\n  }\n  return scripts;\n};\n\nconst extractScriptsFromTask = (task: TaskfileTask): string[] => {\n  const scripts: string[] = [];\n  if (typeof task === 'string') {\n    scripts.push(task);\n    return scripts;\n  }\n  if (Array.isArray(task)) {\n    for (const cmd of task) {\n      if (typeof cmd === 'string') {\n        scripts.push(cmd);\n      }\n    }\n    return scripts;\n  }\n  if (task && typeof task === 'object') {\n    if (task.cmd && typeof task.cmd === 'string') {\n      scripts.push(task.cmd);\n    }\n    if (task.cmds) {\n      if (typeof task.cmds === 'string') {\n        scripts.push(task.cmds);\n      } else if (Array.isArray(task.cmds)) {\n        for (const cmd of task.cmds) {\n          scripts.push(...extractScriptsFromCommand(cmd));\n        }\n      }\n    }\n  }\n  return scripts;\n};\n\nconst resolveConfig: ResolveConfig<TaskfileConfig> = async (localConfig, options) => {\n  if (!localConfig || !options.configFilePath) return [];\n\n  const { configFilePath, getInputsFromScripts, isProduction, configFileDir } = options;\n  const inputs: Input[] = [];\n\n  if (localConfig.includes && typeof localConfig.includes === 'object') {\n    for (const includeValue of Object.values(localConfig.includes)) {\n      const includePath =\n        typeof includeValue === 'string'\n          ? includeValue\n          : includeValue && typeof includeValue === 'object' && 'taskfile' in includeValue\n            ? includeValue.taskfile\n            : undefined;\n      if (includePath) {\n        const resolvedPath = join(configFileDir, includePath);\n        inputs.push(\n          toConfig('taskfile', relative(configFileDir, resolvedPath), { containingFilePath: configFilePath })\n        );\n      }\n    }\n  }\n\n  if (localConfig.tasks && typeof localConfig.tasks === 'object') {\n    for (const task of Object.values(localConfig.tasks)) {\n      for (const script of extractScriptsFromTask(task)) {\n        for (const input of getInputsFromScripts([script], {\n          knownBinsOnly: true,\n          containingFilePath: configFilePath,\n        })) {\n          if (isProduction) Object.assign(input, { optional: true });\n          inputs.push({ ...input, dir: configFileDir });\n        }\n      }\n    }\n  }\n\n  return inputs;\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config: taskFiles,\n  resolveConfig,\n  isRootOnly,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/taskfile/types.ts",
    "content": "// https://taskfile.dev/docs/reference/schema#command\nexport type TaskfileCommand =\n  | string\n  | {\n      cmd?: string;\n      task?: string;\n      defer?: string | { task?: string; [key: string]: unknown };\n      for?: unknown;\n      [key: string]: unknown;\n    };\n\n// https://taskfile.dev/docs/reference/schema#command\nexport type TaskfileTask =\n  | string\n  | string[]\n  | {\n      cmds?: string | TaskfileCommand[];\n      cmd?: string;\n      [key: string]: unknown;\n    };\n\n// https://taskfile.dev/docs/reference/schema#include\ntype TaskfileInclude = string | { taskfile: string; [key: string]: unknown };\n\n// https://taskfile.dev/docs/reference/schema\nexport type TaskfileConfig = {\n  tasks?: Record<string, TaskfileTask>;\n  includes?: Record<string, TaskfileInclude>;\n  [key: string]: unknown;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/travis/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { _glob } from '../../util/glob.ts';\n\n// https://docs.travis-ci.com/user/customizing-the-build/\n\nconst title = 'Travis CI';\n\nconst enablers = 'This plugin is enabled when a `.travis.yml` file is found in the root folder.';\n\nconst isEnabled: IsPluginEnabled = async ({ cwd }) => (await _glob({ cwd, patterns: ['.travis.yml'] })).length > 0;\n\nconst isRootOnly = true;\n\nconst config = ['.travis.yml'];\n\nconst resolveConfig: ResolveConfig = async (config, options) => {\n  if (!config) return [];\n\n  const beforeDeploy = config.before_deploy ?? [];\n  const beforeInstall = config.before_install ?? [];\n  const beforeScript = config.before_script ?? [];\n\n  const scripts = [beforeDeploy, beforeInstall, beforeScript].flat();\n\n  return options.getInputsFromScripts(scripts, { knownBinsOnly: true });\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  isRootOnly,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/ts-node/index.ts",
    "content": "import type { Plugin } from '../../types/config.ts';\n\n// https://typestrong.org/ts-node/docs/options\n\nconst title = 'ts-node';\n\nconst args = {\n  positional: true,\n  nodeImportArgs: true,\n  boolean: ['transpileOnly', 'compilerHost', 'ignoreDiagnostics', 'swc', 'preferTsExts'],\n  alias: { transpileOnly: ['T'], compilerHost: ['H'], ignoreDiagnostics: ['D'] },\n};\n\nconst plugin: Plugin = {\n  title,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/tsdown/index.ts",
    "content": "import type { IsLoadConfig, IsPluginEnabled, Plugin, ResolveConfig, ResolveFromAST } from '../../types/config.ts';\nimport { toProductionEntry } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { getEntryFromAST } from './resolveFromAST.ts';\nimport type { Entry, TsdownConfig } from './types.ts';\n\n// https://github.com/rolldown/tsdown/blob/main/src/options/index.ts\n\nconst title = 'tsdown';\n\nconst enablers = ['tsdown'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['tsdown.config.{ts,mts,cts,js,mjs,cjs,json}', 'package.json'];\n\nconst isLoadConfig: IsLoadConfig = ({ configFileName }) =>\n  configFileName === 'package.json' || configFileName.endsWith('.json');\n\nconst normalizeEntry = (entry: Entry | undefined): string[] => {\n  if (!entry) return [];\n\n  if (typeof entry === 'string') {\n    return [entry];\n  }\n\n  if (Array.isArray(entry)) {\n    return entry.flatMap(normalizeEntry);\n  }\n\n  return Object.values(entry).flatMap(value => (Array.isArray(value) ? value : [value]));\n};\n\nconst resolveConfig: ResolveConfig<TsdownConfig> = async config => {\n  if (typeof config === 'function') config = await config({});\n\n  const entryPatterns = [config]\n    .flat()\n    .flatMap(config => normalizeEntry(config.entry))\n    .map(id => toProductionEntry(id, { allowIncludeExports: true }));\n\n  return entryPatterns;\n};\n\nconst resolveFromAST: ResolveFromAST = program => {\n  const entries = getEntryFromAST(program);\n  return [...entries].map(id => toProductionEntry(id, { allowIncludeExports: true }));\n};\n\nconst args = {\n  config: true,\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  isLoadConfig,\n  resolveConfig,\n  resolveFromAST,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/tsdown/resolveFromAST.ts",
    "content": "import type { Program } from 'oxc-parser';\nimport { collectPropertyValues } from '../../typescript/ast-helpers.ts';\n\nexport const getEntryFromAST = (program: Program): Set<string> => {\n  return collectPropertyValues(program, 'entry');\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/tsdown/types.ts",
    "content": "// https://github.com/rolldown/tsdown/blob/main/src/config/types.ts#L71\nexport type Entry = (string | Record<string, string[] | string>)[] | string | Record<string, string[] | string>;\n\ntype Options = {\n  entry?: Entry;\n};\n\ntype MaybePromise<T> = T | Promise<T>;\n\nexport type TsdownConfig = Options | Options[] | ((overrideOptions: Options) => MaybePromise<Options | Options[]>);\n"
  },
  {
    "path": "packages/knip/src/plugins/tsup/index.ts",
    "content": "import type { IsLoadConfig, IsPluginEnabled, Plugin, ResolveConfig, ResolveFromAST } from '../../types/config.ts';\nimport { toProductionEntry } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { getEntryFromAST } from './resolveFromAST.ts';\nimport type { TsupConfig } from './types.ts';\n\n// https://paka.dev/npm/tsup/api\n// https://github.com/egoist/tsup/blob/dev/src/load.ts\n\nconst title = 'tsup';\n\nconst enablers = ['tsup'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['tsup.config.{js,ts,cjs,mjs,json}', 'package.json'];\n\nconst isLoadConfig: IsLoadConfig = ({ configFileName }) =>\n  configFileName === 'package.json' || configFileName.endsWith('.json');\n\nconst resolveConfig: ResolveConfig<TsupConfig> = async config => {\n  if (typeof config === 'function') config = await config({});\n\n  const entryPatterns = [config]\n    .flat()\n    .flatMap(config => {\n      if (!config.entry) return [];\n      if (Array.isArray(config.entry)) return config.entry;\n      return Object.values(config.entry);\n    })\n    .map(id => toProductionEntry(id, { allowIncludeExports: true }));\n\n  return entryPatterns;\n};\n\nconst resolveFromAST: ResolveFromAST = program => {\n  const entries = getEntryFromAST(program);\n  return [...entries].map(id => toProductionEntry(id, { allowIncludeExports: true }));\n};\n\nconst args = {\n  config: true,\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  isLoadConfig,\n  resolveConfig,\n  resolveFromAST,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/tsup/resolveFromAST.ts",
    "content": "import type { Program } from 'oxc-parser';\nimport { collectPropertyValues } from '../../typescript/ast-helpers.ts';\n\nexport const getEntryFromAST = (program: Program): Set<string> => {\n  return collectPropertyValues(program, 'entry');\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/tsup/types.ts",
    "content": "type Entry = string[] | Record<string, string>;\n\ntype Options = {\n  entry?: Entry;\n};\n\ntype MaybePromise<T> = T | Promise<T>;\n\nexport type TsupConfig = Options | Options[] | ((overrideOptions: Options) => MaybePromise<Options | Options[]>);\n"
  },
  {
    "path": "packages/knip/src/plugins/tsx/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport type { PackageJson } from '../../types/package-json.ts';\nimport { toEntry } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://tsx.is\n\nconst title = 'tsx';\n\nconst enablers = ['tsx'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['package.json'];\n\nconst packageJsonPath = (id: PackageJson) => id;\n\nconst resolveConfig: ResolveConfig<PackageJson> = localConfig => {\n  const scripts = localConfig.scripts;\n\n  const entries = [];\n\n  if (scripts && Object.values(scripts).some(script => /(?<=^|\\s)tsx\\s(.*)--test/.test(script))) {\n    const patterns = [\n      '**/*{.,-,_}test.?(c|m)(j|t)s',\n      '**/test-*.?(c|m)(j|t)s',\n      '**/test.?(c|m)(j|t)s',\n      '**/test/**/*.?(c|m)(j|t)s',\n    ];\n    entries.push(...patterns.map(id => toEntry(id)));\n  }\n\n  return entries;\n};\n\nconst args = {\n  positional: true,\n  nodeImportArgs: true,\n  args: (args: string[]) => args.filter(arg => arg !== 'watch'),\n};\n\nconst plugin: Plugin = {\n  title,\n  isEnabled,\n  packageJsonPath,\n  config,\n  resolveConfig,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/typedoc/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDeferResolve } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { TypeDocConfig } from './types.ts';\n\n// https://typedoc.org/guides/overview/\n// https://github.com/TypeStrong/typedoc/blob/9f0fb048399c7a1273dc452d01cca92b34f4675b/src/lib/utils/options/readers/typedoc.ts#L168\n\nconst title = 'TypeDoc';\n\nconst enablers = ['typedoc'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst packageJsonPath = 'typedocOptions';\n\nconst config = [\n  'typedoc.{js,cjs,mjs,json,jsonc}',\n  'typedoc.config.{js,cjs,mjs}',\n  '.config/typedoc.{js,cjs,mjs,json,jsonc}',\n  '.config/typedoc.config.{js,cjs,mjs}',\n  'package.json',\n  'tsconfig.json',\n];\n\nconst resolveConfig: ResolveConfig<TypeDocConfig | { typedocOptions: TypeDocConfig }> = config => {\n  const cfg = 'typedocOptions' in config ? config.typedocOptions : config; // exception for `tsconfig.json`\n  const plugins = cfg?.plugin ?? [];\n  const themes = cfg?.theme ?? [];\n  return [...plugins, ...themes].map(id => toDeferResolve(id));\n};\n\nconst args = {\n  resolve: ['plugin', 'theme'],\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  packageJsonPath,\n  config,\n  resolveConfig,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/typedoc/types.ts",
    "content": "export type TypeDocConfig = {\n  plugin?: string[];\n  theme?: string[];\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/typescript/index.ts",
    "content": "import type { ConfigArg } from '../../types/args.ts';\nimport type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport type { TsConfigJson } from '../../types/tsconfig-json.ts';\nimport { compact } from '../../util/array.ts';\nimport { toAlias, toConfig, toDeferResolve, toProductionDependency } from '../../util/input.ts';\nimport { dirname, join } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://www.typescriptlang.org/tsconfig\n\nconst title = 'TypeScript';\n\nconst enablers = ['typescript', '@typescript/native-preview'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['tsconfig.json'];\n\nconst resolveConfig: ResolveConfig<TsConfigJson> = async (localConfig, options) => {\n  const { compilerOptions } = localConfig;\n\n  const extend = localConfig.extends\n    ? [localConfig.extends]\n        .flat()\n        .map(specifier => toConfig('typescript', specifier, { containingFilePath: options.configFilePath }))\n    : [];\n\n  const references =\n    localConfig.references\n      ?.filter(reference => reference.path.endsWith('.json'))\n      .map(reference => toConfig('typescript', reference.path, { containingFilePath: options.configFilePath })) ?? [];\n\n  if (!(compilerOptions && localConfig)) return compact([...extend, ...references]);\n\n  const jsx = (compilerOptions?.jsxImportSource ? [compilerOptions.jsxImportSource] : []).map(toProductionDependency);\n\n  const types = compilerOptions.types ?? [];\n  const plugins = Array.isArray(compilerOptions?.plugins)\n    ? compilerOptions.plugins.map(plugin => (typeof plugin === 'object' && 'name' in plugin ? plugin.name : ''))\n    : [];\n  const importHelpers = compilerOptions?.importHelpers ? ['tslib'] : [];\n\n  const paths = compilerOptions.paths as Record<string, string[]> | undefined;\n  const configFileDir = dirname(options.configFilePath);\n  const aliases =\n    paths && configFileDir !== options.cwd\n      ? Object.entries(paths).map(([key, prefixes]) =>\n          toAlias(key, prefixes, { dir: join(configFileDir, (compilerOptions.baseUrl as string) ?? '.') })\n        )\n      : [];\n\n  return compact([\n    ...extend,\n    ...references,\n    ...[...types, ...plugins, ...importHelpers].map(id => toDeferResolve(id)),\n    ...jsx,\n    ...aliases,\n  ]);\n};\n\nconst args = {\n  binaries: ['tsc', 'tsgo'],\n  string: ['project'],\n  alias: { project: ['p'] },\n  config: [['project', (p: string) => (p.endsWith('.json') ? p : join(p, 'tsconfig.json'))]] satisfies ConfigArg,\n};\n\nconst note =\n  \"[What's up with that configurable tsconfig.json location?](/reference/faq#whats-up-with-that-configurable-tsconfigjson-location)\";\n\n/** @public */\nexport const docs = { note };\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/unbuild/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toEntry } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { UnbuildConfig } from './types.ts';\n\n// https://github.com/unjs/unbuild#unbuild\n\nconst title = 'unbuild';\n\nconst enablers = ['unbuild'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['build.config.{js,cjs,mjs,ts,mts,cts,json}'];\n\nconst resolveConfig: ResolveConfig<UnbuildConfig> = config => {\n  return [config]\n    .flat()\n    .map(obj => obj.entries)\n    .flatMap(entries => entries?.map(entry => (typeof entry === 'string' ? entry : entry.input)) ?? [])\n    .map(id => toEntry(id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/unbuild/types.ts",
    "content": "type UnbuildConfigObject = Partial<{\n  name: string;\n  entries:\n    | string[]\n    | {\n        builder: string;\n        input: string;\n        outDir: string;\n      }[];\n  outDir: string;\n  declaration: boolean;\n  rollup: Record<string, unknown>;\n}>;\n\nexport type UnbuildConfig = UnbuildConfigObject | UnbuildConfigObject[];\n"
  },
  {
    "path": "packages/knip/src/plugins/unocss/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { toUnconfig } from '../../util/plugin-config.ts';\n\n// https://unocss.dev/guide/config-file\n// https://github.com/unocss/unocss/blob/main/packages/config/src/index.ts\n\nconst title = 'UnoCSS';\n\nconst enablers = ['unocss'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = [...toUnconfig('uno.config'), ...toUnconfig('unocss.config')];\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/vercel-og/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://vercel.com/docs/functions/og-image-generation\n\nconst title = 'Vercel OG';\n\nconst enablers = ['next', '@vercel/og'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst production = [\n  '{src/,}pages/api/og.{jsx,tsx}',\n  '{src/,}app/api/og/route.{jsx,tsx}',\n  // 'api/og.{jsx,tsx}', // TODO maybe add for non-Next.js projects\n];\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  production,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/vike/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://vike.dev\n\nconst title = 'Vike';\n\nconst enablers = ['vike'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst patterns = ['{pages,renderer}/**/+*.{js,jsx,ts,tsx,vue,react,solid}'];\n\nconst production = [...patterns, ...patterns.map(pattern => `*/${pattern}`)];\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  production,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/vite/helpers.ts",
    "content": "import type { Program } from 'oxc-parser';\nimport { Visitor } from 'oxc-parser';\nimport { findProperty, getDefaultImportName, getImportMap, getStringValues } from '../../typescript/ast-helpers.ts';\nimport { isFile, loadFile } from '../../util/fs.ts';\nimport { type Input, toProductionEntry } from '../../util/input.ts';\nimport { join } from '../../util/path.ts';\n\nexport const getReactBabelPlugins = (program: Program): string[] => {\n  const babelPlugins: string[] = [];\n\n  const importMap = getImportMap(program);\n  const reactPluginNames = new Set<string>();\n\n  for (const [importName, importPath] of importMap) {\n    if (importPath.includes('@vitejs/plugin-react')) reactPluginNames.add(importName);\n  }\n\n  if (reactPluginNames.size === 0) {\n    const defaultImportName = getDefaultImportName(importMap, '@vitejs/plugin-react');\n    if (defaultImportName) reactPluginNames.add(defaultImportName);\n    else reactPluginNames.add('react');\n  }\n\n  const visitor = new Visitor({\n    CallExpression(node) {\n      if (node.callee?.type !== 'Identifier' || node.callee.name !== 'defineConfig') return;\n      const plugins = findProperty(node.arguments?.[0], 'plugins');\n      if (plugins?.type !== 'ArrayExpression') return;\n\n      for (const el of plugins.elements ?? []) {\n        if (el?.type !== 'CallExpression' || el.callee?.type !== 'Identifier') continue;\n        if (!reactPluginNames.has(el.callee.name)) continue;\n\n        const babelPluginsArray = findProperty(findProperty(el.arguments?.[0], 'babel'), 'plugins');\n        for (const v of getStringValues(babelPluginsArray)) babelPlugins.push(v);\n      }\n    },\n  });\n  visitor.visit(program);\n\n  return babelPlugins;\n};\n\nconst moduleScriptPattern =\n  /<script\\b(?=[^>]*\\btype\\s*=\\s*[\"']?module[\"']?)(?=[^>]*\\bsrc\\s*=\\s*[\"']?([^\"' >]+)[\"']?)[^>]*>/gi;\n\nconst normalizeModuleScriptSrc = (value: string) => value.trim().replace(/^\\//, '');\n\nconst getModuleScriptSources = (html: string): string[] => {\n  const matches = html.matchAll(moduleScriptPattern);\n  const sources = [];\n\n  for (const match of matches) {\n    const src = normalizeModuleScriptSrc(match[1]);\n    if (src) sources.push(src);\n  }\n\n  return sources;\n};\n\nexport const getIndexHtmlEntries = async (rootDir: string): Promise<Input[]> => {\n  const indexPath = join(rootDir, 'index.html');\n  if (!isFile(indexPath)) return [];\n\n  const html = await loadFile(indexPath);\n  const entries = getModuleScriptSources(html).map(src => join(rootDir, src));\n  return entries.map(entry => toProductionEntry(entry));\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/vite/index.ts",
    "content": "import type { Args } from '../../types/args.ts';\nimport type { IsPluginEnabled, Plugin, RegisterVisitors, Resolve, ResolveFromAST } from '../../types/config.ts';\nimport { toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { resolveConfig } from '../vitest/index.ts';\nimport { getIndexHtmlEntries, getReactBabelPlugins } from './helpers.ts';\nimport { createImportMetaGlobVisitor } from './visitors/importMetaGlob.ts';\n\n// https://vitejs.dev/config/\n\nconst title = 'Vite';\n\nconst enablers = ['vite', 'vitest'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nexport const config = ['vite.config.{js,mjs,ts,cjs,mts,cts}'];\n\nconst resolveFromAST: ResolveFromAST = program => {\n  const babelPlugins = getReactBabelPlugins(program);\n  return babelPlugins.map(plugin => toDependency(plugin));\n};\n\nconst registerVisitors: RegisterVisitors = ({ ctx, registerVisitor }) => {\n  registerVisitor(createImportMetaGlobVisitor(ctx));\n};\n\nconst resolve: Resolve = async options => {\n  return getIndexHtmlEntries(options.cwd);\n};\n\nconst args: Args = {\n  config: true,\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n  resolveFromAST,\n  resolve,\n  registerVisitors,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/vite/visitors/importMetaGlob.ts",
    "content": "import { IMPORT_FLAGS } from '../../../constants.ts';\nimport type { PluginVisitorContext, PluginVisitorObject } from '../../../types/config.ts';\nimport { _syncGlob } from '../../../util/glob.ts';\nimport { dirname, isAbsolute, join } from '../../../util/path.ts';\nimport { getStringValue, isStringLiteral } from '../../../typescript/visitors/helpers.ts';\n\nexport function createImportMetaGlobVisitor(ctx: PluginVisitorContext): PluginVisitorObject {\n  return {\n    CallExpression(node) {\n      if (\n        node.callee.type !== 'MemberExpression' ||\n        node.callee.computed ||\n        node.callee.object.type !== 'MetaProperty' ||\n        node.callee.property.name !== 'glob' ||\n        node.arguments.length < 1\n      )\n        return;\n\n      const arg = node.arguments[0];\n      let patterns: string[] | undefined;\n      if (isStringLiteral(arg)) {\n        patterns = [getStringValue(arg)!];\n      } else if (arg.type === 'ArrayExpression') {\n        patterns = [];\n        for (const e of arg.elements) {\n          if (e && isStringLiteral(e)) patterns.push(getStringValue(e)!);\n        }\n      }\n\n      if (!patterns?.length) return;\n\n      const dir = dirname(ctx.filePath);\n      const files = _syncGlob({ patterns, cwd: dir });\n\n      for (const f of files) {\n        ctx.addImport(isAbsolute(f) ? f : join(dir, f), arg.start, IMPORT_FLAGS.ENTRY);\n      }\n    },\n  };\n}\n"
  },
  {
    "path": "packages/knip/src/plugins/vitepress/index.ts",
    "content": "import type { IsPluginEnabled, Plugin } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\n\n// https://vitepress.dev/\n\nconst title = 'VitePress';\n\nconst enablers = ['vitepress'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst entry = ['.vitepress/config.{js,ts,mjs,mts}', '.vitepress/theme/index.{js,ts,mjs,mts}'];\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  entry,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/vitest/helpers.ts",
    "content": "import type { ViteConfig } from './types.ts';\n\n/**\n * Sources:\n * - https://github.com/vitest-dev/vitest/blob/main/packages/vitest/src/integrations/env/index.ts\n * - https://github.com/vitest-dev/vitest/blob/main/packages/vitest/src/node/reporters/index.ts\n */\n\ntype KnownEnvironment = 'node' | 'jsdom' | 'happy-dom' | 'edge-runtime';\n\nconst environments = {\n  node: null,\n  jsdom: null,\n  'happy-dom': null,\n  'edge-runtime': null,\n};\n\nconst envPackageNames: Record<Exclude<keyof typeof environments, 'node'>, string> = {\n  jsdom: 'jsdom',\n  'happy-dom': 'happy-dom',\n  'edge-runtime': '@edge-runtime/vm',\n};\n\nexport const getEnvSpecifier = (env: string) => {\n  if (env in envPackageNames) return envPackageNames[env as Exclude<KnownEnvironment, 'node'>];\n  return `vitest-environment-${env}`;\n};\n\n// See full list here:\n// https://github.com/vitest-dev/vitest/blob/v3.2.4/packages/vitest/src/node/reporters/index.ts#L46-L58\n// https://github.com/vitest-dev/vitest/blob/v4.0.3/packages/vitest/src/node/reporters/index.ts#L47-L59\nconst builtInReporters = [\n  'basic',\n  'blob',\n  'default',\n  'dot',\n  'github-actions',\n  'hanging-process',\n  'html',\n  'json',\n  'junit',\n  'tap',\n  'tap-flat',\n  'tree',\n  'verbose',\n];\n\nexport const getExternalReporters = (reporters?: ViteConfig['test']['reporters']) =>\n  reporters\n    ? [reporters]\n        .flat()\n        .map(reporter => (Array.isArray(reporter) ? reporter[0] : reporter))\n        .filter((reporter): reporter is string => typeof reporter === 'string' && !builtInReporters.includes(reporter))\n    : [];\n"
  },
  {
    "path": "packages/knip/src/plugins/vitest/index.ts",
    "content": "import type { ParsedArgs } from 'minimist';\nimport { DEFAULT_EXTENSIONS } from '../../constants.ts';\nimport type { Args } from '../../types/args.ts';\nimport type { IsPluginEnabled, Plugin, PluginOptions, ResolveConfig } from '../../types/config.ts';\nimport { _glob } from '../../util/glob.ts';\nimport { type Input, toAlias, toConfig, toDeferResolve, toDependency, toEntry } from '../../util/input.ts';\nimport { isAbsolute, isInternal, join, toAbsolute, toPosix } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { getIndexHtmlEntries } from '../vite/helpers.ts';\nimport { getEnvSpecifier, getExternalReporters } from './helpers.ts';\nimport type { AliasOptions, COMMAND, MODE, ViteConfig, ViteConfigOrFn, VitestWorkspaceConfig } from './types.ts';\n\n// https://vitest.dev/config/\n\nconst title = 'Vitest';\n\nconst enablers = ['vitest'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['vitest.config.{js,mjs,ts,cjs,mts,cts}', 'vitest.{workspace,projects}.{js,mjs,ts,cjs,mts,cts,json}'];\n\nconst mocks = ['**/__mocks__/**/*.[jt]s?(x)'];\n\nconst entry = ['**/*.{bench,test,test-d,spec,spec-d}.?(c|m)[jt]s?(x)', ...mocks];\n\nconst findConfigDependencies = (localConfig: ViteConfig, options: PluginOptions, vitestRoot: string) => {\n  const { configFileDir: dir } = options;\n  const testConfig = localConfig.test;\n\n  if (!testConfig) return [];\n\n  const env = testConfig.environment;\n  const environments =\n    env && env !== 'node'\n      ? isInternal(env) || isAbsolute(env)\n        ? [toDeferResolve(env)]\n        : [toDependency(getEnvSpecifier(env))]\n      : [];\n  const reporters = getExternalReporters(testConfig.reporters);\n\n  const hasCoverage = testConfig.coverage && (testConfig.coverage.enabled !== false || testConfig.coverage.provider);\n  const coverage = hasCoverage ? [`@vitest/coverage-${testConfig.coverage?.provider ?? 'v8'}`] : [];\n\n  const setupFiles = [testConfig.setupFiles ?? []]\n    .flat()\n    .map(specifier => ({ ...toDeferResolve(specifier), dir: vitestRoot }));\n  const globalSetup = [testConfig.globalSetup ?? []].flat().map(specifier => ({ ...toDeferResolve(specifier), dir }));\n\n  const workspaceDependencies: Input[] = [];\n  if (testConfig.workspace !== undefined) {\n    for (const workspaceConfig of testConfig.workspace) {\n      workspaceDependencies.push(...findConfigDependencies(workspaceConfig, options, vitestRoot));\n    }\n  }\n\n  const projectsDependencies: Input[] = [];\n  if (testConfig.projects !== undefined) {\n    for (const projectConfig of testConfig.projects) {\n      if (typeof projectConfig !== 'string') {\n        projectsDependencies.push(...findConfigDependencies(projectConfig, options, vitestRoot));\n      }\n    }\n  }\n\n  return [\n    ...environments,\n    ...reporters.map(id => toDependency(id)),\n    ...coverage.map(id => toDependency(id)),\n    ...setupFiles,\n    ...globalSetup,\n    ...workspaceDependencies,\n    ...projectsDependencies,\n  ];\n};\n\nconst getConfigs = async (localConfig: ViteConfigOrFn | VitestWorkspaceConfig) => {\n  const configs: ViteConfig[] = [];\n  for (const config of [localConfig].flat()) {\n    if (config && typeof config !== 'string') {\n      if (typeof config === 'function') {\n        for (const command of ['dev', 'serve', 'build'] as COMMAND[]) {\n          for (const mode of ['development', 'production'] as MODE[]) {\n            const cfg = await config({ command, mode, ssrBuild: undefined });\n            configs.push(cfg);\n            if (cfg.test?.projects) {\n              for (const project of cfg.test.projects) {\n                if (typeof project !== 'string') {\n                  configs.push(project);\n                }\n              }\n            }\n          }\n        }\n      } else {\n        configs.push(config);\n        if (config.test?.projects) {\n          for (const project of config.test.projects) {\n            if (typeof project !== 'string') {\n              configs.push(project);\n            }\n          }\n        }\n      }\n    }\n  }\n  return configs;\n};\n\nexport const resolveConfig: ResolveConfig<ViteConfigOrFn | VitestWorkspaceConfig> = async (localConfig, options) => {\n  const inputs = new Set<Input>();\n\n  inputs.add(toEntry(join(options.cwd, 'src/vite-env.d.ts')));\n\n  const configs = await getConfigs(localConfig);\n\n  for (const cfg of configs) {\n    if (cfg.test?.projects) {\n      for (const project of cfg.test.projects) {\n        if (typeof project === 'string') {\n          const projectFiles = await _glob({\n            cwd: options.cwd,\n            patterns: [project],\n            gitignore: false,\n          });\n          for (const projectFile of projectFiles) {\n            inputs.add(toConfig('vitest', projectFile, { containingFilePath: options.configFilePath }));\n          }\n        }\n      }\n    }\n  }\n\n  const addStar = (value: string) => (value.endsWith('*') ? value : join(value, '*').replace(/\\/\\*\\*$/, '/*'));\n  const addAliases = (aliasOptions: AliasOptions) => {\n    for (const [alias, value] of Object.entries(aliasOptions)) {\n      if (!value) continue;\n      const prefixes = [value]\n        .flat()\n        .filter(value => typeof value === 'string')\n        .map(prefix => {\n          if (toPosix(prefix).startsWith(options.cwd)) return prefix;\n          return join(options.cwd, prefix);\n        });\n      if (alias.length > 1) inputs.add(toAlias(alias, prefixes));\n      inputs.add(toAlias(addStar(alias), prefixes.map(addStar)));\n    }\n  };\n\n  const seenRoots = new Set<string>();\n\n  for (const cfg of configs) {\n    const viteRoot = toAbsolute(cfg.root ?? '.', options.cwd);\n    if (!seenRoots.has(viteRoot)) {\n      seenRoots.add(viteRoot);\n      for (const entry of await getIndexHtmlEntries(viteRoot)) inputs.add(entry);\n    }\n\n    const dir = join(options.cwd, cfg.test?.root ?? '.');\n\n    if (cfg.test) {\n      if (cfg.test?.include) {\n        for (const dependency of cfg.test.include) dependency[0] !== '!' && inputs.add(toEntry(join(dir, dependency)));\n        if (!options.config.entry) for (const dependency of mocks) inputs.add(toEntry(join(dir, dependency)));\n      } else {\n        for (const dependency of options.config.entry ?? entry) inputs.add(toEntry(join(dir, dependency)));\n      }\n\n      if (cfg.test.alias) addAliases(cfg.test.alias);\n    }\n\n    if (cfg.resolve?.alias) addAliases(cfg.resolve.alias);\n    if (cfg.resolve?.extensions) {\n      // Filter out default extensions from resolve.extensions\n      const customExtensions = cfg.resolve.extensions.filter(\n        ext => ext.startsWith('.') && !DEFAULT_EXTENSIONS.has(ext)\n      );\n\n      for (const ext of customExtensions) {\n        inputs.add(toEntry(`src/**/*${ext}`));\n      }\n    }\n    for (const dependency of findConfigDependencies(cfg, options, dir)) inputs.add(dependency);\n    const _entry = cfg.build?.lib?.entry ?? [];\n    const deps = (typeof _entry === 'string' ? [_entry] : Object.values(_entry))\n      .map(specifier => join(dir, specifier))\n      .map(id => toEntry(id));\n    for (const dependency of deps) inputs.add(dependency);\n  }\n\n  return Array.from(inputs);\n};\n\nconst args: Args = {\n  config: true,\n  resolveInputs: (parsed: ParsedArgs) => {\n    const inputs: Input[] = [];\n    if (parsed['ui']) inputs.push(toDependency('@vitest/ui', { optional: true }));\n    if (typeof parsed['coverage'] === 'object' && parsed['coverage'].provider) {\n      inputs.push(toDependency(`@vitest/coverage-${parsed['coverage'].provider}`));\n    }\n    if (parsed['reporter']) {\n      for (const reporter of getExternalReporters([parsed['reporter']].flat())) {\n        inputs.push(toDependency(reporter));\n      }\n    }\n    if (parsed['environment'] && parsed['environment'] !== 'node') {\n      inputs.push(toDependency(getEnvSpecifier(parsed['environment'])));\n    }\n    if (typeof parsed['typecheck'] === 'object' && parsed['typecheck'].checker) {\n      inputs.push(toDependency(parsed['typecheck'].checker === 'tsc' ? 'typescript' : parsed['typecheck'].checker));\n    }\n    return inputs;\n  },\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  entry,\n  resolveConfig,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/vitest/types.ts",
    "content": "interface Alias {\n  find: string | RegExp;\n  replacement: string;\n  customResolver?: any;\n}\n\nexport type AliasOptions = readonly Alias[] | { [find: string]: string };\n\ninterface VitestConfig {\n  test: {\n    include: string[];\n    coverage?: {\n      enabled?: boolean;\n      provider: string;\n    };\n    root?: string;\n    environment?: string;\n    globalSetup?: string | string[];\n    reporters?: (string | [string, unknown] | unknown)[];\n    setupFiles?: string | string[];\n    workspace?: (ViteConfig & { test: VitestConfig['test'] & { workspace: never } })[];\n    projects?: (string | (ViteConfig & { test: VitestConfig['test'] & { projects: never } }))[];\n    alias?: AliasOptions;\n  };\n}\n\nexport interface ViteConfig extends VitestConfig {\n  root?: string;\n  plugins?: unknown[];\n  build?: {\n    lib?: {\n      entry: string | string[] | { [entryAlias: string]: string };\n    };\n  };\n  resolve?: {\n    alias?: AliasOptions;\n    extensions?: string[];\n  };\n}\n\nexport type COMMAND = 'dev' | 'serve' | 'build';\nexport type MODE = 'development' | 'production';\n\ninterface Options {\n  command: COMMAND;\n  mode: MODE;\n  ssrBuild?: boolean | undefined;\n}\n\nexport type ViteConfigOrFn =\n  | ViteConfig\n  | ((options: Options) => ViteConfig)\n  | ((options: Options) => Promise<ViteConfig>);\n\nexport type VitestWorkspaceConfig = (string | ViteConfig)[];\n"
  },
  {
    "path": "packages/knip/src/plugins/vue/compiler.ts",
    "content": "import { scriptBodies } from '../../compilers/compilers.ts';\n\nconst compiler = scriptBodies;\n\nexport default compiler;\n"
  },
  {
    "path": "packages/knip/src/plugins/vue/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, RegisterCompilers, ResolveConfig } from '../../types/config.ts';\nimport { type Input, toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { findWebpackDependenciesFromConfig } from '../webpack/index.ts';\nimport compiler from './compiler.ts';\nimport type { VueConfig, WebpackConfiguration } from './types.ts';\n\n// https://cli.vuejs.org/config/\n// https://vuejs.org/guide/scaling-up/tooling.html#vue-cli\n\nconst title = 'Vue';\n\nconst enablers = ['vue'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['vue.config.{js,ts,mjs}'];\n\nconst resolveConfig: ResolveConfig<VueConfig> = async (config, options) => {\n  const { manifest } = options;\n\n  const inputs: Input[] = [];\n\n  if (config.configureWebpack) {\n    const baseConfig = {\n      mode: 'development',\n      entry: {},\n      resolve: {},\n      plugins: [],\n      module: { rules: [] },\n    } satisfies WebpackConfiguration;\n    const modifiedConfig =\n      typeof config.configureWebpack === 'function' ? config.configureWebpack(baseConfig) : config.configureWebpack;\n    const inputsFromConfig = await findWebpackDependenciesFromConfig(modifiedConfig ?? baseConfig, options);\n    for (const input of inputsFromConfig) inputs.push(input);\n  }\n\n  if (\n    manifest.scripts &&\n    Object.values(manifest.scripts).some(script => /(?<=^|\\s)vue-cli-service(\\s|\\s.+\\s)lint(?=\\s|$)/.test(script))\n  ) {\n    inputs.push(toDependency('@vue/cli-plugin-eslint'));\n  }\n\n  return inputs;\n};\n\nconst registerCompilers: RegisterCompilers = ({ registerCompiler, hasDependency }) => {\n  if (hasDependency('vue') || hasDependency('nuxt')) registerCompiler({ extension: '.vue', compiler });\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n  registerCompilers,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/vue/types.ts",
    "content": "import type { Configuration } from 'webpack';\n\nexport type VueConfig = {\n  configureWebpack?: Configuration | ((config: Configuration) => Configuration | undefined);\n};\n\nexport type { Configuration as WebpackConfiguration };\n"
  },
  {
    "path": "packages/knip/src/plugins/webdriver-io/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { WebdriverIOConfig } from './types.ts';\n\n// https://webdriver.io/docs/configuration\n\nconst title = 'WebdriverIO';\n\nconst enablers = ['@wdio/cli'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['wdio.conf.{js,ts}'];\n\nconst resolveConfig: ResolveConfig<WebdriverIOConfig> = async config => {\n  const cfg = config?.config;\n  if (!cfg) return [];\n\n  const frameworks = cfg?.framework ? [`@wdio/${cfg.framework}-framework`] : [];\n\n  const runners =\n    cfg?.runner && cfg.runner !== 'local'\n      ? [`@wdio/${Array.isArray(cfg.runner) ? cfg.runner[0] : cfg.runner}-runner`]\n      : [];\n\n  const reporters = cfg?.reporters\n    ? cfg.reporters\n        .flatMap(reporter => {\n          if (typeof reporter === 'string') return [reporter];\n          if (Array.isArray(reporter) && typeof reporter[0] === 'string') return [reporter[0]];\n          return [];\n        })\n        .map(reporter => `@wdio/${reporter}-reporter`)\n    : [];\n\n  return [...frameworks, ...runners, ...reporters].map(id => toDependency(id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/webdriver-io/types.ts",
    "content": "/// <reference types=\"@wdio/types\" />\n\nexport type WebdriverIOConfig = {\n  config: WebdriverIO.Config;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/webpack/index.ts",
    "content": "import type { ParsedArgs } from 'minimist';\nimport type { ResolveOptions, RuleSetRule, RuleSetUseItem } from 'webpack';\nimport type { Args } from '../../types/args.ts';\nimport type { IsPluginEnabled, Plugin, RegisterVisitors, ResolveConfig } from '../../types/config.ts';\nimport {\n  type Input,\n  toAlias,\n  toDeferResolve,\n  toDeferResolveEntry,\n  toDeferResolveProductionEntry,\n  toDependency,\n} from '../../util/input.ts';\nimport { isInternal, join, toAbsolute } from '../../util/path.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { getDependenciesFromConfig } from '../babel/index.ts';\nimport type { BabelConfigObj } from '../babel/types.ts';\nimport type { Argv, Env, ProvidePlugin, WebpackConfig } from './types.ts';\nimport { createRequireContextVisitor } from './visitors/requireContext.ts';\n// https://webpack.js.org/configuration/\n\nconst title = 'webpack';\n\nconst enablers = ['webpack', 'webpack-cli'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['webpack.config.{js,ts,mjs,cjs,mts,cts}'];\n\nconst hasBabelOptions = (use: RuleSetUseItem) =>\n  Boolean(use) &&\n  typeof use !== 'string' &&\n  'loader' in use &&\n  typeof use.loader === 'string' &&\n  use.loader === 'babel-loader' &&\n  typeof use.options === 'object';\n\nconst info = {\n  compiler: '',\n  issuer: '',\n  realResource: '',\n  resource: '',\n  resourceQuery: '',\n  dependency: '',\n  descriptionData: {},\n  issuerLayer: '',\n};\n\nconst resolveRuleSetDependencies = (rule: RuleSetRule | undefined | null | false | 0 | '...' | ''): string[] => {\n  if (!rule || typeof rule === 'string') return [];\n  if (typeof rule.use === 'string') return [rule.use];\n  let useItem = rule.use ?? rule.loader ?? rule;\n  if (typeof useItem === 'function') useItem = useItem(info);\n  if (typeof useItem === 'string' && hasBabelOptions(rule)) {\n    const d = getDependenciesFromConfig((rule as { options: BabelConfigObj }).options).map(d => d.specifier);\n    return [useItem, ...d];\n  }\n  return [useItem].flat().flatMap((item: RuleSetRule | RuleSetUseItem | undefined | null | false | 0) => {\n    if (!item) return [];\n    if (hasBabelOptions(item)) {\n      const d = getDependenciesFromConfig((item as { options: BabelConfigObj }).options).map(d => d.specifier);\n      return [...resolveUseItem(item), ...d];\n    }\n    if (typeof item !== 'string' && 'oneOf' in item) return item.oneOf?.flatMap(resolveRuleSetDependencies) ?? [];\n    return resolveUseItem(item);\n  });\n};\n\nconst resolveUseItem = (use: RuleSetUseItem) => {\n  if (!use) return [];\n  if (typeof use === 'string') return [use];\n  if ('loader' in use && typeof use.loader === 'string') return [use.loader];\n  return [];\n};\n\nexport const findWebpackDependenciesFromConfig: ResolveConfig<WebpackConfig> = async (config, options) => {\n  const { cwd, isProduction } = options;\n\n  // Projects may use a single config function for both development and production modes, so resolve it twice\n  // https://webpack.js.org/configuration/configuration-types/#exporting-a-function\n  const passes = typeof config === 'function' ? [false, true] : [isProduction];\n\n  const inputs = new Set<Input>();\n\n  for (const isProduction of passes) {\n    const mode = isProduction ? 'production' : 'development';\n    const env: Env = { production: isProduction, mode };\n    const argv: Argv = { mode };\n    const resolvedConfig = typeof config === 'function' ? await config(env, argv) : config;\n\n    for (const opts of [resolvedConfig].flat()) {\n      const entries = [];\n\n      for (const loader of opts.module?.rules?.flatMap(resolveRuleSetDependencies) ?? []) {\n        inputs.add(toDeferResolve(loader.replace(/\\?.*/, '')));\n      }\n\n      for (const plugin of opts?.plugins ?? []) {\n        if (plugin && plugin.constructor.name === 'ProvidePlugin') {\n          const providePluginInstance = plugin as ProvidePlugin;\n          if (providePluginInstance.definitions) {\n            for (const values of Object.values(providePluginInstance.definitions)) {\n              const specifier = typeof values === 'string' ? values : values[0];\n              inputs.add(toDeferResolve(specifier));\n            }\n          }\n        }\n      }\n\n      if (typeof opts.entry === 'string') entries.push(opts.entry);\n      else if (Array.isArray(opts.entry)) entries.push(...opts.entry);\n      else if (typeof opts.entry === 'object') {\n        for (const entry of Object.values(opts.entry)) {\n          if (typeof entry === 'string') entries.push(entry);\n          else if (Array.isArray(entry)) entries.push(...entry);\n          else if (typeof entry === 'function') entries.push((entry as () => string)());\n          else if (entry && typeof entry === 'object' && 'filename' in entry) entries.push(entry['filename'] as string);\n        }\n      }\n\n      if (entries.length === 0 && opts.context) entries.push('./src/index');\n\n      for (const entry of entries) {\n        if (isInternal(entry) || entry.startsWith('#')) {\n          const dir = opts.context ? opts.context : cwd;\n          const input = isProduction\n            ? toDeferResolveProductionEntry(entry, { dir })\n            : toDeferResolveEntry(entry, { dir });\n          inputs.add(input);\n        } else {\n          inputs.add(toDeferResolve(entry));\n        }\n      }\n\n      const processAlias = (aliases: NonNullable<ResolveOptions['alias']>) => {\n        const addStar = (value: string) => (value.endsWith('*') ? value : join(value, '*').replace(/\\/\\*\\*$/, '/*'));\n        for (const [alias, value] of Object.entries(aliases)) {\n          if (!value) continue;\n          const prefixes = Array.isArray(value) ? value : [value];\n          if (alias.endsWith('$')) {\n            inputs.add(toAlias(alias.slice(0, -1), prefixes));\n          } else {\n            if (alias.length > 1) inputs.add(toAlias(alias, prefixes));\n            for (const prefix of prefixes) {\n              inputs.add(toAlias(addStar(alias), [addStar(toAbsolute(prefix, options.configFileDir))]));\n            }\n          }\n        }\n      };\n\n      if (opts.resolve?.alias) {\n        processAlias(opts.resolve.alias);\n      }\n      if (opts.resolveLoader?.alias) {\n        processAlias(opts.resolveLoader.alias);\n      }\n    }\n  }\n\n  return Array.from(inputs);\n};\n\nconst resolveConfig: ResolveConfig<WebpackConfig> = async (localConfig, options) => {\n  const inputs = await findWebpackDependenciesFromConfig(localConfig, options);\n  inputs.push(toDependency('webpack-cli', { optional: true }));\n  return inputs;\n};\n\nconst registerVisitors: RegisterVisitors = ({ ctx, registerVisitor }) => {\n  registerVisitor(createRequireContextVisitor(ctx));\n};\n\nconst isFilterTransitiveDependencies = true;\n\nconst args: Args = {\n  binaries: ['webpack', 'webpack-dev-server'],\n  config: true,\n  resolveInputs: (parsed: ParsedArgs) => {\n    const inputs: Input[] = [toDependency('webpack-cli')];\n    if (parsed._[0] === 'serve') inputs.push(toDependency('webpack-dev-server'));\n    return inputs;\n  },\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n  registerVisitors,\n  isFilterTransitiveDependencies,\n  args,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/webpack/types.ts",
    "content": "import type { Configuration, ProvidePlugin } from 'webpack';\n\ntype Mode = 'none' | 'development' | 'production';\nexport type Env = { production: boolean; mode: Mode };\nexport type Argv = { mode: Mode };\nexport type { ProvidePlugin };\n\ntype Configurations = Configuration | Configuration[];\n\nexport type WebpackConfig =\n  | Configurations\n  | ((env: Env, argv: Argv) => Configurations)\n  | (() => Promise<Configuration>);\n"
  },
  {
    "path": "packages/knip/src/plugins/webpack/visitors/requireContext.ts",
    "content": "import { IMPORT_FLAGS } from '../../../constants.ts';\nimport type { PluginVisitorContext, PluginVisitorObject } from '../../../types/config.ts';\nimport { _syncGlob } from '../../../util/glob.ts';\nimport { dirname, isAbsolute, join } from '../../../util/path.ts';\nimport { getStringValue, isStringLiteral } from '../../../typescript/visitors/helpers.ts';\n\nexport function createRequireContextVisitor(ctx: PluginVisitorContext): PluginVisitorObject {\n  return {\n    CallExpression(node) {\n      if (\n        node.callee.type !== 'MemberExpression' ||\n        node.callee.computed ||\n        node.callee.object.type !== 'Identifier' ||\n        node.callee.object.name !== 'require' ||\n        node.callee.property.name !== 'context' ||\n        node.arguments.length < 1 ||\n        !isStringLiteral(node.arguments[0])\n      )\n        return;\n\n      const dirArg = getStringValue(node.arguments[0])!;\n      const arg1 = node.arguments[1];\n      const recursive = !(arg1?.type === 'Literal' && 'value' in arg1 && arg1.value === false);\n      const regexArg = node.arguments[2];\n      const dir = dirname(ctx.filePath);\n      const baseDir = join(dir, dirArg);\n      const pattern = recursive ? '**/*' : '*';\n      const files = _syncGlob({ patterns: [pattern], cwd: baseDir });\n      const regex =\n        regexArg && 'regex' in regexArg ? new RegExp(regexArg.regex.pattern, regexArg.regex.flags) : undefined;\n\n      for (const f of files) {\n        const relPath = `./${f}`;\n        if (!regex || regex.test(relPath)) {\n          ctx.addImport(isAbsolute(f) ? f : join(baseDir, f), node.arguments[0].start, IMPORT_FLAGS.ENTRY);\n        }\n      }\n    },\n  };\n}\n"
  },
  {
    "path": "packages/knip/src/plugins/wireit/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { WireitConfig } from './types.ts';\n\n// https://github.com/google/wireit\n\nconst title = 'Wireit';\n\nconst enablers = ['wireit'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['package.json'];\n\nconst resolveConfig: ResolveConfig<WireitConfig> = (localConfig, options) => {\n  const scripts = Object.values(localConfig).flatMap(({ command: script }) => (script ? [script] : []));\n\n  const scriptDependencies = options.getInputsFromScripts(scripts);\n\n  return scriptDependencies;\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/wireit/types.ts",
    "content": "export type WireitConfig = Record<string, { command?: string }>;\n"
  },
  {
    "path": "packages/knip/src/plugins/wrangler/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { toProductionEntry } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { WranglerConfig } from './types.ts';\n\n// https://developers.cloudflare.com/workers/wrangler/configuration/\n\nconst title = 'Wrangler';\n\nconst enablers = ['wrangler'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['wrangler.{json,jsonc,toml}'];\n\nconst resolveConfig: ResolveConfig<WranglerConfig> = async config => {\n  return (config.main ? [config.main] : []).map(id => toProductionEntry(id));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/wrangler/types.ts",
    "content": "export type WranglerConfig = {\n  main?: string;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/xo/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { getInputs } from '../eslint/helpers.ts';\nimport type { XOConfig } from './types.ts';\n\n// https://github.com/xojs/xo#config\n// https://github.com/xojs/xo/blob/ee9f0a3d72d55df098fc321c4d54a1ea3804e226/lib/constants.js\n\nconst title = 'xo';\n\nconst enablers = ['xo'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst config = ['package.json', '.xo-config', '.xo-config.{js,cjs,json}', 'xo.config.{js,cjs}'];\n\nconst entry = ['.xo-config.{js,cjs}', 'xo.config.{js,cjs}'];\n\nconst resolveConfig: ResolveConfig<XOConfig> = async (config, options) => {\n  const inputs = getInputs(config, options);\n  return [...inputs];\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  entry,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/xo/types.ts",
    "content": "import type { ESLintConfigDeprecated } from '../eslint/types.ts';\n\nexport type XOConfig = ESLintConfigDeprecated & {\n  envs?: string[] | undefined;\n  globals?: string[] | undefined;\n  ignores?: string[] | undefined;\n  nodeVersion?: string | boolean | undefined;\n  prettier?: boolean | undefined;\n  printConfig?: string | undefined;\n  semicolon?: boolean | undefined;\n  space?: boolean | number | undefined;\n  webpack?: boolean | object | undefined;\n};\n"
  },
  {
    "path": "packages/knip/src/plugins/yarn/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { isFile } from '../../util/fs.ts';\nimport type { Input } from '../../util/input.ts';\nimport { toEntry } from '../../util/input.ts';\n\n// https://yarnpkg.com/features/constraints\n\nconst title = 'Yarn';\n\nconst enablers = 'This plugin is enabled when a `yarn.lock` file is found in the root folder.';\n\nconst isEnabled: IsPluginEnabled = async ({ cwd }) => isFile(cwd, 'yarn.lock');\n\nconst isRootOnly = true;\n\nconst config = ['.yarnrc.yml'];\n\nconst entry = ['yarn.config.cjs'];\n\ntype YarnConfig = {\n  plugins?: Array<string | { path?: string }>;\n  yarnPath?: string;\n};\n\nconst resolveConfig: ResolveConfig<YarnConfig> = config => {\n  const inputs: Input[] = entry.map(id => toEntry(id));\n\n  if (Array.isArray(config.plugins)) {\n    for (const plugin of config.plugins) {\n      if (typeof plugin === 'string') inputs.push(toEntry(plugin));\n      else if (typeof plugin.path === 'string') inputs.push(toEntry(plugin.path));\n    }\n  }\n\n  if (config.yarnPath) {\n    inputs.push(toEntry(config.yarnPath));\n  }\n\n  return inputs;\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  isRootOnly,\n  config,\n  entry,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/yorkie/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, ResolveConfig } from '../../types/config.ts';\nimport { type Input, toDependency } from '../../util/input.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport type { LintStagedConfig } from '../lint-staged/types.ts';\n\n// https://github.com/yyx990803/yorkie\n\nconst title = 'yorkie';\n\nconst enablers = ['yorkie'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst packageJsonPath = 'gitHooks';\n\nconst config = ['package.json'];\n\nconst resolveConfig: ResolveConfig<LintStagedConfig> = (config, options) => {\n  const inputs = new Set<Input>();\n\n  for (const script of Object.values(config).flat()) {\n    const scripts = [script].flat();\n    for (const identifier of options.getInputsFromScripts(scripts)) inputs.add(identifier);\n  }\n\n  // Looks like the idea is to have lint-staged installed too, so there are no refs to yorkie\n  return [toDependency('yorkie'), ...inputs];\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  packageJsonPath,\n  config,\n  resolveConfig,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/zx/index.ts",
    "content": "import type { IsPluginEnabled, Plugin, RegisterVisitors } from '../../types/config.ts';\nimport { hasDependency } from '../../util/plugin.ts';\nimport { createZxVisitor } from './visitors/zx.ts';\n\n// https://google.github.io/zx/\n\nconst title = 'zx';\n\nconst enablers = ['zx'];\n\nconst isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);\n\nconst registerVisitors: RegisterVisitors = ({ ctx, registerVisitor }) => {\n  registerVisitor(createZxVisitor(ctx));\n};\n\nconst plugin: Plugin = {\n  title,\n  enablers,\n  isEnabled,\n  registerVisitors,\n};\n\nexport default plugin;\n"
  },
  {
    "path": "packages/knip/src/plugins/zx/visitors/zx.ts",
    "content": "import type { PluginVisitorContext, PluginVisitorObject } from '../../../types/config.ts';\n\nexport function createZxVisitor(ctx: PluginVisitorContext): PluginVisitorObject {\n  return {\n    TaggedTemplateExpression(node) {\n      if (!ctx.sourceText.startsWith('#!/usr/bin/env zx')) return;\n      if (node.tag.type === 'Identifier' && node.tag.name === '$') {\n        for (const q of node.quasi.quasis) {\n          if (q.value.raw) ctx.addScript(q.value.raw);\n        }\n      }\n    },\n  };\n}\n"
  },
  {
    "path": "packages/knip/src/plugins.ts",
    "content": "import { parseArgs } from 'node:util';\nimport { Plugins } from './plugins/index.ts';\nimport type { Args } from './types/args.ts';\nimport type { Entries, PluginMap } from './types/config.ts';\nimport type { PluginName } from './types/PluginNames.ts';\nimport { timerify } from './util/Performance.ts';\n\nconst PMap: PluginMap = Plugins;\n\nconst { values } = parseArgs({ strict: false, options: { performance: { type: 'boolean' } } });\n\nconst isEnabled = !!values.performance;\n\nconst timerifyMethods = ['resolve', 'resolveConfig', 'resolveAST'] as const;\n\nconst PluginEntries = Object.entries(PMap) as Entries;\n\nif (isEnabled) {\n  for (const [, plugin] of PluginEntries) {\n    for (const method of timerifyMethods) {\n      // @ts-expect-error function signatures don't match but doesn't matter\n      if (method in plugin) plugin[method] = timerify(plugin[method], `${method} (${plugin.title})`);\n    }\n  }\n}\n\nconst pluginArgsMap = new Map(\n  PluginEntries.flatMap(([pluginName, plugin]) => {\n    if (!plugin.args) return [];\n    const item: [PluginName, Args] = [pluginName, plugin.args];\n    if (Array.isArray(plugin.args?.binaries)) return plugin.args.binaries.map(bin => [bin, item]);\n    return [[pluginName, item]];\n  })\n);\n\nexport { PMap as Plugins, PluginEntries, pluginArgsMap };\n"
  },
  {
    "path": "packages/knip/src/reporters/codeclimate.ts",
    "content": "import { createHash } from 'node:crypto';\nimport type * as codeclimate from 'codeclimate-types';\nimport type { Entries } from '../types/entries.ts';\nimport type { Issue, IssueSeverity, IssueSymbol, IssueType, Report, ReporterOptions } from '../types/issues.ts';\nimport { toRelative } from '../util/path.ts';\nimport { flattenIssues, getIssuePrefix, getIssueTypeTitle } from './util/util.ts';\n\nexport default async ({ report, issues, cwd }: ReporterOptions) => {\n  const entries: codeclimate.Issue[] = [];\n\n  for (const [type, isReportType] of Object.entries(report) as Entries<Report>) {\n    if (!isReportType) {\n      continue;\n    }\n\n    for (const issue of flattenIssues(issues[type])) {\n      const { filePath } = issue;\n\n      if (type === 'duplicates' && issue.symbols) {\n        entries.push(\n          ...issue.symbols.map<codeclimate.Issue>(symbol => ({\n            type: 'issue',\n            check_name: getIssueTypeTitle(type),\n            description: getSymbolDescription({ type: issue.type, symbol, parentSymbol: issue.parentSymbol }),\n            categories: ['Duplication'],\n            location: createLocation(filePath, cwd, symbol.line, symbol.col),\n            severity: convertSeverity(issue.severity),\n            fingerprint: createFingerprint(filePath, cwd, symbol.symbol),\n          }))\n        );\n      } else {\n        entries.push({\n          type: 'issue',\n          check_name: getIssueTypeTitle(type),\n          description: getIssueDescription(issue),\n          categories: ['Bug Risk'],\n          location: createLocation(filePath, cwd, issue.line, issue.col),\n          severity: convertSeverity(issue.severity),\n          fingerprint: createFingerprint(filePath, cwd, issue.symbol),\n        });\n      }\n    }\n  }\n\n  const output = JSON.stringify(entries);\n\n  // See: https://github.com/nodejs/node/issues/6379\n  // @ts-expect-error _handle is private\n  process.stdout._handle?.setBlocking?.(true);\n  process.stdout.write(`${output}\\n`);\n};\n\nfunction convertSeverity(severity?: IssueSeverity): codeclimate.Severity {\n  switch (severity) {\n    case 'error':\n      return 'major';\n    case 'warn':\n      return 'minor';\n    default:\n      return 'info';\n  }\n}\n\nfunction getIssueDescription({ type, symbol, symbols, parentSymbol }: Issue) {\n  const symbolDescription = symbols ? `${symbols.map(s => s.symbol).join(', ')}` : symbol;\n  return `${getIssuePrefix(type)}: ${symbolDescription}${parentSymbol ? ` (${parentSymbol})` : ''}`;\n}\n\nfunction getSymbolDescription({\n  type,\n  symbol,\n  parentSymbol,\n}: {\n  type: IssueType;\n  symbol: IssueSymbol;\n  parentSymbol?: string;\n}) {\n  return `${getIssuePrefix(type)}: ${symbol.symbol}${parentSymbol ? ` (${parentSymbol})` : ''}`;\n}\n\nfunction createLocation(filePath: string, cwd: string, line?: number, col?: number): codeclimate.Location {\n  if (col !== undefined) {\n    return {\n      path: toRelative(filePath, cwd),\n      positions: {\n        begin: {\n          line: line ?? 0,\n          column: col,\n        },\n        end: {\n          line: line ?? 0,\n          column: col,\n        },\n      },\n    };\n  }\n\n  return {\n    path: toRelative(filePath, cwd),\n    lines: {\n      begin: line ?? 0,\n      end: line ?? 0,\n    },\n  };\n}\n\nfunction createFingerprint(filePath: string, cwd: string, message: string): string {\n  const md5 = createHash('md5');\n\n  md5.update(toRelative(filePath, cwd));\n  md5.update(message);\n\n  return md5.digest('hex');\n}\n"
  },
  {
    "path": "packages/knip/src/reporters/codeowners.ts",
    "content": "import type { Entries } from '../types/entries.ts';\nimport type { Issue, ReporterOptions } from '../types/issues.ts';\nimport { createOwnershipEngine } from '../util/codeowners.ts';\nimport { relative, resolve } from '../util/path.ts';\nimport { getColoredTitle, getIssueLine, getIssueTypeTitle } from './util/util.ts';\n\ntype OwnedIssue = Issue & { owner: string };\n\ntype ExtraReporterOptions = {\n  path?: string;\n};\n\nconst logIssueRecord = (issues: OwnedIssue[], cwd: string) => {\n  const sortedByFilePath = issues.sort((a, b) => (a.owner < b.owner ? -1 : 1));\n  for (const { filePath, symbols, owner, parentSymbol } of sortedByFilePath) {\n    console.log(getIssueLine({ owner, filePath, symbols, parentSymbol }, cwd));\n  }\n};\n\nexport default ({ report, issues, isShowProgress, options, cwd }: ReporterOptions) => {\n  let opts: ExtraReporterOptions = {};\n  try {\n    opts = options ? JSON.parse(options) : opts;\n  } catch (error) {\n    console.error(error);\n  }\n  const codeownersFilePath = resolve(opts.path ?? '.github/CODEOWNERS');\n  const findOwners = createOwnershipEngine(codeownersFilePath);\n  const reportMultipleGroups = Object.values(report).filter(Boolean).length > 1;\n  const [dependenciesOwner = '[no-owner]'] = findOwners('package.json');\n  let totalIssues = 0;\n\n  const calcFileOwnership = (filePath: string) => findOwners(relative(cwd, filePath))[0] ?? dependenciesOwner;\n  const addOwner = (issue: Issue) => ({\n    ...issue,\n    owner: calcFileOwnership(issue.filePath),\n  });\n\n  for (const [reportType, isReportType] of Object.entries(report) as Entries<typeof report>) {\n    if (isReportType) {\n      const title = reportMultipleGroups && getIssueTypeTitle(reportType);\n\n      const issuesForType = Object.values(issues[reportType]).flatMap(issues => {\n        if (reportType === 'duplicates') return Object.values(issues).map(addOwner);\n        const symbols = Object.values(issues);\n        return addOwner({ ...symbols[0], symbols });\n      });\n\n      if (issuesForType.length > 0) {\n        if (totalIssues) console.log();\n        title && console.log(getColoredTitle(title, issuesForType.length));\n        logIssueRecord(issuesForType, cwd);\n      }\n\n      totalIssues = totalIssues + issuesForType.length;\n    }\n  }\n\n  if (totalIssues === 0 && isShowProgress) {\n    console.log('✂️  Excellent, Knip found no issues.');\n  }\n};\n"
  },
  {
    "path": "packages/knip/src/reporters/compact.ts",
    "content": "import type { Entries } from '../types/entries.ts';\nimport type { Issue, ReporterOptions } from '../types/issues.ts';\nimport { flattenIssues, getColoredTitle, getIssueLine, getIssueTypeTitle } from './util/util.ts';\n\nconst logIssueRecord = (issues: Issue[], cwd: string) => {\n  const sortedByFilePath = issues.sort((a, b) => (a.filePath > b.filePath ? 1 : -1));\n  for (const issue of sortedByFilePath) console.log(getIssueLine(issue, cwd));\n};\n\nexport default ({ report, issues, isShowProgress, cwd }: ReporterOptions) => {\n  const reportMultipleGroups = Object.values(report).filter(Boolean).length > 1;\n  let totalIssues = 0;\n\n  for (const [reportType, isReportType] of Object.entries(report) as Entries<typeof report>) {\n    if (isReportType) {\n      const title = reportMultipleGroups && getIssueTypeTitle(reportType);\n      const issuesForType =\n        reportType === 'duplicates'\n          ? flattenIssues(issues[reportType])\n          : Object.values(issues[reportType])\n              .filter(issues => Object.keys(issues).length > 0)\n              .map(issues => {\n                const items = Object.values(issues);\n                return { ...items[0], symbols: items };\n              });\n\n      if (issuesForType.length > 0) {\n        title && console.log(getColoredTitle(title, issuesForType.length));\n        logIssueRecord(issuesForType, cwd);\n      }\n\n      totalIssues = totalIssues + issuesForType.length;\n    }\n  }\n\n  if (totalIssues === 0 && isShowProgress) {\n    console.log('✂️  Excellent, Knip found no issues.');\n  }\n};\n"
  },
  {
    "path": "packages/knip/src/reporters/disclosure.ts",
    "content": "import type { Entries } from '../types/entries.ts';\nimport type { ReporterOptions } from '../types/issues.ts';\nimport { flattenIssues, getIssueTypeTitle, getTableForType } from './util/util.ts';\n\nexport default ({ report, issues, cwd }: ReporterOptions) => {\n  const reportMultipleGroups = Object.values(report).filter(Boolean).length > 1;\n\n  for (const [reportType, isReportType] of Object.entries(report) as Entries<typeof report>) {\n    if (isReportType) {\n      const title = reportMultipleGroups ? getIssueTypeTitle(reportType) : undefined;\n      const issuesForType = flattenIssues(issues[reportType]);\n      if (issuesForType.length > 0) {\n        console.log(`<details>\\n${title ? `<summary>${title} (${issuesForType.length})</summary>\\n` : ''}\\n\\`\\`\\``);\n        console.log(getTableForType(issuesForType, cwd, { isUseColors: false }).toString());\n        console.log('```\\n\\n</details>\\n');\n      }\n    }\n  }\n};\n"
  },
  {
    "path": "packages/knip/src/reporters/github-actions.ts",
    "content": "import { ISSUE_TYPE_TITLE } from '../constants.ts';\nimport type { Entries } from '../types/entries.ts';\nimport type { ReporterOptions } from '../types/issues.ts';\nimport { relative } from '../util/path.ts';\nimport { hintPrinters } from './util/configuration-hints.ts';\nimport { flattenIssues, getIssueTypeTitle } from './util/util.ts';\n\nconst createGitHubActionsLogger = () => {\n  const formatAnnotation = (\n    level: 'error' | 'warning' | 'notice',\n    message: string,\n    options: {\n      file: string;\n      startLine?: number;\n      endLine?: number;\n      startColumn?: number;\n      endColumn?: number;\n      title?: string;\n    }\n  ) => {\n    const params = [`file=${options.file}`];\n    if (options.startLine != null) params.push(`line=${options.startLine}`);\n    if (options.endLine != null) params.push(`endLine=${options.endLine}`);\n    if (options.startColumn != null) params.push(`col=${options.startColumn}`);\n    if (options.endColumn != null) params.push(`endColumn=${options.endColumn}`);\n    params.push(`title=✂️ Knip${options.title ? ` / ${options.title}` : ''}`);\n\n    const paramString = params.join(',');\n    console.log(`::${level} ${paramString}::${message}`);\n  };\n\n  return {\n    info: (message: string) => console.log(message),\n    error: (\n      message: string,\n      options: {\n        file: string;\n        startLine?: number;\n        endLine?: number;\n        startColumn?: number;\n        endColumn?: number;\n        title?: string;\n      }\n    ) => formatAnnotation('error', message, options),\n    warning: (\n      message: string,\n      options: {\n        file: string;\n        startLine?: number;\n        endLine?: number;\n        startColumn?: number;\n        endColumn?: number;\n        title?: string;\n      }\n    ) => formatAnnotation('warning', message, options),\n    notice: (\n      message: string,\n      options: {\n        file: string;\n        startLine?: number;\n        endLine?: number;\n        startColumn?: number;\n        endColumn?: number;\n        title?: string;\n      }\n    ) => formatAnnotation('notice', message, options),\n  };\n};\n\nexport default ({\n  report,\n  issues,\n  cwd,\n  configurationHints,\n  isDisableConfigHints,\n  isTreatConfigHintsAsErrors,\n  configFilePath,\n}: ReporterOptions) => {\n  const core = createGitHubActionsLogger();\n  const reportMultipleGroups = Object.values(report).filter(Boolean).length > 1;\n\n  for (const [reportType, isReportType] of Object.entries(report) as Entries<typeof report>) {\n    if (isReportType) {\n      const title = reportMultipleGroups && getIssueTypeTitle(reportType);\n\n      const issuesForType = flattenIssues(issues[reportType]);\n      issuesForType.sort((a, b) => a.filePath.localeCompare(b.filePath) || (a.line ?? 0) - (b.line ?? 0));\n      if (issuesForType.length > 0) {\n        title && core.info(`${title} (${issuesForType.length})`);\n\n        for (const issue of issuesForType) {\n          if (issue.isFixed || issue.severity === 'off') continue;\n\n          const log = issue.severity === 'error' ? core.error : core.warning;\n          const filePath = relative(cwd, issue.filePath);\n\n          const message = reportType === 'files' ? issue.symbol : `${issue.symbol} in ${filePath}`;\n\n          log(message, {\n            file: filePath,\n            startLine: issue.line ?? 1,\n            endLine: issue.line ?? 1,\n            startColumn: issue.col ?? 1,\n            endColumn: issue.col ?? 1,\n            title: ISSUE_TYPE_TITLE[issue.type],\n          });\n        }\n      }\n    }\n  }\n\n  if (!isDisableConfigHints && configurationHints.length > 0) {\n    const CONFIG_HINTS_TITLE = 'Configuration hints';\n    core.info(`${CONFIG_HINTS_TITLE} (${configurationHints.length})`);\n\n    const sortedHints = [...configurationHints].sort((a, b) => (a.filePath ?? '').localeCompare(b.filePath ?? ''));\n    for (const hint of sortedHints) {\n      const hintPrinter = hintPrinters.get(hint.type);\n      const message =\n        hintPrinter?.print({\n          ...hint,\n          filePath: hint.filePath ?? configFilePath ?? '',\n          configFilePath,\n        }) ?? '';\n\n      const file = hint.filePath\n        ? relative(cwd, hint.filePath)\n        : configFilePath\n          ? relative(cwd, configFilePath)\n          : 'knip.json';\n\n      const hintMessage = `${message}: ${hint.identifier} in ${file}`;\n\n      if (isTreatConfigHintsAsErrors) {\n        core.error(hintMessage, {\n          file,\n          startLine: 1,\n          endLine: 1,\n          startColumn: 1,\n          endColumn: 1,\n          title: CONFIG_HINTS_TITLE,\n        });\n      } else {\n        core.notice(hintMessage, {\n          file,\n          startLine: 1,\n          endLine: 1,\n          startColumn: 1,\n          endColumn: 1,\n          title: CONFIG_HINTS_TITLE,\n        });\n      }\n    }\n  }\n};\n"
  },
  {
    "path": "packages/knip/src/reporters/index.ts",
    "content": "import codeclimate from './codeclimate.ts';\nimport codeowners from './codeowners.ts';\nimport compact from './compact.ts';\nimport disclosure from './disclosure.ts';\nimport githubActions from './github-actions.ts';\nimport json from './json.ts';\nimport markdown from './markdown.ts';\nimport symbols from './symbols.ts';\n\nexport default {\n  symbols,\n  compact,\n  codeowners,\n  disclosure,\n  codeclimate,\n  json,\n  markdown,\n  'github-actions': githubActions,\n};\n"
  },
  {
    "path": "packages/knip/src/reporters/json.ts",
    "content": "import type { Entries } from '../types/entries.ts';\nimport type { IssueRecords, Report, ReporterOptions } from '../types/issues.ts';\nimport { createOwnershipEngine } from '../util/codeowners.ts';\nimport { isFile } from '../util/fs.ts';\nimport { relative, resolve } from '../util/path.ts';\nimport { convert, flattenIssues } from './util/util.ts';\n\ntype ExtraReporterOptions = {\n  codeowners?: string;\n};\n\ninterface BaseItem {\n  name: string;\n}\n\ninterface Item extends BaseItem {\n  namespace?: string;\n  pos?: number;\n  line?: number;\n  col?: number;\n}\n\ntype BaseItems = Array<BaseItem>;\ntype Items = Array<Item>;\n\ntype Row = {\n  file: string;\n  owners?: BaseItems;\n  binaries?: BaseItems;\n  catalog?: Items;\n  dependencies?: Items;\n  devDependencies?: Items;\n  duplicates?: Array<Items>;\n  enumMembers?: Items;\n  exports?: Items;\n  files?: Items;\n  namespaceMembers?: Items;\n  nsExports?: Items;\n  nsTypes?: Items;\n  optionalPeerDependencies?: Items;\n  types?: Items;\n  unlisted?: BaseItems;\n  unresolved?: Items;\n};\n\nexport default async ({ report, issues, options, cwd }: ReporterOptions) => {\n  let opts: ExtraReporterOptions = {};\n  try {\n    opts = options ? JSON.parse(options) : opts;\n  } catch (error) {\n    console.error(error);\n  }\n\n  const json: Record<string, Row> = {};\n  const codeownersFilePath = resolve(opts.codeowners ?? '.github/CODEOWNERS');\n  const findOwners = isFile(codeownersFilePath) && createOwnershipEngine(codeownersFilePath);\n\n  const initRow = (filePath: string) => {\n    const file = relative(cwd, filePath);\n    const row: Row = {\n      file,\n      ...(findOwners && { owners: findOwners(file).map(name => ({ name })) }),\n      ...(report.binaries && { binaries: [] }),\n      ...(report.catalog && { catalog: [] }),\n      ...(report.dependencies && { dependencies: [] }),\n      ...(report.devDependencies && { devDependencies: [] }),\n      ...(report.duplicates && { duplicates: [] }),\n      ...(report.enumMembers && { enumMembers: [] }),\n      ...(report.exports && { exports: [] }),\n      ...(report.files && { files: [] }),\n      ...(report.namespaceMembers && { namespaceMembers: [] }),\n      ...(report.nsExports && { nsExports: [] }),\n      ...(report.nsTypes && { nsTypes: [] }),\n      ...(report.optionalPeerDependencies && { optionalPeerDependencies: [] }),\n      ...(report.types && { types: [] }),\n      ...(report.unlisted && { unlisted: [] }),\n      ...(report.unresolved && { unresolved: [] }),\n    };\n    return row;\n  };\n\n  for (const [type, isReportType] of Object.entries(report) as Entries<Report>) {\n    if (isReportType) {\n      for (const issue of flattenIssues(issues[type] as IssueRecords)) {\n        const { filePath, symbol, symbols } = issue;\n        json[filePath] = json[filePath] ?? initRow(filePath);\n        if (type === 'duplicates') {\n          symbols && json[filePath][type]?.push(symbols.map(convert));\n        } else if (type === 'binaries') {\n          json[filePath][type]?.push({ name: symbol });\n        } else {\n          json[filePath][type]?.push(convert(issue));\n        }\n      }\n    }\n  }\n\n  const output = JSON.stringify({\n    issues: Object.values(json),\n  });\n\n  // See: https://github.com/nodejs/node/issues/6379\n  // @ts-expect-error _handle is private\n  process.stdout._handle?.setBlocking?.(true);\n  process.stdout.write(`${output}\\n`);\n};\n"
  },
  {
    "path": "packages/knip/src/reporters/markdown.ts",
    "content": "import type { Entries } from '../types/entries.ts';\nimport type { Issue, ReporterOptions } from '../types/issues.ts';\nimport { relative } from '../util/path.ts';\nimport { flattenIssues, getIssueTypeTitle } from './util/util.ts';\n\nexport default ({ report, issues, cwd }: ReporterOptions) => {\n  console.log('# Knip report\\n');\n\n  const getFilePath = (issue: Issue) => {\n    if (!(issue.line && issue.col)) return relative(cwd, issue.filePath);\n    return `${relative(cwd, issue.filePath)}:${issue.line}:${issue.col}`;\n  };\n  const sortLongestSymbol = (a: Issue, b: Issue) => b.symbol.length - a.symbol.length;\n  const sortLongestFilePath = (a: Issue, b: Issue) => getFilePath(b).length - getFilePath(a).length;\n\n  for (const [reportType, isReportType] of Object.entries(report) as Entries<typeof report>) {\n    if (isReportType) {\n      const title = getIssueTypeTitle(reportType);\n      const issuesForType = flattenIssues(issues[reportType]);\n\n      if (issuesForType.length > 0) {\n        console.log(`## ${title} (${issuesForType.length})\\n`);\n        const longestSymbol = issuesForType.sort(sortLongestSymbol)[0].symbol.length;\n        const sortedByFilePath = issuesForType.sort(sortLongestFilePath);\n        const longestFilePath = getFilePath(sortedByFilePath[0]).length;\n\n        console.log(`| ${'Name'.padEnd(longestSymbol)} | ${'Location'.padEnd(longestFilePath)} | Severity |`);\n        console.log(`| :${'-'.repeat(longestSymbol - 1)} | :${'-'.repeat(longestFilePath - 1)} | :------- |`);\n        for (const issue of sortedByFilePath) {\n          console.log(\n            `| ${issue.symbol.padEnd(longestSymbol)} | ${getFilePath(issue).padEnd(longestFilePath)} | ${(issue.severity ?? '').padEnd(8)} |`\n          );\n        }\n        console.log('');\n      }\n    }\n  }\n};\n"
  },
  {
    "path": "packages/knip/src/reporters/symbols.ts",
    "content": "import type { Entries } from '../types/entries.ts';\nimport type { ReporterOptions } from '../types/issues.ts';\nimport { printConfigurationHints } from './util/configuration-hints.ts';\nimport { dim, flattenIssues, getColoredTitle, getIssueTypeTitle, getTableForType } from './util/util.ts';\n\nexport default (options: ReporterOptions) => {\n  const { report, issues, isDisableConfigHints, isShowProgress } = options;\n  const reportMultipleGroups = Object.values(report).filter(Boolean).length > 1;\n  let totalIssues = 0;\n\n  for (const [reportType, isReportType] of Object.entries(report) as Entries<typeof report>) {\n    if (isReportType) {\n      const title = reportMultipleGroups && getIssueTypeTitle(reportType);\n\n      const issuesForType = flattenIssues(issues[reportType]);\n      if (issuesForType.length > 0) {\n        title && console.log(getColoredTitle(title, issuesForType.length));\n        const issues =\n          typeof options.maxShowIssues === 'number'\n            ? Array.from(issuesForType).slice(0, options.maxShowIssues)\n            : issuesForType;\n        if (issues.length > 0) console.log(getTableForType(issues, options.cwd).toString());\n        if (issues.length !== issuesForType.length)\n          console.log(dim(`…${issuesForType.length - issues.length} more items`));\n        totalIssues = totalIssues + issuesForType.length;\n      }\n    }\n  }\n\n  if (!isDisableConfigHints) {\n    printConfigurationHints(options);\n  }\n\n  if (\n    totalIssues === 0 &&\n    isShowProgress &&\n    (!options.isTreatConfigHintsAsErrors || options.configurationHints.length === 0)\n  ) {\n    console.log('✂️  Excellent, Knip found no issues.');\n  }\n};\n"
  },
  {
    "path": "packages/knip/src/reporters/trace.ts",
    "content": "import pc from 'picocolors';\nimport type { GraphExplorer } from '../graph-explorer/explorer.ts';\nimport type { ExportsTreeNode } from '../graph-explorer/operations/build-exports-tree.ts';\nimport type { ModuleGraph } from '../types/module-graph.ts';\nimport type { MainOptions } from '../util/create-options.ts';\nimport { toRelative } from '../util/path.ts';\nimport { toRegexOrString } from '../util/regex.ts';\nimport { Table } from '../util/table.ts';\nimport { formatTrace } from '../util/trace.ts';\nimport type { WorkspaceFilePathFilter } from '../util/workspace-file-filter.ts';\n\ninterface TraceReporterOptions {\n  graph: ModuleGraph;\n  explorer: GraphExplorer;\n  options: MainOptions;\n  workspaceFilePathFilter: WorkspaceFilePathFilter;\n}\n\nexport default ({ graph, explorer, options, workspaceFilePathFilter }: TraceReporterOptions) => {\n  if (options.traceDependency) {\n    const pattern = toRegexOrString(options.traceDependency);\n    const toRel = (path: string) => toRelative(path, options.cwd);\n    const table = new Table({ truncateStart: ['filePath'] });\n    const seen = new Set<string>();\n    for (const [packageName, { imports }] of explorer.getDependencyUsage(pattern)) {\n      const filtered = imports.filter(i => workspaceFilePathFilter(i.filePath));\n      filtered.sort((a, b) => a.filePath.localeCompare(b.filePath) || (a.line ?? 0) - (b.line ?? 0));\n      for (const _import of filtered) {\n        const pos = _import.line ? `:${_import.line}:${_import.col}` : '';\n        const key = `${_import.filePath}${pos}:${packageName}`;\n        if (seen.has(key)) continue;\n        seen.add(key);\n        table.row();\n        table.cell('filePath', pc.whiteBright(`${toRel(_import.filePath)}${pos}`));\n        table.cell('package', pc.cyanBright(packageName));\n      }\n    }\n    for (const line of table.toRows()) console.log(line);\n  } else {\n    const nodes = explorer.buildExportsTree({ filePath: options.traceFile, identifier: options.traceExport });\n    nodes.sort((a, b) => a.filePath.localeCompare(b.filePath) || a.identifier.localeCompare(b.identifier));\n    const toRel = (path: string) => toRelative(path, options.cwd);\n    const isReferenced = (node: ExportsTreeNode) => {\n      if (explorer.isReferenced(node.filePath, node.identifier, { includeEntryExports: false })[0]) return true;\n      if (explorer.hasStrictlyNsReferences(node.filePath, node.identifier)[0]) return true;\n      return !!graph.get(node.filePath)?.exports.get(node.identifier)?.hasRefsInFile;\n    };\n    for (const node of nodes) console.log(formatTrace(node, toRel, isReferenced(node)));\n  }\n};\n"
  },
  {
    "path": "packages/knip/src/reporters/util/configuration-hints.ts",
    "content": "import type { Results } from '../../run.ts';\nimport type { ConfigurationHint, ConfigurationHintType, ReporterOptions } from '../../types/issues.ts';\nimport { relative, toRelative } from '../../util/path.ts';\nimport { Table } from '../../util/table.ts';\nimport { byPathDepth } from '../../util/workspace.ts';\nimport { bright, dim, getColoredTitle, getDimmedTitle } from './util.ts';\n\ninterface PrintHintOptions {\n  type: ConfigurationHintType;\n  identifier: string | RegExp;\n  filePath: string;\n  configFilePath?: string;\n  workspaceName?: string;\n  size?: number;\n}\n\ntype TableRow = ConfigurationHint & { message: string };\n\nconst getWorkspaceName = (hint: ConfigurationHint) =>\n  hint.workspaceName &&\n  hint.workspaceName !== '.' &&\n  hint.type !== 'top-level-unconfigured' &&\n  hint.type !== 'workspace-unconfigured' &&\n  hint.type !== 'package-entry'\n    ? hint.workspaceName\n    : '';\n\nconst getIdentifier = (hint: ConfigurationHint) => {\n  if (hint.identifier === '.') return `. ${dim('(root)')}`;\n  if (hint.identifier instanceof RegExp) return hint.identifier.source.replaceAll('\\\\/', '/');\n  return hint.identifier.toString();\n};\n\nconst getTableForHints = (hints: TableRow[]) => {\n  const table = new Table({ truncateStart: ['identifier', 'workspace', 'filePath'] });\n  for (const hint of hints) {\n    table.row();\n    table.cell('identifier', getIdentifier(hint));\n    table.cell('workspace', getWorkspaceName(hint));\n    table.cell('filePath', hint.filePath);\n    table.cell('description', dim(hint.message));\n  }\n  return table;\n};\n\nconst type = (id: ConfigurationHintType) => bright(id.split('-').at(0));\n\nconst unused = (options: PrintHintOptions) => `Remove from ${type(options.type)}`;\nconst empty = (options: PrintHintOptions) => `Refine ${type(options.type)} pattern (no matches)`;\nconst remove = (options: PrintHintOptions) => `Remove redundant ${type(options.type)} pattern`;\nconst topLevel = (options: PrintHintOptions) =>\n  `Remove, or move unused top-level ${type(options.type)} to one of ${bright('\"workspaces\"')}`;\nconst add = (options: PrintHintOptions) =>\n  options.configFilePath\n    ? `Add ${bright('entry')} and/or refine ${bright('project')} files (${options.size} unused files)`\n    : `Create ${bright('knip.json')} configuration file, and add ${bright('entry')} and/or refine ${bright('project')} files (${options.size} unused files)`;\nconst addWorkspace = (options: PrintHintOptions) =>\n  options.configFilePath\n    ? `Add ${bright('entry')} and/or refine ${bright('project')} files in ${bright(`workspaces[\"${options.workspaceName}\"]`)} (${options.size} unused files)`\n    : `Create ${bright('knip.json')} configuration file with ${bright(`workspaces[\"${options.workspaceName}\"]`)} object (${options.size} unused files)`;\n\nconst packageEntry = () => 'Package entry file not found';\n\nconst hintPrinters = new Map<ConfigurationHintType, { print: (options: PrintHintOptions) => string }>([\n  ['ignore', { print: unused }],\n  ['ignoreFiles', { print: unused }],\n  ['ignoreBinaries', { print: unused }],\n  ['ignoreDependencies', { print: unused }],\n  ['ignoreUnresolved', { print: unused }],\n  ['ignoreWorkspaces', { print: unused }],\n  ['entry-empty', { print: empty }],\n  ['project-empty', { print: empty }],\n  ['entry-redundant', { print: remove }],\n  ['project-redundant', { print: remove }],\n  ['top-level-unconfigured', { print: add }],\n  ['workspace-unconfigured', { print: addWorkspace }],\n  ['entry-top-level', { print: topLevel }],\n  ['project-top-level', { print: topLevel }],\n  ['package-entry', { print: packageEntry }],\n]);\n\nexport { hintPrinters };\n\nconst hintTypesOrder: ConfigurationHintType[][] = [\n  ['top-level-unconfigured', 'workspace-unconfigured'],\n  ['entry-top-level', 'project-top-level'],\n  ['ignore', 'ignoreFiles'],\n  ['ignoreWorkspaces'],\n  ['ignoreDependencies'],\n  ['ignoreBinaries'],\n  ['ignoreUnresolved'],\n  ['entry-empty', 'project-empty', 'entry-redundant', 'project-redundant'],\n  ['package-entry'],\n];\n\ninterface ProcessedHint extends ConfigurationHint {\n  message: string;\n}\n\nexport const finalizeConfigurationHints = (\n  results: Results,\n  options: { cwd: string; configFilePath?: string }\n): ProcessedHint[] => {\n  if (results.counters.files > 20) {\n    const workspaces = results.includedWorkspaceDirs\n      .sort(byPathDepth)\n      .reverse()\n      .map(dir => ({ dir, size: 0 }));\n\n    for (const issues of Object.values(results.issues.files)) {\n      for (const issue of Object.values(issues)) {\n        const workspace = workspaces.find(ws => issue.filePath.startsWith(ws.dir));\n        if (workspace) workspace.size++;\n      }\n    }\n\n    if (workspaces.length === 1) {\n      results.configurationHints.push({ type: 'top-level-unconfigured', identifier: '.', size: workspaces[0].size });\n    } else {\n      const topWorkspaces = workspaces.sort((a, b) => b.size - a.size).filter(ws => ws.size > 1);\n      for (const { dir, size } of topWorkspaces) {\n        const identifier = toRelative(dir, options.cwd);\n        results.configurationHints.push({\n          type: 'workspace-unconfigured',\n          workspaceName: identifier,\n          identifier,\n          size,\n        });\n      }\n    }\n  }\n\n  const hintsByType = new Map<ConfigurationHintType, ConfigurationHint[]>();\n  for (const hint of results.configurationHints) {\n    const hints = hintsByType.get(hint.type) ?? [];\n    hintsByType.set(hint.type, [...hints, hint]);\n  }\n\n  return hintTypesOrder.flatMap(hintTypes =>\n    hintTypes.flatMap(hintType => {\n      const hints = hintsByType.get(hintType) ?? [];\n      const topHints = hints.length > 10 ? Array.from(hints).slice(0, 10) : hints;\n      const row = topHints.map(hint => {\n        hint.filePath = relative(options.cwd, hint.filePath ?? options.configFilePath ?? '');\n        const hintPrinter = hintPrinters.get(hint.type);\n        // @ts-expect-error\n        const message = hintPrinter ? hintPrinter.print({ ...hint, configFilePath: options.configFilePath }) : '';\n        return { ...hint, message };\n      });\n      if (hints.length !== topHints.length) {\n        const more = hints.length - topHints.length;\n        row.push({ type: hintType, identifier: `...${more} more similar hints`, filePath: '', message: '' });\n      }\n      return row;\n    })\n  );\n};\n\nexport const printConfigurationHints = ({\n  cwd,\n  counters,\n  issues,\n  tagHints,\n  configurationHints,\n  enabledPlugins,\n  isTreatConfigHintsAsErrors,\n  includedWorkspaceDirs,\n  selectedWorkspaces,\n  configFilePath,\n}: ReporterOptions) => {\n  const rows = finalizeConfigurationHints(\n    { issues, counters, configurationHints, tagHints, includedWorkspaceDirs, selectedWorkspaces, enabledPlugins },\n    { cwd, configFilePath }\n  );\n\n  if (rows.length > 0) {\n    const getTitle = isTreatConfigHintsAsErrors ? getColoredTitle : getDimmedTitle;\n    console.log(getTitle('Configuration hints', configurationHints.length));\n    console.warn(getTableForHints(rows).toString());\n  }\n\n  if (tagHints.size > 0) {\n    console.log(getDimmedTitle('Tag hints', tagHints.size));\n    for (const hint of tagHints) {\n      const { filePath, identifier, tagName } = hint;\n      const message = `Unused tag in ${toRelative(filePath, cwd)}:`;\n      console.warn(dim(message), `${identifier} → ${tagName}`);\n    }\n  }\n};\n"
  },
  {
    "path": "packages/knip/src/reporters/util/util.ts",
    "content": "import picocolors from 'picocolors';\nimport { ISSUE_TYPE_TITLE, SYMBOL_TYPE } from '../../constants.ts';\nimport type { Issue, IssueRecords, IssueSeverity, IssueSymbol, IssueType } from '../../types/issues.ts';\nimport { relative } from '../../util/path.ts';\nimport { Table } from '../../util/table.ts';\n\nconst plain = (text: string) => text;\nexport const dim = picocolors.gray;\nexport const bright = picocolors.whiteBright;\nconst yellow = picocolors.yellow;\n\nexport const getIssueTypeTitle = (reportType: keyof typeof ISSUE_TYPE_TITLE) => ISSUE_TYPE_TITLE[reportType];\n\nexport const getColoredTitle = (title: string, count: number) =>\n  `${picocolors.yellowBright(picocolors.underline(title))} (${count})`;\n\nexport const getDimmedTitle = (title: string, count: number) =>\n  `${yellow(`${picocolors.underline(title)} (${count})`)}`;\n\ntype LogIssueLine = {\n  owner?: string;\n  filePath: string;\n  symbols?: IssueSymbol[];\n  parentSymbol?: string;\n  severity?: IssueSeverity;\n};\n\nexport const getIssueLine = ({ owner, filePath, symbols, parentSymbol, severity }: LogIssueLine, cwd: string) => {\n  const symbol = symbols ? `: ${symbols.map(s => s.symbol).join(', ')}` : '';\n  const parent = parentSymbol ? ` (${parentSymbol})` : '';\n  const print = severity === 'warn' ? dim : plain;\n  return `${owner ? `${picocolors.cyan(owner)} ` : ''}${print(`${relative(cwd, filePath)}${symbol}${parent}`)}`;\n};\n\nexport const convert = (issue: Issue | IssueSymbol) => ({\n  namespace: 'parentSymbol' in issue ? issue.parentSymbol : undefined,\n  name: issue.symbol,\n  line: issue.line,\n  col: issue.col,\n  pos: issue.pos,\n});\n\nconst sortByPos = (a: Issue, b: Issue) => {\n  const [filePathA, rowA, colA] = a.filePath.split(':');\n  const [filePathB, rowB, colB] = b.filePath.split(':');\n  return filePathA === filePathB\n    ? Number(rowA) === Number(rowB)\n      ? Number(colA) - Number(colB)\n      : Number(rowA) - Number(rowB)\n    : filePathA.localeCompare(filePathB);\n};\n\nconst highlightSymbol =\n  (issue: Issue) =>\n  (_: unknown): string => {\n    if (issue.specifier && issue.specifier !== issue.symbol && issue.specifier.includes(issue.symbol)) {\n      const parts = issue.specifier.split(issue.symbol);\n      const rest = parts.slice(1).join('');\n      return [dim(parts[0]), bright(issue.symbol), dim(rest)].join('');\n    }\n    return issue.symbol;\n  };\n\nexport const getTableForType = (\n  issues: Issue[],\n  cwd: string,\n  options: { isUseColors?: boolean } = { isUseColors: true }\n) => {\n  const table = new Table({ truncateStart: ['filePath'], noTruncate: ['symbolType'] });\n\n  for (const issue of issues.sort(sortByPos)) {\n    table.row();\n\n    const print = options.isUseColors && (issue.isFixed || issue.severity === 'warn') ? dim : plain;\n\n    const isFileIssue = issue.type === 'files';\n    const symbol = issue.symbols ? issue.symbols.map(s => s.symbol).join(', ') : issue.symbol;\n    if (!isFileIssue) table.cell('symbol', print(symbol), options.isUseColors ? highlightSymbol(issue) : () => symbol);\n\n    table.cell('parentSymbol', issue.parentSymbol && print(issue.parentSymbol));\n    table.cell('symbolType', issue.symbolType && issue.symbolType !== SYMBOL_TYPE.UNKNOWN && print(issue.symbolType));\n\n    const pos = issue.line === undefined ? '' : `:${issue.line}${issue.col === undefined ? '' : `:${issue.col}`}`;\n    const cell = isFileIssue ? relative(cwd, symbol) : `${relative(cwd, issue.filePath)}${pos}`;\n    table.cell('filePath', print(cell));\n\n    table.cell('fixed', issue.isFixed && print('(removed)'));\n  }\n\n  return table;\n};\n\nexport const flattenIssues = (issues: IssueRecords): Issue[] => Object.values(issues).flatMap(Object.values);\n\nexport const getIssuePrefix = (type: IssueType) => ISSUE_TYPE_TITLE[type].replace(/ies$/, 'y').replace(/s$/, '');\n"
  },
  {
    "path": "packages/knip/src/reporters/watch.ts",
    "content": "import picocolors from 'picocolors';\nimport type { ConsoleStreamer } from '../ConsoleStreamer.ts';\nimport type { Entries } from '../types/entries.ts';\nimport type { Issues } from '../types/issues.ts';\nimport type { MainOptions } from '../util/create-options.ts';\nimport { perfObserver } from '../util/Performance.ts';\nimport { prettyMilliseconds } from '../util/string.ts';\nimport { flattenIssues, getIssueTypeTitle, getTableForType } from './util/util.ts';\n\ninterface WatchReporter {\n  issues: Issues;\n  streamer: ConsoleStreamer;\n  duration?: number;\n  size: number;\n}\n\nexport default (options: MainOptions, { issues, streamer, duration, size }: WatchReporter) => {\n  const reportMultipleGroups = Object.values(options.includedIssueTypes).filter(Boolean).length > 1;\n  let totalIssues = 0;\n  const lines: string[] = [];\n\n  for (const [reportType, isReportType] of Object.entries(options.includedIssueTypes) as Entries<\n    typeof options.includedIssueTypes\n  >) {\n    if (isReportType) {\n      const title = reportMultipleGroups && getIssueTypeTitle(reportType);\n      const issuesForType = flattenIssues(issues[reportType]);\n\n      if (issuesForType.length > 0) {\n        if (title) {\n          lines.push(`${picocolors.yellowBright(picocolors.underline(title))} (${issuesForType.length})`);\n        }\n        lines.push(...getTableForType(issuesForType, options.cwd).toRows());\n      }\n\n      totalIssues = totalIssues + issuesForType.length;\n    }\n  }\n\n  const mem = perfObserver.getCurrentMemUsageInMb();\n  const ms = duration ?? perfObserver.getCurrentDurationInMs();\n  const summary = `${size} files (${prettyMilliseconds(ms)} • ${mem}MB)`;\n\n  const messages =\n    totalIssues === 0\n      ? ['✂️  Excellent, Knip found no issues.', '', picocolors.gray(summary)]\n      : [...lines, '', picocolors.gray(summary)];\n\n  if (options.isDebug) console.log(messages.join('\\n'));\n  else streamer.cast(messages);\n};\n"
  },
  {
    "path": "packages/knip/src/run.ts",
    "content": "import { watch } from 'node:fs';\nimport { CatalogCounselor } from './CatalogCounselor.ts';\nimport { ConfigurationChief } from './ConfigurationChief.ts';\nimport { ConsoleStreamer } from './ConsoleStreamer.ts';\nimport { DependencyDeputy } from './DependencyDeputy.ts';\nimport { analyze } from './graph/analyze.ts';\nimport { build } from './graph/build.ts';\nimport { IssueCollector } from './IssueCollector.ts';\nimport { ProjectPrincipal } from './ProjectPrincipal.ts';\nimport watchReporter from './reporters/watch.ts';\nimport type { MainOptions } from './util/create-options.ts';\nimport { debugLogObject } from './util/debug.ts';\nimport { getGitIgnoredHandler } from './util/glob-core.ts';\nimport { getModuleSourcePathHandler } from './util/to-source-path.ts';\nimport { getSessionHandler, type OnFileChange, type SessionHandler } from './util/watch.ts';\n\nexport type Results = Awaited<ReturnType<typeof run>>['results'];\n\nexport const run = async (options: MainOptions) => {\n  debugLogObject('*', 'Unresolved configuration', options);\n  debugLogObject('*', 'Included issue types', options.includedIssueTypes);\n\n  const chief = new ConfigurationChief(options);\n  const deputy = new DependencyDeputy(options);\n  const streamer = new ConsoleStreamer(options);\n  const collector = new IssueCollector(options);\n  const counselor = new CatalogCounselor(options);\n\n  streamer.cast('Reading workspace configuration');\n\n  const workspaces = await chief.getWorkspaces();\n  const isGitIgnored = await getGitIgnoredHandler(options, new Set(workspaces.map(w => w.dir)));\n\n  const toSourceFilePath = getModuleSourcePathHandler(chief);\n  const principal = new ProjectPrincipal(options, toSourceFilePath);\n\n  collector.setWorkspaceFilter(chief.workspaceFilePathFilter);\n  collector.setIgnoreIssues(chief.config.ignoreIssues);\n\n  debugLogObject('*', 'Included workspaces', () => workspaces.map(w => w.pkgName));\n  debugLogObject('*', 'Included workspace configs', () =>\n    workspaces.map(w => ({ pkgName: w.pkgName, name: w.name, config: w.config, ancestors: w.ancestors }))\n  );\n\n  const { graph, entryPaths, analyzedFiles, unreferencedFiles, analyzeSourceFile, enabledPluginsStore } = await build({\n    chief,\n    collector,\n    counselor,\n    deputy,\n    principal,\n    isGitIgnored,\n    streamer,\n    workspaces,\n    options,\n  });\n\n  const reAnalyze = await analyze({\n    analyzedFiles,\n    counselor,\n    chief,\n    collector,\n    deputy,\n    entryPaths,\n    graph,\n    streamer,\n    unreferencedFiles,\n    options,\n  });\n\n  let session: SessionHandler | undefined;\n\n  if (options.isWatch || options.isSession) {\n    const isIgnored = (filePath: string) =>\n      (!!options.cacheLocation && filePath.startsWith(options.cacheLocation)) ||\n      filePath.includes('/.git/') ||\n      isGitIgnored(filePath);\n\n    const onFileChange: OnFileChange | undefined = options.isWatch\n      ? ({ issues, duration }) => watchReporter(options, { issues, streamer, size: analyzedFiles.size, duration })\n      : undefined;\n\n    session = await getSessionHandler(options, {\n      analyzedFiles,\n      analyzeSourceFile,\n      chief,\n      collector,\n      analyze: reAnalyze,\n      principal,\n      graph,\n      isIgnored,\n      onFileChange,\n      unreferencedFiles,\n      entryPaths,\n    });\n\n    if (options.isWatch) watch('.', { recursive: true }, session.listener);\n  }\n\n  const { issues, counters, tagHints, configurationHints } = collector.getIssues();\n\n  if (!options.isWatch) streamer.clear();\n\n  return {\n    results: {\n      issues,\n      counters,\n      tagHints,\n      configurationHints,\n      selectedWorkspaces: chief.selectedWorkspaces ? Array.from(chief.selectedWorkspaces) : undefined,\n      includedWorkspaceDirs: Array.from(chief.workspacesByDir.keys()),\n      enabledPlugins: Object.fromEntries(enabledPluginsStore),\n    },\n    session,\n    streamer,\n  };\n};\n"
  },
  {
    "path": "packages/knip/src/schema/configuration.ts",
    "content": "import { z } from 'zod/mini';\nimport { SYMBOL_TYPE } from '../constants.ts';\nimport { globSchema, pluginsSchema } from './plugins.ts';\n\nconst pathsSchema = z.record(z.string(), z.array(z.string()));\n\ntype SyncCompiler = (filename: string, contents: string) => string;\ntype AsyncCompiler = (filename: string, contents: string) => Promise<string>;\n\nconst syncCompilerSchema = z.union([z.literal(true), z.custom<SyncCompiler>()]);\nconst asyncCompilerSchema = z.custom<AsyncCompiler>();\nconst compilerSchema = z.union([syncCompilerSchema, asyncCompilerSchema]);\nconst compilersSchema = z.record(z.string(), compilerSchema);\n\nconst stringOrRegexSchema = z.array(z.union([z.string(), z.instanceof(RegExp)]));\n\nconst issueTypeSchema = z.union([\n  z.literal('files'),\n  z.literal('dependencies'),\n  z.literal('devDependencies'),\n  z.literal('optionalPeerDependencies'),\n  z.literal('unlisted'),\n  z.literal('binaries'),\n  z.literal('unresolved'),\n  z.literal('exports'),\n  z.literal('types'),\n  z.literal('nsExports'),\n  z.literal('nsTypes'),\n  z.literal('duplicates'),\n  z.literal('enumMembers'),\n  z.literal('namespaceMembers'),\n  z.literal('catalog'),\n]);\n\nconst rulesSchema = z.partialRecord(issueTypeSchema, z.enum(['error', 'warn', 'off']));\n\nconst ignorableSymbolTypes = Object.values(SYMBOL_TYPE).filter(type => type !== 'unknown');\n\nconst ignoreExportsUsedInFileObjectSchema = z.strictObject(\n  Object.fromEntries(ignorableSymbolTypes.map(type => [type, z.optional(z.boolean())]))\n);\n\nconst ignoreExportsUsedInFileSchema = z.union([z.boolean(), ignoreExportsUsedInFileObjectSchema]);\n\nconst ignoreIssuesSchema = z.record(z.string(), z.array(issueTypeSchema));\n\nconst rootConfigurationSchema = z.object({\n  /**\n   * A `$schema` field is a URL that you put at the top of your JSON file. This\n   * allows you to get red squiggly lines inside of your IDE when you make a typo or\n   * provide an otherwise invalid configuration option.\n   *\n   * @default undefined\n   *\n   * @example\n   * In JSON, use the provided JSON schema:\n   *\n   * ```json title=\"knip.json\"\n   * {\n   *   \"$schema\": \"https://unpkg.com/knip@6/schema.json\"\n   * }\n   * ```\n   *\n   * @example\n   * In JSONC, use the provided JSONC schema:\n   * ```jsonc title=\"knip.jsonc\"\n   * {\n   *   \"$schema\": \"https://unpkg.com/knip@6/schema-jsonc.json\"\n   * }\n   * ```\n   *\n   * @remarks\n   * Use JSONC if you want to use comments and/or trailing commas.\n   */\n  $schema: z.optional(z.string()),\n  /**\n   * @default {}\n   *\n   * @see {@link https://knip.dev/features/rules-and-filters | Rules & Filters}\n   */\n  rules: z.optional(rulesSchema),\n  /**\n   * Array of glob patterns to find entry files. Prefix with `!` for negation.\n   *\n   * @example\n   * ```json title=\"knip.json\"\n   * {\n   *   \"entry\": [\"src/index.ts\", \"scripts/*.ts\", \"!scripts/except-this-one.ts\"]\n   * }\n   * ```\n   *\n   * @see {@link https://knip.dev/overview/configuration | configuration} and {@link https://knip.dev/explanations/entry-files | entry files}\n   */\n  entry: z.optional(globSchema),\n  /**\n   * Array of glob patterns to find project files.\n   *\n   * @example\n   * ```json title=\"knip.json\"\n   * {\n   *   \"project\": [\"src\\/**\\/*.ts\", \"scripts\\/**\\/*.ts\"]\n   * }\n   * ```\n   *\n   * @see {@link https://knip.dev/overview/configuration | configuration} and {@link https://knip.dev/explanations/entry-files | entry files}\n   */\n  project: z.optional(globSchema),\n  /**\n   * Tools like TypeScript, webpack and Babel support import aliases in various ways.\n   * Knip automatically includes `compilerOptions.paths` from the TypeScript\n   * configuration, but does not automatically use other types of import aliases.\n   * They can be configured manually:\n   *\n   * @example\n   * ```json title=\"knip.json\"\n   * {\n   *   \"paths\": {\n   *     \"@lib\": [\"./lib/index.ts\"],\n   *     \"@lib/*\": [\"./lib/*\"]\n   *   }\n   * }\n   * ```\n   *\n   * @remarks\n   * Each workspace can have its own `paths` configured. Knip `paths` follow the\n   * TypeScript semantics:\n   *\n   * - Path values are an array of relative paths\n   * - Paths without an `*` are exact matches\n   *\n   */\n  paths: z.optional(pathsSchema),\n  /**\n   * :::tip\n   *\n   * Please read {@link https://knip.dev/guides/configuring-project-files | project files configuration} before using the `ignore` option,\n   * because in many cases you'll want to **fine-tune project files** instead.\n   *\n   * :::\n   *\n   * Array of glob patterns to ignore issues from matching files.\n   *\n   * @example\n   * ```json title=\"knip.json\"\n   * {\n   *   \"ignore\": [\"src/generated.ts\", \"fixtures/**\"]\n   * }\n   * ```\n   */\n  ignore: z.optional(globSchema),\n  /**\n   * Array of glob patterns of files to exclude from the \"Unused files\" report section only.\n   *\n   * Unlike `ignore`, which suppresses all issue types for matching files, `ignoreFiles` only\n   * affects the `files` issue type. Use this when a file should still be analyzed for other\n   * issues (exports, dependencies, unresolved) but should not be considered for unused file detection.\n   */\n  ignoreFiles: z.optional(globSchema),\n  /**\n   * Exclude binaries that are used but not provided by any dependency from the\n   * report. Value is an array of binary names or regular expressions.\n   *\n   * @example\n   * ```json title=\"knip.json\"\n   * {\n   *   \"ignoreBinaries\": [\"zip\", \"docker-compose\", \"pm2-.+\"]\n   * }\n   * ```\n   *\n   * @example\n   * Actual regular expressions can be used in dynamic configurations:\n   *\n   * ```ts title=\"knip.ts\"\n   * export default {\n   *   ignoreBinaries: [/^pm2-.+/],\n   * };\n   * ```\n   */\n  ignoreBinaries: z.optional(stringOrRegexSchema),\n  /**\n   * Array of package names to exclude from the report. Regular expressions allowed.\n   *\n   * @example\n   * ```json title=\"knip.json\"\n   * {\n   *   \"ignoreDependencies\": [\"hidden-package\", \"@org/.+\"]\n   * }\n   * ```\n   *\n   * @example\n   * Actual regular expressions can be used in dynamic configurations.\n   * ```ts title=\"knip.ts\"\n   * export default {\n   *   ignoreDependencies: [/@org\\/.*\\/, /^lib-.+/],\n   * };\n   * ```\n   */\n  ignoreDependencies: z.optional(stringOrRegexSchema),\n  /**\n   * Array of enum and namespace members to exclude from the report. Regular expressions\n   * allowed.\n   *\n   * @example\n   * ```json title=\"knip.json\"\n   * {\n   *   \"ignoreMembers\": [\"render\", \"on.+\"]\n   * }\n   * ```\n   *\n   * Actual regular expressions can be used in dynamic configurations.\n   */\n  ignoreMembers: z.optional(stringOrRegexSchema),\n  /**\n   * Array of specifiers to exclude from the report. Regular expressions allowed.\n   *\n   * @example\n   * ```json title=\"knip.json\"\n   * {\n   *   \"ignoreUnresolved\": [\"ignore-unresolved-import\", \"#virtual/.+\"]\n   * }\n   * ```\n   * @example\n   * Actual regular expressions can be used in dynamic configurations:\n   *\n   * ```ts title=\"knip.ts\"\n   * export default {\n   *   ignoreUnresolved: [/^#/.+/],\n   * };\n   * ```\n   */\n  ignoreUnresolved: z.optional(stringOrRegexSchema),\n  /**\n   * In files with multiple exports, some of them might be used only internally. If\n   * these exports should not be reported, there is a `ignoreExportsUsedInFile`\n   * option available. With this option enabled, when something is also no longer\n   * used internally, it will be reported as unused.\n   *\n   * @default false\n   *\n   * @example\n   * ```json title=\"knip.json\"\n   * {\n   *   \"ignoreExportsUsedInFile\": true\n   * }\n   * ```\n   *\n   * @example\n   * In a more fine-grained manner, to ignore only specific issue types:\n   *\n   * ```json title=\"knip.json\"\n   * {\n   *   \"ignoreExportsUsedInFile\": {\n   *     \"interface\": true,\n   *     \"type\": true\n   *   }\n   * }\n   * ```\n   */\n  ignoreExportsUsedInFile: z.optional(ignoreExportsUsedInFileSchema),\n  /**\n   * Ignore specific issue types for specific file patterns. Keys are glob\n   * patterns and values are arrays of issue types to ignore for matching files.\n   * This allows ignoring specific issues (like unused exports) in generated\n   * files while still reporting other issues in those same files.\n   *\n   * @see {@link https://knip.dev/reference/configuration#ignoreissues}\n   */\n  ignoreIssues: z.optional(ignoreIssuesSchema),\n  /**\n   * Array of workspaces to ignore, globs allowed.\n   *\n   * @example\n   * ```json title=\"knip.json\"\n   * {\n   *   \"ignoreWorkspaces\": [\n   *     \"packages/go-server\",\n   *     \"packages/flat/*\",\n   *     \"packages/deep/**\"\n   *   ]\n   * }\n   * ```\n   */\n  ignoreWorkspaces: z.optional(z.array(z.string())),\n  /**\n   * By default, Knip does not report unused exports in entry files. When a\n   * repository (or workspace) is self-contained or private, you may want to include\n   * entry files when reporting unused exports:\n   *\n   * @default false\n   *\n   * @example\n   * ```json title=\"knip.json\"\n   * {\n   *   \"includeEntryExports\": true\n   * }\n   * ```\n   *\n   * @remarks\n   * If enabled, Knip will report unused exports in entry source files. But not in\n   * entry and configuration files as configured by plugins, such as `next.config.js`\n   * or `src/routes/+page.svelte`.\n   *\n   * This will also enable reporting unused members of exported classes and enums.\n   *\n   * Set this option at root level to enable this globally, or within workspace\n   * configurations individually.\n   *\n   */\n  includeEntryExports: z.optional(z.boolean()),\n  /**\n   * Override built-in compilers or add custom compilers for additional file types.\n   *\n   * @see {@link https://knip.dev/features/compilers | Compilers}\n   */\n  compilers: z.optional(compilersSchema),\n  /** @internal */\n  syncCompilers: z.optional(z.record(z.string(), syncCompilerSchema)),\n  /** @internal */\n  asyncCompilers: z.optional(z.record(z.string(), asyncCompilerSchema)),\n  /**\n   * Exports can be tagged with known or arbitrary JSDoc/TSDoc tags.\n   *\n   * @default []\n   *\n   * @example\n   * ```ts\n   * // \\**\n   * //  * Description of my exported value\n   * //  *\n   * //  * \\@type number\n   * //  * \\@internal Important matters\n   * //  * \\@lintignore\n   * //  *\\/\n   * export const myExport = 1;\n   * ```\n   *\n   * And then include (`+`) or exclude (`-`) these tagged exports from the report\n   * like so:\n   *\n   * ```json\n   * {\n   *   \"tags\": [\"-lintignore\"]\n   * }\n   * ```\n   *\n   * This way, you can either focus on or ignore specific tagged exports with tags\n   * you define yourself. This also works for individual class or enum members.\n   *\n   *\n   * @example\n   * The default directive is `+` (include) and the `@` prefix is ignored, so the\n   * notation below is valid and will report only exports tagged `@lintignore` or\n   * `@internal`:\n   *\n   * ```json\n   * {\n   *   \"tags\": [\"@lintignore\", \"@internal\"]\n   * }\n   * ```\n   *\n   * @see {@link https://knip.dev/reference/jsdoc-tsdoc-tags | JSDoc & TSDoc Tags }\n   */\n  tags: z.optional(z.array(z.string())),\n  /**\n   * Exit with non-zero code (1) if there are any configuration hints.\n   *\n   * @default false\n   *\n   * @example\n   * ```json title=\"knip.json\"\n   * {\n   *   \"treatConfigHintsAsErrors\": true\n   * }\n   * ```\n   */\n  treatConfigHintsAsErrors: z.optional(z.boolean()),\n});\n\nconst reportConfigSchema = z.object({\n  /**\n   * @default []\n   *\n   * @see {@link https://knip.dev/features/rules-and-filters | Rules & Filters}\n   */\n  include: z.optional(z.array(issueTypeSchema)),\n  /**\n   * @default []\n   *\n   * @see {@link https://knip.dev/features/rules-and-filters | Rules & Filters}\n   */\n  exclude: z.optional(z.array(issueTypeSchema)),\n});\n\nconst baseWorkspaceConfigurationSchema = z.object({\n  entry: z.optional(globSchema),\n  project: z.optional(globSchema),\n  paths: z.optional(pathsSchema),\n  ignore: z.optional(globSchema),\n  ignoreFiles: z.optional(globSchema),\n  ignoreBinaries: z.optional(stringOrRegexSchema),\n  ignoreDependencies: z.optional(stringOrRegexSchema),\n  ignoreMembers: z.optional(stringOrRegexSchema),\n  ignoreUnresolved: z.optional(stringOrRegexSchema),\n  includeEntryExports: z.optional(z.boolean()),\n});\n\nconst partialPluginsSchema = z.partial(pluginsSchema);\n\nexport const workspaceConfigurationSchema = z.strictObject({\n  ...baseWorkspaceConfigurationSchema.shape,\n  ...partialPluginsSchema.shape,\n});\n\nconst workspacesConfigurationSchema = z.object({\n  workspaces: z.optional(z.record(z.string(), workspaceConfigurationSchema)),\n});\n\nexport const knipConfigurationSchema = z.strictObject({\n  ...rootConfigurationSchema.shape,\n  ...reportConfigSchema.shape,\n  ...workspacesConfigurationSchema.shape,\n  ...partialPluginsSchema.shape,\n});\n"
  },
  {
    "path": "packages/knip/src/schema/plugins.ts",
    "content": "// This file is generated (no need to edit)\nimport { z } from 'zod/mini';\nexport const globSchema = z.union([z.string(), z.array(z.string())]);\n\nexport const pluginSchema = z.union([\n  z.boolean(),\n  globSchema,\n  z.object({\n    config: z.optional(globSchema),\n    entry: z.optional(globSchema),\n    project: z.optional(globSchema),\n  }),\n]);\n\nexport const pluginsSchema = z.object({\n  angular: pluginSchema,\n  astro: pluginSchema,\n  'astro-db': pluginSchema,\n  'astro-og-canvas': pluginSchema,\n  ava: pluginSchema,\n  babel: pluginSchema,\n  biome: pluginSchema,\n  bumpp: pluginSchema,\n  bun: pluginSchema,\n  c8: pluginSchema,\n  capacitor: pluginSchema,\n  changelogen: pluginSchema,\n  changelogithub: pluginSchema,\n  changesets: pluginSchema,\n  commitizen: pluginSchema,\n  commitlint: pluginSchema,\n  convex: pluginSchema,\n  'create-typescript-app': pluginSchema,\n  cspell: pluginSchema,\n  cucumber: pluginSchema,\n  cypress: pluginSchema,\n  danger: pluginSchema,\n  'dependency-cruiser': pluginSchema,\n  docusaurus: pluginSchema,\n  dotenv: pluginSchema,\n  drizzle: pluginSchema,\n  eleventy: pluginSchema,\n  eslint: pluginSchema,\n  execa: pluginSchema,\n  expo: pluginSchema,\n  'expressive-code': pluginSchema,\n  gatsby: pluginSchema,\n  'github-action': pluginSchema,\n  'github-actions': pluginSchema,\n  glob: pluginSchema,\n  'graphql-codegen': pluginSchema,\n  hardhat: pluginSchema,\n  husky: pluginSchema,\n  'i18next-parser': pluginSchema,\n  jest: pluginSchema,\n  karma: pluginSchema,\n  knex: pluginSchema,\n  ladle: pluginSchema,\n  lefthook: pluginSchema,\n  'lint-staged': pluginSchema,\n  linthtml: pluginSchema,\n  'lockfile-lint': pluginSchema,\n  'lost-pixel': pluginSchema,\n  markdownlint: pluginSchema,\n  mdx: pluginSchema,\n  mdxlint: pluginSchema,\n  metro: pluginSchema,\n  mocha: pluginSchema,\n  moonrepo: pluginSchema,\n  msw: pluginSchema,\n  'nano-staged': pluginSchema,\n  nest: pluginSchema,\n  netlify: pluginSchema,\n  next: pluginSchema,\n  'next-intl': pluginSchema,\n  'next-mdx': pluginSchema,\n  nitro: pluginSchema,\n  node: pluginSchema,\n  'node-modules-inspector': pluginSchema,\n  nodemon: pluginSchema,\n  'npm-package-json-lint': pluginSchema,\n  nuxt: pluginSchema,\n  nx: pluginSchema,\n  nyc: pluginSchema,\n  oclif: pluginSchema,\n  'openapi-ts': pluginSchema,\n  oxfmt: pluginSchema,\n  oxlint: pluginSchema,\n  parcel: pluginSchema,\n  payload: pluginSchema,\n  playwright: pluginSchema,\n  'playwright-ct': pluginSchema,\n  'playwright-test': pluginSchema,\n  plop: pluginSchema,\n  pm2: pluginSchema,\n  pnpm: pluginSchema,\n  postcss: pluginSchema,\n  preconstruct: pluginSchema,\n  prettier: pluginSchema,\n  prisma: pluginSchema,\n  qwik: pluginSchema,\n  raycast: pluginSchema,\n  'react-cosmos': pluginSchema,\n  'react-native': pluginSchema,\n  'react-router': pluginSchema,\n  relay: pluginSchema,\n  'release-it': pluginSchema,\n  remark: pluginSchema,\n  remix: pluginSchema,\n  rollup: pluginSchema,\n  rsbuild: pluginSchema,\n  rslib: pluginSchema,\n  rspack: pluginSchema,\n  rstest: pluginSchema,\n  sanity: pluginSchema,\n  'semantic-release': pluginSchema,\n  sentry: pluginSchema,\n  'simple-git-hooks': pluginSchema,\n  'size-limit': pluginSchema,\n  sst: pluginSchema,\n  starlight: pluginSchema,\n  storybook: pluginSchema,\n  stryker: pluginSchema,\n  stylelint: pluginSchema,\n  svelte: pluginSchema,\n  sveltekit: pluginSchema,\n  svgo: pluginSchema,\n  svgr: pluginSchema,\n  swc: pluginSchema,\n  syncpack: pluginSchema,\n  tailwind: pluginSchema,\n  'tanstack-router': pluginSchema,\n  taskfile: pluginSchema,\n  travis: pluginSchema,\n  'ts-node': pluginSchema,\n  tsdown: pluginSchema,\n  tsup: pluginSchema,\n  tsx: pluginSchema,\n  typedoc: pluginSchema,\n  typescript: pluginSchema,\n  unbuild: pluginSchema,\n  unocss: pluginSchema,\n  'vercel-og': pluginSchema,\n  vike: pluginSchema,\n  vite: pluginSchema,\n  vitepress: pluginSchema,\n  vitest: pluginSchema,\n  vue: pluginSchema,\n  'webdriver-io': pluginSchema,\n  webpack: pluginSchema,\n  wireit: pluginSchema,\n  wrangler: pluginSchema,\n  xo: pluginSchema,\n  yarn: pluginSchema,\n  yorkie: pluginSchema,\n  zx: pluginSchema,\n});\n"
  },
  {
    "path": "packages/knip/src/session/build-maps.ts",
    "content": "import { IMPORT_STAR } from '../constants.ts';\nimport type { GraphExplorer } from '../graph-explorer/explorer.ts';\nimport type { UsageLocation } from '../graph-explorer/operations/get-usage.ts';\nimport { getExportedIdentifiers } from '../graph-explorer/utils.ts';\nimport {\n  forEachAliasReExport,\n  forEachNamespaceReExport,\n  forEachPassThroughReExport,\n  getStarReExportSources,\n} from '../graph-explorer/visitors.ts';\nimport type { FileNode, ModuleGraph } from '../types/module-graph.ts';\nimport type { Export, ImportLookup, InternalImport } from './types.ts';\n\nconst FALLBACK_LOCATION = { identifier: undefined, pos: 0, line: 0, col: 0 };\n\nexport const buildImportLookup = (fileNode: FileNode) => {\n  const imports: ImportLookup = new Map();\n\n  for (const _import of fileNode.imports.imports) {\n    if (!_import.filePath || !_import.identifier) continue;\n    let fileMap = imports.get(_import.filePath);\n    if (!fileMap) {\n      fileMap = new Map();\n      imports.set(_import.filePath, fileMap);\n    }\n    const list = fileMap.get(_import.identifier) ?? [];\n    list.push(_import);\n    fileMap.set(_import.identifier, list);\n  }\n\n  return imports;\n};\n\nconst excludeReExports = (location: UsageLocation) =>\n  location.via !== 'reExport' &&\n  location.via !== 'reExportAs' &&\n  location.via !== 'reExportNS' &&\n  location.via !== 'reExportStar';\n\nexport const buildExportsMap = (\n  fileNode: FileNode,\n  filePath: string,\n  graph: ModuleGraph,\n  entryPaths: Set<string>,\n  explorer: GraphExplorer,\n  importLookup: ImportLookup\n) => {\n  const exportsMap = new Map<string, Export>();\n  const getImport = (importedFilePath: string, id: string) =>\n    importLookup.get(importedFilePath)?.get(id)?.[0] ?? FALLBACK_LOCATION;\n\n  const addExport = (\n    identifier: string,\n    sourceFilePath: string,\n    pos: number,\n    line: number,\n    col: number,\n    childExports: Export[] | undefined\n  ) => {\n    if (exportsMap.has(identifier)) return;\n    const usage = explorer.getUsage(sourceFilePath, identifier);\n    const usageEntryPaths = new Set<string>();\n    for (const location of usage.locations) if (location.isEntry) usageEntryPaths.add(location.filePath);\n    if (entryPaths.has(sourceFilePath)) usageEntryPaths.add(sourceFilePath);\n\n    exportsMap.set(identifier, {\n      filePath,\n      identifier,\n      pos,\n      line,\n      col,\n      importLocations: usage.locations\n        .filter(excludeReExports)\n        .sort((a, b) => a.filePath.localeCompare(b.filePath) || a.line - b.line || a.col - b.col),\n      entryPaths: usageEntryPaths,\n      exports: childExports,\n    });\n  };\n\n  for (const _export of fileNode.exports.values()) {\n    addExport(_export.identifier, filePath, _export.pos, _export.line, _export.col, undefined);\n  }\n\n  for (const [importedFilePath, importMaps] of fileNode.imports.internal) {\n    forEachPassThroughReExport(importMaps, (id, _sources) => {\n      if (exportsMap.has(id)) return;\n      const directImport = getImport(importedFilePath, id);\n      addExport(id, filePath, directImport.pos, directImport.line, directImport.col, undefined);\n    });\n\n    forEachAliasReExport(importMaps, (id, alias, _sources) => {\n      if (exportsMap.has(alias)) return;\n      const aliasImport = getImport(importedFilePath, id);\n      addExport(alias, filePath, aliasImport.pos, aliasImport.line, aliasImport.col, undefined);\n    });\n\n    forEachNamespaceReExport(importMaps, (namespace, _sources) => {\n      if (exportsMap.has(namespace)) return;\n      const namespaceImport = getImport(importedFilePath, namespace) ?? getImport(importedFilePath, IMPORT_STAR);\n      const childExports: Export[] = [];\n      const exportedIdentifiers = getExportedIdentifiers(graph, importedFilePath);\n      for (const identifier of exportedIdentifiers.keys()) {\n        const usage = explorer.getUsage(importedFilePath, identifier);\n        const usageEntryPaths = new Set<string>();\n        for (const location of usage.locations) if (location.isEntry) usageEntryPaths.add(location.filePath);\n        if (entryPaths.has(importedFilePath)) usageEntryPaths.add(importedFilePath);\n\n        childExports.push({\n          filePath,\n          identifier,\n          pos: namespaceImport.pos,\n          line: namespaceImport.line,\n          col: namespaceImport.col,\n          importLocations: usage.locations,\n          entryPaths: usageEntryPaths,\n          exports: undefined,\n        });\n      }\n\n      addExport(namespace, filePath, namespaceImport.pos, namespaceImport.line, namespaceImport.col, childExports);\n    });\n\n    const starSources = getStarReExportSources(importMaps);\n    if (starSources) {\n      const starImport = getImport(importedFilePath, IMPORT_STAR);\n      const exportedIdentifiers = getExportedIdentifiers(graph, importedFilePath);\n      for (const [nestedIdentifier] of exportedIdentifiers) {\n        if (nestedIdentifier === 'default') continue;\n        addExport(nestedIdentifier, importedFilePath, starImport.pos, starImport.line, starImport.col, undefined);\n      }\n    }\n  }\n\n  return exportsMap;\n};\n\nexport const buildInternalImports = (fileNode: FileNode, explorer: GraphExplorer, importLookup: ImportLookup) => {\n  const getImport = (importedFilePath: string, id: string) =>\n    importLookup.get(importedFilePath)?.get(id)?.[0] ?? FALLBACK_LOCATION;\n  const internalImports: InternalImport[] = [];\n\n  const addInternalImport = (\n    importedFilePath: string,\n    identifier: string,\n    alias = identifier,\n    importLine: number,\n    importCol: number\n  ) => {\n    const resolution = explorer.resolveDefinition(importedFilePath, identifier);\n    const location =\n      resolution?.type === 'symbol' && resolution.exportNode\n        ? {\n            filePath: resolution.filePath,\n            pos: resolution.exportNode.pos,\n            line: resolution.exportNode.line,\n            col: resolution.exportNode.col,\n          }\n        : { filePath: importedFilePath, pos: 0, line: 0, col: 0 };\n\n    internalImports.push({\n      identifier: alias,\n      filePath: location.filePath,\n      pos: location.pos,\n      line: location.line,\n      col: location.col,\n      importLine,\n      importCol,\n    });\n  };\n\n  for (const [importedFilePath, importMaps] of fileNode.imports.internal) {\n    for (const identifier of importMaps.import.keys()) {\n      const _import = getImport(importedFilePath, identifier);\n      addInternalImport(importedFilePath, identifier, _import.identifier, _import.line, _import.col);\n    }\n\n    for (const [identifier, aliasMap] of importMaps.importAs) {\n      for (const [alias] of aliasMap) {\n        const _import = getImport(importedFilePath, identifier);\n        addInternalImport(importedFilePath, identifier, alias, _import.line, _import.col);\n      }\n    }\n\n    if (importMaps.importNs.size > 0) {\n      const _import = getImport(importedFilePath, IMPORT_STAR);\n      for (const namespace of importMaps.importNs.keys()) {\n        addInternalImport(importedFilePath, namespace, namespace, _import.line, _import.col);\n      }\n    }\n  }\n\n  internalImports.sort((a, b) => a.importLine - b.importLine || a.importCol - b.importCol);\n\n  return internalImports;\n};\n"
  },
  {
    "path": "packages/knip/src/session/file-descriptor.ts",
    "content": "import { createGraphExplorer } from '../graph-explorer/explorer.ts';\nimport type { ModuleGraph } from '../types/module-graph.ts';\nimport { toAbsolute } from '../util/path.ts';\nimport { buildExportsMap, buildImportLookup, buildInternalImports } from './build-maps.ts';\nimport type { ContentionDetails, File, FileMetrics } from './types.ts';\n\nexport interface FileDescriptorOptions {\n  isShowContention?: boolean;\n}\n\nexport const buildFileDescriptor = (\n  filePath: string,\n  cwd: string,\n  graph: ModuleGraph,\n  entryPaths: Set<string>,\n  options: FileDescriptorOptions = {}\n): File | undefined => {\n  const absolutePath = toAbsolute(filePath, cwd);\n\n  const node = graph.get(absolutePath);\n  if (!node) return;\n\n  const explorer = createGraphExplorer(graph, entryPaths);\n\n  const metrics: FileMetrics = { imports: 0, exports: 0, cycles: 0, contention: 0 };\n\n  let t0 = performance.now();\n  const importLookup = buildImportLookup(node);\n  const internalImports = buildInternalImports(node, explorer, importLookup);\n  metrics.imports = performance.now() - t0;\n\n  t0 = performance.now();\n  const exportsMap = buildExportsMap(node, absolutePath, graph, entryPaths, explorer, importLookup);\n  const exports = Array.from(exportsMap.values()).sort(\n    (a, b) => (a.line ?? 0) - (b.line ?? 0) || (a.col ?? 0) - (b.col ?? 0)\n  );\n  metrics.exports = performance.now() - t0;\n\n  t0 = performance.now();\n  const cycles = explorer.findCycles(absolutePath);\n  metrics.cycles = performance.now() - t0;\n\n  t0 = performance.now();\n  const contention: Record<string, ContentionDetails> = Object.create(null);\n  if (options.isShowContention !== false) {\n    const contentionMap = explorer.getContention(absolutePath);\n    for (const identifier of exportsMap.keys()) {\n      const details = contentionMap.get(identifier);\n      if (!details) continue;\n      if (details.branching.length === 0 && details.conflict.length === 0) continue;\n      contention[identifier] = details;\n    }\n  }\n  metrics.contention = performance.now() - t0;\n\n  return {\n    internalImports,\n    exports,\n    cycles,\n    contention,\n    metrics,\n  };\n};\n"
  },
  {
    "path": "packages/knip/src/session/index.ts",
    "content": "export { IMPORT_STAR, KNIP_CONFIG_LOCATIONS, SIDE_EFFECTS } from '../constants.ts';\nexport type { DependencyNode, DependencyNodes } from '../graph-explorer/operations/get-dependency-usage.ts';\nexport { finalizeConfigurationHints } from '../reporters/util/configuration-hints.ts';\nexport { getIssuePrefix } from '../reporters/util/util.ts';\nexport type { Results } from '../run.ts';\nexport type { Issue, Issues, IssueType, Rules } from '../types/issues.ts';\nexport type { PackageJson } from '../types/package-json.ts';\nexport { createOptions, type MainOptions } from '../util/create-options.ts';\nexport { buildFileDescriptor, type FileDescriptorOptions } from './file-descriptor.ts';\nexport { buildPackageJsonDescriptor, type PackageJsonFile } from './package-json-descriptor.ts';\nexport { createSession, type Session } from './session.ts';\nexport type { ContentionDetails, Export, File, SourceLocation } from './types.ts';\n"
  },
  {
    "path": "packages/knip/src/session/package-json-descriptor.ts",
    "content": "import { createGraphExplorer } from '../graph-explorer/explorer.ts';\nimport type { DependencyNodes } from '../graph-explorer/operations/get-dependency-usage.ts';\nimport type { ModuleGraph } from '../types/module-graph.ts';\n\nexport interface PackageJsonFile {\n  dependenciesUsage: Map<string, DependencyNodes>;\n}\n\nexport const buildPackageJsonDescriptor = (graph: ModuleGraph, entryPaths: Set<string>): PackageJsonFile => {\n  const explorer = createGraphExplorer(graph, entryPaths);\n\n  return {\n    dependenciesUsage: explorer.getDependencyUsage(),\n  };\n};\n"
  },
  {
    "path": "packages/knip/src/session/session.ts",
    "content": "import type { CollectorIssues } from '../IssueCollector.ts';\nimport { type Results, run } from '../run.ts';\nimport type { MainOptions } from '../util/create-options.ts';\nimport type { SessionHandler, WatchChange } from '../util/watch.ts';\nimport { buildFileDescriptor, type FileDescriptorOptions } from './file-descriptor.ts';\nimport { buildPackageJsonDescriptor, type PackageJsonFile } from './package-json-descriptor.ts';\nimport type { File } from './types.ts';\n\ntype WatchUpdate = { duration: number; mem: number };\n\nexport interface Session {\n  handleFileChanges(changes: WatchChange[]): Promise<WatchUpdate | undefined>;\n  getIssues(): CollectorIssues;\n  getResults(): Results;\n  describeFile(filePath: string, options?: FileDescriptorOptions): File | undefined;\n  describePackageJson(): PackageJsonFile;\n}\n\nexport const createSession = async (options: MainOptions): Promise<Session> => {\n  const { session, results } = await run(options);\n\n  if (!session) throw new Error('Unable to initialize watch session');\n\n  return createSessionAdapter(session, results, options);\n};\n\nconst createSessionAdapter = (session: SessionHandler, results: Results, options: MainOptions): Session => {\n  return {\n    handleFileChanges: session.handleFileChanges,\n    getIssues: session.getIssues,\n    getResults: () => results,\n    describeFile: (filePath, opts) =>\n      buildFileDescriptor(filePath, options.cwd, session.getGraph(), session.getEntryPaths(), opts),\n    describePackageJson: () => buildPackageJsonDescriptor(session.getGraph(), session.getEntryPaths()),\n  };\n};\n"
  },
  {
    "path": "packages/knip/src/session/types.ts",
    "content": "import type { RE_EXPORT_KIND } from '../graph-explorer/constants.ts';\nimport type { Import, Position } from '../types/module-graph.ts';\n\nexport interface SourceLocation extends Position {\n  filePath: string;\n  identifier: string;\n}\n\nexport interface InternalImport extends SourceLocation {\n  importLine: number;\n  importCol: number;\n}\n\nexport interface Export extends SourceLocation {\n  importLocations: SourceLocation[];\n  entryPaths: Set<string>;\n  exports: Export[] | undefined;\n}\n\nexport interface ContentionDetails {\n  branching: string[];\n  conflict: string[];\n}\n\nexport interface FileMetrics {\n  imports: number;\n  exports: number;\n  cycles: number;\n  contention: number;\n}\n\nexport interface File {\n  exports: Export[];\n  internalImports: InternalImport[];\n  cycles: Cycle[];\n  contention: Record<string, ContentionDetails>;\n  metrics: FileMetrics;\n}\n\nexport type ImportLookup = Map<string, Map<string, Import[]>>;\n\nexport type Cycle = string[];\n\nexport type ReExportKind = (typeof RE_EXPORT_KIND)[keyof typeof RE_EXPORT_KIND];\n"
  },
  {
    "path": "packages/knip/src/types/PluginNames.ts",
    "content": "// This file is generated (no need to edit)\nexport type PluginName =\n  | 'angular'\n  | 'astro'\n  | 'astro-db'\n  | 'astro-og-canvas'\n  | 'ava'\n  | 'babel'\n  | 'biome'\n  | 'bumpp'\n  | 'bun'\n  | 'c8'\n  | 'capacitor'\n  | 'changelogen'\n  | 'changelogithub'\n  | 'changesets'\n  | 'commitizen'\n  | 'commitlint'\n  | 'convex'\n  | 'create-typescript-app'\n  | 'cspell'\n  | 'cucumber'\n  | 'cypress'\n  | 'danger'\n  | 'dependency-cruiser'\n  | 'docusaurus'\n  | 'dotenv'\n  | 'drizzle'\n  | 'eleventy'\n  | 'eslint'\n  | 'execa'\n  | 'expo'\n  | 'expressive-code'\n  | 'gatsby'\n  | 'github-action'\n  | 'github-actions'\n  | 'glob'\n  | 'graphql-codegen'\n  | 'hardhat'\n  | 'husky'\n  | 'i18next-parser'\n  | 'jest'\n  | 'karma'\n  | 'knex'\n  | 'ladle'\n  | 'lefthook'\n  | 'lint-staged'\n  | 'linthtml'\n  | 'lockfile-lint'\n  | 'lost-pixel'\n  | 'markdownlint'\n  | 'mdx'\n  | 'mdxlint'\n  | 'metro'\n  | 'mocha'\n  | 'moonrepo'\n  | 'msw'\n  | 'nano-staged'\n  | 'nest'\n  | 'netlify'\n  | 'next'\n  | 'next-intl'\n  | 'next-mdx'\n  | 'nitro'\n  | 'node'\n  | 'node-modules-inspector'\n  | 'nodemon'\n  | 'npm-package-json-lint'\n  | 'nuxt'\n  | 'nx'\n  | 'nyc'\n  | 'oclif'\n  | 'openapi-ts'\n  | 'oxfmt'\n  | 'oxlint'\n  | 'parcel'\n  | 'payload'\n  | 'playwright'\n  | 'playwright-ct'\n  | 'playwright-test'\n  | 'plop'\n  | 'pm2'\n  | 'pnpm'\n  | 'postcss'\n  | 'preconstruct'\n  | 'prettier'\n  | 'prisma'\n  | 'qwik'\n  | 'raycast'\n  | 'react-cosmos'\n  | 'react-native'\n  | 'react-router'\n  | 'relay'\n  | 'release-it'\n  | 'remark'\n  | 'remix'\n  | 'rollup'\n  | 'rsbuild'\n  | 'rslib'\n  | 'rspack'\n  | 'rstest'\n  | 'sanity'\n  | 'semantic-release'\n  | 'sentry'\n  | 'simple-git-hooks'\n  | 'size-limit'\n  | 'sst'\n  | 'starlight'\n  | 'storybook'\n  | 'stryker'\n  | 'stylelint'\n  | 'svelte'\n  | 'sveltekit'\n  | 'svgo'\n  | 'svgr'\n  | 'swc'\n  | 'syncpack'\n  | 'tailwind'\n  | 'tanstack-router'\n  | 'taskfile'\n  | 'travis'\n  | 'ts-node'\n  | 'tsdown'\n  | 'tsup'\n  | 'tsx'\n  | 'typedoc'\n  | 'typescript'\n  | 'unbuild'\n  | 'unocss'\n  | 'vercel-og'\n  | 'vike'\n  | 'vite'\n  | 'vitepress'\n  | 'vitest'\n  | 'vue'\n  | 'webdriver-io'\n  | 'webpack'\n  | 'wireit'\n  | 'wrangler'\n  | 'xo'\n  | 'yarn'\n  | 'yorkie'\n  | 'zx';\n\nexport const pluginNames = [\n  'angular',\n  'astro',\n  'astro-db',\n  'astro-og-canvas',\n  'ava',\n  'babel',\n  'biome',\n  'bumpp',\n  'bun',\n  'c8',\n  'capacitor',\n  'changelogen',\n  'changelogithub',\n  'changesets',\n  'commitizen',\n  'commitlint',\n  'convex',\n  'create-typescript-app',\n  'cspell',\n  'cucumber',\n  'cypress',\n  'danger',\n  'dependency-cruiser',\n  'docusaurus',\n  'dotenv',\n  'drizzle',\n  'eleventy',\n  'eslint',\n  'execa',\n  'expo',\n  'expressive-code',\n  'gatsby',\n  'github-action',\n  'github-actions',\n  'glob',\n  'graphql-codegen',\n  'hardhat',\n  'husky',\n  'i18next-parser',\n  'jest',\n  'karma',\n  'knex',\n  'ladle',\n  'lefthook',\n  'lint-staged',\n  'linthtml',\n  'lockfile-lint',\n  'lost-pixel',\n  'markdownlint',\n  'mdx',\n  'mdxlint',\n  'metro',\n  'mocha',\n  'moonrepo',\n  'msw',\n  'nano-staged',\n  'nest',\n  'netlify',\n  'next',\n  'next-intl',\n  'next-mdx',\n  'nitro',\n  'node',\n  'node-modules-inspector',\n  'nodemon',\n  'npm-package-json-lint',\n  'nuxt',\n  'nx',\n  'nyc',\n  'oclif',\n  'openapi-ts',\n  'oxfmt',\n  'oxlint',\n  'parcel',\n  'payload',\n  'playwright',\n  'playwright-ct',\n  'playwright-test',\n  'plop',\n  'pm2',\n  'pnpm',\n  'postcss',\n  'preconstruct',\n  'prettier',\n  'prisma',\n  'qwik',\n  'raycast',\n  'react-cosmos',\n  'react-native',\n  'react-router',\n  'relay',\n  'release-it',\n  'remark',\n  'remix',\n  'rollup',\n  'rsbuild',\n  'rslib',\n  'rspack',\n  'rstest',\n  'sanity',\n  'semantic-release',\n  'sentry',\n  'simple-git-hooks',\n  'size-limit',\n  'sst',\n  'starlight',\n  'storybook',\n  'stryker',\n  'stylelint',\n  'svelte',\n  'sveltekit',\n  'svgo',\n  'svgr',\n  'swc',\n  'syncpack',\n  'tailwind',\n  'tanstack-router',\n  'taskfile',\n  'travis',\n  'ts-node',\n  'tsdown',\n  'tsup',\n  'tsx',\n  'typedoc',\n  'typescript',\n  'unbuild',\n  'unocss',\n  'vercel-og',\n  'vike',\n  'vite',\n  'vitepress',\n  'vitest',\n  'vue',\n  'webdriver-io',\n  'webpack',\n  'wireit',\n  'wrangler',\n  'xo',\n  'yarn',\n  'yorkie',\n  'zx',\n] as const;\n"
  },
  {
    "path": "packages/knip/src/types/args.ts",
    "content": "import type { ParsedArgs } from 'minimist';\nimport type { Input } from '../util/input.ts';\n\nexport type ConfigArg = boolean | (string | [string, (id: string) => string])[];\n\nexport type Args = {\n  /**\n   * Default executables for the dependency (e.g. typescript has `[\"tsc\"]`)\n   *\n   * @default plugin name (e.g. eslint has `eslint` executable)\n   */\n  binaries?: string[];\n\n  /**\n   * Add first positional argument as entry point(s)\n   *\n   * @default undefined\n   */\n  positional?: boolean;\n\n  /**\n   * Mark arguments as string\n   *\n   * @default undefined\n   */\n  string?: string[];\n\n  /**\n   * Mark arguments as boolean\n   *\n   * @default undefined\n   */\n  boolean?: string[];\n\n  /**\n   * Define aliases (e.g. `{ require: [\"r\"] }`)\n   * Using `nodeImportArgs: true` will set those automatically\n   *\n   * @default undefined\n   */\n  alias?: Record<string, string[]>;\n\n  /**\n   * Arguments to resolve to a dependency or entry file path.\n   *\n   * @example `resolve: [\"plugin\"]` for `program --plugin package`\n   * @default undefined\n   */\n  resolve?: string[];\n\n  /**\n   * Resolve values for the following arguments: `-r --require --import --loader --experimental-loader --test-reporter`\n   * Shorthand for `resolve` with `alias`\n   *\n   * @example nodeImportArgs: true\n   * @default undefined\n   */\n  nodeImportArgs?: boolean;\n\n  /**\n   * Define arguments that contain config file path.\n   * Usually you'll want to set aliases too. Use `true` for shorthand to set `alias` + `string` + `config`\n   *\n   * @example `config: [\"p\"]` for e.g. `tsc -p tsconfig.lib.json`\n   *\n   * @example `config: true` for e.g. `tsup --config tsup.client.json`\n   *\n   * @default undefined\n   */\n  config?: ConfigArg;\n\n  /**\n   * Modify or filter arguments before parsing.\n   *\n   * @default undefined\n   */\n  args?: (args: string[]) => string[];\n\n  /**\n   * Parse return value as script.\n   * Can be a function that returns an array of string,\n   * or an array of strings and those values will be parsed as scripts (recursion)\n   *\n   * @example fromArgs: [\"exec\"] and `nodemon --exec \"node index.js\"` to also parse `node index.js`\n   *\n   * @example fromArgs: (parsed: ParsedArgs, args: string[]) => argsFrom(args, parsed._[0]);\n   *           and `dotenv KEY=value -- program index.js` to also parse script starting from first positional: `program index.js`\n   *\n   * @default undefined\n   */\n  fromArgs?: string[] | ((parsed: ParsedArgs, args: string[]) => string[]);\n\n  /**\n   * Return inputs from parsed arguments\n   *\n   * @example resolveInputs: (parsed: ParsedArgs) => parsed['flag'] ? [toDependency('dependency'] : []\n   *\n   * @default undefined\n   */\n  resolveInputs?: (parsed: ParsedArgs, options: { cwd: string; args: string[] }) => Input[];\n};\n"
  },
  {
    "path": "packages/knip/src/types/config.ts",
    "content": "import type { Program, VisitorObject } from 'oxc-parser';\nimport type { z } from 'zod/mini';\nimport type { AsyncCompilers, CompilerSync, HasDependency, SyncCompilers } from '../compilers/types.ts';\nimport type { knipConfigurationSchema, workspaceConfigurationSchema } from '../schema/configuration.ts';\nimport type { pluginSchema } from '../schema/plugins.ts';\nimport type { ParsedCLIArgs } from '../util/cli-arguments.ts';\nimport type { Input } from '../util/input.ts';\nimport type { Args } from './args.ts';\nimport type { IssueType, SymbolType } from './issues.ts';\nimport type { Tags } from './options.ts';\nimport type { PluginName } from './PluginNames.ts';\nimport type { PackageJson } from './package-json.ts';\n\nexport interface GetInputsFromScriptsOptions extends BaseOptions {\n  knownBinsOnly?: boolean;\n  containingFilePath: string;\n}\n\nexport type GetInputsFromScripts<T = GetInputsFromScriptsOptions> = (\n  npmScripts: string | string[] | Set<string>,\n  options: T\n) => Input[];\n\nexport type GetInputsFromScriptsPartial = (\n  npmScripts: string | string[] | Set<string>,\n  options?: Partial<GetInputsFromScriptsOptions>\n) => Input[];\n\nexport type FromArgs = (args: string[], options?: Partial<GetInputsFromScriptsOptions>) => Input[];\n\nexport interface BinaryResolverOptions extends GetInputsFromScriptsOptions {\n  fromArgs: FromArgs;\n}\n\nexport type BinaryResolver = (binary: string, args: string[], options: BinaryResolverOptions) => Input[];\n\nexport type RawConfiguration = z.infer<typeof knipConfigurationSchema>;\n\nexport type RawConfigurationOrFn =\n  | RawConfiguration\n  | ((options: ParsedCLIArgs) => RawConfiguration | Promise<RawConfiguration>);\n\nexport type RawPluginConfiguration = z.infer<typeof pluginSchema>;\n\nexport type WorkspaceProjectConfig = z.infer<typeof workspaceConfigurationSchema>;\n\nexport type IgnorePatterns = (string | RegExp)[];\n\ntype IgnorableExport = Exclude<SymbolType, 'unknown'>;\n\nexport type IgnoreExportsUsedInFile = boolean | Partial<Record<IgnorableExport, boolean>>;\n\nexport type IgnoreIssues = Record<string, IssueType[]>;\n\nexport type GetImportsAndExportsOptions = {\n  skipTypeOnly: boolean;\n  isFixExports: boolean;\n  isFixTypes: boolean;\n  isReportExports: boolean;\n  tags: Tags;\n};\n\nexport interface Configuration {\n  ignore: NormalizedGlob;\n  ignoreBinaries: IgnorePatterns;\n  ignoreDependencies: IgnorePatterns;\n  ignoreExportsUsedInFile: IgnoreExportsUsedInFile;\n  ignoreFiles: NormalizedGlob;\n  ignoreIssues: IgnoreIssues;\n  ignoreMembers: IgnorePatterns;\n  ignoreUnresolved: IgnorePatterns;\n  ignoreWorkspaces: string[];\n  isIncludeEntryExports: boolean;\n  syncCompilers: SyncCompilers;\n  asyncCompilers: AsyncCompilers;\n  rootPluginConfigs: Partial<PluginsConfiguration>;\n}\n\ntype NormalizedGlob = string[];\n\nexport type EnsuredPluginConfiguration = {\n  config: NormalizedGlob | null;\n  entry: NormalizedGlob | null;\n  project: NormalizedGlob | null;\n};\n\ninterface BaseWorkspaceConfiguration {\n  entry: NormalizedGlob;\n  project: NormalizedGlob;\n  paths: Record<string, string[]>;\n  ignore: NormalizedGlob;\n  ignoreFiles: NormalizedGlob;\n  isIncludeEntryExports: boolean;\n}\n\ntype PluginConfiguration = EnsuredPluginConfiguration | boolean;\n\nexport type PluginsConfiguration = Record<PluginName, PluginConfiguration>;\n\nexport interface WorkspaceConfiguration extends BaseWorkspaceConfiguration, Partial<PluginsConfiguration> {}\n\ninterface BaseOptions {\n  rootCwd: string;\n  cwd: string;\n  manifestScriptNames: Set<string>;\n  rootManifest: PackageJson | undefined;\n}\n\ntype IsPluginEnabledOptions = {\n  cwd: string;\n  manifest: PackageJson;\n  dependencies: Set<string>;\n  config: WorkspaceConfiguration;\n};\n\nexport type IsPluginEnabled = (options: IsPluginEnabledOptions) => boolean | Promise<boolean>;\n\nexport interface PluginOptions extends BaseOptions {\n  manifest: PackageJson;\n  config: EnsuredPluginConfiguration;\n  configFileDir: string;\n  configFileName: string;\n  configFilePath: string;\n  isProduction: boolean;\n  enabledPlugins: string[];\n  getInputsFromScripts: GetInputsFromScriptsPartial;\n}\n\ntype PluginSetup = () => Promise<void> | void;\n\nexport type IsLoadConfig = (options: PluginOptions, dependencies: Set<string>) => boolean;\n\nexport type ResolveConfig<T = any> = (config: T, options: PluginOptions) => Promise<Input[]> | Input[];\n\nexport type Resolve = (options: PluginOptions) => Promise<Input[]> | Input[];\n\nexport type HandleInput = (input: Input) => string | undefined;\n\nexport type RegisterCompilerInput = {\n  extension: string;\n  compiler: CompilerSync;\n};\n\nexport type RegisterCompiler = (input: RegisterCompilerInput) => void;\n\nexport type ResolveFromAST = (\n  program: Program,\n  options: PluginOptions & {\n    readFile: (filePath: string) => string;\n  }\n) => Input[];\n\nexport type RegisterCompilersOptions = {\n  cwd: string;\n  hasDependency: HasDependency;\n  registerCompiler: RegisterCompiler;\n};\n\n/** Plugin compilers are registered if the plugin is enabled, but might be gated by `hasDependency` */\nexport type RegisterCompilers = (options: RegisterCompilersOptions) => Promise<void> | void;\n\nexport type PluginVisitorContext = {\n  filePath: string;\n  sourceText: string;\n  addScript: (script: string) => void;\n  addImport: (specifier: string, pos: number, modifiers: number) => void;\n};\n\nexport type PluginVisitorObject = VisitorObject;\n\nexport type RegisterVisitorsOptions = {\n  ctx: PluginVisitorContext;\n  registerVisitor: (visitor: PluginVisitorObject) => void;\n  registeredPlugins: Set<string>;\n};\n\nexport type RegisterVisitors = (options: RegisterVisitorsOptions) => void;\n\nexport interface Plugin {\n  title: string;\n  args?: Args;\n  packageJsonPath?: string | ((manifest: PackageJson) => unknown);\n  enablers?: IgnorePatterns | string;\n  isEnabled?: IsPluginEnabled;\n  isRootOnly?: boolean;\n  config?: string[] | ((options: { cwd: string }) => string[]);\n  entry?: string[];\n  production?: string[];\n  project?: string[];\n  setup?: PluginSetup;\n  isLoadConfig?: IsLoadConfig;\n  resolveConfig?: ResolveConfig;\n  resolve?: Resolve;\n  resolveFromAST?: ResolveFromAST;\n  isFilterTransitiveDependencies?: boolean;\n  registerCompilers?: RegisterCompilers;\n  registerVisitors?: RegisterVisitors;\n}\n\nexport type PluginMap = Record<PluginName, Plugin>;\n\nexport type Entries = [PluginName, Plugin][];\n"
  },
  {
    "path": "packages/knip/src/types/entries.ts",
    "content": "export type Entries<T> = Array<{ [K in keyof T]: [K, T[K]] }[keyof T]>;\n"
  },
  {
    "path": "packages/knip/src/types/exports.ts",
    "content": "type ExportPosTuple = [number, number, number];\nexport type Fix = ExportPosTuple | undefined;\nexport type Fixes = Array<ExportPosTuple>;\n"
  },
  {
    "path": "packages/knip/src/types/issues.ts",
    "content": "import type { SYMBOL_TYPE } from '../constants.ts';\nimport type { Fixes } from './exports.ts';\n\nexport type SymbolType = (typeof SYMBOL_TYPE)[keyof typeof SYMBOL_TYPE];\n\nexport interface IssueSymbol {\n  symbol: string;\n  pos?: number;\n  line?: number;\n  col?: number;\n}\n\nexport interface Issue {\n  type: IssueType;\n  filePath: string;\n  workspace: string;\n  symbol: string;\n  symbols?: IssueSymbol[];\n  symbolType?: SymbolType;\n  parentSymbol?: string;\n  specifier?: string;\n  severity?: IssueSeverity;\n  pos?: number;\n  line?: number;\n  col?: number;\n  fixes: Fixes;\n  isFixed?: boolean;\n}\n\nexport type IssueRecords = Record<string, Record<string, Issue>>;\n\nexport type Issues = {\n  files: IssueRecords;\n  dependencies: IssueRecords;\n  devDependencies: IssueRecords;\n  optionalPeerDependencies: IssueRecords;\n  unlisted: IssueRecords;\n  binaries: IssueRecords;\n  unresolved: IssueRecords;\n  exports: IssueRecords;\n  types: IssueRecords;\n  nsExports: IssueRecords;\n  nsTypes: IssueRecords;\n  duplicates: IssueRecords;\n  enumMembers: IssueRecords;\n  namespaceMembers: IssueRecords;\n  catalog: IssueRecords;\n};\n\nexport type IssueType = keyof Issues;\n\nexport type Report = {\n  [key in keyof Issues]: boolean;\n};\n\nexport type Counters = Record<IssueType | 'processed' | 'total', number>;\n\nexport type ReporterOptions = {\n  report: Report;\n  issues: Issues;\n  counters: Counters;\n  tagHints: TagHints;\n  configurationHints: ConfigurationHint[];\n  enabledPlugins: Record<string, string[]>;\n  isDisableConfigHints: boolean;\n  isTreatConfigHintsAsErrors: boolean;\n  cwd: string;\n  isProduction: boolean;\n  isShowProgress: boolean;\n  options: string;\n  preprocessorOptions: string;\n  includedWorkspaceDirs: string[];\n  selectedWorkspaces: string[] | undefined;\n  configFilePath: string | undefined;\n  maxShowIssues?: number;\n};\n\nexport type Reporter = (options: ReporterOptions) => void;\n\nexport type Preprocessor = (options: ReporterOptions) => ReporterOptions;\n\nexport type IssueSeverity = 'error' | 'warn' | 'off';\n\nexport type Rules = Record<IssueType, IssueSeverity>;\n\nexport type ConfigurationHints = Map<string, ConfigurationHint>;\n\nexport type ConfigurationHintType =\n  | 'ignore'\n  | 'ignoreFiles'\n  | 'ignoreBinaries'\n  | 'ignoreDependencies'\n  | 'ignoreUnresolved'\n  | 'ignoreWorkspaces'\n  | 'entry-redundant'\n  | 'project-redundant'\n  | 'entry-top-level'\n  | 'project-top-level'\n  | 'entry-empty'\n  | 'project-empty'\n  | 'package-entry'\n  | 'top-level-unconfigured'\n  | 'workspace-unconfigured';\n\nexport type ConfigurationHint = {\n  type: ConfigurationHintType;\n  identifier: string | RegExp;\n  filePath?: string;\n  workspaceName?: string;\n  size?: number;\n};\n\ntype TagHints = Set<TagHint>;\n\nexport type TagHint = {\n  type: 'tag';\n  filePath: string;\n  identifier: string;\n  tagName: string;\n};\n"
  },
  {
    "path": "packages/knip/src/types/module-graph.ts",
    "content": "import type { Fix, Fixes } from './exports.ts';\nimport type { IssueSymbol, SymbolType } from './issues.ts';\n\nexport type Identifier = string;\ntype FilePath = string;\ntype NamespaceOrAlias = string;\n\ntype Reference = string;\ntype References = Set<Reference>;\n\ntype Tags = Set<string>;\n\nexport interface Position {\n  pos: number;\n  line: number;\n  col: number;\n}\n\nexport type IdToFileMap = Map<Identifier, Set<FilePath>>;\nexport type IdToNsToFileMap = Map<Identifier, Map<NamespaceOrAlias, Set<FilePath>>>;\n\nexport type ImportMaps = {\n  /** Usage references cq. property-access patterns on imports (\"default\", \"named\", \"NS.member\", \"alias.sub\", \"enum.member\", etc.); NOT mere import usage */\n  refs: References;\n  /** Identifiers imported from this file */\n  import: IdToFileMap;\n  /** Identifiers imported with alias (id → alias → files) */\n  importAs: IdToNsToFileMap;\n  /** Namespace imports of this file */\n  importNs: IdToFileMap;\n  /** Identifiers re-exported (not directly imported) */\n  reExport: IdToFileMap;\n  /** Namespace re-exports */\n  reExportNs: IdToFileMap;\n  /** Irregular re-exports: id → namespace/alias → source files */\n  reExportAs: IdToNsToFileMap;\n};\n\nexport type ImportMap = Map<FilePath, ImportMaps>;\n\nexport interface Import extends Position {\n  readonly specifier: string;\n  readonly filePath: string | undefined;\n  readonly identifier: string | undefined;\n  readonly isTypeOnly: boolean;\n}\n\nexport interface ExternalRef {\n  readonly specifier: string;\n  readonly identifier: string | undefined;\n}\n\nexport interface Export extends Position {\n  readonly identifier: Identifier;\n  readonly type: SymbolType;\n  readonly members: ExportMember[];\n  jsDocTags: Tags;\n  hasRefsInFile: boolean;\n  referencedIn: Set<string> | undefined;\n  readonly fixes: Fixes;\n  isReExport: boolean;\n}\n\nexport interface ExportMember extends Position {\n  readonly identifier: Identifier;\n  readonly type: SymbolType;\n  readonly fix: Fix;\n  readonly jsDocTags: Tags;\n  readonly flags: number;\n  hasRefsInFile: boolean;\n}\n\nexport type ExportMap = Map<Identifier, Export>;\n\nexport type Imports = Set<Import>;\n\nexport type FileNode = {\n  imports: {\n    readonly internal: ImportMap;\n    readonly external: Set<Import>;\n    readonly externalRefs: Set<ExternalRef>;\n    unresolved: Set<Import>;\n    readonly programFiles: Set<FilePath>;\n    readonly entryFiles: Set<FilePath>;\n    readonly imports: Imports;\n  };\n  exports: ExportMap;\n  duplicates: Iterable<Array<IssueSymbol>>;\n  scripts: Set<string>;\n  /** Aggregation of other files importing this file's exports */\n  importedBy: undefined | ImportMaps;\n  internalImportCache: undefined | ImportMap;\n};\n\nexport type ModuleGraph = Map<FilePath, FileNode>;\n"
  },
  {
    "path": "packages/knip/src/types/options.ts",
    "content": "export interface Options {\n  cacheLocation: string;\n  cwd: string;\n  excludedIssueTypes: string[];\n  fixTypes: string[];\n  gitignore: boolean;\n  includedIssueTypes: string[];\n  isCache: boolean;\n  isDebug: boolean;\n  isDependenciesShorthand: boolean;\n  isExportsShorthand: boolean;\n  isFilesShorthand: boolean;\n  isFix: boolean;\n  isFormat: boolean;\n  isIncludeEntryExports: boolean;\n\n  isProduction: boolean;\n  isRemoveFiles: boolean;\n  isSession: boolean;\n  isShowProgress: boolean;\n  isStrict: boolean;\n  isUseTscFiles: boolean;\n  isWatch: boolean;\n  tags: string[];\n  tsConfigFile: string | undefined;\n  workspace: string | string[] | undefined;\n}\n\nexport type Tags = [string[], string[]];\n"
  },
  {
    "path": "packages/knip/src/types/package-json.ts",
    "content": "import type { PluginMap, WorkspaceConfiguration } from './config.ts';\n\ntype Primitive = null | undefined | string | number | boolean | symbol | bigint;\n\ntype LiteralUnion<LiteralType, BaseType extends Primitive> = LiteralType | (BaseType & Record<never, never>);\n\ntype Dependencies = Record<string, string>;\n\ntype C = 'import' | 'require' | 'node' | 'node-addons' | 'deno' | 'browser' | 'electron' | 'react-native' | 'default';\ntype ExportCondition = LiteralUnion<C, string>;\ntype Exports = null | string | string[] | { [key in ExportCondition]: Exports } | { [key: string]: Exports };\n\ntype Imports = {\n  [key: `#${string}`]: Exports;\n};\n\ntype PackageJsonPath<T> = T extends { packageJsonPath: infer P } ? (P extends string ? P : never) : never;\n\ntype WithPackageJsonPathAsKey<T> = {\n  [K in keyof T]: PackageJsonPath<T[K]> extends never ? K : PackageJsonPath<T[K]>;\n};\n\ntype PluginConfig<P> = {\n  [K in keyof P as WithPackageJsonPathAsKey<P>[K]]: unknown;\n};\n\ntype Plugins = PluginConfig<PluginMap>;\n\nexport type Scripts = Record<string, string>;\n\nexport type Catalog = Record<string, string>;\nexport type Catalogs = Record<string, Catalog>;\n\nexport type PackageJson = {\n  name?: string;\n  main?: string;\n  bin?: string | Record<string, string>;\n  version?: string;\n  workspaces?: string[] | { packages?: string[]; catalog?: Catalog; catalogs?: Catalogs };\n  exports?: Exports;\n  imports?: Imports;\n  scripts?: Scripts;\n  dependencies?: Dependencies;\n  devDependencies?: Dependencies;\n  peerDependencies?: Dependencies;\n  optionalDependencies?: Dependencies;\n  peerDependenciesMeta?: Record<string, { optional: true }>;\n  resolutions?: Dependencies;\n  module?: string;\n  browser?: string;\n  types?: string;\n  typings?: string;\n  catalog?: Catalog;\n  catalogs?: Catalogs;\n  packageManager?: string;\n  pnpm?: {\n    overrides?: Dependencies;\n  };\n  knip?: WorkspaceConfiguration;\n} & Plugins;\n\nexport type WorkspacePackage = {\n  dir: string;\n  name: string;\n  pkgName: string | undefined;\n  manifest: PackageJson;\n  manifestPath: string;\n  manifestStr: string;\n};\n"
  },
  {
    "path": "packages/knip/src/types/project.ts",
    "content": "export type Paths = Record<string, string[]> | undefined;\n\nexport interface CompilerOptions {\n  allowJs?: boolean;\n  allowNonTsExtensions?: boolean;\n  allowSyntheticDefaultImports?: boolean;\n  baseUrl?: string;\n  declaration?: boolean;\n  declarationMap?: boolean;\n  esModuleInterop?: boolean;\n  inlineSourceMap?: boolean;\n  inlineSources?: boolean;\n  jsx?: number;\n  jsxImportSource?: string;\n  lib?: string[];\n  module?: number;\n  moduleResolution?: number;\n  noEmit?: boolean;\n  outDir?: string;\n  paths?: Record<string, string[]>;\n  pathsBasePath?: string;\n  plugins?: Array<{ name: string } | string>;\n  rootDir?: string;\n  skipDefaultLibCheck?: boolean;\n  skipLibCheck?: boolean;\n  sourceMap?: boolean;\n  types?: string[];\n  [key: string]: unknown;\n}\n"
  },
  {
    "path": "packages/knip/src/types/tsconfig-json.ts",
    "content": "export interface TsConfigJson {\n  extends?: string | string[];\n  compilerOptions?: {\n    types?: string[];\n    jsxImportSource?: string;\n    plugins?: Array<string | { name: string }>;\n    [key: string]: unknown;\n  };\n  references?: Array<{ path: string }>;\n}\n"
  },
  {
    "path": "packages/knip/src/types/workspace.ts",
    "content": "export type DependencySet = Set<string>;\nexport type DependencyArray = Array<string>;\n\ntype WorkspaceManifest = {\n  workspaceDir: string;\n  manifestPath: string;\n  manifestStr: string;\n  dependencies: DependencyArray;\n  devDependencies: DependencyArray;\n  peerDependencies: DependencySet;\n  optionalPeerDependencies: DependencySet;\n  requiredPeerDependencies: DependencyArray;\n  allDependencies: DependencySet;\n  ignoreDependencies: (string | RegExp)[];\n  ignoreBinaries: (string | RegExp)[];\n  ignoreUnresolved: (string | RegExp)[];\n  unusedIgnoreDependencies: Set<string | RegExp>;\n  unusedIgnoreBinaries: Set<string | RegExp>;\n  unusedIgnoreUnresolved: Set<string | RegExp>;\n};\n\nexport type WorkspaceManifests = Map<string, WorkspaceManifest>;\n\nexport type HostDependencies = Map<string, Array<{ name: string; isPeerOptional: boolean }>>;\n\ntype PackageName = string;\ntype BinaryName = string;\n\nexport type InstalledBinaries = Map<PackageName, Set<BinaryName>>;\n"
  },
  {
    "path": "packages/knip/src/types.ts",
    "content": "export type { RawConfigurationOrFn as KnipConfig, WorkspaceProjectConfig } from './types/config.ts';\nexport type { Preprocessor, Reporter, ReporterOptions } from './types/issues.ts';\n"
  },
  {
    "path": "packages/knip/src/typescript/SourceFileManager.ts",
    "content": "import { readFileSync } from 'node:fs';\nimport type { AsyncCompilers, SyncCompilers } from '../compilers/types.ts';\nimport { FOREIGN_FILE_EXTENSIONS } from '../constants.ts';\nimport { debugLog } from '../util/debug.ts';\nimport { extname, isInternal } from '../util/path.ts';\n\ninterface SourceFileManagerOptions {\n  compilers: [SyncCompilers, AsyncCompilers];\n}\n\nexport class SourceFileManager {\n  sourceTextCache = new Map<string, string>();\n  syncCompilers: SyncCompilers;\n  asyncCompilers: AsyncCompilers;\n\n  constructor({ compilers }: SourceFileManagerOptions) {\n    this.syncCompilers = compilers[0];\n    this.asyncCompilers = compilers[1];\n  }\n\n  readFile(filePath: string): string {\n    if (this.sourceTextCache.has(filePath)) return this.sourceTextCache.get(filePath)!;\n    const ext = extname(filePath);\n    const compiler = this.syncCompilers.get(ext);\n    if (FOREIGN_FILE_EXTENSIONS.has(ext) && !compiler) {\n      this.sourceTextCache.set(filePath, '');\n      return '';\n    }\n    const contents = this.readRawFile(filePath);\n    if (contents === undefined) {\n      if (isInternal(filePath)) debugLog('*', `Unable to read ${filePath}`);\n      this.sourceTextCache.set(filePath, '');\n      return '';\n    }\n    const compiled = compiler ? compiler(contents, filePath) : contents;\n    if (compiler) debugLog('*', `Compiled ${filePath}`);\n    this.sourceTextCache.set(filePath, compiled);\n    return compiled;\n  }\n\n  invalidate(filePath: string) {\n    this.sourceTextCache.delete(filePath);\n  }\n\n  async compileAndAddSourceFile(filePath: string) {\n    const contents = this.readRawFile(filePath);\n    if (contents === undefined) throw new Error(`Unable to read ${filePath}`);\n    const ext = extname(filePath);\n    const compiler = this.asyncCompilers.get(ext);\n    if (compiler) {\n      const compiled = await compiler(contents, filePath);\n      debugLog('*', `Compiled ${filePath}`);\n      this.sourceTextCache.set(filePath, compiled);\n    }\n  }\n\n  private readRawFile(filePath: string): string | undefined {\n    try {\n      return readFileSync(filePath, 'utf8');\n    } catch {\n      return undefined;\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/typescript/ast-helpers.ts",
    "content": "import type { ParseResult, Program } from 'oxc-parser';\nimport { Visitor } from 'oxc-parser';\nimport stripJsonComments from 'strip-json-comments';\nimport { extname, isInternal } from '../util/path.ts';\nimport { parseFile } from './visitors/helpers.ts';\n\nconst isStringLiteral = (node: any): boolean =>\n  node?.type === 'StringLiteral' || (node?.type === 'Literal' && typeof node.value === 'string');\n\nconst getStringValue = (node: any): string | undefined => (isStringLiteral(node) ? node.value : undefined);\n\nexport const getImportMap = (program: Program) => {\n  const importMap = new Map<string, string>();\n  for (const node of (program as any).body ?? []) {\n    if (node.type === 'ImportDeclaration') {\n      const importPath = getStringValue(node.source);\n      if (!importPath) continue;\n      for (const spec of node.specifiers ?? []) {\n        if (spec.type === 'ImportDefaultSpecifier' && spec.local?.name) {\n          importMap.set(spec.local.name, importPath);\n        } else if (spec.type === 'ImportSpecifier' && spec.local?.name) {\n          importMap.set(spec.local.name, importPath);\n        }\n      }\n    }\n    if (node.type === 'VariableDeclaration') {\n      for (const decl of node.declarations ?? []) {\n        if (\n          decl.init?.type === 'CallExpression' &&\n          decl.init.callee?.type === 'Identifier' &&\n          decl.init.callee.name === 'require' &&\n          isStringLiteral(decl.init.arguments?.[0]) &&\n          decl.id?.type === 'Identifier'\n        ) {\n          importMap.set(decl.id.name, decl.init.arguments[0].value);\n        }\n      }\n    }\n  }\n  return importMap;\n};\n\nexport const getDefaultImportName = (importMap: Map<string, string>, specifier: string) => {\n  for (const [name, path] of importMap) {\n    if (path === specifier) return name;\n  }\n};\n\nexport const getPropertyValues = (node: any, propertyName: string) => {\n  const values = new Set<string>();\n  if (node?.type !== 'ObjectExpression') return values;\n  for (const prop of node.properties ?? []) {\n    if (prop.type !== 'Property') continue;\n    const name = prop.key?.name ?? prop.key?.value;\n    if (name !== propertyName) continue;\n    const init = prop.value;\n    if (isStringLiteral(init)) {\n      values.add(init.value);\n    } else if (init?.type === 'ArrayExpression') {\n      for (const el of init.elements ?? []) {\n        if (isStringLiteral(el)) values.add(el.value);\n      }\n    } else if (init?.type === 'ObjectExpression') {\n      for (const p of init.properties ?? []) {\n        if (p.type === 'Property' && isStringLiteral(p.value)) {\n          values.add(p.value.value);\n        }\n      }\n    }\n  }\n  return values;\n};\n\n/** Collect all values of a named property from any ObjectExpression in the program */\nexport const collectPropertyValues = (program: Program, propertyName: string): Set<string> => {\n  const values = new Set<string>();\n  const visitor = new Visitor({\n    ObjectExpression(node) {\n      for (const v of getPropertyValues(node, propertyName)) values.add(v);\n    },\n  });\n  visitor.visit(program);\n  return values;\n};\n\n/** Find the first ObjectExpression argument of a named function call */\nexport const findCallArg = (program: Program, fnName: string): any | undefined => {\n  let result: any;\n  const visitor = new Visitor({\n    CallExpression(node) {\n      if (result) return;\n      if (node.callee?.type === 'Identifier' && node.callee.name === fnName) {\n        const arg = node.arguments?.[0];\n        if (arg?.type === 'ObjectExpression') result = arg;\n      }\n    },\n  });\n  visitor.visit(program);\n  return result;\n};\n\n/** Find a named property in an ObjectExpression, returns the value node */\nexport const findProperty = (node: any, name: string): any | undefined => {\n  if (node?.type !== 'ObjectExpression') return;\n  for (const prop of node.properties ?? []) {\n    if (prop.type === 'Property' && (prop.key?.name === name || prop.key?.value === name)) {\n      return prop.value;\n    }\n  }\n};\n\n/** Extract string values from an array expression, including [string, ...] tuples */\nexport const getStringValues = (node: any): Set<string> => {\n  const values = new Set<string>();\n  if (node?.type !== 'ArrayExpression') return values;\n  for (const el of node.elements ?? []) {\n    if (isStringLiteral(el)) {\n      values.add(el.value);\n    } else if (el?.type === 'ArrayExpression' && isStringLiteral(el.elements?.[0])) {\n      values.add(el.elements[0].value);\n    }\n  }\n  return values;\n};\n\nexport const isExternalReExportsOnly = (result: ParseResult): boolean => {\n  const mod = result.module;\n  if (mod.staticExports.length === 0) return false;\n  for (const se of mod.staticExports) {\n    for (const entry of se.entries) {\n      if (!entry.moduleRequest) return false;\n      if (isInternal(entry.moduleRequest.value)) return false;\n    }\n  }\n  if (mod.staticImports.length > 0) return false;\n  return true;\n};\n\n/** Check if a specific named import exists from a module */\nexport const hasImportSpecifier = (program: Program, modulePath: string, specifierName: string): boolean => {\n  for (const node of (program as any).body ?? []) {\n    if (node.type !== 'ImportDeclaration' || getStringValue(node.source) !== modulePath) continue;\n    for (const spec of node.specifiers ?? []) {\n      if (spec.type === 'ImportSpecifier') {\n        const imported = spec.imported?.name ?? spec.local?.name;\n        if (imported === specifierName) return true;\n      }\n    }\n  }\n  return false;\n};\n\nconst collectJsonStringLiterals = (obj: unknown, literals: Set<string>) => {\n  if (typeof obj === 'string') {\n    literals.add(obj);\n  } else if (Array.isArray(obj)) {\n    for (const item of obj) collectJsonStringLiterals(item, literals);\n  } else if (obj && typeof obj === 'object') {\n    for (const val of Object.values(obj)) collectJsonStringLiterals(val, literals);\n  }\n};\n\nexport const collectStringLiterals = (sourceText: string, filePath: string): Set<string> => {\n  const literals = new Set<string>();\n  try {\n    const ext = extname(filePath);\n    if (ext === '.json' || ext === '.jsonc' || ext === '.json5') {\n      collectJsonStringLiterals(JSON.parse(stripJsonComments(sourceText, { trailingCommas: true })), literals);\n      return literals;\n    }\n    const result = parseFile(filePath, sourceText);\n    const visitor = new Visitor({\n      Literal(node: any) {\n        if (typeof node.value === 'string') literals.add(node.value);\n      },\n    });\n    visitor.visit(result.program);\n  } catch {}\n  return literals;\n};\n"
  },
  {
    "path": "packages/knip/src/typescript/follow-imports.ts",
    "content": "import { Visitor, type ParseResult } from 'oxc-parser';\nimport { isInNodeModules } from '../util/path.ts';\nimport { getStringValue, isStringLiteral, stripQuotes } from './visitors/helpers.ts';\n\nconst _requireSpecs: string[] = [];\nconst _requireVisitor = new Visitor({\n  CallExpression(node) {\n    if (node.callee?.type === 'Identifier' && node.callee.name === 'require') {\n      const arg = node.arguments?.[0];\n      if (isStringLiteral(arg)) _requireSpecs.push(getStringValue(arg)!);\n    }\n    if (\n      node.callee?.type === 'MemberExpression' &&\n      !node.callee.computed &&\n      node.callee.object?.type === 'Identifier' &&\n      node.callee.object.name === 'require' &&\n      node.callee.property?.name === 'resolve'\n    ) {\n      const arg = node.arguments?.[0];\n      if (isStringLiteral(arg)) _requireSpecs.push(getStringValue(arg)!);\n    }\n  },\n  TSImportEqualsDeclaration(node) {\n    if (node.moduleReference?.type === 'TSExternalModuleReference') {\n      const expr = node.moduleReference.expression;\n      if (isStringLiteral(expr)) _requireSpecs.push(getStringValue(expr)!);\n    }\n  },\n});\n\nconst _jsDocImportRe = /import\\(\\s*['\"]([^'\"]+)['\"]\\s*\\)/g;\n\nexport function extractSpecifiers(result: ParseResult, sourceText: string, filePath: string): string[] {\n  const specifiers: string[] = [];\n  const mod = result.module;\n\n  for (const si of mod.staticImports) {\n    specifiers.push(si.moduleRequest.value);\n  }\n\n  for (const se of mod.staticExports) {\n    for (const entry of se.entries) {\n      if (entry.moduleRequest) specifiers.push(entry.moduleRequest.value);\n    }\n  }\n\n  for (const di of mod.dynamicImports) {\n    const cleaned = stripQuotes(sourceText.slice(di.moduleRequest.start, di.moduleRequest.end));\n    if (cleaned && !cleaned.includes('$') && !cleaned.includes('+')) {\n      specifiers.push(cleaned);\n    }\n  }\n\n  if (!isInNodeModules(filePath)) {\n    _requireSpecs.length = 0;\n    _requireVisitor.visit(result.program);\n    for (const spec of _requireSpecs) specifiers.push(spec);\n  }\n\n  for (const comment of result.comments) {\n    if (comment.type !== 'Block') continue;\n    let m: RegExpExecArray | null;\n    _jsDocImportRe.lastIndex = 0;\n    while ((m = _jsDocImportRe.exec(comment.value)) !== null) {\n      specifiers.push(m[1]);\n    }\n  }\n\n  return specifiers;\n}\n"
  },
  {
    "path": "packages/knip/src/typescript/get-imports-and-exports.ts",
    "content": "import { isBuiltin } from 'node:module';\nimport type { ParseResult, Visitor } from 'oxc-parser';\nimport { IMPORT_FLAGS, IMPORT_STAR, OPAQUE, PROTOCOL_VIRTUAL, SIDE_EFFECTS } from '../constants.ts';\nimport type { GetImportsAndExportsOptions, IgnoreExportsUsedInFile, PluginVisitorContext } from '../types/config.ts';\nimport type { IssueSymbol, SymbolType } from '../types/issues.ts';\nimport type { Export, FileNode, ImportMap, ImportMaps, Imports } from '../types/module-graph.ts';\nimport { addNsValue, addValue, createImports } from '../util/module-graph.ts';\nimport { getPackageNameFromFilePath, isStartsLikePackageName, sanitizeSpecifier } from '../util/modules.ts';\nimport { timerify } from '../util/Performance.ts';\nimport { dirname, isInNodeModules, resolve } from '../util/path.ts';\nimport { shouldIgnore } from '../util/tag.ts';\nimport {\n  buildLineStarts,\n  getLineAndCol,\n  parseFile,\n  shouldCountRefs,\n  stripQuotes,\n  type ResolveModule,\n  type ResolvedModule,\n} from './visitors/helpers.ts';\nimport { buildJSDocTagLookup } from './visitors/jsdoc.ts';\nimport { collectLocalRefs } from './visitors/local-refs.ts';\nimport { walkAST } from './visitors/walk.ts';\n\nconst jsDocImportRe = /import\\(\\s*['\"]([^'\"]+)['\"]\\s*\\)(?:\\.(\\w+))?/g;\nconst jsDocImportTagRe = /@import\\s+(?:\\{[^}]*\\}|\\*\\s+as\\s+\\w+)\\s+from\\s+['\"]([^'\"]+)['\"]/g;\nconst jsxImportSourceRe = /@jsxImportSource\\s+(\\S+)/;\nconst referenceTypesRe = /\\/\\s*<reference\\s+types\\s*=\\s*\"([^\"]+)\"\\s*\\/>/;\nconst envRe = /@(?:vitest|jest)-environment\\s+(\\S+)/g;\n\ninterface AddInternalImportOptions {\n  specifier: string;\n  identifier: string | undefined;\n  alias: string | undefined;\n  namespace: string | undefined;\n  filePath: string;\n  pos: number;\n  line: number;\n  col: number;\n  modifiers: number;\n}\n\nconst getImportsAndExports = (\n  filePath: string,\n  sourceText: string,\n  resolveModule: ResolveModule,\n  options: GetImportsAndExportsOptions,\n  ignoreExportsUsedInFile: IgnoreExportsUsedInFile,\n  skipExportsForFile: boolean,\n  visitor: Visitor,\n  pluginCtx: PluginVisitorContext | undefined,\n  cachedParseResult?: ParseResult\n): FileNode => {\n  const skipExports = skipExportsForFile || !options.isReportExports;\n  const internal: ImportMap = new Map();\n  const external: Imports = new Set();\n  const unresolved: Imports = new Set();\n  const programFiles = new Set<string>();\n  const entryFiles = new Set<string>();\n  const imports: Imports = new Set();\n  const exports = new Map<string, Export>();\n  const aliasedExports = new Map<string, IssueSymbol[]>();\n  const specifierExportNames = new Set<string>();\n  const scripts = new Set<string>();\n\n  const importAliases = new Map<string, Set<{ id: string; filePath: string }>>();\n  const addImportAlias = (aliasName: string, id: string, importFilePath: string) => {\n    const aliases = importAliases.get(aliasName);\n    if (aliases) aliases.add({ id, filePath: importFilePath });\n    else importAliases.set(aliasName, new Set([{ id, filePath: importFilePath }]));\n  };\n\n  const localImportMap = new Map<\n    string,\n    { importedName: string; filePath: string; isNamespace: boolean; isDynamicImport?: boolean }\n  >();\n  const localDeclarationTypes = new Map<string, SymbolType>();\n  const referencedInExport = new Map<string, Set<string>>();\n  const destructuredExports = new Set<string>();\n\n  const addNsMemberRefs = (internalImport: ImportMaps, namespace: string, member: string | string[]) => {\n    if (typeof member === 'string') {\n      internalImport.refs.add(`${namespace}.${member}`);\n    } else {\n      for (const m of member) internalImport.refs.add(`${namespace}.${m}`);\n    }\n  };\n\n  const addInternalImport = (opts: AddInternalImportOptions) => {\n    const { filePath: importFilePath, namespace, specifier, modifiers } = opts;\n    const identifier = opts.identifier ?? (modifiers & IMPORT_FLAGS.OPAQUE ? OPAQUE : SIDE_EFFECTS);\n    const isStar = identifier === IMPORT_STAR;\n\n    imports.add({\n      filePath: importFilePath,\n      specifier,\n      identifier: namespace ?? opts.identifier,\n      pos: opts.pos,\n      line: opts.line,\n      col: opts.col,\n      isTypeOnly: !!(modifiers & IMPORT_FLAGS.TYPE_ONLY),\n    });\n\n    const file = internal.get(importFilePath);\n    const importMaps = file ?? createImports();\n    if (!file) internal.set(importFilePath, importMaps);\n\n    const nsOrAlias = opts.alias;\n\n    if (modifiers & IMPORT_FLAGS.RE_EXPORT) {\n      if (isStar && namespace) {\n        addValue(importMaps.reExportNs, namespace, filePath);\n      } else if (nsOrAlias) {\n        addNsValue(importMaps.reExportAs, identifier, nsOrAlias, filePath);\n      } else {\n        addValue(importMaps.reExport, identifier, filePath);\n      }\n    } else {\n      if (nsOrAlias && nsOrAlias !== identifier) {\n        if (isStar) {\n          addValue(importMaps.importNs, nsOrAlias, filePath);\n        } else {\n          addNsValue(importMaps.importAs, identifier, nsOrAlias, filePath);\n        }\n      } else if (identifier !== IMPORT_STAR) {\n        addValue(importMaps.import, identifier, filePath);\n      }\n    }\n  };\n\n  const addImport = (\n    specifier: string,\n    identifier: string | undefined,\n    alias: string | undefined,\n    namespace: string | undefined,\n    pos: number,\n    modifiers: number,\n    specifierPos?: number,\n    jsDocTags?: Set<string>,\n    preResolvedModule?: ResolvedModule | undefined\n  ) => {\n    if (!specifier || isBuiltin(specifier)) return;\n\n    const module = preResolvedModule ?? resolveModule(specifier, filePath);\n\n    if (module) {\n      const resolvedFileName = module.resolvedFileName;\n      if (resolvedFileName) {\n        if (!isInNodeModules(resolvedFileName)) {\n          if (modifiers & IMPORT_FLAGS.ENTRY) entryFiles.add(resolvedFileName);\n          if (modifiers & IMPORT_FLAGS.BRIDGE) programFiles.add(resolvedFileName);\n        }\n\n        if (!module.isExternalLibraryImport || !isInNodeModules(resolvedFileName)) {\n          const { line, col } = getLineAndCol(lineStarts, pos);\n          addInternalImport({\n            identifier,\n            alias,\n            namespace,\n            filePath: resolvedFileName,\n            specifier,\n            pos,\n            line,\n            col,\n            modifiers,\n          });\n        }\n\n        if (module.isExternalLibraryImport) {\n          if (options.skipTypeOnly && modifiers & IMPORT_FLAGS.TYPE_ONLY) return;\n\n          const sanitizedSpecifier = sanitizeSpecifier(\n            isInNodeModules(resolvedFileName) || isInNodeModules(specifier)\n              ? getPackageNameFromFilePath(specifier)\n              : specifier\n          );\n\n          if (!isStartsLikePackageName(sanitizedSpecifier)) return;\n\n          const ePos = specifierPos ?? pos;\n          const { line, col } = getLineAndCol(lineStarts, ePos);\n          external.add({\n            filePath: resolvedFileName,\n            specifier: sanitizedSpecifier,\n            identifier: identifier ?? SIDE_EFFECTS,\n            pos: ePos,\n            line,\n            col,\n            isTypeOnly: !!(modifiers & IMPORT_FLAGS.TYPE_ONLY),\n          });\n        }\n      }\n    } else {\n      if (options.skipTypeOnly && modifiers & IMPORT_FLAGS.TYPE_ONLY) return;\n      if (specifier.startsWith(PROTOCOL_VIRTUAL)) return;\n\n      if (modifiers && modifiers & IMPORT_FLAGS.OPTIONAL) {\n        programFiles.add(resolve(dirname(filePath), specifier));\n        return;\n      }\n\n      const uPos = specifierPos ?? pos;\n      const { line, col } = getLineAndCol(lineStarts, uPos);\n      if (!(jsDocTags?.size && shouldIgnore(jsDocTags, options.tags))) {\n        unresolved.add({\n          filePath: undefined,\n          specifier,\n          identifier: identifier ?? SIDE_EFFECTS,\n          pos: uPos,\n          line,\n          col,\n          isTypeOnly: !!(modifiers & IMPORT_FLAGS.TYPE_ONLY),\n        });\n      }\n    }\n  };\n\n  const result = cachedParseResult ?? parseFile(filePath, sourceText);\n  const lineStarts = buildLineStarts(sourceText);\n  const getJSDocTags = buildJSDocTagLookup(result.comments, sourceText);\n\n  let hasNodeModuleImport = false;\n\n  for (const _imports of result.module.staticImports) {\n    const specifier = _imports.moduleRequest.value;\n    if (specifier === 'node:module' || specifier === 'module') hasNodeModuleImport = true;\n    const pos = _imports.moduleRequest.start;\n    const jsdocTags = getJSDocTags(_imports.start);\n\n    if (_imports.entries.length === 0) {\n      addImport(specifier, undefined, undefined, undefined, pos, IMPORT_FLAGS.SIDE_EFFECTS, undefined, jsdocTags);\n      continue;\n    }\n\n    const resolved = resolveModule(specifier, filePath);\n    const internalPath =\n      resolved && !resolved.isExternalLibraryImport && !isInNodeModules(resolved.resolvedFileName)\n        ? resolved.resolvedFileName\n        : undefined;\n\n    for (const entry of _imports.entries) {\n      const modifiers = entry.isType ? IMPORT_FLAGS.TYPE_ONLY : IMPORT_FLAGS.NONE;\n\n      if (entry.importName.kind === 'NamespaceObject') {\n        const localName = entry.localName.value;\n        addImport(\n          specifier,\n          IMPORT_STAR,\n          localName,\n          undefined,\n          entry.localName.start,\n          modifiers,\n          pos,\n          jsdocTags,\n          resolved\n        );\n        if (internalPath)\n          localImportMap.set(localName, { importedName: IMPORT_STAR, filePath: internalPath, isNamespace: true });\n      } else if (entry.importName.kind === 'Default') {\n        const localName = entry.localName.value;\n        const alias = localName !== 'default' ? localName : undefined;\n        addImport(specifier, 'default', alias, undefined, entry.localName.start, modifiers, pos, jsdocTags, resolved);\n        if (internalPath)\n          localImportMap.set(localName, { importedName: 'default', filePath: internalPath, isNamespace: false });\n      } else {\n        const importedName = entry.importName.name!;\n        const localName = entry.localName.value;\n        const alias = localName !== importedName ? localName : undefined;\n        addImport(\n          specifier,\n          importedName,\n          alias,\n          undefined,\n          entry.localName.start,\n          modifiers,\n          pos,\n          jsdocTags,\n          resolved\n        );\n        if (internalPath) localImportMap.set(localName, { importedName, filePath: internalPath, isNamespace: false });\n      }\n    }\n  }\n\n  for (const se of result.module.staticExports) {\n    const jsdocTags = getJSDocTags(se.start);\n    let reExportResolved: ResolvedModule | undefined;\n    let reExportSpecifier: string | undefined;\n    for (const entry of se.entries) {\n      if (entry.moduleRequest) {\n        const specifier = entry.moduleRequest.value;\n        const modifiers = IMPORT_FLAGS.RE_EXPORT | (entry.isType ? IMPORT_FLAGS.TYPE_ONLY : IMPORT_FLAGS.NONE);\n        const pos = entry.moduleRequest.start;\n        if (specifier !== reExportSpecifier) {\n          reExportSpecifier = specifier;\n          reExportResolved = resolveModule(specifier, filePath);\n        }\n        if (entry.importName.kind === 'AllButDefault') {\n          addImport(\n            specifier,\n            IMPORT_STAR,\n            undefined,\n            undefined,\n            pos,\n            modifiers,\n            undefined,\n            jsdocTags,\n            reExportResolved\n          );\n        } else if (entry.importName.kind === 'All') {\n          const ns = entry.exportName.name!;\n          addImport(specifier, IMPORT_STAR, undefined, ns, entry.start, modifiers, pos, jsdocTags, reExportResolved);\n        } else if (entry.importName.kind === 'Name') {\n          const importedName = entry.importName.name!;\n          const exportedName = entry.exportName.name;\n          const alias = exportedName && exportedName !== importedName ? exportedName : undefined;\n          addImport(\n            specifier,\n            importedName,\n            alias,\n            undefined,\n            entry.start,\n            modifiers,\n            pos,\n            undefined,\n            reExportResolved\n          );\n        }\n        continue;\n      }\n\n      if (skipExports) continue;\n    }\n  }\n\n  if (pluginCtx) {\n    pluginCtx.filePath = filePath;\n    pluginCtx.sourceText = sourceText;\n    pluginCtx.addScript = (s: string) => scripts.add(s);\n    pluginCtx.addImport = (spec: string, pos: number, mod: number) =>\n      addImport(spec, undefined, undefined, undefined, pos, mod);\n  }\n\n  walkAST(result.program, sourceText, filePath, {\n    lineStarts,\n    skipExports,\n    options,\n    exports,\n    aliasedExports,\n    specifierExportNames,\n    scripts,\n    addImport,\n    addNsMemberRefs,\n    addImportAlias,\n    internal,\n    localImportMap,\n    localDeclarationTypes,\n    importAliases,\n    referencedInExport,\n    skipBareExprRefs: !!ignoreExportsUsedInFile,\n    destructuredExports,\n    hasNodeModuleImport,\n    resolveModule,\n    programFiles,\n    entryFiles,\n    visitor,\n    getJSDocTags,\n  });\n\n  for (const comment of result.comments) {\n    const text = comment.value;\n\n    let results: RegExpExecArray | null;\n    if (comment.type === 'Block') {\n      jsDocImportRe.lastIndex = 0;\n      while ((results = jsDocImportRe.exec(text)) !== null) {\n        const before = text.slice(0, results.index);\n        const lastOpen = before.lastIndexOf('{');\n        if (lastOpen === -1 || before.indexOf('}', lastOpen) !== -1) continue;\n        const specifier = results[1];\n        const member = results[2];\n        addImport(specifier, member, undefined, undefined, comment.start + results.index, IMPORT_FLAGS.TYPE_ONLY);\n      }\n\n      jsDocImportTagRe.lastIndex = 0;\n      while ((results = jsDocImportTagRe.exec(text)) !== null) {\n        const specifier = results[1];\n        addImport(specifier, undefined, undefined, undefined, comment.start + results.index, IMPORT_FLAGS.TYPE_ONLY);\n      }\n    }\n\n    const jsxMatch = text.match(jsxImportSourceRe);\n    if (jsxMatch) {\n      addImport(jsxMatch[1], undefined, undefined, undefined, comment.start, IMPORT_FLAGS.TYPE_ONLY);\n    }\n\n    envRe.lastIndex = 0;\n    while ((results = envRe.exec(text)) !== null) {\n      const id = stripQuotes(results[1]);\n      if (!id) continue;\n      const isLocal = id.startsWith('.') || id.startsWith('/');\n      const modifiers = isLocal ? IMPORT_FLAGS.ENTRY : IMPORT_FLAGS.NONE;\n      addImport(id, undefined, undefined, undefined, comment.start + results.index, modifiers);\n    }\n\n    if (comment.type === 'Line') {\n      const refMatch = ('/' + comment.value).match(referenceTypesRe);\n      if (refMatch) {\n        addImport(refMatch[1], undefined, undefined, undefined, comment.start, IMPORT_FLAGS.TYPE_ONLY);\n      }\n    }\n  }\n\n  const localRefs = ignoreExportsUsedInFile ? collectLocalRefs(result.program, localImportMap, exports) : undefined;\n\n  for (const [id, item] of exports) {\n    item.referencedIn = referencedInExport.get(id);\n    if (localRefs && shouldCountRefs(ignoreExportsUsedInFile, item.type) && (localRefs.has(id) || item.isReExport)) {\n      item.hasRefsInFile = true;\n    }\n  }\n\n  return {\n    imports: { internal, external, externalRefs: new Set(), programFiles, entryFiles, imports, unresolved },\n    exports,\n    duplicates: [...aliasedExports.values()],\n    scripts,\n    importedBy: undefined,\n    internalImportCache: undefined,\n  };\n};\n\nexport const _getImportsAndExports = timerify(getImportsAndExports);\n"
  },
  {
    "path": "packages/knip/src/typescript/resolve-module-names.ts",
    "content": "import { existsSync } from 'node:fs';\nimport { isBuiltin } from 'node:module';\nimport { DEFAULT_EXTENSIONS, DTS_EXTENSIONS } from '../constants.ts';\nimport { sanitizeSpecifier } from '../util/modules.ts';\nimport { timerify } from '../util/Performance.ts';\nimport { dirname, extname, isAbsolute, isInNodeModules, join } from '../util/path.ts';\nimport { _createSyncModuleResolver, _resolveModuleSync, convertPathsToAlias } from '../util/resolve.ts';\nimport type { ToSourceFilePath } from '../util/to-source-path.ts';\nimport type { ResolveModule, ResolvedModule } from './visitors/helpers.ts';\n\nexport function createCustomModuleResolver(\n  compilerOptions: { paths?: Record<string, string[]> },\n  customCompilerExtensions: string[],\n  toSourceFilePath: ToSourceFilePath\n): ResolveModule {\n  const customCompilerExtensionsSet = new Set(customCompilerExtensions);\n  const hasCustomExts = customCompilerExtensionsSet.size > 0;\n  const extensions = [...DEFAULT_EXTENSIONS, ...customCompilerExtensions, ...DTS_EXTENSIONS];\n  const alias = convertPathsToAlias(compilerOptions.paths as Record<string, string[]>);\n  const resolveSync = hasCustomExts ? _createSyncModuleResolver(extensions) : _resolveModuleSync;\n  const resolveWithAlias = alias ? _createSyncModuleResolver(extensions, alias) : undefined;\n\n  function toSourcePath(resolvedFileName: string): string {\n    if (!hasCustomExts || !customCompilerExtensionsSet.has(extname(resolvedFileName))) {\n      return toSourceFilePath(resolvedFileName) || resolvedFileName;\n    }\n    return resolvedFileName;\n  }\n\n  function toResult(resolvedFileName: string): ResolvedModule {\n    const mapped = toSourcePath(resolvedFileName);\n    return {\n      resolvedFileName: mapped,\n      isExternalLibraryImport: mapped === resolvedFileName && isInNodeModules(resolvedFileName),\n    };\n  }\n\n  function resolveModuleName(name: string, containingFile: string): ResolvedModule | undefined {\n    const sanitizedSpecifier = sanitizeSpecifier(name);\n\n    if (isBuiltin(sanitizedSpecifier)) return undefined;\n\n    const resolvedFileName = resolveSync(sanitizedSpecifier, containingFile);\n    if (resolvedFileName) return toResult(resolvedFileName);\n\n    if (resolveWithAlias) {\n      const aliasResolved = resolveWithAlias(sanitizedSpecifier, containingFile);\n      if (aliasResolved) return toResult(aliasResolved);\n    }\n\n    const candidate = isAbsolute(sanitizedSpecifier)\n      ? sanitizedSpecifier\n      : join(dirname(containingFile), sanitizedSpecifier);\n    if (existsSync(candidate)) {\n      return { resolvedFileName: candidate, isExternalLibraryImport: false };\n    }\n  }\n\n  return timerify(resolveModuleName);\n}\n"
  },
  {
    "path": "packages/knip/src/typescript/visitors/calls.ts",
    "content": "import type { CallExpression, NewExpression } from 'oxc-parser';\nimport { IMPORT_FLAGS, OPAQUE } from '../../constants.ts';\nimport { addValue } from '../../util/module-graph.ts';\nimport { getStringValue, isStringLiteral } from './helpers.ts';\nimport type { WalkState } from './walk.ts';\n\nexport function handleCallExpression(node: CallExpression, s: WalkState) {\n  if (\n    node.callee.type === 'Identifier' &&\n    node.callee.name === 'require' &&\n    node.arguments.length === 1 &&\n    isStringLiteral(node.arguments[0])\n  ) {\n    const specifier = getStringValue(node.arguments[0])!;\n    const reqTags = s.currentVarDeclStart >= 0 ? s.getJSDocTags(s.currentVarDeclStart) : undefined;\n    s.addImport(\n      specifier,\n      'default',\n      undefined,\n      undefined,\n      node.arguments[0].start,\n      IMPORT_FLAGS.NONE,\n      undefined,\n      reqTags\n    );\n    return;\n  }\n\n  if (\n    node.callee.type === 'MemberExpression' &&\n    node.callee.object.type === 'Identifier' &&\n    node.callee.object.name === 'require' &&\n    !node.callee.computed &&\n    node.callee.property.name === 'resolve' &&\n    node.arguments.length >= 1 &&\n    isStringLiteral(node.arguments[0])\n  ) {\n    const specifier = getStringValue(node.arguments[0])!;\n    s.addImport(specifier, undefined, undefined, undefined, node.arguments[0].start, IMPORT_FLAGS.ENTRY);\n    return;\n  }\n\n  if (\n    node.callee.type === 'MemberExpression' &&\n    node.callee.object.type === 'MetaProperty' &&\n    !node.callee.computed &&\n    node.callee.property.name === 'resolve' &&\n    node.arguments.length >= 1 &&\n    isStringLiteral(node.arguments[0])\n  ) {\n    const specifier = getStringValue(node.arguments[0])!;\n    s.addImport(specifier, undefined, undefined, undefined, node.arguments[0].start, IMPORT_FLAGS.ENTRY);\n    return;\n  }\n\n  if (\n    node.callee.type === 'MemberExpression' &&\n    node.callee.object.type === 'Identifier' &&\n    node.callee.object.name === 'module' &&\n    !node.callee.computed &&\n    node.callee.property.name === 'register' &&\n    node.arguments.length >= 1 &&\n    isStringLiteral(node.arguments[0])\n  ) {\n    const specifier = getStringValue(node.arguments[0])!;\n    if (specifier) s.addImport(specifier, undefined, undefined, undefined, node.arguments[0].start, IMPORT_FLAGS.ENTRY);\n    return;\n  }\n\n  if (\n    s.hasNodeModuleImport &&\n    node.callee.type === 'Identifier' &&\n    node.callee.name === 'register' &&\n    node.arguments.length >= 1 &&\n    isStringLiteral(node.arguments[0])\n  ) {\n    const specifier = getStringValue(node.arguments[0])!;\n    const arg1 = node.arguments[1];\n    if (\n      specifier &&\n      (!specifier.startsWith('.') ||\n        (arg1?.type === 'MemberExpression' &&\n          !arg1.computed &&\n          arg1.object.type === 'MetaProperty' &&\n          arg1.property.name === 'url'))\n    ) {\n      s.addImport(specifier, undefined, undefined, undefined, node.arguments[0].start, IMPORT_FLAGS.ENTRY);\n      return;\n    }\n  }\n\n  if (\n    node.callee.type === 'MemberExpression' &&\n    !node.callee.computed &&\n    node.callee.object.type === 'Identifier' &&\n    node.callee.object.name === 'Object' &&\n    node.callee.property.type === 'Identifier' &&\n    (node.callee.property.name === 'keys' ||\n      node.callee.property.name === 'values' ||\n      node.callee.property.name === 'entries' ||\n      node.callee.property.name === 'getOwnPropertyNames')\n  ) {\n    for (const arg of node.arguments) {\n      if (arg.type === 'Identifier') {\n        const _import = s.localImportMap.get(arg.name);\n        if (_import) {\n          const internalImport = s.internal.get(_import.filePath);\n          if (internalImport) {\n            if (_import.isNamespace) addValue(internalImport.import, OPAQUE, s.filePath);\n            else internalImport.refs.add(arg.name);\n          }\n        }\n      }\n    }\n  }\n\n  const markRefIfNs = (name: string) => {\n    const _import = s.localImportMap.get(name);\n    if (_import?.isNamespace) {\n      const internalImport = s.internal.get(_import.filePath);\n      if (internalImport) internalImport.refs.add(name);\n    }\n  };\n  for (const arg of node.arguments) {\n    if (arg.type === 'Identifier') markRefIfNs(arg.name);\n    else if (arg.type === 'ArrayExpression') {\n      for (const el of arg.elements ?? []) {\n        if (el?.type === 'Identifier') markRefIfNs(el.name);\n      }\n    } else if (arg.type === 'ObjectExpression') {\n      for (const prop of arg.properties ?? []) {\n        if (prop.type === 'Property' && prop.shorthand && prop.value?.type === 'Identifier')\n          markRefIfNs(prop.value.name);\n        if (prop.type === 'SpreadElement' && prop.argument?.type === 'Identifier') markRefIfNs(prop.argument.name);\n      }\n    }\n  }\n}\n\nexport function handleNewExpression(node: NewExpression, s: WalkState) {\n  if (\n    node.callee.type === 'Identifier' &&\n    node.callee.name === 'URL' &&\n    node.arguments.length >= 2 &&\n    isStringLiteral(node.arguments[0]) &&\n    node.arguments[1].type === 'MemberExpression' &&\n    !node.arguments[1].computed &&\n    node.arguments[1].object.type === 'MetaProperty' &&\n    node.arguments[1].property.name === 'url'\n  ) {\n    const specifier = getStringValue(node.arguments[0]);\n    if (specifier)\n      s.addImport(\n        specifier,\n        undefined,\n        undefined,\n        undefined,\n        node.arguments[0].start,\n        IMPORT_FLAGS.ENTRY | IMPORT_FLAGS.OPTIONAL\n      );\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/typescript/visitors/exports.ts",
    "content": "import type {\n  ExportNamedDeclaration,\n  ExportDefaultDeclaration,\n  ModuleExportName,\n  ObjectExpression,\n  TSExportAssignment,\n  ExpressionStatement,\n} from 'oxc-parser';\nimport { ALIAS_TAG, FIX_FLAGS, IMPORT_FLAGS, IMPORT_STAR, SYMBOL_TYPE } from '../../constants.ts';\nimport type { Fix } from '../../types/exports.ts';\nimport type { ExportMember } from '../../types/module-graph.ts';\nimport type { SymbolType } from '../../types/issues.ts';\nimport { addNsValue, addValue } from '../../util/module-graph.ts';\nimport {\n  extractEnumMembers,\n  extractNamespaceMembers,\n  getLineAndCol,\n  getStringValue,\n  isStringLiteral,\n} from './helpers.ts';\nimport { EMPTY_TAGS } from './jsdoc.ts';\nimport type { WalkState } from './walk.ts';\n\nconst getName = (n: ModuleExportName | null | undefined) => (n?.type === 'Identifier' ? n.name : undefined);\n\nexport function handleExportNamed(node: ExportNamedDeclaration, s: WalkState) {\n  if (s.skipExports || s.isInNamespace(node)) return;\n\n  if (node.source) {\n    const declTags = s.getJSDocTags(node.start);\n    for (const spec of node.specifiers) {\n      const exportedName = getName(spec.exported) ?? getName(spec.local);\n      if (exportedName) {\n        const isType = node.exportKind === 'type' || spec.exportKind === 'type';\n        const type = isType ? SYMBOL_TYPE.TYPE : SYMBOL_TYPE.UNKNOWN;\n        const fix =\n          (s.options.isFixExports && !isType) || (s.options.isFixTypes && isType)\n            ? [spec.start, spec.end, FIX_FLAGS.OBJECT_BINDING | FIX_FLAGS.EMPTY_DECLARATION]\n            : undefined;\n        const specTags = s.getJSDocTags(spec.start);\n        const tags = specTags.size ? new Set([...declTags, ...specTags]) : declTags;\n        s.addExport(exportedName, type, spec.exported?.start ?? spec.start, [], fix as Fix, true, tags);\n      }\n    }\n    return;\n  }\n\n  const decl = node.declaration;\n  if (decl) {\n    const exportStart = node.start;\n\n    if (decl.type === 'VariableDeclaration') {\n      for (const declarator of decl.declarations) {\n        if (declarator.id.type === 'ObjectPattern') {\n          for (const p of declarator.id.properties) {\n            if (p.type === 'RestElement' && p.argument?.type === 'Identifier') {\n              const name = p.argument.name;\n              const fix: Fix = s.options.isFixExports ? [p.start, p.end, FIX_FLAGS.OBJECT_BINDING] : undefined;\n              s.addExport(name, SYMBOL_TYPE.UNKNOWN, p.argument.start, [], fix, false, s.getJSDocTags(exportStart));\n              s.destructuredExports.add(name);\n            } else if (p.value?.type === 'Identifier') {\n              const name = p.value.name;\n              const fix: Fix = s.options.isFixExports ? [p.start, p.end, FIX_FLAGS.OBJECT_BINDING] : undefined;\n              s.addExport(name, SYMBOL_TYPE.UNKNOWN, p.value.start, [], fix, false, s.getJSDocTags(exportStart));\n              s.destructuredExports.add(name);\n            } else if (p.value?.type === 'AssignmentPattern' && p.value.left?.type === 'Identifier') {\n              const name = p.value.left.name;\n              const fix: Fix = s.options.isFixExports ? [p.start, p.end, FIX_FLAGS.OBJECT_BINDING] : undefined;\n              s.addExport(name, SYMBOL_TYPE.UNKNOWN, p.value.left.start, [], fix, false, s.getJSDocTags(exportStart));\n              s.destructuredExports.add(name);\n            }\n          }\n        } else if (declarator.id.type === 'ArrayPattern') {\n          for (const el of declarator.id.elements) {\n            if (el?.type === 'Identifier') {\n              const fix: Fix = s.options.isFixExports ? [el.start, el.end, FIX_FLAGS.NONE] : undefined;\n              s.addExport(el.name, SYMBOL_TYPE.UNKNOWN, el.start, [], fix, false, s.getJSDocTags(exportStart));\n              s.destructuredExports.add(el.name);\n            } else if (el?.type === 'RestElement' && el.argument?.type === 'Identifier') {\n              const fix: Fix = s.options.isFixExports ? [el.start, el.end, FIX_FLAGS.NONE] : undefined;\n              s.addExport(\n                el.argument.name,\n                SYMBOL_TYPE.UNKNOWN,\n                el.argument.start,\n                [],\n                fix,\n                false,\n                s.getJSDocTags(exportStart)\n              );\n              s.destructuredExports.add(el.argument.name);\n            }\n          }\n        } else if (declarator.id.type === 'Identifier') {\n          const name = declarator.id.name;\n          const fix = s.getFix(exportStart, exportStart + 7);\n          const jsDocTags = s.getJSDocTags(exportStart);\n\n          let isReExport = false;\n          if (declarator.init?.type === 'Identifier') {\n            const _import = s.localImportMap.get(declarator.init.name);\n            if (_import) {\n              isReExport = true;\n              const internalImport = s.internal.get(_import.filePath);\n              if (internalImport) {\n                if (_import.isNamespace) {\n                  addValue(internalImport.reExportNs, name, s.filePath);\n                } else if (_import.importedName !== name) {\n                  addNsValue(internalImport.reExportAs, _import.importedName, name, s.filePath);\n                } else {\n                  addValue(internalImport.reExport, _import.importedName, s.filePath);\n                }\n              }\n            }\n          }\n\n          if (declarator.init?.type === 'ObjectExpression') {\n            const findSpreads = (obj: ObjectExpression, path: string[]) => {\n              for (const prop of obj.properties) {\n                if (prop.type === 'SpreadElement' && prop.argument?.type === 'Identifier') {\n                  const _import = s.localImportMap.get(prop.argument.name);\n                  if (_import) {\n                    isReExport = true;\n                    const internalImport = s.internal.get(_import.filePath);\n                    if (internalImport) {\n                      addNsValue(internalImport.reExportAs, prop.argument.name, path.join('.'), s.filePath);\n                    }\n                    s.accessedAliases.add(name);\n                  }\n                } else if (\n                  prop.type === 'Property' &&\n                  prop.value?.type === 'ObjectExpression' &&\n                  prop.key?.type === 'Identifier'\n                ) {\n                  findSpreads(prop.value, [...path, prop.key.name]);\n                }\n              }\n            };\n            findSpreads(declarator.init, [name]);\n          }\n\n          s.addExport(name, SYMBOL_TYPE.UNKNOWN, declarator.id.start, [], fix, isReExport, jsDocTags);\n\n          if (!jsDocTags.has(ALIAS_TAG) && declarator.init?.type === 'Identifier') {\n            const initName = declarator.init.name;\n            const existingExport = s.exports.get(initName);\n            if (existingExport && !existingExport.isReExport) {\n              if (!s.aliasedExports.has(initName)) {\n                s.aliasedExports.set(initName, [\n                  { symbol: initName, pos: existingExport.pos, line: existingExport.line, col: existingExport.col },\n                ]);\n              }\n              const aliased = s.aliasedExports.get(initName);\n              if (aliased) {\n                const { line: l, col: c } = getLineAndCol(s.lineStarts, declarator.id.start);\n                aliased.push({ symbol: name, pos: declarator.id.start, line: l, col: c });\n              }\n            }\n          }\n        }\n      }\n    } else if ((decl.type === 'FunctionDeclaration' || decl.type === 'TSDeclareFunction') && decl.id) {\n      const fix = s.getFix(exportStart, exportStart + 7);\n      s.addExport(decl.id.name, SYMBOL_TYPE.FUNCTION, decl.id.start, [], fix, false, s.getJSDocTags(exportStart));\n      s.collectRefsInType(decl, decl.id.name, true);\n    } else if (decl.type === 'ClassDeclaration' && decl.id) {\n      const fix = s.getFix(exportStart, exportStart + 7);\n      s.addExport(decl.id.name, SYMBOL_TYPE.CLASS, decl.id.start, [], fix, false, s.getJSDocTags(exportStart));\n    } else if (decl.type === 'TSTypeAliasDeclaration') {\n      const fix = s.getTypeFix(exportStart, exportStart + 7);\n      s.addExport(decl.id.name, SYMBOL_TYPE.TYPE, decl.id.start, [], fix, false, s.getJSDocTags(exportStart));\n      s.collectRefsInType(decl.typeAnnotation, decl.id.name, false);\n    } else if (decl.type === 'TSInterfaceDeclaration') {\n      const fix = s.getTypeFix(exportStart, exportStart + 7);\n      s.addExport(decl.id.name, SYMBOL_TYPE.INTERFACE, decl.id.start, [], fix, false, s.getJSDocTags(exportStart));\n      s.collectRefsInType(decl.body, decl.id.name, false);\n    } else if (decl.type === 'TSEnumDeclaration') {\n      const members = extractEnumMembers(decl, s.options, s.lineStarts, s.getJSDocTags);\n      const fix = s.getTypeFix(exportStart, exportStart + 7);\n      s.addExport(decl.id.name, SYMBOL_TYPE.ENUM, decl.id.start, members, fix, false, s.getJSDocTags(exportStart));\n    } else if (decl.type === 'TSModuleDeclaration' && decl.kind !== 'global' && decl.id.type === 'Identifier') {\n      const members = extractNamespaceMembers(decl, s.options, s.lineStarts, s.getJSDocTags);\n      const fix = s.getFix(exportStart, exportStart + 7);\n      s.addExport(decl.id.name, SYMBOL_TYPE.NAMESPACE, decl.id.start, members, fix, false, s.getJSDocTags(exportStart));\n    }\n  }\n\n  if (node.specifiers && !node.source) {\n    for (const spec of node.specifiers) {\n      const exportedName = getName(spec.exported) ?? getName(spec.local);\n      const localName = getName(spec.local);\n      const isType = node.exportKind === 'type' || spec.exportKind === 'type';\n      const type = isType ? SYMBOL_TYPE.TYPE : SYMBOL_TYPE.UNKNOWN;\n      const fix =\n        (s.options.isFixExports && !isType) || (s.options.isFixTypes && isType)\n          ? [spec.start, spec.end, FIX_FLAGS.OBJECT_BINDING | FIX_FLAGS.EMPTY_DECLARATION]\n          : undefined;\n\n      const _import = localName ? s.localImportMap.get(localName) : undefined;\n      const isReExport = !!_import;\n\n      if (_import) {\n        const internalImport = s.internal.get(_import.filePath);\n        if (internalImport) {\n          if (_import.isNamespace) {\n            addValue(internalImport.reExportNs, exportedName!, s.filePath);\n          } else if (_import.importedName !== exportedName) {\n            addNsValue(internalImport.reExportAs, _import.importedName, exportedName!, s.filePath);\n          } else {\n            addValue(internalImport.reExport, _import.importedName, s.filePath);\n          }\n        }\n      }\n\n      s.addExport(\n        exportedName!,\n        type,\n        spec.exported?.start ?? spec.start,\n        [],\n        fix as Fix,\n        isReExport,\n        s.getJSDocTags(node.start)\n      );\n      if (exportedName) s.specifierExportNames.add(exportedName);\n    }\n  }\n}\n\nexport function handleExportDefault(node: ExportDefaultDeclaration, s: WalkState) {\n  if (s.skipExports || s.isInNamespace(node)) return;\n\n  const decl = node.declaration;\n  const hasDeclarationBody = decl.type === 'ClassDeclaration' || decl.type === 'FunctionDeclaration';\n  const fix: Fix = s.options.isFixExports\n    ? hasDeclarationBody\n      ? [node.start, decl.start, FIX_FLAGS.NONE]\n      : [node.start, node.end + 1, FIX_FLAGS.NONE]\n    : undefined;\n\n  let type: SymbolType = SYMBOL_TYPE.UNKNOWN;\n  let pos = decl.start;\n  let members: ExportMember[] = [];\n\n  if (decl.type === 'FunctionDeclaration') {\n    type = SYMBOL_TYPE.FUNCTION;\n    pos = decl.id?.start ?? decl.start;\n    s.collectRefsInType(decl, 'default', false);\n  } else if (decl.type === 'ClassDeclaration') {\n    type = SYMBOL_TYPE.CLASS;\n    pos = decl.id?.start ?? decl.start;\n    members = [];\n  } else if (decl.type === 'TSInterfaceDeclaration') {\n    type = SYMBOL_TYPE.INTERFACE;\n    pos = decl.id.start;\n    s.collectRefsInType(decl.body, 'default', false);\n  } else if (decl.type === 'Identifier') {\n    type = s.localDeclarationTypes.get(decl.name) ?? SYMBOL_TYPE.UNKNOWN;\n    pos = decl.start;\n    const _import = s.localImportMap.get(decl.name);\n    if (_import) {\n      const internalImport = s.internal.get(_import.filePath);\n      if (internalImport) {\n        if (_import.importedName !== 'default') {\n          addNsValue(internalImport.reExportAs, _import.importedName, 'default', s.filePath);\n        } else {\n          addValue(internalImport.reExport, 'default', s.filePath);\n        }\n      }\n    }\n\n    const jsDocTags = s.getJSDocTags(node.start);\n    if (!jsDocTags.has(ALIAS_TAG) && !s.specifierExportNames.has(decl.name)) {\n      const existingExport = s.exports.get(decl.name);\n      if (existingExport) {\n        if (!s.aliasedExports.has(decl.name)) {\n          s.aliasedExports.set(decl.name, [\n            { symbol: decl.name, pos: existingExport.pos, line: existingExport.line, col: existingExport.col },\n          ]);\n        }\n        const aliased = s.aliasedExports.get(decl.name);\n        if (aliased) {\n          const { line: defLine, col: defCol } = getLineAndCol(s.lineStarts, decl.start);\n          aliased.push({ symbol: 'default', pos: decl.start, line: defLine, col: defCol });\n        }\n      }\n    }\n  }\n\n  s.addExport('default', type, pos, members, fix, false, s.getJSDocTags(node.start));\n}\n\nexport function handleExportAssignment(node: TSExportAssignment, s: WalkState) {\n  if (s.skipExports || s.isInNamespace(node)) return;\n  const expr = node.expression;\n  if (expr.type === 'Identifier') {\n    const _import = s.localImportMap.get(expr.name);\n    if (_import) {\n      const internalImport = s.internal.get(_import.filePath);\n      if (internalImport) {\n        addNsValue(internalImport.reExportAs, expr.name, 'default', s.filePath);\n        internalImport.refs.add(expr.name);\n      }\n      s.addExport('default', SYMBOL_TYPE.UNKNOWN, expr.start, [], undefined, true, s.getJSDocTags(node.start));\n    } else {\n      s.addExport('default', SYMBOL_TYPE.UNKNOWN, expr.start, [], undefined, false, s.getJSDocTags(node.start));\n    }\n  } else {\n    s.addExport('default', SYMBOL_TYPE.UNKNOWN, expr.start, [], undefined, false, s.getJSDocTags(node.start));\n  }\n}\n\nexport function handleExpressionStatement(node: ExpressionStatement, s: WalkState) {\n  if (node.expression.type === 'Identifier') {\n    if (s.destructuredExports.has(node.expression.name)) s.bareExprRefs.add(node.expression.name);\n  }\n  if (!s.isJS) return;\n  const expr = node.expression;\n  if (expr.type !== 'AssignmentExpression' || expr.operator !== '=') return;\n  const left = expr.left;\n\n  if (left.type !== 'MemberExpression') return;\n\n  if (!left.computed && left.object.type === 'Identifier' && left.object.name === 'exports') {\n    if (s.skipExports) return;\n    const name = left.property.name;\n    if (name) {\n      const fix: Fix = s.options.isFixExports ? [node.start, node.end, FIX_FLAGS.NONE] : undefined;\n      s.addExport(name, SYMBOL_TYPE.UNKNOWN, left.property.start, [], fix, false, EMPTY_TAGS);\n    }\n    return;\n  }\n\n  if (\n    left.object.type === 'MemberExpression' &&\n    !left.object.computed &&\n    left.object.object.type === 'Identifier' &&\n    left.object.object.name === 'module' &&\n    left.object.property.name === 'exports'\n  ) {\n    let exportName: string | undefined;\n    if (!left.computed && left.property.type === 'Identifier') {\n      exportName = left.property.name;\n    } else if (left.computed && isStringLiteral(left.property)) {\n      exportName = getStringValue(left.property);\n    }\n\n    if (exportName) {\n      const right = expr.right;\n      let isReExport = false;\n      if (\n        right.type === 'MemberExpression' &&\n        right.object.type === 'CallExpression' &&\n        right.object.callee.type === 'Identifier' &&\n        right.object.callee.name === 'require' &&\n        right.object.arguments.length === 1 &&\n        isStringLiteral(right.object.arguments[0])\n      ) {\n        const specifier = getStringValue(right.object.arguments[0])!;\n        let memberName: string | undefined;\n        if (!right.computed && right.property.type === 'Identifier') memberName = right.property.name;\n        else if (right.computed && isStringLiteral(right.property)) memberName = getStringValue(right.property);\n        if (memberName) {\n          const alias = exportName !== memberName ? exportName : undefined;\n          s.addImport(specifier, memberName, alias, undefined, right.object.arguments[0].start, IMPORT_FLAGS.RE_EXPORT);\n          isReExport = true;\n        }\n      }\n\n      if (!s.skipExports) {\n        const fix: Fix = s.options.isFixExports ? [node.start, node.end, FIX_FLAGS.NONE] : undefined;\n        s.addExport(exportName, SYMBOL_TYPE.UNKNOWN, left.property.start, [], fix, isReExport, EMPTY_TAGS);\n      }\n    }\n    return;\n  }\n\n  if (\n    !left.computed &&\n    left.object.type === 'Identifier' &&\n    left.object.name === 'module' &&\n    left.property.name === 'exports'\n  ) {\n    const right = expr.right;\n    if (right.type === 'ObjectExpression') {\n      if (s.skipExports) return;\n      const props = right.properties;\n      const allShorthand = props.length > 0 && props.every(p => p.type === 'Property' && p.shorthand);\n      if (allShorthand) {\n        for (const prop of props) {\n          if (prop.type === 'Property' && prop.key.type === 'Identifier') {\n            const fix: Fix = s.options.isFixExports ? [prop.start, prop.end, FIX_FLAGS.NONE] : undefined;\n            s.addExport(prop.key.name, SYMBOL_TYPE.UNKNOWN, prop.key.start, [], fix, false, EMPTY_TAGS);\n          }\n        }\n      } else {\n        s.addExport('default', SYMBOL_TYPE.UNKNOWN, right.start, [], undefined, false, EMPTY_TAGS);\n      }\n      for (const prop of props) {\n        if (\n          prop.type === 'SpreadElement' &&\n          prop.argument.type === 'CallExpression' &&\n          prop.argument.callee.type === 'Identifier' &&\n          prop.argument.callee.name === 'require' &&\n          isStringLiteral(prop.argument.arguments[0])\n        ) {\n          const specifier = getStringValue(prop.argument.arguments[0])!;\n          s.addImport(\n            specifier,\n            IMPORT_STAR,\n            undefined,\n            undefined,\n            prop.argument.arguments[0].start,\n            IMPORT_FLAGS.RE_EXPORT\n          );\n        }\n      }\n    } else if (\n      right.type === 'CallExpression' &&\n      right.callee.type === 'Identifier' &&\n      right.callee.name === 'require'\n    ) {\n      if (isStringLiteral(right.arguments[0])) {\n        const specifier = getStringValue(right.arguments[0])!;\n        s.addImport(specifier, IMPORT_STAR, undefined, undefined, right.arguments[0].start, IMPORT_FLAGS.RE_EXPORT);\n      }\n    } else if (!s.skipExports) {\n      s.addExport('default', SYMBOL_TYPE.UNKNOWN, right.start, [], undefined, false, EMPTY_TAGS);\n    }\n    return;\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/typescript/visitors/helpers.ts",
    "content": "import {\n  parseSync,\n  rawTransferSupported,\n  type TSEnumDeclaration,\n  type TSEnumMember,\n  type TSModuleDeclaration,\n} from 'oxc-parser';\nimport { DEFAULT_EXTENSIONS, FIX_FLAGS, SYMBOL_TYPE } from '../../constants.ts';\nimport { extname } from '../../util/path.ts';\nimport type { GetImportsAndExportsOptions, IgnoreExportsUsedInFile } from '../../types/config.ts';\nimport type { Fix } from '../../types/exports.ts';\nimport type { SymbolType } from '../../types/issues.ts';\nimport type { ExportMember } from '../../types/module-graph.ts';\n\nconst defaultParseOptions = {\n  sourceType: 'unambiguous' as const,\n  experimentalRawTransfer: rawTransferSupported(),\n};\n\nexport const parseFile = (filePath: string, sourceText: string) => {\n  const ext = extname(filePath);\n  const parseFileName = DEFAULT_EXTENSIONS.has(ext) ? filePath : `${filePath}.ts`;\n  return parseSync(parseFileName, sourceText, defaultParseOptions);\n};\n\nexport type ResolveModule = (specifier: string, containingFile: string) => ResolvedModule | undefined;\n\nexport interface ResolvedModule {\n  resolvedFileName: string;\n  isExternalLibraryImport: boolean;\n}\n\nexport const buildLineStarts = (sourceText: string): number[] => {\n  const starts = [0];\n  for (let i = 0; i < sourceText.length; i++) {\n    if (sourceText.charCodeAt(i) === 10) starts.push(i + 1);\n  }\n  return starts;\n};\n\nexport const getLineAndCol = (lineStarts: number[], pos: number): { line: number; col: number } => {\n  let lo = 0;\n  let hi = lineStarts.length - 1;\n  while (lo < hi) {\n    const mid = (lo + hi + 1) >> 1;\n    if (lineStarts[mid] <= pos) lo = mid;\n    else hi = mid - 1;\n  }\n  return { line: lo + 1, col: pos - lineStarts[lo] + 1 };\n};\n\nconst isQuoteOrBacktick = (ch: number) => ch === 39 || ch === 34 || ch === 96;\n\nexport const stripQuotes = (name: string) => {\n  const length = name.length;\n  if (length >= 2 && name.charCodeAt(0) === name.charCodeAt(length - 1) && isQuoteOrBacktick(name.charCodeAt(0)))\n    return name.substring(1, length - 1);\n  return name;\n};\n\nexport const isStringLiteral = (node: any): boolean =>\n  node?.type === 'StringLiteral' ||\n  (node?.type === 'Literal' && typeof node.value === 'string') ||\n  (node?.type === 'TemplateLiteral' && node.quasis?.length === 1 && node.expressions?.length === 0);\n\nexport const getStringValue = (node: any): string | undefined => {\n  if (node?.type === 'StringLiteral') return node.value;\n  if (node?.type === 'Literal' && typeof node.value === 'string') return node.value;\n  if (node?.type === 'TemplateLiteral' && node.quasis?.length === 1 && node.expressions?.length === 0)\n    return node.quasis[0].value?.cooked ?? node.quasis[0].value?.raw;\n  return undefined;\n};\n\nexport const shouldCountRefs = (ignoreExportsUsedInFile: IgnoreExportsUsedInFile, type: SymbolType) =>\n  ignoreExportsUsedInFile === true ||\n  (typeof ignoreExportsUsedInFile === 'object' && type !== 'unknown' && ignoreExportsUsedInFile[type]);\n\nexport function extractNamespaceMembers(\n  decl: TSModuleDeclaration,\n  options: GetImportsAndExportsOptions,\n  lineStarts: number[],\n  getJSDocTags: (start: number) => Set<string>,\n  prefix?: string\n): ExportMember[] {\n  if (!decl.body || decl.body.type !== 'TSModuleBlock') return [];\n  const members: ExportMember[] = [];\n\n  const addMember = (name: string, pos: number, stmtStart: number, stmtEnd: number) => {\n    const fullName = prefix ? `${prefix}.${name}` : name;\n    const { line, col } = getLineAndCol(lineStarts, pos);\n    const fix: Fix = options.isFixExports\n      ? [stmtStart, stmtEnd, FIX_FLAGS.OBJECT_BINDING | FIX_FLAGS.WITH_NEWLINE]\n      : undefined;\n    members.push({\n      identifier: fullName,\n      type: SYMBOL_TYPE.MEMBER as SymbolType,\n      pos,\n      line,\n      col,\n      fix,\n      hasRefsInFile: false,\n      jsDocTags: getJSDocTags(stmtStart),\n      flags: 0,\n    });\n  };\n\n  for (const stmt of decl.body.body) {\n    if (stmt.type !== 'ExportNamedDeclaration' || !stmt.declaration) continue;\n    const d = stmt.declaration;\n\n    if (d.type === 'VariableDeclaration') {\n      for (const declarator of d.declarations) {\n        if (declarator.id.type === 'Identifier') {\n          addMember(declarator.id.name, declarator.id.start, stmt.start, stmt.end);\n        }\n      }\n    } else if (d.type === 'TSModuleDeclaration' && d.kind !== 'global' && d.id.type === 'Identifier') {\n      const nestedPrefix = prefix ? `${prefix}.${d.id.name}` : d.id.name;\n      const nested = extractNamespaceMembers(d as TSModuleDeclaration, options, lineStarts, getJSDocTags, nestedPrefix);\n      for (const m of nested) members.push(m);\n    } else if (d.id && 'name' in d.id) {\n      addMember(d.id.name, d.id.start, stmt.start, stmt.end);\n    }\n  }\n  return members;\n}\n\nexport function extractEnumMembers(\n  decl: TSEnumDeclaration,\n  options: GetImportsAndExportsOptions,\n  lineStarts: number[],\n  getJSDocTags: (start: number) => Set<string>\n): ExportMember[] {\n  if (!decl.body?.members) return [];\n  return decl.body.members.map((member: TSEnumMember) => {\n    const name =\n      member.id.type === 'Identifier'\n        ? member.id.name\n        : member.id.type === 'Literal'\n          ? member.id.raw\n            ? stripQuotes(member.id.raw)\n            : member.id.value\n          : '';\n    const pos = member.id.start;\n    const { line, col } = getLineAndCol(lineStarts, pos);\n    const fix: Fix = options.isFixExports\n      ? [member.start, member.end, FIX_FLAGS.OBJECT_BINDING | FIX_FLAGS.WITH_NEWLINE]\n      : undefined;\n    const jsDocTags = getJSDocTags(member.start);\n    return {\n      identifier: name,\n      type: SYMBOL_TYPE.MEMBER as SymbolType,\n      pos,\n      line,\n      col,\n      fix,\n      hasRefsInFile: name === '',\n      jsDocTags,\n      flags: 0,\n    };\n  });\n}\n"
  },
  {
    "path": "packages/knip/src/typescript/visitors/imports.ts",
    "content": "import type { Expression, ImportExpression, VariableDeclarator } from 'oxc-parser';\nimport { IMPORT_FLAGS, IMPORT_STAR, OPAQUE } from '../../constants.ts';\nimport { addValue } from '../../util/module-graph.ts';\nimport { isInNodeModules } from '../../util/path.ts';\nimport { getStringValue, isStringLiteral } from './helpers.ts';\nimport type { WalkState } from './walk.ts';\n\nexport function handleVariableDeclarator(node: VariableDeclarator, s: WalkState) {\n  const init = node.init;\n  if (!init) return;\n  let importExpr: ImportExpression | null = null;\n  if (init.type === 'AwaitExpression' && init.argument.type === 'ImportExpression') {\n    importExpr = init.argument;\n  } else if (init.type === 'ImportExpression') {\n    importExpr = init;\n  }\n\n  if (importExpr && isStringLiteral(importExpr.source)) {\n    s.handledImportExpressions.add(importExpr.start);\n    const specifier = getStringValue(importExpr.source)!;\n\n    if (node.id.type === 'ObjectPattern') {\n      for (const prop of node.id.properties) {\n        if (prop.type === 'Property') {\n          const importedName =\n            prop.key?.type === 'Identifier'\n              ? prop.key.name\n              : prop.key?.type === 'Literal' && typeof prop.key.value === 'string'\n                ? prop.key.value\n                : undefined;\n          const localName = prop.value?.type === 'Identifier' ? prop.value.name : importedName;\n          const alias = localName !== importedName ? localName : undefined;\n          s.addImport(specifier, importedName, alias, undefined, prop.key?.start ?? prop.start, IMPORT_FLAGS.NONE);\n        } else if (prop.type === 'RestElement' && prop.argument?.type === 'Identifier') {\n          s.addImport(specifier, IMPORT_STAR, prop.argument.name, undefined, prop.start, IMPORT_FLAGS.NONE);\n        }\n      }\n    } else if (node.id.type === 'Identifier') {\n      if (init.type === 'AwaitExpression') {\n        s.addImport(specifier, 'default', node.id.name, undefined, importExpr.source.start, IMPORT_FLAGS.NONE);\n        const resolved = s.resolveModule(specifier, s.filePath);\n        if (resolved && !resolved.isExternalLibraryImport && !isInNodeModules(resolved.resolvedFileName)) {\n          s.localImportMap.set(node.id.name, {\n            importedName: IMPORT_STAR,\n            filePath: resolved.resolvedFileName,\n            isNamespace: true,\n            isDynamicImport: true,\n          });\n        }\n      } else {\n        s.addImport(specifier, undefined, undefined, undefined, importExpr.source.start, IMPORT_FLAGS.OPAQUE);\n      }\n    } else {\n      s.addImport(specifier, undefined, undefined, undefined, importExpr.source.start, IMPORT_FLAGS.OPAQUE);\n    }\n    return;\n  }\n\n  if (\n    init.type === 'CallExpression' &&\n    init.callee.type === 'Identifier' &&\n    init.callee.name === 'require' &&\n    init.arguments.length === 1 &&\n    isStringLiteral(init.arguments[0])\n  ) {\n    const specifier = getStringValue(init.arguments[0])!;\n    const reqTags = s.currentVarDeclStart >= 0 ? s.getJSDocTags(s.currentVarDeclStart) : undefined;\n    if (node.id.type === 'ObjectPattern') {\n      for (const prop of node.id.properties) {\n        if (prop.type === 'Property') {\n          const importedName =\n            prop.key?.type === 'Identifier'\n              ? prop.key.name\n              : prop.key?.type === 'Literal' && typeof prop.key.value === 'string'\n                ? prop.key.value\n                : undefined;\n          const localName = prop.value?.type === 'Identifier' ? prop.value.name : importedName;\n          const alias = localName !== importedName ? localName : undefined;\n          s.addImport(\n            specifier,\n            importedName,\n            alias,\n            undefined,\n            prop.key?.start ?? prop.start,\n            IMPORT_FLAGS.NONE,\n            undefined,\n            reqTags\n          );\n        } else if (prop.type === 'RestElement' && prop.argument?.type === 'Identifier') {\n          s.addImport(\n            specifier,\n            IMPORT_STAR,\n            prop.argument.name,\n            undefined,\n            prop.start,\n            IMPORT_FLAGS.NONE,\n            undefined,\n            reqTags\n          );\n        }\n      }\n    } else if (node.id.type === 'Identifier') {\n      s.addImport(\n        specifier,\n        'default',\n        node.id.name,\n        undefined,\n        init.arguments[0].start,\n        IMPORT_FLAGS.NONE,\n        undefined,\n        reqTags\n      );\n    } else {\n      s.addImport(\n        specifier,\n        undefined,\n        undefined,\n        undefined,\n        init.arguments[0].start,\n        IMPORT_FLAGS.SIDE_EFFECTS,\n        undefined,\n        reqTags\n      );\n    }\n    return;\n  }\n\n  if (\n    init.type === 'AwaitExpression' &&\n    init.argument.type === 'CallExpression' &&\n    init.argument.callee.type === 'MemberExpression' &&\n    !init.argument.callee.computed &&\n    init.argument.callee.object.type === 'Identifier' &&\n    init.argument.callee.object.name === 'Promise' &&\n    init.argument.callee.property.name === 'all' &&\n    init.argument.arguments[0]?.type === 'ArrayExpression' &&\n    node.id.type === 'ArrayPattern'\n  ) {\n    const imports = init.argument.arguments[0].elements;\n    const bindings = node.id.elements;\n    for (let i = 0; i < imports.length; i++) {\n      const imp = imports[i];\n      const binding = bindings[i];\n      if (imp?.type === 'ImportExpression' && isStringLiteral(imp.source)) {\n        s.handledImportExpressions.add(imp.start);\n        const specifier = getStringValue(imp.source)!;\n        if (binding?.type === 'ObjectPattern') {\n          for (const prop of binding.properties) {\n            if (prop.type === 'Property') {\n              const importedName =\n                prop.key?.type === 'Identifier'\n                  ? prop.key.name\n                  : prop.key?.type === 'Literal' && typeof prop.key.value === 'string'\n                    ? prop.key.value\n                    : undefined;\n              s.addImport(\n                specifier,\n                importedName,\n                undefined,\n                undefined,\n                prop.key?.start ?? prop.start,\n                IMPORT_FLAGS.NONE\n              );\n            }\n          }\n        } else if (binding?.type === 'Identifier') {\n          s.addImport(specifier, 'default', binding.name, undefined, imp.source.start, IMPORT_FLAGS.NONE);\n        } else {\n          s.addImport(specifier, undefined, undefined, undefined, imp.source.start, IMPORT_FLAGS.SIDE_EFFECTS);\n        }\n      }\n    }\n  }\n\n  if (node.id.type === 'Identifier') {\n    const aliasName = node.id.name;\n    const registerAlias = (expr: Expression) => {\n      if (expr?.type === 'Identifier') {\n        const _import = s.localImportMap.get(expr.name);\n        if (_import) {\n          s.addImportAlias(aliasName, expr.name, _import.filePath);\n          if (_import.isNamespace) {\n            const internalImport = s.internal.get(_import.filePath);\n            if (internalImport) internalImport.refs.add(expr.name);\n          }\n        }\n      }\n    };\n    if (init.type === 'ConditionalExpression') {\n      registerAlias(init.consequent);\n      registerAlias(init.alternate);\n    } else if (init.type === 'Identifier') {\n      registerAlias(init);\n    }\n\n    if (init.type === 'ObjectExpression') {\n      for (const prop of init.properties) {\n        if (prop.type === 'SpreadElement' && prop.argument?.type === 'Identifier') {\n          const _import = s.localImportMap.get(prop.argument.name);\n          if (_import) {\n            s.addImportAlias(aliasName, prop.argument.name, _import.filePath);\n            if (_import.isNamespace) {\n              const internalImport = s.internal.get(_import.filePath);\n              if (internalImport) internalImport.refs.add(prop.argument.name);\n            }\n          }\n        }\n        if (prop.type === 'Property' && prop.shorthand && prop.value?.type === 'Identifier') {\n          const _import = s.localImportMap.get(prop.value.name);\n          if (_import?.isNamespace) {\n            const internalImport = s.internal.get(_import.filePath);\n            if (internalImport) internalImport.refs.add(prop.value.name);\n            let set = s.shorthandNsContainers.get(aliasName);\n            if (!set) {\n              set = new Set();\n              s.shorthandNsContainers.set(aliasName, set);\n            }\n            set.add(prop.value.name);\n          }\n        }\n      }\n    }\n  }\n\n  if (node.id.type === 'ObjectPattern') {\n    let rootName: string | undefined;\n    let memberPath: string[] = [];\n    if (init.type === 'Identifier') {\n      rootName = init.name;\n    } else if (init.type === 'MemberExpression' && !init.computed) {\n      const parts: string[] = [];\n      let cur: Expression = init;\n      while (cur.type === 'MemberExpression' && !cur.computed && cur.property.type === 'Identifier') {\n        parts.unshift(cur.property.name);\n        cur = cur.object;\n      }\n      if (cur.type === 'Identifier') {\n        rootName = cur.name;\n        memberPath = parts;\n      }\n    }\n    if (rootName) {\n      const _import = s.localImportMap.get(rootName);\n      if (_import) {\n        const internalImport = s.internal.get(_import.filePath);\n        if (internalImport) {\n          if (_import.isDynamicImport) {\n            for (const prop of node.id.properties) {\n              if (prop.type === 'Property' && prop.key?.type === 'Identifier') {\n                addValue(internalImport.import, prop.key.name, s.filePath);\n              } else if (prop.type === 'RestElement') {\n                addValue(internalImport.import, OPAQUE, s.filePath);\n              }\n            }\n          } else {\n            const ns = _import.isNamespace ? rootName : _import.importedName;\n            const prefix = [ns, ...memberPath].join('.');\n            for (const prop of node.id.properties) {\n              if (prop.type === 'Property' && prop.key?.type === 'Identifier') {\n                internalImport.refs.add(`${prefix}.${prop.key.name}`);\n              } else if (prop.type === 'RestElement') {\n                addValue(internalImport.import, OPAQUE, s.filePath);\n              }\n            }\n          }\n        }\n      }\n    }\n\n    for (const prop of node.id.properties) {\n      if (prop.type === 'Property' && prop.value?.type === 'AssignmentPattern') {\n        const defaultValue = prop.value.right;\n        if (defaultValue?.type === 'Identifier') {\n          const _import = s.localImportMap.get(defaultValue.name);\n          if (_import) {\n            const internalImport = s.internal.get(_import.filePath);\n            if (internalImport) {\n              if (_import.isNamespace) addValue(internalImport.import, OPAQUE, s.filePath);\n              else internalImport.refs.add(defaultValue.name);\n            }\n            if (prop.value.left?.type === 'Identifier') {\n              s.addImportAlias(prop.value.left.name, defaultValue.name, _import.filePath);\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\nexport function handleImportExpression(node: ImportExpression, s: WalkState) {\n  if (s.handledImportExpressions.has(node.start)) return;\n  const specifier = getStringValue(node.source);\n  if (specifier) {\n    s.addImport(specifier, undefined, undefined, undefined, node.source.start, IMPORT_FLAGS.OPAQUE);\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/typescript/visitors/jsdoc.ts",
    "content": "import type { Comment } from 'oxc-parser';\n\nexport const EMPTY_TAGS: Set<string> = new Set();\n\nexport function buildJSDocTagLookup(comments: Comment[], sourceText: string) {\n  const entries: { reach: number; tags: Set<string> }[] = [];\n\n  for (const comment of comments) {\n    if (comment.type !== 'Block') continue;\n    const value = comment.value;\n    let index = value.indexOf('@');\n    if (index === -1) continue;\n\n    let tags: Set<string> | undefined;\n    while (index !== -1) {\n      let end = index + 1;\n      while (\n        end < value.length &&\n        (((value.charCodeAt(end) | 32) >= 97 && (value.charCodeAt(end) | 32) <= 122) ||\n          (value.charCodeAt(end) >= 48 && value.charCodeAt(end) <= 57) ||\n          value.charCodeAt(end) === 95)\n      )\n        end++;\n      if (end > index + 1) {\n        if (!tags) tags = new Set();\n        tags.add(value.slice(index, end));\n      }\n      index = value.indexOf('@', end);\n    }\n    if (!tags) continue;\n\n    let reach = comment.end;\n    for (;;) {\n      while (reach < sourceText.length) {\n        const ch = sourceText.charCodeAt(reach);\n        if (ch === 32 || ch === 9 || ch === 10 || ch === 13) {\n          reach++;\n          continue;\n        }\n        break;\n      }\n      if (\n        reach + 1 < sourceText.length &&\n        sourceText.charCodeAt(reach) === 47 &&\n        sourceText.charCodeAt(reach + 1) === 47\n      ) {\n        const eol = sourceText.indexOf('\\n', reach + 2);\n        reach = eol === -1 ? sourceText.length : eol + 1;\n        continue;\n      }\n      break;\n    }\n    entries.push({ reach, tags });\n  }\n\n  if (entries.length === 0) return () => EMPTY_TAGS;\n\n  return function getJSDocTags(nodeStart: number): Set<string> {\n    let lo = 0;\n    let hi = entries.length - 1;\n    while (lo <= hi) {\n      const mid = (lo + hi) >> 1;\n      const r = entries[mid].reach;\n      if (r === nodeStart) return entries[mid].tags;\n      if (r < nodeStart) lo = mid + 1;\n      else hi = mid - 1;\n    }\n    return EMPTY_TAGS;\n  };\n}\n"
  },
  {
    "path": "packages/knip/src/typescript/visitors/local-refs.ts",
    "content": "import { Visitor, type Program, type TSTypeName } from 'oxc-parser';\nimport type { Export } from '../../types/module-graph.ts';\n\nlet refs: Set<string>;\nlet importNames: Map<string, unknown>;\nlet exportsMap: Map<string, Export>;\n\nconst add = (name: string) => {\n  if (!importNames.has(name)) refs.add(name);\n};\n\nconst visitor = new Visitor({\n  ClassDeclaration(node) {\n    if (node.superClass?.type === 'Identifier') add(node.superClass.name);\n    for (const impl of node.implements ?? []) {\n      if (impl.expression?.type === 'Identifier') add(impl.expression.name);\n    }\n  },\n  TSInterfaceDeclaration(node) {\n    for (const ext of node.extends ?? []) {\n      if (ext.expression?.type === 'Identifier') add(ext.expression.name);\n    }\n  },\n  Property(node) {\n    if (node.value?.type === 'Identifier') add(node.value.name);\n  },\n  ReturnStatement(node) {\n    if (node.argument?.type === 'Identifier') add(node.argument.name);\n  },\n  AssignmentExpression(node) {\n    if (node.right?.type === 'Identifier') add(node.right.name);\n  },\n  SpreadElement(node) {\n    if (node.argument?.type === 'Identifier') add(node.argument.name);\n  },\n  ConditionalExpression(node) {\n    if (node.test?.type === 'Identifier') add(node.test.name);\n    if (node.consequent?.type === 'Identifier') add(node.consequent.name);\n    if (node.alternate?.type === 'Identifier') add(node.alternate.name);\n  },\n  ArrayExpression(node) {\n    for (const el of node.elements ?? []) {\n      if (el?.type === 'Identifier') add(el.name);\n    }\n  },\n  TemplateLiteral(node) {\n    for (const expr of node.expressions ?? []) {\n      if (expr.type === 'Identifier') add(expr.name);\n    }\n  },\n  BinaryExpression(node) {\n    if (node.left?.type === 'Identifier') add(node.left.name);\n    if (node.right?.type === 'Identifier') add(node.right.name);\n  },\n  LogicalExpression(node) {\n    if (node.left?.type === 'Identifier') add(node.left.name);\n    if (node.right?.type === 'Identifier') add(node.right.name);\n  },\n  UnaryExpression(node) {\n    if (node.argument?.type === 'Identifier') add(node.argument.name);\n  },\n  SwitchStatement(node) {\n    if (node.discriminant?.type === 'Identifier') add(node.discriminant.name);\n    for (const c of node.cases ?? []) {\n      if (c.test?.type === 'Identifier') add(c.test.name);\n    }\n  },\n  IfStatement(node) {\n    if (node.test?.type === 'Identifier') add(node.test.name);\n  },\n  ThrowStatement(node) {\n    if (node.argument?.type === 'Identifier') add(node.argument.name);\n  },\n  WhileStatement(node) {\n    if (node.test?.type === 'Identifier') add(node.test.name);\n  },\n  DoWhileStatement(node) {\n    if (node.test?.type === 'Identifier') add(node.test.name);\n  },\n  YieldExpression(node) {\n    if (node.argument?.type === 'Identifier') add(node.argument.name);\n  },\n  AwaitExpression(node) {\n    if (node.argument?.type === 'Identifier') add(node.argument.name);\n  },\n  ArrowFunctionExpression(node) {\n    if (node.body?.type === 'Identifier') add(node.body.name);\n  },\n  AssignmentPattern(node) {\n    if (node.right?.type === 'Identifier') add(node.right.name);\n  },\n  SequenceExpression(node) {\n    for (const expr of node.expressions ?? []) {\n      if (expr.type === 'Identifier') add(expr.name);\n    }\n  },\n  TSAsExpression(node) {\n    if (node.expression?.type === 'Identifier') add(node.expression.name);\n  },\n  TSSatisfiesExpression(node) {\n    if (node.expression?.type === 'Identifier') add(node.expression.name);\n  },\n  TSNonNullExpression(node) {\n    if (node.expression?.type === 'Identifier') add(node.expression.name);\n  },\n  TSTypeAssertion(node) {\n    if (node.expression?.type === 'Identifier') add(node.expression.name);\n  },\n  ParenthesizedExpression(node) {\n    if (node.expression?.type === 'Identifier') add(node.expression.name);\n  },\n  PropertyDefinition(node) {\n    if (node.value?.type === 'Identifier') add(node.value.name);\n  },\n  ForInStatement(node) {\n    if (node.right?.type === 'Identifier') add(node.right.name);\n  },\n  ForOfStatement(node) {\n    if (node.right?.type === 'Identifier') add(node.right.name);\n  },\n  JSXOpeningElement(node) {\n    if (node.name?.type === 'JSXIdentifier') add(node.name.name);\n    for (const attr of node.attributes ?? []) {\n      if (attr.type === 'JSXSpreadAttribute' && attr.argument?.type === 'Identifier') add(attr.argument.name);\n    }\n  },\n  JSXExpressionContainer(node) {\n    if (node.expression?.type === 'Identifier') add(node.expression.name);\n  },\n  VariableDeclarator(node) {\n    if (node.init?.type === 'Identifier') add(node.init.name);\n  },\n  ExpressionStatement(node) {\n    if (node.expression?.type === 'Identifier') add(node.expression.name);\n  },\n  CallExpression(node) {\n    if (node.callee?.type === 'Identifier') add(node.callee.name);\n    for (const arg of node.arguments ?? []) {\n      if (arg.type === 'Identifier') add(arg.name);\n    }\n  },\n  NewExpression(node) {\n    if (node.callee?.type === 'Identifier') add(node.callee.name);\n    for (const arg of node.arguments ?? []) {\n      if (arg.type === 'Identifier') add(arg.name);\n    }\n  },\n  MemberExpression(node) {\n    if (node.object?.type === 'Identifier') add(node.object.name);\n    if (node.computed && node.property?.type === 'Identifier') add(node.property.name);\n  },\n  TaggedTemplateExpression(node) {\n    if (node.tag?.type === 'Identifier') add(node.tag.name);\n  },\n  TSQualifiedName(node) {\n    let left: TSTypeName = node;\n    const parts: string[] = [];\n    while (left.type === 'TSQualifiedName') {\n      if (left.right.type === 'Identifier') parts.unshift(left.right.name);\n      left = left.left;\n    }\n    if (left.type === 'Identifier') {\n      const rootName = left.name;\n      if (!importNames.has(rootName) && parts.length > 0) {\n        const exp = exportsMap.get(rootName);\n        if (exp) {\n          refs.add(rootName);\n          for (const member of exp.members) {\n            if (member.identifier === parts[0]) member.hasRefsInFile = true;\n          }\n        }\n      }\n    }\n  },\n  TSTypeReference(node) {\n    if (node.typeName?.type === 'Identifier') {\n      const name = node.typeName.name;\n      if (!importNames.has(name)) refs.add(name);\n    }\n  },\n  TSTypeQuery(node) {\n    if (node.exprName?.type === 'Identifier') {\n      const name = node.exprName.name;\n      if (!importNames.has(name)) refs.add(name);\n    }\n  },\n});\n\nexport function collectLocalRefs(\n  program: Program,\n  localImportMap: Map<string, { importedName: string; filePath: string; isNamespace: boolean }>,\n  fileExports: Map<string, Export>\n): Set<string> {\n  refs = new Set();\n  importNames = localImportMap;\n  exportsMap = fileExports;\n  visitor.visit(program);\n  return refs;\n}\n"
  },
  {
    "path": "packages/knip/src/typescript/visitors/members.ts",
    "content": "import type { MemberExpression, JSXMemberExpression } from 'oxc-parser';\nimport { OPAQUE } from '../../constants.ts';\nimport { addValue } from '../../util/module-graph.ts';\nimport { getStringValue, isStringLiteral } from './helpers.ts';\nimport type { WalkState } from './walk.ts';\n\nexport function handleMemberExpression(node: MemberExpression, s: WalkState) {\n  if (node.object.type === 'MemberExpression' && node.object.object.type === 'MemberExpression') {\n    s.chainedMemberExprs.add(node.object);\n  }\n\n  if (node.object.type === 'Identifier') {\n    const localName = node.object.name;\n    const _import = s.localImportMap.get(localName);\n    if (_import) {\n      const internalImport = s.internal.get(_import.filePath);\n      if (internalImport) {\n        let memberName: string | undefined;\n        if (node.computed === false && node.property.type === 'Identifier') {\n          memberName = node.property.name;\n        } else if (node.computed && isStringLiteral(node.property)) {\n          memberName = getStringValue(node.property);\n        } else if (node.computed && _import.isNamespace) {\n          addValue(internalImport.import, OPAQUE, s.filePath);\n          return;\n        }\n        if (memberName) {\n          if (_import.isDynamicImport) {\n            addValue(internalImport.import, memberName, s.filePath);\n          } else {\n            s.addNsMemberRefs(internalImport, localName, memberName);\n          }\n        }\n      }\n    } else {\n      const memberName =\n        node.computed === false && node.property.type === 'Identifier' ? node.property.name : undefined;\n      if (memberName) {\n        const exp = s.exports.get(localName);\n        if (exp) {\n          for (const member of exp.members) {\n            if (member.identifier === memberName) member.hasRefsInFile = true;\n          }\n        }\n      }\n    }\n\n    const aliases = s.importAliases.get(localName);\n    if (aliases) {\n      s.accessedAliases.add(localName);\n      let memberName: string | undefined;\n      if (node.computed === false && node.property.type === 'Identifier') {\n        memberName = node.property.name;\n      } else if (node.computed && isStringLiteral(node.property)) {\n        memberName = getStringValue(node.property);\n      }\n      if (memberName) {\n        for (const alias of aliases) {\n          const internalImport = s.internal.get(alias.filePath);\n          if (internalImport) {\n            s.addNsMemberRefs(internalImport, alias.id, memberName);\n          }\n        }\n      }\n    }\n  }\n\n  if (\n    node.object.type === 'MemberExpression' &&\n    !node.object.computed &&\n    node.object.object.type === 'Identifier' &&\n    node.object.property.type === 'Identifier' &&\n    !node.computed &&\n    node.property.type === 'Identifier'\n  ) {\n    const rootName = node.object.object.name;\n    const _import = s.localImportMap.get(rootName);\n    if (_import) {\n      const internalImport = s.internal.get(_import.filePath);\n      if (internalImport) {\n        const mid = node.object.property.name;\n        s.addNsMemberRefs(internalImport, rootName, mid);\n        if (!s.chainedMemberExprs.has(node)) {\n          s.addNsMemberRefs(internalImport, rootName, `${mid}.${node.property.name}`);\n        }\n      }\n      if (!_import.isNamespace) {\n        const mid = node.object.property.name;\n        const _import = s.localImportMap.get(mid);\n        if (_import) {\n          const midImport = s.internal.get(_import.filePath);\n          if (midImport) s.addNsMemberRefs(midImport, mid, node.property.name);\n        }\n      }\n    } else {\n      const exp = s.exports.get(rootName);\n      if (exp && exp.members.length > 0) {\n        const mid = node.object.property.name;\n        const dottedName = `${mid}.${node.property.name}`;\n        for (const member of exp.members) {\n          if (member.identifier === mid || member.identifier === dottedName) member.hasRefsInFile = true;\n        }\n      }\n    }\n  }\n\n  if (\n    node.object.type === 'MemberExpression' &&\n    !node.object.computed &&\n    node.object.object.type === 'MemberExpression' &&\n    !node.object.object.computed &&\n    node.object.object.object.type === 'Identifier' &&\n    node.object.object.property.type === 'Identifier' &&\n    node.object.property.type === 'Identifier' &&\n    !node.computed &&\n    node.property.type === 'Identifier'\n  ) {\n    const rootName = node.object.object.object.name;\n    const _import = s.localImportMap.get(rootName);\n    if (_import) {\n      const internalImport = s.internal.get(_import.filePath);\n      if (internalImport) {\n        const a = node.object.object.property.name;\n        const b = node.object.property.name;\n        const c = node.property.name;\n        s.addNsMemberRefs(internalImport, rootName, a);\n        s.addNsMemberRefs(internalImport, rootName, `${a}.${b}`);\n        s.addNsMemberRefs(internalImport, rootName, `${a}.${b}.${c}`);\n      }\n    } else {\n      const exp = s.exports.get(rootName);\n      if (exp && exp.members.length > 0) {\n        const a = node.object.object.property.name;\n        const b = node.object.property.name;\n        const c = node.property.name;\n        const dottedName = `${a}.${b}.${c}`;\n        for (const member of exp.members) {\n          if (member.identifier === a || member.identifier === `${a}.${b}` || member.identifier === dottedName)\n            member.hasRefsInFile = true;\n        }\n      }\n    }\n  }\n\n  if (\n    node.object.type === 'MemberExpression' &&\n    !node.object.computed &&\n    node.object.object.type === 'Identifier' &&\n    node.object.property.type === 'Identifier'\n  ) {\n    const containerName = node.object.object.name;\n    const nsName = node.object.property.name;\n    if (s.shorthandNsContainers.get(containerName)?.has(nsName)) {\n      const _import = s.localImportMap.get(nsName);\n      if (_import) {\n        const internalImport = s.internal.get(_import.filePath);\n        if (internalImport) {\n          let memberName: string | undefined;\n          if (!node.computed && node.property.type === 'Identifier') memberName = node.property.name;\n          else if (node.computed && isStringLiteral(node.property)) memberName = getStringValue(node.property);\n          if (memberName) {\n            s.addNsMemberRefs(internalImport, nsName, memberName);\n            s.accessedShorthandNs.add(`${containerName}.${nsName}`);\n          }\n        }\n      }\n    }\n  }\n}\n\nexport function handleJSXMemberExpression(node: JSXMemberExpression, s: WalkState) {\n  if (node.object.type === 'JSXIdentifier') {\n    const localName = node.object.name;\n    const memberName = node.property.type === 'JSXIdentifier' ? node.property.name : undefined;\n    if (memberName) {\n      const _import = s.localImportMap.get(localName);\n      if (_import) {\n        const internalImport = s.internal.get(_import.filePath);\n        if (internalImport) {\n          s.addNsMemberRefs(internalImport, localName, memberName);\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/typescript/visitors/script-visitors.ts",
    "content": "import type { PluginVisitorContext, PluginVisitorObject } from '../../types/config.ts';\n\nexport function createBunShellVisitor(ctx: PluginVisitorContext): PluginVisitorObject {\n  return {\n    TaggedTemplateExpression(node) {\n      const tag = node.tag;\n      if (tag.type === 'Identifier' && tag.name === '$') {\n        for (const q of node.quasi.quasis) {\n          if (q.value.raw) ctx.addScript(q.value.raw);\n        }\n      }\n    },\n  };\n}\n"
  },
  {
    "path": "packages/knip/src/typescript/visitors/walk.ts",
    "content": "import { Visitor, type Program, type Span, type TSTypeName, type VisitorObject } from 'oxc-parser';\nimport type { PluginVisitorObject } from '../../types/config.ts';\nimport { FIX_FLAGS, IMPORT_FLAGS, OPAQUE, SYMBOL_TYPE } from '../../constants.ts';\nimport type { GetImportsAndExportsOptions } from '../../types/config.ts';\nimport type { Fix } from '../../types/exports.ts';\nimport type { IssueSymbol, SymbolType } from '../../types/issues.ts';\nimport type { Export, ExportMember, ImportMap, ImportMaps } from '../../types/module-graph.ts';\nimport { addValue } from '../../util/module-graph.ts';\nimport { isInNodeModules } from '../../util/path.ts';\nimport { getLineAndCol, getStringValue, isStringLiteral, type ResolveModule } from './helpers.ts';\nimport { EMPTY_TAGS } from './jsdoc.ts';\nimport { handleCallExpression, handleNewExpression } from './calls.ts';\nimport {\n  handleExportAssignment,\n  handleExportDefault,\n  handleExportNamed,\n  handleExpressionStatement,\n} from './exports.ts';\nimport { handleImportExpression, handleVariableDeclarator } from './imports.ts';\nimport { handleJSXMemberExpression, handleMemberExpression } from './members.ts';\n\ninterface WalkContext {\n  lineStarts: number[];\n  skipExports: boolean;\n  options: GetImportsAndExportsOptions;\n  exports: Map<string, Export>;\n  aliasedExports: Map<string, IssueSymbol[]>;\n  specifierExportNames: Set<string>;\n  scripts: Set<string>;\n  addImport: (\n    specifier: string,\n    identifier: string | undefined,\n    alias: string | undefined,\n    namespace: string | undefined,\n    pos: number,\n    modifiers: number,\n    specifierPos?: number,\n    jsDocTags?: Set<string>\n  ) => void;\n  addNsMemberRefs: (internalImport: ImportMaps, namespace: string, member: string | string[]) => void;\n  addImportAlias: (aliasName: string, id: string, filePath: string) => void;\n  internal: ImportMap;\n  localImportMap: Map<\n    string,\n    { importedName: string; filePath: string; isNamespace: boolean; isDynamicImport?: boolean }\n  >;\n  localDeclarationTypes: Map<string, SymbolType>;\n  importAliases: Map<string, Set<{ id: string; filePath: string }>>;\n  referencedInExport: Map<string, Set<string>>;\n  skipBareExprRefs: boolean;\n  destructuredExports: Set<string>;\n  hasNodeModuleImport: boolean;\n  resolveModule: ResolveModule;\n  programFiles: Set<string>;\n  entryFiles: Set<string>;\n  visitor: Visitor;\n  getJSDocTags: (nodeStart: number) => Set<string>;\n}\n\nexport interface WalkState extends WalkContext {\n  filePath: string;\n  sourceText: string;\n  isJS: boolean;\n  handledImportExpressions: Set<number>;\n  bareExprRefs: Set<string>;\n  accessedAliases: Set<string>;\n  shorthandNsContainers: Map<string, Set<string>>;\n  accessedShorthandNs: Set<string>;\n  chainedMemberExprs: WeakSet<object>;\n  currentVarDeclStart: number;\n  nsRanges: [number, number][];\n  addExport: (\n    identifier: string,\n    type: SymbolType,\n    pos: number,\n    members: ExportMember[],\n    fix: Fix,\n    isReExport: boolean,\n    jsDocTags: Set<string>\n  ) => void;\n  getFix: (start: number, end: number, flags?: number) => Fix;\n  getTypeFix: (start: number, end: number) => Fix;\n  collectRefsInType: (node: any, exportName: string, signatureOnly: boolean) => void;\n  isInNamespace: (node: Span) => boolean;\n}\n\nlet state: WalkState;\n\nconst _getFix = (start: number, end: number, flags?: number): Fix =>\n  state.options.isFixExports ? [start, end, flags ?? FIX_FLAGS.NONE] : undefined;\n\nconst _getTypeFix = (start: number, end: number): Fix =>\n  state.options.isFixTypes ? [start, end, FIX_FLAGS.NONE] : undefined;\n\nconst _addExport = (\n  identifier: string,\n  type: SymbolType,\n  pos: number,\n  members: ExportMember[],\n  fix: Fix,\n  isReExport: boolean,\n  jsDocTags: Set<string>\n) => {\n  const item = state.exports.get(identifier);\n  if (item) {\n    if (members.length) for (const m of members) item.members.push(m);\n    if (fix) item.fixes.push(fix);\n    if (jsDocTags.size) {\n      if (item.jsDocTags === EMPTY_TAGS) {\n        item.jsDocTags = new Set(jsDocTags);\n      } else {\n        for (const t of jsDocTags) item.jsDocTags.add(t);\n      }\n    }\n    item.isReExport = isReExport;\n  } else {\n    const { line, col } = getLineAndCol(state.lineStarts, pos);\n    state.exports.set(identifier, {\n      identifier,\n      type,\n      members,\n      jsDocTags,\n      pos,\n      line,\n      col,\n      hasRefsInFile: false,\n      referencedIn: undefined,\n      fixes: fix ? [fix] : [],\n      isReExport,\n    });\n  }\n};\n\nconst _collectRefsInType = (node: any, exportName: string, signatureOnly: boolean): void => {\n  if (!node || typeof node !== 'object') return;\n  if (node.type === 'TSTypeQuery') {\n    const name = node.exprName.type === 'Identifier' ? node.exprName.name : undefined;\n    if (name) {\n      const refs = state.referencedInExport.get(name);\n      if (refs) refs.add(exportName);\n      else state.referencedInExport.set(name, new Set([exportName]));\n    }\n    return;\n  }\n  if (signatureOnly && (node.type === 'FunctionBody' || node.type === 'BlockStatement')) return;\n  if (node.type === 'TSTypeReference' && node.typeName.type === 'Identifier') {\n    const name = node.typeName.name;\n    const refs = state.referencedInExport.get(name);\n    if (refs) refs.add(exportName);\n    else state.referencedInExport.set(name, new Set([exportName]));\n  }\n  for (const key in node) {\n    if (key === 'type' || key === 'parent') continue;\n    const val = node[key];\n    if (Array.isArray(val)) {\n      for (const item of val) {\n        if (item && typeof item === 'object' && item.type) _collectRefsInType(item, exportName, signatureOnly);\n      }\n    } else if (val && typeof val === 'object' && val.type) {\n      _collectRefsInType(val, exportName, signatureOnly);\n    }\n  }\n};\n\nconst _isInNamespace = (node: Span) =>\n  state.nsRanges.length > 0 && state.nsRanges.some(([start, end]) => node.start >= start && node.end <= end);\n\nconst coreVisitorObject: VisitorObject = {\n  TSModuleDeclaration(node) {\n    state.nsRanges.push([node.start, node.end]);\n  },\n  ClassDeclaration(node) {\n    if (node.id?.name) state.localDeclarationTypes.set(node.id.name, SYMBOL_TYPE.CLASS);\n  },\n  FunctionDeclaration(node) {\n    if (node.id?.name) state.localDeclarationTypes.set(node.id.name, SYMBOL_TYPE.FUNCTION);\n  },\n  VariableDeclaration(node) {\n    state.currentVarDeclStart = node.start;\n    for (const decl of node.declarations) {\n      if (decl.id.type === 'Identifier') state.localDeclarationTypes.set(decl.id.name, SYMBOL_TYPE.VARIABLE);\n    }\n  },\n  TSEnumDeclaration(node) {\n    if (node.id.name) state.localDeclarationTypes.set(node.id.name, SYMBOL_TYPE.ENUM);\n  },\n  ExportNamedDeclaration(node) {\n    handleExportNamed(node, state);\n  },\n  ExportDefaultDeclaration(node) {\n    handleExportDefault(node, state);\n  },\n  TSExportAssignment(node) {\n    handleExportAssignment(node, state);\n  },\n  ExpressionStatement(node) {\n    handleExpressionStatement(node, state);\n  },\n  VariableDeclarator(node) {\n    handleVariableDeclarator(node, state);\n  },\n  ImportExpression(node) {\n    handleImportExpression(node, state);\n  },\n  CallExpression(node) {\n    handleCallExpression(node, state);\n  },\n  NewExpression(node) {\n    handleNewExpression(node, state);\n  },\n  MemberExpression(node) {\n    handleMemberExpression(node, state);\n  },\n  JSXMemberExpression(node) {\n    handleJSXMemberExpression(node, state);\n  },\n  ForInStatement(node) {\n    if (node.right.type === 'Identifier') {\n      const _import = state.localImportMap.get(node.right.name);\n      if (_import?.isNamespace) {\n        const internalImport = state.internal.get(_import.filePath);\n        if (internalImport) addValue(internalImport.import, OPAQUE, state.filePath);\n      }\n    }\n  },\n  ForOfStatement(node) {\n    if (node.right.type === 'Identifier') {\n      const _import = state.localImportMap.get(node.right.name);\n      if (_import?.isNamespace) {\n        const internalImport = state.internal.get(_import.filePath);\n        if (internalImport) addValue(internalImport.import, OPAQUE, state.filePath);\n      }\n    }\n  },\n  TSQualifiedName(node) {\n    let left: TSTypeName = node;\n    const parts: string[] = [];\n    while (left.type === 'TSQualifiedName') {\n      if (left.right.type === 'Identifier') parts.unshift(left.right.name);\n      left = left.left;\n    }\n    if (left.type === 'Identifier') {\n      const rootName = left.name;\n      const _import = state.localImportMap.get(rootName);\n      if (_import) {\n        const internalImport = state.internal.get(_import.filePath);\n        if (internalImport) {\n          if (parts.length > 0) {\n            let path = '';\n            for (const part of parts) {\n              path = path ? `${path}.${part}` : part;\n              state.addNsMemberRefs(internalImport, rootName, path);\n            }\n          } else {\n            internalImport.refs.add(rootName);\n          }\n        }\n      } else if (parts.length > 0) {\n        const exp = state.exports.get(rootName);\n        if (exp) {\n          let path = '';\n          for (const part of parts) {\n            path = path ? `${path}.${part}` : part;\n            for (const member of exp.members) {\n              if (member.identifier === path) member.hasRefsInFile = true;\n            }\n          }\n        }\n      }\n    }\n  },\n  TSTypeReference(node) {\n    if (node.typeName.type === 'Identifier') {\n      const name = node.typeName.name;\n      const _import = state.localImportMap.get(name);\n      if (_import) {\n        const internalImport = state.internal.get(_import.filePath);\n        if (internalImport) internalImport.refs.add(name);\n      }\n    }\n  },\n  TSTypeQuery(node) {\n    if (node.exprName.type === 'Identifier') {\n      const name = node.exprName.name;\n      const _import = state.localImportMap.get(name);\n      if (_import) {\n        const internalImport = state.internal.get(_import.filePath);\n        if (internalImport) internalImport.refs.add(name);\n      }\n    }\n  },\n  TSImportType(node) {\n    const src = node.source;\n    if (isStringLiteral(src)) {\n      const specifier = getStringValue(src)!;\n      state.addImport(specifier, undefined, undefined, undefined, src.start, IMPORT_FLAGS.TYPE_ONLY);\n    }\n  },\n\n  TSImportEqualsDeclaration(node) {\n    if (node.moduleReference.type === 'TSExternalModuleReference') {\n      const expr = node.moduleReference.expression;\n      if (isStringLiteral(expr)) {\n        const specifier = getStringValue(expr)!;\n        const localName = node.id.name;\n        state.addImport(specifier, 'default', localName, undefined, node.id.start, IMPORT_FLAGS.NONE);\n        if (localName) {\n          const module = state.resolveModule(specifier, state.filePath);\n          if (module && !module.isExternalLibraryImport && !isInNodeModules(module.resolvedFileName)) {\n            state.localImportMap.set(localName, {\n              importedName: 'default',\n              filePath: module.resolvedFileName,\n              isNamespace: false,\n            });\n          }\n        }\n      }\n    } else if (node.moduleReference.type === 'TSQualifiedName') {\n      const left = node.moduleReference.left;\n      const right = node.moduleReference.right;\n      if (left.type === 'Identifier' && right.type === 'Identifier') {\n        const nsName = left.name;\n        const _import = state.localImportMap.get(nsName);\n        if (_import?.isNamespace) {\n          const internalImport = state.internal.get(_import.filePath);\n          if (internalImport) state.addNsMemberRefs(internalImport, nsName, right.name);\n        }\n      }\n    }\n  },\n};\n\nexport function buildVisitor(pluginVisitorObjects: PluginVisitorObject[]): Visitor {\n  if (pluginVisitorObjects.length === 0) return new Visitor(coreVisitorObject);\n  type HandlerFn = (node: never) => void;\n  type HandlerMap = Record<string, HandlerFn | undefined>;\n  const handlerLists = new Map<string, HandlerFn[]>();\n  const coreHandlers = coreVisitorObject as HandlerMap;\n  for (const key in coreHandlers) {\n    const fn = coreHandlers[key];\n    if (fn) handlerLists.set(key, [fn]);\n  }\n  for (const obj of pluginVisitorObjects) {\n    const handlers = obj as HandlerMap;\n    for (const key in handlers) {\n      const fn = handlers[key];\n      if (!fn) continue;\n      const list = handlerLists.get(key);\n      if (list) list.push(fn);\n      else handlerLists.set(key, [fn]);\n    }\n  }\n  const merged: HandlerMap = {};\n  for (const [key, list] of handlerLists) {\n    if (list.length === 1) {\n      merged[key] = list[0];\n    } else {\n      const fns = list;\n      merged[key] = ((node: never) => {\n        for (let i = 0; i < fns.length; i++) fns[i](node);\n      }) as HandlerFn;\n    }\n  }\n  return new Visitor(merged as VisitorObject);\n}\n\nexport function walkAST(program: Program, sourceText: string, filePath: string, ctx: WalkContext) {\n  const isJS =\n    filePath.endsWith('.js') || filePath.endsWith('.mjs') || filePath.endsWith('.cjs') || filePath.endsWith('.jsx');\n\n  state = {\n    ...ctx,\n    filePath,\n    sourceText,\n    isJS,\n    handledImportExpressions: new Set(),\n    bareExprRefs: new Set(),\n    accessedAliases: new Set(),\n    shorthandNsContainers: new Map(),\n    accessedShorthandNs: new Set(),\n    chainedMemberExprs: new WeakSet(),\n    currentVarDeclStart: -1,\n    nsRanges: [],\n    addExport: _addExport,\n    getFix: _getFix,\n    getTypeFix: _getTypeFix,\n    collectRefsInType: _collectRefsInType,\n    isInNamespace: _isInNamespace,\n  };\n\n  ctx.visitor.visit(program);\n\n  for (const [aliasName, aliasSet] of state.importAliases) {\n    if (!state.accessedAliases.has(aliasName)) {\n      for (const alias of aliasSet) {\n        const _import = state.localImportMap.get(alias.id);\n        if (_import?.isNamespace) {\n          const internalImport = state.internal.get(_import.filePath);\n          if (internalImport) {\n            addValue(internalImport.import, OPAQUE, filePath);\n          }\n        }\n      }\n    }\n  }\n\n  for (const [containerName, nsSet] of state.shorthandNsContainers) {\n    for (const nsName of nsSet) {\n      if (!state.accessedShorthandNs.has(`${containerName}.${nsName}`)) {\n        const _import = state.localImportMap.get(nsName);\n        if (_import) {\n          const internalImport = state.internal.get(_import.filePath);\n          if (internalImport) {\n            addValue(internalImport.import, OPAQUE, filePath);\n          }\n        }\n      }\n    }\n  }\n\n  if (!state.skipBareExprRefs) {\n    for (const name of state.bareExprRefs) {\n      const item = state.exports.get(name);\n      if (item) item.hasRefsInFile = true;\n    }\n  }\n\n  state = undefined!;\n}\n"
  },
  {
    "path": "packages/knip/src/util/Performance.ts",
    "content": "import { type PerformanceEntry, PerformanceObserver, performance } from 'node:perf_hooks';\nimport { memoryUsage } from 'node:process';\nimport { parseArgs } from 'node:util';\nimport { getStats } from './math.ts';\nimport { Table } from './table.ts';\n\nconst { values } = parseArgs({\n  strict: false,\n  options: {\n    performance: { type: 'boolean' },\n    'performance-fn': { type: 'string', multiple: true },\n    memory: { type: 'boolean' },\n    'memory-realtime': { type: 'boolean' },\n  },\n});\n\nconst timerifyOnlyFnName = values['performance-fn'];\nconst isMemoryRealtime = !!values['memory-realtime'];\nconst isTimerifyFunctions = !!values.performance || !!timerifyOnlyFnName;\nconst isMemoryUsageEnabled = !!values.memory || isMemoryRealtime;\n\nexport const timerify = <T extends (...params: any[]) => any>(fn: T, name: string = fn.name): T => {\n  if (!isTimerifyFunctions) return fn;\n  if (timerifyOnlyFnName && !timerifyOnlyFnName.includes(name)) return fn;\n  return performance.timerify(Object.defineProperty(fn, 'name', { get: () => name }));\n};\n\ntype MemInfo = {\n  label: string;\n  heapUsed: number;\n  heapTotal: number;\n  rss: number;\n};\n\ninterface MemoryEntry extends PerformanceEntry {\n  detail: MemInfo;\n}\n\nconst getMemInfo = (label: string): MemInfo => {\n  const usage = memoryUsage();\n  return { label, heapUsed: usage.heapUsed, heapTotal: usage.heapTotal, rss: usage.rss };\n};\n\nconst twoFixed = (value: any) => (typeof value === 'number' ? value.toFixed(2) : value);\n\nconst inMB = (bytes: number) => bytes / 1024 / 1024;\n\nconst keys = ['heapUsed', 'heapTotal', 'rss'] as const;\n// oxlint-disable-next-line no-console\nconst logHead = () => console.log(['phase', ...keys].map(key => key.padStart(10)).join('  '));\nconst log = (memInfo: MemInfo) =>\n  // oxlint-disable-next-line no-console\n  console.log([memInfo.label.padStart(10), ...keys.map(key => twoFixed(inMB(memInfo[key])).padStart(10))].join('  '));\n\nclass Performance {\n  isEnabled: boolean;\n  isTimerifyFunctions: boolean;\n  isMemoryUsageEnabled: boolean;\n  startTime = 0;\n  endTime = 0;\n  perfEntries: PerformanceEntry[] = [];\n  memEntries: MemoryEntry[] = [];\n  perfId?: string;\n  memId?: string;\n  fnObserver?: PerformanceObserver;\n  memObserver?: PerformanceObserver;\n\n  constructor({ isTimerifyFunctions = false, isMemoryUsageEnabled = false }) {\n    this.isEnabled = isTimerifyFunctions || isMemoryUsageEnabled;\n    this.isTimerifyFunctions = isTimerifyFunctions;\n    this.isMemoryUsageEnabled = isMemoryUsageEnabled;\n\n    this.startTime = performance.now();\n    const instanceId = Math.floor(performance.now() * 100);\n    this.perfId = `perf-${instanceId}`;\n    this.memId = `mem-${instanceId}`;\n\n    if (isTimerifyFunctions) {\n      this.fnObserver = new PerformanceObserver(items => {\n        for (const entry of items.getEntries()) {\n          this.perfEntries.push(entry);\n        }\n      });\n      this.fnObserver.observe({ type: 'function' });\n    }\n\n    if (isMemoryUsageEnabled) {\n      this.memObserver = new PerformanceObserver(items => {\n        for (const entry of items.getEntries() as MemoryEntry[]) {\n          this.memEntries.push(entry);\n        }\n      });\n      this.memObserver.observe({ type: 'mark' });\n\n      if (isMemoryRealtime) logHead();\n      this.addMemoryMark('start');\n    }\n  }\n\n  private setMark(name: string) {\n    const id = `${this.perfId}:${name}`;\n    performance.mark(`${id}:start`);\n  }\n\n  private clearMark(name: string) {\n    const id = `${this.perfId}:${name}`;\n    performance.mark(`${id}:end`);\n    performance.measure(id, `${id}:start`, `${id}:end`);\n    performance.clearMarks(`${id}:start`);\n    performance.clearMarks(`${id}:end`);\n  }\n\n  private async flush() {\n    this.setMark('_flush');\n    await new Promise(resolve => setTimeout(resolve, 1));\n    this.clearMark('_flush');\n  }\n\n  private getPerfEntriesByName() {\n    return this.perfEntries.reduce(\n      (entries, entry) => {\n        const name = entry.name.replace(`${this.perfId}:`, '');\n        entries[name] = entries[name] ?? [];\n        entries[name].push(entry.duration);\n        return entries;\n      },\n      {} as Record<string, number[]>\n    );\n  }\n\n  getTimerifiedFunctionsTable() {\n    const entriesByName = this.getPerfEntriesByName();\n    const totalDuration = this.getCurrentDurationInMs();\n    const table = new Table({ header: true });\n    for (const [name, values] of Object.entries(entriesByName)) {\n      const stats = getStats(values);\n      table.row();\n      table.cell('Name', name);\n      table.cell('size', values.length);\n      table.cell('min', stats.min, twoFixed);\n      table.cell('max', stats.max, twoFixed);\n      table.cell('median', stats.median, twoFixed);\n      table.cell('sum', stats.sum, twoFixed);\n      table.cell('%', (stats.sum / totalDuration) * 100, v => (typeof v === 'number' ? `${v.toFixed(0)}%` : ''));\n    }\n    table.sort('sum|desc');\n    return table.toString();\n  }\n\n  addMemoryMark(label: string) {\n    if (!this.isMemoryUsageEnabled) return;\n    const id = `${this.memId}:${label}`;\n    const detail = getMemInfo(label);\n    performance.mark(id, { detail });\n    if (isMemoryRealtime) log(detail);\n  }\n\n  getMemoryUsageTable() {\n    const table = new Table({ header: true });\n    let prevHeapUsed = 0;\n    let peakHeapUsed = 0;\n    let peakRss = 0;\n    for (const entry of this.memEntries) {\n      if (!entry.detail) continue;\n      const { label, heapUsed, rss } = entry.detail;\n      const delta = heapUsed - prevHeapUsed;\n      if (heapUsed > peakHeapUsed) peakHeapUsed = heapUsed;\n      if (rss > peakRss) peakRss = rss;\n      table.row();\n      table.cell('Phase', label);\n      table.cell('heapUsed', inMB(heapUsed), twoFixed);\n      table.cell('rss', inMB(rss), twoFixed);\n      table.cell('Δheap', prevHeapUsed === 0 ? '' : `${delta > 0 ? '+' : ''}${twoFixed(inMB(delta))}`, String);\n      prevHeapUsed = heapUsed;\n    }\n    table.row();\n    table.cell('Phase', 'peak');\n    table.cell('heapUsed', inMB(peakHeapUsed), twoFixed);\n    table.cell('rss', inMB(peakRss), twoFixed);\n    table.cell('Δheap', '');\n    return table.toString();\n  }\n\n  getCurrentDurationInMs() {\n    return performance.now() - this.startTime;\n  }\n\n  getMemHeapUsage() {\n    return memoryUsage().heapUsed;\n  }\n\n  getCurrentMemUsageInMb() {\n    return twoFixed(inMB(this.getMemHeapUsage()));\n  }\n\n  public async finalize() {\n    if (!this.isEnabled) return;\n    this.addMemoryMark('end');\n    await this.flush();\n  }\n\n  public reset() {\n    this.perfEntries = [];\n    this.fnObserver?.disconnect();\n    this.memObserver?.disconnect();\n  }\n}\n\nexport const perfObserver = new Performance({ isTimerifyFunctions, isMemoryUsageEnabled });\n"
  },
  {
    "path": "packages/knip/src/util/array.ts",
    "content": "type Collection<T> = Array<T> | Set<T>;\n\n/** Remove duplicates and falsy values from arrays and sets */\nexport const compact = <T>(collection: Collection<T | undefined>) =>\n  Array.from(new Set(collection)).filter((value): value is T => Boolean(value));\n\nexport const arrayify = (value?: string[] | string) =>\n  Array.isArray(value) ? value : typeof value === 'string' ? [value] : [];\n\nexport const partition = <T>(collection: Collection<T>, predicate: (item: T) => unknown) => {\n  const results: [T[], T[]] = [[], []];\n  for (const item of collection) results[predicate(item) ? 0 : 1].push(item);\n  return results;\n};\n"
  },
  {
    "path": "packages/knip/src/util/catalog.ts",
    "content": "import type { CatalogContainer } from '../CatalogCounselor.ts';\nimport type { PackageJson } from '../types/package-json.ts';\nimport { isFile } from './fs.ts';\nimport { _load } from './loader.ts';\nimport { basename, join } from './path.ts';\n\nexport const DEFAULT_CATALOG = 'default';\n\nexport const getCatalogContainer = async (\n  cwd: string,\n  manifest: PackageJson,\n  manifestPath: string,\n  pnpmWorkspacePath?: string,\n  pnpmWorkspace?: any\n): Promise<CatalogContainer> => {\n  const filePath = pnpmWorkspacePath ?? (isFile(cwd, '.yarnrc.yml') ? join(cwd, '.yarnrc.yml') : manifestPath);\n\n  const yarnWorkspace = basename(filePath) === '.yarnrc.yml' ? await _load(filePath) : undefined;\n\n  const catalog =\n    pnpmWorkspace?.catalog ??\n    yarnWorkspace?.catalog ??\n    manifest.catalog ??\n    ((!Array.isArray(manifest.workspaces) && manifest.workspaces?.catalog) || {});\n\n  const catalogs =\n    pnpmWorkspace?.catalogs ??\n    yarnWorkspace?.catalogs ??\n    manifest.catalogs ??\n    ((!Array.isArray(manifest.workspaces) && manifest.workspaces?.catalogs) || {});\n\n  return { filePath, catalog, catalogs };\n};\n\nconst extractEntries = (catalog: unknown): string[] => {\n  if (catalog && typeof catalog === 'object') return Object.keys(catalog).map(entry => `${DEFAULT_CATALOG}:${entry}`);\n  return [];\n};\n\nconst extractNamedEntries = (catalogs: unknown) => {\n  const entries = new Set<string>();\n  if (catalogs && typeof catalogs === 'object') {\n    for (const [catalogName, catalog] of Object.entries(catalogs)) {\n      if (catalog && typeof catalog === 'object') {\n        for (const name of Object.keys(catalog)) entries.add(`${catalogName}:${name}`);\n      }\n    }\n  }\n  return entries;\n};\n\nexport const parseCatalog = (container: CatalogContainer) => {\n  const entries = new Set<string>();\n  if ('catalog' in container) for (const id of extractEntries(container.catalog)) entries.add(id);\n  if ('catalogs' in container) for (const id of extractNamedEntries(container.catalogs)) entries.add(id);\n  return entries;\n};\n\nexport const extractCatalogReferences = (manifest: PackageJson): Set<string> => {\n  const catalogReferences = new Set<string>();\n\n  const checkDependencies = (dependencies: Record<string, string> | undefined) => {\n    if (!dependencies) return;\n\n    for (const [name, version] of Object.entries(dependencies)) {\n      if (typeof version === 'string' && version.startsWith('catalog:')) {\n        const catalogName = version.slice('catalog:'.length) || DEFAULT_CATALOG;\n        catalogReferences.add([catalogName, name].join(':'));\n      }\n    }\n  };\n\n  checkDependencies(manifest.dependencies);\n  checkDependencies(manifest.devDependencies);\n  checkDependencies(manifest.peerDependencies);\n  checkDependencies(manifest.optionalDependencies);\n  checkDependencies(manifest.resolutions);\n  checkDependencies(manifest.pnpm?.overrides);\n\n  return catalogReferences;\n};\n"
  },
  {
    "path": "packages/knip/src/util/cli-arguments.ts",
    "content": "import { parseArgs } from 'node:util';\n\nexport const helpText = `✂️  Find unused dependencies, exports and files in your JavaScript and TypeScript projects\n\nUsage: knip [options]\n\nOptions:\n  -h, --help               Print this help text\n  -V, --version            Print version\n  -n, --no-progress        Don't show dynamic progress updates (automatically enabled in CI environments)\n  -c, --config [file]      Configuration file path\n                           (default: [.]knip.json[c], knip.(js|ts), knip.config.(js|ts) or package.json#knip)\n  --use-tsconfig-files     Use tsconfig.json to define project files (override \\`project\\` patterns)\n  -t, --tsConfig [file]    TypeScript configuration path (default: tsconfig.json)\n\nMode\n  --cache                  Enable caching\n  --cache-location         Change cache location (default: node_modules/.cache/knip)\n  --include-entry-exports  Include entry files when reporting unused exports\n  --no-gitignore           Don't respect .gitignore\n  --production             Analyze only production source files (e.g. no test files, devDependencies)\n  --strict                 Consider only direct dependencies of workspace (not devDependencies, not other workspaces)\n  --watch                  Watch mode\n\nScope\n  -W, --workspace [filter] Filter workspaces by name, directory, or glob (can be repeated)\n  --directory [dir]        Run process from a different directory (default: cwd)\n  --include                Report only provided issue type(s), can be comma-separated or repeated (1)\n  --exclude                Exclude provided issue type(s) from report, can be comma-separated or repeated (1)\n  --dependencies           Shortcut for --include dependencies,unlisted,binaries,unresolved,catalog\n  --exports                Shortcut for --include exports,nsExports,types,nsTypes,enumMembers,namespaceMembers,duplicates\n  --files                  Shortcut for --include files\n  --tags                   Include or exclude tagged exports\n\nFix\n  --fix                    Fix issues (modifies files in your repo)\n  --fix-type               Fix only issues of type, can be comma-separated or repeated (2)\n  --allow-remove-files     Allow Knip to remove files (with --fix)\n  --format                 Format modified files after --fix using the local formatter\n\nOutput\n  --preprocessor           Preprocess the results before providing it to the reporter(s), can be repeated\n  --preprocessor-options   Pass extra options to the preprocessor (as JSON string, see --reporter-options example)\n  --reporter               Select reporter (default: symbols), can be repeated (3)\n  --reporter-options       Pass extra options to the reporter (as JSON string, see example)\n  --no-config-hints        Suppress configuration hints\n  --treat-config-hints-as-errors    Exit with non-zero code (1) if there are any configuration hints\n  --max-issues             Maximum number of total issues before non-zero exit code (default: 0)\n  --max-show-issues        Maximum number of issues to display per type\n  --no-exit-code           Always exit with code zero (0)\n\nTroubleshooting\n  -d, --debug              Show debug output\n  --memory                 Measure memory usage and display data table\n  --memory-realtime        Log memory usage in realtime\n  --performance            Measure count and running time of key functions and display stats table\n  --performance-fn [name]  Measure only function [name]\n  --trace                  Show trace output\n  --trace-dependency [name] Show files that import the named dependency\n  --trace-export [name]    Show trace output for named export(s)\n  --trace-file [file]      Show trace output for exports in file\n\n(1) Issue types: files, dependencies, unlisted, unresolved, exports, nsExports, types, nsTypes, enumMembers, namespaceMembers, duplicates, catalog\n(2) Fixable issue types: dependencies, exports, types, files, catalog\n(3) Built-in reporters: symbols (default), compact, codeowners, json, codeclimate, markdown, disclosure, github-actions\n\nExamples:\n\n$ knip\n$ knip --production\n$ knip --workspace packages/client --include files,dependencies\n$ knip --workspace @myorg/* --workspace '!@myorg/legacy'\n$ knip --workspace './apps/*' --workspace '@shared/utils'\n$ knip -c ./config/knip.json --reporter compact\n$ knip --reporter codeowners --reporter-options '{\"path\":\".github/CODEOWNERS\"}'\n$ knip --tags=-lintignore\n\nWebsite: https://knip.dev`;\n\nexport type ParsedCLIArgs = ReturnType<typeof parseCLIArgs>;\n\nexport default function parseCLIArgs() {\n  return parseArgs({\n    options: {\n      cache: { type: 'boolean' },\n      'cache-location': { type: 'string' },\n      config: { type: 'string', short: 'c' },\n      debug: { type: 'boolean', short: 'd' },\n      dependencies: { type: 'boolean' },\n      directory: { type: 'string' },\n      exclude: { type: 'string', multiple: true },\n      exports: { type: 'boolean' },\n      tags: { type: 'string', multiple: true },\n      files: { type: 'boolean' },\n      fix: { type: 'boolean' },\n      'fix-type': { type: 'string', multiple: true },\n      format: { type: 'boolean' },\n      'allow-remove-files': { type: 'boolean' },\n      help: { type: 'boolean', short: 'h' },\n      include: { type: 'string', multiple: true },\n\n      'include-entry-exports': { type: 'boolean' },\n      'max-issues': { type: 'string' },\n      'max-show-issues': { type: 'string' },\n      memory: { type: 'boolean' },\n      'memory-realtime': { type: 'boolean' },\n      'no-config-hints': { type: 'boolean' },\n      'no-exit-code': { type: 'boolean' },\n      'no-gitignore': { type: 'boolean' },\n      'no-progress': { type: 'boolean', short: 'n' },\n      performance: { type: 'boolean' },\n      'performance-fn': { type: 'string' },\n      production: { type: 'boolean' },\n      preprocessor: { type: 'string', multiple: true },\n      'preprocessor-options': { type: 'string' },\n      reporter: { type: 'string', multiple: true },\n      'reporter-options': { type: 'string' },\n      strict: { type: 'boolean' },\n      trace: { type: 'boolean' },\n      'trace-dependency': { type: 'string' },\n      'trace-export': { type: 'string' },\n      'trace-file': { type: 'string' },\n      'treat-config-hints-as-errors': { type: 'boolean' },\n      tsConfig: { type: 'string', short: 't' },\n      'use-tsconfig-files': { type: 'boolean' },\n      version: { type: 'boolean', short: 'V' },\n      watch: { type: 'boolean' },\n      workspace: { type: 'string', short: 'W', multiple: true },\n    },\n  }).values;\n}\n"
  },
  {
    "path": "packages/knip/src/util/codeowners.ts",
    "content": "import { readFileSync } from 'node:fs';\nimport picomatch from 'picomatch';\nimport { debugLog } from './debug.ts';\nimport { convertGitignoreToPicomatchIgnorePatterns } from './parse-and-convert-gitignores.ts';\n\n/** @internal */\nexport function parseCodeowners(content: string) {\n  const matchers = content\n    .split(/\\r?\\n/)\n    .filter(line => line && !line.startsWith('#'))\n    .map(rule => {\n      const [path, ...owners] = rule.split(/\\s+/);\n      const { patterns } = convertGitignoreToPicomatchIgnorePatterns(path);\n      return { owners, match: picomatch(patterns) };\n    });\n\n  return (filePath: string) => {\n    for (const matcher of [...matchers].reverse()) {\n      if (matcher.match(filePath)) {\n        return matcher.owners;\n      }\n    }\n    return [];\n  };\n}\n\nexport function createOwnershipEngine(filePath: string) {\n  try {\n    const content = readFileSync(filePath, 'utf8');\n    return parseCodeowners(content);\n  } catch (error) {\n    debugLog('*', `Failed to load codeowners file from ${filePath}`);\n    throw error;\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/util/create-input-handler.ts",
    "content": "import type { ConfigurationChief, Workspace } from '../ConfigurationChief.ts';\nimport { IGNORED_RUNTIME_DEPENDENCIES } from '../constants.ts';\nimport type { DependencyDeputy } from '../DependencyDeputy.ts';\nimport type { Issue } from '../types/issues.ts';\nimport type { ExternalRef } from '../types/module-graph.ts';\nimport type { MainOptions } from './create-options.ts';\nimport { debugLog } from './debug.ts';\nimport {\n  fromBinary,\n  type Input,\n  isBinary,\n  isConfig,\n  isDeferResolve,\n  isDeferResolveEntry,\n  isDependency,\n  toDebugString,\n} from './input.ts';\nimport { getPackageNameFromSpecifier } from './modules.ts';\nimport { dirname, isAbsolute, isInNodeModules, isInternal, join } from './path.ts';\nimport { _resolveModuleSync, _resolveSync } from './resolve.ts';\n\nexport type ExternalRefsFromInputs = Map<string, Set<ExternalRef>>;\n\nconst isJoinable = (specifier: string) => {\n  const char = specifier.charCodeAt(0);\n  return char !== 35 && char !== 126 && char !== 64 && !isAbsolute(specifier); // not # ~ @, not absolute\n};\n\nconst getWorkspaceFor = (input: Input, chief: ConfigurationChief, workspace: Workspace) =>\n  (input.dir && chief.findWorkspaceByFilePath(`${input.dir}/`)) ||\n  (input.containingFilePath && chief.findWorkspaceByFilePath(input.containingFilePath)) ||\n  workspace;\n\nconst addExternalRef = (map: ExternalRefsFromInputs, containingFilePath: string, ref: ExternalRef) => {\n  if (!map.has(containingFilePath)) map.set(containingFilePath, new Set());\n  // oxlint-disable-next-line @typescript-eslint/no-non-null-assertion\n  map.get(containingFilePath)!.add(ref);\n};\n\nexport const createInputHandler =\n  (\n    deputy: DependencyDeputy,\n    chief: ConfigurationChief,\n    isGitIgnored: (filePath: string) => boolean,\n    addIssue: (issue: Issue) => void,\n    externalRefs: ExternalRefsFromInputs | undefined,\n    options: MainOptions\n  ) =>\n  (input: Input, workspace: Workspace) => {\n    const { specifier, containingFilePath } = input;\n\n    if (!containingFilePath || IGNORED_RUNTIME_DEPENDENCIES.has(specifier)) return;\n\n    if (isBinary(input)) {\n      const binaryName = fromBinary(input);\n      const inputWorkspace = getWorkspaceFor(input, chief, workspace);\n\n      const dependencies = deputy.maybeAddReferencedBinary(inputWorkspace, binaryName);\n      if (dependencies) {\n        if (externalRefs) {\n          for (const dependency of dependencies) {\n            addExternalRef(externalRefs, containingFilePath, { specifier: dependency, identifier: binaryName });\n          }\n        }\n        return;\n      }\n\n      if (dependencies || input.optional) return;\n\n      addIssue({\n        type: 'binaries',\n        filePath: containingFilePath,\n        workspace: workspace.name,\n        symbol: binaryName,\n        specifier,\n        fixes: [],\n      });\n\n      return;\n    }\n\n    const packageName = getPackageNameFromSpecifier(specifier);\n\n    if (\n      packageName &&\n      (isDependency(input) ||\n        isDeferResolve(input) ||\n        (isDeferResolveEntry(input) && isInNodeModules(specifier)) ||\n        isConfig(input))\n    ) {\n      // Attempt fast path first for external dependencies (including internal workspaces)\n      const isWorkspace = chief.workspacesByPkgName.has(packageName);\n      const inputWorkspace = getWorkspaceFor(input, chief, workspace);\n\n      if (inputWorkspace) {\n        const isHandled = deputy.maybeAddReferencedExternalDependency(inputWorkspace, packageName, isConfig(input));\n\n        if (externalRefs && !isWorkspace) {\n          addExternalRef(externalRefs, containingFilePath, { specifier: packageName, identifier: undefined });\n        }\n\n        if (isWorkspace || isDependency(input)) {\n          if (!isHandled) {\n            if (!input.optional && ((options.isProduction && input.production) || !options.isProduction)) {\n              addIssue({\n                type: 'unlisted',\n                filePath: containingFilePath,\n                workspace: inputWorkspace.name,\n                symbol: packageName,\n                specifier: packageName,\n                fixes: [],\n              });\n            }\n            return;\n          }\n\n          // Return resolved path for refs to internal workspaces\n          const internalPath = _resolveSync(specifier, dirname(containingFilePath));\n          if (internalPath && isInternal(internalPath) && !isGitIgnored(internalPath)) return internalPath;\n        }\n\n        if (isHandled) return;\n      }\n    }\n\n    if (isDeferResolve(input) && deputy.isProduction && !input.production) {\n      return;\n    }\n\n    // oxc-resolver does not resolve \"file\" or \"file.ts\" without \"./\" so we best-guess-absolutify\n    const filePath = isJoinable(specifier) ? join(input.dir ?? dirname(containingFilePath), specifier) : specifier;\n    const basePath = input.dir ? join(input.dir, 'file.ts') : containingFilePath;\n    const resolvedFilePath = _resolveModuleSync(filePath, basePath);\n\n    if (resolvedFilePath && isInternal(resolvedFilePath)) {\n      return isGitIgnored(resolvedFilePath) ? undefined : resolvedFilePath;\n    }\n\n    if (input.optional) return;\n\n    if (!isInternal(filePath)) {\n      addIssue({\n        type: 'unlisted',\n        filePath: containingFilePath,\n        workspace: workspace.name,\n        symbol: packageName ?? specifier,\n        specifier: packageName ?? specifier,\n        fixes: [],\n      });\n    } else if (!isGitIgnored(filePath)) {\n      // Let's start out conservatively\n      if (!isDeferResolveEntry(input) && !isConfig(input)) {\n        addIssue({\n          type: 'unresolved',\n          filePath: containingFilePath,\n          workspace: workspace.name,\n          symbol: specifier,\n          fixes: [],\n        });\n      } else {\n        debugLog(workspace.name, `Unable to resolve ${toDebugString(input, options.cwd)}`);\n      }\n    }\n  };\n"
  },
  {
    "path": "packages/knip/src/util/create-options.ts",
    "content": "import { partitionCompilers } from '../compilers/index.ts';\nimport { ISSUE_TYPES, KNIP_CONFIG_LOCATIONS } from '../constants.ts';\nimport { knipConfigurationSchema } from '../schema/configuration.ts';\nimport type { RawConfiguration } from '../types/config.ts';\nimport type { IssueType } from '../types/issues.ts';\nimport type { Options } from '../types/options.ts';\nimport type { PackageJson } from '../types/package-json.ts';\nimport { getCatalogContainer } from './catalog.ts';\nimport type { ParsedCLIArgs } from './cli-arguments.ts';\nimport { ConfigurationError } from './errors.ts';\nimport { findFile, loadJSON } from './fs.ts';\nimport { getIncludedIssueTypes, shorthandDeps, shorthandExports, shorthandFiles } from './get-included-issue-types.ts';\nimport { defaultRules } from './issue-initializers.ts';\nimport { loadResolvedConfigFile } from './load-config.ts';\nimport { _load } from './loader.ts';\nimport { logWarning } from './log.ts';\nimport { getKeysByValue } from './object.ts';\nimport { isAbsolute, join, normalize, toAbsolute, toPosix } from './path.ts';\nimport { splitTags } from './tag.ts';\n\ninterface CreateOptions extends Partial<Options> {\n  args?: ParsedCLIArgs;\n}\n\n/**\n * - Loads package.json/pnpm-workspace.yaml\n * - Loads & validates knip.json\n * - Creates options\n */\nexport const createOptions = async (options: CreateOptions) => {\n  const { args = {} } = options;\n  const pcwd = process.cwd();\n  const cwd = normalize(toPosix(toAbsolute(options.cwd ?? args.directory ?? pcwd, pcwd)));\n\n  const manifestPath = findFile(cwd, 'package.json');\n  const manifest: PackageJson = manifestPath && (await loadJSON(manifestPath));\n\n  if (!(manifestPath && manifest)) {\n    throw new ConfigurationError('Unable to find package.json');\n  }\n\n  let configFilePath: string | undefined;\n  for (const configPath of args.config ? [args.config] : KNIP_CONFIG_LOCATIONS) {\n    const resolvedConfigFilePath = isAbsolute(configPath) ? configPath : findFile(cwd, configPath);\n    if (resolvedConfigFilePath) {\n      configFilePath = resolvedConfigFilePath;\n      break;\n    }\n  }\n\n  if (args.config && !configFilePath && !manifest.knip) {\n    throw new ConfigurationError(`Unable to find ${args.config} or package.json#knip`);\n  }\n\n  const loadedConfig = Object.assign(\n    {},\n    manifest.knip,\n    configFilePath ? await loadResolvedConfigFile(configFilePath, args) : {}\n  );\n\n  const validIssueTypes = new Set<string>(ISSUE_TYPES);\n  for (const key of ['rules', 'include', 'exclude'] as const) {\n    const value = loadedConfig[key];\n    if (!value) continue;\n    if (Array.isArray(value)) {\n      const invalid = value.filter((v: string) => !validIssueTypes.has(v));\n      if (invalid.length > 0) {\n        loadedConfig[key] = value.filter((v: string) => validIssueTypes.has(v));\n        for (const name of invalid) logWarning('WARNING', `Ignored unknown issue type \"${name}\" in ${key}`);\n      }\n    } else if (typeof value === 'object') {\n      for (const name in value) {\n        if (!validIssueTypes.has(name)) {\n          delete value[name];\n          logWarning('WARNING', `Ignored unknown issue type \"${name}\" in ${key}`);\n        }\n      }\n    }\n  }\n\n  const parsedConfig: RawConfiguration = knipConfigurationSchema.parse(partitionCompilers(loadedConfig));\n\n  if (!configFilePath && manifest.knip) configFilePath = manifestPath;\n\n  const pnpmWorkspacePath = findFile(cwd, 'pnpm-workspace.yaml');\n  const pnpmWorkspace = pnpmWorkspacePath && (await _load(pnpmWorkspacePath));\n\n  const workspaces =\n    pnpmWorkspace?.packages ??\n    (manifest.workspaces\n      ? Array.isArray(manifest.workspaces)\n        ? manifest.workspaces\n        : (manifest.workspaces.packages ?? [])\n      : []);\n\n  const isStrict = options.isStrict ?? args.strict ?? false;\n  const isProduction = options.isProduction ?? args.production ?? isStrict;\n  const isDebug = args.debug ?? false;\n  const isTrace = Boolean(args.trace ?? args['trace-file'] ?? args['trace-export'] ?? args['trace-dependency']);\n\n  const rules = { ...defaultRules, ...parsedConfig.rules };\n  const excludesFromRules = getKeysByValue(rules, 'off');\n\n  const includedIssueTypes = getIncludedIssueTypes({\n    isProduction,\n    exclude: [...excludesFromRules, ...(parsedConfig.exclude ?? [])],\n    include: parsedConfig.include ?? [],\n    excludeOverrides: options.excludedIssueTypes ?? args.exclude ?? [],\n    includeOverrides: [\n      ...(options.includedIssueTypes ?? args.include ?? []),\n      ...(args.dependencies ? shorthandDeps : []),\n      ...(args.exports ? shorthandExports : []),\n      ...(args.files ? shorthandFiles : []),\n    ],\n  });\n\n  for (const [key, value] of Object.entries(includedIssueTypes) as [IssueType, boolean][]) {\n    if (!value) rules[key] = 'off';\n  }\n\n  const fixTypes = options.fixTypes ?? args['fix-type'] ?? [];\n  const isFixFiles = args['allow-remove-files'] && (fixTypes.length === 0 || fixTypes.includes('files'));\n  const tags = splitTags(args.tags ?? options.tags ?? parsedConfig.tags ?? []);\n\n  const workspace = options.workspace ?? args.workspace;\n\n  return {\n    cacheLocation: args['cache-location'] ?? join(cwd, 'node_modules', '.cache', 'knip'),\n    catalog: await getCatalogContainer(cwd, manifest, manifestPath, pnpmWorkspacePath, pnpmWorkspace),\n    config: args.config,\n    configFilePath,\n    cwd,\n    dependencies: args.dependencies ?? false,\n    experimentalTags: tags,\n    exports: args.exports ?? false,\n    files: args.files ?? false,\n    fixTypes,\n    gitignore: args['no-gitignore'] ? false : (options.gitignore ?? true),\n    includedIssueTypes,\n    isCache: args.cache ?? false,\n    isDebug,\n    isDisableConfigHints: args['no-config-hints'] || isProduction || Boolean(workspace),\n    isFix: args.fix ?? options.isFix ?? isFixFiles ?? fixTypes.length > 0,\n    isFixCatalog: fixTypes.length === 0 || fixTypes.includes('catalog'),\n    isFixDependencies: fixTypes.length === 0 || fixTypes.includes('dependencies'),\n    isFixFiles,\n    isFixUnusedExports: fixTypes.length === 0 || fixTypes.includes('exports'),\n    isFixUnusedTypes: fixTypes.length === 0 || fixTypes.includes('types'),\n    isFormat: args.format ?? options.isFormat ?? false,\n    isIncludeEntryExports: args['include-entry-exports'] ?? options.isIncludeEntryExports ?? false,\n    isProduction,\n    isReportDependencies:\n      includedIssueTypes.dependencies ||\n      includedIssueTypes.unlisted ||\n      includedIssueTypes.unresolved ||\n      includedIssueTypes.binaries,\n    isReportExports:\n      includedIssueTypes.exports ||\n      includedIssueTypes.types ||\n      includedIssueTypes.nsExports ||\n      includedIssueTypes.nsTypes ||\n      includedIssueTypes.enumMembers ||\n      includedIssueTypes.namespaceMembers ||\n      includedIssueTypes.duplicates,\n    isReportFiles: includedIssueTypes.files,\n    isReportTypes:\n      includedIssueTypes.types ||\n      includedIssueTypes.nsTypes ||\n      includedIssueTypes.enumMembers ||\n      includedIssueTypes.namespaceMembers,\n    isReportValues: includedIssueTypes.exports || includedIssueTypes.nsExports,\n    isSession: options.isSession ?? false,\n    isShowProgress:\n      !isDebug &&\n      !isTrace &&\n      args['no-progress'] !== true &&\n      options.isShowProgress !== false &&\n      process.stdout.isTTY &&\n      typeof process.stdout.cursorTo === 'function',\n\n    isStrict,\n    isTrace,\n    isTreatConfigHintsAsErrors: args['treat-config-hints-as-errors'] ?? parsedConfig.treatConfigHintsAsErrors ?? false,\n    isUseTscFiles: options.isUseTscFiles ?? args['use-tsconfig-files'] ?? (options.isSession && !configFilePath),\n    isWatch: args.watch ?? options.isWatch ?? false,\n    maxShowIssues: args['max-show-issues'] ? Number(args['max-show-issues']) : undefined,\n    parsedConfig,\n    rules,\n    tags,\n    traceDependency: args['trace-dependency'],\n    traceExport: args['trace-export'],\n    traceFile: args['trace-file'] ? toAbsolute(args['trace-file'], cwd) : undefined,\n    tsConfigFile: args.tsConfig,\n    workspace,\n    workspaces,\n  };\n};\n\nexport type MainOptions = Awaited<ReturnType<typeof createOptions>>;\n"
  },
  {
    "path": "packages/knip/src/util/create-workspace-graph.ts",
    "content": "import type { WorkspacePackage } from '../types/package-json.ts';\nimport { join } from './path.ts';\n\nexport type WorkspaceGraph = Map<string, Set<string>>;\n\nconst types = ['peerDependencies', 'devDependencies', 'optionalDependencies', 'dependencies'] as const;\n\nexport function createWorkspaceGraph(\n  cwd: string,\n  wsNames: string[],\n  wsPkgNames: Set<string>,\n  wsPackages: Map<string, WorkspacePackage>\n) {\n  const graph: WorkspaceGraph = new Map();\n\n  const packagesByPkgName = new Map<string, WorkspacePackage>();\n  for (const pkg of wsPackages.values()) if (pkg.pkgName) packagesByPkgName.set(pkg.pkgName, pkg);\n\n  const getWorkspaceDirs = (pkg: WorkspacePackage) => {\n    const dirs = new Set<string>();\n    for (const type of types) {\n      if (pkg.manifest[type]) {\n        for (const pkgName in pkg.manifest[type]) {\n          if (wsPkgNames.has(pkgName)) {\n            const wsPackage = packagesByPkgName.get(pkgName);\n            if (wsPackage) dirs.add(wsPackage.dir);\n          }\n        }\n      }\n    }\n    return dirs;\n  };\n\n  for (const name of wsNames) {\n    const pkg = wsPackages.get(name);\n    if (pkg) graph.set(join(cwd, name), getWorkspaceDirs(pkg));\n  }\n\n  return graph;\n}\n"
  },
  {
    "path": "packages/knip/src/util/debug.ts",
    "content": "/* oxlint-disable no-console */\nimport util, { parseArgs } from 'node:util';\nimport picocolors from 'picocolors';\n\nconst { values } = parseArgs({ strict: false, options: { debug: { type: 'boolean' } } });\n\nconst IS_DEBUG_ENABLED = values.debug ?? false;\n\nconst IS_COLORS = !process.env.NO_COLOR;\n\nconst noop = () => {};\n\nconst inspectOptions = { maxArrayLength: null, depth: null, colors: IS_COLORS };\n\n/** @public */\nexport const inspect = (obj: unknown) => console.log(util.inspect(obj, inspectOptions));\n\nconst ctx = (text: string | [string, string]) =>\n  typeof text === 'string'\n    ? picocolors.yellow(`[${text}]`)\n    : `${picocolors.yellow(`[${text[0]}]`)} ${picocolors.cyan(text[1])}`;\n\n// Inspect arrays, otherwise Node [will, knip, ...n-100 more items]\nconst logArray = (collection: string[]) => {\n  console.log(util.inspect(collection.sort(), inspectOptions));\n};\n\nexport const debugLog = IS_DEBUG_ENABLED\n  ? (context: string, message: string) => console.log(`${ctx(context)} ${message}`)\n  : noop;\n\nexport const debugLogObject = IS_DEBUG_ENABLED\n  ? (context: string | [string, string], name: string, obj: unknown | (() => unknown)) => {\n      console.log(`${ctx(context)} ${name}`);\n      console.log(util.inspect(typeof obj === 'function' ? obj() : obj, inspectOptions));\n    }\n  : noop;\n\nexport const debugLogArray = IS_DEBUG_ENABLED\n  ? (\n      context: string | [string, string],\n      message: string,\n      elements: string[] | Set<string> | (() => string[] | Set<string>)\n    ) => {\n      const collection = Array.from(typeof elements === 'function' ? elements() : elements);\n      console.debug(`${ctx(context)} ${message} (${collection.length})`);\n      logArray(collection);\n    }\n  : noop;\n"
  },
  {
    "path": "packages/knip/src/util/empty.ts",
    "content": ""
  },
  {
    "path": "packages/knip/src/util/errors.ts",
    "content": "import { core } from 'zod/mini';\n\nconst isZodErrorLike = (error: unknown): error is core.$ZodError<any> => error instanceof core.$ZodError;\n\ninterface ErrorWithCause extends Error {\n  cause: Error;\n}\n\nexport class ConfigurationError extends Error {}\n\nexport class LoaderError extends Error {}\n\nexport const isKnownError = (error: Error) =>\n  error instanceof ConfigurationError || error instanceof LoaderError || isZodErrorLike(error);\n\nexport const hasErrorCause = (error: Error): error is ErrorWithCause =>\n  !isZodErrorLike(error) && error.cause instanceof Error;\n\nexport const isConfigurationError = (error: Error) => error instanceof ConfigurationError;\n\nexport const isModuleNotFoundError = (error: Error): boolean => 'code' in error && error.code === 'MODULE_NOT_FOUND';\n\nexport const isLoaderError = (error: Error): error is LoaderError => error instanceof LoaderError;\n\nexport const getKnownErrors = (error: Error) => {\n  if (isZodErrorLike(error))\n    return [...error.issues].map(error => {\n      let message = error.message;\n      const details = [];\n      if (error.path.length > 0) details.push(`location: ${error.path.join('.')}`);\n      // @ts-expect-error\n      if (typeof error.expected === 'string') details.push(`expected: ${error.expected}`);\n      // @ts-expect-error\n      if (Array.isArray(error.keys)) details.push(`${error.code}: ${error.keys.join(', ')}`);\n      if (details.length > 0) message += ` (${details.join(', ')})`;\n      return new Error(message);\n    });\n  return [error];\n};\n"
  },
  {
    "path": "packages/knip/src/util/file-entry-cache.ts",
    "content": "/**\n * Credits for this idea go to file-entry-cache, this is a modified and simplified version.\n * - https://github.com/jaredwray/cacheable/blob/main/packages/file-entry-cache/README.md\n * - https://github.com/jaredwray/cacheable/blob/main/packages/file-entry-cache/LICENSE\n */\nimport fs from 'node:fs';\n// oxlint-disable-next-line no-restricted-imports\nimport path from 'node:path';\nimport { deserialize, serialize } from 'node:v8';\nimport { debugLog } from './debug.ts';\nimport { isDirectory, isFile } from './fs.ts';\nimport { timerify } from './Performance.ts';\nimport { dirname, isAbsolute, resolve } from './path.ts';\n\ntype MetaData<T> = { size: number; mtime: number; data?: T };\n\nexport type FileDescriptor<T> = {\n  key: string;\n  changed?: boolean;\n  notFound?: boolean;\n  err?: unknown;\n  meta?: MetaData<T>;\n};\n\nconst createCache = (filePath: string) => {\n  try {\n    return deserialize(fs.readFileSync(filePath));\n  } catch (_err) {\n    debugLog('*', `Error reading cache from ${filePath}`);\n  }\n};\n\nconst create = timerify(createCache);\n\nexport class FileEntryCache<T> {\n  filePath: string;\n  cache = new Map<string, MetaData<T>>();\n  normalizedEntries = new Map<string, FileDescriptor<T>>();\n\n  constructor(cacheId: string, _path: string) {\n    this.filePath = path.resolve(_path, cacheId);\n    if (isFile(this.filePath)) this.cache = create(this.filePath);\n    this.removeNotFoundFiles();\n  }\n\n  removeNotFoundFiles() {\n    for (const filePath of this.normalizedEntries.keys()) {\n      try {\n        fs.statSync(filePath);\n      } catch (error) {\n        // @ts-expect-error\n        if (error.code === 'ENOENT') this.cache.delete(filePath);\n      }\n    }\n  }\n\n  getFileDescriptor(filePath: string): FileDescriptor<T> {\n    let fstat: fs.Stats;\n\n    try {\n      if (!isAbsolute(filePath)) filePath = resolve(filePath);\n      fstat = fs.statSync(filePath);\n    } catch (error: unknown) {\n      this.removeEntry(filePath);\n      return { key: filePath, notFound: true, err: error };\n    }\n\n    return this._getFileDescriptorUsingMtimeAndSize(filePath, fstat);\n  }\n\n  _getFileDescriptorUsingMtimeAndSize(filePath: string, fstat: fs.Stats) {\n    let meta = this.cache.get(filePath);\n    const cacheExists = Boolean(meta);\n\n    const cSize = fstat.size;\n    const cTime = fstat.mtime.getTime();\n\n    let isDifferentDate: undefined | boolean;\n    let isDifferentSize: undefined | boolean;\n\n    if (meta) {\n      isDifferentDate = cTime !== meta.mtime;\n      isDifferentSize = cSize !== meta.size;\n    } else {\n      meta = { size: cSize, mtime: cTime };\n    }\n\n    const fd: FileDescriptor<T> = {\n      key: filePath,\n      changed: !cacheExists || isDifferentDate || isDifferentSize,\n      meta,\n    };\n\n    this.normalizedEntries.set(filePath, fd);\n\n    return fd;\n  }\n\n  removeEntry(entryName: string) {\n    if (!isAbsolute(entryName)) entryName = resolve(entryName);\n    this.normalizedEntries.delete(entryName);\n    this.cache.delete(entryName);\n  }\n\n  _getMetaForFileUsingMtimeAndSize(cacheEntry: FileDescriptor<T>) {\n    const stat = fs.statSync(cacheEntry.key);\n    const meta = Object.assign(cacheEntry.meta ?? {}, {\n      size: stat.size,\n      mtime: stat.mtime.getTime(),\n    });\n    return meta;\n  }\n\n  reconcile() {\n    this.removeNotFoundFiles();\n\n    for (const [entryName, cacheEntry] of this.normalizedEntries) {\n      try {\n        const meta = this._getMetaForFileUsingMtimeAndSize(cacheEntry);\n        this.cache.set(entryName, meta);\n      } catch (error) {\n        // @ts-expect-error\n        if (error.code !== 'ENOENT') throw error;\n      }\n    }\n\n    try {\n      const dir = dirname(this.filePath);\n      if (!isDirectory(dir)) fs.mkdirSync(dir, { recursive: true });\n      fs.writeFileSync(this.filePath, serialize(this.cache));\n    } catch (_err) {\n      debugLog('*', `Error writing cache to ${this.filePath}`);\n    }\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/util/fs.ts",
    "content": "import { existsSync, readdirSync, statSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { parse as parseYAMLContents } from 'yaml';\nimport { parse as parseTOML } from 'smol-toml';\nimport stripJsonComments from 'strip-json-comments';\nimport { LoaderError } from './errors.ts';\nimport { extname, join } from './path.ts';\n\nexport const isDirectory = (cwdOrPath: string, name?: string) => {\n  try {\n    return statSync(name ? join(cwdOrPath, name) : cwdOrPath).isDirectory();\n  } catch {\n    return false;\n  }\n};\n\nexport const isFile = (cwdOrPath: string, name?: string) => {\n  try {\n    return statSync(name ? join(cwdOrPath, name) : cwdOrPath).isFile();\n  } catch {\n    return false;\n  }\n};\n\nexport const findFile = (cwd: string, fileName: string) => {\n  const filePath = join(cwd, fileName);\n  return isFile(filePath) ? filePath : undefined;\n};\n\nexport const findFileWithExtensions = (basePath: string, extensions: string[]): string | undefined => {\n  for (const ext of extensions) {\n    const candidate = basePath + ext;\n    if (existsSync(candidate)) return candidate;\n  }\n};\n\nexport const loadFile = async (filePath: string) => {\n  try {\n    const contents = await readFile(filePath);\n    return contents.toString();\n  } catch (error) {\n    throw new LoaderError(`Error loading ${filePath}`, { cause: error });\n  }\n};\n\nexport const hasFileWithExtension = (cwd: string, dirName: string, extensions: string[]): boolean => {\n  if (!isDirectory(cwd, dirName)) return false;\n\n  try {\n    for (const file of readdirSync(join(cwd, dirName))) {\n      if (extensions.includes(extname(file))) return true;\n    }\n  } catch {}\n  return false;\n};\n\nexport const loadJSON = async (filePath: string) => {\n  const contents = await loadFile(filePath);\n  try {\n    return JSON.parse(contents);\n  } catch {\n    return parseJSONC(filePath, contents);\n  }\n};\n\nexport const loadJSONC = async (filePath: string) => {\n  const contents = await loadFile(filePath);\n  return parseJSONC(filePath, contents);\n};\n\nexport const loadYAML = async (filePath: string) => {\n  const contents = await loadFile(filePath);\n  return parseYAML(contents);\n};\n\nexport const loadTOML = async (filePath: string) => {\n  const contents = await loadFile(filePath);\n  return parseTOML(contents);\n};\n\nexport const parseJSONC = async (filePath: string, contents: string) => {\n  try {\n    return JSON.parse(stripJsonComments(contents, { trailingCommas: true, whitespace: false }));\n  } catch (error) {\n    const message = `Error parsing ${filePath} ${extname(filePath) === '.json5' ? 'JSON5 features beyond comments and trailing commas are not fully supported. Consider converting to .jsonc format.' : ''}`;\n    throw new LoaderError(message, { cause: error });\n  }\n};\n\nexport const parseYAML = (contents: string) => {\n  return parseYAMLContents(contents, { logLevel: 'error' });\n};\n"
  },
  {
    "path": "packages/knip/src/util/get-included-issue-types.ts",
    "content": "import { ISSUE_TYPES } from '../constants.ts';\nimport type { Report } from '../types/issues.ts';\nimport { ConfigurationError } from './errors.ts';\n\ntype GetIncludedIssueTypesOptions = {\n  isProduction?: boolean;\n  include: string[];\n  exclude: string[];\n  includeOverrides?: string[];\n  excludeOverrides?: string[];\n};\n\n/** @internal */\nexport const defaultExcludedIssueTypes = ['nsExports', 'nsTypes'];\nconst defaultIssueTypes = ISSUE_TYPES.filter(type => !defaultExcludedIssueTypes.includes(type));\n\nconst normalize = (values: string[]) => values.flatMap(value => value.split(','));\n\nexport const shorthandDeps = ['dependencies', 'unlisted', 'binaries', 'unresolved', 'catalog'];\nexport const shorthandExports = ['exports', 'types', 'enumMembers', 'namespaceMembers', 'duplicates'];\nexport const shorthandFiles = ['files'];\n\nexport const getIncludedIssueTypes = (options: GetIncludedIssueTypesOptions) => {\n  // Allow space-separated argument values (--include files,dependencies)\n  const incl = normalize(options.includeOverrides ?? []);\n  const excl = normalize(options.excludeOverrides ?? []);\n\n  // Naming is hard...\n  for (const type of [...incl, ...excl, ...options.include, ...options.exclude]) {\n    // @ts-expect-error The point is that we're checking for invalid issue types\n    if (!ISSUE_TYPES.includes(type)) throw new ConfigurationError(`Invalid issue type: ${type}`);\n  }\n\n  // CLI arguments override local options\n  const excludes = options.exclude.filter(exclude => !incl.includes(exclude));\n  const includes = options.include.filter(include => !excl.includes(include));\n\n  const _include = [...incl, ...includes];\n  const _exclude = [...excl, ...excludes];\n\n  if (options.isProduction) {\n    // Ignore devDependencies when analyzing production code\n    _exclude.push('devDependencies');\n    _exclude.push('catalog');\n  } else {\n    // Auto-add (or remove) `devDependencies` when `dependencies` are included (or excluded)\n    if (_include.includes('dependencies')) _include.push('devDependencies', 'optionalPeerDependencies');\n    if (_exclude.includes('dependencies')) _exclude.push('devDependencies', 'optionalPeerDependencies');\n  }\n\n  const included = (\n    _include.length > 0\n      ? _include.some(type => !defaultExcludedIssueTypes.includes(type))\n        ? _include\n        : [..._include, ...defaultIssueTypes]\n      : defaultIssueTypes\n  ).filter(group => !_exclude.includes(group));\n\n  return Object.fromEntries(ISSUE_TYPES.map(group => [group, included.includes(group)])) as Report;\n};\n"
  },
  {
    "path": "packages/knip/src/util/git.ts",
    "content": "import { execSync } from 'node:child_process';\nimport { join } from './path.ts';\n\n// TODO More hooks exists, but is it worth adding all of them?\n// https://git-scm.com/docs/githooks\n// https://github.com/fisker/git-hooks-list/blob/main/index.json\n\nconst hookFileNames = [\n  'prepare-commit-msg',\n  'commit-msg',\n  'pre-{applypatch,commit,merge-commit,push,rebase,receive}',\n  'post-{checkout,commit,merge,rewrite}',\n];\n\nconst getGitHooksPath = (defaultPath = '.git/hooks', cwd: string | undefined) => {\n  try {\n    return execSync('git rev-parse --git-path hooks', {\n      encoding: 'utf8',\n      stdio: ['pipe', 'pipe', 'ignore'],\n      cwd,\n    }).trim();\n  } catch (_error) {\n    return defaultPath;\n  }\n};\n\nexport const getGitHookPaths = (defaultPath = '.git/hooks', followGitConfig = true, cwd?: string) => {\n  const gitHooksPath = followGitConfig ? getGitHooksPath(defaultPath, cwd) : defaultPath;\n  return hookFileNames.map(fileName => join(gitHooksPath, fileName));\n};\n"
  },
  {
    "path": "packages/knip/src/util/glob-core.ts",
    "content": "import { readFileSync } from 'node:fs';\nimport { promisify } from 'node:util';\nimport { walk as _walk, type Entry } from '@nodelib/fs.walk';\nimport fg, { type Options as FastGlobOptions } from 'fast-glob';\nimport picomatch from 'picomatch';\nimport { GLOBAL_IGNORE_PATTERNS } from '../constants.ts';\nimport { compact, partition } from './array.ts';\nimport { debugLogObject } from './debug.ts';\nimport { isDirectory, isFile } from './fs.ts';\nimport { timerify } from './Performance.ts';\nimport { parseAndConvertGitignorePatterns } from './parse-and-convert-gitignores.ts';\nimport { dirname, join, relative, toPosix } from './path.ts';\n\nconst walk = promisify(_walk);\n\ntype Options = { gitignore: boolean; cwd: string };\n\ninterface GlobOptions extends FastGlobOptions {\n  gitignore: boolean;\n  cwd: string;\n  dir: string;\n  label?: string;\n}\n\ntype Gitignores = { ignores: Set<string>; unignores: Set<string> };\n\n// ignore patterns are cached per gitignore file\nconst cachedGitIgnores = new Map<string, Gitignores>();\n// ignore patterns are cached per directory as a product of .gitignore in current and ancestor directories\nconst cachedGlobIgnores = new Map<string, string[]>();\n\n// Check if directory is a git root (has .git directory or .git file for worktrees)\nconst isGitRoot = (dir: string) => isDirectory(dir, '.git') || isFile(dir, '.git');\n\n// Get the git directory path, handling worktrees where .git is a file containing \"gitdir: /path/to/git/dir\"\nconst getGitDir = (cwd: string): string | undefined => {\n  const dotGit = join(cwd, '.git');\n  if (isDirectory(dotGit)) return dotGit;\n  if (isFile(dotGit)) {\n    const content = readFileSync(dotGit, 'utf8').trim();\n    const match = content.match(/^gitdir:\\s*(.+)$/);\n    if (match) return join(cwd, match[1]);\n  }\n  return undefined;\n};\n\nconst findAncestorGitignoreFiles = (cwd: string): string[] => {\n  const gitignorePaths: string[] = [];\n  if (isGitRoot(cwd)) return gitignorePaths;\n  let dir = dirname(cwd);\n  let prev: string;\n  while (dir) {\n    const filePath = join(dir, '.gitignore');\n    if (isFile(filePath)) gitignorePaths.push(filePath);\n    if (isGitRoot(dir)) break;\n    // oxlint-disable-next-line no-cond-assign\n    dir = dirname((prev = dir));\n    if (prev === dir || dir === '.') break;\n  }\n  return gitignorePaths;\n};\n\n/** @internal */\nexport const findAndParseGitignores = async (cwd: string, workspaceDirs?: Set<string>) => {\n  const ignores: Set<string> = new Set(GLOBAL_IGNORE_PATTERNS);\n  const unignores: string[] = [];\n  const gitignoreFiles: string[] = [];\n  const pmOptions = { ignore: unignores };\n\n  let deepFilterMatcher: ((str: string) => boolean) | undefined;\n  let prevUnignoreLength = unignores.length;\n  const pendingIgnores: string[] = [];\n\n  const getMatcher = () => {\n    if (!deepFilterMatcher) {\n      deepFilterMatcher = picomatch(Array.from(ignores), pmOptions);\n      pendingIgnores.length = 0;\n    } else if (pendingIgnores.length > 0) {\n      const prev = deepFilterMatcher;\n      const incr = picomatch(pendingIgnores.splice(0), pmOptions);\n      deepFilterMatcher = (path: string) => prev(path) || incr(path);\n    }\n    return deepFilterMatcher;\n  };\n\n  const addFile = (filePath: string, baseDir?: string) => {\n    gitignoreFiles.push(relative(cwd, filePath));\n\n    const dir = baseDir ?? dirname(toPosix(filePath));\n    const base = relative(cwd, dir);\n    const ancestor = base.startsWith('..') ? `${relative(dir, cwd)}/` : undefined;\n\n    const ignoresForDir = new Set(base === '' ? GLOBAL_IGNORE_PATTERNS : []);\n    const unignoresForDir = new Set<string>();\n    const prevIgnoreSize = ignores.size;\n\n    const patterns = readFileSync(filePath, 'utf8');\n\n    for (const rule of parseAndConvertGitignorePatterns(patterns, ancestor)) {\n      const [pattern, extraPattern] = rule.patterns;\n      if (rule.negated) {\n        if (base === '' || base.startsWith('..')) {\n          if (!unignores.includes(extraPattern)) {\n            unignores.push(...rule.patterns);\n            unignoresForDir.add(pattern);\n            unignoresForDir.add(extraPattern);\n          }\n        } else {\n          if (!unignores.includes(extraPattern.startsWith('**/') ? extraPattern : `**/${extraPattern}`)) {\n            const unignore = join(base, pattern);\n            const extraUnignore = join(base, extraPattern);\n            unignores.push(unignore, extraUnignore);\n            unignoresForDir.add(unignore);\n            unignoresForDir.add(extraUnignore);\n          }\n        }\n      } else {\n        if (base === '' || base.startsWith('..')) {\n          ignores.add(pattern);\n          ignores.add(extraPattern);\n          ignoresForDir.add(pattern);\n          ignoresForDir.add(extraPattern);\n        } else if (!unignores.includes(extraPattern.startsWith('**/') ? extraPattern : `**/${extraPattern}`)) {\n          const ignore = join(base, pattern);\n          const extraIgnore = join(base, extraPattern);\n          ignores.add(ignore);\n          ignores.add(extraIgnore);\n          ignoresForDir.add(ignore);\n          ignoresForDir.add(extraIgnore);\n        }\n      }\n    }\n\n    const cacheDir = ancestor ? cwd : dir;\n    const cacheForDir = cachedGitIgnores.get(cacheDir);\n\n    if (cacheForDir) {\n      for (const pattern of ignoresForDir) cacheForDir.ignores.add(pattern);\n      for (const pattern of unignoresForDir) cacheForDir.unignores.add(pattern);\n    } else {\n      cachedGitIgnores.set(cacheDir, { ignores: ignoresForDir, unignores: unignoresForDir });\n    }\n\n    if (unignores.length !== prevUnignoreLength) {\n      deepFilterMatcher = undefined;\n      prevUnignoreLength = unignores.length;\n    } else if (ignores.size !== prevIgnoreSize) {\n      for (const p of ignoresForDir) if (!GLOBAL_IGNORE_PATTERNS.includes(p)) pendingIgnores.push(p);\n    }\n  };\n\n  for (const filePath of findAncestorGitignoreFiles(cwd)) addFile(filePath);\n\n  const gitDir = getGitDir(cwd);\n  if (gitDir) {\n    const excludePath = join(gitDir, 'info/exclude');\n    if (isFile(excludePath)) addFile(excludePath, cwd);\n  }\n\n  // Precompute relevant directories from workspace dirs to avoid walking irrelevant subtrees (e.g. generated output dirs)\n  let isRelevantDir: ((absPath: string) => boolean) | undefined;\n  if (workspaceDirs && workspaceDirs.size > 0) {\n    const relevantAncestors = new Set<string>();\n    const nonRootDirs = new Set<string>();\n    for (const wsDir of workspaceDirs) {\n      if (wsDir !== cwd) nonRootDirs.add(wsDir);\n      let dir = wsDir;\n      while (dir.length >= cwd.length) {\n        relevantAncestors.add(dir);\n        const parent = dirname(dir);\n        if (parent === dir) break;\n        dir = parent;\n      }\n    }\n    if (nonRootDirs.size > 0) {\n      isRelevantDir = (absPath: string) => {\n        if (relevantAncestors.has(absPath)) return true;\n        for (const wsDir of nonRootDirs) if (absPath.startsWith(`${wsDir}/`)) return true;\n        return false;\n      };\n    }\n  }\n\n  const entryFilter = (entry: Entry) => {\n    if (entry.dirent.isFile() && entry.name === '.gitignore') {\n      addFile(entry.path);\n      return true;\n    }\n    return false;\n  };\n\n  const deepFilter = (entry: Entry) =>\n    (!isRelevantDir || isRelevantDir(toPosix(entry.path))) && !getMatcher()(relative(cwd, entry.path));\n\n  await walk(cwd, {\n    concurrency: 16,\n    entryFilter,\n    deepFilter,\n  });\n\n  debugLogObject('*', 'Parsed gitignore files', { gitignoreFiles });\n\n  return { gitignoreFiles, ignores, unignores };\n};\n\nconst _parseFindGitignores = timerify(findAndParseGitignores);\n\nexport async function glob(_patterns: string[], options: GlobOptions): Promise<string[]> {\n  if (Array.isArray(_patterns) && _patterns.length === 0) return [];\n\n  const hasCache = cachedGlobIgnores.has(options.dir);\n  const willCache = !hasCache && options.gitignore && options.label;\n  const cachedIgnores = options.gitignore ? cachedGlobIgnores.get(options.dir) : undefined;\n\n  const _ignore: string[] = [...GLOBAL_IGNORE_PATTERNS];\n  const [negatedPatterns, patterns] = partition(_patterns, pattern => pattern.startsWith('!'));\n\n  if (options.gitignore && willCache) {\n    let dir = options.dir;\n    let prev: string;\n    while (dir) {\n      const cacheForDir = cachedGitIgnores.get(dir);\n      if (cacheForDir) {\n        // fast-glob doesn't support negated patterns in `ignore` (i.e. unignores are.. ignored): https://github.com/mrmlnc/fast-glob/issues/86\n        _ignore.push(...cacheForDir.ignores);\n      }\n      // oxlint-disable-next-line no-cond-assign\n      dir = dirname((prev = dir));\n      if (prev === dir || dir === '.') break;\n    }\n  }\n\n  if (willCache) cachedGlobIgnores.set(options.dir, compact(_ignore));\n\n  const ignorePatterns = (cachedIgnores || _ignore).concat(negatedPatterns.map(pattern => pattern.slice(1)));\n\n  const { dir, label, ...fgOptions } = { ...options, ignore: ignorePatterns };\n\n  const paths = await fg.glob(patterns, fgOptions);\n\n  debugLogObject(relative(options.cwd, dir), label ? `Finding ${label}` : 'Finding paths', () => ({\n    patterns,\n    ...fgOptions,\n    ignore:\n      hasCache && ignorePatterns.length === (cachedIgnores || _ignore).length\n        ? `// using cache from previous glob cwd: ${fgOptions.cwd}`\n        : ignorePatterns,\n    paths,\n  }));\n\n  return paths;\n}\n\nexport async function getGitIgnoredHandler(\n  options: Options,\n  workspaceDirs?: Set<string>\n): Promise<(path: string) => boolean> {\n  cachedGitIgnores.clear();\n\n  if (options.gitignore === false) return () => false;\n\n  const { ignores, unignores } = await _parseFindGitignores(options.cwd, workspaceDirs);\n  const matcher = picomatch(Array.from(ignores), { ignore: unignores });\n\n  const cache = new Map<string, boolean>();\n  const isGitIgnored = (filePath: string) => {\n    let result = cache.get(filePath);\n    if (result === undefined) {\n      result = matcher(relative(options.cwd, filePath));\n      cache.set(filePath, result);\n    }\n    return result;\n  };\n\n  return isGitIgnored;\n}\n"
  },
  {
    "path": "packages/knip/src/util/glob.ts",
    "content": "import fg from 'fast-glob';\nimport { compact } from './array.ts';\nimport { glob } from './glob-core.ts';\nimport { timerify } from './Performance.ts';\nimport { isAbsolute, join, relative } from './path.ts';\n\ninterface GlobOptions {\n  cwd: string;\n  dir?: string;\n  patterns: string[];\n  gitignore?: boolean;\n  name?: boolean;\n  label?: string;\n}\n\nconst prepend = (pattern: string, relativePath: string) =>\n  isAbsolute(pattern.replace(/^!/, '')) ? pattern : prependDirToPattern(relativePath, pattern);\n\n// Globbing from root as cwd to include all gitignore files and ignore patterns, so we need to prepend dirs to patterns\nconst prependDirToPatterns = (cwd: string, dir: string, patterns: string[]) => {\n  const relativePath = relative(cwd, dir);\n  return compact([patterns].flat().map(p => removeProductionSuffix(prepend(p, relativePath)))).sort(negatedLast);\n};\n\nexport const removeProductionSuffix = (pattern: string) => pattern.replace(/!$/, '');\n\nconst negatedLast = (pattern: string) => (pattern.startsWith('!') ? 1 : -1);\n\nexport const prependDirToPattern = (dir: string, pattern: string) => {\n  if (pattern.startsWith('!')) return `!${join(dir, pattern.slice(1))}`;\n  return join(dir, pattern);\n};\n\nexport const negate = (pattern: string) => pattern.replace(/^!?/, '!');\nexport const hasProductionSuffix = (pattern: string) => pattern.endsWith('!');\nexport const hasNoProductionSuffix = (pattern: string) => !pattern.endsWith('!');\n\nconst defaultGlob = async ({ cwd, dir = cwd, patterns, gitignore = true, label }: GlobOptions) => {\n  if (patterns.length === 0) return [];\n\n  const globPatterns = prependDirToPatterns(cwd, dir, patterns);\n\n  // Only negated patterns? Bail out.\n  if (globPatterns[0].startsWith('!')) return [];\n\n  return glob(globPatterns, {\n    cwd,\n    dir,\n    gitignore,\n    absolute: true,\n    dot: true,\n    label,\n  });\n};\n\nconst syncGlob = ({ cwd, patterns }: { cwd?: string; patterns: string | string[] }) =>\n  fg.sync(patterns, { cwd, followSymbolicLinks: false });\n\nconst dirGlob = async ({ cwd, patterns, gitignore = true }: GlobOptions) =>\n  glob(patterns, {\n    cwd,\n    dir: cwd,\n    onlyDirectories: true,\n    gitignore,\n  });\n\nexport const _glob = timerify(defaultGlob);\n\nexport const _syncGlob = timerify(syncGlob);\n\nexport const _dirGlob = timerify(dirGlob);\n"
  },
  {
    "path": "packages/knip/src/util/graph-sequencer.ts",
    "content": "/**\n * Straight up borrowed from @pnpm/deps.graph-sequencer:\n * - https://github.com/pnpm/pnpm/blob/main/deps/graph-sequencer/README.md\n * - https://github.com/pnpm/pnpm/blob/main/LICENSE\n */\ntype Graph<T> = Map<T, Set<T>>;\ntype Groups<T> = T[][];\n\ninterface Result<T> {\n  safe: boolean;\n  chunks: Groups<T>;\n  cycles: Groups<T>;\n}\n\n/**\n * Performs topological sorting on a graph while supporting node restrictions.\n *\n * @param {Graph<T>}  graph - The graph represented as a Map where keys are nodes and values are their outgoing edges.\n * @param {T[]} includedNodes - An array of nodes that should be included in the sorting process. Other nodes will be ignored.\n * @returns {Result<T>} An object containing the result of the sorting, including safe, chunks, and cycles.\n */\nexport function graphSequencer<T>(graph: Graph<T>, includedNodes: T[] = [...graph.keys()]): Result<T> {\n  // Initialize reverseGraph with empty arrays for all nodes.\n  const reverseGraph = new Map<T, T[]>();\n  for (const key of graph.keys()) {\n    reverseGraph.set(key, []);\n  }\n\n  // Calculate outDegree and reverseGraph for the included nodes.\n  const nodes = new Set<T>(includedNodes);\n  const visited = new Set<T>();\n  const outDegree = new Map<T, number>();\n  for (const [from, edges] of graph) {\n    outDegree.set(from, 0);\n    for (const to of edges) {\n      if (nodes.has(from) && nodes.has(to)) {\n        changeOutDegree(from, 1);\n        // oxlint-disable-next-line @typescript-eslint/no-non-null-assertion\n        reverseGraph.get(to)!.push(from);\n      }\n    }\n    if (!nodes.has(from)) {\n      visited.add(from);\n    }\n  }\n\n  const chunks: T[][] = [];\n  const cycles: T[][] = [];\n  let safe = true;\n  while (nodes.size) {\n    const chunk: T[] = [];\n    let minDegree = Number.MAX_SAFE_INTEGER;\n    for (const node of nodes) {\n      // oxlint-disable-next-line @typescript-eslint/no-non-null-assertion\n      const degree = outDegree.get(node)!;\n      if (degree === 0) {\n        chunk.push(node);\n      }\n      minDegree = Math.min(minDegree, degree);\n    }\n\n    if (minDegree === 0) {\n      chunk.forEach(removeNode);\n      chunks.push(chunk);\n    } else {\n      const cycleNodes: T[] = [];\n      for (const node of nodes) {\n        const cycle = findCycle(node);\n        if (cycle.length) {\n          cycles.push(cycle);\n          cycle.forEach(removeNode);\n          cycleNodes.push(...cycle);\n\n          if (cycle.length > 1) {\n            safe = false;\n          }\n        }\n      }\n      chunks.push(cycleNodes);\n    }\n  }\n\n  return { safe, chunks, cycles };\n\n  // Function to update the outDegree of a node.\n  function changeOutDegree(node: T, value: number) {\n    const degree = outDegree.get(node) ?? 0;\n    outDegree.set(node, degree + value);\n  }\n\n  // Function to remove a node from the graph.\n  function removeNode(node: T) {\n    // oxlint-disable-next-line @typescript-eslint/no-non-null-assertion\n    for (const from of reverseGraph.get(node)!) {\n      changeOutDegree(from, -1);\n    }\n    visited.add(node);\n    nodes.delete(node);\n  }\n\n  function findCycle(startNode: T): T[] {\n    const queue: Array<[T, T[]]> = [[startNode, [startNode]]];\n    const cycleVisited = new Set<T>();\n    const cycles: T[][] = [];\n\n    while (queue.length) {\n      // oxlint-disable-next-line @typescript-eslint/no-non-null-assertion\n      const [id, cycle] = queue.shift()!;\n      const nodes = graph.get(id);\n      if (!nodes) continue;\n      for (const to of nodes) {\n        if (to === startNode) {\n          cycleVisited.add(to);\n          cycles.push([...cycle]);\n          continue;\n        }\n        if (visited.has(to) || cycleVisited.has(to)) {\n          continue;\n        }\n        cycleVisited.add(to);\n        queue.push([to, [...cycle, to]]);\n      }\n    }\n\n    if (!cycles.length) {\n      return [];\n    }\n    cycles.sort((a, b) => b.length - a.length);\n    return cycles[0];\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/util/input.ts",
    "content": "import type { IssueType } from '../types/issues.ts';\nimport type { PluginName } from '../types/PluginNames.ts';\nimport { isAbsolute, toRelative } from './path.ts';\n\ntype InputType =\n  | 'binary'\n  | 'entry'\n  | 'project'\n  | 'config'\n  | 'dependency'\n  | 'deferResolve'\n  | 'deferResolveEntry'\n  | 'alias'\n  | 'ignore';\n\nexport interface Input {\n  type: InputType;\n  specifier: string;\n  production?: boolean;\n  optional?: boolean;\n  dir?: string;\n  containingFilePath?: string;\n  allowIncludeExports?: boolean;\n  skipExportsAnalysis?: boolean;\n  group?: string;\n}\n\nexport interface ConfigInput extends Input {\n  type: 'config';\n  containingFilePath?: string;\n  pluginName: PluginName;\n}\n\ninterface AliasInput extends Input {\n  type: 'alias';\n  prefixes: string[];\n}\n\ninterface IgnoreInput extends Input {\n  type: 'ignore';\n  issueType: IssueType;\n}\n\ntype Options = {\n  optional?: boolean;\n  dir?: string;\n  containingFilePath?: string;\n  allowIncludeExports?: boolean;\n};\n\nexport const fromBinary = (input: Input) => input.specifier;\n\nexport const toBinary = (specifier: string, options: Options = {}): Input => ({\n  type: 'binary',\n  specifier,\n  ...options,\n});\n\nexport const isBinary = (input: Input) => input.type === 'binary';\n\nexport const toEntry = (specifier: string): Input => ({ type: 'entry', specifier });\n\nexport const isEntry = (input: Input) => input.type === 'entry' && !input.production;\n\nexport const toProject = (specifier: string): Input => ({ type: 'project', specifier });\n\nexport const isProject = (input: Input) => input.type === 'project';\n\nexport const toProductionEntry = (specifier: string, options: Options = {}): Input => ({\n  type: 'entry',\n  specifier,\n  production: true,\n  ...options,\n});\n\nexport const isProductionEntry = (input: Input) => input.type === 'entry' && input.production === true;\n\nexport const toConfig = (pluginName: PluginName, specifier: string, options: Options = {}): ConfigInput => ({\n  type: 'config',\n  specifier,\n  pluginName,\n  ...options,\n});\n\nexport const isConfig = (input: Input): input is ConfigInput => input.type === 'config';\n\nexport const toDependency = (specifier: string, options: Options = {}): Input => ({\n  type: 'dependency',\n  specifier,\n  ...options,\n});\n\nexport const isDependency = (input: Input) => input.type === 'dependency';\n\nexport const toProductionDependency = (specifier: string): Input => ({\n  type: 'dependency',\n  specifier,\n  production: true,\n});\n\nexport const toDeferResolve = (specifier: string, options: Options = {}): Input => ({\n  type: 'deferResolve',\n  specifier,\n  ...options,\n});\n\nexport const isDeferResolve = (input: Input) => input.type === 'deferResolve';\n\nexport const toDeferResolveProductionEntry = (specifier: string, options: Options = {}): Input => ({\n  type: 'deferResolveEntry',\n  specifier,\n  production: true,\n  ...options,\n});\n\nexport const isDeferResolveProductionEntry = (input: Input) =>\n  input.type === 'deferResolveEntry' && input.production === true;\n\nexport const toDeferResolveEntry = (specifier: string, options: Options = {}): Input => ({\n  type: 'deferResolveEntry',\n  specifier,\n  ...options,\n});\n\nexport const isDeferResolveEntry = (input: Input) => input.type === 'deferResolveEntry';\n\nexport const toAlias = (specifier: string, prefix: string | string[], options: Options = {}): AliasInput => ({\n  type: 'alias',\n  specifier,\n  prefixes: Array.isArray(prefix) ? prefix : [prefix],\n  ...options,\n});\n\nexport const isAlias = (input: Input): input is AliasInput => input.type === 'alias';\n\nexport const toIgnore = (specifier: string, issueType: IssueType): IgnoreInput => ({\n  type: 'ignore',\n  specifier,\n  issueType,\n});\n\nexport const isIgnore = (input: Input): input is IgnoreInput => input.type === 'ignore';\n\nexport const toDebugString = (input: Input, cwd: string) =>\n  `${input.type}:${isAbsolute(input.specifier) ? toRelative(input.specifier, cwd) : input.specifier}${input.containingFilePath ? ` (${toRelative(input.containingFilePath, cwd)})` : ''}`;\n"
  },
  {
    "path": "packages/knip/src/util/issue-initializers.ts",
    "content": "import { ISSUE_TYPES } from '../constants.ts';\nimport type { Counters, Issues, IssueType, Rules } from '../types/issues.ts';\n\nexport const initIssues = (): Issues =>\n  Object.fromEntries(ISSUE_TYPES.map(issueType => [issueType, {}])) as Record<IssueType, never>;\n\nexport const initCounters = (): Counters => ({\n  ...(Object.fromEntries(ISSUE_TYPES.map(issueType => [issueType, 0])) as Record<IssueType, number>),\n  processed: 0,\n  total: 0,\n});\n\nexport const defaultRules = Object.fromEntries(ISSUE_TYPES.map(issueType => [issueType, 'error'])) as Rules;\n"
  },
  {
    "path": "packages/knip/src/util/jiti.ts",
    "content": "import { fileURLToPath } from 'node:url';\nimport { createJiti, type JitiOptions } from 'jiti';\nimport { join } from './path.ts';\n\nconst empty = join(fileURLToPath(import.meta.url), '../empty.js');\n\nconst options = {\n  alias: {\n    '@rushstack/eslint-config/patch/modern-module-resolution': empty,\n    '@rushstack/eslint-patch/modern-module-resolution': empty,\n  },\n};\n\nconst createLoader = (options: JitiOptions) => createJiti(process.cwd(), options);\n\nexport const jiti = createLoader(options);\n"
  },
  {
    "path": "packages/knip/src/util/load-config.ts",
    "content": "import type { ParsedCLIArgs } from './cli-arguments.ts';\nimport { debugLogObject } from './debug.ts';\nimport { ConfigurationError } from './errors.ts';\nimport { _load } from './loader.ts';\n\nconst unwrapFunction = async (maybeFunction: unknown, options: ParsedCLIArgs) => {\n  if (typeof maybeFunction === 'function') {\n    try {\n      return await maybeFunction(options);\n    } catch (error) {\n      debugLogObject('*', 'Error executing function:', error);\n      throw error;\n    }\n  }\n  return maybeFunction;\n};\n\nexport async function loadResolvedConfigFile(configPath: string, options: ParsedCLIArgs) {\n  const loadedValue = await _load(configPath);\n  try {\n    return await unwrapFunction(loadedValue, options);\n  } catch (_error) {\n    throw new ConfigurationError(`Error running the function from ${configPath}`);\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/util/load-tsconfig.ts",
    "content": "import { readFileSync } from 'node:fs';\nimport { parseTsconfig } from 'get-tsconfig';\nimport stripJsonComments from 'strip-json-comments';\nimport type { CompilerOptions } from '../types/project.ts';\nimport { isFile as _isFile } from './fs.ts';\nimport { _syncGlob } from './glob.ts';\nimport { dirname, isAbsolute, join } from './path.ts';\n\nconst hasGlobChar = (p: string) => p.includes('*') || p.includes('?');\nconst hasExtension = (p: string) => {\n  const last = p.lastIndexOf('/');\n  const base = last >= 0 ? p.slice(last + 1) : p;\n  return base !== '.' && base !== '..' && base.includes('.');\n};\n\nconst resolvePatterns = (patterns: string[] | undefined, dir: string, expandDirs = false): string[] | undefined => {\n  if (!patterns) return undefined;\n  return patterns.map(p => {\n    const resolved = isAbsolute(p) ? p : join(dir, p);\n    return expandDirs && !hasGlobChar(p) && !hasExtension(p) ? join(resolved, '**/*') : resolved;\n  });\n};\n\nconst DEFAULT_INCLUDE = ['**/*'];\n\nconst TS_EXTENSIONS = new Set(['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs']);\nconst isDtsExt = /\\.d\\.(m|c)?ts$/;\nconst isTsRelevant = (filePath: string) => {\n  if (isDtsExt.test(filePath)) return true;\n  const ext = filePath.slice(filePath.lastIndexOf('.'));\n  return TS_EXTENSIONS.has(ext);\n};\n\nconst expandFileNames = (\n  dir: string,\n  compilerOptions: CompilerOptions,\n  include?: string[],\n  exclude?: string[],\n  files?: string[]\n): string[] => {\n  const result: string[] = [];\n\n  if (files) {\n    for (const file of files) result.push(file);\n  }\n\n  const effectiveExclude = [...(exclude ?? []), join(dir, 'node_modules/**')];\n  if (compilerOptions.outDir) {\n    effectiveExclude.push(join(compilerOptions.outDir, '**'));\n  }\n\n  const effectiveInclude = include ?? (files ? undefined : DEFAULT_INCLUDE.map(p => join(dir, p)));\n  if (effectiveInclude) {\n    const negated = effectiveExclude.map(p => `!${p}`);\n    const globbed = _syncGlob({ patterns: [...effectiveInclude, ...negated], cwd: dir });\n    for (const f of globbed) if (isTsRelevant(f)) result.push(f);\n  }\n\n  return result;\n};\n\nconst resolveConfig = (tsConfigFilePath: string) => {\n  try {\n    return parseTsconfig(tsConfigFilePath);\n  } catch {\n    try {\n      const raw = readFileSync(tsConfigFilePath, 'utf8');\n      return JSON.parse(stripJsonComments(raw));\n    } catch {\n      return undefined;\n    }\n  }\n};\n\nexport const loadTSConfig = async (tsConfigFilePath: string) => {\n  if (_isFile(tsConfigFilePath)) {\n    const config = resolveConfig(tsConfigFilePath);\n    if (!config) return { isFile: true, compilerOptions: {} as CompilerOptions, fileNames: [] as string[] };\n\n    const dir = dirname(tsConfigFilePath);\n    const compilerOptions = (config.compilerOptions ?? {}) as CompilerOptions;\n\n    if (compilerOptions.outDir && !isAbsolute(compilerOptions.outDir)) {\n      compilerOptions.outDir = join(dir, compilerOptions.outDir);\n    }\n    if (compilerOptions.rootDir && !isAbsolute(compilerOptions.rootDir)) {\n      compilerOptions.rootDir = join(dir, compilerOptions.rootDir);\n    }\n    if (compilerOptions.paths) {\n      compilerOptions.pathsBasePath ??= dir;\n    }\n\n    const include = resolvePatterns(config.include, dir, true);\n    const exclude = resolvePatterns(config.exclude, dir, true);\n    const files = resolvePatterns(config.files, dir);\n    const fileNames = expandFileNames(dir, compilerOptions, include, exclude, files);\n\n    return { isFile: true, compilerOptions, fileNames };\n  }\n  return { isFile: false, compilerOptions: {} as CompilerOptions, fileNames: [] as string[] };\n};\n"
  },
  {
    "path": "packages/knip/src/util/loader.ts",
    "content": "import { LoaderError } from './errors.ts';\nimport { loadFile, loadJSON, loadJSONC, loadTOML, loadYAML, parseJSONC, parseYAML } from './fs.ts';\nimport { jiti } from './jiti.ts';\nimport { timerify } from './Performance.ts';\nimport { extname, isInternal } from './path.ts';\n\nconst load = async (filePath: string) => {\n  try {\n    const ext = extname(filePath);\n    if (filePath.endsWith('rc')) {\n      const contents = await loadFile(filePath);\n      try {\n        return parseYAML(contents);\n      } catch {\n        return parseJSONC(filePath, contents);\n      }\n    }\n\n    if (ext === '.yaml' || ext === '.yml') {\n      return await loadYAML(filePath);\n    }\n\n    if (ext === '' && isInternal(filePath)) {\n      return await loadFile(filePath);\n    }\n\n    if (ext === '.json') {\n      return await loadJSON(filePath);\n    }\n\n    if (ext === '.jsonc' || ext === '.json5') {\n      return await loadJSONC(filePath);\n    }\n\n    if ('Bun' in globalThis) {\n      const imported = await import(filePath);\n      return imported.default ?? imported;\n    }\n\n    if (ext === '.toml') {\n      return await loadTOML(filePath);\n    }\n\n    return await jiti.import(filePath, { default: true });\n  } catch (error) {\n    throw new LoaderError(`Error loading ${filePath}`, { cause: error });\n  }\n};\n\nexport const _load = timerify(load);\n"
  },
  {
    "path": "packages/knip/src/util/log.ts",
    "content": "/* oxlint-disable no-console */\nimport picocolors from 'picocolors';\n\nexport const logWarning = (prefix: string, message: string) => {\n  console.warn(`${picocolors.yellow(prefix)}: ${message}`);\n};\n\nexport const logError = (prefix: string, message: string) => {\n  console.error(`${picocolors.red(prefix)}: ${message}`);\n};\n"
  },
  {
    "path": "packages/knip/src/util/map-workspaces.ts",
    "content": "import { readFile } from 'node:fs/promises';\nimport fg from 'fast-glob';\nimport type { PackageJson, WorkspacePackage } from '../types/package-json.ts';\nimport { partition } from './array.ts';\nimport { ConfigurationError } from './errors.ts';\nimport { getPackageName } from './package-name.ts';\nimport { join } from './path.ts';\n\ntype Packages = Map<string, WorkspacePackage>;\ntype WorkspacePkgNames = Set<string>;\n\nexport default async function mapWorkspaces(cwd: string, workspaces: string[]): Promise<[Packages, WorkspacePkgNames]> {\n  const [negatedPatterns, patterns] = partition(workspaces, p => p.match(/^!/));\n  const packages: Packages = new Map();\n  const wsPkgNames: WorkspacePkgNames = new Set();\n\n  if (patterns.length === 0 && negatedPatterns.length === 0) return [packages, wsPkgNames];\n\n  const manifestPatterns = patterns.map(p => join(p, 'package.json'));\n\n  const matches = await fg.glob(manifestPatterns, {\n    cwd,\n    ignore: ['**/node_modules/**', ...negatedPatterns.map(p => p.slice(1))],\n  });\n\n  for (const match of matches.sort()) {\n    const name = match === 'package.json' ? '.' : match.replace(/\\/package\\.json$/, '');\n    const dir = join(cwd, name);\n    const manifestPath = join(cwd, match);\n    try {\n      const manifestStr = await readFile(manifestPath, 'utf8');\n      const manifest: PackageJson = JSON.parse(manifestStr);\n      const pkgName = getPackageName(manifest, dir);\n      const pkg: WorkspacePackage = { dir, name, pkgName, manifestPath, manifestStr, manifest };\n      packages.set(name, pkg);\n      if (pkgName) wsPkgNames.add(pkgName);\n      else throw new ConfigurationError(`Missing package name in ${manifestPath}`);\n    } catch (error) {\n      // @ts-expect-error\n      if (error?.code === 'ENOENT') debugLog('*', `Unable to load package.json for ${name}`);\n      else throw error;\n    }\n  }\n\n  return [packages, wsPkgNames];\n}\n"
  },
  {
    "path": "packages/knip/src/util/math.ts",
    "content": "export function getStats(values: number[]) {\n  if (values.length === 0) return { min: 0, max: 0, sum: 0, median: 0 };\n\n  const sorted = values.sort((a, b) => a - b);\n  const min = sorted[0];\n  const max = sorted[sorted.length - 1];\n  const sum = sorted.reduce((a, b) => a + b, 0);\n  const mid = Math.floor(sorted.length / 2);\n  const median = sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;\n\n  return { min, max, sum, median };\n}\n"
  },
  {
    "path": "packages/knip/src/util/module-graph.ts",
    "content": "import type {\n  FileNode,\n  IdToFileMap,\n  IdToNsToFileMap,\n  ImportMap,\n  ImportMaps,\n  ModuleGraph,\n} from '../types/module-graph.ts';\n\nconst updateImportMaps = (fromImportMaps: ImportMaps, toImportMaps: ImportMaps) => {\n  for (const id of fromImportMaps.refs) toImportMaps.refs.add(id);\n  for (const [id, v] of fromImportMaps.import) addValues(toImportMaps.import, id, v);\n  for (const [id, v] of fromImportMaps.importAs) addNsValues(toImportMaps.importAs, id, v);\n  for (const [id, v] of fromImportMaps.importNs) addValues(toImportMaps.importNs, id, v);\n  for (const [id, v] of fromImportMaps.reExport) addValues(toImportMaps.reExport, id, v);\n  for (const [id, v] of fromImportMaps.reExportAs) addNsValues(toImportMaps.reExportAs, id, v);\n  for (const [id, v] of fromImportMaps.reExportNs) addValues(toImportMaps.reExportNs, id, v);\n};\n\nexport const updateImportMap = (file: FileNode, importMap: ImportMap, graph: ModuleGraph) => {\n  for (const [importedByFilePath, fileImportMaps] of importMap) {\n    const importMaps = file.imports.internal.get(importedByFilePath);\n    if (!importMaps) file.imports.internal.set(importedByFilePath, fileImportMaps);\n    else updateImportMaps(fileImportMaps, importMaps);\n\n    const importedByFile = graph.get(importedByFilePath) ?? createFileNode();\n    if (!importedByFile.importedBy) importedByFile.importedBy = createImports();\n    updateImportMaps(fileImportMaps, importedByFile.importedBy);\n\n    graph.set(importedByFilePath, importedByFile);\n  }\n};\n\nexport const createFileNode = (): FileNode => ({\n  imports: {\n    internal: new Map(),\n    external: new Set(),\n    externalRefs: new Set(),\n    unresolved: new Set(),\n    programFiles: new Set(),\n    entryFiles: new Set(),\n    imports: new Set(),\n  },\n  exports: new Map(),\n  duplicates: new Set(),\n  scripts: new Set(),\n  importedBy: undefined,\n  internalImportCache: undefined,\n});\n\nexport const createImports = (): ImportMaps => ({\n  refs: new Set(),\n  import: new Map(),\n  importAs: new Map(),\n  importNs: new Map(),\n  reExport: new Map(),\n  reExportAs: new Map(),\n  reExportNs: new Map(),\n});\n\nexport const addValue = (map: IdToFileMap, id: string, value: string) => {\n  if (map.has(id)) map.get(id)?.add(value);\n  else map.set(id, new Set([value]));\n};\n\nexport const addNsValue = (map: IdToNsToFileMap, id: string, ns: string, value: string) => {\n  if (map.has(id)) {\n    if (map.get(id)?.has(ns)) map.get(id)?.get(ns)?.add(value);\n    else map.get(id)?.set(ns, new Set([value]));\n  } else {\n    map.set(id, new Map([[ns, new Set([value])]]));\n  }\n};\n\nconst addValues = (map: IdToFileMap, id: string, values: Set<string>) => {\n  if (map.has(id)) for (const v of values) map.get(id)?.add(v);\n  else map.set(id, values);\n};\n\nconst addNsValues = (map: IdToNsToFileMap, id: string, value: IdToFileMap) => {\n  // @ts-expect-error come on\n  if (map.has(id)) for (const [ns, v] of value) addValues(map.get(id), ns, v);\n  else map.set(id, value);\n};\n"
  },
  {
    "path": "packages/knip/src/util/modules.ts",
    "content": "import { DT_SCOPE, PROTOCOL_VIRTUAL } from '../constants.ts';\nimport { isAbsolute, isInNodeModules, toPosix } from './path.ts';\n\nexport const getPackageNameFromModuleSpecifier = (specifier: string) => {\n  if (!isStartsLikePackageName(specifier)) return;\n  let start = 0;\n  if (specifier.charCodeAt(0) === 64) {\n    const slash = specifier.indexOf('/', 1);\n    if (slash === -1) return specifier;\n    start = slash + 1;\n  }\n  for (let i = start; i < specifier.length; i++) {\n    const ch = specifier.charCodeAt(i);\n    if (ch === 47 || ch === 64) return specifier.slice(0, i);\n  }\n  return specifier;\n};\n\nconst lastPackageNameMatch = /(?<=node_modules\\/)(@[^/]+\\/[^/]+|[^/]+)/g;\nexport const getPackageNameFromFilePath = (value: string) => {\n  const name = value.startsWith('file://') ? value.slice(7) : value;\n  if (name.includes('node_modules/.bin/')) return extractBinary(name);\n  const match = toPosix(name).match(lastPackageNameMatch);\n  if (match) return match[match.length - 1];\n  return name;\n};\n\nexport const getPackageNameFromSpecifier = (specifier: string) =>\n  isInNodeModules(specifier) ? getPackageNameFromFilePath(specifier) : getPackageNameFromModuleSpecifier(specifier);\n\nconst matchPackageNameStart = /^(@[a-z0-9._]|[a-z0-9])/i;\nexport const isStartsLikePackageName = (specifier: string) => {\n  const ch = specifier.charCodeAt(0);\n  if (ch === 46 || ch === 47 || ch === 35 || ch === 126 || ch === 36) return false; // . / # ~ $\n  return matchPackageNameStart.test(specifier);\n};\n\nexport const stripVersionFromSpecifier = (specifier: string) => specifier.replace(/(\\S+)@.*/, '$1');\n\nconst stripNodeModulesFromPath = (command: string) => command.replace(/(?:\\.{0,2}\\/)*node_modules\\//, '');\n\nexport const extractBinary = (command: string) =>\n  stripVersionFromSpecifier(\n    stripNodeModulesFromPath(command)\n      .replace(/^(\\.bin\\/)/, '')\n      .replace(/\\$\\(npm bin\\)\\/(\\w+)/, '$1') // Removed in npm v9\n  );\n\nexport const isValidBinary = (str: string) => !/[*:!()]/.test(str);\n\nexport const isDefinitelyTyped = (packageName: string) => packageName.startsWith(`${DT_SCOPE}/`);\n\nexport const getDefinitelyTypedFor = (packageName: string) => {\n  if (isDefinitelyTyped(packageName)) return packageName;\n  if (packageName.startsWith('@')) return [DT_SCOPE, packageName.slice(1).replace('/', '__')].join('/');\n  return [DT_SCOPE, packageName].join('/');\n};\n\nexport const getPackageFromDefinitelyTyped = (typedDependency: string) => {\n  if (typedDependency.includes('__')) {\n    const [scope, packageName] = typedDependency.split('__');\n    return `@${scope}/${packageName}`;\n  }\n  return typedDependency;\n};\n\nconst CHAR_EXCLAMATION = 33; // '!'\nconst CHAR_DASH = 45; // '-'\nconst CHAR_SLASH = 47; // '/'\nconst CHAR_COLON = 58; // ':'\nconst CHAR_HASH = 35; // '#'\nconst CHAR_QUESTION = 63; // '?'\n\n// Strip `?search` and other proprietary directives from the specifier (e.g. https://webpack.js.org/concepts/loaders/)\nexport const sanitizeSpecifier = (specifier: string) => {\n  if (\n    specifier.startsWith('node:') ||\n    isAbsolute(specifier) ||\n    specifier.charCodeAt(0) === CHAR_COLON ||\n    specifier.startsWith(PROTOCOL_VIRTUAL)\n  ) {\n    return specifier;\n  }\n  const len = specifier.length;\n  let start = 0;\n  let end = len;\n  let colon = -1;\n  let hasSlash = false;\n  for (let i = 0; i < len; i++) {\n    const ch = specifier.charCodeAt(i);\n    if (i === start && (ch === CHAR_EXCLAMATION || ch === CHAR_DASH)) {\n      start++;\n      continue;\n    }\n    if (ch === CHAR_SLASH && colon === -1) {\n      hasSlash = true;\n    }\n    if (colon === -1 && ch === CHAR_COLON && !hasSlash) {\n      colon = i;\n    }\n    if (ch === CHAR_EXCLAMATION || ch === CHAR_QUESTION || (ch === CHAR_HASH && i > start)) {\n      end = i;\n      break;\n    }\n  }\n  return colon !== -1 && colon < end ? specifier.slice(start, colon) : specifier.slice(start, end);\n};\n"
  },
  {
    "path": "packages/knip/src/util/object.ts",
    "content": "/** @internal */\nexport const getValuesByKeyDeep = (obj: any, key: string): unknown[] => {\n  const objects = [];\n  if (obj && typeof obj === 'object') {\n    for (const i in obj) {\n      if (obj[i] && typeof obj[i] === 'object') {\n        const values = getValuesByKeyDeep(obj[i], key);\n        objects.push(...values);\n      } else if (i === key) {\n        objects.push(obj[i]);\n      }\n    }\n  }\n  return objects;\n};\n\nexport const findByKeyDeep = <T>(obj: any, key: string): T[] => {\n  const objects = [];\n  if (obj && typeof obj === 'object') {\n    if (key in obj) {\n      objects.push(obj);\n    }\n    for (const value of Object.values(obj)) {\n      if (Array.isArray(value)) {\n        for (const item of value) {\n          objects.push(...findByKeyDeep(item, key));\n        }\n      } else if (typeof value === 'object') {\n        objects.push(...findByKeyDeep(value, key));\n      }\n    }\n  }\n  return objects;\n};\n\nexport const getKeysByValue = <T>(obj: T, value: unknown): (keyof T)[] => {\n  const keys = [];\n  for (const key in obj) {\n    if (obj[key] === value) keys.push(key);\n  }\n  return keys;\n};\n\nexport const get = <T>(obj: T, path: string) => path.split('.').reduce((o: any, p) => o?.[p], obj);\n"
  },
  {
    "path": "packages/knip/src/util/package-json.ts",
    "content": "// Borrowed from https://github.com/npm/package-json + https://github.com/npm/json-parse-even-better-errors\nimport { readFile, writeFile } from 'node:fs/promises';\nimport type { PackageJson } from '../types/package-json.ts';\n\nconst INDENT = Symbol.for('indent');\nconst NEWLINE = Symbol.for('newline');\nconst DEFAULT_NEWLINE = '\\n';\nconst DEFAULT_INDENT = '  ';\nconst BOM = /^\\uFEFF/;\nconst FORMAT = /^\\s*[{[]((?:\\r?\\n)+)([\\s\\t]*)/;\nconst EMPTY = /^(?:\\{\\}|\\[\\])((?:\\r?\\n)+)?$/;\n\ninterface ExtendedPackageJson extends PackageJson {\n  [INDENT]?: string;\n  [NEWLINE]?: string;\n}\n\nconst stripBOM = (txt: string) => String(txt).replace(BOM, '');\n\nconst parseJson = (raw: string): ExtendedPackageJson => {\n  const txt = stripBOM(raw);\n  const result = JSON.parse(txt);\n  if (result && typeof result === 'object') {\n    const match = txt.match(EMPTY) || txt.match(FORMAT) || [null, '', ''];\n    result[NEWLINE] = match[1] ?? DEFAULT_NEWLINE;\n    result[INDENT] = match[2] ?? DEFAULT_INDENT;\n  }\n  return result;\n};\n\nconst getEntriesFromExports = (obj: any): string[] => {\n  if (typeof obj === 'string') return [obj];\n  let values: string[] = [];\n  for (const prop in obj) {\n    if (typeof obj[prop] === 'string') {\n      values.push(obj[prop]);\n    } else if (obj[prop] === null) {\n      values.push(`!${prop}`);\n    } else if (typeof obj[prop] === 'object') {\n      values = values.concat(getEntriesFromExports(obj[prop]));\n    }\n  }\n  return values;\n};\n\nexport const load = async (filePath: string) => {\n  const file = await readFile(filePath, 'utf8');\n  return parseJson(file);\n};\n\nexport const save = async (filePath: string, content: ExtendedPackageJson) => {\n  const { [INDENT]: indent, [NEWLINE]: newline } = content;\n  const space = indent === undefined ? DEFAULT_INDENT : indent;\n  const EOL = newline === undefined ? DEFAULT_NEWLINE : newline;\n  const fileContent = `${JSON.stringify(content, null, space)}\\n`.replace(/\\n/g, EOL);\n  await writeFile(filePath, fileContent);\n};\n\nexport const getEntrySpecifiersFromManifest = (manifest: PackageJson) => {\n  const { main, module, browser, bin, exports, types, typings } = manifest;\n\n  const entryPaths = new Set<string>();\n\n  if (typeof main === 'string' && main) entryPaths.add(main);\n  if (typeof module === 'string' && module) entryPaths.add(module);\n  if (typeof browser === 'string' && browser) entryPaths.add(browser);\n  if (typeof bin === 'string' && bin) entryPaths.add(bin);\n  if (bin && typeof bin === 'object') for (const id of Object.values(bin)) if (id) entryPaths.add(id);\n  if (typeof types === 'string' && types) entryPaths.add(types);\n  if (typeof typings === 'string' && typings) entryPaths.add(typings);\n\n  if (exports) {\n    for (const item of getEntriesFromExports(exports)) {\n      if (item === './*' || item.trim() === '') continue;\n      const expanded = item\n        .replace(/\\/\\*$/, '/**') // /* → /**\n        .replace(/\\/\\*\\./, '/**/*.') // /*. → /**/*.\n        .replace(/\\/\\*\\//, '/**/'); // /*/ → /**/\n      entryPaths.add(expanded);\n    }\n  }\n\n  if (manifest.imports) {\n    for (const [key, value] of Object.entries(manifest.imports)) {\n      if (!key.startsWith('#')) continue;\n      for (const item of getEntriesFromExports(value)) {\n        if (item.startsWith('.') && !item.includes('*')) entryPaths.add(item);\n      }\n    }\n  }\n\n  return entryPaths;\n};\n\nexport const getManifestImportDependencies = (manifest: PackageJson) => {\n  const dependencies = new Set<string>();\n  if (!manifest.imports) return dependencies;\n  for (const [entry, exportValue] of Object.entries(manifest.imports)) {\n    if (!entry.startsWith('#')) continue;\n    for (const item of getEntriesFromExports(exportValue)) {\n      if (!item.startsWith('.') && !item.startsWith('!')) dependencies.add(item);\n    }\n  }\n  return dependencies;\n};\n"
  },
  {
    "path": "packages/knip/src/util/package-name.ts",
    "content": "import type { PackageJson } from '../types/package-json.ts';\nimport { basename, dirname } from './path.ts';\n\nconst getPkgName = (parent: string, base: string) => (parent.charAt(0) === '@' ? `${parent}/${base}` : base);\n\nconst getName = (dir: string) => (dir ? getPkgName(basename(dirname(dir)), basename(dir)) : undefined);\n\nexport function getPackageName(pkg: PackageJson, pathname: string) {\n  const { name } = pkg;\n  return name || getName(pathname);\n}\n"
  },
  {
    "path": "packages/knip/src/util/parse-and-convert-gitignores.ts",
    "content": "/** @internal */\nexport const convertGitignoreToPicomatchIgnorePatterns = (pattern: string) => {\n  const negated = pattern[0] === '!';\n\n  if (negated) pattern = pattern.slice(1);\n\n  let extPattern: string;\n\n  if (pattern.endsWith('/')) pattern = pattern.slice(0, -1);\n  if (pattern.startsWith('*/**/')) pattern = pattern.slice(5);\n\n  if (pattern === '*' || pattern === '**') return { negated, patterns: [pattern, pattern] };\n\n  if (pattern.startsWith('/')) pattern = pattern.slice(1);\n  else if (!pattern.startsWith('**/')) pattern = `**/${pattern}`;\n\n  if (pattern.endsWith('/*')) extPattern = pattern;\n  else extPattern = `${pattern}/**`;\n\n  return { negated, patterns: [pattern, extPattern] };\n};\n\nexport const parseAndConvertGitignorePatterns = (patterns: string, ancestor?: string) => {\n  const matchFrom = ancestor ? new RegExp(`^(!?/?)(${ancestor})`) : undefined;\n  return patterns\n    .split(/\\r?\\n/)\n    .filter(line => line.trim() && !line.startsWith('#'))\n    .flatMap(line => {\n      const pattern = line.replace(/^\\\\(?=#)/, '').trim();\n      if (ancestor && matchFrom) {\n        if (pattern.match(matchFrom)) return [pattern.replace(matchFrom, '$1')];\n        if (pattern.startsWith('/**/')) return [pattern.slice(1)];\n        if (pattern.startsWith('!/**/')) return [`!${pattern.slice(2)}`];\n        if (pattern.startsWith('/') || pattern.startsWith('!/')) return [];\n      }\n      return [pattern];\n    })\n    .map(pattern => convertGitignoreToPicomatchIgnorePatterns(pattern));\n};\n"
  },
  {
    "path": "packages/knip/src/util/path.ts",
    "content": "// oxlint-disable-next-line no-restricted-imports\nimport path from 'node:path';\n\nconst isWin = process.platform === 'win32';\n\nexport const isAbsolute = path.isAbsolute;\n\nexport const dirname = path.posix.dirname;\n\nexport const extname = path.posix.extname;\n\nexport const basename = path.posix.basename;\n\nexport const join = path.posix.join;\n\nexport const toPosix = isWin ? (value: string) => value.split(path.sep).join(path.posix.sep) : (value: string) => value;\n\nexport const resolve = path.posix.resolve;\n\nexport const relative = (from: string, to: string) => {\n  if (to.startsWith(from)) {\n    const next = to[from.length];\n    if (next === '/') return to.substring(from.length + 1);\n    if (next === undefined) return '.';\n  }\n  const result = path.relative(from, to);\n  return isWin ? toPosix(result) : result || '.';\n};\n\nexport const isInNodeModules = (filePath: string) => filePath.includes('node_modules');\n\nexport const toAbsolute = (id: string, base: string) => (isAbsolute(id) ? id : join(base, id));\n\nexport const toRelative = (id: string, base: string) => (isAbsolute(id) ? relative(base, id) : id);\n\nexport const isInternal = (id: string) => (id.startsWith('.') || isAbsolute(id)) && !isInNodeModules(id);\n\nexport const normalize = path.posix.normalize;\n"
  },
  {
    "path": "packages/knip/src/util/plugin-config.ts",
    "content": "const toConfigMap =\n  (\n    defaultExtensions: string[],\n    builderConfig: {\n      rcPrefix?: string;\n      rcSuffix?: string;\n      configDir?: boolean;\n      configFiles?: boolean;\n      configFilesAllExtensions?: boolean;\n      additionalExtensions?: string[];\n    }\n  ) =>\n  (moduleName: string, options?: typeof builderConfig) => {\n    const config = {\n      rcPrefix: '.',\n      rcSuffix: 'rc',\n      // Generate .config/<file>\n      configDir: true,\n      // Generate <file>.config.<ext>\n      configFiles: true,\n      // Allow for .json, .yaml, .yml, .toml etc\n      configFilesAllExtensions: false,\n      additionalExtensions: [],\n      ...builderConfig,\n      ...options,\n    };\n    const { rcPrefix, rcSuffix } = config;\n    const jsTypeExtensions = ['js', 'ts', 'cjs', 'mjs', 'cts', 'mts'];\n    const extensions = [...defaultExtensions, ...config.additionalExtensions];\n\n    const baseFiles = [\n      `${rcPrefix}${moduleName}${rcSuffix}`,\n      ...(config.configDir ? [`.config/${moduleName}${rcSuffix}`] : []),\n    ];\n\n    const rcFiles = `${rcPrefix}${moduleName}${rcSuffix}.{${extensions.join(',')}}`;\n    const configExtensions = extensions.filter(\n      ext => config.configFilesAllExtensions || jsTypeExtensions.includes(ext)\n    );\n    const configFiles = config.configFiles ? [`${moduleName}.config.{${configExtensions.join(',')}}`] : [];\n    const configDirFiles = config.configDir ? [`.config/${moduleName}${rcSuffix}.{${extensions.join(',')}}`] : [];\n\n    return [...baseFiles, rcFiles, ...configFiles, ...configDirFiles];\n  };\n\nexport const toCosmiconfig = toConfigMap(['json', 'yaml', 'yml', 'js', 'ts', 'cjs', 'mjs'], { configDir: true });\nexport const toLilconfig = toConfigMap(['json', 'ts', 'js', 'cjs', 'mjs'], { configDir: true });\nexport const toUnconfig = toConfigMap(['json', 'ts', 'mts', 'cts', 'js', 'mjs', 'cjs'], {\n  configDir: false,\n  rcPrefix: '',\n  rcSuffix: '',\n  configFiles: false,\n});\nexport const toC12config = toConfigMap(\n  ['json', 'jsonc', 'json5', 'yaml', 'yml', 'js', 'ts', 'mjs', 'cjs', 'mts', 'cts', 'toml'],\n  { configDir: true }\n);\n"
  },
  {
    "path": "packages/knip/src/util/plugin.ts",
    "content": "export { _load as load } from './loader.ts';\n\nimport type { Plugin, PluginOptions, RawPluginConfiguration } from '../types/config.ts';\nimport { arrayify } from './array.ts';\nimport { _load as load } from './loader.ts';\nimport { get } from './object.ts';\nimport { basename } from './path.ts';\n\nexport const hasDependency = (dependencies: Set<string>, values: (string | RegExp)[]) =>\n  values.some(value => {\n    if (typeof value === 'string') {\n      return dependencies.has(value);\n    }\n    if (value instanceof RegExp) {\n      for (const dependency of dependencies) {\n        if (value.test(dependency)) return true;\n      }\n    }\n    return false;\n  });\n\nexport const normalizePluginConfig = (pluginConfig: RawPluginConfiguration) => {\n  if (typeof pluginConfig === 'boolean') return pluginConfig;\n\n  const isObject = typeof pluginConfig !== 'string' && !Array.isArray(pluginConfig);\n\n  const config = isObject\n    ? 'config' in pluginConfig\n      ? arrayify(pluginConfig.config)\n      : null\n    : pluginConfig\n      ? arrayify(pluginConfig)\n      : null;\n\n  const entry = isObject && 'entry' in pluginConfig ? arrayify(pluginConfig.entry) : null;\n\n  const project =\n    isObject && 'project' in pluginConfig\n      ? arrayify(pluginConfig.project)\n      : (entry ?? []).filter(pattern => !pattern.startsWith('!'));\n\n  return { config, entry, project };\n};\n\nexport const loadConfigForPlugin = async (\n  configFilePath: string,\n  plugin: Plugin,\n  options: PluginOptions,\n  pluginName: string\n) => {\n  const { packageJsonPath } = plugin;\n  const { manifest } = options;\n\n  return basename(configFilePath) === 'package.json'\n    ? typeof packageJsonPath === 'function'\n      ? packageJsonPath(manifest)\n      : get(manifest, packageJsonPath ?? pluginName)\n    : await load(configFilePath);\n};\n"
  },
  {
    "path": "packages/knip/src/util/regex.ts",
    "content": "const isRegexLikeMatch = /[*+\\\\(|{^$]/;\nconst isRegexLike = (value: string) => isRegexLikeMatch.test(value);\n\nexport const toRegexOrString = (value: string | RegExp) =>\n  typeof value === 'string' && isRegexLike(value) ? new RegExp(value) : value;\n\nexport const findMatch = (haystack: (string | RegExp)[], needle: string) =>\n  haystack.find(n => (typeof n === 'string' ? n === needle : n.test(needle)));\n"
  },
  {
    "path": "packages/knip/src/util/remove-export.ts",
    "content": "import { FIX_FLAGS } from '../constants.ts';\n\ninterface FixerOptions {\n  text: string;\n  start: number;\n  end: number;\n  flags: number;\n}\n\nconst getOpeningBracketIndex = (text: string) => {\n  let bracketOpenIndex = -1;\n  let j = text.length - 1;\n  while (j >= 0) {\n    const char = text[j];\n    if (char === '{') {\n      bracketOpenIndex = j;\n      break;\n    }\n    if (!/\\s/.test(char) && char !== ',') {\n      if (text.substring(j - 3, j + 1) === 'type') {\n        j = j - 4;\n        continue;\n      }\n      break;\n    }\n    j--;\n  }\n  return bracketOpenIndex;\n};\n\nexport const removeExport = ({ text, start, end, flags }: FixerOptions) => {\n  const beforeStart = text.substring(0, start);\n  const afterEnd = text.substring(end);\n\n  if (flags % FIX_FLAGS.NONE) return beforeStart + afterEnd;\n\n  const subject = text.substring(start, end).trim();\n  if (subject === 'export' || subject === 'export default') return beforeStart + afterEnd;\n\n  let closingBracketOffset = -1;\n  let removeAfterLength = -1;\n\n  if (flags & FIX_FLAGS.OBJECT_BINDING) {\n    let i = 0;\n    while (i <= afterEnd.length) {\n      const char = afterEnd[i];\n      if (char === ',') {\n        removeAfterLength = i + 1;\n      } else if (flags & FIX_FLAGS.WITH_NEWLINE && (char === '\\n' || char === '\\r' || char === '\\r\\n')) {\n        removeAfterLength = i + 1;\n      } else if (char === '}') {\n        closingBracketOffset = i + 1;\n        if (flags & FIX_FLAGS.WITH_NEWLINE) removeAfterLength = i;\n        break;\n      } else if (!/\\s/.test(char)) {\n        if (flags & FIX_FLAGS.WITH_NEWLINE) removeAfterLength = i;\n        break;\n      }\n      i++;\n    }\n  }\n\n  if (flags & FIX_FLAGS.EMPTY_DECLARATION && closingBracketOffset !== -1) {\n    const openingBracketIndex = getOpeningBracketIndex(beforeStart);\n    if (closingBracketOffset !== -1 && openingBracketIndex !== -1) {\n      const beforeBracket = beforeStart.substring(0, openingBracketIndex).trim();\n      const exportLength = beforeBracket.endsWith('export') ? 6 : beforeBracket.endsWith('export type') ? 11 : 0;\n      const exportKeywordOffset = beforeBracket.length - exportLength;\n      if (exportLength) {\n        const fromBracket = afterEnd.substring(closingBracketOffset).trim();\n        if (fromBracket.startsWith('from')) {\n          const specifierQuoteMatch = afterEnd.match(/['\"][^'\"]+['\"]/);\n          if (specifierQuoteMatch?.index) {\n            const fromSpecifierLength = specifierQuoteMatch.index + specifierQuoteMatch[0].length;\n            return beforeBracket.substring(0, exportKeywordOffset) + afterEnd.substring(fromSpecifierLength);\n          }\n        }\n        return beforeBracket.substring(0, exportKeywordOffset) + afterEnd.substring(closingBracketOffset);\n      }\n    }\n  }\n\n  return beforeStart + (removeAfterLength === -1 ? afterEnd : afterEnd.substring(removeAfterLength));\n};\n"
  },
  {
    "path": "packages/knip/src/util/reporter.ts",
    "content": "import internalReporters from '../reporters/index.ts';\nimport type { ReporterOptions } from '../types/issues.ts';\nimport { _load } from './loader.ts';\nimport { isAbsolute, isInternal, resolve } from './path.ts';\n\nexport const runPreprocessors = async (processors: string[], data: ReporterOptions): Promise<ReporterOptions> => {\n  const preprocessors = await Promise.all(\n    processors.map(proc => _load(isInternal(proc) && !isAbsolute(proc) ? resolve(proc) : proc))\n  );\n  return preprocessors.length === 0\n    ? Promise.resolve(data)\n    : runPreprocessors(preprocessors.slice(1), preprocessors[0](data));\n};\n\nexport const runReporters = async (reporter: string[], options: ReporterOptions) => {\n  const reporters = await Promise.all(\n    reporter.map(async reporter => {\n      return reporter in internalReporters\n        ? internalReporters[reporter as keyof typeof internalReporters]\n        : await _load(isInternal(reporter) && !isAbsolute(reporter) ? resolve(reporter) : reporter);\n    })\n  );\n\n  for (const reporter of reporters) await reporter(options);\n};\n"
  },
  {
    "path": "packages/knip/src/util/require.ts",
    "content": "import { createRequire } from 'node:module';\nimport { timerify } from './Performance.ts';\n\nconst require = createRequire(process.cwd());\nexport const _require = timerify(require);\n"
  },
  {
    "path": "packages/knip/src/util/resolve.ts",
    "content": "import { ResolverFactory } from 'oxc-resolver';\nimport { DEFAULT_EXTENSIONS, DTS_EXTENSIONS } from '../constants.ts';\nimport { timerify } from './Performance.ts';\nimport { toPosix } from './path.ts';\n\nconst extensionAlias = {\n  '.js': ['.js', '.ts', '.tsx', '.d.ts'],\n  '.jsx': ['.jsx', '.tsx'],\n  '.mjs': ['.mjs', '.mts', '.d.mts'],\n  '.cjs': ['.cjs', '.cts', '.d.cts'],\n};\n\nconst resolverInstances: ResolverFactory[] = [];\n\nconst createSyncModuleResolver = (extensions: string[], alias?: Record<string, string[]>) => {\n  const aliasOpt = alias && { alias };\n  const baseOptions = {\n    extensions,\n    extensionAlias,\n    conditionNames: ['require', 'import', 'node', 'default'],\n    nodePath: false,\n    ...aliasOpt,\n  };\n  const resolver = new ResolverFactory({ tsconfig: 'auto', ...baseOptions });\n  const fallbackResolver = new ResolverFactory(baseOptions);\n\n  resolverInstances.push(resolver, fallbackResolver);\n\n  return function resolveSync(specifier: string, basePath: string) {\n    const resolved = resolver.resolveFileSync(basePath, specifier);\n    if (resolved.path) return toPosix(resolved.path);\n    if (resolved.error) {\n      const fallback = fallbackResolver.resolveFileSync(basePath, specifier);\n      if (fallback.path) return toPosix(fallback.path);\n    }\n  };\n};\n\nconst resolveModuleSync = createSyncModuleResolver([...DEFAULT_EXTENSIONS, ...DTS_EXTENSIONS, '.json', '.jsonc']);\n\n/**\n * Default module resolver (no custom extensions or path aliases).\n */\nexport const _resolveModuleSync = timerify(resolveModuleSync, 'resolveModuleSync');\n\nexport const _createSyncModuleResolver = (extensions: string[], alias?: Record<string, string[]>) =>\n  timerify(createSyncModuleResolver(extensions, alias), 'resolveModuleSync');\n\n/** Convert TS compilerOptions.paths to oxc-resolver alias format */\nexport function convertPathsToAlias(paths: Record<string, string[]> | undefined): Record<string, string[]> | undefined {\n  if (!paths) return undefined;\n  const alias: Record<string, string[]> = {};\n  for (const key in paths) {\n    const stripWildcard = key.endsWith('/*');\n    const aliasKey = stripWildcard ? key.slice(0, -2) : key;\n    alias[aliasKey] = stripWildcard ? paths[key].map(v => (v.endsWith('/*') ? v.slice(0, -2) : v)) : paths[key];\n  }\n  return alias;\n}\n\nconst createSyncResolver = (extensions: string[]) => {\n  const resolver = new ResolverFactory({\n    extensions,\n    conditionNames: ['require', 'import', 'node', 'default'],\n    nodePath: false,\n  });\n\n  resolverInstances.push(resolver);\n\n  return function resolveSync(specifier: string, baseDir: string) {\n    const resolved = resolver.sync(baseDir, specifier);\n    if (resolved.path) return toPosix(resolved.path);\n  };\n};\n\nexport function clearResolverCache() {\n  for (const resolver of resolverInstances) resolver.clearCache();\n}\n\nconst resolveSync = createSyncResolver([...DEFAULT_EXTENSIONS, '.json', '.jsonc']);\n\n/**\n * Resolver for everything outside the realm of TS module resolution.\n * That's everything coming directly from package.json, scripts and plugins\n * that's an `Input` except those of type `entry` or `project`.\n */\nexport const _resolveSync = timerify(resolveSync);\n"
  },
  {
    "path": "packages/knip/src/util/string.ts",
    "content": "import { stripVTControlCharacters } from 'node:util';\n\n// oxlint-disable-next-line no-control-regex\nconst CONTROL_CHARACTERS = /\\u001b\\[[0-9;]+m/g;\nexport const ELLIPSIS = '…';\n\nconst getTruncatedParts = (input: string, limit: number, fromStart: boolean) => {\n  const parts = [];\n  let width = 0;\n  let index = 0;\n  let truncated = false;\n\n  while (index < input.length) {\n    CONTROL_CHARACTERS.lastIndex = index;\n    const match = CONTROL_CHARACTERS.exec(input);\n\n    if (match && match.index === index) {\n      index = CONTROL_CHARACTERS.lastIndex;\n      parts.push(match[0]);\n      continue;\n    }\n\n    if (fromStart && width >= limit) truncated = true;\n    else parts.push(input[index]);\n\n    width++;\n    index++;\n  }\n\n  if (fromStart) return { parts, truncated };\n\n  let indexRight = 0;\n  const toKeep = (value: string) => value.length > 1 || indexRight++ < limit;\n  const _parts = parts.reverse().filter(toKeep);\n  return { parts: _parts.reverse(), truncated: indexRight > limit };\n};\n\nexport const truncate = (text: string, width: number) => {\n  if (stripVTControlCharacters(text).length <= width) return text;\n  const { parts, truncated } = getTruncatedParts(text, width - ELLIPSIS.length, true);\n  if (!truncated) return text;\n  if (parts.at(-1)?.length === 1) return parts.join('') + ELLIPSIS;\n  return [...parts.slice(0, -1), ELLIPSIS, parts.at(-1)].join('');\n};\n\nexport const truncateStart = (text: string, width: number) => {\n  if (stripVTControlCharacters(text).length <= width) return text;\n  const { parts, truncated } = getTruncatedParts(text, width - ELLIPSIS.length, false);\n  if (!truncated) return text;\n  if (parts[0].length === 1) return ELLIPSIS + parts.join('');\n  return [parts[0], ELLIPSIS, ...parts.slice(1)].join('');\n};\n\nexport const pad = (text: string, width: number, fillString?: string, align?: 'left' | 'center' | 'right') => {\n  const escapedWidth = width + (text.length - stripVTControlCharacters(text).length);\n  return align === 'right'\n    ? text.padStart(escapedWidth, fillString)\n    : align === 'center'\n      ? text.padStart((text.length + escapedWidth) / 2, fillString).padEnd(escapedWidth, fillString)\n      : text.padEnd(escapedWidth, fillString);\n};\n\nexport const prettyMilliseconds = (ms: number): string => {\n  const seconds = ms / 1000;\n  const minutes = Math.floor(seconds / 60);\n  const hours = Math.floor(minutes / 60);\n  if (hours > 0) return `${hours}h ${minutes % 60}m ${Math.floor(seconds % 60)}s`;\n  if (minutes > 0) return `${minutes}m ${Math.floor(seconds % 60)}s`;\n  if (seconds > 10) return `${Math.round(seconds)}s`;\n  if (seconds > 1) return `${seconds.toFixed(1)}s`;\n  return `${Math.round(ms)}ms`;\n};\n"
  },
  {
    "path": "packages/knip/src/util/table.ts",
    "content": "import { stripVTControlCharacters } from 'node:util';\nimport { pad, truncate, truncateStart } from './string.ts';\n\ntype Value = string | number | undefined | false | null;\ntype Align = 'left' | 'center' | 'right';\ntype Row = Record<string, Cell>;\ntype Cell = { value: Value; formatted?: string; fill?: string; align?: Align };\n\nconst DEFAULT_MAX_WIDTH = process.stdout.columns || 120;\nconst MIN_TRUNCATED_WIDTH = 4;\nconst COLUMN_SEPARATOR = '  ';\n\nexport class Table {\n  private columns: string[] = [];\n  private rows: Row[] = [];\n  private header: boolean;\n  private maxWidth: number;\n  private truncateStart: string[] = [];\n  private noTruncate: string[] = [];\n\n  constructor(options?: { maxWidth?: number; header?: boolean; truncateStart?: string[]; noTruncate?: string[] }) {\n    this.header = options?.header ?? false;\n    this.maxWidth = options?.maxWidth || DEFAULT_MAX_WIDTH;\n    this.truncateStart = options?.truncateStart || [];\n    this.noTruncate = options?.noTruncate || [];\n  }\n\n  row() {\n    this.rows.push({});\n    return this;\n  }\n\n  cell(column: string, value: Value, formatter?: (value: Value) => string) {\n    if (!this.columns.includes(column)) this.columns.push(column);\n    const row = this.rows[this.rows.length - 1];\n    const align = typeof value === 'number' ? 'right' : 'left';\n    const formatted = formatter ? formatter(value) : undefined;\n    row[column] = { value, formatted, align };\n    return this;\n  }\n\n  sort(column: string) {\n    this.rows.sort((a, b) => {\n      const [columnName, order] = column.split('|');\n      const vA = a[columnName].value;\n      const vB = b[columnName].value;\n      if (typeof vA === 'string' && typeof vB === 'string') return (order === 'desc' ? -1 : 1) * vA.localeCompare(vB);\n      if (typeof vA === 'number' && typeof vB === 'number') return order === 'desc' ? vB - vA : vA - vB;\n      return !vA ? 1 : !vB ? -1 : 0;\n    });\n    return this;\n  }\n\n  toCells() {\n    const columns = this.columns.filter(col =>\n      this.rows.some(row => typeof row[col].value === 'string' || typeof row[col].value === 'number')\n    );\n\n    if (this.header) {\n      const headerRow: Row = {};\n      const linesRow: Row = {};\n      for (const col of columns) {\n        headerRow[col] = { value: col, align: this.rows[0][col].align === 'right' ? 'center' : 'left' };\n        linesRow[col] = { value: '', fill: '-' };\n      }\n      this.rows.unshift(linesRow);\n      this.rows.unshift(headerRow);\n    }\n\n    const columnWidths = columns.reduce(\n      (acc, col) => {\n        acc[col] = Math.max(\n          ...this.rows.map(row => stripVTControlCharacters(row[col]?.formatted ?? String(row[col].value || '')).length)\n        );\n        return acc;\n      },\n      {} as Record<string, number>\n    );\n\n    const separatorWidth = (columns.length - 1) * COLUMN_SEPARATOR.length;\n    const totalWidth = Object.values(columnWidths).reduce((sum, width) => sum + width, 0) + separatorWidth;\n\n    if (totalWidth > this.maxWidth) {\n      const reservedWidth = columns\n        .filter(col => this.noTruncate.includes(col))\n        .reduce((sum, col) => sum + columnWidths[col], 0);\n\n      const truncatableColumns = columns.filter(col => !this.noTruncate.includes(col));\n      const minWidth = truncatableColumns.length * 4;\n      const availableWidth = this.maxWidth - separatorWidth - reservedWidth - minWidth;\n      const truncatableWidth = truncatableColumns.reduce((sum, col) => sum + columnWidths[col], 0) - minWidth;\n\n      const reduction = availableWidth / truncatableWidth;\n      let roundingDiff = availableWidth;\n\n      for (const col of truncatableColumns) {\n        const reducedWidth = MIN_TRUNCATED_WIDTH + Math.floor((columnWidths[col] - MIN_TRUNCATED_WIDTH) * reduction);\n        columnWidths[col] = reducedWidth;\n        roundingDiff -= reducedWidth - MIN_TRUNCATED_WIDTH;\n      }\n\n      if (roundingDiff > 0) {\n        columnWidths[truncatableColumns.length > 0 ? truncatableColumns[0] : columns[0]] += roundingDiff;\n      }\n    }\n\n    return this.rows.map(row =>\n      columns.map((col, index) => {\n        const cell = row[col];\n        const width = columnWidths[col];\n        const fill = cell.fill || ' ';\n        const padded = pad(String(cell.formatted || cell.value || ''), width, fill, cell.align);\n        const truncated = this.truncateStart.includes(col) ? truncateStart(padded, width) : truncate(padded, width);\n        return index === 0 ? truncated : COLUMN_SEPARATOR + truncated;\n      })\n    );\n  }\n\n  toRows() {\n    return this.toCells().map(row => row.join(''));\n  }\n\n  toString() {\n    return this.toRows().join('\\n');\n  }\n}\n"
  },
  {
    "path": "packages/knip/src/util/tag.ts",
    "content": "import { ALIAS_TAG, BETA_TAG, INTERNAL_TAG, PUBLIC_TAG } from '../constants.ts';\nimport type { Tags } from '../types/options.ts';\n\nexport const splitTags = (rawTags: string[]) => {\n  const tags = rawTags.flatMap(tag => tag.split(','));\n  return tags.reduce<Tags>(\n    ([incl, excl], tag) => {\n      const match = tag.match(/[a-zA-Z]+/);\n      if (match) (tag.startsWith('-') ? excl : incl).push(match[0]);\n      return [incl, excl];\n    },\n    [[], []]\n  );\n};\n\nconst hasTag = (tags: string[], jsDocTags: Set<string>) => tags.some(tag => jsDocTags.has(`@${tag}`));\n\nexport const shouldIgnore = (jsDocTags: Set<string>, tags: Tags) => {\n  const [includeJSDocTags, excludeJSDocTags] = tags;\n  if (includeJSDocTags.length > 0 && !hasTag(includeJSDocTags, jsDocTags)) return true;\n  if (excludeJSDocTags.length > 0 && hasTag(excludeJSDocTags, jsDocTags)) return true;\n  return false;\n};\n\nexport const getShouldIgnoreHandler = (isProduction: boolean) => (jsDocTags: Set<string>) =>\n  jsDocTags.has(PUBLIC_TAG) ||\n  jsDocTags.has(BETA_TAG) ||\n  jsDocTags.has(ALIAS_TAG) ||\n  (isProduction && jsDocTags.has(INTERNAL_TAG));\n\nexport const getShouldIgnoreTagHandler = (tags: Tags) => (jsDocTags: Set<string>) => shouldIgnore(jsDocTags, tags);\n"
  },
  {
    "path": "packages/knip/src/util/to-source-path.ts",
    "content": "import type { CompilerOptions } from '../types/project.ts';\nimport type { ConfigurationChief, Workspace } from '../ConfigurationChief.ts';\nimport { DEFAULT_EXTENSIONS } from '../constants.ts';\nimport { debugLog, debugLogArray } from './debug.ts';\nimport { findFileWithExtensions, isDirectory } from './fs.ts';\nimport { _glob, prependDirToPattern } from './glob.ts';\nimport { isAbsolute, isInternal, join, toRelative } from './path.ts';\n\nconst defaultExtensions = `.{${[...DEFAULT_EXTENSIONS].map(ext => ext.slice(1)).join(',')}}`;\nconst hasTSExt = /(?<!\\.d)\\.(m|c)?tsx?$/;\nconst matchExt = /(\\.d)?\\.(m|c)?(j|t)s$/;\n\nconst sourceExtensions = [...DEFAULT_EXTENSIONS];\n\nexport const augmentWorkspace = (workspace: Workspace, dir: string, compilerOptions: CompilerOptions) => {\n  const srcDir = join(dir, 'src');\n  const outDirHasSrc = compilerOptions.outDir && isDirectory(compilerOptions.outDir, 'src');\n  workspace.srcDir = compilerOptions.rootDir ?? (outDirHasSrc ? dir : isDirectory(srcDir) ? srcDir : dir);\n  workspace.outDir = compilerOptions.outDir || workspace.srcDir;\n};\n\nexport const getModuleSourcePathHandler = (chief: ConfigurationChief) => {\n  const toSourceMapCache = new Map<string, string>();\n\n  return (filePath: string) => {\n    if (!isInternal(filePath) || hasTSExt.test(filePath)) return;\n    if (toSourceMapCache.has(filePath)) return toSourceMapCache.get(filePath);\n    const workspace = chief.findWorkspaceByFilePath(filePath);\n    if (workspace?.srcDir && workspace.outDir) {\n      if (filePath.startsWith(workspace.outDir) || workspace.srcDir === workspace.outDir) {\n        const basePath = filePath.replace(workspace.outDir, workspace.srcDir).replace(matchExt, '');\n        const srcFilePath = findFileWithExtensions(basePath, sourceExtensions);\n        if (srcFilePath) toSourceMapCache.set(filePath, srcFilePath);\n        if (srcFilePath && srcFilePath !== filePath) {\n          debugLog('*', `Source mapping ${toRelative(filePath, chief.cwd)} → ${toRelative(srcFilePath, chief.cwd)}`);\n          return srcFilePath;\n        }\n      }\n    }\n  };\n};\n\nexport const getToSourcePathsHandler = (chief: ConfigurationChief) => {\n  return async (specifiers: Set<string>, dir: string, extensions = defaultExtensions, label: string) => {\n    const patterns = new Set<string>();\n\n    for (const specifier of specifiers) {\n      const absSpecifier = isAbsolute(specifier) ? specifier : prependDirToPattern(dir, specifier);\n      const ws = chief.findWorkspaceByFilePath(absSpecifier);\n      if (ws?.srcDir && ws.outDir && !absSpecifier.startsWith(ws.srcDir) && absSpecifier.startsWith(ws.outDir)) {\n        const pattern = absSpecifier.replace(ws.outDir, ws.srcDir).replace(matchExt, extensions);\n        patterns.add(pattern);\n      } else {\n        patterns.add(absSpecifier);\n      }\n    }\n\n    const filePaths = await _glob({ patterns: Array.from(patterns), cwd: dir, label });\n\n    debugLogArray(toRelative(dir, chief.cwd), 'Source mapping (package.json)', filePaths);\n\n    return filePaths;\n  };\n};\n\nexport type ToSourceFilePath = ReturnType<typeof getModuleSourcePathHandler>;\n"
  },
  {
    "path": "packages/knip/src/util/trace.ts",
    "content": "import pc from 'picocolors';\nimport type { ExportsTreeNode } from '../graph-explorer/operations/build-exports-tree.ts';\n\nexport const formatTrace = (\n  node: ExportsTreeNode,\n  toRelative: (path: string) => string,\n  isReferenced: boolean\n): string => {\n  const lines: string[] = [];\n\n  const file = pc.white;\n  const id = pc.cyanBright;\n  const ref = pc.cyanBright;\n  const via = pc.dim;\n  const ok = pc.green;\n  const fail = pc.red;\n  const dim = pc.dim;\n\n  const entryMarker = node.isEntry ? dim(' ⎆') : '';\n  lines.push(`${file(toRelative(node.filePath))}${dim(':')}${id(node.identifier)}${entryMarker}`);\n\n  const formatVia = (child: ExportsTreeNode): string => {\n    if (!child.via) return id(child.identifier);\n    const parts = child.identifier.split('.');\n    const name = parts[0];\n    const rest = parts.slice(1).join('.');\n    const nameDisplay = child.originalId ? `${id(child.originalId)}${dim(' → ')}${id(name)}` : id(name);\n    return `${via(child.via)}${dim('[')}${nameDisplay}${rest ? `${dim('.')}${id(rest)}` : ''}${dim(']')}`;\n  };\n\n  const formatChild = (child: ExportsTreeNode, prefix: string, isLast: boolean) => {\n    const connector = isLast ? '└── ' : '├── ';\n    const childPrefix = isLast ? '    ' : '│   ';\n    const entryMarker = child.isEntry ? dim(' ⎆') : '';\n    const isLeaf = child.children.length === 0;\n    const leafMarker = isLeaf && !child.via?.startsWith('reExport') ? (isReferenced ? ok(' ✓') : fail(' ✗')) : '';\n\n    lines.push(\n      `${dim(prefix)}${dim(connector)}${file(toRelative(child.filePath))}${dim(':')}${formatVia(child)}${entryMarker}${leafMarker}`\n    );\n\n    if (child.refs.length > 0) {\n      const refsPrefix = isLeaf ? ' ' : '│';\n      lines.push(\n        `${dim(prefix)}${dim(childPrefix)}${dim(refsPrefix)} ${dim('refs: [')}${child.refs.map(r => ref(r)).join(dim(', '))}${dim(']')}`\n      );\n    }\n\n    for (let i = 0; i < child.children.length; i++) {\n      formatChild(child.children[i], prefix + childPrefix, i === child.children.length - 1);\n    }\n  };\n\n  for (let i = 0; i < node.children.length; i++) {\n    formatChild(node.children[i], '', i === node.children.length - 1);\n  }\n\n  if (node.children.length === 0) {\n    const leafMarker = isReferenced ? ok(' ✓') : fail(' ✗');\n    lines.push(`${dim('└── (no imports found)')}${leafMarker}`);\n  }\n\n  return lines.join('\\n');\n};\n"
  },
  {
    "path": "packages/knip/src/util/watch.ts",
    "content": "import type { WatchListener } from 'node:fs';\nimport { readFileSync } from 'node:fs';\nimport type { ConfigurationChief } from '../ConfigurationChief.ts';\nimport { invalidateCache } from '../graph-explorer/cache.ts';\nimport type { IssueCollector } from '../IssueCollector.ts';\nimport type { ProjectPrincipal } from '../ProjectPrincipal.ts';\nimport type { Issues } from '../types/issues.ts';\nimport type { ModuleGraph } from '../types/module-graph.ts';\nimport type { MainOptions } from './create-options.ts';\nimport { debugLog } from './debug.ts';\nimport { isFile } from './fs.ts';\nimport { updateImportMap } from './module-graph.ts';\nimport { toAbsolute, toPosix, toRelative } from './path.ts';\nimport { clearResolverCache } from './resolve.ts';\n\nexport type OnFileChange = (options: { issues: Issues; duration?: number; mem?: number }) => void;\n\nexport type WatchChange = {\n  type: 'added' | 'deleted' | 'modified';\n  filePath: string;\n};\n\nexport type SessionHandler = Awaited<ReturnType<typeof getSessionHandler>>;\n\ntype WatchOptions = {\n  analyzedFiles: Set<string>;\n  analyzeSourceFile: (filePath: string, principal: ProjectPrincipal) => void;\n  chief: ConfigurationChief;\n  collector: IssueCollector;\n  analyze: () => Promise<void>;\n  principal: ProjectPrincipal;\n  graph: ModuleGraph;\n  isIgnored: (path: string) => boolean;\n  onFileChange?: OnFileChange;\n  unreferencedFiles: Set<string>;\n  entryPaths: Set<string>;\n};\n\nconst createUpdate = (options: { startTime: number }) => {\n  const duration = performance.now() - options.startTime;\n  const mem = process.memoryUsage().heapUsed;\n  return { duration, mem };\n};\n\nexport const getSessionHandler = async (\n  options: MainOptions,\n  {\n    analyzedFiles,\n    analyzeSourceFile,\n    chief,\n    collector,\n    analyze,\n    principal,\n    graph,\n    isIgnored,\n    onFileChange,\n    unreferencedFiles,\n    entryPaths,\n  }: WatchOptions\n) => {\n  const handleFileChanges = async (changes: WatchChange[]) => {\n    const startTime = performance.now();\n\n    const added = new Set<string>();\n    const deleted = new Set<string>();\n    const modified = new Set<string>();\n\n    for (const change of changes) {\n      const filePath = toAbsolute(change.filePath, options.cwd);\n      const relativePath = toRelative(change.filePath, options.cwd);\n\n      if (isIgnored(filePath)) {\n        debugLog('*', `ignoring ${change.type} ${relativePath}`);\n        continue;\n      }\n\n      const workspace = chief.findWorkspaceByFilePath(filePath);\n      if (!workspace) continue;\n\n      switch (change.type) {\n        case 'added':\n          principal.addProjectPath(filePath);\n          principal.deletedFiles.delete(filePath);\n          if (principal.projectPaths.has(filePath)) added.add(filePath);\n          debugLog(workspace.name, `Watcher: + ${relativePath}`);\n          break;\n        case 'deleted':\n          deleted.add(filePath);\n          analyzedFiles.delete(filePath);\n          principal.removeProjectPath(filePath);\n          debugLog(workspace.name, `Watcher: - ${relativePath}`);\n          break;\n        default: {\n          const cached = principal.fileManager.sourceTextCache.get(filePath);\n          if (cached !== undefined && cached === readFileSync(filePath, 'utf8')) {\n            debugLog(workspace.name, `Watcher: = ${relativePath}`);\n            continue;\n          }\n          modified.add(filePath);\n          debugLog(workspace.name, `Watcher: ± ${relativePath}`);\n          break;\n        }\n      }\n\n      principal.invalidateFile(filePath);\n    }\n\n    if (added.size === 0 && deleted.size === 0 && modified.size === 0) return;\n\n    clearResolverCache();\n    invalidateCache(graph);\n\n    unreferencedFiles.clear();\n    const cachedUnusedFiles = collector.purge();\n\n    for (const filePath of added) cachedUnusedFiles.add(filePath);\n    for (const filePath of deleted) cachedUnusedFiles.delete(filePath);\n\n    const filePaths = principal.getUsedResolvedFiles();\n\n    if (added.size > 0 || deleted.size > 0) {\n      graph.clear();\n      for (const filePath of filePaths) {\n        const workspace = chief.findWorkspaceByFilePath(filePath);\n        if (workspace) {\n          analyzeSourceFile(filePath, principal);\n        }\n      }\n    } else {\n      for (const [filePath, file] of graph) {\n        if (filePaths.includes(filePath)) {\n          file.importedBy = undefined;\n        } else {\n          graph.delete(filePath);\n          analyzedFiles.delete(filePath);\n          const workspace = chief.findWorkspaceByFilePath(filePath);\n          if (workspace && principal.projectPaths.has(filePath)) cachedUnusedFiles.add(filePath);\n        }\n      }\n\n      for (const filePath of filePaths) {\n        if (!graph.has(filePath)) {\n          const workspace = chief.findWorkspaceByFilePath(filePath);\n          if (workspace) {\n            analyzeSourceFile(filePath, principal);\n          }\n        }\n      }\n\n      for (const filePath of modified) {\n        if (!cachedUnusedFiles.has(filePath)) {\n          const workspace = chief.findWorkspaceByFilePath(filePath);\n          if (workspace) {\n            if (principal.projectPaths.has(filePath) || graph.has(filePath)) {\n              analyzeSourceFile(filePath, principal);\n            }\n          }\n        }\n      }\n\n      for (const filePath of filePaths) {\n        const file = graph.get(filePath);\n        if (file?.internalImportCache) updateImportMap(file, file.internalImportCache, graph);\n      }\n    }\n\n    await analyze();\n\n    const unusedFiles = [...cachedUnusedFiles].filter(filePath => !analyzedFiles.has(filePath));\n    collector.addFilesIssues(unusedFiles);\n    collector.addFileCounts({ processed: analyzedFiles.size, unused: unusedFiles.length });\n\n    for (const issue of collector.getRetainedIssues()) collector.addIssue(issue);\n\n    const update = createUpdate({ startTime });\n\n    if (onFileChange) onFileChange(Object.assign({ issues: getIssues().issues }, update));\n\n    return update;\n  };\n\n  const listener: WatchListener<string | Buffer> = (eventType, filePath) => {\n    debugLog('*', `(raw) ${eventType} ${filePath}`);\n    if (typeof filePath === 'string') {\n      // On Windows, fs.watch provides paths with backslash separators.\n      // Normalize to POSIX separators so downstream posix path utilities work correctly.\n      const normalizedPath = toPosix(filePath);\n      const type = eventType === 'rename' ? (isFile(options.cwd, normalizedPath) ? 'added' : 'deleted') : 'modified';\n      handleFileChanges([{ type, filePath: normalizedPath }]);\n    }\n  };\n\n  const getIssues = () => collector.getIssues();\n\n  const getEntryPaths = () => entryPaths;\n\n  const getGraph = () => graph;\n\n  if (onFileChange) onFileChange({ issues: getIssues().issues });\n\n  return { listener, handleFileChanges, getEntryPaths, getGraph, getIssues };\n};\n"
  },
  {
    "path": "packages/knip/src/util/workspace-file-filter.ts",
    "content": "import { ROOT_WORKSPACE_NAME } from '../constants.ts';\nimport { join } from './path.ts';\n\nexport type WorkspaceFilePathFilter = (filePath: string) => boolean;\n\nexport const createWorkspaceFilePathFilter = (\n  cwd: string,\n  selectedWorkspaces: Set<string> | undefined,\n  availableWorkspaceNames: string[] | undefined\n): WorkspaceFilePathFilter => {\n  if (!selectedWorkspaces || !availableWorkspaceNames) return () => true;\n\n  const includeRoot = selectedWorkspaces.has(ROOT_WORKSPACE_NAME);\n\n  const workspaceDirs = availableWorkspaceNames\n    .filter(name => name !== ROOT_WORKSPACE_NAME)\n    .map(name => ({ name, dir: join(cwd, name) }))\n    .sort((a, b) => b.name.split('/').length - a.name.split('/').length);\n\n  return (filePath: string) => {\n    const match = workspaceDirs.find(ws => filePath.startsWith(`${ws.dir}/`));\n    if (match) return selectedWorkspaces.has(match.name);\n    return includeRoot;\n  };\n};\n"
  },
  {
    "path": "packages/knip/src/util/workspace-selectors.ts",
    "content": "import picomatch from 'picomatch';\nimport type { WorkspacePackage } from '../types/package-json.ts';\nimport { partition } from './array.ts';\nimport { ConfigurationError } from './errors.ts';\nimport { isDirectory, isFile } from './fs.ts';\nimport { join } from './path.ts';\n\ntype WorkspaceSelectorType = 'pkg-name' | 'dir-path' | 'dir-glob';\n\ninterface ParsedSelector {\n  type: WorkspaceSelectorType;\n  pattern: string;\n  isNegated: boolean;\n}\n\n/**\n * Parse a workspace selector token and determine its type.\n * @internal\n */\nexport function parseWorkspaceSelector(token: string, cwd: string): ParsedSelector {\n  const trimmed = token.trim();\n  let isNegated = false;\n  let pattern = trimmed;\n\n  if (trimmed.startsWith('!')) {\n    isNegated = true;\n    pattern = trimmed.slice(1);\n  }\n\n  if (pattern.startsWith('./')) {\n    return {\n      type: 'dir-glob',\n      pattern: pattern.slice(2),\n      isNegated,\n    };\n  }\n\n  const hasGlobChars = /[*?[\\]{}]/.test(pattern);\n  if (pattern.includes('/') && !pattern.startsWith('@') && !hasGlobChars) {\n    return {\n      type: 'dir-path',\n      pattern: pattern,\n      isNegated,\n    };\n  }\n\n  // Existing directory with package.json (backward compatibility)\n  const dirPath = join(cwd, pattern);\n  if (isDirectory(dirPath) && isFile(dirPath, 'package.json')) {\n    return {\n      type: 'dir-path',\n      pattern: pattern,\n      isNegated,\n    };\n  }\n\n  return {\n    type: 'pkg-name',\n    pattern: pattern,\n    isNegated,\n  };\n}\n\n/**\n * Match workspaces by package name pattern.\n * @internal\n */\nexport function matchWorkspacesByPkgName(\n  pattern: string,\n  pkgNames: string[],\n  pkgNameToWorkspaceName: Map<string, string>\n): string[] {\n  const matcher = picomatch(pattern);\n  const matched = pkgNames.filter(name => matcher(name));\n\n  return matched\n    .map(pkgName => pkgNameToWorkspaceName.get(pkgName))\n    .filter((name): name is string => name !== undefined);\n}\n\n/**\n * Match workspaces by directory glob pattern.\n * @internal\n */\nexport function matchWorkspacesByDirGlob(pattern: string, availableWorkspaceNames: string[]): string[] {\n  const matcher = picomatch(pattern);\n  return availableWorkspaceNames.filter(name => matcher(name));\n}\n\n/**\n * Select workspaces based on multiple selectors.\n */\nexport function selectWorkspaces(\n  selectors: string[] | undefined,\n  cwd: string,\n  workspacePackages: Map<string, WorkspacePackage>,\n  availableWorkspaceNames: string[]\n): Set<string> | undefined {\n  if (!selectors || selectors.length === 0) {\n    return undefined;\n  }\n\n  const parsedSelectors = selectors.map(s => parseWorkspaceSelector(s, cwd));\n\n  const pkgNameToWorkspaceName = new Map<string, string>();\n  for (const [workspaceName, pkg] of workspacePackages.entries()) {\n    if (pkg.pkgName) pkgNameToWorkspaceName.set(pkg.pkgName, workspaceName);\n  }\n  const pkgNames = Array.from(pkgNameToWorkspaceName.keys());\n\n  const [positiveSelectors, negativeSelectors] = partition(parsedSelectors, s => !s.isNegated);\n\n  const selectedWorkspaces = new Set<string>(positiveSelectors.length === 0 ? availableWorkspaceNames : []);\n\n  const applySelector = (selector: ParsedSelector) => {\n    let matches: string[] = [];\n\n    switch (selector.type) {\n      case 'pkg-name':\n        matches = matchWorkspacesByPkgName(selector.pattern, pkgNames, pkgNameToWorkspaceName);\n        if (matches.length === 0 && !selector.isNegated && !/[*?[\\]{}]/.test(selector.pattern)) {\n          throw new ConfigurationError(`Workspace package name \"${selector.pattern}\" did not match any workspaces.`);\n        }\n        break;\n\n      case 'dir-path':\n        if (availableWorkspaceNames.includes(selector.pattern)) {\n          matches = [selector.pattern];\n        } else if (!selector.isNegated) {\n          throw new ConfigurationError(`Workspace directory \"${selector.pattern}\" not found.`);\n        }\n        break;\n\n      case 'dir-glob':\n        matches = matchWorkspacesByDirGlob(selector.pattern, availableWorkspaceNames);\n        if (matches.length === 0 && !selector.isNegated) {\n          throw new ConfigurationError(\n            `Workspace directory pattern \"${selector.pattern}\" did not match any workspaces.`\n          );\n        }\n        break;\n    }\n    return matches;\n  };\n\n  for (const selector of positiveSelectors) {\n    for (const match of applySelector(selector)) {\n      selectedWorkspaces.add(match);\n    }\n  }\n\n  for (const selector of negativeSelectors) {\n    for (const match of applySelector(selector)) {\n      selectedWorkspaces.delete(match);\n    }\n  }\n\n  return selectedWorkspaces;\n}\n"
  },
  {
    "path": "packages/knip/src/util/workspace.ts",
    "content": "// Paths sorter predicate function, starting with \"root\", ends with \"the/deep/est/path\"\nexport const byPathDepth = (a: string, b: string) => {\n  const depthA = a.split('/');\n  const depthB = b.split('/');\n  if (depthA.length !== depthB.length) return depthA.length - depthB.length;\n  if (depthA.includes('*') || depthA.includes('**')) return -1;\n  if (depthB.includes('*') || depthB.includes('**')) return 1;\n  return a.length - b.length;\n};\n"
  },
  {
    "path": "packages/knip/src/version.ts",
    "content": "export const version = '6.0.1';\n"
  },
  {
    "path": "packages/knip/test/barrel-namespace-chain.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/barrel-namespace-chain');\n\ntest('Barrel namespace chain: no false positives from OPAQUE, broad namespace refs, or tag hints', async () => {\n  const options = await createOptions({ cwd, tags: ['-knipignore'] });\n  const { issues, counters, tagHints } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 1,\n    processed: 8,\n    total: 8,\n  });\n\n  assert.equal(issues.exports['protocol.ts']['lib.unusedExport'].symbol, 'unusedExport');\n  assert.equal(issues.exports['protocol.ts']['lib.usedExport'], undefined);\n  assert.equal(issues.exports['protocol.ts']['lib.taggedExport'], undefined);\n\n  assert.equal(tagHints.size, 0);\n});\n"
  },
  {
    "path": "packages/knip/test/catalog.empty.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { test } from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\ntest('Should handle empty named catalog entries without null pointer error', async () => {\n  const cwd = resolve('fixtures/catalog-named-empty');\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.catalog['pnpm-workspace.yaml']['prod.lodash']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    catalog: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/catalog.package-json.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { test } from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\ntest('Should track referenced catalog entries (package.json)', async () => {\n  const cwd = resolve('fixtures/catalog-named-package-json');\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.catalog['package.json']['default.lodash']);\n  assert(issues.catalog['package.json']['frontend.nuxt']);\n  assert(issues.catalog['package.json']['backend.fastify']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    catalog: 3,\n    processed: 1,\n    total: 1,\n  });\n});\n\ntest('Should track referenced catalog entries (package.json root)', async () => {\n  const cwd = resolve('fixtures/catalog-named-package-json-root');\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.catalog['package.json']['default.lodash']);\n  assert(issues.catalog['package.json']['frontend.@nu/xt']);\n  assert(issues.catalog['package.json']['backend.fastify']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    catalog: 3,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/catalog.pnpm.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { test } from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\ntest('Should track referenced default catalog entries', async () => {\n  const cwd = resolve('fixtures/catalog-pnpm');\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.catalog['pnpm-workspace.yaml']['default.lodash']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    catalog: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n\ntest('Should track referenced named catalog entries', async () => {\n  const cwd = resolve('fixtures/catalog-named');\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.catalog['pnpm-workspace.yaml']['default.lodash']);\n  assert(issues.catalog['pnpm-workspace.yaml']['frontend.@nu/xt']);\n  assert(issues.catalog['pnpm-workspace.yaml']['backend.fastify']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    catalog: 3,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/catalog.yarn.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { test } from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\ntest('Should track referenced default catalog entries', async () => {\n  const cwd = resolve('fixtures/catalog-yarn');\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.catalog['.yarnrc.yml']['default.@lo/dash']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    catalog: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/cli/cli-config.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { exec } from '../helpers/exec.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\ntest('Support loading js async function for configuration', async () => {\n  const cwd = resolve('fixtures/config-js-async');\n  assert.equal(exec('knip', { cwd }).stdout, '');\n});\n\ntest('Support loading js object files for configuration', async () => {\n  const cwd = resolve('fixtures/config-js-flat');\n  assert.equal(exec('knip', { cwd }).stdout, '');\n});\n\ntest('Support loading json files for configuration', async () => {\n  const cwd = resolve('fixtures/config-json');\n  assert.equal(exec('knip', { cwd }).stdout, '');\n});\n\ntest('Support loading mjs async function files for configuration', async () => {\n  const cwd = resolve('fixtures/config-mjs-async');\n  assert.equal(exec('knip -c knip.mjs', { cwd }).stdout, '');\n});\n\ntest('Support loading package.json for configuration', async () => {\n  const cwd = resolve('fixtures/config-package-json');\n  assert.equal(exec('knip', { cwd }).stdout, '');\n});\n\ntest('Support loading ts async function for configuration', async () => {\n  const cwd = resolve('fixtures/config-ts-async');\n  assert.equal(exec('knip', { cwd }).stdout, '');\n});\n\ntest('Support loading ts object files for configuration', async () => {\n  const cwd = resolve('fixtures/config-ts-flat');\n  assert.equal(exec('knip', { cwd }).stdout, '');\n});\n\ntest('Support loading ts function for configuration', async () => {\n  const cwd = resolve('fixtures/config-ts-function');\n  assert.equal(exec('knip', { cwd }).stdout, '');\n});\n\ntest('Support loading yaml files for configuration', async () => {\n  const cwd = resolve('fixtures/config-yaml');\n  assert.equal(exec('knip -c knip.yaml', { cwd }).stdout, '');\n});\n"
  },
  {
    "path": "packages/knip/test/cli/cli-include.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { exec } from '../helpers/exec.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/cli');\n\ntest('knip --include files,dependencies', () => {\n  const { stdout, status } = exec('knip --include files,dependencies', { cwd });\n  assert.equal(stdout, '');\n  assert.equal(status, 0);\n});\n\ntest('knip --include files --include dependencies', () => {\n  const { stdout, status } = exec('knip --include files --include dependencies', { cwd });\n  assert.equal(stdout, '');\n  assert.equal(status, 0);\n});\n\ntest('knip --include file,dep', () => {\n  const { stderr, status } = exec('knip --include file,dep', { cwd });\n  assert.match(stderr, /Invalid issue type: file/);\n  assert.equal(status, 2);\n});\n\ntest('knip --include files --include deps', () => {\n  const { stderr, status } = exec('knip --include files,deps', { cwd });\n  assert.match(stderr, /Invalid issue type: deps/);\n  assert.equal(status, 2);\n});\n"
  },
  {
    "path": "packages/knip/test/cli/cli-preprocessor.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { exec } from '../helpers/exec.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst skipIfBun = typeof Bun !== 'undefined' ? test.skip : test;\n\nconst cwd = resolve('fixtures/cli-preprocessor');\n\ntest('knip --preprocessor ./index.js', () => {\n  const { stdout } = exec('knip --preprocessor ./index.js', { cwd });\n  assert.equal(stdout, 'hi from js preprocessor');\n});\n\ntest('knip --preprocessor ./index.ts', () => {\n  const { stdout } = exec('knip --preprocessor ./index.ts', { cwd });\n  assert.equal(stdout, 'hi from ts preprocessor');\n});\n\nskipIfBun('knip --preprocessor knip-preprocessor', () => {\n  const { stdout } = exec('knip --preprocessor knip-preprocessor', { cwd });\n  assert.equal(stdout, 'hi from pkg preprocessor');\n});\n\nskipIfBun('knip --preprocessor @org/preprocessor', () => {\n  const { stdout } = exec('knip --preprocessor @org/preprocessor', { cwd });\n  assert.equal(stdout, 'hi from scoped preprocessor');\n});\n\nskipIfBun(`knip --preprocessor with-args --preprocessor-options {\"food\":\"cupcake\"}`, () => {\n  const { stdout } = exec(`knip --preprocessor with-args --preprocessor-options {\"food\":\"cupcake\"}`, { cwd });\n  assert.equal(stdout, 'hi from with-args preprocessor, you gave me: cupcake');\n});\n\ntest('knip --preprocessor {cwd}/index.js', () => {\n  const { stdout } = exec(`knip --preprocessor ${cwd}/index.js`, { cwd });\n  assert.equal(stdout, 'hi from js preprocessor');\n});\n"
  },
  {
    "path": "packages/knip/test/cli/cli-reporter-codeclimate.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport type { Issue } from 'codeclimate-types';\nimport { assertAndRemoveFingerprint, orderByPos } from '../helpers/assertAndRemoveProperty.ts';\nimport { exec } from '../helpers/exec.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/exports');\n\ntest('knip --reporter codeclimate (exports & types)', () => {\n  const json: Issue[] = [\n    {\n      type: 'issue',\n      check_name: 'Unused exports',\n      description: 'Unused export: unusedNumber',\n      categories: ['Bug Risk'],\n      location: { path: 'my-module.ts', positions: { begin: { line: 23, column: 14 }, end: { line: 23, column: 14 } } },\n      severity: 'major',\n    },\n    {\n      type: 'issue',\n      check_name: 'Unused exports',\n      description: 'Unused export: unusedFunction',\n      categories: ['Bug Risk'],\n      location: { path: 'my-module.ts', positions: { begin: { line: 24, column: 14 }, end: { line: 24, column: 14 } } },\n      severity: 'major',\n    },\n    {\n      type: 'issue',\n      check_name: 'Unused exports',\n      description: 'Unused export: default',\n      categories: ['Bug Risk'],\n      location: { path: 'my-module.ts', positions: { begin: { line: 30, column: 16 }, end: { line: 30, column: 16 } } },\n      severity: 'major',\n    },\n    {\n      type: 'issue',\n      check_name: 'Unused exports',\n      description: 'Unused export: renamedExport',\n      categories: ['Bug Risk'],\n      location: {\n        path: 'named-exports.ts',\n        positions: { begin: { line: 6, column: 30 }, end: { line: 6, column: 30 } },\n      },\n      severity: 'major',\n    },\n    {\n      type: 'issue',\n      check_name: 'Unused exports',\n      description: 'Unused export: namedExport',\n      categories: ['Bug Risk'],\n      location: {\n        path: 'named-exports.ts',\n        positions: { begin: { line: 7, column: 15 }, end: { line: 7, column: 15 } },\n      },\n      severity: 'major',\n    },\n    {\n      type: 'issue',\n      check_name: 'Unused exports',\n      description: 'Unused export: unusedZero',\n      categories: ['Bug Risk'],\n      location: {\n        path: 'dynamic-import.ts',\n        positions: { begin: { line: 3, column: 14 }, end: { line: 3, column: 14 } },\n      },\n      severity: 'major',\n    },\n    {\n      type: 'issue',\n      check_name: 'Unused exports',\n      description: 'Unused export: unusedInMix',\n      categories: ['Bug Risk'],\n      location: { path: 'my-mix.ts', positions: { begin: { line: 1, column: 14 }, end: { line: 1, column: 14 } } },\n      severity: 'major',\n    },\n    {\n      type: 'issue',\n      check_name: 'Unused exports',\n      description: 'Unused export: NamedExport',\n      categories: ['Bug Risk'],\n      location: { path: 'default.ts', positions: { begin: { line: 1, column: 14 }, end: { line: 1, column: 14 } } },\n      severity: 'major',\n    },\n    {\n      type: 'issue',\n      check_name: 'Unused exports',\n      description: 'Unused export: nsUnusedKey (MyNamespace)',\n      categories: ['Bug Risk'],\n      location: {\n        path: 'my-namespace.ts',\n        positions: { begin: { line: 3, column: 14 }, end: { line: 3, column: 14 } },\n      },\n      severity: 'major',\n    },\n    {\n      type: 'issue',\n      check_name: 'Unused exported types',\n      description: 'Unused exported type: MyAnyType',\n      categories: ['Bug Risk'],\n      location: { path: 'my-module.ts', positions: { begin: { line: 28, column: 13 }, end: { line: 28, column: 13 } } },\n      severity: 'major',\n    },\n    {\n      type: 'issue',\n      check_name: 'Unused exported types',\n      description: 'Unused exported type: MyEnum',\n      categories: ['Bug Risk'],\n      location: { path: 'types.ts', positions: { begin: { line: 3, column: 13 }, end: { line: 3, column: 13 } } },\n      severity: 'major',\n    },\n    {\n      type: 'issue',\n      check_name: 'Unused exported types',\n      description: 'Unused exported type: MyType',\n      categories: ['Bug Risk'],\n      location: { path: 'types.ts', positions: { begin: { line: 8, column: 15 }, end: { line: 8, column: 15 } } },\n      severity: 'major',\n    },\n    {\n      type: 'issue',\n      check_name: 'Unused exported types',\n      description: 'Unused exported type: MyNamespace (MyNamespace)',\n      categories: ['Bug Risk'],\n      location: {\n        path: 'my-namespace.ts',\n        positions: { begin: { line: 5, column: 18 }, end: { line: 5, column: 18 } },\n      },\n      severity: 'major',\n    },\n    {\n      type: 'issue',\n      check_name: 'Duplicate exports',\n      description: 'Duplicate export: exportedResult',\n      categories: ['Duplication'],\n      location: { path: 'my-module.ts', positions: { begin: { line: 26, column: 14 }, end: { line: 26, column: 14 } } },\n      severity: 'major',\n    },\n    {\n      type: 'issue',\n      check_name: 'Duplicate exports',\n      description: 'Duplicate export: default',\n      categories: ['Duplication'],\n      location: { path: 'my-module.ts', positions: { begin: { line: 30, column: 16 }, end: { line: 30, column: 16 } } },\n      severity: 'major',\n    },\n  ];\n\n  const issues: Issue[] = JSON.parse(exec('knip --reporter codeclimate', { cwd }).stdout);\n\n  const issuesWithoutFingerprints = issues.map(assertAndRemoveFingerprint);\n\n  assert.deepEqual(issuesWithoutFingerprints.sort(orderByPos), json.sort(orderByPos));\n});\n"
  },
  {
    "path": "packages/knip/test/cli/cli-reporter-codeclimate2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport type { Issue } from 'codeclimate-types';\nimport { assertAndRemoveFingerprint, orderByPos } from '../helpers/assertAndRemoveProperty.ts';\nimport { exec } from '../helpers/exec.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/module-resolution-non-std');\n\ntest('knip --reporter codeclimate (files, unlisted & unresolved)', () => {\n  const json: Issue[] = [\n    {\n      categories: ['Bug Risk'],\n      check_name: 'Unused files',\n      description: 'Unused file: src/unused.ts',\n      location: { path: 'src/unused.ts', lines: { begin: 0, end: 0 } },\n      severity: 'major',\n      type: 'issue',\n    },\n    {\n      type: 'issue',\n      check_name: 'Unlisted dependencies',\n      description: 'Unlisted dependency: unresolved',\n      categories: ['Bug Risk'],\n      location: { path: 'src/index.ts', positions: { begin: { line: 9, column: 27 }, end: { line: 9, column: 27 } } },\n      severity: 'major',\n    },\n    {\n      type: 'issue',\n      check_name: 'Unlisted dependencies',\n      description: 'Unlisted dependency: @org/unresolved',\n      categories: ['Bug Risk'],\n      location: { path: 'src/index.ts', positions: { begin: { line: 10, column: 27 }, end: { line: 10, column: 27 } } },\n      severity: 'major',\n    },\n    {\n      type: 'issue',\n      check_name: 'Unresolved imports',\n      description: 'Unresolved import: ./unresolved',\n      categories: ['Bug Risk'],\n      location: { path: 'src/index.ts', positions: { begin: { line: 8, column: 24 }, end: { line: 8, column: 24 } } },\n      severity: 'major',\n    },\n  ];\n\n  const issues: Issue[] = JSON.parse(exec('knip --reporter codeclimate', { cwd }).stdout);\n\n  const issuesWithoutFingerprints = issues.map(assertAndRemoveFingerprint);\n\n  assert.deepEqual(issuesWithoutFingerprints.sort(orderByPos), json.sort(orderByPos));\n});\n"
  },
  {
    "path": "packages/knip/test/cli/cli-reporter-codeclimate3.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport type { Issue } from 'codeclimate-types';\nimport { assertAndRemoveFingerprint, orderByPos } from '../helpers/assertAndRemoveProperty.ts';\nimport { exec } from '../helpers/exec.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/enum-members');\n\ntest('knip --reporter codeclimate (enum members)', () => {\n  const json: Issue[] = [\n    {\n      type: 'issue',\n      check_name: 'Unused exported enum members',\n      description: 'Unused exported enum member: B_Unused (MyEnum)',\n      categories: ['Bug Risk'],\n      location: { path: 'members.ts', positions: { begin: { line: 9, column: 3 }, end: { line: 9, column: 3 } } },\n      severity: 'major',\n    },\n    {\n      type: 'issue',\n      check_name: 'Unused exported enum members',\n      description: 'Unused exported enum member: D-Key (MyEnum)',\n      categories: ['Bug Risk'],\n      location: { path: 'members.ts', positions: { begin: { line: 11, column: 3 }, end: { line: 11, column: 3 } } },\n      severity: 'major',\n    },\n  ];\n\n  const issues: Issue[] = JSON.parse(exec('knip --reporter codeclimate', { cwd }).stdout);\n\n  const issuesWithoutFingerprints = issues.map(assertAndRemoveFingerprint);\n\n  assert.deepEqual(issuesWithoutFingerprints.sort(orderByPos), json.sort(orderByPos));\n});\n"
  },
  {
    "path": "packages/knip/test/cli/cli-reporter-codeclimate4.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport type { Issue } from 'codeclimate-types';\nimport { assertAndRemoveFingerprint, orderByPos } from '../helpers/assertAndRemoveProperty.ts';\nimport { exec } from '../helpers/exec.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/dependencies');\n\ntest('knip --reporter codeclimate (dependencies)', () => {\n  const json: Issue[] = [\n    {\n      categories: ['Bug Risk'],\n      check_name: 'Unused files',\n      description: 'Unused file: unused-module.ts',\n      location: { path: 'unused-module.ts', lines: { begin: 0, end: 0 } },\n      severity: 'major',\n      type: 'issue',\n    },\n    {\n      type: 'issue',\n      check_name: 'Unused dependencies',\n      description: 'Unused dependency: @tootallnate/once',\n      categories: ['Bug Risk'],\n      location: { path: 'package.json', positions: { begin: { line: 8, column: 6 }, end: { line: 8, column: 6 } } },\n      severity: 'major',\n    },\n    {\n      type: 'issue',\n      check_name: 'Unused dependencies',\n      description: 'Unused dependency: fs-extra',\n      categories: ['Bug Risk'],\n      location: { path: 'package.json', positions: { begin: { line: 10, column: 6 }, end: { line: 10, column: 6 } } },\n      severity: 'major',\n    },\n    {\n      type: 'issue',\n      check_name: 'Unused devDependencies',\n      description: 'Unused devDependency: mocha',\n      categories: ['Bug Risk'],\n      location: { path: 'package.json', positions: { begin: { line: 23, column: 6 }, end: { line: 23, column: 6 } } },\n      severity: 'major',\n    },\n    {\n      type: 'issue',\n      check_name: 'Unlisted binaries',\n      description: 'Unlisted binary: jest',\n      categories: ['Bug Risk'],\n      location: { path: 'package.json', lines: { begin: 0, end: 0 } },\n      severity: 'major',\n    },\n    {\n      type: 'issue',\n      check_name: 'Unlisted binaries',\n      description: 'Unlisted binary: start-server',\n      categories: ['Bug Risk'],\n      location: { path: 'package.json', lines: { begin: 0, end: 0 } },\n      severity: 'major',\n    },\n  ];\n\n  const issues: Issue[] = JSON.parse(exec('knip --reporter codeclimate', { cwd }).stdout);\n\n  const issuesWithoutFingerprints = issues.map(assertAndRemoveFingerprint);\n\n  assert.deepEqual(issuesWithoutFingerprints.sort(orderByPos), json.sort(orderByPos));\n});\n"
  },
  {
    "path": "packages/knip/test/cli/cli-reporter-compact.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { exec } from '../helpers/exec.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/compact-reporter');\n\ntest('knip --reporter compact (with empty issue records after ignore)', () => {\n  const { stdout, stderr } = exec('knip --reporter compact', { cwd });\n  assert.equal(stderr, '');\n  assert.match(stdout, /unused-dep/);\n});\n"
  },
  {
    "path": "packages/knip/test/cli/cli-reporter-github-actions.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { test } from 'node:test';\nimport { resolve } from '../../src/util/path.ts';\nimport { showDiff } from '../helpers/diff.ts';\nimport { exec } from '../helpers/exec.ts';\n\nconst moduleCwd = resolve('fixtures/module-resolution-non-std');\n\ntest('knip --reporter github-actions (files, unlisted & unresolved)', () => {\n  const actual = exec('knip --reporter github-actions', { cwd: moduleCwd }).stdout;\n  const expected = `Unused files (1)\n::error file=src/unused.ts,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Unused files::src/unused.ts\nUnlisted dependencies (2)\n::error file=src/index.ts,line=9,endLine=9,col=27,endColumn=27,title=✂️ Knip / Unlisted dependencies::unresolved in src/index.ts\n::error file=src/index.ts,line=10,endLine=10,col=27,endColumn=27,title=✂️ Knip / Unlisted dependencies::@org/unresolved in src/index.ts\nUnresolved imports (1)\n::error file=src/index.ts,line=8,endLine=8,col=24,endColumn=24,title=✂️ Knip / Unresolved imports::./unresolved in src/index.ts`;\n  if (actual !== expected) showDiff(actual, expected);\n  assert.equal(actual, expected);\n});\n\nconst rulesCwd = resolve('fixtures/rules');\n\ntest('knip --reporter github-actions (rules: unused export, unused dep, unresolved)', () => {\n  const actual = exec('knip --reporter github-actions', { cwd: rulesCwd }).stdout;\n  const expected = `Unused files (1)\n::warning file=unused.ts,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Unused files::unused.ts\nUnused dependencies (1)\n::warning file=package.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Unused dependencies::unused in package.json\nUnused devDependencies (1)\n::warning file=package.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Unused devDependencies::@dev/unused in package.json\nReferenced optional peerDependencies (1)\n::warning file=package.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Referenced optional peerDependencies::optional-peer-dep in package.json\nUnlisted dependencies (1)\n::warning file=index.ts,line=5,endLine=5,col=8,endColumn=8,title=✂️ Knip / Unlisted dependencies::unlisted in index.ts\nUnlisted binaries (1)\n::warning file=package.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Unlisted binaries::unlisted in package.json\nUnresolved imports (1)\n::warning file=index.ts,line=4,endLine=4,col=8,endColumn=8,title=✂️ Knip / Unresolved imports::./unresolved in index.ts\nUnused exports (2)\n::warning file=exports.ts,line=2,endLine=2,col=14,endColumn=14,title=✂️ Knip / Unused exports::unused in exports.ts\n::warning file=ns.ts,line=2,endLine=2,col=14,endColumn=14,title=✂️ Knip / Unused exports::unused in ns.ts\nUnused exported types (2)\n::warning file=exports.ts,line=5,endLine=5,col=13,endColumn=13,title=✂️ Knip / Unused exported types::UnusedType in exports.ts\n::warning file=ns.ts,line=5,endLine=5,col=13,endColumn=13,title=✂️ Knip / Unused exported types::UnusedType in ns.ts\nUnused exported enum members (1)\n::warning file=exports.ts,line=15,endLine=15,col=3,endColumn=3,title=✂️ Knip / Unused exported enum members::unused in exports.ts\nDuplicate exports (1)\n::warning file=exports.ts,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Duplicate exports::used|default in exports.ts`;\n  if (actual !== expected) showDiff(actual, expected);\n  assert.equal(actual, expected);\n});\n\nconst workspacesCwd = resolve('fixtures/workspaces');\n\ntest('knip --reporter github-actions (workspaces: unused export, unused dep, unlisted dep)', () => {\n  const actual = exec('knip --reporter github-actions', { cwd: workspacesCwd }).stdout;\n  const expected = `Unused files (1)\n::error file=docs/dangling.ts,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Unused files::docs/dangling.ts\nUnused dependencies (4)\n::error file=apps/backend/package.json,line=7,endLine=7,col=6,endColumn=6,title=✂️ Knip / Unused dependencies::picomatch in apps/backend/package.json\n::error file=apps/backend/package.json,line=8,endLine=8,col=6,endColumn=6,title=✂️ Knip / Unused dependencies::next in apps/backend/package.json\n::error file=package.json,line=11,endLine=11,col=6,endColumn=6,title=✂️ Knip / Unused dependencies::minimist in package.json\n::error file=package.json,line=13,endLine=13,col=6,endColumn=6,title=✂️ Knip / Unused dependencies::zod in package.json\nUnlisted dependencies (4)\n::error file=apps/backend/index.ts,line=3,endLine=3,col=24,endColumn=24,title=✂️ Knip / Unlisted dependencies::globby in apps/backend/index.ts\n::error file=apps/backend/index.ts,line=4,endLine=4,col=18,endColumn=18,title=✂️ Knip / Unlisted dependencies::js-yaml in apps/backend/index.ts\n::error file=apps/frontend/index.ts,line=3,endLine=3,col=19,endColumn=19,title=✂️ Knip / Unlisted dependencies::vanilla-js in apps/frontend/index.ts\n::error file=packages/tools/tsconfig.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Unlisted dependencies::@fixtures/workspaces__tsconfig in packages/tools/tsconfig.json\nUnused exports (1)\n::error file=packages/tools/utils.ts,line=3,endLine=3,col=14,endColumn=14,title=✂️ Knip / Unused exports::helperFn in packages/tools/utils.ts\nUnused exported types (1)\n::error file=packages/shared/types.ts,line=3,endLine=3,col=13,endColumn=13,title=✂️ Knip / Unused exported types::UnusedEnum in packages/shared/types.ts\nConfiguration hints (4)\n::notice file=apps/backend/package.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Configuration hints::Package entry file not found: ./index.js in apps/backend/package.json\n::notice file=apps/frontend/package.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Configuration hints::Package entry file not found: ./index.js in apps/frontend/package.json\n::notice file=packages/shared/package.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Configuration hints::Package entry file not found: ./index.js in packages/shared/package.json\n::notice file=packages/tools/package.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Configuration hints::Package entry file not found: ./index.js in packages/tools/package.json`;\n  if (actual !== expected) showDiff(actual, expected);\n  assert.equal(actual, expected);\n});\n\ntest('knip --reporter github-actions (workspaces: config hints disabled)', () => {\n  const actual = exec('knip --reporter github-actions --no-config-hints', { cwd: workspacesCwd }).stdout;\n  const expected = `Unused files (1)\n::error file=docs/dangling.ts,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Unused files::docs/dangling.ts\nUnused dependencies (4)\n::error file=apps/backend/package.json,line=7,endLine=7,col=6,endColumn=6,title=✂️ Knip / Unused dependencies::picomatch in apps/backend/package.json\n::error file=apps/backend/package.json,line=8,endLine=8,col=6,endColumn=6,title=✂️ Knip / Unused dependencies::next in apps/backend/package.json\n::error file=package.json,line=11,endLine=11,col=6,endColumn=6,title=✂️ Knip / Unused dependencies::minimist in package.json\n::error file=package.json,line=13,endLine=13,col=6,endColumn=6,title=✂️ Knip / Unused dependencies::zod in package.json\nUnlisted dependencies (4)\n::error file=apps/backend/index.ts,line=3,endLine=3,col=24,endColumn=24,title=✂️ Knip / Unlisted dependencies::globby in apps/backend/index.ts\n::error file=apps/backend/index.ts,line=4,endLine=4,col=18,endColumn=18,title=✂️ Knip / Unlisted dependencies::js-yaml in apps/backend/index.ts\n::error file=apps/frontend/index.ts,line=3,endLine=3,col=19,endColumn=19,title=✂️ Knip / Unlisted dependencies::vanilla-js in apps/frontend/index.ts\n::error file=packages/tools/tsconfig.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Unlisted dependencies::@fixtures/workspaces__tsconfig in packages/tools/tsconfig.json\nUnused exports (1)\n::error file=packages/tools/utils.ts,line=3,endLine=3,col=14,endColumn=14,title=✂️ Knip / Unused exports::helperFn in packages/tools/utils.ts\nUnused exported types (1)\n::error file=packages/shared/types.ts,line=3,endLine=3,col=13,endColumn=13,title=✂️ Knip / Unused exported types::UnusedEnum in packages/shared/types.ts`;\n  if (actual !== expected) showDiff(actual, expected);\n  assert.equal(actual, expected);\n});\n\ntest('knip --reporter github-actions (workspaces: config hints as errors)', () => {\n  const actual = exec('knip --reporter github-actions --treat-config-hints-as-errors', { cwd: workspacesCwd }).stdout;\n  const expected = `Unused files (1)\n::error file=docs/dangling.ts,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Unused files::docs/dangling.ts\nUnused dependencies (4)\n::error file=apps/backend/package.json,line=7,endLine=7,col=6,endColumn=6,title=✂️ Knip / Unused dependencies::picomatch in apps/backend/package.json\n::error file=apps/backend/package.json,line=8,endLine=8,col=6,endColumn=6,title=✂️ Knip / Unused dependencies::next in apps/backend/package.json\n::error file=package.json,line=11,endLine=11,col=6,endColumn=6,title=✂️ Knip / Unused dependencies::minimist in package.json\n::error file=package.json,line=13,endLine=13,col=6,endColumn=6,title=✂️ Knip / Unused dependencies::zod in package.json\nUnlisted dependencies (4)\n::error file=apps/backend/index.ts,line=3,endLine=3,col=24,endColumn=24,title=✂️ Knip / Unlisted dependencies::globby in apps/backend/index.ts\n::error file=apps/backend/index.ts,line=4,endLine=4,col=18,endColumn=18,title=✂️ Knip / Unlisted dependencies::js-yaml in apps/backend/index.ts\n::error file=apps/frontend/index.ts,line=3,endLine=3,col=19,endColumn=19,title=✂️ Knip / Unlisted dependencies::vanilla-js in apps/frontend/index.ts\n::error file=packages/tools/tsconfig.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Unlisted dependencies::@fixtures/workspaces__tsconfig in packages/tools/tsconfig.json\nUnused exports (1)\n::error file=packages/tools/utils.ts,line=3,endLine=3,col=14,endColumn=14,title=✂️ Knip / Unused exports::helperFn in packages/tools/utils.ts\nUnused exported types (1)\n::error file=packages/shared/types.ts,line=3,endLine=3,col=13,endColumn=13,title=✂️ Knip / Unused exported types::UnusedEnum in packages/shared/types.ts\nConfiguration hints (4)\n::error file=apps/backend/package.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Configuration hints::Package entry file not found: ./index.js in apps/backend/package.json\n::error file=apps/frontend/package.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Configuration hints::Package entry file not found: ./index.js in apps/frontend/package.json\n::error file=packages/shared/package.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Configuration hints::Package entry file not found: ./index.js in packages/shared/package.json\n::error file=packages/tools/package.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Configuration hints::Package entry file not found: ./index.js in packages/tools/package.json`;\n  if (actual !== expected) showDiff(actual, expected);\n  assert.equal(actual, expected);\n});\n\nconst nuxtCwd = resolve('fixtures/plugins/nuxt');\n\ntest('knip --reporter github-actions (nuxt: unused export, unused dep)', () => {\n  const actual = exec('knip --reporter github-actions', { cwd: nuxtCwd }).stdout;\n  const expected = `Unused dependencies (1)\n::error file=package.json,line=12,endLine=12,col=6,endColumn=6,title=✂️ Knip / Unused dependencies::vue in package.json\nUnused exports (1)\n::error file=utils/fn.ts,line=3,endLine=3,col=14,endColumn=14,title=✂️ Knip / Unused exports::unused in utils/fn.ts`;\n  if (actual !== expected) showDiff(actual, expected);\n  assert.equal(actual, expected);\n});\n\nconst configHintsCwd = resolve('fixtures/configuration-hints');\n\ntest('knip --reporter github-actions (configuration hints)', () => {\n  const actual = exec('knip --reporter github-actions', { cwd: configHintsCwd }).stdout;\n  const expected = `Unused files (1)\n::error file=src/entry.js,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Unused files::src/entry.js\nConfiguration hints (2)\n::notice file=knip.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Configuration hints::Remove, or move unused top-level entry to one of \"workspaces\": [src/entry.js] in knip.json\n::notice file=knip.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Configuration hints::Remove, or move unused top-level project to one of \"workspaces\": [src/**] in knip.json`;\n  if (actual !== expected) showDiff(actual, expected);\n  assert.equal(actual, expected);\n});\n\ntest('knip --reporter github-actions --no-config-hints', () => {\n  const actual = exec('knip --reporter github-actions --no-config-hints', { cwd: configHintsCwd }).stdout;\n  const expected = `Unused files (1)\n::error file=src/entry.js,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Unused files::src/entry.js`;\n  if (actual !== expected) showDiff(actual, expected);\n  assert.equal(actual, expected);\n});\n\nconst configHints2Cwd = resolve('fixtures/configuration-hints2');\n\ntest('knip --reporter github-actions (configuration hints 2)', () => {\n  const actual = exec('knip --reporter github-actions', { cwd: configHints2Cwd }).stdout;\n  const expected = `Configuration hints (4)\n::notice file=knip.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Configuration hints::Refine entry pattern (no matches): lib/index.js in knip.json\n::notice file=knip.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Configuration hints::Refine project pattern (no matches): lib/** in knip.json\n::notice file=knip.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Configuration hints::Remove, or move unused top-level entry to one of \"workspaces\": [src/entry.js, …] in knip.json\n::notice file=knip.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Configuration hints::Remove, or move unused top-level project to one of \"workspaces\": [src/**] in knip.json`;\n  if (actual !== expected) showDiff(actual, expected);\n  assert.equal(actual, expected);\n});\n\nconst treatConfigHintsAsErrorsCwd = resolve('fixtures/treat-config-hints-as-errors');\n\ntest('knip --reporter github-actions --treat-config-hints-as-errors', () => {\n  const actual = exec('knip --reporter github-actions --treat-config-hints-as-errors', {\n    cwd: treatConfigHintsAsErrorsCwd,\n  }).stdout;\n  const expected = `Configuration hints (1)\n::error file=package.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Configuration hints::Remove from ignoreDependencies: pineapple in package.json`;\n  if (actual !== expected) showDiff(actual, expected);\n  assert.equal(actual, expected);\n});\n\nconst treatConfigHintsAsErrors2Cwd = resolve('fixtures/treat-config-hints-as-errors2');\n\ntest('knip --reporter github-actions (treatConfigHintsAsErrors: true)', () => {\n  const actual = exec('knip --reporter github-actions', { cwd: treatConfigHintsAsErrors2Cwd }).stdout;\n  const expected = `Configuration hints (1)\n::error file=package.json,line=1,endLine=1,col=1,endColumn=1,title=✂️ Knip / Configuration hints::Remove from ignoreDependencies: bananas in package.json`;\n  if (actual !== expected) showDiff(actual, expected);\n  assert.equal(actual, expected);\n});\n"
  },
  {
    "path": "packages/knip/test/cli/cli-reporter-json-catalog.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { test } from 'node:test';\nimport { exec } from '../helpers/exec.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/catalog-pnpm');\n\ntest('knip --reporter json (catalog)', () => {\n  const json = {\n    issues: [\n      {\n        file: 'pnpm-workspace.yaml',\n        binaries: [],\n        catalog: [{ namespace: 'default', name: 'lodash', line: 7, col: 3 }],\n        dependencies: [],\n        devDependencies: [],\n        duplicates: [],\n        enumMembers: [],\n        exports: [],\n        files: [],\n        namespaceMembers: [],\n        optionalPeerDependencies: [],\n        types: [],\n        unlisted: [],\n        unresolved: [],\n      },\n    ],\n  };\n\n  const result: typeof json = JSON.parse(exec('knip --reporter json', { cwd }).stdout);\n\n  assert.deepEqual(result, json);\n});\n"
  },
  {
    "path": "packages/knip/test/cli/cli-reporter-json.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { exec } from '../helpers/exec.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/exports');\n\ntest('knip --reporter json (exports & types)', () => {\n  const json = {\n    issues: [\n      {\n        file: 'default.ts',\n        binaries: [],\n        catalog: [],\n        dependencies: [],\n        devDependencies: [],\n        duplicates: [],\n        enumMembers: [],\n        exports: [{ name: 'NamedExport', line: 1, col: 14, pos: 13 }],\n        files: [],\n        namespaceMembers: [],\n        optionalPeerDependencies: [],\n        types: [],\n        unlisted: [],\n        unresolved: [],\n      },\n      {\n        file: 'dynamic-import.ts',\n        binaries: [],\n        catalog: [],\n        dependencies: [],\n        devDependencies: [],\n        duplicates: [],\n        enumMembers: [],\n        exports: [{ name: 'unusedZero', line: 3, col: 14, pos: 39 }],\n        files: [],\n        namespaceMembers: [],\n        optionalPeerDependencies: [],\n        types: [],\n        unlisted: [],\n        unresolved: [],\n      },\n      {\n        file: 'my-mix.ts',\n        binaries: [],\n        catalog: [],\n        dependencies: [],\n        devDependencies: [],\n        duplicates: [],\n        enumMembers: [],\n        exports: [{ name: 'unusedInMix', line: 1, col: 14, pos: 13 }],\n        files: [],\n        namespaceMembers: [],\n        optionalPeerDependencies: [],\n        types: [],\n        unlisted: [],\n        unresolved: [],\n      },\n      {\n        file: 'my-module.ts',\n        binaries: [],\n        catalog: [],\n        dependencies: [],\n        devDependencies: [],\n        duplicates: [\n          [\n            { name: 'exportedResult', line: 26, col: 14, pos: 770 },\n            { name: 'default', line: 30, col: 16, pos: 855 },\n          ],\n        ],\n        enumMembers: [],\n        exports: [\n          { name: 'unusedNumber', line: 23, col: 14, pos: 682 },\n          { name: 'unusedFunction', line: 24, col: 14, pos: 713 },\n          { name: 'default', line: 30, col: 16, pos: 855 },\n        ],\n        files: [],\n        namespaceMembers: [],\n        optionalPeerDependencies: [],\n        types: [{ name: 'MyAnyType', line: 28, col: 13, pos: 822 }],\n        unlisted: [],\n        unresolved: [],\n      },\n      {\n        file: 'my-namespace.ts',\n        binaries: [],\n        catalog: [],\n        dependencies: [],\n        devDependencies: [],\n        duplicates: [],\n        enumMembers: [],\n        exports: [{ namespace: 'MyNamespace', name: 'nsUnusedKey', line: 3, col: 14, pos: 84 }],\n        files: [],\n        namespaceMembers: [],\n        optionalPeerDependencies: [],\n        types: [{ namespace: 'MyNamespace', name: 'MyNamespace', line: 5, col: 18, pos: 119 }],\n        unlisted: [],\n        unresolved: [],\n      },\n      {\n        file: 'named-exports.ts',\n        binaries: [],\n        catalog: [],\n        dependencies: [],\n        devDependencies: [],\n        duplicates: [],\n        enumMembers: [],\n        exports: [\n          { name: 'renamedExport', line: 6, col: 30, pos: 179 },\n          { name: 'namedExport', line: 7, col: 15, pos: 215 },\n        ],\n        files: [],\n        namespaceMembers: [],\n        optionalPeerDependencies: [],\n        types: [],\n        unlisted: [],\n        unresolved: [],\n      },\n      {\n        file: 'types.ts',\n        binaries: [],\n        catalog: [],\n        dependencies: [],\n        devDependencies: [],\n        duplicates: [],\n        enumMembers: [],\n        exports: [],\n        files: [],\n        namespaceMembers: [],\n        optionalPeerDependencies: [],\n        types: [\n          { name: 'MyEnum', line: 3, col: 13, pos: 71 },\n          { name: 'MyType', line: 8, col: 15, pos: 146 },\n        ],\n        unlisted: [],\n        unresolved: [],\n      },\n    ],\n  };\n\n  const result: typeof json = JSON.parse(exec('knip --reporter json', { cwd }).stdout);\n  result.issues = result.issues.sort((a, b) => a.file.localeCompare(b.file));\n\n  assert.deepEqual(result, json);\n});\n"
  },
  {
    "path": "packages/knip/test/cli/cli-reporter-json2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { exec } from '../helpers/exec.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/module-resolution-non-std');\n\ntest('knip --reporter json (files, unlisted & unresolved)', () => {\n  const json = {\n    issues: [\n      {\n        file: 'src/unused.ts',\n        binaries: [],\n        catalog: [],\n        dependencies: [],\n        devDependencies: [],\n        duplicates: [],\n        enumMembers: [],\n        exports: [],\n        files: [{ name: 'src/unused.ts' }],\n        namespaceMembers: [],\n        optionalPeerDependencies: [],\n        types: [],\n        unlisted: [],\n        unresolved: [],\n      },\n      {\n        file: 'src/index.ts',\n        binaries: [],\n        catalog: [],\n        dependencies: [],\n        devDependencies: [],\n        duplicates: [],\n        enumMembers: [],\n        exports: [],\n        files: [],\n        namespaceMembers: [],\n        optionalPeerDependencies: [],\n        types: [],\n        unlisted: [\n          { name: 'unresolved', line: 9, col: 27, pos: 450 },\n          { name: '@org/unresolved', line: 10, col: 27, pos: 490 },\n        ],\n        unresolved: [{ name: './unresolved', line: 8, col: 24, pos: 408 }],\n      },\n    ],\n  };\n\n  assert.equal(exec('knip --reporter json', { cwd }).stdout, JSON.stringify(json));\n});\n"
  },
  {
    "path": "packages/knip/test/cli/cli-reporter-json3.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { exec } from '../helpers/exec.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/enum-members');\n\ntest('knip --reporter json (enum members)', () => {\n  const json = {\n    issues: [\n      {\n        file: 'members.ts',\n        binaries: [],\n        catalog: [],\n        dependencies: [],\n        devDependencies: [],\n        duplicates: [],\n        enumMembers: [\n          { namespace: 'MyEnum', name: 'B_Unused', line: 9, col: 3, pos: 127 },\n          { namespace: 'MyEnum', name: 'D-Key', line: 11, col: 3, pos: 165 },\n        ],\n        exports: [],\n        files: [],\n        namespaceMembers: [],\n        optionalPeerDependencies: [],\n        types: [],\n        unlisted: [],\n        unresolved: [],\n      },\n    ],\n  };\n\n  assert.equal(exec('knip --reporter json', { cwd }).stdout, JSON.stringify(json));\n});\n"
  },
  {
    "path": "packages/knip/test/cli/cli-reporter-json4.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { exec } from '../helpers/exec.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/dependencies');\n\ntest('knip --reporter json (dependencies)', () => {\n  const json = {\n    issues: [\n      {\n        file: 'unused-module.ts',\n        binaries: [],\n        catalog: [],\n        dependencies: [],\n        devDependencies: [],\n        duplicates: [],\n        enumMembers: [],\n        exports: [],\n        files: [{ name: 'unused-module.ts' }],\n        namespaceMembers: [],\n        optionalPeerDependencies: [],\n        types: [],\n        unlisted: [],\n        unresolved: [],\n      },\n      {\n        file: 'package.json',\n        binaries: [{ name: 'jest' }, { name: 'start-server' }],\n        catalog: [],\n        dependencies: [\n          { name: '@tootallnate/once', line: 8, col: 6, pos: 131 },\n          { name: 'fs-extra', line: 10, col: 6, pos: 190 },\n        ],\n        devDependencies: [{ name: 'mocha', line: 23, col: 6, pos: 422 }],\n        duplicates: [],\n        enumMembers: [],\n        exports: [],\n        files: [],\n        namespaceMembers: [],\n        optionalPeerDependencies: [],\n        types: [],\n        unlisted: [],\n        unresolved: [],\n      },\n    ],\n  };\n\n  assert.equal(exec('knip --reporter json', { cwd }).stdout, JSON.stringify(json));\n});\n"
  },
  {
    "path": "packages/knip/test/cli/cli-reporter-markdown.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { exec } from '../helpers/exec.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/module-resolution-non-std');\n\ntest('knip --reporter markdown', () => {\n  const markdown = `# Knip report\n\n## Unused files (1)\n\n| Name          | Location      | Severity |\n| :------------ | :------------ | :------- |\n| src/unused.ts | src/unused.ts | error    |\n\n## Unlisted dependencies (2)\n\n| Name            | Location           | Severity |\n| :-------------- | :----------------- | :------- |\n| @org/unresolved | src/index.ts:10:27 | error    |\n| unresolved      | src/index.ts:9:27  | error    |\n\n## Unresolved imports (1)\n\n| Name         | Location          | Severity |\n| :----------- | :---------------- | :------- |\n| ./unresolved | src/index.ts:8:24 | error    |`;\n  const out = exec('knip --reporter markdown', { cwd }).stdout;\n  assert.equal(out, markdown);\n});\n"
  },
  {
    "path": "packages/knip/test/cli/cli-reporter-symbols-catalog.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { test } from 'node:test';\nimport { exec } from '../helpers/exec.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/catalog-pnpm');\n\ntest('knip --reporter symbols (catalog)', () => {\n  const expected = `Unused catalog entries (1)\nlodash  default  pnpm-workspace.yaml:7:3`;\n\n  const result = exec('knip --reporter symbols', { cwd }).stdout;\n\n  assert.equal(result, expected);\n});\n"
  },
  {
    "path": "packages/knip/test/cli/cli-reporter-symbols-pathlike.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { test } from 'node:test';\nimport { exec } from '../helpers/exec.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/prettier');\n\ntest('knip --reporter symbols (path-like specifier)', () => {\n  const expected = `Unused devDependencies (1)\nprettier  package.json:5:6\nUnlisted dependencies (3)\n@company/prettier-config  package.json\nprettier-plugin-xml       prettier.config.js\nprettier-plugin-astro     prettier.config.js`;\n\n  const result = exec('knip --reporter symbols', { cwd }).stdout.replace(/ +$/gm, '');\n\n  assert.equal(result, expected);\n});\n"
  },
  {
    "path": "packages/knip/test/cli/cli-reporter.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { exec } from '../helpers/exec.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst skipIfBun = typeof Bun !== 'undefined' ? test.skip : test;\n\nconst cwd = resolve('fixtures/cli-reporter');\n\ntest('knip --reporter ./index.js', () => {\n  assert.equal(exec('knip --reporter ./index.js', { cwd }).stdout, 'hi from js reporter');\n});\n\ntest('knip --reporter ./index.ts', () => {\n  assert.equal(exec('knip --reporter ./index.ts', { cwd }).stdout, 'hi from ts reporter');\n});\n\nskipIfBun('knip --reporter knip-reporter', () => {\n  assert.equal(exec('knip --reporter knip-reporter', { cwd }).stdout, 'hi from pkg reporter');\n});\n\nskipIfBun('knip --reporter @org/reporter', () => {\n  assert.equal(exec('knip --reporter @org/reporter', { cwd }).stdout, 'hi from scoped reporter');\n});\n\ntest('knip --reporter {cwd}/index.js', () => {\n  assert.equal(exec(`knip --reporter ${cwd}/index.js`, { cwd }).stdout, 'hi from js reporter');\n});\n"
  },
  {
    "path": "packages/knip/test/cli/cli-trace.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { showDiff } from '../helpers/diff.ts';\nimport { exec } from '../helpers/exec.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/trace');\n\ntest('knip --trace', () => {\n  const actual = exec('knip --trace', { cwd }).stdout;\n  const expected = `require.ts:default\n└── (no imports found) ✗\nrequire.ts:resolve\n└── barrel.ts:reExportStar[resolve]\n    ├── module.ts:importNS[NS.resolve] ✓\n    │     refs: [NS.resolve]\n    └── shared.ts:reExport[resolve]\nshared.ts:CONTAINER\n└── module.ts:importAs[CONTAINER → ROOT] ✓\n      refs: [ROOT.NS, ROOT.NS.resolve]\nshared.ts:resolve\n└── (no imports found) ✗\nshared.ts:shorten\n└── (no imports found) ✗\nstring.ts:leftPad\n└── barrel.ts:reExportNS[STR.leftPad]\n    └── module.ts:importNS[NS.STR.leftPad] ✓\n          refs: [NS.STR, NS.STR.leftPad]\nstring.ts:truncate\n├── module.ts:import[truncate] ✓\n├── module.ts:importAs[truncate → trunc] ✓\n├── shared.ts:reExportAs[truncate → shorten]\n└── barrel.ts:reExportNS[STR.truncate]\n    └── module.ts:importNS[NS.STR.truncate] ✓\n          refs: [NS.STR, NS.STR.truncate]`;\n\n  if (actual !== expected) {\n    showDiff(actual, expected);\n    assert.fail('Output mismatch (see diff above)');\n  }\n});\n"
  },
  {
    "path": "packages/knip/test/cli/cli-treat-config-hints-as-errors.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { exec } from '../helpers/exec.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\ntest('knip --treat-config-hints-as-errors', () => {\n  const cwd = resolve('fixtures/treat-config-hints-as-errors');\n  const result = exec('knip --treat-config-hints-as-errors', { cwd });\n  assert.equal(result.stderr, 'pineapple    package.json  Remove from ignoreDependencies');\n  assert.equal(result.status, 1);\n});\n\ntest('knip (treatConfigHintsAsErrors: true)', () => {\n  const cwd = resolve('fixtures/treat-config-hints-as-errors2');\n  const result = exec('knip', { cwd });\n  assert.equal(result.stderr, 'bananas    package.json  Remove from ignoreDependencies');\n  assert.equal(result.status, 1);\n});\n\ntest('knip (production)', () => {\n  const cwd = resolve('fixtures/treat-config-hints-as-errors2');\n  const result = exec('knip --production', { cwd });\n  assert.equal(result.stderr, '');\n  assert.equal(result.status, 0);\n});\n"
  },
  {
    "path": "packages/knip/test/cli/cli.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { helpText } from '../../src/util/cli-arguments.ts';\nimport { loadJSON } from '../../src/util/fs.ts';\nimport { version } from '../../src/version.ts';\nimport { exec } from '../helpers/exec.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/cli');\n\ntest('knip --version', async () => {\n  assert.equal(exec('knip --version', { cwd }).stdout, version);\n  const contents = await loadJSON(resolve('package.json'));\n  assert.equal(version, contents.version);\n});\n\ntest('knip --help', () => {\n  assert.equal(exec('knip --help', { cwd }).stdout, helpText);\n});\n"
  },
  {
    "path": "packages/knip/test/commonjs-tsconfig.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/commonjs-tsconfig');\n\ntest('Support CommonJS-style imports and exports (w tsconfig.json)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['dir/exports.js']['unused']);\n  assert(issues.exports['dir/script2.js']['identifier']);\n  assert(issues.exports['dir/script2.js']['identifier2']);\n\n  assert(issues.exports['dir/module1.ts']['unused']);\n  assert(issues.exports['dir/module2.ts']['unused']);\n  assert(issues.exports['dir/module3.js']['unused']);\n\n  assert(issues.unlisted['dir/script1.js']['string-literal']);\n  assert(issues.unlisted['dir/script1.js']['another-unlisted']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 6,\n    unlisted: 2,\n    processed: 10,\n    total: 10,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/commonjs.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/commonjs');\n\ntest('Support CommonJS-style imports and exports (w/o tsconfig.json)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['dir/exports.js']['unused']);\n  assert(issues.exports['dir/mod1.js']['identifier2']);\n\n  assert(issues.unlisted['index.js']['side-effects']);\n  assert(issues.unlisted['index.js']['aliased-binding']);\n  assert(issues.unlisted['index.js']['default-identifier']);\n  assert(issues.unlisted['index.js']['named-object-binding']);\n  assert(issues.unlisted['index.js']['no-substitution-tpl-literal']);\n  assert(issues.unlisted['index.js']['string-literal-resolve']);\n  assert(issues.unlisted['dir/mod.js']['string-literal']);\n  assert(issues.unlisted['dir/mod.js']['another-unlisted']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 2,\n    unlisted: 8,\n    processed: 8,\n    total: 8,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/compilers.manual.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/compilers-manual');\n\ntest('Support manually enabled compiler', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 5,\n    total: 5,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/compilers.prisma.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/compilers-prisma');\n\ntest('Built-in compiler for Prisma schema', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['prisma-openapi']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/compilers.scss.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/compilers-scss');\n\ntest('Built-in compiler for SCSS', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert('unused.scss' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    files: 1,\n    processed: 14,\n    total: 14,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/compilers.tailwind.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/compilers-tailwind');\n\ntest('Built-in compiler for Tailwind CSS', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unresolved: 1,\n    processed: 6,\n    total: 6,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/compilers.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/compilers');\n\ntest('Support compiler functions in config', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert('unused.css' in issues.files);\n  assert('unused.md' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 2,\n    processed: 11,\n    total: 11,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/configuration-hints.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/configuration-hints');\n\ntest('Provide configuration hints', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters, configurationHints } = await main(options);\n\n  assert('src/entry.js' in issues.files);\n\n  assert.deepEqual(configurationHints, [\n    { type: 'entry-top-level', identifier: '[src/entry.js]' },\n    { type: 'project-top-level', identifier: '[src/**]' },\n  ]);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    processed: 2,\n    total: 2,\n  });\n});\n\ntest('Provide configuration hints for patterns covered by plugins and project redundancy', async () => {\n  const cwd = resolve('fixtures/configuration-hints-plugin');\n  const options = await createOptions({ cwd });\n  const { counters, configurationHints } = await main(options);\n\n  assert.deepEqual(configurationHints, [\n    { type: 'entry-redundant', identifier: 'create-typescript-app.config.js', workspaceName: '.' },\n    { type: 'entry-redundant', identifier: 'svgo.config.mjs', workspaceName: '.' },\n    { type: 'project-redundant', identifier: 'create-typescript-app.config.js', workspaceName: '.' },\n  ]);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    dependencies: 2,\n    processed: 2,\n    total: 2,\n  });\n});\n\ntest('No hints when user overrides plugin entry config', async () => {\n  const cwd = resolve('fixtures/configuration-hints-plugin-override');\n  const options = await createOptions({ cwd });\n  const { counters, configurationHints } = await main(options);\n\n  assert.deepEqual(configurationHints, [\n    { type: 'entry-redundant', identifier: 'svgo.config.js', workspaceName: '.' },\n    { type: 'entry-redundant', identifier: 'yarn.config.cjs', workspaceName: '.' },\n  ]);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    dependencies: 3,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/configuration-hints2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/configuration-hints2');\n\ntest('Provide configuration hints (2)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters, configurationHints } = await main(options);\n\n  assert.deepEqual(configurationHints, [\n    { type: 'entry-empty', identifier: 'lib/index.js', workspaceName: '.' },\n    { type: 'project-empty', identifier: 'lib/**', workspaceName: '.' },\n    { type: 'entry-top-level', identifier: '[src/entry.js, …]' },\n    { type: 'project-top-level', identifier: '[src/**]' },\n  ]);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/cross-workspace-inputs.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/cross-workspace-inputs');\n\ntest('Assign binaries, dependencies and configuration files to the correct workspace', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n\ntest('Assign binaries, dependencies and configuration files to the correct workspace (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { counters } = await main(options);\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/custom-paths-workspaces.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/custom-paths-workspaces');\n\ntest('Resolve custom path aliases (workspaces)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 7,\n    total: 7,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/definitely-typed.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/definitely-typed');\n\ntest('Find unused DT @types', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['@types/unused']);\n  assert(issues.devDependencies['package.json']['@types/mocha']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 2,\n    processed: 1,\n    total: 1,\n  });\n});\n\ntest('Find type imports in production dependencies (strict)', async () => {\n  const options = await createOptions({ cwd, isProduction: true, isStrict: true });\n  const { issues, counters } = await main(options);\n\n  assert(issues.dependencies['package.json']['type-only-production-types']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    dependencies: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/dependencies-types.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/dependencies-types');\n\ntest('Find unused @types dependencies', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['@types/ajv']);\n  assert(issues.devDependencies['package.json']['@types/eslint__js']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 2,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/dependencies.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/dependencies');\n\ntest('Find unused dependencies', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert('unused-module.ts' in issues.files);\n\n  assert.equal(Object.keys(issues.dependencies['package.json']).length, 2);\n  assert(issues.dependencies['package.json']['@tootallnate/once']);\n  assert(issues.dependencies['package.json']['fs-extra']);\n  assert(issues.devDependencies['package.json']['mocha']);\n\n  assert.equal(Object.keys(issues.binaries).length, 1);\n  assert(issues.binaries['package.json']['start-server']);\n  assert(issues.binaries['package.json']['jest']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    dependencies: 2,\n    devDependencies: 1,\n    binaries: 2,\n    processed: 3,\n    total: 3,\n  });\n});\n\ntest('Find unused dependencies (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { issues, counters } = await main(options);\n\n  assert('unused-module.ts' in issues.files);\n\n  assert.equal(Object.keys(issues.dependencies['package.json']).length, 2);\n  assert(issues.dependencies['package.json']['@tootallnate/once']);\n  assert(issues.dependencies['package.json']['fs-extra']);\n  assert.equal(Object.keys(issues.devDependencies).length, 0);\n\n  assert.equal(Object.keys(issues.binaries).length, 1);\n  assert(issues.binaries['package.json']['start-server']);\n  assert(!issues.binaries['package.json']['jest']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    dependencies: 2,\n    devDependencies: 0,\n    binaries: 1,\n    processed: 3,\n    total: 3,\n  });\n});\n\ntest('Find unused dependencies (strict)', async () => {\n  const options = await createOptions({ cwd, isProduction: true, isStrict: true });\n  const { issues, counters } = await main(options);\n\n  assert('unused-module.ts' in issues.files);\n\n  assert.equal(Object.keys(issues.dependencies['package.json']).length, 3);\n  assert(issues.dependencies['package.json']['@tootallnate/once']);\n  assert(issues.dependencies['package.json']['fs-extra']);\n  assert(issues.dependencies['package.json']['jquery']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    dependencies: 3,\n    binaries: 1,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/dts-baseurl-implicit-relative.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/dts-baseurl-implicit-relative');\n\ntest('Include js files referred by the declaration files', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 11,\n    total: 11,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/dts-compiled.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/dts-compiled');\n\ntest('Include compiled files referred by the declaration files', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert('src/UnusedQuery.graphql' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    processed: 8,\n    total: 8,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/dts.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/dts');\n\ntest('Include declaration files and allow unknown extensions', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    namespaceMembers: 1,\n    processed: 6,\n    total: 6,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/duplicate-dependencies.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/duplicate-dependencies');\n\ntest('Report duplicate package.json entries (listed in both dependencies and devDependencies)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['typescript']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/duplicate-exports-alias.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/duplicate-exports-alias');\n\ntest('Ignore duplicate exports with @alias (JSDoc)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.duplicates['helpers.ts']['isUntagged|isUntaggedAlias']);\n  assert(!issues.duplicates['helpers.ts']['reExportedValue|reExportedAlias']);\n  assert(!issues.duplicates['specifier-default.ts'], 'export { X }; export default X should not be duplicate');\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 5,\n    duplicates: 1,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/empty-main.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/empty-main');\n\ntest('Empty main, module, types, bin fields in package.json should not crash', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/entry-exports-enum-members.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/entry-exports-enum-members');\n\ntest('Find unused exports, types and enum members re-exported in entry file', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n\ntest('Find unused exports, types and enum members re-exported in entry file (2)', async () => {\n  const options = await createOptions({ cwd, isIncludeEntryExports: true });\n  const { issues, counters } = await main(options);\n\n  assert(issues.types['fruit.ts'].Farmer);\n  assert(issues.exports['index.ts'].Farmer);\n  assert(issues.exports['index.ts'].Tree);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 2,\n    types: 1,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/entry-exports-namespace.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/entry-exports-namespace');\n\ntest('Find unused member on namespace re-exported in entry file', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n\ntest('Find unused member on namespace re-exported in entry file (2)', async () => {\n  const options = await createOptions({ cwd, isIncludeEntryExports: true });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['index.ts'].NS);\n  assert(issues.exports['ns.ts'].y);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 2,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/entry-files.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/entry-files');\n\ntest('Use main, bin and exports fields in package.json to find entry files', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 7,\n    total: 7,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/entry-js.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/entry-js');\n\ntest('Find unused files and exports with JS entry file', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert.equal(Object.keys(issues.files).length, 1);\n  assert('dangling.js' in issues.files);\n\n  assert.equal(Object.values(issues.exports).length, 2);\n  assert.equal(issues.exports['my-module.ts']['unused'].symbol, 'unused');\n  assert.equal(issues.exports['my-module.ts']['default'].symbol, 'default');\n  assert.equal(issues.exports['my-namespace.ts']['MyNamespace.key'].symbol, 'key');\n\n  assert.equal(Object.values(issues.types).length, 2);\n  assert.equal(issues.types['my-module.ts']['AnyType'].symbolType, 'type');\n  assert.equal(issues.types['my-namespace.ts']['MyNamespace.MyNamespace'].symbol, 'MyNamespace');\n\n  assert.equal(Object.values(issues.duplicates).length, 1);\n  assert.equal(issues.duplicates['my-module.ts']['myExport|default'].symbols?.length, 2);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    unlisted: 0,\n    exports: 3,\n    types: 2,\n    duplicates: 1,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/enum-members-enumerated.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/enum-members-enumerated');\n\ntest('Consider enum enumerated enum members used', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/enum-members.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/enum-members');\n\ntest('Find unused enum members', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert.equal(Object.keys(issues.enumMembers['members.ts']).length, 2);\n  assert(issues.enumMembers['members.ts']['MyEnum.B_Unused']);\n  assert(issues.enumMembers['members.ts']['MyEnum.D-Key']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    enumMembers: 2,\n    processed: 2,\n    total: 2,\n  });\n});\n\ntest('Find unused enum members (isIncludeEntryExports)', async () => {\n  const options = await createOptions({ cwd, isIncludeEntryExports: true });\n  const { issues, counters } = await main(options);\n\n  assert.equal(Object.keys(issues.enumMembers['members.ts']).length, 2);\n  assert(issues.enumMembers['members.ts']['MyEnum.B_Unused']);\n  assert(issues.enumMembers['members.ts']['MyEnum.D-Key']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    enumMembers: 2,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/export-spread.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/export-spread');\n\ntest('Export named spread identifiers for object/array', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/exports-default-interface.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/exports-default-interface');\n\ntest('Find unused default-exported interfaces', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/exports-default-type.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/export-default-type');\n\ntest('Report type of referenced default exports', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert.equal(issues.exports['class.js']['default'].symbolType, 'class');\n  assert.equal(issues.exports['function.js']['default'].symbolType, 'function');\n  assert.equal(issues.exports['var.js']['default'].symbolType, 'variable');\n  assert.equal(issues.exports['let.js']['default'].symbolType, 'variable');\n  assert.equal(issues.exports['const.js']['default'].symbolType, 'variable');\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 5,\n    processed: 6,\n    total: 6,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/exports-special-characters.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/exports-special-characters');\n\ntest('Handle special characters in named exports and members', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['exports.ts']['$dollar']);\n  assert(issues.exports['exports.ts']['dollar$']);\n  assert(issues.exports['exports.ts']['_underscore']);\n  assert(issues.exports['exports.ts']['__underscores']);\n  assert(issues.exports['exports.ts']['$Dollar']);\n\n  assert(issues.types['exports.ts']['$DollarType']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 5,\n    types: 1,\n    processed: 2,\n    total: 2,\n  });\n});\n\ntest('Handle special characters in named exports and members (nsTypes)', async () => {\n  const options = await createOptions({ cwd, includedIssueTypes: ['nsTypes'] });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['exports.ts']['$dollar']);\n  assert(issues.exports['exports.ts']['dollar$']);\n  assert(issues.exports['exports.ts']['_underscore']);\n  assert(issues.exports['exports.ts']['__underscores']);\n  assert(issues.exports['exports.ts']['$Dollar']);\n\n  assert(issues.types['exports.ts']['$DollarType']);\n\n  assert(issues.enumMembers['exports.ts']['Characters.-']);\n  assert(issues.enumMembers['exports.ts']['Characters.,']);\n  assert(issues.enumMembers['exports.ts']['Characters.:']);\n  assert(issues.enumMembers['exports.ts']['Characters.?']);\n  assert(issues.enumMembers['exports.ts']['Characters..']);\n  assert(issues.enumMembers['exports.ts']['Characters.(']);\n  assert(issues.enumMembers['exports.ts']['Characters.)']);\n  assert(issues.enumMembers['exports.ts']['Characters.[']);\n  assert(issues.enumMembers['exports.ts']['Characters.]']);\n  assert(issues.enumMembers['exports.ts']['Characters.{']);\n  assert(issues.enumMembers['exports.ts']['Characters.}']);\n  assert(issues.enumMembers['exports.ts']['Characters.@']);\n  assert(issues.enumMembers['exports.ts']['Characters.*']);\n  assert(issues.enumMembers['exports.ts']['Characters./']);\n  assert(issues.enumMembers['exports.ts']['Characters.\\\\\\\\']);\n  assert(issues.enumMembers['exports.ts']['Characters.+']);\n  assert(issues.enumMembers['exports.ts']['Characters.|']);\n  assert(issues.enumMembers['exports.ts']['Characters.$']);\n  assert(issues.enumMembers['exports.ts']['Characters.Slash']);\n  assert(issues.enumMembers['exports.ts']['Characters.Space']);\n  assert(issues.enumMembers['exports.ts']['Characters. ']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    enumMembers: 21,\n    exports: 5,\n    types: 1,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/exports-value-refs-default.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/exports-value-refs-default');\n\ntest('Find unused exports in exported types (default)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['refs.ts']['logger']);\n  assert(issues.exports['refs.ts']['UnusedClass']);\n  assert(issues.types['refs.ts']['UnusedTypeInUnusedExport']);\n  assert(issues.types['refs.ts']['UnusedInterface']);\n  assert(issues.types['refs.ts']['UnusedTypeWithClass']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 2,\n    types: 3,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/exports-value-refs.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/exports-value-refs');\n\ntest('Find unused exports in exported types', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['refs.ts']['NotInExportedType']);\n  assert(issues.exports['refs.ts']['myValue']);\n  assert(issues.exports['refs.ts']['myResult']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 3,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/exports.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/exports');\n\ntest('Find unused exports', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert.equal(Object.values(issues.exports).length, 6);\n  assert.equal(issues.exports['default.ts']['NamedExport'].symbol, 'NamedExport');\n  assert.equal(issues.exports['my-module.ts']['default'].symbol, 'default');\n  assert.equal(issues.exports['my-module.ts']['unusedNumber'].symbol, 'unusedNumber');\n  assert.equal(issues.exports['my-module.ts']['unusedFunction'].symbol, 'unusedFunction');\n  assert.equal(issues.exports['my-mix.ts']['unusedInMix'].symbol, 'unusedInMix');\n  assert.equal(issues.exports['named-exports.ts']['renamedExport'].symbol, 'renamedExport');\n  assert.equal(issues.exports['named-exports.ts']['namedExport'].symbol, 'namedExport');\n  assert.equal(issues.exports['dynamic-import.ts']['unusedZero'].symbol, 'unusedZero');\n  assert.equal(issues.exports['my-namespace.ts']['MyNamespace.nsUnusedKey'].line, 3);\n  assert.equal(issues.exports['my-namespace.ts']['MyNamespace.nsUnusedKey'].col, 14);\n  assert.equal(issues.exports['my-namespace.ts']['MyNamespace.nsUnusedKey'].symbol, 'nsUnusedKey');\n  assert(!issues.exports['index.ts']);\n\n  assert.equal(Object.values(issues.types).length, 3);\n  assert.equal(issues.types['my-module.ts']['MyAnyType'].symbolType, 'type');\n  assert.equal(issues.types['types.ts']['MyEnum'].symbolType, 'enum');\n  assert.equal(issues.types['types.ts']['MyType'].symbolType, 'type');\n  assert.equal(issues.types['my-namespace.ts']['MyNamespace.MyNamespace'].symbol, 'MyNamespace');\n  assert(!issues.types['index.ts']);\n\n  assert.equal(Object.values(issues.duplicates).length, 1);\n  assert.equal(issues.duplicates['my-module.ts']['exportedResult|default'].symbols?.length, 2);\n\n  assert.equal(issues.exports['default.ts']['NamedExport'].line, 1);\n  assert.equal(issues.exports['default.ts']['NamedExport'].col, 14);\n\n  assert.equal(issues.types['my-module.ts']['MyAnyType'].line, 28);\n  assert.equal(issues.types['my-module.ts']['MyAnyType'].col, 13);\n  assert.equal(issues.types['my-namespace.ts']['MyNamespace.MyNamespace'].line, 5);\n  assert.equal(issues.types['my-namespace.ts']['MyNamespace.MyNamespace'].col, 18);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 9,\n    types: 4,\n    duplicates: 1,\n    processed: 17,\n    total: 17,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/extensions-css-ts.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/extensions-css-ts');\n\ntest('Resolve files with extensions not in registered compiler extensions', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 7,\n    total: 7,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/fix/fix-catalog-json-root.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { readFile } from 'node:fs/promises';\nimport { test } from 'node:test';\nimport { main } from '../../src/index.ts';\nimport { join } from '../../src/util/path.ts';\nimport { copyFixture } from '../helpers/copy-fixture.ts';\nimport { createOptions } from '../helpers/create-options.ts';\n\ntest('Fix catalog entries', async () => {\n  const cwd = await copyFixture('fixtures/catalog-named-package-json-root');\n  const options = await createOptions({ cwd, isFix: true });\n  const { issues } = await main(options);\n\n  assert(issues.catalog['package.json']['default.lodash']);\n  assert(issues.catalog['package.json']['frontend.@nu/xt']);\n  assert(issues.catalog['package.json']['backend.fastify']);\n\n  assert.equal(\n    await readFile(join(cwd, 'package.json'), 'utf8'),\n    `{\n  \"name\": \"@fixtures/catalog-named-package-json-root\",\n  \"private\": true,\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"dependencies\": {\n    \"react\": \"catalog:\",\n    \"vue\": \"catalog:frontend\",\n    \"express\": \"catalog:backend\"\n  },\n  \"catalog\": {\n    \"react\": \"^18.0.0\"\n  },\n  \"catalogs\": {\n    \"frontend\": {\n      \"vue\": \"^3.0.0\"\n    },\n    \"backend\": {\n      \"express\": \"^4.18.0\"\n    }\n  }\n}\n`\n  );\n});\n"
  },
  {
    "path": "packages/knip/test/fix/fix-catalog-json.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { readFile } from 'node:fs/promises';\nimport { test } from 'node:test';\nimport { main } from '../../src/index.ts';\nimport { join } from '../../src/util/path.ts';\nimport { copyFixture } from '../helpers/copy-fixture.ts';\nimport { createOptions } from '../helpers/create-options.ts';\n\ntest('Fix catalog entries (package.json)', async () => {\n  const cwd = await copyFixture('fixtures/catalog-named-package-json');\n  const options = await createOptions({ cwd, isFix: true });\n  const { issues } = await main(options);\n\n  assert(issues.catalog['package.json']['default.lodash']);\n  assert(issues.catalog['package.json']['frontend.nuxt']);\n  assert(issues.catalog['package.json']['backend.fastify']);\n\n  assert.equal(\n    await readFile(join(cwd, 'package.json'), 'utf8'),\n    `{\n  \"name\": \"@fixtures/catalog-named-package-json\",\n  \"private\": true,\n  \"dependencies\": {\n    \"react\": \"catalog:\",\n    \"vue\": \"catalog:frontend\",\n    \"express\": \"catalog:backend\"\n  },\n  \"workspaces\": {\n    \"packages\": [\n      \"packages/*\"\n    ],\n    \"catalog\": {\n      \"react\": \"^18.0.0\"\n    },\n    \"catalogs\": {\n      \"frontend\": {\n        \"vue\": \"^3.0.0\"\n      },\n      \"backend\": {\n        \"express\": \"^4.18.0\"\n      }\n    }\n  }\n}\n`\n  );\n});\n"
  },
  {
    "path": "packages/knip/test/fix/fix-catalog-yaml.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { readFile } from 'node:fs/promises';\nimport { test } from 'node:test';\nimport { main } from '../../src/index.ts';\nimport { join } from '../../src/util/path.ts';\nimport { copyFixture } from '../helpers/copy-fixture.ts';\nimport { createOptions } from '../helpers/create-options.ts';\n\ntest('Fix catalog entries (pnpm-workspace.yaml)', async () => {\n  const cwd = await copyFixture('fixtures/catalog-named');\n  const options = await createOptions({ cwd, isFix: true });\n  const { issues } = await main(options);\n\n  assert(issues.catalog['pnpm-workspace.yaml']['default.lodash']);\n  assert(issues.catalog['pnpm-workspace.yaml']['frontend.@nu/xt']);\n  assert(issues.catalog['pnpm-workspace.yaml']['backend.fastify']);\n\n  assert.equal(\n    await readFile(join(cwd, 'pnpm-workspace.yaml'), 'utf8'),\n    `packages:\n  - 'packages/*'\n\ncatalog:\n  react: ^18.0.0\n\ncatalogs:\n  frontend:\n    vue: ^3.0.0\n  backend:\n    '@ex/press': ^4.18.0\n`\n  );\n});\n\ntest('Fix catalog entries (.yarnrc.yml)', async () => {\n  const cwd = await copyFixture('fixtures/catalog-yarn');\n  const options = await createOptions({ cwd, isFix: true });\n  const { issues } = await main(options);\n\n  assert(issues.catalog['.yarnrc.yml']['default.@lo/dash']);\n\n  assert.equal(\n    await readFile(join(cwd, '.yarnrc.yml'), 'utf8'),\n    `packages:\n  - \"packages/*\"\n\ncatalog:\n  solid-js: 1.9.10\n  typescript: ^5.0.0\n`\n  );\n});\n"
  },
  {
    "path": "packages/knip/test/fix/fix-exclude-dependencies.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { readFile } from 'node:fs/promises';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport { join } from '../../src/util/path.ts';\nimport { copyFixture } from '../helpers/copy-fixture.ts';\nimport { createOptions } from '../helpers/create-options.ts';\n\ntest('Fix should not remove dependencies when issue type is excluded', async () => {\n  const cwd = await copyFixture('fixtures/fix');\n  const pkgPath = join(cwd, 'package.json');\n  const original = await readFile(pkgPath, 'utf8');\n\n  const options = await createOptions({ cwd, isFix: true, excludedIssueTypes: ['dependencies'] });\n  const { issues, counters } = await main(options);\n\n  assert.equal(counters.dependencies, 0);\n  assert.equal(counters.devDependencies, 0);\n  assert.deepEqual(issues.dependencies, {});\n  assert.deepEqual(issues.devDependencies, {});\n\n  assert.equal(original, await readFile(pkgPath, 'utf8'));\n});\n"
  },
  {
    "path": "packages/knip/test/fix/fix-members.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { readFile } from 'node:fs/promises';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport { join } from '../../src/util/path.ts';\nimport { copyFixture } from '../helpers/copy-fixture.ts';\nimport { createOptions } from '../helpers/create-options.ts';\n\ntest('Fix enum and namespace members', async () => {\n  const cwd = await copyFixture('fixtures/fix-members');\n  const options = await createOptions({ cwd, isFix: true });\n  const { issues } = await main(options);\n\n  assert(issues.enumMembers['enums.ts']['Fruits.orange']);\n  assert(issues.enumMembers['enums.ts']['Directions.North']);\n  assert(issues.enumMembers['enums.ts']['Directions.South']);\n  assert(issues.enumMembers['enums.ts']['Directions.West']);\n  assert.equal(\n    await readFile(join(cwd, 'enums.ts'), 'utf8'),\n    `export enum Directions {\n  East = 2,\n  }\n\nexport enum Fruits {\n  apple = 'apple',\n  }\n`\n  );\n\n  assert(issues.namespaceMembers['namespaces.ts']['Animals.unusedDog']);\n  assert(issues.namespaceMembers['namespaces.ts']['Animals.Birds.unusedParrot']);\n  assert.equal(\n    await readFile(join(cwd, 'namespaces.ts'), 'utf8'),\n    `export namespace Animals {\n  export const cat = 'cat';\n  export function swim() {}\n\n  export namespace Birds {\n    export const eagle = 'eagle';\n    }\n}\n`\n  );\n});\n"
  },
  {
    "path": "packages/knip/test/fix/fix-workspaces.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { readFile } from 'node:fs/promises';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport { join } from '../../src/util/path.ts';\nimport { copyFixture } from '../helpers/copy-fixture.ts';\nimport { createOptions } from '../helpers/create-options.ts';\n\ntest('Fix exports and dependencies in workspaces', async () => {\n  const cwd = await copyFixture('fixtures/fix-workspaces');\n  const options = await createOptions({ cwd, tags: ['-lintignore'], isFix: true });\n  const { issues } = await main(options);\n\n  assert(issues.exports['exports.ts']['d']);\n  assert(issues.exports['packages/lib/exports.ts']['d']);\n  assert(issues.types['exports.ts']['T']);\n  assert(issues.types['packages/lib/exports.ts']['T']);\n  assert(issues.dependencies['package.json']['unused']);\n  assert(issues.dependencies['packages/lib/package.json']['unused']);\n\n  assert(issues.exports['ignored.ts'] === undefined);\n  assert(issues.exports['packages/lib/ignored.ts'] === undefined);\n  assert(issues.dependencies['package.json']['ignored'] === undefined);\n  assert(issues.dependencies['packages/lib/package.json']['ignored'] === undefined);\n  assert(issues.exports['packages/ignored/package.json'] === undefined);\n  assert(issues.exports['packages/ignored/exports.ts'] === undefined);\n  assert(issues.types['exports.ts']['U'] === undefined);\n  assert(issues.types['packages/lib/exports.ts']['U'] === undefined);\n\n  assert.equal(\n    await readFile(join(cwd, 'exports.ts'), 'utf8'),\n    `export const c = 1;\nconst d = 2;\n\ntype T = number;\n\n/** @lintignore */\nexport type U = number;\n`\n  );\n\n  assert.equal(\n    await readFile(join(cwd, 'package.json'), 'utf8'),\n    `{\n  \"name\": \"@fixtures/fix-workspaces\",\n  \"dependencies\": {\n    \"ignored\": \"*\"\n  },\n  \"workspaces\": [\n    \"packages/*\"\n  ]\n}\n`\n  );\n\n  assert.equal(\n    await readFile(join(cwd, 'packages/lib/exports.ts'), 'utf8'),\n    `export const c = 1;\nconst d = 2;\n\ntype T = number;\n\n/** @lintignore */\nexport type U = number;\n`\n  );\n\n  assert.equal(\n    await readFile(join(cwd, 'packages/lib/package.json'), 'utf8'),\n    `{\n  \"name\": \"@fixtures/fix-workspaces__lib\",\n  \"dependencies\": {\n    \"ignored\": \"*\"\n  }\n}\n`\n  );\n});\n\ntest('Fix exported types in workspaces', async () => {\n  const cwd = await copyFixture('fixtures/fix-workspaces');\n  const options = await createOptions({ cwd, tags: ['-lintignore'], isFix: true, fixTypes: ['types'] });\n  const { issues } = await main(options);\n\n  assert(issues.exports['exports.ts']['d']);\n  assert(issues.exports['packages/lib/exports.ts']['d']);\n  assert(issues.types['exports.ts']['T']);\n  assert(issues.types['packages/lib/exports.ts']['T']);\n  assert(issues.dependencies['package.json']['unused']);\n  assert(issues.dependencies['packages/lib/package.json']['unused']);\n\n  assert(issues.exports['ignored.ts'] === undefined);\n  assert(issues.exports['packages/lib/ignored.ts'] === undefined);\n  assert(issues.dependencies['package.json']['ignored'] === undefined);\n  assert(issues.dependencies['packages/lib/package.json']['ignored'] === undefined);\n  assert(issues.exports['packages/ignored/package.json'] === undefined);\n  assert(issues.exports['packages/ignored/exports.ts'] === undefined);\n  assert(issues.types['exports.ts']['U'] === undefined);\n  assert(issues.types['packages/lib/exports.ts']['U'] === undefined);\n\n  assert.equal(\n    await readFile(join(cwd, 'exports.ts'), 'utf8'),\n    `export const c = 1;\nexport const d = 2;\n\ntype T = number;\n\n/** @lintignore */\nexport type U = number;\n`\n  );\n\n  assert.equal(\n    await readFile(join(cwd, 'packages/lib/exports.ts'), 'utf8'),\n    `export const c = 1;\nexport const d = 2;\n\ntype T = number;\n\n/** @lintignore */\nexport type U = number;\n`\n  );\n});\n"
  },
  {
    "path": "packages/knip/test/fix/fix.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { readFile } from 'node:fs/promises';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport { join } from '../../src/util/path.ts';\nimport { copyFixture } from '../helpers/copy-fixture.ts';\nimport { createOptions } from '../helpers/create-options.ts';\n\ntest('Fix exports and dependencies', async () => {\n  const cwd = await copyFixture('fixtures/fix');\n  const options = await createOptions({ cwd, tags: ['-lintignore'], isFix: true });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['access.js']['UNUSED']);\n  assert(issues.exports['access.js']['ACCESS']);\n  assert(issues.exports['exports.js']['identifier2']);\n  assert(issues.exports['mod.ts']['a']);\n  assert(issues.exports['mod.ts']['b']);\n  assert(issues.exports['mod.ts']['c']);\n  assert(issues.exports['mod.ts']['d']);\n  assert(issues.exports['mod.ts']['default']);\n  assert(issues.exports['mod.ts']['x']);\n  assert(issues.exports['mod.ts']['y']);\n  assert(issues.exports['reexported.ts']['Three']);\n  assert(issues.exports['reexported.ts']['Two']);\n\n  assert(issues.exports['ignored.ts'] === undefined);\n  assert(issues.dependencies['package.json']['ignored'] === undefined);\n  assert(issues.types['mod.ts']['U'] === undefined);\n\n  assert.equal(\n    await readFile(join(cwd, 'mod.ts'), 'utf8'),\n    `const x = 1;\nconst y = 2;\n\ninterface McInterFace {}\ntype McType = {};\nenum McEnum {}\n\nexport const z = x + y;\n\nexport const {   } = { a: 1, b: 1 };\n\nexport const [, ] = [3, 4];\n\nexport const [, f] = [5, 6];\n\nexport const [g, , i] = [7, 8, 9];\n\nclass MyClass {}\n\n/** @lintignore */\nexport type U = number;\n`\n  );\n\n  assert.equal(\n    await readFile(join(cwd, 'access.js'), 'utf8'),\n    `module.exports.USED = 1;\n\n\n`\n  );\n\n  assert.equal(\n    await readFile(join(cwd, 'default-x.mjs'), 'utf8'),\n    `const x = 1;\n\n\nexport const dx = 1;\n`\n  );\n\n  assert.equal(await readFile(join(cwd, 'default.mjs'), 'utf8'), `export const d = 1;\\n`);\n\n  assert.equal(\n    await readFile(join(cwd, 'exports.js'), 'utf8'),\n    `const identifier = 1;\nconst identifier2 = 2;\n\nmodule.exports = { identifier,  };\n`\n  );\n\n  assert.equal(\n    await readFile(join(cwd, 'reexports.mjs'), 'utf8'),\n    `;\n;\nexport { One, Rectangle,    Nine,   setter } from './reexported';\n;\n;\n`\n  );\n\n  assert.equal(\n    await readFile(join(cwd, 'reexported.ts'), 'utf8'),\n    `const Two = 2;\nconst Three = 3;\nconst Four = 4;\nconst Five = 5;\n\n;\n\n;\n\nexport { Four as Rectangle,  };\n\ntype Six = any;\ntype Seven = unknown;\nconst Eight = 8;\nconst Nine = 9;\ntype Ten = unknown[];\n\n;\n\nexport {   Nine,  };\n\nexport const One = 1;\n\nconst fn = () => ({ get: () => 1, set: () => 1 });\n\nexport const {  set: setter } = fn();\n`\n  );\n\n  assert.equal(\n    await readFile(join(cwd, 'package.json'), 'utf8'),\n    `{\n  \"name\": \"@fixtures/fix\",\n  \"dependencies\": {\n    \"lodash\": \"*\",\n    \"ignored\": \"*\"\n  },\n  \"devDependencies\": {}\n}\n`\n  );\n\n  assert.equal(counters.exports, 0);\n  assert.equal(counters.types, 0);\n  assert.equal(counters.dependencies, 0);\n  assert.equal(counters.devDependencies, 0);\n});\n\ntest('Fix only exported types', async () => {\n  const cwd = await copyFixture('fixtures/fix');\n  const options = await createOptions({ cwd, tags: ['-lintignore'], isFix: true, fixTypes: ['types'] });\n  const { issues } = await main(options);\n\n  assert(issues.exports['access.js']['ACCESS']);\n  assert(issues.exports['access.js']['UNUSED']);\n  assert(issues.exports['exports.js']['identifier2']);\n  assert(issues.exports['mod.ts']['a']);\n  assert(issues.exports['mod.ts']['b']);\n  assert(issues.exports['mod.ts']['default']);\n  assert(issues.exports['mod.ts']['x']);\n  assert(issues.exports['mod.ts']['y']);\n\n  assert(issues.exports['ignored.ts'] === undefined);\n  assert(issues.dependencies['package.json']['ignored'] === undefined);\n  assert(issues.types['mod.ts']['U'] === undefined);\n\n  assert.equal(\n    await readFile(join(cwd, 'mod.ts'), 'utf8'),\n    `export const x = 1;\nexport const y = 2;\n\ninterface McInterFace {}\ntype McType = {};\nenum McEnum {}\n\nexport const z = x + y;\n\nexport const { a, b } = { a: 1, b: 1 };\n\nexport const [c, d] = [3, 4];\n\nexport const [e, f] = [5, 6];\n\nexport const [g, h, i] = [7, 8, 9];\n\nexport default class MyClass {}\n\n/** @lintignore */\nexport type U = number;\n`\n  );\n\n  assert.equal(\n    await readFile(join(cwd, 'reexported.ts'), 'utf8'),\n    `const Two = 2;\nconst Three = 3;\nconst Four = 4;\nconst Five = 5;\n\nexport { Two, Three };\n\nexport { Four as Fourth, Five as Fifth };\n\nexport { Four as Rectangle, Five as Pentagon };\n\ntype Six = any;\ntype Seven = unknown;\nconst Eight = 8;\nconst Nine = 9;\ntype Ten = unknown[];\n\n;\n\nexport {  Eight, Nine,  };\n\nexport const One = 1;\n\nconst fn = () => ({ get: () => 1, set: () => 1 });\n\nexport const { get: getter, set: setter } = fn();\n`\n  );\n});\n"
  },
  {
    "path": "packages/knip/test/fix/format.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { readFile } from 'node:fs/promises';\nimport os from 'node:os';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport { join } from '../../src/util/path.ts';\nimport { copyFixture } from '../helpers/copy-fixture.ts';\nimport { createOptions } from '../helpers/create-options.ts';\n\nconst skipIfBunWin = typeof Bun !== 'undefined' && os.platform() === 'win32' ? test.skip : test;\n\nskipIfBunWin('Fix and format exports and dependencies', async () => {\n  const cwd = await copyFixture('fixtures/fix');\n  const expected: Record<string, string> = {\n    'mod.ts': `const x = 1;\nconst y = 2;\n\ninterface McInterFace {}\ntype McType = {};\nenum McEnum {}\n\nexport const z = x + y;\n\nexport const {} = { a: 1, b: 1 };\n\nexport const [,] = [3, 4];\n\nexport const [, f] = [5, 6];\n\nexport const [g, , i] = [7, 8, 9];\n\nclass MyClass {}\n\n/** @lintignore */\nexport type U = number;\n`,\n    'access.js': `module.exports.USED = 1;\n`,\n    'default-x.mjs': `const x = 1;\n\nexport const dx = 1;\n`,\n    'default.mjs': `export const d = 1;\n`,\n    'exports.js': `const identifier = 1;\nconst identifier2 = 2;\n\nmodule.exports = { identifier };\n`,\n    'reexports.mjs': `export { One, Rectangle, Nine, setter } from './reexported';\n`,\n    'reexported.ts': `const Two = 2;\nconst Three = 3;\nconst Four = 4;\nconst Five = 5;\n\nexport { Four as Rectangle };\n\ntype Six = any;\ntype Seven = unknown;\nconst Eight = 8;\nconst Nine = 9;\ntype Ten = unknown[];\n\nexport { Nine };\n\nexport const One = 1;\n\nconst fn = () => ({ get: () => 1, set: () => 1 });\n\nexport const { set: setter } = fn();\n`,\n    'package.json': `{\n  \"name\": \"@fixtures/fix\",\n  \"dependencies\": {\n    \"lodash\": \"*\",\n    \"ignored\": \"*\"\n  },\n  \"devDependencies\": {}\n}\n`,\n  };\n\n  const options = await createOptions({ cwd, isFix: true, isFormat: true, tags: ['-lintignore'] });\n  const { issues } = await main(options);\n\n  assert(issues.exports['access.js']['UNUSED']);\n  assert(issues.exports['access.js']['ACCESS']);\n  assert(issues.exports['exports.js']['identifier2']);\n  assert(issues.exports['mod.ts']['a']);\n  assert(issues.exports['mod.ts']['b']);\n  assert(issues.exports['mod.ts']['c']);\n  assert(issues.exports['mod.ts']['d']);\n  assert(issues.exports['mod.ts']['default']);\n  assert(issues.exports['mod.ts']['x']);\n  assert(issues.exports['mod.ts']['y']);\n  assert(issues.exports['reexported.ts']['Three']);\n  assert(issues.exports['reexported.ts']['Two']);\n\n  // check ignore\n  assert(issues.exports['ignored.ts'] === undefined);\n\n  // check ignoreDependencies\n  assert(issues.dependencies['package.json']['ignored'] === undefined);\n\n  // check ignored by tags\n  assert(issues.types['mod.ts']['U'] === undefined);\n\n  for (const [fileName, after] of Object.entries(expected)) {\n    const filePath = join(cwd, fileName);\n    const actual = await readFile(filePath, 'utf8');\n    assert.equal(actual, after);\n  }\n});\n\nskipIfBunWin('Fix and format only exported types', async () => {\n  const cwd = await copyFixture('fixtures/fix');\n  const expected: Record<string, string> = {\n    'mod.ts': `export const x = 1;\nexport const y = 2;\n\ninterface McInterFace {}\ntype McType = {};\nenum McEnum {}\n\nexport const z = x + y;\n\nexport const { a, b } = { a: 1, b: 1 };\n\nexport const [c, d] = [3, 4];\n\nexport const [e, f] = [5, 6];\n\nexport const [g, h, i] = [7, 8, 9];\n\nexport default class MyClass {}\n\n/** @lintignore */\nexport type U = number;\n`,\n    'reexported.ts': `const Two = 2;\nconst Three = 3;\nconst Four = 4;\nconst Five = 5;\n\nexport { Two, Three };\n\nexport { Four as Fourth, Five as Fifth };\n\nexport { Four as Rectangle, Five as Pentagon };\n\ntype Six = any;\ntype Seven = unknown;\nconst Eight = 8;\nconst Nine = 9;\ntype Ten = unknown[];\n\nexport { Eight, Nine };\n\nexport const One = 1;\n\nconst fn = () => ({ get: () => 1, set: () => 1 });\n\nexport const { get: getter, set: setter } = fn();\n`,\n  };\n\n  const options = await createOptions({\n    cwd,\n    isFix: true,\n    isFormat: true,\n    fixTypes: ['types'],\n    tags: ['-lintignore'],\n  });\n  const { issues } = await main(options);\n\n  assert(issues.exports['access.js']['ACCESS']);\n  assert(issues.exports['access.js']['UNUSED']);\n  assert(issues.exports['exports.js']['identifier2']);\n  assert(issues.exports['mod.ts']['a']);\n  assert(issues.exports['mod.ts']['b']);\n  assert(issues.exports['mod.ts']['default']);\n  assert(issues.exports['mod.ts']['x']);\n  assert(issues.exports['mod.ts']['y']);\n\n  // check ignore\n  assert(issues.exports['ignored.ts'] === undefined);\n\n  // check ignoreDependencies\n  assert(issues.dependencies['package.json']['ignored'] === undefined);\n\n  // check ignored by tags\n  assert(issues.types['mod.ts']['U'] === undefined);\n\n  for (const [fileName, after] of Object.entries(expected)) {\n    const filePath = join(cwd, fileName);\n    const actual = await readFile(filePath, 'utf8');\n    assert.equal(actual, after);\n  }\n});\n"
  },
  {
    "path": "packages/knip/test/git-branch-file.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { mkdirSync, rmSync, writeFileSync } from 'node:fs';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/git-branch-file');\nconst gitLogsDir = resolve('fixtures/git-branch-file/.git/logs/refs/heads/don');\n\ntest('Ignore files in .git directory (branch names ending in .ts)', async () => {\n  mkdirSync(gitLogsDir, { recursive: true });\n  writeFileSync(resolve('fixtures/git-branch-file/.git/logs/refs/heads/don/feature.ts'), '');\n\n  try {\n    const options = await createOptions({ cwd });\n    const { issues } = await main(options);\n\n    assert.equal(Object.keys(issues.files).length, 0);\n  } finally {\n    rmSync(resolve('fixtures/git-branch-file/.git'), { recursive: true, force: true });\n  }\n});\n"
  },
  {
    "path": "packages/knip/test/gitignore.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/gitignore');\n\ntest('Obey gitignore', async () => {\n  const options = await createOptions({ cwd, gitignore: true });\n  const { issues } = await main(options);\n\n  assert.equal(Object.keys(issues.files).length, 0);\n});\n"
  },
  {
    "path": "packages/knip/test/graph-explorer/contention.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { createGraphExplorer } from '../../src/graph-explorer/explorer.ts';\nimport type { ModuleGraph } from '../../src/types/module-graph.ts';\nimport { baseExport, baseFileNode, baseImportMaps } from '../helpers/baseNodeObjects.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst createGraph = (): ModuleGraph => new Map();\n\nconst filePath1 = resolve('module-1.ts');\nconst filePath2 = resolve('module-2.ts');\nconst filePath3 = resolve('module-3.ts');\nconst filePath4 = resolve('module-4.ts');\n\ntest('should detect branching (export re-exported through multiple paths)', () => {\n  const graph = createGraph();\n  const entryPaths = new Set<string>();\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n  });\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([\n        [filePath1, { ...baseImportMaps, reExport: new Map([['identifier', new Set([filePath2])]]) }],\n      ]),\n    },\n  });\n\n  graph.set(filePath3, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([\n        [filePath1, { ...baseImportMaps, reExport: new Map([['identifier', new Set([filePath3])]]) }],\n      ]),\n    },\n  });\n\n  graph.set(filePath4, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([\n        [filePath2, { ...baseImportMaps, reExport: new Map([['identifier', new Set([filePath4])]]) }],\n        [filePath3, { ...baseImportMaps, reExport: new Map([['identifier', new Set([filePath4])]]) }],\n      ]),\n    },\n  });\n\n  const explorer = createGraphExplorer(graph, entryPaths);\n  const contention = explorer.getContention(filePath4);\n\n  const fooContention = contention.get('identifier');\n  assert.ok(fooContention);\n  assert.ok(fooContention.branching.length > 0);\n});\n\ntest('should detect conflict (same identifier defined in multiple files)', () => {\n  const graph = createGraph();\n  const entryPaths = new Set<string>();\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n  });\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n  });\n\n  graph.set(filePath3, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([\n        [filePath1, { ...baseImportMaps, reExport: new Map([['identifier', new Set([filePath3])]]) }],\n        [filePath2, { ...baseImportMaps, reExport: new Map([['identifier', new Set([filePath3])]]) }],\n      ]),\n    },\n  });\n\n  const explorer = createGraphExplorer(graph, entryPaths);\n  const contention = explorer.getContention(filePath3);\n\n  const fooContention = contention.get('identifier');\n  assert.ok(fooContention);\n  assert.ok(fooContention.conflict.length >= 2);\n  assert.ok(fooContention.conflict.includes(filePath1));\n  assert.ok(fooContention.conflict.includes(filePath2));\n});\n\ntest('should return empty map when no contention exists', () => {\n  const graph = createGraph();\n  const entryPaths = new Set<string>();\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n  });\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    exports: new Map([['identifier', { ...baseExport, isReExport: true }]]),\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([\n        [filePath1, { ...baseImportMaps, reExport: new Map([['identifier', new Set([filePath2])]]) }],\n      ]),\n    },\n  });\n\n  const explorer = createGraphExplorer(graph, entryPaths);\n  const contention = explorer.getContention(filePath2);\n\n  assert.equal(contention.size, 0);\n});\n\ntest('should ignore default exports', () => {\n  const graph = createGraph();\n  const entryPaths = new Set<string>();\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    exports: new Map([['default', { ...baseExport, identifier: 'default' }]]),\n  });\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    exports: new Map([['default', { ...baseExport, identifier: 'default' }]]),\n  });\n\n  const explorer = createGraphExplorer(graph, entryPaths);\n  const contention1 = explorer.getContention(filePath1);\n  const contention2 = explorer.getContention(filePath2);\n\n  assert.equal(contention1.has('default'), false);\n  assert.equal(contention2.has('default'), false);\n});\n\ntest('should return empty map for non-existent file', () => {\n  const graph = createGraph();\n  const entryPaths = new Set<string>();\n\n  const explorer = createGraphExplorer(graph, entryPaths);\n  const contention = explorer.getContention(resolve('non-existent.ts'));\n\n  assert.equal(contention.size, 0);\n});\n\ntest('should detect conflict through star re-exports chain', () => {\n  const graph = createGraph();\n  const entryPaths = new Set<string>();\n\n  graph.set(filePath3, {\n    ...baseFileNode,\n    exports: new Map([['CONFLICT', { ...baseExport, identifier: 'CONFLICT' }]]),\n  });\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    exports: new Map([['CONFLICT', { ...baseExport, identifier: 'CONFLICT' }]]),\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([[filePath3, { ...baseImportMaps, reExport: new Map([['*', new Set([filePath2])]]) }]]),\n    },\n  });\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    exports: new Map([['CONFLICT', { ...baseExport, identifier: 'CONFLICT' }]]),\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([[filePath2, { ...baseImportMaps, reExport: new Map([['*', new Set([filePath1])]]) }]]),\n    },\n  });\n\n  const file3Node = graph.get(filePath3);\n  if (file3Node) {\n    file3Node.importedBy = {\n      ...baseImportMaps,\n      reExport: new Map([['*', new Set([filePath2])]]),\n    };\n  }\n\n  const file2Node = graph.get(filePath2);\n  if (file2Node) {\n    file2Node.importedBy = {\n      ...baseImportMaps,\n      reExport: new Map([['*', new Set([filePath1])]]),\n    };\n  }\n\n  const explorer = createGraphExplorer(graph, entryPaths);\n\n  const contention1 = explorer.getContention(filePath1);\n  const conflict1 = contention1.get('CONFLICT');\n  assert.ok(conflict1);\n  assert.equal(conflict1.conflict.length, 3);\n  assert.ok(conflict1.conflict.includes(filePath1));\n  assert.ok(conflict1.conflict.includes(filePath2));\n  assert.ok(conflict1.conflict.includes(filePath3));\n\n  const contention2 = explorer.getContention(filePath2);\n  const conflict2 = contention2.get('CONFLICT');\n  assert.ok(conflict2);\n  assert.equal(conflict2.conflict.length, 3);\n  assert.ok(conflict2.conflict.includes(filePath1));\n  assert.ok(conflict2.conflict.includes(filePath2));\n  assert.ok(conflict2.conflict.includes(filePath3));\n\n  const contention3 = explorer.getContention(filePath3);\n  const conflict3 = contention3.get('CONFLICT');\n  assert.ok(conflict3);\n  assert.equal(conflict3.conflict.length, 3);\n  assert.ok(conflict3.conflict.includes(filePath1));\n  assert.ok(conflict3.conflict.includes(filePath2));\n  assert.ok(conflict3.conflict.includes(filePath3));\n});\n\ntest('should detect conflict from source file aggregated by consumer', () => {\n  const fileA = resolve('a.ts');\n  const fileB = resolve('b.ts');\n  const fileC = resolve('c.ts');\n  const indexFile = resolve('index.ts');\n\n  const graph = createGraph();\n  const entryPaths = new Set([indexFile]);\n\n  graph.set(fileA, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n  });\n\n  graph.set(fileB, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n  });\n\n  graph.set(fileC, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n  });\n\n  graph.set(indexFile, {\n    ...baseFileNode,\n    exports: new Map([['identifier', { ...baseExport, isReExport: true }]]),\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([\n        [fileA, { ...baseImportMaps, reExport: new Map([['*', new Set([indexFile])]]) }],\n        [fileB, { ...baseImportMaps, reExport: new Map([['*', new Set([indexFile])]]) }],\n        [fileC, { ...baseImportMaps, reExport: new Map([['*', new Set([indexFile])]]) }],\n      ]),\n    },\n  });\n\n  const fileANode = graph.get(fileA);\n  if (fileANode) {\n    fileANode.importedBy = {\n      ...baseImportMaps,\n      reExport: new Map([['*', new Set([indexFile])]]),\n    };\n  }\n\n  const fileBNode = graph.get(fileB);\n  if (fileBNode) {\n    fileBNode.importedBy = {\n      ...baseImportMaps,\n      reExport: new Map([['*', new Set([indexFile])]]),\n    };\n  }\n\n  const fileCNode = graph.get(fileC);\n  if (fileCNode) {\n    fileCNode.importedBy = {\n      ...baseImportMaps,\n      reExport: new Map([['*', new Set([indexFile])]]),\n    };\n  }\n\n  const explorer = createGraphExplorer(graph, entryPaths);\n\n  const contentionIndex = explorer.getContention(indexFile);\n  const conflictIndex = contentionIndex.get('identifier');\n  assert.ok(conflictIndex);\n  assert.equal(conflictIndex.conflict.length, 3);\n  assert.ok(conflictIndex.conflict.includes(fileA));\n  assert.ok(conflictIndex.conflict.includes(fileB));\n  assert.ok(conflictIndex.conflict.includes(fileC));\n\n  const contentionA = explorer.getContention(fileA);\n  const conflictA = contentionA.get('identifier');\n  assert.ok(conflictA);\n  assert.equal(conflictA.conflict.length, 3);\n  assert.ok(conflictA.conflict.includes(fileA));\n  assert.ok(conflictA.conflict.includes(fileB));\n  assert.ok(conflictA.conflict.includes(fileC));\n\n  const contentionB = explorer.getContention(fileB);\n  const conflictB = contentionB.get('identifier');\n  assert.ok(conflictB);\n  assert.equal(conflictB.conflict.length, 3);\n\n  const contentionC = explorer.getContention(fileC);\n  const conflictC = contentionC.get('identifier');\n  assert.ok(conflictC);\n  assert.equal(conflictC.conflict.length, 3);\n});\n"
  },
  {
    "path": "packages/knip/test/graph-explorer/cycles.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { createGraphExplorer } from '../../src/graph-explorer/explorer.ts';\nimport type { ModuleGraph } from '../../src/types/module-graph.ts';\nimport { baseFileNode, baseImportMaps, getBaseImport } from '../helpers/baseNodeObjects.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst createGraph = (): ModuleGraph => new Map();\n\nconst filePath1 = resolve('module-1.ts');\nconst filePath2 = resolve('module-2.ts');\nconst filePath3 = resolve('module-3.ts');\nconst filePath4 = resolve('module-4.ts');\n\nconst baseImport = getBaseImport(filePath1);\n\ntest('should detect simple circular dependency (A -> B -> A)', () => {\n  const graph = createGraph();\n  const entryPaths = new Set<string>();\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([[filePath2, { ...baseImportMaps }]]),\n      imports: new Set([{ ...baseImport, specifier: './module-2', filePath: filePath2 }]),\n    },\n  });\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([[filePath1, { ...baseImportMaps }]]),\n      imports: new Set([{ ...baseImport, specifier: './module-1', filePath: filePath1, identifier: 'bar' }]),\n    },\n  });\n\n  const explorer = createGraphExplorer(graph, entryPaths);\n  const cycles = explorer.findCycles(filePath1);\n\n  assert.equal(cycles.length, 1);\n  assert.ok(cycles[0].includes(filePath1));\n  assert.ok(cycles[0].includes(filePath2));\n});\n\ntest('should detect longer circular dependency (A -> B -> C -> A)', () => {\n  const graph = createGraph();\n  const entryPaths = new Set<string>();\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([[filePath2, { ...baseImportMaps }]]),\n      imports: new Set([{ ...baseImport, specifier: './module-2', filePath: filePath2 }]),\n    },\n  });\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([[filePath3, { ...baseImportMaps }]]),\n      imports: new Set([{ ...baseImport, specifier: './module-3', filePath: filePath3, identifier: 'bar' }]),\n    },\n  });\n\n  graph.set(filePath3, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([[filePath1, { ...baseImportMaps }]]),\n      imports: new Set([{ ...baseImport, specifier: './module-1', filePath: filePath1, identifier: 'baz' }]),\n    },\n  });\n\n  const explorer = createGraphExplorer(graph, entryPaths);\n  const cycles = explorer.findCycles(filePath1);\n\n  assert.equal(cycles.length, 1);\n  assert.ok(cycles[0].includes(filePath1));\n  assert.ok(cycles[0].includes(filePath2));\n  assert.ok(cycles[0].includes(filePath3));\n});\n\ntest('should returns empty array when no cycles exist', () => {\n  const graph = createGraph();\n  const entryPaths = new Set<string>();\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([[filePath2, { ...baseImportMaps }]]),\n      imports: new Set([{ ...baseImport, specifier: './module-2', filePath: filePath2 }]),\n    },\n  });\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([[filePath3, { ...baseImportMaps }]]),\n      imports: new Set([{ ...baseImport, specifier: './module-3', filePath: filePath3 }]),\n    },\n  });\n\n  graph.set(filePath3, { ...baseFileNode });\n\n  const explorer = createGraphExplorer(graph, entryPaths);\n  const cycles = explorer.findCycles(filePath1);\n\n  assert.equal(cycles.length, 0);\n});\n\ntest('should skip type-only imports', () => {\n  const graph = createGraph();\n  const entryPaths = new Set<string>();\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([[filePath2, { ...baseImportMaps }]]),\n      imports: new Set([{ ...baseImport, specifier: './module-2', filePath: filePath2 }]),\n    },\n  });\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([[filePath1, { ...baseImportMaps }]]),\n      imports: new Set([{ ...baseImport, specifier: './module-1', identifier: 'Type', isTypeOnly: true }]),\n    },\n  });\n\n  const explorer = createGraphExplorer(graph, entryPaths);\n  const cycles = explorer.findCycles(filePath1);\n\n  assert.equal(cycles.length, 0);\n});\n\ntest('should respect maxDepth', () => {\n  const graph = createGraph();\n  const entryPaths = new Set<string>();\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([[filePath2, { ...baseImportMaps }]]),\n      imports: new Set([{ ...baseImport, specifier: './module-2', filePath: filePath2 }]),\n    },\n  });\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([[filePath3, { ...baseImportMaps }]]),\n      imports: new Set([{ ...baseImport, specifier: './module-3', filePath: filePath3, identifier: 'bar' }]),\n    },\n  });\n\n  graph.set(filePath3, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([[filePath4, { ...baseImportMaps }]]),\n      imports: new Set([{ ...baseImport, specifier: './module-4', filePath: filePath4, identifier: 'baz' }]),\n    },\n  });\n\n  graph.set(filePath4, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([[filePath1, { ...baseImportMaps }]]),\n      imports: new Set([{ ...baseImport, specifier: './module-1', filePath: filePath1, identifier: 'qux' }]),\n    },\n  });\n\n  const explorer = createGraphExplorer(graph, entryPaths);\n\n  const cyclesShallow = explorer.findCycles(filePath1, 2);\n  assert.equal(cyclesShallow.length, 0);\n\n  const cyclesDeep = explorer.findCycles(filePath1);\n  assert.equal(cyclesDeep.length, 1);\n});\n"
  },
  {
    "path": "packages/knip/test/graph-explorer/trace-export.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { buildExportsTree } from '../../src/graph-explorer/operations/build-exports-tree.ts';\nimport type { ModuleGraph } from '../../src/types/module-graph.ts';\nimport { baseExport, baseFileNode, baseImportMaps } from '../helpers/baseNodeObjects.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst createGraph = (): ModuleGraph => new Map();\n\nconst filePath1 = resolve('left.ts');\nconst filePath2 = resolve('right.ts');\nconst filePath3 = resolve('pseudo.ts');\nconst filePath4 = resolve('index.ts');\n\ntest('Trace export through reExportNs', () => {\n  const graph = createGraph();\n  const entryPaths = new Set([filePath4]);\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n    importedBy: {\n      ...baseImportMaps,\n      reExportNs: new Map([['namespaceR', new Set([filePath3])]]),\n    },\n  });\n\n  graph.set(filePath3, {\n    ...baseFileNode,\n    importedBy: {\n      ...baseImportMaps,\n      import: new Map([\n        ['namespaceL', new Set([filePath4])],\n        ['namespaceR', new Set([filePath4])],\n      ]),\n      refs: new Set(['namespaceL']),\n    },\n  });\n\n  graph.set(filePath4, {\n    ...baseFileNode,\n    importedBy: {\n      ...baseImportMaps,\n    },\n  });\n\n  const [trace] = buildExportsTree(graph, entryPaths, { filePath: filePath2, identifier: 'identifier' });\n\n  assert(trace.children.length > 0);\n  assert(trace.children[0].via === 'reExportNS');\n  assert(trace.children[0].identifier === 'namespaceR.identifier');\n});\n\ntest('Trace export through importNs (with ref)', () => {\n  const graph = createGraph();\n  const entryPaths = new Set([filePath4]);\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n    importedBy: {\n      ...baseImportMaps,\n      importNs: new Map([['NS', new Set([filePath3])]]),\n    },\n  });\n\n  graph.set(filePath3, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([[filePath1, { ...baseImportMaps, refs: new Set(['NS.identifier']) }]]),\n    },\n    importedBy: {\n      ...baseImportMaps,\n      import: new Map([['namespaceL', new Set([filePath4])]]),\n      refs: new Set(['namespaceL']),\n    },\n  });\n\n  graph.set(filePath4, {\n    ...baseFileNode,\n    importedBy: { ...baseImportMaps },\n  });\n\n  const [trace] = buildExportsTree(graph, entryPaths, { filePath: filePath1, identifier: 'identifier' });\n\n  assert(trace.children.length > 0);\n  const pseudoStep = trace.children[0];\n  assert(pseudoStep.via === 'importNS');\n  assert(pseudoStep.identifier === 'NS.identifier');\n});\n\ntest('Trace export through importNs (with reExportAs)', () => {\n  const graph = createGraph();\n  const entryPaths = new Set([filePath4]);\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n    importedBy: {\n      ...baseImportMaps,\n      importNs: new Map([['NS', new Set([filePath3])]]),\n    },\n  });\n\n  graph.set(filePath3, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([\n        [\n          filePath1,\n          { ...baseImportMaps, reExportAs: new Map([['NS', new Map([['namespaceL', new Set([filePath3])]])]]) },\n        ],\n      ]),\n    },\n    importedBy: {\n      ...baseImportMaps,\n      import: new Map([['namespaceL', new Set([filePath4])]]),\n      refs: new Set(['namespaceL', 'namespaceL.fn']),\n    },\n  });\n\n  graph.set(filePath4, {\n    ...baseFileNode,\n    importedBy: { ...baseImportMaps },\n  });\n\n  const [trace] = buildExportsTree(graph, entryPaths, { filePath: filePath1, identifier: 'identifier' });\n\n  assert(trace.children.length > 0);\n  const pseudoStep = trace.children[0];\n  assert(pseudoStep.via === 'importNS');\n  assert(pseudoStep.identifier === 'NS.identifier');\n});\n"
  },
  {
    "path": "packages/knip/test/graph-explorer/walk-down.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { IMPORT_STAR } from '../../src/constants.ts';\nimport { walkDown } from '../../src/graph-explorer/walk-down.ts';\nimport type { ModuleGraph } from '../../src/types/module-graph.ts';\nimport { baseExport, baseFileNode, baseImportMaps, getBaseImport } from '../helpers/baseNodeObjects.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst createGraph = (): ModuleGraph => new Map();\n\nconst filePath1 = resolve('module-1.ts');\nconst filePath2 = resolve('module-2.ts');\nconst filePath3 = resolve('module-3.ts');\n\nconst baseImport = getBaseImport(filePath1);\n\ntest('should find direct importers', () => {\n  const graph = createGraph();\n  const entryPaths = new Set<string>();\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n    importedBy: {\n      ...baseImportMaps,\n      import: new Map([['identifier', new Set([filePath2])]]),\n    },\n  });\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([\n        [filePath1, { ...baseImportMaps, imported: new Map([['identifier', new Set([filePath2])]]) }],\n      ]),\n      imports: new Set([baseImport]),\n    },\n  });\n\n  const importers: string[] = [];\n  walkDown(\n    graph,\n    filePath1,\n    'identifier',\n    (_sourceFile, _sourceId, importingFile, _identifier, _isEntry) => {\n      importers.push(importingFile);\n      return undefined;\n    },\n    entryPaths\n  );\n\n  assert.equal(importers.length, 1);\n  assert.equal(importers[0], filePath2);\n});\n\ntest('should find aliased importers', () => {\n  const graph = createGraph();\n  const entryPaths = new Set<string>();\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n    importedBy: {\n      ...baseImportMaps,\n      importAs: new Map([['identifier', new Map([['alias', new Set([filePath2])]])]]),\n    },\n  });\n\n  graph.set(filePath2, { ...baseFileNode });\n\n  const importers: Array<{ file: string; identifier: string }> = [];\n  walkDown(\n    graph,\n    filePath1,\n    'identifier',\n    (_sourceFile, _sourceId, importingFile, identifier, _isEntry) => {\n      importers.push({ file: importingFile, identifier });\n      return undefined;\n    },\n    entryPaths\n  );\n\n  assert.equal(importers.length, 1);\n  assert.equal(importers[0].file, filePath2);\n  assert.equal(importers[0].identifier, 'alias');\n});\n\ntest('should follow re-export chain', () => {\n  const graph = createGraph();\n  const entryPaths = new Set<string>();\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n    importedBy: {\n      ...baseImportMaps,\n      reExport: new Map([['identifier', new Set([filePath2])]]),\n    },\n  });\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    importedBy: {\n      ...baseImportMaps,\n      import: new Map([['identifier', new Set([filePath3])]]),\n    },\n  });\n\n  graph.set(filePath3, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([\n        [filePath2, { ...baseImportMaps, imported: new Map([['identifier', new Set([filePath3])]]) }],\n      ]),\n      imports: new Set([{ ...baseImport, specifier: './module-2', filePath: filePath2 }]),\n    },\n  });\n\n  const importers: string[] = [];\n  walkDown(\n    graph,\n    filePath1,\n    'identifier',\n    (_sourceFile, _sourceId, importingFile, _identifier, _isEntry) => {\n      importers.push(importingFile);\n      return undefined;\n    },\n    entryPaths\n  );\n\n  assert.ok(importers.includes(filePath3));\n});\n\ntest('should mark entry files correctly', () => {\n  const graph = createGraph();\n  const entryPaths = new Set([filePath2]);\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n    importedBy: {\n      ...baseImportMaps,\n      import: new Map([['identifier', new Set([filePath2])]]),\n    },\n  });\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([\n        [filePath1, { ...baseImportMaps, imported: new Map([['identifier', new Set([filePath2])]]) }],\n      ]),\n      imports: new Set([baseImport]),\n    },\n  });\n\n  let isEntryFound = false;\n  walkDown(\n    graph,\n    filePath1,\n    'identifier',\n    (_sourceFile, _sourceId, _importingFile, _identifier, isEntry) => {\n      if (isEntry) isEntryFound = true;\n      return undefined;\n    },\n    entryPaths\n  );\n\n  assert.equal(isEntryFound, true);\n});\n\ntest('should bail out early when visitor returns stop', () => {\n  const graph = createGraph();\n  const entryPaths = new Set<string>();\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n    importedBy: {\n      ...baseImportMaps,\n      import: new Map([['identifier', new Set([filePath2, filePath3])]]),\n    },\n  });\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([\n        [\n          filePath1,\n          {\n            ...baseImportMaps,\n            imported: new Map([['identifier', new Set([filePath2])]]),\n          },\n        ],\n      ]),\n      imports: new Set([baseImport]),\n    },\n  });\n\n  graph.set(filePath3, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([\n        [filePath1, { ...baseImportMaps, imported: new Map([['identifier', new Set([filePath3])]]) }],\n      ]),\n      imports: new Set([baseImport]),\n    },\n  });\n\n  const importers: string[] = [];\n  const stopped = walkDown(\n    graph,\n    filePath1,\n    'identifier',\n    (_sourceFile, _sourceId, importingFile, _identifier, _isEntry) => {\n      importers.push(importingFile);\n      return 'stop';\n    },\n    entryPaths\n  );\n\n  assert.equal(stopped, true);\n  assert.equal(importers.length, 1);\n});\n\ntest('should handle circular imports without infinite loop', () => {\n  const graph = createGraph();\n  const entryPaths = new Set<string>();\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n    importedBy: {\n      ...baseImportMaps,\n      import: new Map([['identifier', new Set([filePath2])]]),\n      reExport: new Map([['alias', new Set([filePath2])]]),\n    },\n  });\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    exports: new Map([['alias', { ...baseExport, identifier: 'alias' }]]),\n    importedBy: {\n      ...baseImportMaps,\n      import: new Map([['alias', new Set([filePath1])]]),\n      reExport: new Map([['identifier', new Set([filePath1])]]),\n    },\n  });\n\n  const importers: string[] = [];\n  const stopped = walkDown(\n    graph,\n    filePath1,\n    'identifier',\n    (_sourceFile, _sourceId, importingFile, _identifier, _isEntry) => {\n      importers.push(importingFile);\n      return undefined;\n    },\n    entryPaths\n  );\n\n  assert.equal(stopped, false);\n\n  assert.ok(importers.length <= 4);\n});\n\ntest('should handle namespace imports with member refs', () => {\n  const graph = createGraph();\n  const entryPaths = new Set<string>();\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n    importedBy: {\n      ...baseImportMaps,\n      importNs: new Map([['NS', new Set([filePath2])]]),\n    },\n  });\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([\n        [\n          filePath1,\n          { ...baseImportMaps, refs: new Set(['NS.identifier']), importNs: new Map([['NS', new Set([filePath2])]]) },\n        ],\n      ]),\n      imports: new Set([{ ...baseImport, identifier: IMPORT_STAR }]),\n    },\n  });\n\n  const importers: Array<{ file: string; identifier: string }> = [];\n  walkDown(\n    graph,\n    filePath1,\n    'identifier',\n    (_sourceFile, _sourceId, importingFile, identifier, _isEntry) => {\n      importers.push({ file: importingFile, identifier });\n      return undefined;\n    },\n    entryPaths\n  );\n\n  assert.equal(importers.length, 1);\n  assert.equal(importers[0].file, filePath2);\n  assert.equal(importers[0].identifier, 'NS.identifier');\n});\n\ntest('should visitor receives correct isEntry and via flags', () => {\n  const graph = createGraph();\n  const entryPaths = new Set([filePath2]);\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n    importedBy: {\n      ...baseImportMaps,\n      import: new Map([['identifier', new Set([filePath2])]]),\n      reExport: new Map([['identifier', new Set([filePath3])]]),\n    },\n  });\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([\n        [filePath1, { ...baseImportMaps, imported: new Map([['identifier', new Set([filePath2])]]) }],\n      ]),\n      imports: new Set([baseImport]),\n    },\n  });\n\n  graph.set(filePath3, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([\n        [filePath1, { ...baseImportMaps, reExport: new Map([['identifier', new Set([filePath3])]]) }],\n      ]),\n      imports: new Set([baseImport]),\n    },\n    importedBy: {\n      ...baseImportMaps,\n      import: new Map([['identifier', new Set([filePath2])]]),\n    },\n  });\n\n  const results: Array<{ file: string; isEntry: boolean; via: string }> = [];\n  walkDown(\n    graph,\n    filePath1,\n    'identifier',\n    (_sourceFile, _sourceId, importingFile, _identifier, isEntry, via) => {\n      results.push({ file: importingFile, isEntry, via });\n      return undefined;\n    },\n    entryPaths\n  );\n\n  const file2Result = results.find(r => r.file === filePath2);\n  assert.ok(file2Result !== undefined);\n  assert.equal(file2Result?.isEntry, true);\n  assert.equal(file2Result?.via, 'import');\n});\n"
  },
  {
    "path": "packages/knip/test/graph-explorer/walk-up.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { IMPORT_STAR } from '../../src/constants.ts';\nimport type { RE_EXPORT_KIND } from '../../src/graph-explorer/constants.ts';\nimport { walkUp } from '../../src/graph-explorer/walk-up.ts';\nimport type { ModuleGraph } from '../../src/types/module-graph.ts';\nimport { baseExport, baseFileNode, baseImportMaps } from '../helpers/baseNodeObjects.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\ntype ReExportKind = (typeof RE_EXPORT_KIND)[keyof typeof RE_EXPORT_KIND];\n\nconst createGraph = (): ModuleGraph => new Map();\n\nconst filePath1 = resolve('module-1.ts');\nconst filePath2 = resolve('module-2.ts');\nconst filePath3 = resolve('module-3.ts');\n\ntest('should find self export (original definition)', () => {\n  const graph = createGraph();\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n  });\n\n  const visited: Array<{ filePath: string; identifier: string; via: ReExportKind }> = [];\n  const stopped = walkUp(graph, filePath1, 'identifier', (filePath, identifier, via) => {\n    visited.push({ filePath, identifier, via });\n    return undefined;\n  });\n\n  assert.equal(stopped, false);\n  assert.equal(visited.length, 1);\n  assert.equal(visited[0].filePath, filePath1);\n  assert.equal(visited[0].identifier, 'identifier');\n  assert.equal(visited[0].via, 'self');\n});\n\ntest('should follow direct re-export chain', () => {\n  const graph = createGraph();\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    exports: new Map([['identifier', { ...baseExport, isReExport: true }]]),\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([\n        [filePath2, { ...baseImportMaps, reExport: new Map([['identifier', new Set([filePath1])]]) }],\n      ]),\n    },\n  });\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n  });\n\n  const visited: Array<{ filePath: string; identifier: string; via: ReExportKind }> = [];\n  walkUp(graph, filePath1, 'identifier', (filePath, identifier, via) => {\n    visited.push({ filePath, identifier, via });\n    return undefined;\n  });\n\n  assert.equal(visited.length, 2);\n  assert.equal(visited[0].via, 'passthrough');\n  assert.equal(visited[0].filePath, filePath2);\n  assert.equal(visited[1].via, 'self');\n  assert.equal(visited[1].filePath, filePath2);\n});\n\ntest('should follow aliased re-export', () => {\n  const graph = createGraph();\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    exports: new Map([['identifier', { ...baseExport, isReExport: true }]]),\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([\n        [\n          filePath2,\n          { ...baseImportMaps, reExportAs: new Map([['alias', new Map([['identifier', new Set([filePath1])]])]]) },\n        ],\n      ]),\n    },\n  });\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    exports: new Map([['alias', { ...baseExport, identifier: 'alias' }]]),\n  });\n\n  const visited: Array<{ filePath: string; identifier: string; via: ReExportKind }> = [];\n  walkUp(graph, filePath1, 'identifier', (filePath, identifier, via) => {\n    visited.push({ filePath, identifier, via });\n    return undefined;\n  });\n\n  assert.equal(visited.length, 2);\n  assert.equal(visited[0].via, 'alias');\n  assert.equal(visited[0].identifier, 'alias');\n  assert.equal(visited[1].via, 'self');\n  assert.equal(visited[1].identifier, 'alias');\n});\n\ntest('should follow namespace re-export', () => {\n  const graph = createGraph();\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    exports: new Map([['NS', { ...baseExport, identifier: 'NS', isReExport: true }]]),\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([[filePath2, { ...baseImportMaps, reExportNs: new Map([['NS', new Set([filePath1])]]) }]]),\n    },\n  });\n\n  graph.set(filePath2, { ...baseFileNode });\n\n  const visited: Array<{ filePath: string; identifier: string; via: ReExportKind }> = [];\n\n  walkUp(graph, filePath1, 'NS', (filePath, identifier, via) => {\n    visited.push({ filePath, identifier, via });\n    return undefined;\n  });\n\n  assert.equal(visited.length, 1);\n  assert.equal(visited[0].via, 'namespace');\n  assert.equal(visited[0].identifier, 'NS');\n});\n\ntest('should follow star re-export', () => {\n  const graph = createGraph();\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([[filePath2, { ...baseImportMaps, reExport: new Map([[IMPORT_STAR, new Set([filePath1])]]) }]]),\n    },\n  });\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n  });\n\n  const visited: Array<{ filePath: string; identifier: string; via: ReExportKind }> = [];\n  walkUp(graph, filePath1, 'identifier', (filePath, identifier, via) => {\n    visited.push({ filePath, identifier, via });\n    return undefined;\n  });\n\n  assert.equal(visited.length, 2);\n  assert.equal(visited[0].via, 'star');\n  assert.equal(visited[1].via, 'self');\n});\n\ntest('should bail out early when visitor returns stop', () => {\n  const graph = createGraph();\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([\n        [filePath2, { ...baseImportMaps, reExport: new Map([['identifier', new Set([filePath1])]]) }],\n      ]),\n    },\n  });\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([\n        [filePath3, { ...baseImportMaps, reExport: new Map([['identifier', new Set([filePath2])]]) }],\n      ]),\n    },\n  });\n\n  graph.set(filePath3, {\n    ...baseFileNode,\n    exports: new Map([['identifier', baseExport]]),\n  });\n\n  const visited: string[] = [];\n  const stopped = walkUp(graph, filePath1, 'identifier', filePath => {\n    visited.push(filePath);\n\n    if (filePath === filePath2) return 'stop';\n    return undefined;\n  });\n\n  assert.equal(stopped, true);\n  assert.equal(visited.length, 1);\n  assert.equal(visited[0], filePath2);\n});\n\ntest('should handle circular re-exports without infinite loop', () => {\n  const graph = createGraph();\n\n  graph.set(filePath1, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([\n        [filePath2, { ...baseImportMaps, reExport: new Map([['identifier', new Set([filePath1])]]) }],\n      ]),\n    },\n  });\n\n  graph.set(filePath2, {\n    ...baseFileNode,\n    imports: {\n      ...baseFileNode.imports,\n      internal: new Map([\n        [filePath1, { ...baseImportMaps, reExport: new Map([['identifier', new Set([filePath2])]]) }],\n      ]),\n    },\n  });\n\n  const visited: string[] = [];\n  const stopped = walkUp(graph, filePath1, 'identifier', filePath => {\n    visited.push(filePath);\n    return undefined;\n  });\n\n  assert.equal(stopped, false);\n\n  assert.ok(visited.length <= 2);\n});\n"
  },
  {
    "path": "packages/knip/test/helpers/assertAndRemoveProperty.ts",
    "content": "import assert from 'node:assert/strict';\nimport type { Issue, Location } from 'codeclimate-types';\n\nfunction assertAndRemoveProperty<TIn extends object, TProp extends keyof TIn>(\n  obj: TIn,\n  propName: TProp,\n  assertProperty: (value: TIn[TProp]) => void\n): Omit<TIn, TProp> {\n  const { [propName]: value, ...rest } = obj;\n  assertProperty(value);\n  return rest;\n}\n\nexport function assertAndRemoveFingerprint(issue: Issue) {\n  return assertAndRemoveProperty(issue, 'fingerprint', fingerprint => assert.match(fingerprint, /[a-f0-9]{32}/));\n}\n\nconst getBeginLine = (loc: Location): number =>\n  'lines' in loc ? loc.lines.begin : 'line' in loc.positions.begin ? loc.positions.begin.line : 0;\n\nexport function orderByPos(a: Issue, b: Issue): number {\n  const pathCompare = a.location.path.localeCompare(b.location.path);\n  if (pathCompare !== 0) return pathCompare;\n  return getBeginLine(a.location) - getBeginLine(b.location);\n}\n"
  },
  {
    "path": "packages/knip/test/helpers/baseCounters.ts",
    "content": "import { ISSUE_TYPES } from '../../src/constants.ts';\nimport type { IssueType } from '../../src/types/issues.ts';\n\nconst baseCounters = {\n  ...(Object.fromEntries(ISSUE_TYPES.map(issueType => [issueType, 0])) as Record<IssueType, number>),\n  processed: 0,\n  total: 0,\n};\n\nexport default baseCounters;\n"
  },
  {
    "path": "packages/knip/test/helpers/baseNodeObjects.ts",
    "content": "import type { Export, FileNode, Import, ImportMaps } from '../../src/types/module-graph.ts';\n\nexport const baseImportMaps: ImportMaps = {\n  refs: new Set(),\n  import: new Map(),\n  importAs: new Map(),\n  importNs: new Map(),\n  reExport: new Map(),\n  reExportAs: new Map(),\n  reExportNs: new Map(),\n};\n\nexport const baseFileNode: FileNode = {\n  imports: {\n    internal: new Map(),\n    external: new Set(),\n    unresolved: new Set(),\n    imports: new Set(),\n    externalRefs: new Set(),\n    programFiles: new Set(),\n    entryFiles: new Set(),\n  },\n  exports: new Map(),\n  duplicates: [],\n  scripts: new Set(),\n  importedBy: baseImportMaps,\n  internalImportCache: undefined,\n};\n\nexport const baseExport: Export = {\n  identifier: 'identifier',\n  pos: 0,\n  line: 1,\n  col: 0,\n  type: 'unknown',\n  members: [],\n  jsDocTags: new Set(),\n  hasRefsInFile: false,\n  referencedIn: new Set(),\n  isReExport: false,\n  fixes: [],\n};\n\nexport const getBaseImport = (filePath: string): Import => ({\n  specifier: './module-1',\n  filePath,\n  identifier: 'identifier',\n  isTypeOnly: false,\n  pos: 0,\n  line: 0,\n  col: 0,\n});\n"
  },
  {
    "path": "packages/knip/test/helpers/copy-fixture.ts",
    "content": "import { cp, mkdtemp, realpath } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { join } from '../../src/util/path.ts';\nimport { resolve } from './resolve.ts';\n\nexport const copyFixture = async (fixturePath: string) => {\n  const cwd = await realpath(await mkdtemp(join(tmpdir(), 'knip-fixture-')));\n  await cp(resolve(fixturePath), cwd, { recursive: true });\n  return cwd;\n};\n"
  },
  {
    "path": "packages/knip/test/helpers/create-options.ts",
    "content": "import { createOptions } from '../../src/util/create-options.ts';\n\nconst _createOptions: typeof createOptions = options => {\n  options.isShowProgress = false;\n  return createOptions(options);\n};\n\nexport { _createOptions as createOptions };\n"
  },
  {
    "path": "packages/knip/test/helpers/diff.ts",
    "content": "/* oxlint-disable no-console */\nimport pc from 'picocolors';\n\nexport const showDiff = (actual: string, expected: string) => {\n  const actualLines = actual.split('\\n');\n  const expectedLines = expected.split('\\n');\n  const maxLen = Math.max(actualLines.length, expectedLines.length);\n  console.log(`\\ndiff (${pc.red('-')} expected, ${pc.green('+')} actual):`);\n  for (let i = 0; i < maxLen; i++) {\n    const a = actualLines[i];\n    const e = expectedLines[i];\n    if (a === e) {\n      console.log(`  ${a}`);\n    } else {\n      if (a !== undefined) console.log(`${pc.red('-')} ${a}`);\n      if (e !== undefined) console.log(`${pc.green('+')} ${e}`);\n    }\n  }\n  console.log();\n};\n"
  },
  {
    "path": "packages/knip/test/helpers/exec.ts",
    "content": "import { spawnSync } from 'node:child_process';\n// oxlint-disable-next-line no-restricted-imports\nimport { resolve } from 'node:path';\n\nconst runtime = process.argv[0];\nconst cliPath = runtime.endsWith('bun') ? resolve('src/cli.ts') : resolve('dist/cli.js');\n\nexport const exec = (command: string, options: { cwd: string }) => {\n  const args = command.replace(/^knip/, '').trim().split(' ').filter(Boolean);\n  const output = spawnSync(runtime, [cliPath, ...args], {\n    cwd: options.cwd,\n    env: {\n      PATH: process.env.PATH,\n      NO_COLOR: '1',\n    },\n  });\n\n  return {\n    stdout: output.stdout.toString().trim(),\n    stderr: output.stderr.toString().trim(),\n    status: output.status,\n  };\n};\n"
  },
  {
    "path": "packages/knip/test/helpers/resolve.ts",
    "content": "// oxlint-disable-next-line no-restricted-imports\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { join, toPosix } from '../../src/util/path.ts';\n\nconst base = toPosix(path.join(path.dirname(fileURLToPath(import.meta.url)), '../..'));\n\nexport const resolve = (id: string) => join(base, id);\n"
  },
  {
    "path": "packages/knip/test/ignore-dependencies-binaries-json.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/ignore-dependencies-binaries-json');\n\ntest('Respect ignored binaries and dependencies, including string-to-regex, config hints', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters, configurationHints } = await main(options);\n\n  assert(issues.binaries['package.json']['formatter']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 1,\n    processed: 1,\n    total: 1,\n  });\n\n  assert.deepEqual(configurationHints, [\n    { type: 'ignoreDependencies', workspaceName: '.', identifier: 'stream' },\n    { type: 'ignoreDependencies', workspaceName: '.', identifier: /.+unused-deps.+/ },\n    { type: 'ignoreBinaries', workspaceName: '.', identifier: /.*unused-bins.*/ },\n  ]);\n});\n\ntest('Respect ignored binaries and dependencies, including string-to-regex', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/ignore-dependencies-binaries.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/ignore-dependencies-binaries');\n\ntest('Respect ignored binaries and dependencies, including regex, show config hints', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters, configurationHints } = await main(options);\n\n  assert(issues.binaries['package.json']['formatter']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 3,\n    processed: 2,\n    total: 2,\n  });\n\n  assert.deepEqual(configurationHints, [\n    { type: 'ignoreDependencies', workspaceName: '.', identifier: 'stream' },\n    { type: 'ignoreDependencies', workspaceName: '.', identifier: /.+unused-deps.+/ },\n    { type: 'ignoreBinaries', workspaceName: '.', identifier: /.*unused-bins.*/ },\n  ]);\n});\n\ntest('Respect ignored binaries and dependencies, including regex, no config hints (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n\ntest('Respect ignored binaries when excluding dependencies+unlisted+unresolved', async () => {\n  const options = await createOptions({ cwd, excludedIssueTypes: ['dependencies', 'unlisted', 'unresolved'] });\n  const { issues, counters, configurationHints } = await main(options);\n\n  assert(issues.binaries['package.json']['formatter']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 3,\n    processed: 2,\n    total: 2,\n  });\n\n  assert.deepEqual(configurationHints, [\n    { type: 'ignoreDependencies', workspaceName: '.', identifier: 'stream' },\n    { type: 'ignoreDependencies', workspaceName: '.', identifier: /^@org\\/.*/ },\n    { type: 'ignoreDependencies', workspaceName: '.', identifier: /^rc-.*/ },\n    { type: 'ignoreDependencies', workspaceName: '.', identifier: /.+unused-deps.+/ },\n    { type: 'ignoreBinaries', workspaceName: '.', identifier: /.*unused-bins.*/ },\n  ]);\n});\n"
  },
  {
    "path": "packages/knip/test/ignore-exports-used-in-file-alias-exclude.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/ignore-exports-used-in-file-alias-exclude');\n\ntest('Find unused exports respecting an ignoreExportsUsedInFile (alias)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters, issues } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 3,\n    processed: 3,\n    total: 3,\n  });\n\n  assert(issues.exports['exports.ts']['ash']);\n  assert(issues.exports['more.ts']['kauri']);\n  assert(issues.exports['more.ts']['larch']);\n});\n"
  },
  {
    "path": "packages/knip/test/ignore-exports-used-in-file-id-chars.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/ignore-exports-used-in-file-id-chars');\n\ntest('Find unused exports respecting an ignoreExportsUsedInFile boolean', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert.equal(issues.exports['imported.ts']['unusedFunction'].symbol, 'unusedFunction');\n  assert.equal(issues.exports['imported.ts']['unusedVar'].symbol, 'unusedVar');\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 2,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/ignore-exports-used-in-file-id-underscores.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/ignore-exports-used-in-file-id-underscores');\n\ntest('Find unused exports when identifiers begin with two underscores', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert.equal(issues.exports['imported.ts']['__underscoresUnused'].symbol, '__underscoresUnused');\n  assert.equal(issues.exports['namespace.ts']['NS.__underscoresUnused'].symbol, '__underscoresUnused');\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 2,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/ignore-exports-used-in-file-re-export.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/ignore-exports-used-in-file-re-export');\n\ntest('Find unused exports respecting an ignoreExportsUsedInFile (re-export)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/ignore-exports-used-in-file-shorthand.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/ignore-exports-used-in-file-shorthand');\n\ntest('Find unused exports respecting an ignoreExportsUsedInFile (shorthand)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/ignore-exports-used-in-file-some.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/ignore-exports-used-in-file-some');\n\ntest('Find unused exports respecting an ignoreExportsUsedInFile object', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert.equal(Object.values(issues.exports).length, 1);\n  assert.equal(issues.exports['imported.ts']['referencedNeverFunction'].symbol, 'referencedNeverFunction');\n  assert.equal(issues.exports['imported.ts']['referencedInternallyFunction'].symbol, 'referencedInternallyFunction');\n\n  assert.equal(Object.values(issues.types).length, 1);\n  assert.equal(Object.values(issues.types['imported.ts']).length, 1);\n  assert.equal(issues.types['imported.ts']['ReferencedNeverInterface'].symbol, 'ReferencedNeverInterface');\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    duplicates: 0,\n    exports: 2,\n    nsExports: 0,\n    nsTypes: 0,\n    processed: 2,\n    total: 2,\n    types: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/ignore-exports-used-in-file-typeof-class.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/ignore-exports-used-in-file-typeof-class');\n\ntest('Ignore exports used in file for typeof function and class references', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['src/api.ts']['TreeLeaf']);\n\n  assert(!issues.exports['src/api.ts']?.['TreeNode']);\n  assert(!issues.exports['src/api.ts']?.['logger']);\n  assert(!issues.exports['src/api.ts']?.['Leaf']);\n  assert(!issues.exports['src/api.ts']?.['Node']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 1,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/ignore-exports-used-in-file.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\ntest('Find unused exports respecting ignoreExportsUsedInFile: true', async () => {\n  const cwd = resolve('fixtures/ignore-exports-used-in-file');\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert.equal(Object.values(issues.exports).length, 1);\n  assert.equal(issues.exports['imported.ts']['referencedNeverFunction'].symbol, 'referencedNeverFunction');\n  assert.equal(issues.exports['imported.ts']['default'].symbol, 'default');\n  assert.equal(issues.exports['imported.ts']['DeclaredThenExportedNamed'].symbol, 'DeclaredThenExportedNamed');\n  assert.equal(issues.exports['imported.ts']['Paladin'].symbol, 'Paladin');\n\n  assert.equal(Object.values(issues.types).length, 1);\n  assert.equal(Object.values(issues.types['imported.ts']).length, 1);\n  assert.equal(issues.types['imported.ts']['ReferencedNeverInterface'].symbol, 'ReferencedNeverInterface');\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    duplicates: 0,\n    exports: 4,\n    nsExports: 0,\n    nsTypes: 0,\n    processed: 10,\n    total: 10,\n    types: 1,\n  });\n});\n\ntest('Find unused exports respecting ignoreExportsUsedInFile: false', async () => {\n  const cwd = resolve('fixtures/ignore-exports-used-in-file-false');\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert.equal(Object.values(issues.exports).length, 1);\n  assert.equal(issues.exports['imported.ts']['default'].symbol, 'default');\n  assert.equal(issues.exports['imported.ts']['DeclaredThenExportedNamed'].symbol, 'DeclaredThenExportedNamed');\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 2,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/ignore-files.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/ignore-files');\n\ntest('Respect ignored files', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert('apples/rooted.js' in issues.files);\n  assert('unused.js' in issues.files);\n\n  assert(issues.exports['apples/used.js'].unused);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 2,\n    exports: 1,\n    processed: 8,\n    total: 12,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/ignore-issues.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/ignore-issues');\n\ntest('Ignore specific issue types for specific file patterns', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  // Generated files should NOT report unused exports\n  assert(!issues.exports['src/generated/types.ts']?.['unusedGenerated']);\n  assert(!issues.exports['src/model.generated.ts']?.['unusedExport']);\n  assert(!issues.exports['src/model.generated.ts']?.['GeneratedModel']);\n\n  // Generated files should NOT report unused types\n  assert(!issues.types['src/generated/types.ts']?.['UnusedType']);\n\n  // But unlisted dependencies SHOULD still be reported in generated files\n  assert(issues.unlisted['src/generated/types.ts']?.['nonexistent-package']);\n\n  // Regular files SHOULD report unused exports\n  assert(issues.exports['src/regular.ts']?.['unusedRegular']);\n  assert(issues.exports['src/regular.ts']?.['RegularClass']);\n\n  // Index file reports its unused export\n  assert(issues.exports['src/index.ts']?.['used']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 3,\n    unlisted: 1,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/ignore-members.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/ignore-members');\n\ntest('Respect ignored members, including string-to-regex, show config hints', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.enumMembers['enums.ts']['Direction.Down']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    enumMembers: 1,\n    processed: 4,\n    total: 4,\n  });\n});\n\ntest('Respect ignored members, including string-to-regex, show config hints (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { counters, configurationHints } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    enumMembers: 1,\n    processed: 4,\n    total: 4,\n  });\n\n  assert.deepEqual(configurationHints, []);\n});\n"
  },
  {
    "path": "packages/knip/test/ignore-negated.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/ignore-negated');\n\ntest('Support negated ignore patterns', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert('src/modules/B/unusedFileB.js' in issues.files);\n  assert(!('src/modules/A/unusedFileA.js' in issues.files));\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    processed: 2,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/ignore-patterns.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\ntest('Configuration hints for unused ignore patterns', async () => {\n  const cwd = resolve('fixtures/ignore-patterns');\n  const options = await createOptions({ cwd });\n  const { counters, configurationHints } = await main(options);\n\n  assert.deepEqual(configurationHints, [\n    { type: 'ignore', identifier: 'dist/**', workspaceName: undefined },\n    { type: 'ignore', identifier: 'generated/**', workspaceName: undefined },\n    { type: 'ignoreFiles', identifier: 'temp/**', workspaceName: undefined },\n  ]);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 2,\n  });\n});\n\ntest('Configuration hints for unused ignore patterns (monorepo)', async () => {\n  const cwd = resolve('fixtures/ignore-patterns-monorepo');\n  const options = await createOptions({ cwd });\n  const { counters, configurationHints } = await main(options);\n\n  assert.deepEqual(configurationHints, [\n    { type: 'ignore', identifier: 'build/**', workspaceName: undefined },\n    { type: 'ignore', identifier: 'cache/**', workspaceName: 'packages/lib' },\n  ]);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/ignore-unresolved.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/ignore-unresolved');\n\ntest('Respect ignored unresolved imports, including regex, show config hints', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters, configurationHints } = await main(options);\n\n  assert(issues.unlisted['index.ts']['missing-module']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unlisted: 1,\n    processed: 2,\n    total: 2,\n  });\n\n  assert.deepEqual(configurationHints, [{ type: 'ignoreUnresolved', workspaceName: '.', identifier: 'unused-ignore' }]);\n});\n"
  },
  {
    "path": "packages/knip/test/ignore-unresolved2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/ignore-unresolved2');\n\ntest('Respect ignored unresolved imports, including regex, show config hints', async () => {\n  const options = await createOptions({ cwd });\n  const { counters, configurationHints } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n\n  assert.deepEqual(configurationHints, [\n    { type: 'ignoreUnresolved', workspaceName: '.', identifier: 'unused-root' },\n    { type: 'ignoreUnresolved', workspaceName: '.', identifier: 'unused-top-level' },\n    { type: 'ignoreUnresolved', workspaceName: '.', identifier: './unresolved-workspace' },\n    { type: 'ignoreUnresolved', workspaceName: 'packages/client', identifier: 'unused-workspace' },\n  ]);\n});\n"
  },
  {
    "path": "packages/knip/test/import-equals.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/import-equals');\n\ntest('Find used exports through import-equal NS.member access', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/import-errors.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/import-errors');\n\ntest('Support various ways to import modules (not-a-file.d.ts)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/import-meta-glob.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/import-meta-glob');\n\ntest('Resolve import.meta.glob patterns as entry files', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    processed: 8,\n    total: 8,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/import-named-default-id.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/import-named-default-id');\n\ntest('Find unused exports by correct name', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['utils.ts']['utilOne']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 1,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/import-star-iteration.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/import-star-iteration');\n\ntest('Handle usage of members of a namespace when imported using * and iterating', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  // Classes Orange and Apple are both used using a for (...in) loop\n  // Classes Broccoli and Spinach are both used using a for (...of) loop\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/imports-destructure-spread.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/imports-destructure-spread');\n\ntest('Ignore namespace re-export by entry file', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/imports-dynamic-access.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { test } from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/imports-dynamic-access');\n\ntest('Support dynamic import module access', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['fruits.ts'].fig);\n  assert(issues.exports['fruits.ts'].grape);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 2,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/imports-namespace-jsx.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/imports-namespace-jsx');\n\ntest('Track namespace member access in JSX elements', async () => {\n  const options = await createOptions({ cwd, isIncludeEntryExports: true });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['components.tsx']['Layout.Unused']);\n  assert(!issues.exports['components.tsx']?.['Layout.Container']);\n  assert(!issues.exports['components.tsx']?.['Layout.Header']);\n  assert(!issues.exports['components.tsx']?.['Layout.Footer']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 2,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/imports-namespace-with-nsexports.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/imports-namespace-with-nsexports');\n\ntest(\"Don't ignore namespace re-export by entry file (nsExports)\", async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    nsExports: 8,\n    unlisted: 1,\n    processed: 8,\n    total: 8,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/imports-namespace.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/imports-namespace');\n\ntest('Ignore namespace re-export by entry file', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['namespace3.ts']['NS3.identifier32']);\n  assert(issues.exports['namespace5.ts']['NS5.identifier36']);\n  assert(issues.exports['namespace6.ts']['NS6.identifier38']);\n  assert(issues.exports['namespace9.ts']['NS9.identifier44']);\n  assert(!issues.exports['namespace10.ts'], 'NS10');\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unlisted: 1,\n    exports: 4,\n    processed: 15,\n    total: 15,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/imports-opaque.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/imports-opaque');\n\ntest('Ignore exports of opaque import calls', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 12,\n    total: 12,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/imports-prop-access-call.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/imports-prop-access-call');\n\ntest('Support various prop access calls', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/imports-self.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/imports-self');\n\ntest('Support various ways to import modules', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/imports-typeof.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/imports-typeof');\n\ntest('Support typeof imports', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/imports.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/imports');\n\ntest('Support various ways to import modules', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 28,\n    total: 28,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/include-entry-exports-scripts.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/include-entry-exports-scripts');\n\ntest('Skip unused exports in entry source files and scripts', async () => {\n  const options = await createOptions({ cwd, isIncludeEntryExports: false });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 5,\n    total: 5,\n  });\n});\n\ntest('Report unused exports in source files (skip for scripts and plugin entry files)', async () => {\n  const options = await createOptions({ cwd, isIncludeEntryExports: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 0, // skip for scripts and plugin entry files\n    processed: 5,\n    total: 5,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/include-entry-exports.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/include-entry-exports');\n\ntest('Skip unused exports in entry source files', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n\ntest('Report unused exports in entry source files', async () => {\n  const options = await createOptions({ cwd, isIncludeEntryExports: true });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['cli.js']['a']);\n  assert(issues.exports['index.ts']['default']);\n  assert(issues.exports['main.ts']['x']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 3,\n    types: 3,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/include-entry-reexports.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/include-entry-reexports');\n\ntest('Skip unused nsExports in entry source files', async () => {\n  const options = await createOptions({ cwd, isIncludeEntryExports: false });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n\ntest('Report unused nsExports in entry source files', async () => {\n  const options = await createOptions({ cwd, isIncludeEntryExports: true });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['packages/shared/module-b.mjs']['identifierB']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 1,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/include-libs.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/include-libs');\n\ntest('Find used exports through external lib definitions', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/js-only.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/js-only');\n\ntest('Find unused files and exports with only JS files', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert.equal(Object.keys(issues.files).length, 1);\n  assert('dangling.js' in issues.files);\n\n  assert.equal(Object.values(issues.exports).length, 1);\n  assert.equal(Object.values(issues.exports['my-namespace.js']).length, 2);\n  assert.equal(issues.exports['my-namespace.js']['MyNamespace.x'].symbol, 'x');\n  assert.equal(issues.exports['my-namespace.js']['MyNamespace.z'].symbol, 'z');\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    exports: 2,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/jsdoc-exports.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/jsdoc-exports');\n\ntest('Find exports from jsdoc @type tags', async () => {\n  const options = await createOptions({ cwd, tags: ['-ignoreunresolved'] });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['module.ts']['alphaFn']);\n  assert(issues.exports['module.ts']['internalUnusedFn']);\n  assert(issues.exports['module.ts']['invalidTaggedFn']);\n  assert(issues.exports['module.ts']['unusedFn']);\n\n  assert(issues.types['module.ts']['UnusedInterface']);\n  assert(issues.types['module.ts']['InternalWithLineComment']);\n  assert(!issues.types['module.ts']['UsedViaJSDoc']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 4,\n    types: 2,\n    processed: 3,\n    total: 3,\n  });\n});\n\ntest('Find exports from jsdoc @type tags (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true, tags: ['-ignoreunresolved'] });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['module.ts']['alphaFn']);\n  assert(issues.exports['module.ts']['invalidTaggedFn']);\n  assert(issues.exports['module.ts']['unusedFn']);\n\n  assert(issues.types['module.ts']['UnusedInterface']);\n  assert(issues.types['module.ts']['UsedViaJSDoc']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 3,\n    types: 2,\n    processed: 2,\n    total: 2,\n  });\n});\n\ntest('JSDoc tag with line comment between tag and export is respected', async () => {\n  const options = await createOptions({ cwd, tags: ['-internal', '-ignoreunresolved'] });\n  const { issues, counters } = await main(options);\n\n  assert(!issues.types['module.ts']['InternalWithLineComment']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 3,\n    types: 1,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/jsdoc.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/jsdoc');\n\ntest('Find imports from jsdoc @type tags', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['index.ts']['some-types']);\n  assert(issues.unlisted['index.ts']['type-fest']);\n  assert(issues.unlisted['index.ts']['more-types']);\n  assert(issues.unlisted['index.ts']['some-module']);\n  assert(issues.unlisted['index.ts']['some-other-module']);\n  assert(issues.unlisted['index.ts']['@jest/types']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unlisted: 6,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/jsx.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/jsx');\n\ntest('Support JSX/TSX files', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    dependencies: 0,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/module-block.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/module-block');\n\ntest('Ignore export in module block used in same file', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/module-register.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/module-register');\n\ntest('Resolve module.register() specifier as dependency', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/module-resolution-baseurl-implicit-relative.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/module-resolution-baseurl-implicit-relative');\n\ntest('Resolve implicit relative module specifiers from baseUrl', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/module-resolution-non-std-absolute.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/module-resolution-non-std-absolute');\n\ntest('Resolve non-standard absolute specifiers', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['x-self/index.ts']['x-other']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unlisted: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/module-resolution-non-std-implicit.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/module-resolution-non-std-implicit');\n\ntest('Resolve non-standard relative specifiers (no tsconfig.json)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/module-resolution-non-std.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/module-resolution-non-std');\n\ntest('Resolve non-standard extensions and report unresolved imports', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['src/index.ts']['@org/unresolved']);\n  assert(issues.unlisted['src/index.ts']['unresolved']);\n  assert(issues.unresolved['src/index.ts']['./unresolved']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    unlisted: 2,\n    unresolved: 1,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/module-resolution-tsconfig-paths.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/module-resolution-tsconfig-paths');\n\ntest('Resolve modules properly using tsconfig paths and globs', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert.equal(issues.dependencies['package.json']['internal'].symbol, 'internal');\n\n  assert.equal(issues.unlisted['index.ts']['@unknown'].symbol, '@unknown');\n  assert.equal(issues.unlisted['index.ts']['unresolved'].symbol, 'unresolved');\n\n  assert.equal(issues.exports['internal-package/index.ts']['unused'].symbol, 'unused');\n  assert.equal(issues.exports['unprefixed/module.ts']['unused'].symbol, 'unused');\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    dependencies: 1,\n    unlisted: 2,\n    exports: 2,\n    processed: 6,\n    total: 6,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/negated-production-paths.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/negated-production-paths');\n\ntest('Exclude negated production paths', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n\ntest('Respect negated paths (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/npm-scripts.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport { getDependencyMetaData } from '../src/manifest/index.ts';\nimport { join } from '../src/util/path.ts';\nimport { load } from '../src/util/plugin.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/npm-scripts');\nconst manifest = await load(join(cwd, 'package.json'));\n\ntest('Get metadata from dependencies (getDependencyMetaData)', async () => {\n  const config = {\n    dir: cwd,\n    cwd,\n    packageNames: [...Object.keys(manifest.dependencies ?? {}), ...Object.keys(manifest.devDependencies ?? {})],\n  };\n\n  const { hostDependencies, installedBinaries } = getDependencyMetaData(config);\n\n  const expectedHostDependencies = new Map();\n  expectedHostDependencies.set('pm2-peer-dep', [{ name: 'pm2', isPeerOptional: false }]);\n\n  assert.deepEqual(hostDependencies, expectedHostDependencies);\n\n  assert.deepEqual(\n    installedBinaries,\n    new Map([\n      ['pm2', new Set(['pm2', 'pm2-dev', 'pm2-docker', 'pm2-runtime'])],\n      ['pm2-dev', new Set(['pm2'])],\n      ['pm2-docker', new Set(['pm2'])],\n      ['pm2-runtime', new Set(['pm2'])],\n      ['runnable', new Set(['@org/runnable'])],\n      ['nx', new Set(['nx'])],\n      ['package', new Set(['package-cli'])],\n      ['package-cli', new Set(['package'])],\n      ['unused', new Set(['unused'])],\n      ['eslint', new Set(['eslint', 'eslint-v6', 'eslint-v7', 'eslint-v8'])],\n      ['eslint-v6', new Set(['eslint'])],\n      ['eslint-v7', new Set(['eslint'])],\n      ['eslint-v8', new Set(['eslint'])],\n      ['@commitlint/cli', new Set(['commitlint'])],\n      ['@org/runnable', new Set(['runnable'])],\n      ['tsup', new Set(['tsup'])],\n      ['commitlint', new Set(['@commitlint/cli'])],\n    ])\n  );\n});\n\ntest('Unused dependencies in npm scripts', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters, configurationHints } = await main(options);\n\n  assert('script.js' in issues.files);\n  assert.equal(Object.keys(issues.files).length, 1);\n\n  assert(issues.dependencies['package.json']['express']);\n\n  assert(issues.devDependencies['package.json']['unused']);\n  assert(!issues.devDependencies['package.json']['eslint-v6']);\n  assert(!issues.devDependencies['package.json']['eslint-v7']);\n  assert(!issues.devDependencies['package.json']['eslint-v8']);\n\n  assert(issues.binaries['package.json']['nodemon']);\n  assert(issues.binaries['package.json']['dotenv']);\n  assert(issues.binaries['package.json']['http-server']);\n  assert(!issues.binaries['package.json']['rm']);\n  assert(!issues.binaries['package.json']['bash']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    dependencies: 1,\n    devDependencies: 1,\n    binaries: 3,\n    files: 1,\n    processed: 3,\n    total: 3,\n  });\n\n  assert.deepEqual(configurationHints, [\n    { workspaceName: '.', identifier: 'rm', type: 'ignoreBinaries' },\n    { workspaceName: '.', identifier: 'bash', type: 'ignoreBinaries' },\n    { workspaceName: '.', identifier: 'eslint', type: 'ignoreBinaries' },\n    { workspaceName: undefined, identifier: 'ignore.js', type: 'ignore' },\n  ]);\n});\n\ntest('Unused dependencies in npm scripts (strict)', async () => {\n  const options = await createOptions({ cwd, isProduction: true, isStrict: true });\n  const { issues, counters } = await main(options);\n  assert(issues.dependencies['package.json']['express']);\n  assert(issues.dependencies['package.json']['unused-peer-dep']);\n  assert(issues.dependencies['package.json']['@sap/approuter']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 2,\n    dependencies: 3,\n    processed: 2,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/ns-spread-reexport.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/ns-spread-reexport');\n\ntest('Should report members of re-exported spread namespace', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['fruits.ts']['Fruits.banana']);\n  assert(issues.exports['animals.ts']['Animals.dog']);\n  assert(issues.exports['colors.ts']['Colors.blue']);\n\n  assert(!issues.exports['hello.resolver.ts']);\n  assert(!issues.exports['utils.ts']);\n\n  assert.deepStrictEqual(counters, {\n    ...baseCounters,\n    exports: 3,\n    processed: 14,\n    total: 14,\n  });\n});\n\ntest('Should report members of re-exported spread namespace (with nsExports)', async () => {\n  const options = await createOptions({ cwd, includedIssueTypes: ['nsExports'] });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['fruits.ts']['Fruits.banana']);\n  assert(issues.exports['animals.ts']['Animals.dog']);\n  assert(issues.exports['colors.ts']['Colors.blue']);\n\n  assert(issues.nsExports['hello.resolver.ts']['resolverBarrel.Hello']);\n  assert(issues.nsExports['utils.ts']['Utils.helper']);\n\n  assert.deepStrictEqual(counters, {\n    ...baseCounters,\n    exports: 3,\n    nsExports: 2,\n    processed: 14,\n    total: 14,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/package-entry-points.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport { join } from '../src/util/path.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/package-entry-points');\n\ntest('Resolve package entry points to source files', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters, configurationHints } = await main(options);\n\n  assert(issues.exports['feature/internal/system/used.ts'].unused);\n  assert('feature/internal/system/unused.ts' in issues.files);\n  assert('src/public/lib/rary/lost.js' in issues.files);\n\n  const filePath = join(cwd, 'package.json');\n  assert.deepEqual(configurationHints, [\n    { type: 'package-entry', identifier: './feature/index.js', workspaceName: '.', filePath },\n    { type: 'package-entry', identifier: './not-found.tsx', workspaceName: '.', filePath },\n  ]);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 1,\n    files: 2,\n    processed: 12,\n    total: 12,\n  });\n});\n\ntest('Resolve package entry points to source files and report unused exports', async () => {\n  const options = await createOptions({ cwd, isIncludeEntryExports: true });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['feature/internal/system/used.ts'].unused);\n  assert(issues.exports['feature/my-feature.js'].unused);\n  assert(issues.exports['src/public/lib/rary/index.ts'].entryExport);\n  assert(issues.exports['lib/index.js'].entryExport);\n\n  assert('feature/internal/system/unused.ts' in issues.files);\n  assert('src/public/lib/rary/lost.js' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 4,\n    files: 2,\n    processed: 12,\n    total: 12,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/path-aliases.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/path-aliases');\n\ntest('Resolve import path aliases', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 5,\n    total: 5,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/path-aliases2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/path-aliases2');\n\ntest('Resolve path aliases from plugin inputs', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['lodash-es']);\n  assert(issues.unlisted['index.ts']['lodash']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    unlisted: 1,\n    processed: 16,\n    total: 16,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/pathless.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/pathless');\n\ntest('Resolve local modules without a relative path', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/peer-dependencies-circular.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/peer-dependencies-circular');\n\ntest('Prevent infinite recursion with circular peer dependencies', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    dependencies: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/peer-dependencies-optional-host.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/peer-dependencies-optional-host');\n\ntest('Find referenced optional peerDependencies with respect to hosts', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/peer-dependencies-optional-ignored.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/peer-dependencies-optional-ignored');\n\ntest('No issues for optional peerDependencies also listed in devDependencies', async () => {\n  const options = await createOptions({ cwd });\n  const { counters, configurationHints } = await main(options);\n\n  assert.equal(configurationHints.length, 0);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/peer-dependencies-optional-strict.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\ntest('Optional peerDependencies in strict mode should not be reported as unused dependencies', async () => {\n  const cwd = resolve('fixtures/peer-dependencies-optional-strict');\n  const options = await createOptions({ cwd, isStrict: true });\n  const { issues, counters } = await main(options);\n\n  assert(!issues.dependencies['package.json']?.['pg']);\n  assert(!issues.dependencies['package.json']?.['@types/pg']);\n  assert(issues.dependencies['package.json']['required-peer']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    dependencies: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n\ntest('Unreferenced optional peerDependencies in strict mode should not be reported as unused dependencies', async () => {\n  const cwd = resolve('fixtures/peer-dependencies-optional-strict-unreferenced');\n  const options = await createOptions({ cwd, isStrict: true });\n  const { issues, counters } = await main(options);\n\n  assert(!issues.dependencies['package.json']?.['optional-peer']);\n  assert(!issues.optionalPeerDependencies['package.json']?.['optional-peer']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/peer-dependencies-optional.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/peer-dependencies-optional');\n\ntest('Find referenced optional peerDependencies', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.optionalPeerDependencies['package.json']['pg']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    optionalPeerDependencies: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/peer-dependencies.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/peer-dependencies');\n\ntest('Find unused peer dependencies', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.dependencies['package.json']['unused']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    dependencies: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugin-config.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugin-config');\n\ntest('Use root plugin config', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    total: 3,\n    processed: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugin-disable.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugin-disable');\n\ntest('Disable plugin from dynamically added config file', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 2,\n    total: 2,\n    processed: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugin-negated-entry-globs.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugin-negated-entry-globs');\n\ntest('Handles config file shared by multiple plugins', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert('src/pages/_util.ts' in issues.files);\n  assert('src/pages/blog/_util.ts' in issues.files);\n  assert('src/pages/blog/_util/index.ts' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 3,\n    processed: 7,\n    total: 7,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugin-overlap.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugin-overlap');\n\ntest('Handles config file shared by multiple plugins', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/_template.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/_template');\n\ntest('Find dependencies with the __PLUGIN_NAME__ plugin', async () => {\n  /**\n   * Ideally, plugin tests have no `issues` left and only `total` and `processed` values in `counters`.\n   * This means for instance that a dependency used in a file, is also listed in package.json, resulting in zero issues.\n   *\n   * Missing binaries? Add: node_modules/pkg/package.json with bin → ./index.js\n   *\n   * Failures in \"Publish preview & run ecosystem tests\" can usually be ignored, unless related to your changes.\n   * All other workflows should be green though.\n   *\n   * Docs: https://knip.dev/writing-a-plugin\n   *\n   * Please remove this comment! 🔥\n   */\n\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/angular.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/angular');\n\ntest('Find dependencies with the Angular plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['angular.json']['@angular-devkit/build-angular']);\n  assert(issues.unresolved['tsconfig.spec.json']['jasmine']);\n  assert(issues.devDependencies['package.json']['karma']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    unlisted: 1,\n    unresolved: 1,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/angular2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/angular2');\n\ntest('Find dependencies with the Angular plugin (2)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['karma']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    processed: 7,\n    total: 7,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/angular3.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/angular3');\n\ntest('Find dependencies with the Angular plugin (non-production)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['@angular/cli']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    processed: 10,\n    total: 10,\n  });\n});\n\ntest('Find dependencies with the Angular plugin (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 5,\n    total: 5,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/astro-db.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport { createOptions } from '../../src/util/create-options.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/astro-db');\n\ntest('Find dependencies with the astro-db plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/astro-og-canvas.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/astro-og-canvas');\n\ntest('Find dependencies with the astro-og-canvas plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/astro-sharp-image-service.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/astro-sharp-image-service');\n\ntest('Find no unused sharp dependency when Astro sharpImageService is configured', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/astro.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/astro');\n\ntest('Find dependencies with the Astro plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['src/consts.ts']['UNUSED']);\n\n  assert('src/pages/_top-level-file-unused.ts' in issues.files);\n  assert('src/pages/_top-level-dir-unused/index.ts' in issues.files);\n\n  assert('src/pages/blog/_nested-unused-file.ts' in issues.files);\n  assert('src/pages/blog/_util/unused-component.astro' in issues.files);\n  assert('src/pages/blog/_util/nested/deeply-nested-unused-file.ts' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 1,\n    files: 5,\n    processed: 25,\n    total: 26,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/ava.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/ava');\n\ntest('Find dependencies with the Ava plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unresolved['package.json']['ts-node/esm/transpile-only']);\n  assert(issues.unresolved['ava.config.mjs']['tsconfig-paths/register']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unresolved: 2,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/ava2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/ava2');\n\ntest('Find dependencies with the Ava plugin (2)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert('__tests__/__helpers__/index.cjs' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    processed: 6,\n    total: 6,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/ava3.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/ava3');\n\ntest('Find dependencies with the Ava plugin (3)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert('test.js' in issues.files);\n  assert('test.ts' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 2,\n    processed: 9,\n    total: 9,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/babel.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/babel');\n\ntest('Find dependencies with the Babel plugin (1)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['@babel/preset-react']);\n  assert(issues.devDependencies['package.json']['babel-plugin-prismjs']);\n\n  assert(issues.unlisted['.babelrc']['@babel/plugin-proposal-decorators']);\n  assert(issues.unresolved['.babelrc']['babel-preset-minify']);\n  assert(issues.unresolved['.babelrc']['react-hot-loader/babel']);\n\n  assert(issues.unlisted['.babelrc.js']['@babel/plugin-transform-runtime']);\n  assert(issues.unresolved['.babelrc.js']['babel-plugin-preval']);\n  assert(issues.unresolved['.babelrc.js']['babel-plugin-transform-imports']);\n  assert(issues.unlisted['.babelrc.js']['dotenv']);\n\n  assert(issues.unlisted['babel.config.cts']['@babel/mod']);\n  assert(issues.unlisted['babel.config.cts']['@babel/mod']);\n  assert(issues.unlisted['babel.config.cts']['@babel/plugin-mod']);\n  assert(issues.unlisted['babel.config.cts']['@babel/plugin-mod2']);\n  assert(issues.unlisted['babel.config.cts']['@babel/preset-mod']);\n  assert(issues.unlisted['babel.config.cts']['@babel/preset-mod2']);\n  assert(issues.unlisted['babel.config.cts']['@scope/babel-plugin']);\n  assert(issues.unlisted['babel.config.cts']['@scope/babel-plugin-mod']);\n  assert(issues.unlisted['babel.config.cts']['@scope/babel-preset']);\n  assert(issues.unlisted['babel.config.cts']['@scope/babel-preset-mod']);\n  assert(issues.unlisted['babel.config.cts']['@scope/mod']);\n  assert(issues.unlisted['babel.config.cts']['@scope/prefix-babel-plugin-mod']);\n  assert(issues.unlisted['babel.config.cts']['@scope2/babel-plugin']);\n  assert(issues.unlisted['babel.config.cts']['@scope2/babel-plugin-mod']);\n  assert(issues.unlisted['babel.config.cts']['@scope2/babel-preset']);\n  assert(issues.unlisted['babel.config.cts']['@scope2/babel-preset-mod']);\n  assert(issues.unresolved['babel.config.cts']['babel-plugin-mod']);\n  assert(issues.unresolved['babel.config.cts']['babel-plugin-mod2']);\n  assert(issues.unresolved['babel.config.cts']['babel-preset-mod']);\n  assert(issues.unresolved['babel.config.cts']['babel-preset-mod2']);\n  assert(issues.unresolved['babel.config.cts']['mod/plugin']);\n  assert(issues.unresolved['babel.config.cts']['mod/preset']);\n  assert(issues.unresolved['babel.config.cts']['my-plugin']);\n  assert(issues.unresolved['babel.config.cts']['my-preset']);\n\n  assert(issues.unlisted['babel.config.js']['@babel/plugin-proposal-class-properties']);\n  assert(issues.unlisted['babel.config.js']['@babel/plugin-proposal-nullish-coalescing-operator']);\n  assert(issues.unlisted['babel.config.js']['@babel/plugin-proposal-object-rest-spread']);\n  assert(issues.unlisted['babel.config.js']['@babel/plugin-proposal-optional-chaining']);\n  assert(issues.unlisted['babel.config.js']['@babel/plugin-transform-runtime']);\n  assert(issues.unresolved['babel.config.js']['babel-plugin-lodash']);\n\n  assert(issues.unresolved['babel.config.cts']['./dir/plugin.js']);\n  assert(issues.unresolved['babel.config.cts']['./dir/preset.js']);\n  assert(issues.unresolved['babel.config.cts']['/dir/plugin.js']);\n  assert(issues.unresolved['babel.config.cts']['/dir/preset.js']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 2,\n    unlisted: 24,\n    unresolved: 17,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/biome-workspace.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/biome-workspace');\n\ntest('Find dependencies with the biome plugin within a workspace', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/biome.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/biome');\n\ntest('Find dependencies with the biome plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/bumpp.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { test } from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/bumpp');\n\ntest('Find dependencies with the bumpp plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/bun.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/bun');\n\ntest('Find dependencies with the Bun plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/bun2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/bun2');\n\ntest('Find dependencies with the Bun plugin (path arguments)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/bun3.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/bun3');\n\ntest('Find dependencies with the Bun plugin (preload)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/bun4.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/bun4');\n\ntest('Find dependencies with the Bun plugin (directory arguments)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/capacitor.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/capacitor');\n\ntest('Find dependencies with the Capacitor plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['capacitor.config.json']['@capacitor-community/http']);\n  assert(issues.unlisted['capacitor.config.json']['@capacitor/android']);\n  assert(issues.unlisted['capacitor.config.json']['@capacitor/ios']);\n  assert(issues.unlisted['capacitor.config.json']['@capacitor/app']);\n  assert(issues.unlisted['capacitor.config.json']['@capacitor/splash-screen']);\n  assert(issues.unlisted['capacitor.config.json']['@capacitor/status-bar']);\n  assert(issues.unlisted['capacitor.config.json']['@capacitor/storage']);\n  assert(issues.unlisted['capacitor.config.json']['cordova-plugin-inappbrowser']);\n\n  assert(issues.unlisted['capacitor.config.ts']['@capacitor-community/http']);\n  assert(issues.unlisted['capacitor.config.ts']['@capacitor/app']);\n  assert(issues.unlisted['capacitor.config.ts']['@capacitor/android']);\n  assert(issues.unlisted['capacitor.config.ts']['@capacitor/ios']);\n  assert(issues.unlisted['capacitor.config.ts']['@capacitor/splash-screen']);\n  assert(issues.unlisted['capacitor.config.ts']['@capacitor/status-bar']);\n  assert(issues.unlisted['capacitor.config.ts']['@capacitor/storage']);\n  assert(issues.unlisted['capacitor.config.ts']['cordova-plugin-inappbrowser']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unlisted: 16,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/changelogen.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/changelogen');\n\ntest('Find dependencies with the Changelogen plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/changelogithub.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/changelogithub');\n\ntest('Find dependencies with the Changelogithub plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/changesets.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/changesets');\n\ntest('Find dependencies with the Changesets plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['.changeset/config.json']['@changesets/changelog-github']);\n  assert(!issues.devDependencies['package.json']?.['@changesets/config']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    unlisted: 1,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/commitizen.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/commitizen');\n\ntest('Find dependencies with the Commitizen plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['.czrc']['cz-conventional-changelog']);\n  assert(issues.unlisted['package.json']['cz-conventional-changelog']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    unlisted: 2,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/commitlint.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/commitlint');\n\ntest('Find dependencies with the Commitizen plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['.commitlintrc.json']['@commitlint/config-conventional']);\n  assert(issues.unlisted['.commitlintrc.json']['commitlint-plugin-tense']);\n  assert(issues.unlisted['.commitlintrc.json']['conventional-changelog-atom']);\n\n  assert(issues.unlisted['commitlint.config.js']['@commitlint/config-conventional']);\n  assert(issues.unlisted['commitlint.config.js']['commitlint-plugin-tense']);\n  assert(issues.unlisted['commitlint.config.js']['commitlint-config-lerna']);\n  assert(issues.unlisted['commitlint.config.js']['@commitlint/format']);\n\n  assert(issues.unlisted['package.json']['@commitlint/config-conventional']);\n  assert(issues.unlisted['package.json']['commitlint-plugin-tense']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    unlisted: 9,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/convex.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/convex');\n\ntest('Find dependencies with the convex plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/create-typescript-app.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/create-typescript-app');\n\ntest('Find dependencies with the create-typescript-app plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/cspell.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/cspell');\n\ntest('Find dependencies with the Cspell plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['.cspell.json']['@cspell/dict-cryptocurrencies']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    // case-sensitivity: fast-glob returns two files (.cspell.json and .cSpell.json) while there's only one\n    unlisted: process.platform === 'darwin' || process.platform === 'win32' ? 2 : 1,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/cucumber.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/cucumber');\n\ntest('Find dependencies with the cucumber plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/cypress-multi-reporter.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/cypress-multi-reporter');\n\ntest('Find dependencies with the cypress-multi-reporter plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['cypress.config.ts']['@nrwl/cypress']);\n  assert(issues.unlisted['cypress/support/commands.ts']['@faker-js/faker']);\n  assert(issues.unlisted['cypress/support/e2e.ts']['@testing-library/cypress']);\n  assert(issues.unlisted['cypress.config.ts']['@testing-library/my-fake-reporter']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 0,\n    unlisted: 4,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/cypress.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/cypress');\n\ntest('Find dependencies with the Cypress plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['cypress.config.ts']['@nrwl/cypress']);\n  assert(issues.unlisted['cypress/support/commands.ts']['@faker-js/faker']);\n  assert(issues.unlisted['cypress/support/e2e.ts']['@testing-library/cypress']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 0,\n    unlisted: 3,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/cypress2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/cypress2');\n\ntest('Find dependencies with the Cypress plugin (2)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 6,\n    total: 6,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/danger.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/danger');\n\ntest('Find dependencies with the danger plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/dependency-cruiser.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/dependency-cruiser');\n\ntest('Find dependencies with the dependency-cruiser plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/docusaurus.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/docusaurus');\n\ntest('Find dependencies with the docusaurus plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['docusaurus.config.js']['@docusaurus/theme-search-algolia']);\n  assert(issues.unlisted['docusaurus.config.js']['@docusaurus/plugin-content-blog']);\n\n  assert(issues.dependencies['package.json']['@mdx-js/react']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unlisted: 2,\n    dependencies: 1,\n    processed: 10,\n    total: 10,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/drizzle.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/drizzle');\n\ntest('Find dependencies with the Drizzle plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 0,\n    unlisted: 0,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/eleventy.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/eleventy');\n\ntest('Find dependencies with the Eleventy plugin (1)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/eleventy2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/eleventy2');\n\ntest('Find dependencies with the Eleventy plugin (2)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['eleventy.config.cjs']['prismjs']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unlisted: 1,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/eleventy3.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/eleventy3');\n\ntest('Find dependencies with the Eleventy plugin (3)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 5,\n    total: 5,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/eleventy4.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/eleventy4');\n\n// Uses a config file with default and `config` export. While this is not\n// currently supported, this test ensures that an error is not thrown.\ntest('Find dependencies with the Eleventy plugin (4)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    // One file is found because the `config` export is not used, so the\n    // default `config.dir.data` directory is used instead.\n    files: 1,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/eslint.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/eslint');\n\ntest('Find dependencies with the ESLint plugin (deprecated/1)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['.eslintrc.cjs']['@scope/eslint-plugin-name']);\n  assert(issues.unlisted['.eslintrc.cjs']['@scope/eslint-plugin']);\n  assert(issues.unlisted['.eslintrc.cjs']['@shopify/eslint-plugin']);\n  assert(issues.unlisted['.eslintrc.js']['@babel/plugin-proposal-decorators']);\n  assert(issues.unlisted['.eslintrc.js']['@next/eslint-plugin-next']);\n  assert(issues.unlisted['.eslintrc.js']['@org/eslint-plugin-name']);\n  assert(issues.unlisted['.eslintrc.js']['@scope-only/eslint-plugin']);\n  assert(issues.unlisted['.eslintrc.js']['@scope/eslint-config']);\n  assert(issues.unlisted['.eslintrc.js']['@scope/eslint-plugin']);\n  assert(issues.unlisted['.eslintrc.json']['@babel/plugin-syntax-import-assertions']);\n  assert(issues.unlisted['.eslintrc.yml']['@sinonjs/eslint-config']);\n  assert(issues.unlisted['.eslintrc.yml']['@sinonjs/eslint-plugin-no-prototype-methods']);\n\n  assert(issues.unresolved['.eslintrc.cjs']['eslint-config-airbnb']);\n  assert(issues.unresolved['.eslintrc.cjs']['eslint-config-next']);\n  assert(issues.unresolved['.eslintrc.cjs']['eslint-plugin-import']);\n  assert(issues.unresolved['.eslintrc.js']['eslint-config-airbnb']);\n  assert(issues.unresolved['.eslintrc.js']['eslint-config-next']);\n  assert(issues.unresolved['.eslintrc.js']['eslint-import-resolver-exports']);\n  assert(issues.unresolved['.eslintrc.js']['eslint-import-resolver-typescript']);\n  assert(issues.unresolved['.eslintrc.js']['eslint-plugin-cypress']);\n  assert(issues.unresolved['.eslintrc.js']['eslint-plugin-eslint-comments']);\n  assert(issues.unresolved['.eslintrc.js']['eslint-plugin-eslint-plugin']);\n  assert(issues.unresolved['.eslintrc.json']['eslint-config-airbnb']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unlisted: 12,\n    unresolved: 11,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/eslint2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/eslint2');\n\ntest('Find dependencies with the ESLint plugin (deprecated/2)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/eslint3.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/eslint3');\n\ntest('Find dependencies with the ESLint plugin (flat/3)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/eslint4.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/eslint4');\n\ntest('Find dependencies with the ESLint plugin (flat/4)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/eslint5.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/eslint5');\n\ntest('Find dependencies with the ESLint plugin (flat/5)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/expo.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/expo');\n\ntest('Find dependencies with the Expo plugin (1)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.dependencies['package.json']['expo-router']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n    dependencies: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/expo2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/expo2');\n\ntest('Find dependencies with the Expo plugin (2)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['app.config.js']['expo-camera']);\n  assert(issues.unlisted['app.config.js']['expo-system-ui']);\n  assert(issues.unlisted['app.config.js']['expo-updates']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n    unlisted: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/expo3.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/expo3');\n\ntest('Find dependencies with the Expo plugin (3)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['app.json']['expo-router']);\n  assert(issues.unlisted['app.json']['react-native-ble-plx']);\n\n  assert(issues.dependencies['package.json']['@expo/metro-runtime']);\n  assert(issues.dependencies['package.json']['expo-system-ui']);\n  assert(issues.dependencies['package.json']['expo-updates']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n    unlisted: 2,\n    dependencies: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/expressive-code.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\ntest('Find dependencies with the expressive-code plugin (Next.js)', async () => {\n  const cwd = resolve('fixtures/plugins/expressive-code');\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n\ntest('Find dependencies with the expressive-code plugin (Astro)', async () => {\n  const cwd = resolve('fixtures/plugins/expressive-code2');\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\ntest('Find dependencies with the expressive-code plugin (Starlight)', async () => {\n  const cwd = resolve('fixtures/plugins/expressive-code3');\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/fooi.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/_template');\n\ntest('Find dependencies with the __PLUGIN_NAME__ plugin', async () => {\n  // Ideally, plugin tests have no `issues` left and only `total` and `processed` values in `counters`\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/gatsby.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/gatsby');\n\ntest('Find dependencies with the Gatsby plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['gatsby']);\n  assert(issues.devDependencies['package.json']['gatsby-cli']);\n  assert(issues.devDependencies['package.json']['gatsby-plugin-advanced-sitemap']);\n  assert(issues.devDependencies['package.json']['gatsby-remark-prismjs']);\n\n  assert(issues.unresolved['gatsby-config.js']['gatsby-plugin-webpack-bundle-analyser-v2']);\n  assert(issues.unresolved['gatsby-config.js']['gatsby-remark-node-identity']);\n\n  assert(issues.unlisted['gatsby-config.js']['@sentry/gatsby']);\n  assert(issues.unlisted['gatsby-node.js']['@babel/plugin-proposal-function-bind']);\n  assert(issues.unlisted['gatsby-node.js']['babel-plugin-transform-imports']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 1,\n    devDependencies: 4,\n    unlisted: 3,\n    unresolved: 2,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/github-action.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/github-action');\n\ntest('Find dependencies with the GitHub Action plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/github-actions-workspaces.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/github-actions-workspaces');\n\ntest('Find dependencies with the GitHub Actions plugin (w/ workspaces)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/github-actions.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/github-actions');\n\ntest('Find dependencies with the GitHub Actions plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unresolved['.github/workflows/test.yml']['esbuild-register']);\n\n  // Let's start out conservatively\n  // assert(issues.unresolved['.github/workflows/test.yml']['./script.js']);\n  assert(issues.unresolved['.github/actions/composite/action.yml']['esbuild-register']);\n\n  assert(issues.binaries['.github/actions/composite/action.yml']['eslint']);\n  assert(issues.binaries['.github/actions/composite/action.yml']['playwright']);\n\n  assert(issues.binaries['.github/workflows/test.yml']['changeset']);\n  assert(issues.binaries['.github/workflows/test.yml']['eslint']);\n  assert(issues.binaries['.github/workflows/test.yml']['knip']);\n  assert(issues.binaries['.github/workflows/test.yml']['nyc']);\n  assert(issues.binaries['.github/workflows/test.yml']['playwright']);\n  assert(issues.binaries['.github/workflows/test.yml']['prisma']);\n  assert(issues.binaries['.github/workflows/test.yml']['release-it']);\n  assert(issues.binaries['.github/workflows/test.yml']['wait-on']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 10,\n    unresolved: 2,\n    processed: 10,\n    total: 10,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/graphql-codegen-graphql-config.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/graphql-codegen-graphql-config');\n\ntest('Find dependencies with the graphql-codegen plugin (graphql-config)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['graphql.config.ts']['@graphql-codegen/near-operation-file-preset']);\n  assert(issues.unlisted['graphql.config.ts']['@graphql-codegen/schema-ast']);\n  assert(issues.unlisted['graphql.config.ts']['@graphql-codegen/introspection']);\n  assert(issues.unlisted['graphql.config.ts']['@graphql-codegen/typescript']);\n  assert(issues.unlisted['graphql.config.ts']['@graphql-codegen/typescript-operations']);\n  assert(issues.unlisted['graphql.config.ts']['@graphql-codegen/typescript-urql']);\n  assert(issues.unlisted['graphql.config.ts']['@graphql-codegen/typescript-msw']);\n\n  assert(issues.unlisted['.graphqlrc']['@graphql-codegen/add']);\n  assert(issues.unlisted['.graphqlrc']['@graphql-codegen/typescript']);\n  assert(issues.unlisted['.graphqlrc']['@graphql-codegen/typescript-operations']);\n  assert(issues.unlisted['.graphqlrc']['@graphql-codegen/typed-document-node']);\n  assert(issues.unlisted['.graphqlrc']['@graphql-codegen/typescript-resolvers']);\n\n  assert(issues.unlisted['graphql.config.toml']['@graphql-codegen/add']);\n  assert(issues.unlisted['graphql.config.toml']['@graphql-codegen/typescript']);\n  assert(issues.unlisted['graphql.config.toml']['@graphql-codegen/typescript-operations']);\n  assert(issues.unlisted['graphql.config.toml']['@graphql-codegen/typed-document-node']);\n  assert(issues.unlisted['graphql.config.toml']['@graphql-codegen/typescript-resolvers']);\n\n  assert(issues.unlisted['package.json']['@graphql-codegen/client-preset']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unlisted: 18,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/graphql-codegen-graphql-config2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/graphql-codegen-graphql-config2');\n\ntest('Find dependencies with the graphql-codegen plugin (graphql-config but not installed)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['package.json']['@graphql-codegen/client-preset']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unlisted: 1,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/graphql-codegen.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/graphql-codegen');\n\ntest('Find dependencies with the graphql-codegen plugin (codegen.ts function)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['codegen.ts']['@graphql-codegen/near-operation-file-preset']);\n  assert(issues.unlisted['codegen.ts']['@graphql-codegen/schema-ast']);\n  assert(issues.unlisted['codegen.ts']['@graphql-codegen/introspection']);\n  assert(issues.unlisted['codegen.ts']['@graphql-codegen/typescript']);\n  assert(issues.unlisted['codegen.ts']['@graphql-codegen/typescript-operations']);\n  assert(issues.unlisted['codegen.ts']['@graphql-codegen/typescript-urql']);\n  assert(issues.unlisted['codegen.ts']['@graphql-codegen/typescript-msw']);\n  assert(issues.unlisted['codegen.ts']['@graphql-codegen/graphql-modules-preset']);\n  assert(issues.unlisted['codegen.ts']['graphql-codegen-typescript-validation-schema']);\n  assert(issues.unlisted['codegen.ts']['graphql-codegen-apollo-next-ssr']);\n\n  assert(issues.unlisted['codegen.yaml']['@graphql-codegen/add']);\n  assert(issues.unlisted['codegen.yaml']['@graphql-codegen/typescript']);\n  assert(issues.unlisted['codegen.yaml']['@graphql-codegen/typescript-operations']);\n  assert(issues.unlisted['codegen.yaml']['@graphql-codegen/typed-document-node']);\n  assert(issues.unlisted['codegen.yaml']['@graphql-codegen/typescript-resolvers']);\n\n  assert(issues.unlisted['package.json']['@graphql-codegen/client-preset']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unlisted: 16,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/hardhat.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/hardhat');\n\ntest('Find dependencies with the Hardhat plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/husky-legacy.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/husky-lint-staged');\n\ntest('Find dependencies with the husky plugin (legacy)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/husky-v8.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/husky-v8');\n\ntest('Find dependencies with the husky plugin (v8)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.binaries['.husky/pre-push']['jest']);\n  assert(issues.binaries['.husky/pre-push']['pretty-quick']);\n  assert(issues.binaries['.husky/pre-rebase']['eslint']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 3,\n    processed: 0,\n    total: 0,\n  });\n});\n\ntest('Find dependencies with the husky plugin (v8) (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/husky-v9-1.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/husky-v9-1');\n\ntest('Find dependencies with the husky plugin (v9.1)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/husky-v9.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/husky-v9');\n\ntest('Find dependencies with the husky plugin (v9)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.binaries['.husky/pre-push']['jest']);\n  assert(issues.binaries['.husky/pre-push']['pretty-quick']);\n  assert(issues.binaries['.husky/pre-rebase']['eslint']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 3,\n    processed: 0,\n    total: 0,\n  });\n});\n\ntest('Find dependencies with the husky plugin (v9) (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/i18next-parser.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/i18next-parser');\n\ntest('Find dependencies with the i18next-parser plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/jest.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport { join } from '../../src/util/path.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/jest');\n\ntest('Find dependencies with the Jest plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['jest.config.shared.js']['@jest/types']);\n  assert(issues.unlisted['jest.setup.js']['@testing-library/jest-dom']);\n  assert(issues.unlisted['jest.config.js']['@jest/types']);\n  assert(issues.unlisted['jest.config.js']['@side/jest-runtime']);\n  assert(issues.unlisted['jest.config.js']['@nrwl/react']);\n  assert(issues.unresolved['jest.config.js']['babel-jest']);\n  assert(issues.unresolved['jest.config.js']['identity-obj-proxy']);\n  assert(issues.unresolved['jest.config.js']['jest-junit']);\n  assert(issues.unresolved['jest.config.js']['jest-phabricator']);\n  assert(issues.unresolved['jest.config.js']['jest-runner-eslint']);\n  assert(issues.unresolved['jest.config.js']['jest-silent-reporter']);\n  assert(issues.unresolved['jest.config.js'][join(cwd, '__mocks__/fileMock.js')]);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    unlisted: 5,\n    unresolved: 7,\n    processed: 7,\n    total: 7,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/jest2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/jest2');\n\ntest('Find dependencies with the Jest plugin (2)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['jest']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    unlisted: 0,\n    unresolved: 0,\n    processed: 6,\n    total: 6,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/jest3.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/jest3');\n\ntest('Find dependencies with the Jest plugin (3)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/karma.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/karma');\n\ntest('Find dependencies with the Karma plugin (initial config)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['karma.conf.js']['jasmine-core']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    //👇 Not 2, as `karma-coverage` should be loaded as plugin by default\n    devDependencies: 1,\n    unlisted: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/karma2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/karma2');\n\ntest('Find dependencies with the Karma plugin (test files)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert.ok('out-of-base-path/example.spec.js' in issues.files);\n  assert.ok('src/excluded.spec.js' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    files: 2,\n    processed: 5,\n    total: 5,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/karma3.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/karma3');\n\ntest('Find dependencies with the Karma plugin (plugin dependencies)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert.ok(issues.unlisted['karma.conf.js']['karma-jasmine']);\n  assert.ok(issues.unlisted['karma.conf.js']['karma-coverage']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    unlisted: 2,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/knex.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/knex');\n\ntest('Find dependencies with the knex plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/ladle.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/ladle');\n\ntest('Find dependencies with the ladle plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.dependencies['package.json']['react-dom']);\n  assert(issues.devDependencies['package.json']['@types/react-dom']);\n  assert(issues.binaries['package.json']['ladle']);\n  assert(issues.unlisted['.ladle/vite.config.ts']['vite']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 1,\n    dependencies: 1,\n    devDependencies: 1,\n    unlisted: 1,\n    processed: 5,\n    total: 5,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/lefthook-ci.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/lefthook-ci');\n\ntest('Find dependencies with the Lefthook plugin (CI)', async () => {\n  const CI = process.env.CI;\n  process.env.CI = '1';\n\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 0,\n    total: 0,\n  });\n\n  process.env.CI = CI;\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/lefthook-v1.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport fs from 'node:fs/promises';\nimport os from 'node:os';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport { join } from '../../src/util/path.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst skipIfBun = typeof Bun !== 'undefined' && os.platform() === 'win32' ? test.skip : test;\n\nconst cwd = resolve('fixtures/plugins/lefthook-v1');\n\nskipIfBun('Find dependencies with the lefthook v1 plugin', async () => {\n  const CI = process.env.CI;\n  process.env.CI = '';\n  await fs.rename(join(cwd, '_git'), join(cwd, '.git')); // Can't add .git folder to repo\n\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 0,\n    total: 0,\n  });\n\n  process.env.CI = CI;\n  await fs.rename(join(cwd, '.git'), join(cwd, '_git'));\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/lefthook.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/lefthook');\n\ntest('Find dependencies with the Lefthook plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.binaries['lefthook.yml']['eslint']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/lint-staged.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/lint-staged');\n\ntest('Find dependencies with the lint-staged plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.binaries['package.json']['eslint']);\n  assert(issues.binaries['package.json']['prettier']);\n  assert(issues.binaries['.lintstagedrc.js']['eslint']);\n  assert(issues.binaries['.lintstagedrc.js']['prettier']);\n  assert(issues.devDependencies['package.json']['lint-staged']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 5,\n    devDependencies: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n\ntest('Find dependencies with the lint-staged plugin (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 0,\n    total: 0,\n  });\n});\n\ntest('Find dependencies with the lint-staged plugin (with _comment field)', async () => {\n  const cwd = resolve('fixtures/plugins/lint-staged-comment');\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.binaries['.lintstagedrc.json']['eslint']);\n  assert(issues.binaries['.lintstagedrc.json']['prettier']);\n  assert(issues.devDependencies['package.json']['lint-staged']);\n\n  // Should not report words from _comment as binaries\n  assert(!issues.binaries['.lintstagedrc.json']?.['This']);\n  assert(!issues.binaries['.lintstagedrc.json']?.['Note']);\n  assert(!issues.binaries['.lintstagedrc.json']?.['changes']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 2,\n    devDependencies: 1,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/linthtml.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/linthtml');\n\ntest('Find dependencies with the LintHTML plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/lockfile-lint.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/lockfile-lint');\n\ntest('Find dependencies with the lockfile-lint plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/lost-pixel.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/lost-pixel');\n\ntest('Find dependencies with the lost-pixel plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/markdownlint.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/markdownlint');\n\ntest('Find dependencies with the markdownlint plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.binaries['package.json']['markdownlint']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 1,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/mdx.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/mdx');\n\ntest('Find dependencies with the mdx plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/mdxlint.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/mdxlint');\n\ntest('Find dependencies with the mdxlint plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/metro-defaults.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/metro-defaults');\n\ntest('Ignore unresolved issues for Metro defaults', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.dependencies['package.json']['metro']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    dependencies: 1,\n    unresolved: 0,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/metro-react-native.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/metro-react-native');\n\ntest('Find dependencies with the Metro plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.dependencies['package.json']['react']);\n  assert(issues.dependencies['package.json']['react-native']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    dependencies: 2,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/metro.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/metro');\n\ntest('Find dependencies with the Metro plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    dependencies: 1,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/mocha.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/mocha');\n\ntest('Find dependencies with the Mocha plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/moonrepo.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/moonrepo');\n\ntest('Find dependencies with the moonrepo plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.binaries['.moon/tasks.yml']['ls-lint']);\n  assert(issues.binaries['.moon/tasks/typescript.yml']['tsc']);\n  assert(issues.binaries['.moon/tasks/typescript.yml']['eslint']);\n  assert(issues.binaries['moon.yml']['vite-node']);\n  assert(issues.binaries['moon.yml']['vitest']);\n  assert(issues.binaries['moon.yml']['tsx']);\n\n  assert(issues.devDependencies['package.json']['@moonrepo/cli']);\n  assert(issues.devDependencies['package.json']['vite-node']);\n  assert(issues.devDependencies['package.json']['eslint']);\n  assert(issues.devDependencies['libs/b/package.json']['vitest']);\n\n  assert('libs/b/server/server.ts' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    devDependencies: 4,\n    binaries: 6,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/msw.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/msw');\n\ntest('Should not see the msw files in issues', async () => {\n  const options = await createOptions({ cwd, isStrict: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 4,\n    total: 4,\n    processed: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/nano-staged.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/nano-staged');\n\ntest('Find dependencies with the nano-staged plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.binaries['package.json']['eslint']);\n  assert(issues.binaries['package.json']['prettier']);\n  assert(issues.binaries['.nano-staged.js']['eslint']);\n  assert(issues.binaries['.nano-staged.js']['prettier']);\n  assert(issues.devDependencies['package.json']['nano-staged']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 4,\n    devDependencies: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n\ntest('Find dependencies with the nano-staged plugin (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/nest.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/nest');\n\ntest('Find dependencies with the nest plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 6,\n    total: 6,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/netlify.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/netlify');\n\ntest('Find dependencies with the Netlify plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['netlify.toml']['netlify-plugin-check-output-for-puppy-references']);\n  assert(issues.unlisted['netlify.toml']['package-1']);\n  assert(issues.unlisted['netlify.toml']['package-2']);\n  assert(issues.unlisted['netlify.toml']['package-3']);\n  assert(issues.unlisted['netlify.toml']['package-4']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    unlisted: 5,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/next-intl.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/next-intl');\n\ntest('Find dependencies with the next-intl plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/next-mdx.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/next-mdx');\n\ntest('Find dependencies with the @next/mdx plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['next.config.js']['remark-frontmatter']);\n  assert(issues.unlisted['next.config.js']['remark-mdx-frontmatter']);\n  assert(issues.unlisted['next.config.js']['rehype-starry-night']);\n  assert(issues.unlisted['next.config.js']['recma-export-filepath']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unlisted: 4,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/next-middleware.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/next-middleware');\n\ntest('Support Next.js middleware', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/next-page-extensions.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/next-page-extensions');\n\ntest('Find dependencies with the Next.js plugin (custom pageExtensions)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert('src/unused.ts' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/next.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/next');\n\ntest('Find dependencies with the Next.js plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert('app/unused.ts' in issues.files);\n  assert('pages/unused.jsx' in issues.files);\n\n  assert(issues.unlisted['next.config.js']['next-transpile-modules']);\n  assert(issues.unlisted['pages/[[...route]].tsx']['react']);\n  assert(issues.unlisted['pages/[[...route]].tsx']['react-helmet']);\n  assert(issues.unlisted['pages/home.tsx']['react']);\n  assert(issues.unlisted['app/layout.tsx']['react']);\n  assert(issues.unlisted['app/home/page.tsx']['react']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 2,\n    devDependencies: 0,\n    unlisted: 6,\n    processed: 13,\n    total: 13,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/nitro.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/nitro');\n\ntest('Find dependencies with the nitro plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['server/utils/fn.ts']['unused']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 1,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/node-modules-inspector.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/node-modules-inspector');\n\ntest('Find dependencies with the node-modules-inspector plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/node-test-runner.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/node-test-runner');\n\ntest('Find dependencies with the node-test-runner plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n\ntest('Find dependencies with the node-test-runner plugin (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/node.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/node');\n\ntest('Find dependencies with the node plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/nodemon.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/nodemon');\n\ntest('Find dependencies with the nodemon plugin', async () => {\n  const options = await createOptions({ cwd, isStrict: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/npm-package-json-lint.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/npm-package-json-lint');\n\ntest('Find dependencies with the npm-package-json-lint plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['.npmpackagejsonlintrc.json']['npm-package-json-lint-config-default']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    unlisted: 1,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/nuxt-auto-import-disabled.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/nuxt-auto-import-disabled');\n\ntest('Find dependencies and entries with auto-imports disabled', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert('composables/useTheme.ts' in issues.files);\n  assert('components/StatusBadge.vue' in issues.files);\n\n  assert(!issues.dependencies['package.json']['vue']);\n  assert(issues.dependencies['package.json']['@vueuse/nuxt']);\n\n  assert(issues.exports['utils/format.ts']['formatNumber']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 2,\n    dependencies: 1,\n    exports: 1,\n    processed: 7,\n    total: 7,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/nuxt-auto-import.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/nuxt-auto-import');\n\ntest('Find dependencies and entries through generated definitions in .nuxt dir', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert('composables/useTheme.ts' in issues.files);\n  assert('components/StatusBadge.vue' in issues.files);\n\n  assert(issues.dependencies['package.json']['vue']);\n  assert(issues.dependencies['package.json']['@vueuse/nuxt']);\n\n  assert(issues.exports['utils/format.ts']['formatNumber']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 2,\n    dependencies: 2,\n    exports: 1,\n    processed: 7,\n    total: 7,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/nuxt-config.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/nuxt-config');\n\ntest('Find dependencies with the nuxt plugin (config options)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 8,\n    total: 8,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/nuxt.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/nuxt');\n\ntest('Find dependencies with the nuxt plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.dependencies['package.json']['vue']);\n  assert(issues.exports['utils/fn.ts']['unused']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    dependencies: 1,\n    exports: 1,\n    processed: 8,\n    total: 8,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/nx-crystal.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/nx-crystal');\n\ntest('Find dependencies with the Nx plugin (crystal)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['@nx/cypress']);\n  assert(issues.devDependencies['package.json']['@nrwl/workspace']);\n  assert(issues.unlisted['nx.json']['@nx/nuxt']);\n  assert(issues.binaries['package.json']['nx']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 1,\n    devDependencies: 2,\n    unlisted: 1,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/nx.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/nx');\n\ntest('Find dependencies with the Nx plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['@nx/cypress']);\n  assert(issues.devDependencies['package.json']['@nrwl/devkit']);\n  assert(issues.devDependencies['package.json']['@nrwl/storybook']);\n  assert(issues.devDependencies['package.json']['@nrwl/web']);\n  assert(issues.devDependencies['package.json']['@nrwl/workspace']);\n  assert(issues.devDependencies['package.json']['rimraf']);\n\n  assert(issues.unlisted['package.json']['nx']);\n  assert(issues.unlisted['apps/b/project.json']['@js/cypress']);\n  assert(issues.unlisted['libs/b/project.json']['nx']);\n  assert(issues.unlisted['libs/b/project.json']['@nx/vitest']);\n  assert(issues.unlisted['libs/b/project.json']['@nx/webpack']);\n  assert(issues.unlisted['libs/b/project.json']['webpack-cli']);\n\n  assert(issues.binaries['package.json']['nx']);\n  assert(issues.binaries['libs/b/project.json']['webpack']);\n  assert(issues.binaries['libs/b/project.json']['compodoc']);\n  assert(issues.binaries['libs/b/project.json']['biome']);\n  assert(issues.binaries['libs/b/project.json']['tsc']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 6,\n    devDependencies: 6,\n    unlisted: 6,\n    files: 3,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/nyc.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/nyc');\n\ntest('Find dependencies with the nyc plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['nyc']);\n  assert(issues.unlisted['.nycrc.json']['@istanbuljs/nyc-config-typescript']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    unlisted: 1,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/oclif.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/oclif');\n\ntest('Find dependencies with the oclif plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/openapi-ts.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/openapi-ts');\n\ntest('Find dependencies with the openapi-ts plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/oxfmt.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/oxfmt');\n\ntest('Find dependencies with the oxfmt plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/oxlint.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/oxlint');\n\ntest('Find dependencies with the oxlint plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/parcel.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/parcel');\n\ntest('Find dependencies with the Parcel plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['parcel']);\n  assert(issues.unlisted['.parcelrc']['@parcel/config-default']);\n  assert(issues.unlisted['.parcelrc']['@parcel/transformer-js']);\n  assert(issues.unlisted['.parcelrc']['@parcel/transformer-react-refresh-wrap']);\n  assert(issues.unlisted['.parcelrc']['@parcel/transformer-svg-react']);\n  assert(issues.unlisted['.parcelrc']['@parcel/optimizer-terser']);\n  assert(issues.unlisted['.parcelrc']['@parcel/optimizer-cssnano']);\n  assert(issues.unlisted['.parcelrc']['@parcel/reporter-cli']);\n  assert(issues.unlisted['.parcelrc']['@parcel/reporter-bundle-analyzer']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    binaries: 1,\n    unlisted: 8,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/payload.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/payload');\nconst options = await createOptions({ cwd });\nconst { counters, issues } = await main(options);\n\ntest('Find dependencies with the Payload CMS plugin', async () => {\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n\ntest('Ignore migration issues with the Payload CMS plugin', async () => {\n  assert(!issues.unlisted['migrations/20260218.ts']);\n  assert(!('migrations/20260218.ts' in issues.files));\n});\n\ntest('Mark importMap components as used with the Payload CMS plugin', async () => {\n  assert(!('src/components/ImportMapComponent.tsx' in issues.files));\n  assert(!issues.exports['src/components/ImportMapComponent.tsx']?.ImportMapComponent);\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/playwright-ct.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/playwright-ct');\n\ntest('Find dependencies with the Playwright for components plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 0,\n    unlisted: 0,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/playwright-ct2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/playwright-ct2');\n\ntest('Find dependencies with the Playwright for components plugin (2)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert('src/unused.spec.ts' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/playwright.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/playwright');\n\ntest('Find dependencies with the Playwright plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/playwright2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/playwright2');\n\ntest('Find dependencies with the Playwright plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/plop.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/plop');\n\ntest('Find dependencies with the plop plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/pm2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/pm2');\nconst cwdEcosystem = resolve('fixtures/plugins/pm2-ecosystem');\n\ntest('Find entry files with the pm2 plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(!('src/another.js' in issues.files));\n  assert('src/unused.js' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    processed: 3,\n    total: 3,\n  });\n});\n\ntest('Find entry files with the pm2 plugin from ecosystem config', async () => {\n  const options = await createOptions({ cwd: cwdEcosystem });\n  const { issues, counters } = await main(options);\n\n  assert(!('src/another.js' in issues.files));\n  assert('src/unused.js' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/pnpm.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { test } from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/pnpm');\n\ntest('Find dependencies with the pnpm plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/pnpm2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { test } from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/pnpm2');\n\ntest('Find dependencies with the pnpm plugin (2)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/postcss-cjs.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/postcss-cjs');\n\ntest('Find dependencies with the PostCSS plugin (function)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unresolved['package.json']['autoprefixer']);\n  assert(issues.unlisted['postcss.config.cjs']['autoprefixer']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unlisted: 1,\n    unresolved: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/postcss-next.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/postcss-next');\n\ntest('Find dependencies with the PostCSS plugin (implicit w/ Next.js)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unresolved['package.json']['autoprefixer']);\n  assert(issues.unresolved['postcss.config.json']['autoprefixer']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unresolved: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/postcss-tailwindcss.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/postcss-tailwindcss');\n\ntest('Find dependencies with the PostCSS plugin (with tailwindcss)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unresolved['postcss.config.js']['tailwindcss']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unresolved: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/postcss-tailwindcss2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/postcss-tailwindcss2');\n\ntest('Find dependencies with the PostCSS plugin (with @tailwindcss/postcss)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['postcss.config.mjs']['@tailwindcss/postcss']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unlisted: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/postcss.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/postcss');\n\ntest('Find dependencies with the PostCSS plugin (postcss.config.js function)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unresolved['package.json']['autoprefixer']);\n  assert(issues.unlisted['postcss.config.js']['autoprefixer']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unlisted: 1,\n    unresolved: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/preconstruct.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/preconstruct');\n\ntest('Find dependencies with the preconstruct plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/prettier-re-exports.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/prettier-reexport');\n\ntest('Handle re-exported config', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters, configurationHints } = await main(options);\n\n  assert(!issues.unlisted?.['prettier.config.js']?.['prettier-plugin-test']);\n\n  assert.deepEqual(\n    configurationHints.filter(hint => hint.type === 'ignoreDependencies'),\n    [{ type: 'ignoreDependencies', workspaceName: '.', identifier: 'prettier-plugin-test' }]\n  );\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/prettier.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/prettier');\n\ntest('Find dependencies with the Prettier plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['prettier']);\n  assert(issues.unlisted['prettier.config.js']['prettier-plugin-xml']);\n  assert(issues.unlisted['prettier.config.js']['prettier-plugin-astro']);\n  assert(issues.unlisted['package.json']['@company/prettier-config']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    unlisted: 3,\n    processed: 1,\n    total: 1,\n  });\n});\n\ntest('Find dependencies with the Prettier plugin (.json5 config)', async () => {\n  const cwd = resolve('fixtures/plugins/prettier-json5');\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['prettier']);\n  assert(issues.unlisted['.prettierrc.json5']['prettier-plugin-tailwindcss']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    unlisted: 1,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/prisma.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/prisma');\n\ntest('Find dependencies with the Prisma plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['lint-staged']);\n  assert(issues.binaries['package.json']['lint-staged']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    binaries: 1,\n    processed: 13,\n    total: 13,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/prisma2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/prisma2');\n\ntest('Find default entries with the Prisma plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/qwik.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/qwik');\nconst cwdCustomDirs = resolve('fixtures/plugins/qwik-custom-dirs');\n\ntest('Find dependencies with the Qwik plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 8,\n    total: 8,\n  });\n});\n\ntest('Find dependencies with the Qwik plugin (custom srcDir and routesDir[])', async () => {\n  const options = await createOptions({ cwd: cwdCustomDirs });\n  const { issues, counters } = await main(options);\n\n  assert(!('docs/extra-pages/index.tsx' in issues.files));\n  assert(!('docs/pages/guide.mdx' in issues.files));\n  assert(!('docs/components/mdx-note.tsx' in issues.files));\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 10,\n    total: 10,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/raycast.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/raycast');\n\ntest('Treat Raycast commands and tools as entries from package.json', async () => {\n  const options = await createOptions({ cwd, isStrict: true });\n  const { counters, issues } = await main(options);\n\n  assert(!('src/search-bookmarks.tsx' in issues.files));\n  assert(!('src/shared/load-bookmarks.ts' in issues.files));\n  assert(!('src/tools/organize-tabs.ts' in issues.files));\n  assert('src/unused.ts' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    total: 4,\n    processed: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/react-cosmos.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/react-cosmos');\n\ntest('Find dependencies with the react-cosmos plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/react-native.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/react-native');\n\ntest('Find dependencies with the React Native plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.dependencies['package.json']['react-native']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    dependencies: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/react-router.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport os from 'node:os';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst isWindows = os.platform() === 'win32';\n\ntest('Find dependencies with the react-router plugin', async () => {\n  const cwd = resolve('fixtures/plugins/react-router');\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 10,\n    total: 10,\n    // There is a bug with routes that include () on Windows so they will not be found there, revert when\n    // the bug is fixed\n    files: isWindows ? 1 : 0,\n  });\n});\n\ntest('Find dependencies with the react-router plugin [with custom server entry]', async () => {\n  const cwd = resolve('fixtures/plugins/react-router-with-server-entry');\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 5,\n    total: 5,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/relay.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/relay');\n\ntest('Find dependencies with the relay plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert('unused.ts' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    processed: 5,\n    total: 5,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/relay2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/relay2');\n\ntest('Find dependencies with the relay plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert('unused.ts' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    processed: 5,\n    total: 5,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/release-it.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/release-it');\n\ntest('Find dependencies with the Release It plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['release-it']);\n  assert(issues.unlisted['.release-it.json']['@release-it/bumper']);\n  assert(issues.unlisted['.release-it.json']['@release-it/conventional-changelog']);\n  assert(issues.binaries['.release-it.json']['from-hook']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 1,\n    devDependencies: 1,\n    unlisted: 2,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/remark.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/remark');\n\ntest('Find dependencies with the Remark plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['remark-cli']);\n  assert(issues.unresolved['package.json']['remark-preset-webpro']);\n  assert(issues.binaries['package.json']['remark']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 1,\n    devDependencies: 1,\n    unresolved: 1,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/remix.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/remix');\n\ntest('Find dependencies with the Remix plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['npm-run-all']);\n\n  assert(issues.unresolved['app/root.tsx']['./session.server']);\n\n  assert(issues.unlisted['package.json']['dotenv']);\n  assert(issues.binaries['package.json']['run-s']);\n  assert(issues.binaries['package.json']['run-p']);\n  assert(issues.binaries['package.json']['cross-env']);\n  assert(issues.binaries['package.json']['tailwindcss']);\n  assert(issues.binaries['package.json']['prisma']);\n\n  assert(issues.unlisted['app/entry.client.tsx']['@remix-run/react']);\n  assert(issues.unlisted['app/entry.client.tsx']['react']);\n  assert(issues.unlisted['app/entry.client.tsx']['react-dom']);\n\n  assert(issues.unlisted['app/entry.server.tsx']['@remix-run/node']);\n  assert(issues.unlisted['app/entry.server.tsx']['@remix-run/react']);\n  assert(issues.unlisted['app/entry.server.tsx']['react-dom']);\n\n  assert(issues.unlisted['app/root.tsx']['@remix-run/node']);\n  assert(issues.unlisted['app/root.tsx']['@remix-run/react']);\n\n  assert(issues.unlisted['app/utils.ts']['@remix-run/react']);\n  assert(issues.unlisted['app/utils.ts']['react']);\n\n  assert(issues.unlisted['app/routes/index.tsx']['@remix-run/react']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    unlisted: 12,\n    binaries: 5,\n    unresolved: 1,\n    processed: 8,\n    total: 8,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/rollup.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/rollup');\n\ntest('Find dependencies with the Rollup plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['rollup.config.js']['rollup-plugin-terser']);\n  assert(issues.unlisted['rollup.config.js']['@rollup/plugin-node-resolve']);\n  assert(issues.unlisted['rollup.config.js']['@rollup/plugin-commonjs']);\n  assert(issues.unlisted['rollup.config.js']['acorn-jsx']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 0,\n    unlisted: 4,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/rsbuild.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/rsbuild');\n\ntest('Find dependencies with the rsbuild plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 1,\n    processed: 12,\n    total: 12,\n  });\n});\n\ntest('Find dependencies with the rsbuild plugin (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 11,\n    total: 11,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/rslib.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/rslib');\n\ntest('Find dependencies with the rslib plugin', async () => {\n  const options = await createOptions({ cwd, isStrict: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/rspack.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/rspack');\n\ntest('Find dependencies with the rspack plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/rstest.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { test } from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/rstest');\n\ntest('Find dependencies with the rstest plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters, issues } = await main(options);\n\n  assert('not-included.ts' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/rstest2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { test } from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/rstest2');\n\ntest('Find dependencies with the rstest plugin (2)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters, issues } = await main(options);\n\n  assert('not-included.spec.ts' in issues.files);\n  assert('excluded.test.ts' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 2,\n    processed: 5,\n    total: 5,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/sanity.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/sanity');\n\ntest('Find dependencies with the Sanity plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/semantic-release.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/semantic-release');\n\ntest('Find dependencies with the semantic-release plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['semantic-release']);\n\n  assert(issues.unlisted['.releaserc']['@semantic-release/changelog']);\n  assert(issues.unlisted['.releaserc']['@semantic-release/git']);\n\n  assert(issues.unlisted['package.json']['@semantic-release/changelog']);\n  assert(issues.unlisted['package.json']['@semantic-release/git']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    unlisted: 4,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/sentry.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/sentry');\n\ntest('Find dependencies with the Sentry plugin (non-production)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, { ...baseCounters, processed: 3, total: 3 });\n});\n\ntest('Find dependencies with the Sentry plugin (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, { ...baseCounters, processed: 3, total: 3 });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/simple-git-hooks.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/simple-git-hooks');\n\ntest('Find dependencies with the simple-git-hooks plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.binaries['package.json']['eslint']);\n  assert(issues.binaries['package.json']['lint-staged']);\n  assert(issues.devDependencies['package.json']['lint-staged']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 2,\n    devDependencies: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/size-limit.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/size-limit');\n\ntest('Find dependencies with the size-limit plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/sst.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/sst');\n\ntest('Find dependencies with the sst plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['handlers/other-auth.ts']['sst-auth-handler-dep']);\n  assert(issues.unlisted['handlers/auth.ts']['sst-auth-dep']);\n  assert(issues.unlisted['handlers/some-route.ts']['sst-some-dep']);\n  assert(issues.unlisted['stacks/AuthHandlerStack.ts']['sst-auth-handler-stack-dep']);\n  assert(issues.unlisted['stacks/AuthStack.ts']['sst-auth-stack-dep']);\n  assert(issues.unlisted['sst.config.ts']['sst-config-dep']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unlisted: 6,\n    processed: 6,\n    total: 6,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/sst2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/sst2');\n\ntest('Find dependencies with the sst plugin (2)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/starlight.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/starlight');\n\ntest('Find dependencies with the starlight plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/storybook.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/storybook');\n\ntest('Find dependencies with the Storybook plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['storybook-addon-performance']);\n  assert(issues.unlisted['.storybook/main.js']['@storybook/addon-knobs']);\n  assert(issues.unlisted['.storybook/main.js']['@storybook/builder-webpack5']);\n  assert(issues.unlisted['.storybook/main.js']['@storybook/manager-webpack5']);\n  assert(issues.unlisted['.storybook/main.js']['@storybook/react-webpack5']);\n  assert(issues.unlisted['.storybook/preview.js']['cypress-storybook']);\n  assert(issues.unlisted['.storybook/vitest.setup.ts']['@storybook/your-framework']);\n  assert(issues.unresolved['.storybook/main.js']['storybook-addon-export-to-codesandbox']);\n  assert(issues.binaries['package.json']['storybook']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    unlisted: 6,\n    unresolved: 1,\n    binaries: 1,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/storybook2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/storybook2');\n\ntest('Find dependencies with the Storybook plugin (2)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 6,\n    total: 6,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/stryker.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/stryker');\n\ntest('Find dependencies with the Stryker plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.dependencies['package.json']['@stryker-mutator/core']);\n  assert(issues.unlisted['.stryker.conf.js']['@stryker-mutator/mocha-runner']);\n  assert(issues.unlisted['.stryker.conf.js']['@stryker-mutator/typescript-checker']);\n  assert(issues.unlisted['.stryker.conf.js']['@stryker-mutator/jasmine-framework']);\n  assert(issues.unlisted['.stryker.conf.js']['@stryker-mutator/karma-runner']);\n  assert(issues.unlisted['stryker.conf.cjs']['@stryker-mutator/mocha-runner']);\n  assert(issues.unlisted['stryker.conf.cjs']['@stryker-mutator/typescript-checker']);\n  assert(issues.unlisted['stryker.conf.cjs']['@stryker-mutator/jasmine-framework']);\n  assert(issues.unlisted['stryker.conf.cjs']['@stryker-mutator/karma-runner']);\n  assert(issues.unlisted['stryker.conf.json']['@stryker-mutator/karma-runner']);\n  assert(issues.unlisted['stryker.conf.json']['@stryker-mutator/typescript-checker']);\n  assert(issues.unlisted['stryker.conf.mjs']['@stryker-mutator/mocha-runner']);\n  assert(issues.unlisted['stryker.conf.mjs']['@stryker-mutator/typescript-checker']);\n  assert(issues.unlisted['stryker.conf.mjs']['@stryker-mutator/jasmine-framework']);\n  assert(issues.unlisted['stryker.conf.mjs']['@stryker-mutator/karma-runner']);\n  assert(issues.binaries['package.json']['stryker']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 1,\n    dependencies: 1,\n    unlisted: 14,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/stylelint.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/stylelint');\n\ntest('Find dependencies with the stylelint plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['stylelint']);\n  assert(issues.unresolved['.stylelintrc']['postcss-less']);\n  assert(issues.unresolved['.stylelintrc']['stylelint-config-standard']);\n  assert(issues.unresolved['.stylelintrc']['stylelint-order']);\n  assert(issues.unresolved['.stylelintrc']['stylelint-config-html/html']);\n  assert(issues.unresolved['.stylelintrc']['./myExtendableConfig']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    unresolved: 5,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/stylelint2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/stylelint2');\n\ntest('Find dependencies with the stylelint plugin (2)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 5,\n    total: 5,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/stylelint3.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/stylelint3');\n\ntest('Find dependencies with the stylelint plugin (3)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/svelte.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/svelte');\n\ntest('Use compilers (svelte)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['svelte']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    processed: 18,\n    total: 18,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/sveltekit.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/sveltekit');\n\ntest('Find dependencies with the SvelteKit plugin (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 16,\n    total: 16,\n  });\n});\n\ntest('Find dependencies with the SvelteKit plugin (development)', async () => {\n  const options = await createOptions({ cwd, isProduction: false });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['svelte']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    processed: 18,\n    total: 18,\n  });\n});\n\ntest('Find dependencies with the SvelteKit plugin (custom lib path)', async () => {\n  const cwd2 = resolve('fixtures/plugins/sveltekit2');\n  const options = await createOptions({ cwd: cwd2, isProduction: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/svgo.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/svgo');\n\ntest('Find dependencies with the SVGO plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/svgr.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/svgr');\n\ntest('Find dependencies with the svgr plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/swc.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/swc');\n\ntest('Find dependencies with the swc plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/syncpack.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/syncpack');\n\ntest('Find dependencies with the Syncpack plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/tailwind.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/tailwind');\n\ntest('Find dependencies with the Tailwind plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/tailwind2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/tailwind2');\n\ntest('Find dependencies with the Tailwind plugin (2)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/tanstack-router.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/tanstack-router');\n\ntest('Find dependencies with the tanstack-router plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['src/routes/__root.tsx']['@tanstack/router-devtools']);\n\n  assert(issues.dependencies['package.json']['react']);\n  assert(issues.dependencies['package.json']['react-dom']);\n  assert(issues.devDependencies['package.json']['@tanstack/router-cli']);\n  assert(issues.devDependencies['package.json']['vite']);\n\n  assert(!issues.exports['src/routes/__root.tsx']?.['Route']);\n  assert(!issues.exports['src/routes/index.tsx']?.['Route']);\n  assert(!issues.exports['src/routes/posts/$postId.tsx']?.['Route']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    dependencies: 2,\n    devDependencies: 2,\n    unlisted: 1,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/taskfile.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/taskfile');\n\ntest('Find dependencies with the taskfile plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unresolved['Taskfile.yml']['esbuild-register']);\n  assert(issues.binaries['Taskfile.yml']['eslint']);\n  assert(issues.binaries['Taskfile.yml']['knip']);\n  assert(issues.binaries['Taskfile.yml']['prettier']);\n  assert(issues.binaries['Taskfile.yml']['test-command-object-binary']);\n  assert(issues.binaries['Taskfile.yml']['deferred-binary']);\n  assert(issues.binaries['Taskfile.yml']['for-loop-binary']);\n  assert(issues.binaries['nested/Taskfile.yml']['some-nonexistent-binary']);\n  assert(issues.binaries['shared/Taskfile.yml']['another-nonexistent-binary']);\n  assert(issues.binaries['shared/deep/Taskfile.yml']['yet-another-nonexistent-binary']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    // case-sensitivity: fast-glob returns two files (taskfile.yml and Taskfile.yml) while there's only one\n    binaries: process.platform === 'darwin' || process.platform === 'win32' ? 15 : 9,\n    unresolved: process.platform === 'darwin' || process.platform === 'win32' ? 2 : 1,\n    processed: 7,\n    total: 7,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/taskfile2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/taskfile2');\n\ntest('Find dependencies with custom taskfile path patterns', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.binaries['custom-taskfile.yml']['jest']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 1,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/travis.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/travis');\n\ntest('Find dependencies with the travis plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.binaries['.travis.yml']['patch-version']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 1,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/tsdown.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/tsdown');\n\ntest('Find dependencies with the tsdown plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 8,\n    total: 8,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/tsgo.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/tsgo');\n\ntest('Find dependencies with the TypeScript plugin when using @typescript/native-preview', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.binaries['package.json']['tsgo']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 1,\n    devDependencies: 1,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/tsup.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/tsup');\n\ntest('Find dependencies with the tsup plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 8,\n    total: 8,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/tsx.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/tsx');\n\ntest('Find dependencies with the tsx plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/typedoc.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/typedoc');\n\ntest('Find dependencies with the typedoc plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['typedoc']);\n  assert(issues.unlisted['typedoc.json']['@appium/typedoc-plugin-appium']);\n  assert(issues.unresolved['typedoc.json']['typedoc-plugin-expand-object-like-types']);\n  assert(issues.unresolved['package.json']['typedoc-plugin-umami']);\n  assert(issues.unresolved['tsconfig.json']['typedoc-plugin-zod']);\n  assert(issues.unresolved['typedoc.json']['./dist/index.cjs']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    unlisted: 1,\n    unresolved: 4,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/typescript.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/typescript');\n\ntest('Find dependencies with the TypeScript plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unresolved['tsconfig.json']['typescript-eslint-language-service']);\n  assert(issues.unresolved['tsconfig.json']['ts-graphql-plugin']);\n  assert(issues.unresolved['tsconfig.json']['tslib']); // resolved up to dep of knip itself\n  assert(issues.unlisted['tsconfig.jsx-import-source-preact.json']['preact']);\n  assert(issues.unresolved['tsconfig.jsx-import-source-preact.json']['preact']);\n  assert(issues.unresolved['tsconfig.jsx-import-source-react.json']['vitest/globals']);\n  assert(issues.unlisted['tsconfig.jsx-import-source-react.json']['hastscript']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 1,\n    unlisted: 2,\n    unresolved: 5,\n    processed: 0,\n    total: 0,\n  });\n});\n\ntest('Find dependencies with the TypeScript plugin (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['tsconfig.jsx-import-source-preact.json']['preact']);\n  assert(issues.unlisted['tsconfig.jsx-import-source-react.json']['hastscript']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unlisted: 2,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/typescript2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/typescript2');\n\ntest('Find local extends with the TypeScript plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/unbuild.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/unbuild');\n\ntest('Find dependencies with the unbuild plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/unocss.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/unocss');\n\ntest('Find dependencies with the unocss plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/vercel-og.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/vercel-og');\n\ntest('Find dependencies with the @vercel/og plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/vike.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/vike');\n\ntest('Find dependencies with the vike plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 22,\n    total: 22,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/vite.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/vite');\n\ntest('Find dependencies with the Vite plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/vite2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/vite2');\n\ntest('Should not find issues with extensions when build_type is desktop', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 2,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/vite3.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/vite3');\n\ntest('Find entry from Vite index.html module script', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(!('src/App.tsx' in issues.files));\n  assert('src/unused.ts' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/vite4.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/vite4');\n\ntest('Find entry from Vite index.html with custom root', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(!('app/main.ts' in issues.files));\n  assert(!('app/component.ts' in issues.files));\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/vitepress.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/vitepress');\n\ntest('Find dependencies with the vitepress plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/vitest-npm-script.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/vitest-npm-script');\n\ntest('Find dependencies with the Vitest plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['vitest']);\n  assert(issues.unlisted['vitest.config.ts']['@vitest/coverage-v8']);\n  assert(issues.binaries['package.json']['vitest']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 1,\n    devDependencies: 1,\n    unlisted: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/vitest.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/vitest');\n\ntest('Find dependencies with the Vitest plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['vite.config.ts']['@vitest/coverage-c8']);\n  assert(issues.unlisted['vite.config.ts']['@edge-runtime/vm']);\n  assert(issues.unlisted['vitest.workspace.ts']['@edge-runtime/vm']);\n  assert(issues.unlisted['vitest-default-coverage.config.ts']['jsdom']);\n  assert(issues.unlisted['vitest-default-coverage.config.ts']['@vitest/coverage-v8']);\n  assert(issues.unlisted['vitest.config.ts']['happy-dom']);\n  assert(issues.unlisted['vitest.config.ts']['@vitest/coverage-istanbul']);\n  assert(issues.unlisted['package.json']['@vitest/coverage-istanbul']);\n  assert(issues.unlisted['package.json']['vitest-sonar-reporter']);\n  assert(issues.unlisted['package.json']['jsdom']);\n  assert(issues.unlisted['package.json']['vue-tsc']);\n  assert(issues.unresolved['vitest.config.ts']['setup.js']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unlisted: 11,\n    unresolved: 1,\n    processed: 11,\n    total: 11,\n  });\n});\n\ntest('Find dependencies with the Vitest plugin (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { issues, counters } = await main(options);\n\n  assert('src/setupTests.ts' in issues.files);\n  assert.equal(Object.keys(issues.files).length, 1);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/vitest2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/vitest2');\n\ntest('Find dependencies with the Vitest plugin (2)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert('src/basic.spec.ts' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/vitest3.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/vitest3');\n\ntest('Find dependencies with the Vitest plugin (3)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['@swc/plugin-styled-components']);\n  assert(issues.unlisted['vitest.config.ts']['@vitest/coverage-v8']);\n  assert(issues.unlisted['vitest.config.ts']['jsdom']);\n  assert(issues.unresolved['vitest.config.ts']['./src/setupTests.tsx']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    unlisted: 2,\n    unresolved: 1,\n    processed: 5,\n    total: 5,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/vitest4.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/vitest4');\n\ntest('Find dependencies with the Vitest plugin (4)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert('src/unused.test.ts' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/vitest5.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/vitest5');\n\ntest('Find dependencies with the Vitest plugin (5)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/vitest6.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/vitest6');\n\ntest('Find dependencies with the Vitest plugin (6)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert('src/unused.test.ts' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/vitest7.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/vitest7');\n\ntest('Find dependencies with the Vitest plugin (7)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 0,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/vitest8.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/vitest8');\n\ntest('Find dependencies with the Vitest plugin (8)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unresolved['vitest.config.ts']['./vitest.integration.setup.mjs']);\n  assert(issues.unresolved['vitest.config.ts']['./vitest.unit.setup.ts']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unlisted: 0,\n    unresolved: 2,\n    processed: 5,\n    total: 5,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/vitest9.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/vitest9');\n\ntest('Find dependencies in vitest configuration (projects with inline and external)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unlisted['vitest.config.ts']['jsdom']);\n  assert(issues.unlisted['packages/client/vitest.config.e2e.ts']['happy-dom']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 0,\n    devDependencies: 0,\n    unlisted: 2,\n    unresolved: 0,\n    processed: 7,\n    total: 7,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/vue-webpack.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/vue-webpack');\n\ntest('Support compiler functions in config (vue + webpack)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 6,\n    total: 6,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/vue.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/vue');\n\ntest('Support compiler functions in config (vue)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/webdriver-io.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/webdriver-io');\n\ntest('Find dependencies with the webdriver-io plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/webpack-cli.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/webpack-cli');\n\ntest('Find dependencies with the Webpack plugin (webpack-cli only)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/webpack-re-exports.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/webpack-reexport');\n\ntest('Handle re-exported webpack config', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters, configurationHints } = await main(options);\n\n  assert(!issues.unlisted?.['webpack.config.js']?.['style-loader']);\n  assert(!issues.unlisted?.['webpack.config.js']?.['css-loader']);\n\n  assert.deepEqual(\n    configurationHints.filter(hint => hint.type === 'ignoreDependencies'),\n    [{ type: 'ignoreDependencies', workspaceName: '.', identifier: 'style-loader' }]\n  );\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    binaries: 1,\n    processed: 2,\n    total: 2,\n  });\n});\n\ntest('Handle re-exported webpack config (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { issues, counters, configurationHints } = await main(options);\n\n  assert(!issues.unlisted?.['webpack.config.js']?.['style-loader']);\n  assert(!issues.unlisted?.['webpack.config.js']?.['css-loader']);\n\n  assert.deepEqual(\n    configurationHints.filter(hint => hint.type === 'ignoreDependencies'),\n    [{ type: 'ignoreDependencies', workspaceName: '.', identifier: 'style-loader' }]\n  );\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/webpack.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/webpack');\n\ntest('Find dependencies with the Webpack plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert('src/unused.ts' in issues.files);\n  assert(!('src/routes.ts' in issues.files));\n  assert(issues.devDependencies['package.json']['@babel/plugin-proposal-object-rest-spread']);\n  assert(issues.devDependencies['package.json']['buffer']);\n  assert(issues.unresolved['webpack.config.js']['svgo-loader']);\n  assert(issues.unlisted['webpack.dev.js']['eslint-webpack-plugin']);\n  assert(issues.unlisted['webpack.prod.js']['terser-webpack-plugin']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 2,\n    devDependencies: 2,\n    unlisted: 2,\n    unresolved: 1,\n    processed: 17,\n    total: 17,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/webpack2.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/webpack2');\n\ntest('Find dependencies with the Webpack plugin (2)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n\ntest('Find dependencies with the Webpack plugin (2) (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/wireit.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport { join } from '../../src/util/path.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/wireit');\n\ntest('Find no dependencies when the wireit configuration is missing', async () => {\n  const options = await createOptions({ cwd: join(cwd, 'apps/missing') });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n  });\n});\n\ntest('Find dependencies with the wireit plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.binaries['package.json']['tsc']);\n  assert(issues.binaries['apps/example-configuration/package.json']['rollup']);\n  assert(issues.binaries['apps/example-configuration/package.json']['tsc']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 3,\n    devDependencies: 0,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/wrangler.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/wrangler');\n\ntest('Find dependencies with the wrangler plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/xo.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/xo');\n\ntest('Find dependencies with the xo plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.unresolved['.xo-config.js']['eslint-plugin-unused-imports']);\n  assert(issues.unlisted['xo.config.cjs']['glob']);\n  assert(issues.unresolved['package.json']['eslint-plugin-eslint-comments']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    unlisted: 1,\n    unresolved: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/yarn-berry.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/yarn-berry');\n\ntest('Find dependencies with the yarn plugin (Berry)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/yarn.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/yarn');\n\ntest('Find dependencies with the yarn plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(Object.keys(issues.files).length === 0);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/plugins/yorkie.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../../src/index.ts';\nimport baseCounters from '../helpers/baseCounters.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/plugins/yorkie');\n\ntest('Find dependencies with the yorkie plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['lint-staged']);\n  assert(issues.binaries['package.json']['eslint']);\n  assert(issues.binaries['package.json']['markdownlint']);\n  assert(issues.binaries['package.json']['svgo']);\n  assert(issues.binaries['package.json']['lint-staged']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 4,\n    devDependencies: 1,\n    processed: 0,\n    total: 0,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/pragma.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/pragma');\n\ntest('Support all sorts of pragmas/docblocks', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 5,\n    total: 5,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/re-exports-aliased-ns.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/re-exports-aliased-ns');\n\ntest('Find exports through re-exported aliased namespace', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['2-second.ts']['NS.second']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 1,\n    processed: 5,\n    total: 5,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/re-exports-cjs.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/re-exports-cjs');\n\ntest('Ignore re-exports from entry files (CommonJS', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/re-exports-deep.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/re-exports-deep');\n\ntest('Find deep re-exports', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 7,\n    total: 7,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/re-exports-default-renamed-deep.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/re-exports-default-renamed-deep');\n\ntest('Find exports through re-exported aliased exports (deep)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 12,\n    total: 12,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/re-exports-default-renamed.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/re-exports-default-renamed');\n\ntest('Find exports through re-exported aliased exports', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 8,\n    total: 8,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/re-exports-destructure-spread.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/re-exports-destructure-spread');\n\ntest('Find exports through namespace, spread, destructure', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['animal.ts']['fly']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 1,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/re-exports-enum-members-workspace.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/re-exports-enum-members-workspace');\n\ntest('Find unused re-exported enum members across workspaces', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    enumMembers: 2,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/re-exports-enum-unused.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/re-exports-enum-unused');\n\ntest('Find default re-exported unused enum', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    enumMembers: 2,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/re-exports-enum.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/re-exports-enum');\n\ntest('Find default re-exported enum', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/re-exports-export-declaration.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/re-exports-export-declaration');\n\ntest('Find re-exports through namespaces (1)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 6,\n    total: 6,\n  });\n});\n\ntest('Find re-exports through namespaces (1) including entry files', async () => {\n  const options = await createOptions({ cwd, isIncludeEntryExports: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 6,\n    total: 6,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/re-exports-export-ns.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/re-exports-export-ns');\n\ntest('Find re-exports through namespaces (2)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['4-leaf-C.ts']['NS.fnC']);\n  assert(issues.enumMembers['4-leaf-A.ts']['EnumA.UnusedProp']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    enumMembers: 1,\n    exports: 1,\n    processed: 7,\n    total: 7,\n  });\n});\n\ntest('Find re-exports through namespaces (2) including entry files', async () => {\n  const options = await createOptions({ cwd, isIncludeEntryExports: true });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['index.ts']['default']);\n  assert(issues.exports['4-leaf-C.ts']['NS.fnC']);\n  // assert(issues.nsExports['1-root.ts']['exportedFnOnNs']); // only when `nsExports` is included\n  assert(issues.enumMembers['4-leaf-A.ts']['EnumA.UnusedProp']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 2,\n    enumMembers: 1,\n    processed: 7,\n    total: 7,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/re-exports-ignore-exports-used-in-file.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/re-exports-ignore-exports-used-in-file');\n\ntest('Find unused export through re-export in entry file (includeEntryExports/ignoreExportsUsedInFile)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 1,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/re-exports-ns-member.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/re-exports-ns-member');\n\ntest('Find destructured props of member-accessed imported symbol', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['member-ab.ts']['NS.unusedMemberA']);\n  assert(issues.exports['member-cd.ts']['pseudo.unusedMemberC']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 2,\n    processed: 6,\n    total: 6,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/re-exports-pseudo.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/re-exports-pseudo');\n\ntest('Find export refs through aliased namespace re-exports', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/re-exports-public.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/re-exports-public');\n\ntest('Ignore re-exports from included entry files', async () => {\n  const options = await createOptions({ cwd, isIncludeEntryExports: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/re-exports-recursive.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/re-exports-recursive');\n\ntest('Should not stack overflow on recursive re-exports', async () => {\n  const options = await createOptions({ cwd });\n  await assert.doesNotReject(main(options));\n});\n"
  },
  {
    "path": "packages/knip/test/re-exports-renamed.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/re-exports-renamed');\n\ntest('Find renamed exports', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/re-exports-spread.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/re-exports-spread');\n\ntest('Find exports through namespace and spread', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['animal.ts']['fly']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 1,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/re-exports-twice.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/re-exports-twice');\n\ntest('Find exports through re-exported aliased namespace (twice)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/re-exports-with-decorator.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/re-exports-with-decorator');\n\ntest('Ignore re-exports from entry file (w/ decorator)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/re-exports.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/re-exports');\n\ntest('Ignore re-exports from entry files', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n\ntest('Ignore re-exports from entry files (include entry + ignore @public)', async () => {\n  const options = await createOptions({ cwd, isIncludeEntryExports: true });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['1-entry.ts']['somethingNotToIgnore']);\n  assert(issues.exports['3-re-export-named.ts']['somethingNotToIgnore']);\n  assert(issues.exports['4-my-module.ts']['somethingNotToIgnore']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 3,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/rules.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport { getValuesByKeyDeep } from '../src/util/object.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/rules');\n\ntest('Respect warnings in rules', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  const severities = getValuesByKeyDeep(issues, 'severity');\n\n  assert(severities.every(severity => severity === 'warn'));\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    dependencies: 1,\n    devDependencies: 1,\n    optionalPeerDependencies: 1,\n    unlisted: 1,\n    binaries: 1,\n    unresolved: 1,\n    exports: 2,\n    types: 2,\n    duplicates: 1,\n    enumMembers: 1,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/script-visitors-bun.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/script-visitors-bun');\n\ntest('Find dependencies with custom script visitors (bun)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.binaries['script.ts']['oh-my']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n    binaries: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/script-visitors-execa.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/script-visitors-execa');\n\ntest('Find dependencies with custom script visitors (execa)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  // Let's start out conservatively\n  // assert(issues.unresolved['options.mjs']['hydrate.js']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 6,\n    total: 6,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/script-visitors-zx.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/script-visitors-zx');\n\ntest('Find dependencies with custom script visitors (zx)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/self-reference-from-plugin.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/self-reference-from-plugin');\n\ntest('Allows self-references from plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    processed: 2,\n    total: 2,\n  });\n});\n\ntest('Allows self-references from plugin (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n\ntest('Allows self-references from plugin (strict)', async () => {\n  const options = await createOptions({ cwd, isStrict: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/self-reference.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/self-reference');\n\ntest('Allows self-references', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert.equal(Object.keys(issues.unlisted).length, 0);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n\ntest('Allows self-references (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { issues, counters } = await main(options);\n\n  assert.equal(Object.keys(issues.unlisted).length, 0);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n\ntest('Allows self-references (strict)', async () => {\n  const options = await createOptions({ cwd, isStrict: true });\n  const { issues, counters } = await main(options);\n\n  assert.equal(Object.keys(issues.unlisted).length, 0);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/session/session.contention.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { test } from 'node:test';\nimport { buildFileDescriptor } from '../../src/session/file-descriptor.ts';\nimport { createSession } from '../../src/session/session.ts';\nimport { join } from '../../src/util/path.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\nimport { describeFile } from './util.ts';\n\nconst cwd = resolve('fixtures/session');\n\ntest('reports branching contention for all files in chain', async () => {\n  const left = await describeFile(cwd, 'diamond-left.ts');\n  const right = await describeFile(cwd, 'diamond-right.ts');\n  const base = await describeFile(cwd, 'diamond-base.ts');\n\n  assert.ok(left.file.contention.DIAMOND);\n  assert.ok(right.file.contention.DIAMOND);\n  assert.ok(base.file.contention.DIAMOND);\n\n  assert.ok(\n    left.file.contention.DIAMOND.branching.includes(join(left.cwd, 'diamond-top.ts')),\n    'diamond-left.ts should include diamond-top.ts in branching'\n  );\n});\n\ntest('identifies conflict contention', async () => {\n  const { file } = await describeFile(cwd, 'overload-1.ts');\n\n  const overload = file.contention.OVERLOAD;\n  assert.ok(overload, 'missing OVERLOAD contention summary');\n\n  const expected = [join(cwd, 'overload-1.ts'), join(cwd, 'overload-2.ts'), join(cwd, 'overload-3.ts')];\n\n  assert.deepEqual(overload.conflict, expected);\n  assert.deepEqual(overload.branching, []);\n});\n\ntest('propagates star re-exports for diamond-top.ts', async () => {\n  const { file } = await describeFile(cwd, 'diamond-top.ts');\n\n  const exportedIds = file.exports.map(entry => entry.identifier).sort();\n  assert.ok(exportedIds.includes('DIAMOND'));\n\n  const diamondExport = file.exports.find(entry => entry.identifier === 'DIAMOND');\n  assert.ok(diamondExport);\n  assert.equal(diamondExport.importLocations.length > 0, true);\n});\n\ntest('allows same identifier under different namespaces without contention', async () => {\n  const host = await describeFile(cwd, 'host.ts');\n  const worker = await describeFile(cwd, 'worker.ts');\n\n  assert.equal(host.file.contention.start, undefined);\n  assert.equal(worker.file.contention.start, undefined);\n});\n\ntest('detects circular dependency c → a → b → c', async () => {\n  const { file } = await describeFile(cwd, 'c.ts');\n\n  assert.ok(file.cycles.length > 0);\n\n  const canonicalCycle = [join(cwd, 'c.ts'), join(cwd, 'a.ts'), join(cwd, 'b.ts'), join(cwd, 'c.ts')];\n\n  const hasCycle = file.cycles.some(\n    cycle => cycle.length === canonicalCycle.length && cycle.every((value, idx) => value === canonicalCycle[idx])\n  );\n  assert.ok(hasCycle);\n});\n\ntest('false disables contention reporting', async () => {\n  const options = await createOptions({ cwd, isSession: true });\n  const session = await createSession(options);\n\n  const withContention = session.describeFile(join(options.cwd, 'diamond-top.ts'));\n  assert.ok(withContention);\n  assert.ok(withContention.contention.DIAMOND);\n\n  const { session: sessionHandler } = await (await import('../../src/run.ts')).run(options);\n  assert.ok(sessionHandler);\n\n  const graph = sessionHandler.getGraph();\n  const entryPaths = sessionHandler.getEntryPaths();\n  const filePath = join(options.cwd, 'diamond-top.ts');\n\n  const withoutContention = buildFileDescriptor(filePath, options.cwd, graph, entryPaths, {\n    isShowContention: false,\n  });\n  assert.ok(withoutContention);\n  assert.deepEqual(withoutContention.contention, Object.create(null));\n});\n"
  },
  {
    "path": "packages/knip/test/session/session.file-changes.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { writeFileSync, unlinkSync } from 'node:fs';\nimport { test } from 'node:test';\nimport { createSession } from '../../src/session/session.ts';\nimport type { Issues } from '../../src/types/issues.ts';\nimport { join } from '../../src/util/path.ts';\nimport { createOptions } from '../helpers/create-options.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/session');\n\nconst getUnusedFilePaths = (issues: Issues) =>\n  Object.values(issues.files).flatMap(issue => Object.values(issue).map(i => i.filePath));\n\nconst cleanup = (filePaths: string[]) => {\n  for (const filePath of filePaths) {\n    try {\n      unlinkSync(filePath);\n    } catch {}\n  }\n};\n\ntest('ignores added file without compiler outside project glob', async () => {\n  const options = await createOptions({ cwd, isSession: true });\n  const session = await createSession(options);\n\n  const cssFilePath = join(cwd, 'style.css');\n  writeFileSync(cssFilePath, 'body { color: red; }');\n\n  try {\n    await session.handleFileChanges([{ type: 'added', filePath: cssFilePath }]);\n\n    const unusedFiles = getUnusedFilePaths(session.getIssues().issues);\n    assert.ok(!unusedFiles.includes(cssFilePath), 'CSS file should not be reported as unused');\n  } finally {\n    cleanup([cssFilePath]);\n  }\n});\n\ntest('reports added markdown file when compiler is enabled', async () => {\n  const options = await createOptions({ cwd, isSession: true });\n  const session = await createSession(options);\n\n  const mdFilePath = join(cwd, 'notes.md');\n  writeFileSync(mdFilePath, '# Notes');\n\n  try {\n    await session.handleFileChanges([{ type: 'added', filePath: mdFilePath }]);\n\n    const unusedFiles = getUnusedFilePaths(session.getIssues().issues);\n    assert.ok(unusedFiles.includes(mdFilePath), 'Markdown file with compiler should be reported as unused');\n  } finally {\n    cleanup([mdFilePath]);\n  }\n});\n\ntest('handles modified non-project files gracefully', async () => {\n  const options = await createOptions({ cwd, isSession: true });\n  const session = await createSession(options);\n\n  const cssFilePath = join(cwd, 'style.css');\n  writeFileSync(cssFilePath, 'body { color: red; }');\n\n  try {\n    const result = await session.handleFileChanges([{ type: 'modified', filePath: cssFilePath }]);\n    assert.ok(result === undefined || typeof result === 'object', 'should not throw for non-project modified files');\n  } finally {\n    cleanup([cssFilePath]);\n  }\n});\n"
  },
  {
    "path": "packages/knip/test/session/session.re-exports.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { test } from 'node:test';\nimport { join } from '../../src/util/path.ts';\nimport { resolve } from '../helpers/resolve.ts';\nimport { describeFile } from './util.ts';\n\nconst cwd = resolve('fixtures/session-re-exports');\n\ntest('counts imports via re-exports for implementation file', async () => {\n  const { file } = await describeFile(cwd, 'pkg/public/implementation.ts');\n  const box = file.exports.find(entry => entry.identifier === 'Box');\n  assert.ok(box);\n  const importFilePaths = box.importLocations.map(loc => loc.filePath);\n  assert.ok(importFilePaths.includes(join(cwd, 'pkg/internal/consumer-1.ts')));\n  assert.ok(importFilePaths.includes(join(cwd, 'app/consumer-2.ts')));\n  assert.ok(importFilePaths.includes(join(cwd, 'app/consumer-3.ts')));\n  assert.equal(box.importLocations.length, 3);\n});\n\ntest('counts imports via re-exports from barrel file', async () => {\n  const { file } = await describeFile(cwd, 'pkg/public/barrel.ts');\n  const box = file.exports.find(entry => entry.identifier === 'Box');\n  assert.ok(box);\n  const importFilePaths = box.importLocations.map(loc => loc.filePath);\n  assert.equal(box.importLocations.length, 2);\n  assert.ok(importFilePaths.includes(join(cwd, 'app/consumer-2.ts')));\n  assert.ok(importFilePaths.includes(join(cwd, 'app/consumer-3.ts')));\n});\n"
  },
  {
    "path": "packages/knip/test/session/session.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { test } from 'node:test';\nimport { join } from '../../src/util/path.ts';\nimport { resolve } from '../helpers/resolve.ts';\nimport { describeFile } from './util.ts';\n\nconst cwd = resolve('fixtures/session');\n\ntest('describes exports for a.ts', async () => {\n  const { file } = await describeFile(cwd, 'a.ts');\n\n  assert.deepEqual(\n    file.exports.map(_export => _export.identifier),\n    ['A']\n  );\n  assert.equal(file.internalImports.length, 1);\n  assert.equal(file.internalImports[0].filePath, join(cwd, 'b.ts'));\n  const expectedCycle = [join(cwd, 'a.ts'), join(cwd, 'b.ts'), join(cwd, 'c.ts'), join(cwd, 'a.ts')];\n  const hasCycle = file.cycles.some(\n    cycle => cycle.length === expectedCycle.length && cycle.every((value, idx) => value === expectedCycle[idx])\n  );\n  assert.ok(hasCycle);\n\n  const exportA = file.exports[0];\n  assert.equal(exportA.importLocations.length, 0);\n  assert.ok(exportA.entryPaths.has(join(cwd, 'index.ts')));\n});\n\ntest('tracks internal imports for index.ts', async () => {\n  const { file } = await describeFile(cwd, 'index.ts');\n\n  const overloadImport = file.internalImports.find(entry => entry.identifier === 'OVERLOAD');\n  assert.ok(overloadImport, 'missing OVERLOAD import');\n  assert.equal(overloadImport.filePath, join(cwd, 'overload-1.ts'));\n  assert.equal(file.cycles.length, 0);\n});\n\ntest('tracks usage of default exports', async () => {\n  const { file } = await describeFile(cwd, 'default-export.ts');\n\n  const defaultExport = file.exports.find(entry => entry.identifier === 'default');\n  assert.ok(defaultExport, 'missing default export entry');\n  assert.ok(defaultExport.importLocations.length > 0);\n  assert.ok(defaultExport.importLocations.some(location => location.filePath === join(cwd, 'index.ts')));\n});\n\ntest('ignores namespace importers that never reference a member', async () => {\n  const { file } = await describeFile(cwd, 'rose.ts');\n\n  const roseExport = file.exports.find(entry => entry.identifier === 'rose');\n  assert.ok(roseExport, 'missing rose export entry');\n  const namespaceImportPath = join(cwd, 'flowers.ts');\n  assert.ok(roseExport.importLocations.every(location => location.filePath !== namespaceImportPath));\n});\n\ntest('summarizes diamond-shaped branching contention', async () => {\n  const { file } = await describeFile(cwd, 'diamond-top.ts');\n\n  const diamond = file.contention.DIAMOND;\n  assert.ok(diamond, 'missing DIAMOND contention summary');\n\n  const expected = [join(cwd, 'diamond-top.ts')];\n\n  assert.deepEqual(new Set(diamond.branching), new Set(expected));\n  assert.deepEqual(diamond.conflict, []);\n});\n"
  },
  {
    "path": "packages/knip/test/session/util.ts",
    "content": "import assert from 'node:assert/strict';\nimport { createSession } from '../../src/session/session.ts';\nimport { join } from '../../src/util/path.ts';\nimport { createOptions } from '../helpers/create-options.ts';\n\nexport const describeFile = async (cwd: string, relativePath: string) => {\n  const options = await createOptions({ cwd, isSession: true });\n  const session = await createSession(options);\n  const filePath = join(options.cwd, relativePath);\n  const descriptor = session.describeFile(filePath);\n  assert.ok(descriptor, `missing descriptor for ${relativePath}`);\n  return { file: descriptor, cwd: options.cwd };\n};\n"
  },
  {
    "path": "packages/knip/test/session-dependencies.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { createGraphExplorer } from '../src/graph-explorer/explorer.ts';\nimport { run } from '../src/run.ts';\nimport { createSession } from '../src/session/session.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/session-dependencies');\n\ntest('Get dependency usage from module graph', async () => {\n  const options = await createOptions({ cwd, isSession: true });\n  const { session } = await run(options);\n\n  assert(session, 'Session should be available when isSession is true');\n\n  const graph = session.getGraph();\n  const entryPaths = session.getEntryPaths();\n  const explorer = createGraphExplorer(graph, entryPaths);\n\n  const allUsage = explorer.getDependencyUsage();\n  const lodashUsage = explorer.getDependencyUsage('lodash').get('lodash');\n  const zodUsage = explorer.getDependencyUsage(/z(o|a)d/).get('zod');\n  const unusedUsage = explorer.getDependencyUsage('unused-dep').get('unused-dep');\n\n  assert.equal(allUsage.size, 2);\n  assert(lodashUsage);\n  assert(zodUsage);\n  assert.equal(lodashUsage.imports.length, 2);\n  assert.equal(zodUsage.imports.length, 2);\n  assert.equal(unusedUsage, undefined);\n  assert(lodashUsage.imports[0].line > 0);\n  assert(lodashUsage.imports[0].col > 0);\n});\n\ntest('Get dependency usage via describePackageJson', async () => {\n  const options = await createOptions({ cwd, isSession: true });\n  const session = await createSession(options);\n\n  const { dependenciesUsage } = session.describePackageJson();\n  const lodashUsage = dependenciesUsage.get('lodash');\n  const zodUsage = dependenciesUsage.get('zod');\n  const unusedUsage = dependenciesUsage.get('unused-dep');\n\n  assert(lodashUsage);\n  assert.equal(lodashUsage.imports.length, 2);\n  assert(zodUsage);\n  assert.equal(zodUsage.imports.length, 2);\n  assert.equal(unusedUsage, undefined);\n  assert.equal(dependenciesUsage.size, 2);\n  assert(lodashUsage.imports[0].line > 0);\n  assert(lodashUsage.imports[0].col > 0);\n  assert(lodashUsage.imports[0].pos > 0);\n});\n"
  },
  {
    "path": "packages/knip/test/skip-exports-analysis.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/skip-exports-analysis');\n\ntest('ignore exports', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.binaries['package.json'].nodemon);\n  assert(issues.binaries['package.json'].playwright);\n\n  assert(issues.exports['src/used.js'].default);\n  assert(issues.exports['src/used.js'].unused);\n  assert(issues.exports['lib/used.js'].default);\n  assert(issues.exports['lib/used.js'].unused);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 4,\n    binaries: 2,\n    processed: 7,\n    total: 7,\n  });\n});\n\ntest('ignore exports (isIncludeEntryExports)', async () => {\n  const options = await createOptions({ cwd, isIncludeEntryExports: true });\n  const { issues, counters } = await main(options);\n  assert(issues.binaries['package.json'].nodemon);\n  assert(issues.binaries['package.json'].playwright);\n\n  assert(issues.exports['src/index.js'].reexport);\n\n  assert(issues.exports['src/used.js'].default);\n  assert(issues.exports['src/used.js'].unused);\n  assert(issues.exports['src/used.js'].reexport);\n\n  assert(issues.exports['lib/used.js'].default);\n  assert(issues.exports['lib/used.js'].unused);\n  assert(issues.exports['lib/used.js'].reexport);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 7,\n    binaries: 2,\n    processed: 7,\n    total: 7,\n  });\n});\n\ntest('ignore exports (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['src/used.js'].unused);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 3, // TODO: cancel out like regular dev entries\n    exports: 2,\n    processed: 5,\n    total: 5,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/subpath-import-from-plugin.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/subpath-import-from-plugin');\n\ntest('Allows subpath-imports from plugin', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert.equal(Object.keys(issues.unlisted).length, 0);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n\ntest('Allows subpath-imports from plugin (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { issues, counters } = await main(options);\n\n  assert.equal(Object.keys(issues.unlisted).length, 0);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n\ntest('Allows subpath-imports from plugin (strict)', async () => {\n  const options = await createOptions({ cwd, isStrict: true });\n  const { issues, counters } = await main(options);\n\n  assert.equal(Object.keys(issues.unlisted).length, 0);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/subpath-import.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/subpath-import');\n\ntest('Allows subpath-imports', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert.equal(Object.keys(issues.unlisted).length, 0);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n\ntest('Allows subpath-imports (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { issues, counters } = await main(options);\n  assert.equal(Object.keys(issues.unlisted).length, 0);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n\ntest('Allows subpath-imports (strict)', async () => {\n  const options = await createOptions({ cwd, isStrict: true });\n  const { counters } = await main(options);\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/subpath-imports-outdir.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/subpath-imports-outdir');\n\ntest('Resolve subpath imports to outDir with rootDir=\".\"', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 5,\n    total: 5,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/subpath-patterns.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/subpath-patterns');\n\ntest('Allows subpath-patterns', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert('src/internals/unused.ts' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    processed: 3,\n    total: 3,\n  });\n});\n\ntest('Allows subpath-patterns (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { issues, counters } = await main(options);\n\n  assert('src/internals/unused.ts' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    processed: 3,\n    total: 3,\n  });\n});\n\ntest('Allows subpath-patterns (strict)', async () => {\n  const options = await createOptions({ cwd, isStrict: true });\n  const { issues, counters } = await main(options);\n\n  assert('src/internals/unused.ts' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/tagged-template-literal.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/tagged-template-literal');\n\ntest('Exclude imports from tagged template literals', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 5,\n    total: 5,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/tags-cli.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport { join } from '../src/util/path.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/tags-cli');\n\ntest('Include or exclude tagged exports (package.json)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['unimported.ts']['unimported']);\n  assert(issues.exports['unimported.ts']['unimportedUntagged']);\n  assert(issues.exports['tags.ts']['NS.UnusedUntagged']);\n  assert(issues.exports['tags.ts']['NS.UnusedCustom']);\n  assert(issues.exports['tags.ts']['NS.UnusedInternal']);\n  assert(issues.exports['tags.ts']['NS.UnusedCustomAndInternal']);\n  assert(issues.exports['tags.ts']['NS.MyCustomClass']);\n  assert(issues.enumMembers['tags.ts']['MyEnum.UnusedUntagged']);\n  assert(issues.enumMembers['tags.ts']['MyEnum.UnusedCustom']);\n  assert(issues.enumMembers['tags.ts']['MyEnum.UnusedInternal']);\n  assert(issues.enumMembers['tags.ts']['MyEnum.UnusedCustomAndInternal']);\n  assert(issues.types['tags.ts']['NS.MyCustomEnum']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 7,\n    types: 1,\n    enumMembers: 4,\n    processed: 3,\n    total: 3,\n  });\n});\n\ntest('Include or exclude tagged exports (package.json/include)', async () => {\n  const options = await createOptions({ cwd, tags: ['+custom'] });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['unimported.ts']['unimported']);\n  assert(issues.exports['tags.ts']['NS.UnusedCustom']);\n  assert(issues.exports['tags.ts']['NS.UnusedCustomAndInternal']);\n  assert(issues.exports['tags.ts']['NS.MyCustomClass']);\n  assert(issues.types['tags.ts']['NS.MyCustomEnum']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 4,\n    types: 1,\n    processed: 3,\n    total: 3,\n  });\n});\n\ntest('Include or exclude tagged exports (package.json/exclude)', async () => {\n  const options = await createOptions({ cwd, tags: ['-custom'] });\n  const { issues, counters, tagHints } = await main(options);\n\n  assert(issues.exports['tags.ts']['NS.UnusedUntagged']);\n  assert(issues.exports['tags.ts']['NS.UnusedInternal']);\n  assert(issues.enumMembers['tags.ts']['MyEnum.UnusedUntagged']);\n  assert(issues.enumMembers['tags.ts']['MyEnum.UnusedInternal']);\n\n  assert.deepEqual(\n    tagHints,\n    new Set([\n      {\n        type: 'tag',\n        filePath: join(cwd, 'unimported.ts'),\n        identifier: 'ignored',\n        tagName: '@custom',\n      },\n    ])\n  );\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 3,\n    enumMembers: 2,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/tags-exclude.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/tags-exclude');\n\ntest('Include or exclude tagged exports (exclude)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['tags.ts']['NS.untagged']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 1,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/tags-include.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/tags-include');\n\ntest('Include or exclude tagged exports (include)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['tags.ts']['NS.tagged']);\n  assert(issues.exports['tags.ts']['NS.taggedToo']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 2,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/ts-namespace.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/ts-namespace');\n\ntest('Find unused namespace members', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.namespaceMembers['members.ts']['Fruits.unusedBanana']);\n  assert(issues.namespaceMembers['members.ts']['Fruits.Tropical.unusedPapaya']);\n  assert(issues.namespaceMembers['members.ts']['Animals.unusedDog']);\n  assert(issues.namespaceMembers['members.ts']['Shapes.unusedSquare']);\n  assert(issues.namespaceMembers['members.ts']['Standalone.unusedValue']);\n  assert(issues.namespaceMembers['types.ts']['Types.UnusedType']);\n  assert(issues.namespaceMembers['types.ts']['Types.UnusedInterface']);\n\n  assert(issues.namespaceMembers['merged.ts']['Validator.unusedMinLength']);\n  assert(issues.namespaceMembers['merged.ts']['format.unusedPadding']);\n\n  assert(!issues.namespaceMembers['members.ts']?.['Fruits.apple']);\n  assert(!issues.namespaceMembers['members.ts']?.['Fruits.Tropical.mango']);\n  assert(!issues.namespaceMembers['members.ts']?.['Animals.cat']);\n  assert(!issues.namespaceMembers['members.ts']?.['Animals.Birds.eagle']);\n  assert(!issues.namespaceMembers['members.ts']?.['Shapes.circle']);\n  assert(!issues.namespaceMembers['members.ts']?.['Shapes.Nested.triangle']);\n  assert(!issues.namespaceMembers['members.ts']?.['Standalone.value']);\n  assert(!issues.namespaceMembers['members.ts']?.['Standalone.Nested.deep']);\n  assert(!issues.namespaceMembers['types.ts']?.['Types.UsedType']);\n  assert(!issues.namespaceMembers['types.ts']?.['Types.UsedInterface']);\n  assert(!issues.namespaceMembers['merged.ts']?.['Validator.maxLength']);\n  assert(!issues.namespaceMembers['merged.ts']?.['format.separator']);\n\n  assert(issues.namespaceMembers['modules.ts']['Colors.unusedBlue']);\n  assert(issues.namespaceMembers['modules.ts']['Colors.Shades.unusedLight']);\n  assert(!issues.namespaceMembers['modules.ts']?.['Colors.red']);\n  assert(!issues.namespaceMembers['modules.ts']?.['Colors.Shades.dark']);\n\n  assert(issues.enumMembers['merged.ts']['Status.unusedDefault']);\n  assert(!issues.enumMembers['merged.ts']?.['Status.label']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    enumMembers: 2,\n    namespaceMembers: 11,\n    processed: 5,\n    total: 5,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/tsc-files-mode.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/tsc-files-mode');\n\ntest('Should use tsconfig files/include/exclude as project boundaries', async () => {\n  const options = await createOptions({ cwd, isUseTscFiles: true });\n  const { issues, counters } = await main(options);\n\n  assert.equal(Object.keys(issues.files).length, 0);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 1,\n    processed: 4,\n    total: 4,\n  });\n});\n\ntest('Should report unimported files as unused', async () => {\n  const options = await createOptions({ cwd, isUseTscFiles: false });\n  const { issues, counters } = await main(options);\n\n  assert.equal(Object.keys(issues.files).length, 3);\n  assert('src/excluded.ts' in issues.files);\n  assert('src/declare-module.ts' in issues.files);\n  assert('src/declare-global.ts' in issues.files);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 1,\n    files: 3,\n    processed: 5,\n    total: 5,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/tsconfig-extends.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/tsconfig-extends');\n\ntest('Resolve modules properly with extended ts config', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/tsconfig-include-dir.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/tsconfig-include-dir');\n\ntest('Handle bare directory names in tsconfig include', async () => {\n  const options = await createOptions({ cwd, isUseTscFiles: true });\n  const { issues, counters } = await main(options);\n\n  assert.equal(Object.keys(issues.files).length, 0);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 1,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/tsconfig-nested-paths.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/tsconfig-nested-paths');\n\ntest('Resolve modules using paths from nested tsconfig references', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/tsconfig-paths-extends.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/tsconfig-paths-extends');\n\ntest('Resolve modules properly using tsconfig paths from extended config', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/tsconfig-preset-strict.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/tsconfig-preset-strict');\n\ntest('tsconfig presets in devDependencies should not be unlisted in strict mode', async () => {\n  const options = await createOptions({ cwd, isStrict: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/tsconfig-rootdirs.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/tsconfig-rootdirs');\n\ntest('Resolve modules properly with rootDirs', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/tsconfig.json",
    "content": "{\n  \"$schema\": \"https://json.schemastore.org/tsconfig\",\n  \"extends\": \"../tsconfig.json\",\n  \"compilerOptions\": {\n    \"allowJs\": true,\n    \"rootDir\": \".\",\n    \"baseUrl\": \".\",\n    \"jsx\": \"preserve\"\n  },\n  \"include\": [\".\"]\n}\n"
  },
  {
    "path": "packages/knip/test/type-in-type.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/type-in-type');\n\ntest('Find unused types but not types used in other exported types', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['module.ts']['create']);\n  assert(issues.types['types.ts']['Func']);\n\n  assert(!issues.types['types.ts']['A']);\n  assert(!issues.types['types.ts']['B']);\n  assert(!issues.types['types.ts']['Wrapped']);\n  assert(!issues.types['types.ts']['Mapped']);\n  assert(!issues.types['types.ts']['Tuple']);\n  assert(!issues.types['types.ts']['Intersection']);\n  assert(!issues.types['types.ts']['Conditional']);\n  assert(!issues.types['types.ts']['Nested']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 1,\n    types: 1,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/type-in-value-export.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/type-in-value-export');\n\ntest('Find unused types in value exports', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.types['src/api.ts']['GetPointsResponse']);\n  assert(!issues.types['src/api.ts']['GetPoints']);\n  assert(!issues.types['src/api.ts']['GetPointsParams']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    types: 1,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/types.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/types');\n\ntest('Find @types/pkg that are obsolete, since pkg has types included', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.devDependencies['package.json']['@types/webpack']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    devDependencies: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/unresolved-rtl.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/מסמכים');\n\ntest('Report unresolved imports (rtl)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/url-import-meta-url.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport { createOptions } from '../src/util/create-options.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/url-import-meta-url');\n\ntest('Support URL constructor using import.meta.url', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/util/codeowners.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { parseCodeowners } from '../../src/util/codeowners.ts';\n\ntest('codeowners', () => {\n  const content = `\n# Global owner as fallback\n*       @global-owner\n\n# File extensions\n*.js    @js-owner\n*.ts    @ts-owner\n\n# Directory patterns\n/docs/          @docs-team\n/src/lib/       @lib-team\n/apps/web/      @web-team\n**/tests/       @test-team\n\n# Specific files take precedence\n/src/lib/core.js    @core-team\n  `.trim();\n\n  const findOwners = parseCodeowners(content);\n\n  assert.deepEqual(findOwners('README.md'), ['@global-owner']);\n  assert.deepEqual(findOwners('utils.js'), ['@js-owner']);\n  assert.deepEqual(findOwners('src/types.ts'), ['@ts-owner']);\n  assert.deepEqual(findOwners('docs/api.md'), ['@docs-team']);\n  assert.deepEqual(findOwners('src/lib/utils.js'), ['@lib-team']);\n  assert.deepEqual(findOwners('src/lib/core.js'), ['@core-team']);\n  assert.deepEqual(findOwners('apps/web/index.js'), ['@web-team']);\n  assert.deepEqual(findOwners('src/tests/unit.js'), ['@test-team']);\n});\n\ntest('codeowners pattern matching', () => {\n  const content = `\n# Shallow match with /* (should not match deeply nested files)\n/docs/*      @docs-shallow\n# Deep match (should match all nested files)\n/api/        @api-deep\n  `.trim();\n\n  const findOwners = parseCodeowners(content);\n\n  assert.deepEqual(findOwners('docs/readme.md'), ['@docs-shallow']);\n\n  assert.deepEqual(findOwners('docs/guides/start.md'), []);\n\n  assert.deepEqual(findOwners('api/endpoint.js'), ['@api-deep']);\n  assert.deepEqual(findOwners('api/v1/users.js'), ['@api-deep']);\n});\n\ntest('codeowners with multiple owners', () => {\n  const content = `\n*.js    @js-owner\n\n/src/   @lead-dev @senior-dev @architect\n/docs/  @tech-writer @docs-team docs@example.com\n\n/api/   @backend-team api@company.com @devops-team\n  `.trim();\n\n  const findOwners = parseCodeowners(content);\n\n  assert.deepEqual(findOwners('utils.js'), ['@js-owner']);\n  assert.deepEqual(findOwners('src/app.ts'), ['@lead-dev', '@senior-dev', '@architect']);\n  assert.deepEqual(findOwners('docs/api.md'), ['@tech-writer', '@docs-team', 'docs@example.com']);\n  assert.deepEqual(findOwners('api/users.js'), ['@backend-team', 'api@company.com', '@devops-team']);\n});\n\ntest('codeowners pattern precedence', () => {\n  const content = `\n/src/lib/ @first-owner\n/src/lib/ @second-owner\n  `.trim();\n\n  const findOwners = parseCodeowners(content);\n  assert.deepEqual(findOwners('src/lib/file.js'), ['@second-owner']);\n});\n\ntest('codeowners ownership resolution', () => {\n  const content = `\n/is/not-owned  @some/owner\n/is/owned      @some/other-owner\n  `.trim();\n\n  const findOwners = parseCodeowners(content);\n\n  assert.deepEqual(findOwners('is/not-owned'), ['@some/owner']);\n  assert.deepEqual(findOwners('is/owned'), ['@some/other-owner']);\n});\n\ntest('codeowners file resolution', () => {\n  const content = '/src/ @team';\n  const findOwners = parseCodeowners(content);\n\n  const paths = ['src/file1.js', 'src/file2.js', 'src/nested/file3.js'];\n  const results = paths.map(path => ({ path, owners: findOwners(path) }));\n\n  assert(results.every(r => r.owners[0] === '@team'));\n});\n"
  },
  {
    "path": "packages/knip/test/util/convert-gitignore-patterns.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { EOL } from 'node:os';\nimport test from 'node:test';\nimport {\n  convertGitignoreToPicomatchIgnorePatterns as convert,\n  parseAndConvertGitignorePatterns as parse,\n} from '../../src/util/parse-and-convert-gitignores.ts';\n\ntest('convertGitignoreToPicomatch', () => {\n  assert.deepEqual(convert('*.ext'), { negated: false, patterns: ['**/*.ext', '**/*.ext/**'] });\n  assert.deepEqual(convert('!*.ext'), { negated: true, patterns: ['**/*.ext', '**/*.ext/**'] });\n  assert.deepEqual(convert('dir'), { negated: false, patterns: ['**/dir', '**/dir/**'] });\n  assert.deepEqual(convert('dir/'), { negated: false, patterns: ['**/dir', '**/dir/**'] });\n  assert.deepEqual(convert('.dot'), { negated: false, patterns: ['**/.dot', '**/.dot/**'] });\n  assert.deepEqual(convert('*.stars*'), { negated: false, patterns: ['**/*.stars*', '**/*.stars*/**'] });\n  assert.deepEqual(convert('!.ext'), { negated: true, patterns: ['**/.ext', '**/.ext/**'] });\n  assert.deepEqual(convert('file.ext'), { negated: false, patterns: ['**/file.ext', '**/file.ext/**'] });\n  assert.deepEqual(convert('/root'), { negated: false, patterns: ['root', 'root/**'] });\n  assert.deepEqual(convert('/**/.dot/*'), { negated: false, patterns: ['**/.dot/*', '**/.dot/*'] });\n  assert.deepEqual(convert('!/**/.dot/dir'), { negated: true, patterns: ['**/.dot/dir', '**/.dot/dir/**'] });\n  assert.deepEqual(convert('!/no-root'), { negated: true, patterns: ['no-root', 'no-root/**'] });\n  assert.deepEqual(convert('so/deep/dir'), { negated: false, patterns: ['**/so/deep/dir', '**/so/deep/dir/**'] });\n  assert.deepEqual(convert('/**/root-star'), { negated: false, patterns: ['**/root-star', '**/root-star/**'] });\n  assert.deepEqual(convert('!**/dir/**/f.ext'), { negated: true, patterns: ['**/dir/**/f.ext', '**/dir/**/f.ext/**'] });\n});\n\ntest('parseAndConvertGitignorePatterns', async () => {\n  const gitignorePatterns = ['.git', 'node_modules', 'a/b/c', '/a/b/c'];\n  const globPatterns = parse(gitignorePatterns.join(EOL));\n  assert.deepEqual(globPatterns, [\n    { negated: false, patterns: ['**/.git', '**/.git/**'] },\n    { negated: false, patterns: ['**/node_modules', '**/node_modules/**'] },\n    { negated: false, patterns: ['**/a/b/c', '**/a/b/c/**'] },\n    { negated: false, patterns: ['a/b/c', 'a/b/c/**'] },\n  ]);\n});\n\ntest('parseAndConvertGitignorePatterns (ancestor)', async () => {\n  const gitignorePatterns = ['.git', 'node_modules', 'a/b/c', '/a/b/c'];\n  const globPatterns = parse(gitignorePatterns.join(EOL), 'a/b/');\n  assert.deepEqual(globPatterns, [\n    { negated: false, patterns: ['**/.git', '**/.git/**'] },\n    { negated: false, patterns: ['**/node_modules', '**/node_modules/**'] },\n    { negated: false, patterns: ['**/c', '**/c/**'] }, // TODO FIXME: should probably be ['c', 'c/**']\n    { negated: false, patterns: ['c', 'c/**'] },\n  ]);\n});\n\ntest('parseAndConvertGitignorePatterns (hashes)', async () => {\n  const gitignorePatterns = ['#comment', 'ends-with-hash#', String.raw`\\#starts-with-hash`];\n  const globPatterns = parse(gitignorePatterns.join(EOL));\n  assert.deepEqual(globPatterns, [\n    { negated: false, patterns: ['**/ends-with-hash#', '**/ends-with-hash#/**'] },\n    { negated: false, patterns: ['**/#starts-with-hash', '**/#starts-with-hash/**'] },\n  ]);\n});\n"
  },
  {
    "path": "packages/knip/test/util/find-and-parse-gitignores.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport fs from 'node:fs/promises';\nimport test from 'node:test';\nimport { findAndParseGitignores } from '../../src/util/glob-core.ts';\nimport { join } from '../../src/util/path.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\ntest('findAndParseGitignores', async () => {\n  const cwd = resolve('fixtures/glob');\n  const gitignore = await findAndParseGitignores(cwd);\n  assert.deepEqual(gitignore, {\n    gitignoreFiles: ['../../.gitignore', '../../../../.gitignore', '.gitignore', 'a/.gitignore', 'a/b/.gitignore'],\n    ignores: new Set([\n      '**/.DS_Store',\n      '**/.DS_Store/**',\n      '**/.cache',\n      '**/.cache/**',\n      '.git',\n      '**/node_modules',\n      '**/node_modules/**',\n      '**/packages/*/dist',\n      '**/packages/*/dist/**',\n      '.yarn',\n      '**/a/b/c',\n      '**/a/b/c/**',\n      '**/.npmrc',\n      '**/.npmrc/**',\n      '**/bin/knip',\n      '**/bin/knip-bun',\n      '**/bin/knip-bun/**',\n      '**/bin/knip/**',\n    ]),\n    unignores: [],\n  });\n});\n\ntest('findAndParseGitignores (/a)', async () => {\n  const cwd = resolve('fixtures/glob/a');\n  const gitignore = await findAndParseGitignores(cwd);\n  assert.deepEqual(gitignore, {\n    gitignoreFiles: ['../.gitignore', '../../../.gitignore', '../../../../../.gitignore', '.gitignore', 'b/.gitignore'],\n    ignores: new Set([\n      '.git',\n      '**/node_modules',\n      '**/node_modules/**',\n      '**/packages/*/dist',\n      '**/packages/*/dist/**',\n      '.yarn',\n      '**/b/c',\n      '**/b/c/**',\n      '**/.DS_Store',\n      '**/.DS_Store/**',\n      '**/.cache',\n      '**/.cache/**',\n      '**/.npmrc',\n      '**/.npmrc/**',\n      '**/bin/knip',\n      '**/bin/knip-bun',\n      '**/bin/knip-bun/**',\n      '**/bin/knip/**',\n    ]),\n    unignores: [],\n  });\n});\n\ntest('findAndParseGitignores (/a/b', async () => {\n  const cwd = resolve('fixtures/glob/a/b');\n  const gitignore = await findAndParseGitignores(cwd);\n  assert.deepEqual(gitignore, {\n    gitignoreFiles: [\n      '../.gitignore',\n      '../../.gitignore',\n      '../../../../.gitignore',\n      '../../../../../../.gitignore',\n      '.gitignore',\n    ],\n    ignores: new Set([\n      '.git',\n      '**/node_modules',\n      '**/node_modules/**',\n      '**/packages/*/dist',\n      '**/packages/*/dist/**',\n      '.yarn',\n      '**/c',\n      '**/c/**',\n      '**/.DS_Store',\n      '**/.DS_Store/**',\n      '**/.cache',\n      '**/.cache/**',\n      '**/.npmrc',\n      '**/.npmrc/**',\n      '**/bin/knip',\n      '**/bin/knip-bun',\n      '**/bin/knip-bun/**',\n      '**/bin/knip/**',\n    ]),\n    unignores: [],\n  });\n});\n\nconst worktreeRoot = resolve('fixtures/glob-worktree/root');\nawait fs.copyFile(join(worktreeRoot, 'dot-git'), join(worktreeRoot, '.git')).catch(() => {});\n\ntest('findAndParseGitignores (with .git file)', async () => {\n  const cwd = resolve('fixtures/glob-worktree/root');\n  const gitignore = await findAndParseGitignores(cwd);\n  assert.deepEqual(gitignore, {\n    gitignoreFiles: ['../mock-git-dir/info/exclude', '.gitignore', 'subdir/.gitignore'],\n    ignores: new Set([\n      '.git',\n      '**/node_modules/**',\n      '.yarn',\n      '**/worktree-exclude-ignored',\n      '**/worktree-exclude-ignored/**',\n      '**/worktree-ignored',\n      '**/worktree-ignored/**',\n      'subdir/**/subdir-ignored',\n      'subdir/**/subdir-ignored/**',\n    ]),\n    unignores: [],\n  });\n});\n\ntest('findAndParseGitignores (with .git file in ancestor)', async () => {\n  const cwd = resolve('fixtures/glob-worktree/root/subdir');\n  const gitignore = await findAndParseGitignores(cwd);\n  assert.deepEqual(gitignore, {\n    gitignoreFiles: ['../.gitignore', '.gitignore'],\n    ignores: new Set([\n      '.git',\n      '**/node_modules/**',\n      '.yarn',\n      '**/worktree-ignored',\n      '**/worktree-ignored/**',\n      '**/subdir-ignored',\n      '**/subdir-ignored/**',\n    ]),\n    unignores: [],\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/util/get-included-issue-types.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { ISSUE_TYPES } from '../../src/constants.ts';\nimport {\n  defaultExcludedIssueTypes,\n  getIncludedIssueTypes,\n  shorthandDeps,\n  shorthandExports,\n  shorthandFiles,\n} from '../../src/util/get-included-issue-types.ts';\n\nconst included = (type: string) => [type, true];\nconst excluded = (type: string) => [type, false];\nconst all = Object.fromEntries(ISSUE_TYPES.map(included));\nconst none = Object.fromEntries(ISSUE_TYPES.map(excluded));\nconst defaults = Object.fromEntries([\n  ...ISSUE_TYPES.filter(type => !defaultExcludedIssueTypes.includes(type)).map(included),\n  ...defaultExcludedIssueTypes.map(excluded),\n]);\nconst base = {\n  include: [],\n  exclude: [],\n};\n\ntest('Resolve included issue types (default)', async () => {\n  const config = getIncludedIssueTypes(base);\n  assert.deepEqual(config, { ...defaults });\n});\n\ntest('Resolve included issue types (all)', async () => {\n  const config = getIncludedIssueTypes({ ...base, includeOverrides: ['nsExports', 'nsTypes'] });\n  assert.deepEqual(config, { ...all });\n});\n\ntest('Resolve included issue types (include single)', async () => {\n  const config = getIncludedIssueTypes({ ...base, includeOverrides: ['duplicates'] });\n  assert.deepEqual(config, { ...none, duplicates: true });\n});\n\ntest('Resolve included issue types (exclude some)', async () => {\n  const config = getIncludedIssueTypes({ ...base, excludeOverrides: ['duplicates', 'nsTypes'] });\n  assert.deepEqual(config, { ...defaults, duplicates: false, nsTypes: false });\n});\n\ntest('Resolve included issue types (overlap)', async () => {\n  const config = getIncludedIssueTypes({\n    ...base,\n    includeOverrides: ['exports', 'files', 'nsTypes'],\n    excludeOverrides: ['files', 'duplicates'],\n  });\n  assert.deepEqual(config, { ...none, exports: true, nsTypes: true });\n});\n\ntest('Resolve included issue types (include devDependencies)', async () => {\n  const config = getIncludedIssueTypes({ ...base, includeOverrides: ['dependencies'] });\n  assert.deepEqual(config, { ...none, dependencies: true, devDependencies: true, optionalPeerDependencies: true });\n});\n\ntest('Resolve included issue types (include dependencies)', async () => {\n  const config = getIncludedIssueTypes({ ...base, includeOverrides: ['dependencies'], isProduction: true });\n  assert.deepEqual(config, { ...none, dependencies: true });\n});\n\ntest('Resolve included issue types (exclude dependencies)', async () => {\n  const config = getIncludedIssueTypes({ ...base, excludeOverrides: ['dependencies'] });\n  assert.deepEqual(config, {\n    ...defaults,\n    dependencies: false,\n    devDependencies: false,\n    optionalPeerDependencies: false,\n  });\n});\n\ntest('Resolve included issue types (--dependencies)', async () => {\n  const config = getIncludedIssueTypes({\n    ...base,\n    includeOverrides: shorthandDeps,\n  });\n  assert.deepEqual(config, {\n    ...none,\n    dependencies: true,\n    devDependencies: true,\n    optionalPeerDependencies: true,\n    unlisted: true,\n    binaries: true,\n    unresolved: true,\n    catalog: true,\n  });\n});\n\ntest('Resolve included issue types (--exports)', async () => {\n  const config = getIncludedIssueTypes({\n    ...base,\n    includeOverrides: shorthandExports,\n  });\n  assert.deepEqual(config, {\n    ...none,\n    exports: true,\n    types: true,\n    enumMembers: true,\n    namespaceMembers: true,\n    duplicates: true,\n  });\n});\n\ntest('Resolve included issue types (--files)', async () => {\n  const config = getIncludedIssueTypes({ ...base, includeOverrides: shorthandFiles });\n  assert.deepEqual(config, { ...none, files: true });\n});\n\ntest('Resolve included issue types (all)', async () => {\n  const config = getIncludedIssueTypes({\n    ...base,\n    includeOverrides: [\n      'nsExports',\n      'nsTypes',\n      'dependencies',\n      'optionalPeerDependencies',\n      'unlisted',\n      'binaries',\n      'unresolved',\n      'exports',\n      'types',\n      'enumMembers',\n      'namespaceMembers',\n      'duplicates',\n      'files',\n      'catalog',\n    ],\n  });\n  assert.deepEqual(config, all);\n});\n"
  },
  {
    "path": "packages/knip/test/util/get-inputs-from-scripts.test.ts",
    "content": "/* oxlint-disable no-useless-escape */\n/* oxlint-disable no-template-curly-in-string */\nimport assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { _getInputsFromScripts } from '../../src/binaries/index.ts';\nimport { type Input, toBinary, toConfig, toDeferResolve, toDeferResolveEntry, toDependency, toEntry } from '../../src/util/input.ts';\nimport { join } from '../../src/util/path.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/binaries');\nconst containingFilePath = join(cwd, 'package.json');\nconst pkgScripts = { cwd, manifestScriptNames: new Set(['program', 'spl:t']) };\nconst knownOnly = { cwd, knownBinsOnly: true };\nconst opt = { optional: true };\n\nconst js = toDeferResolveEntry('./script.js', opt);\nconst ts = toDeferResolveEntry('./main.ts', opt);\nconst req = toDeferResolve('./require.js');\n\ntype T = (script: string | string[], dependencies: Input[], options?: { cwd?: string; manifestScriptNames?: Set<string> }) => void;\nconst t: T = (script, dependencies = [], options = { cwd }) =>\n  assert.deepEqual(\n    _getInputsFromScripts(script, {\n      rootCwd: cwd,\n      manifestScriptNames: new Set(),\n      containingFilePath,\n      ...options,\n    }),\n    dependencies\n  );\n\ntest('getInputsFromScripts (unknown programs)', () => {\n  t('program', [toBinary('program')]);\n  t(['program', 'program'], [toBinary('program'), toBinary('program')]);\n  t('program -short --long args', [toBinary('program')]);\n  t('program && program2', [toBinary('program'), toBinary('program2')]);\n  t('program -x && exec -y -- program2 -z', [toBinary('program'), toBinary('exec')]);\n  t('program -x; exec -y -- program2', [toBinary('program'), toBinary('exec')]);\n  t('program script.js -- program2', [toBinary('program')]);\n  t(\"program '*.js' -- program2\", [toBinary('program')]);\n  t('program -s .', [toBinary('program')]);\n  t('program command', [toBinary('program')]);\n  t('adb install -r android/app/app-dev-debug.apk', [toBinary('adb')]);\n  t('./wait-for-postgres.sh -h localhost -p 5433 -U dev -r 10', [toEntry('./wait-for-postgres.sh')]);\n});\n\ntest('getInputsFromScripts (unknown scripts)', () => {\n  t('./script.sh -r 10', [toEntry('./script.sh')]);\n});\n\ntest('getInputsFromScripts (node)', () => {\n  t('node script.js', [toBinary('node'), toDeferResolveEntry('script.js', opt)]);\n  t('node dist/index.js', [toBinary('node'), toDeferResolveEntry('dist/index.js', opt)]);\n  t('./script.js', [toEntry('./script.js')]);\n  t('node --watch ./script.js', [toBinary('node'), js]);\n  t('node script', [toBinary('node'), toDeferResolveEntry('script', opt)]);\n  t('node ./script.js build', [toBinary('node'), js]);\n});\n\ntest('getInputsFromScripts (node --test)', () => {\n  t('node --test --test-reporter=reporter', [toBinary('node'), toDeferResolve('reporter')]);\n  t('node --test --test-reporter=spec', [toBinary('node')]);\n});\n\ntest('getInputsFromScripts (node -r)', () => {\n  t('node -r script.js', [toBinary('node'), toDeferResolve('script.js')]);\n  t('node -r dotenv/config -- node ./script.js', [toBinary('node'), toDeferResolve('dotenv/config')]);\n  t('node -r package/script', [toBinary('node'), toDeferResolve('package/script')]);\n  t('node -r ./require.js ./script.js', [toBinary('node'), js, req]);\n  t('node --require=pkg1 --require pkg2 script', [toBinary('node'), toDeferResolveEntry('script', opt), toDeferResolve('pkg1'), toDeferResolve('pkg2')]);\n  t('node --import tsx ./main.ts', [toBinary('node'), toDeferResolveEntry('./main.ts', opt), toDeferResolve('tsx')]);\n  t('node --import=tsx ./main.ts', [toBinary('node'), ts, toDeferResolve('tsx')]);\n  t('node --loader ts-node/esm node_modules/pkg/bin/cli.js -c ./webpack.config.ts', [toBinary('node'), toDeferResolveEntry('node_modules/pkg/bin/cli.js', opt), toDeferResolve('ts-node/esm')]);\n  t('node --experimental-loader ts-node/esm/transpile-only ./script.js', [toBinary('node'), js, toDeferResolve('ts-node/esm/transpile-only')]);\n  t('node -r @scope/package/register ./dir', [toBinary('node'), toDeferResolveEntry('./dir', opt), toDeferResolve('@scope/package/register')]);\n  t('node -r @scope/package/register ./dir/index', [toBinary('node'), toDeferResolveEntry('./dir/index', opt), toDeferResolve('@scope/package/register')]);\n  t('node --inspect-brk -r pkg/register node_modules/.bin/exec --runInBand', [toBinary('node'), toBinary('exec'), toDeferResolve('pkg/register')]);\n  t('node -r ts-node/register node_modules/.bin/jest', [toBinary('node'), toBinary('jest'), toDeferResolve('ts-node/register')]);\n  t('node -r dotenv-flow/config ./node_modules/.bin/sanity-test codegen', [toBinary('node'), toBinary('sanity-test'), toDeferResolve('dotenv-flow/config')]);\n});\n\ntest('getInputsFromScripts (tsx)', () => {\n  t('tsx ./main.ts', [toBinary('tsx'), ts]);\n  t('tsx watch ./main.ts', [toBinary('tsx'), ts]);\n  t('node --loader tsx ./main.ts', [toBinary('node'), ts, toDeferResolve('tsx')]);\n  t('tsx main', [toBinary('tsx'), toDeferResolveEntry('main', opt)]);\n  t('tsx ./main.ts build', [toBinary('tsx'), ts]);\n});\n\ntest('getInputsFromScripts (--require)', () => {\n  t('program --loader tsx --test \"test/*.spec.ts\"', [toBinary('program')]);\n  t('program --loader ldr --loader tsx --test \"test/*.spec.ts\"', [toBinary('program')]);\n});\n\ntest('getInputsFromScripts (.bin)', () => {\n  t('./node_modules/.bin/tsc --noEmit', [toBinary('tsc')]);\n  t('node_modules/.bin/tsc --noEmit', [toBinary('tsc')]);\n  t('$(npm bin)/tsc --noEmit', [toBinary('tsc')]);\n  t('../../../scripts/node_modules/.bin/tsc --noEmit', []);\n});\n\ntest('getInputsFromScripts (dotenv)', () => {\n  t('dotenv program', [toBinary('dotenv'), toBinary('program')]);\n  t('dotenv -- program', [toBinary('dotenv'), toBinary('program')]);\n  t('dotenv -e .env3 -v VARIABLE=somevalue -- program', [toBinary('dotenv'), toBinary('program')]);\n  t('dotenv -e .env3 -v VARIABLE=somevalue program -- exit', [toBinary('dotenv'), toBinary('program')]);\n  t('dotenv -- mvn exec:java -Dexec.args=\"-g -f\"', [toBinary('dotenv'), toBinary('mvn')]);\n  t('dotenv -- concurrently \"npm ci\"', [toBinary('dotenv'), toBinary('concurrently')]);\n  t('dotenv -- concurrently \"next dev\" \"prisma generate\"', [toBinary('dotenv'), toBinary('concurrently'), toBinary('next'), toBinary('prisma')]);\n});\n\ntest('getInputsFromScripts (cross-env/env vars)', () => {\n  t('cross-env program', [toBinary('cross-env'), toBinary('program')]);\n  t('cross-env NODE_ENV=production program', [toBinary('cross-env'), toBinary('program')]);\n  t('cross-env NODE_ENV=production program subcommand', [toBinary('cross-env'), toBinary('program')]);\n  t('cross-env NODE_OPTIONS=--max-size=3072 program subcommand', [toBinary('cross-env'), toBinary('program')]);\n  t('cross-env NODE_OPTIONS=\"--loader pkg\" knex', [toBinary('cross-env'), toBinary('knex'), toDeferResolve('pkg')]);\n  t('NODE_ENV=production cross-env -- program --cache', [toBinary('cross-env'), toBinary('program')]);\n  t(\"NODE_OPTIONS='--require pkg-a --require pkg-b' program\", [toBinary('program'), toDeferResolve('pkg-a'), toDeferResolve('pkg-b')]);\n});\n\ntest('getInputsFromScripts (cross-env/node)', () => {\n  t('cross-env NODE_ENV=production node -r pkg/config ./script.js', [toBinary('cross-env'), toBinary('node'), js, toDeferResolve('pkg/config')]);\n  t('cross-env NODE_ENV=production node -r node_modules/pkg/cfg ./script.js', [toBinary('cross-env'), toBinary('node'), js, toDeferResolve('node_modules/pkg/cfg')]);\n  t('cross-env NODE_ENV=production node -r esm ./script.js', [toBinary('cross-env'), toBinary('node'), js, toDeferResolve('esm')]);\n});\n\ntest('getInputsFromScripts (nx)', () => {\n  t('nx run myapp:build:production', [toBinary('nx')]);\n  t('nx run-many -t build', [toBinary('nx')]);\n  t('nx exec -- esbuild ./main.ts --outdir=build', [toBinary('nx'), toBinary('esbuild'), toDeferResolve('./main.ts')]);\n});\n\ntest('getInputsFromScripts (npm)', () => {\n  t('npm run script', []);\n  t('npm run publish:latest -- --npm-tag=debug --no-push', []);\n  t('npm exec -- vitest -c vitest.e2e.config.mts', [toBinary('vitest'), toConfig('vitest', 'vitest.e2e.config.mts')]);\n  t('npm run program -- node script.js', [toBinary('node'), toDeferResolveEntry('script.js', opt)], pkgScripts);\n  t('npm run program -- run --coverage.enabled', [], pkgScripts);\n});\n\ntest('getInputsFromScripts (npx)', () => {\n  t('npx pkg', [toBinary('pkg')]);\n  t('npx prisma migrate reset --force', [toBinary('prisma')]);\n  t('npx @scope/pkg', [toDependency('@scope/pkg', opt)]);\n  t('npx tsx watch main', [toBinary('tsx'), toDeferResolveEntry('main', opt)]);\n  t('npx -y pkg', []);\n  t('npx --yes pkg', []);\n  t('npx --no pkg --edit ${1}', [toBinary('pkg')]);\n  t('npx --no -- pkg --edit ${1}', [toBinary('pkg')]);\n  t('npx pkg install --with-deps', [toBinary('pkg')]);\n  t('npx pkg migrate reset --force', [toBinary('pkg')]);\n  t('npx pkg@1.0.0 migrate reset --force', [toDependency('pkg', opt)]);\n  t('npx @scope/cli migrate reset --force', [toDependency('@scope/cli', opt)]);\n  t('npx -- pkg', [toBinary('pkg')]);\n  t('npx -- @scope/cli@1.0.0 migrate reset --force', [toDependency('@scope/cli', opt)]);\n  t('npx retry-cli@0.6.0 -- curl --output /dev/null ', [toDependency('retry-cli', opt), toBinary('curl')]);\n  t('npx --package pkg@0.6.0 -- curl --output /dev/null', [toBinary('curl'), toDependency('pkg', opt)]);\n  t('npx --package @scope/pkg@0.6.0 --package pkg -- curl', [toBinary('curl'), toDependency('@scope/pkg', opt), toDependency('pkg', opt)]);\n  t(\"npx --package=pkg -c 'curl --output /dev/null'\", [toDependency('pkg', opt), toBinary('curl')]);\n  t('npx swagger-typescript-api -p http://localhost:3030/swagger.v1.json', [toBinary('swagger-typescript-api')]);\n  t('npx swagger-typescript-api -- -p http://localhost:3030/swagger.v1.json', [toBinary('swagger-typescript-api')]);\n  t('npx tsx main', [toBinary('tsx'), toDeferResolveEntry('main', opt)]);\n  t('npx tsx ./main.ts build', [toBinary('tsx'), ts]);\n  t('npx tsx ./main.ts -- build', [toBinary('tsx'), ts]);\n});\n\ntest('getInputsFromScripts (bun)', () => {\n  t('bunx pkg', [toBinary('pkg', opt)]);\n  t('bunx cowsay \"Hello world!\"', [toBinary('cowsay', opt)]);\n  t('bunx my-cli --arg value', [toBinary('my-cli', opt)]);\n  t('bunx --bun biome format --write', [toBinary('biome', opt)]);\n  t('bun x pkg', [toBinary('pkg', opt)]);\n  t('bun ./main.ts', [toEntry(join(cwd, 'main.ts'))]);\n  t('bun run script.js', [toEntry(join(cwd, 'script.js'))]);\n  t('bun run ./main', [toEntry(join(cwd, 'main.ts'))]);\n  t('bun run --cwd packages/knip watch', []);\n  t('bun test', []);\n  t('bun add zod', []);\n  t('bun install', []);\n  t('bun remove webpack', []);\n  t('bun update lodash', []);\n  t('bun link', []);\n  t('bun unlink', []);\n  t('bun pm cache', []);\n  t('bun audit', []);\n  t('bun outdated', []);\n  t('bun init', []);\n  t('bun create next-app', []);\n  t('bun upgrade', []);\n  t('bun build ./index.ts --outdir ./dist', []);\n  t('bun patch zod', []);\n  t('bun why elysia', []);\n  t('bun info tailwindcss', []);\n  t('bun publish --access public', []);\n  t('bun publish --access public --tag latest', []);\n  t('bun repl', []);\n  t('bun exec ./script.sh', []);\n  t('bun feedback', []);\n  t('bun ci', []);\n});\n\ntest('getInputsFromScripts (pnpm)', () => {\n  t('pnpm exec program', [toBinary('program')]);\n  t('pnpm exec -- vitest -c vitest.unit.config.mts', [toBinary('vitest'), toConfig('vitest', 'vitest.unit.config.mts')]);\n  t('pnpm run program', []);\n  t('pnpm program', [toBinary('program')]);\n  t('pnpm run program', [], pkgScripts);\n  t('pnpm program', [], pkgScripts);\n  t('pnpm dlx pkg', [toDependency('pkg', opt)]);\n  t('pnpm --package=pkg-a dlx pkg', [toDependency('pkg', opt), toDependency('pkg-a', opt)]);\n  t('pnpm --recursive --parallel test -- --sequence.seed=1700316221712', []);\n  t('pnpm program script.js', [], pkgScripts);\n  t('pnpm --silent program script.js', [], pkgScripts);\n  t('pnpm --silent run program script.js', [], pkgScripts);\n  t(`pnpm --filter=\"[$(git rev-parse HEAD~1)]\" exec pnpm pack`, [toBinary('git')]);\n  t('pnpm --filter docs typedoc:check', []);\n  t('pnpm -r --filter=docs --filter=flarp exec program', [toBinary('program')]);\n  t('pnpm program -- node script.js', [toBinary('node'), toDeferResolveEntry('script.js', opt)], pkgScripts);\n  t('pnpm write:report', []);\n  t('pnpm perf:test:ci', []);\n});\n\ntest('getInputsFromScripts (pnpx/pnpm dlx)', () => {\n  t('pnpx pkg', [toDependency('pkg', opt)]);\n  const inputs = [toDependency('cowsay', opt), toDependency('lolcatjs', opt), toBinary('echo'), toBinary('cowsay'), toBinary('lolcatjs')];\n  t('pnpx --package cowsay --package lolcatjs -c \\'echo \"hi pnpm\" | cowsay | lolcatjs\\'', inputs);\n  t('pnpm --package cowsay --package lolcatjs -c dlx \\'echo \"hi pnpm\" | cowsay | lolcatjs\\'', inputs);\n  t('pnpm --recursive --parallel exec program', [toBinary('program')]);\n  t('pnpm --recursive --parallel exec program', [toBinary('program')], { manifestScriptNames: new Set(['program']) });\n});\n\ntest('getInputsFromScripts (yarn)', () => {\n  t('yarn exec program', [toBinary('program')]);\n  t('yarn run program', [toBinary('program', opt)]);\n  t('yarn program', [toBinary('program')]);\n  t('yarn run program', [], pkgScripts);\n  t('yarn program', [], pkgScripts);\n  t('yarn node ./script.js', [toBinary('node'), js]);\n  t('yarn --mode skip-build', []);\n  const dir = join(cwd, 'components');\n  t('yarn --cwd components vitest -c vitest.components.config.ts', [toBinary('vitest', { dir }), toConfig('vitest', 'vitest.components.config.ts', { dir })]);\n  t('yarn program -- node script.js', [toBinary('node'), toDeferResolveEntry('script.js', opt)], pkgScripts);\n});\n\ntest('getInputsFromScripts (yarn dlx)', () => {\n  t('yarn dlx pkg', [toBinary('pkg', opt)]);\n  t('yarn dlx -p typescript -p ts-node ts-node -T -e \"console.log(\\'hello!\\')\"', [toDependency('typescript', opt), toDependency('ts-node', opt), toBinary('ts-node', opt)]);\n  t('yarn dlx -p ts-node ts-node ./main.ts', [toDependency('ts-node', opt), toBinary('ts-node', opt), ts]);\n  t('yarn --package=pkg-a -p pkg-b dlx pkg', [toDependency('pkg-a', opt), toDependency('pkg-b', opt), toBinary('pkg', opt)]);\n});\n\ntest('getInputsFromScripts (rollup)', () => {\n  t('rollup --watch --watch.onEnd=\"node ./script.js\"', [toBinary('rollup'), toBinary('node'), js]);\n  t('rollup -p ./require.js', [toBinary('rollup'), req]);\n  t('rollup --plugin @rollup/plugin-node-resolve', [toBinary('rollup'), toDeferResolve('@rollup/plugin-node-resolve')]);\n  t('rollup --configPlugin @rollup/plugin-typescript', [toBinary('rollup'), toDeferResolve('@rollup/plugin-typescript')]);\n});\n\ntest('getInputsFromScripts (\"positionals\")', () => {\n  t('execa --quiet ./script.js', [toBinary('execa'), toDeferResolve('./script.js')]);\n  t('npx --yes execa --quiet ./script.js', [toDeferResolve('./script.js')]);\n  t('ts-node --require pkg/register ./main.ts', [toBinary('ts-node'), ts, toDeferResolve('pkg/register')]);\n  t('ts-node -T ./main.ts', [toBinary('ts-node'), ts]);\n  t('babel-node --inspect=0.0.0.0 ./main.ts', [toBinary('babel-node'), toDeferResolve('./main.ts')]);\n  t('zx --quiet script.js', [toBinary('zx'), toDeferResolve('script.js')]);\n  t('npx --yes zx --quiet script.js', [toDeferResolve('script.js')]);\n  t('jiti script.js', [toBinary('jiti'), toDeferResolve('script.js')]);\n  t('npx jiti script.js', [toBinary('jiti'), toDeferResolve('script.js')]);\n  t('npx --yes jiti script.js', [toDeferResolve('script.js')]);\n  t('npx --no jiti script.js', [toBinary('jiti'), toDeferResolve('script.js')]);\n});\n\ntest('getInputsFromScripts (c8)', () => {\n  t('c8 node ./script.js', [toBinary('c8'), toBinary('node'), js]);\n  t('c8 -- node ./script.js', [toBinary('c8'), toBinary('node'), js]);\n  t('c8 npm test', [toBinary('c8')]);\n  t('c8 check-coverage --lines 95 --per-file npm test', [toBinary('c8')]);\n  t(\"c8 --reporter=lcov --reporter text mocha 'test/**/*.spec.js'\", [toBinary('c8'), toBinary('mocha')]);\n  t('c8 --reporter=lcov --reporter text node --test --test-reporter=@org/rep', [toBinary('c8'), toBinary('node'), toDeferResolve('@org/rep')]);\n});\n\ntest('getInputsFromScripts (nodemon)', () => {\n  t('nodemon --require dotenv/config ./script.js --watch ./script.js', [toBinary('nodemon'), toDeferResolve('dotenv/config')]);\n  t(\"nodemon --exec 'ts-node --esm' ./main.ts | pino-pretty\", [toBinary('nodemon'), toBinary('ts-node'), toBinary('pino-pretty')]);\n  t('nodemon ./script.js', [toBinary('nodemon')]);\n});\n\ntest('getInputsFromScripts (concurrently)', () => {\n  t('concurrently some:script npm:version:node', [toBinary('concurrently')]);\n  t('concurrently \"tsx watch s.ts\" \"tsx watch c.ts\"', [toBinary('concurrently'), toBinary('tsx'), toDeferResolveEntry('s.ts', opt), toBinary('tsx'), toDeferResolveEntry('c.ts', opt)]);\n  t('sleep 2 && concurrently \"npm:watch-*\"', [toBinary('sleep'), toBinary('concurrently')]);\n  t('concurrently -g -c red,green,yellow,blue,magenta pnpm:lint:*', [toBinary('concurrently')]);\n  t('concurrently \"npm:lint:*(!fix)\" program --names \"lint:\" --prefixColors auto', [toBinary('concurrently'), toBinary('program')]);\n});\n\ntest('getInputsFromScripts (double-dash)', () => {\n  t('dotenvx run --convention=nextjs -- tsx watch src/index.ts', [toBinary('dotenvx'), toBinary('tsx'), toDeferResolveEntry('src/index.ts', opt)]);\n  t('env-cmd --no-overrides -- tsx watch src/index.ts', [toBinary('env-cmd'), toBinary('tsx'), toDeferResolveEntry('src/index.ts', opt)]);\n  t('op run --env-file=.env -- node server.ts', [toBinary('op'), toBinary('node'), toDeferResolveEntry('server.ts', opt)]);\n  t('op run --env-file=.env -- node --import=mocks.ts server.ts', [toBinary('op'), toBinary('node'), toDeferResolveEntry('server.ts', opt), toDeferResolve('mocks.ts')]);\n});\n\ntest('getInputsFromScripts (advanced bash syntax)', () => {\n  t('if test \"$NODE_ENV\" = \"production\" ; then make install ; fi ', [toBinary('make')]);\n  t('node -e \"if (NODE_ENV === \\'production\\'){process.exit(1)} \" || make install', [toBinary('node'), toBinary('make')]);\n  t('if ! npx pkg --verbose ; then exit 1 ; fi', [toBinary('pkg'), toBinary('exit')]);\n  t('exec < /dev/tty && node_modules/.bin/cz --hook || true', [toBinary('exec'), toBinary('cz'), toBinary('true')]);\n  t('f() { vite build \"$@\" || (echo content; exit 1;) }; f', [toBinary('vite'), toBinary('echo'), toBinary('exit')]);\n  t('var=$(node ./script.js)', [toBinary('node'), js]);\n  t('var=`node ./script.js`;var=`node ./require.js`', [toBinary('node'), js, toBinary('node'), toDeferResolveEntry('./require.js', opt)]);\n  t('diff <(eslint --format json .) expected.json', [toBinary('diff'), toBinary('eslint')]);\n  t('until curl -s localhost:3000; do sleep 1; done', [toBinary('curl'), toBinary('sleep')]);\n  t('coproc eslint .', [toBinary('eslint')]);\n  t('#!/bin/sh\\n. \"$(dirname \"$0\")/_/husky.sh\"\\nnpx lint-staged', [toBinary('lint-staged'), toBinary('dirname')]);\n  t(`for S in \"s\"; do\\n\\tnpx rc@0.6.0\\n\\tnpx @scope/rc@0.6.0\\ndone`, [toDependency('rc', opt), toDependency('@scope/rc', opt)]);\n  t('curl', [], knownOnly);\n  t('program -- mvn exec:java -Dexec.args=\"-g -f\"', [], knownOnly);\n  t('node --maxWorkers=\"$(node -e \\'process.stdout.write(os.cpus().length.toString())\\')\"', [toBinary('node'), toBinary('node')]);\n  t(`pnpm exec \"cat package.json | jq -r '\\\"\\(.name)@\\(.version)\\\"'\" | sort`, [toBinary('cat'), toBinary('jq')]);\n});\n\ntest('getInputsFromScripts (plugins → double-dash)', () => {\n  t('eslint --fix -- src/', [toBinary('eslint')]);\n  t('vitest -c vitest.unit.config.ts -- --bail 1', [toBinary('vitest'), toConfig('vitest', 'vitest.unit.config.ts')]);\n  t('tsc -p tsconfig.lib.json -- --verbose', [toBinary('tsc'), toConfig('typescript', 'tsconfig.lib.json')]);\n  t('nx exec -- vitest --config vitest.int.config.ts', [toBinary('nx'), toBinary('vitest'), toConfig('vitest', 'vitest.int.config.ts')]);\n});\n\ntest('getInputsFromScripts (plugins → config)', () => {\n  t('tsc -p tsconfig.app.json', [toBinary('tsc'), toConfig('typescript', 'tsconfig.app.json')]);\n  t('tsup -c tsup.server.json', [toBinary('tsup'), toConfig('tsup', 'tsup.server.json')]);\n});\n\ntest('getInputsFromScripts (find -exec)', () => {\n  t(\"find blah -exec node -r {} ';'\", [toBinary('find'), toBinary('node')]);\n  t('find blah -exec node -r {} \\\\;', [toBinary('find'), toBinary('node')]);\n  t('find blah -execdir node -r {} +', [toBinary('find'), toBinary('node')]);\n  t('find blah -name \"*.ts\"', [toBinary('find')]);\n});\n"
  },
  {
    "path": "packages/knip/test/util/graph-sequencer.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { graphSequencer } from '../../src/util/graph-sequencer.ts';\n\ntest('Sort workspaces by dependencies', () => {\n  const graph = new Map([\n    ['pkg-a', new Set(['pkg-b', 'pkg-c'])],\n    ['pkg-b', new Set()],\n    ['pkg-c', new Set(['pkg-b'])],\n  ]);\n\n  const sorted = graphSequencer(graph);\n  assert.deepEqual(sorted.chunks.flat(), ['pkg-b', 'pkg-c', 'pkg-a']);\n});\n\ntest('Sort workspaces handles circular dependencies', () => {\n  const graph = new Map([\n    ['pkg-a', new Set(['pkg-b'])],\n    ['pkg-b', new Set(['pkg-c'])],\n    ['pkg-c', new Set(['pkg-a'])],\n  ]);\n\n  const sorted = graphSequencer(graph);\n  assert.equal(sorted.chunks.flat().length, 3);\n  assert.deepEqual(sorted.chunks.flat(), ['pkg-a', 'pkg-b', 'pkg-c']);\n});\n\ntest('Sort workspaces with included subset', () => {\n  const graph = new Map([\n    ['pkg-a', new Set(['pkg-b', 'pkg-c'])],\n    ['pkg-b', new Set()],\n    ['pkg-c', new Set(['pkg-b'])],\n  ]);\n\n  const workspaces = [\n    { dir: 'pkg-a', name: 'pkg-a' },\n    { dir: 'pkg-b', name: 'pkg-b' },\n  ];\n\n  const sorted = graphSequencer(\n    graph,\n    workspaces.map(w => w.dir)\n  );\n  assert.deepEqual(sorted.chunks.flat(), ['pkg-b', 'pkg-a']);\n});\n\ntest('Sort', () => {\n  const graph = new Map([\n    ['packages/e2e-tests', new Set()],\n    ['packages/server', new Set(['packages/shared', 'packages/app'])],\n    ['packages/shared', new Set([])],\n    ['packages/app', new Set(['packages/shared'])],\n    ['.', new Set()],\n  ]);\n\n  const sorted = graphSequencer(graph);\n  assert.deepEqual(sorted.chunks.flat(), [\n    'packages/e2e-tests',\n    'packages/shared',\n    '.',\n    'packages/app',\n    'packages/server',\n  ]);\n});\n"
  },
  {
    "path": "packages/knip/test/util/has-strictly-ns-references.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { hasStrictlyNsReferences } from '../../src/graph-explorer/operations/has-strictly-ns-references.ts';\nimport type { ImportMaps, ModuleGraph } from '../../src/types/module-graph.ts';\n\nconst map: ModuleGraph = new Map();\n\nconst base: ImportMaps = {\n  refs: new Set(),\n  import: new Map(),\n  importAs: new Map(),\n  importNs: new Map(),\n  reExport: new Map(),\n  reExportAs: new Map(),\n  reExportNs: new Map(),\n};\n\nconst filePath = 'test.ts';\nconst id = 'id';\n\ntest('Strictly namespace refs (no namespaces)', () => {\n  assert.deepStrictEqual(hasStrictlyNsReferences(map, filePath, base, id), [false]);\n});\n\ntest('Strictly namespace refs (single ns)', () => {\n  assert.deepStrictEqual(\n    hasStrictlyNsReferences(\n      map,\n      filePath,\n      {\n        ...base,\n        importNs: new Map([['ns', new Set()]]),\n        refs: new Set(['ns']),\n      },\n      id\n    ),\n    [true, 'ns']\n  );\n});\n\ntest('Strictly namespace refs (no id)', () => {\n  assert.deepStrictEqual(\n    hasStrictlyNsReferences(\n      map,\n      filePath,\n      {\n        ...base,\n        importNs: new Map([['ns', new Set()]]),\n        refs: new Set([]),\n      },\n      id\n    ),\n    [false, 'ns']\n  );\n});\n\ntest('Strictly namespace refs (single ns, no id)', () => {\n  assert.deepStrictEqual(\n    hasStrictlyNsReferences(\n      map,\n      filePath,\n      {\n        ...base,\n        importNs: new Map([]),\n        refs: new Set(['ns']),\n      },\n      id\n    ),\n    [false]\n  );\n});\n\ntest('Strictly namespace refs (multiple ns, no id)', () => {\n  assert.deepStrictEqual(\n    hasStrictlyNsReferences(\n      map,\n      filePath,\n      {\n        ...base,\n        importNs: new Map([\n          ['ns', new Set()],\n          ['ns2', new Set()],\n        ]),\n        refs: new Set(['ns']),\n      },\n      id\n    ),\n    [false, 'ns2']\n  );\n});\n\ntest('Strictly namespace refs (member access)', () => {\n  assert.deepStrictEqual(\n    hasStrictlyNsReferences(\n      map,\n      filePath,\n      {\n        ...base,\n        importNs: new Map([['ns', new Set()]]),\n        refs: new Set(['ns', 'ns.prop']),\n      },\n      id\n    ),\n    [false, 'ns']\n  );\n});\n"
  },
  {
    "path": "packages/knip/test/util/load.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { _load as load } from '../../src/util/loader.ts';\nimport { join } from '../../src/util/path.ts';\nimport { resolve } from '../helpers/resolve.ts';\n\ntest('Should load modules (CommonJS)', async () => {\n  const cwd = resolve('fixtures/load-cjs');\n  await assert.doesNotReject(load(join(cwd, 'index.js')));\n});\n\ntest('Should load modules (ESM)', async () => {\n  const cwd = resolve('fixtures/load-esm');\n  await assert.doesNotReject(load(join(cwd, 'index.js')));\n});\n\ntest('Should load modules (ESM/TS)', async () => {\n  const cwd = resolve('fixtures/load-esm-ts');\n  await assert.doesNotReject(load(join(cwd, 'index.ts')));\n});\n\ntest('Should load JSON5 files', async () => {\n  const cwd = resolve('fixtures/load-json5');\n  const config = await load(join(cwd, 'config.json5'));\n  assert.equal(config.name, 'test-config');\n  assert.equal(config.plugins.length, 2);\n  assert.equal(config.enabled, true);\n});\n"
  },
  {
    "path": "packages/knip/test/util/modules.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport { test } from 'node:test';\nimport { getDefinitelyTypedFor, getPackageFromDefinitelyTyped, sanitizeSpecifier } from '../../src/util/modules.ts';\n\ntest('Should return definitely typed package for package name', () => {\n  assert.equal(getDefinitelyTypedFor('node'), '@types/node');\n  assert.equal(getDefinitelyTypedFor('@npmcli/map-workspaces'), '@types/npmcli__map-workspaces');\n  assert.equal(getDefinitelyTypedFor('@types/node'), '@types/node');\n});\n\ntest('Should return package name from definitely typed package name', () => {\n  assert.equal(getPackageFromDefinitelyTyped('node'), 'node');\n  assert.equal(getPackageFromDefinitelyTyped('npmcli__map-workspaces'), '@npmcli/map-workspaces');\n});\n\ntest('Should sanitize import specifier', () => {\n  assert.equal(sanitizeSpecifier('specifier'), 'specifier');\n  assert.equal(sanitizeSpecifier('/specifier'), '/specifier');\n  assert.equal(sanitizeSpecifier('./specifier'), './specifier');\n  assert.equal(sanitizeSpecifier('../specifier.ext'), '../specifier.ext');\n  assert.equal(sanitizeSpecifier('./icon.svg?raw'), './icon.svg');\n  assert.equal(sanitizeSpecifier('../styles.css#hash'), '../styles.css');\n  assert.equal(sanitizeSpecifier('specifier?query=1'), 'specifier');\n  assert.equal(sanitizeSpecifier('specifier#hash'), 'specifier');\n  assert.equal(sanitizeSpecifier('style-loader!css-loader?modules!./styles.css'), 'style-loader');\n  assert.equal(sanitizeSpecifier('!!style-loader!css-loader?modules!./styles.css'), 'style-loader');\n  assert.equal(sanitizeSpecifier('-!style-loader!css-loader?modules!./styles.css'), 'style-loader');\n  assert.equal(sanitizeSpecifier('css-loader?modules!./styles.css'), 'css-loader');\n  assert.equal(sanitizeSpecifier('./:id/specifier'), './:id/specifier');\n  assert.equal(sanitizeSpecifier('#specifier'), '#specifier');\n  assert.equal(sanitizeSpecifier('#id/specifier'), '#id/specifier');\n  assert.equal(sanitizeSpecifier('~/id/specifier'), '~/id/specifier');\n  assert.equal(sanitizeSpecifier(':/id/specifier'), ':/id/specifier');\n  assert.equal(sanitizeSpecifier(':id/specifier'), ':id/specifier');\n  assert.equal(sanitizeSpecifier('astro:content'), 'astro');\n  assert.equal(sanitizeSpecifier('astro:env/client'), 'astro');\n  assert.equal(sanitizeSpecifier('virtual:specifier'), 'virtual:specifier');\n  assert.equal(sanitizeSpecifier('fs'), 'fs');\n  assert.equal(sanitizeSpecifier('node:fs'), 'node:fs');\n  assert.equal(sanitizeSpecifier('node:assert/strict'), 'node:assert/strict');\n});\n"
  },
  {
    "path": "packages/knip/test/util/remove-export.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { FIX_FLAGS } from '../../src/constants.ts';\nimport { removeExport } from '../../src/util/remove-export.ts';\n\nconst getOpts = (text: string, value: string, flags = FIX_FLAGS.OBJECT_BINDING | FIX_FLAGS.EMPTY_DECLARATION) => {\n  const start = text.indexOf(value);\n  return { text, start, end: start + value.length, flags };\n};\n\ntest('Clean export (FIX_FLAGS.NONE)', () => {\n  {\n    const text = 'export const x = 1;';\n    assert.deepEqual(removeExport(getOpts(text, 'export ', 0)), 'const x = 1;');\n  }\n\n  {\n    const text = 'export default 1';\n    assert.deepEqual(removeExport(getOpts(text, 'export default 1', 0)), '');\n  }\n\n  {\n    const text = 'export default class X {};';\n    assert.deepEqual(removeExport(getOpts(text, 'export default ', 0)), 'class X {};');\n  }\n\n  {\n    const text = 'export = { x,\\ny};';\n    assert.deepEqual(removeExport(getOpts(text, 'export = { x,\\ny}', 0)), ';');\n  }\n\n  {\n    const text = 'export const [ AB, CD ] = [1, 2];';\n    assert.deepEqual(removeExport(getOpts(text, 'AB', 0)), 'export const [ , CD ] = [1, 2];');\n  }\n});\n\ntest('Clean export (FIX_FLAGS.OBJECT_BINDING) - destructuring', () => {\n  {\n    const text = 'export const { AB, CD } = {};';\n    assert.deepEqual(removeExport(getOpts(text, 'AB', 1)), 'export const {  CD } = {};');\n  }\n\n  {\n    const text = 'export const { AB: A_B, CD: C_D } = fn();';\n    assert.deepEqual(removeExport(getOpts(text, 'AB: A_B', 1)), 'export const {  CD: C_D } = fn();');\n  }\n});\n\ntest('Clean export (FIX_FLAGS.OBJECT_BINDING) - enum', () => {\n  {\n    const text = 'export enum E { AB = 1, CD = 2 }';\n    assert.deepEqual(removeExport(getOpts(text, 'CD = 2', 1)), 'export enum E { AB = 1,  }');\n  }\n\n  {\n    const text = \"export enum E { AB = 'AB', CD = 'CD', }\";\n    assert.deepEqual(removeExport(getOpts(text, \"AB = 'AB'\", 1)), \"export enum E {  CD = 'CD', }\");\n  }\n\n  {\n    const text = 'export const { AB: A_B, CD: C_D } = fn();';\n    assert.deepEqual(removeExport(getOpts(text, 'AB: A_B', 1)), 'export const {  CD: C_D } = fn();');\n  }\n});\n\ntest('Clean export (FIX_FLAGS.WITH_NEWLINE)', () => {\n  {\n    const text = 'export enum E { AB = 1,\\nCD = 2 }';\n    assert.deepEqual(removeExport(getOpts(text, 'AB = 1', 5)), 'export enum E { CD = 2 }');\n  }\n  {\n    const text = 'export enum E { AB = 1,\\n CD = 2 }';\n    assert.deepEqual(removeExport(getOpts(text, 'AB = 1', 5)), 'export enum E { CD = 2 }');\n  }\n  {\n    const text = 'export enum E { AB = 1,\\n CD = 2,\\n EF = 3 }';\n    assert.deepEqual(removeExport(getOpts(text, 'CD = 2', 5)), 'export enum E { AB = 1,\\n EF = 3 }');\n  }\n  {\n    const text = 'export enum E { AB = 1,CD = 2,EF = 3 }';\n    assert.deepEqual(removeExport(getOpts(text, 'CD = 2', 5)), 'export enum E { AB = 1,EF = 3 }');\n  }\n  {\n    const text = 'export enum E {\\tAB = 1,\\tCD = 2,\\tEF = 3 }';\n    assert.deepEqual(removeExport(getOpts(text, 'CD = 2', 5)), 'export enum E {\\tAB = 1,\\tEF = 3 }');\n  }\n  {\n    const text = 'export enum E {\\tAB = 1,\\n\\tCD = 2,\\n\\tEF = 3 }';\n    assert.deepEqual(removeExport(getOpts(text, 'CD = 2', 5)), 'export enum E {\\tAB = 1,\\n\\tEF = 3 }');\n  }\n  {\n    const text = 'export enum E {\\tAB = 1,\\n\\tCD = 2,\\n\\tEF = 3,\\n}';\n    assert.deepEqual(removeExport(getOpts(text, 'EF = 3', 5)), 'export enum E {\\tAB = 1,\\n\\tCD = 2,\\n\\t}');\n  }\n  {\n    const text = 'export enum E {\\tAB = 1,\\r\\n\\tCD = 2,\\r\\n\\tEF = 3,\\r\\n}';\n    assert.deepEqual(removeExport(getOpts(text, 'EF = 3', 5)), 'export enum E {\\tAB = 1,\\r\\n\\tCD = 2,\\r\\n\\t}');\n  }\n});\n\ntest('Clean export (FIX_FLAGS.EMPTY_DECLARATION)', () => {\n  {\n    const text = 'export { AB }';\n    assert.deepEqual(removeExport(getOpts(text, 'AB')), '');\n  }\n\n  {\n    const text = 'export { AB, CD }';\n    assert.deepEqual(removeExport(getOpts(text, 'AB')), 'export {  CD }');\n  }\n\n  {\n    const text = 'export { AB, CD, EF }';\n    assert.deepEqual(removeExport(getOpts(text, 'CD')), 'export { AB,  EF }');\n  }\n\n  {\n    const text = 'export { AB, CD } from \"specifier\"';\n    assert.deepEqual(removeExport(getOpts(text, 'CD')), 'export { AB,  } from \"specifier\"');\n  }\n\n  {\n    const text = 'export { AB, CD, EF } from \"specifier\"';\n    assert.deepEqual(removeExport(getOpts(text, 'CD')), 'export { AB,  EF } from \"specifier\"');\n  }\n\n  {\n    const text = 'export { AB } from \"specifier\"';\n    assert.deepEqual(removeExport(getOpts(text, 'AB')), '');\n  }\n\n  {\n    const text = \"export { AB } from './specifier'\";\n    assert.deepEqual(removeExport(getOpts(text, 'AB')), '');\n  }\n\n  {\n    const text = \"export { AB as A_B } from './specifier'\";\n    assert.deepEqual(removeExport(getOpts(text, 'AB as A_B')), '');\n  }\n\n  {\n    const text = 'export{AB}from\"specifier\"';\n    assert.deepEqual(removeExport(getOpts(text, 'AB')), '');\n  }\n\n  {\n    const text = 'export{AB}\\n\\n\\n from\\n\\n\\n \"specifier\"';\n    assert.deepEqual(removeExport(getOpts(text, 'AB')), '');\n  }\n\n  {\n    const text = 'export   {   AB       }     from   \"specifier\"';\n    assert.deepEqual(removeExport(getOpts(text, 'AB')), '');\n  }\n\n  {\n    const text = 'export type { AB }';\n    assert.deepEqual(removeExport(getOpts(text, 'AB')), '');\n  }\n\n  {\n    const text = 'export { type AB }';\n    assert.deepEqual(removeExport(getOpts(text, 'type AB')), '');\n  }\n\n  {\n    const text = 'export type { AB as A_B }';\n    assert.deepEqual(removeExport(getOpts(text, 'AB as A_B')), '');\n  }\n\n  {\n    const text = 'export { type AB as A_B }';\n    assert.deepEqual(removeExport(getOpts(text, 'type AB as A_B')), '');\n  }\n\n  {\n    const text = 'export { type AB, type CD, type EF }';\n    assert.deepEqual(removeExport(getOpts(text, 'type CD')), 'export { type AB,  type EF }');\n  }\n\n  {\n    const text = 'export { type AB, CD, type EF }';\n    assert.deepEqual(removeExport(getOpts(text, 'type AB')), 'export {  CD, type EF }');\n  }\n\n  {\n    const text = 'export { AB, CD, type EF }';\n    assert.deepEqual(removeExport(getOpts(text, 'type EF')), 'export { AB, CD,  }');\n  }\n\n  {\n    const text = 'export { AB,   \\nCD    , \\ntype    EF, }';\n    assert.deepEqual(removeExport(getOpts(text, 'type    EF')), 'export { AB,   \\nCD    , \\n }');\n  }\n\n  {\n    const text = 'export { ,  \\ntype    EF, }   from \"specifier\";';\n    assert.deepEqual(removeExport(getOpts(text, 'type    EF')), ';');\n  }\n});\n"
  },
  {
    "path": "packages/knip/test/util/serialize.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { deserialize, serialize } from 'node:v8';\n\ntest('Should serialize and deserialize file back to original', () => {\n  const file = {\n    imported: undefined,\n    internalImportCache: undefined,\n    imports: {\n      internal: new Map([\n        [\n          'file',\n          {\n            refs: new Set(['ref1', 'ref2']),\n            imported: new Map([['name', new Set(['file', 'file2'])]]),\n            importAs: new Map([['name', new Map([['alias', new Set(['file', 'file2'])]])]]),\n            importNs: new Map([['namespace', new Set(['file', 'file2'])]]),\n            reExport: new Map([['*', new Set(['file', 'file2'])]]),\n            reExportAs: new Map([['name', new Map([['alias', new Set(['file', 'file2'])]])]]),\n            reExportNs: new Map([['namespace', new Set(['file', 'file2'])]]),\n          },\n        ],\n        [\n          'file2',\n          {\n            refs: new Set(['ref1', 'ref2']),\n            imported: new Map([['name', new Set(['file', 'file2'])]]),\n            importAs: new Map([\n              [\n                'name',\n                new Map([\n                  ['alias', new Set(['file', 'file2'])],\n                  ['alias2', new Set(['file', 'file2'])],\n                ]),\n              ],\n            ]),\n            importNs: new Map([['namespace', new Set(['file', 'file2'])]]),\n            reExport: new Map([\n              ['*', new Set(['file', 'file2'])],\n              ['id', new Set(['file', 'file2'])],\n            ]),\n            reExportAs: new Map([['name', new Map([['alias', new Set(['file', 'file2'])]])]]),\n            reExportNs: new Map([\n              ['namespace', new Set(['file', 'file2'])],\n              ['namespace2', new Set(['file', 'file2'])],\n            ]),\n          },\n        ],\n      ]),\n      external: new Set(['ext']),\n      unresolved: new Set([{ specifier: 'unresolved', pos: 1, line: 1, col: 1 }]),\n    },\n    exports: {\n      exported: new Map(),\n      duplicate: new Set([\n        [\n          { symbol: 'def', pos: 1, line: 1, col: 1 },\n          { symbol: 'dup', pos: 1, line: 2, col: 2 },\n        ],\n      ]),\n    },\n    scripts: new Set(['script', 'script2']),\n  };\n\n  assert.deepEqual(deserialize(serialize(file)), file);\n});\n"
  },
  {
    "path": "packages/knip/test/util/string.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { pad, truncate, truncateStart } from '../../src/util/string.ts';\n\ntest('truncate', () => {\n  assert.equal(truncate('hello world', 8), 'hello w…');\n  assert.equal(truncate('short', 8), 'short');\n  assert.equal(truncate('\\u001b[31mred\\u001b[0m text', 5), '\\u001b[31mred\\u001b[0m …');\n  assert.equal(truncate('\\u001b[31mlong red text\\u001b[0m', 8), '\\u001b[31mlong re…\\u001b[0m');\n});\n\ntest('truncateStart', () => {\n  assert.equal(truncateStart('hello world', 8), '…o world');\n  assert.equal(truncateStart('short', 8), 'short');\n  assert.equal(truncateStart('\\u001b[31mred\\u001b[0m text', 7), '\\u001b[31m…d\\u001b[0m text');\n  assert.equal(truncateStart('\\u001b[31mlong red text\\u001b[0m', 8), '\\u001b[31m…ed text\\u001b[0m');\n});\n\ntest('pad right', () => {\n  assert.equal(pad('test', 8, '-', 'right'), '----test');\n  assert.equal(pad('\\u001b[31mred\\u001b[0m', 8, '-', 'right'), '-----\\u001b[31mred\\u001b[0m');\n});\n\ntest('pad center', () => {\n  assert.equal(pad('test', 8, '-', 'center'), '--test--');\n  assert.equal(pad('\\u001b[31mred\\u001b[0m', 8, '-', 'center'), '--\\u001b[31mred\\u001b[0m---');\n});\n\ntest('pad left', () => {\n  assert.equal(pad('test', 8, '-'), 'test----');\n  assert.equal(pad('\\u001b[31mred\\u001b[0m', 8, '-'), '\\u001b[31mred\\u001b[0m-----');\n});\n"
  },
  {
    "path": "packages/knip/test/util/table.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { Table } from '../../src/util/table.ts';\n\ntest('Render table with column gaps and truncated values', () => {\n  const table = new Table({ maxWidth: 72, truncateStart: ['col-2'] });\n  table.row();\n  table.cell('col-1', '../../runtime/client/idle.prebuilt.js');\n  table.cell('col-2', 'packages/astro/src/core/client-directive/default.ts:1:25');\n  table.row();\n  table.cell('col-1', '../../runtime/client/visible.prebuilt.js');\n  table.cell('col-2', 'packages/astro/src/core/client-directive/default.ts:5:28');\n\n  const expected = `\n../../runtime/client/idle.pre…  …c/core/client-directive/default.ts:1:25\n../../runtime/client/visible.…  …c/core/client-directive/default.ts:5:28`;\n\n  const output = table.toString();\n  assert.equal(expected.trimStart(), output);\n  assert.equal(output.split('\\n')[0].length, 72);\n});\n\ntest('Render single column table with start-truncated values', () => {\n  const table = new Table({ maxWidth: 40, truncateStart: ['filePath'] });\n  table.row();\n  table.cell('filePath', 'packages/astro/src/core/client-directive/default.ts');\n  table.row();\n  table.cell('filePath', 'packages/astro/src/integrations/hooks.ts');\n\n  const expected = `\n…ro/src/core/client-directive/default.ts\npackages/astro/src/integrations/hooks.ts`;\n\n  assert.equal(expected.trimStart(), table.toString());\n});\n\ntest('Render table with no- and start-truncated values', () => {\n  const table = new Table({ maxWidth: 72, noTruncate: ['col-3'], truncateStart: ['col-4'] });\n  table.row();\n  table.cell('col-1', 'renderFontFace');\n  table.cell('col-2', undefined);\n  table.cell('col-3', 'function');\n  table.cell('col-4', 'packages/astro/src/assets/fonts/implementations/css-renderer.ts:15:17');\n  table.row();\n  table.cell('col-1', 'telemetryNotice');\n  table.cell('col-2', 'msg');\n  table.cell('col-3', undefined);\n  table.cell('col-4', 'packages/astro/src/core/messages.ts:123:17');\n  table.row();\n  table.cell('col-1', 'normalizeInjectedTypeFilename');\n  table.cell('col-2', undefined);\n  table.cell('col-3', 'function');\n  table.cell('col-4', 'packages/astro/src/integrations/hooks.ts:157:17');\n\n  const expected = `\nrenderFontFace           function  …mplementations/css-renderer.ts:15:17\ntelemetryNotice     msg            …es/astro/src/core/messages.ts:123:17\nnormalizeInjected…       function  …tro/src/integrations/hooks.ts:157:17`;\n\n  const output = table.toString();\n  assert.equal(expected.trimStart(), output);\n  assert.equal(output.split('\\n')[0].length, 72);\n  assert.equal(output.split('\\n')[1].length, 72);\n  assert.equal(output.split('\\n')[2].length, 72);\n});\n\ntest('Render table with header', () => {\n  const table = new Table({ header: true });\n  table.row();\n  table.cell('A', 'A1');\n  table.cell('B', 'B1');\n  table.cell('C', 1);\n  table.row();\n  table.cell('A', 'A2');\n  table.cell('B', 'B2');\n  table.cell('C', 2);\n\n  const expected = `\nA   B   C\n--  --  -\nA1  B1  1\nA2  B2  2`;\n\n  const output = table.toString();\n  assert.equal(expected.trimStart(), output);\n});\n"
  },
  {
    "path": "packages/knip/test/util/workspace-selectors.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport type { PackageJson, WorkspacePackage } from '../../src/types/package-json.ts';\nimport {\n  matchWorkspacesByDirGlob,\n  matchWorkspacesByPkgName,\n  parseWorkspaceSelector,\n} from '../../src/util/workspace-selectors.ts';\n\ntest('parseWorkspaceSelector: negated package name', () => {\n  const selector = parseWorkspaceSelector('!@myorg/pkg', '/test/cwd');\n  assert.equal(selector.type, 'pkg-name');\n  assert.equal(selector.pattern, '@myorg/pkg');\n  assert.equal(selector.isNegated, true);\n});\n\ntest('parseWorkspaceSelector: negated directory glob', () => {\n  const selector = parseWorkspaceSelector('!./apps/*', '/test/cwd');\n  assert.equal(selector.type, 'dir-glob');\n  assert.equal(selector.pattern, 'apps/*');\n  assert.equal(selector.isNegated, true);\n});\n\ntest('parseWorkspaceSelector: negated directory path', () => {\n  const selector = parseWorkspaceSelector('!apps/web', '/test/cwd');\n  assert.equal(selector.type, 'dir-path');\n  assert.equal(selector.pattern, 'apps/web');\n  assert.equal(selector.isNegated, true);\n});\n\ntest('parseWorkspaceSelector: directory glob', () => {\n  const selector = parseWorkspaceSelector('./apps/*', '/test/cwd');\n  assert.equal(selector.type, 'dir-glob');\n  assert.equal(selector.pattern, 'apps/*');\n  assert.equal(selector.isNegated, false);\n});\n\ntest('parseWorkspaceSelector: package name', () => {\n  const selector = parseWorkspaceSelector('@myorg/pkg', '/test/cwd');\n  assert.equal(selector.type, 'pkg-name');\n  assert.equal(selector.pattern, '@myorg/pkg');\n  assert.equal(selector.isNegated, false);\n});\n\ntest('parseWorkspaceSelector: package name with glob', () => {\n  const selector = parseWorkspaceSelector('@myorg/*', '/test/cwd');\n  assert.equal(selector.type, 'pkg-name');\n  assert.equal(selector.pattern, '@myorg/*');\n  assert.equal(selector.isNegated, false);\n});\n\ntest('matchWorkspacesByPkgName: exact match', () => {\n  const packages = new Map<string, WorkspacePackage>([\n    [\n      'packages/a',\n      {\n        pkgName: '@test/a',\n        name: 'packages/a',\n        dir: '',\n        manifestPath: '',\n        manifestStr: '',\n        manifest: {} as PackageJson,\n      },\n    ],\n    [\n      'packages/b',\n      {\n        pkgName: '@test/b',\n        name: 'packages/b',\n        dir: '',\n        manifestPath: '',\n        manifestStr: '',\n        manifest: {} as PackageJson,\n      },\n    ],\n  ]);\n\n  const pkgNameToWorkspaceName = new Map<string, string>();\n  for (const [workspaceName, pkg] of packages.entries()) {\n    if (pkg.pkgName) pkgNameToWorkspaceName.set(pkg.pkgName, workspaceName);\n  }\n  const pkgNames = Array.from(pkgNameToWorkspaceName.keys());\n\n  const result = matchWorkspacesByPkgName('@test/a', pkgNames, pkgNameToWorkspaceName);\n  assert.deepEqual(result, ['packages/a']);\n});\n\ntest('matchWorkspacesByPkgName: wildcard match', () => {\n  const packages = new Map<string, WorkspacePackage>([\n    [\n      'packages/a',\n      {\n        pkgName: '@test/a',\n        name: 'packages/a',\n        dir: '',\n        manifestPath: '',\n        manifestStr: '',\n        manifest: {} as PackageJson,\n      },\n    ],\n    [\n      'packages/b',\n      {\n        pkgName: '@test/b',\n        name: 'packages/b',\n        dir: '',\n        manifestPath: '',\n        manifestStr: '',\n        manifest: {} as PackageJson,\n      },\n    ],\n    [\n      'apps/web',\n      {\n        pkgName: '@web/app',\n        name: 'apps/web',\n        dir: '',\n        manifestPath: '',\n        manifestStr: '',\n        manifest: {} as PackageJson,\n      },\n    ],\n  ]);\n\n  const pkgNameToWorkspaceName = new Map<string, string>();\n  for (const [workspaceName, pkg] of packages.entries()) {\n    if (pkg.pkgName) pkgNameToWorkspaceName.set(pkg.pkgName, workspaceName);\n  }\n  const pkgNames = Array.from(pkgNameToWorkspaceName.keys());\n\n  const result = matchWorkspacesByPkgName('@test/*', pkgNames, pkgNameToWorkspaceName);\n  assert.deepEqual(result.sort(), ['packages/a', 'packages/b']);\n});\n\ntest('matchWorkspacesByPkgName: brace expansion', () => {\n  const packages = new Map<string, WorkspacePackage>([\n    [\n      'packages/a',\n      {\n        pkgName: '@test/a',\n        name: 'packages/a',\n        dir: '',\n        manifestPath: '',\n        manifestStr: '',\n        manifest: {} as PackageJson,\n      },\n    ],\n    [\n      'packages/b',\n      {\n        pkgName: '@test/b',\n        name: 'packages/b',\n        dir: '',\n        manifestPath: '',\n        manifestStr: '',\n        manifest: {} as PackageJson,\n      },\n    ],\n    [\n      'packages/c',\n      {\n        pkgName: '@test/c',\n        name: 'packages/c',\n        dir: '',\n        manifestPath: '',\n        manifestStr: '',\n        manifest: {} as PackageJson,\n      },\n    ],\n  ]);\n\n  const pkgNameToWorkspaceName = new Map<string, string>();\n  for (const [workspaceName, pkg] of packages.entries()) {\n    if (pkg.pkgName) pkgNameToWorkspaceName.set(pkg.pkgName, workspaceName);\n  }\n  const pkgNames = Array.from(pkgNameToWorkspaceName.keys());\n\n  const result = matchWorkspacesByPkgName('@test/{a,c}', pkgNames, pkgNameToWorkspaceName);\n  assert.deepEqual(result.sort(), ['packages/a', 'packages/c']);\n});\n\ntest('matchWorkspacesByDirGlob: wildcard', () => {\n  const workspaceNames = ['packages/a', 'packages/b', 'apps/web', 'apps/api'];\n\n  const result = matchWorkspacesByDirGlob('packages/*', workspaceNames);\n  assert.deepEqual(result.sort(), ['packages/a', 'packages/b']);\n});\n\ntest('matchWorkspacesByDirGlob: brace expansion', () => {\n  const workspaceNames = ['packages/a', 'packages/b', 'apps/web', 'apps/api'];\n\n  const result = matchWorkspacesByDirGlob('{packages,apps}/a*', workspaceNames);\n  assert.deepEqual(result.sort(), ['apps/api', 'packages/a']);\n});\n"
  },
  {
    "path": "packages/knip/test/util/workspace.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { byPathDepth } from '../../src/util/workspace.ts';\n\ntest('Sort workspaces by path depth', () => {\n  assert.deepEqual(['first', 'sec/ond', 'th/i/rd'].sort(byPathDepth), ['first', 'sec/ond', 'th/i/rd']);\n  assert.deepEqual(['th/i/rd', 'first', 'sec/ond'].sort(byPathDepth), ['first', 'sec/ond', 'th/i/rd']);\n  assert.deepEqual(['apps/*', 'apps/first'].sort(byPathDepth), ['apps/*', 'apps/first']);\n  assert.deepEqual(['pkg/first', 'pkg/**', 'pkg/de/ep'].sort(byPathDepth), ['pkg/**', 'pkg/first', 'pkg/de/ep']);\n});\n\ntest('Sort workspaces by path depth (reversed)', () => {\n  assert.deepEqual(['first', 'sec/ond', 'th/i/rd'].sort(byPathDepth).reverse(), ['th/i/rd', 'sec/ond', 'first']);\n  assert.deepEqual(['pkg/aaa', 'pkg/**', 'pkg/de/ep'].sort(byPathDepth).reverse(), ['pkg/de/ep', 'pkg/aaa', 'pkg/**']);\n});\n"
  },
  {
    "path": "packages/knip/test/workspace-selectors-errors.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/workspaces');\n\ntest('Error: non-existent package name (exact match)', async () => {\n  await assert.rejects(\n    async () => {\n      const options = await createOptions({ cwd, workspace: '@nonexistent/package' });\n      await main(options);\n    },\n    (error: Error) => {\n      return error.message.includes('did not match any workspaces') && error.message.includes('@nonexistent/package');\n    },\n    'Should throw ConfigurationError for non-existent exact package name'\n  );\n});\n\ntest('Error: non-existent directory path', async () => {\n  await assert.rejects(\n    async () => {\n      const options = await createOptions({ cwd, workspace: 'packages/nonexistent' });\n      await main(options);\n    },\n    (error: Error) => {\n      return error.message.includes('not found') && error.message.includes('packages/nonexistent');\n    },\n    'Should throw ConfigurationError for non-existent directory'\n  );\n});\n\ntest('Error: directory glob with no matches', async () => {\n  await assert.rejects(\n    async () => {\n      const options = await createOptions({ cwd, workspace: './nonexistent/*' });\n      await main(options);\n    },\n    (error: Error) => {\n      return error.message.includes('did not match any workspaces') && error.message.includes('nonexistent/*');\n    },\n    'Should throw ConfigurationError for directory glob with no matches'\n  );\n});\n\ntest('No error: package name glob with no matches (wildcards allowed)', async () => {\n  // This should NOT throw because globs can match 0 workspaces\n  const options = await createOptions({ cwd, workspace: '@nonexistent/*' });\n  const { counters } = await main(options);\n  assert.equal(counters.total, 0, 'Should have 0 total issues when no workspaces selected');\n  assert.equal(counters.processed, 0, 'Should have 0 processed files when no workspaces selected');\n});\n\ntest('No error: package name glob with ? and [] with no matches', async () => {\n  const options1 = await createOptions({ cwd, workspace: '@nonexistent/?' });\n  const { counters: counters1 } = await main(options1);\n  assert.equal(counters1.total, 0);\n  assert.equal(counters1.processed, 0);\n\n  const options2 = await createOptions({ cwd, workspace: '@nonexistent/[ab]' });\n  const { counters: counters2 } = await main(options2);\n  assert.equal(counters2.total, 0);\n  assert.equal(counters2.processed, 0);\n});\n\ntest('No error: negated directory glob with no matches', async () => {\n  const options = await createOptions({\n    cwd,\n    workspace: ['@fixtures/workspaces__*', '!./nonexistent/*'],\n  });\n  const { counters } = await main(options);\n  assert(counters.total > 0, 'Should still analyze workspaces when negation matches nothing');\n});\n"
  },
  {
    "path": "packages/knip/test/workspace-selectors-root.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/workspaces-root');\n\ntest('Exclude root workspace with !. selector', async () => {\n  const options = await createOptions({ cwd, workspace: '!.' });\n  const { issues, counters, selectedWorkspaces } = await main(options);\n\n  assert(!selectedWorkspaces?.includes('.'));\n  assert(selectedWorkspaces?.includes('app'));\n  assert(issues.unlisted['app/index.ts']['vanilla-js']);\n  assert(issues.unlisted['app/tsconfig.json']['@fixtures/workspaces__tsconfig']);\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unlisted: 2,\n    processed: 2,\n    total: 2,\n  });\n});\n\ntest('Select only root workspace with . selector', async () => {\n  const options = await createOptions({ cwd, workspace: '.' });\n  const { issues, counters, selectedWorkspaces } = await main(options);\n\n  assert(selectedWorkspaces?.includes('.'));\n  assert(!selectedWorkspaces?.includes('app'));\n  assert(issues.unlisted['scripts/index.ts']['js-yaml']);\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    dependencies: 1,\n    unlisted: 1,\n    binaries: 1,\n    processed: 1,\n    total: 1,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/workspace-selectors.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport { join } from '../src/util/path.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/workspaces');\n\ntest('Select workspace by package name', async () => {\n  const options = await createOptions({ cwd, workspace: '@fixtures/workspaces__shared' });\n  const { issues, counters, includedWorkspaceDirs } = await main(options);\n\n  assert(issues.types['packages/shared/types.ts']['UnusedEnum']);\n\n  assert.equal(includedWorkspaceDirs.length, 4);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    types: 1,\n    processed: 4,\n    total: 4,\n  });\n});\n\ntest('Select workspaces by package name glob with brace expansion', async () => {\n  const options = await createOptions({ cwd, workspace: ['@fixtures/workspaces__{shared,backend}'] });\n  const { issues, counters } = await main(options);\n\n  assert(issues.types['packages/shared/types.ts']['UnusedEnum']);\n  assert(issues.unlisted['apps/backend/index.ts']['globby']);\n  assert(issues.unlisted['apps/backend/index.ts']['js-yaml']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    types: 1,\n    dependencies: 2,\n    unlisted: 2,\n    processed: 4,\n    total: 4,\n  });\n});\n\ntest('Select workspaces by package name with wildcard', async () => {\n  const options = await createOptions({ cwd, workspace: '@fixtures/workspaces__*' });\n  const { issues, counters } = await main(options);\n\n  assert('docs/dangling.ts' in issues.files);\n  assert(issues.types['packages/shared/types.ts']['UnusedEnum']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    dependencies: 2,\n    unlisted: 4,\n    exports: 1,\n    types: 1,\n    processed: 7,\n    total: 7,\n  });\n});\n\ntest('Select workspaces by directory glob pattern', async () => {\n  const options = await createOptions({ cwd, workspace: './apps/*' });\n  const { issues, counters, includedWorkspaceDirs } = await main(options);\n\n  assert(issues.unlisted['apps/frontend/index.ts']['vanilla-js']);\n  assert(issues.unlisted['apps/backend/index.ts']['globby']);\n  assert(issues.unlisted['apps/backend/index.ts']['js-yaml']);\n  assert(issues.dependencies['apps/backend/package.json']['next']);\n  assert(issues.dependencies['apps/backend/package.json']['picomatch']);\n  assert(includedWorkspaceDirs.includes(join(cwd, 'apps/frontend')));\n  assert(includedWorkspaceDirs.includes(join(cwd, 'apps/backend')));\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    dependencies: 2,\n    unlisted: 3,\n    processed: 2,\n    total: 2,\n  });\n});\n\ntest('Exclude workspace by package name', async () => {\n  const options = await createOptions({ cwd, workspace: ['@fixtures/workspaces__*', '!@fixtures/workspaces__tools'] });\n  const { issues, counters, includedWorkspaceDirs, selectedWorkspaces } = await main(options);\n\n  assert('docs/dangling.ts' in issues.files);\n  assert(issues.types['packages/shared/types.ts']['UnusedEnum']);\n  assert(!includedWorkspaceDirs.includes(join(cwd, 'packages/tools')));\n  assert(!selectedWorkspaces?.includes('packages/tools'));\n  assert(selectedWorkspaces?.includes('apps/frontend'));\n  assert(selectedWorkspaces?.includes('apps/backend'));\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    dependencies: 2,\n    unlisted: 3,\n    types: 1,\n    processed: 5,\n    total: 5,\n  });\n});\n\ntest('Exclude workspaces by directory glob pattern', async () => {\n  const options = await createOptions({ cwd, workspace: ['@fixtures/workspaces__*', '!./apps/*'] });\n  const { issues, counters, includedWorkspaceDirs, selectedWorkspaces } = await main(options);\n\n  assert(includedWorkspaceDirs.includes(join(cwd, 'apps/frontend')));\n  assert(includedWorkspaceDirs.includes(join(cwd, 'apps/backend')));\n  assert(!selectedWorkspaces?.includes('apps/frontend'));\n  assert(!selectedWorkspaces?.includes('apps/backend'));\n  assert(issues.types['packages/shared/types.ts']['UnusedEnum']);\n  assert(issues.exports['packages/tools/utils.ts']['helperFn']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    unlisted: 1,\n    exports: 1,\n    types: 1,\n    processed: 7,\n    total: 7,\n  });\n});\n\ntest('Exclude workspace by directory path', async () => {\n  const options = await createOptions({ cwd, workspace: ['@fixtures/workspaces__*', '!apps/frontend'] });\n  const { issues, counters, selectedWorkspaces } = await main(options);\n\n  assert(!selectedWorkspaces?.includes('apps/frontend'));\n  assert(!issues.unlisted['apps/frontend/index.ts']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    dependencies: 2,\n    unlisted: 3,\n    exports: 1,\n    types: 1,\n    processed: 7,\n    total: 7,\n  });\n});\n\ntest('Only negation selector: exclude workspace from all', async () => {\n  const options = await createOptions({ cwd, workspace: ['!apps/frontend'] });\n  const { issues, counters, selectedWorkspaces } = await main(options);\n\n  assert(!selectedWorkspaces?.includes('apps/frontend'));\n  assert(selectedWorkspaces?.includes('apps/backend'));\n  assert(!issues.unlisted['apps/frontend/index.ts']);\n  assert(issues.unlisted['apps/backend/index.ts']['globby']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    dependencies: 4,\n    unlisted: 3,\n    exports: 1,\n    types: 1,\n    processed: 7,\n    total: 7,\n  });\n});\n\ntest('Multiple workspace selectors union', async () => {\n  const options = await createOptions({\n    cwd,\n    workspace: ['@fixtures/workspaces__shared', '@fixtures/workspaces__backend'],\n  });\n  const { issues, counters, selectedWorkspaces } = await main(options);\n\n  assert(issues.types['packages/shared/types.ts']['UnusedEnum']);\n  assert(issues.unlisted['apps/backend/index.ts']['globby']);\n  assert(selectedWorkspaces?.length === 2);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    types: 1,\n    dependencies: 2,\n    unlisted: 2,\n    processed: 4,\n    total: 4,\n  });\n});\n\ntest('Mixed directory and package name selectors', async () => {\n  const options = await createOptions({ cwd, workspace: ['./apps/frontend', '@fixtures/workspaces__shared'] });\n  const { issues, counters } = await main(options);\n\n  assert(issues.types['packages/shared/types.ts']['UnusedEnum']);\n  assert(issues.unlisted['apps/frontend/index.ts']['vanilla-js']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    types: 1,\n    unlisted: 1,\n    processed: 4,\n    total: 4,\n  });\n});\n\ntest('Backward compatibility: single directory workspace', async () => {\n  const options = await createOptions({ cwd, workspace: 'packages/shared' });\n  const { issues, counters } = await main(options);\n\n  assert(issues.types['packages/shared/types.ts']['UnusedEnum']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    types: 1,\n    processed: 4,\n    total: 4,\n  });\n});\n\ntest('Strict mode: only analyze explicitly selected workspaces', async () => {\n  const options = await createOptions({ cwd, workspace: '@fixtures/workspaces__shared', isStrict: true });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    types: 1,\n    processed: 2,\n    total: 2,\n  });\n});\n\ntest('Empty selection after exclusion', async () => {\n  const options = await createOptions({\n    cwd,\n    workspace: ['@fixtures/workspaces__shared', '!@fixtures/workspaces__shared'],\n  });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, baseCounters);\n});\n"
  },
  {
    "path": "packages/knip/test/workspaces-circular-symlinks.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { _syncGlob } from '../src/util/glob.ts';\nimport { resolve } from './helpers/resolve.ts';\n\ntest('syncGlob should not traverse circular symlinks', () => {\n  const libACwd = resolve('fixtures/workspaces-circular-symlinks/packages/lib-a');\n  const files = _syncGlob({ patterns: ['./**/*.ts'], cwd: libACwd });\n\n  const expected = 1;\n  assert.equal(files.length, expected, `Expected ${expected} file but found ${files.length}`);\n  assert.equal(files[0], 'index.ts');\n});\n"
  },
  {
    "path": "packages/knip/test/workspaces-circular.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/workspaces-circular');\n\ntest('Find unused dependencies, exports and files in workspaces (circular)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/workspaces-cross-reference.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/workspaces-cross-reference');\n\ntest('Resolve imports in separate workspaces without entry file', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['packages/lib-a/mod-a.ts']['unused']);\n  assert(issues.exports['packages/lib-b/mod-b.ts']['unused']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 2,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/workspaces-dts.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/workspaces-dts');\n\ntest('Find unused un-built exports across workspaces', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['packages/shared/src/index.js']['unusedFunction']);\n  assert(issues.exports['packages/shared/src/unused-function.js']['unusedFunction']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 2,\n    processed: 8,\n    total: 8,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/workspaces-entry-files.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/workspaces-entry-files');\n\ntest('Use root plugin entry config in workspaces', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    total: 4,\n    processed: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/workspaces-ignored.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/workspaces-ignored');\n\ntest('Ignore workspaces', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters, configurationHints } = await main(options);\n\n  assert(issues.binaries['packages/e/package.json']['not-ignored']);\n  assert(issues.binaries['packages/production/package.json']['ignored-in-production-mode']);\n  assert(issues.binaries['packages/deep/unignored/package.json']['unignored']);\n\n  assert.deepEqual(configurationHints, [\n    { type: 'ignoreWorkspaces', identifier: 'packages/not-found' },\n    { type: 'ignoreWorkspaces', identifier: 'packages/production-not-found' },\n    { type: 'ignoreWorkspaces', identifier: 'packages/un/**/used' },\n    { type: 'ignoreWorkspaces', identifier: 'packages/wut/*' },\n  ]);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 3,\n  });\n});\n\ntest('Ignore workspaces (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { issues, counters } = await main(options);\n\n  assert(issues.binaries['packages/e/package.json']['not-ignored']);\n  assert(issues.binaries['packages/deep/unignored/package.json']['unignored']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    binaries: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/workspaces-include-entry-exports.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/workspaces-include-entry-exports');\n\ntest('Respect includeEntryExports per workspace', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert(issues.exports['packages/lib/index.js']['unused']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    exports: 1,\n    processed: 2,\n    total: 2,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/workspaces-module-resolution.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/workspaces-module-resolution');\n\ntest('Resolve modules properly across multiple workspaces', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 5,\n    total: 5,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/workspaces-nested.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/workspaces-nested');\n\nconst expectedConfigurationHints = [\n  { type: 'ignoreDependencies', identifier: 'ignored-dep-global', workspaceName: '.' },\n  { type: 'ignoreDependencies', identifier: 'unused-ignored-dep-global', workspaceName: '.' },\n  { type: 'ignoreBinaries', identifier: 'unused-ignored-bin-global', workspaceName: '.' },\n  { type: 'ignoreDependencies', identifier: 'unused-ignored-dep-L-3', workspaceName: 'L-1-1/L-1-2/L-1-3' },\n  { type: 'ignoreBinaries', identifier: 'unused-ignored-bin-L-2', workspaceName: 'L-1-1/L-1-2' },\n  { type: 'ignoreWorkspaces', identifier: 'unused-ignored-workspace' },\n];\n\ntest('Find unused dependencies in nested workspaces with default config in production mode (default)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters, configurationHints } = await main(options);\n\n  assert(issues.dependencies['L-1-1/L-1-2/L-1-3/package.json']['package-1-3-dev']);\n  assert(issues.devDependencies['L-1-1/package.json']['package-1-1-dev']);\n  assert(issues.devDependencies['L-1-1/L-1-2/package.json']['package-1-2-dev']);\n  assert(issues.unlisted['L-1-1/L-1-2/index.ts']['ignored-dep-L-3']);\n  assert(issues.binaries['L-1-1/L-1-2/L-1-3/package.json']['ignored-bin-L-2']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    dependencies: 1,\n    devDependencies: 2,\n    unlisted: 1,\n    binaries: 1,\n    processed: 3,\n    total: 3,\n  });\n\n  assert.deepEqual(configurationHints, expectedConfigurationHints);\n});\n\ntest('Find unused dependencies in nested workspaces with default config in production mode (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { issues, counters } = await main(options);\n\n  assert(issues.dependencies['L-1-1/L-1-2/L-1-3/package.json']['package-1-3-dev']);\n  assert(issues.unlisted['L-1-1/L-1-2/index.ts']['ignored-dep-L-3']);\n  assert(issues.binaries['L-1-1/L-1-2/L-1-3/package.json']['ignored-bin-L-2']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    dependencies: 1,\n    unlisted: 1,\n    binaries: 1,\n    processed: 3,\n    total: 3,\n  });\n});\n\ntest('Find unused dependencies in nested workspaces with default config in production mode (strict)', async () => {\n  const options = await createOptions({ cwd, isStrict: true });\n  const { issues, counters } = await main(options);\n\n  assert(issues.dependencies['L-1-1/L-1-2/L-1-3/package.json']['package-1-3-dev']);\n  assert(issues.unlisted['L-1-1/L-1-2/L-1-3/index.ts']['package-1-2']);\n  assert(issues.unlisted['L-1-1/L-1-2/index.ts']['ignored-dep-L-3']);\n  assert(issues.binaries['L-1-1/L-1-2/L-1-3/package.json']['ignored-bin-L-2']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    dependencies: 1,\n    unlisted: 2,\n    binaries: 1,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/workspaces-noconfig-plugin.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/workspaces-noconfig-plugin');\n\ntest('Find entries of plugins without config file across workspaces', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/workspaces-paths-compilers.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/workspaces-paths-compilers');\n\ntest('Find unused dependencies, exports and files in workspaces (w/ paths + compilers)', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 7,\n    total: 7,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/workspaces-paths.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/workspaces-paths');\n\ntest('Find unused dependencies, exports and files in workspaces (w/ paths)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert.equal(Object.keys(issues.unlisted).length, 1);\n  assert(issues.unlisted['packages/lib-e/src/index.ts']['not']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unlisted: 1,\n    processed: 15,\n    total: 15,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/workspaces-plugin-circular.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/workspaces-plugin-circular');\n\ntest('Does not loop infinitely when configuration files have circular dependencies', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/workspaces-plugin-config.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/workspaces-plugin-config');\n\ntest('Use root plugin config in workspaces', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    total: 26,\n    processed: 26,\n  });\n});\n\ntest('Use root plugin config in workspaces (strict production)', async () => {\n  const options = await createOptions({ cwd, isStrict: true });\n  const { issues, counters } = await main(options);\n\n  assert('packages/frontend/components/component.js' in issues.files);\n  assert('packages/shared/components/component.js' in issues.files);\n  assert('packages/shared/jest-setup.ts' in issues.files);\n  assert.equal(Object.keys(issues.files).length, 3);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 3,\n    total: 8,\n    processed: 8,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/workspaces-plugin-overlap.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/workspaces-plugin-overlap');\n\ntest('Handles config file shared by multiple plugins in workspaces', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/workspaces-pnpm.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/workspaces-pnpm');\n\ntest('Find unused dependencies, exports and files in workspaces (loose)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert.equal(Object.keys(issues.unlisted).length, 1);\n  assert(issues.unlisted['apps/app-a/index.ts']['unlisted']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    unlisted: 1,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/workspaces-self-reference.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/workspaces-self-reference');\n\ntest('Find unused dependencies, exports and files in workspaces with cross self-references', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 3,\n    total: 3,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/workspaces-tooling.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/workspaces-tooling');\n\ntest('Find unused dependencies, exports and files in workspaces with eslint-config package', async () => {\n  const options = await createOptions({ cwd });\n  const { counters } = await main(options);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    processed: 7,\n    total: 7,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/workspaces.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/workspaces');\n\ntest('Find unused dependencies, exports and files in workspaces (default)', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert('docs/dangling.ts' in issues.files);\n\n  assert.equal(Object.keys(issues.dependencies['package.json']).length, 2);\n  assert.equal(Object.keys(issues.dependencies['apps/backend/package.json']).length, 2);\n\n  assert(issues.dependencies['package.json']['minimist']);\n  assert(issues.dependencies['package.json']['zod']);\n  assert(issues.dependencies['apps/backend/package.json']['next']);\n  assert(issues.dependencies['apps/backend/package.json']['picomatch']);\n\n  assert.equal(Object.keys(issues.unlisted).length, 3);\n  assert(issues.unlisted['apps/frontend/index.ts']['vanilla-js']);\n  assert(issues.unlisted['apps/backend/index.ts']['globby']);\n  assert(issues.unlisted['apps/backend/index.ts']['js-yaml']);\n  assert(issues.unlisted['packages/tools/tsconfig.json']['@fixtures/workspaces__tsconfig']);\n\n  assert(issues.types['packages/shared/types.ts']['UnusedEnum']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    exports: 1,\n    types: 1,\n    dependencies: 4,\n    unlisted: 4,\n    processed: 7,\n    total: 7,\n  });\n});\n\ntest('Find unused dependencies, exports and files in workspaces (production)', async () => {\n  const options = await createOptions({ cwd, isProduction: true });\n  const { issues, counters } = await main(options);\n\n  assert('docs/dangling.ts' in issues.files);\n\n  assert.equal(Object.keys(issues.dependencies['package.json']).length, 3);\n  assert.equal(Object.keys(issues.dependencies['apps/backend/package.json']).length, 2);\n\n  assert(issues.dependencies['package.json']['cypress']);\n  assert(issues.dependencies['package.json']['minimist']);\n  assert(issues.dependencies['package.json']['zod']);\n  assert(issues.dependencies['apps/backend/package.json']['next']);\n  assert(issues.dependencies['apps/backend/package.json']['picomatch']);\n\n  assert.equal(Object.keys(issues.unlisted).length, 2);\n  assert(issues.unlisted['apps/frontend/index.ts']['vanilla-js']);\n  assert(issues.unlisted['apps/backend/index.ts']['globby']);\n  assert(issues.unlisted['apps/backend/index.ts']['js-yaml']);\n\n  assert(issues.types['packages/shared/types.ts']['UnusedEnum']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    exports: 1,\n    types: 1,\n    dependencies: 5,\n    unlisted: 3,\n    processed: 7,\n    total: 7,\n  });\n});\n\ntest('Analyze only ancestor and dependent workspaces', async () => {\n  const options = await createOptions({ cwd, workspace: 'packages/shared' });\n  const { issues, counters } = await main(options);\n\n  assert(issues.types['packages/shared/types.ts']['UnusedEnum']);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    types: 1,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/test/zero-config.test.ts",
    "content": "import assert from 'node:assert/strict';\nimport test from 'node:test';\nimport { main } from '../src/index.ts';\nimport baseCounters from './helpers/baseCounters.ts';\nimport { createOptions } from './helpers/create-options.ts';\nimport { resolve } from './helpers/resolve.ts';\n\nconst cwd = resolve('fixtures/zero-config');\n\ntest('Find unused exports in zero-config mode', async () => {\n  const options = await createOptions({ cwd });\n  const { issues, counters } = await main(options);\n\n  assert.equal(Object.keys(issues.files).length, 1);\n\n  assert.equal(Object.values(issues.exports).length, 2);\n  assert.equal(issues.exports['my-module.ts']['unused'].symbol, 'unused');\n  assert.equal(issues.exports['my-module.ts']['default'].symbol, 'default');\n  assert.equal(issues.exports['my-namespace.ts']['MyNamespace.z'].symbol, 'z');\n  assert(!issues.exports['index.ts']);\n\n  assert.equal(Object.values(issues.types).length, 2);\n  assert.equal(issues.types['my-module.ts']['AnyType'].symbolType, 'type');\n  assert.equal(issues.types['my-namespace.ts']['MyNamespace.NS'].symbol, 'NS');\n\n  assert.equal(Object.values(issues.duplicates).length, 1);\n  assert.equal(issues.duplicates['my-module.ts']['myExport|default'].symbols?.length, 2);\n\n  assert.deepEqual(counters, {\n    ...baseCounters,\n    files: 1,\n    exports: 3,\n    types: 2,\n    duplicates: 1,\n    processed: 4,\n    total: 4,\n  });\n});\n"
  },
  {
    "path": "packages/knip/tsconfig.json",
    "content": "{\n  \"$schema\": \"https://json.schemastore.org/tsconfig\",\n  \"compilerOptions\": {\n    \"allowImportingTsExtensions\": true,\n    \"declaration\": true,\n    \"erasableSyntaxOnly\": true,\n    \"esModuleInterop\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"isolatedModules\": true,\n    \"lib\": [\"esnext\"],\n    \"module\": \"nodenext\",\n    \"moduleResolution\": \"nodenext\",\n    \"noEmit\": false,\n    \"outDir\": \"./dist\",\n    \"removeComments\": true,\n    \"resolveJsonModule\": true,\n    \"rewriteRelativeImportExtensions\": true,\n    \"rootDir\": \"./src\",\n    \"skipDefaultLibCheck\": true,\n    \"skipLibCheck\": true,\n    \"sourceMap\": false,\n    \"strict\": true,\n    \"target\": \"es2022\",\n    \"verbatimModuleSyntax\": true\n  },\n  \"include\": [\"./src/**/*.ts\"],\n  \"exclude\": []\n}\n"
  },
  {
    "path": "packages/language-server/README.md",
    "content": "# Knip Language Server\n\n## Knip\n\nThe Knip Language Server is powered by Knip: Find unused files, dependencies,\nand exports in your JavaScript/TypeScript projects.\n\n- Website: [knip.dev][1]\n- GitHub repo: [webpro-nl/knip][2]\n- Follow [@webpro.nl on Bluesky][3] for updates\n- Blogpost: [Knip for Editors & Agents][4]\n- [Sponsor Knip][5]\n\n## Contents\n\n- [Configuration][6]\n- [Diagnostics][7]\n- [Code Actions][8]\n- [File Descriptor][9]\n  - [Annotations][10]\n  - [Export Hover][11]\n  - [Imports][12]\n  - [Exports][13]\n- [File Descriptor for package.json][14]\n  - [Dependency Hover][15]\n\n## Configuration\n\nLatest version of available settings: [types.d.ts][16]\n\n## Diagnostics\n\nDiagnostics should work out of the box.\n\nMost [Knip issue types][17] are translated to `Diagnostic` items with a\n`DiagnosticSeverity` and emitted using `this.connection.sendDiagnostics()`. Also\nsee [diagnostics.js][18] for details.\n\n## Code Actions\n\nCode actions should work out of the box.\n\nSome issues/diagnostics have code actions available. Also see\n[code-actions.js][19] for details.\n\n## File Descriptor\n\nClients request a file descriptor to get available data for a document by\nsending the `REQUEST_FILE_NODE` request, in short:\n\n```ts\nconst file = await this.#client.sendRequest(REQUEST_FILE_NODE, {\n  uri: editor.document.uri.toString(),\n});\n```\n\nType definition for `File`: [session/types.ts][20]\n\nThe file descriptor can be used to implement features like [Annotations][10],\n[Export Hover][11], [Imports][12] and [Exports][13].\n\n### Annotations\n\nAnnotations (aka \"Code Lens\" or \"Inlay Hint\") for exported identifiers can be\nimplemented using data from the file descriptor.\n\nExample:\n\n- [registerCodeLensProvider][21]\n\n### Export Hover\n\nOn hover of an export identifier, the file descriptor can be used to render\nimport locations for the exported identifier.\n\nOptionally, code snippets can be searched for using the provided locations and\nmixed into the rendered list.\n\nExample:\n\n- [registerHoverProvider → getHoverContent][21]\n- [Collect hover snippets][22]\n- [Render export hover][23]\n\n### Imports\n\nThe file descriptor can be used to display an overview of imports of a document\nwith direct links to their definition location.\n\nOptionally, the client can implement:\n\n- Follow cursor between open document and highlight import in view\n- Show cyclic dependencies\n\nExample:\n\n- [setupTreeViews + refresh → getFileForTreeViews][21]\n- [Tree View Imports][24]\n\n### Exports\n\nThe file descriptor can be used to display an overview of exports of a document\nwith direct links to their usage locations.\n\nOptionally, the client can implement:\n\n- Follow cursor between open document and highlight export in view\n- Show contention: naming conflicts through re-exports\n- Show contention: branched/diamond-shaped re-export structures\n\nExample:\n\n- [setupTreeViews + refresh → getFileForTreeViews][21]\n- [Tree View Exports][25]\n\n## File Descriptor for package.json\n\nClients request the `package.json` file descriptor to get dependency usage data\nby sending the `REQUEST_PACKAGE_JSON` request:\n\n```ts\nconst packageJson = await this.#client.sendRequest(REQUEST_PACKAGE_JSON);\n```\n\nType definition for `DependencyNodes`: [session/types.ts][20]\n\n### Dependency Hover\n\nOn hover of a dependency name in `package.json`, the `packageJson` descriptor\ncan be used to render import locations for the dependency.\n\nExample:\n\n- [getDependencyHoverContent][21]\n- [Collect dependency snippets][26]\n- [Render dependency hover][27]\n\n## CLI\n\nTo run the language server directly from CLI:\n\n```sh\nnpx @knip/language-server\n```\n\nAdd `--node-ipc`, `--stdio`, `--socket <port>` or `--pipe <name>` to set\ntransport (default: `--stdio`).\n\n[1]: https://knip.dev\n[2]: https://github.com/webpro-nl/knip\n[3]: https://bsky.app/profile/webpro.nl\n[4]: https://knip.dev/blog/for-editors-and-agents\n[5]: https://knip.dev/sponsors\n[6]: #configuration\n[7]: #diagnostics\n[8]: #code-actions\n[9]: #file-descriptor\n[10]: #annotations\n[11]: #export-hover\n[12]: #imports\n[13]: #exports\n[14]: #file-descriptor-for-packagejson\n[15]: #dependency-hover\n[16]: ./src/types.d.ts\n[17]: https://knip.dev/reference/issue-types\n[18]: ./src/diagnostics.js\n[19]: ./src/code-actions.js\n[20]: ../knip/src/session/types.ts\n[21]: ../vscode-knip/src/index.js\n[22]: ../vscode-knip/src/collect-export-hover-snippets.js\n[23]: ../vscode-knip/src/render-export-hover.js\n[24]: ../vscode-knip/src/tree-view-imports.js\n[25]: ../vscode-knip/src/tree-view-exports.js\n[26]: ../vscode-knip/src/collect-dependency-hover-snippets.js\n[27]: ../vscode-knip/src/render-dependency-hover.js\n"
  },
  {
    "path": "packages/language-server/license",
    "content": "ISC License (ISC)\n\nCopyright 2022-2025 Lars Kappert\n\nPermission to use, copy, modify, and/or distribute this software for any purpose\nwith or without fee is hereby granted, provided that the above copyright notice\nand this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND\nFITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS\nOF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER\nTORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\nTHIS SOFTWARE.\n"
  },
  {
    "path": "packages/language-server/package.json",
    "content": "{\n  \"name\": \"@knip/language-server\",\n  \"version\": \"3.0.0\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/webpro-nl/knip.git\",\n    \"directory\": \"packages/language-server\"\n  },\n  \"bin\": {\n    \"knip-language-server\": \"./src/cli.js\"\n  },\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./src/types.d.ts\",\n      \"default\": \"./src/index.js\"\n    },\n    \"./constants\": \"./src/constants.js\"\n  },\n  \"scripts\": {\n    \"release\": \"release-it -c ../../.release-it.json\"\n  },\n  \"dependencies\": {\n    \"knip\": \"workspace:^\",\n    \"vscode-languageserver\": \"^9.0.1\",\n    \"vscode-languageserver-textdocument\": \"^1.0.12\"\n  },\n  \"engines\": {\n    \"node\": \"^20.19.0 || >=22.12.0\"\n  }\n}\n"
  },
  {
    "path": "packages/language-server/src/cli.js",
    "content": "#!/usr/bin/env node\nimport './index.js';\n"
  },
  {
    "path": "packages/language-server/src/code-actions.js",
    "content": "import { DeleteFile, TextEdit } from 'vscode-languageserver/node.js';\n\n/**\n * @import { WorkspaceEdit } from 'vscode-languageserver';\n * @import { TextDocument } from 'vscode-languageserver-textdocument';\n * @import { Issue } from 'knip/session';\n */\n\n/**\n * @param {TextDocument | undefined} document\n * @param {string} uri\n * @param {Issue} issue\n * @returns {WorkspaceEdit | null}\n */\nexport const createRemoveExportEdit = (document, uri, issue) => {\n  try {\n    if (!document || !issue.fixes?.length) return null;\n\n    const edits = issue.fixes.map(([start, end]) => {\n      return TextEdit.del({ start: document.positionAt(start), end: document.positionAt(end) });\n    });\n\n    return { changes: { [uri]: edits } };\n  } catch (_error) {\n    return null;\n  }\n};\n\n/**\n * @param {TextDocument | undefined} document\n * @param {string} uri\n * @param {Issue} issue\n * @returns {WorkspaceEdit | null}\n */\nexport const createRemoveDependencyEdit = (document, uri, issue) => {\n  try {\n    if (!document || issue.line === undefined) return null;\n\n    const lineIndex = issue.line - 1;\n    const range = { start: { line: lineIndex, character: 0 }, end: { line: lineIndex + 1, character: 0 } };\n    const edits = [TextEdit.del(range)];\n\n    return { changes: { [uri]: edits } };\n  } catch (_error) {\n    return null;\n  }\n};\n\n/**\n * @param {string} uri\n * @returns {WorkspaceEdit | null}\n */\nexport const createDeleteFileEdit = uri => {\n  try {\n    return {\n      documentChanges: [DeleteFile.create(uri, { recursive: false, ignoreIfNotExists: true })],\n    };\n  } catch (_error) {\n    return null;\n  }\n};\n\n/**\n *\n * @param {TextDocument} document\n * @param {Issue} issue\n * @param {string} tag\n */\nexport function createAddJSDocTagEdit(document, issue, tag) {\n  try {\n    if (!document || issue.line === undefined) return null;\n\n    const lineIndex = issue.line - 1;\n    const lineText = document.getText({\n      start: { line: lineIndex, character: 0 },\n      end: { line: lineIndex + 1, character: 0 },\n    });\n    const indent = lineText.match(/^\\s*/)?.[0] || '';\n\n    if (lineIndex > 0) {\n      const prevLineText = document.getText({\n        start: { line: lineIndex - 1, character: 0 },\n        end: { line: lineIndex, character: 0 },\n      });\n      const trimmedPrev = prevLineText.trim();\n\n      if (trimmedPrev.startsWith('/**') && trimmedPrev.endsWith('*/')) {\n        const content = trimmedPrev.slice(3, -2).trim();\n        const range = {\n          start: { line: lineIndex - 1, character: 0 },\n          end: { line: lineIndex - 1, character: prevLineText.length },\n        };\n\n        let multilineJSDoc = `${indent}/**\\n${indent} * ${tag}`;\n        if (content) {\n          multilineJSDoc += `\\n${indent} * ${content}`;\n        }\n        multilineJSDoc += `\\n${indent} */`;\n\n        return [TextEdit.replace(range, multilineJSDoc)];\n      }\n\n      if (trimmedPrev.endsWith('*/')) {\n        for (let i = lineIndex - 2; i >= 0; i--) {\n          const text = document.getText({ start: { line: i, character: 0 }, end: { line: i + 1, character: 0 } });\n          if (text.trim().startsWith('/**')) {\n            const insertPosition = { line: i, character: text.trimEnd().length };\n            const tagLine = `\\n${indent} * ${tag}`;\n            return [TextEdit.insert(insertPosition, tagLine)];\n          }\n        }\n      }\n    }\n\n    const jsdocComment = `${indent}/** ${tag} */\\n`;\n    const insertPosition = { line: lineIndex, character: 0 };\n    return [TextEdit.insert(insertPosition, jsdocComment)];\n  } catch (_error) {\n    return null;\n  }\n}\n"
  },
  {
    "path": "packages/language-server/src/constants.js",
    "content": "export const DEFAULT_JSDOC_TAGS = ['@public', '@internal', '@lintignore'];\n\nexport const REQUEST_START = 'knip.start';\n\nexport const REQUEST_STOP = 'knip.stop';\n\nexport const REQUEST_RESTART = 'knip.restart';\n\nexport const REQUEST_FILE_NODE = 'knip.getFileNode';\n\nexport const REQUEST_PACKAGE_JSON = 'knip.getPackageJson';\n\nexport const REQUEST_RESULTS = 'knip.getResults';\n\nexport const NOTIFICATION_MODULE_GRAPH_BUILT = 'knip.moduleGraphBuilt';\n\nexport const SESSION_LOADING = 'session-loading';\n"
  },
  {
    "path": "packages/language-server/src/diagnostics.js",
    "content": "import { getIssuePrefix } from 'knip/session';\nimport { DiagnosticSeverity, DiagnosticTag } from 'vscode-languageserver/node.js';\n\n/**\n * @import { Diagnostic } from 'vscode-languageserver';\n * @import { TextDocument } from 'vscode-languageserver-textdocument';\n * @import { Config } from './types.js';\n * @import { Issue, Rules } from 'knip/session';\n */\n\nconst dimmedIssueTypes = {\n  exports: 'dimExports',\n  types: 'dimTypes',\n  enumMembers: 'dimEnumMembers',\n  classMembers: 'dimClassMembers',\n  duplicates: 'dimDuplicates',\n};\n\nconst SEVERITY = {\n  error: DiagnosticSeverity.Error,\n  warn: DiagnosticSeverity.Warning,\n  off: DiagnosticSeverity.Information,\n  hint: DiagnosticSeverity.Hint,\n};\n\n/**\n * @param {Issue} issue\n * @param {Rules} rules\n * @param {Config} config\n * @param {TextDocument} [document]\n * @returns {Diagnostic}\n */\nexport const issueToDiagnostic = (issue, rules, config, document) => {\n  if (issue.type === 'files' && document) {\n    return {\n      severity: DiagnosticSeverity.Information,\n      range: {\n        start: { line: 0, character: 0 },\n        end: { line: 0, character: 1 },\n      },\n      message: formatMessage(issue),\n      source: 'knip',\n      code: issue.type,\n    };\n  }\n\n  /** @type {DiagnosticSeverity} */\n  let severity = SEVERITY[rules[issue.type]];\n\n  /** @type {DiagnosticTag[]} */\n  const tags = [];\n\n  if (dimmedIssueTypes[issue.type] && config.editor.exports.highlight[dimmedIssueTypes[issue.type]]) {\n    severity = DiagnosticSeverity.Hint;\n    tags.push(DiagnosticTag.Unnecessary);\n  }\n\n  const line = Math.max(0, (issue.line ?? 1) - 1);\n  const start = Math.max(0, (issue.col ?? 0) - 1);\n  let len = issue.symbol?.length ?? 1;\n\n  if (issue.symbol === 'default' && (issue.type === 'exports' || issue.type === 'types') && document) {\n    const lineText = document.getText({\n      start: { line, character: 0 },\n      end: { line: line + 1, character: 0 },\n    });\n\n    const match = /export\\s+default\\s+([A-Za-z0-9_$]+)/.exec(lineText);\n    if (match) {\n      const exportDefaultEnd = match.index + match[0].length;\n      len = exportDefaultEnd - start;\n    }\n  }\n\n  return {\n    severity,\n    range: {\n      start: { line, character: start },\n      end: { line, character: start + len },\n    },\n    message: formatMessage(issue),\n    source: 'knip',\n    code: issue.type,\n    tags: tags.length > 0 ? tags : undefined,\n  };\n};\n\n/** @param {Issue} issue */\nconst formatMessage = issue => {\n  if (issue.type === 'files') return 'Unused file';\n  return getIssueDescription(issue);\n};\n\n/** @param {Issue} issue */\nconst getIssueDescription = ({ type, symbol, symbols, parentSymbol }) => {\n  const symbolDescription = symbols ? `${symbols.map(s => s.symbol).join(', ')}` : symbol;\n  return `${getIssuePrefix(type)}: ${symbolDescription}${parentSymbol ? ` (${parentSymbol})` : ''}`;\n};\n"
  },
  {
    "path": "packages/language-server/src/index.js",
    "content": "import { LanguageServer } from './server.js';\n\nconst transports = ['--stdio', '--socket', '--node-ipc', '--pipe'];\nif (!process.argv.some(arg => transports.includes(arg))) process.argv.push('--stdio');\n\nnew LanguageServer();\n"
  },
  {
    "path": "packages/language-server/src/server.js",
    "content": "import { readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { createOptions, createSession, KNIP_CONFIG_LOCATIONS } from 'knip/session';\nimport { FileChangeType, ProposedFeatures, TextDocuments } from 'vscode-languageserver';\nimport { CodeActionKind, createConnection } from 'vscode-languageserver/node.js';\nimport { TextDocument } from 'vscode-languageserver-textdocument';\nimport pkg from '../package.json' with { type: 'json' };\nimport {\n  createAddJSDocTagEdit,\n  createDeleteFileEdit,\n  createRemoveDependencyEdit,\n  createRemoveExportEdit,\n} from './code-actions.js';\nimport {\n  DEFAULT_JSDOC_TAGS,\n  NOTIFICATION_MODULE_GRAPH_BUILT,\n  REQUEST_FILE_NODE,\n  REQUEST_PACKAGE_JSON,\n  REQUEST_RESTART,\n  REQUEST_RESULTS,\n  REQUEST_START,\n  REQUEST_STOP,\n  SESSION_LOADING,\n} from './constants.js';\nimport { issueToDiagnostic } from './diagnostics.js';\n\nconst RESTART_FOR = new Set(['package.json', ...KNIP_CONFIG_LOCATIONS]);\n\n/** @param {string} resolvedPath */\nfunction readKnipVersion(resolvedPath) {\n  for (let dir = path.dirname(resolvedPath); dir !== path.dirname(dir); dir = path.dirname(dir)) {\n    try {\n      const pkg = JSON.parse(readFileSync(path.join(dir, 'package.json'), 'utf8'));\n      if (pkg.name === 'knip') return ` v${pkg.version}`;\n    } catch {}\n  }\n  return '';\n}\n\n/** @type {Config} */\nconst DEFAULT_CONFIG = {\n  deferSession: false,\n  editor: {\n    exports: {\n      codelens: { enabled: true },\n      hover: { enabled: true, includeImportLocationSnippet: false, maxSnippets: 10, timeout: 300 },\n      quickfix: { enabled: true },\n      highlight: {\n        dimExports: false,\n        dimTypes: false,\n        dimEnumMembers: false,\n        dimClassMembers: false,\n        dimDuplicates: false,\n      },\n    },\n  },\n  imports: { enabled: true },\n  exports: { enabled: true, contention: { enabled: true } },\n};\n\n/** @param {string} value */\nconst toPosix = value => value.split(path.sep).join(path.posix.sep);\n\n/**\n * @import { Issues, Rules } from 'knip/session';\n * @import { Connection, Diagnostic, CodeAction } from 'vscode-languageserver';\n * @import { CodeActionParams, DidChangeWatchedFilesParams } from 'vscode-languageserver';\n * @import { Config, IssuesByUri } from './types.js';\n *\n * @typedef {import('knip/session').Session} Session\n * @typedef {import('knip/session').File} File\n */\n\nconst FILE_CHANGE_TYPES = new Map([\n  [FileChangeType.Created, 'added'],\n  [FileChangeType.Deleted, 'deleted'],\n  [FileChangeType.Changed, 'modified'],\n]);\n\nconst ISSUE_DESC = {\n  enumMembers: 'enum member',\n  namespaceMembers: 'namespace member',\n  types: 'export keyword',\n  exports: 'export keyword',\n};\n\nexport class LanguageServer {\n  /** @type {Connection} */\n  connection;\n\n  /** @type {undefined | string} */\n  cwd;\n\n  /** @type Set<string> */\n  published = new Set();\n\n  /** @type {undefined | Session} */\n  session;\n\n  /** @type {Rules}  */\n  rules = {};\n\n  /** @type {IssuesByUri} */\n  issuesByUri = new Map();\n\n  /** @type {Map<string, import('vscode-languageserver').Diagnostic[]>} */\n  cycleDiagnostics = new Map();\n\n  /** @type TextDocuments<TextDocument> */\n  documents;\n\n  /** @type {Config | undefined} */\n  initConfig;\n\n  constructor() {\n    this.connection = createConnection(ProposedFeatures.all);\n    this.documents = new TextDocuments(TextDocument);\n    console.log = this.connection.console.log.bind(this.connection.console);\n    console.warn = this.connection.console.warn.bind(this.connection.console);\n    console.error = this.connection.console.error.bind(this.connection.console);\n    console.info = this.connection.console.info.bind(this.connection.console);\n    this.setupHandlers();\n    this.documents.listen(this.connection);\n    this.connection.listen();\n  }\n\n  setupHandlers() {\n    this.connection.onInitialize(params => {\n      const uri = params.workspaceFolders?.[0]?.uri;\n\n      if (!uri) return { capabilities: {} };\n\n      this.cwd = fileURLToPath(uri);\n\n      this.initConfig = params.initializationOptions?.config;\n\n      const capabilities = {\n        codeActionProvider: {\n          codeActionKinds: [CodeActionKind.QuickFix],\n        },\n      };\n\n      return {\n        capabilities,\n        serverInfo: {\n          name: pkg.name,\n          version: pkg.version,\n        },\n      };\n    });\n\n    this.connection.onInitialized(async () => {\n      const config = await this.getConfig();\n      if (config?.deferSession !== true) this.start();\n    });\n\n    this.connection.onRequest(REQUEST_START, () => this.start());\n\n    this.connection.onRequest(REQUEST_STOP, () => this.stop());\n\n    this.connection.onShutdown(() => this.stop());\n\n    this.connection.onRequest(REQUEST_RESTART, () => this.restart());\n\n    this.connection.onRequest(REQUEST_RESULTS, () => this.getResults());\n\n    this.connection.onRequest(REQUEST_FILE_NODE, async params => {\n      const config = await this.getConfig();\n      const isShowContention = config.exports?.contention?.enabled !== false;\n      return this.getFileDescriptor(fileURLToPath(params.uri), { isShowContention });\n    });\n\n    this.connection.onRequest(REQUEST_PACKAGE_JSON, () => this.getPackageJsonDescriptor());\n\n    this.connection.onCodeAction(params => this.handleCodeAction(params));\n\n    this.connection.onDidChangeWatchedFiles(params => this.handleFileChanges(params));\n  }\n\n  /** @returns {Promise<Config>} */\n  async getConfig() {\n    try {\n      const config = await this.connection.workspace.getConfiguration('knip');\n      if (config?.editor) return config;\n    } catch {}\n    return this.initConfig ?? DEFAULT_CONFIG;\n  }\n\n  /**\n   * @param {Issues} issues\n   * @param {Config} config\n   * @param {Rules} rules\n   * */\n  buildDiagnostics(issues, config, rules) {\n    /** @type {Map<string, Diagnostic[]>} */\n    const diagnostics = new Map();\n    this.issuesByUri.clear();\n\n    for (const issuesForType of Object.values(issues)) {\n      for (const issuesForFile of Object.values(issuesForType)) {\n        for (const issue of Object.values(issuesForFile)) {\n          const uri = pathToFileURL(issue.filePath).toString();\n          if (!diagnostics.has(uri)) diagnostics.set(uri, []);\n          const document = this.documents.get(uri);\n          const diagnostic = issueToDiagnostic(issue, rules, config, document);\n          diagnostics.get(uri)?.push(diagnostic);\n          if (!this.issuesByUri.has(uri)) this.issuesByUri.set(uri, new Map());\n          const key = `${diagnostic.range.start.line}:${diagnostic.range.start.character}`;\n          this.issuesByUri.get(uri)?.set(key, { issue, issueType: issue.type, diagnostic });\n        }\n      }\n    }\n    return diagnostics;\n  }\n\n  /** @param {Map<string, Diagnostic[]>} newDiags */\n  publishDiagnostics(newDiags) {\n    for (const [uri, diagnostics] of this.cycleDiagnostics) {\n      const existing = newDiags.get(uri) || [];\n      newDiags.set(uri, [...existing, ...diagnostics]);\n    }\n    for (const [uri, diagnostics] of newDiags) this.connection.sendDiagnostics({ uri, diagnostics });\n    for (const uri of this.published) if (!newDiags.has(uri)) this.connection.sendDiagnostics({ uri, diagnostics: [] });\n    this.published = new Set(newDiags.keys());\n  }\n\n  async #resolveKnipSession() {\n    if (this.cwd) {\n      try {\n        const localRequire = createRequire(path.join(this.cwd, 'package.json'));\n        const resolved = localRequire.resolve('knip/session');\n        const local = await import(pathToFileURL(resolved).href);\n        this.connection.console.log(`Using local knip${readKnipVersion(resolved)}`);\n        return local;\n      } catch {}\n    }\n    this.connection.console.log(\n      `Using bundled knip${readKnipVersion(createRequire(__filename).resolve('knip/session'))}`\n    );\n    return { createOptions, createSession };\n  }\n\n  async start() {\n    if (this.session) return;\n\n    try {\n      const config = await this.getConfig();\n      const knip = await this.#resolveKnipSession();\n\n      const configFilePath = config?.configFilePath\n        ? path.isAbsolute(config.configFilePath)\n          ? config.configFilePath\n          : path.resolve(this.cwd ?? process.cwd(), config.configFilePath)\n        : undefined;\n\n      this.connection.console.log('Creating options');\n      const options = await knip.createOptions({ cwd: this.cwd, isSession: true, args: { config: configFilePath } });\n      this.rules = options.rules;\n\n      this.connection.console.log('Building module graph...');\n      const start = Date.now();\n      const session = await knip.createSession(options);\n      this.connection.console.log(`Finished building module graph (${Date.now() - start}ms)`);\n\n      this.session = session;\n      this.publishDiagnostics(this.buildDiagnostics(session.getIssues().issues, config, this.rules));\n      this.connection.sendNotification(NOTIFICATION_MODULE_GRAPH_BUILT, { duration: Date.now() - start });\n    } catch (_error) {\n      this.connection.console.error(`Error: ${_error}`);\n    }\n  }\n\n  stop() {\n    this.session = undefined;\n    this.fileCache = undefined;\n    for (const uri of this.published) this.connection.sendDiagnostics({ uri, diagnostics: [] });\n    this.published.clear();\n  }\n\n  restart() {\n    this.stop();\n    this.start();\n  }\n\n  getResults() {\n    if (!this.session) return null;\n    return this.session.getResults();\n  }\n\n  /**\n   * @param {DidChangeWatchedFilesParams} params\n   * @return {Promise<void>}\n   */\n  async handleFileChanges(params) {\n    this.fileCache = undefined;\n    this.packageJsonCache = undefined;\n    if (!this.session) return;\n\n    const cwd = this.cwd ?? process.cwd();\n\n    /** @type {{ type: \"added\" | \"deleted\" | \"modified\"; filePath: string }[]} */\n    const changes = [];\n    for (const change of params.changes) {\n      if (!change.uri.startsWith('file:')) continue;\n      const filePath = fileURLToPath(change.uri);\n      if (!filePath.startsWith(cwd) || filePath.includes('/.git/')) continue;\n      if (RESTART_FOR.has(path.basename(change.uri))) return this.restart();\n      const type = FILE_CHANGE_TYPES.get(change.type);\n      if (!type) continue;\n      changes.push({ type, filePath });\n    }\n\n    if (changes.length === 0) return;\n\n    try {\n      const result = await this.session.handleFileChanges(changes);\n\n      if (!result) return;\n\n      this.connection.console.log(\n        `Module graph updated (${Math.floor(result.duration)}ms • ${(result.mem / 1024 / 1024).toFixed(2)}M)`\n      );\n\n      const config = await this.getConfig();\n      this.publishDiagnostics(this.buildDiagnostics(this.session.getIssues().issues, config, this.rules));\n    } catch (_error) {\n      this.connection.console.error(`Error handling file changes: ${_error}`);\n    }\n  }\n\n  /**\n   * @param {string} filePath\n   * @param {{ isShowContention?: boolean }} [options]\n   * @returns {File | typeof SESSION_LOADING | undefined}\n   */\n  getFileDescriptor(filePath, options) {\n    if (!this.session) return SESSION_LOADING;\n    const relPath = toPosix(path.relative(this.cwd ?? process.cwd(), filePath));\n    if (relPath.startsWith('..')) return;\n    if (this.fileCache?.filePath === relPath) return this.fileCache.file;\n    const startTime = performance.now();\n    const file = this.session.describeFile(relPath, options);\n    if (file) {\n      const duration = Math.round(performance.now() - startTime);\n      const mem = process.memoryUsage().heapUsed;\n      this.connection.console.log(\n        `Received file descriptor (${relPath} • ${duration}ms • ${(mem / 1024 / 1024).toFixed(2)}M)`\n      );\n      const m = file.metrics;\n      this.connection.console.log(\n        `  ↳ imports: ${Math.round(m.imports)}ms, exports: ${Math.round(m.exports)}ms, cycles: ${Math.round(m.cycles)}ms, contention: ${Math.round(m.contention)}ms`\n      );\n    } else {\n      this.connection.console.log(`File not in project (${relPath})`);\n    }\n    this.fileCache = { filePath: relPath, file };\n    return file;\n  }\n\n  /**\n   * @returns {import('knip/session').PackageJsonFile & { dependenciesUsage: Record<string, import('knip/session').DependencyNodes> } | typeof SESSION_LOADING}\n   */\n  getPackageJsonDescriptor() {\n    if (!this.session) return SESSION_LOADING;\n    if (this.packageJsonCache) return this.packageJsonCache;\n    const result = this.session.describePackageJson();\n    // Convert Map to object for JSON serialization\n    this.packageJsonCache = { dependenciesUsage: Object.fromEntries(result.dependenciesUsage) };\n    return this.packageJsonCache;\n  }\n\n  /**\n   * @param {CodeActionParams} params\n   * @returns {Promise<CodeAction[]>}\n   */\n  async handleCodeAction(params) {\n    const config = await this.getConfig();\n    if (!config.editor.exports.quickfix.enabled) return [];\n\n    const uri = params.textDocument.uri;\n    const issuesForUri = this.issuesByUri.get(uri);\n    if (!issuesForUri) return [];\n    const document = this.documents.get(uri);\n    const jsdocTags = Array.isArray(config.editor.exports.quickfix.jsdocTags)\n      ? config.editor.exports.quickfix.jsdocTags\n      : DEFAULT_JSDOC_TAGS;\n\n    /** @type {CodeAction[]} */\n    const codeActions = [];\n\n    for (const diagnostic of params.context.diagnostics) {\n      if (diagnostic.source !== 'knip') continue;\n\n      const key = `${diagnostic.range.start.line}:${diagnostic.range.start.character}`;\n      const issuesForFile = issuesForUri.get(key);\n      if (!issuesForFile) continue;\n\n      const { issue, issueType } = issuesForFile;\n\n      if (\n        issueType === 'exports' ||\n        issueType === 'types' ||\n        issueType === 'enumMembers' ||\n        issueType === 'namespaceMembers'\n      ) {\n        const removeExportEdit = createRemoveExportEdit(document, uri, issue);\n        if (!removeExportEdit) continue;\n        codeActions.push({\n          title: `Remove ${ISSUE_DESC[issueType]} (${issue.symbol})`,\n          kind: CodeActionKind.QuickFix,\n          diagnostics: [diagnostic],\n          edit: removeExportEdit,\n        });\n\n        if (document) {\n          for (const tag of jsdocTags) {\n            const jsdocEdit = createAddJSDocTagEdit(document, issue, tag);\n            if (!jsdocEdit) continue;\n            codeActions.push({\n              title: `Add ${tag} JSDoc tag`,\n              kind: CodeActionKind.QuickFix,\n              diagnostics: [diagnostic],\n              edit: { changes: { [uri]: jsdocEdit } },\n            });\n          }\n        }\n      }\n\n      if (issueType === 'dependencies' || issueType === 'devDependencies') {\n        const removeDependencyEdit = createRemoveDependencyEdit(document, uri, issue);\n        if (!removeDependencyEdit) continue;\n        codeActions.push({\n          title: `Remove dependency \"${issue.symbol}\"`,\n          kind: CodeActionKind.QuickFix,\n          diagnostics: [diagnostic],\n          edit: removeDependencyEdit,\n        });\n      }\n\n      if (issueType === 'unlisted') {\n        const workspacePath = path.resolve(this.cwd ?? process.cwd(), issue.workspace);\n        codeActions.push({\n          title: `Install '${issue.symbol}' as dependency`,\n          kind: CodeActionKind.QuickFix,\n          diagnostics: [diagnostic],\n          command: {\n            title: `Install '${issue.symbol}' as dependency`,\n            command: 'knip.installDependency',\n            arguments: [issue.symbol, 'dependencies', workspacePath],\n          },\n        });\n\n        codeActions.push({\n          title: `Install '${issue.symbol}' as devDependency`,\n          kind: CodeActionKind.QuickFix,\n          diagnostics: [diagnostic],\n          command: {\n            title: `Install '${issue.symbol}' as devDependency`,\n            command: 'knip.installDependency',\n            arguments: [issue.symbol, 'devDependencies', workspacePath],\n          },\n        });\n      }\n\n      if (issueType === 'files') {\n        const deleteEdit = createDeleteFileEdit(uri);\n        if (deleteEdit) {\n          codeActions.push({\n            title: `Delete this file`,\n            kind: CodeActionKind.QuickFix,\n            diagnostics: [diagnostic],\n            edit: deleteEdit,\n          });\n        }\n      }\n    }\n\n    return codeActions;\n  }\n}\n"
  },
  {
    "path": "packages/language-server/src/types.d.ts",
    "content": "import type { Issue, IssueType } from 'knip/session';\n\nexport type Config = {\n  deferSession: boolean;\n  configFilePath?: string;\n  editor: {\n    exports: {\n      codelens: {\n        enabled: boolean;\n      };\n      hover: {\n        enabled: boolean;\n        includeImportLocationSnippet: boolean;\n        maxSnippets: number;\n        timeout: number;\n      };\n      quickfix: {\n        enabled: boolean;\n        jsdocTags?: string[];\n      };\n      highlight: {\n        dimExports: boolean;\n        dimTypes: boolean;\n        dimEnumMembers: boolean;\n        dimClassMembers: boolean;\n        dimDuplicates: boolean;\n      };\n    };\n  };\n  imports: {\n    enabled: boolean;\n  };\n  exports: {\n    enabled: boolean;\n    contention: {\n      enabled: boolean;\n    };\n  };\n};\n\nexport type IssueForFile = {\n  issue: Issue;\n  issueType: IssueType;\n  diagnostic: Diagnostic;\n};\n\nexport type IssuesForFile = Map<string, IssueForFile>;\n\nexport type IssuesByUri = Map<string, IssuesForFile>;\n"
  },
  {
    "path": "packages/language-server/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"checkJs\": true,\n    \"module\": \"nodenext\",\n    \"noEmit\": true,\n    \"resolveJsonModule\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"target\": \"esnext\"\n  }\n}\n"
  },
  {
    "path": "packages/mcp-server/.gitignore",
    "content": "src/docs/\n"
  },
  {
    "path": "packages/mcp-server/README.md",
    "content": "# Knip MCP Server\n\n## Knip\n\nThe Knip MCP Server is powered by Knip: Find unused files, dependencies, and\nexports in your JavaScript/TypeScript projects.\n\n- Website: [knip.dev][1]\n- GitHub repo: [webpro-nl/knip][2]\n- Follow [@webpro.nl on Bluesky][3] for updates\n- Blogpost: [Knip for Editors & Agents][4]\n- [Sponsor Knip][5]\n\n## VS Code Extension\n\nInstall the [Knip VS Code Extension][6] or [Knip Open VSX Extension][7] — it\ncomes with the MCP Server included.\n\n## MCP Server\n\nAdd to your MCP configuration:\n\n```jsonc\n{\n  // or \"mcpServers\"\n  \"servers\": {\n    \"knip\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@knip/mcp\"],\n    },\n  },\n}\n```\n\n## Prompts\n\n- `knip-configure` — Guided workflow to set up and optimize Knip configuration\n\n## Tools\n\n- `knip-run` — Run Knip, returns configuration hints and issues\n- `knip-docs` — Get Knip documentation by topic\n\n## Resources\n\nAll pages of the [Knip documentation][1] are available as MCP resources\n(`knip://docs/{topic}`).\n\n[1]: https://knip.dev\n[2]: https://github.com/webpro-nl/knip\n[3]: https://bsky.app/profile/webpro.nl\n[4]: https://knip.dev/blog/for-editors-and-agents\n[5]: https://knip.dev/sponsors\n[6]: https://github.com/webpro-nl/knip/tree/main/packages/mcp-server\n[7]: https://open-vsx.org/extension/webpro/vscode-knip\n"
  },
  {
    "path": "packages/mcp-server/license",
    "content": "ISC License (ISC)\n\nCopyright 2022-2025 Lars Kappert\n\nPermission to use, copy, modify, and/or distribute this software for any purpose\nwith or without fee is hereby granted, provided that the above copyright notice\nand this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND\nFITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS\nOF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER\nTORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\nTHIS SOFTWARE.\n"
  },
  {
    "path": "packages/mcp-server/package.json",
    "content": "{\n  \"name\": \"@knip/mcp\",\n  \"version\": \"0.0.23\",\n  \"description\": \"Knip MCP Server\",\n  \"keywords\": [\n    \"knip\",\n    \"mcp\",\n    \"model-context-protocol\"\n  ],\n  \"license\": \"ISC\",\n  \"author\": \"Lars Kappert <lars@webpro.nl>\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/webpro-nl/knip.git\",\n    \"directory\": \"packages/mcp-server\"\n  },\n  \"bin\": {\n    \"knip-mcp\": \"./src/cli.js\"\n  },\n  \"files\": [\n    \"src\"\n  ],\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": \"./src/server.js\",\n    \"./tools\": \"./src/tools.js\"\n  },\n  \"scripts\": {\n    \"prepack\": \"cp -r ../docs/src/content/docs ./src/docs\",\n    \"release\": \"release-it -c ../../.release-it.json\"\n  },\n  \"dependencies\": {\n    \"@modelcontextprotocol/sdk\": \"^1.27.1\",\n    \"knip\": \"workspace:^\",\n    \"zod\": \"^4.1.11\"\n  },\n  \"engines\": {\n    \"node\": \"^20.19.0 || >=22.12.0\"\n  }\n}\n"
  },
  {
    "path": "packages/mcp-server/src/cli.js",
    "content": "#!/usr/bin/env node\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { mcpServer } from './server.js';\n\nfunction disconnect() {\n  mcpServer.close();\n  process.exitCode = 0;\n}\n\nawait mcpServer.connect(new StdioServerTransport());\n\nprocess.on('SIGINT', disconnect);\nprocess.on('SIGTERM', disconnect);\n"
  },
  {
    "path": "packages/mcp-server/src/curated-resources.js",
    "content": "export const CURATED_RESOURCES = {\n  'getting-started': {\n    name: 'Getting Started',\n    description: 'New to Knip? Start here for installation and first run',\n    path: 'overview/getting-started.mdx',\n  },\n  configuration: {\n    name: 'Configuration',\n    description: 'Understand configuration basics, defaults, and file locations',\n    path: 'overview/configuration.md',\n  },\n  'configuring-project-files': {\n    name: 'Configuring Project Files',\n    description: 'READ FIRST for unused files or false positives. Covers entry/project patterns',\n    path: 'guides/configuring-project-files.md',\n  },\n  'handling-issues': {\n    name: 'Handling Issues',\n    description: 'How to handle each issue type: files, dependencies, exports, types, duplicates',\n    path: 'guides/handling-issues.mdx',\n  },\n  'monorepos-and-workspaces': {\n    name: 'Monorepos & Workspaces',\n    description: 'Multi-package repo? Configure workspaces and cross-references here',\n    path: 'features/monorepos-and-workspaces.md',\n  },\n  'production-mode': {\n    name: 'Production Mode',\n    description: 'Exclude tests, stories, devDependencies with --production and --strict flags',\n    path: 'features/production-mode.md',\n  },\n  compilers: {\n    name: 'Compilers',\n    description: 'Using .vue, .svelte, .astro, .mdx files? Configure compilers to parse them',\n    path: 'features/compilers.md',\n  },\n  'configuration-reference': {\n    name: 'Configuration Reference',\n    description: 'Complete reference of all config options: entry, project, ignore, plugins, etc.',\n    path: 'reference/configuration.md',\n  },\n  'plugins-explanation': {\n    name: 'Plugins',\n    description: 'Config files showing as unused? Understand plugin config vs entry files',\n    path: 'explanations/plugins.md',\n  },\n  'entry-files': {\n    name: 'Entry Files',\n    description: 'Understand how Knip discovers entry files and default patterns per plugin',\n    path: 'explanations/entry-files.md',\n  },\n  'plugin-list': {\n    name: 'Plugin List',\n    description: 'Check if a plugin exists for your tool (Jest, Vitest, ESLint, etc.)',\n    path: 'reference/plugins.md',\n  },\n  'known-issues': {\n    name: 'Known Issues',\n    description: 'Errors or unexpected behavior? Check workarounds for common problems',\n    path: 'reference/known-issues.md',\n  },\n};\n"
  },
  {
    "path": "packages/mcp-server/src/server.js",
    "content": "process.env.NO_COLOR = '1';\n\nimport { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { CURATED_RESOURCES } from './curated-resources.js';\nimport {\n  DOC_TOOL_DESCRIPTION,\n  DOC_TOOL_TOPIC_DESCRIPTION,\n  ERROR_HINT,\n  RUN_KNIP_TOOL_DESCRIPTION,\n  WORKFLOW,\n} from './texts.js';\nimport { getDocs, getErrorMessage, getResults, readContent } from './tools.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkg = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));\n\nconst DOCS = 'knip://docs';\n\nclass MCP {\n  constructor() {\n    this.server = new McpServer({ name: 'Knip', version: pkg.version });\n    this.#registerPrompts();\n    this.#registerResources();\n    this.#registerTools();\n  }\n\n  #registerPrompts() {\n    const resources = Object.entries(CURATED_RESOURCES).map(([id, doc]) => ({\n      uri: `${DOCS}/${id}`,\n      name: doc.name,\n      description: doc.description,\n    }));\n\n    this.server.registerPrompt(\n      'knip-configure',\n      {\n        description: 'Set up and optimize Knip configuration. Guides through initial setup and iterative refinement.',\n        arguments: [{ name: 'cwd', description: 'Working directory (default: current directory)', required: false }],\n      },\n      async ({ cwd }) => ({\n        messages: [\n          {\n            role: 'user',\n            content: {\n              type: 'text',\n              text: `Help me configure Knip for ${cwd || 'this project'}.\\n\\n${WORKFLOW}`,\n            },\n          },\n        ],\n        resources,\n      })\n    );\n  }\n\n  #registerResources() {\n    for (const [id, doc] of Object.entries(CURATED_RESOURCES)) {\n      const uri = `${DOCS}/${id}`;\n      this.server.registerResource(\n        doc.name,\n        uri,\n        { description: doc.description, mimeType: 'text/markdown' },\n        async () => ({ contents: [{ uri, mimeType: 'text/markdown', text: readContent(doc.path) }] })\n      );\n    }\n\n    this.server.registerResource(\n      'docs',\n      new ResourceTemplate(`${DOCS}/{+path}`, { list: undefined }),\n      { description: 'Get Knip documentation page by path', mimeType: 'text/markdown' },\n      async (uri, { path }) => {\n        const result = getDocs(path);\n        if ('content' in result) return { content: [{ type: 'text', text: result.content }] };\n        const errorText = `Documentation not found: ${path}`;\n        return { contents: [{ uri, mimeType: 'text/plain', text: errorText }] };\n      }\n    );\n  }\n\n  #registerTools() {\n    this.server.registerTool(\n      'knip-run',\n      {\n        description: RUN_KNIP_TOOL_DESCRIPTION,\n        inputSchema: {\n          cwd: z.string().optional().describe('Working directory (default: workspace root)'),\n        },\n      },\n      async opts => {\n        try {\n          const cwd = opts.cwd || process.cwd();\n          const results = await getResults(cwd);\n          return { content: [{ type: 'text', text: JSON.stringify(results) }] };\n        } catch (error) {\n          const message = getErrorMessage(error);\n          return {\n            content: [{ type: 'text', text: JSON.stringify({ error: message, hint: ERROR_HINT }) }],\n            isError: true,\n          };\n        }\n      }\n    );\n\n    this.server.registerTool(\n      'knip-docs',\n      { description: DOC_TOOL_DESCRIPTION, inputSchema: { topic: z.string().describe(DOC_TOOL_TOPIC_DESCRIPTION) } },\n      async ({ topic }) => {\n        const docs = getDocs(topic);\n        if ('content' in docs) return { content: [{ type: 'text', text: docs.content }] };\n        return { content: [{ type: 'text', text: docs.error }], isError: true };\n      }\n    );\n  }\n\n  connect(transport) {\n    return this.server.connect(transport);\n  }\n\n  close() {\n    return this.server.close();\n  }\n}\n\nconst mcpServer = new MCP();\n\nexport { mcpServer };\n"
  },
  {
    "path": "packages/mcp-server/src/texts.js",
    "content": "import { CURATED_RESOURCES } from './curated-resources.js';\n\nexport const WORKFLOW = `Workflow:\n\n1. Read essential documentation resources:\n   - configuring-project-files (must read to configure entry patterns)\n   - plugins-explanation (understand entries added by auto-detected plugins)\n   - handling-issues (comprehensive guide to deal with any reported issue type)\n   - configuration-reference (all knip.json configuration options)\n2. Run analysis (knip-run) to get configuration hints and issues\n3. Address the hints by adjusting knip.json\n4. Repeat steps 2-3 until hints are gone and false positives are minimized\n\nImportant notes:\n- For prompts like \"run knip\" or \"clean up codebase\" or \"no more slop\": always run workflow to configure Knip first.\n- If you hit errors (not lint issues), consult: known-issues\n- Before suggesting fixes/solutions, consult: handling-issues and reference/jsdoc-tsdoc-tags\n- For cleanup, consult: features/auto-fix\n- To install Knip and start using it from CLI, consult: getting-started and reference/cli\n- Knip does not remove unused imports/variables inside files (use a linter)\n`;\n\n// pkg.contributes.languageModelTools[0].modelDescription\nexport const RUN_KNIP_TOOL_DESCRIPTION = `Run Knip and return configuration hints and issues.\n\nReturns:\n- configurationHints: Ordered suggestions to improve configuration (address these first)\n- counters: Summary counts of each issue type\n- enabledPlugins: Auto-detected plugins per workspace\n- files: List of unused files\n- issues: Detailed issues by type (dependencies, exports, types, etc.)\n- configFile: Current config file status\n\nIterate: adjust knip.json based on hints, run again until no hints remain and false positives are minimized.`;\n\n// pkg.contributes.languageModelTools[1].modelDescription\nexport const DOC_TOOL_DESCRIPTION = `Get Knip documentation by topic.\n\nIf registered resources are unavailable, use this tool.\n\nAvailable topics (use these IDs):\n${Object.entries(CURATED_RESOURCES)\n  .map(([id, doc]) => `- ${id}: ${doc.description}`)\n  .join('\\n')}\n\nCan also fetch any doc by path (e.g. \"reference/cli\" or \"guides/troubleshooting\").\nUse this instead of fetching from https://knip.dev.`;\n\n// pkg.contributes.languageModelTools[1].inputSchema.properties.topic.description\nexport const DOC_TOOL_TOPIC_DESCRIPTION =\n  'Topic key (e.g. \"configuring-project-files\") for curated resources, or path (e.g. \"explanations/plugins\") for all available docs';\n\nexport const ERROR_HINT = `For unexpected errors (exit code 2) such as \"error loading file\":\n\n- Consult docs: known-issues and configuration-reference\n- If no config file exists, create knip.json in the project root\n- Run knip again`;\n\nexport const CONFIG_REVIEW_HINT = `Review the existing configuration for potential improvements:\n\n- Never use \"ignore\" patterns (hides real issues!), always prefer specific solutions, other ignore* options are allowed\n- Many unused exported types? Add: ignoreExportsUsedInFile: { interface: true, type: true } (prefer this over other ignore* options)\n- Remove ignore patterns that don't match any files\n- Remove redundant ignore patterns: Knip respects .gitignore (node_modules, dist, build, .git)\n- Remove entry patterns covered by config defaults and auto-detected plugins\n- Config files (e.g. vite.config.ts) showing as unused? Enable/disable the plugin explicitly\n- Dependencies matching Node.js builtins: add to ignoreDependencies (e.g. buffer, process)\n- Unresolved imports from path aliases: add paths to Knip config (tsconfig.json semantics)`;\n"
  },
  {
    "path": "packages/mcp-server/src/tools.js",
    "content": "import { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { createOptions, createSession, finalizeConfigurationHints, KNIP_CONFIG_LOCATIONS } from 'knip/session';\nimport { CURATED_RESOURCES } from './curated-resources.js';\nimport { CONFIG_REVIEW_HINT } from './texts.js';\n\nexport { ERROR_HINT } from './texts.js';\n\n/**\n * @param {unknown} error\n * @returns {string}\n */\nexport function getErrorMessage(error) {\n  if (!(error instanceof Error)) return String(error);\n  const messages = [error.message];\n  let cause = error.cause;\n  while (cause instanceof Error) {\n    messages.push(cause.message);\n    cause = cause.cause;\n  }\n  return `${messages.join('\\nCaused by: ')}\\nCurrent working dir: ${process.cwd()}`;\n}\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst docsDir = join(__dirname, './docs');\n\n/**\n * @param {import('knip/session').Results} results\n * @param {{ cwd: string, configFilePath: string | undefined }} options\n */\nexport function buildResults(results, options) {\n  return {\n    configFile: options.configFilePath\n      ? { exists: true, filePath: options.configFilePath, reviewHint: CONFIG_REVIEW_HINT }\n      : { exists: false, locations: KNIP_CONFIG_LOCATIONS },\n    configurationHints: finalizeConfigurationHints(results, options),\n    counters: results.counters,\n    enabledPlugins: results.enabledPlugins,\n    files: Array.from(results.issues.files),\n    issues: Object.fromEntries(Object.entries(results.issues).filter(([key]) => key !== 'files' && key !== '_files')),\n  };\n}\n\n/**\n * @param {string} cwd\n */\nexport async function getResults(cwd) {\n  const options = await createOptions({ cwd, isSession: true, isUseTscFiles: false });\n  const session = await createSession(options);\n  return buildResults(session.getResults(), options);\n}\n\n/** @param {string} filePath */\nexport function readContent(filePath) {\n  try {\n    const content = readFileSync(join(docsDir, filePath), 'utf-8');\n    return content.replace(/^---[\\s\\S]*?---\\n/, '');\n  } catch (error) {\n    return `Error reading ${filePath}: ${error.message}`;\n  }\n}\n\n/**\n * @param {string} topic\n * @returns {{ content: string } | { error: string }}\n */\nexport function getDocs(topic) {\n  const content = findDocPage(topic);\n  if (content) return { content };\n  return { error: `Documentation not found: ${topic}. Available: ${Object.keys(CURATED_RESOURCES).join(', ')}` };\n}\n\n/** @param {string} topic */\nfunction findDocPage(topic) {\n  if (CURATED_RESOURCES[topic]) return readContent(CURATED_RESOURCES[topic].path);\n\n  for (const ext of ['.md', '.mdx']) {\n    const filePath = join(docsDir, `${topic}${ext}`);\n    if (existsSync(filePath)) return readContent(`${topic}${ext}`);\n  }\n\n  return null;\n}\n"
  },
  {
    "path": "packages/vscode-knip/.gitignore",
    "content": "*.vsix\n.vscode-test\n"
  },
  {
    "path": "packages/vscode-knip/.release-it.json",
    "content": "{\n  \"$schema\": \"https://unpkg.com/release-it@19/schema/release-it.json\",\n  \"extends\": \"../../.release-it.json\",\n  \"npm\": {\n    \"publish\": false\n  },\n  \"hooks\": {\n    \"before:init\": [\"pnpm run -C ../mcp-server/ prepack\", \"pnpm test\"],\n    \"after:bump\": \"node scripts/publish.js --publish\"\n  }\n}\n"
  },
  {
    "path": "packages/vscode-knip/README.md",
    "content": "# Knip Editor Extension for VS Code/Open VSX\n\nThe Knip Editor Extension for VS Code/Open VSX is powered by Knip: Find unused\nfiles, dependencies, and exports in your JavaScript/TypeScript projects.\n\n- Website: [knip.dev][1]\n- GitHub repo: [webpro-nl/knip][2]\n- Follow [@webpro.nl on Bluesky][3] for updates\n- Blogpost: [Knip for Editors & Agents][4]\n- [Sponsor Knip][5]\n\n## What Is This?\n\n[The usual suspects][6] like red squiggles for unused exports are there. What\nreally moves the needle for DX with Knip's module graph is **navigation**. A\ncompletely unique way to view & fly through codebases for developers who care.\nConnect the dots during development and refactors, while keeping things in\ncheck. We're starting out with [3 key features][7]:\n\n1. **Hover over Export** for import & usage locations\n2. **Imports Tree View** for direct links to implementations\n3. **Exports Tree View** for direct links to import & usage locations\n\n## Features\n\n- Diagnostics for unused files, dependencies, and exports\n- Hover on exports to see import locations\n- Tree views for imports and exports\n- Code actions to fix or ignore issues\n- CodeLens showing import counts for exports\n\n## MCP Tools\n\nThe extension provides MCP tools for coding agents:\n\n- `knip-configure` — Run Knip and configure\n- `knip-docs` — Get Knip documentation by topic\n\nIn other words, you can tell your coding agent to \"configure knip\" and it will\nRTFM so you don't have to. Using a newer model results in an optimized\n`knip.json` file and an uncluttered codebase.\n\n## Screenshots\n\n- [Lint Findings][6]\n- [Imports & Exports][7]\n- [Contention][8]\n  - [Circular Dependencies][9]\n  - [Conflicts][10]\n  - [Branching][11]\n- [VS Code Extension Settings][12]\n\n### Lint Findings\n\n![Lint Findings][13]\n\n### Imports & Exports\n\n![hover][14]\n\n### Contention\n\nThe IDE extension shows extra issues in the tree views like circular\ndependencies. We're starting out with some extra novelties like conflicting and\nbranched/diamond-shaped import chains.\n\n#### Circular Dependencies\n\nIf an import is part of a circular dependency, Knip will display:\n\n![Circular Dependencies][15]\n\n#### Conflicts\n\nTypeScript shows direct conflicts when importing or re-exporting the same named\nexport from different files. Except when the problem is more subtle and the\nchain spans more than one file. Knip warns:\n\n![Conflicts][16]\n\n#### Branching\n\nBranched or diamond-shaped imports chains indicate unnecessary re-exports and\ncomplexity. They help to untangle large codebases and shrink or get rid of\nbarrel files. Knip warns:\n\n![Branching][17]\n\n### VS Code Extension Settings\n\n![VS Code Extension Settings][18]\n\n[1]: https://knip.dev\n[2]: https://github.com/webpro-nl/knip\n[3]: https://bsky.app/profile/webpro.nl\n[4]: https://knip.dev/blog/for-editors-and-agents\n[5]: https://knip.dev/sponsors\n[6]: #lint-findings\n[7]: #imports--exports\n[8]: #contention\n[9]: #circular-dependencies\n[10]: #conflicts\n[11]: #branching\n[12]: #vs-code-extension-settings\n[13]: https://knip.dev/screenshots/editors-and-agents/diagnostics.webp\n[14]: https://knip.dev/screenshots/editors-and-agents/imports-exports.webp\n[15]: https://knip.dev/screenshots/editors-and-agents/circular-dependency.webp\n[16]: https://knip.dev/screenshots/editors-and-agents/conflict.webp\n[17]: https://knip.dev/screenshots/editors-and-agents/branch.webp\n[18]:\n  https://knip.dev/screenshots/editors-and-agents/vscode-extension-settings.webp\n"
  },
  {
    "path": "packages/vscode-knip/license",
    "content": "ISC License (ISC)\n\nCopyright 2022-2025 Lars Kappert\n\nPermission to use, copy, modify, and/or distribute this software for any purpose\nwith or without fee is hereby granted, provided that the above copyright notice\nand this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND\nFITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS\nOF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER\nTORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\nTHIS SOFTWARE.\n"
  },
  {
    "path": "packages/vscode-knip/package.json",
    "content": "{\n  \"name\": \"vscode-knip\",\n  \"displayName\": \"Knip\",\n  \"version\": \"2.0.0\",\n  \"private\": true,\n  \"description\": \"Find unused files, dependencies, and exports in JavaScript/TypeScript projects\",\n  \"categories\": [\n    \"Linters\",\n    \"Programming Languages\"\n  ],\n  \"keywords\": [\n    \"analysis\",\n    \"dead code\",\n    \"dependencies\",\n    \"entropy\",\n    \"exports\",\n    \"imports\",\n    \"javascript\",\n    \"lint\",\n    \"maintenance\",\n    \"monorepo\",\n    \"types\",\n    \"typescript\",\n    \"unused\"\n  ],\n  \"license\": \"ISC\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/webpro-nl/knip.git\",\n    \"directory\": \"packages/vscode-knip\"\n  },\n  \"sponsor\": {\n    \"url\": \"https://knip.dev/sponsors\"\n  },\n  \"qna\": false,\n  \"publisher\": \"webpro\",\n  \"files\": [\n    \"dist/\",\n    \"icon-192x192.png\",\n    \"license\",\n    \"README.md\"\n  ],\n  \"type\": \"module\",\n  \"main\": \"./dist/extension.js\",\n  \"scripts\": {\n    \"package\": \"node scripts/publish.js\",\n    \"release\": \"release-it\",\n    \"test\": \"pnpm package && node test/run.mjs\",\n    \"vscode:prepublish\": \"node scripts/sync-from-mcp.js\"\n  },\n  \"dependencies\": {\n    \"@knip/language-server\": \"workspace:*\",\n    \"@knip/mcp\": \"workspace:*\",\n    \"mdast-util-to-markdown\": \"2.1.2\",\n    \"unist-builder\": \"4.0.0\",\n    \"vscode-languageclient\": \"^9.0.1\"\n  },\n  \"devDependencies\": {\n    \"@types/mdast\": \"4.0.4\",\n    \"@types/vscode\": \"^1.95.0\",\n    \"@vscode/test-electron\": \"^2.5.2\",\n    \"knip\": \"workspace:*\",\n    \"rolldown\": \"^1.0.0-rc.7\"\n  },\n  \"contributes\": {\n    \"languageModelTools\": [\n      {\n        \"name\": \"knip-configure\",\n        \"displayName\": \"Configure Knip\",\n        \"toolReferenceName\": \"knip-configure\",\n        \"icon\": \"$(tools)\",\n        \"userDescription\": \"Set up and optimize Knip configuration\",\n        \"modelDescription\": \"Run Knip and return configuration hints and issues.\\n\\nReturns:\\n- configurationHints: Ordered suggestions to improve configuration (address these first)\\n- counters: Summary counts of each issue type\\n- enabledPlugins: Auto-detected plugins per workspace\\n- files: List of unused files\\n- issues: Detailed issues by type (dependencies, exports, types, etc.)\\n- configFile: Current config file status\\n\\nIterate: adjust knip.json based on hints, run again until no hints remain and false positives are minimized.\",\n        \"canBeReferencedInPrompt\": true,\n        \"inputSchema\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"cwd\": {\n              \"type\": \"string\",\n              \"description\": \"Working directory (default: workspace root)\"\n            }\n          }\n        }\n      },\n      {\n        \"name\": \"knip-docs\",\n        \"displayName\": \"Knip Documentation\",\n        \"toolReferenceName\": \"knip-docs\",\n        \"icon\": \"$(book)\",\n        \"userDescription\": \"Get Knip documentation\",\n        \"modelDescription\": \"Get Knip documentation by topic.\\n\\nIf registered resources are unavailable, use this tool.\\n\\nAvailable topics (use these IDs):\\n- getting-started: New to Knip? Start here for installation and first run\\n- configuration: Understand configuration basics, defaults, and file locations\\n- configuring-project-files: READ FIRST for unused files or false positives. Covers entry/project patterns\\n- handling-issues: How to handle each issue type: files, dependencies, exports, types, duplicates\\n- monorepos-and-workspaces: Multi-package repo? Configure workspaces and cross-references here\\n- production-mode: Exclude tests, stories, devDependencies with --production and --strict flags\\n- compilers: Using .vue, .svelte, .astro, .mdx files? Configure compilers to parse them\\n- configuration-reference: Complete reference of all config options: entry, project, ignore, plugins, etc.\\n- plugins-explanation: Config files showing as unused? Understand plugin config vs entry files\\n- entry-files: Understand how Knip discovers entry files and default patterns per plugin\\n- plugin-list: Check if a plugin exists for your tool (Jest, Vitest, ESLint, etc.)\\n- known-issues: Errors or unexpected behavior? Check workarounds for common problems\\n\\nCan also fetch any doc by path (e.g. \\\"reference/cli\\\" or \\\"guides/troubleshooting\\\").\\nUse this instead of fetching from https://knip.dev.\",\n        \"canBeReferencedInPrompt\": true,\n        \"inputSchema\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"topic\": {\n              \"type\": \"string\",\n              \"description\": \"Topic key (e.g. \\\"configuring-project-files\\\") for curated resources, or path (e.g. \\\"explanations/plugins\\\") for all available docs\"\n            }\n          },\n          \"required\": [\n            \"topic\"\n          ]\n        }\n      }\n    ],\n    \"commands\": [\n      {\n        \"command\": \"knip.start\",\n        \"title\": \"Knip: Start\"\n      },\n      {\n        \"command\": \"knip.restart\",\n        \"title\": \"Knip: Restart\"\n      },\n      {\n        \"command\": \"knip.expandAll\",\n        \"title\": \"Expand All\",\n        \"icon\": \"$(expand-all)\"\n      },\n      {\n        \"command\": \"knip.showHover\",\n        \"title\": \"Knip: Show Hover\"\n      }\n    ],\n    \"keybindings\": [\n      {\n        \"command\": \"knip.showHover\",\n        \"key\": \"ctrl+cmd+e\",\n        \"when\": \"editorTextFocus\"\n      }\n    ],\n    \"views\": {\n      \"explorer\": [\n        {\n          \"id\": \"knip.imports\",\n          \"name\": \"Imports\",\n          \"contextualTitle\": \"Imports\",\n          \"when\": \"config.knip.imports.enabled\",\n          \"icon\": \"callhierarchy-incoming\"\n        },\n        {\n          \"id\": \"knip.exports\",\n          \"name\": \"Exports\",\n          \"contextualTitle\": \"Exports\",\n          \"when\": \"config.knip.exports.enabled\",\n          \"icon\": \"call-outgoing\"\n        }\n      ]\n    },\n    \"menus\": {\n      \"view/title\": [\n        {\n          \"command\": \"knip.expandAll\",\n          \"when\": \"view == knip.imports\",\n          \"group\": \"navigation\"\n        },\n        {\n          \"command\": \"knip.expandAll\",\n          \"when\": \"view == knip.exports\",\n          \"group\": \"navigation\"\n        }\n      ]\n    },\n    \"configuration\": [\n      {\n        \"title\": \"Knip\",\n        \"order\": 1,\n        \"properties\": {\n          \"knip.deferSession\": {\n            \"type\": \"boolean\",\n            \"default\": false,\n            \"description\": \"Don't build module graph until explicit start\"\n          },\n          \"knip.requireConfig\": {\n            \"type\": \"boolean\",\n            \"default\": false,\n            \"description\": \"Require Knip configuration file\"\n          },\n          \"knip.configFilePath\": {\n            \"type\": \"string\",\n            \"default\": \"\",\n            \"description\": \"Path to Knip configuration file (relative to workspace root)\"\n          }\n        }\n      },\n      {\n        \"id\": \"editor\",\n        \"title\": \"Editor\",\n        \"displayName\": \"Editor\",\n        \"order\": 2,\n        \"properties\": {\n          \"knip.editor.exports.codelens.enabled\": {\n            \"type\": \"boolean\",\n            \"default\": true,\n            \"description\": \"Show CodeLens for imports of exports\"\n          },\n          \"knip.editor.dependencies.hover.enabled\": {\n            \"type\": \"boolean\",\n            \"default\": true,\n            \"description\": \"Show import locations on hover over dependencies in package.json\"\n          },\n          \"knip.editor.exports.hover.enabled\": {\n            \"type\": \"boolean\",\n            \"default\": true,\n            \"description\": \"Show imports for exports on hover\"\n          },\n          \"knip.editor.exports.hover.maxSnippets\": {\n            \"type\": \"number\",\n            \"default\": 10,\n            \"minimum\": -1,\n            \"description\": \"Limit number of snippets per file (-1 = unlimited, 0 = disable)\"\n          },\n          \"knip.editor.exports.hover.includeImportLocationSnippet\": {\n            \"type\": \"boolean\",\n            \"default\": false,\n            \"description\": \"Include import location with snippets\"\n          },\n          \"knip.editor.exports.hover.timeout\": {\n            \"type\": \"number\",\n            \"default\": 300,\n            \"minimum\": 0,\n            \"description\": \"Limit time to collect hover snippets (in ms)\"\n          },\n          \"knip.editor.exports.quickfix.enabled\": {\n            \"type\": \"boolean\",\n            \"default\": true,\n            \"description\": \"Enable quickfixes for unused exports and members of exported classes and enums\"\n          },\n          \"knip.editor.exports.quickfix.jsdocTags\": {\n            \"type\": \"array\",\n            \"default\": [\n              \"@lintignore\",\n              \"@internal\",\n              \"@public\"\n            ],\n            \"description\": \"JSDoc tags for unused export quick fixes\",\n            \"items\": {\n              \"type\": \"string\"\n            }\n          },\n          \"knip.editor.exports.highlight.dimExports\": {\n            \"type\": \"boolean\",\n            \"default\": false,\n            \"description\": \"Dim unused exports (instead of warnings)\"\n          },\n          \"knip.editor.exports.highlight.dimTypes\": {\n            \"type\": \"boolean\",\n            \"default\": false,\n            \"description\": \"Dim unused types (instead of warnings)\"\n          },\n          \"knip.editor.exports.highlight.dimEnumMembers\": {\n            \"type\": \"boolean\",\n            \"default\": false,\n            \"description\": \"Dim unused enum members (instead of warnings)\"\n          },\n          \"knip.editor.exports.highlight.dimClassMembers\": {\n            \"type\": \"boolean\",\n            \"default\": false,\n            \"description\": \"Dim unused class members (instead of warnings)\"\n          },\n          \"knip.editor.exports.highlight.dimDuplicates\": {\n            \"type\": \"boolean\",\n            \"default\": false,\n            \"description\": \"Dim duplicate exports (instead of warnings)\"\n          }\n        }\n      },\n      {\n        \"id\": \"imports\",\n        \"title\": \"Imports\",\n        \"displayName\": \"Imports & Exports\",\n        \"order\": 3,\n        \"type\": \"object\",\n        \"properties\": {\n          \"knip.imports.enabled\": {\n            \"type\": \"boolean\",\n            \"default\": true,\n            \"description\": \"Enable imports view\"\n          }\n        }\n      },\n      {\n        \"id\": \"exports\",\n        \"title\": \"Exports\",\n        \"displayName\": \"Imports & Exports\",\n        \"order\": 4,\n        \"type\": \"object\",\n        \"properties\": {\n          \"knip.exports.enabled\": {\n            \"type\": \"boolean\",\n            \"default\": true,\n            \"description\": \"Enable exports view\"\n          },\n          \"knip.exports.contention.enabled\": {\n            \"type\": \"boolean\",\n            \"default\": true,\n            \"description\": \"Report export contention (branching and conflicts)\"\n          }\n        }\n      }\n    ]\n  },\n  \"activationEvents\": [\n    \"onCommand:knip.restart\",\n    \"onCommand:knip.showHover\",\n    \"onStartupFinished\"\n  ],\n  \"icon\": \"icon-192x192.png\",\n  \"galleryBanner\": {\n    \"color\": \"#F56E0F\",\n    \"theme\": \"light\"\n  },\n  \"engines\": {\n    \"vscode\": \"^1.95.0\"\n  }\n}\n"
  },
  {
    "path": "packages/vscode-knip/scripts/publish.js",
    "content": "#!/usr/bin/env node\nimport { execSync } from 'node:child_process';\nimport { cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { parseArgs } from 'node:util';\nimport { rolldown } from 'rolldown';\n\n// https://marketplace.visualstudio.com/items?itemName=webpro.vscode-knip\n// https://marketplace.visualstudio.com/manage/publishers/webpro\n// https://open-vsx.org/extension/webpro/vscode-knip\n// https://open-vsx.org/user-settings/extensions\n\nconst { values: args } = parseArgs({\n  options: {\n    target: { type: 'string' },\n    publish: { type: 'boolean', default: false },\n    'pre-release': { type: 'boolean', default: false },\n  },\n});\n\nif (args.publish && !process.env.OVSX_PAT) {\n  console.error('Error: OVSX_PAT environment variable is required for publishing to Open VSX');\n  process.exit(1);\n}\n\nconst root = join(dirname(fileURLToPath(import.meta.url)), '..');\nconst dist = join(root, 'dist');\nconst nm = join(dist, 'node_modules');\n\nconst targets = {\n  'darwin-arm64': 'darwin-arm64',\n  'darwin-x64': 'darwin-x64',\n  'linux-x64': 'linux-x64-gnu',\n  'linux-arm64': 'linux-arm64-gnu',\n  'win32-x64': 'win32-x64-msvc',\n  'win32-arm64': 'win32-arm64-msvc',\n};\n\nconst currentTarget = `${process.platform}-${process.arch}`;\n\nconst ext = ['vscode', 'oxc-parser', /^@oxc-parser\\/binding-/, /^@oxc-resolver\\/binding-/, 'jiti', /jiti\\/dist/];\nconst extSession = [...ext, 'knip/session'];\n\nconst flags = [args['pre-release'] && '--pre-release', '--no-dependencies'].filter(Boolean).join(' ');\nconst vsixFiles = [];\n\nrmSync(dist, { recursive: true, force: true });\n\nconst bundle = async (input, output, external = ext, paths) => {\n  const build = await rolldown({ input: join(root, input), external, platform: 'node' });\n  await build.write({ format: 'cjs', minify: true, file: join(dist, output), paths });\n};\n\nconst paths = { 'knip/session': '../../knip/session.js' };\n\nawait bundle('../knip/src/session/index.ts', 'node_modules/knip/session.js');\n\nconst knipPkg = JSON.parse(readFileSync(join(root, '../knip/package.json'), 'utf8'));\nwriteFileSync(join(nm, 'knip/package.json'), JSON.stringify({ name: 'knip', version: knipPkg.version }));\nawait bundle('../mcp-server/src/tools.js', 'node_modules/@knip/mcp/tools.js', extSession, paths);\nawait bundle('../language-server/src/index.js', 'node_modules/@knip/language-server/index.js', extSession, paths);\nawait bundle('src/index.js', 'extension.js', [...extSession, '@knip/language-server', '@knip/mcp/tools'], {\n  'knip/session': './node_modules/knip/session.js',\n  '@knip/mcp/tools': './node_modules/@knip/mcp/tools.js',\n  '@knip/language-server': './node_modules/@knip/language-server/index.js',\n});\n\nconst knipNm = join(dirname(fileURLToPath(import.meta.resolve('knip'))), '..', 'node_modules');\n\ncpSync(join(knipNm, 'jiti'), join(nm, 'jiti'), { recursive: true, dereference: true });\ncpSync(join(knipNm, 'oxc-parser'), join(nm, 'oxc-parser'), { recursive: true, dereference: true });\n\ncpSync(join(root, '../mcp-server/src/docs'), join(nm, '@knip/mcp/docs'), { recursive: true });\n\n// Remove \"type\": \"module\" from package.json so CJS extension.js works\nconst pkgPath = join(root, 'package.json');\nconst pkgOriginal = readFileSync(pkgPath, 'utf8');\nconst pkg = JSON.parse(pkgOriginal);\ndelete pkg.type;\nwriteFileSync(pkgPath, JSON.stringify(pkg, null, 2));\n\n// Copy package.json to dist for test run\nconst distPkg = { ...pkg, main: './extension.js' };\nwriteFileSync(join(dist, 'package.json'), JSON.stringify(distPkg, null, 2));\n\nconst selectedTargets = args.target\n  ? [[args.target, targets[args.target]]]\n  : args.publish\n    ? Object.entries(targets)\n    : [[currentTarget, targets[currentTarget]]];\n\nconst packNativeBinding = (scope, name, binding) => {\n  rmSync(join(nm, scope), { recursive: true, force: true });\n  mkdirSync(join(nm, `${scope}/binding-${binding}`), { recursive: true });\n  const tmp = mkdtempSync(join(tmpdir(), 'oxc-'));\n  execSync(`npm pack ${scope}/binding-${binding}`, { cwd: tmp, stdio: 'pipe' });\n  execSync('tar -xzf *.tgz', { cwd: tmp, stdio: 'pipe' });\n  cpSync(\n    execSync(`find ${tmp}/package -name \"*.node\"`, { encoding: 'utf-8' }).trim(),\n    join(nm, `${scope}/binding-${binding}/${name}.${binding}.node`)\n  );\n  cpSync(join(tmp, 'package/package.json'), join(nm, `${scope}/binding-${binding}/package.json`));\n  rmSync(tmp, { recursive: true });\n};\n\nfor (const [target, binding] of selectedTargets) {\n  packNativeBinding('@oxc-parser', 'parser', binding);\n  packNativeBinding('@oxc-resolver', 'resolver', binding);\n\n  execSync(`pnpm vsce package ${flags} --target ${target}`, { cwd: root, stdio: 'inherit' });\n\n  const pkg = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));\n  vsixFiles.push(join(root, `${pkg.name}-${target}-${pkg.version}.vsix`));\n}\n\nif (args.publish) {\n  for (const vsix of vsixFiles) {\n    execSync(`pnpm vsce publish --packagePath ${vsix}`, { cwd: root, stdio: 'inherit' });\n    execSync(`ovsx publish ${vsix}`, { cwd: root, stdio: 'inherit' });\n  }\n}\n\n// Restore original package.json\nwriteFileSync(pkgPath, pkgOriginal);\n"
  },
  {
    "path": "packages/vscode-knip/scripts/sync-from-mcp.js",
    "content": "#!/usr/bin/env node\nimport { readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n  DOC_TOOL_DESCRIPTION,\n  DOC_TOOL_TOPIC_DESCRIPTION,\n  RUN_KNIP_TOOL_DESCRIPTION,\n} from '../../mcp-server/src/texts.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkgPath = join(__dirname, '../package.json');\n\nconst pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n\npkg.contributes.languageModelTools[0].modelDescription = RUN_KNIP_TOOL_DESCRIPTION;\npkg.contributes.languageModelTools[1].modelDescription = DOC_TOOL_DESCRIPTION;\npkg.contributes.languageModelTools[1].inputSchema.properties.topic.description = DOC_TOOL_TOPIC_DESCRIPTION;\n\nwriteFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`);\n\nconsole.log('Synced content to vscode-knip/package.json');\n"
  },
  {
    "path": "packages/vscode-knip/src/collect-dependency-hover-snippets.js",
    "content": "import * as vscode from 'vscode';\n\n/**\n * @param {import('knip/session').DependencyNode[]} imports\n * @returns {Promise<import('./render-dependency-hover.js').DependencySnippet[]>}\n */\nexport async function collectDependencySnippets(imports) {\n  /** @type {import('./render-dependency-hover.js').DependencySnippet[]} */\n  const snippets = [];\n  /** @type {Map<string, vscode.TextDocument>} */\n  const documents = new Map();\n  const seen = new Set();\n\n  for (const _import of imports) {\n    if (!_import.filePath) continue;\n\n    const dedupeKey = _import.line === undefined ? `${_import.filePath}:ref` : _import.filePath;\n    if (seen.has(dedupeKey)) continue;\n    seen.add(dedupeKey);\n\n    if (_import.line === undefined) {\n      snippets.push({ filePath: _import.filePath, line: undefined, col: undefined, snippet: undefined });\n      continue;\n    }\n\n    try {\n      const uri = vscode.Uri.file(_import.filePath);\n      let doc = documents.get(_import.filePath);\n      if (!doc) {\n        doc = await vscode.workspace.openTextDocument(uri);\n        documents.set(_import.filePath, doc);\n      }\n\n      const lineIndex = _import.line - 1;\n      if (lineIndex < 0 || lineIndex >= doc.lineCount) continue;\n\n      const text = doc.lineAt(lineIndex).text.trim();\n      snippets.push({ filePath: _import.filePath, line: _import.line, col: _import.col, snippet: text });\n    } catch {}\n  }\n\n  return snippets;\n}\n"
  },
  {
    "path": "packages/vscode-knip/src/collect-export-hover-snippets.js",
    "content": "import * as vscode from 'vscode';\n\n/**\n * @import { SourceLocation } from 'knip/session';\n */\n\n/**\n * @typedef {{ line: number; col: number; snippet: string }} HoverSnippet\n * @typedef {(HoverSnippet[] | undefined)[]} HoverSnippets\n */\n\n/**\n * @param {string} identifier\n * @param {SourceLocation[]} locations\n * @param {{ timeout?: number; includeImportLocationSnippet?: boolean }} [options]\n * @returns {Promise<HoverSnippets>}\n */\nexport async function collectExportHoverSnippets(identifier, locations, options = {}) {\n  const { timeout = 300, includeImportLocationSnippet = false } = options;\n\n  if (!Array.isArray(locations) || locations.length === 0) return [];\n\n  const startTime = Date.now();\n\n  /** @type {HoverSnippets} */\n  const snippets = [];\n  const seen = new Set();\n  const documents = new Map();\n\n  for (const [index, location] of locations.entries()) {\n    if (!location?.filePath) continue;\n\n    if (timeout > 0 && Date.now() - startTime > timeout) break;\n\n    try {\n      const uri = vscode.Uri.file(location.filePath);\n      let document = documents.get(location.filePath);\n      if (!document) {\n        document = await vscode.workspace.openTextDocument(uri);\n        documents.set(location.filePath, document);\n      }\n\n      const zeroLine = Math.max((location.line ?? 1) - 1, 0);\n      const zeroChar = Math.max((location.col ?? 1) - 1, 0);\n      const position = new vscode.Position(zeroLine, zeroChar);\n\n      const highlights = await vscode.commands.executeCommand('vscode.executeDocumentHighlights', uri, position);\n\n      if (!Array.isArray(highlights) || highlights.length === 0) continue;\n\n      snippets[index] ??= [];\n\n      for (const highlight of highlights) {\n        if (!highlight?.range) continue;\n        if (!includeImportLocationSnippet && highlight.range.contains(position)) continue;\n\n        const line = highlight.range.start.line;\n        if (line < 0 || line >= document.lineCount) continue;\n\n        const dedupeKey = `${location.filePath}:${line}`;\n        if (seen.has(dedupeKey)) continue;\n        seen.add(dedupeKey);\n\n        const text = document.lineAt(line).text;\n        if (!text.includes(identifier)) continue;\n\n        const char = highlight.range.start.character;\n        const start = Math.max(0, char - 30);\n        const end = Math.min(text.length, highlight.range.end.character + 30);\n\n        let snippet = text.slice(start, end);\n        const trimStart = snippet.length - snippet.trimStart().length;\n        const trimEnd = snippet.length - snippet.trimEnd().length;\n        snippet = snippet.trim();\n\n        if (start > 0 && trimStart === 0) snippet = `…${snippet}`;\n        if (end < text.length && trimEnd === 0) snippet = `${snippet}…`;\n        if (snippet.length > 60) snippet = `${snippet.slice(0, 60).trimEnd()}…`;\n\n        snippets[index].push({ line: line + 1, col: char + 1, snippet });\n      }\n\n      if (snippets[index]) snippets[index].sort((a, b) => a.line - b.line || a.col - b.col);\n    } catch (_error) {}\n  }\n\n  return snippets;\n}\n"
  },
  {
    "path": "packages/vscode-knip/src/index.js",
    "content": "import { existsSync, readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n  REQUEST_FILE_NODE,\n  REQUEST_PACKAGE_JSON,\n  REQUEST_RESTART,\n  REQUEST_START,\n  REQUEST_STOP,\n  SESSION_LOADING,\n} from '@knip/language-server/constants';\nimport { getErrorMessage } from '@knip/mcp/tools';\nimport { KNIP_CONFIG_LOCATIONS } from 'knip/session';\nimport * as vscode from 'vscode';\nimport { LanguageClient, TransportKind } from 'vscode-languageclient/node.js';\nimport { collectDependencySnippets } from './collect-dependency-hover-snippets.js';\nimport { collectExportHoverSnippets } from './collect-export-hover-snippets.js';\nimport { renderDependencyHover } from './render-dependency-hover.js';\nimport { renderExportHover, renderExportHoverEntryPaths } from './render-export-hover.js';\nimport { registerKnipTools, setLanguageClient, setOutputChannel } from './tools.js';\nimport { ExportsTreeViewProvider } from './tree-view-exports.js';\nimport { ImportsTreeViewProvider } from './tree-view-imports.js';\n\nconst require = createRequire(import.meta.url);\n\n/**\n * @import { ExtensionContext, LogOutputChannel, WorkspaceFolder } from 'vscode';\n * @import { ServerOptions, LanguageClientOptions } from 'vscode-languageclient/node.js';\n * @import { PackageJson } from 'knip/session';\n * @import { TreeData } from './tree-view-base.js';\n */\n\n/** @param {string} value */\nconst toPosix = value => value.split(path.sep).join(path.posix.sep);\n\nexport class Extension {\n  /** @type {Extension | undefined} */\n  static #instance;\n\n  /** @type {string} */\n  static #serverModule = require.resolve('@knip/language-server');\n\n  /** @type {ExtensionContext} */\n  #context;\n\n  /** @type {Map<string, LanguageClient>} */\n  #clients = new Map();\n\n  /** @type {LogOutputChannel} */\n  #outputChannel;\n\n  /** @type {ImportsTreeViewProvider | undefined} */\n  #importsProvider;\n\n  /** @type {ExportsTreeViewProvider | undefined} */\n  #exportsProvider;\n\n  /** @type {{ dependenciesUsage: Record<string, import('knip/session').DependencyNodes> } | undefined} */\n  #packageJsonCache;\n\n  /**\n   * @param {ExtensionContext} context\n   */\n  constructor(context) {\n    this.#context = context;\n    this.#outputChannel = vscode.window.createOutputChannel('Knip', { log: true });\n  }\n\n  /**\n   * @param {ExtensionContext} context\n   * @returns {Extension}\n   */\n  static create(context) {\n    Extension.#instance ??= new Extension(context);\n    return Extension.#instance;\n  }\n\n  async init() {\n    this.#registerCommands();\n    this.#registerHoverProvider();\n    this.#registerCodeLensProvider();\n    this.#setupTreeViews();\n    this.#setupEventHandlers();\n\n    // Register LM tools\n    setOutputChannel(this.#outputChannel);\n    registerKnipTools(this.#context);\n\n    this.#outputChannel.info('Initializing extension');\n\n    await this.#startClients();\n    await this.#refresh();\n  }\n\n  async stop() {\n    await this.#stopClients();\n  }\n\n  /**\n   * @param {vscode.Uri} uri\n   * @returns {LanguageClient | undefined}\n   */\n  #getClientForUri(uri) {\n    const folder = vscode.workspace.getWorkspaceFolder(uri);\n    if (!folder) return;\n    return this.#clients.get(folder.uri.toString());\n  }\n\n  /**\n   * @returns {LanguageClient | undefined}\n   */\n  #getActiveClient() {\n    const editor = vscode.window.activeTextEditor;\n    if (!editor) return this.#clients.values().next().value;\n    return this.#getClientForUri(editor.document.uri);\n  }\n\n  async #startClients() {\n    const folders = vscode.workspace.workspaceFolders;\n    if (!folders) return;\n\n    for (const folder of folders) {\n      await this.#startClientForFolder(folder);\n    }\n\n    this.#updateToolsClient();\n    this.#logManagedWorkspaces();\n  }\n\n  /**\n   * @param {WorkspaceFolder} folder\n   */\n  async #startClientForFolder(folder) {\n    const key = folder.uri.toString();\n    if (this.#clients.has(key)) return;\n\n    const config = vscode.workspace.getConfiguration('knip', folder.uri);\n\n    if (config.get('requireConfig', false)) {\n      const hasConfig = await this.#hasKnipConfig(folder);\n      if (!hasConfig) {\n        this.#outputChannel.info(`No config found in ${folder.name}, skipping`);\n        return;\n      }\n    }\n\n    this.#outputChannel.info(`Starting Knip Language Server for ${folder.name}`);\n\n    /** @type {ServerOptions} */\n    const serverOptions = {\n      run: { module: Extension.#serverModule, transport: TransportKind.ipc, runtime: 'node' },\n      debug: {\n        module: Extension.#serverModule,\n        transport: TransportKind.ipc,\n        runtime: 'node',\n        options: { execArgv: ['--inspect=6009'] },\n      },\n    };\n\n    /** @type {LanguageClientOptions} */\n    const clientOptions = {\n      documentSelector: [{ scheme: 'file', pattern: `${folder.uri.fsPath}/**/*` }],\n      synchronize: {\n        fileEvents: [vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(folder, '**/*'))],\n      },\n      workspaceFolder: folder,\n      initializationOptions: { config },\n      outputChannel: this.#outputChannel,\n      outputChannelName: 'Knip',\n    };\n\n    const client = new LanguageClient(`knip-${folder.name}`, `Knip (${folder.name})`, serverOptions, clientOptions);\n    this.#clients.set(key, client);\n\n    await client.start();\n  }\n\n  /**\n   * @param {WorkspaceFolder} folder\n   */\n  async #stopClientForFolder(folder) {\n    const key = folder.uri.toString();\n    const client = this.#clients.get(key);\n    if (!client) return;\n\n    this.#outputChannel.info(`Stopping client for ${folder.name}`);\n    this.#clients.delete(key);\n\n    if (!client.needsStart()) {\n      try {\n        await client.sendRequest(REQUEST_STOP);\n      } catch (_error) {}\n    }\n    if (client.needsStop()) await client.stop();\n  }\n\n  async #stopClients() {\n    this.#outputChannel.info(`Stopping ${this.#clients.size} client(s)...`);\n    for (const client of this.#clients.values()) {\n      if (!client.needsStart()) {\n        try {\n          await client.sendRequest(REQUEST_STOP);\n        } catch (_error) {}\n      }\n      if (client.needsStop()) await client.stop();\n    }\n    this.#clients.clear();\n    setLanguageClient(undefined);\n  }\n\n  #updateToolsClient() {\n    const firstClient = this.#clients.values().next().value;\n    setLanguageClient(firstClient);\n  }\n\n  #logManagedWorkspaces() {\n    const names = [...this.#clients.keys()].map(uri => fileURLToPath(uri));\n    this.#outputChannel.info(`Managing ${this.#clients.size} workspace(s): ${names.join(', ')}`);\n  }\n\n  /**\n   * Walk up directory tree to find lockfile and package.json#packageManager\n   * @param {string} startDir - Starting directory path\n   * @returns {string} Package manager name ('pnpm', 'yarn', or 'npm')\n   * @throws {Error} If no package manager can be detected\n   */\n  #detectPackageManager(startDir) {\n    let dir = startDir;\n    const root = path.parse(dir).root;\n\n    while (dir !== root) {\n      if (existsSync(path.join(dir, 'pnpm-lock.yaml'))) return 'pnpm';\n      if (existsSync(path.join(dir, 'yarn.lock'))) return 'yarn';\n      if (existsSync(path.join(dir, 'package-lock.json'))) return 'npm';\n\n      const packageJsonPath = path.join(dir, 'package.json');\n      if (existsSync(packageJsonPath)) {\n        try {\n          const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));\n          if (packageJson.packageManager) {\n            const [pmName] = packageJson.packageManager.split('@');\n            if (pmName === 'pnpm' || pmName === 'yarn' || pmName === 'npm') {\n              return pmName;\n            }\n          }\n        } catch (_error) {}\n      }\n\n      dir = path.dirname(dir);\n    }\n\n    throw new Error(\n      `Could not detect package manager. Please ensure a lock file (pnpm-lock.yaml, yarn.lock, or package-lock.json) exists in your project. Current working dir: ${startDir}`\n    );\n  }\n\n  #registerCommands() {\n    const start = vscode.commands.registerCommand('knip.start', async () => {\n      for (const client of this.#clients.values()) {\n        try {\n          await client.sendRequest(REQUEST_START);\n        } catch (error) {\n          vscode.window.showErrorMessage((error?.message || error).toString());\n        }\n      }\n    });\n\n    const restart = vscode.commands.registerCommand(REQUEST_RESTART, async () => {\n      const client = this.#getActiveClient();\n      if (!client) return;\n      try {\n        this.#packageJsonCache = undefined;\n        await client.sendRequest(REQUEST_RESTART);\n      } catch (error) {\n        vscode.window.showErrorMessage((error?.message || error).toString());\n      }\n    });\n\n    const showHover = vscode.commands.registerCommand('knip.showHover', async () => {\n      const editor = vscode.window.activeTextEditor;\n      if (!editor) return;\n      await vscode.commands.executeCommand('editor.action.showHover');\n    });\n\n    const installDependency = vscode.commands.registerCommand(\n      'knip.installDependency',\n      async (packageName, dependencyType, workspacePath) => {\n        try {\n          if (!existsSync(workspacePath)) {\n            vscode.window.showErrorMessage(`Workspace directory not found: ${workspacePath}`);\n            return;\n          }\n\n          const packageManager = this.#detectPackageManager(workspacePath);\n          const isDev = dependencyType === 'devDependencies';\n\n          const commands = {\n            npm: `npm install ${packageName}${isDev ? ' --save-dev' : ' --save'}`,\n            pnpm: `pnpm add ${packageName}${isDev ? ' -D' : ''}`,\n            yarn: `yarn add ${packageName}${isDev ? ' -D' : ''}`,\n          };\n\n          const command = commands[packageManager];\n          const terminal = vscode.window.createTerminal({\n            name: `Install ${packageName}`,\n            cwd: workspacePath,\n          });\n          terminal.show();\n          terminal.sendText(command);\n        } catch (error) {\n          vscode.window.showErrorMessage(`Failed to install dependency: ${getErrorMessage(error)}`);\n        }\n      }\n    );\n\n    this.#context.subscriptions.push(start, restart, showHover, installDependency);\n  }\n\n  /**\n   *\n   * @param {vscode.TextDocument} document\n   * @return {Promise<import('knip/session').File | typeof SESSION_LOADING | undefined>}\n   */\n  async #requestFileDescriptor(document) {\n    const uri = document.uri.toString();\n    const client = this.#getClientForUri(document.uri);\n    if (!client) return;\n    return await client.sendRequest(REQUEST_FILE_NODE, { uri });\n  }\n\n  #registerHoverProvider() {\n    const hoverProvider = vscode.languages.registerHoverProvider(\n      { scheme: 'file' },\n      {\n        provideHover: async (document, position) => {\n          const content = await this.#getHoverContent(document, position);\n          if (!content) return null;\n          const md = new vscode.MarkdownString(content.value);\n          md.isTrusted = true;\n          return new vscode.Hover(md);\n        },\n      }\n    );\n    this.#context.subscriptions.push(hoverProvider);\n  }\n\n  #registerCodeLensProvider() {\n    const codeLensProvider = vscode.languages.registerCodeLensProvider(\n      { scheme: 'file' },\n      /** @type {vscode.CodeLensProvider} */ ({ provideCodeLenses: this.#provideCodeLenses.bind(this) })\n    );\n    this.#context.subscriptions.push(codeLensProvider);\n  }\n\n  /**\n   * @param {vscode.TextDocument} document\n   * @param {vscode.CancellationToken} _token\n   * @returns {Promise<vscode.CodeLens[] | null>}\n   */\n  async #provideCodeLenses(document, _token) {\n    const config = vscode.workspace.getConfiguration('knip');\n    if (!config.get('editor.exports.codelens.enabled', true)) return null;\n\n    const file = await this.#requestFileDescriptor(document);\n    if (!file || file === SESSION_LOADING) return null;\n\n    /** @type {vscode.CodeLens[]} */\n    const codeLenses = [];\n\n    for (const _export of file.exports) {\n      const size = _export.importLocations.length;\n      if (size === 0) continue;\n      const pos = document.positionAt(_export.pos);\n      codeLenses.push(\n        new vscode.CodeLens(new vscode.Range(pos, pos), {\n          title: `↻ ${size} import${size > 1 ? 's' : ''}`,\n          command: 'knip.showReferences',\n          arguments: [document.uri, pos, _export.importLocations],\n        })\n      );\n    }\n\n    return codeLenses;\n  }\n\n  /** @param {vscode.TextEditor} [editor]  */\n  async #refresh(editor) {\n    const activeEditor = editor ?? vscode.window.activeTextEditor;\n    if (!activeEditor) {\n      this.#importsProvider?.clear('Open a file to show imports');\n      this.#exportsProvider?.clear('Open a file to show exports');\n      return;\n    }\n\n    const position = activeEditor.selection?.active ?? new vscode.Position(0, 0);\n    const data = await this.#getFileForTreeViews(activeEditor);\n    if (!data) return;\n    await this.#importsProvider?.refresh(data, position);\n    await this.#exportsProvider?.refresh(data, position);\n  }\n\n  /**\n   * @param {vscode.TextEditor} editor\n   * @returns {Promise<TreeData | undefined>}\n   */\n  async #getFileForTreeViews(editor) {\n    const document = editor.document;\n    const uri = document.uri;\n\n    if (uri.scheme !== 'file') return;\n\n    if (path.basename(uri.fsPath) === 'package.json') {\n      /** @type {undefined | PackageJson} */\n      let contents;\n      try {\n        contents = JSON.parse(document.getText());\n      } catch {}\n      if (!contents) return { message: '(error retrieving file)' };\n      return { kind: 'manifest', uri, manifest: contents };\n    }\n\n    const client = this.#getClientForUri(uri);\n    if (!client) return { message: 'Language server not connected' };\n\n    try {\n      const file = await this.#requestFileDescriptor(document);\n      if (file === SESSION_LOADING) return { message: '(building module graph...)' };\n      if (!file) return { message: '(file not in project)' };\n      return { kind: 'file', uri, file };\n    } catch (error) {\n      this.#outputChannel.error(`Error requesting file: ${getErrorMessage(error)}`);\n      return { message: '(error requesting file)' };\n    }\n  }\n\n  /**\n   * @param {vscode.TextDocument} document\n   * @param {vscode.Position} position\n   * @returns {Promise<{ kind: 'markdown'; value: string } | null>}\n   */\n  async #getHoverContent(document, position) {\n    const client = this.#getClientForUri(document.uri);\n    if (!client) return null;\n\n    const config = vscode.workspace.getConfiguration('knip');\n    if (!config.get('editor.exports.hover.enabled', true)) return null;\n\n    const folder = vscode.workspace.getWorkspaceFolder(document.uri);\n    if (!folder) return null;\n    const root = toPosix(folder.uri.fsPath);\n\n    if (path.basename(document.uri.fsPath) === 'package.json') {\n      if (!config.get('editor.dependencies.hover.enabled', true)) return null;\n      return this.#getDependencyHoverContent(document, position, root, client);\n    }\n\n    const file = await this.#requestFileDescriptor(document);\n    if (!file || file === SESSION_LOADING) return null;\n\n    const _export = this.#findExportAtPosition(file, position);\n    if (!_export) return null;\n\n    const filePath = fileURLToPath(document.uri.toString());\n\n    if (_export.importLocations.length > 0) {\n      /** @type {number} */\n      const maxSnippets = config.get('editor.exports.hover.maxSnippets', 3);\n      /** @type {number} */\n      const timeout = config.get('editor.exports.hover.timeout', 300);\n      /** @type {boolean} */\n      const includeImportLocationSnippet = config.get('editor.exports.hover.includeImportLocationSnippet', false);\n\n      /** @type {import('./collect-export-hover-snippets.js').HoverSnippets} */\n      let snippets = [];\n      if (maxSnippets !== 0) {\n        snippets = await collectExportHoverSnippets(_export.identifier, _export.importLocations, {\n          timeout,\n          includeImportLocationSnippet,\n        });\n      }\n\n      return renderExportHover(_export, root, snippets, maxSnippets);\n    }\n\n    if (_export.entryPaths.size > 0) {\n      return renderExportHoverEntryPaths(_export, filePath, root);\n    }\n\n    return null;\n  }\n\n  /**\n   * @param {vscode.TextDocument} document\n   * @param {vscode.Position} position\n   * @param {string} root\n   * @param {LanguageClient} client\n   * @returns {Promise<{ kind: 'markdown'; value: string } | null>}\n   */\n  async #getDependencyHoverContent(document, position, root, client) {\n    const packageName = this.#findDependencyAtPosition(document, position);\n    if (!packageName) return null;\n\n    try {\n      if (!this.#packageJsonCache) {\n        /** @type {{ dependenciesUsage: Record<string, import('knip/session').DependencyNodes> } | typeof SESSION_LOADING | undefined} */\n        const result = await client.sendRequest(REQUEST_PACKAGE_JSON);\n        if (!result || result === SESSION_LOADING) return null;\n        this.#packageJsonCache = result;\n      }\n\n      const usage = this.#packageJsonCache.dependenciesUsage[packageName];\n      if (!usage || usage.imports.length === 0) return null;\n\n      const workspaceDir = `${toPosix(path.dirname(document.uri.fsPath))}/`;\n      const imports = usage.imports.filter(_import => _import.filePath.startsWith(workspaceDir));\n      if (imports.length === 0) return null;\n\n      const snippets = await collectDependencySnippets(imports);\n      return renderDependencyHover({ packageName, imports }, root, snippets);\n    } catch (error) {\n      this.#outputChannel.error(`Error getting dependency usage: ${getErrorMessage(error)}`);\n      return null;\n    }\n  }\n\n  /**\n   * @param {vscode.TextDocument} document\n   * @param {vscode.Position} position\n   * @returns {string | undefined}\n   */\n  #findDependencyAtPosition(document, position) {\n    const text = document.getText();\n    let manifest;\n    try {\n      manifest = JSON.parse(text);\n    } catch {\n      return;\n    }\n\n    const depSections = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];\n\n    for (const section of depSections) {\n      if (!manifest[section]) continue;\n\n      const sectionRegex = new RegExp(`\"${section}\"\\\\s*:\\\\s*\\\\{`, 'g');\n      const sectionMatch = sectionRegex.exec(text);\n      if (!sectionMatch) continue;\n\n      const sectionStart = sectionMatch.index;\n      let braceCount = 1;\n      let sectionEnd = sectionStart + sectionMatch[0].length;\n\n      while (braceCount > 0 && sectionEnd < text.length) {\n        if (text[sectionEnd] === '{') braceCount++;\n        else if (text[sectionEnd] === '}') braceCount--;\n        sectionEnd++;\n      }\n\n      const offset = document.offsetAt(position);\n      if (offset < sectionStart || offset > sectionEnd) continue;\n\n      for (const packageName of Object.keys(manifest[section])) {\n        const packageRegex = new RegExp(`\"(${packageName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')})\"\\\\s*:`, 'g');\n        packageRegex.lastIndex = sectionStart;\n\n        let match;\n        // oxlint-disable-next-line no-cond-assign\n        while ((match = packageRegex.exec(text)) !== null) {\n          if (match.index > sectionEnd) break;\n\n          const matchStart = match.index + 1;\n          const matchEnd = matchStart + packageName.length;\n\n          if (offset >= matchStart && offset <= matchEnd) {\n            return packageName;\n          }\n        }\n      }\n    }\n  }\n\n  /**\n   * @param {import('knip/session').File} file\n   * @param {vscode.Position} position\n   * @returns {import('knip/session').Export | undefined}\n   */\n  #findExportAtPosition(file, position) {\n    for (const _export of file.exports) {\n      const exportLine = _export.line - 1;\n      if (position.line !== exportLine) continue;\n      const col = _export.col - 1;\n      const identifier = _export.identifier;\n      if (identifier === 'default') return _export;\n      if (position.character >= col && position.character <= col + identifier.length) return _export;\n    }\n  }\n\n  /**\n   * @param {vscode.WorkspaceFolder} folder\n   * @returns {Promise<boolean>}\n   */\n  async #hasKnipConfig(folder) {\n    const config = vscode.workspace.getConfiguration('knip');\n    const configFile = config.get('configFilePath', '');\n    const locations = configFile ? [configFile] : KNIP_CONFIG_LOCATIONS;\n\n    for (const location of locations) {\n      const candidate = vscode.Uri.joinPath(folder.uri, location);\n      try {\n        await vscode.workspace.fs.stat(candidate);\n        return true;\n      } catch (_error) {}\n    }\n\n    return false;\n  }\n\n  #setupTreeViews() {\n    this.#importsProvider = new ImportsTreeViewProvider();\n    const importsView = vscode.window.createTreeView('knip.imports', {\n      treeDataProvider: this.#importsProvider,\n      showCollapseAll: true,\n    });\n    this.#importsProvider.setTreeView(importsView);\n    this.#context.subscriptions.push(importsView);\n\n    this.#exportsProvider = new ExportsTreeViewProvider();\n    const exportsView = vscode.window.createTreeView('knip.exports', {\n      treeDataProvider: this.#exportsProvider,\n      showCollapseAll: true,\n    });\n    this.#exportsProvider.setTreeView(exportsView);\n    this.#context.subscriptions.push(exportsView);\n\n    const goToPosition = vscode.commands.registerCommand('knip.goToPosition', (uri, line, col) => {\n      const position = new vscode.Position(line, col);\n      const selection = new vscode.Range(position, position);\n      vscode.window.showTextDocument(uri, { selection });\n    });\n\n    const showReferences = vscode.commands.registerCommand('knip.showReferences', (uri, position, importLocations) => {\n      const locations = importLocations.map(location => {\n        const pos = new vscode.Position(location.line ? location.line - 1 : 0, location.col ? location.col - 1 : 0);\n        return new vscode.Location(vscode.Uri.file(location.filePath), pos);\n      });\n      const vsPosition = new vscode.Position(position.line, position.character);\n      vscode.commands.executeCommand('editor.action.showReferences', vscode.Uri.parse(uri), vsPosition, locations);\n    });\n\n    const expandAll = vscode.commands.registerCommand('knip.expandAll', () => {\n      const expand = async provider => {\n        const treeViewLocal = provider?.treeView;\n        if (!treeViewLocal) return;\n\n        const expandNode = async element => {\n          try {\n            await treeViewLocal.reveal(element, { expand: true, focus: false, select: false });\n            const children = await provider?.getChildren(element);\n            if (children?.length) {\n              for (const child of children) {\n                await expandNode(child);\n              }\n            }\n          } catch (_error) {}\n        };\n\n        const sections = await provider?.getChildren(undefined);\n        if (!sections?.length) return;\n\n        for (const section of sections) {\n          await expandNode(section);\n        }\n\n        try {\n          await treeViewLocal.reveal(sections[0], { expand: true, focus: false, select: false });\n        } catch (_error) {}\n      };\n\n      expand(this.#importsProvider);\n      expand(this.#exportsProvider);\n    });\n\n    this.#context.subscriptions.push(goToPosition, showReferences, expandAll);\n  }\n\n  #setupEventHandlers() {\n    const selectionHandler = vscode.window.onDidChangeTextEditorSelection(event => {\n      if (event.textEditor === vscode.window.activeTextEditor) {\n        const activeSelection = event.selections[0]?.active;\n        if (activeSelection) {\n          this.#importsProvider?.updatePosition(activeSelection);\n          this.#exportsProvider?.updatePosition(activeSelection);\n        }\n      }\n    });\n\n    const editorHandler = vscode.window.onDidChangeActiveTextEditor(editor => {\n      this.#refresh(editor);\n    });\n\n    const diagnosticsHandler = vscode.languages.onDidChangeDiagnostics(() => {\n      this.#packageJsonCache = undefined;\n      this.#refresh();\n    });\n\n    const documentHandler = vscode.workspace.onDidChangeTextDocument(event => {\n      const activeEditor = vscode.window.activeTextEditor;\n      if (!activeEditor) return;\n      if (event.document.uri.toString() !== activeEditor.document.uri.toString()) return;\n      if (path.basename(event.document.uri.fsPath) !== 'package.json') return;\n      this.#refresh(activeEditor);\n    });\n\n    const workspaceFoldersHandler = vscode.workspace.onDidChangeWorkspaceFolders(async event => {\n      for (const folder of event.added) {\n        await this.#startClientForFolder(folder);\n      }\n      for (const folder of event.removed) {\n        await this.#stopClientForFolder(folder);\n      }\n      this.#updateToolsClient();\n      this.#logManagedWorkspaces();\n    });\n\n    this.#context.subscriptions.push(\n      selectionHandler,\n      editorHandler,\n      diagnosticsHandler,\n      documentHandler,\n      workspaceFoldersHandler\n    );\n  }\n}\n\n/** @type {Extension | undefined} */\nlet instance;\n\n/** @param {ExtensionContext} context */\nexport async function activate(context) {\n  instance = Extension.create(context);\n  await instance.init();\n}\n\nexport async function deactivate() {\n  await instance?.stop();\n}\n"
  },
  {
    "path": "packages/vscode-knip/src/render-dependency-hover.js",
    "content": "import { pathToFileURL } from 'node:url';\nimport { toMarkdown } from 'mdast-util-to-markdown';\nimport { u } from 'unist-builder';\n\n/**\n * @typedef {import('mdast').PhrasingContent} PhrasingContent\n * @import { DependencyNodes } from 'knip/session';\n */\n\n/**\n * @typedef {{\n *   filePath: string;\n *   line: number | undefined;\n *   col: number | undefined;\n *   snippet: string | undefined\n * }} DependencySnippet\n */\n\n/** @param {string} text */\nconst replaceLessThan = text => text.replace(/</g, '‹');\n\nconst MAX_TOTAL_SNIPPETS = 30;\nconst MAX_SNIPPETS_PER_FILE = 3;\n\n/**\n * @param {DependencyNodes} usage\n * @param {string} root\n * @param {DependencySnippet[]} snippets\n * @returns {{ kind: 'markdown'; value: string } | null}\n */\nexport function renderDependencyHover(usage, root, snippets) {\n  const { packageName, imports } = usage;\n\n  if (imports.length === 0) return null;\n\n  const uniqueFiles = new Set(imports.map(_import => _import.filePath));\n  const fileCount = uniqueFiles.size;\n  const _root = `${root}/`;\n  const escapedName = packageName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n  const packageNameMatch = new RegExp(`(?<=['\"\\`])${escapedName}(?=['\"\\`/])`, 'g');\n\n  /** @type {Set<string>} */\n  const binaryNames = new Set();\n  for (const _import of imports) {\n    if (_import.binaryName && _import.binaryName !== packageName) binaryNames.add(_import.binaryName);\n  }\n\n  /** @type {PhrasingContent[]} */\n  const nodes = [\n    u('text', `At least ${fileCount} file${fileCount > 1 ? 's' : ''} reference${fileCount > 1 ? '' : 's'} `),\n    u('strong', [u('text', packageName)]),\n  ];\n\n  if (binaryNames.size > 0) nodes.push(u('text', ` (${[...binaryNames].join(', ')})`));\n\n  snippets.sort((a, b) => a.filePath.localeCompare(b.filePath));\n\n  /** @type {Map<string, DependencySnippet[]>} */\n  const snippetsByFile = new Map();\n  for (const snippet of snippets) {\n    if (!snippetsByFile.has(snippet.filePath)) snippetsByFile.set(snippet.filePath, []);\n    snippetsByFile.get(snippet.filePath)?.push(snippet);\n  }\n\n  let totalSnippetsShown = 0;\n\n  for (const [filePath, fileSnippets] of snippetsByFile) {\n    const relativePath = filePath.replace(_root, '');\n    const firstSnippet = fileSnippets[0];\n    const uri = pathToFileURL(filePath).toString();\n    const position = firstSnippet.line && firstSnippet.col ? `#${firstSnippet.line},${firstSnippet.col}` : '';\n\n    nodes.push(u('break'));\n    nodes.push(u('link', { url: `${uri}${position}` }, [u('text', relativePath)]));\n\n    if (totalSnippetsShown >= MAX_TOTAL_SNIPPETS) continue;\n\n    const remainingBudget = MAX_TOTAL_SNIPPETS - totalSnippetsShown;\n    const maxForThisFile = Math.min(MAX_SNIPPETS_PER_FILE, remainingBudget);\n    const limitedSnippets = fileSnippets.slice(0, maxForThisFile);\n    const snippetsOmittedInFile = fileSnippets.length - limitedSnippets.length;\n    totalSnippetsShown += limitedSnippets.length;\n\n    for (const snippet of limitedSnippets) {\n      if (!snippet.snippet) continue;\n\n      const snippetUri = pathToFileURL(filePath).toString();\n      const snippetPosition = snippet.line && snippet.col ? `#${snippet.line},${snippet.col}` : '';\n\n      /** @type {PhrasingContent[]} */\n      const snippetNodes = [];\n      const chunks = replaceLessThan(snippet.snippet).split(packageNameMatch);\n\n      for (let i = 0; i < chunks.length; i++) {\n        if (chunks[i]) snippetNodes.push(u('text', chunks[i]));\n        if (i < chunks.length - 1) snippetNodes.push(u('strong', [u('text', packageName)]));\n      }\n\n      if (snippetNodes.length === 0) snippetNodes.push(u('text', snippet.snippet));\n\n      nodes.push(u('break'));\n      nodes.push(u('inlineCode', String(snippet.line).padStart(4, '\\u00A0')));\n      nodes.push(u('text', '\\u00A0\\u00A0'));\n      nodes.push(u('link', { url: `${snippetUri}${snippetPosition}` }, snippetNodes));\n    }\n\n    if (snippetsOmittedInFile > 0) {\n      nodes.push(u('break'));\n      nodes.push(u('text', `…and ${snippetsOmittedInFile} more`));\n    }\n  }\n\n  return {\n    kind: 'markdown',\n    value: toMarkdown(u('root', [u('paragraph', nodes)])),\n  };\n}\n"
  },
  {
    "path": "packages/vscode-knip/src/render-export-hover.js",
    "content": "import { relative } from 'node:path/posix';\nimport { pathToFileURL } from 'node:url';\nimport { toMarkdown } from 'mdast-util-to-markdown';\nimport { u } from 'unist-builder';\n\n/**\n * @typedef {import('mdast').List} List\n * @typedef {import('mdast').Paragraph} Paragraph\n * @typedef {import('mdast').PhrasingContent} PhrasingContent\n * @typedef {import('./collect-export-hover-snippets.js').HoverSnippets} HoverSnippets\n * @import { Export } from 'knip/session';\n */\n\n/** @param {string} text */\nconst replaceLessThan = text => text.replace(lessThanMatch, '‹');\nconst lessThanMatch = /</g;\n\n/**\n * @param {Export} _export\n * @param {string} root\n * @param {HoverSnippets} snippets\n * @param {number} maxSnippets\n * @returns {{ kind: 'markdown'; value: string }}\n */\nexport function renderExportHover(_export, root, snippets, maxSnippets) {\n  const { identifier, importLocations } = _export;\n  const uniqueFiles = new Set(importLocations.map(loc => loc.filePath));\n  const refs = uniqueFiles.size;\n  const identifierMatch = new RegExp(`\\\\b${identifier}\\\\b`, 'g');\n  const _root = `${root}/`;\n\n  /** @type {PhrasingContent[]} */\n  const nodes = [\n    u('text', `${refs} file${refs > 1 ? 's' : ''} import${refs > 1 ? '' : 's'} `),\n    u('strong', [u('text', identifier)]),\n  ];\n\n  const len = importLocations.length;\n  const sortedIndices = new Array(len);\n  for (let i = 0; i < len; i++) sortedIndices[i] = i;\n  sortedIndices.sort((a, b) => importLocations[a].filePath.localeCompare(importLocations[b].filePath));\n\n  let lastFilePath = '';\n\n  for (const index of sortedIndices) {\n    const loc = importLocations[index];\n    const uri = pathToFileURL(loc.filePath).toString();\n    const position = loc.line && loc.col ? `#${loc.line},${loc.col}` : loc.line ? `#${loc.line}` : '';\n    const relativePath = loc.filePath.replace(_root, '');\n\n    if (loc.filePath !== lastFilePath) {\n      nodes.push(u('break'));\n      nodes.push(u('link', { url: `${uri}${position}` }, [u('text', relativePath)]));\n      lastFilePath = loc.filePath;\n    }\n\n    const snippetsForFile = snippets[index] ?? [];\n    const limitedSnippets = maxSnippets < 0 ? snippetsForFile : snippetsForFile.slice(0, maxSnippets);\n    if (limitedSnippets.length > 0) {\n      for (const snippet of limitedSnippets) {\n        if (!snippet.snippet.includes(identifier)) continue;\n        const snippetUri = pathToFileURL(loc.filePath).toString();\n        const _snippet = replaceLessThan(snippet.snippet);\n\n        const importNode = [];\n        const chunks = _snippet.split(identifierMatch);\n\n        for (let i = 0; i < chunks.length; i++) {\n          if (chunks[i]) importNode.push(u('text', chunks[i]));\n          if (i < chunks.length - 1) importNode.push(u('strong', [u('text', identifier)]));\n        }\n\n        if (importNode.length === 0) importNode.push(u('text', _snippet));\n\n        nodes.push(u('break'));\n        nodes.push(u('inlineCode', String(snippet.line).padStart(4, '\\u00A0')));\n        nodes.push(u('text', '\\u00A0\\u00A0'));\n        nodes.push(u('link', { url: `${snippetUri}#${snippet.line},${snippet.col}` }, importNode));\n      }\n\n      if (maxSnippets > 0 && snippetsForFile.length > maxSnippets) {\n        const remaining = snippetsForFile.length - maxSnippets;\n        nodes.push(u('break'));\n        nodes.push(u('text', `…and ${remaining} more`));\n      }\n    }\n  }\n\n  return {\n    kind: 'markdown',\n    value: toMarkdown(u('root', [u('paragraph', nodes)])),\n  };\n}\n\n/**\n * @param {Export} _export\n * @param {string} filePath\n * @param {string} root\n * @returns {{ kind: 'markdown'; value: string }}\n */\nexport function renderExportHoverEntryPaths(_export, filePath, root) {\n  const { identifier, entryPaths } = _export;\n  const entryPathsArray = Array.from(entryPaths);\n  const isEntryFile = entryPathsArray.length === 1 && entryPathsArray[0] === filePath;\n\n  /** @type {(Paragraph | List)[]} */\n  const rootNode = [\n    u('paragraph', [\n      u('text', `No files import `),\n      u('strong', [u('text', identifier)]),\n      u(\n        'text',\n        isEntryFile\n          ? '; exported by entry file'\n          : `; re-exported by ${entryPathsArray.length} entry file${entryPathsArray.length > 1 ? 's' : ''}:`\n      ),\n    ]),\n  ];\n\n  if (!isEntryFile) {\n    const nodes = entryPathsArray.map(entryPath => {\n      const url = pathToFileURL(entryPath).toString();\n      const text = relative(root, entryPath);\n      return u('listItem', { spread: false }, [u('paragraph', [u('link', { url }, [u('text', text)])])]);\n    });\n\n    rootNode.push(u('list', { ordered: false, spread: false }, nodes));\n  }\n\n  return {\n    kind: 'markdown',\n    value: toMarkdown(u('root', rootNode)),\n  };\n}\n"
  },
  {
    "path": "packages/vscode-knip/src/tools.js",
    "content": "import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { REQUEST_RESULTS } from '@knip/language-server/constants';\nimport { buildResults, ERROR_HINT, getDocs, getErrorMessage, getResults } from '@knip/mcp/tools';\nimport { KNIP_CONFIG_LOCATIONS } from 'knip/session';\nimport * as vscode from 'vscode';\n\n/**\n * @import { ExtensionContext } from 'vscode';\n * @import { LanguageClient } from 'vscode-languageclient/node.js';\n */\n\n/** @type {LanguageClient | undefined} */\nlet languageClient;\n\n/** @type {vscode.LogOutputChannel | undefined} */\nlet outputChannel;\n\n/**\n * @param {LanguageClient | undefined} client\n */\nexport function setLanguageClient(client) {\n  languageClient = client;\n}\n\n/**\n * @param {vscode.LogOutputChannel} channel\n */\nexport function setOutputChannel(channel) {\n  outputChannel = channel;\n}\n\n/**\n * @param {string} cwd\n */\nfunction findKnipConfig(cwd) {\n  for (const location of KNIP_CONFIG_LOCATIONS) {\n    const filePath = join(cwd, location);\n    if (existsSync(filePath)) return filePath;\n  }\n}\n\n/** @implements {vscode.LanguageModelTool<{ topic: string }>} */\nclass KnipDocsTool {\n  /**\n   * @param {vscode.LanguageModelToolInvocationOptions<{ topic: string }>} options\n   * @param {vscode.CancellationToken} _token\n   * @returns {Promise<vscode.LanguageModelToolResult>}\n   */\n  async invoke(options, _token) {\n    const result = getDocs(options.input.topic);\n    const text = 'content' in result ? result.content : result.error;\n    return new vscode.LanguageModelToolResult([new vscode.LanguageModelTextPart(text)]);\n  }\n}\n\n/** @implements {vscode.LanguageModelTool<{ cwd?: string }>} */\nclass KnipConfigureTool {\n  /**\n   * @param {vscode.LanguageModelToolInvocationOptions<{ cwd?: string }>} options\n   * @param {vscode.CancellationToken} _token\n   * @returns {Promise<vscode.LanguageModelToolResult>}\n   */\n  async invoke(options, _token) {\n    try {\n      const workspaceFolder = vscode.workspace.workspaceFolders?.[0];\n      const cwd = options.input.cwd || workspaceFolder?.uri.fsPath || process.cwd();\n\n      let result;\n      const configFilePath = findKnipConfig(cwd);\n\n      if (configFilePath && languageClient && !languageClient.needsStart()) {\n        const results = await languageClient.sendRequest(REQUEST_RESULTS);\n        if (results) result = buildResults(results, { cwd, configFilePath });\n      }\n\n      if (!result) result = await getResults(cwd);\n\n      return new vscode.LanguageModelToolResult([new vscode.LanguageModelTextPart(JSON.stringify(result))]);\n    } catch (error) {\n      const message = getErrorMessage(error);\n      outputChannel?.error(`Error running knip: ${message}`);\n      const result = { error: message, hint: ERROR_HINT };\n      return new vscode.LanguageModelToolResult([new vscode.LanguageModelTextPart(JSON.stringify(result))]);\n    }\n  }\n}\n\nconst configureTool = new KnipConfigureTool();\nconst docsTool = new KnipDocsTool();\n\n/**\n * @param {ExtensionContext} context\n */\nexport function registerKnipTools(context) {\n  context.subscriptions.push(vscode.lm.registerTool('knip-configure', configureTool));\n  context.subscriptions.push(vscode.lm.registerTool('knip-docs', docsTool));\n}\n"
  },
  {
    "path": "packages/vscode-knip/src/tree-view-base.js",
    "content": "import path from 'node:path';\nimport * as vscode from 'vscode';\n\n/**\n * @import { File, PackageJson } from 'knip/session'\n */\n\n/**\n * @typedef {{ label?: string; filePath?: string; children?: TreeNode[] }} TreeNode\n *\n * @typedef {vscode.TreeItem & {\n *   _parent?: TreeViewItem;\n *   _children?: TreeViewItem[];\n *   _reveal?: { identifier: string; line: number; col: number; };\n * }} TreeViewItem\n *\n * @typedef {{ kind: 'file'; uri: vscode.Uri; file: File }\n *   | { kind: 'manifest'; uri: vscode.Uri; manifest: PackageJson }\n *   | { message: string }\n * } TreeData\n */\n\n/** @param {string} specifier */\nconst isGlobLike = specifier => specifier.includes('*');\n\n/** @implements {vscode.TreeDataProvider<TreeViewItem>} */\nexport class BaseTreeViewProvider {\n  constructor() {\n    this._onDidChangeTreeData = new vscode.EventEmitter();\n    this.onDidChangeTreeData = this._onDidChangeTreeData.event;\n    this.currentUri = undefined;\n    this.currentPosition = undefined;\n    this.statusMessage = undefined;\n    this.treeView = undefined;\n\n    /** @type {undefined | 'file' | 'manifest'}  */\n    this.kind = undefined;\n    /** @type {undefined  | PackageJson} */\n    this.manifest = undefined;\n    /** @type {undefined  | File} */\n    this.file = undefined;\n    /** @type {TreeViewItem[]} */\n    this._rootItems = [];\n  }\n\n  /** @param {vscode.TreeView<vscode.TreeItem>} treeView */\n  setTreeView(treeView) {\n    this.treeView = treeView;\n  }\n\n  /**\n   * @public\n   * @param {TreeViewItem} element\n   */\n  getTreeItem(element) {\n    return element;\n  }\n\n  /**\n   * @param {TreeData | undefined} data\n   * @param {vscode.Position | undefined} position\n   */\n  async refresh(data, position) {\n    this.currentPosition = position;\n    this.statusMessage = undefined;\n    this.currentUri = undefined;\n    this.kind = undefined;\n    this.file = undefined;\n    this.manifest = undefined;\n\n    if (data && 'message' in data) {\n      this.statusMessage = data.message;\n    } else if (data?.kind === 'file') {\n      this.currentUri = data.uri;\n      this.kind = 'file';\n      this.file = data.file;\n    } else if (data?.kind === 'manifest') {\n      this.currentUri = data.uri;\n      this.kind = 'manifest';\n      this.manifest = data.manifest;\n    }\n\n    this._onDidChangeTreeData.fire(null);\n\n    if (this.currentUri) {\n      const workspaceFolder = vscode.workspace.getWorkspaceFolder(this.currentUri);\n      this.workspaceRoot = workspaceFolder?.uri.fsPath;\n    }\n  }\n\n  /** @param {vscode.Position} position */\n  updatePosition(position) {\n    this.currentPosition = position;\n    if (\n      position &&\n      this.kind === 'file' &&\n      this.file &&\n      this.treeView &&\n      typeof this.revealItemAtCursor === 'function'\n    ) {\n      this.revealItemAtCursor(position);\n    }\n  }\n\n  /**\n   * @param {string} label\n   * @param {string} [tooltip]\n   * @returns {TreeViewItem}\n   */\n  createMessageItem(label, tooltip) {\n    const item = new vscode.TreeItem(label, vscode.TreeItemCollapsibleState.None);\n    if (tooltip) {\n      item.tooltip = tooltip;\n    }\n    return /** @type {TreeViewItem} */ (item);\n  }\n\n  /** @param {string} [message] */\n  clear(message) {\n    this.currentUri = undefined;\n    this.currentPosition = null;\n    this.kind = undefined;\n    this.file = undefined;\n    this.manifest = undefined;\n    this.statusMessage = message ?? null;\n    this._onDidChangeTreeData.fire(null);\n  }\n\n  /** @param {vscode.Position} position */\n  async revealItemAtCursor(position) {\n    if (!this.file || !this.treeView) return;\n\n    try {\n      const items = this._rootItems ?? [];\n      for (let i = items.length - 1; i >= 0; i--) {\n        const item = items[i];\n        const node = item._reveal;\n        if (!node) continue;\n        const itemLine = node.line - 1;\n        if (position.line > itemLine || (position.line === itemLine && position.character >= node.col - 1)) {\n          await this.treeView.reveal(item, { select: true, focus: false, expand: 1 });\n          return;\n        }\n      }\n    } catch (_error) {}\n  }\n\n  /**\n   * @public\n   * @param {TreeViewItem | undefined} [element]\n   * @returns {import('vscode').ProviderResult<TreeViewItem[]>}\n   */\n  getChildren(element) {\n    if (element) {\n      if (!element._children) return [];\n      if (element._children[0] instanceof vscode.TreeItem) return element._children;\n      return (element._children ?? []).map(child => this.createTreeViewItems(Object.assign(child, { lazy: true })));\n    }\n\n    if (!this.currentUri) {\n      return this.statusMessage ? [this.createMessageItem(this.statusMessage)] : [];\n    }\n\n    if (this.kind === 'manifest') {\n      this._rootItems = [];\n      return this.getManifestItems();\n    }\n\n    const fileNode = this.file;\n    if (!fileNode) {\n      this._rootItems = [];\n      return [this.createMessageItem(this.statusMessage ?? '(bliep boop bap why are we here?)')];\n    }\n\n    const items = this.getFileItems(fileNode);\n    this._rootItems = Array.isArray(items) ? items : [];\n    return items;\n  }\n\n  /**\n   * @public\n   * @param {TreeViewItem} element\n   * @returns {TreeViewItem | undefined}\n   */\n  getParent(element) {\n    return element?._parent;\n  }\n\n  /** @returns {TreeViewItem[]} */\n  getManifestItems() {\n    throw new Error('subclass impl missing');\n  }\n\n  /**\n   * @param {File} _file\n   * @returns {TreeViewItem[]}\n   */\n  getFileItems(_file) {\n    throw new Error('subclass impl missing');\n  }\n\n  /**\n   * @typedef {{\n   *  label?: string;\n   *  tooltip?: string;\n   *  tooltipChildren?: string;\n   *  description?: string;\n   *  filePath?: string;\n   *  line?: number;\n   *  col?: number;\n   *  importLine?: number;\n   *  importCol?: number;\n   *  icon?: string;\n   *  lazy?: boolean\n   *  children?: Node[];\n   * }} Node;\n   * @param {Node} options\n   * @returns {TreeViewItem}\n   */\n  createTreeViewItems(options) {\n    const { filePath, line, col, importLine, importCol } = options;\n\n    const baseDir =\n      this.kind === 'manifest' && this.currentUri ? path.dirname(this.currentUri.fsPath) : this.workspaceRoot;\n    const absPath = filePath && (path.isAbsolute(filePath) ? filePath : baseDir && path.join(baseDir, filePath));\n\n    const uri = absPath ? vscode.Uri.file(absPath) : undefined;\n    const relPath = absPath\n      ? this.workspaceRoot\n        ? path.relative(this.workspaceRoot, absPath) || path.basename(absPath)\n        : absPath\n      : undefined;\n\n    let command;\n    if (importLine !== undefined && this.currentUri) {\n      command = {\n        command: 'knip.goToPosition',\n        title: 'Go to import',\n        arguments: [this.currentUri, Math.max(importLine - 1, 0), Math.max((importCol ?? 1) - 1, 0)],\n      };\n    } else if (line !== undefined) {\n      const targetUri = uri ?? this.currentUri;\n      if (targetUri) {\n        command = {\n          command: 'knip.goToPosition',\n          title: 'Go to export',\n          arguments: [targetUri, Math.max(line - 1, 0), Math.max((col ?? 1) - 1, 0)],\n        };\n      }\n    } else if (uri) {\n      command = {\n        command: 'vscode.open',\n        title: 'Open File',\n        arguments: [uri],\n      };\n    }\n\n    const children = options.children?.[0]?.filePath\n      ? options.children.toSorted((a, b) => (a.filePath && b.filePath ? a.filePath.localeCompare(b.filePath) : 0))\n      : (options.children ?? []);\n\n    const node = this.createTreeViewItem({\n      label: filePath ? (options.icon ?? '→') : (options.label ?? ''),\n      tooltip: options.tooltip ?? '',\n      description: filePath ? relPath : options.description,\n      command,\n      size: children.length,\n    });\n\n    if (!options.lazy) {\n      for (let i = 0; i < children.length; i++) {\n        if (children[i] instanceof vscode.TreeItem) continue;\n        children[i].tooltip = children[i].tooltip ?? options.tooltipChildren;\n        children[i] = this.createTreeViewItems(children[i]);\n        children[i]._parent = node;\n      }\n    }\n\n    return Object.assign(node, { _children: children });\n  }\n\n  /**\n   * @typedef {{\n   *  label: string;\n   *  tooltip: string;\n   *  description?: string;\n   *  command?: vscode.Command;\n   *  size: number;\n   * }} Options;\n   * @param {Options} options\n   */\n  createTreeViewItem(options) {\n    const state =\n      options.size > 0\n        ? options.size > 10\n          ? vscode.TreeItemCollapsibleState.Collapsed\n          : vscode.TreeItemCollapsibleState.Expanded\n        : vscode.TreeItemCollapsibleState.None;\n    const item = new vscode.TreeItem(options.label, state);\n    item.description = options.description;\n    item.command = options.command;\n    item.tooltip = options.tooltip;\n    return item;\n  }\n}\n\n/**\n * @param {unknown} value\n * @returns {TreeNode[]}\n */\nexport const toTree = value => {\n  if (typeof value === 'string') return isGlobLike(value) ? [{ label: value }] : [{ filePath: value }];\n  if (value === null) return [{ label: '!value' }];\n  if (Array.isArray(value)) return value.map((entry, index) => ({ label: `[${index}]`, children: toTree(entry) }));\n  if (typeof value !== 'object') return [{ label: String(value) }];\n  const record = /** @type {Record<string, unknown>} */ (value);\n  return Object.keys(record)\n    .sort()\n    .map(key => ({ label: key, children: toTree(record[key]) }));\n};\n"
  },
  {
    "path": "packages/vscode-knip/src/tree-view-exports.js",
    "content": "import { BaseTreeViewProvider, toTree } from './tree-view-base.js';\n\nconst CONTENTION_ICONS = {\n  contention: '►',\n  branching: '◇',\n  conflict: '‼︎',\n  unused: '',\n};\n\nconst CONTENTION_TOOLTIPS = {\n  branching: 'Contention: branch location',\n  conflict: 'Contention: conflict location',\n};\n\n/**\n * @extends {BaseTreeViewProvider}\n */\nexport class ExportsTreeViewProvider extends BaseTreeViewProvider {\n  /** @param {import('knip/session').File} fileNode */\n  getFileItems(fileNode) {\n    const isDeferChildren = fileNode.exports.length > 9;\n    const nodes = fileNode.exports.map(_export => this.createExportNode(_export, isDeferChildren, fileNode.contention));\n    if (nodes.length === 0) return [this.createMessageItem('(none)')];\n    return nodes;\n  }\n\n  /**\n   * @param {import('knip/session').Export} _export\n   * @param {boolean} [isDeferChildren]\n   * @param {Record<string, import('knip/session').ContentionDetails>} [contention]\n   */\n  createExportNode(_export, isDeferChildren, contention) {\n    const contentionDetails = contention?.[_export.identifier];\n    const contentionItems = this.createContentionDescriptors(contentionDetails).map(d => this.createTreeViewItems(d));\n\n    const children =\n      _export.exports && _export.exports.length > 0\n        ? [..._export.exports.map(e => this.createExportNode(e, isDeferChildren, contention)), ...contentionItems]\n        : [\n            ...(_export.importLocations ?? []).map(location => ({\n              filePath: location.filePath,\n              line: location.line,\n              col: location.col,\n              icon: '→',\n            })),\n            ...contentionItems,\n          ];\n\n    const size = _export.importLocations.length;\n    const branchingCount = contentionDetails?.branching.length ?? 0;\n    const conflictCount = contentionDetails?.conflict.length ?? 0;\n    const contentionCount = branchingCount + conflictCount;\n    const descriptionParts = [`imported ${size}x`];\n    if (contentionCount > 0) descriptionParts.push(`contention ${contentionCount}x`);\n    const node = this.createTreeViewItems({\n      label: _export.identifier,\n      tooltip: 'Go to export location',\n      tooltipChildren: 'Go to usage or contention location',\n      line: _export.line,\n      col: _export.col,\n      description: `${contentionCount > 0 ? CONTENTION_ICONS.contention : size > 0 ? '' : CONTENTION_ICONS.unused} ${descriptionParts.join(' / ')}`,\n      children,\n      lazy: isDeferChildren,\n    });\n    node._reveal = { identifier: _export.identifier, line: _export.line, col: _export.col };\n    return node;\n  }\n\n  /** @param {import('knip/session').ContentionDetails} [contentionDetails] */\n  createContentionDescriptors(contentionDetails) {\n    if (!contentionDetails) return [];\n    /** @type {{ filePath: string; icon: string; tooltip: string }[]} */\n    const descriptors = [];\n    /** @param {'branching' | 'conflict'} type @param {string[]} [filePaths] */\n    const pushDescriptors = (type, filePaths) => {\n      if (!filePaths) return;\n      for (const filePath of filePaths) {\n        descriptors.push({ filePath, icon: CONTENTION_ICONS[type], tooltip: CONTENTION_TOOLTIPS[type] });\n      }\n    };\n    pushDescriptors('branching', contentionDetails.branching);\n    pushDescriptors('conflict', contentionDetails.conflict);\n    return descriptors;\n  }\n\n  /**\n   * @returns {import('vscode').TreeItem[]}\n   */\n  getManifestItems() {\n    const manifest = this.manifest;\n    if (!manifest) {\n      return [this.createMessageItem('Error parsing package.json')];\n    }\n    const nodes = [];\n\n    const stringFields = ['main', 'module', 'browser', 'types', 'typings'];\n    for (const field of stringFields) {\n      if (typeof manifest[field] === 'string') {\n        nodes.push(this.createTreeViewItems({ label: field, children: [{ filePath: manifest[field] }] }));\n      }\n    }\n\n    if (typeof manifest.bin === 'string') {\n      nodes.push(this.createTreeViewItems({ label: 'bin', children: [{ filePath: manifest.bin }] }));\n    } else if (manifest.bin && typeof manifest.bin === 'object') {\n      const children = Object.entries(manifest.bin)\n        .filter(([, value]) => typeof value === 'string')\n        .map(([name, value]) => ({ label: name, children: [{ filePath: value }] }));\n      if (children.length > 0) nodes.push(this.createTreeViewItems({ label: 'bin', children }));\n    }\n\n    if (manifest.exports && typeof manifest.exports === 'object') {\n      for (const [specifier, exportValue] of Object.entries(manifest.exports)) {\n        if (typeof exportValue === 'string') {\n          nodes.push(this.createTreeViewItems({ label: specifier, children: [{ filePath: exportValue }] }));\n        } else if (exportValue === null) {\n          nodes.push(this.createTreeViewItems({ label: specifier, children: [{ label: `!${specifier}` }] }));\n        } else if (typeof exportValue === 'object') {\n          const children = toTree(exportValue);\n          nodes.push(this.createTreeViewItems({ label: specifier, children }));\n        }\n      }\n    }\n\n    if (nodes.length === 0) return [this.createMessageItem('(none)')];\n\n    return nodes;\n  }\n}\n"
  },
  {
    "path": "packages/vscode-knip/src/tree-view-imports.js",
    "content": "import path from 'node:path';\nimport { SIDE_EFFECTS } from 'knip/session';\nimport { BaseTreeViewProvider, toTree } from './tree-view-base.js';\n\n/**\n * @extends {BaseTreeViewProvider}\n */\nexport class ImportsTreeViewProvider extends BaseTreeViewProvider {\n  /** @param {import('knip/session').File} fileNode */\n  getFileItems(fileNode) {\n    if (fileNode.internalImports.length === 0) return [this.createMessageItem('(none)')];\n\n    const nodes = [];\n    for (const _import of fileNode.internalImports) {\n      const cycle = fileNode.cycles.find(cycle => cycle[1] === _import.filePath);\n      let tooltip = 'Go to import location';\n      if (cycle) {\n        const cyclePaths = cycle.map(p => (this.workspaceRoot ? path.relative(this.workspaceRoot, p) : p));\n        tooltip = `Circular dependency\\n\\n${cyclePaths.join('\\n')}`;\n      }\n      const isSideEffect = _import.identifier === SIDE_EFFECTS;\n      const node = this.createTreeViewItems({\n        label: isSideEffect ? '(side effect)' : _import.identifier,\n        description: cycle ? '↻' : undefined,\n        tooltip,\n        tooltipChildren: 'Go to implementation',\n        importLine: _import.importLine,\n        importCol: _import.importCol,\n        children: [{ filePath: _import.filePath, col: _import.col, line: _import.line }],\n      });\n      node._reveal = { identifier: _import.identifier, line: _import.importLine, col: _import.importCol };\n      nodes.push(node);\n    }\n    return nodes;\n  }\n\n  getManifestItems(manifest = this.manifest) {\n    if (!manifest) return [this.createMessageItem('Error parsing package.json')];\n\n    const nodes = [];\n\n    if (manifest.imports && typeof manifest.imports === 'object') {\n      for (const [specifier, importValue] of Object.entries(manifest.imports)) {\n        if (!specifier.startsWith('#')) continue;\n        if (typeof importValue === 'string') {\n          nodes.push(this.createTreeViewItems({ label: specifier, children: [{ filePath: importValue }] }));\n        } else if (importValue === null) {\n          nodes.push(this.createTreeViewItems({ label: specifier, children: [{ label: `!${specifier}` }] }));\n        } else if (typeof importValue === 'object') {\n          const children = toTree(importValue);\n          nodes.push(this.createTreeViewItems({ label: specifier, children }));\n        }\n      }\n    }\n\n    if (nodes.length === 0) {\n      return [this.createMessageItem('(none)')];\n    }\n\n    return nodes;\n  }\n}\n"
  },
  {
    "path": "packages/vscode-knip/test/extension.test.mjs",
    "content": "import assert from 'node:assert/strict';\nimport * as vscode from 'vscode';\n\nexport async function extensionActivates() {\n  const ext = vscode.extensions.getExtension('webpro.vscode-knip');\n  assert.ok(ext, 'Extension should be present');\n  await ext.activate();\n  assert.ok(ext.isActive, 'Extension should be active');\n}\n\nexport async function knipRestartCommandExists() {\n  const commands = await vscode.commands.getCommands(true);\n  assert.ok(commands.includes('knip.restart'), 'knip.restart command should exist');\n}\n"
  },
  {
    "path": "packages/vscode-knip/test/index.mjs",
    "content": "import { readdirSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nexport async function run() {\n  const results = { passed: 0, failed: 0 };\n\n  for (const file of readdirSync(__dirname)) {\n    if (!file.endsWith('.test.mjs')) continue;\n    for (const [name, fn] of Object.entries(await import(resolve(__dirname, file)))) {\n      if (typeof fn !== 'function') continue;\n      try {\n        await fn();\n        console.log(`✓ ${name}`);\n        results.passed++;\n      } catch (err) {\n        console.error(`✗ ${name}:`, err.message);\n        results.failed++;\n      }\n    }\n  }\n\n  console.log(`\\n${results.passed} passed, ${results.failed} failed`);\n  if (results.failed) throw new Error(`${results.failed} tests failed`);\n}\n"
  },
  {
    "path": "packages/vscode-knip/test/run.mjs",
    "content": "import { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { runTests } from '@vscode/test-electron';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nasync function main() {\n  await runTests({\n    extensionDevelopmentPath: resolve(__dirname, '..', 'dist'),\n    extensionTestsPath: resolve(__dirname, 'index.mjs'),\n    launchArgs: ['--disable-extensions'],\n  });\n}\n\nmain().catch(err => {\n  console.error('Failed to run tests:', err);\n  process.exit(1);\n});\n"
  },
  {
    "path": "packages/vscode-knip/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"checkJs\": true,\n    \"module\": \"nodenext\",\n    \"noEmit\": true,\n    \"resolveJsonModule\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"target\": \"esnext\"\n  }\n}\n"
  },
  {
    "path": "pnpm-workspace.yaml",
    "content": "packages:\n  - 'packages/*'\n  - '!packages/knip/fixtures'\n"
  },
  {
    "path": "release.sh",
    "content": "#!/usr/bin/env bash\nset -e\n\ncurl -sf -H \"Authorization: token $GITHUB_TOKEN\" https://api.github.com/user > /dev/null || { echo 'No or invalid GITHUB_TOKEN'; exit 0; }\nvsce verify-pat webpro\novsx verify-pat webpro\nnpm login\n\n# Running the release cycle twice to ensure a single clean commit with multiple tags will be pushed,\n# with manual bump/skip for each package separately. One-off package publish can still be done from package dir.\n\n# Start with publish and create release commit for knip core package\npnpm run --dir packages/knip release --no-git.tag --no-git.push --no-github.release\n\n# Bump and publish other packages\nBUMP_AND_PUBLISH=(--no-git.changelog --no-git.commitMessage --no-git.tag --no-git.push '--git.commitArgs=--amend --no-edit')\npnpm run --dir packages/create-config release \"${BUMP_AND_PUBLISH[@]}\"\npnpm run --dir packages/language-server release \"${BUMP_AND_PUBLISH[@]}\"\npnpm run --dir packages/mcp-server release \"${BUMP_AND_PUBLISH[@]}\"\npnpm run --dir packages/vscode-knip release \"${BUMP_AND_PUBLISH[@]}\"\n\n# Tag other packages (this is why we run the show twice: git-amend + git-tag is moving target but we want to keep it clean)\nTAG=(--no-git.changelog --no-increment --no-git.commit --no-git.push)\npnpm run --dir packages/create-config release \"${TAG[@]}\" --no-npm.publish\npnpm run --dir packages/language-server release \"${TAG[@]}\" --no-npm.publish\npnpm run --dir packages/mcp-server release \"${TAG[@]}\" --no-npm.publish\npnpm run --dir packages/vscode-knip release \"${TAG[@]}\" --no-hooks\n\n# End with core knip package to push & release,\npnpm run --dir packages/knip release --no-increment --no-npm.publish --no-git.commit --no-hooks\n"
  },
  {
    "path": "templates/demo/monorepo/.gitignore",
    "content": "dist\nnode_modules\ntsconfig.tsbuildinfo\n"
  },
  {
    "path": "templates/demo/monorepo/knip.ts",
    "content": "/** @type {import('knip').KnipConfig} */\nconst config = {\n  workspaces: {\n    'packages/shared': {\n      includeEntryExports: true,\n    },\n  },\n};\n\nexport default config;\n"
  },
  {
    "path": "templates/demo/monorepo/package.json",
    "content": "{\n  \"name\": \"@knip/templates-monorepo\",\n  \"version\": \"1.0.0\",\n  \"private\": true,\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"devDependencies\": {\n    \"@types/node\": \"^20.14.8\",\n    \"knip\": \"^5.30.1\",\n    \"typescript\": \"^5.5.4\"\n  }\n}\n"
  },
  {
    "path": "templates/demo/monorepo/packages/client/package.json",
    "content": "{\n  \"name\": \"@monorepo/client\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"main\": \"src/index.ts\",\n  \"dependencies\": {\n    \"@monorepo/shared\": \"*\"\n  }\n}\n"
  },
  {
    "path": "templates/demo/monorepo/packages/client/src/index.ts",
    "content": "import { someFunction, usedFunction } from '@monorepo/shared';\n\nusedFunction;\nsomeFunction;\n"
  },
  {
    "path": "templates/demo/monorepo/packages/client/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"declaration\": true\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"dist/**/*\"]\n}\n"
  },
  {
    "path": "templates/demo/monorepo/packages/server/package.json",
    "content": "{\n  \"name\": \"@monorepo/server\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"main\": \"src/index.ts\",\n  \"dependencies\": {\n    \"@monorepo/shared\": \"*\"\n  }\n}\n"
  },
  {
    "path": "templates/demo/monorepo/packages/server/src/index.ts",
    "content": "import { usedFunction } from '@monorepo/shared';\n\nusedFunction;\n"
  },
  {
    "path": "templates/demo/monorepo/packages/server/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"declaration\": true\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"dist/**/*\"]\n}\n"
  },
  {
    "path": "templates/demo/monorepo/packages/shared/package.json",
    "content": "{\n  \"name\": \"@monorepo/shared\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"main\": \"src/index.ts\"\n}\n"
  },
  {
    "path": "templates/demo/monorepo/packages/shared/src/exports.ts",
    "content": "export const someFunction = () => 'bar';\n"
  },
  {
    "path": "templates/demo/monorepo/packages/shared/src/import-numbers.ts",
    "content": "import { NUMBERS, ONE } from './numbers.js';\n\nONE;\n\nNUMBERS.FOUR;\n"
  },
  {
    "path": "templates/demo/monorepo/packages/shared/src/index.ts",
    "content": "import './import-numbers.js';\n\nexport * from './exports.js';\nexport { usedFunction } from './used-fn.js';\n"
  },
  {
    "path": "templates/demo/monorepo/packages/shared/src/numbers.ts",
    "content": "const ONE = 1;\nconst TWO = 2;\n\nexport { ONE, TWO };\n\nexport const THREE = ONE + TWO;\n\nexport enum NUMBERS {\n  FOUR = 4,\n  FIVE = 5,\n}\n"
  },
  {
    "path": "templates/demo/monorepo/packages/shared/src/used-fn.ts",
    "content": "export const usedFunction = () => 'bar';\n"
  },
  {
    "path": "templates/demo/monorepo/packages/shared/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"declaration\": true\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"dist/**/*\"]\n}\n"
  },
  {
    "path": "templates/demo/monorepo/tsconfig.json",
    "content": "{\n  \"files\": [],\n  \"compilerOptions\": {\n    \"allowSyntheticDefaultImports\": true,\n    \"declaration\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"module\": \"nodenext\",\n    \"moduleResolution\": \"nodenext\",\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"target\": \"ES2022\"\n  },\n  \"references\": [\n    { \"path\": \"packages/client\" },\n    { \"path\": \"packages/server\" },\n    { \"path\": \"packages/shared\" }\n  ]\n}\n"
  },
  {
    "path": "templates/demo/script/demo.lua",
    "content": "local home = os.getenv(\"HOME\")\nlocal knip = home .. \"/p/knip/knip\"\nlocal demo = knip .. \"/templates/demo/monorepo\"\n\nlocal screen = hs.screen.allScreens()[1]\nlocal stageEnterDelay = 1;\nlocal stageExitDelay = 0.5;\nlocal typeSpeedNormal = 0.05;\nlocal typeSpeedFast = 0.01;\n\n-- Keys\n\nlocal function pressReturn()\n  hs.eventtap.keyStroke({}, \"return\")\nend\n\nlocal function pressDown()\n  hs.eventtap.keyStroke({}, \"down\")\nend\n\nlocal function saveFile()\n  hs.eventtap.keyStroke({ \"cmd\" }, \"S\")\nend\n\nlocal function stopRecording()\n  hs.eventtap.keyStroke({ \"ctrl\", \"cmd\" }, \"escape\")\nend\n\n-- Generic helpers\n\nlocal function runInSequence(tasks)\n  local index = 1\n  local function runNextTask()\n    if index <= #tasks then\n      local currentTask = tasks[index]\n      index = index + 1\n      currentTask(runNextTask)\n    end\n  end\n  runNextTask()\nend\n\nlocal function typeText(text, speed, callback)\n  local index = 1\n  hs.timer.doUntil(function()\n    return index > #text\n  end, function()\n    hs.eventtap.keyStrokes(text:sub(index, index))\n    index = index + 1\n    if index > #text and callback then\n      hs.timer.doAfter(0.3, function()\n        pressReturn()\n        callback()\n      end)\n    end\n  end, speed)\nend\n\n-- Stage helpers\n\nlocal function showMessage(text)\n  local id = hs.alert.show(text, screen, s2, 20)\n  return function()\n    hs.alert.closeSpecific(id, 2)\n  end\nend\n\nlocal function leaveStage(close, done)\n  hs.timer.doAfter(stageExitDelay, function()\n    close()\n    hs.timer.doAfter(stageExitDelay, function()\n      done()\n    end)\n  end)\nend\n\nlocal function enterStage(done, text, fn)\n  local close = showMessage(text)\n  hs.timer.doAfter(stageEnterDelay, function()\n    if fn then\n      fn(function()\n        leaveStage(close, done);\n      end)\n    else\n      leaveStage(close, done);\n    end\n  end)\nend\n\n-- VS Code helpers\n\nlocal function toFileStart()\n  hs.eventtap.keyStroke({ \"cmd\" }, \"up\")\nend\n\nlocal function goToTerminal()\n  hs.eventtap.keyStroke({ \"ctrl\" }, \"`\")\nend\n\nfunction openVSCodeWithFile(filePath, done)\n  hs.execute(\"code \" .. filePath, true)\n  hs.timer.doAfter(stageEnterDelay, done);\nend\n\n-- Stages\n\nlocal function init(done)\n  hs.execute(\"code \", true)\n  hs.eventtap.keyStroke({ \"cmd\", \"shift\" }, \"0\")\n  hs.eventtap.keyStroke({ \"cmd\" }, \"=\")\n  hs.eventtap.keyStroke({ \"cmd\" }, \"=\")\n  hs.eventtap.keyStroke({ \"cmd\" }, \"=\")\n  enterStage(done, \"✂️ demo\\n\\nknip --watch\\nknip --fix\", function(cb)\n    openVSCodeWithFile(demo .. \"/packages/shared/src/exports.ts\", cb);\n  end)\nend\n\nlocal function startWatcher(done)\n  enterStage(done, \"First up: start the watcher\", function(cb)\n    goToTerminal()\n    hs.timer.doAfter(stageEnterDelay, function()\n      typeText(\"knip --watch\", typeSpeedNormal, cb)\n    end);\n  end)\nend\n\nlocal function addUnusedExports(done)\n  enterStage(done, \"Excellent, let's add some unused exports...\", function(cb)\n    openVSCodeWithFile(\n      demo .. \"/packages/shared/src/exports.ts\",\n      function()\n        pressReturn()\n        typeText(\"export const UNUSED_EXPORT = 1;\", typeSpeedFast, function()\n          saveFile();\n          pressReturn()\n          typeText(\"export interface UNUSED_INTERFACE {}\", typeSpeedFast, function()\n            saveFile()\n            cb()\n          end)\n        end)\n      end)\n  end)\nend\n\nlocal function addUnlistedDependency(done)\n  enterStage(done, \"Import an unlisted dependency...\", function(cb)\n    openVSCodeWithFile(\n      demo .. \"/packages/server/src/index.ts\",\n      function()\n        toFileStart();\n        pressDown()\n        saveFile();\n        typeText(\"import { something } from 'not-in-package-json';\", typeSpeedFast, function()\n          saveFile();\n          cb()\n        end)\n      end)\n  end)\nend\n\nlocal function undoUnlistedDependency(done)\n  enterStage(done, \"...and get rid of it\", function(cb)\n    toFileStart();\n    pressDown();\n    hs.eventtap.keyStroke({ \"cmd\", \"shift\" }, \"K\")\n    saveFile();\n    cb()\n  end)\nend\n\nlocal function splitTerminal(done)\n  goToTerminal()\n  hs.eventtap.keyStroke({ \"cmd\" }, \"\\\\\")\n  hs.timer.doAfter(stageEnterDelay, done);\nend\n\nlocal function createFiles(done)\n  enterStage(done, \"Let's create some new files\", function(cb)\n    typeText(\"touch packages/client/unused-file.js\", typeSpeedFast, function()\n      typeText(\"touch packages/server/unused-as-well.js\", typeSpeedFast, cb)\n    end)\n  end)\nend\n\nlocal function messageUnusedFiles(done)\n  enterStage(done, \"Now we have some unused files and exports...\", function(cb)\n    openVSCodeWithFile(\n      demo .. \"/packages/server/src/index.ts\",\n      function()\n        goToTerminal()\n        cb()\n      end)\n  end)\nend\n\nlocal function backToFirstFile(done)\n  enterStage(done, \"Let's go back to the other package in the monorepo...\", function(cb)\n    openVSCodeWithFile(demo .. \"/packages/shared/src/exports.ts\", cb)\n  end)\nend\n\nlocal function cleanupUsingAutoFix(done)\n  enterStage(done, \"...and clean this up! ✂️\", function(cb)\n    goToTerminal()\n    pressReturn()\n    typeText(\"knip --fix --allow-remove-files\", typeSpeedNormal, function()\n      hs.timer.doAfter(stageEnterDelay, function()\n        hs.eventtap.keyStroke({ \"option\" }, \"Z\")\n        cb();\n      end);\n    end);\n  end);\nend\n\nlocal function endMessage(done)\n  enterStage(done, \"That's it, thanks for watching!\")\nend\n\nlocal function stop(done)\n  hs.timer.doAfter(stageEnterDelay, function()\n    stopRecording()\n    done();\n  end);\nend\n\n-- Set & run stages\n\nlocal stages = {\n  init,\n  startWatcher,\n  addUnusedExports,\n  addUnlistedDependency,\n  undoUnlistedDependency,\n  splitTerminal,\n  createFiles,\n  messageUnusedFiles,\n  backToFirstFile,\n  cleanupUsingAutoFix,\n  endMessage,\n  stop\n}\n\nrunInSequence(stages)\n"
  },
  {
    "path": "templates/issue-reproduction/basic/.gitignore",
    "content": "dist\n"
  },
  {
    "path": "templates/issue-reproduction/basic/README.md",
    "content": "# Knip Minimal Reproduction\n\nThis is a template with only Knip (and TypeScript) pre-installed.\n\nAdd files and configuration to this project to create a minimal reproduction of\nthe issue:\n\n- Add source files\n- Add relevant TypeScript configuration to `tsconfig.json`\n- Add relevant Knip configuration to `knip.json` (or create `knip.ts`)\n- Install and double-check the latest versions of Knip and TypeScript\n\nUse the terminal inside this project to run Knip from the root of the project\nwith `npx knip`.\n"
  },
  {
    "path": "templates/issue-reproduction/basic/index.ts",
    "content": ""
  },
  {
    "path": "templates/issue-reproduction/basic/knip.json",
    "content": "{\n  \"$schema\": \"https://unpkg.com/knip@6/schema.json\"\n}\n"
  },
  {
    "path": "templates/issue-reproduction/basic/package.json",
    "content": "{\n  \"name\": \"@knip/templates-basic\",\n  \"version\": \"1.0.0\",\n  \"private\": true,\n  \"description\": \"Knip case reproduction template - basic\",\n  \"homepage\": \"https://knip.dev\",\n  \"bugs\": \"https://github.com/webpro-nl/knip/issues\",\n  \"scripts\": {\n    \"build\": \"tsc --build\",\n    \"type-check\": \"tsc --build --noEmit\",\n    \"knip\": \"knip\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"*\",\n    \"knip\": \"*\",\n    \"typescript\": \"*\"\n  }\n}\n"
  },
  {
    "path": "templates/issue-reproduction/basic/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {}\n}\n"
  },
  {
    "path": "templates/issue-reproduction/monorepo/.gitignore",
    "content": "dist\n"
  },
  {
    "path": "templates/issue-reproduction/monorepo/README.md",
    "content": "# Knip Minimal Reproduction\n\nThis is a template with only Knip (and TypeScript) pre-installed.\n\nAdd files and configuration to this project to create a minimal reproduction of\nthe issue:\n\n- Add source files\n- Add relevant TypeScript configuration to `tsconfig.json`\n- Add relevant Knip configuration to `knip.json` (or create `knip.ts`)\n- Install and double-check the latest versions of Knip and TypeScript\n\nUse the terminal inside this project to run Knip from the root of the project\nwith `npx knip`.\n"
  },
  {
    "path": "templates/issue-reproduction/monorepo/knip.json",
    "content": "{\n  \"$schema\": \"https://unpkg.com/knip@6/schema.json\"\n}\n"
  },
  {
    "path": "templates/issue-reproduction/monorepo/package.json",
    "content": "{\n  \"name\": \"@knip/templates-monorepo\",\n  \"version\": \"1.0.0\",\n  \"private\": true,\n  \"description\": \"Knip case reproduction template - monorepo\",\n  \"homepage\": \"https://knip.dev\",\n  \"bugs\": \"https://github.com/webpro-nl/knip/issues\",\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"scripts\": {\n    \"build\": \"tsc --build\",\n    \"type-check\": \"tsc --build --noEmit\",\n    \"knip\": \"knip\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"*\",\n    \"knip\": \"*\",\n    \"typescript\": \"*\"\n  }\n}\n"
  },
  {
    "path": "templates/issue-reproduction/monorepo/packages/client/package.json",
    "content": "{\n  \"name\": \"@monorepo/client\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"main\": \"src/index.ts\",\n  \"dependencies\": {\n    \"@monorepo/lib\": \"*\"\n  }\n}\n"
  },
  {
    "path": "templates/issue-reproduction/monorepo/packages/client/src/index.ts",
    "content": "import { sharedFunction } from '@monorepo/lib';\n\nsharedFunction();\n"
  },
  {
    "path": "templates/issue-reproduction/monorepo/packages/client/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"declaration\": true\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"dist/**/*\"]\n}\n"
  },
  {
    "path": "templates/issue-reproduction/monorepo/packages/lib/package.json",
    "content": "{\n  \"name\": \"@monorepo/lib\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"main\": \"src/index.ts\"\n}\n"
  },
  {
    "path": "templates/issue-reproduction/monorepo/packages/lib/src/index.ts",
    "content": "export const sharedFunction = () => {};\n"
  },
  {
    "path": "templates/issue-reproduction/monorepo/packages/lib/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"declaration\": true\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"dist/**/*\"]\n}\n"
  },
  {
    "path": "templates/issue-reproduction/monorepo/tsconfig.json",
    "content": "{\n  \"files\": [],\n  \"compilerOptions\": {\n    \"allowSyntheticDefaultImports\": true,\n    \"declaration\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"module\": \"nodenext\",\n    \"moduleResolution\": \"nodenext\",\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"target\": \"ES2022\"\n  },\n  \"references\": [{ \"path\": \"packages/client\" }, { \"path\": \"packages/lib\" }]\n}\n"
  },
  {
    "path": "templates/language-server-client/.gitignore",
    "content": "node_modules\n"
  },
  {
    "path": "templates/language-server-client/knip.json",
    "content": "{\n  \"workspaces\": {\n    \"packages/shared\": {\n      \"includeEntryExports\": true\n    }\n  }\n}\n"
  },
  {
    "path": "templates/language-server-client/package.json",
    "content": "{\n  \"name\": \"@knip-ls/root\",\n  \"workspaces\": {\n    \"packages\": [\n      \"packages/*\"\n    ]\n  }\n}\n"
  },
  {
    "path": "templates/language-server-client/packages/client/package.json",
    "content": "{\n  \"name\": \"@knip-ls/client\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"main\": \"src/index.ts\",\n  \"scripts\": {\n    \"blend\": \"node ./src/blend.js\",\n    \"pick\": \"node ./src/pick.js\",\n    \"reap\": \"node ./src/reap.js\"\n  },\n  \"dependencies\": {\n    \"@knip-ls/shared\": \"workspace:*\"\n  }\n}\n"
  },
  {
    "path": "templates/language-server-client/packages/client/src/blend.js",
    "content": "import { FRUITS } from './index.js';\n\n[FRUITS];\n"
  },
  {
    "path": "templates/language-server-client/packages/client/src/index.ts",
    "content": "import * as CONSTANTS from '@knip-ls/shared';\nimport { multiply } from '@knip-ls/shared';\n\nCONSTANTS.TEXT;\n\nmultiply(1, 2);\n\nconst [{ APPLE }, { BANANA }, { CHERRY }] = await Promise.all([\n  import('@knip-ls/shared/fruits'),\n  import('@knip-ls/shared/fruits'),\n  import('@knip-ls/shared/fruits'),\n]);\n\nexport const FRUITS = [APPLE, BANANA, CHERRY];\n"
  },
  {
    "path": "templates/language-server-client/packages/client/src/pick.js",
    "content": "import { FRUITS } from './index.js';\n\nconst healthy = FRUITS;\n"
  },
  {
    "path": "templates/language-server-client/packages/client/src/reap.js",
    "content": "import { FRUITS } from './index.js';\n\nconsole.log(FRUITS);\n"
  },
  {
    "path": "templates/language-server-client/packages/client/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"rootDir\": \"src\",\n    \"declaration\": true\n  },\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "templates/language-server-client/packages/server/package.json",
    "content": "{\n  \"name\": \"@knip-ls/server\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"main\": \"src/index.ts\",\n  \"dependencies\": {\n    \"@knip-ls/shared\": \"workspace:*\"\n  }\n}\n"
  },
  {
    "path": "templates/language-server-client/packages/server/src/branch-definition.ts",
    "content": "export const DIAMOND = 1;\n"
  },
  {
    "path": "templates/language-server-client/packages/server/src/branch-left.ts",
    "content": "export * from './branch-definition.ts';\n"
  },
  {
    "path": "templates/language-server-client/packages/server/src/branch-right.ts",
    "content": "export * from './branch-definition.ts';\n"
  },
  {
    "path": "templates/language-server-client/packages/server/src/branch.ts",
    "content": "export * from './branch-left.ts';\nexport * from './branch-right.ts';\n"
  },
  {
    "path": "templates/language-server-client/packages/server/src/circular-a.ts",
    "content": "import './circular-b.ts';\n\nexport const A = 1;\n"
  },
  {
    "path": "templates/language-server-client/packages/server/src/circular-b.ts",
    "content": "import './circular-c.ts';\n"
  },
  {
    "path": "templates/language-server-client/packages/server/src/circular-c.ts",
    "content": "import { A } from './circular-a.ts';\n\nA;\n"
  },
  {
    "path": "templates/language-server-client/packages/server/src/conflict-1.ts",
    "content": "export const CONFLICT = 1;\n"
  },
  {
    "path": "templates/language-server-client/packages/server/src/conflict-2.ts",
    "content": "export const CONFLICT = 2;\n"
  },
  {
    "path": "templates/language-server-client/packages/server/src/conflict-3.ts",
    "content": "export const CONFLICT = 3;\n"
  },
  {
    "path": "templates/language-server-client/packages/server/src/index.ts",
    "content": "import { multiply } from '@knip-ls/shared';\nimport './circular-a.ts';\nimport { DIAMOND } from './branch.ts';\nimport { OVERLOAD } from './overload-1.ts';\n\nexport * from './conflict-1.ts';\nexport * from './conflict-2.ts';\nexport * from './conflict-3.ts';\n\nmultiply;\nDIAMOND;\nOVERLOAD;\n"
  },
  {
    "path": "templates/language-server-client/packages/server/src/overload-1.ts",
    "content": "export const OVERLOAD = 1;\n\nexport * from './overload-2.ts';\n"
  },
  {
    "path": "templates/language-server-client/packages/server/src/overload-2.ts",
    "content": "export const OVERLOAD = 2;\n\nexport * from './overload-3.ts';\n"
  },
  {
    "path": "templates/language-server-client/packages/server/src/overload-3.ts",
    "content": "export const OVERLOAD = 3;\n"
  },
  {
    "path": "templates/language-server-client/packages/server/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"rootDir\": \"src\",\n    \"declaration\": true,\n    \"noEmit\": true,\n    \"allowImportingTsExtensions\": true\n  },\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "templates/language-server-client/packages/shared/package.json",
    "content": "{\n  \"name\": \"@knip-ls/shared\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": \"./src/index.ts\",\n    \"./fruits\": \"./src/fruits.ts\"\n  },\n  \"dependencies\": {\n    \"some-package\": \"*\"\n  }\n}\n"
  },
  {
    "path": "templates/language-server-client/packages/shared/src/constants.ts",
    "content": "export const DESCRIPTION = 'unused';\n\nexport const TEXT = 'used';\n\nconst FIRST = 1;\nconst SECOND = 2;\n\nexport { FIRST, SECOND };\n"
  },
  {
    "path": "templates/language-server-client/packages/shared/src/fruits.ts",
    "content": "export const APPLE = 'A';\nexport const BANANA = 'B';\nexport const CHERRY = 'C';\nexport const DATE = 'D';\nexport const ELDERBERRY = 'E';\n"
  },
  {
    "path": "templates/language-server-client/packages/shared/src/helpers.ts",
    "content": "import { CHERRY } from './fruits.js';\n\nexport const multiply = (value: number, multiplier: number) => value * multiplier;\n\nCHERRY;\n"
  },
  {
    "path": "templates/language-server-client/packages/shared/src/index.ts",
    "content": "export * from './constants.js';\nexport * as FRUITS from './fruits.js';\nexport * from './helpers.js';\n"
  },
  {
    "path": "templates/language-server-client/packages/shared/src/unused.ts",
    "content": ""
  },
  {
    "path": "templates/language-server-client/packages/shared/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"rootDir\": \"src\",\n    \"declaration\": true\n  },\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "templates/language-server-client/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"allowSyntheticDefaultImports\": true,\n    \"declaration\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"module\": \"preserve\",\n    \"moduleResolution\": \"bundler\",\n    \"rootDir\": \"src\",\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"target\": \"ES2022\"\n  },\n  \"files\": [],\n  \"references\": [{ \"path\": \"packages/client\" }, { \"path\": \"packages/server\" }, { \"path\": \"packages/shared\" }]\n}\n"
  },
  {
    "path": "templates/playground/basic/README.md",
    "content": "# Knip Playground (basic)\n\nWelcome to this Knip playground. There are issues on purpose in this codebase\nbelow so Knip will report various types of issues.\n\nFeel free to (fork this project and) play around!\n\nRun `npm run knip` on this codebase in the terminal below and Knip will report\nthe following issues:\n\n```\n$ npm run knip\nUnused files (1)\nclutter.ts\nUnlisted dependencies (1)\nlodash  util.ts\nUnused exports (1)\nunusedFunction  unknown  util.ts:6:14\n```\n\n- `clutter.ts` is an unused file, since it's not imported by any of the others\n- `lodash` is an unlisted dependency, because it's used in `util.ts`, but not\n  listed in `package.json`\n- `unusedFunction` is exported from `util.ts` but it's not imported anywhere, so\n  it's reported as an unused export.\n"
  },
  {
    "path": "templates/playground/basic/clutter.ts",
    "content": "export const clutter = true;\n"
  },
  {
    "path": "templates/playground/basic/index.ts",
    "content": "import { used } from './util.js';\n\nused;\n"
  },
  {
    "path": "templates/playground/basic/package.json",
    "content": "{\n  \"name\": \"@knip/playground-basic\",\n  \"version\": \"1.0.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"knip\": \"knip\"\n  },\n  \"dependencies\": {\n    \"picocolors\": \"^1.1.0\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.14.8\",\n    \"knip\": \"^5.30.1\",\n    \"typescript\": \"^5.5.4\"\n  }\n}\n"
  },
  {
    "path": "templates/playground/basic/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {}\n}\n"
  },
  {
    "path": "templates/playground/basic/util.ts",
    "content": "import _ from 'lodash';\nimport pc from 'picocolors';\n\nexport const used = () => pc.blue('Hello');\n\nexport const unusedFunction = () => _.random();\n"
  },
  {
    "path": "templates/playground/monorepo/.gitignore",
    "content": "dist\nnode_modules\ntsconfig.tsbuildinfo\n"
  },
  {
    "path": "templates/playground/monorepo/README.md",
    "content": "# Knip Playground (monorepo)\n\nWelcome to this Knip playground. There are issues on purpose in this codebase\nbelow so Knip will report various types of issues.\n\nFeel free to (fork this project and) play around!\n\nRun `npm run knip` on this codebase in the terminal below and Knip will report\nthe following issues:\n\n```\n$ npm run knip\nUnused dependencies (1)\ntinyglobby  package.json\nUnlisted dependencies (1)\njs-yaml  packages/shared/src/used-fn.ts\nUnused exports (1)\nunusedFunction  unknown  packages/shared/src/exports.ts:7:14\n```\n\n- `tinyglobby` is unused in the root `package.json`. It's listed and used in the\n  `packages/shared` workspace, but not referenced elsewhere in the root\n  workspace.\n- `js-yaml` is used in `packages/shared/src/used-fn.ts`, but it's not listed as\n  a dependency in `packages/shared/package.json`.\n- `unusedFunction` is exported from `packages/shared/src/exports.ts` but it's\n  not imported anywhere.\n"
  },
  {
    "path": "templates/playground/monorepo/knip.ts",
    "content": "/** @type {import('knip').KnipConfig} */\nconst config = {\n  workspaces: {\n    'packages/shared': {\n      includeEntryExports: true,\n    },\n  },\n};\n\nexport default config;\n"
  },
  {
    "path": "templates/playground/monorepo/package.json",
    "content": "{\n  \"name\": \"@knip/playground-monorepo\",\n  \"version\": \"1.0.0\",\n  \"private\": true,\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"scripts\": {\n    \"build\": \"tsc -b packages/shared packages/server packages/client\",\n    \"knip\": \"knip\"\n  },\n  \"dependencies\": {\n    \"tinyglobby\": \"^0.2.6\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.14.8\",\n    \"knip\": \"^5.30.1\",\n    \"typescript\": \"^5.5.4\"\n  }\n}\n"
  },
  {
    "path": "templates/playground/monorepo/packages/client/package.json",
    "content": "{\n  \"name\": \"@monorepo/client\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"main\": \"dist/index.js\",\n  \"dependencies\": {\n    \"@monorepo/shared\": \"*\"\n  }\n}\n"
  },
  {
    "path": "templates/playground/monorepo/packages/client/src/index.ts",
    "content": "import { someFunction, usedFunction } from '@monorepo/shared';\n\nusedFunction;\nsomeFunction;\n"
  },
  {
    "path": "templates/playground/monorepo/packages/client/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"declaration\": true\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"dist/**/*\"]\n}\n"
  },
  {
    "path": "templates/playground/monorepo/packages/server/package.json",
    "content": "{\n  \"name\": \"@monorepo/server\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"main\": \"dist/index.js\",\n  \"dependencies\": {\n    \"@monorepo/shared\": \"*\"\n  }\n}\n"
  },
  {
    "path": "templates/playground/monorepo/packages/server/src/index.ts",
    "content": "import { usedFunction } from '@monorepo/shared';\n\nusedFunction;\n"
  },
  {
    "path": "templates/playground/monorepo/packages/server/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"declaration\": true\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"dist/**/*\"]\n}\n"
  },
  {
    "path": "templates/playground/monorepo/packages/shared/package.json",
    "content": "{\n  \"name\": \"@monorepo/shared\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"main\": \"dist/index.js\",\n  \"dependencies\": {\n    \"tinyglobby\": \"^0.2.6\"\n  }\n}\n"
  },
  {
    "path": "templates/playground/monorepo/packages/shared/src/exports.ts",
    "content": "import { glob } from 'tinyglobby';\n\nglob;\n\nexport const someFunction = () => 'bar';\n\nexport const unusedFunction = () => 'unused';\n"
  },
  {
    "path": "templates/playground/monorepo/packages/shared/src/index.ts",
    "content": "export * from './exports.js';\nexport { usedFunction } from './used-fn.js';\n"
  },
  {
    "path": "templates/playground/monorepo/packages/shared/src/used-fn.ts",
    "content": "import jsYaml from 'js-yaml';\n\njsYaml;\n\nexport const usedFunction = () => 'bar';\n"
  },
  {
    "path": "templates/playground/monorepo/packages/shared/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"declaration\": true\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"dist/**/*\"]\n}\n"
  },
  {
    "path": "templates/playground/monorepo/tsconfig.json",
    "content": "{\n  \"files\": [],\n  \"compilerOptions\": {\n    \"allowSyntheticDefaultImports\": true,\n    \"declaration\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"module\": \"nodenext\",\n    \"moduleResolution\": \"nodenext\",\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"target\": \"ES2022\"\n  },\n  \"references\": [\n    { \"path\": \"packages/client\" },\n    { \"path\": \"packages/server\" },\n    { \"path\": \"packages/shared\" }\n  ]\n}\n"
  }
]