# Handling Issues

Source: https://knip.dev/guides/handling-issues

Issues reported by Knip may contain false positives, but also tons of useful
information. Getting the most out of Knip may require some configuration.

Go over the issue types one by one. For instance, reducing the number of unused
files will also reduce the number of unused dependencies.

- Unused files
- Unused dependencies
- Unresolved imports
- Unused exports

## Unused files

Getting the list of unused files right trickles down into the other issue types
as well, so we start here. Files are reported as unused if they are in the set
ofprojectfiles, but not in the set of files resolved from theentryfiles:

```typescript
unused files = project files - (entry files + resolved files)
```

Let’s go over common causes for unused files:

- Missing generated files
- Dynamic import specifiers
- Unsupported arguments in scripts
- Unsupported file formats
- Missing plugin
- Incomplete plugin
- TypeScript path aliases in monorepos
- Relative paths across workspaces
- Integrated monorepos
- Auto-mocking or auto-imports

In most cases you can addentrypatterns manually.

Use--filestofilter the reportand focus only on unused files:

```typescript
knip--files
```

This works with other issue types as well. For instance, use--dependenciesto
focus only on dependencies and exclude issues related to unused files and
exports.

Don’t add unused files to theignoreoption before readingconfiguring
project files. Learn why and when to useentry,project, production
mode andignorepatterns for better results and performance.

### Missing generated files

For certain features, Knip needs to run after relevant files are generated. For
instance,source mappingin a monorepo may require files to be built intodistfolders first. And generated files in thesrcdirectory may import
other files. For instance, thesrc/routeTree.gen.tsfile generated by@tanstack/routermust exist so Knip can find the imported route files.

Solution: compile and/or generate the relevant files first so Knip can
resolve and find the source files.

If Knip still reports false positives, you may need to strategically add anentryfile manually.

### Dynamic import specifiers

Dynamic import specifiers aren’t resolved, such as:

```typescript
constentry=awaitimport(path.join(baseDir,'entry.ts'));
```

Solution: addentry.tstoentrypatterns.

### Unsupported arguments in scripts

Some tooling command arguments aren’t recognized:

```typescript
{"name":"my-lib","version":"1.0.0","scripts":{"build":"unknown-build-cli --entry production.ts"}}
```

Solution: addproduction.tstoentrypatterns.

This works the same for any script, also those in GitHub Actions workflows or
Git hooks. Seescript parserfor more details about Knip’s script parser.

### Unsupported file formats

Entry files referenced in HTML files (e.g.<script src="production.js">).

```typescript
<html><body><scripttype="module"src="production.js"></script></body></html>
```

Solution: addproduction.jstoentrypatterns. Or add an.htmlcompiler to extract and resolve the value of<script src>elements.

Knip has support for some popular framework formats throughcompilers, and
additional compilers can be added for any file type. The recommended solution is
usually to add the file as shown in each example as anentryfile.

### Missing plugin

You might be using a tool or framework for which Knip doesn’t have a plugin
(yet). Configuration and entry files (and related dependencies) may be reported
as unused because there is no plugin yet that would include those files. Two
examples:

