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

placefill

v1.0.0

Published

Lightweight placeholder filling for strings, objects, and arrays.

Readme

placefill

English | 简体中文

轻量的占位符填充 SDK,面向字符串、对象和数组。

placefill 解决的是“把数据填进模板”这件事。它不是完整模板引擎,也不会把 HTML 解析成 AST。

为什么用 placefill

  • 深度处理对象和数组中的字符串
  • 支持 user.nameitems.0matrix.1.2 这类路径
  • 对纯占位符字符串保留原始值类型,比如 {{count}}
  • 支持受控的同步函数执行
  • 支持轻量标签模板,适合富文本片段拼装
  • 未注册标签会透明保留,同时继续处理已知子标签和占位符
  • 零运行时依赖

适合的场景

适合在生产环境里做可预测、低复杂度的模板填充:

  • 事务消息:订单通知、客服工单摘要、营销文案
  • 对象 payload 生成:邮件 payload、推送 payload、内部配置对象
  • CMS / 内容拼装:文章片段、CTA 模块、本地化富文本片段
  • 编译一次多次渲染:i18n 片段、服务端重复渲染、模板注册表

不适合的场景

下面这些需求不建议用 placefill

  • 条件判断、循环、异步表达式、任意表达式求值
  • 完整 HTML 解析或 AST 变换
  • 完整组件系统或模板语言

安装

# 发布到 npm 前,可先用本地 tarball 安装
npm pack
npm install ./placefill-0.1.0.tgz
# or
pnpm add ./placefill-0.1.0.tgz
# or
bun add ./placefill-0.1.0.tgz

# 发布到 npm 后
npm install placefill
# or
pnpm add placefill
# or
bun add placefill

运行环境

placefill 面向能运行 ES2020 产物的现代浏览器、Node.js 和 edge runtime。

  • 零运行时依赖
  • Node.js >=16.20
  • 适合前端应用、服务端 handler、worker 和 CLI 脚本
  • API 保持同步、可预测,便于排障和压测

快速开始

import { fill } from "placefill";

const result = fill(
  {
    title: "Hello {{user.name}}",
    firstItem: "{{items.0}}",
  },
  {
    user: { name: "Lin" },
    items: ["A", "B"],
  },
);

console.log(result.value);
// {
//   title: "Hello Lin",
//   firstItem: "A"
// }

编译一次,多次渲染

同一个模板会重复渲染时,优先使用 compile

import { compile } from "placefill";

const template = compile('<Link href="/orders/{{order.id}}">{{user.name}}</Link>', {
  tags: {
    Link: '<a href="{{attrs.href}}">{{children}}</a>',
  },
});

console.log(template.render({ order: { id: "A-100" }, user: { name: "Lin" } }).value);
console.log(template.render({ order: { id: "A-101" }, user: { name: "Qin" } }).value);
// <a href="/orders/A-100">Lin</a>
// <a href="/orders/A-101">Qin</a>

这类场景尤其适合 compile

  • 服务端重复渲染
  • i18n 片段复用
  • 启动时加载模板注册表
  • 富文本模板按不同数据多次渲染

何时用 fill,何时用 compile

按调用模式选最简单的入口:

  • fill(objectOrArray, values):适合一次性的深度 payload 组装
  • fill(string, values):适合简单的单次字符串渲染;重复渲染同类字符串时可通过 cacheSize 复用共享编译缓存
  • compile(template).render(values):适合热路径、重复渲染、富文本标签模板,以及启动后长期复用的模板注册表

如果标签模板里用了函数,又希望 fill(string, ...) 命中缓存,建议提供稳定的 cacheKey

富文本与标签模板

import { fill } from "placefill";

const result = fill(
  '欢迎 <highlight>{{user.name}}</highlight>,查看 <link href="/orders/{{order.id}}">订单</link>',
  { user: { name: "Lin" }, order: { id: "A-100" } },
  {
    tags: {
      highlight: "<strong>{{children}}</strong>",
      link: '<a href="{{attrs.href}}">{{children}}</a>',
    },
  },
);

console.log(result.value);
// 欢迎 <strong>Lin</strong>,查看 <a href="/orders/A-100">订单</a>

