TensorFlow.js 仓库 Bazel 迁移实战指南:从 ts_library 构建到 pkg_npm 发布
TensorFlow.js 仓库 Bazel 迁移实战指南从 ts_library 构建到 pkg_npm 发布【免费下载链接】tfjsA WebGL accelerated JavaScript library for training and deploying ML models.项目地址: https://gitcode.com/gh_mirrors/tf/tfjs导读BAZEL_MIGRATION.md是 TensorFlow.jsTFJSmonorepo 中指导各子包迁移到 Bazel 构建体系的官方操作手册。本篇文章以该文档为主线结合仓库内真实存在的BUILD.bazel、tools/*.bzl宏定义、link-package伪包机制与发布脚本完整还原编译 → 打包 → 测试 → 发布 → 下游依赖改造的整条迁移链路。读完本文你将掌握 TFJS 仓库中一个包从传统的 npm/rollup 构建切换到 Bazel 的每一步实操如何用ts_library编译源码、用tfjs_bundle生成多格式 bundle、用nodejs_test与tfjs_web_test跑测试、用pkg_npm产出可发布产物并理解仓库中link-package与根package.json在 Bazel 构建体系里扮演的关键角色。说明本文所有命令、构建文件与脚本均以当前仓库实际内容为准。该迁移在仓库中仍在持续推进文中步骤可能随构建宏的演进而调整。一、迁移背景与范围ScopeTFJS 是一个包含十余个子包的 monorepotfjs-core、tfjs-backend-cpu、tfjs-backend-webgl、tfjs-layers、tfjs-converter 等。迁移到 Bazel 意味着每个包需要新增一系列 Bazel 目标target来完成三件事构建包本身编译 TypeScript 源码并产出 bundle运行包的测试Node 测试与浏览器测试打包用于发布到 npm生成dist目录与各格式产物。迁移策略是从根包向叶子包逐步推进仓库首先迁移根包tfjs-core、tfjs-backend-cpu再逐步扩展到依赖它们的叶子包。这一策略取代了早期新老两套构建并行维护的做法——后者的失败原因是 Bazel 对 ts 源码提出了一些结构上的要求例如必须显式声明依赖、输出目录与源码目录分离导致两套构建难以长期共存。从当前仓库看迁移范围已扩展到 scripts/bazel_packages.ts 中登记的tfjs-core、tfjs-backend-cpu、tfjs-tfdf、tfjs-tflite、tfjs-converter、tfjs-backend-webgl、tfjs-backend-webgpu、tfjs-layers、tfjs-data、tfjs-backend-wasm共十个包可以直观看到迁移的推进进度。二、迁移前的关键注意事项Caveats动手迁移前务必先理解 Bazel 构建体系与既有 npm 工作流之间的几个本质差异依赖必须显式声明Bazel 只会把你显式声明为依赖/输入的文件和规则提供给构建。这与 Node 的模块解析就近查找node_modules完全不同任何漏声明的依赖都会直接导致构建失败。所有 Bazel 构建共用根package.jsonBazel经由rules_nodejs只认仓库根目录的package.json与根node_modules。包的依赖必须同时加入根package.json只要在 BUILD 文件中用npm//dependency-name引用依赖构建就只会看到根node_modules不会误用包自己的node_modules。作为代价即使构建不读包的node_modules为了编辑器代码补全正常工作可能仍需在包内运行一次yarn。版本固定风险对tensorflow作用域包的显式固定版本依赖可能引发问题这会影响某些被迁移到 Bazel 的 demo。文档建议 demo 暂时留在 Bazel 之外保持其易读性。三、迁移七步走完整操作流程以下步骤对大多数包通用wasm、react native 等特殊包除外每一步都以当前仓库中 tfjs-core 的真实配置为范例。步骤 1确保所有依赖已迁移一个包的依赖必须先行迁移才能迁移它自身。依赖清单可从对应 issue见原文档引用的 #5287中获取也可以直接查看仓库根目录的 scripts/package_dependencies.json其中记录了各包之间的依赖关系。步骤 2向根package.json添加依赖将包package.json中的 dependencies 同步添加到仓库根 package.json 中。原因如前所述rules_nodejs通过单一根package.json解析所有 npm 依赖。此步完成后包内构建对npm//xxx的引用才有解析依据。步骤 3在包根目录创建BUILD.bazelBazel 会在BUILD和BUILD.bazel文件中查找目标。TFJS 统一使用.bazel扩展名blaze 内部使用BUILD为避免冲突故如此约定。该文件负责包级规则例如 npm 打包pkg_npm、bundle 生成tfjs_bundle、跨包测试nodejs_test等。编辑器可安装 Bazel 官方 VSCode 扩展获得 Starlark 语法高亮。仓库中 tfjs-core/BUILD.bazel 即为范例它包含了tfjs_bundle、copy_ts_library_to_dist、copy_to_dist、copy_file、pkg_npm、tfjs_web_test、nodejs_test与test_suite等完整目标。步骤 4在src目录创建另一个BUILD.bazelsrc/BUILD.bazel负责用ts_library编译包的 TypeScript 源码也可能定义测试 bundle。仓库中的 tfjs-core/src/BUILD.bazel 是这一层的完整样板。步骤 5用ts_library编译源码ts_library是rules_nodejs提供的规则。TFJS 在其 tools/defaults.bzl 中对其做了仓库级封装设置了默认值默认tsconfig为//:tsconfig_ts_library该 tsconfig 未开启incremental因为ts_library在 Bazel 目标层面做增量重建而非 TypeScript 编译器层面默认devmode_target es5commonjs 输出供 nodejs 直接使用默认prodmode_target es2017ESModule 输出后续在tfjs_bundle.bzl中再降级到 es5默认按module_name推导package_name。tfjs-core 的核心源码编译目标如下tfjs-core/src/BUILD.bazel# Compiles the majority of tfjs-core using the tensorflow/tfjs-core/dist # module name. ts_library( name tfjs-core_src_lib, srcs glob( [**/*.ts], exclude TEST_SRCS TEST_ENTRYPOINTS [index.ts], ), module_name tensorflow/tfjs-core/dist, deps [ npm//types/jasmine, npm//types/long, npm//types/node, npm//types/offscreencanvas, npm//types/seedrandom, npm//webgpu/types, npm//jasmine, npm//long, npm//node-fetch, npm//seedrandom, ], ) # Compiles the index.ts entrypoint of tfjs-core separately from the rest of # the sources in order to use the tensorflow/tfjs-core module name instead # of tensorflow/tfjs-core/dist, ts_library( name tfjs-core_lib, srcs [index.ts], module_name tensorflow/tfjs-core, deps [ :tfjs-core_src_lib, ], )这里的核心技巧是用两次ts_library区分模块名绝大多数源文件按tensorflow/tfjs-core/src/相对路径被引用因此module_name设为tensorflow/tfjs-core/dist而入口index.ts需要能被import ... from tensorflow/tfjs-core使用所以单独编译并设置module_name tensorflow/tfjs-core。如果你的包存在从dist导入的情况例如import {} from tensorflow/tfjs-core/dist/ops/ops_for_converter那么该导入应对应目标包src/BUILD.bazel中某个设置了正确module_name的规则——去对方的 BUILD 文件里找到包含该文件且模块名匹配的目标即可。步骤 6用tfjs_bundle生成多格式 bundle编译完成后需要把产物打包成单个文件。规则放在包根BUILD.bazel而非src/BUILD.bazel。为了支持不同的运行环境浏览器 script 标签、Node、ESModule、微信小程序等TFJS 为每个包生成多种 bundle统一封装在tfjs_bundle宏中。其定义位于 tools/tfjs_bundle.bzl对每个minify ∈ {true, false}组合会生成四类 bundle产物名name 为宏传入名格式目标{name}.es2017[.min]UMDES2017 UMD{name}[.min]UMDes5旧浏览器 script 标签{name}.fesm[.min]ESMES Module 消费方{name}.node[.min]CJSes5Node.js以 tfjs-core 为例tfjs-core/BUILD.bazeltfjs_bundle( name tf-core, entry_point //tfjs-core/src:index.ts, external [ crypto, ], leave_as_require [ crypto, node-fetch, util, ], umd_name tf, deps [ //tfjs-core/src:tfjs-core_lib, ], )参数含义如下namebundle 基名所有产物由此派生entry_point打包入口指向src下的ts_library目标中的源文件umd_nameUMD 全局变量名如tfexternal排除在 bundle 之外的模块 IDglobals的键会自动并入globals从模块 ID 到全局变量的映射用于解析外部模块leave_as_require保持为require()调用、不转成import的模块列表——commonjs插件会把require()转为import但某些模块必须保留原样见 tools/tfjs_bundle.bzl 的注释minify是否用 terser 压缩宏内部对每个格式都生成压缩与非压缩两版以便用 rollup-plugin-visualizer 对压缩版做 bundle 可视化。该宏底层调用tfjs_rollup_bundle依次经过_make_rollup_config生成 rollup 配置渲染 tools/rollup_template.config.mjs 模板再调用npm//bazel/rollup的rollup_bundle依赖链中包含 commonjs、node-resolve、sourcemaps、terser、visualizer 等 rollup 插件以及仓库自研的downlevel_to_es5_plugin和make_rollup_config见 tools/tfjs_bundle.bzl。步骤 7编译测试并用nodejs_test跑 Node 测试7.1 用ts_library编译测试在src/BUILD.bazel中用ts_library编译测试。tfjs-core 的特殊之处在于它会把测试文件一起发布下游包要复用这些测试例如 webgpu 包跑 core 的后端无关测试因此必须设置module_name tensorflow/tfjs-core/dist不发布测试的包可以省略module_name。文档提到未来大版本可能停止把测试发布到 npm。# tfjs-core/src/BUILD.bazel ts_library( name tfjs-core_test_lib, srcs glob( TEST_SRCS, exclude TEST_ENTRYPOINTS, ) [ :tests, :version_test, ], # TODO(msoulanille): Mark this as testonly once its no longer needed in the # npm package (for other downstream packages tests). module_name tensorflow/tfjs-core/dist, deps [ :tfjs-core_lib, :tfjs-core_src_lib, ], )7.2 更新测试入口点许多包有src/run_tests.ts或类似文件用于指定 Jasmine 要运行的测试文件路径。由于 Bazel 的输出出现在不同位置路径必须相应更新且.ts要改为.js——因为不再用ts-node跑 Node 测试输入变成了ts_library编译出的.js产物// 迁移前 const coreTests node_modules/tensorflow/tfjs-core/src/tests.ts; const unitTests src/**/*_test.ts; // 迁移后 const coreTests tfjs-core/src/tests.js; const unitTests the-package-name/src/**/*_test.js;同时运行测试的nodejs_test规则必须设置link_workspace_root True否则测试文件在运行时无法访问。该属性在 tfjs-core/BUILD.bazel 与 tfjs-backend-cpu/BUILD.bazel 中均有体现。7.3 为什么不用jasmine_node_testTFJS 的测试框架支持通过jasmine_util.ts中的setTestEnvs与setupTestFilters精细控制要运行的测试它们被用在自定义 Jasmine 入口文件setup_test.ts中。这套机制与jasmine_node_test自带 Jasmine 启动入口不兼容因此改用nodejs_test规则。# tfjs-core/BUILD.bazel load(build_bazel_rules_nodejs//:index.bzl, js_library, nodejs_test) # This is necessary for tests to have access to # the package.json so src/version_test.ts can require() it. js_library( name package_json, srcs [ :package.json, ], ) nodejs_test( name tfjs-core_node_test, data [ //tfjs-backend-cpu/src:tfjs-backend-cpu_lib, //tfjs-core/src:setup_test_lib, //tfjs-core/src:test_node_lib, //tfjs-core/src:tfjs-core_lib, //tfjs-core/src:tfjs-core_src_lib, //tfjs-core/src:tfjs-core_test_lib, ], entry_point //tfjs-core/src:test_node.ts, link_workspace_root True, tags [ci], )注意凡希望进入持续集成的测试都必须打上ci标签。当前仓库的实际写法与文档示例略有差异用setup_test_lib/test_node_lib替代了直接的package_json依赖但核心思想一致把所有编译产物作为data传入入口指向测试启动文件。步骤 8用 esbuild tfjs_web_test跑浏览器测试浏览器测试先用esbuild将测试打包为单个文件。TFJS 在 tools/defaults.bzl 中封装了esbuild默认设置resolveExtensions优先解析.mjs再.js。# tfjs-core/src/BUILD.bazel load(//tools:defaults.bzl, esbuild) esbuild( name tfjs-core_test_bundle, testonly True, entry_point setup_test.ts, external [ # webworker tests call require(tensorflow/tfjs), which # is external to the test bundle. # Note: This is not a bazel target. Its just a string. tensorflow/tfjs, worker_threads, util, ], sources_content True, deps [ :setup_test_lib, :tfjs-core_lib, :tfjs-core_test_lib, //tfjs-backend-cpu/src:tfjs-backend-cpu_lib, ], )随后esbuild bundle 被喂给tfjs_web_test宏定义于 tools/tfjs_web_test.bzl该宏底层使用karma_web_test把 bundle 提供给浏览器执行。browsers参数控制启用哪些 BrowserStack 浏览器完整浏览器列表位于 tools/karma_template.conf.js。tfjs_web_test的默认行为默认浏览器集合为bs_chrome_mac、bs_firefox_mac、bs_safari_mac、bs_ios_12、bs_android_10、win_10_chromepresubmit_browsers默认取列表第一个PR 预提交只跑这些其余浏览器自动打上nightly标签进入每晚 CIciTrue时浏览器测试还会自动打上nightly标签见 tools/tfjs_web_test.bzl。# tfjs-core/BUILD.bazel load(//tools:tfjs_web_test.bzl, tfjs_web_test) tfjs_web_test( name tfjs-core_test, srcs [ //tfjs-core/src:tfjs-core_test_bundle, ], browsers [ bs_android_10, bs_chrome_mac, bs_firefox_mac, bs_ios_12, bs_safari_mac, win_10_chrome, ], static_files [ # Listed here so sourcemaps are served //tfjs-core/src:tfjs-core_test_bundle, ], )迁移前测试由各包的karma.conf.js按路径引入迁移后所有测试必须被 bundle 显式 import 才会执行即在测试 bundle 的入口中 import 每个测试文件。为简化这一步仓库提供了enumerate_tests规则tools/enumerate_tests.bzl自动生成带全部 import 的tests.ts其底层脚本是 tools/enumerate-tests.ts它读取测试文件清单、剔除bazel-out/.../bin/前缀、把相对路径转成import ./xxx;语句后写回输出文件。# tfjs-core/src/BUILD.bazel load(//tools:enumerate_tests.bzl, enumerate_tests) # Generates the tests.ts file that imports all test entrypoints. enumerate_tests( name tests, srcs [:shared_test_srcs], root_path tfjs-core/src, )在 tfjs-core 的实际 BUILD 中shared_test_srcs是一个 filegroupglob 出所有**/*_test.ts并排除测试入口点再加上make_version_test_file生成的version_testenumerate_tests以此生成tests.ts再并入tfjs-core_test_lib编译。步骤 9更新package.json核对入口点与tfjs_bundle、ts_library的输出一致main指向 Node bundledist/tf-{package-name}.node.jsjsnext:main与module指向copy_ts_library_to_dist生成的 ESModule 输出dist/index.js。若包有浏览器测试更新sideEffects字段把ts_library在./src下生成的.mjs文件如src/foo.mjs标记为 sideEffects。原因Bazel 直接向src输出虽然随后用另一个 Bazel 规则把产物复制到dist但浏览器测试 bundle 仍然从src导入因此必须标记避免被 tree-shaking 误删。步骤 10用pkg_npm打包发布 npm发布使用rules_nodejs的pkg_npm规则但在声明发布目标前还有几个必要步骤10.1 复制编译产物到dist大部分包把编译输出放在dist目录。但ts_library的输出与源码同目录位于 Bazel 的dist/bin输出目录需要复制到dist且复制必须被 Bazel 感知这样pkg_npm才能引用。复制用copy_to_dist规则tools/copy_to_dist.bzl它在dest_dir默认dist中为srcs的每个文件建立符号链接并保持文件树的相对结构可用root参数去掉共同前缀。不能直接复制ts_library的默认输出——其默认输出是.d.ts声明文件。copy_ts_library_to_dist宏负责提取规则的 ES Module.mjs输出并重命名为.js同时把.d.ts一并复制到dist见 tools/copy_to_dist.bzl实现上通过output_group es6_sources取.mjs再以extension .js改写扩展名。load(//tools:copy_to_dist.bzl, copy_ts_library_to_dist) copy_ts_library_to_dist( name copy_src_to_dist, srcs [ //tfjs-core/src:tfjs-core_lib, //tfjs-core/src:tfjs-core_src_lib, //tfjs-core/src:tfjs-core_test_lib, ], root src, # Consider src to be the root directory of the copy # (i.e. create dist/index.js instead of dist/src/index.js) )10.2 复制 bundlestfjs_bundle的各产物同样复制到distcopy_to_dist( name copy_bundles, srcs [ :tf-core, :tf-core.es2017, :tf-core.es2017.min, :tf-core.fesm, :tf-core.fesm.min, :tf-core.min, :tf-core.node, ], )10.3 复制微信小程序miniprogram文件微信小程序需要专门的文件布局用copy_file规则逐个复制load(bazel_skylib//rules:copy_file.bzl, copy_file) copy_file( name copy_miniprogram, src :tf-core.min.js, out dist/miniprogram/index.js, ) copy_file( name copy_miniprogram_map, src :tf-core.min.js.map, out dist/miniprogram/index.js.map, )10.4 声明pkg_npm并发布load(build_bazel_rules_nodejs//:index.bzl, pkg_npm) pkg_npm( name tfjs-core_pkg, package_name tensorflow/tfjs-core, srcs [ # Add any static files the package should include here package.json, README.md, ], tags [ci], deps [ :copy_bundles, :copy_miniprogram, :copy_miniprogram_map, :copy_src_to_dist, :copy_test_snippets, # 仅 core 有用于把测试片段工具一并打包 ], )发布命令bazel run //tfjs-core:tfjs-core_pkg.publish当前仓库中pkg_npm与各复制规则的实际配置可见 tfjs-core/BUILD.bazel。步骤 11配置 npm 发布脚本在包package.json中添加发布脚本供 monorepo 的主发布脚本调用scripts: { publish-npm: bazel run :tfjs-core_pkg.publish }同时要让发布测试与发布脚本认识这个包在 scripts/publish-npm.ts 中把包名加入BAZEL_PACKAGES集合当前集合在 scripts/bazel_packages.ts 中维护含 tfjs-core、tfjs-backend-cpu、tfjs-converter、tfjs-layers 等十个包在e2e/scripts/publish-tfjs-ci.sh中把包名加入BAZEL_PACKAGES列表。还应添加只构建不发布的脚本供link-package使用build: bazel build :tfjs-core_pkg,步骤 12更新下游包的package.json路径如果没有包依赖你的包即没有包通过link依赖它可跳过本节。核心问题Bazel 的输出与源码不在同一目录——产物被符号链接到dist/bin/[package-name]/.....而不是[package-name]/dist。由于 Bazel 输出目录与 Node 模块解析算法之间的细节差异下游包不能直接link:到 Bazel 的输出。解决方案仓库维护一个link-package伪包link-package/build_deps.ts把 Bazel 产物复制进去。它拥有自己的node_modules因此能在 Bazel 产物之间正确完成 Node 模块解析。该伪包永远不会发布迁移完成后将被移除。12.1 把包加入link-package在build_deps.ts的PACKAGES列表中加入包名。对于 npm 名为tensorflow/tfjs-foo的包monorepo 目录名与PACKAGES值都是tfjs-foo其pkg_npm目标名应为tfjs-foo_pkgconst PACKAGES: ReadonlySetstring new Set([ ..., tfjs-foo, ]);build_deps.ts的完整逻辑根据传入的包名通过findTransitiveBazelDeps找出含传递依赖的所有 Bazel 包用yarn bazel build //pkg:pkg_pkg构建再把dist/bin/{pkg}/{pkg}_pkg复制到link-package/node_modules/tensorflow/下link-package/build_deps.ts。12.2 修改下游依赖路径把下游依赖该包的package.json路径改为指向link-package中的副本devDependencies: { tensorflow/tfjs-core: link:../link-package/node_modules/tensorflow/tfjs-core, tensorflow/tfjs-foo: link:../link-package/node_modules/tensorflow/tfjs-foo }查找下游包的方法grep -r --excludeyarn.lock --exclude-dirnode_modules link:.*tfjs-foo .12.3 删除下游包的build-tfjs-foo脚本scripts: { build-deps: ....... yarn build-tfjs-foo, // -- 移除 yarn build-deps-foo build-tfjs-foo: remove this script // -- 一并删除该脚本 }步骤 13把 lint 迁移到仓库级 lint 脚本13.1 加入仓库级 tslint tsconfig在 tsconfig_tslint.json 中添加路径映射并把包从exclude列表中移除paths: { ..., tensorflow/the-new-package: [the-new-package/src/index.ts], tensorflow/the-new-package/dist/*: [the-new-package/src/*] }验证 lint 是否对新包生效在某个文件里故意制造一个 lint 错误例如const x Hello, world!注意双引号然后在仓库根目录运行yarn lint。13.2 删除包自身的 TypeScript lint 脚本删除包package.json中的lint脚本、tslint.json文件以及包cloudbuild.yml中的 lint 步骤移除package.json中与 tslint 相关的依赖并运行yarn重新生成yarn.lock。步骤 14更新或删除cloudbuild.yml更新包的cloudbuild.yml删除已由 Bazel 构建的步骤——它们将由在其它包步骤之前运行的bazel-tests步骤执行任何打了ci标签的 Bazel 规则都会在 CI 中构建/测试。Bazel 产物路径不同位于tfjs/dist/bin/...依赖 Bazel 产物的残留步骤需要更新路径。若cloudbuild.yml的所有步骤都由 Bazel 接管可以删除该文件但不要把包从 scripts/package_dependencies.json 中移除。在仓库根目录运行yarn update-cloudbuild-tests重新生成 cloudbuild golden 文件。步骤 15推送前运行 Bazel linter推送前在仓库根目录运行yarn bazel:format与yarn bazel:lint-fix。CI 中也会运行 linter若仅 CI 构建失败很可能是格式问题。仓库根 package.json 中的相关脚本定义bazel:format使用 buildifier 对全部*.bzl、WORKSPACE、BUILD、BUILD.bazel文件做格式化/告警检查并派生出bazel:format-check、bazel:lint、bazel:lint-fix、bazel:lint-check等变体。步骤 16完成迁移至此全部完成。文档中这一节以庆祝符号收尾也侧面说明这是一套被仓库实际验证过的流程。四、PR 评审检查清单文档最后给出了评审他人迁移 PR 时的核对要点这也是迁移者自查的完整清单包已加入e2e/scripts/publish-tfjs-ci.sh与 scripts/publish-npm.ts 的BAZEL_PACKAGESpkg_npm生成的包包含全部所需文件如 README包已加入 link-package 的 package.json下游包已改为指向 link-package 中的副本浏览器测试覆盖所有期望的浏览器配置且会在 nightly CI 中运行浏览器测试包含全部所需测试enumerate_tests规则通常是让浏览器真正跑起测试的关键尽可能多的 cloudbuild 步骤已转为 Bazel 并移出 cloudbuild 文件若构建与测试完全由 Bazel 处理删除包的cloudbuild.yml但不从package_dependencies.json移除测试已打上nightly或ci标签tfjs_web_test会自动打上两者主pkg_npm规则已打上ci或nightly标签保证构建的各环节都被测试到package.json脚本已更新且包含bazel/bazelisk作为 devDependency包有build-npm与publish-npm脚本发布脚本使用检查生成的 bundle 体积_stats文件确认没有混入意外文件包已加入仓库级 tslint tsconfig且原始 lint 脚本已移除。五、理解仓库中的关键工具链为了让读者对整套迁移有全局认识下面把上文涉及的仓库级工具与文件汇总成一张速查表工具 / 文件路径作用ts_library/esbuild封装tools/defaults.bzl设置仓库级默认 tsconfig、编译目标es5 devmode / es2017 prodmode、esbuild 解析顺序tfjs_bundle宏tools/tfjs_bundle.bzl基于 rollup 生成 UMD/ESM/CJS 多格式 bundlecopy_to_dist/copy_ts_library_to_disttools/copy_to_dist.bzl把 Bazel 产物符号链接复制到dist.mjs改名为.jstfjs_web_test宏tools/tfjs_web_test.bzl封装karma_web_test支持 BrowserStack 多浏览器与 grep/headless 构建标志enumerate_tests规则tools/enumerate_tests.bzl生成 import 全部测试的tests.tskarma 配置模板tools/karma_template.conf.js浏览器测试的 karma 配置模板与浏览器清单rollup 配置模板tools/rollup_template.config.mjstfjs_bundle渲染的 rollup 配置模板link-package 构建脚本link-package/build_deps.ts构建 Bazel 包并复制产物到 link-package 的 node_modulesBazel 包登记表scripts/bazel_packages.ts记录已迁移的包集合包依赖关系scripts/package_dependencies.json各包之间的依赖关系迁移顺序依据六、迁移常见疑问速览Q1为什么一个包需要两个BUILD.bazel职责分离。包根的BUILD.bazel负责包级产物bundle、npm 打包、跨包 nodejs 测试src/BUILD.bazel负责源码与测试的编译ts_library、esbuild 测试 bundle。这也是仓库中 tfjs-core/BUILD.bazel 与 tfjs-core/src/BUILD.bazel 的实际划分方式。Q2module_name为什么这么重要ts_library的module_name决定编译产物的模块身份。TFJS 包之间通过tensorflow/xxx与tensorflow/xxx/dist两种路径互相引用只有module_name设置正确下游包才能以预期路径导入这些产物。这也是index.ts需要单独编译的原因。Q3浏览器测试与 Node 测试为什么走不同链路Node 测试走nodejs_test直接执行ts_library的.js产物配合setTestEnvs/setupTestFilters过滤浏览器测试走esbuild打包 →karma_web_test。两者都依赖ci/nightly标签接入 CI。Q4为什么需要link-package而不直接 link 到 Bazel 输出Bazel 输出位于dist/bin/...Node 模块解析在该位置无法正确工作link-package通过自建node_modules复制产物为 Bazel 产物之间提供正确的解析语义是迁移过渡期的桥接机制。结语BAZEL_MIGRATION.md是一份仍在演进中的迁移手册但它已经足够完整地描述了 TFJS 仓库构建体系转型的全貌从依赖声明、源码编译、多格式打包、双通道测试到 npm 发布与下游依赖改造。文中所有 BUILD 文件示例都能在当前仓库中找到一一对应的真实实现tfjs-core、tfjs-backend-cpu 等包的BUILD.bazel与 tools 目录下的宏定义读者可以将本文作为阅读这些真实构建文件的路线图或作为在自己项目中引入 Bazel 构建 TFJS 风格包的参考模板。【免费下载链接】tfjsA WebGL accelerated JavaScript library for training and deploying ML models.项目地址: https://gitcode.com/gh_mirrors/tf/tfjs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考