Skip to content

Commit f8e6119

Browse files
committed
feat: add alias support for yarn v1 and v2
1 parent 38e5d67 commit f8e6119

File tree

12 files changed

+2055
-59
lines changed

12 files changed

+2055
-59
lines changed

lib/aliasesPreprocessors/pkgJson.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { parsePkgJson } from '../dep-graph-builders/util';
2+
3+
export const rewriteAliasesPkgJson = (packageJsonContent: string): string => {
4+
const pkgJsonPreprocessed = parsePkgJson(packageJsonContent);
5+
pkgJsonPreprocessed.dependencies = rewriteAliases(
6+
pkgJsonPreprocessed.dependencies,
7+
);
8+
pkgJsonPreprocessed.devDependencies = rewriteAliases(
9+
pkgJsonPreprocessed.devDependencies,
10+
);
11+
pkgJsonPreprocessed.optionalDependencies = rewriteAliases(
12+
pkgJsonPreprocessed.optionalDependencies,
13+
);
14+
pkgJsonPreprocessed.peerDependencies = rewriteAliases(
15+
pkgJsonPreprocessed.peerDependencies,
16+
);
17+
return JSON.stringify(pkgJsonPreprocessed);
18+
};
19+
20+
export const rewriteAliases = (
21+
dependencies: Record<string, string> | undefined,
22+
): Record<string, string> | undefined => {
23+
if (!dependencies) {
24+
return undefined;
25+
}
26+
const newDependencies: Record<string, string> = {};
27+
for (const key in dependencies) {
28+
const value = dependencies[key];
29+
if (value.startsWith('npm:')) {
30+
newDependencies[value.substring(4, value.lastIndexOf('@'))] =
31+
value.substring(value.lastIndexOf('@') + 1, value.length);
32+
} else {
33+
newDependencies[key] = value;
34+
}
35+
}
36+
return newDependencies;
37+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const rewriteAliasesInLockV1 = (lockfileContent: string): string => {
2+
const regex = /^(\s*)"(.+?@npm:)([^"]+)":/gm;
3+
4+
const lockfilePreprocessed = lockfileContent.replace(regex, '$1"$3":');
5+
6+
return lockfilePreprocessed;
7+
};
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { NormalisedPkgs } from '../dep-graph-builders/types';
2+
import * as cloneDeep from 'lodash.clonedeep';
3+
export const rewriteAliasesInLockV2 = (
4+
lockfileNormalisedPkgs: NormalisedPkgs,
5+
): NormalisedPkgs => {
6+
const lockfileNormalisedPkgsPreprocessed: NormalisedPkgs = cloneDeep(
7+
lockfileNormalisedPkgs,
8+
);
9+
for (const pkg in lockfileNormalisedPkgsPreprocessed) {
10+
const pkgSplit = pkg.substring(0, pkg.lastIndexOf('@'));
11+
const resolutionSplit =
12+
lockfileNormalisedPkgsPreprocessed[pkg].resolution?.split(
13+
/@npm[:%3A]/,
14+
)[0];
15+
16+
if (
17+
!pkg.startsWith('v2@workspace') &&
18+
resolutionSplit &&
19+
pkgSplit != resolutionSplit
20+
) {
21+
const newPkg = lockfileNormalisedPkgsPreprocessed[pkg];
22+
delete lockfileNormalisedPkgsPreprocessed[pkg];
23+
const newKey = pkg.replace(pkgSplit, resolutionSplit);
24+
lockfileNormalisedPkgsPreprocessed[newKey] = newPkg;
25+
}
26+
if (pkg.startsWith('v2@workspace')) {
27+
const newDependencies: Record<string, string> = {};
28+
for (const key in lockfileNormalisedPkgsPreprocessed[pkg].dependencies) {
29+
const value = lockfileNormalisedPkgsPreprocessed[pkg].dependencies[key];
30+
if (value.startsWith('npm:')) {
31+
newDependencies[value.substring(4, value.lastIndexOf('@'))] =
32+
value.substring(value.lastIndexOf('@') + 1, value.length);
33+
} else {
34+
newDependencies[key] = value;
35+
}
36+
}
37+
lockfileNormalisedPkgsPreprocessed[pkg].dependencies = newDependencies;
38+
}
39+
}
40+
41+
return lockfileNormalisedPkgsPreprocessed;
42+
};

