@naanlang/naan 1.0.3 → 1.0.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/LICENSE.md +1 -1
- package/README.md +8 -6
- package/bin/index.js +2 -2
- package/dist/env_web.js +30 -6
- package/dist/naan.min.js +4 -4
- package/frameworks/browser/https_request.nlg +120 -0
- package/frameworks/browser/sworker.js +29 -39
- package/frameworks/browser/terminals.nlg +6 -4
- package/frameworks/browser/workers.nlg +13 -6
- package/frameworks/client/apiclient.nlg +1 -1
- package/frameworks/client/psm_client.nlg +80 -32
- package/frameworks/common/common.nlg +104 -26
- package/frameworks/common/preferences.nlg +1 -0
- package/frameworks/common/watching.nlg +1 -0
- package/frameworks/node/apiserver.nlg +9 -5
- package/frameworks/node/filesystem.nlg +89 -31
- package/frameworks/node/gitter.nlg +27 -6
- package/frameworks/node/https_request.nlg +119 -0
- package/frameworks/node/node.nlg +1 -0
- package/frameworks/node/psm_server.nlg +29 -7
- package/frameworks/project/build.nlg +13 -7
- package/frameworks/project/proj_cliser.nlg +11 -3
- package/frameworks/project/proj_console.nlg +13 -4
- package/frameworks/project/proj_folder.nlg +10 -1
- package/frameworks/project/proj_lambda.nlg +13 -4
- package/frameworks/project/proj_static.nlg +11 -3
- package/frameworks/project/projects.nlg +102 -20
- package/lib/browser/env_web.js +32 -8
- package/lib/core/naanlib.js +4 -4
- package/package.json +2 -1
- package/plugins/serviceAws/aws/aws-sdk-node.min.js +2 -0
- package/plugins/serviceAws/aws/aws-sdk.min.js +2 -88
- package/plugins/serviceAws/aws_cloudwatchlogs.nlg +7 -6
- package/plugins/serviceAws/aws_dynamo.nlg +128 -45
- package/plugins/serviceAws/aws_lambda.nlg +11 -4
- package/plugins/serviceAws/aws_s3.nlg +44 -14
- package/plugins/serviceAws/dbt_aws.nlg +29 -36
- package/plugins/serviceAws/psm_aws.nlg +21 -16
- package/plugins/serviceAws/serviceAws.nlg +6 -4
- package/test/harness.nlg +3 -1
- package/test/test_01_core.nlg +5 -5
- package/test/test_02_context.nlg +4 -4
- package/test/test_03_jsinterop.nlg +9 -3
- package/plugins/serviceAws/aws_cognito.nlg +0 -76
|
@@ -30,6 +30,49 @@ function EncodeQuery(prefix, items, local result, item) {
|
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
/*
|
|
34
|
+
* ContentTypeFromFileExt
|
|
35
|
+
*
|
|
36
|
+
* Given a filename with extension, return a tuple of the recommended contentType and encoding
|
|
37
|
+
* for that type of data: `(contentType, encoding). Currently the encoding is false for text and
|
|
38
|
+
* "binary" for everything else.
|
|
39
|
+
*
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
function ContentTypeFromFileExt(filepath, local mimeType, encoding) {
|
|
43
|
+
mimeType = {
|
|
44
|
+
"css": "text/css",
|
|
45
|
+
"csv": "text/csv",
|
|
46
|
+
"gif": "image/gif",
|
|
47
|
+
"htm": "text/html",
|
|
48
|
+
"html": "text/html",
|
|
49
|
+
"ico": "image/x-icon",
|
|
50
|
+
"ics": "application/octet-stream",
|
|
51
|
+
"jpeg": "image/jpeg",
|
|
52
|
+
"jpg": "image/jpeg",
|
|
53
|
+
"js": "text/javascript",
|
|
54
|
+
"json": "application/json",
|
|
55
|
+
"mov": "video/mp4",
|
|
56
|
+
"mp3": "audio/mpeg",
|
|
57
|
+
"mp4": "video/mp4",
|
|
58
|
+
"nlg": "text/plain",
|
|
59
|
+
"npk": "application/json",
|
|
60
|
+
"ogg": "video/ogg",
|
|
61
|
+
"svg": "image/svg+xml",
|
|
62
|
+
"text": "text/plain",
|
|
63
|
+
"txt": "text/plain",
|
|
64
|
+
"zip": "application/octet-stream"
|
|
65
|
+
}[JSpath.extname(filepath).substring(1)]
|
|
66
|
+
if !mimeType
|
|
67
|
+
mimeType = "application/octet-stream"
|
|
68
|
+
if mimeType.startsWith("text/")
|
|
69
|
+
mimeType = mimeType.concat("; charset=UTF-8")
|
|
70
|
+
else
|
|
71
|
+
encoding = "binary"
|
|
72
|
+
list(mimeType, encoding)
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
|
|
33
76
|
/*
|
|
34
77
|
* UUID
|
|
35
78
|
*
|
|
@@ -66,7 +109,7 @@ function JsonParse(data) {
|
|
|
66
109
|
list(false, data)
|
|
67
110
|
} catch {
|
|
68
111
|
if true
|
|
69
|
-
|
|
112
|
+
list(exception)
|
|
70
113
|
}
|
|
71
114
|
};
|
|
72
115
|
|
|
@@ -84,7 +127,7 @@ function JsonStringify(data) {
|
|
|
84
127
|
list(false, data)
|
|
85
128
|
} catch {
|
|
86
129
|
if true
|
|
87
|
-
|
|
130
|
+
list(exception)
|
|
88
131
|
}
|
|
89
132
|
};
|
|
90
133
|
|
|
@@ -125,11 +168,8 @@ function JsLoadScript(libpath, globalID, local scriptTag, fpath) {
|
|
|
125
168
|
}
|
|
126
169
|
js.w.document.body.appendChild(scriptTag)
|
|
127
170
|
pending.wait()
|
|
128
|
-
} else if js.g
|
|
129
|
-
|
|
130
|
-
fpath = "./".concat(libpath)
|
|
131
|
-
jsScripts[libpath] = js.r(fpath) // load in NodeJS context
|
|
132
|
-
}
|
|
171
|
+
} else if js.g
|
|
172
|
+
jsScripts[libpath] = js.r(libpath) // load in NodeJS context
|
|
133
173
|
jsScripts[libpath]
|
|
134
174
|
};
|
|
135
175
|
|
|
@@ -260,13 +300,15 @@ EncodeBase64 = false;
|
|
|
260
300
|
DecodeBase64 = false;
|
|
261
301
|
JSONstringify = false;
|
|
262
302
|
JSONparse = false;
|
|
303
|
+
Uint8ArrayFromString = false;
|
|
263
304
|
|
|
264
305
|
function comrInit(local manifest) {
|
|
265
|
-
manifest = `(EncodeQuery, UUID, JsLoadScript, LoadComponentOnDemand, LiveExport, comrInit)
|
|
306
|
+
manifest = `(EncodeQuery, ContentTypeFromFileExt, UUID, JsLoadScript, LoadComponentOnDemand, LiveExport, comrInit)
|
|
266
307
|
|
|
267
|
-
Naan.module.build(module.id, "
|
|
308
|
+
Naan.module.build(module.id, "common", closure(modobj, compobj) {
|
|
268
309
|
compobj.manifest = manifest
|
|
269
310
|
modobj.exports.EncodeQuery = EncodeQuery
|
|
311
|
+
modobj.exports.ContentTypeFromFileExt = ContentTypeFromFileExt
|
|
270
312
|
modobj.exports.UUID = UUID
|
|
271
313
|
modobj.exports.JsLoadScript = JsLoadScript
|
|
272
314
|
modobj.exports.LoadComponentOnDemand = LoadComponentOnDemand
|
|
@@ -291,14 +333,18 @@ function comrInit(local manifest) {
|
|
|
291
333
|
EncodeURIComponent = wsg.encodeURIComponent
|
|
292
334
|
DecodeURIComponent = wsg.decodeURIComponent
|
|
293
335
|
//
|
|
294
|
-
// everywhere: Base65
|
|
295
|
-
EncodeBase64 = wsg.btoa
|
|
296
|
-
DecodeBase64 = wsg.atob
|
|
297
|
-
//
|
|
298
336
|
// everywhere: JSON
|
|
299
337
|
JSONstringify = wsg.JSON.stringify
|
|
300
338
|
JSONparse = wsg.JSON.parse
|
|
301
339
|
//
|
|
340
|
+
// everywhere: Uint8ArrayFromString
|
|
341
|
+
//
|
|
342
|
+
Uint8ArrayFromString = function uint8ArrayFromString(data) {
|
|
343
|
+
wsg.Uint8Array.from(Array.from(data).map(function(letter) {
|
|
344
|
+
letter.charCodeAt(0)
|
|
345
|
+
}))
|
|
346
|
+
}
|
|
347
|
+
//
|
|
302
348
|
// binaryFrom - convert arbitrary item to binary buffer or text.
|
|
303
349
|
//
|
|
304
350
|
function binaryFrom(data) {
|
|
@@ -327,22 +373,17 @@ function comrInit(local manifest) {
|
|
|
327
373
|
result
|
|
328
374
|
}
|
|
329
375
|
//
|
|
330
|
-
//
|
|
376
|
+
// EncodeBase64url - decode base64url to original string
|
|
377
|
+
// See https://en.wikipedia.org/wiki/Base64 for details on variants
|
|
331
378
|
//
|
|
332
|
-
function
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
pending = new(nonce)
|
|
336
|
-
reader.onload = function(event) { pending.signal(event.target.result) }
|
|
337
|
-
reader.readAsDataURL(blob)
|
|
338
|
-
result = pending.wait()
|
|
339
|
-
result.slice(37)
|
|
379
|
+
EncodeBase64url = function encodeBase64url(data) {
|
|
380
|
+
data = EncodeBase64(data)
|
|
381
|
+
data.replace(RegExp("[+]", "g"), '-').replace(RegExp("/", "g"), '_')
|
|
340
382
|
}
|
|
341
383
|
//
|
|
342
384
|
// DecodeBase64url - decode base64url to original string
|
|
343
|
-
// See https://en.wikipedia.org/wiki/Base64 for details on variants
|
|
344
385
|
//
|
|
345
|
-
DecodeBase64url = function decodeBase64url(base64url
|
|
386
|
+
DecodeBase64url = function decodeBase64url(base64url) {
|
|
346
387
|
base64url = base64url.replace(RegExp("-", "g"), '+').replace(RegExp("_", "g"), '/')
|
|
347
388
|
DecodeBase64(base64url)
|
|
348
389
|
}
|
|
@@ -350,6 +391,31 @@ function comrInit(local manifest) {
|
|
|
350
391
|
// For browser windows and web workers
|
|
351
392
|
//
|
|
352
393
|
if js.w || js.s {
|
|
394
|
+
//
|
|
395
|
+
// base64Format - create base 64 string from binary array
|
|
396
|
+
//
|
|
397
|
+
function browserBase64Format(data, local blob, reader, pending, result) {
|
|
398
|
+
blob = xnew(wsg.Blob, [data])
|
|
399
|
+
reader = xnew(wsg.FileReader)
|
|
400
|
+
pending = new(nonce)
|
|
401
|
+
reader.onload = function(event) { pending.signal(event.target.result) }
|
|
402
|
+
reader.readAsDataURL(blob)
|
|
403
|
+
result = pending.wait()
|
|
404
|
+
result.slice(37)
|
|
405
|
+
}
|
|
406
|
+
//
|
|
407
|
+
// binary to Base64
|
|
408
|
+
EncodeBase64 = function browserEncodeBase64(data) {
|
|
409
|
+
if xobject(data)
|
|
410
|
+
browserBase64Format(data)
|
|
411
|
+
else
|
|
412
|
+
wsg.btoa(data)
|
|
413
|
+
}
|
|
414
|
+
//
|
|
415
|
+
// Base64 to binary
|
|
416
|
+
DecodeBase64 = function browserDecodeBase64(data) {
|
|
417
|
+
wsg.atob(data)
|
|
418
|
+
}
|
|
353
419
|
//
|
|
354
420
|
// MD5 functions
|
|
355
421
|
JsLoadScript("frameworks/browser/spark-md5/spark-md5.min.js", "SparkMD5")
|
|
@@ -373,7 +439,7 @@ function comrInit(local manifest) {
|
|
|
373
439
|
throw(Error("crypto functions unavailable"))
|
|
374
440
|
data = binaryFrom(data)
|
|
375
441
|
if string(data)
|
|
376
|
-
data =
|
|
442
|
+
data = Uint8ArrayFromString(data)
|
|
377
443
|
`(error, result) = await(wsg.crypto.subtle.digest(algo, data))
|
|
378
444
|
if error
|
|
379
445
|
throw(Error("crypto digest failed", error))
|
|
@@ -392,7 +458,7 @@ function comrInit(local manifest) {
|
|
|
392
458
|
//
|
|
393
459
|
// HashSHA-256 in base64
|
|
394
460
|
HashSHA256_base64 = function browserSHA256_base64(data) {
|
|
395
|
-
|
|
461
|
+
browserBase64Format(browserDigest(data, "SHA-256"))
|
|
396
462
|
}
|
|
397
463
|
//
|
|
398
464
|
// random bytes as UInt8Array - use toint() for integer
|
|
@@ -405,6 +471,16 @@ function comrInit(local manifest) {
|
|
|
405
471
|
// For NodeJS
|
|
406
472
|
//
|
|
407
473
|
if js.g {
|
|
474
|
+
//
|
|
475
|
+
// binary to Base64
|
|
476
|
+
EncodeBase64 = function nodejsEncodeBase64(data) {
|
|
477
|
+
wsg.Buffer.from(data, "binary").toString("base64")
|
|
478
|
+
}
|
|
479
|
+
//
|
|
480
|
+
// Base64 to binary
|
|
481
|
+
DecodeBase64 = function nodejsDecodeBase64(data) {
|
|
482
|
+
wsg.Buffer.from(data, "base64").toString("binary")
|
|
483
|
+
}
|
|
408
484
|
if !crypto
|
|
409
485
|
crypto = js.r("crypto")
|
|
410
486
|
if crypto {
|
|
@@ -456,6 +532,8 @@ function comrInit(local manifest) {
|
|
|
456
532
|
modobj.exports.JSONparse = JSONparse
|
|
457
533
|
modobj.exports.JsonStringify = JsonStringify
|
|
458
534
|
modobj.exports.JsonParse = JsonParse
|
|
535
|
+
modobj.exports.Uint8ArrayFromString = Uint8ArrayFromString
|
|
536
|
+
modobj.exports.EncodeBase64url = EncodeBase64url
|
|
459
537
|
modobj.exports.DecodeBase64url = DecodeBase64url
|
|
460
538
|
modobj.exports.HashMD5 = HashMD5
|
|
461
539
|
modobj.exports.HashMD5_base64 = HashMD5_base64
|
|
@@ -125,6 +125,7 @@ function prefsInit(local manifest) {
|
|
|
125
125
|
Naan.module.build(module.id, "preferences", function(modobj, compobj) {
|
|
126
126
|
require("../common/common.nlg")
|
|
127
127
|
compobj.manifest = manifest
|
|
128
|
+
updateExports()
|
|
128
129
|
})
|
|
129
130
|
|
|
130
131
|
module.exports.Preferences = Preferences
|
|
@@ -110,8 +110,12 @@ closure MakeAPI(port, options, callback, local chroot, server) {
|
|
|
110
110
|
|
|
111
111
|
server.app.use(function(req,res,next) {
|
|
112
112
|
res.header("Access-Control-Allow-Origin", "*")
|
|
113
|
-
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
|
|
114
|
-
|
|
113
|
+
res.header("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS")
|
|
114
|
+
res.header("Access-Control-Allow-Headers", "x-naanlang-api-guid")
|
|
115
|
+
if req.method == "OPTIONS"
|
|
116
|
+
res.sendStatus(200)
|
|
117
|
+
else
|
|
118
|
+
next()
|
|
115
119
|
})
|
|
116
120
|
|
|
117
121
|
server.app.use("", express.static(chroot))
|
|
@@ -167,8 +171,8 @@ closure MakeAPI(port, options, callback, local chroot, server) {
|
|
|
167
171
|
res.status(403).send("unauthorized")
|
|
168
172
|
timeoutms = toint(req.query.timeout)
|
|
169
173
|
after = toint(req.query.after)
|
|
170
|
-
if not integer(timeoutms) or timeoutms >
|
|
171
|
-
res.status(400).send("timeout is integer milliseconds no greater than
|
|
174
|
+
if not integer(timeoutms) or timeoutms > 7200000
|
|
175
|
+
res.status(400).send("timeout is integer milliseconds no greater than 7200000")
|
|
172
176
|
else if not integer(after)
|
|
173
177
|
res.status(400).send("after must be an integer")
|
|
174
178
|
else {
|
|
@@ -197,7 +201,7 @@ closure MakeAPI(port, options, callback, local chroot, server) {
|
|
|
197
201
|
|
|
198
202
|
server.app.all("/psm", closure(req, res, local guid) {
|
|
199
203
|
guid = req.get("x-naanlang-api-guid")
|
|
200
|
-
if server.guid && guid != server.guid
|
|
204
|
+
if server.guid && guid != server.guid && req.method !== "OPTIONS"
|
|
201
205
|
res.status(403).send("unauthorized")
|
|
202
206
|
else if !server.hostfs
|
|
203
207
|
res.status(503).send("host filesystem not available")
|
|
@@ -23,7 +23,7 @@ closure Filesystem(rootpath, local files, badCreatePathRegEx) {
|
|
|
23
23
|
if !rootpath
|
|
24
24
|
return (list(Error("mandatory rootpath missing")))
|
|
25
25
|
else if rootpath == ""
|
|
26
|
-
rootpath =
|
|
26
|
+
rootpath = JSpath.sep
|
|
27
27
|
files = new(object, this)
|
|
28
28
|
files.rootpath = rootpath
|
|
29
29
|
files.path = JSpath
|
|
@@ -54,24 +54,31 @@ closure Filesystem(rootpath, local files, badCreatePathRegEx) {
|
|
|
54
54
|
// Convert a path argument to a local path, returning a standard result tuple. If the path is
|
|
55
55
|
// invalid this will report why. Otherwise this returns the correct localpath, which is the path
|
|
56
56
|
// we use with the underlying API.
|
|
57
|
+
// Win32 driver letters are annoying here. If the rootpath specifies a drive letter then the
|
|
58
|
+
// paths must match it. If the rootpath doesn't specify a drive letter then path can have any
|
|
59
|
+
// drive letter, or none.
|
|
57
60
|
|
|
58
61
|
badCreatePathRegEx = RegExp("[:]|[/\\\\][/\\\\]|[/\\\\]$|(^|[/\\\\])[.][.]?$")
|
|
59
62
|
|
|
60
|
-
closure localPath(path, create, local pattern, testpath) {
|
|
63
|
+
files.localPath = closure localPath(path, create, local pattern, testpath, locroot) {
|
|
61
64
|
if !path
|
|
62
65
|
return (list(Error("filesystem path false")))
|
|
63
66
|
else if !string(path)
|
|
64
67
|
return (list(Error("filesystem path invalid:", typeof(path))))
|
|
68
|
+
if JSpath === JSpath.win32 {
|
|
69
|
+
locroot = JSpath.resolve(path, rootpath) // use path's drive letter if we don't have one
|
|
70
|
+
pattern = "\\:\\" // unchanged and unique on win32
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
locroot = rootpath
|
|
74
|
+
pattern = "/:/" // unchanged and unique on posix
|
|
75
|
+
}
|
|
65
76
|
if JSpath.isAbsolute(path)
|
|
66
|
-
path = JSpath.resolve(path)
|
|
77
|
+
path = JSpath.resolve(locroot, path) // add drive letter to absolute path
|
|
67
78
|
else
|
|
68
79
|
path = JSpath.normalize(path) // normalize for current platform
|
|
69
|
-
if
|
|
70
|
-
|
|
71
|
-
else
|
|
72
|
-
pattern = "/:/" // unchanged and unique on posix
|
|
73
|
-
if ((testpath = commonParents(path, rootpath)) != "")
|
|
74
|
-
list(false, JSpath.resolve(rootpath, path)) // ### shouldn't be so lax on this test
|
|
80
|
+
if (commonParents(path, locroot) != "")
|
|
81
|
+
list(false, JSpath.resolve(locroot, path))
|
|
75
82
|
else if ((testpath = JSpath.resolve(pattern, path)).indexOf(pattern.slice(0,-1)) < 0)
|
|
76
83
|
list(Error("filesystem path reaches above root:", path))
|
|
77
84
|
else if create && testpath.indexOf(pattern) < 0
|
|
@@ -79,7 +86,31 @@ closure Filesystem(rootpath, local files, badCreatePathRegEx) {
|
|
|
79
86
|
else if create && badCreatePathRegEx.test(path)
|
|
80
87
|
list(Error("filesystem create path invalid", path))
|
|
81
88
|
else
|
|
82
|
-
list(false, JSpath.resolve(
|
|
89
|
+
list(false, JSpath.resolve(locroot, path))
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// folderPath
|
|
93
|
+
//
|
|
94
|
+
// Return a folder path for the specified folderID, relative to our root. If the folder path is
|
|
95
|
+
// not within our root then it is returned as an absolute path.
|
|
96
|
+
|
|
97
|
+
files.folderPath = function folderPath(folderID, callback, local envID, path) {
|
|
98
|
+
if !callback
|
|
99
|
+
return (syncAdapter(folderPath, folderID))
|
|
100
|
+
envID = {
|
|
101
|
+
"HomeDir": "HOME",
|
|
102
|
+
"TempDir": "TMPDIR",
|
|
103
|
+
"PackageDir": "npm_config_prefix"
|
|
104
|
+
}[folderID]
|
|
105
|
+
if envID {
|
|
106
|
+
path = js.g.process.env[envID]
|
|
107
|
+
if path.startsWith(rootpath)
|
|
108
|
+
path = path.slice(rootpath.length) // remove rootpath from beginning
|
|
109
|
+
}
|
|
110
|
+
if path
|
|
111
|
+
callback(false, path)
|
|
112
|
+
else
|
|
113
|
+
callback(Error("unknown folderID:", folderID))
|
|
83
114
|
}
|
|
84
115
|
|
|
85
116
|
// winDrives
|
|
@@ -150,11 +181,11 @@ closure Filesystem(rootpath, local files, badCreatePathRegEx) {
|
|
|
150
181
|
// readLines
|
|
151
182
|
//
|
|
152
183
|
// Read the contents of a file into an array of lines with the following options:
|
|
153
|
-
// pattern:
|
|
154
|
-
// fixed
|
|
155
|
-
// ignoreCase:
|
|
156
|
-
// invert:
|
|
157
|
-
// returnText:
|
|
184
|
+
// pattern: -- only return lines matching pattern
|
|
185
|
+
// fixed -- pattern is a fixed string, not a regular expression
|
|
186
|
+
// ignoreCase: -- ignore case during match
|
|
187
|
+
// invert: -- return lines that DO NOT match the regex
|
|
188
|
+
// returnText: -- return text of matched lines
|
|
158
189
|
//
|
|
159
190
|
|
|
160
191
|
files.readLines = closure readLines(path, options, callback,
|
|
@@ -639,8 +670,10 @@ closure Filesystem(rootpath, local files, badCreatePathRegEx) {
|
|
|
639
670
|
// Recursively build the tree
|
|
640
671
|
|
|
641
672
|
closure recurTree(localpath, depth, parent) {
|
|
642
|
-
if depthlimit && depth >= depthlimit
|
|
673
|
+
if depthlimit && depth >= depthlimit {
|
|
674
|
+
recurNext()
|
|
643
675
|
return
|
|
676
|
+
}
|
|
644
677
|
++pending.active
|
|
645
678
|
fs.readdir(localpath, { }, function(err, nodes, local child, node, stat) {
|
|
646
679
|
if err.code == "ENOTDIR"
|
|
@@ -670,13 +703,21 @@ closure Filesystem(rootpath, local files, badCreatePathRegEx) {
|
|
|
670
703
|
parent: child
|
|
671
704
|
}) } }
|
|
672
705
|
--pending.active
|
|
673
|
-
|
|
674
|
-
node = queue.pop()
|
|
675
|
-
recurTree(node.path, node.depth, node.parent) }
|
|
676
|
-
if pending.active == 0 && queue.length == 0
|
|
677
|
-
pending.signal(list(false, root))
|
|
706
|
+
recurNext()
|
|
678
707
|
})
|
|
679
708
|
}
|
|
709
|
+
|
|
710
|
+
// recurNext
|
|
711
|
+
//
|
|
712
|
+
// Pull more queued items and signal when done.
|
|
713
|
+
|
|
714
|
+
function recurNext(local node) {
|
|
715
|
+
while queue.length > 0 && pending.active < 20 {
|
|
716
|
+
node = queue.pop()
|
|
717
|
+
recurTree(node.path, node.depth, node.parent) }
|
|
718
|
+
if pending.active == 0 && queue.length == 0
|
|
719
|
+
pending.signal(list(false, root))
|
|
720
|
+
}
|
|
680
721
|
|
|
681
722
|
`(error, localpath) = localPath(path)
|
|
682
723
|
if error
|
|
@@ -707,21 +748,31 @@ closure Filesystem(rootpath, local files, badCreatePathRegEx) {
|
|
|
707
748
|
// dirSearch
|
|
708
749
|
//
|
|
709
750
|
// Search the files in a directory for a pattern.
|
|
710
|
-
|
|
751
|
+
//
|
|
752
|
+
// Most options are handled by readLines, but the following is done here:
|
|
753
|
+
// excludeDirs: [dirnames] -- exclude the specified directory names
|
|
754
|
+
|
|
711
755
|
files.dirSearch = closure dirSearch(path, options, callback,
|
|
712
|
-
local error, localpaths, output) {
|
|
756
|
+
local error, localpaths, dexmatch, item, output) {
|
|
713
757
|
if !callback
|
|
714
758
|
return (syncAdapter(dirSearch, path, options))
|
|
715
759
|
`(error, localpath) = localPath(path)
|
|
716
760
|
if error
|
|
717
761
|
return (asyncResult(callback, error))
|
|
762
|
+
if options.excludeDirs.0 { // match all provided paths
|
|
763
|
+
dexmatch = []
|
|
764
|
+
for item in options.excludeDirs // escape all special characters
|
|
765
|
+
dexmatch.push(item.replace(RegExp("[^A-Za-z0-9-_\n]","g"), "\\$&"))
|
|
766
|
+
dexmatch = RegExp(dexmatch.join("|"))
|
|
767
|
+
}
|
|
718
768
|
output = []
|
|
719
769
|
debuglog("dirSearch begins", localpath)
|
|
720
770
|
asyncArray(localpaths, 10, closure(localpath, index, elements, local error, direnum, node, lines, line) {
|
|
721
771
|
`(error, direnum) = dirList(localpath, { })
|
|
722
772
|
if direnum.children
|
|
723
773
|
for node in direnum.children
|
|
724
|
-
|
|
774
|
+
if !dexmatch || !node.name.match(dexmatch)
|
|
775
|
+
localpaths.push(JSpath.join(localpath, node.name)) // more files or directories to examine
|
|
725
776
|
else {
|
|
726
777
|
`(error, lines) = readLines(localpath, options)
|
|
727
778
|
if error && localpaths.length == 1 {
|
|
@@ -945,22 +996,29 @@ closure Filesystem(rootpath, local files, badCreatePathRegEx) {
|
|
|
945
996
|
//
|
|
946
997
|
// Return an array of text matches in a file or recursive directory of files with the following
|
|
947
998
|
// options:
|
|
948
|
-
// pattern:
|
|
949
|
-
// ignoreCase:
|
|
950
|
-
// invert:
|
|
951
|
-
// returnText:
|
|
952
|
-
//
|
|
999
|
+
// pattern: -- pattern to find
|
|
1000
|
+
// ignoreCase: -- ignore case during match
|
|
1001
|
+
// invert: -- return lines that DO NOT match the regex
|
|
1002
|
+
// returnText: -- return text of matched lines
|
|
1003
|
+
// excludeDirs: [dirnames] -- exclude the specified directory names
|
|
1004
|
+
// maxCount: -- integer maximum number of results (default 10,000)
|
|
953
1005
|
// grep-specific options:
|
|
954
|
-
// ignoreBinary:
|
|
1006
|
+
// ignoreBinary: -- ignore binary files
|
|
955
1007
|
//
|
|
956
1008
|
files.grepLines = closure grepLines(path, options, callback,
|
|
957
|
-
local error, localpath, cmdargs, execOptions, grep, result, lineRE, maxcount) {
|
|
1009
|
+
local error, localpath, cmdargs, item, execOptions, grep, result, lineRE, maxcount) {
|
|
958
1010
|
if !callback
|
|
959
1011
|
return (syncAdapter(grepLines, path, options))
|
|
960
1012
|
`(error, localpath) = localPath(path)
|
|
961
1013
|
if error
|
|
962
1014
|
return (asyncResult(callback, error))
|
|
963
1015
|
cmdargs = ["-rn"] // recursively search directories for files; report line numbers
|
|
1016
|
+
if options.excludeDirs.0 { // exclude a directory
|
|
1017
|
+
for item in options.excludeDirs {
|
|
1018
|
+
cmdargs.push("--exclude-dir")
|
|
1019
|
+
cmdargs.push(item)
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
964
1022
|
if options.ignoreCase
|
|
965
1023
|
cmdargs.push("-i")
|
|
966
1024
|
if options.invert
|
|
@@ -23,6 +23,28 @@ closure Gitter(fs, local gitt) {
|
|
|
23
23
|
gitt = new(object, this)
|
|
24
24
|
gitt.roots = {}
|
|
25
25
|
|
|
26
|
+
// gitPath
|
|
27
|
+
//
|
|
28
|
+
// Convert a Windows path to a posix (git) path if needed.
|
|
29
|
+
//
|
|
30
|
+
function gitPath(fspath) {
|
|
31
|
+
if fs.path.sep == "/"
|
|
32
|
+
fspath
|
|
33
|
+
else
|
|
34
|
+
fspath.split(fs.path.sep).join("/")
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// fsPath
|
|
38
|
+
//
|
|
39
|
+
// Convert a posix (git) path to a filesystem path if needed.
|
|
40
|
+
//
|
|
41
|
+
function fsPath(gitpath) {
|
|
42
|
+
if fs.path.sep == "/"
|
|
43
|
+
gitpath
|
|
44
|
+
else
|
|
45
|
+
gitpath.split("/").join(fs.path.sep)
|
|
46
|
+
}
|
|
47
|
+
|
|
26
48
|
// repoRoot
|
|
27
49
|
//
|
|
28
50
|
// Compute the path from the fs root to the repo root.
|
|
@@ -54,11 +76,10 @@ closure Gitter(fs, local gitt) {
|
|
|
54
76
|
// Compute the bridge path to an absolute repopath (which can point inside a repo) from the
|
|
55
77
|
// phyiscal repo root. If these are the same, or an error occurs, then the result is "".
|
|
56
78
|
//
|
|
57
|
-
|
|
58
79
|
function bridgePath(repopath, local top) {
|
|
59
80
|
top = repoRoot(repopath).1
|
|
60
81
|
if top && repopath.startsWith(top) && repopath.length > top.length
|
|
61
|
-
top = repopath.slice(top.length+1).concat(
|
|
82
|
+
top = repopath.slice(top.length+1).concat(fs.path.sep) // move leading "/" of bridge path to trailing
|
|
62
83
|
else
|
|
63
84
|
"" // we are looking at the root, or an error
|
|
64
85
|
}
|
|
@@ -151,12 +172,12 @@ closure Gitter(fs, local gitt) {
|
|
|
151
172
|
outLE: "\x00" // lines delimited by nulls
|
|
152
173
|
}
|
|
153
174
|
if options.pathspec
|
|
154
|
-
execOptions.cmdargs = execOptions.cmdargs.concat("--", options.pathspec)
|
|
175
|
+
execOptions.cmdargs = execOptions.cmdargs.concat("--", gitPath(options.pathspec))
|
|
155
176
|
result = []
|
|
156
177
|
execOptions.outproc = function(output, liner, kill, local line, path) {
|
|
157
178
|
while (line = liner.read())
|
|
158
179
|
if line != "" {
|
|
159
|
-
path = line.substring(3)
|
|
180
|
+
path = fsPath(line.substring(3))
|
|
160
181
|
if path.startsWith(bridge) { // must start with bridge to be within repopath
|
|
161
182
|
path = path.slice(bridge.length)
|
|
162
183
|
result.push({
|
|
@@ -183,7 +204,7 @@ closure Gitter(fs, local gitt) {
|
|
|
183
204
|
// Retrieve the content of a specific file and revision. The file pathspec must be the path of
|
|
184
205
|
// the file within the repo.
|
|
185
206
|
|
|
186
|
-
gitt.showfile = closure showfile(repopath, options, callback, local execOptions
|
|
207
|
+
gitt.showfile = closure showfile(repopath, options, callback, local execOptions) {
|
|
187
208
|
if !callback
|
|
188
209
|
return (syncAdapter(showfile, repopath, options))
|
|
189
210
|
if !string(repopath)
|
|
@@ -192,7 +213,7 @@ closure Gitter(fs, local gitt) {
|
|
|
192
213
|
return (asyncResult(callback, Error("both pathspec and hash required:", options.pathspec, options.hash)))
|
|
193
214
|
repopath = fs.path.resolve(fs.rootpath, repopath)
|
|
194
215
|
execOptions = {
|
|
195
|
-
cmdargs: ["show", options.hash.concat(":", bridgePath(repopath), options.pathspec)]
|
|
216
|
+
cmdargs: ["show", options.hash.concat(":", bridgePath(repopath), gitPath(options.pathspec))]
|
|
196
217
|
execops: {
|
|
197
218
|
cwd: repopath
|
|
198
219
|
}
|