dependency-cruiser 10.8.0 → 11.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +1 -1
  2. package/package.json +22 -16
  3. package/src/enrich/clear-caches.js +1 -1
  4. package/src/enrich/derive/circular/get-cycle.js +1 -1
  5. package/src/enrich/derive/dependents/get-dependents.js +1 -1
  6. package/src/enrich/derive/{metrics/get-stability-metrics.js → folders/aggregate-to-folders.js} +39 -30
  7. package/src/enrich/derive/folders/index.js +9 -0
  8. package/src/enrich/derive/folders/utl.js +44 -0
  9. package/src/enrich/derive/metrics/get-module-metrics.js +39 -0
  10. package/src/enrich/derive/metrics/index.js +14 -0
  11. package/src/enrich/derive/{utl.js → module-utl.js} +28 -0
  12. package/src/enrich/derive/orphan/is-orphan.js +1 -1
  13. package/src/enrich/derive/reachable/get-path.js +1 -1
  14. package/src/enrich/derive/reachable/index.js +1 -1
  15. package/src/enrich/enrich-modules.js +2 -2
  16. package/src/enrich/index.js +2 -2
  17. package/src/enrich/summarize/summarize-modules.js +24 -5
  18. package/src/extract/get-dependencies.js +11 -6
  19. package/src/extract/parse/to-typescript-ast.js +24 -8
  20. package/src/extract/utl/get-extension.js +9 -5
  21. package/src/main/options/normalize.js +28 -0
  22. package/src/meta.js +1 -1
  23. package/src/report/dot/dot.template.js +1 -1
  24. package/src/report/dot/index.js +7 -2
  25. package/src/report/dot/module-utl.js +47 -19
  26. package/src/report/dot/prepare-custom-level.js +2 -2
  27. package/src/report/dot/prepare-flat-level.js +2 -2
  28. package/src/report/dot/prepare-folder-level.js +2 -2
  29. package/src/report/error-html/error-html.template.js +1 -1
  30. package/src/report/error-html/utl.js +50 -7
  31. package/src/report/error.js +55 -21
  32. package/src/report/teamcity.js +37 -11
  33. package/src/report/utl/index.js +16 -0
  34. package/src/schema/baseline-violations.schema.js +1 -35
  35. package/src/schema/configuration.schema.js +1 -493
  36. package/src/schema/cruise-result.schema.js +1 -642
  37. package/src/utl/regex-util.js +57 -0
  38. package/src/validate/match-dependency-rule.js +19 -32
  39. package/src/validate/match-module-rule.js +1 -1
  40. package/src/validate/matchers.js +58 -48
  41. package/src/validate/violates-required-rule.js +1 -1
  42. package/types/baseline-violations.d.ts +1 -1
  43. package/types/cruise-result.d.ts +11 -50
  44. package/types/options.d.ts +5 -0
  45. package/types/reporter-options.d.ts +6 -0
  46. package/types/restrictions.d.ts +12 -0
  47. package/types/rule-summary.d.ts +23 -0
  48. package/types/shared-types.d.ts +7 -0
  49. package/types/violations.d.ts +43 -0
  50. package/src/enrich/derive/metrics/folder.js +0 -9
  51. package/src/enrich/derive/metrics/module-utl.js +0 -27
  52. package/src/enrich/derive/metrics/module.js +0 -29
  53. package/src/enrich/derive/metrics/utl.js +0 -28
  54. package/src/validate/utl.js +0 -31
package/README.md CHANGED
@@ -21,7 +21,7 @@ you can stick on the wall to impress your grandma.
21
21
  ### Install it
22
22
 
23
23
  - `npm install --save-dev dependency-cruiser` to use it as a validator in your project (recommended) or...
24
- - `npm install --global dependency-cruiser` if you just want to to inspect multiple projects.
24
+ - `npm install --global dependency-cruiser` if you just want to inspect multiple projects.
25
25
 
26
26
  ### Show stuff to your grandma
27
27
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dependency-cruiser",
3
- "version": "10.8.0",
3
+ "version": "11.2.0",
4
4
  "description": "Validate and visualize dependencies. With your rules. JavaScript, TypeScript, CoffeeScript. ES6, CommonJS, AMD.",
