Use Knip
Rhapsody uses Knip for automated dependency management. Knip finds and fixes unused files, exports and dependencies, using deep analysis from fine-grained entry points based on the actual frameworks and tooling in (mono)repos for accurate and actionable results.
The tool is integrated as a part of infrastructure/linters package.
Why Knip?
The value of removing clutter from your code is undeniable. However, finding them is a manual and tedious job. This is where Knip comes in. As codebases grow in complexity and size, automated and comprehensive tooling becomes critical.
- Easier maintenance: things are easier to manage when there’s less of it.
- Improved performance: startup time, build time and/or bundle size can be negatively impacted when unused code, files and/or dependencies are included. Relying on tree-shaking when bundling code helps, but it’s not a silver bullet.
- Easier onboarding: there should be no doubts about whether files, dependencies and exports are actually in use or not. Especially for people new to the project and/or taking over responsibilities this is harder to grasp.
There are alternative tools that cover the same areas. However, there are a few things that make Knip fit our needs.
- Optimized for usage in monorepos: Knip works without much configuration in our current codebase (with minimal number of false positives) and can be scoped to selected applications or packages.
- Automatic plugin-based configuration: Knip creates a configuration based on the dependencies it can find in the project and their respective configuration (see Plugins).
- Useful when migrating or re-writing applications: there’s a good chance code or dependencies will be left behind when performing large code migration (which we do fairly regularly).
- Go-to open source solution: with 3 million downloads a month and a decent support from the community.
Common CLI Commands
You can run Knip locally in the same way it's used on CI or by passing additional arguments to narrow down the results. It's most useful when run to automatically fix issues. Here are some example commands you can use.
pnpm knip: run in the entire codebasepnpm knip --changed: custom command - run in the entire codebase, but only report issues in files changedpnpm knip --fix: run and automate fixespnpm knip --workspace <workspace>: run in a selected<workspace>pnpm knip --fix --workspace <workspace>: run and automate fixes in a selected<workspace>pnpm knip --fix --include <issue_type>: run and automate fixes for a selected<issue_type>
To see the full list of supported commands, see documentation.
Knip Has Reported Issues - What Now?
Depending on the issue severity, you might have to fix the issues reported by Knip (for errors, however it’s still recommended to fix the warnings as well). Here are the steps you can try to address any given issue.
- Fix the issue automatically. You can run
pnpm run knip --fixto let Knip fix the issues automatically.- Make sure to commit only the relevant change.
- Knip doesn’t remove unused files by default. You can change that by passing
--allow-remove-files.
- Fix the issue manually. Address the issues reported either by removing highlighted code or dependencies.
- Ignore the issue. See next section for more details.
- Creating custom configuration. See next section for more details.
As we introduce Knip to our codebase, it may flag some false positives. If you spot something that shouldn't be reported, take a look at adjusting its configuration settings.
If it still incorrectly reports something as unused, you can create a minimal reproduction and open an issue on Knip’s GitHub.
Ignoring Code
Please read project files configuration before using the ignore option, because in many cases you’ll want to exclude project files instead.
There are a few ways to ignore code reported by Knip.
- Adjust configuration. There are a few configuration options for ignoring issues, these options can be applied globally or scoped to selected workspaces.
ignore: ignore issues in specific files or directories.ignoreDependencies: ignore specific dependencies.ignoreWorkspaces: ignore entire workspaces.
- Mark the code with JSDoc/TSDoc. Knip tries to minimize configuration and introduces no new syntax. That’s why it hooks into JSDoc and TSDoc tags.
- Mark the code as
@public. By default, Knip reports unused exports in non-entry files. Tag the export as public and Knip will not report it. - Mark the code as
@internal. Internal exports are not meant for public consumption, but only for internal usage such as tests. Mark the export as internal and Knip will not report the export in production mode. - Mark the code as
@beta. Works identical to@public.
- Mark the code as
For more details, see configuration reference.
Creating Custom Configuration
Creating custom configuration shouldn’t be needed for regular applications or shared packages.
In situations when the package doesn’t follow the standard structure, has specific requirements or multiple custom entry points, you might have to create a custom configuration.
If the package doesn’t match the default configuration, it can be overwritten in knip.json.
{
"workspaces": {
"infrastructure/scaffolding": {
"entry": "src/plopFile.js"
}
},
}
Some examples that require custom configuration in our case are the docs-site or infrastructure/scaffolding packages.
For more details, see configuration reference.
Workflow Integration
We're introducing Knip into our pipeline in an incremental way, tightening up its configuration as we address each issue it reports. You can see the current configuration in knip.json#rules.
When integrating the tool in our workflow pipeline, we are aiming to achieve the following.
- Knip runs as a part of our linters job, reporting issues for pull requests only in areas related with the change scope.
- Issues can be defined as blocking (errors) or non-blocking (warnings).
- Non-blocking issues (warnings) are highlighted for engineers.
- Integration can be made more more strict as we go.
To achieve the above, we’ve made the following changes.
- Extended the
lintersjob in the main GitHub Actions workflow. - Reporting issues only in the changed files (locally, it still runs on all files by default).
- Added custom
rulesconfiguration to selectively define issue policy. - Created a workflow step to post the command results as a pull request comment.
A contrived CI integration plan looks a follows:
- Turn on warnings for a selected issue type.
- Fix reported warnings or adjust the configuration if required.
- Change from warnings to errors for the selected issue type.
- Start again with another issue type.