Skip to content

Commit 208dcf5

Browse files
authored
Merge pull request #290 from snyk/fix/yarn-handle-resolutions-aliases
fix: handle npm aliases in resolutions field (yarn)
2 parents 3cbdaa5 + ea206a9 commit 208dcf5

File tree

5 files changed

+117
-10
lines changed

5 files changed

+117
-10
lines changed

lib/aliasesPreprocessors/pkgJson.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@ export const rewriteAliasesPkgJson = (packageJsonContent: string): string => {
6666
pkgJsonPreprocessed.overrides,
6767
);
6868
}
69+
// Process resolutions field to extract aliases (yarn)
70+
if (pkgJsonPreprocessed.resolutions) {
71+
rewriteAliasesInOverrides(
72+
pkgJsonPreprocessed,
73+
pkgJsonPreprocessed.resolutions,
74+
);
75+
}
6976
return JSON.stringify(pkgJsonPreprocessed);
7077
};
7178

lib/dep-graph-builders/util.ts

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { InvalidUserInputError } from '../errors';
44
import { NormalisedPkgs } from './types';
55
import { OutOfSyncError } from '../errors';
66
import { LockfileType } from '../parsers';
7+
import { parseNpmAlias } from '../aliasesPreprocessors/pkgJson';
78

89
export type Dependencies = Record<
910
string,
@@ -87,10 +88,13 @@ export const getTopLevelDeps = (
8788

8889
if (pkgJson.aliases) {
8990
for (const alias of Object.keys(pkgJson.aliases)) {
90-
deps[alias] = {
91-
...deps[alias],
92-
...{ alias: { ...pkgJson.aliases[alias] } },
93-
};
91+
// Only add alias metadata to dependencies that are actually in deps
92+
if (deps[alias]) {
93+
deps[alias] = {
94+
...deps[alias],
95+
...{ alias: { ...pkgJson.aliases[alias] } },
96+
};
97+
}
9498
}
9599
}
96100

@@ -170,29 +174,60 @@ export const getChildNode = (
170174
const childNodeKey = `${name}@${depInfo.version}`;
171175
let childNode: PkgNode;
172176

177+
// Check if this lockfile entry is for an aliased package
178+
// by looking for a corresponding npm: entry in the lockfile
179+
let aliasInfo = depInfo.alias;
180+
if (!aliasInfo && pkgs[childNodeKey]) {
181+
// Look for any key in pkgs that matches the pattern: name@npm:*
182+
// and has the same version as our current entry
183+
for (const key in pkgs) {
184+
if (key.startsWith(`${name}@npm:`)) {
185+
const pkgEntry = pkgs[key];
186+
if (pkgEntry.version === pkgs[childNodeKey].version) {
187+
// Extract the npm: portion and parse it using the shared helper
188+
const npmPortion = key.substring(name.length + 1); // Remove "name@" prefix
189+
const parsed = parseNpmAlias(npmPortion);
190+
if (parsed) {
191+
const targetPkgName = parsed.packageName;
192+
// Only add alias info if the alias name is different from the target name
193+
if (targetPkgName !== name) {
194+
aliasInfo = {
195+
aliasName: name,
196+
aliasTargetDepName: targetPkgName,
197+
semver: parsed.version,
198+
version: parsed.version,
199+
};
200+
}
201+
break;
202+
}
203+
}
204+
}
205+
}
206+
}
207+
173208
if (!pkgs[childNodeKey]) {
174209
// Handle optional dependencies that don't have separate package entries
175210
if (depInfo.isOptional) {
176211
childNode = {
177212
id: childNodeKey,
178-
name: depInfo.alias?.aliasTargetDepName ?? name,
213+
name: aliasInfo?.aliasTargetDepName ?? name,
179214
version: depInfo.version,
180215
dependencies: {},
181216
isDev: depInfo.isDev,
182217
missingLockFileEntry: true,
183-
alias: depInfo.alias,
218+
alias: aliasInfo,
184219
};
185220
} else if (strictOutOfSync && !/^file:/.test(depInfo.version)) {
186221
throw new OutOfSyncError(childNodeKey, LockfileType.yarn);
187222
} else {
188223
childNode = {
189224
id: childNodeKey,
190-
name: depInfo.alias?.aliasTargetDepName ?? name,
225+
name: aliasInfo?.aliasTargetDepName ?? name,
191226
version: depInfo.version,
192227
dependencies: {},
193228
isDev: depInfo.isDev,
194229
missingLockFileEntry: true,
195-
alias: depInfo.alias,
230+
alias: aliasInfo,
196231
};
197232
}
198233
} else {
@@ -208,11 +243,11 @@ export const getChildNode = (
208243
: {};
209244
childNode = {
210245
id: `${name}@${depData.version}`,
211-
name: depInfo.alias?.aliasTargetDepName ?? name,
246+
name: aliasInfo?.aliasTargetDepName ?? name,
212247
version: depData.version,
213248
dependencies: { ...dependencies, ...optionalDependencies },
214249
isDev: depInfo.isDev,
215-
alias: depInfo.alias,
250+
alias: aliasInfo,
216251
};
217252
}
218253

test/jest/dep-graph-builders/aliases.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,46 @@ describe('Testing aliases for npm', () => {
563563
// Verify the alias metadata is present
564564
expect(depGraphJson).toContain('"alias":"elliptic=>[email protected]"');
565565
});
566+
567+
it('match aliased package in resolutions field - yarn-lock-v1', async () => {
568+
const pkgJsonContent = readFileSync(
569+
join(
570+
__dirname,
571+
`./fixtures/aliases/yarn-lock-v1-with-resolutions/package.json`,
572+
),
573+
'utf8',
574+
);
575+
const yarnLockContent = readFileSync(
576+
join(
577+
__dirname,
578+
`./fixtures/aliases/yarn-lock-v1-with-resolutions/yarn.lock`,
579+
),
580+
'utf8',
581+
);
582+
583+
const newDepGraph = await parseYarnLockV1Project(
584+
pkgJsonContent,
585+
yarnLockContent,
586+
{
587+
includeDevDeps: false,
588+
includeOptionalDeps: false,
589+
includePeerDeps: false,
590+
pruneLevel: 'cycles',
591+
strictOutOfSync: false,
592+
honorAliases: true,
593+
},
594+
);
595+
596+
expect(newDepGraph).toBeDefined();
597+
expect(() => JSON.parse(JSON.stringify(newDepGraph))).not.toThrow();
598+
599+
// ms should be aliased to dry-uninstall via resolutions
600+
const depGraphJson = JSON.stringify(newDepGraph);
601+
expect(depGraphJson).toContain('dry-uninstall');
602+
603+
// Verify the alias metadata is present (version will be from lockfile)
604+
expect(depGraphJson).toContain('"alias":"ms=>dry-uninstall');
605+
});
566606
});
567607

568608
describe.each(['pnpm-lock-v5', 'pnpm-lock-v6', 'pnpm-lock-v9'])(
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"name": "yarn-with-resolutions",
3+
"version": "1.0.0",
4+
"dependencies": {
5+
"debug": "4.3.1"
6+
},
7+
"resolutions": {
8+
"ms": "npm:[email protected]"
9+
}
10+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2+
# yarn lockfile v1
3+
4+
5+
6+
version "4.3.1"
7+
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
8+
integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
9+
dependencies:
10+
ms "2.1.2"
11+
12+
13+
version "0.3.0"
14+
resolved "https://registry.yarnpkg.com/dry-uninstall/-/dry-uninstall-0.3.0.tgz#29847a27ed3b3bb94e6212547a677e37f4427011"
15+
integrity sha512-b8h94RVpETWkVV59x62NsY++79bM7Si6Dxq7a4iVxRcJU3ZJJ4vaiC7wUZwM8WDK0ySRL+i+T/1SMAzbJLejYA==

0 commit comments

Comments
 (0)