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

@codehz/preeval

v0.5.0

Published

Publish-build pre-evaluation of imported ESM calls with optional runtime materialization

Readme

@codehz/preeval

Build-time pre-evaluation of marked constants and selected statically imported ESM function calls. The plugin serializes static results into the author-side publish build and can pass those payloads through explicit runtime materializers for values such as Map, class instances, proxies, or registration handles.

Install

bun add -d @codehz/preeval
npm i -D @codehz/preeval
pnpm add -D @codehz/preeval

Mark constants

Put a // @preeval line comment directly above a top-level const (exported or not):

// @preeval
export const answer = 40 + 2

// @preeval
const label = ['a', 'b'].join('-')
export { label }

const base = 40
// @preeval
const local = base + 2
export const out = local

Rules:

  • Line comment only, exact text @preeval (whitespace around it is trimmed).
  • Must be the closest comment above the declaration; blank lines between the comment and the declaration are allowed, other comments are not.
  • The declaration must be a module top-level const with a single identifier declarator and an initializer. Function/block-local consts, destructuring targets, and multi-declarator declarations are ignored.
  • Export is optional: export const x, const x; export { x }, and non-exported const x are all valid targets.
  • The value must be JSON-superset (see Value domain).

Configure imported functions

Use rules to define an explicit match → evaluate → optional materialize pipeline:

import preeval from '@codehz/preeval/vite'

export default defineConfig({
  plugins: [
    preeval({
      rules: [
        {
          match: { from: 'message-compiler', import: 'compile' },
        },
        {
          match: { from: 'runtime-package', import: 'compile' },
          evaluate: { from: 'pure-package', import: 'compilePure' },
          materialize: { from: 'runtime-package', import: 'materialize' },
          onFailure: 'warn',
        },
      ],
    }),
  ],
})

match selects the original binding by host-resolved module identity and ESM export name. When the bundler exposes a resolver (Rollup/Vite/Rolldown this.resolve, esbuild build.resolve, Bun Bun.resolveSync), match.from is resolved against root (default process.cwd()) and each import source against the current file; equal resolved ids match even when the written specifiers differ (./x vs ../lib/x.js, aliases, extension resolution). Without a host resolver, matching falls back to exact source-string equality. Matching is binding-aware and supports named imports, default imports, import aliases, and static namespace properties (ns.fn() / ns['fn'](), including the same forms as tagged-template tags). Direct calls and tagged templates are macro sites. Local aliases, re-exports/barrels, CommonJS require, dynamic imports, and computed property expressions are not followed.

evaluate selects the build-time callee/tag and defaults to match. The evaluator receives the original call arguments or tagged-template inputs. It must return a synchronous JSON-superset payload. The evaluator import exists only in the temporary evaluation module and is never emitted.

materialize is an optional hard runtime boundary. A successful site emits materialize(<serialized payload>); the materializer receives exactly one argument, never the original arguments or template inputs. It is imported only by the final emitted module and is never loaded or executed during pre-evaluation. Its return value may be any runtime value, including Map, class instances, proxies, and registration handles.

Every matched syntactic call or tagged template runs once during the build even inside a function, loop, or dead control-flow branch. Payload inputs may use literals, ambient globals, imports, stable module-level const/function/class/enum dependencies, and lexical bindings declared within that argument or interpolation. Captures of outer function/block bindings are rejected, as are let/var, reassigned module bindings, this, arguments, super, new.target, and import.meta.

If a later automatic target or marked binding depends directly or transitively on a materialized result, pre-evaluation stops at that boundary. The outer target follows its own failure policy instead of importing or executing the materializer at build time.

Usage

The plugin only runs during your publish build (Vite library mode, Rollup, Rolldown, esbuild, or Bun.build). It is a no-op for consumer builds, and consumers never need it.

Vite

import preeval from '@codehz/preeval/vite'

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

Rollup

import preeval from '@codehz/preeval/rollup'

export default {
  plugins: [preeval()],
}

Rolldown

import preeval from '@codehz/preeval/rolldown'

export default {
  plugins: [preeval()],
}

esbuild

import { build } from 'esbuild'
import preeval from '@codehz/preeval/esbuild'

await build({
  entryPoints: ['src/index.ts'],
  bundle: true,
  outfile: 'dist/index.js',
  plugins: [preeval()],
})

esbuild does not honor enforce. Keep this plugin ahead of other plugins that rewrite the same modules. For tsup, use @codehz/preeval/esbuild (default) or @codehz/preeval/rollup when tsup is configured for the Rollup path.

Bun.build

import preeval from '@codehz/preeval/bun'

await Bun.build({
  plugins: [preeval()],
})

Options

