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

@kindly-note/legacy-plugin-adapter

v0.1.0

Published

Adapter that wraps an upstream highlight.js-shaped HLJSPlugin (six legacy before:* / after:* hooks) into a modern @kindly-note/core Plugin. Mutation semantics quarantined inside the adapter; the modern protocol stays pure.

Readme

@kindly-note/legacy-plugin-adapter

Wraps an upstream highlight.js-shaped plugin ({ 'before:highlight': fn, 'after:highlight': fn, ... }) onto kindly-note's modern Plugin protocol — mutation semantics quarantined, per-phase tree-shaking preserved.

The upstream highlight.js@11 plugin shape is six optional hooks that mutate a shared context. kindly-note's modern protocol (@kindly-note/core spec §2) is six pure transforms with per-phase tree-shaking and per-plugin error isolation. This adapter is the bridge: pass a real upstream-ecosystem plugin (highlightjs-line-numbers.js, highlightjs-copy-clipboard, highlightjs-badge, …) verbatim, get back a modern Plugin you can hand to createHighlighter.

Install

npm install @kindly-note/core @kindly-note/legacy-plugin-adapter
# or: bun add / pnpm add / yarn add

@kindly-note/core is a peer dependency.

Usage

import { createHighlighter } from '@kindly-note/core';
import { adaptLegacyPlugin } from '@kindly-note/legacy-plugin-adapter';
import javascript from '@kindly-note/lang-javascript';
import lineNumbers from 'highlightjs-line-numbers.js';

const hl = createHighlighter({
  languages: [javascript],
  plugins: [adaptLegacyPlugin(lineNumbers, 'line-numbers')],
});

const { value } = hl.highlight('const x = 1;\nconst y = 2;', { language: 'javascript' });
// value is the line-numbered table HTML the legacy plugin produced.

The optional second argument is a diagnostic name; the returned plugin's name is legacy:<name> (default: legacy:legacy).

How an after:highlight mutation propagates through

highlightjs-line-numbers.js rewrites result.value from inside an after:highlight hook. Inside the adapter, that hook runs against a shallow mutable copy of the engine's frozen HighlightResult; the adapter returns the mutated copy as the next immutable result in the modern pipeline. Subsequent plugins, the emitter consumer, and hl.highlight()'s caller all see the rewritten .value — exactly as if the plugin had been registered on upstream highlight.js. The engine's original HighlightResult is never touched.

The same threading works for before:highlight mutations of code / language, and for before:highlightElement mutations of language.

Hook mapping

The six legacy hooks map onto kindly-note's modern phases as follows (architect-spec.md §3.3):

| Legacy hook | Modern phase | Notes | |----------------------------|---------------------------------------------|-------| | before:highlight | transformCode (+ shortCircuit) | Mutable { code, language, result? } shim. Setting ctx.result short-circuits the engine; the adapter stashes it in PluginContext.state and returns it from shortCircuit. | | after:highlight | transformResult | Shallow { ...result } copy; mutated copy becomes the next immutable result. | | before:highlightElement | beforeElement | Mutable { el, language } shim; el is by-reference passthrough. | | after:highlightElement | afterElement | Shallow-copy of result so legacy mutations of result.value don't leak back into the engine's frozen result. | | before:highlightBlock | beforeElement (deprecated alias) | Pre-upgraded onto before:highlightElement à la upstream upgradePluginAPI. When the modern hook is also present, the modern wins and the deprecated is not also fired. | | after:highlightBlock | afterElement (deprecated alias) | Same upgrade pattern. |

Mutation faithfulness — what the upstream contract guarantees — is preserved inside the adapter. The modern protocol still only ever sees pure transforms.

The adapter only assigns the modern phases the legacy plugin actually uses, so per-phase tree-shaking (@kindly-note/core spec §2.7) is preserved: a legacy plugin that only sets after:highlight produces a modern Plugin with transformResult defined and every other phase undefined. The engine skips absent phases at zero cost.

API

import type { Plugin } from '@kindly-note/core';

/**
 * Wrap an upstream-shaped LegacyHLJSPlugin into a modern Plugin.
 * The returned plugin's name is `legacy:<name>` (default: `legacy:legacy`).
 */
export function adaptLegacyPlugin(
  legacy: LegacyHLJSPlugin,
  name?: string,
): Plugin;

/** Verbatim from highlight.js@11's types/index.d.ts:126-134. All hooks optional. */
export interface LegacyHLJSPlugin {
  'before:highlight'?(context: LegacyBeforeHighlightContext): void;
  'after:highlight'?(result: HighlightResult): void;
  'before:highlightElement'?(args: LegacyBeforeHighlightElementData): void;
  'after:highlightElement'?(args: LegacyAfterHighlightElementData): void;
  /** Deprecated upstream — scheduled for removal in highlight.js v12. */
  'before:highlightBlock'?(args: LegacyBeforeHighlightBlockData): void;
  /** Deprecated upstream — scheduled for removal in highlight.js v12. */
  'after:highlightBlock'?(args: LegacyAfterHighlightBlockData): void;
}

The four per-hook arg types — LegacyBeforeHighlightContext, LegacyBeforeHighlightElementData, LegacyAfterHighlightElementData, LegacyBeforeHighlightBlockData, LegacyAfterHighlightBlockData — are also exported as named types for plugin authors who want to type their own hook implementations.

The adapter does not mutate the caller-supplied legacy object: it clones before pre-upgrading deprecated hooks, so the same plugin can be passed to multiple highlighters without double-shimming.

Status

V0. Public surface — adaptLegacyPlugin and LegacyHLJSPlugin — is stable and matches architect-spec.md §3.2 normative. Sync-only, no DOM at runtime (DOM types only for the legacy Element references), no Node built-ins; runs unmodified on browsers, Node, Deno, Bun, and Workers/Edge.

License

MIT — see LICENSE.

Co-Authored-By: Claude Opus 4.7 (1M context) [email protected]