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

@accesslint/source

v0.1.1

Published

Audit component source (JSX/TSX) for accessibility from a real parse — synthetic HTML into @accesslint/core, with source lines and no guesses.

Readme

@accesslint/source

Audit component source for accessibility. Parses a JSX/TSX file, renders its intrinsic elements as HTML, and runs @accesslint/core over the result — so a finding comes back with the rule, the message, and the line in the file you wrote.

No bundler, no dev server, no rendering, and no execution of the code being audited. Point it at a file and it answers.

The contract

What the source does not pin down produces silence, not a guess. A finding this package returns is a defect at runtime whatever the unknowns turn out to be. Everything else comes back as a candidate — the violation the engine reported, plus the unknown that stopped it from being a finding.

That split is the point. Findings are safe to post as review comments on a protected branch. Candidates are input for a later layer that can go get the missing evidence (the component's own definition, the spread's source) and decide.

False negatives are the accepted price.

Usage

import { auditSource } from "@accesslint/source";
import { Window } from "happy-dom";

const window = new Window();
for (const key of Object.getOwnPropertyNames(window)) {
  if (!globalThis[key]) globalThis[key] = window[key];
}
globalThis.getComputedStyle = window.getComputedStyle.bind(window);

const { findings, candidates } = auditSource({
  source: await readFile("Avatar.tsx", "utf8"),
  filename: "Avatar.tsx",
  document: window.document,
});

for (const finding of findings) {
  console.log(`${finding.file}:${finding.line} ${finding.ruleId} — ${finding.message}`);
}

The DOM is yours to provide: happy-dom, jsdom, or a real browser, along with the globals the engine's rules read (getComputedStyle, the element constructors). The package brings no DOM of its own.

renderJsx is exported too, for looking at the synthetic HTML directly.

What it can and cannot see

| The source says | The audit reads it as | | -------------------------------------- | ---------------------------------------------------------------------------- | | <img src="/a.png" /> | an intrinsic element, fully audited | | <img src={url} /> | src present with an unknown value; a missing alt here is still a finding | | <img {...props} /> | absence unprovable — every missing-attribute finding becomes a candidate | | <img {...props} alt="" /> | alt provably empty: nothing after a spread can override it | | <img alt="" {...props} /> | alt unknown: the spread can override it | | <Button>…</Button> | the component vanishes, its children are audited in place | | <ul><MenuItem /></ul> | the list's contents are unknown — container rules go quiet | | <ul>{items.map((i) => <div />)}</ul> | the <div> is real, and reported | | <h1>{title}</h1> | a stand-in name, so "empty heading" is not claimed | | {cond ? <a /> : <b />} | both arms, so document-order rules go quiet for the file | | style={{ display: "none" }} | hidden, exactly as at runtime | | aria-hidden={flag} | may not be in the accessibility tree: the subtree goes quiet | | a file that does not parse | no findings at all |

Every file is treated as a component, so page-level rules never run — with one exception: a file containing a literal <html> tag (a Next.js root layout, a Remix root) is asked whether it set lang, because all of that evidence is in the one tag.

Rendering-dependent rules (contrast, spacing, focus visibility) and cross-element idref rules are off in source mode. SOURCE_MODE_DISABLED_RULES is exported if you want to see the list.

Two kinds of file are skipped outright, both because a finding in one could not be a defect a user hits: JSX rendered by something that is not a DOM (satori, @vercel/og, Next's image modules), and test files and fixtures, whose markup is written to be wrong. includeTestFiles turns the second one back on.

Coverage today

JSX and TSX, measured against ten popular React repositories — 12,557 files, 109 findings, zero confirmed false positives after hand-triaging every one. What the first run got wrong lives on as the "what the corpus taught" tests in src/audit.test.ts.

Vue and Svelte map onto the same semantics and are next, each behind the same gate.