Effect 仓库 Bundle Size 开发工作流实战:用 Rollup + gzip 测量、对比与分析包体积
Effect 仓库 Bundle Size 开发工作流实战用 Rollup gzip 测量、对比与分析包体积【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code本文面向在 .repos/effect-smolEffect 生态源码仓库中开发或维护库代码的开发者与 AI Agent系统讲解其内置的effect/bundle包体积测量工具链如何使用bundle-analyze分析 bundle 组成、用bundle-compare-selected对比显式入口文件的体积变化、何时直用内部 CLI以及如何编写高质量测量 fixture 并做好工作区清理。读完本文你将能够独立完成本地源码改动对某个入口文件 bundle 体积影响的完整测量闭环并理解 Rollup 插件管线、gzip 测量与 Markdown 报告生成的底层实现。工具包定位为 Effect 入口点提供可重复的包体积测量.repos/effect-smol/packages/tools/bundle/README.md 描述的是仓库内部用于测量Effect 入口点entrypoint打包体积的工具链以 Rollup 打包经过 minification压缩与 gzip 后统计字节数。这个工具包被声明为私有包effect/bundle见 package.json其依赖包括rollup、rollup/plugin-node-resolve、rollup/plugin-replace、rollup/plugin-terser、rollup-plugin-esbuild、rollup-plugin-visualizer以及effect/platform-node与effect自身——也就是说这套测量工具本身就是用 Effect 的 CLI、Effect、FileSystem、Stream 等模块实现的。仓库根目录的package.json暴露了三个封装好的 npm script见 .repos/effect-smol/package.json它们分别对应三个 shell 脚本npm script底层脚本用途pnpm bundle-analyzescripts/bundle-analyze.sh分析显式入口文件的 bundle 组成pnpm bundle-comparescripts/bundle-compare.sh对比本地稳定 fixtures 与基线 ref 的体积pnpm bundle-compare-selectedscripts/bundle-compare-selected.sh对比用户显式指定的入口文件与基线 ref 的体积本文以下所有命令均需在.repos/effect-smol目录即 effect-smol 仓库根下执行。工作流选型先回答用户到底想做什么文档强调这个工具链面向的是本地源码改动如何影响 bundle 体积这一场景因此只测量用户显式命名的文件或为当前调查专门创建的文件不要扫描scratchpad/目录也不要扫描 packages/tools/bundle/fixtures后者只存放稳定对比 fixture。在运行对比之前需要先确认用户要哪种模式默认清理模式cleanup mode适合单次检查或一次性调查跑完自动清理基线工作区keep-base 模式适合多次重复测量测量结束后需显式清理。除非用户明确表示要做多轮实验否则一律使用清理模式。另外如果用户想知道生成的 bundle 由哪些模块组成应使用Bundle Composition组成分析工作流而不是体积对比工作流。工作流一分析 Bundle 组成Analyze Bundle Composition当用户给出明确的 fixture 并想了解生成的 bundle 由哪些模块构成时使用此工作流。执行步骤确认要分析的 fixture 精确路径一个或多个不扫描目录运行pnpm bundle-analyze只传显式路径读取打印的 Markdown 表格找到每个 fixture 生成的*.raw-data.json路径优先分析*.raw-data.json只有用户想看可视化产物时才打开*.treemap.html汇报最大的模块、值得注意的依赖分组以及任何意外的引入内容并在回答中给出生成产物的路径。推荐使用仓库级封装命令pnpm bundle-analyze scratchpad/my-fixture.ts可以一次传入多个显式文件pnpm bundle-analyze \ scratchpad/schema-codec.ts \ scratchpad/schema-arbitrary.ts该封装命令在生成分析产物之前会先执行pnpm build构建当前 checkout因此 bundle 不会基于过期的dist文件生成这一点在 scripts/bundle-analyze.sh 中可以看到它先调用pnpm build再调用内部 CLI。默认情况下产物写入tmp/bundle-analysis目录可用--output-dir指定其他目录pnpm bundle-analyze --output-dir tmp/schema-analysis scratchpad/my-fixture.ts对于每个选中的 fixture工具会写出三个文件name.min.js生成的 Rollup 输出供人工检查name.treemap.html交互式 treemap供人工可视化检查name.raw-data.json原始可视化数据适合 AI 分析。需要特别注意的是分析构建禁用了标识符 mangling因此模块名和导出名在可视化输出中保持可读见下文源码解析这也意味着不要把此工作流生成的.min.js大小当作精确体积对比数字。要精确测量体积应使用bundle-compare-selected或report。运行后从命令打印的 Markdown 表格中查找精确路径。对 AI 分析而言应首先读取*.raw-data.json文件汇总最大的模块与依赖分组当用户想可视化检查 bundle 时再使用*.treemap.html。解读 raw-data.json按体积对模块排序原始数据包含一棵树tree加模块元数据module metadata。要做快速的按体积排序摘要可按renderedLength或gzipLength对 module parts 排序再通过metaUid映射到nodeMetas[metaUid].idnode -e const data JSON.parse(require(node:fs).readFileSync(process.argv[1], utf8)); console.log(Object.entries(data.nodeParts).map(([uid, part]) ({ uid, id: data.nodeMetas[part.metaUid]?.id, rendered: part.renderedLength, gzip: part.gzipLength })).sort((a, b) b.rendered - a.rendered).slice(0, 20)) tmp/bundle-analysis/my-fixture.raw-data.json这条命令读取raw-data.json把nodeParts中的每个模块部件映射回nodeMetas中的模块 id按renderedLength降序输出前 20 个模块——这是定位谁把 bundle 撑大了的最快方式。同时再次强调边界本工作流不用于与基线 ref 对比——做体积影响对比用bundle-compare-selected看当前 bundle 组成用bundle-analyze两者各司其职。工作流二对比显式 Scratchpad FixtureCompare Explicit Scratchpad Fixtures当需要回答本地改动让某个入口文件变大还是变小时使用体积对比工作流。推荐仓库级封装命令pnpm bundle-compare-selected --base main scratchpad/my-fixture.ts同样支持多个显式文件pnpm bundle-compare-selected --base main \ scratchpad/schema-codec.ts \ scratchpad/schema-arbitrary.ts若省略--base脚本默认对比main分支见 scripts/bundle-compare-selected.sh 中BASE_REFmain的默认值。默认清理 vs --keep-base默认情况下封装脚本在退出前会移除tmp/bundle-base即使失败也会清理因为脚本用trap cleanup EXIT注册了清理钩子避免一次性测量后残留额外的 git worktree 状态。如果需要多轮重复测量以获得更快的反馈循环且用户明确要求则传入--keep-basepnpm bundle-compare-selected --base main --keep-base scratchpad/my-fixture.ts使用--keep-base时应在最后一次测量结束后手动清理tmp/bundle-basegit worktree remove --force tmp/bundle-base封装脚本的内部流程从 scripts/bundle-compare-selected.sh 可以看到完整流程用pnpm build构建当前 checkout在请求的 base ref 上创建或复用tmp/bundle-baseworktree需要时构建基线 checkout并且只在构建成功后才写入tmp/bundle-base/.bundle-build-stamp戳记文件仅当缓存基线的 HEAD 与.bundle-build-stamp同时匹配请求的 base ref 时才复用该基线 checkout避免拿未构建或过期的基线做对比只把选中的文件复制进基线 checkout 的相同相对路径下调用内部 bundle CLI 对比当前与基线的体积。第 5 步很关键选中文件会被复制进基线 checkout因此报告隔离的是源码改动而不是 fixture 文本本身的差异。这也解释了为什么 fixture 应当自包含见下文 Fixture 指南。预期输出Markdown 表格命令的预期输出是一张 Markdown 表格| File Name | Current Size | Previous Size | Difference | | :------------------------- | :----------: | :-----------: | :---------------: | | scratchpad/my-fixture.ts | 42.10 KB | 40.80 KB | 1.30 KB (3.19%) |数字单位为十进制 KB除以 1000见 Reporter.ts 中sizeInBytes / 1000的计算差异带符号与百分比。工作流三直用内部 CLI仅当基线已备好只有当基线 checkout 已经准备并构建完成时才直接使用内部 CLInode packages/tools/bundle/src/bin.ts compare-selected \ --base-dir tmp/bundle-base \ scratchpad/my-fixture.ts注意--base-dir的值必须是基线 checkout 的根目录而不是基线 fixture 目录。从 Cli.ts 可以看到compare-selected子命令接收--base-dir别名-b和变长的paths参数而compare子命令的--base-dir指向的是 fixtures 目录见 scripts/bundle-compare.sh 传入的是$BASE_DIR/packages/tools/bundle/fixtures两条命令语义不同勿混淆。工作流四仅测当前体积Current Size Only如果用户只想知道某个入口文件当前的打包体积不涉及基线对比使用pnpm --dir packages/tools/bundle report ../../../scratchpad/my-fixture.ts这条命令不对比任何 base ref直接对传入的入口文件打包并打印| File Name | Current Size |表格对应 Cli.ts 中的report子命令其 handler 调用reporter.reportSelected后输出 Markdown。Fixture 编写指南临时 fixture 应放在scratchpad/目录。好的临时 fixture 是小而聚焦、只导入公开包 API的入口文件例如import * as Effect from effect/Effect import * as Schema from effect/Schema const schema Schema.Struct({ name: Schema.String }) Schema.decodeUnknownEffect(schema)({ name: effect }).pipe(Effect.runFork)两个关键约束尽量自包含。compare-selected工作流只会把显式指定的入口文件复制进基线 checkout见上文流程第 5 步。如果 fixture 导入了本地相对路径的辅助模块要么避免这种形态要么在测量前把入口 fixture 改写成自包含的。不要把临时 fixture 加进packages/tools/bundle/fixtures/。该目录存放的是稳定对比 fixture由常规 bundle-size 工作流pnpm bundle-compare使用。目前目录里已有一批覆盖典型场景的稳定 fixture例如 basic.ts最小 Effect 入口、schema.tsSchema.Struct decode、http-client.tsHttpClient 调用等可以作为编写 scratchpad fixture 的参照。从 Fixtures.ts 的实现看稳定 fixture 是从包内fixtures目录以*.ts模式 glob 发现并按名称排序的每个 fixture 作为独立的 Rollup 入口打包因此它应代表所要测量的导入形态且不依赖 fixture 发现顺序。清理与验证封装脚本默认会清理tmp/bundle-base。如果用了--keep-base进行多轮调查应在用户结束调查时移除该 worktreegit worktree remove --force tmp/bundle-base验证清理是否成功git worktree list正常情况下应只剩下主仓库的 worktree。源码深度Rollup 服务与插件管线插件管线Plugins.tsPlugins.ts 负责拼装 Rollup 插件管线顺序是有意为之的文档注释明确警告修改此模块时务必保持插件顺序本地包解析插件createResolveLocalPackageImports把effect/effect/*的导入解析到各包构建后的dist文件保证测量的是产物而非源码toLocalDistPath把src/*.ts映射为dist/*.js。正则EFFECT_PACKAGE_REGEX /^(effect\/[\w-]|effect)(\/.*)?$/匹配 Effect 相关导入nodeResolve随后执行常规 node 解析replace把process.env.NODE_ENV替换为productionpreventAssignment: true模拟生产构建esbuild以target: node20默认值、format: esm、treeShaking: true将 TypeScript 降级为 ESM供 Rollup 继续 tree-shakingterser压缩并做标识符 mangling——但当visualize为 true 时禁用 manglemangle: resolved.mangle !resolved.visualize这正是分析产物里模块名保持可读的原因。可视化支持来自rollup-plugin-visualizer默认gzipSize: true、open: false以treemap与raw-data两种模板分别生成name.treemap.html与name.raw-data.json见 Rollup.ts 中createVisualizationOutputs。打包与测量Rollup.tsRollup.ts 实现Rollup上下文服务核心行为bundle 在内存中生成产物代码通过Stream同时流向 gzip 测量与可选的.min.js文件输出只统计 Rollupchunk类型输出Stream.filter(output output.type chunk)忽略 assets当 Rollup 因动态导入或共享 chunk 生成多个 chunk 时它们的代码会流式合并测量gzip 使用createGzip({ level: 9 })即最高压缩级别字节数即最终报告中的体积可选的输出文件以入口文件名stem命名因此它更适合视为检查产物而非完整的 Rollup 输出目录bundleAll对多个入口以concurrency: paths.length并发打包。打包完成后会打印类似Bundled path size: xx.xx kB的结构化日志通过Effect.annotateLogs。报告生成Reporter.tsReporter.ts 负责把测量结果转成 Markdown按文件名basename匹配对比当前 fixture 与基线目录中同名文件配对若基线目录中不存在同名文件该 fixture 报告为无变化unchanged体积按十进制 KB/ 1000展示差异同时给出绝对值与百分比百分比按Math.abs(diff) / prevSize * 100计算compare工作流对应 CI 中的 Bundle job会把报告写入--output-path指定文件默认stats.txtCli.ts 中Flag.withDefault(stats.txt)而report/compare-selected直接打印到 stdout可视化产物的命名基于入口文件名 stem因此重名文件可能让产物互相覆盖输出会有误导性——分析时需留意。测试与验证工具包自带针对插件管线的单元测试 Plugins.test.ts用于验证 Rollup 插件组合行为配合 scripts/bundle-compare.sh对应 CI 的 Bundle job可以本地复现 CI 上的全量 fixture 体积对比流程。整个 CLI 使用 Effect 的unstable/cli命令框架Command.runNodeRuntime.runMain见 bin.ts顶层命令为bundle子命令为compare、compare-selected、report、visualize、visualize-selected。小结一条完整的测量流程把上述工作流串起来一次典型的评估本地改动对 bundle 体积影响的流程是在scratchpad/写一个自包含、只导入公开 API 的入口 fixture用pnpm bundle-compare-selected --base main scratchpad/my-fixture.ts得到当前与基线的体积对比表格若需要了解体积构成用pnpm bundle-analyze scratchpad/my-fixture.ts生成分析产物优先读*.raw-data.json定位大模块若只需当前体积用pnpm --dir packages/tools/bundle report fixture多次测量时加--keep-base结束后git worktree remove --force tmp/bundle-base并用git worktree list验证清理完成。整套工具链保证了测量结果可复现、可对比、可审计从重新构建当前 checkout到基线 worktree 的 stamp 校验从fixture 复制进基线以隔离源码改动到gzip level 9 的精确字节统计每一步都在源码层面有据可查适合作为库项目包体积回归防护的参考实现。【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考