/
get-manifest.mjs
143 lines (129 loc) · 4.64 KB
/
get-manifest.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import { join, dirname, sep } from "node:path";
import { readFileSync } from "node:fs";
import memoize, { memoizeClear } from "memoize";
import mergePackages from "./merge-manifests.mjs";
/**
* return the contents of the package manifest ('package.json' closest to
* the passed folder (or null if there's no such package.json/ that
* package.json is invalid).
*
* This behavior is consistent with node's lookup mechanism
*
* @param {string} pFileDirectory the folder relative to which to find
* the package.json
* @return {any} the contents of the package.json as a javascript
* object or null if the package.json could not be
* found or is invalid
*/
const getSingleManifest = memoize((pFileDirectory) => {
let lReturnValue = null;
try {
// find the closest package.json from pFileDirectory
const lPackageContent = readFileSync(
join(pFileDirectory, "package.json"),
"utf8",
);
try {
lReturnValue = JSON.parse(lPackageContent);
} catch (pError) {
// left empty on purpose
}
} catch (pError) {
const lNextDirectory = dirname(pFileDirectory);
if (lNextDirectory !== pFileDirectory) {
// not yet reached root directory
lReturnValue = getSingleManifest(lNextDirectory);
}
}
return lReturnValue;
});
function maybeReadPackage(pFileDirectory) {
let lReturnValue = {};
try {
const lPackageContent = readFileSync(
join(pFileDirectory, "package.json"),
"utf8",
);
try {
lReturnValue = JSON.parse(lPackageContent);
} catch (pError) {
// left empty on purpose
}
} catch (pError) {
// left empty on purpose
}
return lReturnValue;
}
function getIntermediatePaths(pFileDirectory, pBaseDirectory) {
let lReturnValue = [];
let lIntermediate = pFileDirectory;
while (
lIntermediate !== pBaseDirectory &&
// safety hatch in case pBaseDirectory is either not a part of
// pFileDirectory or not something uniquely comparable to a
// dirname
lIntermediate !== dirname(lIntermediate)
) {
lReturnValue.push(lIntermediate);
lIntermediate = dirname(lIntermediate);
}
lReturnValue.push(pBaseDirectory);
return lReturnValue;
}
// despite the two parameters there's no resolver function provided
// to memoize. This is deliberate - the pBaseDirectory will typically
// be the same for each call in a typical cruise, so the default
// memoize resolver (the first param) will suffice.
const getCombinedManifests = memoize((pFileDirectory, pBaseDirectory) => {
// The way this is called, this shouldn't happen. If it is, there's
// something gone terribly awry
if (
!pFileDirectory.startsWith(pBaseDirectory) ||
pBaseDirectory.endsWith(sep)
) {
throw new Error(
`Unexpected Error: Unusual baseDir passed to package reading function: '${pBaseDirectory}'\n` +
`Please file a bug: https://github.com/sverweij/dependency-cruiser/issues/new?template=bug-report.md` +
`&title=Unexpected Error: Unusual baseDir passed to package reading function: '${pBaseDirectory}'`,
);
}
const lReturnValue = getIntermediatePaths(
pFileDirectory,
pBaseDirectory,
).reduce(
(pAll, pCurrent) => mergePackages(pAll, maybeReadPackage(pCurrent)),
{},
);
return Object.keys(lReturnValue).length > 0 ? lReturnValue : null;
});
/**
* return
* - the contents of the package manifest ('package.json') closest to the passed
* folder (see read-package-deps above) when pCombinedDependencies === false
* - the 'combined' contents of all manifests between the passed folder
* and the 'root' folder (the folder the cruise was run from) in
* all other cases
* @param {string} pFileDirectory the folder relative to which to find
* the (closest) package.json
* @param {string} pBaseDirectory the directory to consider as base (or 'root')
* @param {Boolean} pCombinedDependencies whether to stop (false) or continue
* searching until the 'root'
* @return {any} the contents of a package.json as a javascript
* object or null if a package.json could not be
* found or is invalid
*/
export function getManifest(
pFileDirectory,
pBaseDirectory,
pCombinedDependencies = false,
) {
if (pCombinedDependencies) {
return getCombinedManifests(pFileDirectory, pBaseDirectory);
} else {
return getSingleManifest(pFileDirectory);
}
}
export function clearCache() {
memoizeClear(getCombinedManifests);
memoizeClear(getSingleManifest);
}