5
5
  "keywords": [
6
6
  "static analysis",
@@ -76,7 +76,6 @@
76
76
  "depcruise:all": "node ./bin/dependency-cruise.js src bin test configs types tools --config",
77
77
  "depcruise:baseline": "node ./bin/depcruise-baseline.js src bin test configs types tools",
78
78
  "depcruise:explain": "node ./bin/dependency-cruise.js src bin test configs types tools --output-type err-long --config --progress none",
79
- "depcruise:graph:dev": "node ./bin/dependency-cruise.js bin src --prefix vscode://file/$(pwd)/ --config --output-type dot --progress cli-feedback | dot -T svg | node ./bin/wrap-stream-in-html.js | browser",
80
79
  "depcruise:graph:doc": "npm-run-all depcruise:graph:doc:json --parallel depcruise:graph:doc:fmt-* depcruise:graph:doc:samples",
81
80
  "depcruise:graph:doc:json": "node ./bin/dependency-cruise.js bin src test --config --output-type json --output-to tmp_graph_deps.json --progress",
82
81
  "depcruise:graph:doc:fmt-detail": "./bin/depcruise-fmt.js -T dot -f - tmp_graph_deps.json | dot -T svg | tee doc/real-world-samples/dependency-cruiser-without-node_modules.svg | node bin/wrap-stream-in-html.js > docs/dependency-cruiser-dependency-graph.html",
@@ -88,7 +87,9 @@
88
87
  "depcruise:graph:dot": "node ./bin/dependency-cruise.js bin src --config --output-type dot | dot -T svg > tmp_deps.svg",
89
88
  "depcruise:graph:fdp": "node ./bin/dependency-cruise.js bin src --config --output-type dot | fdp -GK=0.1 -Gsplines=true -T svg > tmp_deps.svg",
90
89
  "depcruise:graph:osage": "node ./bin/dependency-cruise.js bin src --config --output-type dot | osage -Gpack=32 -GpackMode=array2 -T svg > tmp_deps.svg",
91
- "depcruise:report": "node ./bin/dependency-cruise.js src bin test configs types --output-type err-html --config --output-to dependency-violations.html",
90
+ "depcruise:graph:view": "node ./bin/dependency-cruise.js bin src --prefix vscode://file/$(pwd)/ --config configs/.dependency-cruiser-show-metrics-config.json --output-type dot --progress cli-feedback | dot -T svg | node ./bin/wrap-stream-in-html.js | browser",
91
+ "depcruise:report": "node ./bin/dependency-cruise.js src bin test configs types --output-type err-html --config configs/.dependency-cruiser-show-metrics-config.json --output-to dependency-violations.html",
92
+ "depcruise:report:view": "node ./bin/dependency-cruise.js src bin test configs types --output-type err-html --config configs/.dependency-cruiser-show-metrics-config.json --output-to - | browser",
92
93
  "depcruise:focus": "node ./bin/dependency-cruise.js src bin test configs types --progress --config --output-type text --focus",
93
94
  "lint": "npm-run-all --parallel --aggregate-output lint:eslint lint:prettier:check lint:types",
94
95
  "lint:eslint": "eslint bin/dependency-cruise.js src test configs tools/**/*.mjs --cache --cache-location .cache/eslint/",
@@ -151,18 +152,18 @@
151
152
  "safe-regex": "2.1.1",
152
153
  "semver": "^7.3.5",
153
154
  "semver-try-require": "^5.0.1",
154
- "teamcity-service-messages": "0.1.11",
155
+ "teamcity-service-messages": "0.1.12",
155
156
  "tsconfig-paths-webpack-plugin": "3.5.2",
156
157
  "wrap-ansi": "^7.0.0"
157
158
  },
