Skip to content

Commit

Permalink
feat: yarn lock v2 support plus improvements
Browse files Browse the repository at this point in the history
This adds support for yarn.lock v2, that yaml-like format. This means
initial support for yarn2 and yarn3 as at time of writing they use this
version.

Additionally support for depgraph for yarn lock v1 (Yarn1) has been
added which should improve performance for these older projects.
  • Loading branch information
JamesPatrickGill committed Dec 1, 2022
1 parent bd96e74 commit 790aa24
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 17 deletions.
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -118,7 +118,7 @@
"snyk-gradle-plugin": "3.24.6",
"snyk-module": "3.1.0",
"snyk-mvn-plugin": "2.31.3",
"snyk-nodejs-lockfile-parser": "1.44.0",
"snyk-nodejs-lockfile-parser": "1.45.1",
"snyk-nuget-plugin": "1.23.5",
"snyk-php-plugin": "1.9.2",
"snyk-policy": "^1.25.0",
Expand Down
29 changes: 25 additions & 4 deletions src/lib/plugins/nodejs-plugin/index.ts
Expand Up @@ -4,6 +4,8 @@ import * as types from '../types';
import * as analytics from '../../analytics';
import { MissingTargetFileError } from '../../errors/missing-targetfile-error';
import { MultiProjectResult } from '@snyk/cli-interface/legacy/plugin';
import { DepGraph } from '@snyk/dep-graph';
import { PkgTree } from 'snyk-nodejs-lockfile-parser';

export async function inspect(
root: string,
Expand All @@ -18,19 +20,38 @@ export async function inspect(
targetFile.endsWith('yarn.lock');

const getLockFileDeps = isLockFileBased && !options.traverseNodeModules;
const depTree: any = getLockFileDeps
const depRes: PkgTree | DepGraph = getLockFileDeps
? await lockParser.parse(root, targetFile, options)
: await modulesParser.parse(root, targetFile, options);

if (depTree?.meta?.lockfileVersion) {
analytics.add('lockfileVersion', depTree.meta.lockfileVersion);
let scannedProjects: any[] = [];
if (isResDepGraph(depRes)) {
if (depRes.pkgManager.version) {
analytics.add(
'lockfileVersion',
(depRes as DepGraph)?.pkgManager.version,
);
}
scannedProjects = [{ depGraph: depRes }];
} else {
if (depRes.meta?.lockfileVersion) {
analytics.add(
'lockfileVersion',
(depRes as PkgTree)?.meta?.lockfileVersion,
);
}
scannedProjects = [{ depTree: depRes }];
}

return {
plugin: {
name: 'snyk-nodejs-lockfile-parser',
runtime: process.version,
},
scannedProjects: [{ depTree }],
scannedProjects,
};
}

function isResDepGraph(depRes: PkgTree | DepGraph): depRes is DepGraph {
return 'rootPkg' in depRes;
}
74 changes: 71 additions & 3 deletions src/lib/plugins/nodejs-plugin/npm-lock-parser.ts
Expand Up @@ -5,14 +5,20 @@ import { spinner } from '../../spinner';
import * as analytics from '../../analytics';
import * as fs from 'fs';
import * as lockFileParser from 'snyk-nodejs-lockfile-parser';
import { PkgTree } from 'snyk-nodejs-lockfile-parser';
import {
NodeLockfileVersion,
PkgTree,
InvalidUserInputError,
ProjectParseOptions,
} from 'snyk-nodejs-lockfile-parser';
import { Options } from '../types';
import { DepGraph } from '@snyk/dep-graph';

export async function parse(
root: string,
targetFile: string,
options: Options,
): Promise<PkgTree> {
): Promise<PkgTree | DepGraph> {
const lockFileFullPath = path.resolve(root, targetFile);
if (!fs.existsSync(lockFileFullPath)) {
throw new Error(
Expand Down Expand Up @@ -47,9 +53,29 @@ export async function parse(
});
const resolveModuleSpinnerLabel = `Analyzing npm dependencies for ${lockFileFullPath}`;
debug(resolveModuleSpinnerLabel);

const strictOutOfSync = options.strictOutOfSync !== false;
const lockfileVersion = lockFileParser.getLockfileVersionFromFile(targetFile);
if (
lockfileVersion === NodeLockfileVersion.YarnLockV1 ||
lockfileVersion === NodeLockfileVersion.YarnLockV2
) {
return buildDepGraph(
root,
manifestFileFullPath,
lockFileFullPath,
lockfileVersion,
{
includeDevDeps: options.dev || false,
includeOptionalDeps: true,
strictOutOfSync,
pruneCycles: true,
},
);
}

try {
await spinner(resolveModuleSpinnerLabel);
const strictOutOfSync = options.strictOutOfSync !== false;
return lockFileParser.buildDepTreeFromFiles(
root,
manifestFileFullPath,
Expand All @@ -61,3 +87,45 @@ export async function parse(
await spinner.clear<void>(resolveModuleSpinnerLabel)();
}
}

async function buildDepGraph(
root: string,
manifestFilePath: string,
lockfilePath: string,
lockfileVersion: NodeLockfileVersion,
options: ProjectParseOptions,
): Promise<DepGraph> {
const manifestFileFullPath = path.resolve(root, manifestFilePath);
const lockFileFullPath = path.resolve(root, lockfilePath);

if (!fs.existsSync(manifestFileFullPath)) {
throw new InvalidUserInputError(
'Target file package.json not found at ' +
`location: ${manifestFileFullPath}`,
);
}
if (!fs.existsSync(lockFileFullPath)) {
throw new InvalidUserInputError(
'Lockfile not found at location: ' + lockFileFullPath,
);
}

const manifestFileContents = fs.readFileSync(manifestFileFullPath, 'utf-8');
const lockFileContents = fs.readFileSync(lockFileFullPath, 'utf-8');

switch (lockfileVersion) {
case NodeLockfileVersion.YarnLockV1:
return await lockFileParser.parseYarnLockV1Project(
manifestFileContents,
lockFileContents,
options,
);
case NodeLockfileVersion.YarnLockV2:
return lockFileParser.parseYarnLockV2Project(
manifestFileContents,
lockFileContents,
options,
);
}
throw new Error('Failed to build dep graph from current project');
}
4 changes: 2 additions & 2 deletions test/jest/acceptance/snyk-test/all-projects.spec.ts
Expand Up @@ -56,7 +56,7 @@ describe('snyk test --all-projects (mocked server only)', () => {
'✗ 1/3 potential projects failed to get dependencies',
);
expect(stderr).toMatch(
`Dependency snyk was not found in yarn.lock. Your package.json and yarn.lock are probably out of sync. Please run "yarn install" and try again.`,
`Dependency snyk@1.320.0 was not found in yarn.lock. Your package.json and yarn.lock are probably out of sync. Please run "yarn install" and try again.`,
);
});

Expand All @@ -81,7 +81,7 @@ describe('snyk test --all-projects (mocked server only)', () => {
'✗ 1/3 potential projects failed to get dependencies',
);
expect(stderr).toMatch(
`Dependency snyk was not found in yarn.lock. Your package.json and yarn.lock are probably out of sync. Please run "yarn install" and try again.`,
`Dependency snyk@1.320.0 was not found in yarn.lock. Your package.json and yarn.lock are probably out of sync. Please run "yarn install" and try again.`,
);
});

Expand Down

0 comments on commit 790aa24

Please sign in to comment.