dependency-cruiser 13.0.0-beta-4 → 13.0.0-beta-6
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.
- package/README.md +26 -0
- package/package.json +10 -10
- package/src/cache/cache.mjs +11 -9
- package/src/cache/content-strategy.mjs +2 -4
- package/src/cache/find-content-changes.mjs +9 -12
- package/src/cache/helpers.mjs +26 -4
- package/src/cache/metadata-strategy.mjs +25 -16
- package/src/cache/options-compatible.mjs +2 -2
- package/src/cli/format.mjs +1 -1
- package/src/cli/index.mjs +27 -24
- package/src/cli/listeners/cli-feedback.mjs +3 -3
- package/src/cli/listeners/ndjson.mjs +4 -4
- package/src/cli/listeners/performance-log/format-helpers.mjs +7 -7
- package/src/cli/listeners/performance-log/index.mjs +13 -8
- package/src/cli/tools/wrap-stream-in-html.mjs +8 -5
- package/src/config-utl/extract-babel-config.mjs +5 -5
- package/src/config-utl/extract-depcruise-config/read-config.mjs +6 -4
- package/src/config-utl/extract-known-violations.mjs +3 -3
- package/src/config-utl/extract-webpack-resolve-config.mjs +4 -2
- package/src/enrich/enrich-modules.mjs +9 -14
- package/src/enrich/soften-known-violations.mjs +2 -5
- package/src/extract/index.mjs +3 -8
- package/src/extract/transpile/meta.d.ts +6 -7
- package/src/main/cruise.mjs +105 -0
- package/src/main/format.mjs +26 -0
- package/src/main/index.mjs +4 -123
- package/src/main/resolve-options/normalize.mjs +1 -2
- package/src/meta.js +1 -1
- package/src/report/dot/default-theme.mjs +4 -0
- package/src/report/index.mjs +1 -0
- package/src/report/null.mjs +12 -0
- package/src/utl/bus.mjs +27 -2
- package/src/utl/bus-log-levels.mjs +0 -9
package/README.md
CHANGED
|
@@ -59,6 +59,19 @@ a one liner:
|
|
|
59
59
|
npx depcruise src --include-only "^src" --output-type dot | dot -T svg > dependency-graph.svg
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
+
> <details>
|
|
63
|
+
> <summary>dependency-cruiser v12 and older: add --config option</summary>
|
|
64
|
+
>
|
|
65
|
+
> While not necessary from dependency-cruiser v13, in v12 and older you'll have
|
|
66
|
+
> to pass the --config option to make it find the .dependency-cruiser.js
|
|
67
|
+
> configuration file:
|
|
68
|
+
>
|
|
69
|
+
> ```shell
|
|
70
|
+
> npx depcruise src --include-only "^src" --config --output-type dot | dot -T svg > dependency-graph.svg
|
|
71
|
+
> ```
|
|
72
|
+
|
|
73
|
+
</details>
|
|
74
|
+
|
|
62
75
|
- You can read more about what you can do with `--include-only` and other command line
|
|
63
76
|
options in the [command line interface](./doc/cli.md) documentation.
|
|
64
77
|
- _[Real world samples](./doc/real-world-samples.md)_
|
|
@@ -110,6 +123,19 @@ Sample rule:
|
|
|
110
123
|
npx depcruise src
|
|
111
124
|
```
|
|
112
125
|
|
|
126
|
+
> <details>
|
|
127
|
+
> <summary>dependency-cruiser v12 and older: add --config option</summary>
|
|
128
|
+
>
|
|
129
|
+
> While not necessary from dependency-cruiser v13, in v12 and older you'll have
|
|
130
|
+
> to pass the --config option to make it find the .dependency-cruiser.js
|
|
131
|
+
> configuration file:
|
|
132
|
+
>
|
|
133
|
+
> ```shell
|
|
134
|
+
> npx depcruise --config .dependency-cruiser.js src
|
|
135
|
+
> ```
|
|
136
|
+
|
|
137
|
+
</details>
|
|
138
|
+
|
|
113
139
|
This will validate against your rules and shows any violations in an eslint-like format:
|
|
114
140
|
|
|
115
141
|

|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dependency-cruiser",
|
|
3
|
-
"version": "13.0.0-beta-
|
|
3
|
+
"version": "13.0.0-beta-6",
|
|
4
4
|
"description": "Validate and visualize dependencies. With your rules. JavaScript, TypeScript, CoffeeScript. ES6, CommonJS, AMD.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"static analysis",
|
|
@@ -169,19 +169,19 @@
|
|
|
169
169
|
"semver-try-require": "6.2.2",
|
|
170
170
|
"teamcity-service-messages": "0.1.14",
|
|
171
171
|
"tsconfig-paths-webpack-plugin": "4.0.1",
|
|
172
|
-
"watskeburt": "0.
|
|
172
|
+
"watskeburt": "0.11.1",
|
|
173
173
|
"wrap-ansi": "8.1.0"
|
|
174
174
|
},
|
|
175
175
|
"devDependencies": {
|
|
176
|
-
"@babel/core": "7.21.
|
|
177
|
-
"@babel/plugin-transform-modules-commonjs": "7.21.
|
|
178
|
-
"@babel/preset-typescript": "7.21.
|
|
179
|
-
"@swc/core": "1.3.
|
|
176
|
+
"@babel/core": "7.21.5",
|
|
177
|
+
"@babel/plugin-transform-modules-commonjs": "7.21.5",
|
|
178
|
+
"@babel/preset-typescript": "7.21.5",
|
|
179
|
+
"@swc/core": "1.3.55",
|
|
180
180
|
"@types/lodash": "4.14.194",
|
|
181
|
-
"@types/node": "18.16.
|
|
181
|
+
"@types/node": "18.16.3",
|
|
182
182
|
"@types/prompts": "2.4.4",
|
|
183
|
-
"@typescript-eslint/eslint-plugin": "5.59.
|
|
184
|
-
"@typescript-eslint/parser": "5.59.
|
|
183
|
+
"@typescript-eslint/eslint-plugin": "5.59.1",
|
|
184
|
+
"@typescript-eslint/parser": "5.59.1",
|
|
185
185
|
"@vue/compiler-sfc": "3.2.47",
|
|
186
186
|
"c8": "7.13.0",
|
|
187
187
|
"chai": "4.3.7",
|
|
@@ -199,7 +199,7 @@
|
|
|
199
199
|
"eslint-plugin-unicorn": "^46.0.0",
|
|
200
200
|
"husky": "8.0.3",
|
|
201
201
|
"intercept-stdout": "0.1.2",
|
|
202
|
-
"lint-staged": "13.2.
|
|
202
|
+
"lint-staged": "13.2.2",
|
|
203
203
|
"mocha": "10.2.0",
|
|
204
204
|
"normalize-newline": "4.1.0",
|
|
205
205
|
"npm-run-all": "4.1.5",
|
package/src/cache/cache.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFile, mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { scannableExtensions } from "../extract/transpile/meta.mjs";
|
|
4
|
+
import { bus } from "../utl/bus.mjs";
|
|
4
5
|
import { optionsAreCompatible } from "./options-compatible.mjs";
|
|
5
6
|
import MetadataStrategy from "./metadata-strategy.mjs";
|
|
6
7
|
import ContentStrategy from "./content-strategy.mjs";
|
|
@@ -25,10 +26,10 @@ export default class Cache {
|
|
|
25
26
|
* @param {import("../../types/dependency-cruiser.js").IRevisionData=} pRevisionData
|
|
26
27
|
* @returns {boolean}
|
|
27
28
|
*/
|
|
28
|
-
canServeFromCache(pCruiseOptions, pCachedCruiseResult, pRevisionData) {
|
|
29
|
+
async canServeFromCache(pCruiseOptions, pCachedCruiseResult, pRevisionData) {
|
|
29
30
|
this.revisionData =
|
|
30
31
|
pRevisionData ??
|
|
31
|
-
this.cacheStrategy.getRevisionData(
|
|
32
|
+
(await this.cacheStrategy.getRevisionData(
|
|
32
33
|
".",
|
|
33
34
|
pCachedCruiseResult,
|
|
34
35
|
pCruiseOptions,
|
|
@@ -37,7 +38,8 @@ export default class Cache {
|
|
|
37
38
|
scannableExtensions.concat(pCruiseOptions.extraExtensionsToScan)
|
|
38
39
|
),
|
|
39
40
|
}
|
|
40
|
-
);
|
|
41
|
+
));
|
|
42
|
+
bus.debug("cache: - comparing");
|
|
41
43
|
return (
|
|
42
44
|
this.cacheStrategy.revisionDataEqual(
|
|
43
45
|
pCachedCruiseResult.revisionData,
|
|
@@ -54,10 +56,10 @@ export default class Cache {
|
|
|
54
56
|
* @param {string} pCacheFolder
|
|
55
57
|
* @returns {import("../../types/dependency-cruiser.js").ICruiseResult}
|
|
56
58
|
*/
|
|
57
|
-
read(pCacheFolder) {
|
|
59
|
+
async read(pCacheFolder) {
|
|
58
60
|
try {
|
|
59
61
|
return JSON.parse(
|
|
60
|
-
|
|
62
|
+
await readFile(join(pCacheFolder, CACHE_FILE_NAME), "utf8")
|
|
61
63
|
);
|
|
62
64
|
} catch (pError) {
|
|
63
65
|
return { modules: [], summary: {} };
|
|
@@ -69,11 +71,11 @@ export default class Cache {
|
|
|
69
71
|
* @param {import("../../types/dependency-cruiser.js").ICruiseResult} pCruiseResult
|
|
70
72
|
* @param {import("../../types/dependency-cruiser.js").IRevisionData=} pRevisionData
|
|
71
73
|
*/
|
|
72
|
-
write(pCacheFolder, pCruiseResult, pRevisionData) {
|
|
74
|
+
async write(pCacheFolder, pCruiseResult, pRevisionData) {
|
|
73
75
|
const lRevisionData = pRevisionData ?? this.revisionData;
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
await mkdir(pCacheFolder, { recursive: true });
|
|
78
|
+
await writeFile(
|
|
77
79
|
join(pCacheFolder, CACHE_FILE_NAME),
|
|
78
80
|
JSON.stringify(
|
|
79
81
|
this.cacheStrategy.prepareRevisionDataForSaving(
|
|
@@ -2,9 +2,8 @@ import { isDeepStrictEqual } from "node:util";
|
|
|
2
2
|
import { join } from "node:path/posix";
|
|
3
3
|
import findContentChanges from "./find-content-changes.mjs";
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
getFileHashSync,
|
|
6
6
|
isInterestingChangeType,
|
|
7
|
-
addCheckSumToChange,
|
|
8
7
|
moduleIsInterestingForDiff,
|
|
9
8
|
} from "./helpers.mjs";
|
|
10
9
|
|
|
@@ -17,7 +16,7 @@ function addCheckSumToModule(pBaseDirectory) {
|
|
|
17
16
|
if (moduleIsInterestingForDiff(pModule)) {
|
|
18
17
|
return {
|
|
19
18
|
...pModule,
|
|
20
|
-
checksum:
|
|
19
|
+
checksum: getFileHashSync(join(pBaseDirectory, pModule.source)),
|
|
21
20
|
};
|
|
22
21
|
}
|
|
23
22
|
return pModule;
|
|
@@ -56,7 +55,6 @@ export default class ContentStrategy {
|
|
|
56
55
|
getRevisionData(pDirectory, pCachedCruiseResult, pCruiseOptions, pOptions) {
|
|
57
56
|
const lOptions = {
|
|
58
57
|
diffListFn: findContentChanges,
|
|
59
|
-
checksumFn: addCheckSumToChange,
|
|
60
58
|
baseDir: process.cwd(),
|
|
61
59
|
...pOptions,
|
|
62
60
|
};
|
|
@@ -1,26 +1,23 @@
|
|
|
1
1
|
import { join } from "node:path/posix";
|
|
2
|
-
import bus from "../utl/bus.mjs";
|
|
3
|
-
import busLogLevels from "../utl/bus-log-levels.mjs";
|
|
2
|
+
import { bus } from "../utl/bus.mjs";
|
|
4
3
|
import findAllFiles from "../utl/find-all-files.mjs";
|
|
5
4
|
import {
|
|
6
|
-
|
|
5
|
+
getFileHashSync,
|
|
7
6
|
excludeFilter,
|
|
8
7
|
includeOnlyFilter,
|
|
9
8
|
hasInterestingExtension,
|
|
10
9
|
moduleIsInterestingForDiff,
|
|
11
10
|
} from "./helpers.mjs";
|
|
12
11
|
|
|
13
|
-
const { DEBUG } = busLogLevels;
|
|
14
|
-
|
|
15
12
|
/**
|
|
16
13
|
* @param {Set<string>} pFileSet
|
|
17
|
-
* @param {typeof
|
|
14
|
+
* @param {typeof getFileHashSync} pFileHashFunction
|
|
18
15
|
* @returns {(pModule:import("../..").IModule) => import('../..').IRevisionChange}
|
|
19
16
|
*/
|
|
20
17
|
function diffCachedModuleAgainstFileSet(
|
|
21
18
|
pFileSet,
|
|
22
19
|
pBaseDirectory,
|
|
23
|
-
pFileHashFunction =
|
|
20
|
+
pFileHashFunction = getFileHashSync
|
|
24
21
|
) {
|
|
25
22
|
return (pModule) => {
|
|
26
23
|
if (!moduleIsInterestingForDiff(pModule)) {
|
|
@@ -75,7 +72,7 @@ export default function findContentChanges(
|
|
|
75
72
|
pCachedCruiseResult,
|
|
76
73
|
pOptions
|
|
77
74
|
) {
|
|
78
|
-
bus.
|
|
75
|
+
bus.debug("cache: - getting revision data");
|
|
79
76
|
const lFileSet = new Set(
|
|
80
77
|
findAllFiles(pDirectory, {
|
|
81
78
|
baseDir: pOptions.baseDir,
|
|
@@ -84,12 +81,12 @@ export default function findContentChanges(
|
|
|
84
81
|
}).filter(hasInterestingExtension(pOptions.extensions))
|
|
85
82
|
);
|
|
86
83
|
|
|
87
|
-
bus.
|
|
84
|
+
bus.debug("cache: - getting (cached - new)");
|
|
88
85
|
const lDiffCachedVsNew = pCachedCruiseResult.modules.map(
|
|
89
86
|
diffCachedModuleAgainstFileSet(lFileSet, pOptions.baseDir)
|
|
90
87
|
);
|
|
91
88
|
|
|
92
|
-
bus.
|
|
89
|
+
bus.debug("cache: - getting (new - cached)");
|
|
93
90
|
lDiffCachedVsNew.forEach(({ name }) => lFileSet.delete(name));
|
|
94
91
|
|
|
95
92
|
const lDiffNewVsCached = [];
|
|
@@ -97,10 +94,10 @@ export default function findContentChanges(
|
|
|
97
94
|
lDiffNewVsCached.push({
|
|
98
95
|
name: lFileName,
|
|
99
96
|
changeType: "added",
|
|
100
|
-
checksum:
|
|
97
|
+
checksum: getFileHashSync(join(pOptions.baseDir, lFileName)),
|
|
101
98
|
});
|
|
102
99
|
}
|
|
103
100
|
|
|
104
|
-
bus.
|
|
101
|
+
bus.debug("cache: - returning revision data");
|
|
105
102
|
return lDiffCachedVsNew.concat(lDiffNewVsCached);
|
|
106
103
|
}
|
package/src/cache/helpers.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint-disable import/exports-last */
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
|
+
import { readFile } from "node:fs/promises";
|
|
4
5
|
import { extname } from "node:path";
|
|
5
6
|
import memoize from "lodash/memoize.js";
|
|
6
7
|
import { filenameMatchesPattern } from "../graph-utl/match-facade.mjs";
|
|
@@ -13,11 +14,32 @@ function hash(pString) {
|
|
|
13
14
|
return createHash("sha1").update(pString).digest("base64");
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
/**
|
|
18
|
+
* @param {import("fs").PathOrFileDescriptor} pFileName
|
|
19
|
+
* @returns {Promise<string>}
|
|
20
|
+
*/
|
|
21
|
+
async function _getFileHash(pFileName) {
|
|
22
|
+
try {
|
|
23
|
+
return hash(await readFile(pFileName, "utf8"));
|
|
24
|
+
} catch (pError) {
|
|
25
|
+
return "file not found";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const getFileHash = memoize(_getFileHash);
|
|
30
|
+
|
|
31
|
+
export async function addCheckSumToChange(pChange) {
|
|
32
|
+
return {
|
|
33
|
+
...pChange,
|
|
34
|
+
checksum: await getFileHash(pChange.name),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
16
38
|
/**
|
|
17
39
|
* @param {import("fs").PathOrFileDescriptor} pFileName
|
|
18
40
|
* @returns {string}
|
|
19
41
|
*/
|
|
20
|
-
function
|
|
42
|
+
function _getFileHashSync(pFileName) {
|
|
21
43
|
try {
|
|
22
44
|
return hash(readFileSync(pFileName, "utf8"));
|
|
23
45
|
} catch (pError) {
|
|
@@ -25,16 +47,16 @@ function _getFileHash(pFileName) {
|
|
|
25
47
|
}
|
|
26
48
|
}
|
|
27
49
|
|
|
28
|
-
export const
|
|
50
|
+
export const getFileHashSync = memoize(_getFileHashSync);
|
|
29
51
|
|
|
30
52
|
/**
|
|
31
53
|
* @param {import("watskeburt").IChange} pChange
|
|
32
54
|
* @param {import("../../types/dependency-cruiser.js").IRevisionChange}
|
|
33
55
|
*/
|
|
34
|
-
export function
|
|
56
|
+
export function addCheckSumToChangeSync(pChange) {
|
|
35
57
|
return {
|
|
36
58
|
...pChange,
|
|
37
|
-
checksum:
|
|
59
|
+
checksum: getFileHashSync(pChange.name),
|
|
38
60
|
};
|
|
39
61
|
}
|
|
40
62
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { isDeepStrictEqual } from "node:util";
|
|
2
|
-
import {
|
|
2
|
+
import { getSHA, list } from "watskeburt";
|
|
3
|
+
import { bus } from "../utl/bus.mjs";
|
|
3
4
|
import {
|
|
4
5
|
isInterestingChangeType,
|
|
5
|
-
|
|
6
|
+
addCheckSumToChangeSync,
|
|
6
7
|
excludeFilter,
|
|
7
8
|
includeOnlyFilter,
|
|
8
9
|
changeHasInterestingExtension,
|
|
@@ -21,26 +22,34 @@ export default class MetaDataStrategy {
|
|
|
21
22
|
* @param {(import("watskeburt").IChange) => import("../..").IRevisionChange} pOptions.checksumFn
|
|
22
23
|
* @returns {import("../../types/dependency-cruiser.js").IRevisionData}
|
|
23
24
|
*/
|
|
24
|
-
getRevisionData(
|
|
25
|
+
async getRevisionData(
|
|
26
|
+
pDirectory,
|
|
27
|
+
pCachedCruiseResult,
|
|
28
|
+
pCruiseOptions,
|
|
29
|
+
pOptions
|
|
30
|
+
) {
|
|
25
31
|
const lOptions = {
|
|
26
|
-
shaRetrievalFn:
|
|
27
|
-
diffListFn:
|
|
28
|
-
checksumFn:
|
|
32
|
+
shaRetrievalFn: getSHA,
|
|
33
|
+
diffListFn: list,
|
|
34
|
+
checksumFn: addCheckSumToChangeSync,
|
|
29
35
|
...pOptions,
|
|
30
36
|
};
|
|
31
37
|
try {
|
|
32
|
-
|
|
38
|
+
bus.debug("cache: - getting sha");
|
|
39
|
+
const lSHA = await lOptions.shaRetrievalFn();
|
|
40
|
+
bus.debug("cache: - getting diff");
|
|
41
|
+
const lDiff = await lOptions.diffListFn(lSHA);
|
|
42
|
+
const lChanges = lDiff
|
|
43
|
+
.filter(({ name }) => excludeFilter(pCruiseOptions.exclude)(name))
|
|
44
|
+
.filter(({ name }) =>
|
|
45
|
+
includeOnlyFilter(pCruiseOptions.includeOnly)(name)
|
|
46
|
+
)
|
|
47
|
+
.filter(changeHasInterestingExtension(lOptions.extensions))
|
|
48
|
+
.filter(isInterestingChangeType(lOptions.interestingChangeTypes));
|
|
49
|
+
bus.debug("cache: - sha-summing diff");
|
|
33
50
|
return {
|
|
34
51
|
SHA1: lSHA,
|
|
35
|
-
changes: lOptions
|
|
36
|
-
.diffListFn(lSHA)
|
|
37
|
-
.filter(({ name }) => excludeFilter(pCruiseOptions.exclude)(name))
|
|
38
|
-
.filter(({ name }) =>
|
|
39
|
-
includeOnlyFilter(pCruiseOptions.includeOnly)(name)
|
|
40
|
-
)
|
|
41
|
-
.filter(changeHasInterestingExtension(lOptions.extensions))
|
|
42
|
-
.filter(isInterestingChangeType(lOptions.interestingChangeTypes))
|
|
43
|
-
.map(lOptions.checksumFn),
|
|
52
|
+
changes: lChanges.map(lOptions.checksumFn),
|
|
44
53
|
};
|
|
45
54
|
} catch (pError) {
|
|
46
55
|
throw new Error(
|
|
@@ -117,8 +117,8 @@ export function optionsAreCompatible(pOldOptions, pNewOptions) {
|
|
|
117
117
|
filterOptionIsCompatible(pOldOptions.collapse, pNewOptions.collapse) &&
|
|
118
118
|
limitIsCompatible(pOldOptions.maxDepth, pNewOptions.maxDepth) &&
|
|
119
119
|
optionIsCompatible(
|
|
120
|
-
pOldOptions.knownViolations,
|
|
121
|
-
pNewOptions.knownViolations
|
|
120
|
+
pOldOptions.knownViolations || [],
|
|
121
|
+
pNewOptions.knownViolations || []
|
|
122
122
|
) &&
|
|
123
123
|
optionIsCompatible(
|
|
124
124
|
pOldOptions.enhancedResolveOptions,
|
package/src/cli/format.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import getStream from "get-stream";
|
|
2
|
-
import
|
|
2
|
+
import _format from "../main/format.mjs";
|
|
3
3
|
import validateFileExistence from "./utl/validate-file-existence.mjs";
|
|
4
4
|
import normalizeOptions from "./normalize-cli-options.mjs";
|
|
5
5
|
import { getInStream, write } from "./utl/io.mjs";
|
package/src/cli/index.mjs
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable import/max-dependencies */
|
|
2
1
|
import { join } from "node:path";
|
|
3
2
|
import { glob } from "glob";
|
|
4
3
|
import cloneDeep from "lodash/cloneDeep.js";
|
|
@@ -6,10 +5,9 @@ import set from "lodash/set.js";
|
|
|
6
5
|
import isInstalledGlobally from "is-installed-globally";
|
|
7
6
|
import chalk from "chalk";
|
|
8
7
|
|
|
9
|
-
import
|
|
10
|
-
import bus from "../utl/bus.mjs";
|
|
8
|
+
import cruise from "../main/cruise.mjs";
|
|
9
|
+
import { INFO, bus } from "../utl/bus.mjs";
|
|
11
10
|
|
|
12
|
-
import busLogLevels from "../utl/bus-log-levels.mjs";
|
|
13
11
|
import validateFileExistence from "./utl/validate-file-existence.mjs";
|
|
14
12
|
import normalizeCliOptions from "./normalize-cli-options.mjs";
|
|
15
13
|
import { write } from "./utl/io.mjs";
|
|
@@ -23,10 +21,10 @@ async function extractResolveOptions(pCruiseOptions) {
|
|
|
23
21
|
pCruiseOptions?.ruleSet?.options?.webpackConfig?.fileName ?? null;
|
|
24
22
|
|
|
25
23
|
if (lWebPackConfigFileName) {
|
|
26
|
-
const extractWebpackResolveConfig = await import(
|
|
24
|
+
const { default: extractWebpackResolveConfig } = await import(
|
|
27
25
|
"../config-utl/extract-webpack-resolve-config.mjs"
|
|
28
26
|
);
|
|
29
|
-
lResolveOptions = await extractWebpackResolveConfig
|
|
27
|
+
lResolveOptions = await extractWebpackResolveConfig(
|
|
30
28
|
lWebPackConfigFileName,
|
|
31
29
|
pCruiseOptions?.ruleSet?.options?.webpackConfig?.env ?? null,
|
|
32
30
|
pCruiseOptions?.ruleSet?.options?.webpackConfig?.arguments ?? null
|
|
@@ -37,10 +35,10 @@ async function extractResolveOptions(pCruiseOptions) {
|
|
|
37
35
|
|
|
38
36
|
async function addKnownViolations(pCruiseOptions) {
|
|
39
37
|
if (pCruiseOptions.knownViolationsFile) {
|
|
40
|
-
const extractKnownViolations = await import(
|
|
38
|
+
const { default: extractKnownViolations } = await import(
|
|
41
39
|
"../config-utl/extract-known-violations.mjs"
|
|
42
40
|
);
|
|
43
|
-
const lKnownViolations = extractKnownViolations
|
|
41
|
+
const lKnownViolations = await extractKnownViolations(
|
|
44
42
|
pCruiseOptions.knownViolationsFile
|
|
45
43
|
);
|
|
46
44
|
|
|
@@ -59,8 +57,10 @@ async function extractTSConfigOptions(pCruiseOptions) {
|
|
|
59
57
|
pCruiseOptions?.ruleSet?.options?.tsConfig?.fileName ?? null;
|
|
60
58
|
|
|
61
59
|
if (lTSConfigFileName) {
|
|
62
|
-
const extractTSConfig = await import(
|
|
63
|
-
|
|
60
|
+
const { default: extractTSConfig } = await import(
|
|
61
|
+
"../config-utl/extract-ts-config.mjs"
|
|
62
|
+
);
|
|
63
|
+
lReturnValue = extractTSConfig(lTSConfigFileName);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
return lReturnValue;
|
|
@@ -71,10 +71,10 @@ async function extractBabelConfigOptions(pCruiseOptions) {
|
|
|
71
71
|
const lBabelConfigFileName =
|
|
72
72
|
pCruiseOptions?.ruleSet?.options?.babelConfig?.fileName ?? null;
|
|
73
73
|
if (lBabelConfigFileName) {
|
|
74
|
-
const extractBabelConfig = await import(
|
|
74
|
+
const { default: extractBabelConfig } = await import(
|
|
75
75
|
"../config-utl/extract-babel-config.mjs"
|
|
76
76
|
);
|
|
77
|
-
lReturnValue = extractBabelConfig
|
|
77
|
+
lReturnValue = extractBabelConfig(lBabelConfigFileName);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
return lReturnValue;
|
|
@@ -95,8 +95,7 @@ function setUpListener(pCruiseOptions) {
|
|
|
95
95
|
if (Boolean(lListenerFunction)) {
|
|
96
96
|
lListenerFunction(
|
|
97
97
|
bus,
|
|
98
|
-
pCruiseOptions?.ruleSet?.options?.progress?.maximumLevel ??
|
|
99
|
-
busLogLevels.INFO
|
|
98
|
+
pCruiseOptions?.ruleSet?.options?.progress?.maximumLevel ?? INFO
|
|
100
99
|
);
|
|
101
100
|
}
|
|
102
101
|
}
|
|
@@ -118,17 +117,19 @@ async function runCruise(pFileDirectoryArray, pCruiseOptions) {
|
|
|
118
117
|
setUpListener(lCruiseOptions);
|
|
119
118
|
|
|
120
119
|
bus.emit("start");
|
|
120
|
+
const [lResolveOptions, tsConfig, babelConfig] = await Promise.all([
|
|
121
|
+
extractResolveOptions(lCruiseOptions),
|
|
122
|
+
extractTSConfigOptions(lCruiseOptions),
|
|
123
|
+
extractBabelConfigOptions(lCruiseOptions),
|
|
124
|
+
]);
|
|
121
125
|
const lReportingResult = await cruise(
|
|
122
126
|
pFileDirectoryArray,
|
|
123
127
|
lCruiseOptions,
|
|
124
|
-
|
|
125
|
-
{
|
|
126
|
-
tsConfig: await extractTSConfigOptions(lCruiseOptions),
|
|
127
|
-
babelConfig: await extractBabelConfigOptions(lCruiseOptions),
|
|
128
|
-
}
|
|
128
|
+
lResolveOptions,
|
|
129
|
+
{ tsConfig, babelConfig }
|
|
129
130
|
);
|
|
130
131
|
|
|
131
|
-
bus.
|
|
132
|
+
bus.progress("cli: writing results", { complete: 1 });
|
|
132
133
|
bus.emit("write-start");
|
|
133
134
|
write(lCruiseOptions.outputTo, lReportingResult.output);
|
|
134
135
|
|
|
@@ -162,11 +163,13 @@ export default async function executeCli(pFileDirectoryArray, pCruiseOptions) {
|
|
|
162
163
|
}
|
|
163
164
|
/* c8 ignore stop */
|
|
164
165
|
if (lCruiseOptions.info === true) {
|
|
165
|
-
const formatMetaInfo = await import(
|
|
166
|
-
|
|
166
|
+
const { default: formatMetaInfo } = await import(
|
|
167
|
+
"./format-meta-info.mjs"
|
|
168
|
+
);
|
|
169
|
+
process.stdout.write(await formatMetaInfo());
|
|
167
170
|
} else if (lCruiseOptions.init) {
|
|
168
|
-
const initConfig = await import("./init-config/index.mjs");
|
|
169
|
-
initConfig
|
|
171
|
+
const { default: initConfig } = await import("./init-config/index.mjs");
|
|
172
|
+
initConfig(lCruiseOptions.init);
|
|
170
173
|
} else {
|
|
171
174
|
lExitCode = await runCruise(pFileDirectoryArray, lCruiseOptions);
|
|
172
175
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import figures from "figures";
|
|
3
|
-
import
|
|
3
|
+
import { SUMMARY } from "../../utl/bus.mjs";
|
|
4
4
|
|
|
5
5
|
const FULL_ON = 100;
|
|
6
6
|
|
|
@@ -26,7 +26,7 @@ function getPercentageBar(pPercentage, pParameters) {
|
|
|
26
26
|
function getProgressMessageWriter(pStream, pState, pMaxLogLevel) {
|
|
27
27
|
return (pMessage, pOptions) => {
|
|
28
28
|
const lOptions = {
|
|
29
|
-
level:
|
|
29
|
+
level: SUMMARY,
|
|
30
30
|
complete: pState.complete,
|
|
31
31
|
...(pOptions || {}),
|
|
32
32
|
};
|
|
@@ -56,7 +56,7 @@ function getStartWriter(pStream) {
|
|
|
56
56
|
|
|
57
57
|
export default function setUpCliFeedbackListener(
|
|
58
58
|
pEventEmitter,
|
|
59
|
-
pMaxLogLevel =
|
|
59
|
+
pMaxLogLevel = SUMMARY,
|
|
60
60
|
pStream = process.stderr
|
|
61
61
|
) {
|
|
62
62
|
const lState = {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EOL } from "node:os";
|
|
2
|
-
import
|
|
2
|
+
import { INFO, SUMMARY } from "../../utl/bus.mjs";
|
|
3
3
|
|
|
4
4
|
const MICRO_SECONDS_PER_SECOND = 1000000;
|
|
5
5
|
|
|
@@ -63,7 +63,7 @@ function getProgressLine(pMessage, pState, pLevel, pMaxLevel) {
|
|
|
63
63
|
|
|
64
64
|
function getProgressWriter(pStream, pState, pMaxLevel) {
|
|
65
65
|
return (pMessage, pOptions) => {
|
|
66
|
-
const lOptions = { level:
|
|
66
|
+
const lOptions = { level: SUMMARY, ...(pOptions || {}) };
|
|
67
67
|
|
|
68
68
|
pStream.write(getProgressLine(pMessage, pState, lOptions.level, pMaxLevel));
|
|
69
69
|
};
|
|
@@ -71,12 +71,12 @@ function getProgressWriter(pStream, pState, pMaxLevel) {
|
|
|
71
71
|
|
|
72
72
|
export default function setUpNDJSONListener(
|
|
73
73
|
pEventEmitter,
|
|
74
|
-
pMaxLevel =
|
|
74
|
+
pMaxLevel = INFO,
|
|
75
75
|
pStream = process.stderr
|
|
76
76
|
) {
|
|
77
77
|
let lState = {
|
|
78
78
|
runStartTime: new Date(Date.now()).toISOString(),
|
|
79
|
-
previousMessage: "
|
|
79
|
+
previousMessage: "starting nodejs",
|
|
80
80
|
previousTime: 0,
|
|
81
81
|
previousUserUsage: 0,
|
|
82
82
|
previousSystemUsage: 0,
|
|
@@ -38,13 +38,13 @@ export function formatHeader() {
|
|
|
38
38
|
return chalk
|
|
39
39
|
.bold(
|
|
40
40
|
`${
|
|
41
|
-
pad("elapsed real") +
|
|
42
|
-
pad("user") +
|
|
43
|
-
pad("system") +
|
|
44
41
|
pad("∆ rss") +
|
|
45
42
|
pad("∆ heapTotal") +
|
|
46
43
|
pad("∆ heapUsed") +
|
|
47
|
-
pad("∆ external")
|
|
44
|
+
pad("∆ external") +
|
|
45
|
+
pad("⏱ system") +
|
|
46
|
+
pad("⏱ user") +
|
|
47
|
+
pad("⏱ real")
|
|
48
48
|
}after step...\n`
|
|
49
49
|
)
|
|
50
50
|
.concat(formatDividerLine());
|
|
@@ -73,13 +73,13 @@ export function formatPerfLine({
|
|
|
73
73
|
message,
|
|
74
74
|
}) {
|
|
75
75
|
return `${
|
|
76
|
-
formatTime(elapsedTime) +
|
|
77
|
-
formatTime(elapsedUser, MS_PER_MICRO_SECOND) +
|
|
78
|
-
formatTime(elapsedSystem, MS_PER_MICRO_SECOND) +
|
|
79
76
|
formatMemory(deltaRss) +
|
|
80
77
|
formatMemory(deltaHeapTotal) +
|
|
81
78
|
formatMemory(deltaHeapUsed) +
|
|
82
79
|
formatMemory(deltaExternal) +
|
|
80
|
+
formatTime(elapsedSystem, MS_PER_MICRO_SECOND) +
|
|
81
|
+
formatTime(elapsedUser, MS_PER_MICRO_SECOND) +
|
|
82
|
+
formatTime(elapsedTime) +
|
|
83
83
|
message
|
|
84
84
|
}\n`;
|
|
85
85
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { INFO, SUMMARY } from "../../../utl/bus.mjs";
|
|
2
2
|
import { getHeader, getProgressLine, getEndText } from "./handlers.mjs";
|
|
3
3
|
|
|
4
4
|
function getHeaderWriter(pStream, pMaxLevel) {
|
|
5
5
|
return (_pMessage, pOptions) => {
|
|
6
|
-
const lOptions = { level:
|
|
6
|
+
const lOptions = { level: SUMMARY, ...(pOptions || {}) };
|
|
7
7
|
|
|
8
8
|
pStream.write(getHeader(lOptions.level, pMaxLevel));
|
|
9
9
|
};
|
|
@@ -11,14 +11,19 @@ function getHeaderWriter(pStream, pMaxLevel) {
|
|
|
11
11
|
|
|
12
12
|
function getProgressWriter(pStream, pState, pMaxLevel) {
|
|
13
13
|
return (pMessage, pOptions) => {
|
|
14
|
-
const lOptions = { level:
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
const lOptions = { level: SUMMARY, ...(pOptions || {}) };
|
|
15
|
+
const lProgressLine = getProgressLine(
|
|
16
|
+
pMessage,
|
|
17
|
+
pState,
|
|
18
|
+
lOptions.level,
|
|
19
|
+
pMaxLevel
|
|
20
|
+
);
|
|
21
|
+
pStream.write(lProgressLine);
|
|
17
22
|
};
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
function getEndWriter(pStream, pState, pMaxLevel) {
|
|
21
|
-
return (_pMessage, pLevel =
|
|
26
|
+
return (_pMessage, pLevel = SUMMARY) => {
|
|
22
27
|
pStream.write(getEndText(pState, pLevel, pMaxLevel));
|
|
23
28
|
pStream.end();
|
|
24
29
|
};
|
|
@@ -26,11 +31,11 @@ function getEndWriter(pStream, pState, pMaxLevel) {
|
|
|
26
31
|
|
|
27
32
|
export default function setUpPerformanceLogListener(
|
|
28
33
|
pEventEmitter,
|
|
29
|
-
pMaxLevel =
|
|
34
|
+
pMaxLevel = INFO,
|
|
30
35
|
pStream = process.stderr
|
|
31
36
|
) {
|
|
32
37
|
let lState = {
|
|
33
|
-
previousMessage: "
|
|
38
|
+
previousMessage: "starting nodejs",
|
|
34
39
|
previousTime: 0,
|
|
35
40
|
previousUserUsage: 0,
|
|
36
41
|
previousSystemUsage: 0,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
|
|
4
4
|
const HEADER_FILE = fileURLToPath(
|
|
@@ -25,10 +25,13 @@ const FOOTER_FILE = fileURLToPath(
|
|
|
25
25
|
* @param {readStream} pStream stream whose characters are to be slapped between header and footer
|
|
26
26
|
* @param {writeStream} pOutStream stream to write to
|
|
27
27
|
*/
|
|
28
|
-
export default function wrap(pInStream, pOutStream) {
|
|
29
|
-
const lHeader =
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
export default async function wrap(pInStream, pOutStream) {
|
|
29
|
+
const [lHeader, lScript, lEnd] = await Promise.all([
|
|
30
|
+
readFile(HEADER_FILE, "utf8"),
|
|
31
|
+
readFile(SCRIPT_FILE, "utf8"),
|
|
32
|
+
readFile(FOOTER_FILE, "utf8"),
|
|
33
|
+
]);
|
|
34
|
+
|
|
32
35
|
const lFooter = `<script>${lScript}</script>${lEnd}`;
|
|
33
36
|
|
|
34
37
|
pOutStream.write(lHeader);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
2
|
|
|
3
3
|
import { extname } from "node:path";
|
|
4
4
|
import json5 from "json5";
|
|
@@ -11,10 +11,10 @@ async function getJSConfig(pBabelConfigFileName) {
|
|
|
11
11
|
let lReturnValue = {};
|
|
12
12
|
|
|
13
13
|
try {
|
|
14
|
-
const lModule = await import(
|
|
14
|
+
const { default: lModule } = await import(
|
|
15
15
|
`file://${makeAbsolute(pBabelConfigFileName)}`
|
|
16
16
|
);
|
|
17
|
-
lReturnValue = lModule
|
|
17
|
+
lReturnValue = lModule;
|
|
18
18
|
} catch (pError) {
|
|
19
19
|
throw new Error(
|
|
20
20
|
`${
|
|
@@ -35,11 +35,11 @@ async function getJSConfig(pBabelConfigFileName) {
|
|
|
35
35
|
return lReturnValue;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
function getJSON5Config(pBabelConfigFileName) {
|
|
38
|
+
async function getJSON5Config(pBabelConfigFileName) {
|
|
39
39
|
let lReturnValue = {};
|
|
40
40
|
|
|
41
41
|
try {
|
|
42
|
-
lReturnValue = json5.parse(
|
|
42
|
+
lReturnValue = json5.parse(await readFile(pBabelConfigFileName, "utf8"));
|
|
43
43
|
} catch (pError) {
|
|
44
44
|
throw new Error(
|
|
45
45
|
`Encountered an error while parsing the babel config '${pBabelConfigFileName}':` +
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { extname } from "node:path";
|
|
3
3
|
import json5 from "json5";
|
|
4
4
|
|
|
@@ -6,8 +6,10 @@ export default async function readConfig(pAbsolutePathToConfigFile) {
|
|
|
6
6
|
if (
|
|
7
7
|
[".js", ".cjs", ".mjs", ""].includes(extname(pAbsolutePathToConfigFile))
|
|
8
8
|
) {
|
|
9
|
-
const
|
|
10
|
-
|
|
9
|
+
const { default: config } = await import(
|
|
10
|
+
`file://${pAbsolutePathToConfigFile}`
|
|
11
|
+
);
|
|
12
|
+
return config;
|
|
11
13
|
}
|
|
12
|
-
return json5.parse(
|
|
14
|
+
return json5.parse(await readFile(pAbsolutePathToConfigFile, "utf8"));
|
|
13
15
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
2
|
import json5 from "json5";
|
|
3
3
|
import makeAbsolute from "./make-absolute.mjs";
|
|
4
4
|
|
|
5
|
-
export default function extractKnownViolations(pKnownViolationsFileName) {
|
|
5
|
+
export default async function extractKnownViolations(pKnownViolationsFileName) {
|
|
6
6
|
try {
|
|
7
7
|
return json5.parse(
|
|
8
|
-
|
|
8
|
+
await readFile(makeAbsolute(pKnownViolationsFileName), "utf8")
|
|
9
9
|
);
|
|
10
10
|
} catch (pError) {
|
|
11
11
|
if (pError instanceof SyntaxError) {
|
|
@@ -78,8 +78,10 @@ function isNativelySupported(pWebpackConfigFilename) {
|
|
|
78
78
|
async function attemptImport(pAbsoluteWebpackConfigFileName) {
|
|
79
79
|
try {
|
|
80
80
|
if (isNativelySupported(pAbsoluteWebpackConfigFileName)) {
|
|
81
|
-
const lModule = await import(
|
|
82
|
-
|
|
81
|
+
const { default: lModule } = await import(
|
|
82
|
+
`file://${pAbsoluteWebpackConfigFileName}`
|
|
83
|
+
);
|
|
84
|
+
return lModule;
|
|
83
85
|
} else {
|
|
84
86
|
tryRegisterNonNative(pAbsoluteWebpackConfigFileName);
|
|
85
87
|
/* we're using still using require instead of dynamic imports here because
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import bus from "../utl/bus.mjs";
|
|
2
|
-
import busLogLevels from "../utl/bus-log-levels.mjs";
|
|
1
|
+
import { bus } from "../utl/bus.mjs";
|
|
3
2
|
import addFocus from "../graph-utl/add-focus.mjs";
|
|
4
3
|
import IndexedModuleGraph from "../graph-utl/indexed-module-graph.mjs";
|
|
5
4
|
import deriveCycles from "./derive/circular.mjs";
|
|
@@ -11,31 +10,27 @@ import softenKnownViolations from "./soften-known-violations.mjs";
|
|
|
11
10
|
import deriveModuleMetrics from "./derive/metrics/index.mjs";
|
|
12
11
|
|
|
13
12
|
export default function enrichModules(pModules, pOptions) {
|
|
14
|
-
bus.
|
|
13
|
+
bus.info("analyzing: cycles");
|
|
15
14
|
const lIndexedModules = new IndexedModuleGraph(pModules);
|
|
16
15
|
let lModules = deriveCycles(pModules, lIndexedModules, {
|
|
17
16
|
pSourceAttribute: "source",
|
|
18
17
|
pDependencyName: "resolved",
|
|
19
18
|
});
|
|
20
|
-
bus.
|
|
21
|
-
lModules = addDependents(lModules
|
|
22
|
-
bus.
|
|
19
|
+
bus.info("analyzing: dependents");
|
|
20
|
+
lModules = addDependents(lModules);
|
|
21
|
+
bus.info("analyzing: orphans");
|
|
23
22
|
lModules = deriveOrphans(lModules);
|
|
24
|
-
bus.
|
|
23
|
+
bus.info("analyzing: reachables");
|
|
25
24
|
lModules = deriveReachable(lModules, pOptions.ruleSet);
|
|
26
|
-
bus.
|
|
27
|
-
level: busLogLevels.INFO,
|
|
28
|
-
});
|
|
25
|
+
bus.info("analyzing: module metrics");
|
|
29
26
|
lModules = deriveModuleMetrics(lModules, pOptions);
|
|
30
|
-
bus.
|
|
31
|
-
level: busLogLevels.INFO,
|
|
32
|
-
});
|
|
27
|
+
bus.info("analyzing: add focus (if any)");
|
|
33
28
|
lModules = addFocus(lModules, pOptions.focus);
|
|
34
29
|
|
|
35
30
|
// when validate === false we might want to skip the addValidations.
|
|
36
31
|
// We don't at this time, however, as "valid" is a mandatory
|
|
37
32
|
// attribute (to simplify reporter logic)
|
|
38
|
-
bus.
|
|
33
|
+
bus.info("analyzing: validations");
|
|
39
34
|
lModules = addValidations(lModules, pOptions.ruleSet, pOptions.validate);
|
|
40
35
|
|
|
41
36
|
lModules = softenKnownViolations(lModules, pOptions.knownViolations);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import bus from "../utl/bus.mjs";
|
|
2
|
-
import busLogLevels from "../utl/bus-log-levels.mjs";
|
|
1
|
+
import { bus } from "../utl/bus.mjs";
|
|
3
2
|
import isSameViolation from "./summarize/is-same-violation.mjs";
|
|
4
3
|
|
|
5
4
|
function softenModuleViolation(
|
|
@@ -118,9 +117,7 @@ export default function softenKnownViolations(
|
|
|
118
117
|
pSoftenedSeverity = "ignore"
|
|
119
118
|
) {
|
|
120
119
|
if (Boolean(pKnownViolations)) {
|
|
121
|
-
bus.
|
|
122
|
-
level: busLogLevels.INFO,
|
|
123
|
-
});
|
|
120
|
+
bus.info("analyzing: comparing against known errors");
|
|
124
121
|
return pModules.map((pModule) =>
|
|
125
122
|
softenKnownViolation(pModule, pKnownViolations, pSoftenedSeverity)
|
|
126
123
|
);
|
package/src/extract/index.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import has from "lodash/has.js";
|
|
2
|
-
import bus from "../utl/bus.mjs";
|
|
3
|
-
import busLogLevels from "../utl/bus-log-levels.mjs";
|
|
2
|
+
import { bus } from "../utl/bus.mjs";
|
|
4
3
|
import getDependencies from "./get-dependencies.mjs";
|
|
5
4
|
import gatherInitialSources from "./gather-initial-sources.mjs";
|
|
6
5
|
import clearCaches from "./clear-caches.mjs";
|
|
@@ -62,17 +61,13 @@ function extractFileDirectoryArray(
|
|
|
62
61
|
) {
|
|
63
62
|
let lVisited = new Set();
|
|
64
63
|
|
|
65
|
-
bus.
|
|
66
|
-
level: busLogLevels.INFO,
|
|
67
|
-
});
|
|
64
|
+
bus.info("reading files: gathering initial sources");
|
|
68
65
|
const lInitialSources = gatherInitialSources(
|
|
69
66
|
pFileDirectoryArray,
|
|
70
67
|
pCruiseOptions
|
|
71
68
|
);
|
|
72
69
|
|
|
73
|
-
bus.
|
|
74
|
-
level: busLogLevels.INFO,
|
|
75
|
-
});
|
|
70
|
+
bus.info("reading files: visiting dependencies");
|
|
76
71
|
return lInitialSources.reduce((pDependencies, pFilename) => {
|
|
77
72
|
if (!lVisited.has(pFilename)) {
|
|
78
73
|
lVisited.add(pFilename);
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IAvailableExtension,
|
|
3
|
+
IAvailableTranspiler,
|
|
4
|
+
} from "../../../types/dependency-cruiser.d.ts";
|
|
5
|
+
|
|
1
6
|
export interface ITranspilerWrapper {
|
|
2
7
|
isAvailable: () => boolean;
|
|
3
8
|
transpile: (
|
|
@@ -11,12 +16,6 @@ export function getWrapper(pExtension, pTranspileOptions): ITranspilerWrapper;
|
|
|
11
16
|
|
|
12
17
|
export const scannableExtensions: string[];
|
|
13
18
|
|
|
14
|
-
export const allExtensions:
|
|
15
|
-
|
|
16
|
-
export interface IAvailableTranspiler {
|
|
17
|
-
name: string;
|
|
18
|
-
version: string;
|
|
19
|
-
available: boolean;
|
|
20
|
-
}
|
|
19
|
+
export const allExtensions: IAvailableExtension[];
|
|
21
20
|
|
|
22
21
|
export function getAvailableTranspilers(): IAvailableTranspiler[];
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/* eslint-disable no-return-await */
|
|
2
|
+
/* eslint-disable no-magic-numbers */
|
|
3
|
+
import { bus } from "../utl/bus.mjs";
|
|
4
|
+
import { validateCruiseOptions } from "./options/validate.mjs";
|
|
5
|
+
import { normalizeCruiseOptions } from "./options/normalize.mjs";
|
|
6
|
+
import reportWrap from "./report-wrap.mjs";
|
|
7
|
+
|
|
8
|
+
const TOTAL_STEPS = 10;
|
|
9
|
+
|
|
10
|
+
export function c(pComplete, pTotal = TOTAL_STEPS) {
|
|
11
|
+
return { complete: pComplete / pTotal };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** @type {import("../../types/dependency-cruiser.js").cruise} */
|
|
15
|
+
// eslint-disable-next-line max-lines-per-function, max-statements
|
|
16
|
+
export default async function cruise(
|
|
17
|
+
pFileAndDirectoryArray,
|
|
18
|
+
pCruiseOptions,
|
|
19
|
+
pResolveOptions,
|
|
20
|
+
pTranspileOptions
|
|
21
|
+
) {
|
|
22
|
+
bus.summary("parsing options", c(1));
|
|
23
|
+
/** @type {import("../../types/strict-options.js").IStrictCruiseOptions} */
|
|
24
|
+
let lCruiseOptions = normalizeCruiseOptions(
|
|
25
|
+
validateCruiseOptions(pCruiseOptions),
|
|
26
|
+
pFileAndDirectoryArray
|
|
27
|
+
);
|
|
28
|
+
let lCache = null;
|
|
29
|
+
|
|
30
|
+
if (lCruiseOptions.cache) {
|
|
31
|
+
bus.summary(
|
|
32
|
+
`cache: checking freshness with ${lCruiseOptions.cache.strategy}`,
|
|
33
|
+
c(2)
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const { default: Cache } = await import("../cache/cache.mjs");
|
|
37
|
+
lCache = new Cache(lCruiseOptions.cache.strategy);
|
|
38
|
+
const lCachedResults = await lCache.read(lCruiseOptions.cache.folder);
|
|
39
|
+
|
|
40
|
+
if (await lCache.canServeFromCache(lCruiseOptions, lCachedResults)) {
|
|
41
|
+
bus.summary("cache: reporting from cache", c(8));
|
|
42
|
+
return await reportWrap(lCachedResults, lCruiseOptions);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
bus.summary("importing analytical modules", c(3));
|
|
47
|
+
const [
|
|
48
|
+
{ default: normalizeRuleSet },
|
|
49
|
+
{ default: validateRuleSet },
|
|
50
|
+
{ default: normalizeFilesAndDirectories },
|
|
51
|
+
{ default: normalizeResolveOptions },
|
|
52
|
+
{ default: extract },
|
|
53
|
+
{ default: enrich },
|
|
54
|
+
] = await Promise.all([
|
|
55
|
+
// despite rule set parsing being behind an if, it's the 'normal' use case
|
|
56
|
+
// for dependency-cruiser, so import it unconditionally nonetheless
|
|
57
|
+
import("./rule-set/normalize.mjs"),
|
|
58
|
+
import("./rule-set/validate.mjs"),
|
|
59
|
+
import("./files-and-dirs/normalize.mjs"),
|
|
60
|
+
import("./resolve-options/normalize.mjs"),
|
|
61
|
+
import("../extract/index.mjs"),
|
|
62
|
+
import("../enrich/index.mjs"),
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
if (Boolean(lCruiseOptions.ruleSet)) {
|
|
66
|
+
bus.summary("parsing rule set", c(4));
|
|
67
|
+
lCruiseOptions.ruleSet = normalizeRuleSet(
|
|
68
|
+
validateRuleSet(lCruiseOptions.ruleSet)
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const lNormalizedFileAndDirectoryArray = normalizeFilesAndDirectories(
|
|
73
|
+
pFileAndDirectoryArray
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
bus.summary("determining how to resolve", c(5));
|
|
77
|
+
const lNormalizedResolveOptions = await normalizeResolveOptions(
|
|
78
|
+
pResolveOptions,
|
|
79
|
+
lCruiseOptions,
|
|
80
|
+
pTranspileOptions?.tsConfig
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
bus.summary("reading files", c(6));
|
|
84
|
+
const lExtractionResult = extract(
|
|
85
|
+
lNormalizedFileAndDirectoryArray,
|
|
86
|
+
lCruiseOptions,
|
|
87
|
+
lNormalizedResolveOptions,
|
|
88
|
+
pTranspileOptions
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
bus.summary("analyzing", c(7));
|
|
92
|
+
const lCruiseResult = enrich(
|
|
93
|
+
lExtractionResult,
|
|
94
|
+
lCruiseOptions,
|
|
95
|
+
lNormalizedFileAndDirectoryArray
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
if (lCruiseOptions.cache) {
|
|
99
|
+
bus.summary("cache: saving", c(8));
|
|
100
|
+
await lCache.write(lCruiseOptions.cache.folder, lCruiseResult);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
bus.summary("reporting", c(9));
|
|
104
|
+
return await reportWrap(lCruiseResult, lCruiseOptions);
|
|
105
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import Ajv from "ajv";
|
|
2
|
+
|
|
3
|
+
import cruiseResultSchema from "../schema/cruise-result.schema.mjs";
|
|
4
|
+
import { validateFormatOptions } from "./options/validate.mjs";
|
|
5
|
+
import { normalizeFormatOptions } from "./options/normalize.mjs";
|
|
6
|
+
import reportWrap from "./report-wrap.mjs";
|
|
7
|
+
|
|
8
|
+
function validateResultAgainstSchema(pResult) {
|
|
9
|
+
const ajv = new Ajv();
|
|
10
|
+
|
|
11
|
+
if (!ajv.validate(cruiseResultSchema, pResult)) {
|
|
12
|
+
throw new Error(
|
|
13
|
+
`The supplied dependency-cruiser result is not valid: ${ajv.errorsText()}.\n`
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** @type {import("../../types/dependency-cruiser.js").format} */
|
|
18
|
+
export default async function format(pResult, pFormatOptions = {}) {
|
|
19
|
+
const lFormatOptions = normalizeFormatOptions(pFormatOptions);
|
|
20
|
+
validateFormatOptions(lFormatOptions);
|
|
21
|
+
|
|
22
|
+
validateResultAgainstSchema(pResult);
|
|
23
|
+
|
|
24
|
+
// eslint-disable-next-line no-return-await
|
|
25
|
+
return await reportWrap(pResult, lFormatOptions);
|
|
26
|
+
}
|
package/src/main/index.mjs
CHANGED
|
@@ -1,135 +1,16 @@
|
|
|
1
|
-
/* eslint-disable no-return-await */
|
|
2
|
-
/* eslint-disable no-magic-numbers */
|
|
3
|
-
|
|
4
|
-
import Ajv from "ajv";
|
|
5
|
-
|
|
6
|
-
import cruiseResultSchema from "../schema/cruise-result.schema.mjs";
|
|
7
|
-
import bus from "../utl/bus.mjs";
|
|
8
1
|
import {
|
|
9
2
|
allExtensions,
|
|
10
3
|
getAvailableTranspilers,
|
|
11
4
|
} from "../extract/transpile/meta.mjs";
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
15
|
-
validateCruiseOptions,
|
|
16
|
-
validateFormatOptions,
|
|
17
|
-
} from "./options/validate.mjs";
|
|
18
|
-
import {
|
|
19
|
-
normalizeCruiseOptions,
|
|
20
|
-
normalizeFormatOptions,
|
|
21
|
-
} from "./options/normalize.mjs";
|
|
22
|
-
import reportWrap from "./report-wrap.mjs";
|
|
23
|
-
|
|
24
|
-
const TOTAL_STEPS = 9;
|
|
25
|
-
|
|
26
|
-
function c(pComplete, pTotal = TOTAL_STEPS) {
|
|
27
|
-
return { complete: pComplete / pTotal };
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function validateResultAgainstSchema(pResult) {
|
|
31
|
-
const ajv = new Ajv();
|
|
32
|
-
|
|
33
|
-
if (!ajv.validate(cruiseResultSchema, pResult)) {
|
|
34
|
-
throw new Error(
|
|
35
|
-
`The supplied dependency-cruiser result is not valid: ${ajv.errorsText()}.\n`
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
/** @type {import("../../types/dependency-cruiser.js").format} */
|
|
40
|
-
export async function format(pResult, pFormatOptions = {}) {
|
|
41
|
-
const lFormatOptions = normalizeFormatOptions(pFormatOptions);
|
|
42
|
-
validateFormatOptions(lFormatOptions);
|
|
43
|
-
|
|
44
|
-
validateResultAgainstSchema(pResult);
|
|
45
|
-
|
|
46
|
-
return await reportWrap(pResult, lFormatOptions);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** @type {import("../../types/dependency-cruiser.js").cruise} */
|
|
50
|
-
// eslint-disable-next-line max-lines-per-function, max-statements
|
|
51
|
-
export async function cruise(
|
|
52
|
-
pFileAndDirectoryArray,
|
|
53
|
-
pCruiseOptions,
|
|
54
|
-
pResolveOptions,
|
|
55
|
-
pTranspileOptions
|
|
56
|
-
) {
|
|
57
|
-
bus.emit("progress", "parsing options", c(1));
|
|
58
|
-
/** @type {import("../../types/strict-options.js").IStrictCruiseOptions} */
|
|
59
|
-
let lCruiseOptions = normalizeCruiseOptions(
|
|
60
|
-
validateCruiseOptions(pCruiseOptions),
|
|
61
|
-
pFileAndDirectoryArray
|
|
62
|
-
);
|
|
63
|
-
let lCache = null;
|
|
64
|
-
|
|
65
|
-
if (lCruiseOptions.cache) {
|
|
66
|
-
bus.emit(
|
|
67
|
-
"progress",
|
|
68
|
-
`cache: check freshness with ${lCruiseOptions.cache.strategy}`,
|
|
69
|
-
c(2)
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
const CacheModule = await import("../cache/cache.mjs");
|
|
73
|
-
const Cache = CacheModule.default;
|
|
74
|
-
lCache = new Cache(lCruiseOptions.cache.strategy);
|
|
75
|
-
const lCachedResults = lCache.read(lCruiseOptions.cache.folder);
|
|
76
|
-
|
|
77
|
-
if (lCache.canServeFromCache(lCruiseOptions, lCachedResults)) {
|
|
78
|
-
bus.emit("progress", "cache: reporting from cache", c(8));
|
|
79
|
-
return await reportWrap(lCachedResults, lCruiseOptions);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (Boolean(lCruiseOptions.ruleSet)) {
|
|
84
|
-
bus.emit("progress", "parsing rule set", c(3));
|
|
85
|
-
const normalizeRuleSet = await import("./rule-set/normalize.mjs");
|
|
86
|
-
lCruiseOptions.ruleSet = normalizeRuleSet.default(
|
|
87
|
-
validateRuleSet(lCruiseOptions.ruleSet)
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const lNormalizedFileAndDirectoryArray = normalizeFilesAndDirectories(
|
|
92
|
-
pFileAndDirectoryArray
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
bus.emit("progress", "determining how to resolve", c(4));
|
|
96
|
-
let normalizeResolveOptions = await import("./resolve-options/normalize.mjs");
|
|
97
|
-
const lNormalizedResolveOptions = await normalizeResolveOptions.default(
|
|
98
|
-
pResolveOptions,
|
|
99
|
-
lCruiseOptions,
|
|
100
|
-
pTranspileOptions?.tsConfig
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
bus.emit("progress", "reading files", c(5));
|
|
104
|
-
const extract = await import("../extract/index.mjs");
|
|
105
|
-
const lExtractionResult = extract.default(
|
|
106
|
-
lNormalizedFileAndDirectoryArray,
|
|
107
|
-
lCruiseOptions,
|
|
108
|
-
lNormalizedResolveOptions,
|
|
109
|
-
pTranspileOptions
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
bus.emit("progress", "analyzing", c(6));
|
|
113
|
-
const enrich = await import("../enrich/index.mjs");
|
|
114
|
-
const lCruiseResult = enrich.default(
|
|
115
|
-
lExtractionResult,
|
|
116
|
-
lCruiseOptions,
|
|
117
|
-
lNormalizedFileAndDirectoryArray
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
if (lCruiseOptions.cache) {
|
|
121
|
-
bus.emit("progress", "cache: save", c(7));
|
|
122
|
-
lCache.write(lCruiseOptions.cache.folder, lCruiseResult);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
bus.emit("progress", "reporting", c(8));
|
|
126
|
-
return await reportWrap(lCruiseResult, lCruiseOptions);
|
|
127
|
-
}
|
|
5
|
+
import format from "./format.mjs";
|
|
6
|
+
import cruise from "./cruise.mjs";
|
|
128
7
|
|
|
129
8
|
export {
|
|
130
9
|
allExtensions,
|
|
131
10
|
getAvailableTranspilers,
|
|
132
11
|
} from "../extract/transpile/meta.mjs";
|
|
12
|
+
export { default as cruise } from "./cruise.mjs";
|
|
13
|
+
export { default as format } from "./format.mjs";
|
|
133
14
|
|
|
134
15
|
export default {
|
|
135
16
|
cruise,
|
|
@@ -84,10 +84,9 @@ async function compileResolveOptions(
|
|
|
84
84
|
// Also: requiring the plugin only when it's necessary will save some
|
|
85
85
|
// startup time (especially on a cold require cache)
|
|
86
86
|
if (pResolveOptions.tsConfig && isTsConfigPathsEligible(pTSConfig)) {
|
|
87
|
-
const
|
|
87
|
+
const { default: TsConfigPathsPlugin } = await import(
|
|
88
88
|
"tsconfig-paths-webpack-plugin"
|
|
89
89
|
);
|
|
90
|
-
const TsConfigPathsPlugin = TsConfigPathsPluginModule.default;
|
|
91
90
|
lResolveOptions.plugins = pushPlugin(
|
|
92
91
|
lResolveOptions.plugins,
|
|
93
92
|
// @ts-expect-error TS2351 "TsConfPathsPlugin is not constructable" - is unjustified
|
package/src/meta.js
CHANGED
|
@@ -127,6 +127,10 @@ export default {
|
|
|
127
127
|
criteria: { "rules[0].severity": "info" },
|
|
128
128
|
attributes: { fontcolor: "blue", color: "blue" },
|
|
129
129
|
},
|
|
130
|
+
{
|
|
131
|
+
criteria: { dynamic: true },
|
|
132
|
+
attributes: { style: "dashed" },
|
|
133
|
+
},
|
|
130
134
|
{
|
|
131
135
|
criteria: { valid: false },
|
|
132
136
|
attributes: { fontcolor: "red", color: "red" },
|
package/src/report/index.mjs
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the results of a cruise in JSON
|
|
3
|
+
*
|
|
4
|
+
* @param {import("../../types/cruise-result").ICruiseResult} pResults
|
|
5
|
+
* @returns {import("../../types/dependency-cruiser").IReporterOutput}
|
|
6
|
+
*/
|
|
7
|
+
export default function nullReporter(pResults) {
|
|
8
|
+
return {
|
|
9
|
+
output: "",
|
|
10
|
+
exitCode: pResults.summary.error,
|
|
11
|
+
};
|
|
12
|
+
}
|
package/src/utl/bus.mjs
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
|
+
/* eslint-disable import/exports-last */
|
|
1
2
|
import EventEmitter from "node:events";
|
|
2
3
|
|
|
3
|
-
const
|
|
4
|
+
export const OFF = -1;
|
|
5
|
+
export const SUMMARY = 40;
|
|
6
|
+
export const INFO = 50;
|
|
7
|
+
export const DEBUG = 60;
|
|
8
|
+
export const TRACE = 70;
|
|
9
|
+
export const EXTRA_STRONG = 80;
|
|
10
|
+
export const ALL = 99;
|
|
4
11
|
|
|
5
|
-
|
|
12
|
+
class Bus extends EventEmitter {
|
|
13
|
+
progress(pMessage, pOptions, ...pArguments) {
|
|
14
|
+
this.emit("progress", pMessage, pOptions, pArguments);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
summary(pMessage, pOptions, ...pArguments) {
|
|
18
|
+
this.progress(pMessage, { ...pOptions, level: SUMMARY }, pArguments);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
info(pMessage, pOptions, ...pArguments) {
|
|
22
|
+
this.progress(pMessage, { ...pOptions, level: INFO }, pArguments);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
debug(pMessage, pOptions, ...pArguments) {
|
|
26
|
+
this.progress(pMessage, { ...pOptions, level: DEBUG }, pArguments);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const bus = new Bus();
|