Advanced scenarios, special cases, and error recovery patterns.
Packages installed from local filesystem don't have a resolved field. URL-based validators automatically skip these:
// This package will be automatically skipped by URL validators
{
'local-package@1.0.0': {
version: '1.0.0'
// No resolved field - automatically skipped
}
}Behavior: All URL-based validators (ValidateHost, ValidateHttps, ValidateScheme, ValidateUrl, ValidatePackageNames) automatically skip packages without a resolved field.
Git dependencies and older lockfiles may not have integrity hashes:
// This package will be automatically skipped by ValidateIntegrity
{
'git-package@1.0.0': {
version: '1.0.0',
resolved: 'git+https://github.com/user/repo.git'
// No integrity field - automatically skipped
}
}Behavior: ValidateIntegrity automatically skips packages without an integrity field.
The parser automatically detects npm v1/v2 vs v3 format:
const parser = new ParseLockfile({
lockfilePath: './package-lock.json',
lockfileType: 'npm'
});
// Automatically detects v1/v2 vs v3 format
const lockfile = parser.parseSync();The parser automatically detects and normalizes Yarn formats:
const parser = new ParseLockfile({
lockfilePath: './yarn.lock',
lockfileType: 'yarn'
});
// Automatically detects classic vs Berry format
// Berry format is normalized to match classic structure
const lockfile = parser.parseSync();For non-standard filenames, always specify lockfileType explicitly:
// npm-shrinkwrap.json is NOT auto-detected by filename
const parser = new ParseLockfile({
lockfilePath: './npm-shrinkwrap.json',
lockfileType: 'npm' // Required!
});
// Custom lockfile names
const parser = new ParseLockfile({
lockfilePath: './custom.lock',
lockfileType: 'yarn' // Required!
});function safeParseLockfile(path, type) {
try {
const parser = new ParseLockfile({
lockfilePath: path,
lockfileType: type
});
return parser.parseSync();
} catch (error) {
if (error.name === 'ParsingError') {
// Handle parsing-specific errors
if (error.message.includes('NO_PARSER_FOR_PATH')) {
// Try with explicit type
return safeParseLockfile(path, 'npm');
}
if (error.message.includes('READ_FAILED')) {
// Try reading as text
const fs = require('fs');
const content = fs.readFileSync(path, 'utf-8');
const parser = new ParseLockfile({
lockfileText: content,
lockfileType: type
});
return parser.parseSync();
}
}
throw error;
}
}By default, empty hostnames are rejected. Allow them if needed:
const validator = new ValidateHost({ packages: lockfile.object });
// Default: empty hostnames are rejected
const result1 = validator.validate(['npm']);
// Packages with empty hostnames will fail
// Allow empty hostnames
const result2 = validator.validate(['npm'], { emptyHostname: true });
// Packages with empty hostnames will passAll these are equivalent when using shortcuts:
const validator = new ValidateHost({ packages: lockfile.object });
// All equivalent
validator.validate(['npm']);
validator.validate(['registry.npmjs.org']);
validator.validate(['https://registry.npmjs.org']);
// Full URLs have hostname extracted automatically
validator.validate(['https://registry.npmjs.org/path/to/resource']);
// Only 'registry.npmjs.org' is used for comparisonSchemes must include the trailing colon:
const validator = new ValidateScheme({ packages: lockfile.object });
// Correct format (with colon)
validator.validate(['https:', 'http:', 'git:']);
// Incorrect format (without colon) - will not match
validator.validate(['https', 'http', 'git']); // Will fail validationSome packages legitimately use different names in lockfile vs registry:
const validator = new ValidatePackageNames({ packages: lockfile.object });
// Allow specific package name aliases
// Format: 'declared-name:actual-name'
const result = validator.validate([
'my-lodash:lodash', // package.json has "my-lodash": "npm:lodash@4.17.21"
'my-react:react',
'company-utils:@company/utils'
]);Exclusions match by package name prefix:
const validator = new ValidateIntegrity({ packages: lockfile.object });
// Exclusion matches by prefix
integrityExclude: ['lodash'] // Matches 'lodash@4.17.21', 'lodash@4.18.0', etc.
integrityExclude: ['@babel/core'] // Matches '@babel/core@7.20.0', '@babel/core@7.21.0', etc.
// Exclude multiple packages
const result = validator.validate({
integrityExclude: [
'legacy-package',
'old-dependency',
'@company/legacy-lib',
'@types/old-types'
]
});const validator = new ValidateHost({ packages: lockfile.object });
// Allow multiple registries
const result = validator.validate([
'npm', // Public npm
'registry.mycompany.com', // Private corporate registry
'registry.verdaccio.org' // Verdaccio
]);ValidatePackageNames only validates packages from official public registries (npm, yarn, verdaccio). Packages from private registries are automatically skipped:
// These will be validated
resolved: 'https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz'
resolved: 'https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz'
// These will be skipped (private registries)
resolved: 'https://registry.mycompany.com/lodash/-/lodash-4.17.21.tgz'
resolved: 'https://nexus.example.org/lodash/-/lodash-4.17.21.tgz'Handle different parsing error types:
try {
const parser = new ParseLockfile({ lockfilePath: './package-lock.json' });
const lockfile = parser.parseSync();
} catch (error) {
if (error.name === 'ParsingError') {
switch (true) {
case error.message.includes('NO_LOCKFILE'):
console.error('No lockfile path or content provided');
break;
case error.message.includes('NO_PARSER_FOR_PATH'):
console.error('Cannot determine lockfile type. Specify lockfileType explicitly.');
break;
case error.message.includes('READ_FAILED'):
console.error('Cannot read lockfile from filesystem');
break;
case error.message.includes('PARSE_NPMLOCKFILE_FAILED'):
console.error('npm lockfile parsing failed - invalid JSON or format');
break;
case error.message.includes('PARSE_YARNLOCKFILE_FAILED'):
console.error('yarn lockfile parsing failed - invalid SYML or format');
break;
default:
console.error('Parsing error:', error.message);
}
} else {
console.error('Unexpected error:', error);
}
}Collect errors from multiple validators:
const {
ParseLockfile,
ValidateHost,
ValidateHttps,
ValidateIntegrity
} = require('lockfile-lint-api');
function collectAllErrors(lockfilePath) {
const parser = new ParseLockfile({ lockfilePath });
const lockfile = parser.parseSync();
const allErrors = [];
// HTTPS validation
const httpsValidator = new ValidateHttps({ packages: lockfile.object });
const httpsResult = httpsValidator.validate();
if (httpsResult.type === 'error') {
allErrors.push(...httpsResult.errors.map(e => ({ type: 'https', ...e })));
}
// Host validation
const hostValidator = new ValidateHost({ packages: lockfile.object });
const hostResult = hostValidator.validate(['npm']);
if (hostResult.type === 'error') {
allErrors.push(...hostResult.errors.map(e => ({ type: 'host', ...e })));
}
// Integrity validation
const integrityValidator = new ValidateIntegrity({ packages: lockfile.object });
const integrityResult = integrityValidator.validate();
if (integrityResult.type === 'error') {
allErrors.push(...integrityResult.errors.map(e => ({ type: 'integrity', ...e })));
}
return allErrors;
}
const errors = collectAllErrors('./package-lock.json');
if (errors.length > 0) {
console.error(`Found ${errors.length} validation errors:`);
errors.forEach(error => {
console.error(`[${error.type}] ${error.package}: ${error.message}`);
});
}Use validateSingle for targeted checks on specific packages:
const validator = new ValidateHost({ packages: lockfile.object });
// Check specific critical packages
const criticalPackages = ['lodash@4.17.21', 'express@4.18.2', 'react@18.2.0'];
criticalPackages.forEach(packageName => {
if (!validator.validateSingle(packageName, ['npm'])) {
console.error(`CRITICAL: ${packageName} is not from allowed hosts`);
process.exit(1);
}
});Note: validateSingle may throw errors if URL parsing fails, unlike validate() which catches errors silently.
URL whitelisting requires exact matches - no pattern matching:
const validator = new ValidateUrl({ packages: lockfile.object });
// Exact match required
const allowedUrls = [
'https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz'
];
// This will PASS
// resolved: 'https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz'
// This will FAIL (different version)
// resolved: 'https://registry.npmjs.org/lodash/-/lodash-4.17.22.tgz'
// This will FAIL (different path)
// resolved: 'https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz?cache=bust'URL matching is case-sensitive:
// These are different URLs
'https://registry.npmjs.org/package/-/package-1.0.0.tgz'
'https://registry.npmjs.org/Package/-/Package-1.0.0.tgz' // Different!