Skip to content

Commit

Permalink
Merge pull request #24 from snyk/fix/error-codes
Browse files Browse the repository at this point in the history
fix: add code to all errors
  • Loading branch information
Kirill89 authored Oct 31, 2018
2 parents bda244f + b801e7f commit d21be0d
Show file tree
Hide file tree
Showing 13 changed files with 194 additions and 37 deletions.
3 changes: 3 additions & 0 deletions lib/errors/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export {InvalidUserInputError} from './invalid-user-input-error';
export {UnsupportedRuntimeError} from './unsupported-runtime-error';
export {OutOfSyncError} from './out-of-sync-error';
9 changes: 9 additions & 0 deletions lib/errors/invalid-user-input-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class InvalidUserInputError extends Error {
public code = 422;
public name = 'InvalidUserInputError';

constructor(...args) {
super(...args);
Error.captureStackTrace(this, InvalidUserInputError);
}
}
26 changes: 26 additions & 0 deletions lib/errors/out-of-sync-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const LOCK_FILE_NAME = {
npm: 'package-lock.json',
yarn: 'yarn.lock',
};

const INSTALL_COMMAND = {
npm: 'npm install',
yarn: 'yarn install',
};

export class OutOfSyncError extends Error {
public code = 422;
public name = 'OutOfSyncError';
public dependencyName: string;
public lockFileType: string;

constructor(dependencyName: string, lockFileType: 'yarn' | 'npm') {
super(`Dependency ${dependencyName} was not found in ` +
`${LOCK_FILE_NAME[lockFileType]}. Your package.json and ` +
`${LOCK_FILE_NAME[lockFileType]} are probably out of sync. Please run ` +
`"${INSTALL_COMMAND[lockFileType]}" and try again.`);
this.dependencyName = dependencyName;
this.lockFileType = lockFileType;
Error.captureStackTrace(this, OutOfSyncError);
}
}
9 changes: 9 additions & 0 deletions lib/errors/unsupported-runtime-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class UnsupportedRuntimeError extends Error {
public name = 'UnsupportedRuntimeError';
public code = 500;

constructor(...args) {
super(...args);
Error.captureStackTrace(this, UnsupportedRuntimeError);
}
}
24 changes: 13 additions & 11 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {LockfileParser, Lockfile, ManifestFile, PkgTree,
import {PackageLockParser} from './parsers/package-lock-parser';
import {YarnLockParser} from './parsers/yarn-lock-parse';
import getRuntimeVersion from './get-node-runtime-version';
import {UnsupportedRuntimeError, InvalidUserInputError} from './errors';

export {
buildDepTree,
Expand Down Expand Up @@ -34,16 +35,15 @@ async function buildDepTree(
if (getRuntimeVersion() >= 6) {
lockfileParser = new YarnLockParser();
} else {
const unsupportedRuntimeError = new Error();
unsupportedRuntimeError.name = 'UnsupportedRuntimeError';
// tslint:disable:max-line-length
unsupportedRuntimeError.message = 'Parsing `yarn.lock` is not supported on Node.js version less than 6. Please upgrade your Node.js environment or use `package-lock.json`';
throw unsupportedRuntimeError;
throw new UnsupportedRuntimeError('Parsing `yarn.lock` is not ' +
'supported on Node.js version less than 6. Please upgrade your ' +
'Node.js environment or use `package-lock.json`');
}
break;
default:
throw new Error(`Unsupported lockfile type ${lockfileType} provided.
Only 'npm' or 'yarn' is currently supported.`);
throw new InvalidUserInputError('Unsupported lockfile type ' +
`${lockfileType} provided. Only 'npm' or 'yarn' is currently ` +
'supported.');
}

const manifestFile: ManifestFile = parseManifestFile(manifestFileContents);
Expand All @@ -64,18 +64,20 @@ async function buildDepTreeFromFiles(
} else if (_.endsWith(lockFilePath, 'yarn.lock')) {
lockFileType = LockfileType.yarn;
} else {
throw new Error(`Unknown lockfile ${lockFilePath}.
Please provide either package-lock.json or yarn.lock.`);
throw new InvalidUserInputError(`Unknown lockfile ${lockFilePath}. ` +
'Please provide either package-lock.json or yarn.lock.');
}

const manifestFileFullPath = path.resolve(root, manifestFilePath);
const lockFileFullPath = path.resolve(root, lockFilePath);

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

const manifestFileContents = fs.readFileSync(manifestFileFullPath, 'utf-8');
Expand Down
4 changes: 3 additions & 1 deletion lib/parsers/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as _ from 'lodash';
import {PackageLock} from './package-lock-parser';
import {YarnLock} from './yarn-lock-parse';
import {InvalidUserInputError} from '../errors';

export interface Dep {
name: string;
Expand Down Expand Up @@ -53,7 +54,8 @@ export function parseManifestFile(manifestFileContents: string): ManifestFile {
try {
return JSON.parse(manifestFileContents);
} catch (e) {
throw new Error(`package.json parsing failed with error ${e.message}`);
throw new InvalidUserInputError('package.json parsing failed with error ' +
e.message);
}
}

Expand Down
11 changes: 6 additions & 5 deletions lib/parsers/package-lock-parser.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as _ from 'lodash';
import {LockfileParser, PkgTree, Dep, DepType, ManifestFile,
getTopLevelDeps, Lockfile, LockfileType} from './';
import {InvalidUserInputError, OutOfSyncError} from '../errors';

export interface PackageLock {
name: string;
Expand Down Expand Up @@ -31,14 +32,16 @@ export class PackageLockParser implements LockfileParser {
packageLock.type = LockfileType.npm;
return packageLock;
} catch (e) {
throw new Error(`package-lock.json parsing failed with error ${e.message}`);
throw new InvalidUserInputError('package-lock.json parsing failed with ' +
`error ${e.message}`);
}
}

public async getDependencyTree(
manifestFile: ManifestFile, lockfile: Lockfile, includeDev = false): Promise<PkgTree> {
if (lockfile.type !== LockfileType.npm) {
throw new Error('Unsupported lockfile provided. Please provide `package-lock.json`.');
throw new InvalidUserInputError('Unsupported lockfile provided. Please ' +
'provide `package-lock.json`.');
}
const packageLock = lockfile as PackageLock;

Expand Down Expand Up @@ -102,9 +105,7 @@ export class PackageLockParser implements LockfileParser {
} else {
// tree was walked to the root and dependency was not found
if (!lockfilePath.length) {
throw new Error(`Dependency ${depName} was not found in package-lock.json.
Your package.json and package-lock.json are probably out of sync.
Please run "npm install" and try again.`);
throw new OutOfSyncError(depName, 'npm');
}
// dependency was not found on a current path, remove last key (move closer to the root) and try again
// visitedDepPaths can be passed by a reference, because traversing up doesn't update it
Expand Down
18 changes: 12 additions & 6 deletions lib/parsers/yarn-lock-parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import * as _ from 'lodash';
import {LockfileParser, PkgTree, Dep, DepType, ManifestFile,
getTopLevelDeps, Lockfile, LockfileType} from './';
import getRuntimeVersion from '../get-node-runtime-version';
import {
InvalidUserInputError,
UnsupportedRuntimeError,
OutOfSyncError,
} from '../errors';

export interface YarnLock {
type: string;
Expand Down Expand Up @@ -33,7 +38,8 @@ export class YarnLockParser implements LockfileParser {
// the import, so it has to be required conditionally
// more details at https://github.com/yarnpkg/yarn/issues/6304
if (getRuntimeVersion() < 6) {
throw new Error('yarn.lock parsing is supported for Node.js v6 and higher.');
throw new UnsupportedRuntimeError('yarn.lock parsing is supported for ' +
'Node.js v6 and higher.');
}
this.yarnLockfileParser = require('@yarnpkg/lockfile');
}
Expand All @@ -45,14 +51,16 @@ export class YarnLockParser implements LockfileParser {
yarnLock.type = LockfileType.yarn;
return yarnLock;
} catch (e) {
throw new Error(`yarn.lock parsing failed with an error: ${e.message}`);
throw new InvalidUserInputError('yarn.lock parsing failed with an ' +
`error: ${e.message}`);
}
}

public async getDependencyTree(
manifestFile: ManifestFile, lockfile: Lockfile, includeDev = false): Promise<PkgTree> {
if (lockfile.type !== LockfileType.yarn) {
throw new Error('Unsupported lockfile provided. Please provide `package-lock.json`.');
throw new InvalidUserInputError('Unsupported lockfile provided. ' +
'Please provide `package-lock.json`.');
}
const yarnLock = lockfile as YarnLock;

Expand Down Expand Up @@ -92,9 +100,7 @@ export class YarnLockParser implements LockfileParser {
const dep = _.get(lockFile.object, depKey);

if (!dep) {
throw new Error(`Dependency ${depKey} was not found in yarn.lock.
Your package.json and yarn.lock are probably out of sync.
Please run "yarn install" and try again.`);
throw new OutOfSyncError(searchedDep.name, 'yarn');
}

if (depPath.indexOf(depKey) >= 0) {
Expand Down
24 changes: 24 additions & 0 deletions test/lib/fixtures/out-of-sync/package-lock.json

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

17 changes: 17 additions & 0 deletions test/lib/fixtures/out-of-sync/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "trucolor",
"version": "0.7.1",
"description": "TTY color toolkit supporting Truecolor (24bit RGB)",
"author": "Mark Griffiths <[email protected]> (http://thebespokepixel.com/)",
"main": "index.js",
"module": "index.mjs",
"dependencies": {
"debug": "2.0.x",
"lodash": "4.17.11"
},
"engines": {
"node": ">=8.0"
},
"homepage": "https://github.com/MarkGriffiths/trucolor",
"license": "MIT"
}
15 changes: 15 additions & 0 deletions test/lib/fixtures/out-of-sync/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


[email protected]:
version "2.0.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.0.0.tgz#89bd9df6732b51256bc6705342bba02ed12131ef"
dependencies:
ms "0.6.2"

[email protected]:
version "0.6.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-0.6.2.tgz#d89c2124c6fdc1353d65a8b77bf1aac4b193708c"
dependencies:
debug "2.0.x"
28 changes: 24 additions & 4 deletions test/lib/package-lock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {test} from 'tap';
import {buildDepTreeFromFiles} from '../../lib';
import * as fs from 'fs';
import * as _ from 'lodash';
import {InvalidUserInputError, OutOfSyncError} from '../../lib/errors';

const load = (filename) => JSON.parse(
fs.readFileSync(`${__dirname}/fixtures/${filename}`, 'utf8'),
Expand Down Expand Up @@ -146,17 +147,36 @@ test('Performance: Parse big npm package-lock.json with cyclic deps and dev-deps
});

test('Parse invalid npm package-lock.json', async (t) => {
t.rejects(buildDepTreeFromFiles(
t.rejects(
buildDepTreeFromFiles(
`${__dirname}/fixtures/invalid-files/`,
'package.json',
'package-lock.json',
), new Error('package-lock.json parsing failed with error'), 'Expected error is thrown');
),
new InvalidUserInputError('package-lock.json parsing failed with error'),
'Expected error is thrown',
);
});

test('Parse invalid package.json', async (t) => {
t.rejects(buildDepTreeFromFiles(
t.rejects(
buildDepTreeFromFiles(
`${__dirname}/fixtures/invalid-files/`,
'package.json_invalid',
'package-lock.json',
), new Error('package.json parsing failed with error'), 'Expected error is thrown');
),
new InvalidUserInputError('package.json parsing failed with error'),
'Expected error is thrown',
);
});

test('Out of sync package-lock.json', async (t) => {
t.rejects(
buildDepTreeFromFiles(
`${__dirname}/fixtures/out-of-sync/`,
'package.json',
'package-lock.json',
),
new OutOfSyncError('lodash', 'npm'),
);
});
43 changes: 33 additions & 10 deletions test/lib/yarn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,25 @@ import {buildDepTreeFromFiles} from '../../lib';
import getRuntimeVersion from '../../lib/get-node-runtime-version';
import * as fs from 'fs';
import * as _ from 'lodash';
import {
UnsupportedRuntimeError,
InvalidUserInputError,
OutOfSyncError,
} from '../../lib/errors';

if (getRuntimeVersion() < 6) {
test('Parse yarn.lock', async (t) => {
const expectedError = new Error();
expectedError.name = 'UnsupportedRuntimeError';
expectedError.message = 'Parsing `yarn.lock` is not supported on Node.js version less than 6. Please upgrade your Node.js environment or use `package-lock.json`';
t.rejects(buildDepTreeFromFiles(
`${__dirname}/fixtures/goof/`,
'package.json',
'yarn.lock',
), expectedError, 'Information about non-supported environment is shown');
t.rejects(
buildDepTreeFromFiles(
`${__dirname}/fixtures/goof/`,
'package.json',
'yarn.lock',
),
new UnsupportedRuntimeError('Parsing `yarn.lock` is not supported on ' +
'Node.js version less than 6. Please upgrade your Node.js environment ' +
'or use `package-lock.json`'),
'Information about non-supported environment is shown',
);
});
} else {
const load = (filename) => JSON.parse(
Expand Down Expand Up @@ -138,10 +146,25 @@ if (getRuntimeVersion() < 6) {
});

test('Parse invalid yarn.lock', async (t) => {
t.rejects(buildDepTreeFromFiles(
t.rejects(
buildDepTreeFromFiles(
`${__dirname}/fixtures/invalid-files/`,
'package.json',
'yarn.lock',
), new Error('yarn.lock parsing failed with an error'), 'Expected error is thrown');
),
new InvalidUserInputError('yarn.lock parsing failed with an error'),
'Expected error is thrown',
);
});

test('Out of sync yarn.lock', async (t) => {
t.rejects(
buildDepTreeFromFiles(
`${__dirname}/fixtures/out-of-sync/`,
'package.json',
'yarn.lock',
),
new OutOfSyncError('lodash', 'yarn'),
);
});
}

0 comments on commit d21be0d

Please sign in to comment.