ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Deno 依赖管理全解:deno add / deno install 的配置改写、node_modules 安装与源码级原理

Deno 依赖管理全解:deno add / deno install 的配置改写、node_modules 安装与源码级原理 Deno 依赖管理全解deno add / deno install 的配置改写、node_modules 安装与源码级原理【免费下载链接】denoA modern runtime for JavaScript and TypeScript.项目地址: https://gitcode.com/GitHub_Trending/de/deno本文为 Deno 仓库中deno add/deno install pkg依赖管理链路的深度技术指南从 CLI 标志解析、配置文件deno.json/package.json的精确改写到依赖如何真正落入node_modules与 lockfile。读完本文你将掌握--dev、--save-optional、--no-save、--save-exact、--package-json等标志的端到端行为理解optionalDependencies不会被常规安装路径物化这一关键限制及其源码级 workaround并知道如何定位和验证相关代码。整体链路概览Deno 添加依赖的完整流程可以概括为四步标志解析deno add pkg或deno install pkginstall 的本地安装分支解析出统一的AddFlags结构版本决议add()将每个包请求解析为具体版本find_package_and_select_version_for_req配置改写由ConfigUpdater决定改写deno.json的imports还是package.json的某个依赖小节并落盘实际安装npm_install_after_modification()构建新CliFactory后调用cache_top_level_deps()把包物化到node_modules并更新 lockfile。核心实现集中在 cli/tools/pm/mod.rs 与 cli/tools/pm/cache_deps.rs标志解析则分布在两套解析器中见下节。两套必须保持同步的标志解析器Deno 的 CLI 标志解析目前存在于两处且必须同步维护第二套正在逐步取代第一套属于 CLI parser 拆分工作的一部分cli/args/flags.rs — 基于clap的旧版解析器。add子命令在add_subcommand()中定义install复用共享参数构造器add_dev_arg()、add_optional_arg()、add_no_save_arg()。两者最终都汇入add_parse_inner()构建AddFlags结构。libs/cli_parser/ — 新的手写解析器。命令形状声明在 libs/cli_parser/src/defs.rsADD_SUBCOMMAND、INSTALL_SUBCOMMAND在 libs/cli_parser/src/convert.rs 中add_parse及产出InstallFlagsLocal::Add的 install 分支转换为具体 flags。AddFlags结构本身定义在 libs/cli_parser/src/flags.rs并通过crate::args再导出其字段直接对应文档中提到的全部标志pub struct AddFlags { pub dev: bool, pub optional: bool, pub no_save: bool, pub default_registry: OptionDefaultRegistry, pub lockfile_only: bool, pub save_exact: bool, pub package_json: bool, pub unscoped: bool, // ... }修改AddFlags时的硬性要求来自仓库维护文档的明确警告你必须同时更新——结构体本身、两套解析器、以及每一处AddFlags { .. }字面量。字面量存在于两个解析器的测试套件 cli/args/flags.rs 与 libs/cli_parser/src/tests_full.rs 中共享测试用例add_or_install_subcommand会对add和install两个子命令循环断言是两套解析器行为一致性的护栏。大写字母短标志的 lint 约束仓库还有一条自定义 linttools/lint.js 中的ensureNoNonPermissionCapitalLetterShortFlags禁止大写字母短标志除非在带文档化先例的显式白名单中。-Ddev与-Osave-optional在白名单内二者的正当性都来自与npm install对应短标志保持一致。这也是为什么在 libs/cli_parser/src/defs.rs 的INSTALL_SUBCOMMAND定义中你能看到dev绑定短标志D、save-optional绑定O并分别声明了conflicts_with互斥关系。依赖写到哪里配置改写器cli/tools/pm/mod.rs 是配置改写的核心。add()入口约 L536先解析每个包请求并选出具体版本再决定触碰哪个配置文件。两个可能同时生效的配置文件load_configs()L442负责发现配置规则如下deno.json依赖写入imports字段package.json依赖写入某个依赖小节dependencies/devDependencies/optionalDependencies。重要行为load_configs()在找不到合适的配置文件时会创建一个create_deno_json()/create_package_json()写入一个{}因为 Deno 需要一个配置来管理node_modules。当deno.json不存在、package.json存在且请求中没有jsr:前缀的规范时会直接使用package.json而不创建deno.json。npm 包在两者并存时落入哪边由三者共同决定--package-json标志add_flags.package_json强制 package.json 管理此时若package.json不存在会被创建preferPackageJson配置项效果等同--package-json路径就近原则从源码看add()中prefer_npm_config的计算是用path_distance()比较两个配置文件到起始目录的距离——如果deno.json比package.json离 CWD 更近npm 依赖会写入更近的deno.json源码注释给出的例子deno.json在 CWD 而package.json在父目录时写入deno.json。另有一个前置校验值得注意若deno.json中存在importMap字段deno add/deno install pkg会直接报错提示需要把 import map 内联进 Deno 配置文件mod.rs 中的bail!。ConfigUpdater::add按 DependencyKind 精确改写实际的改写由ConfigUpdater::add(selected, kind)L186完成kind是DependencyKind枚举enum DependencyKind { Normal, Dev, Optional, } impl DependencyKind { fn package_json_section(self) - static str { match self { DependencyKind::Normal dependencies, DependencyKind::Dev devDependencies, DependencyKind::Optional optionalDependencies, } } }两种配置文件的改写语义不同deno.jsonkind被忽略—— 一切写入imports。源码中该分支只取root_object.object_value_or_set(imports)然后以import_name - 包名版本范围的形式插入或更新且按名称字典序选择插入位置insert_index()保持imports键有序。package.jsonkind决定小节dependencies/devDependencies/optionalDependencies。并且add()会从另外两个小节中移除同名包保证一个包永远不会被声明两次L239-L254。新建小节时new_dependency_section_index()L274保证稳定的出现顺序dependencies→devDependencies→optionalDependencies—— 例如创建optionalDependencies时优先插到已有的devDependencies之后否则插到dependencies之后最后才回落到对象末尾。对 npm 别名与 JSR 包的写入格式也值得了解package_json_dependency_entry()L392带 npm 前缀且别名与包名一致的写入包名 - 版本带自定义别名如my-libnpm:pkg^1的别名映射为npm:pkg版本jsr:包写入package.json时会被转换为 npm 兼容的npm:jsr/scope 中 / 替换为 __/name版本形式。ConfigUpdater::remove()L300与此镜像对package.json一次性清理全部三个小节中的匹配项若删空了整个小节还会顺手移除父属性并修正换行remove_prop_and_maybe_parent_prop()。改写基于jsonc_parser的 CST具体语法树进行因此能保留原有注释与格式只有modified true时commit()才会写盘。包如何真正被安装配置可选地改写并提交后add()调用npm_install_after_modification()它构建一个全新的CliFactory以便从磁盘拾取刚编辑过的配置然后调用cache_deps::cache_top_level_deps()。cache_top_level_deps()cli/tools/pm/cache_deps.rs是add、remove、install、outdated、audit、x等命令共享的安装例程其模型是从项目的import mapdeno.json的imports与package.json 依赖推导图根集合graph roots——源码中遍历 import map 条目jsr:条目会跳过 workspace 内已声明的 JSR 包、对 dist-tag 的 jsr 规范发出警告并跳过npm:条目会跳过版本匹配的 workspace npm 包用 npm 解析构建模块图build_graph_roots_with_npm_resolution由npm_installer.cache_packages(PackageCaching::All)把所有东西物化进node_modules--lockfile-only时则只做install_resolution_if_pending()的解析安装。关键坑optionalDependencies 永远不会从 package.json 被安装安装器只能看到dependencies和devDependencies。这是外部deno_package_jsoncrate 的限制PackageJsonDeps/resolve_local_package_json_deps()只暴露这两个映射——安装器使用的解析依赖中不存在optional_dependencies。因此写入optionalDependencies的包不会被常规安装路径物化即使是普通的deno install也不例外。为了让--save-optional与--save-dev后者会在 add 时安装行为对等add()选择直接安装可选包而不是依赖配置推导的根。具体实现是CacheTopLevelDepsOptions中的additional_roots: VecUrl字段cache_deps.rs#L26-L32放入其中的规范会被无条件追加到图根集合L240并安装无论它是否出现在配置文件中。源码中add()的处理逻辑mod.rs#L742-L780清晰地注释了这一点// Some packages must be resolved and installed directly as additional graph // roots, rather than relying on them being picked up from the configuration // file during the install step: // // * --no-save: the package is installed into node_modules (and the // lockfile) but not declared as a dependency at all. // * --save-optional: Denos installer does not materialize // optionalDependencies from package.json, so install the package // directly to keep parity with --save-dev (which does install on add). let install_directly add_flags.no_save || kind DependencyKind::Optional;也就是说add()会为--save-optional和--no-save见下同时填充additional_roots。真正的修复——让安装器理解optionalDependencies——是一项跨 crate 的独立改动上游deno_package_json的resolve_local_package_json_deps npm 安装器目前尚未完成。标志端到端行为对照标志解析为写入位置安装行为--dev/-DDependencyKind::DevdevDependenciesdeno.json 则仍是imports正常安装dev 依赖属于配置推导的图根--save-optional/-ODependencyKind::OptionaloptionalDependencies因安装器忽略该小节包同时被推入additional_roots保证 add 时被安装--no-save—不写配置不重写、不提交任何配置文件解析并安装进node_modules与 lockfile实现方式是跳过ConfigUpdater::add/commit调用并把包推入additional_roots--save-exact/--exact写入的版本范围保存精确版本号不带^由find_package_and_select_version_for_req的save_exact参数控制--package-json—强制package.json管理不存在则创建见load_configs()的force_package_json分支要点补充--dev、--save-optional、--no-save三者互斥在两套解析器中都用conflicts_with强制可对照 libs/cli_parser/src/defs.rs 中INSTALL_SUBCOMMAND的定义no-save与dev、save-optional、entrypoint、global互斥additional_roots还会流经cache_top_level_deps()建图条件从“有 import map”放宽为“有 import map或有 additional roots”cache_deps.rs#L55 的if has_import_map || !options.additional_roots.is_empty()因此--no-save在只有package.json而无 import map 的项目中也能工作由于--no-save跳过commit配置内容保持不变包仅进入node_modules与 lockfile。测试与验证规格测试位于tests/specs/add/下相关的目录包括 dev/、save_optional/、no_save/、package_json_flag/、exiting_dev_deps/目录中还有alias、version_ranges、prefer_package_json、jsr_prefers_deno_json等更多场景。以 tests/specs/add/no_save/test.jsonc 为例这类测试通常包含deno.json与package.json两种工程布局分别断言配置改写结果。只关心配置结果不想要下载噪音的规格测试会把add步骤的输出设为output: [WILDCARD]然后在后续eval步骤中断言文件内容。运行一个子集./x test-spec add::。解析器单元测试add_or_install_subcommand同时存在于两套解析器的测试中——cli/args/flags.rscargo test -p deno --lib add_or_installlibs/cli_parser/src/tests_full.rscargo test -p deno_cli_parser add_or_install已知限制与后续方向文档明确列出两项待办对应源码现状让安装器尊重package.json的optionalDependencies需要改动上游deno_package_json的resolve_local_package_json_deps与 npm 安装器。完成后--save-optional即可走常规的配置推导安装路径并可以移除为其设置的additional_rootsworkaround。在改动完成前--no-save对additional_roots的依赖仍需保留。--no-save在无配置目录中会创建空配置文件这是load_configs()的副作用它需要一份配置来管理node_modules与“no save”的字面语义略有冲突常见场景已有项目不受影响。小结Deno 的deno add/deno install pkg是一条“解析 → 决议 → 精确改写配置 → 重建工厂并缓存依赖”的完整管线两套标志解析器clap旧版与 libs/cli_parser/ 手写新版共同产出AddFlagscli/tools/pm/mod.rs 中的ConfigUpdater基于 JSONC CST 以保留格式的方式把依赖写入deno.json的imports或package.json的对应小节cli/tools/pm/cache_deps.rs 的cache_top_level_deps()从 import map 与 package.json 推导图根并物化node_modules。理解additional_roots这一机制是理解--no-save与--save-optional为何必须绕过“配置推导图根”模型的关键而optionalDependencies不被安装器支持这一限制则是当前实现中最需要向使用者说明的行为边界。【免费下载链接】denoA modern runtime for JavaScript and TypeScript.项目地址: https://gitcode.com/GitHub_Trending/de/deno创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表