Skip to content

Commit

Permalink
feat: [OSM-1039] move nodejs plugin functionality (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
gemaxim committed Apr 11, 2024
1 parent 61f3fdd commit fb006b8
Show file tree
Hide file tree
Showing 48 changed files with 2,055 additions and 49 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pr-housekeeping.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ jobs:
- uses: actions/stale@v7
with:
stale-pr-message: "Your PR has not had any activity for 60 days. In 7 days I'll close it. Make some activity to remove this."
close-pr-message: "Your PR has now been stale for 7 days. I'm closing it."
close-pr-message: "Your PR has now been stale for 7 days. I'm closing it."
21 changes: 21 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"singleQuote": true,
"trailingComma": "all",
"overrides": [
{
"files": "*.csproj",
"options": {
"parser": "html"
}
},
{
"files": "*.json",
"options": {
"printWidth": 40,
"parser": "json",
"bracketSpacing": true,
"trailingComma": "none"
}
}
]
}
5 changes: 5 additions & 0 deletions .releaserc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"branches": [
"main"
]
}
2 changes: 1 addition & 1 deletion jest.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
"moduleFileExtensions": ["ts", "js", "json"],
"forceExit": true,
"testTimeout": 80000
}
}
56 changes: 12 additions & 44 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,12 @@
import * as modulesParser from './npm-modules-parser';
import * as lockParser from './npm-lock-parser';
import * as types from './types';
import { MissingTargetFileError } from './errors';
import { MultiProjectResult } from '@snyk/cli-interface/legacy/plugin';
import { DepGraph } from '@snyk/dep-graph';
import { PkgTree } from 'snyk-nodejs-lockfile-parser';

function isResDepGraph(depRes: PkgTree | DepGraph): depRes is DepGraph {
return 'rootPkg' in depRes;
}

