@naanlang/naan 1.4.0 → 1.4.2

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.
Files changed (42) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +10 -10
  3. package/bin/index.js +25 -15
  4. package/dist/env_web.js +68 -73
  5. package/dist/naan.min.js +5 -5
  6. package/frameworks/browser/sworker.js +23 -23
  7. package/frameworks/browser/terminals.nlg +52 -3
  8. package/frameworks/client/psm_client.nlg +2 -0
  9. package/frameworks/client/relaycon.nlg +1 -1
  10. package/frameworks/common/common.nlg +10 -54
  11. package/frameworks/node/filesystem.nlg +5 -4
  12. package/frameworks/node/psm_server.nlg +1 -1
  13. package/frameworks/node/shell.nlg +134 -0
  14. package/frameworks/node/worker.nlg +1 -1
  15. package/frameworks/project/build.nlg +19 -5
  16. package/frameworks/project/projects.nlg +12 -1
  17. package/frameworks/running/debugnub.nlg +102 -12
  18. package/frameworks/running/debugutil.nlg +30 -13
  19. package/frameworks/running/executors.nlg +7 -3
  20. package/frameworks/running/running.nlg +26 -6
  21. package/frameworks/running/sourcecode.nlg +3 -1
  22. package/frameworks/running/taskexec.nlg +1 -1
  23. package/frameworks/storage/dbt_pouch.nlg +37 -19
  24. package/frameworks/storage/file_manager.nlg +1 -1
  25. package/frameworks/storage/psm_dbtables.nlg +23 -24
  26. package/frameworks/storage/resources.nlg +12 -4
  27. package/frameworks/storage/storage.nlg +2 -2
  28. package/lib/browser/env_web.js +73 -78
  29. package/lib/core/naanlib.js +5 -5
  30. package/package.json +1 -1
  31. package/plugins/gitClient/gitClient.nlg +3 -3
  32. package/plugins/openSearch/openSearch.nlg +2 -2
  33. package/plugins/serviceAws/aws_s3.nlg +17 -1
  34. package/plugins/serviceAws/psm_aws.nlg +4 -2
  35. package/plugins/serviceAws/serviceAws.nlg +1 -1
  36. package/plugins/serviceGC/gc_api.nlg +4 -4
  37. package/plugins/serviceGitHub/serviceGitHub.nlg +1 -1
  38. package/plugins/serviceGitLab/psm_gitlab.nlg +1 -1
  39. package/plugins/serviceGitLab/serviceGitLab.nlg +1 -1
  40. package/test/harness.nlg +9 -9
  41. package/test/test_01_core.nlg +24 -2
  42. package/test/test_02_context.nlg +1 -1
@@ -36,7 +36,7 @@
36
36
  *
37
37
  */
38
38
 
39
- var CurrentCacheName = "Naanlang-1.4.0+1";
39
+ var CurrentCacheName = "Naanlang-1.4.2+1";
40
40
 
41
41
 
42
42
  //
@@ -92,18 +92,18 @@ var terminated; // flag that this service worker
92
92
  var pubVersion = event.data.hereIsMyVersion;
93
93
  var sourceId = event.source.id; // client id of sender of message
94
94
  msgPorts[sourceId] = msgport;
