Skip to content

Commit 1abb336

Browse files
IlyaKhDCopilot
andauthored
fix(install-internal-package): retarget replacement to node_modules/.pnpm (#38)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 22a96de commit 1abb336

2 files changed

Lines changed: 17 additions & 8 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
.idea/
44
.vscode/
5+
.DS_Store
56

67
logs
78
*.log

install-internal-package/replace-package.js

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const assert = require('node:assert');
22
const { execFileSync } = require('node:child_process');
3-
const { rmSync, mkdirSync, lstatSync, unlinkSync } = require('node:fs');
3+
const { rmSync, mkdirSync, lstatSync, realpathSync } = require('node:fs');
44
const { join } = require('node:path');
55
const { createRequire } = require('node:module');
66

@@ -21,16 +21,24 @@ for (const searchDir of cwdRequire.resolve.paths(packageName) || []) {
2121

2222
assert(entryPath, `Package "${packageName}" is not installed. It must be installed before it can be replaced.`);
2323

24-
if (lstatSync(entryPath).isSymbolicLink()) {
25-
unlinkSync(entryPath);
26-
} else {
27-
rmSync(entryPath, { recursive: true, force: true });
28-
}
24+
const targetPath = (() => {
25+
if (!lstatSync(entryPath).isSymbolicLink()) {
26+
return entryPath;
27+
}
28+
29+
// If it's a symlink (pnpm), replace the target content to preserve the resolution chain.
30+
// pnpm places dependencies as siblings of the target, so the symlink must stay intact.
31+
const resolved = realpathSync(entryPath);
32+
assert(resolved.startsWith(process.cwd() + '/'), `Symlink target "${resolved}" resolves outside the project`);
33+
return resolved;
34+
35+
})();
2936

30-
mkdirSync(entryPath, { recursive: true });
37+
rmSync(targetPath, { recursive: true, force: true });
38+
mkdirSync(targetPath, { recursive: true });
3139

3240
const listing = execFileSync('tar', ['-tzf', packageFile]).toString();
3341
const unsafeEntry = listing.split('\n').find(e => e.includes('..') || e.startsWith('/'));
3442
assert(!unsafeEntry, `Archive contains unsafe path: ${unsafeEntry}`);
3543

36-
execFileSync('tar', ['-xzf', packageFile, '-C', entryPath, '--strip-components=1']);
44+
execFileSync('tar', ['-xzf', packageFile, '-C', targetPath, '--strip-components=1']);

0 commit comments

Comments
 (0)