minimatch 0.0.4 → 0.0.5
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/.travis.yml +4 -0
- package/README.md +7 -0
- package/blerg.js +62 -0
- package/minimatch.js +45 -72
- package/package.json +1 -4
- package/t.js +4 -0
- package/test/basic.js +31 -5
- package/.gitmodules +0 -3
- package/node_modules/lru-cache/LICENSE +0 -23
- package/node_modules/lru-cache/README.md +0 -12
- package/node_modules/lru-cache/lib/lru-cache.js +0 -151
- package/node_modules/lru-cache/package.json +0 -13
package/.travis.yml
ADDED
package/README.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# minimatch
|
|
2
|
+
|
|
3
|
+
A minimal matching utility.
|
|
4
|
+
|
|
5
|
+
[](http://travis-ci.org/isaacs/minimatch)
|
|
6
|
+
|
|
7
|
+
|
|
1
8
|
This is the matching library used internally by npm.
|
|
2
9
|
|
|
3
10
|
Eventually, it will replace the C binding in node-glob.
|
package/blerg.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
|
|
2
|
+
// Turn something like {a,b{c,d},}x{e,f} into
|
|
3
|
+
// ["axe", "axf", "bcxe", "bcxf", "bdxe", "bdxf", "xe", "xf"]
|
|
4
|
+
// Only {,} groups are expanded. While in many cases {x,y} is
|
|
5
|
+
// functionally equivalent to @(x|y), for the purpose of globbing
|
|
6
|
+
// files, only {x,y} gets expanded as multiple patterns.
|
|
7
|
+
minimatch.patternSet = patternSet
|
|
8
|
+
function patternSet (pattern) {
|
|
9
|
+
if (!pattern.match(/{/) || !pattern.match(/}/)) {
|
|
10
|
+
// shortcut - no sets.
|
|
11
|
+
return [pattern]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// a{b,c{d,e},{f,g}h}x{y,z}
|
|
15
|
+
//
|
|
16
|
+
// t=[before set, set, after set]
|
|
17
|
+
// t=["a", ["b", "c{d,e}", "{f,g}h"], "x{y,z}"]
|
|
18
|
+
|
|
19
|
+
// start walking, and note the position of the first {
|
|
20
|
+
// and the corresponding }
|
|
21
|
+
var p = pattern.indexOf("{")
|
|
22
|
+
, l = pattern.length
|
|
23
|
+
, d = 0
|
|
24
|
+
, escaping = false
|
|
25
|
+
, inClass = false
|
|
26
|
+
while (++ p < l) {
|
|
27
|
+
switch (pattern.charAt(p)) {
|
|
28
|
+
case "{":
|
|
29
|
+
d ++
|
|
30
|
+
continue
|
|
31
|
+
case "}":
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
// t[2] = patternSet(t[2])
|
|
36
|
+
// t = [t[0]].concat([t[1].map(patternSet)]).concat([t[2]])
|
|
37
|
+
//
|
|
38
|
+
// t=["a",[["b"],[["cd","ce"]],[["fh","gh"]]],["xy","xz"]]
|
|
39
|
+
//
|
|
40
|
+
// // first turn into
|
|
41
|
+
// // [["ab"], ["acd", "ace"], ["afh", "agh"]]
|
|
42
|
+
// return t[1].map(function (p) {
|
|
43
|
+
// return p.map(function (p) {
|
|
44
|
+
// return t[0] + p
|
|
45
|
+
// })
|
|
46
|
+
// })
|
|
47
|
+
// // flatten into ["ab", "acd", "ace", "afh", "agh"]
|
|
48
|
+
// .reduce(function (l, r) {
|
|
49
|
+
// return l.concat(r)
|
|
50
|
+
// }, [])
|
|
51
|
+
// // tack all the endings onto each one
|
|
52
|
+
// .map(function (p) {
|
|
53
|
+
// return t[2].map(function (e) {
|
|
54
|
+
// return p + e
|
|
55
|
+
// })
|
|
56
|
+
// })
|
|
57
|
+
// // flatten again
|
|
58
|
+
// .reduce(function (l, r) {
|
|
59
|
+
// return l.concat(r)
|
|
60
|
+
// }, [])
|
|
61
|
+
}
|
|
62
|
+
|
package/minimatch.js
CHANGED
|
@@ -17,6 +17,7 @@ minimatch.filter = function (pattern, options) {
|
|
|
17
17
|
minimatch.match = function (list, pattern, options) {
|
|
18
18
|
if (!options) options = {}
|
|
19
19
|
var ret = list.filter(minimatch.filter(pattern, options))
|
|
20
|
+
if (options.debug) console.error("\nmatch: %s %j %j", pattern, list, ret)
|
|
20
21
|
|
|
21
22
|
// set the null flag to allow empty match sets
|
|
22
23
|
// Note that minimatch itself, and filter(), do not
|
|
@@ -47,7 +48,7 @@ function minimatch (p, pattern, options) {
|
|
|
47
48
|
if (pattern.trim().charAt(0) === "#") return false
|
|
48
49
|
|
|
49
50
|
// check the cache
|
|
50
|
-
var re = cache.get(pattern)
|
|
51
|
+
var re = cache.get(pattern + "\n" + JSON.stringify(options))
|
|
51
52
|
if (!re && re !== false) {
|
|
52
53
|
cache.set(pattern, re = minimatch.makeRe(pattern, options))
|
|
53
54
|
}
|
|
@@ -93,18 +94,45 @@ minimatch.makeRe = makeRe
|
|
|
93
94
|
function makeRe (pattern, options) {
|
|
94
95
|
options = options || {}
|
|
95
96
|
|
|
97
|
+
function clearStateChar () {
|
|
98
|
+
if (stateChar) {
|
|
99
|
+
// we had some state-tracking character
|
|
100
|
+
// that wasn't consumed by this pass.
|
|
101
|
+
switch (stateChar) {
|
|
102
|
+
case "*":
|
|
103
|
+
re += oneStar
|
|
104
|
+
break
|
|
105
|
+
case "?":
|
|
106
|
+
re += "."
|
|
107
|
+
break
|
|
108
|
+
default:
|
|
109
|
+
re += "\\"+stateChar
|
|
110
|
+
break
|
|
111
|
+
}
|
|
112
|
+
stateChar = false
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
96
116
|
var braceDepth = 0
|
|
97
117
|
, re = ""
|
|
98
118
|
, escaping = false
|
|
99
|
-
, oneStar = "[^\\/]*?"
|
|
100
|
-
|
|
119
|
+
, oneStar = options.dot ? "[^\\/]*?"
|
|
120
|
+
: "(?:(?!(?:\\\/|^)\\.)[^\\/])*?"
|
|
121
|
+
, twoStar = options.dot ? ".*?"
|
|
122
|
+
// not a ^ or / followed by a dot,
|
|
123
|
+
// followed by anything, any number of times.
|
|
124
|
+
: "(?:(?!(?:\\\/|^)\\.).)*?"
|
|
101
125
|
, reSpecials = "().*{}+?[]^$/\\"
|
|
102
126
|
, patternListStack = []
|
|
103
127
|
, stateChar
|
|
104
128
|
, negate = false
|
|
105
129
|
, negating = false
|
|
106
130
|
, inClass = false
|
|
107
|
-
, reClassStart =
|
|
131
|
+
, reClassStart = -1
|
|
132
|
+
, classStart = -1
|
|
133
|
+
, classStartPattern = options.dot ? ""
|
|
134
|
+
: "(?:(?!(?:\\\/|^)\\.)"
|
|
135
|
+
, classEndPattern = options.dot ? "" : ")"
|
|
108
136
|
|
|
109
137
|
for ( var i = 0, len = pattern.length, c
|
|
110
138
|
; (i < len) && (c = pattern.charAt(i))
|
|
@@ -129,6 +157,7 @@ function makeRe (pattern, options) {
|
|
|
129
157
|
}
|
|
130
158
|
continue
|
|
131
159
|
|
|
160
|
+
// the various stateChar values
|
|
132
161
|
case "!":
|
|
133
162
|
if (i === 0 || negating) {
|
|
134
163
|
negate = !negate
|
|
@@ -213,21 +242,8 @@ function makeRe (pattern, options) {
|
|
|
213
242
|
|
|
214
243
|
// these are mostly the same in regexp and glob :)
|
|
215
244
|
case "[":
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
switch (stateChar) {
|
|
219
|
-
case "*":
|
|
220
|
-
re += oneStar
|
|
221
|
-
break
|
|
222
|
-
case "?":
|
|
223
|
-
re += "."
|
|
224
|
-
break
|
|
225
|
-
default:
|
|
226
|
-
re += "\\"+stateChar
|
|
227
|
-
break
|
|
228
|
-
}
|
|
229
|
-
stateChar = false
|
|
230
|
-
}
|
|
245
|
+
// swallow any state-tracking char before the [
|
|
246
|
+
clearStateChar()
|
|
231
247
|
|
|
232
248
|
if (escaping || inClass) {
|
|
233
249
|
re += "\\" + c
|
|
@@ -236,6 +252,7 @@ function makeRe (pattern, options) {
|
|
|
236
252
|
inClass = true
|
|
237
253
|
classStart = i
|
|
238
254
|
reClassStart = re.length
|
|
255
|
+
re += classStartPattern
|
|
239
256
|
re += c
|
|
240
257
|
}
|
|
241
258
|
continue
|
|
@@ -252,7 +269,7 @@ function makeRe (pattern, options) {
|
|
|
252
269
|
escaping = false
|
|
253
270
|
} else {
|
|
254
271
|
inClass = false
|
|
255
|
-
re += c
|
|
272
|
+
re += c + classEndPattern
|
|
256
273
|
}
|
|
257
274
|
continue
|
|
258
275
|
|
|
@@ -271,6 +288,8 @@ function makeRe (pattern, options) {
|
|
|
271
288
|
re += "\\}"
|
|
272
289
|
escaping = false
|
|
273
290
|
} else {
|
|
291
|
+
// swallow any state char that wasn't consumed
|
|
292
|
+
clearStateChar()
|
|
274
293
|
re += ")"
|
|
275
294
|
braceDepth --
|
|
276
295
|
}
|
|
@@ -281,27 +300,15 @@ function makeRe (pattern, options) {
|
|
|
281
300
|
re += ","
|
|
282
301
|
escaping = false
|
|
283
302
|
} else {
|
|
303
|
+
// swallow any state char that wasn't consumed
|
|
304
|
+
clearStateChar()
|
|
284
305
|
re += "|"
|
|
285
306
|
}
|
|
286
307
|
continue
|
|
287
308
|
|
|
288
309
|
default:
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
// that wasn't consumed by this pass.
|
|
292
|
-
switch (stateChar) {
|
|
293
|
-
case "*":
|
|
294
|
-
re += oneStar
|
|
295
|
-
break
|
|
296
|
-
case "?":
|
|
297
|
-
re += "."
|
|
298
|
-
break
|
|
299
|
-
default:
|
|
300
|
-
re += "\\"+stateChar
|
|
301
|
-
break
|
|
302
|
-
}
|
|
303
|
-
stateChar = false
|
|
304
|
-
}
|
|
310
|
+
// swallow any state char that wasn't consumed
|
|
311
|
+
clearStateChar()
|
|
305
312
|
|
|
306
313
|
if (escaping) {
|
|
307
314
|
// no need
|
|
@@ -321,20 +328,7 @@ function makeRe (pattern, options) {
|
|
|
321
328
|
|
|
322
329
|
// handle trailing things that only matter at the very end.
|
|
323
330
|
if (stateChar) {
|
|
324
|
-
|
|
325
|
-
// that wasn't consumed by this pass.
|
|
326
|
-
switch (stateChar) {
|
|
327
|
-
case "*":
|
|
328
|
-
re += oneStar
|
|
329
|
-
break
|
|
330
|
-
case "?":
|
|
331
|
-
re += "."
|
|
332
|
-
break
|
|
333
|
-
default:
|
|
334
|
-
re += "\\"+stateChar
|
|
335
|
-
break
|
|
336
|
-
}
|
|
337
|
-
stateChar = false
|
|
331
|
+
clearStateChar()
|
|
338
332
|
} else if (escaping) {
|
|
339
333
|
re += "\\\\"
|
|
340
334
|
}
|
|
@@ -345,7 +339,7 @@ function makeRe (pattern, options) {
|
|
|
345
339
|
// this is a huge pita. We now have to re-walk
|
|
346
340
|
// the contents of the would-be class to re-translate
|
|
347
341
|
// any characters that were passed through as-is
|
|
348
|
-
var cs = re.substr(reClassStart + 1)
|
|
342
|
+
var cs = re.substr(reClassStart + classStartPattern.length + 1)
|
|
349
343
|
, csOpts = Object.create(options)
|
|
350
344
|
csOpts.partial = true
|
|
351
345
|
|
|
@@ -355,11 +349,6 @@ function makeRe (pattern, options) {
|
|
|
355
349
|
|
|
356
350
|
if (options.partial) return re
|
|
357
351
|
|
|
358
|
-
// don't match "." files unless pattern starts with "."
|
|
359
|
-
if (!options.dot && pattern.charAt(0) !== ".") {
|
|
360
|
-
re = "(?!\\.)" + re
|
|
361
|
-
}
|
|
362
|
-
|
|
363
352
|
// must match entire pattern
|
|
364
353
|
// ending in a * or ** will make it less strict.
|
|
365
354
|
re = "^" + re + "$"
|
|
@@ -381,19 +370,3 @@ function makeRe (pattern, options) {
|
|
|
381
370
|
return false
|
|
382
371
|
}
|
|
383
372
|
}
|
|
384
|
-
|
|
385
|
-
if (require.main === module) {
|
|
386
|
-
// more tests in test/*.js
|
|
387
|
-
var tests = ["{a,b{c,d}}"
|
|
388
|
-
,"a.*$?"
|
|
389
|
-
,"\\{a,b{c,d}}"
|
|
390
|
-
,"a/{c/,}d/{e/,f/{g,h,i}/}k"
|
|
391
|
-
,"!*.bak"
|
|
392
|
-
,"!!*.bak"
|
|
393
|
-
,"!!!*.bak"
|
|
394
|
-
,"\\a\\b\\c\\d"
|
|
395
|
-
]
|
|
396
|
-
tests.forEach(function (t) {
|
|
397
|
-
console.log([t,makeRe(t)])
|
|
398
|
-
})
|
|
399
|
-
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
|
|
3
3
|
"name": "minimatch",
|
|
4
4
|
"description": "a glob matcher in javascript",
|
|
5
|
-
"version": "0.0.
|
|
5
|
+
"version": "0.0.5",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git://github.com/isaacs/minimatch.git"
|
|
@@ -14,9 +14,6 @@
|
|
|
14
14
|
"engines": {
|
|
15
15
|
"node": "*"
|
|
16
16
|
},
|
|
17
|
-
"bundleDependencies": [
|
|
18
|
-
"lru-cache"
|
|
19
|
-
],
|
|
20
17
|
"dependencies": {
|
|
21
18
|
"lru-cache": "~1.0.2"
|
|
22
19
|
},
|
package/t.js
ADDED
package/test/basic.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// http://www.bashcookbook.com/bashinfo/source/bash-1.14.7/tests/glob-test
|
|
2
2
|
|
|
3
3
|
var tap = require("tap")
|
|
4
|
+
, globalBefore = Object.keys(global)
|
|
4
5
|
, mm = require("../")
|
|
5
6
|
, files = [ "a", "b", "c", "d", "abc"
|
|
6
7
|
, "abd", "abe", "bb", "bcd"
|
|
@@ -10,6 +11,8 @@ var tap = require("tap")
|
|
|
10
11
|
, ".x", ".y" ])
|
|
11
12
|
|
|
12
13
|
tap.test("basic tests", function (t) {
|
|
14
|
+
var start = Date.now()
|
|
15
|
+
|
|
13
16
|
// [ pattern, [matches], MM opts, files, TAP opts]
|
|
14
17
|
; [ "http://www.bashcookbook.com/bashinfo" +
|
|
15
18
|
"/source/bash-1.14.7/tests/glob-test"
|
|
@@ -32,13 +35,13 @@ tap.test("basic tests", function (t) {
|
|
|
32
35
|
, ["\\.\\./*/", ["\\.\\./*/"]]
|
|
33
36
|
, ["s/\\..*//", ["s/\\..*//"]]
|
|
34
37
|
|
|
35
|
-
|
|
38
|
+
, "legendary larry crashes bashes"
|
|
36
39
|
, ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\\1/"
|
|
37
40
|
, ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\\1/"]]
|
|
38
41
|
, ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/"
|
|
39
42
|
, ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/"]]
|
|
40
43
|
|
|
41
|
-
|
|
44
|
+
, "character classes"
|
|
42
45
|
, ["[a-c]b*", ["abc", "abd", "abe", "bb", "cb"]]
|
|
43
46
|
, ["[a-y]*[^c]", ["abd", "abe", "bb", "bcd",
|
|
44
47
|
"bdir/", "ca", "cb", "dd", "de"]]
|
|
@@ -87,8 +90,8 @@ tap.test("basic tests", function (t) {
|
|
|
87
90
|
, ["[[]", ["["], null, ["["]]
|
|
88
91
|
, ["[", ["["], null, ["["]]
|
|
89
92
|
, ["[*", ["[abc"], null, ["[abc"]]
|
|
90
|
-
, "a right bracket shall lose its special meaning and
|
|
91
|
-
"represent itself in a bracket expression if it occurs
|
|
93
|
+
, "a right bracket shall lose its special meaning and\n" +
|
|
94
|
+
"represent itself in a bracket expression if it occurs\n" +
|
|
92
95
|
"first in the list. -- POSIX.2 2.8.3.2"
|
|
93
96
|
, ["[]]", ["]"], null, ["]"]]
|
|
94
97
|
, ["[]-]", ["]"], null, ["]"]]
|
|
@@ -111,6 +114,21 @@ tap.test("basic tests", function (t) {
|
|
|
111
114
|
, ["[ia]?[ck]", ["ABC", "IjK"], { nocase: true, null: true }
|
|
112
115
|
, ["xYz", "ABC", "IjK"]]
|
|
113
116
|
|
|
117
|
+
// [ pattern, [matches], MM opts, files, TAP opts]
|
|
118
|
+
, "onestar/twostar"
|
|
119
|
+
, ["{/*,*}", [], {null: true}, ["/asdf/asdf/asdf"]]
|
|
120
|
+
, ["{/?,*}", ["/a", "bb"], {null: true}
|
|
121
|
+
, ["/a", "/b/b", "/a/b/c", "bb"]]
|
|
122
|
+
|
|
123
|
+
, "dots should not match unless requested"
|
|
124
|
+
, ["**", ["a/b"], {}, ["a/b", "a/.d", ".a/.d"]]
|
|
125
|
+
|
|
126
|
+
// this also tests that changing the options needs
|
|
127
|
+
// to change the cache key, even if the pattern is
|
|
128
|
+
// the same!
|
|
129
|
+
, ["**", ["a/b","a/.d",".a/.d"], { dot: true }
|
|
130
|
+
, [ ".a/.d", "a/.d", "a/b"]]
|
|
131
|
+
|
|
114
132
|
].forEach(function (c) {
|
|
115
133
|
if (typeof c === "function") return c()
|
|
116
134
|
if (typeof c === "string") return t.comment(c)
|
|
@@ -131,8 +149,16 @@ tap.test("basic tests", function (t) {
|
|
|
131
149
|
|
|
132
150
|
t.equivalent( actual, expect
|
|
133
151
|
, JSON.stringify(pattern) + " " + JSON.stringify(expect)
|
|
134
|
-
,
|
|
152
|
+
, tapOpts )
|
|
135
153
|
})
|
|
154
|
+
|
|
155
|
+
t.comment("time=" + (Date.now() - start) + "ms")
|
|
156
|
+
t.end()
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
tap.test("global leak test", function (t) {
|
|
160
|
+
var globalAfter = Object.keys(global)
|
|
161
|
+
t.equivalent(globalAfter, globalBefore, "no new globals, please")
|
|
136
162
|
t.end()
|
|
137
163
|
})
|
|
138
164
|
|
package/.gitmodules
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
|
|
2
|
-
All rights reserved.
|
|
3
|
-
|
|
4
|
-
Permission is hereby granted, free of charge, to any person
|
|
5
|
-
obtaining a copy of this software and associated documentation
|
|
6
|
-
files (the "Software"), to deal in the Software without
|
|
7
|
-
restriction, including without limitation the rights to use,
|
|
8
|
-
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the
|
|
10
|
-
Software is furnished to do so, subject to the following
|
|
11
|
-
conditions:
|
|
12
|
-
|
|
13
|
-
The above copyright notice and this permission notice shall be
|
|
14
|
-
included in all copies or substantial portions of the Software.
|
|
15
|
-
|
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
18
|
-
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
20
|
-
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
21
|
-
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
22
|
-
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
23
|
-
OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# lru cache
|
|
2
|
-
|
|
3
|
-
A cache object that deletes the least-recently-used items.
|
|
4
|
-
|
|
5
|
-
Usage:
|
|
6
|
-
|
|
7
|
-
var LRU = require("lru-cache")
|
|
8
|
-
, cache = LRU(10) // max 10 items. default = Infinity
|
|
9
|
-
cache.set("key", "value")
|
|
10
|
-
cache.get("key") // "value"
|
|
11
|
-
|
|
12
|
-
RTFS for more info.
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
module.exports = LRUCache
|
|
3
|
-
|
|
4
|
-
function hOP (obj, key) {
|
|
5
|
-
return Object.prototype.hasOwnProperty.call(obj, key)
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
function LRUCache (maxLength) {
|
|
9
|
-
if (!(this instanceof LRUCache)) {
|
|
10
|
-
return new LRUCache(maxLength)
|
|
11
|
-
}
|
|
12
|
-
var cache = {} // hash of items by key
|
|
13
|
-
, lruList = {} // list of items in order of use recency
|
|
14
|
-
, lru = 0 // least recently used
|
|
15
|
-
, mru = 0 // most recently used
|
|
16
|
-
, length = 0 // number of items in the list
|
|
17
|
-
|
|
18
|
-
// resize the cache when the maxLength changes.
|
|
19
|
-
Object.defineProperty(this, "maxLength",
|
|
20
|
-
{ set : function (mL) {
|
|
21
|
-
if (!mL || !(typeof mL === "number") || mL <= 0 ) mL = Infinity
|
|
22
|
-
maxLength = mL
|
|
23
|
-
// if it gets above double maxLength, trim right away.
|
|
24
|
-
// otherwise, do it whenever it's convenient.
|
|
25
|
-
if (length > maxLength) trim()
|
|
26
|
-
}
|
|
27
|
-
, get : function () { return maxLength }
|
|
28
|
-
, enumerable : true
|
|
29
|
-
})
|
|
30
|
-
this.maxLength = maxLength
|
|
31
|
-
Object.defineProperty(this, "length",
|
|
32
|
-
{ get : function () { return length }
|
|
33
|
-
, enumerable : true
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
this.set = function (key, value) {
|
|
37
|
-
if (hOP(cache, key)) {
|
|
38
|
-
this.get(key)
|
|
39
|
-
cache[key].value = value
|
|
40
|
-
return undefined
|
|
41
|
-
}
|
|
42
|
-
var hit = {key:key, value:value, lu:mru++}
|
|
43
|
-
lruList[hit.lu] = cache[key] = hit
|
|
44
|
-
length ++
|
|
45
|
-
if (length > maxLength) trim()
|
|
46
|
-
}
|
|
47
|
-
this.get = function (key) {
|
|
48
|
-
if (!hOP(cache, key)) return undefined
|
|
49
|
-
var hit = cache[key]
|
|
50
|
-
delete lruList[hit.lu]
|
|
51
|
-
if (hit.lu === lru) lruWalk()
|
|
52
|
-
hit.lu = mru ++
|
|
53
|
-
lruList[hit.lu] = hit
|
|
54
|
-
return hit.value
|
|
55
|
-
}
|
|
56
|
-
this.del = function (key) {
|
|
57
|
-
if (!hOP(cache, key)) return undefined
|
|
58
|
-
var hit = cache[key]
|
|
59
|
-
delete cache[key]
|
|
60
|
-
delete lruList[hit.lu]
|
|
61
|
-
if (hit.lu === lru) lruWalk()
|
|
62
|
-
length --
|
|
63
|
-
}
|
|
64
|
-
function lruWalk () {
|
|
65
|
-
// lru has been deleted, hop up to the next hit.
|
|
66
|
-
lru = Object.keys(lruList).shift()
|
|
67
|
-
}
|
|
68
|
-
function trim () {
|
|
69
|
-
if (length <= maxLength) return undefined
|
|
70
|
-
var prune = Object.keys(lruList).slice(0, length - maxLength)
|
|
71
|
-
for (var i = 0, l = (length - maxLength); i < l; i ++) {
|
|
72
|
-
delete cache[ lruList[prune[i]].key ]
|
|
73
|
-
delete lruList[prune[i]]
|
|
74
|
-
}
|
|
75
|
-
length = maxLength
|
|
76
|
-
lruWalk()
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (!process || !module || module !== process.mainModule) return undefined
|
|
81
|
-
|
|
82
|
-
var l = LRUCache(3)
|
|
83
|
-
, assert = require("assert")
|
|
84
|
-
|
|
85
|
-
l.set(1, 1)
|
|
86
|
-
l.set(2, 1)
|
|
87
|
-
l.set(3, 1)
|
|
88
|
-
l.set(4, 1)
|
|
89
|
-
l.set(5, 1)
|
|
90
|
-
l.set(6, 1)
|
|
91
|
-
|
|
92
|
-
assert.equal(l.get(1), undefined)
|
|
93
|
-
assert.equal(l.get(2), undefined)
|
|
94
|
-
assert.equal(l.get(3), undefined)
|
|
95
|
-
assert.equal(l.get(4), 1)
|
|
96
|
-
assert.equal(l.get(5), 1)
|
|
97
|
-
assert.equal(l.get(6), 1)
|
|
98
|
-
|
|
99
|
-
// now keep re-getting the 6 so it remains the most recently used.
|
|
100
|
-
// in this case, we'll have 6, 7, 8, 9, 10, 11, so the ending length = 5
|
|
101
|
-
l.set(7, 1)
|
|
102
|
-
l.get(6)
|
|
103
|
-
l.set(8, 1)
|
|
104
|
-
l.get(6)
|
|
105
|
-
l.set(9, 1)
|
|
106
|
-
l.get(6)
|
|
107
|
-
l.set(10, 1)
|
|
108
|
-
l.get(6)
|
|
109
|
-
l.set(11, 1)
|
|
110
|
-
assert.equal(l.length, 3)
|
|
111
|
-
assert.equal(l.get(4), undefined)
|
|
112
|
-
assert.equal(l.get(5), undefined)
|
|
113
|
-
assert.equal(l.get(6), 1)
|
|
114
|
-
assert.equal(l.get(7), undefined)
|
|
115
|
-
assert.equal(l.get(8), undefined)
|
|
116
|
-
assert.equal(l.get(9), undefined)
|
|
117
|
-
assert.equal(l.get(10), 1)
|
|
118
|
-
assert.equal(l.get(11), 1)
|
|
119
|
-
|
|
120
|
-
// test changing the maxLength, verify that the LRU items get dropped.
|
|
121
|
-
l.maxLength = 100
|
|
122
|
-
for (var i = 0; i < 100; i ++) l.set(i, i)
|
|
123
|
-
assert.equal(l.length, 100)
|
|
124
|
-
for (var i = 0; i < 100; i ++) {
|
|
125
|
-
assert.equal(l.get(i), i)
|
|
126
|
-
}
|
|
127
|
-
l.maxLength = 3
|
|
128
|
-
assert.equal(l.length, 3)
|
|
129
|
-
for (var i = 0; i < 97; i ++) {
|
|
130
|
-
assert.equal(l.get(i), undefined)
|
|
131
|
-
}
|
|
132
|
-
for (var i = 98; i < 100; i ++) {
|
|
133
|
-
assert.equal(l.get(i), i)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// now remove the maxLength restriction, and try again.
|
|
137
|
-
l.maxLength = "hello"
|
|
138
|
-
for (var i = 0; i < 100; i ++) l.set(i, i)
|
|
139
|
-
assert.equal(l.length, 100)
|
|
140
|
-
for (var i = 0; i < 100; i ++) {
|
|
141
|
-
assert.equal(l.get(i), i)
|
|
142
|
-
}
|
|
143
|
-
// should trigger an immediate resize
|
|
144
|
-
l.maxLength = 3
|
|
145
|
-
assert.equal(l.length, 3)
|
|
146
|
-
for (var i = 0; i < 97; i ++) {
|
|
147
|
-
assert.equal(l.get(i), undefined)
|
|
148
|
-
}
|
|
149
|
-
for (var i = 98; i < 100; i ++) {
|
|
150
|
-
assert.equal(l.get(i), i)
|
|
151
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
{ "name" : "lru-cache"
|
|
2
|
-
, "description" : "A cache object that deletes the least-recently-used items."
|
|
3
|
-
, "version" : "1.0.4"
|
|
4
|
-
, "author" : "Isaac Z. Schlueter <i@izs.me>"
|
|
5
|
-
, "scripts" : { "test" : "node lib/lru-cache.js" }
|
|
6
|
-
, "main" : "lib/lru-cache"
|
|
7
|
-
, "repository" : "git://github.com/isaacs/node-lru-cache.git"
|
|
8
|
-
, "licenses" :
|
|
9
|
-
[ { "type" : "MIT"
|
|
10
|
-
, "url" : "http://github.com/isaacs/node-lru-cache/raw/master/LICENSE"
|
|
11
|
-
}
|
|
12
|
-
]
|
|
13
|
-
}
|