snyk-nodejs-lockfile-parser
Version:
Generate a dep tree given a lockfile
130 lines • 5.51 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getLockfileVersionFromFile = exports.NodeLockfileVersion = void 0;
exports.getPnpmLockfileVersion = getPnpmLockfileVersion;
exports.getYarnLockfileVersion = getYarnLockfileVersion;
exports.getNpmLockfileVersion = getNpmLockfileVersion;
exports.parseJsonFile = parseJsonFile;
exports.describeLikelyJsonCause = describeLikelyJsonCause;
const fs_1 = require("fs");
const js_yaml_1 = require("js-yaml");
const errors_1 = require("./errors");
const error_catalog_nodejs_public_1 = require("@snyk/error-catalog-nodejs-public");
var NodeLockfileVersion;
(function (NodeLockfileVersion) {
NodeLockfileVersion["NpmLockV1"] = "NPM_LOCK_V1";
NodeLockfileVersion["NpmLockV2"] = "NPM_LOCK_V2";
NodeLockfileVersion["NpmLockV3"] = "NPM_LOCK_V3";
NodeLockfileVersion["YarnLockV1"] = "YARN_LOCK_V1";
NodeLockfileVersion["YarnLockV2"] = "YARN_LOCK_V2";
NodeLockfileVersion["PnpmLockV5"] = "PNPM_LOCK_V5";
NodeLockfileVersion["PnpmLockV6"] = "PNPM_LOCK_V6";
NodeLockfileVersion["PnpmLockV9"] = "PNPM_LOCK_V9";
})(NodeLockfileVersion || (exports.NodeLockfileVersion = NodeLockfileVersion = {}));
const getLockfileVersionFromFile = (targetFile) => {
const lockFileContents = (0, fs_1.readFileSync)(targetFile, 'utf-8');
if (targetFile.endsWith('package-lock.json')) {
return getNpmLockfileVersion(lockFileContents);
}
else if (targetFile.endsWith('yarn.lock')) {
return getYarnLockfileVersion(lockFileContents);
}
else if (targetFile.endsWith('pnpm-lock.yaml')) {
return getPnpmLockfileVersion(lockFileContents);
}
else {
throw new errors_1.InvalidUserInputError(`Unknown lockfile ${targetFile}. ` +
'Please provide either package-lock.json, yarn.lock or pnpm-lock.yaml');
}
};
exports.getLockfileVersionFromFile = getLockfileVersionFromFile;
function getPnpmLockfileVersion(lockFileContents) {
const rawPnpmLock = (0, js_yaml_1.load)(lockFileContents, {
json: true,
schema: js_yaml_1.FAILSAFE_SCHEMA,
});
const { lockfileVersion } = rawPnpmLock;
if (lockfileVersion.startsWith('5')) {
return NodeLockfileVersion.PnpmLockV5;
}
else if (lockfileVersion.startsWith('6')) {
return NodeLockfileVersion.PnpmLockV6;
}
else if (lockfileVersion.startsWith('9')) {
return NodeLockfileVersion.PnpmLockV9;
}
else {
throw new error_catalog_nodejs_public_1.OpenSourceEcosystems.PnpmUnsupportedLockfileVersionError(`The pnpm-lock.yaml lockfile version ${lockfileVersion} is not supported`);
}
}
function getYarnLockfileVersion(lockFileContents) {
if (lockFileContents.includes('__metadata')) {
return NodeLockfileVersion.YarnLockV2;
}
else {
return NodeLockfileVersion.YarnLockV1;
}
}
function getNpmLockfileVersion(lockFileContents) {
// Parse first; surfacing the real JSON error happens inside parseJsonFile.
const lockfileJson = parseJsonFile(lockFileContents, 'package-lock.json');
// The version check runs *outside* the parse try/catch so that an
// unsupported (but otherwise valid JSON) lockfile is not mis-reported as a
// JSON syntax error.
const lockfileVersion = lockfileJson.lockfileVersion || null;
switch (lockfileVersion) {
case null:
case 1:
return NodeLockfileVersion.NpmLockV1;
case 2:
return NodeLockfileVersion.NpmLockV2;
case 3:
return NodeLockfileVersion.NpmLockV3;
default:
throw new errors_1.InvalidUserInputError(`Unsupported npm lockfile version "${lockfileVersion}" in package-lock.json. ` +
'Please provide a package-lock.json with lockfileVersion 1, 2 or 3');
}
}
/**
* Parse JSON from a manifest or lockfile. On failure throws an
* InvalidUserInputError that preserves the underlying parser message
* (including the position of the syntax error) and appends a best-effort hint
* about the likely cause.
*
* `fileLabel` is the file kind shown in the error, e.g. 'package.json' or
* 'package-lock.json'.
*/
function parseJsonFile(content, fileLabel) {
try {
return JSON.parse(content);
}
catch (e) {
throw new errors_1.InvalidUserInputError(`${fileLabel} parsing failed with error ${e.message}` +
describeLikelyJsonCause(content));
}
}
/**
* Best-effort, allocation-light hint describing the most likely reason a JSON
* parse failed. Inspects only the leading characters of the content, never
* throws, and returns '' when nothing recognisable is found - so it is always
* safe to append to a parse-error message.
*/
function describeLikelyJsonCause(content) {
if (!content) {
return ' The file is empty.';
}
// A byte-order mark (UTF-8/UTF-16/UTF-32) decoded into the string.
if (content.charCodeAt(0) === 0xfeff) {
return ' The file begins with a byte-order mark (BOM); re-save it as UTF-8 without a BOM.';
}
// NUL bytes strongly suggest the file is UTF-16/UTF-32 encoded.
if (content.includes('\x00')) {
return ' The file contains NUL bytes; it may be UTF-16/UTF-32 encoded. Re-save it as UTF-8.';
}
// Unresolved git merge-conflict markers.
if (/^(<{7}|={7}|>{7})( |$)/m.test(content)) {
return ' The file appears to contain unresolved git merge-conflict markers.';
}
return '';
}
//# sourceMappingURL=utils.js.map