An utility for scan and analyze packages in node_modules
. It uses file system scan to determine which package physical instances are exactly installed in node_modules
.
Is a part of Discovery.js projects.
Install:
npm install @discoveryjs/node-modules
Use:
const fetchNodeModules = require('@discoveryjs/node-modules');
fetchNodeModules('absolute/path/to/project').then(packages => {
// do something with found packages
});
See examples below.
Main function to fetch a package list for a specified basedir
. The method check basedir
for node_modules
and package.json
to retrieve a package list. When basedir
is not specified process.cwd()
is using. Root package.json
is optional and used to determine which packages are using for development purposes only.
A list of packages contains each physical instance of packages. That is, if a package has several copies of itself (e.g. due to various versions) all of them will be in the list. Each entry has following properties:
name
– value ofname
field inpackage.json
version
– value ofversion
field inpackage.json
; can benull
for rootpackage.json
dev
– boolean value, whichtrue
when a package is using for dev purposes onlypath
– relative tobasedir
path to a package. It can be used as an unique identifier of a package instance (anddeps.resolved
use the same values for a reference)entry
- relative topath
path to an entry point module. It can benull
when entry is not resolveddeps
- list of entries fromdependencies
,peerDependencies
andoptionalDependencies
sections ofpackage.json
.devDependencies
are included for rootpackage.json
only. Each entry has following properties:type
– one ofprod
,peer
,optional
ordev
name
- a key from a dependency sectionversion
- a value from a dependency sectionresolved
- resolved path to a package. It can be used to find a physical instance of package it refers to. It may containnull
, if no physical instance of package is not found (Note: that's a missed dependency forprod
,peer
anddev
dependencies, but not a problem foroptional
).
packageJson
- content of apackage.json
parsed withJSON.parse()
Analyzer to use with @discoveryjs/scan-fs:
const scanFs = require('@discoveryjs/scan-fs');
const nodeModules = require('@discoveryjs/node-modules');
scanFs({
...
rules: [
...
{
test: /\/package\.json$/,
extract: nodeModules.analyzer
}
]
});
const fetchNodeModules = require('@discoveryjs/node-modules');
fetchNodeModules(__dirname).then(modules => {
const groupByName = modules.reduce(
(map, entry) => map.set(entry.name, (map.get(entry.name) || []).concat(entry)),
new Map()
);
// find packages with more than one physical instance
// and sort by entry count from top to bottom
const duplicates = [...groupByName]
.filter(([, entries]) => entries.length > 1)
.sort(([, a], [, b]) => b.length - a.length);
// output findings
duplicates.forEach(([name, entries]) => {
console.log(`${name} (${entries.length} entries)`);
entries.forEach(({ path, version }) => console.log(` ${path} ${version}`));
});
});
The same example but using jora:
const fetchNodeModules = require('@discoveryjs/node-modules');
const jora = require('jora');
fetchNodeModules(__dirname).then(modules => {
const duplicates = jora(`
group(<name>).({ name: key, entries: value })
.[entries.size() > 1]
.sort(<entries.size()>)
.reverse()
`)(modules);
// output findings
duplicates.forEach(({ name, entries }) => {
console.log(`${name} (${entries.length} entries)`);
entries.forEach(({ path, version }) => console.log(` ${path} ${version}`));
});
});
Example of output in both cases:
ansi-regex (2 entries)
node_modules/ansi-regex 3.0.0
node_modules/strip-ansi/node_modules/ansi-regex 4.1.0
resolve-from (2 entries)
node_modules/resolve-from 5.0.0
node_modules/import-fresh/node_modules/resolve-from 4.0.0
...
MIT