news 2026/9/28 3:33:56

ReScript 构建系统 feature 机制详解:使用 rewatch 按需编译可选源码目录

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ReScript 构建系统 feature 机制详解:使用 rewatch 按需编译可选源码目录
  • 编译器
  • 编程语言
  • 开发工具

【免费下载链接】rescript-compiler

ReScript is a robustly typed language that compiles to efficient and human-readable JavaScript.

项目地址:https://gitcode.com/gh_mirrors/re/rescript-compiler
点击查看免费下载

导读

rewatch是 ReScript 编译器仓库中自研的增量构建工具(替代传统bsb),其 Features 规范 提供了一套**构建期可选源码(build-time optional sources)**机制:包作者可以在rescript.json中给源码目录打上feature标签,由使用者通过命令行或依赖声明决定哪些目录参与编译。本文将以该规范文档为主线,结合rewatch/src下的 Rust 实现与 rewatch/tests/features 测试套件,完整讲解 feature 的声明、选择、传递与增量清理规则,帮助你用这一机制交付带可选后端、实验模块或平台专属代码的库,而不会让消费者承担无关源码的编译成本。

Features 是什么:可选源码目录的构建期开关

Features 允许一个包声明其源码树中的可选部分,这些部分在构建时可以被包含或排除。典型场景包括:

  • 发布带多个后端的库(如 native / web),消费者只编译自己需要的那个;
  • 内置实验性模块,默认不参与构建;
  • 存放平台专属代码,按目标环境筛选。

原文档明确强调:Features 是rewatch的扩展机制,不属于旧版bsb构建配置规范(legacy build-configuration spec)。也就是说,它是在bsb之外由 rewatch 新增的rescript.json字段,旧配置完全不受影响。这一约定在源码中同样可见:config.rs 的注释写明features是 "a new feature of rewatch, and it's not part of the rescript.json spec"。

给源码目录打标签:sources中的feature属性

在rescript.json的sources数组里,任意条目都可以加一个feature属性。该目录仅当该 feature 在当前包处于激活状态时才参与构建:

{ "name": "@example/lib", "sources": [ { "dir": "src" }, { "dir": "src-native", "feature": "native" }, { "dir": "src-experimental", "feature": "experimental" } ] }

规则如下:

  • 未打标签的源码目录(无feature)永远编译,无论激活了哪些 feature;
  • 带标签目录的feature会向下级联(cascade)到嵌套的subdirs:子目录若未声明自己的feature,则继承父目录的。

级联逻辑在源码中有直接实现:config.rs 的Source::set_feature只对feature为None的子树补上父级标签,且不会覆盖子目录自己的显式声明;对应的单测test_feature_cascades_to_qualified_subdirs与test_feature_cascade_does_not_overwrite_explicit_child(config.rs)分别验证了这两种行为。

声明 feature 之间的关系:顶层features映射

顶层features映射用于声明 feature 名称及可选的含义关系(implication)——即某个 feature 激活时顺带激活其他 feature:

{ "features": { "full": ["native", "experimental"] } }

关键点:

  • 只有当需要「一个 feature 蕴含另一个」时才必须在此声明;纯叶子 feature(无蕴含关系)可以保持未声明状态,直接作为源码目录标签使用即可;
  • 上述配置下,请求full会**传递性(transitively)**激活native与experimental;
  • 循环关系(如a -> b -> a)在构建期会被拒绝并报出清晰错误。

循环检测实现在 config.rs 的expand_feature:递归展开时维护一个stack,一旦发现当前 feature 已在栈中,就返回Cycle detected in features map: ...错误并列出循环链路。测试脚本 05-features-cycle-errors.sh 构造了"a": ["b"], "b": ["a"]的配置,验证rewatch build --features a会失败且错误信息包含 "Cycle detected"。

另外,collect_declared_features(config.rs)会把features映射的键、蕴含列表中的值以及所有出现在sources标签里的 feature 名汇总成集合,用于计算「未显式限制时默认全部激活」的基线。

命令行选择:rewatch build --features

运行rewatch build或rewatch watch时,通过--features参数把编译范围限制到指定集合。不带该参数时,每个 feature 都处于激活状态,整个源码树全部编译:

rewatch build # all features active (default) rewatch build --features native # only untagged + native rewatch build --features native,full # multiple features; also expands `full`

行为细节:

  • CLI 标志只作用于当前包(即你正在构建的那个包),不会向下流到依赖;每个依赖的激活 feature 集合来自其消费者的声明(见下一节);
  • 空值(--features ""或--features ,)会被拒绝;省略标志才表示「全部激活」。

