Skip to content

Commit

Permalink
Merge pull request #97 from snyk/feat/added-yarn2-back
Browse files Browse the repository at this point in the history
feat: added back yarn2 support
  • Loading branch information
JamesPatrickGill committed Apr 1, 2021
2 parents 3e4fe67 + a4557f0 commit 0bd6a0a
Show file tree
Hide file tree
Showing 17 changed files with 645 additions and 605 deletions.
26 changes: 3 additions & 23 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@ import {
} from './parsers';
import { PackageLockParser } from './parsers/package-lock-parser';
import { YarnLockParser } from './parsers/yarn-lock-parser';
// import { Yarn2LockParser } from './parsers/yarn2-lock-parse';
// import getRuntimeVersion from './get-node-runtime-version';
import { Yarn2LockParser } from './parsers/yarn2-lock-parser';
import {
UnsupportedRuntimeError,
InvalidUserInputError,
OutOfSyncError,
UnsupportedError,
} from './errors';

export {
Expand Down Expand Up @@ -57,26 +55,8 @@ async function buildDepTree(
lockfileParser = new YarnLockParser();
break;
case LockfileType.yarn2:
throw new UnsupportedError(
'Yarn2 support has been temporarily removed to support Node.js versions 8.x.x',
);
/**
* Removing yarn 2 support as this breaks support for yarn with Node.js 8
* See: https://github.com/snyk/snyk/issues/1270
*
* Uncomment following code once Snyk stops Node.js 8 support
* // parsing yarn.lock is supported for Node.js v10 and higher
* if (getRuntimeVersion() >= 10) {
* lockfileParser = new Yarn2LockParser();
* } else {
* throw new UnsupportedRuntimeError(
* 'Parsing `yarn.lock` is not ' +
* 'supported on Node.js version less than 10. Please upgrade your ' +
* 'Node.js environment or use `package-lock.json`',
* );
* }
* break;
*/
lockfileParser = new Yarn2LockParser();
break;
default:
throw new InvalidUserInputError(
'Unsupported lockfile type ' +
Expand Down
4 changes: 2 additions & 2 deletions lib/parsers/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { PackageLock } from './package-lock-parser';
import { YarnLock } from './yarn-lock-parser';
import { InvalidUserInputError } from '../errors';
// import { Yarn2Lock } from './yarn2-lock-parse';
import { Yarn2Lock } from './yarn2-lock-parser';

export interface Dep {
name: string;
Expand Down Expand Up @@ -82,7 +82,7 @@ export interface LockfileParser {
) => Promise<PkgTree>;
}

export type Lockfile = PackageLock | YarnLock; // | Yarn2Lock;
export type Lockfile = PackageLock | YarnLock | Yarn2Lock;

export function parseManifestFile(manifestFileContents: string): ManifestFile {
try {
Expand Down
164 changes: 82 additions & 82 deletions lib/parsers/yarn-utils.ts
Original file line number Diff line number Diff line change
@@ -1,87 +1,87 @@
// import { structUtils } from '@yarnpkg/core';
// import * as _flatMap from 'lodash.flatmap';
import { structUtils } from '@yarnpkg/core';
import * as _flatMap from 'lodash.flatmap';

// const BUILTIN_PLACEHOLDER = 'builtin';
// const MULTIPLE_KEYS_REGEXP = / *, */g;
const BUILTIN_PLACEHOLDER = 'builtin';
const MULTIPLE_KEYS_REGEXP = / *, */g;

// export type ParseDescriptor = typeof structUtils.parseDescriptor;
// export type ParseRange = typeof structUtils.parseRange;
export type ParseDescriptor = typeof structUtils.parseDescriptor;
export type ParseRange = typeof structUtils.parseRange;

// const keyNormalizer = (
// parseDescriptor: ParseDescriptor,
// parseRange: ParseRange,
// ) => (rawDescriptor: string): string[] => {
// // See https://yarnpkg.com/features/protocols
// const descriptors: string[] = [rawDescriptor];
// const descriptor = parseDescriptor(rawDescriptor);
// const name = `${descriptor.scope ? '@' + descriptor.scope + '/' : ''}${
// descriptor.name
// }`;
// const range = parseRange(descriptor.range);
// const protocol = range.protocol;
// switch (protocol) {
// case 'npm:':
// case 'file:':
// descriptors.push(`${name}@${range.selector}`);
// descriptors.push(`${name}@${protocol}${range.selector}`);
// break;
// case 'git:':
// case 'git+ssh:':
// case 'git+http:':
// case 'git+https:':
// case 'github:':
// if (range.source) {
// descriptors.push(
// `${name}@${protocol}${range.source}${
// range.selector ? '#' + range.selector : ''
// }`,
// );
// } else {
// descriptors.push(`${name}@${protocol}${range.selector}`);
// }
// break;
// case 'patch:':
// if (range.source && range.selector.indexOf(BUILTIN_PLACEHOLDER) === 0) {
// descriptors.push(range.source);
// } else {
// descriptors.push(
// `${name}@${protocol}${range.source}${
// range.selector ? '#' + range.selector : ''
// }`,
// );
// }
// break;
// case null:
// case undefined:
// if (range.source) {
// descriptors.push(`${name}@${range.source}#${range.selector}`);
// } else {
// descriptors.push(`${name}@${range.selector}`);
// }
// break;
// case 'http:':
// case 'https:':
// case 'link:':
// case 'portal:':
// case 'exec:':
// case 'workspace:':
// case 'virtual:':
// default:
// // For user defined plugins
// descriptors.push(`${name}@${protocol}${range.selector}`);
// break;
// }
// return descriptors;
// };
const keyNormalizer = (
parseDescriptor: ParseDescriptor,
parseRange: ParseRange,
) => (rawDescriptor: string): string[] => {
// See https://yarnpkg.com/features/protocols
const descriptors: string[] = [rawDescriptor];
const descriptor = parseDescriptor(rawDescriptor);
const name = `${descriptor.scope ? '@' + descriptor.scope + '/' : ''}${
descriptor.name
}`;
const range = parseRange(descriptor.range);
const protocol = range.protocol;
switch (protocol) {
case 'npm:':
case 'file:':
descriptors.push(`${name}@${range.selector}`);
descriptors.push(`${name}@${protocol}${range.selector}`);
break;
case 'git:':
case 'git+ssh:':
case 'git+http:':
case 'git+https:':
case 'github:':
if (range.source) {
descriptors.push(
`${name}@${protocol}${range.source}${
range.selector ? '#' + range.selector : ''
}`,
);
} else {
descriptors.push(`${name}@${protocol}${range.selector}`);
}
break;
case 'patch:':
if (range.source && range.selector.indexOf(BUILTIN_PLACEHOLDER) === 0) {
descriptors.push(range.source);
} else {
descriptors.push(
`${name}@${protocol}${range.source}${
range.selector ? '#' + range.selector : ''
}`,
);
}
break;
case null:
case undefined:
if (range.source) {
descriptors.push(`${name}@${range.source}#${range.selector}`);
} else {
descriptors.push(`${name}@${range.selector}`);
}
break;
case 'http:':
case 'https:':
case 'link:':
case 'portal:':
case 'exec:':
case 'workspace:':
case 'virtual:':
default:
// For user defined plugins
descriptors.push(`${name}@${protocol}${range.selector}`);
break;
}
return descriptors;
};

// export type YarnLockFileKeyNormalizer = (fullDescriptor: string) => Set<string>;
export type YarnLockFileKeyNormalizer = (fullDescriptor: string) => Set<string>;

// export const yarnLockFileKeyNormalizer = (
// parseDescriptor: ParseDescriptor,
// parseRange: ParseRange,
// ): YarnLockFileKeyNormalizer => (fullDescriptor: string) => {
// const allKeys = fullDescriptor
// .split(MULTIPLE_KEYS_REGEXP)
// .map(keyNormalizer(parseDescriptor, parseRange));
// return new Set<string>(_flatMap(allKeys));
// };
export const yarnLockFileKeyNormalizer = (
parseDescriptor: ParseDescriptor,
parseRange: ParseRange,
): YarnLockFileKeyNormalizer => (fullDescriptor: string) => {
const allKeys = fullDescriptor
.split(MULTIPLE_KEYS_REGEXP)
.map(keyNormalizer(parseDescriptor, parseRange));
return new Set<string>(_flatMap(allKeys));
};
56 changes: 0 additions & 56 deletions lib/parsers/yarn2-lock-parse.ts

This file was deleted.

106 changes: 106 additions & 0 deletions lib/parsers/yarn2-lock-parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import * as yaml from 'yaml';
import * as yarnCore from '@yarnpkg/core';

import { LockParserBase, DepMap } from './lock-parser-base';
import { Dep, Lockfile, LockfileType, ManifestFile, PkgTree, Scope } from '.';
import { config } from '../config';
import { YarnLockDeps } from './yarn-lock-parser';
import { InvalidUserInputError } from '../errors';
import { yarnLockFileKeyNormalizer } from './yarn-utils';

export interface Yarn2Lock {
type: string;
object: YarnLockDeps;
dependencies?: YarnLockDeps;
lockfileType: LockfileType.yarn2;
}

export class Yarn2LockParser extends LockParserBase {
constructor() {
super(LockfileType.yarn2, config.YARN_TREE_SIZE_LIMIT);
}

public parseLockFile(lockFileContents: string): Yarn2Lock {
try {
const rawYarnLock: any = yaml.parse(lockFileContents);
delete rawYarnLock.__metadata;
const dependencies: YarnLockDeps = {};

const structUtils = yarnCore.structUtils;
const parseDescriptor = structUtils.parseDescriptor;
const parseRange = structUtils.parseRange;

const keyNormalizer = yarnLockFileKeyNormalizer(
parseDescriptor,
parseRange,
);

Object.entries(rawYarnLock).forEach(
([fullDescriptor, versionData]: [string, any]) => {
keyNormalizer(fullDescriptor).forEach((descriptor) => {
dependencies[descriptor] = versionData;
});
},
);
return {
dependencies,
lockfileType: LockfileType.yarn2,
object: dependencies,
type: LockfileType.yarn2,
};
} catch (e) {
throw new InvalidUserInputError(
`yarn.lock parsing failed with an error: ${e.message}`,
);
}
}

public async getDependencyTree(
manifestFile: ManifestFile,
lockfile: Lockfile,
includeDev = false,
strict = true,
): Promise<PkgTree> {
const depTree = await super.getDependencyTree(
manifestFile,
lockfile,
includeDev,
strict,
);

if (!depTree.meta) depTree.meta = {};
depTree.meta.packageManagerVersion = '2';

return depTree;
}

protected getDepMap(lockfile: Lockfile): DepMap {
const yarnLockfile = lockfile as Yarn2Lock;
const depMap: DepMap = {};

for (const [depName, dep] of Object.entries(yarnLockfile.object)) {
const subDependencies = Object.entries({
...(dep.dependencies || {}),
...(dep.optionalDependencies || {}),
});
depMap[depName] = {
labels: {
scope: Scope.prod,
},
name: getName(depName),
requires: subDependencies.map(([key, ver]) => `${key}@${ver}`),
version: dep.version,
};
}

return depMap;
}

protected getDepTreeKey(dep: Dep): string {
return `${dep.name}@${dep.version}`;
}
}

function getName(depName: string) {
return depName.slice(0, depName.indexOf('@', 1));
}

0 comments on commit 0bd6a0a

Please sign in to comment.