158
159
  "devDependencies": {
159
- "@babel/core": "7.16.0",
160
- "@babel/plugin-transform-modules-commonjs": "7.16.0",
161
- "@babel/preset-typescript": "7.16.0",
162
- "@swc/core": "1.2.113",
163
- "@typescript-eslint/eslint-plugin": "5.4.0",
164
- "@typescript-eslint/parser": "5.4.0",
165
- "@vue/compiler-sfc": "3.2.23",
160
+ "@babel/core": "7.16.5",
161
+ "@babel/plugin-transform-modules-commonjs": "7.16.5",
162
+ "@babel/preset-typescript": "7.16.5",
163
+ "@swc/core": "1.2.123",
164
+ "@typescript-eslint/eslint-plugin": "5.8.0",
165
+ "@typescript-eslint/parser": "5.8.0",
166
+ "@vue/compiler-sfc": "3.2.26",
166
167
  "c8": "7.10.0",
167
168
  "chai": "4.3.4",
168
169
  "chai-json-schema": "1.5.1",
@@ -172,22 +173,22 @@
172
173
  "eslint-config-prettier": "8.3.0",
173
174
  "eslint-plugin-budapestian": "3.0.1",
174
175
  "eslint-plugin-import": "2.25.3",
175
- "eslint-plugin-mocha": "9.0.0",
176
+ "eslint-plugin-mocha": "^9.0.0",
176
177
  "eslint-plugin-node": "11.1.0",
177
178
  "eslint-plugin-security": "1.4.0",
178
179
  "eslint-plugin-unicorn": "39.0.0",
179
180
  "husky": "^4.3.8",
180
181
  "intercept-stdout": "0.1.2",
181
- "lint-staged": "12.1.2",
182
+ "lint-staged": "12.1.4",
182
183
  "mocha": "9.1.3",
183
184
  "normalize-newline": "^3.0.0",
184
185
  "npm-run-all": "4.1.5",
185
- "prettier": "2.5.0",
186
+ "prettier": "2.5.1",
186
187
  "proxyquire": "2.1.3",
187
188
  "shx": "0.3.3",
188
- "svelte": "3.44.2",
189
+ "svelte": "3.44.3",
189
190
  "symlink-dir": "5.0.1",
190
- "typescript": "4.5.2",
191
+ "typescript": "4.5.4",
191
192
  "upem": "^7.0.0",
192
193
  "vue-template-compiler": "2.6.14",
193
194
  "yarn": "1.22.17"
@@ -219,6 +220,11 @@
219
220
  "policy": "wanted",
220
221
  "because": "version 5 only exports ejs - and we use cjs and don't transpile"
221
222
  },
223
+ {
224
+ "package": "eslint-plugin-mocha",
225
+ "policy": "wanted",
226
+ "because": "version 10 dropped support for node 12, which we still do support"
227
+ },
222
228
  {
223
229
  "package": "normalize-newline",
224
230
  "policy": "wanted",
@@ -1,4 +1,4 @@
1
- const utl = require("./derive/utl");
1
+ const utl = require("./derive/module-utl");
2
2
 
3
3
  module.exports = function clearCaches() {
4
4
  utl.clearCache();
@@ -1,4 +1,4 @@
1
- const { findModuleByName } = require("../utl");
1
+ const { findModuleByName } = require("../module-utl");
2
2
  /* about the absence of checks whether attributes/ objects actually
3
3
  * exist:
4
4
  * - it saves CPU cycles to the effect of being ~30% faster than with the
@@ -1,4 +1,4 @@
1
- const { isDependent } = require("../utl");
1
+ const { isDependent } = require("../module-utl");
2
2
 
3
3
  module.exports = function getDependents(pModule, pModules) {
4
4
  // perf between O(n) in an unconnected graph and O(n^2) in a fully connected one
@@ -1,11 +1,12 @@
1
1
  /* eslint-disable security/detect-object-injection */
2
2
  const path = require("path").posix;
3
- const { foldersObject2folderArray, getParentFolders } = require("./utl");
3
+ const { calculateInstability, metricsAreCalculable } = require("../module-utl");
4
4
  const {
5
5
  getAfferentCouplings,
6
6
  getEfferentCouplings,
7
- metricsAreCalculable,
8
- } = require("./module-utl");
7
+ getParentFolders,
8
+ object2Array,
9
+ } = require("./utl");
9
10
 
10
11
  function upsertCouplings(pAllDependents, pNewDependents) {
11
12
  pNewDependents.forEach((pNewDependent) => {
@@ -34,16 +35,14 @@ function upsertFolderAttributes(pAllMetrics, pModule, pDirname) {
34
35
  )
35
36
  );
36
37
  pAllMetrics[pDirname].moduleCount += 1;
37
-
38
38
  return pAllMetrics;
39
39
  }
40
40
 
41
- function orderFolderMetrics(pLeftMetric, pRightMetric) {
42
- // return pLeft.name.localeCompare(pRight.name);
43
- // For intended use in a table it's probably more useful to sort by
44
- // instability. Might need to be either configurable or flexible
45
- // in the output, though
46
- return pRightMetric.instability - pLeftMetric.instability;
41
+ function aggregateToFolder(pAllFolders, pModule) {
42
+ getParentFolders(path.dirname(pModule.source)).forEach((pParentDirectory) =>
43
+ upsertFolderAttributes(pAllFolders, pModule, pParentDirectory)
44
+ );
45
+ return pAllFolders;
47
46
  }
48
47
 
49
48
  function sumCounts(pAll, pCurrent) {
@@ -59,12 +58,15 @@ function getFolderLevelCouplings(pCouplingArray) {
59
58
  : path.dirname(pCoupling.name)
60
59
  )
61
60
  )
62
- );
61
+ ).map((pCoupling) => ({ name: pCoupling }));
63
62
  }