参数解析在 cli.rs 中实现:validate_features_string对空串报错「--features must not be empty. Omit the flag to build with all features active.」,并通过value_parser挂到#[arg(long = "features")]上;parsed()会把逗号分隔的原始值切分成 feature 名列表。测试build_features_flag_rejects_empty_string与build_features_flag_strips_whitespace(cli.rs)验证了空值拒绝与前后空白剔除。rewatch watch同样接受--features(测试watch_features_flag_is_parsed),且build的 feature 参数会透传给内部 watch 参数(features_flag_round_trips_through_build_to_watch_args)。

限制依赖的 feature:对象形式的依赖条目

当消费一个使用了 features 的其他 ReScript 包时,把dependencies或dev-dependencies中的字符串简写换成对象形式,并列出想要激活的 feature:

{ "dependencies": [ "@plain/dep", { "name": "@example/lib", "features": ["native"] } ] }

规则总结:

  • 简写("@plain/dep")——消费者想要该依赖的所有 feature。这是既有行为,未启用 features 的配置不受任何影响;
  • 带features的对象——消费者把依赖限制在列出的 feature 上(以及这些 feature 通过依赖自身features映射传递性蕴含的那些)。显式空列表("features": [])表示「只要未打标签的源码目录,不要任何 feature 门控代码」;
  • 不带features的对象——等价于简写,所有 feature 激活。

依赖合并语义:同一个依赖被多个消费者以不同 feature 集引用时,取各请求的并集(union)。只要任一消费者要求全部 feature,该依赖就以全部 feature 构建。Features 永远是**加性(additive)**的——启用更多 feature 不会移除模块,因此并集始终安全。

这套合并逻辑在 packages.rs 的compute_active_features中实现,其文档注释逐条对应上述规则:CLI 缺席时根包取全部声明 feature;依赖条目无features字段表示要全部;带列表则精确请求;随后把所有消费者的请求求并集,再交给resolve_active_features做传递性展开。对应单测覆盖了 CLI 缺席取全部、CLI 限制并传递展开、依赖消费方限制、显式空列表(test_compute_active_features_honours_explicit_empty_features_list,packages.rs)等场景。

与其他标志的交互:type: "dev"、--prod与clean

  • type: "dev"与--prod和 features 是正交的。一个源码目录可以同时声明type: "dev"和feature;它只在这两个过滤器都通过时才编译(不在--prod下,且 feature 已激活)。
  • rewatch clean忽略--features,总是清理每一个feature 门控目录下的全部构建产物,与当前激活了哪些 feature 无关——这让clean的行为在任何 feature 组合下都可预期。

源码佐证有两处:其一,packages.rs 的compute_active_features_prod_ignores_dev_dependency_feature_requests测试验证了--prod下不会因为dev-dependencies的简写(= 全部 feature)而把依赖翻成「全部激活」,只有非 dev 边的限制生效;其二,clean.rs 注释明确clean始终作用于全部源码目录集合,以便清理掉用户本次构建未启用的 feature 产物。

增量构建如何处理 feature 变化

  • 关闭某个 feature:该 feature 的源码文件从构建视野中移除。下一次rewatch build会看到缩小后的文件集合,并通过与处理已删除源文件相同的 diff 机制清理对应的构建产物(.mjs、.cmj等)。
  • rewatch watch场景:rescript.json中features的变更会触发全量重建(full rebuild),重新计算激活集合,并对激活的源码目录重新注册文件监听。而 CLI 的--features标志只在 watcher 启动时求值一次,修改它必须重启 watcher。

feature 关闭后旧产物被清理的行为有端到端测试验证:04-features-toggle-cleans-artifacts.sh 先全量构建出src-experimental/Experimental.mjs,再执行rewatch build --features native,断言Experimental.mjs已被移除而Native.mjs仍然存在。默认全量构建行为则由 01-features-default-all-active.sh 验证——不带--features时三个源码目录各产出一个.mjs文件。

验证 feature 名称:宽松的叶子与严格的循环

  • 未知的 feature 名(出现在 CLI 输入或源码标签中)会被当作叶子 feature 接受——它们不会匹配任何东西,除非某个源码目录恰好以该名字打标签。对应实现是resolve_active_features对未在映射中的名字直接保留(config.rs 的注释:unknown feature names are kept in the output — treated as leaf features)。
  • 顶层features映射中的循环是硬错误(hard error),错误信息会点名循环参与者,即前述expand_feature的Cycle detected报错。

完整示例:一个带可选后端与实验模块的库

综合以上全部机制,Features.md 给出的完整配置如下:

{ "name": "@example/lib", "sources": [ { "dir": "src" }, { "dir": "src-native", "feature": "native" }, { "dir": "src-web", "feature": "web" }, { "dir": "src-experimental", "feature": "experimental" } ], "features": { "all-backends": ["native", "web"] }, "dependencies": [ "@plain/dep", { "name": "@other/heavy", "features": ["native"] } ] }

