fix(esm): Vendor 支持子路径,不然 es-toolkit/compat 这种装不了

有些包把东西放在子路径下(es-toolkit 的 toString 只在 compat 里,主入口没有),
而摊平之后原包的 exports 映射就没了,import "es-toolkit/compat" 解析不到。

现在直接写子路径就行:

    esm.Install(ctx, "es-toolkit/compat", "app/node_modules")

拉的是根包,摊平的是子路径,落到 node_modules/es-toolkit/compat/。根包和子路径
可以共存——子路径目录嵌在根包目录里,而最小 package.json 不写 exports,
所以解析器认得出来。

npm.SplitPath 负责拆名字,scoped 包名自带一个斜杠所以前两段才是包名。
This commit is contained in:
2026-09-07 10:58:34 +08:00
parent 00d95c496f
commit ad722b12f9
6 changed files with 179 additions and 4 deletions
+19 -4
View File
@@ -30,6 +30,11 @@ import (
// fromDir 是**装好依赖的目录**(npm i 跑过的那个,它下面有 node_modules);
// outRoot 一般传脚本目录旁边的 node_modules。
//
// name 可以带子路径,比如 es-toolkit/compat——有些包把东西放在子路径下,
// 而摊平之后原包的 exports 映射就没了,子路径解析不到。这时把子路径本身
// 当成一个包摊平,落到 node_modules/es-toolkit/compat/,脚本照常
// `import { toString } from "es-toolkit/compat"`。
//
// target 要跟加载脚本的 Loader 一致,否则可能打出脚本引擎跑不了的语法——
// 用 WithTarget 改过 Loader 的话这里也要改。
func Vendor(fromDir, name, outRoot string, opts ...VendorOption) (VendorResult, error) {
@@ -39,7 +44,11 @@ func Vendor(fromDir, name, outRoot string, opts ...VendorOption) (VendorResult,
}
var res VendorResult
version, err := installedVersion(fromDir, name)
// name 可以带子路径(es-toolkit/compat)。版本从**根包**读,
// 但打包入口和输出目录都用完整的名字。
pkg, _ := npm.SplitPath(name)
version, err := installedVersion(fromDir, pkg)
if err != nil {
return res, err
}
@@ -85,11 +94,17 @@ func Install(ctx context.Context, spec, outRoot string, opts ...VendorOption) (V
}
defer os.RemoveAll(tmp)
if _, err := npm.Fetch(ctx, tmp, spec); err != nil {
return VendorResult{}, err
// spec 可能带子路径(es-toolkit/compat@^1):拉的是根包,摊的是子路径
name, rng := npm.SplitSpec(spec)
pkg, _ := npm.SplitPath(name)
fetchSpec := pkg
if rng != "" {
fetchSpec = pkg + "@" + rng
}
name, _ := npm.SplitSpec(spec)
if _, err := npm.Fetch(ctx, tmp, fetchSpec); err != nil {
return VendorResult{}, err
}
return Vendor(tmp, name, outRoot, opts...)
}