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

@sister.software/oxlint-config

v11.0.0

Published

Sister Software's oxlint config

Readme

@sister.software/oxlint-config

Sister Software's shared oxlint configuration.

Replaces @sister.software/eslint-config. It preserves the two features that mattered most — platform-layering import boundaries and license-header enforcement — while shedding the ESLint plugin ecosystem that made major upgrades painful.

Install

yarn add -D @sister.software/oxlint-config oxlint

oxlint is a peer dependency. Node >=24 is required (oxlint runs the .ts config and the JS header plugin via Node).

Usage

Create an oxlint.config.ts (oxlint auto-discovers it):

import { createOxlintConfig } from "@sister.software/oxlint-config"

export default createOxlintConfig({
	spdxLicenseIdentifier: "AGPL-3.0",
})

createOxlintConfig() returns a complete oxlint config object — there is no extends step.

Options

| Option | Default | Purpose | | ------------------------ | ------------------------- | ----------------------------------------------------------- | | packageNamespace | @sister.software | Namespace the runtime-boundary rules apply to. | | copyrightHolder | Sister Software | @copyright value stamped into file headers. | | spdxLicenseIdentifier | UNLICENSED | @license value stamped into file headers. | | author | Teffen Ellis, et al. | @author value stamped into file headers. | | react | false | Enable oxlint's React plugin. | | headers | true | Enforce file headers (error severity, auto-fixed). | | padding | true | Blank line before return/block-like statements. | | consolePadding | true | Blank line on each side of a console.* call. | | multilineStatementPadding | true | Blank line on each side of a multi-line statement. | | braces | true | Braces around single-statement bodies. | | restrictProcessGlobals | false | Forbid direct process.env / process.argv access. | | limits | calibrated defaults | Override individual legibility ceilings. | | testFilePatterns | **/*.test.ts, … | Globs where the size and constant rules switch off. | | generatedFilePatterns | **/*.gen.ts, … | Globs where only the size ceilings switch off. | | untypedFilePatterns | **/*.js, **/*.mjs, … | Globs where the multi-line JSDoc requirement switches off. | | unnamedThresholds | false | Flag numeric literals used as comparison thresholds. | | constantDocs | false | Require JSDoc on exported / SCREAMING_CASE constants. | | lengthTruthiness | true | Rewrite explicit length comparisons to truthiness. | | multilineJSDoc | true | Require JSDoc blocks to span multiple lines. | | sectionMarkers | true | Enforce the banner → MARK → region → split ladder. | | ignorePatterns | build output, .yarn | Replace the default ignore list. | | overrides | {} | Extra config merged last (escape hatch). |

limits takes any subset of the ceilings; unspecified keys keep their calibrated default:

createOxlintConfig({
	spdxLicenseIdentifier: "AGPL-3.0",
	limits: { maxStatements: 60, complexity: 40 },
})

