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

swc-loop-protect

v0.1.0

Published

An SWC plugin that protects against infinite loops and infinite recursion in dynamically evaluated JavaScript.

Readme

swc-loop-protect

An SWC plugin that protects against infinite loops and infinite recursion in dynamically evaluated JavaScript.

Inspired by @freecodecamp/loop-protect (a Babel plugin), rewritten from scratch in Rust as a native SWC/WASM plugin.

This does not solve the halting problem — it rewrites JavaScript at compile time, inserting time-based guards into loops and depth-based guards into functions.

Loop Protection

All loop types — while, for, do-while, for...in, and for...of — are wrapped with a Date.now() check that breaks out of the loop if it runs longer than a configurable timeout.

Input:

while (true) {
  doSomething();
}

console.log('All finished');

Output:

var _LP1 = Date.now();
while (true) {
  if (Date.now() - _LP1 > 100) break;
  doSomething();
}

console.log('All finished');

The loop is cleanly exited and any code after it continues to execute.

Suspending loops are skipped

Loops containing await or yield are skipped — they suspend execution, so a wall-clock timeout would fire incorrectly. for await...of loops are also skipped. An await or yield inside a nested function does not cause the outer loop to be skipped.

Iteration-based checking

For code with large but finite loops, enable the iterations option. Date.now() is only checked once every N passes, reducing overhead:

var _LPC1 = 1;
var _LP1 = Date.now();
while (true) {
  if (_LPC1++ % 100 === 0 && Date.now() - _LP1 > 100) break;
  doSomething();
}

Recursion Protection

When enabled, every function and arrow expression is wrapped with a global call-depth counter on globalThis._RD. This catches both direct and indirect recursion across files.

Input:

function foo() {
  foo();
}

Output:

function foo() {
  globalThis._RD = (globalThis._RD || 0) + 1;
  if (globalThis._RD > 1000) {
    globalThis._RD = 0;
    throw new Error("Maximum recursion depth exceeded");
  }
  try {
    foo();
  } finally {
    globalThis._RD--;
  }
}

Key details:

  • The counter lives on globalThis._RD, so it is shared across all files/modules.
  • try/finally ensures the counter is decremented even if the function throws.
  • The counter is reset to 0 before throwing, so the error can be caught without leaving the counter in a broken state.
  • Expression-body arrows (() => expr) are automatically converted to block bodies.
  • Disabled by default — set maxRecursionDepth in the plugin config to enable.

Configuration

The plugin accepts a JSON config object:

| Option | Type | Default | Description | | ------------------- | ---------------- | ------- | ------------------------------------------------------------- | | timeout | number | 100 | Max loop runtime in milliseconds before break | | iterations | number \| null | null | Only check Date.now() every N loop iterations | | maxRecursionDepth | number \| null | null | Max call depth before throwing (enables recursion protection) |

Example SWC config (.swcrc)

{
  "jsc": {
    "experimental": {
      "plugins": [
        ["swc-loop-protect", { "timeout": 100, "maxRecursionDepth": 1000 }]
      ]
    }
  }
}

Building

# Native (for testing)
cargo test

# WASM plugin binary
cargo build-wasip1 --release

The WASM binary is output to target/wasm32-wasip1/release/swc_loop_protect.wasm.

License

MIT — based on the original loop-protect by Remy Sharp and contributors.