- Configuration filetool.config.jscontains a reference to the package"@tool/plugin"→ both the file and the dependency are reported as unused.
- A framework automatically imports all files matchingsrc/models/*.ts→ those
files are reported as unused.

Solution:create a new pluginfor the tool or framework that’s notin
the listyet. Or work around it and addentrypatterns and maybe ignore a
dependency or two (usingignoreDependencies).

### Incomplete plugin

Files may be reported as unused if existing plugins do not include that entry
file pattern yet. See theplugins section of entry filesfor more details.

Solution:override plugin configurationto customize default patterns
for existing plugins. Or even better: send a pull request to improve the plugin.

### TypeScript path aliases in monorepos

When using TypeScript path aliases in import specifiers, aliases referencing
other workspaces are not special-cased. This may cause false positives. For
Knip, it’s better to be explicit and list other workspaces as dependencies inpackage.json.

Solution: move such “workspace aliases” fromcompilerOptions…

```typescript
{"compilerOptions":{"paths":{"@org/common/*":["packages/common/*"]}}}
```

…todependenciesordevDependenciesinpackage.json:

```typescript
{"name":"@org/lib","dependencies":{"@org/common":"workspace:*"}}
```

An additional benefit is that Knip will report unused and unlisted dependencies
from now on.

Also seeFAQ: Why can’t I use path aliases to reference other workspaces?

### Relative paths across workspaces

Relative paths to import from other workspaces are not special-cased. For Knip,
it’s better to be explicit and list other workspaces as dependencies inpackage.jsonto be used in imports.

Solution: migrate from relative paths…

```typescript
import{ something }from'../../common/file.ts';
```

…to dependency-based imports…

```typescript
import{ something }from'@org/common';
```

An additional benefit is that Knip will report unused and unlisted dependencies
from now on.

Also seeTypeScript path aliases in monorepos.

### Integrated monorepos

Multiple instances of configuration files like.eslintrcandjest.config.jsonacross the repository may be reported as unused when working
in a (mono)repo with a singlepackage.json.

Solution: seeintegrated monoreposfor more details and how to
configure plugins to target those configuration files.

### Auto-mocking or auto-imports

Some frameworks have features like “auto-mocking” or “auto-imports” enabled,
such as Jest and Nuxt.

Solution: include such entry files by extending theentryfile patterns.
This is recommended in most cases:

```typescript
{"entry":["src/index.ts","src/models/*.ts"]}
```

Alternatively, exceptions and outliers can be excluded from the analysis using
negatedprojectpatterns:

```typescript
{"project":["src/**/*.ts","!**/__mocks__/**"]}
```

## Unused dependencies

Dependencies imported in unused files are reported as unused dependencies.
That’s why it’s strongly recommended to try and remedyunused filesfirst.
Betterentryandprojectfile coverage will solve many cases of reported
unused dependencies.

The most common causes for unused dependencies include:

- Missing or incomplete plugins
- Unrecognized references
- Type Definition Packages
- Dependencies named after Node.js builtins

Use--dependenciestofilter the reportand focus only on issues related
to dependencies:

```typescript
knip--dependencies
```

In a monorepo, a dependency is unused in the root workspace’spackage.jsonif
it’s also listed in a descendant workspace, and referenced only in the
descendant workspace.

### Missing or incomplete plugin

If a plugin exists and the dependency is referenced in the configuration file,
but its custom dependency finder does not detect it, then that’s a false
positive. Please open a pull request or issue to fix it.

Solution: adding the configuration file as anentryfile pattern may be a
temporary stopgap that fixes your situation, but it’s better to create a new
plugin or fix an existing one.

### Unrecognized reference

Sometimes a reference to a dependency is unrecognizable or unreachable to Knip,
so it’s a false positive and incorrectly reported as unused.

Solution: add a new plugin or improve an existing one. If you don’t feel
like a plugin could solve it, a last resort is to useignoreDependencies.

If a binary (or “executable”) is referenced you’ll want to useignoreBinariesinstead. Seeunlisted binaries.

### Dependencies named after Node.js builtins

Some packages have the same name as a Node.js builtin (for instancebufferorprocess).

Solution: if Knip reports such a dependency as unused, add it toignoreDependencies.

#### Conditional or dynamic dependencies

Dependencies added conditionally in configuration files may not be detected by
Knip. This happens because Knip loads and executes config files, and conditional
logic may evaluate differently during analysis.

For example, this Playwright configuration conditionally adds a reporter:

```typescript
import{ defineConfig }from'@playwright/test';constreporters:any[]=[['list']];if(process.env.REPORT_PORTAL_ENABLED) {reporters.push(['@reportportal/agent-js-playwright',config]);}exportdefaultdefineConfig({reporter:reporters,});
```

Ifprocess.env.REPORT_PORTAL_ENABLEDevaluates tofalsewhen Knip runs, the@reportportal/agent-js-playwrightdependency won’t be detected and may be
reported as unused.

This limitation exists because Knip executes configuration files to parse their
exported value. While Knip can parse configuration files statically using AST
(Abstract Syntax Tree) analysis, this approach becomes complex very quickly and
most of the time it is easier to use theignoreDependenciesconfiguration option for conditionals.

```typescript
{"ignoreDependencies":["@reportportal/agent-js-playwright"]}
```

This pattern can be applied to any plugin that loads configuration files.

### Type Definition Packages

#### Bundled types

Many packages come with their type definitions bundled. This means thepackage.json#typesfield in the package points to an internal/local type
definition file. In this case, the separate types package is obsolete.

Knip reporting this is also useful for future regressions: if a package had a
DefinitelyTyped or similar package for its types before and later on starts
shipping those types bundled with the source code, Knip will report the obsolete
types dependency as unused.

Examples include ESLint, Webpack and React Router, rendering the@types/eslint,@types/webpackand@types/react-routerdependencies
obsolete respectively.

Solution: remove the types dependency (often@types/...)

#### Production types

Knip is strict in the divide betweendependenciesanddevDependencies. Some
packages are published with one or more type packages listed independencies.
In strict production mode, even when re-exported and part of the package’s
public API, Knip does not try to figure out what exactly are “production types”
and expects those indevDependencies.

Solution: list exceptions inignoreDependencies.

### Unlisted dependencies

This means that a dependency is referenced directly in source code or
configuration, but not listed inpackage.json.

An unlisted dependency is usually a transitive dependency that’s imported or
referenced directly. The dependency is installed (since it’s a dependency of
another dependency) and lives innode_modules, but it’s not listed explicitly
inpackage.json.

You should not rely on transitive dependencies for various reasons, including
control, security and stability.

Solution: install and list the dependency independenciesordevDependencies.

### Unlisted binaries

Binaries are executable Node.js scripts. Some npm packages, when installed, add
one or more executable files to be used from scripts inpackage.json. Examples
include TypeScript that comes with thetscbinary, ESLint comes witheslint,
Next.js withnext, and so on.

Knip detects such binaries in scripts and checks whether there’s a package
installed that includes that binary. It looks up thebinfield in thepackage.jsonfile of installed packages. If it doesn’t find it, it will be
reported as an unlisted binary as there is no package that contains it.

Binaries that are installed on the OS already and thus likely not meant to be
installed from npm are not reported as unlisted (details:list of ignored
binaries in source).

#### Missing binaries

An unused dependency and an unlisted binary with the same name indicatesnode_modulesnot containing the relevant package. And this might be caused by
either the way your package manager installs dependencies and binaries, or by
not running Knip from the root of the repository.

Solution: run Knip from the project root. If needed,lint workspaces
individually.

Sometimes binaries and how they’re reported can be a bit confusing. See this
example:

```typescript
{"name":"lib","scripts":{"commitlint":"commitlint --edit"},"devDependencies":{"@commitlint/cli":"*"}}
```

This example works fine without anything reported, as the@commitlint/clipackage includes thecommitlintbinary. However, some script may containnpx commitlintand here Knip assumescommitlintis the name of the package. This
technically works, ascommitlintis a transitive dependency of@commitlint/cli.

Solution: usenpx @commitlint/cli

In some cases, usingnpxin a script may result in Knip not understanding
intention without an explicit--yesor--no-installflag.

Solution: usenpx --yesornpx --no-installso Knip will either ignore
or consider the binary and package(s) referenced, respectively.

## Unresolved imports

Knip may ignore or be unable to resolve an import specifier or dependency
references. The most common causes for unresolved imports:

- Template strings
- Extensionless imports
- Unrecognized path aliases
- External aliased imports

### Template strings

Using template strings in dynamic imports might be ignored or not handled
properly by Knip, resulting in false positives. Examples of dynamic import
template strings:

```typescript
import(`./${value}.ts`);import(`@org/name/dist/${value}.js`);
```

Solution: for internal source files, add the file(s) to theentrypatterns. For external dependencies, add the dependency to theignoreDependencieslist.

### Extensionless imports

Knip does not support extensionless imports for some non-standard extensions,
such as for.svgfiles. Bundlers like Webpack may support this, but Knip does
not. Here’s an example:

```typescript
importComponentfrom'./Component';// → Should resolve to ./Component.vueimportArrowIconfrom'../icons/Arrow';// → Does NOT resolve to ../icons/Arrow.svg
```

The first import is resolved properly, because.vueis a known extension if
the Vue plugin is enabled. The second import might not be resolved, because.svgis not a known extension.

Solution: include the extension. Hosts supporting ES Modules (e.g. Node.js,
browsers) require a file extension for import specifiers.

### Unrecognized path aliases

Knip considers TS config path aliases andpaths configured in knip.json,
but not those in e.g. Webpack or Vite configurations.

Solution: configurepathsor try relative imports. Otherwise, useignoreUnresolvedas a last resort.

### External aliased imports

External libraries may use aliased imports that aren’t resolved by Knip.

For instance,unplugin-iconsdoes this to import icons from icon sets as
components. Such imports are reported as unused. Use thepathsconfiguration
optionto tell Knip where to find the icon types:

```typescript
{"paths":{"~icons/*":["node_modules/unplugin-icons/types/[framework].d.ts"]}}
```

Where[framework]is the name of the framework you’re using (seeavailable
types).

Solution: try—include-libsor configurepaths.

## Unused exports

By default, Knip does not report unused exports ofentryfiles.

The most common causes for unused exports include:

- Namespace enumerations
- External libraries

Use the--exportsflag tofilterand focus only on issues related to
exports:

```typescript
knip--exports
```

UseincludeEntryExportsto report unused exports of entry files as well.
This can be set per workspace.

### Namespace enumerations

For exports on an imported namespace, Knip considers all exports referenced if
that namespace is used in certain patterns like enumeration. Individual exports
are thennotreported.

Solution: if all exports on imported namespaces should be considered
individually, include thensExportsissue type to disable the heuristic.

Seenamespace importsto see all related patterns.

### External libraries

Are the exports consumed or imported by an external library, resulting in a
non-standard consumption of your exports? Here’s an example:

- index.js
- components.js

```typescript
importloadablefrom'@loadable/component';exportconstDynamicApple=dynamic(()=>import('./components.js').then(mod=>mod.Apple));exportconstLoadableOrange=loadable(()=>import('./components.js'),{resolveComponent:components=>components.Orange,});
```

```typescript
exportconstApple=()=>'Apple';exportconstOrange=()=>'Orange';
```

Knip understandsAppleis used, since it’s standard usage. ButOrangeis
referenced through a function of an external library. For performance reasons,
Knip does not include external type definitions by default so it won’t see the
export being referenced.

Solution: include the type definitions of external libraries with the—include-libsflag:

```typescript
knip--include-libs
```

This comes at a performance and memory penalty, but should give better results
if you need it. This flag is implied whenclassMembersare included (that
feature comes with roughly the same performance penalty).

### Exclude exports from the report

To exclude false positives from the report, there are a few options:

- Ignore exports used in filefor exports used internally.
- Individual exports can betagged using JSDoc syntax.
- Have the export in an entry file:Add the file to theentryfile patterns array in the configuration.Move the export(s) to an entry file.Add the file to theexportsfield ofpackage.json
- Re-export the unused export(s) from an entry file.

Ignore exports used in filefor exports used internally.

Individual exports can betagged using JSDoc syntax.

Have the export in an entry file:

- Add the file to theentryfile patterns array in the configuration.
- Move the export(s) to an entry file.
- Add the file to theexportsfield ofpackage.json

Re-export the unused export(s) from an entry file.

### Missing unused exports?

Did you expect certain exports in the report, but are they missing? They might
be exported from an entry file. In that case, use—include-entry-exportsto make Knip also report unused exports in entry files.

The exports of non-standard extensions like.astro,.mdx,.vueor.svelteare not available by default. Seecompilersfor more details on
how to include them.

### Class members

Unused members of exported classes are not reported by default, here’s how to
enable them:

```typescript
knip--includeclassMembers
```

This option is also available in the Knip configuration file. Note that this
feature comes at a cost: linting will take more time and more memory.

Individual class members can betagged using JSDoc syntax.

Classes exported from entry files are ignored, and so are their members. Use—include-entry-exportsto make Knip also report members of unused exports
in entry files.

### Enum members

Unused enums and unused members of exported enums are reported by default.
Reporting such members can be disabled:

```typescript
knip--excludeenumMembers
```

Individual enum members can betagged using JSDoc syntax.

Enums exported from entry files are ignored, and so are their members. Use—include-entry-exportsto make Knip also report members of unused exports
in entry files.

## Feedback or false positives?

If you believe Knip incorrectly reports something as unused (i.e. there’s a
false positive), feel free to create aminimal reproductionand open an
issue on GitHub. It’ll make Knip better for everyone!

ISC License© 2024Lars Kappert