ReScript Playground Bundle 构建与集成指南:从 cmij.js 依赖生成到浏览器端编译
编译器编程语言开发工具【免费下载链接】rescript-compilerReScript is a robustly typed language that compiles to efficient and human-readable JavaScript.项目地址https://gitcode.com/gh_mirrors/re/rescript-compiler点击查看免费下载导读packages/playground是 ReScript 编译器中用于构建“Playground 浏览器端编译 Bundle”的核心工程它把编译器本体compiler.js与一批第三方库Belt、React 等的编译产物打包成cmij.js文件使得在前端环境中也能完成带完整依赖解析的 ReScript 编译。读完本文你将掌握如何生成这些cmij.js产物、如何在浏览器 / Node 环境中加载它们并调用rescript_compilerAPI 编译代码以及底层jsoo_playground_main.ml暴露的配置接口与返回结构。什么是 Playground Bundlecmij.js 的使命ReScript 编译器本身由 OCaml 实现通过js_of_ocaml可将其编译为可在浏览器运行的 JavaScript。但“能跑编译器”不等于“能编译用户代码”——真正编译一段let a div/还需要 Belt、React 等库的接口信息.cmi/.cmj编译产物注入编译器的状态。packages/playground工程的核心任务就是把这一批依赖的编译产物以cmij.js的形式固化下来。cmij.js是由js_of_ocaml build-fs生成的虚拟文件系统镜像代表一组可被 Playground Bundle 使用的“序列化依赖”。每个库包括编译器内建模块对应一个独立文件packages/compiler-builtins/cmij.js编译器内建运行时模块packages/rescript/belt/cmij.jsBelt 标准库packages/rescript/react/cmij.jsReact 及其相关绑定。加载顺序上先加载compiler.js编译器本体再依次加载各个cmij.js依赖即注入编译器的状态中之后即可编译引用这些库的代码。环境与版本约定Setup构建前需要确认两个配置文件中的版本一致性packages/playground/rescript.json声明了工程自身名称playground、源码目录srcsubdirs: true、包产物格式esmodule后缀.res.js以及构建时纳入的依赖列表{ name: playground, sources: { dir: src, subdirs: true }, package-specs: { module: esmodule, in-source: false, suffix: .res.js }, dependencies: [rescript/belt, rescript/react] }packages/playground/package.jsondependencies中的rescript/belt、rescript/react、rescript均使用workspace:^引用即与本仓库当前源码版本保持一致devDependencies包含rollup^4.32.0、rollup/plugin-node-resolve与h32.0.1-rc.26。关键前提generate_cmijs.mjs的注释明确要求“用与编译器 Bundle 相同的 rescript 版本构建 cmij 文件”You need to build cmij files with the same rescript version as the compiler bundle。版本不匹配会导致cmij.js中的接口信息与compiler.js无法兼容因此必须基于同一份仓库源码同步构建而不能混用不同版本的产物。构建流程Building在仓库根目录执行yarn workspace playground build该命令实际对应 packages/playground/package.json 中的脚本build: rescript clean rescript build node scripts/generate_cmijs.mjs rollup -c touch .buildstamp整个流水线分为四步rescript clean rescript build清理并编译 playground 自身源码node scripts/generate_cmijs.mjs调用js_of_ocaml build-fs为编译器内建模块与所有依赖生成cmij.js详见下文rollup -c依据 packages/playground/rollup.config.mjs 将packages/rescript/runtime/lib/es6/*.mjs打包为compiler-builtins/stdlib下的 ESM 输出并最终产出 UMD 格式的compiler.jstouch .buildstamp标记一次完整构建完成。注意README 中“All the cmij files will now be available in thepackages/directory”里的packages/指的是packages/playground/packages/见 packages/playground/scripts/common.mjs 中playgroundPackagesDir path.join(playgroundDir, packages)与仓库根目录下存放源码包的packages/不是同一个目录。产物目录结构构建完成后packages/playground/packages/下会形成如下结构README 原文示例tree packages packages └── rescript ├── belt │ └── cmij.js └── react ├── React.js ├── ReactDOM.js ├── ReactDOMRe.js ├── ReactDOMServer.js ├── ReactDOMStyle.js ├── ReactEvent.js ├── ReactTestUtils.js ├── ReasonReact.js ├── RescriptReactErrorBoundary.js ├── RescriptReactRouter.js └── cmij.js 3 directories, 12 files此外compiler-builtins/目录存放内建模块的cmij.js其中stdlib/子目录由 rollup 生成对应compiler.js所需的 ES6 标准库模块。React 相关库除cmij.js外还会展开各模块的独立 JS 文件供浏览器运行时按需加载。在 Playground Bundle 中加载 cmij 依赖并使用假设compiler.js就是你的 Playground BundleUMD 模块可通过module.exports或globalThis访问典型用法如下README 核心示例可由 packages/playground/playground_test.cjs 验证const { rescript_compiler } require(./compiler.js); require(./packages/compiler-builtins/cmij.js); require(./packages/rescript/belt/cmij.js); require(./packages/rescript/react/cmij.js); let comp rescript_compiler.make(); comp.rescript.compile(let a div/);由于 Belt 与 React 的接口信息已通过cmij.js注入编译器状态上述脚本可以成功编译引用了这两个库的代码例如const result comp.rescript.compile( jsxConfig({ version: 4, mode: automatic }) module A { react.component let make (~a) { let state React.useState(() 0) div {React.string(a)} /div } } let sum [1,2,3] -Belt.Array.map(x x * 2) -Array.reduce(0, (acc, item) acc item) let a A ahello / );这里的关键点是必须先require各cmij.js再调用rescript_compiler.make()创建编译器实例。cmij.js在被加载时会把对应的编译产物注册进编译器的全局状态之后创建出的实例才能解析 Belt / React 等模块名。深入generate_cmijs.mjs 的生成原理packages/playground/scripts/generate_cmijs.mjs 是整个流程的核心其逻辑可以拆解为三个部分重新构建依赖脚本开头执行yarn rescript clean与yarn rescript build确保所有依赖的.cmi/.cmj都是与当前 Bundle 相同版本刚生成的产物。内建模块单独打包将packages/rescript/runtime/lib/ocaml目录单独打成compiler-builtins的cmij.js。源码注释解释了原因内建模块必须独立成包否则在 Playground 内编译时无法被正常使用We need to build the compilers builtin modules as a separate cmij. Otherwise we cant use them for compilation within the playground。按依赖列表循环打包读取 playground 的rescript.json中dependencies字段rescript/belt、rescript/react对每个包执行buildCmijfunction buildCmij(pkgDir, pkgName) { const libOcamlFolder path.join(pkgDir, lib, ocaml); const outputFolder path.join(playgroundPackagesDir, pkgName); fs.mkdirSync(outputFolder, { recursive: true }); const cmijFile path.join(outputFolder, cmij.js); const inputFiles fs.readdirSync(libOcamlFolder).filter(isCmij).join( ); exec(js_of_ocaml build-fs -o ${cmijFile} -I ${libOcamlFolder} ${inputFiles}); } function isCmij(basename) { return /\.cm(i|j)$/.test(basename); }isCmij只筛选.cmi接口与.cmj编译单元两类文件js_of_ocaml build-fs会把它们连同目录结构一并序列化进单个cmij.js。因此每新增一个需要注入 Playground 的第三方库只需在packages/playground/rescript.json的dependencies中加入该包名yarn workspace playground build便会自动为其生成对应的cmij.js。底层 APIjsoo_playground_main.ml 暴露的编译器接口Playground 所使用的浏览器端编译 API 由 OCaml 源码 compiler/jsoo/jsoo_playground_main.ml 定义其构建规则见 compiler/jsoo/duneexecutables名为jsoo_playground_mainmodes js wasm仅在%{profile} browser时启用。该文件通过Js.export rescript_compiler导出全局对象包含api_version当前为7用于告知前端该 Bundle 支持的功能集注释中记录了演进历史v3 默认 Uncurried 并引入config.uncurriedv4 加入open_modulesv5 移除.ml支持v6 加入experimental_features与jsx_preserve_modev7 加入调试输出 API。version编译器版本号对应Bs_version.version。make()创建编译器实例返回带以下方法的对象方法说明rescript.compile(code)编译一段 ReScript 源码返回{ js_code, warnings, type_hints, type }rescript.compileWithDebug(code)编译并额外输出parsetree、typedtree、lambda、lam等调试产物rescript.format(code)对源码做格式化ReScript → ReScript 美化打印rescript.version该语言后端的版本convertSyntax(fromLang, toLang, src)语言间语法转换当前仅支持ressetModuleSystem(value)设置commonjs或esmodulesetFilename(value)设置源码文件名默认playground.ressetWarnFlags(value)设置警告 flag默认Bsc_warnings.defaults_wsetOpenModules(list)隐式打开模块列表对应config.open_modulessetExperimentalFeatures(list)启用实验性特性如LetUnwrapsetJsxPreserveMode(bool)是否开启 JSX preserve 模式getConfig()读取当前配置module_system、warn_flags、jsx_preserve_mode、experimental_features、open_modules编译结果中type字段的可能取值从Error_ret模块可以完整梳理success、syntax_error、type_error、warning_error、warning_flag_error、unexpected_error。其中warnings数组中的每条警告都带有fullMsg、shortMsg、warnNumber、isError以及行列定位信息type_hints则记录每个表达式、绑定、核心类型、类型声明的类型提示与起止行列供前端实现 hover 等交互。每次compile调用前都会执行reset_compiler ()重置警告缓冲、实验特性、Env缓存保证多次编译之间互不污染——这是 Playground 能“反复编译不同代码”的关键设计。端到端验证playground_test.cjs 的集成测试packages/playground/playground_test.cjs 提供了完整的端到端冒烟测试覆盖以 CommonJS 方式require(./compiler.js)并加载三个cmij.jsmake()后打印初始配置并通过setExperimentalFeatures([LetUnwrap])、setJsxPreserveMode(true)修改配置再打印编译一段包含 React 组件react.component、react.componentWithProps、JSX、Belt 管道Belt.Array.map、uncurried 函数与Console.log的代码断言编译结果js_code非空、无errors、且至少产生一条 warning测试代码中的未使用变量state恰好制造了这条警告否则判为失败。运行方式为yarn workspace playground test对应node ./playground_test.cjs。测试开头注释明确说明 Playground Bundle 是 UMD 模块——这解释了为什么既可以用require也可以在浏览器中通过globalThis访问。本地分发与线上发布构建完成后可通过两个脚本进一步分发yarn workspace playground serve-bundle启动 packages/playground/serve-bundle.mjs 中的 h3 静态服务器端口8888以/v{版本号}/compiler.js、/v{版本号}/rescript/belt/cmij.js等 URL 提供本地产物同时提供/playground-bundles/versions.json版本列表接口——这适合在 Playground 前端开发阶段做本地联调。yarn workspace playground upload-bundle调用 packages/playground/scripts/upload_bundle.mjs 发布到 Cloudflare R2。脚本会以v{rescript_compiler.version}作为目标目录先打包compiler.js与全部packages/产物为tar.zst归档再用rclone sync上传并维护versions.json按 semver 排序所有已发布版本。运行前提是本机安装rclone与zstd非 CI 环境下会先弹出[y/N]确认。注意事项与限制版本必须严格一致cmij.js的构建版本必须与compiler.js相同否则序列化的接口信息无法匹配源码注释与构建脚本均有明确要求。依赖由rescript.json驱动新增 Playground 库时应修改 packages/playground/rescript.json 的dependencies构建脚本会自动为其生成cmij.js。内建模块需单独打包不要试图把运行时内建模块混入某个第三方库的cmij.js否则 Playground 内编译会因找不到内建模块而失败。API 存在版本演进api_version 7意味着旧版前端需要按该字段选择对应的绑定集若使用config.experimental_features、config.jsx_preserve_mode等配置需确保 Bundle 版本 ≥ 6。语言支持范围Lang模块当前仅支持res且 v5 起已移除.ml支持convertSyntax目前也只能处理 ReScript → ReScript。整体来看packages/playground是“把 ReScript 完整编译能力搬进浏览器”的工程化落点通过cmij.js机制解决依赖注入通过jsoo_playground_main.ml提供稳定、可配置的 JS API再配合playground_test.cjs、serve-bundle.mjs与upload_bundle.mjs完成验证、联调与发布形成一条从源码到线上 Playground 的完整链路。赞分享编译器编程语言开发工具【免费下载链接】rescript-compilerReScript is a robustly typed language that compiles to efficient and human-readable JavaScript.项目地址https://gitcode.com/gh_mirrors/re/rescript-compiler点击查看免费下载相关推荐LiteRT-LM 源码构建指南从 Bazel 构建 Android 双 JAR 到集成预编译依赖LiteRT LM 源码构建指南从 Bazel 构建 Android 双 JAR 到集成预编译依赖 导读 当你在开发 LiteRT LM Android 演示大模型人工智能本地部署多模态三步解锁智慧教育平台电子课本tchMaterial-parser让教材下载变得简单三步解锁智慧教育平台电子课本tchMaterial parser让教材下载变得简单 在教育数字化浪潮中国家中小学智慧教育平台已成为师生获取教材资源的重要渠道网页爬虫教育PyTorch Android 构建与集成指南从 Maven 依赖到源码编译 libpytorch.so 的完整实践PyTorch Android 构建与集成指南从 Maven 依赖到源码编译 libpytorch.so 的完整实践 PyTorch 的 Android 集成人工智能机器学习深度学习分布式训练模型编译上一篇KBQA-BERT完全指南如何用BERT构建高效知识图谱问答系统下一篇如何通过annotated-transformer实现Transformer模型可解释性完整注意力可视化指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考