Skip to content

Commit

Permalink
feat: [OSM-1122] improve workspaces parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
gemaxim committed Jul 16, 2024
1 parent c3a51a5 commit 20fd57c
Show file tree
Hide file tree
Showing 16 changed files with 358 additions and 423 deletions.
3 changes: 2 additions & 1 deletion lib/dep-graph-builders/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
extractPkgsFromYarnLockV2,
} from './yarn-lock-v2';
import { parseNpmLockV2Project } from './npm-lock-v2';
import { parsePnpmProject } from './pnpm';
import { parsePnpmProject, parsePnpmWorkspace } from './pnpm';
import { parsePkgJson } from './util';

export {
Expand All @@ -29,5 +29,6 @@ export {
parseYarnLockV2Project,
extractPkgsFromYarnLockV2,
parsePnpmProject,
parsePnpmWorkspace,
parsePkgJson,
};
15 changes: 4 additions & 11 deletions lib/dep-graph-builders/pnpm/build-dep-graph-pnpm.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { DepGraphBuilder } from '@snyk/dep-graph';
import { getTopLevelDeps } from '../util';
import type {
Overrides,
PnpmProjectParseOptions,
PnpmWorkspaceArgs,
} from '../types';
import type { Overrides, PnpmProjectParseOptions } from '../types';
import type { PackageJsonBase } from '../types';
import { getPnpmChildNode } from './utils';
import { eventLoopSpinner } from 'event-loop-spinner';
Expand All @@ -15,7 +11,7 @@ export const buildDepGraphPnpm = async (
lockFileParser: PnpmLockfileParser,
pkgJson: PackageJsonBase,
options: PnpmProjectParseOptions,
workspaceArgs?: PnpmWorkspaceArgs,
importer?: string,
) => {
const {
strictOutOfSync,
Expand All @@ -37,7 +33,7 @@ export const buildDepGraphPnpm = async (
const topLevelDeps = getTopLevelDeps(pkgJson, options);

const extractedTopLevelDeps =
lockFileParser.extractTopLevelDependencies(options) || {};
lockFileParser.extractTopLevelDependencies(options, importer) || {};

for (const name of Object.keys(topLevelDeps)) {
topLevelDeps[name].version = extractedTopLevelDeps[name].version;
Expand All @@ -58,10 +54,7 @@ export const buildDepGraphPnpm = async (
strictOutOfSync,
includeOptionalDeps,
includeDevDeps,
// we have rootWorkspaceOverrides if this is workspace pkg with overrides
// at root - therefore it should take precedent
// TODO: inspect if this is needed at all, seems like pnpm resolves everything in lockfile
workspaceArgs?.rootOverrides || pkgJson.pnpm?.overrides || {},
pkgJson.pnpm?.overrides || {},
pruneWithinTopLevelDeps,
lockFileParser,
);
Expand Down
50 changes: 3 additions & 47 deletions lib/dep-graph-builders/pnpm/index.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,4 @@
import { parsePkgJson } from '../util';
import {
PackageJsonBase,
PnpmProjectParseOptions,
PnpmWorkspaceArgs,
} from '../types';
import { buildDepGraphPnpm } from './build-dep-graph-pnpm';
import { DepGraph } from '@snyk/dep-graph';
import { getPnpmLockfileParser } from './lockfile-parser/index';
import { PnpmLockfileParser } from './lockfile-parser/lockfile-parser';
import { NodeLockfileVersion } from '../../utils';
import { parsePnpmProject } from './parse-project';
import { parsePnpmWorkspace } from './parse-workspace';

export const parsePnpmProject = async (
pkgJsonContent: string,
pnpmLockContent: string,
options: PnpmProjectParseOptions,
lockfileVersion?: NodeLockfileVersion,
workspaceArgs?: PnpmWorkspaceArgs,
): Promise<DepGraph> => {
const {
includeDevDeps,
includeOptionalDeps,
strictOutOfSync,
pruneWithinTopLevelDeps,
} = options;

const pkgJson: PackageJsonBase = parsePkgJson(pkgJsonContent);

const lockFileParser: PnpmLockfileParser = getPnpmLockfileParser(
pnpmLockContent,
lockfileVersion,
workspaceArgs,
);

const depgraph = await buildDepGraphPnpm(
lockFileParser,
pkgJson,
{
includeDevDeps,
strictOutOfSync,
includeOptionalDeps,
pruneWithinTopLevelDeps,
},
workspaceArgs,
);

return depgraph;
};
export { parsePnpmProject, parsePnpmWorkspace };
71 changes: 24 additions & 47 deletions lib/dep-graph-builders/pnpm/lockfile-parser/lockfile-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,47 +15,28 @@ export abstract class PnpmLockfileParser {
public lockFileVersion: string;
public rawPnpmLock: any;
public packages: Record<PnpmDepPath, PnpmLockPkg>;
public dependencies: Record<string, any>;
public devDependencies: Record<string, any>;
public optionalDependencies: Record<string, any>;
public peerDependencies: Record<string, any>;
public extractedPackages: NormalisedPnpmPkgs;
public importers: PnpmImporters;
public workspaceArgs?: PnpmWorkspaceArgs;

public constructor(rawPnpmLock: any, workspaceArgs?: PnpmWorkspaceArgs) {
this.rawPnpmLock = rawPnpmLock;
this.lockFileVersion = rawPnpmLock.lockFileVersion;
this.lockFileVersion = rawPnpmLock.lockfileVersion;
this.workspaceArgs = workspaceArgs;
const depsRoot = this.getRoot(rawPnpmLock);
this.packages = rawPnpmLock.packages || {};
this.dependencies = depsRoot.dependencies || {};
this.devDependencies = depsRoot.devDependencies || {};
this.optionalDependencies = depsRoot.optionalDependencies || {};
this.peerDependencies = depsRoot.peerDependencies || {};
this.extractedPackages = {};
this.importers = this.normaliseImporters(rawPnpmLock);
}

public isWorkspaceLockfile() {
return this.workspaceArgs?.isWorkspacePkg;
}

public getRoot(rawPnpmLock: any) {
let depsRoot = rawPnpmLock;
if (this.workspaceArgs?.isWorkspacePkg) {
depsRoot = rawPnpmLock.importers[this.workspaceArgs.workspacePath];
}
if (this.workspaceArgs?.isRoot) {
if (!this.workspaceArgs.workspacePath) {
this.workspaceArgs.workspacePath = '.';
}
depsRoot = rawPnpmLock.importers[this.workspaceArgs.workspacePath];
}
return depsRoot;
return this.workspaceArgs?.isWorkspace;
}

public extractPackages() {
// Packages should be parsed only one time for a parser
if (Object.keys(this.extractedPackages).length > 0) {
return this.extractedPackages;
}
const packages: NormalisedPnpmPkgs = {};
Object.entries(this.packages).forEach(
([depPath, versionData]: [string, any]) => {
Expand All @@ -81,42 +62,38 @@ export abstract class PnpmLockfileParser {
return packages;
}

public extractTopLevelDependencies(options: {
includeDevDeps: boolean;
includeOptionalDeps?: boolean;
includePeerDeps?: boolean;
}): PnpmDeps {
let importerName;
if (this.isWorkspaceLockfile()) {
importerName = this.workspaceArgs?.workspacePath;
public extractTopLevelDependencies(
options: {
includeDevDeps: boolean;
includeOptionalDeps?: boolean;
includePeerDeps?: boolean;
},
importer?: string,
): PnpmDeps {
let root = this.rawPnpmLock;
if (importer) {
root = this.rawPnpmLock.importers[importer];
}

const prodDeps = this.normalizeTopLevelDeps(
this.dependencies || {},
root.dependencies || {},
false,
importerName,
importer,
);
const devDeps = options.includeDevDeps
? this.normalizeTopLevelDeps(
this.devDependencies || {},
true,
importerName,
)
? this.normalizeTopLevelDeps(root.devDependencies || {}, true, importer)
: {};

const optionalDeps = options.includeOptionalDeps
? this.normalizeTopLevelDeps(
this.optionalDependencies || {},
root.optionalDependencies || {},
false,
importerName,
importer,
)
: {};

const peerDeps = options.includePeerDeps
? this.normalizeTopLevelDeps(
this.peerDependencies || {},
false,
importerName,
)
? this.normalizeTopLevelDeps(root.peerDependencies || {}, false, importer)
: {};

return { ...prodDeps, ...devDeps, ...optionalDeps, ...peerDeps };
Expand Down
5 changes: 0 additions & 5 deletions lib/dep-graph-builders/pnpm/lockfile-parser/lockfile-v5.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,8 @@ import { PnpmLockfileParser } from './lockfile-parser';
import { PnpmWorkspaceArgs } from '../../types';

export class LockfileV5Parser extends PnpmLockfileParser {
public specifiers: Record<string, string>;

public constructor(rawPnpmLock: any, workspaceArgs?: PnpmWorkspaceArgs) {
super(rawPnpmLock, workspaceArgs);
const depsRoot = this.getRoot(rawPnpmLock);
this.specifiers = depsRoot.specifiers;
}

public parseDepPath(depPath: string): ParsedDepPath {
Expand All @@ -36,7 +32,6 @@ export class LockfileV5Parser extends PnpmLockfileParser {
name,
version,
isDev,
specifier: this.specifiers[name],
};
return pnpmDeps;
},
Expand Down
1 change: 0 additions & 1 deletion lib/dep-graph-builders/pnpm/lockfile-parser/lockfile-v6.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ export class LockfileV6Parser extends PnpmLockfileParser {
pnpmDeps[name] = {
name,
version,
specifier: depInfo.specifier,
isDev,
};
return pnpmDeps;
Expand Down
5 changes: 1 addition & 4 deletions lib/dep-graph-builders/pnpm/lockfile-parser/lockfile-v9.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@ import { PnpmWorkspaceArgs } from '../../types';
import { LockfileV6Parser } from './lockfile-v6';

const DEFAULT_WORKSPACE_ARGS: PnpmWorkspaceArgs = {
isWorkspacePkg: true,
isRoot: true,
workspacePath: '.',
isWorkspace: true,
projectsVersionMap: {},
rootOverrides: {},
};
export class LockfileV9Parser extends LockfileV6Parser {
public settings;
Expand Down
47 changes: 47 additions & 0 deletions lib/dep-graph-builders/pnpm/parse-project.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { parsePkgJson } from '../util';
import { PackageJsonBase, PnpmProjectParseOptions } from '../types';
import { buildDepGraphPnpm } from './build-dep-graph-pnpm';
import { DepGraph } from '@snyk/dep-graph';
import { getPnpmLockfileParser } from './lockfile-parser/index';
import { NodeLockfileVersion } from '../../utils';

export const parsePnpmProject = async (
pkgJsonContent: string,
pnpmLockContent: string,
options: PnpmProjectParseOptions,
lockfileVersion?: NodeLockfileVersion,
): Promise<DepGraph> => {
const {
includeDevDeps,
includeOptionalDeps,
strictOutOfSync,
pruneWithinTopLevelDeps,
} = options;
let importer = '';

const pkgJson: PackageJsonBase = parsePkgJson(pkgJsonContent);

const lockFileParser = getPnpmLockfileParser(
pnpmLockContent,
lockfileVersion,
);

// Lockfile V9 simple project has the root importer
if (lockFileParser.lockFileVersion.startsWith('9')) {
importer = '.';
}

const depgraph = await buildDepGraphPnpm(
lockFileParser,
pkgJson,
{
includeDevDeps,
strictOutOfSync,
includeOptionalDeps,
pruneWithinTopLevelDeps,
},
importer,
);

return depgraph;
};
Loading

0 comments on commit 20fd57c

Please sign in to comment.