A few months back I posted a Show HN to share my idea about removing unused code in TypeScript projects automatically by statically analyzing the codebase: https://news.ycombinator.com/item?id=41554014
I didn't expect much response but the post gained a lot of feedback and controversy mostly due to how rough-edged the tool was. Since then, I've been working on polishing the tool and I've managed to release v1 with a new name "tsr" so I'd like to share it here.
Here are the key features:
- it supports comprehensive auto-editing: not only does it remove unused export keywords, it can remove declarations that become unnecessary after removing the export keyword
- small: 234kB compared to 5.8MB with Knip
- fast: Benchmarks with the vuejs/core repository shows that it's 2.5x faster compared to Knip.
Internally, I've written some custom logic since then to traverse the files and detect the usage of declarations instead of relying on ts.LanguageService.findReferences (which Knip also uses) to avoid limitations caused by this API and for more performant results.
I'm not trying to be offensive here, but I've realized that the design choices of the tool and the explanation in README was a little too difficult and unfriendly for the average TypeScript user.
I've added a more detailed explanation to README so I hope it will change your mind if you've previously had a negative impression :)
You need a valid tsconfig that defines the scope of the project and it seems renovate’s tsconfig doesn’t meet this requirement. You can always --skip manually as an alternative option.
Renovate's TS tooling seems to work fine otherwise, and for ts-remove-unused as a new tool wanting to get adoption then you should be aiming to work the way your users already work, and not fail + tell them that they are the ones which need to change.
BTW the VSCode extension which someone else linked to discovered everything perfectly, which is another hint that your tool needs to improve, not your users.
Even if tsconfig.json was very important, and the users are all wrong and you're right, the only thing your readme says is: "The CLI will respect the tsconfig.json for loading source files." which is insufficient.
My understanding is that Knip is for detecting unused things and that it's auto-fix feature is still experimental, but with that out of the way...
- In general, I personally do not like the idea of having to add another config file in my repo. I feel it's contradictory that I need to add more to my codebase to clean up the clutter in my codebase. I understand that relying on tsconfig to specify the target files has its pros and cons but I hope that my tool will encourage users to maintain their tsconfig.
- Knip has its own ecosystem around removing unused stuff in your codebase (not limited to exports). I personally prefer tools that are more single-purposed and does one thing right so I don't have any ideas for expanding features; It will be focused on auto-fixing unused code caused by unncessary `export`s.
I may be opinionated but I believe that the best practice is to configure a separate tsconfig for test files with project references. As long as the test files are not included in the tsconfig passed to ts-remove-unused, it should remove exports that are only used in test files.
In the JavaScript API, it would be nice if there was the ability to have custom comparative functions for this sort of use case. A user defined function that gets called right before final output which allows the user to create custom filtering rules and return a boolean to indicate inclusion or exclusion. I could see this being useful not only for the case I just presented, but also other cases such as NextJS projects that export default functions in pages that are never imported elsewhere in the project.
I wont go into detail about what it is (since the docs are good enough) but in short, it's a feature to let you setup multiple TypeScript projects in the same repo.
Some things that project references will solve:
- importing test files from your impl files will throw an error
- you can specify what types are available. for example you shouldn't be able to use `@types/node` in your frontend code or the `DOM` type in your test code (or vice-versa)
- no type checking the whole repository again just because you changed the test.
I think there's quite some control over how strict you can be when you say you're using TypeScript. Project references is another way of making your config more robust, and it may not be necessary depending on your needs (like when some people disable strict:true). I think it's a better choice and relying on a properly configured project shouldn't be a issue for users who are actually going to use this (who are likely maintainers of their project)
Thanks for the input! I think there may be a misunderstanding about what this does. Existing linters work great for detecting unused code within a file but once you add `export` to it, you can’t detect unused code with linters even if it’s not referenced from any file.
You’re right that this tool may not be useful for some codebases. If your modules are more like “scripts” that include side effects, deleting modules just because it’s not referenced may break things. That should not be the case for react projects that are based on components.
In our development process, we don’t allow the changes made by this tool to be deployed automatically. Instead we make sure the changes are okay by creating a pull request and reviewing. We treat it more like an assistant that does all the cumbersome clean up work in no time.
I didn't expect much response but the post gained a lot of feedback and controversy mostly due to how rough-edged the tool was. Since then, I've been working on polishing the tool and I've managed to release v1 with a new name "tsr" so I'd like to share it here.
Here are the key features: - it supports comprehensive auto-editing: not only does it remove unused export keywords, it can remove declarations that become unnecessary after removing the export keyword - small: 234kB compared to 5.8MB with Knip - fast: Benchmarks with the vuejs/core repository shows that it's 2.5x faster compared to Knip.
Internally, I've written some custom logic since then to traverse the files and detect the usage of declarations instead of relying on ts.LanguageService.findReferences (which Knip also uses) to avoid limitations caused by this API and for more performant results.
Any feedback is very much appreciated!