dependency-cruiser
Version:
Validate and visualize dependencies. With your rules. JavaScript, TypeScript, CoffeeScript. ES6, CommonJS, AMD.
149 lines (131 loc) • 4.47 kB
JavaScript
;
const _ = require('lodash');
const pathToPosix = require('../utl/pathToPosix');
const extract = require('./extract');
const deriveCirculars = require('./derive/circular');
const deriveOrphans = require('./derive/orphan');
const gather = require('./gatherInitialSources');
const summarize = require('./summarize');
const addValidations = require('./addValidations');
/* eslint max-params:0 */
function extractRecursive (pFileName, pOptions, pVisited, pDepth, pResolveOptions) {
pVisited.add(pFileName);
const lDependencies = (pOptions.maxDepth <= 0 || pDepth < pOptions.maxDepth)
? extract(pFileName, pOptions, pResolveOptions)
: [];
return lDependencies
.filter(pDep => pDep.followable && !pDep.matchesDoNotFollow)
.reduce(
(pAll, pDep) => {
if (!pVisited.has(pDep.resolved)){
return pAll.concat(
extractRecursive(pDep.resolved, pOptions, pVisited, pDepth + 1, pResolveOptions)
);
}
return pAll;
}, [{
source: pathToPosix(pFileName),
dependencies: lDependencies
}]
);
}
function extractFileDirArray(pFileDirArray, pOptions, pResolveOptions) {
let lVisited = new Set();
return _.spread(_.concat)(
gather(pFileDirArray, pOptions)
.reduce((pDependencies, pFilename) => {
if (!lVisited.has(pFilename)){
lVisited.add(pFilename);
return pDependencies.concat(
extractRecursive(pFilename, pOptions, lVisited, 0, pResolveOptions)
);
}
return pDependencies;
}, [])
);
}
function isNotFollowable(pToDependency) {
return !(pToDependency.followable);
}
function notInFromListAlready(pFromList) {
return pToListItem =>
!pFromList.some(pFromListItem => pFromListItem.source === pToListItem.resolved);
}
function toDependencyToSource(pToListItem) {
return {
source : pToListItem.resolved,
followable : pToListItem.followable,
coreModule : pToListItem.coreModule,
couldNotResolve : pToListItem.couldNotResolve,
matchesDoNotFollow: pToListItem.matchesDoNotFollow,
dependencyTypes : pToListItem.dependencyTypes,
dependencies : []
};
}
function complete(pAll, pFromListItem) {
return pAll
.concat(pFromListItem)
.concat(
pFromListItem.dependencies
.filter(isNotFollowable)
.filter(notInFromListAlready(pAll))
.map(toDependencyToSource)
);
}
function makeOptionsPresentable(pOptions) {
const SHARABLE_OPTIONS = [
"rulesFile",
"outputTo",
"doNotFollow",
"exclude",
"maxDepth",
"moduleSystems",
"outputType",
"prefix",
"tsPreCompilationDeps",
"preserveSymlinks",
"webpackConfig"
];
return SHARABLE_OPTIONS
.filter(pOption => pOptions.hasOwnProperty(pOption) && pOptions[pOption] !== 0)
.reduce(
(pAll, pOption) => {
pAll[pOption] = pOptions[pOption];
return pAll;
},
{}
);
}
module.exports = (pFileDirArray, pOptions, pCallback, pResolveOptions) => {
const lCallback = pCallback ? pCallback : (pInput => pInput);
const lOptions = Object.assign(
{
maxDepth: 0
},
pOptions
);
let lModules = _(
extractFileDirArray(pFileDirArray, lOptions, pResolveOptions).reduce(complete, [])
).uniqBy(pDependency => pDependency.source)
.value();
lModules = deriveCirculars(lModules, lOptions);
lModules = deriveOrphans(lModules, lOptions);
lModules = addValidations(
lModules,
lOptions.validate,
lOptions.ruleSet
);
return lCallback(
{
modules : lModules,
summary :
Object.assign(
summarize(lModules),
{
optionsUsed: makeOptionsPresentable(lOptions)
}
)
}
);
};
/* eslint security/detect-object-injection: 0 */