64
63
 
65
64
  function calculateFolderMetrics(pFolder) {
66
- const lModuleDependents = foldersObject2folderArray(pFolder.dependents);
67
- const lModuleDependencies = foldersObject2folderArray(pFolder.dependencies);
65
+ const lModuleDependents = object2Array(pFolder.dependents);
66
+ const lModuleDependencies = object2Array(pFolder.dependencies);
67
+ // this calculation might look superfluous (why not just .length the dependents
68
+ // and dependencies?), but it isn't because there can be > 1 relation between
69
+ // two folders
68
70
  const lAfferentCouplings = lModuleDependents.reduce(sumCounts, 0);
69
71
  const lEfferentCouplings = lModuleDependencies.reduce(sumCounts, 0);
70
72
 
@@ -72,26 +74,33 @@ function calculateFolderMetrics(pFolder) {
72
74
  ...pFolder,
73
75
  afferentCouplings: lAfferentCouplings,
74
76
  efferentCouplings: lEfferentCouplings,
75
- // when both afferentCouplings and efferentCouplings equal 0 instability will
76
- // yield NaN. Judging Bob Martin's intention, a component with no outgoing
77
- // dependencies is maximum stable (0)
78
- instability:
79
- lEfferentCouplings / (lEfferentCouplings + lAfferentCouplings) || 0,
77
+ instability: calculateInstability(lEfferentCouplings, lAfferentCouplings),
80
78
  dependents: getFolderLevelCouplings(lModuleDependents),
81
79
  dependencies: getFolderLevelCouplings(lModuleDependencies),
82
80
  };
83
81
  }
84
82
 
