npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jeremybarbet/oxlint-plugin

v0.3.0

Published

Shared oxlint JS plugin: dead-code rules that oxlint, tsc and knip each miss, plus a few house rules.

Readme

@jeremybarbet/oxlint-plugin

Shared oxlint JS plugin.

Install

yarn add -D @jeremybarbet/oxlint-plugin
// oxlint.config.mts
import { defineConfig } from 'oxlint';

export default defineConfig({
  jsPlugins: ['@jeremybarbet/oxlint-plugin'],
  rules: {
    'op/no-export-all': 'error',
    'op/no-unused-modules': 'error',
    'op/no-unused-class-members': 'error',
  },
});

Rules

no-unused-modules

Reports exports nothing else imports, and optionally files with no exports at all. A port of import/no-unused-modules from eslint-plugin-import.

It overlaps with knip, but the diagnostic lands on the export itself, in the editor, on the same pass as every other lint rule — rather than as a batch report from a separate command.

Not reported:

  • Exports tagged /** @public */ or /** @beta */, the same opt-out knip honours.
  • Exports consumed inside their own file. Those are over-exposed, not dead — the fix is dropping the export keyword, not deleting the declaration. Most exported return types look like this.
  • Anything matching **/*.config.*, always. A config file is loaded by path by the tool that owns it, so its default export is an entry point by definition.

| Option | Default | | | --- | --- | --- | | unusedExports | true | Report exports nothing imports. | | missingExports | false | Report files with no exports. Noisy — every spec and mock qualifies. | | ignoreExports | [] | Globs whose exports are never reported. Put your entry points here. | | ignoreDefaultExports | false | Never report export default. Pass globs to scope it — a file-based router like expo-router loads its routes by path, so their default export is an entry point, never an import. |

'op/no-unused-modules': ['error', {
  ignoreExports: ['scripts/**'],
  ignoreDefaultExports: ['app/**'], // expo-router loads routes by path
}],

ignoreDefaultExports is the narrow version of ignoreExports: listing app/** there would also silence the named exports in those files, so a route that grew a dead helper would go unreported.

no-unused-class-members

Reports public class members that nothing outside the declaring file references.

This is the gap that motivated the package. knip analyses module-level exports only, so an exported class that is used hides its entire body from it; tsc's noUnusedLocals covers private members alone. In a codebase where most logic lives on service and controller classes, that leaves most of the code unchecked.

The check is name-based — oxlint exposes no type checker to JS plugins — so it is deliberately conservative. A member whose name is referenced anywhere else, even on an unrelated class, counts as used. It under-reports rather than pointing at live code.

Not reported:

  • private and #private members, and constructor parameter properties.
  • Any member carrying a decorator. A decorator means something outside the type system reaches it — a @Cron schedule, a GraphQL @Field, a class-validator constraint.
  • Members of classes that extend or implement anything, since those can be invoked polymorphically by a base class or by whatever holds the interface.
  • Members referenced only from test files (mirrors knip --production).

| Option | Default | | | --- | --- | --- | | ignoreNames | framework lifecycle hooks | Member names never reported. | | ignoreUsagesIn | specs, mocks, src/test/** | Globs whose references do not count as usage. | | reportPrivateCandidates | false | Also report members used only inside their own file, suggesting private. Off by default: those are a refactor prompt, not dead code. |

no-export-all

Disallows export *, which defeats tree-shaking and invites name clashes.

no-unused-styles

React Native. Reports entries in a StyleSheet.create object that nothing consumes. Understands the plain object form and both function forms react-native-unistyles uses.

require-cache-control

NestJS + Apollo. Requires @CacheField over @Field for non-scalar GraphQL object type fields, so a nested type cannot silently break Apollo's cache-control computation. Ships an autofix suggestion.

Project discovery

no-unused-modules and no-unused-class-members need a whole-project view, so they crawl and parse the project once per process and answer per-file from the result. (Oxlint's plugin API is per-file with no end-of-run hook, so this is the same approach eslint-plugin-import takes.) It costs roughly 120ms on an 830-file repo.

Discovery is zero-config: everything under the oxlint working directory that looks like source, skipping node_modules, dist, build, coverage, android, ios and dotfile directories. Import specifiers are resolved through tsconfig.json paths, including the nodenext .js.ts rewrite.

If the defaults are not enough, narrow it via settings. The key is the package name, not the rule prefix, so it does not move when you alias the plugin:

settings: {
  '@jeremybarbet/oxlint-plugin': {
    roots: ['src'],
    ignore: ['src/api/generated'],
  },
},