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

unplugin-inline

v1.8.0

Published

An unplugin to inline pure function calls.

Readme

unplugin-inline

AST-driven unplugin that inlines pure functions at build time across Vite, Rollup, Webpack, and esbuild.


Why

V8 refuses to inline functions whose bytecode exceeds ~460 instructions or ~600 bytes, even on the hot path—every call still pays full frame setup cost at runtime. Marking a function with /* @__INLINE__ */ moves that cost to build time instead, flattening the logic directly into the caller.

Installation

pnpm add -D unplugin-inline
# or
npm install -D unplugin-inline
# or
yarn add -D unplugin-inline

Example Usage

1. Standard Block Inlining (@__INLINE__)

Consider a physics simulation where transformPoint is called millions of times per frame. The function is large enough that V8 refuses to inline it automatically.

Source Code

src/physics.ts

/* @__INLINE__ */
function transformPoint(x: number, y: number, z: number, matrix: Float32Array): number {
  const m = matrix;
  const tx = m[0] * x + m[4] * y + m[8] * z + m[12]
  const ty = m[1] * x + m[5] * y + m[9] * z + m[13]
  const tz = m[2] * x + m[6] * y + m[10] * z + m[14]
  const tw = m[3] * x + m[7] * y + m[11] * z + m[15]

  return Math.sqrt(tx * tx + ty * ty + tz * tz) / tw
}

// Called millions of times per frame — no function call overhead in the output
export function processVertices(vertices: Float32Array, matrix: Float32Array): number {
  let sum = 0
  for (let i = 0; i < vertices.length; i += 3) {
    sum += transformPoint(vertices[i], vertices[i + 1], vertices[i + 2], matrix)
  }
  return sum
}

Compiled Output

The compiled output has no transformPoint declaration. Its body is placed directly at the call site as a flat labeled block:

dist/physics.js

function processVertices(vertices, matrix) {
  let sum = 0;
  for (let i = 0; i < vertices.length; i += 3) {
    let _transformPointResult;
    _transformPointLabel: {
      const _x = vertices[i];
      const _y = vertices[i + 1];
      const _z = vertices[i + 2];
      const _matrix = matrix;
      const tx = _matrix[0] * _x + _matrix[4] * _y + _matrix[8] * _z + _matrix[12];
      const ty = _matrix[1] * _x + _matrix[5] * _y + _matrix[9] * _z + _matrix[13];
      const tz = _matrix[2] * _x + _matrix[6] * _y + _matrix[10] * _z + _matrix[14];
      const tw = _matrix[3] * _x + _matrix[7] * _y + _matrix[11] * _z + _matrix[15];
      _transformPointResult = Math.sqrt(tx * tx + ty * ty + tz * tz) / tw;
    }
    sum += _transformPointResult;
  }
  return sum;
}

2. Macro Expression Inlining (@__INLINE_MACRO__)

For extremely hot, small math utility functions, generating block scopes can bloat the bundle and create unnecessary extra variables environments. Using @__INLINE_MACRO__ bypasses block generation entirely, performing a direct AST expression substitution wrapped in parentheses to preserve operator precedence.

src/math.ts

/** @__INLINE_MACRO__ */
const blendAlpha = (a: number, b: number) => (a * b + 128) >> 8;

export const color = blendAlpha(100, 255) * 2;

dist/math.js

export const color = (((100) * (255) + 128) >> 8) * 2;

Both block (/* @__INLINE__ */) and line (// @__INLINE__) comment styles are recognized.

Bundler Configuration

Vite

vite.config.ts

import { defineConfig } from 'vite'
import { vitePlugin } from 'unplugin-inline'

export default defineConfig({
  plugins: [vitePlugin()]
})

esbuild

build.js

import esbuild from 'esbuild'
import { esbuildPlugin } from 'unplugin-inline'

esbuild.build({
  entryPoints: ['input.js'],
  bundle: true,
  plugins: [esbuildPlugin()],
})

tsup

tsup.config.ts

import { defineConfig } from 'tsup'
import { esbuildPlugin } from 'unplugin-inline'

export default defineConfig({
  esbuildPlugins: [esbuildPlugin()],
})

Rollup

rollup.config.js

import { rollupPlugin } from 'unplugin-inline'

export default {
  input: 'input.js',
  plugins: [rollupPlugin()],
}

Webpack

webpack.config.js

const { webpackPlugin } = require('unplugin-inline')

module.exports = {
  plugins: [webpackPlugin()],
}

Configuration Options

| Option | Type | Default | Description | |----------------------------|------------|-----------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | inlineIdentifier | string | '@__INLINE__' | The comment string used to mark functions for standard block inlining. | | inlineMacroIdentifier | string | '@__INLINE_MACRO__' | The comment string used to mark functions for direct AST expression substitution (Macros). | | autoConvertInlineToMacro | boolean | true | If true, the plugin will attempt to automatically upgrade standard @__INLINE__ functions to macros if they meet all safety requirements, falling back to block-scoping if they don't or if passed impure arguments. | | allowedGlobals | string[] | See Defaults | Global identifiers available inside inlined functions. | | fileExtensions | string[] | See Defaults | File extensions the plugin will process. |

⚠️ Requirements & Restrictions

A function must be pure to be inlinable. The plugin enforces strict AST analysis and will throw build errors if you violate these rules:

  • No async or generatorsasync/await and function* alter execution timing.
  • **No this or arguments** — both bind to the caller after inlining, producing unpredictable results.
  • No outer scope mutations — cannot reassign variables declared outside the function's own block.
  • No outer scope references — cannot read variables from an outer scope. Standard globals (Math, JSON, etc.) are permitted — see allowedGlobals for the full default list.
  • No recursive functions — recursion cannot be unrolled at the call site.
  • No call expressions in conditionals — the function must be called as a standalone statement or direct assignment, not inside a ternary or if condition. Assign the result first:
// ❌ Not allowed
if (processValue(x) > 100) { // ...
}

// ✅ Assign first, then use
const processed = processValue(x)
if (processed > 100) { // ...
}

Specific Requirements for Macros (@__INLINE_MACRO__)

Because macros perform direct AST substitution rather than creating a lexical block scope, they have additional strict requirements:

  • Must resolve to a single pure expression — Macros cannot contain multiple statements, variable declarations, or block-level logic (like if statements). Arrow functions with implicit returns or standard functions with a single return statement are allowed.
  • No Side-Effect Duplication (Multiple Evaluation Bug) — If an argument with side-effects (e.g., i++, Math.random(), getPixel()) is passed into a macro, the plugin analyzes how many times that parameter is referenced in the macro body. If the parameter is referenced more than once, expanding the macro would cause the side-effect to be evaluated multiple times. The build will throw a safety validation error.

Benchmarks

npm run bench

Release Automation

  • update package.json file version (example: 1.0.99)
  • manually create a github release with a tag matching the package.json version prefixed with v (example: v1.0.99)
  • npm should be updated automatically

License

MIT