85
- module.exports = function getStabilityMetrics(pModules) {
86
- return foldersObject2folderArray(
87
- pModules.filter(metricsAreCalculable).reduce((pAllFolders, pModule) => {
88
- getParentFolders(path.dirname(pModule.source)).forEach(
89
- (pParentDirectory) =>
90
- upsertFolderAttributes(pAllFolders, pModule, pParentDirectory)
91
- );
92
- return pAllFolders;
93
- }, {})
94
- )
95
- .map(calculateFolderMetrics)
96
- .sort(orderFolderMetrics);
83
+ function findFolderByName(pAllFolders, pName) {
84
+ return pAllFolders.find((pFolder) => pFolder.name === pName);
85
+ }
86
+
87
+ function denormalizeInstability(pAllFolders) {
88
+ return (pFolder) => ({
89
+ ...pFolder,
90
+ dependencies: pFolder.dependencies.map((pDependency) => {
91
+ const lFolder = findFolderByName(pAllFolders, pDependency.name) || {};
92
+ return {
93
+ ...pDependency,
94
+ instability: lFolder.instability >= 0 ? lFolder.instability : 0,
95
+ };
96
+ }),
97
+ });
98
+ }
99
+
100
+ module.exports = function aggregateToFolders(pModules) {
101
+ const lFolders = object2Array(
102
+ pModules.filter(metricsAreCalculable).reduce(aggregateToFolder, {})
103
+ ).map(calculateFolderMetrics);
104
+
105
+ return lFolders.map(denormalizeInstability(lFolders));
97
106
  };
@@ -0,0 +1,9 @@
1
+ const aggregateToFolders = require("./aggregate-to-folders");
2
+
3
+ module.exports = function deriveFolderMetrics(pModules, pOptions) {
4
+ let lReturnValue = {};
5
+ if (pOptions.metrics) {
6
+ lReturnValue = { folders: aggregateToFolders(pModules) };
7
+ }
8
+ return lReturnValue;
9
+ };
@@ -0,0 +1,44 @@
1
+ /* eslint-disable security/detect-object-injection */
2
+ const path = require("path").posix;
3
+
4
+ function getAfferentCouplings(pModule, pDirname) {
5
+ return pModule.dependents.filter(
6
+ (pDependent) => !pDependent.startsWith(pDirname.concat(path.sep))
7
+ );
8
+ }
9
+
10
+ function getEfferentCouplings(pModule, pDirname) {
11
+ return pModule.dependencies.filter(
12
+ (pDependency) => !pDependency.resolved.startsWith(pDirname.concat(path.sep))
13
+ );
14
+ }
15
+
16
+ /**
17
+ *
18
+ * @param {string} pPath
19
+ * @returns string[]
20
+ */
21
+ function getParentFolders(pPath) {
22
+ let lFragments = pPath.split("/");
23
+ let lReturnValue = [];
24
+
25
+ while (lFragments.length > 0) {
26
+ lReturnValue.push(lFragments.join("/"));
27
+ lFragments.pop();
28
+ }
29
+ return lReturnValue.reverse();
30
+ }
31
+
32
+ function object2Array(pObject) {
33
+ return Object.keys(pObject).map((pKey) => ({
34
+ name: pKey,
35
+ ...pObject[pKey],
36
+ }));
37
+ }
38
+
39
+ module.exports = {
40
+ getAfferentCouplings,
41
+ getEfferentCouplings,
42
+ getParentFolders,
43
+ object2Array,
44
+ };
@@ -0,0 +1,39 @@
1
+ const { findModuleByName } = require("../module-utl");
2
+ const { calculateInstability, metricsAreCalculable } = require("../module-utl");
3
+
4
+ function addInstabilityMetric(pModule) {
5
+ return {
6
+ ...pModule,
7
+ ...(metricsAreCalculable(pModule)
8
+ ? {
9
+ instability: calculateInstability(
10
+ pModule.dependencies.length,
11
+ pModule.dependents.length
12
+ ),
13
+ }
14
+ : {}),
15
+ };
16
+ }
17
+
18
+ function addInstabilityToDependency(pAllModules) {
19
+ return (pDependency) => ({
20
+ ...pDependency,
21
+ instability:
22
+ (findModuleByName(pAllModules, pDependency.resolved) || {}).instability ||
23
+ 0,
24
+ });
25
+ }
26
+
27
+ function deNormalizeInstabilityMetricsToDependencies(pModule, _, pAllModules) {
28
+ return {
29
+ ...pModule,
30
+ dependencies: pModule.dependencies.map(
31
+ addInstabilityToDependency(pAllModules)
32
+ ),
33
+ };
34
+ }
35
+
36
+ module.exports = {
37
+ addInstabilityMetric,
38
+ deNormalizeInstabilityMetricsToDependencies,
39
+ };
@@ -0,0 +1,14 @@
1
+ const { clearCache } = require("../module-utl");
2
+ const {
3
+ addInstabilityMetric,
4
+ deNormalizeInstabilityMetricsToDependencies,
5
+ } = require("./get-module-metrics");
6
+
7
+ module.exports = function deriveModulesMetrics(pModules, pOptions) {
8
+ if (pOptions.metrics) {
9
+ const lModules = pModules.map(addInstabilityMetric);
10
+ clearCache();
11
+ return lModules.map(deNormalizeInstabilityMetricsToDependencies);
12
+ }
13
+ return pModules;
14
+ };
@@ -28,6 +28,32 @@ function isDependent(pResolvedName) {
28
28
  );
29
29
  }
30
30
 
