firefly-compiler 0.6.11 → 0.6.13
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/bin/Release.ff +1 -1
- package/lux/Lux.ff +188 -116
- package/package.json +1 -1
- package/vscode/package.json +1 -1
package/bin/Release.ff
CHANGED
|
@@ -36,7 +36,7 @@ release(
|
|
|
36
36
|
runSuccessful(system, "git", ["commit", "-a", "-m", "Autorelease_v" + version], system.path(".."))
|
|
37
37
|
runSuccessful(system, "git", ["push"], system.path(".."))
|
|
38
38
|
system.writeLine("Due to 2FA requirements, you must now manually run:")
|
|
39
|
-
system.writeLine("cd ..; npm publish; npm install -g firefly-compiler")
|
|
39
|
+
system.writeLine("cd ..; npm login; npm publish; npm install -g firefly-compiler")
|
|
40
40
|
//runSuccessful(system, "npm", ["install", "-g", "firefly-compiler"], system.path(".."))
|
|
41
41
|
}
|
|
42
42
|
|
package/lux/Lux.ff
CHANGED
|
@@ -169,7 +169,7 @@ extend self: Lux {
|
|
|
169
169
|
} finally {
|
|
170
170
|
patchText(self)
|
|
171
171
|
if(!self.element.keepChildren) {
|
|
172
|
-
doWhile {removeCurrentChild(self)}
|
|
172
|
+
doWhile {removeCurrentChild(self, self.depth)}
|
|
173
173
|
}
|
|
174
174
|
patchAttributes(self)
|
|
175
175
|
self.depth -= 1
|
|
@@ -268,10 +268,24 @@ extend self: Lux {
|
|
|
268
268
|
onClick(handler: LuxEvent => Unit) {self.on("click", handler)}
|
|
269
269
|
onInput(handler: LuxEvent => Unit) {self.on("input", handler)}
|
|
270
270
|
|
|
271
|
-
|
|
272
|
-
self.
|
|
271
|
+
useHook[T](body: LuxHook[T] => Unit) {
|
|
272
|
+
// if(!self.canHook) {throw(LuxHookException)}
|
|
273
|
+
// self.canHook = False
|
|
273
274
|
self.depth += 1
|
|
274
|
-
|
|
275
|
+
try {
|
|
276
|
+
if(self.dry.isEmpty()) {
|
|
277
|
+
self.element.element.set("_lux$C" + self.depth, {}!)
|
|
278
|
+
}
|
|
279
|
+
body(LuxHook(self.Lux(element = self.element.LuxElement())))
|
|
280
|
+
} finally {
|
|
281
|
+
self.depth -= 1
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
useState[T: HasAnyTag](initialValue: T, body: (T, T => Unit) => Unit) {
|
|
286
|
+
self.useHook: hook =>
|
|
287
|
+
if(hook.dry()) {body(initialValue, {_ => })} else:
|
|
288
|
+
let value = if(hook.hasValue()) {hook.grabValue()} else {initialValue}
|
|
275
289
|
mutable i = 0
|
|
276
290
|
while {i < self.renderQueue.size()} {
|
|
277
291
|
let item = self.renderQueue.grab(i)
|
|
@@ -281,109 +295,85 @@ extend self: Lux {
|
|
|
281
295
|
i += 1
|
|
282
296
|
}
|
|
283
297
|
}
|
|
284
|
-
let luxCopy = self.Lux(element = self.element.LuxElement())
|
|
285
298
|
function setState(newValue: T): Unit {
|
|
286
|
-
|
|
299
|
+
if(hook.luxCopy.element.element.hasOwn("_lux$R")) {
|
|
300
|
+
hook.luxCopy.element = hook.luxCopy.element.LuxElement(
|
|
301
|
+
element = hook.luxCopy.element.element->"_lux$R"
|
|
302
|
+
)
|
|
303
|
+
}
|
|
304
|
+
hook.setValue(newValue)
|
|
287
305
|
self.renderQueue.push(RenderQueueItem(
|
|
288
|
-
luxCopy = luxCopy
|
|
306
|
+
luxCopy = hook.luxCopy
|
|
289
307
|
render = {
|
|
290
|
-
let child = luxCopy.element.child
|
|
291
|
-
let element = luxCopy.element.childAt(child)
|
|
292
308
|
body(newValue, {setState(_)})
|
|
293
|
-
if(!element.equals(luxCopy.element.childAt(child))) {
|
|
294
|
-
removeCurrentChild(luxCopy.Lux(
|
|
295
|
-
element = luxCopy.element.LuxElement(child = luxCopy.element.child + 1)
|
|
296
|
-
))
|
|
297
|
-
}
|
|
298
309
|
}
|
|
299
310
|
))
|
|
300
311
|
}
|
|
301
|
-
|
|
302
|
-
body(value, {setState(_)})
|
|
303
|
-
} finally {
|
|
304
|
-
self.depth -= 1
|
|
305
|
-
}
|
|
312
|
+
body(value, {setState(_)})
|
|
306
313
|
}
|
|
307
314
|
|
|
308
|
-
useCallback1[A1
|
|
309
|
-
self.
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
body {a1 =>
|
|
316
|
-
let latestCallback = getStateOnElement(element, depth, callback)
|
|
315
|
+
useCallback1[A1](callback: A1 => Unit, body: (A1 => Unit) => Unit) {
|
|
316
|
+
self.useHook: hook =>
|
|
317
|
+
if(hook.dry()) {body(callback)} else:
|
|
318
|
+
hook.setValue(callback)
|
|
319
|
+
body {a1 =>
|
|
320
|
+
let latestCallback = hook.grabValue()
|
|
321
|
+
self.renderLock.do {
|
|
317
322
|
latestCallback(a1)
|
|
323
|
+
processRenderQueue(self)
|
|
318
324
|
}
|
|
319
|
-
} finally {
|
|
320
|
-
self.depth -= 1
|
|
321
325
|
}
|
|
322
326
|
}
|
|
323
327
|
|
|
324
328
|
useLazy1[A1: Equal: HasAnyTag](a1: A1, body: A1 => Unit = {_ => }) {
|
|
325
|
-
self.
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
| _ =>
|
|
333
|
-
setStateOnElement(self.element.element, self.depth, Some(a1))
|
|
334
|
-
body(a1)
|
|
335
|
-
}
|
|
336
|
-
} finally {
|
|
337
|
-
self.depth -= 1
|
|
329
|
+
self.useHook: hook =>
|
|
330
|
+
if(hook.dry()) {body(a1)} else:
|
|
331
|
+
if(!hook.hasValue() || hook.grabValue() != a1) {
|
|
332
|
+
hook.setValue(a1)
|
|
333
|
+
body(a1)
|
|
334
|
+
} else {
|
|
335
|
+
self.element.keepChildren = True
|
|
338
336
|
}
|
|
339
337
|
}
|
|
340
338
|
|
|
341
339
|
useMemo1[A1: Equal: HasAnyTag, T: HasAnyTag](a1: A1, compute: A1 => T, body: T => Unit) {
|
|
342
|
-
self.
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
let
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
v
|
|
353
|
-
| Pair(_, None) =>
|
|
354
|
-
let v = compute(a1)
|
|
355
|
-
setStateOnElement(self.element.element, self.depth, Pair(a1, Some(v)))
|
|
356
|
-
v
|
|
340
|
+
self.useHook: hook =>
|
|
341
|
+
if(hook.dry()) {body(compute(a1))} else:
|
|
342
|
+
if(hook.hasValue()) {
|
|
343
|
+
let pair: Pair[A1, T] = hook.grabValue()
|
|
344
|
+
if(pair.first == a1) {
|
|
345
|
+
body(pair.second)
|
|
346
|
+
} else {
|
|
347
|
+
let value = compute(a1)
|
|
348
|
+
hook.setValue(Pair(a1, value))
|
|
349
|
+
body(value)
|
|
357
350
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
351
|
+
} else {
|
|
352
|
+
let value = compute(a1)
|
|
353
|
+
hook.setValue(Pair(a1, value))
|
|
354
|
+
body(value)
|
|
361
355
|
}
|
|
362
356
|
}
|
|
363
|
-
|
|
357
|
+
|
|
364
358
|
useSuspense(suspense: () => Unit, body: Lux => Unit) {
|
|
365
|
-
self.
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
let
|
|
371
|
-
|
|
372
|
-
let subtask = self.task.spawn {task =>
|
|
373
|
-
let lux = luxCopy.Lux(
|
|
359
|
+
self.useHook: hook =>
|
|
360
|
+
if(hook.dry()) {suspense()} else:
|
|
361
|
+
hook.cleanup()
|
|
362
|
+
suspense()
|
|
363
|
+
let fragment = hook.luxCopy.document.createFragment()
|
|
364
|
+
let subtask = hook.luxCopy.task.spawn {task =>
|
|
365
|
+
let lux = hook.luxCopy.Lux(
|
|
374
366
|
task = task
|
|
375
367
|
renderLock = task.lock()
|
|
376
368
|
renderQueue = Array.new()
|
|
377
|
-
element = luxCopy.element.LuxElement(element = fragment)
|
|
369
|
+
element = hook.luxCopy.element.LuxElement(element = fragment)
|
|
378
370
|
)
|
|
379
371
|
try {
|
|
380
372
|
body(lux)
|
|
381
373
|
task.throwIfAborted()
|
|
382
|
-
lux.copyFrom(luxCopy)
|
|
374
|
+
lux.copyFrom(hook.luxCopy)
|
|
383
375
|
lux.renderLock.do {
|
|
384
|
-
|
|
385
|
-
lux.element.removeAt(lux.element.child)
|
|
386
|
-
lux.element.insertBefore(fragment, lux.jsSystem.null())
|
|
376
|
+
replaceWithFragment(lux.element.element, fragment, lux.depth)
|
|
387
377
|
}
|
|
388
378
|
} catchAny {error =>
|
|
389
379
|
if(error.name() != "AbortError") {
|
|
@@ -391,10 +381,128 @@ extend self: Lux {
|
|
|
391
381
|
}
|
|
392
382
|
}
|
|
393
383
|
}
|
|
394
|
-
|
|
395
|
-
|
|
384
|
+
hook.setCleanup {subtask.abort()}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
useWebSocket(
|
|
388
|
+
url: String
|
|
389
|
+
onMessage: Buffer => Unit
|
|
390
|
+
body: (Buffer => Unit) => Unit
|
|
391
|
+
) {
|
|
392
|
+
self.useHook: hook =>
|
|
393
|
+
if(hook.dry()) {} else:
|
|
394
|
+
self.useCallback1 {buffer =>
|
|
395
|
+
onMessage(buffer)
|
|
396
|
+
}: onMessage =>
|
|
397
|
+
function createWebSocket(): JsValue {
|
|
398
|
+
let socket = Js->WebSocket->(url)
|
|
399
|
+
hook.setValue(Pair(socket, url))
|
|
400
|
+
hook.setCleanup {
|
|
401
|
+
socket->close()
|
|
402
|
+
}
|
|
403
|
+
socket->binaryType = "arraybuffer"
|
|
404
|
+
Js.awaitCancellablePromise {resolve, reject, onSettle =>
|
|
405
|
+
socket->onopen = Js->{resolve(Unit)}
|
|
406
|
+
socket->onclose = Js->{reject(_?)}
|
|
407
|
+
}
|
|
408
|
+
socket->onopen = Js.null()
|
|
409
|
+
socket->onclose = Js.null()
|
|
410
|
+
socket->addEventListener("close", Js->{
|
|
411
|
+
Log.debug("Closed")
|
|
412
|
+
})
|
|
413
|
+
socket->addEventListener("message", Js->{m =>
|
|
414
|
+
let buffer = Js->DataView->(m->data).grabBuffer() // TODO: Handle text
|
|
415
|
+
onMessage(buffer)
|
|
416
|
+
})
|
|
417
|
+
socket->addEventListener("error", Js->{e =>
|
|
418
|
+
Log.debug("Error")
|
|
419
|
+
})
|
|
420
|
+
socket
|
|
421
|
+
}
|
|
422
|
+
let socket = if(!hook.hasValue()) {
|
|
423
|
+
createWebSocket()
|
|
424
|
+
} else {
|
|
425
|
+
let pair = hook.grabValue()
|
|
426
|
+
let oldSocket: JsValue = pair.first
|
|
427
|
+
let oldUrl: String = pair.second
|
|
428
|
+
if(oldUrl == url) {
|
|
429
|
+
oldSocket
|
|
430
|
+
} else {
|
|
431
|
+
hook.cleanup()
|
|
432
|
+
createWebSocket()
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
let send: Buffer => Unit = {buffer => socket->send(buffer)}
|
|
436
|
+
body(send)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
capability LuxHook[T](luxCopy: Lux)
|
|
442
|
+
|
|
443
|
+
extend self[T]: LuxHook[T] {
|
|
444
|
+
dry(): Bool {
|
|
445
|
+
!self.luxCopy.dry.isEmpty()
|
|
446
|
+
}
|
|
447
|
+
setValue(value: T) {
|
|
448
|
+
self.luxCopy.element.element.set("_lux$S" + self.luxCopy.depth, value!)
|
|
449
|
+
}
|
|
450
|
+
hasValue(): Bool {
|
|
451
|
+
self.luxCopy.element.element.hasOwn("_lux$S" + self.luxCopy.depth)
|
|
452
|
+
}
|
|
453
|
+
grabValue(): T {
|
|
454
|
+
let value = self.luxCopy.element.element.get("_lux$S" + self.luxCopy.depth)
|
|
455
|
+
if(value.isUndefined() && !self.hasValue()) {
|
|
456
|
+
throw(GrabException)
|
|
457
|
+
}
|
|
458
|
+
value? // If value is an async function, will the effect type system see through this?
|
|
396
459
|
}
|
|
460
|
+
setCleanup(body: () => Unit) {
|
|
461
|
+
self.luxCopy.element.element.set("_lux$C" + self.luxCopy.depth, body!)
|
|
462
|
+
}
|
|
463
|
+
cleanup() {
|
|
464
|
+
let key = "_lux$C" + self.luxCopy.depth
|
|
465
|
+
let cleanupFunction = self.luxCopy.element.element.get(key)
|
|
466
|
+
self.luxCopy.element.element.set(key, {}!)
|
|
467
|
+
cleanupFunction?() // Async cleanup is not waited for. Is that ok?
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
cleanupElement(element: JsValue, depth: Int) {
|
|
472
|
+
mutable d = depth
|
|
473
|
+
doWhile {
|
|
474
|
+
let cleanupFunction = element.get("_lux$C" + d)
|
|
475
|
+
if(cleanupFunction.isUndefined()) {
|
|
476
|
+
False
|
|
477
|
+
} else {
|
|
478
|
+
Js->Reflect->deleteProperty(element, "_lux$C" + d)
|
|
479
|
+
Js->Reflect->deleteProperty(element, "_lux$S" + d)
|
|
480
|
+
cleanupFunction?() // Async cleanup is not waited for. Is that ok?
|
|
481
|
+
d += 1
|
|
482
|
+
True
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
element->children.each {child =>
|
|
486
|
+
cleanupElement(child, d + 1)
|
|
487
|
+
}
|
|
488
|
+
}
|
|
397
489
|
|
|
490
|
+
replaceWithFragment(element: JsValue, fragment: JsValue, depth: Int) {
|
|
491
|
+
cleanupElement(element, depth) // Assumes that hooks have no siblings
|
|
492
|
+
mutable d = depth
|
|
493
|
+
doWhile {
|
|
494
|
+
let cleanupFunction = fragment.get("_lux$C" + d)
|
|
495
|
+
if(cleanupFunction.isUndefined()) {
|
|
496
|
+
False
|
|
497
|
+
} else {
|
|
498
|
+
element.set("_lux$C" + d, cleanupFunction)
|
|
499
|
+
if(fragment.hasOwn("_lux$S" + d)) {element.set("_lux$S" + d, fragment.get("_lux$S" + d))}
|
|
500
|
+
d += 1
|
|
501
|
+
True
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
fragment->"_lux$R" = element
|
|
505
|
+
element->replaceChildren(fragment)
|
|
398
506
|
}
|
|
399
507
|
|
|
400
508
|
processRenderQueue(self: Lux) {
|
|
@@ -411,50 +519,14 @@ processRenderQueue(self: Lux) {
|
|
|
411
519
|
}
|
|
412
520
|
}
|
|
413
521
|
|
|
414
|
-
removeCurrentChild(self: Lux): Bool {
|
|
522
|
+
removeCurrentChild(self: Lux, depth: Int): Bool {
|
|
415
523
|
let child = self.element.childAt(self.element.child)
|
|
416
524
|
if(!child.isNullOrUndefined() && !child->children.isNullOrUndefined()) {
|
|
417
|
-
|
|
525
|
+
cleanupElement(child, depth)
|
|
418
526
|
}
|
|
419
527
|
self.element.removeAt(self.element.child)
|
|
420
528
|
}
|
|
421
529
|
|
|
422
|
-
getStateOnElement[T /*: HasAnyTag*/](element: JsValue, depth: Int, fallback: T): T {
|
|
423
|
-
let value = element.get("lux" + depth)
|
|
424
|
-
if(value.isNullOrUndefined()) {fallback} else {
|
|
425
|
-
value?
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
setStateOnElement[T /*: HasAnyTag*/](element: JsValue, depth: Int, value: T): Unit {
|
|
430
|
-
element.set("lux" + depth, value!)
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
getTaskOnElement(element: JsValue): Option[Task] {
|
|
435
|
-
let value = element->luxTask
|
|
436
|
-
if(value.isNullOrUndefined()) {None} else {
|
|
437
|
-
value?
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
setTaskOnElement(element: JsValue, task: Option[Task]): Unit {
|
|
442
|
-
element->luxTask = task!
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
abortTasksOnElement(lux: Lux, element: JsValue, onlyChildren: Bool): Unit {
|
|
446
|
-
if(!onlyChildren) {
|
|
447
|
-
getTaskOnElement(element).each {task => forceAsyncAbort(lux, task)}
|
|
448
|
-
}
|
|
449
|
-
element->children.each {child =>
|
|
450
|
-
abortTasksOnElement(lux, child, False)
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
forceAsyncAbort(self: Lux, task: Task): Unit {
|
|
455
|
-
task.abort()
|
|
456
|
-
}
|
|
457
|
-
|
|
458
530
|
patchText(self: Lux) {
|
|
459
531
|
if(!self.texts.isEmpty()):
|
|
460
532
|
let value = self.texts.drain().join()
|
package/package.json
CHANGED