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

@gantfdt/fp-babel-plugin-profiler

v0.2.4

Published

Build-time babel plugin that wraps React component exports with @gantfdt/fp-runtime-react fpProfile Profiler boundaries

Readme

@gantfdt/fp-babel-plugin-profiler

构建期 babel 插件:自动给识别出的 React 组件 export 包上 fpProfile Profiler 边界(运行时 HOC 在 @gantfdt/fp-runtime-react)。规格见 docs/PROFILER_AUTOWRAP.md(本文档只是使用说明,不复制设计)。

// 变换前(宿主源码)
function SearchList(props) { ... }
export default SearchList;
export default () => { ... }

// 变换后(import 自动注入并去重;importPath 缺省时为插件 require.resolve 出的绝对路径)
import { fpProfile as __fpProfile } from '/abs/path/node_modules/@gantfdt/fp-runtime-react/dist/index.js';
function SearchList(props) { ... }
export default __fpProfile(SearchList, { id: 'src/pages/search/index.tsx:SearchList', name: 'SearchList' });
export default __fpProfile(() => { ... }, { id: 'src/pages/search/index.tsx:default', name: 'search' });

直接的 export default function(具名/匿名)和 export function 不转换:拆成求值期包装会破坏函数声明导出的提升语义,循环依赖下可抛 ReferenceError。小写命名在 default 引用、函数表达式等路径也不进入组件候选,防止把多参数普通工具函数误包成 React 组件;export specifier 同时检查本地名和导出名。此处宁可少采,也不改变业务模块语义。

用法

经 @gantfdt/fp-umi-plugin 注入(fpPerf.autoProfile),或直接使用:

// babel 配置
plugins: [
  [require.resolve('@gantfdt/fp-babel-plugin-profiler'), {
    cwd: process.cwd(),            // componentId 相对路径与 include/exclude 的基准目录
    include: ['src/pages/**'],     // minimatch 风格 glob,相对 cwd;默认 ['src/**']
    exclude: ['**/*.test.*'],      // 默认 ['**/*.test.*', '**/*.spec.*', '**/node_modules/**']
    // importPath: '...',          // 注入 import 的 specifier;缺省 require.resolve('@gantfdt/fp-runtime-react')
    manifest: { outFile: 'dist/fp-components.json' }, // 存在时注册一次 process exit hook 自动写出
  }],
]

Options

| 字段 | 类型 | 默认 | 说明 | |---|---|---|---| | cwd | string | process.cwd() | 相对路径基准;文件不在 cwd 内则跳过 | | include | string[] | ['src/**'] | picomatch glob,相对 cwd 的 POSIX 路径 | | exclude | string[] | ['**/*.test.*', '**/*.spec.*', '**/node_modules/**'] | 同上 | | importPath | string | 插件内 require.resolve('@gantfdt/fp-runtime-react') 的绝对路径 | pnpm 严格结构下宿主不能裸引包名(设计文档 D4) | | manifest.outFile | string | 无 | 存在时注册一次 process.on('exit') 自动 flush | | manifest.deferMerge | boolean | false | umi 场景(umi-plugin 注入时置位):本进程 exit 钩子只落 worker sidecar;umi-plugin 在 onBuildComplete 临时合并、在主进程 exit 最终合并产出 fp-components.json |

manifest(fp-components.json)

进程内聚合本进程本次实际变换文件的产出。落盘走 sidecar 汇合(2026-10-23 起):exit 钩子把切片序列化为 $TMPDIR/fp-profiler-manifest-<sha1(outFile) 前 12 位>/worker-<pid>.json(先写临时文件再 rename 保证原子性),权威合并方读全部 sidecar 做 union(components 按 file+\0+id、skipped 按 file+\0+reason 去重,config 先到先得,排序字典序确定性)后落盘并清理 worker sidecar。独立单进程场景 flushManifest(outFile) 原地完成「落 sidecar → 合并 → 清理」全流程,产物与旧版逐字段一致:

{
  "build": "20260923-1130",
  "components": [
    { "id": "src/pages/search/index.tsx:default", "name": "OrderList", "file": "src/pages/search/index.tsx", "line": 42, "exportKind": "default", "wrapped": true }
  ],
  "skipped": [{ "file": "src/utils/tree.ts", "reason": "no-jsx-return" }]
}

