what
|
ad722b12f9
|
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 包名自带一个斜杠所以前两段才是包名。
|
2026-09-07 10:58:34 +08:00 |
|
what
|
00d95c496f
|
feat(esm/npm): 用 Go 拉依赖树,加库不再需要 node
零依赖的包下个 tarball 就能用,有依赖的得先解析依赖树——读 semver 范围、
查注册表定版本、递归。这个包把那件事用 Go 做了,于是整条链没有 node:
esm.Install(ctx, "qs", "app/node_modules")
// qs v6.16.0 打进 50 个文件 -> 73.3 KB(依赖树 19 个包 1.7 MB)
= npm.Fetch(拉依赖树到临时目录)+ Vendor(摊平成一个文件)。
刻意不做的(它不是 npm):
- 不跑安装脚本。那是供应链攻击的主要入口,而纯 JS 库没有编译步骤
- 不管 devDependencies / peerDependencies / 平台二进制
- semver 只实现 ^ / ~ / 精确 / x / >= 这个子集
- 只平铺不嵌套
碰上支持不了的(复合范围、主版本冲突)明确报错并指向 npm + Vendor,
不猜版本——猜错了装出来能跑但行为不对,比装不上难查。
子集划得这么小是有依据的:抽 8 个常见包的 44 个传递依赖统计,^ 占 95%,
~ 和精确各一两处,主版本冲突 0 个。
安全上做了两件事:校验注册表给的 sha512(中间的缓存代理、私有源镜像
是真实存在的),以及挡住 tarball 里带 ../ 的路径。
测试全部走内存假注册表,不碰网络:依赖树平铺、共同依赖只装一次、
版本冲突报错、校验和不符、目录穿越。semver 那组表驱动——写这组时抓到
一个真 bug:1.2.x 被映射成了 ^1.2.0,只锁主版本,实际该锁到 1.2。
|
2026-09-07 10:11:51 +08:00 |
|
what
|
efb3141734
|
feat(esm): Vendor 把装好的 npm 包连同依赖摊平成单文件
解决的是「脚本要用第三方库,但目标机器上没有 node」。
npm 真正干的活是解析依赖树——读 semver 范围、查注册表定版本、递归、处理冲突。
这步绕不开,得在有 node 的机器上做一次。但做完之后依赖树就是死数据了,用
esbuild 摊平成一个文件,发布物里只带那一个就够:
qs v6.16.0 打进 47 个文件 -> 73.6 KB (原 19 个包 1.7 MB)
es-toolkit v1.52.0 打进 219 个文件 -> 51.9 KB
产物是最小的 node_modules 布局,脚本照常 import,写法完全不变。
两个实现细节:
- 入口不能直接写包名,esbuild 的 EntryPoints 是文件路径。所以造一段转发
源码当 stdin 入口,包名放进 import,才走正常的 node_modules 解析。
- 转发源码里写 export { default } 时,只有具名导出的包会报错(ESM 原生的
很多是这样),退回去用只带具名导出的版本重打一次。
打包目标从 Loader 里提成了共用常量:摊平出来的库必须跟脚本同一档,
否则库能打出脚本引擎跑不了的语法。
零依赖的包不用这个——直接下 tarball 解开就行,README 里记了命令。
|
2026-09-07 09:54:18 +08:00 |
|
what
|
6ce9b483fd
|
feat(esm): 按目录加载脚本、打包与热更新
把一个目录当脚本仓库:按路径寻址、esbuild 打包、内容变了自动重编。
- Loader 扫目录建索引,Load(name) 给出打好包的源码和版本号
- bundler esbuild 的封装。ESM 格式而不是 IIFE——IIFE 会附带一整套
CommonJS interop helper,每建一个 VM 都要重跑一遍
- plugin 把扩展的 TS 模块变成可以 import 的虚拟模块,磁盘上没有文件
- typings 把这些虚拟模块的类型按 node_modules 布局落盘,编辑器才认识
esbuild 原生实现了 Node 的模块解析,所以脚本能直接 import node_modules
里的第三方库。写出来的类型文件是 index.ts 而不是 index.d.ts:扩展给的是
真正的模块源码,里面可能带实现,声明文件里不允许有实现。
node_modules 不参与热更新的版本计算——依赖包是装出来的,改动总伴随显式的
安装动作,而真实的 npm 包动辄上千个文件,每次取脚本 stat 一遍太贵。
|
2026-09-05 22:13:57 +08:00 |
|