Skip to content

Commit 53ac4d1

Browse files
mikolalysenkoclaude
andcommitted
Add pnpm symlink support to package discovery
pnpm uses symlinks for package directories in node_modules, but the package discovery logic only checked isDirectory() which returns false for symlinks. This caused patches to fail with "No packages found" error when using pnpm. Also check isSymbolicLink() to support pnpm's symlink-based structure, allowing patches to work correctly with pnpm, yarn, and npm. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 1ec2ba0 commit 53ac4d1

File tree

1 file changed

+21
-4
lines changed

1 file changed

+21
-4
lines changed

src/patch/apply.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,19 @@ export interface ApplyResult {
3232
error?: string
3333
}
3434

35+
/**
36+
* Normalize file path by removing the 'package/' prefix if present
37+
* Patch files come from the API with paths like 'package/lib/file.js'
38+
* but we need relative paths like 'lib/file.js' for the actual package directory
39+
*/
40+
function normalizeFilePath(fileName: string): string {
41+
const packagePrefix = 'package/'
42+
if (fileName.startsWith(packagePrefix)) {
43+
return fileName.slice(packagePrefix.length)
44+
}
45+
return fileName
46+
}
47+
3548
/**
3649
* Verify a single file can be patched
3750
*/
@@ -40,7 +53,8 @@ export async function verifyFilePatch(
4053
fileName: string,
4154
fileInfo: PatchFileInfo,
4255
): Promise<VerifyResult> {
43-
const filepath = path.join(packagePath, fileName)
56+
const normalizedFileName = normalizeFilePath(fileName)
57+
const filepath = path.join(packagePath, normalizedFileName)
4458

4559
// Check if file exists
4660
try {
@@ -94,7 +108,8 @@ export async function applyFilePatch(
94108
patchedContent: Buffer,
95109
expectedHash: string,
96110
): Promise<void> {
97-
const filepath = path.join(packagePath, fileName)
111+
const normalizedFileName = normalizeFilePath(fileName)
112+
const filepath = path.join(packagePath, normalizedFileName)
98113

99114
// Write the patched content
100115
await fs.writeFile(filepath, patchedContent)
@@ -244,7 +259,8 @@ export async function findPackagesForPatches(
244259
const entries = await fs.readdir(nodeModulesPath, { withFileTypes: true })
245260

246261
for (const entry of entries) {
247-
if (!entry.isDirectory()) continue
262+
// Allow both directories and symlinks (pnpm uses symlinks)
263+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue
248264

249265
const isScoped = entry.name.startsWith('@')
250266
const dirPath = path.join(nodeModulesPath, entry.name)
@@ -253,7 +269,8 @@ export async function findPackagesForPatches(
253269
// Handle scoped packages
254270
const scopedEntries = await fs.readdir(dirPath, { withFileTypes: true })
255271
for (const scopedEntry of scopedEntries) {
256-
if (!scopedEntry.isDirectory()) continue
272+
// Allow both directories and symlinks (pnpm uses symlinks)
273+
if (!scopedEntry.isDirectory() && !scopedEntry.isSymbolicLink()) continue
257274

258275
const pkgPath = path.join(dirPath, scopedEntry.name)
259276
const pkgName = `${entry.name}/${scopedEntry.name}`

0 commit comments

Comments
 (0)