The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.
Please select the desired protocol below to get the URL.
This URL has Read-Only access.
main_repo / deps / npm / node_modules / read-installed / read-installed.js @ 5aef65a9
History | View | Annotate | Download (9.36 KB)
1 |
|
---|---|
2 |
// Walk through the file-system "database" of installed
|
3 |
// packages, and create a data object related to the
|
4 |
// installed versions of each package.
|
5 |
|
6 |
/*
|
7 |
This will traverse through all node_modules folders,
|
8 |
resolving the dependencies object to the object corresponding to
|
9 |
the package that meets that dep, or just the version/range if
|
10 |
unmet.
|
11 |
|
12 |
Assuming that you had this folder structure:
|
13 |
|
14 |
/path/to
|
15 |
+-- package.json { name = "root" }
|
16 |
`-- node_modules
|
17 |
+-- foo {bar, baz, asdf}
|
18 |
| +-- node_modules
|
19 |
| +-- bar { baz }
|
20 |
| `-- baz
|
21 |
`-- asdf
|
22 |
|
23 |
where "foo" depends on bar, baz, and asdf, bar depends on baz,
|
24 |
and bar and baz are bundled with foo, whereas "asdf" is at
|
25 |
the higher level (sibling to foo), you'd get this object structure:
|
26 |
|
27 |
{ <package.json data>
|
28 |
, path: "/path/to"
|
29 |
, parent: null
|
30 |
, dependencies:
|
31 |
{ foo :
|
32 |
{ version: "1.2.3"
|
33 |
, path: "/path/to/node_modules/foo"
|
34 |
, parent: <Circular: root>
|
35 |
, dependencies:
|
36 |
{ bar:
|
37 |
{ parent: <Circular: foo>
|
38 |
, path: "/path/to/node_modules/foo/node_modules/bar"
|
39 |
, version: "2.3.4"
|
40 |
, dependencies: { baz: <Circular: foo.dependencies.baz> }
|
41 |
}
|
42 |
, baz: { ... }
|
43 |
, asdf: <Circular: asdf>
|
44 |
}
|
45 |
}
|
46 |
, asdf: { ... }
|
47 |
}
|
48 |
}
|
49 |
|
50 |
Unmet deps are left as strings.
|
51 |
Extraneous deps are marked with extraneous:true
|
52 |
deps that don't meet a requirement are marked with invalid:true
|
53 |
deps that don't meet a peer requirement are marked with peerInvalid:true
|
54 |
|
55 |
to READ(packagefolder, parentobj, name, reqver)
|
56 |
obj = read package.json
|
57 |
installed = ./node_modules/*
|
58 |
if parentobj is null, and no package.json
|
59 |
obj = {dependencies:{<installed>:"*"}}
|
60 |
deps = Object.keys(obj.dependencies)
|
61 |
obj.path = packagefolder
|
62 |
obj.parent = parentobj
|
63 |
if name, && obj.name !== name, obj.invalid = true
|
64 |
if reqver, && obj.version !satisfies reqver, obj.invalid = true
|
65 |
if !reqver && parentobj, obj.extraneous = true
|
66 |
for each folder in installed
|
67 |
obj.dependencies[folder] = READ(packagefolder+node_modules+folder,
|
68 |
obj, folder, obj.dependencies[folder])
|
69 |
# walk tree to find unmet deps
|
70 |
for each dep in obj.dependencies not in installed
|
71 |
r = obj.parent
|
72 |
while r
|
73 |
if r.dependencies[dep]
|
74 |
if r.dependencies[dep].verion !satisfies obj.dependencies[dep]
|
75 |
WARN
|
76 |
r.dependencies[dep].invalid = true
|
77 |
obj.dependencies[dep] = r.dependencies[dep]
|
78 |
r = null
|
79 |
else r = r.parent
|
80 |
return obj
|
81 |
|
82 |
|
83 |
TODO:
|
84 |
1. Find unmet deps in parent directories, searching as node does up
|
85 |
as far as the left-most node_modules folder.
|
86 |
2. Ignore anything in node_modules that isn't a package folder.
|
87 |
|
88 |
*/
|
89 |
|
90 |
try {
|
91 |
var fs = require("graceful-fs") |
92 |
} catch (er) {
|
93 |
var fs = require("fs") |
94 |
} |
95 |
|
96 |
try {
|
97 |
var log = require("npmlog") |
98 |
} catch (_) {
|
99 |
var log = { verbose: noop, info: noop, warn: noop, error: noop } |
100 |
function noop () {} |
101 |
} |
102 |
|
103 |
var path = require("path") |
104 |
var asyncMap = require("slide").asyncMap |
105 |
var semver = require("semver") |
106 |
var readJson = require("read-package-json") |
107 |
var url = require("url") |
108 |
|
109 |
module.exports = readInstalled |
110 |
|
111 |
function readInstalled (folder, depth, cb) { |
112 |
if (typeof cb !== "function") cb = depth, depth = Infinity |
113 |
readInstalled_(folder, null, null, null, 0, depth, function (er, obj) { |
114 |
if (er) return cb(er) |
115 |
// now obj has all the installed things, where they're installed
|
116 |
// figure out the inheritance links, now that the object is built.
|
117 |
resolveInheritance(obj) |
118 |
cb(null, obj)
|
119 |
}) |
120 |
} |
121 |
|
122 |
var rpSeen = {}
|
123 |
function readInstalled_ (folder, parent, name, reqver, depth, maxDepth, cb) { |
124 |
var installed
|
125 |
, obj |
126 |
, real |
127 |
, link |
128 |
|
129 |
fs.readdir(path.resolve(folder, "node_modules"), function (er, i) { |
130 |
// error indicates that nothing is installed here
|
131 |
if (er) i = []
|
132 |
installed = i.filter(function (f) { return f.charAt(0) !== "." }) |
133 |
next() |
134 |
}) |
135 |
|
136 |
readJson(path.resolve(folder, "package.json"), function (er, data) { |
137 |
obj = copy(data) |
138 |
|
139 |
if (!parent) {
|
140 |
obj = obj || true
|
141 |
er = null
|
142 |
} |
143 |
return next(er)
|
144 |
}) |
145 |
|
146 |
fs.lstat(folder, function (er, st) {
|
147 |
if (er) {
|
148 |
if (!parent) real = true |
149 |
return next(er)
|
150 |
} |
151 |
fs.realpath(folder, function (er, rp) {
|
152 |
//console.error("realpath(%j) = %j", folder, rp)
|
153 |
real = rp |
154 |
if (st.isSymbolicLink()) link = rp
|
155 |
next(er) |
156 |
}) |
157 |
}) |
158 |
|
159 |
var errState = null |
160 |
, called = false
|
161 |
function next (er) { |
162 |
if (errState) return |
163 |
if (er) {
|
164 |
errState = er |
165 |
return cb(null, []) |
166 |
} |
167 |
//console.error('next', installed, obj && typeof obj, name, real)
|
168 |
if (!installed || !obj || !real || called) return |
169 |
called = true
|
170 |
if (rpSeen[real]) return cb(null, rpSeen[real]) |
171 |
if (obj === true) { |
172 |
obj = {dependencies:{}, path:folder} |
173 |
installed.forEach(function (i) { obj.dependencies[i] = "*" }) |
174 |
} |
175 |
if (name && obj.name !== name) obj.invalid = true |
176 |
obj.realName = name || obj.name |
177 |
obj.dependencies = obj.dependencies || {} |
178 |
|
179 |
// "foo":"http://blah" is always presumed valid
|
180 |
if (reqver
|
181 |
&& semver.validRange(reqver) |
182 |
&& !semver.satisfies(obj.version, reqver)) { |
183 |
obj.invalid = true
|
184 |
} |
185 |
|
186 |
if (parent
|
187 |
&& !(name in parent.dependencies)
|
188 |
&& !(name in (parent.devDependencies || {}))) {
|
189 |
obj.extraneous = true
|
190 |
} |
191 |
obj.path = obj.path || folder |
192 |
obj.realPath = real |
193 |
obj.link = link |
194 |
if (parent && !obj.link) obj.parent = parent
|
195 |
rpSeen[real] = obj |
196 |
obj.depth = depth |
197 |
//if (depth >= maxDepth) return cb(null, obj)
|
198 |
asyncMap(installed, function (pkg, cb) {
|
199 |
var rv = obj.dependencies[pkg]
|
200 |
if (!rv && obj.devDependencies) rv = obj.devDependencies[pkg]
|
201 |
if (depth >= maxDepth) {
|
202 |
// just try to get the version number
|
203 |
var pkgfolder = path.resolve(folder, "node_modules", pkg) |
204 |
, jsonFile = path.resolve(pkgfolder, "package.json")
|
205 |
return readJson(jsonFile, function (er, depData) { |
206 |
// already out of our depth, ignore errors
|
207 |
if (er || !depData || !depData.version) return cb(null, obj) |
208 |
obj.dependencies[pkg] = depData.version |
209 |
cb(null, obj)
|
210 |
}) |
211 |
} |
212 |
|
213 |
readInstalled_( path.resolve(folder, "node_modules/"+pkg)
|
214 |
, obj, pkg, obj.dependencies[pkg], depth + 1, maxDepth
|
215 |
, cb ) |
216 |
|
217 |
}, function (er, installedData) {
|
218 |
if (er) return cb(er) |
219 |
installedData.forEach(function (dep) {
|
220 |
obj.dependencies[dep.realName] = dep |
221 |
}) |
222 |
|
223 |
// any strings here are unmet things. however, if it's
|
224 |
// optional, then that's fine, so just delete it.
|
225 |
if (obj.optionalDependencies) {
|
226 |
Object.keys(obj.optionalDependencies).forEach(function (dep) {
|
227 |
if (typeof obj.dependencies[dep] === "string") { |
228 |
delete obj.dependencies[dep]
|
229 |
} |
230 |
}) |
231 |
} |
232 |
return cb(null, obj) |
233 |
}) |
234 |
} |
235 |
} |
236 |
|
237 |
// starting from a root object, call findUnmet on each layer of children
|
238 |
var riSeen = []
|
239 |
function resolveInheritance (obj) { |
240 |
if (typeof obj !== "object") return |
241 |
if (riSeen.indexOf(obj) !== -1) return |
242 |
riSeen.push(obj) |
243 |
if (typeof obj.dependencies !== "object") { |
244 |
obj.dependencies = {} |
245 |
} |
246 |
Object.keys(obj.dependencies).forEach(function (dep) {
|
247 |
findUnmet(obj.dependencies[dep]) |
248 |
}) |
249 |
Object.keys(obj.dependencies).forEach(function (dep) {
|
250 |
resolveInheritance(obj.dependencies[dep]) |
251 |
}) |
252 |
findUnmet(obj) |
253 |
} |
254 |
|
255 |
// find unmet deps by walking up the tree object.
|
256 |
// No I/O
|
257 |
var fuSeen = []
|
258 |
function findUnmet (obj) { |
259 |
if (fuSeen.indexOf(obj) !== -1) return |
260 |
fuSeen.push(obj) |
261 |
//console.error("find unmet", obj.name, obj.parent && obj.parent.name)
|
262 |
var deps = obj.dependencies = obj.dependencies || {}
|
263 |
|
264 |
//console.error(deps)
|
265 |
Object.keys(deps) |
266 |
.filter(function (d) { return typeof deps[d] === "string" }) |
267 |
.forEach(function (d) {
|
268 |
//console.error("find unmet", obj.name, d, deps[d])
|
269 |
var r = obj.parent
|
270 |
, found = null
|
271 |
while (r && !found && typeof deps[d] === "string") { |
272 |
// if r is a valid choice, then use that.
|
273 |
found = r.dependencies[d] |
274 |
if (!found && r.realName === d) found = r
|
275 |
|
276 |
if (!found) {
|
277 |
r = r.link ? null : r.parent
|
278 |
continue
|
279 |
} |
280 |
if ( typeof deps[d] === "string" |
281 |
// url deps presumed innocent.
|
282 |
&& !url.parse(deps[d]).protocol |
283 |
&& !semver.satisfies(found.version, deps[d])) { |
284 |
// the bad thing will happen
|
285 |
log.warn("unmet dependency", obj.path + " requires "+d+"@'"+deps[d] |
286 |
+"' but will load\n"
|
287 |
+found.path+",\nwhich is version "+found.version
|
288 |
) |
289 |
found.invalid = true
|
290 |
} else {
|
291 |
found.extraneous = false
|
292 |
} |
293 |
deps[d] = found |
294 |
} |
295 |
|
296 |
}) |
297 |
|
298 |
var peerDeps = obj.peerDependencies = obj.peerDependencies || {}
|
299 |
Object.keys(peerDeps).forEach(function (d) {
|
300 |
var dependency
|
301 |
|
302 |
if (!obj.parent) {
|
303 |
dependency = obj.dependencies[d] |
304 |
|
305 |
// read it as a missing dep
|
306 |
if (!dependency) {
|
307 |
obj.dependencies[d] = peerDeps[d] |
308 |
} |
309 |
} else {
|
310 |
dependency = obj.parent.dependencies && obj.parent.dependencies[d] |
311 |
} |
312 |
|
313 |
if (!dependency) return |
314 |
|
315 |
dependency.extraneous = false
|
316 |
|
317 |
if (!semver.satisfies(dependency.version, peerDeps[d])) {
|
318 |
dependency.peerInvalid = true
|
319 |
} |
320 |
}) |
321 |
|
322 |
log.verbose("readInstalled", "returning", obj._id) |
323 |
return obj
|
324 |
} |
325 |
|
326 |
function copy (obj) { |
327 |
if (!obj || typeof obj !== 'object') return obj |
328 |
if (Array.isArray(obj)) return obj.map(copy) |
329 |
|
330 |
var o = {}
|
331 |
for (var i in obj) o[i] = copy(obj[i]) |
332 |
return o
|
333 |
} |