@react-three/tsl 10.0.0-canary.e53bd54 → 10.0.0-canary.ee5a08a
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/index.cjs +331 -31
- package/dist/index.d.cts +120 -50
- package/dist/index.d.mts +120 -50
- package/dist/index.d.ts +120 -50
- package/dist/index.mjs +333 -33
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -177,6 +177,10 @@ function extractTSLValue(value) {
|
|
|
177
177
|
if (node.isConstNode) {
|
|
178
178
|
return node.value;
|
|
179
179
|
}
|
|
180
|
+
if (node.isVarNode && node.node) {
|
|
181
|
+
const inner = extractTSLValue(node.node);
|
|
182
|
+
return inner === node.node ? value : inner;
|
|
183
|
+
}
|
|
180
184
|
if ("value" in node) {
|
|
181
185
|
let extractedValue = node.value;
|
|
182
186
|
if (typeof node.traverse === "function") {
|
|
@@ -312,33 +316,129 @@ function ensureTSLExtension() {
|
|
|
312
316
|
});
|
|
313
317
|
}
|
|
314
318
|
|
|
319
|
+
const SCOPE = Symbol("readTracking.scope");
|
|
320
|
+
const LEAF_GUARDS = {
|
|
321
|
+
uniforms: isUniformNode,
|
|
322
|
+
nodes: isTSLNode,
|
|
323
|
+
buffers: isBufferLike,
|
|
324
|
+
gpuStorage: isStorageLike
|
|
325
|
+
};
|
|
326
|
+
function isScope(kind, value) {
|
|
327
|
+
return !!value && typeof value === "object" && !LEAF_GUARDS[kind](value);
|
|
328
|
+
}
|
|
329
|
+
function classify(kind, value) {
|
|
330
|
+
return kind !== "textures" && isScope(kind, value) ? SCOPE : value;
|
|
331
|
+
}
|
|
332
|
+
function resolveContainer(kind, root, scopePath) {
|
|
333
|
+
let container = root;
|
|
334
|
+
for (const key of scopePath) {
|
|
335
|
+
const next = container?.[key];
|
|
336
|
+
container = isScope(kind, next) ? next : void 0;
|
|
337
|
+
}
|
|
338
|
+
return container;
|
|
339
|
+
}
|
|
340
|
+
function observeNow(read, view) {
|
|
341
|
+
if (read.kind === "textures") {
|
|
342
|
+
const map = view("textures");
|
|
343
|
+
if (read.op === "keys") return [...map.keys()];
|
|
344
|
+
return read.op === "has" ? map.has(read.path[0]) : map.get(read.path[0]);
|
|
345
|
+
}
|
|
346
|
+
const root = view(read.kind);
|
|
347
|
+
if (read.op === "keys") return Object.keys(resolveContainer(read.kind, root, read.path) ?? {});
|
|
348
|
+
const container = resolveContainer(read.kind, root, read.path.slice(0, -1));
|
|
349
|
+
const key = read.path[read.path.length - 1];
|
|
350
|
+
if (read.op === "has") return !!container && key in container;
|
|
351
|
+
return classify(read.kind, container?.[key]);
|
|
352
|
+
}
|
|
353
|
+
function sameKeys(a, b) {
|
|
354
|
+
return a.length === b.length && a.every((key, i) => key === b[i]);
|
|
355
|
+
}
|
|
356
|
+
function hasChanged(read, view) {
|
|
357
|
+
const now = observeNow(read, view);
|
|
358
|
+
return read.op === "keys" ? !sameKeys(read.seen, now) : !Object.is(read.seen, now);
|
|
359
|
+
}
|
|
360
|
+
const warned = /* @__PURE__ */ new Set();
|
|
361
|
+
function warnOnce(message) {
|
|
362
|
+
if (typeof process !== "undefined" && process.env.NODE_ENV === "production") return;
|
|
363
|
+
if (warned.has(message)) return;
|
|
364
|
+
warned.add(message);
|
|
365
|
+
console.warn(message);
|
|
366
|
+
}
|
|
367
|
+
function describeRead(kind, path) {
|
|
368
|
+
if (kind === "textures") return `textures.get('${path[0]}')`;
|
|
369
|
+
const scopes = path.slice(0, -1).map((key) => `.scope('${key}')`);
|
|
370
|
+
return `${kind}${scopes.join("")}.${path[path.length - 1] ?? ""}`;
|
|
371
|
+
}
|
|
372
|
+
function createReadTracker(hookName) {
|
|
373
|
+
const tracker = {
|
|
374
|
+
reads: [],
|
|
375
|
+
closed: false,
|
|
376
|
+
stale: false,
|
|
377
|
+
observe: (read) => {
|
|
378
|
+
if (tracker.closed) {
|
|
379
|
+
warnOnce(
|
|
380
|
+
`[${hookName}] ${describeRead(read.kind, read.path)} was read after the creator returned, probably inside Fn(). That read is not tracked, so replacing the resource will not rebuild this graph. Read the resource in the creator and close over it in Fn.`
|
|
381
|
+
);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
tracker.reads.push(read);
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
return tracker;
|
|
388
|
+
}
|
|
389
|
+
function isTrackerStale(tracker, view) {
|
|
390
|
+
if (!tracker.stale) tracker.stale = tracker.reads.some((read) => hasChanged(read, view));
|
|
391
|
+
return tracker.stale;
|
|
392
|
+
}
|
|
393
|
+
function warnMissingReads(hookName) {
|
|
394
|
+
return {
|
|
395
|
+
nested: false,
|
|
396
|
+
observe: (read) => {
|
|
397
|
+
if (read.op !== "get" || read.seen !== void 0) return;
|
|
398
|
+
warnOnce(
|
|
399
|
+
`[${hookName}] The creator read ${describeRead(read.kind, read.path)}, which does not exist yet. ${hookName} creators run once per generation, so this one will not re-run when it appears. Register it earlier (above this hook, or higher in the tree), or call rebuild* once it exists. To probe on purpose, use .has().`
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
315
405
|
var __defProp = Object.defineProperty;
|
|
316
406
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
317
407
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
318
|
-
var _a, _b;
|
|
408
|
+
var _a, _b, _c, _d;
|
|
319
409
|
const INTERNAL_DATA = Symbol("ScopedStore.data");
|
|
320
410
|
const INTERNAL_IS_LEAF = Symbol("ScopedStore.isLeaf");
|
|
321
|
-
|
|
411
|
+
const INTERNAL_READS = Symbol("ScopedStore.reads");
|
|
412
|
+
const INTERNAL_CHILDREN = Symbol("ScopedStore.children");
|
|
413
|
+
_d = INTERNAL_DATA, _c = INTERNAL_IS_LEAF, _b = INTERNAL_READS, _a = INTERNAL_CHILDREN;
|
|
322
414
|
const _ScopedStore = class _ScopedStore {
|
|
323
|
-
constructor(data, isLeaf) {
|
|
415
|
+
constructor(data, isLeaf, reads) {
|
|
416
|
+
/** @internal */
|
|
417
|
+
__publicField(this, _d);
|
|
418
|
+
/** @internal */
|
|
419
|
+
__publicField(this, _c);
|
|
420
|
+
/** @internal */
|
|
324
421
|
__publicField(this, _b);
|
|
422
|
+
/** @internal */
|
|
325
423
|
__publicField(this, _a);
|
|
326
424
|
this[INTERNAL_DATA] = data;
|
|
327
425
|
this[INTERNAL_IS_LEAF] = isLeaf;
|
|
426
|
+
this[INTERNAL_READS] = reads;
|
|
328
427
|
return new Proxy(this, {
|
|
329
428
|
get(target, prop, receiver) {
|
|
330
429
|
if (typeof prop === "string") {
|
|
331
430
|
if (prop === "scope" || prop === "has" || prop === "keys") {
|
|
332
431
|
return Reflect.get(target, prop, receiver);
|
|
333
432
|
}
|
|
334
|
-
return target
|
|
433
|
+
return readEntry(target, prop);
|
|
335
434
|
}
|
|
336
435
|
return Reflect.get(target, prop, receiver);
|
|
337
436
|
},
|
|
338
437
|
has(target, prop) {
|
|
339
|
-
return typeof prop === "string" ? prop
|
|
438
|
+
return typeof prop === "string" ? target.has(prop) : Reflect.has(target, prop);
|
|
340
439
|
},
|
|
341
440
|
ownKeys(target) {
|
|
441
|
+
observeKeys(target);
|
|
342
442
|
return Reflect.ownKeys(target[INTERNAL_DATA]);
|
|
343
443
|
},
|
|
344
444
|
getOwnPropertyDescriptor(target, prop) {
|
|
@@ -360,54 +460,152 @@ const _ScopedStore = class _ScopedStore {
|
|
|
360
460
|
scope(key) {
|
|
361
461
|
const value = this[INTERNAL_DATA][key];
|
|
362
462
|
const isLeaf = this[INTERNAL_IS_LEAF];
|
|
463
|
+
const reads = this[INTERNAL_READS];
|
|
363
464
|
const scope = value && typeof value === "object" && !isLeaf(value) ? value : {};
|
|
364
|
-
|
|
465
|
+
const child = reads ? { ...reads, path: [...reads.path, key] } : void 0;
|
|
466
|
+
return new _ScopedStore(scope, isLeaf, child);
|
|
365
467
|
}
|
|
366
468
|
/**
|
|
367
469
|
* Check if a key exists in the store.
|
|
368
470
|
*/
|
|
369
471
|
has(key) {
|
|
370
|
-
|
|
472
|
+
const found = key in this[INTERNAL_DATA];
|
|
473
|
+
const reads = this[INTERNAL_READS];
|
|
474
|
+
reads?.observe({ kind: reads.kind, op: "has", path: [...reads.path, key], seen: found });
|
|
475
|
+
return found;
|
|
371
476
|
}
|
|
372
477
|
/**
|
|
373
478
|
* Get all keys in the store.
|
|
374
479
|
*/
|
|
375
480
|
keys() {
|
|
481
|
+
observeKeys(this);
|
|
376
482
|
return Object.keys(this[INTERNAL_DATA]);
|
|
377
483
|
}
|
|
378
484
|
};
|
|
379
485
|
let ScopedStore = _ScopedStore;
|
|
486
|
+
function readEntry(target, key) {
|
|
487
|
+
const value = target[INTERNAL_DATA][key];
|
|
488
|
+
const reads = target[INTERNAL_READS];
|
|
489
|
+
if (!reads) return value;
|
|
490
|
+
const isLeaf = target[INTERNAL_IS_LEAF];
|
|
491
|
+
const isScope = !!value && typeof value === "object" && !isLeaf(value);
|
|
492
|
+
const path = [...reads.path, key];
|
|
493
|
+
reads.observe({ kind: reads.kind, op: "get", path, seen: isScope ? SCOPE : value });
|
|
494
|
+
if (!isScope || !reads.nested) return value;
|
|
495
|
+
const children = target[INTERNAL_CHILDREN] ?? (target[INTERNAL_CHILDREN] = /* @__PURE__ */ new Map());
|
|
496
|
+
let child = children.get(key);
|
|
497
|
+
if (!child) {
|
|
498
|
+
child = new ScopedStore(value, isLeaf, { ...reads, path });
|
|
499
|
+
children.set(key, child);
|
|
500
|
+
}
|
|
501
|
+
return child;
|
|
502
|
+
}
|
|
503
|
+
function observeKeys(target) {
|
|
504
|
+
const reads = target[INTERNAL_READS];
|
|
505
|
+
reads?.observe({ kind: reads.kind, op: "keys", path: reads.path, seen: Object.keys(target[INTERNAL_DATA]) });
|
|
506
|
+
}
|
|
380
507
|
function createScopedStore(data, isLeaf) {
|
|
381
508
|
return new ScopedStore(data, isLeaf);
|
|
382
509
|
}
|
|
383
|
-
function
|
|
510
|
+
function observeTextures(map, observe) {
|
|
511
|
+
const observeKeys2 = () => observe({ kind: "textures", op: "keys", path: [], seen: [...map.keys()] });
|
|
512
|
+
const observeAll = () => {
|
|
513
|
+
observeKeys2();
|
|
514
|
+
for (const [url, texture] of map) observe({ kind: "textures", op: "get", path: [url], seen: texture });
|
|
515
|
+
};
|
|
516
|
+
return new Proxy(map, {
|
|
517
|
+
get(target, prop) {
|
|
518
|
+
switch (prop) {
|
|
519
|
+
case "get":
|
|
520
|
+
return (url) => {
|
|
521
|
+
const texture = target.get(url);
|
|
522
|
+
observe({ kind: "textures", op: "get", path: [url], seen: texture });
|
|
523
|
+
return texture;
|
|
524
|
+
};
|
|
525
|
+
case "has":
|
|
526
|
+
return (url) => {
|
|
527
|
+
const found = target.has(url);
|
|
528
|
+
observe({ kind: "textures", op: "has", path: [url], seen: found });
|
|
529
|
+
return found;
|
|
530
|
+
};
|
|
531
|
+
case "size":
|
|
532
|
+
observeKeys2();
|
|
533
|
+
return target.size;
|
|
534
|
+
case "keys":
|
|
535
|
+
return () => {
|
|
536
|
+
observeKeys2();
|
|
537
|
+
return target.keys();
|
|
538
|
+
};
|
|
539
|
+
case "values":
|
|
540
|
+
return () => {
|
|
541
|
+
observeAll();
|
|
542
|
+
return target.values();
|
|
543
|
+
};
|
|
544
|
+
case "entries":
|
|
545
|
+
case Symbol.iterator:
|
|
546
|
+
return () => {
|
|
547
|
+
observeAll();
|
|
548
|
+
return target.entries();
|
|
549
|
+
};
|
|
550
|
+
case "forEach":
|
|
551
|
+
return (callback, thisArg) => {
|
|
552
|
+
observeAll();
|
|
553
|
+
target.forEach(callback, thisArg);
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
const value = Reflect.get(target, prop, target);
|
|
557
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
function createResourceView(primary, local = primary) {
|
|
562
|
+
return ((kind) => kind === "textures" ? extension.getTextureView(local) : withStagedOverlay(primary, kind, primary.getState()[kind]));
|
|
563
|
+
}
|
|
564
|
+
function createLazyCreatorState(state, store, options = {}) {
|
|
565
|
+
const { reads } = options;
|
|
566
|
+
const view = options.view ?? (store ? createResourceView(store) : void 0);
|
|
384
567
|
let _uniforms = null;
|
|
385
568
|
let _nodes = null;
|
|
386
569
|
let _buffers = null;
|
|
387
570
|
let _gpuStorage = null;
|
|
388
|
-
|
|
389
|
-
|
|
571
|
+
let _textures = null;
|
|
572
|
+
const read = (kind) => view ? view(kind) : state[kind];
|
|
573
|
+
const wrap = (kind, isLeaf) => new ScopedStore(
|
|
574
|
+
read(kind),
|
|
575
|
+
isLeaf,
|
|
576
|
+
reads && { ...reads, kind, path: [] }
|
|
577
|
+
);
|
|
578
|
+
const properties = {
|
|
390
579
|
uniforms: {
|
|
391
580
|
get() {
|
|
392
|
-
return _uniforms ?? (_uniforms =
|
|
581
|
+
return _uniforms ?? (_uniforms = wrap("uniforms", isUniformNode));
|
|
393
582
|
}
|
|
394
583
|
},
|
|
395
584
|
nodes: {
|
|
396
585
|
get() {
|
|
397
|
-
return _nodes ?? (_nodes =
|
|
586
|
+
return _nodes ?? (_nodes = wrap("nodes", isTSLNode));
|
|
398
587
|
}
|
|
399
588
|
},
|
|
400
589
|
buffers: {
|
|
401
590
|
get() {
|
|
402
|
-
return _buffers ?? (_buffers =
|
|
591
|
+
return _buffers ?? (_buffers = wrap("buffers", isBufferLike));
|
|
403
592
|
}
|
|
404
593
|
},
|
|
405
594
|
gpuStorage: {
|
|
406
595
|
get() {
|
|
407
|
-
return _gpuStorage ?? (_gpuStorage =
|
|
596
|
+
return _gpuStorage ?? (_gpuStorage = wrap("gpuStorage", isStorageLike));
|
|
408
597
|
}
|
|
409
598
|
}
|
|
410
|
-
}
|
|
599
|
+
};
|
|
600
|
+
if (options.view) {
|
|
601
|
+
properties.textures = {
|
|
602
|
+
get() {
|
|
603
|
+
const textures = options.view("textures");
|
|
604
|
+
return _textures ?? (_textures = reads ? observeTextures(textures, reads.observe) : textures);
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
return Object.create(state, properties);
|
|
411
609
|
}
|
|
412
610
|
|
|
413
611
|
function resolvePrimary(local) {
|
|
@@ -483,7 +681,7 @@ function useUniforms(creatorOrScope, scope) {
|
|
|
483
681
|
const processedInput = React.useMemo(() => {
|
|
484
682
|
let raw = creatorOrScope;
|
|
485
683
|
if (typeof creatorOrScope === "function") {
|
|
486
|
-
const wrappedState = createLazyCreatorState(store.getState(), store);
|
|
684
|
+
const wrappedState = createLazyCreatorState(store.getState(), store, { reads: warnMissingReads("useUniforms") });
|
|
487
685
|
raw = creatorOrScope(wrappedState);
|
|
488
686
|
}
|
|
489
687
|
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
@@ -565,18 +763,20 @@ function isSameThreeType(a, b) {
|
|
|
565
763
|
const uniformOf = tsl.uniform;
|
|
566
764
|
function useUniform(name, value) {
|
|
567
765
|
const store = usePrimaryStore();
|
|
766
|
+
const memoizedInput = useCompareMemoize(value === void 0 ? name : { [name]: normalizeValue(value) });
|
|
568
767
|
const registered = useScopedResource({
|
|
569
768
|
store,
|
|
570
769
|
kind: "uniforms",
|
|
571
770
|
scope: void 0,
|
|
572
771
|
isLeaf: isUniformNode,
|
|
573
|
-
input:
|
|
772
|
+
input: memoizedInput,
|
|
574
773
|
create: () => value === void 0 ? {} : { [name]: value },
|
|
575
774
|
prepare: createNamedUniform,
|
|
576
775
|
reconcile: (existing2) => {
|
|
577
|
-
if (value
|
|
578
|
-
|
|
579
|
-
|
|
776
|
+
if (value === void 0 || isUniformNode(value)) return;
|
|
777
|
+
const next = normalizeValue(value);
|
|
778
|
+
if (isTSLNode(next)) return;
|
|
779
|
+
existing2.value = next;
|
|
580
780
|
}
|
|
581
781
|
});
|
|
582
782
|
if (registered[name]) return registered[name];
|
|
@@ -586,6 +786,9 @@ function useUniform(name, value) {
|
|
|
586
786
|
`[useUniform] Uniform "${name}" not found. Create it first with: useUniform('${name}', initialValue)`
|
|
587
787
|
);
|
|
588
788
|
}
|
|
789
|
+
function normalizeValue(value) {
|
|
790
|
+
return typeof value === "string" ? new THREE.Color(value) : vectorize(value);
|
|
791
|
+
}
|
|
589
792
|
function createNamedUniform(name, value) {
|
|
590
793
|
let node;
|
|
591
794
|
if (isUniformNode(value)) {
|
|
@@ -626,7 +829,16 @@ function useNodes(creatorOrScope, scope) {
|
|
|
626
829
|
isLeaf: isTSLNode,
|
|
627
830
|
create: () => {
|
|
628
831
|
if (isReader) return {};
|
|
629
|
-
|
|
832
|
+
const nodes2 = creatorOrScope(
|
|
833
|
+
createLazyCreatorState(store.getState(), store, { reads: warnMissingReads("useNodes") })
|
|
834
|
+
);
|
|
835
|
+
if (nodes2 == null) {
|
|
836
|
+
warnOnce(
|
|
837
|
+
"[useNodes] The creator returned nothing, so nothing was registered. useNodes registers the nodes its creator returns. To put a node onto a Three object (scene.fogNode = ...), use useLocalNodes and return a function that assigns it: it runs after commit and may return a cleanup."
|
|
838
|
+
);
|
|
839
|
+
return {};
|
|
840
|
+
}
|
|
841
|
+
return nodes2;
|
|
630
842
|
},
|
|
631
843
|
prepare: (name, node) => {
|
|
632
844
|
const setName = Reflect.get(node, "setName");
|
|
@@ -646,16 +858,100 @@ function useNodes(creatorOrScope, scope) {
|
|
|
646
858
|
function rebuildAllNodes(store, scope) {
|
|
647
859
|
rebuildResource(store, "nodes", scope);
|
|
648
860
|
}
|
|
649
|
-
function
|
|
861
|
+
function areDepsEqual(next, prev) {
|
|
862
|
+
if (next.length !== prev.length) return false;
|
|
863
|
+
for (let i = 0; i < next.length; i++) if (!Object.is(next[i], prev[i])) return false;
|
|
864
|
+
return true;
|
|
865
|
+
}
|
|
866
|
+
function useDependencyToken(deps) {
|
|
867
|
+
const previous = React.useRef(null);
|
|
868
|
+
const record = previous.current;
|
|
869
|
+
if (typeof process !== "undefined" && process.env.NODE_ENV !== "production" && record) {
|
|
870
|
+
const hadDeps = record.deps !== void 0;
|
|
871
|
+
const hasDeps = deps !== void 0;
|
|
872
|
+
if (hadDeps !== hasDeps) {
|
|
873
|
+
console.warn(
|
|
874
|
+
`[useLocalNodes] The dependency array was ${hasDeps ? "added" : "omitted"} between renders. Pass an array on every render or on none; the mode must not change for a mounted component.`
|
|
875
|
+
);
|
|
876
|
+
} else if (hasDeps && record.deps.length !== deps.length) {
|
|
877
|
+
console.warn(
|
|
878
|
+
`[useLocalNodes] The dependency array length changed between renders (${record.deps.length} \u2192 ${deps.length}). Declare a fixed-length list; conditional dependencies belong inside the array as values.`
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
if (deps === void 0) {
|
|
883
|
+
previous.current = { deps: void 0, token: {} };
|
|
884
|
+
} else if (!record || record.deps === void 0 || !areDepsEqual(deps, record.deps)) {
|
|
885
|
+
previous.current = { deps, token: {} };
|
|
886
|
+
} else if (record.deps !== deps) {
|
|
887
|
+
previous.current = { deps, token: record.token };
|
|
888
|
+
}
|
|
889
|
+
return previous.current.token;
|
|
890
|
+
}
|
|
891
|
+
function useLocalNodes(creator, deps) {
|
|
892
|
+
const local = extension.useStore();
|
|
650
893
|
const store = usePrimaryStore();
|
|
651
|
-
const
|
|
652
|
-
const nodes = usePrimaryThree((s) => s.nodes);
|
|
653
|
-
const textures = usePrimaryThree((s) => s.textures);
|
|
894
|
+
const view = React.useMemo(() => createResourceView(store, local), [store, local]);
|
|
654
895
|
const hmrVersion = usePrimaryThree((s) => s._hmrVersion);
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
896
|
+
const depsToken = useDependencyToken(deps);
|
|
897
|
+
const committedReads = React.useRef(null);
|
|
898
|
+
const readsVersion = React.useRef(0);
|
|
899
|
+
const getReadsVersion = () => {
|
|
900
|
+
const tracker2 = committedReads.current;
|
|
901
|
+
if (tracker2 && !tracker2.stale && isTrackerStale(tracker2, view)) readsVersion.current++;
|
|
902
|
+
return readsVersion.current;
|
|
903
|
+
};
|
|
904
|
+
const subscribe = React.useCallback(
|
|
905
|
+
(onChange) => {
|
|
906
|
+
const unsubscribe = store.subscribe(onChange);
|
|
907
|
+
if (local === store) return unsubscribe;
|
|
908
|
+
const unsubscribeLocal = local.subscribe(onChange);
|
|
909
|
+
return () => {
|
|
910
|
+
unsubscribe();
|
|
911
|
+
unsubscribeLocal();
|
|
912
|
+
};
|
|
913
|
+
},
|
|
914
|
+
[store, local]
|
|
915
|
+
);
|
|
916
|
+
const resourceVersion = React.useSyncExternalStore(subscribe, getReadsVersion, getReadsVersion);
|
|
917
|
+
const evaluation = React.useMemo(() => {
|
|
918
|
+
const tracker2 = createReadTracker("useLocalNodes");
|
|
919
|
+
const reads = { observe: tracker2.observe, nested: true };
|
|
920
|
+
const value2 = creator(createLazyCreatorState(local.getState(), store, { reads, view }));
|
|
921
|
+
tracker2.closed = true;
|
|
922
|
+
return { value: value2, tracker: tracker2 };
|
|
923
|
+
}, [view, hmrVersion, depsToken, resourceVersion]);
|
|
924
|
+
const { value, tracker } = evaluation;
|
|
925
|
+
const installing = typeof value === "function";
|
|
926
|
+
useModeDiagnostics(value, installing);
|
|
927
|
+
useIsomorphicLayoutEffect(() => {
|
|
928
|
+
committedReads.current = tracker;
|
|
929
|
+
if (!installing) return;
|
|
930
|
+
tracker.closed = false;
|
|
931
|
+
let cleanup;
|
|
932
|
+
try {
|
|
933
|
+
cleanup = value();
|
|
934
|
+
} finally {
|
|
935
|
+
tracker.closed = true;
|
|
936
|
+
}
|
|
937
|
+
return typeof cleanup === "function" ? cleanup : void 0;
|
|
938
|
+
}, [evaluation]);
|
|
939
|
+
return installing ? void 0 : value;
|
|
940
|
+
}
|
|
941
|
+
function useModeDiagnostics(value, installing) {
|
|
942
|
+
const previous = React.useRef(null);
|
|
943
|
+
if (typeof process !== "undefined" && process.env.NODE_ENV === "production") return;
|
|
944
|
+
if (value === void 0 || value === null) {
|
|
945
|
+
warnOnce(
|
|
946
|
+
"[useLocalNodes] The creator returned nothing. To put a node onto a Three object (scene.fogNode = ...), build it in the creator and return a function that assigns it: that function runs after commit and may return a cleanup. Assigning inside the creator runs during render, which React may discard or repeat."
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
if (previous.current !== null && previous.current !== installing) {
|
|
950
|
+
warnOnce(
|
|
951
|
+
"[useLocalNodes] The creator switched between returning a record and returning an install function. Keep one form for a mounted component."
|
|
952
|
+
);
|
|
953
|
+
}
|
|
954
|
+
previous.current = installing;
|
|
659
955
|
}
|
|
660
956
|
|
|
661
957
|
const disposeBuffer = (buffer) => {
|
|
@@ -701,7 +997,9 @@ function useBuffers(creatorOrScope, scope) {
|
|
|
701
997
|
isLeaf: isBufferLike,
|
|
702
998
|
create: () => {
|
|
703
999
|
if (isReader) return {};
|
|
704
|
-
return creatorOrScope(
|
|
1000
|
+
return creatorOrScope(
|
|
1001
|
+
createLazyCreatorState(store.getState(), store, { reads: warnMissingReads("useBuffers") })
|
|
1002
|
+
);
|
|
705
1003
|
},
|
|
706
1004
|
prepare: (name, buffer) => {
|
|
707
1005
|
const setName = Reflect.get(buffer, "setName");
|
|
@@ -765,7 +1063,9 @@ function useGPUStorage(creatorOrScope, scope) {
|
|
|
765
1063
|
isLeaf: isStorageLike,
|
|
766
1064
|
create: () => {
|
|
767
1065
|
if (isReader) return {};
|
|
768
|
-
return creatorOrScope(
|
|
1066
|
+
return creatorOrScope(
|
|
1067
|
+
createLazyCreatorState(store.getState(), store, { reads: warnMissingReads("useGPUStorage") })
|
|
1068
|
+
);
|
|
769
1069
|
},
|
|
770
1070
|
prepare: (name, storage) => {
|
|
771
1071
|
const label = scopedNodeName(scope, name);
|