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

@portbay/swc-plugin-loc

v0.1.1

Published

Dev-time SWC plugin that stamps each JSX host element with its authored source location (data-pb-loc) for PortBay's visual editor. SWC/Turbopack parity with @portbay/babel-plugin-loc — covers Next.js App Router.

Downloads

169

Readme

@portbay/swc-plugin-loc

Dev-time SWC plugin that stamps each rendered JSX host element with its authored source locationdata-pb-loc="<file>:<line>:<col>" — so PortBay's visual editor can resolve a clicked element back to the exact span in your source instead of guessing with a text search.

This is the SWC/Turbopack member of the family, for Next.js App Router (which compiles with SWC by default, not Babel). It is byte-for-byte parity with @portbay/babel-plugin-loc and @portbay/vite-plugin-loc:

<button data-pb-loc="app/page.tsx:42:7">Get started</button>
  • file — project-root-relative, POSIX.
  • line1-based line of the element's opening <.
  • col1-based column of that <.
  • Host elements (lowercase <div>, <button>, <my-widget>) get data-pb-loc. Component call sites (<Hero title="…" />) get data-pb-comp as a prop — React copies it into the component fiber's memoizedProps, so PortBay can edit a component's literal props at the JSX call site (see the repo README). Member (<Foo.Bar>) and namespaced names are never stamped.
  • Idempotent, and appended after your attributes so a {...spread} can never override it. A single .map() element yields one source loc shared by every rendered copy.

Dev-only, by design

The attribute is emitted only in development. PortBay wires the plugin into next dev and removes it otherwise; the plugin also self-gates on NODE_ENV (production ⇒ off) when no explicit enabled flag is passed. It never reaches a production bundle — no DOM bloat, no leaking of local file paths.

Unlike the Babel/Vite plugins, there is no PORTBAY_LOC env escape hatch: SWC plugins run in a sandboxed wasm VM with no access to process.env. Pass enabled: true/false through plugin config instead (PortBay does this for you).

Install

pnpm add -D @portbay/swc-plugin-loc

The package ships a prebuilt portbay_swc_plugin_loc.wasm; no Rust toolchain is required to consume it. (Building from source does — see below.)

Config — one entry, both bundlers

// next.config.js
/** @type {import('next').NextConfig} */
module.exports = {
  experimental: {
    swcPlugins: [
      [
        '@portbay/swc-plugin-loc',
        { root: __dirname, enabled: process.env.NODE_ENV !== 'production' },
      ],
    ],
  },
};

The first element must be the bare package specifier, not require.resolve('@portbay/swc-plugin-loc'). require.resolve returns an absolute filesystem path, and Turbopack resolves the swcPlugins entry as a module specifier: on Next 16.3.0 an absolute path fails the compile outright with Module not found: … server relative imports are not implemented yet. The bare specifier is resolved correctly by both Turbopack and webpack, so there is no pipeline that wants the require.resolve form.

root must be the project root the emitted paths should be relative to (usually __dirname); files outside it are skipped rather than stamped with a .. path.

Turbopack (the next dev default since Next 16)

Turbopack reads the same experimental.swcPlugins entry — the config above covers both pipelines. No separate loader is needed.

The two bundlers hand the plugin different filename shapes, and it handles both:

| Bundler | Filename context | Handling | |---|---|---| | webpack / @swc/core | absolute (/proj/app/page.tsx) | root stripped off the front | | Turbopack | already project-root-relative (app/page.tsx) | used as-is |

Both produce identical coordinates. A path that escapes root — absolute and outside it, or relative and climbing above it — is refused rather than stamped with a .. the resolver would reject.

Fixed after 0.1.0. Before the fix, the relative form was fed to Path::strip_prefix, which failed, so the plugin stamped nothing at all under Turbopack — silently, with a successful build and an HTTP 200. That is why every bail now emits a warning (below).

Version note (verify against your Next version): wasm SWC plugin support in Turbopack landed during the Next 15 line and is still evolving. On a Next release where Turbopack ignores swcPlugins entirely, precise editing falls back to the text-search resolver (zero behavior change, just less precision). PortBay's loc_detect reports which pipeline is active.

When it decides not to stamp, it says so

If the plugin is enabled but cannot turn a file into a root-relative path, it emits a warning naming the reason, the configured root, and the filename it was handed — on the plugin's HANDLER diagnostic channel and on the wasm sandbox's stderr. Two channels because whether a HANDLER warning is ever printed is the host's decision (@swc/core's Node binding buffers plugin diagnostics and drops them when the transform succeeds; stderr gets through).

The commonest cause is a root that is not the directory the bundler's filenames are relative to. The warning fires per refused file: swc instantiates a fresh wasm module per transform, so no plugin-side state survives to de-duplicate it.

swc_core ↔ Next compatibility (important)

An SWC wasm plugin is ABI-coupled to the SWC compiler that loads it. The host that matters is @next/swc — the compiler Next.js bundles for next dev / next buildnot the standalone @swc/core on npm (Next does not use it, and it lags @next/swc). This blob is built against:

| Property | Value | |---|---| | swc_core | 71.0.3 | | swc_ecma_ast | 25.0.0 | | Plugin schema | __plugin_transform_schema_v1 |

Verified compatibility

| Host | Result | |---|---| | Next.js 16.3.0 default (Turbopack) | Works at 0.1.1 (2026-08-13) — verified end-to-end against a live next dev: served HTML carries byte-accurate data-pb-loc. Under 0.1.0 the same app served HTTP 200 with zero stamps | | Next.js 16.3.0 (next dev --webpack) | Works at 0.1.1 (2026-08-13) — served HTML data-pb-loc set is identical to the Turbopack run | | Next.js 16.2.x (webpack lane) | Works (2026-07-04) — data-pb-loc stamped in the rendered DOM, byte-accurate | | Standalone @swc/core 1.15.47 | Works (2026-07-31) — loads and stamps; the earlier "AST schema not compatible" no-op no longer reproduces on this version | | Standalone @swc/core ≤ 1.14.x | Hard error — failed to invoke plugin |

Two failure modes

  • Host older than the blob → the compile fails loudly (failed to invoke plugin).
  • Host rejects the AST schema (e.g. standalone @swc/core, or a Next that bundles an older @next/swc) → the plugin is silently skipped: no stamp, no error, next reports success. Precise editing then degrades to text search. PortBay's browser bar detects this (installed + configured but no [data-pb-loc] in the DOM) and shows a "wired but not active" warning.

When your Next version bundles an @next/swc outside this blob's range, rebuild from source pinned to a matching swc_core (see https://plugins.swc.rs/), bump the minor, and re-verify. PortBay pins and rebuilds this blob per supported Next range; the version above is what ships in this package.

Build from source

Requires the Rust toolchain and the wasm target:

rustup target add wasm32-wasip1
pnpm build      # cargo build --release + copy the .wasm to the package root
pnpm test       # cargo test — the parity suite (mirrors the babel vitest cases)

License

MIT © Tribal House LLC. Independent implementation; no third-party plugin code.