Preserve special protocols (patch:, portal:, link:) when resolving catalog references
closes: #6920
What's the problem this PR addresses?
When using the catalog plugin with TypeScript, installations would fail even with simple version specifications. For example:
catalog:
typescript: 5.9.2
{
"devDependencies": {
"typescript": "catalog:"
}
}
This would fail with:
Error: typescript@patch:typescript@npm%3A5.9.2#... isn't supported by any available resolver
Root cause: When resolving typescript: 5.9.2 from the catalog, Yarn automatically applies compatibility patches internally, resulting in a patch: protocol range in yarn.lock. However, the catalog plugin was incorrectly stripping or mangling this protocol information during resolution, causing the resolver to fail.
This issue affects not just TypeScript, but any package that uses special protocols like patch:, portal:, or link: - whether explicitly specified in the catalog or automatically applied by Yarn.
How did you fix it?
1. Added safe protocol preservation
Introduced SAFE_PROTOCOLS_TO_ALWAYS_KEEP to explicitly preserve protocols that have special semantics:
-
patch:- for applying patches to packages (including auto-applied compat patches) -
portal:- for local workspace references -
link:- for symlinked packages
2. Implemented tryRoundTripRange function
This function safely normalizes ranges while preserving protocol information:
- Only removes the
npm:protocol when it's exactly the default protocol - Never removes safe protocols that carry semantic meaning
- Includes idempotency checks to ensure normalization doesn't change the range's meaning
- Falls back to the original range if any errors occur
3. Fixed hook implementations
-
beforeWorkspacePacking: Now usesconvertToManifestRangeto strip internal params (like__locator) before applying the round-trip normalization, ensuring clean package.json files in published packages -
reduceDependency: Returns the full range with protocol intact, as the resolver needs complete protocol information during dependency resolution
4. Added comprehensive tests
Added test cases for all three safe protocols to ensure they are correctly preserved when resolving catalog references.
Testing
Tested with both explicit and implicit patch protocol usage:
# Explicit patch in catalog
catalog:
typescript: "patch:typescript@npm%3A^5.9.3#optional!builtin<compat/typescript>"
# Simple version that gets auto-patched
catalog:
typescript: 5.9.2
Both cases now work correctly, with the patch protocol being properly preserved during resolution and installation.
Checklist
- [x] I have read the Contributing Guide.
- [x] I have set the packages that need to be released for my changes to be effective.
- [x] I will check that all automated PR checks pass before the PR gets reviewed.
I think the fix might be different and just a matter of reorganizing the catalog order in the hook list, at least for typescript. See #6969.