@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.esm.js
CHANGED
|
@@ -8843,6 +8843,7 @@ class VBindings {
|
|
|
8843
8843
|
*/
|
|
8844
8844
|
clearChanges() {
|
|
8845
8845
|
this.#changes.clear();
|
|
8846
|
+
this.#changeLog.length = 0;
|
|
8846
8847
|
if (this.#dirtyScopes) {
|
|
8847
8848
|
for (const scope of this.#dirtyScopes) {
|
|
8848
8849
|
scope.#changes.clear();
|
|
@@ -8856,14 +8857,39 @@ class VBindings {
|
|
|
8856
8857
|
*/
|
|
8857
8858
|
#addChange(key) {
|
|
8858
8859
|
this.#changes.add(key);
|
|
8859
|
-
|
|
8860
|
-
|
|
8860
|
+
let root = this;
|
|
8861
|
+
while (root.#parent) {
|
|
8862
|
+
root = root.#parent;
|
|
8863
|
+
}
|
|
8864
|
+
root.#changeLog.push(key);
|
|
8865
|
+
if (root !== this) {
|
|
8866
|
+
(root.#dirtyScopes ??= new Set()).add(this);
|
|
8861
8867
|
}
|
|
8868
|
+
}
|
|
8869
|
+
/**
|
|
8870
|
+
* Every change recorded since the last clearChanges(), in order and with repeats, on the
|
|
8871
|
+
* root; the change set itself cannot tell a write made during an update cycle from the one
|
|
8872
|
+
* the cycle was scheduled for when both touch the same identifier.
|
|
8873
|
+
*/
|
|
8874
|
+
#changeLog = [];
|
|
8875
|
+
/**
|
|
8876
|
+
* A marker for {@link changesSince}: the number of changes recorded so far.
|
|
8877
|
+
*/
|
|
8878
|
+
get changeLogLength() {
|
|
8879
|
+
return this.#root.#changeLog.length;
|
|
8880
|
+
}
|
|
8881
|
+
/**
|
|
8882
|
+
* The distinct identifiers recorded after the marker was taken.
|
|
8883
|
+
*/
|
|
8884
|
+
changesSince(marker) {
|
|
8885
|
+
return Array.from(new Set(this.#root.#changeLog.slice(marker)));
|
|
8886
|
+
}
|
|
8887
|
+
get #root() {
|
|
8862
8888
|
let root = this;
|
|
8863
8889
|
while (root.#parent) {
|
|
8864
8890
|
root = root.#parent;
|
|
8865
8891
|
}
|
|
8866
|
-
|
|
8892
|
+
return root;
|
|
8867
8893
|
}
|
|
8868
8894
|
/**
|
|
8869
8895
|
* Sets a binding value.
|
|
@@ -10769,8 +10795,7 @@ class VNode {
|
|
|
10769
10795
|
const changed = this.bindings.isAffected(changes, this.#textEvaluator.identifiers, this.#textEvaluator.trackedPaths);
|
|
10770
10796
|
// If the text node has changed, update its content
|
|
10771
10797
|
if (changed) {
|
|
10772
|
-
|
|
10773
|
-
text.data = this.#textEvaluator.evaluate(this.bindings);
|
|
10798
|
+
this.#renderText();
|
|
10774
10799
|
}
|
|
10775
10800
|
}
|
|
10776
10801
|
else if (this.#nodeType === Node.ELEMENT_NODE) {
|
|
@@ -10791,7 +10816,7 @@ class VNode {
|
|
|
10791
10816
|
for (const preparer of this.#directiveManager.bindingsPreparers) {
|
|
10792
10817
|
const changed = this.bindings.isAffected(changes, preparer.dependentIdentifiers, preparer.trackedPaths);
|
|
10793
10818
|
if (changed) {
|
|
10794
|
-
preparer.prepareBindings();
|
|
10819
|
+
this.#renderSafely(() => preparer.prepareBindings());
|
|
10795
10820
|
}
|
|
10796
10821
|
}
|
|
10797
10822
|
}
|
|
@@ -10800,7 +10825,7 @@ class VNode {
|
|
|
10800
10825
|
for (const updater of this.#directiveManager.domUpdaters) {
|
|
10801
10826
|
const changed = this.bindings.isAffected(changes, updater.dependentIdentifiers, updater.trackedPaths);
|
|
10802
10827
|
if (changed) {
|
|
10803
|
-
updater.applyToDOM();
|
|
10828
|
+
this.#renderSafely(() => updater.applyToDOM());
|
|
10804
10829
|
}
|
|
10805
10830
|
}
|
|
10806
10831
|
}
|
|
@@ -10838,8 +10863,7 @@ class VNode {
|
|
|
10838
10863
|
forceUpdate() {
|
|
10839
10864
|
if (this.#nodeType === Node.TEXT_NODE && this.#textEvaluator) {
|
|
10840
10865
|
// If this is a text node with a text evaluator, update its content if needed
|
|
10841
|
-
|
|
10842
|
-
text.data = this.#textEvaluator.evaluate(this.bindings);
|
|
10866
|
+
this.#renderText();
|
|
10843
10867
|
}
|
|
10844
10868
|
else if (this.#nodeType === Node.ELEMENT_NODE) {
|
|
10845
10869
|
// If this is an element node, update directives and child nodes
|
|
@@ -10857,13 +10881,13 @@ class VNode {
|
|
|
10857
10881
|
}
|
|
10858
10882
|
// Prepare bindings for each preparer if relevant identifiers have changed
|
|
10859
10883
|
for (const preparer of this.#directiveManager.bindingsPreparers) {
|
|
10860
|
-
preparer.prepareBindings();
|
|
10884
|
+
this.#renderSafely(() => preparer.prepareBindings());
|
|
10861
10885
|
}
|
|
10862
10886
|
}
|
|
10863
10887
|
// Apply DOM updaters from directives, if any
|
|
10864
10888
|
if (this.#directiveManager?.domUpdaters) {
|
|
10865
10889
|
for (const updater of this.#directiveManager.domUpdaters) {
|
|
10866
|
-
updater.applyToDOM();
|
|
10890
|
+
this.#renderSafely(() => updater.applyToDOM());
|
|
10867
10891
|
}
|
|
10868
10892
|
}
|
|
10869
10893
|
// Recursively update child virtual nodes. A copy: an update can
|
|
@@ -10885,6 +10909,38 @@ class VNode {
|
|
|
10885
10909
|
});
|
|
10886
10910
|
}
|
|
10887
10911
|
}
|
|
10912
|
+
/**
|
|
10913
|
+
* Evaluates this text node's expressions into its content.
|
|
10914
|
+
*/
|
|
10915
|
+
#renderText() {
|
|
10916
|
+
const text = this.#node;
|
|
10917
|
+
this.#renderSafely(() => {
|
|
10918
|
+
text.data = this.#textEvaluator.evaluate(this.bindings);
|
|
10919
|
+
});
|
|
10920
|
+
}
|
|
10921
|
+
/**
|
|
10922
|
+
* Runs one binding's render step. A binding whose expression throws (for example one that
|
|
10923
|
+
* reads a member of a value that is still undefined) is logged and left as it was, so that
|
|
10924
|
+
* it does not stop the rest of the tree from rendering.
|
|
10925
|
+
* @param render The render step.
|
|
10926
|
+
*/
|
|
10927
|
+
#renderSafely(render) {
|
|
10928
|
+
try {
|
|
10929
|
+
render();
|
|
10930
|
+
}
|
|
10931
|
+
catch (error) {
|
|
10932
|
+
this.#vApplication.logManager.getLogger(this.constructor.name).error(`Error rendering ${this.#describe()}: ${error}`);
|
|
10933
|
+
}
|
|
10934
|
+
}
|
|
10935
|
+
/**
|
|
10936
|
+
* Describes this node for a log message: a text node by its text, an element by its tag.
|
|
10937
|
+
*/
|
|
10938
|
+
#describe() {
|
|
10939
|
+
if (this.#nodeType === Node.TEXT_NODE) {
|
|
10940
|
+
return `text "${(this.#node.textContent ?? '').trim()}"`;
|
|
10941
|
+
}
|
|
10942
|
+
return `<${this.#nodeName.toLowerCase()}>`;
|
|
10943
|
+
}
|
|
10888
10944
|
/**
|
|
10889
10945
|
* Adds a child virtual node to this virtual node.
|
|
10890
10946
|
* @param child The child virtual node to add.
|
|
@@ -15446,6 +15502,14 @@ class VApplication {
|
|
|
15446
15502
|
* Flag to indicate if an update is already scheduled.
|
|
15447
15503
|
*/
|
|
15448
15504
|
#updateScheduled = false;
|
|
15505
|
+
/**
|
|
15506
|
+
* How many update cycles in a row ended with changes made during the cycle (by a watcher
|
|
15507
|
+
* or a lifecycle hook), each of which runs another cycle. Reset when a cycle ends clean;
|
|
15508
|
+
* past MAX_CASCADED_UPDATES the late changes are dropped, so a watcher that writes on every
|
|
15509
|
+
* cycle cannot keep the application updating forever.
|
|
15510
|
+
*/
|
|
15511
|
+
#cascadedUpdates = 0;
|
|
15512
|
+
static #MAX_CASCADED_UPDATES = 100;
|
|
15449
15513
|
/**
|
|
15450
15514
|
* The custom element hosting this application when it powers a component
|
|
15451
15515
|
* defined via defineComponent(). Set by IchigoElement before mounting.
|
|
@@ -15603,8 +15667,11 @@ class VApplication {
|
|
|
15603
15667
|
vApplication: this,
|
|
15604
15668
|
bindings: this.#bindings
|
|
15605
15669
|
});
|
|
15606
|
-
// Initial rendering
|
|
15607
|
-
|
|
15670
|
+
// Initial rendering. Every binding is applied, not only those whose identifiers were
|
|
15671
|
+
// recorded as changed while the data was set up: a value that starts undefined is never
|
|
15672
|
+
// recorded (undefined -> undefined is no change), and an expression over globals only
|
|
15673
|
+
// has no identifier to record at all.
|
|
15674
|
+
this.#vNode.forceUpdate();
|
|
15608
15675
|
// Expand the components in the tree. Last, so every component element
|
|
15609
15676
|
// has been compiled and has had its prop values delivered before its
|
|
15610
15677
|
// template is cloned in. See VNode.expandComponents.
|
|
@@ -15889,8 +15956,10 @@ class VApplication {
|
|
|
15889
15956
|
}
|
|
15890
15957
|
this.#updateScheduled = true;
|
|
15891
15958
|
queueMicrotask(() => {
|
|
15892
|
-
|
|
15959
|
+
// Cleared before the update runs, not after: a change made during the update (by a
|
|
15960
|
+
// watcher or a lifecycle hook) then schedules the next cycle instead of being dropped.
|
|
15893
15961
|
this.#updateScheduled = false;
|
|
15962
|
+
this.#update();
|
|
15894
15963
|
});
|
|
15895
15964
|
}
|
|
15896
15965
|
/**
|
|
@@ -15904,14 +15973,46 @@ class VApplication {
|
|
|
15904
15973
|
// This converts paths like "model.elements[0].executionListeners"
|
|
15905
15974
|
// to "selectedElement.executionListeners" when selectedElement points to model.elements[0]
|
|
15906
15975
|
this.#applyComputedPathMappings();
|
|
15976
|
+
// The changes this cycle was scheduled for. Whatever is recorded after this point was
|
|
15977
|
+
// written during the cycle itself (see below); the marker tells the two apart even when
|
|
15978
|
+
// a watcher writes the very identifier it watches.
|
|
15979
|
+
const scheduledChanges = this.#bindings ? this.#bindings.changes : [];
|
|
15980
|
+
const lateMarker = this.#bindings ? this.#bindings.changeLogLength : 0;
|
|
15907
15981
|
// Notify watchers before DOM update
|
|
15908
15982
|
if (this.#bindings) {
|
|
15909
|
-
this.#watcher.notify(
|
|
15983
|
+
this.#watcher.notify(scheduledChanges, (p) => this.#getValueByPath(p), this.#bindings.raw);
|
|
15984
|
+
// A watcher may have written data. The computed properties that depend on it were
|
|
15985
|
+
// only marked dirty, which the DOM diff below does not see; resolve them now, so this
|
|
15986
|
+
// render already shows what the watcher did, and so the computed names are in the
|
|
15987
|
+
// change set when the diff runs.
|
|
15988
|
+
this.#flushDirtyComputeds();
|
|
15989
|
+
this.#applyComputedPathMappings();
|
|
15910
15990
|
}
|
|
15911
15991
|
// Update the DOM
|
|
15912
15992
|
this.#vNode?.update();
|
|
15913
|
-
// Clear the set of changed identifiers after the update
|
|
15914
|
-
|
|
15993
|
+
// Clear the set of changed identifiers after the update. Changes recorded during the
|
|
15994
|
+
// cycle (by watchers or lifecycle hooks) are carried into the next cycle instead: the DOM
|
|
15995
|
+
// diff above has seen them, but watchers that depend on them have not, since watcher
|
|
15996
|
+
// notification ran before they were written.
|
|
15997
|
+
if (this.#bindings) {
|
|
15998
|
+
const late = this.#bindings.changesSince(lateMarker);
|
|
15999
|
+
this.#bindings.clearChanges();
|
|
16000
|
+
if (late.length > 0) {
|
|
16001
|
+
this.#cascadedUpdates++;
|
|
16002
|
+
if (this.#cascadedUpdates > VApplication.#MAX_CASCADED_UPDATES) {
|
|
16003
|
+
this.#logger.warn(`Update cycle did not settle after ${VApplication.#MAX_CASCADED_UPDATES} cascaded updates; dropping changes: ${late.join(', ')}`);
|
|
16004
|
+
this.#cascadedUpdates = 0;
|
|
16005
|
+
return;
|
|
16006
|
+
}
|
|
16007
|
+
for (const change of late) {
|
|
16008
|
+
this.#bindings.markChanged(change);
|
|
16009
|
+
}
|
|
16010
|
+
this.#scheduleUpdate();
|
|
16011
|
+
}
|
|
16012
|
+
else {
|
|
16013
|
+
this.#cascadedUpdates = 0;
|
|
16014
|
+
}
|
|
16015
|
+
}
|
|
15915
16016
|
}
|
|
15916
16017
|
/**
|
|
15917
16018
|
* Applies computed source path mappings to the current changes.
|