31
+ function metricsAreCalculable(pModule) {
32
+ return (
33
+ !pModule.coreModule &&
34
+ !pModule.couldNotResolve &&
35
+ !pModule.matchesDoNotFollow
36
+ );
37
+ }
38
+
39
+ /**
40
+ * returns the Instability of a component given the number of incoming (afferent)
41
+ * and outgoign (efferent) connections ('couplings')
42
+ *
43
+ * @param {number} pEfferentCouplingCount
44
+ * @param {number} pAfferentCouplingCount
45
+ * @returns number
46
+ */
47
+ function calculateInstability(pEfferentCouplingCount, pAfferentCouplingCount) {
48
+ // when both afferentCouplings and efferentCouplings equal 0 instability will
49
+ // yield NaN. Judging Bob Martin's intention, a component with no outgoing
50
+ // dependencies is maximum stable (0)
51
+ return (
52
+ pEfferentCouplingCount /
53
+ (pEfferentCouplingCount + pAfferentCouplingCount) || 0
54
+ );
55
+ }
56
+
31
57
  function clearCache() {
32
58
  gIndexedGraph = null;
33
59
  }
@@ -36,4 +62,6 @@ module.exports = {
36
62
  findModuleByName,
37
63
  clearCache,
38
64
  isDependent,
65
+ metricsAreCalculable,
66
+ calculateInstability,
39
67
  };
@@ -1,4 +1,4 @@
1
- const { isDependent } = require("../utl");
1
+ const { isDependent } = require("../module-utl");
2
2
 
