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

@aetherall/aexlint

v0.6.0

Published

Readability rules for Oxlint, syntax and type-aware, in one plugin.

Readme

aexlint

Readability rules for Oxlint: syntax rules that measure how much a reader must hold at once, and type-aware rules that find knowledge about values repeated, re-checked, or held outside its owner. No autofix.

Standalone CLI

Requires Node.js 24+.

pnpx @aetherall/aexlint check
pnpx @aetherall/aexlint check src --typed --format json

check defaults to the current directory and runs every syntax rule as an error. --typed adds every type-aware rule; the project needs its dependencies installed and a TypeScript project (see below). Default limits:

| Rule | Limit | | ----------------------------- | ------------------------- | | max-expression-depth | 3 | | max-expression-complexity | 4 | | max-decision-depth | 2 | | max-held-context | 3 | | max-call-assembly | width 60, 3 outside names | | max-interpretation-spread | 4 | | max-projection-spread | 2 | | no-fact-outside-owner | 4 | | no-default-outside-owner | 1 | | no-derivation-outside-owner | 2 |

The limits are starting points, not validated thresholds. For other limits or a subset of rules, use the plugin.

The CLI runs its own pinned Oxlint and its bundled type-aware backend. It does not use the project's Oxlint, --type-aware or oxlint-tsgolint, so a project can keep linting with another Oxlint version; the optional oxlint-tsgolint peer that package managers list is not needed.

The CLI ignores Oxlint configurations and enables no built-in checks. Oxlint's file exclusions, ignore files and disable comments apply. Add exclusions with repeatable --ignore-pattern <glob> or in .aexlintignore (one glob per line, # comments). --format/-f accepts Oxlint output formats. Findings fail the command. Use -- before paths starting with a dash.

Adopting gradually with a committed baseline

Record the current findings once and commit the file:

aexlint check --typed --write-baseline .aexlint-baseline

CI then runs aexlint check --typed --baseline .aexlint-baseline and reports only findings missing from it. To accept findings, or drop the entries of findings you fixed, record again for the paths you worked on:

aexlint check --typed --write-baseline .aexlint-baseline src/billing

With paths, --write-baseline replaces only the entries of files under them and keeps the rest, so the change shows exactly what was accepted, a branch behind its target cannot drop entries for other files, and two branches conflict only where they touch the same files. Without paths it replaces the whole file. Entries stay until their path is recorded again, so a fixed finding can come back in that file unreported. Upgrading aexlint or changing ignore patterns changes which findings exist: record the whole file again in the same change.

Checking only the changed files is faster but misses findings a change causes in files it does not touch, such as callers of a changed type or files joining an over-limit spread.

Reporting only new findings against the base revision

Without a committed file, record the findings of the base revision, then check the branch against them:

aexlint="$PWD/node_modules/.bin/aexlint"
git worktree add ../base "$(git merge-base origin/master HEAD)"
(cd ../base && pnpm install --frozen-lockfile && "$aexlint" check --write-baseline /tmp/aexlint-baseline)
"$aexlint" check --baseline /tmp/aexlint-baseline

Use the same version, flags and paths for both runs, from the root of each checkout. This also reports findings fixed on the base, but costs a second install and run per check. A finding is its file, rule and message: it survives moved lines, but comes back when its message changes, when its file is renamed, or when it is caused by a change elsewhere. Identical findings in a file are counted, including those silenced by disable comments. Parse errors are never skipped. When a rule cannot run, such as a typed rule without its backend, --write-baseline fails and writes nothing. Baselines apply only to the CLI.

Plugin setup

Requires Node.js 24+ and Oxlint 1.83.0.

pnpm add -D [email protected] @aetherall/aexlint

.oxlintrc.json:

{
  "jsPlugins": ["@aetherall/aexlint"],
  "rules": {
    "aexlint/max-expression-depth": ["warn", { "max": 3 }],
    "aexlint/no-repeated-value-policy": "warn"
  }
}

The plugin enables no rules by default. Type-aware rules need the TypeScript setup below; syntax rules need nothing more.

Rules

Syntax

| Rule | Reports | | ---------------------------------------------------------------------- | ----------------------------------------------------------------- | | max-expression-depth | Nested calls | | max-expression-complexity | Work inside one expression | | max-decision-depth | Nested logical groups and ternaries | | max-call-assembly | Wide calls drawing on many outside names | | max-held-context | Enclosing decisions, loops, and callbacks | | no-else-if | else if chains | | no-multiline-condition | Conditions spanning multiple lines | | no-positional-separator | Separators placed by a pass's position rather than between pieces | | prefer-domain-predicate | Unnamed classifications built from several checks |

Type-aware

| Rule | Reports | | ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- | | prefer-truthy-presence-check | Nullish comparisons equivalent to truthiness checks | | max-interpretation-spread | Literal unions interpreted across too many files | | max-projection-spread | Repeated object projections across files | | no-forgeable-validated-result | Validated results callers can construct without validation | | no-caller-enforced-invariant | Caller-side checks that a receiving class does not enforce | | no-repeated-value-group | Groups of literal values written out in several places | | no-repeated-value-policy | The same literal test and outcome in several files | | no-procedural-value-dispatch | Branches on literal values that contain their own control flow | | no-external-variant-behavior | Variant representations built outside a known owner | | no-repeated-selection | Handlers repeating a test their sibling selector already made | | no-repeated-conversion | Conversions written identically in several places | | no-repeated-presence-check | Presence checks a called function already makes | | no-fact-outside-owner | Facts about a type's values established outside its owner | | no-embedded-translation | Variant fields read into the surrounding state instead of a projection | | no-split-result | Outcomes split across optional result fields, or across values a decision sets | | no-overbroad-type-predicate | Type predicates that reject values their declared target permits | | no-lookup-fallback | Lookups naming most literal values and giving the rest one constant | | no-widened-union | string parameters and results compared with literals hiding a union | | no-detached-assertion | Entry assertions that hold only because of an earlier size or key check | | no-default-outside-owner | Defaults of optional properties that readers outside the owner repeat or contradict | | no-detached-key | Map values whose methods are given the key they are stored under | | no-lost-write | Field and element writes replaced before anything can read them | | no-deferred-entry | Entries a local collection only holds back for the one loop over it | | no-inline-scan | Loops emitting values computed from state carried across passes | | no-derivation-outside-owner | Values computed from a property the same way by several readers outside its owner |

max-held-context allows else if chains and no-else-if reports them; early returns satisfy both.

Type-aware setup and limits

  • Typed rules analyse files that belong to a TypeScript 7-compatible tsconfig.json project with dependencies installed. Files outside every project, such as scripts or configuration files no tsconfig.json includes, get syntax rules only. Referenced projects may need declaration outputs built first.
  • prefer-truthy-presence-check requires strictNullChecks.
  • Run tsc too: type errors are not reported, and unresolved types make rules skip checks.
  • Use plain oxlint, not --type-aware. The plugin runs its own bundled backend for Linux, macOS and Windows on x64 and arm64; no Go needed. It starts with every run, which adds about 10 ms when only syntax rules are enabled.
  • Some rules read the whole program, including files outside the lint command. Project code is every non-declaration file outside node_modules; linked workspace packages count.
  • Setup errors fail the run. Editor integration (oxlint --lsp) is not supported.

Development

See the development guide.