@mintjamsinc/ichigojs 0.1.87 → 0.1.89
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/dist/ichigo.cjs +118 -17
- package/dist/ichigo.cjs.map +1 -1
- package/dist/ichigo.esm.js +118 -17
- package/dist/ichigo.esm.js.map +1 -1
- package/dist/ichigo.esm.min.js +1 -1
- package/dist/ichigo.min.cjs +1 -1
- package/dist/ichigo.umd.js +118 -17
- package/dist/ichigo.umd.js.map +1 -1
- package/dist/ichigo.umd.min.js +1 -1
- package/dist/types/ichigo/VBindings.d.ts +8 -0
- package/package.json +1 -1
package/dist/ichigo.cjs
CHANGED
|
@@ -8849,6 +8849,7 @@
|
|
|
8849
8849
|
*/
|
|
8850
8850
|
clearChanges() {
|
|
8851
8851
|
this.#changes.clear();
|
|
8852
|
+
this.#changeLog.length = 0;
|
|
8852
8853
|
if (this.#dirtyScopes) {
|
|
8853
8854
|
for (const scope of this.#dirtyScopes) {
|
|
8854
8855
|
scope.#changes.clear();
|
|
@@ -8862,14 +8863,39 @@
|
|
|
8862
8863
|
*/
|
|
8863
8864
|
#addChange(key) {
|
|
8864
8865
|
this.#changes.add(key);
|
|
8865
|
-
|
|
8866
|
-
|
|
8866
|
+
let root = this;
|
|
8867
|
+
while (root.#parent) {
|
|
8868
|
+
root = root.#parent;
|
|
8869
|
+
}
|
|
8870
|
+
root.#changeLog.push(key);
|
|
8871
|
+
if (root !== this) {
|
|
8872
|
+
(root.#dirtyScopes ??= new Set()).add(this);
|
|
8867
8873
|
}
|
|
8874
|
+
}
|
|
8875
|
+
/**
|
|
8876
|
+
* Every change recorded since the last clearChanges(), in order and with repeats, on the
|
|
8877
|
+
* root; the change set itself cannot tell a write made during an update cycle from the one
|
|
8878
|
+
* the cycle was scheduled for when both touch the same identifier.
|
|
8879
|
+
*/
|
|
8880
|
+
#changeLog = [];
|
|
8881
|
+
/**
|
|
8882
|
+
* A marker for {@link changesSince}: the number of changes recorded so far.
|
|
8883
|
+
*/
|
|
8884
|
+
get changeLogLength() {
|
|
8885
|
+
return this.#root.#changeLog.length;
|
|
8886
|
+
}
|
|
8887
|
+
/**
|
|
8888
|
+
* The distinct identifiers recorded after the marker was taken.
|
|
8889
|
+
*/
|
|
8890
|
+
changesSince(marker) {
|
|
8891
|
+
return Array.from(new Set(this.#root.#changeLog.slice(marker)));
|
|
8892
|
+
}
|
|
8893
|
+
get #root() {
|
|
8868
8894
|
let root = this;
|
|
8869
8895
|
while (root.#parent) {
|
|
8870
8896
|
root = root.#parent;
|
|
8871
8897
|
}
|
|
8872
|
-
|
|
8898
|
+
return root;
|
|
8873
8899
|
}
|
|
8874
8900
|
/**
|
|
8875
8901
|
* Sets a binding value.
|
|
@@ -10775,8 +10801,7 @@
|
|
|
10775
10801
|
const changed = this.bindings.isAffected(changes, this.#textEvaluator.identifiers, this.#textEvaluator.trackedPaths);
|
|
10776
10802
|
// If the text node has changed, update its content
|
|
10777
10803
|
if (changed) {
|
|
10778
|
-
|
|
10779
|
-
text.data = this.#textEvaluator.evaluate(this.bindings);
|
|
10804
|
+
this.#renderText();
|
|
10780
10805
|
}
|
|
10781
10806
|
}
|
|
10782
10807
|
else if (this.#nodeType === Node.ELEMENT_NODE) {
|
|
@@ -10797,7 +10822,7 @@
|
|
|
10797
10822
|
for (const preparer of this.#directiveManager.bindingsPreparers) {
|
|
10798
10823
|
const changed = this.bindings.isAffected(changes, preparer.dependentIdentifiers, preparer.trackedPaths);
|
|
10799
10824
|
if (changed) {
|
|
10800
|
-
preparer.prepareBindings();
|
|
10825
|
+
this.#renderSafely(() => preparer.prepareBindings());
|
|
10801
10826
|
}
|
|
10802
10827
|
}
|
|
10803
10828
|
}
|
|
@@ -10806,7 +10831,7 @@
|
|
|
10806
10831
|
for (const updater of this.#directiveManager.domUpdaters) {
|
|
10807
10832
|
const changed = this.bindings.isAffected(changes, updater.dependentIdentifiers, updater.trackedPaths);
|
|
10808
10833
|
if (changed) {
|
|
10809
|
-
updater.applyToDOM();
|
|
10834
|
+
this.#renderSafely(() => updater.applyToDOM());
|
|
10810
10835
|
}
|
|
10811
10836
|
}
|
|
10812
10837
|
}
|
|
@@ -10844,8 +10869,7 @@
|
|
|
10844
10869
|
forceUpdate() {
|
|
10845
10870
|
if (this.#nodeType === Node.TEXT_NODE && this.#textEvaluator) {
|
|
10846
10871
|
// If this is a text node with a text evaluator, update its content if needed
|
|
10847
|
-
|
|
10848
|
-
text.data = this.#textEvaluator.evaluate(this.bindings);
|
|
10872
|
+
this.#renderText();
|
|
10849
10873
|
}
|
|
10850
10874
|
else if (this.#nodeType === Node.ELEMENT_NODE) {
|
|
10851
10875
|
// If this is an element node, update directives and child nodes
|
|
@@ -10863,13 +10887,13 @@
|
|
|
10863
10887
|
}
|
|
10864
10888
|
// Prepare bindings for each preparer if relevant identifiers have changed
|
|
10865
10889
|
for (const preparer of this.#directiveManager.bindingsPreparers) {
|
|
10866
|
-
preparer.prepareBindings();
|
|
10890
|
+
this.#renderSafely(() => preparer.prepareBindings());
|
|
10867
10891
|
}
|
|
10868
10892
|
}
|
|
10869
10893
|
// Apply DOM updaters from directives, if any
|
|
10870
10894
|
if (this.#directiveManager?.domUpdaters) {
|
|
10871
10895
|
for (const updater of this.#directiveManager.domUpdaters) {
|
|
10872
|
-
updater.applyToDOM();
|
|
10896
|
+
this.#renderSafely(() => updater.applyToDOM());
|
|
10873
10897
|
}
|
|
10874
10898
|
}
|
|
10875
10899
|
// Recursively update child virtual nodes. A copy: an update can
|
|
@@ -10891,6 +10915,38 @@
|
|
|
10891
10915
|
});
|
|
10892
10916
|
}
|
|
10893
10917
|
}
|
|
10918
|
+
/**
|
|
10919
|
+
* Evaluates this text node's expressions into its content.
|
|
10920
|
+
*/
|
|
10921
|
+
#renderText() {
|
|
10922
|
+
const text = this.#node;
|
|
10923
|
+
this.#renderSafely(() => {
|
|
10924
|
+
text.data = this.#textEvaluator.evaluate(this.bindings);
|
|
10925
|
+
});
|
|
10926
|
+
}
|
|
10927
|
+
/**
|
|
10928
|
+
* Runs one binding's render step. A binding whose expression throws (for example one that
|
|
10929
|
+
* reads a member of a value that is still undefined) is logged and left as it was, so that
|
|
10930
|
+
* it does not stop the rest of the tree from rendering.
|
|
10931
|
+
* @param render The render step.
|
|
10932
|
+
*/
|
|
10933
|
+
#renderSafely(render) {
|
|
10934
|
+
try {
|
|
10935
|
+
render();
|
|
10936
|
+
}
|
|
10937
|
+
catch (error) {
|
|
10938
|
+
this.#vApplication.logManager.getLogger(this.constructor.name).error(`Error rendering ${this.#describe()}: ${error}`);
|
|
10939
|
+
}
|
|
10940
|
+
}
|
|
10941
|
+
/**
|
|
10942
|
+
* Describes this node for a log message: a text node by its text, an element by its tag.
|
|
10943
|
+
*/
|
|
10944
|
+
#describe() {
|
|
10945
|
+
if (this.#nodeType === Node.TEXT_NODE) {
|
|
10946
|
+
return `text "${(this.#node.textContent ?? '').trim()}"`;
|
|
10947
|
+
}
|
|
10948
|
+
return `<${this.#nodeName.toLowerCase()}>`;
|
|
10949
|
+
}
|
|
10894
10950
|
/**
|
|
10895
10951
|
* Adds a child virtual node to this virtual node.
|
|
10896
10952
|
* @param child The child virtual node to add.
|
|
@@ -15452,6 +15508,14 @@
|
|
|
15452
15508
|
* Flag to indicate if an update is already scheduled.
|
|
15453
15509
|
*/
|
|
15454
15510
|
#updateScheduled = false;
|
|
15511
|
+
/**
|
|
15512
|
+
* How many update cycles in a row ended with changes made during the cycle (by a watcher
|
|
15513
|
+
* or a lifecycle hook), each of which runs another cycle. Reset when a cycle ends clean;
|
|
15514
|
+
* past MAX_CASCADED_UPDATES the late changes are dropped, so a watcher that writes on every
|
|
15515
|
+
* cycle cannot keep the application updating forever.
|
|
15516
|
+
*/
|
|
15517
|
+
#cascadedUpdates = 0;
|
|
15518
|
+
static #MAX_CASCADED_UPDATES = 100;
|
|
15455
15519
|
/**
|
|
15456
15520
|
* The custom element hosting this application when it powers a component
|
|
15457
15521
|
* defined via defineComponent(). Set by IchigoElement before mounting.
|
|
@@ -15609,8 +15673,11 @@
|
|
|
15609
15673
|
vApplication: this,
|
|
15610
15674
|
bindings: this.#bindings
|
|
15611
15675
|
});
|
|
15612
|
-
// Initial rendering
|
|
15613
|
-
|
|
15676
|
+
// Initial rendering. Every binding is applied, not only those whose identifiers were
|
|
15677
|
+
// recorded as changed while the data was set up: a value that starts undefined is never
|
|
15678
|
+
// recorded (undefined -> undefined is no change), and an expression over globals only
|
|
15679
|
+
// has no identifier to record at all.
|
|
15680
|
+
this.#vNode.forceUpdate();
|
|
15614
15681
|
// Expand the components in the tree. Last, so every component element
|
|
15615
15682
|
// has been compiled and has had its prop values delivered before its
|
|
15616
15683
|
// template is cloned in. See VNode.expandComponents.
|
|
@@ -15895,8 +15962,10 @@
|
|
|
15895
15962
|
}
|
|
15896
15963
|
this.#updateScheduled = true;
|
|
15897
15964
|
queueMicrotask(() => {
|
|
15898
|
-
|
|
15965
|
+
// Cleared before the update runs, not after: a change made during the update (by a
|
|
15966
|
+
// watcher or a lifecycle hook) then schedules the next cycle instead of being dropped.
|
|
15899
15967
|
this.#updateScheduled = false;
|
|
15968
|
+
this.#update();
|
|
15900
15969
|
});
|
|
15901
15970
|
}
|
|
15902
15971
|
/**
|
|
@@ -15910,14 +15979,46 @@
|
|
|
15910
15979
|
// This converts paths like "model.elements[0].executionListeners"
|
|
15911
15980
|
// to "selectedElement.executionListeners" when selectedElement points to model.elements[0]
|
|
15912
15981
|
this.#applyComputedPathMappings();
|
|
15982
|
+
// The changes this cycle was scheduled for. Whatever is recorded after this point was
|
|
15983
|
+
// written during the cycle itself (see below); the marker tells the two apart even when
|
|
15984
|
+
// a watcher writes the very identifier it watches.
|
|
15985
|
+
const scheduledChanges = this.#bindings ? this.#bindings.changes : [];
|
|
15986
|
+
const lateMarker = this.#bindings ? this.#bindings.changeLogLength : 0;
|
|
15913
15987
|
// Notify watchers before DOM update
|
|
15914
15988
|
if (this.#bindings) {
|
|
15915
|
-
this.#watcher.notify(
|
|
15989
|
+
this.#watcher.notify(scheduledChanges, (p) => this.#getValueByPath(p), this.#bindings.raw);
|
|
15990
|
+
// A watcher may have written data. The computed properties that depend on it were
|
|
15991
|
+
// only marked dirty, which the DOM diff below does not see; resolve them now, so this
|
|
15992
|
+
// render already shows what the watcher did, and so the computed names are in the
|
|
15993
|
+
// change set when the diff runs.
|
|
15994
|
+
this.#flushDirtyComputeds();
|
|
15995
|
+
this.#applyComputedPathMappings();
|
|
15916
15996
|
}
|
|
15917
15997
|
// Update the DOM
|
|
15918
15998
|
this.#vNode?.update();
|
|
15919
|
-
// Clear the set of changed identifiers after the update
|
|
15920
|
-
|
|
15999
|
+
// Clear the set of changed identifiers after the update. Changes recorded during the
|
|
16000
|
+
// cycle (by watchers or lifecycle hooks) are carried into the next cycle instead: the DOM
|
|
16001
|
+
// diff above has seen them, but watchers that depend on them have not, since watcher
|
|
16002
|
+
// notification ran before they were written.
|
|
16003
|
+
if (this.#bindings) {
|
|
16004
|
+
const late = this.#bindings.changesSince(lateMarker);
|
|
16005
|
+
this.#bindings.clearChanges();
|
|
16006
|
+
if (late.length > 0) {
|
|
16007
|
+
this.#cascadedUpdates++;
|
|
16008
|
+
if (this.#cascadedUpdates > VApplication.#MAX_CASCADED_UPDATES) {
|
|
16009
|
+
this.#logger.warn(`Update cycle did not settle after ${VApplication.#MAX_CASCADED_UPDATES} cascaded updates; dropping changes: ${late.join(', ')}`);
|
|
16010
|
+
this.#cascadedUpdates = 0;
|
|
16011
|
+
return;
|
|
16012
|
+
}
|
|
16013
|
+
for (const change of late) {
|
|
16014
|
+
this.#bindings.markChanged(change);
|
|
16015
|
+
}
|
|
16016
|
+
this.#scheduleUpdate();
|
|
16017
|
+
}
|
|
16018
|
+
else {
|
|
16019
|
+
this.#cascadedUpdates = 0;
|
|
16020
|
+
}
|
|
16021
|
+
}
|
|
15921
16022
|
}
|
|
15922
16023
|
/**
|
|
15923
16024
|
* Applies computed source path mappings to the current changes.
|