资讯动态

scriptc的CommonJS支持全解析:require模块图、缓存与循环引用的完整指南

发布时间:2026/10/1 8:41:21 来源:尧图企业网站定制
scriptc的CommonJS支持全解析require模块图、缓存与循环引用的完整指南【免费下载链接】scriptcTypeScript-to-Native Compiler项目地址: https://gitcode.com/GitHub_Trending/sc/scriptcscriptc是一款 TypeScript 到原生的编译器把你的 TypeScript/JavaScript 编译成无 Node 依赖的可执行文件。它的一大卖点就是完整支持 CommonJS 语义require模块图解析、require 缓存命中、循环引用判定全部按 Node.js 的运行时行为编译成原生代码。 为什么 CommonJS 支持很难做大多数编译到原生的方案只处理 ESM 静态导入而现实世界的 npm 生态大量依赖requirerequire是运行时行为模块可能延迟加载、条件加载、重复加载缓存语义同一模块第二次require必须返回同一个 exports 对象不重新执行循环引用a.cjs引b.cjsb.cjs再引a.cjsNode 用部分初始化的缓存对象兜底路径解析规则node_modules向上查找、exports/imports条件、.js/.json/.node扩展名探测本身就是一个复杂算法。scriptc 的做法是在编译期完整复刻 Node 24 默认解析器把require的运行时语义下沉为确定性的模块初始化顺序让原生二进制与 Node 的输出逐字节一致。️ require 模块图从入口到完整依赖图scriptc 的 CJS 解析器是一个纯文件系统实现复刻了 Node 24 的默认解析行为核心实现在 cjs-resolve.ts相对路径./x、../x相对当前文件目录解析裸包名从当前目录一路向上收集node_modulesNode 特有的跳过node_modules/node_modules规则也一并复刻见 cjsNodeModulePathspackage.json的exports/imports条件按node、require等条件精确匹配子路径必须是精确文件遗留探测只有旧式请求才尝试.js/.json/.node扩展名和目录index。更妙的是错误信息也按 Node 复刻找不到模块时抛出带Require stack的MODULE_NOT_FOUND包配置非法时抛ERR_INVALID_PACKAGE_CONFIG——编译期报错和你在 Node 里看到的几乎一模一样。模块图确定后lower-modules.ts 把每个源文件拆成声明部分 顶层语句顶层语句打包成该模块的%init初始化函数并按Node 的求值顺序深度优先后序入口文件最后执行排列。也就是说require的何时加载问题在编译期就彻底确定了。 require 缓存第二次 require 是缓存命中这是 CJS 语义的核心承诺也是 scriptc 测试语料里专门覆盖的场景1621-cjs-require-cache-hitconst a require(./counter.js); a.bump(); // 1 const b require(./counter.js); // 第二次 require b.bump(); // 2 a.bump(); // 3 —— 两个句柄共享同一状态在 scriptc 中每个模块的%init都带一次性执行守卫第一次require执行初始化之后所有再次访问都直接命中已初始化的导出对象——共享状态、零重复执行与 Node 的缓存行为完全一致。这正是把运行时缓存编译期固化的优雅之处。 循环引用处理良性回边 vs 硬拒绝模块图里出现环a → b → a是日常scriptc 用一个循环准入引擎逐条判定而不是见环就报错环的类型Node 的行为scriptc 的处理良性回边第二次进入时命中缓存拿到部分初始化对象用守卫式%init精确复现 Node 的求值顺序可观察半初始化状态得到undefined等中间值行为未定义编译期诊断SC1016指出具体环和原因判定逻辑见 appendForkModules 中的 cycleAdmissionReason 及其配套的makeCycleAdmission。良性环的例子可以参见 2121-esm-cycle-inert-backedge回边只绑定了裸命名空间、没有任何成员被读取Node 能良性求值scriptc 就按 Node 的顺序复现而两个声明互相依赖的硬环2382-cycle-two-decl则会被诊断拦下。一句话总结能像 Node 一样跑通就按 Node 跑Node 本身也危险的就编译期报错——永远不静默错误。 CJS 与 ESM 互操作编译期复刻 Node 的导出探测器如果你写的是 ESM 却import { x } from ./lib.cjsNode 从不执行 CJS 模块而是用它内置的cjs lexer词法扫描源文件猜出哪些名字能被具名导入——这套规则怪癖极多扫描遇到字面量值就停止、require值触发再导出等。scriptc 在 cjs-syntax.ts 中逐字节忠实移植了 Node 的探测器因此编译器能精确回答这次具名导入在 Node 里到底能不能链接成功Node 会在链接期抛SyntaxError的组合scriptc 也会在编译期给出同样的失败而不是产生一个运行时才崩的二进制。️ 静态编译不了--dynamic与--npm-static两条路真实的 npm 包往往是压缩过、无类型、面向 V8 的 JS。scriptc 提供两档策略详见官方文档 dependencies/page.mdx--dynamic把依赖的 JS 在构建期嵌入二进制由内嵌的 quickjs-ng 引擎执行动态岛。岛内require内置模块走高保真 shim值跨边界时按值拷贝并做校验——类型不匹配是可捕获的 TypeError而非内存破坏。运行时不再需要node_modules。--npm-static实验性让指定的包脱离动态岛作为普通程序模块静态编译编译器会报告每一处暂缓点达到即报错并指名具体不支持的操作。 如何快速验证 CommonJS 行为scriptc 的正确性主张是差分测试语料里的每个程序都会在Node 和编译后的二进制上各跑一遍stdout、stderr、退出码必须逐字节一致。语料库中大量 CJS 场景require 缓存、条件 require、菱形依赖、require目录中间文件等都分布在 tests/corpus/ 下例如1621-cjs-require-cache-hit —— 缓存命中与共享状态1622-cjs-require-conditional —— 条件 require1623-cjs-require-diamond —— 菱形依赖2383-cycle-assertdoc —— 循环引用与断言你也可以对自己的程序跑scriptc coverage file它会告诉你哪些语句能静态编译、哪些会落入动态岛并逐个给出带编号的诊断。 小结scriptc 的 CommonJS 支持不是能跑就行的兼容层而是一套编译期精确复刻 Node 24 语义的完整方案require 模块图——纯文件系统的 Node 24 默认解析器exports/imports/遗留探测全覆盖报错信息与 Node 一致require 缓存——一次性%init守卫固化第二次 require 即缓存命中共享状态零重复初始化循环引用——准入引擎区分良性回边按 Node 顺序复现与危险环SC1016 编译期诊断互操作与兜底——编译期复刻 cjs lexer 决定具名导入成败--dynamic/--npm-static覆盖静态编译够不到的真实 npm 生态。配合差分测试的逐字节比对你可以放心地把 CommonJS 风格的项目编译成不依赖 Node 的原生可执行文件。【免费下载链接】scriptcTypeScript-to-Native Compiler项目地址: https://gitcode.com/GitHub_Trending/sc/scriptc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