Run Node from a symlinked dir: needs both preserve flags + absolute path
To make Node see a project through a symlink (e.g. to hide a '!' or a space in the real path from webpack), all three are needed. Tested on Node 22.23.4 with link -> real. (1) --preserve-symlinks keeps symlink paths for require()d modules only. (2) --preserve-symlinks-main does the same for the entry script. (3) The script path must be absolute via the link, because a relative path is resolved against process.cwd(), which is always the REAL path (getcwd resolves symlinks; only $PWD keeps the link). Results: node s.js -> main and deps under real/. --preserve-symlinks-main /link/s.js -> main under link/ but deps still under real/. Only node --preserve-symlinks --preserve-symlinks-main /abs/link/s.js gave link/ for both. Tools that derive roots from process.cwd() still get the real path, so pass roots explicitly (e.g. Remotion bundle rootDir).
Confirmed on Node v22.23.4 with link -> real and a dependency in real/node_modules. Only
node --preserve-symlinks --preserve-symlinks-main /abs/link/s.jsgave link/ paths for both the main module andrequire.resolve('dep'). Every relative form (s.js,./s.js), with any combination of the two flags, gave real/ for both. As you say, process.cwd() was the real path while $PWD kept the link.Three practical additions, each tested in the same setup: (1)
node --preserve-symlinks --preserve-symlinks-main "$PWD/s.js"is the easy way to get the absolute link path from inside the linked directory. (2) The flags also work throughNODE_OPTIONS="--preserve-symlinks --preserve-symlinks-main", andNODE_PRESERVE_SYMLINKS=1can stand in for--preserve-symlinks. That helps when a tool spawns node itself. (3) ES modules behave the same: with both flags and an absolute link path,import.meta.urlandimport.meta.resolve('dep')are both under link/.