UNPKG

dependency-cruiser

Version:

Validate and visualize dependencies. With your rules. JavaScript, TypeScript, CoffeeScript. ES6, CommonJS, AMD.

451 lines (436 loc) 14.6 kB
import { ICruiseOptions } from "./options"; import { IFlattenedRuleSet } from "./rule-set"; import { DependencyType, ModuleSystemType, ProtocolType } from "./shared-types"; import { IViolation } from "./violations"; import { IRuleSummary } from "./rule-summary"; import { IChange } from "watskeburt"; export interface IRevisionChange extends IChange { // optional because // - in content strategy 'ignored' and 'deleted' files can't have a checksum // - in metadata strategy we don't calculate a checksum because ~we're lazy~ // it's an unnecessary expense checksum?: string; } /** * caching revisionData. doc and proper naming will follow */ export interface IRevisionData { SHA1: string; changes: IRevisionChange[]; } export interface ICruiseResult { /** * A list of modules, with for each module the modules it depends upon */ modules: IModule[]; /** * A list of folders, as derived from the detected modules, with for each * folder a bunch of metrics (adapted from 'Agile software development: * principles, patterns, and practices' by Robert C Martin (ISBN 0-13-597444-5). * Note: these metrics substitute 'components' and 'classes' from that book * with 'folders' and 'modules'; the closest relatives that work for the most * programming styles in JavaScript (and its derivative languages). */ folders?: IFolder[]; /** * Data summarizing the found dependencies */ summary: ISummary; /** * caching revisionData. doc and proper naming will follow */ revisionData?: IRevisionData; } export interface IModule { /** * The (resolved) file name of the module, e.g. 'src/main/index.js' */ source: string; /** * 'true' if this module violated a rule; 'false' in all other cases. The violated rule will * be in the 'rule' object at the same level. */ valid: boolean; /** * list of modules this module depends on */ dependencies: IDependency[]; /** * list of modules that depend on this module (values are _resolved_ names of * those modules) */ dependents: string[]; /** * Whether or not this is a node.js core module */ coreModule?: boolean; /** * 'true' if dependency-cruiser could not resolve the module name in the source code to a * file name or core module. 'false' in all other cases. */ couldNotResolve?: boolean; /** * the type of inclusion - local, core, unknown (= we honestly don't know), undetermined (= * we didn't bother determining it) or one of the npm dependencies defined in a package.json * ('npm' for 'dependencies', 'npm-dev', 'npm-optional', 'npm-peer', 'npm-no-pkg' for * development, optional, peer dependencies and dependencies in node_modules but not in * package.json respectively) */ dependencyTypes?: DependencyType[]; /** * Whether or not this is a dependency that can be followed any further. This will be * 'false' for for core modules, json, modules that could not be resolved to a file and * modules that weren't followed because it matches the doNotFollow expression. */ followable?: boolean; /** * the license, if known (usually known for modules pulled from npm, not for local ones) */ license?: string; /** * 'true' if the file name of this module matches the 'doNotFollow' regular expression */ matchesDoNotFollow?: boolean; /** * 'true' if the file name of this module matches the 'focus' filter regular expression */ matchesFocus?: boolean; /** * 'true' if the file name of this module matches the 'reaches' filter regular expression */ matchesReaches?: boolean; /** * 'true' if the file name of this module matches the 'highlight' regular expression */ matchesHighlight?: boolean; /** * 'true' if this module does not have dependencies, and no module has it as a dependency */ orphan?: boolean; /** * An array of objects that tell whether this module is 'reachable', and according to rule * in which this reachability was defined */ reachable?: IReachable[]; /** * An array of objects that tell which other modules it reaches, * and that falls within the definition of the passed rule. */ reaches?: IReaches[]; /** * an array of rules violated by this module - left out if the module is valid */ rules?: IRuleSummary[]; /** * true if the module was 'consolidated'. Consolidating implies the * entity a Module represents might be several modules at the same time. * This attribute is set by tools that consolidate modules for reporting * purposes - it will not be present after a regular cruise. */ consolidated?: boolean; /** * "number of dependents/ (number of dependents + number of dependencies) * A measure for how stable the module is; ranging between 0 (completely * stable module) to 1 (completely instable module). Derived from Uncle * Bob's instability metric - but applied to a single module instead of * to a group of them. This attribute is only present when dependency-cruiser * was asked to calculate metrics. */ instability?: number; /** * checksum of the contents of the module. This attribute is currently only * available when the cruise was executed with caching and the cache strategy * is 'content'. */ checksum?: string; } export interface IDependency { /** * 'true' if following this dependency will ultimately return to the source, false in all * other cases */ circular: boolean; /** * Whether or not this is a node.js core module - deprecated in favor of dependencyType === * core */ coreModule: boolean; /** * 'true' if dependency-cruiser could not resolve the module name in the source code to a * file name or core module. 'false' in all other cases. */ couldNotResolve: boolean; /** * 'true' if the dependency between this dependency and its parent only * exists before compilation takes place. 'false in all other cases. * Dependency-cruiser will only specify this attribute for TypeScript and * then only when the option 'tsPreCompilationDeps' has the value 'specify'. */ preCompilationOnly?: boolean; /** * 'true' when the module included the module explicitly as type only with the * `type` keyword e.g. `import type { IThingus } from 'thing'`. Dependency-cruiser * will only specify this attribute for TypeScript and when the 'tsPreCompilationDeps' * option has either the value `true` or `"specify"`. */ typeOnly?: boolean; /** * If following this dependency will ultimately return to the source (circular === true), * this attribute will contain an (ordered) array of module names that shows (one of) the * circular path(s) */ cycle?: string[]; /** * the type of inclusion - local, core, unknown (= we honestly don't know), undetermined (= * we didn't bother determining it) or one of the npm dependencies defined in a package.json * ('npm' for 'dependencies', 'npm-dev', 'npm-optional', 'npm-peer', 'npm-no-pkg' for * development, optional, peer dependencies and dependencies in node_modules but not in * package.json respectively) */ dependencyTypes: DependencyType[]; /** * true if this dependency is dynamic, false in all other cases */ dynamic: boolean; /** * true if the dependency was defined by a require not named 'require' * false in all other cases */ exoticallyRequired: boolean; /** * If this dependency was defined by a require not named require (as defined in the * exoticRequireStrings option): the string that was used */ exoticRequire?: string; /** * Whether or not this is a dependency that can be followed any further. This will be * 'false' for for core modules, json, modules that could not be resolved to a file and * modules that weren't followed because it matches the doNotFollow expression. */ followable: boolean; /** * the license, if known (usually known for modules pulled from npm, not for local ones) */ license?: string; /** * 'true' if the file name of this module matches the doNotFollow regular expression */ matchesDoNotFollow?: boolean; /** * The name of the module as it appeared in the source code, e.g. './main' */ module: string; /** * If the module specification is an URI with a protocol in it (e.g. * `import * as fs from 'node:fs'` or * `import stuff from 'data:application/json,some-thing'`) - * this attribute holds the protocol part (e.g. 'node:', 'data:', 'file:'). * * Also see https://nodejs.org/api/esm.html#esm_urls */ protocol: ProtocolType; /** * If the module specification is an URI and contains a mime type, this * attribute holds the mime type (e.g. in `import stuff from 'data:application/json,some-thing' * `this would be data:application/json) * * Also see https://nodejs.org/api/esm.html#esm_urls */ mimeType: string; moduleSystem: ModuleSystemType; /** * The (resolved) file name of the module, e.g. 'src/main//index.js' */ resolved: string; /** * an array of rules violated by this dependency - left out if the dependency is valid */ rules?: IRuleSummary[]; /** * 'true' if this dependency violated a rule; 'false' in all other cases. The violated rule * will be in the 'rule' object at the same level. */ valid: boolean; /** * the (de-normalized) instability of the dependency - also available in * the module on the 'to' side of this dependency */ instability: number; } export interface IReachable { /** * The name of the rule where the reachability was defined */ asDefinedInRule: string; /** * The matchedFrom attribute shows what the 'from' module that causes the 'reachable' * information to be what it is. Sometimes the 'asDefinedInRule' is not specific enough - * e.g. when the from part can be many modules and/ or contains capturing groups used * in the to part of the rule. */ matchedFrom: string; /** * 'true' if this module is reachable from any of the modules matched by the from part of a * reachability-rule in 'asDefinedInRule', 'false' if not. */ value: boolean; } export interface IReachesModule { /** * The (resolved) file name of the module, e.g. 'src/main/index.js' */ source: string; /** * The path along which the 'to' module is reachable from this one. */ via: string[]; } export interface IReaches { /** * The name of the rule where the reachability was defined */ asDefinedInRule: string; /** * An array of modules that is (transitively) reachable from this module. */ modules: IReachesModule[]; } /** * Data summarizing the found dependencies */ export interface ISummary { /** * the number of errors in the dependencies */ error: number; /** * the number of ignored notices in the dependencies */ ignore: number; /** * the number of informational level notices in the dependencies */ info: number; optionsUsed: IOptions; /** * rules used in the cruise */ ruleSetUsed?: IFlattenedRuleSet; /** * the number of modules cruised */ totalCruised: number; /** * the number of dependencies cruised */ totalDependenciesCruised?: number; /** * A list of violations found in the dependencies. The dependencies themselves also contain * this information, this summary is here for convenience. */ violations: IViolation[]; /** * the number of warnings in the dependencies */ warn: number; } /** * the (command line) options used to generate the dependency-tree */ export interface IOptions extends ICruiseOptions { /** * arguments passed on the command line */ args?: string; /** * File the output was written to ('-' for stdout) */ outputTo?: string; /** * The rules file used to validate the dependencies (if any) */ rulesFile?: string; } export interface IFolderDependency { /** * the (resolved) name of the dependency */ name: string; /** * 'true' if this folder dependency violated a rule; 'false' in all other * cases. The violated rule will be in the 'rules' object at the same level. */ valid: boolean; /** * the instability of the dependency (de-normalized - this is a duplicate of * the one found in the instability of the folder with the same name) */ instability?: number; /** * 'true' if following this dependency will ultimately return to the source, false in all * other cases */ circular: boolean; /** * If following this dependency will ultimately return to the source (circular === true), * this attribute will contain an (ordered) array of module names that shows (one of) the * circular path(s) */ cycle?: string[]; /** * an array of rules violated by this dependency - left out if the dependency * is valid */ rules?: IRuleSummary[]; } export interface IFolderDependent { /** * name ('path') of the dependent */ name: string; } export interface IFolder { /** * The name of the folder. FOlder names are normalized to posix (so * separated by forward slashes e.g.: src/things/morethings) */ name: string; /** * List of folders depending on this folder */ dependents?: IFolderDependent[]; /** * List of folders this folder depends upon */ dependencies?: IFolderDependency[]; /** * The total number of modules detected in this folder and its sub-folders */ moduleCount: number; /** * The number of modules outside this folder that depend on modules * within this folder. Only present when dependency-cruiser was * "asked to calculate it. */ afferentCouplings?: number; /** * The number of modules inside this folder that depend on modules * outside this folder. Only present when dependency-cruiser was * asked to calculate it. */ efferentCouplings?: number; /** * efferentCouplings/ (afferentCouplings + efferentCouplings) * * A measure for how stable the folder is; ranging between 0 * (completely stable folder) to 1 (completely instable folder) * Note that while 'instability' has a negative connotation it's also * (unavoidable in any meaningful system. It's the basis of Martin's * variable component stability principle: 'the instability of a folder * should be larger than the folders it depends on'. Only present when * dependency-cruiser was asked to calculate it., */ instability?: number; } export * from "./violations"; export * from "./rule-summary";