Repository navigation
Generating Compile Cache Ahead of Runtime #58482
Description
Activity
- addedfeature requestIssues requesting new Node.js features.Issues requesting new Node.js features.
on May 27, 2025 I think theoretically yes, but in practice it is more subtle:
- Static analysis only goes so far for statically imported ESM - if there's any CJS in the graph (even if it's imported) their dependencies would be excluded because
require()is not very reliable for walking. If there's any dynamicimport()in the graph with dynamic module request it would also not be reliable. Considering how common it is for CJS to show up in the wild in dependencies it might result in a lot of cache miss in the real world. - If there's any loader hooks used to mutate resolution the walk again would be unreliable without executing the hooks.
That is technically a bundler problem and different bundlers can have different behaviors, with varying degree of accuracy. The accuracy of the graph would only be a best effort if it's implemented by Node.js alone without actually executing the code. It would probably be more effective for Node.js to just provide an interface that just takes a list of files and their formats to generate the cache from, and leave the walking and the generation of that list to a third party, so that users can edit the list to make up for any misses or they can use more advanced tools to generate more accurate graphs.
There are already some pretty good existing tools in the wild that should be able to generate this list e.g. madge, dependency-cruiser, and there might be more in the future, there's no need to re-invent a non-perfect wheel in core that might not necessarily be as good as new tools that might show up in the future.
- Static analysis only goes so far for statically imported ESM - if there's any CJS in the graph (even if it's imported) their dependencies would be excluded because
Joyee says it more eloquently but I can perhaps phrase it more succinctly: too fragile to be viable.
So what if we skip walking the module tree path and only consider receiving a list of paths to generate a cache for, would that be achievable?
Possible, yes, but it seems kind of pointless. It'd be useless for the average node app.
Depending on what you want to do, you can maybe already achieve it through the
vmmodule'sproduceCachedDataoption.I think a list-based approach is still practical and wouldn't be too difficult to implement. So instead of the proposal in OP, an alternative workflow (using https://github.com/sverweij/dependency-cruiser, which provides a lot of powerful options, and this is using the most basic ones) can be
npx depcruise --output-type json --no-config ./entrypoint.js | jq '[.modules[].source] | unique' > deps.json NODE_COMPILE_CACHE=/path/to/cache/dir node --generate-compile-cache deps.json NODE_COMPILE_CACHE=/path/to/cache/dir node entrypoint.jsUsing the vm module doesn't exactly provide the same thing (e.g. cache miss handling and a directory structure that is compatible with the built-in compile cache)
Reacted by Adam HaglundReacted by Adam HaglundI would be open to writing a simple CLI npm package to generate a list for this if needed :)
Should I update the OP to this new approach, or just add a link to the new comment?
Updating the OP or adding a link SGTM.
I think this may also work nicely with #58755 so that the bundle would be portable.
Actually now I realize that the list needs more than just the paths, it also needs the module type e.g. CommonJS/ESM/TypeScript. It may also be better paired with #55756 if support for TypeScript is needed.
Reacted by Adam HaglundHey,
Do we have any update for the list based AOT Caching approach?I have a WIP here which currently works like this:
# Put the list of files + their format and the files into a workspace $ mkdir workspace $ cd workspace $ echo "console.log('hello')" > hello.js $ echo '[{ "source": "hello.js", "format": "commonjs" }]' > list.json # Build the compile cache in that workspace, using environment variables to specify cache directory (relative) and portability $ NODE_COMPILE_CACHE=.compile_cache NODE_COMPILE_CACHE_PORTABLE=1 NODE_DEBUG_NATIVE=compile_cache node --compile-cache-for list.js # Move the workspace to a different place for deployment $ cd ../ $ mv workspace deploy # Run the entry point from the deployment directory $ cd deploy $ NODE_COMPILE_CACHE=.compile_cache NODE_COMPILE_CACHE_PORTABLE=1 NODE_DEBUG_NATIVE=compile_cache node hello.js
I think it's possible to add a JS API for this too, though that would take a bit more work than just a command line option for a dedicated Node.js process to do this because we'd want a separate cache handler for that API (e.g. you probably don't want to mix the compile cache of the cache building script itself and its dependencies with the compile cache of the application).
Reacted by Adam Haglund, sharmaharisam and Nagy MátéI was in a meeting that touched on this topic and someone (I forgot who, sorry!) suggested that it would be nice it we can ship it in the Node.js distribution for the version of npm bundled, so that after people installed Node.js and run the npm bundled with it, it can just pick that up. I'll think about how to design it in a way to make that possible.
I would be open to writing a simple CLI npm package to generate a list for this if needed :)
I'm not 100% sure what all the constraints are, but if the question is "what dependencies does my package have and where are they?" then this is what
npm querywas designed to solve. Namely "how do I discover and list things in my dependencies based on common criteria?"~/.n/v/n/v/l/n/npm (master|✔) $ cd (npm prefix -g)/lib/node_modules/npm ~/.n/v/n/v/l/n/npm (master|✔) $ npm query ':root *'|jq '.[] | "name:\(.name), path=\(.location)"'|head -20 "name:@isaacs/cliui, path=node_modules/@isaacs/cliui" "name:ansi-regex, path=node_modules/@isaacs/cliui/node_modules/ansi-regex" "name:emoji-regex, path=node_modules/@isaacs/cliui/node_modules/emoji-regex" "name:string-width, path=node_modules/@isaacs/cliui/node_modules/string-width" "name:strip-ansi, path=node_modules/@isaacs/cliui/node_modules/strip-ansi" "name:@isaacs/fs-minipass, path=node_modules/@isaacs/fs-minipass" "name:@isaacs/string-locale-compare, path=node_modules/@isaacs/string-locale-compare" "name:@npmcli/agent, path=node_modules/@npmcli/agent" "name:@npmcli/arborist, path=node_modules/@npmcli/arborist" "name:@npmcli/config, path=node_modules/@npmcli/config" "name:@npmcli/fs, path=node_modules/@npmcli/fs" "name:@npmcli/git, path=node_modules/@npmcli/git" "name:@npmcli/installed-package-contents, path=node_modules/@npmcli/installed-package-contents" "name:@npmcli/map-workspaces, path=node_modules/@npmcli/map-workspaces" "name:@npmcli/metavuln-calculator, path=node_modules/@npmcli/metavuln-calculator" "name:@npmcli/name-from-folder, path=node_modules/@npmcli/name-from-folder" "name:@npmcli/node-gyp, path=node_modules/@npmcli/node-gyp" "name:@npmcli/package-json, path=node_modules/@npmcli/package-json" "name:@npmcli/promise-spawn, path=node_modules/@npmcli/promise-spawn" "name:@npmcli/query, path=node_modules/@npmcli/query"
github-actions commented
on Mar 19, 2026 on Mar 19, 2026 – with GitHub ActionsContributorMore actionsThere has been no activity on this feature request for 5 months. To help maintain relevant open issues, please add the never-stale
Issues and PRs exempt from automated stale handling. label or close this issue if it should be closed. If not, the issue will be automatically closed 6 months after the last non-automated comment.
For more information on how the project manages feature requests, please consult the feature request management document.- addedstaleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.Issues and PRs marked stale due to inactivity and scheduled for automatic closure.
on Mar 19, 2026 Pls no stale Mr GitHub bot :(
- removedstaleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.Issues and PRs marked stale due to inactivity and scheduled for automatic closure.
on Mar 20, 2026 I'm not 100% sure what all the constraints are, but if the question is "what dependencies does my package have and where are they?"
Sorry, I missed this reply, I think the question is more that - since:
- Node.js distributes npm CLI
- The version of Node.js used to run this bundled npm CLI is already known in the distribution
- Can we pre-compile the cache and put them in the bundle, so after people installed a distribution of Node.js, the first
npm somethingwill already pick up the precompiled cache compiled suing that version of Node.js
github-actions commented
on Sep 13, 2026 on Sep 13, 2026 – with GitHub ActionsContributorMore actionsThis issue has been marked as stale due to 90 days of inactivity.
It will be automatically closed in 30 days if no further activity occurs. If this is still relevant, please leave a comment or update it to keep it open.- addedstaleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.Issues and PRs marked stale due to inactivity and scheduled for automatic closure.
on Sep 13, 2026
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsAwaiting Triage
What is the problem this feature will solve?
It is currently not possible to generate the compile cache without executing the files that will be cached.
Currently, container images and lambda functions can't easily generate code caches ahead of the actual runtime, since the code may have effects you may not want to execute early.
What is the feature you are proposing to solve the problem?
Would it be possible to add a way to generate the compile cache without executing the relevant files, i.e. walking the module tree from the entry point, generating caches along the way?
Perhaps something like
node --generate-compile-cache path/to/entrypoint.mjsand/or
What alternatives have you considered?
You may be able to work around this by adding "dry-run" code paths specifically for code cache generation, but this is a hacky workaround that can require massive effort.