3
3
  module.exports = (pModule, pGraph) => {
4
4
  if (pModule.dependencies.length > 0) {
@@ -1,4 +1,4 @@
1
- const { findModuleByName } = require("../utl");
1
+ const { findModuleByName } = require("../module-utl");
2
2
 
3
3
  function getPath(pGraph, pFrom, pTo, pVisited = new Set()) {
4
4
  let lReturnValue = [];
@@ -4,7 +4,7 @@ const _clone = require("lodash/clone");
4
4
  const _get = require("lodash/get");
5
5
  const _has = require("lodash/has");
6
6
  const matchers = require("../../../validate/matchers");
7
- const { extractGroups } = require("../../../validate/utl");
7
+ const { extractGroups } = require("../../../utl/regex-util");
8
8
  const getPath = require("./get-path");
9
9
 
10
10
  function getReachableRules(pRuleSet) {
@@ -8,7 +8,7 @@ const addDependents = require("./derive/dependents");
8
8
  const deriveReachable = require("./derive/reachable");
9
9
  const addValidations = require("./add-validations");
10
10
  const softenKnownViolations = require("./soften-known-violations");
11
- const deriveModuleMetrics = require("./derive/metrics/module");
11
+ const deriveModuleMetrics = require("./derive/metrics");
12
12
 
13
13
  module.exports = function enrichModules(pModules, pOptions) {
14
14
  bus.emit("progress", "analyzing: cycles", { level: busLogLevels.INFO });
@@ -19,7 +19,7 @@ module.exports = function enrichModules(pModules, pOptions) {
19
19
  lModules = deriveOrphans(lModules);
20
20
  bus.emit("progress", "analyzing: reachables", { level: busLogLevels.INFO });
21
21
  lModules = deriveReachable(lModules, pOptions.ruleSet);
22
- bus.emit("progress", "analyzing: calculating module metrics", {
22
+ bus.emit("progress", "analyzing: module metrics", {
23
23
  level: busLogLevels.INFO,
24
24
  });
25
25
  lModules = deriveModuleMetrics(lModules, pOptions);
@@ -1,5 +1,5 @@
1
1
  const enrichModules = require("./enrich-modules");
2
- const deriveFolderMetrics = require("./derive/metrics/folder.js");
2
+ const aggregateToFolders = require("./derive/folders");
3
3
  const summarize = require("./summarize");
4
4
  const clearCaches = require("./clear-caches");
5
5
 
@@ -10,7 +10,7 @@ module.exports = function enrich(pModules, pOptions, pFileAndDirectoryArray) {
10
10
  clearCaches();
11
11
  return {
12
12
  modules: lModules,
13
- ...deriveFolderMetrics(lModules, pOptions),
13
+ ...aggregateToFolders(lModules, pOptions),
14
14
  summary: summarize(lModules, pOptions, pFileAndDirectoryArray),
15
15
  };
16
16
  };
@@ -1,14 +1,15 @@
1
1
  const _flattenDeep = require("lodash/flattenDeep");
2
2
  const _get = require("lodash/get");
3
+ const _has = require("lodash/has");
3
4
  const _uniqWith = require("lodash/uniqWith");
4
5
  const { findRuleByName } = require("../../graph-utl/rule-set");
5
6
  const compare = require("../../graph-utl/compare");
6
7
  const isSameViolation = require("./is-same-violation");
7
8
 
8
- function cutNonTransgressions(pSourceEntry) {
9
+ function cutNonTransgressions(pModule) {
9
10
  return {
10
- source: pSourceEntry.source,
11
- dependencies: pSourceEntry.dependencies.filter(
11
+ ...pModule,
12
+ dependencies: pModule.dependencies.filter(
12
13
  (pDependency) => pDependency.valid === false
13
14
  ),
14
15
  };
@@ -30,21 +31,38 @@ function extractMetaData(pViolations) {
30
31
  }
31
32
  function toDependencyViolationSummary(pRule, pModule, pDependency, pRuleSet) {
32
33
  let lReturnValue = {
34
+ type: "dependency",
33
35
  from: pModule.source,
34
36
  to: pDependency.resolved,
35
37
  rule: pRule,
36
38
  };
37
39
 
38
40
  if (
39
- pDependency.cycle &&
41
+ _has(pDependency, "cycle") &&
40
42
  _get(findRuleByName(pRuleSet, pRule.name), "to.circular")
41
43
  ) {
42
44
  lReturnValue = {
43
45
  ...lReturnValue,
46
+ type: "cycle",
44
47
  cycle: pDependency.cycle,
45
48
  };
46
49
  }
47
50
 
51
+ if (
52
+ _has(pModule, "instability") &&
53
+ _has(pDependency, "instability") &&
54
+ _has(findRuleByName(pRuleSet, pRule.name), "to.moreUnstable")
55
+ ) {
56
+ lReturnValue = {
57
+ ...lReturnValue,
58
+ type: "instability",
59
+ metrics: {
60
+ from: { instability: pModule.instability },
61
+ to: { instability: pDependency.instability },
62
+ },
63
+ };
64
+ }
65
+
48
66
  return lReturnValue;
49
67
  }
50
68
 
@@ -82,7 +100,7 @@ function extractDependencyViolations(pModules, pRuleSet) {
82
100
 
83
101
  function toModuleViolationSummary(pRule, pModule, pRuleSet) {
84
102
  let lReturnValue = [
85
- { from: pModule.source, to: pModule.source, rule: pRule },
103
+ { type: "module", from: pModule.source, to: pModule.source, rule: pRule },
86
104
  ];
87
105
  if (
88
106
  pModule.reaches &&
@@ -101,6 +119,7 @@ function toModuleViolationSummary(pRule, pModule, pRuleSet) {
101
119
  []
102
120
  )
103
121
  .map((pToModule) => ({
122
+ type: "reachability",
104
123
  from: pModule.source,
105
124
  to: pToModule.to,
106
125
  rule: pRule,
@@ -21,15 +21,18 @@ function extractFromSwcAST(pOptions, pFileName) {
21
21
  );
22
22
  }
23
23
 
24
- function extractFromTypeScriptAST(pOptions, pFileName) {
24
+ function extractFromTypeScriptAST(pOptions, pFileName, pTranspileOptions) {
25
25
  return extractTypeScriptDeps(
26
- toTypescriptAST.getASTCached(path.join(pOptions.baseDir, pFileName)),
26
+ toTypescriptAST.getASTCached(
27
+ path.join(pOptions.baseDir, pFileName),
28
+ pTranspileOptions
29
+ ),
27
30
  pOptions.exoticRequireStrings
28
31
  );
29
32
  }
30
33
 
31
34
  function isTypeScriptCompatible(pFileName) {
32
- return [".ts", ".tsx", ".js", ".mjs", ".cjs"].includes(
35
+ return [".ts", ".tsx", ".js", ".mjs", ".cjs", ".vue"].includes(
33
36
  path.extname(pFileName)
34
37
  );
35
38
  }
@@ -88,9 +91,11 @@ function extractWithTsc(
88
91
  pFileName,
89
92
  pTranspileOptions
90
93
  ) {
91
- pDependencies = extractFromTypeScriptAST(pCruiseOptions, pFileName).filter(
92
- (pDep) => pCruiseOptions.moduleSystems.includes(pDep.moduleSystem)
93
- );
94
+ pDependencies = extractFromTypeScriptAST(
95
+ pCruiseOptions,
96
+ pFileName,
97
+ pTranspileOptions
98
+ ).filter((pDep) => pCruiseOptions.moduleSystems.includes(pDep.moduleSystem));
94
99
 
95
100
  if (pCruiseOptions.tsPreCompilationDeps === "specify") {
96
101
  pDependencies = detectPreCompilationNess(
@@ -2,21 +2,28 @@ const fs = require("fs");
2
2
  const tryRequire = require("semver-try-require");
3
3
  const _memoize = require("lodash/memoize");
4
4
  const { supportedTranspilers } = require("../../../src/meta.js");
5
+ const transpile = require("../transpile");
6
+ const getExtension = require("../utl/get-extension");
5
7
 
6
8
  const typescript = tryRequire("typescript", supportedTranspilers.typescript);
7
9
 
8
10
  /**
9
11
  * Compiles pTypescriptSource into a (typescript) AST
10
12
  *
11
- * @param {string} pTypescriptSource - the source to compile
12
- * @param {string} [pFileName] - (optional) file name the typescript
13
- * compiler can use in error messages
13
+ * @param {object} pFileRecord Record with source code, an extension and a filename
14
+ * @param {any} [pTranspileOptions] options for the transpiler(s) - a tsconfig or
15
+ * a babel config
14
16
  * @return {object} - a (typescript) AST
15
17
  */
16
- function getASTFromSource(pTypescriptSource, pFileName) {
18
+ function getASTFromSource(pFileRecord, pTranspileOptions) {
19
+ let lSource = pFileRecord.source;
20
+ if (pFileRecord.extension === ".vue") {
21
+ lSource = transpile(pFileRecord, pTranspileOptions);
22
+ }
23
+
17
24
  return typescript.createSourceFile(
18
- pFileName || "$internal-file-name",
19
- pTypescriptSource,
25
+ pFileRecord.filename || "$internal-file-name",
26
+ lSource,
20
27
  typescript.ScriptTarget.Latest,
21
28
  false
22
29
  );
@@ -27,10 +34,19 @@ function getASTFromSource(pTypescriptSource, pFileName) {
27
34
  * AST and returns it
28
35
  *
29
36
  * @param {string} pFileName - the name of the file to compile
37
+ * @param {any} [pTranspileOptions] options for the transpiler(s) - a tsconfig or
38
+ * a babel config
30
39
  * @return {object} - a (typescript) AST
31
40
  */
32
- function getAST(pFileName) {
33
- return getASTFromSource(fs.readFileSync(pFileName, "utf8"), pFileName);
41
+ function getAST(pFileName, pTranspileOptions) {
42
+ return getASTFromSource(
43
+ {
44
+ source: fs.readFileSync(pFileName, "utf8"),
45
+ extension: getExtension(pFileName),
46
+ filename: pFileName,
47
+ },
48
+ pTranspileOptions
49
+ );
34
50
  }
35
51
 
36
52
  const getASTCached = _memoize(getAST);
@@ -5,16 +5,20 @@ const path = require("path");
5
5
  *
6
6
  * Just using path.extname would be fine for most cases,
7
7
  * except for coffeescript, where a markdown extension can
8
- * mean literate coffeescript.
8
+ * mean literate coffeescript, and for typescript where
9
+ * .d.ts and .ts are slightly different beasts
9
10
  *
10
11
  * @param {string} pFileName path to the file to be parsed
11
12
  * @return {string} extension
12
13
  */
13
14
  module.exports = function getExtensions(pFileName) {
14
- let lReturnValue = path.extname(pFileName);
15
+ if (pFileName.endsWith(".d.ts")) {
16
+ return ".d.ts";
17
+ }
15
18
 
16
- if (lReturnValue === ".md") {
17
- return pFileName.endsWith(".coffee.md") ? ".coffee.md" : lReturnValue;
19
+ if (pFileName.endsWith(".coffee.md")) {
20
+ return ".coffee.md";
18
21
  }
19
- return lReturnValue;
22
+
23
+ return path.extname(pFileName);
20
24
  };