Skip to content

Commit

Permalink
feat: [OSM-1122] use workspaces parser (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
gemaxim authored Jul 25, 2024
1 parent 307cede commit 47710cf
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 131 deletions.
152 changes: 22 additions & 130 deletions lib/workspaces/pnpm-workspaces-parser.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,12 @@
import * as debugModule from 'debug';
import * as pathUtil from 'path';
import * as fs from 'fs';

const debug = debugModule('snyk-pnpm-workspaces');
import * as lockFileParser from 'snyk-nodejs-lockfile-parser';
import { MultiProjectResultCustom, ScannedProjectCustom } from '../types';
import { getFileContents, isSubpath, normalizeFilePath } from '../utils';
import {
getWorkspacesMap,
packageJsonBelongsToWorkspace,
sortTargetFiles,
} from './workspace-utils';
import { DepGraph } from '@snyk/dep-graph';

const PNPM_ROOT_FILES = [
'pnpm-workspace.yaml',
'package.json',
'pnpm-lock.yaml',
];

// Compute project versions map
// This is needed because the lockfile doesn't present the version of
// a project that's part of a workspace, we need to retrieve it from
// its corresponding package.json
function computeProjectVersionMaps(root: string, targetFiles) {
const projectsVersionMap = {};
for (const directory of Object.keys(targetFiles)) {
if (!isSubpath(directory, root)) {
continue;
}
const packageJsonFileName = pathUtil.join(directory, 'package.json');
const packageJson = getFileContents(root, packageJsonFileName);

try {
const parsedPkgJson = lockFileParser.parsePkgJson(packageJson.content);
const projectVersion = parsedPkgJson.version;
projectsVersionMap[
normalizeFilePath(pathUtil.relative(root, directory))
] = projectVersion || 'undefined';
} catch (err: any) {
debug(
`Error getting version for project: ${packageJsonFileName}. ERROR: ${err}`,
);
continue;
}
}
return projectsVersionMap;
}
import { sortTargetFiles } from './workspace-utils';
import { ScannedNodeProject } from 'snyk-nodejs-lockfile-parser/dist/dep-graph-builders/types';

export async function processPnpmWorkspaces(
root: string,
Expand All @@ -56,7 +17,7 @@ export async function processPnpmWorkspaces(
},
targetFiles: string[],
): Promise<MultiProjectResultCustom> {
const pnpmTargetFiles = sortTargetFiles(targetFiles, PNPM_ROOT_FILES);
const pnpmWorkspaceDirs = sortTargetFiles(targetFiles, ['pnpm-lock.yaml']);

debug(`Processing potential Pnpm workspaces (${targetFiles.length})`);

Expand All @@ -68,101 +29,32 @@ export async function processPnpmWorkspaces(
scannedProjects: [],
};

let pnpmWorkspacesMap = {};
const pnpmWorkspacesFilesMap = {};

let rootWorkspaceManifestContent = {};
const projectsVersionMap = {};

// the folders must be ordered highest first
for (const directory of Object.keys(pnpmTargetFiles)) {
for (const directory of Object.keys(pnpmWorkspaceDirs)) {
debug(`Processing ${directory} as a potential Pnpm workspace`);
let isPnpmWorkspacePackage = false;
let isRootPackageJson = false;
const packageJsonFileName = pathUtil.join(directory, 'package.json');
const packageJson = getFileContents(root, packageJsonFileName);
pnpmWorkspacesMap = {
...pnpmWorkspacesMap,
...getWorkspacesMap(root, directory, packageJson),
};

for (const workspaceRoot of Object.keys(pnpmWorkspacesMap)) {
const match = packageJsonBelongsToWorkspace(
packageJsonFileName,
pnpmWorkspacesMap,
workspaceRoot,
);
if (match) {
debug(`${packageJsonFileName} matches an existing workspace pattern`);
pnpmWorkspacesFilesMap[packageJsonFileName] = {
root: workspaceRoot,
};
isPnpmWorkspacePackage = true;
}
if (packageJsonFileName === workspaceRoot) {
isRootPackageJson = true;
const workspaceRootDir = pathUtil.dirname(workspaceRoot);
projectsVersionMap[workspaceRootDir] = computeProjectVersionMaps(
workspaceRootDir,
pnpmTargetFiles,
);
rootWorkspaceManifestContent = JSON.parse(packageJson.content);
}
}

if (!(isPnpmWorkspacePackage || isRootPackageJson)) {
const pnpmWorkspacePath = pathUtil.join(directory, 'pnpm-workspace.yaml');
if (!fs.existsSync(pnpmWorkspacePath)) {
debug(
`${packageJsonFileName} is not part of any detected workspace, skipping`,
`Workspace file not found at ${directory}. Can't be a pnpm workspace root.`,
);
continue;
}

const rootDir = isPnpmWorkspacePackage
? pathUtil.dirname(pnpmWorkspacesFilesMap[packageJsonFileName].root)
: pathUtil.dirname(packageJsonFileName);

try {
const rootPnpmLockfileName = pathUtil.join(rootDir, 'pnpm-lock.yaml');
const pnpmLock = getFileContents(root, rootPnpmLockfileName);
const lockfileVersion = lockFileParser.getPnpmLockfileVersion(
pnpmLock.content,
);
const res: DepGraph = await lockFileParser.parsePnpmProject(
packageJson.content,
pnpmLock.content,
{
includeDevDeps: settings.dev || false,
includeOptionalDeps: settings.optional || true,
pruneWithinTopLevelDeps: true,
strictOutOfSync:
settings.strictOutOfSync === undefined
? true
: settings.strictOutOfSync,
},
lockfileVersion,
{
isWorkspacePkg: true,
workspacePath: normalizeFilePath(
pathUtil.relative(rootDir, directory),
),
isRoot: isRootPackageJson,
projectsVersionMap: projectsVersionMap[rootDir],
rootOverrides: rootWorkspaceManifestContent?.['pnpm.overrides'] || {},
},
);
const project: ScannedProjectCustom = {
packageManager: 'pnpm',
targetFile: pathUtil.relative(root, packageJson.fileName),
depGraph: res as any,
plugin: {
name: 'snyk-nodejs-lockfile-parser',
runtime: process.version,
},
};
result.scannedProjects.push(project);
} catch (e) {
debug(`Error process workspace: ${packageJsonFileName}. ERROR: ${e}`);
}
const scannedProjects: ScannedNodeProject[] =
await lockFileParser.parsePnpmWorkspace(root, directory, {
includeDevDeps: settings.dev || false,
includeOptionalDeps: settings.optional || true,
includePeerDeps: true,
pruneWithinTopLevelDeps: true,
strictOutOfSync:
settings.strictOutOfSync === undefined
? true
: settings.strictOutOfSync,
});
result.scannedProjects = result.scannedProjects.concat(
scannedProjects as ScannedProjectCustom[],
);
}

if (!result.scannedProjects.length) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"lodash.isempty": "^4.4.0",
"lodash.sortby": "^4.7.0",
"micromatch": "4.0.7",
"snyk-nodejs-lockfile-parser": "^1.56.1",
"snyk-nodejs-lockfile-parser": "1.58.5",
"snyk-resolve-deps": "4.8.0"
},
"devDependencies": {
Expand Down

0 comments on commit 47710cf

Please sign in to comment.