docs(hmr): clarify CommonJS request refresh
This commit is contained in:
1 parent
980b410340
commit
69f555f06a
6 files changed
+24
-20
No files matched your search
+6
-6
@@ -15,17 +15,17 @@
|
||||
en: e72abbf8fafc89bb
|
||||
zh: f6c59177696c5cd3
|
||||
/agent-note-expire-hmr-package-configuration/decision/hmr-package-configuration-expiry:
|
||||
en: e5677d53b4aa8c1b
|
||||
zh: 3aa660a2d13c63e2
|
||||
en: 04a76774135df963
|
||||
zh: 8f84d6d9f1fdfa9a
|
||||
/agent-note-expire-hmr-package-configuration/decision/future-work:
|
||||
en: a65f33aa459568ed
|
||||
zh: b75c5eb69420ca2d
|
||||
en: 94635542451fac60
|
||||
zh: d0e366a39c03c10c
|
||||
/agent-note-expire-hmr-package-configuration/alternatives-considered:
|
||||
en: 47590f8e9712ab5f
|
||||
zh: 681162750e3b5cfb
|
||||
/agent-note-expire-hmr-package-configuration/verification:
|
||||
en: eecb458e1c7e1b3e
|
||||
zh: ec2a3e1633d5427a
|
||||
en: 89f8e51419468406
|
||||
zh: 372e3b02773ddd01
|
||||
/agent-note-expire-hmr-package-configuration/consequences:
|
||||
en: e557562e3f7b07bc
|
||||
zh: 3cd196a63e4099b0
|
||||
+5
-3
@@ -25,6 +25,7 @@ In HMR's change dispatch, a changed file named `package.json` outside `node_modu
|
||||
- package.json reads, scope lookups, type lookups, and nearest-manifest lookups below that directory read the current file. Ownership follows the real directory when a consumer reaches the package through a link.
|
||||
- ESM `ResolveCache` is cleared. Its entries do not record every consulted manifest, and a package entry may resolve outside the package directory.
|
||||
- CommonJS `_pathCache` is cleared for the same reason. Unrelated requests recompute their resolution without unloading their modules.
|
||||
- CommonJS loads using the default resolver pass the freshly resolved filename to the native loader. Its private request alias cannot select an older entry; cached module instances remain intact.
|
||||
|
||||
Configuration invalidation alone leaves loaded modules unchanged. Stopping and restarting a Loader entry resolves its package name again and selects the new entry. Tracking active entry URLs independently of later package-name resolutions is deferred.
|
||||
|
||||
@@ -36,15 +37,16 @@ The implementation lives in `packages/boot/hmr/src/package-manifest.ts`, which e
|
||||
| `package_json_reader.getNearestParentPackageJSON` | Replaced: expired directories bypass its JS cache; a lookup without a manifest returns the native absent result |
|
||||
| The ESM Loader's `ResolveCache` instance | The prototype `get` is replaced for one lookup to obtain the instance and restored at once; the resolution cache is then cleared |
|
||||
| CommonJS `Module._pathCache` | The request-to-filename cache is cleared |
|
||||
| CommonJS `Module._load` | Default-resolver requests load by resolved filename; builtins and registered resolve hooks keep the original path |
|
||||
|
||||
The binding's `getNearestParentPackageJSON` is called only by `package_json_reader` and is not replaced. The replacements are installed at the first expiry and restored when the HMR service is disposed.
|
||||
The binding's `getNearestParentPackageJSON` is called only by `package_json_reader` and is not replaced. Each HMR instance owns a separate configuration cache. Its replacements are installed at the first expiry and restored when the service is disposed, alongside watcher and reload-queue cleanup.
|
||||
|
||||
### Future Work
|
||||
|
||||
- Online replacement inside `node_modules`, same-path reinstall, changed link targets, and cross-package reload propagation remain unsupported. Package updates need process restart; this does not guarantee that every management result already reports that requirement correctly.
|
||||
- TSX versions using an asynchronous loader thread keep that thread's package configuration outside these hooks. Synchronizing it is deferred; this change does not provide general Worker cache synchronization.
|
||||
- After exports or main moves an entry, HMR can fail to locate its old loaded module by package name. Keeping an active-entry URL association is deferred; restarting the Loader entry selects the new entry.
|
||||
- An already loaded CommonJS request can still hit Node's private request cache and return its existing module even after `require.resolve()` changes. Invalidating that request cache and replacing module instances are deferred.
|
||||
- CommonJS private-request-cache refresh with registered synchronous resolve hooks is deferred. Those requests retain their original loader behavior.
|
||||
- Disposing HMR restores the native readers, whose previous cached configuration can become visible again. Preserving invalidation state across HMR replacement is deferred.
|
||||
|
||||
## Alternatives considered
|
||||
@@ -59,7 +61,7 @@ The binding's `getNearestParentPackageJSON` is called only by `package_json_read
|
||||
|
||||
| Coverage | Location |
|
||||
|---|---|
|
||||
| Expired exports, main, imports, type, scope, and nearest manifests; parity with the native reader; the `node_modules` boundary; restoration | `packages/boot/hmr/tests/package-manifest.spec.ts` |
|
||||
| Expired exports, main, imports, type, scope, and nearest manifests; native-reader parity; actual CommonJS loads and retained module instances; the `node_modules` boundary; restoration | `packages/boot/hmr/tests/package-manifest.spec.ts` |
|
||||
| Configuration-only manifests, JSON-module and host reloads, source reloads, entry restarts, and the `node_modules` exclusion | `packages/boot/hmr/tests/package-manifest-dispatch.spec.ts` |
|
||||
|
||||
Tests need no API key and make no model calls.
|
||||
|
||||
+5
-3
@@ -25,6 +25,7 @@ HMR 的文件变化分派中,文件名为 `package.json` 且不在 `node_modul
|
||||
- 位于该目录下的 package.json 读取、scope 查询、type 查询、最近 package.json 查询,都按当前磁盘解析。消费方经由链接路径访问时,按真实目录判断归属。
|
||||
- 清空 ESM `ResolveCache`。其条目不记录查询过的全部 manifest,包入口也可能解析到包目录之外。
|
||||
- 基于相同原因清空 CommonJS `_pathCache`。无关请求重新计算解析结果,不卸载其模块。
|
||||
- 使用默认 resolver 的 CommonJS 加载把重新解析的文件名交给原生 loader。其私有请求别名不再选中旧入口,已缓存的模块实例保持不变。
|
||||
|
||||
仅使配置失效不会改变已加载模块。停止并重新启动 Loader entry 时,会重新按包名解析并选择新入口。独立于后续包名解析记录活动入口 URL 留待后续。
|
||||
|
||||
@@ -36,15 +37,16 @@ HMR 的文件变化分派中,文件名为 `package.json` 且不在 `node_modul
|
||||
| `package_json_reader.getNearestParentPackageJSON` | 替换:已失效目录绕过它的 JS 缓存;没有 manifest 时返回原生的缺失结果 |
|
||||
| ESM Loader 的 `ResolveCache` 实例 | 临时替换原型的 `get`,取得实例后立即恢复,再清空解析缓存 |
|
||||
| CJS `Module._pathCache` | 清空请求到文件名的缓存 |
|
||||
| CJS `Module._load` | 默认 resolver 的请求按解析后的文件名加载;内置模块及注册的 resolve hooks 保留原路径 |
|
||||
|
||||
binding 的 `getNearestParentPackageJSON` 只被 `package_json_reader` 自己调用,不替换。这些替换在第一次失效时安装,HMR 服务卸载时恢复。
|
||||
binding 的 `getNearestParentPackageJSON` 只被 `package_json_reader` 自己调用,不替换。每个 HMR 实例拥有独立的配置缓存,在第一次失效时安装 hook,并在服务销毁时恢复,同时清理 watcher 和重载队列。
|
||||
|
||||
### 后续工作
|
||||
|
||||
- 不支持 `node_modules` 内在线替换、同路径重装、link 换目标及跨包重载传播。包更新需要重启进程;这不保证每个管理操作的结果都已正确报告这一要求。
|
||||
- 使用异步 loader 线程的 TSX 版本,其线程内包配置不受这些 hook 管理。线程同步留待后续;本次不提供通用 Worker 缓存同步。
|
||||
- exports 或 main 改变入口后,HMR 可能无法按包名找到旧的已加载模块。保留活动 entry URL 关联留待后续;重启 Loader entry 会选择新入口。
|
||||
- 已加载的 CommonJS 请求仍可能命中 Node 私有请求缓存,即使 `require.resolve()` 改变也返回原模块。请求缓存失效及模块实例替换留待后续。
|
||||
- 注册了同步 resolve hooks 时,CommonJS 私有请求缓存刷新留待后续;这些请求保留原有 loader 行为。
|
||||
- 销毁 HMR 会恢复原生 reader,其此前缓存的配置可能重新可见。跨 HMR 替换保留失效状态留待后续。
|
||||
|
||||
## Alternatives considered
|
||||
@@ -59,7 +61,7 @@ binding 的 `getNearestParentPackageJSON` 只被 `package_json_reader` 自己调
|
||||
|
||||
| 覆盖面 | 位置 |
|
||||
|---|---|
|
||||
| 失效后的 exports、main、imports、type、scope、最近 package.json,与原生读取的一致性,node_modules 边界,恢复 | `packages/boot/hmr/tests/package-manifest.spec.ts` |
|
||||
| 失效后的 exports、main、imports、type、scope、最近 package.json,原生 reader 对照,实际 CommonJS 加载及旧模块实例保留,node_modules 边界,恢复 | `packages/boot/hmr/tests/package-manifest.spec.ts` |
|
||||
| 配置专用 manifest、JSON 模块与宿主重载、源码重载、entry 重启和 node_modules 排除规则 | `packages/boot/hmr/tests/package-manifest-dispatch.spec.ts` |
|
||||
|
||||
测试不需要 API key,不调用模型。
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
en: 45ee194ad781d603
|
||||
zh: b3d7fcd8ed73717c
|
||||
/deepseek-ai-dsh-hmr/understand-the-implementation:
|
||||
en: 22cc23a232d1a4c0
|
||||
zh: 1775437af183d999
|
||||
en: 62af58987aad9ad3
|
||||
zh: ef66c9a8fb24b6cb
|
||||
/deepseek-ai-dsh-hmr/model-experience:
|
||||
en: 215e7ba838619b7b
|
||||
zh: a311da3843709f90
|
||||
@@ -39,8 +39,8 @@
|
||||
en: 89e0624fafd701fb
|
||||
zh: 19d36a33c1d9af9d
|
||||
/deepseek-ai-dsh-hmr/known-limitations-and-deferred-work:
|
||||
en: ebfd5ba2a83aea31
|
||||
zh: 802474ad532b9002
|
||||
en: bc0c2ea681297be5
|
||||
zh: 73adddd67dcc50a3
|
||||
/deepseek-ai-dsh-hmr/known-limitations-and-deferred-work/dev-note:
|
||||
en: 7972e4b2005ec9a4
|
||||
zh: 754c49ec538c164c
|
||||
@@ -60,7 +60,7 @@ App-boot owns profile parsing and patch precedence. HMR reads the launcher’s d
|
||||
|
||||
A changed `package.json` outside `node_modules` expires that package's cached configuration. A configuration-only manifest triggers no module reload; a manifest imported as a JSON module retains dependency-driven reloads, and one in the host dependency graph retains host reloads. Dedicated configuration watches keep their existing ownership. A new package entry takes effect when its Loader entry restarts.
|
||||
|
||||
[package-manifest.ts](src/package-manifest.ts) owns the Node internal interfaces for package reads, scope, type, and nearest-manifest lookups. Manifest invalidation clears the ESM `ResolveCache` and CommonJS `_pathCache`: neither records every consulted manifest, and entries may point outside their package directory. Those are resolution caches, not module-evaluation caches; ordinary module replacement remains HMR's separate operation.
|
||||
[package-manifest.ts](src/package-manifest.ts) owns the Node internal interfaces for package reads, scope, type, and nearest-manifest lookups. Each HMR instance owns its configuration cache and restores its installed hooks on disposal. Manifest invalidation clears the ESM `ResolveCache` and CommonJS `_pathCache`: neither records every consulted manifest, and entries may point outside their package directory. With Node's default resolver, CommonJS loads resolve the request to a filename before entering the native loader, bypassing its old request alias while preserving evaluated modules. Ordinary module replacement remains HMR's separate operation.
|
||||
|
||||
Watched module paths use Node ESM resolution's `realpathSync()` spelling, including Windows short directory names, so file events match the module cache.
|
||||
|
||||
@@ -93,7 +93,7 @@ Reloading a contributing plugin can change later request prefixes; HMR does not
|
||||
- Replacing installed package versions still requires a restart through Plugin Manager; manifests below `node_modules` keep Node's cached configuration. The browser Client module graph retains its separate browser-side loading mechanism.
|
||||
- Future work: support TSX's asynchronous loader thread. Main-thread hooks do not invalidate that thread's package configuration; ordinary application-created Workers are outside the supported scope.
|
||||
- Future work: retain active entry URLs after `exports` or `main` changes. HMR still resolves entry names when locating loaded modules; after an entry moves, source changes may not reload the old entry until the Loader entry restarts.
|
||||
- Future work: an already loaded CommonJS request can retain its module through Node's private request cache even after `require.resolve()` changes. Package configuration invalidation does not replace that module instance.
|
||||
- CommonJS requests with registered synchronous resolve hooks retain the original loader path; refreshing their private request cache is deferred. Builtins retain Node's original loading behavior.
|
||||
- Future work: retain package invalidation across HMR disposal and replacement. Restoring the native readers can expose their older cached configuration again.
|
||||
- `watchConfig()` resolves when Chokidar reports readiness. On darwin, libuv starts the FSEvents stream afterwards on its own thread, so a write that lands within milliseconds of registration is not reported until the next event in that directory; edits made after startup are unaffected.
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ App-boot 负责 profile 解析和 patch 优先级规则。HMR 读取启动器提
|
||||
|
||||
`node_modules` 之外的 `package.json` 变化使该包的配置缓存失效。仅作为配置的 manifest 不触发模块重载;作为 JSON 模块导入的 manifest 保留依赖驱动重载,位于宿主依赖图中的 manifest 保留宿主重载。专属配置监听保留原有归属。新包入口在 Loader entry 重启时生效。
|
||||
|
||||
[package-manifest.ts](src/package-manifest.ts) 封装 package 读取、scope、type 和最近 manifest 查询所需的 Node internal 接口。manifest 失效会清空 ESM `ResolveCache` 和 CommonJS `_pathCache`:它们都不记录查询过的全部 manifest,入口也可能指向包目录之外。这些是解析缓存,不是模块求值缓存;普通模块替换仍由 HMR 独立处理。
|
||||
[package-manifest.ts](src/package-manifest.ts) 封装 package 读取、scope、type 和最近 manifest 查询所需的 Node internal 接口。每个 HMR 实例拥有自己的配置缓存,销毁时恢复它安装的 hook。manifest 失效会清空 ESM `ResolveCache` 和 CommonJS `_pathCache`:它们都不记录查询过的全部 manifest,入口也可能指向包目录之外。使用 Node 默认 resolver 时,CommonJS 加载先将请求解析为文件名,再进入原生 loader,绕过旧请求别名并保留已求值模块。普通模块替换仍由 HMR 独立处理。
|
||||
|
||||
被监听模块的路径沿用 Node ESM 解析所用的 `realpathSync()` 表示,包括 Windows 短目录名,使文件事件与模块缓存匹配。
|
||||
|
||||
@@ -93,7 +93,7 @@ App-boot 负责 profile 解析和 patch 优先级规则。HMR 读取启动器提
|
||||
- 通过插件管理器替换已安装包版本仍需要重启;`node_modules` 之下的 manifest 保留 Node 缓存的配置。浏览器 Client 模块图保留独立的浏览器侧加载机制。
|
||||
- 后续工作:支持 TSX 的异步 loader 线程。主线程 hook 不会使该线程内的包配置失效;普通应用自行创建的 Worker 不在支持范围内。
|
||||
- 后续工作:在 `exports` 或 `main` 改变后保留活动 entry URL。HMR 定位已加载模块时仍重新解析 entry 名称;入口移动后,源码变化可能无法重载旧入口,需要先重启 Loader entry。
|
||||
- 后续工作:已加载的 CommonJS 请求可能通过 Node 私有请求缓存保留原模块,即使 `require.resolve()` 已改变。包配置失效不替换该模块实例。
|
||||
- 注册了同步 resolve hooks 的 CommonJS 请求保留原有 loader 路径,其私有请求缓存刷新留待后续。内置模块保留 Node 原有的加载行为。
|
||||
- 后续工作:跨 HMR 销毁和替换保留包配置失效状态。恢复原生 reader 后,其旧配置缓存可能重新可见。
|
||||
- `watchConfig()` 在 Chokidar 报告就绪时 resolve。darwin 上 libuv 随后才在自己的线程启动 FSEvents 流,因此注册后数毫秒内落地的写入要等到该目录的下一个事件才会被报告;启动之后的编辑不受影响。
|
||||
|
||||
|
||||
Reference in new issue
Block a user