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 ofnamefield inpackage.jsonversion– value ofversionfield inpackage.json; can benullfor rootpackage.jsondev– boolean value, whichtruewhen a package is using for dev purposes onlypath– relative tobasedirpath to a package. It can be used as an unique identifier of a package instance (anddeps.resolveduse the same values for a reference)entry- relative topathpath to an entry point module. It can benullwhen entry is not resolveddeps- list of entries fromdependencies,peerDependenciesandoptionalDependenciessections ofpackage.json.devDependenciesare included for rootpackage.jsononly. Each entry has following properties:type– one ofprod,peer,optionalordevname- 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,peeranddevdependencies, but not a problem foroptional).
packageJson- content of apackage.jsonparsed 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