对应的三种构建结果:

  • rewatch build(在@example/lib下)——编译所有源码目录;@other/heavy只以其nativefeature 构建(因为消费者只请求了它);@plain/dep以全部feature 构建;
  • rewatch build --features all-backends——编译src、src-native、src-web,跳过src-experimental;
  • rewatch build --features experimental——编译src、src-experimental,跳过两个后端目录。

仓库的测试仓库中有一个可运行的真实样例:testrepo/packages/with-features/rescript.json,其结构与本例一致(src、src-native、src-experimental三个目录,features.full蕴含native与experimental,产物后缀.mjs),配合 rewatch/tests/features 下的 6 个脚本(默认全激活、CLI 限制、传递展开、切换清理产物、循环报错、空标志拒绝),可以完整复现本文介绍的所有行为。

小结

Features 为 ReScript 包提供了一套轻量、可传递、纯构建期的可选源码开关:在sources上打标签声明可选目录,用顶层features映射表达蕴含关系,消费者则通过--features或依赖对象形式精确挑选。其核心设计——叶子 feature 无需声明、未知名字宽松接受、循环硬报错、请求取并集且严格加性、clean无视 feature 组合——保证了即使多个消费者以不同 feature 集依赖同一包,构建结果也始终安全一致。对库作者而言,这意味着实验模块、平台分支和多后端实现可以随库分发,却不会强迫每个消费者为其付出编译时间。

进一步阅读:构建配置全貌见 CompilerConfigurationSpec.md,多包仓库场景见 MonorepoSupport.md;features 的 CLI 参数与包级合并逻辑可分别深入 cli.rs 与 packages.rs。

  • 编译器
  • 编程语言
  • 开发工具

【免费下载链接】rescript-compiler

ReScript is a robustly typed language that compiles to efficient and human-readable JavaScript.

项目地址:https://gitcode.com/gh_mirrors/re/rescript-compiler
点击查看免费下载

相关推荐

上一篇:Wand-Enhancer终极指南:3步免费解锁WeMod Pro高级功能完整教程
下一篇:华为HarmonyOS设备重获Google服务的终极解决方案:microG框架完整指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 3:33:33

成都seo论坛改版实录:用3个设计规范解决被黑挂马后的性能优化

成都seo论坛改版实录:用3个设计规范解决被黑挂马后的性能优化 网站被黑挂马,后台全是广告代码,打开速度卡到怀疑人生?别慌。我在成都做SEO论坛站群时,就栽过这跟头。当时没做基础防护,被注入恶意脚本,首页加载耗时从1.2秒飙到8秒,收录直接掉零。…

作者头像 李华
网站建设 2026/9/28 3:33:28

3步搞定wordpress修改code标签,被黑挂马别慌,选型指南

3步搞定wordpress修改code标签,被黑挂马别慌,选型指南 网站后台突然弹不出正常页面,浏览器提示不安全,或者页面里莫名其妙多出了一堆乱码和跳转链接?别慌,这十有八九是被黑挂马了。很多站长这时候第一反应是删库重装,或者到处找“黑客”去洗数据,既费钱又折腾。其实,90%的挂马源头都藏在主题的模…

作者头像 李华
网站建设 2026/9/28 3:33:26

启东做网站避坑指南:3个实战案例拆解设计规范与前端落地

启东做网站避坑指南:3个实战案例拆解设计规范与前端落地 域名选错了,服务器配置拉胯,网站上线半天打不开?别慌。 启东这边不少老板找我们做网站,第一句话就是:“你们能帮我搞定域名和服务器吗?我不懂技术,怕被坑。”…

作者头像 李华
网站建设 2026/9/28 3:33:19

伺服电机脉冲接口三大模式原理与选型指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/28 3:33:20

网站开发需要多少费用?懂性能优化才不亏

网站开发需要多少费用?懂性能优化才不亏 网站做好了没人访问,这是很多老板最头疼的事。花了钱建站,结果百度搜不到,谷歌也排不上去,流量全是0。 其实这不只是设计或代码的问题,核心在于 性能优化 没做到位。搜索引擎抓取的耐心很有限,加载慢、结构乱的网站直接被降权。 今天不聊虚的,直接拆解…

作者头像 李华
网站建设 2026/9/28 3:32:56

网站建设华博互联新手入门:3招解决模板丑痛点

网站建设华博互联新手入门:3招解决模板丑痛点 做网站最怕什么?不是代码写不出来,也不是服务器买不起,而是做出来的东西“太丑”。很多新手入门建站时,图省事直接套模板,结果上线后客户第一句话就是:“这设计有点掉价。”模板网站太丑不够用,成了困扰无数中小企业老板和运营人员的头号难题。…

作者头像 李华