95
- console.log("[1.4.0+1] received new msgport for", sourceId, pubID, "-", pubVersion);
96
- if (pubVersion != "1.4.0+1") {
95
+ console.log("[1.4.2+1] received new msgport for", sourceId, pubID, "-", pubVersion);
96
+ if (pubVersion != "1.4.2+1") {
97
97
  if (upgradeMsgPorts) { // if not activated
98
- console.log("[1.4.0+1] update pending for", sourceId);
98
+ console.log("[1.4.2+1] update pending for", sourceId);
99
99
  upgradeMsgPorts[sourceId] = msgport;
100
100
  }
101
101
  else {
102
102
  msgport.postMessage({ // notify new version available
103
103
  id: "upgrade",
104
- version: "1.4.0+1"
104
+ version: "1.4.2+1"
105
105
  });
106
- console.log("[1.4.0+1] update sent for:", sourceId);
106
+ console.log("[1.4.2+1] update sent for:", sourceId);
107
107
  }
108
108
  }
109
109
  Reaper(); // clean up obsolete info
@@ -134,10 +134,10 @@ var terminated; // flag that this service worker
134
134
  msgport.onmessage = function(msg) {
135
135
  msg = msg.data;
136
136
  if (msg.id == "skipWaiting") { // try to activate this SW now
137
- console.log("[1.4.0+1] attempting skipWaiting");
137
+ console.log("[1.4.2+1] attempting skipWaiting");
138
138
  self.skipWaiting();
139
139
  } else if (msg.id == "terminate") { // termiante this SW now
140
- console.log("[1.4.0+1] terminated");
140
+ console.log("[1.4.2+1] terminated");
141
141
  terminate();
142
142
  } else if (msg.id == "abortURL") { // abort an I/O transaction
143
143
  var href = new URL(msg.url).href;
@@ -150,7 +150,7 @@ var terminated; // flag that this service worker
150
150
  } else if (msg.id == "response") // response from fetch request
151
151
  processResponse(msg);
152
152
  else if (msg.id == "text") // just log some text
153
- console.log("[1.4.0+1] msg received:", msg.text);
153
+ console.log("[1.4.2+1] msg received:", msg.text);
154
154
  };
155
155
 
156
156
  // send text to IDE log
@@ -161,7 +161,7 @@ var terminated; // flag that this service worker
161
161
  // don't clutter the log
162
162
  msgport.postMessage({
163
163
  id: "text",
164
- text: "port received by 1.4.0+1",
164
+ text: "port received by 1.4.2+1",
165
165
  });
166
166
  */
167
167
  if (--pending === 0)
@@ -178,7 +178,7 @@ var terminated; // flag that this service worker
178
178
  includeUncontrolled: true
179
179
  }).then(function(clientList) {
180
180
  clientList.every(function(client) {
181
- console.log("[1.4.0+1] requesting new msgport for", client.id);
181
+ console.log("[1.4.2+1] requesting new msgport for", client.id);
182
182
  ++pending;
183
183
  client.postMessage({ // tell client(s) we need this fetch source
184
184
  msg: "Naan_need_fetch_port",
@@ -224,12 +224,12 @@ function Reaper() {
224
224
  clients[clientList[clidex].id] = clientList[clidex];
225
225
  for (var sourceId in msgPorts)
226
226
  if (!clients[sourceId]) {
227
- console.log("[1.4.0+1] source gone:", sourceId);
227
+ console.log("[1.4.2+1] source gone:", sourceId);
228
228
  delete msgPorts[sourceId]; // no longer a source
229
229
  }
230
230
  for (var clientId in fetchPorts)
231
231
  if (!clients[clientId]) {
232
- console.log("[1.4.0+1] client gone:", clientId);
232
+ console.log("[1.4.2+1] client gone:", clientId);
233
233
  delete fetchPorts[clientId]; // no longer a client
234
234
  }
235
235
  for (var fqdex = 0; fqdex < fetchQueue.length; ++fqdex) {
@@ -263,16 +263,16 @@ function ClearCaches() {
263
263
  return (Promise.all(
264
264
  cacheNames.map(function(cacheName) {
265
265
  if (cacheName != CurrentCacheName) {
266
- console.log('[1.4.0+1] deleting old cache:', cacheName);
266
+ console.log('[1.4.2+1] deleting old cache:', cacheName);
267
267
  return (caches.delete(cacheName));
268
268
  }
269
269
  })
270
270
  ));
271
271
  }).then(function() { // claim all clients
272
- console.log('[1.4.0+1] claiming clients');
272
+ console.log('[1.4.2+1] claiming clients');
273
273
  return (self.clients.claim());
274
274
  }).then(function() {
275
- console.log('[1.4.0+1] clients claimed');
275
+ console.log('[1.4.2+1] clients claimed');
276
276
  return (Promise.resolve(true));
277
277
  });
278
278
  return (promise);
@@ -315,7 +315,7 @@ function GetClientResponse(event, urlpath) {
315
315
  msgport.postMessage({
316
316
  id: "fetch",
317
317
  seq: seqno,
318
- version: "1.4.0+1",
318
+ version: "1.4.2+1",
319
319
  request: {
320
320
  method: event.request.method,
321
321
  url: event.request.url
@@ -363,7 +363,7 @@ function GetClientResponse(event, urlpath) {
363
363
  */
364
364
 
365
365
  self.addEventListener('install', function(event) {
366
- console.log("[1.4.0+1] install");
366
+ console.log("[1.4.2+1] install");
367
367
  self.skipWaiting();
368
368
  });
369
369
 
@@ -376,20 +376,20 @@ self.addEventListener('install', function(event) {
376
376
  */
377
377
 
378
378
  self.addEventListener('activate', function(event) {
379
- console.log("[1.4.0+1] activate");
379
+ console.log("[1.4.2+1] activate");
380
380
  self.clients.matchAll({ // for debugging, list controlled clients
381
381
  includeUncontrolled: true
382
382
  }).then(function(clientList) {
383
383
  var urls = clientList.map(function(client) {
384
384
  return (client.url);
385
385
  });
386
- console.log('[1.4.0+1] matching clients:', urls.join(', '));
386
+ console.log('[1.4.2+1] matching clients:', urls.join(', '));
387
387
  });
388
388
  var promise = ClearCaches().then(function() {
389
389
  for (var sourceId in upgradeMsgPorts) {
390
390
  upgradeMsgPorts[sourceId].postMessage({ // notify new version available
391
391
  id: "upgrade",
392
- version: "1.4.0+1"
392
+ version: "1.4.2+1"
393
393
  });
394
394
  }
395
395
  upgradeMsgPorts = false;
@@ -421,7 +421,7 @@ self.addEventListener('fetch', function(event) {
421
421
  var promise;
422
422
  var url = new URL(request.url);
423
423
  var nocache = request.method != "GET"
424
- || url.search.length !== 0 && url.searchParams.get("naanver") !== "fae76573fc7eeb3020b11e0d88fe69d4"
424
+ || url.search.length !== 0 && url.searchParams.get("naanver") !== "9f1a6b0c23c290d0ee26783e99bdccc1"
425
425
  || request.headers.get('range');
426
426
  if (url.pathname.startsWith("/run/")) {
427
427
  nocache = true;
@@ -455,7 +455,7 @@ self.addEventListener('fetch', function(event) {
455
455
  statusText: "Request Cancelled"
456
456
  }));
457
457
  }
458
- console.log("[1.4.0+1] fetch failed", request.url, e);
458
+ console.log("[1.4.2+1] fetch failed", request.url, e);
459
459
  return (new Response(undefined, {
460
460
  status: 404,
461
461
  statusText: "Fetch Failed"
@@ -152,9 +152,9 @@ closure TermHost(track, api, name, options, local target, nlregex, xtarg)
152
152
 
153
153
  // onerror
154
154
  // An error occurred on the WebSocket connection.
155
+ // This can happen in normal operation when the connection times out.
155
156
  target.ws.onerror = function onerror(e) {
156
157
  pending.signal(list(Error("websocket failed", e)))
157
- debuglog("ws error")
158
158
  }
159
159
 
160
160
  // onclose
@@ -655,6 +655,7 @@ closure TermLocal(track, name, options, local target, nlregex, listener)
655
655
  // Destroy the execution instance.
656
656
 
657
657
  target.destroy = function destroy() {
658
+ gWorkers[target.guid] = undefined
658
659
  target.execClosed(Error("instance destroyed"))
659
660
  track.delete(target)
660
661
  termDetach()
@@ -750,7 +751,7 @@ closure WorkerToMain(local nideRunning, track) {
750
751
  error = Error("WorkerToMain only available in browser workers")
751
752
  return (list(error))
752
753
  }
753
- nideRunning = require("frameworks/running/executors.nlg")
754
+ nideRunning = require("naanlib:frameworks/running/executors.nlg")
754
755
  track = nideRunning.ExecutorTracker()
755
756
  track.register(mainProxy, "proxy")
756
757
  track.spawn("proxy") // returns result tuple
@@ -865,6 +866,10 @@ closure termIDE(track, name, workerID, naancont, title, local target, connected)
865
866
  if inbound
866
867
  inbound.active = active
867
868
  }
869
+
870
+ connection.WriteLn = function WriteLn(text) {
871
+ term.WriteLn(text)
872
+ }
868
873
 
869
874
  connection.termDetach = function termDetach() { // disconnect the terminal
870
875
  db.detach(target)
@@ -921,7 +926,46 @@ closure termIDE(track, name, workerID, naancont, title, local target, connected)
921
926
  }
922
927
  target.execReset() // reset our parent class
923
928
  }
924
-
929
+
930
+ // targetClose
931
+ //
932
+ // Mark the target window as closed.
933
+ //
934
+ function targetClose(local conn) {
935
+ for conn in connected {
936
+ conn.db.detach(target)
937
+ conn.WriteLn("\x1b[90m\x1b[3m".concat("\ntarget closed", "\x1b[0m"))
938
+ }
939
+ target.execReset()
940
+ target.naancont = naancont = false
941
+ }
942
+
943
+ // setCheckStatus
944
+ //
945
+ // Begin or end checking status on the worker to see if stopped responding. The challenge
946
+ // here is that we open vsite windows in the main thread, but there is no reliable event to learn
947
+ // when they are closed. The solution is to start checking if the window is closed after it gets
948
+ // a visibilityChange event to hidden. This terminates when the window gets marked closed or when
949
+ // it becomes visible again--which is quite unlikely unless someone uses the explicit hide/show
950
+ // window methods.
951
+ //
952
+ target.setCheckStatus = closure setCheckStatus(hidden, window) {
953
+ if hidden {
954
+ if !target.statusPoll
955
+ target.statusPoll = future(function() {
956
+ while target.statusPoll {
957
+ if window.closed {
958
+ targetClose()
959
+ target.statusPoll = false
960
+ break
961
+ } else
962
+ sleep(100) // check 10x/second
963
+ }
964
+ }, 10) // start after 10 msec
965
+ } else
966
+ target.statusPoll = false
967
+ }
968
+
925
969
  // vsiteRefresh
926
970
  //
927
971
  // The vsite upon which we are based has changed.
@@ -969,6 +1013,11 @@ closure termInstallVirtualWatcher(track, naancont) {
969
1013
  if target
970
1014
  target.updateController()
971
1015
  }
1016
+ else if msg.op == "VsiteVisibilityChange" {
1017
+ target = findIDEtarget(track, msg.name, "NaanVsite", msg.naancont)
1018
+ if target
1019
+ target.setCheckStatus(msg.hidden, msg.window)
1020
+ }
972
1021
  }
973
1022
 
974
1023
  require("../running/executors.nlg").TaskOnMessageHook(watchVsites)
@@ -625,6 +625,8 @@ closure psmcFsConnector(psm, api, local connClassID, connector, watch) {
625
625
  //
626
626
  // Connect to the specified service.
627
627
  connector.connect = function connect(resID, service, args, local error, creds, api, rootpath) {
628
+ if !resID || args && !tuple(args)
629
+ return (list(Error("invalid arguments")))
628
630
  `(error, creds) = connector.access(resID)
629
631
  if !error {
630
632
  if creds == "HostFS"
@@ -85,7 +85,7 @@ closure RelayCon(host, secret, options, local relay, nlregex)
85
85
  })
86
86
  result = relay.wscon.connect()
87
87
  if !result.0
88
- sendControl("spawn", { name: "NaanIDE Server" }) // ensure worker is connected
88
+ sendControl("spawn", { name: options.name }) // ensure worker is connected
89
89
  else if options.debug
90
90
  ErrorDebuglog("WebSocket connect failed:", result.0)
91
91
  result
@@ -182,53 +182,6 @@ function JsonStringify(data) {
182
182
  }
183
183
  };
184
184
 
185
-
186
- /*
187
- * JsLoadScript
188
- *
189
- * Load the specified script, or return its main entry point if already loaded. This blocks the
190
- * caller until the script load completes. The return value is false if an error occurred.
191
- *
192
- */
193
-
194
- jsScripts = { }; // script values become false on reload
195
-
196
- closure JsLoadScript(libpath, globalID, local savens, hostpath, pending, scriptTag) {
197
- global(jsScripts, JSpath, js)
198
- if jsScripts[libpath]
199
- return // already loaded
200
- savens = nsactive()
201
- hostpath = js.w.location.origin.concat(JSpath.join(JSpath.dirname(js.w.location.pathname), libpath))
202
- if js.s.importScripts && tostring(js.s.importScripts) == "[Function importScripts]" {
203
- try {
204
- js.s.importScripts(hostpath) // load in worker context
205
- jsScripts[libpath] = js.s[globalID]
206
- } catch {
207
- debuglog("JsLoadScript:", libpath, "failed", exception)
208
- js.s.console.log("JsLoadScript:", libpath, "failed", exception) // ensure visibility in JS console
209
- }
210
- } else if js.w { // load in browser context
211
- pending = new(nonce)
212
- scriptTag = js.w.document.createElement("script")
213
- scriptTag.src = hostpath.concat(requireQuery())
214
- scriptTag.onload = function (event) {
215
- jsScripts[libpath] = js.w[globalID]
216
- pending.signal(js.w[globalID])
217
- true
218
- }
219
- scriptTag.onerror = function (event) {
220
- debuglog("JsLoadScript:", libpath, "not found")
221
- pending.signal(false)
222
- true
223
- }
224
- js.w.document.body.appendChild(scriptTag)
225
- pending.wait()
226
- } else if js.g
227
- jsScripts[libpath] = js.r(libpath) // load in NodeJS context
228
- nsactive(savens)
229
- jsScripts[libpath]
230
- };
231
-
232
185
 
233
186
  /*
234
187
  * LoadComponentOnDemand
@@ -247,7 +200,9 @@ macro LoadComponentOnDemand(functionKeys, componentPath, local modobj, key) {
247
200
  componentPath = eval(componentPath)
248
201
  if name(functionKeys)
249
202
  functionKeys = list(functionKeys)
250
- for key in functionKeys
203
+ for key in functionKeys {
204
+ if modobj.exports[key]
205
+ continue // already defined
251
206
  closure (key, path, local exproc) {
252
207
  function loadComponent args { // called when exported function first called
253
208
  exproc = require(path)[key]
@@ -257,6 +212,7 @@ macro LoadComponentOnDemand(functionKeys, componentPath, local modobj, key) {
257
212
  }
258
213
  modobj.exports[key] = loadComponent
259
214
  } (key, componentPath)
215
+ }
260
216
  true
261
217
  };
262
218
 
@@ -274,7 +230,7 @@ macro LoadComponentOnDemand(functionKeys, componentPath, local modobj, key) {
274
230
  * LiveImport() function to a module's exports.
275
231
  *
276
232
  * To use LiveImport, call from the importing module as follows:
277
- * require("frameworks/common/common.nlg").LiveImport(<fixproc>);
233
+ * require("naanlib:frameworks/common/common.nlg").LiveImport(<fixproc>);
278
234
  * The optional <fixproc> is called after the imports in case the foreign module wants to fix up
279
235
  * anything after the import:
280
236
  * fixproc(exporterMod);
@@ -362,7 +318,7 @@ Uint8ArrayFromString = false;
362
318
 
363
319
  function comrInit(local manifest) {
364
320
  manifest = `(EncodeQuery, DecodeQuery, ContentTypeFromFileExt, UUID, VersionCheck, JsonParse,
365
- JsonStringify, JsLoadScript, LoadComponentOnDemand, LiveExport, comrInit)
321
+ JsonStringify, LoadComponentOnDemand, LiveExport, comrInit)
366
322
 
367
323
  Naan.module.build(module.id, "common", closure(modobj, compobj) {
368
324
  compobj.manifest = manifest
@@ -370,8 +326,9 @@ function comrInit(local manifest) {
370
326
  modobj.exports.DecodeQuery = DecodeQuery
371
327
  modobj.exports.ContentTypeFromFileExt = ContentTypeFromFileExt
372
328
  modobj.exports.UUID = UUID
373
- modobj.exports.JsLoadScript = JsLoadScript
374
329
  modobj.exports.LoadComponentOnDemand = LoadComponentOnDemand
330
+ letimport(module.owner.list.Util.exports.JsLoadScript) // use the Util version
331
+ modobj.exports.JsLoadScript = JsLoadScript
375
332
  modobj.exports.LiveExport = LiveExport // allow others to create export updaters
376
333
  updateExports = LiveExport() // export LiveImport() so we can update
377
334
 
@@ -478,13 +435,12 @@ function comrInit(local manifest) {
478
435
  }
479
436
  //
480
437
  // MD5 functions
481
- if symbol(NideBuild) { // execute if we are not building
482
- JsLoadScript("frameworks/browser/spark-md5/spark-md5.min.js", "SparkMD5")
483
- }
484
438
  //
485
439
  // hasMD5 in hex
486
440
  HashMD5 = function browserMD5(data, raw) {
487
441
  data = binaryFrom(data)
442
+ if !wsg.SparkMD5
443
+ JsLoadScript("frameworks/browser/spark-md5/spark-md5.min.js", "SparkMD5")
488
444
  if xobject(data)
489
445
  wsg.SparkMD5.ArrayBuffer.hash(data, raw)
490
446
  else
@@ -1137,6 +1137,11 @@ closure Filesystem(rootpath, local files, badCreatePathRegEx) {
1137
1137
  })
1138
1138
  spawn.on("close", function(code) {
1139
1139
  output.exitCode = code // zero on success; null on killed
1140
+ completed(false, output)
1141
+ })
1142
+ spawn.on("exit", function(code, signal) {
1143
+ output.exitCode = code // if exited
1144
+ output.signalName = signal // if terminated
1140
1145
  })
1141
1146
  spawn.stderr.on("error", function(err) {
1142
1147
  completed(Error("stderr stream failed:", err))
@@ -1155,8 +1160,6 @@ closure Filesystem(rootpath, local files, badCreatePathRegEx) {
1155
1160
  call(options.errproc, output)
1156
1161
  if linerOut
1157
1162
  linerErr = false // let end-of-out do the completion
1158
- else
1159
- completed(false, output)
1160
1163
  })
1161
1164
  linerErr.on("readable", function (local line) {
1162
1165
  if options.errproc
@@ -1179,8 +1182,6 @@ closure Filesystem(rootpath, local files, badCreatePathRegEx) {
1179
1182
  call(options.outproc)
1180
1183
  if linerErr
1181
1184
  linerOut = false // let end-of-err do the completion
1182
- else
1183
- completed(false, output)
1184
1185
  })
1185
1186
  linerOut.on("readable", function (local line) {
1186
1187
  if options.outproc
@@ -525,7 +525,7 @@ function MakeServerPSM(api, name, local importPSM, error, path, psm) {
525
525
  if !error {
526
526
  App.psm = psm
527
527
  psmsFsConnector(App.psm, api)
528
- require("frameworks/storage/dbt_pouch.nlg").DbConnector(App.psm) // Pouch databases as filesystems
528
+ require("naanlib:frameworks/storage/dbt_pouch.nlg").DbConnector(App.psm) // Pouch databases as filesystems
529
529
  } else
530
530
  ErrorDebuglog("Cannot create PSM", error)
531
531
  App.psm
@@ -0,0 +1,134 @@
1
+ /*
2
+ * shell.nlg
3
+ * Naanlib/frameworks/node
4
+ *
5
+ * Shell access and utilities for the host OS.
6
+ *
7
+ * column positioning: // // !
8
+ *
9
+ * Copyright (c) 2024 by Richard C. Zulch
10
+ *
11
+ */
12
+
13
+
14
+ /*
15
+ * ShellConnect
16
+ *
17
+ * Executing this installs itself in the current dialect to provide a REPL shell escape, returning a
18
+ * standard result tuple. This sends lines starting with ! to the shell with "bash -c". The result is
19
+ * output as text and also assigned to the $$ variable in the active namespace.
20
+ *
21
+ * Options:
22
+ * {
23
+ * fs: <filesystem> // optional: the filesystem object to use
24
+ * cwd: <string> // optional: Current Working Directory
25
+ * derive: <string> // optional: dialect to derive from, or "" for no !
26
+ * }
27
+ *
28
+ */
29
+
30
+ closure ShellConnect(options, local shob, error, fs, xparser, shelly) {
31
+ global(js, compress)
32
+ shob = new(object, this)
33
+
34
+ // readline
35
+ // read one line of text from the source
36
+ function readline(source, local cc, output) {
37
+ output = ""
38
+ while cc = source.readchar() {
39
+ output = output.concat(cc)
40
+ if cc == "\n"
41
+ break }
42
+ output
43
+ }
44
+
45
+ // exec
46
+ //
47
+ // Execute a shell command with optional current-working-directory.
48
+ //
49
+ shob.exec = closure exec(cmd, args, cwd, local pending, report, xlparams) {
50
+ pending = new(nonce)
51
+ report = {
52
+ stdout: []
53
+ stderr: []
54
+ }
55
+ xlparams = {
56
+ cmdargs: args
57
+ outproc: function(output, liner, kill, local line) {
58
+ while line = liner.read() {
59
+ report.stdout.push(line)
60
+ printline("\x1b[33m${line}\x1b[0m") // amber for stdout
61
+ }
62
+ }
63
+ errproc: function(output, liner, kill, local line) {
64
+ while line = liner.read() {
65
+ report.stderr.push(line)
66
+ printline("\x1b[36m${line}\x1b[0m") // cyan for stderr
67
+ }
68
+ }
69
+ }
70
+ if cwd
71
+ xlparams.execops = { cwd: cwd }
72
+ else if options.cwd
73
+ xlparams.execops = { cwd: options.cwd }
74
+ fs.execLines(cmd, xlparams, function(error, result) {
75
+ evalactive(list(`assign, `(compress, "$$"), report)) // assign to $$ in active namespace
76
+ pending.signal(list(error, merge(result, report)))
77
+ })
78
+ pending.wait()
79
+ }
80
+
81
+ // shellparser
82
+ // intercept command-line parsing and look for a !shell escape
83
+ function shellparser(source, grammar, operators, local token) {
84
+ token = source.tokenread()
85
+ if token.atom == `! {
86
+ source.tokenlast(true) // "consume" the ! token
87
+ text = readline(source)
88
+ source.tokenpush({ // don't show any output
89
+ atom: `;;
90
+ loc: tuple(0, text.length)
91
+ })
92
+ exec("bash", ["-c", text], js.d) // default directory: js.d
93
+ }
94
+ else {
95
+ source.tokenpush(token)
96
+ xparser(source, grammar, operators)
97
+ }
98
+ }
99
+
100
+ if options.fs
101
+ fs = options.fs
102
+ else
103
+ `(error, fs) = Filesystem("") // error not possible
104
+ if options.derive != "" { // install hook
105
+ if string(options.derive)
106
+ shelly = Dialect.derive(options.derive)
107
+ else
108
+ shelly = Dialect
109
+ xparser = shelly.parser
110
+ shelly.parser = shellparser
111
+ }
112
+
113
+ // finis
114
+ shob
115
+ };
116
+
117
+
118
+ /*
119
+ * shInit
120
+ *
121
+ * Initialize the node module.
122
+ *
123
+ *
124
+ */
125
+
126
+ function shInit(local manifest) {
127
+ manifest = `(ShellConnect, shInit)
128
+
129
+ Naan.module.build(module.id, "shell", function(modobj, compobj) {
130
+ require("./filesystem.nlg")
131
+ compobj.manifest = manifest
132
+ modobj.exports.ShellConnect = ShellConnect
133
+ })
134
+ } ();
@@ -312,7 +312,7 @@ closure workThread(workco, workerID, startup, guid
312
312
  // create our worker thread
313
313
 
314
314
  // options = { execArgv: ["--inspect-brk"] } // uncomment for NodeJS debugging
315
- worker.thread = xnew(threads.Worker, JSpath.resolve(js.d, "node_worker.js"), options)
315
+ worker.thread = xnew(threads.Worker, JSpath.resolve(js.d, "lib/node_worker.js"), options)
316
316
  subChannel = xnew(threads.MessageChannel)
317
317
  worker.thread.postMessage({ hereIsYourPort: subChannel.port1 }, [subChannel.port1])
318
318
  worker.msgport = subChannel.port2
@@ -230,17 +230,26 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
230
230
  // <substitute text, one or more lines>
231
231
  // [File EOF]
232
232
  //
233
+ // Special line starts:
234
+ // # comment
235
+ // \# line starts with #
236
+ // \\ line starts with \
237
+ //
233
238
  // Note that the newlines before and after the substitute text are not included. This returns a
234
239
  // a dictionary of variables and replacement text. The defs argument is optional, and defines a
235
240
  // substitution dictionary applied to the replacement text defined in this file.
236
241
  //
237
- function parseSubstitutionLines(lines, defs, local ignoregex, varegex, line, find, subs, curvar, sub) {
242
+ function parseSubstitutionLines(lines, defs,
243
+ local ignoregex, escapex, varegex, line, find, subs, curvar, sub) {
238
244
  subs = { }
239
245
  ignoregex = RegExp("^#")
246
+ escapex = RegExp("^\\\\(#|\\\\)")
240
247
  varegex = RegExp("^[$]([^$]+)[$]")
241
248
  for line in lines {
242
- if ignoregex.exec(line)
243
- continue
249
+ if ignoregex.test(line)
250
+ continue // comment
251
+ if escapex.test(line)
252
+ line = line.slice(1) // not-a-comment escape
244
253
  if find = line.match(varegex) {
245
254
  curvar = find[1]
246
255
  subs[curvar] = ""
@@ -271,6 +280,11 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
271
280
  // <substitute text, one or more lines>
272
281
  // [File EOF]
273
282
  //
283
+ // Special line starts:
284
+ // # comment
285
+ // \# line starts with #
286
+ // \\ line starts with \
287
+ //
274
288
  // Note that the newlines before and after the substitute text are not included. This returns a
275
289
  // standard `(error, subs) result tuple where subs is a dictionary of variables and replacement
276
290
  // text. The defs argument is optional, and defines a substitution dictionary applied to the
@@ -293,7 +307,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
293
307
  //
294
308
  function applySubstitutions(text, subs, local subbed) {
295
309
  subbed = { }
296
- list(text.replace(RegExp("[$][-A-Za-z0-9_./()\\\\]+[$]", "g"), function(match, local subst) {
310
+ list(text.replace(RegExp("[$][^$\\s]+[$]", "g"), function(match, local subst) {
297
311
  subst = subs[match.slice(1,-1)]
298
312
  if subst {
299
313
  subbed[match] = true
@@ -368,7 +382,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
368
382
  // for each file. The result is a standard (error, data) tuple.
369
383
 
370
384
  function zip1(files, options, local libzip, ziparc, file, result) {
371
- libzip = require("frameworks/project/jszip.nlg").JSZip()
385
+ libzip = require("naanlib:frameworks/project/jszip.nlg").JSZip()
372
386
  if !libzip
373
387
  return (list(Error("Build requires zip but it's not available")))
374
388
  ziparc = libzip.create()
@@ -296,7 +296,7 @@ closure projConnector(projman, projtype, local connector, watch) {
296
296
  *
297
297
  * Nide.proj/
298
298
  * nide_cliser.cfg - JSON definition of the project (see below)
299
- * NaanIDE_version.txt - [default] substitution file defining 1.4.0+1 etc.
299
+ * NaanIDE_version.txt - [default] substitution file defining 1.4.2+1 etc.
300
300
  * NaanIDE_version_builds.txt - [default] contains current build number as decimal string
301
301
  * build/ - [optional] default build output location
302
302
  * NaanIDE.lic - [optional] defines Zulch Laboratories, Inc. and other license info
@@ -563,6 +563,17 @@ closure projConfig(projman, where, projID, local procon, fs, configpath) {
563
563
  }
564
564
  }
565
565
 
566
+ // persist
567
+ //
568
+ // Return a dictionary for persistent local project state, like view configuration.
569
+ //
570
+ procon.persist = function persist() {
571
+ if procon.localDict.persist {
572
+ }
573
+ else
574
+ procon.localDict.persist = {}
575
+ }
576
+
566
577
  // buildrules
567
578
  //
568
579
  // Get the build rules for the named stage, reloading the project configuration if changed.