feat(ext/node): implement module.enableCompileCache and companions#34190
Merged
Conversation
Add the Node 22.8+ compile-cache surface (`enableCompileCache`, `flushCompileCache`, `getCompileCacheDir`, `module.constants.compileCacheStatus`, and the `NODE_COMPILE_CACHE` / `NODE_DISABLE_COMPILE_CACHE` / `NODE_COMPILE_CACHE_PORTABLE` env-var handshake) to `node:module`. The actual V8 code cache is still owned by Deno's runtime, so this layer focuses on the public API and on the debug traces (`NODE_DEBUG_NATIVE=COMPILE_CACHE`) that the `parallel/test-compile-cache-*` Node.js compat tests assert on. A tiny on-disk content-hash cache backs the `writing cache for <file>: success` / `skip persisting <file> because cache was the same` alternation so the hit/miss flow looks correct across runs. Also forward `--require`/`--import` modules through the node→deno CLI translator so `spawnSync(process.execPath, ['-r', wrapper, main])` (the pattern these tests use) actually preloads the wrapper. Adds 12 previously-failing tests to `tests/node_compat/config.jsonc`. Four `.mjs`/dynamic-import tests are marked `ignore` with explanations: their assertions check stderr for entries loaded via Deno's ESM main / dynamic-import paths, which bypass `node:module` and so can't emit the trace from this polyfill. Closes denoland/orchid#123 Co-Authored-By: Divy Srivastava <[email protected]>
…port forwarding The previous commit started forwarding `--require` / `--import` through the node->deno CLI translator. These two tests had been silently passing because the flags were being dropped before they reached deno: - `parallel/test-preload-self-referential.js` calls `-r self_ref` to preload a package by its own bare-specifier name; Deno's `--require` does not run this through Node module resolution, so the require now fails to find `self_ref`. - `es-module/test-import-preload-require-cycle.js` calls `--import preload.mjs` where the preload uses `module.registerHooks()`, which is not implemented yet in Deno (tracked in #34081). Mark both as `ignore` with the explanation. Both were already flaky in CI. Co-Authored-By: Divy Srivastava <[email protected]>
3 tasks
bartlomieju
pushed a commit
that referenced
this pull request
May 25, 2026
…ons" (#34190) (#34348) ## Summary Reverts #34190 (commit 04cbd8c). Deno already enables V8 code cache by default, so polyfilling Node's `module.enableCompileCache` / `flushCompileCache` / `getCompileCacheDir` surface on top doesn't actually buy us any additional caching — it just shadows the engine-level cache that's already on. What it *does* buy us is reads of `NODE_COMPILE_CACHE`, `NODE_DISABLE_COMPILE_CACHE`, `NODE_COMPILE_CACHE_PORTABLE`, and `NODE_DEBUG_NATIVE` at startup, which now trip `--allow-env` prompts on scripts that previously ran with just `--allow-write` (reported in #34326). Net effect of the revert: - Removes the `node:module` compile-cache API surface and its `module.constants.compileCacheStatus` enum. - Removes the `_extensions[".js" | ".cjs" | ".mjs" | ".mts"]` hooks that emitted the COMPILE_CACHE debug traces and unconditionally read those env vars. - Removes `ext/node/polyfills/internal/compile_cache.js` (471 lines). - Drops the `libs/node_shim` `--require`/`--import` forwarding — restores the prior (silent-drop) behavior for the node→deno CLI translator. The two compat tests that came along for the ride (`es-module/test-import-preload-require-cycle.js`, `parallel/test-preload-self-referential.js`) revert to the same shape they had before #34190. - Drops the corresponding `parallel/test-compile-cache-*` entries from `tests/node_compat/config.jsonc`; the permission-model-gated tests that pre-existed are left untouched. Refs #34326 Co-authored-by: divybot <[email protected]> Co-authored-by: Divy Srivastava <[email protected]>
littledivy
added a commit
to crowlKats/deno
that referenced
this pull request
Jun 10, 2026
…oland#34190) ## Summary - Polyfill the Node 22.8+ compile-cache surface on `node:module`: `enableCompileCache(dirOrOptions)`, `flushCompileCache()`, `getCompileCacheDir()`, the `module.constants.compileCacheStatus` enum (`ENABLED` / `ALREADY_ENABLED` / `FAILED` / `DISABLED`), and the `NODE_COMPILE_CACHE` / `NODE_DISABLE_COMPILE_CACHE` / `NODE_COMPILE_CACHE_PORTABLE` env-var handshake. - Hook `Module._extensions[".js" | ".cjs" | ".mjs" | ".mts"]` so that when `NODE_DEBUG_NATIVE=COMPILE_CACHE` is set, the polyfill emits the same `reading cache from … / was not initialized, initializing the in-memory entry / writing cache for … success / cache for … was accepted, keeping the in-memory entry / skip persisting … because cache was the same` traces that the `parallel/test-compile-cache-*` Node.js compat tests assert on. A small content-hash cache is written under `<dir>/<bucket>/<hash>` so the hit/miss flow stays correct across runs (with the bad-syntax path rolling back its entry). - Forward `--require` / `--import` from the node→deno CLI translator (`libs/node_shim`) so that `spawnSync(process.execPath, ['-r', wrapper, main])` — the pattern these tests use — actually preloads the wrapper. Without this the translator was dropping `-r` and the wrapper never ran. - Wire `node:module`'s `loadESMFromCJS` for `.mjs`/`.mts` through a thin trampoline so the same traces fire for `require()`'d ESM modules. 12 previously-failing tests are added to `tests/node_compat/config.jsonc` as expected passes; 4 `.mjs`/dynamic-import-driven tests (`test-compile-cache-esm.js`, `test-compile-cache-portable-esm.js`, `test-compile-cache-dynamic-import.js`, `test-compile-cache-bad-syntax.js`) are marked `ignore` with an explanation: their assertions look for the debug trace on modules loaded through Deno's ESM main / dynamic-import paths, which bypass `node:module` and therefore can't emit the trace from this polyfill. Closes denoland/orchid#123 ## Test plan - [x] `cargo test --test node_compat -- parallel::test-compile-cache` — 17 pass, 7 `ignore` (3 permission-model + 4 ESM-entry), 0 fail. - [x] `deno lint` clean for `ext/node/polyfills/01_require.js` and `ext/node/polyfills/internal/compile_cache.js`. - [x] `cargo clippy -p deno_node -p node_shim` clean. - [ ] CI green. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: divybot <[email protected]> Co-authored-by: Divy Srivastava <[email protected]>
littledivy
added a commit
to crowlKats/deno
that referenced
this pull request
Jun 10, 2026
…ons" (denoland#34190) (denoland#34348) ## Summary Reverts denoland#34190 (commit 04cbd8c). Deno already enables V8 code cache by default, so polyfilling Node's `module.enableCompileCache` / `flushCompileCache` / `getCompileCacheDir` surface on top doesn't actually buy us any additional caching — it just shadows the engine-level cache that's already on. What it *does* buy us is reads of `NODE_COMPILE_CACHE`, `NODE_DISABLE_COMPILE_CACHE`, `NODE_COMPILE_CACHE_PORTABLE`, and `NODE_DEBUG_NATIVE` at startup, which now trip `--allow-env` prompts on scripts that previously ran with just `--allow-write` (reported in denoland#34326). Net effect of the revert: - Removes the `node:module` compile-cache API surface and its `module.constants.compileCacheStatus` enum. - Removes the `_extensions[".js" | ".cjs" | ".mjs" | ".mts"]` hooks that emitted the COMPILE_CACHE debug traces and unconditionally read those env vars. - Removes `ext/node/polyfills/internal/compile_cache.js` (471 lines). - Drops the `libs/node_shim` `--require`/`--import` forwarding — restores the prior (silent-drop) behavior for the node→deno CLI translator. The two compat tests that came along for the ride (`es-module/test-import-preload-require-cycle.js`, `parallel/test-preload-self-referential.js`) revert to the same shape they had before denoland#34190. - Drops the corresponding `parallel/test-compile-cache-*` entries from `tests/node_compat/config.jsonc`; the permission-model-gated tests that pre-existed are left untouched. Refs denoland#34326 Co-authored-by: divybot <[email protected]> Co-authored-by: Divy Srivastava <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
node:module:enableCompileCache(dirOrOptions),flushCompileCache(),getCompileCacheDir(), themodule.constants.compileCacheStatusenum (ENABLED/ALREADY_ENABLED/FAILED/DISABLED), and theNODE_COMPILE_CACHE/NODE_DISABLE_COMPILE_CACHE/NODE_COMPILE_CACHE_PORTABLEenv-var handshake.Module._extensions[".js" | ".cjs" | ".mjs" | ".mts"]so that whenNODE_DEBUG_NATIVE=COMPILE_CACHEis set, the polyfill emits the samereading cache from … / was not initialized, initializing the in-memory entry / writing cache for … success / cache for … was accepted, keeping the in-memory entry / skip persisting … because cache was the sametraces that theparallel/test-compile-cache-*Node.js compat tests assert on. A small content-hash cache is written under<dir>/<bucket>/<hash>so the hit/miss flow stays correct across runs (with the bad-syntax path rolling back its entry).--require/--importfrom the node→deno CLI translator (libs/node_shim) so thatspawnSync(process.execPath, ['-r', wrapper, main])— the pattern these tests use — actually preloads the wrapper. Without this the translator was dropping-rand the wrapper never ran.node:module'sloadESMFromCJSfor.mjs/.mtsthrough a thin trampoline so the same traces fire forrequire()'d ESM modules.12 previously-failing tests are added to
tests/node_compat/config.jsoncas expected passes; 4.mjs/dynamic-import-driven tests (test-compile-cache-esm.js,test-compile-cache-portable-esm.js,test-compile-cache-dynamic-import.js,test-compile-cache-bad-syntax.js) are markedignorewith an explanation: their assertions look for the debug trace on modules loaded through Deno's ESM main / dynamic-import paths, which bypassnode:moduleand therefore can't emit the trace from this polyfill.Closes denoland/orchid#123
Test plan
cargo test --test node_compat -- parallel::test-compile-cache— 17 pass, 7ignore(3 permission-model + 4 ESM-entry), 0 fail.deno lintclean forext/node/polyfills/01_require.jsandext/node/polyfills/internal/compile_cache.js.cargo clippy -p deno_node -p node_shimclean.🤖 Generated with Claude Code