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

preact-alchemy

v1.0.0

Published

A tiny transform that turns `let` bindings into @preact/signals when you opt in with a "use alchemy" directive. It lets you write normal JS and get reactive updates without manual `.value` plumbing.

Readme

preact-alchemy

A tiny transform that turns let bindings into @preact/signals when you opt in with a "use alchemy" directive. It lets you write normal JS and get reactive updates without manual .value plumbing.

Install

pnpm add preact-alchemy

Quick start

Add the directive as the first statement inside a function body:

function counter() {
  'use alchemy'
  let count = 0
  count += 1
  return count
}

This is rewritten to:

import { signal as __alchemy_signal } from '@preact/signals'
function counter() {
  let count = __alchemy_signal(0)
  count.value += 1
  return count.value
}

How it works

When a function body starts with the string literal directive "use alchemy", the transform:

  • Rewrites let declarations in that function into signals.
  • Rewrites reads/writes of those bindings to use .value.
  • Projects object literal shorthands that reference reactive bindings.
  • Injects import { signal as __alchemy_signal } from "@preact/signals"; when needed.

Scope rules

Only let declarations are restricted: let declarations inside loops or nested functions are not converted into signals. Reads and writes of a reactive binding remain reactive anywhere it is in scope, including inside loops and nested functions.

function demo() {
  'use alchemy'
  let count = 0 // becomes a signal

  for (let i = 0; i < 2; i++) {
    count++ // reactive read/write
    let local = 0 // NOT converted
  }

  function inner() {
    count++ // reactive read/write
    let local = 0 // NOT converted
  }
}

Object literal projection

Shorthand properties referencing a reactive binding are rewritten so you don’t leak signals by accident:

return { count }
  • At the top level: becomes a getter so reads stay reactive.
  • Inside loops/nested functions: becomes an eager value.
return {
  get count() {
    return count.value
  },
}
// or
return { count: count.value }

Destructuring

Destructuring let declarations are supported. Each extracted name becomes a signal:

let { a, b } = obj
// ->
let { a, b } = obj
a = __alchemy_signal(a)
b = __alchemy_signal(b)

Vite plugin

Use the built-in Vite plugin for zero-config integration:

// vite.config.ts
import { defineConfig } from 'vite'
import preactAlchemy from 'preact-alchemy/vite'

export default defineConfig({
  plugins: [preactAlchemy()],
})

The plugin:

  • Runs on .js/.jsx/.mjs/.cjs/.ts/.tsx/.mts/.cts files, after Vite compiles TypeScript to JavaScript.
  • Skips files in node_modules.
  • Only transforms files that include the "use alchemy" directive.

Programmatic API

import { transform } from 'preact-alchemy'

const result = transform(code, id)
console.log(result.code)
console.log(result.map) // Source map if changes were made

transform(code, id?)

  • code: string input source.
  • id: optional file name for warnings and source maps.
  • Returns { code, map? }.

Limitations

  • JavaScript + JSX only. TypeScript/Flow syntax is not supported and is skipped with a warning. TypeScript is supported only after compilation to JavaScript (for example, via the Vite plugin).
  • Only let declarations are converted to signals. const and var are left untouched.
  • The directive must be the first statement in the function body.

Tips

  • You can keep normal JS semantics and sprinkle reactivity only where needed.
  • If you already use a __alchemy_signal variable, the import is auto-aliased.

License

MIT