@mintjamsinc/ichigojs 0.1.89 → 0.1.90
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/README.md +21 -0
- package/dist/ichigo.cjs +247 -51
- package/dist/ichigo.cjs.map +1 -1
- package/dist/ichigo.esm.js +247 -51
- 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 +247 -51
- package/dist/ichigo.umd.js.map +1 -1
- package/dist/ichigo.umd.min.js +1 -1
- package/dist/types/ichigo/VBindings.d.ts +10 -1
- package/dist/types/ichigo/VNode.d.ts +9 -0
- package/dist/types/ichigo/VTextEvaluator.d.ts +3 -3
- package/dist/types/ichigo/util/ExpressionEvaluator.d.ts +9 -8
- package/dist/types/ichigo/util/ExpressionUtils.d.ts +10 -0
- package/dist/types/ichigo/util/ReactiveProxy.d.ts +42 -0
- package/package.json +1 -1
package/dist/ichigo.esm.js
CHANGED
|
@@ -6807,6 +6807,13 @@ class ReactiveProxy {
|
|
|
6807
6807
|
* through a reactive proxy. Nested calls record into the innermost set only;
|
|
6808
6808
|
* the previous set is restored when fn returns or throws.
|
|
6809
6809
|
*
|
|
6810
|
+
* The set is kept closed under ancestors: whenever a path is recorded, every
|
|
6811
|
+
* path above it ("items" and "items[2]" for "items[2].name") is in the set as
|
|
6812
|
+
* well. A read reached through a proxy records them on its way down; a read
|
|
6813
|
+
* that starts partway (a proxy a computed returned, a v-for item) has them
|
|
6814
|
+
* added when it is recorded. changeAffectsRead relies on this to answer with
|
|
6815
|
+
* one lookup.
|
|
6816
|
+
*
|
|
6810
6817
|
* @param paths The set that receives the paths read.
|
|
6811
6818
|
* @param fn The function to run.
|
|
6812
6819
|
* @returns The value fn returns.
|
|
@@ -6821,6 +6828,57 @@ class ReactiveProxy {
|
|
|
6821
6828
|
this.tracker = previous;
|
|
6822
6829
|
}
|
|
6823
6830
|
}
|
|
6831
|
+
/**
|
|
6832
|
+
* Records a read made outside any proxy while track() runs: a binding read by
|
|
6833
|
+
* name (`this.flag`, `this[key]`), which the bindings report here. Reads below
|
|
6834
|
+
* a binding are recorded by the proxy itself.
|
|
6835
|
+
*
|
|
6836
|
+
* @param path The binding name read.
|
|
6837
|
+
*/
|
|
6838
|
+
static recordRead(path) {
|
|
6839
|
+
this.tracker?.add(path);
|
|
6840
|
+
}
|
|
6841
|
+
/**
|
|
6842
|
+
* Records a read made through a proxy while track() runs: the member `key` of
|
|
6843
|
+
* the object at `containerPath`. The container's own path and its ancestors are
|
|
6844
|
+
* added first when they are missing, which happens when the proxy was not
|
|
6845
|
+
* reached from a root binding during this run (see track()). A container that
|
|
6846
|
+
* is already recorded has its ancestors recorded too, so the common case is one
|
|
6847
|
+
* lookup.
|
|
6848
|
+
*
|
|
6849
|
+
* @param containerPath The source path of the object read from ("" for a root).
|
|
6850
|
+
* @param key The property key read.
|
|
6851
|
+
*/
|
|
6852
|
+
static recordProxyRead(containerPath, key) {
|
|
6853
|
+
const tracker = this.tracker;
|
|
6854
|
+
if (containerPath && !tracker.has(containerPath)) {
|
|
6855
|
+
for (let i = 1; i < containerPath.length; i++) {
|
|
6856
|
+
const c = containerPath[i];
|
|
6857
|
+
if (c === '.' || c === '[') {
|
|
6858
|
+
tracker.add(containerPath.substring(0, i));
|
|
6859
|
+
}
|
|
6860
|
+
}
|
|
6861
|
+
tracker.add(containerPath);
|
|
6862
|
+
}
|
|
6863
|
+
tracker.add(this.joinPath(containerPath, key));
|
|
6864
|
+
}
|
|
6865
|
+
/**
|
|
6866
|
+
* Checks whether a change touches something read during a tracked run (see
|
|
6867
|
+
* track()): the path read was written, or an object or array holding it was
|
|
6868
|
+
* replaced. A change below a path read does not count, since a read of the
|
|
6869
|
+
* changed member itself would have been recorded too; this keeps a read of a
|
|
6870
|
+
* whole collection ("items") from matching a write to one element.
|
|
6871
|
+
*
|
|
6872
|
+
* The set holds the ancestors of every path read, so a replaced container is
|
|
6873
|
+
* found by the same lookup as the path itself.
|
|
6874
|
+
*
|
|
6875
|
+
* @param changePath The path that was changed (e.g., "items[2].name")
|
|
6876
|
+
* @param readPaths The paths read during the tracked run
|
|
6877
|
+
* @returns True if the change touches a path read
|
|
6878
|
+
*/
|
|
6879
|
+
static changeAffectsRead(changePath, readPaths) {
|
|
6880
|
+
return readPaths.has(changePath);
|
|
6881
|
+
}
|
|
6824
6882
|
/**
|
|
6825
6883
|
* The `Object.prototype.toString` tags of the *only* value types we deeply
|
|
6826
6884
|
* proxy. Everything else is returned untouched (raw).
|
|
@@ -6912,7 +6970,7 @@ class ReactiveProxy {
|
|
|
6912
6970
|
// Function-valued members (push, Map#get, …) are accesses to behaviour,
|
|
6913
6971
|
// not data, so they are left out.
|
|
6914
6972
|
if (ReactiveProxy.tracker && typeof key !== 'symbol' && typeof value !== 'function') {
|
|
6915
|
-
ReactiveProxy.
|
|
6973
|
+
ReactiveProxy.recordProxyRead(path, key);
|
|
6916
6974
|
}
|
|
6917
6975
|
// If the value is an object or array, make it reactive too
|
|
6918
6976
|
if (typeof value === 'object' && value !== null) {
|
|
@@ -7331,6 +7389,18 @@ class ExpressionUtils {
|
|
|
7331
7389
|
});
|
|
7332
7390
|
return Array.from(identifiers);
|
|
7333
7391
|
}
|
|
7392
|
+
/**
|
|
7393
|
+
* Whether an expression refers to one of the application's methods, judged by the
|
|
7394
|
+
* identifiers extractIdentifiers returned for it. A called method may read bindings
|
|
7395
|
+
* the source analysis cannot name (e.g. `this[key]`), so such an expression records
|
|
7396
|
+
* its reads when it runs.
|
|
7397
|
+
* @param identifiers The identifiers extracted from the expression.
|
|
7398
|
+
* @param functionDependencies The application's methods and their dependencies.
|
|
7399
|
+
* @returns True if an identifier names a method.
|
|
7400
|
+
*/
|
|
7401
|
+
static callsFunction(identifiers, functionDependencies) {
|
|
7402
|
+
return identifiers.some(id => Object.prototype.hasOwnProperty.call(functionDependencies, id));
|
|
7403
|
+
}
|
|
7334
7404
|
/**
|
|
7335
7405
|
* Returns the binding name an identifier chain starts with
|
|
7336
7406
|
* (e.g. "items" for "items[0].name" or "user.name").
|
|
@@ -7729,26 +7799,27 @@ class ExpressionEvaluator {
|
|
|
7729
7799
|
*/
|
|
7730
7800
|
additionalContext;
|
|
7731
7801
|
/**
|
|
7732
|
-
* Whether
|
|
7733
|
-
*
|
|
7802
|
+
* Whether the paths read during evaluation are recorded: when an identifier has an
|
|
7803
|
+
* index known only at run time ("items[*].name"), or when the expression calls a
|
|
7804
|
+
* method, whose reads (`this[key]` among them) the source analysis may not see.
|
|
7734
7805
|
*/
|
|
7735
|
-
|
|
7806
|
+
tracksReads;
|
|
7736
7807
|
/**
|
|
7737
|
-
* The source paths read
|
|
7738
|
-
*
|
|
7808
|
+
* The source paths read during the last evaluation, or undefined before the
|
|
7809
|
+
* first evaluation and when nothing is recorded.
|
|
7739
7810
|
*/
|
|
7740
7811
|
trackedPathSet;
|
|
7741
7812
|
/**
|
|
7742
7813
|
* Private constructor. Use ExpressionEvaluator.create() to create instances.
|
|
7743
7814
|
*/
|
|
7744
|
-
constructor(expression, bindings, identifiers, originalIdentifiers, evaluatorFunc, additionalContext) {
|
|
7815
|
+
constructor(expression, bindings, identifiers, originalIdentifiers, evaluatorFunc, callsMethod, additionalContext) {
|
|
7745
7816
|
this.expression = expression;
|
|
7746
7817
|
this.bindings = bindings;
|
|
7747
7818
|
this.identifiers = identifiers;
|
|
7748
7819
|
this.originalIdentifiers = originalIdentifiers;
|
|
7749
7820
|
this.evaluatorFunc = evaluatorFunc;
|
|
7750
7821
|
this.additionalContext = additionalContext;
|
|
7751
|
-
this.
|
|
7822
|
+
this.tracksReads = callsMethod || originalIdentifiers.some(id => id.includes('[*]'));
|
|
7752
7823
|
}
|
|
7753
7824
|
/**
|
|
7754
7825
|
* Creates an ExpressionEvaluator instance.
|
|
@@ -7790,7 +7861,7 @@ class ExpressionEvaluator {
|
|
|
7790
7861
|
// Cache the compiled function
|
|
7791
7862
|
this.cache.set(cacheKey, evaluatorFunc);
|
|
7792
7863
|
}
|
|
7793
|
-
return new ExpressionEvaluator(expression, bindings, baseIdentifiers, extractedIdentifiers, evaluatorFunc, options?.additionalContext);
|
|
7864
|
+
return new ExpressionEvaluator(expression, bindings, baseIdentifiers, extractedIdentifiers, evaluatorFunc, ExpressionUtils.callsFunction(extractedIdentifiers, functionDependencies), options?.additionalContext);
|
|
7794
7865
|
}
|
|
7795
7866
|
/**
|
|
7796
7867
|
* Compiles an expression into a function.
|
|
@@ -7845,9 +7916,11 @@ class ExpressionEvaluator {
|
|
|
7845
7916
|
values.push(...Object.values(this.additionalContext));
|
|
7846
7917
|
}
|
|
7847
7918
|
// Call the compiled function with the gathered values. When an index is
|
|
7848
|
-
// known only at run time, record which concrete
|
|
7849
|
-
//
|
|
7850
|
-
|
|
7919
|
+
// known only at run time or a method is called, record which concrete
|
|
7920
|
+
// paths this evaluation read (see trackedPaths). The values above are
|
|
7921
|
+
// gathered outside the tracked call on purpose: a binding passed in by
|
|
7922
|
+
// name is already among the identifiers.
|
|
7923
|
+
if (!this.tracksReads) {
|
|
7851
7924
|
return this.evaluatorFunc(...values);
|
|
7852
7925
|
}
|
|
7853
7926
|
const paths = new Set();
|
|
@@ -7862,11 +7935,11 @@ class ExpressionEvaluator {
|
|
|
7862
7935
|
}
|
|
7863
7936
|
}
|
|
7864
7937
|
/**
|
|
7865
|
-
* The source paths read
|
|
7866
|
-
*
|
|
7938
|
+
* The source paths read during the last evaluation, recorded only for an
|
|
7939
|
+
* expression with a run-time index ("items[i].name") or a method call.
|
|
7867
7940
|
* Each evaluation replaces the previous set, so a path read for a former
|
|
7868
7941
|
* index value is dropped as soon as the expression runs with the new one.
|
|
7869
|
-
* Undefined before the first evaluation and for expressions
|
|
7942
|
+
* Undefined before the first evaluation and for other expressions.
|
|
7870
7943
|
*/
|
|
7871
7944
|
get trackedPaths() {
|
|
7872
7945
|
return this.trackedPathSet;
|
|
@@ -8658,7 +8731,14 @@ class VBindings {
|
|
|
8658
8731
|
this.#resolveComputedIfDirty(key);
|
|
8659
8732
|
}
|
|
8660
8733
|
if (Reflect.has(obj, key)) {
|
|
8661
|
-
|
|
8734
|
+
const value = Reflect.get(obj, key);
|
|
8735
|
+
// Record the read for a computed property or expression being tracked
|
|
8736
|
+
// (see ReactiveProxy.track). Methods are behaviour, not data; a binding of a
|
|
8737
|
+
// parent scope is recorded by the parent's own trap.
|
|
8738
|
+
if (typeof key === 'string' && typeof value !== 'function') {
|
|
8739
|
+
ReactiveProxy.recordRead(key);
|
|
8740
|
+
}
|
|
8741
|
+
return value;
|
|
8662
8742
|
}
|
|
8663
8743
|
return this.#parent?.raw[key];
|
|
8664
8744
|
},
|
|
@@ -9003,6 +9083,13 @@ class VBindings {
|
|
|
9003
9083
|
markComputedDirty(key) {
|
|
9004
9084
|
this.#dirtyComputeds.add(key);
|
|
9005
9085
|
}
|
|
9086
|
+
/**
|
|
9087
|
+
* Indicates whether a computed property is marked dirty and not yet recomputed.
|
|
9088
|
+
* @param key The computed property name.
|
|
9089
|
+
*/
|
|
9090
|
+
isComputedDirty(key) {
|
|
9091
|
+
return this.#dirtyComputeds.has(key);
|
|
9092
|
+
}
|
|
9006
9093
|
/**
|
|
9007
9094
|
* Reads the currently cached value of a computed property without triggering pull-based
|
|
9008
9095
|
* re-evaluation. Used by the recompute routine to obtain the previous value for change
|
|
@@ -9122,10 +9209,14 @@ class VBindings {
|
|
|
9122
9209
|
* path fits the identifier (not evaluated yet, a raw object, a short-circuited read),
|
|
9123
9210
|
* the identifier keeps matching every index.
|
|
9124
9211
|
*
|
|
9212
|
+
* The recorded paths also count as dependencies of their own: they include what a
|
|
9213
|
+
* method called by the expression read, such as `this[key]`, which the identifiers
|
|
9214
|
+
* found in the source cannot name.
|
|
9215
|
+
*
|
|
9125
9216
|
* @param changes The change paths of the current update cycle.
|
|
9126
9217
|
* @param identifiers The identifiers the expression depends on.
|
|
9127
9218
|
* @param trackedPaths The paths read during the expression's last evaluation, if recorded.
|
|
9128
|
-
* @returns True if some change matches some identifier.
|
|
9219
|
+
* @returns True if some change matches some identifier or recorded path.
|
|
9129
9220
|
*/
|
|
9130
9221
|
isAffected(changes, identifiers, trackedPaths) {
|
|
9131
9222
|
if (changes.length === 0) {
|
|
@@ -9139,6 +9230,9 @@ class VBindings {
|
|
|
9139
9230
|
}
|
|
9140
9231
|
}
|
|
9141
9232
|
}
|
|
9233
|
+
if (trackedPaths && trackedPaths.size > 0) {
|
|
9234
|
+
return changes.some(change => ReactiveProxy.changeAffectsRead(change, trackedPaths));
|
|
9235
|
+
}
|
|
9142
9236
|
return false;
|
|
9143
9237
|
}
|
|
9144
9238
|
/**
|
|
@@ -10205,8 +10299,8 @@ class VTextEvaluator {
|
|
|
10205
10299
|
*/
|
|
10206
10300
|
#evaluate;
|
|
10207
10301
|
/**
|
|
10208
|
-
* The source paths read
|
|
10209
|
-
*
|
|
10302
|
+
* The source paths read during the last evaluation, recorded only when some
|
|
10303
|
+
* expression has a run-time index ("items[i].name") or calls a method.
|
|
10210
10304
|
*/
|
|
10211
10305
|
#trackedPaths;
|
|
10212
10306
|
/**
|
|
@@ -10249,10 +10343,15 @@ class VTextEvaluator {
|
|
|
10249
10343
|
func: new Function(args, funcBody)
|
|
10250
10344
|
};
|
|
10251
10345
|
});
|
|
10252
|
-
// Only an expression with an index known at run time
|
|
10253
|
-
|
|
10254
|
-
//
|
|
10255
|
-
const
|
|
10346
|
+
// Only an expression with an index known at run time, or one that calls a method
|
|
10347
|
+
// (whose reads, such as `this[key]`, the source analysis may not see), needs its
|
|
10348
|
+
// reads recorded
|
|
10349
|
+
const tracksReads = this.#identifiers.some(id => id.includes('[*]')) ||
|
|
10350
|
+
ExpressionUtils.callsFunction(this.#identifiers, functionDependencies);
|
|
10351
|
+
// Create a function to reconstruct the text with evaluated expressions. The values
|
|
10352
|
+
// are gathered outside the tracked call: a binding passed in by name is already
|
|
10353
|
+
// among the identifiers.
|
|
10354
|
+
const evaluateText = (bindings, paths) => {
|
|
10256
10355
|
let result = text;
|
|
10257
10356
|
evaluators.forEach((evaluator, i) => {
|
|
10258
10357
|
// Build values for baseArgs (no dots) in the same order as function parameters
|
|
@@ -10266,15 +10365,16 @@ class VTextEvaluator {
|
|
|
10266
10365
|
});
|
|
10267
10366
|
// Evaluate the expression and replace {{...}} in the text.
|
|
10268
10367
|
// A function replacement keeps `$`-patterns in the value literal.
|
|
10269
|
-
|
|
10368
|
+
const value = paths ? ReactiveProxy.track(paths, () => evaluator.func(...values)) : evaluator.func(...values);
|
|
10369
|
+
result = result.replace(matches[i][0], () => toDisplayString(value));
|
|
10270
10370
|
});
|
|
10271
10371
|
return result;
|
|
10272
10372
|
};
|
|
10273
|
-
this.#evaluate =
|
|
10373
|
+
this.#evaluate = tracksReads
|
|
10274
10374
|
? (bindings) => {
|
|
10275
10375
|
const paths = new Set();
|
|
10276
10376
|
this.#trackedPaths = undefined;
|
|
10277
|
-
const result =
|
|
10377
|
+
const result = evaluateText(bindings, paths);
|
|
10278
10378
|
this.#trackedPaths = paths;
|
|
10279
10379
|
return result;
|
|
10280
10380
|
}
|
|
@@ -10296,9 +10396,9 @@ class VTextEvaluator {
|
|
|
10296
10396
|
return this.#identifiers;
|
|
10297
10397
|
}
|
|
10298
10398
|
/**
|
|
10299
|
-
* The source paths read
|
|
10300
|
-
*
|
|
10301
|
-
* undefined before the first evaluation and for texts
|
|
10399
|
+
* The source paths read during the last evaluation. Recorded only when an
|
|
10400
|
+
* expression has a run-time index ("items[i].name") or calls a method;
|
|
10401
|
+
* undefined before the first evaluation and for other texts.
|
|
10302
10402
|
*/
|
|
10303
10403
|
get trackedPaths() {
|
|
10304
10404
|
return this.#trackedPaths;
|
|
@@ -10719,6 +10819,29 @@ class VNode {
|
|
|
10719
10819
|
this.#dependentIdentifiers = [...new Set(ids)];
|
|
10720
10820
|
return this.#dependentIdentifiers;
|
|
10721
10821
|
}
|
|
10822
|
+
/**
|
|
10823
|
+
* Whether a change of the current update cycle concerns this node, so that a node it
|
|
10824
|
+
* depends on should update it: a change matches one of its identifiers, or touches a
|
|
10825
|
+
* path one of its bindings read the last time it ran. The recorded reads cover what a
|
|
10826
|
+
* called method read, such as `this[key]`, which the identifiers cannot name. Each
|
|
10827
|
+
* binding decides again for itself when the node updates.
|
|
10828
|
+
* @param changes The change paths of the current update cycle.
|
|
10829
|
+
*/
|
|
10830
|
+
isAffectedBy(changes) {
|
|
10831
|
+
if (changes.length === 0) {
|
|
10832
|
+
return false;
|
|
10833
|
+
}
|
|
10834
|
+
if (this.dependentIdentifiers.some(id => changes.some(change => this.bindings.doesChangeMatchIdentifier(change, id)))) {
|
|
10835
|
+
return true;
|
|
10836
|
+
}
|
|
10837
|
+
const recorded = [
|
|
10838
|
+
this.#textEvaluator?.trackedPaths,
|
|
10839
|
+
...(this.#directiveManager?.bindingsPreparers ?? []).map(preparer => preparer.trackedPaths),
|
|
10840
|
+
...(this.#directiveManager?.domUpdaters ?? []).map(updater => updater.trackedPaths),
|
|
10841
|
+
];
|
|
10842
|
+
return recorded.some(paths => paths !== undefined && paths.size > 0 &&
|
|
10843
|
+
changes.some(change => ReactiveProxy.changeAffectsRead(change, paths)));
|
|
10844
|
+
}
|
|
10722
10845
|
get preparableIdentifiers() {
|
|
10723
10846
|
// If already computed, return the cached preparable identifiers
|
|
10724
10847
|
if (this.#preparableIdentifiers) {
|
|
@@ -10831,8 +10954,7 @@ class VNode {
|
|
|
10831
10954
|
}
|
|
10832
10955
|
// Recursively update dependent virtual nodes
|
|
10833
10956
|
this.#dependents?.forEach(dependentNode => {
|
|
10834
|
-
|
|
10835
|
-
if (changed) {
|
|
10957
|
+
if (dependentNode.isAffectedBy(changes)) {
|
|
10836
10958
|
dependentNode.update();
|
|
10837
10959
|
}
|
|
10838
10960
|
});
|
|
@@ -10847,8 +10969,7 @@ class VNode {
|
|
|
10847
10969
|
// For document fragments (e.g. from <template v-if>), propagate updates
|
|
10848
10970
|
// to dependent virtual nodes so that child bindings stay reactive.
|
|
10849
10971
|
this.#dependents?.forEach(dependentNode => {
|
|
10850
|
-
|
|
10851
|
-
if (changed) {
|
|
10972
|
+
if (dependentNode.isAffectedBy(changes)) {
|
|
10852
10973
|
dependentNode.update();
|
|
10853
10974
|
}
|
|
10854
10975
|
});
|
|
@@ -10861,6 +10982,55 @@ class VNode {
|
|
|
10861
10982
|
* This is useful when an immediate update is needed, bypassing the usual change detection.
|
|
10862
10983
|
*/
|
|
10863
10984
|
forceUpdate() {
|
|
10985
|
+
// One pass per outermost call; a call made while a pass runs (a directive rendering
|
|
10986
|
+
// a fresh clone, a component mounting) joins it. The node is stamped with the pass,
|
|
10987
|
+
// so that the recursion over the children below skips a child a directive has
|
|
10988
|
+
// already rendered in this pass (see #forceChildren).
|
|
10989
|
+
const outermost = VNode.#forcePass === 0;
|
|
10990
|
+
if (outermost) {
|
|
10991
|
+
VNode.#forcePass = ++VNode.#forcePassCounter;
|
|
10992
|
+
}
|
|
10993
|
+
this.#forcedInPass = VNode.#forcePass;
|
|
10994
|
+
try {
|
|
10995
|
+
this.#forceUpdate();
|
|
10996
|
+
}
|
|
10997
|
+
finally {
|
|
10998
|
+
if (outermost) {
|
|
10999
|
+
VNode.#forcePass = 0;
|
|
11000
|
+
}
|
|
11001
|
+
}
|
|
11002
|
+
}
|
|
11003
|
+
/**
|
|
11004
|
+
* The pass counter behind {@link forceUpdate}: the last pass number handed out.
|
|
11005
|
+
*/
|
|
11006
|
+
static #forcePassCounter = 0;
|
|
11007
|
+
/**
|
|
11008
|
+
* The pass that is running, or 0 between passes.
|
|
11009
|
+
*/
|
|
11010
|
+
static #forcePass = 0;
|
|
11011
|
+
/**
|
|
11012
|
+
* The pass this node was last forced in, or 0 if never.
|
|
11013
|
+
*/
|
|
11014
|
+
#forcedInPass = 0;
|
|
11015
|
+
/**
|
|
11016
|
+
* Forces the children that have not been forced in the running pass. A v-if / v-for /
|
|
11017
|
+
* slot clone is a child of its host, and the host's directive renders it with its own
|
|
11018
|
+
* forceUpdate() call as soon as it is created, so recursing into it here would render it a
|
|
11019
|
+
* second time. A clone that already existed, and that the directive updated with
|
|
11020
|
+
* update(), has not been forced in this pass and is still forced here.
|
|
11021
|
+
*/
|
|
11022
|
+
#forceChildren() {
|
|
11023
|
+
// A copy: an update can destroy a v-if / v-for clone, which removes it from this list.
|
|
11024
|
+
this.#childVNodes?.slice().forEach(childVNode => {
|
|
11025
|
+
if (childVNode.#forcedInPass !== VNode.#forcePass) {
|
|
11026
|
+
childVNode.forceUpdate();
|
|
11027
|
+
}
|
|
11028
|
+
});
|
|
11029
|
+
}
|
|
11030
|
+
/**
|
|
11031
|
+
* The body of {@link forceUpdate}, run within a pass.
|
|
11032
|
+
*/
|
|
11033
|
+
#forceUpdate() {
|
|
10864
11034
|
if (this.#nodeType === Node.TEXT_NODE && this.#textEvaluator) {
|
|
10865
11035
|
// If this is a text node with a text evaluator, update its content if needed
|
|
10866
11036
|
this.#renderText();
|
|
@@ -10890,11 +11060,8 @@ class VNode {
|
|
|
10890
11060
|
this.#renderSafely(() => updater.applyToDOM());
|
|
10891
11061
|
}
|
|
10892
11062
|
}
|
|
10893
|
-
// Recursively update child virtual nodes
|
|
10894
|
-
|
|
10895
|
-
this.#childVNodes?.slice().forEach(childVNode => {
|
|
10896
|
-
childVNode.forceUpdate();
|
|
10897
|
-
});
|
|
11063
|
+
// Recursively update child virtual nodes
|
|
11064
|
+
this.#forceChildren();
|
|
10898
11065
|
// If no directive requires template preservation, call onUpdated for directives that do not templatize
|
|
10899
11066
|
if (!this.#templatized) {
|
|
10900
11067
|
this.#directiveManager?.directives?.forEach(d => {
|
|
@@ -10904,9 +11071,7 @@ class VNode {
|
|
|
10904
11071
|
}
|
|
10905
11072
|
else if (this.#nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
|
10906
11073
|
// For document fragments, recursively force update child VNodes
|
|
10907
|
-
this.#
|
|
10908
|
-
childVNode.forceUpdate();
|
|
10909
|
-
});
|
|
11074
|
+
this.#forceChildren();
|
|
10910
11075
|
}
|
|
10911
11076
|
}
|
|
10912
11077
|
/**
|
|
@@ -15477,6 +15642,13 @@ class VApplication {
|
|
|
15477
15642
|
* A dictionary mapping computed property names to their dependencies.
|
|
15478
15643
|
*/
|
|
15479
15644
|
#computedDependencies;
|
|
15645
|
+
/**
|
|
15646
|
+
* The paths each computed property read the last time it was evaluated (see
|
|
15647
|
+
* ReactiveProxy.track). A change to one of them marks the computed dirty as well as
|
|
15648
|
+
* a change to the dependencies found in its source, since a read the source does not
|
|
15649
|
+
* spell out, such as `this[key]`, is seen only here.
|
|
15650
|
+
*/
|
|
15651
|
+
#computedReads = new Map();
|
|
15480
15652
|
/**
|
|
15481
15653
|
* A map tracking source paths for computed property values.
|
|
15482
15654
|
* Maps source path (e.g., "model.elements[0]") to computed property name (e.g., "selectedElement").
|
|
@@ -16046,20 +16218,33 @@ class VApplication {
|
|
|
16046
16218
|
}
|
|
16047
16219
|
/**
|
|
16048
16220
|
* Marks computed properties as dirty (pull-based invalidation) when a dependency changes.
|
|
16049
|
-
*
|
|
16050
|
-
* dependencies
|
|
16051
|
-
*
|
|
16052
|
-
*
|
|
16221
|
+
* A computed depends on what its source names (statically analyzed, with computed→computed and
|
|
16222
|
+
* computed→method dependencies flattened to their underlying reactive paths) and on what it read
|
|
16223
|
+
* the last time it ran (#computedReads). A computed marked dirty is itself a change for the
|
|
16224
|
+
* computeds that read it, so marking follows those reads too. A computed already dirty is
|
|
16225
|
+
* skipped: the computeds that read it were marked when it was, and a computed cannot be
|
|
16226
|
+
* recomputed without reading — and so resolving — the dirty ones it depends on. The actual
|
|
16227
|
+
* recomputation is deferred until the value is read (see #recomputeOne).
|
|
16053
16228
|
* @param identifier The changed identifier reported by the bindings change tracker.
|
|
16054
16229
|
*/
|
|
16055
16230
|
#markDirtyComputeds(identifier) {
|
|
16056
16231
|
if (!this.#options.computed || !this.#bindings) {
|
|
16057
16232
|
return;
|
|
16058
16233
|
}
|
|
16059
|
-
|
|
16060
|
-
|
|
16061
|
-
|
|
16062
|
-
|
|
16234
|
+
const pending = [identifier];
|
|
16235
|
+
while (pending.length > 0) {
|
|
16236
|
+
const changed = pending.pop();
|
|
16237
|
+
for (const key of Object.keys(this.#computedDependencies)) {
|
|
16238
|
+
if (this.#bindings.isComputedDirty(key)) {
|
|
16239
|
+
continue;
|
|
16240
|
+
}
|
|
16241
|
+
const deps = this.#computedDependencies[key] || [];
|
|
16242
|
+
const reads = this.#computedReads.get(key);
|
|
16243
|
+
if (deps.some(dep => this.#dependencyAffectedBy(dep, changed)) ||
|
|
16244
|
+
(reads !== undefined && ReactiveProxy.changeAffectsRead(changed, reads))) {
|
|
16245
|
+
this.#bindings.markComputedDirty(key);
|
|
16246
|
+
pending.push(key);
|
|
16247
|
+
}
|
|
16063
16248
|
}
|
|
16064
16249
|
}
|
|
16065
16250
|
}
|
|
@@ -16119,10 +16304,21 @@ class VApplication {
|
|
|
16119
16304
|
return;
|
|
16120
16305
|
}
|
|
16121
16306
|
const computedFn = VApplication.#getComputedGetter(def);
|
|
16307
|
+
const bindings = this.#bindings;
|
|
16122
16308
|
try {
|
|
16123
16309
|
// Read the previous value without triggering re-resolution, for change detection.
|
|
16124
|
-
const oldValue =
|
|
16125
|
-
|
|
16310
|
+
const oldValue = bindings.peekComputed(key);
|
|
16311
|
+
// Record what the getter reads, replacing the previous record: the reads can differ
|
|
16312
|
+
// from run to run (a branch, a dynamic key). Kept even when the getter throws, so a
|
|
16313
|
+
// change to what it read before failing retries it.
|
|
16314
|
+
const reads = new Set();
|
|
16315
|
+
let newValue;
|
|
16316
|
+
try {
|
|
16317
|
+
newValue = ReactiveProxy.track(reads, () => computedFn.call(bindings.raw));
|
|
16318
|
+
}
|
|
16319
|
+
finally {
|
|
16320
|
+
this.#computedReads.set(key, reads);
|
|
16321
|
+
}
|
|
16126
16322
|
// Check if the value actually changed.
|
|
16127
16323
|
let hasChanged = oldValue !== newValue;
|
|
16128
16324
|
// For arrays, always treat as changed (VBindings detects length changes via its length
|