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

@monstermann/meta

v0.6.0

Published

Attaches metadata about call sites to the functions that are being called.

Readme

Attaches metadata about call sites to the functions that are being called.

Before:

import { signal } from "signals";

const count = signal(0);

After:

import { signal } from "signals";
const meta = { path: "source.ts", line: 3, name: "count" };

const count = signal(0, meta);

Installation

bun add -D @monstermann/meta

Usage

params lists the functions that receive metadata, and what each of them receives:

import { meta } from "@monstermann/meta";

export default defineConfig({
    plugins: [
        meta({
            params: [
                {
                    module: "signals",
                    function: "signal",
                    position: 2,
                    // signal(0) → signal(0, { path: "source.ts", line: 3, name: "count" })
                    meta: ({ path, line, name }) => ({ path, line, name }),
                },
                {
                    module: "logger",
                    function: "log",
                    position: 2,
                    // log("Hello") → log("Hello", { at: "source.ts:5" })
                    meta: ({ path, line }) => ({ at: `${path}:${line}` }),
                },
            ],
        }),
    ],
});

| Property | Description | | ---------- | ------------------------------------------------------------------------------------------------------------------------------- | | module | The module the function is imported from. Optional, without it everything that is called by that name is matched. | | function | Its name as exported by the module: "signal", "Foo.bar" for Foo.bar(), "default.bar" for members of the default export. | | position | The position of the argument, starting at 1. Missing arguments in between are filled with undefined. | | meta | Returns what the call receives, a record of strings, numbers, booleans or null. |

meta is called with what is known about the call:

| Property | Description | | -------- | -------------------------------------------------------------------------------- | | path | The path of the module, relative to process.cwd(). | | line | The line of the call. | | name | Taken from what the result is assigned to, see Names. | | hmr | Return it to receive callbacks for when the module is replaced, see HMR. |

  • Works with Vite, Rolldown and tsdown.
  • With a module, renamed imports (import { signal as s }) and namespace imports (import * as S) are found, and functions that only share the name are left alone.
  • Calls are skipped when the argument is already taken, when its position is unknown because of spread arguments, or when the record is empty.
  • Calls that receive the same share one record.
  • Strings that are used more than once are declared once and shared between the records.

Namespaces and classes

function follows the members of what is being called:

const meta = ({ name }) => ({ name });

meta({
    params: [
        // Rect.create(0, 0) → Rect.create(0, 0, meta)
        { module: "geometry", function: "Rect.create", position: 3, meta },
        // new Store() → new Store(meta)
        { module: "stores", function: "Store", position: 1, meta },
        // Store.from(items) → Store.from(items, meta)
        { module: "stores", function: "Store.from", position: 2, meta },
        // Your own functions, wherever they are imported from or declared:
        // createThing() → createThing(meta)
        { function: "createThing", position: 1, meta },
    ],
});
  • Methods of instances can only be matched by the name of the variable ("store.add" for store.add()), and this.foo() not at all.
  • Run this plugin before anything that rewrites the calls, such as @monstermann/barrels-treeshake turning Rect.create() into _create().

Standalone

import { transform } from "@monstermann/meta";

const result = transform(code, "source.ts", {
    params: [
        {
            module: "signals",
            function: "signal",
            position: 2,
            meta: ({ name }) => ({ name }),
        },
    ],
});
result?.code;
result?.map;

Options

| Option | Default | Description | | --------- | -------------- | ---------------------------------------------------------------------- | | params | | See above. | | hmr | false | See HMR. | | include | /\.[jt]sx?$/ | Plugin only: RegExp(s), only files whose path matches are transformed. | | exclude | | Plugin only: RegExp(s), files whose path matches are skipped. | | enforce | | Plugin only: "pre" or "post". |

Names

The name of a record is taken from what the result is assigned to:

// "count"
const count = signal(0);

// "createCount"
function createCount() {
    return signal(0);
}

// "state.count"
const state = {
    count: signal(0),
};

HMR

When a module is replaced during development, whatever its previous version created keeps running: effects, subscriptions, timers. A param that returns hmr receives a Set of callbacks that is shared by all calls of the module. The callbacks are called and removed right before the module runs again, and when it is removed.

meta({
    params: [
        {
            module: "effects",
            function: "effect",
            position: 2,
            // effect(fn) → effect(fn, { hmr: hmr })
            meta: ({ hmr }) => ({ hmr }),
        },
    ],
});

The function that receives it registers its cleanup:

import type { MetaHmr } from "@monstermann/meta";

export function effect(fn: () => void, meta?: { hmr?: MetaHmr }) {
    const dispose = start(fn);
    meta?.hmr?.add(dispose);
    return dispose;
}

What the transform adds to a module:

import { effect } from "effects";
const hmr = import.meta.hot
    ? (import.meta.hot.data["@monstermann/meta"] ??= new globalThis.Set())
    : undefined;
if (hmr) {
    const clear = () => {
        for (const cb of hmr) {
            hmr.delete(cb);
            cb();
        }
    };
    clear();
    import.meta.hot.dispose(clear);
    import.meta.hot.prune(clear);
}
const meta = { hmr: hmr };

effect(() => console.log("example"), meta);
  • The plugin enables the hmr option by default in the dev server of Vite, and leaves it off for builds.
  • While the option is off, hmr is left out of the records. A call whose record only consists of it is then left alone.
  • It relies on import.meta.hot, without it hmr is undefined.
  • Remove a callback from the set when the cleanup already happened, to not keep it around until the next update.