berry icon indicating copy to clipboard operation
berry copied to clipboard

Preserve special protocols (patch:, portal:, link:) when resolving catalog references

Open ho991217 opened this issue 2 months ago • 1 comments

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 uses convertToManifestRange to 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 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.

ho991217 avatar Nov 04 '25 08:11 ho991217

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.

arcanis avatar Nov 07 '25 10:11 arcanis