export async function inspect(
root: string,
targetFile: string,
options: types.Options = {},
): Promise<MultiProjectResult> {
if (!targetFile) {
throw MissingTargetFileError(root);
}
const isLockFileBased =
targetFile.endsWith('package-lock.json') ||
targetFile.endsWith('yarn.lock');

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

let scannedProjects: any[] = [];
if (isResDepGraph(depRes)) {
scannedProjects = [{ depGraph: depRes }];
} else {
scannedProjects = [{ depTree: depRes }];
}

return {
plugin: {
name: 'snyk-nodejs-lockfile-parser',
runtime: process.version,
},
scannedProjects,
};
}
import { inspect } from './inspect';
import {
processNpmWorkspaces,
processPnpmWorkspaces,
processYarnWorkspaces,
} from './workspaces';
export {
inspect,
processNpmWorkspaces,
processPnpmWorkspaces,
processYarnWorkspaces,
};
45 changes: 45 additions & 0 deletions lib/inspect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import * as modulesParser from './npm-modules-parser';
import * as lockParser from './lock-parser';
import * as types from './types';
import { MissingTargetFileError } from './errors';
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,
targetFile: string,
options: types.Options = {},
): Promise<MultiProjectResult> {
if (!targetFile) {
throw MissingTargetFileError(root);
}
const isLockFileBased =
targetFile.endsWith('package-lock.json') ||
targetFile.endsWith('yarn.lock') ||
targetFile.endsWith('pnpm-lock.yaml');

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

let scannedProjects: any[] = [];
if (isResDepGraph(depRes)) {
scannedProjects = [{ depGraph: depRes }];
} else {
scannedProjects = [{ depTree: depRes }];
}

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

function isResDepGraph(depRes: PkgTree | DepGraph): depRes is DepGraph {
return 'rootPkg' in depRes;
}
82 changes: 82 additions & 0 deletions lib/lock-parser/build-dep-graph.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import * as path from 'path';
import * as fs from 'fs';
import * as lockFileParser from 'snyk-nodejs-lockfile-parser';
import {
NodeLockfileVersion,
InvalidUserInputError,
ProjectParseOptions,
} from 'snyk-nodejs-lockfile-parser';
import { DepGraph } from '@snyk/dep-graph';

export 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.PnpmLockV5:
case NodeLockfileVersion.PnpmLockV6:
return await lockFileParser.parsePnpmProject(
manifestFileContents,
lockFileContents,
{
includeDevDeps: options.includeDevDeps,
includeOptionalDeps: options.includeOptionalDeps,
pruneWithinTopLevelDeps: true,
strictOutOfSync: options.strictOutOfSync,
},
lockfileVersion,
);
case NodeLockfileVersion.YarnLockV1:
return await lockFileParser.parseYarnLockV1Project(
manifestFileContents,
lockFileContents,
{
includeDevDeps: options.includeDevDeps,
includeOptionalDeps: options.includeOptionalDeps,
includePeerDeps: options.includePeerDeps || false,
pruneLevel: 'withinTopLevelDeps',
strictOutOfSync: options.strictOutOfSync,
},
);
case NodeLockfileVersion.YarnLockV2:
return await lockFileParser.parseYarnLockV2Project(
manifestFileContents,
lockFileContents,
{
includeDevDeps: options.includeDevDeps,
includeOptionalDeps: options.includeOptionalDeps,
pruneWithinTopLevelDeps: true,
strictOutOfSync: options.strictOutOfSync,
},
);
case NodeLockfileVersion.NpmLockV2:
case NodeLockfileVersion.NpmLockV3:
return await lockFileParser.parseNpmLockV2Project(
manifestFileContents,
lockFileContents,
options,
);
}
throw new Error('Failed to build dep graph from current project');
}
73 changes: 73 additions & 0 deletions lib/lock-parser/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import * as path from 'path';
import * as fs from 'fs';
import { buildDepGraph } from './build-dep-graph';
import * as lockFileParser from 'snyk-nodejs-lockfile-parser';
import { NodeLockfileVersion, PkgTree } 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 | DepGraph> {
const lockFileFullPath = path.resolve(root, targetFile);
if (!fs.existsSync(lockFileFullPath)) {
throw new Error(
'Lockfile ' + targetFile + ' not found at location: ' + lockFileFullPath,
);
}

const fullPath = path.parse(lockFileFullPath);
const manifestFileFullPath = path.resolve(fullPath.dir, 'package.json');
const shrinkwrapFullPath = path.resolve(fullPath.dir, 'npm-shrinkwrap.json');

if (!fs.existsSync(manifestFileFullPath)) {
throw new Error(
`Could not find package.json at ${manifestFileFullPath} ` +
`(lockfile found at ${targetFile})`,
);
}

if (fs.existsSync(shrinkwrapFullPath)) {
throw new Error(
'Both `npm-shrinkwrap.json` and `package-lock.json` were found in ' +
fullPath.dir +
'.\n' +
'Please run your command again specifying `--file=package.json` flag.',
);
}

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

return lockFileParser.buildDepTreeFromFiles(
root,
manifestFileFullPath,
lockFileFullPath,
options.dev,
strictOutOfSync,
);
}
6 changes: 5 additions & 1 deletion lib/npm-modules-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ export async function parse(
options.file && options.file.replace('yarn.lock', 'package.json');
}

if (targetFile.endsWith('pnpm-lock.yaml')) {
options.file =
options.file && options.file.replace('pnpm-lock.yaml', 'package.json');
}

// package-lock.json falls back to package.json (used in wizard code)
if (targetFile.endsWith('package-lock.json')) {
options.file =
Expand Down Expand Up @@ -63,7 +68,6 @@ export async function parse(
`without dependencies.\nPlease run '${packageManager} install' first.`,
);
}

return resolveNodeDeps(
root,
Object.assign({}, options, { noFromArrays: true }),
Expand Down
4 changes: 4 additions & 0 deletions lib/workspaces/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { processNpmWorkspaces } from './npm-workspaces-parser';
import { processPnpmWorkspaces } from './pnpm-workspaces-parser';
import { processYarnWorkspaces } from './yarn-workspaces-parser';
export { processNpmWorkspaces, processPnpmWorkspaces, processYarnWorkspaces };
Loading

0 comments on commit fb006b8

Please sign in to comment.