patch-package uses patches with filenames like @scope+pkg+1.2.3.patch and content containing node_modules/ prefixes.
pnpm uses patches with filenames like @scope__pkg@1.2.3.patch and content without node_modules/ prefixes.
Features
Core Flow
- Scans for patch files (default ./patches directory) when processing config files
- Normalizes the path, then performs conversion: content rewrite + filename rewrite
- Aggregates results into patchedDependencies entries, writes them back to config
- Runs pnpm install automatically via workflow step to apply the patches
Content Conversion
- Detection: converts only if patch content includes node_modules diff headers (diff --git a/node_modules/..., -- a/node_modules/..., +++ b/node_modules/...)
- Prefix calculation: derives a removal pattern from the patch filename by taking all +separated parts except the last (version), joining with /, and prefixing with node_modules
- Rewrite: globally removes that prefix from the patch content to meet pnpm's expected format
Filename Conversion
- Input naming (patch-package): <name parts joined by '+'>+<version>.patch
- Output naming (pnpm): <name parts joined by '__'>@<version>.patch
- Example: @scope+pkg+1.2.3.patch → @scope__pkg@1.2.3.patch
Batch vs Single-File Processing
- Directory mode: enumerates all .patch files, then converts each (content + filename)
- Single-file mode: validates existence, .patch extension, presence of + in name, and ensures the target converted file doesn't already exist; then converts
- If a patch doesn't need conversion (no node_modules headers), it's skipped
Configuration Updates
Patched Dependencies
For each converted file:
- Key: filename without .patch, replace __ back to /, e.g. <name>/<sub>/...@<version>
- Value: <patchesDir>/<convertedFileName> where patchesDir is the directory name containing the patches (e.g., patches)
- Merges these into existing patchedDependencies, overwriting duplicate keys
Config Files
- Prefers pnpm-workspace.yaml: reads top-level patchedDependencies, merges new entries, and writes back
- Fallback to package.json: updates pnpm.patchedDependencies in place if workspace file is absent
- Merge strategy: additive with overwrite, preserving unrelated fields
Examples
Before (patch-package)
File: patches/@react-spring+core+9.4.5.patch
diff
After (pnpm)
File: patches/@react-spring__core@9.4.5.patch
diff
Configuration in pnpm-workspace.yaml:
yaml
Error Handling
- Directory mode: errors on non-existent path or non-directory
- Single-file mode: blocks if the converted target file already exists (to avoid overwrite)
- Requires filenames containing + and ending with .patch; otherwise errors
- Normalizes Windows path separators to / where relevant (regex and key generation)