| Option | Default | Description | | ----------- | ------------------- | --------------------------------------------------------------------------------------------------- | | onFailure | 'warn' | Global ignore \| warn \| error policy for marked bindings and automatic calls. | | rules | [] | Explicit match, optional evaluate, optional materialize, and optional rule-level onFailure. | | root | process.cwd() | Base directory for resolving relative rules[].match.from values under host resolve. | | include | /\.[cm]?[jt]sx?$/ | File-id filter passed to the bundler. | | exclude | /node_modules/ | File-id filter passed to the bundler. |

When rules is empty, the plugin retains the marker-only source filter. When any automatic rule exists, every file passing include/exclude is inspected because substring filtering could miss aliased or namespace calls.

strict is not accepted; use onFailure: 'error'.

Failure policy

  • ignore: preserve the original failed call/tag or marked initializer and emit no diagnostic.
  • warn (default): preserve the failed target and emit a source-located warning.
  • error: report through the bundler error hook and abort the build.

Rule-level onFailure overrides the global policy only for that automatic target. Configuration errors, including malformed references, missing fields, and duplicate match references, always throw when the plugin is created.

Automatic targets run before marked bindings. A failed automatic target cannot be executed again through a containing marked initializer. Materialized results similarly block every outer automatic or marked closure that reaches them; unrelated marked targets continue evaluating.

Value domain

Marked values and automatic evaluator payloads must be JSON-superset values that round-trip through JSON without information loss:

  • null, booleans, strings, finite numbers
  • plain objects (prototype Object.prototype or null) and arrays, without circular references

Rejected evaluator payloads: undefined, NaN, ±Infinity, bigint, functions, symbols, Date, Map, Set, class instances, and circular structures. A runtime materializer's return value is not serialized and is unrestricted.

Requirements / caveats

  • Use it on the author-side publish build (tsup, Vite, Rollup, Rolldown, esbuild, Bun.build) — not in consumer apps.
  • Evaluation extracts the same-file lexical dependency closure: imports and stable module-level const, function, class, and enum declarations. Unrelated top-level statements and side-effect-only imports are not executed.
  • Calls, constructors, getters, imported-module initialization, and other side effects inside the extracted closure are allowed. Configuring a rule is an explicit trust decision; there is no purity analysis or sandbox.
  • Only synchronous results are supported. Promise/thenable results are failures and are never implicitly awaited.
  • Imported modules load fully. There is no recursive project-graph trimming or persistent build cache. Rule match uses the host resolver when available (otherwise exact specifier strings). Evaluation still loads modules via adjacent temporary files and does not re-run the bundler graph.
  • Successful automatic replacements remove their original import specifier only when the binding has no remaining runtime references. Evaluator imports exist only in temporary modules. Materializer imports are emitted only for successful surviving materialized sites and are deduplicated by exact source/export reference.
  • Automatic calls use macro semantics and execute in expression/source order. Non-materialized successful results may feed later static evaluation; materialized results are runtime boundaries. A successful marked initializer takes precedence over nested non-materialized automatic edits.
  • Interference checks for marked bindings are per remaining mark. A runtime-boundary-blocked mark does not prevent unrelated marks from succeeding. Never-called top-level function/class declarations are ignored unless a pre-target call site can reach them.
  • TypeScript:
    • Bun: evaluates TS/TSX natively (including relative .ts imports).
    • Node: does not transpile TypeScript or JSX. Run the publish build with a host loader such as tsx (for example, NODE_OPTIONS='--import tsx/esm' vite build), or ensure the extracted entry and its dependencies are plain JS. The temporary entry keeps the original .ts/.tsx/.jsx extension so the loader can process the full relative dependency graph.
  • Hidden shared state from imported modules or ambient globals remains outside single-file static proof.
  • Static-analysis limits include dynamic code such as eval(...)/new Function(...), and bundler-only virtual modules, path aliases, or package mappings that the adjacent temporary file cannot load. Failures follow onFailure.
  • import.meta in an extracted closure is refused because the temporary path would leak.
  • No declaration-file rewriting or first-class webpack support.

How it works

  1. Analyze — Babel finds configured direct import calls, tagged templates, and marked top-level constants, verifies binding identity (host-resolved match.from when possible), and builds stable same-file dependency closures.
  2. Extract + evaluate — automatic sites execute children-first with evaluate ?? match; marked bindings then evaluate against successful non-runtime intermediate constants. Temporary ESM files are written next to the source, dynamically imported, and removed in finally.
  3. Serialize + emit — evaluator payloads become literals. Rules with materialize emit a hygienic runtime import and materialize(<literal>); other successes emit the literal directly. MagicString applies one non-overlapping final edit set and removes dead matched import bindings.