npm-dependencies-extractor
Advanced tools
Comparing version 0.0.5 to 0.0.6
{ | ||
"name": "npm-dependencies-extractor", | ||
"version": "0.0.5", | ||
"version": "0.0.6", | ||
"description": "Retrieves the (flat) list of package dependencies for Javascript projects using npm", | ||
@@ -21,3 +21,7 @@ "main": "index.js", | ||
"npm", | ||
"sbom", | ||
"software bill of material", | ||
"flat list", | ||
"extract", | ||
"retrieve", | ||
"dependencies flat list", | ||
@@ -24,0 +28,0 @@ "extract dependencies", |
[![Build Status](https://travis-ci.com/philips-software/npm-dependencies-extractor.svg?branch=master)](https://travis-ci.com/philips-software/npm-dependencies-extractor) | ||
# npm-dependencies-extractor | ||
This is a CLI package that provides a command *extract-dependencies* to extract the flat list of (all) dependencies from a package-lock.json file (or another file with the same structure of your choice. If you want, instead of the package-lock.json, you may specify as input file the JSON file generated by the npm command to list json dependencies, such as: | ||
This is a CLI package that provides a command *extract-dependencies* to extract the flat list of (all installed) dependencies from a package-lock.json file (or another file with the same structure of your choice. If you want, instead of the package-lock.json, you may specify as input file the JSON file generated by the npm command to list json dependencies, such as: | ||
``` | ||
@@ -33,2 +33,9 @@ npm list --json > inputFile.json | ||
# Status | ||
0.0.6, see [CHANGELOG.md](./CHANGELOG.md) | ||
## Technology stack | ||
- Javascript | ||
- This software is intended to be used standalone, as a command-line tool | ||
## Prerequisites | ||
@@ -67,3 +74,3 @@ - you should have Node installed (this script was tested with node v8.12.0) | ||
```shell | ||
npx npm-dependencies-extractor extract-dependencies [options] | ||
npx npm-dependencies-extractor [options] | ||
``` | ||
@@ -83,3 +90,8 @@ | ||
### Sample usage | ||
``` | ||
npm run extract-dependencies -- -i ./test-data/input-with-optionals/package-lock-with-2-mandatory-dependencies.json --verbose | ||
``` | ||
## Usage scenarios | ||
@@ -94,5 +106,10 @@ | ||
``` | ||
npx npm-dependencies-extractor extract-dependencies [options] | ||
npx npm-dependencies-extractor [options] | ||
``` | ||
or, if you don't want to install it from github master, run: | ||
``` | ||
npx github:philips-software/npm-dependencies-extractor [options] | ||
``` | ||
### Scenario 2: You include the npm-dependencies-extractor as a dependency of your project, and call its command in your project's scripts, by: | ||
@@ -113,1 +130,27 @@ ```shell | ||
## Owners | ||
See [CODEOWNERS](./CODEOWNERS) | ||
## Maintainers | ||
See [MAINTAINERS.md](./MAINTAINERS.md) | ||
## Contributing | ||
See [CONTRIBUTING.md](./CONTRIBUTING.md) | ||
## License | ||
See [LICENSE.md](./LICENSE.md) | ||
## Author | ||
Sanda Contiu | ||
## Keywords | ||
- dependencies | ||
- npm | ||
- sbom | ||
- software bill of material | ||
- flat list | ||
- extract | ||
- retrieve | ||
- dependencies flat list | ||
- extract dependencies | ||
- list dependencies |
@@ -6,18 +6,23 @@ const utilities = require('./utilities'); | ||
const isDependencyOptional = ({ jsonDependencyDetails }) => Object.keys(jsonDependencyDetails).includes('optional') | ||
&& (jsonDependencyDetails.optional === true); | ||
// Gets the dependencies from the 'dependencies' attribute | ||
const getRecursivelyDependenciesReducer = (accumulator, currentPackageKeyPairTwoSizedArray) => { | ||
// Push the current package info (name and version only) | ||
accumulator.push( | ||
formatDependencyAsJsonObject( | ||
currentPackageKeyPairTwoSizedArray[0], | ||
currentPackageKeyPairTwoSizedArray[1].version, | ||
), | ||
); | ||
if (!isDependencyOptional({ jsonDependencyDetails: currentPackageKeyPairTwoSizedArray[1] })) { | ||
// Push the current package info (name and version only) | ||
accumulator.push( | ||
formatDependencyAsJsonObject( | ||
currentPackageKeyPairTwoSizedArray[0], | ||
currentPackageKeyPairTwoSizedArray[1].version, | ||
), | ||
); | ||
if (Object.keys(currentPackageKeyPairTwoSizedArray[1]).includes('dependencies')) { | ||
// go recursively and concatenate the found dependencies | ||
accumulator = accumulator.concat( // eslint-disable-line no-param-reassign | ||
Object.entries(currentPackageKeyPairTwoSizedArray[1].dependencies) | ||
.reduce(getRecursivelyDependenciesReducer, []), | ||
); | ||
if (Object.keys(currentPackageKeyPairTwoSizedArray[1]).includes('dependencies')) { | ||
// go recursively and concatenate the found dependencies | ||
accumulator = accumulator.concat( // eslint-disable-line no-param-reassign | ||
Object.entries(currentPackageKeyPairTwoSizedArray[1].dependencies) | ||
.reduce(getRecursivelyDependenciesReducer, []), | ||
); | ||
} | ||
} | ||
@@ -38,2 +43,3 @@ return accumulator; | ||
getFlatListOfDependencies, | ||
isDependencyOptional, | ||
}; |
@@ -16,2 +16,7 @@ const packageLockFileUniqueVersions = '../test-data/npm-dependencies-unique-versions/test-package-lock.json'; | ||
const packageLock2MandatoryDependencies = require('../test-data/input-with-optionals/package-lock-with-2-mandatory-dependencies.json'); | ||
const packageLock3DeclaredOptionalDependenciesAllActuallyOptional = require('../test-data/input-with-optionals/package-lock-with-total-3-dependencies-3-optionals-not-required-by-any-mandatory-dependency.json'); | ||
const packageLock4Dependencies3DeclaredOptionalActually2Optional = require('../test-data/input-with-optionals/package-lock-with-total-4-dependencies-3-optionals-of-which-1-also-occurs-as-mandatory.json'); | ||
/* | ||
@@ -134,1 +139,72 @@ The package-lock.json loaded in the next test case reflects the | ||
}); | ||
describe('isDependencyOptional', () => { | ||
it('returns true for a dependency json with key "optional" set to true', | ||
() => { | ||
const jsonDependencyDetails = { | ||
version: '2.14.0', | ||
resolved: 'https://registry.npmjs.org/nan/-/nan-2.14.0.tgz', | ||
integrity: 'some-key', | ||
optional: true, | ||
}; | ||
expect(dependenciesExtractor.isDependencyOptional({ jsonDependencyDetails })) | ||
.toBe(true); | ||
}); | ||
it('returns false for a dependency json with key "optional" set to false', | ||
() => { | ||
const jsonDependencyDetails = { | ||
version: '2.14.0', | ||
resolved: 'https://registry.npmjs.org/nan/-/nan-2.14.0.tgz', | ||
integrity: 'some-key', | ||
optional: false, | ||
}; | ||
expect(dependenciesExtractor.isDependencyOptional({ jsonDependencyDetails })) | ||
.toBe(false); | ||
}); | ||
it('returns false for a dependency json with no key "optional"', | ||
() => { | ||
const jsonDependencyDetails = { | ||
version: '2.14.0', | ||
resolved: 'https://registry.npmjs.org/nan/-/nan-2.14.0.tgz', | ||
integrity: 'some-key', | ||
}; | ||
expect(dependenciesExtractor.isDependencyOptional({ jsonDependencyDetails })) | ||
.toBe(false); | ||
}); | ||
}); | ||
describe('getFlatListOfDependencies deals with optional dependencies as follows:', () => { | ||
it(`extracts all mandatory dependencies from input ${packageLock2MandatoryDependencies}`, | ||
() => { | ||
expect(dependenciesExtractor | ||
.getFlatListOfDependencies(packageLock2MandatoryDependencies)) | ||
.toEqual([ | ||
{ [NAME_KEY]: '@babel/code-frame', [VERSION_KEY]: '7.5.5' }, | ||
{ [NAME_KEY]: '@babel/highlight', [VERSION_KEY]: '7.5.0' }, | ||
]); | ||
}); | ||
// eslint-disable-next-line prefer-template | ||
it('extracts zero dependencies from input (when input\'s dependencies are all optional) ' | ||
+ packageLock3DeclaredOptionalDependenciesAllActuallyOptional, | ||
() => { | ||
expect(dependenciesExtractor | ||
.getFlatListOfDependencies(packageLock3DeclaredOptionalDependenciesAllActuallyOptional)) | ||
.toEqual([]); | ||
}); | ||
// eslint-disable-next-line prefer-template | ||
it('extracts all mandatory dependencies from input json ' | ||
+ packageLock4Dependencies3DeclaredOptionalActually2Optional, | ||
() => { | ||
expect(dependenciesExtractor | ||
.getFlatListOfDependencies(packageLock4Dependencies3DeclaredOptionalActually2Optional)) | ||
.toEqual([ | ||
{ [NAME_KEY]: 'ansi-regex', [VERSION_KEY]: '2.1.1' }, | ||
{ [NAME_KEY]: 'has-ansi', [VERSION_KEY]: '2.0.0' }, | ||
]); | ||
}); | ||
}); |
@@ -14,2 +14,3 @@ #! /usr/bin/env node | ||
const formatter = require('./formatter'); | ||
const utilities = require('./utilities'); | ||
@@ -62,2 +63,7 @@ program | ||
if (!utilities.isValidPackageLockJson(dependencies)) { | ||
infoMessage(chalk`Input {blue ${input}} not a valid {blue package-lock.json} format, exiting.`); | ||
return; | ||
} | ||
const extractedDependenciesJsonArr = dependencyExtractor.getFlatListOfDependencies(dependencies); | ||
@@ -64,0 +70,0 @@ |
@@ -46,5 +46,14 @@ const { | ||
const isValidPackageLockJson = input => typeof input !== 'undefined' | ||
&& typeof input.dependencies !== 'undefined' | ||
&& !Object.entries(input.dependencies).find( | ||
entry => typeof entry === 'undefined' | ||
|| typeof entry[1] === 'undefined' | ||
|| typeof entry[1].version === 'undefined', | ||
); | ||
module.exports = { | ||
sortByNameAndVersionCaseInsensitive, | ||
getUniquesByNameAndVersion, | ||
isValidPackageLockJson, | ||
}; |
@@ -111,1 +111,6 @@ const utilities = require('./utilities'); | ||
}); | ||
test('isValidPackageLockJson returns false if input is incorrect', () => { | ||
const input = { dependencies: { 'some package': { } } }; | ||
expect(utilities.isValidPackageLockJson(input)).toBe(false); | ||
}); |
83924
34
1592
151