Generated files are exempt from the size ceilings only — every correctness rule still applies, because generated code ships. Point generatedFilePatterns at your own globs if they differ from the defaults (**/*.gen.ts(x), **/*.generated.ts, **/generated/**).

Features

Platform layering

Files are matched by their final name segment, and cross-runtime imports are flagged via core no-restricted-imports:

  • browserclient, browser
  • nodeserver, node, sdk
  • agnosticshared, common
  • workerworker

A *-client file importing a *-server package (or vice versa) is an error; *-shared/*-common files may not import Node built-ins at all.

License headers

A bundled oxlint JS plugin (sister-software/require-file-header) reports and auto-fixes the @copyright/@license/@author header, preserving any other JSDoc tags already in the leading comment block. Run oxlint --fix to stamp headers.

Statement padding

padding-lines (warn, autofixed) puts a blank line before the statements that end a path or a step — return, continue, break, a bare x++/x-- — and before block-like statements. An interface, type, enum or namespace with a body counts as block-like: same braces, same weight on the page.

multiline-statement-padding (warn, autofixed) puts one on each side of a statement that spans lines, so a reader sees where it starts and stops without matching brackets:

const canonical = {          const canonical = {
	raw,                            raw,
	source,                         source,
}                     →      }
const aligned = align()
                             const aligned = align()

An exported declaration is padded exactly like the unexported form — both rules look through the export wrapper.

Neither edge is required at a block boundary, where the brace already separates. console.* calls are left to the rule below, which groups consecutive ones — this one would split a run that happens to contain a tall call, and a run of output is one thing.

Console padding

console-padding (warn, autofixed) puts a blank line on each side of a console.* call, so the lines that produce output stand apart from the lines that compute it:

const total = sum(stats)
console.log(`${total} shards`)      const total = sum(stats)
const headers = ["source"]
console.log(headers)          →     console.log(`${total} shards`)

                                    const headers = ["source"]

                                    console.log(headers)

A RUN of calls is one group — no blank lines inside it, one before the first and one after the last, because consecutive calls are one block of output. Neither edge is required where a block boundary already separates them: nothing before a call that opens a block, nothing after one that closes it.

Section markers

Four rules over the comments people use to carve a file into sections. They form one ladder, and each rung is a smaller file than the last:

// ---------------------------------------------------------------
// Second section                     →  // MARK: Second section
// ---------------------------------------------------------------
  1. prefer-mark-comment (error, autofixed) — a ---- banner is three lines of decoration carrying one label. It becomes // MARK: <label>, with a blank line on each side. A banner wrapping more than one line of prose is reported but never collapsed — that prose is content.
  2. concise-section-marker (error) — a marker's label is a label, not a paragraph. Past maxBodyLength (60), the detail belongs in the JSDoc of what it describes, or the file wants splitting and an @file block.
  3. prefer-region-over-marks (warn) — past one marker, a file has sections, and sections have ENDS. //#region / //#endregion fold, so a reader can collapse what they are not reading.
  4. max-regions (warn) — past maxRegions (10), the sections want to be files. Deliberately high: four well-named regions are usually fine, and splitting can separate a type from its helpers. This is a runaway detector, not a style nudge.

Rungs 3 and 4 warn rather than error: where a section ends, and whether it should move, are judgment calls no fixer gets to make. Both switch off in test files, where the suite's shape is dictated by the code under test.

createOxlintConfig({ sectionMarkers: { maxBodyLength: 80, maxRegions: 6 } })

Multi-line JSDoc

multiline-jsdoc (error, autofixed) expands a one-line block:

/** The answer. */          →     /**
                                   * The answer.
                                   */

A one-line block reads as an aside; the multi-line form reads as documentation, and it leaves somewhere to put the second sentence. Only blocks that lead their line are rewritten — one sharing a line with code is a type cast, and expanding it changes what the line means. Off for untypedFilePatterns, where a JSDoc block often IS the type annotation.

Legibility tiers

v10 turns on four tiers of upstream rules by default:

  1. Structural guardrails — nesting depth, function and file size, parameter count, cyclomatic complexity. The numbers are ceilings, calibrated so considered code stays silent and runaway generation does not. Tune them with limits.
  2. Defect classes correctness misses — import cycles, in-place .sort()/.reverse() on arrays that may be shared, hook-order violations, .then() without a return, dead stores.
  3. Test discipline — the vitest plugin, including expect-expect, which catches a test that runs, passes, and asserts nothing.
  4. Mechanical hygiene — literal form, import discipline, modern-API preference. All autofixable via oxlint --fix.

The design record lives in docs/superpowers/specs/2026-07-28-oxlint-legibility-tiers-design.md, including a register of the rules that were measured and turned down. Check it before enabling something that looks like an oversight — no-magic-numbers in particular is off on purpose, and verify-fixtures.mjs fails the build if it or eleven others are switched on.

Named thresholds and documented constants

Two opt-in rules, complements rather than duplicates:

  • unnamedThresholds flags a number used as a comparison threshold — if (entries.length > 12). Numbers inside array and object literals are untouched, so a bounding box or a codepoint table stays silent. Radix-prefixed literals are exempt by default: cp >= 0x3040 already reads as a codepoint boundary.
  • constantDocs requires a JSDoc block on module-level constants that are exported or SCREAMING_CASE — the two signals that a constant is public surface or a tuning knob. Function-valued constants are out of scope.

The first says a threshold needs a name; the second says a name needs an explanation. A data table satisfies both by documenting the table, rather than extracting a constant per row.

Write provenance in those blocks — what the value means and where it came from:

/** Entries above this count mean the surface is too ambiguous to disambiguate; set by the 2026-03 sweep. */
const AMBIGUITY_CEILING = 12

Not /** The ambiguity ceiling. */, which leaves the reader exactly where they started.

Length checks

The house convention is truthiness — if (items.length), not if (items.length > 0). sister-software/prefer-length-truthiness enforces it and autofixes, which is why unicorn/explicit-length-check (which enforces the opposite) is off.

It only rewrites where the value is already coerced to a boolean — a condition, a ternary test, the operand of !, or a &&/|| inside one of those. Elsewhere the comparison is the value, and rewriting it would change the type:

if (items.length > 0) { … }              // → if (items.length)
if (items.length === 0) { … }            // → if (!items.length)
const hasItems = items.length > 0        // untouched — this is a boolean, not a condition
if (items.length === 2) { … }            // untouched — not an emptiness check

Escape hatch

Both rules, and every tier rule, take a scoped disable with a stated reason:

// oxlint-disable-next-line max-statements -- generated dispatch table; splitting it hides the shape.

Prefer that over a config-level exemption, which silently widens to every future file.