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

@nomercy-entertainment/eslint-plugin-player

v0.3.0

Published

ESLint rules that enforce the NoMercy player-trio v2 code standard.

Readme

@nomercy-entertainment/eslint-plugin-player

The player trio (core, video, music) shares one code standard. This plugin makes the mechanical parts of that standard enforceable, so a violation fails lint at write time instead of surfacing in review weeks later. It encodes only the rules that a machine can judge without guessing. Naming intent, comment usefulness, and architecture stay with the code-quality reviewer.

Compatibility

These rules target the v2 player trio (the 2.0.x line of nomercy-player-core, nomercy-video-player, and nomercy-music-player). This package keeps its own version line rather than matching the trio, because the rules evolve on their own cadence: a version bump here means a rule changed, not that the player did. Pin it independently and read this note for the player line it tracks.

Rules

Every rule ships in the recommended preset at error.

| Rule | Catches | |------|---------| | player/no-single-letter-ident | Single-letter parameter and variable names. Allows x y z (maths) and i j k (loops). Covers interface method signatures and overloads, not just implementations. | | player/no-compat-vocab | The v1 factory names, the *StateToken types, the PlayerCore class, and compat markers in comments (@deprecated, "compatible alias", v1-compat). The v2 classes NMVideoPlayer and NMMusicPlayer stay allowed. | | player/no-history-comments | Bug-number history, spec citations (§), and naked TODO/FIXME with no issue reference. | | player/no-object-literal-cast | { ... } as SomeType. Type the object at construction instead. as const is exempt. | | player/no-unknown-cast | x as unknown as T with no adjacent comment. A short justification on the same line or the line above keeps a structurally forced cast legal. | | player/no-raw-player-bus | this.player.on/once/off/emit(...) inside a plugin. Bypasses scoping and auto-dispose; use the plugin's own this.on/once/off/emit. Only fires inside a class that extends Plugin — core mixins are untouched. | | player/no-raw-timers-in-plugin | Raw setTimeout/setInterval/requestAnimationFrame/addEventListener and new ResizeObserver/MutationObserver/IntersectionObserver/PerformanceObserver inside a plugin. No auto-cleanup; use this.timeout/interval/frame/listen and this.lifecycle.observe(new ResizeObserver(...)). | | player/no-raw-throw-in-plugin | throw new Error(...) (and other raw error ctors) inside a plugin. Skips the structured error surface; use this.throw({...}) or this.report({...}). Re-throwing a caught value is allowed. | | player/no-raw-fetch-in-plugin | The global fetch (bare, window.fetch, globalThis.fetch) inside a plugin. Sends no auth and never aborts on dispose; use this.fetch(url, options?) which runs the player's auth pipeline and aborts on teardown. A public URL that must not carry auth takes an eslint-disable with a reason. | | player/plugin-id-required | A concrete class extending Plugin with no own static id. Inheriting the base default 'plugin' collides and breaks storage/mount namespacing. abstract intermediate bases are exempt. |

Wiring

The trio configs consume it from the sibling package:

import player from '../eslint-plugin-player/index.js';

export default antfu({ /* ... */ }, {
  files: ['src/**/*.ts'],
  plugins: { player },
  rules: {
    'player/no-single-letter-ident': 'error',
    'player/no-compat-vocab': 'error',
    'player/no-history-comments': 'error',
    'player/no-object-literal-cast': 'error',
    'player/no-unknown-cast': 'error',
    'player/no-raw-player-bus': 'error',
    'player/no-raw-timers-in-plugin': 'error',
    'player/no-raw-throw-in-plugin': 'error',
    'player/no-raw-fetch-in-plugin': 'error',
    'player/plugin-id-required': 'error',
  },
}, {
  files: ['src/**/*.test.ts', 'src/__tests__/**/*.ts'],
  rules: {
    'player/no-object-literal-cast': 'off',
    'player/no-unknown-cast': 'off',
    'player/no-raw-throw-in-plugin': 'off',
    'player/no-raw-timers-in-plugin': 'off',
    'player/no-raw-player-bus': 'off',
    'player/no-raw-fetch-in-plugin': 'off',
    'player/plugin-id-required': 'off',
  },
});

Test files relax the two cast rules (mock construction legitimately casts) and the boundary rules (test-fixture plugins throw raw errors, use raw timers, fetch, and build ad-hoc plugin classes to drive the real paths).

The boundary rules

no-raw-player-bus, no-raw-timers-in-plugin, no-raw-throw-in-plugin, and no-raw-fetch-in-plugin are the mechanical half of the this-versus-this.player boundary: a plugin extends this and reaches the player through this.player, but its own lifecycle helpers (this.on, this.timeout, this.throw, this.fetch, this.lifecycle.observe, ...) are the sanctioned surface because they scope, auth, and auto-clean. Reaching past them to the raw player bus, raw timers or observers, a raw throw, or the global fetch leaks a listener/observer, drops an error off the structured path, or sends an unauthenticated request that never aborts on teardown. All fire only inside a class that extends Plugin, so core mixins — where this already is the player — never trigger them. A genuinely-needed exception (a third-party emitter that is not an EventTarget, a deliberate re-emit of a dynamically-named player event, a public URL that must not carry auth) takes an eslint-disable-next-line with a one-line reason, the same escape hatch no-unknown-cast uses.

Cross-file checks

Two things eslint cannot see (it lints one file at a time) ship as scripts for CI:

| Script | Asserts | |--------|---------| | scripts/check-package-exports.mjs | Every exports target resolves to a real file, and no subpath reaches into src/. | | scripts/check-duplicate-symbols.mjs | No exported constant, type, or enum is byte-identical in both video and music. Identical bodies are shared code that belongs in core. |

npm run check:exports
npm run check:duplicates

Tests

npm install
npm test