跨构建快照(2026-10-25 起,babel-loader 磁盘缓存语义):缓存命中的文件本次构建不经插件(零 sidecar),其包装记录由快照承接——<cwd>/node_modules/.cache/fp-profiler/manifest-<sha12(outFile)>.json(cwd 不可写时回退到 sidecar 目录内 snapshot.json),内容 { configHash, components, skipped, capturedAt }。configHash = sha1(JSON({cwd, include, exclude})) 前 16 位:与本次生效配置不匹配即丢弃(options 变 → babel-loader 缓存 key 变 → 全量重转 → 本次 sidecar 已是全集)。快照条目按 fs.existsSync 修剪已删文件(只剪快照条目)。快照是跨构建资产:clearManifestSidecars 只清 worker-*.json、保留快照。本包导出 VERSION 供 umi-plugin 注入 babel options 排雷(插件升版本 → 缓存 key 变 → 自动全量重转)。零匹配告警三态不变:无 sidecar 且无快照 → 告警;无 sidecar 有快照承接(暖缓存正常路径)→ 静默;有 sidecar 但汇合 0 匹配 → 告警。umi 的 onBuildComplete 是 provisional 合并,以 deferWarnings: true 禁止两类告警及告警 latch;此时无 sidecar/manifest 是合法中间状态。最终覆盖告警由主进程 exit 兜底合并判定,且同进程 babel writer 的 exit 监听器先于 umi 合并监听器执行。

skipped reason 词汇表:

| reason | 含义 | |---|---| | no-jsx-return | 候选函数静态分析不到 JSX 返回 | | hook | useXxx 命名 | | lowercase-identifier | 小写开头命名(default 引用/函数表达式、named export、export specifier 的本地名或导出名) | | hoisted-function-export | 直接的 default/named 函数声明导出;当前没有保持导出提升语义的包装变换,保守不采 | | uncertain-form | 条件/动态/计算属性等静态不可判形态 | | not-a-component | 导出体显然不是组件(字面量、非组件 class 等) | | imported-reference | 默认导出的是裸 import 引用(组件会在其自身模块被包) | | re-export-form | export ... from 再导出 | | react-lazy | React.lazy 本体(被 lazy 的组件在其自身模块内按规则处理) | | no-candidates | 文件内没有任何 export 候选 |

Node API

const plugin = require('@gantfdt/fp-babel-plugin-profiler');
plugin.VERSION;                                // 插件自身版本(读 package.json),umi-plugin 注入 babel options 排雷用
plugin.flushManifest(outFile);                 // 独立场景:落本进程 sidecar → 汇合(含快照承接)→ 写出并返回 manifest 对象(worker sidecar 已清理)
plugin.flushManifest(outFile, { snapshot: false }); // 完全跳过快照读写(宿主 dry-run 等只读场景)
plugin.resetManifest();                        // 清空进程内聚合(测试隔离用)
// 合并:读全部 worker sidecar ∪ 跨构建快照(ctx 的 configHash 匹配才承接)→ union → 落盘 → 更新快照 → 清理;
// ctx = { cwd, include, exclude }(默认展开后的生效配置);无 sidecar 且无快照返回 null 不写盘。
plugin.mergeAndFlushManifest(outFile, ctx); // 默认按 F3 三态立即告警,供最终合并/直接调用
plugin.mergeAndFlushManifest(outFile, ctx, { deferWarnings: true }); // provisional:仅合并,不调用 noCoverage/zeroMatch,也不设置告警 latch
plugin.clearManifestSidecars(outFile);         // 只清 worker sidecar(构建起点防被杀构建残留污染),保留跨构建快照
plugin.getManifestSidecarDir(outFile);         // sidecar 聚合目录路径($TMPDIR/fp-profiler-manifest-<hash>)

ESM import plugin from ... 亦可(module.exports.default = module.exports)。

幂等与失败模式

  • 检测到 __fpProfile(...) 包装引用即跳过;同一文件跑两遍产物逐字节一致。
  • 任何识别不确定的形态跳过并记 skipped——漏包只是「该组件没数据」,不会改变组件行为。
  • 运行时 fail-open 由 fpProfile 保证(window.__fp 缺失时返回原组件),不在本包职责内。