Repository navigation
Files inside symlink directories are not resolved when using sea #295
Description
What version of pkg are you using?
6.22.0
What version of Node.js are you using?
26.7.0
What operating system are you using?
Windows
What CPU architecture are you using?
x86_64
What Node versions, OSs and CPU architectures are you building for?
node26-win-x64, node26-linux-x64
Describe the Bug
I have a project using npm workspaces, which contains a main module for the application and an additional library module the application depends on. When running npm install, npm will create symlinks inside the node_modules directory, pointing to the actual application and library folder, so that you can import modules using the usual @test/lib format.
e.g.
package.json (name = test)
packages/lib/index.js (name = lib)
packages/lib/package.json
packages/app/index.js (name = app, dependencies = lib)
packages/app/package.json
node_modules/@test/app -> ../../packages/app
node_modules/@test/lib -> ../../packages/lib
When packaging the app without SEA, it works fine. When packaging with SEA, the app will fail to start with the error ERR_MODULE_NOT_FOUND for the import of the @test/lib package.
While debugging this issue I already figured out, that the manifest correctly contains a symlink entry with node_modules/@test/lib pointing to packages/lib. But later on when the resolvePackageInVFS function tries to readFileSync the contained package.json, the read will fail with an entry not found error.
I think the problem is inside the _resolveSymlink function. If I try to access node_modules/@test/lib, it will correctly find the entry for it in the symlinks record of the manifest. But if I try to access a child path like node_modules/@test/lib/package.json, there will be no direct entry for it (because only the parent directory path is recorded). And _resolveSymlink does not check for symlinks of parent directories.
The fix would probably be to also check for symlink entries of parent directories. But that would also slow down _resolveSymlink, because every parent (and its parent) directory also needs to be checked. I'm not sure what the right solution is here.
Expected Behavior
Files inside symlinked directories should be resolved correctly.
To Reproduce
A minimal example project can be found here: https://github.com/mpotthoff/pkg-workspace-test
The problem can be reproduced on windows and linux. Here the reproduction on linux:
- Clone the repository and enter it
- npm install
- pkg --sea --targets node26-linux-x64 packages/app
- ./app
Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@pkg-workspace-test/lib' imported from /snapshot/pkg-workspace-test/packages/app/index.js
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:301:9)
at packageResolve (node:internal/modules/esm/resolve:784:25)
at moduleResolve (node:internal/modules/esm/resolve:873:18)
at defaultResolve (node:internal/modules/esm/resolve:1006:11)
at #cachedDefaultResolve (node:internal/modules/esm/loader:708:20)
at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:728:38)
at nextStep (node:internal/modules/customization_hooks:189:26)
at resolveBareSpecifier (/tmp/pkg-sea-uW9yA6/sea-main.js:2999:14)
at vfsResolveHook (/tmp/pkg-sea-uW9yA6/sea-main.js:3019:16)
at nextStep (node:internal/modules/customization_hooks:189:26) {
code: 'ERR_MODULE_NOT_FOUND'
}
Without sea:
- pkg --targets node26-linux-x64 packages/app
- ./app
Hello World
Activity
refactor(prelude): bound symlink memo and shorten the resolution walk
fix(prelude): keep exact symlink entries ahead of their symlinked parent
fix(sea): resolve symlinks in realpath, which also unbreaks readlink
test(yao-pkg#295): build the symlink fixture at test time so Windows …
fix: address review round 1 on symlink resolution
fix(sea): report symlinks in lstat, readlink and readdir
fix: route ELOOP through callbacks, share the readlink contract
refactor(prelude): one error shape, one link-target policy, one ELOOP…
fix(sea): files inside symlinks are not resolved correctly (yao-pkg#295)
refactor(prelude): bound symlink memo and shorten the resolution walk
fix(prelude): keep exact symlink entries ahead of their symlinked parent
fix(sea): resolve symlinks in realpath, which also unbreaks readlink
test(yao-pkg#295): build the symlink fixture at test time so Windows …
fix: address review round 1 on symlink resolution
fix(sea): report symlinks in lstat, readlink and readdir
fix: route ELOOP through callbacks, share the readlink contract
refactor(prelude): one error shape, one link-target policy, one ELOOP…