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

@hazae41/saumon

v1.0.3

Published

Ultra simple macro system for TypeScript

Readme

Saumon 🐟

Ultra simple macro system for TypeScript

deno install -gf -RW npm:@hazae41/saumon --unstable-worker-options

📦 NPM

Goals

  • Ultra simple and minimalist
  • Secured with Deno permissions
  • Won't interfere with your existing tools
  • Can output arbitrary code (TypeScript types, JSX components, JSON data)
  • Resistant to supply-chain attacks

Example

Compile-time code evaluation

data.macro.ts (input)

const data = $$(() => fetch("/api/data").then(r => r.json()).then(JSON.stringify))

data.ts (output)

const data = { ... }

Compile-time code generation

log.macro.ts (input)

import { $$ } from "@hazae41/saumon"

$$(() => `console.log("hello world")`)

log.ts (output)

console.log("hello world")

Setup

Install Deno

npm install -g deno

Install Saumon with Deno

deno install -gf -RW npm:@hazae41/saumon --unstable-worker-options

Usage

A macro is like a regular JS function, but the compiler will replace all its calls by the string value it returns

CLI

You can transform a single file

saumon ./src/test.macro.ts

Or a whole directory

saumon ./src/**/**

Files

The compiler will only transform files with .macro.* extensions

This is good for performances because it won't parse all your code

And this is good for security because it will only run code in there

Typing

All macros must be called with $$(() => Awaitable<string>)

export declare function $$<T>(f: () => Awaitable<string>): T

You can spoof the returned type to avoid warnings while you code

const x = $$<number>(() => `${Math.random()}`) * 100

Imports

You can import anything in a macro call

log.ts

export function $log$(x: string) {
  return `console.log("${x}")`
}

main.macro.ts

import { $$ } from "@hazae41/saumon"

$$(async () => {
  const { $log$ } = await import("./log.ts")

  return $log$("hello world")
})

You can even import things from libraries

main.macro.ts

import { $$ } from "@hazae41/saumon"

$$(async () => {
  const { $log$ } = await import("some-lib")

  return $log$("hello world")
})

Async

You can define and run async macros

Just return a Promise and the compiler will wait for it

fetch.macro.ts

import { $$ } from "@hazae41/saumon"

const data = $$(() => fetch("https://dummyjson.com/products/1").then(r => r.json()).then(JSON.stringify))

fetch.ts

export const data = { "id": 1 }

You can also await macroed code

promise.macro.ts

import { $$ } from "@hazae41/saumon"

const x = await $$<Promise<number>>(() => `Promise.resolve(123)`)

promise.ts

const x = await Promise.resolve(123)

Shared variables

When calling a macro, in-file local variables are NOT accessible

This is because macro calls are ran isolated from their surrounding code (in a worker)

They can still access imports, so you can put shared things in some file, and/or pass them

x.ts

export const x = Math.random()

main.macro.ts

import { $$ } from "@hazae41/saumon"

const x = $$<number>(async () => {
  const { x } = await import("./x.ts")

  console.log(`x is ${x}`)

  return `${x}`
})

console.log(`x is ${x}`) // exact same as above

Permissions

If you use Deno as your runtime, you can benefit from it's permissions based-security

$ deno run -RW ./src/bin.ts ./test/fetch.macro.ts
┏ ⚠️  Deno requests net access to "dummyjson.com:443".
┠─ Requested by `fetch()` API.
┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.
┠─ Learn more at: https://docs.deno.com/go/--allow-net
┠─ Run again with --allow-net to bypass this prompt.
┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all net permissions) > y
✅ Granted net access to "dummyjson.com:443".

Each macro call will have its own independent permissions

So when you type A it's always within the same macro call

Security

Macro files are transformed ahead-of-time by the developer.

This means the output code is fully available in the Git, and won't interfere with code analysis tools.

The macro code SHOULD only be transformed when needed (e.g. when modified, when the fetched data is stale), and its output SHOULD be verified by the developer.

The developer SHOULD also provide the input macro file in the Git, so its output can be reproducible by people and automated tools.