dependency-cruiser
Version:
Validate and visualize dependencies. With your rules. JavaScript, TypeScript, CoffeeScript. ES6, CommonJS, AMD.
141 lines (130 loc) • 3.34 kB
JavaScript
import isSameViolation from "./is-same-violation.mjs";
import { findRuleByName } from "#graph-utl/rule-set.mjs";
import { compareViolations } from "#graph-utl/compare.mjs";
import { uniqWith } from "#utl/array-util.mjs";
function cutNonTransgressions(pModule) {
return {
...pModule,
dependencies: pModule.dependencies.filter(
(pDependency) => pDependency.valid === false,
),
};
}
function toDependencyViolationSummary(pRule, pModule, pDependency, pRuleSet) {
let lReturnValue = {
type: "dependency",
from: pModule.source,
to: pDependency.resolved,
rule: pRule,
};
if (
Object.hasOwn(pDependency, "cycle") &&
findRuleByName(pRuleSet, pRule.name)?.to?.circular
) {
lReturnValue = {
...lReturnValue,
type: "cycle",
cycle: pDependency.cycle,
};
}
if (
Object.hasOwn(pModule, "instability") &&
Object.hasOwn(pDependency, "instability") &&
Object.hasOwn(
findRuleByName(pRuleSet, pRule.name)?.to ?? {},
"moreUnstable",
)
) {
lReturnValue = {
...lReturnValue,
type: "instability",
metrics: {
from: { instability: pModule.instability },
to: { instability: pDependency.instability },
},
};
}
return lReturnValue;
}
/**
* Takes an array of dependencies, and extracts the violations from it.
*
* Each violation has a from a to and the violated rule () e.g.
* {
* from: "./here.js",
* to: "./there.js",
* rule: {
* name: "some-rule",
* severity: "warn"
* }
* }
*
* @param {any} pModules an array of modules
* @param {any} pRuleSet? a rule set
* @return {any} an array of violations
*/
function extractDependencyViolations(pModules, pRuleSet) {
return pModules
.map(cutNonTransgressions)
.filter((pModule) => pModule.dependencies.length > 0)
.map((pModule) =>
pModule.dependencies.map((pDependency) =>
pDependency.rules.map((pRule) =>
toDependencyViolationSummary(pRule, pModule, pDependency, pRuleSet),
),
),
)
.flat(Infinity);
}
function toModuleViolationSummary(pRule, pModule, pRuleSet) {
let lReturnValue = [
{ type: "module", from: pModule.source, to: pModule.source, rule: pRule },
];
if (pModule.reaches && findRuleByName(pRuleSet, pRule.name)?.to?.reachable) {
lReturnValue = pModule.reaches
.filter((pReachable) => pReachable.asDefinedInRule === pRule.name)
.reduce(
(pAll, pReachable) =>
pAll.concat(
pReachable.modules.map((pReachableModule) => ({
to: pReachableModule.source,
via: pReachableModule.via,
})),
),
[],
)
.map((pToModule) => ({
type: "reachability",
from: pModule.source,
to: pToModule.to,
rule: pRule,
via: pToModule.via,
}));
}
return lReturnValue;
}
function extractModuleViolations(pModules, pRuleSet) {
return pModules
.filter((pModule) => pModule.valid === false)
.reduce(
(pAllModules, pModule) =>
pAllModules.concat(
pModule.rules.reduce(
(pAllRules, pRule) =>
pAllRules.concat(
toModuleViolationSummary(pRule, pModule, pRuleSet),
),
[],
),
),
[],
);
}
export default function summarizeModules(pModules, pRuleSet) {
return uniqWith(
extractDependencyViolations(pModules, pRuleSet)
.concat(extractModuleViolations(pModules, pRuleSet))
.sort(compareViolations),
isSameViolation,
);
}