标签行为说明:

  • 标签名大小写敏感
  • 支持 MyCompui.slotx:labelmy-comp 这类标签名
  • 未注册标签会原样保留
  • 标签模板里的 {{children}}{{attrs.xxx}} 固定使用双大括号
  • 如果标签片段本身有错,异常片段会原样保留,同一字符串里其他合法占位符仍会继续处理

返回结果

fillcompile().render() 都返回结构化结果:

| 字段 | 说明 | | -------- | ------------------------------------------------ | | value | 渲染结果 | | ok | 没有 error 时为 true | | issues | 收集到的 warning 和 error | | stats | 占位符数量、替换数量、告警、错误、耗时等统计信息 |

支持的占位符格式

默认仅识别双大括号:

fill("Hello {{name}}", { name: "Lin" });

double_braces 保持向后兼容,仍支持 {{full name}}{{user:name}}{{用户}} 这类直取 key。

其他格式需要显式启用:

fill("Hello {name}", { name: "Lin" }, { format: "single_braces" });
fill("Hello ${name}", { name: "Lin" }, { format: "dollar_braces" });
fill("Hello %name%", { name: "Lin" }, { format: "percent" });

对于 single_bracesdollar_bracespercent,占位符限制为 path-like key,例如 {user.name}${order.id}%campaign.slug%

真实场景示例

仓库里的 examples 目录包含了一组更接近真实业务的案例:

本地运行:

pnpm run examples
pnpm dlx tsx examples/campaign-banner-i18n.ts

配置选项概览

| 选项 | 类型 | 默认值 | 说明 | | ------------------------- | ------------------------------------- | --------------- | -------------------------------------- | | format | PlaceholderFormat | double_braces | 占位符语法 | | defaultValue | string \| number \| boolean \| null | 未设置 | 缺失值的替代值 | | onError | collect \| throw | collect | 错误处理策略 | | executeFunctions | boolean | false | 是否执行同步占位符函数 | | tags | Record<string, TagTemplate> | {} | 标签模板注册表 | | maxDepth | number | 100 | 对象 / 数组遍历最大深度 | | maxTagDepth | number | 100 | 标签嵌套最大深度 | | treatUndefinedAsMissing | boolean | false | 是否将 undefined 视为缺失 | | cacheSize | number | 200 | fill(string, ...) 的共享编译缓存大小 | | cacheKey | string | 未设置 | 函数标签场景下的显式缓存 key |

补充说明:

  • values 必须是普通对象,否则会抛 FillError
  • 纯占位符字符串如 {{count}} 可以直接返回数字、布尔、数组、对象、Date 或函数结果
  • 占位符函数仅支持同步执行

兼容性

  • 同时提供 ESM 和 CJS 两种产物
  • 打包目标为 ES2020
  • 内置类型声明
  • 面向当前 Node.js 版本和 evergreen 浏览器 + 现代打包器
  • 不提供 UMD 或全局变量形式的浏览器构建

对 Node.js 用户:

  • 包导出同时支持 importrequire
  • prepublishOnly 会执行 checktestpack

对浏览器用户:

  • 通过 bundler 或原生模块链路消费 ESM 产物
  • 运行时无依赖,发布产物默认面向现代运行时
  • 如果要兼容更老的浏览器,需要由使用方自己的构建链路负责转译和 polyfill

基准测试

基准文件位于 tests/bench/fill.bench.ts。分组方式按用户调用模式,而不是按内部函数名;同时使用 vitest bench 的 warmup、多轮采样、轮转数据集,并直接复用仓库里的 examples 语义,避免只看理想化的单 token 微基准。

当前包含三层基准:

  • layer: micro:低噪声回归基准,覆盖短字符串、默认值回退、富文本标签、对象 payload
  • layer: realistic one-shot:低复用场景,每次迭代都显式计入 compile 成本,覆盖单大括号通知、订单确认 payload、账单提醒邮件
  • layer: realistic repeat:固定模板反复渲染场景,覆盖客服工单摘要、CMS 文章片段、营销横幅

本地运行:

pnpm run bench

结果解读建议:

  • 单次组比较的是 fill(...)compile(...).render(...),其中 compile 成本会在每次迭代里计算进去
  • 重复字符串组比较 fill(cache off)fill(shared cache)compile.render(precompiled)
  • 重复对象组只比较 fill(...)compile.render(...),因为共享缓存只对字符串模板生效

开发

pnpm run check
pnpm test
pnpm build
pnpm run bench
pnpm run examples

许可证

MIT