lib/dep-graph-builders/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export type NormalisedPkgs = Record<
2424
PkgIdentifier,
2525
{
2626
version: string; // This is resolved version
27+
resolution?: string;
2728
dependencies: Record<string, string>;
2829
optionalDependencies: Record<string, string>;
2930
}
@@ -35,6 +36,7 @@ export type DepGraphBuildOptions = {
3536
strictOutOfSync: boolean;
3637
includePeerDeps?: boolean;
3738
pruneNpmStrictOutOfSync?: boolean;
39+
honorAliases?: boolean;
3840
};
3941

4042
export type LockFileParseOptions = {
@@ -57,6 +59,7 @@ export type YarnLockV2ProjectParseOptions = {
5759
includeOptionalDeps: boolean;
5860
strictOutOfSync: boolean;
5961
pruneWithinTopLevelDeps: boolean;
62+
honorAliases?: boolean;
6063
};
6164

6265
/*
@@ -73,6 +76,7 @@ export type YarnLockV1ProjectParseOptions = {
7376
includePeerDeps: boolean;
7477
strictOutOfSync: boolean;
7578
pruneLevel: PruneLevel;
79+
honorAliases?: boolean;
7680
};
7781

7882
export type Yarn1DepGraphBuildOptions = {

lib/dep-graph-builders/yarn-lock-v1/simple.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import { buildDepGraphYarnLockV1Simple } from '.';
2+
import { rewriteAliasesPkgJson } from '../../aliasesPreprocessors/pkgJson';
3+
import { rewriteAliasesInLockV1 } from '../../aliasesPreprocessors/yarn-lock-v1';
4+
25
import { PackageJsonBase, YarnLockV1ProjectParseOptions } from '../types';
36
import { parsePkgJson } from '../util';
47
import { buildDepGraphYarnLockV1SimpleCyclesPruned } from './build-depgraph-simple-pruned';
@@ -15,11 +18,16 @@ export const parseYarnLockV1Project = async (
1518
includePeerDeps,
1619
pruneLevel,
1720
strictOutOfSync,
21+
honorAliases,
1822
} = options;
1923

20-
const pkgs = extractPkgsFromYarnLockV1(yarnLockContent);
24+
const pkgs = extractPkgsFromYarnLockV1(
25+
honorAliases ? rewriteAliasesInLockV1(yarnLockContent) : yarnLockContent,
26+
);
2127

22-
const pkgJson: PackageJsonBase = parsePkgJson(pkgJsonContent);
28+
const pkgJson: PackageJsonBase = parsePkgJson(
29+
honorAliases ? rewriteAliasesPkgJson(pkgJsonContent) : pkgJsonContent,
30+
);
2331

2432
const depGraph =
2533
pruneLevel === 'cycles'

lib/dep-graph-builders/yarn-lock-v2/simple.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77
} from '../types';
88
import { buildDepGraphYarnLockV2Simple } from './build-depgraph-simple';
99
import { DepGraph } from '@snyk/dep-graph';
10+
import { rewriteAliasesPkgJson } from '../../aliasesPreprocessors/pkgJson';
11+
import { rewriteAliasesInLockV2 } from '../../aliasesPreprocessors/yarn-lock-v2';
1012

1113
export const parseYarnLockV2Project = async (
1214
pkgJsonContent: string,
@@ -19,11 +21,16 @@ export const parseYarnLockV2Project = async (
1921
includeOptionalDeps,
2022
strictOutOfSync,
2123
pruneWithinTopLevelDeps,
24+
honorAliases,
2225
} = options;
2326

24-
const pkgs = extractPkgsFromYarnLockV2(yarnLockContent);
27+
const pkgs = honorAliases
28+
? rewriteAliasesInLockV2(extractPkgsFromYarnLockV2(yarnLockContent))
29+
: extractPkgsFromYarnLockV2(yarnLockContent);
2530

26-
const pkgJson: PackageJsonBase = parsePkgJson(pkgJsonContent);
31+
const pkgJson: PackageJsonBase = parsePkgJson(
32+
honorAliases ? rewriteAliasesPkgJson(pkgJsonContent) : pkgJsonContent,
33+
);
2734

2835
const depgraph = await buildDepGraphYarnLockV2Simple(
2936
pkgs,
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { join } from 'path';
2+
import { readFileSync } from 'fs';
3+
import {
4+
parseNpmLockV2Project,
5+
parseYarnLockV1Project,
6+
parseYarnLockV2Project,
7+
} from '../../../lib/';
8+
9+
describe('Testing aliases', () => {
10+
it('match aliased package - yarn-lock-v1', async () => {
11+
const pkgJsonContent = readFileSync(
12+
join(__dirname, `./fixtures/aliases/yarn-lock-v1/package.json`),
13+
'utf8',
14+
);
15+
const yarnLockContent = readFileSync(
16+
join(__dirname, `./fixtures/aliases/yarn-lock-v1/yarn.lock`),
17+
'utf8',
18+
);
19+
20+
const newDepGraph = await parseYarnLockV1Project(
21+
pkgJsonContent,
22+
yarnLockContent,
23+
{
24+
includeDevDeps: true,
25+
includeOptionalDeps: true,
26+
includePeerDeps: false,
27+
pruneLevel: 'withinTopLevelDeps',
28+
strictOutOfSync: true,
29+
honorAliases: true,
30+
},
31+
);
32+
33+
expect(newDepGraph).toBeDefined;
34+
35+
expect(JSON.stringify(newDepGraph)).toContain('@yao-pkg/pkg');
36+
expect(JSON.stringify(newDepGraph)).not.toContain("'pkg'");
37+
});
38+
39+
it('match aliased package - yarn-lock-v2', async () => {
40+
const pkgJsonContent = readFileSync(
41+
join(__dirname, `./fixtures/aliases/yarn-lock-v2/package.json`),
42+
'utf8',
43+
);
44+
const yarnLockContent = readFileSync(
45+
join(__dirname, `./fixtures/aliases/yarn-lock-v2/yarn.lock`),
46+
'utf8',
47+
);
48+
49+
const newDepGraph = await parseYarnLockV2Project(
50+
pkgJsonContent,
51+
yarnLockContent,
52+
{
53+
includeDevDeps: true,
54+
includeOptionalDeps: true,
55+
strictOutOfSync: true,
56+
pruneWithinTopLevelDeps: true,
57+
honorAliases: true,
58+
},
59+
);
60+
console.log(JSON.stringify(newDepGraph));
61+
expect(newDepGraph).toBeDefined;
62+
63+
expect(JSON.stringify(newDepGraph)).toContain('@yao-pkg/pkg');
64+
expect(JSON.stringify(newDepGraph)).not.toContain("'pkg@");
65+
});
66+
67+
// it('match aliased package - npm-lock-v2', async () => {
68+
// const pkgJsonContent = readFileSync(
69+
// join(__dirname, `./fixtures/aliases/package.json`),
70+
// 'utf8',
71+
// );
72+
// const pkgLockContent = readFileSync(
73+
// join(__dirname, `./fixtures/aliases/package-lock.json`),
74+
// 'utf8',
75+
// );
76+
77+
// const newDepGraph = await parseNpmLockV2Project(
78+
// pkgJsonContent,
79+
// pkgLockContent,
80+
// {
81+
// includeDevDeps: false,
82+
// includeOptionalDeps: true,
83+
// pruneCycles: true,
84+
// strictOutOfSync: true,
85+
// },
86+
// );
87+
88+
// expect(newDepGraph).toBeDefined;
89+
90+
// expect(JSON.stringify(newDepGraph)).toContain('@yao-pkg/pkg');
91+
// expect(JSON.stringify(newDepGraph)).not.toContain("'pkg'");
92+
// });
93+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"name": "yarnalias",
3+
"version": "2.0.0",
4+
"main": "index.js",
5+
"license": "MIT",
6+
"dependencies": {
7+
"lodash": "npm:lodash@^4.17.15",
8+
"pkg": "npm:@yao-pkg/pkg@^6.4.1"
9+
}
10+
}

0 commit comments

Comments
 (0)