Skip to content

Commit

Permalink
feat: add support for Yarn patch protocol (#887)
Browse files Browse the repository at this point in the history
Transparently handles the patch protocol version syntax by falling back
to the base version string.

#223
https://yarnpkg.com/cli/patch

Fixes #223

Signed-off-by: Peter Somogyvari <peter.metz@unarin.com>
  • Loading branch information
petermetz authored Jul 5, 2024
1 parent 10d308e commit 05579ab
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 0 deletions.
20 changes: 20 additions & 0 deletions packages/core/src/package-graph/__tests__/package-graph.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,26 @@ describe('PackageGraph', () => {
expect(node).toHaveProperty('localDependents', expect.any(Map));
});

it('handles yarn patch protocol versions transparently and without hiccups', () => {
const pkg = new Package(
{
name: 'my-pkg',
version: '1.0.0',
dependencies: {
'@ionic-native/splash-screen':
'patch:@ionic-native/splash-screen@npm%3A5.36.0#~/.yarn/patches/@ionic-native-splash-screen-npm-5.36.0-531cbbe0f8.patch',
},
} as unknown as RawManifest,
'/path/to/my-pkg'
);
const node = new PackageGraph([pkg]).get('my-pkg');
expect(node?.externalDependencies.get('@ionic-native/splash-screen').rawSpec).toBe('5.36.0');
expect(node?.externalDependencies.get('@ionic-native/splash-screen').fetchSpec).toBe('5.36.0');
expect(node).toHaveProperty('externalDependencies', expect.any(Map));
expect(node).toHaveProperty('localDependencies', expect.any(Map));
expect(node).toHaveProperty('localDependents', expect.any(Map));
});

it('computes prereleaseId from prerelease version', () => {
const node = new PackageGraph([new Package({ name: 'my-pkg', version: '1.2.3-rc.4' } as unknown as RawManifest, '/path/to/my-pkg')]).get(
'my-pkg'
Expand Down
19 changes: 19 additions & 0 deletions packages/core/src/package-graph/package-graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ import { Package } from '../package.js';
import { ValidationError } from '../validation-error.js';
import { NpaResolveResult } from '../models/index.js';

/**
* A regular expression used to capture and substitute package versions (referred to as "spec" in the lerna-lite code).
*
* An example of what a spec looks like when the yarn patch protocol has been applied to it is this:
* `patch:@ionic-native/splash-screen@npm%3A5.36.0#~/.yarn/patches/@ionic-native-splash-screen-npm-5.36.0-531cbbe0f8.patch`
*
* We extract the original version string of the package from it and continue package graph traversal with that so that
* we can finish building the graph in projects where the yarn patch protocol is under use for patching third-party code.
*
* @see https://github.com/lerna-lite/lerna-lite/issues/223
* @see https://yarnpkg.com/cli/patch
*/
export const YARN_PATCH_PROTOCOL_REG_EXP = new RegExp(`patch:(.*)@npm%3A(.*)#~\\/.yarn\\/patches\\/(.*).patch`);

/**
* A graph of packages in the current project.
*
Expand Down Expand Up @@ -68,6 +82,11 @@ export class PackageGraph extends Map<string, PackageGraphNode> {
// @see https://github.com/yarnpkg/yarn/issues/4212
let spec = graphDependencies[depName].replace(/^link:/, 'file:');

// Yarn patch protocol handling: We swap out the patch URL with the regular semantic version.
if (spec.startsWith('patch:')) {
spec = spec.replace(YARN_PATCH_PROTOCOL_REG_EXP, '$2');
}

// npa doesn't support the explicit workspace: protocol, supported by
// pnpm and Yarn.
const isWorkspaceSpec = /^workspace:/.test(spec);
Expand Down

0 comments on commit 05579ab

Please sign in to comment.