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:
@@ -323,6 +323,26 @@ func SplitSpec(spec string) (name, rng string) {
|
||||
return spec, ""
|
||||
}
|
||||
|
||||
// SplitPath 把带子路径的名字拆成包名和子路径。
|
||||
//
|
||||
// es-toolkit → es-toolkit, ""
|
||||
// es-toolkit/compat → es-toolkit, "compat"
|
||||
// @scope/pkg → @scope/pkg, ""
|
||||
// @scope/pkg/sub/dir → @scope/pkg, "sub/dir"
|
||||
//
|
||||
// scoped 包名自带一个斜杠,所以前两段是包名。
|
||||
func SplitPath(name string) (pkg, subpath string) {
|
||||
segs := strings.Split(name, "/")
|
||||
n := 1
|
||||
if strings.HasPrefix(name, "@") {
|
||||
n = 2
|
||||
}
|
||||
if len(segs) <= n {
|
||||
return name, ""
|
||||
}
|
||||
return strings.Join(segs[:n], "/"), strings.Join(segs[n:], "/")
|
||||
}
|
||||
|
||||
func keysOf(m map[string]release) []string {
|
||||
out := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
|
||||
@@ -220,3 +220,22 @@ func pick(versions []string, r versionRange) (string, error) {
|
||||
}
|
||||
return bestRaw, nil
|
||||
}
|
||||
|
||||
// Satisfies 判断某个版本满不满足一个范围。
|
||||
//
|
||||
// 给「已经装过了,还用不用重装」这类判断用:配置里写 qs@^6,装着的是 6.16.0
|
||||
// 就跳过,是 5.x 就得换。
|
||||
//
|
||||
// 范围的写法支持范围见本文件开头;解析不了会返回错误,调用方可以选择当作
|
||||
// 「需要重装」还是直接报错。
|
||||
func Satisfies(version, rangeSpec string) (bool, error) {
|
||||
v, err := parseVersion(version)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
r, err := parseRange(rangeSpec)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return r.allows(v), nil
|
||||
}
|
||||
|
||||
@@ -121,3 +121,46 @@ func TestSplitSpec(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSatisfies(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
ver, rng string
|
||||
want bool
|
||||
}{
|
||||
{"6.16.0", "^6", true},
|
||||
{"5.9.0", "^6", false},
|
||||
{"6.16.0", "", true}, // 空范围=任意
|
||||
{"6.16.0", "*", true},
|
||||
{"1.2.9", "~1.2", true},
|
||||
{"1.3.0", "~1.2", false},
|
||||
{"1.2.3", "1.2.3", true},
|
||||
} {
|
||||
got, err := Satisfies(c.ver, c.rng)
|
||||
if err != nil {
|
||||
t.Errorf("Satisfies(%q, %q): %v", c.ver, c.rng, err)
|
||||
continue
|
||||
}
|
||||
if got != c.want {
|
||||
t.Errorf("Satisfies(%q, %q) = %v", c.ver, c.rng, got)
|
||||
}
|
||||
}
|
||||
if _, err := Satisfies("1.0.0", "^1 || ^2"); err == nil {
|
||||
t.Error("支持不了的范围该报错")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitPath(t *testing.T) {
|
||||
for _, c := range []struct{ in, pkg, sub string }{
|
||||
{"es-toolkit", "es-toolkit", ""},
|
||||
{"es-toolkit/compat", "es-toolkit", "compat"},
|
||||
{"lodash/isString", "lodash", "isString"},
|
||||
{"@scope/pkg", "@scope/pkg", ""},
|
||||
{"@scope/pkg/sub", "@scope/pkg", "sub"},
|
||||
{"@scope/pkg/a/b", "@scope/pkg", "a/b"},
|
||||
} {
|
||||
pkg, sub := SplitPath(c.in)
|
||||
if pkg != c.pkg || sub != c.sub {
|
||||
t.Errorf("SplitPath(%q) = %q, %q,该是 %q, %q", c.in, pkg, sub, c.pkg, c.sub)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user