Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81164 views
1
module.exports = globSync
2
globSync.GlobSync = GlobSync
3
4
var fs = require("fs")
5
var minimatch = require("minimatch")
6
var Minimatch = minimatch.Minimatch
7
var Glob = require("./glob.js").Glob
8
var util = require("util")
9
var path = require("path")
10
var assert = require("assert")
11
var common = require("./common.js")
12
var alphasort = common.alphasort
13
var isAbsolute = common.isAbsolute
14
var setopts = common.setopts
15
var ownProp = common.ownProp
16
17
function globSync (pattern, options) {
18
if (typeof options === 'function' || arguments.length === 3)
19
throw new TypeError('callback provided to sync glob')
20
21
return new GlobSync(pattern, options).found
22
}
23
24
function GlobSync (pattern, options) {
25
if (!pattern)
26
throw new Error("must provide pattern")
27
28
if (typeof options === 'function' || arguments.length === 3)
29
throw new TypeError('callback provided to sync glob')
30
31
if (!(this instanceof GlobSync))
32
return new GlobSync(pattern, options)
33
34
setopts(this, pattern, options)
35
36
if (this.noprocess)
37
return this
38
39
var n = this.minimatch.set.length
40
this.matches = new Array(n)
41
for (var i = 0; i < n; i ++) {
42
this._process(this.minimatch.set[i], i, false)
43
}
44
this._finish()
45
}
46
47
GlobSync.prototype._finish = function () {
48
assert(this instanceof GlobSync)
49
common.finish(this)
50
}
51
52
53
GlobSync.prototype._process = function (pattern, index, inGlobStar) {
54
assert(this instanceof GlobSync)
55
56
// Get the first [n] parts of pattern that are all strings.
57
var n = 0
58
while (typeof pattern[n] === "string") {
59
n ++
60
}
61
// now n is the index of the first one that is *not* a string.
62
63
// See if there's anything else
64
var prefix
65
switch (n) {
66
// if not, then this is rather simple
67
case pattern.length:
68
this._processSimple(pattern.join('/'), index)
69
return
70
71
case 0:
72
// pattern *starts* with some non-trivial item.
73
// going to readdir(cwd), but not include the prefix in matches.
74
prefix = null
75
break
76
77
default:
78
// pattern has some string bits in the front.
79
// whatever it starts with, whether that's "absolute" like /foo/bar,
80
// or "relative" like "../baz"
81
prefix = pattern.slice(0, n).join("/")
82
break
83
}
84
85
var remain = pattern.slice(n)
86
87
// get the list of entries.
88
var read
89
if (prefix === null)
90
read = "."
91
else if (isAbsolute(prefix) || isAbsolute(pattern.join("/"))) {
92
if (!prefix || !isAbsolute(prefix))
93
prefix = "/" + prefix
94
read = prefix
95
} else
96
read = prefix
97
98
var abs = this._makeAbs(read)
99
100
var isGlobStar = remain[0] === minimatch.GLOBSTAR
101
if (isGlobStar)
102
this._processGlobStar(prefix, read, abs, remain, index, inGlobStar)
103
else
104
this._processReaddir(prefix, read, abs, remain, index, inGlobStar)
105
}
106
107
GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar) {
108
var entries = this._readdir(abs, inGlobStar)
109
110
// if the abs isn't a dir, then nothing can match!
111
if (!entries)
112
return
113
114
// It will only match dot entries if it starts with a dot, or if
115
// dot is set. Stuff like @(.foo|.bar) isn't allowed.
116
var pn = remain[0]
117
var negate = !!this.minimatch.negate
118
var rawGlob = pn._glob
119
var dotOk = this.dot || rawGlob.charAt(0) === "."
120
121
var matchedEntries = []
122
for (var i = 0; i < entries.length; i++) {
123
var e = entries[i]
124
if (e.charAt(0) !== "." || dotOk) {
125
var m
126
if (negate && !prefix) {
127
m = !e.match(pn)
128
} else {
129
m = e.match(pn)
130
}
131
if (m)
132
matchedEntries.push(e)
133
}
134
}
135
136
var len = matchedEntries.length
137
// If there are no matched entries, then nothing matches.
138
if (len === 0)
139
return
140
141
// if this is the last remaining pattern bit, then no need for
142
// an additional stat *unless* the user has specified mark or
143
// stat explicitly. We know they exist, since readdir returned
144
// them.
145
146
if (remain.length === 1 && !this.mark && !this.stat) {
147
if (!this.matches[index])
148
this.matches[index] = Object.create(null)
149
150
for (var i = 0; i < len; i ++) {
151
var e = matchedEntries[i]
152
if (prefix) {
153
if (prefix.slice(-1) !== "/")
154
e = prefix + "/" + e
155
else
156
e = prefix + e
157
}
158
159
if (e.charAt(0) === "/" && !this.nomount) {
160
e = path.join(this.root, e)
161
}
162
this.matches[index][e] = true
163
}
164
// This was the last one, and no stats were needed
165
return
166
}
167
168
// now test all matched entries as stand-ins for that part
169
// of the pattern.
170
remain.shift()
171
for (var i = 0; i < len; i ++) {
172
var e = matchedEntries[i]
173
var newPattern
174
if (prefix)
175
newPattern = [prefix, e]
176
else
177
newPattern = [e]
178
this._process(newPattern.concat(remain), index, inGlobStar)
179
}
180
}
181
182
183
GlobSync.prototype._emitMatch = function (index, e) {
184
if (!this.matches[index][e]) {
185
if (this.nodir) {
186
var c = this.cache[this._makeAbs(e)]
187
if (c === 'DIR' || Array.isArray(c))
188
return
189
}
190
191
this.matches[index][e] = true
192
if (this.stat || this.mark)
193
this._stat(this._makeAbs(e))
194
}
195
}
196
197
198
GlobSync.prototype._readdirInGlobStar = function (abs) {
199
var entries
200
var lstat
201
var stat
202
try {
203
lstat = fs.lstatSync(abs)
204
} catch (er) {
205
// lstat failed, doesn't exist
206
return null
207
}
208
209
var isSym = lstat.isSymbolicLink()
210
this.symlinks[abs] = isSym
211
212
// If it's not a symlink or a dir, then it's definitely a regular file.
213
// don't bother doing a readdir in that case.
214
if (!isSym && !lstat.isDirectory())
215
this.cache[abs] = 'FILE'
216
else
217
entries = this._readdir(abs, false)
218
219
return entries
220
}
221
222
GlobSync.prototype._readdir = function (abs, inGlobStar) {
223
var entries
224
225
if (inGlobStar && !ownProp(this.symlinks, abs))
226
return this._readdirInGlobStar(abs)
227
228
if (ownProp(this.cache, abs)) {
229
var c = this.cache[abs]
230
if (!c || c === 'FILE')
231
return null
232
233
if (Array.isArray(c))
234
return c
235
}
236
237
try {
238
return this._readdirEntries(abs, fs.readdirSync(abs).sort(alphasort))
239
} catch (er) {
240
this._readdirError(abs, er)
241
return null
242
}
243
}
244
245
GlobSync.prototype._readdirEntries = function (abs, entries) {
246
// if we haven't asked to stat everything, then just
247
// assume that everything in there exists, so we can avoid
248
// having to stat it a second time.
249
if (!this.mark && !this.stat) {
250
for (var i = 0; i < entries.length; i ++) {
251
var e = entries[i]
252
if (abs === "/")
253
e = abs + e
254
else
255
e = abs + "/" + e
256
this.cache[e] = true
257
}
258
}
259
260
this.cache[abs] = entries
261
262
// mark and cache dir-ness
263
return entries
264
}
265
266
GlobSync.prototype._readdirError = function (f, er) {
267
// handle errors, and cache the information
268
switch (er.code) {
269
case "ENOTDIR": // totally normal. means it *does* exist.
270
this.cache[f] = 'FILE'
271
break
272
273
case "ENOENT": // not terribly unusual
274
case "ELOOP":
275
case "ENAMETOOLONG":
276
case "UNKNOWN":
277
this.cache[f] = false
278
break
279
280
default: // some unusual error. Treat as failure.
281
this.cache[f] = false
282
if (this.strict) throw er
283
if (!this.silent) console.error("glob error", er)
284
break
285
}
286
}
287
288
GlobSync.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar) {
289
290
var entries = this._readdir(abs, inGlobStar)
291
292
// no entries means not a dir, so it can never have matches
293
// foo.txt/** doesn't match foo.txt
294
if (!entries)
295
return
296
297
// test without the globstar, and with every child both below
298
// and replacing the globstar.
299
var remainWithoutGlobStar = remain.slice(1)
300
var gspref = prefix ? [ prefix ] : []
301
var noGlobStar = gspref.concat(remainWithoutGlobStar)
302
303
// the noGlobStar pattern exits the inGlobStar state
304
this._process(noGlobStar, index, false)
305
306
var len = entries.length
307
var isSym = this.symlinks[abs]
308
309
// If it's a symlink, and we're in a globstar, then stop
310
if (isSym && inGlobStar)
311
return
312
313
for (var i = 0; i < len; i++) {
314
var e = entries[i]
315
if (e.charAt(0) === "." && !this.dot)
316
continue
317
318
// these two cases enter the inGlobStar state
319
var instead = gspref.concat(entries[i], remainWithoutGlobStar)
320
this._process(instead, index, true)
321
322
var below = gspref.concat(entries[i], remain)
323
this._process(below, index, true)
324
}
325
}
326
327
GlobSync.prototype._processSimple = function (prefix, index) {
328
// XXX review this. Shouldn't it be doing the mounting etc
329
// before doing stat? kinda weird?
330
var exists = this._stat(prefix)
331
332
if (!this.matches[index])
333
this.matches[index] = Object.create(null)
334
335
// If it doesn't exist, then just mark the lack of results
336
if (!exists)
337
return
338
339
if (prefix && isAbsolute(prefix) && !this.nomount) {
340
if (prefix.charAt(0) === "/") {
341
prefix = path.join(this.root, prefix)
342
} else {
343
prefix = path.resolve(this.root, prefix)
344
}
345
}
346
347
if (process.platform === "win32")
348
prefix = prefix.replace(/\\/g, "/")
349
350
// Mark this as a match
351
this.matches[index][prefix] = true
352
}
353
354
// Returns either 'DIR', 'FILE', or false
355
GlobSync.prototype._stat = function (f) {
356
var abs = f
357
if (f.charAt(0) === "/")
358
abs = path.join(this.root, f)
359
else if (this.changedCwd)
360
abs = path.resolve(this.cwd, f)
361
362
363
if (f.length > this.maxLength)
364
return false
365
366
if (!this.stat && ownProp(this.cache, f)) {
367
var c = this.cache[f]
368
369
if (Array.isArray(c))
370
c = 'DIR'
371
372
// It exists, but not how we need it
373
if (abs.slice(-1) === "/" && c !== 'DIR')
374
return false
375
376
return c
377
}
378
379
var exists
380
var stat = this.statCache[abs]
381
if (!stat) {
382
try {
383
stat = fs.statSync(abs)
384
} catch (er) {
385
return false
386
}
387
}
388
389
this.statCache[abs] = stat
390
391
if (abs.slice(-1) === "/" && !stat.isDirectory())
392
return false
393
394
var c = stat.isDirectory() ? 'DIR' : 'FILE'
395
this.cache[f] = this.cache[f] || c
396
return c
397
}
398
399
GlobSync.prototype._mark = function (p) {
400
return common.mark(this, p)
401
}
402
403
GlobSync.prototype._makeAbs = function (f) {
404
return common.makeAbs(this, f)
405
}
406
407