拓冰建站拓冰建站
首页 / 资讯中心 / 正文

core-js 贡献指南:从新增 polyfill 到维护兼容性数据的完整实践

core-js 贡献指南从新增 polyfill 到维护兼容性数据的完整实践【免费下载链接】core-jsStandard Library项目地址: https://gitcode.com/GitHub_Trending/co/core-js本篇指南以 core-js 仓库根目录的 CONTRIBUTING.md 为骨架结合仓库源码packages/core-js、packages/core-js-compat、tests等逐层展开完整讲解如何为一个标准库 polyfill 项目新增功能、维护兼容性数据、遵守编码规范并通过全套测试。读完你将掌握一套可直接照做的贡献流程定位模块与入口点、复用 internals 助手、跑通浏览器/Node.js/Bun/Deno/Rhino/Hermes 各引擎的兼容性测试以及如何在不改动任何现有功能的前提下保证新 polyfill 在 ES3 起的全引擎环境中安全落地。一、先看懂 core-js 的仓库布局在动手写 polyfill 之前理解 monorepo 的目录分工是第一步。contributing 文档反复引用的路径正好对应了 core-js 的三个关键区域packages/core-js/modulespolyfill 的实现代码所在地每一个.js文件对应一个功能模块例如esnext.set.intersection.js、es.array.at.jspackages/core-js/internals所有 polyfill 共享的内部助手helpers所在地例如获取全局内置对象的get-built-in.js、导出 polyfill 的export.js、标记 pure 版本的is-pure.jspackages/core-js-pure/overridecore-js-pure不污染全局变量的纯版本中与全局版本实现差异较大的少数模块其余部分在构建时直接复制自core-js包。除此之外兼容性数据与测试分布在packages/core-js-compatdata.mjs、modules-by-versions.mjs、mapping.mjs与tests/unit-global、unit-pure、compat、entries等目录中。文档要求共享助手优先复用已有实现这一约定保证了整个 monorepo 内数百个 polyfill 不会各自为政。二、如何新增一个 polyfill完整操作清单CONTRIBUTING.md 给出了新增 polyfill 的完整清单下面逐条展开并结合源码说明为什么。1. 实现代码放入packages/core-js/modulespolyfill 的实现文件统一放在 packages/core-js/modules。同时必须满足两条硬性约束在 ES3 及所有可能的引擎中都能正常工作。如果某个引擎例如旧版 IE无法实现该特性那么该 polyfill 绝不能破坏core-js的其他功能或宿主应用。避免通过运行时打补丁的方式观察/破坏内置对象在 polyfill 代码中缓存所有全局内置对象不反复读取globalThis上的属性并且不要从实例上调用原型方法防止被外部篡改的原型链影响 polyfill 结果。从源码可以印证这一约定internals/get-built-in.js 只实现了一个极薄的封装读取globalThis[namespace]并校验其可调用性// packages/core-js/internals/get-built-in.js module.exports function (namespace, method) { return arguments.length 2 ? aFunction(globalThis[namespace]) : globalThis[namespace] globalThis[namespace][method]; };所有 polyfill 都通过这样的助手一次性取回内置对象并缓存而不是在每次调用时动态访问全局对象。2. 共享助手放到internals并优先复用逻辑可复用的部分应提取到 packages/core-js/internals。该目录目前包含 300 多个助手文件覆盖对象属性定义、迭代器协议、promise 队列、字符转码等方方面面。新增代码前应先确认是否已有等价助手避免重复实现。3. 严禁在internals|modules中直接 import/modules/路径这是一个容易踩坑的工程约束packages/core-js/internals和packages/core-js/modules内的文件不得直接 import/modules/下的路径否则会破坏 Babel / swc 对 core-js 的优化。正确的做法是在es|stable|actual|full等入口点中声明这类依赖并通过internals/get-built-in之类的助手访问。这样工具链才能正确识别每个入口点实际依赖的模块子集实现按需打包。4. 导出 polyfill优先使用internals/export助手绝大多数场景下导出 polyfill 应使用 internals/export.jscontributing 文档中标注的packages/core-js/modules/export.js在该版本已迁移至internals目录。只有当该助手不适用时才考虑其他方式例如需要 polyfill访问器accessors的场景。这个助手的设计非常有代表性其options对象完整支持以下语义源码注释即文档options 字段含义target目标对象名global目标是否为全局对象stat以静态方法形式导出到 targetproto以原型方法形式导出到 targetreal是否为pure版本提供真实原型方法forced即使引擎已原生支持也强制导出bind将方法绑定到目标pure版本必需wrap包装构造函数以防止全局污染pure版本必需unsafe使用简单赋值而非delete definePropertysham标记该 polyfill 并非完全实现enumerable以可枚举属性导出dontCallGetSet避免触发 target 上的 gettername函数名与 key 不一致时指定.name其核心逻辑见 export.js 源码会先根据global/stat决定写入目标全局对象、globalThis[TARGET]或prototype然后通过isForced判断引擎是否原生支持——若目标已存在同类型属性且未强制导出则跳过否则调用defineBuiltIn落盘。这份代码清楚地解释了为什么在原生支持的环境里 core-js 不会重复覆盖内置方法。5. pure 版本差异放到packages/core-js-pure/override如果 pure 版本core-js-pure的实现与全局版本显著不同这是少数情况多数时候internals/is-pure常量就足够可把差异实现放入 packages/core-js-pure/override。其余部分在打包时自动从core-js复制。is-pure常量的双版本实现就是最直观的例证// packages/core-js/internals/is-pure.jscore-js 全局版本 module.exports false; // packages/core-js-pure/override/internals/is-pure.jspure 版本 module.exports true;同一模块名在两种构建中取值相反polyfill 代码据此决定是修改全局对象还是返回独立实现。6. 为 polyfill 添加特性检测与兼容性数据这一步让新 polyfill 进入 core-js 的兼容性体系在 tests/compat/tests.js 添加特性检测feature detection在 packages/core-js-compat/src/data.mjs 添加兼容性数据做法见下文更新 core-js-compat 数据在 packages/core-js-compat/src/modules-by-versions.mjs 登记 polyfill 模块名。modules-by-versions这份数据同时承担两个职责打包时生成默认 polyfill 列表以及生成索引——也就是说新增模块如果不登记按需打包工具将无法把它纳入默认清单。7. 在需要的入口点中登记根据特性类型把新模块加入以下入口目录packages/core-js/es稳定的 ECMAScript 特性packages/core-js/stable稳定集合packages/core-js/actual当前已完成、可安全使用的特性packages/core-js/full全量packages/core-js/proposals提案特性packages/core-js/stage按 TC39 阶段分组packages/core-js/webWeb 标准如atob、queueMicrotask、URL等。从仓库结构看这些目录中的每个文件都是对应模块的入口壳真正的实现在modules中——这正是第 3 条约束的意义所在工具链扫描入口点即可推导依赖树。8. 编写单元测试新增 polyfill 需要同时在 tests/unit-global全局版本和 tests/unit-pure纯版本编写单元测试纯版本的测试不得使用任何现代标准库特性因为 pure 版本本身不提供这些特性。9. 补充入口点测试在 tests/entries/unit.mjs 中添加入口点测试验证新模块在 CommonJS 入口中能被正确解析与导出。10. 遵守代码风格并跑通全部测试提交前必须确认符合 tests/eslint/eslint.config.js 定义的风格并且通过下文测试一节列出的全部测试。11. 更新文档与 CHANGELOG最后在 docs/web/docs 站点文档与 CHANGELOG.md 中登记新特性供用户检索与升级参考。三、如何更新core-js-compat数据core-js-compat是 core-js 的兼容性大脑它决定每个目标环境需要哪些 polyfill。更新数据分两步先在各引擎中采集真实结果再把结果写入数据文件。1. 在各引擎中运行兼容性测试并采集结果contributing 文档要求针对不同引擎使用不同 runner这些命令在 package.json 中均有对应脚本引擎运行方式结果输出浏览器含 chrome / safari / firefox / ie 等在浏览器中打开 tests/compat/index.html页面表格展示每个core-js模块是否必需Node.jsnpm run compat-node控制台输出追加json参数npm run compat-node json可得到 JSONDenonpm run compat-deno控制台输出追加json参数得到 JSONBunnpm run compat-bun控制台输出Rhinonpm run compat-rhino YOUR_PATH_TO_RHINO控制台输出Hermes含 React Native 内置版本npm run compat-hermes YOUR_PATH_TO_HERMES控制台输出从源码可以确认这些 runner 的真实形态例如 tests/compat/node-runner.js 会依次加载tests.js、compat-data.js与common-runner.js然后判断命令行参数中是否含有json分别以JSON.stringify(global.results)或showResults输出tests/compat/index.html 则通过加载compat-data.js、tests.js与browsers-runner.js在浏览器中渲染结果表格。2. 把采集到的数据写入数据源结果数据写入 packages/core-js-compat/src/data.mjs若需要新增版本映射例如基于 Safari 推导新的 iOS Safari 版本、基于 Chrome 推导新的 Node.js 版本写入 packages/core-js-compat/src/mapping.mjs。mapping.mjs的实现形式是上游引擎版本 → 下游引擎版本的对照表例如ChromeToNode把 Chrome 引擎版本一一映射到 Node.js 版本[3, 0.0.3]、[41, 1.0]、[58, 8.0]……这样在缺少某引擎直接测试数据时可通过映射推导其能力基线。3. 各引擎的测试方式与数据继承关系CONTRIBUTING.md 给出了完整的引擎支持矩阵这里完整列出mandatory check列标注了发布前必须人工核对的项目engine如何运行测试基础数据继承自mandatory check新版本映射androidbrowser runnerchrome,chrome-androidbunbun runnersafari仅 ESrequiredchromebrowser runnerrequiredchrome-androidbrowser runnerchromedenodeno runnerchrome仅 ES非 ES 特性requirededgebrowser runnerie,chromerequired 18electronbrowser runnerchromerequiredfirefoxbrowser runnerrequiredfirefox-androidbrowser runnerfirefoxhermeshermes runnerrequirediebrowser runnerrequirediosbrowser runnersafari若与safari不一致nodenode runnerchrome仅 ES非 ES 特性requiredoperabrowser runnerchrome若与chrome - 16不一致opera-androidbrowser runneropera,chrome-androidrequiredphantombrowser runnersafariquestbrowser runnerchrome-androidrequiredreact-nativehermes runnerhermesrequiredrhinorhino runnerrequiredsafaribrowser runnerrequiredsamsungbrowser runnerchrome-androidrequired如果你无法访问全部所需浏览器/版本contributing 文档建议借助 Sauce Labs、BrowserStack 或 Cloud Browser 等云测试服务采集结果但最终数据仍须落到data.mjs。四、风格与标准三套语法约束并行core-js 的代码风格由 tests/eslint/eslint.config.js 强制执行npm run lint即可校验。该配置规模庞大数千行规则覆盖代码正确性、风格、正则安全、复杂度乃至 ASCII 命名等维度。更关键的是不同位置的代码受不同标准库与语法上限约束polyfill 实现只能使用 ES3 语法与标准库且不得依赖全局作用域中的其他 polyfill。这是为了保证 polyfill 能在最古老的引擎中先行加载、自举运行。单元测试可使用现代语法但必须经过 babel.config.js 这个最小化 Babel 配置转译pure 版本的单元测试则不得使用任何现代标准库特性。Node.js 工具与脚本只能使用 Node.js 8 可用的语法与标准库保证贡献者的开发环境兼容性。文件命名与模块命名约定文件名一律使用 kebab-casepolyfill 模块名遵循namespace.subnamespace-where-required.feature-name约定例如esnext.set.intersection顶层 namespace 语义固定es表示稳定的 ECMAScript 特性esnext表示 ECMAScript 提案web表示其他 Web 标准。这一命名约定贯穿整个仓库——在packages/core-js/modules与tests/unit-global中es.、esnext.、web.前缀的文件名随处可见是检索和理解模块归属的第一线索。五、测试体系从全量到分项1. 准备工作初始化 monorepo任何测试之前先执行npm run prepare-monorepo该脚本负责准备 monorepo 结构并安装依赖对应 package.json 中的prepare-monorepo脚本。全量测试一条命令即可npm t从 package.json 的test脚本可以看到它实际串联了prepare与test-raw而test-raw又依次执行 lint、打包测试包、单元测试Karma Node.js Bun、Promise 测试、Observable 测试、入口点测试、compat 数据/工具测试、builder 测试以及各类一致性检查check。2. 分项测试命令一览测试项命令说明Lintnpm run lintESLint 类型定义 publint 等Karma 单元测试npx run-s prepare bundle test-unit-karma现代 Chromium、Firefox、WebKitPlaywright、古老 WebKitPhantomJS、IE11如可用Node.js 单元测试npx run-s prepare bundle test-unit-nodeBun 单元测试npx run-s prepare bundle test-unit-bunTest262npx run-s prepare bundle-package test262TC39 官方一致性测试不包含在默认测试中Promises/A 与 ES6 Promisenpx run-s prepare test-promisesECMAScript Observablenpx run-s prepare test-observablesCommonJS 入口点npx run-s prepare test-entriescore-js-compat 工具npx run-s prepare test-compat-toolscore-js-buildernpx run-s prepare test-builder注意两个细节除 lint 与npm t之外的大多数分项命令都先执行prepare并构建测试包bundle因为测试运行的是打包产物若要在某个特定浏览器中手动运行测试先执行npx run-s prepare bundle构建好包与测试 bundle然后直接打开对应页面全局版本用 tests/unit-browser/global.html纯版本用 tests/unit-browser/pure.html。六、总结一次完整贡献的检查清单把以上内容串成可执行的最终清单在 packages/core-js/modules 编写 ES3 兼容的实现缓存全局内置对象、不从实例调用原型方法复用 packages/core-js/internals 中的助手新增助手放入该目录通过 internals/export.js 导出特殊场景例外不直接跨modules路径 import必要时在 packages/core-js-pure/override 提供 pure 版本差异实现在 tests/compat/tests.js、packages/core-js-compat/src/data.mjs 与 packages/core-js-compat/src/modules-by-versions.mjs 登记检测与数据在 es / stable / actual / full / proposals / stage / web 相应入口点登记在 tests/unit-global 与 tests/unit-pure 编写单元测试在 tests/entries/unit.mjs 补充入口点测试通过npm run lint与npm t或分项命令验证在 docs/web/docs 与 CHANGELOG.md 记录变更。core-js 的贡献门槛看似严苛ES3 语法上限、多引擎兼容矩阵、数千行 lint 规则但每一道约束都对应一个明确的技术目标让 polyfill 能在任何旧引擎中安全自举、不破坏宿主环境、可被构建工具精确按需裁剪。理解了这些约束背后的源码逻辑新增一个 polyfill 就不再是照抄模板而是一次对 ECMAScript 兼容性工程全链路的系统实践。【免费下载链接】core-jsStandard Library项目地址: https://gitcode.com/GitHub_Trending/co/core-js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门